/* ============================================================================
   styles.css — LE MOTEUR
   ----------------------------------------------------------------------------
   Ne lit que les variables de tokens.css. Aucune valeur esthétique en dur.
   Garde-fous tenus partout : on n'anime que transform / translate / scale /
   opacity / background-position (thread compositeur) ; le mouvement se coupe
   sous prefers-reduced-motion mais l'effet, lui, ne s'éteint jamais.
   ============================================================================ */

/* ---------------------------------------------------------------- 1. POLICES
   Self-host, WOFF2, font-display: swap. Le fallback porte des métriques
   MESURÉES (canvas TextMetrics) pour qu'il occupe exactement la même place que
   la vraie police : le swap ne décale rien, CLS nul.                          */
@font-face{
  font-family:"Space Grotesk";
  src:url("fonts/SpaceGrotesk-var.woff2") format("woff2");
  font-weight:300 700; font-style:normal; font-display:swap;
}
@font-face{
  font-family:"Satoshi";
  src:url("fonts/Satoshi-var.woff2") format("woff2");
  font-weight:300 900; font-style:normal; font-display:swap;
}
@font-face{
  font-family:"Space Grotesk fallback";
  src:local("Arial"), local("Helvetica");
  size-adjust:102.58%; ascent-override:95.54%; descent-override:28.27%; line-gap-override:0%;
}
@font-face{
  font-family:"Satoshi fallback";
  src:local("Arial"), local("Helvetica");
  size-adjust:97.26%; ascent-override:103.84%; descent-override:24.68%; line-gap-override:0%;
}

/* ------------------------------------------------------------------- 2. BASE */
*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%; scroll-behavior:smooth}
@media (prefers-reduced-motion:reduce){ html{scroll-behavior:auto} }
/* Le header est collant : une ancre doit s'arrêter SOUS lui, jamais dessous. */
:target,[id]{scroll-margin-top:calc(var(--head-h, 76px) + var(--space-s))}

body{
  margin:0;
  background:var(--bg);
  color:var(--text);
  font-family:var(--font-text),"Satoshi fallback",sans-serif;
  font-size:var(--step-0);
  line-height:1.62;
  -webkit-font-smoothing:antialiased;
  text-rendering:optimizeLegibility;
  overflow-x:hidden;
}
img,picture,svg,video{display:block;max-width:100%}
img{height:auto}
a{color:inherit}
:focus-visible{outline:2px solid var(--blue);outline-offset:4px;border-radius:2px}
::selection{background:var(--blue);color:var(--deep-0)}

h1,h2,h3,h4{
  font-family:var(--font-display),"Space Grotesk fallback",sans-serif;
  font-weight:700;
  line-height:1.0;
  letter-spacing:-.028em;
  text-wrap:balance;
  margin:0;
}
p{text-wrap:pretty;margin:0}
.lede,.body-l{font-size:var(--step-1);line-height:1.55;color:var(--text-soft)}
/* Mesures de ligne ponctuelles (remplacent des styles en ligne : HTML valide) */
.lede--52{max-width:52ch} .lede--56{max-width:56ch} .lede--58{max-width:58ch}
/* Piège à robots : hors écran ET hors de l'arbre d'accessibilité. */
.botcheck{position:absolute;left:-9999px;width:1px;height:1px;opacity:0;pointer-events:none}

.skip{position:absolute;left:-9999px;top:0;z-index:200;background:var(--blue);color:var(--deep-0);
  padding:12px 20px;font-size:var(--step--1);letter-spacing:.1em;text-transform:uppercase;text-decoration:none}
.skip:focus{left:12px;top:12px}

.visually-hidden{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;
  clip-path:inset(50%);white-space:nowrap;border:0}

/* -------------------------------------------------------------- 3. LE MÉTAL
   Deux couches : un « horizon » vertical (ce que la pièce reflète) + un reflet
   balayé horizontalement. Le balayage anime background-position, propriété
   prise en charge par le compositeur : le métal bouge sans repeindre la page. */
.metal{
  background-image:var(--metal-sheen),var(--metal-face);
  background-size:260% 100%,100% 100%;
  background-position:120% 0,0 0;
  background-repeat:no-repeat;
  background-blend-mode:screen,normal;
  -webkit-background-clip:text;background-clip:text;
  -webkit-text-fill-color:transparent;color:transparent;
  animation:metalSweep var(--t-sheen) ease-in-out infinite;
  /* le mot métallisé porte sa propre ombre portée bleue : il « décolle » */
  filter:drop-shadow(0 2px 18px oklch(60% .12 250 / .28));
}
@keyframes metalSweep{
  0%   {background-position:120% 0,0 0}
  55%,100%{background-position:-45% 0,0 0}
}
/* Une plaque de métal (surface, pas texte) */
.plate{background-image:var(--metal-plate);color:var(--on-metal)}

/* ------------------------------------------------------------ 4. L'ATMOSPHÈRE
   Un seul fond traverse tout le document : dégradé profond + deux foyers de
   lumière qui dérivent + trame d'atelier + vignette + grain. Aucune section
   ne repose sur un aplat.                                                     */
.atmos{position:fixed;inset:0;z-index:-1;pointer-events:none;overflow:hidden;
  background:
    radial-gradient(120% 80% at 50% -10%, var(--deep-2), transparent 60%),
    linear-gradient(180deg, var(--deep-1) 0%, var(--deep-0) 100%);
}
.atmos__glow{position:absolute;inset:-25%;
  background:
    radial-gradient(38% 34% at 24% 20%, var(--glow-cold), transparent 68%),
    radial-gradient(32% 30% at 80% 74%, var(--glow-pale), transparent 70%);
  animation:atmosDrift var(--t-ambient) ease-in-out infinite alternate;
  will-change:transform}
