/* ============================================================
   Slashi — direction artistique « éditorial »
   Refonte du 10 août 2026.

   Trois règles qui gouvernent tout ce fichier :
   1. AUCUNE valeur n'est inventée hors des tokens du bloc :root.
   2. UN SEUL angle sur tout le site : zéro. Boutons, champs, cartes,
      images — rien n'est arrondi. Mélanger deux arrondis fait amateur.
   3. UNE SEULE couleur d'accent : le vert de la marque, trois usages
      maximum par écran. Aucun dégradé, aucun halo, aucun flou.
   ============================================================ */

/* ---------- Polices auto-hébergées (RGPD : aucun appel externe) ----------
   Poppins pour les titres, Inter pour le texte : les polices d'origine de
   Slashi, rétablies le 10/08/2026 après essai de Clash Display + Satoshi.
   Pierre a comparé les deux rendus et préféré celui-ci — son choix prime sur
   la recommandation de la skill design-premium, qui déconseille ces polices
   parce qu'elles sont très répandues.

   ⚠️ Poppins n'existe ici qu'en graisse 400. Ne demande jamais une graisse
   supérieure aux titres : le navigateur fabriquerait un faux gras, qui bave.
   Pour de vrais titres plus épais, il faudrait d'abord ajouter les fichiers
   poppins-500 et poppins-600 dans assets/fonts/.

   JetBrains Mono reste : elle porte les intitulés « // » et les chiffres,
   où l'espacement fixe sert.

   font-display: block — choix maintenu du site : on préfère un court instant
   sans texte au clignotement d'une police de secours. */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400;
  font-display: block;
  src: url('assets/fonts/inter-400-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400;
  font-display: block;
  src: url('assets/fonts/inter-400-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 500;
  font-display: block;
  src: url('assets/fonts/inter-500-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 500;
  font-display: block;
  src: url('assets/fonts/inter-500-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 600;
  font-display: block;
  src: url('assets/fonts/inter-600-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 600;
  font-display: block;
  src: url('assets/fonts/inter-600-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Poppins';
  font-style: normal;
  font-weight: 400;
  font-display: block;
  src: url('assets/fonts/poppins-400-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Poppins';
  font-style: normal;
  font-weight: 400;
  font-display: block;
  src: url('assets/fonts/poppins-400-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 400;
  font-display: block;
  src: url('assets/fonts/jetbrainsmono-400-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 400;
  font-display: block;
  src: url('assets/fonts/jetbrainsmono-400-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 500;
  font-display: block;
  src: url('assets/fonts/jetbrainsmono-500-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ============================ TOKENS ============================
   Source de vérité du site. Toute valeur employée plus bas en dérive. */
:root {
  /* PALETTE — dominante encre (fond officiel de la marque Slashi),
     accent vert unique (le slash du logo). Contrastes mesurés sur l'encre :
     blanc 17,5:1 · vert 11,1:1 · sourdine 7,5:1 — tous au-dessus de AA. */
  --encre:       #0B0F0D;
  --encre-haut:  #0F1512;   /* surfaces légèrement détachées du fond */
  --blanc:       #F5F5F7;
  --vert:        #4ADE80;
  --vert-encre:  #07140D;   /* texte posé SUR le vert */
  --sourdine:    #9AA3A0;   /* texte secondaire */

  /* FILETS — la séparation se fait par un trait, jamais par une ombre */
  --filet:       rgba(245, 245, 247, 0.12);
  --filet-fort:  rgba(245, 245, 247, 0.30);

  /* CANAUX — les mêmes couleurs, écrites en composantes rouge/vert/bleu.
     Sert uniquement quand une opacité doit varier : un dégradé, ou le fond
     animé du hero, qui module la transparence bloc par bloc. */
  --encre-canaux:  11 15 13;
  --blanc-canaux: 245 245 247;
  --vert-canaux:   74 222 128;

  /* FOND ANIMÉ DU HERO — les quatre SEULES couleurs que connaît fond-hero.js.
     Le script les lit ici au démarrage : rien n'est écrit en dur dans le
     JavaScript, et changer l'ambiance du fond se fait depuis ce bloc.
     Les opacités, elles, sont réglées dans le CONFIG du script.
       fond  = le fond de la maquette (le fond officiel de la marque)
       fil   = le trait des blocs au repos
       bloc  = le remplissage d'un bloc allumé — blanc : c'est une lumière
               qui s'allume, pas une couleur qui s'ajoute
       lueur = le halo autour des blocs allumés — le vert de la marque, qui
               reste ainsi une signature et non un aplat */
  --hero-fond:  var(--encre);
  --hero-fil:   rgb(var(--blanc-canaux));
  --hero-bloc:  rgb(var(--blanc-canaux));
  --hero-lueur: rgb(var(--vert-canaux));

  /* TYPE — six tailles, pas une septième.
     Du corps (17) au display (68) : rapport 4x. */
  --t-micro:   13px;
  --t-petit:   15px;
  --t-corps:   17px;
  --t-inter:   22px;
  --t-titre:   34px;
  --t-display: 68px;

  /* ESPACEMENT — multiples de 8 uniquement */
  --e1:  8px;
  --e2: 16px;
  --e3: 24px;
  --e4: 32px;
  --e5: 48px;
  --e6: 64px;
  --e7: 96px;

  /* ANGLE — registre éditorial : zéro, partout, sans exception */
  --angle: 0;

  /* MOTION — une courbe, deux durées : le survol répond au doigt (court),
     l'apparition accompagne la lecture (plus ample). Une seule grammaire de
     mouvement dans tout le site : fondu + 12 px vers le haut. */
  --duree:            250ms;
  --duree-apparition: 500ms;
  /* Menu mobile : le rideau et les entrées ne vont pas à la même vitesse, et
     surtout ils ne se chevauchent jamais — voir le bloc « MENU MOBILE ». */
  --duree-rideau:     300ms;
  --duree-entree:     260ms;
  /* Temps laissé au slash pour traverser un bouton avant que la page ne
     change. Le script ne recopie plus cette valeur : il la lit ici. */
  --duree-clic:       300ms;
  /* Le slash qui traverse un bouton au survol : plus ample qu'au clic, parce
     qu'on a le temps de regarder. Était écrit en clair à deux endroits. */
  --duree-slash:      480ms;
  /* Le moment où les entrées du menu ont fini de partir et où le rideau
     commence à se lever. Trois endroits en dépendent — le rideau, son fond, et
     le départ de la page dans le script — et ils doivent bouger ensemble ou
     la fermeture se désynchronise. Nommé ici le 2026-09-09 pour cette raison. */
  --duree-sortie:     530ms;
  /* Le geste du logo, joué au survol ou au toucher, jamais tout seul. */
  --duree-logo:       600ms;
  /* Le passage d'une page à l'autre, en deux moitiés : le logo rejoint le
     centre pendant que le voile monte, puis revient à sa place pendant qu'il
     redescend. La seconde est plus courte : on attend pour regarder partir,
     moins pour arriver.
     Essayé à 550 + 400 le 2026-09-09, jugé trop rapide par Pierre — le geste
     passait avant qu'on l'ait suivi. Porté à 900 + 600, soit une seconde et
     demie. C'est plus que le repère habituel du site, et c'est assumé : ici
     il y a quelque chose à regarder, ce n'est pas une attente vide. */
  --duree-depart:     900ms;
  --duree-arrivee:    600ms;
  /* Le temps où le logo reste immobile au centre, sur le noir, avant que la
     page change. Court : c'est le point d'orgue du geste, pas une attente.
     Sans lui, le logo disparaissait à l'instant même où il finissait sa course,
     et le mouvement n'avait pas de fin — demandé par Pierre le 2026-09-09. */
  --duree-pause:      250ms;
  /* Combien le logo grossit en gagnant le centre. */
  --logo-zoom:        2.4;
  --courbe:           cubic-bezier(0.2, 0, 0, 1);
  --montee:           12px;

  /* MISE EN PAGE
     --maxw est la largeur du CONTENU. --largeur ajoute les marges latérales :
     c'est elle qu'on applique aux blocs, pour que tout le site s'aligne sur
     une seule et même colonne — hero, sections, pied de page compris. */
  --pad-x:   var(--e6);
  --maxw:    1200px;
  --largeur: calc(1200px + var(--pad-x) * 2);

  /* FAMILLES */
  --display: 'Poppins', var(--sans);
  --sans:    'Inter', system-ui, -apple-system, sans-serif;
  --mono:    'JetBrains Mono', ui-monospace, monospace;
}

/* ============================ BASE ============================ */
* { box-sizing: border-box; }

html { scroll-padding-top: 96px; }

body {
  margin: 0;
  background: var(--encre);
  color: var(--blanc);
  font-family: var(--sans);
  font-size: var(--t-corps);
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  /* clip (et non hidden) : évite de créer un conteneur de défilement
     qui casserait position:sticky sur la nav */
  overflow-x: clip;
}

img { max-width: 100%; height: auto; }
a { color: inherit; text-decoration: none; }

/* iOS pose un rectangle gris sur tout élément touché : on le retire, et on
   fournit nous-mêmes un retour à l'appui là où rien ne bouge à l'écran. */
/* La surbrillance grise que met iOS au toucher est retirée : elle déborde des
   formes et ignore la charte. MAIS retirer un retour oblige à le remplacer —
   sinon un appui ne produit plus rien du tout, et le doigt ne sait pas ce qu'il
   a attrapé. Le remplacement est la classe « est-touche » ci-dessous. */
a, button, summary, [role="button"] { -webkit-tap-highlight-color: transparent; }

/* ---------------------------------------------------------------------------
   RETOUR AU TOUCHER  (Contrat-Interface, règle 4 — ajouté le 2026-09-05)

   Sur un écran tactile il n'y a pas de survol : le doigt masque ce qu'il touche
   et les entrées d'une liste sont proches les unes des autres. Sans réponse
   immédiate, on ne sait pas si on a pris la bonne.

   Le principe tient en une phrase : **ce que le survol montre à la souris,
   l'appui le montre au doigt.** La classe « est-touche » est donc toujours
   écrite À CÔTÉ du « :hover » correspondant, jamais dans une règle séparée —
   ainsi l'apparence reste définie à un seul endroit et les deux gestes ne
   peuvent pas diverger.

   Un point non négociable : la réponse est INSTANTANÉE. Une confirmation qui
   met un quart de seconde à venir n'est plus une confirmation, c'est un écho.
   D'où la durée nulle imposée ici, qui prime sur les transitions de couleur
   déclarées ailleurs.

   La DURÉE nulle ne suffit pas : il faut aussi annuler le RETARD. Les entrées
   du menu portent en permanence un « transition-delay » de 300 à 570 ms, celui
   qui les fait arriver l'une après l'autre à l'ouverture. Il s'applique à
   toutes leurs transitions, la couleur comprise. Sans la ligne ci-dessous, le
   vert partait un tiers de seconde après le doigt — et comme le doigt se lève
   avant, on ne voyait rien du tout. Mesuré et corrigé le 2026-09-07, après
   deux corrections qui visaient la mauvaise cause.

   ATTENTION, leçon du 2026-09-07 : cette annulation ne doit valoir qu'au MOMENT
   DU TOUCHER. Écrite en « !important », elle écrasait aussi les retards qui
   font partir les entrées du menu l'une après l'autre : l'entrée touchée
   disparaissait d'un coup, avant les autres, et le mouvement d'ensemble était
   détruit. D'où le retrait du « !important » ici, et une annulation déclarée
   localement, à côté de chaque couleur, en excluant la fermeture.

   Règle à suivre pour tout élément qui aura sa propre transition : déclarer son
   instantanéité à côté de sa couleur, et vérifier qu'elle ne mord pas sur une
   animation d'ensemble. */
.est-touche { transition-duration: 0s; transition-delay: 0s; }

::selection { background: var(--vert); color: var(--vert-encre); }

:focus-visible {
  outline: 2px solid var(--vert);
  outline-offset: 3px;
  border-radius: var(--angle);
}

h1, h2, h3 {
  font-family: var(--display);
  font-weight: 400;          /* Poppins n'existe qu'en 400 ici */
  letter-spacing: -0.02em;
  line-height: 1.08;
  text-wrap: balance;        /* évite un mot orphelin en fin de titre */
  margin: 0;
}
p { text-wrap: pretty; }

.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--vert); color: var(--vert-encre);
  padding: var(--e2) var(--e3);
  font-weight: 600;
  border-radius: var(--angle);
}
.skip-link:focus { left: 0; }

/* ---------- Utilitaires de texte ---------- */

/* L'intitulé « // Section » : la marque Slashi parle en monospace.
   En vert depuis le 2026-08-25, à la demande de Pierre : le « // » est le
   slash de la marque, il porte l'accent au même titre que la numérotation
   et les boutons d'action. */
.mono-eyebrow {
  font-family: var(--mono);
  font-size: var(--t-micro);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--vert);
  margin: 0 0 var(--e4);
}

/* Le mot qui porte l'accent dans un titre. Couleur pleine : un texte en
   dégradé est illisible en petit et daté partout ailleurs. */
.titre-accent { color: var(--vert); }

.accent { color: var(--vert); font-weight: 600; }
.muted  { color: var(--sourdine); }
.sep    { color: var(--filet-fort); }

/* ============================ BOUTONS ============================ */
/* ============================ BOUTONS ============================
   LA FORME EST LA SIGNATURE : le bouton est un parallélogramme incliné,
   c'est-à-dire un « / » — le nom de la marque, donné à l'objet le plus
   cliqué du site. Un rectangle à coins vifs, même bien fait, se retrouve
   à l'identique sur n'importe quel site : un concurrent de Nevers avait
   exactement les mêmes boutons, flèche comprise (constaté le 13/08/2026).

   ⚠️ La forme se fait par « clip-path », PAS par « transform: skewX() ».
   Trois raisons, toutes vérifiées :
     1. le libellé reste droit et lisible — avec un skew il aurait fallu le
        redresser par une seconde transformation ;
     2. « transform » est déjà utilisé ailleurs sur les boutons (enfoncement
        à l'appui, cascade du menu mobile) : deux règles se seraient écrasées ;
     3. le découpage s'applique aussi au balayage, qui suit donc exactement
        la forme au lieu de dépasser dans les coins.

   Conséquence à connaître : ce qui est découpé n'est plus cliquable. C'est
   cohérent — on ne clique que ce qu'on voit — et la hauteur reste au-dessus
   du plancher tactile de 44 px.

   LE CONTOUR VERT (2026-08-25, demande de Pierre). Un parallélogramme découpé
   ne peut pas porter de « border » : elle serait coupée sur les bords
   obliques. Le contour est donc fait en deux couches : le fond du bouton est
   vert, et une couche « ::after », rentrée de 2 px et découpée de la même
   façon, porte la couleur de remplissage. Ce qui reste visible du fond vert
   entre les deux, c'est le contour. */
.btn {
  position: relative;
  display: inline-flex; align-items: center;
  /* Marges élargies : le texte ne doit pas venir toucher les bords obliques. */
  padding: var(--e2) var(--e4);
  border: 0;
  background: var(--vert);          /* le contour */
  font-family: var(--sans);
  font-size: var(--t-petit);
  font-weight: 600;
  letter-spacing: 0.01em;
  cursor: pointer;
  overflow: hidden;
  min-height: 48px;
  /* L'inclinaison : 14 px de décalage entre le haut et le bas, soit environ
     16° — la pente d'un slash. */
  clip-path: polygon(14px 0, 100% 0, calc(100% - 14px) 100%, 0 100%);
  /* Aucune transition de fond ici : le changement de couleur est porté par
     la couche oblique ci-dessous, pas par un fondu. */
}
.btn--lg {
  padding: var(--e2) var(--e5);
  font-size: var(--t-corps);
  min-height: 56px;
  clip-path: polygon(16px 0, 100% 0, calc(100% - 16px) 100%, 0 100%);
}

/* Le remplissage, à l'intérieur du contour. Le contour fait 2 px PARTOUT,
   mesurés perpendiculairement au bord. Sur un bord oblique, 2 px d'épaisseur
   réelle demandent un décalage horizontal un peu plus grand (2,08 px pour une
   pente de 14 px sur 48 px de haut), et le point du haut s'éloigne du coin
   pendant que celui du bas s'en rapproche. D'où ces valeurs non rondes :
   avec un simple « inset: 2px », le contour était plus fin sur les côtés
   obliques qu'en haut et en bas (constaté par Pierre le 2026-08-26). Les
   valeurs supposent un bouton d'une seule ligne (48 px, 56 px en --lg). */
.btn::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -2;
  clip-path: polygon(15.5px 2px, calc(100% - 2.67px) 2px, calc(100% - 15.5px) calc(100% - 2px), 2.67px calc(100% - 2px));
  pointer-events: none;
}
.btn--lg::after {
  clip-path: polygon(17.5px 2px, calc(100% - 2.65px) 2px, calc(100% - 17.5px) calc(100% - 2px), 2.65px calc(100% - 2px));
}

/* ---------- Le slash fait le passage d'une couleur à l'autre ----------
   La nouvelle couleur n'arrive PAS en fondu : elle est portée par une couche
   pleine dont le bord est oblique, et c'est ce bord — le slash — qui balaie le
   bouton. Derrière lui, la couleur d'arrivée ; devant lui, celle de départ.
   Le slash est donc la ligne de front, pas un reflet posé par-dessus.

   La couche déborde largement du bouton (inset négatif) : le découpage en
   parallélogramme la recadre, si bien qu'aucun coin ne reste à découvert quand
   elle est en place. */
.btn { isolation: isolate; }

.btn::before {
  content: '';
  position: absolute;
  inset: -25% -35%;
  z-index: -1;
  transform: translateX(-118%) skewX(-16deg);
  transition: transform var(--duree-slash) var(--courbe);
  pointer-events: none;
}
.btn:hover::before,
.btn:focus-visible::before { transform: translateX(0) skewX(-16deg); }

/* Au CLIC, le slash traverse d'un coup avant que la page ne change : sans ça,
   sur une connexion rapide, la page suivante s'affiche avant qu'on ait rien vu.
   Plus vif qu'au survol (300 ms contre 480) — on accepte d'attendre pour
   regarder, pas pour agir.
   C'est aussi le seul moment où l'effet est visible sur téléphone, où le survol
   n'existe pas. */
.btn--part::before {
  transform: translateX(0) skewX(-16deg);
  transition-duration: var(--duree-clic);
}

/* Les deux boutons sont sombres dans un contour vert, et passent au vert au
   survol et à l'appui. Plus de gris (retiré le 2026-08-26 à la demande de
   Pierre, après un essai d'une journée). Le libellé va du blanc au sombre EN
   FONDU, sur la durée du balayage : c'est le choix de Pierre, contre la
   bascule sèche d'avant. Le principal est légèrement plus clair que le
   secondaire pour rester repérable. */
.btn {
  color: var(--blanc);
  /* Le libellé change de couleur exactement pendant que le slash traverse :
     une seule durée nommée pour les deux, elles ne peuvent plus diverger. */
  transition: color var(--duree-slash) var(--courbe);
}
/* Les boutons ne reçoivent PAS « est-touche », et c'est voulu : leur retour au
   toucher existe déjà (l'enfoncement plus bas), et leur texte ne devient sombre
   que parce que le fond vert arrive en même temps. Poser la couleur sans le
   fond donnerait un texte illisible, et poser les deux ferait traverser le
   slash à l'appui puis une seconde fois au clic. Vérifié le 2026-09-05. */
.btn:hover,
.btn:focus-visible,
.btn--part { color: var(--vert-encre); }
.btn--part { transition-duration: var(--duree-clic); }
.btn::before { background: var(--vert); }

.btn--primary::after { background: var(--encre-haut); }
.btn--ghost::after   { background: var(--encre); }

/* Retour à l'appui : sur téléphone, rien ne bouge à l'écran quand on
   touche un bouton — cet enfoncement bref est la seule confirmation. */
.btn:active { transform: translateY(1px); }

.btn[disabled] { opacity: .55; cursor: default; }

/* Les flèches « → » ont été retirées des boutons le 13/08/2026 : un
   concurrent de Nevers avait exactement les mêmes. Leurs règles disparaissent
   avec elles — une règle sans élément à styler est une fausse piste pour la
   prochaine lecture du fichier. */

/* ============================== NAV ============================== */
.nav {
  position: sticky; top: 0; z-index: 50;
  display: flex; align-items: center; gap: var(--e4);
  padding-block: var(--e3);
  /* Le fond et le filet courent sur toute la largeur, mais le logo et les
     liens s'alignent sur la colonne du contenu. Le « 100 % » se calcule sur
     la largeur réellement disponible — barre de défilement déduite — là où
     100vw l'inclurait et décalerait tout de quelques pixels. */
  padding-inline: max(var(--pad-x), calc((100% - var(--maxw)) / 2));
  background: color-mix(in srgb, var(--encre) 92%, transparent);
  border-bottom: 1px solid var(--filet);
}
.nav__brand { display: flex; align-items: center; }
.nav__logo { height: 26px; width: auto; display: block; }

/* ---------------------------------------------------------------------------
   LE GESTE DU LOGO — au survol et au toucher, jamais tout seul

   Il tournait en boucle, six secondes sans fin, depuis le dessin lui-même. Un
   mouvement qui ne répond à rien finit par ne plus être vu : il prend de
   l'attention sans rien dire. Le geste est le même, il est désormais provoqué —
   demandé par Pierre le 2026-09-09.

   Pourquoi le dessin est maintenant DANS la page et non plus chargé comme une
   image : une image est isolée, aucun survol ne l'atteint et aucun style ne la
   traverse. Il fallait l'amener dans la page pour que le doigt et la souris
   puissent lui parler.

   Ce n'est plus une boucle mais un aller-retour : à l'arrivée il se fait, au
   départ il se défait. Il est donc interruptible, et se rejoue toujours à
   l'endroit où on l'a laissé. */
.nav__logo .sl-letters { transition: opacity var(--duree-logo) var(--courbe); }
.nav__logo .sl-slash,
.nav__logo .sl-eye { transition: transform var(--duree-logo) var(--courbe); }

/* Le même geste, quel qu'en soit le déclencheur : la souris, le doigt, ou le
   départ vers une autre page. Une seule définition — si le geste change un
   jour, il change partout du même coup. */
.nav__brand:hover .sl-letters,
.nav__brand:focus-visible .sl-letters,
.nav__brand.est-touche .sl-letters,
.nav__logo.transforme .sl-letters { opacity: 0; }

.nav__brand:hover .sl-slash,
.nav__brand:focus-visible .sl-slash,
.nav__brand.est-touche .sl-slash,
.nav__logo.transforme .sl-slash { transform: translateX(582.17px); }

.nav__brand:hover .sl-eye,
.nav__brand:focus-visible .sl-eye,
.nav__brand.est-touche .sl-eye,
.nav__logo.transforme .sl-eye { transform: translateX(-1104.08px); }

/* Pendant le voyage, la transformation prend le temps du trajet plutôt que
   celui d'un survol : les deux mouvements finissent alors ensemble. */
.nav__logo.en-vol .sl-letters { transition-duration: var(--duree-depart); }
.nav__logo.en-vol .sl-slash,
.nav__logo.en-vol .sl-eye { transition-duration: var(--duree-depart); }

/* ---------------------------------------------------------------------------
   LE PASSAGE D'UNE PAGE À L'AUTRE

   Une animation ne peut pas traverser un changement de page : tout est détruit
   au moment où la nouvelle arrive. Elle se joue donc en DEUX MOITIÉS qui se
   raccordent sur un écran entièrement noir — c'est ce noir qui cache la
   couture, et c'est pour cela qu'il doit être complet avant que la page parte.

   Le DÉPART, sur la page qu'on quitte : le logo gagne le centre en grossissant
   et joue sa transformation pendant le trajet ; le voile monte en même temps et
   efface le reste. À la fin, il ne reste que le logo sur du noir.

   L'ARRIVÉE, sur la page qui vient : on repart de cette image exacte. Le logo
   regagne sa place en haut à gauche pendant que le voile redescend. C'est lui
   qui donne l'impression d'une seule scène au lieu de deux pages.

   GARDE-FOU : le voile d'arrivée est une animation de la feuille de style, pas
   du script. Elle s'efface toute seule, une fois, sans que rien ait à le
   demander. Même si le script ne tourne pas, l'écran ne peut pas rester noir. */

.sl-voile {
  position: fixed;
  inset: 0;
  z-index: 900;
  background: var(--encre);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--duree-depart) var(--courbe);
}
.sl-voile.est-monte { opacity: 1; }

/* LE LOGO EN VOYAGE — c'est le VRAI qui part, jamais une copie.

   Une copie serait un second logo, avec sa vie propre : deux définitions à
   tenir d'accord, et la certitude qu'elles finiront par diverger. C'est ce que
   la règle 2 du Contrat-Interface interdit, et c'est ce qui avait été fait ici
   le 2026-09-09 avant que Pierre le remarque : « on dirait que tu as créé un
   second composant ».

   Il sort simplement de son en-tête le temps du trajet : posé à l'endroit exact
   où il se trouvait, il ne bouge pas d'un pixel à cet instant.

   UNE SEULE CHOSE EST ANIMÉE : le déplacement. Le trajet précédent animait les
   coordonnées, ce qui oblige le navigateur à recalculer la page entière à
   chaque image — d'où le mouvement sale que Pierre a vu. Un déplacement se
   confie à la carte graphique et reste net. */
.nav__logo.en-vol {
  position: fixed;
  z-index: 901;
  pointer-events: none;
  transform-origin: center center;
  will-change: transform;
  transition: transform var(--duree-depart) var(--courbe);
  /* Le dessin doit rester net à la grande taille : le script lui donne d'emblée
     ses dimensions maximales et le réduit optiquement, pour que le navigateur
     travaille toujours à la définition la plus fine. Voir « detacherLogo ». */
  image-rendering: auto;
  shape-rendering: geometricPrecision;
}
.nav__logo.en-vol.arrive { transition-duration: var(--duree-arrivee); }

/* L'ARRIVÉE, posée avant le premier affichage par un court script en tête de
   page : sans lui, on verrait la nouvelle page une fraction de seconde avant
   que le voile ne la couvre. L'animation part pleine et s'efface. */
html.sl-arrive::before {
  content: '';
  position: fixed;
  inset: 0;
  z-index: 900;
  background: var(--encre);
  pointer-events: none;
  animation: sl-lever var(--duree-arrivee) var(--courbe) forwards;
}
@keyframes sl-lever { from { opacity: 1 } to { opacity: 0 } }

/* Personne n'attend s'il a demandé moins de mouvement : la page change tout de
   suite, sans voile ni voyage. */
@media (prefers-reduced-motion: reduce) {
  .sl-voile { display: none; }
  /* Le logo reste à sa place, sans voyage ni transformation. */
  .nav__logo.en-vol { position: static; transform: none; }
  html.sl-arrive::before { animation: none; opacity: 0; }
}

.nav__links {
  display: flex; align-items: center; gap: var(--e4);
  margin-left: auto;
  font-size: var(--t-petit);
  color: var(--sourdine);
}
/* Le soulignement de la navigation — écrans larges uniquement, puisque
   « .nav__links » disparaît dès que le menu passe en bouton (960 px).

   Le trait se déploie depuis la gauche au survol, et se rétracte vers la
   droite quand on s'en va : c'est le changement de point d'ancrage entre les
   deux états qui produit cette impression de trait qui traverse. Un seul
   geste, dans la couleur d'accent, sans rien déplacer autour.

   Le filet est un pseudo-élément et non une bordure : une bordure ajouterait
   de la hauteur au seul lien actif, qui ne serait alors plus aligné avec ses
   voisins — c'était le cas jusqu'ici. */
.nav__links a {
  position: relative;
  padding-bottom: 4px;
  transition: color var(--duree) var(--courbe);
}
.nav__links a::after {
  content: '';
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 1px;
  background: var(--vert);
  transform: scaleX(0);
  transform-origin: right;      /* au repos : c'est par la droite qu'il part */
  transition: transform var(--duree) var(--courbe);
}
.nav__links a:hover::after,
.nav__links a:focus-visible::after {
  transform: scaleX(1);
  transform-origin: left;       /* au survol : il arrive par la gauche */
}
.nav__links a:hover { color: var(--blanc); }

/* La page courante garde son trait en permanence : c'est un repère, pas une
   décoration. Il ne bouge donc pas au survol. */
.nav__links a.is-active { color: var(--blanc); }
.nav__links a.is-active::after {
  transform: scaleX(1);
  transform-origin: left;
}

.nav__toggle {
  display: none;
  margin-left: auto;
  width: 48px; height: 48px;
  flex-direction: column; justify-content: center; align-items: stretch; gap: 5px;
  padding: 0 12px;
  background: none; border: 1px solid var(--filet-fort);
  border-radius: var(--angle);
  cursor: pointer;
  transition: border-color var(--duree) var(--courbe);
}
.nav__toggle:hover { border-color: var(--vert); }

.nav__toggle span {
  display: block;
  height: 1px;
  background: var(--blanc);
  /* Le pivot se fait au centre du trait : sans ça, les deux branches de la
     croix ne se rejoindraient pas au même point. */
  transform-origin: center;
  transition: transform var(--duree) var(--courbe),
              opacity   var(--duree) var(--courbe),
              background var(--duree) var(--courbe);
}

/* Le hamburger devient une croix. On s'appuie sur « aria-expanded », que le
   JavaScript tient déjà à jour pour les lecteurs d'écran : l'état visuel et
   l'état annoncé ne peuvent donc pas diverger — ils sont la même chose.

   Les 6 px de déplacement ne sont pas arbitraires : trait de 1 px + espace de
   5 px, donc 6 px séparent le centre de chaque trait de celui du milieu. */
.nav__toggle[aria-expanded="true"] span:nth-child(1) {
  transform: translateY(6px) rotate(45deg);
  background: var(--vert);
}
.nav__toggle[aria-expanded="true"] span:nth-child(2) {
  opacity: 0;
  transform: scaleX(0);
}
.nav__toggle[aria-expanded="true"] span:nth-child(3) {
  transform: translateY(-6px) rotate(-45deg);
  background: var(--vert);
}
.nav__toggle[aria-expanded="true"] { border-color: var(--vert); }

/* L'attribut « hidden » du HTML doit gagner sur la mise en page : sans cette
   règle, le menu resterait ouvert en permanence, et le bouton sans effet. */
.mobile-menu[hidden] { display: none; }

.mobile-menu {
  position: fixed; inset: 72px 0 0; z-index: 45;
  display: flex; flex-direction: column; gap: var(--e1);
  padding-block: var(--e4);
  padding-inline: max(var(--pad-x), calc((100% - var(--maxw)) / 2));
  overflow-y: auto;

  /* ⚠️ L'état au repos est VISIBLE, et ce n'est pas un détail. Une première
     version rendait le panneau transparent par défaut et comptait sur le
     JavaScript pour poser une classe à l'image suivante — mesuré le
     13/08/2026 : quand cette image ne vient pas, le menu s'ouvre sans rien
     afficher. Un menu présent mais invisible est pire qu'un menu qui ne
     s'ouvre pas : le clic passe, et le visiteur ne comprend pas.
     Ici, le panneau est visible dès qu'il n'est plus « hidden ». */

  /* ⚠️ À L'OUVERTURE, LE PANNEAU GLISSE SANS S'ESTOMPER ; À LA FERMETURE, IL
     S'ESTOMPE. Les deux moments ne demandent pas la même chose, et les traiter
     de la même façon a produit deux défauts successifs :
       - en animant son opacité À L'OUVERTURE, les deux fondus (le sien et
         celui des entrées) se multipliaient, et l'on voyait la page défiler
         derrière un fond devenu translucide ;
       - en la lui retirant AUSSI À LA FERMETURE, les entrées partaient mais le
         panneau restait un rectangle vide, qui disparaissait ensuite d'un coup.
     D'où ce partage : l'opacité est déclarée ici pour être animable, mais seul
     l'état de fermeture la modifie. */
  transition: transform var(--duree-rideau) var(--courbe);
}

/* L'état de départ de l'ouverture ET celui de la fermeture sont le même :
   panneau transparent, remonté de 12 px.

   ⚠️ NE PAS remplacer par @starting-style. Essayé le 13/08/2026 : l'animation
   ne jouait qu'à la toute première ouverture, puis plus jamais — il fallait
   recharger la page pour la revoir. @starting-style ne s'applique qu'à la
   première apparition de l'élément dans la page, pas à chaque fois qu'on le
   réaffiche. Une classe posée à chaque ouverture, elle, rejoue toujours. */
/* ---------- Le fond, sur sa propre couche ----------
   Il est séparé du panneau pour une raison précise : c'est LUI qui s'estompe,
   pas le contenu. Le texte de la page derrière s'efface donc en douceur au
   lieu d'être coupé net, mais les entrées du menu, elles, gardent leur
   opacité pleine pendant tout leur mouvement.
   Poser le fondu sur le panneau entier reviendrait à rendre les entrées
   translucides en même temps que le fond — c'est ce que faisaient les
   versions précédentes, et l'on voyait la page défiler à travers les mots. */
.mobile-menu::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 0;
  background: var(--encre);
  border-top: 1px solid var(--filet);
  transition: opacity var(--duree-rideau) var(--courbe);
}
/* Le contenu passe au-dessus du fond. */
.mobile-menu > * { position: relative; z-index: 1; }

/* L'état de départ de l'ouverture : le panneau est en place, simplement
   remonté, et son fond n'est pas encore posé — il va s'installer en fondu. */
.mobile-menu.s-ouvre {
  transform: translateY(calc(var(--montee) * -1));
}
.mobile-menu.s-ouvre::before { opacity: 0; }

/* L'état de fermeture du panneau : exactement l'inverse de son ouverture,
   donc le même glissement — et AUCUN fondu, puisqu'il n'y en a pas non plus
   à l'ouverture. C'est ce qui évite de voir la page à travers pendant que les
   entrées s'en vont : le fond reste plein jusqu'au bout. */
.mobile-menu.se-ferme {
  transform: translateY(calc(var(--montee) * -1));
}
/* Le fond s'en va exactement quand le panneau s'en va — même fenêtre de temps,
   donc même retard : 490 → 740 ms. La page réapparaît en fondu au lieu de
   revenir d'un coup. */
.mobile-menu.se-ferme::before {
  opacity: 0;
  transition-delay: var(--duree-sortie);
}

/* Ce bloc était UN seul, et il mélangeait deux choses de nature différente :
   le mouvement du menu, et l'apparence d'une entrée. Le bouton en héritait donc
   aussi la police et la taille — il n'avait ni l'une ni l'autre de celles qu'il
   a sur toutes les autres pages. Séparé le 2026-09-08.

   LE MOUVEMENT vaut pour TOUTES les entrées, le bouton compris : il arrive et
   il repart avec les autres, c'est la chorégraphie du menu et elle ne se
   découpe pas. */
.mobile-menu a {
  transition: opacity var(--duree-entree) var(--courbe),
              transform var(--duree-entree) var(--courbe),
              color var(--duree) var(--courbe);
}
/* L'APPARENCE ne vaut QUE pour les entrées simples. Le bouton est un composant :
   il porte sa propre police, sa taille, ses marges, sa forme, et le menu n'a
   pas à les redéfinir. Un contexte place un composant, il ne le repeint pas. */
.mobile-menu a:not(.btn) {
  padding: var(--e2) 0;
  font-family: var(--display);
  font-size: var(--t-inter);
  border-bottom: 1px solid var(--filet);
}
/* Les entrées se posent l'une après l'autre — même mouvement que le hero,
   même écart de 70 ms. Une seule grammaire dans tout le site.
   L'état de repos reste visible : c'est la classe d'ouverture qui porte
   l'état transparent, et elle est retirée dans la foulée. */
.mobile-menu.s-ouvre a {
  opacity: 0;
  transform: translateY(var(--montee));
  transition-delay: 0ms;
}
/* Les entrées ne commencent qu'une fois le rideau ENTIÈREMENT posé, à
   300 ms — jamais avant. Elles s'échelonnent ensuite de 45 ms, la septième
   finissant à 570 + 260 = 830 ms, durée totale de l'ouverture. */
.mobile-menu a:nth-child(1) { transition-delay: 300ms; }
.mobile-menu a:nth-child(2) { transition-delay: 345ms; }
.mobile-menu a:nth-child(3) { transition-delay: 390ms; }
.mobile-menu a:nth-child(4) { transition-delay: 435ms; }
.mobile-menu a:nth-child(5) { transition-delay: 480ms; }
.mobile-menu a:nth-child(6) { transition-delay: 525ms; }
.mobile-menu a:nth-child(7) { transition-delay: 570ms; }

/* La fermeture est le miroir de l'ouverture : les entrées repartent dans
   l'ordre INVERSE — la dernière arrivée s'en va la première — et le panneau
   ne se retire qu'ensuite, sinon il emporterait la cascade avec lui et on ne
   verrait rien. Les écarts sont plus serrés qu'à l'ouverture (25 ms contre
   70) : on accepte d'attendre pour voir arriver, jamais pour voir partir. */
.mobile-menu.se-ferme a {
  opacity: 0;
  transform: translateY(var(--montee));
}
/* MIROIR TEMPOREL EXACT DE L'OUVERTURE.
   L'ouverture dure 740 ms en tout : le panneau glisse de 0 à 250 ms, puis
   chaque entrée arrive avec 70 ms de retard sur la précédente, la septième
   finissant à 740. La fermeture rejoue cette même partition à l'envers : un
   élément qui à l'ouverture s'anime de [d ; d+250] s'anime ici de
   [740 − (d+250) ; 740 − d]. Les mêmes écarts de 70 ms, la même durée, le
   même ordre — simplement retourné.

   Les retards se lisent donc à l'envers : la dernière entrée arrivée est la
   première à partir. */
.mobile-menu.se-ferme a:nth-child(7) { transition-delay:   0ms; }
.mobile-menu.se-ferme a:nth-child(6) { transition-delay:  45ms; }
.mobile-menu.se-ferme a:nth-child(5) { transition-delay:  90ms; }
.mobile-menu.se-ferme a:nth-child(4) { transition-delay: 135ms; }
.mobile-menu.se-ferme a:nth-child(3) { transition-delay: 180ms; }
.mobile-menu.se-ferme a:nth-child(2) { transition-delay: 225ms; }
.mobile-menu.se-ferme a:nth-child(1) { transition-delay: 270ms; }

/* Le panneau est le PREMIER à bouger à l'ouverture, il est donc le DERNIER à
   la fermeture : 490 ms de retard, soit 740 − 250. Il glisse pendant que les
   toutes dernières entrées achèvent de partir, exactement comme il glissait
   pendant que les premières arrivaient. La constante DUREE de main.js doit
   rester au-dessus des 740 ms du total. */
.mobile-menu.se-ferme { transition-delay: var(--duree-sortie); }

/* « :not(.btn) » sur les trois états, et ce n'est pas un détail : sans lui, le
   menu redéfinit la couleur du BOUTON qu'il contient, et sa règle est plus
   forte que celle du bouton lui-même. Doigt posé, le fond du bouton devenait
   vert et son libellé restait vert : illisible. Sur les autres pages, aucune
   règle ne concurrence le bouton et il se comporte bien — c'est exactement la
   divergence que la règle 2 du Contrat-Interface interdit.
   Un contexte ne redéfinit jamais un composant : il le place, il l'espace, il
   ne le repeint pas. Constaté par Pierre le 2026-09-08. */
.mobile-menu a:not(.btn):hover,
.mobile-menu a:not(.btn):active,
.mobile-menu a:not(.btn).est-touche { color: var(--vert); }
/* Au doigt, sans durée ET sans retard ; au survol, le fondu doux reste.
   Le retard vient des règles d'échelonnement ci-dessus, qui sont permanentes.

   « :not(.se-ferme) » est la pièce maîtresse : pendant la fermeture, l'entrée
   touchée doit retrouver son retard de cascade et partir à son tour, avec les
   autres. Sans cette exclusion, elle disparaissait d'un coup et le mouvement
   d'ensemble était détruit — constaté par Pierre le 2026-09-07 : « une partie
   part avant, c'est pas ensemble ». */
.mobile-menu:not(.se-ferme) a:not(.btn).est-touche,
.mobile-menu a:not(.btn):active { transition-duration: 0s; transition-delay: 0s; }
.mobile-menu a.is-active { color: var(--vert); }

/* Au moment du choix, l'entrée de la page COURANTE cesse d'être vraie : on est
   déjà en train de quitter cette page. Elle revient donc à sa couleur de base,
   et le vert ne désigne plus qu'une seule chose — ce qu'on vient de toucher.
   Sans ça, deux entrées seraient vertes en même temps et le vert ne dirait plus
   rien : ni où l'on est, ni où l'on va.

   « inherit » plutôt qu'une couleur écrite : la base reste définie à un seul
   endroit (règle 2 du Contrat-Interface). Le « :not » protège le cas où l'on
   touche l'entrée de la page où l'on est déjà — elle reste verte, à juste titre.

   Comme au-dessus, l'annulation du retard s'arrête à la fermeture : passé ce
   moment, cette entrée repart avec les autres, dans la cascade. */
.mobile-menu.choix-fait a.is-active:not(.est-touche) { color: inherit; }
.mobile-menu.choix-fait:not(.se-ferme) a.is-active:not(.est-touche) {
  transition-duration: 0s;
  transition-delay: 0s;
}
/* Ne reste ici que du PLACEMENT : où le bouton se met dans le menu, et comment
   son libellé s'y centre. Plus rien qui touche à son apparence.
   Les rattrapages qui vivaient ici — remettre ses marges latérales, effacer la
   bordure que le menu lui collait — ont disparu le 2026-09-08 : ils réparaient
   les dégâts d'une règle trop large, laquelle ne l'attrape plus. Un rattrapage
   qui disparaît est le signe qu'on a corrigé la cause et non le symptôme. */
.mobile-menu .btn { margin-top: var(--e3); justify-content: center; }

/* ============================== HERO ==============================
   Une thèse, alignée à gauche : qui, où, et quoi faire — visible sans
   faire défiler sur téléphone. */
.hero {
  padding-block: var(--e7) var(--e6);
  padding-inline: max(var(--pad-x), calc((100% - var(--maxw)) / 2));
  border-bottom: 1px solid var(--filet);
  /* Trois couches empilées : le fond animé (0), le voile (1), le texte (2).
     « isolation » enferme cet empilement dans le hero, pour qu'il ne puisse
     pas interférer avec le reste de la page. */
  position: relative;
  isolation: isolate;
}

/* ---------- Le fond animé (canvas) ----------
   Purement décoratif : il est masqué aux lecteurs d'écran et ne reçoit aucun
   clic. Si le script ne s'exécute pas, ou si le navigateur ne fournit pas de
   contexte 2D, le canvas reste vide ou masqué et c'est le fond de la marque
   qui s'affiche — le hero est identique, sans mouvement. */
.hero__fond {
  position: absolute;
  inset: 0;
  z-index: 0;
  display: block;
  width: 100%;
  height: 100%;
  pointer-events: none;
}
.hero__fond[hidden] { display: none; }

/* ---------- Le voile de lisibilité ----------
   C'est un dégradé CSS posé PAR-DESSUS le canvas et SOUS le texte — jamais
   dessiné dans le canvas. Sur grand écran il couvre la gauche, là où sont le
   titre et les boutons ; le fond reste lisible à droite, où part l'onde.

   ⚠️ SES ARRÊTS SUIVENT LA COLONNE DE TEXTE, PAS DES POURCENTAGES D'ÉCRAN.
   La raison est mesurée, pas théorique : le contenu du site est centré dans
   une colonne de largeur fixe (--maxw), alors qu'un pourcentage grandit avec
   l'écran. Les deux se séparent donc dès qu'on s'élargit — en plein écran, le
   texte débordait dans la partie où le voile s'était déjà effacé, et la trame
   venait mordre dessus. C'est le « ça décale tout » signalé par Pierre le
   17/08/2026. La variable --colonne ci-dessous reprend EXACTEMENT le calcul du
   padding du hero : le voile et le texte ne peuvent plus se désolidariser,
   quelle que soit la largeur. */
.hero { --colonne: max(var(--pad-x), calc((100% - var(--maxw)) / 2)); }

.hero::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  /* +660px : la fin réelle du texte le plus large (le sous-titre, plafonné à
     30em). Le filet des chiffres va plus loin, mais un trait d'un pixel à 12 %
     d'opacité n'a rien à protéger. */
  background: linear-gradient(to right,
    rgb(var(--encre-canaux) / 1) 0%,
    rgb(var(--encre-canaux) / 0.90) calc(var(--colonne) + 660px),
    rgb(var(--encre-canaux) / 0.55) calc(var(--colonne) + 920px),
    rgb(var(--encre-canaux) / 0.25) 100%);
}

.hero__inner { position: relative; z-index: 2; }

.hero__title {
  font-size: clamp(40px, 8vw, var(--t-display));
  margin: 0 0 var(--e4);
}
.hero__line { display: block; }
.hero__line:last-child { color: var(--sourdine); }

.hero__sub {
  max-width: 30em;
  margin: 0 0 var(--e5);
  font-size: var(--t-inter);
  line-height: 1.5;
  color: var(--sourdine);
}

/* ---------- Les métiers qui défilent ----------
   ⚠️ LA LIGNE EST UNE BOÎTE FLEX ALIGNÉE AU CENTRE, ET C'EST VOLONTAIRE.
   La version précédente posait le mot dans un conteneur « inline-flex » avec
   découpe : or la ligne de base d'un élément en ligne qui masque son
   débordement se calcule différemment d'un navigateur à l'autre. Le mot était
   correctement aligné sous Chrome et décalé sous Safari — signalé par Pierre
   le 13/08/2026. En passant la ligne en flex avec « align-items: center »,
   plus aucune ligne de base n'intervient : le résultat est identique partout.
   Ne pas revenir à un affichage en ligne avec overflow. */
.hero__metiers {
  margin: 0 0 var(--e5);
  font-size: var(--t-inter);
  line-height: 1.4;
  color: var(--sourdine);
}

/* ⚠️ LE MOT EST DU TEXTE ORDINAIRE DANS LA PHRASE, ET C'EST TOUT L'ENJEU.
   Deux tentatives ont échoué avant celle-ci, toutes deux parce qu'elles
   faisaient du mot une boîte séparée qu'il fallait ensuite aligner sur le
   texte voisin :
     1. un conteneur en ligne avec découpe — la ligne de base d'un tel élément
        se calcule différemment selon le navigateur : juste sous Chrome,
        décalé sous Safari ;
     2. une ligne en flex alignée au centre — mieux, mais toujours pas exact.
   Ici, le mot affiché reste dans le flux du texte : il n'y a plus rien à
   aligner, il se pose sur la même ligne que le reste, partout pareil.
   Ne pas lui remettre de hauteur fixe ni de découpe. */
.rotation {
  position: relative;
  display: inline-block;
  white-space: nowrap;
  transition: width var(--duree-apparition) var(--courbe);
}

/* Repli tant que le script n'a pas pris la main : les 44 métiers sont là, on
   n'en montre qu'un. C'est le seul cas où une découpe est nécessaire, et il
   disparaît dès que le script s'exécute. */
.rotation:not(.rotation--actif) {
  overflow: hidden;
  height: 1.4em;
  vertical-align: bottom;
}
.rotation__liste > span,
.rotation__mot {
  line-height: 1.4;
  white-space: nowrap;
  color: var(--vert);
  font-weight: 600;
}
.rotation__liste > span { display: block; }

/* Le mot affiché est dans le flux : c'est lui qui porte la ligne de base. */
.rotation__mot { display: inline-block; }

/* Seul le mot qui s'en va quitte le flux, le temps de s'effacer. Son
   déplacement est volontairement court (0.3em) : sans découpe, un grand
   mouvement viendrait mordre sur la ligne du dessus. */
.rotation__mot--sort {
  position: absolute;
  left: 0; top: 0;
  transform: translateY(-0.3em);
  opacity: 0;
}
.rotation__mot--anime {
  transition: transform var(--duree-apparition) var(--courbe),
              opacity var(--duree-apparition) var(--courbe);
}
/* L'état d'où arrive le mot entrant. Il est retiré dans la foulée par le
   script, si bien que l'état au repos reste « visible ». */
.rotation__mot--entre { transform: translateY(0.3em); opacity: 0; }

/* Sert uniquement à mesurer la largeur du mot suivant avant de l'afficher :
   invisible, hors flux, mais bien composé avec la même police. */
.rotation__regle {
  position: absolute;
  visibility: hidden;
  pointer-events: none;
  white-space: nowrap;
}

.hero__actions { display: flex; flex-wrap: wrap; gap: var(--e2); }

/* Sans filet au-dessus depuis le 2026-08-25 (demande de Pierre). */
.hero__stats {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--e2);
  margin: var(--e6) 0 0; padding: 0;
  list-style: none;
  font-family: var(--mono);
  font-size: var(--t-micro);
  letter-spacing: 0.06em;
  color: var(--sourdine);
}

/* En-tête des pages intérieures : même grammaire que le hero, en plus court. */
.page-hero {
  padding-block: var(--e6);
  padding-inline: max(var(--pad-x), calc((100% - var(--maxw)) / 2));
  border-bottom: 1px solid var(--filet);
}
.page-hero h1 {
  max-width: 14em;
  font-size: clamp(34px, 6vw, 56px);
  margin: 0 0 var(--e3);
}
/* Le « :not » est indispensable : l'intitulé « // Section » est lui aussi un
   paragraphe, et cette règle-ci est plus spécifique que « .mono-eyebrow ».
   Sans l'exclusion, l'intitulé héritait de la taille du texte d'introduction
   — 22 px au lieu de 13 — sur les huit pages intérieures, alors que l'accueil
   restait correct. Constaté et corrigé le 13/08/2026. */
.page-hero > p:not(.mono-eyebrow) {
  max-width: 34em;
  margin: 0;
  font-size: var(--t-inter);
  line-height: 1.5;
  color: var(--sourdine);
}

/* Coordonnées : une clé en monospace, une valeur. Pas de séparateurs « · »
   qui se retrouvent orphelins en colonne sur téléphone, pas d'émoji. */
.contact-infos {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--e3);
  margin: var(--e5) 0 0; padding: var(--e3) 0 0;
  list-style: none;
  border-top: 1px solid var(--filet);
  font-size: var(--t-petit);
  color: var(--blanc);
}
.contact-infos__cle {
  display: block;
  margin-bottom: 2px;
  font-family: var(--mono);
  font-size: var(--t-micro);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--sourdine);
}
/* Cible tactile confortable pour appeler ou écrire depuis un téléphone. */
.contact-infos a {
  display: inline-block;
  padding: var(--e1) 0;
  color: var(--vert);
}
.contact-infos a:active { opacity: .7; }

/* ============================ SECTIONS ============================ */
.section {
  max-width: var(--largeur);
  margin: 0 auto;
  padding: var(--e7) var(--pad-x);
}
.section--tight { padding-top: var(--e6); padding-bottom: var(--e6); }

.section__head { margin-bottom: var(--e6); }
.section__title { font-size: clamp(30px, 4.5vw, var(--t-titre)); }
/* Variante : le titre d'un bloc d'introduction est un peu plus discret que
   celui d'une section ordinaire. Elle existait depuis longtemps, mais imposée
   par trois conteneurs au lieu d'être nommée — le titre changeait d'aspect
   selon l'endroit où il se trouvait, ce que la règle 2 du Contrat-Interface
   interdit. Nommée le 2026-09-08 : elle est désormais disponible partout, et
   on voit dans la page laquelle est employée. */
.section__title--intro { font-size: clamp(28px, 4vw, var(--t-titre)); }
/* Un bouton sous une grille de cartes, pour renvoyer vers la page détaillée. */
.section__foot { margin-top: var(--e5); }

/* ===================== OFFRE / BANDEAU FONDATEUR =====================
   Bloc pleine largeur sur fond légèrement détaché : c'est l'objet le plus
   fort de la page après le hero, il n'a pas besoin d'un effet en plus. */
.founder {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--e5);
  padding-block: var(--e6);
  padding-inline: max(var(--pad-x), calc((100% - var(--maxw)) / 2));
  border-top: 1px solid var(--filet);
  border-bottom: 1px solid var(--filet);
}
.founder__text { flex: 1 1 420px; }
.founder__title {
  font-size: clamp(24px, 3.5vw, 30px);
  margin: 0 0 var(--e2);
}
.founder__desc { margin: 0; color: var(--sourdine); }
.founder__desc strong { color: var(--blanc); font-weight: 600; }
.founder__cta { flex: 0 0 auto; }

