*, ::before, ::after {
  --tw-border-spacing-x: 0;
  --tw-border-spacing-y: 0;
  --tw-translate-x: 0;
  --tw-translate-y: 0;
  --tw-rotate: 0;
  --tw-skew-x: 0;
  --tw-skew-y: 0;
  --tw-scale-x: 1;
  --tw-scale-y: 1;
  --tw-pan-x:  ;
  --tw-pan-y:  ;
  --tw-pinch-zoom:  ;
  --tw-scroll-snap-strictness: proximity;
  --tw-gradient-from-position:  ;
  --tw-gradient-via-position:  ;
  --tw-gradient-to-position:  ;
  --tw-ordinal:  ;
  --tw-slashed-zero:  ;
  --tw-numeric-figure:  ;
  --tw-numeric-spacing:  ;
  --tw-numeric-fraction:  ;
  --tw-ring-inset:  ;
  --tw-ring-offset-width: 0px;
  --tw-ring-offset-color: #fff;
  --tw-ring-color: rgb(59 130 246 / 0.5);
  --tw-ring-offset-shadow: 0 0 #0000;
  --tw-ring-shadow: 0 0 #0000;
  --tw-shadow: 0 0 #0000;
  --tw-shadow-colored: 0 0 #0000;
  --tw-blur:  ;
  --tw-brightness:  ;
  --tw-contrast:  ;
  --tw-grayscale:  ;
  --tw-hue-rotate:  ;
  --tw-invert:  ;
  --tw-saturate:  ;
  --tw-sepia:  ;
  --tw-drop-shadow:  ;
  --tw-backdrop-blur:  ;
  --tw-backdrop-brightness:  ;
  --tw-backdrop-contrast:  ;
  --tw-backdrop-grayscale:  ;
  --tw-backdrop-hue-rotate:  ;
  --tw-backdrop-invert:  ;
  --tw-backdrop-opacity:  ;
  --tw-backdrop-saturate:  ;
  --tw-backdrop-sepia:  ;
  --tw-contain-size:  ;
  --tw-contain-layout:  ;
  --tw-contain-paint:  ;
  --tw-contain-style:  ;
}

::backdrop {
  --tw-border-spacing-x: 0;
  --tw-border-spacing-y: 0;
  --tw-translate-x: 0;
  --tw-translate-y: 0;
  --tw-rotate: 0;
  --tw-skew-x: 0;
  --tw-skew-y: 0;
  --tw-scale-x: 1;
  --tw-scale-y: 1;
  --tw-pan-x:  ;
  --tw-pan-y:  ;
  --tw-pinch-zoom:  ;
  --tw-scroll-snap-strictness: proximity;
  --tw-gradient-from-position:  ;
  --tw-gradient-via-position:  ;
  --tw-gradient-to-position:  ;
  --tw-ordinal:  ;
  --tw-slashed-zero:  ;
  --tw-numeric-figure:  ;
  --tw-numeric-spacing:  ;
  --tw-numeric-fraction:  ;
  --tw-ring-inset:  ;
  --tw-ring-offset-width: 0px;
  --tw-ring-offset-color: #fff;
  --tw-ring-color: rgb(59 130 246 / 0.5);
  --tw-ring-offset-shadow: 0 0 #0000;
  --tw-ring-shadow: 0 0 #0000;
  --tw-shadow: 0 0 #0000;
  --tw-shadow-colored: 0 0 #0000;
  --tw-blur:  ;
  --tw-brightness:  ;
  --tw-contrast:  ;
  --tw-grayscale:  ;
  --tw-hue-rotate:  ;
  --tw-invert:  ;
  --tw-saturate:  ;
  --tw-sepia:  ;
  --tw-drop-shadow:  ;
  --tw-backdrop-blur:  ;
  --tw-backdrop-brightness:  ;
  --tw-backdrop-contrast:  ;
  --tw-backdrop-grayscale:  ;
  --tw-backdrop-hue-rotate:  ;
  --tw-backdrop-invert:  ;
  --tw-backdrop-opacity:  ;
  --tw-backdrop-saturate:  ;
  --tw-backdrop-sepia:  ;
  --tw-contain-size:  ;
  --tw-contain-layout:  ;
  --tw-contain-paint:  ;
  --tw-contain-style:  ;
}/*
! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com
*//*
1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4)
2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116)
*/

*,
::before,
::after {
  box-sizing: border-box; /* 1 */
  border-width: 0; /* 2 */
  border-style: solid; /* 2 */
  border-color: #e5e7eb; /* 2 */
}

::before,
::after {
  --tw-content: '';
}

/*
1. Use a consistent sensible line-height in all browsers.
2. Prevent adjustments of font size after orientation changes in iOS.
3. Use a more readable tab size.
4. Use the user's configured `sans` font-family by default.
5. Use the user's configured `sans` font-feature-settings by default.
6. Use the user's configured `sans` font-variation-settings by default.
7. Disable tap highlights on iOS
*/

html,
:host {
  line-height: 1.5; /* 1 */
  -webkit-text-size-adjust: 100%; /* 2 */
  -moz-tab-size: 4; /* 3 */
  -o-tab-size: 4;
     tab-size: 4; /* 3 */
  font-family: var(--font-body), Helvetica Neue, Arial, sans-serif; /* 4 */
  font-feature-settings: normal; /* 5 */
  font-variation-settings: normal; /* 6 */
  -webkit-tap-highlight-color: transparent; /* 7 */
}

/*
1. Remove the margin in all browsers.
2. Inherit line-height from `html` so users can set them as a class directly on the `html` element.
*/

body {
  margin: 0; /* 1 */
  line-height: inherit; /* 2 */
}

/*
1. Add the correct height in Firefox.
2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655)
3. Ensure horizontal rules are visible by default.
*/

hr {
  height: 0; /* 1 */
  color: inherit; /* 2 */
  border-top-width: 1px; /* 3 */
}

/*
Add the correct text decoration in Chrome, Edge, and Safari.
*/

abbr:where([title]) {
  -webkit-text-decoration: underline dotted;
          text-decoration: underline dotted;
}

/*
Remove the default font size and weight for headings.
*/

h1,
h2,
h3,
h4,
h5,
h6 {
  font-size: inherit;
  font-weight: inherit;
}

/*
Reset links to optimize for opt-in styling instead of opt-out.
*/

a {
  color: inherit;
  text-decoration: inherit;
}

/*
Add the correct font weight in Edge and Safari.
*/

b,
strong {
  font-weight: bolder;
}

/*
1. Use the user's configured `mono` font-family by default.
2. Use the user's configured `mono` font-feature-settings by default.
3. Use the user's configured `mono` font-variation-settings by default.
4. Correct the odd `em` font sizing in all browsers.
*/

code,
kbd,
samp,
pre {
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; /* 1 */
  font-feature-settings: normal; /* 2 */
  font-variation-settings: normal; /* 3 */
  font-size: 1em; /* 4 */
}

/*
Add the correct font size in all browsers.
*/

small {
  font-size: 80%;
}

/*
Prevent `sub` and `sup` elements from affecting the line height in all browsers.
*/

sub,
sup {
  font-size: 75%;
  line-height: 0;
  position: relative;
  vertical-align: baseline;
}

sub {
  bottom: -0.25em;
}

sup {
  top: -0.5em;
}

/*
1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297)
2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016)
3. Remove gaps between table borders by default.
*/

table {
  text-indent: 0; /* 1 */
  border-color: inherit; /* 2 */
  border-collapse: collapse; /* 3 */
}

/*
1. Change the font styles in all browsers.
2. Remove the margin in Firefox and Safari.
3. Remove default padding in all browsers.
*/

button,
input,
optgroup,
select,
textarea {
  font-family: inherit; /* 1 */
  font-feature-settings: inherit; /* 1 */
  font-variation-settings: inherit; /* 1 */
  font-size: 100%; /* 1 */
  font-weight: inherit; /* 1 */
  line-height: inherit; /* 1 */
  letter-spacing: inherit; /* 1 */
  color: inherit; /* 1 */
  margin: 0; /* 2 */
  padding: 0; /* 3 */
}

/*
Remove the inheritance of text transform in Edge and Firefox.
*/

button,
select {
  text-transform: none;
}

/*
1. Correct the inability to style clickable types in iOS and Safari.
2. Remove default button styles.
*/

button,
input:where([type='button']),
input:where([type='reset']),
input:where([type='submit']) {
  -webkit-appearance: button; /* 1 */
  background-color: transparent; /* 2 */
  background-image: none; /* 2 */
}

/*
Use the modern Firefox focus style for all focusable elements.
*/

:-moz-focusring {
  outline: auto;
}

/*
Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737)
*/

:-moz-ui-invalid {
  box-shadow: none;
}

/*
Add the correct vertical alignment in Chrome and Firefox.
*/

progress {
  vertical-align: baseline;
}

/*
Correct the cursor style of increment and decrement buttons in Safari.
*/

::-webkit-inner-spin-button,
::-webkit-outer-spin-button {
  height: auto;
}

/*
1. Correct the odd appearance in Chrome and Safari.
2. Correct the outline style in Safari.
*/

[type='search'] {
  -webkit-appearance: textfield; /* 1 */
  outline-offset: -2px; /* 2 */
}

/*
Remove the inner padding in Chrome and Safari on macOS.
*/

::-webkit-search-decoration {
  -webkit-appearance: none;
}

/*
1. Correct the inability to style clickable types in iOS and Safari.
2. Change font properties to `inherit` in Safari.
*/

::-webkit-file-upload-button {
  -webkit-appearance: button; /* 1 */
  font: inherit; /* 2 */
}

/*
Add the correct display in Chrome and Safari.
*/

summary {
  display: list-item;
}

/*
Removes the default spacing and border for appropriate elements.
*/

blockquote,
dl,
dd,
h1,
h2,
h3,
h4,
h5,
h6,
hr,
figure,
p,
pre {
  margin: 0;
}

fieldset {
  margin: 0;
  padding: 0;
}

legend {
  padding: 0;
}

ol,
ul,
menu {
  list-style: none;
  margin: 0;
  padding: 0;
}

/*
Reset default styling for dialogs.
*/
dialog {
  padding: 0;
}

/*
Prevent resizing textareas horizontally by default.
*/

textarea {
  resize: vertical;
}

/*
1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300)
2. Set the default placeholder color to the user's configured gray 400 color.
*/

input::-moz-placeholder, textarea::-moz-placeholder {
  opacity: 1; /* 1 */
  color: #9ca3af; /* 2 */
}

input::placeholder,
textarea::placeholder {
  opacity: 1; /* 1 */
  color: #9ca3af; /* 2 */
}

/*
Set the default cursor for buttons.
*/

button,
[role="button"] {
  cursor: pointer;
}

/*
Make sure disabled buttons don't get the pointer cursor.
*/
:disabled {
  cursor: default;
}

/*
1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14)
2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210)
   This can trigger a poorly considered lint error in some tools but is included by design.
*/

img,
svg,
video,
canvas,
audio,
iframe,
embed,
object {
  display: block; /* 1 */
  vertical-align: middle; /* 2 */
}

/*
Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14)
*/

img,
video {
  max-width: 100%;
  height: auto;
}

