/* ============================================================================
   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
   Une matière par section, toutes passées par le MÊME étalonnage (voir
   _ambiances_otaweb.py) : au long-scroll on traverse des pièces d'un seul
   bâtiment, pas cinq images collées.
   Trois règles tenues ici :
   · décoratives → un ::before, jamais un <img> : rien n'entre dans l'arbre
     d'accessibilité, rien n'est annoncé au lecteur d'écran ;
   · jamais sous le texte à pleine force → chaque masque confine la matière à
     la zone que le texte laisse libre (le tiers droit en desktop, le haut ou
     le pied en mobile) ;
   · une ambiance se sent, elle ne se regarde pas → `--amb-o` reste bas.
   `background-attachment` n'est pas utilisé : `fixed` coûte un repeint plein
   cadre à chaque frame de scroll, c'est le budget INP qui paie.              */
.section[data-amb]::before{
  content:"";position:absolute;inset:0;z-index:-1;pointer-events:none;
  background-image:var(--amb);
  /* `cover` par défaut, mais réglable : une matière abstraite supporte d'être
     agrandie, un motif STRUCTURÉ non. Sur une section haute et étroite, `cover`
     agrandit dix fois — le réseau du repli n'y était plus qu'une tache floue.
     Quand le motif doit garder son échelle, on la lui donne. */
  background-size:var(--amb-size,cover);
  background-position:var(--amb-pos,50% 50%);
  background-repeat:no-repeat;
  opacity:var(--amb-o,.55);
  -webkit-mask-image:var(--amb-mask,linear-gradient(180deg,#000,#000));
          mask-image:var(--amb-mask,linear-gradient(180deg,#000,#000));
}
/* Le rayon qui perce : « le meilleur du web ne devrait pas être réservé ». */
#pourquoi{--amb:url("img/amb-faisceau.webp");--amb-o:.85;--amb-pos:76% 12%;
  --amb-mask:radial-gradient(78% 82% at 80% 24%, #000 4%, transparent 74%)}
/* La vitesse tenue : « l'IA accélère, la main décide ». */
#comment{--amb:url("img/amb-trainees.webp");--amb-o:.62;--amb-pos:62% 8%;
  --amb-mask:radial-gradient(72% 46% at 76% 14%, #000 2%, transparent 72%)}
/* La matière brute : trois familles de projets, l'acier dont c'est fait. */
#ce-quon-fait{--amb:url("img/amb-acier.webp");--amb-o:.7;--amb-pos:50% 64%;
  --amb-mask:linear-gradient(180deg, transparent 10%, #000 52%, #000 82%, transparent 100%)}
/* La brume qui se lève : les prix, sans avoir à les demander. */
#prix{--amb:url("img/amb-clarte.webp");--amb-o:.5;--amb-pos:50% 96%;
  --amb-mask:linear-gradient(180deg, transparent 42%, #000 100%)}
/* Le foyer : la lumière posée au sol, là où on vous accueille. */
#devis{--amb:url("img/amb-foyer.webp");--amb-o:.62;--amb-pos:50% 100%;
  --amb-mask:radial-gradient(96% 70% at 50% 100%, #000 6%, transparent 78%)}
/* Le pied du puits : la même plaque d'acier, retournée et presque éteinte.
   Assez loin de la section 03 pour ne pas se lire comme une redite.          */
.site-foot{--amb:url("img/amb-acier.webp");--amb-o:.34;--amb-pos:50% 8%;
  --amb-mask:linear-gradient(180deg, #000 0%, transparent 62%)}
/* Le pied porte DÉJÀ un aplat dégradé qui creuse le puits. Un ::before en
   z-index:-1 passerait dessous et ne se verrait pas : la matière se pose donc
   par-dessus (z-index:0) et le contenu est relevé d'un cran. */
.site-foot::after{
  content:"";position:absolute;inset:0;z-index:0;pointer-events:none;
  background-image:var(--amb);background-size:cover;background-position:var(--amb-pos);
  background-repeat:no-repeat;opacity:var(--amb-o);
  transform:scaleY(-1);
  -webkit-mask-image:var(--amb-mask);mask-image:var(--amb-mask)}
.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 bande démarre SOUS le sur-titre : à 4 % le point le plus clair du faisceau
     tombait derrière « 01 Pourquoi » et le ramenait à 3,16:1 (seuil 4,5, mesuré). */
  #pourquoi{--amb:url("img/amb-faisceau-m.webp");--amb-o:.78;--amb-pos:42% 4%;
    --amb-mask:linear-gradient(180deg,transparent 9%,#000 24%,transparent 62%)}
  #comment{--amb:url("img/amb-trainees-m.webp");--amb-o:.7;--amb-pos:50% 26%;
    --amb-mask:linear-gradient(180deg,transparent 6%,#000 26%,transparent 52%)}
  #ce-quon-fait{--amb:url("img/amb-acier-m.webp");--amb-o:.75;--amb-pos:50% 72%;
    --amb-mask:linear-gradient(180deg,transparent 44%,#000 76%,transparent 100%)}
  #prix{--amb:url("img/amb-clarte-m.webp");--amb-o:.55;--amb-pos:50% 100%;
    --amb-mask:linear-gradient(180deg,transparent 56%,#000 100%)}
  #devis{--amb:url("img/amb-foyer-m.webp");--amb-o:.7;--amb-pos:50% 100%;
    --amb-mask:radial-gradient(150% 52% at 50% 100%, #000 8%, transparent 82%)}
  .site-foot{--amb:url("img/amb-acier-m.webp");--amb-o:.4}
}

/* 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{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}

@media (max-width:900px){
  .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}
}
@media (min-width:901px){ .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);

  /* LE CIEL — le décollage, seule section qui était restée sur un aplat.
     Même vocabulaire que les ambiances (`--sky-*` répond à `--amb-*`), deux
     différences assumées : elle a un SUJET (donc elle se regarde, là où une
     ambiance se sent seulement), et elle est portée par un <img> parce qu'elle
     est le LCP.

     Le contraste n'est PAS confié à un masque. Un masque ne fait que doser la
     photo : sous un point clair il laisse passer un point clair, et le texte
     paie. Ici c'est un SCRIM — un aplat de la couleur du fond posé par-dessus
     l'image, sous le texte. Le support du texte redevient donc `--deep-1`, une
     valeur connue, quoi que fasse la photo dessous. Deuxième métier du scrim :
     sa passe verticale fond le bas de l'image dans la page, donc la photo n'a
     pas de bord — aucune couture entre le hero et la section suivante. */
  --sky-o:1;
  /* cadré bas : c'est la BASE incandescente et le sol illuminé qui disent
     « décollage ». Cadré au centre, il ne restait qu'un trait dans le ciel —
     une texture, pas un sujet (mesuré à la capture, première passe). */
  --sky-pos:44% 64%;
  --sky-zoom:1.28;
  --sky-origin:0% 100%;
  --sky-scrim:
    /* La colonne de texte, éteinte de la gauche vers le couloir central.
       Le scrim s'étend jusqu'à 70 % et non 62 % : la colonne de texte est
       passée de 1,25fr à 1,35fr, et le titre en deux lignes a fait DESCENDRE
       l'appel à l'action de ~70 px, en plein dans la traînée lumineuse. Mesuré
       à 3,94:1 sur « Mesurer par vous-même » pour un seuil de 4,50. Le scrim
       suit la colonne qu'il protège : c'est lui qui décide du contraste, pas
       la photo. */
    linear-gradient(96deg,
      var(--deep-1) 2%,
      oklch(15% .05 258 / .90) 26%,
      oklch(15% .05 258 / .56) 46%,
      transparent 70%),
    /* le haut sous l'en-tête, et le raccord du bas avec la page */
    /* Le pied du hero se referme plus tôt et plus franchement : le lien
       d'accent y était mesuré à 4,54:1 pour un seuil de 4,50 — juste au-dessus,
       donc pas tenable. Un gate ne se passe pas à quatre centièmes près. */
    linear-gradient(180deg,
      oklch(15% .05 258 / .66) 0%,
      transparent 22%,
      transparent 58%,
      oklch(15% .05 258 / .58) 82%,
      var(--deep-1) 100%);
}
.hero__sky{position:absolute;inset:0;z-index:-1;pointer-events:none;overflow:hidden}
/* `<picture>` est une boîte INLINE sans hauteur propre : sans ceci, le `height:100%`
   de l'image se résout contre elle, retombe sur `auto`, et `object-fit:cover` ne
   s'applique jamais — l'image se pose à sa hauteur intrinsèque et son horizon
   dessine une couture en travers du hero (défaut mesuré à la capture). */
.hero__sky picture{display:contents}
/* Le zoom est un second réglage, distinct du cadrage. En desktop l'image
   couvre déjà le hero à 94 % de sa hauteur : `object-position` n'a plus que
   60 px de latitude et ne déplace donc quasiment rien (mesuré). C'est
   l'agrandissement — pris depuis le coin BAS GAUCHE — qui fait deux choses à
   la fois : il grossit l'arc, et il le pousse vers le couloir libre, à droite
   du texte. `scale` est purement compositeur : le zoom ne coûte pas un repeint. */
.hero__sky img{width:100%;height:100%;object-fit:cover;
  object-position:var(--sky-pos);opacity:var(--sky-o);
  transform:scale(var(--sky-zoom,1));transform-origin:var(--sky-origin,50% 50%)}
.hero__sky::after{content:"";position:absolute;inset:0;background:var(--sky-scrim)}
/* MOBILE : le cadrage portrait, où l'arc traverse toute la hauteur. Le texte
   n'a plus de « tiers libre » à côté de lui, il occupe toute la largeur : le
   scrim devient donc HORIZONTAL (il couche la photo sous la bande du titre)
   et on cale l'arc à droite pour qu'il monte dans la marge, pas dans les mots. */
@media (max-width:980px){
  /* Le scrim tient plus longtemps qu'en desktop, et jusqu'en bas : sur un
     écran étroit la traînée traverse la colonne de texte au lieu de la longer,
     et elle passait sous le lien d'accent (2,99:1) comme sous le repère de bas
     de hero (2,65:1). Elle reste largement lisible — c'est l'objet le plus
     lumineux de l'image — mais elle cesse de disputer le texte. */
  .hero{--sky-o:.76;--sky-pos:72% 42%;
    --sky-scrim:
      linear-gradient(180deg,
        oklch(15% .05 258 / .76) 0%,
        oklch(15% .05 258 / .86) 26%,
        oklch(15% .05 258 / .74) 52%,
        oklch(15% .05 258 / .60) 74%,
        oklch(15% .05 258 / .72) 100%),
      linear-gradient(90deg, oklch(15% .05 258 / .62), transparent 66%);
  }
}
/* 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)}
@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}
}
.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 (monogramme calligraphié, chromé, dans son cercle).
   La pièce est gardée TELLE QUELLE : le chrome reflète le bleu roi de la marque et
   ces reflets sont l'identité, les gommer casserait la pièce. Seul le fond bleu
   s'éteint, et il s'éteint ICI, par `mask-image` : le WebP reste opaque, donc
   léger (62 Ko en 860 px contre 95 Ko pour un alpha peint plus petit), et le
   fondu devient indépendant de la résolution. */
.emblem{position:relative;width:min(100%,440px);aspect-ratio:1;margin-inline:auto;isolation:isolate}
.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%)}
/* 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}}
@keyframes ringSpin{to{transform:rotate(360deg)}}
@media (max-width:980px){ .emblem{width:min(74%,320px)} }

/* --------------------------------------------------- 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. */

/* --------------------------------------------------------- 10. COMMENT
   Liste éditoriale verticale à décrochage : jamais trois cartes égales.       */
.principles{display:grid;gap:var(--space-l);margin-block-start:var(--space-l)}
.principle{display:grid;grid-template-columns:auto 1fr;gap:clamp(18px,3.5vw,52px);align-items:start}
.principle:nth-child(2){margin-inline-start:clamp(0px,7vw,104px)}
.principle:nth-child(3){margin-inline-start:clamp(0px,3vw,46px)}
.principle__n{font-family:var(--font-display),"Space Grotesk fallback",sans-serif;
  font-size:var(--step-3);font-weight:700;line-height:1;font-variant-numeric:tabular-nums;
  color:transparent;background-image:var(--metal-face);
  -webkit-background-clip:text;background-clip:text}
.principle__h{font-size:var(--step-2);margin-block-end:var(--space-xs)}
.principle__p{max-width:56ch;color:var(--text-soft)}
.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, signaux qui courent le long des arêtes. Peint
   en canvas 2D, donc zéro octet d'image et une matière qui n'a pas de fin de
   boucle perceptible.
   Le canvas est hors flux (position:absolute) : il ne peut pas décaler la mise
   en page, CLS nul par construction. Il monte en opacité une fois armé, pour
   qu'aucune apparition brutale ne coupe la lecture.                           */
/* Le réseau habite le HAUT de la section — là où le texte affirme que c'est
   vivant — puis s'éteint : plus bas, les cartes de démonstration ont leur
   propre matière et deux matières superposées, c'est de l'empilement. Borner
   la hauteur sert aussi le budget : la section fait plus de 2 000 px de haut,
   peindre tout cela à chaque image 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);
  /* Même discipline que les cinq ambiances : la matière se confine à la zone
     que le texte laisse libre. Ici le titre et le chapô tiennent la moitié
     gauche, le réseau vit donc à DROITE et se fond de tous les côtés — un
     seul dégradé radial fait le cadrage et l'extinction du bas, sans avoir à
     composer deux masques (dont l'addition donnerait l'union, pas
     l'intersection : l'inverse de ce qu'on veut). */
  /* Foyer décalé à 80 % : le réseau garde sa surface, il cesse seulement de
     déborder sous la colonne de texte. Le contraste, lui, n'est pas confié au
     masque (un masque dose la photo, il ne garantit rien) mais au SCRIM
     ci-dessous — même doctrine que le ciel du hero. */
  -webkit-mask-image:radial-gradient(70% 92% at 80% 38%, #000 8%, transparent 78%);
          mask-image:radial-gradient(70% 92% at 80% 38%, #000 8%, transparent 78%)}
.plexus.is-on{opacity:.6}
/* 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. Il se pose en ::after de la section — à z-index
   égal l'ordre de peinture suit l'ordre du document, donc il couvre le canvas
   ET l'image de repli (mouvement réduit), et il reste sous le texte.
   En desktop il est HORIZONTAL : il éteint la colonne de gauche et laisse le
   couloir droit, là où le réseau vit, intact.
   Mesuré sans lui, en trois passes : le chapô de section tombait entre 3,41:1
   et 4,38:1 (seuil 4,50) quand un halo de nœud passait dessous — un défaut
   intermittent, donc invisible à un contrôle en un seul tir. */
#demos::after{content:"";position:absolute;z-index:-1;pointer-events:none;
  inset-block-start:0;inset-inline:0;height:min(100%, 880px);
  background:linear-gradient(94deg,
    oklch(15% .05 258 / .60) 0%,
    oklch(15% .05 258 / .44) 38%,
    transparent 66%)}
/* En mobile le texte occupe toute la largeur : il n'y a plus de « côté libre ».
   La respiration devient verticale — le réseau prend la bande haute, au-dessus
   et autour du titre, puis s'éteint avant le chapô. */
@media (max-width:760px){
  .plexus{height:min(100%, 680px);
    -webkit-mask-image:radial-gradient(128% 54% at 66% 12%, #000 4%, transparent 82%);
            mask-image:radial-gradient(128% 54% at 66% 12%, #000 4%, transparent 82%)}
  /* ≥ .58 : le repli statique mobile est à .62 — l'animé ne doit pas être
     MOINS présent que son propre repli (paradoxe relevé à l'audit 08/08). */
  .plexus.is-on{opacity:.58}

  /* Le scrim bascule à la VERTICALE. En mobile il n'existe aucune zone du haut
     de section sans texte : sur-titre, H2 et chapô occupent toute la largeur,
     et le réseau passe forcément dessous. Il n'y a donc plus de colonne à
     éteindre, mais une bande — franche sous le sur-titre (le texte le plus
     petit et le plus pâle), presque nulle au niveau des cartes.
     Mesuré sans lui, en trois passes : « La démonstration » tombait à 3,31:1
     (seuil 4,50) sous un halo de nœud, et le mot métallisé « parole » à 2,90:1
     (seuil 3,00) sur le REPLI statique — celui que voit quelqu'un en mouvement
     réduit, et que le contrôle en régime normal ne regarde jamais. */
  #demos::after{height:min(100%, 680px);
    background:linear-gradient(180deg,
      oklch(15% .05 258 / .62) 0%,
      oklch(15% .05 258 / .50) 32%,
      oklch(15% .05 258 / .26) 64%,
      transparent 94%)}
}
/* 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 occupe la MÊME zone que le canvas — la bande haute — et garde
   l'échelle de son maillage grâce à `--amb-size`. C'est la condition pour que
   quelqu'un qui a désactivé les animations voie un réseau, et non le grain
   flou d'une image agrandie dix fois. */
.plexus-repli #demos{--amb:url("img/plexus.webp");--amb-o:.6;
  --amb-size:132% auto;--amb-pos:88% 2%;
  --amb-mask:radial-gradient(76% 92% at 76% 20%, #000 8%, transparent 74%)}
@media (max-width:760px){
  .plexus-repli #demos{--amb:url("img/plexus-m.webp");--amb-o:.62;
    --amb-size:215% auto;--amb-pos:64% 1%;
    --amb-mask:radial-gradient(128% 30% at 66% 7%, #000 6%, transparent 82%)}
}

/* ---------------------------------------------------------------- 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 » : cinq pages, dans leur cadre de navigateur, avec leur
   é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)}

/* 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). */
@media (max-width:719px){
  .shot{display:grid;grid-template-rows:1fr auto}
  .shot picture{align-self:end}
}
.shot img{display:block;width:100%;height:auto;border-radius:var(--r-photo);
  transition:transform var(--t-fast) var(--ease)}
.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), trois plus petites en dessous (les maquettes) : la hiérarchie de
   la grille dit la même chose que les légendes. */
@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}
  .shot:nth-child(-n+2){grid-column:span 3}
  .shot:nth-child(n+3){grid-column:span 2}
  .shot:hover img{transform:translateY(-4px)}
  .gallery__hint{display:none}
}

/* ------------------------------------------------------------- 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 le devis : même corps, valeur plus haute. Il appelle, il ne crie pas. */
.chute__bridge{color:var(--text);line-height:1.6}

/* ----------------------------------------------------------------- 14. PRIX */
.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)}

/* Bloc A : trois paliers de création. Le palier recommandé est relevé.
   --badge-row : la bande réservée à la pastille, en HAUT de la carte relevée.
   La pastille en sortant du flux, les trois titres et les trois prix retombent
   sur la même ligne d'œil ; c'est la carte entière qui monte, pas son texte.  */
.tiers{display:grid;gap:var(--space-s);--badge-h:32px;--badge-row:calc(var(--badge-h) + var(--space-s))}
@media (min-width:880px){ .tiers{grid-template-columns:repeat(3,1fr);align-items:start} }
.tier{position:relative;padding:var(--space-m);border-radius:var(--r-panel);
  border:1px solid var(--line);background:oklch(19% .05 258 / .5)}
/* Même mécanique que la carte de démo : fond propre transparent + overflow
   qui borne le disque conique, sinon il déborde sur toute la section. */
.tier--reco{background:none;border-color:transparent;isolation:isolate;overflow:clip;
  padding-block-start:calc(var(--space-m) + var(--badge-row))}
@media (min-width:880px){ .tier--reco{margin-block-start:calc(-1 * var(--badge-row))} }
/* La bordure conique qui tourne : un seul élément de la page la porte. */
.tier--reco::before{content:"";position:absolute;z-index:0;inset:-180%;
  background:conic-gradient(from 0deg,var(--line) 0deg,var(--blue) 22deg,
    oklch(100% 0 0 / .85) 38deg,var(--line) 60deg,var(--line) 360deg);
  animation:ringSpin 9s linear infinite;will-change:transform}
.tier--reco::after{content:"";position:absolute;z-index:0;inset:1px;border-radius:inherit;
  background:linear-gradient(180deg, oklch(24% .058 256), oklch(18% .052 258))}
.tier--reco>*{position:relative;z-index:1}
/* Hors du flux, dans la bande réservée : la pastille ne pousse plus le titre
   ni le prix vers le bas. La carte est bornée (overflow), donc la pastille
   reste DANS la carte, jamais à cheval sur son bord.                          */
.tier--reco>.tier__badge{position:absolute;z-index:1;
  inset-block-start:var(--space-m);inset-inline-start:var(--space-m);
  display:inline-flex;align-items:center;height:var(--badge-h);margin:0;
  font-size:var(--step--2);letter-spacing:.2em;text-transform:uppercase;line-height:1;
  padding-inline:12px;border-radius:var(--r);background-image:var(--metal-plate);color:var(--on-metal);
  font-weight:600}
.tier__n{font-family:var(--font-display),"Space Grotesk fallback",sans-serif;font-weight:600;
  font-size:var(--step-1);letter-spacing:-.01em}
.tier__price{font-family:var(--font-display),"Space Grotesk fallback",sans-serif;font-weight:700;
  font-size:var(--step-3);line-height:1;letter-spacing:-.03em;margin-block:10px var(--space-s);
  font-variant-numeric:tabular-nums lining-nums;
  color:transparent;background-image:var(--metal-face);
  -webkit-background-clip:text;background-clip:text}
.tier__d{font-size:var(--step--1);color:var(--text-soft)}

/* 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}

/* Bloc B : l'abonnement, en lignes — un rythme différent des paliers du haut. */
.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__price{font-family:var(--font-display),"Space Grotesk fallback",sans-serif;font-weight:700;
  font-size:var(--step-2);letter-spacing:-.03em;font-variant-numeric:tabular-nums lining-nums;
  color:transparent;background-image:var(--metal-face);
  -webkit-background-clip:text;background-clip:text}
.plan__tag{font-size:var(--step--2);letter-spacing:.18em;text-transform:uppercase;color:var(--text-dim)}
.plan__d{color:var(--text-soft);font-size:var(--step--1);max-width:66ch}

.others{display:grid;gap:var(--space-s);margin-block-start:var(--space-m)}
.other{display:flex;flex-wrap:wrap;gap:6px 16px;align-items:baseline;
  padding-block-end:var(--space-s);border-block-end:1px solid var(--line)}
.other__n{font-family:var(--font-display),"Space Grotesk fallback",sans-serif;font-weight:600;
  font-size:var(--step-0)}
.other__d{color:var(--text-soft);font-size:var(--step--1);font-variant-numeric:tabular-nums}

.note{margin-block-start:var(--space-m);font-size:var(--step--1);color:var(--text-dim);max-width:70ch}

/* 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 */
.faq{margin-block-start:var(--space-l);border-block-start:1px solid var(--line)}
.faq details{border-block-end:1px solid var(--line)}
.faq summary{cursor:pointer;list-style:none;padding-block:var(--space-s);min-height:44px;
  display:flex;align-items:center;justify-content:space-between;gap:var(--space-s);
  font-family:var(--font-display),"Space Grotesk fallback",sans-serif;font-weight:600;
  font-size:var(--step-1);letter-spacing:-.015em;
  transition:color var(--t-fast) var(--ease)}
.faq summary::-webkit-details-marker{display:none}
.faq summary:hover{color:var(--blue)}
.faq summary::after{content:"";flex:0 0 auto;width:13px;height:13px;
  border-inline-end:1.5px solid var(--blue);border-block-end:1.5px solid var(--blue);
  transform:rotate(45deg) translateY(-3px);transition:transform var(--t-fast) var(--ease)}
.faq details[open] summary::after{transform:rotate(225deg) translateY(-3px)}
.faq p{padding-block-end:var(--space-m);color:var(--text-soft);max-width:70ch;font-size:var(--step--1)}

/* ----------------------------------------------------------------- 16. DEVIS */
.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,.tier--reco::before,
  .hero__hint::before{animation:none}
  .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}
}
