/* ==========================================================================
   CMC 6 : couche FX (glassmorphisme + motion poussé, budget perf strict)
   Chargée APRES styles.css. Transform/opacity uniquement (GPU). Tout le
   motion non essentiel tombe sous prefers-reduced-motion.
   Fil de lecture : je comprends > tu es credible > je vois des preuves > je reserve
   ========================================================================== */

/* ---------- Variables FX ---------- */
:root {
  --glass-bg: rgba(255, 255, 255, 0.045);
  --glass-stroke-strong: rgba(255, 255, 255, 0.5);
  --glass-stroke-soft: rgba(255, 255, 255, 0.14);
  --ease-spring: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --page-max: 1440px;        /* largeur max structurante (grand ecran) */
  --gutter: 24px;            /* marge du cadre = ancre du notch */
  --notch-r: 36px;           /* rayon des angles concaves du notch + coins bas de la nav */
  /* Rayon des coins de la coque hero : CONTINU. Plus petit en largeur etroite
     (libere de la place pour un galbe de notch rond), grand en large. Cale pour
     ~18px a 994px et ~38px a 1308px. */
  --hr: clamp(14px, calc(18px + (100vw - 994px) * 0.064), 42px);
}

/* ==========================================================================
   0bis. Switch Gris <-> Ardoise (decision prise, plus un outil de test)
   ========================================================================== */
.theme-toggle-top {
  position: fixed;
  top: var(--s-4);
  right: var(--s-4);
  z-index: 96;
  display: flex;
  align-items: center;
  gap: 0.55rem;
  padding: 0.4rem 0.85rem 0.4rem 0.4rem;
  border: none;
  border-radius: var(--radius-pill);
  background: color-mix(in oklab, var(--night-raised) 88%, var(--night-deep));
  box-shadow: inset 0 0 0 1px var(--lavender-15), 0 12px 30px -16px rgba(0, 0, 0, 0.5);
  -webkit-backdrop-filter: blur(14px) saturate(160%);
  backdrop-filter: blur(14px) saturate(160%);
  color: var(--lavender);
  font-family: var(--font-body);
  font-size: 0.8125rem;
  font-weight: 600;
  cursor: pointer;
  transition: color 0.25s var(--ease-out);
}
.theme-toggle-top[aria-pressed="true"] { color: var(--cream); }
.theme-toggle-track {
  position: relative;
  width: 34px;
  height: 18px;
  border-radius: 999px;
  background: var(--lavender-15);
  flex-shrink: 0;
  transition: background-color 0.25s var(--ease-out);
}
.theme-toggle-thumb {
  position: absolute;
  top: 2px;
  left: 2px;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--cream);
  transition: transform 0.25s var(--ease-spring);
}
.theme-toggle-top[aria-pressed="true"] .theme-toggle-track { background: var(--ember); }
.theme-toggle-top[aria-pressed="true"] .theme-toggle-thumb { transform: translateX(16px); }

@media (max-width: 640px) {
  .theme-toggle-top {
    top: calc(env(safe-area-inset-top, 0px) + var(--s-3));
    right: var(--s-3);
    padding: 0.3rem 0.4rem;
  }
  .theme-toggle-label { display: none; }
}

/* ==========================================================================
   1. Barre de progression de scroll
   ========================================================================== */
.scroll-progress {
  position: fixed;
  inset: 0 0 auto 0;
  height: 2px;
  z-index: 100;
  transform: scaleX(0);
  transform-origin: 0 50%;
  background: linear-gradient(90deg, var(--shape-periwinkle), var(--ember));
  will-change: transform;
  pointer-events: none;
}

/* ==========================================================================
   2. Systeme glass reactif
   ========================================================================== */
.glass {
  position: relative;
  overflow: hidden;
  border: none;
  background: var(--glass-bg);
  -webkit-backdrop-filter: blur(18px) saturate(165%);
  backdrop-filter: blur(18px) saturate(165%);
  box-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.12);
}
.glass::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 1.4px;
  background: linear-gradient(
    160deg,
    var(--glass-stroke-strong) 0%,
    var(--glass-stroke-soft) 22%,
    transparent 45%,
    transparent 58%,
    var(--glass-stroke-soft) 80%,
    var(--glass-stroke-strong) 100%
  );
  -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
          mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  pointer-events: none;
  z-index: 1;
}

/* Carte glass : tilt 3D + halo qui suit le pointeur (--mx/--my en %) */
.glass-card {
  position: relative;
  border-radius: var(--radius);
  padding: clamp(1.5rem, 3vw, 2.25rem);
  /* Fond plein (pas de backdrop-filter) : les cartes sont sur fond sombre uni,
     le blur coute cher au scroll sans rien apporter ici. Budget perf. */
  background: color-mix(in oklab, var(--night-raised) 92%, var(--night-deep));
  box-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.08), 0 18px 50px -30px rgba(0, 0, 0, 0.8);
  transform: perspective(900px) rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg)) translateZ(0);
  transition: transform 0.4s var(--ease-spring), box-shadow 0.4s var(--ease-out);
  transform-style: preserve-3d;
  overflow: hidden;
  isolation: isolate;
}
.glass-card::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 1px;
  background: linear-gradient(150deg, rgba(255, 255, 255, 0.35), rgba(255, 255, 255, 0.04) 40%, transparent 70%);
  -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
          mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  pointer-events: none;
  z-index: 2;
}
/* Halo reactif : centre = pointeur */
.glass-card::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: radial-gradient(280px circle at var(--mx, 50%) var(--my, 0%), rgba(246, 25, 66, 0.18), transparent 60%);
  opacity: 0;
  transition: opacity 0.4s var(--ease-out);
  pointer-events: none;
  z-index: 0;
}
.glass-card:hover {
  box-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.14), 0 30px 70px -28px rgba(0, 0, 0, 0.9);
}

/* Lift au survol. NB : sur la home les cartes sont aussi [data-fx], et la regle
   de reveal `.js [data-fx].is-in { transform: none }` (spec 0,3,0) ecrase le
   transform de .glass-card. Il faut donc une regle :hover plus specifique pour
   que le lift prenne. Gate hover+motion : pas de hover collant sur tactile, pas
   de mouvement en reduced-motion. */
@media (hover: hover) and (prefers-reduced-motion: no-preference) {
  .js [data-fx].glass-card.is-in:hover { transform: translateY(-5px); }
}
.glass-card:hover::after { opacity: 1; }
.glass-card > * { position: relative; z-index: 1; }

/* Pilule glass (eyebrow, badges) */
.glass-pill {
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  padding: 0.5rem 1rem;
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.05);
  -webkit-backdrop-filter: blur(14px) saturate(170%);
  backdrop-filter: blur(14px) saturate(170%);
  box-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.12);
  font-size: var(--text-label);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--lavender);
}
.glass-pill .dot-live {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--ember);
  box-shadow: 0 0 0 0 rgba(246, 25, 66, 0.6);
  animation: pulseLive 2.4s var(--ease-out) infinite;
}
@keyframes pulseLive {
  0% { box-shadow: 0 0 0 0 rgba(246, 25, 66, 0.55); }
  70% { box-shadow: 0 0 0 8px rgba(246, 25, 66, 0); }
  100% { box-shadow: 0 0 0 0 rgba(246, 25, 66, 0); }
}

/* ==========================================================================
   3. Hero cinetique
   ========================================================================== */
.hero {
  min-height: 100svh;
  display: flex;
  align-items: center;
}
.hero-inner { padding-block: clamp(7rem, 16vh, 11rem); }

/* Titre : degrade anime + clip reveal ligne par ligne.
   Le degrade background-clip:text est porte par le SPAN (le texte), pas par
   le h1 : un parent .clip-line en overflow:hidden empeche sinon le degrade
   de remplir les glyphes (texte transparent = invisible). */