/* Make elements with the HTML hidden attribute stay hidden by default */
[hidden]:where(:not([hidden="until-found"])) {
  display: none;
}
.container {
  width: 100%;
}
@media (min-width: 640px) {

  .container {
    max-width: 640px;
  }
}
@media (min-width: 768px) {

  .container {
    max-width: 768px;
  }
}
@media (min-width: 1024px) {

  .container {
    max-width: 1024px;
  }
}
@media (min-width: 1280px) {

  .container {
    max-width: 1280px;
  }
}
@media (min-width: 1536px) {

  .container {
    max-width: 1536px;
  }
}
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border-width: 0;
}
.pointer-events-none {
  pointer-events: none;
}
.pointer-events-auto {
  pointer-events: auto;
}
.visible {
  visibility: visible;
}
.invisible {
  visibility: hidden;
}
.static {
  position: static;
}
.fixed {
  position: fixed;
}
.absolute {
  position: absolute;
}
.relative {
  position: relative;
}
.sticky {
  position: sticky;
}
.inset-0 {
  inset: 0px;
}
.inset-\[-30\%\] {
  inset: -30%;
}
.inset-\[-40\%\] {
  inset: -40%;
}
.inset-x-0 {
  left: 0px;
  right: 0px;
}
.-right-1 {
  right: -0.25rem;
}
.-top-1 {
  top: -0.25rem;
}
.bottom-0 {
  bottom: 0px;
}
.bottom-10 {
  bottom: 2.5rem;
}
.bottom-24 {
  bottom: 6rem;
}
.bottom-6 {
  bottom: 1.5rem;
}
.left-0 {
  left: 0px;
}
.left-3 {
  left: 0.75rem;
}
.left-6 {
  left: 1.5rem;
}
.left-\[24\%\] {
  left: 24%;
}
.right-0 {
  right: 0px;
}
.right-3 {
  right: 0.75rem;
}
.right-4 {
  right: 1rem;
}
.right-6 {
  right: 1.5rem;
}
.top-0 {
  top: 0px;
}
.top-1\/2 {
  top: 50%;
}
.top-3 {
  top: 0.75rem;
}
.top-4 {
  top: 1rem;
}
.top-6 {
  top: 1.5rem;
}
.top-\[57\%\] {
  top: 57%;
}
.top-\[calc\(100\%\+0\.75rem\)\] {
  top: calc(100% + 0.75rem);
}
.top-full {
  top: 100%;
}
.z-10 {
  z-index: 10;
}
.z-20 {
  z-index: 20;
}
.z-30 {
  z-index: 30;
}
.z-40 {
  z-index: 40;
}
.z-50 {
  z-index: 50;
}
.z-\[58\] {
  z-index: 58;
}
.z-\[60\] {
  z-index: 60;
}
.z-\[70\] {
  z-index: 70;
}
.col-span-2 {
  grid-column: span 2 / span 2;
}
.-mx-2 {
  margin-left: -0.5rem;
  margin-right: -0.5rem;
}
.mx-auto {
  margin-left: auto;
  margin-right: auto;
}
.mb-1 {
  margin-bottom: 0.25rem;
}
.mb-2 {
  margin-bottom: 0.5rem;
}
.mb-3 {
  margin-bottom: 0.75rem;
}
.mb-4 {
  margin-bottom: 1rem;
}
.mb-5 {
  margin-bottom: 1.25rem;
}
.mb-6 {
  margin-bottom: 1.5rem;
}
.mb-8 {
  margin-bottom: 2rem;
}
.ml-1 {
  margin-left: 0.25rem;
}
.ml-2 {
  margin-left: 0.5rem;
}
.mr-2 {
  margin-right: 0.5rem;
}
.mt-0\.5 {
  margin-top: 0.125rem;
}
.mt-1 {
  margin-top: 0.25rem;
}
.mt-1\.5 {
  margin-top: 0.375rem;
}
.mt-12 {
  margin-top: 3rem;
}
.mt-2 {
  margin-top: 0.5rem;
}
.mt-3 {
  margin-top: 0.75rem;
}
.mt-4 {
  margin-top: 1rem;
}
.mt-5 {
  margin-top: 1.25rem;
}
.mt-6 {
  margin-top: 1.5rem;
}
.mt-8 {
  margin-top: 2rem;
}
.mt-auto {
  margin-top: auto;
}
.block {
  display: block;
}
.inline-block {
  display: inline-block;
}
.inline {
  display: inline;
}
.flex {
  display: flex;
}
.inline-flex {
  display: inline-flex;
}
.table {
  display: table;
}
.grid {
  display: grid;
}
.contents {
  display: contents;
}
.hidden {
  display: none;
}
.\!h-12 {
  height: 3rem !important;
}
.h-10 {
  height: 2.5rem;
}
.h-11 {
  height: 2.75rem;
}
.h-12 {
  height: 3rem;
}
.h-16 {
  height: 4rem;
}
.h-2\.5 {
  height: 0.625rem;
}
.h-20 {
  height: 5rem;
}
.h-24 {
  height: 6rem;
}
.h-3 {
  height: 0.75rem;
}
.h-3\.5 {
  height: 0.875rem;
}
.h-32 {
  height: 8rem;
}
.h-36 {
  height: 9rem;
}
.h-4 {
  height: 1rem;
}
.h-40 {
  height: 10rem;
}
.h-6 {
  height: 1.5rem;
}
.h-7 {
  height: 1.75rem;
}
.h-72 {
  height: 18rem;
}
.h-8 {
  height: 2rem;
}
.h-9 {
  height: 2.25rem;
}
.h-\[100dvh\] {
  height: 100dvh;
}
.h-\[26rem\] {
  height: 26rem;
}
.h-\[30\%\] {
  height: 30%;
}
.h-\[3px\] {
  height: 3px;
}
.h-full {
  height: 100%;
}
.h-px {
  height: 1px;
}
.h-screen {
  height: 100vh;
}
.max-h-\[75vh\] {
  max-height: 75vh;
}
.max-h-\[90dvh\] {
  max-height: 90dvh;
}
.max-h-\[92vh\] {
  max-height: 92vh;
}
.min-h-0 {
  min-height: 0px;
}
.min-h-screen {
  min-height: 100vh;
}
.\!w-12 {
  width: 3rem !important;
}
.w-0 {
  width: 0px;
}
.w-10 {
  width: 2.5rem;
}
.w-11 {
  width: 2.75rem;
}
.w-14 {
  width: 3.5rem;
}
.w-16 {
  width: 4rem;
}
.w-2\.5 {
  width: 0.625rem;
}
.w-20 {
  width: 5rem;
}
.w-28 {
  width: 7rem;
}
.w-3 {
  width: 0.75rem;
}
.w-3\.5 {
  width: 0.875rem;
}
.w-32 {
  width: 8rem;
}
.w-4 {
  width: 1rem;
}
.w-40 {
  width: 10rem;
}
.w-6 {
  width: 1.5rem;
}
.w-7 {
  width: 1.75rem;
}
.w-8 {
  width: 2rem;
}
.w-80 {
  width: 20rem;
}
.w-9 {
  width: 2.25rem;
}
.w-\[18\%\] {
  width: 18%;
}
.w-auto {
  width: auto;
}
.w-fit {
  width: -moz-fit-content;
  width: fit-content;
}
.w-full {
  width: 100%;
}
.w-px {
  width: 1px;
}
.w-screen {
  width: 100vw;
}
.min-w-0 {
  min-width: 0px;
}
.min-w-4 {
  min-width: 1rem;
}
.min-w-\[720px\] {
  min-width: 720px;
}
.min-w-\[760px\] {
  min-width: 760px;
}
.min-w-\[840px\] {
  min-width: 840px;
}
.min-w-\[900px\] {
  min-width: 900px;
}
.max-w-2xl {
  max-width: 42rem;
}
.max-w-3xl {
  max-width: 48rem;
}
.max-w-4xl {
  max-width: 56rem;
}
.max-w-5xl {
  max-width: 64rem;
}
.max-w-6xl {
  max-width: 72rem;
}
.max-w-\[220px\] {
  max-width: 220px;
}
.max-w-\[85\%\] {
  max-width: 85%;
}
.max-w-lg {
  max-width: 32rem;
}
.max-w-md {
  max-width: 28rem;
}
.max-w-sm {
  max-width: 24rem;
}
.max-w-xl {
  max-width: 36rem;
}
.max-w-xs {
  max-width: 20rem;
}
.flex-1 {
  flex: 1 1 0%;
}
.shrink {
  flex-shrink: 1;
}
.shrink-0 {
  flex-shrink: 0;
}
.grow {
  flex-grow: 1;
}
.origin-left {
  transform-origin: left;
}
.origin-top-left {
  transform-origin: top left;
}
.-translate-y-1\/2 {
  --tw-translate-y: -50%;
  transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
}
.transform {
  transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
}
.cursor-default {
  cursor: default;
}
.cursor-pointer {
  cursor: pointer;
}
.select-all {
  -webkit-user-select: all;
     -moz-user-select: all;
          user-select: all;
}
.resize {
  resize: both;
}
.snap-x {
  scroll-snap-type: x var(--tw-scroll-snap-strictness);
}
.snap-mandatory {
  --tw-scroll-snap-strictness: mandatory;
}
.snap-start {
  scroll-snap-align: start;
}
.grid-cols-1 {
  grid-template-columns: repeat(1, minmax(0, 1fr));
}
.grid-cols-2 {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
.grid-cols-3 {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}
.flex-row {
  flex-direction: row;
}
.flex-col {
  flex-direction: column;
}
.flex-wrap {
  flex-wrap: wrap;
}
.items-start {
  align-items: flex-start;
}
.items-end {
  align-items: flex-end;
}
.items-center {
  align-items: center;
}
.items-baseline {
  align-items: baseline;
}
.justify-start {
  justify-content: flex-start;
}
.justify-end {
  justify-content: flex-end;
}
.justify-center {
  justify-content: center;
}
.justify-between {
  justify-content: space-between;
}
.gap-0 {
  gap: 0px;
}
.gap-0\.5 {
  gap: 0.125rem;
}
.gap-1 {
  gap: 0.25rem;
}
.gap-1\.5 {
  gap: 0.375rem;
}
.gap-10 {
  gap: 2.5rem;
}
.gap-12 {
  gap: 3rem;
}
.gap-14 {
  gap: 3.5rem;
}
.gap-16 {
  gap: 4rem;
}
.gap-2 {
  gap: 0.5rem;
}
.gap-2\.5 {
  gap: 0.625rem;
}
.gap-3 {
  gap: 0.75rem;
}
.gap-4 {
  gap: 1rem;
}
.gap-5 {
  gap: 1.25rem;
}
.gap-6 {
  gap: 1.5rem;
}
.gap-7 {
  gap: 1.75rem;
}
.gap-8 {
  gap: 2rem;
}
.gap-x-4 {
  -moz-column-gap: 1rem;
       column-gap: 1rem;
}
.gap-x-5 {
  -moz-column-gap: 1.25rem;
       column-gap: 1.25rem;
}
.gap-x-6 {
  -moz-column-gap: 1.5rem;
       column-gap: 1.5rem;
}
.gap-x-8 {
  -moz-column-gap: 2rem;
       column-gap: 2rem;
}
.gap-y-1\.5 {
  row-gap: 0.375rem;
}
.gap-y-12 {
  row-gap: 3rem;
}
.gap-y-2 {
  row-gap: 0.5rem;
}
.divide-y > :not([hidden]) ~ :not([hidden]) {
  --tw-divide-y-reverse: 0;
  border-top-width: calc(1px * calc(1 - var(--tw-divide-y-reverse)));
  border-bottom-width: calc(1px * var(--tw-divide-y-reverse));
}
.divide-paper\/10 > :not([hidden]) ~ :not([hidden]) {
  border-color: rgb(var(--color-paper) / 0.1);
}
.self-start {
  align-self: flex-start;
}
.self-end {
  align-self: flex-end;
}
.self-center {
  align-self: center;
}
.overflow-hidden {
  overflow: hidden;
}
.overflow-visible {
  overflow: visible;
}
.overflow-x-auto {
  overflow-x: auto;
}
.overflow-y-auto {
  overflow-y: auto;
}
.truncate {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.whitespace-nowrap {
  white-space: nowrap;
}
.whitespace-pre-wrap {
  white-space: pre-wrap;
}
.break-all {
  word-break: break-all;
}
.rounded-2xl {
  border-radius: 1rem;
}
.rounded-3xl {
  border-radius: 1.5rem;
}
.rounded-\[1\.75rem\] {
  border-radius: 1.75rem;
}
.rounded-full {
  border-radius: 9999px;
}
.rounded-lg {
  border-radius: 0.5rem;
}
.rounded-sm {
  border-radius: 0.125rem;
}
.rounded-xl {
  border-radius: 0.75rem;
}
.rounded-t-3xl {
  border-top-left-radius: 1.5rem;
  border-top-right-radius: 1.5rem;
}
.border {
  border-width: 1px;
}
.border-0 {
  border-width: 0px;
}
.border-b {
  border-bottom-width: 1px;
}
.border-t {
  border-top-width: 1px;
}
.border-dashed {
  border-style: dashed;
}
.border-\[rgba\(28\2c 28\2c 27\2c 0\.25\)\] {
  border-color: rgba(28,28,27,0.25);
}
.border-accent\/20 {
  border-color: rgb(var(--color-accent) / 0.2);
}
.border-amber-200\/20 {
  border-color: rgb(253 230 138 / 0.2);
}
.border-amber-300\/20 {
  border-color: rgb(252 211 77 / 0.2);
}
.border-amber-300\/30 {
  border-color: rgb(252 211 77 / 0.3);
}
.border-paper\/10 {
  border-color: rgb(var(--color-paper) / 0.1);
}
.border-paper\/15 {
  border-color: rgb(var(--color-paper) / 0.15);
}
.border-paper\/20 {
  border-color: rgb(var(--color-paper) / 0.2);
}
.border-paper\/5 {
  border-color: rgb(var(--color-paper) / 0.05);
}
.bg-\[\#05060a\] {
  --tw-bg-opacity: 1;
  background-color: rgb(5 6 10 / var(--tw-bg-opacity, 1));
}
.bg-accent\/15 {
  background-color: rgb(var(--color-accent) / 0.15);
}
.bg-accent\/5 {
  background-color: rgb(var(--color-accent) / 0.05);
}
.bg-amber-300\/10 {
  background-color: rgb(252 211 77 / 0.1);
}
.bg-amber-300\/5 {
  background-color: rgb(252 211 77 / 0.05);
}
.bg-paper\/10 {
  background-color: rgb(var(--color-paper) / 0.1);
}
.bg-paper\/15 {
  background-color: rgb(var(--color-paper) / 0.15);
}
.bg-paper\/5 {
  background-color: rgb(var(--color-paper) / 0.05);
}
.bg-paper\/80 {
  background-color: rgb(var(--color-paper) / 0.8);
}
.bg-paper\/\[0\.06\] {
  background-color: rgb(var(--color-paper) / 0.06);
}
.bg-paper\/\[0\.08\] {
  background-color: rgb(var(--color-paper) / 0.08);
}
.bg-red-400 {
  --tw-bg-opacity: 1;
  background-color: rgb(248 113 113 / var(--tw-bg-opacity, 1));
}
.bg-sky-300 {
  --tw-bg-opacity: 1;
  background-color: rgb(125 211 252 / var(--tw-bg-opacity, 1));
}
.bg-sky-300\/40 {
  background-color: rgb(125 211 252 / 0.4);
}
.bg-transparent {
  background-color: transparent;
}
.bg-void {
  --tw-bg-opacity: 1;
  background-color: rgb(var(--color-void) / var(--tw-bg-opacity, 1));
}
.bg-void-soft {
  --tw-bg-opacity: 1;
  background-color: rgb(var(--color-void-soft) / var(--tw-bg-opacity, 1));
}
.bg-void-soft\/40 {
  background-color: rgb(var(--color-void-soft) / 0.4);
}
.bg-void\/40 {
  background-color: rgb(var(--color-void) / 0.4);
}
.bg-void\/60 {
  background-color: rgb(var(--color-void) / 0.6);
}
.bg-void\/70 {
  background-color: rgb(var(--color-void) / 0.7);
}
.bg-white {
  --tw-bg-opacity: 1;
  background-color: rgb(255 255 255 / var(--tw-bg-opacity, 1));
}
.bg-white\/95 {
  background-color: rgb(255 255 255 / 0.95);
}
.bg-gradient-to-br {
  background-image: linear-gradient(to bottom right, var(--tw-gradient-stops));
}
.bg-gradient-to-r {
  background-image: linear-gradient(to right, var(--tw-gradient-stops));
}
.from-\[rgba\(124\2c 198\2c 255\2c 0\.6\)\] {
  --tw-gradient-from: rgba(124,198,255,0.6) var(--tw-gradient-from-position);
  --tw-gradient-to: rgba(124, 198, 255, 0) var(--tw-gradient-to-position);
  --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
}
.from-accent\/60 {
  --tw-gradient-from: rgb(var(--color-accent) / 0.6) var(--tw-gradient-from-position);
  --tw-gradient-to: rgb(var(--color-accent) / 0) var(--tw-gradient-to-position);
  --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
}
.to-\[rgba\(31\2c 79\2c 120\2c 0\.85\)\] {
  --tw-gradient-to: rgba(31,79,120,0.85) var(--tw-gradient-to-position);
}
.to-paper\/80 {
  --tw-gradient-to: rgb(var(--color-paper) / 0.8) var(--tw-gradient-to-position);
}
.object-cover {
  -o-object-fit: cover;
     object-fit: cover;
}
.\!p-2 {
  padding: 0.5rem !important;
}
.p-1 {
  padding: 0.25rem;
}
.p-2 {
  padding: 0.5rem;
}
.p-3 {
  padding: 0.75rem;
}
.p-3\.5 {
  padding: 0.875rem;
}
.p-4 {
  padding: 1rem;
}
.p-5 {
  padding: 1.25rem;
}
.p-6 {
  padding: 1.5rem;
}
.p-7 {
  padding: 1.75rem;
}
.p-8 {
  padding: 2rem;
}
.\!px-2 {
  padding-left: 0.5rem !important;
  padding-right: 0.5rem !important;
}
.\!px-2\.5 {
  padding-left: 0.625rem !important;
  padding-right: 0.625rem !important;
}
.\!px-3 {
  padding-left: 0.75rem !important;
  padding-right: 0.75rem !important;
}
.\!py-1 {
  padding-top: 0.25rem !important;
  padding-bottom: 0.25rem !important;
}
.\!py-1\.5 {
  padding-top: 0.375rem !important;
  padding-bottom: 0.375rem !important;
}
.\!py-2 {
  padding-top: 0.5rem !important;
  padding-bottom: 0.5rem !important;
}
.px-1 {
  padding-left: 0.25rem;
  padding-right: 0.25rem;
}
.px-2 {
  padding-left: 0.5rem;
  padding-right: 0.5rem;
}
.px-2\.5 {
  padding-left: 0.625rem;
  padding-right: 0.625rem;
}
.px-3 {
  padding-left: 0.75rem;
  padding-right: 0.75rem;
}
.px-4 {
  padding-left: 1rem;
  padding-right: 1rem;
}
.px-5 {
  padding-left: 1.25rem;
  padding-right: 1.25rem;
}
.px-6 {
  padding-left: 1.5rem;
  padding-right: 1.5rem;
}
.px-7 {
  padding-left: 1.75rem;
  padding-right: 1.75rem;
}
.px-8 {
  padding-left: 2rem;
  padding-right: 2rem;
}
.py-0\.5 {
  padding-top: 0.125rem;
  padding-bottom: 0.125rem;
}
.py-1 {
  padding-top: 0.25rem;
  padding-bottom: 0.25rem;
}
.py-1\.5 {
  padding-top: 0.375rem;
  padding-bottom: 0.375rem;
}
.py-10 {
  padding-top: 2.5rem;
  padding-bottom: 2.5rem;
}
.py-16 {
  padding-top: 4rem;
  padding-bottom: 4rem;
}
.py-2 {
  padding-top: 0.5rem;
  padding-bottom: 0.5rem;
}
.py-2\.5 {
  padding-top: 0.625rem;
  padding-bottom: 0.625rem;
}
.py-24 {
  padding-top: 6rem;
  padding-bottom: 6rem;
}
.py-28 {
  padding-top: 7rem;
  padding-bottom: 7rem;
}
.py-3 {
  padding-top: 0.75rem;
  padding-bottom: 0.75rem;
}
.py-3\.5 {
  padding-top: 0.875rem;
  padding-bottom: 0.875rem;
}
.py-4 {
  padding-top: 1rem;
  padding-bottom: 1rem;
}
.py-5 {
  padding-top: 1.25rem;
  padding-bottom: 1.25rem;
}
.py-6 {
  padding-top: 1.5rem;
  padding-bottom: 1.5rem;
}
.py-7 {
  padding-top: 1.75rem;
  padding-bottom: 1.75rem;
}
.py-8 {
  padding-top: 2rem;
  padding-bottom: 2rem;
}
.pb-10 {
  padding-bottom: 2.5rem;
}
.pb-12 {
  padding-bottom: 3rem;
}
.pb-16 {
  padding-bottom: 4rem;
}
.pb-20 {
  padding-bottom: 5rem;
}
.pb-24 {
  padding-bottom: 6rem;
}
.pb-28 {
  padding-bottom: 7rem;
}
.pb-3 {
  padding-bottom: 0.75rem;
}
.pb-4 {
  padding-bottom: 1rem;
}
.pb-40 {
  padding-bottom: 10rem;
}
.pb-5 {
  padding-bottom: 1.25rem;
}
.pb-8 {
  padding-bottom: 2rem;
}
.pl-1 {
  padding-left: 0.25rem;
}
.pl-2 {
  padding-left: 0.5rem;
}
.pr-4 {
  padding-right: 1rem;
}
.pt-0\.5 {
  padding-top: 0.125rem;
}
.pt-1 {
  padding-top: 0.25rem;
}
.pt-2 {
  padding-top: 0.5rem;
}
.pt-28 {
  padding-top: 7rem;
}
.pt-3 {
  padding-top: 0.75rem;
}
.pt-36 {
  padding-top: 9rem;
}
.pt-4 {
  padding-top: 1rem;
}
.pt-40 {
  padding-top: 10rem;
}
.pt-5 {
  padding-top: 1.25rem;
}
.pt-6 {
  padding-top: 1.5rem;
}
.pt-7 {
  padding-top: 1.75rem;
}
.pt-8 {
  padding-top: 2rem;
}
.\!text-left {
  text-align: left !important;
}
.text-left {
  text-align: left;
}
.text-center {
  text-align: center;
}
.text-right {
  text-align: right;
}
.font-display {
  font-family: var(--font-display), Helvetica Neue, Arial, sans-serif;
}
.font-sans {
  font-family: var(--font-body), Helvetica Neue, Arial, sans-serif;
}
.text-2xl {
  font-size: 1.5rem;
  line-height: 2rem;
}
.text-3xl {
  font-size: 1.875rem;
  line-height: 2.25rem;
}
.text-4xl {
  font-size: 2.25rem;
  line-height: 2.5rem;
}
.text-\[0\.65rem\] {
  font-size: 0.65rem;
}
.text-\[0\.6rem\] {
  font-size: 0.6rem;
}
.text-\[0\.7rem\] {
  font-size: 0.7rem;
}
.text-\[10px\] {
  font-size: 10px;
}
.text-\[11px\] {
  font-size: 11px;
}
.text-\[8px\] {
  font-size: 8px;
}
.text-\[9px\] {
  font-size: 9px;
}
.text-base {
  font-size: 1rem;
  line-height: 1.5rem;
}
.text-lg {
  font-size: 1.125rem;
  line-height: 1.75rem;
}
.text-sm {
  font-size: 0.875rem;
  line-height: 1.25rem;
}
.text-xl {
  font-size: 1.25rem;
  line-height: 1.75rem;
}
.text-xs {
  font-size: 0.75rem;
  line-height: 1rem;
}
.font-medium {
  font-weight: 500;
}
.font-normal {
  font-weight: 400;
}
.font-semibold {
  font-weight: 600;
}
.uppercase {
  text-transform: uppercase;
}
.normal-case {
  text-transform: none;
}
.italic {
  font-style: italic;
}
.leading-none {
  line-height: 1;
}
.leading-relaxed {
  line-height: 1.625;
}
.leading-tight {
  line-height: 1.25;
}
.tracking-tightest {
  letter-spacing: -0.04em;
}
.tracking-wider2 {
  letter-spacing: 0.2em;
}
.text-\[\#1c1c1b\] {
  --tw-text-opacity: 1;
  color: rgb(28 28 27 / var(--tw-text-opacity, 1));
}
.text-\[\#2e5566\] {
  --tw-text-opacity: 1;
  color: rgb(46 85 102 / var(--tw-text-opacity, 1));
}
.text-\[\#f5f5f2\] {
  --tw-text-opacity: 1;
  color: rgb(245 245 242 / var(--tw-text-opacity, 1));
}
.text-\[rgba\(28\2c 28\2c 27\2c 0\.4\)\] {
  color: rgba(28,28,27,0.4);
}
.text-\[rgba\(28\2c 28\2c 27\2c 0\.6\)\] {
  color: rgba(28,28,27,0.6);
}
.text-accent {
  --tw-text-opacity: 1;
  color: rgb(var(--color-accent) / var(--tw-text-opacity, 1));
}
.text-amber-800 {
  --tw-text-opacity: 1;
  color: rgb(146 64 14 / var(--tw-text-opacity, 1));
}
.text-amber-800\/60 {
  color: rgb(146 64 14 / 0.6);
}
.text-amber-800\/90 {
  color: rgb(146 64 14 / 0.9);
}
.text-paper {
  --tw-text-opacity: 1;
  color: rgb(var(--color-paper) / var(--tw-text-opacity, 1));
}
.text-paper\/25 {
  color: rgb(var(--color-paper) / 0.25);
}
.text-paper\/30 {
  color: rgb(var(--color-paper) / 0.3);
}
.text-paper\/35 {
  color: rgb(var(--color-paper) / 0.35);
}
.text-paper\/40 {
  color: rgb(var(--color-paper) / 0.4);
}
.text-paper\/45 {
  color: rgb(var(--color-paper) / 0.45);
}
.text-paper\/50 {
  color: rgb(var(--color-paper) / 0.5);
}
.text-paper\/55 {
  color: rgb(var(--color-paper) / 0.55);
}
.text-paper\/60 {
  color: rgb(var(--color-paper) / 0.6);
}
.text-paper\/65 {
  color: rgb(var(--color-paper) / 0.65);
}
.text-paper\/70 {
  color: rgb(var(--color-paper) / 0.7);
}
.text-paper\/75 {
  color: rgb(var(--color-paper) / 0.75);
}
.text-paper\/80 {
  color: rgb(var(--color-paper) / 0.8);
}
.text-paper\/85 {
  color: rgb(var(--color-paper) / 0.85);
}
.text-paper\/90 {
  color: rgb(var(--color-paper) / 0.9);
}
.text-red-300\/80 {
  color: rgb(252 165 165 / 0.8);
}
.text-void {
  --tw-text-opacity: 1;
  color: rgb(var(--color-void) / var(--tw-text-opacity, 1));
}
.text-white {
  --tw-text-opacity: 1;
  color: rgb(255 255 255 / var(--tw-text-opacity, 1));
}
.text-white\/85 {
  color: rgb(255 255 255 / 0.85);
}
.underline {
  text-decoration-line: underline;
}
.line-through {
  text-decoration-line: line-through;
}
.decoration-paper\/20 {
  text-decoration-color: rgb(var(--color-paper) / 0.2);
}
.underline-offset-2 {
  text-underline-offset: 2px;
}
.underline-offset-4 {
  text-underline-offset: 4px;
}
.antialiased {
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
.accent-accent {
  accent-color: rgb(var(--color-accent) / 1);
}
.opacity-0 {
  opacity: 0;
}
.opacity-100 {
  opacity: 1;
}
.opacity-70 {
  opacity: 0.7;
}
.shadow-\[0_0_12px_rgba\(125\2c 211\2c 252\2c 0\.6\)\] {
  --tw-shadow: 0 0 12px rgba(125,211,252,0.6);
  --tw-shadow-colored: 0 0 12px var(--tw-shadow-color);
  box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
}
.outline {
  outline-style: solid;
}
.\!ring {
  --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color) !important;
  --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color) !important;
  box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000) !important;
}
.ring {
  --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
  --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color);
  box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
}
.blur {
  --tw-blur: blur(8px);
  filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
}
.blur-sm {
  --tw-blur: blur(4px);
  filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
}
.drop-shadow {
  --tw-drop-shadow: drop-shadow(0 1px 2px rgb(0 0 0 / 0.1)) drop-shadow(0 1px 1px rgb(0 0 0 / 0.06));
  filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
}
.filter {
  filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
}
.backdrop-blur-sm {
  --tw-backdrop-blur: blur(4px);
  backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);
}
.transition {
  transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter;
  transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
  transition-duration: 150ms;
}
.transition-all {
  transition-property: all;
  transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
  transition-duration: 150ms;
}
.transition-colors {
  transition-property: color, background-color, border-color, text-decoration-color, fill, stroke;
  transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
  transition-duration: 150ms;
}
.transition-opacity {
  transition-property: opacity;
  transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
  transition-duration: 150ms;
}
.transition-transform {
  transition-property: transform;
  transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
  transition-duration: 150ms;
}
.duration-150 {
  transition-duration: 150ms;
}
.duration-200 {
  transition-duration: 200ms;
}
.duration-300 {
  transition-duration: 300ms;
}
.duration-500 {
  transition-duration: 500ms;
}
.duration-700 {
  transition-duration: 700ms;
}
.ease-cinematic {
  transition-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
}