@keyframes atmosDrift{
  from{transform:translate3d(-2.5%,-1.5%,0) scale(1.02)}
  to  {transform:translate3d(2.5%,2%,0) scale(1.08)}
}
/* La trame d'atelier : un quadrillage de marbre de contrôle, très effacé. */
.atmos__grid{position:absolute;inset:0;
  background-image:
    linear-gradient(90deg, var(--grid-line) 1px, transparent 1px),
    linear-gradient(0deg,  var(--grid-line) 1px, transparent 1px);
  background-size:96px 96px;
  mask-image:radial-gradient(110% 80% at 50% 30%, #000 10%, transparent 75%)}
.atmos__vignette{position:absolute;inset:0;
  background:radial-gradient(115% 95% at 50% 32%, transparent 48%, var(--vignette) 100%)}
.atmos__grain{position:absolute;inset:0;opacity:var(--grain-opacity);mix-blend-mode:overlay;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E")}
@media (max-width:720px){ :root{--grain-opacity:.04} .atmos__grid{background-size:64px 64px} }

/* -------------------------------------------------------------- 5. STRUCTURE */
.wrap{width:100%;max-width:var(--maxw);margin-inline:auto;padding-inline:var(--pad-x)}
/* overflow-x:clip (et non hidden) : coupe les débords de matière pleine largeur
   sans créer de conteneur de défilement, donc sans casser le header collant. */
.section{padding-block:var(--space-section);position:relative;overflow-x:clip}
.stack>*+*{margin-block-start:var(--space-s)}
.ferre{max-width:66ch}
@media (min-width:1024px){ .ferre{margin-inline-start:clamp(0px,3.5vw,56px)} }

/* ------------------------------------------------------- 5 bis. LES AMBIANCES
   REFONTE V5 — ON PART D'IMAGES. Une matière par section, choisie pour ce que
   la section RACONTE : la fusée là où l'on parle de ce qui traîne, des mains au
   travail là où l'on parle de ce qu'on fait pour de vrais commerces, du métal et
   du verre là où l'on parle de nous et de notre méthode. Toutes passent par le
   même étalonnage (`09_ops/_tools/_fonds_otaweb_v5.mjs`) : au long-scroll on
   traverse les pièces d'un seul bâtiment, pas six images collées.

   Quatre décisions structurelles, et aucune n'est cosmétique :

   1. UN <img loading="lazy"> ET NON UN background-image CSS. Une image de fond
      CSS est téléchargée dès que son élément entre dans la mise en page, même à
      six écrans sous la ligne de flottaison : les cinq ambiances de la V4
      pesaient 11,4 Ko sur le PREMIER écran, sans que personne les ait vues. Le
      lazy natif du navigateur règle ça sans une ligne de JavaScript, et il
      fonctionne script désactivé. `aria-hidden` + `alt=""` : rien n'entre dans
      l'arbre d'accessibilité. `data-fond` dit aux outils de contrôle que ces
      pixels SONT le fond, donc qu'ils doivent être mesurés sous les textes.
   2. DEUX CADRAGES, PAS DEUX TAILLES. En desktop le sujet vit dans le tiers
      droit, à côté de la colonne de texte ; en mobile il n'y a plus de tiers
      libre, donc l'image est recadrée en portrait et confinée à une BANDE, et
      deux sections voisines ne s'allument jamais à la même hauteur.
   3. LE CONTRASTE EST CONFIÉ À UN SCRIM, PAS AU MASQUE. Un masque ne fait que
      doser la photo : sous un point clair il laisse passer un point clair, et le
      texte paie. Le scrim, lui, est un aplat de la couleur du fond posé entre la
      photo et les mots : leur support redevient --deep-1, une valeur connue,
      quoi que fasse l'image dessous.
   4. LE FOND BOUGE, IL NE MEURT PAS. Un Ken-Burns très lent et très court
      (7 % d'amplitude, 34 s), en `transform` seul — thread compositeur, zéro
      repeint. Il se met en pause hors écran, et il s'arrête sous mouvement
      réduit sans que l'image disparaisse.
   `background-attachment:fixed` reste proscrit : un repeint plein cadre à
   chaque frame de scroll, c'est le budget INP qui paie.                       */
.amb{position:absolute;inset-block-start:0;inset-inline:0;z-index:-1;pointer-events:none;
  overflow:hidden;height:var(--amb-h,100%);
  -webkit-mask-image:var(--amb-mask,linear-gradient(180deg,#000,#000));
          mask-image:var(--amb-mask,linear-gradient(180deg,#000,#000))}
/* `display:contents` : sans lui le <picture>, boîte inline sans hauteur propre,
   fait retomber le `height:100%` de l'image sur `auto` et `object-fit` ne
   s'applique jamais (défaut mesuré au hero en V4). */
.amb picture{display:contents}
.amb img{width:100%;height:100%;object-fit:cover;object-position:var(--amb-pos,50% 50%);
  opacity:var(--amb-o,.6);
  animation:ambKen var(--amb-t,34s) var(--ease-sine) infinite alternate;
  transform-origin:var(--amb-origin,50% 50%);will-change:transform}
@keyframes ambKen{
  from{transform:scale(var(--amb-z1,1.015))}
  to  {transform:scale(var(--amb-z2,1.085))}
}
/* LE SCRIM. Peint dans la couleur du fond, il est rigoureusement invisible là où
   l'image n'a rien peint : on peut donc le SCULPTER bande par bande — appuyer
   sous le petit texte, relâcher là où l'image doit se voir — sans qu'aucune
   marche ne se lise. C'est lui qui autorise des fonds aussi présents. */
.amb::after{content:"";position:absolute;inset:0;background:var(--amb-scrim,none)}

/* --- Section 02 · CE QUI COINCE · LA FUSÉE, PLEIN CADRE ---------------------
   La seule image chaude du site, et elle ne sort pas d'ici (décision de Rayan).
   Elle n'est pas confinée dans un coin : elle occupe toute la largeur et la
   hauteur d'un écran, comme une plaque de cinéma, puis se fond dans la page.
   Le contraste chaud / froid devient le parti pris de la section — l'ambre de
   ce qui décolle contre le bleu de tout le reste. Elle est BORNÉE en hauteur
   (`--amb-h`) : la section fait plus de 3 000 px avec ses sept face-à-face, et
   étirer une photo sur 3 000 px n'en laisse qu'une tache. */
/* Le chapeau de cette section tenait 103 caractères par ligne — très au-delà du
   confort de lecture, qui plafonne à 80 — parce que les classes en `ch` sont
   calibrées sur une police plus étroite que celle-ci. On le cale sur la largeur
   du titre : dans une colonne éditoriale, c'est le titre qui donne la mesure. */
#ce-qui-coince .lede{max-width:790px}
#ce-qui-coince{padding-block-start:var(--space-section);
  /* L'IMAGE DESCEND SUR LE TEXTE (demande de Rayan, 18/08). Ce bloc était taillé
     pour la fusée : 62svh de vide réservés au-dessus, image bornée à 86svh, et un
     voile quasi nul parce qu'« aucun mot ne se posait sur la plaque ». Avec
     l'atelier, la lampe se perdait au-dessus du bandeau et TOUT le texte tombait
     sur du noir uni. Trois réglages changent ensemble, et ils sont solidaires :
     le vide du haut tombe à 14svh pour que le titre entre dans l'image, l'image
     s'étend à 132svh pour couvrir titre + accroche + premières lignes, et le
     cadrage remonte à 62 % pour amener la lampe DERRIÈRE le titre au lieu de la
     laisser en haut de cadre. */
  --amb-h:min(100%, 132svh);--amb-o:.92;--amb-pos:56% 62%;--amb-t:42s;
  --amb-mask:linear-gradient(180deg, #000 0%, #000 74%, transparent 99%);
  /* Et le voile REDEVIENT un voile. Dès lors que des mots se posent dessus, il
     ne raccorde plus, il protège : valeurs remontées au niveau des autres
     sections, avec un creux volontaire entre 8 et 30 % pour laisser respirer la
     zone éclairée que Rayan veut voir, puis un appui franc sous le titre. */
  --amb-scrim:linear-gradient(180deg,
      oklch(15% .05 258 / .58) 0%,
      oklch(15% .05 258 / .30) 14%,
      oklch(15% .05 258 / .34) 30%,
      oklch(15% .05 258 / .62) 52%,
      oklch(15% .05 258 / .80) 74%,
      oklch(15% .05 258 / .92) 100%)}
/* --- Section 03 · NOTRE FAÇON DE FAIRE · L'ACIER USINÉ ----------------------
   On parle de nous et de notre méthode : de la matière, pas de l'humain. Un
   bloc d'acier fraîchement usiné sous une lumière rasante, arête vive en haut.
   C'est littéralement le sujet du titre : la main décide, la pièce est nette. */
#comment{--amb-h:min(100%, 78svh);--amb-o:.72;--amb-pos:64% 40%;--amb-t:38s;
  --amb-mask:radial-gradient(88% 76% at 78% 32%, #000 8%, transparent 82%);
  --amb-scrim:linear-gradient(98deg,
      oklch(15% .05 258 / .72) 0%,
      oklch(15% .05 258 / .50) 36%,
      oklch(15% .05 258 / .14) 62%,
      transparent 80%)}
/* --- Section 04 · CE QU'ON FAIT · LES MAINS QUI EMBALLENT -------------------
   Trois métiers, trois vrais commerces : ici l'image doit être HUMAINE et
   concrète, pas une texture. Des mains qui ficellent un colis sur un comptoir,
   le soir. C'est pour eux qu'on travaille, et ça se voit. */
#ce-quon-fait{--amb-h:min(100%, 84svh);--amb-o:.52;--amb-pos:66% 50%;--amb-t:40s;
  --amb-mask:radial-gradient(96% 82% at 72% 46%, #000 10%, transparent 84%);
  --amb-scrim:linear-gradient(96deg,
      oklch(15% .05 258 / .90) 0%,
      oklch(15% .05 258 / .86) 38%,
      oklch(15% .05 258 / .56) 56%,
      oklch(15% .05 258 / .18) 72%,
      transparent 86%)}
/* --- Section 06 · COMBIEN · LE PRISME ---------------------------------------
   « Le prix d'un projet ne se lit pas dans une grille » : un faisceau froid qui
   traverse un bloc de verre et se décompose. La clarté, littéralement.        */
#combien{--amb-h:min(100%, 72svh);--amb-o:.58;--amb-pos:58% 56%;--amb-t:44s;
  --amb-mask:radial-gradient(84% 70% at 74% 44%, #000 6%, transparent 80%);
  --amb-scrim:linear-gradient(98deg,
      oklch(15% .05 258 / .70) 0%,
      oklch(15% .05 258 / .48) 34%,
      oklch(15% .05 258 / .16) 60%,
      transparent 80%)}
/* --- Section 07 · RENDEZ-VOUS · LA TABLE ------------------------------------
   Une main posée sur un carnet ouvert et une tasse, sous la lumière d'une
   fenêtre. Le carnet est VIERGE : c'est la conversation qui reste à avoir.    */
#rendez-vous{--amb-h:100%;--amb-o:.5;--amb-pos:36% 62%;--amb-t:46s;
  --amb-mask:radial-gradient(96% 62% at 34% 88%, #000 6%, transparent 84%);
  --amb-scrim:linear-gradient(180deg,
      oklch(15% .05 258 / .66) 0%,
      oklch(15% .05 258 / .44) 42%,
      oklch(15% .05 258 / .58) 100%)}
/* --- Le pied du puits · LA MÊME PLAQUE D'ACIER, RETOURNÉE ET PRESQUE ÉTEINTE
   Zéro octet ajouté : c'est le fichier de la section 03, déjà en cache.       */
.site-foot .amb{z-index:0;--amb-o:.3;--amb-pos:50% 30%;--amb-t:50s;
  --amb-mask:linear-gradient(180deg, #000 0%, transparent 74%);
  --amb-scrim:linear-gradient(180deg, oklch(15% .05 258 / .44), transparent 60%)}
.site-foot .amb img{transform-origin:50% 0%;scale:-1 1}
.site-foot>.wrap{position:relative;z-index:1}
/* LA SIGNATURE DE FIN — l'emblème en filigrane, à la façon d'un gaufrage sur
   du papier à en-tête. À 7 % il ne se lit plus comme un logo affiché une
   troisième fois, mais comme une matière : c'est l'opacité qui change la
   nature de l'objet. Le pied de page était le dernier grand aplat du
   long-scroll (constaté à la capture, station 8).
   Zéro octet ajouté : c'est le fichier du hero, déjà téléchargé, donc déjà en
   cache — d'où la même bascule d'écran que lui (980 px), et non celle des
   ambiances (760 px), sinon on demanderait un troisième fichier pour rien.
   Le mask fait ici le détourage : la source n'a pas de couche alpha, son fond
   bleu est plein. */
.site-foot::before{
  content:"";position:absolute;z-index:0;pointer-events:none;
  inset-block-start:-14%;inset-inline-end:-10%;
  width:min(62%, 560px);aspect-ratio:1;
  background-image:url("img/embleme-hero.webp");
  background-size:contain;background-repeat:no-repeat;
  opacity:.07;
  -webkit-mask-image:radial-gradient(closest-side,#000 50%,transparent 94%);
          mask-image:radial-gradient(closest-side,#000 50%,transparent 94%)}
@media (max-width:980px){
  .site-foot::before{background-image:url("img/embleme-hero-mobile.webp");
    width:min(78%, 340px);inset-block-start:-8%;inset-inline-end:-16%;opacity:.08}
}
/* MOBILE. Deux choses changent, pas une.
   1. Le fichier : un téléphone ne télécharge pas une image de portable.
   2. LA GÉOMÉTRIE DU MASQUE. Les masques ci-dessus visent le tiers droit, l'espace
      que la colonne de texte laisse libre en desktop. Sur 390 px il n'y a plus de
      tiers droit : le texte occupe toute la largeur et la matière tombait hors
      écran (mesuré à la capture, les ambiances y étaient quasi invisibles). En
      mobile la respiration est HORIZONTALE : chaque matière prend une bande, et
      on alterne haut / milieu / bas pour que deux sections voisines ne s'allument
      jamais au même endroit.                                                     */
@media (max-width:760px){
  /* La fusée garde son statut de plaque plein cadre : c'est la seule image du
     site qui a le droit de se REGARDER. Elle occupe la hauteur d'un écran, sous
     le sur-titre, et s'éteint avant le premier face-à-face. */
  /* LA FUSÉE A SON PROPRE ÉCRAN, et c'est la seule façon honnête de tenir le
     « plein cadre » demandé. Partition relevée au Range sur un vrai 390 px :
     sur-titre 13-16 %, titre 20-38 %, chapeau 45-87 % — le texte occupait la
     plaque ENTIÈRE. Deux essais l'ont prouvé : voiler assez pour protéger le
     chapeau revient à effacer la fusée (mesuré 1,98:1, puis 1,65:1 sur les
     face-à-face quand la plaque a été rallongée). Il n'y a pas de réglage de
     voile qui règle ça, il y a une décision de mise en page : le récit commence
     APRÈS la plaque. Elle occupe un écran, en clair, sans un mot dessus ; le
     scrim n'y sert plus qu'à la raccorder au hero au-dessus et à la page en
     dessous. Coût : un demi-écran de défilement sur une page qui en fait vingt. */
  #ce-qui-coince{padding-block-start:calc(var(--space-section) + 56svh);
    --amb-h:min(100%, 78svh);--amb-pos:56% 50%;
    --amb-mask:linear-gradient(180deg,#000 0%,#000 58%,transparent 94%)}
  /* Les autres : chacune sa BANDE, et jamais deux à la même hauteur — c'est ce
     qui donne une respiration horizontale au long-scroll mobile. */
  #comment{--amb-h:min(100%, 66svh);--amb-o:.74;--amb-pos:56% 34%;
    --amb-mask:linear-gradient(180deg,transparent 4%,#000 22%,#000 58%,transparent 92%);
    --amb-scrim:linear-gradient(180deg,
      oklch(15% .05 258 / .62) 0%,
      oklch(15% .05 258 / .76) 16%,
      oklch(15% .05 258 / .70) 40%,
      oklch(15% .05 258 / .52) 62%,
      oklch(15% .05 258 / .58) 100%)}
  /* Chapeau mesuré à 54-68 % de la plaque : le voile tient jusqu'à 70 %, puis
     lâche sur les mains, qui vivent dans le bas de l'image. */
  #ce-quon-fait{--amb-h:min(100%, 72svh);--amb-o:.58;--amb-pos:58% 56%;
    --amb-mask:linear-gradient(180deg,transparent 6%,#000 30%,#000 70%,transparent 100%);
    --amb-scrim:linear-gradient(180deg,
      oklch(15% .05 258 / .68) 0%,
      oklch(15% .05 258 / .84) 18%,
      oklch(15% .05 258 / .82) 46%,
      oklch(15% .05 258 / .74) 62%,
      oklch(15% .05 258 / .40) 76%,
      oklch(15% .05 258 / .54) 100%)}
  #combien{--amb-h:min(100%, 60svh);--amb-o:.58;--amb-pos:60% 52%;
    --amb-mask:linear-gradient(180deg,transparent 4%,#000 26%,#000 64%,transparent 96%);
    --amb-scrim:linear-gradient(180deg,
      oklch(15% .05 258 / .60) 0%,
      oklch(15% .05 258 / .78) 16%,
      oklch(15% .05 258 / .66) 32%,
      oklch(15% .05 258 / .42) 54%,
      oklch(15% .05 258 / .52) 100%)}
  #rendez-vous{--amb-h:min(100%, 64svh);--amb-o:.52;--amb-pos:42% 66%;
    --amb-mask:linear-gradient(180deg,transparent 2%,#000 24%,#000 66%,transparent 100%);
    --amb-scrim:linear-gradient(180deg,
      oklch(15% .05 258 / .62) 0%,
      oklch(15% .05 258 / .70) 20%,
      oklch(15% .05 258 / .52) 52%,
      oklch(15% .05 258 / .60) 100%)}
  .site-foot .amb{--amb-o:.34}
}

/* Sur-titre : numéro + libellé, séparés par un filet de métal. */
.eyebrow{display:flex;align-items:center;gap:14px;
  font-family:var(--font-display),"Space Grotesk fallback",sans-serif;
  font-size:var(--step--1);font-weight:500;letter-spacing:.26em;text-transform:uppercase;
  color:var(--text-dim);margin:0 0 var(--space-m)}
.eyebrow::after{content:"";flex:0 0 auto;width:clamp(34px,6vw,72px);height:1px;
  background:linear-gradient(90deg,var(--metal-edge),transparent)}
.eyebrow b{font-weight:700;color:var(--text-soft);font-variant-numeric:tabular-nums}

/* --------------------------------------------------------- 6. EN-TÊTE / NAV */
.site-head{position:sticky;top:0;z-index:60;
  display:flex;align-items:center;justify-content:space-between;gap:var(--space-s);
  padding:16px var(--pad-x);
  transition:background var(--t-fast) var(--ease),border-color var(--t-fast) var(--ease);
  border-block-end:1px solid transparent}
.site-head.is-solid{
  background:color-mix(in oklch, var(--deep-0) 78%, transparent);
  backdrop-filter:blur(14px) saturate(1.15);
  border-block-end-color:var(--line)}
/* Jauge de lecture : le filet de métal qui se remplit au scroll (CSS natif). */
.site-head__rail{position:absolute;left:0;right:0;bottom:-1px;height:1px;transform-origin:0 50%;
  transform:scaleX(0);background:linear-gradient(90deg,var(--blue),var(--metal-edge))}
@supports (animation-timeline:scroll()){
  .site-head__rail{animation:railGrow linear both;animation-timeline:scroll(root block)}
}
@keyframes railGrow{from{transform:scaleX(0)}to{transform:scaleX(1)}}

.brand{display:inline-flex;align-items:center;gap:11px;text-decoration:none;
  font-family:var(--font-display),"Space Grotesk fallback",sans-serif;
  font-size:var(--step-1);font-weight:700;letter-spacing:-.03em}
/* La marque de nav, c'est l'emblème lui-même, en pastille. */
.brand__mark{width:1.35em;height:1.35em;flex:0 0 auto;border-radius:50%}

.nav{display:flex;align-items:center;gap:clamp(12px,2vw,30px)}
.nav__links{display:flex;align-items:center;gap:clamp(14px,2vw,30px)}
.nav__links a{font-size:var(--step--1);letter-spacing:.06em;text-decoration:none;color:var(--text-soft);
  transition:color var(--t-fast) var(--ease)}
.nav__links a:hover{color:var(--text)}
.nav__links a[aria-current="true"]{color:var(--text)}
.btn__short,.btn__mini{display:none}

/* Bouton d'ouverture du menu (mobile) */
.burger{display:none;align-items:center;gap:10px;background:none;border:0;cursor:pointer;
  color:var(--text);font:inherit;font-size:var(--step--1);letter-spacing:.2em;text-transform:uppercase;
  padding:10px 4px;min-height:44px}
.burger__bars{display:grid;gap:4px;width:22px}
.burger__bars i{display:block;height:1.5px;background:var(--metal-edge);
  transition:transform var(--t-fast) var(--ease),opacity var(--t-fast) var(--ease)}
.menu-open .burger__bars i:nth-child(1){transform:translateY(5.5px) rotate(45deg)}
.menu-open .burger__bars i:nth-child(2){opacity:0}
.menu-open .burger__bars i:nth-child(3){transform:translateY(-5.5px) rotate(-45deg)}

.drawer{position:fixed;inset:0;z-index:55;display:grid;align-content:center;gap:var(--space-s);
  padding:var(--space-l) var(--pad-x);
  background:color-mix(in oklch, var(--deep-0) 94%, transparent);
  backdrop-filter:blur(18px);
  opacity:0;visibility:hidden;transform:translateY(-8px);
  transition:opacity var(--t-fast) var(--ease),transform var(--t-fast) var(--ease),visibility 0s linear var(--t-fast)}
.menu-open .drawer{opacity:1;visibility:visible;transform:none;transition-delay:0s}
.drawer a{font-family:var(--font-display),"Space Grotesk fallback",sans-serif;
  font-size:var(--step-3);font-weight:700;letter-spacing:-.02em;text-decoration:none;color:var(--text);
  padding:6px 0;border-block-end:1px solid var(--line)}
.drawer a:last-of-type{border:0}

/* 920 et non 900 : avec « Combien ça coûte » en nav et « Prendre rendez-vous »
   en bouton, la barre desktop réclame 905 px pour tenir sur une ligne (mesuré).
   Entre 901 et 904 px elle passait à deux lignes ; le burger prend le relais
   avant ce point, avec 15 px de marge. */
@media (max-width:920px){
  .nav__links{display:none}
  .burger{display:inline-flex}
  /* Le CTA reste visible en tête sur mobile, en version courte. */
  .site-head .btn--metal{padding:12px 18px;letter-spacing:.1em}
  .btn__long{display:none}
  .btn__short{display:inline;white-space:nowrap}
}
/* « Rendez-vous » demande 140 px de bouton et n'en dispose plus sous 360 px : le
   libellé s'y coupait en deux lignes et la barre passait de 79 à 101 px (mesuré).
   On ne tronque pas un mot, on en choisit un autre. */
@media (max-width:359px){
  .btn__short{display:none}
  .btn__mini{display:inline;white-space:nowrap}
}
@media (min-width:921px){ .drawer{display:none} }

/* ------------------------------------------------------------------ 7. CTA
   Zéro CTA encadré en pointillés d'école : soit une plaque de métal usinée
   (l'action principale, unique), soit du texte accent souligné d'un filet.    */
.btn{display:inline-flex;align-items:center;gap:12px;text-decoration:none;
  font-family:var(--font-display),"Space Grotesk fallback",sans-serif;
  font-size:var(--step--1);font-weight:600;letter-spacing:.14em;text-transform:uppercase;
  min-height:46px;padding:14px 26px;border-radius:var(--r);border:0;cursor:pointer;
  transition:transform var(--t-fast) var(--ease),box-shadow var(--t-fast) var(--ease)}

.btn--metal{position:relative;overflow:hidden;
  background-image:var(--metal-plate);color:var(--on-metal);
  box-shadow:0 1px 0 var(--metal-edge) inset, 0 10px 30px oklch(10% .04 260 / .5)}
.btn--metal:hover{transform:translateY(-2px);box-shadow:0 1px 0 var(--metal-edge) inset,0 16px 38px oklch(10% .04 260 / .6)}
.btn--metal::after{content:"";position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(105deg,transparent 38%,oklch(100% 0 0 / .75) 50%,transparent 62%);
  transform:translateX(-130%);animation:plateSheen 4.6s ease-in-out 1.2s infinite}
@keyframes plateSheen{0%{transform:translateX(-130%)}22%,100%{transform:translateX(130%)}}

.btn--text{position:relative;padding:8px 0;border-radius:0;color:var(--blue);
  letter-spacing:.16em}
.btn--text::before{content:"→";display:inline-block;order:2;
  transition:transform var(--t-fast) var(--ease)}
.btn--text:hover::before{transform:translateX(6px)}
.btn--text::after{content:"";position:absolute;left:0;bottom:0;height:1px;width:100%;
  background:linear-gradient(90deg,var(--blue),transparent);
  transform:scaleX(.999);transform-origin:left;transition:transform .55s var(--ease)}
.btn--text:hover::after,.btn--text:focus-visible::after{transform:scaleX(1)}

/* ------------------------------------------------------------------ 8. HERO */
.hero{position:relative;min-height:100svh;display:grid;align-items:center;
  padding-block:var(--space-l) var(--space-m);

  /* LA MATIÈRE — le hero n'a plus d'image. Elle est peinte en CSS, et c'est un
     choix mesuré autant qu'esthétique.
     Esthétique : la fusée est descendue d'un cran, elle est devenue la plaque
     plein cadre de la section 02. Le premier écran n'a donc plus qu'UN sujet,
     l'emblème qui tourne — et une photo derrière lui le disputait au lieu de le
     servir (à la capture mobile, il n'en restait qu'un trait de lumière).
     Mesurée : 10,8 Ko et une requête de moins DEVANT l'élément LCP, sur un lien
     bridé où la bande passante est le facteur limitant.
     Trois couches, aucun octet : la profondeur (le haut s'assombrit sous
     l'en-tête, le pied se raccorde à la page), un foyer d'atelier qui dérive
     derrière la pièce, et un balayage spéculaire très lent en travers du cadre —
     le même vocabulaire que le métal des titres. */
  /* Deux passes. La verticale creuse le haut sous l'en-tête et raccorde le pied
     à la page. L'HORIZONTALE protège la colonne de texte : le balayage
     spéculaire traverse le cadre, et selon la seconde où on le fige il passe
     sous le lien d'accent (mesuré 4,48:1 pour un seuil de 4,50 en mouvement
     réduit, où il s'arrête là où on l'a garé). Un contraste ne doit pas dépendre
     de l'instant : c'est le scrim qui décide, pas la lumière qui passe. */
  --sky-scrim:
    linear-gradient(96deg,
      oklch(15% .05 258 / .40) 0%,
      oklch(15% .05 258 / .24) 34%,
      transparent 62%),
    linear-gradient(180deg,
      oklch(11% .04 260 / .72) 0%,
      transparent 24%,
      transparent 62%,
      oklch(13% .045 258 / .34) 88%,
      var(--deep-1) 100%);
}
.hero__sky{position:absolute;inset:0;z-index:-1;pointer-events:none;overflow:hidden;
  background:
    /* la nappe froide qui remonte du sol, sous la pièce */
    radial-gradient(76% 42% at 64% 106%, oklch(56% .12 254 / .46), transparent 76%),
    /* la lumière d'atelier qui tombe du haut-droit, derrière le titre */
    radial-gradient(58% 46% at 88% 6%, oklch(60% .10 248 / .30), transparent 72%),
    /* le creux du fond : la page est un puits, pas un aplat */
    radial-gradient(126% 94% at 50% -22%, oklch(23% .06 256 / .95), transparent 64%)}
/* LA TRAME D'ATELIER, resserrée et plus présente que celle du document : le
   premier écran n'a plus d'image, c'est la matière qui doit y tenir le rôle.
   Elle est masquée en diagonale pour ne pas se lire comme un quadrillage
   d'application, et elle s'éteint avant la ligne de flottaison. */
.hero__grain{position:absolute;inset:0;pointer-events:none;
  background-image:
    linear-gradient(90deg, oklch(72% .05 250 / .07) 1px, transparent 1px),
    linear-gradient(0deg,  oklch(72% .05 250 / .07) 1px, transparent 1px);
  background-size:54px 54px;
  -webkit-mask-image:radial-gradient(92% 74% at 84% 14%, #000 4%, transparent 72%);
          mask-image:radial-gradient(92% 74% at 84% 14%, #000 4%, transparent 72%)}
.hero__sky::after{content:"";position:absolute;inset:0;background:var(--sky-scrim)}
/* LE BALAYAGE SPÉCULAIRE. Une bande de lumière très large et très pâle qui
   traverse le cadre en trente-quatre secondes. On n'anime qu'un `translate3d` :
   thread compositeur, zéro repeint, et le fond cesse d'être une image fixe. */
.hero__sky::before{content:"";position:absolute;inset-block:-30%;inset-inline-start:-60%;
  width:70%;
  background:linear-gradient(104deg, transparent, oklch(76% .07 246 / .22) 46%, transparent);
  transform:translate3d(0,0,0) rotate(8deg);
  animation:heroSheen 34s linear infinite;will-change:transform}
@keyframes heroSheen{
  from{transform:translate3d(0,0,0) rotate(8deg)}
  to  {transform:translate3d(260%,0,0) rotate(8deg)}
}
/* LE FOYER D'ATELIER, qui respire derrière la pièce. */
.hero__sheen{position:absolute;inset:-22%;
  background:
    radial-gradient(38% 34% at 76% 32%, oklch(60% .13 250 / .44), transparent 68%),
    radial-gradient(30% 26% at 40% 84%, oklch(76% .06 244 / .22), transparent 72%);
  animation:heroDrift 28s var(--ease-sine) infinite alternate;will-change:transform}
@keyframes heroDrift{
  from{transform:translate3d(-2%,1.5%,0) scale(1.02)}
  to  {transform:translate3d(3%,-2%,0) scale(1.1)}
}
/* MOBILE : le texte occupe toute la largeur, la matière se couche donc sous lui
   et le foyer descend derrière la pièce, qui passe sous l'appel à l'action. */
@media (max-width:980px){
  .hero{--sky-scrim:
      linear-gradient(180deg,
        oklch(11% .04 260 / .74) 0%,
        oklch(15% .05 258 / .52) 22%,
        oklch(15% .05 258 / .40) 58%,
        oklch(15% .05 258 / .30) 100%);
  }
  .hero__sheen{inset:-14%;
    background:
      radial-gradient(48% 27% at 56% 74%, oklch(60% .13 250 / .46), transparent 70%),
      radial-gradient(44% 22% at 26% 17%, oklch(76% .07 244 / .22), transparent 74%)}
  .hero__grain{background-size:44px 44px;
    -webkit-mask-image:radial-gradient(120% 34% at 70% 10%, #000 2%, transparent 74%);
            mask-image:radial-gradient(120% 34% at 70% 10%, #000 2%, transparent 74%)}
  .hero__sky::before{width:110%;inset-inline-start:-110%}
}
/* 1,35 / 0,65 et non 1,25 / 0,75 : le nouveau H1 tient en DEUX lignes, et ces
   deux lignes doivent être grandes. Dix points de colonne rendus au texte, ce
   sont dix pour cent de corps en plus sur le titre ; l'emblème perd 53 px et
   reste, de loin, le sujet du cadre. */
.hero__grid{display:grid;gap:var(--space-l);grid-template-columns:1.35fr .65fr;align-items:center}
.hero__title{font-size:var(--step-6);line-height:.96;letter-spacing:-.035em;max-width:17ch}
.hero__lede{max-width:46ch;margin-block:var(--space-m);color:var(--text-soft);
  font-size:var(--step-1);line-height:1.5}
.hero__cta{display:flex;gap:var(--space-m);align-items:center;flex-wrap:wrap}
/* `flex-wrap` n'est pas un détail cosmétique ici : sans lui, cette ligne a un
   min-content d'environ 350 px (six éléments flex insécables, dont un
   interlettrage de .34em). Ce min-content gonflait la PISTE DE GRILLE du hero,
   donc `.wrap`, donc le document entier : 350 px de contenu pour 320 px
   d'écran. Le débordement ne venait pas d'une largeur en dur mais d'une
   contrainte intrinsèque remontée par la grille — d'où l'inutilité d'un
   correctif appliqué plus haut. On desserre aussi l'interlettrage et l'écart
   sous 380 px : la ligne tient alors sans se casser en trois. */
/* LA LIGNE DE PREUVE. Elle se pose entre les CTA et l'indice de défilement, en
   petit corps et en --text-soft (pas --text-dim) : c'est une preuve, elle doit
   se LIRE, pas se deviner. Sa mesure est bornée pour qu'elle tienne en deux
   lignes sous le bouton et ne devienne jamais un paragraphe. */
.hero__proof{margin-block-start:var(--space-s);max-width:54ch;
  font-size:var(--step--1);line-height:1.55;color:var(--text-soft)}
.hero__hint{margin-top:var(--space-l);display:flex;flex-wrap:wrap;align-items:center;gap:8px 14px;
  font-size:var(--step--2);letter-spacing:.34em;text-transform:uppercase;color:var(--text-dim)}
/* Le libellé et son point médian ne se séparent pas : ils forment un seul objet
   de flux. `gap:inherit` reprend l'écart du parent, donc l'espace AVANT le point
   (dans l'objet) et APRÈS lui (entre deux objets) restent identiques, y compris
   quand la règle des 380 px resserre l'ensemble. */
.hero__hint>span{display:inline-flex;align-items:center;gap:inherit}
.hero__hint i{font-style:normal}
/* Sous 470 px la ligne des quatre repères ne tient plus (elle réclame 437 px de
   piste, mesuré). On la compose alors en deux paires — et les points médians
   s'effacent : un séparateur ne sépare plus rien en fin de ligne, il y pend.
   Sur deux lignes, c'est l'écart qui sépare. Vérifié 320 → 460 px. */
@media (max-width:470px){
  /* Deux repères par ligne, quelle que soit la largeur des mots : une grille,
     et non un retour à la ligne qu'on borne à l'aveugle. Le filet quitte le
     flux pour ne pas occuper une cellule, et reste à gauche du bloc. */
  .hero__hint{position:relative;display:grid;grid-template-columns:max-content max-content;
    justify-content:start;padding-inline-start:24px}
  .hero__hint::before{position:absolute;inset-block-start:1px;inset-inline-start:0}
  .hero__hint i{display:none}
}
@media (max-width:380px){
  .hero__hint{letter-spacing:.2em;gap:6px 10px}
  /* Sous 380 px la piste (~276 px) est plus étroite que le min-content de
     l'eyebrow hero (~296 px) : resserrer rend ~41 px, une seule ligne. */
  .eyebrow{letter-spacing:.16em;gap:8px}
}

/* Le hero en colonne : l'anneau est LE sujet, il doit être dans le premier
   écran, pas 35 px en dessous. Le bloc de texte se dissout dans la grille
   (display:contents) pour que l'anneau remonte juste sous l'appel à l'action
   et que le repère Pourquoi/Comment/Quoi passe DESSOUS lui. Le rythme est
   ensuite tenu par un seul écart, celui de la grille : plus aucune marge
   interne ne s'ajoute par-dessus.                                            */
@media (max-width:980px){
  .hero{min-height:auto;padding-block:var(--space-s) var(--space-l)}
  .hero__grid{grid-template-columns:1fr;gap:var(--space-s)}
  .hero__text{display:contents}
  .hero .eyebrow{margin-block-end:0}
  /* Même contrainte qu'en desktop, autre géométrie : ici la colonne, c'est la
     fenêtre moins ses marges. Le corps suit donc la largeur au lieu de la
     devancer, pour que « en jours, pas en mois. » tienne sur UNE ligne dès
     ~355 px. Sous cette largeur, la ligne se casse en deux : c'est assumé,
     descendre encore le corps rendrait le titre plus petit que le chapô.
     Garde-fou WCAG 1.4.4 respecté : 54,4 / 32 = 1,7 (< 2,5). */
  .hero__title{font-size:clamp(2rem, 10vw - 0.25rem, 3.4rem);max-width:15ch}
  .hero__lede{font-size:var(--step-0);line-height:1.55;max-width:50ch;margin-block:0}
  .hero__cta{gap:var(--space-s)}
  /* `display:contents` sur .hero__text : chaque enfant devient un élément de
     grille, l'écart est tenu par la grille SEULE. Toute marge propre s'y
     ajouterait et casserait la cadence. */
  .hero__proof{margin-block-start:0}
  .hero__hint{order:1;margin-top:0}
}
/* LA RÉSERVE DE L'ACCROCHE. « Ce qui traîne, ce n'est pas le site. » est la plus
   serrée des sept propositions : mesurée à 273 px de texte pour 276 px de piste
   à 320 px de large, soit TROIS pixels. Ça tient, et ça ne tient que là — un
   rendu de police légèrement plus large sur un autre appareil casserait la
   ligne en deux et détruirait la découpe voulue. Sous 341 px, et seulement là,
   le corps descend d'un cran : la marge passe de 3 à 25 px, et rien ne change
   au-dessus de cette largeur. */
@media (max-width:340px){
  .hero__title{font-size:clamp(1.80rem, 9.2vw, 2.2rem)}
}
.hero__hint::before{content:"";width:1px;height:30px;
  background:linear-gradient(180deg,var(--metal-edge),transparent);
  animation:hintPulse 3.4s var(--ease-sine) infinite}
@keyframes hintPulse{0%,100%{transform:scaleY(.55);opacity:.5}50%{transform:scaleY(1);opacity:1}}

/* Le split du titre : chaque ligne se lève sous son propre masque. */
.split .line{display:block;overflow:hidden;padding-block:.06em}
.js .split .line>span{display:block;transform:translateY(106%)}

/* LE SUJET : l'emblème Otaweb, monté en PIÈCE DE MÉTAL QUI TOURNE.
   ---------------------------------------------------------------------------
   Le critère posé par Rayan : « que ça ait l'air d'une vraie pièce de métal qui
   tourne, pas d'une image qui se déforme. » Une image qu'on écrase en `scaleX`
   rate quatre choses, et ce sont ces quatre-là qui font l'objet :

   1. LA PERSPECTIVE. Ici c'est un vrai `rotateY` dans un contexte 3D : le bord
      qui vient vers nous est réellement plus grand que celui qui s'éloigne.
      Un `scaleX` est symétrique, donc plat, quoi qu'on fasse.
   2. L'ÉPAISSEUR. Huit copies du disque échelonnées en profondeur, masquées en
      ANNEAU : de face elles ne sont qu'un liseré sombre qui donne son arête à la
      pièce ; de trois quarts elles se lisent comme une tranche pleine.
   3. DEUX FACES DIFFÉRENTES. Devant l'emblème, derrière du chrome brossé en
      cercles concentriques. Une pièce n'a pas deux fois le même côté — c'est ce
      qui interdit de lire une carte qui se retourne.
   4. LA RÉPONSE SPÉCULAIRE. Le chrome ne s'éclaire pas uniformément : il FLASHE
      au passage de face, puis s'éteint. Le balayage est calé sur la rotation,
      une fois par face et par tour.

   Budget : zéro octet ajouté (tout est peint), une seule propriété animée par
   couche (`transform` / `opacity` / `background-position`), donc thread
   compositeur. Le mouvement ne s'arme qu'après `load` — au premier affichage la
   pièce est de face, exactement comme avant, donc l'élément LCP ne change pas.  */
.emblem{position:relative;width:min(100%,440px);aspect-ratio:1;margin-inline:auto;
  isolation:isolate;perspective:1150px;perspective-origin:50% 46%}
.coin{position:absolute;inset:0;transform-style:preserve-3d;
  transform:rotateY(var(--coin-a,0deg))}
.emblem.is-turning .coin{animation:coinTurn var(--t-coin,8s) linear infinite}
@keyframes coinTurn{from{transform:rotateY(0deg)}to{transform:rotateY(360deg)}}

.coin__face{position:absolute;inset:0;backface-visibility:hidden;transform-style:preserve-3d}
.coin__face--front{transform:translateZ(var(--coin-t,7px))}
/* Le DOS : du chrome poli, pas l'emblème retourné.
   Un `repeating-conic-gradient` fin a été essayé et jeté : à 320 px de diamètre
   ses bandes se croisent en MOIRÉ, et le dos se lisait comme un disque
   hypnotique (constaté à la capture, station 0). Ce qui fait le chrome, ce ne
   sont pas des rayures, ce sont quatre lobes de lumière qui tournent avec la
   pièce — d'où un `conic-gradient` à lobes larges, posé sous le modelé radial
   d'une surface bombée. Aucune fréquence spatiale fine, donc aucun moiré à
   aucune taille. Le masque est celui de la face avant : même silhouette. */
.coin__face--back{transform:rotateY(180deg) translateZ(var(--coin-t,7px));
  background:
    /* le modelé d'une surface bombée : clair vers le haut-gauche, éteint au bord */
    /* le cercle gravé, trace de l'outil : deux arêtes fines, rien de plus */
    radial-gradient(closest-side,
      transparent 0 40.5%, oklch(88% .012 250 / .20) 41% 41.6%,
      transparent 42% 43%, oklch(12% .04 260 / .46) 43.4% 44.2%,
      transparent 45%),
    radial-gradient(closest-side at 38% 34%,
      oklch(56% .02 250 / .40) 0%,
      oklch(30% .035 256 / .34) 44%,
      oklch(18% .05 260 / .74) 76%,
      oklch(14% .05 262 / .95) 100%),
    /* quatre lobes de lumière : ce qui fait le chrome, pas des rayures */
    conic-gradient(from 210deg,
      oklch(20% .045 258) 0deg,
      oklch(42% .028 252) 42deg,
      oklch(23% .04 256) 96deg,
      oklch(52% .024 250) 150deg,
      oklch(21% .042 256) 208deg,
      oklch(39% .03 252) 268deg,
      oklch(22% .04 256) 320deg,
      oklch(20% .045 258) 360deg);
  -webkit-mask-image:radial-gradient(closest-side,#000 34%,oklch(0% 0 0 / .55) 58%,transparent 90%);
          mask-image:radial-gradient(closest-side,#000 34%,oklch(0% 0 0 / .55) 58%,transparent 90%)}
/* LE MONOGRAMME FRAPPÉ EN CREUX AU REVERS. Une pièce qui montre un disque nu la
   moitié du temps, c'est une marque absente la moitié du temps — sur le PREMIER
   écran, c'est cher payé. La même image que la face avant est donc reprise ici,
   mais traitée en gravure : écrasée en valeur, très contrastée, et retournée en
   X pour qu'elle se lise à l'endroit une fois la face pivotée. Deux copies :
   l'une sombre décalée d'un pixel vers le bas (le creux), l'autre claire décalée
   d'un pixel vers le haut (l'arête que la lumière attrape). C'est ce décalage,
   et lui seul, qui fait lire « frappé » plutôt que « imprimé ».
   Zéro octet : le fichier est déjà en cache, c'est celui du hero. */
.coin__grav{position:absolute;inset:9%;transform:scaleX(-1);pointer-events:none;
  background-image:url("img/embleme-hero-mobile.webp");
  background-size:contain;background-position:50% 50%;background-repeat:no-repeat;
  -webkit-mask-image:radial-gradient(closest-side,#000 62%,transparent 96%);
          mask-image:radial-gradient(closest-side,#000 62%,transparent 96%);
  /* INVERSION, et c'est tout le truc : la source a un monogramme CLAIR sur un
     fond sombre et opaque. Multiplier ça tel quel noircit tout le disque (essai
     fait, revers éteint). Inversée, l'image devient un monogramme sombre sur un
     fond clair : le `multiply` ne mord alors QUE sur le monogramme, et le
     chrome autour reste intact. */
  filter:grayscale(1) invert(1) brightness(1.06) contrast(1.35);opacity:.62;
  mix-blend-mode:multiply}
.coin__grav::after{content:"";position:absolute;inset:0;
  background-image:inherit;background-size:contain;background-position:50% calc(50% - 1.5px);
  background-repeat:no-repeat;
  filter:grayscale(1) brightness(1.5) contrast(1.6);opacity:.16;mix-blend-mode:screen}
@media (min-width:981px){ .coin__grav{background-image:url("img/embleme-hero.webp")} }

/* LA TRANCHE. Un plan qui contient l'axe de rotation : invisible de face (il est
   alors vu par la tranche, surface projetée nulle), pleinement visible quand la
   pièce passe de profil — exactement la visibilité complémentaire des deux
   faces. Sans lui, la pièce DISPARAÎT deux fois par tour, ce qui est le défaut
   que ce montage devait précisément corriger (constaté au banc, angle 90°).
   Son dégradé horizontal est l'ombrage d'un cylindre : sombre, spéculaire,
   sombre. Son masque vertical reprend le fondu du bord de l'œuvre, pour que la
   tranche ne soit pas plus nette que la pièce dont elle est le bord.          */
.coin__edge{position:absolute;opacity:0;inset-block:29%;inset-inline-start:50%;
  width:calc(var(--coin-t,7px) * 2);margin-inline-start:calc(var(--coin-t,7px) * -1);
  border-radius:calc(var(--coin-t,7px));
  transform:rotateY(90deg);
  background:linear-gradient(90deg,
    oklch(20% .04 258) 0%, oklch(38% .032 256) 26%,
    oklch(74% .018 250) 48%, oklch(42% .03 254) 70%,
    oklch(21% .04 258) 100%);
  -webkit-mask-image:linear-gradient(180deg, transparent 0%, #000 26%, #000 74%, transparent 100%);
          mask-image:linear-gradient(180deg, transparent 0%, #000 26%, #000 74%, transparent 100%)}

.emblem__img{display:block;width:100%;height:auto;
  -webkit-mask-image:radial-gradient(closest-side,#000 55%,transparent 99%);
          mask-image:radial-gradient(closest-side,#000 55%,transparent 99%)}


/* LE BALAYAGE. Il vit DANS la face, donc il hérite de son `backface-visibility`
   et ne peut pas éclairer un côté qu'on ne voit pas. `screen` : une lumière
   s'ajoute, elle ne repeint pas. */
.coin__spec{position:absolute;inset:0;border-radius:50%;pointer-events:none;opacity:0;
  background:linear-gradient(102deg,
    transparent 37%, oklch(100% .002 250 / .48) 47.5%, transparent 58%);
  background-size:260% 100%;background-position:135% 0;
  mix-blend-mode:screen;
  -webkit-mask-image:radial-gradient(closest-side,#000 52%,transparent 93%);
          mask-image:radial-gradient(closest-side,#000 52%,transparent 93%)}
.emblem.is-turning .coin__spec{animation:coinSpec var(--t-coin,8s) var(--ease-sine) infinite}
.emblem.is-turning .coin__edge{animation:coinEdge var(--t-coin,8s) linear infinite}
@keyframes coinEdge{
  0%,50%,100%{opacity:0}
  17%,67%{opacity:.22}
  25%,75%{opacity:1}
  33%,83%{opacity:.22}
}
/* La face avant est frontale à 0 % du cycle, la face arrière à 50 % : un simple
   décalage négatif d'un demi-tour suffit, une seule règle pour les deux. */
.emblem.is-turning .coin__face--back .coin__spec{animation-delay:calc(var(--t-coin,8s) / -2)}
/* Le reflet ENTRE par la droite quand la face revient vers nous (74 → 100 %),
   FLASHE au passage de face (0 %), puis SORT par la gauche (0 → 26 %). Au-delà,
   la face est de dos : il n'y a plus rien à éclairer, l'opacité reste nulle et
   le navigateur n'a plus rien à composer. */
@keyframes coinSpec{
  0%  {opacity:.52; background-position:46% 0}
  10% {opacity:.46; background-position:4% 0}
  20% {opacity:.18; background-position:-32% 0}
  26% {opacity:0;   background-position:-54% 0}
  74% {opacity:0;   background-position:152% 0}
  80% {opacity:.18; background-position:126% 0}
  90% {opacity:.46; background-position:88% 0}
  100%{opacity:.52; background-position:46% 0}
}

/* Sa lumière propre : un halo de son bleu roi, qui respire très lentement. */
.emblem::before{content:"";position:absolute;inset:-12%;z-index:-1;border-radius:50%;
  background:radial-gradient(closest-side, oklch(46% .20 264 / .42), transparent 72%);
  animation:haloBreath 9s var(--ease-sine) infinite alternate}
@keyframes haloBreath{from{transform:scale(.94);opacity:.72}to{transform:scale(1.06);opacity:1}}
@media (max-width:980px){
  .emblem{width:min(74%,320px);perspective:820px}
}

/* --------------------------------------------------- 9. POURQUOI (manifeste)
   Pas une carte, pas une liste, pas une icône : du texte, de l'espace, et une
   ligne qui brille.                                                           */
.manifesto{position:relative}
.manifesto__h{font-size:var(--step-4);max-width:19ch;margin-block-end:var(--space-l)}
.manifesto__body{max-width:60ch;display:grid;gap:var(--space-m);
  font-size:var(--step-1);line-height:1.6;color:var(--text-soft)}
.manifesto__body strong{color:var(--text);font-weight:400}
.manifesto__chute{margin-block-start:var(--space-l);padding-block-start:var(--space-m);
  border-block-start:1px solid var(--line);
  font-family:var(--font-display),"Space Grotesk fallback",sans-serif;
  font-size:var(--step-2);font-weight:600;line-height:1.22;max-width:26ch;letter-spacing:-.02em}
/* Le trait rasant CSS a été retiré : l'ambiance « faisceau » de la section fait
   ce travail avec de la vraie matière. Deux lumières sur la même section, c'était
   de l'empilement, pas de l'épure. */

/* ------------------------------------------------- 9 bis. LE FACE À FACE
   Sept situations, sept sorties. Le risque de cette section n'est pas de compo,
   il est de TON : traitée en rouge, en gros, en cartes agressives, elle devient
   un réquisitoire quel que soit le texte. La sobriété est donc une condition de
   fonctionnement, pas un parti pris.
   Trois décisions en découlent :
   · Pas de carte. Une partition : un filet entre chaque paire, rien autour.
     (Test de la boîte : de la prose encadrée, c'est le marqueur « IA » n° 1.)
   · La situation est la colonne CALME — corps d'affichage, valeur adoucie ; la
     réponse est la colonne qu'on veut retenir — blanche, marquée d'un filet
     bleu, le même que celui des listes de `#combien`. Le pivot se voit sans
     qu'on ait à l'écrire.
   · Les deux libellés de colonne n'apparaissent qu'UNE fois, en tête, et
     seulement là où il y a deux colonnes. Répétés sept fois, ce serait du bruit ;
     en mobile, où tout s'empile, c'est le filet bleu qui porte l'information.   */
.faces{position:relative;margin-block-start:var(--space-l);border-block-start:1px solid var(--line)}
/* Quand les deux libellés de colonne sont là, c'est eux qui portent l'écart :
   la liste se recolle à eux, sinon on lit trois blocs au lieu de deux. */
@media (min-width:900px){ .faces__legend + .faces{margin-block-start:0} }
/* RÈGLE ZÉRO, appliquée au bloc le plus HAUT de la page. La plaque de la fusée
   est bornée à un écran ; sous elle, sept face-à-face déroulent près de deux
   mille pixels qui, sans ceci, retomberaient sur l'aplat du document (constaté
   à la capture, station 1). Deux foyers en diagonale, très bas en opacité, qui
   débordent en pleine largeur — `overflow-x:clip` de la section les coupe net.
   Zéro octet : c'est de la lumière, pas une image.                            */
/* Un seul vocabulaire de lumière pour TOUS les blocs longs de la page — le
   face-à-face, le déroulé du projet, les postes de prix, la FAQ. Chacun de ces
   blocs dépasse le millier de pixels et vient APRÈS la plaque photographique de
   sa section, qui est bornée en hauteur. Sans cette nappe, le long-scroll
   retombe quatre fois sur l'aplat du document. La même règle partout, c'est
   aussi ce qui fait que la page se lit comme une seule matière et non comme des
   sections juxtaposées. Zéro octet : c'est de la lumière, pas une image. */
.faces,.process,.plans,.faq{position:relative}
.faces::after,.process::after,.plans::after,.faq::after{
  content:"";position:absolute;z-index:-1;pointer-events:none;
  inset-block:-6% -10%;inset-inline-start:-28vw;width:126vw;
  background:
    radial-gradient(26% 30% at 22% 16%, oklch(66% .12 250 / .13), transparent 72%),
    radial-gradient(30% 26% at 82% 44%, oklch(60% .10 254 / .11), transparent 74%),
    radial-gradient(28% 28% at 30% 78%, oklch(68% .11 248 / .11), transparent 74%)}
/* Décalées d'un bloc à l'autre : deux nappes identiques à deux écrans d'écart
   se verraient comme un motif. */
.process::after{background:
  radial-gradient(24% 34% at 78% 18%, oklch(66% .12 250 / .12), transparent 72%),
  radial-gradient(28% 30% at 18% 62%, oklch(60% .10 254 / .11), transparent 74%)}
.plans::after{background:
  radial-gradient(26% 32% at 16% 22%, oklch(66% .12 250 / .12), transparent 72%),
  radial-gradient(30% 28% at 86% 68%, oklch(60% .10 254 / .10), transparent 74%)}
.faq::after{background:
  radial-gradient(28% 26% at 84% 14%, oklch(66% .12 250 / .11), transparent 74%),
  radial-gradient(26% 30% at 20% 58%, oklch(60% .10 254 / .11), transparent 74%),
  radial-gradient(24% 24% at 74% 92%, oklch(68% .11 248 / .10), transparent 76%)}
.face{display:grid;gap:var(--space-s) var(--space-l);align-items:start;
  padding-block:var(--space-m);border-block-end:1px solid var(--line)}
.face__pb{font-family:var(--font-display),"Space Grotesk fallback",sans-serif;
  font-weight:500;font-size:var(--step-1);line-height:1.34;letter-spacing:-.015em;
  color:var(--text-soft);max-width:36ch}
.face__rp{position:relative;padding-inline-start:22px;color:var(--text);
  font-size:var(--step-0);line-height:1.6;max-width:56ch}
.face__rp::before{content:"";position:absolute;inset-inline-start:0;inset-block-start:.72em;
  width:11px;height:1px;background:var(--blue)}
.faces__legend{display:none}
@media (min-width:900px){
  .face{grid-template-columns:minmax(280px,.86fr) 1.14fr}
  .faces__legend{display:grid;grid-template-columns:minmax(280px,.86fr) 1.14fr;
    gap:var(--space-l);margin-block:var(--space-l) var(--space-2xs);
    font-size:var(--step--2);letter-spacing:.28em;text-transform:uppercase;color:var(--text-dim)}
  .faces__legend span:last-child{padding-inline-start:22px}
}
.house-rule{margin-block-start:var(--space-l);padding:var(--space-s) 0 0;
  border-block-start:1px solid var(--line);
  font-size:var(--step-1);color:var(--text);max-width:44ch}

/* Le déroulé d'un projet : quatre jalons sur un rail de métal. */
.process{margin-block-start:var(--space-l);display:grid;gap:0;
  grid-template-columns:repeat(auto-fit,minmax(200px,1fr))}
.process__step{position:relative;padding:var(--space-m) var(--space-s) 0 0}
.process__step::before{content:"";position:absolute;inset-block-start:0;inset-inline:0;height:1px;
  background:var(--line)}
.process__step::after{content:"";position:absolute;inset-block-start:-2px;inset-inline-start:0;
  width:5px;height:5px;border-radius:50%;background:var(--blue)}
.process__n{font-size:var(--step--2);letter-spacing:.28em;color:var(--text-dim);
  font-variant-numeric:tabular-nums}
.process__h{font-family:var(--font-display),"Space Grotesk fallback",sans-serif;font-weight:600;
  font-size:var(--step-0);margin-block:6px 8px;letter-spacing:-.01em}
.process__p{font-size:var(--step--1);color:var(--text-soft);max-width:34ch}

/* ------------------------------------------------------------- 11. FAMILLES */
.families{display:grid;gap:var(--space-l);margin-block-start:var(--space-l)}
@media (min-width:900px){ .families{grid-template-columns:repeat(3,1fr);gap:var(--space-m)} }
.family{position:relative;padding:var(--space-m) 0 0;border-block-start:1px solid var(--line);
  transition:border-color var(--t-fast) var(--ease)}
.family:hover{border-block-start-color:var(--blue)}
@media (min-width:900px){
  .family:nth-child(2){margin-block-start:var(--space-m)}
  .family:nth-child(3){margin-block-start:calc(var(--space-m) * 2)}
}
.family__h{font-size:var(--step-2);margin-block-end:var(--space-s)}
.family__p{color:var(--text-soft);max-width:44ch}

/* ------------------------------------------------- 11 bis. LE PLEXUS VIVANT
   Le fond de la section démonstration : nœuds qui dérivent, liens qui naissent
   et meurent avec la distance, INFLUX qui traversent le réseau et le laissent
   luire derrière eux. Peint en canvas 2D — zéro octet d'image, et une matière
   sans fin de boucle perceptible. Hors flux (position:absolute), donc CLS nul
   par construction ; il monte en opacité une fois armé.
   Le réseau habite le HAUT de la section puis s'éteint : plus bas, les cartes
   ont leur propre matière. Borner la hauteur sert aussi le budget — la section
   dépasse 2 000 px, tout repeindre serait quatre millions de pixels pour rien. */
.plexus{position:absolute;inset-block-start:0;inset-inline:0;
  height:min(100%, 880px);z-index:-1;width:100%;
  pointer-events:none;opacity:0;transition:opacity 1.6s var(--ease);
  /* Le masque CADRE, il ne protège pas : le contraste est confié au SCRIM
     ci-dessous. Foyer à 76 % — le réseau vit à DROITE, là où le titre et le
     chapô laissent la place. Plateau porté de 8 à 16 % et extinction de 78 à
     84 % : le réseau atteignait son point de fuite avant d'occuper le couloir
     droit et se lisait en poussière, pas en maillage (Rayan, 08/08).
     Rayon VERTICAL 52 % : le réseau est éteint à 74 % de la bande, juste
     au-dessus des cartes (qui commencent à 75 %, mesuré). Il débordait dessus,
     et leur fond ne couvrant qu'à 72 %, une arête chaude traversant la carte 02
     a fait tomber son texte à 4,39:1.                                          */
  -webkit-mask-image:radial-gradient(84% 52% at 76% 30%, #000 16%, transparent 84%);
          mask-image:radial-gradient(84% 52% at 76% 30%, #000 16%, transparent 84%)}
/* .6 → .86 : la présence se règle ICI, un seul gradateur. */
.plexus.is-on{opacity:.86}
/* LE SCRIM DE LA SECTION — un aplat de la couleur du fond posé ENTRE le réseau
   et le texte : le support des mots redevient --deep-1, une valeur connue, quoi
   que fasse le réseau dessous. En ::after : à z-index égal l'ordre de peinture
   suit l'ordre du document, donc il couvre le canvas ET l'image de repli, et
   reste sous le texte.
   Comme il est peint dans la couleur du fond, il est RIGOUREUSEMENT INVISIBLE
   là où le réseau n'a rien peint. On peut donc le SCULPTER — appuyer sur les
   bandes de petit texte, relâcher ailleurs — sans qu'aucune marche ne se voie
   sur le fond nu. C'est lui qui permet de monter la présence du réseau sans
   toucher au contraste des mots.
   Deux couches, sur la partition MESURÉE (bande 880 px : sur-titre 17–19 %,
   H2 23–40 %, chapô 48–60 % mais jusqu'à x = 88 %, cartes 75–97 %). Une seule
   couche horizontale ne suffisait pas : le chapô traverse le couloir droit, là
   où le réseau est le plus dense — une tête d'influx l'a fait tomber à 2,44:1. */
#demos::after{content:"";position:absolute;z-index:-1;pointer-events:none;
  inset-block-start:0;inset-inline:0;height:min(100%, 880px);
  background:
    /* 1. la bande du chapô (trois lignes de petit corps jusqu'à x = 88 %) puis
          celle des cartes, dont le fond propre ne couvre qu'à 72 % */
    linear-gradient(180deg,
      transparent 39%,
      oklch(15% .05 258 / .62) 48%,
      oklch(15% .05 258 / .60) 62%,
      oklch(15% .05 258 / .44) 69%,
      oklch(15% .05 258 / .52) 77%,
      oklch(15% .05 258 / .52) 100%),
    /* 2. la colonne de texte, à gauche */
    linear-gradient(94deg,
      oklch(15% .05 258 / .60) 0%,
      oklch(15% .05 258 / .54) 34%,   /* sur-titre + titre */
      oklch(15% .05 258 / .32) 52%,
      oklch(15% .05 258 / .10) 68%,   /* le réseau reprend la main */
      transparent 80%)}
/* En mobile le texte occupe toute la largeur : plus de « côté libre ». La
   respiration devient verticale, et c'est le scrim, sculpté ligne à ligne, qui
   décide de ce qu'on voit à chaque hauteur. */
@media (max-width:760px){
  /* L'ancien masque (74 % de haut éteint à 82 %) confinait le réseau aux
     150 premiers pixels : à la capture, on comptait DEUX points. */
  .plexus{height:min(100%, 680px);
    -webkit-mask-image:radial-gradient(150% 74% at 58% 16%, #000 20%, transparent 88%);
            mask-image:radial-gradient(150% 74% at 58% 16%, #000 20%, transparent 88%)}
  .plexus.is-on{opacity:.92}

  /* Partition MESURÉE au `Range` sur un vrai 390 px et APRÈS les révélations au
     scroll : relevée section hors écran, elle était fausse de 30 px (les `.rv`
     restent translatés) et tout le relief tombait à côté. Bande de 680 px :
     0–11 % marge haute, aucun mot ; 13–15 % le sur-titre (13,5 px gris pâle,
     seuil 4,5 — le point le plus serré de la page, son budget de fond ne vaut
     qu'une fois et demie le fond nu : POCHE dédiée) ; 19–38 % le H2 (39 px
     blanc, seuil 3,0, mesuré 13,3:1 — c'est là que le réseau doit se voir
     DERRIÈRE le titre) ; 44–74 % le chapô, pleine largeur ; > 86 % les cartes
     ont leur fond. Poches ELLIPTIQUES : une bande en travers couperait le
     réseau d'un trait horizontal. */
  #demos::after{height:min(100%, 680px);
    background:
      /* 1. la poche du sur-titre (x 6–53 %, y 13–15 %) */
      radial-gradient(78% 10% at 28% 14%,
        oklch(15% .05 258 / .90) 0%, oklch(15% .05 258 / .86) 50%, transparent 92%),
      /* 2. le mot métallisé « parole » (H2 ligne 3, x 6–34 %, y 32–38 %) : un
         `background-clip:text` n'a pas de couleur propre, c'est le pôle le PLUS
         SOMBRE du métal qui fait foi — 2,95:1 pour 3,00 sous une arête chaude. */
      /* Poche RENFORCÉE avec la dynamique du réseau : à .62 elle tenait un
         plexus à sept influx, elle laissait passer une tête à quatorze
         (mesuré 2,70:1 pour un seuil de 3,00, une fois sur quatorze passes —
         un défaut stochastique se traque en répétant, pas en regardant). */
      radial-gradient(62% 10% at 20% 35%, oklch(15% .05 258 / .82), transparent 80%),
      /* 3. la partition verticale */
      linear-gradient(180deg,
        oklch(15% .05 258 / .22)  0%,
        oklch(15% .05 258 / .52) 12%,
        oklch(15% .05 258 / .50) 16%,
        oklch(15% .05 258 / .36) 24%,
        oklch(15% .05 258 / .40) 38%,
        oklch(15% .05 258 / .64) 46%,
        oklch(15% .05 258 / .60) 76%,
        oklch(15% .05 258 / .34) 86%,
        transparent 100%)}
}
/* LE REPLI — mouvement réduit, ou pas de contexte 2D. La classe est posée par
   le script, qui est le seul à connaître les deux cas ; l'image n'est donc
   téléchargée QUE par ceux qui la reçoivent, jamais par les autres. La section
   garde une matière : on ne renvoie personne sur un écran vide.               */
.plexus-repli .plexus{display:none}
/* LE REPLI EST LE PLEXUS LUI-MÊME, FIGÉ. C'était une photo de banque d'images :
   un flou bleuté qui ne se lisait plus comme un réseau, posé ailleurs que le
   canvas et deux fois moins présent — en mouvement réduit on voyait une AUTRE
   section. La pièce vient de `09_ops/_tools/_repli_plexus_export.mjs`, qui
   laisse la vraie boucle tourner puis exporte une image où des influx ont laissé
   leur sillage : même maillage, même densité, même cadrage.
   Servie en `screen` (aplatie sur noir : 15 Ko en 1440×880, contre 133 Ko à
   canal alpha), taille NATIVE, calée sur la bande — d'où un masque en pixels
   ABSOLUS identique à celui de `.plexus`, le pseudo-élément couvrant toute la
   section. Le scrim passe par-dessus : même protection du texte.
   La règle est écrite ICI et nulle part ailleurs : la section `#demos` n'a pas
   de `<div class="amb">` (son fond est peint, pas photographié), donc l'image
   n'existe dans la feuille que sous la classe `.plexus-repli`, posée par le
   script. Personne d'autre ne la télécharge.                                  */
.plexus-repli #demos::before{
  content:"";position:absolute;inset-block-start:0;inset-inline:0;z-index:-1;
  height:min(100%, 880px);pointer-events:none;
  background-image:url("img/plexus.webp");background-size:100% 880px;
  background-position:0 0;background-repeat:no-repeat;
  opacity:.86;mix-blend-mode:screen;
  -webkit-mask-image:radial-gradient(84% 458px at 76% 264px, #000 16%, transparent 84%);
          mask-image:radial-gradient(84% 458px at 76% 264px, #000 16%, transparent 84%)}
@media (max-width:760px){
  .plexus-repli #demos::before{height:min(100%, 680px);
    background-image:url("img/plexus-m.webp");background-size:100% 680px;opacity:.92;
    -webkit-mask-image:radial-gradient(150% 503px at 58% 109px, #000 20%, transparent 88%);
            mask-image:radial-gradient(150% 503px at 58% 109px, #000 20%, transparent 88%)}
}

/* ---------------------------------------------------------------- 12. DÉMOS
   Deux démonstrations de PREUVE — un chiffre mesuré chez le visiteur, une fiche
   lue dans le code de la page. Les deux démos d'EFFET (halo au curseur, barres
   pilotées par le scroll) ont été retirées le 08/08 : une section qui ne
   contient plus que du vérifiable est plus forte qu'une section qui mélange les
   deux registres. Aucune image ici non plus.                                   */
.demos{display:grid;gap:var(--space-m);margin-block-start:var(--space-l)}
@media (min-width:820px){ .demos{grid-template-columns:repeat(2,1fr)} }
/* overflow:clip et non hidden : `hidden` ferait de la carte un conteneur de
   défilement, ce qui déroute les timelines de la page. `clip` borne à
   l'identique, sans conteneur de défilement.                                   */
.demo{position:relative;overflow:clip;border-radius:var(--r-panel);
  background:linear-gradient(180deg, oklch(22% .055 256 / .72), oklch(16% .05 258 / .72));
  border:1px solid var(--line);
  padding:var(--space-m);display:flex;flex-direction:column;gap:var(--space-s);
  min-height:290px;
  transition:border-color var(--t-fast) var(--ease),transform var(--t-fast) var(--ease)}
.demo:hover{border-color:var(--line-strong);transform:translateY(-3px)}
.demo__n{font-size:var(--step--2);letter-spacing:.3em;color:var(--text-dim);
  font-variant-numeric:tabular-nums}
.demo__h{font-family:var(--font-display),"Space Grotesk fallback",sans-serif;font-weight:600;
  font-size:var(--step-1);letter-spacing:-.015em}
.demo__p{font-size:var(--step--1);color:var(--text-soft);max-width:42ch}
.demo__stage{margin-block-start:auto;position:relative;min-height:104px;border-radius:var(--r);
  overflow:clip;background:oklch(11% .04 260 / .55);border:1px solid var(--line)}

/* 01 — la vitesse : le chiffre mesuré dans le navigateur du visiteur. */
.speed{position:absolute;inset:0;display:grid;place-content:center;gap:6px;text-align:center}
.speed__v{font-family:var(--font-display),"Space Grotesk fallback",sans-serif;font-weight:700;
  font-size:var(--step-4);line-height:1;letter-spacing:-.03em;font-variant-numeric:tabular-nums;
  color:transparent;background-image:var(--metal-face);
  -webkit-background-clip:text;background-clip:text}
.speed__l{font-size:var(--step--2);letter-spacing:.24em;text-transform:uppercase;color:var(--text-dim)}

/* 02 — être trouvé : la page lit SES propres balises et les affiche.
   `position:static` et non `absolute` : en mobile le titre de la fausse SERP passe
   sur deux lignes, dépassait la hauteur plancher du cadre et se faisait rogner en
   haut par l'`overflow:clip` (défaut mesuré, pré-existant). En flux, le cadre prend
   la hauteur de son contenu et plus rien n'est coupé, à toutes les largeurs. */
.serp{position:static;padding:14px 16px;display:grid;align-content:center;gap:5px;
  text-align:start;font-family:var(--font-text),"Satoshi fallback",sans-serif}
.serp__u{font-size:var(--step--2);color:var(--text-dim);letter-spacing:.02em}
.serp__t{font-size:var(--step-0);color:var(--blue);line-height:1.25;font-weight:500}
.serp__d{font-size:var(--step--2);color:var(--text-soft);line-height:1.45}
.serp__b{margin-block-start:4px;font-size:var(--step--2);color:var(--text-dim);
  display:flex;gap:8px;align-items:center}
.serp__b b{font-weight:600;color:var(--text-soft)}

/* ------------------------------------------------ 12 bis. LA GALERIE
   « Ce que ça donne » : huit pages en trois familles, chacune tirée sur son
   carton ivoire — un cadre de papier, plus un châssis de navigateur — avec son
   étiquette PEINTE DANS L'IMAGE — lisible sur les grandes cartes et à DPR ≥ 2 ;
   sur les petites vignettes à DPR 1 elle se devine plus qu'elle ne se lit, et
   c'est la LÉGENDE HTML (+ l'alt) qui porte l'information partout (audit 08/08).
   Ce n'est pas une sixième carte de démo : pas de panneau, pas de bordure
   autour de chaque vignette. L'image porte son propre cadre, la légende se
   pose dessous en petit corps. Planche-contact, pas interface.                */
.gallery{position:relative;margin-block-start:var(--space-l);padding-block-start:var(--space-m)}
/* Le même filet de métal que la chute : un seul vocabulaire de séparation dans
   toute la section. */
.gallery::before{content:"";position:absolute;inset-block-start:0;inset-inline:0;height:1px;
  background:linear-gradient(90deg,var(--metal-edge),var(--line) 44%,transparent)}
/* Règle zéro : le bloc ne se pose pas sur un aplat. Le plexus s'éteint bien
   au-dessus (il est borné à 880 px du haut de section) ; la galerie réclame
   donc sa propre lumière. Deux foyers en diagonale, très bas en opacité :
   ils font respirer les vignettes sans jamais concurrencer leurs images.      */
.gallery::after{content:"";position:absolute;z-index:-1;pointer-events:none;
  inset-block:calc(var(--space-m) * -1) calc(var(--space-m) * -1);
  inset-inline-start:-24vw;width:118vw;
  background:
    radial-gradient(32% 46% at 22% 16%, oklch(70% .12 248 / .16), transparent 72%),
    radial-gradient(40% 56% at 84% 82%, oklch(62% .10 252 / .13), transparent 76%)}
.gallery__h{font-family:var(--font-display),"Space Grotesk fallback",sans-serif;font-weight:600;
  font-size:var(--step-2);letter-spacing:-.02em;margin-block-start:var(--space-2xs)}
.gallery__lede{margin-block-start:var(--space-s);max-width:62ch;
  font-size:var(--step--1);line-height:1.6;color:var(--text-soft)}
/* Le titre d'une famille de la galerie et son chapeau. Un cran SOUS
   `.gallery__h`, sans filet ni pastille : c'est un repère de lecture, pas une
   sous-section. Le chapeau ne redit jamais ce qu'est le métier (la section 04
   s'en charge), il dit ce qu'on a sous les yeux et où regarder. */
.gallery__fam{font-family:var(--font-display),"Space Grotesk fallback",sans-serif;
  font-weight:600;font-size:var(--step-1);letter-spacing:-.015em;
  margin-block-start:var(--space-l)}
.gallery__famp{margin-block-start:var(--space-2xs);max-width:56ch;
  font-size:var(--step--1);line-height:1.6;color:var(--text-dim)}
.gallery__fam:first-of-type{margin-block-start:var(--space-m)}
/* La famille à UNE seule pièce garde EXACTEMENT le gabarit des autres
   maquettes. La présenter en grand la rendrait plus grande que nos deux pages
   réellement servies : la compo dirait alors le contraire des étiquettes, à
   toutes les largeurs. Le trou se règle par la composition (`.gallery__paire`),
   pas par la taille. */

/* MOBILE — un rail qu'on fait glisser. Cinq captures de téléphone empilées,
   c'est 4 000 px de page en plus : la section des prix passerait sous
   l'horizon. Le rail garde la galerie à hauteur d'un écran, montre le rendu
   MOBILE des pages (ce que le visiteur aurait vraiment sous les yeux) et
   laisse la vignette suivante affleurer — l'affordance est visuelle, pas
   écrite. Il déborde jusqu'aux bords de l'écran pour que ce débord se voie.   */
.gallery__grid{display:grid;grid-auto-flow:column;grid-auto-columns:76%;
  gap:var(--space-s);margin-block-start:var(--space-m);
  list-style:none;padding:0;
  overflow-x:auto;overscroll-behavior-x:contain;
  scroll-snap-type:x mandatory;scroll-behavior:smooth;
  margin-inline:calc(var(--pad-x) * -1);padding-inline:var(--pad-x);
  /* Sans scroll-padding, le snap `mandatory` ramène l'item à x=0 et ANNULE le
     padding-inline : le rail décrochait de la gouttière de 22 px (audit 08/08). */
  scroll-padding-inline-start:var(--pad-x);
  scrollbar-width:none}
.gallery__grid::-webkit-scrollbar{display:none}
.shot{scroll-snap-align:start}
/* Rail mobile seulement : la carte resto vient d'une page d'UN SEUL écran
   (844 px), aucun cadrage au gabarit plein n'existe — elle s'aligne en PIED
   avec les autres : décrochement voulu, pas accident (audit 08/08). En
   desktop les rangées restent calées en tête (ratios égaux, légendes libres).
   `subgrid` est ce qui rend l'alignement VRAI. Avec deux pistes déclarées dans
   chaque carte, la piste des légendes prend la hauteur de SA propre légende :
   une légende d'une ligne de plus (le resto) remontait son image de 21 px,
   mesuré. En reprenant les pistes du rail, toutes les cartes partagent les
   mêmes lignes : les images tombent au même pied, les légendes partent au même
   niveau, quel que soit le nombre de lignes. */
@media (max-width:719px){
  .gallery__grid{grid-template-rows:1fr auto}
  /* `row-gap:0` : un subgrid hérite des gouttières du parent sur l'axe repris.
     Sans cette remise à zéro, la gouttière du rail (--space-s) s'ajoutait à la
     marge propre de la légende, qui reculait de 18 px sous son image. */
  .shot{display:grid;grid-template-rows:subgrid;grid-row:span 2;row-gap:0}
  .shot picture{align-self:end}
}
/* LA VIGNETTE, SANS CADRE (17/08). Les captures étaient montées sur un papier
   teinté ; aucune teinte n'a convenu, et le montage imposait aussi des tailles
   inégales. La vignette est désormais la capture nue, au cadrage fixe. Ce qui
   la détache de la page n'est plus un fond mais un liseré fin et une ombre
   portée : la pièce est posée sur la page, elle n'est plus collée sur un
   carton. L'étiquette de vérité vit sous l'image, dans `.shot__cap`. */
.shot img{display:block;width:100%;height:auto;border-radius:var(--r-photo);
  border:1px solid oklch(100% 0 0 / .10);
  box-shadow:0 2px 4px -1px oklch(8% .03 262 / .5),
             0 16px 30px -12px oklch(8% .03 262 / .7);
  transition:transform var(--t-fast) var(--ease),
             box-shadow var(--t-fast) var(--ease)}
.shot:hover img{box-shadow:0 3px 6px -1px oklch(8% .03 262 / .55),
                           0 22px 42px -14px oklch(8% .03 262 / .8)}
.shot__cap{margin-block-start:var(--space-xs);max-width:46ch;
  font-size:var(--step--1);line-height:1.5;color:var(--text-dim)}
.gallery__hint{margin-block-start:var(--space-s);
  font-size:var(--step--2);letter-spacing:.28em;text-transform:uppercase;color:var(--text-dim)}

/* DESKTOP — la grille. Deux vignettes larges (les deux pages réellement
   servies), les six maquettes plus petites : la hiérarchie de la grille dit la
   même chose que les légendes, dans les trois familles. */
@media (min-width:720px){
  /* 6 colonnes dès 720 px : la hiérarchie réel (span 3) > maquette (span 2)
     doit tenir à TOUTES les largeurs — en 2 colonnes + item 5 pleine largeur,
     la maquette s'affichait plus grande que les pages réelles entre 720 et
     1023 px, et la compo disait l'inverse du chapô (audit 08/08). */
  .gallery__grid{grid-auto-flow:row;grid-template-columns:repeat(6,1fr);
    gap:var(--space-m) var(--space-m);
    overflow:visible;scroll-snap-type:none;
    margin-inline:0;padding-inline:0}
  /* TOUTES LES PIÈCES ONT LE MÊME GABARIT (17/08). Auparavant les deux pages
     servies occupaient span 3 et les maquettes span 2 : la hiérarchie était
     juste, mais la galerie donnait l'impression d'un montage inégal. Elles
     sont désormais au même format ; c'est l'étiquette, et elle seule, qui
     distingue une page servie d'une maquette. */
  .shot{grid-column:span 2}
  .shot:hover img{transform:translateY(-4px)}
  .gallery__hint{display:none}

  /* FAMILLE COURTE (une ou deux pièces). Trois essais, une règle (10/08) :
     les PIÈCES gardent la même amorce à gauche dans toute la galerie — c'est
     le fil conducteur, le déplacer pour boucher un vide casse la colonne. Le
     TEXTE s'adapte : le chapeau se pose CONTRE la pièce (après une colonne
     vide il se lit comme un second trou) et se cale à MI-HAUTEUR, ce qui
     répartit le blanc en deux respirations au lieu d'une cellule vide.
     Les pistes internes sont déjà au gabarit : la pièce n'y re-déclare pas de
     span, sinon elle occuperait deux pistes. */
  .gallery__paire{display:grid;grid-template-columns:repeat(6,1fr);
    gap:var(--space-m);align-items:start}
  .gallery__paire > .gallery__famp,
  .gallery__paire > .gallery__grid{grid-row:1;margin-block-start:0}
  .gallery__paire > .gallery__famp{align-self:center;grid-column:5 / -1}
  .gallery__paire > .gallery__grid{grid-column:1 / span 4;grid-template-columns:repeat(2,1fr)}
  .gallery__paire > .gallery__grid--solo{grid-column:1 / span 2;grid-template-columns:1fr}
  .gallery__paire:has(.gallery__grid--solo) > .gallery__famp{grid-column:3 / span 3}
  .gallery__paire .shot{grid-column:auto}
}

/* ------------------------------------------------------------- 13. LA CHUTE
   Le pied de la démonstration : ce qui tourne ici tourne déjà ailleurs. Ni une
   cinquième carte, ni un nouveau bloc massif. Un filet de métal, une ligne
   d'accent, deux phrases, un pont. La retenue est le sujet.
   Elle ne se pose pas sur du vide : une nappe de lumière remonte du bas de
   section derrière elle (même idiome que le trait rasant du manifeste).       */
.chute{position:relative;margin-block-start:var(--space-l);padding-block-start:var(--space-m);
  display:grid;gap:var(--space-m)}
/* Deux colonnes qui s'alignent sur leur CENTRE, pas sur leur haut : la ligne
   d'accent tient tête au corps au lieu de laisser un demi-écran mort sous elle. */
@media (min-width:960px){
  .chute{grid-template-columns:.92fr 1.08fr;gap:clamp(28px,5vw,76px);align-items:center}
}
/* Le filet : l'arête de métal des pièces du site, pas un trait gris. */
.chute::before{content:"";position:absolute;inset-block-start:0;inset-inline:0;height:1px;
  background:linear-gradient(90deg,var(--metal-edge),var(--line) 44%,transparent)}
/* La nappe de lumière derrière la chute (règle zéro : aucun aplat). Elle est
   posée SOUS la ligne d'accent, côté gauche : l'espace qui reste y devient de
   la lumière, pas du vide. Elle déborde en pleine largeur, comme le trait
   rasant du manifeste, et `overflow-x:clip` de .section la coupe proprement.  */
.chute::after{content:"";position:absolute;z-index:-1;pointer-events:none;
  inset-block:calc(var(--space-m) * -1) calc(var(--space-l) * -2);
  inset-inline-start:-30vw;width:120vw;
  background:
    radial-gradient(30% 78% at 32% 62%, oklch(70% .12 248 / .17), transparent 70%),
    radial-gradient(46% 60% at 26% 104%, oklch(62% .10 252 / .16), transparent 74%)}
.chute__accent{font-family:var(--font-display),"Space Grotesk fallback",sans-serif;
  font-weight:700;font-size:var(--step-2);line-height:1.16;letter-spacing:-.022em;max-width:18ch}
.chute__body{display:grid;gap:var(--space-s);justify-items:start;max-width:58ch}
.chute__p{color:var(--text-soft);line-height:1.6}
/* Le pont vers la suite : même corps, valeur plus haute. Il appelle, il ne crie pas. */
.chute__bridge{color:var(--text);line-height:1.6}

/* -------------------------------------------------------------- 14. COMBIEN */
.price-intro{max-width:56ch;color:var(--text-soft);margin-block-end:var(--space-l)}
.price-block+.price-block{margin-block-start:var(--space-l)}
.price-block__h{font-size:var(--step--1);letter-spacing:.26em;text-transform:uppercase;
  color:var(--text-dim);margin-block-end:var(--space-m);
  display:flex;align-items:center;gap:14px}
.price-block__h::after{content:"";flex:1;height:1px;background:var(--line)}

/* Le détail : une liste sobre, pas une carte de plus. */
.details{margin-block-start:var(--space-m);display:grid;gap:10px;
  font-size:var(--step--1);color:var(--text-soft);max-width:74ch;list-style:none;padding:0}
.details li{position:relative;padding-inline-start:20px}
.details li::before{content:"";position:absolute;inset-inline-start:0;inset-block-start:.62em;
  width:7px;height:1px;background:var(--blue)}
.details strong{color:var(--text);font-weight:600}

/* Les trois postes qui font le prix, en lignes — un rythme de partition, pas
   trois cartes côte à côte : chaque poste se lit comme une entrée de sommaire. */
.plans{display:grid;gap:0;border-block-start:1px solid var(--line)}
.plan{display:grid;gap:var(--space-xs) var(--space-m);padding-block:var(--space-m);
  border-block-end:1px solid var(--line);align-items:start}
@media (min-width:820px){ .plan{grid-template-columns:minmax(210px,.9fr) 2fr} }
.plan__head{display:flex;flex-wrap:wrap;align-items:baseline;gap:12px}
.plan__n{font-family:var(--font-display),"Space Grotesk fallback",sans-serif;font-weight:600;
  font-size:var(--step-1)}
.plan__d{color:var(--text-soft);font-size:var(--step--1);max-width:66ch}

.note{margin-block-start:var(--space-m);font-size:var(--step--1);color:var(--text-dim);max-width:70ch}
/* Dans la chute, la grille porte déjà son `gap` : la marge propre de la note
   s'y ajouterait et décrocherait la micro-copie de son bouton. */
.chute__body .note{margin-block-start:0}

/* Le bandeau Client Fondateur : une plaque de métal gravée, une seule fois. */
.founder{margin-block-start:var(--space-l);position:relative;overflow:hidden;
  border-radius:var(--r-panel);padding:var(--space-m);
  background-image:var(--metal-plate);color:var(--on-metal);
  box-shadow:0 1px 0 var(--metal-edge) inset, 0 24px 60px oklch(8% .03 262 / .5)}
.founder__h{font-family:var(--font-display),"Space Grotesk fallback",sans-serif;font-weight:700;
  font-size:var(--step-2);letter-spacing:-.028em;max-width:20ch;color:var(--on-metal)}
.founder__p{margin-block-start:var(--space-s);max-width:56ch;font-size:var(--step--1);
  color:oklch(28% .04 258)}
.founder::after{content:"";position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(105deg,transparent 40%,oklch(100% 0 0 / .55) 50%,transparent 60%);
  transform:translateX(-130%);animation:plateSheen 7s ease-in-out 2s infinite}

/* ------------------------------------------------------------------ 15. FAQ
   L'avant, relevé à la capture : huit `<details>` nus, sauts secs, ouvert et
   fermé traités pareil, questions au corps d'un sous-titre (5 sur 8 à deux
   lignes sur mobile), réponse à 13,4 px, chevron à 1 100 px de sa question.
   Une liste administrative, pas un objet qu'on manie.

   ⚠ PERF, décidé en connaissance de cause (HOUDINI_PROPERTY_ARSENAL §8 ·
   MOTION_NATIF_PLAYBOOK §6ter.8 → INTERACTIF_NATIF_ARSENAL §12) :
   • Le déploiement anime `block-size` = de la mise en page, pas du compositeur.
     C'est le seul moyen d'animer une hauteur intrinsèque sans mesurer au
     script. Coût borné : au clic seulement, fini en 0,34 s, zéro boucle.
   • 0,34 s n'est pas esthétique : un décalage qui suit une entrée utilisateur
     ne compte dans le CLS que passé 500 ms. Mesuré, CLS comptabilisé 0,0000 sur
     3 tirs. Ne JAMAIS rallonger au-delà de ~0,45 s.
   • Latence d'entrée mesurée (CPU bridé ×4) : 168-192 ms mobile, contre
     232-248 ms avec l'accordéon nu — la transition étale ce que le saut brut
     faisait d'un coup.
   • Le reste est du compositeur (`transform`, `opacity`). Seul l'indicateur
     repeint : 28 × 28 px, une fois par clic — la case gratuite de Houdini §8.
   • `interpolate-size` est posé sur `.faq` et pas sur `:root` : il s'hérite,
     donc le borner au bloc suffit.
   Hors Chromium, `interpolate-size`/`::details-content` sont ignorés : la FAQ
   s'ouvre instantanément, complète. Enrichissement pur, zéro dette. */
.faq{
  interpolate-size:allow-keywords;
  counter-reset:faq;
  /* Seul bloc de la page à traverser les 1 136 px : c'est d'abord ça qui le
     faisait dénoter. Borné, il retrouve la colonne des autres (.price-intro
     56ch, .details 74ch) et l'indicateur revient près de sa question. */
  max-width:74ch;
  /* Le libellé gravé qui annonce le bloc porte désormais l'écart de tête. */
  margin-block-start:0;
  border-block-start:1px solid var(--line);
  /* Le débord de la lumière d'état : elle commence AVANT le texte, sinon la
     ligne allumée a l'air d'une boîte posée dans la colonne. `overflow-x:clip`
     de la .section coupe net, aucun débordement de page possible. */
  --faq-bleed:var(--space-xs)}
/* Ce libellé monte d'un cran quand les trois autres `price-block__h` restent en
   --text-dim : il tombe sur le foyer clair de la nappe `.faq::after`, où
   --text-dim se mesure à 4,07:1, sous le seuil AA. Corrigé, il montre le même
   écart PERÇU que ses frères posés sur un fond plus sombre. La règle est le
   contraste constant, pas le token constant. */
.faq-head{margin-block-start:var(--space-l);color:var(--text-soft)}

.faq details{position:relative;border-block-end:1px solid var(--line);
  padding-inline:var(--faq-bleed);
  margin-inline:calc(var(--faq-bleed) * -1)}

/* Le liseré d'état pousse du haut vers le bas : c'est lui, plus que la hauteur,
   qui donne la sensation d'ouverture. Même vocabulaire que les filets bleus de
   .face__rp et .details li — la FAQ parle la langue de la page. transform pur. */
.faq details::before{content:"";position:absolute;z-index:2;
  inset-block:var(--space-2xs);inset-inline-start:0;width:1.5px;
  background:linear-gradient(var(--blue),transparent);
  transform:scaleY(0);transform-origin:top;
  transition:transform .55s var(--ease)}
.faq details[open]::before{transform:scaleY(1)}

/* La lumière de la ligne active : un dégradé qui s'éteint vers la droite, pas un
   aplat — on n'ajoute pas de boîte sur cette page, on y allume de la matière.
   Teintes issues des tokens (color-mix), aucune couleur neuve dans le moteur.
   Pas de z-index négatif : `details` n'ouvrant pas de contexte d'empilement, un
   `-1` passerait derrière la nappe de `.faq::after` et l'ambiance de section. */
.faq details::after{content:"";position:absolute;inset:0;z-index:0;
  pointer-events:none;opacity:0;
  background:linear-gradient(90deg,
    color-mix(in oklab, var(--line) 62%, transparent) 0%,
    color-mix(in oklab, var(--surface) 55%, transparent) 46%,
    transparent 88%);
  transition:opacity var(--t-fast) var(--ease)}
/* Le survol n'existe qu'au pointeur : sur un écran tactile, `:hover` reste collé
   après le doigt et laisserait deux lignes allumées à la fois. */
@media (hover:hover){ .faq details:hover::after{opacity:.55} }
.faq details[open]::after{opacity:1}

.faq summary{position:relative;z-index:1;cursor:pointer;list-style:none;
  /* Pas de `min-height` : le padding seul donne déjà 67 px de cible au doigt
     (mesuré), quand un plancher creusait 40 px de vide sous les questions d'une
     ligne — `align-items:start` poussait le texte en haut d'une boîte trop
     grande et la réponse semblait décrochée. */
  padding-block:var(--space-s);
  display:grid;grid-template-columns:1fr auto;align-items:start;
  column-gap:var(--space-xs);
  font-family:var(--font-display),"Space Grotesk fallback",sans-serif;font-weight:600;
  /* Un cran plus bas : huit questions au corps d'un sous-titre faisaient un mur,
     première cause du « trop de texte ». La hiérarchie passe désormais par la
     FAMILLE et la VALEUR, pas par la taille — grotesque blanc contre
     géométrique adouci — ce qui laisse monter la réponse à 16 px. */
  font-size:var(--step-0);line-height:1.4;letter-spacing:-.012em;
  /* Les questions fermées reculent d'un cran de valeur, l'ouverte seule est en
     blanc : c'est ce qui fait qu'on voit UNE question mise en scène plutôt que
     huit titres de même poids. Mesuré 7,6:1 sur l'ambiance, très au-dessus AA. */
  color:var(--text-soft);
  transition:color var(--t-fast) var(--ease)}
.faq summary::-webkit-details-marker{display:none}
.faq details[open]>summary{color:var(--white)}
@media (hover:hover){ .faq summary:hover{color:var(--white)} }

/* L'indicateur : un + qui devient un −. Le chevron à 180°, tout le monde l'a ;
   deux traits dans un cartouche à arête vive, c'est la pièce usinée de la DA.
   La barre verticale se rétracte par `background-size`. */
/* Le cartouche se centre sur la PREMIÈRE ligne de la question, pas sur la boîte :
   sur une question de deux lignes, un centrage de boîte le fait glisser entre les
   deux. `1lh` suit tout seul le changement de corps au point de bascule. */
.faq summary::after{content:"";align-self:start;
  margin-block-start:calc((1lh - 28px) * .5);
  inline-size:28px;block-size:28px;border:1px solid var(--line);border-radius:var(--r);
  /* Traits en `currentColor` : le signe se calme ou s'allume par la seule
     couleur du cartouche. Fermé il reste gris — huit accents bleus alignés
     volaient la vedette aux questions, vu à la capture du premier essai. */
  color:var(--text-dim);
  background-image:linear-gradient(currentColor,currentColor),
                   linear-gradient(currentColor,currentColor);
  background-repeat:no-repeat;background-position:center,center;
  background-size:1.5px 12px, 12px 1.5px;
  transition:background-size .34s var(--ease),color var(--t-fast) var(--ease),
             border-color var(--t-fast) var(--ease)}
@media (hover:hover){
  .faq summary:hover::after{border-color:var(--line-strong);color:var(--text-soft)}
}
.faq details[open] summary::after{background-size:1.5px 0, 12px 1.5px;
  color:var(--blue);border-color:var(--blue-deep)}

/* LE DÉPLOIEMENT. `overflow:clip` (et non hidden) pour ne pas créer de conteneur
   de défilement ; `content-visibility` en `allow-discrete` pour que le contenu
   reste peint pendant la FERMETURE, sinon il disparaît d'un coup et le rideau se
   referme sur du vide. */
.faq details::details-content{
  block-size:0;overflow:clip;
  transition:block-size .34s var(--ease), content-visibility .34s allow-discrete}
.faq details[open]::details-content{block-size:auto}
/* Le volet que l'exclusivité referme AU-DESSUS de celui qu'on ouvre s'escamote
   sans glisser : app.js le ferme et rattrape le défilement dans le même souffle,
   pour que la question cliquée ne bouge pas d'un pixel (mesuré sans ça : 186 px
   de saut sous le doigt). Une fermeture animée se battrait avec ce rattrapage. */
.faq details.is-closing-fast::details-content{transition:none}
.faq details.is-closing-fast::before,
.faq details.is-closing-fast::after,
.faq details.is-closing-fast>p{transition:none}

/* La réponse MONTE avec un temps de retard sur le rideau : c'est ce décalage
   qui fait lire un déploiement plutôt qu'un volet qui coulisse. Compositeur.
   ⚠ `@starting-style` n'est pas un raffinement, c'est la condition : volet
   fermé, `::details-content` porte `content-visibility:hidden`, le paragraphe
   n'est PAS rendu — et un élément qui passe de non-rendu à rendu n'a aucun état
   de départ à interpoler. Mesuré au premier essai : opacité à 1,00 dès la 12ᵉ
   milliseconde, le texte apparaissait d'un bloc. */
.faq details>p{
  /* au-dessus de la lumière d'état : un bloc en flux se peint AVANT un pseudo
     positionné à z-index 0, donc sans ceci le dégradé passerait sur le texte. */
  position:relative;z-index:1;
  padding-block-end:var(--space-m);color:var(--text-soft);
  /* 48ch et non 62 : l'unité `ch` est l'avance du « 0 », et celui de Satoshi est
     large — 62ch portaient 89 à 95 caractères par ligne sur grand écran, bien
     au-delà des 90 où l'œil perd sa ligne. Valeur posée sur la mesure comptée
     caractère par caractère (_diag_faq_otaweb.mjs), pas sur la théorie. */
  font-size:var(--step-0);line-height:1.62;max-width:48ch;
  transition:opacity .34s var(--ease) .06s,transform .4s var(--ease) .06s}
.faq details[open]>p{opacity:1;transform:none}
@starting-style{
  .faq details[open]>p{opacity:0;transform:translateY(12px)}
}

/* Le numéro n'apparaît qu'où il y a la place de le porter : sur téléphone la
   largeur est la ressource rare, et une réponse indentée y tomberait sous les
   45 caractères par ligne. Passé 700 px il fait le rythme de partition (même
   geste que le 04 du sur-titre) et remplit la gouttière de la colonne bornée. */
@media (min-width:700px){
  .faq{--faq-num:2.6ch}
  .faq summary{grid-template-columns:var(--faq-num) 1fr auto;
    column-gap:var(--space-s);font-size:var(--step-1)}
  .faq details{counter-increment:faq}
  /* Le numéro se cale sur la première ligne de la question en se donnant
     exactement SA hauteur de ligne : `1lh` ne servirait à rien ici, le pseudo
     redéfinit sa font-size et calculerait sa propre interligne, pas celle de la
     question. */
  .faq summary::before{content:counter(faq,decimal-leading-zero);
    align-self:start;line-height:calc(1.4 * var(--step-1));
    font-size:var(--step--2);letter-spacing:.14em;font-weight:500;
    font-variant-numeric:tabular-nums;color:var(--text-dim);
    transition:color var(--t-fast) var(--ease)}
  .faq details[open] summary::before{color:var(--blue)}
  .faq details>p{padding-inline-start:calc(var(--faq-num) + var(--space-s))}
}

/* ----------------------------------------------------------- 16. RENDEZ-VOUS */
.quote{display:grid;gap:var(--space-l)}
@media (min-width:960px){ .quote{grid-template-columns:.85fr 1.15fr;gap:var(--space-l)} }
.quote__p{color:var(--text-soft);max-width:46ch}
.form{display:grid;gap:var(--space-s)}
.field{display:grid;gap:8px}
.field label{font-size:var(--step--1);letter-spacing:.1em;text-transform:uppercase;color:var(--text-soft)}
.field .req{color:var(--blue)}
.field input,.field select,.field textarea{
  font:inherit;font-size:var(--step-0);color:var(--text);
  background:oklch(11% .04 260 / .6);
  border:1px solid var(--line);border-radius:var(--r);
  padding:14px 16px;min-height:48px;width:100%;
  transition:border-color var(--t-fast) var(--ease),background var(--t-fast) var(--ease)}
.field textarea{min-height:140px;resize:vertical;line-height:1.55}
.field select{appearance:none;
  background-image:linear-gradient(45deg,transparent 50%,var(--blue) 50%),
    linear-gradient(135deg,var(--blue) 50%,transparent 50%);
  background-position:calc(100% - 22px) 22px,calc(100% - 16px) 22px;
  background-size:6px 6px,6px 6px;background-repeat:no-repeat}
.field input:focus,.field select:focus,.field textarea:focus{
  border-color:var(--blue);background:oklch(13% .045 260 / .8);outline:none}
.field input:focus-visible,.field select:focus-visible,.field textarea:focus-visible{
  outline:2px solid var(--blue);outline-offset:2px}
.field .err{font-size:var(--step--2);color:oklch(74% .16 30);min-height:1.1em}
.field.is-bad input,.field.is-bad select,.field.is-bad textarea{border-color:oklch(64% .17 30)}
.form__foot{display:flex;flex-wrap:wrap;gap:var(--space-s) var(--space-m);align-items:center}
.form__re{font-size:var(--step--1);color:var(--text-soft);max-width:34ch}
.form__msg{font-size:var(--step-0);padding:14px 16px;border-radius:var(--r);
  border:1px solid var(--line);background:oklch(19% .05 258 / .6)}
.form__msg[hidden]{display:none}
.form__msg.is-ok{border-color:var(--blue);color:var(--text)}
.form__msg.is-ko{border-color:oklch(64% .17 30);color:var(--text)}
/* Neutre : ni faute, ni succès. Un simple renseignement, filet de métal. */
.form__msg.is-info{border-color:var(--line-strong);color:var(--text-soft)}
.privacy{font-size:var(--step--2);color:var(--text-dim);max-width:60ch}
.contacts{display:grid;gap:12px;margin-block-start:var(--space-m);
  padding-block-start:var(--space-m);border-block-start:1px solid var(--line);
  font-size:var(--step--1);color:var(--text-soft)}

/* ---------------------------------------------------------------- 17. FOOTER */
.site-foot{position:relative;overflow:clip;
  border-block-start:1px solid var(--line);padding-block:var(--space-l) var(--space-m);
  background:linear-gradient(180deg,transparent,oklch(9% .035 262 / .8))}
.foot__top{display:grid;gap:var(--space-l);margin-block-end:var(--space-l)}
@media (min-width:820px){ .foot__top{grid-template-columns:1.4fr 1fr 1fr 1fr} }
.foot__base{font-size:var(--step--1);color:var(--text-soft);max-width:38ch;margin-block-start:var(--space-s)}
.foot__proof{font-size:var(--step--2);color:var(--text-dim);margin-block-start:var(--space-s);max-width:34ch}
.foot__col h2{font-size:var(--step--1);letter-spacing:.22em;text-transform:uppercase;
  color:var(--text-dim);font-weight:500;margin-block-end:var(--space-s)}
/* Cibles tactiles : le lien porte 4 px de coussin haut et bas (WCAG 2.5.8,
   24 px minimum). L'écart de la liste est réduit d'autant : l'œil ne voit
   aucune différence, le doigt oui.                                           */
.foot__col ul{list-style:none;margin:0;padding:0;display:grid;gap:2px}
.foot__col a{display:inline-block;padding-block:4px;min-width:26px;
  font-size:var(--step--1);color:var(--text-soft);text-decoration:none;
  transition:color var(--t-fast) var(--ease)}
.foot__col a:hover{color:var(--text)}
.foot__bar{display:flex;flex-wrap:wrap;gap:var(--space-s) var(--space-m);justify-content:space-between;
  padding-block-start:var(--space-m);border-block-start:1px solid var(--line);
  font-size:var(--step--2);color:var(--text-dim)}

/* -------------------------------------------------------------- 18. REVEALS */
.js .rv{opacity:0;transform:translateY(28px);transition:opacity .9s var(--ease),transform .95s var(--ease)}
.rv.in{opacity:1;transform:none}
.js .rv-stagger>*{opacity:0;transform:translateY(22px);transition:opacity 1s var(--ease),transform 1s var(--ease)}
.js .rv-stagger.in>*{opacity:1;transform:none;transition-delay:calc(var(--i,0) * .08s)}
/* Une page imprimée ne défile pas : tout ce qui n'a pas encore été révélé
   doit s'imprimer, sinon la feuille sort blanche. */
@media print{
  .js .rv,.js .rv-stagger>*{opacity:1;transform:none;transition:none}
  .atmos{display:none}
}

/* -------------------------------------------- 19. PAGE LÉGALE (gabarit sobre) */
.legal-body{max-width:74ch}
.legal-body h1{font-size:var(--step-4);max-width:18ch;margin-block-end:var(--space-l)}
.legal-body h2{font-size:var(--step-2);margin-block:var(--space-l) var(--space-s)}
.legal-body h2:first-of-type{margin-block-start:0}
.legal-body h3{font-size:var(--step-1);margin-block:var(--space-m) var(--space-2xs);color:var(--text)}
.legal-body b{color:var(--text);font-weight:600}
.legal-body p,.legal-body li{color:var(--text-soft);font-size:var(--step-0)}
.legal-body ul{padding-inline-start:20px;display:grid;gap:8px}
.legal-body a{color:var(--blue)}

/* ---------------------------------------- 20. MOUVEMENT RÉDUIT
   Le mouvement s'arrête ; la matière reste. Le métal garde son dégradé (donc
   son texte reste visible), l'anneau garde sa lumière, les reveals s'ouvrent. */
@media (prefers-reduced-motion:reduce){
  .atmos__glow,.emblem::before,
  .hero__hint::before{animation:none}
  /* Les fonds ne bougent plus, mais ils sont TOUS là : le Ken-Burns s'arrête sur
     une valeur haute de sa course, jamais sur rien. */
  .amb img{animation:none;transform:scale(1.05)}
  .hero__sky::before,.hero__sheen{animation:none}
  .hero__sky::before{transform:translate3d(120%,0,0) rotate(8deg)}
  /* LA PIÈCE NE TOURNE PLUS, ELLE POSE. Figée à seize degrés, elle montre son
     épaisseur et une amorce de reflet : on voit toujours un objet de métal, pas
     une image à plat. C'est la version immobile, pas la version amputée. */
  .emblem.is-turning .coin{animation:none;transform:rotateY(-16deg)}
  .emblem.is-turning .coin__spec{animation:none;opacity:.34;background-position:58% 0}
  .emblem.is-turning .coin__face--back .coin__spec{opacity:0}
  .emblem.is-turning .coin__edge{animation:none;opacity:.26}
  .metal,.btn--metal::after,.founder::after{animation:none}
  .btn--metal::after,.founder::after{opacity:.35;transform:translateX(0)}
  .metal{background-position:38% 0,0 0}
  .site-head__rail{animation:none;transform:scaleX(0)}
  .js .split .line>span{transform:none}
  .js .rv,.js .rv-stagger>*{opacity:1;transform:none;transition:none}
  .demo:hover{transform:none}
  /* Le rail de la galerie ne glisse plus tout seul sous le doigt : le
     défilement reste possible, il n'est simplement plus animé. */
  .gallery__grid{scroll-behavior:auto}
  .shot img{transition:none}
  /* LA FAQ : le déploiement devient instantané, les ÉTATS restent tous là.
     Le liseré bleu, la lumière de la ligne active, le + qui passe au −, le
     numéro qui vire au bleu : rien de tout cela n'est du mouvement, ce sont
     des repères — les couper laisserait un accordéon muet sur ce qui est
     ouvert. On ne coupe que le glissement.
     Le paragraphe est remis d'aplomb explicitement : sa position de départ
     (opacity 0 / translateY) n'est plus rattrapée par aucune transition. */
  .faq details::before{transition:none}
  .faq details::after{transition:none}
  .faq summary,.faq summary::after,.faq summary::before{transition:none}
  .faq details::details-content{transition:none}
  .faq details>p{transition:none}
  .faq details[open]>p{opacity:1;transform:none;transition-delay:0s}
}

/* ============================================================================
   V6 — LE SITE ANNONCE AU LIEU D'ARGUMENTER
   ----------------------------------------------------------------------------
   Trois ajouts, et un seul principe : ce que le texte a cessé de démontrer,
   la composition doit le dire. Les promesses deviennent une ligne qu'on lit
   d'un coup d'œil, les familles reçoivent une image, et le joint direct
   (WhatsApp) reste sous le pouce pendant tout le défilement.
   ============================================================================ */

/* --- LES TROIS PROMESSES (premier écran) ---------------------------------
   Une ligne par famille : le métier à gauche, la promesse à droite. Le filet
   supérieur les relie en tableau ; c'est ce qui les fait lire comme une grille
   de trois et non comme trois phrases empilées. En mobile la ligne se casse en
   deux niveaux plutôt que de comprimer la promesse, qui est le mot qui porte. */
.promesses{list-style:none;margin:var(--space-m) 0 0;padding:0;
  border-block-start:1px solid var(--line)}
.promesses li{display:flex;flex-wrap:wrap;gap:2px var(--space-s);
  align-items:baseline;justify-content:space-between;
  padding-block:var(--space-xs);border-block-end:1px solid var(--line)}
.promesses b{font-family:var(--font-display),"Space Grotesk fallback",sans-serif;
  font-weight:500;font-size:var(--step--1);letter-spacing:.06em;
  text-transform:uppercase;color:var(--text-dim)}
.promesses span{font-family:var(--font-display),"Space Grotesk fallback",sans-serif;
  font-weight:700;font-size:var(--step-1);color:var(--text);
  text-align:end;text-wrap:balance}
/* La première promesse est celle du H1 : elle porte le reflet, les deux autres
   restent blanches. Trois mots métallisés côte à côte annuleraient l'accent. */
.promesses li:first-child span{background:var(--metal-face);
  -webkit-background-clip:text;background-clip:text;color:transparent}
.promesses__note{margin-block-start:var(--space-2xs);
  font-size:var(--step--1);color:var(--text-dim)}

/* --- LES FAMILLES, EN IMAGES ---------------------------------------------
   L'image passe AVANT le filet de tête : la carte s'ouvre sur elle, et le
   filet devient le socle du texte. Le ratio est déclaré en CSS en plus des
   attributs HTML pour que la place soit réservée même si le fichier tarde. */
.family__ph{margin:0 0 var(--space-s);overflow:hidden;border-radius:var(--r-photo);
  background:var(--deep-2);border:1px solid var(--line)}
.family__ph img{display:block;width:100%;height:auto;aspect-ratio:4/5;
  object-fit:cover;
  transition:transform var(--t-std) var(--ease),filter var(--t-std) var(--ease)}
.family:hover .family__ph img{transform:scale(1.03);filter:brightness(1.12)}
@media (prefers-reduced-motion:reduce){
  .family__ph img{transition:none}
  .family:hover .family__ph img{transform:none}
}
/* La promesse de la carte : le seul endroit de la section où le bleu de marque
   sert de couleur de texte. Elle répète mot pour mot la ligne du premier écran,
   c'est voulu : une promesse qui change de formulation cesse d'être une promesse. */
.family__promise{font-family:var(--font-display),"Space Grotesk fallback",sans-serif;
  font-weight:700;font-size:var(--step-1);color:var(--blue-lite);
  margin-block:calc(var(--space-s) * -.5) var(--space-xs)}

/* --- LE JOINT DIRECT (WhatsApp) ------------------------------------------
   Vert de la marque WhatsApp et non bleu Otaweb : cette bulle n'est pas un
   ornement, c'est un code que le visiteur reconnaît sans le lire. Le liseré
   sombre et l'ombre froide la raccrochent à la page.
   `inset-block-end` en unités dvh-safe : sur iOS la barre d'adresse mange le
   bas de la fenêtre, et une bulle collée à 0 devient inatteignable. */
.wa{position:fixed;z-index:60;inset-inline-end:clamp(14px,3vw,26px);
  inset-block-end:calc(clamp(14px,3vw,26px) + env(safe-area-inset-bottom,0px));
  display:grid;place-items:center;width:56px;height:56px;border-radius:50%;
  background:#25d366;color:#0b1220;
  border:1px solid oklch(100% 0 0 / .28);
  box-shadow:0 10px 30px oklch(8% .03 262 / .55), 0 2px 6px oklch(8% .03 262 / .4);
  transition:transform var(--t-fast) var(--ease),opacity var(--t-fast) var(--ease),
             visibility var(--t-fast) var(--ease)}
.wa:hover{transform:scale(1.07)}
.wa:focus-visible{outline:2px solid var(--white);outline-offset:3px}
.wa__ico{display:block}
/* Effacée quand le formulaire est à l'écran : sur mobile elle recouvrait le
   bouton d'envoi, qui est exactement le geste qu'on ne veut pas gêner. */
.wa[data-repli]{opacity:0;visibility:hidden;transform:scale(.8) translateY(8px)}
@media (prefers-reduced-motion:reduce){ .wa{transition:none} }
@media print{ .wa{display:none} }

/* --- V6 · correctifs mesurés à l'image (mobile Pixel 7) -------------------- */

/* La promesse la plus longue (« Sur n'importe quelle plateforme ») passait à la
   ligne et se retrouvait alignée à gauche pendant que les deux autres restaient
   à droite : la grille de trois se lisait de travers. Sous 620 px on empile
   franchement, label au-dessus, promesse en dessous, tout aligné à gauche. */
@media (max-width:619px){
  .promesses li{display:block;padding-block:var(--space-s)}
  .promesses span{display:block;text-align:start;margin-block-start:2px}
}

/* En pleine largeur, un portrait 4/5 fait une image de près de mille pixels de
   haut : la section devient un diaporama qu'on fait défiler au lieu d'une page
   qu'on lit. Le portrait est juste en colonne étroite (les trois familles côte
   à côte), pas en pleine largeur. */
.family__ph img{aspect-ratio:3/2}
@media (min-width:900px){ .family__ph img{aspect-ratio:4/5} }

/* La bulle passait par-dessus le texte des cartes en défilant. Elle reste
   atteignable au pouce mais cesse d'être le premier objet de l'écran : un cran
   plus petite, et légèrement en retrait tant qu'on ne la vise pas. */
.wa{width:52px;height:52px;opacity:.92}
.wa:hover,.wa:focus-visible{opacity:1}