.hero h1 { color: var(--cream); }
.hero h1 .clip-line > span {
  background: linear-gradient(100deg, var(--cream) 0%, var(--cream) 42%, #ffd7e1 55%, var(--cream) 70%, var(--cream) 100%);
  background-size: 220% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
}
@keyframes titleSheen {
  0%, 100% { background-position: 0% 0; }
  50% { background-position: 100% 0; }
}

.clip-line { display: block; overflow: hidden; padding-bottom: 0.08em; }
/* Auto-play a l'ouverture : robuste meme si motion.js echoue */
.js .clip-line > span {
  display: block;
  animation: lineIn 0.95s var(--ease-spring) both, titleSheen 9s var(--ease-out) infinite;
  animation-delay: calc(var(--i, 0) * 90ms + 150ms), 0s;
}
@keyframes lineIn {
  from { transform: translateY(110%); }
  to { transform: translateY(0); }
}

/* Entree douce des autres blocs du hero */
.js .hero-rise {
  animation: riseIn 0.85s var(--ease-out) both;
  animation-delay: calc(var(--i, 0) * 90ms + 400ms);
}
@keyframes riseIn {
  from { opacity: 0; transform: translateY(22px); }
  to { opacity: 1; transform: none; }
}

/* Logos clients de confiance sous le hero */
.hero-trust {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 1.5rem;
  margin-top: var(--s-8);
  color: var(--lavender-dim);
  font-size: 0.9rem;
}
.hero-trust .trust-logos {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem 1.25rem;
  list-style: none;
  padding: 0;
}
.hero-trust .trust-logos a {
  display: inline-flex;
  align-items: center;
  opacity: 1;
  transition: transform 0.25s var(--ease-spring);
}
.hero-trust .trust-logos a:hover { transform: translateY(-2px); }
/* Vrais logos clients en COULEURS d'origine sur fond sombre. Legerement attenues
   au repos, pleins au survol. Elema est un logo NOIR (concu pour fond clair) :
   on l'inverse en blanc pour qu'il soit visible (cf .ti-invert). */
.hero-trust .trust-logos img {
  height: 24px;
  width: auto;
  max-width: 124px;
  object-fit: contain;
  opacity: 0.88;
  transition: opacity 0.25s var(--ease-out);
}
.hero-trust .trust-logos .ti-invert {
  filter: brightness(0) invert(1);
  opacity: 0.78;
}
.hero-trust .trust-logos a:hover img { opacity: 1; }

/* Indice de scroll */
.scroll-cue {
  position: absolute;
  left: 50%;
  bottom: clamp(1.25rem, 4vh, 2.5rem);
  transform: translateX(-50%);
  display: grid;
  justify-items: center;
  gap: 0.5rem;
  color: var(--lavender-dim);
  font-size: 0.7rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
}
.scroll-cue .line {
  width: 1px;
  height: 38px;
  background: linear-gradient(var(--lavender), transparent);
  animation: cueDrop 2.2s var(--ease-out) infinite;
  transform-origin: top;
}
@keyframes cueDrop {
  0% { transform: scaleY(0); opacity: 0; }
  35% { transform: scaleY(1); opacity: 1; }
  100% { transform: scaleY(1) translateY(12px); opacity: 0; }
}

/* Parallaxe sur les formes du fond (translateY pilote par JS via --par) */
.hero-shapes { will-change: transform; }

/* ==========================================================================
   4. Reveals scroll-driven (data-fx + stagger via --i)
   ========================================================================== */
.js [data-fx] {
  opacity: 0;
  will-change: transform, opacity;
  transition: opacity 0.7s var(--ease-out), transform 0.9s var(--ease-spring), filter 0.7s var(--ease-out);
  transition-delay: calc(var(--i, 0) * 80ms);
}
.js [data-fx="up"] { transform: translateY(34px); }
.js [data-fx="scale"] { transform: scale(0.94); }
.js [data-fx="blur"] { filter: blur(14px); transform: translateY(18px); }
.js [data-fx="left"] { transform: translateX(-40px); }
.js [data-fx="right"] { transform: translateX(40px); }
.js [data-fx].is-in {
  opacity: 1;
  transform: none;
  filter: none;
}

/* Texte qui s'eclaire mot par mot au scroll (statement) */
.kinetic { line-height: 1.18; }
.kinetic .word {
  /* Lisible de base (gris lavande), s'eclaire en cream au scroll */
  color: var(--lavender-dim);
  transition: color 0.5s var(--ease-out);
}
.kinetic .word.lit { color: var(--cream); }
.kinetic .word.accent.lit { color: var(--ember); }

/* ==========================================================================
   5. Boutons magnetiques + glass CTA renforce
   ========================================================================== */
.magnetic { will-change: transform; }
.btn { position: relative; overflow: hidden; }
.btn-primary::after {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(120px circle at var(--mx, 50%) var(--my, 50%), rgba(246, 25, 66, 0.25), transparent 60%);
  opacity: 0;
  transition: opacity 0.3s var(--ease-out);
}
.btn-primary:hover::after { opacity: 1; }

.magnetic > * { position: relative; z-index: 1; }

/* ==========================================================================
   6. Marquee glass
   ========================================================================== */
.proof .marquee {
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
}
.proof .marquee-track li {
  position: relative;
  transition: color 0.25s var(--ease-out);
}

/* ==========================================================================
   7. Cartes resultats / projets / engagements en glass
   ========================================================================== */
.results-grid,
.pledges-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--s-6);
}
.result-card .metric {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(2.8rem, 6vw, 4.2rem);
  line-height: 1;
  letter-spacing: -0.03em;
  background: linear-gradient(180deg, var(--cream), color-mix(in oklab, var(--ember) 60%, var(--cream)));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  margin-bottom: var(--s-3);
}
.result-card .metric sup { font-size: 0.4em; vertical-align: super; -webkit-text-fill-color: var(--ember); }
.result-card h3 { font-size: 1.15rem; margin-bottom: var(--s-2); }
.result-card p { color: var(--lavender); font-size: 0.98rem; }
.result-card .src {
  display: inline-block;
  margin-top: var(--s-4);
  font-size: var(--text-label);
  letter-spacing: 0.05em;
  color: var(--lavender-dim);
}

/* Carte projet : screenshot + survol */
.proj-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: var(--s-6);
}
.proj-card { padding: 0; }
.proj-card .shot {
  position: relative;
  aspect-ratio: 16 / 10;
  overflow: hidden;
  border-radius: var(--radius) var(--radius) 0 0;
}
.proj-card .shot img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transform: scale(1.02);
  transition: transform 0.7s var(--ease-spring);
}
.proj-card:hover .shot img { transform: scale(1.08); }
.proj-card .shot::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, transparent 50%, rgba(13, 9, 20, 0.65));
}
.proj-card .proj-body { padding: clamp(1.25rem, 3vw, 1.75rem); }
.proj-card .proj-body h3 { font-size: 1.25rem; margin-bottom: 0.35rem; }
.proj-card .proj-meta { color: var(--lavender-dim); font-size: 0.85rem; margin-bottom: var(--s-3); }
.proj-card .proj-link {
  display: inline-flex;
  align-items: center;
  gap: 0.4em;
  font-weight: 600;
  color: var(--cream);
  text-decoration: none;
}
.proj-card .proj-link span { transition: transform 0.3s var(--ease-spring); }
.proj-card:hover .proj-link span { transform: translate(3px, -3px); }

/* ==========================================================================
   8. Accordeon FAQ glass
   ========================================================================== */
.faq-glass { display: grid; gap: var(--s-3); max-width: 880px; }
.faq-glass details {
  border-radius: var(--radius);
  padding: 0 clamp(1.1rem, 3vw, 1.6rem);
  background: color-mix(in oklab, var(--night-raised) 50%, transparent);
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
  box-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.07);
}
.faq-glass summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--s-4);
  padding: clamp(1rem, 2.5vw, 1.35rem) 0;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.1rem;
}
.faq-glass summary::-webkit-details-marker { display: none; }
.faq-glass summary .chev {
  flex: none;
  width: 22px;
  height: 22px;
  transition: transform 0.35s var(--ease-spring);
  color: var(--ember);
}
.faq-glass details[open] summary .chev { transform: rotate(45deg); }
.faq-glass details p {
  color: var(--lavender);
  padding-bottom: clamp(1rem, 2.5vw, 1.35rem);
  max-width: 68ch;
}

/* ==========================================================================
   9. Rythme de sections + accents typo
   ========================================================================== */
.section-head { max-width: 60ch; margin-bottom: clamp(2.5rem, 6vh, 4.5rem); }
.section-head h2 { font-size: var(--text-h2); margin-block: var(--s-3) var(--s-4); }
.section-head p { color: var(--lavender); font-size: var(--text-lead); }

/* Numeral geant en filigrane de section */
.big-numeral {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(9rem, 26vw, 22rem);
  line-height: 0.8;
  letter-spacing: -0.04em;
  color: transparent;
  -webkit-text-stroke: 1px var(--lavender-15);
  pointer-events: none;
  user-select: none;
}

/* ==========================================================================
   10. Responsive + reduced-motion
   ========================================================================== */
@media (max-width: 720px) {
  .glass-card { transform: none !important; }
  .hero h1 { animation-duration: 12s; }
}