/* ============================= CARTES =============================
   Pas de bordure grise sur les quatre côtés, pas d'ombre : un filet
   supérieur et un numéro. C'est la signature du site. */
.cards {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--e5);
}
.cards--two { grid-template-columns: repeat(2, 1fr); }

.card {
  display: block;
  padding: var(--e3) 0 0;
  border-top: 1px solid var(--filet-fort);
  border-radius: var(--angle);
  transition: border-color var(--duree) var(--courbe);
}
a.card:hover { border-top-color: var(--vert); }

/* Le numéro suivi du slash : le nom de la marque devient la numérotation
   du site. Le slash est décoratif, d'où le pseudo-élément. */
.card__num {
  display: block;
  margin-bottom: var(--e2);
  font-family: var(--mono);
  font-size: var(--t-micro);
  letter-spacing: 0.12em;
  color: var(--vert);
}
.card__num::after { content: ' /'; color: var(--filet-fort); }

.card__date {
  display: block;
  margin-bottom: var(--e2);
  font-family: var(--mono);
  font-size: var(--t-micro);
  letter-spacing: 0.08em;
  color: var(--sourdine);
}
.card h3 { font-size: var(--t-inter); margin: 0 0 var(--e2); }
.card p  { margin: 0; color: var(--sourdine); font-size: var(--t-petit); }