:root {
  /* ============================================================
     Mode sombre — un vrai thème, pas un simple filtre. Chaque token
     Tailwind (void/paper/concrete/accent, voir tailwind.config.ts) lit sa
     valeur ici comme un triplet "R G B" (jamais un hex) pour rester
     compatible avec la syntaxe `rgb(var(--x) / <alpha>)` que Tailwind utilise
     pour ses variantes d'opacité (`bg-void/50`, `text-paper/70`...).
     `html.dark` plus bas redéfinit CHACUNE de ces variables — c'est ce qui
     fait basculer automatiquement les ~250+ usages existants de ces tokens
     à travers tout le site, sans toucher un seul composant. */
  --color-void: 247 245 240; /* fond de page */
  --color-void-soft: 241 239 231; /* sections secondaires */
  --color-void-raised: 255 255 255; /* surfaces "élevées" (cartes) */
  --color-paper: 33 31 28; /* encre / texte principal */
  --color-paper-dim: 107 106 99; /* texte atténué */
  --color-concrete-100: 244 241 234;
  --color-concrete-300: 217 212 200;
  --color-concrete-500: 163 156 140;
  --color-concrete-700: 107 101 88;
  --color-concrete-900: 51 47 41;
  --color-accent: 46 85 102;
  --color-accent-dim: 199 219 225;

  /* Tells the browser this page is dark-themed so browser-rendered native
     controls (a <select>'s popup listbox chief among them — the one UI
     piece `.glass-surface`/`.glass-input`'s own CSS can never reach)
     default to dark colors instead of assuming a light page. Without
     this, a <select> here rendered a white popup with our near-white
     `.glass-input` text color inherited into it — white-on-white,
     illegible. Zero visual effect on anything else on the site.
     `html.dark` overrides this to `dark` below. */
  color-scheme: light;
  /* Sans ceci, l'impression navigateur n'imprime les couleurs de fond /
     bordures que si le visiteur coche lui-même "Graphiques d'arrière-plan"
     dans la boîte de dialogue d'impression (décoché par défaut dans
     Chrome). Le thème sombre du PDF en dépend entièrement : son texte est
     presque blanc, lisible uniquement sur un fond sombre réellement
     imprimé — sans ce fond, "presque blanc sur papier blanc" est
     indiscernable d'une page vide. Force le rendu "exact" quel que soit ce
     réglage, sans action requise du visiteur. */
  print-color-adjust: exact;
  -webkit-print-color-adjust: exact;
  /* --font-display/--font-body are now injected by next/font/google
     (layout.tsx, Fraunces + Inter) with these exact variable names — no
     fallback declaration needed here anymore, next/font supplies its own. */
  /* Site-wide mouse/gyroscope depth signal, damped + self-centering —
     written every frame by components/dom/DepthEngine.tsx. -1..1 on both
     axes, (0,0) = resting position. Declared here purely as documentation
     of the contract; every actual read uses `var(--depth-x, 0)` with its
     own fallback so nothing depends on this initial value. */
  --depth-x: 0;
  --depth-y: 0;

  /* Source unique de la courbe "cinématique" du site — remplace les 6
     copies littérales de ce même cubic-bezier plus bas dans ce fichier
     (voir tailwind.config.ts `transitionTimingFunction.cinematic` et
     src/animations/easings.ts `EASE_CINEMATIC`, qui documentent la même
     valeur côté Tailwind/GSAP : les trois DOIVENT rester identiques). */
  --ease-cinematic: cubic-bezier(0.16, 1, 0.3, 1);
}

/* ============================================================
   Mode sombre — palette réelle, pensée pour la lisibilité (jamais un simple
   filtre "invert()"/`filter: brightness()` sur le thème clair, qui produit
   systématiquement du gris terne et des contrastes ratés).
   REFONTE (audit) : la première version de cette palette était un gris-
   bleu si proche du noir neutre qu'elle se lisait comme "beige + inversion
   de texte" plutôt que comme un vrai thème — exactement le reproche fait.
   Toutes les valeurs ci-dessous partagent maintenant la même teinte bleu
   nuit (teinte ~220-225°, jamais un bleu saturé/agressif — saturation
   modérée, seule la LUMINOSITÉ varie d'un ton à l'autre) au lieu d'un gris
   neutre : un fond beige remplacé par du bleu nuit profond, pas du noir.
   - `void` : bleu nuit profond (jamais un noir pur #000, trop dur à l'œil
     et qui écrase tout relief) ; `raised` est le plus CLAIR des trois (une
     carte "élevée" se rapproche de la lumière), `soft` le plus sombre
     (section reculée) — inversion volontaire de la hiérarchie du thème
     clair (où `raised` est blanc pur, le plus clair) pour la même lecture
     "élévation = plus proche de la lumière" des deux côtés.
   - `paper` : ivoire chaud, jamais blanc pur (`#ffffff` sur fond sombre
     "brûle l'œil — l'exigence explicite "éviter le blanc agressif") — le
     contraste chaud (texte) / froid (fond bleu nuit) est volontaire, un
     classique des interfaces sombres premium plutôt qu'un simple négatif.
   - `concrete` : même teinte bleu nuit que `void`, rampe INVERSÉE par
     rapport au clair (100 = subtil/proche du fond, 900 = fort contraste)
     pour que chaque usage existant (`bg-concrete-100` pour un panneau
     discret, `text-concrete-700` pour un texte secondaire plus marqué...)
     garde le même RÔLE sémantique dans les deux thèmes, jamais le même hex.
   - `accent` : le bleu clair (#7cc6ff) déjà utilisé partout ailleurs sur le
     site comme lueur/accent (zebra, dossier, Siri-glow) — assez distinct du
     bleu nuit du fond pour rester lisible comme accent, jamais le bleu
     ardoise sombre du thème clair qui s'y fondrait.
   Chaque valeur ci-dessous a été vérifiée à au moins 4.5:1 sur son fond
   `--color-void` (souvent bien au-delà) — voir le commentaire de chaque
   token pour le ratio approximatif, exigence "contraste suffisant". */
html.dark {
  color-scheme: dark;
  --color-void: 14 20 36; /* #0e1424 — bleu nuit profond, le fond principal du thème sombre */
  --color-void-soft: 9 13 24; /* #090d18 — section reculée, plus sombre que le fond */
  --color-void-raised: 24 33 54; /* #182136 — carte "élevée", plus claire que le fond */
  --color-paper: 240 238 231; /* #f0eee7 — ivoire chaud, jamais blanc pur — ~15:1 sur le fond */
  --color-paper-dim: 172 176 194; /* #acb0c2 — texte atténué, teinté bleu nuit plutôt que gris neutre, ~7.3:1 */
  --color-concrete-100: 20 27 44; /* #141b2c — fond de panneau discret */
  --color-concrete-300: 40 52 78; /* #28344e — bordures/séparateurs visibles */
  --color-concrete-500: 142 152 178; /* #8e98b2 — texte moyen, ~5.7:1 */
  --color-concrete-700: 196 203 221; /* #c4cbdd — texte secondaire marqué */
  --color-concrete-900: 236 239 247; /* #ecEFF7 — emphase maximale, jamais blanc pur */
  --color-accent: 124 198 255; /* #7cc6ff — ~9:1 sur le fond */
  --color-accent-dim: 20 32 54; /* #142036 — teinte accent sombre pour fonds discrets */
}

* {
  box-sizing: border-box;
}