@media (prefers-reduced-motion: reduce) {
  .scroll-progress,
  .scroll-cue,
  .hero h1,
  .dot-live { animation: none !important; }
  .hero h1 { background-position: 0 0; }
  .clip-line > span,
  .hero-rise,
  .js [data-fx] {
    transform: none !important;
    opacity: 1 !important;
    filter: none !important;
    transition: none !important;
    animation: none !important;
  }
  .glass-card { transform: none !important; }
  .kinetic .word { color: var(--cream); }
  .proj-card .shot img { transform: none !important; }
}

/* ==========================================================================
   FX v2 : techniques signature (curseur, rouleaux, volet, pin, intro)
   Toujours transform/opacity. Tout tombe sous reduced-motion / tactile.
   ========================================================================== */

/* ---------- Intro de marque (auto-play, robuste sans JS) ---------- */
.intro {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: grid;
  place-content: center;
  background: var(--night-deep);
  animation: introOut 1.15s var(--ease-spring) 0.55s forwards;
}
.intro-mark {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(3rem, 12vw, 7rem);
  letter-spacing: -0.04em;
  color: var(--cream);
  opacity: 0;
  transform: translateY(14px);
  animation: introMark 0.7s var(--ease-spring) 0.1s forwards;
}
.intro-mark em { color: var(--ember); font-style: normal; }
@keyframes introMark { to { opacity: 1; transform: none; } }
@keyframes introOut {
  to { transform: translateY(-101%); }
}
.intro.skip { display: none; }

/* ---------- Curseur custom ---------- */
.cursor { position: fixed; top: 0; left: 0; z-index: 150; pointer-events: none; display: none; }
body.cursorless,
body.cursorless a,
body.cursorless button,
body.cursorless summary,
body.cursorless .glass-card,
body.cursorless input { cursor: none; }
body.cursorless .cursor { display: block; }
.c-dot {
  position: fixed;
  top: 0; left: 0;
  width: 7px; height: 7px;
  margin: -3.5px 0 0 -3.5px;
  border-radius: 50%;
  background: var(--ember);
  will-change: transform;
}
.c-ring {
  position: fixed;
  top: 0; left: 0;
  width: 34px; height: 34px;
  margin: -17px 0 0 -17px;
  border-radius: 50%;
  border: 1.5px solid rgba(255, 255, 255, 0.85);
  mix-blend-mode: difference;
  display: grid;
  place-content: center;
  will-change: transform;
  transition: width 0.3s var(--ease-spring), height 0.3s var(--ease-spring), background-color 0.3s var(--ease-out), border-color 0.3s var(--ease-out);
}
.cursor.is-hover .c-ring { width: 56px; height: 56px; margin: -28px 0 0 -28px; }
.cursor.is-label .c-ring {
  width: 76px; height: 76px;
  margin: -38px 0 0 -38px;
  background: var(--ember);
  border-color: transparent;
  mix-blend-mode: normal;
}
.c-label {
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: #fff;
  opacity: 0;
  transform: scale(0.6);
  transition: opacity 0.25s var(--ease-out), transform 0.25s var(--ease-spring);
  white-space: nowrap;
}
.cursor.is-label .c-label { opacity: 1; transform: none; }

/* Halo doux : glow flou, sans forme geometrique. */
.c-halo {
  position: fixed;
  top: 0; left: 0;
  width: 64px; height: 64px;
  margin: -32px 0 0 -32px;
  border-radius: 50%;
  background: radial-gradient(circle, var(--ember) 0%, transparent 70%);
  filter: blur(10px);
  will-change: transform;
}

/* Trainee : pool de petits points, positionnes par JS (effet comete). */
.c-trail-dot {
  position: fixed;
  top: 0; left: 0;
  width: 9px; height: 9px;
  margin: -4.5px 0 0 -4.5px;
  border-radius: 50%;
  background: var(--ember);
  will-change: transform;
}

/* Visibilite par style actif : attribut data-cursor-style sur <html>, pose par
   le panneau de comparaison (motion.js). Chaque style montre uniquement ses
   propres elements, tout le reste reste invisible (pas de display:none pour
   ne pas geler la boucle rAF qui continue a les positionner en fond). */
.c-dot, .c-ring, .c-halo, .c-trail-dot { opacity: 0; }
html[data-cursor-style="classic"] .c-dot,
html[data-cursor-style="classic"] .c-ring { opacity: 1; }
html[data-cursor-style="ring"] .c-ring { opacity: 1; }
html[data-cursor-style="halo"] .c-halo { opacity: 0.6; }
html[data-cursor-style="tight"] .c-dot { opacity: 1; }
html[data-cursor-style="trail"] .c-dot,
html[data-cursor-style="trail"] .c-trail-dot { opacity: 1; }

/* ---------- Compteurs a rouleaux (odometre) ---------- */
.roll { display: inline-flex; align-items: center; font-variant-numeric: tabular-nums; }
.roll .col {
  display: inline-block;
  height: 1em;
  overflow: hidden;
  vertical-align: baseline;
}
.roll .reel {
  display: flex;
  flex-direction: column;
  transform: translateY(0);
  transition: transform 1.3s cubic-bezier(0.16, 1, 0.3, 1);
}
.roll .reel span { height: 1em; line-height: 1; display: block; text-align: center; }
/* Chaque glyphe porte le degrade (overflow:hidden empeche le clip parent) */
.result-card .metric.is-rolled .roll-aff,
.result-card .metric.is-rolled .reel span {
  background: linear-gradient(180deg, var(--cream), color-mix(in oklab, var(--ember) 60%, var(--cream)));
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
}
.result-card .metric.is-rolled .roll-aff.suffix { -webkit-text-fill-color: var(--ember); }

/* ---------- Reveal des captures projet en volet ---------- */
.proj-card .shot img {
  clip-path: inset(0 0 100% 0);
  transition: clip-path 0.95s var(--ease-spring), transform 0.7s var(--ease-spring);
  will-change: clip-path, transform;
}
.proj-card.is-in .shot img { clip-path: inset(0 0 0 0); }

/* ---------- Sequence epinglee Methode 21 (desktop) ---------- */
.method-rail {
  position: relative;
  height: 2px;
  margin: var(--s-12) 0 var(--s-8);
  background: var(--lavender-15);
  border-radius: 2px;
}
.method-rail .rail-fill {
  position: absolute;
  inset: 0 auto 0 0;
  width: var(--fill, 8%);
  background: linear-gradient(90deg, var(--shape-periwinkle), var(--ember));
  border-radius: 2px;
  transition: width 0.25s var(--ease-out);
}
.method-rail .rail-step {
  position: absolute;
  top: 50%;
  transform: translate(-50%, -50%);
  display: grid;
  justify-items: center;
  gap: 0.5rem;
  font-size: var(--text-label);
  color: var(--lavender-dim);
  letter-spacing: 0.05em;
  white-space: nowrap;
}
.method-rail .rail-step::before {
  content: "";
  width: 12px; height: 12px;
  border-radius: 50%;
  background: var(--night-raised);
  box-shadow: inset 0 0 0 1.5px var(--lavender-30);
  transition: background-color 0.3s var(--ease-out), box-shadow 0.3s var(--ease-out), transform 0.3s var(--ease-spring);
}
.method-rail .rail-step span { transform: translateY(2px); }
.method-rail .rail-step.done::before { background: var(--ember); box-shadow: inset 0 0 0 1.5px var(--ember), 0 0 16px var(--ember-glow); transform: scale(1.15); }
.method-rail .rail-step.done { color: var(--cream); }

.phase-card {
  transition: transform 0.5s var(--ease-spring), box-shadow 0.5s var(--ease-out), opacity 0.5s var(--ease-out);
}
.phase-card .phase-num {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 1rem;
  letter-spacing: 0.05em;
  color: var(--ember);
}

/* La sequence Methode n'est plus epinglee : section normale, 3 phases visibles
   d'emblee, a egalite (plus de dim ni de scroll-jacking). */

/* ==========================================================================
   Reduced-motion / tactile : on neutralise toute la couche FX v2
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  .intro { display: none; }
  .cursor { display: none !important; }
  .proj-card .shot img { clip-path: none !important; transition: none !important; }
  .roll .reel { transition: none !important; }
  .method-seq { height: auto !important; }
  .method-seq .method-pin { position: static !important; min-height: 0 !important; }
  .method-seq .phase-card { opacity: 1 !important; transform: none !important; }
  .method-rail .rail-fill { width: 100% !important; }
  /* Morph notch <-> pilule instantane (pas d'animation) */
  .nav-pill,
  .nav-pill::before,
  .nav-pill::after,
  .nav-frame,
  .frame-corner { transition: none !important; }
}
@media (hover: none) {
  .cursor { display: none !important; }
  .method-seq { height: auto; }
  .method-seq .method-pin { position: static; min-height: 0; }
  .method-seq .phase-card { opacity: 1; transform: none; }
}