.card__more {
  display: inline-block;
  margin-top: var(--e3);
  font-family: var(--mono);
  font-size: var(--t-micro);
  letter-spacing: 0.08em;
  color: var(--vert);
  transition: transform var(--duree) var(--courbe);
}
/* Même geste que la flèche des boutons : au survol, l'invitation avance. */
a.card:hover .card__more { transform: translateX(4px); }

/* ========================= AUDIT / MÉTHODE =========================
   Mise en page asymétrique : un intitulé court à gauche, la matière à
   droite. Jamais trois blocs identiques centrés. */
.audit__layout,
.method__layout,
.about__layout {
  display: grid;
  grid-template-columns: 0.75fr 1.25fr;
  gap: var(--e7);
  align-items: start;
}
/* Les trois règles qui imposaient ici une taille au titre ont été retirées le
   2026-09-08 : la taille est portée par « .section__title--intro », posée sur
   le titre lui-même. */

.audit__lead { margin: 0 0 var(--e4); font-size: var(--t-inter); line-height: 1.5; }
.audit__note { margin: var(--e4) 0; color: var(--sourdine); font-size: var(--t-petit); }

/* Les étapes : une vraie séquence, donc une vraie numérotation.
   Le filet vertical relie les étapes entre elles. */
.steps { margin: 0; padding: 0; list-style: none; }
.step {
  position: relative;
  display: grid;
  grid-template-columns: 64px 1fr;
  gap: var(--e3);
  padding: var(--e4) 0;
  border-top: 1px solid var(--filet);
}
.step:first-child { border-top: 0; padding-top: 0; }
.step__num {
  font-family: var(--mono);
  font-size: var(--t-micro);
  letter-spacing: 0.12em;
  color: var(--vert);
}
.step__num::after { content: ' /'; color: var(--filet-fort); }
.step h3 { font-size: var(--t-inter); margin: 0 0 var(--e1); }
.step p  { margin: 0; color: var(--sourdine); font-size: var(--t-petit); }