html,
body {
  padding: 0;
  margin: 0;
  /* "Pierre & Ivoire" — warm off-white → ivory → stone, fixed so it never
     tiles/scrolls oddly on a tall page. */
  background: linear-gradient(180deg, #faf9f5 0%, #f2efe7 55%, #ece7db 100%);
  background-attachment: fixed;
  color: #211f1c;
  /* La transition de fond/texte au changement de thème doit rester rapide
     mais visible — jamais un "clic" instantané qui donne l'impression d'un
     bug, ni une transition longue qui laisse un entre-deux illisible (même
     principe que le correctif `.print-sheet` plus bas dans ce fichier, qui
     documente pourquoi un état intermédiaire entre deux thèmes est
     dangereux). */
  transition: background-color 0.25s ease, color 0.25s ease;
  overscroll-behavior: none;
  /* Passe UX responsive — filet de sécurité contre tout débordement
     horizontal (exigence explicite : "aucun débordement horizontal" à
     chaque changement de section). Le site utilise beaucoup de
     transforms/parallax (DepthEngine, cartes flottantes, séquences
     canvas plein écran) qui peuvent, sur un sous-pixel ou un écart
     d'arrondi, dépasser légèrement la largeur du viewport ; ce filet ne
     change rien au layout normal (rien ici ne déborde intentionnellement
     en largeur) mais garantit qu'un dépassement accidentel ne crée
     jamais de scroll horizontal ni de bande blanche sur le côté.
     CAUSE RÉELLE DU BUG "SCROLL 3D CASSÉ" (audit) : `overflow-x: hidden`
     seul, sur `html`/`body`, est la cause racine du scroll 3D cassé sur
     toute la page — pas un bug dans HeroSequence.tsx/StorySequence.tsx
     eux-mêmes, dont la logique (section haute + enfant `position: sticky`
     + progression calculée via `getBoundingClientRect`) est correcte.
     Le CSS Overflow Module force `overflow-y` à `auto` dès que
     `overflow-x` vaut autre chose que `visible` et que `overflow-y` ne
     l'est pas déjà explicitement — `html`/`body` devenaient donc chacun
     un "conteneur de défilement" au sens strict. Or `position: sticky`
     ne s'épingle QUE par rapport au plus proche ancêtre qui est un
     conteneur de défilement : dès que `body` (dont la boîte ne défile
     jamais réellement, c'est `html`/le document qui défile) en devient
     un lui aussi, les enfants `sticky` de HeroSequence/StorySequence
     perdent leur ancrage au vrai viewport et se comportent comme un
     `position: static` — la section haute de 300dvh+ défile alors
     entièrement "normalement", révélant tout le fond sombre de son
     enfant sticky comme un immense bloc noir qui remonte avec le reste
     de la page, exactement le symptôme décrit ("toute la page continue à
     défiler", "on arrive vers une section entièrement noire").
     `overflow-x: clip` (Chrome 90+/Firefox 69+/Safari 16+, largement
     répandu) coupe le débordement horizontal SANS créer de conteneur de
     défilement ni forcer `overflow-y` — c'est la correction structurelle
     réelle, pas un correctif visuel. `overflow-x: hidden` reste en repli
     pour les navigateurs plus anciens qui ignorent silencieusement la
     valeur `clip` (auquel cas le scroll 3D resterait cassé sur ces
     navigateurs résiduels, mais le site resterait au moins protégé du
     scroll horizontal). */
  overflow-x: hidden;
  overflow-x: clip;
}

/* Le fond littéral de `<html>` (hérité de la règle `html, body` ci-dessus)
   n'était JAMAIS redéfini par le thème sombre — seul `html.dark body`
   existait plus bas. `<body>` recouvre normalement `<html>`, mais dès que
   son fond ne couvre pas parfaitement tout le canevas (rebond élastique
   iOS/macOS en haut/bas de page, `background-attachment: fixed` pendant un
   changement de layout, marge d'arrondi) c'est le beige de `<html>` qui
   apparaît PENDANT que le reste de la page est sombre — exactement le bug
   décrit ("certains éléments passent en clair mais le fond principal reste
   beige"). `<html>` porte maintenant sa propre valeur sombre, jamais plus
   la valeur claire figée. */
html.dark {
  background: linear-gradient(180deg, #10192c 0%, #0c1424 55%, #080e1a 100%);
}

/* Fond sombre — même dégradé subtil "profondeur" que le clair, jamais un
   simple aplat, mais un vrai bleu nuit plutôt que de l'ivoire (et plutôt
   qu'un gris/noir neutre — voir la refonte de palette ci-dessus). Mêmes
   valeurs que `html.dark` ci-dessus (jamais deux bleus nuit différents
   qui se contrediraient à la jonction `html`/`body`).
   `!important`-free : ces règles n'ont besoin de gagner que sur la règle
   `html, body` ci-dessus, ce qui est déjà garanti par l'ordre de la feuille
   (`html.dark body` a une spécificité strictement supérieure). */
html.dark body {
  background: linear-gradient(180deg, #10192c 0%, #0c1424 55%, #080e1a 100%);
  color: rgb(var(--color-paper));
}

/* Hard scroll lock while the Loader is up — see lib/scroll-controller.ts
   setScrollLocked. Belt-and-braces alongside that controller's own
   locked flag, since that alone doesn't block every scroll input path
   (keyboard, native touch fallback) in every browser. */
html.scroll-locked,
html.scroll-locked body {
  overflow: hidden !important;
  height: 100%;
}

::-webkit-scrollbar {
  width: 0px;
  background: transparent;
}

/* NOTE: deliberately does NOT set `position` here. It used to force
   `position: relative`, which — because these rules compile in *after*
   Tailwind's utilities — silently overrode `fixed`/`absolute` utility
   classes on the same element (equal specificity, later in the cascade
   wins). Position is controlled entirely by whichever Tailwind utility
   each usage adds (`fixed`, `absolute`, `sticky`, ...); this class only
   ever contributes z-index + the pointer-events passthrough pattern. */
.dom-layer {
  z-index: 10;
  pointer-events: none;
}

.dom-layer a,
.dom-layer button,
.dom-layer [role="button"],
[role="button"].dom-layer {
  pointer-events: auto;
}

/* Custom cursor — native cursor is hidden only once CustomCursor has
   mounted and confirmed a fine pointer (mouse/trackpad), so touch devices
   and the brief SSR/hydration gap never end up with no cursor at all. */
body.custom-cursor-active,
body.custom-cursor-active a,
body.custom-cursor-active button {
  cursor: none;
}

.cursor-dot,
.cursor-ring {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 9999;
  pointer-events: none;
  border-radius: 9999px;
  /* Centering (-50%, -50%) is applied via GSAP (xPercent/yPercent) in
     CustomCursor.tsx, not here — GSAP owns `transform` on these elements
     once mounted, and a static CSS transform would just get overwritten. */
  /* `difference` blend against whatever is under the cursor — a light
     ink dot on the "Pierre & Ivoire" pages, a light dot on the dark 3D
     scroll sequence, always legible either way instead of a single fixed
     color that only worked for one of the two backgrounds. */
  mix-blend-mode: difference;
}

.cursor-dot {
  width: 6px;
  height: 6px;
  background: #ffffff;
}

.cursor-ring {
  width: 32px;
  height: 32px;
  border: 1.5px solid rgba(255, 255, 255, 0.9);
  transition: width 0.25s ease, height 0.25s ease, border-color 0.25s ease;
}

.cursor-ring--hover {
  width: 52px;
  height: 52px;
  border-color: #ffffff;
}

/* Contrast correction for `text-paper/NN` opacity utilities — these
   percentages were tuned back when `paper` was near-white text on a dark
   background (old dark theme); flipping `paper` to dark ink on a light
   background (see tailwind.config.ts) kept the SAME percentages, which now
   blend a dark color into a light one instead, reading as washed-out grey
   rather than legible secondary text. Raising each tier here (same
   "compiles after Tailwind's utilities, wins on equal specificity" trick
   documented above for `.dom-layer`) fixes every occurrence of these
   classes across the app from this one place, instead of touching each of
   the ~250 individual usages. Never applies to the immersive 3D sequence
   (HeroSequence/StorySequence/TransitionSection), which uses literal
   `text-[#f5f5f2]/NN` arbitrary values, not this token.

   BUG CORRIGÉ (passe mode sombre) — ces règles peignaient un `rgb(33 31 28 /
   NN%)` LITTÉRAL, indépendant du thème choisi. `--color-paper` vaut une
   encre sombre en thème clair mais un ivoire clair en thème sombre (voir
   `html.dark` plus haut) — en gardant l'ancien littéral, ces ~250 usages de
   `text-paper/NN` à travers le site auraient peint de l'encre sombre sur un
   fond sombre en mode nuit : exactement le "texte noir sur fond sombre"
   explicitement à éviter. `rgb(var(--color-paper) / NN%)` lit la bonne
   couleur pour le thème actif — même pourcentages des deux côtés (du texte
   clair sur fond sombre à ces mêmes opacités reste largement au-dessus du
   seuil AA, la direction inverse du problème d'origine). */
/* CAUSE RÉELLE D'UN AUTRE ÉCHEC DE BUILD EN PRODUCTION (même famille que le
   correctif aspect-[X/Y] plus bas dans ce fichier) : ces 12 sélecteurs
   utilisaient l'échappement `\/` (`.text-paper\/25`, valide en CSS strict
   pour cibler littéralement la classe `text-paper/25`) — cssnano, sur le
   serveur de déploiement réel, plantait avec exactement la même erreur
   ("Unexpected '/'. Escaping special characters with \ may help.") en
   tentant de reparser ce sélecteur échappé pendant sa passe de fusion de
   règles. `[class~="text-paper/25"]` cible EXACTEMENT la même chose (un
   élément portant cette classe parmi d'autres, `~=` sur `class` étant conçu
   pour ça) mais sans aucun `/` en position de sélecteur : ici il vit à
   l'intérieur d'une chaîne entre guillemets, où cssnano ne le traite jamais
   comme un caractère spécial. Comportement strictement identique, mécanisme
   CSS différent. */
[class~="text-paper/25"] { color: rgb(var(--color-paper) / 45%); }
[class~="text-paper/30"] { color: rgb(var(--color-paper) / 50%); }
[class~="text-paper/35"] { color: rgb(var(--color-paper) / 55%); }
[class~="text-paper/40"] { color: rgb(var(--color-paper) / 62%); }
[class~="text-paper/45"] { color: rgb(var(--color-paper) / 66%); }
[class~="text-paper/50"] { color: rgb(var(--color-paper) / 70%); }
[class~="text-paper/55"] { color: rgb(var(--color-paper) / 74%); }
[class~="text-paper/60"] { color: rgb(var(--color-paper) / 78%); }
[class~="text-paper/70"] { color: rgb(var(--color-paper) / 85%); }
[class~="text-paper/75"] { color: rgb(var(--color-paper) / 88%); }
[class~="text-paper/80"] { color: rgb(var(--color-paper) / 91%); }
[class~="text-paper/90"] { color: rgb(var(--color-paper) / 96%); }

/* Site-wide depth/parallax — see components/dom/DepthEngine.tsx. Any
   element can opt in by adding this class plus its own `--depth-strength`
   (how many px it moves per unit of --depth-x/--depth-y — bigger = reads
   as closer to the viewer, smaller/absent = reads as background). Layers
   at different strengths on the same screen is what sells "depth": video
   canvases sit low (background), overlaid text sits higher (foreground).

   NOTE: only ever apply this to an element whose `transform` isn't ALSO
   being driven from JS (inline style) — inline styles beat stylesheet
   rules regardless of specificity, so a GSAP tween left running on the
   same element would permanently freeze this. ImmersiveNav's entrance
   tween uses `clearProps: "transform"` specifically to hand control back
   to this rule once it finishes — see ImmersiveNav.tsx. */
.depth-layer {
  transform: translate3d(
      calc(var(--depth-x, 0) * var(--depth-strength, 1) * 1px),
      calc(var(--depth-y, 0) * var(--depth-strength, 1) * 1px),
      0
    )
    rotate(calc(var(--depth-x, 0) * var(--depth-rotate-strength, 0) * 1deg));
}

/* ============================================================
   Glass design system — the ONE premium "floating glass capsule" language
   used by every interactive surface site-wide: nav capsules, every button
   (Loader, SoundToggle, the project-start flow), and that flow's
   selectable option cards. Defined once here so nothing re-derives its
   own glassmorphism recipe — see README "Design system boutons/cartes".

   Composition, not a single class: pick a surface (.glass-surface, always)
   + a shape (.glass-pill / .glass-card / .glass-icon) + optionally
   .glass-accent for the one "primary action" tint. */
.glass-surface {
  position: relative;
  overflow: hidden; /* clips the ::before reflection to the shape's own rounded corners */
  background: rgba(255, 255, 255, 0.55);
  border: 1px solid rgba(33, 31, 28, 0.08);
  backdrop-filter: blur(20px) saturate(160%);
  -webkit-backdrop-filter: blur(20px) saturate(160%);
  box-shadow:
    inset 0 1px 1px rgba(255, 255, 255, 0.6),
    0 8px 20px -14px rgba(33, 31, 28, 0.35);
  color: rgba(33, 31, 28, 0.86);
  text-decoration: none;
  cursor: pointer;
  transition: transform 0.4s var(--ease-cinematic), background-color 0.35s ease,
    border-color 0.35s ease, box-shadow 0.35s ease, color 0.35s ease, filter 0.35s ease;
}

/* Soft moving highlight — "une réflexion qui suit doucement la souris sur
   les éléments en verre". A dedicated pseudo-element with its own
   compositor-only `transform` (see components/dom/DepthEngine.tsx) rather
   than recomputing a `background-position` every frame, which would force
   a repaint instead. Percentage-sized so it scales with whatever shape
   it's sitting on — a small pill button or a large option card. */
.glass-surface::before {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 140%;
  height: 140%;
  background: radial-gradient(circle, rgba(255, 255, 255, 0.55), rgba(255, 255, 255, 0) 70%);
  transform: translate3d(calc(-50% + var(--depth-x, 0) * 22px), calc(-50% + var(--depth-y, 0) * 16px), 0);
  pointer-events: none;
}

.glass-surface:hover {
  background: rgba(255, 255, 255, 0.75);
  border-color: rgba(33, 31, 28, 0.16);
  color: rgba(33, 31, 28, 1);
  filter: brightness(1.03);
}

.glass-surface:disabled,
.glass-surface[aria-disabled="true"] {
  cursor: not-allowed;
  opacity: 0.4;
  filter: none;
}

/* Pill — the canonical button shape everywhere on the site. `will-change:
   transform` is set on the RESTING state (not just `:hover`) so the browser
   promotes these to their own compositor layer ahead of time — the first
   hover/tap on any pill no longer has to pay for that promotion mid-
   transition, which is what read as a small stutter on first touch. */
/* min-height 2.75rem (44px) — la plus petite cible tactile recommandée
   (WCAG 2.5.5 / Apple HIG). `.glass-pill` sert de bouton sur ~150 usages à
   travers le site ; avant ce correctif il ne faisait qu'environ 38px de
   haut (0.7rem de padding + texte 13px), sous ce seuil partout où il sert
   de CTA principal. Un seul endroit à corriger pour que toutes ces cibles
   deviennent confortables au doigt, pas seulement à la souris. */
/* Correctif lisibilité (audit) : `white-space: nowrap` + `.glass-surface`
   (overflow: hidden) sur le même nœud coupait silencieusement, sans "…",
   tout texte de bouton plus long que sa boîte — d'autant plus qu'en enfant
   flex, une boîte avec overflow non-visible a une largeur minimale
   automatique de 0, donc le navigateur la laissait rétrécir sous la
   largeur de son propre texte plutôt que de le renvoyer à la ligne
   (cartes tarifaires, "Choisir le dossier vérifié — 290 €", CTA du menu,
   etc.). `white-space: normal` autorise le texte à s'enrouler sur
   plusieurs lignes — `min-height` (pas `height`) laisse déjà le bouton
   grandir en conséquence — et `flex-shrink: 0` empêche un parent
   `flex`/`flex-wrap` de comprimer le bouton lui-même : dans une rangée,
   c'est tout le bouton qui passe à la ligne suivante plutôt que son texte
   qui soit tronqué. `max-width: 100%` reste un filet de sécurité pour
   qu'un texte exceptionnellement long ne déborde jamais d'un conteneur
   étroit (ex. une modale mobile). */
/* Audit lisibilité (public non-initié au "web moderne") : 0.8125rem (13px)
   était encore trop petit pour un texte de bouton — la cible tactile de
   44px n'aide pas si le texte lui-même reste difficile à lire pour
   quelqu'un qui plisse déjà les yeux sur un écran de téléphone. Remonté à
   0.9375rem (15px) + font-weight 600 par défaut (au lieu d'hériter le 400
   d'Inter, trop fin pour un texte d'action) ; min-height suit à 2.875rem
   pour garder un padding vertical confortable autour du texte agrandi.
   `text-xs`/`uppercase`/`tracking-wider2` ajoutés au cas par cas sur un
   bouton n'a plus d'effet sur la taille/graisse (cette règle, compilée
   après les utilities Tailwind dans ce fichier, gagne à spécificité
   égale) — c'est voulu : un seul endroit décide de la taille réelle d'un
   bouton, jamais une classe utilitaire copiée-collée au cas par cas. */
.glass-pill {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  min-height: 2.875rem;
  max-width: 100%;
  padding: 0.75rem 1.4rem;
  border-radius: 9999px;
  font-size: 0.9375rem;
  font-weight: 600;
  font-family: var(--font-body);
  letter-spacing: 0.01em;
  text-transform: none;
  white-space: normal;
  text-align: center;
  flex-shrink: 0;
  will-change: transform;
}

/* Variante "grand" — réservée à L'action principale d'une page (hiérarchie
   demandée par l'audit : chaque page doit avoir une action principale qui
   se remarque en priorité, distincte des actions secondaires). Un simple
   `text-lg` Tailwind sur un `.glass-pill` n'aurait aucun effet (voir la
   note au-dessus de `.glass-pill` : cette feuille de style, compilée après
   les utilities, gagne toujours à spécificité égale) — d'où cette classe
   dédiée, à ajouter EN PLUS de `.glass-pill` (`Button.tsx` size="lg"). */
.glass-pill--lg {
  min-height: 3.25rem;
  padding: 0.9rem 2rem;
  font-size: 1.0625rem;
}

/* Icon-only variant (SoundToggle, a future "back" control, ...) — fixed
   square footprint instead of the pill's auto-width text padding. Bumped
   from 2.5rem (40px) to 2.75rem (44px) for the same tap-target reason as
   `.glass-pill` above. */
.glass-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.75rem;
  height: 2.75rem;
  border-radius: 9999px;
  will-change: transform;
}

/* Badge/tag — pour tout usage NON interactif ("statut", "bientôt
   disponible", compteur...) qui empruntait jusqu'ici `.glass-pill` avec un
   padding réduit au cas par cas (3 valeurs différentes trouvées lors de
   l'audit : px-2.5 py-1 / px-3 py-1 / px-2 py-0.5). Un seul padding
   compact, jamais de min-height 44px (un badge ne se touche pas) ni de
   curseur pointeur/lift au survol. */
.glass-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.35rem;
  padding: 0.25rem 0.65rem;
  border-radius: 9999px;
  font-size: 0.7rem;
  font-family: var(--font-body);
  letter-spacing: 0.02em;
  white-space: nowrap;
  cursor: default;
}

/* Card — larger selectable surface for the project-start flow. Same
   proactive `will-change` reasoning as `.glass-pill` above — this class
   backs every option card AND (via `.project-type-card`) the homepage's
   18-card grid, the single densest hover surface on the site. */
.glass-card {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.75rem;
  padding: 1.75rem;
  border-radius: 1.75rem;
  text-align: left;
  cursor: pointer;
  will-change: transform;
}

/*
 * Compact variant (step 2's 11-option grid — icon + label, no
 * description). A dedicated modifier class rather than Tailwind
 * `items-center`/`text-center` utilities on the same element: this
 * stylesheet's own custom rules compile in AFTER `@tailwind utilities`
 * (see the .dom-layer comment further up for the same cascade issue
 * caught once already), so a plain `.glass-card` rule would always beat a
 * same-specificity Tailwind utility class regardless of source order in
 * the JSX — only another rule IN this file, ordered after, reliably wins.
 */
.glass-card--compact {
  align-items: center;
  gap: 0.6rem;
  padding: 1.25rem 0.9rem;
  border-radius: 1.25rem;
  text-align: center;
}

/* Hover/active per shape — kept separate (rather than one shared rule) so
   each shape's `transform` doesn't need `!important` gymnastics to win;
   ordered after .glass-surface:hover/:disabled above so these lifts and
   compressions take precedence when both apply. */
.glass-pill:hover,
.glass-icon:hover {
  transform: translateY(-1px) scale(1.035);
}

.glass-pill:active,
.glass-icon:active {
  transform: translateY(0) scale(0.965);
  transition-duration: 0.15s;
}

.glass-card:hover {
  transform: translateY(-4px);
  box-shadow:
    inset 0 1px 1px rgba(255, 255, 255, 0.7),
    0 24px 48px -20px rgba(33, 31, 28, 0.28);
}

.glass-card:active {
  transform: translateY(-1px) scale(0.985);
  transition-duration: 0.15s;
}

/* Brief "just picked this" flash before a card's step transitions away —
   toggled from JS for the duration of the exit animation only. */
.glass-card--selected {
  border-color: rgba(63, 110, 130, 0.55);
  box-shadow:
    inset 0 1px 1px rgba(255, 255, 255, 0.7),
    0 0 0 1px rgba(63, 110, 130, 0.3),
    0 24px 48px -20px rgba(33, 31, 28, 0.3);
}

/* The recommended offer at StepReveal's 3-card screen — a held (not
   flashed) version of .glass-card--selected's glow, plus a touch of scale
   so it visually leads the other two rather than just having a badge. */
.offer-card--highlight {
  border-color: rgba(63, 110, 130, 0.55);
  box-shadow:
    inset 0 1px 1px rgba(255, 255, 255, 0.7),
    0 0 0 1px rgba(63, 110, 130, 0.32),
    0 32px 64px -24px rgba(33, 31, 28, 0.3);
  /* Réduit de 1.03 à 1.015 (passe de finition compacité) — la carte
     "Recommandé" ne doit plus donner l'impression d'un panneau
     disproportionné à côté des deux autres, juste une mise en avant subtile.
     Le composant React ne pose plus `md:scale-105` en plus de cette règle
     (les deux se cumulaient en spécificité égale, gagnées par CELLE-CI mais
     source de confusion) — une seule source de vérité pour cette échelle. */
  transform: scale(1.015);
}

/* DossierPreviewGallery.tsx's large document cards — "un effet de dossier
   physique" on desktop only (hover-capable + fine pointer), never on
   touch, where there's no equivalent gesture. */
.plan-preview-card {
  transition:
    transform 0.35s var(--ease-cinematic),
    filter 0.35s var(--ease-cinematic);
  will-change: transform, filter;
}

@media (hover: hover) and (pointer: fine) {
  .plan-preview-card:hover {
    transform: translateY(-8px) scale(1.015) rotate(0deg) !important;
    filter: drop-shadow(0 40px 70px rgba(33, 31, 28, 0.28));
  }
}

/* The one "primary action" tint — usable on a pill or a card. Now the
   deepest-toned element on the page by design (the page itself is light),
   so it still reads unambiguously as "press me". */
.glass-accent {
  color: #faf9f5;
  font-weight: 600;
  background: linear-gradient(180deg, #4a7d92, #2e5566);
  border-color: rgba(46, 85, 102, 0.85);
  box-shadow:
    inset 0 1px 1px rgba(255, 255, 255, 0.25),
    0 10px 24px -10px rgba(46, 85, 102, 0.55);
}

.glass-accent:hover {
  background: linear-gradient(180deg, #558aa0, #35606f);
  color: #faf9f5;
}

/* Variante destructive — même structure que `.glass-accent` (couleur de
   fond pleine, jamais juste un texte teinté en rouge) pour que "Supprimer"
   se reconnaisse au premier coup d'œil comme une action à part, avec la
   même qualité visuelle que l'action primaire plutôt qu'un texte coloré
   improvisé (voir DeleteButton.tsx, seul bouton destructif du site). */
.glass-destructive {
  color: #fdf4f3;
  font-weight: 600;
  background: linear-gradient(180deg, #b3554a, #8a3a30);
  border-color: rgba(138, 58, 48, 0.85);
  box-shadow:
    inset 0 1px 1px rgba(255, 255, 255, 0.2),
    0 10px 24px -10px rgba(138, 58, 48, 0.5);
}

.glass-destructive:hover {
  background: linear-gradient(180deg, #c1655a, #9c4438);
  color: #fdf4f3;
}

/* ============================================================
   Focus clavier visible — avant ce correctif, une seule classe
   `focus-visible:` existait dans tout le site (un cas isolé, hors de ce
   fichier) : `.glass-input:focus` change de fond mais n'a pas d'anneau
   franc, et aucun bouton/CTA/lien n'affichait de repère de focus clavier
   du tout. `:focus-visible` (pas `:focus`) — l'anneau n'apparaît qu'à la
   navigation clavier, jamais après un simple clic/tap souris ou tactile,
   donc aucun changement visuel pour l'immense majorité des visiteurs qui
   n'utilisent jamais Tab. */
.glass-surface:focus-visible,
.glass-input:focus-visible,
a:focus-visible,
button:focus-visible,
[role="button"]:focus-visible,
input:focus-visible,
textarea:focus-visible,
select:focus-visible {
  outline: 2px solid rgba(63, 110, 130, 0.85);
  outline-offset: 2px;
}

/* ============================================================
   `prefers-reduced-motion` — absent du site jusqu'ici : chaque animation
   continue (halos qui pulsent/tournent, zoom d'image au survol, lift des
   boutons/cartes...) tournait sans condition, y compris pour les visiteurs
   ayant explicitement demandé moins de mouvement au niveau de leur système
   (réglage pensé, entre autres, pour les personnes sujettes au mal des
   transports/vertige visuel — un public qui recoupe largement "peu à
   l'aise avec le numérique", la cible prioritaire de cette passe). Raccourcit
   toute transition/animation CSS à une durée quasi nulle plutôt que de les
   supprimer une par une : aucun risque d'oublier une des ~15 animations
   `@keyframes` de ce fichier. Les boucles JS ambiantes équivalentes
   (CustomCursor, DepthEngine) ont leur propre vérification, voir
   use-prefers-reduced-motion.ts. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}

/* ============================================================
   Implantation simulator — Plan/Satellite/Urbanisme view toggle
   (ImplantationMap.tsx). Previously a plain `.glass-pill`-scale group —
   too discreet against a busy map background. A dedicated, larger,
   higher-contrast treatment for just this control, same 3 modes/behaviour. */
.map-view-toggle {
  display: flex;
  overflow: hidden;
  border-radius: 9999px;
  border: 1px solid rgba(33, 31, 28, 0.14);
  box-shadow: 0 14px 30px -16px rgba(33, 31, 28, 0.42);
}

.map-view-toggle-btn {
  padding: 0.6rem 1.15rem;
  font-size: 0.8125rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: rgba(33, 31, 28, 0.65);
  transition: background-color 0.25s ease, color 0.25s ease, box-shadow 0.25s ease;
}

.map-view-toggle-btn:hover {
  color: rgba(33, 31, 28, 0.92);
}

.map-view-toggle-btn--active {
  color: #faf9f5;
  background: linear-gradient(180deg, #4a7d92, #2e5566);
  box-shadow:
    inset 0 0 0 1px rgba(255, 255, 255, 0.25),
    inset 0 1px 1px rgba(255, 255, 255, 0.3);
}

/* The standalone "Urbanisme" pill, sized to match the segmented control
   above it for a cohesive group — active state adds a visible contrast
   ring on top of .glass-accent's own tint, not just the fill alone. */
.map-view-toggle-standalone {
  padding: 0.6rem 1.15rem;
  font-size: 0.8125rem;
  font-weight: 600;
}

.map-view-toggle-standalone--active {
  box-shadow:
    inset 0 1px 1px rgba(255, 255, 255, 0.25),
    0 0 0 2px rgba(122, 178, 199, 0.55),
    0 12px 26px -10px rgba(46, 85, 102, 0.55);
}

/* ============================================================
   Card deck — "Démarrer un projet"'s persistent rail
   (components/start/deck/). Every answered step becomes a small physical
   card that joins this rail instead of a step-counter dot row; clicking a
   filled card re-opens that step for editing. Built on `.glass-card` (the
   .deck-card--* modifiers below only add the "stack of physical cards"
   touches — thickness edge, quieter permanent border, ghost/pulse states —
   `.glass-card--compact`'s own padding/centering already does the rest). */
.deck-card {
  position: relative;
}

/* A thin lighter "edge" offset behind the card — reads as a card resting
   on top of others in the stack, without paying for another blur layer. */
.deck-card::after {
  content: "";
  position: absolute;
  inset: 3px -3px -3px 3px;
  background: rgba(33, 31, 28, 0.05);
  border-radius: inherit;
  z-index: -1;
}

.deck-card__value {
  overflow: hidden;
  max-width: 100%;
  white-space: nowrap;
  text-overflow: ellipsis;
  font-size: 0.8125rem;
  /* Was 0.55 — too faint at this size against the light theme ("clair sur
     clair, compliqué de voir"), raised for real legibility rather than a
     merely-technical-contrast pass. */
  color: rgba(33, 31, 28, 0.78);
}

/* Filled — a permanently-resting card, quieter than the momentary
   `.glass-card--selected` flash (which stays reserved for "just picked
   this" elsewhere, e.g. OptionCard/ProjectTypeCard). */
.deck-card--filled {
  border-color: rgba(33, 31, 28, 0.14);
}

/* Empty/not-yet-reached slot — a placeholder, not a button. */
.deck-card--ghost {
  border-style: dashed;
  border-color: rgba(33, 31, 28, 0.28);
  background: transparent;
  /* Was 0.5 — combined with the label's own text-paper/70, the not-yet-
     reached cards were nearly invisible against the light theme. */
  opacity: 0.65;
  cursor: default;
}

.deck-card--ghost::after {
  content: none;
}

/* The slot currently "out of the deck, in the visitor's hands" — reuses
   .glass-card--selected's exact glow language, breathing via the keyframe
   below rather than held static, so it reads as alive/in-progress rather
   than merely selected. */
.deck-card--active-ghost {
  cursor: default;
  border-color: rgba(63, 110, 130, 0.45);
  animation: deck-card-pulse 2.4s ease-in-out infinite;
}

.deck-card--active-ghost::after {
  content: none;
}

@keyframes deck-card-pulse {
  0%,
  100% {
    box-shadow:
      inset 0 1px 1px rgba(255, 255, 255, 0.6),
      0 0 0 1px rgba(63, 110, 130, 0.16),
      0 24px 48px -20px rgba(33, 31, 28, 0.3);
  }
  50% {
    box-shadow:
      inset 0 1px 1px rgba(255, 255, 255, 0.7),
      0 0 0 1px rgba(63, 110, 130, 0.36),
      0 24px 48px -20px rgba(33, 31, 28, 0.3);
  }
}

/* Flying clone used by DeckFlightLayer while a card travels from the
   active stage into its deck slot — positioned/sized entirely from JS
   (measured rects), this only supplies its visual face. */
.deck-flight-card {
  position: fixed;
  z-index: 60;
  pointer-events: none;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.35rem;
  border-radius: 1.25rem;
  text-align: center;
  will-change: transform, opacity;
}

/* The zebra hotspot's ambient "this is interactive" hint (see
   components/story/ZebraHotspot.tsx) — a soft, continuous pulse.
   Deliberately a separate element/rule from the GSAP-driven hover glow
   rather than trying to reuse one element for both: mixing a running CSS
   keyframe animation and GSAP tweens on the same property of the same
   element fights over which one wins each frame.

   Raised from the original 0.14–0.28 opacity range — that read as too
   subtle to notice unprompted (most visitors never discovered the zebra
   was interactive at all), and discoverability now matters more than
   understatement: "il faut que l'on soit guidé au plus pour cliquer sur
   le zèbre afin que le max de personnes remplisse le formulaire". */
@keyframes zebra-pulse {
  0%,
  100% {
    opacity: 0.24;
    transform: scale(0.92);
  }
  50% {
    opacity: 0.48;
    transform: scale(1.08);
  }
}

.zebra-glow-idle {
  background: radial-gradient(circle, rgba(124, 198, 255, 0.4), rgba(124, 198, 255, 0) 70%);
  animation: zebra-pulse 3.2s ease-in-out infinite;
  pointer-events: none;
}

.zebra-glow-hover {
  background: radial-gradient(circle, rgba(124, 198, 255, 0.55), rgba(124, 198, 255, 0) 70%);
  pointer-events: none;
}

/* The zebra hotspot's "Démarrer mon projet" CTA bubble (components/story/
   ZebraHotspot.tsx) — two nested pieces so a running CSS keyframe and a
   GSAP tween never fight over the same element's `transform` (see the
   comment on the JSX itself, same reasoning as the idle/hover glow split
   above):
     - `.zebra-cta-glow` (outer, GSAP-driven show/hide) — an ambient
       drop-shadow glow that reads as "light coming off the bubble",
       selling "semble flotter au-dessus de la scène" rather than a flat
       UI element pasted in a corner.
     - `.zebra-cta-float` (inner, pure CSS) — a slow, continuous vertical
       bob while the bubble is visible, reinforcing the same "floating"
       read with actual motion. */
.zebra-cta-glow {
  border-radius: 9999px;
  filter: drop-shadow(0 10px 28px rgba(124, 198, 255, 0.35)) drop-shadow(0 3px 10px rgba(0, 0, 0, 0.5));
}

@keyframes zebra-cta-bob {
  0%,
  100% {
    transform: translateY(0);
  }
  50% {
    transform: translateY(-5px);
  }
}

.zebra-cta-float {
  animation: zebra-cta-bob 3s ease-in-out infinite;
}

/* Text-input shape for the glass design system — the address step's
   search field (components/start/StepAddress.tsx) is the first field in
   the site, so unlike `.glass-pill`/`.glass-card`/`.glass-icon` (all
   buttons), this one needs its own focus state instead of hover/active. */
.glass-input {
  display: block;
  width: 100%;
  border-radius: 9999px;
  padding: 0.9rem 1.5rem;
  text-align: center;
  font-family: var(--font-body);
  font-size: 0.9rem;
  color: rgba(33, 31, 28, 0.92);
}

/* Passe UX responsive — iOS Safari zoome automatiquement la page au focus
   sur tout <input>/<select>/<textarea> dont le font-size calculé est
   inférieur à 16px. `.glass-input` est utilisé par TOUS les champs du
   site (adresse, contact, compte, paiement, connexion...) à 0.9rem
   (14.4px) — sous ce seuil, donc chaque champ déclenchait ce zoom
   involontaire au focus sur iPhone/iPad, contraire à l'exigence "aucun
   zoom automatique du navigateur". Corrigé uniquement sous le point de
   rupture mobile/tablette (`max-width: 768px`, cf. TABLET_SHORT_SIDE_MIN
   dans device-tier.ts) pour ne rien changer à la typographie desktop
   voulue (0.9rem) sur les écrans où ce bug n'existe pas. */
@media (max-width: 768px) {
  .glass-input {
    font-size: 16px;
  }
}

.glass-input::-moz-placeholder {
  color: rgba(33, 31, 28, 0.4);
}

.glass-input::placeholder {
  color: rgba(33, 31, 28, 0.4);
}

.glass-input:focus {
  outline: none;
  border-color: rgba(63, 110, 130, 0.5);
  background: rgba(255, 255, 255, 0.75);
}

/* <select> belt-and-suspenders on top of the :root color-scheme fix above
   — explicit dark colors on the popup's own <option> list rather than
   relying solely on the browser inferring them, plus a custom chevron
   replacing the default UA arrow (which read oddly against the glass
   pill shape) via `appearance: none`. Only one <select> exists on the
   site today (profileType, /espace/profil) but this targets the shared
   `.glass-input` class, so any future <select> is covered automatically. */
select.glass-input {
  color-scheme: light;
  -moz-appearance: none;
       appearance: none;
  -webkit-appearance: none;
  padding-right: 2.75rem;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23211f1c' stroke-width='1.5'%3E%3Cpath d='m6 9 6 6 6-6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 1.25rem center;
}

select.glass-input option {
  background-color: #ffffff;
  color: #211f1c;
}

/* Address step's satellite-loading beat — a rotating glass ring built from
   a masked conic-gradient sweep plus a pulsing core, rather than a generic
   spinner, so the wait for the "wow" satellite reveal (components/start/
   StepAddress.tsx) still reads as the same premium visual language as
   everything else on the site. Pure CSS keyframes, same reasoning as
   `.zebra-glow-idle` above: no JS needed for a continuous ambient loop. */
@keyframes geo-loader-spin {
  to {
    transform: rotate(360deg);
  }
}

@keyframes geo-loader-pulse {
  0%,
  100% {
    transform: scale(0.85);
    opacity: 0.6;
  }
  50% {
    transform: scale(1);
    opacity: 1;
  }
}

.geo-loader {
  position: relative;
  width: 64px;
  height: 64px;
  border-radius: 9999px;
  background: rgba(255, 255, 255, 0.55);
  border: 1px solid rgba(33, 31, 28, 0.1);
}

.geo-loader::before {
  content: "";
  position: absolute;
  inset: -1px;
  border-radius: 9999px;
  padding: 1px;
  background: conic-gradient(
    from 0deg,
    rgba(63, 110, 130, 0) 0%,
    rgba(63, 110, 130, 0.9) 20%,
    rgba(63, 110, 130, 0) 40%
  );
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
  animation: geo-loader-spin 1.4s linear infinite;
}

.geo-loader::after {
  content: "";
  position: absolute;
  inset: 22px;
  border-radius: 9999px;
  background: radial-gradient(circle, rgba(63, 110, 130, 0.9), rgba(63, 110, 130, 0.2) 70%);
  animation: geo-loader-pulse 1.6s ease-in-out infinite;
}

/* The address step's satellite-view marker (components/start/
   SatelliteView.tsx) — a custom div-icon rather than Leaflet's default
   marker image, both to dodge the classic bundler-breaks-the-default-
   marker-icon-path issue and to keep it visually on-brand. */
.satellite-pin {
  width: 14px;
  height: 14px;
  border-radius: 9999px;
  background: rgba(124, 198, 255, 0.95);
  box-shadow: 0 0 0 6px rgba(124, 198, 255, 0.25), 0 0 16px 4px rgba(124, 198, 255, 0.6);
}

/* ============================================================
   Implantation simulator (components/start/ImplantationMap.tsx) — the
   project rectangle, its rotation handle, the north indicator and the
   distance-to-boundary labels drawn directly on the map. */
.implantation-rect {
  cursor: grab;
}
.implantation-rect:active {
  cursor: grabbing;
}
/* "Afficher un petit repère/snap visuel lorsqu'un alignement est trouvé" —
   toggled by ImplantationMap.tsx whenever a translation or rotation drag
   engages the smart alignment engine (existing building / parcel boundary
   / parallel-perpendicular axis), cleared the instant it releases. */
.implantation-rect--snapped {
  filter: drop-shadow(0 0 6px rgba(255, 207, 92, 0.85));
}

.implantation-rotate-handle {
  width: 16px;
  height: 16px;
  border-radius: 9999px;
  background: rgba(245, 245, 242, 0.95);
  border: 2px solid rgba(124, 198, 255, 0.9);
  box-shadow: 0 0 10px 2px rgba(124, 198, 255, 0.5);
  cursor: grab;
}
.implantation-rotate-handle:active {
  cursor: grabbing;
}

.implantation-north {
  position: absolute;
  top: 0.75rem;
  left: 0.75rem;
  padding: 0.35rem 0.6rem;
  border-radius: 9999px;
  font-size: 0.7rem;
  letter-spacing: 0.04em;
  color: rgba(245, 245, 242, 0.85);
  background: rgba(5, 6, 10, 0.55);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  z-index: 500;
}

.implantation-distance-label {
  transform: translate(-50%, -50%);
  white-space: nowrap;
  padding: 0.15rem 0.4rem;
  border-radius: 9999px;
  font-size: 0.65rem;
  color: rgba(5, 6, 10, 0.9);
  background: rgba(245, 245, 242, 0.9);
}

/* ============================================================
   Project-type card grid — "Quelle est la nature de votre projet ?"
   (components/start/ProjectTypeCard.tsx, StepProjectType.tsx). Real
   illustrative photos replaced the old icon-only 3D carousel this used to
   be — `.glass-card`'s own hover lift already gives the "envie de
   cliquer" feel the brief asked for; this block only adds the photo
   frame and a slow image zoom on hover for a touch more life. */
/* Redesigned so the image dominates the card ("le client doit avoir envie
   de sélectionner une carte") — media bleeds to the card's own edges
   (zero padding around it, unlike `.glass-card`'s default 1.75rem) and
   fills most of the card height; the label sits in a thin bottom band
   instead of sharing the same generous padding as the image. `.glass-card`
   is still the base class (hover lift, cursor, border-radius) — this only
   overrides its padding/gap for this one card type. */
.project-type-card {
  align-items: stretch;
  gap: 0;
  padding: 0;
  overflow: hidden;
}

.project-type-card__media {
  position: relative;
  width: 100%;
  aspect-ratio: 4 / 5;
  overflow: hidden;
  /* Variable de thème plutôt qu'un rgba() littéral (audit couleurs codées en
     dur) — suit --color-paper (encre en clair, ivoire en sombre), donc un
     léger voile cohérent avec le thème actif des deux côtés au lieu d'un
     seul remplacement scopé `html.dark .project-type-card__media`. */
  background: rgb(var(--color-paper) / 4%);
}

.project-type-card__image {
  -o-object-fit: cover;
     object-fit: cover;
  transition: transform 0.6s var(--ease-cinematic);
}

.project-type-card:hover .project-type-card__image {
  transform: scale(1.06);
}

.project-type-card__label {
  padding: 0.85rem 1rem;
  border-top: 1px solid rgb(var(--color-paper) / 10%);
}

/* ============================================================
   Carrousel horizontal — passe de finition homepage. Réutilisé pour "Vos
   travaux" (cartes compactes icône+titre) et, sur mobile, "Les 3 packs".
   Défilement natif (`overflow-x: auto` + `scroll-snap`), jamais réimplémenté
   en JS — c'est ce qui garantit que le scroll horizontal reste strictement
   local à `.carousel-viewport` : la molette/trackpad, le swipe tactile et
   les boutons précédent/suivant (useHorizontalCarousel.ts) agissent tous sur
   CE scroll interne, jamais sur `<html>/<body>` qui ne défilent qu'en Y. */
.carousel-viewport {
  display: flex;
  overflow-x: auto;
  overflow-y: hidden;
  overscroll-behavior-x: contain;
  scroll-snap-type: x proximity;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  -ms-overflow-style: none;
}

/* Zone scrollable interne d'une modale/drawer (RealPaymentModal, ExtrasStep,
   Dp2ExpandedOverlay) — bug corrigé (ÉTAPE 53) : `.glass-surface` définit
   `overflow: hidden` (pour clipper son reflet ::before aux coins arrondis),
   et comme cette règle compile APRÈS `@tailwind utilities` dans ce fichier
   (voir la note en tête de fichier), elle l'emportait TOUJOURS sur un
   utilitaire Tailwind `overflow-y-auto` posé sur le MÊME élément — même
   spécificité, ordre de source gagnant. Résultat concret : une carte
   "Services supplémentaires" avec `max-h-[..vh] overflow-y-auto` restait
   figée à `overflow-y: hidden` en cascade finale, sans qu'aucun outil de
   dev ne le signale comme une erreur — juste un contenu qui dépasse en
   silence, page bloquée. Solution durable : ne JAMAIS poser
   `overflow-y-auto` sur l'élément qui porte `.glass-surface` — toujours sur
   un enfant dédié à défilement, qui porte cette classe en plus. */
.momentum-scroll-y {
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
}

.carousel-viewport::-webkit-scrollbar {
  display: none;
}

.carousel-viewport > * {
  scroll-snap-align: start;
  flex-shrink: 0;
}

/* Bouton précédent/suivant — capsule ronde `.glass-surface`, désactivée
   (jamais masquée : sa présence indique déjà "carrousel", même en bout de
   liste) quand il n'y a plus rien à défiler dans cette direction. */
.carousel-nav-btn {
  display: none;
}

@media (min-width: 768px) {
  .carousel-nav-btn {
    display: inline-flex;
  }
}

.carousel-nav-btn:disabled {
  opacity: 0.3;
  cursor: default;
}

/* Carte compacte "Vos travaux" — remplace l'ancien format photo pleine
   carte (4:5, hérité de .project-type-card, toujours utilisé tel quel dans
   /demarrer) : ici, hiérarchie ICÔNE → TITRE → rien de plus, une carte
   nettement plus petite plutôt qu'une grande zone vide autour d'une icône.
   Largeur fixe (pas `w-full` dans une grille) car chaque carte est un
   enfant de `.carousel-viewport`, dimensionné pour lui-même. */
.travaux-card {
  display: flex;
  width: 8.5rem;
  flex-direction: column;
  align-items: center;
  gap: 0.65rem;
  padding: 1.1rem 0.75rem;
  border-radius: 1.1rem;
  text-align: center;
}

@media (min-width: 640px) {
  .travaux-card {
    width: 9.5rem;
  }
}

.travaux-card__icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 3rem;
  height: 3rem;
  border-radius: 9999px;
  background: rgb(var(--color-accent) / 12%);
  color: rgb(var(--color-accent));
}

.travaux-card__label {
  font-size: 0.8125rem;
  line-height: 1.25;
  font-weight: 600;
}

/* ============================================================
   Ratios d'aspect — remplace les classes Tailwind arbitraires `aspect-[X/Y]`
   utilisées un peu partout (aperçus de documents/plans, cartes exemples...).
   CAUSE RÉELLE D'UN ÉCHEC DE BUILD EN PRODUCTION : Tailwind génère un
   sélecteur littéral du type `.aspect-\[4\/3\]` pour ces classes — sur le
   pipeline de minification CSS du serveur de déploiement (cssnano), ce
   sélecteur échappé fait planter le parseur de sélecteurs avec l'erreur
   "Unexpected '/'. Escaping special characters with \ may help." (le
   `\/` à l'intérieur du sélecteur, valide en CSS strict, n'est pas
   correctement reparsé par cette version de cssnano lors d'une passe de
   fusion de règles). Ce bac à sable ne peut pas lancer `next build` (binaire
   SWC natif absent) donc ce bug n'avait jamais pu être reproduit ici — seule
   la vraie machine de déploiement l'a révélé. Remplacer chaque usage par une
   classe dédiée ci-dessous (valeur `aspect-ratio` en CSS normal, aucun `/`
   dans un sélecteur) élimine la cause à la racine, sans changer le rendu. */
.ratio-4-3 {
  aspect-ratio: 4 / 3;
}

.ratio-16-10 {
  aspect-ratio: 16 / 10;
}

.ratio-39-55 {
  aspect-ratio: 39 / 55;
}

.ratio-45-32 {
  aspect-ratio: 45 / 32;
}

.ratio-55-39 {
  aspect-ratio: 55 / 39;
}

.ratio-210-297 {
  aspect-ratio: 210 / 297;
}

.ratio-297-210 {
  aspect-ratio: 297 / 210;
}

.ratio-220-310 {
  aspect-ratio: 220 / 310;
}

/* Badge icône compact — "Services supplémentaires" (ExtraServiceCard.tsx).
   Même principe que `.travaux-card__icon` : une icône proportionnée à sa
   propre taille plutôt qu'à celle, arbitraire, du reste de la carte. */
.extra-card__icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2.75rem;
  height: 2.75rem;
  border-radius: 0.85rem;
  background: rgb(var(--color-accent) / 12%);
  color: rgb(var(--color-accent));
}

/* Siri / Apple Intelligence-style animated glow — currently only around
   the sound toggle (components/dom/SoundToggle.tsx), reported as
   "quasiment invisible" as a plain glass circle against the video
   background. A slow-rotating multicolor conic-gradient ring plus a
   heavily blurred bloom of the same gradient behind it, rather than a
   static colored badge — draws the eye the way Siri's current animated
   glow does, without turning a deliberately understated button into a
   loud, un-premium one.

   Two separate elements (not one with two pseudo-elements) so the ring
   can use the mask-composite "cut a ring out of a filled shape" trick
   (same technique as `.geo-loader::before` above) independently of the
   bloom's own blur — both share the same `siri-glow-rotate` animation/
   duration so they stay in phase.

   `.siri-glow--dim` (muted) vs `.siri-glow--active` (sound on) isn't just
   decorative: dimmer/desaturated vs full color reads as an actual
   indicator light (off vs on), not just an attention-grabbing sticker. */
@keyframes siri-glow-rotate {
  to {
    transform: rotate(360deg);
  }
}

.siri-glow {
  border-radius: 9999px;
}

.siri-glow-ring,
.siri-glow-bloom {
  position: absolute;
  border-radius: 9999px;
  background: conic-gradient(from 0deg, #0a84ff, #bf5af2, #ff375f, #ff9f0a, #64d2ff, #0a84ff);
  animation: siri-glow-rotate 5s linear infinite;
  pointer-events: none;
  transition: opacity 0.5s ease, filter 0.5s ease;
}

.siri-glow-ring {
  inset: -5px;
  padding: 2px;
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
}

.siri-glow-bloom {
  inset: -11px;
  filter: blur(11px);
}

.siri-glow--dim .siri-glow-ring {
  opacity: 0.45;
  filter: saturate(0.5);
}

.siri-glow--dim .siri-glow-bloom {
  opacity: 0.16;
}

.siri-glow--active .siri-glow-ring {
  opacity: 1;
}

.siri-glow--active .siri-glow-bloom {
  opacity: 0.55;
}

/* StepContact.tsx's header icon — a soft, continuous pulsing glow behind
   it, same ambient-halo idea as `.zebra-glow-idle` above, to give this
   "moment clé" a bit more visual weight than three bare form fields. */
@keyframes contact-icon-pulse {
  0%,
  100% {
    opacity: 0.35;
    transform: scale(0.92);
  }
  50% {
    opacity: 0.6;
    transform: scale(1.06);
  }
}

.contact-icon-glow::before {
  content: "";
  position: absolute;
  inset: -14px;
  z-index: -1;
  border-radius: 9999px;
  background: radial-gradient(circle, rgba(63, 110, 130, 0.4), rgba(63, 110, 130, 0) 70%);
  animation: contact-icon-pulse 3s ease-in-out infinite;
  pointer-events: none;
}

/* ImmersiveNav's capsules are .glass-surface .glass-pill .depth-layer (see
   ImmersiveNav.tsx) plus this one nav-specific bit: they start invisible
   in the stylesheet, not via a React inline style, so the GSAP entrance
   tween — which sets this same property directly on the element — is the
   only thing that ever touches it. An inline `style={{opacity:0}}` in JSX
   would get re-applied by React on any re-render and stomp the
   GSAP-driven value right back to invisible. */
.nav-capsule {
  opacity: 0;
}

/* Le dossier vivant — DossierButton.tsx's ambient halo. Explicitly chosen
   by the user to be MORE marked/spectacular than `.siri-glow` above, not
   a variant of it: two counter-rotating conic-gradient rings (rather than
   one) at different speeds/radii, plus a blurred bloom that itself
   breathes (`dossier-pulse`), so at rest it already reads as more alive
   than a single steady ring. Intensity is driven by one CSS custom
   property, `--dossier-life` (0..1, set inline from
   DOSSIER_STAGE_INTENSITY in dossier-stage.ts) — every layer below reads
   from it via calc() rather than each needing its own JS-driven update,
   so a stage change is a single style write that cascades to the whole
   glow at once. */
@keyframes dossier-ring-cw {
  to {
    transform: rotate(360deg);
  }
}

@keyframes dossier-ring-ccw {
  to {
    transform: rotate(-360deg);
  }
}

@keyframes dossier-pulse {
  0%,
  100% {
    transform: scale(0.94);
  }
  50% {
    transform: scale(1.1);
  }
}

.dossier-life-glow {
  --dossier-life: 0.12;
  border-radius: 9999px;
}

.dossier-ring,
.dossier-bloom {
  position: absolute;
  border-radius: 9999px;
  pointer-events: none;
}

.dossier-ring--outer {
  inset: -12px;
  padding: 2px;
  background: conic-gradient(from 0deg, #0a84ff, #64d2ff, #bf5af2, #ff375f, #ff9f0a, #0a84ff);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
  animation: dossier-ring-cw 7s linear infinite;
  opacity: var(--dossier-life);
  transition: opacity 0.6s ease;
}

.dossier-ring--inner {
  inset: -6px;
  padding: 1.5px;
  background: conic-gradient(from 180deg, #ff9f0a, #ff375f, #bf5af2, #64d2ff, #0a84ff, #ff9f0a);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
  animation: dossier-ring-ccw 4.5s linear infinite;
  opacity: calc(var(--dossier-life) * 0.85);
  transition: opacity 0.6s ease;
}

.dossier-bloom {
  inset: -20px;
  background: conic-gradient(from 90deg, #0a84ff, #bf5af2, #ff375f, #ff9f0a, #64d2ff, #0a84ff);
  filter: blur(18px);
  opacity: calc(var(--dossier-life) * 0.7);
  animation: dossier-ring-cw 7s linear infinite, dossier-pulse 2.6s ease-in-out infinite;
  transition: opacity 0.6s ease;
}

.dossier-badge {
  position: absolute;
  top: -4px;
  right: -4px;
  display: flex;
  min-width: 18px;
  height: 18px;
  align-items: center;
  justify-content: center;
  border-radius: 9999px;
  background: rgba(124, 198, 255, 0.9);
  color: #04060a;
  font-size: 10px;
  font-weight: 600;
  line-height: 1;
  padding: 0 4px;
}

/* DossierPanel.tsx's slide-in drawer — a right-edge panel rather than a
   centered modal, closer to "un tiroir qui s'ouvre sur le projet en
   cours" than an interruptive dialog; content keeps scrolling/using the
   rest of the site conceptually available behind it (even though a
   backdrop dims it for focus while open).

   z-index bumped to 55/56 (was 45/46) — bug fix: this panel used to be
   rendered NESTED inside DossierButton.tsx's own `.dom-layer` wrapper,
   which (because `.dom-layer`'s z-index:10 compiles in after `@tailwind
   utilities` and silently wins over that wrapper's `z-40` Tailwind
   class — the exact cascade issue already documented on `.dom-layer`
   above) trapped the whole panel inside a stacking context effectively
   pinned at z-index:10. ImmersiveNav genuinely sits at z-50 (no
   `.dom-layer` class on it, nothing overrides it) and could render on
   top of the panel near the top of the screen — the panel LOOKED open
   (z-index doesn't affect visibility) but its close button was actually
   unclickable there, the click landing on the nav instead. Fixed
   structurally too: DossierPanel now renders as a sibling of that
   wrapper, not nested inside it — these values only need to comfortably
   clear ImmersiveNav's real z-50 now, not compensate for a trap. */
.dossier-panel-backdrop {
  position: fixed;
  inset: 0;
  z-index: 55;
  background: rgba(33, 31, 28, 0.35);
  backdrop-filter: blur(2px);
  opacity: 0;
  pointer-events: none;
}

.dossier-panel {
  position: fixed;
  top: 0;
  right: 0;
  z-index: 56;
  height: 100dvh;
  width: min(420px, 100vw);
  overflow-y: auto;
  padding: 1.75rem 1.5rem;
  background: rgba(255, 255, 255, 0.92);
  backdrop-filter: blur(24px) saturate(140%);
  border-left: 1px solid rgba(33, 31, 28, 0.08);
  box-shadow: -30px 0 60px -20px rgba(33, 31, 28, 0.35);
  display: flex;
  flex-direction: column;
  opacity: 0;
  transform: translateX(100%);
}

/* The opacity:0 / translateX(100%) resting state above is only ever the
   very first paint before GSAP's fromTo takes over (same "no separate
   CSS resting state to fight a JS tween" reasoning as `.nav-capsule`
   elsewhere in this file) — once mounted, DossierPanel.tsx's own tween
   owns both properties for as long as the drawer exists in the DOM.
   `.dossier-panel-backdrop`'s `pointer-events: none` default matters
   more here than usual: DossierPanel only ever mounts these two
   elements while the drawer is open or mid-close-animation, and unmounts
   them entirely once the close tween completes — so there's never a
   moment where an invisible-but-present backdrop is left intercepting
   clicks on the rest of the page. */

/* /espace/estimations/[id]'s printable view — "Imprimer" IS "Enregistrer
   en PDF" here (browser print-to-PDF, no separate PDF-generation
   dependency). `.print-hide` marks chrome that makes no sense on paper
   (espace/layout.tsx's nav/logout bar, the page's own "Imprimer" button);
   `.print-sheet` resets the dark glassmorphism theme to plain black-on-
   white for the estimation content itself, since printing the site's
   actual dark background would waste every cartridge it's printed with.
   The classes below (`.print-accent-rule`, `.print-kv-table`, etc.) style
   that document's internal hierarchy — letterhead rule, key/value rows,
   totals table, callout — consistently on screen (dark glass theme) and
   on paper (the @media print overrides further down flip their colors). */
.print-accent-rule {
  height: 2px;
  border-radius: 9999px;
  background: linear-gradient(to right, rgba(124, 198, 255, 0.9), rgba(124, 198, 255, 0));
}

.print-section-title {
  padding-bottom: 0.5rem;
  border-bottom: 1px solid rgba(245, 245, 242, 0.1);
}

.print-kv-table th,
.print-kv-table td {
  text-align: left;
  padding: 0.6rem 0;
  border-bottom: 1px solid rgba(245, 245, 242, 0.08);
  font-weight: 400;
  vertical-align: top;
}

.print-kv-table th {
  width: 40%;
  color: rgba(245, 245, 242, 0.5);
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.print-kv-table td {
  color: rgba(245, 245, 242, 0.9);
}

.print-totals-table td {
  padding: 0.6rem 0;
  border-bottom: 1px solid rgba(245, 245, 242, 0.08);
  color: rgba(245, 245, 242, 0.7);
}

.print-totals-row-final td {
  padding-top: 1rem;
  border-bottom: none;
  border-top: 1px solid rgba(245, 245, 242, 0.15);
  font-family: var(--font-display);
  font-size: 1.25rem;
  color: #f5f5f2;
  font-weight: 600;
}

.print-callout {
  border-radius: 1rem;
  border: 1px solid rgba(124, 198, 255, 0.3);
  background: rgba(124, 198, 255, 0.06);
  padding: 1.25rem 1.5rem;
}

.print-footer {
  border-top: 1px solid rgba(245, 245, 242, 0.1);
}

/* Bug corrigé : "Télécharger" produisait des pages blanches. Cause réelle
   — `.glass-surface` déclare `transition: background-color/border-color/
   box-shadow/color 0.35s`, mais EstimationPrintControls.tsx n'attend que
   2 frames (~32ms) après avoir changé `data-pdf-theme` avant d'appeler
   `window.print()` : l'impression capturait donc un état intermédiaire,
   à mi-chemin entre les deux thèmes — ni vraiment sombre ni vraiment
   clair, souvent quasi illisible (texte presque blanc sur fond presque
   blanc). Le changement de thème est une bascule fonctionnelle avant
   téléchargement, pas une animation que le visiteur doit voir : elle doit
   être instantanée. Diagnostiqué en désactivant les transitions pendant
   le débogage (session précédente) sans jamais reporter le correctif dans
   le code — corrigé ici, pour de bon. */
.print-sheet,
.print-sheet * {
  transition: none !important;
}

/* Tableaux/encadré jamais coupés au milieu par la pagination navigateur. */
.print-kv-table,
.print-totals-table,
.print-callout,
.print-footer {
  -moz-column-break-inside: avoid;
       break-inside: avoid;
  page-break-inside: avoid;
}

/* Chaque section (titre + son contenu, ex. "Prestations incluses" + la
   liste) traitée comme un seul bloc : sans ça, la coupure de page pouvait
   tomber entre un titre et son contenu (le titre seul en bas d'une page,
   le tableau qui commence sur la suivante). */
.print-avoid-break {
  -moz-column-break-inside: avoid;
       break-inside: avoid;
  page-break-inside: avoid;
}

/* Thème clair du document (`data-pdf-theme="light"`, posé par
   EstimationPrintControls.tsx sur `.print-sheet` — et recopié sur `<html>`
   pour les sélecteurs qui doivent atteindre `body`, voir @media print plus
   bas). Actif à l'écran ET à l'impression, pas seulement dans @media print
   : "identique à l'écran" n'a de sens que si ce que le navigateur imprime
   est vraiment ce qui vient d'être affiché, pas une variante recalculée
   uniquement au moment d'imprimer. Chaque utilitaire Tailwind
   `text-paper/*`/`border-paper/10` réellement utilisé à l'intérieur du
   print-sheet est remappé ici vers l'encre foncée équivalente, à la même
   opacité — même hiérarchie visuelle, juste sur fond clair. Le thème
   sombre n'a besoin d'aucune règle ici : c'est déjà l'apparence par défaut
   du print-sheet (`.glass-surface` + `text-paper/*` tels quels). */
.print-sheet[data-pdf-theme="light"] {
  /* !important : cette règle doit gagner face à `.glass-surface` (même
     élément porte les deux classes) quel que soit l'ordre de définition
     de `.glass-surface` dans la feuille de style. */
  background: #ffffff !important;
  border-color: rgba(28, 28, 27, 0.12) !important;
  box-shadow: none !important;
  backdrop-filter: none !important;
}

.print-sheet[data-pdf-theme="light"] .text-paper,
.print-sheet[data-pdf-theme="light"] .print-kv-table td {
  color: #1c1c1b;
}

/* Même correctif d'échappement que le bloc `[class~="text-paper/NN"]`
   plus haut — `\/` remplacé par un sélecteur d'attribut `class~=`. */
.print-sheet[data-pdf-theme="light"] [class~="text-paper/90"] { color: rgba(28, 28, 27, 0.9); }
.print-sheet[data-pdf-theme="light"] [class~="text-paper/80"] { color: rgba(28, 28, 27, 0.8); }
.print-sheet[data-pdf-theme="light"] [class~="text-paper/70"] { color: rgba(28, 28, 27, 0.7); }
.print-sheet[data-pdf-theme="light"] [class~="text-paper/60"] { color: rgba(28, 28, 27, 0.6); }
.print-sheet[data-pdf-theme="light"] [class~="text-paper/50"] { color: rgba(28, 28, 27, 0.5); }
.print-sheet[data-pdf-theme="light"] [class~="text-paper/40"] { color: rgba(28, 28, 27, 0.4); }
.print-sheet[data-pdf-theme="light"] [class~="border-paper/10"] { border-color: rgba(28, 28, 27, 0.12); }

.print-sheet[data-pdf-theme="light"] .print-section-title,
.print-sheet[data-pdf-theme="light"] .print-footer {
  border-color: rgba(28, 28, 27, 0.12);
}

.print-sheet[data-pdf-theme="light"] .print-kv-table th,
.print-sheet[data-pdf-theme="light"] .print-kv-table td,
.print-sheet[data-pdf-theme="light"] .print-totals-table td {
  border-color: rgba(28, 28, 27, 0.1);
}

.print-sheet[data-pdf-theme="light"] .print-kv-table th {
  color: rgba(28, 28, 27, 0.5);
}

.print-sheet[data-pdf-theme="light"] .print-totals-table td {
  color: rgba(28, 28, 27, 0.7);
}

.print-sheet[data-pdf-theme="light"] .print-totals-row-final td {
  /* Après (pas avec) la règle `.print-totals-table td` ci-dessus — même
     spécificité, donc l'ordre décide : la ligne "Total TTC" doit rester en
     pleine intensité, pas à l'opacité atténuée des lignes de sous-total. */
  color: #1c1c1b;
  border-top-color: rgba(28, 28, 27, 0.3);
}

.print-sheet[data-pdf-theme="light"] .print-callout {
  border-color: rgba(47, 127, 184, 0.35);
  background: rgba(124, 198, 255, 0.1);
}

@page {
  size: A4;
  margin: 2cm;
}

@media print {
  .print-hide {
    display: none !important;
  }

  /* `body`'s own dark background (html, body base rule above) already
     matches the "mode sombre" download with no extra rule needed — only
     the light download needs an explicit flip. `body` isn't a descendant
     of `.print-sheet`, hence reading the theme off `<html>` here instead.
     Both `html` AND `body` need the override, not just `body` : per spec,
     when the root element (`html`) has its own non-transparent background,
     THAT is what paints the page's canvas, not body's — body's background
     only covers its own box. With only `body` flipped to white, `html`'s
     near-black background (see the shared html/body rule above) kept
     painting through as a thick dark border around the lighter content,
     exactly the "grosses bordures noires" a client saw on a printed/PDF
     devis. */
  html[data-pdf-theme="light"],
  html[data-pdf-theme="light"] body {
    background: #fff !important;
  }

  /* La vraie dernière pièce du puzzle des bordures noires : `color-scheme:
     dark` sur `:root` (tout en haut de ce fichier) fait choisir à Chrome
     un gris quasi noir (~#121212) comme fond de LA PAGE ENTIÈRE avant même
     de peindre le `background` ci-dessus — visible dans le flux PDF brut
     (`.0706 .0706 .0706 rg` en tout premier, sur toute la page) : ce n'est
     pas le CSS `background` de html/body qui manquait, c'est ce choix de
     fond par défaut du moteur d'impression, piloté par `color-scheme`,
     totalement indépendant. `color-scheme: light` fait choisir un fond
     blanc à la place. */
  html[data-pdf-theme="light"] {
    color-scheme: light !important;
  }

  .print-sheet {
    box-shadow: none !important;
    border-radius: 0 !important;
    padding: 0 !important;
    backdrop-filter: none !important;
  }

  /* espace/layout.tsx's shared dashboard shell reserves a full viewport
     height plus generous top/bottom padding for its fixed nav bar
     (min-h-screen, pt-40, pb-24) — on screen. Left as-is for print, that
     dead space pushes `.print-sheet` down far enough to spill onto a
     second, mostly-empty page, or even strand it there entirely while a
     blank first page sits in front of it. */
  .print-reset-layout {
    min-height: 0 !important;
    padding: 0 !important;
    /* `bg-void` (#05060a) is a class on this div itself, not inherited from
       html/body — the fix above only repaints the canvas (html/body), but
       this wrapper still painted its OWN dark background on top of it,
       full-bleed across the page width while `.print-content-width` below
       stays centered and narrower, showing up as thick dark bands on
       either side of the printed content. Transparent here lets whichever
       canvas color is actually correct for the chosen theme (already
       handled above) show through instead, for both themes at once. */
    background: transparent !important;
  }

  /* `max-w-3xl` (48rem/768px) is a screen-oriented reading width, wider
     than A4's printable area (21cm minus the 2cm `@page` margin on each
     side ≈ 17cm ≈ 642px) — forcing the browser to either shrink everything
     to fit or spill/crop content instead of using the actual page width.
     Print gets the full printable width instead, `@page`'s own margin
     already providing the border. */
  .print-content-width {
    width: 100% !important;
    max-width: none !important;
    margin: 0 !important;
  }
}

/* ---------------------------------------------------------------------
   Panneau DP2 (Dp2Panel.tsx et alentours) — uniquement ce que les classes
   glass-* et print-* existantes ne couvrent pas déjà, même logique de
   composition que le reste de ce fichier plutôt qu'un nouveau langage
   visuel. */

/* Le zoom à l'écran (boutons +/-/Ajuster de Dp2PanelContent.tsx) est un
   pur transform CSS séparé du conteneur `.print-sheet` lui-même, pour
   qu'un visiteur ayant zoomé n'imprime jamais un document mis à l'échelle
   au hasard — voir la règle @media print plus bas qui neutralise ce
   transform spécifiquement à l'impression. */
.dp2-zoom-viewport {
  overflow: auto;
  display: flex;
  justify-content: center;
}

.dp2-zoom-wrap {
  transition: transform 0.25s var(--ease-cinematic);
  transform-origin: top center;
  width: 100%;
}

@media print {
  .dp2-zoom-wrap {
    transform: none !important;
  }

  /* Dp2ExpandedOverlay.tsx garde sciemment son conteneur racine hors de
     .print-hide (un ancêtre display:none masquerait aussi le .print-sheet
     imbriqué) — son propre habillage (fond, flou, marges) est donc
     neutralisé ici individuellement, même schéma que .print-reset-layout
     plus haut dans ce fichier. */
  .dp2-expanded-overlay {
    position: static !important;
    background: none !important;
    backdrop-filter: none !important;
    padding: 0 !important;
  }

  .dp2-overlay-sheet {
    max-height: none !important;
    box-shadow: none !important;
    padding: 0 !important;
    background: none !important;
    border: none !important;
  }
}

/* Filigrane "APERÇU" (Dp2Document.tsx) — rendu directement en SVG
   (`<text>` avec transform="rotate(...)"), aucune règle CSS nécessaire ;
   documenté ici uniquement pour que ce bloc reste le point de repère
   "tout ce qui concerne le DP2" dans cette feuille de style. */

/* ============================================================
   MODE SOMBRE — équivalents des classes .glass-*/.map-view-toggle*/
   .deck-card*/.dossier-panel*/.geo-loader/select.glass-input ci-dessus.
   Toutes peignent leurs couleurs en `rgba(...)` LITTÉRAUX plutôt qu'en
   tokens `--color-*` (nécessaire pour composer flou/ombre/transparence),
   donc elles ne basculent jamais automatiquement avec `html.dark` —
   contrairement aux ~250 usages `bg-void`/`text-paper`/`border-concrete-*`
   ailleurs sur le site, qui suivent déjà les variables définies plus haut.
   Cette section réécrit chacune, dans le même ordre que la version claire
   ci-dessus, avec la MÊME structure (rayon, flou, forme d'ombre) — seules
   les couleurs changent : une vitre claire et fine en thème clair devient
   une vitre sombre et légèrement plus opaque en thème sombre (une vitre sur
   fond noir a besoin d'un peu plus de matière pour rester lisible qu'une
   vitre sur fond clair). N'importe quelle règle qui ne mentionne QUE des
   couleurs déjà neutres (le dégradé bleu de `.glass-accent`, la lueur
   `.zebra-glow-*`/`.contact-icon-glow`, qui utilisent déjà le bleu clair
   `#7cc6ff`/`#7dd3fc`...) n'a pas besoin d'être répétée ici — elle
   fonctionne déjà correctement sur fond sombre. */

html.dark .glass-surface {
  background: rgb(var(--color-void-raised) / 55%);
  border-color: rgb(var(--color-paper) / 12%);
  box-shadow:
    inset 0 1px 1px rgb(var(--color-void-raised) / 40%),
    0 8px 20px -14px rgb(0 0 0 / 60%);
  color: rgb(var(--color-paper) / 88%);
}

html.dark .glass-surface::before {
  background: radial-gradient(circle, rgb(var(--color-void-raised) / 35%), rgb(var(--color-void-raised) / 0%) 70%);
}

html.dark .glass-surface:hover {
  background: rgb(var(--color-void-raised) / 78%);
  border-color: rgb(var(--color-paper) / 24%);
  color: rgb(var(--color-paper) / 100%);
}

html.dark .glass-card:hover {
  box-shadow:
    inset 0 1px 1px rgb(var(--color-void-raised) / 45%),
    0 24px 48px -20px rgb(0 0 0 / 55%);
}

html.dark .glass-card--selected {
  border-color: rgb(var(--color-accent) / 55%);
  box-shadow:
    inset 0 1px 1px rgb(var(--color-void-raised) / 45%),
    0 0 0 1px rgb(var(--color-accent) / 32%),
    0 24px 48px -20px rgb(0 0 0 / 55%);
}

html.dark .offer-card--highlight {
  border-color: rgb(var(--color-accent) / 55%);
  box-shadow:
    inset 0 1px 1px rgb(var(--color-void-raised) / 45%),
    0 0 0 1px rgb(var(--color-accent) / 34%),
    0 32px 64px -24px rgb(0 0 0 / 55%);
}

/* L'anneau de focus clavier (`rgba(63, 110, 130, 0.85)`, un bleu ardoise
   assez sombre) manquerait de contraste sur un fond déjà sombre — remplacé
   par l'accent clair du thème sombre, largement plus visible. */
html.dark .glass-surface:focus-visible,
html.dark .glass-input:focus-visible,
html.dark a:focus-visible,
html.dark button:focus-visible,
html.dark [role="button"]:focus-visible,
html.dark input:focus-visible,
html.dark textarea:focus-visible,
html.dark select:focus-visible {
  outline-color: rgb(var(--color-accent) / 90%);
}

html.dark .map-view-toggle {
  border-color: rgb(var(--color-paper) / 16%);
  box-shadow: 0 14px 30px -16px rgb(0 0 0 / 60%);
}

html.dark .map-view-toggle-btn {
  color: rgb(var(--color-paper) / 65%);
}

html.dark .map-view-toggle-btn:hover {
  color: rgb(var(--color-paper) / 92%);
}

html.dark .deck-card::after {
  background: rgb(var(--color-paper) / 8%);
}

html.dark .deck-card__value {
  color: rgb(var(--color-paper) / 78%);
}

html.dark .deck-card--filled {
  border-color: rgb(var(--color-paper) / 18%);
}

html.dark .deck-card--ghost {
  border-color: rgb(var(--color-paper) / 30%);
}

/* Même halo respirant que `@keyframes deck-card-pulse` ci-dessus, rejoué
   avec les couleurs sombres — une @keyframe ne peut pas être "patchée"
   partiellement comme une règle normale, donc une variante complète plutôt
   qu'un correctif partiel. */
@keyframes deck-card-pulse-dark {
  0%,
  100% {
    box-shadow:
      inset 0 1px 1px rgb(var(--color-void-raised) / 45%),
      0 0 0 1px rgb(var(--color-accent) / 22%),
      0 24px 48px -20px rgb(0 0 0 / 55%);
  }
  50% {
    box-shadow:
      inset 0 1px 1px rgb(var(--color-void-raised) / 50%),
      0 0 0 1px rgb(var(--color-accent) / 42%),
      0 24px 48px -20px rgb(0 0 0 / 55%);
  }
}

html.dark .deck-card--active-ghost {
  border-color: rgb(var(--color-accent) / 45%);
  animation-name: deck-card-pulse-dark;
}

html.dark .dossier-panel-backdrop {
  background: rgb(0 0 0 / 55%);
}

html.dark .dossier-panel {
  background: rgb(var(--color-void-raised) / 92%);
  border-left-color: rgb(var(--color-paper) / 10%);
  box-shadow: -30px 0 60px -20px rgb(0 0 0 / 65%);
}

html.dark .glass-input {
  color: rgb(var(--color-paper) / 92%);
}

html.dark .glass-input::-moz-placeholder {
  color: rgb(var(--color-paper) / 40%);
}

html.dark .glass-input::placeholder {
  color: rgb(var(--color-paper) / 40%);
}

html.dark .glass-input:focus {
  border-color: rgb(var(--color-accent) / 55%);
  background: rgb(var(--color-void-raised) / 78%);
}

/* Même correctif que `:root { color-scheme: light }` en haut de ce fichier,
   pour la popup native d'un <select> — sans ça, un <select> resterait rendu
   avec des couleurs de listbox CLAIRES par le navigateur alors que le reste
   de la page (et le texte de l'option lui-même, `option` ci-dessous) est
   sombre. Chevron redessiné dans la même encre claire que le texte du thème
   sombre plutôt que l'encre sombre du thème clair (`%23211f1c`), sinon
   invisible sur le fond sombre du champ. */
html.dark select.glass-input {
  color-scheme: dark;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23f0eee7' stroke-width='1.5'%3E%3Cpath d='m6 9 6 6 6-6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}

html.dark select.glass-input option {
  /* Même bleu nuit que --color-void-raised (refonte palette) — un <option>
     natif n'hérite pas fiablement des variables CSS/opacité Tailwind selon
     les navigateurs, d'où la valeur littérale, mais elle doit rester
     synchronisée avec --color-void-raised ci-dessus si celle-ci change. */
  background-color: #182136;
  color: #f0eee7;
}

html.dark .geo-loader {
  background: rgb(var(--color-void-raised) / 55%);
  border-color: rgb(var(--color-paper) / 14%);
}

html.dark .project-type-card__media {
  background: rgb(var(--color-paper) / 6%);
}

html.dark .project-type-card__label {
  border-top-color: rgb(var(--color-paper) / 12%);
}

.placeholder\:text-paper\/40::-moz-placeholder {
  color: rgb(var(--color-paper) / 0.4);
}

.placeholder\:text-paper\/40::placeholder {
  color: rgb(var(--color-paper) / 0.4);
}

.first\:border-t-0:first-child {
  border-top-width: 0px;
}

.first\:pt-0:first-child {
  padding-top: 0px;
}

.last\:border-0:last-child {
  border-width: 0px;
}

.hover\:-translate-y-0\.5:hover {
  --tw-translate-y: -0.125rem;
  transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
}

.hover\:scale-110:hover {
  --tw-scale-x: 1.1;
  --tw-scale-y: 1.1;
  transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
}

.hover\:border-paper\/30:hover {
  border-color: rgb(var(--color-paper) / 0.3);
}

.hover\:bg-paper\/10:hover {
  background-color: rgb(var(--color-paper) / 0.1);
}

.hover\:bg-paper\/5:hover {
  background-color: rgb(var(--color-paper) / 0.05);
}

.hover\:text-paper:hover {
  --tw-text-opacity: 1;
  color: rgb(var(--color-paper) / var(--tw-text-opacity, 1));
}

.hover\:text-paper\/60:hover {
  color: rgb(var(--color-paper) / 0.6);
}

.hover\:text-paper\/70:hover {
  color: rgb(var(--color-paper) / 0.7);
}

.hover\:text-paper\/80:hover {
  color: rgb(var(--color-paper) / 0.8);
}

.hover\:text-paper\/90:hover {
  color: rgb(var(--color-paper) / 0.9);
}

.focus-visible\:outline:focus-visible {
  outline-style: solid;
}

.focus-visible\:outline-2:focus-visible {
  outline-width: 2px;
}

.focus-visible\:outline-offset-8:focus-visible {
  outline-offset: 8px;
}

.focus-visible\:outline-accent:focus-visible {
  outline-color: rgb(var(--color-accent) / 1);
}

.active\:scale-\[0\.98\]:active {
  --tw-scale-x: 0.98;
  --tw-scale-y: 0.98;
  transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
}

.disabled\:cursor-not-allowed:disabled {
  cursor: not-allowed;
}

.disabled\:opacity-40:disabled {
  opacity: 0.4;
}

.disabled\:opacity-50:disabled {
  opacity: 0.5;
}

.group:hover .group-hover\:scale-105 {
  --tw-scale-x: 1.05;
  --tw-scale-y: 1.05;
  transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
}

.group:hover .group-hover\:opacity-100 {
  opacity: 1;
}

@media (min-width: 640px) {

  .sm\:col-span-2 {
    grid-column: span 2 / span 2;
  }

  .sm\:col-span-3 {
    grid-column: span 3 / span 3;
  }

  .sm\:grid-cols-2 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .sm\:grid-cols-3 {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }

  .sm\:grid-cols-4 {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }

  .sm\:flex-row {
    flex-direction: row;
  }

  .sm\:items-end {
    align-items: flex-end;
  }

  .sm\:items-center {
    align-items: center;
  }

  .sm\:justify-between {
    justify-content: space-between;
  }

  .sm\:gap-4 {
    gap: 1rem;
  }

  .sm\:px-8 {
    padding-left: 2rem;
    padding-right: 2rem;
  }

  .sm\:text-left {
    text-align: left;
  }
}

@media (min-width: 768px) {

  .md\:bottom-10 {
    bottom: 2.5rem;
  }

  .md\:bottom-14 {
    bottom: 3.5rem;
  }

  .md\:bottom-28 {
    bottom: 7rem;
  }

  .md\:bottom-6 {
    bottom: 1.5rem;
  }

  .md\:left-12 {
    left: 3rem;
  }

  .md\:right-10 {
    right: 2.5rem;
  }

  .md\:right-6 {
    right: 1.5rem;
  }

  .md\:top-6 {
    top: 1.5rem;
  }

  .md\:z-10 {
    z-index: 10;
  }

  .md\:block {
    display: block;
  }

  .md\:\!h-11 {
    height: 2.75rem !important;
  }

  .md\:h-96 {
    height: 24rem;
  }

  .md\:\!w-11 {
    width: 2.75rem !important;
  }

  .md\:w-56 {
    width: 14rem;
  }

  .md\:scale-105 {
    --tw-scale-x: 1.05;
    --tw-scale-y: 1.05;
    transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
  }

  .md\:grid-cols-3 {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }

  .md\:grid-cols-4 {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }

  .md\:flex-row {
    flex-direction: row;
  }

  .md\:flex-row-reverse {
    flex-direction: row-reverse;
  }

  .md\:items-start {
    align-items: flex-start;
  }

  .md\:items-center {
    align-items: center;
  }

  .md\:justify-between {
    justify-content: space-between;
  }

  .md\:gap-16 {
    gap: 4rem;
  }

  .md\:gap-8 {
    gap: 2rem;
  }

  .md\:rounded-3xl {
    border-radius: 1.5rem;
  }

  .md\:p-12 {
    padding: 3rem;
  }

  .md\:p-6 {
    padding: 1.5rem;
  }

  .md\:px-10 {
    padding-left: 2.5rem;
    padding-right: 2.5rem;
  }

  .md\:px-8 {
    padding-left: 2rem;
    padding-right: 2rem;
  }

  .md\:py-10 {
    padding-top: 2.5rem;
    padding-bottom: 2.5rem;
  }

  .md\:py-20 {
    padding-top: 5rem;
    padding-bottom: 5rem;
  }

  .md\:py-4 {
    padding-top: 1rem;
    padding-bottom: 1rem;
  }

  .md\:pb-16 {
    padding-bottom: 4rem;
  }

  .md\:pt-10 {
    padding-top: 2.5rem;
  }

  .md\:pt-28 {
    padding-top: 7rem;
  }

  .md\:pt-32 {
    padding-top: 8rem;
  }

  .md\:pt-40 {
    padding-top: 10rem;
  }

  .md\:text-left {
    text-align: left;
  }

  .md\:text-2xl {
    font-size: 1.5rem;
    line-height: 2rem;
  }

  .md\:text-3xl {
    font-size: 1.875rem;
    line-height: 2.25rem;
  }

  .md\:text-4xl {
    font-size: 2.25rem;
    line-height: 2.5rem;
  }

  .md\:text-5xl {
    font-size: 3rem;
    line-height: 1;
  }

  .md\:text-6xl {
    font-size: 3.75rem;
    line-height: 1;
  }

  .md\:text-base {
    font-size: 1rem;
    line-height: 1.5rem;
  }
}

@media (min-width: 1024px) {

  .lg\:col-span-2 {
    grid-column: span 2 / span 2;
  }

  .lg\:max-w-none {
    max-width: none;
  }

  .lg\:max-w-sm {
    max-width: 24rem;
  }

  .lg\:grid-cols-2 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .lg\:grid-cols-3 {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }

  .lg\:grid-cols-4 {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }

  .lg\:grid-cols-5 {
    grid-template-columns: repeat(5, minmax(0, 1fr));
  }

  .lg\:items-start {
    align-items: flex-start;
  }

  .lg\:justify-start {
    justify-content: flex-start;
  }

  .lg\:gap-12 {
    gap: 3rem;
  }

  .lg\:text-left {
    text-align: left;
  }
}

@media (min-width: 1280px) {

  .xl\:block {
    display: block;
  }

  .xl\:hidden {
    display: none;
  }

  .xl\:h-\[32rem\] {
    height: 32rem;
  }

  .xl\:grid-cols-3 {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }

  .xl\:grid-cols-4 {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
}