/* Skip intro au retour (classe posee en <head> avant le paint) */
html.no-intro .intro { display: none; }
/* Rail Methode : sequence epinglee desktop seulement */
@media (max-width: 860px) { .method-rail { display: none; } }

/* ==========================================================================
   COQUE HERO : nav + hero dans un grand cadre arrondi insere (test)
   ========================================================================== */
/* Fond du body : aubergine profond (plus vivant que le quasi-noir) + lueur douce
   en haut (fixe au viewport = profondeur) + motif grille. Tout INTEGRE au
   background -> derriere absolument tout (contenu, cartes, nav). */
.shell-page {
  background-color: oklch(0.145 0.04 300);
  background-image:
    linear-gradient(rgba(255, 255, 255, 0.055) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255, 255, 255, 0.055) 1px, transparent 1px),
    radial-gradient(120% 90% at 50% 0%, oklch(0.22 0.08 305), transparent 55%);
  background-size: 64px 64px, 64px 64px, 100% 100%;
  background-attachment: scroll, scroll, fixed;
}

/* (Motif de fond integre au background de .shell-page ci-dessus -> tout en
   arriere-plan, derriere le contenu et la nav.) */

/* ============ APERCU THEME CREPUSCULE (preview) : panneau theme, valeur "dusk" ============
   Toujours "famille sombre" (texte clair --cream inchange, pas besoin de
   repingler le hero) : on remonte juste la luminosite des 3 surfaces, la nuit
   la plus profonde etait jugee trop dense. .glass-card etc suivent tout seuls
   (deja bases sur color-mix(--night-raised, --night-deep)). */
html.theme-dusk {
  --night-deep: oklch(0.19 0.025 302);
  --night: oklch(0.25 0.028 305);
  --night-raised: oklch(0.31 0.032 310);
}
html.theme-dusk body.shell-page {
  background-color: oklch(0.21 0.045 300);
  background-image:
    linear-gradient(rgba(255, 255, 255, 0.06) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255, 255, 255, 0.06) 1px, transparent 1px),
    radial-gradient(120% 90% at 50% 0%, oklch(0.32 0.095 305), transparent 55%);
  /* scroll (pas fixed) + hauteur en vh (pas %) : sinon le halo reste plaque
     au viewport pendant tout le scroll et son bord de fondu retombe a une
     hauteur d'ecran fixe, qui denote sur les sections en dessous (signale
     par l'operateur, cf. theme-light/slate). Ici il scrolle avec la page et
     disparait apres ~1 ecran, avant "Ils ont fait appel a nous". */
  background-size: 64px 64px, 64px 64px, 100% 100vh;
  background-attachment: scroll, scroll, scroll;
  background-repeat: repeat, repeat, no-repeat;
}

/* ============ 5 AUTRES PISTES "entre-deux" (carte blanche, meme luminosite
   approx que Crepuscule, hue/chroma differents) : toutes famille sombre,
   --cream inchange, hero jamais repingle. ============ */

/* Ardoise : plus froid et desature que Crepuscule, moins de chaleur magenta. */
html.theme-slate {
  --night-deep: oklch(0.20 0.018 288);
  --night: oklch(0.27 0.02 288);
  --night-raised: oklch(0.34 0.022 290);
}
html.theme-slate body.shell-page {
  background-color: oklch(0.23 0.03 286);
  background-image:
    linear-gradient(rgba(255, 255, 255, 0.055) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255, 255, 255, 0.055) 1px, transparent 1px),
    radial-gradient(120% 90% at 50% 0%, oklch(0.34 0.06 288), transparent 55%);
  background-size: 64px 64px, 64px 64px, 100% 100vh;
  background-attachment: scroll, scroll, scroll;
  background-repeat: repeat, repeat, no-repeat;
}

/* Encre : vire au bleu-indigo plutot qu'au violet-magenta. */
html.theme-ink {
  --night-deep: oklch(0.18 0.035 268);
  --night: oklch(0.24 0.04 268);
  --night-raised: oklch(0.31 0.045 270);
}
html.theme-ink body.shell-page {
  background-color: oklch(0.20 0.055 266);
  background-image:
    linear-gradient(rgba(255, 255, 255, 0.06) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255, 255, 255, 0.06) 1px, transparent 1px),
    radial-gradient(120% 90% at 50% 0%, oklch(0.33 0.11 268), transparent 55%);
  background-size: 64px 64px, 64px 64px, 100% 100vh;
  background-attachment: scroll, scroll, scroll;
  background-repeat: repeat, repeat, no-repeat;
}

/* Grenat : chaud, tire vers le vin/l'ember plutot que le violet. */
html.theme-garnet {
  --night-deep: oklch(0.20 0.035 15);
  --night: oklch(0.27 0.04 15);
  --night-raised: oklch(0.34 0.045 16);
}
html.theme-garnet body.shell-page {
  background-color: oklch(0.22 0.05 12);
  background-image:
    linear-gradient(rgba(255, 255, 255, 0.06) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255, 255, 255, 0.06) 1px, transparent 1px),
    radial-gradient(120% 90% at 50% 0%, oklch(0.35 0.11 15), transparent 55%);
  background-size: 64px 64px, 64px 64px, 100% 100vh;
  background-attachment: scroll, scroll, scroll;
  background-repeat: repeat, repeat, no-repeat;
}

/* Mousse : neutre froid, pointe de vert, tres desature (quasi charbon). */
html.theme-moss {
  --night-deep: oklch(0.20 0.012 155);
  --night: oklch(0.27 0.014 155);
  --night-raised: oklch(0.34 0.016 155);
}
html.theme-moss body.shell-page {
  background-color: oklch(0.22 0.018 150);
  background-image:
    linear-gradient(rgba(255, 255, 255, 0.05) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255, 255, 255, 0.05) 1px, transparent 1px),
    radial-gradient(120% 90% at 50% 0%, oklch(0.34 0.045 155), transparent 55%);
  background-size: 64px 64px, 64px 64px, 100% 100vh;
  background-attachment: scroll, scroll, scroll;
  background-repeat: repeat, repeat, no-repeat;
}

/* Sable de nuit : neutre chaud sombre, echo du beige mais en registre nuit. */
html.theme-nightsand {
  --night-deep: oklch(0.21 0.025 65);
  --night: oklch(0.28 0.028 65);
  --night-raised: oklch(0.35 0.032 68);
}
html.theme-nightsand body.shell-page {
  background-color: oklch(0.23 0.035 63);
  background-image:
    linear-gradient(rgba(255, 255, 255, 0.055) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255, 255, 255, 0.055) 1px, transparent 1px),
    radial-gradient(120% 90% at 50% 0%, oklch(0.36 0.07 65), transparent 55%);
  background-size: 64px 64px, 64px 64px, 100% 100vh;
  background-attachment: scroll, scroll, scroll;
  background-repeat: repeat, repeat, no-repeat;
}

/* ============ APERCU THEME CLAIR (preview) : ouvrir l'URL avec #light ============
   Bascule de palette via la classe html.theme-light (posee par motion.js sur
   #light). N'affecte PAS le site normal. Le hero (shader WebGL) reste sombre :
   c'est le seul element non gere par la palette. */