/* ============================ CHIFFRES ============================ */
.results {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--e5);
  padding: var(--e5) 0;
  border-top: 1px solid var(--filet-fort);
  border-bottom: 1px solid var(--filet-fort);
}
.stat p { margin: var(--e1) 0 0; color: var(--sourdine); font-size: var(--t-petit); }
.stat__num {
  font-family: var(--display);
  font-weight: 400;
  font-size: clamp(36px, 5vw, 52px);
  line-height: 1;
  letter-spacing: -0.02em;
}

/* ============================= TARIFS ============================= */
.pricing {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--e5);
}
.price-card {
  position: relative;
  display: flex; flex-direction: column;
  padding: var(--e4) 0 0;
  border-top: 1px solid var(--filet-fort);
  border-radius: var(--angle);
}
/* La formule mise en avant l'est par son filet, pas par une boîte. */
.price-card--featured { border-top: 2px solid var(--vert); }

/* Le badge se pose SUR le filet, hors du flux : sans ça, la carte qui en
   porte un décalerait tout son contenu vers le bas, et les trois colonnes
   ne démarreraient plus à la même hauteur. */
.price-card__badge {
  position: absolute;
  top: var(--e1); right: 0;
  font-family: var(--mono);
  font-size: var(--t-micro);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--vert);
}
.price-card h3 { font-size: var(--t-inter); margin: 0 0 var(--e2); }
.price-card__desc { margin: 0 0 var(--e3); color: var(--sourdine); font-size: var(--t-petit); }

