/* ==========================================================================
   Percer : les onze composants de la home, et les blocs de page.
   14 septembre 2026.

   Toutes les valeurs viennent de tokens.css. Ce fichier n'y redéfinit qu'un
   seul jeton, --type-unit, signalé et commenté juste en dessous, et il attend
   un oui pour être remonté dans tokens.css.

   Règles du design system respectées ligne à ligne :
   1. Le violet ne sort jamais des labels, du gradient site et de l'anneau
      de focus.
   2. Tous les textes sont en noir ou en gris.
   3. Le gradient noir vers gris est réservé aux titres, en background-clip,
      et jamais en dessous de 60 unités de corps.
   4. La direction artistique bascule en sombre sur Signal, et le gradient
      devient le chaud.
   5. Les rayons sont des multiples de 4, pris dans l'échelle corrigée.
   6. Jamais de tiret cadratin, y compris dans les libellés de boutons.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Bases, en complément de tokens.css
   -------------------------------------------------------------------------- */

/* --------------------------------------------------------------------------
   CORRECTION PROPOSÉE, isolée ici pour pouvoir être retirée en trois lignes.
   Elle n'est pas dans tokens.css parce que la section 2 de CLAUDE.md range
   « changer une valeur du système » dans ce qui se demande avant de faire.
   Elle attend donc un oui.

   Le problème. CLAUDE.md annonce « le H1 passe de 67 px à
   95 px en continu », et marque les coefficients du sizing fluide comme
   provisoires. Le coefficient posé, .045rem + .078vw, atteint son plafond dès
   679 px de large : au-delà, --type-unit vaut 1,25 px et ne bouge plus. Tous
   les écrans d'ordinateur reçoivent donc la valeur maximale. Le H1 sort à
   95 px partout, ce qui est très exactement le résultat que l'affectation de
   la section 5.2 écarte en choisissant display-3xl plutôt que display-4xl, et
   le corps sort à 22,5 px au lieu des 18 px de son propre tableau.

   La correction. La rampe couvre 480 à 1920 px au lieu de saturer à 679, et
   le plafond descend de .0781rem à .0687rem. Ce plafond est calé sur le
   tableau de la section 5.2 lui-même : à 1440 px, --type-unit vaut 1,03 px,
   donc le corps sort à 18,5 px et non à 22,5, ce qui est la valeur que le
   tableau annonce. Le H1 va de 67 px à 83 px au lieu de 67 à 95.

   Ce second réglage a été demandé le 14 septembre 2026 : la page sortait
   trop grosse et le logo du hero paraissait minuscule à côté.

   À valider, puis à remonter dans tokens.css et dans memoire/.
   -------------------------------------------------------------------------- */
:root {
  --type-unit: clamp(.055rem, .0504rem + .0153vw, .0687rem);

  /* De quoi le hero remonte son fond derrière la barre. Volontairement plus
     grand que la barre, qui mesure 79 px à 1440 : sa hauteur suit l'échelle
     typographique et bougera au prochain réglage. Surtirer coûte quelques
     pixels de fond clippés hors de la page ; sous-tirer laisse un liseré
     blanc en haut, et c'est visible. */
  --hauteur-entete: calc(var(--spacing) * 104);

  /* Le bleu du système, nommé. Ce n'est pas une couleur nouvelle : c'est
     l'extrémité bleue de --gradient-site, qui n'avait jamais eu de jeton à
     elle parce qu'elle ne servait qu'à l'intérieur du gradient. Le bouton
     primaire en a besoin en aplat depuis l'arbitrage du 14 septembre 2026.
     À remonter dans tokens.css avec le reste. */
  /* ⚠️ Depuis le design system du 16 septembre 2026, les aplats d’interface sont
     noirs : la couleur n’est plus qu’N accent. Le jeton garde son nom pour que
     les treize règles qui le lisent suivent. L’extrémité bleue du dégradé reste
     dans --gradient-site. */
  /* ⚠️ Revenu au bleu le 16 septembre 2026, sur demande : « tous les boutons
     noirs doivent devenir bleus ». Il avait été passé au noir plus tôt le même
     jour, avec le design system. */
  --bleu: #0091FF;

  /* ⚠️ **Le vert entre dans la page le 15 septembre 2026, sur demande**, pour
     la coche de la carte du système. Le système n'avait aucun vert : il a un
     rouge d'erreur et c'est tout. Sa valeur est choisie sur le contraste, pas
     sur le goût : **5,02 pour 1 sur blanc**, comme le gris lisible, et 4,39 sur
     sa propre pastille. Les deux pastilles sont opaques et non teintées, pour
     qu'elles se lisent pareil sur la carte blanche et sur le bleu du survol.
     À remonter dans tokens.css avec le reste. */
  --succes:       #1A7F4B;
  --succes-fond:  #E7F3EC;
  --erreur-fond:  #FBEAE6;

  /* Le jeu d'ombres des cartes. **C'est l'échelle qui compte** : le flou double
     à chaque couche, 1, 3, 6, 12, 24, et l'étalement vaut chaque fois la moitié
     du flou, en négatif. C'est ce qui la rend serrée sous la carte et très
     large loin d'elle, là où une ombre unique donne un halo plat. C'est la même
     grammaire que l'ombre de la barre de navigation, à la même encre que le
     texte, et aucune valeur nouvelle n'entre.
     La lueur, elle, est intérieure : elle allume l'arête haute et le pourtour,
     et c'est ce qui fait tenir la carte sur un fond cassé. */
  /* sh-base, du design system (nom et valeurs de pulsor.agency, repris par le Figma). */
  --ombre-carte:
    0 0 0 1px rgb(6 86 134 / .04),
    0 1px 1px -.5px rgb(1 34 54 / .04),
    0 3px 3px -1.5px rgb(1 34 54 / .06),
    0 6px 8px -3px rgb(1 34 54 / .01),
    0 6px 12px -6px rgb(6 86 134 / .02),
    0 24px 24px -12px rgb(6 86 134 / .04);
  /* sh-btn et sh-glass, du design system. */
  --sh-btn: -21px 16px 10px 0 rgb(0 0 0 / .04), -12px 9px 9px 0 rgb(0 0 0 / .13), -5px 4px 6px 0 rgb(0 0 0 / .23), -1px 1px 4px 0 rgb(0 0 0 / .26);
  --sh-glass: inset 0 0 35.2px 0 rgb(255 255 255 / .03), inset 0 0 12px 0 rgb(255 255 255 / .08);

  /* La même ombre, appuyée. **L'échelle ne change pas** : le flou double encore
     à chaque couche et l'étalement vaut toujours la moitié du flou en négatif.
     Ce qui change, c'est l'opacité, un peu plus du double, et **une sixième
     couche à quarante-huit** qui allonge la portée. Une ombre qu'on renforce en
     ajoutant de l'opacité sans allonger la portée devient une seconde forme
     posée sous la carte ; c'est le rapport des deux qui donne la hauteur, comme
     sur le bouton primaire. */
  --ombre-carte-forte:
    0 1px 1px -.5px rgb(28 28 28 / .06),
    0 3px 3px -1.5px rgb(28 28 28 / .06),
    0 6px 6px -3px rgb(28 28 28 / .07),
    0 12px 12px -6px rgb(28 28 28 / .08),
    0 24px 24px -12px rgb(28 28 28 / .09),
    0 48px 48px -24px rgb(28 28 28 / .10);
  --lueur-carte:
    inset 0 1px 0 0 rgb(255 255 255 / .55),
    inset 0 0 calc(var(--spacing) * 60) 0 rgb(255 255 255 / .35);

  /* Le fond des cartes. Il est ici et pas recopié : cinq endroits le portent,
     les quatre rangées du constat, les colonnes des deux Duos, le bloc des
     quatre leviers et celui des fondations. Cinq copies finissent par diverger.

     ⚠️ **Il a été deux blancs transparents jusqu'au 15 septembre 2026, et il ne
     se voyait pas.** Du blanc à 85 % et du blanc à 30 % sur un fond déjà cassé,
     l'écart valait cinq unités de luminance : la carte se lisait comme un aplat
     blanc. Elle va maintenant du blanc pur en bas à un ton sous la page en
     haut, et le dégradé se voit.

     ⚠️ **Le haut est plafonné par le contraste, et c'est un plafond serré.** Du
     gris de corps se pose sur ces cartes : à `#F5F5F5` il tient **4,57 pour
     1**, juste au-dessus du minimum AA. Un ton de plus et il tombe à 4,49.
     **Assombrir ce dégradé demande de passer tout ce qui est en gris au noir**,
     comme sur la carte sombre du constat. */
  --fond-carte: linear-gradient(to top, #FFFFFF 0%, #F5F5F5 100%);

  /* Le même fond, en bleu ciel. Deux endroits le portent : le survol de la
     carte du système, en section 02, et le bloc de la publicité, en 03.
     ⚠️ **Il ne se lit qu'avec du texte noir.** Le blanc y tombe à 1,48 pour 1
     au point le plus dense, et aucun voile ne peut le rattraper puisque le fond
     s'éclaircit vers le haut. Le noir tient 5,91 en bas et 11,03 en haut. */
  --fond-carte-bleu: linear-gradient(
    to top,
    rgb(0 145 255 / .85) 0%,
    rgb(0 145 255 / .30) 100%);

  /* Le même fond, en gris sombre. Deux endroits le portent : la carte sans
     système, en section 02, et le bloc de la publicité, en 03. C'est une
     nuance de l'encre du texte, pas une couleur de plus.
     ⚠️ **Le gris de corps n'y passe pas**, 3,45 pour 1 au point le plus dense :
     tout ce qui s'y écrit est en noir, qui y tient 11,19.
     ⚠️ **La lueur y est réduite de 35 à 14 %.** À 35 %, celle des cartes
     claires éclaircit les arêtes jusqu'au blanc et annule le gris qu'on vient
     de poser. */
  --fond-carte-sombre: linear-gradient(
    to top,
    rgb(28 28 28 / .16) 0%,
    rgb(28 28 28 / .09) 100%);

  /* Le même fond, en teinte de violet. Un seul endroit le porte : la colonne
     de ce qu'on attend, en section 07.
     ⚠️ **C'est une teinte, pas un aplat**, ce que la section 2 interdit. La
     valeur est celle de la pilule des labels, portée de 8 à 12 % pour qu'elle
     se voie à cette taille.
     ⚠️ **Le gris de corps n'y passe pas**, 4,03 pour 1 au point le plus dense :
     tout ce qui s'y écrit est en noir, qui y tient 13,07. C'est la même
     conséquence que sur la carte grise, et elle se mesure de la même façon. */
  --fond-carte-violet: linear-gradient(
    to top,
    rgb(102 71 240 / .12) 0%,
    rgb(102 71 240 / .05) 100%);

  --lueur-carte-douce:
    inset 0 1px 0 0 rgb(255 255 255 / .55),
    inset 0 0 calc(var(--spacing) * 60) 0 rgb(255 255 255 / .14);
}

*,
*::before,
*::after { box-sizing: border-box; }

html { scroll-behavior: smooth; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

body {
  margin: 0;
  /* La barre flotte au-dessus du contenu : on décale les ancres de sa
     hauteur plus son retrait. */
  scroll-padding-top: calc(var(--spacing) * 112);
}

h1, h2, h3, h4, p, ul, ol, figure, table, dl { margin: 0; }

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

img, svg { max-width: 100%; }

a { color: inherit; }

::selection {
  background: var(--noir);
  color: var(--blanc);
}

/* --------------------------------------------------------------------------
   2. Primitives de mise en page
   -------------------------------------------------------------------------- */

/* La pile : c'est elle qui porte le rythme, jamais des marges posées à la
   main sur chaque élément. Trois valeurs seulement, comme le dit le système. */
.pile       { display: flex; flex-direction: column; }
.pile-intra   > * + * { margin-block-start: var(--rythme-intra); }
.pile-bloc    > * + * { margin-block-start: var(--rythme-bloc); }

.pile-intra, .pile-bloc { display: flex; flex-direction: column; }

/* Le conteneur de bord, pour le haut de page.

   Le conteneur ordinaire est borné à 1280 px et centré : sur un écran de
   1920, il laisse 361 px à gauche, soit 19 % de la largeur, et le bloc du
   hero part vers le milieu au lieu de tenir le bord. La référence garde une
   gouttière de 9 % à toutes les largeurs.

   Ce conteneur remplace donc la largeur maximale par une gouttière
   proportionnelle, bornée en bas par la marge du système et en haut à
   180 px pour qu'un très grand écran ne creuse pas indéfiniment.

   Il ne sert qu'à l'en-tête et au hero, qui forment une composition à part,
   pleine largeur. Les sections en dessous gardent la grille de 1280 px. */
.conteneur-bord {
  max-width: none;
  padding-inline: clamp(var(--marge-laterale), 8vw, calc(var(--spacing) * 180));
}

.mesure-corps  { max-width: var(--mesure-corps); }
.mesure-incise { max-width: var(--mesure-incise); }
.mesure-titre  { max-width: var(--mesure-titre); }

/* Le trait de séparation de section. Un seul gris, celui de la grille. */
.filet {
  height: 1px;
  border: 0;
  background: var(--gris-grille);
  margin: 0;
}

/* --------------------------------------------------------------------------
   3. Typographie de page
   -------------------------------------------------------------------------- */

/* Le nom de section. Il reprend le libellé de la navigation, donc il dit
   où on est, ce qui est une information. Il est gris et non violet : le
   violet a exactement deux usages sur cette page, le nom des quatre leviers
   et le levier rattaché à un résultat ou à une personne. */
/* ⚠️ **Le label de section est une pilule de verre depuis le 15 septembre
   2026, sur demande, et il l'est partout.** C'était un texte gris en 700 ;
   c'est maintenant le dessin exact du label du hero, qui était le seul à le
   porter. Les quinze labels de la page se tiennent donc, et il n'y a plus
   qu'une seule règle pour eux tous.

   La matière est celle du bouton à contour et de la capsule du prix : teinte
   de noir à 4 %, liseré à 8 %, flou de ce qui passe derrière, deux reflets
   blancs et deux ombres portées très faibles.

   ⚠️ Deux choses viennent avec la pilule, et ce sont des décisions, pas des
   effets de bord. La graisse descend de 700 à 400, sur demande, et c'est la
   plus légère des deux fontes variables chargées. Et la couleur passe du gris
   lisible au noir : sur un fond qui s'éclaircit, le gris perdait le peu de
   contraste qu'il avait, quand le noir en tient quatorze pour un.

   ⚠️ **Ce n'est pas un bouton et il ne doit jamais s'en donner l'air** : ni
   curseur main, ni état de survol. Un élément qui ressemble à un bouton et ne
   clique pas est un piège, pas un ornement. */
.section-nom {
  align-self: flex-start;
  display: inline-flex;
  align-items: center;
  padding: calc(var(--spacing) * 7) calc(var(--spacing) * 18);
  border-radius: var(--r-full);
  /* ⚠️ **L'étiquette prend la typographie de pulsor.agency le 16 septembre
     2026, sur demande** : Geist Mono 500, en capitales, chiffres tabulaires,
     interlettrage du corps. Elle était en Inter 400, en casse normale. La
     couleur ne bouge pas, et le contraste non plus : il ne dépend pas de la
     famille. */
  font-family: var(--font-mono);
  font-size: var(--eyebrow);
  font-weight: 500;
  line-height: 1.428;
  letter-spacing: var(--ls-corps);
  text-transform: uppercase;
  font-variant-numeric: tabular-nums;
  /* ⚠️ **Le label reprend le violet le 15 septembre 2026, sur demande, et c'est
     un retour à la règle** : la section 2 dit que le violet ne sert qu'aux
     labels, aux gradients et à l'anneau de focus. Il tient **5,5 pour 1** sur le
     verre de la pilule, au-dessus du minimum AA pour ce corps.
     **Deux exceptions, et les deux sont écrites plus bas** : le hero, qui garde
     son encre noire, et ce qui est posé sur le panneau sombre, où le violet
     tombe à 3,35 pour 1. */
  color: var(--violet);
  max-width: none;
  /* ⚠️ **La pilule prend la teinte du violet le 15 septembre 2026, sur
     demande.** Elle portait la teinte de noir commune au verre du site.
     **Huit pour cent, et pas plus** : le violet sur sa propre teinte tient
     **4,75 pour 1** à cette valeur, et il passe sous le minimum AA dès dix. La
     pilule des cartes, elle, garde le verre noir. */
  background: rgb(102 71 240 / .08);
  border: 1px solid rgb(102 71 240 / .20);
  -webkit-backdrop-filter: blur(25px);
  backdrop-filter: blur(25px);
  box-shadow:
    inset 0 1px 0 0 rgb(255 255 255 / .25),
    inset 0 0 calc(var(--spacing) * 20) 0 rgb(255 255 255 / .25),
    0 calc(var(--spacing) * 2) calc(var(--spacing) * 8) rgb(28 28 28 / .04),
    0 calc(var(--spacing) * 7) calc(var(--spacing) * 35) rgb(28 28 28 / .04);

  /* ⚠️ **Tous les labels prennent la matière des labels sur fond sombre
     depuis le 16 septembre 2026, sur demande** : violet à 20 %, liseré d'un
     pixel et lueur intérieure en violet éclairci. Les valeurs ci-dessus sont
     celles de la version retirée.

     ⚠️ **Sur fond clair, l'encre ne peut être ni le violet du système ni le
     violet presque blanc du sombre.** À 20 % de teinte, le violet du système
     tombe sous le minimum AA, et le presque blanc ne se lit pas du tout. Le
     texte prend donc un violet approfondi, **#4A30CF**, qui tient plus de
     5 pour 1 au cœur de la pilule. Les labels posés sur du sombre gardent
     leur encre, plus bas. */
  /* ⚠️ **Sur fond clair, le centre est presque transparent**, repris le même
     jour sur demande : à 20 % la pilule se lisait comme un aplat. La teinte
     descend à 5 %, et la lueur se resserre sur le bord. Le sombre garde ses
     20 %, plus bas. */
  background: rgb(102 71 240 / .05);
  border: 1px solid #8C76FF;
  color: #4A30CF;
  box-shadow:
    inset 0 0 calc(var(--spacing) * 8) 0 rgb(140 118 255 / .45),
    inset 0 0 calc(var(--spacing) * 2) 0 rgb(140 118 255 / .4);
}

.eyebrow {
  font-size: var(--eyebrow);
  font-weight: 700;
  line-height: 1.428;
  letter-spacing: -.15px;
  color: var(--violet);
}

/* Un intitulé de tableau nécessaire au lecteur d'écran, inutile à l'œil. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Le prix, source unique. Il vit sur --prix-mensuel et --prix-total dans
   tokens.css, et il n'est écrit en dur nulle part dans le balisage : il
   change encore, et un changement doit se faire à un seul endroit. */
.prix-mensuel::after { content: var(--prix-mensuel); }
.prix-total::after   { content: var(--prix-total); }

/* Le numéro de temps de lecture : il encode l'ordre des huit temps du Miro,
   qui est un argument, pas un ornement. */
.temps {
  display: flex;
  align-items: baseline;
  gap: calc(var(--spacing) * 12);
  font-size: var(--legende);
  color: var(--gris-lisible);
}

.temps-num {
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  color: var(--noir);
}

.chapo {
  font-size: var(--corps-lg);
  line-height: 1.5;
  color: var(--gris-lisible);
  max-width: var(--mesure-corps);
}

.incise {
  font-size: var(--legende);
  line-height: 1.45;
  color: var(--gris-lisible);
  max-width: var(--mesure-incise);
}

/* Les trois titres intermédiaires partagent 1,15 d'interligne et -0,02 em
   d'interlettrage : la règle du système veut que l'interlettrage se desserre
   à mesure que la taille descend, et 40, 32 et 24 px sont le même palier. */
.titre-lg { font-family: var(--font-titre); font-weight: var(--graisse-titre); font-size: var(--titre-lg); line-height: var(--lh-titre); letter-spacing: var(--ls-titre-petit); }
.titre-md { font-family: var(--font-titre); font-weight: var(--graisse-titre); font-size: var(--titre-md); line-height: var(--lh-titre); letter-spacing: var(--ls-titre-petit); }
.titre-sm { font-family: var(--font-titre); font-weight: var(--graisse-titre); font-size: var(--titre-sm); line-height: var(--lh-titre); letter-spacing: var(--ls-titre-petit); }

.corps-sm { font-size: var(--corps-sm); line-height: 1.5; }
.legende  { font-size: var(--legende);  line-height: 1.45; }

.gris     { color: var(--gris-lisible); }
.fort     { font-weight: 700; color: var(--noir); }

/* Le gradient de titre ne descend jamais sous 60 unités : son extrémité
   claire tombe à 3,24:1. En dessous, on l'enlève plutôt que de le réduire.
   L'inline-block est nécessaire au background-clip, il impose donc de
   reposer la largeur et la coupure : sans ça, un mot long déborde de la
   page au lieu de passer à la ligne. */
.titre-gradient {
  display: inline-block;
  max-width: 100%;
  padding-block-end: .08em;
  overflow-wrap: break-word;
}

/* --------------------------------------------------------------------------
   4. Boutons et liens
   -------------------------------------------------------------------------- */

.bouton {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: calc(var(--spacing) * 8);
  font-family: var(--font-corps);
  font-size: var(--cta-lg);
  font-weight: 700;
  line-height: 1.333;
  letter-spacing: var(--ls-titre-petit);
  text-decoration: none;
  padding: calc(var(--spacing) * 16) calc(var(--spacing) * 28);
  border: 0;
  border-radius: var(--r-md);
  cursor: pointer;
  transition:
    background-color var(--motion-instant) var(--ease-out),
    color var(--motion-instant) var(--ease-out);
}

/* --------------------------------------------------------------------------
   Le libellé qui roule, au survol du bouton primaire.

   Relevé sur pulsor.agency le 14 septembre 2026, et refait à leur grammaire :
   le libellé est coupé en caractères, et chaque lettre monte d'une ligne,
   décalée de 10 ms sur sa voisine. **C'est le décalage qui fait l'effet.** Un
   mot entier qui monte d'un bloc est un autre effet, et ce n'était pas
   celui-là.

   Le second exemplaire n'est pas un élément, c'est une ombre de texte posée
   une ligne plus bas. Rien n'est doublé dans le document : la lettre emmène
   sa propre copie avec elle, et la fenêtre la découvre en la laissant monter.

   La fenêtre et le saut sont en 1lh, la hauteur d'une ligne de ce bouton.
   Les deux tailles de bouton n'ont pas le même interligne, 1,333 et 1,375 :
   une valeur en em aurait laissé dépasser un morceau de la copie sur l'une
   des deux. Le repli en em reste pour les moteurs sans lh.

   La rotation d'un millième de degré n'est pas un résidu. Sans elle, la
   lettre est rendue en niveaux de gris pendant qu'elle bouge puis repasse en
   sous-pixel une fois posée, et tout le libellé scintille à l'arrivée. Avec
   elle, la transformation n'est jamais alignée sur les axes et le rendu ne
   change pas en cours de route.

   Le roulement se déclenche aussi au focus clavier. Un effet qui ne répond
   qu'à la souris dit à qui tabule que le bouton n'a pas bougé.
   -------------------------------------------------------------------------- */
.roule {
  display: inline-block;
  overflow: hidden;
  vertical-align: bottom;
  white-space: nowrap;
  block-size: 1.34em;
  block-size: 1lh;
}

/* ⚠️ **Un libellé qui ne tient pas sur une ligne se replie, et il perd le roulé.**
   Le « white-space: nowrap » ci-dessus est ce qui rend l'effet possible : la
   copie de chaque lettre est une ombre posée une ligne plus bas, et la fenêtre
   d'une ligne la garde hors champ. **Un libellé sur deux lignes ferait tomber
   les ombres de la première sur la seconde**, donc l'effet et le repli
   s'excluent.

   ⚠️ **Et sans ce repli, un libellé long ne se réduit jamais.** Relevé le
   16 septembre 2026 à 375 px : le bouton « Vos fondations en place, en moins de
   deux mois » de la section 07 faisait **436 px de large pour 335 disponibles**,
   et il poussait la page entière de côté. Le bouton ne pouvait pas rétrécir, le
   libellé ne pouvait pas se casser, et rien ne le signalait.

   **C'est le même arbitrage que sous « prefers-reduced-motion »** : le roulé est
   un enrichissement, la lisibilité du bouton ne l'est pas. Le script mesure,
   pose le marqueur, et ne découpe que ce qui tient. */
.roule[data-plie] {
  overflow: visible;
  white-space: normal;
  block-size: auto;
  text-align: center;
}

.lettre {
  display: inline-block;
  position: relative;
  text-shadow: 0 1.34em currentColor;
  text-shadow: 0 1lh currentColor;
  transform: translateY(0) rotate(.001deg);
  /* ⚠️ La courbe est celle de pulsor.agency depuis le 16 septembre 2026, sur
     demande : « smooth-ease », et non plus le « ease-inout » du système, qui
     était une approximation choisie le 14 pour ne pas ajouter de courbe. */
  transition: transform var(--lettres-duree) var(--lettres-courbe);
  transition-delay: calc(var(--i) * var(--lettres-pas));
}

.bouton:hover .lettre,
.bouton:focus-visible .lettre,
.lien-nav:hover .lettre,
.lien-nav:focus-visible .lettre {
  transform: translateY(-1.34em) rotate(.001deg);
  transform: translateY(-1lh) rotate(.001deg);
}

/* Comme partout ailleurs : on retire le mouvement au lieu de le raccourcir.
   À 0,01 ms, le libellé sauterait d'une ligne à l'autre. La découpe elle-même
   n'a alors pas lieu, et le libellé reste d'un seul tenant. */
@media (prefers-reduced-motion: reduce) {
  .lettre { transition: none; }
  .bouton:hover .lettre,
  .bouton:focus-visible .lettre { transform: none; }
}

/* Le bouton primaire porte le gradient de site.

   Deux règles écrites disent le contraire : la section 2 range le violet dans
   « jamais un bouton », et la 5.1 dit que le gradient site ne va « jamais à
   un bouton ». Arbitré le 14 septembre 2026, et reporté dans CLAUDE.md.

   Le voile noir n'est pas un effet, c'est ce qui rend l'arbitrage tenable.
   En blanc sur l'extrémité bleue du gradient, #0091FF, le contraste tombe à
   3,26 pour 1, sous le seuil AA de 4,5 qu'impose la section 2. Un voile à
   18 % le remonte à 4,6. Il est en ombre intérieure et non en calque de
   fond, parce qu'une ombre se peint au-dessus du fond, sous le texte, suit
   le rayon, et se transitionne, ce qu'un background-image ne fait pas.

   Les états s'assombrissent au lieu de s'éclaircir, à l'inverse du bouton
   noir : sur un fond saturé, éclaircir repasserait sous le seuil AA. */
.bouton-primaire {
  background: var(--bleu);
  color: var(--blanc);
  /* Deux ombres intérieures, et l'ordre compte : la première se peint sur la
     seconde. La lueur blanche court sur les bords, le voile noir couvre tout
     le fond.

     Le voile n'est pas un effet. En blanc sur le bleu du système, #0091FF,
     le contraste tombe à 3,26 pour 1, sous le minimum AA de 4,5 qu'impose la
     section 2. À 18 % il remonte à 4,6. Sans lui, le bouton ne respecte plus
     la règle de contraste de ce même document. */
  /* L'ombre portée est relevée du bouton de pulsor.agency, sur demande. Quatre
     couches, pas une : c'est ce qui la rend molle près du bouton et large
     loin de lui, là où une ombre unique donne un halo plat.

       0,034 à 32 px, 0,11 à 18, 0,195 à 8, 0,22 à 2

     Les distances sont une fois et demie celles du relevé et les opacités
     0,85 fois : l ombre porte plus loin et pèse un peu moins, sur demande.
     Les rapports entre les quatre couches, eux, ne bougent pas, sinon
     l empilement se remet à ressembler à un halo.

     ⚠️ Elle est décalée vers la gauche autant que vers le bas. Ce n'est pas
     un détail de copie : l'original pose une source de lumière en haut à
     droite, et ce bouton est le seul endroit de la page qui en ait une. Le
     jour où une autre ombre entre dans le système, c'est cette direction
     qu'il faudra suivre, ou celle-ci qu'il faudra reprendre.

     ⚠️ Les quatre couches sont des valeurs hors système : le document ne
     définit aucune échelle d'ombre. Elles entrent ici et nulle part ailleurs.

     Les quatre ombres intérieures restent, et elles passent en premier. La
     dernière, le voile noir à 18 %, n'est pas un effet : en blanc sur le bleu
     du système le contraste tombe à 3,26 pour 1, sous le minimum AA de 4,5
     qu'impose la section 2, et le voile le remonte à 4,6. Une ombre portée ne
     change rien au contraste du libellé, donc rien de ce calcul ne bouge. */
  box-shadow:
    inset 0 0 calc(var(--spacing) * 18) rgb(255 255 255 / .58),
    inset 0 0 calc(var(--spacing) * 5) rgb(255 255 255 / .40),
    inset 0 1px 0 rgb(255 255 255 / .70),
    inset 0 0 0 calc(var(--spacing) * 100) rgb(0 0 0 / .18),
    -32px 24px 15px rgb(0 0 0 / .034),
    -18px 14px 14px rgb(0 0 0 / .11),
    -8px 6px 9px rgb(0 0 0 / .195),
    -2px 2px 6px rgb(0 0 0 / .22);
  transition:
    box-shadow var(--motion-instant) var(--ease-out),
    color var(--motion-instant) var(--ease-out);
}

/* Les états s'assombrissent au lieu de s'éclaircir, à l'inverse du bouton
   noir : sur un fond saturé, éclaircir repasserait sous le seuil AA. */
/* Les deux états reprennent la liste entière, ombre portée comprise : une
   règle de survol qui n'énumère que les ombres intérieures efface la portée,
   et le bouton se pose à plat au moment précis où il devrait se lever.

   Elle s'écarte au survol et se resserre au clic, dans la même direction :
   c'est la seule façon qu'un bouton se lève et s'enfonce sans que la source
   de lumière bouge. */
.bouton-primaire:hover {
  box-shadow:
    inset 0 0 calc(var(--spacing) * 22) rgb(255 255 255 / .70),
    inset 0 0 calc(var(--spacing) * 6) rgb(255 255 255 / .50),
    inset 0 1px 0 rgb(255 255 255 / .80),
    inset 0 0 0 calc(var(--spacing) * 100) rgb(0 0 0 / .26),
    -39px 30px 20px rgb(0 0 0 / .042),
    -23px 17px 17px rgb(0 0 0 / .13),
    -9px 8px 12px rgb(0 0 0 / .21),
    -2px 3px 8px rgb(0 0 0 / .24);
}
.bouton-primaire:active {
  box-shadow:
    inset 0 0 calc(var(--spacing) * 14) rgb(255 255 255 / .44),
    inset 0 0 calc(var(--spacing) * 4) rgb(255 255 255 / .30),
    inset 0 1px 0 rgb(255 255 255 / .52),
    inset 0 0 0 calc(var(--spacing) * 100) rgb(0 0 0 / .34),
    -18px 14px 11px rgb(0 0 0 / .034),
    -11px 8px 9px rgb(0 0 0 / .10),
    -5px 3px 6px rgb(0 0 0 / .17),
    -2px 2px 5px rgb(0 0 0 / .20);
}

.bouton-secondaire {
  background: var(--gris-clair);
  color: var(--noir);
}

/* --------------------------------------------------------------------------
   Le bouton à contour, relevé sur un composant fourni le 15 septembre 2026.

   ⚠️ **L'original est un bouton de thème sombre.** Son libellé est en
   rgb(252, 252, 252) sur un fond à 4 % de noir : posé tel quel sur cette page,
   qui est blanche, il donne **1,05 pour 1** de contraste, et la section 2
   impose AA sur tout texte. **Deux valeurs basculent donc, et deux
   seulement** : le liseré passe du blanc à 8 % au noir à 8 %, et le libellé
   prend --noir. Tout le reste est celui de l'original.

   ⚠️ Les trois ombres intérieures blanches sont gardées telles quelles, et
   elles ne se voient pas : sur un fond presque blanc, du blanc à 4 et à 8 %
   n'ajoute rien. Ce sont les reflets de verre de l'original, et le flou
   d'arrière-plan est dans le même cas. Les quatre reviendraient d'eux-mêmes le
   jour où ce bouton passerait sur une surface sombre, et c'est pour ça qu'ils
   restent.

   Le rayon est --r-md, soit 12 px, quand l'original dit 12,37 : la section 2
   veut des rayons multiples de quatre, et trois dixièmes de pixel ne valent
   pas de sortir de l'échelle.

   ⚠️ La garniture de l'original, 10,3 sur 20,6, n'est pas reprise. C'est une
   valeur de son échelle à lui. Chez nous, elle donnerait un bouton de 44 px
   de haut à côté d'un primaire de 56, et la paire du hero se lirait de
   travers. Le bouton garde donc la garniture de sa taille, comme tous les
   autres, et seule sa peau change.

   Les deux états sont PROPOSÉ : l'original n'en donne aucun, et la section 5.5
   veut qu'un bouton en ait.
   -------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------
   Le verre.

   Une seule définition, portée par deux éléments qui n'ont rien à voir : le
   bouton à contour et le label posé au-dessus du titre. C'est la même matière,
   elle s'écrit donc une fois. Le jour où elle change, elle change pour les
   deux, et c'est voulu.

   La recette : une teinte de noir à 4 %, un liseré à 8 %, le flou de ce qui
   passe derrière, et deux reflets blancs qui font l'épaisseur. Rien de tout ça
   n'est une couleur du système : ce sont des transparences posées sur ce qui
   se trouve dessous, donc la matière suit le fond au lieu de le couvrir.
   -------------------------------------------------------------------------- */
.verre {
  background-color: rgb(28 28 28 / .04);
  border: 1px solid rgb(28 28 28 / .08);
  color: var(--noir);
  -webkit-backdrop-filter: blur(25px);
  backdrop-filter: blur(25px);
}

/* ⚠️ **Le label du hero reste en noir, et c'est une exception assumée.** Le
   violet et le bleu ont quitté le premier écran le 15 septembre 2026, sauf sur
   le bouton primaire : c'est un appauvrissement volontaire du haut de page, et
   le label y participe. Les labels des autres sections, eux, ont repris le
   violet le même jour. */
/* ⚠️ **L'exception a été levée puis rétablie le 16 septembre 2026, sur
   demande** : le label du hero garde son verre noir et son encre noire, avec
   les reflets et l'ombre de la pilule d'origine. Tous les autres labels
   prennent la matière violette. */
.s-hero .section-nom {
  color: var(--noir);
  background: rgb(28 28 28 / .04);
  border-color: rgb(28 28 28 / .08);
  box-shadow:
    inset 0 1px 0 0 rgb(255 255 255 / .25),
    inset 0 0 calc(var(--spacing) * 20) 0 rgb(255 255 255 / .25),
    0 calc(var(--spacing) * 2) calc(var(--spacing) * 8) rgb(28 28 28 / .04),
    0 calc(var(--spacing) * 7) calc(var(--spacing) * 35) rgb(28 28 28 / .04);
}

/* ⚠️ **Le violet est pour le label qui ouvre une section, pas pour ceux qui
   vivent dans une carte.** Les quatre numéros de manque et les deux intitulés
   du Duo reviennent donc au noir : ils nomment une carte, ils n'annoncent pas
   un temps de la page. C'est la même pilule et la même graisse, seule l'encre
   sépare les deux emplois. */
.rangee .section-nom,
.duo-part .section-nom {
  color: var(--noir);
  background: rgb(28 28 28 / .04);
  border-color: rgb(28 28 28 / .08);
}

/* La double coque, relevée sur le CTA « Contacter un agent » d'agentfrancais.com
   le 15 septembre 2026. Le bouton n'est pas une boîte, c'en est deux : une
   enveloppe, puis un noyau posé dedans avec une gouttière tout autour.

   ⚠️ **Les deux rayons sont concentriques, et c'est ce qui fait tenir le
   dessin.** Le rayon de l'enveloppe vaut celui du noyau plus la gouttière,
   sinon les deux arrondis ne sont pas parallèles et l'œil voit un liseré qui
   s'épaissit dans les coins. Le relevé dit 17 et 12 pour une gouttière de 5 ;
   la section 2 veut des rayons multiples de quatre, donc c'est 16 et 12 pour
   une gouttière de 4. La règle est respectée et les arrondis restent
   parallèles.

   **Les couleurs et le flou sont les nôtres**, comme demandé : la teinte à 4 %
   de noir sur les deux coques, donc 8 % là où elles se superposent, ce qui
   détache le noyau de son enveloppe sans ajouter de couleur. Le liseré et le
   flou d'arrière-plan ne bougent pas.

   Ce qui vient du relevé : la double ombre portée, très large et très faible,
   et les reflets blancs qui passent de l'enveloppe au noyau, là où ils sont
   dans l'original. */
.bouton.bouton-contour {
  padding: calc(var(--spacing) * 4);
  border-radius: var(--r-lg);
  background-color: rgb(28 28 28 / .04);
  border: 1px solid rgb(28 28 28 / .08);
  color: var(--noir);
  box-shadow:
    0 calc(var(--spacing) * 2) calc(var(--spacing) * 8) rgb(28 28 28 / .04),
    0 calc(var(--spacing) * 7) calc(var(--spacing) * 35) rgb(28 28 28 / .04);
  -webkit-backdrop-filter: blur(25px);
  backdrop-filter: blur(25px);
  transition:
    background-color var(--motion-instant) var(--ease-out),
    box-shadow var(--motion-slow) var(--ease-inout);
}

.bouton-noyau {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: calc(var(--spacing) * 8);
  inline-size: 100%;
  padding: calc(var(--spacing) * 12) calc(var(--spacing) * 24);
  border-radius: var(--r-md);
  background-color: rgb(28 28 28 / .04);
  box-shadow:
    inset 0 1px 0 0 rgb(255 255 255 / .25),
    inset 0 0 calc(var(--spacing) * 20) 0 rgb(255 255 255 / .25);
  transition: background-color var(--motion-instant) var(--ease-out);
}

.bouton-md .bouton-noyau {
  padding: calc(var(--spacing) * 8) calc(var(--spacing) * 16);
}

.bouton-contour:hover  .bouton-noyau { background-color: rgb(28 28 28 / .07); }
.bouton-contour:active .bouton-noyau { background-color: rgb(28 28 28 / .10); }

/* La variante violette du bouton à contour, demandée le 15 septembre 2026 pour
   les quatre rangées du constat : « ça rappelle le violet du logo ». Elle a été
   bleue une heure, et le bleu est parti pour cette raison. Le dessin ne change
   pas, deux coques et le chevron qui se relaie ; seule la couleur bouge.

   ⚠️ **Le voile de contraste du bouton primaire n'est pas repris, et c'est
   mesuré.** Sur le bleu du système, le libellé blanc tombe à 3,23 pour 1 et il
   faut un voile noir à 18 % pour le remonter à 4,61. Sur le violet, **le blanc
   donne 5,64 pour 1 sans rien**. Reprendre le voile ici n'aurait donc aucune
   justification, et il assombrirait précisément ce qu'on vient chercher : le
   violet du logo. Les reflets blancs sont réduits pour la même raison, un
   violet lavé ne rappelle plus rien.

   ⚠️ **Le violet en aplat sur un bouton renverse la règle de la section 2**,
   qui dit qu'il ne sert qu'aux labels, aux gradients et à l'anneau de focus, et
   jamais un bouton. C'est le second renversement de cette règle après le
   gradient du bouton primaire, et il est noté dans CLAUDE.md. */
.bouton.bouton-contour.bouton-violet {
  background-color: rgb(102 71 240 / .10);
  border-color: rgb(102 71 240 / .22);
}

.bouton-violet .bouton-noyau {
  background-color: var(--violet);
  color: var(--blanc);
  box-shadow:
    inset 0 0 calc(var(--spacing) * 20) rgb(255 255 255 / .26),
    inset 0 1px 0 rgb(255 255 255 / .45);
}

/* Les états s'assombrissent au lieu de s'éclaircir, comme sur le primaire :
   sur un fond saturé, éclaircir repasserait vers le seuil AA. */
.bouton-contour.bouton-violet:hover .bouton-noyau {
  background-color: var(--violet);
  box-shadow:
    inset 0 0 calc(var(--spacing) * 22) rgb(255 255 255 / .30),
    inset 0 1px 0 rgb(255 255 255 / .52),
    inset 0 0 0 calc(var(--spacing) * 100) rgb(0 0 0 / .10);
}

.bouton-contour.bouton-violet:active .bouton-noyau {
  background-color: var(--violet);
  box-shadow:
    inset 0 0 calc(var(--spacing) * 16) rgb(255 255 255 / .22),
    inset 0 1px 0 rgb(255 255 255 / .38),
    inset 0 0 0 calc(var(--spacing) * 100) rgb(0 0 0 / .18);
}

/* La variante bleue du bouton à contour, demandée le 15 septembre 2026 pour le
   CTA de la carte Percer. Le dessin ne change pas, deux coques et le chevron qui
   se relaie ; seule la couleur bouge, et elle reprend le remplissage du bouton
   primaire.

   ⚠️ **Le voile noir à 18 % vient avec le bleu et il ne se retire pas**, à
   l'inverse de la variante violette juste au-dessus : en blanc sur le bleu du
   système le libellé tombe à 3,23 pour 1, et le voile le remonte à 4,61. C'est
   la seule valeur de voile de la page, et c'est la même que celle du panneau
   qu'il accompagne.

   ⚠️ **Le bleu plein se pose maintenant à trois endroits** : le bouton
   primaire, le panneau de la carte Percer, et ce bouton. La section 2 n'en
   autorise qu'un, parce qu'il signe l'appel à l'action. C'est la règle qu'il
   faut trancher, et elle est notée dans CLAUDE.md. */
.bouton.bouton-contour.bouton-bleu {
  background-color: rgb(0 145 255 / .10);
  border-color: rgb(0 145 255 / .24);
}

.bouton-bleu .bouton-noyau {
  background-color: var(--bleu);
  color: var(--blanc);
  box-shadow:
    inset 0 0 calc(var(--spacing) * 20) rgb(255 255 255 / .26),
    inset 0 1px 0 rgb(255 255 255 / .45),
    inset 0 0 0 calc(var(--spacing) * 100) rgb(0 0 0 / .18);
}

/* Les états s'assombrissent au lieu de s'éclaircir, comme sur le primaire : sur
   un fond saturé, éclaircir repasserait sous le seuil AA. */
.bouton-contour.bouton-bleu:hover .bouton-noyau {
  box-shadow:
    inset 0 0 calc(var(--spacing) * 22) rgb(255 255 255 / .30),
    inset 0 1px 0 rgb(255 255 255 / .52),
    inset 0 0 0 calc(var(--spacing) * 100) rgb(0 0 0 / .26);
}

.bouton-contour.bouton-bleu:active .bouton-noyau {
  box-shadow:
    inset 0 0 calc(var(--spacing) * 16) rgb(255 255 255 / .22),
    inset 0 1px 0 rgb(255 255 255 / .38),
    inset 0 0 0 calc(var(--spacing) * 100) rgb(0 0 0 / .32);
}

/* Le chevron qui se relaie. Deux exemplaires dans une fenêtre de vingt
   pixels : le premier sort par la droite, le second entre par la gauche, et
   la fenêtre coupe ce qui déborde. Relevé au caractère : le décalage au repos
   vaut moins cent pour cent moins deux pixels, et l'arrivée zéro.

   Il répond aussi au focus clavier, comme le libellé qui roule : un effet qui
   ne répond qu'à la souris dit à qui tabule que le bouton n'a pas bougé. */
.bouton-icone {
  position: relative;
  flex: none;
  inline-size: calc(var(--spacing) * 20);
  block-size: calc(var(--spacing) * 20);
  overflow: hidden;
}

.bouton-icone .fleche {
  position: absolute;
  inset: 1px;
  inline-size: auto;
  block-size: auto;
  /* ⚠️ C est « translate » et non « transform ». Un SVG n honore pas la
     propriete transform posee en CSS : elle se resout en identite, et la
     fleche ne bouge pas d un pixel. La propriete individuelle, elle, marche.
     Mesure a l appui : transform translateX(20px) deplace de 0, translate 20px
     deplace de 20. */
  transition: translate var(--motion-slow) var(--ease-inout);
}

/* Le decalage vaut la largeur de la fenetre, vingt pixels, et il est ecrit
   en pixels et non en pourcentage : sur un SVG, un pourcentage de translation
   se resout sur la boite de reference du dessin et non sur la boite de mise en
   page, et il ne donne pas ce qu on croit. Le releve calculait -20 px. */
.bouton-icone .fleche-2 { translate: calc(var(--spacing) * -20); }

.bouton:hover .bouton-icone .fleche-1,
.bouton:focus-visible .bouton-icone .fleche-1 { translate: calc(var(--spacing) * 20); }

.bouton:hover .bouton-icone .fleche-2,
.bouton:focus-visible .bouton-icone .fleche-2 { translate: 0; }

@media (prefers-reduced-motion: reduce) {
  .bouton-icone .fleche { transition: none; }
  .bouton:hover .bouton-icone .fleche-1,
  .bouton:focus-visible .bouton-icone .fleche-1,
  .bouton-icone .fleche-2 { translate: none; }
  .bouton:hover .bouton-icone .fleche-2,
  .bouton:focus-visible .bouton-icone .fleche-2 { translate: none; }
}
.bouton-secondaire:hover  { background: var(--gris-hover); }
.bouton-secondaire:active { background: var(--gris-actif); }

.bouton[aria-disabled="true"],
.bouton:disabled {
  background: var(--gris-grille);
  color: var(--gris-texte);
  box-shadow: none;
  cursor: not-allowed;
}

/* cta-md porte son propre interligne et son propre interlettrage, relevés du
   Figma : 1,375 et -0,009375 em. Sans ça il hériterait des valeurs de cta-lg. */
.bouton-md {
  font-size: var(--cta-md);
  line-height: 1.375;
  letter-spacing: -.009375em;
  padding: calc(var(--spacing) * 12) calc(var(--spacing) * 20);
}

.boutons {
  display: flex;
  flex-wrap: wrap;
  gap: calc(var(--spacing) * 12);
}

/* --------------------------------------------------------------------------
   Les boutons et les trois preuves, sur la même ligne.

   ⚠️ La classe s appelle preuve-ligne et pas trust, et ce n est pas une
   question de goût : trust, badge, seal et leurs voisins sont des motifs que
   les listes de filtres cosmétiques visent. Un bloqueur qui masque la ligne
   emporterait trois arguments du premier écran sans que personne le voie
   depuis le navigateur du studio. Le vocabulaire du projet suffit, et il ne
   ressemble à aucun motif publicitaire.

   Les preuves sont alignées horizontalement, un filet entre chacune, et un
   filet de plus les sépare des boutons, là où le registre change.

   Elles font 625 px à elles trois, les boutons 437 : il faut donc 1080 px de
   colonne pour tenir la ligne, et c'est la boîte de l'entonnoir qui les
   donne ou non. Au-delà de 1500 px elle se resserre juste assez. En dessous,
   la ligne casse et les preuves passent sous les boutons, toujours
   horizontales : à ces largeurs, tenir la ligne coûterait un cône réduit à
   trois cents pixels, et le décor ne vaut pas la colonne de texte.

   À 14 px le gris lisible donne 5,02 pour 1, et le chiffre passe en noir, ce
   qui le sort de la phrase sans changer de corps.
   -------------------------------------------------------------------------- */
.hero-actions {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: calc(var(--spacing) * 16);
}

/* Le prix passe sous les boutons depuis le 15 septembre 2026. Il tenait à leur
   droite dès 1320 px de fenêtre ; il prend maintenant sa propre ligne à toutes
   les largeurs.

   ⚠️ La rangée est une colonne, et non une ligne qui se casse. Une base à cent
   pour cent aurait fait passer la capsule à la ligne, mais en la faisant
   grandir jusqu'au bord : dans une ligne souple, c'est la base qui donne la
   largeur. En colonne, chacun garde celle de son contenu. */

/* La capsule. Le prix y est en gras d un seul tenant, la durée en romain, et
   un point médian entre les deux. Le repère est une capture fournie le
   15 septembre 2026 ; elle était en blanc sur fond sombre, la page est en noir
   sur blanc, donc c est le liseré du système qui la dessine et non un fond. */
.preuve-ligne {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  gap: calc(var(--spacing) * 4) calc(var(--spacing) * 10);
  margin: 0;
  padding: calc(var(--spacing) * 9) calc(var(--spacing) * 20);
  /* Le rayon des boutons, et non plus une pilule. C'est celui de l'enveloppe du
     bouton à contour, --r-lg, avec qui la capsule partage sa matière et sous
     qui elle se pose. Le bouton primaire, lui, est à --r-md : il n'a pas de
     gouttière autour d'un noyau, donc pas de rayon d'enveloppe. */
  border-radius: var(--r-lg);
  list-style: none;
  font-size: var(--legende);
  line-height: 1.45;
  /* ⚠️ Le gris est plus sombre que le gris lisible du système, et c'est une
     obligation de contraste. La capsule est passée du blanc plein au verre :
     son fond composé tombe à #F1F0ED, et le gris lisible n'y tient plus que
     4,35 pour 1, sous le minimum AA de 4,5. À #5F5F5F il tient 5,62, et cela
     dans le pire cas, capsule posée sur la page sans la lueur qui l'éclaircit.

     C'est la deuxième fois que cette valeur apparaît pour la même raison : la
     mention « Client signé » des cartes de sortie y est passée le 14 septembre
     2026, quand elle a reçu un voile teinté. **Deux emplois, c'est le moment
     d'en faire un jeton**, et c'est à demander.

     La couleur reprend la main sur celle du verre : la classe est déclarée plus
     haut, celle-ci gagne donc par l'ordre. Le chiffre, lui, est en noir, il est
     posé plus bas. */
  color: #5F5F5F;
  /* Le fond et le liseré viennent de la classe « verre ». Ce qui reste ici est
     ce qui donne l'épaisseur : les deux reflets à l'intérieur et la double
     ombre portée, comme sur le label et sur le bouton à contour. */
  box-shadow:
    inset 0 1px 0 0 rgb(255 255 255 / .25),
    inset 0 0 calc(var(--spacing) * 20) 0 rgb(255 255 255 / .25),
    0 calc(var(--spacing) * 2) calc(var(--spacing) * 8) rgb(28 28 28 / .04),
    0 calc(var(--spacing) * 7) calc(var(--spacing) * 35) rgb(28 28 28 / .04);
}

.preuve-ligne li {
  display: flex;
  align-items: baseline;
  gap: calc(var(--spacing) * 7);
  white-space: nowrap;
}

/* Le séparateur est porté par le terme qui suit, et non posé entre deux
   éléments : c'est ce qui le fait disparaître tout seul en début de ligne
   quand la série se casse. */
.preuve-ligne li + li::before {
  content: "·";
  align-self: center;
  margin-inline-end: calc(var(--spacing) * 6);
  color: var(--gris-texte);
}

.preuve-ligne b {
  /* ⚠️ Quatre cents, la plus légère graisse chargée : les fontes variables du
     site couvrent l'axe 400 à 700, une demande de 300 y est ramenée à 400.

     ⚠️ Le prix n'est donc plus distingué que par sa couleur. Il l'était par
     deux choses, le gras et le noir ; il ne reste que le noir contre le gris de
     la durée. C'est assez pour le sortir de la phrase, mais c'est un appui de
     moins, et la balise b ne porte plus de graisse : elle ne dit plus que
     « ceci compte », ce qui reste juste. */
  font-weight: 400;
  color: var(--noir);
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

.lien {
  color: var(--noir);
  text-decoration: underline;
  text-underline-offset: .18em;
  text-decoration-thickness: 1px;
  transition: text-decoration-thickness var(--motion-instant) var(--ease-out);
}
.lien:hover { text-decoration-thickness: 2px; }

/* Le lien de navigation n'est pas souligné au repos : c'est un repère, pas
   un appel. Il le devient au survol. */
/* ⚠️ Relevé sur agentfrancais.com le 15 septembre 2026 : leurs liens de barre
   font 14 px en graisse 400, quand les nôtres faisaient 16 px en 500. Deux
   crans plus bas d'un coup, et c'est ce qui était demandé.

   Leur interlettrage, lui, n'est pas repris : ils posent -0,544 px, soit
   -0,039 em à ce corps, quand la règle du système dit que l'interlettrage se
   desserre à mesure que la taille descend. Le jeton cta, à -0,011 em, est ce
   que le système prévoit à 14 px. */
.lien-nav {
  font-size: var(--cta);
  font-weight: 400;
  color: var(--gris-lisible);
  text-decoration: none;
  transition: color var(--motion-instant) var(--ease-out);
}
.lien-nav:hover { color: var(--noir); }

/* --------------------------------------------------------------------------
   5. En-tête : une barre flottante en pilule.
   Reprise de la forme de la référence, transposée dans le système Percer :
   la référence pose une pilule blanche sur un aplat bleu, ici le fond de
   page est blanc, donc la pilule tient par un trait et une ombre basse.
   Aucun aplat de couleur, aucun compteur de places.
   -------------------------------------------------------------------------- */

/* La barre flotte : le fond reste transparent et c'est la pilule qui porte
   la matière. Le retrait de 16 px est un padding et non un décalage de
   sticky, pour que la barre se colle exactement à 16 px du haut. */
.entete {
  position: sticky;
  top: 0;
  z-index: 50;
  padding-block-start: calc(var(--spacing) * 16);
  /* Le retrait de la barre dans son conteneur. C est lui qui donne la largeur
     de la pilule, et il a été doublé le 15 septembre 2026 : la barre était
     trop large et son milieu se voyait vide. Relevé à 1440 px : la pilule
     passe de 83 % de la fenêtre à 76 %, et de 67 à 66 une fois défilée. */
  --retrait-entete: clamp(0px, calc(12vw - calc(var(--spacing) * 100)), calc(var(--spacing) * 300));
  /* De combien la pilule se resserre encore quand la page défile. Relevé sur
     agentfrancais.com : leur barre passe de 1200 à 956 px, soit 122 de chaque
     côté. La valeur est proportionnelle pour tenir à toutes les largeurs.
     ⚠️ Elle a baissé avec le retrait ci-dessus : les deux se cumulent, et à
     122 de contraction la barre défilée devenait plus étroite que son
     contenu ne le demande. */
  --contraction-entete: clamp(0px, 5vw, calc(var(--spacing) * 90));
  /* L écart entre le contenu et le bord de la pilule, de chaque côté. C est la
     seule valeur qui décide du vide autour du texte : la pilule se pose à cette
     distance du contenu, quelle que soit la largeur de la barre. */
  --marge-pilule: calc(var(--spacing) * 28);
}

/* La barre est nue au-dessus du hero : la marque à gauche au bord de la
   grille, les liens au centre, le bouton à droite. Pas de fond, elle repose
   directement sur le fond du hero. Une fois le hero passé, elle se referme
   en pilule. */
.entete-barre {
  position: relative;
  display: flex;
  align-items: center;
  gap: calc(var(--spacing) * 24);
  min-height: calc(var(--spacing) * 56);
  padding-block: calc(var(--spacing) * 8);
  /* La marque et le bouton rentrent vers le centre. Le retrait est posé sur
     la barre et non sur le conteneur : la pilule, elle, est ancrée à la
     boîte de la barre et garde donc sa largeur. Il s'annule de lui-même
     sous 875 px, là où la place manque déjà entre la marque et les liens. */
  padding-inline: calc(var(--retrait-entete) + var(--contraction-pose, 0px));
  /* Le contenu se resserre en même temps que la pilule, et de la même
     quantité : l'écart entre les deux ne bouge donc pas d'un pixel. */
  transition: padding var(--motion-slow) var(--ease-inout);
}

/* Le groupe de gauche : la marque, le filet, les liens. */
.entete-gauche {
  display: flex;
  align-items: center;
  gap: calc(var(--spacing) * 16);
}

/* Le filet entre la marque et les liens. Douze pour cent du noir, relevé
   tel quel : plus clair il disparaît, plus sombre il se lit comme une
   séparation de sections. */
.entete-filet {
  flex: none;
  inline-size: 1px;
  block-size: calc(var(--spacing) * 16);
  background: rgb(28 28 28 / .12);
}

/* La pilule est un calque derrière la barre, et non le fond de la barre
   elle-même. C'est ce qui permet de la faire apparaître sans rien déplacer :
   si la pilule était le fond, il faudrait animer le remplissage intérieur,
   et la marque comme le bouton glisseraient de vingt-quatre pixels au
   moment de la bascule. Là, seul le calque change. */
.entete-barre::before {
  content: "";
  position: absolute;
  z-index: -1;
  inset-block: calc(var(--spacing) * -6);
  /* La pilule se pose à --marge-pilule du contenu, et non à une distance du
     bord de la barre : le retrait et la contraction entrent tous les deux dans
     le calcul, donc l écart au texte ne bouge pas, quelle que soit la
     largeur. */
  inset-inline: calc(var(--retrait-entete) + var(--contraction-pose, 0px) - var(--marge-pilule));
  border-radius: var(--r-full);
  background: transparent;
  border: 1px solid transparent;
  box-shadow: 0 0 0 rgb(28 28 28 / 0);
  transition:
    background-color var(--motion-slow) var(--ease-inout),
    border-color var(--motion-slow) var(--ease-inout),
    box-shadow var(--motion-slow) var(--ease-inout),
    inset var(--motion-slow) var(--ease-inout);
}

/* La pose, relevée sur agentfrancais.com le 15 septembre 2026.

   ⚠️ **L'ombre est à six couches, et l'échelle est ce qui compte** : le flou
   double à chaque couche, 1, 3, 6, 12, 24, et l'étalement vaut chaque fois la
   moitié du flou en négatif. C'est ce qui la rend serrée sous la pilule et
   très large loin d'elle. Une ombre unique à la même opacité donne un halo.

   La première couche, à zéro de flou et un pixel d'étalement, est un anneau :
   elle remplace le liseré, qui redevient donc transparent. Les couleurs de
   l'original sont des gris bleutés ; ce sont les nôtres qui sont posées ici,
   le noir du système, parce qu'aucun bleu n'entre dans le système par une
   ombre. */
.entete[data-pose="oui"] .entete-barre { --contraction-pose: var(--contraction-entete); }

.entete[data-pose="oui"] .entete-barre::before {
  --contraction-pose: var(--contraction-entete);
  background: var(--blanc);
  border-color: transparent;
  box-shadow:
    0 0 0 1px rgb(28 28 28 / .04),
    0 1px 1px -.5px rgb(28 28 28 / .04),
    0 3px 3px -1.5px rgb(28 28 28 / .04),
    0 6px 6px -3px rgb(28 28 28 / .04),
    0 12px 12px -6px rgb(28 28 28 / .04),
    0 24px 24px -12px rgb(28 28 28 / .04);
}

/* Dans Signal, la pilule suit le thème sombre.

   ⚠️ **Elle est translucide et floutée depuis le 16 septembre 2026, sur
   demande.** Le fond du thème à 75 % au lieu de plein, et le flou de ce qui
   défile derrière. Seule la pilule baisse : la marque, les liens et le bouton
   sont posés au-dessus d elle et gardent leur encre pleine.

   ⚠️ **Les 75 % sont un plancher de contraste, pas un goût.** Le titre de la
   section Signal passe sous la barre, en gradient chaud. Au pire, un aplat
   orange du gradient derrière la pilule, sans compter le flou qui le dilue :
   les liens en gris du thème y tiennent **4,69 pour 1** à 75 %, et tombent à
   **4,45** à 72 %. Descendre plus bas demande de refaire ce calcul.

   Le liseré passe au blanc à 10 %, la valeur du bouton à contour du thème, et
   un reflet blanc court en haut, comme sur le noyau du bouton : c est ce qui
   dessine une matière de verre au lieu d un trou dans la page. */
.entete[data-pose="oui"][data-theme="signal"] .entete-barre::before {
  background: rgb(17 17 17 / .75);
  border-color: rgb(255 255 255 / .10);
  box-shadow: inset 0 1px 0 0 rgb(255 255 255 / .06);
  -webkit-backdrop-filter: blur(20px);
  backdrop-filter: blur(20px);
}

.marque {
  display: inline-flex;
  align-items: center;
  gap: calc(var(--spacing) * 10);
  text-decoration: none;
  font-family: var(--font-titre);
  font-weight: var(--graisse-titre);
  font-size: var(--titre-sm);
  letter-spacing: -.03em;
  color: var(--noir);
  transition: color var(--motion-slow) var(--ease-inout);
}

.marque-mot { display: inline-block; }

.chevron {
  display: block;
  width: calc(var(--spacing) * 27);
  height: auto;
  flex: none;
}

/* Les liens sont centrés dans la barre, le bouton reste à droite.

   Le centrage est absolu et non par marges automatiques. Avec des marges
   auto, les liens se centrent dans l'espace qui reste entre la marque et le
   bouton : la marque est étroite, le bouton est large, et l'ensemble tombe
   nettement à gauche du milieu. Sorti du flux, le bloc se cale sur le milieu
   de la barre, quelle que soit la largeur de ce qui l'entoure. */
.entete-nav {
  display: flex;
  align-items: center;
  gap: calc(var(--spacing) * 24);
}

.entete-nav .lien-nav { transition: color var(--motion-slow) var(--ease-inout); }

.entete-cta { margin-inline-start: auto; }

/* Le doublon du bouton, réservé au menu déplié des petits écrans. */
.entete-nav-cta { display: none; }

/* Dans une pilule, le bouton est une pilule, et son noyau aussi : deux
   arrondis concentriques restent parallèles à rayon plein comme à douze. */
.entete .bouton { border-radius: var(--r-full); }
.entete .bouton-noyau { border-radius: var(--r-full); }

/* L'en-tête au thème Signal. Les couleurs viennent du jeu sombre, pas de
   littéraux : c'est la même palette que la section. */
/* Le sombre est porté par la pilule, pas par toute la largeur : derrière
   l en-tete, c est deja la section Signal qui defile. */
.entete[data-theme="signal"] .marque { color: var(--texte); }
.entete[data-theme="signal"] .lien-nav { color: var(--texte-secondaire); }
.entete[data-theme="signal"] .lien-nav:hover { color: var(--texte); }
/* Sur le theme Signal, le gradient de site n a rien a faire : la section a
   son propre gradient, le chaud. Le bouton repasse en aplat clair. */
.entete[data-theme="signal"] .bouton-primaire { background: var(--texte); color: var(--fond); box-shadow: none; }
.entete[data-theme="signal"] .bouton-primaire:hover { background: var(--blanc); box-shadow: none; }
.entete[data-theme="signal"] .bouton-secondaire { background: var(--surface-haute); color: var(--texte); }

/* Le filet entre la marque et les liens passe au blanc, à la même valeur :
   du noir à 12 % sur du sombre ne se voit pas. */
.entete[data-theme="signal"] .entete-filet { background: rgb(255 255 255 / .12); }

/* La flèche du bouton. Elle avance de deux pixels au survol, en instant :
   c'est un changement d'état, pas une apparition. */
.fleche {
  width: calc(var(--spacing) * 14);
  height: calc(var(--spacing) * 14);
  flex: none;
  transition: transform var(--motion-instant) var(--ease-out);
}

.bouton:hover .fleche { transform: translateX(calc(var(--spacing) * 2)); }

/* Le bouton d'ouverture du menu, sous 900 px. */
.entete-menu { display: none; }

/* --------------------------------------------------------------------------
   6. StatRow : 2 à 6 tuiles, poids visuel strictement égal.
   Aucune tuile mise en avant, aucune atténuée, aucune flèche, aucun
   graphique de progression. Une tuile « n.c. » a le même corps et la même
   graisse que les autres.
   -------------------------------------------------------------------------- */

.stat-row {
  display: grid;
  grid-template-columns: repeat(var(--stat-colonnes, 3), minmax(0, 1fr));
  gap: 1px;
  background: var(--gris-grille);
  border: 1px solid var(--gris-grille);
  border-radius: var(--r-lg);
  overflow: hidden;
}

.stat {
  display: flex;
  flex-direction: column;
  gap: calc(var(--spacing) * 8);
  background: var(--blanc);
  padding: calc(var(--spacing) * 32) calc(var(--spacing) * 28);
}

.stat-valeur {
  font-family: var(--font-titre);
  font-weight: var(--graisse-titre);
  font-size: var(--display-4xl);
  line-height: var(--lh-display);
  letter-spacing: var(--ls-display);
  color: var(--noir);
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

.stat-libelle {
  font-size: var(--legende);
  line-height: 1.45;
  color: var(--gris-lisible);
}

/* Échelle compacte. display-4xl est l'échelle des chiffres seuls, mais un
   nombre à cinq ou six glyphes ne tient pas dans un tiers de la grille à
   cette taille. L'échelle baisse alors sur toute la rangée, jamais sur une
   tuile : le poids visuel reste strictement égal, c'est la règle du
   composant et c'est ce qui rend les bons chiffres croyables. */
.stat-row[data-echelle="compact"] .stat-valeur { font-size: var(--display-2xl); }

/* --------------------------------------------------------------------------
   7. LeverGrid : quatre entrées, jamais trois, jamais cinq.

   ⚠️ **Elles passent en sélecteur épinglé le 15 septembre 2026, sur demande,
   d'après une référence fournie.** C'était une grille réglée qui montrait les
   quatre d'un coup. Un levier se lit maintenant à la fois, et on en change de
   deux façons : le bouton, ou le défilement.

   ⚠️ **La section 04 dit que les quatre entrées ne se replient jamais dans un
   accordéon, parce que c'est le détail visible qui fait la preuve.** Un
   sélecteur n'en montre qu'une : la règle est cassée. **Ce qui la tient
   encore** : le lecteur ne peut pas quitter la section sans être passé sur les
   quatre, puisque le défilement les traverse une par une. Il ne choisit pas ce
   qu'il voit, il choisit l'ordre.

   ⚠️ **L'épinglage est un enrichissement, jamais la condition d'accès au
   contenu.** Sans script, sous 1024 px et sous « prefers-reduced-motion », le
   marqueur n'est pas posé : les quatre s'empilent et la barre disparaît. Le
   lecteur passe alors sur les quatre en descendant, ce qui est la même
   garantie par un autre moyen.

   ⚠️ **Ce n'est pas une apparition au défilement.** Les quatre panneaux sont
   dans le document dès la première image, aucun n'attend d'être découvert : ce
   qui change, c'est celui qui est au premier plan. La règle de la section 2
   vise ce qui se montre en descendant, et elle tient.
   -------------------------------------------------------------------------- */
.piliers { flex: none; }

/* Épinglé, le bloc dure quatre écrans et son contenu en occupe un seul. La
   hauteur est en « svh » : sur un téléphone, « vh » compte la barre d'adresse
   rétractée et la scène déborde d'une centaine de pixels. */
.piliers[data-arme] {
  block-size: calc(var(--piliers-nb, 4) * 100svh);
}

.piliers[data-arme] .piliers-colle {
  position: sticky;
  inset-block-start: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: calc(var(--spacing) * 40);
  block-size: 100svh;
}

/* ⚠️ **Sur une fenêtre basse, la scène se resserre, elle ne se coupe pas.** Un
   « overflow: hidden » sur le bloc épinglé masquait le débordement au lieu de
   le régler : relevé à 1440 par 760, le titre du levier était tranché en haut
   sans que rien ne le signale. **C'est le cadre du visuel qui rend la hauteur**,
   parce que c'est lui qui en a le plus : en 16/9 il fait quatre-vingts pixels
   de moins qu'en 16/11. Les deux titres descendent d'un cran avec lui, et le
   rythme du bloc se serre.

   Relevé le 15 septembre 2026 à 1440 par 760 : la scène tient dans 596 px pour
   760 disponibles. **Aucun overflow n'est posé**, donc un débordement futur se
   verra au lieu d'être avalé. */
@media (max-height: 900px) {
  .piliers[data-arme] .piliers-colle { gap: calc(var(--spacing) * 28); }
  .piliers[data-arme] .piliers-tete h2 { font-size: var(--titre-md); }
  .piliers[data-arme] .pilier-visuel { --cadre-rapport: 16 / 9; }
  .piliers[data-arme] .pilier-loi { font-size: var(--titre-md); }
  .piliers[data-arme] .pilier { gap: calc(var(--spacing) * 40); }
}

.piliers-colle {
  display: flex;
  flex-direction: column;
  gap: calc(var(--spacing) * 48);
}

/* ⚠️ **La barre d'onglets est commune depuis le 16 septembre 2026** : elle
   servait aux quatre leviers, elle sert aussi à Signal. Les classes ont donc
   perdu le mot « pilier ». Une seconde barre écrite à côté aurait fini par
   diverger de celle-ci, comme deux copies d'un même dégradé.

   **Ce qui reste propre aux leviers, c'est son affichage** : là-bas, elle
   n'existe que quand le script arme l'épinglage, parce que quatre boutons qui
   ne font rien valent moins que rien. Ailleurs, elle est là. */
.onglets {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-self: center;
  gap: calc(var(--spacing) * 4);
  padding: calc(var(--spacing) * 5);
  border-radius: var(--r-full);
  /* La teinte de verre du site, celle de la pilule des labels. */
  background: rgb(28 28 28 / .04);
  border: 1px solid rgb(28 28 28 / .08);
}

/* Aux leviers, la barre attend que le script arme l'épinglage. */
.piliers:not([data-arme]) .onglets { display: none; }

.onglet {
  display: inline-flex;
  align-items: center;
  gap: calc(var(--spacing) * 8);
  padding: calc(var(--spacing) * 10) calc(var(--spacing) * 20);
  border: 0;
  border-radius: var(--r-full);
  background: transparent;
  color: var(--gris-lisible);
  font-family: var(--font-corps);
  font-size: var(--cta-md);
  font-weight: 500;
  letter-spacing: -.009375em;
  cursor: pointer;
  transition:
    color var(--motion-instant) var(--ease-out),
    background-color var(--motion-instant) var(--ease-out),
    box-shadow var(--motion-instant) var(--ease-out);
}

/* L'onglet tenu est du papier posé sur le verre : c'est le device du site,
   une surface plus claire que son fond avec la lueur et l'ombre des cartes.
   **Aucune couleur ne désigne l'onglet tenu**, la référence en met une par
   onglet et la section 2 ne le permet pas. */
.onglet[data-actif] {
  background: var(--blanc);
  color: var(--noir);
  box-shadow: var(--lueur-carte-douce), var(--ombre-carte);
}

/* ⚠️ **Les numéros sont remplacés par une icône le 15 septembre 2026, sur
   demande.** Les onglets portaient 01 à 04 en gris, puis des emoji quelques
   minutes, puis ceci. Chacun porte le signe de son levier : la carte pour
   Cadrer, la palette pour Valoriser, la base pour Centraliser, la loupe pour
   Capter.

   ⚠️ **Les emoji sont sortis parce qu'ils ne se commandent pas.** Un emoji est
   une image dessinée par la police du poste : sa couleur ne se redéfinit pas,
   elle change d'un système à l'autre, et quatre glyphes en couleur entraient
   dans une palette qui n'en a aucun. **Un tracé coolicons prend l'encre qu'on
   lui donne**, il suit donc la règle du site au lieu de s'y ajouter, et il
   retrouve le violet de l'onglet tenu que les emoji avaient fait perdre.

   **Les quatre tracés viennent de coolicons**, comme la coche des panneaux et
   les trois icônes du constat, et ils sont recopiés dans le balisage : aucun
   fichier, aucune police d'icônes, aucune dépendance.

   ⚠️ **La boussole n'est pas reprise pour Cadrer, et c'est voulu** : elle est
   déjà sur le premier constat, « impossible de savoir par où commencer ». Le
   même tracé sur le manque et sur le levier qui y répond ferait lire un lien
   qui n'est pas celui-là. La carte dit la même chose au futur.

   **Ils ne portent aucune information que le libellé ne dise déjà**, donc ils
   sortent de l'arbre d'accessibilité : le nom du bouton reste « Cadrer ».

   ⚠️ **Le violet ne sort que sur l'onglet tenu.** Sur le blanc de l'onglet il
   tient **5,66 pour 1**, bien au-dessus des 3 pour 1 qu'un élément graphique
   porteur de sens demande. Au repos l'icône reste grise : quatre violets d'un
   coup se disputeraient l'attention. */
.onglet-icone {
  flex: none;
  display: inline-flex;
  color: var(--gris-texte);
  transition: color var(--motion-instant) var(--ease-out);
}

.onglet-icone svg {
  inline-size: calc(var(--spacing) * 18);
  block-size: calc(var(--spacing) * 18);
}

.onglet[data-actif] .onglet-icone { color: var(--violet); }

/* La scène. Épinglée, les quatre panneaux se superposent sur la même cellule
   de grille : **la scène prend alors la hauteur du plus haut des quatre**, et
   elle ne saute pas d'un levier à l'autre. Déroulée, ils s'empilent. */
.piliers-scene {
  display: grid;
  gap: var(--rythme-bloc);
  list-style: none;
  margin: 0;
  padding: 0;
}

.piliers[data-arme] .piliers-scene { gap: 0; }

.piliers[data-arme] .pilier {
  grid-area: 1 / 1;
  opacity: 0;
  /* La visibilité sort le panneau au repos de l'ordre de tabulation et de la
     voix. Elle bascule à la fin du fondu en sortie, et tout de suite en
     entrée, sinon le panneau qui arrive reste muet le temps de la transition. */
  visibility: hidden;
  transition:
    opacity var(--motion-quick) var(--ease-out),
    visibility 0s linear var(--motion-quick);
}

.piliers[data-arme] .pilier[data-actif] {
  opacity: 1;
  visibility: visible;
  transition-delay: 0s;
}

.pilier {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: calc(var(--spacing) * 56);
  align-items: center;
}

/* ⚠️ **Le cadre est en 16/11 et non en 4/3.** Épinglé, tout doit tenir dans un
   écran : l'intitulé, le titre, la barre et le panneau. Un cadre en 4/3 prenait
   soixante pixels de plus en hauteur, et la scène débordait sous 900 px de
   fenêtre. */
.pilier-visuel { --cadre-rapport: 16 / 11; }

/* L'en-tête du bloc, épinglé avec le reste. Son titre ne prend pas la mesure
   des 32 caractères : il tiendrait sur quatre lignes et mangerait la scène. */
.piliers-tete { align-items: center; text-align: center; }
.piliers-tete .section-nom { align-self: center; }
.piliers-tete h2 { max-width: calc(var(--spacing) * 880); }

.pilier-texte {
  display: flex;
  flex-direction: column;
  gap: calc(var(--spacing) * 24);
  align-items: start;
}

.pilier-loi {
  font-size: var(--titre-lg);
  line-height: var(--lh-titre);
  letter-spacing: var(--ls-titre-petit);
  max-width: var(--mesure-titre);
}

.pilier-points {
  display: flex;
  flex-direction: column;
  gap: calc(var(--spacing) * 16);
  list-style: none;
  margin: 0;
  padding: 0;
}

.pilier-points li {
  display: flex;
  align-items: start;
  gap: calc(var(--spacing) * 14);
  font-size: var(--corps);
  line-height: var(--lh-corps);
  color: var(--gris-lisible);
  max-width: var(--mesure-corps);
}

/* La coche reprend le tracé de coolicons déjà recopié ailleurs, et l'encre de
   son texte : **ni pastille verte, ni couleur d'état**, que la section 2
   écarte. C'est la forme qui dit la coche. */
.pilier-coche {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: calc(var(--spacing) * 24);
  block-size: calc(var(--spacing) * 24);
  margin-block-start: calc(var(--spacing) * 2);
  border-radius: var(--r-full);
  border: 1px solid var(--gris-grille);
  color: var(--noir);
}

.pilier-coche svg {
  inline-size: calc(var(--spacing) * 14);
  block-size: calc(var(--spacing) * 14);
}

.pilier-quoi { color: var(--noir); font-weight: 700; }

/* La séparation fondations contre moteur : elle répond à « pourquoi je paye
   encore après la livraison », et elle se lit ici, pas seulement dans l'offre. */
/* ⚠️ **Ce bloc n'est plus dans la page depuis le 15 septembre 2026, sur
   demande.** Ses règles restent ici : le document dit que cette séparation doit
   être lisible dans la section 04, c'est la seule réponse propre à « pourquoi
   je paye encore après la livraison », et elle peut revenir. **Si elle ne
   revient pas, ces règles sortent avant la mise en ligne.**

   ⚠️ **Le bloc des fondations contre le moteur prend le traitement des leviers,
   sur demande du 15 septembre 2026.** C'étaient deux pavés de gris clair posés
   côte à côte ; c'est maintenant une carte en deux temps, avec le même dégradé
   continu, le même filet d'un pixel entre les deux et le même rayon que le bloc
   au-dessus. Les deux blocs se répondent, et celui-ci cesse de ressembler à un
   encadré technique.

   **Les deux moitiés restent de poids strictement égal** : c'est la séparation
   qui répond à « pourquoi je paye encore après la livraison », et une moitié
   mise en avant ferait pencher la réponse. */
.moteur {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0;
  background-color: transparent;
  background-image: var(--fond-carte);
  border: 1px solid var(--gris-grille);
  border-radius: var(--r-xl);
  overflow: hidden;
  box-shadow: var(--lueur-carte), var(--ombre-carte);
}

.moteur-part {
  display: flex;
  flex-direction: column;
  gap: calc(var(--spacing) * 8);
  padding: calc(var(--spacing) * 32);
  border-inline-end: 1px solid var(--gris-grille);
}

.moteur-part:last-child { border-inline-end: 0; }

.moteur-part p { max-width: none; }

/* --------------------------------------------------------------------------
   8. CardGrid : 2 à 4 cartes de même hauteur, aucune mise en avant.
   -------------------------------------------------------------------------- */

/* Le nombre de colonnes est porté par un attribut et non par un style en
   ligne : un style en ligne l'emporterait sur les points de rupture, et la
   grille resterait à quatre colonnes sur un téléphone. */
.card-grid {
  display: grid;
  grid-template-columns: repeat(var(--cartes-colonnes, 3), minmax(0, 1fr));
  gap: calc(var(--spacing) * 24);
}

.card-grid[data-colonnes="2"] { --cartes-colonnes: 2; }
.card-grid[data-colonnes="4"] { --cartes-colonnes: 4; }
.stat-row[data-colonnes="2"] { --stat-colonnes: 2; }
.stat-row[data-colonnes="3"] { --stat-colonnes: 3; }
.stat-row[data-colonnes="6"] { --stat-colonnes: 6; }

.carte {
  display: flex;
  flex-direction: column;
  gap: calc(var(--spacing) * 12);
  background: var(--gris-clair);
  border-radius: var(--r-xl);
  padding: calc(var(--spacing) * 28);
}

.carte p { max-width: none; }

.carte-titre {
  font-family: var(--font-titre);
  font-weight: var(--graisse-titre);
  font-size: var(--titre-sm);
  line-height: var(--lh-titre);
  letter-spacing: var(--ls-titre-petit);
}

.carte-texte {
  font-size: var(--corps-sm);
  line-height: 1.5;
  color: var(--gris-lisible);
}

/* Le rattachement au levier se voit dans le composant : c'est lui qui prouve
   que la méthode produit quelque chose. Il pousse en bas de carte pour que la
   ligne soit à la même hauteur sur les six. */
.carte-levier {
  margin-block-start: auto;
  padding-block-start: calc(var(--spacing) * 12);
  font-size: var(--eyebrow);
  font-weight: 700;
  letter-spacing: -.15px;
  color: var(--violet);
}

/* Les cartes des résultats. 16 septembre 2026, sur demande, d'après les
   cartes de l'éligibilité : la matière de verre blanc de .cond-carte, une
   animation qui occupe la carte (resultats-anim.js), et un cadre blanc
   superposé qui porte le pilier, le titre et le sous-titre.

   Le cadre reprend la pilule de l'éligibilité : blanc plein, liseré à 6 %,
   ombre de carte, à 4 px du bord de la carte, rayon de la carte moins cette
   marge pour que les deux arrondis restent parallèles. Il est en bas sur la
   rangée du haut, en haut sur celle du bas, et il mord sur l'animation : la
   zone réserve sous lui la place qu'il recouvre, la scène n'est jamais
   cachée. */
.card-grid-anim {
  --res-marge: calc(var(--spacing) * 4);
  --res-rayon-int: calc(var(--r-xl) - var(--res-marge));
  --res-morsure: calc(var(--spacing) * 28);
  gap: calc(var(--spacing) * 24);
}

.card-grid-anim .res-carte {
  gap: 0;
  padding: var(--res-marge);
  border-radius: var(--r-xl);
  background-color: transparent;
  background-image: linear-gradient(
    to top,
    rgb(255 255 255 / .90) 0%,
    rgb(255 255 255 / .82) 100%);
  -webkit-backdrop-filter: blur(25px);
  backdrop-filter: blur(25px);
  box-shadow: var(--lueur-carte), var(--ombre-carte);
}

/* La carte s'illumine au survol, de l'intérieur, 17 septembre 2026, sur
   demande : rien ne sort de la carte. Une lueur intérieure aux couleurs du
   gradient de site monte depuis les bords, et une lumière suit la souris
   (--lx, --ly, posés par resultats-anim.js). Aucune teinte ne touche un
   texte : la lumière passe sous le cadre blanc opaque et sous la scène. */
.card-grid-anim .res-carte {
  position: relative;
  box-shadow:
    var(--lueur-carte),
    inset 0 0 0 1px rgb(102 71 240 / 0),
    inset 0 0 calc(var(--spacing) * 28) 0 rgb(102 71 240 / 0),
    inset 0 calc(var(--spacing) * -40) calc(var(--spacing) * 60) calc(var(--spacing) * -20) rgb(0 145 255 / 0),
    var(--ombre-carte);
  transition: box-shadow var(--motion-slow) var(--ease-out);
}

.card-grid-anim .res-carte::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: radial-gradient(
    circle calc(var(--spacing) * 240) at var(--lx, 50%) var(--ly, 40%),
    rgb(0 145 255 / .22),
    rgb(102 71 240 / .10) 45%,
    transparent 75%);
  opacity: 0;
  transition: opacity var(--motion-slow) var(--ease-out);
  pointer-events: none;
}

.card-grid-anim .res-carte:is(:hover, :focus-within) {
  box-shadow:
    var(--lueur-carte),
    inset 0 0 0 1px rgb(102 71 240 / .18),
    inset 0 0 calc(var(--spacing) * 28) 0 rgb(102 71 240 / .14),
    inset 0 calc(var(--spacing) * -40) calc(var(--spacing) * 60) calc(var(--spacing) * -20) rgb(0 145 255 / .18),
    var(--ombre-carte);
}

.card-grid-anim .res-carte:is(:hover, :focus-within)::before { opacity: 1; }

/* La zone de l'animation, sans fond : la scène est posée sur le verre. */
.res-carte .carte-anim.carte-anim {
  --cadre-rapport: 4 / 3;
  flex: none;
  min-block-size: calc(var(--spacing) * 272);
  inline-size: 100%;
  max-width: none;
  margin: 0;
  border: 0;
  border-radius: var(--res-rayon-int);
  background: transparent;
}

.res-carte.carte-anim-haut .carte-anim.carte-anim {
  --cadre-rapport: 4 / 3.4;
  padding-block-end: calc(var(--spacing) * 20 + var(--res-morsure));
}

.res-carte.carte-anim-bas .carte-anim.carte-anim {
  --cadre-rapport: 4 / 3.4;
  padding-block-start: calc(var(--spacing) * 20 + var(--res-morsure));
}

/* Le cadre superposé. Il grandit pour que les cartes d'une rangée aient la
   même hauteur sans laisser de vide sous le texte. */
.res-cadre {
  position: relative;
  z-index: 1;
  flex: 1 0 auto;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: calc(var(--spacing) * 8);
  margin-inline: calc(var(--spacing) * 8);
  padding: calc(var(--spacing) * 20) calc(var(--spacing) * 22) calc(var(--spacing) * 22);
  border-radius: calc(var(--res-rayon-int) - var(--spacing) * 4);
  background: var(--blanc);
  border: 1px solid rgb(28 28 28 / .06);
  box-shadow: var(--ombre-carte);
}

.carte-anim-haut .res-cadre {
  margin-block: calc(var(--res-morsure) * -1) calc(var(--spacing) * 4);
}

.carte-anim-bas .res-cadre {
  margin-block: calc(var(--spacing) * 4) calc(var(--res-morsure) * -1);
}

.res-cadre .carte-titre { margin-block-start: calc(var(--spacing) * 2); }

/* Les six cadres ont la même hauteur, 17 septembre 2026, sur demande : le
   titre et le texte réservent chacun deux lignes, qu'ils les occupent ou
   non. Les titres tombent à la même hauteur, les textes aussi, et le cadre
   ne remonte plus plus haut sur une carte que sur sa voisine. Sur plusieurs
   colonnes seulement : empilées, les cartes n'ont rien à aligner. */
@media (min-width: 768px) {
  .res-cadre .carte-titre,
  .res-cadre .carte-texte {
    min-block-size: 2lh;
    text-wrap: balance;
  }
}

/* Le chiffre en coin, 17 septembre 2026, sur demande : le label du pilier
   sort, l'ancien titre devient un chiffre posé en haut à droite, sa légende
   dessous, et un vrai H3 prend sa place. Il est dernier dans le balisage,
   pour que la voix lise le titre d'abord, et remonte par `order`. */
.res-stat {
  order: -1;
  align-self: flex-end;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  margin: 0;
  text-align: right;
}

.res-stat-valeur {
  font-family: var(--font-titre);
  font-weight: var(--graisse-titre);
  font-size: var(--titre-lg);
  line-height: 1;
  letter-spacing: -.02em;
  color: var(--noir);
  white-space: nowrap;
}

.res-stat-unite {
  margin-block-start: calc(var(--spacing) * 2);
  font-size: var(--text-xs);
  line-height: 1.4;
  color: var(--gris-lisible);
}

/* Les six cartes tiennent dans un seul écran, 17 septembre 2026, sur
   demande. Chaque carte vaut la moitié de la hauteur de fenêtre, moins la
   barre en haut (96 px, la réserve des leviers), l'écart entre les rangées
   et une marge basse. La scène prend ce qui reste sous le cadre de texte,
   qui se resserre. Sur une fenêtre trop basse ou étroite, la règle ne
   s'applique pas : la scène garderait trop peu de place pour se lire. */
@media (min-width: 1024px) and (min-height: 700px) {
  .card-grid-anim {
    --res-hauteur: calc((100svh - 96px - var(--spacing) * 24 - var(--spacing) * 16) / 2);
    --res-morsure: calc(var(--spacing) * 20);
  }

  .card-grid-anim .res-carte { block-size: var(--res-hauteur); }

  .res-carte .carte-anim.carte-anim {
    flex: 1 1 0;
    aspect-ratio: auto;
    min-block-size: 0;
  }

  .res-carte.carte-anim-haut .carte-anim.carte-anim {
    padding-block: calc(var(--spacing) * 16) calc(var(--spacing) * 12 + var(--res-morsure));
  }

  .res-carte.carte-anim-bas .carte-anim.carte-anim {
    padding-block: calc(var(--spacing) * 12 + var(--res-morsure)) calc(var(--spacing) * 16);
  }

  .res-cadre {
    flex: none;
    gap: calc(var(--spacing) * 4);
    padding: calc(var(--spacing) * 14) calc(var(--spacing) * 20) calc(var(--spacing) * 16);
  }

  .res-stat-valeur { font-size: var(--titre-md); }
}

/* --------------------------------------------------------------------------
   9. Duo : deux colonnes de poids strictement égal, y compris quand l'une
   est là pour perdre la comparaison.
   -------------------------------------------------------------------------- */

.duo {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1px;
  background: var(--gris-grille);
  border: 1px solid var(--gris-grille);
  border-radius: var(--r-xl);
  overflow: hidden;
}

.duo-part {
  display: flex;
  flex-direction: column;
  gap: calc(var(--spacing) * 12);
  background: var(--blanc);
  padding: calc(var(--spacing) * 36);
}

.duo-part p { max-width: none; }

/* --------------------------------------------------------------------------
   10. Frise : la timeline de l'offre, en vue Gantt.

   Quatre temps en colonnes, quatre leviers en barres décalées au-dessus, et
   sous chaque colonne la ligne de vérification du composant, qui dit toujours
   « Signal ». Relevé sur une capture fournie le 15 septembre 2026.

   ⚠️ **Les barres, les traits et les colonnes sont les enfants d'une seule
   grille**, et c'est ce qui les empêche de se désaligner. Deux grilles
   superposées auraient deux gabarits, donc deux vérités, et la première
   retouche de gouttière ferait tomber les barres entre les colonnes.

   ⚠️ **Les traits verticaux sont un fond, pas quatre éléments.** Ils tombent
   aux quarts de la largeur, exactement là où les colonnes commencent, et ils
   gardent la valeur du quadrillage du hero, un noir à 6 % : plus clair il
   disparaît, plus sombre il se lit comme une séparation de sections. Le trait
   de droite est une ombre intérieure et non une bordure : une bordure
   ajouterait un pixel à la boîte et décalerait la grille d'autant.
   -------------------------------------------------------------------------- */

.frise {
  position: relative;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  grid-template-rows: repeat(4, auto) auto auto auto;
  column-gap: 0;
  row-gap: calc(var(--spacing) * 10);
  background-image: repeating-linear-gradient(
    to right,
    rgb(28 28 28 / .06) 0 1px,
    transparent 1px 25%);
  box-shadow: inset -1px 0 0 0 rgb(28 28 28 / .06);
}

/* La barre d'un levier : elle part de sa colonne de début et s'arrête à sa
   colonne de fin, les deux posées sur l'élément en jetons de grille. Le
   décalage vertical vient du rang, un par levier, dans l'ordre de la section
   04 : c'est l'escalier de la capture, et c'est l'ordre qui le dessine. */
.frise-barre {
  grid-row: var(--rang);
  grid-column: var(--de) / var(--a);
  align-self: start;
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: calc(var(--spacing) * 4) calc(var(--spacing) * 10);
  min-inline-size: 0;
  margin-inline: calc(var(--spacing) * 16) calc(var(--spacing) * 24);
  padding: calc(var(--spacing) * 10) calc(var(--spacing) * 18);
  border-radius: var(--r-full);
  font-family: var(--font-corps);
  font-size: var(--corps-sm);
  letter-spacing: -.011em;
  text-align: start;
  background-color: rgb(28 28 28 / .04);
  border: 1px solid rgb(28 28 28 / .08);
  color: var(--noir);
  cursor: pointer;
  transition:
    background-color var(--motion-instant) var(--ease-out),
    border-color var(--motion-instant) var(--ease-out),
    color var(--motion-instant) var(--ease-out);
}

.frise-barre-quand {
  font-size: var(--legende);
  color: var(--gris-lisible);
  font-variant-numeric: tabular-nums;
}

/* ⚠️ **Les quatre barres sont des pilules, arrondies aux deux bouts**, sur
   demande du 15 septembre 2026. Centraliser et Capter avaient l'arête droite
   coupée net et collée au bord, pour dire qu'elles ne s'arrêtent pas ; elles
   rentrent dans le rang et prennent le même retrait que les deux autres.

   **Ce qui distingue le moteur des fondations tient donc maintenant à deux
   choses, et à deux seulement** : la longueur de la barre, qui va jusqu'à la
   dernière colonne, et son libellé, qui dit « Mois 2 à 12 » et « Mois 3 à 12 ».
   La note sous la frise est ce qui reste pour le dire en toutes lettres, elle
   ne peut donc pas sauter. */

/* Le bleu est celui des quatre leviers de la section 04, lueur comprise.
   ⚠️ Le voile noir est retiré le 17 septembre 2026, sur demande : le libellé
   blanc sur ce bleu nu tombe à 3,26 pour 1, sous le minimum AA, par choix,
   comme le bouton du hero. */
.frise-barre[aria-pressed="true"] {
  background-color: var(--bleu);
  border-color: transparent;
  color: var(--blanc);
  box-shadow:
    inset 0 1px 0 0 rgb(255 255 255 / .45),
    inset 0 0 calc(var(--spacing) * 12) 0 rgb(255 255 255 / .28);
}

.frise-barre[aria-pressed="true"] .frise-barre-nom { font-weight: 500; }
.frise-barre[aria-pressed="true"] .frise-barre-quand { color: var(--blanc); }

.frise-barre:hover { background-color: rgb(28 28 28 / .08); }

/* L'état s'assombrit au lieu de s'éclaircir, comme le bouton primaire et comme
   les leviers : sur un fond saturé, éclaircir repasserait sous le seuil AA. */
.frise-barre[aria-pressed="true"]:hover {
  background-color: var(--bleu);
  box-shadow:
    inset 0 1px 0 0 rgb(255 255 255 / .55),
    inset 0 0 calc(var(--spacing) * 14) 0 rgb(255 255 255 / .34);
}

/* Une colonne de temps. Les quatre sont étirées à la même hauteur par la
   grille, et la ligne de validation est poussée en bas : les quatre tombent
   donc à la même hauteur quelle que soit la longueur des listes. */
.frise-temp {
  position: relative;
  isolation: isolate;
  grid-row: 5;
  grid-column: var(--col);
  display: flex;
  flex-direction: column;
  gap: calc(var(--spacing) * 16);
  min-inline-size: 0;
  margin-block-start: calc(var(--spacing) * 28);
  padding: calc(var(--spacing) * 20) calc(var(--spacing) * 16);
  border-radius: var(--r-lg);
}

/* ⚠️ **La colonne en avant prend la matière des cartes, et elle la prend par
   un calque.** Un dégradé n'est pas une valeur interpolable : background-image
   ne se transitionne pas et la carte sauterait d'un coup. C'est donc l'opacité
   d'un ::before qui court, comme sur la carte du système en section 02.

   Le calque est en z-index négatif et la colonne est isolée : sans l'isolation
   il passerait derrière le fond de la frise, et les traits verticaux se
   peindraient par-dessus la carte.

   ⚠️ **Rien ne perd de contraste au passage.** La colonne en avant gagne un
   fond, les autres n'en perdent pas : le gris de corps tient 4,77 pour 1 sur le
   blanc cassé de la page et 4,57 sur le haut du dégradé de carte. Atténuer les
   trois autres colonnes aurait été la façon simple de faire ressortir la
   quatrième, et c'est celle qui casse la règle AA. */
.frise-temp::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  background-image: var(--fond-carte);
  box-shadow: var(--lueur-carte), var(--ombre-carte);
  opacity: 0;
  transition: opacity var(--motion-quick) var(--ease-out);
}

.frise-temp[data-actif="oui"]::before { opacity: 1; }

.frise-mois {
  margin: 0;
  max-width: none;
  padding-block-end: calc(var(--spacing) * 12);
  border-block-end: 1px solid var(--gris-grille);
  font-size: var(--corps-sm);
  font-weight: 700;
  color: var(--noir);
}

.frise-liste {
  display: grid;
  gap: calc(var(--spacing) * 10);
  margin: 0;
  padding: 0;
  list-style: none;
}

.frise-liste li {
  position: relative;
  padding-inline-start: calc(var(--spacing) * 16);
  font-size: var(--legende);
  line-height: 1.45;
  color: var(--gris-lisible);
}

/* La puce est décorative : elle n'ajoute rien au sens de la ligne, et c'est ce
   qui l'autorise à rester au gris clair du système. */
.frise-liste li::before {
  content: "";
  position: absolute;
  inset-block-start: .55em;
  inset-inline-start: 0;
  inline-size: calc(var(--spacing) * 6);
  block-size: calc(var(--spacing) * 6);
  border-radius: var(--r-full);
  background: var(--gris-texte);
}

/* La ligne de vérification du composant. Elle dit toujours « Signal », et elle
   est poussée en bas de colonne pour que les quatre s'alignent. */
.frise-validation {
  display: flex;
  align-items: flex-start;
  gap: calc(var(--spacing) * 8);
  margin: 0;
  margin-block-start: auto;
  max-width: none;
  padding-block-start: calc(var(--spacing) * 12);
  border-block-start: 1px solid var(--gris-grille);
  font-size: var(--legende);
  line-height: 1.45;
  color: var(--gris-lisible);
}

.frise-validation strong { color: var(--noir); font-weight: 700; }

.frise-coche {
  flex: none;
  inline-size: calc(var(--spacing) * 14);
  block-size: calc(var(--spacing) * 14);
  margin-block-start: .15em;
  color: var(--noir);
}

/* --------------------------------------------------------------------------
   11. PricingCompare : le comparatif à trois, dans l'UI de la carte d'offre

   **Le principe est celui du 15 septembre 2026** : on ne compare pas un prix à
   un prix, on compare trois façons de couvrir la même fonction. Percer au
   milieu, plus large et en bleu ; l'alternant et le directeur marketing de part
   et d'autre, sur le papier des cartes du site.

   **Le dessin est celui du composant « percer-pricing-card »**, fourni le
   16 septembre 2026 et sorti du composant Framer « v3/card lp » : une coque,
   une carte d'en-tête posée dedans, l'emblème, la boîte teintée qui porte le
   nom, le prix et le bouton, puis la liste à coches en pastille. **C'est sa
   variante étroite qui est reprise**, emblème au-dessus et en-tête centré : une
   colonne de trois n'a pas la largeur de sa variante large, où l'emblème se
   pose à gauche du texte.

   **Ce qui ne vient pas du composant, et ce sont des règles** :

   1. **Ni React ni Montserrat.** La section 5 interdit les dépendances et les
      polices servies par un tiers ; la carte est en HTML, aux jetons.
   2. **Ses thèmes jaune et violet restent dehors**, et ses ombres bleutées
      aussi : aucun bleu n'entre dans le système par une ombre.
   3. **Ses rayons, 40 et 33, ne sont ni concentriques ni multiples de quatre.**
      Ici 32, 24 et 12, pour des gouttières de 8 et de 12 : chaque arrondi est
      parallèle à celui qui l'entoure.
   4. **Ses pastilles illustrées nomment deux offres de l'ancien site.**
      L'emblème porte la marque de Percer au milieu, et un pictogramme neutre
      sur les deux embauches.
   5. **Son texte descend à 10 px sur téléphone.** Rien ici ne passe sous la
      légende.

   ⚠️ **La loi de la carte vide de la section 6 ne décrit plus le code** : les
   deux embauches portent chacune sept croix, en face des sept coches de Percer.
   Elles ne répètent pas « non inclus », elles disent sept choses différentes.
   C'est la règle qu'il faut réécrire dans le vault.
   -------------------------------------------------------------------------- */

.pc-trio {
  display: grid;
  /* La colonne du milieu valait un quart de plus, sur demande du 15 septembre
     2026. ⚠️ **Elle ne vaut plus que 8 % de plus depuis le 16 septembre, sur
     demande** : les deux embauches s'élargissent de 354 à 374 px à 1440, à
     hauteur inchangée. **Percer y perd de la largeur et gagne de la hauteur**,
     443 sur 869 devient 404 sur 892 : c'est maintenant sa hauteur, plus que sa
     largeur, qui le distingue. */
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.08fr) minmax(0, 1fr);
  gap: calc(var(--spacing) * 24);
  /* ⚠️ **Le trio sort du conteneur depuis le 16 septembre 2026, sur demande** :
     les cartes étaient trop fines. Il s'arrête à vingt pixels de chaque bord
     de l'écran, la mesure des panneaux sombres, et il plafonne à 1 440 px pour
     qu'un grand écran n'en fasse pas trois bandeaux. Relevé : **20 à 1 405** à
     1440, 233 à 1 673 à 1920.

     **C'est la seule chose de la section qui sort de la colonne de texte**, et
     c'est un écart avec la règle des panneaux, qui n'élargissent que leur fond.
     Ici ce sont les cartes elles-mêmes : leur bord ne s'aligne plus sur celui
     du titre de la section.

     La moitié de la barre de défilement est rendue par la même variable que la
     lueur de l'éligibilité, sinon les deux bords ne seraient pas à vingt
     pixels. Le premier terme gagne sous 1 480 px, le plafond au-delà. */
  margin-inline: max(
    calc(50% - 50vw + var(--barre-defilement, 0px) / 2 + var(--spacing) * 20),
    calc(50% - var(--spacing) * 720));
  /* Les trois cartes ont la même hauteur : c'est la couleur et la largeur qui
     distinguent Percer, jamais une carte plus haute que ses voisines. */
  align-items: stretch;
}

/* --- La coque ---------------------------------------------------------- */

.carte-offre {
  display: flex;
  flex-direction: column;
  padding: calc(var(--spacing) * 8);
  /* 32 = 24 + 8 : la coque et l'en-tête sont concentriques. */
  border-radius: var(--r-2xl);
  background-image: var(--fond-carte);
  border: 1px solid var(--gris-grille);
  box-shadow: var(--lueur-carte), var(--ombre-carte);
}

.carte-offre p { max-width: none; }

/* La coque de Percer reprend la luminance exacte du papier, `#F1F4F8` contre
   `#F5F5F5` : le gris de corps y garde ses 4,56 pour 1 au point le plus dense,
   et aucun calcul de contraste de la carte n'est à refaire. */
.carte-offre-percer {
  background-image: linear-gradient(to top, #FFFFFF 0%, #F1F4F8 100%);
  border-color: rgb(0 145 255 / .28);
  box-shadow: var(--lueur-carte), var(--ombre-carte-forte);
}

/* --- L'en-tête --------------------------------------------------------- */

.carte-offre-tete {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: calc(var(--spacing) * 12);
  padding: calc(var(--spacing) * 12);
  border-radius: var(--r-xl);
  background: var(--blanc);
  border: 1px solid var(--gris-grille);
  box-shadow: var(--ombre-carte);
}

/* L'emblème. Neutre sur les deux embauches : la couleur de marque ne se pose
   pas sur ce qui est là pour perdre la comparaison. */
.carte-offre-embleme {
  flex: none;
  display: grid;
  place-items: center;
  width: calc(var(--spacing) * 64);
  height: calc(var(--spacing) * 64);
  margin-block-start: calc(var(--spacing) * 4);
  border-radius: var(--r-lg);
  background: var(--gris-clair);
  border: 1px solid rgb(28 28 28 / .06);
  color: var(--gris-lisible);
  box-shadow:
    inset 0 1px 0 0 rgb(255 255 255 / .7),
    inset 0 0 calc(var(--spacing) * 20) 0 rgb(255 255 255 / .5);
}

.carte-offre-embleme svg {
  width: calc(var(--spacing) * 26);
  height: calc(var(--spacing) * 26);
}

.carte-offre-embleme img {
  width: calc(var(--spacing) * 32);
  height: auto;
}

/* ⚠️ **Sur Percer, l'emblème est une tuile de verre et son chevron est blanc**,
   sur demande du 15 septembre 2026. C'est la matière de la classe verre,
   retournée pour un fond saturé : sur du bleu, le verre se fait clair. Le
   chevron tient **3,57 pour 1** sur ce verre posé sur le bleu voilé, au-dessus
   des 3 d'un élément graphique porteur de sens ; éclaircir la teinte du verre
   le fait descendre. */
.carte-offre-tete-bleue .carte-offre-embleme {
  background: rgb(255 255 255 / .16);
  border-color: rgb(255 255 255 / .34);
  color: var(--blanc);
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
  box-shadow:
    inset 0 1px 0 0 rgb(255 255 255 / .45),
    inset 0 0 calc(var(--spacing) * 14) 0 rgb(255 255 255 / .20);
}

/* La boîte teintée. 24 = 12 + 12, concentrique avec l'en-tête. **La teinte est
   celle du composant, 4 %**, prise ici sur l'encre du texte : le gris de corps
   y tient 4,61 pour 1. La monter demande de le remesurer. */
.carte-offre-boite {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: calc(var(--spacing) * 6);
  width: 100%;
  padding: calc(var(--spacing) * 16);
  border-radius: var(--r-md);
  border: 1px solid rgb(28 28 28 / .06);
  background-color: rgb(28 28 28 / .04);
  text-align: center;
}

.carte-offre-surtitre {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: calc(var(--spacing) * 8);
  margin-block-end: calc(var(--spacing) * 4);
  font-size: var(--legende);
  line-height: 1.3;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--gris-lisible);
}

/* ⚠️ **La pastille nomme un engagement, elle ne vante rien.** « Le plus
   populaire » et « offre d'été » sont écartés par la section 2 : deux
   embauches disent leur contrat, Percer dit sa durée. Elle ne s'appelle pas
   `badge` : les listes de filtres cosmétiques visent ce mot.

   ⚠️ **Troisième emploi de `#5F5F5F`, et toujours pour la même raison** : du
   texte secondaire sur une surface teintée. Trois emplois, c'est un jeton qui
   manque, et ça se demande avant de le poser. */
.pc-engagement {
  padding: calc(var(--spacing) * 3) calc(var(--spacing) * 10);
  border-radius: var(--r-full);
  background: var(--blanc);
  border: 1px solid var(--gris-grille);
  letter-spacing: -.011em;
  text-transform: none;
  color: #5F5F5F;
  white-space: nowrap;
}

.carte-offre-titre {
  font-size: var(--titre-sm);
  line-height: var(--lh-titre);
  letter-spacing: var(--ls-titre-petit);
  color: var(--noir);
}

.carte-offre-prix {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: center;
  /* En flex, l'espace de tête du « / an » disparaît : c'est l'écart qui le rend. */
  gap: 0 calc(var(--spacing) * 6);
  margin-block-start: calc(var(--spacing) * 6);
  font-size: var(--corps-sm);
  color: var(--gris-lisible);
}

/* `display-4xl` est réservé aux chiffres seuls et il vaut 92 px : sur trois
   colonnes, « 78 100 € / an » y tiendrait sur trois lignes. C'est le cran du H2,
   avec ses trois valeurs prises ensemble. */
.carte-offre-montant {
  font-family: var(--font-titre);
  font-weight: var(--graisse-titre);
  font-size: var(--titre-lg);
  line-height: var(--lh-titre);
  letter-spacing: var(--ls-titre-petit);
  color: var(--noir);
}

.carte-offre-desc {
  font-size: var(--legende);
  line-height: 1.45;
  color: var(--gris-lisible);
}

/* ⚠️ **Les trois boutons font la même largeur relative et portent le même
   dessin** : chacun prend la largeur de sa boîte. Celui de Percer est bleu et
   dit « Voir mon éligibilité », vers le rendez-vous, comme la barre de
   navigation. */
.carte-offre-boite .boutons {
  width: 100%;
  margin-block-start: calc(var(--spacing) * 12);
}

.carte-offre-boite .boutons .bouton { flex: 1 1 100%; }

/* La boîte de Percer, en bleu.

   ⚠️ **Le voile noir à 18 % n'est pas un effet, et il ne se retire pas.** En
   blanc sur le bleu du système le contraste tombe à 3,26 pour 1 ; le voile le
   remonte à 4,61. C'est le voile du bouton primaire, la seule valeur de voile
   de la page. L'étalement vaut plus que la moitié de la boîte, sinon le centre
   reste au bleu nu.

   ⚠️ **Tout ce qui s'écrit dessus est en blanc plein.** À 4,61, la moindre
   atténuation passe sous le seuil, 3,9 à 92 % : ce sont la taille et la
   graisse qui séparent les lignes.

   ⚠️ **Le bleu plein se pose donc à trois endroits** : le bouton primaire,
   cet en-tête, et le bouton qu'il porte. La section 2 n'en autorise qu'un.

   ⚠️ **Le bleu est posé sur l'en-tête lui-même depuis le 16 septembre 2026,
   sur demande**, et non plus sur une boîte bleue dans un en-tête blanc. La
   boîte perd donc sa teinte et son liseré sur cette carte : une boîte dans une
   boîte de la même couleur ne ferait qu'un liseré de plus. */
.carte-offre-tete-bleue {
  background-color: var(--bleu);
  border-color: var(--bleu);
  box-shadow:
    inset 0 0 0 calc(var(--spacing) * 400) rgb(0 0 0 / .18),
    var(--ombre-carte);
}

.carte-offre-tete-bleue .carte-offre-boite {
  background-color: transparent;
  border-color: transparent;
}

.carte-offre-tete-bleue .carte-offre-surtitre,
.carte-offre-tete-bleue .carte-offre-titre,
.carte-offre-tete-bleue .carte-offre-prix,
.carte-offre-tete-bleue .carte-offre-montant,
.carte-offre-tete-bleue .carte-offre-desc { color: var(--blanc); }

/* La pastille s'assombrit au lieu de s'éclaircir : du blanc à 10 % ferait
   tomber son libellé à 3,95 pour 1, le voile le porte à 6,29. */
.carte-offre-tete-bleue .pc-engagement {
  background: rgb(0 0 0 / .18);
  border-color: rgb(255 255 255 / .28);
  color: var(--blanc);
}

/* --- La liste ---------------------------------------------------------- */

.carte-offre-colonne {
  flex: 1;
  display: flex;
  flex-direction: column;
  padding: calc(var(--spacing) * 24) calc(var(--spacing) * 16) calc(var(--spacing) * 16);
}

/* Trois lignes réservées : les trois textes n'ont pas la même longueur, et les
   trois listes doivent partir de la même hauteur. */
.carte-offre-texte-intro {
  min-height: calc(3 * 1.5em);
  margin-block-end: calc(var(--spacing) * 24);
  font-size: var(--corps-sm);
  line-height: 1.5;
  color: var(--gris-lisible);
}

.carte-offre-colonne-titre {
  margin-block-end: calc(var(--spacing) * 8);
  font-family: var(--font-titre);
  font-weight: var(--graisse-titre);
  font-size: var(--corps-lg);
  line-height: 1.2;
  letter-spacing: -.015em;
  color: var(--noir);
}

.carte-offre-liste {
  display: flex;
  flex-direction: column;
}

.carte-offre-point {
  display: flex;
  align-items: flex-start;
  gap: calc(var(--spacing) * 10);
  padding-block: calc(var(--spacing) * 6);
}

/* La coche en pastille du composant. **Ni rouge ni verte** : la section 2
   écarte la flèche verte, la section 6 dit qu'une mise en garde en rouge se lit
   comme une erreur du site. La croix des embauches prend le gris de corps, la
   coche de Percer le bleu du système, qui tient 3,23 pour 1 sur la pastille
   blanche, au-dessus des 3 d'un élément graphique porteur de sens. */
.carte-offre-coche {
  flex: none;
  display: grid;
  place-items: center;
  padding: calc(var(--spacing) * 4);
  border-radius: var(--r-sm);
  background: var(--blanc);
  border: 1px solid rgb(28 28 28 / .08);
  color: var(--gris-lisible);
  box-shadow: var(--ombre-carte);
}

.carte-offre-percer .carte-offre-coche { color: var(--bleu); }

.carte-offre-coche svg {
  width: calc(var(--spacing) * 14);
  height: calc(var(--spacing) * 14);
}

.carte-offre-texte {
  flex: 1 1 0;
  min-width: 0;
  padding-block-start: calc(var(--spacing) * 2);
  font-size: var(--corps-sm);
  line-height: 1.4;
  color: var(--gris-lisible);
}

/* La note se pose au bas de la carte : les trois cartes ont la même hauteur,
   c'est sa marge automatique qui absorbe l'écart entre les listes. */
.carte-offre-note {
  margin-block-start: auto;
  padding-block-start: calc(var(--spacing) * 20);
  font-size: var(--legende);
  line-height: 1.45;
  color: var(--gris-lisible);
}

/* --- Les deux embauches -------------------------------------------------

   ⚠️ **Le gris est posé sur l'en-tête lui-même depuis le 16 septembre 2026,
   sur demande**, comme le bleu de Percer : plus de boîte grise dans un
   en-tête blanc. **Il est opaque et c'est voulu** : une teinte de noir sur la
   coque, déjà dégradée, tomberait à #EBEAE7 en haut, où le gris de corps ne
   tient plus que 4,19 pour 1. #F6F6F6 est le composite exact de l'ancienne
   boîte sur du blanc, et le gris de corps y tient 4,65. */
.carte-offre-embauche .carte-offre-tete {
  background: #F6F6F6;
}

.carte-offre-embauche .carte-offre-boite {
  background-color: transparent;
  border-color: transparent;
}

/* Sur le gris, la tuile grise de l'emblème disparaîtrait : elle passe au
   blanc, comme la pastille de coche du composant. */
.carte-offre-embauche .carte-offre-embleme {
  background: var(--blanc);
}

/* ⚠️ **Les deux embauches ont un ratio imposé, 7 sur 12 au bureau, depuis le
   16 septembre 2026, sur demande** : elles ne sont plus aussi hautes que
   Percer, et leur hauteur suit leur largeur sans que la forme bouge. Elles se
   centrent sur la carte du milieu, qui garde la hauteur de son contenu.

   ⚠️ **Le ratio a été 9 sur 19, puis 1 sur 2, le même jour.** Les cartes ont
   été élargies deux fois sans toucher à leur hauteur : c'est donc la largeur du
   ratio qui a bougé, et la hauteur vaut autour de 745 px à 1440 dans les trois
   cas. **C'est la dernière valeur qu'il faut reporter dans le vault.**

   ⚠️ **Le ratio change sous 1 366 px, et c'est ce qui garde les deux cartes à
   la même hauteur.** Un ratio qui ne tient plus le contenu laisse chaque carte
   grandir à son propre texte, et les deux embauches ne font alors plus la même
   taille : 701 contre 680 à 1280 avec un seul ratio. Chaque palier est donc
   choisi pour contenir le texte, ce qui rend les deux cartes égales à coup sûr,
   et reste sous la hauteur de Percer.

   | Fenêtre | Ratio | Embauches | Percer |
   |---|---|---|---|
   | 1920 | 7 sur 12 | 452 sur 775 | 876 |
   | 1440 | 7 sur 12 | 434 sur 744 | 846 |
   | 1366 | 7 sur 12 | 410 sur 703 | 864 |
   | 1280 | 1 sur 2 | 382 sur 764 | 881 |
   | 1100 | 4 sur 9 | 324 sur 729 | 976 |
   | 1024 | 5 sur 12 | 299 sur 718 | 970 |

   ⚠️ **Le ratio ne tient que parce que leur contenu a été resserré**, et c'est
   la contrepartie. À gabarit égal, leurs sept croix pesaient autant que les
   sept coches de Percer : 867 px contre 869, pour une boîte de 748.
   Aucun texte n'est retiré. Ce sont les tailles qui descendent d'un cran,
   légende pour le texte et la liste, emblème à 48, prix au cran du H3, et le
   rythme de la liste qui se serre. **Une carte qui recule dit moins fort, elle
   ne dit pas moins.**

   **Ajouter une ligne demande de refaire ces mesures**, à toutes les
   largeurs du tableau : c'est le palier le plus étroit de chaque ratio qui
   casse en premier.

   ⚠️ **Le ratio reste un plancher, pas une coupe.** Aucun débordement n'est
   masqué : si le texte ne tient plus, la carte grandit à son contenu au lieu
   de le couper. */
.carte-offre-embauche {
  align-self: center;
  aspect-ratio: 7 / 12;
}

@media (max-width: 1365px) {
  .carte-offre-embauche { aspect-ratio: 1 / 2; }
}

@media (max-width: 1179px) {
  .carte-offre-embauche { aspect-ratio: 4 / 9; }
}

@media (max-width: 1099px) {
  .carte-offre-embauche { aspect-ratio: 5 / 12; }
}

.carte-offre-embauche .carte-offre-embleme {
  width: calc(var(--spacing) * 48);
  height: calc(var(--spacing) * 48);
}

.carte-offre-embauche .carte-offre-embleme svg {
  width: calc(var(--spacing) * 20);
  height: calc(var(--spacing) * 20);
}

.carte-offre-embauche .carte-offre-titre { font-size: var(--corps-lg); }
.carte-offre-embauche .carte-offre-montant { font-size: var(--titre-md); }

.carte-offre-embauche .carte-offre-colonne {
  padding: calc(var(--spacing) * 16) calc(var(--spacing) * 12) calc(var(--spacing) * 12);
}

.carte-offre-embauche .carte-offre-texte-intro {
  min-height: 0;
  margin-block-end: calc(var(--spacing) * 16);
  font-size: var(--legende);
}

.carte-offre-embauche .carte-offre-colonne-titre { font-size: var(--corps); }

.carte-offre-embauche .carte-offre-point { padding-block: calc(var(--spacing) * 2); }

.carte-offre-embauche .carte-offre-coche { padding: calc(var(--spacing) * 3); }

.carte-offre-embauche .carte-offre-coche svg {
  width: calc(var(--spacing) * 12);
  height: calc(var(--spacing) * 12);
}

.carte-offre-embauche .carte-offre-texte {
  padding-block-start: calc(var(--spacing) * 1);
  font-size: var(--legende);
}

.carte-offre-embauche .carte-offre-note { padding-block-start: calc(var(--spacing) * 8); }

/* Le logo de Percer, blanc sur la tuile de verre. Il est plus grand que le
   pictogramme des embauches parce qu'il est plus dense : trois chevrons dans
   un carré. */
.carte-offre-embleme .carte-offre-logo {
  width: calc(var(--spacing) * 30);
  height: auto;
}

/* Une colonne sous 1024 px, dans l'ordre du balisage : Percer reste au milieu,
   c'est la place qu'il a dans la comparaison. Les trois lignes réservées au
   texte ne servent plus à rien quand les cartes sont empilées. */
@media (max-width: 1023px) {
  .carte-offre-texte-intro { min-height: 0; }
  .carte-offre-embauche { aspect-ratio: auto; }
  .pc-trio { margin-inline: 0; }
}

.pc-tableau {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--corps-sm);
}

.pc-tableau th,
.pc-tableau td {
  text-align: start;
  padding: calc(var(--spacing) * 16) calc(var(--spacing) * 20);
  border-block-end: 1px solid var(--gris-grille);
  vertical-align: baseline;
}

.pc-tableau thead th {
  font-size: var(--legende);
  font-weight: 500;
  color: var(--gris-lisible);
  border-block-end-color: var(--noir);
}

.pc-tableau tbody th { font-weight: 400; color: var(--gris-lisible); }

.pc-tableau .num {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
  text-align: end;
}

.pc-tableau thead .num { text-align: end; }

.pc-tableau .ligne-percer td,
.pc-tableau .ligne-percer th {
  font-weight: 700;
  color: var(--noir);
  border-block-end: 0;
}

/* La rangée Percer se distingue par la graisse et un fond très clair, jamais
   par une couleur : le violet ne descend pas dans un tableau. */
.pc-tableau .ligne-percer { background: var(--gris-clair); }

/* Un tableau large défile dans son propre conteneur. min-width à zéro est
   ce qui l'empêche de pousser la page entière sur le côté. */
.tableau-scroll { overflow-x: auto; min-width: 0; }

/* --------------------------------------------------------------------------
   07f bis. L'éligibilité : trois conditions, un verdict, une contrepartie

   ⛔ **Le bloc reste bloqué pour la mise en ligne**, et son marqueur est sur le
   conteneur : la section 3 met les critères d'éligibilité dans ce qui manque, et
   ce qui est écrit ici est déduit des sources, pas arrêté.

   ⚠️ **La forme vient d'une référence fournie le 15 septembre 2026, sur
   demande.** Trois choses n'en sont pas reprises, et ce sont des règles.

   1. **Elle tutoie**, la page vouvoie. La section 2 ne laisse pas le choix.
   2. **Ses trois cartes sont en bleu plein.** Le bleu plein est la signature de
      l'appel à l'action, et la page n'en a qu'un. Les cartes prennent donc la
      matière commune du site, dégradé, lueur et ombres.
   3. **Ses listes opposent une croix rouge à une coche verte.** La section 2
      écarte la flèche verte, la section 6 dit qu'une mise en garde en rouge se
      lit comme une erreur du site, et le Duo du constat avait déjà tranché :
      **c'est la forme des deux tracés qui oppose, pas leur couleur.** Les deux
      marques prennent l'encre de leur texte.
   -------------------------------------------------------------------------- */
/* ⚠️ **Une lueur du gradient du site porte le bloc depuis le 15 septembre 2026,
   sur demande.** C'est le même geste qu'au hero, où une lueur fixe porte la
   colonne de texte : deux dégradés radiaux posés derrière, éteints avant les
   bords, et rien qui bouge. La section 5.1 autorise le gradient aux fonds.

   ⚠️ **Elle est noyée à 9 % et elle éclaircit au lieu d'assombrir**, ce qui est
   la condition pour qu'elle n'entre dans aucun calcul de contraste : tout ce
   qui se lit par-dessus est posé sur une carte opaque, et le titre et le chapô
   sont sur un fond **plus** clair que le reste de la page. Le monter demande de
   remesurer le chapô, qui est le seul gris posé à même ce fond.

   **Elle est fixe et elle ne coûte rien** : deux dégradés posés, aucune image
   par seconde, aucun budget de mouvement. */
.eligibilite {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--rythme-bloc);
  align-items: center;
  text-align: center;
}

/* ⚠️ **Elle s'élargit à la page entière le 15 septembre 2026, sur demande, et
   s'arrête à vingt pixels de chaque bord de l'écran.** C'est la mesure du
   panneau sombre du constat, reprise telle quelle. **Le bloc, lui, reste dans
   le conteneur** : seul le fond s'élargit, le texte garde l'alignement des
   autres sections.

   ⚠️ **L'échappée se calcule en « 50% - 50vw », et « vw » compte la barre de
   défilement.** Sans correction, le fond s'arrête à treize pixels du bord au
   lieu de vingt sur un poste à barre classique, et il déborde d'autant de la
   zone lisible. **La largeur de la barre est donc posée en variable par le
   script**, une seule fois, et la moitié en est rendue de chaque côté. Le repli
   est zéro : sans script, on retombe sur le comportement d'avant, qui est faux
   de sept pixels et rien de plus. */
.eligibilite::before {
  content: "";
  position: absolute;
  inset-block: calc(var(--rythme-section) * -.55);
  inset-inline: calc(50% - 50vw + var(--barre-defilement, 0px) / 2 + var(--spacing) * 20);
  z-index: -1;
  border-radius: var(--r-2xl);
  /* ⚠️ **Les deux foyers sont repris le 16 septembre 2026, sur demande, et le
     motif est que le fond ne descendait pas jusqu au simulateur.** La boîte, elle,
     y descendait : elle s arrête **63 px sous lui**. C est la peinture qui
     s éteignait, et c est le piège de ce réglage. Les foyers étaient à 18 % et
     30 % de la hauteur, sur un bloc de 1 807 px : au-delà des deux tiers il ne
     restait **rien du tout**, densité mesurée à zéro sur les deux bords aux trois
     derniers quarts. **Une boîte qui va plus bas ne suffit pas, il faut un foyer
     qui porte jusque-là.**

     Ils sont maintenant posés aux deux bords, à mi-hauteur basse, et étirés sur
     quatre cinquièmes de la hauteur. Relevé le long des deux marges visibles, à
     5, 20, 35, 50, 65, 80, 93 et 99 % de la boîte : **densité continue de 20 % à
     99 %**, maximum 0,085 au milieu, et **0,04 et 0,03 sous le simulateur**, là
     où il n y avait rien.

     ⚠️ **Et ça répare un défaut de contraste qui était là avant.** Cette lueur
     assombrit le fond au lieu de l éclaircir, contrairement à ce qui était écrit
     ici, et le chapô de la section est en gris lisible, posé à même elle. Sous
     les anciens foyers, qui tombaient en haut à gauche, il tenait **4,336 pour
     1**, sous le minimum AA, et la pilule du label 4,646. Le haut de la boîte est
     maintenant vide de teinte : **4,77 et 4,79**.

     **Trois choses seulement se posent à même ce fond**, et c est ce qui borne le
     réglage : la pilule du label, le H3, qui est du gros texte, et ce chapô. Tout
     le reste, les cartes, le bouton, les verdicts et le simulateur, est opaque.
     **Remonter un foyer demande de refaire ces deux mesures sur les cinq
     largeurs**, pas seulement de regarder. */
  background:
    radial-gradient(48% 80% at 3% 68%, rgb(102 71 240 / .09) 0%, transparent 70%),
    radial-gradient(44% 78% at 97% 62%, rgb(0 145 255 / .09) 0%, transparent 70%);
  pointer-events: none;
}

/* ⚠️ **L éligibilité est séparée par un écart de section, et non de bloc.** Le
   16 septembre 2026, la lueur se superposait à la carte de l avance posée
   au-dessus : relevé à 1440 px, un écart de bloc de 60 px pour un débord de
   lueur de 80, soit **vingt pixels de chevauchement**. Ce n est pas un bloc de
   plus dans l offre, c est le temps qui dit à qui elle s adresse : il prend
   donc l écart de section, comme la réponse du constat.

   ⚠️ **Et le débord de la lueur est lié à ce rythme, pas écrit en pixels.** À
   55 % de l écart de section, il reste toujours 45 % de dégagement, à toutes
   les largeurs. Écrit en dur, il redevenait faux dès que le rythme changeait,
   et c est exactement ce qui vient d arriver. **Le poids du sélecteur compte** :
   la pile pose sa marge par « .pile-bloc > * + * », qui pèse une classe ; il en
   faut deux pour gagner. */
.pile-bloc > .eligibilite { margin-block-start: var(--rythme-section); }

.eligi-tete { align-items: center; }
.eligi-tete .section-nom { align-self: center; }
.eligi-tete h2 { max-width: calc(var(--spacing) * 760); }
.eligi-tete p { max-width: var(--mesure-incise); }

/* Les trois conditions. Poids strictement égal : trois points chacune, même
   tuile, même géométrie. Une carte plus fournie se lirait comme la vraie
   condition, et les trois doivent être réunies. */
.cond-grille {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: calc(var(--spacing) * 24);
  inline-size: 100%;
  list-style: none;
  margin: 0;
  padding: 0;
  text-align: start;
}

/* ⚠️ **Les trois cartes sont en verre depuis le 16 septembre 2026, sur
   demande.** Elles portaient le fond de carte opaque, blanc vers un ton sous la
   page.

   **Un flou d'arrière-plan ne fait rien sous un fond opaque**, il n'y a rien à
   voir au travers : le fond passe donc en blanc translucide, et c'est lui qui
   laisse monter la lueur du gradient posée derrière le bloc. C'est la même
   matière que le label, le bouton à contour et la capsule du prix, à une
   différence près : **ils sont teintés de noir, celle-ci est teintée de blanc.**
   Sur une lueur de couleur, une teinte de noir salirait ce qu'on vient de
   poser.

   ⚠️ **L'opacité est bornée par le contraste, et le plafond est serré.** Du gris
   de corps se lit sur ces cartes : au point le plus translucide, sur la lueur la
   plus dense, il tient **4,81 pour 1** en haut et 4,89 en bas, pour un minimum
   AA de 4,5. **Le fond de
   carte opaque tenait 4,57 en haut** : le verre passe donc, mais il n'a que
   deux dixièmes de marge. **Descendre l'opacité demande de remesurer**, et
   c'est le texte courant qui casse en premier.

   ⚠️ **Le flou n'est pas une valeur neuve** : c'est celui de la classe « verre »,
   25 px, à l'identique. Un second flou dans le fichier serait une seconde
   vérité.

   **Le blanc va du plus opaque en bas au plus translucide en haut**, comme tous
   les fonds de carte du site : denses en bas, effacés en haut. La grammaire ne
   change pas, seule la matière change. */
.cond-carte {
  display: flex;
  flex-direction: column;
  gap: calc(var(--spacing) * 20);
  padding: calc(var(--spacing) * 32);
  border-radius: var(--r-xl);
  background-image: linear-gradient(
    to top,
    rgb(255 255 255 / .90) 0%,
    rgb(255 255 255 / .82) 100%);
  -webkit-backdrop-filter: blur(25px);
  backdrop-filter: blur(25px);
  box-shadow: var(--lueur-carte), var(--ombre-carte);
}

/* ⚠️ **La tuile est en bleu plein avec une lueur intérieure blanche, depuis le
   15 septembre 2026, sur demande.** Elle a porté la teinte de violet de la
   pilule des labels, puis le gradient du site, puis ceci. C'est **la matière du
   levier tenu** de la section 04, reprise telle quelle : le bleu du système, un
   filet blanc en haut, une lueur blanche diffuse, et le voile noir.

   ⚠️ **Le voile noir n'est pas décoratif.** À 22 % comme au levier tenu, il
   remonte le blanc de 3,23 à **5,01 pour 1** sur ce bleu. Le glyphe est un
   élément graphique porteur de sens, il lui faudrait 3 pour 1. **Reprendre ce
   bleu sans le voile casse la règle de contraste**, et c'est la troisième fois
   que ce voile entre pour cette raison exacte.

   ⚠️ **Le bleu plein se répand donc sur la page.** Il était la signature de
   l'appel à l'action, il est déjà au levier tenu, et il est ici sur trois
   tuiles. La section 2 ne le dit plus. */
.cond-icone {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: calc(var(--spacing) * 48);
  block-size: calc(var(--spacing) * 48);
  border-radius: var(--r-md);
  /* ⚠️ **La tuile prend la matière des labels, en bleu, depuis le 16 septembre
     2026, sur demande** : bleu du système à 20 %, liseré d'un pixel et lueur
     intérieure en bleu éclairci. Elle était en bleu plein, avec le voile noir
     et un tracé blanc.

     ⚠️ **Le tracé passe au bleu approfondi, #0A5FB0.** La tuile est
     maintenant claire, sur une carte claire : un tracé blanc n'y tiendrait
     pas. Celui-ci tient plus de 5 pour 1 au cœur de la tuile, au-dessus des
     3 pour 1 d'un élément graphique. Le voile noir sort avec le bleu plein,
     il n'a plus rien à remonter. */
  /* Centre presque transparent, comme les labels sur fond clair. */
  border: 1px solid #5CB3FF;
  background-color: rgb(0 145 255 / .05);
  box-shadow:
    inset 0 0 calc(var(--spacing) * 10) 0 rgb(51 170 255 / .45),
    inset 0 0 calc(var(--spacing) * 2) 0 rgb(51 170 255 / .4);
  color: #0A5FB0;
}

.cond-icone svg {
  inline-size: calc(var(--spacing) * 24);
  block-size: calc(var(--spacing) * 24);
}

/* Le second tracé de l'étiquette est un trou dans le premier : il se superpose
   au lieu de suivre. */
.cond-icone svg + svg { position: absolute; }

.cond-titre {
  font-family: var(--font-titre);
  font-weight: var(--graisse-titre);
  font-size: var(--titre-sm);
  line-height: var(--lh-titre);
  letter-spacing: var(--ls-titre-petit);
}

.cond-points,
.verdict-liste {
  display: flex;
  flex-direction: column;
  gap: calc(var(--spacing) * 14);
  list-style: none;
  margin: 0;
  padding: 0;
}

.cond-points li,
.verdict-liste li {
  display: flex;
  align-items: start;
  gap: calc(var(--spacing) * 12);
  font-size: var(--corps-sm);
  line-height: 1.5;
  color: var(--gris-lisible);
  max-width: none;
}

.cond-marque,
.verdict-marque {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: calc(var(--spacing) * 20);
  block-size: calc(var(--spacing) * 20);
  margin-block-start: calc(var(--spacing) * 2);
  border-radius: var(--r-full);
  border: 1px solid var(--gris-grille);
  color: var(--noir);
}

.cond-marque svg,
.verdict-marque svg {
  inline-size: calc(var(--spacing) * 12);
  block-size: calc(var(--spacing) * 12);
}

/* Le bouton porte le libellé de la barre de navigation, mot pour mot, et la
   même cible. **Deux libellés pour une même promesse en feraient deux**, et la
   section 2 n'en autorise qu'une. Il garde le dessin à contour : le bleu plein
   reste au seul « Réserver un appel ». */
.eligi-action { align-self: center; }

/* Le verdict, en deux colonnes de poids égal. La gauche est là pour perdre la
   comparaison, et elle n'est ni rétrécie, ni grisée, ni barrée. */
.verdicts {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: calc(var(--spacing) * 24);
  inline-size: 100%;
  text-align: start;
}

/* ⚠️ **Les deux colonnes portent deux matières depuis le 15 septembre 2026, sur
   demande.** Elles étaient blanches toutes les deux, et la section était fade.
   La disqualification prend le gris sombre, déjà porté par le bloc de la
   publicité ; ce qu'on attend prend la teinte de violet. **Ce sont les deux
   matières du système, pas deux couleurs de plus**, et la référence opposait un
   rose à un lavande par le même geste.

   ⚠️ **La loi du poids égal tient encore, et c'est ce qu'il faut surveiller.**
   Les deux cartes ont la même géométrie, la même garniture et les mêmes
   marques ; la colonne de gauche n'est ni rétrécie, ni grisée, ni barrée. C'est
   la matière qui distingue les deux propos, pas la mise en page.

   ⚠️ **Tout ce qui s'écrit sur les deux passe en noir.** Le gris de corps ne
   tient que 3,45 pour 1 sur le gris sombre et 4,03 sur la teinte de violet, les
   deux sous le minimum AA. Le noir y tient 11,19 et 13,07. C'est la même
   conséquence qu'au Duo du constat, et elle se mesure de la même façon. */
.verdict {
  display: flex;
  flex-direction: column;
  gap: calc(var(--spacing) * 20);
  padding: calc(var(--spacing) * 32);
  border-radius: var(--r-xl);
  box-shadow: var(--lueur-carte-douce), var(--ombre-carte);
}

/* ⚠️ **La carte de droite a porté la teinte de violet une heure le 15 septembre
   2026, et elle est revenue au papier, sur demande.** Le violet en surface
   tirait la carte vers la couleur de marque là où elle ne dit rien de la
   marque, et l'opposition se lisait comme un jugement. Le papier contre la
   surface morte suffit : **c'est la même opposition qu'au Duo du constat**,
   claire contre sombre. Le jeton « --fond-carte-violet » reste défini et ne
   sert plus ; s'il n'est pas repris, il sort avant la mise en ligne. */
.verdict-dehors  { background-image: var(--fond-carte-sombre); }
.verdict-attendu { background-image: var(--fond-carte); }

.verdict-dehors .verdict-liste li { color: var(--noir); }

.verdict-marque { border-color: rgb(28 28 28 / .18); }

.verdict-liste li b {
  display: block;
  color: var(--noir);
  font-weight: 700;
}

.verdict-sous { display: block; margin-block-start: calc(var(--spacing) * 2); }

/* --------------------------------------------------------------------------
   07f ter. Le simulateur du volume

   Il remplace un paragraphe qui énonçait le calcul au lieu de le montrer, comme
   la multiplication des quatre leviers en section 04. **Rien ne bouge tant que
   personne ne touche un curseur**, ce n'est donc pas un mouvement de plus.

   ⚠️ **Les deux curseurs sont des « input range » du navigateur, habillés.** Ni
   composant maison, ni dépendance : ils sont accessibles au clavier et à la
   voix sans une ligne de script, ce qu'un faux curseur ne donne jamais.
   -------------------------------------------------------------------------- */

/* --------------------------------------------------------------------------
   12. GuaranteeTable : quatre lignes, la dernière colonne identique sur les
   quatre. La ligne de restriction n'est jamais séparée du tableau.

   ⛔ **Ce bloc n a plus de balisage depuis le 15 septembre 2026, sur demande.**
   Le tableau de garantie est sorti de la section 07 avec les trois engagements.
   Ses règles restent ici parce que c est un des onze composants du système et
   que son copy est arrêté : il revient d une seule pose. **S il ne revient pas,
   elles sortent avant la mise en ligne.**
   -------------------------------------------------------------------------- */

.guarantee {
  border: 1px solid var(--gris-grille);
  border-radius: var(--r-xl);
  overflow: hidden;
}

.guarantee-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--corps-sm);
}

.guarantee-table th,
.guarantee-table td {
  text-align: start;
  padding: calc(var(--spacing) * 18) calc(var(--spacing) * 24);
  border-block-end: 1px solid var(--gris-grille);
  vertical-align: baseline;
}

.guarantee-table thead th {
  font-size: var(--legende);
  font-weight: 500;
  color: var(--gris-lisible);
  background: var(--gris-clair);
}

.guarantee-table tbody th {
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  width: 1%;
}

.guarantee-table .ou {
  color: var(--noir);
  font-weight: 700;
  white-space: nowrap;
}

/* La restriction fait partie du tableau. Elle n'est ni repliée, ni renvoyée
   en note : sans elle, « garantie » se lit « chiffre d'affaires ». */
.guarantee-restriction {
  padding: calc(var(--spacing) * 24);
  border-block-start: 0;
  font-size: var(--corps-sm);
  line-height: 1.5;
  color: var(--gris-lisible);
}

.guarantee-restriction p { max-width: none; }
.guarantee-restriction strong { color: var(--noir); }

/* --------------------------------------------------------------------------
   13. TeamGrid : une personne, un rôle, le levier dont elle répond.
   Pas d'écoles, pas de diplômes.

   ⚠️ **En piste de cartes depuis le 16 septembre 2026, sur demande, d'après une
   référence fournie.** C'était une grille réglée de cinq cases égales. Ce sont
   maintenant cinq portraits en hauteur, le nom et le rôle posés en bas sur un
   dégradé sombre, une carte révélée en bleu, et deux flèches.

   ⚠️ **Aucune carte n'est mise en avant, et c'est l'écart avec la référence.**
   Elle donne le bleu à la fondatrice, en permanence. Ici, la carte bleue est
   celle qu'on survole ou qu'on atteint au clavier ; la première ne l'est qu'au
   repos, et elle rend la main dès qu'une autre est visée. Présenter l'équipe
   comme une couverture, c'est aussi ne pas en hiérarchiser les membres.

   ⚠️ **La carte révélée porte le levier, pas une biographie.** La référence y
   écrit un parcours ; la loi du composant écarte les écoles et les diplômes, et
   c'est le levier que le client obtient.

   **Tout ce que dit la carte est dans le document dès le départ**, les deux
   faces comprises : la révélation est visuelle. La voix lit un seul nom
   accessible par carte, posé sur l'élément, et les deux faces en sortent.
   -------------------------------------------------------------------------- */
.equipe {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: calc(var(--spacing) * 24);
}

/* L'en-tête de la section porte les flèches à droite, sur la ligne du titre.
   Elles s'alignent sur le bas du bloc de texte, là où la piste commence. */
.equipe-tete {
  display: flex;
  align-items: end;
  justify-content: space-between;
  gap: calc(var(--spacing) * 32);
}

.equipe-fleches {
  flex: none;
  display: flex;
  gap: calc(var(--spacing) * 10);
}

/* ⚠️ **Les flèches sont du bleu du système, avec la lueur intérieure et le
   voile**, la matière du levier tenu et des tuiles de l'éligibilité. Le voile
   n'est pas décoratif : il remonte le blanc de 3,23 à 5,01 pour 1. */
.equipe-fleche {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: calc(var(--spacing) * 48);
  block-size: calc(var(--spacing) * 48);
  padding: 0;
  border: 0;
  border-radius: var(--r-full);
  background-color: var(--bleu);
  color: var(--blanc);
  cursor: pointer;
  box-shadow:
    inset 0 1px 0 0 rgb(255 255 255 / .45),
    inset 0 0 calc(var(--spacing) * 12) 0 rgb(255 255 255 / .28),
    inset 0 0 0 calc(var(--spacing) * 100) rgb(0 0 0 / .22);
  transition: opacity var(--motion-instant) var(--ease-out);
}

.equipe-fleche svg {
  inline-size: calc(var(--spacing) * 20);
  block-size: calc(var(--spacing) * 20);
}

.equipe-fleche:hover {
  box-shadow:
    inset 0 1px 0 0 rgb(255 255 255 / .45),
    inset 0 0 calc(var(--spacing) * 12) 0 rgb(255 255 255 / .28),
    inset 0 0 0 calc(var(--spacing) * 100) rgb(0 0 0 / .32);
}

/* Une flèche qui ne mène nulle part se retire à moitié, elle ne disparaît pas :
   sa place ne doit pas sauter quand on atteint un bout de la piste. */
.equipe-fleche:disabled {
  opacity: .35;
  cursor: default;
}

/* La piste. **Quatre cartes et un bout de la cinquième** au bureau : c'est ce
   qui dit, sans rien écrire, qu'il y en a une de plus. */
.equipe-piste {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: calc((100% - var(--spacing) * 16 * 3) / 4.25);
  gap: calc(var(--spacing) * 16);
  margin: 0;
  padding: 0;
  list-style: none;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  scrollbar-width: none;
  /* La garniture laisse passer l'anneau de focus et l'ombre des cartes, que le
     défilement couperait net sinon. */
  padding-block: calc(var(--spacing) * 6);
}

.equipe-piste::-webkit-scrollbar { display: none; }

/* ⚠️ **Les bords de la piste s'effacent en fondu, sur demande du 16 septembre
   2026**, mais seulement du côté où il reste des cartes à voir. La carte coupée
   au bord se lisait comme une erreur de cadrage ; en fondu, elle se lit comme
   une suite.

   **Un fondu permanent à gauche éteindrait la première carte au repos**, qui
   est alignée sur le texte de la section et n'a rien de caché derrière elle.
   Le script pose donc deux marqueurs, début et fin de course, et chaque bord ne
   s'efface que quand la piste a de quoi défiler de ce côté.

   Les deux largeurs sont des propriétés enregistrées : sans ça, une valeur
   posée dans un dégradé ne s'anime pas, et le fondu sautait d'un coup au
   premier clic sur une flèche.

   ⚠️ **Le marqueur de début est posé dans le balisage**, et non par le script
   seul. Sans lui, la piste arrivait sans marqueur, le fondu gauche s'allumait,
   puis s'éteignait en fondu dès que le script passait : un effet au
   chargement, sur une carte qui n'a rien de caché derrière elle. */
@property --fondu-g {
  syntax: '<length>';
  inherits: false;
  initial-value: 0px;
}

@property --fondu-d {
  syntax: '<length>';
  inherits: false;
  initial-value: 0px;
}

.equipe-piste {
  --fondu: calc(var(--spacing) * 72);
  -webkit-mask-image: linear-gradient(
    to right,
    transparent 0,
    #000 var(--fondu-g),
    #000 calc(100% - var(--fondu-d)),
    transparent 100%);
  mask-image: linear-gradient(
    to right,
    transparent 0,
    #000 var(--fondu-g),
    #000 calc(100% - var(--fondu-d)),
    transparent 100%);
  transition:
    --fondu-g var(--motion-quick) var(--ease-out),
    --fondu-d var(--motion-quick) var(--ease-out);
}

.equipe-piste:not([data-debut]) { --fondu-g: var(--fondu); }
.equipe-piste:not([data-fin])   { --fondu-d: var(--fondu); }

.membre {
  position: relative;
  aspect-ratio: 5 / 7;
  scroll-snap-align: start;
  overflow: hidden;
  border-radius: var(--r-xl);
  background: var(--gris-clair);
  box-shadow: var(--lueur-carte-douce), var(--ombre-carte);
  outline-offset: 3px;
}

/* Le cadre de remplacement du portrait, à la grammaire du site : un titre et un
   texte qui disent ce que la photo montrera. Il se tient en haut, pour laisser
   le bas au dégradé qui porte le nom. */
.membre-visuel {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  gap: calc(var(--spacing) * 6);
  padding: calc(var(--spacing) * 20);
}

.membre-visuel p {
  max-width: none;
  font-size: var(--legende);
  line-height: 1.45;
  color: var(--gris-lisible);
}

.membre-visuel .cadre-visuel-titre { color: var(--noir); font-weight: 700; }

/* La face : le nom et le rôle posés en bas, sur un dégradé qui monte du noir.
   ⚠️ **Le dégradé est dimensionné par le contraste, pas par le goût.** À la
   hauteur du rôle, le fond composé tombe à rgb(38 38 38), où le gris du rôle
   tient **8,06 pour 1** et le blanc du nom **15,13**. */
.membre-face,
.membre-dos {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: end;
  gap: calc(var(--spacing) * 4);
  padding: calc(var(--spacing) * 24);
  text-align: center;
  transition: opacity var(--motion-quick) var(--ease-out);
}

.membre-face {
  background: linear-gradient(
    to top,
    rgb(17 17 17 / .92) 0%,
    rgb(17 17 17 / .78) 28%,
    rgb(17 17 17 / 0) 58%);
}

.membre-nom {
  font-family: var(--font-titre);
  font-weight: var(--graisse-titre);
  font-size: var(--titre-sm);
  line-height: var(--lh-titre);
  letter-spacing: var(--ls-titre-petit);
  color: var(--blanc);
}

.membre-role {
  font-size: var(--legende);
  line-height: 1.45;
  color: #BDBDBD;
}

/* Le dos : la carte passe au bleu du système, et le levier dont la personne
   répond prend la place du portrait. */
.membre-dos {
  justify-content: start;
  text-align: start;
  background-color: var(--bleu);
  box-shadow:
    inset 0 1px 0 0 rgb(255 255 255 / .45),
    inset 0 0 calc(var(--spacing) * 24) 0 rgb(255 255 255 / .22),
    inset 0 0 0 calc(var(--spacing) * 600) rgb(0 0 0 / .22);
  opacity: 0;
}

.membre-dos .membre-role { color: rgb(255 255 255 / .82); }

.membre-repond {
  margin-block-start: auto;
  font-size: var(--legende);
  color: rgb(255 255 255 / .82);
}

.membre-levier {
  font-family: var(--font-titre);
  font-weight: var(--graisse-titre);
  font-size: var(--titre-sm);
  line-height: var(--lh-titre);
  letter-spacing: var(--ls-titre-petit);
  color: var(--blanc);
}

/* ⚠️ **La révélation se fait au survol et au clavier, et seulement là**, depuis
   le 16 septembre 2026, sur demande. La première carte était révélée au repos
   pendant une heure le même jour : elle ne l'est plus, **aucune carte n'est
   bleue tant que personne ne la vise.** */
.membre:hover .membre-dos,
.membre:focus-visible .membre-dos { opacity: 1; }

.membre:hover .membre-face,
.membre:focus-visible .membre-face { opacity: 0; }

/* ⛔ **Portraits provisoires, posés le 16 septembre 2026, sur demande.** Ce sont
   cinq des trente visages générés de l'entonnoir du hero : **des personnes qui
   n'existent pas, sous les prénoms de personnes qui existent.** C'est tenable
   tant que le bloc est marqué bloqué, et c'est le cas ; **ça ne l'est plus une
   seconde après la mise en ligne.** Le marqueur « data-provisoire » est sur
   chaque image pour qu'on les retrouve toutes d'un coup.

   ⚠️ **Ils sont flous, et c'est attendu.** Les fichiers font 128 px de côté et
   la carte en demande près de 380 de haut : l'image est agrandie trois fois. La
   réserve à 512 px existe dans « tools/avatars-src/ », mais ce dossier ne part
   jamais en production, et la page ne doit pas pointer dedans. */
.membre-photo {
  position: absolute;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  object-position: center 30%;
}

/* --------------------------------------------------------------------------
   13 bis. Le cas client : la capture du site à gauche, quatre cartes à
   droite. Depuis le 16 septembre 2026, sur demande.

   ⚠️ **C'est la grille de l'opportunité, en miroir**, et ses règles viennent
   avec : sept douzièmes contre cinq, un écart de 48, les deux colonnes étirées
   sur la rangée, et la matière des atouts sur les quatre cartes.
   -------------------------------------------------------------------------- */

/* ⚠️ **Le cas est séparé de l'équipe par un écart de section et non de
   bloc**, sur demande du 16 septembre 2026. C'est le geste de l'éligibilité
   en section 07 : le cas n'est pas un bloc de plus sur l'équipe, c'est un
   autre temps de la preuve. */
.pile-bloc > .cas-bloc { margin-block-start: var(--rythme-section); }

.cas-client {
  display: grid;
  grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
  grid-template-rows: auto;
  gap: calc(var(--spacing) * 16) calc(var(--spacing) * 48);
  align-items: stretch;
}

/* ⚠️ **La pile des quatre cartes vaut la hauteur de la capture, sur demande.**
   La rangée prend la plus haute des deux colonnes, comme à l'opportunité :
   les cartes s'étirent si la capture est plus haute, et la capture se recadre
   si les cartes le sont. **Elle se recadre, elle ne se déforme pas** :
   `object-fit: cover`, calé en haut, là où se lisent le logo et le titre du
   site. Relevé à 1440 px : la pile demande à peine plus que les 340 px de la
   capture, le recadrage est donc de quelques pour cent. */
.cas-visuel {
  grid-column: 1;
  grid-row: 1;
  position: relative;
  display: block;
  min-height: 0;
  border-radius: var(--r-xl);
}

.cas-visuel img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center top;
  border: 1px solid var(--gris-grille);
  border-radius: var(--r-xl);
  box-shadow: var(--ombre-carte);
  transition: box-shadow var(--motion-quick) var(--ease-out);
}

.cas-visuel:hover img { box-shadow: var(--ombre-carte-forte); }

/* L'icône de redirection. ⚠️ **Elle prend le dessin du bouton de la barre de
   navigation depuis le 16 septembre 2026, sur demande** : la double coque en
   pilule, une enveloppe, une gouttière de quatre pixels, puis un noyau, les
   deux arrondis concentriques. Elle est posée dans le coin bas droit de la
   capture.

   ⚠️ **C'est la version sombre de ce bouton, celle de la barre sur Signal**,
   et pas la claire. Le coin bas droit de la capture est du ciel bleu profond :
   un verre de noir à 4 % y disparaîtrait, et une encre noire ne s'y lirait
   pas. Le verre passe au blanc, aux valeurs de Signal, et le trait au blanc.
   Le flou de 25 px est celui de la classe verre, à l'identique.

   Ce n'est pas un second bouton : la capture reste un lien caché à la voix et
   au clavier, et l'icône n'est qu'un signe posé dessus. */
.cas-visuel-icone {
  position: absolute;
  inset-block-end: calc(var(--spacing) * 16);
  inset-inline-end: calc(var(--spacing) * 16);
  display: block;
  padding: calc(var(--spacing) * 4);
  border-radius: var(--r-full);
  background-color: rgb(255 255 255 / .05);
  border: 1px solid rgb(255 255 255 / .10);
  color: var(--blanc);
  -webkit-backdrop-filter: blur(25px);
  backdrop-filter: blur(25px);
}

.cas-visuel-noyau {
  display: grid;
  place-items: center;
  inline-size: calc(var(--spacing) * 40);
  block-size: calc(var(--spacing) * 40);
  border-radius: var(--r-full);
  background-color: rgb(255 255 255 / .05);
  box-shadow:
    inset 0 1px 0 0 rgb(255 255 255 / .10),
    inset 0 0 calc(var(--spacing) * 20) 0 rgb(255 255 255 / .06);
  transition: background-color var(--motion-instant) var(--ease-out);
}

.cas-visuel-noyau svg {
  display: block;
  inline-size: calc(var(--spacing) * 20);
  block-size: calc(var(--spacing) * 20);
}

.cas-visuel:hover  .cas-visuel-noyau { background-color: rgb(255 255 255 / .10); }
.cas-visuel:active .cas-visuel-noyau { background-color: rgb(255 255 255 / .14); }

/* Les infos d'Eliance, sur la capture. ⚠️ **Elles remplacent la légende
   depuis le 16 septembre 2026, sur demande** : au survol et au focus, un
   dégradé noir monte du bas de la capture et le nom s'y pose.

   **Le dégradé est dimensionné par le contraste.** Il est à 85 % de noir au
   pied de la capture, là où se lit le texte : le blanc y tient quel que soit
   le ciel dessous. Le texte laisse la place de l'icône à sa droite.

   ⚠️ **Sans souris, il n'y a pas de survol**, et le nom ne se verrait jamais.
   Il est donc posé au repos sur un pointeur grossier, comme la carte du
   système en section 02. */
.cas-visuel-infos {
  position: absolute;
  inset-inline: 0;
  inset-block-end: 0;
  display: flex;
  flex-direction: column;
  gap: calc(var(--spacing) * 4);
  padding: calc(var(--spacing) * 72) calc(var(--spacing) * 88) calc(var(--spacing) * 20) calc(var(--spacing) * 24);
  border-end-start-radius: var(--r-xl);
  border-end-end-radius: var(--r-xl);
  background: linear-gradient(to top, rgb(0 0 0 / .85) 0%, rgb(0 0 0 / .6) 45%, rgb(0 0 0 / 0) 100%);
  color: var(--blanc);
  opacity: 0;
  transition: opacity var(--motion-quick) var(--ease-out);
}

.cas-visuel:hover .cas-visuel-infos,
.cas-visuel:focus-visible .cas-visuel-infos { opacity: 1; }

@media (hover: none), (pointer: coarse) {
  .cas-visuel-infos { opacity: 1; }
}

.cas-visuel-nom {
  font-family: var(--font-titre);
  font-size: var(--titre-sm);
  font-weight: var(--graisse-titre);
  line-height: var(--lh-titre);
  letter-spacing: var(--ls-titre-petit);
}

.cas-visuel-detail {
  font-size: var(--corps-sm);
  line-height: 1.5;
  color: rgb(255 255 255 / .86);
}

/* Les quatre cartes empilées. Le rab de hauteur se répartit également sur
   les quatre, comme les atouts de l'opportunité : une carte qui grandirait
   plus que ses voisines se lirait comme la plus importante. */
.cas-chiffres {
  grid-column: 2;
  grid-row: 1;
  display: flex;
  flex-direction: column;
  gap: calc(var(--spacing) * 12);
  margin: 0;
  padding: 0;
  list-style: none;
}

/* La matière des atouts de l'opportunité, à l'identique. Le chiffre et son
   libellé sur une ligne : empilés, les quatre cartes doubleraient la hauteur
   de la capture et la recadreraient de moitié. */
.cas-chiffre {
  /* Base nulle : les quatre cartes ont exactement la même hauteur, quel que
     soit leur texte. Chaque libellé tient donc sur une ligne, et le
     quatrième a été raccourci pour ça. */
  flex: 1 1 0;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: calc(var(--spacing) * 4) calc(var(--spacing) * 16);
  padding: calc(var(--spacing) * 14) calc(var(--spacing) * 24);
  background-color: transparent;
  background-image: var(--fond-carte);
  border: 1px solid var(--gris-grille);
  border-radius: var(--r-xl);
  box-shadow: var(--lueur-carte), var(--ombre-carte);
}

/* L'échelle des chiffres est celle du titre de section, sur les quatre à la
   fois : la loi du StatRow veut que l'échelle change sur toute la rangée et
   jamais sur une tuile. Le gris du libellé tient 4,57 pour 1 au point le plus
   sombre du dégradé de carte. */
.cas-chiffre .stat-valeur {
  font-size: var(--titre-lg);
  line-height: var(--lh-titre);
  letter-spacing: var(--ls-titre-petit);
  white-space: nowrap;
}

/* --------------------------------------------------------------------------
   14. ProofBox : un mot ou un chiffre en très gros, une phrase dessous.

   ⛔ **Plus aucun balisage ne l'utilise depuis le 16 septembre 2026** : l'encadré
   « Bing » du cas client est supprimé, sur demande. Ces règles sortent avant
   la mise en ligne s'il ne revient pas.
   -------------------------------------------------------------------------- */

.proof-box {
  display: flex;
  flex-direction: column;
  gap: calc(var(--spacing) * 12);
  padding: calc(var(--spacing) * 40);
  background: var(--gris-clair);
  border-radius: var(--r-2xl);
}

.proof-mot {
  font-family: var(--font-titre);
  font-weight: var(--graisse-titre);
  font-size: var(--display-4xl);
  line-height: var(--lh-display);
  letter-spacing: var(--ls-display);
}

.proof-phrase {
  font-size: var(--corps);
  line-height: var(--lh-corps);
  color: var(--gris-lisible);
  max-width: var(--mesure-corps);
}

/* --------------------------------------------------------------------------
   15. Faq : accordéon. Le texte des réponses est dans le HTML même replié.
   details garde le contenu dans le document : une réponse injectée au clic
   n'existe pas pour un moteur de réponse.
   -------------------------------------------------------------------------- */

.faq {
  border-block-start: 1px solid var(--gris-grille);
}

.faq-item { border-block-end: 1px solid var(--gris-grille); }

.faq-item > summary {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: calc(var(--spacing) * 24);
  padding-block: calc(var(--spacing) * 24);
  cursor: pointer;
  list-style: none;
  font-family: var(--font-titre);
  font-weight: var(--graisse-titre);
  font-size: var(--titre-sm);
  line-height: var(--lh-titre);
  letter-spacing: var(--ls-titre-petit);
  transition: color var(--motion-instant) var(--ease-out);
}

.faq-item > summary::-webkit-details-marker { display: none; }
.faq-item > summary:hover { color: var(--gris-lisible); }

.faq-signe {
  flex: none;
  width: calc(var(--spacing) * 16);
  height: calc(var(--spacing) * 16);
  color: var(--gris-lisible);
  transition: transform var(--motion-quick) var(--ease-out);
}

.faq-item[open] .faq-signe { transform: rotate(45deg); }

.faq-reponse {
  padding-block-end: calc(var(--spacing) * 28);
  font-size: var(--corps-sm);
  line-height: 1.6;
  color: var(--gris-lisible);
  max-width: var(--mesure-corps);
}

.faq-reponse p { max-width: none; }
.faq-reponse p + p { margin-block-start: calc(var(--spacing) * 12); }
.faq-reponse strong { color: var(--noir); font-weight: 700; }

/* Les questions sont rangées par levier depuis le 16 septembre 2026, sur
   demande, et présentées comme le sélecteur de la méthode, **en vertical** :
   les quatre leviers à gauche sur quatre douzièmes, les questions du levier
   tenu à droite sur huit.

   **C'est la barre d'onglets commune**, celle des leviers et de Signal, avec
   une variante de sens. Le script est celui des onglets simples : il pose les
   rôles, l'état, les flèches du clavier, et le drapeau data-arme. **Sans ce
   drapeau, rien n'est masqué** : la barre sort et les quatre groupes
   s'empilent sous leur titre. */
.faq-vues {
  display: grid;
  grid-template-columns: minmax(0, 4fr) minmax(0, 8fr);
  gap: calc(var(--spacing) * 48);
  align-items: start;
}

.faq-vues:not([data-arme]) { grid-template-columns: minmax(0, 1fr); }
.faq-vues:not([data-arme]) .onglets { display: none; }

/* La barre en colonne. Elle reste à l'écran pendant qu'on lit une longue
   liste de réponses, sous la barre de navigation : elle ne bouge pas, elle
   reste, ce n'est ni une apparition ni un mouvement.

   ⚠️ **Le rayon n'est plus plein.** Une pilule qui s'allonge en hauteur devient
   un stade, et le contenu y flotte. La barre prend le rayon des cartes, 24, et
   les onglets gardent le leur, plein : leur hauteur de 44 donne un rayon de 22,
   plus la gouttière de 5, soit 27 pour 24 posés. **Trois pixels d'écart entre
   deux arrondis concentriques**, sous le seuil de ce qui se voit ; 28 ne serait
   pas un multiple de quatre. */
.onglets.onglets-vertical {
  position: sticky;
  top: calc(var(--hauteur-entete) + var(--spacing) * 16);
  flex-direction: column;
  flex-wrap: nowrap;
  align-items: stretch;
  align-self: start;
  justify-content: start;
  border-radius: var(--r-xl);
}

/* Le libellé sur deux lignes : le nom du levier, puis ce qu'il porte. La place
   est là, et c'est ce qui dit d'un coup d'œil quelles questions on va trouver. */
.onglets-vertical .onglet {
  justify-content: start;
  gap: calc(var(--spacing) * 12);
  padding: calc(var(--spacing) * 12) calc(var(--spacing) * 16);
  border-radius: var(--r-lg);
  text-align: start;
}

.onglets-vertical .onglet:hover:not([data-actif]) { background: rgb(28 28 28 / .04); }

.onglet-texte {
  display: flex;
  flex-direction: column;
  gap: calc(var(--spacing) * 2);
}

.onglet-sous {
  font-size: var(--legende);
  font-weight: 400;
  line-height: 1.45;
  letter-spacing: -.011em;
  color: var(--gris-lisible);
}

/* La scène. Armée, les quatre vues se superposent sur la même cellule, comme
   celles de Signal : **la scène prend la hauteur de la plus haute**, et la
   carte de Charly ne saute pas d'un onglet à l'autre. */
.faq-scene {
  display: grid;
  gap: var(--rythme-bloc);
}

.faq-vues[data-arme] .faq-scene { gap: 0; }

.faq-vues[data-arme] .faq-vue {
  grid-area: 1 / 1;
  opacity: 0;
  /* La visibilité sort la vue au repos de l'ordre de tabulation et de la
     voix. Elle bascule à la fin du fondu en sortie, et tout de suite en
     entrée. Le texte, lui, reste dans le document. */
  visibility: hidden;
  transition:
    opacity var(--motion-quick) var(--ease-out),
    visibility 0s linear var(--motion-quick);
}

.faq-vues[data-arme] .faq-vue[data-actif] {
  opacity: 1;
  visibility: visible;
  transition-delay: 0s;
}

/* Le titre de la vue. Sans script, il nomme le groupe. Armé, l'onglet le dit
   déjà : il sort de l'écran, et reste à la voix pour qui lit la vue seule. */
.faq-vue-titre {
  margin-block-end: calc(var(--spacing) * 12);
  font-size: var(--titre-sm);
  line-height: var(--lh-titre);
  letter-spacing: var(--ls-titre-petit);
}

.faq-vue-titre .chiffre { color: var(--gris-lisible); }

.faq-vues[data-arme] .faq-vue-titre {
  position: absolute;
  inline-size: 1px;
  block-size: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Les questions descendent d'un cran, de 24 à 20 px : elles tiennent sur huit
   douzièmes. */
.faq-vue .faq-item > summary {
  font-size: var(--corps-lg);
  padding-block: calc(var(--spacing) * 20);
}

@media (prefers-reduced-motion: reduce) {
  .faq-vues[data-arme] .faq-vue { transition: none; }
}

/* --------------------------------------------------------------------------
   La carte de Charly, sous les questions.

   La matière des cartes du site, et l'interface du dock posée dedans, avec
   ses propres classes : c'est le même objet, il se lit pareil. Le texte à
   gauche sur cinq douzièmes, l'interface à droite sur sept, comme la carte de
   la publicité.
   -------------------------------------------------------------------------- */
.faq-charly {
  display: grid;
  grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
  gap: calc(var(--spacing) * 48);
  align-items: center;
  padding: calc(var(--spacing) * 48);
  background-color: transparent;
  background-image: var(--fond-carte);
  border: 1px solid var(--gris-grille);
  border-radius: var(--r-xl);
  box-shadow: var(--lueur-carte), var(--ombre-carte);
}

/* Sans script, l'interface n'existe pas : le texte reprend toute la carte. */
.faq-charly:not(:has(.faq-chat:not([hidden]))) { grid-template-columns: minmax(0, 1fr); }

.faq-charly-texte {
  display: flex;
  flex-direction: column;
  gap: calc(var(--spacing) * 8);
}

.faq-charly-titre {
  font-size: var(--titre-lg);
  line-height: var(--lh-titre);
  letter-spacing: var(--ls-titre-petit);
}

/* « Demandez à Charly » est l'invitation, pas un détail : il prend le corps
   de chapô, en noir. */
.faq-charly-sous {
  font-size: var(--corps-lg);
  line-height: 1.5;
  color: var(--noir);
}

/* L'interface. La coque du dock est opaque et porte déjà sa lueur et son
   ombre ; posée dans une carte, elle prend l'ombre simple pour ne pas se
   détacher plus que la carte elle-même. */
.faq-chat { margin: 0; }
.faq-chat[hidden] { display: none; }

.faq-chat .chat-coque { box-shadow: var(--lueur-carte), var(--ombre-carte); }

/* Pas de fil ici : le filet d'en-tête suffit, celui de la saisie le
   doublerait. */
.faq-chat .chat-saisie { border-block-start: 0; }

.faq-chat .chat-champ { min-block-size: calc(var(--spacing) * 64); }

/* --------------------------------------------------------------------------
   16. ConditionBox : condition d'éligibilité ou mise en garde.
   Fond gris clair, pas de couleur d'alerte, pas d'icône : une mise en garde
   en rouge se lit comme une erreur du site.
   -------------------------------------------------------------------------- */

.condition-box {
  display: flex;
  flex-direction: column;
  gap: calc(var(--spacing) * 12);
  padding: calc(var(--spacing) * 32);
  background: var(--gris-clair);
  border-radius: var(--r-xl);
}

.condition-box p { max-width: var(--mesure-corps); }

.condition-titre {
  font-family: var(--font-titre);
  font-weight: var(--graisse-titre);
  font-size: var(--titre-sm);
  line-height: var(--lh-titre);
  letter-spacing: var(--ls-titre-petit);
}

.condition-liste {
  display: flex;
  flex-direction: column;
  gap: calc(var(--spacing) * 10);
  font-size: var(--corps-sm);
  line-height: 1.5;
}

.condition-liste li {
  display: grid;
  grid-template-columns: calc(var(--spacing) * 16) minmax(0, 1fr);
  gap: calc(var(--spacing) * 12);
}

.condition-liste li::before {
  content: "";
  margin-block-start: .55em;
  width: calc(var(--spacing) * 6);
  height: calc(var(--spacing) * 6);
  border-radius: var(--r-full);
  background: var(--noir);
}

/* --------------------------------------------------------------------------
   17. Blocs de page
   -------------------------------------------------------------------------- */

/* 01. Hero. Aucun logo client, aucune pastille de preuve. */
/* Le retrait haut était à .82 du rythme de section, soit 131 px, et les
   boutons tombaient sous la ligne de flottaison sur un écran de portable.
   Il descend à .42. Le retrait bas reste généreux : c'est lui qui laisse
   voir la calotte de la planète sous le contenu. */
.s-hero {
  /* L entonnoir, et ce qui en découle. Deux réglages tiennent la boîte, le
     canal et le retrait du texte : ils ne peuvent donc pas diverger quand on
     retouche l un des deux. */
  --largeur-entonnoir: clamp(calc(var(--spacing) * 340), 40vw, calc(var(--spacing) * 820));
  /* La boîte sort de la section par la droite, et c est ce débord qui rend
     la grande course possible. L axe du cône penche de dix degrés vers la
     droite et sa course va jusqu à cinquante : sans débord, il serait tranché
     par l arête de sa boîte dès le premier tiers visible. En débordant, il
     est coupé par le bord de l écran, ce qui se lit comme un cadrage et non
     comme une erreur. Le retrait du texte se calcule depuis la part visible,
     les deux ne peuvent donc pas diverger. */
  --debord-entonnoir: clamp(0px, 9vw, calc(var(--spacing) * 200));
  /* Le rab de gauche. Le débord ci-dessus règle le côté du bord de l écran ;
     celui-ci règle l autre, qui est dans l écran et où une arête droite se
     voit. Le cône penché sort de sa largeur nominale par la gauche quand il
     vise le plus à gauche : la boîte est donc plus large que lui de ce rab,
     et le canevas la couvre entière.

     ⚠️ La largeur nominale reste `--largeur-entonnoir`. Le rab agrandit la
     boîte, jamais le cône : si le cône se calait sur la boîte, il grandirait
     avec elle et son débordement suivrait, ce qui ne réglerait rien.

     Le second terme est la place réellement libre à gauche de la boîte, et
     c est lui qui commande dès que la fenêtre se resserre. Le conteneur est
     centré et plafonné à `--largeur-max` : ce qui dépasse de lui se partage
     en deux, et cette moitié-là est du vide entre la colonne de texte et la
     boîte. Le rab ne prend que ce vide. Il ne touche pas aux 40 px que le
     retrait du texte réserve en plus, qui restent la marge entre le cône et
     la lecture.

     Sous 1280 px, ce vide vaut zéro et le rab avec lui : il n y a alors plus
     de place à donner, et c est le voile de bord, dans site.js, qui éteint le
     cône sur l arête au lieu de l y trancher. */
  --rab-entonnoir: min(
    calc(var(--largeur-entonnoir) * .28),
    calc((100vw - min(100vw, var(--largeur-max))) / 2)
  );
  --taille-canal: calc(var(--largeur-entonnoir) * .12);
  /* L axe du cône penche de dix degrés à droite en moyenne, donc sa masse
     visible aussi. Le décalage remet cette masse au milieu du vide qui reste
     entre le texte et le bord de l écran. C est le contenu qui glisse dans la
     boîte, pas la boîte dans la page : déplacer la boîte reprendrait autant
     de pixels à la colonne de texte, et les trois preuves repasseraient sous
     les boutons. */
  --decalage-entonnoir: calc(var(--largeur-entonnoir) * .103);
  /* ⚠️ Mesuré à 1440 px sur vingt-huit secondes, soit un tour complet des sept
     moteurs : le décalage ramène la masse visible de 89 px à 36 px du centre
     du vide, et laisse 40 px entre le cône et la colonne de texte. Aller
     jusqu au centre exact demanderait 36 px de plus et ne laisserait que
     quatre pixels : le cône frôlerait le texte à chaque fois qu il penche à
     gauche. C est cette marge qui fixe la valeur, pas l œil. */
  position: relative;
  /* Ce qui dépasse se coupe ici. Sans ça, une boîte posée hors de la grille
     pousserait la page entière sur le côté. */
  overflow: hidden;

  /* Le fond doit remonter derrière la barre de navigation, sinon le hero
     s'ouvre sur un bandeau blanc. La section est donc tirée vers le haut de
     la hauteur de l'en-tête, et son retrait haut regagne la même valeur :
     la boîte part de zéro, le contenu ne bouge pas d'un pixel.

     On ne peut pas envelopper l'en-tête dans un conteneur à overflow caché
     pour arriver au même résultat : un ancêtre à overflow casse le sticky,
     et la barre cesserait de suivre le défilement. */
  margin-block-start: calc(var(--hauteur-entete) * -1);
  padding-block: calc(var(--rythme-section) * .42 + var(--hauteur-entete))
                 calc(var(--rythme-section) * .26);

  /* Le bloc de texte est centré sur la hauteur, comme l'entonnoir en face.
     Il était ancré en bas jusqu'au 15 septembre 2026 : le titre tombait sous
     le milieu et les deux blocs ne se répondaient pas. Le retrait haut reste
     plus grand que le bas, il porte la hauteur de l'en-tête ; le centrage se
     fait donc dans la boîte de contenu et non dans la boîte entière, ce qui
     laisse une dizaine de pixels d'écart avec le centre de l'entonnoir. À
     cette échelle, c'est sous le seuil du visible.

     svh et non vh : sur un téléphone, vh compte la barre d'adresse qui se
     replie, et le bloc finirait sous le pli. */
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-block-size: 100vh;
  min-block-size: 100svh;
}

/* La trame de points. Tout le dessin se fait au canevas : la position, l âge
   et le fondu de chaque tache révélée y sont gérés image par image. La règle
   ici ne fait que poser le calque. */
.hero-points {
  position: absolute;
  inset: 0;
  z-index: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

/* Sans pointeur fin, il n'y a pas de curseur à suivre : la trame ne sert à
   rien et ne se charge pas de calculs pour rien. */
@media (hover: none), (pointer: coarse) {
  .hero-points { display: none; }
}

/* --------------------------------------------------------------------------
   L'entrée du titre, mot par mot.

   La grammaire est relevée sur pulsor.agency, sur demande : chaque mot monte
   de 11 px, se révèle et se défloute depuis 10 px, décalé de proche en
   proche. Chez eux c'est GSAP, SplitText et Lenis ; la section 5 interdit
   toute dépendance, donc c'est réécrit en découpe maison et en keyframes.

   ⚠️ Elle joue **au chargement, et sur le seul premier écran**. La section 2
   dit « aucune apparition au défilement, nulle part » et ajoute que c'est la
   règle qui ne bouge pas. Une entrée au chargement n'est pas une apparition
   au défilement : rien n'attend plus bas qu'on descende pour se montrer. En
   étendre le principe aux sections suivantes rouvrirait cette règle.

   Le mot est en inline-block pour pouvoir bouger, ce qui n'empêche pas la
   ligne de se casser entre deux mots puisque les espaces restent dehors. */
.mot { display: inline-block; }

/* Le mouvement n'est armé qu'une fois la découpe mesurée. Le dégradé du titre
   est recollé mot par mot sur des positions relevées à l'arrêt, et un relevé
   pris pendant la montée les décalerait toutes de onze pixels. */
/* ⚠️ **Valeurs recalées sur pulsor.agency le 16 septembre 2026, sur demande**,
   courbe comprise. Le relevé du 14 donnait 11 px, 38 ms et 0,82 s en
   « ease-out » ; leur script dit autre chose, et c'est lui qui fait foi :
   yPercent 20, flou de 10 px, 1 s, 75 ms par mot, courbe « smooth-ease »
   cubic-bezier(.66, .01, .17, .97). Toutes les valeurs sont des jetons. */
[data-mots-prets] .mot {
  will-change: transform, opacity, filter;
  animation: mot-entre var(--titre-mots-duree) var(--titre-mots-courbe) both;
  animation-delay: calc(var(--retard, 0ms) + var(--i) * var(--titre-mots-pas));
}

@keyframes mot-entre {
  from { opacity: 0; translate: 0 var(--titre-mots-montee); filter: blur(var(--texte-flou)); }
  to   { opacity: 1; translate: 0 0; filter: blur(0); }
}

/* Le sous-titre, ligne par ligne : yPercent 60, flou de 10 px, 1,2 s en
   expo.out, 50 ms par ligne, 0,4 s après le départ du titre. Chez eux,
   data-load-sub au premier chargement. site.js pose le retard en --retard. */
[data-mots="lignes"][data-mots-prets] .mot {
  animation-name: sous-titre-entre;
  animation-duration: var(--sous-titre-duree);
  animation-timing-function: var(--sous-titre-courbe);
  animation-delay: calc(var(--retard, 0ms) + var(--ligne, 0) * var(--sous-titre-pas));
}

@keyframes sous-titre-entre {
  from { opacity: 0; translate: 0 var(--sous-titre-montee); filter: blur(var(--texte-flou)); }
  to   { opacity: 1; translate: 0 0; filter: blur(0); }
}

/* Deux écarts dans le H1, et le premier est un piège.

   ⚠️ **Un mot animé sort du découpage du dégradé de son parent.** Le
   `will-change` et la translation non nulle le promeuvent dans sa propre
   couche, et le `background-clip: text` du parent ne découpe plus que ce qui
   est resté avec lui. Le titre devient alors **entièrement invisible** : sa
   couleur est transparente et plus rien ne se peint dessous. Rien ne le
   signale, ni dans le HTML ni dans les styles calculés, où la règle du parent
   est toujours là. Elle ne s'applique simplement plus au mot.

   Chaque mot porte donc son propre dégradé, dimensionné sur la boîte du titre
   entier et décalé de la position qu'il y occupe. Le relevé est fait en JS
   juste après la découpe : le dégradé retrouve exactement le tracé qu'il avait
   quand la phrase était d'un seul tenant, au lieu de recommencer à chaque mot.

   ⛔ **Le flou revient sur le H1 le 16 septembre 2026 au soir, sur demande** :
   « leur H1 a un blur à l'apparition ». La crainte écrite ici était qu'il
   déborde de la boîte du mot, donc du dégradé : le filtre s'applique au mot
   déjà peint, dégradé compris, et son débord est flouté avec lui. Vérifié au
   rendu, pendant et après l'entrée. */
h1[data-mots-prets] .mot {
  animation-name: mot-entre;
  /* ⚠️ Le dégradé est découpé sur le texte, mais il n est peint que dans la
     boîte du mot. Or à un interligne de 1,05 la boîte est plus courte que les
     glyphes : le corps du titre donne 91 px de hauteur de police pour 75,9 px
     de ligne, et les jambages du g, du q et du p dépassent de 7,6 px sous
     elle. Sans fond dessous, ils ne se peignent pas, et le titre paraît coupé
     net à mi-hauteur des jambages.

     La marge négative rend exactement ce que la garniture prend : la boîte de
     marge, qui est celle que la ligne mesure, ne bouge pas d un pixel, et
     l interligne du titre reste celui du système. C est la boîte peinte, elle
     seule, qui s agrandit. */
  padding-block: .26em;
  margin-block: -.26em;
  /* Les côtés du glyphe débordent aussi : voir apparitions.css, h2 .lg-mot. */
  padding-inline: var(--garniture-glyphe);
  margin-inline: calc(-1 * var(--garniture-glyphe));
  background-image: var(--gradient-titre);
  background-size: var(--fond-l, 100%) var(--fond-h, 100%);
  background-position: var(--fond-x, 0) var(--fond-y, 0);
  background-repeat: no-repeat;
  -webkit-background-clip: text;
  background-clip: text;
}

@keyframes mot-entre-sans-flou {
  from { opacity: 0; translate: 0 var(--titre-mots-montee); }
  to   { opacity: 1; translate: 0 0; }
}

/* Comme partout : on retire le mouvement au lieu de le raccourcir. À 0,01 ms,
   les mots sauteraient en place au lieu d'entrer, et le titre clignoterait. */
@media (prefers-reduced-motion: reduce) {
  [data-mots-prets] .mot, h1[data-mots-prets] .mot { animation: none; }
}

/* --------------------------------------------------------------------------
   La lueur fixe du bloc de gauche.

   La page est en blanc cassé depuis le 15 septembre 2026, et le hero y pose du
   blanc franc sous sa colonne de texte. **Elle éclaircit au lieu d'assombrir**,
   et c'est ce qui la rend compatible avec la règle de contraste sans avoir
   rien à mesurer : le texte y est sur un fond plus clair que le reste de la
   page, donc mieux contrasté, pas moins.

   Elle est fixe. Ce n'est pas une lueur qui dérive comme les cinq retirées le
   14 septembre : c'est un dégradé posé. Il ne coûte pas une image par seconde
   et il ne compte dans aucun budget de mouvement.

   Deux couches. La large porte la masse et va jusqu'au bord gauche, la serrée
   rallume le centre de la colonne. Elle s'arrête bien avant l'entonnoir : son
   foyer est au cinquième de la largeur, et elle est éteinte aux deux tiers. */
.hero-lueur {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  /* ⚠️ La couleur vient du moteur affiché dans l entonnoir, et elle change
     avec lui. C est site.js qui pose --soleil, en trois composantes, à chaque
     pivot : la lueur et le cône ne peuvent donc pas se désaccorder, ils lisent
     le même mélange.

     Le repli est le blanc franc. Il sert avant que le script ait posé quoi que
     ce soit, et si l entonnoir ne tourne pas.

     ⚠️ La teinte est déjà noyée dans du blanc par le script, à 8 % seulement.
     Ce n est pas un réglage d apparence : la lueur est ce qui porte la colonne
     de texte, et une teinte franche y ferait descendre le fond perçu sous le
     minimum AA du gris de corps. Voir la mesure dans CLAUDE.md. */
  background:
    radial-gradient(42% 46% at 22% 46%, rgb(var(--soleil, 255 255 255) / .9), rgb(255 255 255 / 0) 72%),
    radial-gradient(78% 86% at 12% 52%, rgb(var(--soleil, 255 255 255) / .75), rgb(255 255 255 / 0) 68%);
}

/* Le contenu passe devant le décor. */
.hero-grille { position: relative; z-index: 1; }

/* Le label du hero, en ligne au-dessus du titre.

   Il a été posé sur le titre du 14 au 15 septembre 2026, accroché à un mot
   puis centré dans la gouttière entre les deux premières lignes, et il est
   revenu dans le flux sur demande. Rien ne reste de ce montage : ni la
   position absolue, ni le repère sur le h1, ni la mesure du centre du texte
   en JS. **En ligne, il n a besoin d aucune des trois.**
   -------------------------------------------------------------------------- */

.hero-grille {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  /* Centré sur la hauteur du hero, comme l entonnoir en face. Il était collé
     en bas : le titre tombait sous le milieu et les deux blocs ne se
     répondaient pas. */
  align-items: center;
  /* On réserve la boîte de l entonnoir, sa marge au bord et une gouttière,
     sinon le texte passerait sous les avatars. */
  padding-inline-end: calc(var(--largeur-entonnoir) - var(--debord-entonnoir) + var(--spacing) * 40);
}

/* ⚠️ Le H1 est en gras d'un seul tenant depuis le 15 septembre 2026. Il a été
   en deux graisses du 14 au 15 : le romain posait ce qu'on vend, le gras
   tombait sur la promesse. Les deux règles qui portaient ce partage sont
   retirées, et le titre reprend le 700 des jetons, comme tous les titres.

   La balise b reste dans le balisage : elle dit toujours quel segment compte, et
   c'est une information qui survit au style.

   ⚠️ Mais elle doit être remise à la graisse de son parent. Sans cette règle,
   le navigateur lui applique son `bolder` par défaut, qui vaut 900 face à un
   parent à 700 : le titre repart alors à deux graisses, et il ne se voit
   presque pas sur un seul mot. */
.s-hero h1 b { font-weight: inherit; }

/* La mesure display de 20 caractères est calée sur du display court. Le H1
   arrêté en fait soixante-dix, et à 20ch il tombe sur cinq lignes, ce qui
   est exactement le résultat que design-system.md refuse quand il choisit
   display-3xl plutôt que display-4xl. On reprend donc la mesure de titre,
   qui est une valeur du système, et elle ne borne plus rien : c'est la
   colonne de texte qui casse les lignes avant elle.
   Correction proposée : --mesure-display sert au display court, un H1 long
   se cale sur --mesure-titre.

   ⚠️ **Le titre descend d'un niveau pour tenir sur deux lignes**, sur demande
   du 15 septembre 2026. C'est de l'arithmétique, pas du goût. Relevé à
   1440 px : la colonne de texte fait 881 px, ce que l'entonnoir en face lui
   laisse. Des six coupes possibles du titre en deux lignes, la moins large
   demande 1 060 px à display-3xl, qui vaut 78 px ici. Il manquait 179 px, et
   les seuls endroits où les prendre étaient l'entonnoir, qu'il aurait fallu
   ramener à deux cents pixels de cône visible, ou le corps du titre.

   display-2xl vaut 61,6 px à cette largeur : la coupe la plus étroite y est
   rendue sur 846 px, et il reste 35 px. **Aucune valeur neuve n'entre dans le
   système** : la taille, l'interlettrage et l'interligne sont les trois
   valeurs que la section 5.2 pose ensemble sur la ligne des 60 px, et la
   règle du système veut qu'une taille qui descend desserre son interlettrage
   et ouvre son interligne.

   ⚠️ L'interlettrage compte dans ce calcul autant que le corps. En passant de
   -0,04 à -0,035 em, la ligne regagne un demi-centième d'em par caractère,
   soit dix pixels sur les trente-deux de la seconde ligne. Un calcul fait au
   seul rapport des corps donne 837 px et se trompe de dix pixels.

   ⚠️ **Deux lignes est une règle de grand écran, et le seuil est mesuré** :
   le titre tient sur deux lignes au-dessus de 1 390 px, et en dessous il en
   reprend trois, parce que la colonne se resserre plus vite que le corps ne
   descend. C'est la boîte de l'entonnoir qui prend la différence, et elle est
   proportionnelle à la fenêtre. Le détail des seuils est dans CLAUDE.md.

   ⚠️ **Le H1 a donc le corps des H2 de section, et la section 5.2 dit
   l'inverse** : elle choisit display-3xl pour le H1 en argumentant sur le
   nombre de lignes. La hiérarchie ne tient plus que par la place et par le
   dégradé. C'est l'arbitrage à confirmer, et les deux autres sorties sont
   écrites dans CLAUDE.md. */
.s-hero h1 {
  max-width: var(--mesure-titre);
  font-size: var(--display-2xl);
  line-height: 1.1;
  letter-spacing: var(--ls-display-2xl);
}

/* --------------------------------------------------------------------------
   L'entonnoir : ce qui arrive, par où ça passe, ce qui en sort

   Trois temps sur la verticale, et un seul canevas pour les trois : les
   avatars descendent, disparaissent dans le canal, et d'autres en
   ressortent. Le canal seul est du balisage, parce qu'il porte des images et
   qu'un changement d'image se fait mieux en opacité qu'en redessinant.
   -------------------------------------------------------------------------- */
.hero-entonnoir {
  /* La part de la course que la largeur disponible autorise. Le même angle
     ne tient pas de la même façon dans une boîte deux cents pixels plus
     étroite : sous 1600 px la course se resserre, sinon le cône passerait
     l essentiel de son temps hors champ au lieu de balayer. site.js lit
     cette valeur à chaque changement de moteur. */
  --facteur-angle: 1;
  position: absolute;
  z-index: 0;
  /* La boîte commence sous la barre de navigation. Le hero, lui, remonte
     derrière elle : sans ce retrait, un avatar descendrait derrière les
     liens et le bouton, ce qui se lit comme un défaut de calque.

     Et elle s'arrête avant le bas, pour que la composition soit centrée sur la
     hauteur qu'on en voit. La boîte n'est pas ce qu'on voit : le cône est
     éteint sur son premier cinquième, donc il laisse 14 % de la boîte vide en
     haut, quand les cartes descendent presque jusqu'en bas. Sans ce retrait
     bas, l'encre pèse une centaine de pixels sous le milieu. Le retrait le
     remonte, et il est en pourcentage pour suivre la hauteur de la fenêtre.

     ⚠️ Il raccourcit le cône d'autant : sa pointe est aux deux tiers de la
     boîte, donc deux tiers de ce qui est retiré en bas lui sont retirés à lui.
     C'est le prix du centrage, et c'est le seul réglage qui le donne : le haut
     ne peut pas descendre plus bas sans passer derrière la barre. */
  inset-block: calc(var(--hauteur-entete) + var(--spacing) * 16) 7.5%;
  inset-inline-end: calc(var(--debord-entonnoir) * -1);
  width: calc(var(--largeur-entonnoir) + var(--rab-entonnoir));
  /* Le rab est porté par le padding, et ce n est pas un détournement gratuit :
     c est la seule façon pour le script de lire sa valeur en pixels sans la
     recalculer de son côté, ce qui ferait deux vérités. Le canevas, lui, est
     posé en inset: 0 et couvre la boîte de padding entière, rab compris. */
  padding-inline-start: var(--rab-entonnoir);
  pointer-events: none;
}

.entonnoir-flux {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
}

/* Le canal. Même traitement que les anciennes pastilles : blanc, arrondi,
   détaché par l'ombre seule et sans liseré. */
.entonnoir-canal {
  position: absolute;
  /* Le canal n est pas au milieu mais aux deux tiers : le cône du haut est
     la partie qui doit se voir, la sortie n a besoin que de la place de
     quelques cartes. */
  inset-block-start: 66%;
  /* Le canal suit le décalage du cône. C est site.js qui lit ensuite sa
     position réelle pour y poser la pointe : les deux ne peuvent donc pas
     diverger, quelle que soit la valeur du décalage.

     Il est mesuré depuis le bord droit, et non depuis le milieu de la boîte :
     le rab s ajoute à gauche, et un milieu de boîte emmènerait le canal de la
     moitié du rab vers la droite à chaque fois qu on retoucherait ce rab. Au
     bord droit, la place du canal ne dépend que de la largeur nominale. */
  inset-inline-end: calc(var(--largeur-entonnoir) * .5 + var(--decalage-entonnoir));
  translate: 50% -50%;
  display: grid;
  place-items: center;
  width: var(--taille-canal);
  height: var(--taille-canal);
  border-radius: calc(var(--taille-canal) * .28);
  background: var(--blanc);
  /* La lueur intérieure. Le cône entre dans la carte par le côté d où il
     vient, et il y pose sa teinte : c est la même lumière, vue de l autre
     côté de la porte. Le script ne donne que la direction, en sinus et
     cosinus, et la couleur ; la portée et le flou restent ici, avec le reste
     des tailles, et suivent donc la carte quand elle change de taille.

     La direction est l opposée de celle du cône. Une ombre intérieure décalée
     vers la droite laisse sa nappe sur l arête gauche : pour que la lumière
     paraisse venir du cône, il faut décaler à l opposé de lui.

     Deux couches, comme pour l ombre portée du bouton : la large fait la
     nappe, la serrée rallume l arête. Une seule donnerait un halo plat.

     Les logos sont posés au-dessus : une ombre intérieure se peint sur le
     fond de la boîte et sous son contenu, la marque du moteur n est donc
     jamais lavée par la lueur. */
  --lueur-portee: calc(var(--taille-canal) * .34);
  --lueur-flou: calc(var(--taille-canal) * .52);
  box-shadow:
    0 calc(var(--spacing) * 8) calc(var(--spacing) * 28) rgb(28 28 28 / .14),
    inset calc(var(--lueur-portee) * var(--lueur-x, 0))
          calc(var(--lueur-portee) * var(--lueur-y, 1))
          var(--lueur-flou) var(--lueur-teinte, transparent),
    inset calc(var(--lueur-portee) * var(--lueur-x, 0) * .42)
          calc(var(--lueur-portee) * var(--lueur-y, 1) * .42)
          calc(var(--lueur-flou) * .34) var(--lueur-vive, transparent);
}

/* Les sept logos sont empilés dans la même case de grille, un seul opaque.
   C'est ce qui rend le changement instantané et sans saut de mise en page. */
.entonnoir-canal img {
  grid-area: 1 / 1;
  width: 54%;
  height: 54%;
  object-fit: contain;
  opacity: 0;
  transition: opacity var(--motion-slow) var(--ease-inout);
}

.entonnoir-canal img[data-actif] { opacity: 1; }

/* Le fondu se retire au lieu de se raccourcir, et site.js arrête le
   changement de logo : sans les deux, le canal passerait d'un logo à l'autre
   d'un coup sec toutes les quatre secondes. */
@media (prefers-reduced-motion: reduce) {
  .entonnoir-canal img { transition: none; }
}

.hero-sous {
  font-size: var(--corps-lg);
  line-height: 1.5;
  color: var(--gris-lisible);
  max-width: var(--mesure-corps);
}

/* ⚠️ **La réponse est séparée du constat par un écart de section, pas de
   bloc**, sur demande du 15 septembre 2026. Ce n'est pas un bloc de plus dans
   la même idée : les quatre manques se referment, et un autre temps s'ouvre.
   L'écart passe donc de 59,5 px à 80 px à 1440.

   ⚠️ **80 px est le plafond disponible, et c'est un problème connu.** C'est
   l'écart entre deux sections : au-delà, deux idées d'une même section seraient
   plus éloignées que deux sections, et la page perdrait sa hiérarchie. Le
   rythme relevé sur heysimon.fr donne 80 pour la section et 60 pour le bloc,
   soit un rapport de 1,35 quand la section 5.3 en demande 2,5. **Pour que cet
   écart puisse grandir, c'est le rythme de section qu'il faut remonter**, et ça
   se demande : c'est écrit en 5.3, avec les trois sorties possibles. */
.temps-suivant { margin-block-start: var(--rythme-section); }

/* --------------------------------------------------------------------------
   02. Le constat, en panneau sombre

   ⚠️ **C'est la deuxième section sombre de la page, et la section 2 dit qu'il
   n'y en a qu'une.** « Signal, et Signal seul, bascule en thème sombre. Il n'y
   a pas de mode sombre sur ce site, il y a une section sombre. » Il y en a deux
   depuis le 15 septembre 2026, sur demande, et **la règle est à réécrire dans
   le vault**, pas à contourner ici.

   **Ce qui la distingue quand même de Signal** : elle ne prend pas le thème.
   Aucun `data-theme`, donc ni le gradient chaud, ni les jetons de surface, ni
   l'accent rose. C'est un fond sombre et rien d'autre, et les jetons du thème
   Signal ne sont pas repris : **les cartes claires de la section les lisent en
   repli**, et les poser ici rendrait leur texte blanc sur du papier.

   **Les cartes ne changent pas.** Le papier posé sur du sombre se détache mieux
   que sur du clair, c'est le seul endroit où la section y gagne sans rien
   toucher. Ce qui change, c'est ce qui est posé à même le fond, et la carte qui
   recule, qui ne peut pas rester un gris sombre sur du sombre.
   -------------------------------------------------------------------------- */
/* La section ne porte plus de fond : elle porte le rythme, et le panneau
   sombre est un bloc dedans. Son retrait haut passe au panneau, sinon le
   sombre commencerait à cent neuf pixels sous le hero et le panneau en
   rajouterait autant à l'intérieur. */
.s-constat { padding-block-start: 0; }

/* ⚠️ **Le panneau déborde du conteneur et s'arrête à vingt pixels de chaque
   bord de l'écran**, sur demande du 15 septembre 2026. Le conteneur se recentre
   à l'intérieur, avec sa largeur maximale et ses marges : **le texte du constat
   reste donc aligné sur celui des autres sections**, seul le fond s'élargit. */
/* Le même panneau porte le rendez-vous depuis le 16 septembre 2026, sur
   demande : les deux sélecteurs partagent la règle pour ne pas diverger. */
.constat-sombre,
.panneau-sombre {
  margin-inline: calc(var(--spacing) * 20);
  padding-block: var(--rythme-section);
  background: #111111;
  border-radius: var(--r-2xl);
}

/* --------------------------------------------------------------------------
   Le fond sombre, mis au point sur fond.html et posé le 16 septembre 2026, sur
   demande, sur les trois surfaces sombres de la page : le constat, Signal et
   le rendez-vous.

   Trois couches. Le noir du panneau, qui ne change pas. Un dégradé radial plus
   clair en haut à droite, accentué le même jour sur demande : sa teinte la plus
   claire, #323232, est plafonnée par le contraste (voir le jeton). Et une grille de deux sur deux, invisible, dont
   la case haut gauche porte le logo en trame de points.

   ⚠️ **La grille passe sous le contenu par un contexte d'empilement isolé** :
   elle est en z-index négatif, donc au-dessus du fond du panneau et sous tout
   le reste, sans qu'aucun enfant ait à changer de position.

   ⚠️ **Signal et le chatbot ne font qu'un panneau** : le dégradé et la grille
   sont posés sur Signal seul. Un second dégradé en haut à droite du chatbot
   ferait une tache claire juste sous la jointure.

   ⚠️ **Le sélecteur pèse deux classes**, parce que Signal et les deux autres
   panneaux posent leur fond par un raccourci qui remettrait l'image à zéro.
   -------------------------------------------------------------------------- */
:is(.constat-sombre, .panneau-sombre, .s-signal).fond-sombre {
  position: relative;
  isolation: isolate;
  background-image: var(--degrade-fond-sombre);
}

.fond-sombre > .fond-grille {
  position: absolute;
  inset: 0;
  z-index: -1;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  grid-template-rows: repeat(2, minmax(0, 1fr));
  overflow: hidden;
  border-radius: inherit;
  pointer-events: none;
}

.fond-case { position: relative; }

.fond-trame {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
}

/* ⚠️ **Le panneau porte le rythme des deux sections qu'il enveloppe.** Elles
   gardent leur balise et leur rôle, mais pas leur garniture verticale : le
   panneau la donne une fois en haut et une fois en bas, et l'écart entre les
   deux vient d'une marge. Laissées à leur garniture, elles auraient ajouté deux
   rythmes de section au sien, soit trois cent quarante pixels de noir vide. */
.constat-sombre > .section,
.panneau-sombre > .section { padding-block: 0; }
.constat-sombre > .section + .section { margin-block-start: var(--rythme-section); }

/* Ce qui est posé à même le fond prend l'encre claire. Les valeurs sont celles
   du thème Signal, qui sont mesurées : 17,4 pour 1 pour le texte, 6,7 pour le
   secondaire. Elles sont écrites et non lues, pour ne pas les faire descendre
   dans les cartes claires. */
.sur-sombre { color: #F5F5F5; }

.sur-sombre .chapo { color: #9A9A9A; }

/* ⚠️ Le dégradé de titre est inversé sur ce fond : le noir vers gris n'y
   existerait pas. C'est le même angle et les mêmes arrêts, du blanc cassé vers
   le gris du système, qui tient 5,9 pour 1 sur ce fond à son extrémité la plus
   claire. */
.sur-sombre h2 {
  background-image: linear-gradient(50.26deg, #F5F5F5 43.855%, #8F8F8F 87.018%);
}

/* Le verre se fait clair sur un fond sombre, comme il se fait sombre sur un
   fond coloré : il pose toujours une teinte qui s'éloigne de son fond. */
/* ⚠️ **Sur le panneau sombre, c'est la pilule qui s'éclaircit, pas le violet
   qui se délave.** Le violet du système ne tient que 3,35 pour 1 sur ce fond,
   sous le minimum AA : posé sur une pilule blanche à 94 %, il tient **4,90** et
   garde sa valeur exacte. L'autre sortie aurait été d'éclaircir le violet, donc
   d'inventer une seconde teinte de marque pour un seul label.

   ⚠️ **C'est le seul endroit où la pilule n'est pas du verre.** Le verre pose
   une teinte qui s'éloigne un peu de son fond ; ici il faut une surface, parce
   que c'est elle qui porte le contraste du texte. */
/* ⚠️ **La pilule opaque est remplacée le 16 septembre 2026, sur demande** :
   presque transparente, un liseré violet d'un pixel et une lueur intérieure
   violette. Ce qui est au-dessus décrit la version retirée.

   ⚠️ **Le violet du système ne peut donc plus porter le texte.** Sans surface
   claire dessous, il retombe à 3,35 pour 1, sous le minimum AA pour un texte
   de 14 px. Le texte prend le violet mêlé de blanc à 45 %, **#AB9AF7**, qui
   tient plus de 6 pour 1 au cœur de la pilule, lueur comprise. C'est la sortie que la note
   ci-dessus écartait, une seconde teinte de marque : elle entre par cette
   demande, et elle ne sert qu'aux labels posés sur du sombre.

   Le liseré, lui, garde le violet exact : un trait n'est pas du texte, et il
   tient les 3 pour 1 d'un élément graphique. Les quatre labels concernés sont
   ceux du constat, de ce que ça coûte et du rendez-vous (le chatbot est
   supprimé le 17 septembre 2026). */
.sur-sombre .section-nom {
  /* ⚠️ **Éclaircie le même jour, sur demande** : la première version, violet à
     8 % et lueur du violet exact, se lisait trop sombre. La teinte monte à
     20 %, la lueur prend un violet éclairci, le liseré aussi, et le texte suit
     vers le clair pour garder son écart. */
  background: rgb(102 71 240 / .2);
  border-color: #8C76FF;
  color: #CFC6FF;
  box-shadow:
    inset 0 0 calc(var(--spacing) * 14) 0 rgb(140 118 255 / .6),
    inset 0 0 calc(var(--spacing) * 4) 0 rgb(170 152 255 / .5);
}

/* ⚠️ **La carte qui recule ne peut pas rester un gris sombre sur du sombre.**
   Son dégradé de noir la rendrait invisible et son texte noir illisible. Elle
   devient donc une carte de verre claire, très basse : même rôle, polarité
   inversée. Le blanc à 6 % sur ce fond donne un gris à 0,04 de luminance, où le
   blanc cassé tient 15,8 pour 1. */
.constat-sombre .duo-separe .duo-part.duo-part-gris {
  background-image: linear-gradient(
    to top,
    rgb(255 255 255 / .07) 0%,
    rgb(255 255 255 / .03) 100%);
  border-color: rgb(255 255 255 / .10);
  color: #F5F5F5;
  box-shadow:
    inset 0 1px 0 0 rgb(255 255 255 / .10),
    inset 0 0 calc(var(--spacing) * 60) 0 rgb(255 255 255 / .04);
}

.constat-sombre .duo-part-gris h3 { color: #F5F5F5; }

/* ⚠️ Le poids compte : la liste des cartes est posée par
   `.duo-separe .duo-part .duo-liste li`, qui pèse trois classes et un élément.
   La règle sombre doit peser plus, sinon le noir de la carte claire gagne et le
   texte disparaît sur le fond sombre. */
.constat-sombre .duo-separe .duo-part.duo-part-gris .duo-liste li { color: #D6D6D6; }

.constat-sombre .duo-part-gris .section-nom {
  background: rgb(255 255 255 / .08);
  border-color: rgb(255 255 255 / .16);
  color: #F5F5F5;
  box-shadow: inset 0 1px 0 0 rgb(255 255 255 / .14);
}

.constat-sombre .duo-part-gris .duo-marque {
  background: rgb(255 255 255 / .12);
  color: #F5F5F5;
}

/* --------------------------------------------------------------------------
   02 bis. Les trois constats, en colonnes

   Repris du pitch deck le 15 septembre 2026, sur demande, et mis en forme
   d'après une référence fournie : une icône dans une tuile, un filet qui court
   jusqu'au bord de la colonne, un titre, un texte. Ils remplacent les quatre
   rangées illustrées, qui sortent avec leurs quatre cadres et leurs quatre
   boutons.

   **Le filet n'est pas un ornement** : il mène l'œil de l'icône au bord et
   donne aux trois colonnes une même ligne de départ, ce qui les tient alignées
   sans cadre. C'est ce qui remplace la carte : sur un fond sombre, trois
   cartes claires auraient fait trois boîtes là où il n'y a que trois idées.

   ⚠️ **La tuile est claire et l'icône violette.** Le violet ne tient que 3,35
   pour 1 sur le fond sombre, il lui faut donc une surface, exactement comme à
   la pilule du label, et c'est la même surface : sur elle, il tient 4,79.
   -------------------------------------------------------------------------- */
.constats {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: calc(var(--spacing) * 40);
  list-style: none;
  margin: 0;
  padding: 0;
}

.constat {
  display: flex;
  flex-direction: column;
  gap: calc(var(--spacing) * 14);
}

.constat-tete {
  display: flex;
  align-items: center;
  gap: calc(var(--spacing) * 20);
  margin-block-end: calc(var(--spacing) * 10);
}

.constat-icone {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: calc(var(--spacing) * 52);
  block-size: calc(var(--spacing) * 52);
  border-radius: var(--r-md);
  /* ⚠️ **La tuile prend la matière des labels sur fond sombre depuis le
     16 septembre 2026, sur demande** : violet à 20 %, liseré et lueur
     intérieure en violet éclairci, tracé en violet presque blanc. Elle était
     opaque, avec le violet exact en tracé. Seul le rayon diffère du label : une
     tuile reste un carré arrondi. */
  border: 1px solid #8C76FF;
  background: rgb(102 71 240 / .2);
  color: #CFC6FF;
  box-shadow:
    inset 0 0 calc(var(--spacing) * 14) 0 rgb(140 118 255 / .6),
    inset 0 0 calc(var(--spacing) * 4) 0 rgb(170 152 255 / .5);
}

.constat-icone svg {
  inline-size: calc(var(--spacing) * 24);
  block-size: calc(var(--spacing) * 24);
}

.constat-filet {
  flex: 1;
  block-size: 1px;
  background: rgb(255 255 255 / .14);
}

.constat-titre {
  font-size: var(--titre-sm);
  color: #F5F5F5;
}

/* ⚠️ **Le texte monte de 16 à 18 px le 15 septembre 2026, sur demande.** Il
   était au bas de l'échelle de corps, sous un H2 à 60 px : l'écart se voyait.
   Le H2 est descendu à 40 le même jour, le texte monte au corps courant du
   site, et les trois niveaux de la colonne se suivent enfin, 40, 24, 18. */
.constat-texte {
  font-size: var(--corps);
  line-height: var(--lh-corps);
  color: #9A9A9A;
  max-width: none;
}

/* Le titre du constat, en trois temps, repris tel quel du deck.

   ⚠️ **Il portait trois encres et il n'en porte plus aucune depuis le
   15 septembre 2026, sur demande.** La pose était en gris, le coup en blanc
   cassé, la chute en violet : trois couleurs dans un titre, là où le reste du
   site n'en a qu'une. **Il prend maintenant le dégradé des H2 sur fond sombre**,
   le blanc cassé vers le gris du système, comme les dix autres titres de la
   page. C'est la règle du site, et l'exception qu'il fallait écrire pour lui
   tombe avec.

   Les intervalles ne sont donc plus là que pour casser la ligne aux trois
   temps. **Ils ne portent aucune couleur, et ils ne doivent pas en porter** :
   le dégradé est découpé sur le texte du parent, une encre posée sur un
   intervalle le recouvrirait.

   ⚠️ **Le violet part avec eux, et c'est un retour à la règle.** Il ne tenait
   que 3,35 pour 1 sur ce fond, et il n'y avait droit qu'au titre de gros texte.
   Sur le panneau sombre, il ne reste que là où il a une surface sous lui, la
   pilule du label et les trois tuiles d'icônes.

   **Seule la mesure est reprise au système** : ce titre prend la largeur du
   conteneur et non les 32 caractères, parce que ses trois temps se cassent déjà
   d'eux-mêmes.

   ⚠️ **Il n'est pas en italique, quand le deck l'est.** La section 5.2 ne pose
   que quatre fichiers de police et aucun italique : un italique synthétique
   penche les glyphes au lieu de les redessiner, et une cinquième police se
   justifie avant d'entrer. */
.sur-sombre h2.constat-h2 {
  width: auto;
  max-width: none;
}

.constat-h2 > span { display: block; }

/* 02. Constat. La chute est un titre depuis le 15 septembre 2026, et elle est
   remontée au-dessus des blocages : elle annonce ce qui suit au lieu de le
   conclure. L'encadré qui portait le verdict n'a donc plus d'objet, les deux
   colonnes du Duo le remplacent.

   ⚠️ La section porte deux h2. La hiérarchie reste continue, il n'y a pas de
   saut de niveau, et le second ouvre un temps que le premier n'ouvrait pas.
   C'est un écart au reste de la page, où chaque section n'en porte qu'un, et
   il est noté.

   ⚠️ **Les deux listes du Duo sont identiques en tout point** : même marqueur,
   même corps, même couleur, même nombre de lignes. La colonne de gauche est là
   pour perdre la comparaison, et la loi du composant dit qu'une colonne
   ridiculisée par le design annule l'argument. Pas de coche verte en face
   d'une croix rouge : ce sont les mots qui font la différence. */
/* ⛔ **Ce qui suit n a plus de balisage depuis le 15 septembre 2026.** Le Duo du
   constat est sorti de la page avec sa chute, qui est montée en titre de la
   section 04 : les deux cartes colorées, les listes à marques et le partage à
   cinq rangées ne servent plus. **Le Duo lui-même vit encore**, en section 03,
   sur les deux courbes du coût du lead, et ses règles de base restent donc en
   place. Celles-ci sont gardées pour que le bloc revienne d une seule pose, et
   **elles sortent avant la mise en ligne s il ne revient pas.** */
.duo-liste {
  display: flex;
  flex-direction: column;
  gap: calc(var(--spacing) * 14);
  margin-block-start: calc(var(--spacing) * 4);
  font-size: var(--corps-sm);
  line-height: 1.5;
}

/* La liste rend ses trois points aux rangées partagées par les deux cartes. */
.duo-separe[data-rangs="5"] .duo-liste {
  display: grid;
  grid-template-rows: subgrid;
  grid-row: span 3;
  row-gap: calc(var(--spacing) * 14);
}

.duo-liste li {
  display: grid;
  grid-template-columns: calc(var(--spacing) * 24) minmax(0, 1fr);
  gap: calc(var(--spacing) * 12);
  align-items: start;
  color: var(--gris-lisible);
}

/* ⚠️ **Les deux marques diffèrent depuis le 15 septembre 2026, sur demande** :
   une croix à gauche, une coche à droite. La loi du Duo dit qu'une colonne
   ridiculisée par le design annule l'argument, et c'est le troisième point de
   cette carte qui s'en écarte, après la couleur et le fond.

   **Ce qui est refusé et qui ne revient pas** : la croix rouge et la coche
   verte. La section 2 interdit la flèche verte à côté d'un chiffre, et la
   section 6 dit qu'une mise en garde en rouge se lit comme une erreur du site.
   Les deux marques sont donc de la même encre que leur texte, et c'est leur
   forme seule qui les oppose.

   Les deux tracés viennent de coolicons v4.1, « check » et « close-md », fournis le
   15 septembre 2026. Ils sont recopiés dans le balisage, en courant d'encre :
   **aucun fichier, aucune police d'icônes, aucune dépendance n'entre**.
   ⚠️ L'archive fournie ne contient aucune licence. Coolicons est donné pour du
   MIT sur son dépôt, mais ça n'est pas vérifiable depuis ce qui a été livré :
   **à confirmer avant mise en ligne.** */
.duo-marque {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: calc(var(--spacing) * 24);
  block-size: calc(var(--spacing) * 24);
  margin-block-start: -.08em;
  border-radius: var(--r-full);
  background: var(--gris-clair);
  color: var(--noir);
}

/* ⚠️ **La croix et la coche sont revenues à l'encre de leur carte le
   15 septembre 2026.** Elles ont été rouge et verte quelques heures le même
   jour, sur demande, puis retirées sur avis : une croix rouge en face d'une
   coche verte est le code du comparatif de logiciel, et la section 2 écarte la
   flèche verte pour exactement cette raison. **L'opposition tient dans la forme
   des deux tracés**, elle n'a pas besoin de la couleur, et la section garde
   ainsi une seule couleur à l'écran.

   La pastille prend la valeur opposée à son fond : claire sur la carte sombre,
   grise sur le papier. Le tracé est noir dans les deux cas, à plus de dix pour
   un sur sa pastille.

   ⚠️ **Le jeton `--succes` reste défini et ne sert plus.** Il est laissé en
   place parce que la décision peut revenir, et parce qu'une couleur qui entre
   puis sort deux fois vaut d'être écrite une fois pour toutes. **S'il n'est pas
   repris, il sort du fichier avant la mise en ligne.** */
.duo-part .duo-marque {
  background: var(--gris-clair);
  color: var(--noir);
}

.duo-part-gris .duo-marque {
  background: rgb(255 255 255 / .62);
  color: var(--noir);
}

.duo-marque svg {
  inline-size: calc(var(--spacing) * 15);
  block-size: calc(var(--spacing) * 15);
}

/* Les deux cartes se détachent, au lieu de partager une boîte et un filet.
   C'est une variante du Duo et non un réglage du Duo : la section 03 garde les
   deux colonnes jointes, où la comparaison porte sur la même ligne. */
.duo-separe {
  gap: calc(var(--spacing) * 24);
  background: none;
  border: 0;
  border-radius: 0;
  overflow: visible;
  /* Cinq rangées partagées : le label, le titre, puis **une rangée par point
     de la liste**. Les deux cartes s'y accrochent en sous-grille, et la liste
     de chacune s'accroche à son tour aux trois dernières. La croix n° 1 tombe
     donc à la hauteur de la coche n° 1, et ainsi de suite, quelle que soit la
     longueur des textes.

     ⚠️ C'est une sous-grille dans une sous-grille, et il en faut deux : la
     carte pour partager les rangées avec sa voisine, la liste pour rendre ses
     trois points à ces rangées au lieu de les empiler chez elle.

     Les deux cartes ont la même garniture, donc le décalage qu'elle introduit
     est le même des deux côtés. Un moteur sans sous-grille ignore les deux
     règles et retombe sur des rangées automatiques : les cartes restent justes,
     elles ne s'alignent plus. */
  /* Le nombre de rangées partagées dépend de ce que la carte contient : trois
     pour un titre, un paragraphe et une figure, cinq quand une liste rend ses
     points à la grille. Il est posé en attribut plutôt qu'en classe, comme le
     nombre de colonnes du CardGrid. */
  grid-template-rows: repeat(var(--duo-rangs, 3), auto);
  align-items: stretch;
}

.duo-separe[data-rangs="5"] { --duo-rangs: 5; }

.duo-separe .duo-part {
  display: grid;
  grid-template-rows: subgrid;
  grid-row: span var(--duo-rangs, 3);
  /* ⚠️ 48 px au lieu des 36 du Duo joint, sur demande du 15 septembre 2026.
     Une carte serrée se lit comme un tableau comparatif, une carte large se lit
     comme une page. C'est le second levier du registre, après l'air entre les
     sections. La règle de rupture sous 1024 px porte sur .duo-part, qui pèse
     moins : elle est reprise plus bas pour ces cartes. */
  padding: calc(var(--spacing) * 48);
  border: 1px solid var(--gris-grille);
  /* ⚠️ Le rayon descend de 52 à 24, sur demande du 15 septembre 2026 : un
     arrondi serré se lit plus cher qu'un arrondi mou. C'est le rayon du Duo
     joint de la section 03, donc rien de neuf n'entre. */
  border-radius: var(--r-xl);
}

/* La pilule ne s'étire pas : en grille, un enfant prend toute la colonne. */
.duo-separe .section-nom { justify-self: start; }

/* Les deux cartes du Duo prennent la même matière que les rangées : dégradé de
   blanc, lueur intérieure et jeu d'ombres. Au repos elles sont identiques, ce
   qui rend à la loi du composant l'égalité de poids que le bleu plein lui avait
   prise. */
.duo-separe .duo-part {
  position: relative;
  background-color: transparent;
  background-image: var(--fond-carte);
  box-shadow: var(--lueur-carte), var(--ombre-carte);
}

/* **L'écart entre les deux cartes est une affaire de valeur, pas de couleur.**
   Le système est du papier, plus clair que la page ; le sans système est une
   surface morte, franchement plus sombre. Entre les deux, le lecteur sait où
   regarder sans qu'on ait eu à surligner quoi que ce soit, et c'est ce qui
   sépare un registre de cabinet d'un tableau de prix de logiciel.

   Les deux dégradés gardent la grammaire de la section, denses en bas et
   effacés en haut. **Aucune couleur n'entre : c'est la valeur qui parle**, et
   c'est la même encre que le texte.

   ⚠️ L'écart a été essayé à 13 % contre 7 % le même jour, et jugé timide. Il
   est maintenant de 16 % de noir contre 85 % de blanc.

   ⚠️ **Les deux listes sont en noir.** Sur la carte sombre, le gris de corps ne
   tient que 3,3 pour 1, sous le minimum AA ; le noir y tient 10,6. Sur la carte
   de papier, le gris passerait, mais le mettre là et le noir en face donnerait
   plus de poids au problème qu'à la réponse. **Même encre des deux côtés**,
   c'est la dernière symétrie qui reste à ce Duo, et elle a aussi l'avantage de
   ne rien faire changer au survol.

   ⚠️ **Le bleu du survol se pose sur du papier**, comme avant l'essai des deux
   gris : le calcul de contraste du survol est inchangé. */
.duo-separe .duo-part.duo-part-bleu {
  background-image: linear-gradient(
    to top,
    rgb(255 255 255 / .85) 0%,
    rgb(255 255 255 / .30) 100%);
  box-shadow: var(--lueur-carte), var(--ombre-carte);
}

.duo-separe .duo-part.duo-part-gris {
  background-image: var(--fond-carte-sombre);
  box-shadow: var(--lueur-carte-douce), var(--ombre-carte);
}

.duo-separe .duo-part .duo-liste li { color: var(--noir); }

/* Le contenu passe devant le calque bleu, qui est en position absolue et donc
   peint après les boîtes du flux. */
.duo-separe .duo-part > * { position: relative; z-index: 1; }

/* ⚠️ **Le bleu est sur un calque, et c'est la seule façon de l'animer.** Un
   dégradé n'est pas une valeur interpolable : `background-image` ne se
   transitionne pas, la couleur sautait d'un coup. Le bleu est donc peint sur un
   `::before` qui couvre la carte, et c'est **son opacité** qui passe de zéro à
   un. Une opacité, elle, s'anime.

   Le calque est en position absolue, donc il sort du flux et **ne devient pas
   un élément de la sous-grille** qui aligne les deux cartes. Il déborde d'un
   pixel pour couvrir le liseré, et son rayon suit d'autant.

   ⚠️ La durée est `quick`, 220 ms, quand la section 5.5 pose 120 ms pour les
   états. Une surface de cette taille qui bascule en 120 ms se lit comme un
   saut, pas comme une transition. C'est un écart assumé, du même ordre que les
   450 ms du libellé qui roule.

   ⚠️ **Le texte ne change pas de couleur et reste noir, et c'est mesuré.** Sur
   ce bleu ciel, le blanc donne **1,48 pour 1** au point le plus dense de la
   carte : il n'y a pas de voile qui puisse le sauver, puisque le fond
   s'éclaircit vers le haut. Le noir, lui, tient **5,91** en bas et **11,03** en
   haut. **Pour du texte blanc, il faudrait abandonner le bleu ciel** et
   reprendre le bleu voilé, qui est deux tons plus sombre. */
.duo-part-bleu::before {
  content: "";
  position: absolute;
  inset: -1px;
  z-index: 0;
  border-radius: calc(var(--r-xl) + 1px);
  background-image: var(--fond-carte-bleu);
  box-shadow: var(--lueur-carte);
  opacity: 0;
  transition: opacity var(--motion-quick) var(--ease-out);
  pointer-events: none;
}

.duo-separe .duo-part.duo-part-bleu:hover::before { opacity: 1; }

/* ⚠️ **La liste passe au noir sous le calque, et c'est une obligation de
   contraste.** Le gris de corps ne tient que **1,82 pour 1** sur le bas de la
   carte bleue, quand le noir y tient 5,91 et 11,03 en haut. C'est la seule
   chose qui change d'encre : le titre est déjà en noir, et le label lit son
   verre. La couleur s'anime avec le fond, sur la même durée. */
.duo-liste li { transition: color var(--motion-quick) var(--ease-out); }

.duo-separe .duo-part.duo-part-bleu:hover .duo-liste li { color: var(--noir); }

/* ⚠️ Sans souris, il n'y a pas de survol, et la carte du système resterait
   blanche pour de bon. Elle prend donc sa couleur au repos sur un pointeur
   grossier : c'est le seul endroit du site où un état de survol a un
   équivalent posé, et c'est parce qu'il porte un sens, pas un ornement. */
@media (hover: none), (pointer: coarse) {
  .duo-separe .duo-part.duo-part-bleu::before { opacity: 1; }
  .duo-separe .duo-part.duo-part-bleu .duo-liste li { color: var(--noir); }
}

/* --------------------------------------------------------------------------
   03 bis. Les deux courbes du coût du lead

   ⚠️ **Elles se tracent quand elles arrivent à l'écran, et c'est une apparition
   au défilement.** La section 2 dit « aucune apparition au défilement, nulle
   part » et ajoute que c'est la règle qui ne bouge pas. L'entrée du titre du
   hero avait été tolérée parce qu'elle joue au chargement et sur le premier
   écran, et la note de CLAUDE.md disait alors : en étendre le principe aux
   sections suivantes rouvrirait cette règle, et il faudrait la rouvrir pour de
   bon, pas au coin d'une intégration. **C'est fait le 15 septembre 2026, sur
   demande, et c'est écrit dans CLAUDE.md comme une réouverture.** Portée : deux
   courbes, une section, une fois par chargement, et rien d'autre ne suit.

   **Sous `prefers-reduced-motion`, le tracé n'a pas lieu** : la courbe est
   entière à la première image. On retire le mouvement, on ne le raccourcit pas.

   Le tracé se fait sur `stroke-dashoffset`, avec `pathLength` ramené à 1 :
   la longueur réelle du chemin n'a alors plus à être mesurée, et les deux
   courbes se tracent exactement à la même vitesse bien qu'elles n'aient pas la
   même longueur.

   ⚠️ La durée vaut deux fois `motion-slow`. La section 5.5 ne prévoit rien de
   plus long que 450 ms, mais un trait qui se dessine en 450 ms ne se lit pas
   comme un tracé, il clignote. La valeur reste attachée au jeton pour ne pas
   ouvrir une échelle de plus.
   -------------------------------------------------------------------------- */
.courbe {
  margin: 0;
  margin-block-start: calc(var(--spacing) * 8);
  display: flex;
  flex-direction: column;
  gap: calc(var(--spacing) * 10);
}

.courbe svg {
  display: block;
  inline-size: 100%;
  block-size: auto;
  overflow: visible;
}

/* ⚠️ La ligne des 100 € porte une valeur, ce n est pas un cadre : elle prend
   le gris qui tient 3,24 pour 1, le minimum d un élément graphique porteur de
   sens. Le trait du bas, lui, n est que la base du cadre et garde le gris des
   séparateurs. */
.courbe-echelle line {
  stroke: var(--gris-grille);
  stroke-width: 1;
}

.courbe-echelle line[stroke-dasharray] { stroke: var(--gris-texte); }

.courbe-echelle text {
  font-family: var(--font-corps);
  font-size: 10px;
  letter-spacing: -.011em;
  fill: var(--gris-lisible);
}

/* ⚠️ **Les deux courbes ne sont plus de la même couleur depuis le 15 septembre
   2026, sur demande** : le rouge pour ce qui est acheté, le vert pour ce qui
   est construit. La loi du Duo veut deux colonnes de poids strictement égal, y
   compris celle qui est là pour perdre la comparaison : **c'est le second
   endroit de la page où elle est renversée**, après le Duo du constat.

   ⚠️ **Et ça met un rouge et un vert d'état sur un graphique**, ce que la
   section 2 écarte avec la flèche verte à côté d'un chiffre, et la section 6
   avec le rouge qui se lit comme une erreur du site. C'est demandé, c'est
   assumé, et c'est reporté tel quel.

   Les deux teintes sont celles du système, et les contrastes sont mesurés :
   **4,76 pour 1 pour le rouge, 5,02 pour le vert**, sur le blanc de la carte.
   Les deux portent du texte à ce corps, il leur faut donc les 4,5 pleins, et
   pas les 3 d'un élément graphique.

   La teinte est posée une fois sur la figure, et le trait, la nappe, le point
   et le prix la lisent : une courbe ne peut pas se retrouver à moitié rouge. */
.courbe-achete    { --teinte: var(--erreur); }
.courbe-construit { --teinte: var(--succes); }

.courbe-trace {
  fill: none;
  stroke: var(--teinte);
  stroke-width: 2.5;
  stroke-linecap: round;
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
  transition: stroke-dashoffset calc(var(--motion-slow) * 2) var(--ease-out);
}

/* La nappe monte pendant que le trait se dessine, avec un temps de retard : le
   trait ouvre, la couleur suit. Elle ne se dessine pas, elle se lève. */
.courbe-nappe {
  opacity: 0;
  transition: opacity calc(var(--motion-slow) * 2) var(--ease-out) var(--motion-quick);
}

/* Le point de départ et son prix. **Les deux courbes partent du même prix**,
   celui du copy arrêté, et c'est ce qui rend les deux directions comparables.
   Ils arrivent une fois le trait posé : d'abord la trajectoire, puis le chiffre
   qui la date. */
.courbe-point {
  fill: var(--teinte);
  opacity: 0;
  transition: opacity var(--motion-quick) var(--ease-out) calc(var(--motion-slow) * 2);
}

/* ⚠️ Le prix est en graisse légère depuis le 15 septembre 2026, sur demande.
   Quatre cents, et non trois : les deux fontes du site sont chargées sur l'axe
   400 à 700, une demande de 300 y serait ramenée à 400 par le moteur, et une
   vraie Light demanderait un fichier de plus. C'est la même décision que sur le
   label du hero. */
.courbe-prix {
  fill: var(--teinte);
  font-family: var(--font-corps);
  font-size: 12px;
  font-weight: 400;
  letter-spacing: -.011em;
  opacity: 0;
  transition: opacity var(--motion-quick) var(--ease-out) calc(var(--motion-slow) * 2);
}

/* ⚠️ La flèche est en noir et non à la teinte de la courbe, sur demande : elle
   dit la direction, la couleur dit déjà le camp. Une flèche verte à côté d'un
   chiffre est dans les interdits de la section 2 ; en noir, elle n'est plus
   qu'un signe de lecture. */
.courbe-fleche { fill: var(--noir); }

/* La barre lit la courbe. Elle part de « now », descend toute la hauteur du
   cadre, et se déplace le long de l'axe : le point la suit sur la courbe, et le
   prix se lit sur l'échelle, zéro à la base et 100 € sur la ligne en tiret. */
.courbe-barre {
  stroke: var(--gris-texte);
  stroke-width: 1;
  opacity: 0;
  transition: opacity var(--motion-quick) var(--ease-out) calc(var(--motion-slow) * 2);
}

/* Le mois que la barre a atteint, sous l'axe. **Il s'efface près des deux
   bouts**, où les repères fixes disent déjà le temps et où les deux libellés se
   superposeraient. */
.courbe-temps {
  font-family: var(--font-corps);
  font-size: 10px;
  letter-spacing: -.011em;
  fill: var(--noir);
  opacity: 0;
  transition: opacity var(--motion-quick) var(--ease-out);
}

[data-tracee] .courbe-temps { opacity: 1; }

[data-tracee] .courbe-trace { stroke-dashoffset: 0; }
[data-tracee] .courbe-nappe,
[data-tracee] .courbe-barre,
[data-tracee] .courbe-point,
[data-tracee] .courbe-prix { opacity: 1; }

/* 03. Le bloc publicité ne ressemble pas à une note de bas de section :
   même poids typographique que le reste, sinon il n'est pas lu. */
/* ⚠️ **Le bloc de la publicité est une carte pleine largeur depuis le
   15 septembre 2026, sur demande.** Il était un titre et un paragraphe posés
   nus sous les deux cartes du coût, et il en avait pris l'air d'une note de bas
   de section, ce que la section 7.03 interdit explicitement : s'il est petit,
   gris ou replié, il n'est pas lu, et il perd sa fonction, qui est d'empêcher
   un dirigeant dont la régie tourne de lire une attaque.

   **Il prend donc la matière des autres cartes**, et il prend toute la largeur.
   C'est ce qui le sort du rang sans en faire une troisième option : deux
   colonnes côte à côte se comparent, une pleine largeur se lit comme une prise
   de parole.

   **Le titre tient sa colonne et le texte la sienne**, cinq douzièmes contre
   sept. Le texte garde le corps courant et le noir plein : la section 7.03 veut
   le même poids typographique que le reste, et c'est la seule chose qui ne se
   négocie pas ici. */

/* ⚠️ **Sur le panneau sombre, ce bloc ne peut pas rester un gris sombre.** Son
   dégradé de noir le rendrait invisible et son texte noir illisible : c'est
   exactement la panne que la carte du Duo avait déjà rencontrée sur ce fond, et
   c'est la troisième fois. Il devient une carte de verre claire, très basse :
   **même rôle, polarité inversée.**

   ⚠️ **Le titre et le texte tiennent 14,59 et 10,95 pour 1** sur cette surface,
   là où le noir y tomberait à 1,4. La section 7.03 veut le même poids typographique
   que le reste de la section, et c'est ce qui ne se négocie pas : le corps et la
   graisse ne bougent pas, seule l'encre s'inverse.

   ⚠️ **Le poids du sélecteur compte**, comme les deux fois précédentes : la
   règle claire pèse une classe, celle-ci en pèse deux. */

/* --------------------------------------------------------------------------
   05. L'opportunité, reprise de la planche du même nom du pitch deck.

   Trois atouts empilés d'un côté, l'illustration de l'autre, et une bande
   dessous. **Rien de neuf n'entre dans le système** : la matière des atouts
   est celle des cartes, le cadre est le cadre de remplacement du site, et le
   violet de la bande est celui des labels.
   -------------------------------------------------------------------------- */

/* Le mois vient du navigateur en minuscules, comme l'usage typographique
   français le veut, et il ouvre ici une phrase. La capitale est donc posée par
   le style et non par le script : une seconde règle de casse dans le script
   ferait diverger ce titre de celui du constat, qui lit le même mois. */
.opp-mois { text-transform: capitalize; }

/* Cinq douzièmes contre sept, comme la carte de la publicité : le texte tient
   sa colonne, l'illustration la sienne.

   ⚠️ **Les deux colonnes font la même hauteur depuis le 16 septembre 2026, sur
   demande** : la pile des trois atouts se termine exactement où l illustration
   se termine. Les deux montants sont donc étirés sur la rangée, et non calés en
   haut. C est la rangée qui décide, et elle prend le plus haut des deux : si le
   texte des atouts grandit, c est l illustration qui suit, et jamais l inverse.
   **Rien n est donc jamais comprimé.** */
.opp-grille {
  display: grid;
  grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
  gap: calc(var(--spacing) * 48);
  align-items: stretch;
}

.opp-atouts {
  display: flex;
  flex-direction: column;
  gap: calc(var(--spacing) * 16);
  height: 100%;
}

/* Le rab de hauteur se répartit également sur les trois, et pas au prorata de
   leur texte : une carte qui grandirait plus que ses voisines parce qu elle en
   dit plus se lirait comme la plus importante, et les trois pèsent pareil.
   La base reste le contenu, donc aucune carte ne se retrouve jamais à l étroit. */
.opp-atout { flex: 1 1 auto; }

/* Le titre et l'icône sur la même ligne, le texte sous les deux et sur toute
   la largeur : l'icône est posée à droite dans le deck, et un texte qui
   l'enjamberait perdrait vingt-quatre pixels de mesure pour rien. */
.opp-atout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  column-gap: calc(var(--spacing) * 16);
  row-gap: calc(var(--spacing) * 10);
  align-items: start;
  padding: calc(var(--spacing) * 24) calc(var(--spacing) * 28);
  background-color: transparent;
  background-image: var(--fond-carte);
  border: 1px solid var(--gris-grille);
  border-radius: var(--r-xl);
  box-shadow: var(--lueur-carte), var(--ombre-carte);
}

.opp-atout-titre {
  grid-column: 1;
  grid-row: 1;
  font-family: var(--font-titre);
  font-weight: var(--graisse-titre);
  font-size: var(--titre-sm);
  line-height: var(--lh-titre);
  letter-spacing: var(--ls-titre-petit);
}

/* L'icône est noire, pas violette. Le violet ouvre une section, il ne vit pas
   dans une carte : c'est la même règle que les labels du Duo et des rangées. */
.opp-atout-icone {
  grid-column: 2;
  grid-row: 1;
  display: inline-flex;
  color: var(--noir);
}

.opp-atout-icone svg {
  display: block;
  width: calc(var(--spacing) * 24);
  height: calc(var(--spacing) * 24);
}

/* Le gris lisible tient 4,57 pour 1 au point le plus sombre du dégradé de
   carte, et 5,02 au plus clair. Assombrir ce dégradé demande de passer ce
   texte au noir, comme sur la carte sombre du constat. */
.opp-atout-texte {
  grid-column: 1 / -1;
  max-width: none;
  font-size: var(--corps-sm);
  line-height: 1.5;
  color: var(--gris-lisible);
}

/* L'illustration du deck est en 4/3, et ce rapport est un plancher, pas une
   règle : il donne la hauteur de la rangée tant que les trois atouts tiennent
   dessous, et il cède dès qu ils en demandent plus. Une hauteur définie et une
   largeur définie l emportent ensemble sur le rapport, il n y a donc rien à
   désactiver. **C est ce qui garantit les deux colonnes à la même hauteur sans
   jamais rogner un texte.** */
.opp-cadre { --cadre-rapport: 4 / 3; }

/* La bande violette du deck est remplacée par le bouton du système : rien à
   styler ici, le bouton primaire porte déjà tout. Seul reste l écart qui le
   détache des atouts, et c est celui des blocs. */

/* 07d. Les engagements : trois par écran au maximum, et toujours des majeurs.
   L'ordre est l'ordre de force : exclusivité, propriété, garantie.

   ⛔ **Ce bloc n a plus de balisage depuis le 15 septembre 2026, sur demande.**
   Ses règles restent ici pour la même raison que celles du tableau de garantie,
   et elles sortent avec lui si les deux ne reviennent pas. */
.engagements {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: calc(var(--spacing) * 24);
  counter-reset: engagement;
}

.engagement {
  display: flex;
  flex-direction: column;
  gap: calc(var(--spacing) * 10);
  padding-block-start: calc(var(--spacing) * 20);
  border-block-start: 2px solid var(--noir);
}

.engagement p { max-width: none; }

/* 11. Le rendez-vous. Un seul bouton, aucun champ de qualification. */
.s-rdv {
  background: var(--gris-clair);
  border-radius: var(--r-3xl);
  padding: calc(var(--rythme-section) * .62) calc(var(--spacing) * 48);
}

.rdv-inner {
  display: flex;
  flex-direction: column;
  gap: calc(var(--spacing) * 24);
  max-width: calc(var(--spacing) * 720);
}

/* --------------------------------------------------------------------------
   18. La section Signal : le seul moment sombre du site.
   Portée locale via data-theme, jamais sur html. Aucun basculeur, aucun
   prefers-color-scheme. Le violet ne suit pas : en sombre, l'accent est le
   magenta du gradient chaud.
   -------------------------------------------------------------------------- */

/* ⚠️ **Le panneau s'arrête à vingt pixels de chaque bord de l'écran depuis le
   16 septembre 2026, sur demande.** Il tenait dans le conteneur, donc de 113 à
   1 313 à 1440 px. C'est la mesure du panneau sombre du constat, reprise telle
   quelle, et les deux surfaces sombres de la page ont maintenant la même
   emprise.

   **Le bloc s'échappe du conteneur au lieu d'en sortir.** Il reste dans le
   flux, et il reprend en marge négative exactement ce que le conteneur lui a
   pris de chaque côté.

   ⚠️ **La marge latérale ne s'ajoute pas à ce calcul, et l'avoir ajoutée
   coûtait quarante pixels.** « 50 % » se mesure sur la boîte de contenu du
   parent, qui est **déjà à l'intérieur** du retrait du conteneur : « 50% - 50vw »
   ramène donc au bord de l'écran, retrait compris. Relevé avant correction :
   le panneau allait de 60 à 1 365 au lieu de 20 à 1 405.

   ⚠️ **« vw » compte la barre de défilement, la largeur lisible ne la compte
   pas.** La moitié de la barre est donc rendue de chaque côté, par la même
   variable que la lueur de l'éligibilité. Le repli est zéro. */
.s-signal {
  margin-inline: calc(50% - 50vw + var(--barre-defilement, 0px) / 2 + var(--spacing) * 20);
  background: var(--fond);
  color: var(--texte);
  border-radius: var(--r-3xl);
  padding: calc(var(--rythme-section) * .72) calc(var(--spacing) * 56);
  transition:
    background-color var(--motion-slow) var(--ease-inout),
    color var(--motion-slow) var(--ease-inout);
}

.s-signal .chapo,
.s-signal .incise,
.s-signal .legende,
.s-signal .gris { color: var(--texte-secondaire); }

.s-signal .fort { color: var(--texte); }

/* Le gradient de titre est redéfini par le thème : magenta vers orange. */
.s-signal .titre-gradient { background: var(--gradient-signal); -webkit-background-clip: text; background-clip: text; color: transparent; }

.s-signal .lien { color: var(--texte); }

.signal-tete {
  display: flex;
  align-items: center;
  gap: calc(var(--spacing) * 14);
}

.signal-mark {
  width: calc(var(--spacing) * 32);
  height: auto;
  flex: none;
}

.signal-mot {
  font-family: var(--font-titre);
  font-weight: var(--graisse-titre);
  font-size: var(--cta-lg);
  letter-spacing: -.03em;
  color: var(--texte);
}

/* ⛔ **Ces deux règles n'ont plus de balisage depuis le 16 septembre 2026.** La
   grille à deux colonnes et la liste de six lignes ont été remplacées par les
   cinq vues à onglets. Elles restent le temps de voir si les onglets tiennent ;
   **si elles ne reviennent pas, elles sortent avant la mise en ligne.** */
.signal-grille {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr);
  gap: calc(var(--spacing) * 56);
  align-items: start;
}

.signal-liste {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: calc(var(--spacing) * 1);
  background: var(--bordure);
  border: 1px solid var(--bordure);
  border-radius: var(--r-lg);
  overflow: hidden;
}

.signal-liste li {
  background: var(--surface);
  padding: calc(var(--spacing) * 18) calc(var(--spacing) * 20);
  font-size: var(--corps-sm);
  line-height: 1.4;
  color: var(--texte);
}

/* Le cadre au bon rapport, tant que l'image n'existe pas. Il porte un texte
   qui dit ce qu'elle montrera. Jamais un « à venir », jamais une maquette
   inventée, jamais une image générique.

   ⚠️ Il s'appelait .signal-cadre et ne servait qu'aux captures de Signal.
   Depuis le 15 septembre 2026 il sert aussi aux illustrations du constat :
   c'est le même objet, il n'y en a donc qu'un, et son nom ne dit plus une
   section. **Ses couleurs sont écrites en repli** : les jetons de surface
   n'existent que sous le thème Signal, et sans repli le cadre serait invisible
   sur le reste de la page.

   Le rapport est une variable : 16/10 pour une capture d'écran, 4/3 pour une
   illustration, et c'est le seul réglage qui change d'un endroit à l'autre. */
.cadre-visuel {
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: calc(var(--spacing) * 10);
  aspect-ratio: var(--cadre-rapport, 16 / 10);
  max-width: 100%;
  padding: calc(var(--spacing) * 28);
  background: var(--surface, var(--gris-clair));
  border: 1px dashed var(--bordure, var(--gris-grille));
  border-radius: var(--r-xl);
}

.cadre-visuel p { max-width: none; color: var(--texte-secondaire, var(--gris-lisible)); font-size: var(--corps-sm); line-height: 1.5; }
.cadre-visuel .cadre-visuel-titre { color: var(--texte, var(--noir)); font-weight: 700; }

/* --------------------------------------------------------------------------
   09 bis. Les cinq vues de Signal, en onglets

   ⚠️ **Depuis le 16 septembre 2026, sur demande, d'après une référence
   fournie.** C'était une liste de six lignes à côté d'un cadre de capture
   unique. Une vue à la fois, cinq onglets, et pour chacune ce qu'elle montre et
   la capture qui l'accompagnera.

   **La barre d'onglets est celle des quatre leviers**, à l'identique : elle a
   perdu le mot « pilier » de ses classes le même jour, précisément pour servir
   ici aussi. Ce qui change, c'est le thème sous elle.

   ⚠️ **Elle est le seul endroit du site où ces classes changent de couleur**, et
   c'est parce que Signal est le seul moment sombre. Le verre de noir de la
   barre deviendrait invisible : il passe en verre de blanc, et l'onglet tenu
   prend la surface haute du thème au lieu du papier.

   ⚠️ **Le violet de l'onglet tenu ne descend pas ici.** Le thème Signal a son
   accent, le rose, et une page qui montre deux accents dans la même section
   n'en a plus aucun. L'icône tenue prend donc l'accent du thème, où elle tient
   **4,94 pour 1** sur la surface haute, et le texte secondaire en tient 6,19 sur
   la surface de la vue.
   -------------------------------------------------------------------------- */
.signal-vues {
  display: flex;
  flex-direction: column;
  gap: calc(var(--spacing) * 40);
}

.signal-vues .onglets {
  align-self: center;
  max-inline-size: 100%;
  background: rgb(255 255 255 / .05);
  border-color: rgb(255 255 255 / .10);
}

.signal-vues .onglet { color: var(--texte-secondaire); }

.signal-vues .onglet[data-actif] {
  background: var(--surface-haute);
  color: var(--texte);
  box-shadow: inset 0 1px 0 0 rgb(255 255 255 / .08);
}

.signal-vues .onglet-icone { color: var(--texte-secondaire); }
.signal-vues .onglet[data-actif] .onglet-icone { color: var(--accent); }
.signal-vues .onglet:hover { background: rgb(255 255 255 / .06); }

/* La scène. Les cinq vues se superposent sur la même cellule de grille :
   **la scène prend la hauteur de la plus haute des cinq**, et elle ne saute pas
   d'un onglet à l'autre. */
.signal-scene {
  display: grid;
  list-style: none;
  margin: 0;
  padding: 0;
}

.signal-vue {
  grid-area: 1 / 1;
  display: grid;
  grid-template-columns: minmax(0, 4fr) minmax(0, 7fr);
  gap: calc(var(--spacing) * 48);
  align-items: center;
  padding: calc(var(--spacing) * 40);
  border-radius: var(--r-xl);
  background: var(--surface);
  opacity: 0;
  /* La visibilité sort la vue au repos de l'ordre de tabulation et de la voix.
     Elle bascule à la fin du fondu en sortie, et tout de suite en entrée, sinon
     la vue qui arrive reste muette le temps de la transition. */
  visibility: hidden;
  transition:
    opacity var(--motion-quick) var(--ease-out),
    visibility 0s linear var(--motion-quick);
}

.signal-vue[data-actif] {
  opacity: 1;
  visibility: visible;
  transition-delay: 0s;
}

.signal-texte {
  display: flex;
  flex-direction: column;
  align-items: start;
  gap: calc(var(--spacing) * 28);
}

.signal-dit {
  font-size: var(--corps);
  line-height: var(--lh-corps);
  color: var(--texte-secondaire);
  max-width: none;
}

/* ⚠️ **Le cadre de remplacement ne peut pas garder la surface du thème ici.**
   Il lit « --surface », et la vue qui le porte lit la même : posé dessus, il
   disparaît. C'est la même panne que la carte sombre sur le panneau du constat,
   pour la quatrième fois. Il prend donc la surface haute, et son liseré monte à
   14 % de blanc : **un cadre de remplacement doit se voir**, c'est toute sa
   fonction tant que la capture n'existe pas. */
.signal-capture {
  --cadre-rapport: 16 / 10;
  background: var(--surface-haute);
  border-color: rgb(255 255 255 / .14);
}

/* ⚠️ **Les six captures réelles sont posées le 18 septembre 2026, sur demande**,
   et le cadre de remplacement ci-dessus n'a plus de balisage dans Signal. Il
   reste écrit : il sert encore ailleurs sur la page, et il reviendrait ici si
   une septième vue s'ajoutait avant sa capture.

   **Les six sont recadrées au même rapport, 2 pour 1**, coin haut gauche gardé,
   et c'est ce rapport que le cadre porte. Six rapports différents donneraient
   six hauteurs, et la scène sauterait d'un onglet à l'autre : la loi du poids
   égal des six vues se joue là, comme sur les quatre leviers.

   Le liseré et l'ombre ne sont pas repris : la capture est claire sur un fond
   noir, elle se détache d'elle-même, et une ombre noire sur du noir ne dit
   rien, ce que le bouton de cette même section a déjà appris. */
/* ⚠️ **Cinq des six captures sont remplacées le 18 septembre 2026, sur
   demande, par des vues mises en scène** : l'application entière, barre
   latérale comprise, dans un cadre sombre à coins arrondis, posé sur le
   dégradé du thème. Parcours, trafic, veille, contenus et leads. **Le cadre
   est dans l'image**, avec ses coins transparents : la boîte ne pose donc ni
   fond, ni rayon, ni recadrage, sinon elle doublerait le cadre.

   **Les cinq ont le même rapport, 2 000 sur 1 374**, et c'est lui que la boîte
   porte : la loi du poids égal tient toujours d'un onglet à l'autre.

   Le projet a reçu la sienne le 21 septembre 2026, sur demande : les six
   vues sont mises en scène. `.signal-visuel-capture` n'a plus de balisage et
   sort au prochain nettoyage. */
.signal-visuel {
  aspect-ratio: 2000 / 1374;
  min-width: 0;
}

.signal-visuel img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.signal-visuel-capture {
  overflow: hidden;
  border-radius: var(--r-xl);
  background: var(--surface-haute);
}

.signal-visuel-capture img {
  object-fit: cover;
  object-position: left top;
}

/* ⚠️ **L'appel à l'action de Signal est le bouton de la barre de navigation, et
   il est dans la carte, sur demande du 16 septembre 2026.** Il a été une pilule
   claire à pastille, puis le bouton primaire, avant celui-ci : **c'est la
   troisième version qu'il faut reporter dans le vault**, pas les deux premières.

   **Ce que ça règle** : la page portait trois boutons bleus, hero, éligibilité
   et Signal. Elle en porte deux, et l'arbitrage ouvert sur « le bleu signe
   l'appel à l'action » cesse de s'aggraver ici.

   ⚠️ **Le verre du bouton est du noir, et il disparaîtrait sur ce fond.** Les
   deux coques valent 4 % de noir sur un fond déjà noir : on ne verrait ni
   l'enveloppe, ni le noyau, ni le liseré. **Elles passent au blanc**, aux mêmes
   valeurs, et l'encre passe à celle du thème. Les deux rayons restent
   concentriques, le chevron qui se relaie ne bouge pas, et la double ombre
   portée sort : une ombre noire sur du noir ne dit rien.

   Le libellé tient **15,96 pour 1** sur le noyau composé.

   **La référence en met un par panneau, et c'est repris.** Un seul est visible
   et exposé à la voix à la fois, puisque les autres vues sont en
   « visibility: hidden » : ce sont six copies du même lien, pas six appels.

   ⚠️ **Le libellé dit « Voir mon éligibilité » depuis le 16 septembre 2026,
   sur demande**, et non plus « Réserver un appel » : il n'y a plus de
   réservation, le lien mène au formulaire de préqualification. C'est le
   libellé de la barre de navigation, mot pour mot, pour ne pas en ajouter un
   troisième vers la même cible. */
.signal-action { align-self: start; }

.s-signal .bouton.bouton-contour,
.entete[data-theme="signal"] .bouton.bouton-contour {
  background-color: rgb(255 255 255 / .05);
  border-color: rgb(255 255 255 / .10);
  color: var(--texte);
  box-shadow: none;
}

.s-signal .bouton-contour .bouton-noyau,
.entete[data-theme="signal"] .bouton-contour .bouton-noyau {
  background-color: rgb(255 255 255 / .05);
  box-shadow:
    inset 0 1px 0 0 rgb(255 255 255 / .10),
    inset 0 0 calc(var(--spacing) * 20) 0 rgb(255 255 255 / .06);
}

.s-signal .bouton-contour:hover  .bouton-noyau,
.entete[data-theme="signal"] .bouton-contour:hover  .bouton-noyau { background-color: rgb(255 255 255 / .10); }
.s-signal .bouton-contour:active .bouton-noyau,
.entete[data-theme="signal"] .bouton-contour:active .bouton-noyau { background-color: rgb(255 255 255 / .14); }

/* --------------------------------------------------------------------------
   19. Pied de page
   -------------------------------------------------------------------------- */

/* Refait le 16 septembre 2026 d'après une référence fournie, sur demande.

   Trois plans, de l'arrière vers l'avant :

   1. **Une feuille blanche**, pleine largeur et à angles droits, qui descend
      jusqu'au bas de l'écran. Elle a été arrondie en haut et arrêtée à vingt
      pixels des bords une heure le 16 septembre 2026, puis élargie sur demande.
   2. **Une carte**, posée dans la feuille, liseré très léger et ombre large.
      La marque et sa phrase à gauche, les colonnes de liens groupées à droite,
      un filet, puis la mention de propriété et les liens légaux.
   3. **Le mot de marque en filigrane**, sous la carte, coupé par le bas de
      page et effacé vers le bas.

   ⚠️ **Ce qui n'est pas repris de la référence.** Sa phrase de marque porte
   un tiret cadratin, que la section 2 interdit ; la nôtre n'en a pas. Et elle
   a trois colonnes : la page n'a de copy arrêté que pour deux, Navigation et
   Contact, la colonne Légal étant passée dans la ligne du bas comme dans la
   référence. **Une troisième colonne s'écrirait**, et ça se demande.

   ⚠️ **Ce qui sort de l'ancien pied de page.** Le bouton primaire de la
   colonne de marque, que la référence n'a pas, et la ligne du bas en trois
   temps : la phrase d'exclusivité, déjà dite dans la phrase de marque, et le
   lien « On regarde vos chiffres ensemble ? ». Le rendez-vous reste dans la
   colonne Contact. */
.pied {
  margin-block-start: calc(var(--spacing) * 12);
}

.pied-feuille {
  position: relative;
  overflow: hidden;
  padding-block: calc(var(--spacing) * 56) 0;
  background: linear-gradient(180deg, var(--blanc) 0%, var(--blanc) 55%, var(--fond-page) 100%);
  box-shadow: 0 calc(var(--spacing) * -1) 0 rgb(28 28 28 / .03);
}

/* Le dégradé de Percer monte du bas de la feuille, sur demande du
   16 septembre 2026. Son masque plafonne à 42 % au bas de la feuille : il
   était à 85 %, jugé trop intense. C'est --gradient-site tel quel, violet vers bleu, et la
   section 5.1 le réserve aux visuels et aux fonds : c'en est un.

   Il s'éteint vers le haut par un masque, et il passe derrière la moitié basse
   de la carte. La carte est blanche et opaque, donc aucun texte ne se pose sur
   la couleur : le contraste de la carte ne bouge pas. Le seul texte posé
   dessus est le filigrane, décoratif et retiré de l'arbre. */
.pied-feuille::after {
  content: "";
  position: absolute;
  inset-inline: 0;
  inset-block-end: 0;
  z-index: 0;
  block-size: 70%;
  background: var(--gradient-site);
  -webkit-mask-image: linear-gradient(0deg, rgb(0 0 0 / .42) 0%, rgb(0 0 0 / .16) 40%, transparent 100%);
  mask-image: linear-gradient(0deg, rgb(0 0 0 / .42) 0%, rgb(0 0 0 / .16) 40%, transparent 100%);
  pointer-events: none;
}

.pied-feuille > .conteneur {
  position: relative;
  z-index: 1;
  max-width: calc(var(--spacing) * 1120);
}

/* La carte est en verre depuis le 16 septembre 2026, sur demande : un blanc à
   75 % et le flou de la classe verre, 25 px, pour laisser monter le dégradé
   du pied de page derrière elle.

   ⚠️ **L'opacité est bornée par le contraste.** Le gris lisible se lit sur
   cette carte, et le dégradé passe sous sa moitié basse : à 75 % de blanc, le
   fond composé reste au-dessus de 0,9 de luminance et le gris y tient environ
   4,6 pour 1, pour un minimum de 4,5. **Descendre l'opacité ou monter le
   dégradé demande de remesurer.**

   La carte est aussi plus basse, sur demande : garniture de 28 au lieu de 40,
   et les écarts internes resserrés d'autant. */
.pied-carte {
  background: rgb(255 255 255 / .75);
  -webkit-backdrop-filter: blur(25px);
  backdrop-filter: blur(25px);
  border: 1px solid rgb(28 28 28 / .06);
  border-radius: var(--r-xl);
  padding: calc(var(--spacing) * 28) calc(var(--spacing) * 40);
  box-shadow: var(--ombre-carte), 0 calc(var(--spacing) * 32) calc(var(--spacing) * 64) calc(var(--spacing) * -24) rgb(28 28 28 / .08);
}

.pied-grille {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: calc(var(--spacing) * 40);
}

.pied-colonne-marque {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: calc(var(--spacing) * 12);
  flex: 0 1 calc(var(--spacing) * 340);
}

.pied .marque { font-size: var(--corps-lg); gap: calc(var(--spacing) * 8); }
.pied .chevron { width: calc(var(--spacing) * 22); }

.pied-baseline {
  margin: 0;
  font-size: var(--legende);
  line-height: 1.45;
  color: var(--gris-lisible);
}

.pied-colonnes {
  display: flex;
  gap: calc(var(--spacing) * 64);
}

.pied-colonne {
  display: flex;
  flex-direction: column;
  gap: calc(var(--spacing) * 10);
}

.pied-titre {
  margin: 0;
  font-size: var(--legende);
  font-weight: 500;
  color: var(--noir);
}

.pied-liste,
.pied-legal {
  display: flex;
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: var(--legende);
  color: var(--gris-lisible);
}

.pied-liste { flex-direction: column; gap: calc(var(--spacing) * 6); }

.pied-liste a,
.pied-legal a,
.pied-bouton-lien {
  color: var(--gris-lisible);
  text-decoration: none;
  transition: color var(--motion-instant) var(--ease-out);
}

.pied-liste a:hover,
.pied-legal a:hover,
.pied-bouton-lien:hover { color: var(--noir); }

.pied-bouton-lien {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  text-align: start;
  cursor: pointer;
}

/* Les liens légaux sont soulignés, comme dans la référence : ce sont les
   seuls liens de la carte qu'on vient chercher plutôt que suivre. */
.pied-legal { flex-wrap: wrap; gap: calc(var(--spacing) * 8) calc(var(--spacing) * 16); }

.pied-legal a,
.pied-legal .pied-bouton-lien {
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-color: rgb(111 111 111 / .45);
}

.pied-legal a:hover,
.pied-legal .pied-bouton-lien:hover { text-decoration-color: currentColor; }

.pied-bas {
  display: flex;
  flex-wrap: wrap;
  gap: calc(var(--spacing) * 12) calc(var(--spacing) * 24);
  align-items: baseline;
  justify-content: space-between;
  margin-block-start: calc(var(--spacing) * 24);
  padding-block-start: calc(var(--spacing) * 16);
  border-block-start: 1px solid var(--gris-grille);
  font-size: var(--legende);
  color: var(--gris-lisible);
}

/* La mention de propriété et l'origine vont ensemble, à gauche, séparées par
   un point médian ; les liens légaux restent seuls à droite, comme dans la
   référence. */
.pied-mentions {
  display: flex;
  flex-wrap: wrap;
  gap: calc(var(--spacing) * 4) calc(var(--spacing) * 10);
  margin: 0;
  max-width: none;
}

/* Le cœur remplace le mot, sur demande. C'est un tracé et non un emoji : un
   emoji est dessiné par la police du poste et sa couleur ne se commande pas.
   Il porte le dégradé de site, qui sert aux visuels. Le tracé sort de l'arbre
   et le mot « cœur » reste dans le texte, masqué à l'écran : un nom posé sur
   l'image ne remontait pas dans la phrase, qui se lisait « Fait avec à Toulouse ». */
.pied-coeur {
  display: inline-block;
  inline-size: 1em;
  block-size: 1em;
  vertical-align: -.14em;
}

/* Le mot reste dans le texte pour la voix, et sort de l'écran. */
.pied-coeur-mot {
  position: absolute;
  inline-size: 1px;
  block-size: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.pied-origine::before {
  content: "\00B7";
  margin-inline-end: calc(var(--spacing) * 10);
}

/* Le mot est coupé par le bas de page et s'efface vers le bas. L'effacement
   est un masque et non un dégradé de texte : la section 2 réserve le dégradé
   aux titres, et ce mot n'en est pas un, il est retiré de l'arbre.

   Il tient par la masse et non par le contraste : gris de grille sur blanc, il
   se voit sans jamais concurrencer le texte de la carte. Sa hauteur visible
   est fixée par la boîte, qui coupe le bas des lettres. */
.pied-filigrane {
  position: relative;
  z-index: 1;
  block-size: .66em;
  margin-block-start: calc(var(--spacing) * 24);
  overflow: hidden;
  text-align: center;
  font-family: var(--font-titre);
  font-weight: var(--graisse-titre);
  font-size: clamp(6rem, 22vw, 20rem);
  line-height: .9;
  letter-spacing: -.05em;
  /* Blanc et non plus gris de grille : il se pose sur le dégradé, où un gris
     se lirait comme une salissure. Il passe devant le dégradé, d'où le z-index. */
  color: rgb(255 255 255 / .6);
  -webkit-mask-image: linear-gradient(180deg, #000 30%, transparent 100%);
  mask-image: linear-gradient(180deg, #000 30%, transparent 100%);
  user-select: none;
  pointer-events: none;
}

/* --------------------------------------------------------------------------
   20. Le consentement
   Trois règles : refuser demande exactement le même nombre de clics
   qu'accepter, aucun enregistrement avant le clic, un moyen permanent de
   revenir sur son choix en pied de page.
   -------------------------------------------------------------------------- */

.consentement {
  position: fixed;
  inset-block-end: calc(var(--spacing) * 16);
  left: calc(var(--spacing) * 16);
  right: calc(var(--spacing) * 16);
  width: auto;
  z-index: 60;
  margin-inline: auto;
  max-width: calc(var(--spacing) * 720);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: calc(var(--spacing) * 20);
  padding: calc(var(--spacing) * 20) calc(var(--spacing) * 24);
  background: var(--blanc);
  border: 1px solid var(--gris-grille);
  border-radius: var(--r-xl);
  box-shadow: 0 calc(var(--spacing) * 12) calc(var(--spacing) * 40) rgb(28 28 28 / .12);
}

.consentement[hidden] { display: none; }

.consentement-texte {
  flex: 1 1 calc(var(--spacing) * 320);
  font-size: var(--corps-sm);
  line-height: 1.5;
  color: var(--gris-lisible);
  max-width: none;
}

/* Les deux boutons ont la même taille et le même nombre de clics.
   Refuser n'est ni plus petit, ni plus gris, ni caché dans un réglage. */
.consentement-actions {
  display: flex;
  gap: calc(var(--spacing) * 10);
}

.consentement-actions .bouton { flex: 1 1 auto; white-space: nowrap; }

/* --------------------------------------------------------------------------
   21. Le drapeau de publication
   Cinq points bloquent la mise en ligne, pas la construction. Ils sont
   marqués ici et se masquent d'un seul endroit : passer
   data-publication à « en-ligne » sur <html> les retire tous.
   -------------------------------------------------------------------------- */

[data-publication="en-ligne"] [data-bloque] { display: none !important; }

/* Le marquage visuel du brouillon a été retiré le 14 septembre 2026 : le
   trait pointillé et sa mention coupaient la page en deux à chaque élément
   bloqué, et le hero en portait un. L'avertissement n'est pas perdu pour
   autant, il a changé de support : site.js liste les éléments bloqués en
   console tant que data-publication vaut « brouillon ». Sans ce relais, on
   basculerait le drapeau sans savoir ce qui disparaît. */

[data-publication="en-ligne"] .brouillon-seul { display: none !important; }

/* Les liens dont la cible attend l'arborescence. En brouillon ils se
   signalent, en ligne ils doivent avoir été remplacés. */
[data-publication="brouillon"] [data-lien-a-creer]::after {
  content: " (page à créer)";
  color: var(--gris-lisible);
}

/* Un texte non remplacé doit se voir immédiatement. Jamais de faux latin :
   l'encadré dit le rôle de la phrase attendue. */
.a-ecrire {
  display: block;
  padding: calc(var(--spacing) * 16) calc(var(--spacing) * 20);
  border: 1px dashed var(--gris-texte);
  border-radius: var(--r-sm);
  font-size: var(--corps-sm);
  line-height: 1.5;
  color: var(--gris-lisible);
  max-width: var(--mesure-corps);
}

.a-ecrire strong { color: var(--noir); font-weight: 700; }

/* --------------------------------------------------------------------------
   22. Points de rupture : 480, 768, 1024, 1280, 1536.
   Sur mobile, tout passe en une colonne, sans exception. Une rangée de six
   chiffres devient six lignes, pas trois rangées de deux : l'égalité de poids
   visuel entre les chiffres est un argument de la page.
   -------------------------------------------------------------------------- */

/* --------------------------------------------------------------------------
   L'entonnoir passe sous le texte dès 1024 px, et non 768.

   Le retrait qu'il réserve vaut sa boîte plus sa marge. Entre 768 et 1024,
   la colonne de texte tombait trop court pour un H1 de cette taille et les
   mots se coupaient en plein milieu. Ce n'est pas un réglage à affiner,
   c'est une largeur où la composition à deux colonnes ne tient pas.

   Il reste : c'est la seule image du hero. Il revient dans le flux, sous le
   texte, centré, à une taille qui lui laisse ses trois temps.
   -------------------------------------------------------------------------- */
/* Sous 1500 px, la boîte de l entonnoir se resserre pour que les boutons et
   les trois preuves tiennent sur une seule ligne. Les boutons font 420 px et
   le bloc des preuves 220 : il faut 660 px de colonne, et à 46 vw la boîte
   n en laissait autant qu au-delà de 1480 px. À 36 vw, la ligne tient dès
   1060 px. Le cône y perd de la largeur, et c est le prix assumé : sur un
   portable, la colonne de texte passe avant le décor. */
@media (max-width: 1499px) {
  .s-hero { --largeur-entonnoir: clamp(calc(var(--spacing) * 340), 36vw, calc(var(--spacing) * 560)); }
}

/* Le dernier palier avant que l entonnoir ne passe sous le texte. Dix-neuf
   pixels manquaient à 1024 pour que la ligne tienne : la boîte les rend. */
@media (max-width: 1100px) {
  .s-hero { --largeur-entonnoir: clamp(calc(var(--spacing) * 300), 31vw, calc(var(--spacing) * 380)); }
}

@media (max-width: 1599px) { .hero-entonnoir { --facteur-angle: .74; } }
@media (max-width: 1279px) { .hero-entonnoir { --facteur-angle: .58; } }

@media (max-width: 1023px) {
  .hero-entonnoir {
    /* Sous le texte et pleine largeur, il n y a plus de vide à occuper sur un
       côté : le décalage n a plus d objet et il rendrait le cône de travers. */
    --decalage-entonnoir: 0px;
    /* Pleine largeur et centré : il n y a plus de vide à gauche à occuper, et
       un rab y reprendrait de la largeur au cône au lieu de lui en donner. */
    --rab-entonnoir: 0px;
    position: relative;
    inset: auto;
    order: 1;
    align-self: center;
    width: 100%;
    height: calc(var(--spacing) * 460);
    margin-block-start: var(--rythme-bloc);
  }

  .hero-grille { padding-inline-end: 0; }

}

@media (max-width: 1279px) {

  /* Le duo texte plus mark ne tient qu'au-delà de 1280 px. En dessous, le
     mark prend 200 px sur une colonne qui en a besoin et le H1 part à six
     lignes. Le mark passe donc sous le texte, qui reprend toute la largeur. */
  .hero-grille { grid-template-columns: minmax(0, 1fr); gap: calc(var(--spacing) * 40); }
}

@media (max-width: 1023px) {
  .signal-vue {
    grid-template-columns: minmax(0, 1fr);
    gap: calc(var(--spacing) * 28);
    padding: calc(var(--spacing) * 28);
  }
  .card-grid, .card-grid[data-colonnes] { --cartes-colonnes: 2; }
  .stat-row, .stat-row[data-colonnes] { --stat-colonnes: 2; }
  .engagements { grid-template-columns: minmax(0, 1fr); }
  /* Le cas client passe en une colonne : la capture au-dessus, les quatre
     chiffres dessous. */
  .cas-client { grid-template-columns: minmax(0, 1fr); gap: calc(var(--spacing) * 16); }
  .cas-visuel, .cas-chiffres { grid-column: 1; grid-row: auto; }
  .cas-visuel-infos { padding-inline: calc(var(--spacing) * 16) calc(var(--spacing) * 80); }
  .cas-chiffres { margin-block-start: calc(var(--spacing) * 16); }
  /* En colonne, rien ne fixe la hauteur : la capture reprend son rapport. */
  .cas-visuel img { height: auto; }
  .cond-grille,
  .verdicts { grid-template-columns: minmax(0, 1fr); }
  .equipe-piste { grid-auto-columns: calc((100% - var(--spacing) * 16 * 2) / 2.4); }

  /* ⚠️ **La frise passe en une colonne sous 1024 px.** La géométrie du Gantt ne
     survit pas à une colonne de deux cent cinquante pixels : les quatre barres
     deviennent quatre pilules pleine largeur au-dessus des quatre temps
     empilés, et le clic continue de mettre les mois en avant. Les traits
     verticaux sortent avec la grille, ils n'ont plus de colonnes à border. */
  .frise {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: none;
    row-gap: calc(var(--spacing) * 12);
    background-image: none;
    box-shadow: none;
  }
  .frise-barre,
  .frise-temp { grid-row: auto; grid-column: 1; }
  .frise-barre { margin-inline: 0; }
  .frise-temp {
    margin-block-start: 0;
    padding: calc(var(--spacing) * 24) calc(var(--spacing) * 20);
  }
  /* Empilés, les quatre temps ont besoin de plus d'air entre eux que les
     quatre pilules, et le groupe des pilules doit se détacher du premier
     temps : sans ça, la frise se lit comme une seule liste de dix blocs. */
  .frise-barre + .frise-temp { margin-block-start: calc(var(--spacing) * 20); }
  .frise-temp + .frise-temp { margin-block-start: calc(var(--spacing) * 12); }
  /* Le visuel repasse au-dessus du texte. **L'épinglage ne s'arme pas sous
     cette largeur**, c'est le script qui le refuse : quatre écrans de
     défillement retenu sur un téléphone se lisent comme une page bloquée. */
  .pilier { grid-template-columns: minmax(0, 1fr); gap: calc(var(--spacing) * 32); }
  /* Une colonne. Le filet reste : il tire l'œil de l'icône vers le bord, et
     c'est encore vrai empilé, où il sépare deux constats au lieu d'aligner
     trois colonnes. */
  .constats { grid-template-columns: minmax(0, 1fr); gap: calc(var(--spacing) * 44); }
  /* Une colonne : l illustration passe sous les trois atouts. Elle est
     décorative tant qu elle n existe pas, elle ne doit donc pas ouvrir la
     section sur un téléphone. */
  .opp-grille { grid-template-columns: minmax(0, 1fr); gap: calc(var(--spacing) * 40); }
  /* La FAQ passe en une colonne : la barre revient au-dessus des questions,
  en ligne, et elle ne reste plus collée, elle couvrirait la moitié d un écran
  étroit. Le complément de chaque levier sort, faute de place. */
  .faq-vues { grid-template-columns: minmax(0, 1fr); gap: calc(var(--spacing) * 28); }
  .onglets.onglets-vertical { position: static; flex-direction: row; flex-wrap: wrap; justify-content: center; align-self: center; }
  .onglets-vertical .onglet { padding: calc(var(--spacing) * 10) calc(var(--spacing) * 16); border-radius: var(--r-full); }
  .onglets-vertical .onglet-sous { display: none; }
  .faq-charly { grid-template-columns: minmax(0, 1fr); gap: calc(var(--spacing) * 28); padding: calc(var(--spacing) * 32); }
  .s-signal { padding-inline: calc(var(--spacing) * 32); }
}

@media (max-width: 1023px) {
  .pied-carte { padding: calc(var(--spacing) * 32); }

  /* Le trio passe en une colonne dès 1024 et non à 768 : à trois colonnes dans
     cette bande, « Un directeur marketing » tient sur trois lignes et le prix
     se casse au milieu. L'ordre du balisage est gardé, donc Percer reste au
     milieu : c'est la place qu'il a dans la comparaison, et une carte déplacée
     en tête par le style se lirait dans un autre ordre que celui qui est lu à
     voix haute. */
  .pc-trio {
    grid-template-columns: minmax(0, 1fr);
    align-items: stretch;
  }
}

/* Sous 1024 px, la barre est déjà au plus juste : la resserrer lui prendrait
   la place de la marque. Et le filet n'a plus rien à séparer dès que les liens
   passent dans le menu déplié. */
@media (max-width: 1023px) {
  .entete { --contraction-entete: 0px; }
}

@media (max-width: 899px) {
  .entete-filet { display: none; }

  /* Le menu se déplie sous la pilule, dans une pilule à coins adoucis :
     la barre garde sa forme, elle ne redevient pas un bandeau. */
  .entete-barre { position: relative; }
  .entete-nav {
    display: none;
    position: absolute;
    inset-inline: 0;
    transform: none;
    inset-block-start: calc(100% + var(--spacing) * 8);
    flex-direction: column;
    align-items: stretch;
    gap: calc(var(--spacing) * 4);
    margin-inline: 0;
    padding: calc(var(--spacing) * 16) calc(var(--spacing) * 24);
    background: var(--blanc);
    border: 1px solid var(--gris-grille);
    border-radius: var(--r-xl);
    box-shadow: 0 calc(var(--spacing) * 8) calc(var(--spacing) * 32) rgb(28 28 28 / .1);
  }
  .entete[data-theme="signal"] .entete-nav { background: var(--surface); border-color: var(--bordure); }
  .entete-nav[data-ouvert="oui"] { display: flex; }
  .entete-nav .lien-nav { padding-block: calc(var(--spacing) * 10); font-size: var(--corps-sm); }
  .entete-menu { display: inline-flex; margin-inline-start: auto; }
  .entete-cta { margin-inline-start: 0; }
}

@media (max-width: 599px) {
  /* Sous 600 px, la pilule ne tient plus la marque, le menu et le bouton.
     Le bouton sort de la barre et réapparaît dans le menu déplié : c'est
     toujours le même appel à l'action, au même endroit du parcours. */
  .entete-cta { display: none; }
  .entete-nav-cta { display: inline-flex; margin-block-start: calc(var(--spacing) * 12); }
}

@media (max-width: 767px) {
  .equipe-piste { grid-auto-columns: 78%; }
  /* Sur téléphone, les flèches passent sous le texte : en ligne, elles lui
     laissaient deux cents pixels et le titre se cassait tous les deux mots. */
  .equipe-tete { flex-direction: column; align-items: stretch; }
  .equipe-fleches { align-self: end; }

  /* L'échelle display n'a pas de plancher assez bas pour un téléphone : à
     375 px, --display-3xl vaut encore 77 px, et un mot comme
     « d'acquisition » déborde de l'écran. Le rapport entre les niveaux est
     conservé, seul le plancher descend.
     Correction proposée : poser un plancher mobile dans l'échelle display. */
  h1 { font-size: var(--h1); }
  /* ⚠️ Le plancher mobile est posé sur `h1`, qui ne pèse plus assez face au
     `.s-hero h1` du hero depuis qu'il porte une taille. Sans cette reprise,
     le titre du hero resterait à 61 px sur un téléphone. */
  .s-hero h1 { font-size: var(--h1); }
  h2 { font-size: var(--h2); }
  .stat-valeur,
  .proof-mot,
  .stat-row[data-echelle="compact"] .stat-valeur { font-size: var(--h1); }
  /* Le prix du trio suit le cran du H2 et non l'échelle des chiffres seuls :
     il est au niveau `titre-lg`, il descend avec lui. Sans cette reprise, il
     passerait au-dessus du H2 de la section sur un téléphone. */
  .titre-lg,
  .carte-offre-montant { font-size: var(--h3); }

  /* Une colonne partout, sans exception. */

  /* Sur téléphone, 8vw vaut 30 px contre les 24 de la marge du système : le
     hero se décalait de six pixels par rapport aux sections. À cette
     largeur, la gouttière proportionnelle n'apporte rien, le conteneur de
     bord reprend donc la marge commune. */
  .conteneur-bord { padding-inline: var(--marge-laterale); }

  .hero-grille { grid-template-columns: minmax(0, 1fr); gap: calc(var(--spacing) * 32); }
  /* Une colonne : les filets passent tous à l'horizontale, et le dernier sort. */
  .moteur { grid-template-columns: minmax(0, 1fr); }
  .moteur-part { border-inline-end: 0; border-block-end: 1px solid var(--gris-grille); }
  .moteur-part:last-child { border-block-end: 0; }
  .card-grid, .card-grid[data-colonnes] { --cartes-colonnes: 1; }
  .stat-row, .stat-row[data-colonnes] { --stat-colonnes: 1; }
  /* Les preuves passent l une sous l autre à cette largeur. Les filets, qui
     séparent des voisines sur une ligne, deviendraient des traits posés
     devant chacune sauf la première. Ils sortent tous les deux. */
  .preuve-ligne { justify-content: center; }

  .duo { grid-template-columns: minmax(0, 1fr); }
  .duo-part { padding: calc(var(--spacing) * 28); }
  .duo-separe .duo-part { padding: calc(var(--spacing) * 32); }
  .signal-liste { grid-template-columns: minmax(0, 1fr); }
  /* Sur téléphone, tout passe en une colonne, les colonnes de liens comprises. */
  .pied-grille,
  .pied-colonnes { flex-direction: column; gap: calc(var(--spacing) * 28); }
  .pied-colonne-marque { flex-basis: auto; }
  .pied-carte { padding: calc(var(--spacing) * 24); }
  .pied-feuille { padding-block-start: calc(var(--spacing) * 32); }
  .pied-bas,
  .pied-legal { flex-direction: column; align-items: flex-start; gap: calc(var(--spacing) * 12); }
  .s-signal,
  .s-rdv { border-radius: var(--r-xl); padding-inline: calc(var(--spacing) * 24); }
  .proof-box { padding: calc(var(--spacing) * 28); }

  /* En colonne, flex-basis pilote la hauteur et non la largeur : le texte
     doit donc revenir à sa base automatique, sinon la bannière prend
     320 px de haut pour trois lignes. */
  .consentement { flex-direction: column; align-items: stretch; }
  .consentement-texte { flex: 0 0 auto; }
}

@media (max-width: 479px) {
  h1 { font-size: var(--h1); }
  h2 { font-size: var(--h2); }
  .stat-valeur,
  .proof-mot,
  .stat-row[data-echelle="compact"] .stat-valeur { font-size: var(--h1); }

  /* Le H2 de la section descend à 32 unités à cette largeur : le prix passe
     sous lui pour que la hiérarchie tienne sur un téléphone. */
  .carte-offre-montant { font-size: var(--h3); }

  .boutons .bouton { flex: 1 1 100%; }
  .consentement-actions { flex-direction: column; }
}

/* --------------------------------------------------------------------------
   23. Impression : la page se lit sur papier, en une colonne, sans la
   scénographie sombre.
   -------------------------------------------------------------------------- */

@media print {
  .entete, .consentement, .s-rdv .boutons { display: none; }
  .s-signal { background: var(--blanc); color: var(--noir); border: 1px solid var(--gris-grille); }
  .s-signal .titre-gradient { background: none; color: var(--noir); }
}

/* ==========================================================================
   Le design system du 16 septembre 2026 (da-video/design-system.md).

   Cette section vient en dernier et reprend ce qui, plus haut, contredit le
   design system : le bouton primaire, les labels. Les jetons, la typographie
   et le mouvement sont dans tokens.css et apparitions.css.
   ========================================================================== */

/* Le bouton primaire est noir, avec sh-btn. Il était bleu voilé : le voile
   n'a plus d'objet, le blanc tient 17,1:1 sur #1C1C1C. Les états
   s'assombrissent, comme le prévoit la section 5.5. Le formulaire suit. */
.bouton-primaire,
.prequal .bouton-primaire {
  background: var(--noir);
  color: var(--blanc);
  box-shadow: inset 0 1px 0 rgb(255 255 255 / .12), var(--sh-btn);
  transition:
    background-color var(--t-micro) var(--ease-out),
    box-shadow var(--t-micro) var(--ease-out);
}
.bouton-primaire:hover,
.prequal .bouton-primaire:hover {
  background: var(--noir-hover);
  box-shadow: inset 0 1px 0 rgb(255 255 255 / .14), var(--sh-btn);
}
.bouton-primaire:active,
.prequal .bouton-primaire:active {
  background: var(--noir-actif);
  box-shadow: inset 0 1px 0 rgb(255 255 255 / .08), -1px 1px 4px 0 rgb(0 0 0 / .26);
}

/* Le label : un point à l'accent de la DA et un texte mono en capitales, à
   l'encre ink-2. Plus de pilule. Le poids des sélecteurs est volontaire : les
   variantes plus haut en pèsent jusqu'à trois classes. */
.section-nom.section-nom.section-nom {
  display: inline-flex;
  align-items: center;
  gap: calc(var(--spacing) * 10);
  padding: 0;
  border: 0;
  border-radius: 0;
  background: none;
  box-shadow: none;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  color: rgb(28 28 28 / .64);
}
.section-nom.section-nom.section-nom::before {
  content: "";
  flex: none;
  inline-size: calc(var(--spacing) * 8);
  block-size: calc(var(--spacing) * 8);
  border-radius: var(--r-full);
  background: var(--bleu); /* bleu uni, 21 septembre 2026, sur demande */
}
/* Sur les surfaces sombres, l'encre passe au blanc à 64 % (7,9:1 sur #1C1C1C). */
:is(.sur-sombre, .fond-sombre, .constat-sombre, [data-theme="signal"]) .section-nom.section-nom.section-nom {
  color: rgb(255 255 255 / .64);
}
/* Dans la DA Signal, le point prend l'accent chaud. */
[data-theme="signal"] .section-nom.section-nom.section-nom::before {
  background: var(--gradient-signal);
}

/* Les petits intitulés prennent la grammaire du label : mono, capitales,
   ink-2. Le violet ne porte plus de texte. */
.eyebrow,
.carte-levier {
  font-family: var(--font-mono);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: var(--ls-corps);
  color: rgb(28 28 28 / .64);
}

/* La tuile d'icône du design system : surface de la DA, liseré d'un pixel,
   icône à l'encre du texte. Sur le panneau sombre du constat, la surface est
   un blanc à 4 %. */
.constat-icone.constat-icone {
  border: 1px solid rgb(255 255 255 / .10);
  background: rgb(255 255 255 / .04);
  color: #FFFFFF;
  box-shadow: var(--sh-glass);
}

/* Les tuiles de l'éligibilité, sur fond clair : surface blanche, liseré,
   icône à l'encre du texte, sh-base. Le bleu éclairci sort. */
.cond-icone.cond-icone {
  border: 1px solid rgb(28 28 28 / .08);
  background: var(--blanc);
  color: var(--noir);
  box-shadow: var(--ombre-carte);
}

/* ⚠️ La pilule de l'en-tête déborde de --marge-pilule autour du contenu. Sur
   téléphone, la marge de page ne fait que 20 px : à 28, la pilule sortait de
   8 px à droite et la page défilait de côté. Le débordement était masqué par
   un overflow-x retiré avec les anciennes apparitions. La marge est bornée à
   la marge de page moins 8 px, et vaut toujours 28 au-delà de 1 024 px. */
.entete {
  --marge-pilule: min(calc(var(--spacing) * 28), calc(var(--marge-laterale) - var(--spacing) * 8));
}

/* ==========================================================================
   Le constat sur fond blanc, 16 septembre 2026, sur demande.
   Le panneau sombre est retiré du balisage : la section reprend le rythme
   normal, et tout ce qui était écrit pour le sombre repasse à l'encre claire
   du design system.
   ========================================================================== */
.s-constat.s-constat { padding-block-start: var(--rythme-section); }

/* Le titre tient sur deux lignes : ses deux temps s'enchaînent au lieu de
   passer chacun à la ligne, et il prend toute la largeur du conteneur. */
h2.constat-h2 {
  width: auto;
  max-width: none;
}
h2.constat-h2 > span { display: inline; }

/* « Et c'est normal. » dans la pilule de verre du label d'origine, sur
   demande du 16 septembre 2026 : verre noir à 4 %, liseré à 8 %, reflets
   blancs et ombre très douce, texte noir en casse normale. */
.constat-chute {
  display: inline-flex;
  align-items: center;
  align-self: flex-start;
  width: fit-content;
  max-width: none;
  margin: var(--rythme-bloc) 0 0;
  padding: calc(var(--spacing) * 9) calc(var(--spacing) * 22);
  border-radius: var(--r-full);
  font-family: var(--font-corps);
  font-size: var(--text-m);
  font-weight: 400;
  line-height: 1.4;
  letter-spacing: var(--ls-corps);
  color: var(--noir);
  background: rgb(28 28 28 / .04);
  border: 1px solid rgb(28 28 28 / .08);
  -webkit-backdrop-filter: blur(25px);
  backdrop-filter: blur(25px);
  box-shadow:
    inset 0 1px 0 0 rgb(255 255 255 / .25),
    inset 0 0 calc(var(--spacing) * 20) 0 rgb(255 255 255 / .25),
    0 calc(var(--spacing) * 2) calc(var(--spacing) * 8) rgb(28 28 28 / .04),
    0 calc(var(--spacing) * 7) calc(var(--spacing) * 35) rgb(28 28 28 / .04);
}

.constat-icone.constat-icone {
  border: 1px solid rgb(28 28 28 / .08);
  background: var(--blanc);
  color: var(--noir);
  box-shadow: var(--ombre-carte);
}
.constat-filet { background: rgb(28 28 28 / .10); }
.constat-titre { color: var(--noir); }
.constat-texte { color: var(--gris-lisible); }

/* Le titre rend .26em à la mise en page pour ses jambages : sans reprise, les
   tuiles du constat touchent presque sa seconde ligne. */
.s-constat .constats { margin-block-start: var(--rythme-intra); }

/* Le numéro et le sous-titre de chaque levier, remis le 16 septembre 2026,
   sur demande, au-dessus de la loi du panneau. Grammaire du label. */
.pilier-sous {
  display: flex;
  align-items: baseline;
  gap: calc(var(--spacing) * 10);
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--text-s);
  font-weight: 500;
  letter-spacing: var(--ls-corps);
  text-transform: uppercase;
  color: rgb(28 28 28 / .64);
}
.pilier-num { color: var(--noir); }

/* Le bloc épinglé de la méthode sur l'échelle de Pulsor. Le chapô est revenu
   le 16 septembre 2026 et le niveau titre-lg vaut maintenant 60 px : la scène
   ne tenait plus dans l'écran. Fenêtre haute : titre en h2 sur deux lignes,
   loi du levier en h3. Fenêtre basse : un cran de moins pour chacun. */
.piliers-tete h2 { max-width: calc(var(--spacing) * 1120); }
.pilier-loi { font-size: var(--h3); }
@media (max-height: 900px) {
  .piliers[data-arme] .piliers-tete h2 { font-size: var(--h3); max-width: calc(var(--spacing) * 880); }
  .piliers[data-arme] .pilier-loi { font-size: var(--h4); }
}

/* La barre de navigation flotte au-dessus du bloc épinglé : le haut de la
   scène lui est réservé, sinon le label passe dessous. Sur les fenêtres les
   plus basses, c'est encore le visuel qui rend la hauteur. */
.piliers[data-arme] .piliers-colle {
  box-sizing: border-box;
  padding-block: calc(var(--spacing) * 96) calc(var(--spacing) * 16);
}
@media (max-height: 820px) {
  .piliers[data-arme] .piliers-colle { gap: calc(var(--spacing) * 20); }
  .piliers[data-arme] .pilier-visuel { --cadre-rapport: 2 / 1; }
}
@media (max-height: 740px) {
  .piliers[data-arme] .pilier-visuel { --cadre-rapport: 5 / 2; }
}

/* L'ouverture de la méthode, 16 septembre 2026, sur demande. Le titre s'écrit
   au centre de l'écran (site.js pose --titre-dy et l'opacité de chaque
   lettre), puis remonte, et le reste du bloc arrive avec --reste. Tout est
   piloté par le défilement : aucune transition ici, elle ferait traîner. */
.frappe-mot { white-space: nowrap; }
/* En ligne, les lettres gardent leur crénage. */
.piliers-tete h2 .frappe { display: inline; }

/* ⚠️ **Le dégradé du titre est découpé sur tout son texte, lettres
   transparentes comprises** : peint sur le parent, il montrerait la phrase
   entière avant qu'elle soit écrite. Chaque lettre porte le sien (--h2-fond),
   celui du parent prend une taille nulle, qui ne se peint pas, même en
   mosaïque. Il reste lisible par le script. */
.piliers-tete h2:has(.frappe) { background-size: 0 0 !important; }

.piliers[data-arme] .piliers-tete h2 {
  translate: 0 var(--titre-dy, 0px);
  position: relative;
  z-index: 1;
}

/* Le reste du bloc entre par les gestes des apparitions (apparitions.js,
   groupe de l'ouverture) : texte brouillé pour les labels, lignes masquées
   pour le chapô et la loi, montée pour la barre, les onglets, le visuel et
   les points, roulé pour les icônes et les coches. */

/* Pendant l'ouverture, ce qui n'est pas encore là ne se clique pas. */
.piliers[data-ouverture] .onglets,
.piliers[data-ouverture] .piliers-scene { pointer-events: none; }

/* ==========================================================================
   Le bouton principal de la home, 16 septembre 2026, sur demande.
   Composant fourni (ButtonWhite), repris en bleu uni du design system,
   #0091FF, Auxiliary 700 du Figma.

   ⚠️ **Le libellé blanc ne tient que 3,26 pour 1 sur ce bleu**, sous le
   minimum AA de 4,5 que la section 2 impose. C'est un choix demandé et
   assumé, après avoir proposé le libellé noir (5,3) et le voile (4,6).

   ⚠️ La police du composant est Montserrat : elle n'est ni dans le design
   system ni dans le projet, le bouton prend Geist. Le halo blanc du composant
   est gardé tel quel : écrit pour un fond sombre, il est presque invisible sur
   le blanc de la page. Les états s'assombrissent, comme le veut la section 5.5.
   ========================================================================== */
.btn-principal {
  display: inline-flex;
  flex-direction: row;
  align-items: center;
  gap: 8.5px;
  padding: 10.31px 16.5px 10.31px 20.62px;
  border: 0;
  border-radius: 16px;
  background: #0091FF;
  color: #FFFFFF;
  font-family: var(--font-corps);
  font-size: 16px;
  font-weight: 500;
  line-height: 18px;
  letter-spacing: var(--ls-corps);
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  box-shadow:
    0px 16px 32px 0px rgba(252, 252, 252, 0.08),
    0px 8px 16px 0px rgba(252, 252, 252, 0.08),
    0px 4px 8px 0px rgba(252, 252, 252, 0.08),
    0px 0px 10px 0px rgba(252, 252, 252, 0.08),
    0px 0px 50px 0px rgba(252, 252, 252, 0.12),
    inset 0px 1px 0px 0px rgba(252, 252, 252, 0.12),
    0px 0px 36px 0px rgba(252, 252, 252, 0.08),
    0px 0px 4px 0px rgba(252, 252, 252, 0.2);
  transition: background-color var(--t-micro) var(--ease-out);
}
.btn-principal:hover { background: #0082E6; }
.btn-principal:active { background: #0073CC; }

.btn-principal__label {
  margin: 0;
  padding: 0;
}

.btn-principal__icon {
  display: inline-flex;
  flex: 0 0 auto;
  width: 13px;
  height: 13px;
  line-height: 0;
}
.btn-principal__icon svg { width: 100%; height: 100%; }

/* Les deux boutons du hero se centrent sur la même ligne, le composant
   étant moins haut que le bouton à contour. */
.s-hero .boutons { align-items: center; gap: calc(var(--spacing) * 16); }

/* Le bouton principal prend le dégradé fourni le 16 septembre 2026, sur
   demande : linéaire, de #37A9FF à gauche à #0091FF à droite. Le libellé
   blanc y tient 2,6:1 à gauche et 3,26:1 à droite, sous le minimum AA,
   comme choisi. Les états assombrissent les deux arrêts. */
.btn-principal { background: linear-gradient(90deg, #37A9FF 0%, #0091FF 100%); }
.btn-principal:hover { background: linear-gradient(90deg, #2A9CF2 0%, #0082E6 100%); }
.btn-principal:active { background: linear-gradient(90deg, #1E8FE0 0%, #0073CC 100%); }

/* « Et c'est normal. » centrée sous les trois constats, sur demande. */
.constat-chute { align-self: center; justify-self: center; }

/* Les deux boutons du hero à la même taille, sur demande du 16 septembre
   2026 : une grille à colonnes égales leur donne la largeur du plus large et
   la hauteur du plus haut, et les deux libellés prennent le même corps. */
.s-hero .boutons {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  align-items: stretch;
  width: max-content;
  max-width: 100%;
}
.s-hero .boutons > .btn-principal { justify-content: center; }
.s-hero .boutons > .bouton-contour .bouton-noyau {
  flex: 1 1 auto;
  justify-content: center;
  font-size: 16px;
  font-weight: 500;
}
@media (max-width: 479px) {
  .s-hero .boutons { grid-auto-flow: row; grid-auto-rows: 1fr; width: 100%; }
}

/* Les tuiles d'icônes du constat, en couleur, sur demande du 16 septembre
   2026 : aplat jaune #FCC101, tracé blanc à 50 %, sans liseré ni ombre,
   coins plus ronds. Les trois tuiles prennent la même couleur, la seule
   fournie. Décoratives, hors de l'arbre : la règle de contraste du texte ne
   s'y applique pas. */
.constat-icone.constat-icone.constat-icone {
  border: 0;
  border-radius: var(--r-lg);
  background: #FCC101;
  color: rgb(255 255 255 / .5);
  box-shadow: none;
}

/* Rayon plein sur les deux boutons du hero, sur demande du 16 septembre
   2026 : le bouton bleu, et les deux coques du bouton à contour, qui restent
   concentriques puisqu'une pilule dans une pilule garde des arrondis
   parallèles. */
.s-hero .boutons > .btn-principal,
.s-hero .boutons > .bouton-contour,
.s-hero .boutons > .bouton-contour .bouton-noyau { border-radius: var(--r-full); }

/* « septembre 2026 » dans le titre du constat porte le cadre des tuiles
   d'icônes juste en dessous, sur demande du 16 septembre 2026 : blanc, liseré
   à 8 %, ombre des cartes, rayon 16. Il remplace le surlignage violet. Le
   segment sort du dégradé du titre et prend l'encre noire pleine. Le clone
   redessine le cadre sur chaque ligne si le segment se casse.
   La marge négative à droite colle la virgule qui suit au cadre : sans elle,
   l'approche gauche du glyphe laissait un jour visible. Elle est posée sur le
   cadre et non sur la virgule, qui n'est un élément qu'une fois le titre
   découpé par le script.
   L'ombre est à lui, sur demande : une arête basse à peine marquée, puis
   quatre couches neutres de plus en plus larges et de plus en plus
   rentrées, pour un cadre posé plutôt qu'un halo. */
.constat-marque {
  padding-inline: .28em;  /* .12em jusqu'au 17 septembre 2026, élargi sur demande */
  margin-inline-end: -.2em; /* suit la garniture : la virgule reste collée au cadre */
  border: 1px solid rgb(28 28 28 / .08);
  border-radius: var(--r-lg);
  background: var(--blanc);
  box-shadow:
    inset 0 -2px 0 0 rgb(28 28 28 / .04),
    0 1px 1px rgb(28 28 28 / .04),
    0 2px 4px -1px rgb(28 28 28 / .06),
    0 8px 16px -4px rgb(28 28 28 / .08),
    0 20px 40px -12px rgb(28 28 28 / .10);
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
  color: var(--noir);
  -webkit-text-fill-color: var(--noir);
}

/* Tous les boutons primaires sont bleus, sur demande du 16 septembre 2026 :
   le dégradé du bouton du hero, #37A9FF vers #0091FF, libellé blanc sous le
   minimum AA par choix. Le sélecteur pèse autant que la règle noire écrite
   plus haut dans cette section, et vient après elle. */
.bouton-primaire,
.prequal .bouton-primaire {
  background: linear-gradient(90deg, #37A9FF 0%, #0091FF 100%);
  color: #FFFFFF;
}
.bouton-primaire:hover,
.prequal .bouton-primaire:hover { background: linear-gradient(90deg, #2A9CF2 0%, #0082E6 100%); }
.bouton-primaire:active,
.prequal .bouton-primaire:active { background: linear-gradient(90deg, #1E8FE0 0%, #0073CC 100%); }

/* Les flèches de l'équipe, d'après une référence fournie le 16 septembre
   2026 : une pilule gris clair qui porte deux boutons ronds et des chevrons
   coolicons. Le bouton actif est blanc à chevron noir, avec une ombre douce ;
   celui qui ne mène nulle part se fond dans la pilule, chevron gris. */
.equipe-fleches.equipe-fleches {
  gap: calc(var(--spacing) * 4);
  padding: calc(var(--spacing) * 4);
  border-radius: var(--r-full);
  background: #F0F0F0;
  box-shadow:
    inset 0 0 0 1px rgb(28 28 28 / .04),
    0 calc(var(--spacing) * 8) calc(var(--spacing) * 24) rgb(28 28 28 / .06);
}
.equipe-fleches .equipe-fleche,
.equipe-fleches .equipe-fleche:hover {
  background: var(--blanc);
  color: var(--noir);
  box-shadow:
    inset 0 -1px 0 0 rgb(28 28 28 / .08),
    0 1px 2px rgb(28 28 28 / .08),
    0 calc(var(--spacing) * 4) calc(var(--spacing) * 12) rgb(28 28 28 / .10);
  transition: background-color var(--t-micro) var(--ease-out), box-shadow var(--t-micro) var(--ease-out);
}
.equipe-fleches .equipe-fleche:hover:not(:disabled) { background: #FAFAFA; }
.equipe-fleches .equipe-fleche:disabled {
  opacity: 1;
  background: #F5F5F5;
  color: #9A9A9A;
  box-shadow: inset 0 0 0 1px rgb(28 28 28 / .05);
}
.equipe-fleches .equipe-fleche svg {
  inline-size: calc(var(--spacing) * 24);
  block-size: calc(var(--spacing) * 24);
}

/* Le tracé des tuiles du constat passe au blanc plein, sur demande du
   16 septembre 2026 : à 50 %, il ressortait trop peu sur les trois couleurs. */
.constat-icone.constat-icone.constat-icone { color: #FFFFFF; }

/* Les tuiles du constat repassent en blanc, icône violette, sur demande du
   16 septembre 2026 : tuile blanche, liseré et ombre des cartes, tracé
   #6647F0 sur les trois (il a été bleu quelques minutes). */
.constats > .constat .constat-icone.constat-icone.constat-icone {
  background: var(--blanc);
  color: #6647F0;
  border: 1px solid rgb(28 28 28 / .08);
  box-shadow: var(--ombre-carte);
}

/* Le bouton principal prend le geste d'un composant fourni le
   16 septembre 2026, sur demande (ButtonWithIconDemo, shadcn et lucide) :
   une pilule de 48 px, une pastille blanche de 40 px à droite portant la
   flèche en diagonale. Au survol et au focus clavier, la pastille glisse à
   gauche en tournant de 45 degrés, et le libellé échange ses garnitures pour
   lui laisser la place. 500 ms, comme le composant.
   Réécrit à la main : ni React, ni Tailwind, ni lucide n'entrent, la flèche
   est recopiée dans le balisage. Le dégradé bleu et le corps de 16 px sont
   gardés : le composant est en 14 px, mais les deux boutons du hero ont le
   même corps depuis le même jour. Rien ne bouge tant que personne ne survole.
   Étendu le même jour, sur demande, au bouton de l'opportunité et à celui de
   l'éligibilité : les sélecteurs ne sont plus bornés au hero. */
.btn-principal {
  position: relative;
  overflow: hidden;
  min-height: 48px;
  border-radius: var(--r-full);
  padding: 4px 58px 4px 24px;
  transition:
    padding 500ms var(--ease-inout),
    background-color var(--t-micro) var(--ease-out);
}
.btn-principal:hover,
.btn-principal:focus-visible {
  padding: 4px 24px 4px 58px;
}
.btn-principal__label { position: relative; z-index: 1; }

.btn-principal .btn-principal__icon {
  position: absolute;
  top: 50%;
  right: 6px;
  width: 40px;
  height: 40px;
  align-items: center;
  justify-content: center;
  border-radius: var(--r-full);
  background: var(--blanc);
  color: var(--noir);
  translate: 0 -50%;
  transition:
    right 500ms var(--ease-inout),
    rotate 500ms var(--ease-inout);
}
.btn-principal .btn-principal__icon svg { width: 16px; height: 16px; }
.btn-principal:hover .btn-principal__icon,
.btn-principal:focus-visible .btn-principal__icon {
  right: calc(100% - 46px);
  rotate: 45deg;
}
/* La variante violette, sur les trois boutons du formulaire, 16 septembre
   2026, sur demande. Le violet du système en aplat, comme les boutons du
   constat : le blanc y tient 5,64 pour 1 sans voile. Les états
   s'assombrissent. Les boutons s'étirent dans la rangée du formulaire, le
   libellé se centre donc dans la place qui reste. */
.btn-principal.btn-violet { background: var(--violet); justify-content: center; }
.btn-principal.btn-violet:hover,
.btn-principal.btn-violet:focus-visible { background: #5536DF; }
.btn-principal.btn-violet:active { background: #4A2FC8; }
.btn-principal .btn-principal__icon { color: var(--noir); }
.btn-violet .btn-principal__icon { color: var(--violet); }
/* Le bouton du hero prend le chevron du « Voir la méthode », un seul,
   17 septembre 2026, sur demande, incliné de 45° vers le haut comme la
   flèche d'origine. Au survol, la pastille pivote de 45° comme avant, et
   le chevron revient à l'horizontale. */
.btn-principal.btn-chevron .btn-principal__icon svg {
  width: 20px;
  height: 20px;
  rotate: -45deg;
}

/* L'envoi en cours et le consentement non coché, que le script du
   formulaire pose : ils visaient `.bouton`, que ces boutons ne portent plus.
   Désactivé, le bouton passe au gris de la section 5.5 et ne bouge plus. */
.btn-principal[data-occupe] { opacity: .7; cursor: progress; }
.btn-principal[aria-disabled="true"],
.btn-principal.btn-principal[aria-disabled="true"]:hover,
.btn-principal.btn-principal[aria-disabled="true"]:active,
.btn-principal.btn-principal[aria-disabled="true"]:focus-visible {
  background: var(--gris-grille);
  color: var(--gris-texte);
  box-shadow: none;
  cursor: not-allowed;
  padding: 4px 58px 4px 24px;
}
.btn-principal[aria-disabled="true"] .btn-principal__icon.btn-principal__icon {
  right: 6px;
  rotate: none;
  color: var(--gris-texte);
}

/* Un libellé long se replie sur téléphone au lieu de pousser la page : le
   bouton de l'opportunité est plus large que la colonne à 375 px. */
@media (max-width: 479px) {
  .btn-principal { max-width: 100%; white-space: normal; text-align: center; }
}

/* Les cartes de l'éligibilité, centrées, 16 septembre 2026, sur demande.
   L'icône et le titre partagent une pilule blanche à ombre (sh-base), comme
   la référence fournie ; les points se centrent sous elle. */
.cond-carte.cond-carte { align-items: center; text-align: center; }

/* La pilule prend toute la largeur de la carte, sur demande : elle s'arrête
   à la même distance du bord de la carte que le rond de l'icône du bord de la
   pilule, 4 px. Le haut suit la même marge. Son rayon est celui de la carte
   moins cette marge, 20 px, pour que les deux arrondis restent parallèles. */
.cond-carte, .verdict { --cond-marge: calc(var(--spacing) * 4); }

.cond-tete {
  display: flex;
  align-items: center;
  gap: calc(var(--spacing) * 10);
  align-self: stretch;
  margin-inline: calc(var(--cond-marge) - var(--spacing) * 32);
  margin-block-start: calc(var(--cond-marge) - var(--spacing) * 32);
  /* Le liseré compte dans la marge : le rond est à 4 px du bord extérieur. */
  padding: calc(var(--cond-marge) - 1px) calc(var(--spacing) * 18) calc(var(--cond-marge) - 1px) calc(var(--cond-marge) - 1px);
  border-radius: calc(var(--r-xl) - var(--cond-marge));
  background: var(--blanc);
  border: 1px solid rgb(28 28 28 / .06);
  box-shadow: var(--ombre-carte);
  text-align: start;
}

.cond-tete .cond-icone.cond-icone {
  flex: none;
  inline-size: calc(var(--spacing) * 36);
  block-size: calc(var(--spacing) * 36);
  border-radius: var(--r-full);
}
.cond-tete .cond-icone svg {
  inline-size: calc(var(--spacing) * 18);
  block-size: calc(var(--spacing) * 18);
}

.cond-tete .cond-titre {
  font-size: var(--text-m);
  font-weight: 600;
  line-height: 1.25;
  letter-spacing: -.01em;
}

/* Les trois coches d'une carte tombent sur la même verticale : la liste est
   un bloc centré, à la largeur de sa plus longue ligne, et ses lignes sont
   alignées à gauche. */
.cond-carte .cond-points {
  align-self: center;
  align-items: stretch;
  inline-size: fit-content;
  max-inline-size: 100%;
}
.cond-carte .cond-points li { justify-content: flex-start; text-align: start; }

/* Le rythme de section ne se compte qu'une fois, sur demande du 16 septembre
   2026. Chaque section portait le rythme en haut et en bas : entre deux
   sections, les deux s'additionnaient, 213 px à 961 px de large et 232 à
   1 440, pour une valeur de 116. Une section qui en suit une autre perd donc
   sa garniture haute, et l'écart vaut le rythme, une fois.
   Le hero garde son exception : sa garniture basse est courte, la section qui
   le suit garde la sienne. Les panneaux sombres ne sont pas touchés : leur
   garniture intérieure est sous leur fond, pas entre deux sections. */
main > .section + .section { padding-top: 0; }
main > .s-hero + .section { padding-top: var(--rythme-section); }

/* Les trois engagements du hero, sur demande du 17 septembre 2026 : plus de
   capsule, une icône en préfixe, tout à l'encre noire, et les trois sur une
   même ligne. La rangée des actions se casse d'elle-même : la ligne tient à
   côté des boutons si la colonne le permet, et passe dessous sinon. */
/* Trois étages depuis le 17 septembre 2026, sur demande : les boutons, le
   prix, puis les engagements. Chacun garde la largeur de son contenu. */
.s-hero .hero-actions {
  flex-direction: column;
  flex-wrap: nowrap;
  align-items: flex-start;
  gap: calc(var(--spacing) * 24);
}
.s-hero .preuve-ligne {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-start;
  gap: calc(var(--spacing) * 8) calc(var(--spacing) * 24);
  padding: 0;
  background: none;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  color: var(--noir);
  font-size: var(--legende);
}
/* Le prix à droite des boutons, en texte seul ; les engagements prennent
   toute la ligne suivante. */
.s-hero .preuve-ligne { flex-basis: auto; }
/* Les badges du hero, sur demande du 17 septembre 2026, d'après une
   référence : le prix en capsule bleue claire à liseré bleu et pastille
   verte, les engagements en pilules grises. L'encre est un bleu approfondi,
   #006BBF : le bleu du système sur sa teinte ne tient que 3 pour 1, celui-ci
   tient 4,9 sur la capsule et 4,8 sur le gris. La pastille ne bouge pas. */
.hero-prix {
  display: inline-flex;
  align-items: center;
  gap: calc(var(--spacing) * 16);
  margin: 0;
  padding: calc(var(--spacing) * 8) calc(var(--spacing) * 16);
  border: 1px solid #5CB3FF;
  border-radius: var(--r-lg);
  background: #E8F4FF;
  color: #006BBF;
  font-size: var(--corps);
  font-weight: 500;
  letter-spacing: -.015em;
  white-space: nowrap;
}
.hero-prix-point {
  flex: none;
  width: 10px;
  height: 10px;
  border-radius: var(--r-full);
  background: #4ADE3A;
}
.hero-prix-sep { font-weight: 700; }
/* Les engagements du hero, sur demande du 17 septembre 2026, d'après une
   référence : plus de pilule grise. Icône de 18 px au tracé en dégradé de
   site, 6 px d'écart, texte à l'encre en 400, et un filet vertical de
   1 × 12 px à 12 % d'encre entre deux engagements. */
.preuve-defs { position: absolute; width: 0; height: 0; overflow: hidden; }
.s-hero .preuve-ligne { gap: calc(var(--spacing) * 16); }
.s-hero .preuve-ligne li {
  display: inline-flex;
  align-items: center;
  gap: calc(var(--spacing) * 8);
  padding: 0;
  border-radius: 0;
  background: none;
  color: var(--noir);
  font-size: var(--corps);
  font-weight: 400;
}
.s-hero .preuve-ligne li + li::before {
  content: "";
  flex: none;
  align-self: center;
  width: 1px;
  height: 12px;
  margin-inline-end: calc(var(--spacing) * 16);
  background: rgb(28 28 28 / .12);
  color: transparent;
}
.s-hero .preuve-ligne li.debut-ligne::before { content: none; }
.s-hero .preuve-ligne svg { flex: none; width: 18px; height: 18px; }
@media (max-width: 767px) {
  .s-hero .preuve-ligne { flex-direction: column; align-items: flex-start; }
  .s-hero .preuve-ligne li + li::before { content: none; }
}

/* Les deux cartes du verdict prennent le même dessin, sur demande : titre et
   icône dans la pilule pleine largeur, liste en bloc centré aux marques
   alignées. Filtre pour la disqualification, document pour ce qu'on attend. */
/* Les listes du verdict partent du bord gauche de la carte, sur demande. */
.verdict .verdict-liste { align-self: stretch; }

/* Le verdict revient au dessin plat, sur demande : pas de pilule. Ce qui
   change par rapport à avant : l'icône se pose devant le titre, dans un rond
   à liseré comme les marques de la liste. Rien n'y porte d'ombre. Un filet
   sous l'en-tête a été essayé puis retiré, sur demande. */
.verdict .cond-tete {
  margin: 0;
  padding: 0;
  gap: calc(var(--spacing) * 12);
  background: none;
  border: 0;
  border-radius: 0;
  box-shadow: none;
}
.verdict .cond-tete .cond-icone.cond-icone {
  inline-size: calc(var(--spacing) * 40);
  block-size: calc(var(--spacing) * 40);
  background: none;
  border: 1px solid rgb(28 28 28 / .18);
  box-shadow: none;
  color: var(--noir);
}
.verdict .cond-tete .cond-titre {
  font-size: var(--titre-sm);
  line-height: var(--lh-titre);
  letter-spacing: var(--ls-titre-petit);
}

/* ==========================================================================
   Les illustrations des quatre leviers, 17 septembre 2026, sur demande.

   Elles remplacent les cadres de remplacement de la méthode. Quatre SVG dans
   le balisage, aucun fichier et aucune dépendance. Ce qu'elles montrent est
   ce que chaque cadre annonçait, mot pour mot.

   Trois règles tiennent, et elles décident du dessin :
   - **aucun chiffre** : le plan n'a ni montant ni pourcentage, et ses mois ne
     dessinent aucune courbe, ce serait un graphique sans échelle ;
   - **aucune marque de tiers** : « Recherche » et « Réponse IA », pas de
     logo de moteur ; le seul logo est celui de Signal ;
   - **aucune capture inventée** : Signal est un nœud de schéma, pas un écran.

   Le violet n'y entre que par le gradient de site, que la section 5.1
   réserve aux visuels. Tout le texte est à l'encre ou au gris lisible.

   L'illustration entre avec son levier : le cadre monte (apparitions.js),
   puis ses pièces le suivent une à une et ses traits se tracent. Une fois,
   à chaque levier affiché. Sans script ni mouvement, tout est là.
   ========================================================================== */
.pilier-illu {
  position: relative;
  aspect-ratio: var(--cadre-rapport, 16 / 11);
  max-width: 100%;
  overflow: hidden;
  background: var(--gris-clair);
  border: 1px solid rgb(28 28 28 / .06);
  border-radius: var(--r-xl);
}

/* ⚠️ **Le dessin est posé en absolu, et c'est ce qui le fait tenir.** Une
   hauteur en pour cent ne se résout pas sur une boîte dont la hauteur vient
   de son rapport : le SVG prenait la hauteur de sa largeur et sortait par le
   bas. Posé sur les quatre bords, il se cale dans le cadre, quel que soit le
   rapport que la fenêtre impose. */
.ill {
  position: absolute;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
}

.ill text { font-family: var(--font-mono); font-size: 14px; fill: var(--noir); letter-spacing: 0; }
.ill .ill-doux { fill: var(--gris-lisible); }
.ill .ill-mois { font-size: 13px; fill: var(--gris-lisible); }
.ill .ill-petit { font-size: 11px; }
.ill .ill-fort { font-weight: 500; }
.ill .ill-requete { font-family: var(--font-corps); font-size: 16px; }
.ill .ill-marque { font-family: var(--font-corps); font-size: 15px; font-weight: 600; }

.ill-carte { fill: var(--blanc); stroke: rgb(28 28 28 / .08); stroke-width: 1; }
.ill-ombre { filter: drop-shadow(0 6px 10px rgb(28 28 28 / .08)); }
.ill-case { fill: #FAFAFA; stroke: var(--gris-grille); }
.ill-case-forte { fill: var(--blanc); stroke: rgb(28 28 28 / .14); }
.ill-cible { fill: var(--blanc); stroke: var(--noir); stroke-width: 1.5; }
.ill-tache { fill: rgb(28 28 28 / .16); }
.ill-voile { fill: rgb(28 28 28 / .10); }
.ill-encre { fill: var(--noir); }
.ill-gris { fill: #DCDCDC; }
.ill-gris-clair { fill: #E6E6E6; }
.ill-page-grise { fill: #F7F7F7; stroke: #DEDEDE; }
.ill-pastille { fill: var(--blanc); stroke: rgb(28 28 28 / .14); }
.ill-pastille-forte { fill: var(--blanc); stroke-width: 1.5; }
.ill-trace { fill: none; stroke: #CFCFCF; stroke-width: 1.5; }
.ill-trace-forte { stroke-width: 2.5; }
.ill-trace-pointe { stroke-dasharray: .02 .02; }

/* L'entrée des pièces. L'attente n'est qu'une opacité, sous le drapeau que
   apparitions.js pose seulement s'il peut tenir sa promesse. */
.ill-el { transform-box: fill-box; }

html[data-apparitions] .pilier-illu:not([data-apparu]) .ill-el { opacity: 0; }

.pilier-illu[data-apparu] .ill-el {
  animation: ill-entre 700ms var(--gemeos-out-quint) backwards;
  animation-delay: calc(var(--app-retard, 0ms) + 250ms + var(--i, 0) * 45ms);
}
.pilier-illu[data-apparu] .ill-traits { animation: none; }
.pilier-illu[data-apparu] .ill-traits .ill-trace:not(.ill-trace-pointe) {
  stroke-dasharray: 1;
  animation: ill-trace 1200ms var(--ease-out) backwards;
  animation-delay: calc(var(--app-retard, 0ms) + 500ms);
}

@keyframes ill-entre {
  from { opacity: 0; translate: 0 12px; }
  to   { opacity: 1; translate: 0 0; }
}
@keyframes ill-trace {
  from { stroke-dashoffset: 1; }
  to   { stroke-dashoffset: 0; }
}

/* --------------------------------------------------------------------------
   Seconde version, 17 septembre 2026, sur demande : « plus grandes, plus
   complexes, un petit truc au survol, des curseurs de l'équipe à la Figma ».

   **Plus grandes.** La toile est en 16/9, le rapport du cadre épinglé : le
   dessin le remplit au lieu d'y flotter, et la colonne du visuel prend
   plus de place que celle du texte, au-delà de 1 024 px.

   **Les curseurs.** Chacun porte le prénom de qui répond du levier, d'après
   la table de la section 09 : Paco sur Cadrer et Centraliser, Solène, Meije
   et Raphaël sur Valoriser, Raphaël sur Centraliser, Nico sur Capter. Le
   curseur « Vous » est le client, qui valide. ⚠️ **Les prénoms sont bloqués
   pour la mise en ligne** (section 3) : chaque curseur d'équipe porte
   data-bloque et disparaît avec le reste. Les cinq couleurs tiennent le
   blanc de l'étiquette au-dessus de 4,5 pour 1, et quatre sont déjà des
   valeurs du fichier.

   **Le survol.** Les curseurs se déplacent vers un autre endroit du dessin,
   ce qui est sélectionné se soulève, une sélection ou une bulle de
   commentaire paraît. Rien ne bouge tant que la souris ne passe pas : ce
   n'est pas un mouvement permanent. Sans survol possible, rien de tout ça,
   et sous prefers-reduced-motion les transitions sont coupées par tokens.css.
   -------------------------------------------------------------------------- */
@media (min-width: 1024px) {
  .pilier { grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr); gap: calc(var(--spacing) * 48); }
}

.ill .ill-nom { font-family: var(--font-corps); font-size: 12px; font-weight: 500; fill: var(--blanc); }
.ill .ill-initiale { font-family: var(--font-corps); font-size: 10px; font-weight: 600; fill: var(--blanc); }
.ill .ill-bulle { font-family: var(--font-corps); font-size: 12px; fill: var(--noir); }
.ill .ill-requete { font-size: 15px; }
.ill .ill-marque { font-size: 14px; }
.ill-marque-fond { fill-opacity: .1; transition: fill-opacity var(--motion-slow) var(--ease-out); }

.ill-curseur,
.ill-leve,
.ill-apparait {
  transform-box: view-box;
  transition:
    translate 900ms var(--gemeos-inout-cubic),
    opacity var(--motion-quick) var(--ease-out);
}
.ill-apparait { opacity: 0; }

@media (hover: hover) {
  .pilier-illu:hover .ill-curseur { translate: var(--hx, 0) var(--hy, 0); }
  .pilier-illu:hover .ill-leve { translate: 0 -4px; transition-duration: var(--motion-slow); }
  .pilier-illu:hover .ill-apparait { opacity: 1; transition-delay: 500ms; }
  .pilier-illu:hover .ill-marque-fond { fill-opacity: .18; }
  .pilier-illu:hover .ill-trace-forte { stroke-width: 3.5; }
}
.ill-trace-forte { transition: stroke-width var(--motion-slow) var(--ease-out); }

/* Empilés, les leviers n'ont pas de hauteur à tenir : le cadre prend le
   rapport de la toile, sans bandes vides au-dessus et en dessous. */
@media (max-width: 1023px) {
  .pilier-illu { --cadre-rapport: 16 / 9; }
}

/* --------------------------------------------------------------------------
   Le bloc épinglé de la méthode tient dans l'écran, 17 septembre 2026, sur
   demande : « on ne voit pas tout le copy ». Relevé à 1903 par 940 : la
   scène finissait à 960 px, sous le bas de l'écran, et le dock du chatbot
   couvrait le bas du texte.

   **La scène prend la hauteur qui reste, et le visuel se règle sur elle.**
   Les rapports posés par paliers de hauteur (16/11, 2/1, 5/2) devinaient la
   place ; ici elle est mesurée : la scène est un conteneur de taille, et le
   cadre ne dépasse jamais sa hauteur, en gardant le 16/9 de la toile.

   **Le bas est réservé au dock du chatbot** (88 px), comme le haut l'est à la
   barre de navigation. Sur une fenêtre basse, l'en-tête et la loi du levier
   descendent encore d'un cran.
   -------------------------------------------------------------------------- */
.piliers[data-arme] .piliers-colle {
  padding-block-end: calc(var(--spacing) * 88);
}
.piliers[data-arme] .piliers-scene {
  flex: 1 1 0;
  min-block-size: 0;
  container-type: size;
  grid-template-rows: minmax(0, 1fr);
}
/* La colonne du visuel ne prend que la largeur que sa hauteur lui permet :
   ce qu'il n'occupe pas revient au texte, qui passe sur moins de lignes. */
@media (min-width: 1024px) {
  .piliers[data-arme] .pilier {
    grid-template-columns:
      min(calc((100% - var(--spacing) * 48) * .574), calc(100cqh * 16 / 9))
      minmax(0, 1fr);
  }
}
.piliers[data-arme] .pilier { block-size: 100%; }
.piliers[data-arme] .pilier-visuel {
  --cadre-rapport: 16 / 9;
  inline-size: min(100%, calc(100cqh * 16 / 9));
  justify-self: center;
}
/* Les deux titres suivent la hauteur de la fenêtre, bornés par l'échelle :
   jamais plus que leur niveau, jamais moins que deux crans dessous. */
.piliers[data-arme] .piliers-tete h2 {
  font-size: clamp(var(--h4), 5.2svh, var(--h2));
  max-width: calc(var(--spacing) * 1120);
}
.piliers[data-arme] .pilier-loi { font-size: clamp(var(--h5), 3.4svh, var(--h3)); }
/* L'en-tête épinglé ne cumule plus la marge du rythme intra et l'écart du
   bloc : un seul écart, qui suit la hauteur. */
.piliers[data-arme] .piliers-tete { gap: clamp(8px, 1.6svh, 20px); }
.piliers[data-arme] .piliers-tete > * { margin-block-start: 0; }
.piliers[data-arme] .piliers-colle { gap: clamp(16px, 3svh, 40px); }
@media (max-height: 820px) {
  .piliers[data-arme] .pilier-texte { gap: calc(var(--spacing) * 16); }
  .piliers[data-arme] .pilier-points { gap: calc(var(--spacing) * 10); }
  .piliers[data-arme] .pilier-points li { font-size: var(--corps-sm); }
}

/* --------------------------------------------------------------------------
   Troisième version, 17 septembre 2026, sur demande : « des vraies illus ».

   Cadrer est une page de proposition Percer, à la grammaire des planches
   de la proposition commerciale. Valoriser est le fichier de travail de
   Percer, avec deux vraies captures de ce site (images/methode). Centraliser
   est l'orbite de Signal, dans la DA de Signal. Capter est une page de
   résultats reconstruite et une vraie capture de notre formulaire.

   Les curseurs suivent la répartition donnée le même jour : Solène (DA),
   Nico (design system et site), Meije (webdesign) et Raphaël (support) sur
   Valoriser ; Nico seul sur Centraliser et sur Capter ; Paco et le client
   sur Cadrer.

   ⚠️ **L'orbite ne tourne qu'au survol.** Une orbite permanente a été
   retirée du hero le 14 septembre : elle compterait comme un mouvement
   permanent de plus. Les logos se contre-tournent et restent droits.
   -------------------------------------------------------------------------- */
.pilier-illu-sombre {
  background: #111111;
  border-color: rgb(255 255 255 / .08);
}

.ill .ill-violet { fill: #6647F0; }
.ill .ill-vert { fill: #1A7F4B; }
.ill .ill-clair { fill: rgb(255 255 255 / .64); letter-spacing: .12em; }
.ill .ill-mini { font-size: 8px; }
.ill .ill-petit { font-size: 10px; letter-spacing: .04em; }
.ill .ill-corps { font-family: var(--font-corps); font-size: 10.5px; fill: var(--gris-lisible); }
.ill .ill-encre-texte { fill: var(--noir); }
.ill .ill-fort-corps { font-weight: 600; }
.ill .ill-marque-mot { font-family: var(--font-titre); font-weight: var(--graisse-titre, 700); font-size: 15px; letter-spacing: -.03em; }
.ill .ill-grand { font-size: 26px; }
.ill .ill-titre-doc { font-family: var(--font-titre); font-weight: var(--graisse-titre, 700); font-size: 23px; letter-spacing: -.035em; }
.ill .ill-nom-doc { font-family: var(--font-titre); font-weight: var(--graisse-titre, 700); font-size: 15px; letter-spacing: -.02em; }
.ill .ill-cadre-nom { font-family: var(--font-corps); font-size: 9px; fill: var(--gris-lisible); }
.ill .ill-specimen { font-family: var(--font-titre); font-weight: var(--graisse-titre, 700); font-size: 40px; letter-spacing: -.04em; }
.ill .ill-bouton { font-family: var(--font-corps); font-size: 11px; font-weight: 500; fill: var(--blanc); }
.ill .ill-lien { font-family: var(--font-corps); font-size: 13px; font-weight: 500; fill: #0A5FB0; }
.ill .ill-dfs { font-size: 9px; font-weight: 500; }
.ill .ill-dfs-for { fill: #0091FF; }

.ill-trajet { stroke-dasharray: none; }

/* --------------------------------------------------------------------------
   Quatrième passe, 17 septembre 2026, sur demande.

   ⛔ **L'orbite tourne en permanence**, et Capter alterne SEO et GEO en
   permanence : **ce sont deux mouvements permanents de plus** que les trois
   autorisés en section 2 de CLAUDE.md. Les garde-fous du site tiennent :
   ils ne jouent que sur le levier affiché, quand son illustration est à
   l'écran (data-joue, posé par site.js), et sous prefers-reduced-motion ils
   se retirent au lieu de se raccourcir : l'orbite reste immobile, et Capter
   montre la page de résultats seule.

   Cadrer ne laisse plus de gris : la diapositive est le cadre.
   -------------------------------------------------------------------------- */
.pilier-illu-slide { background: var(--blanc); }

.ill .ill-blanc { fill: var(--blanc); }
.ill .ill-noir { fill: var(--noir); }

/* L'orbite est tournée par site.js, une seule horloge pour tous les logos. */

/* Les deux moteurs se relaient toutes les cinq secondes, sans survol :
   site.js pose data-vue (seo ou geo) sur l'illustration. **La vue qui
   arrive rejoue son entrée**, pièce par pièce : sa règle d'animation ne
   s'applique que tant qu'elle est la vue affichée, et reprend à zéro à
   chaque retour. Avant la première bascule, data-vue est absent et
   l'entrée est celle du levier. */
.ill-alterne-a,
.ill-alterne-b { transition: opacity 500ms var(--ease-out); }
.ill:not([data-vue="geo"]) .ill-alterne-b,
.ill[data-vue="geo"] .ill-alterne-a { opacity: 0; }

.ill[data-vue] :is(.ill-alterne-a, .ill-alterne-b) .ill-el { animation: none; }
.ill[data-vue="seo"] .ill-alterne-a .ill-el,
.ill[data-vue="geo"] .ill-alterne-b .ill-el {
  animation: ill-entre-vue 700ms var(--gemeos-out-quint) backwards;
  animation-delay: calc(200ms + var(--i, 0) * 180ms);
}
/* Un nom à elle : une animation ne repart que si son nom change, et les
   pièces de la vue GEO portaient déjà « ill-entre » à la première bascule. */
@keyframes ill-entre-vue {
  from { opacity: 0; translate: 0 12px; }
  to   { opacity: 1; translate: 0 0; }
}

/* Hors de l'écran ou sur un levier caché, site.js ne bascule rien. Sous
   prefers-reduced-motion, il ne bascule jamais : la page Google reste. */

/* La lecture de l'onglet tenu : le temps avant le levier suivant. Le script
   pose --avance, de 0 à 1. Depuis le 17 septembre 2026, sur demande, c'est
   **l'onglet entier qui se remplit** d'un blanc cassé, de gauche à droite,
   sur un fond presque transparent. Le remplissage est découpé et non
   étiré : une pilule étirée écraserait ses arrondis. */
.piliers[data-arme] .onglet { position: relative; isolation: isolate; }
.piliers[data-arme] .onglet[data-actif] { background: rgb(255 255 255 / .18); }
.piliers[data-arme] .onglet::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  background: #FDFCF8;
  clip-path: inset(0 calc((1 - var(--avance, 0)) * 100%) 0 0 round var(--r-full));
  opacity: 0;
  transition: opacity var(--motion-quick) var(--ease-out);
}
.piliers[data-arme] .onglet[data-actif]::after { opacity: 1; }

/* Centraliser : les tuiles sombres du design system (rayon 12, surface
   blanche à 4 %, liseré blanc à 10 %, lueur intérieure de sh-glass). */
.ill .ill-tuile-sombre {
  /* Le blanc à 5 % sur #111, rendu opaque : les rayons ne passent pas dessous. */
  fill: #1D1D1D;
  stroke: rgb(255 255 255 / .12);
  stroke-width: 1;
}
.ill .ill-tuile-signal { fill: #222222; }
.ill .ill-dfs-sombre { fill: #F5F5F5; }
.ill .ill-dfs-sombre .ill-dfs-for { fill: #5CB3FF; }

/* --------------------------------------------------------------------------
   Cinquième passe, 17 septembre 2026, sur demande : « l'utilisateur est
   saturé d'information ». Cadrer et Valoriser passent en fil de fer, sans
   un mot ; Capter perd sa moitié CRO et occupe toute la carte.

   **La montée dans la page de résultats.** Percer part du quatrième rang et
   double les trois autres un par un : chaque résultat doublé descend d'un
   rang au moment où Percer passe. La montée se joue à l'entrée du levier et
   à chaque retour de la vue SEO, jamais en boucle dans la vue. Sous
   prefers-reduced-motion, les rangs sont posés d'emblée, Percer en tête.
   ⚠️ C'est l'image d'un premier rang : à relire contre l'interdit d'une
   position Google promise (section 2 de CLAUDE.md). Aucun chiffre de rang
   n'est écrit.
   -------------------------------------------------------------------------- */
.ill .ill-requete-g { font-size: 13px; }
.ill-grimpe { --r: 62px; }

.pilier-illu[data-apparu] .ill:not([data-vue="geo"]) .ill-grimpe {
  animation: 2600ms var(--ease-inout) 350ms both;
}
.pilier-illu[data-apparu] .ill:not([data-vue="geo"]) .ill-grimpe-percer { animation-name: ill-grimpe-percer; }
.pilier-illu[data-apparu] .ill:not([data-vue="geo"]) .ill-grimpe-1 { animation-name: ill-grimpe-1; }
.pilier-illu[data-apparu] .ill:not([data-vue="geo"]) .ill-grimpe-2 { animation-name: ill-grimpe-2; }
.pilier-illu[data-apparu] .ill:not([data-vue="geo"]) .ill-grimpe-3 { animation-name: ill-grimpe-3; }
.pilier-illu[data-apparu] .ill:not([data-vue="geo"]) .ill-grimpe-fond {
  animation: ill-grimpe-fond 2600ms var(--ease-inout) 350ms both;
}
.ill-grimpe-fond { fill-opacity: .14; }

@keyframes ill-grimpe-percer {
  0%, 22%  { translate: 0 calc(var(--r) * 3); }
  34%, 50% { translate: 0 calc(var(--r) * 2); }
  62%, 78% { translate: 0 var(--r); }
  90%, 100% { translate: 0 0; }
}
@keyframes ill-grimpe-3 {
  0%, 22% { translate: 0 calc(var(--r) * -1); }
  34%, 100% { translate: 0 0; }
}
@keyframes ill-grimpe-2 {
  0%, 50% { translate: 0 calc(var(--r) * -1); }
  62%, 100% { translate: 0 0; }
}
@keyframes ill-grimpe-1 {
  0%, 78% { translate: 0 calc(var(--r) * -1); }
  90%, 100% { translate: 0 0; }
}
@keyframes ill-grimpe-fond {
  0%, 85% { fill-opacity: 0; }
  100% { fill-opacity: .14; }
}

/* ==========================================================================
   L'offre datée, sur demande du 17 septembre 2026, d'après une référence
   fournie : chaque colonne de la frise s'ouvre sur la date de son jalon, en
   gros, puis son rang en mois, en Geist Mono capitales. Les dates sont
   posées par site.js (data-jalon) depuis le jour de la visite.
   ========================================================================== */
.frise-mois {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: calc(var(--spacing) * 12);
}

.frise-quand {
  display: flex;
  flex-direction: column;
  gap: calc(var(--spacing) * 4);
  min-inline-size: 0;
  margin: 0;
  max-width: none;
}

.frise-date {
  font-family: var(--font-titre);
  font-weight: var(--graisse-titre, 700);
  font-size: var(--h4);
  line-height: 1.1;
  letter-spacing: -.02em;
  color: var(--noir);
  white-space: nowrap;
}

.frise-rang {
  font-family: var(--font-mono);
  font-size: calc(var(--spacing) * 11);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--gris-lisible);
}


/* Le détail replié, sur demande du 17 septembre 2026 : de base, chaque
   colonne ne montre que sa date ; un « + » à droite déroule la liste et la
   validation. Le repli n'est armé que par site.js (data-repli sur la frise) :
   sans script, tout reste ouvert et le bouton reste caché. Le texte replié est
   toujours dans le HTML. La hauteur court par grid-template-rows, 0fr à 1fr. */
.frise-plus {
  flex: none;
  display: grid;
  place-items: center;
  inline-size: calc(var(--spacing) * 32);
  block-size: calc(var(--spacing) * 32);
  padding: 0;
  border: 1px solid rgb(28 28 28 / .08);
  border-radius: var(--r-full);
  background: var(--blanc);
  box-shadow: 0 1px 2px rgb(28 28 28 / .06);
  color: var(--noir);
  cursor: pointer;
  transition: background-color var(--motion-instant) var(--ease-out);
}
.frise-plus[hidden] { display: none; }
.frise-plus:hover { background-color: var(--gris-clair); }
.frise-plus svg {
  inline-size: calc(var(--spacing) * 14);
  block-size: calc(var(--spacing) * 14);
  transition: rotate var(--motion-quick) var(--ease-out);
}
.frise-plus[aria-expanded="true"] svg { rotate: 45deg; }

.frise-detail { display: grid; }
.frise-detail-in {
  display: flex;
  flex-direction: column;
  gap: calc(var(--spacing) * 16);
  min-block-size: 0;
}

[data-repli] .frise-temp { gap: 0; align-self: start; }
[data-repli] .frise-mois {
  border-block-end-color: transparent;
  transition: border-color var(--motion-quick) var(--ease-out);
}
[data-repli] .frise-temp:has(.frise-plus[aria-expanded="true"]) .frise-mois {
  border-block-end-color: var(--gris-grille);
}
[data-repli] .frise-detail {
  grid-template-rows: 0fr;
  opacity: 0;
  visibility: hidden;
  transition:
    grid-template-rows var(--motion-slow) var(--ease-inout),
    opacity var(--motion-quick) var(--ease-out),
    visibility 0s linear var(--motion-slow);
}
[data-repli] .frise-detail-in { overflow: hidden; }
[data-repli] .frise-detail[data-ouvert] {
  grid-template-rows: 1fr;
  opacity: 1;
  visibility: visible;
  transition:
    grid-template-rows var(--motion-slow) var(--ease-inout),
    opacity var(--motion-quick) var(--ease-out) 80ms,
    visibility 0s;
}
[data-repli] .frise-detail[data-ouvert] .frise-detail-in {
  padding-block-start: calc(var(--spacing) * 16);
}
[data-repli] .frise-detail-in { padding-block-start: 0; transition: padding var(--motion-slow) var(--ease-inout); }
/* La validation n'est plus poussée en bas : les colonnes repliées n'ont pas
   la même hauteur que celle qu'on ouvre. */
[data-repli] .frise-validation { margin-block-start: 0; }

/* La hauteur minimale posée par site.js ne doit pas écarter les puces. */
.frise-liste { align-content: start; }

/* --------------------------------------------------------------------------
   08. Le prix comparé, recadré le 17 septembre 2026, sur demande

   Les trois cartes se lisaient comme trois offres. Chacune porte maintenant
   le même ratio prix/bénéfice, leviers couverts et coût par levier, et les
   embauches n'ont plus de bouton : on ne vend pas un alternant.
   -------------------------------------------------------------------------- */

/* Le bénéfice, ramené au panier de la cible. */
.pc-rentab {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: calc(var(--spacing) * 12);
  text-align: center;
}

.pc-note {
  max-width: var(--mesure-incise);
  font-size: var(--legende);
  line-height: 1.45;
  color: var(--gris-lisible);
}

.pc-note a { color: var(--noir); }


/* Le panier pour lequel le tarif est fixé, sous le prix de Percer. Même
   pastille que la durée, dans sa version sur bleu (6,29 pour 1). */
.carte-offre-cible {
  margin-block-start: calc(var(--spacing) * 8);
  font-size: var(--legende);
  line-height: 1.3;
}
.carte-offre-cible .pc-engagement { display: inline-block; white-space: normal; }
.carte-offre-cible .insecable { white-space: nowrap; }

/* Les côtés des glyphes, sur le titre entier, 17 septembre 2026, sur demande.
   Le H2 et le H3 portent un dégradé découpé sur le texte, et il ne se peint
   que dans leur boîte : le premier et le dernier glyphe de chaque ligne en
   débordent à l'interlettrage serré. La garniture latérale agrandit la boîte
   peinte, la marge négative rend la place.
   ⚠️ **content-box**, sinon la garniture se prend sur la mesure (max-width,
   fit-content) et une ligne peut casser ailleurs.
   ⚠️ **!important sur la marge** : la remise à zéro « h1, h2, … { margin: 0 } »
   et les règles de section en « margin: X 0 0 » l'écrasaient, et le texte se
   décalait de 7 px. Aucun titre ne se centre par une marge automatique ; ceux
   qui se centrent autrement gardent leur centre, la marge étant symétrique. */
h2,
h3 {
  box-sizing: content-box;
  padding-inline: var(--garniture-glyphe);
  margin-inline: calc(-1 * var(--garniture-glyphe)) !important;
}