html.theme-light {
  --night-deep: oklch(0.965 0.01 320);
  --night: oklch(0.95 0.013 318);
  --night-raised: oklch(1 0 0);
  --cream: oklch(0.26 0.035 305);
  --lavender: oklch(0.46 0.05 305);
  --lavender-dim: oklch(0.57 0.045 305);
  --lavender-15: rgba(50, 25, 70, 0.10);
  --lavender-08: rgba(50, 25, 70, 0.05);
  --lavender-30: rgba(50, 25, 70, 0.18);
  --ember: oklch(0.55 0.235 18);
  --glass-stroke-strong: rgba(50, 25, 70, 0.18);
  --glass-stroke-soft: rgba(50, 25, 70, 0.06);
}
html.theme-light body.shell-page {
  background-color: oklch(0.975 0.012 345);   /* tres clair, chaud */
  /* V1 wash haut : degrade chaud doux et equilibre en haut (rose gauche, corail
     droite, violet), fondu vers le clair. */
  background-image:
    linear-gradient(rgba(60, 20, 50, 0.04) 1px, transparent 1px),
    linear-gradient(90deg, rgba(60, 20, 50, 0.04) 1px, transparent 1px),
    radial-gradient(80% 65% at 80% -8%, oklch(0.86 0.115 28 / 0.8), transparent 52%),
    radial-gradient(80% 65% at 16% -8%, oklch(0.85 0.13 348 / 0.85), transparent 52%),
    radial-gradient(130% 100% at 50% -4%, oklch(0.89 0.085 305), transparent 58%);
  background-size: 64px 64px, 64px 64px, 100% 100vh, 100% 100vh, 100% 100vh;
  background-attachment: scroll, scroll, scroll, scroll, scroll;
  background-repeat: repeat, repeat, no-repeat, no-repeat, no-repeat;
}
html.theme-light .glass-card {
  background: oklch(1 0 0);
  box-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.6), 0 10px 30px -14px rgba(50, 25, 70, 0.22);
}
html.theme-light .glass-card::before {
  background: linear-gradient(150deg, rgba(50, 25, 70, 0.18), rgba(50, 25, 70, 0.04) 40%, transparent 70%);
}
/* .nav-pill couleur/ombre : MOBILE UNIQUEMENT. Sur desktop, motion.js pilote
   background+ombre en inline a chaque frame de scroll (opaque au notch ->
   transparent en flottant, cf. navGlass) ; un !important ici gagnait quand
   meme sur l'inline et cassait le fondu (ombre figee, jamais transparente
   au notch). motion.js lit maintenant --night-deep/--night-raised du theme
   actif directement, donc plus besoin de dupliquer la couleur ici pour le
   desktop. */
@media (max-width: 760px) {
  html.theme-light .nav-pill {
    background: color-mix(in oklab, var(--night-deep) 88%, transparent) !important;
    box-shadow: 0 8px 30px -12px rgba(50, 25, 70, 0.25) !important;
  }
  html.theme-light body.nav-floated .nav-pill {
    background: color-mix(in oklab, var(--night-raised) 95%, transparent) !important;
  }
}
html.theme-light .site-aurora::after { background: rgba(245, 240, 250, 0.5); }

/* ============ APERCU THEME GRIS (preview) : panneau theme, valeur "gray" ============
   Meme mecanique que theme-light. Gris chaud mat, quasi neutre (chroma tres
   basse), esprit beton d'atelier plutot que gris corporate froid. */
html.theme-gray {
  --night-deep: oklch(0.90 0.006 290);
  --night: oklch(0.93 0.006 290);
  --night-raised: oklch(0.98 0.004 290);
  --cream: oklch(0.24 0.01 290);
  --lavender: oklch(0.42 0.015 290);
  --lavender-dim: oklch(0.54 0.014 290);
  --lavender-15: rgba(40, 40, 46, 0.10);
  --lavender-08: rgba(40, 40, 46, 0.05);
  --lavender-30: rgba(40, 40, 46, 0.18);
  --ember: oklch(0.54 0.22 18);
  --glass-stroke-strong: rgba(40, 40, 46, 0.16);
  --glass-stroke-soft: rgba(40, 40, 46, 0.06);
}
html.theme-gray body.shell-page {
  background-color: oklch(0.925 0.006 290);
  background-image:
    linear-gradient(rgba(30, 30, 35, 0.035) 1px, transparent 1px),
    linear-gradient(90deg, rgba(30, 30, 35, 0.035) 1px, transparent 1px),
    radial-gradient(110% 80% at 50% -6%, oklch(0.88 0.012 290), transparent 60%);
  background-size: 64px 64px, 64px 64px, 100% 100vh;
  background-attachment: scroll, scroll, scroll;
  background-repeat: repeat, repeat, no-repeat;
}
html.theme-gray .glass-card {
  background: oklch(0.98 0.004 290);
  box-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.7), 0 10px 30px -14px rgba(30, 30, 35, 0.18);
}
html.theme-gray .glass-card::before {
  background: linear-gradient(150deg, rgba(40, 40, 46, 0.14), rgba(40, 40, 46, 0.03) 40%, transparent 70%);
}
@media (max-width: 760px) {
  html.theme-gray .nav-pill {
    background: color-mix(in oklab, var(--night-deep) 88%, transparent) !important;
    box-shadow: 0 8px 30px -12px rgba(30, 30, 35, 0.22) !important;
  }
  html.theme-gray body.nav-floated .nav-pill {
    background: color-mix(in oklab, var(--night-raised) 95%, transparent) !important;
  }
}
html.theme-gray .site-aurora::after { background: rgba(235, 235, 238, 0.55); }

/* ============ APERCU THEME BEIGE (preview) : panneau theme, valeur "beige" ============
   Meme mecanique que theme-light. Sable/papier kraft chaud : plus "atelier
   local" que le rose-violet du theme clair, pense pour la cible artisans/TPE. */
html.theme-beige {
  --night-deep: oklch(0.89 0.02 75);
  --night: oklch(0.93 0.022 78);
  --night-raised: oklch(0.985 0.012 80);
  --cream: oklch(0.28 0.03 55);
  --lavender: oklch(0.45 0.035 60);
  --lavender-dim: oklch(0.56 0.03 65);
  --lavender-15: rgba(70, 45, 20, 0.11);
  --lavender-08: rgba(70, 45, 20, 0.055);
  --lavender-30: rgba(70, 45, 20, 0.2);
  --ember: oklch(0.55 0.235 18);
  --glass-stroke-strong: rgba(70, 45, 20, 0.16);
  --glass-stroke-soft: rgba(70, 45, 20, 0.06);
}
html.theme-beige body.shell-page {
  background-color: oklch(0.935 0.022 78);
  background-image:
    var(--noise),
    linear-gradient(rgba(70, 45, 20, 0.035) 1px, transparent 1px),
    linear-gradient(90deg, rgba(70, 45, 20, 0.035) 1px, transparent 1px),
    radial-gradient(120% 90% at 50% -6%, oklch(0.88 0.035 70), transparent 58%);
  background-size: 160px 160px, 64px 64px, 64px 64px, 100% 100vh;
  background-attachment: scroll, scroll, scroll, scroll;
  background-repeat: repeat, repeat, repeat, no-repeat;
  background-blend-mode: overlay, normal, normal, normal;
}
html.theme-beige .glass-card {
  background: oklch(0.985 0.012 80);
  box-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.65), 0 10px 30px -14px rgba(70, 45, 20, 0.2);
}
html.theme-beige .glass-card::before {
  background: linear-gradient(150deg, rgba(70, 45, 20, 0.16), rgba(70, 45, 20, 0.03) 40%, transparent 70%);
}
@media (max-width: 760px) {
  html.theme-beige .nav-pill {
    background: color-mix(in oklab, var(--night-deep) 88%, transparent) !important;
    box-shadow: 0 8px 30px -12px rgba(70, 45, 20, 0.25) !important;
  }
  html.theme-beige body.nav-floated .nav-pill {
    background: color-mix(in oklab, var(--night-raised) 95%, transparent) !important;
  }
}
html.theme-beige .site-aurora::after { background: rgba(244, 235, 220, 0.5); }

/* Le hero garde SON fond sombre quel que soit le theme actif (contrainte
   operateur), mais ses textes/boutons heritent --cream/--lavender* comme le
   reste de la page : sur un theme clair, ils deviennent une encre foncee,
   illisible sur un fond hero qui, lui, ne change pas. On repingle ces tokens
   aux valeurs sombres d'origine, mais seulement a l'interieur de .hero-shell :
   tout ce qui vit dans le hero reste lisible, tout le reste suit le theme. */
html.theme-light .hero-shell,
html.theme-gray .hero-shell,
html.theme-beige .hero-shell {
  --cream: oklch(0.96 0.008 320);
  --lavender: oklch(0.78 0.04 305);
  --lavender-dim: oklch(0.62 0.05 305);
  --lavender-08: rgba(195, 180, 205, 0.08);
  --lavender-15: rgba(195, 180, 205, 0.15);
  --lavender-30: rgba(195, 180, 205, 0.3);
}

.hero-shell {
  position: relative;
  isolation: isolate;
  display: flex;
  flex-direction: column;
  /* Hero = exception pleine largeur (pas de cap --page-max), juste le gutter */
  width: calc(100% - 2 * var(--gutter));
  margin: var(--gutter) auto;
  border-radius: var(--hr);
  overflow: hidden;
  background: var(--night);
  min-height: calc(100svh - 2 * var(--gutter));
  box-shadow: 0 40px 120px -60px rgba(0, 0, 0, 0.9);
}