.price-card__list { margin: 0 0 var(--e4); padding: 0; list-style: none; }
.price-card__list li {
  padding: var(--e1) 0;
  border-bottom: 1px solid var(--filet);
  font-size: var(--t-petit);
  color: var(--sourdine);
}
/* Pas de puce ici : le filet entre deux lignes suffit à les séparer.
   Le slash de la marque est réservé à la numérotation des sections et des
   étapes — répété sur chaque ligne de chaque formule, il ne signifierait
   plus rien. */

.price-card__price {
  margin: auto 0 var(--e3);
  font-family: var(--display);
  font-weight: 400;
  font-size: var(--t-titre);
  line-height: 1;
  letter-spacing: -0.02em;
}
.price-card__from,
.price-card__per {
  display: inline-block;
  font-family: var(--mono);
  font-size: var(--t-micro);
  letter-spacing: 0.08em;
  color: var(--sourdine);
}
.price-card__from { display: block; margin-bottom: var(--e1); }

.pricing__note {
  max-width: 52em;
  margin: var(--e5) 0 0; padding-top: var(--e3);
  border-top: 1px solid var(--filet);
  font-size: var(--t-petit);
  color: var(--sourdine);
}

/* ============================ À PROPOS ============================ */
.about__body p { margin: 0 0 var(--e3); color: var(--sourdine); }
.about__body strong { color: var(--blanc); font-weight: 600; }
.about__body .btn { margin-top: var(--e2); }