/* L'ombre ci-dessus (noir 90%) est calibree pour le fond quasi-noir d'origine :
   elle s'y fond, invisible. Sur un theme plus clair, cette meme ombre reste
   plaquee sous le hero et se voit comme une tache sombre qui denote (signale
   par l'operateur : "difference en degrade" en bas du hero, surtout au
   centre). Allegee ici, par famille de theme. */
html.theme-dusk .hero-shell,
html.theme-slate .hero-shell,
html.theme-ink .hero-shell,
html.theme-garnet .hero-shell,
html.theme-moss .hero-shell,
html.theme-nightsand .hero-shell {
  /* 0.5 restait visible (signale par l'operateur sur Ardoise : "petit
     decalage" + grille plus visible dedans) : ces fonds sont beaucoup plus
     clairs que le sombre d'origine (0.14 de base), 0.5 de noir y ressort
     encore. Redescendu. */
  box-shadow: 0 40px 120px -60px rgba(0, 0, 0, 0.16);
}
html.theme-light .hero-shell,
html.theme-gray .hero-shell,
html.theme-beige .hero-shell {
  box-shadow: 0 30px 90px -50px rgba(0, 0, 0, 0.14);
}

/* Desktop ecran assez haut : la coque fait EXACTEMENT un ecran (min == max) ->
   le cadre sombre (gutter) est identique en haut et en bas, jamais rogne. Le
   contenu est cale pour rentrer dans cette hauteur (cf. padding hero-inner). */
@media (min-width: 761px) and (min-height: 720px) {
  .hero-shell { max-height: calc(100svh - 2 * var(--gutter)); }
}

/* Fond anime au niveau de la coque (derriere nav + hero) */
.hero-shell .shell-bg { position: absolute; inset: 0; z-index: 0; }

/* ---------- Nav notch CREUSE : encastree dans le bord haut de la coque ----------
   Au repos : la nav est de la couleur du cadre (--night-deep) et fusionne avec
   la bande haute, avec des angles concaves (::before/::after) = le cadre qui
   redescend autour de la nav, decoupe dans le hero colore.
   Au scroll : le fond derriere devient sombre, donc la nav se detache en ilot
   glass (bordure + ombre + pilule), les angles concaves s'effacent. */
.nav {
  position: fixed;
  inset-inline: 0;
  top: 0;
  z-index: 96;
  display: flex;
  justify-content: center;
  padding: 0;
  pointer-events: none;
}
/* ---------- Morph LIE AU SCROLL : pilote par --p (0 = notch, 1 = pilule) ----------
   Le JS met --p a jour selon la position de sortie du hero ; le CSS interpole
   tout en calc(). Pas de transition : c'est un scrub fluide tied-to-scroll. */
.nav-pill {
  pointer-events: auto;
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--s-6);
  /* Reserve laterale CONTINUE (~90px/cote) plafonnee a 900px et plancher 620px
     (largeur mini du contenu resserre) : garantit de la place pour un galbe de
     notch ROND a toutes les largeurs, sans saut de breakpoint. A >=1080px la
     pilule fait 900px (vue large inchangee). */
  width: min(900px, max(620px, calc(100% - 180px)));
  max-width: 900px;
  margin: 0;
  border: none;
  /* background + box-shadow poses en INLINE par motion.js selon --p (le calc
     couleur dans une custom prop substituee n'interpole pas dans ce moteur).
     Fallback sans JS = le fond de base .nav-pill (styles.css). */
  padding: calc(0.6rem + (1 - var(--p, 0)) * var(--gutter)) 0.6rem 0.65rem 1.35rem;
  border-top-left-radius: calc(var(--p, 0) * 40px);
  border-top-right-radius: calc(var(--p, 0) * 40px);
  border-bottom-left-radius: calc(var(--notch-r) + var(--p, 0) * (40px - var(--notch-r)));
  border-bottom-right-radius: calc(var(--notch-r) + var(--p, 0) * (40px - var(--notch-r)));
  /* PAS de backdrop-filter sur la pilule : inutile (elle est transparente au
     floating) et il ne se rend pas (transform) -> ce serait du compositing GPU
     gaspille a chaque frame. Le flou est porte par le calque .nav-glass decouple
     (motion.js). Voir [[feedback_backdrop_filter_decoupled_glass]]. */
  /* Decollage via `top` (pilule en position:relative) et NON `transform` : un
     transform sur un element backdrop-filter casse son flou sur GPU reel (la
     pilule pre-notch n'avait pas de transform et floutait). top n'affecte ni le
     compositing du backdrop, ni le layout (pas de reflow). */
  top: calc(var(--p, 0) * var(--gutter));
}
/* Angles concaves : raccordent la nav au bord haut du hero ; se retractent
   vers la pilule a mesure que --p monte. */