/* ============================== FAQ ==============================
   <details> natif : clavier, état et lecteur d'écran sont gratuits,
   et la page reste utilisable sans JavaScript. */
.faq__list { max-width: 62em; }

.faq__item {
  border-bottom: 1px solid var(--filet);
  border-radius: var(--angle);
}
.faq__item summary {
  display: flex; align-items: flex-start; gap: var(--e2);
  padding: var(--e3) 0;
  font-family: var(--display);
  font-weight: 400;
  font-size: var(--t-inter);
  line-height: 1.3;
  cursor: pointer;
  list-style: none;
  transition: color var(--duree) var(--courbe);
}
.faq__item summary::-webkit-details-marker { display: none; }
/* Le marqueur d'ouverture : un « + » qui devient « − », en mono. */
.faq__item summary::after {
  content: '+';
  margin-left: auto;
  font-family: var(--mono);
  font-size: var(--t-inter);
  color: var(--vert);
  transition: transform var(--duree) var(--courbe);
}
.faq__item[open] summary::after { content: '−'; }
/* Pendant la fermeture animée (main.js), l'élément est encore « ouvert » :
   le marqueur repasse à « + » tout de suite pour ne pas mentir. */
.faq__item.se-ferme summary::after { content: '+'; }
.faq__item summary:hover,
.faq__item summary:active,
.faq__item summary.est-touche { color: var(--vert); }
/* La règle générale « .est-touche » ne l'emporterait pas sur « .faq__item
   summary » : l'instantanéité se déclare donc ici, à côté de la couleur. */
.faq__item summary:active,
.faq__item summary.est-touche { transition-duration: 0s; transition-delay: 0s; }
.faq__item summary:focus-visible { outline: 2px solid var(--vert); outline-offset: 4px; }

.faq__item p {
  max-width: 58em;
  margin: 0; padding: 0 0 var(--e4);
  color: var(--sourdine);
  font-size: var(--t-petit);
}

/* Pas d'animation d'ouverture ici, et c'est délibéré : mesuré le 10/08/2026,
   la transition de hauteur sur ::details-content s'ouvrait bien mais ne se
   refermait plus — la question restait dépliée pour toujours. Le <details>
   natif s'ouvre d'un coup, sans un seul défaut. Un élément natif qui fait le
   travail gagne toujours contre une animation cosmétique qui le casse. */

/* ========================= APPEL À L'ACTION ========================= */
.cta {
  padding: var(--e6) 0;
  border-top: 1px solid var(--filet-fort);
}
.cta__title { font-size: clamp(30px, 5vw, 46px); margin: 0 0 var(--e2); }
.cta__sub {
  max-width: 34em;
  margin: 0 0 var(--e5);
  color: var(--sourdine);
  font-size: var(--t-inter);
  line-height: 1.5;
}
.cta__alt {
  max-width: 46em;
  margin: var(--e4) 0 0;
  font-size: var(--t-micro);
  line-height: 1.7;
  color: var(--sourdine);
}
.cta__alt a { color: var(--vert); text-decoration: underline; text-underline-offset: 3px; }

/* =========================== FORMULAIRE =========================== */
.contact-form {
  display: flex; flex-direction: column; gap: var(--e3);
  max-width: 46em;
  /* Cases à cocher et sélecteurs prennent la couleur du site. */
  accent-color: var(--vert);
}
.contact-form__row { display: grid; grid-template-columns: 1fr 1fr; gap: var(--e3); }

.contact-form label {
  display: flex; flex-direction: column; gap: var(--e1);
  font-family: var(--mono);
  font-size: var(--t-micro);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--sourdine);
}

/* Les champs ont EXACTEMENT la forme des grands boutons (2026-08-26, après un
   premier essai raté) : même pente de 16° (14 px de décalage pour 48 px de
   haut), contour vert d'1 px au repos et de 2 px (celui des boutons) quand
   on écrit dedans, mesuré perpendiculairement au bord, même technique en
   deux couches. Le « champ » porte le vert et la découpe, le
   champ de saisie à l'intérieur porte le fond, découpé 2 px plus petit.

   Le premier essai était raté pour deux raisons : la pente était fixée à
   12 px quelle que soit la hauteur — 12° sur un champ d'une ligne, à peine 4°
   sur la zone de texte, rien à voir avec les boutons — et le contour ne
   faisait qu'1 px. Pour que la pente soit la même partout, la hauteur des
   champs est FIXE : 56 px pour une ligne (celle des grands boutons, donc
   leur découpe exacte), 160 px pour la zone de texte (découpe recalculée :
   46,67 px de décalage pour la même pente). Ne pas rendre ces hauteurs
   variables sans recalculer les découpes. */
.champ {
  display: block;
  background: var(--vert);
  clip-path: polygon(16px 0, 100% 0, calc(100% - 16px) 100%, 0 100%);
}
.champ--texte {
  clip-path: polygon(46.67px 0, 100% 0, calc(100% - 46.67px) 100%, 0 100%);
}

.contact-form input,
.contact-form textarea,
.contact-form select {
  display: block;
  width: 100%;
  height: 56px;
  padding: var(--e2) var(--e4);
  background: var(--encre-haut);
  border: 0;
  border-radius: var(--angle);
  /* Même pente que les grands boutons, contour d'1 px au repos. */
  clip-path: polygon(16.75px 1px, calc(100% - 1.33px) 1px, calc(100% - 16.75px) calc(100% - 1px), 1.33px calc(100% - 1px));
  color: var(--blanc);
  font-family: var(--sans);
  /* 17px minimum : en dessous, iOS zoome tout seul sur le champ. */
  font-size: var(--t-corps);
  line-height: 24px;
  text-transform: none;
  letter-spacing: normal;
}
/* Quand on écrit dedans, le contour passe à 2 px : celui des boutons
   (2026-08-26, demande de Pierre : fin au repos, épais à la sélection). */
.contact-form input:focus,
.contact-form textarea:focus,
.contact-form select:focus {
  outline: none;
  clip-path: polygon(17.5px 2px, calc(100% - 2.65px) 2px, calc(100% - 17.5px) calc(100% - 2px), 2.65px calc(100% - 2px));
}

/* Zone de texte : 160 px de haut, même pente. Le texte commence à 56 px du
   bord pour ne pas toucher la découpe (42 px au niveau de la première ligne).
   Pas de poignée de redimensionnement : elle vivrait dans le coin découpé. */
.contact-form textarea {
  height: 160px;
  padding: var(--e2) 56px;
  resize: none;
  clip-path: polygon(47.42px 1px, calc(100% - 1.33px) 1px, calc(100% - 47.42px) calc(100% - 1px), 1.33px calc(100% - 1px));
}
.contact-form textarea:focus {
  clip-path: polygon(48.17px 2px, calc(100% - 2.67px) 2px, calc(100% - 48.17px) calc(100% - 2px), 2.67px calc(100% - 2px));
}

/* Liste déroulante : l'habillage natif du navigateur est retiré pour que la
   hauteur et la découpe soient respectées partout (Safari compris). Le petit
   chevron vert à droite remplace la flèche native, il est dessiné dans la
   feuille de style, aucun fichier ni service tiers. */
.contact-form select {
  appearance: none;
  -webkit-appearance: none;
  padding-right: var(--e6);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' fill='none' stroke='%234ADE80' stroke-width='2'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--e4) center;
  cursor: pointer;
}
.contact-form select option { background: var(--encre-haut); color: var(--blanc); }

.contact-form .btn { align-self: flex-start; }

/* Piège à robots : hors de l'écran plutôt que display:none, que certains
   automates savent détecter pour ignorer le champ. */