.nav-pill::before,
.nav-pill::after {
  content: "";
  position: absolute;
  top: var(--gutter);
  width: var(--notch-r);
  height: var(--notch-r);
  background: var(--night-deep);
  pointer-events: none;
  opacity: calc(1 - var(--p, 0));
  transform: scale(calc(1 - var(--p, 0) * 0.8));
}
.nav-pill::before {
  left: calc(-1 * var(--notch-r) + 0.5px);
  transform-origin: right center;
  -webkit-mask: radial-gradient(circle var(--notch-r) at 0 100%, transparent calc(var(--notch-r) - 0.5px), #000 var(--notch-r));
          mask: radial-gradient(circle var(--notch-r) at 0 100%, transparent calc(var(--notch-r) - 0.5px), #000 var(--notch-r));
}
.nav-pill::after {
  right: calc(-1 * var(--notch-r) + 0.5px);
  transform-origin: left center;
  -webkit-mask: radial-gradient(circle var(--notch-r) at 100% 100%, transparent calc(var(--notch-r) - 0.5px), #000 var(--notch-r));
          mask: radial-gradient(circle var(--notch-r) at 100% 100%, transparent calc(var(--notch-r) - 0.5px), #000 var(--notch-r));
}
/* Bande de cadre fixe en haut : se fond a mesure que --p monte.
   DESKTOP : motion.js l'agrandit (height inline) et lui pose un clip-path
   continu (galbe unique) -> ce calque DEVIENT toute la silhouette du notch.
   Sans JS, il reste la bande haute de --gutter (fallback propre). */
.nav-frame {
  position: fixed;
  inset: 0 0 auto 0;
  height: var(--gutter);
  background: var(--night-deep);
  z-index: 95;
  pointer-events: none;
  opacity: calc(1 - var(--p, 0));
  /* PAS de will-change ici : un calque composite directement derriere la pilule
     casse son backdrop-filter (le flou glass de la pilule flottante). */
}

/* Notch desktop refait en TRACE UNIQUE : le galbe vient du clip-path continu
   pose sur .nav-frame (motion.js). On masque les wedges concaves ::before/::after
   (technique a 2 arcs) qui creaient la couture. Mobile garde l'ancien systeme. */
@media (min-width: 761px) {
  .nav-pill::before,
  .nav-pill::after { display: none; }
}

/* Hero : remplit le reste de la coque */
.hero-shell .hero {
  position: relative;
  z-index: 10;
  flex: 1;
  min-height: 0;
}
/* Padding cale pour que le hero tienne PILE un ecran (contenu ~852px + 2*pad
   <= 100svh - 2*gutter). En vh -> s'adapte a la hauteur d'ecran. */
.hero-shell .hero-inner { padding-block: clamp(0.75rem, 1.5vh, 2.5rem); }

/* ---------- Desktop ETROIT : la pilule (reserve laterale) devient juste pour le
   contenu -> on resserre l'espacement de la nav pour que logo + 4 liens + CTA
   rentrent sans deborder. La largeur de pilule et --hr sont globales/continues. */
@media (min-width: 761px) and (max-width: 880px) {
  .nav-pill { gap: 0.5rem; }
  .nav-links { gap: 0.1rem; }
  .nav-links a { padding-inline: 0.55rem; }
  .nav-cta .btn { padding-inline: 0.95rem; }
}

@media (max-width: 760px) {
  .hero-shell {
    margin: var(--gutter) auto;      /* cadre uniforme sur mobile (cale sur --gutter) */
    border-radius: 24px;
    min-height: calc(100svh - 2 * var(--gutter));
  }
  .hero-shell .nav { padding: 0; }
  /* Hero compact sur mobile : on tue le gros padding desktop (sinon la carte fait
     1,6x l'ecran et le contenu plonge sous la nav du bas). */
  .hero-shell .hero { display: block; min-height: 0; padding-block: 0; }
  /* La nav vit en bas : on reserve la place sous le contenu du hero. */
  .hero-shell .hero-inner { padding-block: clamp(1.25rem, 4vh, 2.5rem) 88px; }
  /* Logos clients gardes sur mobile, mais compacts (marge reduite, plus petits)
     pour laisser la place a la nav du bas. */
  .hero-trust { margin-top: var(--s-5); gap: 0.4rem 1rem; }
  .hero-trust .trust-logos { gap: 0.6rem 1rem; }
  .hero-trust .trust-logos img { height: 20px; max-width: 92px; }
  /* Menu ouvert (nav EN BAS) : panneau pleine largeur qui s'etend vers le HAUT. */
  .hero-shell .nav-pill.open {
    background: var(--night-raised);
    border-radius: 22px;
    padding: 0.5rem 0.55rem 0.6rem 1.1rem;
    box-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.08), 0 -16px 44px -18px rgba(0, 0, 0, 0.9);
  }
  /* Rattrapage : cette regle (pas de !important, oubliee au premier passage)
     lit --night-raised, trop clair une fois le theme force par ailleurs. Le
     box-shadow d'origine est aussi calibre pour un fond sombre (ombre noire
     0.9 alpha = vignette bien visible sur un theme clair) : allege ici. */
  html.theme-light .hero-shell .nav-pill.open,
  html.theme-gray .hero-shell .nav-pill.open,
  html.theme-beige .hero-shell .nav-pill.open {
    background: color-mix(in oklab, var(--night-deep) 97%, transparent) !important;
    box-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.6), 0 -12px 30px -16px rgba(50, 25, 70, 0.18) !important;
  }
}

/* ==========================================================================
   Ameliorations : accessibilite focus + nav compacte sticky
   ========================================================================== */

/* Etats focus nets (clavier) */
a:focus-visible,
button:focus-visible,
summary:focus-visible,
input:focus-visible,
.audit-glass:focus-visible,
[tabindex]:focus-visible {
  outline: 2px solid var(--ember);
  outline-offset: 3px;
  border-radius: 6px;
}
/* Le curseur custom ne doit jamais masquer le focus clavier */
body.cursorless :focus-visible { cursor: auto; }

/* ============ NAV MOBILE EN BAS (notch + pilule flottante) ============
   Sur mobile la nav passe en bas de l'ecran (accessible au pouce). Le notch se
   creuse alors dans le bord BAS du hero (coins concaves retournes vers le haut) ;
   des qu'on scrolle (classe body.nav-floated posee par motion.js), la pilule se
   detache et flotte en bas. Toute la geometrie du desktop est mirroir verticale. */
@media (max-width: 760px) {
  :root { --gutter: 8px; --notch-r: 16px; --hr: 16px; }

  /* La nav s'ancre en bas. padding-bottom = safe-area iOS : la pilule remonte
     au-dessus de l'indicateur d'accueil / barre Safari (sinon coupee dessous). */
  .nav {
    top: auto;
    bottom: 0;
    align-items: flex-end;
    padding: 0 0 env(safe-area-inset-bottom, 0px);
  }

  /* Compact (logo + burger), centree, DOCKED dans le bord bas du hero :
     arrondi en HAUT (vers le hero), carre en BAS (fondu au cadre bas). */
  .nav-pill {
    width: max-content;
    max-width: calc(100% - 2 * (var(--gutter) + var(--notch-r)));
    gap: var(--s-4);
    padding: 0.55rem 0.7rem 0.6rem 1.1rem;
    border-radius: 22px 22px 0 0;
    transform: translateY(0);
    background: var(--night-deep);
    -webkit-backdrop-filter: none;
            backdrop-filter: none;
    box-shadow: none;
    /* La COULEUR mene la FORME : le fond change vite et d'abord (0.24s), puis la
       forme (transform + border-radius) se transforme apres un court delai, donc
       deja a la bonne couleur. Vaut pour les 2 sens (docke <-> flottant). */
    transition:
      background-color 0.24s var(--ease-out),
      box-shadow 0.3s var(--ease-out),
      transform 0.46s var(--ease-spring) 0.12s,
      border-radius 0.42s var(--ease-spring) 0.12s;
  }

  /* Coins concaves AU BAS, carvant vers le HAUT (dans le hero). */
  .nav-pill::before,
  .nav-pill::after {
    top: auto;
    bottom: var(--gutter);
    opacity: 1;
    transform: none;
  }
  .nav-pill::before {
    left: calc(-1 * var(--notch-r) + 0.5px);
    -webkit-mask: radial-gradient(circle var(--notch-r) at 0 0, transparent calc(var(--notch-r) - 0.5px), #000 var(--notch-r));
            mask: radial-gradient(circle var(--notch-r) at 0 0, transparent calc(var(--notch-r) - 0.5px), #000 var(--notch-r));
  }
  .nav-pill::after {
    right: calc(-1 * var(--notch-r) + 0.5px);
    -webkit-mask: radial-gradient(circle var(--notch-r) at 100% 0, transparent calc(var(--notch-r) - 0.5px), #000 var(--notch-r));
            mask: radial-gradient(circle var(--notch-r) at 100% 0, transparent calc(var(--notch-r) - 0.5px), #000 var(--notch-r));
  }

  /* Bande de cadre fixe EN BAS (englobe le safe-area pour combler derriere
     l'indicateur d'accueil iOS). */
  .nav-frame {
    top: auto;
    bottom: 0;
    height: calc(var(--gutter) + env(safe-area-inset-bottom, 0px));
    opacity: 1;
  }

  /* Pieces de coin du hero : en BAS sur mobile (mirroir vertical des masks). */
  .frame-corner {
    top: auto;
    bottom: var(--gutter);
    opacity: 1;
  }
  .frame-corner.tl {
    -webkit-mask: radial-gradient(circle var(--hr) at 100% 0, transparent calc(var(--hr) - 0.5px), #000 var(--hr));
            mask: radial-gradient(circle var(--hr) at 100% 0, transparent calc(var(--hr) - 0.5px), #000 var(--hr));
  }
  .frame-corner.tr {
    -webkit-mask: radial-gradient(circle var(--hr) at 0 0, transparent calc(var(--hr) - 0.5px), #000 var(--hr));
            mask: radial-gradient(circle var(--hr) at 0 0, transparent calc(var(--hr) - 0.5px), #000 var(--hr));
  }

  /* FLOATED : des qu'on scrolle, la pilule se detache et flotte en bas.
     Pas de backdrop-filter ici : une pilule translucide flottante composee a
     chaque frame pendant le scroll est un tueur de perf mobile (cf memoire). On
     prend un fond plus opaque a la place, rendu quasi identique sans le cout GPU. */
  body.nav-floated .nav-pill {
    transform: translateY(calc(-1 * var(--gutter)));
    border-radius: 40px;
    background: oklch(0.205 0.03 308 / 0.95);
    box-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.12), 0 -16px 44px -18px rgba(0, 0, 0, 0.9);
  }
  body.nav-floated .nav-pill::before,
  body.nav-floated .nav-pill::after { opacity: 0; }
  body.nav-floated .nav-frame,
  body.nav-floated .frame-corner { opacity: 0; }

  /* Menu ouvert : pleine largeur, s'etend vers le HAUT (wrap-reverse = la rangee
     logo+burger reste en bas, les liens et le CTA montent au-dessus). */
  .nav-pill.open {
    width: calc(100% - 2 * var(--gutter));
    max-width: none;
    flex-wrap: wrap-reverse;
  }
  .nav-pill.open::before,
  .nav-pill.open::after { opacity: 0; }
  body.nav-floated .nav-pill.open {
    transform: translateY(calc(-1 * var(--gutter)));
  }

  /* La pilule flotte en bas en permanence : on degage le bas du footer pour que
     ses liens (mentions legales, confidentialite) ne passent jamais dessous. */
  .footer { padding-bottom: calc(6.5rem + env(safe-area-inset-bottom, 0px)); }
}

/* ==========================================================================
   Coins arrondis epingles du hero (Phase 1) + logo de marque dans la navbar
   ========================================================================== */

/* Pieces de coin fixes : maintiennent les extremites HAUTES du hero arrondies
   pendant tout le scroll Phase 1 (sinon le hero defile et montre des coins
   carres). night-deep = invisible sur le contenu sombre, visible sur le hero.
   S'effacent quand la nav devient pilule (hero sorti). */
.frame-corner {
  position: fixed;
  top: var(--gutter);
  width: var(--hr);
  height: var(--hr);
  background: var(--night-deep);
  z-index: 95;
  pointer-events: none;
  opacity: calc(1 - var(--p, 0)); /* se fondent a mesure que le hero sort */
}
.frame-corner.tl {
  left: var(--gutter);
  -webkit-mask: radial-gradient(circle var(--hr) at 100% 100%, transparent calc(var(--hr) - 0.5px), #000 var(--hr));
          mask: radial-gradient(circle var(--hr) at 100% 100%, transparent calc(var(--hr) - 0.5px), #000 var(--hr));
}
.frame-corner.tr {
  right: var(--gutter);
  -webkit-mask: radial-gradient(circle var(--hr) at 0 100%, transparent calc(var(--hr) - 0.5px), #000 var(--hr));
          mask: radial-gradient(circle var(--hr) at 0 100%, transparent calc(var(--hr) - 0.5px), #000 var(--hr));
}

/* Logo de marque dans la navbar : deux fichiers superposes (couleur + noir),
   un seul affiche a la fois. Le noir (asset reel fourni par l'operateur, pas
   un filtre) sort sur les themes clairs ou les pastels couleur perdent tout
   contraste. */
.nav-logo {
  position: relative;
  display: inline-flex;
  /* Hauteur explicite : le logo noir est position:absolute (ne dimensionne
     pas son parent), sinon .nav-logo s'effondre a 0x0 des que .logo-color
     passe en display:none sur un theme clair. */
  height: 31px;
}
.nav-logo .brand-logo {
  display: block;
  height: 31px;
  width: auto;
}
.nav-logo .logo-black { display: none; }
html.theme-light .nav-logo .logo-color,
html.theme-gray .nav-logo .logo-color,
html.theme-beige .nav-logo .logo-color {
  display: none;
}
html.theme-light .nav-logo .logo-black,
html.theme-gray .nav-logo .logo-black,
html.theme-beige .nav-logo .logo-black {
  /* PAS inset:0 (.nav-logo collapse a 0x0 une fois .logo-color cache).
     max-width:none : le reset global `img{max-width:100%}` (styles.css)
     resout ce % contre .nav-logo (ancetre positionne), qui fait 0 de large
     (son seul contenu dimensionnant est display:none) -> ecrasait notre
     width explicite a 0. */
  display: block;
  position: absolute;
  top: 0;
  left: 0;
  height: 31px;
  width: 94.7px;
  max-width: none;
}
@media (max-width: 760px) {
  html.theme-light .nav-logo .logo-black,
  html.theme-gray .nav-logo .logo-black,
  html.theme-beige .nav-logo .logo-black {
    height: 26px;
    width: 79.4px;
  }
}
@media (max-width: 760px) {
  .nav-logo { height: 26px; }
  .nav-logo .brand-logo { height: 26px; }
}

/* ==========================================================================
   Navbar : disposition 3 zones equilibree + liens raffines (classe)
   logo a gauche | liens parfaitement centres | CTA a droite
   ========================================================================== */
@media (min-width: 761px) {
  .nav-pill {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    gap: var(--s-6);
  }
  .nav-pill .nav-logo { grid-column: 1; justify-self: start; margin: 0; }
  .nav-pill .nav-links { grid-column: 2; justify-self: center; }
  .nav-pill .nav-cta  { grid-column: 3; justify-self: end; }
}

/* Desktop ETROIT (761-1080px) : 5 liens + logo + CTA deviennent serres et le
   CTA debordait sur 2 lignes. On compacte (gaps, taille, on cache la fleche)
   sans passer au menu burger (la nav du bas est pour le pouce mobile). */
@media (min-width: 761px) and (max-width: 1080px) {
  .nav-pill { gap: var(--s-2); padding-left: 1rem; }
  .nav-links { gap: clamp(0.25rem, 0.7vw, 0.8rem); }
  .nav-links a { font-size: 0.85rem; padding: 0.3rem 0; letter-spacing: 0; }
  .nav-cta .btn { padding: 0.5rem 0.85rem; font-size: 0.85rem; gap: 0; }
  .nav-cta .cta-arrow { display: none; }
  .nav-logo { height: 27px; }
  .nav-logo .brand-logo { height: 27px; }
  html.theme-light .nav-logo .logo-black,
  html.theme-gray .nav-logo .logo-black,
  html.theme-beige .nav-logo .logo-black {
    height: 27px;
    width: 82.5px;
  }
}

/* Liens centres + INDICATEUR GLISSANT (thumb glass qui glisse sous le lien
   survole et se gare sur le lien actif). Le thumb est injecte par motion.js. */
.nav-links {
  position: relative;
  gap: clamp(0.6rem, 2.2vw, 2.1rem);
}
.nav-links a {
  position: relative;
  z-index: 1;
  padding: 0.3rem 0.15rem;
  border-radius: 0;
  background: none;
  font-weight: 500;
  font-size: 0.95rem;
  letter-spacing: 0.01em;
  color: var(--lavender);
  transition: color 0.3s var(--ease-out);
}
.nav-links a:hover,
.nav-links a:focus-visible { color: var(--cream); background: none; } /* tue le fond-pilule de base (carre blanc) */
.nav-links a[aria-current="page"] { color: var(--cream); }

.nav-thumb {
  position: absolute;
  top: 50%;
  left: 0;
  height: 2rem;
  width: 0;
  transform: translate(0, -50%);
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.06);
  box-shadow: inset 0 0 0 1px rgba(246, 25, 66, 0.3), 0 0 20px -6px var(--ember-glow);
  opacity: 0;
  pointer-events: none;
  z-index: 0;
  transition: transform 0.45s var(--ease-spring), width 0.45s var(--ease-spring),
    height 0.45s var(--ease-spring), opacity 0.3s var(--ease-out);
}

/* Logo : leger feedback au survol */
.nav-logo { transition: opacity 0.3s var(--ease-out); }
.nav-logo:hover { opacity: 0.82; }

/* CTA pilule cream + fleche qui avance au survol */
.nav-cta .btn { padding: 0.62rem 1.4rem; font-weight: 600; letter-spacing: 0.01em; gap: 0.45em; }
.cta-arrow { display: inline-block; transition: transform 0.35s var(--ease-spring); }
.nav-cta .btn:hover .cta-arrow { transform: translateX(4px); }

@media (prefers-reduced-motion: reduce) {
  .nav-thumb, .cta-arrow { transition: none; }
}

/* ==========================================================================
   ESSAI : hero en "Liquid Metal plein" (home uniquement)
   Ici ne reste que ce qui est PROPRE A LA HOME : le mount du shader et son
   degrade de secours. L'aurore de fond partagee (.site-aurora, voile, footer)
   est passee dans styles.css pour que les pages internes en profitent aussi
   (elles ne chargent pas ce fichier).
   ========================================================================== */

/* ---- Mount du shader dans le hero ---- */
.hero-metal { position: absolute; inset: 0; }

/* Degrade de secours (mobile / reduced-motion / pas de WebGL / import KO).
   Porte par .shell-bg : le canvas du shader, monte dans .hero-metal, le
   recouvre quand il est actif (le canvas Paper Shaders est en z-index:-1
   DANS .hero-metal, donc au-dessus de ce fond de .shell-bg). */
.hero-shell .shell-bg {
  background:
    radial-gradient(120% 95% at 72% 28%, rgba(139, 92, 246, 0.26), transparent 58%),
    radial-gradient(90% 80% at 18% 82%, rgba(74, 45, 110, 0.5), transparent 70%),
    #05060a;
}

/* ==========================================================================
   PAGES INTERNES (.page-inner) : meme navbar a notch + hero shader que la home.
   Le hero-shell interne est un BANDEAU plus court que la home sur desktop, mais
   PLEIN ECRAN sur mobile pour que le notch du bas se creuse dans le shader (pas
   dans le contenu). h1 a une echelle de page interne (plus petit que l'accueil).
   ========================================================================== */
.page-inner .hero-shell {
  min-height: clamp(360px, 56svh, 580px);
}
.page-inner .hero h1 {
  font-size: clamp(2.4rem, 3vw + 1.4rem, 3.7rem);
}
@media (max-width: 760px) {
  /* Bandeau (pas plein ecran) sur mobile : sur une page de contenu, un hero qui
     remplit l'ecran enterre le contenu. On garde un hero dominant mais qui laisse
     le contenu depasser sous le pli (affordance de scroll). Les heros riches
     (offres, audit) grandissent au-dela pour tenir leur contenu. Contrepartie
     assumee : au repos le notch du bas se creuse dans le contenu sombre (discret)
     plutot que dans le shader ; des qu'on scrolle il flotte de toute facon. */
  .page-inner .hero-shell {
    min-height: clamp(440px, 72svh, 720px);
  }
}