.contact-form__piege {
  position: absolute; left: -9999px; width: 1px; height: 1px;
  overflow: hidden;
}
.contact-form__statut {
  margin: 0;
  font-size: var(--t-petit);
  line-height: 1.6;
  color: var(--sourdine);
}
.contact-form__statut.is-succes {
  color: var(--vert);
  font-size: var(--t-inter);
  padding: var(--e3) 0;
}
.contact-form__statut.is-erreur { color: #FCA5A5; }

/* ===================== ARTICLES ET PAGES LÉGALES ===================== */
.article,
.legal {
  max-width: var(--largeur);
  margin: 0 auto;
  padding: var(--e6) var(--pad-x);
}
/* Le texte garde une longueur de ligne confortable (~40 em), mais il commence
   sur la MÊME colonne que le reste du site. Un article centré au milieu d'un
   site aligné à gauche se voit immédiatement. */
.article > *,
.legal > * { max-width: 40em; }

.article h1,
.legal h1 {
  font-size: clamp(30px, 4.5vw, 44px);
  margin-bottom: var(--e3);
}
.article__meta,
.legal__date {
  font-family: var(--mono);
  font-size: var(--t-micro);
  letter-spacing: 0.08em;
  color: var(--sourdine);
  margin: 0 0 var(--e3);
}
.article__lead {
  font-size: var(--t-inter);
  line-height: 1.5;
  color: var(--blanc);
  margin: 0 0 var(--e5);
}
.article h2,
.legal h2 {
  font-size: var(--t-inter);
  margin: var(--e6) 0 var(--e2);
  padding-top: var(--e3);
  border-top: 1px solid var(--filet);
}
.article h3, .legal h3 { font-size: var(--t-corps); margin: var(--e4) 0 var(--e1); }
/* « :not(.legal__date) » : la date de mise à jour est un composant, elle porte
   sa propre couleur. Elle déclarait la même que celle-ci, donc rien ne se
   voyait — mais le jour où elle changerait, cette règle l'aurait annulée en
   silence. Protégée le 2026-09-08, avant que le piège se referme. */
.article p, .legal p:not(.legal__date), .article li, .legal li { color: var(--sourdine); }
.article a, .legal a { color: var(--vert); text-decoration: underline; text-underline-offset: 3px; }

/* ============================= FOOTER ============================= */
.footer {
  padding-block: var(--e6) var(--e4);
  padding-inline: max(var(--pad-x), calc((100% - var(--maxw)) / 2));
  border-top: 1px solid var(--filet-fort);
}
.footer__top {
  display: grid;
  grid-template-columns: 1.2fr 2fr;
  gap: var(--e6);
}
.footer__logo { height: 24px; width: auto; display: block; margin-bottom: var(--e2); }
.footer__brand p { margin: 0; color: var(--sourdine); font-size: var(--t-petit); }

.footer__cols { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--e4); }
.footer__col { display: flex; flex-direction: column; gap: var(--e1); font-size: var(--t-petit); }
.footer__col a, .footer__col span:not(.footer__col-title) { color: var(--sourdine); }
.footer__col a { transition: color var(--duree) var(--courbe); }
.footer__col a:hover { color: var(--vert); }
/* En vert depuis le 2026-08-25, comme les intitulés « // ». */
.footer__col-title {
  font-family: var(--mono);
  font-size: var(--t-micro);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--vert);
  margin-bottom: var(--e1);
}

.footer__bottom {
  display: flex; flex-wrap: wrap; gap: var(--e2);
  justify-content: space-between;
  margin-top: var(--e6); padding-top: var(--e3);
  border-top: 1px solid var(--filet);
  font-family: var(--mono);
  font-size: var(--t-micro);
  letter-spacing: 0.08em;
  color: var(--sourdine);
}

/* ============================ RESPONSIVE ============================ */
@media (max-width: 960px) {
  :root { --pad-x: var(--e4); }

  .nav__links, .nav__cta { display: none; }
  .nav__toggle { display: flex; }

  .cards, .cards--two, .pricing, .results { grid-template-columns: 1fr; gap: var(--e4); }
  .audit__layout, .method__layout, .about__layout { grid-template-columns: 1fr; gap: var(--e4); }
  .footer__top { grid-template-columns: 1fr; gap: var(--e5); }
  .footer__cols { grid-template-columns: 1fr 1fr; }

  .section  { padding: var(--e6) var(--pad-x); }
  .hero     { padding-block: var(--e6) var(--e5); }

  /* Sur téléphone le texte occupe le haut : le voile bascule donc en
     hauteur, et le fond ne respire qu'en bas — là où descend le foyer des
     ondes (voir CONFIG_MOBILE dans fond-hero.js). */
  .hero::after {
    background: linear-gradient(to bottom,
      rgb(var(--encre-canaux) / 1) 0%,
      rgb(var(--encre-canaux) / 0.92) 38%,
      rgb(var(--encre-canaux) / 0.55) 70%,
      rgb(var(--encre-canaux) / 0.25) 100%);
  }
  .founder  { padding-block: var(--e5); gap: var(--e3); }
  .contact-form__row { grid-template-columns: 1fr; }
  .contact-form .btn, .founder__cta { align-self: stretch; width: 100%; justify-content: center; }

  /* En colonne, un séparateur « · » se retrouve seul sur sa ligne : on le
     retire et on empile proprement. */
  .hero__stats { flex-direction: column; align-items: flex-start; gap: var(--e1); }
  .hero__stats .sep { display: none; }

  .results { padding: var(--e4) 0; }
  .stat { padding-bottom: var(--e3); border-bottom: 1px solid var(--filet); }
  .stat--last { padding-bottom: 0; border-bottom: 0; }
}

@media (min-width: 961px) {
  .mobile-menu { display: none !important; }
}

@media (max-width: 480px) {
  .footer__cols { grid-template-columns: 1fr; gap: var(--e4); }
}

/* ============================ MOUVEMENT ============================
   UNE seule grammaire dans tout le site : fondu + 12 px vers le haut.
   Elle sert à deux moments, et à deux moments seulement — l'arrivée sur la
   page, et l'arrivée d'un bloc structurant à l'écran. Le reste ne bouge pas.

   Règle de conception à ne pas défaire : l'opacité de départ n'est JAMAIS
   posée en dur dans une règle CSS. Elle vit uniquement dans les keyframes,
   avec « both ». Conséquence : si l'animation ne s'exécute pas — navigateur
   trop ancien, animations désactivées, moteur de recherche — le contenu est
   visible d'emblée. Un texte masqué par une animation qui ne se déclenche pas
   est un texte qui n'existe pour personne. */

@keyframes apparition {
  from { opacity: 0; transform: translateY(var(--montee)); }
  to   { opacity: 1; transform: none; }
}

/* 1. L'ARRIVÉE SUR LA PAGE — le hero se pose en cascade.
      Décalages de 70 ms : assez pour lire une progression, trop court pour
      qu'on ait le sentiment d'attendre.

      ⚠️ ICI, ON N'ANIME QUE LE DÉPLACEMENT — JAMAIS L'OPACITÉ. La raison est
      mesurée, pas théorique : une apparition en fondu déclenchée par le TEMPS
      laisse le contenu à « opacity: 0 » tant qu'elle ne s'est pas jouée. Le
      13/08/2026, dans un rendu figé, le hero est resté invisible
      indéfiniment — avec des @keyframes comme avec une transition et
      @starting-style. Un titre que le navigateur ou un robot d'indexation ne
      révèle jamais est un titre qui n'existe pour personne.
      Le déplacement, lui, est sans risque : au pire, le texte s'affiche
      12 px plus bas, ce que personne ne remarque.
      Le fondu reste autorisé PLUS BAS, sur les blocs animés au défilement :
      leur progression dépend de la position dans la page et non du temps, ce
      qui a été vérifié en fenêtre de 9 000 px — tous à pleine opacité.

      NE JAMAIS ajouter « opacity » dans ce bloc-ci. */
.hero__inner > *,
.page-hero > *:nth-child(-n+3) {
  transition: transform var(--duree-apparition) var(--courbe);
}
.hero__inner > *:nth-child(2),
.page-hero > *:nth-child(2) { transition-delay:  70ms; }
.hero__inner > *:nth-child(3),
.page-hero > *:nth-child(3) { transition-delay: 140ms; }
.hero__inner > *:nth-child(4) { transition-delay: 210ms; }
.hero__inner > *:nth-child(5) { transition-delay: 280ms; }

/* L'état de la toute première image, et d'elle seule. Pas d'opacité ici :
   voir l'avertissement ci-dessus. */
@starting-style {
  .hero__inner > *,
  .page-hero > *:nth-child(-n+3) {
    transform: translateY(var(--montee));
  }
}

/* 2. L'ARRIVÉE D'UN BLOC À L'ÉCRAN — piloté par le défilement, en CSS pur.
      « animation-timeline: view() » confie l'animation au navigateur : aucun
      JavaScript, aucune reprise en main du défilement, et le mouvement suit
      exactement le doigt ou la molette.

      Le @supports est indispensable : sur un navigateur qui ne connaît pas
      cette propriété (Firefox à ce jour), la règle entière est ignorée et les
      blocs restent simplement visibles — c'est la bonne dégradation.

      Seuls les blocs STRUCTURANTS sont concernés : cartes, étapes, chiffres,
      formules, bandeaux. Ni les titres, ni les paragraphes, ni les liens. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .card,
    .step,
    .stat,
    .price-card,
    .founder {
      animation: apparition linear both;
      animation-timeline: view();
      /* Le bloc finit son apparition bien avant le milieu de l'écran :
         personne ne doit avoir à faire défiler pour finir de lire. */
      animation-range: entry 5% cover 25%;
    }
  }
}

/* Réglage système « réduire les animations » : plus rien ne bouge.
   Ce bloc vient en dernier et neutralise tout ce qui précède. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation: none !important;
    transition: none !important;
  }
}
