/* FICHIER GÉNÉRÉ — ne pas éditer.
   Source : src/scss/main.scss — voir eleventy.config.js */

@charset "UTF-8";
/* Point d'entrée compilé en assets/css/main.css (voir eleventy.config.js).

   L'ordre des `@use` FAIT la cascade : chaque partial est émis une seule
   fois, à l'endroit de son premier `@use` — plus besoin de préfixe
   numérique sur un nom de fichier pour piloter l'ordre de sortie. Cet ordre
   doit rester en phase avec la liste des `{% include %}` de src/index.njk
   (pour les sections de l'accueil ; les autres partials habillent des blocs
   réutilisables ailleurs sur le site, voir src/_includes/blocs/). */
/* Points de rupture du site — une seule valeur par palier, quel que soit le
   nombre de fichiers qui la consultent. Avant Sass, chaque section recopiait
   le rem à la main : 60rem apparaissait dans dix-neuf fichiers différents, et
   rien ne garantissait qu'ils restent d'accord. */
/* =========================================================================
   Design tokens — extraits des variables Figma (frame « Final Page »).

   Les valeurs de COULEUR sont reprises telles quelles depuis Figma.
   L'échelle TYPOGRAPHIQUE et l'échelle d'ESPACEMENT ont été normalisées :
   la maquette contient des tailles à la demi-décimale (14.5, 16.5, 15.5,
   18.5…) héritées d'un kit importé. On les a ramenées sur une échelle
   entière cohérente — l'écart visuel est nul, la maintenance bien meilleure.
   ========================================================================= */
:root {
  /* ---------- Couleurs de marque ---------- */
  --c-base-main: #f9f9f5; /* fond principal, off-white */
  --c-base-surface: #e8e2d7; /* fond alternatif, sable */
  --c-text-main: #2c2a2e; /* texte courant */
  --c-blue-dark: #202b5c; /* sections sombres, eyebrow du hero */
  --c-blue-zodiac: #0f1c3f; /* sombre profond */
  /* Le bleu où la page s'éteint : le creux du dégradé bas de la section
     finale, et le fond du pied de page. Deux encres et non une seule, parce
     que le bleu nuit de section n'est pas assez profond pour marquer une fin.
     La maquette y descend à #0d153c, ce mélange y tombe à quelques points
     près. */
  --c-blue-abysse: color-mix(in srgb, var(--c-blue-zodiac) 80%, #2c2a2e);
  /* ⚠️ LE BLEU DE MARQUE A DEUX NOMS, ET CE N'EST PAS UN DOUBLON.
     `--c-blue` est le RÔLE « accent de la page » : `.ton-page--*` le réassigne à
     la teinte d'un pilier, et c'est tout l'intérêt du dispositif. `--c-blue-biron`
     est la COULEUR, que rien ne réassigne jamais.

     Promu le 26/08/2026, quand la figure `composeur` de /plateforme/ a eu besoin
     du bleu de Biron sur une page dont l'accent est le rose : elle dessine
     l'interface du produit, où le bleu code les métriques. Sans ce nom il aurait
     fallu réécrire #3f58d7 en clair, donc tenir deux valeurs accordées.

     ⚠️ Ne pas s'en servir comme raccourci pour « le bleu » : partout où l'on veut
     l'accent de la page — formes, mises en valeur, boutons —, c'est `--c-blue`
     qu'il faut lire, sinon la page cesse de suivre son `tonPage`. */
  --c-blue-biron: #3f58d7;
  --c-blue: var(--c-blue-biron); /* bleu des accents typographiques */
  --c-blue-action: #2340ff; /* bleu des boutons — plus saturé */
  --c-blue-400: #7885ce;
  --c-blue-100: #dde2f4;
  --c-blue-50: #eff1fa;
  --c-casper: #aab4d2; /* texte secondaire sur fond sombre */
  --c-fiord: #4c5772; /* texte secondaire sur fond clair */
  /* Le corail du logo. C'est la seule couleur vive de la palette qui ne soit
     pas déjà affectée à un pilier, d'où son emploi pour les trois chiffres qui
     se contredisent dans le lecteur du hero.

     ⚠️ Contraste 3,06 sur du BLANC : au-dessus du seuil de 3,0 exigé pour du
     grand texte, mais sans marge. Sur le crème il tombe à 2,90 et ne passe
     plus — à n'employer que sur des cartes blanches. */
  --c-alerte: #ee6873;
  --c-fluo: #d1ff00; /* accent fluo */
  --c-starship: #d4f24a; /* accent fluo adouci */
  --c-melrose: #c4b5fd;
  --c-mustard: #fcd34d;
  --c-white: #ffffff;
  --c-black: #000000;
  /* ---------- Couleurs par pilier produit ----------
     Chaque brique Biron a sa couleur d'identification. */
  --c-pilier-dictionnaire: #00b3a4;
  --c-pilier-plateforme: #ff5d73;
  --c-pilier-connecteur: #8b5cff;
  --c-pilier-integrations: #f0a22e;
  /* ⚠️ LE VERT A UNE SECONDE VALEUR, POUR LES FONDS SOMBRES — même besoin que le
     rose et l'orange plus bas, et même règle : une bande que la maquette dit sombre
     reste sombre (règle 10 bis de MAQUETTE.md). Le vert du pilier ne porte le blanc
     qu'à 2,63 ; celui-ci le porte à 5,58.

     ⚠️ IL EST ARRIVÉ LE DERNIER DES TROIS, LE 25/08/2026, sur la question d'Hugo :
     « on peut les faire passer dans une teinte plus sombre pour en faire des blocs sur
     lesquels on écrit en blanc ? » Le dictionnaire était le seul pilier encore clair
     en aplat — les trois autres avaient basculé les 19 et 20/08. Le commentaire de
     _texte-panneau.scss qui justifiait de le laisser clair (« aucune maquette ne dit
     sombre cette bande-là ») est donc levé, et le périmètre est le pilier entier :
     les deux boîtes de /dictionnaire/ ET le pavé dictionnaire de /produit/.

     ⚠️ CE N'EST PAS LA MÊME OPÉRATION QUE POUR L'ORANGE, et c'est le seul point où ce
     jeton-ci est plus simple que les deux autres : l'aplat #00b3a4 est DÉJÀ à
     saturation 100 %. L'assombrir ne peut donc pas désaturer, et il n'y a pas de
     « maronasse » à craindre — le piège de l'orange était qu'un jaune-orange foncé
     devient brun, ce qui obligeait à faire tourner la teinte vers le rouge. Ici on
     tient la teinte ET la saturation, et on ne descend que la clarté :

       aplat   #00b3a4  175°  S 100 %  L 35 %
       sombre  #00756c  175°  S 100 %  L 23 %

     Un `color-mix` vers le bleu nuit reste malgré tout à éviter, pour l'autre raison :
     il ferait virer le vert au bleu-gris et la teinte ne se lirait plus comme celle du
     pilier posée sur la même page.

     ⚠️ IL Y A DEUX SEUILS À TENIR, PAS UN, exactement comme pour l'orange : sur un fond
     sombre `.on-dark` met --text-accent au fluo, et c'est lui qui porte la mise en
     valeur du titre. Il faut donc que le BLANC et le FLUO passent tous les deux — et
     c'est le fluo qui contraint, pas le blanc. Candidats mesurés le 25/08/2026 :

       #008f83  L 28 %  blanc 4,00  fluo 3,44  ✗ même le blanc ne passe pas
       #00857a  L 26 %  blanc 4,55  fluo 3,91  ✗ le blanc passe, le fluo non
       #008075  L 25 %  blanc 4,86  fluo 4,17  ✗ idem
       #007a70  L 24 %  blanc 5,20  fluo 4,46  ✗ il manque 0,04 au fluo
       #00756c  L 23 %  blanc 5,58  fluo 4,79  RETENU — le premier qui tient les deux
       #007067  L 22 %  blanc 5,96  fluo 5,13  tient, la boîte lit plus « sombre » que « verte »
       #00665e  L 20 %  blanc 6,86  fluo 5,89  tient, la vivacité du pilier part

     #00756c est donc le plus proche de l'aplat qui tienne les deux seuils, et ses
     marges sont à 0,1 près celles de l'orange sombre retenu (5,66 / 4,85) — c'est ce
     qui a décidé entre lui et #007067.

     Contrastes : blanc 5,58 ✓ · fluo 4,79 ✓ · casper 2,70 ✗ (à passer au blanc,
     règle 11) · --c-text-main 2,55 ✗ (l'ancienne encre de cette boîte) · sur blanc
     5,58, donc utilisable telle quelle comme encre de badge. */
  --c-pilier-dictionnaire-sombre: #00756c;
  /* ⚠️ LE VIOLET A UNE SECONDE VALEUR LUI AUSSI, ET C'EST LA SEULE DES QUATRE QUI
     SOIT DÉRIVÉE. Les trois autres sont des aplats choisis à la main, précisément
     parce qu'un mélange désature — mais celle-ci est un mélange assumé depuis le
     19/08/2026, et son point d'arrêt a été arbitré sur des mesures : le violet du
     pilier est bancal des deux côtés (3,43 pour du texte foncé, 4,14 pour du blanc,
     il manque 0,36 au seuil de 4,5), et aucune encre ne passe dessus tel quel.

     80 % vers le zodiac donne #724fd9, choisi le 25/08/2026 — « il est encore un peu
     clair » — parce qu'il aligne le poids de la boîte sur celui des trois autres :
     blanc 5,47, contre 5,58 pour le vert et 5,66 pour l'orange. À 90 % (#7f56ec) il
     n'était qu'à 4,72, le plus faible des quatre.

     Contrastes : blanc 5,47 ✓ · fluo 4,70 ✓ · casper 2,65 ✗ (à passer au blanc,
     règle 11) · --c-blue-zodiac 3,05 ✗ (l'encre de titre du site, à passer au blanc
     elle aussi) · sur blanc 5,47, donc utilisable telle quelle comme encre de badge.

     ⚠️ IL N'EST PAS L'APLAT DE BOUTON DE `.ton-page--connecteur`, resté à 90 %, et
     les deux NE DOIVENT PAS ÊTRE « RÉHARMONISÉS ». Ils ont partagé cette valeur sans
     partager le rôle : ici un FOND DE BOÎTE, là un aplat dont les 90 % sont calés sur
     le seuil de lisibilité de son libellé blanc. Assombrir un fond ne dit rien du
     bouton.

     ⚠️ CONSÉQUENCE DISPONIBLE MAIS NON PRISE : à 5,47 le fluo repasse le seuil du
     texte courant (4,70 contre 4,05 à 90 %), donc les boîtes qui avaient dû passer
     --text-accent au blanc pourraient lui rendre le fluo. Elles ne le font pas, faute
     que ça ait été demandé — c'est une décision d'Hugo, pas un effet de bord à
     ramasser au passage. */
  --c-pilier-connecteur-sombre: color-mix(
    in srgb,
    var(--c-pilier-connecteur) 80%,
    var(--c-blue-zodiac)
  );
  /* ⚠️ LE ROSE A UNE SECONDE VALEUR, POUR LES FONDS SOMBRES — même besoin que
     l'orange plus bas, et même règle : une bande que la maquette dit sombre reste
     sombre (règle 10 bis de MAQUETTE.md). Le rose du pilier ne porte le blanc qu'à
     2,97 ; celui-ci le porte à 4,85.

     ⚠️ C'EST UNE COULEUR CHOISIE PAR HUGO LE 19/08/2026, PAS UN MÉLANGE CALCULÉ.
     Elle a remplacé le jour même un `color-mix` du rose vers le bleu nuit, écarté
     pour la raison qui vaut aussi pour l'orange : mélanger désature. Ne pas la
     « ramener » sur un `color-mix` de --c-pilier-plateforme en croyant réduire une
     valeur en dur — le rapprochement serait faux et la teinte changerait.

     ⚠️ ELLE A ÉTÉ PROMUE EN JETON LE 20/08/2026, quand un second bloc en a eu
     besoin. Elle vivait en clair dans `.onglets-metiers__boite--plateforme` ; la
     recopier dans `_texte-panneau.scss` aurait laissé deux valeurs à tenir
     accordées. Les deux la lisent maintenant ici.

     Contrastes : blanc 4,85 ✓ · casper 2,35 ✗ (à passer au blanc, règle 11) ·
     fluo 4,16 ⚠️ (jamais du texte courant) · sur blanc 4,85, donc utilisable telle
     quelle comme encre de badge. */
  --c-pilier-plateforme-sombre: #bc4b64;
  /* ⚠️ L'ORANGE DU PILIER A UNE SECONDE VALEUR, POUR L'ENCRE, et c'est le seul des
     quatre dans ce cas. En aplat #f0a22e tient très bien (texte sombre à 6,71) ; en
     ENCRE sur le crème il ne donne que 2,01 — un tiers de moins que le vert du
     dictionnaire (2,90) ou le rose de la plateforme (2,82), les deux niveaux déjà
     acceptés par Hugo pour des surtitres et des marqueurs.

     ⚠️ CE N'EST PAS UN MÉLANGE VERS LE BLEU NUIT, et c'est là qu'est tout le sujet :
     un `color-mix` de l'orange vers le zodiac garde sa TEINTE (38° environ) et ne
     baisse que sa clarté — or un orange jaune assombri est un BRUN. La maquette
     employait #c6820f (« Meteor », 38°, 3,01) et Hugo l'a écarté le 20/08/2026 :
     « maronasse ».

     La solution n'est pas de foncer davantage, c'est de FAIRE TOURNER LA TEINTE vers
     le rouge. À luminance égale, un orange à 28° garde sa saturation là où un orange
     à 38° la perd — et gagne du contraste au passage :

       #f0a22e  33°  S 81 %  2,01  l'aplat du pilier, inutilisable en encre
       #c6820f  38°  S 92 %  3,01  Meteor, écarté — trop jaune, donc brun une fois foncé
       #cc6207  28°  S 97 %  3,74  RETENU — même famille que l'aplat, franchement plus vif

     On ne descend pas plus bas que 28° : à 17° (#d9480f) le contraste monte encore à
     4,07, mais la teinte devient un rouge-orangé qui ne se lit plus comme la même
     couleur que l'aplat #f0a22e posé sur la même page. */
  --c-pilier-integrations-encre: #cc6207;
  /* ⚠️ ET UNE TROISIÈME VALEUR, POUR LES FONDS SOMBRES. Règle d'Hugo, 20/08/2026 :
     « je veux que les parties dites sombres soient des parties sombres avec du texte
     blanc, quitte à assombrir le orange. »

     Le besoin : là où la maquette peint une bande BLEU NUIT et où la page reprend la
     couleur du pilier, la boîte doit rester un vrai fond sombre — donc porter du
     BLANC à 4,5. Ni l'aplat (2,12) ni l'encre (3,95) n'y arrivent.

     ⚠️ CE N'EST PAS DAVANTAGE UN MÉLANGE VERS LE BLEU NUIT que l'encre au-dessus, et
     la démonstration est chiffrée. Mélanger désature, et c'est LA DÉSATURATION qui
     fait le marron, pas l'assombrissement :

       #b55a0e  encre mélangée à 88 %   27°  S 86 %  blanc 4,74  ← maronasse
       #974e17  encre mélangée à 72 %   26°  S 74 %  blanc 6,14  ← franchement marron
       #88491b  encre mélangée à 64 %   25°  S 67 %  blanc 6,96  ← du brun, plus un orange

     La bonne opération est de DESCENDRE LA CLARTÉ À SATURATION PLEINE, et de laisser
     la teinte tourner vers le rouge — la même opération que pour l'encre, poussée
     d'un cran. Hugo est arrivé de son côté dans cette famille en proposant #E67A0D
     (30°, S 89 %) : même parenté, simplement trop clair pour du blanc (2,94).

     Candidats montés sur /labo/orange-sombre/ et comparés le 20/08/2026 :

       #e67300  30°  S 100 %  blanc 3,08  fluo 2,64  sa teinte saturée — trop clair
       #be5f00  30°  S 100 %  blanc 4,35  fluo 3,73  manque 0,15 au seuil
       #b45a00  30°  S 100 %  blanc 4,77  fluo 4,09  le blanc passe, LE FLUO NON
       #aa5500  30°  S 100 %  blanc 5,24  fluo 4,49  passe de justesse sur les deux
       #ab4a00  26°  S 100 %  blanc 5,66  fluo 4,85  RETENU par Hugo
       #964b00  30°  S 100 %  blanc 6,36  fluo 5,45  tient, mais la vivacité part

     ⚠️ IL Y A DEUX SEUILS À TENIR, PAS UN, et c'est ce qui a décidé. Sur un fond
     sombre, `.on-dark` met --text-accent au fluo, et c'est lui qui porte la mise en
     valeur du titre : il faut donc que le BLANC et le FLUO passent tous les deux.
     C'est ce qui écarte #b45a00, pourtant le plus proche de la teinte d'origine.

     ⚠️ LES TROIS VALEURS DU PILIER FORMENT UNE ÉCHELLE RÉGULIÈRE, et c'est ce qui les
     fait lire comme une même couleur plutôt que comme trois oranges :

       aplat   #f0a22e  36°  S  87 %  L 56 %
       encre   #cc6207  28°  S  93 %  L 41 %
       sombre  #ab4a00  26°  S 100 %  L 34 %

     La clarté descend, la saturation monte, la teinte tourne vers le rouge. Toute
     nouvelle valeur de ce pilier doit rester sur cette pente : c'est elle qui
     remplace la règle « on assombrit », qui donnait du brun. */
  --c-pilier-integrations-sombre: #ab4a00;
  /* ---------- Rôles sémantiques ----------
     On passe par des rôles plutôt que d'appeler les couleurs brutes dans
     les composants : changer un rôle suffit à repeindre tout le site. */
  /* ⚠️ LE FOND DE PAGE EST LE CRÈME, SAUF SUR TROIS FAMILLES DE PAGES.

     Il est passé au bleu très pâle le 21/08/2026 pour tout le site, puis remis
     au crème le 25/08/2026 : « il faudrait que les pages aient un fond crème,
     sauf les pages de cas client, blog et newsletter qui doivent un fond bleu ».
     Ces trois-là — les pages ÉDITORIALISÉES, celles qui racontent au lieu de
     présenter — gardent --c-blue-50, via `.fond-page--bleu` (voir plus bas).

     ⚠️ TROIS BLOCS ONT MESURÉ LEURS CONTRASTES CONTRE LE BLEU : les cartes de
     `cas-clients` et de `grille-articles` (blanches, 1,05 sur --c-blue-50, d'où
     leur filet obligatoire), l'encart de `article` (en --c-blue-100 pour ne pas
     disparaître) et la carte de `une`. Tous vivent sur les pages restées bleues,
     et rien n'est donc à reprendre — mais déplacer l'un d'eux sur une page crème
     demande de refaire ses mesures.

     ⚠️ --c-base-main NE SE CONFOND PAS AVEC --bg-page pour autant : il reste le
     CRÈME nommé, et quelques surfaces le posent en clair pour lui-même — les
     panneaux clairs dans une boîte sombre (`temoignages`, `connecteurs`,
     `onglets-metiers`). Ceux-là se mesurent contre le bleu nuit qui les entoure,
     pas contre le fond de la page. Un fond qui veut dire « le fond de la page »
     doit lire --bg-page, et lui seul — c'est ce qui fait que les trois familles
     ci-dessus basculent d'une seule ligne. */
  --bg-page: var(--c-base-main);
  --bg-surface: var(--c-white);
  --bg-inverse: var(--c-blue-dark);
  /* Fond de la section courante. Chaque section sombre le redéfinit
     localement. Les boutons secondaires s'y adossent : ils prennent
     toujours la couleur du fond sur lequel ils sont posés. */
  --section-bg: var(--bg-page);
  /* ⚠️ LE CORPS DE TEXTE PREND --text-strong, JAMAIS --text-muted.
     Règle du site depuis le 18/08/2026 : le texte courant est à pleine encre —
     blanc sur fond sombre, --c-text-main sur fond clair. Or c'est exactement ce
     que --text-strong vaut déjà dans les deux contextes, ici et sous `.on-dark`.
     Un chapeau ou un paragraphe n'a donc aucune raison de passer par --text-muted,
     et aucun override par contexte n'est nécessaire.

     --text-muted reste en service, mais pour ce qui n'est PAS du corps de texte :
     légendes, étiquettes mono, métas d'article, mentions légales, états vides.
     C'est la distinction à faire en migrant un bloc — pas « gris ou pas gris »,
     mais « est-ce que ça se lit comme du texte courant ». */
  --text-strong: var(--c-text-main);
  --text-muted: var(--c-fiord);
  --text-accent: var(--c-blue);
  --text-on-inverse: var(--c-white);
  --text-muted-on-inverse: var(--c-casper);
  --border-subtle: rgba(44, 42, 46, 0.12);
  --border-strong: rgba(44, 42, 46, 0.24);
  /* ---------- Typographie ---------- */
  /* Police des titres. Essai en cours depuis le 13/08/2026 pour remplacer
     Recoleta : POUR CHANGER DE POLICE, une seule ligne à modifier — celle
     de --font-titre, en y mettant l'un des cinq jetons ci-dessous. Les quatre
     autres restent déclarés dans _base.scss et ne pèsent rien tant qu'ils ne
     sont pas appelés — un @font-face n'est téléchargé qu'une fois demandé. */
  --f-recoleta: "Recoleta", "Fraunces", Georgia, "Times New Roman", serif;
  --f-rubik: "Rubik Variable", system-ui, sans-serif;
  --f-sora: "Sora Variable", system-ui, sans-serif;
  --f-space-grotesk: "Space Grotesk Variable", system-ui, sans-serif;
  --f-jakarta: "Plus Jakarta Sans Variable", system-ui, sans-serif;
  --font-titre: var(--f-sora);
  /* Reste nommé --font-serif parce que tout le CSS du site l'appelle ainsi,
     depuis l'époque où la police des titres était forcément une serif. */
  --font-serif: var(--font-titre);
  /* ⚠️ LE NOM DOIT ÊTRE "Inter Variable", PAS "Inter". C'est le nom donné au
     @font-face dans _base.scss — convention @fontsource, qui suffixe les
     fontes variables — et il n'existe nulle part ailleurs : écrit "Inter", le
     navigateur ne trouve rien, ne signale rien, et tout le texte du site
     tombe silencieusement sur system-ui. Le même piège vaut pour les quatre
     jetons de titre ci-dessus. */
  --font-sans: "Inter Variable", system-ui, -apple-system, "Segoe UI",
    sans-serif;
  --font-mono: "Chivo Mono Variable", ui-monospace, "SFMono-Regular", Consolas,
    monospace;
  /* Échelle fluide. Chaque niveau atteint son plafond à 1920px et non
     entre 980 et 1280 comme auparavant — sinon la page est typographiquement
     figée sur tout le desktop : un écran 1920 affichait exactement le même
     texte qu'un 1280, avec seulement plus de vide autour.

     Lecture des formules : la borne haute est la valeur à 1920, la pente
     est calée pour que la borne basse tombe sur un mobile 375. */
  --fs-mono: 0.6875rem; /* 11px  — eyebrows mono */
  --fs-xs: 0.875rem; /* 14px  — légendes */
  --fs-sm: 0.9375rem; /* 15px  — nav */
  --fs-btn: 0.9375rem; /* 15px  — boutons */
  --fs-base: clamp(1rem, 0.94rem + 0.2vw, 1.125rem); /* 16→18 */
  --fs-md: 1.125rem; /* 18px */
  --fs-h3: clamp(1.375rem, 1.05rem + 1vw, 2rem); /* 22→32 */
  --fs-h2: clamp(1.75rem, 1.25rem + 1.6vw, 3rem); /* 28→48 */
  --fs-quote: clamp(1.5rem, 1.1rem + 1.4vw, 2.5rem); /* 24→40 — citations */
  /* Bande mobile → desktop, jusqu'à 1280. La bande desktop est plus bas,
     dans une media query : un seul clamp de 360 à 1920 serait mou partout.
     Les deux bandes se raccordent exactement à 1280, sans saut. */
  --fs-h1: clamp(2.0625rem, 1.7446rem + 1.413vw, 2.875rem); /* 33→46 */
  --fs-eyebrow-lg: clamp(1.0625rem, 0.985rem + 0.3315vw, 1.25rem); /* 17→20 */
  --fs-lead: clamp(1rem, 0.974rem + 0.1105vw, 1.0625rem); /* 16→17 */
  --lh-tight: 1.08; /* titres — resserré, un 72px n'a pas besoin de 1.1 */
  --lh-snug: 1.35; /* sous-titres */
  --lh-normal: 1.6; /* texte courant */
  /* Interlettrage optique : plus le corps est grand, plus il faut serrer.
     Un titre à 72px avec le serrage d'un 44px paraît lâche. */
  --ls-display: -0.035em; /* h1 */
  --ls-tight: -0.03em; /* h2, h3 */
  --ls-wide: 0.14em; /* eyebrows mono en capitales */
  --fw-regular: 400;
  --fw-medium: 500;
  --fw-semibold: 600;
  --fw-bold: 700;
  /* ---------- Espacement (base 4px) ---------- */
  --sp-1: 0.25rem;
  --sp-2: 0.5rem;
  --sp-3: 0.75rem;
  --sp-4: 1rem;
  --sp-5: 1.25rem;
  --sp-6: 1.5rem;
  --sp-8: 2rem;
  --sp-10: 2.5rem;
  --sp-12: 3rem;
  --sp-16: 4rem;
  --sp-20: 5rem;
  --sp-24: 6rem;
  --sp-32: 8rem;
  /* Respiration verticale entre sections, fluide. */
  --sp-section: clamp(4rem, 2rem + 6vw, 7.5rem);
  /* ---------- L'exemption du chrome ----------
     ⚠️ L'EN-TÊTE ET LE PIED DE PAGE NE SUIVENT PAS LES 85 % du site. Règle
     d'Hugo, 26/08/2026 : « 85 % uniforme, sauf sur le header et le footer ».
     La barre de navigation est la même sur toutes les pages et sa nav tombait
     à 12,75 px — elle reste donc à sa taille d'aujourd'hui.

     ⚠️ LE MÉCANISME EST UN CONTRE-ZOOM, ET C'EST LE SEUL QUI TIENNE. Un `rem`
     se résout TOUJOURS contre la racine, jamais contre un ancêtre : aucune
     déclaration posée sur l'en-tête ne peut « rebaser » les rem de ses
     descendants. Redonner sa taille au chrome autrement voudrait dire
     surcharger localement une dizaine de jetons (--fs-sm, --fs-btn, --fs-md,
     --fs-mono, --sp-*…) PLUS les seize valeurs en rem écrites en clair dans
     ses deux feuilles, toutes en nombres bâtards. `zoom` le fait en une
     déclaration et rattrape aussi les paddings, les gouttières et le logo.

     ⚠️ IL EST SANS DANGER ICI POUR UNE RAISON PRÉCISE : `zoom` établit un bloc
     conteneur pour les descendants en `position: fixed`. Le menu mobile est en
     `position: absolute; top: 100%` dans l'en-tête, pas en `fixed` — vérifié
     le 26/08/2026. Y introduire un descendant `fixed` (un panneau plein écran,
     une modale) le dimensionnerait sur l'en-tête et non sur la fenêtre.

     Si le contre-zoom pose un jour problème, le repli est de surcharger les
     jetons de police dans les deux feuilles : le texte retrouve sa taille, mais
     la barre reste 15 % plus serrée qu'aujourd'hui. */
  --echelle-chrome: 1.176470588; /* 1 / 0.85 */
  /* ---------- Layout ---------- */
  --container-max: 80rem; /* 1280px — la grille de la maquette */
  /* Hauteur de l'en-tête collant. DEUX VALEURS, parce que l'en-tête vit dans
     son propre repère : `--h-entete-brute` est la cote telle qu'on l'écrit
     DANS l'en-tête (déjà contre-zoomé), `--h-entete` est sa hauteur RÉELLE à
     l'écran, celle que doit lire tout ce qui se cale dessous.

     Elle était recopiée en clair (4.25rem) dans trois fichiers — `.site-header__inner`,
     `--article-colle` et `--coeur-colle`. C'est justement l'exemption ci-dessus
     qui les faisait diverger : sans jeton, les deux `scroll-margin-top` auraient
     rétréci de 15 % pendant que l'en-tête gardait sa hauteur, et un clic dans un
     sommaire aurait atterri une dizaine de pixels SOUS la barre. */
  --h-entete-brute: 4.25rem;
  --h-entete: calc(var(--h-entete-brute) * var(--echelle-chrome));
  --container-pad: clamp(1.25rem, 0.5rem + 3vw, 3rem);
  /* Le hero dispose de sa propre largeur, plus généreuse que la grille du
     reste du site. Sans ça il est doublement figé : `--container-pad`
     plafonne à 1333px, donc la largeur de contenu se bloque à 1280px dès
     1376px de viewport — entre 1376 et 1920, pas un pixel ne bouge, quelle
     que soit l'échelle typographique. */
  --container-hero: 91rem; /* 1456px */
  /* ---------- Panneaux ----------
     Les grands cadres de la page — « Au cœur de Biron », « Une approche data
     unique » — partagent leur filet et les marges de leur bande de titre.
     Ces jetons existent pour que ce partage soit RÉEL : recopiés d'une
     feuille à l'autre, les deux valeurs divergent au premier réglage.

     Le filet est bleu depuis le 14/08/2026, et éclairci : à `--c-blue-400`
     pur il devient un trait franc qui se dispute la lecture avec le contenu
     du cadre. Dilué de blanc, il cadre sans parler. */
  --filet-panneau-teinte: color-mix(in srgb, var(--c-blue-400) 65%, var(--c-white));
  --filet-panneau: 1px solid var(--filet-panneau-teinte);
  /* La maquette donne 22px en haut comme en bas, ce qui étouffe le titre. On
     dégage nettement au-dessus et on garde le dessous plus court — la bande
     se lit alors comme un en-tête et non comme un bandeau serré. */
  --pad-panneau-entete-haut: clamp(var(--sp-8), 3vw, 3.5rem);
  --pad-panneau-entete-bas: clamp(var(--sp-5), 1.7vw, 2rem);
  --pad-panneau-entete-inline: clamp(var(--sp-6), 3.1vw, 3.625rem);
  /* ---------- La séquence des piliers ----------
     « Biron : ni un logiciel… » (sections/approche.njk) et « Au cœur de Biron »
     (sections/coeur.njk) se lisent comme une seule séquence depuis le
     09/09/2026. Elles ont partagé leur grille entière le 16/09/2026 — mêmes
     fractions, même gouttière, le texte de la première tombant sur la largeur du
     sommaire « Les quatre piliers » et son trio sur les cartes de piliers.

     ⚠️ CE PARTAGE A ÉTÉ DÉFAIT LE JOUR MÊME, pour les COLONNES seulement : la
     paire texte/trio de la première a été resserrée puis recentrée sous son
     titre, et des fractions `fr` remplissent tout le conteneur par construction
     — elles ne savent pas laisser du blanc de chaque côté. Ses colonnes sont
     donc dimensionnées par leur contenu (voir `.approche__grille`), et
     `--colonnes-piliers` n'a plus qu'un consommateur, `.coeur__suite`. Il reste
     un jeton plutôt qu'une cote écrite sur place : c'est la grille de la
     maquette, et le jour où une troisième section la reprend, le partage doit
     être RÉEL — recopiées d'une feuille à l'autre, les deux cotes divergeraient
     au premier réglage et rien ne signalerait le décalage.

     LA GOUTTIÈRE, ELLE, EST TOUJOURS PARTAGÉE, et volontairement : les deux
     sections se suivent, elles doivent respirer pareil. Les fractions sont
     celles de la maquette (504 / 778). La gouttière garde son alias
     `--coeur-ecart-cartes` dans _coeur.scss, où elle borne aussi le débord des
     badges et se resserre sous `lg` : c'est l'alias qui est redéfini là-bas,
     jamais ce jeton-ci. */
  --colonnes-piliers: minmax(0, 504fr) minmax(0, 778fr);
  --gouttiere-piliers: clamp(var(--sp-6), 2.4vw, 2.5rem);
  /* ---------- Formes ---------- */
  --radius-sm: 0.375rem;
  --radius-md: 0.5625rem; /* 9px — le radius Figma, coin circulaire */
  --radius-lg: 1rem;
  --radius-full: 62.4375rem;
  /* LE rayon des cadres du site : panneaux bordés, boîtes, captures. Il valait
     12 px chez « Au cœur de Biron », 15 px sur les cartes de témoignage,
     --radius-lg (16 px) chez « approche » et rien du tout ailleurs. Un seul
     jeton pour tous : 12 px, la cote des captures de la maquette. Tranché le
     13/08/2026.

     Il ne remplace pas --radius-sm ni --radius-btn-squircle, qui habillent des
     objets plus petits (menu, boutons) et suivent une autre échelle. */
  --radius-cadre: 0.75rem; /* 12px */
  /* Rayon des boutons quand le coin est une superellipse (squircle).
     Un coin adouci « mange » visuellement moins que l'arc de cercle : à
     rayon égal il paraît plus carré. On compense en montant la valeur.
     14px est le seuil où l'effet devient lisible sur un bouton de 36px
     sans que la forme bascule vers la pilule (18px). */
  --radius-btn-squircle: 0.875rem; /* 14px */
  /* Ombres portées en sable chaud et non en bleu-noir. Sur un fond crème,
     une ombre froide se lit comme une salissure sous l'objet ; une ombre
     chaude se lit comme une retombée de lumière. Tranché le 12/08/2026.

     La teinte est stockée en composantes pour qu'un seul endroit suffise à
     repeindre toutes les ombres du site.

     Les alphas sont nettement plus hauts qu'avec l'ancienne teinte : #c6baa9
     est clair, donc à opacité égale il assombrit environ deux fois moins.
     Ils restent volontairement sous la compensation complète — l'ombre doit
     rester douce, c'était tout l'objet du changement. */
  --shadow-tint: 198 186 169; /* #c6baa9 */
  --shadow-sm: 0 1px 2px rgb(var(--shadow-tint) / 0.18);
  --shadow-md: 0 4px 16px rgb(var(--shadow-tint) / 0.26);
  --shadow-lg: 0 24px 64px rgb(var(--shadow-tint) / 0.34);
  /* ---------- Mouvement ----------
     Ces durées sont mises à 0.01ms plus bas quand l'utilisateur a demandé
     à réduire les animations (voir _base.scss). */
  --dur-fast: 150ms;
  --dur-base: 280ms;
  /* Entrée au défilement (sections/_reveal.scss). Plus longue que
     --dur-base : c'est un mouvement qu'on regarde arriver, pas une réaction
     à un geste. */
  --dur-reveal: 520ms;
  /* Équivalents CSS des courbes GSAP employées dans le hero, pour que les
     transitions de survol et les animations d'entrée parlent le même
     langage. Auparavant `power3.out` (JS) et cubic-bezier(0.22,1,0.36,1)
     (CSS) cohabitaient sur les mêmes boutons. */
  --ease-out: cubic-bezier(0.215, 0.61, 0.355, 1); /* ≈ power3.out */
  --ease-in-out: cubic-bezier(0.645, 0.045, 0.355, 1); /* ≈ power3.inOut */
  --ease-editorial: cubic-bezier(0.16, 1, 0.3, 1); /* entrées longues */
  --z-header: 100;
  --z-overlay: 200;
  /* Au-dessus du menu mobile ouvert (--z-overlay) : la bannière de
     consentement doit rester joignable même si les deux se superposent un
     instant. Seul consommateur : --cc-z-index dans
     scss/sections/_consentement.scss. */
  --z-consentement: 300;
}

/* =========================================================================
   Contexte sombre.

   À poser sur n'importe quelle section au fond foncé. Les rôles sémantiques
   basculent en bloc et les composants suivent sans qu'on ait à écrire une
   seule règle spécifique — un bouton, un filet ou un texte secondaire
   posés là-dedans se repeignent tout seuls.
   ========================================================================= */
/* =========================================================================
   Bande desktop de l'échelle typographique.

   Elle reprend exactement là où la bande basse plafonne, à 1280. Sans elle,
   la page était typographiquement figée sur tout le desktop : entre 1280 et
   1920, seul le h1 changeait — de 0,03 px.
   ========================================================================= */
@media (min-width: 80rem) {
  :root {
    --fs-h1: clamp(2.875rem, -0.375rem + 4.0625vw, 4.5rem); /* 46→72 */
    --fs-eyebrow-lg: clamp(1.25rem, 0.75rem + 0.625vw, 1.5rem); /* 20→24 */
    --fs-lead: clamp(1.0625rem, 0.6875rem + 0.4688vw, 1.25rem); /* 17→20 */
  }
}
/* =========================================================================
   LE FOND DE PAGE BLEU — les pages éditorialisées.

   Posée sur `<body>` par layouts/base.njk quand une page déclare
   `fondPage: bleu`, et non sur `<main>` comme `.ton-page--*` : ici c'est le
   FOND qui change, et l'en-tête translucide comme les bandes de transition
   halftone naissent dessus. Sur `<main>` la page se serait dédoublée en deux
   teintes au niveau de l'en-tête.

   Une seule variable à réaffecter, et tout suit : les sections lisent
   --bg-page pour leur fond, `--halftone-fond` en hérite, `.clients__frame`
   aussi. `.on-dark` continue de la redéfinir localement, plus bas dans
   l'arbre — une boîte sombre reste sombre sur les deux fonds.

   Qui la porte : /cas-clients/ et ses études (src/cas-clients/), /blog/ et ses
   articles (src/blog/blog.json), /newsletter/ et ses éditions
   (src/newsletter/newsletter.json). Les fichiers de données de dossier font
   qu'une nouvelle étude, un nouvel article ou une nouvelle édition l'héritent
   sans rien déclarer.
   ========================================================================= */
.fond-page--bleu {
  --bg-page: var(--c-blue-50);
  --section-bg: var(--bg-page);
}

.on-dark {
  --section-bg: var(--c-blue-dark);
  --bg-page: var(--c-blue-dark);
  --text-strong: var(--c-white);
  --text-muted: var(--c-casper);
  --text-accent: var(--c-fluo);
  --border-subtle: rgba(255, 255, 255, 0.25);
  --border-strong: rgba(255, 255, 255, 0.45);
  color: var(--text-strong);
}

/* =========================================================================
   Ton de page — les accents d'une page entière à la couleur d'un pilier.

   Posée par layouts/base.njk sur `<main>` quand une page déclare `tonPage`
   dans sa fiche. Elle marche exactement comme `.on-dark` juste au-dessus : elle
   ne repeint pas des composants un par un, elle réaffecte les VARIABLES, et
   tout ce qui les lit suit sans qu'on écrive une règle de plus.

   ⚠️ SUR `<main>` ET NON SUR `<body>` : l'en-tête et le pied de page en sont
   les frères, donc ils gardent le bleu du site. C'est voulu — la barre de
   navigation est la même sur toutes les pages.

   ---------------------------------------------------------------------
   CE QUE LA CLASSE REPEINT, en réaffectant deux variables seulement :

     --text-accent   le surtitre d'un hero, les sous-titres en chasse fixe, les
                     puces, le marqueur par défaut d'un `item`, et la mise en
                     valeur `((accent))` du YAML (`.ton--accent`)
     --c-blue        le bouton principal (`.btn--primary`), les FORMES de marque
                     (`.forme--bleu`, qui lit --shape-color), et la mise en
                     valeur `((bleu))` du YAML (`.ton--bleu`)

   CE QU'ELLE NE TOUCHE PAS, ET POURQUOI CHAQUE FOIS :

     --c-blue-zodiac  c'est la couleur des TITRES (`.hero-page__titre`,
                      `.cadre__titre`) — Hugo a tranché le 18/08/2026 qu'ils
                      restent bleu nuit, comme dans la maquette. C'est aussi
                      l'encre du bouton fluo de l'appel et la base de
                      --c-blue-abysse, son dégradé de fond : la réaffecter
                      verdirait un libellé sur aplat fluo et un décor.
     --c-blue-dark    un FOND, pas un accent — les boîtes sombres et
                      `.forme--bleu-nuit`.
     --c-blue-400/100/50  des filets et des dégradés, dont --filet-panneau.
                      Un filet d'un cheveu n'est pas un accent.
     --c-fluo         déjà l'accent des contextes sombres. `.on-dark` étant plus
                      proche de l'élément, il l'emporte à l'intérieur d'une boîte
                      sombre : les accents y restent donc fluo, et c'est la
                      convention du site.

   ---------------------------------------------------------------------
   ⚠️ CHAQUE PILIER SE DÉCLARE À LA MAIN, ET CE N'EST PAS UN OUBLI.

   Les quatre --c-pilier-* n'ont pas la même luminance, et --c-blue sert de FOND
   au bouton principal avec un libellé blanc dessus. Les contrastes du blanc :

     dictionnaire  #00b3a4  3,08  ← accepté par Hugo pour un libellé gras 15,5px
     plateforme    #ff5d73  2,85  ← refusé, la teinte du bouton est ASSOMBRIE
     integrations  #f0a22e  2,12  ← inutilisable, seul pilier encore non déclaré
     connecteur    #8b5cff  4,14  ← refusé de peu, la teinte du bouton est ASSOMBRIE

   Les mettre dans une boucle Sass livrerait donc des boutons illisibles sans le
   moindre avertissement. Chacun demande son propre arbitrage — voir la table des
   contrastes en tête de sections/_texte-panneau.scss, qui a déjà dû trancher la
   même question pour les boîtes.

   ⚠️ LES TROIS PILIERS DÉCLARÉS POSENT LEUR TEINTE TELLE QUELLE sur --c-blue. Le
   dictionnaire s'en tient là (3,08 sous un libellé blanc, accepté). La plateforme
   et le connecteur ajoutent UNE seule surcharge, --btn-primary-bg, qui n'assombrit
   QUE l'aplat du bouton — le seul endroit du site où du blanc est posé sur
   --c-blue. Le reste de la page garde la teinte à pleine force. Ne pas généraliser
   ce fonçage à --c-blue en croyant simplifier : il éteindrait les formes de marque
   et les `((bleu))`.
   ========================================================================= */
.ton-page--dictionnaire {
  --text-accent: var(--c-pilier-dictionnaire);
  --c-blue: var(--c-pilier-dictionnaire);
  /* Le survol du bouton principal. Le couple d'origine va du bleu de marque au
     bleu d'action, plus saturé ; ici on assombrit vers le bleu nuit, ce qui
     remonte au passage le contraste du libellé blanc. */
  --c-blue-action: color-mix(
    in srgb,
    var(--c-pilier-dictionnaire) 82%,
    var(--c-blue-zodiac)
  );
}

/* ---------- Plateforme, ajouté le 19/08/2026 pour src/plateforme.md ----------

   LE ROSE DU PILIER EST POSÉ TEL QUEL SUR TOUTE LA PAGE — accents, formes de
   marque, marqueurs, `((bleu))`. Exactement comme le dictionnaire pose son vert.

   ⚠️ UNE SEULE EXCEPTION, ET ELLE EST NOMMÉE : L'APLAT DU BOUTON PRINCIPAL.

   C'est le seul endroit du site où du blanc est posé sur --c-blue. Partout
   ailleurs cette variable est de l'ENCRE, mesurée contre le crème :

     en encre  le rose donne 2,82 sur le crème — le même ordre de grandeur que le
               vert du dictionnaire (2,90), et le même arbitrage. Ces accents-là
               sont des surtitres en capitales, des puces et des numéros
               décoratifs (`aria-hidden`), jamais du texte qui porte de
               l'information. Le corps de texte reste sur --text-strong.

     en aplat  le rose ne donne que 2,85 sous un libellé BLANC — illisible. Il est
               donc mélangé au bleu nuit jusqu'à 72 %, ce qui porte le blanc à
               4,88.

   ⚠️ CE FONÇAGE PASSE PAR --btn-primary-bg ET NON PAR --c-blue. La première
   version réaffectait --c-blue lui-même : le bouton devenait lisible, mais les
   FORMES DE MARQUE et les mises en valeur `((bleu))` de toute la page fonçaient
   avec lui, sans aucune raison — elles ne portent pas de blanc. Hugo l'a corrigé
   le 19/08/2026 : « on ne met le rose foncé que si on écrit du texte sur le
   rose ». La variable de bouton est déclarée dans `.btn--primary`
   (_components.scss), qui explique aussi pourquoi son repli est dans le `var()`.

   Le mélange va vers --c-blue-zodiac et non vers le noir : c'est la couleur que le
   site emploie déjà pour assombrir une teinte de pilier sans la faire virer au gris
   (voir `--panneau-ton` dans sections/_texte-panneau.scss).

   ⚠️ NE PAS REMONTER LE 72 %. Chaque point de rose regagné retire du contraste au
   libellé : à 75 % on tombe à 4,59, à 78 % à 4,35 — sous le seuil. Si le rose du
   pilier change, c'est ce chiffre-là qu'il faut recalculer, pas ajuster à l'œil. */
.ton-page--plateforme {
  --text-accent: var(--c-pilier-plateforme);
  --c-blue: var(--c-pilier-plateforme);
  --btn-primary-bg: color-mix(
    in srgb,
    var(--c-pilier-plateforme) 72%,
    var(--c-blue-zodiac)
  );
  /* Le survol assombrit encore : 55 % de rose, soit 6,71 pour le libellé blanc.
     Le sens du mouvement est le même que sur le dictionnaire — on va vers le bleu
     nuit, jamais vers le clair, sinon le libellé blanc s'efface au survol. */
  --btn-primary-bg-hover: color-mix(
    in srgb,
    var(--c-pilier-plateforme) 55%,
    var(--c-blue-zodiac)
  );
}

/* ---------- Connecteur IA, ajouté le 19/08/2026 pour src/connecteur-ia.md ----------

   Même parti que la plateforme : LE VIOLET DU PILIER EST POSÉ TEL QUEL sur toute
   la page — accents, formes de marque, marqueurs, surtitres, `((bleu))` — et
   seul l'aplat du bouton principal est assombri.

   ⚠️ LE VIOLET EST LE CAS LE MOINS SÉVÈRE DES QUATRE, ET IL EST ASSOMBRI QUAND
   MÊME. Les contrastes mesurés :

     en encre  3,83 sur le crème. C'est le MEILLEUR des trois piliers déclarés
               (dictionnaire 2,90, plateforme 2,82), donc rien à arbitrer : ces
               accents sont des surtitres en capitales, des puces et des numéros
               décoratifs, jamais du texte qui porte de l'information.

     en aplat  4,14 sous un libellé BLANC. Il manque 0,36 au seuil de 4,5 — assez
               peu pour tenter de laisser passer, et c'est exactement pour ça que
               le chiffre est écrit ici. Un fonçage à 90 % de violet le porte à
               4,72, et le violet reste parfaitement reconnaissable à ce taux.

   C'est LE MÊME calcul que `--panneau-ton` dans sections/_texte-panneau.scss, qui
   avait déjà dû trancher pour ce violet-là — la boîte « connecteur » de
   `texte-panneau` est le seul pilier des quatre à passer en `.on-dark`. Les deux
   endroits doivent rester d'accord : si le violet du pilier change, ce sont ces
   deux 90 % qu'il faut recalculer, pas ajuster à l'œil.

   ⚠️ LE FONÇAGE PASSE PAR --btn-primary-bg ET NON PAR --c-blue. La faute a déjà
   été commise sur la plateforme le 19/08/2026 : réaffecter --c-blue rend le bouton
   lisible mais éteint TOUTES les formes de marque et les mises en valeur
   `((bleu))` de la page, sans aucun signal. Règle d'Hugo : « on ne met le foncé
   que si on écrit du texte dessus. » */
.ton-page--connecteur {
  --text-accent: var(--c-pilier-connecteur);
  --c-blue: var(--c-pilier-connecteur);
  --btn-primary-bg: color-mix(
    in srgb,
    var(--c-pilier-connecteur) 90%,
    var(--c-blue-zodiac)
  );
  /* Le survol assombrit encore : 70 % de violet, soit 6,32 pour le libellé blanc.
     Le sens du mouvement est celui des deux autres piliers — on va vers le bleu
     nuit, jamais vers le clair, sinon le libellé blanc s'efface au survol. */
  --btn-primary-bg-hover: color-mix(
    in srgb,
    var(--c-pilier-connecteur) 70%,
    var(--c-blue-zodiac)
  );
}

/* ---------- Intégrations, ajouté le 20/08/2026 pour src/excel-sheets.md ----------

   ⚠️ C'EST LE SEUL PILIER DONT L'ENCRE ELLE-MÊME EST ASSOMBRIE, et c'est une
   EXCEPTION à la règle 10 de MAQUETTE.md — « une teinte de pilier est de l'encre,
   pas un aplat de bouton », qui veut que la teinte soit posée telle quelle et que
   seul l'aplat du bouton fonce. Les trois autres piliers la suivent.

   La règle ne tient pas ici, et c'est mesuré : #f0a22e ne donne que 2,01 sur le
   crème, contre 2,90 pour le vert du dictionnaire et 2,82 pour le rose de la
   plateforme — les deux niveaux qu'Hugo a acceptés. La poser telle quelle
   reviendrait à descendre d'un tiers sous ce qu'il a validé ailleurs.

   L'exception a été tranchée par Hugo le 20/08/2026. La maquette fonçait déjà l'orange
   de son côté (#c6820f pour tous ses accents), mais Hugo a écarté cette valeur le jour
   même — « maronasse » — et l'encre retenue fait tourner la teinte vers le rouge au
   lieu de seulement l'assombrir : #cc6207. Le raisonnement complet est sur
   --c-pilier-integrations-encre, en tête de fichier.

   ⚠️ L'ORANGE DU PILIER RESTE EMPLOYÉ TEL QUEL EN APLAT — c'est le cas du
   placeholder de la section 03, où le texte sombre donne 6,71. Les deux valeurs ne
   se remplacent pas l'une l'autre : la claire est un fond, la foncée est une encre.

   Contrastes de l'encre retenue : 3,74 sur le crème. Sous un libellé blanc elle ne
   donne que 3,95, donc l'aplat du bouton est foncé lui aussi. Les chiffres sont sur
   la règle elle-même. */
.ton-page--integrations {
  /* ⚠️ LES DEUX RÔLES NE PRENNENT PAS LA MÊME VALEUR ICI, contrairement aux trois
     autres piliers qui posent la même teinte sur les deux. C'est demandé par Hugo le
     20/08/2026 — « les formes sur la page doivent être dans l'orange original » — et
     c'est cohérent avec ce que chaque rôle porte :

       --text-accent  du TEXTE : surtitres, marqueurs, mentions en chasse fixe. Il lui
                      faut du contraste, donc l'encre foncée (3,74 sur le crème).
       --c-blue       de l'ORNEMENT : les formes de marque du décor de hero, les
                      `((bleu))`. Rien n'y est à lire, donc l'orange du pilier à
                      pleine force — le foncer n'apportait aucune lisibilité et
                      éteignait le décor.

     ⚠️ CONSÉQUENCE À CONNAÎTRE : `((bleu))` et `((accent))` ne donnent plus la même
     couleur sur cette page. C'est voulu — le premier est une mise en valeur
     décorative, le second une encre — mais aucune autre page pilier ne fait ça. */
  --text-accent: var(--c-pilier-integrations-encre);
  --c-blue: var(--c-pilier-integrations);
  /* ⚠️ L'APLAT DU BOUTON PART DE L'ENCRE, PAS DE --c-blue, et c'est la seule règle de
     ce fichier dans ce cas. Ailleurs les deux sont égaux, donc la question ne se pose
     pas. Ici --c-blue vaut l'orange clair : l'employer comme base donnerait un aplat
     à foncer de moitié, qui perdrait la teinte. On part donc de l'encre déjà tournée
     vers le rouge, et 88 % suffisent à porter le blanc de 3,95 à 4,74.

     Ne pas remonter ce chiffre : à 95 % on tombe à 4,26, sous le seuil de 4,5. */
  --btn-primary-bg: color-mix(
    in srgb,
    var(--c-pilier-integrations-encre) 88%,
    var(--c-blue-zodiac)
  );
  /* Le survol assombrit encore : 70 % de l'encre, soit 6,33 pour le libellé blanc.
     Même sens que les trois autres piliers — on va vers le bleu nuit, jamais vers le
     clair, sinon le libellé blanc s'efface au survol. */
  --btn-primary-bg-hover: color-mix(
    in srgb,
    var(--c-pilier-integrations-encre) 70%,
    var(--c-blue-zodiac)
  );
}

/* Un masque CSS et son équivalent -webkit- : Safari ne supporte encore que
   la version préfixée de `mask-*`. Sans ce mixin, chaque forme masquée à une
   seule image recopiait les six déclarations à la main.

   Ne couvre PAS les masques à plusieurs couches (`mask-composite`) : les
   mots-clés de composition ne sont pas les mêmes entre la version standard
   et la version -webkit-, les faire cohabiter casse la composition. Ces
   blocs-là restent écrits à la main — voir le commentaire de
   `.texte-illustre__grille` dans sections/_texte-illustre.scss. */
/* =========================================================================
   Reset, polices et styles de base.
   ========================================================================= */
/* ---------- Polices ---------- */
/* Inter et Chivo Mono sont des polices libres, servies en local depuis
   node_modules (voir eleventy.config.js) plutôt que depuis un CDN, qui
   poserait un problème RGPD. Ce sont des fontes variables : un seul fichier
   couvre toutes les graisses.

   Inter a remplacé Figtree le 11/08/2026 : très grande hauteur d'x,
   ajustements optiques poussés, différenciation nette du l, du I et du 1 —
   ce qui manquait à Figtree. */
@font-face {
  font-family: "Inter Variable";
  src: url("/assets/fonts/inter-variable.woff2") format("woff2-variations");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}
/* Chivo Mono a remplacé JetBrains Mono le 11/08/2026. Contrainte de marque :
   le zéro ne doit avoir ni point ni barre. JetBrains Mono a un zéro pointé —
   comme 16 des 19 polices monospace libres testées, le zéro marqué étant la
   norme du genre, hérité des besoins du code. */
@font-face {
  font-family: "Chivo Mono Variable";
  src: url("/assets/fonts/chivo-mono-variable.woff2") format("woff2-variations");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}
/* Candidates pour les titres, en remplacement de Recoleta (13/08/2026).
   Toutes des fontes variables libres : un seul fichier par police couvre
   toutes les graisses. On bascule de l'une à l'autre en changeant
   --font-titre dans _tokens.scss ; les quatre restent déclarées ici, seule
   celle qui est appelée est téléchargée par le navigateur. */
@font-face {
  font-family: "Rubik Variable";
  src: url("/assets/fonts/rubik-variable.woff2") format("woff2-variations");
  font-weight: 300 900;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Sora Variable";
  src: url("/assets/fonts/sora-variable.woff2") format("woff2-variations");
  font-weight: 100 800;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Space Grotesk Variable";
  src: url("/assets/fonts/space-grotesk-variable.woff2") format("woff2-variations");
  font-weight: 300 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Plus Jakarta Sans Variable";
  src: url("/assets/fonts/plus-jakarta-sans-variable.woff2") format("woff2-variations");
  font-weight: 200 800;
  font-style: normal;
  font-display: swap;
}
/* Recoleta est une police commerciale (Latinotype). La licence webfont
   n'est pas encore acquise : les .woff2 sont donc absents du dépôt.

   `local()` est déclaré en premier — si la police est installée sur la
   machine (c'est le cas de tout poste qui ouvre la maquette Figma), le
   navigateur l'utilise directement et le rendu local est exact, sans
   requête réseau ni fichier à fournir.

   Le jour où la licence est acquise : déposer recoleta-semibold.woff2 et
   recoleta-bold.woff2 dans src/assets/fonts/ et il n'y a rien d'autre à
   faire. En attendant, les visiteurs voient le fallback (Georgia). */
@font-face {
  font-family: "Recoleta";
  src: local("Recoleta SemiBold"), local("Recoleta-SemiBold"), url("/assets/fonts/recoleta-semibold.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Recoleta";
  src: local("Recoleta Bold"), local("Recoleta-Bold"), url("/assets/fonts/recoleta-bold.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}
/* ---------- Reset ---------- */
*,
*::before,
*::after {
  box-sizing: border-box;
}

* {
  margin: 0;
}

/* ⚠️ L'ÉCHELLE DU SITE EST À 85 %, ET C'EST CETTE LIGNE QUI LA PORTE.
   Demande d'Hugo, 26/08/2026 : « toutes les polices sont trop grosses, on
   voudrait qu'on ait le même aspect qu'en mode 80 % de zoom sur le
   navigateur » — tranché à 85 % pour ne pas descendre le texte courant sous
   le seuil des légendes (voir plus bas).

   ⚠️ CE N'EST PAS UNE RÉDUCTION DES POLICES, c'est une réduction de TOUT, et
   c'est là qu'est le raisonnement. Un zoom navigateur ne touche pas la
   typographie : il réduit polices, marges, gouttières, largeur de conteneur et
   rayons du même coup, et c'est ce qui fait que le résultat reste harmonieux.
   N'avoir baissé que l'échelle typographique aurait laissé les espacements
   calibrés pour l'ancienne taille — la page se creuse de trous, les boutons
   deviennent trop capitonnés autour de leur label, et surtout la MESURE
   s'allonge : le conteneur restant à 1280 px, les lignes passent d'environ 65
   à 76 caractères et il faut reprendre à la main tous les `max-width` du site.

   Tout le CSS du site est écrit en `rem` (les `px` qu'on trouve dans les
   feuilles sont presque tous des commentaires qui annotent une valeur en rem),
   donc une seule déclaration ici déplace l'ensemble en conservant EXACTEMENT
   toutes les proportions et toutes les mesures.

   ⚠️ EN POURCENTAGE ET JAMAIS EN PX : `font-size: 13.6px` écraserait la taille
   par défaut d'un visiteur qui a agrandi sa police dans son navigateur ; un
   pourcentage la respecte, puisqu'il s'applique à elle.

   Trois conséquences à connaître :

     • L'EN-TÊTE ET LE PIED DE PAGE EN SONT EXEMPTÉS (Hugo, même jour), par un
       contre-zoom déclaré sur eux — voir `--echelle-chrome` dans _tokens.scss
       et `.site-header` dans _components.scss.

     • LES MÉDIA QUERIES NE BOUGENT PAS. Un `em`/`rem` dans une media query se
       résout toujours contre la taille INITIALE (16 px), jamais contre celle de
       la racine : les points de rupture restent donc à leur position actuelle
       en viewport. C'est une différence avec un vrai zoom navigateur, et elle
       joue dans le bon sens — la mise en page mobile ne se déclenche pas plus
       tard qu'avant.

     • LES PENTES DES `clamp()` NE SUIVENT PAS. L'échelle fluide mélange `rem`
       et `vw`, et la moitié `vw` est relative au viewport, pas à la racine :
       les plafonds sont donc atteints un peu plus tôt. Sur desktop tout est
       déjà au plafond, donc aucun effet ; entre ~700 et 1280 px le texte est
       proportionnellement un peu plus gros que la cible. Recalculer les pentes
       si ça se voit — ne pas croire à un bug. */
html {
  font-size: 85%;
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  min-height: 100svh;
  background-color: var(--bg-page);
  color: var(--text-strong);
  font-family: var(--font-sans);
  font-size: var(--fs-base);
  font-weight: var(--fw-regular);
  line-height: var(--lh-normal);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

img,
picture,
svg,
video,
canvas {
  display: block;
  max-width: 100%;
  height: auto;
}

input,
button,
textarea,
select {
  font: inherit;
  color: inherit;
}

button {
  background: none;
  border: none;
  cursor: pointer;
}

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

a {
  color: inherit;
  text-decoration: none;
}

/* Un focus visible et cohérent partout. On ne le retire jamais : c'est le
   seul repère des personnes qui naviguent au clavier. */
:focus-visible {
  outline: 2px solid var(--c-blue);
  outline-offset: 3px;
  border-radius: var(--radius-sm);
}

/* ---------- Typographie ---------- */
h1,
h2,
h3,
h4 {
  font-family: var(--font-serif);
  font-weight: var(--fw-bold);
  line-height: var(--lh-tight);
  letter-spacing: var(--ls-tight);
  text-wrap: balance;
}

h1 {
  font-size: var(--fs-h1);
  letter-spacing: var(--ls-display);
}

h2 {
  font-size: var(--fs-h2);
}

h3 {
  font-size: var(--fs-h3);
}

p {
  text-wrap: pretty;
}

/* ---------- Primitives de layout ---------- */
.container {
  width: 100%;
  max-width: calc(var(--container-max) + var(--container-pad) * 2);
  margin-inline: auto;
  padding-inline: var(--container-pad);
}

/* Réservé aux lecteurs d'écran : invisible à l'œil, lu à la voix. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Lien d'évitement : première tabulation de la page, permet de sauter la
   navigation pour aller droit au contenu. */
.skip-link {
  position: absolute;
  top: var(--sp-2);
  left: var(--sp-2);
  z-index: var(--z-overlay);
  padding: var(--sp-3) var(--sp-5);
  background-color: var(--c-blue);
  color: var(--c-white);
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
  border-radius: var(--radius-md);
  transform: translateY(-150%);
  transition: transform var(--dur-fast) var(--ease-out);
}

.skip-link:focus-visible {
  transform: translateY(0);
}

/* ---------- Mouvement réduit ----------
   Quand l'utilisateur a activé « réduire les animations » dans son OS, on
   neutralise durées et déplacements. Le contenu reste intégralement
   visible : les éléments animés à l'apparition sont remis à leur état
   final plutôt que masqués. */
@media (prefers-reduced-motion: reduce) {
  :root {
    --dur-fast: 0.01ms;
    --dur-base: 0.01ms;
    --dur-slow: 0.01ms;
    --dur-reveal: 0.01ms;
  }
  html {
    scroll-behavior: auto;
  }
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    /* ⚠️ LE DÉLAI AUSSI, et il manquait — ajouté le 18/08/2026. Neutraliser la
       seule DURÉE ne suffit pas dès qu'une animation est échelonnée : chaque
       élément s'exécutait instantanément, mais toujours après son délai, et le
       contenu apparaissait donc encore en cascade. C'est exactement ce que ce
       bloc dit vouloir éviter. Le premier cas est le décalage des cartes de
       connecteurs (sections/_connecteurs.scss). */
    animation-delay: 0s !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}
/* =========================================================================
   Composants réutilisables : boutons, eyebrows, en-tête.
   ========================================================================= */
/* ---------- Boutons ----------
   Cotes reprises de la maquette : 36px de haut, 26px de padding latéral,
   Figtree SemiBold 15.5px, rayon 9px. C'est ce rapport — bas et large —
   qui donne leur allure allongée.

   Le gabarit visuel reste à 36px, y compris sur mobile. La zone cliquable
   est étendue à 44px par un pseudo-élément (voir plus bas) : la cible est
   confortable au pouce sans que le bouton grossisse à l'écran. */
.btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  min-height: 2.25rem;
  padding: 0 1.625rem;
  font-family: var(--font-sans);
  font-size: var(--fs-btn);
  font-weight: var(--fw-semibold);
  line-height: 1.5;
  text-align: center;
  white-space: nowrap;
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  transition: background-color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out), transform var(--dur-fast) var(--ease-out);
}

/* Coin adouci — l'équivalent CSS du « corner smoothing » de Figma.
   Un border-radius classique dessine un arc de cercle ; Figma dessine une
   superellipse, dont la courbure démarre plus tôt et se raccorde au côté
   sans cassure. C'est ce raccord qui donne l'allure « squircle ».

   Supporté par Chrome et Edge aujourd'hui. Ailleurs, le bouton garde le
   coin arrondi de 9px de la maquette — la dégradation est invisible pour
   qui ne compare pas les deux côte à côte.

   ⚠️ NE PAS PASSER LES BOUTONS AUX COINS CARRÉS. Essayé le 18/08/2026 pour les
   aligner sur les panneaux (`.cadre`, border-radius: 0), et REFUSÉ par Hugo le
   jour même : le squircle est voulu. Ce bloc s'applique après `.btn` et
   l'écrase, donc quiconque met `border-radius: 0` plus haut sans toucher ici
   n'obtiendra un coin carré que sur Firefox et Safari — un écart entre
   navigateurs, jamais le changement visé. */
@supports (corner-shape: squircle) {
  .btn {
    border-radius: var(--radius-btn-squircle);
    corner-shape: squircle;
  }
}
/* Zone tactile de 44px, centrée sur le bouton et invisible.

   ⚠️ CE PSEUDO EST ÉCRASÉ PAR `.lien-couvrant`, juste en dessous. Qui le modifie
   doit y aller voir : c'est le seul endroit du site qui reprend ce `::after` à son
   compte. */
.btn::after {
  content: "";
  position: absolute;
  inset: 50% 0 auto;
  height: 2.75rem;
  transform: translateY(-50%);
}

/* ---------- Le clic pleine carte ----------
   Le couple `.carte-cliquable` (sur la carte) + `.lien-couvrant` (sur son CTA)
   étend la zone de clic du bouton à toute la carte. Demandé par Céline le
   28/08/2026 — que le clic ouvre le cas « n'importe où sur l'encart » — d'abord
   pour /cas-clients/, puis pour la grille d'articles du blog le lendemain de la
   même demande. Deux hôtes : le couple est donc ici et non dans l'une des deux
   feuilles de section, comme `carte-etiquettes` et `.trio` avant lui.

   ⚠️ UN SEUL LIEN DANS LE DOM, ET C'EST TOUT L'ENJEU. La règle 4 de MAQUETTE.md a
   fait perdre leur clic pleine carte à ces deux grilles le 21/08/2026, quand leur
   CTA de pied est devenu un bouton : ce qu'elle bannit, ce sont DEUX liens vers la
   même page dans une même carte — c'est exactement le défaut que la grille du blog
   portait alors. On ne les remet pas. Le procédé est celui de la « rangée qui EST
   le lien » (`.edition__lien`, l'archive de la newsletter) appliqué à une carte :
   le lien reste unique, c'est sa SURFACE qui s'étend. Aucun arrêt de tabulation
   n'est ajouté, et le focus continue de tomber sur le bouton, qui porte son anneau.

   ⚠️ LE CALQUE ÉCRASE DÉLIBÉRÉMENT `.btn::after`, la zone tactile de 44px. Sans
   perte : il la remplace par une surface strictement plus grande. Le sélecteur est
   à (0,2,1) contre (0,1,1) — il gagne donc par la SPÉCIFICITÉ et non par l'ordre
   des règles, qui suffirait ici mais ne se verrait pas. C'est la leçon du piège de
   `carte-etiquettes` : quand on écrase la primitive d'un atome partagé, on
   l'écrase explicitement.

   ⚠️ D'OÙ `position: static` SUR LE LIEN. `.btn` est `position: relative`, donc
   son pseudo se résoudrait sur le bouton et ne couvrirait que lui. Le remettre en
   flux fait porter le calque par le premier ancêtre positionné, que
   `.carte-cliquable` garantit être la CARTE. Sans cette classe-là, l'ancêtre
   suivant est la section (`.cas-clients` et `.grille-articles` sont toutes deux
   `position: relative`) : le calque couvrirait la section entière et la première
   carte volerait le clic de toutes les autres. Les deux classes vont ensemble ;
   l'une sans l'autre ne fait rien de bon.

   ⚠️ `z-index: 1` N'EST PAS DÉCORATIF. Un élément positionné se peint au-dessus du
   contenu en flux, donc le calque passe déjà devant le texte et le cadre en
   attente — mais pas devant un descendant qui se positionnerait ou porterait un
   `transform`, lequel se peindrait par-dessus et avalerait le clic sans qu'aucun
   message ne le signale. C'est le piège documenté au long dans `.item__pastille`
   et retrouvé sur `orbite` : le symptôme ne désigne jamais la cause. Le rang le
   ferme d'avance.

   ⚠️ ET IL INTERDIT TOUT AUTRE ÉLÉMENT INTERACTIF DANS LA CARTE : un second lien
   y serait recouvert, donc inatteignable à la souris, en silence. C'est la
   condition à vérifier avant d'ajouter quoi que ce soit dedans.

   Ce que ça coûte, et c'est le prix connu du procédé : le texte de la carte n'est
   plus sélectionnable. Accepté comme sur les rangées de la newsletter — une carte
   de cas client ou d'article est une porte d'entrée, pas un texte qu'on cite. */
.carte-cliquable {
  position: relative;
}

.carte-cliquable .lien-couvrant {
  position: static;
}

.carte-cliquable .lien-couvrant::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  /* Les deux cotes de `.btn::after` sont remises à leur valeur initiale : sur
     `inset: 0`, une hauteur fixe et un décalage vertical ne veulent plus rien
     dire. */
  height: auto;
  transform: none;
}

/* ⚠️ ET L'ENFONCEMENT DE `.btn:active` EST ANNULÉ SUR CE LIEN. C'EST LA LIGNE
   SANS LAQUELLE RIEN NE MARCHE, et le défaut qu'elle corrige mérite d'être connu :
   il a été livré le 28/08/2026 et rapporté le lendemain — « je vois bien le hover
   et en bas à gauche la détection du lien mais quand je clique, le lien ne s'ouvre
   pas ».

   LE MÉCANISME. `.btn:active` pose `transform: translateY(1px)`. Or **tout
   `transform` non nul fait de l'élément le BLOC CONTENEUR de ses descendants
   absolus**, son propre `::after` compris. Au moment où le bouton de la souris
   s'enfonce, le calque cesse donc de se résoudre sur la carte et se replie
   instantanément sur le BOUTON : le `mousedown` est parti sur le lien, le `mouseup`
   tombe sur le cadre visuel, et **aucun clic n'est jamais constitué**. Le survol,
   lui, n'engage pas `:active` : l'URL s'affiche dans la barre d'état, tout a l'air
   normal, et le lien ne s'ouvre pas.

   ⚠️ LE SYMPTÔME NE DÉSIGNE PAS LA CAUSE, une fois de plus — et il envoie droit
   vers le cache du navigateur ou vers un `preventDefault()` qui n'existe pas. Le
   test qui tranche en trois secondes, dans la console : relever
   `document.elementFromPoint(x, y)` au repos, puis avec `transform` posé à la main
   sur le lien. Si la cible change, c'est ça.

   ⚠️ ET AUCUN SUBSTITUT N'EST POSÉ : `translate`, `rotate`, `scale` et
   `perspective` créent EXACTEMENT le même bloc conteneur que `transform`. Un
   enfoncement de remplacement écrit avec l'un d'eux rouvrirait le défaut à
   l'identique. Si un retour d'appui est demandé un jour, il doit passer par une
   propriété qui ne crée pas de bloc conteneur — la couleur de fond, par exemple.

   Ce qu'on perd est mince et la carte le rend au centuple : l'enfoncement de 1px
   était calibré pour une cible de la taille d'un bouton, alors que c'est toute la
   carte qui répond ici, avec sa teinte, son filet, son soulèvement et son zoom. */
.carte-cliquable .lien-couvrant:active {
  transform: none;
}

.btn:active {
  transform: translateY(1px);
}

/* CTA principal : bleu de marque au repos, bleu d'action au survol.

   ---------------------------------------------------------------------
   ⚠️ L'APLAT PASSE PAR SES PROPRES VARIABLES, ET C'EST LE SEUL ENDROIT DU SITE
   OÙ DU BLANC EST POSÉ SUR --c-blue.

   Partout ailleurs --c-blue est de l'ENCRE (`.forme--bleu`, `.ton--bleu`, le
   survol de `.btn--ghost`) ou une teinte de marqueur : sa lisibilité s'y mesure
   contre le crème, pas contre du blanc. Ici c'est un FOND, avec un libellé blanc
   dessus — donc la contrainte de contraste est inverse, et elle est propre à ce
   bouton.

   `.ton-page--*` peut donc assombrir CE fond sans toucher à l'encre de la page :
   c'est ce que fait `.ton-page--plateforme`, dont le rose est parfait en encre
   (2,82 sur le crème, comme le vert du dictionnaire) et illisible en aplat sous
   du blanc (2,85). Hugo l'a tranché ainsi le 19/08/2026 : « on ne met le rose
   foncé que si on écrit du texte sur le rose ».

   ⚠️ NE PAS REVENIR À `var(--c-blue)` EN DUR ICI. C'était le cas avant, et la
   seule façon d'assombrir le bouton était alors de réaffecter --c-blue pour
   toute la page — ce qui fonçait du même coup les formes de marque et les mises
   en valeur `((bleu))`, qui n'ont aucune raison de l'être.

   ⚠️ LA VALEUR PAR DÉFAUT EST DANS LE `var()`, PAS DÉCLARÉE SUR `.btn--primary`.
   Ce n'est pas un raccourci d'écriture : une propriété personnalisée posée SUR
   l'élément l'emporte sur la même propriété héritée d'un ancêtre. Un
   `--btn-primary-bg: var(--c-blue)` écrit ici masquerait donc celui que
   `.ton-page--*` pose sur `<main>`, et le bouton resterait bleu sans que rien ne
   le signale. Le repli en second argument, lui, ne s'applique QUE si aucun
   ancêtre n'a défini la variable. */
.btn--primary {
  background-color: var(--btn-primary-bg, var(--c-blue));
  border-color: var(--btn-primary-bg, var(--c-blue));
  color: var(--c-white);
}

.btn--primary:hover {
  background-color: var(--btn-primary-bg-hover, var(--c-blue-action));
  border-color: var(--btn-primary-bg-hover, var(--c-blue-action));
}

/* CTA de navigation : fond de la section, contour de la couleur du texte.
   `currentColor` fait suivre le filet automatiquement — sur une section
   sombre où le texte passe en blanc, le contour passe en blanc aussi. */
.btn--secondary {
  /* Survol par défaut — contexte clair : aplat fluo, filet d'origine
     conservé. C'est le filet sombre qui redessine le bouton par-dessus le
     fluo, et c'est ce qui fait tenir la variante. */
  --btn-hover-bg: var(--c-fluo);
  --btn-hover-border: currentColor;
  --btn-hover-color: var(--c-text-main);
  background-color: var(--section-bg);
  border-width: 2px;
  border-color: currentColor;
  color: var(--text-strong);
}

/* Contexte sombre : l'aplat fluo écraserait la section. On passe à un
   voile de 25% et c'est le filet qui prend le fluo. Le texte reste blanc. */
.on-dark .btn--secondary {
  --btn-hover-bg: color-mix(in srgb, var(--c-fluo) 25%, var(--section-bg));
  --btn-hover-border: var(--c-fluo);
  --btn-hover-color: var(--text-strong);
}

.btn--secondary:hover {
  background-color: var(--btn-hover-bg);
  border-color: var(--btn-hover-border);
  color: var(--btn-hover-color);
}

/* ---------- Le bouton fluo ----------
   ⚠️ IL AVAIT ÉTÉ RETIRÉ LE 13/08/2026, faute d'appelant : le seul bouton fluo du
   site était alors celui du bloc « appel », qui redéfinit ses couleurs lui-même.
   Il revient le 29/09/2026, demandé par Xavier pour le CTA du hero de /tarifs/ —
   et cette fois il a sa place dans les primitives plutôt que dans une feuille de
   section, parce qu'il y a maintenant TROIS boutons fluo sur le site : celui-ci,
   `.appel__cta` et le bouton d'envoi de `partials/inscription.njk`.

   ⚠️ LES DEUX AUTRES PORTENT ENCORE LEUR PROPRE COPIE de ce couple, chacun dans sa
   feuille. C'est le troisième exemplaire, donc le signal de les faire passer ici —
   pas fait aujourd'hui pour ne pas toucher `appel`, qui ferme presque toutes les
   pages du site, au milieu d'une revue. Les valeurs sont identiques au jeton près :
   si l'une des trois bouge, les trois doivent bouger.

   L'APLAT EST `--c-starship` ET NON `--c-fluo`, et la distinction est dans les
   jetons : `--c-fluo` (#d1ff00) est l'accent — encre, trames, formes, pastille de
   `.badge-section` — tandis que `--c-starship` (#d4f24a) est « l'accent fluo
   adouci », celui que le site pose en grand aplat sous un libellé. C'est lui que
   portent les deux boutons existants.

   ⚠️ LE SURVOL NE REPREND PAS CELUI DE `.appel__cta`, qui passe au BLANC : ce geste
   est calibré sur le bleu nuit, où le blanc est une vraie montée. Ici le bouton est
   posé sur le crème, où blanc sur crème ne se distingue presque pas (#ffffff contre
   #f9f9f5) — le bouton paraîtrait s'éteindre. Il monte donc vers `--c-fluo`, l'accent
   à pleine saturation : aucune valeur nouvelle, et la montée se lit sur les deux
   fonds. L'encre ne bouge pas, elle gagne même du contraste.

   `:hover` nu et non `mix.hover-hover`, comme `.btn--secondary` et `.btn--ghost`
   juste au-dessus : c'est la convention de la famille `.btn` dans ce fichier, et un
   survol qui reste collé après un appui tactile est ici sans conséquence — les deux
   états sont fluo. */
.btn--fluo {
  color: var(--c-blue-zodiac);
  background-color: var(--c-starship);
  border-color: var(--c-starship);
}

.btn--fluo:hover {
  background-color: var(--c-fluo);
  border-color: var(--c-fluo);
}

.btn--ghost {
  color: var(--text-strong);
  padding-inline: var(--sp-3);
}

.btn--ghost:hover {
  color: var(--c-blue);
}

/* La flèche des boutons « Découvrir » avance légèrement au survol. */
.btn__arrow {
  transition: transform var(--dur-fast) var(--ease-out);
}

.btn:hover .btn__arrow {
  transform: translateX(3px);
}

/* ---------- Groupe de boutons ----------
   Deux CTA qui vont ensemble. En ligne tant qu'il y a la place, empilés
   ensuite — et une fois empilés ils prennent tous la largeur du plus
   large. Deux boutons superposés de largeurs différentes donnent une
   arête irrégulière qui se voit immédiatement.

   `max-content` sur la colonne de grille la dimensionne sur le plus large
   des enfants ; `min(100%, …)` empêche le débordement sur écran étroit. */
.btn-group {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--sp-3);
}

/* ⚠️ LE SÉLECTEUR EST DÉDOUBLÉ, ET C'EST STRUCTUREL. Un bloc hôte qui redéclare
   `display` sur le même élément — `.hero-page__actions` le fait, pour poser sa
   propre gouttière — est à spécificité égale (0,1,0) et écrit dans un partial
   `@use` APRÈS celui-ci : il reprenait donc le `display: flex`, la grille ne
   s'appliquait pas, et les deux boutons empilés gardaient chacun sa largeur.
   Une media query ne change pas la spécificité, donc rien ne le signalait —
   défaut constaté sur le hero de /produit/ le 27/08/2026. Le sélecteur composé
   passe à (0,2,0) et l'emporte où que la règle hôte soit écrite ; c'est le même
   remède que `.archi__fiche-valeur--ok` dans sections/_hero-page.scss.

   La règle reste entièrement dans le `mq-down` : au-dessus de `xs` la mise en
   ligne du desktop n'est pas touchée. */
@media (max-width: 30rem) {
  .btn-group.btn-group {
    display: grid;
    grid-template-columns: min(100%, max-content);
    justify-content: center;
  }
}
/* ---------- Eyebrow ----------
   Le petit label au-dessus des titres. Deux déclinaisons dans la maquette :
   en mono capitales (labels de section) et en sans (accroche du hero). */
.eyebrow {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  font-weight: var(--fw-regular);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--text-muted);
}

/* Accroche du hero : Figtree Medium 20px, bleu profond. */
.eyebrow--sans {
  font-family: var(--font-sans);
  font-size: var(--fs-eyebrow-lg);
  font-weight: var(--fw-medium);
  line-height: 1.4;
  letter-spacing: 0;
  text-transform: none;
  color: var(--c-blue-dark);
}

/* ---------- Mise en valeur au fil du texte ----------
   Posées par les marques `**gras**` et `((couleur))` des fichiers de contenu,
   converties par lib/typographie.js. Voir CONTENU.md pour l'usage.

   Le gras passe par <strong>, dont la graisse est réglée ici plutôt que laissée
   au navigateur : celui-ci applique 700, et Inter y devient trop lourd au
   milieu d'un texte courant. 600 suffit à détacher le mot. */
strong {
  font-weight: var(--fw-semibold);
}

/* `.ton` ne porte aucune couleur : c'est son modificateur qui colore. Un ton mal
   orthographié ne peut donc pas rendre un texte invisible — il laisse d'ailleurs
   ses marques en clair à l'écran, voir lib/typographie.js. */
/* Suit le fond : bleu sur clair, fluo sur sombre — c'est --text-accent qui
   bascule, dans le bloc `.on-dark` de _tokens.scss. */
.ton--accent {
  color: var(--text-accent);
}

.ton--bleu {
  color: var(--c-blue);
}

.ton--fluo {
  color: var(--c-fluo);
}

/* ---------- Lien au fil du texte ----------
   La marque `[[texte|/chemin/]]` de lib/typographie.js. C'est le cas que la
   règle 4 de MAQUETTE.md laisse en lien plutôt qu'en bouton : une sortie posée
   DANS un corps de texte, pas un CTA au pied d'un panneau.

   Il suit le fond comme `.ton--accent` — bleu sur clair, fluo sur sombre — mais
   il porte son SOULIGNEMENT EN PERMANENCE, et pas seulement au survol : les
   mêmes phrases portent des mises en valeur `((…))` de cette teinte exacte, donc
   la couleur seule ne dirait pas ce qui se clique. Le survol épaissit le trait
   au lieu de le faire apparaître. */
.lien-texte {
  color: var(--text-accent);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.2em;
}

.lien-texte:hover {
  text-decoration-thickness: 2px;
}

/* ---------- Pastille ----------
   La pilule en Recoleta qui coiffe un bloc : les trois badges de la séquence
   sombre, les pastilles de module de « Au cœur de Biron », l'intitulé des
   témoignages. Trois écritures identiques au détail près, réunies ici.

   Ce qui reste à la charge de l'appelant, parce que ça change à chaque fois :
   la couleur du fond et du texte, le corps, et les marges. */
.pastille {
  display: inline-block;
  margin: 0;
  padding: 0.625rem 1.5rem;
  font-family: var(--font-serif);
  font-weight: var(--fw-semibold);
  line-height: 1.18;
  letter-spacing: -0.02em;
  border-radius: var(--radius-full);
}

/* ---------- Pastille en chip teintée ----------
   Reprise de la maquette (Figma 182:920) : le ton à 30 %, un filet du même
   ton, le libellé en Chivo Mono dans ce ton plein. Employée par les quatre
   modules de « Au cœur de Biron » et par les trois panneaux de la séquence
   sombre. L'intitulé des témoignages garde l'aplat plein en Recoleta.

   L'appelant fournit UNE seule chose, `--pastille-ton`. S'il l'oublie, les
   trois couleurs deviennent invalides et la chip se rend en noir sur
   transparent : la panne se voit, ce qui est le bon comportement.

   Le corps est plus petit que celui de la pastille pleine, et il le faut :
   à corps égal une mono occupe un tiers de largeur en plus, et la chip se
   mettait à concurrencer le titre de sa section. Le rembourrage suit dans la
   même proportion (7/18 contre 10/24) — gardé tel quel, il aurait donné une
   pilule bouffie autour d'un texte réduit.

   ⚠️ Le contraste dépend entièrement du fond, mesuré : sur le bleu nuit des
   sections sombres, le fluo sur son propre ton à 30 % donne 5,06:1, largement
   au-delà du seuil. Sur le blanc des blocs de « Au cœur de Biron », les tons
   de pilier tombent entre 1,70:1 et 2,82:1, sous le seuil de 3:1 — voir la
   note de sections/_coeur.scss, qui donne la retouche d'une ligne. */
.pastille--chip {
  padding: 0.4375rem 1.125rem; /* 7px 18px */
  font-family: var(--font-mono);
  font-size: clamp(1rem, 0.86rem + 0.43vw, 1.375rem); /* 16 → 22px */
  color: var(--pastille-ton);
  /* Alpha et non mélange avec du blanc : la chip garde son ton juste quel que
     soit le fond du bloc qui la porte — c'est ce qui lui permet de servir sur
     le blanc comme sur le bleu nuit. */
  background-color: color-mix(in srgb, var(--pastille-ton) 30%, transparent);
  border: 1px solid var(--pastille-ton);
}

/* ---------- Le fil d'Ariane ----------
   Le chemin qui situe une page terminale — partials/filAriane.njk.

   La grammaire des libellés de service du site : chasse fixe, capitales,
   interlettrage élargi, encre éteinte. La même que `.eyebrow`, et l'atome ne
   déclare que son APPARENCE — le bloc hôte garde le placement (ses marges).

   ⚠️ LES CHEVRONS SONT DES PSEUDO-ÉLÉMENTS, et il le faut : écrits dans le DOM ils
   seraient annoncés à la voix, alors que la structure de la liste dit déjà le
   chemin. Le raisonnement complet est en tête du partial.

   ⚠️ L'ENCRE VIENT DES RÔLES, jamais d'une couleur en dur : le fil sert aujourd'hui
   sur une bande SOMBRE (le hero d'étude de cas), où `.on-dark` passe --text-muted
   au casper — 6,4 sur le bleu nuit. Sur du crème il prendra le gris du site sans
   qu'on écrive une ligne. */
.fil-ariane {
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--text-muted);
}

.fil-ariane__liste {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2) var(--sp-3);
  margin: 0;
  padding: 0;
  list-style: none;
}

.fil-ariane__entree {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
}

.fil-ariane__entree + .fil-ariane__entree::before {
  content: "›";
  /* L'interlettrage élargi ajoute une espace APRÈS le chevron, qui le pousse vers
     le libellé suivant. On la reprend ici plutôt qu'en annulant le letter-spacing,
     qui fait partie de la grammaire des capitales du site. */
  margin-inline-end: calc(var(--ls-wide) * -1);
}

/* L'entrée courante prend l'accent — dans la maquette, « Hardloop » est le seul
   libellé coloré du fil. Sur la bande sombre, `.on-dark` a déjà mis ce rôle au
   fluo ; une couleur en dur serait fausse sur un fond clair. */
.fil-ariane__entree [aria-current] {
  color: var(--text-accent);
}

/* Le lien hérite l'encre éteinte du fil et ne se souligne qu'au survol : souligné
   en permanence, un chemin de trois entrées en capitales devient un tapis de
   traits. */
.fil-ariane__lien {
  color: inherit;
  text-decoration: none;
}

.fil-ariane__lien:hover,
.fil-ariane__lien:focus-visible {
  color: var(--text-strong);
  text-decoration: underline;
  text-underline-offset: 0.25em;
}

/* ---------- Le badge de section ----------
   La pastille FLUO à encre sombre qui annonce une section : « CAS MIS EN AVANT »,
   « LE DÉFI », « LES RÉSULTATS ».

   ⚠️ EXTRAITE DE `sections/_cas-clients.scss` LE 21/08/2026, le jour où
   `texte-module` a eu besoin du même objet pour les six libellés de bande de
   /cas-clients/hardloop/ — demande d'Hugo : « on les garde et on les met dans des
   badges ». Recopier l'aplat, l'encre et les cinq déclarations de chasse aurait
   laissé deux jeux de valeurs à tenir accordés, sans que rien ne signale leur
   divergence. C'est la règle du site : l'atome ici, le placement dans le bloc hôte.

   ⚠️ UN APLAT FLUO À ENCRE SOMBRE, et c'est la troisième sortie de la règle 16 de
   MAQUETTE.md : le fluo n'est pas une teinte de pilier et porte --c-text-main à
   15,3, donc l'aplat y est légitime sans recalcul par page.

   Mêmes valeurs que `.item--pastille .item__surtitre` et
   `.etapes--solo .etape__numero`, aux mêmes fins. Ces deux-là restent chez elles :
   ce sont des rangs d'un ATOME, pas des libellés de section — les réunir aurait
   demandé de leur retirer leur placement, qui n'est pas le même.

   ⚠️ IL PORTE SON PROPRE COUPLE FOND + ENCRE, écrit en clair, et il ne le tient
   d'aucun contexte : c'est ce qui le rend juste dans une boîte sombre, où
   --text-strong vaudrait le blanc — illisible sur le fluo.

   `width: fit-content` et non `align-self: start` : le badge sert dans une pile
   flex, où `justify-self` ne fait rien, comme dans une grille. La largeur
   intrinsèque vaut des deux côtés. */
.badge-section {
  width: fit-content;
  margin: 0;
  padding: 0.25rem var(--sp-4);
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--c-text-main);
  background-color: var(--c-fluo);
  border-radius: var(--radius-full);
}

/* ---------- La barre de filtres ----------
   Une rangée de pilules qui restreint une liste — les secteurs de /cas-clients/
   et les catégories du blog. Le moteur est src/assets/js/filtres.js.

   ⚠️ EXTRAITE DE `_cas-clients.scss` LE 21/08/2026, le jour où la grille
   d'articles du blog a eu besoin de la même barre. Recopier la pilule, son aplat
   d'actif et sa règle de masquage aurait laissé deux jeux de valeurs à tenir
   accordés, sans que rien ne signale leur divergence — c'est la raison qui avait
   déjà fait sortir `.carte-etiquettes`, `.quinconce` et `.badge-section` ici.

   ⚠️ LA RANGÉE N'EXISTE QU'À L'ÉTAT ENRICHI. Tant que filtres.js n'a pas posé
   `data-filtres-actif` sur la section, elle n'est pas rendue du tout : sans JS,
   personne ne voit un bouton qui ne fait rien. C'est le même mécanisme que
   `[data-onglets-actif]` et `[data-etapes-actif]` — la classe d'état enrichi ne
   style QUE l'état enrichi.

   ⚠️ ET C'EST L'INVERSE DU RÉFLEXE : on ne masque pas les entrées en CSS pour les
   révéler ensuite, on masque le CONTRÔLE. Une règle qui cacherait les cartes en
   laisserait la plupart invisibles le jour où le script tombe.

   ⚠️ CE NE SONT PAS DES `.btn` DU SITE, contrairement aux CTA des cartes : un
   `.btn` engage une action ou une navigation, un filtre ne fait que restreindre
   une liste et porte un état (`aria-pressed`). Les rendre en boutons pleins
   mettrait une rangée d'aplats bleus au-dessus d'une autre, et l'actif ne se
   distinguerait plus des autres.

   Le placement — où la rangée se pose dans son en-tête — reste au bloc hôte. */
.filtres {
  display: none;
}

[data-filtres-actif] .filtres {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
}

.filtre {
  padding: 0.5rem 1.125rem;
  font-family: inherit;
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  line-height: 1.2;
  color: var(--text-strong);
  background-color: transparent;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-full);
  cursor: pointer;
  transition: color var(--dur-fast) var(--ease-out), background-color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out);
}

/* ⚠️ APLAT QUI PORTE DU BLANC : IL LIT --btn-primary-bg, JAMAIS --c-blue EN
   DIRECT — règle 10 de MAQUETTE.md. Le défaut est dans le repli du `var()` et non
   dans une déclaration à part : une page pilier redéfinit --btn-primary-bg sur
   `<main>`, et une propriété posée ici battrait la valeur héritée. */
/* `aria-selected` : la même pastille sert d'ONGLET sur /irritants/ (01/10/2026),
   où elle bascule un panneau au lieu de filtrer une liste. */
.filtre[aria-pressed=true],
.filtre[aria-selected=true] {
  color: var(--c-white);
  background-color: var(--btn-primary-bg, var(--c-blue));
  border-color: transparent;
}

@media (hover: hover) {
  .filtre:not([aria-pressed=true]):not([aria-selected=true]):hover {
    color: var(--text-accent);
    border-color: var(--c-blue);
  }
}
/* ---------- L'inscription à la newsletter ----------
   Le champ e-mail, son bouton et le lien de repli — partials/inscription.njk.
   Deux hôtes : l'encart de la une du blog et le bloc `newsletter` qui la ferme.

   ⚠️ PREMIER ET SEUL FORMULAIRE DU SITE À CE JOUR. Rien dans _base.scss ne prépare
   un champ de saisie : tout ce qu'il faut est ici, et il n'y a donc aucune règle
   générale sur `input` à aller chercher ailleurs.

   ⚠️ LES VALEURS SONT ÉCRITES EN RÔLES, ET LE 21/08/2026 A PROUVÉ QUE ÇA SERVAIT.
   Le voile du champ est un `color-mix` vers `currentColor` et non vers le blanc :
   sur du bleu nuit il donne le voile blanc de la maquette (7 %), sur un fond clair
   il donne un voile sombre de même force. Le filet est `--border-subtle`, le texte
   `--text-strong`, l'invite `--text-muted`, et le bouton d'envoi est fluo à encre
   sombre — qui tient sur les deux fonds.

   Les trois premiers emplois étaient sur fond sombre ; le quatrième, l'ouverture
   de /newsletter/, est sur fond CLAIR depuis qu'Hugo a retiré le bleu nuit de
   cette bande. Ce formulaire n'a pas bougé d'une ligne pour ça. Une valeur blanche
   écrite en dur aurait rendu le champ invisible ce jour-là. */
.inscription {
  display: grid;
  gap: var(--sp-3);
  width: 100%;
}

/* Tout empilé — l'encart étroit de la une. */
.inscription--empile {
  grid-template-columns: minmax(0, 1fr);
}

/* Tout sur UNE rangée — le pré-pied de page, large et centré : le champ, le bouton
   d'envoi et, quand il existe, le second CTA.

   ⚠️ C'EST UNE RANGÉE FLEX QUI PASSE À LA LIGNE, ET NON UNE GRILLE, DEPUIS LE
   21/08/2026. Demande d'Hugo : « mettre les deux boutons l'un à côté de l'autre
   plutôt que l'un au-dessus de l'autre ». Avec trois objets sur la rangée, une
   grille aurait demandé un jeu de pistes par largeur — trois colonnes en large,
   deux en moyen, une en étroit, soit trois règles à tenir accordées. Le `wrap`
   fait les trois tout seul, dans cet ordre exact : le second CTA descend d'abord,
   le champ prend la ligne ensuite. Aucun point de rupture publié.

   ⚠️ ET C'EST `justify-content` QUI CENTRE, PAS `margin-inline: auto`. Les objets
   sont dimensionnés sur leur contenu, donc la rangée est plus étroite que sa
   boîte : c'est la distribution DANS la rangée qu'il faut centrer, pas la rangée
   dans son parent. Demande d'Hugo du même jour (« centrer le formulaire ») : elle
   était déjà centrée au sens strict, mais étirée en parts égales, et rien ne se
   lisait comme un objet posé au milieu. Le piège est le même que pour la rangée de
   chiffres de `hero-cas` — baisser une largeur ne rapproche rien tant qu'un `1fr`
   étire pour remplir.

   ⚠️ LA MESURE N'EST PLUS LES 620 px DE LA MAQUETTE. La planche ne dessine que
   deux objets sur la rangée ; à trois, 620 px renverraient le second CTA à la ligne
   — c'est-à-dire l'empilement qu'on vient de retirer. 50rem laisse la place aux
   trois avec de la marge pour un libellé plus long. */
.inscription--rangee {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--sp-4);
  max-width: 50rem; /* 800px — voir l'avertissement ci-dessus */
  margin-inline: auto;
}

/* Le champ est le seul objet élastique de la rangée : les boutons gardent leur
   chasse (`.btn` est `nowrap` par principe). La base de 18rem est ce qui décide du
   moment où la rangée passe à la ligne. */
.inscription--rangee .inscription__saisie {
  flex: 1 1 18rem;
}

/* ⚠️ `min-width: 0` EST NÉCESSAIRE, ET IL NE SE VOIT QUE DANS UNE COLONNE ÉTROITE.
   Un `<input>` porte une taille intrinsèque (l'attribut `size`, 20 caractères par
   défaut) qui lui sert de largeur minimale automatique quand il est enfant d'une
   grille ou d'un flex. Sans cette ligne, le champ refuse de descendre sous une
   vingtaine de caractères et pousse la piste — l'encart de la une, borné à 15 %,
   déborderait. */
/* Prénom + Nom, ajoutés le 23/09/2026 pour suivre le formulaire Brevo. Une
   rangée à part entière, toujours sur sa propre ligne au-dessus du champ
   e-mail — `flex: 1 1 100%` fait ce travail dans `.inscription--rangee` (même
   principe que `.inscription__consentement` plus bas) et ne gêne pas dans
   `.inscription--empile`, où le parent est une grille et ignore `flex`. */
.inscription__identite {
  display: flex;
  flex: 1 1 100%;
  flex-wrap: wrap;
  gap: var(--sp-3);
}

.inscription__champ {
  flex: 1 1 10rem;
  min-width: 0;
}

.inscription__saisie {
  width: 100%;
  min-width: 0;
  min-height: 2.8125rem; /* 45px — la cote de la maquette */
  padding: 0 var(--sp-4);
  font-family: var(--font-sans);
  font-size: var(--fs-btn);
  color: var(--text-strong);
  background-color: color-mix(in srgb, currentColor 7%, transparent);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
}

.inscription__saisie::placeholder {
  color: var(--text-muted);
  /* Firefox baisse l'opacité des placeholders : sans ça, la couleur mesurée
     n'est pas celle qui s'affiche. */
  opacity: 1;
}

.inscription__saisie:focus-visible {
  outline: 2px solid var(--text-accent);
  outline-offset: 2px;
  border-color: var(--text-accent);
}

/* Le bouton fluo à encre sombre. Ce sont les deux seuls aplats fluo à libellé du
   site, avec `.appel__cta`, et ils partagent leurs valeurs — la troisième sortie
   de la règle 16 de MAQUETTE.md : le fluo n'est pas une teinte de pilier et porte
   --c-blue-zodiac à 14,4, donc l'aplat est légitime sans recalcul par page. */
.inscription__envoi {
  min-height: 2.8125rem;
  color: var(--c-blue-zodiac);
  background-color: var(--c-starship);
  border-color: var(--c-starship);
}

@media (hover: hover) {
  .inscription__envoi:hover {
    background-color: var(--c-white);
    border-color: var(--c-white);
  }
}
/* Le second CTA sous le formulaire — « Voir les éditions ». C'est un
   `.btn--secondary` depuis le 21/08/2026 et il ne déclare RIEN de sa forme : le
   bouton du site s'habille tout seul, y compris sur fond sombre. Cette règle ne
   pose que sa place, comme toute feuille hôte. */
/* ⚠️ AUCUNE MARGE PROPRE : le second CTA est maintenant un objet de la rangée (ou
   de la pile), et c'est la gouttière du conteneur qui l'espace. Une marge en plus
   s'ajouterait à elle, et se verrait doublement le jour où la rangée passe à la
   ligne. */
.inscription__pied {
  margin: 0;
}

/* Sauf empilé : là, la gouttière de la pile (--sp-3) est calée sur l'écart entre
   le champ et son bouton, qui vont ensemble. Le second CTA, lui, est un autre
   objet et demande un cran de plus. */
.inscription--empile .inscription__pied {
  margin-block-start: var(--sp-3);
}

/* Le champ piège anti-robots de Brevo (`email_address_check`) — voir
   l'avertissement en tête de partials/inscription.njk. Hors-écran plutôt que
   `display: none` : un robot simple qui ignore les éléments masqués par CSS
   le remplirait quand même, ce que Brevo utilise pour écarter l'envoi. */
.inscription__piege {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* La case de consentement RGPD exigée par Brevo (champ `OPT_IN`) — voir le
   même avertissement. Aucune planche ne la dessine, donc son habillage reprend
   les rôles du formulaire plutôt qu'une forme propre : texte atténué, lien
   souligné comme ailleurs sur le site. */
.inscription__consentement {
  /* `flex: 1 1 100%` et non `flex-basis` seul : un `max-width` sur cet objet
     aurait bridé la TAILLE HYPOTHÉTIQUE que l'algorithme de retour à la ligne
     utilise pour décider qui tient sur la ligne — avec un plafond, le bouton
     d'envoi revenait se glisser à côté de la case au lieu de descendre seul. */
  flex: 1 1 100%;
  margin: 0;
  font-size: var(--fs-xs);
  line-height: var(--lh-normal);
  color: var(--text-muted);
  text-align: left;
}

.inscription__consentement label {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-2);
  cursor: pointer;
}

.inscription__consentement input[type=checkbox] {
  flex-shrink: 0;
  width: 1.125rem;
  height: 1.125rem;
  margin-block-start: 0.125rem;
  accent-color: var(--text-accent);
}

.inscription__consentement a {
  color: inherit;
  text-decoration: underline;
}

/* ---------- Aperçu en attente ----------
   Le cadre gris qui tient la place d'une capture pas encore livrée : le hero,
   « approche », les quatre visuels de « Au cœur de Biron ».

   ⚠️ Le jour où les vraies captures arrivent, ce composant disparaît — avec
   les classes `*__placeholder` des sections qui l'accompagnent. Ça a commencé
   le 25/08/2026 : `/produit/` a reçu ses cinq illustrations, et `hero-page`
   comme `texte-panneau` savent désormais rendre une image à la place du cadre
   (champ `image`, dimensions lues dans le fichier — voir lib/image.js). Le
   cadre reste le comportement par défaut partout où rien n'est déclaré.

   La mise en page est une grille à UNE colonne et non `place-content: center`.
   Ce dernier dimensionne le contenu en `max-content` : une légende longue
   débordait alors du cadre au lieu de s'enrouler. Le défaut avait été corrigé
   pour le seul visuel où il se voyait — il était présent sur les trois. */
.apercu {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  align-content: center;
  justify-items: center;
  gap: var(--sp-2);
  padding-inline: var(--sp-4);
  overflow: hidden;
  background: linear-gradient(180deg, var(--c-white), var(--c-blue-50));
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-cadre);
  color: var(--text-muted);
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  text-align: center;
  /* ⚠️ IL REMET LES DEUX RÔLES D'ENCRE EN CONTEXTE CLAIR, ET IL LE DOIT.

     Son fond est un dégradé BLANC écrit en clair : ce cadre est donc une carte
     claire quel que soit le fond sous lui. Posé dans un `.on-dark` — c'est le cas
     depuis le 19/08/2026, dans la boîte violette du connecteur IA — il héritait
     d'une légende blanche sur son propre blanc et d'un filet blanc translucide :
     invisibles tous les deux, et sans le moindre signal. C'est la règle 12 de
     MAQUETTE.md, branche « plusieurs encres ».

     Les deux valeurs sont celles que `:root` donne à ces rôles. Le doublon est
     assumé pour --border-subtle : sa valeur claire est une rgba littérale, il n'y
     a pas de jeton brut derrière elle. Les deux lignes doivent rester en phase
     avec `:root` dans _tokens.scss. */
  --text-muted: var(--c-fiord);
  --border-subtle: rgba(44, 42, 46, 0.12);
}

/* Le visuel réel d'un article (`visuel.image`), quand il existe — il
   remplace le texte d'attente au-dessus, sans changer le cadre qui le porte :
   même ratio, même arrondi, posés par la carte hôte (`.carte-article__apercu`,
   `.une__apercu`). */
.apercu__image {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* ---------- Le cadre : LE panneau fermé du site ----------
   RÈGLE, tranchée le 17/08/2026 : dès qu'un bloc est un panneau fermé, il a
   TOUJOURS les quatre mêmes caractères —

     coins CARRÉS          border-radius: 0
     filet BLEU éclairci   var(--filet-panneau)
     fond BLANC            var(--c-white)  ← et non le crème de la page
     titre DEDANS          dans sa propre bande, séparée d'un filet

   Elle vit ici, en primitive, et non recopiée dans chaque feuille de bloc.
   C'est ce qui a manqué : `rangees-illustrees` et `connecteurs` avaient le
   filet mais pas de fond, et le crème de la section les traversait.

   ---------------------------------------------------------------------------
   L'EXCEPTION AU « TITRE DEDANS », précisée le 18/08/2026.

   Le titre dans sa bande sert à donner au cadre sa charge : sans lui, un cadre
   n'est qu'un rectangle posé là, sans dire ce qu'il contient. Mais quand le
   cadre enferme un MODULE INTERACTIF — une liste d'onglets, un tableau qu'on
   manipule — le module porte déjà cette charge tout seul : il s'annonce par sa
   forme et par le fait qu'il répond. Le titre peut alors sortir et s'énoncer sur
   le crème, comme celui d'une section ordinaire.

   Les trois autres caractères ne bougent pas, eux : coins carrés, filet bleu,
   fond blanc restent obligatoires dans tous les cas.

   Le seul cas à ce jour est `connecteurs` (« Plus de 50 connecteurs »), dont le
   cadre n'encadre que le tablist des compartiments métier. `rangees-illustrees`
   garde son titre dedans : ses rangées sont du contenu, pas un module.

   Le nom est `.cadre` et non `.panneau` : ce dernier est déjà pris par les
   panneaux de la séquence sombre (partials/panneau.njk).

   Les rembourrages de l'en-tête viennent des jetons PARTAGÉS
   --pad-panneau-entete-*, les mêmes que « Au cœur de Biron ». */
.cadre {
  background-color: var(--c-white);
  border: var(--filet-panneau);
  border-radius: 0;
  /* Le cadre est BLANC, la page est crème. Sans cette ligne, un
     `.btn--secondary` posé dedans se remplit du crème de la page — il prend
     toujours la couleur de son fond, et son fond ici c'est le cadre. */
  --section-bg: var(--c-white);
}

.cadre__entete {
  padding: var(--pad-panneau-entete-haut) var(--pad-panneau-entete-inline) var(--pad-panneau-entete-bas);
  border-bottom: var(--filet-panneau);
}

/* Le titre d'un cadre ne déclare RIEN de sa police : _base.scss pose déjà
   serif, gras et la taille du niveau. On ne touche qu'à la marge et à la
   mesure. */
.cadre__titre {
  margin: 0;
  max-width: 24ch;
  color: var(--c-blue-zodiac);
  text-wrap: balance;
}

/* ---------- Le quinconce ----------
   Des entrées qui se répondent en alternance, une par rangée, chacune n'occupant
   qu'une moitié de la largeur. C'est le rythme des « 4 garanties » de la page
   produit (Figma 185:7105).

   ⚠️ EXTRAIT DE `_liste-glyphes.scss` LE 20/08/2026, quand le bloc `face-a-face` a
   eu besoin du même rythme pour sa liste (« les mettre comme les items des
   garanties »). C'est la raison qui avait déjà fait sortir `.carte-etiquettes` dans
   ce fichier : recopier la grille, l'alternance et son point de rupture aurait
   laissé deux jeux de valeurs à tenir accordés, et rien n'aurait signalé leur
   divergence.

   ⚠️ C'EST UNE EXCEPTION À « la disposition appartient au bloc hôte », et elle est
   bornée : ce qui est ici, c'est le RYTHME (grille, alternance, respiration), pas la
   place du bloc dans la page. Un bloc hôte garde son titre, son fond et ses marges
   de section.

   L'alternance est calculée sur le rang par le gabarit, jamais déclarée dans les
   données : une entrée insérée au milieu ne demande donc pas de reprendre les
   suivantes. Elle est purement décorative — elle ne porte aucun sens. */
.quinconce {
  margin: 0;
  padding: 0;
  list-style: none;
}

/* La grille est portée par l'ENTRÉE et non par la liste : le contenu n'occupe qu'une
   moitié, et c'est l'alternance de la moitié occupée qui donne le rythme. Sur la
   liste, il aurait fallu deux entrées par rangée. */
.quinconce__entree {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
}

/* La respiration entre deux entrées, franche : c'est le seul signal de séparation.
   Posée ENTRE les entrées et non autour de chacune, pour que la première et la
   dernière ne décollent pas de ce qui les encadre.

   ⚠️ AUCUN FILET, et c'est la décision du 17/08/2026 : sans cadre autour, un filet
   qui court sur toute la largeur ne délimite plus rien, il barre la page.

   ⚠️ LE RYTHME EST UN BOUTON, ET SON DÉFAUT EST DANS LE REPLI DU `var()` — jamais
   dans une déclaration ici, sans quoi il serait insurchargeable depuis un ancêtre
   (une propriété personnalisée posée SUR l'élément bat celle qu'il hérite). Le
   défaut est calé sur du contenu NU : `liste-glyphes` et la liste de `face-a-face`
   posent des `item()` sans fond, et il faut cette respiration-là pour que deux
   entrées se détachent. Un hôte dont les entrées portent déjà une CARTE — fond,
   arête, rembourrage — n'a plus besoin d'autant : c'est la carte qui sépare, et
   `.quotidien` le resserre de moitié pour cette raison (voir _quotidien.scss).
   Les deux autres hôtes ne bougent pas d'un pixel, ne déclarant rien. */
.quinconce__entree + .quinconce__entree {
  margin-block-start: var(--quinconce-rythme, clamp(var(--sp-10), 4.5vw, var(--sp-20)));
}

.quinconce__entree--gauche > * {
  grid-column: 1;
  padding-inline-end: var(--sp-10);
}

.quinconce__entree--droite > * {
  grid-column: 2;
  padding-inline-start: var(--sp-10);
}

/* Une seule colonne. L'alternance disparaît : à cette largeur elle ne se lit plus
   comme un rythme, seulement comme des marges incohérentes d'une entrée à l'autre. */
@media (max-width: 60rem) {
  .quinconce__entree {
    grid-template-columns: minmax(0, 1fr);
  }
  .quinconce__entree--gauche > *,
  .quinconce__entree--droite > * {
    grid-column: 1;
    padding-inline: 0;
  }
}
/* ---------- Le chemin ----------
   Le tracé en pointillé bouclé posé derrière une liste en quinconce, demandé
   par Hugo le 25/08/2026 pour « Nos 4 garanties qui s'appliquent en continu ».
   Voir partials/chemin.njk pour le balisage et assets/js/chemin.js pour la
   géométrie — le `d` du <path> est calculé, jamais écrit à la main.

   Ce fichier ne porte que l'APPARENCE du trait et l'empilement. Le geste, lui,
   se règle en `data-chemin-*` sur l'enveloppe.

   ⚠️ TOUT EST EN BOUTON AVEC LE DÉFAUT DANS LE REPLI DU `var()`, et jamais en
   déclaration à part : une propriété personnalisée posée ICI battrait la même
   propriété héritée d'un ancêtre, et une page qui voudrait un tracé plus fin ou
   d'une autre teinte n'aurait aucun moyen de le dire.

   ⚠️ LE FLUO NE SE MESURE PAS ICI. C'est du décor pur : aucun texte ne se pose
   dessus, aucun contraste n'est en jeu. Ne pas l'assombrir « pour la
   lisibilité » — il n'y a rien à lire. */
.chemin {
  position: relative;
}

.chemin__trace {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  /* Les boucles débordent la boîte de la liste par construction : sans ça,
     elles seraient coupées net sur son bord. Le débordement horizontal est
     borné côté script (`amplitudeMax`) et arrêté côté section (`overflow-x:
     clip` sur le bloc hôte), pour qu'il ne puisse jamais créer de barre de
     défilement. */
  overflow: visible;
  pointer-events: none;
}

/* ⚠️ RIEN À TRAVERSER SOUS `lg`, DONC PAS DE TRACÉ. À cette largeur
   l'alternance du quinconce s'effondre : chaque entrée prend toute la largeur,
   il n'y a plus de moitié vide. Or c'est exactement là que le trait passe — le
   laisser courir reviendrait à le faire passer sur le texte, faute de vide où
   passer. C'est la prémisse du décor qui disparaît, pas son rendu qui se
   dégrade. Le script n'en sait rien et n'a pas à le savoir : il calcule, le CSS
   décide si ça se montre.

   ⚠️ C'EST UN PLANCHER, PAS LE SEUIL DE TOUS LES EMPLOIS. Un hôte dont la
   prémisse tombe plus haut relève la borne chez lui : la boucle des « 4
   garanties » disparaît dès `xl`, faute de place sur les flancs du bloc (voir
   sections/_liste-numerotee.scss). Ne pas remonter CE seuil pour autant — il
   n'a à répondre que du quinconce. */
@media (max-width: 60rem) {
  .chemin__trace {
    display: none;
  }
}
/* ⚠️ LE CONTENU PASSE AU-DESSUS PAR UN `z-index` EXPLICITE, ET NON EN
   RENVOYANT LE TRACÉ EN ARRIÈRE — les deux réflexes échouent ici.

   Sans `z-index` du tout, le <svg> l'emporterait : il est POSITIONNÉ, donc
   peint dans la couche des éléments positionnés, au-dessus du texte en flux
   normal quel que soit l'ordre du document.

   Et `z-index: -1` sur le <svg> l'enverrait derrière le fond de la section —
   `.liste-glyphes` peint `--bg-page` — où il serait purement invisible, parce
   que `.chemin` ne crée pas de contexte d'empilement. */
.chemin > :not(.chemin__trace) {
  position: relative;
  z-index: 1;
}

.chemin__trait {
  fill: none;
  stroke: var(--chemin-couleur, var(--c-fluo));
  stroke-width: var(--chemin-epaisseur, 8px);
  stroke-dasharray: var(--chemin-tirets, 14 20);
  /* Bouts ronds : c'est ce qui donne au pointillé son grain souple plutôt
     qu'une suite de rectangles. Ils ajoutent une demi-épaisseur de chaque côté
     du tiret — le vide déclaré est donc plus grand que le vide vu. */
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* ---------- L'item ----------
   Marqueur, intitulé, description. Ce trio revient dans six pages de la
   maquette sous des mises en page très différentes — fiche numérotée, rangées
   de produits, garanties, étapes de méthode. Ce qui varie est la DISPOSITION,
   posée par le bloc qui l'accueille ; l'atome, lui, ne change pas.

   Il est ici plutôt que dans une feuille de section pour une raison précise :
   deux blocs l'emploient — `rangees-illustrees` et `liste-glyphes` — et sans
   tronc commun un ajustement sur l'un laisserait l'autre derrière sans que rien
   ne le signale.

   Le bloc hôte ne pose que la disposition de `.item` (grille, colonne,
   alternance) et, s'il le veut, la teinte du marqueur via --item-ton.
   Voir partials/item.njk. */
/* Deux rangs empilés : la ligne d'intitulé (marqueur + titre + cartouche), puis
   le texte. Et non deux colonnes — voir le long commentaire de
   partials/item.njk, c'est ce changement qui rend le marqueur alignable. */
.item {
  display: flex;
  flex-direction: column;
  /* Bouton, défaut dans le repli du `var()` : c'est la gouttière ENTRE LES RANGS de
     l'atome — l'étiquette, la ligne d'intitulé, le texte. Un module à mi-largeur la
     resserre (voir `.texte-module__module--demi`), parce qu'à ce format une case ne
     porte plus qu'une étiquette et une valeur de deux mots : à 12px de gouttière, les
     deux se lisent comme un couple. Écrit en déclaration à part, il serait
     insurchargeable depuis un ancêtre. */
  gap: var(--item-rangs-gap, var(--sp-3));
  /* La cote du marqueur en forme de marque, et le retrait qui s'en déduit. Les deux
     vivent ici, ensemble, parce que le second est CALCULÉ à partir du premier :
     éparpillés, un changement de taille de forme aurait laissé le retrait à son
     ancienne valeur, et le texte se serait décalé d'un ou deux pixels sans que rien
     ne le signale. */
  --item-marqueur-w: 1.75rem;
  --item-retrait: 0px;
}

/* ---------- Le texte aligné sur l'intitulé ----------
   Demandé par Hugo le 19/08/2026 : « aligner le texte au début du titre et pas du
   point ». La description se renfonce donc de la largeur du marqueur plus sa
   gouttière, et son premier caractère tombe exactement sous celui de l'intitulé.

   ⚠️ CE N'EST PAS UN RETOUR AUX DEUX COLONNES, et c'est important : la version d'avant
   le 18/08/2026 mettait le MARQUEUR dans sa propre colonne, ce qui lui retirait toute
   ligne de base commune avec l'intitulé — le centrer verticalement demandait alors de
   deviner où tombe la première ligne du titre, ce que le CSS ne permet pas (voir le
   long commentaire de partials/item.njk). Ici le marqueur reste DANS la ligne de
   l'intitulé, leurs lignes de base s'alignent toujours, et c'est seulement le TEXTE
   qui se décale.

   ⚠️ IL FAUT DONC QUE LE MARQUEUR AIT UNE LARGEUR FIXE. C'est le cas de la forme de
   marque (--item-marqueur-w), et c'est pourquoi le retrait est une OPTION et non le
   défaut : un marqueur « 01 » en chasse fixe ou un badge « ÉTAPE 12 » n'ont pas de
   largeur connue à l'avance, et le retrait tomberait à côté. Les deux blocs qui
   l'emploient — `liste-glyphes` et la liste de `face-a-face` — sont tous les deux en
   forme de marque. */
.item--retrait {
  --item-retrait: calc(var(--item-marqueur-w) + var(--sp-3));
}

/* ---------- L'escalier des items en mode mobile ----------
   Demandé par Hugo le 27/08/2026 pour la section « Chacun construit la lecture dont
   il a besoin » de /plateforme/ — « un peu de rembourrage pour les titres des items
   et encore une indentation supplémentaire pour les textes de ces items » — puis
   étendu le même jour à TOUTES les listes d'items. Empilées à même la gouttière de
   page, les entrées se lisaient comme un seul pavé : le marqueur, l'intitulé et la
   description partaient tous du même bord.

   Deux marches, donc : l'item entier rentre d'un cran (le rembourrage de
   l'intitulé), et sa description d'un cran de plus.

   ⚠️ LA SECONDE MARCHE EST `--item-retrait`, celui de la règle juste au-dessus, et
   son long commentaire dit pourquoi il n'est valable QUE pour un marqueur de largeur
   connue d'avance — d'où le `:has()` sur la forme de marque. Un « 01 » en chasse
   fixe, un badge « ÉTAPE 12 », un glyphe de pilier ou un item sans marqueur du tout
   n'ont pas de largeur à laquelle aligner quoi que ce soit, et le retrait tomberait
   à côté : ceux-là ne reçoivent rien, faute de repère, pas par oubli.

   ⚠️ IL EST POSÉ SUR `.item` ET NON SUR UN ANCÊTRE : l'atome déclare
   `--item-retrait: 0px` sur lui-même, donc une valeur héritée serait masquée. Le
   sélecteur vise bien `.item`, et il est plus spécifique que celui de l'atome.

   Ce qu'il touche, relevé dans la sortie construite et non supposé : les piles de
   `texte-module` (/plateforme/, /cas-clients/hardloop/ ×2), ses entrées en `rangee`
   (/dictionnaire/, qui repassent en une seule colonne sous `lg` — c'est bien une pile
   à cette largeur), `liste-glyphes` (/produit/, /a-propos/), la suite de
   `face-a-face` (/connecteur-ia/) et l'agenda de `rendez-vous`
   (/parler-a-un-expert/). Les trois derniers portent déjà `.item--retrait` à toutes
   les largeurs : ils ne gagnent ici que la première marche, et la seconde se
   redéclare à l'identique.

   ⚠️ AUCUNE EXCLUSION POUR LA GRILLE ET LES CARTES DE `texte-module`, et ce n'est pas
   un oubli : aucune de leurs cellules ne porte de marqueur en forme de marque
   aujourd'hui (vérifié dans la sortie construite), donc le `:has()` les écarte tout
   seul. Le jour où l'une en portera, c'est le rembourrage propre de la cellule qu'il
   faudra regarder — une case en a déjà un, et l'escalier y rejouerait le même
   décalage deux fois.

   Rien ne bouge au-dessus de `lg`. */
@media (max-width: 60rem) {
  .item:has(.item__marqueur--marque) {
    padding-inline-start: var(--sp-3);
    --item-retrait: calc(var(--item-marqueur-w) + var(--sp-3));
  }
}
/* Chasse fixe et capitales : c'est la grammaire des intitulés techniques du
   site — mêmes réglages que la date des articles et les étiquettes de figure.

   `font-variant-numeric: tabular-nums` n'est pas cosmétique : sans lui, les
   chiffres de Chivo Mono restent à chasse fixe mais le « 1 » de « 01 » et
   celui de « 11 » ne s'alignent pas verticalement d'une ligne à l'autre. */
.item__marqueur {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  font-weight: var(--fw-medium);
  font-variant-numeric: tabular-nums;
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--item-encre, var(--item-ton, var(--text-accent)));
}

/* Il y avait ici `padding-block-start: 0.35em`, pour poser à la main la ligne de
   base du petit marqueur sur celle du titre. Retiré le 18/08/2026 : le rang
   aligne désormais les lignes de base lui-même, et ce décalage venait s'y
   ajouter au lieu de le corriger. */
/* ---------- Alignement des marqueurs en forme ----------
   ⚠️ AUCUNE CONSTANTE ICI, ET C'EST LE POINT. Trois réglages successifs ont
   échoué avant celui-ci — hauteur de boîte de ligne, puis remontée en em — parce
   qu'ils essayaient tous de deviner où tombent les capitales dans la ligne. Le
   CSS n'expose pas les métriques de la fonte : `1cap` en donne la hauteur, mais
   jamais sa position. Toute valeur écrite à la main était condamnée à dériver.

   La solution est de laisser le navigateur faire le calcul, avec deux règles qui
   se combinent :

     `align-items: baseline` sur `.item__entete` accroche la ligne de base du
       marqueur à celle du titre ;
     `vertical-align: middle` sur la forme la centre sur la HAUTEUR D'X de la
       fonte — la définition typographique du milieu optique d'une ligne.

   Pour que la seconde soit juste, le marqueur doit porter la même fonte et le
   même corps que l'intitulé : c'est de SA fonte que le navigateur tire la
   hauteur d'x. D'où les deux déclarations de police ci-dessous, sur un élément
   qui ne contient pourtant aucun texte. Elles ne sont pas décoratives. */
.item__marqueur--glyphe,
.item__marqueur--marque {
  padding-block-start: 0;
  /* Annule la fonte mono de `.item__marqueur` pour reprendre celle du rang.
     `font` en bloc plutôt que trois propriétés : il remet aussi le corps et
     l'interligne, dont dépend la hauteur d'x que lit `vertical-align`. */
  font: inherit;
}

.item__marqueur--glyphe > *,
.item__marqueur--marque > * {
  display: inline-block;
  vertical-align: middle;
}

/* La forme de marque en marqueur. Sa largeur est fixe et non fluide : c'est
   une pastille de repérage, pas un élément de décor — à taille variable elle
   se mettrait à concurrencer l'intitulé qu'elle annonce. */
.item__forme {
  /* La cote vient de l'atome, parce que le retrait du texte est calculé dessus —
     voir `--item-marqueur-w`. La changer ici seul décalerait le texte. */
  --shape-w: var(--item-marqueur-w);
  --shape-color: var(--item-ton, var(--c-blue));
}

/* ---------- La forme qui PORTE son numéro ----------
   Demandée par Hugo le 21/08/2026 pour /parler-a-un-expert/ — « les numéros dans
   des Shapes ». Le pourquoi du balisage (le chiffre est un FRÈRE de la forme, pas
   son enfant, parce qu'un masque découperait le texte avec elle) est en tête de
   partials/item.njk.

   ⚠️ RIEN ICI NE REFAIT L'ALIGNEMENT SUR LE TITRE. Le gabarit émet aussi
   `--marque`, donc la pastille hérite déjà de la fonte du rang et du
   `vertical-align: middle` qui font tout le travail — voir le long commentaire
   juste au-dessus. Ces règles-ci ne s'occupent que de l'INTÉRIEUR de la forme. */
/* ⚠️ LA COTE VA SUR `.item` ET NON SUR LE MARQUEUR, et c'est structurel :
   `--item-retrait` la lit pour calculer le renfoncement du texte. Posée sur le
   marqueur seul, la forme grossirait et le texte resterait renfoncé de l'ancienne
   valeur — un décalage de 12 px sans le moindre signal.

   Une forme qui doit contenir « 01 » ne peut pas avoir la cote d'une forme qui ne
   contient rien : à 1,75rem, deux chiffres de 11 px débordent de l'aplat. 2,25rem
   les tient avec de l'air autour, et reste sous la hauteur de capitale d'un
   intitulé — au-delà, la pastille se met à concurrencer le titre qu'elle annonce,
   ce que la note de `.item__forme` dit déjà de la forme nue.

   Les trois formes sont mesurées au même endroit : le chiffre tombe au CENTRE
   vertical, qui est justement le point le plus large des trois (citron, losange,
   écu). C'est ce qui permet une cote unique pour la rotation. */
.item:has(.item__marqueur--marque-numero) {
  --item-marqueur-w: 2.25rem;
}

/* La pile forme + chiffre. Une grille à UNE cellule, les deux enfants dedans :
   c'est la case qui centre le chiffre sur la forme, donc aucune cote n'est écrite
   à la main et la cote de la forme peut changer sans rien recaler.

   `inline-grid` et non `inline-block` : la règle `.item__marqueur--marque > *`
   au-dessus pose `inline-block`, qui ferait de la pastille un simple conteneur en
   ligne où le chiffre suivrait la forme au lieu de se superposer à elle. Elle est
   surchargée ici, à spécificité supérieure, et `vertical-align` — qu'elle pose
   aussi — continue de s'appliquer : une grille en ligne le respecte. */
.item__marqueur--marque-numero > .item__pastille {
  display: inline-grid;
  place-items: center;
}

.item__marqueur--marque-numero .item__pastille > * {
  grid-area: 1/1;
}

/* ⚠️ LE CHIFFRE DOIT ÊTRE POSITIONNÉ, SINON LA FORME LUI PASSE DESSUS. Défaut vu au
   rendu le 21/08/2026 — « tu n'as pas mis de chiffre dans les formes » : le chiffre
   était bien dans le DOM, blanc, au bon endroit, et invisible.

   La cause n'est pas l'ordre du DOM, qui est le bon (la forme d'abord, le chiffre
   ensuite). C'est que `.forme` porte `transform: rotate(var(--shape-rot, 0deg))`, et
   qu'un `transform` autre que `none` — même une rotation de ZÉRO degré — fait de
   l'élément un contexte d'empilement, peint comme s'il était `position: relative;
   z-index: 0`. Il passe donc à l'étape 8 de l'ordre de peinture, après les blocs
   ordinaires du même parent. Un chiffre non positionné se retrouve DERRIÈRE la forme
   quelle que soit sa place dans le DOM.

   `z-index` seul n'y ferait rien : il n'a aucun effet sur un élément non positionné.
   D'où les deux déclarations de la règle plus bas, qui vont ensemble. */
/* ⚠️ APLAT QUI PORTE DU BLANC : IL LIT --btn-primary-bg, JAMAIS --c-blue EN
   DIRECT. Règle 10 de MAQUETTE.md — sur une page pilier, --c-blue prend la teinte
   du pilier telle quelle et le blanc y échoue sur trois des quatre.
   --btn-primary-bg porte déjà l'arbitrage fait pour `.btn--primary`, pilier par
   pilier : c'est la même variable, donc rien à recalculer ici. C'est le sixième
   aplat du site à porter du blanc.

   Le sélecteur a deux classes (0,2,0) et il le faut : `.forme--bleu`, que le
   gabarit pose aussi, vaut (0,1,0) mais est écrite PLUS BAS dans ce fichier — à
   spécificité égale elle l'emporterait et la forme reprendrait --c-blue en direct.
   C'est le piège que `texte-module` a déjà dû corriger le 21/08/2026. */
.item__marqueur--marque-numero .item__forme {
  --shape-color: var(--btn-primary-bg, var(--c-blue));
}

/* Le chiffre. Chasse fixe et tabular-nums comme le marqueur « 01 » nu, dont il est
   le même objet à un autre endroit — mais SANS interlettrage : à 0,14em, deux
   chiffres se décentrent de la forme d'un demi-caractère vers la droite, l'espace
   final n'étant pas repris.

   `--fs-mono` (11 px) dans une forme de 40 px : c'est la seule cote de l'échelle
   qui laisse de l'air dans un citron, dont la largeur utile au centre vaut environ
   les deux tiers de la boîte. */
.item__pastille-numero {
  /* Voir l'avertissement ci-dessus : sans ces deux lignes, la forme — qui porte un
     `transform` — passe DEVANT le chiffre et le rend invisible. */
  position: relative;
  z-index: 1;
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  font-weight: var(--fw-medium);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0;
  line-height: 1;
  color: var(--c-white);
}

.item__marqueur--etape {
  /* Le seul marqueur qui n'est pas décoratif : les étapes se suivent, leur
     ordre porte du sens, il reste donc lisible à la voix. */
  white-space: nowrap;
}

.item__texte {
  display: flex;
  flex-direction: column;
  /* Le même bouton que la gouttière des rangs de `.item`, pour que l'accroche, le
     corps et les puces se resserrent avec eux. */
  gap: var(--item-rangs-gap, var(--sp-3));
  /* Nul par défaut : le retrait est une option, voir `.item--retrait`. */
  margin-inline-start: var(--item-retrait);
}

/* Le marqueur, l'intitulé et le cartouche partagent une ligne.

   `baseline` fait tout le travail d'alignement : les trois éléments accrochent
   leur ligne de base à celle de la première ligne du titre. C'est pour ça que
   le marqueur a rejoint ce rang — dans sa colonne d'avant, il n'avait aucune
   ligne de base commune avec le titre et il fallait deviner. Ne pas repasser en
   `center` : quand l'intitulé se replie sur deux lignes, tout descendrait au
   milieu du bloc de titre. */
/* ⚠️ LA FONTE DU RANG EST DÉCLARÉE ICI, UNE SEULE FOIS, et le titre comme le
   marqueur en héritent. Ce n'est pas une élégance : le marqueur tire sa hauteur
   d'x de SA PROPRE fonte, donc la moindre divergence avec le titre décale
   l'alignement. C'est exactement ce qui a fait échouer les premières
   tentatives — le titre était passé de --fs-md à --fs-h3 et le marqueur, qui
   recopiait --fs-md, calculait sur un corps qui n'existait plus. Deux
   déclarations séparées, c'est un décalage qui attend son heure. */
.item__entete {
  display: flex;
  align-items: baseline;
  gap: var(--sp-3);
  font-family: var(--font-serif);
  /* ⚠️ LE CORPS EST UN BOUTON, ET SON DÉFAUT EST DANS LE REPLI DU `var()`. Un
     module à mi-largeur (`module.demi` de `texte-module`) descend ses intitulés à
     --fs-base : à 22→32 px dans une case deux fois plus étroite, un intitulé se
     replie sur trois lignes. Écrire le défaut dans une déclaration à part l'aurait
     rendu insurchargeable — une propriété posée SUR l'élément bat toujours la même
     héritée d'un ancêtre. Même mécanique que --repere-taille. */
  font-size: var(--item-entete-taille, var(--fs-h3));
  line-height: var(--lh-snug);
}

/* ⚠️ AUCUNE `font-weight` ICI, ET C'EST VOLONTAIRE : c'est un <h3>, il prend donc
   le gras des titres du site déclaré une seule fois dans _base.scss (h1 à h4).
   C'est exactement ce que font les autres intitulés du site — `.cadre__titre`,
   `.texte-module__titre`, `.cas__titre`, `.hero-page__titre` n'en déclarent aucune
   non plus.

   Une `--fw-semibold` traînait ici depuis l'époque où cet intitulé était figé à
   --fs-md (18 px), corps auquel un demi-gras est juste. Le jour où le corps est
   passé à --fs-h3 (22 → 32 px) en héritant de `.item__entete`, la graisse n'a pas
   été rejugée : à 32 px, le demi-gras ne se lit plus comme un titre à côté du
   <h2> de section qui, lui, est en gras. Corrigé le 25/08/2026 sur signalement
   d'Hugo (« Créer vos dashboards » sur /plateforme/). Ne pas la remettre. */
.item__titre {
  margin: 0;
  font-family: inherit;
  font-size: inherit;
  line-height: inherit;
  letter-spacing: var(--ls-tight);
  color: var(--text-strong);
}

/* ---------- Le cartouche ----------
   ⚠️ POURQUOI LA TEINTE DU PILIER EST DILUÉE ET LE TEXTE SOMBRE : à pleine
   teinte, aucun pilier ne porte du texte de 11 px. En aplat franc, le blanc
   plafonne à 2,97 sur le rose et 2,12 sur l'orange ; --c-text-main y passe pour
   trois piliers sur quatre mais tombe à 3,43 sur le violet du connecteur.
   Diluée à 15 %, la teinte donne ~12 de contraste sur les quatre, et le filet
   reste à pleine force — un filet n'est pas du texte. */
.item__badge {
  /* Seul élément du rang à se ferrer à droite. Une marge auto plutôt qu'un
     `space-between` sur le rang : le marqueur et l'intitulé doivent rester
     collés, ce que `space-between` aurait dispersés. */
  margin-inline-start: auto;
  flex: none;
  padding: 0.125rem var(--sp-2);
  border: 1px solid var(--item-ton, var(--text-accent));
  border-radius: var(--radius-full);
  background-color: color-mix(in srgb, var(--item-ton, var(--text-accent)) 100%, var(--c-white));
  font-family: var(--font-mono);
  font-size: var(--fs-base);
  font-weight: var(--fw-medium);
  /* letter-spacing: var(--ls-display); */
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--c-white);
}

/* ---------- Le surtitre ----------
   L'étiquette de rattachement, au-dessus de l'intitulé. Elle reprend EXACTEMENT
   la grammaire de `.item__marqueur` — chasse fixe, capitales, interlettrage
   élargi, teinte de l'item — parce que c'est le même registre : un libellé de
   service qui situe l'entrée, pas une phrase.

   ⚠️ ELLE N'EST PAS DANS `.item__entete`, contrairement au marqueur et au
   cartouche. Ce rang-là aligne ses enfants sur la ligne de base du titre et
   porte la fonte de titrage ; le surtitre est un rang à lui, au-dessus. C'est
   aussi ce qui permet à `subgrid` de l'aligner d'une carte à l'autre dans une
   grille — voir sections/_texte-module.scss.

   Pas de `font-variant-numeric` ici, contrairement au marqueur : ce sont des
   mots, pas des numéros à faire coïncider d'une ligne à l'autre. */
.item__surtitre {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--item-encre, var(--item-ton, var(--text-accent)));
}

/* ---------- Le rang du haut : l'étiquette et le logo ----------
   Un rang flex plutôt qu'un logo posé en absolu dans le coin de la carte : le rang
   fait sa hauteur, donc rien ne peut se chevaucher quand la carte se resserre. Le
   pourquoi complet est dans partials/item.njk.

   `align-items: center` et non `start` : le logo est nettement plus haut que la
   pastille, et c'est lui qui donne la hauteur du rang. Ferrés en haut, les deux
   objets se décrocheraient l'un de l'autre sans que rien ne l'exige.

   ⚠️ `gap` MALGRÉ LE `space-between` : sans lui, un libellé de pastille assez long
   viendrait toucher le bloc du logo sur une carte étroite — `space-between` ne
   ménage rien quand il n'y a plus d'espace à répartir. */
/* ⚠️ SA GOUTTIÈRE EST HORIZONTALE — elle sépare l'étiquette du logo, pas deux rangs.
   Elle ne suit donc PAS le bouton `--item-rangs-gap` : un module resserré rapproche
   ses rangs, il n'a aucune raison de coller un logo à son étiquette. */
.item__rang-haut {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
}

/* ---------- Le bloc blanc du logo ----------
   ⚠️ LE FOND EST --c-white EN DUR, et c'est l'un des rares endroits du site où
   une couleur est écrite plutôt qu'un rôle. La raison n'est pas graphique : un logo
   tiers porte sa propre couleur de marque, qu'on ne peut ni changer ni faire
   dialoguer avec un fond coloré. Passer par --section-bg le rendrait orange sur la
   carte du pilier, et les deux logos y perdraient leur lisibilité de marque.

   ⚠️ LA HAUTEUR EST FIXE, LA LARGEUR NON, et c'est ce qui rend le bloc juste pour
   des logos de proportions très différentes : la marque Zapier est carrée (256×256),
   le logotype n8n fait 2,5:1 (mot compris). Les deux blocs ont donc la même hauteur
   et des largeurs différentes — ce qui se lit comme deux logos, alors qu'un carré
   imposé aurait réduit le logotype n8n à un timbre illisible.

   `flex: none` parce que le bloc est un enfant de `.item__rang-haut` : sans lui,
   `space-between` le laisserait se comprimer avant la pastille. */
.item__logo {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  height: 2.5rem;
  padding-inline: var(--sp-3);
  border-radius: var(--radius-md);
  background-color: var(--c-white);
}

/* La hauteur du bloc moins ses marges internes. `width: auto` laisse la largeur
   suivre le ratio du fichier — c'est ce qui demande un viewBox sur chaque logo,
   voir le commentaire ajouté en tête d'Assets/n8n_logo.svg. */
.item__logo img {
  display: block;
  height: 1.5rem;
  width: auto;
  max-width: 100%;
}

/* ---------- Le surtitre en pastille fluo ----------
   Demandé par Hugo le 20/08/2026 pour les deux cartes de /integrations/ — « des
   badges fluo comme sur le scroll de la page Excel et Sheets ».

   ⚠️ C'EST LE MÊME OBJET QUE `.etapes--solo .etape__numero`, et les valeurs sont
   les mêmes des deux côtés : aplat fluo, encre courante, arrondi complet, même
   rembourrage. La chasse fixe en capitales, elle, vient déjà de `.item__surtitre`
   au-dessus — c'est la grammaire commune des deux étiquettes.

   ⚠️ CE N'EST PAS LE MÉCANISME DE PASTILLE RETIRÉ LE MÊME JOUR, et la différence
   est mesurée. Celui-là posait la TEINTE DU PILIER en aplat, ce qu'interdit la
   règle 16 de MAQUETTE.md : aucune encre ne passe sur les quatre teintes. Le fluo
   n'est pas une teinte de pilier — il porte --c-text-main à 15,3 — donc l'aplat y
   est légitime, et une seule règle sert les quatre pages sans recalcul.

   ⚠️ L'ENCRE EST ÉCRITE EN CLAIR, ET IL LE FAUT : la pastille sert justement dans
   les contextes où les rôles ont été remis à plat — sur l'aplat orange des cartes,
   --item-ton comme --text-accent valent déjà l'encre courante, et sur un fond
   sombre --text-strong vaudrait le blanc, illisible sur le fluo. La pastille porte
   son propre couple fond + encre, elle ne le tient d'aucun contexte.

   ⚠️ `width: fit-content` ET NON `align-self: start`. L'atome est une pile flex
   hors grille et une grille dedans : `align-self` y agit sur des axes différents,
   `justify-self` ne fait rien en flex. La largeur intrinsèque, elle, vaut des deux
   côtés. */
.item--pastille .item__surtitre {
  width: fit-content;
  padding: 0.25rem var(--sp-4);
  border-radius: var(--radius-full);
  background-color: var(--c-fluo);
  color: var(--c-text-main);
}

/* L'accroche reprend la chasse fixe des intitulés techniques du site, mais
   SANS capitales ni interlettrage élargi : c'est une phrase, pas une
   étiquette. En capitales, « Vos dashboards, configurés pour vos équipes »
   deviendrait illisible sur deux lignes. */
.item__accroche {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  line-height: var(--lh-snug);
  color: var(--item-encre, var(--item-ton, var(--text-accent)));
}

.item__corps {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  font-size: var(--fs-base);
  line-height: var(--lh-normal);
  color: var(--text-strong);
}

.item__corps p {
  margin: 0;
}

/* ---------- La chute d'une entrée ----------
   Une ligne qui referme l'entrée, sous les puces. Ajoutée le 20/08/2026 pour les
   deux cartes de rôles de /a-propos/, où la maquette écrit « Ce sont eux qui font
   **le gros du travail au contact des clients.** ».

   ⚠️ ELLE EST À L'ENCRE DISCRÈTE, ET C'EST SON ACCENT QUI PORTE. La phrase entière
   à la teinte de l'item aurait fait un second surtitre en bas de carte ; ici la
   ligne reste du corps de texte, et seul le `**gras**` de la fin s'allume — c'est
   lib/typographie.js qui le rend, et il prend --text-accent. Le partage est le même
   que partout : ce qui est à lire reste à l'encre courante.

   Le retrait haut la décolle des puces plus franchement que la gouttière de la pile,
   parce qu'elle change de nature — ce n'est plus un élément de la liste.

   ⚠️ `align-self: end`, ET C'EST LA DEMANDE ELLE-MÊME : « j'aimerais que les deux
   soient à la même distance du bas de leur boîte ». La chute occupe le DERNIER rang de
   la carte, donc son bas est à distance constante du bord — quel que soit le nombre de
   lignes de chaque chute.

   J'ai essayé `start` avant : ça aligne les PREMIÈRES lignes, ce qui n'est pas la même
   chose et laissait la chute la plus courte flotter loin du bas de sa carte.

   ⚠️ ET CETTE PROPRIÉTÉ NE SERT À RIEN TANT QUE LA CHUTE N'EST PAS AU BON NIVEAU
   D'IMBRICATION. C'est ce qui a fait perdre deux essais : rendue dans l'atome, elle
   était à deux niveaux de sous-grille et ne partageait pas son rang avec sa voisine.
   Elle est maintenant rendue par le bloc hôte, en enfant direct de la carte — voir
   `.texte-module__entrees--chute` et le champ `chute` de partials/item.njk. Hors
   grille, la propriété est simplement inerte. */
.item__chute {
  margin: var(--sp-3) 0 0;
  align-self: end;
  font-size: var(--fs-xs);
  line-height: var(--lh-normal);
  color: var(--text-muted);
}

/* ⚠️ LE GRAS PREND LA TEINTE D'ACCENT, demandé par Hugo le 20/08/2026. C'est le seul
   endroit du site où une mise en valeur passe par le `**gras**` de
   lib/typographie.js plutôt que par `((…))` — et c'est justifié ici : la phrase est
   une seule ligne dont la fin porte tout le sens (« Ce sont eux qui font **le gros du
   travail au contact des clients.** »), donc le gras ET la couleur vont ensemble.

   Le rôle et non une couleur en dur : dans une boîte sombre, `.on-dark` passe
   l'accent au fluo tout seul. */
.item__chute strong {
  color: var(--text-accent);
}

/* ⚠️ LA VARIANTE EN PASTILLE NE PORTE PLUS DE CORPS NI D'ENCRE : c'est la chip
   à l'intérieur qui les tient. Les déclarations de `.item__chute` qui restent
   utiles sont la marge et `align-self: end` — donc l'alignement des trois chutes
   au bas de leur carte, qui est tout l'intérêt du champ.

   La teinte est passée par `--pastille-ton` côté gabarit, pas ici : dans une
   boîte sombre, `.on-dark` a déjà fait passer --text-accent au fluo, et la chip
   suit sans qu'on ait deux couleurs à tenir accordées. */
.item__chute--pastille {
  font-size: inherit;
  color: inherit;
}

/* ⚠️ LA CHIP EST RAMENÉE À LA COTE DE LA MAQUETTE, comme `.offres__pastille` et
   pour la même raison : `.pastille--chip` se rend par défaut en
   `clamp(1rem … 1.375rem)`, soit 17,9 px ici, quand la maquette la pose à
   12,75 px. Au bas d'une carte, elle y pesait autant que le titre. */
.item__chute--pastille .pastille--chip {
  padding: var(--sp-1) var(--sp-4);
  font-size: var(--fs-sm);
}

/* ---------- Les puces ----------
   Une liste de phrases courtes sous la description — les cas d'usage de la
   section 04 de /integrations/.

   ⚠️ LA PUCE EST UN `::before`, PAS UN `list-style-type`. La teinte de la puce
   doit être celle de l'item et celle du texte reste l'encre courante : un
   marqueur de liste native hérite de la couleur du texte, et les deux ne peuvent
   pas différer sans `::marker` — dont `color` n'est pas encore fiable partout.

   Elle prend --item-encre : c'est un point de 5px, donc de l'ENCRE et non de
   l'ornement. Sur l'orange du pilier, l'aplat clair y serait invisible. */
.item__puces {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: var(--fs-base);
  line-height: var(--lh-normal);
  color: var(--text-strong);
}

.item__puces li {
  display: flex;
  gap: var(--sp-3);
}

/* ⚠️ LES PUCES À L'ENCRE D'ACCENT — `module.pucesEnAccent`, ajouté le 11/09/2026
   pour /tarifs/, dont la maquette pose ces listes en couleur.

   ⚠️ C'EST UNE OPTION ET NON LE DÉFAUT, et il faut que ça le reste : sur
   /integrations/ et /dictionnaire/, ces mêmes puces énumèrent des cas d'usage au
   fil d'un texte, et les colorer les ferait lire comme des liens. Sur /tarifs/
   elles énumèrent ce qui EST inclus — le contenu de l'offre, pas un commentaire.

   `--text-accent` et non une couleur en dur : dans la boîte sombre, `.on-dark`
   l'a déjà fait passer au fluo, donc la règle vaut des deux côtés sans qu'on ait
   deux teintes à tenir accordées. */
.texte-module__entrees--puces-accent .item__puces {
  color: var(--text-accent);
}

.item__puces li::before {
  content: "";
  /* `flex: none` et une remontée d'un demi-cadratin : la puce doit tomber sur la
     première ligne du texte, pas se centrer sur un paragraphe de deux lignes.
     `0.55em` place son centre à peu près sur la hauteur d'x — la seule métrique
     de fonte qu'on puisse approcher sans la connaître. */
  flex: none;
  width: 5px;
  height: 5px;
  margin-block-start: 0.55em;
  border-radius: var(--radius-full);
  background-color: var(--item-encre, var(--item-ton, var(--text-accent)));
}

/* Les CTA sont des boutons (voir partials/item.njk). En ligne tant qu'il y a la
   place, ferrés à gauche — et non centrés comme `.btn-group`, parce qu'ils
   s'alignent sur le texte de l'item, pas sur un axe de section.

   Il y avait ici un `.item__lien`, le CTA rendu en lien nu à la teinte du
   pilier. Retiré le 18/08/2026 avec le passage aux boutons. */
.item__liens {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-3);
}

/* ---------- L'invite ----------
   L'exemple de question posée à l'IA, sous un filet tireté. C'est du TEXTE
   TECHNIQUE — ce qu'on tape dans un assistant — d'où la chasse fixe, mais sans
   capitales ni interlettrage : c'est une phrase entière.

   --text-muted et non --text-strong : c'est le seul endroit du site où cette
   encre porte du texte suivi, et c'est justifié — l'invite est un EXEMPLE, en
   retrait de l'affirmation qu'elle illustre. La règle « le corps de texte est
   toujours à l'encre forte » vaut pour le corps de texte, et la description
   juste au-dessus le reste.

   Le filet est en `--border-subtle`, qui bascule tout seul en blanc translucide
   dans `.on-dark` : rien à reprendre si l'invite se retrouve un jour dans une
   boîte sombre. */
.item__invite {
  display: flex;
  gap: var(--sp-2);
  margin: 0;
  padding-block-start: var(--sp-4);
  border-top: 1px dashed var(--border-subtle);
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  line-height: var(--lh-snug);
  color: var(--text-muted);
}

/* Le chevron prend la teinte de l'item : c'est le seul signe coloré de la
   carte, et il marque le passage de l'énoncé à l'exemple. */
.item__invite-chevron {
  flex: none;
  color: var(--item-ton, var(--text-accent));
}

/* Teintes de pilier. Les jetons existent déjà dans _tokens.scss : on ne fait
   ici que les brancher sur --item-ton, que le marqueur et le lien lisent. */
/* ---------- La teinte de l'item ----------
   ⚠️ DEUX VARIABLES ET NON UNE, ET C'EST LA RÈGLE 10 DE MAQUETTE.md descendue
   dans l'atome :

     --item-ton    l'APLAT — ce qui n'est pas à lire : le masque du marqueur en
                   forme de marque (`--shape-color`), le cartouche.
     --item-encre  l'ENCRE — ce qui est à lire : le marqueur « 01 », le surtitre,
                   l'accroche. Absente, elle retombe sur --item-ton.

   Sur trois piliers les deux valeurs sont égales, donc la distinction ne se voit
   pas. Sur `integrations` elle est indispensable : #f0a22e ne donne que 2,01 sur
   le crème et 2,12 sur le blanc du cadre — un tiers sous les niveaux acceptés
   (2,90 pour le vert, 2,82 pour le rose). C'est --c-pilier-integrations-encre
   (#cc6207, 3,74) qui porte le texte, et l'orange du pilier qui reste sur les
   formes : « les formes sur la page doivent être dans l'orange original » —
   Hugo, 20/08/2026.

   ⚠️ CORRECTIF DU 20/08/2026 : cette ligne posait l'orange clair sur les DEUX
   emplois. Les surtitres « FINANCE / RETAIL / DIRECTION » de la section 04 de
   /excel-sheets/ sortaient donc à 2,12 sur le blanc de leur carte, alors que
   l'en-tête de cette page annonce l'inverse (« ils prennent ici l'orange foncé
   du pilier »). Le code disait le contraire de son intention, sans le moindre
   signal — c'est exactement le piège que la règle 10 décrit. */
.item--dictionnaire {
  --item-ton: var(--c-pilier-dictionnaire);
}

.item--plateforme {
  --item-ton: var(--c-pilier-plateforme);
}

.item--connecteur {
  --item-ton: var(--c-pilier-connecteur);
}

.item--integrations {
  --item-ton: var(--c-pilier-integrations);
  --item-encre: var(--c-pilier-integrations-encre);
}

/* Sur écran étroit, seule la respiration entre les deux rangs se resserre.

   Cette requête faisait auparavant passer le marqueur AU-DESSUS de l'intitulé :
   il occupait sa propre colonne, et côte à côte un titre de quelques mots se
   replierait sur une colonne trop maigre. Ce n'est plus le cas — le marqueur
   partage la ligne du titre et n'y prend que sa largeur de forme, donc il n'y a
   plus de colonne à sacrifier. */
@media (max-width: 40rem) {
  .item {
    gap: var(--sp-2);
  }
}
/* ---------- Formes de marque ----------
   Les trois exports Figma (citron, écu, losange). Règles de la marque : on
   n'utilise que ces trois formes, elles sont interchangeables, et on peut
   les faire tourner.

   Le SVG est posé en MASQUE et non en <img> : la couleur vient alors du
   CSS, donc un seul fichier par forme sert toutes les teintes. C'est ce
   qui permet les losanges bleus de la section « approche » sans dupliquer
   les exports.

   Contrepartie : un masque n'a pas de taille intrinsèque, la hauteur ne se
   déduit plus de la largeur. C'est le rôle de --shape-ratio, posé par le
   partial partials/forme.njk qui connaît les viewBox des exports.

   La section appelante ne fournit que --shape-w, --shape-rot et un
   ancrage. La rotation reste en CSS même quand GSAP anime la forme : le
   script lit --shape-rot comme position de repos et oscille autour. */
.forme {
  display: block;
  width: var(--shape-w, 100%);
  aspect-ratio: var(--shape-ratio);
  background-color: var(--shape-color, var(--c-fluo));
  transform: rotate(var(--shape-rot, 0deg));
  mask-image: var(--shape-src);
  mask-size: 100% 100%;
  mask-repeat: no-repeat;
  -webkit-mask-image: var(--shape-src);
  -webkit-mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat;
}

.forme--fluo {
  --shape-color: var(--c-fluo);
}

.forme--bleu {
  --shape-color: var(--c-blue);
}

/* Bleu profond, pour une forme posée sur un fond clair qui doit rester
   lisible sous un texte blanc — l'avatar des témoignages. */
.forme--bleu-nuit {
  --shape-color: var(--c-blue-dark);
}

/* Le miroir de la précédente : une forme posée sur un fond SOMBRE, où les quatre
   autres teintes s'effacent. Sur le violet du pilier connecteur, mesurés — le violet
   lui-même donne 1,14 (invisible comme couleur, visible seulement comme bord), le
   fluo 4,05, le bleu nuit 3,53 ; le blanc donne 4,72. C'est la seule qui détache
   franchement une forme de ce fond-là.

   Et elle porte de l'ENCRE, ce qui est sa raison d'être : les trois marques
   d'assistants de l'orbite de /connecteur-ia/ y sont posées en <img>, chacune avec sa
   couleur de marque, ce qu'aucun aplat de pilier n'aurait accepté. C'est la règle de
   `.item__logo` — la couleur d'une marque tierce n'est pas à nous pour la faire
   dialoguer avec un fond teinté. */
.forme--blanc {
  --shape-color: var(--c-white);
}

/* L'ACCENT DE LA PAGE, ET C'EST UN RÔLE, PAS UNE COULEUR. Les trois teintes
   au-dessus nomment un ton du nuancier ; celle-ci lit --text-accent, que
   `.ton-page--*` réaffecte au ton du pilier et que `.on-dark` met au fluo. Une
   forme qui la porte suit donc sa page sans que la page ait à la nommer — c'est
   ce qui rend les formes de décor de /plateforme/ rouges et celles d'une page de
   dictionnaire vertes, avec la même donnée.

   ⚠️ ELLE NE DOUBLE PAS `.forme--bleu`, MÊME SI LES DEUX COÏNCIDENT SUR UNE PAGE
   SANS PILIER : `--c-blue` est un ton, --text-accent une intention. Écrire
   `teinte: bleu` dans une donnée pour obtenir une forme rouge serait exactement le
   nom qui ment. */
.forme--accent {
  --shape-color: var(--text-accent);
}

/* L'ORNEMENT DE LA PAGE — l'autre rôle, et il faut les deux. `.forme--accent` lit
   --text-accent, qui est de l'ENCRE ; celle-ci lit --c-blue, qui est de
   l'ORNEMENT. Les deux rôles sont documentés à cet endroit dans _tokens.scss, et
   sur trois des quatre piliers ils portent la même valeur — la distinction ne se
   voit donc que sur /excel-sheets/, le seul pilier qui les sépare : l'encre y est
   l'orange brûlé #cc6207, l'ornement l'orange original #f0a22e.

   ⚠️ C'EST UNE CONSIGNE D'HUGO DU 20/08/2026 — « les formes sur la page doivent
   être dans l'orange original ». Un grand aplat de décor posé en fond de section
   ne porte rien à lire : le foncer n'apporte aucune lisibilité et éteint le décor.
   C'est le raisonnement écrit dans `.ton-page--integrations`, appliqué.

   ⚠️ ELLE NE DOUBLE PAS `.forme--bleu`, POUR LA MÊME RAISON QUE `.forme--accent` :
   `--c-blue` est un ton, l'ornement une intention. Écrire `teinte: bleu` dans une
   donnée pour obtenir une forme orange serait le nom qui ment ; c'est la macro qui
   choisit la teinte, jamais le fichier de contenu.

   ⚠️ LE REPLI SUR FOND SOMBRE EST EXPLICITE, et c'est ce que `.forme--accent`
   obtenait gratuitement : --text-accent passe au fluo sous `.on-dark`, --c-blue non.
   Sans cette seconde règle, une forme d'ornement dans une boîte sombre y prendrait
   le bleu (ou le ton du pilier) et s'y éteindrait. Aucune page ne le fait
   aujourd'hui — la règle est là pour que la première ne le découvre pas. */
.forme--ornement {
  --shape-color: var(--c-blue);
}

.on-dark .forme--ornement {
  --shape-color: var(--c-fluo);
}

/* ---------- Forme remplie d'une trame ----------
   La forme découpe l'extérieur, la trame de points découpe l'intérieur. Deux
   masques sur deux éléments imbriqués : un masque s'applique à tout le
   sous-arbre, donc l'imbrication donne l'intersection.

   Le grain porte deux couches de masque composées en intersection : les
   points, pavés à leur taille naturelle, et un dégradé qui module la
   densité. C'est ce partage qui permet à un seul fichier de servir toutes
   les formes — le motif généré est volontairement uniforme, la montée en
   densité appartient au CSS (voir buildHalftoneFond dans eleventy.config.js).

   `mask-size` du motif est en PIXELS et non en %, pour que le grain reste
   identique quelle que soit la taille de la forme. En %, la plus grande
   forme aurait des points deux fois plus gros que la plus petite. */
.forme-trame {
  position: relative;
  display: block;
  width: var(--shape-w);
  aspect-ratio: var(--shape-ratio);
  transform: rotate(var(--shape-rot, 0deg));
  mask-image: var(--shape-src);
  mask-size: 100% 100%;
  mask-repeat: no-repeat;
  -webkit-mask-image: var(--shape-src);
  -webkit-mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat;
}

/* Masque à deux couches composées (points + dégradé) : les mots-clés de
   `mask-composite` ne sont pas les mêmes entre la version standard et la
   version -webkit-, les faire cohabiter casse la composition. Reste donc
   écrit à la main — voir aussi sections/_texte-illustre.scss
   (.texte-illustre__grille) pour le même cas. */
.forme-trame__grain {
  position: absolute;
  inset: 0;
  background-color: var(--shape-color, var(--c-fluo));
  /* Le dégradé s'éteint des DEUX côtés, avec un plateau au milieu. La
     maquette ne fait pas décroître la densité d'un bord à l'autre : elle
     culmine au cœur de la forme et se dilue vers ses deux bords, ce qui la
     fait naître du fond plutôt que d'y être posée. Une rampe simple donnait
     une forme dense collée à son arête. */
  mask-image: url("/assets/img/halftone-fond.svg"), linear-gradient(var(--grain-angle, 90deg), transparent, black var(--grain-debut, 32%), black var(--grain-fin, 58%), transparent);
  mask-size: 504px 504px, 100% 100%;
  mask-repeat: repeat, no-repeat;
  mask-composite: intersect;
}

/* =========================================================================
   DÉCOR-FORMES — jusqu'à trois formes de marque en trame dans les deux bandes
   que la composition d'une section laisse libres de part et d'autre du conteneur,
   deux à gauche et une à droite.
   Atome : partials/formesDecor.njk, qui porte le balisage et le pourquoi des deux
   rendus. Ici : la géométrie.

   Écrite d'abord dans sections/_texte-module.scss le 25/08/2026 — Hugo, en trois
   temps : « un chart matriciel halftone à droite de la section », puis, dessin en
   main, « les motifs remplissent à peu près les zones rouges », puis « remplace-les
   par des shapes halftone classiques en fait ». Les cotes ci-dessous sont relevées
   sur ce dessin, pris à 1950 px de fenêtre. Passée à TROIS le 26/08/2026 pour
   « Une infrastructure… » de /plateforme/ — « 3 formes de part et d'autre » — et
   EXTRAITE ICI le même jour quand `comparatif` a demandé le même décor.

   ⚠️ EXTRAITE PLUTÔT QUE RECOPIÉE : ce qui se partage n'est pas l'apparence — les
   atomes `formeTrame` et `forme` la portent — mais la GÉOMÉTRIE. Deux feuilles de
   section auraient tenu deux jeux de cotes en accord sans rien pour signaler leur
   divergence. C'est le parti de `.quinconce` et de `.chemin` : la primitive porte
   le rythme, le bloc hôte garde ses marges et son titre. Ce qui reste dans
   sections/_texte-module.scss est ce que la primitive ne peut pas savoir : la
   composition `rangee`, qui borne le côté gauche autrement, et l'ancrage `tableau`,
   où les formes enjambent les bords du PANNEAU et non ceux de la page.

   ---------------------------------------------------------------------
   ⚠️ CE SONT DES FORMES DE MARQUE, PAS LES GRAPHIQUES DE partials/motif.njk. Ceux-là
   sont l'objet spécial des boîtes sombres — le donut du dictionnaire, les quatre
   briques de /produit/. Le décor en trame du SITE, ce sont citron, écu et losange, et
   `coeur` les pose déjà exactement comme ici : en fluo, sur le crème, débordant de
   leur section. Les deux graphiques qui ont occupé ces deux zones quelques heures le
   25/08/2026 sont partis avec l'arbitrage de teinte qui allait avec (le vert sombre
   du pilier, 5,29 sur le crème) : une forme en trame n'a pas de séries à distinguer,
   donc rien à mesurer les unes contre les autres.

   ⚠️ CE QUI LES COUPE EST LE BORD DE LA PAGE, d'où l'`overflow-x: clip` sur l'hôte —
   sans lui le débord ouvrirait une barre de défilement horizontale. `clip` et non
   `hidden` : il ne force pas l'autre axe, donc rien ne devient scrollable
   verticalement, et le débord vertical d'un décor voisin reste intact.

   ---------------------------------------------------------------------
   L'ARÊTE INTÉRIEURE, ET C'EST LA SEULE CHOSE QUI LES TIENT À L'ÉCART DU TEXTE.

   `--forme-arete` est la même expression des deux côtés : le bord EXTÉRIEUR du
   conteneur à sa largeur maximale, moins de combien la forme y entre. Un côté la
   consomme en `inset-inline-end`, l'autre en `inset-inline-start` — d'où deux
   règles courtes au lieu de deux géométries.

   Elle se mesure depuis le bord du conteneur et non depuis le texte, et ce n'est
   pas un détail : le rembourrage du conteneur est un `clamp()`, donc une cote
   prise sur le texte changerait à chaque largeur. Prise sur le conteneur, elle
   donne un DÉGAGEMENT CONSTANT à toute largeur — 32 px du bord de la boîte de
   contenu dans le cas général, 54 px du flanc du schéma sous `rangee`.

   ⚠️ L'AVANCÉE EST DONC DÉRIVÉE ET NON NOMMÉE : 1rem par défaut, relevée par l'hôte
   quand sa composition borne un côté par autre chose que le conteneur (voir
   `rangee` dans sections/_texte-module.scss). Recopier cette valeur-là dans la règle
   de base poserait la forme PAR-DESSUS le contenu de toute section sans visuel.

   ⚠️ LE DÉFAUT EST DANS LE REPLI DE `var()` ET NON DÉCLARÉ, pour qu'un réglage posé
   par l'hôte ne soit pas masqué — même règle que `--btn-primary-bg`.

   ⚠️ AUCUNE ROTATION, ET C'EST POUR CETTE RAISON. `coeur` fait tourner ses trois
   formes de 8 à 14 degrés, et c'est la conduite habituelle ; ici une rotation
   grossirait l'encombrement horizontal de la forme d'environ 7 % de sa largeur —
   soit 33 px pour celle de gauche, sur 54 px de dégagement. La règle « jamais deux
   fois la même forme dans la même orientation côte à côte » est de toute façon
   tenue par le choix de formes différentes — et avec trois clés déclarées, les trois
   formes du site y passent.

   ⚠️ LE DÉCOR PASSE SOUS LE TEXTE, ET C'EST UN ORDRE DE CALQUES DEPUIS LE
   26/08/2026 — demande d'Hugo : « les shapes ne doivent pas passer devant le
   texte ». Avant, rien n'avait de `z-index` et c'était l'ÉPINGLAGE seul qui
   interdisait le recouvrement : chaque cote était calculée pour que la forme
   n'atteigne pas le texte, donc grossir une forme ou déplacer un contenu pouvait
   ramener le défaut sans que rien ne le signale.

   ⚠️ ET LA RAISON POUR LAQUELLE UN `z-index: -1` NE MARCHAIT PAS ÉTAIT INCOMPLÈTE.
   Ce qui l'envoyait sous l'aplat opaque de la section, ce n'est pas l'aplat : c'est
   que la section n'était PAS un contexte d'empilement (`position: relative` +
   `z-index: auto` n'en crée pas), donc la forme remontait jusqu'au contexte racine.
   Un `isolation: isolate` suffit à la retenir : dans ce contexte, un `z-index: -1`
   peint à l'ÉTAPE 2, après le fond de la section (étape 1) et avant tout son
   contenu. C'est la construction que l'ancrage `tableau` emploie sur `__corps`.

   Ce qui reste vrai de l'épinglage : il garde les formes hors du texte à l'œil, et
   c'est lui qui fait qu'on en voit quelque chose. Le calque n'est qu'un filet de
   sécurité — mais il rend le défaut impossible au lieu d'évitable.

   ⚠️ AUCUN POINT DE RUPTURE N'EST PUBLIÉ, et la formule s'en charge : sous la
   largeur maximale du conteneur, `50% + C / 2` dépasse la largeur de la section et
   la forme sort d'elle-même de l'écran, où le `clip` la mange. Elle s'efface au lieu
   de dégrader — le service que rendait un `mq-up("xl")` explicite, en une règle de
   moins. Quand les deux côtés avancent d'1rem, les trois formes s'en vont ENSEMBLE
   vers 1340 px ; une avancée inégale les fait partir à des largeurs différentes.

   Largeur de bande libre, en fonction de la gouttière (fenêtre − conteneur) / 2 :

                     1340 px      1440 px      1600 px      1950 px
     gauche/rangee   166 px       216 px       296 px       471 px
     gouttière         0 px        48 px       128 px       303 px

   ---------------------------------------------------------------------
   LA TAILLE EST UNE PART DE LA LARGEUR DE LA SECTION, comme dans `coeur`, et non
   une part de sa hauteur. Deux raisons. La forme doit ATTEINDRE le bord de la page,
   ce qui est une affaire de largeur ; et une part de hauteur la ferait grossir sur
   un écran étroit, où la section s'allonge par replis de texte — exactement quand la
   bande libre se rétrécit.

   Elle passe donc par `--shape-w`, le bouton de l'atome, et la HAUTEUR découle du
   rapport de la forme (`aspect-ratio`, posé en ligne par partials/forme.njk depuis
   le viewBox de l'export). Rien à déclarer pour elle.

   ⚠️ ET C'EST LA BANDE QUI PORTE LA COTE DE LARGEUR, pas la forme : `--shape-w`
   vaut 100 % dans la bande de droite, où la forme est seule. Ça évite d'avoir à
   battre le `position: relative` de l'atome par l'ordre du fichier, appui que
   l'ancienne règle prenait et dont la panne aurait été spectaculaire et muette.

   Tailles obtenues à 1950 px, en regard des zones du dessin du dictionnaire :

     gauche  citron  468 × 454 px   zone 471 × 362
     droite  écu     682 × 724 px   zone 303 × 718

   ⚠️ UNE FORME QUASI CARRÉE NE PEUT PAS REMPLIR LES DEUX AXES D'UNE ZONE EN 1,3.
   Celle de gauche déborde donc en HAUTEUR, et c'est le bon axe à laisser filer :
   rien ne se trouve à gauche du schéma bridé. Celle de droite tombe juste.

   ⚠️ LES DEUX FORMES DE GAUCHE SONT PLUS PETITES QUE CELLE DU DESSIN (19 % et 21 %
   contre 24 %), et ce n'est pas un réglage de goût : elles partagent une bande, donc
   la somme de leurs hauteurs doit tenir dans celle de la section. Les grossir, c'est
   les faire se rejoindre — et deux trames superposées fondent en une tache.

   ---------------------------------------------------------------------
   LE GRAIN. La trame de `formeTrame` culmine au coeur de la forme et se dilue vers
   ses deux bords (voir `.forme-trame__grain`). Ici une bonne part de la forme est
   hors écran : le plateau est donc décalé vers la moitié VISIBLE, sinon la densité
   tombe du côté qu'on ne voit pas et il ne reste qu'un bord dilué.

   L'axe reste à 90deg des deux côtés — 0 % est le bord gauche, 100 % le bord droit —
   et ce sont les bornes du plateau qui sont en miroir. Faire tourner l'axe à 270deg
   aurait marché aussi, au prix d'un sens de lecture inversé dans les pourcentages.
   ========================================================================= */
/* L'HÔTE SE CÂBLE TOUT SEUL, et c'est ce qui dispense les blocs de déclarer quoi
   que ce soit : `:has(> …)` ne remonte que d'un cran, donc cette règle ne peut
   attraper que la section qui contient effectivement les bandes.

   ⚠️ LE CROCHET EST LA BANDE ET NON LA FORME. Sur les formes, il attraperait aussi
   l'ancrage `tableau` de `texte-module`, dont les formes sont enfants directes de
   `__corps` : `__corps` y gagnerait un `overflow-x: clip` qui trancherait justement
   les moitiés extérieures qu'il doit montrer.

   L'arête contient `var(--forme-avancee)`, qui n'est PAS résolue ici : une propriété
   personnalisée est substituée là où elle est EMPLOYÉE, donc chaque bande lit sa
   propre avancée. D'où une seule expression pour deux géométries.

   `position: relative` et `overflow-x: clip` sont redéclarés ici même si les hôtes
   actuels les portent déjà pour leurs propres raisons : la primitive ne peut pas le
   supposer, et à valeur égale il n'y a pas de conflit. */
:has(> .decor-formes__bande) {
  --forme-arete: calc(
    50% + (var(--container-max) + var(--container-pad) * 2) / 2 -
      var(--forme-avancee, 1rem)
  );
  position: relative;
  overflow-x: clip;
  /* Le contexte d'empilement qui permet aux formes de passer DERRIÈRE le texte.
     Voir la note « le décor passe sous le texte » plus haut : sans lui, un
     `z-index: -1` remonte jusqu'au contexte racine et se range sous l'aplat opaque
     de la section, où il devient invisible. */
  isolation: isolate;
}

/* Les deux bandes partagent tout sauf leur flanc et leur largeur. Le `flex` en
   colonne centrée est ce qui rend le recouvrement impossible à gauche — voir l'atome
   — et il ne coûte rien à droite, où la bande n'a qu'une forme. */
.decor-formes__bande {
  position: absolute;
  /* Sous le texte. Le contexte d'empilement est sur l'hôte — voir plus haut. */
  z-index: -1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--sp-6);
}

.decor-formes__bande--gauche {
  inset-block: 0;
  inset-inline-end: var(--forme-arete);
  /* La largeur de la gouttière de page à 1950 px, à peu de chose près. Au-dessous,
     la bande sort d'elle-même de l'écran avec l'arête — aucun point de rupture. */
  inline-size: 16%;
}

/* ⚠️ LES DEUX NE SONT PAS ALIGNÉES SUR LE MÊME FLANC, et c'est la conduite de
   `coeur` et de `temoignages` : à pas régulier, l'œil attrape l'alignement et la
   paire se lit comme une frise au lieu d'un décor. */
.decor-formes__bande--gauche > .decor-formes__forme {
  /* Le plateau de densité vers la droite : c'est la moitié visible. */
  --grain-debut: 55%;
  --grain-fin: 92%;
}

.decor-formes__bande--gauche > .decor-formes__forme--haut-gauche {
  --shape-w: 88%;
  align-self: flex-start;
}

.decor-formes__bande--gauche > .decor-formes__forme--bas-gauche {
  --shape-w: 100%;
  align-self: flex-end;
}

/* La bande de droite : étroite, et haute de presque toute la section. Elle n'a
   qu'une forme, donc sa largeur EST la cote de la forme. */
.decor-formes__bande--droite {
  inset-block-start: 9%;
  inset-inline-start: var(--forme-arete);
  inline-size: 35%;
}

/* ⚠️ L'APLAT SE COTE AUTREMENT QUE LA TRAME. Le 35 % ci-dessus était justifié PAR la
   trame, et cette justification tombe avec elle : la forme débordait largement hors
   écran parce que sa moitié invisible était sa moitié DILUÉE — c'est tout le propos du
   plateau de grain décalé. Un APLAT n'a pas de moitié diluée : au même 35 % il donne
   une masse pleine coupée net par le bord de page. Il est donc ramené à la largeur de
   la bande, où les trois formes se lisent comme une famille dans les deux gouttières.

   Aucune page n'emploie l'aplat dans cet ancrage aujourd'hui — les deux qui le
   demandent passent par `ancrage: tableau`. La règle reste pour que la première ne
   redécouvre pas l'arbitrage. */
.decor-formes__bande--droite.decor-formes__bande--plein {
  inline-size: 15%;
}

.decor-formes__bande--droite > .decor-formes__forme {
  --shape-w: 100%;
  /* Le plateau vers la gauche, moitié visible de ce côté-ci. */
  --grain-debut: 8%;
  --grain-fin: 45%;
}

/* =========================================================================
   MOTIF — un graphique en halftone, décor d'une boîte sombre.
   Atome : partials/motif.njk, qui porte la géométrie et le pourquoi du procédé.

   L'atome possède l'APPARENCE — la trame et le calcul des trois séries. L'hôte
   possède le PLACEMENT et la teinte. Voir `.texte-panneau__motif` pour le
   premier hôte, et /labo/graphiques-halftone/ pour la planche des onze dessins.

   ⚠️ UNE SEULE COUCHE DE MASQUE, donc la paire `-webkit-` est ici sans danger.
   L'avertissement de `.forme-trame__grain` juste au-dessus ne porte que sur
   `mask-composite`, dont les mots-clés diffèrent entre les deux syntaxes ; il
   n'y a pas de composition ici, les tracés du SVG portent déjà la forme.

   La tuile est posée EN PIXELS, comme partout ailleurs sur le site : le grain
   ne change donc pas avec la taille du motif, et c'est ce qui le garde de la
   même famille que le donut et que le grain des formes de marque.
   ========================================================================= */
.motif {
  /* ---------------------------------------------------------------
     LES TROIS SÉRIES, à partir de DEUX variables que pose l'hôte.

     `--motif-base` est la série mise en avant, `--motif-fond` le fond de la
     boîte sur laquelle le motif est posé. Les deux séries qui reculent sont la
     base MÉLANGÉE AU FOND, et non la base éclaircie vers le blanc : mélangée au
     fond, une série recule au lieu de changer de couleur, et les trois restent
     de la même famille. C'est la formule du donut (_texte-module.scss), reprise
     telle quelle, y compris ses deux proportions.

     ⚠️ LES DÉFAUTS SONT DANS LE `var()`, JAMAIS DÉCLARÉS ICI. Une propriété
     posée SUR l'élément bat la même propriété héritée d'un ancêtre : écrite en
     déclaration, `--motif-base` masquerait la valeur que la boîte hôte pose, et
     le motif resterait fluo sans la moindre alerte. Même règle que
     `--btn-primary-bg`.

     Le repli fluo-sur-bleu-nuit n'est là que par principe : les cinq hôtes
     possibles du site posent tous leur propre paire. */
  --motif-t2: color-mix(
    in srgb,
    var(--motif-base, var(--c-fluo)) 78%,
    var(--motif-fond, var(--c-blue-dark))
  );
  --motif-t3: color-mix(
    in srgb,
    var(--motif-base, var(--c-fluo)) 52%,
    var(--motif-fond, var(--c-blue-dark))
  );
  display: block;
  mask-image: url("/assets/img/halftone-fond.svg");
  mask-size: 504px 504px;
  mask-repeat: repeat;
  -webkit-mask-image: url("/assets/img/halftone-fond.svg");
  -webkit-mask-size: 504px 504px;
  -webkit-mask-repeat: repeat;
}

/* Remplissages et tracés en DEUX familles séparées, et non une classe qui
   poserait `fill` ET `stroke` : une règle CSS bat l'attribut de présentation
   `fill="none"`, donc une classe mixte remplirait le ruban de la courbe. */
.motif__s1 {
  fill: var(--motif-base, var(--c-fluo));
}

.motif__s2 {
  fill: var(--motif-t2);
}

.motif__s3 {
  fill: var(--motif-t3);
}

.motif__l1 {
  fill: none;
  stroke: var(--motif-base, var(--c-fluo));
}

.motif__l2 {
  fill: none;
  stroke: var(--motif-t2);
}

.motif__l3 {
  fill: none;
  stroke: var(--motif-t3);
}

/* ---------------------------------------------------------------
   LA DÉCOUPE — ce qui permet à deux tracés de SE SUPERPOSER sans fondre l'un
   dans l'autre. Demandée par Hugo le 25/08/2026 pour les bulles : « les bulles
   peuvent même se superposer. Dans ce cas là on affiche que les points d'une des
   bulles et on vient découper l'autre. »

   ⚠️ LE PROBLÈME N'EST PAS L'ORDRE DE PEINTURE, C'EST LA TRAME. Le masque est
   posé sur le <svg> ENTIER, donc tous les tracés partagent la MÊME grille de
   points : deux disques qui se chevauchent y posent exactement les mêmes points,
   et leur intersection ne se lit plus du tout — même teinte, ils fusionnent en une
   seule tache ; teintes différentes, la limite est une frontière de couleur sans
   arête. Empiler dans le bon ordre ne répare ni l'un ni l'autre.

   Le remède est donc un VIDE : le tracé porte un liseré de la couleur du FOND,
   peint SOUS son propre remplissage (`paint-order: stroke`). Il creuse ainsi une
   saignée dans tout ce qui a été peint avant lui, et n'entame jamais son propre
   remplissage. Sur le fond lui-même le liseré est invisible — du fond sur du fond.

   Trois conséquences à connaître :

     • ELLE REPOSE SUR L'INVARIANT `--motif-fond` = LE FOND RÉEL. C'est déjà la
       règle de l'atome (les séries qui reculent sont la base mélangée à ce fond),
       et les trois hôtes la tiennent — `texte-panneau` lit `--panneau-ton`,
       `onglets-metiers` le fond de sa boîte, l'atelier sa propre variable. Un
       hôte qui déclarerait autre chose verrait apparaître des anneaux.

     • L'ORDRE DU TABLEAU EST L'ORDRE DE PEINTURE, donc l'ordre de découpe : un
       tracé creuse ses PRÉDÉCESSEURS, jamais ses suivants. Les bulles sont
       rangées du plus grand au plus petit pour cette seule raison.

     • LA SAIGNÉE DOIT AVOIR DE LA SURFACE, comme tout le reste du motif : elle
       est faite de points manquants, donc plus fine qu'un pas de trame elle peut
       tomber entre deux points et ne rien montrer. 16 unités de liseré (8 de part
       et d'autre) valent environ un pas à l'échelle où le motif est rendu. */
.motif__decoupe {
  stroke: var(--motif-fond, var(--c-blue-dark));
  stroke-width: var(--motif-decoupe, 16);
  paint-order: stroke;
}

/* ---------- En-tête ---------- */
.site-header {
  /* Le chrome est exempté des 85 % du site — voir `--echelle-chrome` dans
     _tokens.scss pour le mécanisme et ses limites. */
  zoom: var(--echelle-chrome);
  position: sticky;
  top: 0;
  z-index: var(--z-header);
  background-color: color-mix(in srgb, var(--bg-page) 85%, transparent);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid transparent;
  transition: border-color var(--dur-base) var(--ease-out), background-color var(--dur-base) var(--ease-out);
}

/* Une fine bordure n'apparaît qu'une fois la page défilée, pour décoller
   l'en-tête du contenu sans l'alourdir en haut de page. */
.site-header[data-scrolled=true] {
  border-bottom-color: var(--border-subtle);
}

/* ⚠️ ET ELLE APPARAÎT AUSSI TANT QU'UN DÉROULANT EST OUVERT, même en haut de
   page. Le panneau ne porte plus de filet en haut (voir `.site-nav__menu`) :
   sans ce trait il resterait ouvert par le dessus. Il court sur toute la
   largeur, ce qui est le propos — c'est l'arête de l'en-tête, et le panneau
   s'accroche dessous.

   Toujours pas une ligne de JS : `:has()` fait le lien du panneau vers la
   barre, que ni `:focus-within` ni `:hover` posé sur l'en-tête ne pourraient
   distinguer d'un simple survol du logo. Le survol reste enfermé dans
   `hover-hover`, comme l'ouverture du panneau elle-même. */
.site-header:has(.site-nav__groupe:focus-within) {
  border-bottom-color: var(--border-subtle);
}

@media (hover: hover) {
  .site-header:has(.site-nav__groupe:hover) {
    border-bottom-color: var(--border-subtle);
  }
}
.site-header__inner {
  /* ⚠️ POSITIONNÉ DEPUIS LE 31/08/2026 : c'est lui qui sert de bloc conteneur au
     panneau pleine mesure de « Solution ». Il porte le `.container`, donc le
     panneau s'aligne exactement sur la mesure du reste de la page. Voir
     `.site-nav__menu--large`. */
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-6);
  min-height: var(--h-entete-brute);
}

.site-logo {
  font-family: var(--font-serif);
  font-size: 1.375rem;
  font-weight: var(--fw-bold);
  letter-spacing: var(--ls-tight);
  color: var(--text-strong);
  flex-shrink: 0;
}

/* ---------- Navigation ---------- */
.site-nav {
  display: flex;
  align-items: center;
  gap: var(--sp-1);
}

.site-nav__link {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1);
  padding: var(--sp-2) var(--sp-3);
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  color: var(--text-strong);
  border-radius: var(--radius-sm);
  transition: color var(--dur-fast) var(--ease-out);
}

.site-nav__link:hover {
  color: var(--c-blue);
}

/* ⚠️ « CONNEXION » N'EXISTE DANS LA NAV QU'AU DOIGT. Au-dessus de `lg` c'est le
   bouton fantôme de `.site-header__actions` qui la porte ; rendue ici aussi,
   l'entrée serait DOUBLÉE dans la barre. Sous `lg` le rapport s'inverse : c'est
   le bouton qui s'efface et cette rangée qui ouvre le menu déplié, comme dans
   la maquette. Exactement une des deux copies à chaque largeur. */
.site-nav__link--connexion {
  display: none;
}

.site-nav__chevron {
  width: 0.75rem;
  height: 0.75rem;
  transition: transform var(--dur-fast) var(--ease-out);
}

/* ---------- Le déroulant de « Solution » ----------

   Aucun JS : le panneau s'ouvre au survol et au focus clavier. Le survol est
   enfermé dans `hover-hover` pour qu'un écran tactile ne le laisse pas ouvert
   après un appui ; `:focus-within` reste hors du garde, c'est lui qui sert
   au clavier sur toutes les machines.

   Sur mobile (voir la bascule en bas de fichier) rien de tout ceci ne
   s'applique : le panneau redescend dans le flux, déplié. */
/* ⚠️ LE GROUPE PREND TOUTE LA HAUTEUR DE LA BARRE, ET C'EST CE QUI DÉCOLLE LE
   PANNEAU. Il est le bloc conteneur du déroulant, qui s'ouvre à `top: 100%` ;
   à la hauteur de son seul lien, ce 100 % tombait au bas du LIEN — donc à
   l'intérieur de l'en-tête, et le panneau chevauchait la barre sur une dizaine
   de pixels. Étiré (`align-self: stretch` sur la nav puis sur le groupe), le
   100 % tombe sur le bord bas de l'en-tête : le panneau commence là où la
   barre finit. Le lien, lui, reste centré — c'est le `align-items` du groupe
   qui s'en charge, aucune cote n'est écrite nulle part.

   La bascule mobile n'a rien à défaire : `.site-nav` y passe en `position:
   absolute`, où `align-self` n'a plus de prise. */
.site-nav {
  align-self: stretch;
}

.site-nav__groupe {
  position: relative;
  align-self: stretch;
  display: flex;
  align-items: center;
}

.site-nav__menu {
  position: absolute;
  /* Le pixel est celui de la bordure basse de l'en-tête, qui apparaît au
     défilement : sans lui, le filet du panneau se poserait juste dessus et les
     deux traits n'en feraient qu'un, épais de deux pixels. */
  top: calc(100% + 1px);
  inset-inline-start: 0;
  z-index: 1;
  /* La maquette donne 331 px pour un texte de 24 px ; le panneau est ramené à
     l'échelle de la nav (voir .site-nav__sous-lien), donc sa largeur avec —
     13rem, soit les mêmes proportions. C'est un PLANCHER, pas une largeur :
     le panneau s'élargit pour son contenu.

     ⚠️ MAIS IL LUI FAUT UNE LARGEUR ÉCRITE DEPUIS LE 31/08/2026, et un `max-width`
     n'y suffit PAS. Un élément en absolu se dimensionne à son contenu : avec les
     descriptions en phrases et un `white-space: normal`, il se rétractait à
     208 px en cassant chaque phrase sur quatre lignes. Un plafond ne relève pas
     un plancher — seule une largeur le fait.

     26rem est la cote de « Ressources » ; « Solution » s'en affranchit par
     `--large`, plus bas. Le `min-width` reste : il ne sert plus à personne
     aujourd'hui, mais il protège un futur déroulant sans description. */
  min-width: 13rem;
  inline-size: 26rem;
  /* Aucune marge intérieure : les rangées vont d'un filet à l'autre, et c'est
     leur propre trait qui les sépare. */
  padding: 0;
  /* ⚠️ LE PANNEAU PORTE LE TON DE L'EN-TÊTE, MAIS OPAQUE — demandé par Hugo le
     27/08/2026. Il prolonge la barre au lieu de se poser devant elle : même
     crème, même filet discret. Le blanc et le bleu saturé #2340ff de la
     maquette (Figma 16:8) partent avec, c'est l'habillage de `.cadre` et il
     faisait du déroulant un objet étranger à la barre qui l'ouvre. Les coins
     restent droits et il n'y a toujours aucune ombre : sur ces deux points
     l'en-tête et l'ancien panneau disaient déjà la même chose.

     ⚠️ NE PAS Y REMETTRE LE `backdrop-filter` DE L'EN-TÊTE : IL NE PEUT PAS
     AGIR ICI. `.site-header` en porte un, ce qui fait de lui la RACINE
     D'ARRIÈRE-PLAN de tous ses descendants — sous la barre, là où le panneau
     s'ouvre, cette racine ne contient plus rien à flouter. Un fond translucide
     y laissait donc passer le texte de la page, NET, ce qui a été essayé et
     rejeté le jour même. La translucidité de la barre n'est pas reproductible
     dans ce qu'elle contient ; ce qui l'est, c'est sa teinte. */
  background-color: var(--bg-page);
  border: 1px solid var(--border-subtle);
  /* ⚠️ AUCUN FILET EN HAUT : C'EST CELUI DE L'EN-TÊTE QUI SERT D'ARÊTE.
     Demandé par Hugo le 27/08/2026 — le panneau portait le sien juste sous
     celui de la barre, et les deux traits accolés se lisaient comme un seul,
     épais. Le panneau s'ouvre donc SOUS la bordure de l'en-tête (le pixel du
     `top`) et n'en redessine pas une seconde : une arête, partagée.

     Elle n'existe que si la barre porte la sienne, d'où la règle qui la fait
     apparaître pendant qu'un déroulant est ouvert, plus bas dans ce fichier. */
  border-top: 0;
  border-radius: 0;
  /* Comme la nav mobile : masqué sans être retiré du flux pour pouvoir
     animer, `visibility` interdisant au clavier d'y entrer tant qu'il est
     fermé. */
  visibility: hidden;
  opacity: 0;
  transform: translateY(-0.25rem);
  transition: opacity var(--dur-fast) var(--ease-out), transform var(--dur-fast) var(--ease-out), visibility var(--dur-fast);
}

/* ---------- Le panneau de « Solution » ----------
   ⚠️ IL SE CALE SUR SON LIEN DEPUIS LE 07/09/2026, comme celui de « Ressources ».
   Il partait jusque-là du bord gauche du `.container` : le groupe passait en
   `position: static` et le panneau se positionnait sur `.site-header__inner`,
   ce qui lui donnait toute la mesure de la page. Aligné sur le mot « Solution »,
   il se lit comme ouvert PAR lui, et non posé sous la barre entière.

   Il n'y a donc plus rien à faire de particulier : il se cale sur son groupe,
   exactement comme le panneau étroit, et ne se distingue que par sa largeur et
   sa grille interne. Les deux règles qui portaient l'ancien montage — le
   `position: static` sur le groupe et le `inset-inline: 0` — sont parties.

   ⚠️ 58rem TIENT DANS LA PAGE PARCE QUE « SOLUTION » EST LA PREMIÈRE ENTRÉE.
   Le lien commence à ~170 px du bord du container, qui fait 1376 px : le panneau
   finit vers 1100, il reste 275 px. Une entrée plus à droite qui voudrait ce
   gabarit déborderait — c'est la cote à vérifier avant d'en donner un second. */
.site-nav__menu--large {
  inline-size: 58rem;
  display: grid;
  grid-template-columns: 20.5rem 1fr;
}

.site-nav__menu--large .site-nav__briques {
  /* Le crème du panneau s'arrête au bord de la carte : les deux colonnes se
     touchent sans gouttière, comme dans la maquette. */
  padding-inline: var(--sp-6);
}

/* ⚠️ IL RÉTRÉCIT ENTRE `lg` ET `xl`, ET C'EST UNE REQUÊTE MÉDIA — PAS UN
   `max-inline-size`. Le panneau se montre dès 960 px ; à 58rem il finissait alors
   à 1058 px pour un écran de 965, donc hors cadre.

   ⚠️ DEUX SORTIES ONT ÉTÉ ESSAYÉES ET ÉCARTÉES, toutes deux à cause du `zoom` de
   l'en-tête (`--echelle-chrome`, 1,176) :

     · `max-inline-size: calc(100vw - 13rem)` — les unités de fenêtre sont
       MULTIPLIÉES par le zoom comme le reste, si bien que le plafond calculé
       (944 px de rendu) dépassait la largeur qu'il devait borner (928). Il ne
       mordait jamais ;
     · le même plafond une fois la grille débloquée — même cause, même effet.

   Une requête média, elle, s'évalue sur la FENÊTRE et ignore le zoom des
   éléments. C'est la seule mesure fiable ici.

   46rem tient à 960 px de fenêtre : le lien « Solution » commence vers 130 px, le
   panneau finit vers 865, l'écran en fait 945. */
@media (max-width: 80rem) {
  .site-nav__menu--large {
    inline-size: 46rem;
  }
}
/* ⚠️ LA CELLULE À ENFANTS COUVRE LES TROIS RANGS, et c'est ce qui supprime le
   vide entre « Le dictionnaire de données » et « La plateforme de pilotage ».

   Le défaut : une piste de grille se dimensionne sur sa cellule la PLUS HAUTE,
   et « Les intégrations » avec ses deux entrées mesure 145 px quand un pilier en
   fait 90. Les deux partageant le premier rang, celui-ci passait à 145 — donc
   56 px de crème sous le dictionnaire, que rien n'expliquait.

   En couvrant `1 / -1`, cette cellule ne pèse plus sur un rang en particulier :
   les trois se dimensionnent sur les piliers seuls, et elle s'aligne en tête de
   sa colonne. Les colonnes deviennent indépendantes, ce qu'une grille ne fait
   pas d'elle-même — c'est justement ce qui la distingue de deux colonnes. */
.site-nav__grille > li:has(.site-nav__enfants) {
  grid-column: 2;
  grid-row: 1/-1;
}

/* La carte occupe la colonne de gauche, les briques le reste.

   ⚠️ `min-inline-size: 0` EST CE QUI REND LE PLAFOND OPÉRANT. Une piste de grille
   en `1fr` porte un plancher implicite — son contenu minimum — et ce plancher
   passe AVANT le `max-inline-size` du conteneur : le panneau restait à 928 px et
   débordait de l'écran alors que sa cote calculée disait 803. C'est le débordement
   classique des grilles, et il ne se signale nulle part. */
.site-nav__menu--large > .site-nav__briques {
  grid-column: 2;
  min-inline-size: 0;
}

.site-nav__groupe:focus-within .site-nav__menu {
  visibility: visible;
  opacity: 1;
  transform: translateY(0);
}

.site-nav__groupe:focus-within .site-nav__chevron {
  transform: rotate(180deg);
}

@media (hover: hover) {
  .site-nav__groupe:hover .site-nav__menu {
    visibility: visible;
    opacity: 1;
    transform: translateY(0);
  }
  .site-nav__groupe:hover .site-nav__chevron {
    transform: rotate(180deg);
  }
}
/* ⚠️ LE PANNEAU EST PLAT DEPUIS LE 27/08/2026, et ses styles de second niveau
   sont partis avec la rangée qui l'ouvrait. « La solution complète » menait à
   /produit/, exactement comme la tête « Solution » qui la surplombait ; elle a
   été retirée à la demande d'Hugo, et les cinq pages filles sont remontées d'un
   cran. Le retrait de second niveau et le gras de la tête (reconnue à ses
   enfants par `:has()`, jamais à son rang) n'avaient plus d'appelant : ils sont
   dans git au 27/08/2026, pas ici. */
/* ⚠️ AUCUN FILET ENTRE LES RANGÉES, alors que la maquette en dessine un.
   Retiré à la demande d'Hugo le 25/08/2026 : la marge intérieure sépare seule,
   comme dans les entrées empilées de `texte-module` et les garanties de
   `liste-glyphes`. Le seul trait qui reste est celui du panneau. */
.site-nav__sous-lien {
  /* ⚠️ EN BLOC DEPUIS LE 31/08/2026, et non plus en `flex` : la rangée porte
     maintenant DEUX lignes — son intitulé et sa description — là où elle n'avait
     qu'un libellé et sa flèche. Le `flex` les aurait mises côte à côte.

     C'est `.site-nav__rangee` qui garde le flex, un cran plus bas, pour la ligne
     d'intitulé seule. La bascule mobile, elle, rétablit le flex ici : là-bas la
     rangée redevient « libellé à gauche, flèche au bout ». */
  display: block;
  /* La maquette compose la rangée à 24 px dans 16 / 20 px de marge. Elle est
     ramenée ici à l'échelle de l'en-tête : le texte prend --fs-sm, celui de
     l'entrée qui ouvre le panneau, et les marges suivent dans les mêmes
     proportions (12 / 16 px). La graisse a suivi le 25/08/2026 : le gras de la
     maquette est calibré pour 24 px, à 15 px il pesait plus lourd que l'entrée
     qui ouvre le panneau. Demi-gras, donc — un cran au-dessus du medium de la
     nav, ce qui laisse la hiérarchie dans le bon sens. */
  padding: var(--sp-3) var(--sp-4);
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
  color: var(--text-strong);
  border-radius: var(--radius-sm, 0.375rem);
  /* ⚠️ PLUS DE `nowrap`. Les descriptions sont des phrases : elles doivent
     pouvoir passer à la ligne, sinon la rangée pousse le panneau à la largeur
     de sa plus longue phrase. */
  white-space: normal;
  transition: color var(--dur-fast) var(--ease-out), background-color var(--dur-fast) var(--ease-out);
}

/* ⚠️ UNE RANGÉE INERTE N'EST PAS UN LIEN DÉSACTIVÉ : c'est un INTITULÉ DE
   GROUPE. « Les intégrations » annonce ses deux enfants et ne mène nulle part —
   d'où un `<div>` dans le gabarit, hors de l'ordre de tabulation. Le survol ne
   fait rien non plus : un fond qui s'allume sous le doigt promet un clic. */
.site-nav__sous-lien--inerte {
  cursor: default;
}

/* ⚠️ DEUX CLASSES, ET C'EST L'ORDRE QUI L'EXIGEAIT. À une seule, cette règle
   valait (0,2,0) comme `.site-nav__sous-lien:hover` — qui est écrite APRÈS et
   l'emportait donc. L'intitulé de groupe s'allumait au survol comme un lien,
   alors qu'il n'ouvre rien. */
.site-nav__sous-lien.site-nav__sous-lien--inerte:hover {
  color: var(--text-strong);
  background-color: transparent;
}

/* La ligne d'intitulé : le point, le libellé, le cartouche. */
.site-nav__rangee {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
}

/* ⚠️ UN POINT, ET JAMAIS UNE PASTILLE PLEINE — règle 16 de MAQUETTE.md. Aucune
   encre ne passe sur les quatre teintes de pilier en aplat : le blanc échoue sur
   trois d'entre elles (2,63 / 2,97 / 2,12) et l'encre foncée sur le violet
   (3,43). Un point ne porte pas de texte : la teinte y reste à pleine force sans
   rien à mesurer, et une seule règle sert les quatre piliers. */
.site-nav__point {
  flex: none;
  inline-size: 0.5rem;
  block-size: 0.5rem;
  border-radius: var(--radius-full);
  background-color: var(--ton);
}

.site-nav__intitule {
  min-inline-size: 0;
}

/* Le cartouche « New ». ⚠️ IL DILUE SA TEINTE À 15 %, et c'est ce qui le rend
   lisible : c'est l'exception que la règle 16 prévoit pour `.item__badge`, la
   seule sortie qui garde un aplat de pilier. L'encre reste l'encre courante. */
.site-nav__cartouche {
  flex: none;
  padding: 0.0625rem var(--sp-2);
  border: 1px solid var(--ton);
  border-radius: var(--radius-full);
  background-color: color-mix(in srgb, var(--ton) 15%, var(--bg-page));
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  font-weight: var(--fw-medium);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-strong);
}

/* La description. */
.site-nav__texte {
  display: block;
  font-size: var(--fs-xs);
  font-weight: var(--fw-regular);
  line-height: 1.45;
  color: var(--text-muted);
}

/* ⚠️ LE RETRAIT SUIT LE POINT, ET RIEN D'AUTRE. Il aligne le premier mot de la
   description sur l'intitulé, par-dessus la gouttière du point. Posé sans
   condition — c'était le cas jusqu'au 31/08/2026 — il creusait un vide en tête
   de chaque description de « Ressources », dont les rangées n'ont pas de point :
   un alignement sur une colonne qui n'existe pas. */
.site-nav__sous-lien:has(.site-nav__point) .site-nav__texte {
  padding-inline-start: 1rem;
}

/* ---------- Le second niveau ----------
   Remis le 07/09/2026 avec la branche qui l'appelle — « Les intégrations » et ses
   deux entrées. Il est HORS de la rangée dans le document : un lien dans un lien
   n'existe pas en HTML.

   Le retrait aligne les enfants sur l'intitulé du parent, par-dessus la gouttière
   de son point : c'est ce qui les rattache à lui sans qu'aucun trait ne le dise. */
/* ⚠️ `ul.` EST OBLIGATOIRE, ET C'EST UN RESET DU SITE QUI L'IMPOSE.
   `ul[class], ol[class] { padding: 0 }` vaut (0,1,1) et battait `.site-nav__enfants`
   à une seule classe : le retrait ne s'appliquait pas, et les deux entrées se
   calaient 32 px à gauche de « Votre donnée partout » au lieu de s'aligner
   dessus. Rien ne le signalait — la règle était bien dans la feuille.

   Le compte : la description commence au rembourrage de la rangée (--sp-4) plus
   son propre retrait de 1rem. Les enfants doivent donc partir de la somme des
   deux ; leur `<a>` reprend ensuite son rembourrage et l'annule en marge
   négative, pour que le fond de survol déborde sans décaler le texte. */
ul.site-nav__enfants {
  list-style: none;
  margin: 0;
  padding: 0 0 0 calc(var(--sp-4) + 1rem);
}

.site-nav__enfant {
  display: flex;
  align-items: center;
  /* ⚠️ PLUS DE `space-between` : il servait à pousser la flèche de QUEUE au bout
     de la rangée. La flèche est passée en tête le 07/09/2026, et le libellé doit
     maintenant la suivre immédiatement. */
  gap: 0;
  padding: var(--sp-2) var(--sp-4);
  margin-inline-start: calc(var(--sp-4) * -1);
  border-radius: var(--radius-sm, 0.375rem);
  font-size: var(--fs-xs);
  color: var(--text-strong);
  transition: color var(--dur-fast) var(--ease-out), background-color var(--dur-fast) var(--ease-out);
}

.site-nav__enfant:hover,
.site-nav__enfant:focus-visible {
  color: var(--c-blue);
  background-color: color-mix(in srgb, var(--c-blue) 8%, transparent);
}

/* La flèche de tête. Elle est plus pâle que le libellé au repos et prend son
   encre au survol : elle marque le rattachement sans peser autant que le mot.

   ⚠️ `flex: none` ET UN ÉCART FIXE : sans eux, un libellé long la comprimerait
   avant de passer à la ligne. */
.site-nav__enfant-fleche {
  flex: none;
  margin-inline-end: var(--sp-2);
  color: var(--text-muted);
  transition: color var(--dur-fast) var(--ease-out), translate var(--dur-fast) var(--ease-out);
}

.site-nav__enfant:hover .site-nav__enfant-fleche,
.site-nav__enfant:focus-visible .site-nav__enfant-fleche {
  color: inherit;
  translate: 0.15rem 0;
}

/* ---------- La carte de famille ----------
   ⚠️ ELLE REMET LE BLEU DANS LE DÉROULANT, retiré le 27/08/2026 parce qu'il
   « faisait du déroulant un objet étranger à la barre qui l'ouvre ». Deux choses
   ont changé depuis, et c'est ce qui a permis de le rouvrir le 31/08/2026 :

     · la teinte est `--c-blue` (#3f58d7), LE bleu de la charte, et non le
       `--c-blue-action` (#2340ff) saturé des boutons qui avait été retiré ;
     · le panneau reste CRÈME. C'est une carte bleue DANS le panneau, pas un
       panneau bleu — la barre et son déroulant portent toujours le même fond.

   ⚠️ AUCUNE OMBRE, AUCUN RAYON : règle 5, et les coins droits du panneau. */
.site-nav__carte {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  padding: var(--sp-6);
  background-color: var(--c-blue);
  color: var(--c-white);
  transition: background-color var(--dur-fast) var(--ease-out);
}

/* ⚠️ LA CARTE ENTIÈRE EST UNE PORTE depuis le 31/08/2026, par `.carte-cliquable`
   (voir en tête de ce fichier et le gabarit). Il lui faut donc un survol, sinon
   rien ne dit qu'elle s'ouvre : c'est le fond qui bouge, d'un cran vers le bleu
   des boutons.

   ⚠️ ET RIEN D'AUTRE. Pas d'ombre (règle 5), et surtout AUCUN `transform` : il
   ferait du lien couvrant le bloc conteneur de son propre `::after`, qui se
   replierait sur le texte — le clic n'ouvrirait plus rien. C'est le défaut du
   28/08/2026, et il ne se voit pas au survol. */
/* ⚠️ LE VOILE DÉBORDE DE LA MARGE INTÉRIEURE, ET IL LE DOIT. `inset: 0` sur un
   élément en absolu se résout sur la BOÎTE DE REMBOURRAGE de son bloc conteneur,
   pas sur sa boîte de bordure : la carte portant ses 24 px de marge elle-même,
   le voile s'arrêtait à 291 px sur 343 — la bande de crème autour du texte
   n'ouvrait rien, alors qu'elle est visiblement dans le cadre bleu.

   Les cartes de cas clients n'ont pas ce défaut parce que leur marge est sur un
   enfant, pas sur l'élément positionné. Ici on la reprend en négatif, ce qui
   ramène le voile exactement sur la boîte de bordure. Si la marge change, cette
   cote change avec — c'est le même jeton des deux côtés. */
.site-nav__carte.carte-cliquable .lien-couvrant::after {
  inset: calc(var(--sp-6) * -1);
}

.site-nav__carte.carte-cliquable:hover {
  background-color: color-mix(in srgb, var(--c-blue) 88%, var(--c-blue-action));
}

.site-nav__carte.carte-cliquable:hover .site-nav__carte-lien span {
  translate: 0.25rem 0;
}

/* ⚠️ LES ENCRES DE LA CARTE SONT MESURÉES SUR #3f58d7, pas choisies à l'œil. Le
   bleu de la charte est plus clair que celui des boutons : la marge y est
   courte, et toute nouvelle encre pâle posée ici est à remesurer.
     · blanc plein  → 5,85   (le titre et le lien)
     · blanc à 88 % → 4,93   (le paragraphe)
   Le surtitre à 85 % (4,72) est parti avec le surtitre lui-même, le
   31/08/2026 : il redisait le titre. */
.site-nav__carte-titre {
  margin: 0;
  font-size: var(--fs-lg, 1.375rem);
  line-height: 1.15;
  color: var(--c-white);
}

.site-nav__carte-texte {
  margin: 0;
  font-size: var(--fs-xs);
  line-height: 1.5;
  color: rgba(255, 255, 255, 0.88);
}

.site-nav__carte-lien {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  margin-block-start: auto;
  font-size: var(--fs-xs);
  font-weight: var(--fw-semibold);
  color: var(--c-white);
}

.site-nav__carte-lien span {
  transition: translate var(--dur-fast) var(--ease-out);
}

.site-nav__carte-lien:hover span {
  translate: 0.25rem 0;
}

/* ---------- Les briques et leur grille ---------- */
.site-nav__briques {
  padding: var(--sp-5) var(--sp-6) var(--sp-4);
}

.site-nav__surtitre {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  font-weight: var(--fw-medium);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.site-nav__grille {
  /* Les cellules ne s'étirent pas : sans ça, une rangée haute d'un côté grandit
     la piste et creuse un vide sous la cellule d'en face. */
  align-items: start;
  list-style: none;
  margin: var(--sp-4) 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0 var(--sp-5);
  /* Le retrait négatif ramène le fond de survol des rangées au bord du texte :
     sans lui, la marge intérieure de la rangée décalerait la colonne. */
  margin-inline: calc(var(--sp-4) * -1);
}

/* ⚠️ UNE SEULE COLONNE POUR « RESSOURCES », ET C'EST LE NOMBRE D'ENTRÉES QUI LE
   DICTE. À trois, une grille de deux laisse la troisième seule sur son rang — un
   trou que rien ne justifie — et leurs descriptions sont des phrases, qui
   casseraient toutes en colonnes étroites. Si une quatrième ressource arrive, la
   grille de « Solution » redevient le bon gabarit. */
.site-nav__grille--colonne {
  grid-template-columns: 1fr;
}

/* ⚠️ LA GRILLE SE REMPLIT COLONNE PAR COLONNE, ET NON RANG PAR RANG. C'est ce
   qui met les trois piliers à gauche — dictionnaire, plateforme, connecteur —
   et les deux faces des intégrations à droite, l'une sous l'autre.

   En remplissage par rangs, la cinquième entrée retombait à gauche d'un nouveau
   rang, en diagonale de sa jumelle ocre. Une classe posée sur elle l'avait
   d'abord ramenée en colonne 2 ; ce détour est parti le 31/08/2026 avec le
   `grid-auto-flow`, qui obtient le même résultat sans nommer de colonne — donc
   sans casser si l'ordre de nav.yaml change.

   ⚠️ TROIS RANGS ÉCRITS, ET C'EST LA SEULE COTE À SURVEILLER. Avec cinq entrées
   elle donne 3 + 2 ; à six elle donnerait 3 + 3, ce qui tient encore. À SEPT
   elle ouvrirait une TROISIÈME COLONNE, que le panneau n'a pas prévue. */
.site-nav__menu--large .site-nav__grille {
  grid-auto-flow: column;
  grid-template-rows: repeat(3, auto);
}

/* ⚠️ LE PIED DU PANNEAU EST PARTI LE 07/09/2026, avec le seul appel qui le
   remplissait. Ses règles ne sont pas laissées en place : un mécanisme sans
   appelant se lit comme s'il agissait, et « Ressources » n'en a jamais eu. Elles
   sont dans git à cette date si un panneau redemande un pied.



/* La teinte de survol suit le filet : elle lisait le bleu saturé des boutons
   quand le panneau en portait le trait, elle lit maintenant le bleu de la nav —
   celui que `.site-nav__link` prend déjà au survol, un cran plus haut. */
.site-nav__sous-lien:hover,
.site-nav__sous-lien:focus-visible {
  color: var(--c-blue);
  background-color: color-mix(in srgb, var(--c-blue) 8%, transparent);
}

/* La flèche avance au survol. `translate` et non `transform` : c'est la
   propriété autonome, elle se compose avec un `transform` venu d'ailleurs au
   lieu de vivre dedans. */
.site-nav__fleche {
  transition: translate var(--dur-fast) var(--ease-out);
}

/* ⚠️ MAIS PAS SUR LA BARRE DE BUREAU. Les entrées de premier niveau en portent
   une depuis le 27/08/2026 pour la maquette mobile, où chaque rangée qui mène
   quelque part se termine par une flèche. Alignées horizontalement dans la
   barre, ces mêmes flèches ne désignent plus rien — il n'y a pas de bord droit
   de rangée à conclure. Elle est donc rendue dans le document et masquée ici,
   puis rétablie dans la bascule mobile en bas de fichier. */
.site-nav__link .site-nav__fleche {
  display: none;
}

/* ⚠️ NI SUR LES RANGÉES DU PANNEAU, DEPUIS LE 31/08/2026. Même raison, un cran
   plus bas : la flèche conclut une rangée en désignant son bord droit, et le
   panneau n'en a plus. Ses rangées portent maintenant deux lignes dans une
   grille de deux colonnes — la flèche ne tombait au bout de rien, et sur la
   colonne de gauche elle pointait vers la colonne de droite.

   Elles restent dans le document et reviennent dans la bascule mobile, où la
   rangée redevient pleine largeur : c'est exactement le montage de
   `.site-nav__link` ci-dessus. */
.site-nav__sous-lien .site-nav__fleche {
  display: none;
}

.site-nav__sous-lien:hover .site-nav__fleche,
.site-nav__sous-lien:focus-visible .site-nav__fleche {
  translate: 0.25rem 0;
}

.site-header__actions {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  flex-shrink: 0;
}

/* ---------- Bouton menu mobile ----------
   La maquette ne couvre pas le mobile : ce menu est une proposition.
   Trois barres qui se transforment en croix à l'ouverture. */
.nav-toggle {
  display: none;
  align-items: center;
  justify-content: center;
  width: 2.75rem;
  height: 2.75rem;
  margin-right: calc(var(--sp-2) * -1);
  border-radius: var(--radius-sm);
}

.nav-toggle__bars {
  position: relative;
  display: block;
  width: 1.25rem;
  height: 2px;
  background-color: var(--text-strong);
  transition: background-color var(--dur-fast) var(--ease-out);
}

.nav-toggle__bars::before,
.nav-toggle__bars::after {
  content: "";
  position: absolute;
  left: 0;
  width: 100%;
  height: 2px;
  background-color: var(--text-strong);
  transition: transform var(--dur-base) var(--ease-out);
}

.nav-toggle__bars::before {
  top: -6px;
}

.nav-toggle__bars::after {
  top: 6px;
}

.nav-toggle[aria-expanded=true] .nav-toggle__bars {
  background-color: transparent;
}

.nav-toggle[aria-expanded=true] .nav-toggle__bars::before {
  transform: translateY(6px) rotate(45deg);
}

.nav-toggle[aria-expanded=true] .nav-toggle__bars::after {
  transform: translateY(-6px) rotate(-45deg);
}

/* ⚠️ LA PAGE EST VERROUILLÉE PENDANT QUE LE MENU MOBILE EST OUVERT — demandé
   par Hugo le 27/08/2026 : « le scroll scroll la page mais pas le menu ».
   Le panneau se borne maintenant à la hauteur disponible et défile tout seul
   (voir `.site-nav` dans la bascule ci-dessous), mais ça ne suffit pas : quand
   il tient à l'écran il n'a RIEN à consommer, et le geste trouve toujours la
   page dessous — le contenu glisse derrière un menu ouvert.

   La classe est posée sur `<html>` par main.js, comme reveal.js pose la sienne.
   Sur `<body>` elle n'aurait aucun effet : c'est l'élément racine qui est le
   conteneur de défilement. Elle ne vit que le temps de l'ouverture, et
   l'ouverture n'existe que sous `lg` — au retour en bureau le script ferme le
   menu et la retire.

   ⚠️ `scrollbar-gutter` VA AVEC LE VERROU, ce n'est pas une précaution : sur
   une fenêtre de bureau étroite (sous 960 px, ascenseur classique), le
   `overflow: hidden` escamote l'ascenseur et toute la page saute de sa largeur.
   Le gutter réserve la place qu'il occupait — zéro décalage. */
html.menu-ouvert {
  overflow: hidden;
  scrollbar-gutter: stable;
}

/* ---------- Bascule mobile ----------

   ⚠️ LA MAQUETTE MOBILE DU 27/08/2026 GOUVERNE CE BLOC, et elle diffère de la
   barre de bureau sur trois points — voir aussi partials/header.njk :

   1. LES SÉPARATEURS TRAVERSENT L'ÉCRAN d'un bord à l'autre. C'est la raison
      pour laquelle la marge latérale a QUITTÉ la nav pour passer sur chaque
      rangée : portés par des rangées rentrées dans une nav elle-même rentrée,
      les filets s'arrêtaient à `--container-pad` de chaque bord. Chaque rangée
      porte donc son propre retrait, et son filet court sur toute la largeur.
      ⚠️ Ne pas remettre de `padding-inline` sur `.site-nav` : ça les rognerait
      tous d'un coup, sans qu'aucune règle ne le dise.

   2. UNE TÊTE DE DÉROULANT EST L'INTITULÉ DE SA SECTION, posé en haut à gauche
      au-dessus de sa liste et à la taille des autres entrées. Le groupe passe
      donc en colonne : il est une RANGÉE en `flex`, et le menu redevenant
      statique ici, l'intitulé et sa liste restaient CÔTE À CÔTE, tassés.

   3. CHAQUE RANGÉE QUI MÈNE QUELQUE PART SE TERMINE PAR UNE FLÈCHE, au bord
      droit — y compris au premier niveau, où la barre de bureau n'en a pas.
      ⚠️ Y COMPRIS UNE TÊTE DE DÉROULANT QUI MÈNE QUELQUE PART, depuis que
      « Solution » est la seule route du menu vers /produit/ (27/08/2026, demande
      d'Hugo). C'est le gabarit qui tranche, sur l'ADRESSE de l'entrée et non sur
      son intitulé : « Ressources » n'a qu'une ancre morte et reste sans flèche.

   Un quatrième point a vécu deux jours et n'est plus : le déroulant avait un
   second niveau, rentré du même cran que le premier pour ne pas repousser les
   libellés hors du champ. La rangée qui l'ouvrait a été retirée le 27/08/2026,
   le panneau est redevenu plat, et ses deux règles sont parties avec elle. */
@media (max-width: 60rem) {
  .nav-toggle {
    display: inline-flex;
  }
  .site-nav {
    position: absolute;
    inset-inline: 0;
    top: 100%;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    /* Aucune marge latérale ni en haut : c'est chaque rangée qui porte son
       retrait (point 1 ci-dessus) et sa marge intérieure haute. Ne reste que
       l'air sous la dernière, avant le filet de fermeture du panneau. */
    padding: 0 0 var(--sp-6);
    /* ⚠️ BORNÉ À LA HAUTEUR DISPONIBLE, ET DÉFILANT. Le panneau est en
       `position: absolute` DANS un en-tête collant : il ne défile jamais avec
       la page, donc plus haut que l'écran son bas est INATTEIGNABLE — et le
       geste emporte la page derrière. Le verrou qui va avec est `html.menu-ouvert`,
       juste au-dessus de ce bloc.

       ⚠️ LE `svh` EST DIVISÉ PAR L'ÉCHELLE DU CHROME, ET C'EST OBLIGATOIRE.
       `.site-header` porte `zoom: var(--echelle-chrome)` (voir _tokens.scss) :
       tout ce qui s'écrit ici est peint 17,6 % plus grand. Une unité de viewport
       se résout bien contre le viewport, puis se fait zoomer comme le reste — un
       `100svh` brut vaudrait 117,6 % de l'écran, et le panneau reprendrait
       exactement le débordement qu'on cherche à retirer. La hauteur de l'en-tête,
       elle, se retire en `--h-entete-brute` : cette cote est déjà écrite dans ce
       repère, c'est à ça que sert le couple des deux jetons.

       `svh` et non `vh` : sur mobile `vh` compte la barre d'adresse rétractée,
       donc une hauteur qu'on n'a pas à l'instant où le menu s'ouvre. */
    max-block-size: calc(100svh / var(--echelle-chrome) - var(--h-entete-brute));
    overflow-y: auto;
    /* Arrivé au bout du panneau, le geste ne se propage pas à la page. */
    overscroll-behavior: contain;
    background-color: var(--bg-page);
    border-bottom: 1px solid var(--border-subtle);
    box-shadow: var(--shadow-md);
    /* Fermé par défaut : on masque sans retirer du flux pour pouvoir
       animer, mais `visibility` empêche le focus clavier d'y entrer. */
    visibility: hidden;
    opacity: 0;
    transform: translateY(-0.5rem);
    transition: opacity var(--dur-base) var(--ease-out), transform var(--dur-base) var(--ease-out), visibility var(--dur-base);
  }
  .site-nav[data-open=true] {
    visibility: visible;
    opacity: 1;
    transform: translateY(0);
  }
  .site-nav__link {
    padding-block: var(--sp-4);
    padding-inline: var(--container-pad);
    font-size: var(--fs-md);
    border-bottom: 1px solid var(--border-subtle);
    border-radius: 0;
    justify-content: space-between;
  }
  /* La flèche du premier niveau, rétablie (point 4). */
  .site-nav__link .site-nav__fleche {
    display: inline;
  }
  /* Et la rangée « Connexion » apparaît, en tête du menu. */
  .site-nav__link--connexion {
    display: flex;
  }
  /* Le déroulant redescend dans le flux, déplié sous son entrée : au doigt,
     un panneau qui s'ouvre au survol n'existe pas. Toutes les propriétés
     posées plus haut sont donc annulées ici, y compris le chevron, qui
     n'annonce plus rien à ouvrir. */
  .site-nav__groupe {
    /* ⚠️ EN COLONNE — voir le point 2 en tête de bloc. Sans cette ligne
       l'intitulé de section et sa liste se partagent une rangée. */
    flex-direction: column;
    align-items: stretch;
  }
  .site-nav__menu {
    position: static;
    min-width: 0;
    /* Pas de retrait ici : il est passé sur les rangées, pour que le filet de
       fermeture du groupe traverse l'écran comme les autres. */
    padding: 0 0 var(--sp-3);
    background-color: transparent;
    border: 0;
    border-bottom: 1px solid var(--border-subtle);
    border-radius: 0;
    box-shadow: none;
    /* ⚠️ `inherit` ET SURTOUT PAS `visible`. `visibility` S'HÉRITE, et un
       enfant `visible` sous un parent `hidden` EST VISIBLE — donc cliquable et
       atteignable au clavier. Écrite `visible` (c'était le cas jusqu'au
       27/08/2026), cette ligne ressortait les huit rangées de « Solution » et
       « Ressources » du panneau FERMÉ : l'œil ne les voyait pas — le panneau
       porte `opacity: 0` — mais `opacity` n'empêche ni le clic ni le focus, et
       le panneau couvre presque tout l'écran. Un appui n'importe où atterrissait
       donc sur un lien du menu, et la tabulation traversait un menu fermé.

       ⚠️ NE PAS « CORRIGER » ÇA EN `pointer-events: none` SUR LE PANNEAU FERMÉ :
       ça rattraperait le clic et laisserait le focus clavier entrer. C'est
       exactement pour ça que tout ce panneau se ferme en `visibility` et non en
       `opacity` seule — il suffit de le laisser hériter.

       Le `opacity: 1` reste nécessaire, lui : il défait le `0` de la règle de
       bureau, et c'est le panneau entier qui porte le fondu.

       Les deux règles `:focus-within` / `:hover` du déroulant de bureau battent
       celle-ci en spécificité (0,3,0 contre 0,1,0) et ne sont pas annulées ici :
       elles sont INATTEIGNABLES au doigt comme sur une fenêtre étroite, parce
       qu'un élément `visibility: hidden` ne reçoit pas le pointeur et n'est pas
       focusable — le panneau fermé ne peut donc être ni survolé ni focusé. Les
       neutraliser serait du code mort que rien ne signalerait. */
    visibility: inherit;
    opacity: 1;
    transform: none;
  }
  .site-nav__groupe .site-nav__chevron {
    display: none;
  }
  /* Le trait passe sous le déroulant, pas entre « Solution » et ses cinq
     lignes : elles forment un seul bloc. */
  .site-nav__groupe .site-nav__link {
    border-bottom: 0;
  }
  /* La rangée du déroulant, rentrée d'un cran sous l'intitulé de sa section.
     Le retrait est dans la marge intérieure de la rangée, pas dans celle de la
     liste : c'est ce qui laisse le filet de fermeture traverser. */
  .site-nav__sous-lien {
    /* ⚠️ LE FLEX REVIENT ICI. La rangée est en bloc sur le bureau, où elle porte
       son intitulé PUIS sa description ; au doigt elle redevient « libellé à
       gauche, flèche au bout », qui est la maquette du 27/08/2026. */
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-4);
    padding-block: var(--sp-3);
    padding-inline: calc(var(--container-pad) + var(--sp-4)) var(--container-pad);
    font-size: var(--fs-sm);
    font-weight: var(--fw-medium);
    white-space: normal;
    border-radius: 0;
  }
  /* ---------- CE QUE LE PANNEAU LAISSE AU VESTIAIRE ----------
     ⚠️ LA MAQUETTE MOBILE A ÉTÉ TRANCHÉE SÉPARÉMENT LE 27/08/2026, et le panneau
     de bureau ne doit pas déborder dessus. Trois choses ne se rendent donc pas
     au doigt :

       · LA CARTE de famille. Elle mène à /produit/, où l'entrée « Solution »
         mène déjà — au doigt cette entrée EST un lien, alors qu'elle n'ouvre
         rien sur le bureau. La carte y ferait donc le doublon qu'on a retiré le
         27/08 en supprimant « La solution complète ».
       · LE SURTITRE. Sur mobile la tête de déroulant sert déjà d'intitulé de
         section, posé au-dessus de sa liste : deux intitulés pour une liste.
       · LE PIED. « Parler à un expert » est déjà dans la barre, et le menu
         déplié occupe presque tout l'écran — le bouton est à un pouce.

     Les descriptions partent avec : elles doublent la hauteur de chaque rangée
     dans un menu qui en empile déjà huit sur un écran de téléphone.

     Le point de pilier et le cartouche « New » RESTENT : ils tiennent sur la
     ligne d'intitulé sans la casser, et le cartouche porte une information que
     rien d'autre ne dit. */
  .site-nav__carte,
  .site-nav__surtitre,
  .site-nav__texte {
    display: none;
  }
  /* ⚠️ LES FLÈCHES REVIENNENT, elles. Masquées sur le bureau — voir
     `.site-nav__sous-lien .site-nav__fleche` plus haut —, elles retrouvent ici
     leur emploi : la rangée redevient pleine largeur, et la maquette du
     27/08/2026 en veut une au bout de chacune de celles qui mènent quelque part.
     Les rangées inertes n'en portent pas, le gabarit ne leur en met pas. */
  .site-nav__sous-lien .site-nav__fleche {
    display: inline;
  }
  /* Les deux enfants des intégrations, rentrés d'un cran de plus que leur
     intitulé — comme les rangées le sont sous leur tête de section. */
  /* ⚠️ `ul.` ICI AUSSI. La règle de bureau a dû monter à `ul.site-nav__enfants`
     pour battre le reset `ul[class]` du site ; à une seule classe, cette
     surcharge-ci ne la battait plus et le retrait de bureau restait en place, en
     s'ajoutant à celui des rangées mobiles. */
  ul.site-nav__enfants {
    padding: 0;
  }
  .site-nav__enfant {
    padding-block: var(--sp-3);
    padding-inline: calc(var(--container-pad) + var(--sp-4) * 2) var(--container-pad);
    margin-inline-start: 0;
    border-radius: 0;
    font-size: var(--fs-sm);
  }
  /* Le panneau redescend dans le flux : la grille de deux colonnes et la largeur
     écrite plus haut n'ont plus de sens, il prend la mesure de l'écran. */
  .site-nav__menu,
  .site-nav__menu--large {
    display: block;
    inline-size: auto;
  }
  .site-nav__grille,
  .site-nav__grille--colonne {
    grid-template-columns: 1fr;
    margin: 0;
  }
  /* ⚠️ LE REMPLISSAGE PAR COLONNES EST ANNULÉ, ET CE N'EST PAS UNE PRÉCAUTION.
     Laissé en place sur une grille ramenée à une colonne, il empilerait les
     cinq entrées sur les TROIS rangs déclarés puis en ouvrirait une seconde,
     implicite : deux rangées partiraient à droite d'un menu déplié qui, lui,
     fait toute la largeur de l'écran. */
  .site-nav__menu--large .site-nav__grille {
    grid-auto-flow: row;
    grid-template-rows: auto;
  }
  .site-nav__briques {
    padding: 0;
  }
  /* Masquée : sa copie ouvre maintenant le menu déplié, en tête de la nav
     (voir partials/header.njk). Les deux ne se rendent jamais ensemble. */
  .site-header__actions .btn--ghost {
    display: none;
  }
}
/* =========================================================================
   LA CARTE D'ÉTIQUETTES — partials/carte-etiquettes.njk.

   Un intitulé en chasse fixe, puis des pilules. Origine : les deux cartes de
   l'anneau de « Pensé pour les entreprises qui pilotent par la donnée », sur
   l'accueil — Figma 185:7468.

   ⚠️ REMONTÉE ICI LE 18/08/2026, depuis sections/_texte-halftone.scss. Deux blocs
   l'emploient maintenant : `texte-halftone` la pose en absolu sur son anneau,
   `texte-module` la range dans le flux de sa boîte verte. Ce fichier ne porte donc
   que l'APPARENCE — fond, filet, rayon, typographie. Chaque bloc hôte garde SA
   mise en place, et c'est ce qui empêche les deux de diverger.

   Ne pas y remettre de `position`, de `top/left` ni d'ombre : ce sont trois choses
   qui appartiennent à l'endroit où la carte est posée, pas à la carte.
   ========================================================================= */
.carte-etiquettes {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  padding: clamp(var(--sp-3), 1.2vw, var(--sp-4));
  border-radius: var(--radius-cadre);
}

.carte-etiquettes--clair {
  background-color: var(--c-base-main);
  border: 1px solid var(--border-subtle);
}

.carte-etiquettes--sombre {
  background-color: var(--c-blue-dark);
  border: 1px solid var(--c-blue-dark);
}

.carte-etiquettes__titre {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
}

.carte-etiquettes--clair .carte-etiquettes__titre {
  color: var(--text-muted);
}

.carte-etiquettes--sombre .carte-etiquettes__titre {
  color: var(--c-fluo);
}

.carte-etiquettes__liste {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  margin: 0;
  padding: 0;
}

/* Petite pilule. Ce n'est pas `.pastille` : celle-ci est un intitulé de bloc à
   20-30 px, on est ici sur une étiquette de 13 px. Les deux partageraient une
   forme et rien d'autre. */
.carte-etiquettes__puce {
  padding: 0.25rem 0.8125rem; /* 4px 13px */
  font-family: var(--font-mono);
  font-size: 0.8125rem; /* 13px */
  line-height: 1.4;
  white-space: nowrap;
  border-radius: var(--radius-full);
}

.carte-etiquettes--clair .carte-etiquettes__puce {
  color: var(--c-blue-zodiac);
  border: 1px solid var(--border-subtle);
}

.carte-etiquettes--sombre .carte-etiquettes__puce {
  color: var(--c-white);
  border: 1px solid rgba(255, 255, 255, 0.35);
}

/* ---------- LA SORTIE : une flèche, puis LA pilule ----------
   Demandée par Hugo le 16/09/2026 pour les deux cartes de l'accueil : la pile
   énumère le problème, la sortie donne la réponse — « CA vérifié 11,62 M€ »
   face à douze chiffres qui se contredisent, « Biron, source unifiée » face à
   douze outils dispersés.

   ⚠️ ELLE EST REMPLIE LÀ OÙ LES AUTRES SONT CONTOURNÉES. C'est ce qui en fait
   le seul objet lisible de la carte, et c'est tout son propos : à côté d'une
   pile où les étiquettes se masquent les unes les autres, l'aplat désigne la
   sortie sans qu'aucun mot n'ait à le dire. Deux aplats, un par ton :

     carte claire — l'aplat d'accent, libellé blanc. ⚠️ RÈGLE 10 : c'est un
     aplat de plus à porter du blanc, il lit donc --btn-primary-bg — le fonçage
     mesuré de la teinte — et JAMAIS --c-blue en direct, avec le défaut dans le
     repli du `var()`. Sur une page pilier, --c-blue vaut la teinte à pleine
     force, où le blanc tombe entre 2,12 et 4,14.

     carte sombre — le fluo, encre sombre. La règle 16 ne l'interdit pas : elle
     vise les teintes de PILIER, sur lesquelles aucune encre ne passe. Le fluo
     n'en est pas une, il porte --c-text-main à 15,3 — c'est déjà l'aplat de
     `.appel__cta` et du bouton de `partials/inscription.njk`.

   ⚠️ CE BLOC NE POSE QUE L'APPARENCE, jamais le placement. La sortie est rendue
   après la liste, donc EN DESSOUS par défaut, ce qui est le repli raisonnable
   pour un hôte qui n'en dit rien. Sur l'accueil elle est à DROITE de la pile,
   et c'est sections/_texte-halftone.scss qui le fait, en passant la carte en
   grille — comme il posait déjà le positionnement absolu et l'ombre. C'est lui
   aussi qui glisse l'origine de l'entonnoir SOUS la pile et découpe la pilule en
   deux lignes ; les deux tiennent à la place dont cet emploi-là dispose. */
.carte-etiquettes__sortie {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  margin: 0;
}

/* L'ENTONNOIR — le dessin donné par Hugo le 16/09/2026, en SVG en ligne.

   ⚠️ `non-scaling-stroke` EST STRUCTUREL, PAS COSMÉTIQUE. Le dessin est coté pour
   923 px de large et se rend ici autour de 157 : sans lui, un filet de 1 unité
   arriverait à 0,17 px, c'est-à-dire un voile gris clair au lieu d'un trait.
   Avec lui, l'épaisseur est lue en pixels d'écran et ne dépend plus de la taille
   à laquelle on pose le dessin — c'est donc aussi ce qui rend l'entonnoir
   redimensionnable sans rien recalculer.

   ⚠️ LA POINTE, ELLE, EST PLEINE ET SE MET À L'ÉCHELLE : elle est redessinée à
   38 unités là où le fichier d'origine en donnait 8, qui se seraient rendues à
   1,4 px. Agrandir l'entonnoir la grandit avec lui, quand les filets, eux, ne
   bougent pas — c'est voulu, une pointe d'un pixel ne se verrait pas.

   La taille par défaut n'est là que pour qu'un hôte qui n'en dit rien obtienne
   un objet raisonnable ; sur l'accueil elle est reprise en cote (voir
   sections/_texte-halftone.scss). */
.carte-etiquettes__entonnoir {
  display: block;
  block-size: 3rem;
  inline-size: auto;
  aspect-ratio: 923/638;
}

.carte-etiquettes__entonnoir path {
  vector-effect: non-scaling-stroke;
}

.carte-etiquettes--clair .carte-etiquettes__entonnoir {
  color: var(--btn-primary-bg, var(--c-blue));
}

.carte-etiquettes--sombre .carte-etiquettes__entonnoir {
  color: var(--c-fluo);
}

/* La pilule de sortie est un `<span>` là où les autres sont des `<li>` : il lui
   faut donc un bloc pour que son rembourrage compte. Le `flex` du parent le lui
   donne, mais on le dit ici pour que la règle tienne seule. */
.carte-etiquettes__puce--sortie {
  display: inline-block;
}

.carte-etiquettes--clair .carte-etiquettes__puce--sortie {
  color: var(--c-white);
  background-color: var(--btn-primary-bg, var(--c-blue));
  border-color: var(--btn-primary-bg, var(--c-blue));
}

.carte-etiquettes--sombre .carte-etiquettes__puce--sortie {
  color: var(--c-text-main);
  background-color: var(--c-fluo);
  border-color: var(--c-fluo);
}

/* ---------- Les pilules à l'aplat d'accent ----------
   Demandées par Hugo le 21/08/2026 pour « En coulisses » de /cas-clients/hardloop/.
   La carte garde son fond clair ; seules les pilules se remplissent.

   ⚠️ LA RÈGLE 16 DE MAQUETTE.md NE COUVRE PAS CE CAS. Elle veut une pastille
   CONTOURNÉE quand la teinte est celle d'un pilier, parce qu'aucune encre ne passe
   sur les quatre teintes en aplat (le blanc échoue sur trois, le noir sur le
   violet). Le bleu du site n'est pas dans cette table : il porte du blanc à 5,84,
   puisque c'est l'aplat de `.btn--primary`. L'aplat est donc légitime ici.

   ⚠️ MAIS LA RÈGLE 10 S'APPLIQUE : c'est un aplat de plus à porter du blanc, après
   `.btn--primary`, `.hero-page__badge` et la carte du `comparatif`. Il lit donc
   --btn-primary-bg — le fonçage mesuré de la teinte — et JAMAIS --c-blue en direct.
   Sur une page pilier, --c-blue vaut la teinte du pilier à pleine force, où le blanc
   tombe entre 2,12 et 4,14.

   ⚠️ ET LE DÉFAUT VA DANS LE REPLI DU `var()`, jamais dans une déclaration à part :
   une propriété personnalisée posée SUR l'élément bat celle qu'un ancêtre hérite,
   donc `--btn-primary-bg: var(--c-blue)` écrit ici masquerait la valeur que
   `.ton-page--*` pose sur `<main>`. C'est la convention du site, expliquée dans
   CLAUDE.md.

   ⚠️ SA PLACE DANS LE FICHIER EST LOAD-BEARING, comme celle de `puces: blanc` juste en
   dessous : à spécificité égale (deux classes), c'est l'ordre qui tranche. Les DEUX
   remplissages sont donc écrits APRÈS les deux tons — ce n'était pas le cas au départ,
   et `ton: sombre` + `puces: bleu` aurait rendu des pilules contournées sans que rien ne
   le signale. La règle à tenir : les tons de carte d'abord, les remplissages de pilule
   ensuite.

   ⚠️ `border-color: transparent` ET NON `border: none` : l'atome pose un filet d'1px,
   et le retirer ferait rétrécir la pilule de 2px en largeur comme en hauteur. On
   garde la géométrie et on éteint la couleur — même remède que les pilules du
   dictionnaire dans sections/_texte-module.scss. */
.carte-etiquettes--puces-bleu .carte-etiquettes__puce {
  background-color: var(--btn-primary-bg, var(--c-blue));
  border-color: transparent;
  color: var(--c-white);
}

/* ---------- Les pilules en APLAT BLANC ----------
   Ajouté le 21/08/2026 pour la fiche du client de /cas-clients/hardloop/, en deux temps.
   Hugo a d'abord demandé d'inverser la carte et ses pilules — « on peut inverser le fond
   du conteneur et le fond des badges ? » — puis de passer la carte au sombre : « tu peux
   le mettre en fond sombre finalement ».

   ⚠️ C'EST UN REMPLISSAGE DE PILULE, PAS UN TON DE CARTE, et le premier essai s'y était
   trompé : il avait ajouté un ton `bleu` qui portait les deux à la fois. Le fond sombre
   existait déjà — c'est `ton: sombre` — donc il n'y avait qu'à composer les deux champs,
   ce qu'ils savent faire depuis `puces: bleu`. Un ton de plus aurait fait deux objets là
   où un suffit, et il n'aurait plus eu d'utilisateur.

   ⚠️ CE QU'IL SURCHARGE : le ton `sombre` rend ses pilules CONTOURNÉES, libellé blanc sur
   le bleu nuit de la carte. Ici elles sont pleines, donc leur encre repasse au sombre.

   ⚠️ ET SA PLACE DANS LE FICHIER EST LOAD-BEARING : les deux règles ont la MÊME
   spécificité (deux classes), donc c'est l'ordre qui tranche, et celle-ci doit être
   écrite APRÈS celle du ton. Posée juste sous la déclaration de la CARTE sombre — ce que
   le premier essai faisait — elle tombait avant la règle de pilule du même ton et
   n'avait aucun effet : les pilules restaient contournées à libellé blanc. Défaut vu
   dans le CSS compilé le 21/08/2026. Ne pas la remonter.

   ⚠️ ET LES PILULES NE SONT PAS `.on-dark` POUR AUTANT, alors que la carte est sombre.
   Elles sont BLANCHES : leur contenu reste dans les rôles du contexte clair, et c'est ce
   qui permet à un libellé éteint et à une valeur forte de cohabiter dedans. `.on-dark`
   sur la carte aurait mis leurs deux encres au blanc, illisibles sur elles.

   `border-color: transparent` et non `border: none` : l'atome pose un filet d'1px, et le
   retirer ferait rétrécir la pilule de 2px en largeur comme en hauteur. On garde la
   géométrie et on éteint la couleur — même remède que les pilules du dictionnaire. */
.carte-etiquettes--puces-blanc .carte-etiquettes__puce {
  background-color: var(--c-white);
  border-color: transparent;
  color: var(--c-blue-zodiac);
}

/* =========================================================================
   LA COMPOSITION DES TROIS FORMES — partials/trio.njk.

   ⚠️ DÉPLACÉE ICI DEPUIS `sections/_texte-illustre.scss` LE 20/08/2026, quand le
   hero de la page « Pourquoi Biron » a eu besoin du même diagramme. C'est la
   règle du site — l'atome vit dans `partials/`, son apparence dans ce fichier,
   et le bloc hôte ne garde que le placement. Les deux hôtes d'aujourd'hui la
   posent dans deux moitiés opposées : `.texte-illustre__visuel` à gauche,
   `.hero-page__visuel` à droite.
   ========================================================================= */
/* =========================================================================
   La composition des trois formes — Figma 197:5924.

   Trois formes de marque qui se chevauchent en diagramme, chacune portant sa
   promesse, et la marque à leur intersection. La légende dessous nomme ce que
   l'image montre.

   TOUTES LES COTES SONT EN POURCENTAGE de la scène, relevées sur la maquette
   (cadre de 528 × 582, dont 528 × 510 pour la scène et le reste pour la
   légende). La composition est donc rigoureusement homothétique : elle tient
   à n'importe quelle largeur sans réglage.

   Les corps de texte suivent, eux aussi. Ils sont en `cqw` — pourcentage de
   la largeur du conteneur — et non en `rem` : un texte à taille fixe dans une
   image qui se redimensionne finit par déborder de sa forme d'un côté ou par
   s'y perdre de l'autre. Le plancher en `max()` évite qu'il devienne
   illisible sur les écrans les plus étroits.

   ⚠️ Les formes sont des MASQUES, pas des images. Les exports Figma portent
   des teintes voisines mais pas identiques à celles du site — #D6F224 contre
   le fluo #d1ff00, #3B4EE8 contre le bleu #3f58d7. Posées telles quelles,
   elles auraient introduit un second fluo à côté du vrai, sur la même page.
   En masque, la forme vient de l'export et la couleur des jetons.

   La marque du centre fait exception : c'est une image, parce qu'elle porte
   DEUX couleurs — ce qu'un masque ne sait pas faire — et que son export est
   déjà exactement aux couleurs du site.
   ========================================================================= */
.trio {
  container-type: inline-size;
  margin: 0;
}

.trio__scene {
  position: relative;
  /* 528 × 510 : le cadre de la maquette moins la bande de légende. */
  aspect-ratio: 528/510;
}

/* Les trois formes ont exactement la même taille — 282 × 282 dans la
   maquette. Seuls leur ancrage, leur découpe et leur teinte changent. */
.trio__forme {
  position: absolute;
  width: 53.41%; /* 282 / 528 */
  height: 55.29%; /* 282 / 510 */
  background-color: var(--trio-ton);
  mask-image: var(--trio-src);
  mask-size: 100% 100%;
  mask-repeat: no-repeat;
  -webkit-mask-image: var(--trio-src);
  -webkit-mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat;
}

.trio__forme--logiciel {
  --trio-ton: var(--c-fluo);
  --trio-src: url("/assets/shapes/Trio - Logiciel.svg");
  top: 0;
  left: 0;
  /* Origine = le centre de la SCÈNE, exprimé dans les coordonnées de cette
     forme-ci. Voir le bloc « Le lent tour » plus bas. */
  transform-origin: 93.6% 90.4%;
  animation-name: trio-orbite-a;
  animation-duration: 11.1s;
}

.trio__forme--equipe {
  --trio-ton: var(--c-blue);
  --trio-src: url("/assets/shapes/Trio - Equipe.svg");
  top: 10.2%; /* 52 / 510 */
  left: 46.59%; /* 246 / 528 */
  transform-origin: 6.38% 71.98%;
  animation-name: trio-orbite-b;
  animation-duration: 12.3s;
}

.trio__forme--accompagnement {
  --trio-ton: var(--c-blue-dark);
  --trio-src: url("/assets/shapes/Trio - Accompagnement.svg");
  top: 44.71%; /* 228 / 510 */
  left: 20.45%; /* 108 / 528 */
  transform-origin: 55.33% 9.57%;
  animation-name: trio-orbite-c;
  animation-duration: 12.9s;
}

/* =========================================================================
   Le lent tour.

   Les quatre pièces tournent AUTOUR DU CENTRE DE LA SCÈNE, pas autour du
   leur : chacune porte donc son propre `transform-origin`, qui est ce même
   point unique exprimé dans ses coordonnées à elle. Le calcul est toujours
   le même — pour l'axe X, (50 % − left) / largeur ; pour l'axe Y,
   (50 % − top) / hauteur. C'est pour ça que ces nombres n'ont l'air de rien
   et qu'il ne faut surtout pas les arrondir « pour faire propre » : déplacer
   une pièce oblige à les recalculer.

   ⚠️ Les libellés, eux, NE BOUGENT PAS. C'est ce qui borne l'amplitude : à
   9°, la forme la plus éloignée du centre décrit un arc d'environ 26 px, ce
   que son libellé encaisse sans sortir de la couleur — les formes font 282 px
   de large. Beaucoup plus ample, et les mots se décolleraient de leur forme.
   L'alternative aurait été de faire orbiter les libellés avec elles en les
   contre-tournant pour qu'ils restent droits ; ça demande une enveloppe par
   libellé et deux animations à tenir en phase, pour un gain discutable.

   LES CADENCES ONT ÉTÉ CHERCHÉES EN DEUX TEMPS, et l'écart entre les deux
   mérite d'être connu avant d'y retoucher.

   Un premier calage les avait posées pour qu'un aller-retour tienne dans les
   dix secondes que la section retient — l'animation étant en `alternate`, un
   cycle complet vaut DEUX FOIS la durée, ce qui donnait 3,7 à 4,9 s. Le
   mouvement s'y voyait, mais il s'y voyait trop.

   Elles ont été triplées le 14/08/2026 : 11,1 / 12,3 / 12,9 / 14,7 s, soit
   des cycles de 22 à 29 s. On ne voit donc plus qu'une fraction du
   balancement en passant, et c'est le parti pris — le mouvement se sent, il
   ne se suit pas.

   Les quatre durées restent sans diviseur commun : la composition respire au
   lieu de tourner, et les quatre mouvements ne retombent en phase qu'au bout
   de dizaines d'heures.

   Le coût est négligeable, contrairement à la trame animée essayée sur « Au
   cœur de Biron » : une rotation est composée par le GPU, qui réutilise une
   texture déjà peinte. Rien n'est repeint à chaque image.
   ========================================================================= */
.trio__forme,
.trio__marque-fond {
  animation-timing-function: ease-in-out;
  animation-iteration-count: infinite;
  animation-direction: alternate;
}

@keyframes trio-orbite-a {
  from {
    transform: rotate(-7deg);
  }
  to {
    transform: rotate(7deg);
  }
}
@keyframes trio-orbite-b {
  from {
    transform: rotate(9deg);
  }
  to {
    transform: rotate(-9deg);
  }
}
@keyframes trio-orbite-c {
  from {
    transform: rotate(-6deg);
  }
  to {
    transform: rotate(8deg);
  }
}
/* Celle-ci n'est pas une orbite mais une rotation SUR PLACE, d'où
   l'amplitude plus franche : rien ne se déplace, donc rien ne peut se
   décoller de son libellé. */
@keyframes trio-tour-centre {
  from {
    transform: rotate(-11deg);
  }
  to {
    transform: rotate(11deg);
  }
}
@media (prefers-reduced-motion: reduce) {
  .trio__forme,
  .trio__marque-fond {
    animation: none;
  }
}
/* Aucun `z-index` nulle part : formes, libellés et marque sont tous en
   position absolue, donc ils se peignent dans l'ordre du DOM. L'ordre du
   gabarit EST l'ordre des plans — fluo, bleu, bleu nuit, puis les libellés,
   puis la marque. */
.trio__label {
  position: absolute;
  margin: 0;
  font-family: var(--font-titre);
  font-weight: var(--fw-bold);
  font-size: max(0.75rem, 4.55cqw); /* 24px sur les 528px de la maquette */
  line-height: 1.25;
  text-align: center;
  text-wrap: balance;
}

.trio__label--logiciel {
  top: 21.76%; /* 111 / 510 */
  left: 12.5%; /* 66 / 528 */
  width: 26.14%; /* 138 / 528 */
  color: var(--c-blue-dark);
}

/* ⚠️ SEUL LIBELLÉ DES TROIS À NE PLUS PORTER LA COTE DE LA MAQUETTE, et c'est la
   longueur de son texte qui l'impose. Il disait « Une équipe data » et tenait sur
   DEUX lignes ; depuis le 20/08/2026 il dit « Une expertise data & technique » et
   en prend QUATRE. Or les libellés sont ancrés par le HAUT : les deux lignes de
   plus sont donc descendues, et « technique » sortait par le bas-droit de la forme
   bleue, là où elle s'incurve.

   Il remonte d'exactement UNE hauteur de ligne — 1,25 × 4,55cqw, soit 30 des 510
   unités de la scène, soit 5,88 % — ce qui replace le CENTRE optique du libellé où
   était celui de l'ancien. 151 − 30 = 121, d'où les 23,73 %.

   ⚠️ Le compte de lignes ne dépend PAS de la largeur d'affichage, et c'est ce qui
   rend ce décalage fixe légitime : la boîte est en % de la scène et le corps en
   `cqw`, les deux suivent donc la même échelle et le point de repli ne bouge
   jamais. Un libellé qui déborde à une largeur déborde à toutes.

   ⚠️ Corollaire : TOUT CHANGEMENT DE CE TEXTE DEMANDE DE REVOIR CETTE COTE. C'est
   le prix de l'ancrage par le haut, et il est assumé — les trois libellés sont
   posés au pourcentage sur une composition homothétique, pas mis en page.

   ---------------------------------------------------------------------
   IL EST AUSSI DÉCALÉ VERS LA DROITE, et c'est le même texte plus long qui
   l'impose. La marque du centre occupe x 172,6 → 331,9 ; la boîte du libellé
   partait de 319,5, elle EMPIÉTAIT donc de 12,5 unités sur le losange blanc.
   Tant que le libellé tenait sur deux lignes centrées, le chevauchement tombait
   dans le blanc au-dessus et en dessous du losange ; à quatre lignes, il tombe
   en plein dessus.

   Décalé de 20 unités — 319,5 + 20 = 339,5, d'où les 64,3 %. C'est le minimum
   qui dégage la marque, avec 7,6 unités de reste.

   ⚠️ ON NE PEUT PAS ALLER BEAUCOUP PLUS LOIN. À la hauteur du libellé, la forme
   bleue s'étend jusqu'à x ≈ 508 ; la ligne la plus large (« expertise », ~119
   unités) centrée dans la boîte va jusqu'à 470. Il reste donc une marge, mais
   elle se referme vite — la forme s'incurve. */
.trio__label--equipe {
  top: 23.73%; /* 121 / 510 — la cote de la maquette (151) moins une ligne */
  left: 64.3%; /* 339.5 / 528 — 319,5 dans la maquette, décalé pour dégager la marque */
  width: 26.89%; /* 142 / 528 */
  color: var(--c-white);
}

.trio__label--accompagnement {
  top: 67.84%; /* 346 / 510 */
  left: 25.66%; /* 135.5 / 528 */
  width: 42.99%; /* 227 / 528 */
  color: var(--bg-page);
}

/* ---------- La marque du centre ----------
   Deux calques dans la même boîte : le losange, qui tourne, et le logo, qui
   ne bouge pas. Les deux exports partagent la zone de dessin de l'original,
   ils se superposent donc au pixel près — `inset: 0` suffit, rien à recaler.

   ⚠️ Le losange tourne sur LUI-MÊME, pas autour du centre de la scène comme
   les trois autres formes. C'est ce qui permet au logo de rester droit : une
   orbite l'aurait déplacé, et il aurait fallu le faire suivre. Une rotation
   en place ne déplace rien, le logo n'a qu'à ne pas tourner. */
.trio__marque {
  position: absolute;
  top: 29.61%; /* 151 / 510 */
  left: 32.69%; /* 172.6 / 528 */
  width: 30.19%; /* 159.4 / 528 */
  aspect-ratio: 159.393/164.375;
}

.trio__marque-fond,
.trio__marque-logo {
  position: absolute;
  inset: 0;
  mask-size: 100% 100%;
  mask-repeat: no-repeat;
  -webkit-mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat;
}

.trio__marque-fond {
  background-color: var(--bg-page);
  mask-image: url("/assets/shapes/Trio - Marque fond.svg");
  -webkit-mask-image: url("/assets/shapes/Trio - Marque fond.svg");
  /* En place : l'origine est son propre centre, contrairement aux trois
     formes qui portent celui de la scène. L'amplitude peut donc être plus
     franche, une rotation en place ne décale aucun texte. */
  transform-origin: 50% 50%;
  animation-name: trio-tour-centre;
  animation-duration: 14.7s;
}

.trio__marque-logo {
  background-color: var(--c-blue-dark);
  mask-image: url("/assets/shapes/Trio - Marque logo.svg");
  -webkit-mask-image: url("/assets/shapes/Trio - Marque logo.svg");
}

/* La légende, en chasse fixe comme tous les eyebrows du site. Les capitales
   viennent du style et non des données : le texte reste lisible dans le YAML,
   et une traduction n'a pas à les porter.

   Elle est posée dans un cartouche blanc, sans filet : le crème de la page
   passe derrière l'illustration, et une ligne de texte y flottait sans
   attache. Le blanc lui donne son assise — c'est le seul aplat blanc de la
   moitié qui la porte, ce qui suffit à la rattacher à l'image.

   `width: fit-content` plus les marges automatiques : le cartouche épouse son
   texte et se centre. Une `figcaption` est un bloc, elle prendrait sinon
   toute la largeur et le blanc s'étalerait d'un bord à l'autre. */
.trio__legende {
  width: fit-content;
  margin-block-start: 6%; /* un peu moins que les 45px de la maquette, le
   rembourrage du cartouche reprenant la différence */
  margin-inline: auto;
  padding: 0.5em 1.1em;
  background-color: var(--c-white);
  font-family: var(--font-mono);
  font-size: max(0.625rem, 2.37cqw); /* 12,5px sur les 528px de la maquette */
  letter-spacing: 0.06em;
  text-transform: uppercase;
  text-align: center;
  color: var(--c-blue-dark);
}

/* =========================================================================
   LE REPÈRE — partials/repere.njk.

   Un chiffre et ce qu'il compte. Origine : les deux rangées de « À propos »
   (Figma 144:4980 et 144:5114).

   ⚠️ EXTRAIT DE `sections/_reperes.scss` LE 20/08/2026, le jour de sa création,
   quand les trois chiffres de la section 01 sont rentrés DANS la boîte du texte :
   deux hôtes, donc l'atome sort. C'est la règle du site — l'atome porte son
   apparence, le bloc hôte son placement (en rangée pour `reperes`, en colonne pour
   `texte-module`).

   ⚠️ AUCUN `order` : le gabarit écrit le `<dd>` avant le `<dt>`, donc l'ordre du DOM
   est déjà l'ordre visuel. Le raisonnement sur qui est le terme et qui est la valeur
   est en tête du partial.
   ========================================================================= */
.repere {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
}

/* ⚠️ LA TAILLE EST UN BOUTON, ET SON DÉFAUT EST DANS LE REPLI DU `var()`. Les
   chiffres d'une carte de cas client sont plus petits que ceux d'une rangée de
   section (demande d'Hugo, 21/08/2026 : « les chiffres clés sont un peu trop
   grands ») : l'hôte pose --repere-taille sur un ancêtre, et l'atome n'a rien à
   savoir de lui. Écrire le défaut dans une déclaration à part au lieu du repli
   l'aurait rendu insurchargeable — une propriété posée SUR l'élément bat toujours
   la même héritée d'un ancêtre. */
.repere__valeur {
  margin: 0;
  font-family: var(--font-titre);
  font-size: var(--repere-taille, var(--fs-h2));
  font-weight: var(--fw-bold);
  line-height: var(--lh-tight);
  letter-spacing: var(--ls-display);
  /* La teinte d'accent : le bleu du site sur le crème, le fluo dans une boîte
     sombre — c'est `.on-dark` qui bascule, rien à déclarer ici. */
  color: var(--text-accent);
}

.repere__libelle {
  font-size: var(--fs-xs);
  line-height: var(--lh-normal);
  color: var(--text-muted);
}

/* ⚠️ --text-muted TIENT SUR LE BLEU NUIT, et cette note est là pour qu'on ne
   « corrige » pas ce qui va bien. `.on-dark` le passe au casper (#aab4d2), qui donne
   6,4 sur #202b5c : c'est le cas pour lequel le rôle a été calé. C'est seulement sur
   une teinte de PILIER qu'il casse (2,29 sur le violet, 2,35 sur le rose) — voir la
   règle 11 de MAQUETTE.md. */
/* ---------- L'infobulle de /tarifs/ ----------
   Une seule bulle, réutilisée par les quinze déclencheurs du grand tableau, et
   positionnée par assets/js/tarifs.js.

   ⚠️ ELLE VIT SUR `<body>`, PAS DANS LE TABLEAU, et c'est ce qui l'empêche
   d'être rognée : le tableau est dans un conteneur qui défile latéralement.
   D'où `position: fixed` — ses coordonnées sont celles de la fenêtre, que le
   script relit à chaque ouverture.

   Elle n'existe que sur cette page : son balisage vient d'une maquette et non
   des blocs du site, où l'aide d'un critère se rend en CSS pur — voir
   `aide` dans blocs/comparatif.njk. */
.tarifs-bulle {
  position: fixed;
  z-index: 50;
  max-inline-size: 22rem;
  padding: var(--sp-3) var(--sp-4);
  font-family: var(--font-sans);
  font-size: var(--fs-xs);
  line-height: var(--lh-normal);
  color: var(--text-on-inverse);
  background-color: var(--c-blue-zodiac);
  border-radius: var(--radius-sm);
  pointer-events: none;
}

/* Le « i » du tableau : la maquette le pose sans état de focus, ce qui le rend
   invisible au clavier. On lui en donne un. */
[data-tip]:focus-visible {
  outline: 2px solid var(--text-accent);
  outline-offset: 2px;
}

/* =========================================================================
   Ouverture — plan commun au hero et au bandeau clients.

   La trame et les formes fluo doivent traverser d'un bloc à l'autre sans
   césure. C'est ce conteneur qui porte le rognage : les formes ancrées au
   visuel produit peuvent donc déborder sous le hero et se poursuivre
   derrière les références clients, au lieu d'être coupées à la frontière.
   ========================================================================= */
.intro {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  /* Respiration avant la trame halftone. Ce vide laisse les formes fluo
     finir leur course et la grille s'éteindre progressivement, au lieu que
     tout s'arrête net contre la transition. */
  padding-bottom: clamp(4rem, 2rem + 6vw, 9rem);
}

.intro__decor {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
}

/* La trame démarre à 28 % de la hauteur du groupe et descend jusqu'en bas,
   bandeau clients compris. Le fondu du haut la fait naître du fond plutôt
   que de la poser sur une arête. */
.intro__grid {
  position: absolute;
  inset-inline: 0;
  top: 28%;
  bottom: 0;
}

/* ⚠️ SURCHARGE LOCALE, PAS UNE MODIFICATION DU JETON. `--grid-opacity` vaut 0,55
   sur `.grid-pattern` (sections/_hero.scss) et sert AUSSI à la FAQ, aux blocs
   texte-illustré et à « Recruter ou externaliser » de /pourquoi-biron/ : y
   toucher éclaircirait la trame partout.

   0,30 est la valeur retenue le 31/08/2026, en même temps que l'illustration
   fixe du hero — son fond transparent laisse enfin voir la trame derrière
   l'image, et à 0,55 elle passait devant le sujet.

   ⚠️ LES DEUX CLASSES SONT INDISPENSABLES. `.intro__grid` seul ne suffit pas :
   il est à la même spécificité que `.grid-pattern`, et main.scss importe
   `sections/intro` AVANT `sections/hero` — c'est donc 0,55 qui l'emportait.
   Premier essai fait, et raté exactement là. */
.intro__grid.grid-pattern {
  --grid-opacity: 0.3;
}

/* =========================================================================
   HERO
   ========================================================================= */
/* Pas de `overflow: hidden` ici : c'est .intro qui rogne, pour que les
   formes fluo ancrées au visuel puissent se poursuivre derrière le bandeau
   clients.

   `isolation` en revanche est nécessaire, et ne rogne rien — elle ne fait
   qu'enfermer les niveaux d'empilement du hero. Sans elle, le z-index 2 de
   .hero__inner remonte au niveau de .intro et fait passer les formes
   PAR-DESSUS le cadre clients, qui est à 1. */
.hero {
  position: relative;
  isolation: isolate;
  padding-top: clamp(3rem, 1rem + 6vw, 5.5rem);
  padding-bottom: 0;
}

/* Le hero déborde la grille de 80rem du reste du site : c'est le seul
   moyen qu'il gagne réellement en largeur au-delà de 1376px. */
.hero .container {
  max-width: calc(var(--container-hero) + var(--container-pad) * 2);
}

.hero__inner {
  position: relative;
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}

.hero__eyebrow {
  margin-bottom: var(--sp-4);
}

/* 68rem et non 56rem comme la maquette : la phrase la plus longue du
   rotateur mesure 1047 px à 44 px, il lui faut ce dégagement pour tenir
   sur une ligne. La ligne 1 du titre (881 px) n'en profite pas — elle est
   plus courte et reste centrée. */
.hero__title {
  max-width: 100%;
  margin-bottom: var(--sp-6);
  line-height: 1.06;
  letter-spacing: -0.032em;
  /* Neutralise le `text-wrap: balance` hérité de la règle h1 de _base.scss,
     qui entre en conflit avec le `nowrap` du rotateur.

     `wrap` et surtout pas `nowrap` : ce dernier n'annule pas le balance, il
     interdit tout retour à la ligne — et le titre sort alors de l'écran en
     mobile. Le contrôle du retour à la ligne se fait sur le rotateur, pas
     ici. */
  text-wrap: wrap;
}

/* Le crénage se resserre quand le corps grossit : à 72 px, le serrage d'un
   44 px paraît lâche. */
@media (min-width: 100rem) {
  .hero__title {
    letter-spacing: -0.042em;
  }
}
/* ⚠️ `balance` ICI, alors que le titre parent l'annule expressément — et les
   deux ne se contredisent pas.

   Sur `.hero__title`, `balance` s'applique à un bloc qui contient AUSSI le
   rotateur, dont les phrases sont en `nowrap` ; le calcul entre alors en
   conflit avec elles. Sur cette ligne-ci, il n'a que le texte de la ligne 1 à
   équilibrer, le rotateur étant un autre bloc.

   Sans lui, « Vos équipes ont besoin de chiffres fiables pour » se coupe au
   dernier mot possible et laisse « pour » seul sur sa ligne. `balance`
   répartit les mots entre les deux lignes au lieu de remplir la première. */
.hero__title-line {
  display: block;
  text-wrap: balance;
}

/* ---------- Rotateur de phrases ----------
   Les phrases sont empilées dans une seule cellule de grille : le
   conteneur prend la hauteur de la plus haute et ne bouge plus. Sans ça,
   passer d'une phrase d'une ligne à une phrase de deux ferait sauter tout
   le contenu situé en dessous à chaque bascule. */
.rotator {
  display: grid;
  justify-items: center;
  /* Le conteneur prend la hauteur de la phrase la plus haute. Une phrase
     courte est donc centrée dans cette hauteur plutôt que collée en haut,
     ce qui évite un trou visible sous les phrases d'une seule ligne. */
  align-items: center;
  color: var(--c-blue);
}

/* Ligne unique tant qu'il y a la place. En dessous de 900 px, on rend le
   retour à la ligne au navigateur : le forcer plus bas obligerait à
   descendre le titre sous 32 px. */
.rotator__phrase {
  grid-area: 1/1;
  white-space: nowrap;
}

@media (max-width: 56.25rem) {
  .rotator__phrase {
    white-space: normal;
  }
}
/* Le masque de chaque mot. Il coupe dans les QUATRE directions — c'est ce
   qui permet au mot de monter depuis le bas sans déborder, mais c'est aussi
   ce qui rognait les lettres sur les côtés.

   Chaque marge intérieure est compensée par une marge négative de même
   valeur : le masque grandit, la position du mot ne bouge pas.

   - en bas  : les jambages (g, p, q) descendent sous la ligne de base
   - sur les côtés : dans Recoleta, l'encre de certains glyphes déborde de
     leur chasse (le `r` notamment), et l'interlettrage négatif de −0,042 em
     resserre encore les boîtes. Invisible à 44 px, criant à 72 px.

   Brique commune à la ligne 1 du titre et aux phrases du rotateur : les
   deux doivent pouvoir être révélées de la même façon. */
.word {
  display: inline-block;
  overflow: hidden;
  vertical-align: bottom;
  padding-bottom: 0.16em;
  margin-bottom: -0.16em;
  padding-inline: 0.09em;
  margin-inline: -0.09em;
}

.word > span {
  display: inline-block;
}

/* Les phrases occupent toutes la même cellule : si rien ne les masque,
   elles se superposent et le titre devient illisible. On les cache donc
   par défaut, et le JS ne révèle que ce qu'il pilote.

   Ce défaut couvre tous les cas de figure — JS désactivé, GSAP absent,
   script oublié dans un layout — et pas seulement `.no-js`. Dans le pire
   des cas on perd le défilement, jamais la lisibilité. */
.rotator__phrase:not(.is-active) {
  opacity: 0;
}

/* ---------- Entrée en scène ----------
   L'état masqué n'existe QUE sous la classe `hero-anim`, posée en <head>
   et retirée par un chien de garde si hero.js n'a jamais pris la main.

   L'ancienne logique reposait sur `.no-js`, que le script inline retirait
   inconditionnellement : une panne de hero.js laissait alors le hero
   entièrement invisible. Ici, toute défaillance rend le contenu visible. */
.hero-anim [data-hero-enter] {
  opacity: 0;
}

/* Chapô : même couleur que le titre, pas de gris atténué.
   La mesure est bornée à ~62 caractères — au-delà, l'œil perd la ligne en
   revenant à la marge. */
.hero__lead {
  /* En `em` et non en `rem` : la mesure est indexée sur la taille du texte
     lui-même, donc la justification reste à ~62 caractères que le chapô
     fasse 17 ou 22 px. En `rem`, grossir le texte réduit la justification
     et le bloc change de forme au lieu de grandir. */
  max-width: 34em;
  margin-bottom: var(--sp-8);
  font-size: var(--fs-lead);
  line-height: var(--lh-normal);
  color: var(--text-strong);
}

/* La mise en ligne / empilement est portée par .btn-group. */
.hero__actions {
  margin-bottom: clamp(3rem, 1rem + 5vw, 5rem);
}

/* ---------- Visuel produit ----------
   Capture de la plateforme. En attente du visuel définitif : le bloc
   affiche un cadre vide au bon ratio pour ne pas fausser la mise en page.

   Les formes fluo sont ancrées sur ce conteneur, pas sur la page : elles
   dépassent de derrière le visuel. C'est ce qui les rend solidaires du
   bloc — elles suivent sa taille à toutes les largeurs d'écran. */
.hero__visual-wrap {
  position: relative;
  width: 100%;
  /* ⚠️ LARGEUR CONTENUE DEPUIS LE 31/08/2026, à la place de `var(--container-hero)`.
     L'illustration fixe remplace le lecteur, et « contenue » est la largeur
     retenue à l'arbitrage : 896 px d'affichage.

     ⚠️ 65,9rem ET NON 56rem, ET C'EST TOUTE LA SUBTILITÉ. L'aperçu qui a servi à
     trancher tournait à la racine par défaut du navigateur (16 px), où 56rem fait
     bien 896 px. Le site rend à 85 % (`html { font-size: 85% }`, _base.scss),
     donc 13,6 px : les mêmes 56rem n'y valent que 762 px, soit 15 % de moins que
     ce qui a été validé. 896 / 13,6 = 65,9.

     Reste en rem, pas en px, pour suivre la préférence de taille du visiteur —
     c'est la convention du site.

     Ce n'est pas seulement la largeur de l'image : LES FORMES FLUO SONT ANCRÉES
     SUR CE CONTENEUR (`.hero__shapes` est en `inset: 0`), donc les contraindre
     ici est ce qui les garde solidaires de l'illustration. Le poser sur le seul
     `<img>` laisserait les formes s'écarter à la largeur du container.

     Si le lecteur revient, remettre `var(--container-hero)`. Le jeton n'est
     déclaré que dans labo/_hero-variants.scss : sur l'accueil il était indéfini,
     donc `max-width` retombait sur `none`. */
  /* ⚠️ 80rem DEPUIS LE 07/09/2026, contre 65,9 auparavant : 1088 px d'affichage
     au lieu de 896. Demandé avec la nouvelle illustration.

     ⚠️ ET CE N'EST PAS LE REMÈDE AU FLOU, C'EST SON AGGRAVANT. Le fichier livré
     fait 1379 px de large : à 896 px d'affichage il servait 1,54 pixel par pixel
     d'écran, à 1088 il n'en sert plus que 1,27. Sur un écran rétina, qui en
     demande deux, on passe de 0,77 à 0,63. Le texte de l'illustration est donc
     PLUS doux qu'avant, pas moins.

     La netteté ne se gagne qu'à l'export : il faut 2 176 px de large pour servir
     1088 en rétina. Tant que la source n'y est pas, cette cote est un choix de
     composition, pas de qualité. */
  max-width: 80rem;
  margin-inline: auto;
  /* Le visuel se redresse depuis une légère inclinaison à l'entrée : c'est
     ce qui porte la profondeur du parti pris « focus-pull ». */
  perspective: 1600px;
}

/* ---------- L'illustration fixe ----------
   Elle porte sa propre transparence (WebP à bloc alpha) : pas de fond, pas de
   filet, pas de rayon — la trame fluo et les formes passent derrière elle.

   ⚠️ AUCUNE OMBRE PORTÉE, et ce n'est pas un oubli : règle 5 de MAQUETTE.md.
   Le `box-shadow` de `.hero__visual` appartient au cadre du lecteur, qui est un
   panneau opaque ; ici il dessinerait un rectangle autour d'une image détourée. */
.hero__illustration {
  position: relative;
  z-index: 2;
  /* Les trois copies occupent la MÊME cellule : le conteneur prend leur
     hauteur commune et ne bouge plus. Même dispositif que `.rotator`
     ci-dessus — et c'est aussi ce qui préserve la réservation de place, la
     cellule étant dimensionnée par les `width`/`height` des images. */
  display: grid;
}

/* ---------- Les trois panneaux ----------
   Un seul fichier, trois copies, trois bandes verticales jointives. Le
   pourquoi est dans sections/hero.njk ; ici, seulement les cotes.

   ⚠️ LES POURCENTAGES VIENNENT DES COTES DES PANNEAUX, PAS DE L'ŒIL. Le SVG
   d'origine mesure 1486 unités de large (983 + 503, le panneau de droite étant
   le dernier), et le panneau central y occupe x = 451 → 1021 :

     bord gauche du central : 451 / 1486 = 30,3499 %
     bord droit  du central : 1021 / 1486 = 68,7079 %

   Ces deux valeurs sont les seules coutures. Elles ne dépendent pas de la
   définition du fichier : si l'illustration est ré-exportée plus grande, elles
   restent justes — seules les cotes de _data/hero.yaml feraient bouger.

   ⚠️ LES DEUX BANDES LATÉRALES DÉBORDENT D'UN CHEVEU SOUS LA CENTRALE (0,15 %,
   soit ~3 px sur le fichier). C'est volontaire : la bande centrale est dessinée
   par-dessus, donc le recouvrement ne se voit pas, alors qu'une jointure exacte
   laisse passer un liseré de fond dès que l'arrondi tombe mal. */
.hero__panneau {
  grid-area: 1/1;
  display: block;
  inline-size: 100%;
  block-size: auto;
}

/* À droite du panneau central — la première à arriver. */
.hero__panneau--droite {
  clip-path: inset(0 0 0 68.56%);
}

/* À gauche du panneau central. */
.hero__panneau--gauche {
  clip-path: inset(0 69.5% 0 0);
}

/* Le panneau central lui-même — dernier arrivé, donc au-dessus. Ses deux
   bords sont les coutures exactes ; ce sont les bandes latérales qui
   débordent, pas lui. */
.hero__panneau--centre {
  clip-path: inset(0 31.2921% 0 30.3499%);
}

.hero__visual {
  position: relative;
  z-index: 2;
  border-radius: var(--radius-cadre) var(--radius-cadre) 0 0;
  overflow: hidden;
  box-shadow: var(--shadow-lg);
}

/* Le gabarit est celui de `.apercu` (_components.scss). Ne restent ici que le
   ratio de la maquette et la coupe basse : le visuel du hero est rogné par la
   section, il n'a donc ni filet ni rayon en bas. */
.hero__visual-placeholder {
  aspect-ratio: 1193/667;
  border-bottom: none;
  border-radius: inherit;
}

/* ---------- Formes fluo ----------
   La forme elle-même — masque, teinte, ratio — est la primitive `.forme`
   de _components.scss, posée par partials/forme.njk. Ne restent ici que
   l'ancrage et les cotes propres au hero.

   Chaque forme est réglée par quatre variables : sa taille et sa rotation
   (--shape-w, --shape-rot) et son ancrage (top/left/right). Tout est en %
   de la largeur du conteneur : les formes restent solidaires du visuel à
   toutes les largeurs d'écran.

   Pour ajuster : ouvre l'inspecteur, sélectionne le .hero__shape et joue
   sur --shape-w / --shape-rot / top / left. Les valeurs se lisent en
   direct, il n'y a plus qu'à me les donner. */
.hero__shapes {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
}

.hero__shape {
  position: absolute;
}

/* Haut droite — le losange, posé de biais sur le coin. */
.hero__shape--top-right {
  --shape-w: 19%;
  --shape-rot: 18deg;
  top: -14%;
  right: -6%;
}

/* ⚠️ LES DEUX FORMES DU BAS ONT ÉTÉ DIVISÉES PAR DEUX le 31/08/2026 (« trop
   grosses »). LEURS ANCRAGES ONT CHANGÉ EN MÊME TEMPS, ET C'EST OBLIGATOIRE :

   `top`/`left` placent le COIN HAUT-GAUCHE, pas le centre. Réduire `--shape-w`
   seul fait donc rétrécir la forme VERS ce coin. Sur celle de gauche, ancrée à
   `left: -22%`, son bord droit serait passé de 778 px à 5 px : elle aurait
   quasiment disparu hors du cadre.

   Les ancrages ci-dessous sont recalculés pour que chaque forme garde le CENTRE
   qu'elle avait — même place, moitié moins grande. Tout étant en % du
   `.hero__visual-wrap`, dont le rapport est celui de l'illustration et donc
   constant, ces valeurs tiennent à toutes les largeurs.

   Pour rejouer l'exercice : réduire `--shape-w` de Δ, puis ajouter Δ/2 à `left`
   et la moitié de la hauteur retirée à `top`.

   ⚠️ ET NE PAS PRENDRE LES COTES DANS L'INSPECTEUR SANS Y REGARDER : ces formes
   sont tournées, et `getBoundingClientRect()` rend la boîte englobante APRÈS
   rotation, sensiblement plus grande que la boîte de mise en page. Le citron
   mesure 332 px de large mais s'y annonce à 429. Un premier calcul fait sur ces
   cotes-là a décalé les deux centres de 25 px. Le centre, lui, est juste : la
   rotation se fait autour de lui. */
/* Bas droite — le citron. */
.hero__shape--bottom-right {
  --shape-w: 19%;
  --shape-rot: -22deg;
  top: 87.25%; /* 74% + la moitié de la hauteur retirée (26,5%) */
  left: 91.5%; /* 82% + 19%/2 */
}

/* Bas gauche — l'écu, sort toujours par la gauche, moins loin. */
.hero__shape--bottom-left {
  --shape-w: 16.5%;
  --shape-rot: 26deg;
  top: 70.55%; /* 58% + la moitié de la hauteur retirée (25,1%) */
  left: -13.75%; /* -22% + 16,5%/2 */
}

/* ---------- Trame quadrillée ----------
   Motif dessiné, pas d'image lourde à charger.

   Les dégradés répétés CSS donnaient des traits d'épaisseur inégale : sur
   un écran dont la mise à l'échelle n'est pas entière (Windows à 125%),
   une ligne sur deux tombe à cheval entre deux pixels physiques et le
   navigateur l'étale — elle paraît alors plus pâle et plus épaisse.

   D'où le passage à un motif SVG avec `shape-rendering: crispEdges`, qui
   désactive l'anticrénelage : chaque trait est forcé sur un pixel entier,
   donc tous strictement identiques. La taille intrinsèque du SVG porte le
   pas, ce qui évite tout redimensionnement du motif. */
.grid-pattern {
  --grid-opacity: 0.55;
  background-repeat: repeat;
  opacity: var(--grid-opacity);
}

/* Trait de 2px : il fallait l'épaissir, mais un entier — 1,5px ne tombe pas
   sur la grille de pixels et `crispEdges` l'arrondirait à 1 ou 2 selon le
   zoom, ce qui ramènerait l'irrégularité qu'on vient justement de corriger.

   Seul le pas de 16 px sert au site. Les pas de 12 et 24, essayés puis
   écartés, ne servent qu'à /labo/grille/ : ils sont dans labo/_labo.scss. */
.grid-pattern--16 {
  background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' shape-rendering='crispEdges'><rect width='16' height='2' fill='%23d1ff00'/><rect width='2' height='16' fill='%23d1ff00'/></svg>");
}

/* ---------- Et la teinte FLUO n'a pas de variante ----------
   C'est `--16` ci-dessus qui la porte, et c'est pour ça qu'elle n'est pas déclarée
   ici à côté des trois autres : une `.grid-pattern--fluo` qui redirait la même URI
   et la même opacité mettrait une couleur à deux endroits.

   L'hôte qui nomme ses teintes n'ajoute donc rien pour elle — voir le champ `trame`
   de blocs/texte-module.njk, et les `{% if b.trame == "bleu" %}` de blocs/clients.njk
   et de blocs/hero-page.njk, qui font pareil dans l'autre sens.

   Les 0,55 par défaut sont la bonne valeur sur le CRÈME, où elle sert partout
   (l'accueil, `.faq__grille`, `.texte-illustre__grille`, et depuis le 26/08/2026 la
   section « Recruter ou externaliser » de /pourquoi-biron/) : le calcul d'écart de
   clarté mené pour les trois autres variantes ne s'applique pas ici, le fluo étant
   à 4 points du crème (L* 94 contre 98) — aucune opacité ne lui ferait peser le
   trait d'une teinte sombre, et c'est ce trait discret qui est recherché. */
/* ---------- La même trame, en BLEU ----------
   Demandée par Hugo le 21/08/2026 pour le hero de /cas-clients/ : « une grille comme
   dans la page à propos mais bleu ». La trame et son fondu sont donc ceux de
   `hero-page --centre` ; seule la couleur change.

   ⚠️ ELLE PORTE SA PROPRE OPACITÉ, ET PLUS BASSE QUE LE FLUO. C'est contre-intuitif
   et c'est mesuré : le fluo #d1ff00 est une couleur CLAIRE, donc son écart de luminance
   avec le fond de page est faible et 55 % y donnent un trait discret. Le --c-blue-400
   #7885ce, lui, est nettement plus sombre que le --c-blue-50 du fond : à 55 % le
   quadrillage passait devant le titre. À 45 % il retrouve le poids de trait de la
   version fluo.

   ⚠️ LA DÉCLARATION DOIT RESTER APRÈS CELLE DE `.grid-pattern`, qui pose
   --grid-opacity à 0,55 : les deux sélecteurs ont la même spécificité, c'est donc
   l'ordre du fichier qui tranche.

   La couleur est écrite en dur dans l'URI, comme celle de la trame fluo : une
   data-URI ne peut pas lire une propriété personnalisée. */
.grid-pattern--bleu {
  --grid-opacity: 0.45;
  background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' shape-rendering='crispEdges'><rect width='16' height='2' fill='%237885ce'/><rect width='2' height='16' fill='%237885ce'/></svg>");
}

/* ---------- La même trame, en VIOLET ----------
   Demandée par Hugo le 26/08/2026 pour la section « Des cas d'usage concrets »
   de /connecteur-ia/ : « la grille est violette ». C'est la teinte du pilier
   connecteur, celle de l'accent de cette page.

   ⚠️ ELLE PORTE SA PROPRE OPACITÉ, PLUS BASSE ENCORE QUE LE BLEU, et c'est le
   même raisonnement que celui de la variante bleue poussé d'un cran. Le #8b5cff
   est plus sombre que le #7885ce (L* 52 contre 57) ET il se pose sur le CRÈME,
   plus clair que le --c-blue-50 sur lequel le bleu a été calé (L* 98 contre 95) :
   l'écart de clarté passe de 38 à 46 points, donc à opacité égale le trait
   pèserait un cinquième de plus. 0,45 × 38 / 46 ≈ 0,37, arrondi à 0,35 — la
   trame est ici posée dans une bande de crème vide où rien ne vient la
   contredire, alors que la bleue passe derrière le contenu d'un hero.

   La couleur est écrite en dur dans l'URI, comme celles des deux autres trames :
   une data-URI ne peut pas lire une propriété personnalisée. C'est aussi pourquoi
   elle ne suit PAS `--text-accent` — la trame ne changerait pas de teinte si un
   jour le bloc servait sur une page d'un autre pilier. */
.grid-pattern--violet {
  --grid-opacity: 0.35;
  background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' shape-rendering='crispEdges'><rect width='16' height='2' fill='%238b5cff'/><rect width='2' height='16' fill='%238b5cff'/></svg>");
}

/* ---------- La même trame, en ORANGE ----------
   Demandée par Hugo le 26/08/2026 pour la section « Un plugin utile pour toutes
   vos équipes » de /excel-sheets/ : « une grille orange ». C'est la teinte du
   pilier intégrations, celle des aplats de cette page.

   ⚠️ C'EST LE PILIER, PAS SON ENCRE. Le pilier intégrations est le seul à porter
   deux jetons — --c-pilier-integrations pour l'APLAT, --c-pilier-integrations-encre
   pour l'ENCRE (voir la règle 10 de MAQUETTE.md). Une trame quadrillée est un
   décor, pas un texte : elle prend l'aplat, comme le violet prend le pilier
   connecteur et non sa valeur sombre.

   ⚠️ ELLE NE PORTE PLUS LE MÊME POIDS DE TRAIT QUE LES TROIS AUTRES, ET C'EST
   VOULU. Le calcul de parité mené pour les variantes bleue et violette donnait
   0,65 : le #f0a22e est une couleur CLAIRE — L* 73, contre 52 pour le violet —
   donc son écart de clarté avec le crème (L* 98) tombe à 25 points là où le
   violet en a 46, et à opacité égale le trait pèserait moitié moins ;
   0,35 × 46 / 25 ≈ 0,64, arrondi à 0,65 — ce qui posait l'orange à L* 81,
   exactement le violet voilé à 0,35.

   Hugo l'a fait redescendre le 26/08/2026 (« on peut réduire l'opacité des
   grilles oranges ? »). À 0,45 l'orange donne un L* de 86, soit 12 points
   d'écart au fond au lieu de 17. La parité des quatre trames est donc rompue sur
   cette variante — délibérément, et pas par oubli du calcul : ne pas la
   « réharmoniser » sur les trois autres.

   Corollaire inchangé : si la trame devait un jour peser PLUS, c'est la couleur
   qu'il faudrait changer, pas l'opacité — l'encre du pilier (#cc6207, L* 54) est
   là pour ça, elle retomberait dans le régime du violet. Dans l'autre sens, en
   revanche, l'opacité a toute la marge voulue.

   La couleur est écrite en dur dans l'URI, comme celles des trois autres trames :
   une data-URI ne peut pas lire une propriété personnalisée. C'est aussi pourquoi
   elle ne suit PAS `--text-accent`. */
.grid-pattern--orange {
  --grid-opacity: 0.45;
  background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' shape-rendering='crispEdges'><rect width='16' height='2' fill='%23f0a22e'/><rect width='2' height='16' fill='%23f0a22e'/></svg>");
}

/* Fondu vers le haut : la trame naît du fond au lieu de démarrer net.
   Le masque ne joue que sur l'opacité globale — il ne touche pas à
   l'épaisseur des traits, qui reste uniforme sur toute la hauteur. */
.grid-pattern--fade-top {
  mask-image: linear-gradient(to bottom, transparent, black 35%);
}

/* Fondu aux deux extrémités. La trame naît du fond en haut et s'y éteint
   en bas, au lieu de buter contre la transition halftone. */
.grid-pattern--fade-both {
  mask-image: linear-gradient(to bottom, transparent, black 30%, black 76%, transparent);
}

/* Le positionnement de la trame est porté par .intro (sections/_intro.scss) :
   elle traverse le hero ET le bandeau clients. Le bloc « texte-illustre »
   compose son propre masque, sur ses deux axes (voir sections/_texte-illustre.scss). */
/* =========================================================================
   LE LECTEUR DU HERO — trois scènes cliquables.

   Il remplace le cadre gris qui tenait la place d'une capture. Le gabarit
   est dans partials/lecteur.njk, le comportement dans assets/js/lecteur.js.

   ⚠️ LA SCÈNE N'A PLUS DE DÉCOR. La trame quadrillée et les deux formes de
   marque qui flottaient derrière ont été retirées le 14/08/2026 : la scène
   est déjà chargée en cartes, en rails et en chiffres, et le fond se
   disputait la lecture avec eux. Le cadre est nu.

   ⚠️ ET LE CHIVO MONO N'Y EST JAMAIS EN GRAS. Une chasse fixe épaissit mal :
   les nombres y perdent en lisibilité au lieu d'en gagner. Les deux `<b>` du
   gabarit portent donc un `font-weight` explicite, pour défaire celui que le
   navigateur applique par défaut.

   ---------------------------------------------------------------------
   UN SEUL REPÈRE DE COORDONNÉES, et c'est la clé de tout le fichier.

   La scène est dessinée dans un cadre de 1200 × 675 en pixels absolus, que
   le script met à l'échelle de la largeur disponible par un `scale()`. Les
   cotes ci-dessous ne sont donc PAS des cotes d'écran : ce sont des cotes de
   plan. Elles ne changent jamais, quelle que soit la fenêtre.

   C'est ce qui permet de poser un rail SVG et une carte HTML au même endroit
   sans qu'ils dérivent l'un par rapport à l'autre — les deux partagent le
   même système. En les exprimant en pourcentages, il aurait fallu recalculer
   chaque courbe de Bézier à chaque largeur.

   Conséquence à connaître : ne jamais mettre de `rem` ni de `vw` dans la
   scène. Un `rem` ne suit pas le `scale()` et casserait l'alignement dès que
   l'utilisateur change la taille de police de son navigateur.
   ========================================================================= */
.lecteur {
  /* Le filet des cartes de la scène. Franchement bleu, et non le
     `--filet-panneau` éclairci des grands panneaux du site : ces cartes-ci
     sont petites et nombreuses — jusqu'à quatre par scène — et un filet
     dilué s'y lisait comme un gris. Il leur faut de quoi tenir seul, l'ombre
     portée ayant été retirée.

     `--lecteur-filet-survol` est le bleu d'accent du site, celui des liens
     actifs et des questions ouvertes de la FAQ. Il ne sert qu'aux cartes
     CLIQUABLES : mettre une réaction de survol sur une carte inerte promet
     une action qui n'existe pas. */
  --lecteur-filet: 1px solid var(--c-blue-400);
  --lecteur-filet-survol: 1px solid var(--c-blue);
  /* ⚠️ AUCUNE OMBRE PORTÉE dans le lecteur, et c'est un choix, pas un oubli.
     Trois traitements ont été essayés le 14/08/2026 puis abandonnés : l'ombre
     bleu nuit du prototype, un halo fluo, un voile crème destiné à dégager le
     pourtour des cartes. Les cartes portent leur filet, il suffit à les poser.

     Le gris des mentions secondaires de la scène — références, unités. Plus
     clair que --text-muted, qui reste lisible en texte courant : ici ce sont
     des indications qu'on ne lit que si on les cherche. */
  --lecteur-gris: color-mix(in srgb, var(--text-muted) 62%, var(--bg-page));
  position: relative;
}

/* ---------- Onglets ----------
   Un seul bloc, et non trois pilules séparées : les trois étapes forment un
   parcours, la boîte commune le dit. C'est un pavé qui glisse de l'une à
   l'autre qui porte la sélection, plus un fond posé sur chaque onglet.

   `width: fit-content` et non `justify-content: center` : le bloc doit
   épouser ses onglets, sinon le curseur glisserait dans une boîte plus large
   qu'eux et le calcul de ses cotes n'aurait plus de sens. */
.lecteur__onglets {
  position: relative;
  display: flex;
  align-items: center;
  width: fit-content;
  padding: 4px;
  margin: 0 auto var(--sp-5);
  background-color: var(--c-white);
  /* Pas de contour : le bloc se signale par son aplat blanc, et un filet de
     plus autour d'un pavé déjà cerné faisait double bordure. */
  /* Allure de BOUTON et non de panneau : le rayon des boutons du site, et non
     les coins vifs des blocs de texte. Ces onglets sont une commande, pas un
     cadre — c'est ce que le rayon doit dire. Demandé le 14/08/2026. */
  border-radius: var(--radius-md);
}

/* La flèche d'évolution entre deux onglets. Elle dit que les trois étapes se
   suivent, là où trois onglets côte à côte les donnent pour interchangeables. */
.lecteur__onglets-fleche {
  padding-inline: 2px;
  font-size: var(--fs-sm);
  line-height: 1;
  color: var(--lecteur-gris);
  user-select: none;
}

/* Le pavé de sélection. Ses cotes sont posées par le script — voir
   `placerCurseur()` dans lecteur.js.

   Il est en `transform` et non en `left` : une translation est composée par
   le GPU, là où animer `left` déclencherait un calcul de mise en page à
   chaque image. La largeur, elle, doit bien être animée — les trois libellés
   n'ont pas la même — mais sur un élément en position absolue, elle ne
   repousse rien autour. */
.lecteur__onglets-curseur {
  position: absolute;
  top: 4px;
  bottom: 4px;
  left: 0;
  width: 0;
  background-color: var(--c-blue);
  /* ⚠️ RAYON INTÉRIEUR = RAYON EXTÉRIEUR − ÉCART. Le bloc porte 9 px de rayon
     et 4 px de rembourrage : à 9 px lui aussi, le pavé paraissait plus rond
     que la boîte qui le contient, et les deux courbes ne se suivaient pas.
     Deux arcs concentriques ne le sont que si leurs rayons diffèrent de la
     distance qui les sépare. */
  border-radius: calc(var(--radius-md) - 4px);
  transition: transform var(--dur-base) var(--ease-out), width var(--dur-base) var(--ease-out);
}

@media (prefers-reduced-motion: reduce) {
  .lecteur__onglets-curseur {
    transition: none;
  }
}
.lecteur__onglets[hidden] {
  display: none;
}

/* Les onglets sont NUS : ni fond ni filet, c'est le curseur qui les habille
   au passage. `position: relative` les fait peindre au-dessus de lui. */
.lecteur__onglet {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 0.5625rem;
  padding: 0.5625rem 1.25rem;
  font-family: var(--font-sans);
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
  color: var(--c-blue-dark);
  background-color: transparent;
  border: 0;
  cursor: pointer;
  transition: color var(--dur-base) var(--ease-out);
}

/* Le numéro d'étape, en chasse fixe : c'est un repère, pas un mot.

   ⚠️ `font-weight` explicite sur un <b> : sans lui le navigateur applique son
   gras par défaut. Le Chivo Mono ne se met JAMAIS en gras dans ce lecteur —
   une chasse fixe épaissit mal et se met à peser. */
.lecteur__onglet b {
  font-weight: var(--fw-regular);
  font-family: var(--font-mono);
  font-size: 0.75rem;
  color: var(--lecteur-gris);
  transition: color var(--dur-base) var(--ease-out);
}

@media (hover: hover) {
  .lecteur__onglet:not([aria-selected=true]):hover {
    color: var(--c-blue);
  }
}
/* L'onglet courant n'a plus de fond à lui : le curseur le lui apporte. */
.lecteur__onglet[aria-selected=true] {
  color: var(--c-white);
}

/* Le fluo ne sert QUE sur l'onglet actif : c'est le seul endroit du lecteur
   où l'accent du site apparaisse, et il y désigne exactement une chose. */
.lecteur__onglet[aria-selected=true] b {
  color: var(--c-fluo);
}

.lecteur__onglet:focus-visible {
  outline: 2px solid var(--c-blue);
  outline-offset: 3px;
}

/* ---------- Légende ----------
   Elle est SOUS les onglets et au-dessus du cadre : elle appartient au
   sommaire, pas à l'image.

   C'est elle qui PORTE LE PROPOS de chaque scène — l'image l'illustre, elle
   ne l'énonce pas. Elle est donc traitée en titre : police de display, corps
   de h3, avec sa partie saillante en bleu d'accent. Elle était en texte
   courant à 18 px jusqu'au 14/08/2026 et passait après l'animation.

   ⚠️ AUCUNE HAUTEUR RÉSERVÉE ICI. Les trois légendes n'ont pas la même
   longueur ; sans réservation, le cadre remonterait ou descendrait à chaque
   changement de scène. Mais une valeur écrite en dur serait soit trop courte
   — et ça sauterait — soit trop longue, et c'est le trou dont on ne veut pas.
   C'est donc le script qui MESURE la plus haute des trois et réserve
   exactement ce qu'il faut. Voir `mesurerLegende()` dans lecteur.js. */
.lecteur__legende {
  max-width: 52rem;
  margin: 0 auto var(--sp-4);
  text-align: center;
  font-family: var(--font-titre);
  font-size: var(--fs-h3);
  font-weight: var(--fw-semibold);
  line-height: 1.2;
  letter-spacing: -0.02em;
  color: var(--c-blue-dark);
}

.lecteur__legende em {
  font-style: normal;
  color: var(--c-blue);
}

/* La ligne de détail revient au texte courant : deux niveaux de titre dans
   une légende, et plus rien ne domine. */
.lecteur__legende small {
  display: block;
  max-width: 44rem;
  margin: var(--sp-2) auto 0;
  font-family: var(--font-sans);
  font-size: var(--fs-base);
  font-weight: var(--fw-regular);
  line-height: var(--lh-normal);
  letter-spacing: 0;
  color: var(--text-muted);
}

/* ---------- Cadre et flèches ----------
   La zone réserve la place des flèches de part et d'autre : elles vivent
   hors du cadre, pour ne jamais passer par-dessus la scène. */
.lecteur__zone {
  position: relative;
  padding-inline: 3.625rem;
}

/* ⚠️ NI FOND NI FILET — essai du 14/08/2026. La scène est posée à même la
   page : plus de cadre autour d'elle, plus de crème sous elle. Les cartes
   portent déjà chacune leur contour et leur ombre, un cadre de plus en faisait
   un cadre dans un cadre.

   `overflow: hidden` reste : c'est lui qui contient la scène mise à
   l'échelle, indépendamment de tout habillage. */
.lecteur__cadre {
  position: relative;
  overflow: hidden;
}

/* La hauteur du cadre et l'échelle de la scène sont posées par le script :
   elles dépendent de la largeur réellement disponible, que le CSS ne connaît
   pas. Voir `ajuster()` dans lecteur.js. */
.lecteur__scene-hote {
  position: relative;
  width: 1200px;
  height: 675px;
  overflow: hidden;
  transform-origin: top left;
}

.lecteur__fleche {
  position: absolute;
  top: 50%;
  translate: 0 -50%;
  z-index: 3;
  display: grid;
  place-content: center;
  width: 2.75rem;
  height: 2.75rem;
  padding: 0;
  font-size: 1.1875rem;
  line-height: 1;
  color: var(--c-blue-dark);
  background-color: var(--c-white);
  border: var(--lecteur-filet);
  border-radius: var(--radius-full);
  cursor: pointer;
  transition: border-color var(--dur-base) var(--ease-out), background-color var(--dur-base) var(--ease-out);
}

.lecteur__fleche[hidden] {
  display: none;
}

.lecteur__fleche--prec {
  left: 0;
}

.lecteur__fleche--suiv {
  right: 0;
}

@media (hover: hover) {
  .lecteur__fleche:hover:not(:disabled) {
    border-color: var(--c-blue-dark);
  }
}
.lecteur__fleche:disabled {
  opacity: 0.32;
  cursor: default;
}

.lecteur__fleche:focus-visible {
  outline: 2px solid var(--c-blue);
  outline-offset: 3px;
}

/* =========================================================================
   LA SCÈNE — tout ce qui suit est en cotes de plan (1200 × 675).
   ========================================================================= */
/* ⚠️ `text-align: left` n'est pas une redite du défaut. Le hero centre son
   texte (`.hero__inner`) et la scène en héritait : les libellés des cartes se
   retrouvaient centrés sans que rien ne le demande ici. On reprend la main à
   la racine de la scène — ce qui doit être centré l'est ensuite
   explicitement, carte indicateur et valeurs convergentes. */
.lecteur__scene {
  position: absolute;
  inset: 0;
  text-align: left;
  opacity: 0;
  pointer-events: none;
}

.lecteur__scene.is-on {
  opacity: 1;
  pointer-events: auto;
}

/* L'horloge de référence : un élément sans dimension dont le script anime
   une propriété inerte, seulement pour disposer d'une base de temps qui se
   met en pause avec le reste. */
.lecteur__horloge {
  position: absolute;
  top: 0;
  left: 0;
  width: 0;
  height: 0;
  opacity: 0;
  pointer-events: none;
}

/* ---------- Rails et flux ----------
   Le rail est le trait au repos ; le flot est le pointillé qui le parcourt.
   Les deux suivent exactement le même tracé — c'est le même `d`, dupliqué
   dans le gabarit, ce qui garantit qu'ils ne peuvent pas diverger. */
.lecteur__flux {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.lecteur__rail {
  fill: none;
  stroke: var(--border-subtle);
  stroke-width: 1.5;
}

.lecteur__flot {
  fill: none;
  stroke: rgba(32, 43, 92, 0.42);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-dasharray: 5 13;
  /* Durée d'un tour de motif, en millisecondes. Lue par le script, qui en
     fait la cadence du défilement — voir `flotter()` dans lecteur.js. */
  --flot-cadence: 820;
  opacity: 0;
}

/* ---------- Scène 1 : des pointillés irréguliers ----------
   Les trois flux de la scène 1 ne défilent pas au même pas ni à la même
   allure. C'est le désaccord de la scène rendu jusque dans le trait : trois
   sources qui parlent, aucune qui parle comme l'autre.

   ⚠️ CHAQUE MOTIF DOIT AVOIR UN NOMBRE PAIR DE VALEURS. En nombre impair, SVG
   duplique la liste pour alterner trait et vide, et la période réelle vaut
   alors le double de la somme — le script décalerait d'une demi-période et la
   boucle sauterait à chaque tour.

   Les cadences aussi diffèrent, et pas seulement les motifs : à cadence égale
   sur des sommes voisines, les trois flux repartent presque ensemble et l'œil
   rattrape la régularité qu'on cherche à casser. */
[data-lecteur-scene="0"] [data-flot=s0] {
  stroke-dasharray: 2 11 7 16 4 9; /* somme 49 */
  --flot-cadence: 820;
}

[data-lecteur-scene="0"] [data-flot=s1] {
  stroke-dasharray: 9 7 3 15 6 12; /* somme 52 */
  --flot-cadence: 1040;
}

[data-lecteur-scene="0"] [data-flot=s2] {
  stroke-dasharray: 5 18 2 8 11 6; /* somme 50 */
  --flot-cadence: 930;
}

/* Le flux des scènes 2 et 3, une fois la donnée certifiée.

   `--c-blue` est le bleu de survol du site — celui des liens actifs, des
   questions ouvertes de la FAQ, des cartes de témoignage au passage du
   curseur. Il dit ici la même chose : cette donnée-là est vivante.

   Un essai en blanc a précédé, le 14/08/2026 : sur le crème du fond il donnait
   un contraste de 1,02, autant dire des traits qu'on devine sans les lire. */
.lecteur__flot--vif {
  stroke: var(--c-blue);
  stroke-width: 3.5;
}

/* Le rail au repos suit son flux : bleu lui aussi sur les scènes 2 et 3,
   sombre sur la scène 1, où rien n'est encore certifié. */
[data-lecteur-scene="1"] .lecteur__rail,
[data-lecteur-scene="2"] .lecteur__rail {
  stroke: var(--c-blue);
}

.lecteur__point {
  fill: var(--c-blue-dark);
  opacity: 0;
  transform-box: fill-box;
  transform-origin: center;
}

/* ---------- Scène 1 · le désaccord ---------- */
.lecteur__kpi {
  position: absolute;
  left: 120px;
  top: 232px;
  width: 460px;
  height: 211px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  background-color: var(--c-white);
  border: var(--lecteur-filet);
  border-radius: 0;
  opacity: 0;
  /* Seule carte dont le contenu est centré, comme dans la maquette. */
  text-align: center;
}

.lecteur__kpi-nom {
  margin: 0;
  font-family: var(--font-titre);
  font-size: 34px;
  font-weight: var(--fw-bold);
  line-height: 1.15;
  letter-spacing: -0.4px;
  text-align: center;
  color: var(--c-blue-dark);
}

.lecteur__kpi-tag {
  font-family: var(--font-mono);
  font-size: 22px;
  letter-spacing: 0.6px;
  color: var(--lecteur-gris);
}

.lecteur__service {
  position: absolute;
  left: 740px;
  width: 340px;
  height: 100px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-inline: 26px;
  background-color: var(--c-white);
  border: var(--lecteur-filet);
  border-radius: 0;
  opacity: 0;
}

.lecteur__service[data-service="0"] {
  top: 174px;
}

.lecteur__service[data-service="1"] {
  top: 288px;
}

.lecteur__service[data-service="2"] {
  top: 402px;
}

.lecteur__service-nom {
  font-size: 19px;
  font-weight: var(--fw-semibold);
  color: var(--text-muted);
}

/* Les trois chiffres qui se contredisent, dans le corail du logo — la seule
   couleur vive de la palette qui ne soit pas déjà prise par un pilier.

   ⚠️ `transform-origin: right` : la pulsation grandit vers la GAUCHE, donc le
   bord droit des trois nombres reste sur un axe commun. C'est ce qui permet
   de les comparer d'un coup d'œil, ce que toute la scène cherche à provoquer.
   Une pulsation centrée les ferait danser autour de leur milieu et l'axe
   disparaîtrait. */
.lecteur__service-num {
  min-width: 150px;
  text-align: right;
  font-family: var(--font-mono);
  font-size: 30px;
  font-variant-numeric: tabular-nums;
  color: var(--c-alerte);
  transform-origin: right center;
}

/* ---------- Scène 2 · la certification ---------- */
/* Le nœud Biron. C'était un rond bleu nuit portant le mot « Biron » ; c'est
   désormais un CITRON de marque portant le glyphe, comme au centre de la
   composition de « Une approche data unique ».

   Les deux calques sont empilés dans la même cellule de grille plutôt que
   positionnés en absolu : la hauteur se déduit alors du ratio de la forme,
   qu'on n'a pas à répéter ici. Le glyphe vient après dans le document, il se
   peint donc par-dessus. */
.lecteur__biron {
  position: absolute;
  left: 80px;
  top: 231px;
  width: 120px;
  aspect-ratio: 690/670; /* le ratio du citron */
  display: grid;
  place-items: center;
  opacity: 0;
}

.lecteur__biron-forme,
.lecteur__biron-glyphe {
  grid-area: 1/1;
  mask-size: 100% 100%;
  mask-repeat: no-repeat;
  -webkit-mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat;
}

.lecteur__biron-forme {
  width: 100%;
  height: 100%;
  background-color: var(--c-blue-dark);
  mask-image: url("/assets/shapes/Citron.svg");
  -webkit-mask-image: url("/assets/shapes/Citron.svg");
}

/* Le glyphe est en CRÈME et non en blanc : c'est la couleur du fond de page,
   donc le « b » se lit comme découpé dans la forme plutôt que posé dessus —
   exactement ce que fait la marque au centre du trio.

   ⚠️ 90 % DE LA FORME NE VEUT PAS DIRE UN GLYPHE À 90 %. Le tracé n'occupe
   que le milieu de sa zone de dessin — 54 % en largeur, 34 % en hauteur — donc
   le « b » rendu fait environ la moitié de la largeur du citron. C'est cette
   marge intégrée au fichier qui explique qu'il ait fallu monter si haut. */
.lecteur__biron-glyphe {
  width: 90%;
  height: 90%;
  background-color: var(--bg-page);
  mask-image: url("/assets/shapes/Trio - Marque logo.svg");
  -webkit-mask-image: url("/assets/shapes/Trio - Marque logo.svg");
}

.lecteur__fiche {
  position: absolute;
  left: 330px;
  top: 83px;
  width: 490px;
  height: 420px;
  overflow: hidden;
  background-color: var(--c-white);
  border: var(--lecteur-filet);
  border-radius: 0;
  opacity: 0;
}

.lecteur__fiche-tete {
  height: 104px;
  padding-inline: 24px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  border-bottom: var(--lecteur-filet);
}

.lecteur__ref {
  display: block;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.8px;
  color: var(--lecteur-gris);
}

.lecteur__fiche-nom {
  margin: 6px 0 0;
  font-family: var(--font-titre);
  font-size: 22px;
  font-weight: var(--fw-bold);
  letter-spacing: -0.03em;
  color: var(--c-blue-dark);
}

/* La case du chiffre. Sa largeur est RÉSERVÉE : « à certifier » et le nombre
   certifié s'y succèdent au même endroit, et sans place fixe le titre de la
   fiche se déplacerait au moment du tampon. */
.lecteur__tete-droite {
  position: relative;
  flex: 0 0 auto;
  width: 176px;
  height: 78px;
}

.lecteur__attente {
  position: absolute;
  right: 0;
  top: 25px;
  display: inline-flex;
  align-items: center;
  padding: 5px 12px;
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--lecteur-gris);
  border: 1px dashed var(--lecteur-gris);
  border-radius: var(--radius-full);
  opacity: 0;
}

.lecteur__certbloc {
  position: absolute;
  right: 0;
  top: 0;
  text-align: right;
}

.lecteur__anneau {
  position: absolute;
  right: -14px;
  top: -10px;
  width: 190px;
  height: 56px;
  border: 2px solid var(--c-blue);
  /* Coins vifs comme les cartes qu'il accompagne. */
  border-radius: 0;
  opacity: 0;
  transform-origin: center;
  pointer-events: none;
}

.lecteur__certnum {
  display: block;
  text-align: right;
  font-family: var(--font-mono);
  font-size: 34px;
  font-variant-numeric: tabular-nums;
  color: var(--c-blue);
  opacity: 0;
  transform-origin: right center;
  /* ⚠️ INTERLIGNE SERRÉ, et ce n'est pas cosmétique. Le corps du site est à
     --lh-normal (1,6) : un nombre de 34 px y occupe 54 px de hauteur de
     ligne. Avec le badge dessous et son écart, le bloc dépassait les 78 px de
     sa case et le « Certifié » venait buter sur le filet de l'en-tête. À 1,05
     l'ensemble retombe à 66 px et respire. */
  line-height: 1.05;
}

.lecteur__badge {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  margin-top: 8px;
  padding: 5px 12px;
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--c-blue-dark);
  background-color: var(--c-fluo);
  border-radius: var(--radius-full);
  opacity: 0;
}

.lecteur__badge i {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background-color: var(--c-blue-dark);
}

.lecteur__ligne {
  height: 66px;
  padding-inline: 26px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  border-bottom: var(--lecteur-filet);
  opacity: 0;
}

.lecteur__etape {
  font-size: 22px;
  font-weight: var(--fw-semibold);
  letter-spacing: -0.3px;
  color: var(--text-strong);
}

.lecteur__coche {
  flex: 0 0 auto;
  display: grid;
  place-content: center;
  width: 28px;
  height: 28px;
  font-size: 15px;
  font-weight: var(--fw-bold);
  color: var(--c-blue-dark);
  background-color: var(--c-fluo);
  border-radius: 50%;
  transform-origin: center;
}

.lecteur__fiche-pied {
  height: 50px;
  padding-inline: 26px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--lecteur-gris);
  opacity: 0;
}

/* Les trois services sous la fiche, avec le MÊME chiffre. Miroir exact de la
   scène 1 : mêmes noms, mêmes places, mais une seule valeur au lieu de
   trois. */
.lecteur__conv {
  position: absolute;
  left: 330px;
  top: 531px;
  width: 490px;
  display: flex;
  gap: 12px;
}

.lecteur__conv > div {
  flex: 1 1 0;
  padding-block: 9px;
  text-align: center;
  background-color: var(--c-white);
  border: var(--lecteur-filet);
  border-radius: 0;
  opacity: 0;
}

.lecteur__conv span {
  display: block;
  font-size: 13px;
  font-weight: var(--fw-semibold);
  color: var(--text-muted);
}

.lecteur__conv b {
  /* Voir la note sur les <b> plus haut : le gras par défaut est à défaire. */
  font-weight: var(--fw-regular);
  display: block;
  margin-top: 2px;
  font-family: var(--font-mono);
  font-size: 20px;
  font-variant-numeric: tabular-nums;
  color: var(--c-blue);
}

/* ---------- Le nœud dictionnaire, scènes 2 et 3 ---------- */
.lecteur__dico {
  position: absolute;
  width: 170px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  text-decoration: none;
  color: inherit;
  opacity: 0;
  pointer-events: none;
}

.lecteur__dico--2 {
  left: 950px;
  top: 216px;
}

.lecteur__dico--3 {
  left: 80px;
  top: 263px;
}

.lecteur__dico img {
  width: auto;
  height: 150px;
}

/* Un cartouche blanc sous le glyphe, à la manière de la légende du trio de
   « Une approche data unique » : le nom flottait sur le crème sans attache,
   alors que tout le reste de la scène est posé sur du blanc. */
.lecteur__dico-nom {
  padding: 8px 14px;
  background-color: var(--c-white);
  border: var(--lecteur-filet);
  transition: color var(--dur-base) var(--ease-out), border-color var(--dur-base) var(--ease-out);
  font-size: 18px;
  font-weight: var(--fw-semibold);
  line-height: 1.25;
  letter-spacing: -0.2px;
  text-align: center;
  color: var(--c-blue-dark);
}

/* ---------- Scène 3 · les trois usages ---------- */
.lecteur__usage {
  position: absolute;
  left: 470px;
  width: 640px;
  height: 120px;
  display: flex;
  align-items: center;
  padding: 0 24px 0 75px;
  text-decoration: none;
  color: inherit;
  background-color: var(--c-white);
  border: var(--lecteur-filet);
  border-radius: 0;
  opacity: 0;
  pointer-events: none;
  transition: border-color var(--dur-base) var(--ease-out);
}

.lecteur__usage[data-usage="0"] {
  top: 112px;
}

.lecteur__usage[data-usage="1"] {
  top: 278px;
}

.lecteur__usage[data-usage="2"] {
  top: 444px;
}

/* L'icône déborde de la carte par la gauche, à cheval sur le rail qui
   l'alimente : c'est ce qui rattache visuellement l'usage à sa source.

   `--lecteur-ico-echelle` rattrape l'inégalité des exports — voir la note
   dans hero.yaml, où il est aussi dit qu'aucun usage ne l'emploie plus depuis
   le 25/08/2026, le viewBox du connecteur ayant été recadré sur son dessin.
   L'agrandissement se fait AUTOUR DU CENTRE du glyphe, ce qui
   le laisse à sa place : `scale` s'applique avant `translateY` dans une liste
   de transformations, et son origine par défaut est le centre. Corriger par
   la hauteur aurait au contraire décalé l'icône vers le bas et la gauche. */
.lecteur__usage-ico {
  position: absolute;
  left: -55px;
  top: 50%;
  width: auto;
  height: 110px;
  transform: translateY(-50%) scale(var(--lecteur-ico-echelle, 1));
}

.lecteur__usage-txt {
  flex: 1 1 auto;
  min-width: 0;
}

.lecteur__usage-lab {
  display: block;
  font-size: 24px;
  font-weight: var(--fw-semibold);
  line-height: 1.2;
  letter-spacing: -0.3px;
  color: var(--text-strong);
}

.lecteur__usage-sous {
  display: block;
  margin-top: 4px;
  font-size: 18px;
  line-height: 1.32;
  color: var(--text-muted);
}

.lecteur__logos {
  display: flex;
  align-items: center;
  gap: 16px;
}

.lecteur__logo {
  display: block;
  width: auto;
}

/* Texte et non plus `<img>` depuis le 16/09/2026 — voir partials/lecteur.njk.
   Le corps est calé pour occuper à l'œil la même hauteur que les 24px de
   l'ancien export, chasse d'un logotype comprise. */
.lecteur__logo--biron {
  font-family: var(--font-serif);
  font-size: 1.375rem;
  font-weight: var(--fw-bold);
  letter-spacing: var(--ls-tight);
  line-height: 1;
  color: var(--text-strong);
}

.lecteur__logo--claude {
  width: 34px;
  height: 34px;
}

.lecteur__logo--excel {
  height: 33px;
}

.lecteur__logo--zapier {
  width: 31px;
  height: 31px;
}

/* La flèche n'apparaît qu'une fois la scène posée : tant que l'animation
   court, le lien n'est pas cliquable et ne doit rien promettre. */
.lecteur__fleche-lien {
  flex: 0 0 auto;
  margin-left: 12px;
  font-size: 20px;
  color: var(--c-blue);
  opacity: 0;
  transform: translateX(-6px);
  transition: opacity var(--dur-base) var(--ease-out), transform var(--dur-base) var(--ease-out);
}

/* ---------- Les liens deviennent vivants à la fin de la scène ---------- */
.lecteur__usage.is-actif,
.lecteur__dico.is-actif {
  pointer-events: auto;
  cursor: pointer;
}

.lecteur__usage.is-actif .lecteur__fleche-lien {
  opacity: 1;
  transform: none;
}

@media (hover: hover) {
  .lecteur__usage.is-actif:hover {
    border: var(--lecteur-filet-survol);
  }
  .lecteur__usage.is-actif:hover .lecteur__fleche-lien {
    transform: translateX(5px);
  }
  /* Le cartouche du dictionnaire réagit comme une carte : son filet suit, pas
     seulement la couleur de son texte. */
  .lecteur__dico.is-actif:hover .lecteur__dico-nom {
    color: var(--c-blue);
    border: var(--lecteur-filet-survol);
  }
}
.lecteur__usage.is-actif:focus-visible,
.lecteur__dico.is-actif:focus-visible {
  outline: 2px solid var(--c-blue);
  outline-offset: 4px;
}

/* =========================================================================
   Écrans étroits.

   La scène garde ses cotes de plan et se contente de rétrécir : à 1200 px de
   large ramenés à 360, le texte de la fiche tomberait sous 7 px. On admet
   donc que le lecteur devienne illisible en dessous d'un certain point — et
   plutôt que de le laisser en l'état, les flèches se rapprochent pour rendre
   la place au cadre, seul remède qui ne demande pas de redessiner la scène.

   ⚠️ Une vraie version mobile demanderait une composition à part, verticale.
   C'est un chantier en soi, pas un réglage : à ouvrir si le trafic mobile le
   justifie.
   ========================================================================= */
@media (max-width: 60rem) {
  .lecteur__zone {
    padding-inline: var(--sp-8);
  }
  .lecteur__fleche {
    width: 2.25rem;
    height: 2.25rem;
    font-size: 1rem;
  }
  .lecteur__legende {
    min-height: 5.5rem;
  }
}
/* =========================================================================
   Bandeau clients — blocs/clients.njk.

   ⚠️ C'EST UN BLOC DEPUIS LE 21/08/2026, plus une section de l'accueil : la
   page des cas clients le reprend sous son hero. Deux hôtes donc, et deux
   habillages — voir « le cadre fermé est le défaut » plus bas.
   ========================================================================= */
.clients {
  /* `position` obligatoire : sans elle, la section reste un bloc statique
     et son fond se peint AVANT le décor de .intro, qui est positionné. Le
     cadre laisserait alors la trame apparaître au travers. */
  position: relative;
  z-index: 1;
  /* ⚠️ RESSERRÉ EN BAS LE 21/08/2026 (« supprimer un peu d'espace entre la boîte des
     clients et le cas mis en avant »). La grande respiration de l'accueil est
     rétablie plus bas, sous `.intro` : là-bas le bandeau ferme un plan de décor et a
     besoin de place ; ailleurs il est suivi d'une section qui pose déjà la sienne, et
     les deux s'additionnaient — jusqu'à 14rem de vide. */
  padding-block: clamp(4rem, 2rem + 5vw, 7rem) clamp(var(--sp-8), 3vw, var(--sp-12));
}

/* Un bandeau qui suit directement un hero n'ajoute pas sa propre respiration :
   celle du hero suffit, et la maquette de /cas-clients/ pose d'ailleurs les
   deux dans la même bande. Sans cette règle les deux rembourrages
   s'additionnaient, soit près de 12rem de vide entre le chapeau et la piste. */
.hero-page + .clients {
  padding-top: 0;
}

/* Cotes reprises de la maquette. Le fond est opaque dans les deux variantes : c'est
   lui qui découpe la trame derrière le cadre et donne l'impression d'une carte posée
   dessus.

   ⚠️ LES DEUX HÔTES N'ONT PAS LE MÊME HABILLAGE, et le partage suit celui du cadre
   ouvert / fermé déjà en place :

     hors accueil  fond CRÈME, filet BLEU — une carte assumée, dans la grammaire des
                   panneaux du site. Demande d'Hugo le 21/08/2026 pour /cas-clients/.
     sur l'accueil fond de la page, filet FLUO — le bandeau y est le prolongement du
                   hero, pas un objet à part.

   ⚠️ LE CRÈME EST ÉCRIT EN CLAIR ICI, ET C'EST VOULU. Sur l'accueil ce fond doit
   suivre celui de la page (--bg-page), puisque son rôle est de la masquer ; ailleurs
   il ne masque plus rien, c'est une carte qui a sa couleur à elle. Les deux rôles ont
   été confondus le matin même, quand le fond de page est passé au bleu pâle et que
   la carte a suivi sans qu'on le veuille. */
.clients__frame {
  display: flex;
  flex-direction: column;
  gap: 1.25rem; /* 20px */
  padding: 1.875rem 2.5625rem; /* 30px 41px */
  background-color: var(--c-base-main);
  border: 1px solid var(--filet-panneau-teinte);
}

/* ---------- L'accueil : un cadre OUVERT par le bas ----------
   Là-bas le bandeau se prolonge dans la bande de trame qui suit, et une arête
   en bas couperait ce prolongement. Ailleurs il n'y a rien à prolonger, et un
   cadre ouvert se lirait comme inachevé — d'où le cadre fermé par défaut.

   ⚠️ LA BASCULE PASSE PAR L'ANCÊTRE, PAS PAR UN CHAMP DU BLOC : la donnée de
   `_data/clients.yaml` est partagée par les deux hôtes, elle n'a pas à décrire
   la mise en page de l'un d'eux. Voir l'en-tête de blocs/clients.njk. */
.intro .clients {
  padding-bottom: 0;
}

/* Le bandeau qui suit un hero n'ajoute pas sa propre respiration en haut : celle du
   hero suffit. Voir la règle d'adjacence plus haut. */
/* ⚠️ HORS ACCUEIL, LA PISTE EST PLUS DENSE (« la grille du bloc client est trop
   large, elle peut être plus dense », 21/08/2026). Les cotes de la maquette — 30/41px
   de rembourrage, 60px entre deux noms — sont celles de l'accueil, où le bandeau est
   le prolongement du hero et a toute la place. Ailleurs c'est une carte parmi
   d'autres, et à cet écartement elle n'affichait que quatre ou cinq noms.

   Resserré des deux côtés : le rembourrage du cadre et l'écart entre les noms. La
   variante de l'accueil, elle, garde les cotes de la maquette — voir juste en
   dessous. */
.clients__frame {
  padding: var(--sp-5) var(--sp-6);
}

.clients__item {
  padding-inline: var(--sp-5); /* 20px, soit 40px entre deux noms au lieu de 60 */
}

.intro .clients__frame {
  /* Sur l'accueil, le cadre reprend le filet fluo de la maquette, le fond de la page
     et ses cotes d'origine — voir le partage expliqué plus haut. */
  padding: 1.875rem 2.5625rem; /* 30px 41px */
  background-color: var(--bg-page);
  border-color: var(--c-fluo);
  border-bottom: none;
}

.intro .clients__item {
  padding-inline: 1.875rem; /* 30px de chaque côté, soit 60px entre deux noms */
}

/* =========================================================================
   LE QUADRILLAGE AUTOUR DU BANDEAU — variante `trame`.

   Demande d'Hugo le 21/08/2026 : « la grille uniquement autour de la section
   client ». Elle remplace un premier montage où la trame venait du HERO, dont le
   décor débordait sous sa propre section pour raccorder les deux bandes.

   ⚠️ CE PREMIER MONTAGE MARCHAIT, ET IL VAUT DE SAVOIR CE QU'IL COÛTAIT : il
   faisait peindre une section par sa voisine, il exigeait de celle-ci qu'elle reste
   TRANSPARENTE, et il imposait au hero un `overflow: visible` qui interdisait à ses
   formes de déborder sur les flancs — donc de se replier comme partout ailleurs.
   Trois contraintes croisées pour un décor. En donnant la trame à la section qui la
   montre, les trois tombent d'un coup.

   ⚠️ LE CADRE ÉTANT OPAQUE, LA TRAME N'APPARAÎT QU'AUTOUR DE LUI, et c'est tout
   l'effet : la carte se lit comme posée sur le quadrillage. Rien à découper à la
   main, c'est le fond du cadre qui s'en charge — exactement le procédé de l'accueil,
   où la trame vient du plan de décor de `.intro`.

   ⚠️ LA SECTION REPREND SA RESPIRATION EN HAUT. `.hero-page + .clients` la met à
   zéro pour coller le bandeau au hero ; sans marge, la trame n'aurait aucune place
   au-dessus du cadre et ne se verrait que dans les gouttières latérales.
   ========================================================================= */
.clients--trame {
  padding-block: clamp(var(--sp-8), 4vw, var(--sp-12));
}

.hero-page + .clients--trame {
  padding-top: clamp(var(--sp-8), 4vw, var(--sp-12));
}

/* ⚠️ LE DÉCOR MONTE AU-DESSUS DE LA SECTION, demandé par Hugo le 21/08/2026 : « faire
   grandir la grille vers le haut ». Il dépasse donc l'arête haute de --clients-trame-haut
   et vient mordre sur le bas du hero.

   ⚠️ C'EST SANS DANGER POUR LE TEXTE DU HERO, et c'est mesuré : le débord (48 → 96px)
   reste inférieur au rembourrage bas du hero (--sp-section, 64 → 120px), donc il ne
   monte que dans du vide. Une valeur plus grande passerait sous le chapeau.

   ⚠️ ET LE BANDEAU PEINT PAR-DESSUS LE HERO SANS RIEN À DÉCLARER : il porte déjà
   `z-index: 1` quand le hero est à zéro. C'est la même propriété qui, dans l'autre
   sens, empêchait le montage précédent — le décor du hero débordant vers le bas — de
   se voir dès que cette section peignait un fond. */
.clients--trame {
  --clients-trame-haut: clamp(3rem, 6vw, 6rem);
  --clients-trame-bas: clamp(2rem, 4vw, 4rem);
}

.clients__decor {
  position: absolute;
  inset: calc(-1 * var(--clients-trame-haut, 0px)) 0 0;
  /* Sous le cadre : un enfant à `z-index: 0` passe derrière un frère positionné plus
     loin dans le DOM au même niveau. */
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
}

/* Le fondu des deux bords : la trame naît du fond et y retourne, au lieu de buter
   sur les arêtes du décor. Sans lui on lirait deux traits horizontaux francs.

   ⚠️ LES ARRÊTS SONT EN LONGUEUR, PAS EN POURCENTAGE, et c'est ce qui rend les deux
   cotes indépendantes : un pourcentage se compte sur la hauteur du décor, or celle-ci
   contient désormais le débord — régler la hauteur du débord déplaçait alors aussi la
   naissance de la trame, et les deux réglages se poursuivaient. En longueur, le fondu
   du haut occupe exactement le débord : la trame naît à son sommet et atteint sa
   pleine force à l'arête de la section. */
.clients__grille {
  position: absolute;
  inset: 0;
  mask-image: linear-gradient(to bottom, transparent 0, black var(--clients-trame-haut, 22%), black calc(100% - var(--clients-trame-bas, 22%)), transparent 100%);
}

/* Le contenu passe devant la trame. `.clients` porte déjà `position: relative`. */
.clients > .container {
  position: relative;
  z-index: 1;
}

/* Mono capitales, interlettrage 0.14em — les cotes de la maquette
   (10,5 px / 1,47 px) correspondent exactement au token --ls-wide. */
.clients__label {
  text-align: center;
  color: var(--text-strong);
}

/* Le viewport masque la piste et estompe ses deux bords : sans ce fondu,
   les noms apparaissent et disparaissent d'un coup sur une arête franche,
   ce qui trahit immédiatement le procédé. */
.clients__viewport {
  overflow: hidden;
  mask-image: linear-gradient(to right, transparent, black 8%, black 92%, transparent);
}

.clients__track {
  display: flex;
  width: max-content;
  /* Repli sans JS : la piste défile en CSS. GSAP reprend la main dès qu'il
     est chargé et remplace cette animation par la sienne, calée sur la
     largeur réelle pour une vitesse constante. */
  animation: clients-defile 40s linear infinite;
}

@keyframes clients-defile {
  to {
    transform: translateX(-50%);
  }
}
.clients__list {
  display: flex;
  align-items: center;
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Recoleta SemiBold 18px en Primary Blue — repris tel quel de la maquette.
   Les références ne sont pas en mono : c'est le serif qui porte le nom des
   marques, la mono ne sert qu'au label au-dessus.

   Ce traitement typographique est le REPLI d'une référence sans logo. Les 27px
   de la maquette deviennent donc un PLANCHER de hauteur de rang, et non plus
   une hauteur fixe — voir juste en dessous pourquoi les logos ne s'y calent
   plus. */
.clients__item {
  display: flex;
  align-items: center;
  min-height: 1.6875rem; /* 27px — la hauteur de rang de la maquette */
  padding-inline: 1.875rem; /* 30px de chaque côté, soit 60px entre deux */
  font-family: var(--font-serif);
  font-size: 1.125rem;
  font-weight: var(--fw-semibold);
  line-height: 1;
  color: var(--c-blue);
  white-space: nowrap;
}

/* =========================================================================
   LA HAUTEUR D'UN LOGO VIENT DE SON FICHIER, PAS DE LA FEUILLE.

   ⚠️ TOUS LES LOGOS AVAIENT LA MÊME HAUTEUR DE RANG JUSQU'AU 28/08/2026, et
   c'était le défaut. Les huit exports sont déjà équilibrés les uns par rapport
   aux autres — le macaron carré de Cabaïa fait 66 px de haut, le logotype
   filiforme de Smallable 24 — et les ramener tous à 27 px écrasait cet
   équilibre : le macaron sortait à 23 px de large contre 227 pour Smallable, un
   dixième de sa place. Hugo a tranché sur capture de référence : ce sont les
   proportions d'export qui doivent se retrouver.

   `--clients-logo-h` porte la hauteur d'export EN PIXELS, posée sur l'élément
   par blocs/clients.njk et LUE DANS LE FICHIER (filtre `dimensions`). Rien n'est
   écrit à la main, donc un logo réexporté à une autre taille reprend sa place
   tout seul — et aucune table de huit valeurs n'est à tenir accordée avec le
   dossier de dépôt.

   ⚠️ `* 1rem / 16` CONVERTIT CES PIXELS D'EXPORT EN REM, et ce n'est pas une
   coquetterie : le site rend à 85 % de son échelle écrite (`html { font-size:
   85% }`, voir CLAUDE.md), donc un logo exprimé en `px` serait le seul objet de
   la page à ne pas suivre ce facteur. Écrit ainsi, il le suit — 66 px d'export
   rendent 56 px à l'écran, comme un `1.6875rem` rend 23 px.

   ⚠️ L'ÉCHELLE COMMUNE A SON DÉFAUT DANS LE `var()`, jamais dans une
   déclaration sur l'élément : une propriété posée SUR le logo battrait celle
   qu'un ancêtre lui donne, et le réglage des écrans étroits plus bas serait
   silencieusement sans effet. Règle du site — voir CLAUDE.md « A component knob
   that an ancestor is meant to override ».

   `width: auto` laisse la largeur suivre le ratio intrinsèque : aucun logo n'est
   déformé, et aucune largeur n'est écrite nulle part.
   ========================================================================= */
.clients__logo {
  height: calc(var(--clients-logo-h, 27) * var(--clients-logo-echelle, 1) * 1rem / 16);
  width: auto;
}

/* Le survol suspend le défilement : on laisse le temps de lire un nom qui
   accroche l'œil. */
.clients__viewport:hover .clients__track {
  animation-play-state: paused;
}

/* =========================================================================
   Écrans étroits.

   Les cotes de la maquette sont celles d'un écran large : 41 px de
   rembourrage de chaque côté, c'est 82 px pris sur les 390 d'un téléphone, et
   le fondu des deux bords en mange 16 % de plus. Il ne restait la place que de
   deux noms. On resserre tout.
   ========================================================================= */
@media (max-width: 48rem) {
  .clients__frame {
    padding: var(--sp-5) var(--sp-4);
  }
  /* 20 px de chaque côté, soit 40 px entre deux noms au lieu de 60. */
  .clients__item {
    padding-inline: var(--sp-5);
  }
  /* ⚠️ LES LOGOS RETOMBENT D'UN QUART, et c'est une contrainte de HAUTEUR, pas de
     largeur : à l'échelle 1 le rang est commandé par le macaron de Cabaïa, soit
     56 px, et le bandeau passait à une centaine de pixels de haut sur un
     téléphone — pour un objet qui n'est qu'un bandeau de références sous le hero.
     À 0,75 il retombe à 42 px, au-dessus du plancher de rang.

     Les proportions entre les huit sont intactes : c'est un facteur commun, ce
     que la hauteur unique d'avant ne savait justement pas faire. */
  .clients {
    --clients-logo-echelle: 0.75;
  }
  .clients__viewport {
    mask-image: linear-gradient(to right, transparent, black 5%, black 95%, transparent);
  }
}
@media (prefers-reduced-motion: reduce) {
  .clients__track {
    animation: none;
    /* Sans mouvement, la piste doit rester lisible : on la ramène à une
       liste centrée qui passe à la ligne, et le second exemplaire est
       retiré. */
    width: 100%;
    justify-content: center;
  }
  .clients__list[aria-hidden=true] {
    display: none;
  }
  .clients__list {
    flex-wrap: wrap;
    justify-content: center;
    row-gap: var(--sp-3);
  }
  .clients__item {
    padding-inline: var(--sp-4);
  }
  .clients__viewport {
    mask-image: none;
  }
}
/* =========================================================================
   Transition halftone

   Le motif est généré au build en SVG (voir eleventy.config.js), parce que
   trois exigences dépassent ce que le CSS sait faire :

   - la grille reste régulière mais la TAILLE des points est bruitée, ce qui
     demande une valeur différente par cellule ;
   - ce sont de vrais cercles, pas des dégradés radiaux tronqués ;
   - la montée en densité suit une ELLIPSE et non une ligne, ce qui enlève
     au procédé son caractère industriel et carré.

   Le SVG est un MASQUE : la couleur vient d'ici, donc un seul fichier sert
   les deux sens de transition — le sens sortie retourne simplement le
   motif.
   ========================================================================= */
.halftone {
  /* ⚠️ --bg-page ET NON LE CRÈME EN DUR. Une bande de transition naît sur le fond
     de la PAGE — c'est tout son métier. Écrite en dur, elle posait un liseré crème
     dès que le fond de page a changé (21/08/2026). */
  --halftone-fond: var(--bg-page);
  --halftone-encre: var(--c-blue-dark);
  position: relative;
  /* La maquette donne 410 px pour 1904 de large, soit ~21 % de la largeur.
     On s'en approche sans aller jusqu'au bout : une bande de transition de
     410 px coûte beaucoup de défilement pour du pur décor. */
  height: clamp(8rem, 3rem + 13vw, 22rem);
  background-color: var(--halftone-fond);
  overflow: hidden;
  pointer-events: none;
}

/* Sens sortie : du bleu nuit vers le crème.

   Les couleurs ne changent PAS — ce sont toujours des points bleus sur un
   fond crème. C'est le MOTIF qu'on retourne : dense en haut pour raccorder
   la section sombre, clairsemé en bas pour rejoindre le crème. Le
   retournement inverse aussi la vague, crêtes et creux s'échangeant. */
.halftone--sortie .halftone__trame {
  transform: scaleY(-1);
}

/* Liseré de raccord.

   Le masque est mis à l'échelle de la hauteur de bande : sa dernière rangée
   tombe donc sur un pixel fractionnaire et ressort partiellement
   transparente. Le fond crème transparaît alors en un trait d'un pixel,
   juste à la jointure avec le bloc sombre.

   On couvre cette arête par un aplat de 2 px du côté saturé du motif — en
   bas à l'entrée, en haut à la sortie, puisque le motif y est retourné. */
.halftone::after {
  content: "";
  position: absolute;
  inset: auto 0 -1px;
  height: 5px;
  background-color: var(--halftone-encre);
}

.halftone--sortie::after {
  inset: -1px 0 auto;
}

/* Ceinture et bretelles : le fond de la bande vire à l'encre sur ses
   derniers pour-cent, du côté saturé. Là où le masque est déjà plein, cela
   ne change rien ; si une arête ressort transparente, c'est de l'encre
   qu'on voit au travers et non du crème. */
.halftone {
  background-image: linear-gradient(to bottom, transparent 88%, var(--halftone-encre));
}

.halftone--sortie {
  background-image: linear-gradient(to top, transparent 88%, var(--halftone-encre));
}

/* Le motif est PAVÉ horizontalement. `auto 100%` le met à l'échelle de la
   hauteur de bande uniquement : l'échelle reste uniforme, donc les cercles
   restent ronds, et la répétition garantit une couverture pleine largeur
   quel que soit l'écran.

   C'est ce que permet la vague : périodique, elle se raccorde exactement
   d'une répétition à l'autre. Une ellipse unique aurait imposé d'étirer le
   masque à la largeur de l'écran, avec toutes les déformations que ça
   entraîne. */
.halftone__trame {
  position: absolute;
  inset: 0;
  background-color: var(--halftone-encre);
  mask-image: url("/assets/img/halftone-mask.svg");
  mask-size: auto 100%;
  mask-repeat: repeat-x;
  mask-position: center bottom;
  -webkit-mask-image: url("/assets/img/halftone-mask.svg");
  -webkit-mask-size: auto 100%;
  -webkit-mask-repeat: repeat-x;
  -webkit-mask-position: center bottom;
}

/* Le sens sortie est traité plus haut, par le `scaleY(-1)` posé sur la
   trame : il n'y a rien de plus à faire ici.

   Un commentaire décrivait à cet endroit une autre approche — garder le
   masque tel quel et échanger les deux couleurs. Elle a été abandonnée, et
   le commentaire est resté à décrire du code qui n'existe pas. Vérifié en
   capture le 12/08/2026 : c'est bien le retournement qui produit la sortie,
   et les couleurs ne changent jamais. */
/* =========================================================================
   Séquence sombre — trois panneaux enchaînés au scroll
   ========================================================================= */
/* La section elle-même ne porte plus de fond : depuis le 13/08/2026 le
   sombre est tenu par une BOÎTE arrondie posée sur le crème de la page, et
   non plus par un aplat pleine largeur. C'est la section qui garde la
   hauteur d'écran, parce que c'est elle que ScrollTrigger épingle. */
.sequence {
  position: relative;
  /* Un écran plein, et il le faut : la section est épinglée en HAUT de la
     fenêtre (`start: "top top"` dans sequence.js). Toute hauteur inférieure
     décolle la boîte du centre de l'écran et laisse en dessous une bande de
     page nue que la trame ne couvre pas.

     Essayée à 78 % le 13/08/2026 pour alléger la section, puis remise à 100 :
     c'est la DURÉE de l'épinglage qui pesait, pas la hauteur du bloc. Elle se
     règle par `data-sequence-duree` sur la section. */
  min-height: 100svh;
  display: grid;
  align-content: center;
  padding-block: var(--sp-12);
  padding-inline: var(--container-pad);
}

/* La règle qui peignait le `pin-spacer` en bleu nuit a disparu avec l'aplat.
   Elle existait parce que ce conteneur n'a aucun fond, et que le crème de la
   page transparaissait en liseré contre les bandes halftone. Ce fond nu est
   désormais exactement ce qu'on veut voir autour de la boîte. */
/* La boîte. 28 px de rayon et un fond `--c-blue-dark`, relevés sur la
   maquette.

   ⚠️ Ces 28 px ne passent pas par `--radius-cadre` (12 px), le jeton tranché
   le matin même comme rayon unique des cadres du site. La maquette est
   formelle, et à cette échelle — une boîte qui occupe presque tout l'écran —
   12 px se lirait comme un angle droit. Le jeton reste juste pour les
   panneaux et les captures ; il ne couvre simplement pas cet objet-là. */
/* La trame de fond couvre toute la section, sous la boîte : ce qu'on en voit
   est donc la bande au-dessus, celle en dessous et les deux marges. Elle
   remplace les bandes halftone d'entrée et de sortie qui ont sauté avec
   l'aplat — même motif, mais continu et en mouvement au lieu de deux
   transitions posées bout à bout.

   Dessinée dans un canvas par trame-fond.js ; il ne reste ici que le
   cadrage. Sans JavaScript le canvas est vide, donc invisible. */
.sequence__trame {
  position: absolute;
  inset: 0;
  z-index: 0;
  display: block;
  width: 100%;
  height: 100%;
  pointer-events: none;
  /* Le fondu haut et bas qui raccorde aux sections voisines n'est PAS ici.
     Il a d'abord été fait en `mask-image`, et c'était une erreur : un masque
     joue sur l'opacité, donc les points pâlissent sans disparaître et la
     grille reste lisible jusqu'au bord — on voit un rectangle grisé, pas une
     transition. Les anciennes bandes halftone marchaient parce que c'est le
     CALIBRE des points qui tombait à zéro.

     Le fondu est donc dans le canvas, réglé par `data-fondu`. */
}

.sequence__boite {
  position: relative;
  z-index: 1;
  width: 100%;
  /* 1421 px dans la maquette, soit --container-hero à quelques pixels près.
     Élargie à 1536 le 13/08/2026 : la boîte est le seul objet plein cadre de
     la page, elle supporte de déborder de la grille. */
  max-width: 96rem;
  margin-inline: auto;
  /* 78/178 dans la maquette, sur une boîte de 1421. Les bornes basses
     ramènent la boîte à une carte mobile plutôt qu'à un cadre vide.

     ⚠️ Le plancher du rembourrage horizontal a été RELEVÉ le 27/08/2026 (Hugo :
     « Observation, Problématique, Résolution est un peu trop serré en mobile,
     augmenter un peu les marges sur les bords quitte à réduire un peu les
     polices »). C'est le PLANCHER seul qui bouge, et la pente est inchangée :
     les 34 px tiennent donc jusqu'à ~510 px de large, largeur à laquelle la
     courbe d'origine les rejoint exactement — aucun saut à la jointure, et
     rien ne change au-dessus.

     C'est bien le rembourrage INTÉRIEUR qu'on ouvre, et non la bande de crème
     autour de la boîte : celle-là est `--container-pad`, partagée par toutes
     les sections du site, et l'élargir ici seul décalerait cette boîte de
     toutes les autres. */
  padding-block: clamp(2.5rem, 1.5rem + 3vw, 4.875rem);
  padding-inline: clamp(2.5rem, -0.5rem + 8vw, 11.125rem);
  background-color: var(--c-blue-dark);
  color: var(--c-white);
  /* Coins arrondis, et AUCUN contour — comme la boîte des témoignages, qui
     reprend ce même rayon.

     ⚠️ LES DEUX BOÎTES SOMBRES SE TRAITENT ENSEMBLE. Elles sont passées aux
     coins vifs le 14/08/2026 puis y ont renoncé : c'est le seul endroit du
     site où l'arrondi survit, les panneaux clairs restant carrés. Changer
     l'une sans l'autre est toujours une erreur.

     Sur l'absence de contour : un filet fluo a été essayé puis retiré. La
     raison n'est pas esthétique — sur bleu nuit posé sur crème, l'arête existe
     déjà par le contraste, un filet n'y ajoute rien qu'une couleur de plus.
     C'est l'inverse des panneaux CLAIRS, dont le blanc sur crème ne se
     distingue quasiment pas : là, le filet ne décore pas, il fait exister le
     bord. D'où deux traitements différents pour une même famille d'objets. */
  border-radius: 1.75rem; /* 28px */
  overflow: hidden;
}

/* Le gabarit horizontal est désormais celui de la boîte : le `.container`
   des panneaux ajouterait par-dessus sa propre marge et sa propre largeur
   maximale, et le contenu se retrouverait deux fois en retrait. */
.sequence__boite .container {
  max-width: none;
  padding-inline: 0;
}

.sequence__scene {
  position: relative;
  z-index: 1;
  display: grid;
  place-items: center;
}

/* =========================================================================
   Effets de fond — signaler qu'on avance alors que le texte reste en place

   Trois principes à l'essai dans /labo/sequence/. Tous se pilotent par la
   progression du défilement (voir sequence.js) et restent sous le contenu.
   ========================================================================= */
.sequence__fond {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
}

/* --- Points qui dérivent ---
   Reprend le motif du halftone plutôt que d'introduire une forme nouvelle.
   Débordement volontaire en haut et en bas pour que la dérive ne découvre
   jamais de bord vide. */
.sequence__points {
  position: absolute;
  inset: -25% 0;
  background-image: radial-gradient(circle, rgba(255, 255, 255, 0.11) 1.2px, transparent 1.4px);
  background-size: 26px 26px;
  will-change: transform;
}

/* --- Jauge de progression --- */
.sequence__jauge {
  position: absolute;
  left: clamp(1rem, 3vw, 3rem);
  top: 50%;
  translate: 0 -50%;
  width: 2px;
  height: min(38vh, 20rem);
  background-color: rgba(255, 255, 255, 0.16);
  border-radius: var(--radius-full);
  overflow: hidden;
}

.sequence__jauge > span {
  display: block;
  width: 100%;
  height: 0%;
  background-color: var(--c-fluo);
  border-radius: inherit;
}

/* --- Nuages ---
   Deux masses très floutées, plus sombres que le fond. Elles se déplacent
   à contre-sens du défilement : le mouvement se perçoit sans jamais
   attirer l'œil, parce qu'aucun contour n'est identifiable. */
.sequence__nuages {
  position: absolute;
  inset: -35%;
  background: radial-gradient(38% 30% at 28% 32%, rgba(11, 21, 48, 0.95), transparent 68%), radial-gradient(34% 26% at 72% 62%, rgba(11, 21, 48, 0.8), transparent 70%), radial-gradient(30% 24% at 52% 88%, rgba(63, 88, 215, 0.22), transparent 72%);
  filter: blur(30px);
  will-change: transform;
}

/* Les trois panneaux occupent la même cellule : ils se succèdent au même
   endroit, comme les trois instances empilées de la maquette. */
.panneau {
  grid-area: 1/1;
  width: 100%;
}

.panneau__grille {
  display: grid;
  grid-template-columns: minmax(0, 458fr) minmax(0, 494fr);
  gap: clamp(2rem, 1rem + 4vw, 5.75rem);
  align-items: start;
}

@media (max-width: 60rem) {
  .panneau__grille {
    grid-template-columns: 1fr;
    gap: var(--sp-8);
  }
}
/* Chip teintée fluo, le même traitement que les pastilles de « Au cœur de
   Biron » — demandé le 13/08/2026. Tout est dans `.pastille--chip`
   (_components.scss) ; il ne reste ici que le ton et la marge basse.

   Elle passe au passage de --fs-h2 (48 px) au corps de la chip (22 px). Le
   changement est net, et il est voulu : « Problématique » en mono à 48 px
   faisait 375 px de large et se lisait comme un second titre à côté du vrai. */
.panneau__badge {
  --pastille-ton: var(--c-fluo);
  margin-bottom: var(--sp-6);
}

.panneau__titre {
  font-family: var(--font-serif);
  font-size: var(--fs-h2);
  font-weight: var(--fw-semibold);
  line-height: 1.18;
  letter-spacing: -0.02em;
  color: var(--c-white);
  text-wrap: balance;
}

/* La partie mise en avant du titre passe en fluo adouci — le #d1ff00 pur
   serait trop violent sur une phrase entière. */
.panneau__accent {
  color: var(--c-starship);
}

.panneau__corps {
  font-size: var(--fs-base);
  line-height: 1.65;
  color: var(--c-blue-100);
}

.panneau__cta {
  margin-top: var(--sp-8);
}

/* =========================================================================
   Composition « duo », celle que la page de production emploie
   (sections/sequence.njk passe la variante « duo » au partial).

   Les trois compositions comparées en atelier — « centre », « large » et
   « colonnes » — ne servent plus qu'à /labo/sequence-composition/ : elles
   vivent dans labo/_labo.scss, qui n'est chargé que par l'atelier.
   ========================================================================= */
/* Titre et corps côte à côte, le titre sur les deux tiers. Demandé le
   13/08/2026, en même temps que la boîte.

   Ce n'est pas la variante C de l'atelier : celle-là donne les deux tiers
   au CORPS. Ici le rapport est inversé, le titre est la masse et le texte
   la colonne d'appoint.

   Les lignes de base sont alignées (`baseline`) : la pilule décale le haut
   de la colonne de titre, un `start` ferait démarrer les deux colonnes à
   des hauteurs visiblement différentes. */
.panneau--duo .panneau__grille {
  grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
  align-items: baseline;
  gap: clamp(2rem, 1rem + 3vw, 3.5rem);
}

.panneau--duo .panneau__titre {
  font-size: var(--fs-h1);
}

@media (max-width: 60rem) {
  .panneau--duo .panneau__grille {
    grid-template-columns: minmax(0, 1fr);
  }
}
/* Contrepartie du rembourrage relevé (voir `.sequence__boite`) : sur un
   téléphone la marge gagnée est prise sur la colonne de texte, et Hugo l'a
   accordée — « quitte à réduire un peu les polices ».

   Le titre redescend d'UN CRAN de l'échelle du site, du --fs-h1 de la
   composition « duo » au --fs-h2 commun : 24 px au lieu de 30 sur un écran de
   375. C'est la seule réduction admissible sous la règle 3 de MAQUETTE.md —
   un `font-size` inventé pour l'occasion donnerait un titre qui ne ressemble
   à rien d'autre sur le site.

   Borné à `xs` et non à `lg` : à 700 px la colonne fait encore 546 px, le
   --fs-h1 y tient sans peine. Le resserrage est un fait de téléphone. */
@media (max-width: 30rem) {
  .panneau--duo .panneau__titre {
    font-size: var(--fs-h2);
  }
  /* ⚠️ `.btn` est en `white-space: nowrap` par construction, et « En savoir
     plus sur la fiabilisation » demande ~267 px de large. La colonne tombant
     à 273 px sur un écran de 375 — et à 218 sur un 320 —, le bouton débordait
     et la boîte, qui est en `overflow: hidden`, le rognait. Défaut préexistant
     sur les petits écrans, que le rembourrage relevé rendait visible plus tôt.

     Même traitement que les boutons de la colonne bornée de `inscription` :
     on rend la césure au libellé et on resserre le rembourrage, plutôt que de
     raccourcir un texte que la maquette donne. */
  .panneau__cta .btn {
    width: 100%;
    padding-inline: var(--sp-4);
    white-space: normal;
  }
}
/* Sélecteur `ul.` et non `.` seul : le reset de _base.scss cible `ul[class]`,
   qui l'emporterait sur une simple classe. */
ul.panneau__liste {
  margin-top: var(--sp-4);
  padding-left: var(--sp-5);
  list-style: disc;
}

ul.panneau__liste li::marker {
  color: var(--c-fluo);
}

ul.panneau__liste li + li {
  margin-top: var(--sp-3);
}

/* =========================================================================
   BLOC « texte-illustre » — blocs/texte-illustre.njk.
   Origine : « Une approche data unique », Figma 54:2323.

   Quatre colonnes de grille, dont deux vides :

     1  la marge gauche du gabarit — vide, mais le décor la recouvre
     2  la voie du visuel
     3  la colonne de texte, encadrée de filets fluo
     4  une marge de crème, dont la largeur cale le filet droit sur la
        marge de --container-max : le filet tombe exactement là où
        commence le contenu du reste du site.

   La maquette laissait ce filet flotter à 219 px du bord, sans rapport
   avec la grille de la page. Choix tranché le 12/08/2026 : on l'aligne.

   ⚠️ Les colonnes 1 et 2 n'en faisaient qu'une jusqu'au 14/08/2026, et le
   visuel s'y calait par une marge de 31 %. Il en résultait un visuel ni
   centré ni aligné : à 1904 px il commençait à 293 px du bord de page alors
   que la marge du gabarit tombe à 312 px — il sortait donc du gabarit par la
   gauche. En scindant la bande, le visuel a sa propre piste, qui va du bord
   du GABARIT au filet de la boîte, et il n'a plus qu'à s'y centrer.

   Le décor, lui, enjambe les deux pistes : la trame doit continuer de courir
   jusqu'au bord de la page.

   ⚠️ Les quatre formes flottantes du plan de décor ont été RETIRÉES le
   14/08/2026, en même temps que le cadre gris cédait la place à la
   composition des trois formes. Le décor ne porte donc plus que sa trame.

   Aucune hauteur n'est fixée. Ce sont les paddings de la colonne de texte
   qui donnent à la section ses 957 px de maquette, et le visuel se centre
   sur ce qu'elle mesure.
   ========================================================================= */
.texte-illustre {
  /* Largeur exacte de la marge de contenu de `.container` (_base.scss) :
     à 1904 px de large, 312 px — le filet droit y tombe au pixel. En
     dessous de 1280 px la soustraction devient négative et c'est le
     padding de conteneur qui reprend la main. */
  --texte-illustre-marge: max(var(--container-pad), (100% - var(--container-max)) / 2);
  /* 649 px dans la maquette, ramenés à 608. Le plancher évite que la colonne
     devienne illisible avant le passage en une seule colonne.

     ⚠️ Ce nombre EST le rapport entre la boîte et l'illustration : tout ce
     que la colonne de texte ne prend pas revient à la voie du visuel. Il a
     été resserré le 14/08/2026 parce que l'illustration paraissait petite —
     et c'est ici, plus que dans sa propre largeur, que se règle son échelle.
     À ne pas descendre beaucoup plus bas : sous 1280 px de fenêtre, la
     colonne de texte tombe alors sous 300 px de mesure utile. */
  --texte-illustre-col: clamp(21rem, 32vw, 38rem);
  /* Marge crème autour de la boîte de texte : c'est elle qui la détache du
     bord de la section et fait lire les quatre filets comme un cadre. Les
     cotes intérieures de la maquette (155/44/136/77) sont conservées
     telles quelles — la boîte est simplement posée plus bas. */
  --texte-illustre-marge-boite: clamp(var(--sp-8), 3.4vw, 4rem);
  /* Paddings du CORPS de la colonne de texte. Bornes hautes = les cotes de la
     maquette (136, 77) ; la pente est calée pour qu'elles soient atteintes
     vers 1900 px et se resserrent proprement en dessous.

     ⚠️ Ceux de la bande de titre ne sont plus ici : elle prend les jetons
     `--pad-panneau-entete-*`, partagés avec « Au cœur de Biron ». Elle avait
     155 px au-dessus du titre, contre 56 pour le cadre voisin — l'écart se
     voyait d'une section à l'autre. Demandé le 14/08/2026.

     ⚠️ Le rembourrage vertical du corps est descendu de 136 à 84 px le même
     jour : 136 px entre le filet et la première ligne creusaient un trou en
     haut de la colonne. La cote de la maquette n'est donc plus tenue ici. */
  --texte-illustre-pad-corps: clamp(var(--sp-8), 4.6vw, 5.25rem);
  --texte-illustre-pad-corps-inline: clamp(var(--sp-6), 4.2vw, 4.8125rem);
  position: relative;
  display: grid;
  /* ⚠️ `--texte-illustre-marge` contient un `100%`, qui se résout contre le
     conteneur de grille. Il n'est donc utilisable QU'ICI, sur la section : le
     même jeton posé en marge d'un enfant se résoudrait contre la cellule de
     cet enfant et donnerait une tout autre valeur. C'est la raison pour
     laquelle la marge est une PISTE DE GRILLE et non un rembourrage. */
  grid-template-columns: var(--texte-illustre-marge) 1fr var(--texte-illustre-col) var(--texte-illustre-marge);
  background-color: var(--bg-page);
  /* Les formes débordent largement de la bande : la section les rogne à
     ses limites. Sans ça, une forme fluo empiéterait sur la séquence
     sombre voisine. */
  overflow: hidden;
}

/* ---------- Plan de décor ---------- */
/* Le décor enjambe la marge du gabarit ET la voie du visuel : il occupe donc
   exactement la bande d'avant. Les positions des formes, qui sont en % de ce
   conteneur, n'ont pas bougé d'un pixel. */
.texte-illustre__decor {
  grid-column: 1/3;
  grid-row: 1;
  position: relative;
  z-index: 0;
  pointer-events: none;
}

/* La trame occupe toute la bande, bord de page compris. Elle n'a aucun bord
   à cadrer : ce sont ses dégradés qui la font naître du fond.

   Deux masques composés en intersection, un par axe — l'opacité finale est
   le produit des deux :

     axe X  extinction vers la gauche sur le tiers, pleine densité à droite
            contre le filet de la boîte ;
     axe Y  amorce courte en haut et en bas.

   Le fondu vertical n'est pas cosmétique. Sans lui, la première ligne
   horizontale du motif tombe pile sur le bord haut de la section et s'y lit
   comme un liseré — mesuré à #eafc97 sur 2 px, soit 35 niveaux plus sombre
   que le crème. Décaler le motif de 2 px l'aurait chassée du haut mais
   l'aurait fait réapparaître en bas dès que la hauteur du contenu change ;
   un fondu est insensible à la hauteur.

   `mask-composite` n'est pas doublé en -webkit- : les mots-clés des deux
   versions diffèrent et les faire cohabiter casse la composition. Tout
   navigateur qui comprend `mask-composite` comprend `mask-image` sans
   préfixe. Reste donc écrit à la main — voir aussi _components.scss
   (.forme-trame__grain) pour le même cas. */
.texte-illustre__grille {
  --grid-fade-x: 33%;
  --grid-fade-y: 7%;
  position: absolute;
  inset: 0;
  mask-image: linear-gradient(to right, transparent, black var(--grid-fade-x)), linear-gradient(to bottom, transparent, black var(--grid-fade-y), black calc(100% - var(--grid-fade-y)), transparent);
  mask-composite: intersect;
}

/* ---------- Visuel ---------- */
/* Centré dans sa voie, sur les deux axes. Plus de marge de calage : la piste
   commence là où commence le gabarit et finit sur le filet de la boîte, donc
   « au milieu » se dit simplement.

   Le plafond de 36 rem (576 px) est ce qui commande la taille sur grand
   écran : la voie est toujours plus large que ça, donc c'est lui qui gagne.
   Il est légèrement au-dessus des 528 px de la maquette, ce que l'on peut se
   permettre parce que l'illustration est vectorielle — un agrandissement ne
   lui coûte rien. Le pourcentage ne reprend la main qu'en dessous de 1100 px
   de fenêtre environ. */
.texte-illustre__visuel {
  grid-column: 2;
  grid-row: 1;
  z-index: 1;
  align-self: center;
  justify-self: center;
  width: min(88%, 36rem);
}

/* Gabarit dans `.apercu` (_components.scss). Ne restent ici que le ratio de
   la maquette (523 × 598, portrait) — la page ne bougera pas d'un pixel quand
   la vraie capture arrivera — et l'ombre.

   ⚠️ Ce cadre gris ne sert plus « Une approche data unique », qui porte
   désormais la composition des trois formes. Il reste pour tout autre usage
   du bloc, quand `visuel.illustration` n'est pas renseigné. */
.texte-illustre__placeholder {
  aspect-ratio: 523/598;
  box-shadow: var(--shadow-lg);
}

/* ---------- Colonne de texte ---------- */
.texte-illustre__texte {
  grid-column: 3;
  grid-row: 1;
  z-index: 2;
  display: flex;
  flex-direction: column;
  /* Cadre fermé sur les quatre côtés, détaché des bords de la section par
     sa marge. La maquette ne posait que les deux filets verticaux, sur
     toute la hauteur ; refermer la boîte était une demande explicite. */
  /* Filet bleu, comme le cadre de « Au cœur de Biron ». Le jeton est partagé :
     les deux doivent rester identiques. */
  border: var(--filet-panneau);
  /* Coins vifs, comme les panneaux de « Au cœur de Biron » et de la FAQ
     depuis le 14/08/2026. Le jeton `--radius-cadre` n'est pas remis à zéro
     pour autant : il habille aussi le cadre du visuel juste à côté, qui garde
     les siens. Ce sont les PANNEAUX qui se carrent, pas tout. */
  border-radius: 0;
  margin-block: var(--texte-illustre-marge-boite);
  /* Blanc, et non plus le crème de la section. Il faut de toute façon un fond
     OPAQUE ici : c'est lui qui coupe les formes net sur le filet gauche,
     comme dans la maquette où le losange de droite disparaît exactement sur
     le filet. Sans fond, il continuerait derrière le texte. Le blanc y ajoute
     le détachement du cadre sur le crème de la page. */
  background-color: var(--c-white);
}

/* ⚠️ La bande de titre prend les marges du cadre de « Au cœur de Biron », au
   jeton près. Le rembourrage latéral (58 px) reste plus court que celui du
   corps juste dessous (77 px) : les deux bords gauches ne s'alignent donc pas
   tout à fait. C'est déjà ce que faisait la maquette, en plus marqué — elle
   donnait 44 contre 77. À aligner si l'écart gêne. */
.texte-illustre__entete {
  padding: var(--pad-panneau-entete-haut) var(--pad-panneau-entete-inline) var(--pad-panneau-entete-bas);
}

.texte-illustre__titre {
  margin: 0;
}

/* Le filet horizontal de la maquette : il court sur toute la largeur
   intérieure de la colonne. Bleu comme le cadre qui l'entoure — il en est un
   séparateur interne, pas un accent : c'est le même jeton. */
.texte-illustre__corps {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-10);
  padding: var(--texte-illustre-pad-corps) var(--texte-illustre-pad-corps-inline);
  border-top: var(--filet-panneau);
}

.texte-illustre__groupes {
  max-width: 40rem;
  font-size: var(--fs-base);
  line-height: var(--lh-normal);
  color: var(--text-strong);
}

/* Un groupe = des lignes accolées ; l'écart entre groupes vaut exactement une
   ligne vide, comme dans la maquette. D'où le `em` : il suit le corps du
   texte, pas une échelle d'espacement.

   Ces deux classes se sont appelées `__blocs` / `__bloc` jusqu'au 13/08/2026.
   Renommées en même temps que le champ YAML : sur une page, « blocs » désigne
   la liste des blocs de la page. */
.texte-illustre__groupe + .texte-illustre__groupe {
  margin-block-start: 1.6em;
}

.texte-illustre__groupe p {
  margin: 0;
}

/* ---------- Groupe rendu en liste ----------
   Un groupe de plusieurs lignes est une liste d'affirmations parallèles, et
   c'est ainsi qu'il se rend depuis le 14/08/2026.

   La grille, plutôt qu'un `list-style`, pour une raison précise : les lignes
   repliées s'alignent alors sous le TEXTE et non sous le tiret. C'est tout
   l'intérêt de l'opération — sans ça, une affirmation longue reproduit
   exactement le défaut qu'on corrige.

   Le tiret passe par `::before` et non par `::marker` : `content` sur
   `::marker` n'est arrivé qu'avec Safari 17, et le repli y aurait été une
   puce ronde. */
.texte-illustre__groupe--liste {
  margin: 0;
  padding: 0;
  list-style: none;
}

.texte-illustre__groupe--liste li {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 0.55em;
}

/* Une puce ronde, dessinée plutôt qu'écrite : un caractère « • » dépend du
   dessin de la police et de son alignement vertical, alors qu'une boîte ronde
   se cale exactement où on veut. Le `0.62em` de décalage la pose sur l'œil
   moyen de la ligne plutôt que sur sa base. */
.texte-illustre__groupe--liste li::before {
  content: "";
  width: 0.4375em;
  aspect-ratio: 1;
  margin-block-start: 0.62em;
  border-radius: 50%;
  background-color: var(--c-blue);
}

/* Une demi-ligne entre les items : assez pour que les trois se détachent,
   pas assez pour qu'ils se lisent comme trois groupes distincts — l'écart
   entre groupes vaut 1,6 em. */
.texte-illustre__groupe--liste li + li {
  margin-block-start: 0.55em;
}

/* =========================================================================
   Une seule colonne.

   La maquette ne couvre pas le mobile. Le décor reste derrière le visuel
   plutôt que de disparaître, et la boîte garde ses quatre filets.

   Le décor court d'un bord à l'autre de l'écran : la section ne porte donc
   PAS de `padding-inline` ici, ce sont la boîte et le visuel qui prennent
   chacun leur gouttière. Sans ça, la trame s'arrêtait 24 px avant le bord
   sur une arête franche, et les formes n'avaient pas de quoi déborder.

   Les paddings intérieurs de la boîte n'ont pas besoin d'être redéfinis :
   leurs clamps descendent déjà à 24 px de côté à cette largeur.
   ========================================================================= */
@media (max-width: 60rem) {
  .texte-illustre {
    grid-template-columns: 1fr;
    grid-template-rows: auto auto;
  }
  /* Décor et visuel se remettent dans la même cellule, empilés. */
  .texte-illustre__decor,
  .texte-illustre__visuel {
    grid-column: 1;
    grid-row: 1;
  }
  /* Pleine densité d'un bord à l'autre. Le fondu horizontal n'avait de sens
     que pour dissoudre une arête intérieure — le filet gauche de la boîte,
     qui n'est plus à côté mais au-dessous. Au bord de l'écran, la coupe est
     naturelle et n'a pas besoin d'être adoucie. Le fondu vertical, lui,
     reste : c'est lui qui évite le liseré sur les bords haut et bas. */
  .texte-illustre__grille {
    --grid-fade-x: 0%;
  }
  .texte-illustre__texte {
    grid-column: 1;
    grid-row: 2;
    margin: var(--sp-10) var(--container-pad) var(--sp-12);
  }
  /* Le visuel reprend sa place normale dans le flux : plus de décalage vers
     la droite, il se centre. La gouttière est portée ici puisque la section
     n'en a plus. */
  .texte-illustre__visuel {
    width: min(100% - var(--container-pad) * 2, 26rem);
    margin-inline: auto;
    padding-block: var(--sp-12) 0;
  }
  /* Le bouton s'aligne sur le texte plutôt que de se centrer : centré dans
     une colonne étroite, il flotte. */
  .texte-illustre__corps {
    align-items: flex-start;
  }
}
/* =========================================================================
   BLOC « texte-halftone » — blocs/texte-halftone.njk.
   Origine : « Pensé pour les entreprises qui pilotent par la donnée »,
   Figma 185:7468.

   Sur l'accueil, ce bloc s'insère entre « texte-illustre » et « Au cœur de
   Biron » — voir l'ordre des `@use "sections/..."` dans src/scss/main.scss,
   qui porte désormais la cascade (plus de préfixe numérique sur le nom de
   fichier, voir CLAUDE.md « CSS build »).

   L'ordre choisi n'engage QUE l'accueil : les classes sont préfixées, donc
   l'ordre de la cascade n'a aucune conséquence sur une page qui range ses
   blocs autrement.

   Deux écarts assumés avec la maquette — colonnes inversées, texte hors cadre.
   Le pourquoi est en tête de blocs/texte-halftone.njk.
   ========================================================================= */
.texte-halftone {
  --texte-halftone-gouttiere: clamp(var(--sp-8), 5vw, 6rem);
  position: relative;
  background-color: var(--bg-page);
  /* Le haut ET le bas passent chacun par une variable dont LA VALEUR PAR
     DÉFAUT EST DANS LE `var()`, jamais dans une déclaration : c'est ce qui
     permet à un voisin de la reprendre sans que la spécificité s'en mêle
     (voir l'accolement à `.intro` et à `.quotidien` plus bas). */
  padding-block: var(--texte-halftone-haut, clamp(var(--sp-16), 10vw, 11rem)) var(--texte-halftone-bas, clamp(var(--sp-16), 10vw, 11rem));
  /* L'anneau déborde à droite : on rogne ici, la section n'a rien de collant. */
  overflow: clip;
}

/* ⚠️ RESSERRAGE DU RACCORD AVEC LE BANDEAU CLIENTS — accueil uniquement,
   demandé par Hugo le 16/09/2026 (« réduire l'espace entre la partie clients
   et la partie Vous n'avez pas besoin de plus de données »).

   Les deux vides s'ADDITIONNAIENT sans rien entre eux : les 114 px de bas de
   `.intro` (à 1440) plus les 144 px de haut d'ici, soit 258 px de crème nue.
   C'est un reliquat de composition — jusqu'au 350c10f, `.intro` était suivie
   de la section `sequence`, SOMBRE : sa respiration basse laissait la trame
   s'éteindre avant l'arête. Ce voisin est parti, la respiration est restée, et
   elle tombe désormais contre le vide d'une section de même fond.

   Ce qui est rogné est donc le vide PUR, pas le décor : la queue de trame et
   les formes fluo vivent dans le bas de `.intro`, qui ne bouge pas.

   Scopé à l'accolement, donc /exemple-blocs/ et toute autre page gardent la
   respiration pleine du bloc. Et c'est une PROPRIÉTÉ PERSONNALISÉE, pas une
   surcharge de `padding-block` : la règle étroite plus bas réécrit le
   raccourci sans lire la variable, donc le rendu mobile ne bouge pas d'un
   pixel — là où un sélecteur `.intro + .texte-halftone` (0,2,0) l'aurait
   emporté sur elle à toutes les largeurs. */
.intro + .texte-halftone {
  --texte-halftone-haut: clamp(var(--sp-8), 3vw, var(--sp-12));
}

/* ⚠️ RESSERRAGE DU RACCORD AVEC « VOTRE QUOTIDIEN » — accueil uniquement,
   demandé par Céline le 16/09/2026 : le bas de `.texte-halftone` (jusqu'à
   11rem) s'ajoutait au haut de `.quotidien` (jusqu'à 2rem seulement, déjà
   resserré pour son quinconce) et laissait un vide disproportionné entre les
   deux sections. `:has()` cible le voisin PRÉCÉDENT, ce que `+` ne sait pas
   faire dans l'autre sens. */
.texte-halftone:has(+ .quotidien) {
  --texte-halftone-bas: clamp(var(--sp-6), 3vw, var(--sp-10));
}

.texte-halftone__inner {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
  gap: var(--texte-halftone-gouttiere);
  align-items: center;
}

/* ---------- Colonne de texte ----------
   Hors cadre, et c'est tout l'intérêt : la mesure est bornée par le texte
   lui-même et non par une boîte. */
.texte-halftone__texte {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: clamp(var(--sp-6), 2.4vw, var(--sp-10));
}

/* Corps par défaut du h2, soit --fs-h2 : 48 px en grand écran, comme « Au cœur
   de Biron », « Les 4 garanties Biron » ou « Une approche data unique ».

   La maquette le donne à 34 px, et il ne faut pas la suivre ici : elle enferme
   ce titre dans une boîte de 647 px, où 48 px aurait débordé. Le texte étant
   sorti de sa boîte, la contrainte a disparu — et un titre de section à 34 px
   au milieu de titres à 48 se lisait comme un sous-titre.

   Seule la mesure est bornée, pour que le titre se replie en trois lignes
   pleines plutôt qu'en une ligne longue suivie d'un mot seul. */
.texte-halftone__titre {
  margin: 0;
  max-width: 24ch;
  color: var(--c-blue-zodiac);
}

.texte-halftone__corps {
  display: flex;
  flex-direction: column;
  gap: var(--sp-5);
  /* 640 px dans la maquette, mais elle enferme le texte dans une boîte de
     647 px. À l'air libre, la mesure doit être bornée pour rester lisible :
     34em, la même que les autres textes courants du site. */
  max-width: 34em;
  font-size: var(--fs-base);
  line-height: var(--lh-normal);
  color: var(--text-strong);
}

/* ---------- Le graphique ---------- */
.texte-halftone__visuel {
  position: relative;
  /* Carré : c'est le disque qui commande. Il déborde largement à droite, comme
     la maquette le fait à gauche. */
  aspect-ratio: 1;
  width: 128%;
}

.texte-halftone__anneau {
  position: absolute;
  inset: 0;
}

/* ---------- L'anneau ----------
   Il est DESSINÉ, dans un canvas, par src/assets/js/cible.js (le script garde
   son nom d'origine : il cible l'attribut `data-cible-anneau`). Le pourquoi est
   en tête de ce fichier-là : garder des points entiers à la frontière demande
   de décider point par point, ce qu'un masque CSS — qui décide pixel par
   pixel — ne sait pas faire. Il ne reste donc ici que le cadrage.

   Les réglages d'allure (pas de la grille, bords, bruit, encre) sont posés en
   attributs `data-` sur le canvas, dans blocs/texte-halftone.njk. */
.texte-halftone__anneau {
  display: block;
  width: 100%;
  height: 100%;
}

/* ---------- Les deux cartes ----------
   Posées sur l'anneau, dans son trou et sur son flanc gauche — là où elles ne
   couvrent pas la trame la plus dense. En pourcentages du visuel : elles
   restent solidaires du disque à toutes les largeurs. */
/* ⚠️ L'APPARENCE DE LA CARTE N'EST PLUS ICI depuis le 18/08/2026 : fond, filet,
   rayon, intitulé et pilules sont remontés dans `.carte-etiquettes`
   (_components.scss), parce que la page dictionnaire emploie la même carte. Les
   recopier aurait donné deux jeux de valeurs à maintenir.

   Ce fichier ne garde que ce qui appartient à CET emploi : le positionnement
   absolu sur l'anneau et l'ombre portée. Ne pas y ramener de couleur ni de
   typographie — elles se désaccorderaient de l'autre bloc en silence. */
.texte-halftone__carte {
  position: absolute;
  box-shadow: 0 4px 40px 4px rgb(var(--shadow-tint)/0.38);
}

/* ⚠️ REPOSITIONNÉES LE 09/09/2026, quand « Pensé pour les entreprises… » est
   devenue « La problématique » (voir _data/cible.yaml) : les deux nouvelles
   cartes portaient chacune 3 lignes de pilules contre 1 avant, et aux cotes
   d'origine le bord de la carte claire tombait pile sur le titre de la
   sombre. Écartées pour que seul un coin se touche — jamais une ligne de
   texte.

   ⚠️ PUIS ÉCARTÉES À NOUVEAU LE 16/09/2026, quand les cartes sont devenues des
   PILES à sortie : de 3 valeurs sur une colonne elles sont passées à 12 en pile,
   plus un entonnoir et une pilule de sortie à droite — une carte de 426 × 236 au
   lieu de 267 × 184. Mesuré à 1440 px, en pourcentages du visuel (666 px de
   côté) : 64 % de large, 36 % de haut. La claire va donc de 6 à 42 % en
   ordonnée, la sombre de 47 à 83 % — cinq points les séparent.

   ⚠️ LES DEUX CARTES N'ONT PAS TOUT À FAIT LA MÊME LARGEUR — 426 et 402 — et
   c'est normal : tout leur est commun sauf la pilule de sortie, qui se
   dimensionne sur son propre libellé. C'est la plus large qui commande les cotes
   ci-dessous.

   ⚠️ CES 426 PX SONT LE RÉSULTAT D'UN RÉGIME, pas d'un premier jet — Hugo, le
   même jour : « profite de ça pour un peu réduire la largeur des blocs en
   général ». Ils étaient à 476, et c'est d'abord LA LARGEUR ÉCRITE DU TAS qui
   les a ramenés : la piste valait 17rem de plafond pour 143 px d'étiquettes, donc
   quatre-vingt-huit pixels de vide (voir `.carte-etiquettes__liste`). Le reste
   est en pincées — les décalages du désordre resserrés, les cinq libellés les
   plus longs raccourcis à 18 caractères, le rembourrage de la pilule de sortie
   réduit. AUCUNE ne touche à ce qui fait la section : ni le nombre de valeurs,
   ni le recouvrement, ni la taille de la sortie.

   ⚠️ CE DÉGAGEMENT EST CALCULÉ, PAS CONSTATÉ, et rien ne le vérifie. Les deux
   cartes sont posées en absolu : elles ne se poussent pas, elles se recouvrent.
   Quatre choses le referment en silence — desserrer `--pile-recouvrement`,
   ajouter des valeurs dans _data/cible.yaml, allonger un libellé au point que la
   pile prenne deux étiquettes par rang, ou grossir `--entonnoir-h`.

   ⚠️ ET C'EST 1280 PX QUI COMMANDE L'ABSCISSE, PAS 1440. Le conteneur est à sa
   largeur maximale dès 1280 px, mais la fenêtre, elle, est plus étroite de
   160 px : le visuel déborde alors du côté droit et la section le rogne. La
   marge disponible à droite y tombe à 620 px pour une carte de 426, ce qui
   plafonne le décalage de la claire à 29 % — d'où ces 26 %. Vérifier une cote
   d'abscisse à 1280 px, jamais seulement au large. */
.texte-halftone__carte--clair {
  top: 6%;
  left: 26%;
}

.texte-halftone__carte--sombre {
  top: 47%;
  left: 0%;
}

/* ---------- LA PILE DE PILULES, ET SA SORTIE ----------
   ⚠️ DEMANDÉE PAR HUGO LE 16/09/2026 : « on multiplie le nombre de pilules,
   superposées, qu'on ne puisse pas toutes les lire, pour que ça fasse vraiment
   un côté overwhelming et pas cool ». Les deux cartes portaient trois valeurs
   bien rangées, c'est-à-dire l'exact contraire de ce que la section raconte —
   des sources dispersées et des chiffres contradictoires.

   ⚠️ PUIS REPRISE LE MÊME JOUR, sur deux points : « je veux pas d'effet de
   dégradé en bas, je veux que tout tienne dans la boîte », et une SORTIE à
   droite — une flèche et une dernière pilule qui répond à la pile.

   ⚠️ CE QUI A ÉTÉ RETIRÉ, ET POURQUOI NE PAS LE RÉTABLIR. La pile était d'abord
   écrêtée à une hauteur fixe et estompée par un masque : les douze valeurs y
   étaient, mais les dernières se dissolvaient. Ce qu'on y perdait, c'est que
   l'illisibilité devenait un EFFET posé par-dessus, au lieu de venir de
   l'encombrement lui-même. Elle ne vient plus que du recouvrement, et les douze
   pilules sont entières dans la boîte.

   ⚠️ C'EST DONC LE RECOUVREMENT QUI FIXE LA HAUTEUR DES CARTES, et non plus un
   plafond. Les deux sont posées en absolu sur l'anneau, elles ne peuvent pas se
   pousser l'une l'autre : desserrer `--pile-recouvrement` les fait grandir l'une
   DANS l'autre, sans que rien ne le signale. Les cotes mesurées sont au-dessus
   de leurs positions, plus bas dans ce fichier.

   ⚠️ TOUT CELA EST PROPRE À CET EMPLOI, et doit le rester. `.carte-etiquettes`
   est un atome partagé (page dictionnaire, /cas-clients/hardloop/) où la carte
   est une liste d'étiquettes qu'on doit pouvoir LIRE. Descendre le recouvrement
   dans _components.scss rendrait ces deux-là illisibles sans que rien ne le
   signale. L'atome ne porte que l'APPARENCE de la sortie ; sa mise en place à
   droite de la pile est ici.

   Trois pièces, et chacune répond à un mot de la demande :

     SUPERPOSÉES — des marges négatives, jamais du positionnement absolu. En
     flux, chaque pilule pousse encore la suivante, donc la pile fonctionne
     quel que soit le nombre de valeurs et sans qu'aucun compte ne soit publié
     nulle part. Les pilules prennent un FOND OPAQUE ici (elles sont
     contournées et transparentes dans l'atome) : c'est lui qui fait que celle
     du dessus MASQUE celle du dessous. Sans fond, les contours se croisent et
     donnent un enchevêtrement de filets, pas une pile. Le fond est celui de la
     carte pour la sombre, du blanc pour la claire — la pilule se détache alors
     du crème sans introduire de troisième couleur.

     ⚠️ L'ORDRE DE PEINTURE EST CELUI DU DOM, sans le moindre `z-index` : un
     élément flex se peint de façon atomique, donc la dernière pilule passe
     par-dessus la précédente. C'est ce qui donne le sens de la pile, du haut
     vers le bas — et c'est aussi ce qui laisse la DERNIÈRE entièrement lisible,
     juste en face de la sortie.

     QU'ON NE PUISSE PAS TOUTES LES LIRE — le recouvrement ne laisse voir qu'un
     peu plus de la moitié de chaque pilule. En dessous d'un tiers la pile
     devient une trame de filets où plus rien ne se lit ; au-dessus des trois
     quarts on retombe sur une liste aérée.

     PAS COOL — un désordre. Rotations et décalages alternés par `nth-child`,
     sur des pas premiers entre eux (3, 4, 5, 7) pour que la période ne se voie
     pas : à pas égaux, une pile régulière redevient une frise, ce que ce fichier
     évite déjà ailleurs.

     ⚠️ C'EST `rotate:` ET NON `transform:`, la règle du site — reveal.js pose
     un `transform: none` à (0,3,1) sur tout élément révélé, qui emporterait la
     rotation en silence. Les deux propriétés se composent au lieu de se
     remplacer. */
.texte-halftone__carte {
  /* Ce qui reste visible de chaque pilule = sa hauteur moins ce recouvrement.
     Une seule cote pour les deux cartes : elles doivent peser pareil.

     ⚠️ DESSERRÉ LE 16/09/2026 (Céline : « le texte est trop caché ») — de
     0,8rem à 0,4rem. Toujours dans la fourchette lisible du gabarit (« en
     dessous d'un tiers la pile devient une trame de filets ») : à six
     étiquettes par carte (contre douze à l'origine), la carte a de la marge
     pour desserrer sans grandir démesurément. La bascule mobile garde sa
     propre valeur (1,25rem), tenue pour une autre raison — la hauteur de la
     carte empilée, voir plus bas. */
  --pile-recouvrement: 0.4rem;
  /* LA CARTE PASSE EN GRILLE : l'intitulé sur toute la largeur, la pile à
     gauche, la sortie à droite, l'une centrée sur l'autre.

     ⚠️ C'EST UN PLACEMENT PAR LIGNES ET COLONNES, jamais par
     `grid-template-areas` : l'atome rend l'intitulé et la sortie de façon
     FACULTATIVE, et une zone nommée qui ne reçoit rien creuse quand même sa
     piste. Placé ainsi, un enfant absent ne laisse rien derrière lui.

     ⚠️ CETTE RÈGLE PÈSE LE MÊME POIDS QUE `.carte-etiquettes` (0,1,0) et ne
     l'emporte que par l'ordre des feuilles — _components.scss est chargée avant
     les sections dans main.scss. Ne pas y remonter ce fichier. */
  display: grid;
  grid-template-columns: max-content max-content;
  align-items: center;
  /* ⚠️ PLUS D'AIR SUR TROIS CÔTÉS, demandé par Hugo le 16/09/2026 (« un peu plus
     de marge à gauche, à droite et en bas sur les deux blocs »). L'atome donne
     un rembourrage égal sur les quatre, calibré pour une carte de trois
     étiquettes ; à douze en pile plus un entonnoir et une sortie, la carte a
     grossi et ce rembourrage ne pesait plus rien en regard.

     ⚠️ LE HAUT NE BOUGE PAS, et c'est délibéré : c'est le seul côté qui n'a pas
     un objet au ras du bord mais un intitulé, dont l'interligne fait déjà une
     partie du travail. L'augmenter décollerait le titre de sa carte.

     ⚠️ ET C'EST DE LA MARGE INTÉRIEURE, pas de la marge : les deux cartes sont
     posées en absolu sur l'anneau, une marge extérieure ne les décollerait de
     rien — elle décalerait seulement leur bord par rapport à leurs cotes de
     position, sans que rien ne le signale. */
  padding: clamp(var(--sp-3), 1.2vw, var(--sp-4)) var(--sp-6) var(--sp-6);
  /* ⚠️ GOUTTIÈRE NULLE, et c'est une pièce de l'entonnoir, pas une négligence :
     il entre dans la pile par une marge négative, et une gouttière entre les
     deux colonnes ne ferait que décaler d'autant le point où naissent ses
     courbes. Une seule cote commande ce point, `--entonnoir-sous`. Le titre,
     lui, enjambe les deux colonnes et ne voit pas cette gouttière. */
  column-gap: 0;
  /* ⚠️ INDISPENSABLE, et c'est la pièce qui fait passer l'entonnoir SOUS la pile.
     Il porte un `z-index: -1` ; sans contexte d'empilement ici, ce -1 remonterait
     jusqu'au contexte racine et le dessin filerait sous le fond opaque de la
     section — c'est-à-dire invisible, sans que rien ne le signale. Avec le
     contexte, il se peint juste après le fond de la carte et juste avant les
     pilules, qui sont opaques et le masquent donc sur toute leur surface. */
  isolation: isolate;
}

.texte-halftone__carte .carte-etiquettes__titre {
  grid-column: 1/-1;
  /* La grille n'a pas de `row-gap` : l'écart sous l'intitulé est porté ici, pour
     que la pile et la sortie restent, elles, centrées l'une sur l'autre. */
  margin-block-end: var(--sp-3);
}

/* ---------- L'ENTONNOIR, ET LA PILULE SUR DEUX ÉTAGES ----------
   Demandés par Hugo le 16/09/2026 : « l'origine de la flèche doit être sous les
   étiquettes de gauche » et « la pilule résultante peut être plus grosse : sur
   deux étages pour mieux optimiser la place du bloc ».

   ⚠️ SOUS LA PILE VEUT DIRE DERRIÈRE, ET LE MOT EST CHOISI. L'entonnoir démarre
   à l'intérieur de la pile, en marge négative ; ses quatre courbes sont donc
   masquées par les pilules, qui sont opaques, et n'apparaissent qu'en sortant
   d'elles. C'est ce qui donne l'impression qu'elles naissent des étiquettes.
   Deux pièces le rendent possible — la marge négative ici, et le contexte
   d'empilement posé sur la carte plus haut.

   ⚠️ LE RETRAIT SE MESURE DEPUIS LE BORD DROIT DU TAS, QUI DOIT DONC AVOIR UNE
   LARGEUR ÉCRITE. C'est ce qui a manqué aux deux premiers réglages — voir la
   règle de `.carte-etiquettes__liste` plus bas, qui explique pourquoi un
   `max-width` sur un conteneur flex repliable ne dimensionne pas ce qu'on croit.

   ⚠️ ET IL SE VÉRIFIE SUR LA PILULE LA PLUS COURTE, jamais sur la plus longue.
   Les douze n'ont pas la même largeur, et celles du haut et du bas du tas — là
   précisément où l'entonnoir s'ouvre — sont quelconques : une origine posée en
   deçà de la seule plus longue naîtrait dans le vide à côté des courtes. La plus
   courte des vingt-quatre s'arrête à 104 px, l'origine est à 95.
   ⚠️ RACCOURCIR UNE ÉTIQUETTE DE _data/cible.yaml SOUS CETTE COTE ROUVRE
   L'ÉCART, sans que rien ne le signale.

   ⚠️ CE QUE LE RETRAIT COÛTE, C'EST L'OUVERTURE. Les courbes convergent à 87 %
   de la largeur du dessin : entré de 42 % — la valeur d'ici — l'entonnoir montre
   encore la moitié de son ouverture ; entré de 60 %, il ne montre plus qu'un
   fuseau déjà refermé. Les deux cotes se règlent donc ensemble : enfoncer
   l'origine demande de grandir `--entonnoir-h`, ce qui élargit la carte. */
.texte-halftone__carte .carte-etiquettes__sortie {
  grid-column: 2;
  /* La pointe touche la pilule : l'axe doit se terminer DANS l'objet qu'il
     désigne, un écart le transformerait en trait qui s'arrête avant. */
  gap: 0;
}

.texte-halftone__carte .carte-etiquettes__entonnoir {
  /* Une seule cote commande le dessin : la largeur en découle par le rapport du
     `viewBox`, écrit ici plutôt que laissé à `inline-size: auto` — dans une
     rangée flex, une largeur intrinsèque de remplacé se négocie, une largeur
     calculée ne se négocie pas. */
  --entonnoir-h: 7.5rem;
  /* 4,5rem = 61 px, retranchés du bord droit d'un tas large de 11,5rem = 156 :
     l'origine tombe à 95 px du bord gauche des étiquettes. La plus courte des
     vingt-quatre s'arrête à 104 px, donc les quatre courbes naissent derrière
     toutes, y compris celles du haut et du bas du tas. */
  --entonnoir-sous: 4.5rem;
  flex: 0 0 auto;
  block-size: var(--entonnoir-h);
  inline-size: calc(var(--entonnoir-h) * 923 / 638);
  margin-inline-start: calc(-1 * var(--entonnoir-sous));
  z-index: -1;
}

/* ⚠️ LA PILULE DE SORTIE PREND UN RAYON FIXE, jamais `--radius-full`. C'est la
   règle posée par Hugo le 21/08/2026 : « il faut pas adapter le radius à la
   taille ». Un rayon plein plafonne à la moitié de la hauteur de la boîte, donc
   sur deux lignes la FORME changerait avec le contenu — une ligne de plus et le
   même objet se lirait comme un autre.

   Les deux étages sortent du `**gras**` déjà écrit dans les données : le
   `<strong>` passe en bloc, l'intitulé occupe la première ligne et la valeur la
   seconde. Rien à ajouter dans _data/cible.yaml, et les deux cartes suivent la
   même grammaire que leurs douze étiquettes. */
.texte-halftone__carte .carte-etiquettes__puce--sortie {
  padding: 0.55rem 0.9rem;
  /* La valeur monte d'un cran au-dessus des étiquettes de la pile (0,8125rem) :
     c'est ce qui la détache, au même titre que son aplat. */
  font-size: var(--fs-base);
  line-height: 1.3;
  text-align: center;
  white-space: normal;
  border-radius: var(--radius-cadre);
}

.texte-halftone__carte .carte-etiquettes__puce--sortie strong {
  display: block;
  font-size: var(--fs-mono);
  font-weight: 500;
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
}

/* ⚠️ LA PILE A UNE LARGEUR DÉFINIE, ET C'EST LA PIÈCE QUI MANQUAIT. Elle portait
   un simple `max-width: 17rem`, et ça ne dimensionne PAS ce qu'on croit : la
   taille max-content d'un conteneur flex qui se replie, c'est la SOMME de ses
   enfants — la largeur à laquelle plus rien ne se replierait, soit ici douze
   pilules bout à bout, près de 1800 px. Écrêtée par le plafond, la piste de
   grille valait donc 17rem tout rond, 231 px, quand les pilules n'en occupaient
   que 143. Quatre-vingt-huit pixels de vide à leur droite, dans lesquels
   l'entonnoir naissait.

   ⚠️ C'EST AUSSI POURQUOI LES DEUX RÉGLAGES PRÉCÉDENTS N'ONT RIEN DONNÉ, et
   Hugo l'a décrit exactement : « les origines des flèches n'ont pas bougé, seules
   les étiquettes se sont déplacées ». L'origine se mesure depuis le bord droit
   de la PISTE ; la piste valait le plafond, une constante. Enfoncer l'entonnoir
   le déplaçait bien, mais de 33 px dans un vide qui en faisait 88 ; et
   raccourcir les libellés ne touchait que les pilules, la piste ne bougeant pas
   d'un pixel. Un plafond n'est pas une largeur.

   « Un tas de largeur contenue » : la pile a donc une largeur ÉCRITE, de laquelle
   `--entonnoir-sous` se déduit. ⚠️ ELLE DOIT RESTER SUPÉRIEURE À LA PLUS LONGUE
   ÉTIQUETTE (147 px aujourd'hui, décalage du désordre compris), sans quoi les
   pilules débordent de leur tas — elles sont en `nowrap`, elles ne se replient
   pas. */
.texte-halftone__carte .carte-etiquettes__liste {
  grid-column: 1;
  inline-size: 11.5rem;
  /* L'écart est porté par les marges des pilules, qui sont négatives. */
  gap: 0;
}

.texte-halftone__carte .carte-etiquettes__liste > .carte-etiquettes__puce {
  /* Recouvrement horizontal : la marge de fin tire la pilule SUIVANTE sur
     celle-ci, elle est donc portée par toutes, la dernière d'un rang comprise
     (où elle ne fait rien de visible). */
  margin-inline-end: -0.7rem;
  /* ⚠️ NI GRANDIR NI RÉTRÉCIR, et le second terme est indispensable depuis que
     la pile a une largeur définie : par défaut un enfant flex est compressible,
     donc une étiquette plus large que le tas verrait sa BOÎTE rétrécie sans que
     son texte suive — il est en `nowrap` — et le libellé sortirait de son propre
     fond. Elle garde ici sa largeur naturelle et déborderait franchement, ce qui
     se voit et se corrige, plutôt que de se déformer en silence. */
  flex: none;
}

/* Recouvrement vertical. Sur la première pilule il remonterait toute la pile
   sous l'intitulé de la carte, d'où le `:not(:first-child)`. */
.texte-halftone__carte .carte-etiquettes__liste > .carte-etiquettes__puce:not(:first-child) {
  margin-block-start: calc(-1 * var(--pile-recouvrement));
}

.texte-halftone__carte--clair .carte-etiquettes__liste > .carte-etiquettes__puce {
  background-color: var(--c-white);
}

.texte-halftone__carte--sombre .carte-etiquettes__liste > .carte-etiquettes__puce {
  background-color: var(--c-blue-dark);
}

/* Le désordre. Les pas se recouvrent volontairement : une pilule peut prendre
   une rotation d'un jeu et un décalage d'un autre.

   ⚠️ LE PLUS GRAND DÉCALAGE ÉLARGIT LA COLONNE D'AUTANT — une marge de début
   compte dans la contribution max-content de la pilule qui la porte. Ils sont
   passés de 1,6 / 1,1 / 0,5rem à 0,9 / 0,6 / 0,3 le 16/09/2026, quand Hugo a
   demandé de réduire la largeur des cartes : douze pixels de gagnés sans rien
   perdre du désordre, les rotations en faisant déjà l'essentiel. */
.texte-halftone__carte .carte-etiquettes__liste > .carte-etiquettes__puce:nth-child(3n) {
  rotate: -1.8deg;
}

.texte-halftone__carte .carte-etiquettes__liste > .carte-etiquettes__puce:nth-child(4n) {
  rotate: 1.4deg;
  margin-inline-start: 0.6rem;
}

.texte-halftone__carte .carte-etiquettes__liste > .carte-etiquettes__puce:nth-child(5n+2) {
  rotate: 2.4deg;
  margin-inline-start: 0.3rem;
}

.texte-halftone__carte .carte-etiquettes__liste > .carte-etiquettes__puce:nth-child(7n+3) {
  rotate: -2.6deg;
  margin-inline-start: 0.9rem;
}

/* =========================================================================
   Une seule colonne.

   Le texte passe au-dessus, le graphique en dessous. Il reste large et déborde
   des deux côtés : rétréci à la largeur de l'écran, l'anneau devient un motif
   timbre-poste et les deux cartes n'ont plus de place où se poser.
   ========================================================================= */
@media (max-width: 60rem) {
  .texte-halftone {
    padding-block: var(--sp-section);
  }
  .texte-halftone__inner {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--sp-12);
  }
  .texte-halftone__visuel {
    width: 118%;
    /* Recentré sur la colonne : il déborde alors autant à gauche qu'à droite. */
    margin-inline: -9%;
  }
  /* ⚠️ LA SORTIE REPASSE SOUS LA PILE, depuis le 16/09/2026. Côte à côte, la
     carte fait 426 px ; le visuel n'en fait que 416 à 390 px de fenêtre — elle le
     couvrirait donc bord à bord —, donc
     une seule carte couvrirait plus que le disque et sortirait de l'écran. En
     colonne elle retombe à 243, la rangée de sortie
     commandant alors la largeur à la place de la pile.

     ⚠️ L'ENTONNOIR NE TOURNE PAS D'UN QUART DE TOUR, et c'est délibéré : une
     rotation ne change pas la boîte de disposition, donc un dessin de 128 × 88
     redressé occuperait 88 de large pour 128 de haut tout en n'en réservant que
     88 — il dépasserait de sa rangée par les deux bouts, sans que rien ne le
     signale. Il reste horizontal, en tête de la rangée de sortie, et rétrécit :
     il ne désigne plus la pilule depuis la pile, seulement depuis sa gauche.

     ⚠️ ET IL NE S'ENFONCE PLUS SOUS LES ÉTIQUETTES : sa rangée est SOUS la pile,
     il n'y a donc plus rien au-dessus de lui pour en masquer l'origine. Un
     retrait ne le glisserait que dans le rembourrage de la carte. */
  .texte-halftone__carte {
    grid-template-columns: max-content;
    /* La pile se resserre : 0,8rem laissait une carte de 273 px de haut, soit
       66 % du visuel — deux ne tenaient pas l'une sous l'autre. À 1,25rem la
       carte fait 227 px, et il reste de quoi les décaler. C'est bien le
       recouvrement qu'on ouvre, jamais le nombre de valeurs : c'est lui le
       sujet de la pile. */
    --pile-recouvrement: 1.25rem;
  }
  .texte-halftone__carte .carte-etiquettes__sortie {
    grid-column: 1;
    margin-block-start: var(--sp-3);
  }
  .texte-halftone__carte .carte-etiquettes__entonnoir {
    --entonnoir-h: 3rem;
    --entonnoir-sous: 0rem;
  }
  /* Les deux cartes se DÉCALENT EN DIAGONALE, et pas seulement vers le centre.
     Côte à côte elles ne tiennent pas : chacune fait ici 243 × 227 pour un
     visuel de 416 de côté, et la sombre recouvrirait la claire.

     ⚠️ LA SOMBRE DÉBORDE DU VISUEL PAR LE BAS, d'une quarantaine de pixels : à
     55 % de haut pour 55 % d'ordonnée, il n'y a pas d'autre place. Elle tombe
     dans le rembourrage de la section (`--sp-section`, 54 px à cette largeur),
     qui ne rogne qu'à ses propres bords — c'est voulu, et c'est ce qui laisse
     les deux piles entières. */
  .texte-halftone__carte--clair {
    top: 0%;
    left: 26%;
  }
  .texte-halftone__carte--sombre {
    top: 55%;
    /* 4 % et non 2 % : le visuel déborde de 9 % à gauche, donc une carte posée
       trop près de SON bord sortait de l'écran et son filet était rogné. */
    left: 4%;
  }
}
/* =========================================================================
   « Votre quotidien » — 4 cas concrets en grille 2×2, chacun dans une carte
   de sable posée sur le crème.

   Ajoutée le 09/09/2026 (Céline). Pastilles sans chiffre : de simples
   repères de forme (voir partials/forme.njk), pas un rang numéroté.

   Cartes de sable depuis le 15/09/2026 (Hugo) : les quatre cas étaient posés
   nus sur le crème jusque-là.
   ========================================================================= */
.quotidien {
  /* ⚠️ LE RYTHME DU QUINCONCE, RESSERRÉ EN TROIS FOIS — demandé par Hugo le
     16/09/2026 : « tu peux limiter les écarts verticaux entre les items », puis
     « tu peux encore plus rapprocher », puis « tu peux le faire encore ». Le
     défaut de la primitive (34 → 68px) est passé à 17 → 27, puis à 10 → 14, puis
     à 8 → 10.

     Le défaut est calé sur du contenu NU : `liste-glyphes` et la liste de
     `face-a-face` posent des `item()` sans fond, et il faut cette respiration-là
     pour que deux entrées se détachent. Ici chaque entrée porte une CARTE, qui se
     sépare déjà de la suivante par son fond, son arête et son rembourrage —
     c'est ce qui laisse descendre si bas sans que deux cas se confondent.

     ⚠️ LE RYTHME EST À BOUT DE COURSE, ET LE LEVIER EST PASSÉ AU REMBOURRAGE.
     C'est la leçon de ce troisième pas, et elle vaut pour n'importe quelle liste
     de cartes : entre deux d'entre elles, l'intervalle VU vaut « rembourrage +
     écart + rembourrage », donc le rembourrage y compte DOUBLE et l'écart une
     seule fois. Au deuxième resserrage, diviser l'écart par deux ne resserrait
     déjà plus l'intervalle que d'un sixième. Le rembourrage BLOC descend donc
     avec lui (voir `.quotidien__carte`), et c'est ce qui rend ce pas-ci visible
     là où un quatrième sur le seul écart ne rendrait presque plus rien.

     ⚠️ POSÉ ICI ET NON DANS `.quinconce` : c'est une primitive partagée, et une
     valeur écrite là-bas atteindrait `liste-glyphes` et la liste de
     `face-a-face`, qui n'ont pas de carte et ont besoin du rythme large. La
     propriété s'hérite, donc la poser sur la section suffit à n'atteindre
     qu'elle.

     ⚠️ CHAQUE RESSERRAGE RÉTRÉCIT LE TRACÉ EN POINTILLÉ, qui se cale sur la
     HAUTEUR de la liste : ~41 % de la largeur à l'origine, ~36 % après le
     premier resserrage, ~34 % après le deuxième, ~30 % après celui-ci — le
     rembourrage pesant bien plus lourd que l'écart, c'est lui qui fait l'essentiel
     de la perte. Voir le gabarit : le remède est `data-chemin-*`, jamais de
     rouvrir le rythme. */
  --quinconce-rythme: clamp(var(--sp-2), 0.7vw, var(--sp-3));
  padding-block: clamp(1rem, 0.5rem + 1.5vw, 2rem);
  /* Les boucles du tracé (partials/chemin.njk) débordent la liste par
     construction : la section les arrête pour qu'elles ne puissent jamais créer
     de barre de défilement horizontale. Même règle que `.liste-glyphes`. */
  overflow-x: clip;
}

.quotidien__inner {
  display: flex;
  flex-direction: column;
  gap: var(--sp-8);
}

/* Aucune taille, aucune police : un <h2> nu prend l'échelle du site (règle 3
   de MAQUETTE.md). Seule l'encre est posée, comme le fait `.approche__titre`
   juste en dessous — les titres du site restent bleu nuit. */
.quotidien__titre {
  margin: 0;
  color: var(--c-blue-zodiac);
}

/* LA LISTE N'A PLUS DE RÈGLE ICI : c'est `.quinconce` (_components.scss) qui
   porte la grille, l'alternance, le rythme vertical et le point de rupture
   depuis le 16/09/2026. Le gabarit dit pourquoi, et pourquoi la carte est
   enveloppée d'une `.quotidien__cellule` — laquelle ne porte volontairement
   aucun style : elle n'existe que pour recevoir le retrait latéral de la
   primitive à la place de la carte.

   Une grille 2×2 à gouttière --sp-6 vivait ici, et _probleme-conclusion.scss en
   dérivait la largeur de sa carte « Notre réponse » pour qu'elle coïncide avec
   ces cases. Ce calcul à deux moitiés est retiré des deux côtés : la conclusion
   est désormais empilée et centrée sur sa propre mesure, il n'y a plus de case
   à rejoindre. */
/* ---------- La carte de sable ----------
   Demandée par Hugo le 15/09/2026 : « mets les quatre éléments dans des boîtes
   grises comme on a fait la boîte IA sans gouvernance ». C'est le camp dépassé
   du face-à-face de /connecteur-ia/ (.face-a-face__colonne--moins), et les trois
   valeurs qui le font sont reprises telles quelles : le sable --c-base-surface,
   le rembourrage `clamp(--sp-6, 2.4vw, --sp-10)` et le rayon de 28px.

   ⚠️ AUCUN FILET, et c'est mesuré, pas un oubli : le sable se détache du crème
   à 1,22 — assez pour dessiner son arête seul, là où les cartes BLANCHES de
   `cas-clients` à 1,05 en réclament un. Même arbitrage que sur la carte d'en
   face, dont le commentaire porte les mêmes chiffres.

   ⚠️ 28px ET NON LES 20px DES CARTES DU SITE (`module.cartes`) : c'est le rayon
   de la boîte nommée en référence, et ces cartes ont la même largeur qu'elle
   (une demi-colonne). Elles sont en revanche bien plus basses ; si l'arrondi
   paraît lourd, c'est --carte-radius qu'il faut descendre à 1.25rem, et rien
   d'autre.

   --section-bg et --bg-page suivent le fond, comme sur la carte de référence :
   un objet posé dedans qui lirait « le fond de la page » recopierait le sable
   et non le crème. */
.quotidien__carte {
  --carte-radius: 1.75rem;
  /* La chasse du titre de carte, NOMMÉE parce que la croix la relit : les deux
     doivent avoir le même corps pour que leur alignement se voie, et deux
     littéraux auraient divergé au premier réglage sans que rien ne le signale. */
  --carte-titre-taille: clamp(1.125rem, 0.87rem + 0.79vw, 1.625rem);
  --section-bg: var(--c-base-surface);
  --bg-page: var(--c-base-surface);
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  /* ⚠️ `baseline` ET NON `start` : la croix se cale sur la LIGNE DE BASE du
     titre, pas sur le haut de sa boîte de ligne. C'est la leçon du long
     commentaire « Alignement des marqueurs en forme » de _components.scss —
     toute valeur écrite à la main pour deviner où tombent les capitales dans la
     ligne est condamnée à dériver, le navigateur est le seul à connaître les
     métriques de la fonte.

     La colonne de texte est un conteneur flex en colonne : sa première ligne de
     base est celle de son premier enfant, donc celle du titre. C'est ce qui rend
     l'alignement juste sans que la règle ait à savoir que la description existe.

     C'était `center`, qui centrait le repère sur la hauteur du couple
     titre + description — d'où la demande du 15/09/2026. */
  align-items: baseline;
  gap: var(--sp-5);
  /* ⚠️ LE REMBOURRAGE EST DISSOCIÉ SUR LES DEUX AXES depuis le 16/09/2026, et
     c'est le vrai levier du troisième resserrage (« tu peux le faire encore »).
     Le rythme du quinconce ne pouvait plus rien donner : dans l'intervalle vu
     entre deux cartes, le rembourrage compte deux fois et l'écart une seule.

     Seul le BLOC descend (34 → 27px au large), l'INLINE ne bouge pas : c'est la
     hauteur qu'Hugo resserre, et rétrécir aussi les flancs aurait changé la
     mesure du texte dans la carte — donc son nombre de lignes, donc sa hauteur,
     ce qui travaille contre la demande. La dissymétrie qui en résulte est légère
     et va dans le sens de l'objet : une carte qui porte un titre et deux lignes
     n'a pas besoin d'autant de haut que de côté.

     ⚠️ CE N'EST PLUS LE REMBOURRAGE DE LA BOÎTE « IA SANS GOUVERNANCE » d'où la
     carte est venue. Le fond, l'arête et le rayon en viennent toujours ; le
     rembourrage, non — ne pas le « réaligner » dessus. */
  padding-block: clamp(var(--sp-5), 1.9vw, var(--sp-8));
  padding-inline: clamp(var(--sp-6), 2.4vw, var(--sp-10));
  border-radius: var(--carte-radius);
  background-color: var(--c-base-surface);
}

/* ---------- La croix ----------
   ⚠️ L'ENCRE DISCRÈTE EST REPRISE DU ✕ DU FACE-À-FACE, et son raisonnement
   tient ici tel quel : le camp sans Biron est ÉTEINT (--text-muted), celui avec
   Biron est ALLUMÉ. Ces quatre cartes sont le constat, donc le camp éteint.
   5,58 sur le sable — mesuré sur ce même fond dans _face-a-face.scss.

   La maquette d'origine du face-à-face posait ce ✕ en rose ; le commentaire de
   ce fichier-là dit pourquoi le site ne le suit pas (le seul rose disponible est
   --c-pilier-plateforme, et il se lirait comme un renvoi vers la plateforme).
   Si la croix paraît trop discrète, c'est --c-blue qu'il faut lui donner, pas un
   rose.

   AUCUNE LARGEUR FIXE : les quatre cartes portent le même glyphe au même corps,
   il n'y a donc rien à faire coïncider de l'une à l'autre — même raison que dans
   _face-a-face.scss. */
.quotidien__croix {
  /* La MÊME fonte et le MÊME corps que le titre, sur un élément qui ne porte
     qu'un glyphe. Ce n'est pas décoratif : c'est de sa fonte que le navigateur
     tire la ligne de base sur laquelle `align-items: baseline` va l'accrocher,
     et deux métriques différentes décaleraient le calage — c'est la raison que
     donne déjà `.item__marqueur--glyphe` dans _components.scss. */
  font-family: var(--font-serif);
  font-size: var(--carte-titre-taille);
  line-height: 1.3;
  font-weight: var(--fw-semibold);
  color: var(--text-muted);
}

.quotidien__texte {
  display: flex;
  flex-direction: column;
  gap: 0.375rem;
}

.quotidien__carte-titre {
  font-family: var(--font-serif);
  font-weight: var(--fw-semibold);
  font-size: var(--carte-titre-taille);
  line-height: 1.3;
  letter-spacing: -0.01em;
  color: var(--c-blue-zodiac);
}

.quotidien__carte-texte {
  font-size: var(--fs-base);
  line-height: var(--lh-normal);
  color: var(--text-muted);
}

/* Aucun point de rupture ici : c'est `.quinconce` qui s'effondre en une colonne
   sous `lg`, et pour sa raison — à cette largeur l'alternance ne se lit plus
   comme un rythme, seulement comme des marges incohérentes d'une entrée à
   l'autre. La grille d'avant tombait sous `sm` ; ce n'est pas un réglage perdu,
   c'est le seuil de l'objet qui a changé avec l'objet. */
/* =========================================================================
   Conclusion de « La problématique » — sections/probleme-conclusion.njk.

   Le constat nu sur le crème, puis la réponse de Biron dans une carte bleu
   nuit : les deux empilés et centrés sur un même axe.

   Les deux moitiés ont été inverties le 15/09/2026 (Hugo) : la carte sombre
   portait le constat et la réponse était un exergue derrière une barre fluo.
   Le gabarit dit pourquoi. Cette feuille reprenait alors
   .texte-module__chute-rangee / __conclusion / __exergue ; elle ne le fait
   plus — la composition n'est plus celle-là, et les emprunter aurait couplé
   cette section aux réglages de /pourquoi-biron/.
   ========================================================================= */
/* ⚠️ LE RETRAIT DU HAUT EST OUVERT EN DEUX FOIS le 16/09/2026 (27 → 41 → 54px au
   large) : « un tout petit peu plus d'espace entre la fin des items, le Pourquoi
   en arrive-t-on là et Notre réponse », puis « mais un POIL plus d'espace entre
   le dernier item et le Pourquoi en arrive-t-on là ».

   C'EST LUI QUI PORTE L'ESPACE APRÈS LES ITEMS, et non le bas de `.quotidien` :
   les deux s'additionnent (l'une finit, l'autre commence), mais le retrait bas de
   `.quotidien` est écrit en raccourci avec son retrait HAUT, qui sépare la
   section de « La problématique » au-dessus. L'ouvrir là-bas aurait déplacé une
   respiration qu'on ne touchait pas. Total entre la dernière carte et le titre du
   constat : 54 → 68 → ~76px.

   ⚠️ LES DEUX RESPIRATIONS DE LA FIN DE SECTION NE SONT PLUS ÉGALES, et c'est
   voulu depuis ce second réglage : la première sépare deux SECTIONS (les cas, puis
   la conclusion), la seconde sépare deux objets D'UNE MÊME pensée (le constat et
   sa réponse). Elles étaient calées sur la même valeur au réglage précédent ; ne
   pas les « réaccorder » sans le demander.

   ⚠️ CET ESPACE EST LE CONTRECOUP DES TROIS RESSERRAGES du quinconce le même jour
   (voir --quinconce-rythme dans _quotidien.scss). Les cartes s'étant rapprochées
   de 135 à 64px les unes des autres, la fin de section devait suivre pour que la
   conclusion ne se lise pas comme une cinquième carte. Ne pas resserrer ceci sans
   regarder ce qu'il en est de cela. */
.conclusion-bloc {
  padding-block: clamp(var(--sp-6), 3.4vw, var(--sp-16)) clamp(5rem, 3rem + 5vw, 9rem);
}

/* ---------- La pile ----------
   ⚠️ UNE PILE ET NON PLUS UNE RANGÉE, d'où le changement de nom : c'était
   `__rangee`, trois colonnes (constat | flèche | carte). Hugo, 16/09/2026 :
   « finir sur Pourquoi on en arrive là et notre réponse centrée et alignées
   verticalement ». Un nom qui aurait continué de dire « rangée » aurait menti.

   `align-items: center` centre les deux blocs sur un même axe vertical — et
   c'est ce qui les « aligne », leurs mesures étant différentes.

   ⚠️ LA FLÈCHE QUI LES SÉPARAIT A ÉTÉ RETIRÉE le 16/09/2026 (« enlève la
   flèche »), balise et règles comprises : un objet sans appelant se lit comme
   s'il agissait encore. Il ne reste donc que l'espacement de la pile pour
   marquer le passage du constat à la réponse, ce qui suffit — c'est le parti du
   site partout ailleurs (voir le quinconce, dont la respiration est « le seul
   signal de séparation »). Elle est dans git à cette date.

   ⚠️ CE QUI DISPARAÎT AVEC LA RANGÉE, et qu'il ne faut pas chercher à rétablir :

     la LARGEUR CALQUÉE sur les cases de « Votre quotidien » (demandée la
       veille) n'a plus de case à rejoindre — cette section est passée en
       quinconce le même jour. Le calcul à deux moitiés est retiré des deux
       côtés, avertissement croisé compris.

     la HAUTEUR ÉGALE des deux blocs (demandée la veille aussi) disait que deux
       objets CÔTE À CÔTE devaient descendre à la même ligne. Empilés, ils n'ont
       plus de hauteur à partager. Les deux `justify-content: center` qui la
       servaient sont partis avec elle. */
.conclusion-bloc__pile {
  /* ⚠️ DEUX MESURES DEPUIS LE 16/09/2026, ET PLUS UNE SEULE PARTAGÉE : Hugo a
     demandé la phrase de réponse « en plus gros dans une box plus large ». La
     carte sort donc de la mesure du constat — elles restent centrées sur le même
     axe, mais ne sont plus de même largeur.

     45rem ≈ 612px pour le CONSTAT, où il compose à ~78 signes par ligne : un peu
     long pour de la prose suivie, acceptable pour deux points brefs ouverts par
     un gras. Inchangé.

     64rem ≈ 870px pour la CARTE, et la valeur est calée sur sa phrase, passée à
     --fs-h2 : à cette mesure, rembourrage ôté, elle tient sur deux lignes. Le
     plafond réel reste le conteneur (80rem), donc la carte occupe ~80 % de la
     largeur de page — nettement plus large que le constat, ce qui est la demande,
     sans toucher les bords. */
  --conclusion-mesure: 45rem;
  --conclusion-mesure-carte: 64rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  /* ⚠️ OUVERT DEUX FOIS, et la seconde est la demande d'aération du 16/09/2026
     (« un tout petit peu plus d'espace […] et Notre réponse ») : 34 → 54 → 68px
     au large.

     La première ouverture accompagnait le RETRAIT DE LA FLÈCHE, et ce n'était pas
     une décision à part : la flèche occupait 44px de haut entre deux écarts de
     34, soit ~112px de séparation. La retirer sans toucher à l'espacement aurait
     ramené les deux blocs à 34px l'un de l'autre, un tiers de ce qu'Hugo avait
     validé. À 68px, l'écart revient près de ce que la flèche laissait — la
     différence étant qu'il n'y a plus d'objet au milieu pour l'occuper, ce qui
     est exactement l'effet cherché.

     C'est le seul espace ENTRE le constat et la carte : il n'y a plus rien entre
     eux, donc aucune autre valeur ne s'y ajoute — à la différence de l'espace
     après les items, qui est une somme de deux retraits (voir plus haut). */
  gap: clamp(var(--sp-10), 5vw, var(--sp-20));
}

/* Chacun remplit sa mesure : sans `inline-size`, un bloc flex en colonne se
   réduirait à son contenu, et la carte cesserait d'être « plus large » dès que
   sa phrase tiendrait sur une ligne. */
.conclusion-bloc__constat {
  inline-size: 100%;
  max-inline-size: var(--conclusion-mesure);
}

.conclusion-bloc__reponse {
  inline-size: 100%;
  max-inline-size: var(--conclusion-mesure-carte);
}

/* ---------- Le constat, nu sur le crème ---------- */
.conclusion-bloc__constat {
  display: flex;
  flex-direction: column;
  gap: var(--sp-5);
}

/* ⚠️ LE TITRE EST CENTRÉ, MAIS PAS SA LISTE, et c'est l'arbitrage d'Hugo du
   16/09/2026 : le bloc entier est centré sur la page, les deux raisons gardent
   leurs puces et leur fer à gauche. L'alternative — deux phrases centrées sans
   disque — a été écartée par lui.

   Ce que ça évite : centré, un disque se retrouve loin de son texte dès que la
   raison passe sur deux lignes, et les deux en font trois. Le centrage s'arrête
   donc au titre, ce qui suffit à faire converger la fin de section après le
   zigzag du quinconce.

   Aucune taille en face, et c'est ce qui fait tout le travail : le titre est un
   <h3> depuis le 15/09/2026 (le constat est une SOUS-SECTION, pas une section de
   plein rang), et un <h3> nu prend --fs-h3. `text-wrap: balance` vient lui aussi
   de la règle de base des titres.

   Seule l'encre est posée : les titres du site restent bleu nuit. */
.conclusion-bloc__titre {
  text-align: center;
  color: var(--c-blue-zodiac);
}

/* ⚠️ Sélecteur `ul.` et non `.` seul : le reset de _base.scss cible
   `ul[class]` (élément + attribut, spécificité 0,1,1) et l'emporterait sur
   une simple classe (0,1,0), même chargée après lui. */
ul.conclusion-bloc__liste {
  margin: 0;
  padding-left: 0;
  list-style: none;
}

/* ⚠️ UN LOSANGE BLEU, PAS UN DISQUE, depuis le 16/09/2026 (Céline : « pour que
   ça ressorte mieux »). Le disque `::marker` ne peut pas devenir une forme de
   marque — un marqueur de liste ne peut porter ni masque CSS ni transform —
   d'où la puce en frère du texte plutôt qu'un pseudo-élément, exactement le
   raisonnement de « marque » dans partials/item.njk. */
.conclusion-bloc__liste li {
  display: flex;
  /* `flex-start` et non plus `baseline`, depuis que la forme est aussi
     grande que les deux lignes de texte (voir `.conclusion-bloc__forme`) :
     une forme de cette taille alignée sur la ligne de base flotterait bien
     au-dessus du texte, la baseline d'un gros aplat n'étant pas celle d'un
     caractère. Callée en haut, elle borne le bloc de texte comme une vraie
     icône plutôt que comme une puce de liste. */
  align-items: flex-start;
  gap: var(--sp-3);
  font-size: var(--fs-base);
  line-height: var(--lh-normal);
  color: var(--text-strong);
}

.conclusion-bloc__liste li + li {
  margin-block-start: var(--sp-3);
}

.conclusion-bloc__puce {
  flex-shrink: 0;
}

/* ⚠️ TROIS RÉGLAGES DE SUITE le 16/09/2026 (Céline) — 0,6 → 1rem (« plus
   grosses »), puis « la taille des 2 lignes de paragraphe » (3,2em, deux fois
   --lh-normal), puis « trop grosses […] réduit par 2 » : 1,6em. Restée en
   `em` plutôt qu'en `rem` : elle se cale ainsi sur --lh-normal (1,6) du texte
   qui la suit — une ligne de ce corps — et resterait juste si le corps du
   texte changeait un jour, sans recalcul à la main. */
.conclusion-bloc__forme {
  --shape-w: 1.6em;
}

/* ---------- La carte de réponse ----------
   ⚠️ ARRONDI 20px ET NON LES 28px DES BOÎTES DE SECTION : c'est le rayon des
   cartes du site, et la raison est celle que porte `.texte-module__conclusion`
   — une carte posée dans une page qui arrondit ses boîtes à 28 doit se lire
   comme une carte, pas comme une petite boîte de section.

   ⚠️ AUCUN LISERÉ : un anneau autour d'un aplat bleu nuit posé sur crème
   double une arête que le contraste fait déjà (règle 1 de MAQUETTE.md).

   `.on-dark` est sur la CARTE, jamais sur la section : posée sur la section,
   la classe redéfinirait --bg-page, dont la section se sert comme fond, et la
   carte s'y dissoudrait.

   ⚠️ SON CONTENU EST CENTRÉ, à la différence du constat au-dessus : Hugo écrit
   « notre réponse centrée », au singulier, et la carte n'a pas de liste à puces
   qui s'y opposerait. C'est la fin de la section, le point d'arrivée du zigzag
   du quinconce — un surtitre et une phrase, tous deux centrés.

   Son REMBOURRAGE s'ouvre d'un cran avec elle (34 → 54px au large) : calé sur
   une phrase au corps du texte, il paraissait serré sous une phrase de 41px.
   C'est une conséquence de l'agrandissement, pas une demande à part. */
.conclusion-bloc__reponse {
  /* Les deux variables du motif — voir `.conclusion-bloc__reponse-decor`
     plus bas. Postées ici et non sur le décor : c'est cette carte qui EST
     le fond réel, l'invariant que _components.scss demande (les séries qui
     reculent sont la base mélangée à ce fond). */
  --motif-fond: var(--c-blue-dark);
  --motif-base: color-mix(in srgb, var(--c-blue) 40%, var(--c-white));
  position: relative;
  overflow: hidden;
  border-radius: 1.25rem; /* 20px */
  background-color: var(--c-blue-dark);
  padding: clamp(var(--sp-8), 3.5vw, var(--sp-16));
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
  text-align: center;
}

/* ---------- Le décor ----------
   Le motif « bulles » (partials/motif.njk), demandé par Céline le 16/09/2026 :
   même procédé que les briques de /produit/ — un dessin en demi-teinte DANS la
   boîte, rogné par son arrondi — mais « bulles » est le seul des dix dessins
   assez généreux en surface pour couvrir la largeur d'un rectangle horizontal
   sans se réduire à un coin (voir l'en-tête de partials/motif.njk).

   ⚠️ LARGEUR PLUTÔT QUE HAUTEUR, à l'inverse de /produit/ (`--motif-taille`
   en `block-size`) : cette carte est un rectangle large et bas quand les
   briques produit sont hautes et étroites. Caler sur la largeur laisse le
   dessin suivre le rapport de son propre viewBox (349 × 220, un peu plus
   large que haut) au lieu de le faire déborder par les côtés — et c'est ce
   qui le rend plus haut que la place qui lui reste sous le texte : il DOIT
   déborder quelque part, et lequel des deux bouts déborde est tout l'enjeu
   ci-dessous.

   ⚠️ LE TEXTE RESTE LISIBLE PAR EMPILEMENT, PAS PAR TRANSPARENCE — c'est le
   `z-index` qui fait le travail, jamais une opacité sur le motif : baisser
   son opacité aurait aussi éteint le contraste de ses propres points entre
   eux, qui est ce qui les rend lisibles comme des bulles plutôt que comme du
   bruit. `--motif-base` à 40 % de blanc (contre 55 % essayé puis écarté)
   tient le compromis : les pilules blanches et le texte gras restent le
   sujet, les bulles un fond qu'on remarque en second.

   ⚠️ ANCRÉ EN HAUT, PAS EN BAS — le REVIREMENT du 16/09/2026. Un premier essai
   ancrait le motif au bas de la carte et rognait son excédent PAR LE HAUT, à
   l'intérieur d'une bande : ça coupait des bulles en deux au ras de la bande,
   en plein milieu de leur disque. Céline : « il ne faut pas rogner en haut […]
   elles doivent être pleines vers le haut, il faut rogner en bas ». Le motif
   est donc ancré par le HAUT de `.conclusion-bloc__etiquettes-zone` — juste
   sous la phrase, jamais dessus — et c'est son BAS qui déborde. Rien ne le
   rogne à l'intérieur de la zone (elle n'a pas d'`overflow` à elle) : c'est la
   carte entière, `.conclusion-bloc__reponse`, qui absorbe ce débord à SON
   bord et à SON arrondi — le seul endroit où une coupe nette est déjà
   attendue, exactement comme la référence de Céline. */
.conclusion-bloc__etiquettes-zone {
  position: relative;
}

.conclusion-bloc__reponse-decor {
  position: absolute;
  inset-block-start: 0;
  inset-inline: 0;
  z-index: 0;
  pointer-events: none;
}

/* ⚠️ 138 %, PAS 100 % — les bulles s'arrêtaient avant le bord droit de la
   carte à 100 %, et ce n'est pas un souci de largeur mais de DESSIN : la
   composition de `motifBulles` (partials/motif.njk) est elle-même plus dense
   à gauche qu'à droite — quatre bulles seulement passé x=305, sur un viewBox
   qui va jusqu'à 381. Zoomer et centrer pousse cette partie clairsemée hors
   du cadre des deux côtés, et ne garde que le CŒUR dense de la composition,
   qui couvre alors toute la largeur de la carte. Toute valeur au-dessus de
   100 % marche dans ce sens ; 138 % est la première qui ne laisse plus de
   bord visiblement plus clairsemé que le centre, jugé à l'écran. */
.conclusion-bloc__reponse-decor .motif {
  position: absolute;
  inset-block-start: 0;
  inset-inline-start: 50%;
  translate: -50% 0;
  /* ⚠️ `max-width` EXPLICITEMENT LEVÉ, ET C'EST CE QUI A COÛTÉ LE PLUS DE
     TEMPS À TROUVER. Le reset global du site (_base.scss) pose
     `img, picture, svg, video, canvas { max-width: 100% }` : `inline-size`
     (la propriété juste en dessous) GAGNE le calcul de largeur par
     spécificité, mais `max-width` est une propriété À PART qui continue de
     plafonner le résultat à 100 % du conteneur — les deux ne se disputent
     pas la même déclaration, donc la spécificité plus forte d'ici ne suffit
     pas à l'emporter. Sans cette ligne, `inline-size: 138%` se voyait
     silencieusement ramené à 100 %, sans qu'aucun outil ne le signale. */
  max-width: none;
  inline-size: 138%;
  block-size: auto;
}

/* Les étiquettes passent devant le décor — la seule raison pour laquelle il
   reste dans la même zone au lieu d'un calque à part. */
.conclusion-bloc__etiquettes {
  position: relative;
  z-index: 1;
}

/* Un <p> et non un <h3> : « Notre réponse » étiquette la phrase qui suit, elle
   n'ouvre pas une sous-partie du document — le seul titre de cette section est
   celui du constat. C'est d'ailleurs ce que sa forme dit maintenant.

   ⚠️ LA FORME VIENT DE `.eyebrow` (_components.scss), POSÉE DANS LE GABARIT :
   mono, 11px, capitales, interlettrage large. Demandé par Hugo le 16/09/2026 —
   « mets le titre Notre réponse en mono et petite ». Il portait la chasse d'un
   <h3> jusque-là, et tout ce bloc de déclarations a disparu avec : c'est la
   primitive qui les porte, il n'y a plus de faux <h3> à tenir accordé avec
   _base.scss.

   ⚠️ SEULE L'ENCRE EST REPRISE, et le sélecteur est COMPOSÉ pour ça. `.eyebrow`
   met --text-muted, qui donnerait du casper sur ce fond ; le fluo est ce qui
   distingue l'étiquette de la phrase blanche en dessous, qu'Hugo a demandée
   blanche il y a deux jours. À classe simple, la règle pèserait le même poids
   que `.eyebrow` et ne l'emporterait que par l'ordre des feuilles — la leçon de
   `carte-etiquettes` : quand on surcharge un atome partagé, on le surcharge par
   la spécificité, pas par le hasard du chargement.

   Le fluo arrive par --text-accent et non écrit en clair : sur ce fond
   `.on-dark` a déjà mis ce rôle au fluo (11,54 sur le bleu nuit), et une couleur
   en dur serait fausse le jour où la carte changerait de fond — c'est ce qui est
   arrivé à `.etapes__boite` quand elle est devenue verte. */
.conclusion-bloc__reponse-titre.eyebrow {
  color: var(--text-accent);
}

/* ⚠️ --fs-h2, LE CRAN AU-DESSUS DE CELUI DU TITRE DU CONSTAT. Hugo l'a demandée
   deux fois le 16/09/2026, dans le même sens : d'abord « en même taille de
   police que Pourquoi en arrive-t-on là » (--fs-h3, la chasse d'un <h3> nu),
   puis « en plus gros dans une box plus large ». C'est donc le barreau suivant
   de l'échelle du site, jamais une valeur inventée (règle 3 de MAQUETTE.md) —
   24→41px, contre 19→27 pour le titre au-dessus.

   Elle prend aussi la police et l'interlignage d'un titre : à cette chasse, le
   corps de texte du site et son --lh-normal de 1,6 laisseraient des lignes
   bien trop espacées. C'est une déclaration, plus du texte courant.

   --ls-tight est bien le crénage des h2 comme des h3 dans _base.scss : la valeur
   ne bouge donc pas avec la chasse.

   « Le texte qui suit en blanc » (14/09/2026) : c'est --text-strong, que
   `.on-dark` met au blanc — jamais --text-muted, qui donnerait du casper. */
/* ⚠️ TAILLE RÉDUITE ET INTERLIGNE OUVERT le 16/09/2026 (Céline : « un peu plus
   d'interligne » et « réduit un peu la taille »), une fois les deux cartes
   d'étiquettes ajoutées dessous : la phrase à --fs-h2 serrée à --lh-tight
   (1,08) pesait trop lourd au-dessus d'elles. `calc(... * 0.85)` garde le
   fluide du clamp (règle 3 de MAQUETTE.md — jamais une valeur bornée
   inventée à côté) plutôt que de redescendre à --fs-h3, un cran entier plus
   bas que ce qui a été demandé. --lh-snug (1,35) est le palier suivant après
   --lh-tight dans l'échelle du site — ni collé comme un titre, ni aéré comme
   du texte courant. */
.conclusion-bloc__reponse-texte {
  font-family: var(--font-titre);
  font-size: calc(var(--fs-h2) * 0.85);
  font-weight: var(--fw-bold);
  line-height: var(--lh-snug);
  letter-spacing: var(--ls-tight);
  color: var(--text-strong);
  text-wrap: balance;
}

/* ---------- Les deux cartes d'étiquettes ----------
   Ajoutées le 16/09/2026 (Céline) : les cartes « Secteurs » et « Équipes » de
   l'ancienne section (_data/cible.yaml sur `master`), reprises via
   partials/carte-etiquettes.njk — pas la version en pile de
   sections/_texte-halftone.scss, qui ne s'applique qu'aux cartes de
   `.texte-halftone__carte`. Celles-ci gardent la mise en page simple de
   l'atome : un intitulé, des pilules qui se replient.

   ⚠️ TROIS RÉGLAGES DE SUITE SUR `ton`, ET C'EST LE DERNIER QUI COMPTE :
   d'abord les deux en `sombre` (lisibles sur le bleu nuit de cette carte),
   puis reprises à l'identique de `master` (« Secteurs » clair, « Équipes »
   sombre), puis Céline, en voyant ce second rendu empilé en quinconce
   vertical : « les 2 blanches ». Les deux cartes sont donc en `ton: clair`
   dans les données, sans surcharge de couleur ici.

   ⚠️ CÔTE À CÔTE, MÊME HAUTEUR, ALIGNÉES — dernier réglage du 16/09/2026.
   Le quinconce (décalage vertical d'une carte, `align-items: flex-start`)
   a été essayé puis écarté : Céline « ça ne va toujours pas […] je veux
   que les 2 cartes soient de la même hauteur […] et qu'elles soient
   alignées ». `align-items` repasse donc à son défaut `stretch` : les deux
   cartes prennent la hauteur de la plus grande (« Équipes », 6 valeurs sur
   trois rangs), et « Secteurs » (4 valeurs, deux rangs) hérite du même
   cadre avec un peu d'air sous ses pilules plutôt qu'une hauteur propre. */
.conclusion-bloc__etiquettes {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--sp-4);
}

/* ⚠️ UNE LARGEUR PLAFONNÉE, ET C'EST CE QUI PERMET LA RANGÉE. Sans elle,
   chaque carte s'étire à la largeur de tous ses libellés mis bout à bout —
   « Équipes » (6 valeurs) dépassait alors 490px, et les deux ensemble ne
   tenaient plus côte à côte dans la mesure de la carte de réponse : la
   rangée retombait en pile. Plafonnée, chaque carte replie ses pilules sur
   deux colonnes (comme sur `master`), les deux prennent alors une largeur
   proche l'une de l'autre, et la rangée tient. */
.conclusion-bloc__etiquette {
  max-width: 16rem;
}

/* ⚠️ LE FILET DES PILULES ÉTAIT INVISIBLE, ET C'ÉTAIT LE VRAI DÉFAUT derrière
   « il manque les étiquettes qui entourent les mots ». `.carte-etiquettes--clair`
   lit `--border-subtle` (_components.scss), une PROPRIÉTÉ PERSONNALISÉE — et
   `.on-dark`, posée sur `.conclusion-bloc__reponse` qui contient ces cartes, la
   redéfinit en blanc translucide pour les filets qui vivent DIRECTEMENT sur le
   bleu nuit (voir _components.scss, ~L.605). Une carte `clair` héritait donc
   d'un filet blanc à 25 % sur son propre fond crème — quasi invisible (contraste
   ≈ 1). Le filet clair d'origine (`rgba(44, 42, 46, 0.12)`, _tokens.scss) est
   redéclaré ici, sur le sélecteur le plus spécifique aux deux cartes, pour ne
   pas rouvrir `--border-subtle` au niveau de la carte entière — ce qui aurait
   aussi blanchi le rembourrage textuel de `.on-dark` ailleurs dans le bloc. */
.conclusion-bloc__etiquette.carte-etiquettes--clair {
  --border-subtle: rgba(44, 42, 46, 0.12);
}

/* AUCUN POINT DE RUPTURE : la pile est déjà la disposition étroite. La règle
   `md` d'avant ne faisait qu'effondrer la rangée de trois colonnes en une seule
   et redresser la flèche — les deux sont devenus la règle tout court. La mesure
   commune est bornée par `inline-size: 100%`, donc elle se resserre d'elle-même
   sous 45rem sans qu'un seuil soit publié nulle part. */
/* =========================================================================
   « Biron : ni un logiciel… » — sections/approche.njk.

   Remplace le gabarit .texte-illustre pour cette section le 09/09/2026
   (Céline) : plus de cadre ni de trame, un titre centré en pleine largeur,
   puis texte à gauche / trio à droite en dessous.
   ========================================================================= */
.approche {
  background-color: var(--bg-page);
  padding-block: var(--sp-section);
}

.approche__titre {
  margin: 0 auto clamp(var(--sp-10), 5vw, var(--sp-16));
  max-width: 32ch;
  text-align: center;
  color: var(--c-blue-zodiac);
}

/* LA PAIRE EST CENTRÉE SOUS LE TITRE (16/09/2026), et c'est ce qui l'a
   détachée de la grille de « AU CŒUR DE BIRON ».

   Elle portait jusque-là les jetons partagés `--colonnes-piliers` /
   `--gouttiere-piliers`, pour que les deux sections — qui se lisent comme une
   seule séquence — tombent au même endroit : le texte sur la largeur du
   sommaire « Les quatre piliers », le trio sur celle des cartes de piliers.
   Deux gestes du même jour l'ont défaite, dans cet ordre : le trio ramené
   contre le texte (il flottait à 143px de lui, centré dans une colonne de
   640px), puis le couple recentré, parce qu'à 802px sur 1088 il pendait à
   gauche sous un titre, lui, centré.

   ⚠️ LE SECOND GESTE EST CELUI QUI TRANCHE, et il n'avait pas d'autre issue :
   des fractions `fr` remplissent tout le conteneur par construction, donc elles
   ne savent pas laisser du blanc de chaque côté — `justify-content` reste sans
   effet tant qu'une piste mange la place. Les deux colonnes sont maintenant
   dimensionnées par leur CONTENU, et les deux sections ne s'alignent plus. Ce
   qui est conservé : la MESURE du texte, reprise à l'identique de l'ancienne
   colonne (30,5rem ≈ 414px, ce que valait le 504fr dans 1088), pour que la
   longueur de ligne du paragraphe ne bouge pas ; et la gouttière, toujours prise
   au jeton partagé, pour que les deux sections respirent pareil.

   La gouttière ne vaut que pour les COLONNES : en dessous de `lg` la grille
   tombe à une seule, et ce qui sépare alors le texte du trio est une respiration
   verticale, calibrée pour cet empilement-là et non sur l'écart entre deux
   colonnes. D'où les deux cotes séparées plutôt qu'un `gap` unique.

   `align-items: center` et non `start` (16/09/2026) : les deux moitiés se lisent
   comme un seul bloc, donc elles se centrent l'une sur l'autre. En `start` la
   plus courte des deux s'accrochait en haut et laissait son creux en bas, ce qui
   les séparait au lieu de les tenir ensemble. Sans effet sous `lg`, où chaque
   élément est seul sur sa rangée. */
.approche__grille {
  display: grid;
  grid-template-columns: minmax(0, 30.5rem) minmax(0, 26rem);
  justify-content: center;
  column-gap: var(--gouttiere-piliers);
  row-gap: clamp(var(--sp-10), 5vw, var(--sp-16));
  align-items: center;
}

.approche__gauche {
  display: flex;
  flex-direction: column;
  gap: var(--sp-6);
}

/* ⚠️ `max-width` NE MORD QUE SOUS `lg`. À deux colonnes la cellule de gauche est
   bornée à 30,5rem par la grille elle-même, donc bien en deçà de ces 40rem :
   c'est la colonne qui donne la mesure. La borne reste pour la colonne unique,
   où le texte courrait sinon sur toute la largeur de la page. */
.approche__texte {
  display: flex;
  flex-direction: column;
  gap: var(--sp-6);
  max-width: 40rem;
  font-size: var(--fs-base);
  line-height: var(--lh-normal);
  color: var(--text-strong);
}

.approche__texte p {
  margin: 0;
}

.approche__visuel {
  width: 100%;
}

/* LE TRIO NE SE CENTRE PLUS DANS SA COLONNE (16/09/2026) : ce qui l'éloignait
   du texte n'était pas la gouttière (34px) mais les 143px de blanc que son
   centrage lui laissait à gauche. Sa taille, elle, ne bouge pas — c'est le parti
   choisi contre celui de l'élargir jusqu'à remplir la colonne.

   Sa piste vaut maintenant ses 26rem exactement (voir `.approche__grille`), donc
   il n'y a plus de blanc à distribuer et rien à écrire ici pour le caler : le
   `margin-inline: auto` retiré serait aujourd'hui sans effet de toute façon.

   ⚠️ Le centrage revient sous `lg`, où la grille tombe à une colonne : le trio
   y est seul sur toute la largeur de la page, et collé à gauche il pendrait dans
   le vide. C'est le `margin-inline` de la règle étroite qui le rattrape. */
.approche__visuel .trio {
  width: min(100%, 26rem);
}

@media (max-width: 60rem) {
  .approche__grille {
    grid-template-columns: minmax(0, 1fr);
  }
  .approche__visuel .trio {
    margin-inline: auto;
  }
}
/* La phrase facultative sous le titre (`approche.chapeau`, posée par
   /irritants/ seulement). Le titre garde sa marge basse : la phrase remonte
   pour s'y accrocher, et reprend la même marge sous elle. */
.approche__chapeau {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-3);
  max-width: 60ch;
  /* Un peu d'air sous le titre avant l'étiquette (Céline, 01/10/2026). */
  margin: calc(-1 * clamp(var(--sp-2), 1.5vw, var(--sp-4))) auto clamp(var(--sp-10), 5vw, var(--sp-16));
  font-size: var(--fs-lead);
  line-height: var(--lh-normal);
  text-align: center;
}

.approche__chapeau p:not(.badge-section) {
  margin: 0;
}

/* =========================================================================
   Au cœur de Biron — Figma 194:5769.

   ⚠️ RECOMPOSÉE LE 27/08/2026, depuis la variante départagée sur
   /labo/coeur-cadres/. Ce qui suit décrit l'état actuel ; l'état précédent était
   UN panneau blanc opaque contenant tout — titre, sommaire et quatre blocs. Il
   n'en reste que la cellule de gauche, qui garde le panneau et ses coins vifs.
   Le titre est centré hors cadre, et les quatre blocs sont des CARTES détachées,
   chacune sur l'aplat sombre de son pilier.

   Conséquence à connaître : plus aucun aplat ne recouvre les trois formes de
   trame du décor. Elles se voient désormais entre les cartes et autour de
   l'en-tête, là où elles n'existaient que dans les marges — c'est pour ça que
   celle du haut à gauche est repositionnée plus bas dans ce fichier.

   ⚠️ AUCUN `overflow: hidden` NI `auto` SUR .coeur. Le sommaire de gauche tient
   par `position: sticky`, qui se règle sur le plus proche ancêtre à overflow non
   visible : l'un ou l'autre ferait de la section le conteneur de défilement et
   le sommaire ne collerait plus. C'est .coeur__decor qui porte le rognage du
   décor. Le `clip` posé sur l'axe horizontal est la seule exception, et il est
   sans danger — voir sa justification sur la règle `.coeur` elle-même.

   ---------------------------------------------------------------------
   La colonne de gauche a été un aparté — un paragraphe et un bouton — avant
   de devenir ce sommaire, le 14/08/2026. Les noms de classe `__aparte` et
   `__aparte-contenu` en sont restés : ils désignent la CELLULE et son
   contenu collant, ce qui reste juste quel qu'en soit le contenu.
   ========================================================================= */
.coeur {
  /* Le filet du cadre, ses séparateurs internes et les marges de la bande de
     titre viennent de jetons PARTAGÉS avec « Une approche data unique »
     (_tokens.scss) : les deux cadres doivent rester identiques, et recopier
     leurs valeurs ici les aurait fait diverger au premier réglage. */
  --coeur-pad-module: clamp(var(--sp-8), 4.2vw, 5rem);
  /* Hauteur RÉELLE de l'en-tête collant du site (`--h-entete`, jeton) plus une
     respiration :
     c'est de là que le sommaire doit se décrocher, sinon il passe dessous.
     C'est aussi ce qui fixe `scroll-margin-top` sur les blocs, donc le point
     d'arrivée d'un clic dans le sommaire. */
  --coeur-colle: calc(var(--h-entete) + var(--sp-8));
  /* La gouttière qui a remplacé le filet vertical du panneau : elle sépare le
     panneau de la colonne de cartes ET les cartes entre elles. Ce sont les deux
     respirations de la même composition, donc une seule cote.

     ⚠️ Elle borne aussi le débord des badges (voir `.coeur__glyphe`) : la
     baisser rapproche les badges de la carte du dessus.

     Sa valeur vient du jeton PARTAGÉ avec « Biron : ni un logiciel… » (voir
     `--gouttiere-piliers` dans _tokens.scss) : les deux sections se suivent,
     elles respirent pareil. ⚠️ Ce partage-là est le seul qui reste — leurs
     COLONNES ne s'alignent plus depuis le 16/09/2026, la section du dessus ayant
     recentré sa paire texte/trio, ce que des fractions `fr` ne savent pas faire.
     L'alias reste, parce que c'est lui que le resserrement sous `lg` redéfinit —
     la section du dessus, elle, est déjà en une seule colonne à cette
     largeur-là. */
  --coeur-ecart-cartes: var(--gouttiere-piliers);
  /* L'écart entre le texte d'une carte et son illustration. Cote À PART de la
     précédente, malgré des valeurs voisines : l'une sépare deux cartes, l'autre
     deux choses DANS une carte. */
  --coeur-texte-visuel: clamp(var(--sp-8), 3.2vw, 3.25rem);
  /* LE RETRAIT DE L'ILLUSTRATION PAR RAPPORT AU TEXTE. La maquette pose le
     visuel un cran plus à l'intérieur que le titre — 89px contre 60 sur une
     carte de 740 — si bien que le titre part franchement à gauche pendant que la
     capture reste en réserve. Sans lui les deux bords coïncideraient et la carte
     se lirait comme un cadre.

     À 1440 la carte fait ~639px pour 519 de contenu, soit 81 % de sa largeur :
     ces 16px de chaque côté ramènent l'image aux 76 % de la maquette. */
  --coeur-visuel-retrait: clamp(0px, 1.1vw, 1rem);
  /* LE FONDU DU BAS DE L'ILLUSTRATION, ET LE RECOUVREMENT QU'IL AUTORISE —
     Figma 471:2739, repris le 16/09/2026. La capture s'éteint vers l'aplat de la
     carte au lieu de s'arrêter sur un bord franc, et le texte vient se poser sur
     la queue devenue transparente. C'est ce qui remplace l'ancienne composition,
     où le texte précédait l'illustration.

     Les deux bornes sont des parts de la HAUTEUR de l'image : le fondu s'amorce à
     55 % et s'achève à 82 %, il reste donc 18 % de vide sous l'encre — et c'est
     là-dedans que le titre vient se loger.

     ⚠️ UNE SEULE COTE DE RECOUVREMENT POUR TROIS HAUTEURS D'IMAGE DIFFÉRENTES,
     ET LA CONTRAINTE EST CHIFFRÉE : depuis que les quatre partagent une largeur
     (16/09/2026, voir `.coeur__illustration`), leurs hauteurs vont de 376 à
     560px à 1440. Le recouvrement est une cote ABSOLUE, la queue transparente une
     PART — donc ce qui doit tenir, c'est que le recouvrement reste plus court que
     la plus courte des queues. Mesuré à 1440 : queues de 80 (intégrations), 85
     (connecteur) et 101px (plateforme) contre un recouvrement plafonné à 4,9rem,
     soit 67px. Marge la plus étroite : 13px sur les intégrations.

     Le dictionnaire, plus court encore (376px), ne compte pas : il n'a ni fondu
     ni recouvrement. Mais la borne à surveiller est bien LA PLUS COURTE DES TROIS
     AUTRES — monter ce plafond, ou rapprocher `--coeur-fondu-fin` de 100 %, ferait
     passer le titre sur une capture encore visible, et rien ne l'annoncerait. */
  --coeur-fondu-debut: 55%;
  --coeur-fondu-fin: 82%;
  --coeur-recouvrement: clamp(var(--sp-6), 5.5vw, 4.9rem);
  /* La mesure de la bande de palier. Nommée plutôt qu'écrite deux fois : en petit
     écran la plaque crème doit l'ÉLARGIR de son rembourrage au lieu de la rogner
     (`box-sizing: border-box`), et deux valeurs en clair auraient divergé au
     premier réglage.

     ⚠️ CE N'EST PLUS LES 380px DE LA MAQUETTE, et c'est la conséquence du
     changement d'objet du 16/09/2026 : la phrase est passée d'un paragraphe de
     16px à une QUOTE au corps des titres (voir `.coeur__bande-texte`). À 24rem
     elle tombait sur cinq lignes de vingt caractères. Les 52ch sont ceux de
     `.intertitre--quote` — même objet, mêmes valeurs, rien de réinventé.

     En `ch` et non en `rem` : la mesure suit alors le corps qui la porte, donc
     un réglage de `--fs-h3` n'a pas à être reporté ici. ⚠️ L'unité se résout
     chez qui LIT la variable (le paragraphe), pas chez qui la déclare — c'est
     ce qui la rend juste, la section n'étant pas au corps des titres. */
  --coeur-bande-mesure: 52ch;
  /* La cote du badge de pilier au coin de la carte. UNE SEULE COTE, ET TOUT EN
     DÉCOULE : son débord hors de la carte et la place que le titre lui réserve.
     Écrites en clair, ces trois valeurs auraient dérivé au premier ajustement,
     et c'est la réserve qui se serait taue — un badge qui recouvre un titre ne
     lève aucune erreur. Même dispositif que `--panneau-glyphe-taille` sur les
     briques produit.

     ⚠️ LE DÉBORD EST BORNÉ PAR LA GOUTTIÈRE, ET LA BORNE EST ÉCRITE PLUTÔT QUE
     VÉRIFIÉE. Le badge sort par le HAUT de sa carte, donc il entre dans l'écart
     qui la sépare de la précédente ; au-delà, il se pose dessus. Un simple
     facteur tenait la contrainte à la main, et un agrandissement l'a franchie
     sans bruit. Le `min()` la rend infranchissable : il reste toujours un
     demi-rem d'écart visible entre deux cartes.

     Conséquence : passé le point où le plafond mord, grossir le badge ne le fait
     plus sortir DAVANTAGE, il grossit vers l'intérieur. Pour qu'il déborde plus,
     c'est `--coeur-ecart-cartes` qu'il faut monter. */
  --coeur-badge: clamp(5.5rem, 9vw, 10rem);
  --coeur-badge-debord: min(
    calc(var(--coeur-badge) * 0.28),
    calc(var(--coeur-ecart-cartes) - 0.5rem)
  );
  position: relative;
  background-color: var(--bg-page);
  padding-block: var(--sp-section);
  /* ⚠️ PLUS DE RESPIRATION EN HAUT depuis le 09/09/2026 : cette section
     enchaîne directement sur « Biron : ni un logiciel… » (sections/approche.njk),
     son propre padding-top ferait double emploi avec le padding-bottom de
     celle-ci. */
  padding-top: 0;
  /* ⚠️ `clip` ET SURTOUT PAS `hidden`, voir l'avertissement en tête de fichier.
     Le badge déborde aussi par le CÔTÉ : en petit écran la carte est à 5 % du
     bord de la fenêtre, soit 19px sur un mobile de 375, pour 18px de débord — le
     badge arrive au ras du bord et un pixel de plus ouvrirait une barre de
     défilement horizontale. `clip` rogne sans créer de conteneur de défilement,
     donc sans casser le collant du sommaire. L'axe vertical reste `visible`,
     sans quoi le débord vers le haut serait tranché — et c'est la seule paire
     mixte légale, `visible` avec autre chose que `clip` deviendrait `auto`. */
  overflow-x: clip;
  overflow-y: visible;
}

/* ---------- Décor ---------- */
.coeur__decor {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden; /* voir l'avertissement en tête de fichier */
  pointer-events: none;
}

.coeur__forme {
  position: absolute;
  width: var(--shape-w);
}

/* Les trois positions viennent de la maquette, en % de la section. Les
   proportions des formes ont permis de les identifier sans ambiguïté :
   1033×1004 = citron, 701×722 = losange, 1230×1307 = écu. */
/* ⚠️ ELLE S'ÉPINGLE SUR LA MESURE DU TEXTE, PAS SUR UN POURCENTAGE DE SECTION,
   et c'est le titre centré qui l'impose : le panneau ne la recouvre plus.
   `left: 2.4%` la plaçait par rapport à la FENÊTRE alors que le texte est centré
   sur le conteneur — deux rythmes différents, si bien qu'une valeur choisie pour
   dégager le texte à une largeur le laissait toucher à une autre. Épinglée par
   son arête DROITE à la mesure du chapeau (46rem, la plus large des deux — le
   titre est borné à 34ch), la garde est constante à toute largeur, et la forme
   sort par la gauche d'elle-même quand la fenêtre se resserre. C'est le
   dispositif de partials/formesDecor.njk, appliqué au texte au lieu du
   conteneur.

   ⚠️ `left: auto` EST OBLIGATOIRE, PAS UNE PRÉCAUTION. Avec une largeur définie
   et les deux arêtes contraintes, la spécification déclare `right` surcontraint
   et l'IGNORE : la forme resterait où elle est, sans la moindre alerte.

   ⚠️ LA GARDE EST UN POURCENTAGE ET NON UNE COTE FIXE, À CAUSE DE LA ROTATION.
   Les -8° élargissent l'emprise horizontale de `hauteur × sin 8°`, soit environ
   6,5 % de la largeur de la forme de chaque côté — et la forme fait 54,3 % de la
   FENÊTRE, donc ce surplus grandit avec elle. Une garde en `rem` se fait manger :
   mesurée, 5rem laissait 33px de jeu à 1024 et plus que 3 à 1920, c'est-à-dire
   qu'elle touchait le texte exactement là où l'écran est le plus large. En
   pourcentage de la même référence, il reste 0,025 × la largeur de fenêtre, soit
   26px à 1024 et 48 à 1920. C'est la seule cote à baisser si la forme part trop
   loin. */
.coeur__forme--haut-gauche {
  --shape-w: 54.3%;
  --shape-rot: -8deg;
  top: 4.6%;
  left: auto;
  right: calc(50% + 23rem + 6%);
}

.coeur__forme--droite {
  --shape-w: 36.8%;
  --shape-rot: 14deg;
  top: 38.2%;
  left: 72.6%;
}

.coeur__forme--bas-gauche {
  --shape-w: 64.6%;
  --shape-rot: 6deg;
  top: 57.1%;
  left: -21.8%;
}

/* La trame emplit la forme d'un bord à l'autre : pas de rampe de densité.

   Rien à regénérer pour autant. halftone-fond.svg est DÉJÀ de densité
   uniforme et pavable — un choix explicite du générateur, pour que le grain
   reste identique quelle que soit la taille de la forme et que l'orientation
   de la montée se règle en CSS (voir `buildHalftoneFond` dans
   eleventy.config.js). La rampe venait donc entièrement de _components.scss,
   en seconde couche de masque. On ne garde que l'image.

   Ce que ça change à l'œil : la forme ne naît plus du fond, elle y est
   posée. Son contour est net, dessiné par le masque de forme et non plus par
   l'extinction de la trame. Tranché le 14/08/2026. */
.coeur .forme-trame__grain {
  /* 504px : la taille du pavé, fixée par `cote` dans le générateur. En dur
     ici comme dans _components.scss — c'est la seule valeur des deux
     fichiers qui doive s'accorder, et elle ne bouge jamais. */
  mask-image: url("/assets/img/halftone-fond.svg");
  mask-size: 504px 504px;
  mask-repeat: repeat;
  /* ⚠️ `intersect` vient de la règle de _components.scss, qui compose deux
     couches. Avec une seule, il ne reste rien à intersecter et le masque
     s'annule : la remise à `add` n'est pas cosmétique. */
  mask-composite: add;
}

/* ---------- Panneau ---------- */
/* Depuis le 27/08/2026 le panneau n'est plus qu'un CONTENEUR DE LARGEUR : il
   garde `--container-max` et son centrage, et c'est la cellule de gauche qui a
   repris son aplat et son filet (voir `.coeur__aparte`). Le panneau ne disparaît
   donc pas, il se resserre — même objet, mêmes coins vifs, il n'enferme plus que
   le sommaire.

   Le `z-index` reste : il place le contenu au-dessus du décor de formes, qu'aucun
   aplat ne recouvre plus. */
.coeur__panneau {
  position: relative;
  z-index: 1;
  width: 100%;
  /* LA MESURE DU `.container` DU SITE, gouttière comprise (16/09/2026). Elle
     était écrite `max-width: var(--container-max)` sans rembourrage, ce qui
     donne bien la même largeur utile que `.container` une fois la fenêtre assez
     large — mais pas en dessous : entre `lg` et ~1170px, `.container` se
     resserre de sa gouttière alors que le panneau, lui, touchait le bord de la
     fenêtre. Les deux colonnes de « Biron : ni un logiciel… » tombant maintenant
     sur celles d'ici, cet écart-là se serait vu comme un défaut d'alignement sur
     toute cette bande. Le rendu au-delà ne bouge pas d'un pixel : le contenu
     vaut `--container-max` de part et d'autre.

     Sous `lg` le panneau reprend ses 5 % de gouttière et remet ce rembourrage à
     zéro — voir la règle dans le bloc `mq-down` en bas de fichier. */
  max-width: calc(var(--container-max) + var(--container-pad) * 2);
  padding-inline: var(--container-pad);
  margin-inline: auto;
}

/* ---------- En-tête : le titre et son chapeau ---------- */
/* L'en-tête est sorti du cadre et centré. Le rembourrage horizontal tombe à
   zéro — les jetons `--pad-panneau-entete-*` servaient à décoller le texte du
   filet du panneau, qui n'existe plus ici. Le bas garde le sien, augmenté :
   c'est lui qui sépare l'en-tête des deux colonnes, maintenant que le filet ne
   le fait plus. */
.coeur__entete {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-4);
  padding-block: var(--pad-panneau-entete-haut) clamp(var(--sp-10), 4vw, var(--sp-16));
  text-align: center;
}

/* Une mesure sur le titre, jamais un corps (règle 3 de MAQUETTE.md) : centré et
   libre, il court sur toute la largeur du conteneur et son retour à la ligne
   tombe n'importe où. */
.coeur__titre {
  margin: 0;
  max-width: 34ch;
  color: var(--c-blue-zodiac);
}

/* La mesure est bornée à 46rem alors que la bande en fait plus de 80 : une
   ligne de texte courant qui court sur toute la largeur d'un panneau se lit
   mal, l'œil perd le retour à la ligne. */
.coeur__chapeau {
  margin: 0 auto;
  max-width: 46rem;
  font-size: var(--fs-base);
  line-height: var(--lh-normal);
  color: var(--text-strong);
}

/* ---------- Le ton des piliers ----------
   LES QUATRE PILIERS NE SONT NOMMÉS QU'ICI, et ces classes portent DEUX tons :

     --pastille-ton         la teinte VIVE — la forme du sommaire et celle du
                            badge ;
     --pastille-ton-sombre  la valeur SOMBRE — l'aplat de la carte, et l'encre
                            du titre de carte quand il n'est pas sur cet aplat.

   La classe est posée sur l'`<article>` d'un bloc ET sur l'entrée de sommaire qui
   lui répond : c'est ce qui garantit que les deux colonnes parlent la même
   couleur sans la redire nulle part. Sur l'article, les deux tons descendent
   jusqu'au titre de la carte et au motif, qui n'ont donc rien à déclarer. */
.coeur__pastille--dictionnaire {
  --pastille-ton: var(--c-pilier-dictionnaire);
  --pastille-ton-sombre: var(--c-pilier-dictionnaire-sombre);
}

.coeur__pastille--plateforme {
  --pastille-ton: var(--c-pilier-plateforme);
  --pastille-ton-sombre: var(--c-pilier-plateforme-sombre);
}

.coeur__pastille--connecteur {
  --pastille-ton: var(--c-pilier-connecteur);
  --pastille-ton-sombre: var(--c-pilier-connecteur-sombre);
}

.coeur__pastille--integrations {
  --pastille-ton: var(--c-pilier-integrations);
  --pastille-ton-sombre: var(--c-pilier-integrations-sombre);
}

/* ---------- La colonne de gauche : le sommaire ----------
   Le rembourrage horizontal est de 2,75rem (44px) là où la maquette en donne
   7,0625rem (113px). À 113px le sommaire flottait au milieu d'une colonne de
   504px sans jamais s'accrocher à rien ; le rail a besoin d'un bord auquel
   se tenir. */
.coeur__suite {
  display: grid;
  grid-template-columns: var(--colonnes-piliers);
  gap: var(--coeur-ecart-cartes);
}

/* La cellule de gauche a repris le panneau : même aplat, même filet, mêmes coins
   vifs. Les coins ne passent pas par `--radius-cadre` remis à zéro : le jeton
   habille AUSSI les cadres des visuels (`.apercu`), qui gardent les leurs. */
.coeur__aparte {
  padding: var(--coeur-pad-module) clamp(var(--sp-5), 2.6vw, 2.75rem);
  background-color: var(--c-white);
  border: var(--filet-panneau);
  border-radius: 0;
}

.coeur__aparte-contenu {
  position: sticky;
  top: var(--coeur-colle);
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--sp-8);
}

/* Le titre du sommaire est un vrai titre : police de display, corps de h3.
   Il assume de dominer les entrées à 20px au lieu de les annoncer en
   sourdine — un eyebrow en chasse fixe a été essayé, il s'effaçait devant sa
   propre liste.

   Il est aligné sur le RAIL et non sur les libellés : le titre coiffe la
   liste entière, rail compris, et un titre en retrait de sa propre barre
   donnait l'impression que le rail n'appartenait pas au bloc. */
.coeur__sommaire-titre {
  margin: 0 0 var(--sp-8) 0;
  font-family: var(--font-titre);
  font-size: var(--fs-h3);
  font-weight: 600;
  line-height: var(--lh-tight);
  color: var(--c-blue-zodiac);
}

/* ---------- Le rail de progression ----------
   Deux traits superposés sur le même axe : celui du dessous, pointillé, dit
   ce qui reste à lire ; celui du dessus, plein, ce qui l'est déjà. C'est le
   second qui grandit, sa hauteur venant de `--progression` (0 → 1), posé par
   coeur.js au fil du défilement.

   En continu et non par cran : un rail qui saute d'une entrée à l'autre ne
   dit rien de plus que le point actif, alors qu'un rail qui avance dit AUSSI
   où l'on en est à l'intérieur d'un bloc. C'est là tout son intérêt.

   Sans le script, `--progression` vaut 0 : le rail reste entièrement
   pointillé et ne ment pas. */
.coeur__sommaire {
  --rail-gouttiere: 1.625rem;
  --rail-epaisseur: 2px;
  position: relative;
  display: flex;
  flex-direction: column;
  padding-left: var(--rail-gouttiere);
}

.coeur__sommaire::before,
.coeur__sommaire::after {
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  width: var(--rail-epaisseur);
}

/* LES DEUX TRAITS SONT AU FLUO depuis le 27/08/2026, donc ils n'ont plus que
   leur CONTINUITÉ pour se distinguer : la couleur ne les sépare plus.

   ⚠️ Le fluo ne pèse que 1,16 sur du blanc. Sur 2px d'épaisseur, ce qui lui
   manque est de la SURFACE et non une autre teinte — même règle que « la trame
   demande de la surface » de partials/motif.njk. `--rail-epaisseur` est la seule
   cote à toucher si le rail doit se voir davantage.

   Le pointillé est un dégradé répété et non une `border-left: dashed` : une
   bordure en pointillé ne laisse pas régler la longueur de ses tirets, qui
   varie alors d'un moteur à l'autre. C'est aussi pourquoi sa couleur se change
   dans `background-image` et non dans `background-color`, qu'il recouvrirait
   entièrement. */
.coeur__sommaire::before {
  bottom: 0;
  background-image: linear-gradient(var(--c-fluo) 50%, transparent 50%);
  background-size: var(--rail-epaisseur) 0.5rem;
}

.coeur__sommaire::after {
  height: calc(var(--progression, 0) * 100%);
  background-color: var(--c-fluo);
}

/* ---------- Les entrées du sommaire ----------
   POLICE DE DISPLAY depuis le 27/08/2026. Elles ont longtemps été en Chivo Mono,
   « parce qu'un sommaire est un index, pas du texte courant » ; le passage au
   display a été demandé en même temps que celui des titres de carte, si bien que
   les deux colonnes continuent de se répondre au lieu de parler chacune sa
   langue.

   Le corps ne bouge pas. Le plafond de 20px avait été calculé sur une chasse
   FIXE, large : « Le dictionnaire de données » y faisait 26 signes pour environ
   312px, plus la forme, l'écart et la gouttière du rail — 360px pour 416px
   utiles dans la colonne. En display la même chaîne est nettement plus courte,
   donc la contrainte se desserre ; le monter ramènerait le risque de casse sur
   deux lignes, qui désaligne le rail et fait changer de taille la forme active
   au défilement. */
.coeur__sommaire-lien {
  display: flex;
  align-items: center;
  gap: 0.8125rem;
  padding-block: 0.8125rem;
  font-family: var(--font-titre);
  font-size: clamp(0.9375rem, 1.4vw, 1.25rem); /* 15 → 20px */
  line-height: 1.35;
  color: color-mix(in srgb, var(--text-strong) 60%, transparent);
  text-decoration: none;
  transition: color var(--dur-base) var(--ease-out);
}

.coeur__sommaire-lien:hover {
  color: var(--text-strong);
}

/* Pas de pavé teinté sous l'entrée active : le rail et la forme disent déjà
   la position, un troisième signal pour la même information rendait la
   colonne bavarde. Il ne reste que le contraste du libellé. */
.coeur__sommaire-lien[aria-current=true] {
  color: var(--c-blue-zodiac);
  font-weight: 500;
}

/* ---------- La forme d'une entrée ----------
   Le rond a laissé place à une FORME DE MARQUE le 27/08/2026. Elle est rendue par
   la macro `forme()` de partials/forme.njk, donc son rapport et son masque
   viennent de la table de l'atome : rien n'est recopié ici, et il n'y a que la
   taille et la couleur à poser.

   ⚠️ `--shape-color` GAGNE CONTRE `.forme--fluo` PAR L'ORDRE DES FEUILLES, PAS
   PAR LA SPÉCIFICITÉ : les deux sélecteurs valent (0,1,0), et c'est parce que
   _components.scss est émis avant les sections que cette règle-ci l'emporte.
   C'est le même appui que `.item__forme`, documenté dans CLAUDE.md.

   La cote monte de 0,75 à 1,125rem : un rond de 12px se lit, une silhouette de
   12px n'est plus qu'une tache. */
.coeur__sommaire-point {
  --shape-color: var(--pastille-ton);
  --shape-w: 1.125rem;
  flex: none;
  transition: scale var(--dur-base) var(--ease-out);
}

/* ⚠️ LE HALO DE L'ENTRÉE ACTIVE NE PEUT PAS SURVIVRE AU MASQUE, et aucun réglage
   ne le sauve. Un `box-shadow` est peint hors de la boîte du bord, que le masque
   découpe. Un `filter: drop-shadow()` non plus : l'ordre de rendu de CSS applique
   le filtre PUIS le masque, donc la lueur est rognée elle aussi. La seule voie du
   site est une seconde copie plus grande derrière la forme
   (`.chaine__forme-contour`), et elle demande du balisage.

   Il ne reste donc que l'agrandissement, et il suffit : le rail et le contraste
   du libellé portent déjà la position.

   `scale` et non `transform` : ce sont deux propriétés distinctes qui se
   COMPOSENT, donc l'agrandissement s'ajoute à la rotation que `.forme` pose en
   `transform` au lieu de l'effacer. C'est la règle du site pour tout déplacement
   sur une forme. */
.coeur__sommaire-lien[aria-current=true] .coeur__sommaire-point {
  scale: 1.25;
}

.coeur__sommaire-label {
  min-width: 0;
}

/* ---------- Le cartouche de nouveauté ----------
   « New », sur l'entrée de sommaire ET sur la ligne de titre de la carte. Seul
   le connecteur IA en porte un, ici comme dans le menu de l'en-tête et sur
   /produit/.

   ⚠️ DEUX FONDS, DONC DEUX RECETTES, ET ELLES NE SONT PAS INTERCHANGEABLES.
   C'est la règle 10 de MAQUETTE.md appliquée à un même objet posé deux fois :
   ce qui se mesure n'est pas la pastille, c'est le contraste qu'elle tient
   contre le fond qui la reçoit.

     dans le sommaire  fond BLANC (`.coeur__aparte`) — pastille CERCLÉE ET
                       CREUSE : bord à la teinte vive, encre à la valeur sombre,
                       aucun aplat. « Tout en nuances de violet, sur nul fond »
                       (Hugo, 16/09/2026). C'est aussi la forme canonique de la
                       règle 16 de MAQUETTE.md — une pastille teintée d'un pilier
                       est cerclée et non remplie.

                       ⚠️ ELLE S'ÉCARTE DONC DEUX FOIS DE SA SOURCE, le cartouche
                       du menu de l'en-tête (`.site-nav__cartouche`), qui est
                       rempli à 15 % et porte l'encre courante. L'encre est
                       mesurée : la valeur sombre du connecteur tient 5,47 sur le
                       blanc du panneau, quand la teinte VIVE n'y tiendrait que
                       3,45 — sous le seuil qu'un libellé de cette taille demande.

                       ⚠️ ET CETTE MESURE NE VAUT QUE POUR LE CONNECTEUR, seul
                       pilier à porter un cartouche aujourd'hui. Sur blanc les
                       trois autres donnent 5,58 (dictionnaire), 5,66
                       (intégrations) et 4,85 (plateforme) : tous passent, mais un
                       second pilier badgé se remesure, il ne s'ajoute pas.
     sur la carte      fond SOMBRE au ton du pilier — la recette de
                       `.texte-panneau__badge` : aplat blanc, libellé à la
                       valeur sombre du pilier, qui y tient de 4,85 à 5,66. La
                       recette du sommaire y disparaîtrait : la teinte vive du
                       connecteur ne vaut que 1,32 sur son propre aplat sombre,
                       autant pour le cerclage que pour la dilution.

   Le corps est `--fs-xs` et non `--fs-mono` : à la taille des eyebrows il se
   lisait comme une mention technique et non comme une annonce — c'est le même
   arbitrage que `.texte-panneau__badge`. `nowrap` parce que « New » replié sur
   deux lignes ne veut plus rien dire, et `flex: none` pour qu'un intitulé long
   ne le comprime pas. */
.coeur__cartouche {
  flex: none;
  padding: 0.0625rem var(--sp-2);
  border: 1px solid var(--pastille-ton);
  border-radius: var(--radius-full);
  background-color: transparent;
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--pastille-ton-sombre);
}

/* Sur la carte, l'inverse du FOND : aplat blanc, et pas de bord — le blanc
   détache déjà franchement la pastille de l'aplat sombre. L'encre, elle, ne
   change pas : c'est la même valeur sombre du pilier, qui tient 5,47 sur blanc.
   Rien à redire ici, donc — une seule déclaration pour les deux emplois. */
.coeur__cartouche--carte {
  padding: 0.25rem var(--sp-3);
  border: 0;
  background-color: var(--c-white);
}

/* ---------- La colonne de droite : quatre cartes de même rang ---------- */
/* La colonne a cessé d'être une colonne de tableau le 27/08/2026 : plus d'aplat,
   plus de filet vertical, et les séparateurs internes ont laissé la place à la
   gouttière. C'est ce qui détache les cartes les unes des autres. */
.coeur__modules {
  display: flex;
  flex-direction: column;
  gap: var(--coeur-ecart-cartes);
}

/* La carte d'un outil : l'aplat SOMBRE de son pilier, 20px de rayon — le rayon
   des CARTES du site (`cas-clients`, `grille-articles`), et non les 28px réservés
   aux boîtes qui occupent toute une section. Chacune porte son visuel et sa
   couleur : c'est la quatrième branche de la règle 13 de MAQUETTE.md, une cellule
   qui devient une carte.

   Les quatre aplats sont ceux des briques de « La solution complète », par les
   mêmes jetons : le blanc y tient de 4,85 (plateforme) à 5,66 (intégrations). Les
   encres sont réaffectées ici plutôt que par `.on-dark`, comme le fait
   `texte-panneau` pour ses quatre boîtes — c'est le dispositif établi pour ces
   aplats-là. `--text-strong` suffit : c'est la seule encre que la carte emploie,
   son titre portant la sienne juste en dessous.

   ⚠️ L'ÉCART TEXTE / ILLUSTRATION PORTE UNE EXCEPTION, ET ELLE EST DANS LES
   FICHIERS. Les cinq exports sont détourés, donc chacun porte SA marge
   transparente au-dessus de son encre — mesurées au canvas le 27/08/2026, celle
   des intégrations vaut 104px là où les trois autres commencent à 0. La marge
   qui manquait aux autres, ce fichier-là l'avait déjà : il garde donc l'ancienne
   cote. Le CSS ne voit pas où commence l'encre d'une image, et recadrer un export
   livré est une autre affaire. */
.coeur__module {
  --text-strong: var(--c-white);
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--coeur-texte-visuel);
  padding: var(--coeur-pad-module);
  background-color: var(--pastille-ton-sombre);
  border-radius: 1.25rem;
  /* L'ancre de défilement : sans elle, un clic amène le haut du bloc juste
     sous l'en-tête collant, au ras. Elle sert aussi de repli si le script ne
     tourne pas — le saut natif la respecte, et coeur.js la LIT plutôt que de
     redéfinir sa propre constante, pour que les deux trajets arrivent au
     même endroit. */
  scroll-margin-top: var(--coeur-colle);
}

.coeur__module-tete {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--sp-3);
}

/* LE TITRE DE LA CARTE. C'était un libellé en chasse fixe précédé d'un point, dans
   la grammaire du sommaire ; c'est devenu un petit titre en police de display,
   sans puce, le 27/08/2026 — en même temps que le passage du sommaire à la même
   police, si bien que les deux colonnes se répondent toujours.

   ⚠️ LE CORPS EST `--fs-h3`, ET LE CHEMIN POUR Y ARRIVER EST INSTRUCTIF. Une
   première version avait pris la cote de `.cas--carte .cas__titre` (20 → 22px),
   celle des cartes du site — « les titres ne sont pas assez mis en valeur ». Or
   cette cote-là est un PLAFOND : elle existe pour rabattre l'échelle du site dans
   une carte de grille étroite, à trois par rangée. Ici la carte fait toute la
   colonne et ne porte qu'un titre et une phrase, donc rien ne justifiait de
   rabattre. Le titre reprend le jeton du niveau, et l'écart à la règle 3 de
   MAQUETTE.md disparaît avec la cote écrite à la main : ne reste que ce qu'un
   `<p>` ne reçoit pas de _base.scss — la famille, la graisse, l'interligne.

   1,2 d'interligne et non `--lh-tight` (1,08) : ce dernier est calé pour un titre
   de 72px, il écrase un 32px sur deux lignes. Même arbitrage que `.corps__titre`
   des articles.

   Le blanc et non le ton du pilier : sur l'aplat sombre de sa propre carte, la
   teinte vive tomberait de 1,32 (connecteur) à 2,67 (intégrations). Le blanc y
   tient de 4,85 à 5,66. */
/* La rangée du titre de carte : l'intitulé et, le cas échéant, son cartouche.

   ⚠️ ELLE NE S'APPELLE PAS `.coeur__entete`, ET C'EST UNE LEÇON PAYÉE. Ce nom-là
   est DÉJÀ celui de l'en-tête de section, plus haut dans ce fichier — une pile
   centrée avec son propre `flex-direction: column` et un rembourrage bas de 4vw.
   Posé sur cette rangée, il ne redéfinissait que ce que la nouvelle règle
   écrivait : la direction restait en colonne (le cartouche tombait SOUS le
   titre) et le rembourrage creusait un vide entre le titre et sa description.
   Rien ne le signalait. C'est la règle du site : avant de poser une classe, on
   grep le sélecteur nu.

   ⚠️ `center` ET NON `baseline`, ET LE CALCUL EXPLIQUE POURQUOI. `baseline` est
   l'alignement de `.item__entete`, et il a été essayé ici d'abord ; il pose la
   ligne de base du libellé sur celle du titre, ce qui n'aligne les deux objets
   que si leurs corps sont voisins. Ils ne le sont pas : le titre fait jusqu'à
   32px et la pastille 12px, plus son rembourrage. La pastille se retrouve alors
   centrée 3px SOUS la ligne de base quand le centre optique du titre est 11px
   AU-DESSUS — huit pixels d'écart, et elle pend visiblement.

   Centrée sur la boîte de ligne du titre, elle tombe à 2px de ce centre optique.
   C'est l'arbitrage de `.texte-panneau__badge`, sur exactement ce couple-là
   (un titre de section et son cartouche).

   ⚠️ IL NE VAUT QUE TANT QUE LE TITRE TIENT SUR UNE LIGNE — sur un intitulé
   replié, `center` centrerait la pastille sur le pavé entier, à mi-hauteur. Les
   quatre titres de carte sont des syntagmes de deux ou trois mots (« Le
   connecteur IA », seize signes) et la carte fait toute la largeur de sa colonne
   en mobile, donc aucun ne se replie ; c'est le même avertissement que
   `texte-panneau`, qui repasse en `flex-start` là où le sien se replie. */
.coeur__etiquette-rangee {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
}

.coeur__etiquette {
  margin: 0;
  font-family: var(--font-titre);
  font-size: var(--fs-h3); /* 22 → 32px */
  font-weight: 600;
  line-height: 1.2;
  color: var(--c-white);
}

.coeur__module-texte {
  margin: 0;
  max-width: 22rem;
  font-size: var(--fs-base);
  line-height: var(--lh-normal);
  color: var(--text-strong);
}

/* ---------- La bande de transition ----------
   Elle dit le passage du socle à ce qu'il porte, d'où sa place après le seul
   dictionnaire. 173px de haut dans la maquette pour un texte de 380px, soit
   une bande qui respire nettement plus que les blocs : c'est ce qui la fait
   lire comme un palier et non comme un cinquième bloc.

   LE TEXTE EST CENTRÉ depuis le 27/08/2026, ce qui RÉTABLIT la maquette. Il avait
   été ferré à gauche parce que, centré, il était le seul bloc de la colonne à ne
   pas partir du même bord que les quatre autres. Cet argument est tombé avec la
   recomposition : la colonne n'est plus un tableau à cinq rangées mais quatre
   cartes détachées, et la bande n'est plus une rangée parmi elles — c'est une
   phrase POSÉE ENTRE deux cartes, sur le fond de la page, qui n'a donc plus de
   bord commun à tenir.

   Elle perd aussi son rembourrage vertical pour une bonne part, la gouttière des
   cartes en faisant déjà la moitié. C'est la BOÎTE qui se centre, pas seulement le
   texte : les deux vont ensemble, `text-align` seul laisserait le paragraphe
   centré dans une boîte ferrée à gauche.

   ---------------------------------------------------------------------------
   C'EST UNE QUOTE DEPUIS LE 16/09/2026 — demandé par Hugo, « on peut mettre plus
   en valeur ce texte avec les outils à notre disposition et ce qu'on a déjà
   fait ». D'où le fait que rien n'est inventé ici : c'est l'objet déjà en place
   sur la section 02 de /a-propos/ (`.intertitre--centre.intertitre--quote`) et
   sur l'`exergue` de `texte-module`, et les valeurs en sont reprises.

   ⚠️ C'EST LA BARRE QUI DÉTACHE LA PHRASE, pas sa taille : à 4px et non 1px,
   parce que ce n'est pas un filet de séparation mais une attache, et qu'à 1px sur
   le crème elle disparaîtrait à côté d'un corps de 32px.

   ⚠️ L'ENCRE EST CELLE DES TITRES (`--c-blue-zodiac`) ET NON LA TEINTE D'ACCENT.
   Tant que la phrase portait seule sa mise en valeur, l'accent était juste ;
   maintenant que la barre la porte, la phrase est du TITRAGE — et les titres du
   site restent bleu nuit.

   ⚠️ LE TEXTE RESTE CENTRÉ SOUS SA BARRE, ET C'EST VOULU. Le commentaire de
   l'`exergue` écarte ce couple — une barre devant un texte centré ne touche plus
   son premier caractère et redevient un ornement. Hugo a tranché l'inverse le
   20/08/2026 sur /a-propos/ (« c'est une quote, c'est bien de le garder
   centré ») : la barre y devient un repère de gauche plutôt qu'une attache, et
   c'est cette lecture-là qui vaut ici, où la phrase est un palier posé au milieu
   d'une colonne. NE PAS « CORRIGER » VERS LE FER À GAUCHE.

   ⚠️ ET LA MESURE BRIDÉE EST STRUCTURELLE, pas cosmétique : sans elle la boîte
   prend toute la colonne, le centrage ne se voit plus et la barre se retrouve au
   bord du panneau. Voir `--coeur-bande-mesure`. */
.coeur__bande {
  padding-block: clamp(var(--sp-2), 1vw, var(--sp-6));
}

.coeur__bande-texte {
  margin-inline: auto;
  max-width: var(--coeur-bande-mesure);
  /* La barre, et la gouttière qui l'écarte du texte. Cotes de
     `.intertitre--quote`. */
  border-inline-start: 4px solid var(--c-fluo);
  padding-inline-start: clamp(var(--sp-4), 1.6vw, var(--sp-6));
  text-align: center;
  font-family: var(--font-titre);
  font-size: var(--fs-h3);
  font-weight: var(--fw-bold);
  /* `--lh-snug` et non `--lh-tight` : sur plusieurs lignes de 32px, 1.08 colle
     les jambages d'une ligne aux capitales de la suivante. Les titres du site
     peuvent se resserrer parce qu'ils tiennent souvent sur une ligne. */
  line-height: var(--lh-snug);
  letter-spacing: var(--ls-display);
  color: var(--c-blue-zodiac);
  text-wrap: balance;
}

/* ---------- Visuels ---------- */
/* ⚠️ `static` ET C'EST DÉLIBÉRÉ. Le `position: relative` d'origine n'avait qu'un
   rôle : servir de référence au badge de pilier, qui était posé au coin de
   l'IMAGE. Depuis le 27/08/2026 le badge est au coin de la CARTE ; rendu statique,
   le visuel cesse d'être cette référence et le badge remonte au premier ancêtre
   positionné, la carte. Rien n'a eu à être reparenté.

   ⚠️ Et le visuel ne porte PAS de `z-index`, contrairement à la tête : à valeur
   égale c'est le nœud le plus tardif du DOM qui gagne, et la tête, qui vient
   après, recouvrirait le badge. */
.coeur__visuel {
  position: static;
  /* Le retrait qui détache l'image du bord du titre — voir
     `--coeur-visuel-retrait`. En REMBOURRAGE et non en marge : c'est cette boîte
     qui donne aux quatre illustrations leur largeur commune, chacune valant
     simplement 100 % d'elle. */
  padding-inline: var(--coeur-visuel-retrait);
}

/* Gabarit dans `.apercu` (_components.scss) — c'est de ce visuel-ci que vient
   sa mise en page en grille à une colonne. Le ratio est posé en ligne, il
   change d'un visuel à l'autre.

   La maquette donne 80px de flou et 10 d'étalement : un halo très large, qui
   fait flotter le visuel bien au-delà de son cadre. Ramené à 48 et 4 — il
   décolle encore l'image du blanc sans déborder sur le filet du bloc. */
.coeur__placeholder {
  box-shadow: 0 3px 48px 4px rgb(var(--shadow-tint)/0.38);
}

/* L'illustration définitive, quand la donnée en déclare une. Aucun cadre, aucun
   filet et SURTOUT PAS LE HALO du cadre ci-dessus : les quatre exports sont
   détourés, un `box-shadow` dessinerait donc un rectangle d'ombre autour d'une
   forme dont les coins sont vides. Elles se posent à même l'aplat de la carte.

   ---------------------------------------------------------------------------
   DEUX RÉGLAGES PAR IMAGE, tous deux mesurés et non choisis à l'œil.

   ⚠️ LES DÉFAUTS SONT DANS LES `var()`, JAMAIS DÉCLARÉS SUR L'IMAGE : une
   propriété posée SUR l'élément bat la même propriété héritée d'un ancêtre, et
   les réglages posés par carte n'auraient aucun effet.

   ① LA LARGEUR EST LA MÊME POUR LES QUATRE depuis le 16/09/2026, et c'est demandé
     (Hugo : « je veux bien que tu agrandisses les images de telle sorte à ce
     qu'elles fassent la même largeur […] la largeur de l'illustration de la carte
     "Les intégrations" est la cible »). Elles occupent donc toute la boîte du
     visuel, et les deux réductions par pilier — plateforme 70 %, connecteur 82 %
     — sont tombées avec la variable qui les portait.

     ⚠️ CE QU'ON ABANDONNE EN ÉCHANGE, ET C'EST ASSUMÉ : ces deux réductions
     servaient à ÉGALISER LES HAUTEURS RENDUES, les quatre rapports étant très
     différents. À largeur commune (489px de boîte à 1440) les hauteurs repartent
     de 376 (dictionnaire, 1,302) à 560 (plateforme, seul export en PORTRAIT à
     0,874), en passant par 444 (intégrations) et 472 (connecteur). Les cartes
     n'ont donc plus la même hauteur, et les images les plus hautes descendent
     nettement plus bas — « c'est pas grave qu'elles dépassent en dessous grâce au
     dégradé ».

     ⚠️ C'EST BIEN LA LARGEUR QU'ON RÈGLE, ET SÛREMENT PAS `max-block-size`. Les
     attributs `width`/`height` de l'image — lus dans le fichier par
     lib/image.js — sont une largeur SPÉCIFIÉE : un plafond de hauteur rabattrait
     la seule hauteur et étirerait l'image, et le `inline-size: auto` qui
     rétablirait le rapport annulerait du même coup la réservation de place, donc
     l'image n'aurait plus aucune taille avant son chargement. Leçon des
     illustrations d'`etapes`, voir « Images » dans CLAUDE.md. C'est aussi pourquoi
     un `object-fit: cover` dans une zone de hauteur fixe n'est pas la sortie :
     au-delà de 376px de zone il rognerait le dictionnaire SUR LES CÔTÉS, son
     rapport étant le plus large des quatre.

   ② LE RECENTRAGE A ÉTÉ RETIRÉ LE 16/09/2026, ET C'EST UNE CORRECTION. Deux des
     exports portaient une colonne transparente à droite — connecteur 60px sur
     1046, intégrations 120 sur 1237 — que le CSS ne voit pas : leur boîte était
     centrée, leur encre non. Un `translate` en rattrapait la moitié.

     ⚠️ LES DEUX EXPORTS ONT DEPUIS ÉTÉ RECADRÉS et le rattrapage était resté.
     Boîtes d'encre remesurées au canvas (seuil alpha 8) le 16/09/2026 —

       dictionnaire  677 × 520    encre x 23 → 652, y 0 → 495  (le seul détouré)
       plateforme    900 × 1030   encre pleine boîte
       connecteur    986 × 952    encre pleine boîte   (était 1046 de large)
       intégrations  1117 × 1015  encre pleine boîte   (était 1237 × 1149)

     Le `translate` décentrait donc de 30 et 60px des images déjà centrées, et
     l'écart texte/visuel des intégrations compensait un vide de 104px au-dessus
     de l'encre qui n'existe plus. Les trois règles sont tombées ensemble.

     ⚠️ LEÇON À GARDER : UNE COMPENSATION MESURÉE SUR UN FICHIER DOIT MOURIR AVEC
     LUI. Rien ne signale un rattrapage devenu faux, et son symptôme — une image
     qui pend d'un côté — est exactement celui qu'il prétend corriger.

     Son retrait sert aussi l'empilement : un `translate` autre que `none`, `0 0`
     compris, fait de l'image un contexte d'empilement peint AVEC LES ÉLÉMENTS
     POSITIONNÉS, donc au-dessus du texte en flux qui la suit. Depuis que le titre
     recouvre le bas de l'illustration, c'était le défaut prêt à partir — le
     `z-index` posé plus bas sur `.coeur__module-tete` en est la deuxième garde. */
.coeur__illustration {
  display: block;
  width: 100%;
  height: auto;
}

/* ---------- Le fondu du bas, et le texte qui vient s'y poser ----------
   Figma 471:2739. La capture s'éteint vers l'aplat de la carte plutôt que de
   s'arrêter sur un bord franc, et le titre se pose sur la queue transparente.

   ⚠️ LE DICTIONNAIRE EN EST EXCLU, ET C'EST DEMANDÉ (Hugo, 16/09/2026) : son
   texte reste sous l'image, sans fondu ni recouvrement. C'est aussi la seule des
   quatre illustrations qui soit détourée — 24px de vide sous son encre — donc la
   seule dont le bas ne présente pas de bord franc à éteindre.

   UN MASQUE ET NON UN DÉGRADÉ POSÉ PAR-DESSUS : l'aplat de la carte se voit alors
   par transparence, sans qu'aucune règle n'ait à redire sa couleur — un calque
   opaque, lui, serait à tenir d'accord avec `--pastille-ton-sombre`. La paire
   `-webkit-` reste indispensable, Safari ne connaissant encore que celle-là ; le
   mixin `mask()` ne sert pas ici, il est fait pour une image de masque et sa
   taille, pas pour un dégradé. */
.coeur__module:not(.coeur__pastille--dictionnaire) .coeur__illustration {
  -webkit-mask-image: linear-gradient(to bottom, #000 var(--coeur-fondu-debut), transparent var(--coeur-fondu-fin));
  mask-image: linear-gradient(to bottom, #000 var(--coeur-fondu-debut), transparent var(--coeur-fondu-fin));
}

/* La marge est NÉGATIVE DE L'ÉCART PLUS DU RECOUVREMENT : la gouttière flex de la
   carte s'applique de toute façon entre le visuel et le texte, et s'ajoute à la
   marge. L'écrire ainsi rend le résultat lisible — le titre monte exactement de
   `--coeur-recouvrement` au-dessus du bas de l'image.

   ⚠️ GARDÉE PAR `:has()` : sans illustration, c'est le cadre en attente qui prend
   la place, avec son bord franc et son halo, et le titre lui passerait dessus. */
.coeur__module:not(.coeur__pastille--dictionnaire):has(.coeur__illustration) .coeur__module-tete {
  margin-block-start: calc(-1 * (var(--coeur-texte-visuel) + var(--coeur-recouvrement)));
}

/* ---------- Le badge de pilier, à cheval sur le coin de la CARTE ----------
   Il était posé sur l'angle du VISUEL ; il est passé au coin de la carte le
   27/08/2026, où il ne recouvre plus l'illustration. Ses deux retraits sont le
   débord en négatif, si bien qu'il sort du même montant vers le haut et vers la
   droite, et l'arrondi de la carte passe sous lui.

   ⚠️ LE TITRE DOIT RÉSERVER LA PLACE, un élément absolu n'en prend aucune. C'est
   la leçon de `.item__logo` : sans réserve, le texte passe SOUS le badge à mesure
   que la carte se resserre. La réserve est CALCULÉE et non écrite — l'arête
   gauche du badge est à `badge − débord` du bord de la carte, le texte s'arrête à
   `--coeur-pad-module`, donc ce qui empiète vaut `badge − débord − rembourrage`,
   et le `max()` l'annule quand le rembourrage suffit déjà. Il ne reste ainsi que
   9 à 15px de réserve en grand écran : le titre garde presque toute sa mesure. */
.coeur__glyphe {
  position: absolute;
  top: calc(-1 * var(--coeur-badge-debord));
  right: calc(-1 * var(--coeur-badge-debord));
  width: var(--coeur-badge);
  height: auto;
}

.coeur__module-tete {
  padding-inline-end: max(0px, var(--coeur-badge) - var(--coeur-badge-debord) + var(--sp-4) - var(--coeur-pad-module));
}

/* ⚠️ LE TEXTE PASSE AU-DESSUS DE L'ILLUSTRATION QU'IL RECOUVRE, et c'est écrit
   plutôt que laissé à l'ordre du DOM. En flux normal le texte du titre est bien
   peint après l'image, mais il suffirait d'un `transform`, d'un `translate` ou
   d'un `filter` sur l'image pour qu'elle passe au calque des éléments positionnés
   et repasse dessus — c'est précisément ce que faisait le rattrapage de
   recentrage retiré le 16/09/2026.

   ⚠️ `z-index` SEUL, SANS `position` : la tête est un ÉLÉMENT FLEX de la carte,
   pour lequel `z-index` s'applique quelle que soit sa position. Un
   `position: relative` ici ferait de la tête le bloc conteneur d'un éventuel
   descendant absolu, ce dont elle n'a pas besoin.

   ⚠️ NE PAS LE POSER AUSSI SUR `.coeur__visuel` : le badge de pilier y est
   absolu, et à `z-index` égal c'est le nœud le plus tardif du DOM qui gagne —
   la tête recouvrirait le badge. */
.coeur__module-tete {
  z-index: 1;
}

/* ---------- Le bouton du sommaire ----------
   « Un bouton de la taille du texte, ferré à gauche » : la colonne est en
   `align-items: stretch`, donc `align-self` suffit — le bouton reprend la largeur
   de son libellé et se cale au bord gauche, sur le rail.

   ⚠️ BORNÉ À `lg`, ET C'EST VOULU. En dessous, une règle plus bas lui impose
   `width: 100%` avec sa raison : à sa largeur naturelle, dans une colonne
   étroite, il coupait son libellé. */
@media (min-width: 60rem) {
  .coeur__aparte-contenu .btn {
    align-self: flex-start;
  }
}
/* =========================================================================
   Une seule colonne, panneau centré.

   La maquette ne couvre pas le mobile. Deux partis pris :

   Le sommaire cesse de coller. Sur un écran étroit il n'y a pas de colonne à
   côté de laquelle rester, et il ne surligne plus rien de visible. Il reprend
   sa place dans le flux, en tête de section, où il devient un menu de saut —
   les liens fonctionnent toujours, et le rail garde son sens : il dit où l'on
   en est.

   Le panneau est CENTRÉ, avec une gouttière égale de chaque côté — les
   formes de fond réapparaissent donc des deux bords, alors qu'un panneau
   pleine largeur les masquait entièrement. Les visuels rétrécissent
   d'autant : compromis assumé.
   ========================================================================= */
/* ---------- La plaque crème ----------
   Un aplat au FOND DE PAGE, opaque, sous un texte posé hors carte. Il ne se voit
   pas — crème sur crème, aucun bord ne se dessine — sauf là où une forme de trame
   passe derrière : c'est exactement ce qu'il est là pour rattraper.

   `--bg-page` et surtout pas `--c-base-main` : ce fond DIT le fond de la page,
   c'est le cas de `.clients__frame`, et la règle du dépôt veut qu'on lise le rôle.

   Le rayon est celui des CARTES de la colonne (1,25rem), et pas un troisième :
   une plaque posée entre deux cartes qui porterait son propre arrondi se lirait
   comme un objet d'une autre nature.

   ⚠️ IL N'EST POSÉ QU'EN DESSOUS DE `lg`, et ce n'est pas un oubli. À l'écran
   large les trois formes sont épinglées pour DÉGAGER le texte — celle du haut à
   gauche l'est sur la mesure du chapeau, avec sa garde, précisément pour ça — donc
   la plaque n'y aurait rien à rattraper et ne ferait que recouvrir le décor que la
   recomposition du 27/08/2026 a rendu visible autour de l'en-tête. En dessous, les
   formes passent à 84–96 % de large et traversent le texte : la garde redevient
   nécessaire. */
@media (max-width: 60rem) {
  .coeur {
    /* 5 % de chaque côté, soit 10 % en tout. */
    --coeur-gouttiere: 5%;
    /* Les blocs se resserrent aussi : 20px de côté au lieu de 32. */
    --coeur-pad-module: var(--sp-5);
    /* PLUS D'AIR ENTRE LES CARTES qu'à l'écran large — demandé le 27/08/2026.
       ⚠️ LA COTE EST COUPLÉE AU DÉBORD DU BADGE, et sous un certain seuil
       l'augmenter NE SE VOIT PAS : le débord est plafonné à
       `--coeur-ecart-cartes - 0.5rem` (voir `--coeur-badge-debord`), donc tant
       que ce plafond mord, tout ce qu'on ajoute à la gouttière est repris par le
       badge et l'écart visible reste bloqué à son demi-rem. Le badge cesse de
       grandir à 0,28 × sa taille, soit ~21px sur un mobile de 375 ; il faut donc
       passer 21 + 6,8 ≈ 2,05rem pour que l'écart commence à s'ouvrir. À 3rem il
       vaut ~20px, contre 6,8 avant. C'est la première cote à revoir si les cartes
       se retrouvent trop espacées — jamais le débord, qui n'en est que la
       conséquence. */
    --coeur-ecart-cartes: var(--sp-12);
  }
  /* LES DEUX TEXTES POSÉS HORS CARTE prennent la plaque : l'en-tête et la phrase
     de palier sont les seuls que rien ne recouvre, et les formes de trame leur
     passent dessous à cette largeur-là.

     L'en-tête gagne un rembourrage horizontal, qui était à zéro : il n'avait
     qu'un filet à fuir, et le voilà dans un cadre. Il prend la cote des cartes,
     si bien que la plaque et elles respirent au même rythme. */
  .coeur__entete {
    padding: var(--coeur-pad-module);
    /* Ce que le rembourrage bas faisait — séparer l'en-tête des colonnes — passe
       en MARGE : resté en rembourrage il creuserait un vide au bas de la plaque,
       qui viendrait toucher le panneau du dessous. L'écart à l'œil ne bouge pas. */
    margin-block-end: clamp(var(--sp-10), 4vw, var(--sp-16));
    background-color: var(--bg-page);
    border-radius: 1.25rem;
  }
  .coeur__bande-texte {
    /* Le rembourrage de la plaque reprend celui de la barre sur le bord de
       départ : la gouttière de la quote passe de sa clamp à la cote des cartes,
       ce qui la met au même rythme qu'elles. */
    padding: var(--coeur-pad-module);
    /* La mesure du texte ne bouge pas : le rembourrage s'AJOUTE à la boîte au
       lieu de la rogner, `box-sizing: border-box` obligeant à l'écrire. */
    max-width: calc(var(--coeur-bande-mesure) + 2 * var(--coeur-pad-module));
    background-color: var(--bg-page);
    border-radius: 1.25rem;
    /* ⚠️ LES DEUX COINS DE DÉPART REDEVIENNENT VIFS, ET CE N'EST PAS UN GOÛT :
       un `border-radius` arrondit AUSSI la bordure, si bien que la barre fluo se
       recourbait en crochet dans les deux coins de la plaque au lieu de rester
       droite. La plaque, elle, ne perd rien de visible — elle est du fond de page
       rendu opaque, et son seul office est de masquer les formes de trame qui lui
       passent dessous à cette largeur-là. */
    border-start-start-radius: 0;
    border-end-start-radius: 0;
  }
  .coeur__panneau {
    /* `width: auto` et non 100% : avec une largeur imposée, les marges
       s'AJOUTENT au lieu de se soustraire et le panneau déborde. */
    width: auto;
    max-width: none;
    /* La gouttière de cette largeur-là est la marge de 5 %, pas celle du
       `.container` : les deux s'ajouteraient. */
    padding-inline: 0;
    margin-inline: var(--coeur-gouttiere);
  }
  /* Le panneau étant centré, il y a une gouttière de chaque côté : les
     formes se répartissent gauche / droite / gauche plutôt que de s'entasser
     à gauche. */
  .coeur__forme--haut-gauche {
    --shape-w: 84%;
    top: 2%;
    left: -40%;
  }
  .coeur__forme--droite {
    --shape-w: 76%;
    top: 36%;
    left: 62%;
  }
  .coeur__forme--bas-gauche {
    --shape-w: 96%;
    top: 64%;
    left: -54%;
  }
  .coeur__suite {
    grid-template-columns: minmax(0, 1fr);
  }
  .coeur__aparte {
    padding: var(--coeur-pad-module);
  }
  .coeur__aparte-contenu {
    position: static;
    gap: var(--sp-6);
  }
  /* Le CTA prend toute la largeur de son bloc.

     C'est ce qui le fait tenir sur une ligne, et ce n'est pas un détail de
     réglage : laissé à son `fit-content`, il se calait sur une largeur plus
     étroite que la place disponible et coupait son libellé — élargir le bloc
     n'y changeait rien, la mesure l'a montré (le bouton RÉTRÉCISSAIT quand on
     lui donnait de l'air). Une largeur imposée supprime le problème, et donne
     au passage une cible tactile pleine largeur. */
  .coeur__aparte-contenu .btn {
    width: 100%;
    white-space: normal;
    text-align: center;
    /* 12px et non 26 : la largeur du bouton ne dépend plus de son padding,
       celui-ci ne fait que rogner la place du libellé. */
    padding-inline: var(--sp-3);
  }
  /* Rien à reprendre sur le badge : sa taille et son débord se replient d'eux-
     mêmes par leurs `clamp()` et par le plafond de la gouttière, et le `clip`
     horizontal de la section le coupe au bord de la fenêtre s'il en approche. */
}
/* =========================================================================
   BLOC « liste-numerotee » — blocs/liste-numerotee.njk.
   Origine : « Les 4 garanties Biron », Figma 116:22469.

   ⚠️ Écart assumé avec la maquette : ce bloc ne porte plus de panneau
   bordé. Le pourquoi est en tête de blocs/liste-numerotee.njk.

   ⚠️ Le citron rempli de trame qui occupait le flanc droit a été retiré le
   14/08/2026, et sa troisième colonne réservée avec lui. Les formes de
   marque reviennent en petit : une par ligne, en aplat bleu, le numéro en
   blanc dedans.

   La respiration verticale (240 px de part et d'autre) est en revanche
   RESTÉE. Elle avait été calculée pour laisser voir la forme, qui n'est plus
   là — à revoir si la section paraît désormais trop vide.
   ========================================================================= */
.liste-numerotee {
  --liste-numerotee-gouttiere: clamp(var(--sp-8), 3.5vw, 4rem);
  position: relative;
  background-color: var(--bg-page);
  padding-block: clamp(var(--sp-16), 12.6vw, 15rem);
  /* Le contrat de la primitive `chemin` : c'est l'hôte qui borne l'horizontale,
     pour qu'un décor ne puisse jamais créer de barre de défilement. Ici la
     boucle est bornée à la largeur de son enveloppe et ne déborde donc pas —
     mais la règle vaut aussi le jour où l'on toucherait à `position` ou à la
     largeur ci-dessous, et c'est déjà ce que posent `liste-glyphes` et
     `face-a-face`.

     ⚠️ La verticale reste `visible`, et il le faut : la boucle est plus haute
     que le contenu et déborde dans le rembourrage de la section. Deux valeurs
     prises dans {visible, clip} ne se contraignent pas l'une l'autre — c'est
     `hidden` qui aurait forcé un conteneur de défilement sur les deux axes. */
  overflow-x: clip;
}

/* ---------- Le tracé en pointillé fluo ----------
   ⚠️ CE N'EST PLUS LA BOUCLE DEPUIS LE 31/08/2026. Elle entourait le bloc
   (`boucle.svg`, demandée par Hugo le 27/08/2026) ; le dessin est maintenant
   `ligne-jaune.svg`, un geste qui DESCEND LE LONG DU TEXTE, de la droite du
   titre jusque sous la quatrième garantie. Le réglage d'ajustement est au point
   d'appel, dans blocs/liste-numerotee.njk, et il a changé avec le dessin.

   Ce qui change ici tient en trois points, et chacun découle du même fait : un
   tracé qui ENTOURE et un tracé qui ACCOMPAGNE n'ont pas les mêmes besoins.

   1. ⚠️ IL SE MONTRE DÈS `lg`, ET NON PLUS À PARTIR DE `xl`. Le seuil de 1280 px
      était celui de la boucle, et il avait une raison précise : il lui fallait du
      JEU SUR LES FLANCS du bloc, or le conteneur n'atteint sa largeur maximale
      qu'autour de 1170 px — en dessous elle serait venue épouser le contour du
      texte au lieu d'en faire le tour. Ce geste-ci ne borde rien : il vit DANS
      le bloc, dans la gouttière entre le titre et la liste. Sa prémisse est
      celle de la primitive, qui masque déjà le tracé sous `lg` — il n'y a donc
      plus de surcharge à écrire, et les deux lignes qui la portaient sont
      parties.

   2. ⚠️ IL GARDE SON PROPRE POINTILLÉ, plus fin que celui du site. Le fichier
      livré dessine 4 px et des tirets de 8 8 ; `lib/chemin.js` ne retient que le
      `d` — « seul son `d` voyage » — et l'apparence vient des `--chemin-*`, qui
      valent 8 px et 14 20. Servi tel quel, le geste prenait donc un trait deux
      fois plus épais que dessiné, et des tirets presque doubles : sur un geste
      de 294 px de large, ça le rendait grossier. Les deux jetons sont donc
      reposés ici, aux valeurs du fichier.

      Le tracé est rendu à 0,945 de sa taille native (294 × 417 pour 312 × 441) :
      l'écart est assez faible pour que les cotes du dessin tiennent telles
      quelles, sans recalcul.

   3. Les 96rem restent, mais plus pour la même raison. C'était la mesure des
      boîtes sombres du site, choisie pour que les flancs de la boucle passent au
      dehors de la colonne de texte. Ici l'enveloppe ne sert plus qu'à donner son
      REPÈRE au placement horizontal : `data-chemin-position="0.23"` se calcule
      sur elle. La changer déplacerait le geste — et déplacerait aussi le
      contenu, qui vit dans la même enveloppe. */
.liste-numerotee .chemin {
  inline-size: min(96rem, 100% - var(--container-pad) * 2);
  margin-inline: auto;
  --chemin-epaisseur: 4px;
  --chemin-tirets: 8 8;
}

/* ---------- Mise en page ----------
   Deux colonnes : le titre, puis les garanties. Il y en avait une troisième,
   vide, qui réservait la voie du citron — sans elle la largeur des intitulés
   aurait dépendu du hasard du texte et fini par mordre sur la forme. La forme
   partie, la colonne n'avait plus d'objet. */
.liste-numerotee__inner {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.6fr);
  gap: var(--liste-numerotee-gouttiere);
  align-items: start;
}

.liste-numerotee__titre {
  margin: 0;
  color: var(--c-blue-zodiac);
  text-wrap: balance;
}

/* ---------- Les quatre garanties ---------- */
.liste-numerotee__liste {
  counter-reset: garantie;
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Recoleta SemiBold, comme dans la maquette : ces intitulés sont des titres
   courts et non du texte courant, le serif le dit.

   Le rembourrage latéral est repris en marge négative : le voile de survol
   s'étend alors au-delà du texte, sans que le texte quitte la grille. */
.liste-numerotee__ligne {
  counter-increment: garantie;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  /* Centré, et non plus sur la ligne de base. L'alignement par la base avait
     été demandé le 13/08/2026 quand le numéro était un chiffre nu : son point
     devait se poser exactement sur la ligne du texte. Le numéro est maintenant
     une pastille, une boîte sans ligne de base signifiante — c'est son centre
     qui doit répondre à celui de l'intitulé. */
  align-items: center;
  gap: clamp(var(--sp-4), 1.6vw, var(--sp-6));
  padding: clamp(var(--sp-4), 1.4vw, var(--sp-5)) var(--sp-4);
  margin-inline: calc(var(--sp-4) * -1);
  border-radius: var(--radius-cadre);
  font-family: var(--font-serif);
  font-weight: var(--fw-semibold);
  /* 26 px en grand écran : assez pour que les quatre intitulés tiennent
     chacun sur une seule ligne, ce que --fs-h3 (32 px) ne permettait pas —
     deux d'entre eux se repliaient et cassaient le rythme de la colonne. */
  font-size: clamp(1.125rem, 0.87rem + 0.79vw, 1.625rem); /* 18 → 26px */
  line-height: 1.3;
  letter-spacing: -0.01em;
  color: var(--c-blue-zodiac);
  transition: background-color var(--dur-fast) var(--ease-out);
}

/* ---------- Le numéro dans sa forme ----------
   Forme et chiffre sont EMPILÉS EN GRILLE, tous deux en cellule 1/1, plutôt
   que positionnés en absolu : la hauteur de l'enveloppe se déduit alors du
   ratio de la forme, sans avoir à le connaître ici — chaque forme a le sien
   (690/670, 652/692, 650/670).

   `overflow` reste visible : la rotation agrandit la boîte englobante de la
   forme, qui déborde donc un peu de l'enveloppe. C'est voulu, et c'est sans
   conséquence — rien ne la jouxte à moins d'une gouttière. */
.liste-numerotee__numero {
  display: grid;
  place-items: center;
  /* La forme part de là : `.forme` prend `--shape-w` avec 100% pour défaut. */
  width: clamp(3.25rem, 4.6vw, 4.75rem);
}

.liste-numerotee__pastille {
  grid-area: 1/1;
}

/* Le chiffre est le FRÈRE de la forme, pas son enfant : un masque s'applique
   à tout un sous-arbre, et le chiffre suivrait alors et la découpe et la
   rotation. Ici il reste droit quelle que soit l'inclinaison de la forme.

   ⚠️ CE `z-index` N'EST PAS DÉCORATIF — sans lui le chiffre est invisible.

   `.forme` porte un `transform: rotate()`, et un élément transformé est peint
   comme s'il créait un contexte d'empilement à z-index 0 : il passe donc à
   l'étape des éléments positionnés, APRÈS le contenu en ligne des descendants
   non positionnés — ce qu'est le chiffre. Être le frère suivant dans le DOM
   ne suffit pas, l'aplat bleu recouvre le blanc.

   Un `z-index` sur un élément de grille agit sans `position`, la spec Grid le
   prévoit ; `position: relative` est là par ceinture et bretelles, et parce
   qu'il rend l'intention lisible pour qui relit.

   Plus de point après le chiffre, ni l'interlettrage négatif qui le ramenait
   contre lui : le point servait à rattacher le numéro à son intitulé, ce dont
   la pastille se charge désormais. */
.liste-numerotee__chiffre {
  grid-area: 1/1;
  position: relative;
  z-index: 1;
  font-family: var(--font-mono);
  /* Un cran plus haut et plus gras que le premier jet : une chasse fixe sur
     un bleu moyen a besoin de matière pour tenir dans une pastille de 52 à
     76 px. */
  font-size: clamp(1.1875rem, 1.5vw, 1.625rem); /* 19 → 26px */
  font-weight: var(--fw-semibold);
  line-height: 1;
  /* La couleur du FOND DE PAGE et non du blanc : le chiffre se lit alors
     comme découpé dans la forme, qui laisse voir le crème à travers, plutôt
     que posé dessus. La nuance est mince à l'œil — le crème est très clair —
     mais elle change ce que la pastille raconte.

     ⚠️ `--bg-page` bascule au bleu nuit dans les sections sombres
     (_tokens.scss). Ce bloc-ci pose son propre fond crème, il n'y a donc pas
     de piège ici ; mais un jour où on le poserait sur un fond sombre, le
     chiffre suivrait tout seul — ce qui est exactement ce qu'on veut. */
  color: var(--bg-page);
}

.liste-numerotee__chiffre::before {
  content: counter(garantie);
}

/* ---------- Survol ----------
   Un voile fluo très dilué. Le fluo plein est le survol des boutons du site ;
   à 10 % il devient une réaction et non une promesse d'action — ces lignes ne
   sont pas cliquables.

   Sous `hover: hover` uniquement : sur un écran tactile, le survol se
   déclenche au toucher et reste collé sur la dernière ligne touchée.

   La durée passe par --dur-fast, ramenée à 0,01 ms quand l'utilisateur
   demande moins d'animations (_base.scss) : le garde-fou est déjà en place. */
@media (hover: hover) {
  .liste-numerotee__ligne:hover {
    background-color: color-mix(in srgb, var(--c-fluo) 10%, transparent);
  }
}
/* =========================================================================
   Une seule colonne.

   Le titre passe au-dessus, les garanties en dessous.
   ========================================================================= */
@media (max-width: 60rem) {
  /* ⚠️ La boucle en pointillé a déjà disparu bien avant ici — dès `xl`, voir
     plus haut. C'est là qu'on vient la chercher. */
  .liste-numerotee {
    padding-block: var(--sp-section);
  }
  .liste-numerotee__inner {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--sp-8);
  }
  .liste-numerotee__ligne {
    /* ⚠️ LA PASTILLE RESTE SUR LA LIGNE DE L'INTITULÉ, y compris ici — tranché
       par Hugo le 27/08/2026 : « les puces sur la même ligne que le texte en
       mode mobile. Si le texte est trop long, il peut revenir à la ligne. »

       Cette requête faisait passer la pastille AU-DESSUS du texte (grille à une
       seule colonne + `justify-items: start`, sans quoi une pastille de largeur
       fixe se serait étirée sur toute la ligne). Le motif invoqué était qu'un
       intitulé de quatre mots se replierait sur trois lignes étroites : c'est
       exactement ce qui est désormais accepté, le repli valant mieux qu'un
       marqueur décollé de ce qu'il numérote.

       Il n'y a donc plus rien à déclarer sur la grille — la ligne garde les deux
       colonnes `auto minmax(0, 1fr)` de la règle de base, et la seconde étant
       bornée par `minmax(0, …)`, le texte se replie au lieu de pousser la ligne.
       Ne pas y remettre `justify-items`, qui étirerait la pastille.

       Seul le corps du texte descend, comme avant. */
    font-size: var(--fs-md);
  }
}
/* =========================================================================
   Ils l'ont vécu — Figma 160:1778.

   Une boîte bleu nuit posée sur le crème, et dedans un carrousel : la carte
   courante centrée, ses voisines qui dépassent de part et d'autre.

   La section était un aplat pleine largeur encadré de deux bandes de trame
   jusqu'au 14/08/2026. Elle porte maintenant sa propre rupture sur l'arête de
   sa boîte, comme la séquence sombre.

   DERRIÈRE LA BOÎTE, trois formes de marque remplies de trame. C'est la
   grammaire de « Au cœur de Biron » : la boîte est opaque et les recouvre,
   elles n'existent que par ce qui dépasse. La section avait perdu toute
   texture en abandonnant ses bandes ; celle-ci ne coûte pas un pixel de
   défilement.

   Deux émergent par le bas, une coiffe la boîte — pas par les côtés, où elles
   se seraient disputé la place avec les flèches. Leur trame s'éteint aux deux
   extrémités : aucune ne peut donc se faire trancher net par la section
   voisine, ce qui est le défaut classique d'un décor rogné à ses bords.

   ⚠️ TROIS NIVEAUX, à ne pas confondre :
     .temoignages         l'enveloppe pleine largeur, sur le crème
     .temoignages__decor  le plan des formes, qui porte le rognage
     .temoignages__boite  le bleu nuit, opaque, où vit tout le contenu

   La piste est une bande à DÉFILEMENT NATIF avec accrochage (scroll-snap),
   pas une translation pilotée en JavaScript. Trois raisons : le glissement
   au doigt fonctionne sans une ligne de code, la molette et le clavier aussi,
   et sans JS tout le contenu reste atteignable — seules les flèches
   disparaissent.
   ========================================================================= */
.temoignages {
  /* Largeur de la carte : 1280 dans la maquette. Le `100%` est résolu contre
     la boîte de contenu de la piste — qui n'a pas de rembourrage, justement
     pour que cette base soit la largeur de la section et rien d'autre. */
  --temoignages-depassement: clamp(1.5rem, 10vw, 12.5rem);
  /* 112 px dans la maquette, montés à 140 : la carte ayant rétréci, la
     gouttière reprend une partie de la place libérée pour que les voisines ne
     se découvrent pas trop. */
  --temoignages-gouttiere: clamp(var(--sp-6), 7.4vw, 8.75rem);
  /* Largeur d'une carte. Utilisée à deux endroits — la largeur de lame et la
     position des flèches — et c'est volontaire qu'ils partagent la variable :
     les deux résolvent leur `100%` contre la même boîte, la largeur de la
     section. C'est cette condition qui rend l'expression sûre ; c'est en la
     violant, avec un rembourrage sur la piste, qu'on avait obtenu une lame de
     902 px au lieu de 1280.

     ⚠️ Le plafond n'est plus `--container-max` (1280 px) mais 62 rem, soit
     992 px. À 1280, la citation courait sur près de 1200 px de mesure — une
     ligne de 90 signes, bien au-delà de ce qu'on lit confortablement.
     Ramenée à 992, la carte tient une soixantaine de signes par ligne.
     Demandé le 14/08/2026. */
  --temoignages-carte: min(
    62rem,
    100% - 2 * var(--temoignages-depassement)
  );
  /* Hauteur dont l'ombre de la carte a besoin au-dessus d'elle. Elle valait
     120 px tant qu'une forme fluo dépassait aussi ; la forme partie, il ne
     reste que l'ombre à laisser passer — 67 px de flou pour −10 d'étalement,
     soit une cinquantaine de pixels utiles. */
  --temoignages-debord: clamp(var(--sp-6), 3vw, var(--sp-12));
  /* Écart titre → carte, revenu aux 58 px de la maquette. Il avait été poussé
     à 96 pour une raison qui n'existe plus : les formes fluo qui dépassaient
     des cartes montaient jusque dans la rangée du cartouche et, fluo sur
     fluo, en dissolvaient le contour. */
  --temoignages-ecart-titre: clamp(var(--sp-8), 3.4vw, 3.625rem);
  position: relative;
  background-color: var(--bg-page);
  /* Le rembourrage bas est nettement plus généreux que le haut : c'est LUI la
     bande où les formes se voient. Tout le reste de leur silhouette passe
     derrière la boîte. */
  padding-block: clamp(var(--sp-10), 4.6vw, var(--sp-16)) clamp(var(--sp-16), 8vw, 8.75rem);
}

/* ---------- Décor ----------
   ⚠️ Le rognage est PORTÉ PAR CE PLAN et non par la section. Un `overflow`
   sur la section en ferait un conteneur de défilement, ce qui perturberait
   l'accrochage `scroll-snap` de la piste du carrousel. Il ne sert que de
   filet : les cotes ci-dessous font que rien ne devrait le toucher.

   ⚠️ UNE FORME TOURNÉE PEINT HORS DE SA BOÎTE, et c'est ce qui avait fait
   couper l'une d'elles. À −5° sur une forme de 419 px, le coin bas descend
   d'environ 18 px sous son bord ; il ne restait que 6 px de garde. Les
   dégagements ci-dessous sont donc calés bien au-delà de ce qu'une rotation
   peut coûter, et non au plus juste. */
.temoignages__decor {
  position: absolute;
  inset-block: 0;
  left: 50%;
  translate: -50% 0;
  width: 100%;
  /* Même boîte que `.temoignages__boite` — sinon les formes restent calées
     sur la pleine largeur de la section pendant que la boîte se centre au-delà
     de 96 rem, et la composition part visiblement vers la gauche. */
  max-width: 96rem;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
}

/* En dessous de 96 rem, la boîte troque `margin-inline: auto` contre
   `var(--container-pad)` (voir plus bas) : le décor doit rétrécir pareil,
   sinon les formes débordent sous le rembourrage de la boîte. */
@media (max-width: 96rem) {
  .temoignages__decor {
    width: calc(100% - 2 * var(--container-pad));
  }
}
/* Chaque forme s'ancre au bord dont elle dépasse — par le bas pour celles qui
   émergent sous la boîte, par le haut pour celle qui la coiffe — jamais aux
   deux. C'est ce qui rend leur position indépendante de la hauteur du
   carrousel. */
.temoignages__forme {
  position: absolute;
  width: var(--shape-w);
  /* ⚠️ Ceinture ET bretelles. `--grain-angle: 0deg` met la montée de densité
     sur l'axe VERTICAL : la trame s'éteint donc aux DEUX extrémités de chaque
     forme, en haut comme en bas. C'est exactement ce qu'il faut ici, où l'on
     ne voit jamais que l'une ou l'autre — le reste est sous la boîte. Même si
     une cote dérivait au point de faire toucher un bord, il n'y aurait rien à
     trancher : le motif y est déjà éteint. */
  --grain-angle: 0deg;
  --grain-debut: 30%;
  --grain-fin: 62%;
}

/* Largeurs et positions dépareillées : à pas régulier, l'œil attrape
   l'alignement et la rangée se lit comme une frise. */
.temoignages__forme--gauche {
  --shape-w: 20%;
  --shape-rot: -8deg;
  bottom: 3.5rem;
  left: 5%;
}

.temoignages__forme--droite {
  --shape-w: 21%;
  --shape-rot: -5deg;
  bottom: 3rem;
  left: 70%;
}

/* Celle-ci coiffe la boîte au lieu d'en sortir : elle casse la rangée du bas,
   qui alignait ses trois formes sur un même bord. Elle est décalée vers la
   droite pour ne tomber au-dessus d'aucune des deux autres. */
.temoignages__forme--haut {
  --shape-w: 18%;
  --shape-rot: 11deg;
  top: 3rem;
  left: 46%;
}

/* ---------- La boîte ----------
   Cotes de `.sequence__boite` : 96 rem et 28 px de rayon, pour que les deux
   sections sombres de la page se traitent pareil. Tranché le 14/08/2026 sur
   le banc /labo/temoignages-raccord/.

   Son opacité n'est pas un détail de teinte : c'est elle qui recouvre les
   formes et ne leur laisse que les marges. Sans elle, la composition perd son
   cadrage — exactement comme le panneau de « Au cœur de Biron ».

   ⚠️ Pas d'`overflow` malgré le rayon. Les cartes ne s'approchent jamais des
   angles — le rembourrage vertical les en écarte — et la piste rogne déjà son
   contenu horizontalement. Un `overflow` ici couperait en revanche l'ombre des
   cartes, que la piste s'échine justement à laisser passer. */
.temoignages__boite {
  position: relative;
  z-index: 1;
  max-width: 96rem;
  margin-inline: auto;
  background-color: var(--c-blue-dark);
  /* Coins arrondis et aucun contour — comme `.sequence__boite`, dont ce
     rayon est repris à l'identique. Le pourquoi de l'absence de contour est
     détaillé là-bas : sur du bleu nuit posé sur crème, le contraste fait déjà
     l'arête. */
  border-radius: 1.75rem; /* 28px */
  padding-block: clamp(var(--sp-12), 4.7vw, 5.5rem);
}

/* En dessous de 96 rem, `margin-inline: auto` ne ménage plus rien : la boîte
   toucherait les deux bords de l'écran et cesserait d'en être une. */
@media (max-width: 96rem) {
  .temoignages__boite {
    margin-inline: var(--container-pad);
  }
}
/* ---------- Titre ----------
   Un vrai titre de section, comme partout ailleurs, et non plus un cartouche.
   Il reprend le gabarit de `.panneau__titre` (sections/_sequence.scss),
   l'autre titre sombre de la page : serif, --fs-h2, blanc. Demandé le
   14/08/2026.

   `position: relative` et `z-index` ne sont plus nécessaires — ils tenaient
   le titre au-dessus des formes fluo qui dépassaient des cartes et venaient
   dans sa rangée. Ces formes n'existent plus. */
.temoignages__titre {
  margin: 0;
  font-family: var(--font-serif);
  font-size: var(--fs-h2);
  font-weight: var(--fw-semibold);
  line-height: 1.18;
  letter-spacing: -0.02em;
  color: var(--c-white);
  text-wrap: balance;
}

/* ---------- Carrousel ---------- */
.temoignages__carrousel {
  position: relative;
}

/* ---------- Les fondus de bord ----------
   Deux voiles bleu nuit aux flancs du carrousel : la carte voisine s'y dilue
   au lieu de s'arrêter sur l'arête de la boîte. Ils marquent les côtés et
   disent qu'il y a une suite, sans la donner entièrement à lire.

   Ils sont SŒURS de la piste et non dedans : la piste rogne son contenu, elle
   les aurait emportés dans son défilement.

   La largeur est calée pour s'éteindre AVANT la carte courante. À 1904 px, la
   voisine dépasse de 132 px et le voile en fait 224 : il couvre donc tout ce
   qui dépasse, plus une partie de la gouttière, et il est déjà transparent
   quand il arrive sur la carte du milieu — qui reste intacte.

   `z-index: 1` les met au-dessus des cartes et SOUS les flèches, qui sont à
   2. Elles tombent précisément dans cette bande : voilées, elles perdraient
   leur contraste. `pointer-events: none` laisse passer le glissement au
   doigt, que le voile intercepterait sinon.

   ⚠️ Rien à craindre pour les angles arrondis de la boîte, malgré l'absence
   de rognage : le carrousel est inséré dans le rembourrage vertical de
   celle-ci, les voiles n'approchent donc jamais ses coins. */
.temoignages__carrousel::before,
.temoignages__carrousel::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  z-index: 1;
  width: clamp(3rem, 12vw, 14rem);
  pointer-events: none;
}

.temoignages__carrousel::before {
  left: 0;
  background-image: linear-gradient(to right, var(--c-blue-dark), transparent);
}

.temoignages__carrousel::after {
  right: 0;
  background-image: linear-gradient(to left, var(--c-blue-dark), transparent);
}

.temoignages__piste {
  display: flex;
  gap: var(--temoignages-gouttiere);
  /* AUCUN rembourrage latéral, et c'est délibéré.

     On serait tenté d'en mettre pour que la première et la dernière lame
     puissent venir se centrer. Deux raisons de s'en passer : la boucle fait
     qu'on reste toujours dans la copie du milieu, donc aucune lame de bord
     n'a jamais besoin de se centrer ; et surtout un rembourrage en % créait
     une double résolution — la largeur de lame, elle aussi en %, se calculait
     alors contre une base déjà rétrécie par ce rembourrage. Mesuré : 902 px
     de lame au lieu de 1280. */
  /* ⚠️ `overflow-x: auto` rogne AUSSI en vertical : la spécification force
     `overflow-y` à `auto` dès que l'un des deux axes n'est plus `visible`.
     Sans rembourrage vertical, l'ombre de la carte se faisait donc couper
     net — constaté à l'écran.
     On ménage la place DANS la boîte de défilement, puis on la reprend par
     une marge négative pour que la carte reste à la hauteur de la maquette. */
  padding-block: var(--temoignages-debord) clamp(var(--sp-6), 2.5vw, var(--sp-12));
  margin-block-start: calc(var(--temoignages-ecart-titre) - var(--temoignages-debord));
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  overscroll-behavior-x: contain;
  /* Pas de `scroll-behavior: smooth` ici : le recalage de boucle doit être
     INSTANTANÉ. Les flèches demandent l'animation au cas par cas, en
     JavaScript. */
  /* La barre de défilement n'a rien à faire là : le carrousel a ses
     flèches, et sur un fond sombre elle ferait tache. */
  scrollbar-width: none;
}

.temoignages__piste::-webkit-scrollbar {
  display: none;
}

/* Le focus clavier doit se voir : la piste est focalisable pour permettre le
   défilement aux flèches du clavier. */
.temoignages__piste:focus-visible {
  outline: 2px solid var(--c-fluo);
  outline-offset: 4px;
}

.temoignages__lame {
  position: relative;
  flex: 0 0 var(--temoignages-carte);
  /* La lame est elle-même une boîte flexible pour que la carte s'y étire en
     hauteur. Les lames, étant des éléments flexibles de la piste, prennent
     déjà toutes la hauteur de la plus haute ; c'est la CARTE qui restait à sa
     hauteur de contenu, d'où des cartes inégales. */
  display: flex;
  /* Centrée dans la fenêtre de défilement, ce qui laisse les voisines
     dépasser symétriquement — 192 px à 1904, contre 200 dans la maquette. */
  scroll-snap-align: center;
}

/* ---------- Deux citations : côte à côte, sans carrousel ----------
   Rendu à la place de la piste quand la liste en compte moins de trois — c'est
   le gabarit qui tranche, sur le nombre de citations (voir son en-tête).

   TROIS CHOSES QUE CETTE GRILLE N'A PAS À REFAIRE, et c'est ce qui rend l'état
   si court à écrire :

   1. `auto-fit` ne compte rien. Deux citations donnent deux colonnes égales,
      une seule en donne une pleine largeur, et l'écran étroit repasse à une
      colonne tout seul — aucun point de rupture à déclarer, aucun nombre à
      tenir en accord avec le gabarit.

   2. Les `flex` de `.temoignages__lame` deviennent INERTES sans rien annuler.
      `flex: 0 0 var(--temoignages-carte)` ne s'applique qu'à l'enfant d'une
      boîte flexible ; dans une grille, la déclaration est simplement ignorée.
      Le `display: flex` de la lame, lui, sert encore : c'est lui qui étire la
      carte sur toute la hauteur de la rangée, donc qui met les deux filets
      d'auteur sur une même ligne de base. `scroll-snap-align` est sans effet
      hors conteneur de défilement.

   3. Pas de rembourrage à ménager pour l'ombre des cartes. La piste devait le
      faire parce que son `overflow-x` rogne aussi en vertical ; ici rien ne
      rogne, l'ombre se peint librement.

   La gouttière ne reprend pas `--temoignages-gouttiere` (jusqu'à 140 px, calée
   pour éloigner des cartes qui se chevauchent presque) : deux cartes posées
   n'ont pas ce problème et 140 px les feraient lire comme deux sections. */
.temoignages__duo {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 22rem), 1fr));
  gap: clamp(var(--sp-6), 2.4vw, var(--sp-10));
  margin-block-start: var(--temoignages-ecart-titre);
}

/* ---------- La carte ---------- */
.temoignages__carte {
  /* ⚠️ PAS DE `z-index` ICI, et ce n'est pas un oubli.

     La carte en portait un — `z-index: 1` — du temps où une forme fluo était
     posée derrière elle, à 0. La forme retirée, il est resté ; et comme les
     voiles de bord du carrousel sont eux aussi à 1, les trois se retrouvaient
     à égalité. À z-index égal, c'est l'ORDRE DU DOCUMENT qui tranche : le
     voile de gauche, premier enfant du carrousel, passait sous les cartes,
     tandis que celui de droite, dernier enfant, passait dessus. D'où un
     dégradé visible à droite et absent à gauche — une asymétrie parfaite
     produite par deux règles rigoureusement symétriques.

     `position: relative` reste : les initiales de l'avatar en dépendent. */
  position: relative;
  /* Occupe toute la lame : c'est ce qui donne à toutes les cartes la même
     hauteur, celle imposée par le témoignage le plus long. */
  /* Le rembourrage LATÉRAL est une variable parce qu'il est repris à
     l'identique par les deux moitiés de la carte. C'est ce qui les aligne de
     part et d'autre du filet qui les sépare. */
  --temoignages-pad-inline: clamp(var(--sp-5), 1.9vw, 2.25rem);
  flex: 1;
  display: flex;
  flex-direction: column;
  margin: 0;
  /* ⚠️ AUCUN rembourrage sur la carte, et c'est ce qui rend le filet
     possible : ce sont ses deux moitiés qui portent le leur. Un rembourrage
     ici aurait laissé le trait s'arrêter à 44 px de chaque bord, flottant au
     lieu de séparer. */
  padding: 0;
  background-color: var(--c-base-main);
  /* Le gabarit des CARTES CLAIRES DANS UNE BOÎTE SOMBRE, et non celui des
     panneaux de texte du site. Demandé par Hugo le 27/08/2026 ; la carte
     portait des coins vifs et un filet bleu depuis le 14/08/2026, qui étaient
     eux-mêmes un retour en arrière sur un état arrondi et sans contour.

     Deux choses, et chacune a sa raison ailleurs sur le site :

     ARRONDI DE 16px, PAS LES 28px DE LA BOÎTE. Deux arrondis concentriques de
     même rayon se concurrencent — l'écart et la cote sont ceux de la carte à
     la une du blog, de `module.cartes` et des cartes de /cas-clients/.

     AUCUN CONTOUR. C'est l'argument de la boîte elle-même, relu à l'envers :
     du crème posé sur du bleu nuit, le contraste fait déjà l'arête. Un filet
     n'est dû qu'aux cartes blanches posées sur le bleu clair des pages
     éditoriales, où il ne reste que 1,05 de contraste pour dessiner le bord. */
  border-radius: var(--radius-lg); /* 16px */
  /* Ombre discrète — 32 px de flou à 30 % d'opacité, contre 67 px à 50 %
     à l'origine. Elle a été resserrée le jour où un filet cernait la carte,
     et elle reste à cette valeur maintenant qu'il est parti : le contraste
     crème / bleu nuit suffit à l'arête, l'ombre ne fait plus que poser la
     carte. */
  box-shadow: 0 2px 32px -8px rgba(44, 42, 46, 0.3);
}

/* La mesure est bornée sous la largeur de la carte : à 992 px de carte moins
   son rembourrage, il resterait 900 px de ligne. Le blanc qui reste à droite
   de la citation n'est pas une chute, c'est ce qui l'empêche de s'étirer. */
.temoignages__citation {
  margin: 0;
  max-width: 48rem; /* 768px */
  /* Les rembourrages sont resserrés une seconde fois — 32 px en haut, 40 en
     bas, 36 sur les côtés, contre 70/44 à l'origine. La raison n'est pas
     esthétique : la carte doit pouvoir tenir QUATRE LIGNES de citation sans
     grandir, et comme toutes les cartes prennent la hauteur de la plus haute,
     chaque pixel rendu au texte est un pixel qui ne s'ajoute pas aux trois. */
  padding: clamp(var(--sp-5), 1.8vw, var(--sp-8)) var(--temoignages-pad-inline) clamp(var(--sp-6), 2.2vw, var(--sp-10));
}

.temoignages__citation p {
  margin: 0;
  font-family: var(--font-serif);
  font-weight: var(--fw-medium);
  /* 22 px à 1900, contre 32 dans la maquette et 40 pour --fs-quote. Le corps
     a été descendu trois fois : 40 faisait passer la citation sur trois lignes
     et rendait la carte massive, 32 restait trop présent, 28 l'était encore
     une fois la carte rétrécie. Une citation n'est pas un titre. */
  font-size: clamp(1rem, 0.86rem + 0.45vw, 1.375rem); /* 16 → 22px */
  line-height: 1.32;
  letter-spacing: -0.025em;
  color: var(--c-blue-dark);
  text-wrap: pretty;
}

/* ---------- L'auteur ---------- */
/* Repoussé en bas de la carte. Puisque toutes les cartes ont la même hauteur
   mais pas la même longueur de citation, un auteur collé à son texte
   flotterait à une hauteur différente d'une carte à l'autre. Aligné en bas,
   il donne une ligne de base commune au carrousel — et, depuis que le filet
   les sépare, une rangée de traits alignés d'une carte à l'autre. */
.temoignages__auteur {
  margin-block-start: auto;
  display: flex;
  align-items: center;
  /* L'écart avec le filet vaut le rembourrage du bord, et pas un cran
     d'espacement générique : c'est ce qui CENTRE optiquement la forme dans sa
     case. À 16 px elle se collait au trait alors qu'elle en avait 44 de
     l'autre côté, et l'ensemble penchait à droite. */
  gap: var(--temoignages-pad-inline);
  /* En variable parce que l'identité doit pouvoir l'annuler — voir plus bas.
     Resserré en même temps que ceux de la citation : la forme étant passée de
     46 à 60 px, le bloc auteur aurait sinon gagné 14 px de haut sur toutes les
     cartes, au détriment de la place laissée au texte. */
  --temoignages-pad-auteur: clamp(var(--sp-4), 1.4vw, var(--sp-6));
  padding: var(--temoignages-pad-auteur) var(--temoignages-pad-inline);
  border-top: var(--filet-panneau);
}

/* La forme masque la place de la photo ; les initiales se posent par-dessus.
   Deux éléments et non un seul : un masque rognerait le texte avec le reste.

   Elle est passée de 46 à 60 px : dans une case dont elle est le seul objet,
   46 px la faisaient paraître posée là faute de mieux. Sa hauteur commande
   celle de toute la rangée — donc aussi la longueur du filet vertical, qui
   s'étire d'autant. */
.temoignages__avatar {
  position: relative;
  flex-shrink: 0;
  width: 3.75rem; /* 60px */
}

.temoignages__avatar-forme {
  --shape-w: 100%;
  display: block;
}

/* ---------- Le portrait, depuis le 31/08/2026 ----------
   Il remplace l'aplat bleu nuit et ses initiales, sans rien changer d'autre :
   même forme, même rotation citron → losange → écu, même masque. C'est la macro
   `formePhoto` de partials/forme.njk qui pose `--shape-src` et `--shape-ratio`
   en ligne, comme le fait `forme` pour l'aplat.

   ⚠️ `object-fit: cover` EST LA LIGNE QUI COMPTE. Les cinq portraits sont en
   414 × 464, soit un rapport de 0,892, et les trois formes tournent entre 0,941
   (écu) et 1,030 (citron). Sans lui, chaque visage serait étiré d'une valeur
   différente selon le rang qu'il occupe dans la rotation — un défaut qui ne se
   voit qu'en comparant deux cartes, donc presque jamais à la relecture. */
.temoignages__photo {
  display: block;
  inline-size: 100%;
  block-size: auto;
  aspect-ratio: var(--shape-ratio);
  object-fit: cover;
  mask-image: var(--shape-src);
  mask-size: 100% 100%;
  mask-repeat: no-repeat;
  -webkit-mask-image: var(--shape-src);
  -webkit-mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat;
}

.temoignages__initiales {
  position: absolute;
  inset: 0;
  display: grid;
  place-content: center;
  font-family: var(--font-mono);
  /* Suit l'agrandissement de la forme : à --fs-xs, les initiales flottaient
     au milieu des 60 px. */
  font-size: var(--fs-sm);
  font-weight: var(--fw-bold);
  color: var(--c-white);
}

/* Le filet vertical qui sépare la photo de l'identité, écho de celui qui
   sépare la citation de l'auteur. Il court sur TOUTE la hauteur du bloc
   auteur — du filet horizontal jusqu'au bas de la carte — et non sur la seule
   hauteur de l'avatar.

   Deux déclarations le font, et elles vont ensemble :

     align-self: stretch   sans lui, l'identité ne fait que la hauteur de ses
                           deux lignes de texte ; étirée, elle prend celle de
                           la rangée, c'est-à-dire celle de l'avatar.
     marge négative        `stretch` cale la boîte de MARGE sur la hauteur de
                           la rangée. Une marge négative de la valeur du
                           rembourrage pousse donc la boîte de BORDURE d'autant
                           au-delà, en haut comme en bas — le trait rejoint
                           ainsi les deux arêtes du bloc. Le rembourrage rendu
                           à l'intérieur remet le texte là où il était.

   Le `justify-content: center` remet le texte à mi-hauteur, que `stretch`
   venait de lui retirer. */
.temoignages__identite {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-self: stretch;
  margin-block: calc(var(--temoignages-pad-auteur) * -1);
  padding-block: var(--temoignages-pad-auteur);
  padding-inline-start: var(--sp-4);
  border-inline-start: var(--filet-panneau);
}

.temoignages__nom {
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
  color: var(--c-blue-dark);
}

.temoignages__role {
  font-size: var(--fs-xs);
  color: var(--c-blue-400);
}

/* ---------- Flèches ----------
   Posées dans le dépassement, à cheval sur les voisines. `hidden` en HTML :
   elles n'apparaissent que si le script prend la main, sinon elles
   promettraient une commande qui ne répond pas. */
.temoignages__fleche {
  position: absolute;
  top: 50%;
  translate: 0 -50%;
  z-index: 2;
  display: grid;
  place-content: center;
  width: 3rem;
  height: 3rem;
  padding: 0;
  font-size: var(--fs-md);
  color: var(--c-white);
  background-color: color-mix(in srgb, var(--c-blue-zodiac) 70%, transparent);
  border: 1px solid rgba(255, 255, 255, 0.3);
  border-radius: var(--radius-full);
  cursor: pointer;
  transition: background-color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}

.temoignages__fleche[hidden] {
  display: none;
}

/* Centrées dans la gouttière qui sépare la carte courante de sa voisine :
   elles tombent ainsi sur le bleu nuit et non sur une carte crème, où leur
   halo sombre faisait tache. Le calcul : le bord de la carte, moins la
   moitié de la gouttière, moins la moitié du bouton. */
.temoignages__fleche--prec {
  left: calc(50% - (var(--temoignages-carte) + var(--temoignages-gouttiere)) / 2 - 1.5rem);
}

.temoignages__fleche--suiv {
  right: calc(50% - (var(--temoignages-carte) + var(--temoignages-gouttiere)) / 2 - 1.5rem);
}

@media (hover: hover) {
  .temoignages__fleche:hover {
    color: var(--c-blue-zodiac);
    background-color: var(--c-fluo);
    border-color: var(--c-fluo);
  }
}
.temoignages__fleche:focus-visible {
  outline: 2px solid var(--c-fluo);
  outline-offset: 3px;
}

/* =========================================================================
   Écrans étroits.

   La carte prend presque toute la largeur : le dépassement des voisines,
   qui sert à faire comprendre qu'il y en a d'autres, se réduit à un indice.
   Les flèches se rapprochent du bas de la carte plutôt que de flotter
   par-dessus le texte.
   ========================================================================= */
@media (max-width: 60rem) {
  .temoignages {
    --temoignages-depassement: clamp(var(--sp-4), 6vw, var(--sp-10));
    --temoignages-gouttiere: var(--sp-4);
  }
  /* Trois formes autour d'une boîte étroite, ce serait trois taches. On garde
     celle du haut, qui coiffe, et une seule en bas, toutes deux plus larges. */
  .temoignages__forme--droite {
    display: none;
  }
  .temoignages__forme--gauche {
    --shape-w: 52%;
    left: 44%;
  }
  .temoignages__forme--haut {
    --shape-w: 44%;
    left: -8%;
  }
  .temoignages__citation p {
    line-height: 1.4;
  }
  /* La gouttière est trop étroite pour y loger un bouton : les flèches
     reviennent au ras des bords, par-dessus le bord des cartes voisines. */
  .temoignages__fleche {
    width: 2.5rem;
    height: 2.5rem;
  }
  .temoignages__fleche--prec {
    left: var(--sp-2);
  }
  .temoignages__fleche--suiv {
    right: var(--sp-2);
  }
}
/* =========================================================================
   Questions fréquentes — Figma 73:1448.

   Panneau blanc bordé de bleu, découpé 340 / 938. Décor : une trame sur le
   flanc gauche, deux formes bleues sur le flanc droit — le panneau est opaque
   et les recouvre là où ils se croisent.

   ⚠️ Les filets étaient FLUO, comme le veut la maquette. Ils passent au bleu
   le 14/08/2026, avec le jeton `--filet-panneau` partagé par « Au cœur de
   Biron » et « Une approche data unique » : trois panneaux voisins dans la
   page n'ont aucune raison de porter trois contours différents.
   ========================================================================= */
.faq {
  --faq-filet: var(--filet-panneau);
  --faq-pad-accroche: clamp(var(--sp-8), 3.6vw, 4.3125rem); /* 69px */
  --faq-pad-accroche-inline: clamp(var(--sp-6), 2.3vw, 2.75rem); /* 44px */
  --faq-pad-liste: clamp(var(--sp-6), 2.3vw, 2.75rem); /* 44px */
  /* ---------- Repères pour caler la trame sur le filet du panneau ----------
     Le panneau est centré et plafonné ; la trame, elle, part du bord de page.
     Pour que son bord droit tombe EXACTEMENT sur le filet vertical qui sépare
     les deux colonnes, il faut donc reconstituer la position de ce filet dans
     le repère de la section : la marge à gauche du panneau, plus la première
     colonne de sa grille.

     340 / (340 + 938) = 0,26604 — la proportion de la grille du panneau. */
  --faq-panneau-large: min(100%, var(--container-max));
  --faq-panneau-marge: calc((100% - var(--faq-panneau-large)) / 2);
  --faq-axe-filet: calc(
    var(--faq-panneau-marge) + var(--faq-panneau-large) * 0.26604
  );
  position: relative;
  background-color: var(--bg-page);
  padding-block: clamp(var(--sp-16), 12.4vw, 14.75rem);
}

/* ---------- Décor ---------- */
.faq__decor {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
}

/* La trame court du bord de page jusqu'au FILET VERTICAL du panneau, sur
   lequel elle s'arrête au pixel. Elle tombait auparavant à 34,5 % de la
   section, une valeur qui n'avait de rapport avec rien et qui dérivait par
   rapport au filet à chaque changement de largeur.

   ⚠️ Les `100%` de `--faq-axe-filet` se résolvent ICI, contre le plan de
   décor. C'est valide parce que ce plan est en `inset: 0` sur la section : il
   a donc exactement la largeur contre laquelle le panneau se centre. Poser ce
   jeton sur un élément plus étroit donnerait une tout autre valeur.

   Le fondu est vertical seulement : la trame naît du fond en haut et s'y
   éteint en bas, mais s'arrête net sur ses côtés. */
.faq__grille {
  position: absolute;
  inset-block: 0;
  inset-inline: 0 auto;
  width: var(--faq-axe-filet);
}

.faq__formes {
  position: absolute;
  inset: 0;
}

.faq__forme {
  position: absolute;
}

/* Losange étiré. La maquette lui donne 300 × 397 alors que sa proportion
   naturelle est 0,97 : l'instance est déformée en hauteur. On reproduit
   l'étirement en forçant l'aspect-ratio — le masque suit, il est en
   `mask-size: 100% 100%`. */
.faq__forme--haut {
  --shape-w: 15.8%;
  --shape-rot: 0deg;
  aspect-ratio: 300/397;
  top: 8.7%;
  left: 75.7%;
}

/* Ancrée au BAS et non au haut. En pourcentage de la hauteur de section elle
   disparaissait derrière le panneau : la section est plus courte que dans la
   maquette (la réponse ouverte y occupe moins de place), donc le même
   pourcentage ne tombe pas au même endroit. Depuis le bas, la distance au
   bord inférieur du panneau est fixe — c'est le rembourrage de la section. */
.faq__forme--bas {
  --shape-w: 9.6%;
  --shape-rot: -10deg;
  bottom: 7%;
  left: 67.9%;
}

/* ---------- Panneau ---------- */
.faq__panneau {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: minmax(0, 340fr) minmax(0, 938fr);
  width: 100%;
  max-width: var(--container-max);
  margin-inline: auto;
  /* Blanc, comme le panneau de « Au cœur de Biron » : c'est ce qui le détache
     du crème de la page, maintenant que son filet ne crie plus. */
  background-color: var(--c-white);
  border: var(--faq-filet);
  /* Coins vifs, comme le panneau de « Au cœur de Biron » depuis le
     14/08/2026. Le jeton `--radius-cadre` n'est pas remis à zéro pour autant :
     il habille aussi les cadres de visuels et les cartes d'articles, qui
     gardent les leurs. Ce sont les PANNEAUX qui se carrent, pas tout. */
  border-radius: 0;
}

/* Le titre prend les marges de bande d'en-tête partagées par les autres
   panneaux — `--pad-panneau-entete-*` (_tokens.scss). Il ouvrait à 93 px du
   haut de la cellule, contre 56 pour ses voisins : l'écart se voyait d'une
   section à l'autre.

   Ces 93 px avaient une raison, disparue depuis : un surtitre en chasse fixe
   coiffait le titre, et sa hauteur avait été rendue au rembourrage quand on
   l'a retiré plutôt que perdue. Le titre n'en avait pas besoin. */
.faq__accroche {
  display: flex;
  flex-direction: column;
  padding: var(--pad-panneau-entete-haut) var(--faq-pad-accroche-inline) var(--faq-pad-accroche);
  border-right: var(--faq-filet);
}

/* ⚠️ --fs-h3 et non --fs-h2 comme les autres titres de section, et c'est un
   choix contraint : la colonne ne fait que 250 px de large en grand écran, où
   --fs-h2 monterait à 48 px et casserait le titre en quatre lignes d'un mot.
   32 px est aussi la cote de la maquette.

   La couleur passe à --c-blue-zodiac, celle des titres de « Au cœur de
   Biron ». --text-strong tirait vers le gris du texte courant. */
.faq__titre {
  margin: 0;
  font-size: var(--fs-h3);
  line-height: 1.14;
  letter-spacing: -0.03em;
  color: var(--c-blue-zodiac);
}

/* ---------- Les questions ---------- */
/* ⚠️ AUCUN rembourrage latéral, et c'est ce qui rend les séparations pleine
   largeur : ce sont la question et la réponse qui portent le leur. Sur la
   liste, le filet de chaque question se serait arrêté à 44 px de chaque bord
   et aurait flotté au lieu de séparer. Même mécanique que les cartes de
   témoignages. */
.faq__liste {
  padding-block: clamp(var(--sp-5), 1.9vw, 2.25rem) var(--faq-pad-liste);
}

.faq__item {
  border-bottom: var(--faq-filet);
}

/* La dernière question n'a pas de filet : celui du panneau est juste en
   dessous, et les deux se doublaient en un trait épais. */
.faq__item:last-child {
  border-bottom: 0;
}

/* Le marqueur natif du <summary> n'a rien à faire là : le « + » le remplace.
   Il faut le neutraliser des deux façons — la standard et celle de WebKit. */
.faq__question {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-6);
  padding-block: clamp(var(--sp-4), 1vw, 1.25rem);
  padding-inline: var(--faq-pad-liste);
  font-family: var(--font-serif);
  font-weight: var(--fw-semibold);
  font-size: var(--fs-md);
  line-height: 1.35;
  letter-spacing: -0.01em;
  color: var(--text-strong);
  cursor: pointer;
  list-style: none;
  /* La transition est déclarée ICI et non dans l'état ouvert : sur l'état
     cible seulement, le retour à la fermeture ne s'animait pas. */
  transition: color var(--dur-base) var(--ease-out);
}

.faq__question::-webkit-details-marker {
  display: none;
}

/* Le signe : un « + » qui devient croix par une rotation d'un quart de tour,
   comme dans la maquette. La rotation est le seul mouvement de l'accordéon —
   la hauteur, elle, ne s'animerait qu'au prix d'`interpolate-size`, dont on
   se passe ici. */
.faq__signe {
  flex-shrink: 0;
  font-family: var(--font-sans);
  font-weight: 300;
  font-size: 1.3125rem; /* 21px */
  line-height: 1;
  /* `currentColor` et non une couleur à lui : le signe suit désormais le texte
     de sa question en toutes circonstances — au repos, au survol et à
     l'ouverture, où la question passe au bleu. Une couleur fixe le laissait
     bleu même quand le texte ne l'était pas, et il fallait alors penser à la
     tenir d'accord avec trois règles. */
  color: currentColor;
  transition: rotate var(--dur-fast) var(--ease-out);
}

.faq__item[open] .faq__signe {
  rotate: 45deg;
}

@media (hover: hover) {
  .faq__question:hover {
    color: var(--c-blue);
  }
  .faq__question:hover .faq__signe {
    scale: 1.15;
  }
}
/* ---------- Ouverture animée ----------
   La réponse apparaît en fondu et glisse de quelques pixels. On n'anime PAS
   la hauteur, et c'est un choix mesuré : la recette moderne
   (`::details-content` + `interpolate-size: allow-keywords`) figeait la
   hauteur de l'item — 220 px même refermé, constaté à la mesure. Elle aurait
   en outre rendu instable la correction de défilement ci-dessous, qui a
   besoin d'un changement de hauteur immédiat.

   Animer l'apparition du contenu suffit à donner le mouvement demandé, et ne
   dépend d'aucune propriété expérimentale.

   La durée passe par --dur-base, ramenée à 0,01 ms quand l'utilisateur
   demande moins d'animations (_base.scss) : le garde-fou est déjà en place. */
/* ---------- Deux chemins d'animation, un par moteur ----------

   Chrome et Firefox ne dévoilent pas un <details> de la même façon, et aucun
   des deux mécanismes ne marche partout. On pose donc les deux ; chaque
   navigateur prend celui qu'il sait faire.

   FIREFOX — l'animation par images clés sur la réponse (juste en dessous).
   Firefox joue les animations d'un contenu qui vient d'être rendu.

   CHROME — le dépliage de `::details-content` (bloc suivant). Chrome masque le
   contenu fermé par `content-visibility: hidden` et n'y RELANCE PAS les
   animations au dévoilement : l'animation par images clés y reste muette,
   c'est exactement l'écart constaté à l'usage. En revanche c'est Chrome qui
   sait animer une hauteur `auto`, ce que Firefox ne sait pas encore.

   La durée reste un multiple de --dur-base pour suivre la réduction de
   mouvement, et `--ease-editorial` est la courbe des entrées longues du
   site. */
.faq__item[open] .faq__reponse {
  animation: faq-reponse-apparait calc(var(--dur-base) * 1.6) var(--ease-editorial);
}

@keyframes faq-reponse-apparait {
  from {
    opacity: 0;
    translate: 0 -1rem;
    clip-path: inset(0 0 100% 0);
  }
  to {
    opacity: 1;
    translate: 0 0;
    clip-path: inset(0 0 0 0);
  }
}
/* Le dépliage, pour les moteurs qui savent interpoler une hauteur `auto`.

   `interpolate-size` s'hérite : on le pose sur l'item et non sur la racine,
   pas question d'ouvrir cette porte à tout le site pour une section.
   `content-visibility` doit être de la transition avec `allow-discrete`,
   sinon le contenu apparaît d'un bloc au lieu d'accompagner la hauteur. */
@supports (interpolate-size: allow-keywords) and (selector(::details-content)) {
  .faq__item {
    interpolate-size: allow-keywords;
  }
  .faq__item::details-content {
    block-size: 0;
    overflow: hidden;
    transition: block-size calc(var(--dur-base) * 1.6) var(--ease-editorial), content-visibility calc(var(--dur-base) * 1.6) allow-discrete;
  }
  .faq__item[open]::details-content {
    block-size: auto;
  }
}
/* Pendant que faq.js mesure les hauteurs, on force les états finaux et on
   coupe tout mouvement : sans ça il mesurerait le DÉBUT du dépliage — soit
   une hauteur nulle — et réserverait n'importe quoi. */
.faq--mesure .faq__item::details-content {
  transition: none !important;
}

.faq--mesure .faq__item[open]::details-content {
  block-size: auto !important;
}

.faq--mesure .faq__item:not([open])::details-content {
  block-size: 0 !important;
}

.faq--mesure .faq__reponse {
  animation: none !important;
}

/* La question ouverte prend la couleur d'accent. Ce repère-là ne dépend
   d'aucune animation : il reste visible même si l'utilisateur a demandé moins
   de mouvement. */
.faq__item[open] > .faq__question {
  color: var(--c-blue);
}

.faq__reponse {
  padding-block-end: clamp(var(--sp-4), 1.2vw, 1.375rem); /* 22px */
  padding-inline-start: var(--faq-pad-liste);
  /* La réponse s'arrête plus tôt que la question sur le bord droit : une ligne
     de texte qui court jusqu'au filet se lit mal. D'où le supplément, qui
     s'ajoute au rembourrage commun au lieu de le remplacer. */
  padding-inline-end: calc(var(--faq-pad-liste) + clamp(0px, 2vw, 2.5rem));
}

.faq__reponse p {
  margin: 0;
  font-size: var(--fs-base);
  line-height: var(--lh-normal);
  color: var(--text-strong);
  text-wrap: pretty;
}

/* ---------- La réponse manquante ----------
   La maquette du connecteur IA pose cinq questions et n'en répond qu'à une : les
   quatre autres portent cette mention plutôt qu'une réponse inventée (règle 8 de
   MAQUETTE.md, et le pourquoi est en tête de blocs/faq.njk).

   Elle reprend EXACTEMENT l'habillage de `.onglets-metiers__vide`, qui remplit le
   même office sur la page plateforme : chasse fixe, capitales, encre discrète.
   C'est ce qui la fait lire comme un ÉTAT et non comme une affirmation — la même
   phrase en corps de texte se lirait comme la réponse elle-même.

   ⚠️ LE SÉLECTEUR EST DESCENDANT, ET C'EST OBLIGATOIRE. `.faq__reponse p` juste
   au-dessus pèse une classe ET un type, donc il l'emporterait sur un
   `.faq__reponse-vide` seul — la mention repartirait en corps de texte à l'encre
   forte, sans le moindre message. Deux classes suffisent à reprendre la main. */
.faq__reponse .faq__reponse-vide {
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--text-muted);
}

/* =========================================================================
   Une seule colonne.

   L'accroche passe au-dessus des questions ; le filet vertical devient
   horizontal. La trame se réduit à une bande sur le bord gauche et les
   formes viennent chercher le bord droit — le panneau occupant presque toute
   la largeur, c'est tout ce qu'il leur reste.
   ========================================================================= */
@media (max-width: 60rem) {
  .faq {
    padding-block: var(--sp-section);
    padding-inline: var(--container-pad);
  }
  .faq__panneau {
    grid-template-columns: minmax(0, 1fr);
  }
  .faq__accroche {
    border-right: 0;
    border-bottom: var(--faq-filet);
  }
  .faq__grille {
    width: 22%;
  }
  .faq__forme--haut {
    --shape-w: 30%;
    top: 3%;
    left: 74%;
  }
  .faq__forme--bas {
    --shape-w: 20%;
    bottom: 4%;
    left: 80%;
  }
}
/* =========================================================================
   BLOC « appel » — blocs/appel.njk.
   Origine : « Prochaine étape », Figma 73:1449.

   Section bleu nuit, précédée par défaut de la bande halftone d'entrée. Le
   bloc sait aussi poser une bande de sortie quand il n'est PAS en fin de
   page — voir le champ `transition` en tête de blocs/appel.njk.

   Deux couches de décor, comme dans la maquette : une COURBE de points qui
   dérive lentement en bas, et un dégradé qui creuse le bord inférieur.
   ========================================================================= */
.appel {
  /* Deux encres, et pas une seule : les points ont besoin de contraste pour
     se lire sur le bleu nuit, le dégradé du bas a besoin d'être doux pour ne
     pas trancher. Avec une valeur unique, l'un des deux était toujours
     sacrifié — les points se noyaient.

     Les deux restent dans la famille demandée, entre le bleu marine du fond
     et le noir du texte. */
  --appel-encre-points: var(--c-blue-zodiac);
  /* L'encre du dégradé est passée en jeton (--c-blue-abysse) : le pied de page
     reprend exactement cette teinte, la fin de la page est d'un seul tenant. */
  --appel-encre-fond: var(--c-blue-abysse);
  position: relative;
  background-color: var(--c-blue-dark);
  /* En haut, la place du surtitre mono retiré est rendue au rembourrage : le
     titre ne doit pas monter d'un cran vers la bande de trame.
     En bas, la maquette réserve 202 px sous le dernier lien, la place que
     réclame la courbe. */
  padding-block: clamp(var(--sp-16), 5.6vw, 4.25rem) clamp(var(--sp-24), 12vw, 14.25rem);
  /* Le décor déborde volontairement : on le rogne ici. Pas de `sticky` dans
     cette section, donc aucun risque à poser un overflow. */
  overflow: clip;
}

/* ---------- Décor ---------- */
.appel__decor {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
}

/* La courbe de points, FIXE. Elle occupe la moitié basse de la section.

   Le masque est celui des bandes de transition : sa frontière haute suit une
   vague — c'est de là que vient la courbe — et il est pavable
   horizontalement, donc il couvre n'importe quelle largeur d'écran sans être
   étiré. Mêmes réglages que le partial halftone, pour que le grain soit le
   même d'un bout à l'autre du site.

   Une version qui dérivait lentement a été essayée puis écartée : Hugo
   préfère l'image fixe. La structure s'en trouve simplifiée — la dérive
   demandait une couche deux fois plus large pour que le raccord soit
   invisible. */
.appel__courbe {
  position: absolute;
  inset: auto 0 0;
  height: 62%;
  background-color: var(--appel-encre-points);
  mask-image: url("/assets/img/halftone-mask.svg");
  mask-size: auto 100%;
  mask-repeat: repeat-x;
  mask-position: center bottom;
  -webkit-mask-image: url("/assets/img/halftone-mask.svg");
  -webkit-mask-size: auto 100%;
  -webkit-mask-repeat: repeat-x;
  -webkit-mask-position: center bottom;
}

/* Le dégradé du bas, celui de la maquette : il creuse le bord inférieur et
   assoit la section. Volontairement bas et court — à 45 % de hauteur il
   noyait la zone où les points se lisent, et la courbe disparaissait dans
   son propre fond. */
.appel__fond-bas {
  position: absolute;
  inset: auto 0 0;
  height: 34%;
  background: linear-gradient(to top, var(--appel-encre-fond) 22%, transparent);
}

/* ---------- Contenu ---------- */
.appel__contenu {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 100%;
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--container-pad);
  text-align: center;
}

/* Corps propre, plus grand que le --fs-h2 des autres titres de section : ce
   bloc est le second moment le plus important de la page, juste derrière le
   hero, et il n'avait qu'un rapport de 2,7 avec son sous-titre là où le hero
   atteint 3,6. C'est exactement le défaut qui rendait le hero « pas assez
   premium » avant qu'on l'y corrige.

   Défini en FRACTION DU h1 et non par un clamp indépendant. Un premier essai
   avec son propre clamp passait devant le hero à 1440 px — 54,8 contre 51,9 —
   parce que les deux pentes se croisaient. Ici la hiérarchie tient par
   construction, à toutes les largeurs.

   À noter : le gain est surtout sensible sur les grands écrans. À 1440 px,
   --fs-h2 plafonnait déjà à 43 px, tout près de ce que la place autorise ;
   c'était un défaut de large écran, où le titre stagnait à 48 px quand le hero
   montait à 72. */
.appel__titre {
  margin: 0;
  max-width: 54rem; /* 860px */
  font-size: calc(var(--fs-h1) * 0.88);
  color: var(--c-white);
  text-wrap: balance;
}

.appel__sous-titre {
  margin: clamp(var(--sp-4), 1.4vw, var(--sp-5)) 0 0;
  max-width: 38rem;
  font-size: var(--fs-base);
  line-height: var(--lh-normal);
  /* Blanc et non plus casper : c'est du corps de texte sur fond sombre. Écrit
     en dur comme le titre, parce que ce bloc peint ses couleurs lui-même au
     lieu de passer par `.on-dark`. */
  color: var(--c-white);
}

/* ---------- Boutons ----------
   Le bouton est en starship et son survol part vers le BLANC — demandé. On
   garde la géométrie de `.btn` (hauteur, coin squircle, zone tactile) et on ne
   redéfinit que les couleurs. */
.appel__cta {
  margin-block-start: clamp(var(--sp-6), 2.2vw, var(--sp-8));
  padding-inline: 1.875rem; /* 30px */
  color: var(--c-blue-zodiac);
  background-color: var(--c-starship);
  border-color: var(--c-starship);
}

@media (hover: hover) {
  .appel__cta:hover {
    background-color: var(--c-white);
    border-color: var(--c-white);
  }
}
/* Le lien secondaire suit le même geste : starship au repos, blanc au
   survol. Deux appels à l'action qui ne réagiraient pas de la même façon
   donneraient l'impression que l'un des deux est inerte. */
/* `inline` et non `inline-flex` : en flex, la flèche restait seule à droite
   quand le libellé passait sur deux lignes. Dans le flux du texte, elle suit
   le dernier mot. */
.appel__lien {
  display: inline;
  margin-block-start: clamp(var(--sp-4), 1.4vw, 1.375rem);
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
  color: var(--c-starship);
  transition: color var(--dur-fast) var(--ease-out);
}

.appel__lien-fleche {
  display: inline-block;
  margin-inline-start: var(--sp-2);
  transition: transform var(--dur-fast) var(--ease-out);
}

@media (hover: hover) {
  .appel__lien:hover {
    color: var(--c-white);
  }
  .appel__lien:hover .appel__lien-fleche {
    transform: translateX(3px);
  }
}
/* =========================================================================
   Écrans étroits.

   La courbe monte plus haut : sur un écran étroit la section est plus haute
   que large, et une courbe cantonnée au tiers bas s'y perdrait.
   ========================================================================= */
@media (max-width: 60rem) {
  .appel {
    padding-block: var(--sp-10) clamp(var(--sp-16), 22vw, var(--sp-32));
  }
  .appel__courbe {
    height: 46%;
  }
  .appel__titre {
    max-width: none;
  }
}
/* =========================================================================
   Pied de page.

   Il est en --c-blue-zodiac (#0F1C3F) — demandé le 13/08/2026. Le bas de page
   descend ainsi d'un seul mouvement : le bleu nuit de la section finale, puis
   son dégradé, puis ce bleu-ci, sans couture ni palier.

   Le contraste est volontairement bas — un pied de page se consulte, il
   n'appelle pas le regard. Le seul élément vif est la mention « (à créer) »,
   qui doit se voir.
   ========================================================================= */
.pied {
  /* Exempté des 85 % du site, comme l'en-tête — voir `--echelle-chrome` dans
     _tokens.scss pour le mécanisme et ses limites. */
  zoom: var(--echelle-chrome);
  background-color: var(--c-blue-zodiac);
  padding-block: var(--sp-16) var(--sp-8);
  font-size: var(--fs-xs);
}

.pied__inner {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: var(--sp-12) var(--sp-16);
}

/* ---------- Marque ---------- */
.pied__marque {
  /* La phrase de réassurance ne dépasse pas trois lignes en grand écran :
     au-delà elle se lit comme un paragraphe, pas comme une signature.

     ⚠️ 30rem DEPUIS LE 01/09/2026, ET C'EST LA PHRASE QUI A CHANGÉ, PAS LA RÈGLE.
     La nouvelle fait 174 signes contre 110 à l'ancienne : à 26rem elle passait à
     QUATRE lignes, donc hors de la règle qui précède. 30rem la ramène à trois —
     mesuré, et c'est le premier cran qui y arrive.

     Le pied fait 1376 px de large : les 54 px repris par cette colonne sont pris
     sur les colonnes de liens, qui les avaient. */
  max-width: 30rem;
}

.pied__logo {
  display: block;
  font-family: var(--font-serif);
  font-size: 1.375rem;
  font-weight: var(--fw-bold);
  letter-spacing: var(--ls-tight);
  color: var(--text-on-inverse);
}

.pied__reassurance {
  margin-block-start: var(--sp-4);
  font-size: var(--fs-sm);
  line-height: var(--lh-normal);
  /* De la prose, donc à pleine encre. Les mentions légales et les titres de
     colonne du pied restent, eux, en casper : ce n'est pas du corps de texte. */
  color: var(--text-on-inverse);
}

/* ---------- Colonnes de liens ---------- */
.pied__colonnes {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-16);
}

.pied__colonne-titre {
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--c-casper);
}

.pied__liste {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  margin-block-start: var(--sp-5);
  padding: 0;
  list-style: none;
}

.pied__lien {
  font-size: var(--fs-sm);
  color: var(--text-on-inverse);
  transition: color var(--dur-fast) var(--ease-out);
}

/* ---------- Les réseaux ----------
   Sous la signature de marque, ajoutés le 01/09/2026.

   ⚠️ LA MARQUE PREND `currentColor`, C'EST TOUT L'INTÉRÊT DE L'INSCRIRE. Le pied
   est bleu nuit et son encre est `--text-on-inverse` ; une image posée en `<img>`
   aurait gardé sa couleur d'origine et il aurait fallu la fournir en blanc, donc
   la refournir le jour où le pied change de fond. */
.pied__reseaux {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  margin-block-start: var(--sp-6);
  padding: 0;
  list-style: none;
}

.pied__reseau {
  display: inline-flex;
  align-items: center;
  /* ⚠️ LA MARGE EST LÀ POUR LA CIBLE, PAS POUR L'ESPACEMENT. La marque fait
     22 px de côté ; nue, le lien offrait donc une cible de 22 px, sous les 24
     exigés au doigt. Ce rembourrage la porte à 29 sans rien changer au dessin.
     L'écart entre les deux entrées est tenu par le `gap` de la liste, qui en
     tient compte. */
  padding: var(--sp-1);
  margin: calc(var(--sp-1) * -1);
  color: var(--text-on-inverse);
  transition: color var(--dur-fast) var(--ease-out);
}

.pied__reseau-marque,
.pied__reseau-masque {
  inline-size: 1.375rem;
  block-size: 1.375rem;
}

/* ⚠️ LA COULEUR EST UN FOND, LA FORME EST UN MASQUE. Le fichier de Welcome to
   the Jungle est une silhouette noire sur transparent : le masque n'en lit que
   le canal alpha, et c'est `currentColor` qui peint. La marque suit donc l'encre
   du pied — blanche au repos, fluo au survol — sans qu'un second fichier soit à
   fournir pour chaque état.

   ⚠️ `contain` ET NON `100% 100%`, contrairement à `mix.mask` : le fichier est
   un carré de 512 px dont le dessin ne remplit pas tout le cadre. Étiré, il
   perdrait sa marge propre et paraîtrait plus gros que la marque LinkedIn, qui
   la porte dans son viewBox. */
.pied__reseau-masque {
  display: block;
  background-color: currentColor;
  -webkit-mask: var(--marque) center/contain no-repeat;
  mask: var(--marque) center/contain no-repeat;
}

/* ⚠️ LE REPLI EN TEXTE PORTE LE CORPS DES LIENS DU PIED, et non celui d'un
   pictogramme : sans dessin, l'entrée EST un lien comme les autres. Welcome to
   the Jungle est dans ce cas tant que sa marque n'est pas fournie. */
.pied__reseau--texte {
  font-size: var(--fs-sm);
}

@media (hover: hover) {
  .pied__reseau:hover {
    color: var(--c-fluo);
  }
}
@media (hover: hover) {
  a.pied__lien:hover {
    color: var(--c-fluo);
  }
}
/* Une entrée dont la page n'existe pas encore. Le contraste tombe pour
   qu'on ne la confonde pas avec un lien, et la mention passe en fluo pour
   qu'elle ne puisse pas se glisser en production sans qu'on la voie. */
.pied__lien--absent {
  color: var(--c-casper);
  cursor: default;
}

.pied__a-creer {
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  font-style: normal;
  color: var(--c-fluo);
}

/* ---------- Ligne du bas ---------- */
.pied__bas {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: var(--sp-2) var(--sp-8);
  margin-block-start: var(--sp-16);
  padding-block-start: var(--sp-6);
  border-block-start: 1px solid rgba(255, 255, 255, 0.12);
}

.pied__mention {
  color: var(--text-muted-on-inverse);
}

/* Réinitialisation de bouton : `.pied__consentement` porte déjà
   `.pied__mention` pour la couleur et la taille, il ne lui manque que
   d'arrêter de ressembler à un <button>. Le survol reprend celui de
   `.pied__lien` — la seule différence avec un vrai lien du pied est
   sémantique (une action, pas une destination), pas visuelle. */
.pied__consentement {
  padding: 0;
  background: none;
  border: none;
  font: inherit;
  cursor: pointer;
  transition: color var(--dur-fast) var(--ease-out);
}

@media (hover: hover) {
  .pied__consentement:hover {
    color: var(--c-fluo);
  }
}
/* =========================================================================
   Écrans étroits.
   ========================================================================= */
@media (max-width: 48rem) {
  .pied {
    padding-block: var(--sp-12) var(--sp-6);
  }
  .pied__inner {
    gap: var(--sp-10);
  }
  /* Les deux colonnes restent côte à côte tant qu'elles tiennent, et
     s'empilent d'elles-mêmes sinon — mesuré : vers 500 px elles cohabitent,
     à 390 px elles passent l'une sous l'autre, « Politique de
     confidentialité » étant trop large pour une demi-largeur. */
  .pied__colonnes {
    gap: var(--sp-8);
  }
  .pied__bas {
    margin-block-start: var(--sp-10);
  }
}
/* ---------------------------------------------------------------------
   Page courante — layouts/page.njk.

   Aucune des huit sections de l'accueil n'utilise ce fichier : il n'habille
   que le corps rédigé en Markdown des pages de texte (mentions légales,
   politique de confidentialité, à propos).

   Une seule idée : une colonne de lecture étroite. Le `container` du site
   fait 1280 px, largeur juste pour une mise en page à plusieurs blocs,
   beaucoup trop pour du texte au fil — on plafonne donc la mesure à 68
   caractères, qui est la limite de confort en lecture longue.
   --------------------------------------------------------------------- */
.page {
  padding-block: var(--sp-section);
}

/* ⚠️ PAS DE `font-weight` : c'est un <h1>, il prend le gras des titres déclaré
   dans _base.scss. Une --fw-regular y traînait, calibrée du temps où la police de
   titrage était Recoleta — une serif de display tient un titre à 400. Depuis que
   --font-titre est passé au Sora (13/08/2026), 400 est exactement la graisse du
   texte courant : le titre de page ne se lisait plus comme un titre.
   Corrigé le 25/08/2026 avec `.item__titre`. */
.page__titre {
  max-width: 40rem;
  font-family: var(--font-serif);
  font-size: var(--fs-h1);
  line-height: var(--lh-tight);
  letter-spacing: var(--ls-display);
  color: var(--text-strong);
}

.page__chapeau {
  max-width: 34rem;
  margin-block-start: var(--sp-5);
  font-size: var(--fs-eyebrow-lg);
  line-height: var(--lh-snug);
  color: var(--text-strong);
}

.page__corps {
  max-width: 42rem; /* ≈ 68 caractères dans la graisse et le corps du site */
  margin-block-start: var(--sp-10);
  font-size: var(--fs-base);
  line-height: var(--lh-normal);
  color: var(--text-strong);
}

/* Les éléments du corps sont produits par Markdown : on ne peut pas leur
   poser de classe, d'où les sélecteurs de balise, cantonnés au corps. */
.page__corps > * + * {
  margin-block-start: var(--sp-5);
}

/* Même correction que `.page__titre` ci-dessus : la graisse vient de _base.scss. */
.page__corps h2,
.page__corps h3 {
  font-family: var(--font-serif);
  line-height: var(--lh-tight);
  letter-spacing: var(--ls-tight);
}

.page__corps h2 {
  margin-block-start: var(--sp-12);
  font-size: var(--fs-h3);
}

.page__corps h3 {
  margin-block-start: var(--sp-8);
  font-size: var(--fs-md);
}

.page__corps a {
  color: var(--text-accent);
  text-decoration-thickness: 1px;
  text-underline-offset: 0.2em;
}

.page__corps ul,
.page__corps ol {
  padding-inline-start: var(--sp-6);
}

.page__corps li + li {
  margin-block-start: var(--sp-2);
}

.page__corps strong {
  font-weight: var(--fw-semibold);
}

/* =========================================================================
   BLOC « grille-articles » — blocs/grille-articles.njk.
   Plus l'en-tête d'article (layouts/article.njk), qui tient en trois règles
   et ne méritait pas sa propre feuille.

   Origine : la section 02 de la maquette du blog — Figma 144:5720.

   ⚠️ LES CARTES SONT INDÉPENDANTES — fond blanc, coins arrondis, filet discret —
   alors que la maquette les range dans un quadrillage fermé. C'est la quatrième
   branche de la règle 13 de MAQUETTE.md : une case qui porte son propre visuel et
   son propre bouton n'est pas une cellule de tableau, c'est une porte qu'on
   ouvre. Ce bloc rendait déjà des cartes détachées avant que la règle ne le dise
   — c'est lui qu'elle légitime rétroactivement.

   ⚠️ LES COTES SONT CELLES DES CARTES DE /cas-clients/, délibérément : ce sont
   deux grilles de cartes ouvrables sur le même site, et deux arrondis ou deux
   filets différents se liraient comme un oubli.
   ========================================================================= */
.grille-articles {
  position: relative;
  background-color: var(--bg-page);
  padding-block: var(--sp-section);
  /* ⚠️ 20px ET NON LES 28px DES BOÎTES DE SECTION : deux cartes posées dans une
     page qui porte déjà des boîtes arrondies à 28 donneraient deux arrondis
     concentriques qui se concurrencent. Même valeur que `.cas--carte`. */
  --carte-article-rayon: 1.25rem;
}

/* La boîte de la une porte déjà sa propre respiration basse : deux respirations
   de section qui s'additionnent cassent la page en deux. */
.une + .grille-articles {
  padding-top: clamp(var(--sp-6), 2vw, var(--sp-10));
}

/* =========================================================================
   L'EN-TÊTE — le titre à gauche, les filtres à droite.

   `align-items: end` cale la rangée de boutons sur le bas du titre, comme la
   maquette. En repli, `flex-wrap` la passe simplement dessous.

   L'apparence des filtres est la primitive partagée `.filtres` / `.filtre`
   (_components.scss), commune avec /cas-clients/ : rien à déclarer ici.
   ========================================================================= */
.grille-articles__entete {
  display: flex;
  flex-wrap: wrap;
  align-items: end;
  justify-content: space-between;
  gap: var(--sp-6);
  margin-block-end: clamp(var(--sp-8), 3vw, var(--sp-12));
}

/* Le titre ne déclare rien de sa police (règle 3 de MAQUETTE.md) : _base.scss
   pose déjà serif, graisse, interlignage et la taille du niveau. */
.grille-articles__titre {
  margin: 0;
  max-width: 30ch;
  color: var(--c-blue-zodiac);
  text-wrap: balance;
}

/* L'état vide — soit le blog sans article, soit un filtre qui ne montre rien. Il
   est sur le fond de page et non dans une carte : ce n'est pas un article, c'est
   un message. */
.grille-articles__vide {
  margin: 0;
  padding: clamp(var(--sp-10), 5vw, var(--sp-20));
  font-size: var(--fs-md);
  color: var(--text-muted);
  text-align: center;
}

/* =========================================================================
   LA GRILLE.

   `auto-fill` et non `auto-fit` : avec `auto-fit`, un seul article s'étirerait
   sur toute la largeur et sa carte ferait trois fois la taille des autres.
   À 20rem de plancher, on retrouve les trois colonnes de la maquette au-delà de
   1280px.
   ========================================================================= */
.grille-articles__liste {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 20rem), 1fr));
  gap: clamp(var(--sp-5), 2vw, var(--sp-8));
  margin: 0;
  padding: 0;
  list-style: none;
}

.grille-articles__cellule {
  display: flex;
}

/* ⚠️ `hidden` NE SUFFIT PAS QUAND UNE RÈGLE D'AUTEUR POSE `display`. La règle
   `[hidden] { display: none }` vient de la feuille du navigateur, et n'importe
   quelle déclaration d'auteur la bat : sans cette ligne, une carte masquée par
   filtres.js resterait visible et le filtre n'aurait aucun effet. */
.grille-articles__cellule[hidden] {
  display: none;
}

/* =========================================================================
   LA CARTE.

   ⚠️ LE CHROME EST SUR L'`<article>`, PAS SUR LA CELLULE, et c'est STRUCTUREL.
   La cellule porte `data-reveal`, donc sections/_reveal.scss lui impose son
   `transform: none` ET sa propre liste de `transition` à (0,3,1) de spécificité —
   `@use`é en dernier dans main.scss exprès pour gagner. Tant que le chrome vivait
   sur la cellule, le survol de ce bloc était réduit à son changement de filet, en
   silence, depuis l'arrivée de reveal.js.

   La leçon générale, écrite au long dans sections/_cas-clients.scss : **un
   élément qui porte `data-reveal` ne doit pas porter en plus une animation de
   survol.**
   ========================================================================= */
.carte-article {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: var(--sp-5);
  padding: clamp(var(--sp-5), 2vw, var(--sp-8));
  background-color: var(--c-white);
  /* ⚠️ LE FILET EST OBLIGATOIRE MALGRÉ « fond blanc » : le fond de page est
     --c-blue-50 (#eff1fa) et la carte est blanche, soit 1,05 de contraste. Sans
     contour elle s'efface. C'est la limite de la règle 1 de MAQUETTE.md — « le
     contraste fait l'arête » ne vaut que si le contraste est là. */
  border: 1px solid var(--filet-panneau-teinte);
  border-radius: var(--carte-article-rayon);
  /* La carte est blanche, la page est bleu pâle. Sans cette ligne, un
     `.btn--secondary` posé dedans se remplirait du fond de la page. */
  --section-bg: var(--c-white);
  /* ⚠️ `scale` ET `translate`, JAMAIS `transform` : ce sont des propriétés à
     part, qui se COMPOSENT avec `transform` au lieu d'y être — un
     `transform: none` venu de _reveal.scss ne peut donc pas les annuler. */
  transition: background-color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out), scale var(--dur-base) var(--ease-out), translate var(--dur-base) var(--ease-out);
}

/* Les quatre mêmes gestes que les cartes de /cas-clients/, aux mêmes valeurs :
   la teinte mélange --c-blue (donc suit la page si elle est une page pilier), le
   filet passe au bleu plein, la carte se soulève et grandit d'un cheveu. Aucune
   ombre — règle 5. */
@media (hover: hover) {
  .carte-article:hover {
    background-color: color-mix(in srgb, var(--c-blue) 4%, var(--c-white));
    border-color: var(--c-blue);
    scale: 1.03;
    translate: 0 -2px;
  }
}
/* Le zoom met en mouvement TOUT le contenu de la carte, texte compris : c'est
   exactement ce que `prefers-reduced-motion` demande d'éviter. La teinte et le
   filet restent — ce ne sont pas des mouvements. */
@media (prefers-reduced-motion: reduce) {
  .carte-article:hover {
    scale: 1;
    translate: none;
  }
}
/* Le focus clavier se voit sur la carte, sans anneau : c'est le BOUTON qui est
   cliquable, et il porte déjà le sien. */
.carte-article:focus-within {
  border-color: var(--c-blue);
}

/* Le ratio exact de la maquette (nœud I144:5740;49:3250, 365 × 205) — règle 6.
   Ici `.apercu` garde son fond clair : la maquette dessine bien un cadre clair
   dans ces cartes, contrairement à celui de la une. */
.carte-article__apercu {
  aspect-ratio: 365/205;
}

/* ---------- La ligne de métadonnées ----------
   Catégorie · temps de lecture. Le point est posé par le style : dans le DOM, un
   lecteur d'écran l'annoncerait. */
.carte-article__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-3);
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--text-muted);
}

.carte-article__meta > * + *::before {
  content: "·";
  margin-inline-end: var(--sp-3);
}

.carte-article__categorie {
  font-weight: var(--fw-bold);
  color: var(--text-accent);
}

/* ⚠️ ÉCART À LA RÈGLE 3, borné à ce bloc et de même nature que celui de
   `.cas--carte .cas__titre` : --fs-h3 va de 22 à 32px, la maquette donne 21px à
   ces titres. Le clamp PLAFONNE à la valeur de la maquette au lieu d'en faire son
   plancher — c'est le même jeton, borné à l'autre bout. */
.carte-article__titre {
  margin: 0;
  font-size: clamp(1.1875rem, 1.05rem + 0.45vw, 1.3125rem); /* 19 → 21px */
  color: var(--c-blue-zodiac);
  text-wrap: balance;
}

/* --text-strong et non --text-muted : c'est du corps de texte. */
.carte-article__chapeau {
  margin: 0;
  font-size: var(--fs-sm);
  line-height: var(--lh-normal);
  color: var(--text-strong);
}

/* ---------- Le pied ----------
   ⚠️ `margin-block-start: auto` EST LE DISPOSITIF D'ALIGNEMENT, et il évite tout
   un échafaudage de `subgrid`. Les cellules d'une même rangée ont la même hauteur
   (elles s'étirent), donc pousser le pied vers le bas suffit à ce que les dates
   et les boutons tombent à la même ligne d'une carte à l'autre — quel que soit le
   nombre de lignes du titre ou du chapeau. Une `min-height` serait fausse au
   premier changement d'intitulé, et fausse différemment à chaque largeur.

   Même dispositif que `.cas__chiffres`, pour la même raison. */
.carte-article__pied {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  margin: auto 0 0;
  padding-block-start: var(--sp-5);
  border-top: 1px solid var(--border-subtle);
}

.carte-article__date {
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--text-muted);
}

/* =========================================================================
   ARTICLE DE BLOG — layouts/article.njk.

   Origine : « Biron - Article blog - template » (Figma 144:5296).

   Deux blocs dans ce fichier, et la séparation est celle du gabarit :

     .article    l'ENVELOPPE — fil, bandeau de titre, grille, signature, pied.
                 Ce qui entoure la lecture.
     .corps      le TEXTE RÉDIGÉ — sections, titres, listes, encarts,
                 tableaux, fiches. Ce que l'auteur écrit en Markdown.

   ⚠️ `.corps` NE PEUT PAS PORTER DE CLASSE SUR SES ÉLÉMENTS : ils sont
   produits par Markdown. D'où des sélecteurs de balise, cantonnés au bloc —
   même contrainte et même parti que `.page__corps` (sections/_page.scss), qui
   habille, lui, les pages de texte légal. Les deux ne se confondent pas :
   `.page__corps` est une colonne de texte au fil, `.corps` est un article
   structuré en sections numérotées.

   Les classes qu'on voit ici — `.corps__section`, `.corps-tableau`,
   `.corps-fiche` — sont posées par lib/article.js et par le shortcode
   `{% fiches %}`, pas écrites à la main dans le Markdown.
   ========================================================================= */
.article {
  padding-block: var(--sp-8) var(--sp-section);
  /* Hauteur RÉELLE de l'en-tête collant du site (`--h-entete`, jeton — l'en-tête
     est contre-zoomé, sa cote écrite et sa hauteur à l'écran diffèrent)
     plus une respiration. Deux consommateurs, et c'est ce qui les garde
     d'accord : le décrochage du sommaire, et le `scroll-margin-top` des
     sections — donc le point d'arrivée d'un clic dans le sommaire. Même
     jeton, même raison que `--coeur-colle`. */
  --article-colle: calc(var(--h-entete) + var(--sp-6));
}

.article__fil {
  margin-block-end: var(--sp-6);
}

/* ---------- Le bandeau de titre ----------
   Le filet de pied court sur la largeur de la colonne de texte : il est donc
   sur ce bloc, à l'intérieur du container, et non sur le container lui-même —
   dont le rembourrage latéral l'aurait fait dépasser de part et d'autre. */
.article__bandeau {
  padding-block-end: clamp(var(--sp-8), 3.5vw, var(--sp-12));
  border-block-end: 1px solid var(--border-subtle);
}

.article__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2) var(--sp-3);
  margin: 0 0 var(--sp-6);
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--text-muted);
}

/* Le point séparateur est posé par le style et non écrit dans le gabarit : il
   est décoratif, et un lecteur d'écran n'a pas à énoncer « point médian »
   entre deux métadonnées. Même technique que le fil d'Ariane. */
.article__meta > * + *::before {
  content: "·";
  margin-inline-end: var(--sp-3);
}

/* La catégorie est le seul élément coloré de la ligne — c'est ce qui la
   désigne comme la rubrique et non comme une métadonnée de plus. */
.article__categorie {
  color: var(--text-accent);
}

/* ⚠️ --fs-h2 ET NON --fs-h1 SUR UN `<h1>`, et c'est mesuré : la maquette pose
   le titre à 46 px (Figma 144:5320), ce que --fs-h2 vaut exactement en haut de
   sa plage — et il n'a pas de seconde bande au-delà de 1280 px, contrairement
   à --fs-h1 qui monte à 72. Un titre d'article est une PHRASE, pas un slogan
   de hero : à 72 px, trois lignes de titre remplissent l'écran avant le
   premier mot du texte.

   La police, la graisse et l'interlettrage ne sont pas redéclarés — _base.scss
   les pose déjà sur tout `<h1>` (règle 3 de MAQUETTE.md). */
.article__titre {
  max-width: 26ch;
  margin: 0;
  font-size: var(--fs-h2);
  color: var(--c-blue-zodiac);
}

/* --text-strong et non --text-muted : le chapeau se lit comme du texte
   courant, et le corps de texte est à pleine encre (voir le commentaire de
   --text-strong dans _tokens.scss). */
.article__chapeau {
  max-width: 46ch;
  margin: var(--sp-5) 0 0;
  font-size: var(--fs-eyebrow-lg);
  line-height: var(--lh-snug);
  color: var(--text-strong);
}

/* =========================================================================
   LA GRILLE — colonne de lecture et sommaire.

   La maquette : 760 px de texte, 250 px de sommaire, 190 px entre les deux
   dans une grille de 1280 (Figma 144:5323). On garde les deux largeurs et on
   laisse l'espace restant se répartir, plutôt que de figer la gouttière : la
   colonne de lecture est PLAFONNÉE et non élastique, sinon sur un grand écran
   elle s'élargirait bien au-delà de la mesure de confort.
   ========================================================================= */
.article__grille {
  display: grid;
  gap: clamp(var(--sp-10), 5vw, var(--sp-24));
  padding-block-start: clamp(var(--sp-8), 3vw, var(--sp-12));
}
@media (min-width: 60rem) {
  .article__grille {
    grid-template-columns: minmax(0, 47.5rem) minmax(0, 15.625rem);
    justify-content: space-between;
    align-items: start;
  }
}

/* Le sommaire est AVANT la colonne dans le DOM (voir le gabarit) : au large,
   la grille les remet dans l'ordre visuel. Rien à faire sous le point de
   rupture, où l'empilement du DOM est déjà le bon. */
.article__colonne {
  min-width: 0;
}
@media (min-width: 60rem) {
  .article__colonne {
    grid-area: 1/1;
  }
}

/* ---------- La ligne de signature ---------- */
.article__signature {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4) var(--sp-6);
  padding-block: var(--sp-4);
  border-block-end: 1px solid var(--border-subtle);
}

.article__auteur {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--sp-2) var(--sp-3);
  margin: 0;
  font-size: var(--fs-sm);
  color: var(--text-muted);
}

.article__nom {
  font-size: var(--fs-base);
  font-weight: var(--fw-semibold);
  color: var(--text-strong);
}

.article__role::before {
  content: "·";
  margin-inline-end: var(--sp-3);
}

.article__partage {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
}

.article__partage-intitule {
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--text-muted);
}

/* ---------- Le pied de lecture ---------- */
.article__pied {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-5);
  margin-block-start: clamp(var(--sp-10), 4vw, var(--sp-16));
  padding-block-start: var(--sp-6);
  border-block-start: 1px solid var(--border-subtle);
}

.article__pied-texte {
  margin: 0;
  font-size: var(--fs-base);
  color: var(--text-strong);
}

/* =========================================================================
   LE PARTAGE — partials/partage.njk.

   Quatre carrés arrondis à filet. Le survol passe à l'accent plutôt qu'au
   noir : ce sont des actions, pas du texte.
   ========================================================================= */
.partage {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
}

/* Le même sélecteur pour les trois `<a>` et le `<button>` : rien ne les
   distingue à l'œil, et le bouton doit remettre à zéro ce qu'un agent
   utilisateur met sur lui (fond, bordure, police). */
.partage__lien {
  display: grid;
  place-items: center;
  width: 2.125rem;
  height: 2.125rem;
  padding: 0;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  background-color: transparent;
  color: var(--text-muted);
  font: inherit;
  cursor: pointer;
  transition: color var(--dur-base) var(--ease-out), border-color var(--dur-base) var(--ease-out);
}

.partage__lien:hover,
.partage__lien:focus-visible {
  color: var(--text-accent);
  border-color: var(--text-accent);
}

/* La coche du retour de copie. Le libellé lu à la voix est dans le gabarit —
   ceci n'en est que la trace visible, d'où l'accent sur le cadre entier
   plutôt qu'une pastille de plus. */
.partage__lien[data-copie] {
  color: var(--text-accent);
  border-color: var(--text-accent);
}

.partage__icone {
  width: 1.125rem;
  height: 1.125rem;
}

/* Les deux icônes d'ACTION sont au filet, les deux marques sont pleines. Le
   `fill: none` est indispensable : sans lui, un tracé ouvert est rempli par
   défaut et l'enveloppe devient un pâté. */
.partage__icone--filet {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* =========================================================================
   LE SOMMAIRE — partials/sommaire.njk.

   ⚠️ COLLANT, tranché par Hugo le 21/08/2026 contre un sommaire figé en haut
   de page comme dans la maquette : sur un article de 7 000 px, un plan qui
   sort de l'écran au deuxième écran ne sert plus à rien.

   ⚠️ IL NE COLLE QU'AU-DESSUS DU POINT DE RUPTURE. Empilé au-dessus du texte,
   il n'a pas de colonne à lui : collé, il recouvrirait la lecture.

   Le `max-height` + `overflow-y` n'est pas une précaution théorique : un
   article à quinze sections dépasse la hauteur d'écran, et sans lui les
   dernières entrées seraient inatteignables.
   ========================================================================= */
@media (min-width: 60rem) {
  .sommaire {
    grid-area: 1/2;
    position: sticky;
    top: var(--article-colle);
    max-height: calc(100vh - var(--article-colle) - var(--sp-8));
    overflow-y: auto;
  }
}

.sommaire__intitule {
  margin: 0 0 var(--sp-4);
  padding-block-end: var(--sp-4);
  border-block-end: 1px solid var(--border-subtle);
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--text-muted);
}

.sommaire__liste {
  margin: 0;
  padding: 0;
  list-style: none;
  counter-reset: none;
}

.sommaire__entree + .sommaire__entree {
  border-block-start: 1px solid var(--border-subtle);
}

.sommaire__lien {
  display: block;
  padding-block: var(--sp-3);
  font-size: var(--fs-sm);
  line-height: var(--lh-snug);
  color: var(--text-muted);
  transition: color var(--dur-base) var(--ease-out);
}

.sommaire__lien:hover,
.sommaire__lien:focus-visible {
  color: var(--text-strong);
}

/* L'entrée en cours de lecture, posée par sommaire.js. Deux marques et non
   une : la couleur seule ne suffirait pas à qui ne distingue pas le bleu du
   gris — la graisse la double. */
.sommaire__lien[aria-current] {
  color: var(--text-accent);
  font-weight: var(--fw-semibold);
}

/* =========================================================================
   LE CORPS RÉDIGÉ.
   ========================================================================= */
.corps {
  font-size: var(--fs-base);
  line-height: var(--lh-normal);
  color: var(--text-strong);
}

/* ---------- Les sections ----------
   ⚠️ NI FILET NI RANG D'OUVERTURE, retirés le 21/08/2026 : « il faut enlever
   les chiffres et les séparateurs entre les parties », et « on peut augmenter
   l'espace entre 2 parties ». Les deux demandes vont ensemble — c'est
   maintenant l'ESPACE seul qui marque la coupure, là où la maquette la marquait
   par un trait. D'où un dégagement franchement plus grand qu'avant (48→80px
   contre 32→48) : sous un trait, une petite respiration suffit ; sans trait,
   elle doit être lisible à elle seule.

   Toutes les sections prennent le même dégagement, l'introduction comprise. La
   variante `--intro` qui l'en dispensait n'existait que pour lui épargner un
   filet qu'elle n'a plus.

   `scroll-margin-top` : le point d'arrivée d'un clic dans le sommaire. Sans
   lui, la section se cale sous l'en-tête collant et son titre disparaît. */
.corps__section {
  padding-block-start: clamp(var(--sp-12), 5vw, var(--sp-20));
  scroll-margin-top: var(--article-colle);
}

/* La dernière phrase d'une conclusion est composée un cran au-dessus du texte
   courant : la maquette lui donne sa propre respiration (Figma 144:5661).
   Automatique, donc rien à demander à l'auteur — la chute d'une conclusion est
   toujours son dernier paragraphe. */
.corps__section--chute > p:last-child {
  margin-block-start: var(--sp-8);
  font-size: var(--fs-eyebrow-lg);
  line-height: var(--lh-snug);
  color: var(--c-blue-zodiac);
}

/* ---------- Les titres ----------
   ⚠️ CHAQUE TITRE PORTE SA MARGE BASSE, et c'est le défaut corrigé le
   21/08/2026 — « il y a des problèmes d'interlignages notamment entre les H2 et
   le texte qui suit ». Les titres étaient à `margin: 0` et les paragraphes
   n'avaient qu'une marge BASSE : entre un titre et son premier paragraphe, il
   n'y avait donc rien du tout. Le rythme du corps est descendant — chaque bloc
   pousse le suivant, jamais l'inverse — donc un titre sans marge basse colle à
   son texte.

   ⚠️ ET L'INTERLIGNE DES TITRES EST DESSERRÉ. `--lh-tight` (1,08) est calibré
   pour un titre d'affiche de 72 px ; à 32 px dans une colonne de lecture, un
   titre de section sur deux lignes s'y écrase. 1,2 est écrit en clair faute de
   jeton entre --lh-tight (1,08) et --lh-snug (1,35), qui est un interligne de
   sous-titre — trop lâche pour un titre.

   Seules la taille, la couleur et l'interligne sont posés : _base.scss tient
   déjà la police, la graisse et l'interlettrage (règle 3 de MAQUETTE.md).

   --fs-h3 sur un `<h2>` et non --fs-h2, pour la raison qui vaut déjà sur le
   titre de l'article : dans une colonne de 760 px, un titre de section à
   48 px pèse autant que le titre de la page. */
.corps__titre {
  margin: 0 0 var(--sp-6);
  font-size: var(--fs-h3);
  line-height: 1.2;
  color: var(--c-blue-zodiac);
}

/* Le dégagement d'un `<h3>` est plus court que celui d'une section : il coupe à
   l'intérieur d'une partie, pas entre deux. */
.corps h3 {
  margin-block: clamp(var(--sp-10), 3.5vw, var(--sp-12)) var(--sp-5);
  font-size: var(--fs-md);
  line-height: 1.3;
  color: var(--c-blue-zodiac);
}

.corps h4 {
  margin-block: var(--sp-8) var(--sp-4);
  font-size: var(--fs-base);
  line-height: 1.3;
  color: var(--c-blue-zodiac);
}

/* ---------- Le texte au fil ----------
   --sp-6 et non --sp-5 : à un interligne de 1,6, une marge de 20 px se
   confondait avec l'espace entre deux lignes d'un même paragraphe. */
.corps p {
  margin-block: 0 var(--sp-6);
}

.corps > *:last-child,
.corps__section > *:last-child {
  margin-block-end: 0;
}

.corps a {
  color: var(--text-accent);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.2em;
}

.corps strong {
  font-weight: var(--fw-semibold);
  color: var(--c-blue-zodiac);
}

/* =========================================================================
   LES LISTES.

   ⚠️ LES DEUX SONT DES RANGÉES À FILET, pas des puces indentées, et c'est ce
   que montre la maquette (Figma 144:5368 pour la liste à puces, 144:5546 pour
   la liste numérotée) : chaque entrée est séparée de la suivante par un trait,
   comme les rangées d'un tableau. Le filet est sur l'entrée SUIVANTE et non
   entre toutes, sinon la première ouvrirait sur un trait qui ne sépare rien.

   ⚠️ LE MARQUEUR EST UN `::before` ET NON UN `::marker`. `::marker` n'accepte
   qu'une poignée de propriétés — ni `width`, ni `border-radius`, ni un
   positionnement — donc ni le point rond de la liste à puces, ni le `01` mono
   aligné sur la première ligne de la liste numérotée.
   ========================================================================= */
/* --sp-8, comme l'encart, les tableaux et les fiches : tous les blocs du corps
   se décrochent du texte au fil de la même façon. */
.corps ul,
.corps ol {
  margin-block: var(--sp-8);
  padding: 0;
  list-style: none;
}

.corps li {
  position: relative;
  padding-block: var(--sp-4);
  padding-inline-start: var(--sp-8);
}

.corps li + li {
  border-block-start: 1px solid var(--border-subtle);
}

/* Un paragraphe seul dans une entrée ne porte pas la marge du texte au fil :
   c'est le rembourrage de l'entrée qui fait l'air. */
.corps li > p {
  margin-block: 0;
}

.corps li > p + p {
  margin-block-start: var(--sp-3);
}

/* Le point de la liste à puces. `top` en em plutôt qu'en rem : il suit le
   corps du texte, donc reste sur la première ligne quelle que soit la taille
   de police héritée. */
.corps ul > li::before {
  content: "";
  position: absolute;
  inset-inline-start: 0;
  top: 0.85em;
  width: 0.375rem;
  height: 0.375rem;
  border-radius: var(--radius-full);
  background-color: var(--text-accent);
}

/* La liste numérotée. `decimal-leading-zero` donne « 01 » sans qu'aucun
   numéro ne soit écrit dans le Markdown — l'auteur écrit `1.`, `2.`… et
   l'insertion d'une étape ne demande rien à renuméroter. */
.corps ol {
  counter-reset: corps-etape;
}

.corps ol > li {
  counter-increment: corps-etape;
  padding-inline-start: var(--sp-12);
}

.corps ol > li::before {
  content: counter(corps-etape, decimal-leading-zero);
  position: absolute;
  inset-inline-start: 0;
  top: calc(var(--sp-4) + 0.2em);
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-wide);
  color: var(--text-accent);
}

/* ⚠️ LE PREMIER GRAS D'UNE ENTRÉE NUMÉROTÉE DEVIENT SON TITRE, et c'est ce qui
   permet d'écrire les étapes de la maquette en Markdown pur :

       1. **Centraliser les données sources**
          Repartir des événements bruts…

   markdown-it rend cette entrée comme un seul paragraphe où le gras ouvre le
   texte ; le passer en `display: block` sépare le titre de sa description sans
   qu'aucun balisage ne soit demandé à l'auteur.

   ⚠️ SEULEMENT DANS UN `<ol>`, ET C'EST LOAD-BEARING. La liste à puces de la
   maquette (Figma 144:5368) ouvre deux de ses quatre entrées sur un gras
   INLINE — « **Meta** s'attribue du chiffre d'affaires… » — et les deux autres
   au milieu de la phrase. Appliqué aux `<ul>`, ce sélecteur aurait détaché en
   titre le nom de l'outil dans deux entrées sur quatre, et laissé les deux
   autres en ligne : la même liste rendue de deux façons.

   Le partage à retenir : dans une liste NUMÉROTÉE le gras d'ouverture est le
   titre de l'étape, dans une liste à PUCES le gras n'est que du gras. C'est
   documenté dans CONTENU.md.

   Les deux sélecteurs couvrent les deux rendus de markdown-it : une liste
   aérée (une ligne vide entre les entrées) emballe chaque entrée dans un
   `<p>`, une liste serrée non.

   Le titre reprend la police des titres — c'est bien un intitulé — mais garde
   `--fs-base` : dans une entrée de liste, un cran de plus le mettrait au
   niveau d'un h3. */
.corps ol > li > p:first-child > strong:first-child,
.corps ol > li > strong:first-child {
  display: block;
  margin-block-end: var(--sp-2);
  font-family: var(--font-titre);
  font-size: var(--fs-base);
  line-height: var(--lh-snug);
  color: var(--c-blue-zodiac);
}

/* =========================================================================
   L'ENCART — la citation Markdown.

   « Point clé », « À retenir » (Figma 144:5385 et 144:5483) : un aplat bleu
   très clair, un filet vertical à l'accent, et un intitulé mono facultatif.

   ⚠️ IL S'ÉCRIT AVEC UNE CITATION MARKDOWN, et c'est ce qui le rend
   accessible à l'écriture :

       > **Point clé**
       > Une même commande peut être rattachée à…

   Le premier gras devient l'intitulé, par le même mécanisme que les entrées de
   liste ci-dessus. Sans gras d'ouverture, l'encart n'a pas d'intitulé — c'est
   le troisième de la maquette (Figma 144:5595), qui n'en porte pas.

   ⚠️ --c-blue-100 ET NON --c-blue-50 : depuis le 21/08/2026 le fond de page
   EST --c-blue-50, un encart de cette teinte serait invisible. Le contraste
   avec le fond reste faible et c'est assumé — c'est le filet vertical qui
   marque l'encart, comme dans la maquette.

   ⚠️ CE N'EST PAS UN EXERGUE. L'exergue de `texte-module` est une phrase que
   la PAGE prononce, derrière une barre fluo, sans guillemets ni attribution
   (règle 17). Ici l'objet est un aparté du texte, pas une prise de parole.
   ========================================================================= */
.corps blockquote {
  margin-block: var(--sp-8);
  padding: var(--sp-6);
  border-inline-start: 2px solid var(--c-blue);
  border-start-end-radius: var(--radius-cadre);
  border-end-end-radius: var(--radius-cadre);
  background-color: var(--c-blue-100);
}

.corps blockquote > *:last-child {
  margin-block-end: 0;
}

.corps blockquote p:first-child > strong:first-child {
  display: block;
  margin-block-end: var(--sp-3);
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--text-accent);
}

/* =========================================================================
   LES TABLEAUX.

   Deux habillages, et le second se déclare par un gras sur le dernier
   en-tête (voir lib/article.js) :

     .corps-tableau            rangée d'en-têtes BLEU NUIT, corps blanc à
                               filets. C'est le tableau de la maquette
                               (Figma 144:5396).
     .corps-tableau--phare     la dernière colonne portée par une carte bleue
                               qui DÉBORDE en haut et en bas.

   ⚠️ LE SECOND EST UN EMPRUNT AU BLOC `comparatif`, et c'est un choix d'Hugo
   du 21/08/2026 : « on peut le faire comme le tableau comparatif de la page
   Pourquoi Biron ». La maquette de l'article mettait sa colonne sur un aplat
   clair sous une rangée d'en-têtes sombre (Figma 144:5609) ; l'aplat bleu
   débordant est le langage déjà en place sur le site pour désigner « notre »
   colonne, et le reprendre évite une troisième peau de tableau.

   Ce qui est emprunté est la TECHNIQUE et les COTES, pas la composition
   entière : ni plateau titré, ni logo dans l'en-tête, ni bascule en une carte
   par critère. Le comparatif porte cinq colonnes dans 1190 px ; ici il y en a
   deux ou trois dans 760, et les cellules restent lisibles jusqu'au point de
   rupture.

   Les trois pièces qui font le débord — plateau relatif dont le rembourrage
   RÉSERVE l'excroissance, carte en absolu qui le recouvre, tableau au-dessus
   par `z-index` avec les cellules de la colonne TRANSPARENTES — sont décrites
   au long dans sections/_comparatif.scss. Ne pas les remonter ici : ce fichier
   n'en est qu'un second consommateur.
   ========================================================================= */
.corps-tableau {
  /* La colonne des critères, comme dans le comparatif : 220 des 1190 px de la
     maquette. Elle sert au premier `<th>` ET au calcul de la position de la
     carte — les deux doivent lire la même valeur. */
  --corps-critere-w: 19%;
  /* Le débord de la carte, EN HAUT COMME EN BAS : une seule variable, c'est ce
     qui les garde symétriques. En haut c'est la hauteur minimale de la rangée
     d'en-têtes, en bas le rembourrage du plateau. */
  --corps-debord: clamp(2.5rem, 1.8rem + 2.2vw, 3.5rem);
  --corps-radius: var(--radius-lg);
  --corps-pad-block: var(--sp-4);
  --corps-pad-inline: clamp(var(--sp-4), 0.5rem + 1.2vw, var(--sp-6));
  margin-block: var(--sp-8);
}

.corps-tableau table {
  width: 100%;
  border-collapse: collapse;
  text-align: start;
  font-size: var(--fs-sm);
  line-height: var(--lh-normal);
}

.corps-tableau th,
.corps-tableau td {
  padding: var(--corps-pad-block) var(--corps-pad-inline);
  text-align: start;
  vertical-align: middle;
}

/* ---------- L'habillage ordinaire ----------
   ⚠️ `overflow: auto` FAIT LES DEUX, et c'est pour ça qu'il n'est pas `hidden` :
   il rogne les coins de la rangée sombre — un `border-radius` sur le `<table>`
   seul n'y suffit pas, `border-collapse` remet les cellules à angle droit — ET
   il donne au tableau son propre défilement. `hidden` aurait rendu
   inatteignable la fin d'un tableau à cinq colonnes écrit un jour dans un
   article, sans que rien ne le signale.

   `auto` et non `auto` sur le seul axe horizontal : `overflow-x` seul force de
   toute façon l'axe vertical à `auto`, autant l'écrire. */
.corps-tableau:not(.corps-tableau--phare) {
  overflow: auto;
  border: 1px solid var(--border-subtle);
  border-radius: var(--corps-radius);
}

.corps-tableau:not(.corps-tableau--phare) thead th {
  background-color: var(--c-blue-dark);
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--c-white);
}

.corps-tableau:not(.corps-tableau--phare) tbody td {
  background-color: var(--c-white);
}

.corps-tableau:not(.corps-tableau--phare) tbody tr + tr td {
  border-block-start: 1px solid var(--border-subtle);
}

/* ⚠️ L'EMPHASE DE LA PREMIÈRE COLONNE NE VAUT QUE POUR LE TABLEAU À COLONNE
   PHARE, et la distinction est réelle : là, la première colonne est l'INTITULÉ
   de la rangée et les suivantes sont les valeurs comparées — l'écart d'encre
   est ce qui les sépare, comme dans le comparatif. Le tableau ordinaire de la
   maquette (Figma 144:5396) confronte, lui, deux colonnes de même rang :
   « Outils différents » face à « Outils similaires ». Y appuyer la première
   aurait fait croire à une hiérarchie qui n'existe pas. */
.corps-tableau--phare tbody td:first-child {
  font-weight: var(--fw-medium);
  color: var(--c-blue-zodiac);
}

.corps-tableau--phare tbody td:not(:first-child) {
  color: var(--text-muted);
}

/* ---------- La colonne phare ---------- */
.corps-tableau--phare {
  position: relative;
  padding-block: 0 var(--corps-debord);
}

/* `table-layout: fixed` est STRUCTUREL et non cosmétique : c'est ce qui
   garantit que les colonnes valent exactement ce que la carte calcule. En
   `auto`, une valeur longue élargirait la dernière colonne et la carte ne
   tomberait plus dessus. */
.corps-tableau--phare table {
  position: relative;
  z-index: 1;
  table-layout: fixed;
}

.corps-tableau--phare th:first-child {
  width: var(--corps-critere-w);
}

/* La carte. Sa géométrie se calcule depuis `--corps-cols`, publié en ligne par
   lib/article.js : le nombre de colonnes comparées. La phare est toujours la
   DERNIÈRE, d'où un simple `100% - une colonne` — rien n'est écrit en dur,
   ajouter une colonne au tableau déplace la carte tout seul. */
.corps-tableau__phare {
  --corps-col-w: calc(
    (100% - var(--corps-critere-w)) / var(--corps-cols, 2)
  );
  position: absolute;
  inset-block: 0;
  z-index: 0;
  inset-inline-start: calc(100% - var(--corps-col-w));
  width: var(--corps-col-w);
  border-radius: var(--corps-radius);
  /* ⚠️ --btn-primary-bg ET NON --c-blue EN DIRECT : c'est un aplat qui porte
     du BLANC, et la règle 10 de MAQUETTE.md veut que tous les aplats de ce
     genre lisent la même variable — sur une page pilier, --c-blue sortirait
     un texte blanc à 2,12 sur l'orange. Le défaut est dans le repli du
     `var()`, jamais dans une déclaration à part qui masquerait la valeur
     posée par `.ton-page--*`. */
  background-color: var(--btn-primary-bg, var(--c-blue));
}

/* La rangée d'en-têtes est HORS du panneau clair — celui-ci est porté par les
   cellules du CORPS — et tombe donc dans le débord haut de la carte. Sur une
   cellule de tableau, `height` agit comme un minimum : la rangée vaut donc le
   débord, et centrer dans la cellule centre dans le débord. */
.corps-tableau--phare thead th {
  height: var(--corps-debord);
  padding-block: var(--sp-2);
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--text-muted);
}

/* L'en-tête de la colonne phare est déjà sur la carte : son encre est le
   fluo de la maquette, qui porte 14,9 sur le bleu d'action. */
.corps-tableau--phare thead th:last-child {
  color: var(--c-fluo);
}

.corps-tableau--phare tbody td {
  background-color: var(--c-white);
  border-block-start: 1px solid var(--border-subtle);
}

.corps-tableau--phare tbody tr:first-child td {
  border-block-start: 0;
}

/* Les quatre coins du panneau, posés sur les cellules d'extrémité : c'est la
   contrepartie de ne pas avoir de conteneur autour du corps du tableau — un
   conteneur aurait empêché la carte de le dépasser. */
.corps-tableau--phare tbody tr:first-child td:first-child {
  border-start-start-radius: var(--corps-radius);
}

.corps-tableau--phare tbody tr:last-child td:first-child {
  border-end-start-radius: var(--corps-radius);
}

/* ⚠️ FOND TRANSPARENT, et c'est par elles que la carte se voit. Une couleur
   posée ici, même celle de la carte, casserait le débord : la carte n'aurait
   plus rien à dépasser.

   Le filet de rangée continue à travers la carte, en blanc voilé — le filet du
   site (du noir à 12 %) disparaît sur l'aplat, et c'est ce trait qui fait lire
   la rangée d'un bout à l'autre. */
.corps-tableau--phare tbody td:last-child {
  background-color: transparent;
  border-block-start-color: rgba(255, 255, 255, 0.28);
  color: var(--c-white);
  font-weight: var(--fw-medium);
}

/* ---------- Sous le point de rupture ----------
   ⚠️ LA CARTE DÉBORDANTE DISPARAÎT, et il n'y a pas d'alternative : sa
   géométrie EST une colonne du tableau, et à cette largeur les colonnes ne
   tiennent plus. C'est la CELLULE de la solution qui reprend l'aplat, dans
   chaque rangée — le repère survit donc à la bascule, comme dans le
   comparatif.

   Le tableau redevient `auto` : sans carte à viser, la largeur fixe n'a plus
   de raison d'être et elle serrerait les colonnes sans profit.

   ⚠️ L'EN-TÊTE DE LA COLONNE PHARE REPREND L'APLAT LUI AUSSI, et ce n'est pas
   une finition : son encre est le fluo, calibré sur la carte. La carte partie,
   il ne restait sous lui que le fond de page — le bleu clair des pages
   éditoriales (`--c-blue-50`) — où le fluo tombe à 1,1 : illisible. Deux
   sorties, et c'est l'aplat qui gagne. Changer l'encre (l'en-tête reprend
   `--text-muted` comme ses voisins) rend le libellé lisible mais efface le
   seul repère qui reste de « notre » colonne au-dessus des cellules déjà
   bleues, alors que l'aplat garde le couple fluo-sur-bleu qui EST le langage
   du site — 5,0 sur le bleu d'action, davantage sur une page pilier où
   `--btn-primary-bg` est assombri.

   La colonne devient alors un bloc bleu continu de l'en-tête à la dernière
   rangée : ses quatre coins sont arrondis sur les cellules d'extrémité, ce qui
   reconstitue le rectangle de la carte — la contrepartie de ne pas avoir de
   conteneur, déjà employée sur la première colonne. */
@media (max-width: 48rem) {
  .corps-tableau--phare {
    padding-block-end: 0;
    overflow: auto;
  }
  .corps-tableau__phare {
    display: none;
  }
  .corps-tableau--phare table {
    table-layout: auto;
  }
  .corps-tableau--phare thead th:last-child,
  .corps-tableau--phare tbody td:last-child {
    background-color: var(--btn-primary-bg, var(--c-blue));
  }
  .corps-tableau--phare thead th:last-child {
    border-start-start-radius: var(--corps-radius);
    border-start-end-radius: var(--corps-radius);
  }
  .corps-tableau--phare tbody tr:last-child td:last-child {
    border-end-start-radius: var(--corps-radius);
    border-end-end-radius: var(--corps-radius);
  }
}
/* =========================================================================
   LES FICHES — le shortcode `{% fiches %}`.

   Les rangées de la section 02 de la maquette (Figma 144:5431) : un surtitre
   mono et un intitulé dans une colonne étroite, un paragraphe et une note mono
   dans la large.

   ⚠️ C'EST UN `<dl>` DONT CHAQUE COUPLE EST ENVELOPPÉ, et l'enveloppe n'est
   pas un caprice : un `<dl>` en grille place ses `<dt>` et ses `<dd>` un par
   un, donc une définition de deux lignes décalerait tous les termes suivants.
   Le `<div>` intermédiaire est explicitement autorisé dans un `<dl>` pour
   grouper un couple, et il ne change rien à ce qui est annoncé à la voix.

   Le filet est sur le couple SUIVANT, comme pour les listes : le premier
   n'ouvre pas sur un trait qui ne sépare rien.
   ========================================================================= */
.corps-fiches {
  margin-block: var(--sp-8);
}

.corps-fiche {
  display: grid;
  gap: var(--sp-2) var(--sp-6);
  padding-block: var(--sp-5);
}
@media (min-width: 48rem) {
  .corps-fiche {
    /* 120 des 760 px de la maquette pour la colonne de gauche. */
    grid-template-columns: minmax(0, 8rem) minmax(0, 1fr);
  }
}

.corps-fiche + .corps-fiche {
  border-block-start: 1px solid var(--border-subtle);
}

.corps-fiche__terme {
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
}

.corps-fiche__surtitre {
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--text-accent);
}

.corps-fiche__intitule {
  font-family: var(--font-titre);
  font-size: var(--fs-base);
  font-weight: var(--fw-bold);
  line-height: var(--lh-snug);
  color: var(--c-blue-zodiac);
}

.corps-fiche__def {
  margin: 0;
}

.corps-fiche__def > p {
  margin: 0;
}

/* La note. Mono et à l'encre éteinte : c'est une précision de bas de rangée,
   pas la suite du texte — la seule chose du corps d'article qui ne soit pas à
   pleine encre, et c'est justement ce que --text-muted est là pour porter
   (voir son commentaire dans _tokens.scss). */
.corps-fiche__note {
  margin-block: var(--sp-3) 0;
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  letter-spacing: 0.04em;
  color: var(--text-muted);
}

/* Après « article » et pas ailleurs : la page d'édition reprend ses classes et
   n'ajoute que le bloc « Le mois prochain ». Voir en tête du fichier. */
/* =========================================================================
   ÉDITION DE LA NEWSLETTER — la PAGE d'une parution. Figma 144:6340.

   ⚠️ LE PRÉFIXE EST `parution__` ET NON `edition__`, ET C'EST OBLIGATOIRE.
   `.edition` (singulier) est déjà pris : c'est la RANGÉE de l'archive, dans
   sections/_editions.scss (pluriel), et c'est un grid à trois colonnes. Le
   gabarit portait d'abord `class="article edition container"` — la page
   entière devenait une rangée de tableau, breadcrumb, ligne de méta, titre et
   corps empilés dans les mêmes cases.

   Le mode de panne est ce qui rend le piège coûteux : rien n'avertit. Aucune
   erreur de build, aucun avertissement Sass — juste une page illisible, dont
   le premier réflexe est de blâmer le cache du navigateur. Les deux fichiers
   ne diffèrent que par un « s » (_edition.scss / _editions.scss), d'où le
   renommage en _parution.scss.

   AVANT DE POSER UNE CLASSE SUR UN ÉLÉMENT DE PAGE, chercher le sélecteur nu
   dans src/scss/ : `grep -rn "^\.macasse" src/scss/`. Un nom au singulier est
   très souvent déjà l'unité d'une liste au pluriel.

   Ce fichier est volontairement court. La page d'édition reprend les classes
   `.article__*` de sections/_article.scss, parce que les deux planches
   dessinent la même composition — ligne de méta mono, titre, chapeau, filet,
   signature, colonne de lecture. Le raisonnement complet est en tête de
   layouts/edition.njk.

   Il n'y a donc ici QUE ce que l'article n'a pas : le bloc « Le mois prochain »
   (nœud 144:6424), propre à la newsletter.
   ========================================================================= */
/* La planche laisse 52 px entre la fin du texte et le filet, puis 29 px sous
   le filet. Le premier écart est une respiration de fin de lecture, pas une
   marge de bloc : il est donc porté ici et non par `.corps`, qui sert aussi
   aux articles où ce bloc n'existe pas. */
.parution__suite {
  margin-block-start: clamp(var(--sp-8), 4vw, var(--sp-12));
  padding-block-start: var(--sp-6);
  border-block-start: 1px solid var(--border-subtle);
}

/* ⚠️ --text-muted ET NON --text-accent, contrairement aux intitulés de partie
   du corps. La planche pose celui-ci en gris là où elle met les autres au
   bleu : l'accent désigne une partie de l'édition, et « Le mois prochain »
   n'en est pas une — c'est ce qui vient après elle.

   Le gris de la planche est #9aa1b5, qui ne donne que 2,4 sur le crème. Le
   site le ramène sur --text-muted (#4c5772), exactement comme .article__meta
   le fait déjà pour la ligne de métadonnées, que la planche pose dans ce même
   #9aa1b5 : un seul gris secondaire sur le site, mesuré. */
.parution__suite-intitule {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--text-muted);
}

/* --text-muted ici aussi, et c'est l'exception que la règle du corps de texte
   supporte : ce n'est pas du texte courant mais une note de pied qui annonce
   la suite. La planche la pose à #4c5772, soit exactement ce jeton. */
.parution__suite-texte {
  max-width: 46ch;
  margin: var(--sp-3) 0 0;
  font-size: var(--fs-base);
  line-height: var(--lh-normal);
  color: var(--text-muted);
}

/* =========================================================================
   BLOC « hero-page » — blocs/hero-page.njk.
   Origine : le hero de la page produit, Figma 185:6868.

   L'ouverture des pages intérieures. Deux moitiés : le texte à gauche, le
   décor de marque à droite.

   La maquette laisse la moitié droite VIDE. On ne la remplit donc pas d'un
   cadre en attente — on y laisse respirer la trame et les formes, comme le
   hero de l'accueil. C'est la seule façon de tenir la composition de la
   maquette sans promettre un visuel qui n'existe pas.

   Ce bloc n'a PAS l'animation d'entrée du hero d'accueil : celle-ci est calée
   sur le rotator et le lecteur, qui n'existent qu'là-bas. Ici l'apparition
   passe par [data-reveal], comme le reste du site.
   ========================================================================= */
.hero-page {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  background-color: var(--bg-page);
  /* Moins que --sp-section en haut : le bloc suit immédiatement l'en-tête
     collant, qui occupe déjà 71 px de hauteur visuelle. */
  padding-block: clamp(var(--sp-12), 7vw, var(--sp-24)) var(--sp-section);
}

/* ---------- Décor ---------- */
.hero-page__decor {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
}

/* La trame ne couvre que la moitié droite, celle que le texte n'occupe pas.
   Sur toute la largeur elle passait derrière le chapeau et gênait sa
   lecture — le texte du hero est plus grand qu'ailleurs, donc plus sensible
   à ce qui se trouve dessous. */
.hero-page__grille {
  position: absolute;
  inset-block: 0;
  inset-inline: 48% 0;
}

.hero-page__formes {
  position: absolute;
  inset: 0;
}

.hero-page__forme {
  position: absolute;
}

/* Les trois formes tiennent dans la moitié droite et débordent volontairement
   du bord : c'est la section qui les rogne. */
.hero-page__forme--haut {
  --shape-w: 9%;
  top: 12%;
  left: 62%;
}

.hero-page__forme--milieu {
  --shape-w: 13%;
  top: 46%;
  left: 84%;
}

.hero-page__forme--bas {
  --shape-w: 7%;
  top: 74%;
  left: 68%;
}

/* ---------- Mise en page ---------- */
.hero-page__inner {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(var(--sp-8), 5vw, 6rem);
  align-items: center;
}

.hero-page__texte {
  display: flex;
  flex-direction: column;
  align-items: start;
  gap: var(--sp-6);
}

/* Le cadre en attente. Ratio repris de la capture du hero d'accueil pour que
   les deux ouvertures aient la même assise. */
.hero-page__placeholder {
  aspect-ratio: 523/380;
}

/* ---------- L'illustration définitive ----------
   Trois déclarations, et chacune fait quelque chose :

     display: block   supprime l'espace de ligne de base sous une image en flux
                      en ligne, qui décalerait la moitié droite de quelques px.
     width: 100%      elle occupe sa colonne. Sans plafond : l'export fait 1253 px
                      de large et la demi-colonne en fait environ 600 à 1904 px
                      d'écran, donc elle ne s'agrandit jamais assez pour flouter.
                      Le jour où un export arrive plus petit, c'est lui qu'il faut
                      refaire — pas un `max-width` qui le laisserait passer.

                      ⚠️ NE PAS Y AJOUTER DE PLAFOND DE HAUTEUR sans passer en
                      même temps à `object-fit: contain`. Une largeur définie
                      rend la boîte définie, `max-block-size` n'en rabat alors
                      que la hauteur, et `object-fit` (`fill` par défaut) étire
                      l'image dedans. Mesuré, et raconté en entier dans
                      sections/_texte-panneau.scss, où les panneaux en portent un.
     height: auto     annule l'attribut `height` du HTML, qui est là pour RÉSERVER
                      la place et non pour l'imposer. Sans lui, l'image s'écrase
                      dès que sa colonne est plus étroite que son export.

   Aucun cadre, aucun filet : les cinq illustrations sont détourées (coins
   transparents). Elles ne portent plus d'ombre depuis leur réexport du
   25/08/2026 — cuite dans l'image, elle laissait un halo crème visible sur
   l'aplat, et la règle 5 de MAQUETTE.md l'interdit de toute façon. */
.hero-page__illustration {
  display: block;
  width: 100%;
  height: auto;
}

/* ---------- La composition des trois formes ----------
   Ajoutée le 20/08/2026 pour la page « Pourquoi Biron », dont la maquette pose
   ce diagramme dans son hero (nœud Figma 202:13034).

   L'atome porte déjà tout ce qui le fait tenir — son `container-type`, le ratio
   de sa scène, les vingt cotes en pourcentage de ses formes et de ses libellés
   (voir _components.scss). Il ne reste ici que sa MESURE et son centrage dans la
   moitié droite : c'est la limite entre l'atome et son hôte.

   La mesure est celle de la maquette (528px), et c'est le maximum : la
   composition est rigoureusement homothétique, elle tiendrait à n'importe quelle
   largeur, mais au-delà les libellés en `cqw` deviennent plus gros que le chapeau
   d'à côté. */
.hero-page__trio {
  width: 100%;
  max-width: 33rem;
  margin-inline: auto;
}

/* =========================================================================
   LA FIGURE « TROIS CHIFFRES, UNE SEULE VÉRITÉ » — Figma 319:2402.

   Sélectionnée par Hugo le 25/08/2026 en remplacement du cadre en attente du
   hero de /dictionnaire/. Trois services annoncent trois CA différents pour le
   même indicateur, le flux passe par Biron, et il en ressort UNE fiche
   certifiée.

   Construite en HTML et non posée en image — la consigne était d'« adapter les
   polices à celles qu'on a actuellement sur le site », et un export SVG cuit
   les polices de la maquette dans le fichier. La correspondance, une fois pour
   toutes :

     maquette         ici
     Recoleta Bold    --font-serif  (Sora)   noms de fiche, indicateur, pivot
     Figtree Bold     --font-sans   (Inter)  — plus employé, voir ci-dessous
     JetBrains Mono   --font-mono   (Chivo)  références, libellés, chiffres

   Les corps de la maquette (6,8 à 16,6 px sur une planche de 492 px de large)
   sont TOUS sous le plancher typographique du site. Ils remontent donc à
   l'échelon le plus proche de l'échelle — la figure est plus dense que la
   planche, et c'est précisément ce que « adapter les polices » demande.

   ---------------------------------------------------------------------
   ⚠️ ELLE EST HABILLÉE COMME `.archi__fiche`, LA CARTE KPI DE LA SECTION 01, et
   ce n'est pas un rapprochement de goût : Hugo l'a demandé le 25/08/2026 après
   une première version aux cotes propres — « tu peux le faire plus dans le
   style de la carte Chiffre d'affaires HT [...] dans le bloc Concrètement,
   qu'est-ce qu'un dictionnaire de données Biron ? ». Ce qui en vient, à
   l'identique, et qu'il ne faut donc pas « harmoniser » vers autre chose :

     filet         1px solid var(--border-subtle) — NEUTRE, et non le filet bleu
                   éclairci --filet-panneau qu'employait la première version
     rayon         --radius-cadre pour la fiche
     ombre         celle de --shadow-tint, sur la SEULE fiche
     en-tête       référence en chasse fixe au-dessus du nom en display, la
                   pastille à droite, une bande séparée d'un filet
     libellés      chasse fixe, --fs-mono, CAPITALES, --text-muted
     pastille      voile de l'accent à 12 % sur blanc, encre de l'accent
                   assombrie à 65 % vers le bleu nuit — 4,87 de contraste

   ⚠️ LA PASTILLE EST LA TROISIÈME COPIE DE CETTE RECETTE dans le dépôt, après
   `.archi__fiche-badge` et `.fiche-equipes__badge`. C'est le signal qu'elle doit
   monter en atome partagé dans _components.scss. Ce n'est pas fait ici parce que
   l'extraction butera sur `.archi__fiche-valeur--ok`, qui doit continuer de
   défaire ce que `.archi__fiche-valeur` déclare : à spécificité égale, un atome
   émis depuis _components.scss perd contre la feuille de section, et c'est
   exactement le piège que CLAUDE.md décrit à propos des atomes empruntés.

   Différence assumée avec les deux autres : elles écrivent
   --c-pilier-dictionnaire en dur, parce qu'elles ne servent que cette page. Ici
   c'est --c-blue, que `.ton-page--*` réaffecte — donc la même couleur sur cette
   page, et la teinte de la page hôte partout ailleurs.

   ⚠️ L'OMBRE PORTÉE EST UNE DÉROGATION À LA RÈGLE 5 DE MAQUETTE.md, héritée de
   `.archi__fiche`, qui la justifie en place : « elle doit se détacher du fond
   sans filet épais ». Elle est réservée à la fiche — quatre cartes ombrées dans
   une planche feraient une pile, là où la carte de référence en porte une seule.

   Elle vit dans cette feuille et non dans _components.scss : un seul bloc
   l'emploie. Si une deuxième page en a besoin, c'est là qu'elle devra monter.
   ========================================================================= */
/* La scène. Trois colonnes, SANS GOUTTIÈRE : la colonne du milieu n'est QUE de
   l'espace de liaison, et ses deux tronçons pointillés doivent toucher les
   cartes d'un côté et la fiche de l'autre. Une gouttière les ferait s'arrêter
   court, et le schéma se lirait comme trois objets posés côte à côte.

   La fiche pèse plus que la colonne de gauche (1,5fr contre 1fr) : c'est le
   rapport de la maquette, où elle occupe 228 px des 492 px de la planche.

   ---------------------------------------------------------------------
   ⚠️ ELLE N'A PAS DE PLANCHE, ET IL NE FAUT PAS EN REMETTRE. Une enveloppe crème
   arrondie a existé le 25/08/2026, demandée par Hugo (« mets le tout dans un box
   blanc crème ») puis retirée par lui le 26 (« enlève le fond »). Ce qu'elle
   coûtait : sur cette page le fond de page EST le crème, donc l'aplat ne se
   distinguait de rien et c'était le filet seul qui dessinait son arête — une
   arête pour une planche invisible. Et l'aplat étant opaque, il masquait la trame
   et les formes flottantes du décor du hero, qui est précisément ce que cette
   moitié de page a à montrer.

   Ce qu'elle réglait reste vrai et se règle ailleurs : la figure ne doit pas se
   lire comme cinq objets flottants. Ce sont les filets des cartes et le flux
   pointillé qui la tiennent ensemble, pas un fond.

   ---------------------------------------------------------------------
   LA FICHE PREND LE DOUBLE DE LA COLONNE DE GAUCHE, demandé par Hugo le
   26/08/2026 (« fais en sorte que la carte de droite soit plus large »). La
   maquette est à 1 : 1,69 (135 px de cartes contre 228 de fiche sur 492) ; on va
   donc au-delà d'elle, et la colonne de liaison se resserre de 22 à 16 % pour
   payer une partie de l'écart. */
.convergence {
  display: grid;
  grid-template-columns: minmax(0, 1fr) clamp(3.5rem, 16%, 6.5rem) minmax(0, 2fr);
  align-items: center;
  /* ⚠️ ELLE REMET LES DEUX RÔLES D'ENCRE EN CONTEXTE CLAIR, ET ELLE LE DOIT,
     pour la raison exacte de `.apercu` (voir _components.scss) : ses cartes sont
     des aplats BLANCS écrits en clair, donc c'est une figure claire quel que soit
     le fond sous elle. Posée un jour dans un `.on-dark`, elle hériterait
     de libellés blancs sur son propre blanc et de filets blancs translucides —
     invisibles tous les deux, et sans le moindre signal. C'est la règle 12 de
     MAQUETTE.md, branche « plusieurs encres ».

     Les deux valeurs sont celles que `:root` donne à ces rôles, et les deux
     lignes doivent rester en phase avec _tokens.scss. */
  --text-muted: var(--c-fiord);
  --border-subtle: rgba(44, 42, 46, 0.12);
}

/* ---------- La colonne des sources ---------- */
.convergence__sources {
  display: grid;
  gap: var(--sp-2);
}

/* L'indicateur, en tête : c'est la QUESTION posée aux trois services. En police
   de display et centré, comme la maquette — et au corps de `.archi__fiche-nom`,
   puisque c'est le même objet, le nom d'un indicateur. */
.convergence__indicateur {
  margin: 0;
  padding: var(--sp-3) var(--sp-2);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  background-color: var(--c-white);
  font-family: var(--font-serif);
  font-size: clamp(1.0625rem, 0.9rem + 0.4vw, 1.375rem);
  font-weight: var(--fw-semibold);
  line-height: var(--lh-snug);
  text-align: center;
  /* Écrite en dur, et pour la raison de `.archi__fiche-nom` : ces cartes sont des
     aplats BLANCS, et leur encre ne peut pas venir de --text-strong, qui passe au
     blanc dans `.on-dark` et disparaîtrait. */
  color: var(--c-blue-zodiac);
}

.convergence__services {
  display: grid;
  gap: var(--sp-2);
  margin: 0;
  padding: 0;
  list-style: none;
}

/* ⚠️ `flex-wrap` ET NON UN POINT DE RUPTURE. La colonne de gauche a maigri le
   26/08/2026 pour élargir la fiche, et « MARKETING » plus « 10,20 M€ » n'y tient
   plus toujours sur une ligne. Le repli fait descendre le chiffre tout seul quand
   la place manque, à toute largeur et sans qu'aucune valeur soit publiée — c'est
   le parti de `.inscription--rangee`. */
.convergence__service {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-2);
  padding: var(--sp-3);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  background-color: var(--c-white);
}

/* Le nom du service, en libellé de fiche : chasse fixe, capitales, encre
   secondaire. C'est `.archi__fiche-label` mot pour mot, et c'est ce qui laisse le
   chiffre être la seule chose forte de la carte. */
.convergence__service-nom {
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--text-muted);
}

/* LES TROIS CHIFFRES QUI SE CONTREDISENT, AU ROUGE DU PILIER PLATEFORME.

   Arbitrage d'Hugo, 25/08/2026 : « il faut mettre le rouge plateforme ». La
   première version prenait --c-alerte, le corail du logo, qui est très exactement
   la couleur de la maquette (#ee6873) — mais il tombe à 5 points du rose du
   pilier plateforme (#ff5d73), donc sur une page pilier ce rouge-là dit
   « plateforme » sans en être un.

   ⚠️ C'EST LA VALEUR SOMBRE DU JETON, PAS LE ROSE TEL QUEL. #ff5d73 ne donne que
   2,97 sur du blanc, très en dessous des 4,5 exigés d'un texte de ce corps ;
   #bc4b64 en donne 4,85, et son jeton dit lui-même qu'il est « utilisable telle
   quelle comme encre de badge ».

   ⚠️ NE PAS LA RECONSTRUIRE PAR UN color-mix DU ROSE VERS LE BLEU NUIT. C'est une
   couleur choisie par Hugo le 19/08/2026, pas un mélange calculé, et son jeton
   interdit nommément ce rapprochement : mélanger désature, la teinte changerait.

   `tabular-nums` aligne les trois montants en colonne. */
.convergence__service-valeur {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-weight: var(--fw-bold);
  font-size: var(--fs-base);
  color: var(--c-pilier-plateforme-sombre);
  white-space: nowrap;
}

/* ---------- Le pivot ----------
   Le jeton bleu nuit, et les deux tronçons pointillés qui l'abordent et le
   quittent. Les tronçons sont les DEUX PSEUDO-ÉLÉMENTS de cette colonne : ils
   n'ont pas de balise, et `flex: 1` leur fait remplir tout ce que le jeton
   laisse — donc jusqu'aux cartes à gauche et jusqu'à la fiche à droite. */
.convergence__pivot {
  display: flex;
  align-items: center;
  margin: 0;
}

/* LES POINTILLÉS SONT UN DÉGRADÉ RÉPÉTÉ ET NON UNE BORDURE `dashed` : une
   bordure ne s'anime pas, un fond se déplace. `background-size` fixe le pas du
   motif et l'animation décale le fond d'exactement un pas, donc la boucle est
   invisible. C'est la technique de `.fiche-equipes__tiges`, à l'horizontale — et
   comme elle, ils sont à l'accent de la page et non à un bleu pâle.

   Le sens va des services VERS la fiche : c'est le sens de lecture du schéma. */
.convergence__pivot::before,
.convergence__pivot::after {
  content: "";
  flex: 1;
  block-size: 2px;
  background-image: repeating-linear-gradient(to right, var(--c-blue) 0 4px, transparent 4px 9px);
  background-size: 9px 100%;
  animation: convergence-flux 1.1s linear infinite;
}

@keyframes convergence-flux {
  to {
    background-position-x: 9px;
  }
}
/* LE PENDANT VERTICAL, pour le pivot basculé du resserrage sous 480 px (voir plus
   bas). Deux blocs d'images-clés et non un seul qui décalerait
   `background-position` sur les deux axes : la tuile du tronçon horizontal fait
   100 % de sa hauteur, donc un décalage en y y ferait apparaître une couture. */
@keyframes convergence-flux-vertical {
  to {
    background-position-y: 9px;
  }
}
/* ⚠️ L'ANIMATION S'ARRÊTE EN MOUVEMENT RÉDUIT, ET LES POINTILLÉS RESTENT : le
   schéma garde son dessin, il cesse seulement de bouger. C'est la règle du
   site — chaque module de mouvement court-circuite son animation, jamais son
   contenu. */
@media (prefers-reduced-motion: reduce) {
  .convergence__pivot::before,
  .convergence__pivot::after {
    animation: none;
  }
}
/* Le jeton. `aspect-ratio: 1` plutôt qu'une hauteur : la largeur suit la colonne
   et le cercle reste rond quelle que soit cette largeur. */
.convergence__jeton {
  flex: none;
  display: grid;
  place-items: center;
  inline-size: clamp(2.75rem, 48%, 4rem);
  aspect-ratio: 1;
  border-radius: var(--radius-full);
  background-color: var(--c-blue-zodiac);
  font-family: var(--font-serif);
  font-weight: var(--fw-semibold);
  font-size: var(--fs-xs);
  color: var(--c-white);
}

/* ---------- La fiche ----------
   Les cotes de `.archi__fiche`, à l'identique : filet neutre, --radius-cadre,
   fond blanc, l'ombre de --shadow-tint, et `overflow: hidden` pour que les filets
   pleine largeur des rubriques ne dépassent pas des coins arrondis. */
.convergence__fiche {
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-cadre);
  background-color: var(--c-white);
  box-shadow: 0 4px 40px 4px rgb(var(--shadow-tint)/0.28);
  overflow: hidden;
}

/* L'en-tête, dans l'anatomie de la carte de référence : la référence en chasse
   fixe AU-DESSUS du nom en display, et l'étiquette à droite. La première version
   posait la référence et l'étiquette sur une ligne, puis le nom sous la bande —
   ce qui faisait deux bandes là où la carte de référence n'en a qu'une. */
.convergence__entete {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--sp-4);
  padding: clamp(var(--sp-4), 1.6vw, var(--sp-5));
  border-block-end: 1px solid var(--border-subtle);
}

.convergence__reference {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--text-muted);
}

.convergence__nom {
  margin: var(--sp-1) 0 0;
  font-family: var(--font-serif);
  font-size: clamp(1.0625rem, 0.9rem + 0.4vw, 1.375rem);
  font-weight: var(--fw-semibold);
  line-height: var(--lh-snug);
  color: var(--c-blue-zodiac);
}

/* « Fiche indicateur » — l'étiquette de droite, à la place qu'occupe la pastille
   dans la carte de référence. Elle s'efface au resserrage : c'est la moins utile
   des deux mentions, la référence désignant déjà l'indicateur. */
.convergence__etiquette {
  flex: none;
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--text-muted);
  white-space: nowrap;
}

/* Les rubriques. Le filet est posé ENTRE les lignes (`+`) et non en tête de
   chacune, comme `.archi__fiche-ligne` : la première n'en porte donc pas, celui
   de l'en-tête faisant déjà la séparation. Aucun compte n'est publié. */
.convergence__lignes {
  margin: 0;
  padding: 0;
  list-style: none;
}

.convergence__ligne {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  padding: clamp(var(--sp-3), 1.2vw, var(--sp-4)) clamp(var(--sp-4), 1.6vw, var(--sp-5));
}

.convergence__ligne + .convergence__ligne {
  border-block-start: 1px solid var(--border-subtle);
}

/* Le libellé d'une rubrique, en chasse fixe capitale : `.archi__fiche-label` mot
   pour mot. La première version les composait en sans semi-gras au corps du
   site, ce qui faisait quatre lignes de titres dans une carte qui n'en a qu'un. */
.convergence__libelle {
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--text-muted);
}

/* LA COCHE — la recette de pastille de la carte de référence : un VOILE de
   l'accent à 12 % sur blanc, et l'encre de l'accent ASSOMBRIE à 65 % vers le
   bleu nuit, ce qui la porte à 4,87 de contraste. Arbitrage d'Hugo, 25/08/2026,
   contre l'aplat fluo de la maquette — quatre pastilles fluo plus une pilule
   fluo sur une planche crème.

   Elle part de --c-blue et non de --c-pilier-dictionnaire écrit en dur : la
   figure suit ainsi la teinte de la page qui l'héberge, alors que les deux autres
   copies de cette recette ne servent que cette page-ci. */
.convergence__coche {
  flex: none;
  display: grid;
  place-items: center;
  inline-size: 1.375rem;
  aspect-ratio: 1;
  border-radius: var(--radius-full);
  background-color: color-mix(in srgb, var(--c-blue) 12%, var(--c-white));
  font-size: var(--fs-mono);
  font-weight: var(--fw-bold);
  line-height: 1;
  color: color-mix(in srgb, var(--c-blue) 65%, var(--c-blue-zodiac));
}

/* Le pied : le chiffre tranché, et la pastille de certification. */
.convergence__pied {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  margin: 0;
  padding: clamp(var(--sp-4), 1.6vw, var(--sp-5));
  border-block-start: 1px solid var(--border-subtle);
}

/* LE CHIFFRE, en chasse fixe comme le veut la maquette — une valeur numérique se
   lit mieux ainsi, et `tabular-nums` aligne ses colonnes. --fs-h3 est le corps
   que `.fiche-equipes__valeur` donne au même objet, la valeur d'un KPI dans une
   figure : rien n'est inventé ici.

   ⚠️ SA TEINTE N'EST PAS --c-blue, alors que la maquette écrit très exactement
   #3f58d7, la valeur du jeton. La raison est que `.ton-page--*` réaffecte cette
   variable : sur cette page ce serait le vert du dictionnaire, soit 2,63 de
   contraste sur du blanc. C'est le bleu nuit, comme `.fiche-equipes__valeur` — et
   l'accent de la figure vit désormais dans les pastilles. */
.convergence__valeur {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-weight: var(--fw-bold);
  font-size: var(--fs-h3);
  line-height: 1;
  color: var(--c-blue-zodiac);
  white-space: nowrap;
}

/* La pastille « Certifié » — la même recette que la coche, et le point qui la
   précède est l'équivalent de `.archi__puce` : l'accent à PLEINE force, parce que
   c'est un signe et non du texte. C'est un `::before`, un ornement n'ayant rien à
   faire dans le balisage. */
.convergence__statut {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-1) var(--sp-3);
  border-radius: var(--radius-full);
  background-color: color-mix(in srgb, var(--c-blue) 12%, var(--c-white));
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: color-mix(in srgb, var(--c-blue) 65%, var(--c-blue-zodiac));
  white-space: nowrap;
}

.convergence__statut::before {
  content: "";
  flex: none;
  inline-size: 0.3125rem;
  aspect-ratio: 1;
  border-radius: var(--radius-full);
  background-color: var(--c-blue);
}

/* ---------- Resserrage ----------
   Sous 480 px la scène ne tient plus en trois colonnes : la fiche descend sous
   les sources, et les trois services passent sur une rangée.

   ⚠️ LE PIVOT NE DISPARAÎT PLUS, IL BASCULE À LA VERTICALE. Il était masqué ici
   jusqu'au 27/08/2026, au motif qu'empilés les objets se lisaient déjà dans
   l'ordre du flux ; Hugo a tranché le contraire le 27 (« là on voit plus le rond
   avec Biron »), et le motif était faux : l'empilement donne l'ORDRE, il ne dit
   pas la CAUSE. Sans le jeton on lit trois chiffres rouges puis une fiche
   certifiée, et rien ne dit que c'est Biron qui fait passer de l'un à l'autre —
   ce qui est tout le propos de la figure. Ne pas rétablir le masquage.

   Il bascule sans changer d'objet : mêmes tronçons pointillés, même jeton, seul
   l'axe change. Le sens de lecture étant ici le haut vers le bas, le flux le
   suit, et le balisage ne bouge pas d'une ligne.

   ⚠️ LA GOUTTIÈRE DE LA GRILLE PASSE À ZÉRO, et c'est la règle de la scène large
   appliquée à la verticale : les tronçons doivent TOUCHER les cartes en haut et la
   fiche en bas, sans quoi le lien s'arrête court et se lit comme un tiret posé
   entre deux objets. C'est donc le pivot qui porte tout l'espace de respiration,
   par la longueur de ses deux tronçons.

   ⚠️ ET CETTE LONGUEUR EST UNE MESURE, PAS UN CONFORT. Le pas du motif est de
   9 px FIXES — c'est un `background-size` en pixels, que la racine à 85 % ne
   touche pas — et `.conversation` a déjà établi qu'une dent et demie ne se lit
   plus comme un lien. 2rem rendent ~27 px, soit trois dents ; en dessous le
   pointillé redevient un tiret. */
@media (max-width: 30rem) {
  .convergence {
    grid-template-columns: minmax(0, 1fr);
    gap: 0;
  }
  /* L'axe du flux passe à la verticale. `align-items: center`, hérité de la règle
     large, centre alors le jeton et ses deux tronçons dans la largeur — le pivot
     occupant toute la colonne. */
  .convergence__pivot {
    flex-direction: column;
  }
  /* Les tronçons debout. `flex: 1` n'a plus rien à distribuer, la hauteur du pivot
     venant de son contenu : la longueur est donc donnée en clair. */
  .convergence__pivot::before,
  .convergence__pivot::after {
    flex: none;
    inline-size: 2px;
    block-size: var(--sp-8);
    background-image: repeating-linear-gradient(to bottom, var(--c-blue) 0 4px, transparent 4px 9px);
    background-size: 100% 9px;
    animation-name: convergence-flux-vertical;
  }
  /* ⚠️ LE GARDE-FOU DE MOUVEMENT RÉDUIT EST REJOUÉ ICI, ET IL LE FAUT : la règle
     générale, plus haut dans la feuille, est à la même spécificité que celle-ci et
     lui est ANTÉRIEURE — son `animation: none` perdrait donc contre le
     `animation-name` ci-dessus, et l'animation reviendrait en mouvement réduit sur
     mobile, sans le moindre signal. Les deux requêtes média s'imbriquent. */
}
@media (max-width: 30rem) and (prefers-reduced-motion: reduce) {
  .convergence__pivot::before,
  .convergence__pivot::after {
    animation: none;
  }
}
@media (max-width: 30rem) {
  .convergence__services {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}
@media (max-width: 30rem) {
  /* En trois colonnes étroites, le nom et son chiffre ne tiennent plus sur une
     ligne. */
  .convergence__service {
    flex-direction: column;
    align-items: start;
    gap: var(--sp-1);
  }
  .convergence__etiquette {
    display: none;
  }
}
/* =========================================================================
   LA FIGURE « UNE QUESTION, UNE RÉPONSE TRACÉE » — blocs/hero-page.njk,
   `visuel.illustration: conversation`.

   Origine : Figma 189:11547, sélectionné par Hugo le 26/08/2026 en
   remplacement du cadre en attente du hero de /connecteur-ia/.

   Trois objets sur une rangée, dans le sens de lecture du flux : l'entrepôt,
   le jeton MCP, la conversation avec l'assistant. Les deux tronçons pointillés
   sont les pseudo-éléments de la colonne du milieu — même dispositif que
   `.convergence` juste au-dessus, et pour la même raison : la grille ne porte
   AUCUNE gouttière, donc les tronçons touchent vraiment les deux cartes.

   ⚠️ LA RANGÉE EST UN CHOIX D'HUGO, 26/08/2026, contre une recomposition qui
   aurait mis le flux en bandeau au-dessus et la carte pleine largeur dessous.
   Le prix en est écrit dans le gabarit : à 430 px de colonne les lignes les plus
   longues se replient sur deux lignes là où la maquette les tient sur une.

   ⚠️ AUCUNE COTE DE TEXTE NE VIENT DE LA MAQUETTE. Elle écrit sa conversation
   entre 7,9 et 10,3 px ; le site n'a que --fs-mono (0,6875rem) et --fs-xs
   (0,875rem) en dessous du corps courant. Ils rendent respectivement 9,4 et
   11,9 px, la racine du site étant à 85 % — donc à peine plus gros que la
   maquette, et la figure tombe très près de son dessin. C'est de la chance, pas
   une méthode : ne pas en déduire qu'on peut recopier des corps de planche.

   ⚠️ TROIS TEINTES, TROIS PROVENANCES, et aucune n'est interchangeable. La
   source porte le VERT DU DICTIONNAIRE, qui vient de l'export de la forme et
   nomme le pilier sans un mot ; le jeton et les pointillés prennent --c-blue, le
   RÔLE, que `.ton-page--connecteur` a passé au violet — soit exactement le
   #8b5cff de la maquette ; la conversation reste sur des aplats blancs. La tuile
   bleue de la maquette a disparu le 26/08/2026 avec le pictogramme de base de
   données (voir le gabarit), et --c-blue-biron avec elle : ce jeton-là n'a plus
   de client ici.
   ========================================================================= */
.conversation {
  display: grid;
  /* La colonne de gauche porte sa mesure — ~110 px, la cote de la maquette —
     depuis que son libellé est « DICTIONNAIRE DE DONNÉES » : il se replie sur
     deux lignes et ne peut donc plus la dimensionner lui-même, comme le faisait
     « DATAWAREHOUSE ».

     ⚠️ SA BORNE BASSE EST MESURÉE, PAS ESTIMÉE : le mot « DICTIONNAIRE » fait
     83 px en chasse fixe capitale interlettrée, et il ne se coupe pas. Sous
     7.75rem (105 px, rembourrage et filet compris) il DÉBORDE la carte au lieu de
     la faire grandir — une piste de grille est fixée par la grille, pas par son
     contenu, et la taille minimale automatique d'un élément de grille ne remonte
     pas jusqu'à elle. Le libellé étant en --fs-mono, un jeton FIXE, ces 83 px ne
     bougent à aucune largeur : la borne est donc juste partout. */
  grid-template-columns: clamp(6.75rem, 15%, 7.25rem) clamp(7rem, 19%, 8.5rem) minmax(0, 1fr);
  /* ⚠️ LA FORME DU DICTIONNAIRE ET LE JETON MCP PARTAGENT CETTE COTE, et c'est
     une demande d'Hugo du 26/08/2026 (« grossir le rond MCP à la même taille que
     la shape dictionnaire »). Deux littéraux se seraient désaccordés au premier
     réglage, sans que rien ne le signale : les deux objets sont les deux NŒUDS
     du même flux, ils ont donc une mesure et non deux.

     La colonne du milieu monte avec : à 6.5rem elle ne laissait plus que 13 px
     de pointillé de chaque côté du jeton, soit une dent et demie du motif — un
     tronçon qui ne se lit plus comme un lien. */
  --conversation-noeud: 4.5rem;
  align-items: center;
  /* ⚠️ ELLE REMET LES DEUX RÔLES D'ENCRE EN CONTEXTE CLAIR, ET ELLE LE DOIT,
     pour la raison exacte de `.apercu` et de `.convergence` : ses aplats sont
     BLANCS et écrits en clair, donc c'est une figure claire quel que soit le
     fond sous elle. Posée un jour dans un `.on-dark`, elle hériterait de
     libellés blancs sur son propre blanc et de filets blancs translucides —
     invisibles tous les deux, et sans le moindre signal. C'est la règle 12 de
     MAQUETTE.md, branche « plusieurs encres ».

     Les deux valeurs sont celles que `:root` donne à ces rôles, et les deux
     lignes doivent rester en phase avec _tokens.scss. */
  --text-muted: var(--c-fiord);
  --border-subtle: rgba(44, 42, 46, 0.12);
}

/* ⚠️ CETTE FIGURE ÉLARGIT SA MOITIÉ, ET ELLE PAYE SUR LA GOUTTIÈRE, PAS SUR LE
   TEXTE. Demande d'Hugo du 26/08/2026 : « élargir un peu le bloc IA à droite ».

   Il n'y avait rien à prendre À L'INTÉRIEUR de la figure — mesuré : la colonne de
   gauche est bornée par le mot « DICTIONNAIRE », qui ne se coupe pas, et celle du
   milieu venait de grossir de 18 px avec le jeton, soit plus que tout ce qu'un
   rééquilibrage aurait rendu. Le seul gisement était la moitié droite elle-même.

   ⚠️ LA GOUTTIÈRE EST LE VRAI GISEMENT, ET C'EST CE QUI SAUVE LE TITRE. Une
   première version prenait les 30 px sur la seule colonne de texte (partage
   45 / 55) : mesurée à 457 px, elle faisait casser le titre en plein mot —
   « Posez la / question. Fiez- / vous à la réponse. » Le hero laisse 72 px entre
   ses deux moitiés à 1440 px, on en reprend 31 : le texte revient à 487 px et
   retrouve ses trois lignes d'origine, « Posez la question. » intacte sur la
   première, pendant que la conversation garde ses 30 px de gain. Le partage ne
   fait plus que le complément (0,93 / 1,07).

   Reste un effet à connaître : entre `lg` et ~1400 px le titre passe de deux à
   trois lignes. C'est le rendu qu'il a déjà à 1440, et la coupure tombe entre les
   deux phrases — c'est le prix accepté. Une page qui poserait cette figure sous un
   titre plus long devrait le revérifier.

   ⚠️ BORNÉE À `lg` PAR NÉCESSITÉ, pas par prudence : en dessous, le hero passe en
   une seule colonne, et ce sélecteur-ci (0,2,0) l'emporterait sur le
   `.hero-page__inner` (0,1,0) du resserrage, qui remettrait donc deux colonnes sur
   un écran étroit. */
@media (min-width: 60rem) {
  .hero-page__inner:has(.conversation) {
    grid-template-columns: minmax(0, 0.93fr) minmax(0, 1.07fr);
    gap: clamp(var(--sp-6), 3vw, 3rem);
  }
}
/* ---------- La source : l'entrepôt ---------- */
/* ⚠️ LA CARTE SE RESSERRE SUR LA FORME, ET SON LIBELLÉ EST HORS FLUX — c'est ce
   qui aligne les trois objets du flux. Demande d'Hugo du 26/08/2026 : « que le
   logo MCP et le trait soient alignés avec la shape dictionnaire ».

   Le défaut venait de la boîte : la grille centre les CARTES, et tant que le
   libellé était dedans, le centre de la carte tombait plus bas que celui de la
   forme d'exactement la moitié du bloc « gouttière + libellé » — 16 px mesurés,
   soit la forme à 410, le pointillé à 420 et le jeton à 430.

   ⚠️ LE REMÈDE N'EST PAS UN DÉCALAGE CALCULÉ. Remonter le pivot de
   `calc(-0.5 * (gouttière + 2 lignes de libellé))` marche aujourd'hui et ment
   demain : le compte de lignes serait écrit dans la feuille, et un mot de plus au
   libellé décalerait tout en silence — c'est le piège `--rangs-N`. Hors flux, le
   libellé ne pèse plus rien dans la hauteur de la carte, donc la forme EST le
   centre de la carte, et l'alignement tient à n'importe quelle longueur.

   C'est exactement le dispositif que le jeton MCP emploie déjà pour son propre
   libellé, deux règles plus bas — les deux nœuds du flux ont désormais la même
   anatomie : une forme, et son nom pendu dessous. */
.conversation__source {
  position: relative;
  justify-self: center;
  display: grid;
  place-items: center;
  padding: var(--sp-3);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-cadre);
  background-color: var(--c-white);
}

/* LA FORME DU PILIER DICTIONNAIRE. Elle a remplacé le 26/08/2026 la tuile bleue
   et son pictogramme de base de données, et le logo Biron est parti avec — voir
   le gabarit, qui dit pourquoi la tuile ne pouvait pas rester sous une forme
   déjà remplie de sa couleur.

   ⚠️ ELLE EST COTÉE EN `rem`, JAMAIS EN POURCENTAGE. La leçon a coûté un tour le
   26/08/2026 et vaut pour toute piste de grille dimensionnée par son contenu :
   pendant le calcul de la piste la largeur du conteneur est indéfinie, donc un
   pourcentage y retombe sur `auto`, c'est-à-dire sur la taille INTRINSÈQUE de
   l'élément — 180 px pour une `<img>` (son attribut `width`), et 300 px pour un
   `<svg>` en ligne sans `width` ni `height`, qui prend la taille par défaut d'un
   remplacé. La carte partait ainsi à ~330 px au lieu de 110 et mangeait la
   conversation d'à côté, sans la moindre erreur nulle part.

   4.5rem font 61 px, la racine du site étant à 85 % : c'est l'encombrement de la
   tuile de la maquette (63 px), donc la carte garde son assise. La hauteur suit
   le rapport de l'export (206 × 212), quasi carré. */
.conversation__glyphe {
  display: block;
  inline-size: var(--conversation-noeud);
  block-size: auto;
}

/* Le libellé de la carte, dans la grammaire des libellés du site : chasse fixe,
   capitales, interlettrage élargi, encre secondaire. C'est `.archi__fiche-label`
   mot pour mot — et c'est aussi ce que la maquette compose ici, en capitales
   suivies.

   ⚠️ IL SE REPLIE, ET LA COLONNE NE DÉPEND PLUS DE LUI. Tant que le libellé
   valait « DATAWAREHOUSE », il tenait sur une ligne et c'est lui qui donnait sa
   largeur à la piste `auto` — la cote de la maquette tombait toute seule.
   « DICTIONNAIRE DE DONNÉES » mesure la moitié en plus : gardé sur une ligne, il
   porterait la carte à ~180 px et reprendrait à la conversation ce qu'elle a de
   plus rare. La piste porte donc désormais sa propre mesure (voir la grille en
   tête de bloc) et le libellé se replie dedans, centré. */
.conversation__source-nom {
  position: absolute;
  inset-block-start: 100%;
  inset-inline-start: 50%;
  translate: -50% 0;
  margin-block-start: var(--sp-2);
  inline-size: 6.75rem;
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  letter-spacing: var(--ls-wide);
  line-height: var(--lh-snug);
  text-transform: uppercase;
  text-align: center;
  text-wrap: balance;
  color: var(--text-muted);
}

/* ---------- Le pivot : le jeton MCP ---------- */
.conversation__pivot {
  display: flex;
  align-items: center;
  justify-content: center;
}

/* LES POINTILLÉS SONT UN DÉGRADÉ RÉPÉTÉ ET NON UNE BORDURE `dashed` : une
   bordure ne s'anime pas, un fond se déplace. `background-size` fixe le pas du
   motif et l'animation décale le fond d'exactement un pas, donc la boucle est
   invisible. Même technique et mêmes cotes que `.convergence__pivot`.

   Le sens va de l'entrepôt VERS la conversation : c'est le sens de lecture. */
.conversation__pivot::before,
.conversation__pivot::after {
  content: "";
  flex: 1;
  block-size: 2px;
  background-image: repeating-linear-gradient(to right, var(--c-blue) 0 4px, transparent 4px 9px);
  background-size: 9px 100%;
  animation: conversation-flux 1.1s linear infinite;
}

@keyframes conversation-flux {
  to {
    background-position-x: 9px;
  }
}
/* Le même flux, debout : le tronçon qui descend du jeton MCP vers la
   conversation empilée en dessous, sous `xs`. Le motif se déplace vers le BAS,
   soit le sens de lecture de la figure empilée. */
@keyframes conversation-flux-vertical {
  to {
    background-position-y: 9px;
  }
}
/* ⚠️ L'ANIMATION S'ARRÊTE EN MOUVEMENT RÉDUIT, ET LES POINTILLÉS RESTENT : le
   schéma garde son dessin, il cesse seulement de bouger. */
@media (prefers-reduced-motion: reduce) {
  .conversation__pivot::before,
  .conversation__pivot::after {
    animation: none;
  }
}
/* Le jeton, à la cote de la forme du dictionnaire — voir --conversation-noeud en
   tête de bloc. `aspect-ratio: 1` plutôt qu'une hauteur : le cercle reste rond
   quelle que soit la cote. */
.conversation__jeton {
  position: relative;
  flex: none;
  display: grid;
  place-items: center;
  inline-size: var(--conversation-noeud);
  aspect-ratio: 1;
  border-radius: var(--radius-full);
  background-color: var(--c-blue);
}

/* ⚠️ LE LOGO EST UN MASQUE, PAS UNE IMAGE, exactement comme `.chaine__logo` :
   l'export du protocole MCP est un tracé NOIR, qui resterait noir sur le violet
   à 3,01 de contraste. En masque, sa teinte vient des jetons du site.

   Le blanc donne 4,14 sur le violet du pilier — sous le seuil de 4,5, et accepté
   ici pour la même raison que là-bas : le logo est DÉCORATIF (toute la figure est
   `aria-hidden`) et le libellé « MCP Biron » juste dessous, lui, est à pleine
   encre sur le crème. */
.conversation__logo {
  inline-size: 62%;
  aspect-ratio: 1;
  background-color: var(--c-white);
  mask-image: url("/assets/img/logos/mcp.svg");
  mask-size: contain;
  mask-repeat: no-repeat;
  mask-position: center;
  -webkit-mask-image: url("/assets/img/logos/mcp.svg");
  -webkit-mask-size: contain;
  -webkit-mask-repeat: no-repeat;
  -webkit-mask-position: center;
}

/* ⚠️ LE LIBELLÉ EST HORS FLUX, ET C'EST CE QUI ALIGNE LE JETON SUR LES DEUX
   CARTES. La maquette pose le centre du cercle exactement au centre de la carte
   de l'entrepôt, le libellé pendant dessous ; laissé en flux, il ferait remonter
   le jeton de la moitié de sa propre hauteur et l'alignement se perdrait.

   Il prend --text-strong et non une encre écrite en clair : contrairement à tout
   le reste de la figure, il est posé sur LE FOND DE LA PAGE et non sur un aplat
   blanc, donc il doit suivre son contexte. */
.conversation__pivot-nom {
  position: absolute;
  inset-block-start: 100%;
  inset-inline-start: 50%;
  translate: -50% 0;
  margin-block-start: var(--sp-2);
  font-size: var(--fs-xs);
  font-weight: var(--fw-medium);
  line-height: var(--lh-snug);
  color: var(--text-strong);
  white-space: nowrap;
}

/* ---------- La conversation ---------- */
/* L'OMBRE EST LA DÉROGATION DÉJÀ ACCORDÉE À `.archi__fiche` ET REPRISE PAR
   `.convergence__fiche` — mêmes valeurs. Elle est bornée à CETTE carte : la
   carte de l'entrepôt n'a qu'un filet, parce que deux cartes ombrées dans une
   même planche font une pile. La règle 5 de MAQUETTE.md interdit l'ombre
   partout ailleurs.

   ⚠️ UN SEUL ENFANT DEPUIS LE 15/09/2026 (Figma 447:11724) : l'icône Claude
   qui posait ici un second enfant, à côté du fil, s'est déplacée DANS le fil,
   devant la seule bulle de question — voir `.conversation__question` plus bas
   et le gabarit. `display: flex` reste inoffensif à un seul enfant, on ne l'a
   donc pas retiré. */
.conversation__carte {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
  padding: var(--sp-3);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-cadre);
  background-color: var(--c-white);
  box-shadow: 0 4px 40px 4px rgb(var(--shadow-tint)/0.28);
}

/* Le fil. Son fond est le crème ÉCRIT EN CLAIR et non --bg-page : ce n'est pas
   le fond de la page, c'est un panneau clair dans une carte blanche — le même
   emploi que les panneaux clairs de `temoignages` et de `connecteurs`.

   Son encre est --c-text-main en clair, pour la raison de `.convergence` : ce
   panneau est un aplat clair quel que soit le fond sous lui, et --text-strong
   passerait au blanc dans un `.on-dark`.

   `min-inline-size: 0` est obligatoire : sans lui, un élément flex refuse de
   descendre sous la largeur intrinsèque de son contenu, et la carte pousserait
   sa colonne. */
.conversation__fil {
  flex: 1;
  min-inline-size: 0;
  display: grid;
  gap: var(--sp-3);
  padding: var(--sp-3);
  border-radius: var(--radius-md);
  background-color: var(--c-base-main);
  font-size: var(--fs-xs);
  line-height: var(--lh-normal);
  color: var(--c-text-main);
}

/* LA QUESTION — Figma 447:11724, 15/09/2026 : l'icône Claude devant sa bulle,
   plutôt qu'un avatar posé à côté de toute la carte (voir le gabarit). Les
   deux sont donc une rangée : l'icône hors flux de la bulle, comme le glyphe
   du dictionnaire est hors flux de son libellé plus haut dans ce fichier. */
.conversation__question {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-2);
}

.conversation__question-icone {
  flex: none;
  inline-size: 1.75rem;
  block-size: auto;
}

.conversation__question-texte {
  flex: 1;
  min-inline-size: 0;
  margin: 0;
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  background-color: var(--c-white);
}

/* ---------- La trace ---------- */
.conversation__trace {
  display: grid;
  gap: var(--sp-2);
}

/* Le chevron est un ORNEMENT, donc un pseudo-élément : rien à faire dans le
   balisage. Il dit que le détail se déplie, ce que la figure montre déjà. */
.conversation__environnement {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  margin: 0;
  color: var(--text-muted);
}

.conversation__environnement::after {
  content: "▾";
  font-size: 0.75em;
}

/* Le filet vertical qui tient les deux appels ensemble, comme la maquette. */
.conversation__appels {
  display: grid;
  gap: var(--sp-2);
  margin: 0;
  padding-inline-start: var(--sp-1);
  border-inline-start: 1px solid var(--border-subtle);
  list-style: none;
}

.conversation__appel {
  display: flex;
  align-items: baseline;
  gap: var(--sp-2);
  padding-inline-start: var(--sp-3);
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  line-height: var(--lh-snug);
  color: var(--text-muted);
}

/* Le « b » de Biron devant chaque appel. En police de display, comme la maquette
   le compose en serif — et MÉLANGÉ AU FOND plutôt qu'éclairci vers le blanc :
   sur le crème du fil, un mélange vers le blanc laisserait une teinte plus
   froide que son support. C'est la règle des séries de partials/motif.njk. */
.conversation__signe {
  flex: none;
  font-family: var(--font-serif);
  font-weight: var(--fw-bold);
  color: color-mix(in srgb, var(--text-muted) 60%, var(--c-base-main));
}

/* ---------- La réponse ---------- */
.conversation__reponse {
  display: grid;
  gap: var(--sp-2);
}

/* Les trois paragraphes de la réponse — l'intro, l'alerte et la proposition,
   Figma 447:11724, 15/09/2026 : la maquette conclut sur un texte rédigé,
   plus sur une liste de montants (voir le gabarit), donc les trois partagent
   cette seule classe. Le fragment en gras de l'alerte est un `<strong>` posé
   dans le texte, pas une classe à part. */
.conversation__reponse-texte {
  margin: 0;
}

/* ---------- La zone de saisie ---------- */
/* La marge supérieure s'ajoute à la gouttière du fil : la maquette détache
   nettement la saisie de la réponse, ce qui est ce qui la fait lire comme la
   suite de la conversation et non comme sa conclusion.

   ⚠️ VIDE DEPUIS LE 15/09/2026 (Figma 447:11724) : la maquette ne pose plus de
   relance écrite dans le champ — voir le gabarit. `justify-items: end` suffit
   donc à lui seul à placer le bouton, sans qu'il reste de texte à étirer à
   côté de lui. */
.conversation__saisie {
  display: grid;
  justify-items: end;
  gap: var(--sp-2);
  margin: var(--sp-2) 0 0;
  padding: var(--sp-3);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  background-color: var(--c-white);
}

/* Le bouton reste NEUTRE, comme dans la maquette : à l'accent de la page il
   deviendrait l'objet le plus fort de la carte, alors que ce que la figure
   raconte est la trace, pas l'envoi. */
.conversation__envoi {
  display: grid;
  place-items: center;
  inline-size: 1.5rem;
  aspect-ratio: 1;
  border-radius: var(--radius-sm);
  background-color: color-mix(in srgb, var(--text-muted) 22%, var(--c-white));
  font-size: var(--fs-xs);
  line-height: 1;
  color: var(--c-text-main);
}

/* ---------- Resserrages ----------

   Sous `lg` le hero passe en une colonne, et la figure hérite de TOUTE la
   largeur du conteneur : elle s'étalerait à 900 px de large pour 300 de haut.
   On la borne à sa mesure de bureau et on la centre — même parti que
   `.hero-page__trio` au même palier.

   Sous `xs` la rangée ne tient plus : la carte descend sous les deux autres
   objets, et le second tronçon de pointillés se REDRESSE — il ne relie plus le
   jeton à une carte posée à sa droite, mais à la conversation empilée dessous.
   Demande d'Hugo du 28/08/2026 : « en mode mobile […] un pointillé qui part de
   MCP Biron pour aller vers la discussion Claude en dessous. »

   ⚠️ LE TRONÇON REDRESSÉ N'EST PAS LE `::after` DU PIVOT, ET C'EST DÉLIBÉRÉ.
   Il lui faut deux appuis : partir SOUS le libellé « MCP Biron » (qui pend du
   jeton, hors flux, et qu'un trait vertical traverserait de part en part) et
   TOUCHER le bord haut de la carte, comme les deux tronçons horizontaux touchent
   les leurs. Aucun des deux éléments ne porte les deux appuis : depuis le pivot,
   la fin serait une cote en clair qui déborderait sur la carte au premier
   réglage ; depuis la carte, c'est la FIN qui est structurelle
   (`inset-block-end: 100%`) et le début qui garde du jeu — or c'est là qu'il y a
   du jeu, une demi-dent de dégagement sous le libellé ne se voyant pas. On met
   l'exactitude là où elle est due. Le `::after` du pivot reste donc masqué. */
@media (max-width: 60rem) {
  .conversation {
    max-width: 44rem;
    margin-inline: auto;
  }
}
@media (max-width: 30rem) {
  .conversation {
    grid-template-columns: auto minmax(0, 1fr);
    /* Une gouttière plus large qu'ailleurs, et pour deux raisons mesurables.
       Les deux libellés des nœuds sont HORS FLUX, donc ils débordent sous leur
       rangée — 32 px pour celui de l'entrepôt, 23 px pour celui du jeton : sous
       --sp-10 ils recouvriraient la conversation empilée dessous.

       ⚠️ ET C'EST DÉSORMAIS LE TRONÇON DEBOUT QUI LA DIMENSIONNE, pas les
       libellés. --sp-12 ne laissait que 23 px sous le libellé du jeton, soit
       deux dents et demie du motif — la feuille a déjà établi deux fois qu'en
       dessous de trois dents un pointillé redevient un tiret (voir la longueur
       des tronçons de `.convergence` au même palier). --sp-16 porte le tronçon
       à ~37 px, soit quatre dents, pour 13,6 px de hauteur en plus. La longueur
       du tronçon se déduit de cette valeur, elles restent donc en phase. */
    row-gap: var(--sp-16);
  }
  .conversation__carte {
    position: relative;
    grid-column: 1/-1;
  }
  /* LE TRONÇON DEBOUT — il descend du nœud MCP jusqu'au bord haut de la carte.

     ⚠️ SON AXE SE DÉDUIT DE LA COTE DU NŒUD, il n'est pas centré sur la carte :
     le jeton est plaqué contre le bord droit de sa colonne (son `::before` en
     `flex: 1` mange tout le reste), et cette colonne finit au bord droit de la
     grille, que la carte occupe en entier. Le centre du jeton tombe donc à une
     demi-cote de nœud du bord droit de la carte. Les 2 px retirés sont le filet
     de la carte (les décalages d'un absolu se résolvent contre la boîte de
     rembourrage) plus la demi-largeur du tronçon.

     ⚠️ SA LONGUEUR SE DÉDUIT DE LA GOUTTIÈRE, elle n'est pas en clair : elle
     part du bord haut de la carte et remonte de la gouttière, plus les 0,89rem
     dont la grille recentre le pivot dans sa rangée (la carte de l'entrepôt est
     plus haute que le jeton), moins ce qu'occupe le libellé « MCP Biron » — sa
     marge, sa ligne — moins un dégagement d'une marge sous lui. Changer la
     gouttière déplace donc le tronçon avec elle. Le 0,89rem est la seule mesure
     en clair : 12,1 px, soit la moitié de l'écart entre la carte de l'entrepôt
     (85,4 px) et le jeton (61,2 px). */
  .conversation__carte::before {
    content: "";
    position: absolute;
    inset-block-end: 100%;
    inset-inline-end: calc(var(--conversation-noeud) / 2 - 2px);
    inline-size: 2px;
    block-size: calc(var(--sp-16) + 0.89rem - var(--sp-2) * 2 - var(--fs-xs) * var(--lh-snug));
    background-image: repeating-linear-gradient(to bottom, var(--c-blue) 0 4px, transparent 4px 9px);
    background-size: 100% 9px;
    animation: conversation-flux-vertical 1.1s linear infinite;
  }
  .conversation__pivot::after {
    display: none;
  }
  /* ⚠️ LE GARDE-FOU DE MOUVEMENT RÉDUIT EST REJOUÉ ICI, ET IL LE FAUT : celui de
     la règle générale, plus haut dans la feuille, ne porte que sur les deux
     tronçons du pivot — celui-ci est un troisième objet, et il est déclaré après.
     Les deux requêtes média s'imbriquent. */
}
@media (max-width: 30rem) and (prefers-reduced-motion: reduce) {
  .conversation__carte::before {
    animation: none;
  }
}
/* ---------- Surtitre ----------
   Filet court puis libellé en chasse fixe : c'est l'anatomie qu'emploient
   toutes les pages pilier de la maquette. Absent sur la page produit, mais le
   champ existe pour les suivantes. */
.hero-page__eyebrow {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--text-accent);
}

.hero-page__filet {
  width: var(--sp-6);
  height: 1px;
  background-color: currentColor;
}

/* ---------- Surtitre à forme de pilier ----------
   Demandé par Hugo le 18/08/2026 : la forme du pilier remplace le filet, et le
   libellé monte d'un cran. Le surtitre cesse d'être une ligne de service pour
   devenir la signature de la page.

   Il ne passe PAS en --fs-eyebrow-lg (17→20) : cette échelle est celle des
   surtitres en serif du hero d'accueil, pas d'une chasse fixe en capitales, où
   elle deviendrait envahissante. --fs-xs (14px) est le cran juste au-dessus du
   mono de 11px, ce qui est bien le « un peu plus gros » demandé.

   ⚠️ La casse est FORCÉE EN CAPITALES par la règle générale du surtitre. Le
   libellé écrit dans la fiche de la page (« Le dictionnaire de données ») se lit
   donc en « LE DICTIONNAIRE DE DONNÉES ». C'est cohérent avec le reste du site ;
   si tu veux la casse d'origine, c'est `text-transform` qu'il faut lever. */
.hero-page__eyebrow--glyphe {
  gap: var(--sp-3);
  font-size: var(--fs-xs);
}

/* La hauteur commande, la largeur suit le ratio de l'export (206 × 212). Les
   attributs width/height du gabarit sont là pour réserver la place avant le
   chargement, pas pour dimensionner. */
.hero-page__glyphe {
  /* Grossie deux fois le 18/08/2026 à la demande d'Hugo : 2.25rem (36px), puis
     3.25rem (52px), puis le double de celle-ci — 6.5rem, soit 104px.

     On rejoint donc presque la cote de la maquette, qui pose la forme à 118px.
     La différence est que la maquette la met SEULE sur sa ligne, au-dessus du
     surtitre, alors qu'ici elle est en ligne avec lui : à 104px c'est elle qui
     commande la hauteur de la ligne, et le libellé de 14px se centre dessus. */
  height: 6.5rem;
  width: auto;
  flex: none;
}

/* ---------- La pastille de nouveauté ----------
   Demandée par Hugo le 19/08/2026 pour la page du connecteur IA : une pastille
   violette « Nouveau », entre le surtitre et le titre.

   Elle est sur SA PROPRE LIGNE et non collée au surtitre : `.hero-page__texte`
   est une pile flex en `align-items: start`, donc la pastille se réduit à son
   libellé et la gouttière de la pile fait l'espacement. Rien à déclarer.

   ⚠️ L'APLAT VIENT DE --btn-primary-bg, PAS DE --c-blue DIRECTEMENT, et c'est
   ce qui la rend lisible sur les pages pilier. C'est le deuxième endroit du site
   où du BLANC est posé sur la teinte d'accent — le premier étant l'aplat de
   `.btn--primary`, dont cette variable est justement le fonçage mesuré. Les
   contrastes du libellé blanc, selon la page :

     aucun tonPage   --c-blue #3f58d7  5,85  ← le repli du `var()`
     plateforme      72 % de rose      4,88
     connecteur      90 % de violet    4,72
     dictionnaire    #00b3a4           3,08  ← hérité de l'arbitrage du bouton

   Le dictionnaire ne déclare pas --btn-primary-bg : sa pastille prendrait donc
   les 3,08 qu'Hugo a acceptés pour le libellé du bouton. Si cette page reçoit un
   jour un badge, c'est ce chiffre-là qu'il faut retrancher — un libellé de 11px
   n'a pas la même tolérance qu'un bouton en gras de 15,5px.

   ⚠️ LE DÉFAUT EST DANS LE `var()`, jamais dans une déclaration à part. Une règle
   `--btn-primary-bg: var(--c-blue)` posée ici masquerait la valeur que
   `.ton-page--*` pose sur `<main>`, puisqu'une propriété personnalisée définie SUR
   un élément l'emporte sur celle héritée d'un ancêtre. C'est la convention du
   site, expliquée dans CLAUDE.md. */
.hero-page__badge {
  margin: 0;
  /* Grossie à la demande d'Hugo le 19/08/2026. --fs-xs (14px) est le cran juste
     au-dessus du mono de 11px, et c'est le même pas que celui pris par
     `.hero-page__eyebrow--glyphe` juste au-dessus, pour la même raison : une
     chasse fixe en capitales devient envahissante bien avant une serif, donc on
     ne monte pas jusqu'à --fs-eyebrow-lg.

     Le rembourrage suit d'un cran lui aussi : à padding constant, une pastille
     dont seul le libellé grossit se met à serrer son texte. */
  padding: var(--sp-2) var(--sp-4);
  border-radius: var(--radius-full);
  background-color: var(--btn-primary-bg, var(--c-blue));
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  /* L'interlettrage élargi ajoute une espace APRÈS la dernière lettre, que
     l'aplat rend visible : la pastille paraît décentrée vers la droite. On la
     reprend sur le rembourrage de fin plutôt qu'en retirant le letter-spacing,
     qui fait partie de la grammaire des libellés en capitales du site. */
  padding-inline-end: calc(var(--sp-4) - 0.14em);
  color: var(--c-white);
}

/* Le gabarit de titre du site : serif SEMIBOLD, 1.18, -0.02em. Il vient de
   `.panneau__titre` (sections/_sequence.scss), que `.temoignages__titre`
   reprend déjà à l'identique. Ne pas y substituer --lh-tight ni --ls-display :
   ce sont les valeurs du hero d'accueil, calées sur un corps bien plus grand. */
.hero-page__titre {
  margin: 0;
  color: var(--c-blue-zodiac);
  text-wrap: balance;
}

.hero-page__chapeau {
  margin: 0;
  max-width: 34rem;
  font-size: var(--fs-lead);
  line-height: var(--lh-normal);
  color: var(--text-strong);
}

/* ---------- Actions ---------- */
.hero-page__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-6);
  margin-block-start: var(--sp-2);
}

/* ⚠️ C'EST `align-self` QUI CENTRE LE COUPLE EMPILÉ, ET NON `justify-content`.
   `.btn-group` porte bien `justify-content: center`, mais il n'avait rien à
   centrer : `.hero-page__texte` est une colonne flex en `align-items: start`,
   donc le groupe est dimensionné sur son contenu et ferré à gauche — chaque
   ligne se centrait dans cette boîte étroite, ce qui donnait le décalage entre
   les deux boutons empilés. C'est le groupe lui-même qu'il faut centrer dans sa
   colonne, ce que fait `align-self`.

   ⚠️ NE PAS PASSER PAR `.hero-page__texte { align-items: center }` : ça
   centrerait aussi le titre et le chapeau, qui portent leur propre mesure et
   restent ferrés à gauche. Ni par une largeur de 100 % : la boîte du groupe vaut
   exactement le plus large des deux boutons, et c'est ce qui fait tenir
   l'égalisation de `.btn-group`.

   La gouttière descend à `--sp-3` — celle de `.btn-group` lui-même, et donc celle
   du hero d'accueil : les deux ouvertures empilent désormais au même pas, sans
   troisième valeur introduite. `--sp-6` est calibré pour la mise en LIGNE, où il
   sépare deux boutons côte à côte ; empilés, il creuse.

   Tout est dans le `mq-down("xs")`, la plage même de l'empilement : au-dessus,
   la rangée du desktop n'est pas touchée. */
@media (max-width: 30rem) {
  .hero-page__actions {
    align-self: center;
    gap: var(--sp-3);
  }
}
.hero-page__lien {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  font-size: var(--fs-btn);
  font-weight: var(--fw-medium);
  color: var(--text-strong);
  text-decoration: none;
}

.hero-page__lien:hover {
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.25em;
}

/* =========================================================================
   Une seule colonne.

   Le texte reprend toute la largeur et la trame passe derrière : sur un écran
   étroit il n'y a plus de moitié droite à réserver, et la laisser vide
   coûterait un écran entier de vide avant le premier contenu.
   ========================================================================= */
@media (max-width: 60rem) {
  .hero-page__inner {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--sp-10);
  }
  .hero-page__grille {
    inset-inline: 0;
  }
  /* Les formes se réfugient sur le flanc droit et débordent de l'écran :
     à cette largeur, elles croiseraient le texte partout ailleurs. */
  .hero-page__forme--haut {
    --shape-w: 16%;
    top: 4%;
    left: 78%;
  }
  .hero-page__forme--milieu {
    --shape-w: 22%;
    top: 62%;
    left: 88%;
  }
  .hero-page__forme--bas {
    display: none;
  }
  /* ---------- MODE MOBILE : LE TEXTE SE CENTRE DANS UNE BOÎTE CRÈME ----------

     Demande d'Hugo du 27/08/2026 : « en mode mobile les titres des héros de
     pages produit et des différents produit sont centrés. Le titre et le texte
     sont dans une boîte crème arrondie. Dans ces heros, on ne met des formes
     qu'au niveau de l'illustration. »

     ⚠️ RIEN NE BOUGE AU-DESSUS DE `lg`. Les trois dispositifs vivent dans ce
     seul resserrage : le rendu de bureau validé reste au pixel près.

     ⚠️ LA BOÎTE EST LE FOND DE PAGE RENDU OPAQUE, et c'est ce qui lui donne sa
     raison d'être. Sous `lg` la trame reprend TOUTE la largeur (règle juste
     au-dessus) et passe donc derrière le titre, alors qu'elle était bornée à la
     moitié droite précisément pour ne pas le faire — voir le commentaire de
     `.hero-page__grille`. La boîte la rebouche là où il y a du texte et la
     laisse respirer autour. C'est le même service que le fondu de trame de
     `--centre`, qui pose son titre sur du crème nu par un masque.

     Elle lit donc `--bg-page` et jamais `--c-base-main` en clair : c'est le fond
     de la page qu'elle restitue, pas une teinte à elle. Même raison que
     `.clients__frame`.

     Pas de filet : c'est la trame tout autour qui dessine l'arête, et un filet
     ferait une seconde annonce. Corollaire à connaître — sur un hero à
     `trame: false` la boîte ne se voit plus, et il n'y a alors plus rien dont il
     faille détacher le texte.

     ⚠️ `--centre` EN A ÉTÉ EXCLUE UN JOUR, ET NE L'EST PLUS — demande d'Hugo du
     28/08/2026, « pareil sur le texte du hero header » de /a-propos/. L'exclusion
     tenait à ceci : `--centre` éteint sa trame aux deux bouts par un masque
     vertical, qui pose son titre sur du crème nu, donc la boîte y aurait fait
     double emploi. C'EST VRAI AU LARGE ET FAUX EN ÉTROIT, et le raisonnement est
     ce qu'il faut retenir : les cotes de ce masque sont des PARTS DE LA HAUTEUR DE
     LA SECTION (naissance à 20 %, opacité pleine à 36 %), calées sur des
     proportions de bureau où le surtitre et le titre occupent le premier quart et
     le chapeau le suivant. Une fois les colonnes empilées, ces proportions
     s'inversent — le titre passe à cinq ou six lignes quand la photo pleine
     largeur se réduit à son ratio — et le point où la trame devient opaque tombe
     DANS le titre au lieu de tomber sur le chapeau. Le masque ne protège donc plus
     rien, et c'est la boîte qui reprend le service.

     ⚠️ NE PAS « CORRIGER » LE MASQUE À LA PLACE : ses pourcentages n'ont pas de
     valeur juste en étroit, puisque la part du texte dans la hauteur dépend du
     nombre de lignes du titre, donc de la page. La boîte, elle, épouse le texte
     quel qu'il soit.

     La boîte proprement dite vaut donc pour TOUTES les dispositions ; le centrage
     reste réservé aux autres, `--centre` centrant déjà son texte à toute largeur.

     ⚠️ ELLE EST EN REVANCHE CONDITIONNÉE À LA PRÉSENCE DE LA TRAME, qui est sa
     seule raison d'être. Un hero à `trame: false` — celui de /cas-clients/ — n'a
     rien derrière son texte, donc rien dont il faille le détacher : sans ce test la
     boîte y serait invisible (du crème sur du crème) et n'y ajouterait que son
     rembourrage. Le test porte sur `__grille`, que le gabarit ne rend justement pas
     quand `trame` vaut `false`. Même conditionnement que la boîte jumelle de
     `texte-module--portrait`. */
  .hero-page:has(.hero-page__grille) .hero-page__texte {
    padding: var(--sp-10) var(--sp-8);
    /* Le rayon des boîtes du site — celui de `.temoignages__boite` et des cinq
       autres. Il n'a toujours pas de jeton ; voir CLAUDE.md. */
    border-radius: 1.75rem;
    background-color: var(--bg-page);
  }
  .hero-page:not(.hero-page--centre) .hero-page__texte {
    align-items: center;
    text-align: center;
  }
  /* Le chapeau porte sa propre mesure (34rem), qui le ferre à gauche dans un
     bloc centré : il faut la lui rendre symétrique. Même correctif que
     `.hero-page--centre .hero-page__chapeau`. */
  .hero-page:not(.hero-page--centre) .hero-page__chapeau {
    margin-inline: auto;
  }
  /* Les deux boutons se replient dès qu'ils ne tiennent plus sur une ligne :
     sans ça, la seconde resterait ferrée à gauche sous la première. */
  .hero-page:not(.hero-page--centre) .hero-page__actions {
    justify-content: center;
  }
  /* ---------- LES FORMES DESCENDENT AU NIVEAU DE L'ILLUSTRATION ----------

     La boîte étant opaque, une forme posée dans la zone du texte passerait
     dessous et n'en dépasserait que des échardes dans les gouttières. Les deux
     qui restent rejoignent donc l'illustration, qui occupe le bas de la section
     en une colonne.

     ⚠️ ELLES SONT ANCRÉES PAR LE BAS ET EN LONGUEUR FIXE, JAMAIS EN POURCENTAGE
     DE LA SECTION. La hauteur de la section dépend de celle du texte, qui change
     d'une page à l'autre : un `top: 62 %` retomberait n'importe où. Le bord bas
     de l'illustration, lui, est une ligne connue — c'est le bas de la section
     moins son `padding-block-end`, soit `--sp-section`. Ce qui est posé
     au-dessus de cette ligne d'une distance fixe tombe donc dans l'illustration,
     tant qu'elle est plus haute que cette distance : les figures et les exports
     livrés font tous au moins 300 px de haut à ces largeurs.

     Elles passent DERRIÈRE l'illustration (le décor est en `z-index: 0`), et
     c'est voulu : les illustrations sont détourées et les figures construites
     sont des cartes blanches posées sur du vide, donc les formes se voient dans
     leurs angles, dans les gouttières, et hors de l'écran que la section rogne.

     ⚠️ `top: auto` EST OBLIGATOIRE. Sans lui la forme porterait `top` ET
     `inset-block-end` alors que sa hauteur est déduite de `--shape-w` par le
     ratio : la sur-contrainte ne lève pas d'erreur, elle fait sauter l'inset de
     fin, et la forme resterait simplement où la règle large l'a mise. Même
     piège que l'ancrage large de `--centre`, en bas de ce fichier. */
  .hero-page:not(.hero-page--centre) .hero-page__forme--haut {
    --shape-w: 18%;
    top: auto;
    inset-block-end: calc(var(--sp-section) + 15rem);
    left: 84%;
  }
  .hero-page:not(.hero-page--centre) .hero-page__forme--milieu {
    --shape-w: 22%;
    top: auto;
    inset-block-end: calc(var(--sp-section) + 1rem);
    left: -11%;
  }
  /* En une colonne, la composition se resserre : à 528px elle prendrait presque
     un écran de haut à elle seule, juste après le chapeau. */
  .hero-page__trio {
    max-width: 26rem;
  }
}
/* =========================================================================
   DISPOSITION « centre » — le texte au milieu, le visuel pleine largeur dessous.

   Ajoutée le 20/08/2026 pour /a-propos/, dont la maquette ouvre sur une photo
   d'équipe pleine largeur sous un titre et un chapeau centrés.

   ⚠️ CE N'EST PAS L'OUVERTURE PAR DÉFAUT DU SITE, et il ne faut pas la
   généraliser : les six pages produit s'ouvrent toutes sur le couple
   texte-à-gauche / décor-à-droite, et cette régularité est ce qui les fait lire
   comme une famille. « À propos » n'en fait pas partie.

   ⚠️ LA TRAME REPREND TOUTE LA LARGEUR. Par défaut elle ne couvre que la moitié
   droite, pour ne pas passer sous le chapeau — voir le commentaire de
   `.hero-page__grille`. Ici le texte est centré et le visuel occupe toute la
   largeur : il n'y a plus de moitié à réserver, et une trame sur la seule droite
   se lirait comme un défaut de calage.
   ========================================================================= */
/* ⚠️ LA TRAME S'ÉTEINT AUX DEUX BOUTS, et c'est ce qui met le titre sur du crème
   nu. Demande d'Hugo du 20/08/2026 : « je veux que le titre soit sur fond crème et
   que la grille commence à devenir opaque sur le sous-titre. »

   Les cotes reprennent celles de `.grid-pattern--fade-both` (sections/_hero.scss),
   la primitive du site pour un fondu aux deux extrémités — mais RÉGLÉES ICI et non
   dans la primitive, parce que le point où la trame devient opaque doit tomber sur
   le chapeau de CE hero, pas sur celui de l'accueil qui la partage.

   Le repère de lecture, si les proportions bougent : le surtitre et le titre
   occupent le premier quart, le chapeau le suivant. La trame naît donc à 20 % et est
   pleine à 36 %, soit sur le chapeau.

   ⚠️ ET ELLE COUVRE TOUTE LA LARGEUR : par défaut elle est bornée à la moitié droite
   pour ne pas passer sous le chapeau (voir `.hero-page__grille`), ce qui n'a plus de
   sens quand le texte est centré et le visuel pleine largeur. */
.hero-page--centre .hero-page__grille {
  inset-inline: 0;
  mask-image: linear-gradient(to bottom, transparent 20%, black 36%, black 88%, transparent 100%);
}

/* =========================================================================
   LES FORMES PASSENT DE PART ET D'AUTRE DE LA PHOTO.

   Demande d'Hugo du 20/08/2026 — « enlève les formes de derrière le titre pour les
   mettre de part et d'autre de la photo ». Elles ne changent pas de nombre, seulement
   de place : derrière un titre centré, elles le concurrençaient ; dans les marges de
   la photo, elles l'encadrent.

   ⚠️ LES COTES SONT EN POURCENTAGE DE LA SECTION, comme les positions par défaut
   au-dessus. La photo occupe grossièrement la moitié basse et la largeur du
   conteneur (80rem) : les formes se posent donc DANS LES MARGES, au-delà de ce
   conteneur, à la hauteur de la photo.

   ⚠️ ELLES DÉBORDENT VOLONTAIREMENT DU BORD — la section les rogne, c'est le parti
   déjà pris par les positions par défaut. C'est ce qui les fait lire comme du décor
   qui passe derrière la page et non comme trois objets posés.
   ========================================================================= */
.hero-page--centre .hero-page__forme--haut {
  --shape-w: 11%;
  top: 46%;
  left: -3%;
}

.hero-page--centre .hero-page__forme--milieu {
  --shape-w: 8%;
  top: 76%;
  left: 94%;
}

.hero-page--centre .hero-page__forme--bas {
  --shape-w: 7%;
  top: 88%;
  left: 4%;
}

.hero-page--centre .hero-page__inner {
  grid-template-columns: minmax(0, 1fr);
  gap: clamp(var(--sp-10), 4vw, var(--sp-20));
}

.hero-page--centre .hero-page__texte {
  align-items: center;
  text-align: center;
  /* Le texte se centre sur la page, pas sur sa propre colonne : sans la mesure,
     un titre en `clamp()` irait d'un bord à l'autre et perdrait sa forme. */
  max-width: 56rem;
  margin-inline: auto;
}

/* Le chapeau a sa propre mesure (34rem) qui le ferre à gauche dans son bloc
   centré : il faut la lui rendre symétrique. */
.hero-page--centre .hero-page__chapeau {
  margin-inline: auto;
}

.hero-page--centre .hero-page__actions {
  justify-content: center;
}

/* ⚠️ LE VISUEL EST PLEINE LARGEUR ET GARDE LE RATIO DE LA MAQUETTE (1149 × 735).
   C'est ce qui empêche la page de sauter à l'arrivée de la photo — règle 6 de
   MAQUETTE.md. Le cadre en attente par défaut est au ratio 523 × 380 d'une
   capture posée à côté du texte, qui n'a rien à voir. */
.hero-page--centre .hero-page__placeholder {
  aspect-ratio: 1149/735;
}

/* La grille de portraits de /a-propos/ tenait toute la largeur du conteneur
   (80rem) une fois montée, ce qui écrasait le reste de la page sous une bande
   de ~820px de haut. Bridée à 64rem — sensiblement plus petite que la pleine
   largeur, mais elle garde une présence : à 46rem elle devenait un timbre-poste
   perdu dans la page. */
.hero-page--centre .hero-page__visuel {
  max-width: 64rem;
  margin-inline: auto;
}

/* =========================================================================
   LA VARIANTE SOMBRE — un aplat bleu nuit pleine largeur.

   Demandée par Hugo le 21/08/2026 pour la page des cas clients : « on va mettre
   le hero en sombre, ça sera la marque de fabrique de la partie cas client sur le
   site. On fera ça avec les parties éditorialisées. » C'est donc l'ouverture d'une
   famille de pages, pas un cas unique.

   ⚠️ C'EST LA SEULE BANDE SOMBRE PLEINE LARGEUR DU SITE, et l'exception à la
   règle 1 de MAQUETTE.md est bornée à ce bloc : un hero est collé sous l'en-tête
   collant, il n'a ni marge, ni arrondi, ni arête possible. Partout ailleurs un
   aplat sombre devient une boîte.

   ⚠️ RIEN À DÉCLARER POUR LE FOND. `.on-dark` redéfinit --bg-page, dont la section
   se sert déjà comme fond : la classe seule peint la bande. C'est précisément
   l'effet que la règle 1 signale comme un piège quand la classe est posée sur une
   section au lieu de sa boîte — ici il n'y a pas de boîte, et c'est ce qu'on veut.
   ========================================================================= */
/* --c-blue-zodiac donne 1,2 sur le bleu nuit. C'est le rôle qui suit le fond, comme
   `.reperes__titre` et `.texte-module--sombre .texte-module__titre`. */
.hero-page--sombre .hero-page__titre {
  color: var(--text-strong);
}

/* ⚠️ LA TRAME EST ÉTOUFFÉE, ET SON OPACITÉ EST CALÉE SUR LE FOND CLAIR. À 55 %
   elle est discrète sur le fond de page ; sur le bleu nuit le même fluo donne 11,5
   de contraste et le quadrillage passe devant le titre. Le bouton --grid-opacity
   existe pour ça (voir sections/_hero.scss). */
.hero-page--sombre .hero-page__grille {
  --grid-opacity: 0.16;
}

/* ⚠️ RESSERRÉ EN HAUT COMME EN BAS. Demande d'Hugo le 21/08/2026 : « mon but
   rapprocher la section [suivante] du header pour perdre moins de place sur la
   page. » Le rembourrage par défaut du bloc est celui d'un hero à deux colonnes où
   la moitié droite porte une capture ; une bande éditoriale n'a qu'un titre et un
   chapeau. */
.hero-page--sombre {
  padding-block: clamp(var(--sp-10), 4.5vw, var(--sp-16)) clamp(var(--sp-8), 3.5vw, var(--sp-12));
}

/* ⚠️ UNE « TRANSITION GRILLE » A OCCUPÉ CETTE PLACE LE 21/08/2026, PUIS A ÉTÉ
   RETIRÉE LE MÊME JOUR. Ce qu'elle faisait, pour qui voudrait la rebâtir : le décor
   du hero débordait sous sa section (`overflow: visible` + un `inset-block-end`
   négatif), portait une trame en BLEU NUIT — donc invisible sur la bande sombre,
   visible seulement dans le débord, sur le fond clair — et l'éteignait par un
   masque en dégradé. Le principe de la bande halftone, sans la bande : aucune
   hauteur ajoutée.

   Elle exigeait une chose de sa voisine : Être TRANSPARENTE, puisqu'une section qui
   suit est au-dessus dans l'ordre d'empilement et repeindrait le débord.

   Hugo l'a écartée en même temps qu'il repassait le hero de /cas-clients/ en clair
   (« enlève la grille et le fond foncé sur le hero »). La faculté `fond: sombre`
   reste, elle, parce que la règle 18 vit désormais dans `hero-cas` — mais sans
   raccord : un hero sombre devant une section claire donne une arête franche. */
/* ---------- Les formes sur la bande sombre ----------
   ⚠️ RÈGLE DORMANTE : aucune page n'emploie `fond: sombre` aujourd'hui. Elle est
   gardée parce qu'elle CORRIGE la variante — voir plus bas, la conclusion précédente
   était fausse — et que `hero-cas` hérite du raisonnement de la règle 18.

   ⚠️ ELLES ONT ÉTÉ MASQUÉES PUIS RÉTABLIES LE 21/08/2026, et la mesure qui les avait
   fait disparaître est justement celle qui dit comment les rendre : `forme--bleu`
   vaut --c-blue (#3f58d7), soit **1,6** sur le bleu nuit. Deux des trois s'éteignaient
   donc, et il ne restait qu'une forme fluo isolée — ce qui déséquilibrait la
   composition au lieu de l'habiller. La conclusion d'alors était de tout retirer ; la
   bonne était de REPEINDRE les deux qui tombent.

   --c-blue-400 (#7885ce) donne 1,9 sur le bleu nuit : assez pour qu'une forme se
   détache, pas assez pour concurrencer le titre. Le fluo, lui, ne bouge pas — il
   porte l'accent de la composition, et c'est ce contraste entre deux formes sourdes
   et une vive qui fait la lecture.

   ⚠️ LA RETOUCHE PASSE PAR --shape-color, PAS PAR --c-blue. Remapper --c-blue sur la
   section aurait tout attrapé au passage — la pastille de nouveauté, l'aplat du
   bouton principal, une mise en valeur `((bleu))` — et c'est exactement le piège que
   la règle 10 de MAQUETTE.md décrit. `.forme` lit `--shape-color` en premier : c'est
   le point d'entrée prévu pour ça. */
.hero-page--sombre .hero-page__forme--haut,
.hero-page--sombre .hero-page__forme--bas {
  --shape-color: var(--c-blue-400);
}

/* =========================================================================
   UN HERO CENTRÉ SANS VISUEL — les formes remontent le long du texte.

   Demande d'Hugo le 21/08/2026 sur /cas-clients/ : « rapprocher les formes du
   titre, sans passer dessous. Aujourd'hui les formes sont coupées à la transition
   avec la section suivante. »

   ⚠️ LE DÉFAUT VENAIT DE `--centre`, QUI SUPPOSE UN VISUEL EN DESSOUS. Ses trois
   formes sont posées à 46 %, 76 % et 88 % de la hauteur, c'est-à-dire le long de la
   photo pleine largeur d'/a-propos/. Sans visuel, la bande ne fait plus que le titre
   et son chapeau : les deux dernières formes tombent alors sur l'arête basse et la
   section les rogne — ce qui se lit comme un accident, là où le débord LATÉRAL du
   hero par défaut se lit comme un parti.

   ⚠️ LE SCOPE EST `:has()` ET NON UNE CLASSE DE PLUS, pour la raison déjà écrite
   dans sections/_texte-module.scss : c'est la PRÉSENCE DU VISUEL qui commande, et le
   gabarit n'a rien à savoir de cette règle. Sa spécificité (trois classes) passe
   d'ailleurs devant celle de `--centre`, donc l'ordre du fichier ne la porte pas à
   bout de bras.

   ⚠️ « SANS PASSER DESSOUS » SE CALCULE SUR LA MESURE DU TEXTE, ET L'ARÊTE EST
   DÉRIVÉE, JAMAIS UN POURCENTAGE. `--centre` donne au bloc de texte
   `max-width: 56rem`, centré sur la section : son bord gauche tombe donc à
   `calc(50% - 28rem)` et son bord droit à `calc(50% + 28rem)`, à toute largeur. Les
   trois formes s'y accrochent — les deux de gauche par `inset-inline-end`, celle de
   droite par `left`, la même expression sur les deux flancs.

   ⚠️ C'EST CE QUI LES RAPPROCHE DU TITRE, et un pourcentage ne pouvait pas le faire.
   Les cotes précédentes (13 % et 87 %) étaient justes À 1024px SEULEMENT, la largeur
   où le conteneur suit encore la fenêtre. Au-delà il se fige à 80rem et se recentre,
   donc le texte se resserre en pourcentage pendant que les formes, elles, restaient
   sur leur 13 % : à 1440px il y avait 130px de vide entre une forme et le texte, à
   2560px près de 500. L'arête dérivée supprime cet écart à toutes les largeurs d'un
   coup — demande d'Hugo du 26/08/2026, « rapproche-les un peu du titre ».

   ⚠️ AGRANDIR UNE FORME, C'EST DÉPLACER SON BORD EXTÉRIEUR, JAMAIS SON BORD INTÉRIEUR.
   L'arête étant maintenant portée par l'ancrage, la largeur pousse d'elle-même vers
   le bord de l'écran, où la section rogne : c'est le débord volontaire du hero par
   défaut. Rien à recalculer en grossissant, et c'est tout l'intérêt.

   ⚠️ CE QUI PEUT ENCORE MORDRE SUR LE TEXTE, C'EST LA ROTATION, pas la largeur. Les
   trois formes oscillent de ±35°, ±30° et ±42,5° autour de leur repos (voir
   assets/js/shapes.js), et une forme presque carrée qui tourne de 35° déborde de sa
   boîte d'environ 20 % de sa largeur. L'arête est donc celle de la BOÎTE de texte,
   pas de son encre : un titre centré ne remplit pas ses 56rem, et c'est cette marge-là
   que la rotation consomme. C'était déjà le cas à 1024px avant cette révision — rien
   de nouveau n'est introduit ici, mais c'est ce qui interdit de resserrer davantage.

   ⚠️ LEURS TAILLES SONT PLAFONNÉES EN REM, ET C'EST CE QUI REND LE CALCUL SÛR.
   `--shape-w` est un pourcentage de la LARGEUR de la section, alors que ce qui doit
   tenir, c'est la HAUTEUR : une bande de titre centré ne grandit presque pas avec
   l'écran, tandis qu'une forme à 13 % passe de 166px à 333px entre 1280 et 2560. En
   pourcentage seul, une forme ressortait sous l'arête basse sur les très grands
   écrans. Le `min()` fige la cote haute, et les hauteurs restent alors bornées quelle
   que soit la largeur.

   ---------------------------------------------------------------------------
   LES TROIS FORMES NE SE CHEVAUCHENT PLUS — demande d'Hugo du 26/08/2026, et c'est
   une RÉPARTITION qui la résout, pas un réglage de cotes.

   ⚠️ DEUX GRANDES FORMES NE TIENNENT PAS SUR UN MÊME FLANC, ET ÇA SE CHIFFRE. Sur une
   bande d'environ 28rem (~381px : c'est l'hypothèse de tout ce qui suit, corroborée
   par les bornes qu'écrivait déjà la version précédente de ce bloc), le losange à
   13rem pesait 182px de haut et l'écu à 9rem 130px, soit 82 % de la bande à eux deux.
   Il restait 18 % pour la marge haute, la marge basse ET l'écart entre les deux.

   ⚠️ ET C'EST LA DÉRIVE QUI REND LA CHOSE IMPOSSIBLE, PAS LA TAILLE. Les deux oscillent
   verticalement de ±22,5px et ±27,5px (`data-float-y` à 45 et 55, amplitude TOTALE —
   voir assets/js/shapes.js) : au pire de leur phase elles se rapprochent donc de 50px,
   soit 13 % de la bande. L'écart au repos doit couvrir ça sans quoi elles se touchent
   par intermittence — un défaut qu'on ne voit pas sur une capture. 82 % + 13 % ne
   laissent rien. Aucune valeur de `top` ne s'en sort.

   ⚠️ C'EST DONC L'ÉCU QUI CHANGE DE FLANC, et le choix des deux qui cohabitent n'est
   pas libre : le flanc partagé doit recevoir LES DEUX PLUS PETITES. Citron (11rem,
   38 %) et écu (8rem, 30 %) font 68 %, ce qui laisse 32 % — de quoi absorber les 13 %
   de dérive et garder un écart lisible. Le losange, la plus grande, reste donc seul
   à gauche, et il en profite pour grossir encore (14rem).

   ⚠️ L'ÉCU EST ANCRÉ PAR LE BAS (`inset-block-end`) ET NON PAR LE HAUT. C'est ce qui
   rend l'écart auto-ajustable : sur une bande plus haute que l'hypothèse, l'espace
   entre le citron et lui grandit tout seul au lieu de le pousser sous l'arête. Sa
   marge basse (8 %, ~30px) couvre ses ±27,5px de dérive — c'est exactement le défaut
   corrigé plus tôt dans ce fichier, une forme qui vient battre l'arête basse au fil
   de son oscillation et se lit comme un accident.

   ⚠️ LES DEUX FORMES DE DROITE NE SONT PAS À FLEUR DE LA MÊME ARÊTE : l'écu est décalé
   de 2,5rem vers le bord. Deux formes alignées au cordeau sur un même flanc se lisent
   comme une frise, pas comme un décor — c'est la règle que pose déjà
   partials/formesDecor.njk pour ses deux formes de gauche.
   --------------------------------------------------------------------------- */
.hero-page--centre:not(:has(.hero-page__visuel)) {
  /* La demi-mesure du bloc de texte : 56rem centrés, donc 28rem de part et d'autre
     du milieu de la section. Une seule déclaration, lue par les trois formes — deux
     cotes séparées divergeraient au premier réglage sans que rien ne le signale. */
  --hero-arete: 28rem;
}

/* Le losange, seul sur le flanc gauche : bas de forme à 76 % au repos, 81 % au pire
   de sa dérive. Il a toute la hauteur pour lui. */
.hero-page--centre:not(:has(.hero-page__visuel)) .hero-page__forme--haut {
  --shape-w: min(18%, 14rem);
  top: 24%;
  left: auto;
  inset-inline-end: calc(50% + var(--hero-arete));
}

/* Le citron fluo, en haut du flanc droit : 6 % à 44 %. */
.hero-page--centre:not(:has(.hero-page__visuel)) .hero-page__forme--milieu {
  --shape-w: min(14%, 11rem);
  top: 6%;
  left: calc(50% + var(--hero-arete));
}

/* L'écu, en bas du même flanc : ancré par le bas, il commence à 62 %. L'écart au
   repos avec le citron fait donc 18 % de la bande (~67px), dont la dérive des deux
   ne peut consommer que 46px. */
.hero-page--centre:not(:has(.hero-page__visuel)) .hero-page__forme--bas {
  --shape-w: min(11%, 8rem);
  top: auto;
  inset-block-end: 8%;
  left: calc(50% + var(--hero-arete) + 2.5rem);
}

/* Écrans étroits : le texte prend toute la largeur, il n'y a plus de marge latérale
   où loger une forme sans la faire passer dessous. Les deux qui restent se réfugient
   hors champ sur les flancs, où le `overflow: hidden` de la section les rogne — c'est
   le même parti que le repli du hero par défaut, qui ne s'applique pas ici parce que
   `--centre` le masque. */
@media (max-width: 60rem) {
  .hero-page--centre:not(:has(.hero-page__visuel)) .hero-page__forme--haut {
    --shape-w: 20%;
    top: 6%;
    /* ⚠️ L'ancrage large passe par `inset-inline-end` : sans cette remise à `auto`,
       la boîte porterait ses deux insets ET une largeur explicite. La sur-contrainte
       ne lève pas d'erreur, elle fait sauter l'inset de fin — la forme resterait
       simplement où la règle large l'a mise. */
    inset-inline-end: auto;
    left: -9%;
  }
  .hero-page--centre:not(:has(.hero-page__visuel)) .hero-page__forme--milieu {
    --shape-w: 22%;
    top: 56%;
    left: 89%;
  }
  .hero-page--centre:not(:has(.hero-page__visuel)) .hero-page__forme--bas {
    display: none;
  }
}
/* =========================================================================
   BLOC « intertitre » — blocs/intertitre.njk.

   Un titre de section posé sur le fond de page, qui ouvre un groupe de blocs.

   ⚠️ SA GOUTTIÈRE BASSE EST VOLONTAIREMENT COURTE. Le bloc n'existe que pour
   annoncer ce qui le suit immédiatement : s'il respirait autant qu'une section
   entière, le titre flotterait entre deux groupes au lieu d'appartenir au
   second. Le haut, lui, garde la respiration pleine de section — c'est là que
   se fait la coupure avec ce qui précède.
   ========================================================================= */
.intertitre {
  background-color: var(--bg-page);
  padding-block: var(--sp-section) 0;
}

.intertitre__inner {
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
}

/* Le titre ne déclare rien de sa police : _base.scss pose déjà le titrage et la
   taille du niveau. Il reprend le bleu profond des titres de section sur fond
   clair, comme `.cadre__titre` et `.connecteurs__titre`. */
.intertitre__titre {
  margin: 0;
  max-width: 24ch;
  color: var(--c-blue-zodiac);
  text-wrap: balance;
}

.intertitre__chapeau {
  margin: 0;
  max-width: 42rem;
  font-size: var(--fs-base);
  line-height: var(--lh-normal);
  color: var(--text-strong);
}

/* ---------- Le surtitre ----------
   Même anatomie que l'`eyebrow` de `hero-page` : chasse fixe, capitales, teinte
   d'accent. Les capitales sont ici et pas dans les données. */
.intertitre__surtitre {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--text-accent);
}

/* =========================================================================
   Variante centrée — un ÉNONCÉ seul au milieu de la page.

   ⚠️ ELLE REPREND UNE RESPIRATION BASSE PLEINE, et c'est le point : la gouttière
   basse du bloc est volontairement courte parce qu'il annonce ce qui le suit
   immédiatement. Centré, il n'annonce plus rien — c'est une affirmation qui se
   tient seule, et elle a besoin d'air des deux côtés. Sans ça elle collerait à la
   section suivante et se lirait comme son titre.

   Le titre y perd aussi sa mesure de 24ch : un énoncé centré sur trois lignes
   étroites se lit comme un titre de section, sur une mesure large comme une
   affirmation.
   ========================================================================= */
.intertitre--centre {
  padding-block: var(--sp-section);
}

.intertitre--centre .intertitre__inner {
  align-items: center;
  text-align: center;
}

.intertitre--centre .intertitre__titre {
  max-width: 46ch;
}

.intertitre--centre .intertitre__chapeau {
  max-width: 52ch;
}

/* =========================================================================
   VARIANTE QUOTE — une barre fluo devant l'énoncé.

   Demandée par Hugo le 20/08/2026 pour la section 02 de /a-propos/. C'est le même
   objet que l'`exergue` de `texte-module` : les valeurs sont reprises de là, pas
   réinventées.

   ⚠️ C'EST LE BLOC QUI EST CENTRÉ, PAS LE TEXTE, et c'est ce qui rend le couple
   `quote` + `centre` légitime alors que je l'avais écarté sur `texte-module`. Là
   j'avais centré le TEXTE, et une barre devant un texte centré ne touche plus son
   premier caractère : elle flotte à distance et redevient un ornement. Ici le texte
   reste ferré à gauche DANS un bloc de mesure bridée, lui-même centré sur la page —
   la barre reste donc collée au premier caractère de chaque ligne.

   ⚠️ D'OÙ LA MESURE BRIDÉE, QUI EST STRUCTURELLE ICI et pas cosmétique : sans elle
   le bloc occupe toute la largeur, le centrage ne se voit pas, et la barre se
   retrouve au bord de la page.
   ========================================================================= */
.intertitre--quote .intertitre__inner {
  align-items: start;
  text-align: start;
}

.intertitre--quote .intertitre__titre {
  /* La barre. 4px et non 1px : ce n'est pas un filet de séparation mais une
     attache — à 1px sur le crème elle disparaîtrait à côté d'un corps de 32px. */
  border-inline-start: 4px solid var(--c-fluo);
  padding-inline-start: clamp(var(--sp-4), 1.6vw, var(--sp-6));
  max-width: 52ch;
}

/* ⚠️ CENTRÉ, LE TEXTE L'EST AUSSI, ET PAS SEULEMENT LE BLOC. Hugo l'a redemandé le
   20/08/2026 — « la quote de Notre raison d'être doit rester centrée » — après avoir
   vu la première version, où le bloc était centré mais son texte ferré à gauche.

   Ça revient sur le raisonnement que j'avais tenu sur `texte-module` : « une barre
   devant un texte centré ne touche plus son premier caractère, elle flotte et
   redevient un ornement ». C'est vrai, et c'est assumé ici — ce qui compte davantage
   sur cet énoncé, c'est qu'il se tienne au milieu de la page. La barre devient un
   repère de gauche plutôt qu'une attache.

   ⚠️ NE PAS « CORRIGER » ÇA VERS LE FER À GAUCHE au motif du commentaire de
   `texte-module` : les deux cas sont contradictoires exprès, et c'est le second qui
   est le plus récent. */
.intertitre--centre.intertitre--quote .intertitre__inner {
  align-items: center;
  text-align: center;
}

/* =========================================================================
   EN MOBILE, L'INTERTITRE EST CENTRÉ — demandé par Hugo le 27/08/2026 pour
   « Vos outils au quotidien » sur /produit/.

   ⚠️ SEUL LE TEXTE EST CENTRÉ, PAS LA RESPIRATION. La variante `--centre`
   reprend une gouttière basse pleine parce qu'un énoncé centré n'annonce plus
   rien ; ici le bloc continue d'annoncer les boîtes qui le suivent et doit
   leur rester collé. C'est donc l'alignement seul qui bascule, et le
   resserrement de groupe de `_texte-panneau.scss` reste valable.

   ⚠️ UNE SEULE CLASSE, ET C'EST CE QUI ÉPARGNE LA VARIANTE `quote`.
   `.intertitre--quote .intertitre__inner` ferre son contenu à gauche avec deux
   classes et gagne donc ici : une barre devant un texte centré ne touche plus
   son premier caractère et redevient un ornement. Une quote reste ferrée à
   gauche en mobile, sauf si elle déclare aussi `centre` — ce que fait la seule
   instance du site (/a-propos/), qui ne bouge donc pas.
   ========================================================================= */
@media (max-width: 60rem) {
  .intertitre__inner {
    align-items: center;
    text-align: center;
  }
}
/* =========================================================================
   BLOC « texte-panneau » — blocs/texte-panneau.njk.
   Origine : « Le dictionnaire de données », Figma 185:7025.

   Texte à gauche, panneau encadré à droite. Le panneau se lit en trois
   bandes : une barre d'étiquettes, le visuel, puis le lien.

   ⚠️ AUCUNE COULEUR EN DUR DANS CE FICHIER — et ce n'est pas une préférence
   de style, c'est ce qui rend le bloc utilisable. Il est en bleu nuit sur la
   page produit et sur le crème ailleurs ; un `--c-blue-zodiac` posé sur le
   titre donnerait du bleu nuit sur bleu nuit. Tout passe par les rôles
   (--text-strong, --text-muted, --text-accent, --border-subtle) que la classe
   `.on-dark` de _tokens.scss bascule en bloc.

   Seule exception admise : le filet du panneau, qui reste fluo dans les deux
   fonds — c'est la couleur de cadre du site, elle tient sur crème comme sur
   bleu nuit.
   ========================================================================= */
.texte-panneau {
  --texte-panneau-gouttiere: clamp(var(--sp-8), 5vw, 6rem);
  /* La cote unique du glyphe de pilier, et le débord qui s'en déduit. Les deux
     vivent ici et non sur la boîte : l'espacement de la section les relit. */
  --panneau-glyphe-taille: clamp(6rem, 10vw, 15.5rem);
  --panneau-glyphe-debord: calc(var(--panneau-glyphe-taille) * 0.4);
  position: relative;
  background-color: var(--bg-page);
  padding-block: var(--sp-section);
  /* ⚠️ Le glyphe déborde de la boîte sur le côté. Sur un écran plus étroit que
     la boîte (96rem), ce débord sortirait de la page et ouvrirait une barre de
     défilement horizontale. `clip` le coupe au bord — et non `hidden`, qui
     ferait de la section un conteneur de défilement et casserait au passage tout
     positionnement collant à l'intérieur. L'axe vertical reste `visible` pour
     que le débord vers le haut, lui, continue de se voir. */
  overflow-x: clip;
  overflow-y: visible;
}

/* Le supplément d'espacement demandé le 25/08/2026 : « augmenter la distance
   entre les boîtes pour qu'il n'y ait pas de conflits ». Le glyphe monte
   au-dessus de sa boîte, donc c'est en HAUT qu'il faut de la place, et
   exactement la hauteur de son débord — relue, jamais réécrite.

   Sur la boîte qui précède, rien à ajouter : c'est le même intervalle vu de
   l'autre côté. */
.texte-panneau:has(.texte-panneau__glyphe) {
  padding-block-start: calc(var(--sp-section) + var(--panneau-glyphe-debord));
}

/* ---------- La boîte ----------
   L'aplat n'est pas pleine largeur : c'est une BOÎTE posée sur le crème,
   exactement comme `.temoignages__boite` et `.sequence__boite` dont les cotes
   sont reprises à l'identique — 96rem, rayon 28px, aucun contour. Sur un aplat
   franc posé sur crème, le contraste fait déjà l'arête.

   C'est ce qui dispense le bloc de bandes de trame : la boîte porte sa propre
   rupture.

   ⚠️ LA GÉOMÉTRIE EST SÉPARÉE DE LA COULEUR, et il a fallu la séparer : elle
   était soudée à `--sombre`, si bien qu'un autre fond de boîte perdait d'un
   coup largeur, rayon et gouttières. Les cotes appartiennent à « il y a une
   boîte », pas à « elle est bleu nuit ». */
.texte-panneau--boite .texte-panneau__boite {
  position: relative;
  max-width: 96rem;
  margin-inline: auto;
  border-radius: 1.75rem; /* 28px, comme les deux autres boîtes du site */
  padding-block: clamp(var(--sp-12), 4.7vw, 5.5rem);
}

/* ---------- Le glyphe du pilier, au coin de la boîte ----------
   Demandé par Hugo le 25/08/2026 : la forme de l'outil posée au coin supérieur,
   DU CÔTÉ DE L'IMAGE, débordant de la boîte.

   C'est le même objet que le `glyphe` de `hero-page` — un des quatre fichiers
   de Assets/Shapes/, un par pilier — et il se rend de la même façon : un `<img>`
   et JAMAIS un masque. Ces exports-là sont déjà remplis de deux couleurs, la
   teinte vive du pilier pour la forme et une teinte claire pour l'icône d'outil
   qu'ils enferment ; un masque écraserait le dessin intérieur d'une seule
   couleur et il ne resterait qu'une silhouette.

   Il se lit sur la boîte parce que celle-ci porte la valeur SOMBRE du pilier
   quand le glyphe en porte la valeur vive : #8B5CFF sur le violet assombri,
   #00B3A4 sur le vert, etc. C'est la seule raison pour laquelle ces formes n'ont
   besoin d'aucun filet ni contour.

   ⚠️ UNE SEULE COTE, --panneau-glyphe-taille, ET TOUT EN DÉCOULE : le débord
   au-dessus de la boîte, le débord sur le côté, et le supplément d'espacement
   de la section (voir plus bas). Écrites en clair, ces trois valeurs auraient
   dérivé au premier ajustement, et c'est l'espacement qui se serait tu — deux
   boîtes qui se chevauchent ne lèvent aucune erreur. */
.texte-panneau__glyphe {
  position: absolute;
  /* ⚠️ 2 ET NON 1, DEPUIS QUE LE MOTIF EXISTE. Le motif a obligé à poser
     `position: relative; z-index: 1` sur `.texte-panneau__inner` (voir plus
     bas) ; à égalité de z-index c'est le dernier du DOM qui gagne, donc l'image
     serait passée devant le bas du glyphe. Et il y a bien recouvrement : le
     glyphe pend de 60 % de sa taille sous l'arête haute de la boîte, soit
     jusqu'à 9,3rem, quand la marge haute de la boîte plafonne à 5,5rem. */
  z-index: 2;
  inline-size: var(--panneau-glyphe-taille);
  block-size: auto;
  inset-block-start: calc(var(--panneau-glyphe-debord) * -1);
  inset-inline-end: calc(var(--panneau-glyphe-taille) * -0.3);
}

/* ⚠️ AUCUNE COMPENSATION PAR FICHIER ICI, ET IL NE FAUT PAS EN REMETTRE.

   Hugo, le 25/08/2026 : « le logo connecteur est plus petit que les autres. »
   Il ne l'était pas — c'était son CANEVAS qui était plus grand. Boîtes de dessin
   mesurées au `getBBox()` dans Chrome sur les quatre fichiers : les quatre
   dessins font 205px de large, mais celui du connecteur était posé au milieu
   d'un viewBox de 256, avec 25px de marge de chaque côté. Le CSS dimensionnant
   la boîte, le dessin sortait 20 % plus petit.

   Une compensation CSS a été écrite puis retirée le jour même, et c'est elle
   qu'il ne faut pas rétablir : grossir la boîte grossit AUSSI sa marge interne,
   si bien que le dessin retombait 21px trop bas et 25px trop en dedans. Le
   remettre en place aurait demandé trois nombres tirés d'un seul fichier — un
   facteur d'échelle et deux décalages — tous faux au premier réexport, et sans
   la moindre alerte.

   C'est donc le FICHIER qui a été recadré : `Assets/Shapes/Shape - Connecteur
   IA.svg` porte maintenant `viewBox="25 22 205 211.4"` au lieu de `0 0 256 260`,
   ce qui l'aligne sur ses trois voisins. Seul cet attribut a changé, aucune
   donnée de tracé. Le même défaut rapetissait la forme sur /connecteur-ia/ et
   /dictionnaire/, où elle est corrigée du même coup. */
/* Il suit l'image, pas la boîte : quand `disposition: inverse` renvoie le visuel
   à gauche, le glyphe passe au coin gauche avec lui. C'est ce qui fait qu'il
   alterne avec le reste au lieu de rester planté du même côté. */
.texte-panneau--inverse .texte-panneau__glyphe {
  inset-inline-end: auto;
  inset-inline-start: calc(var(--panneau-glyphe-taille) * -0.3);
}

/* =========================================================================
   LE MOTIF, SOUS L'IMAGE — demandé par Hugo le 25/08/2026.

   Un graphique en trame posé dans le coin bas de la boîte, DU CÔTÉ DE L'IMAGE,
   surdimensionné et coupé par l'arrondi : il n'en affleure qu'un morceau, sous
   la capture. C'est la grammaire du donut de « Des données contrôlées », et son
   avertissement vaut ici : posé derrière la boîte ENTIÈRE il n'affleurerait que
   dans la gouttière laissée sur un écran large, et posé derrière l'image seule
   il n'en resterait que des bribes autour d'elle.

   L'atome est partials/motif.njk, son apparence est dans _components.scss.
   Cette feuille ne pose que le PLACEMENT et les TEINTES.
   ========================================================================= */
/* L'enveloppe de clip. Elle épouse la boîte et reprend SON arrondi, ce qui
   laisse le motif déborder du coin sans sortir de la boîte.

   ⚠️ ELLE EXISTE PARCE QUE LA BOÎTE NE PEUT PAS PORTER `overflow: hidden` : le
   glyphe du pilier déborde volontairement par le haut, et un overflow sur la
   boîte le décapiterait. `border-radius: inherit` plutôt que la valeur
   recopiée : le jour où l'arrondi de la boîte change, le clip suit tout seul. */
.texte-panneau__decor {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  border-radius: inherit;
  pointer-events: none;
}

/* ⚠️ IL FAUT REMETTRE LE CONTENU AU-DESSUS. L'enveloppe est positionnée, donc
   elle peint par-dessus le contenu en flux normal quel que soit l'ordre du DOM ;
   sans cette règle le motif passe DEVANT le texte et l'image. `z-index: -1` sur
   l'enveloppe serait l'autre voie, mais elle enverrait le motif derrière le fond
   de la boîte, où il est simplement invisible — c'est le même piège que le
   tracé de `.chemin`. */
.texte-panneau__inner {
  position: relative;
  z-index: 1;
}

/* LE PIED EST COLLÉ À L'ARÊTE BASSE, ET LA HAUTEUR EST LA COTE DE RÉFÉRENCE.
   Hugo, le 25/08/2026 : « qu'ils soient collés en bas des blocs et qu'ils
   prennent 70 % de la hauteur max de la boîte. »

   Quatre formulations se sont succédé le même jour. Les trois premières sont
   écartées, il ne faut pas y revenir :

   ① UNE TAILLE FIXE en rem, ancrée par le bas. La cime dépendait alors du dessin
     et non de la boîte : elle tombait vers 47 % et différait d'une brique à
     l'autre. « Ils sont tous très bas ».

   ② LA HAUTEUR, AVEC UN PLAFOND (« la cime à 60 % »). Elle réglait la cime, mais
     le plafond destiné aux boîtes très hautes MORDAIT sur les quatre briques —
     leur hauteur avait été sous-estimée. Le motif restait petit, et rien dans la
     règle ne le disait. « Ils sont toujours petits ».

   ③ LA LARGEUR DE LA BOÎTE, sans plafond. Elle donnait la bonne taille — validée
     — mais la cime retombait où la hauteur de chaque brique voulait, donc encore
     inégale d'une brique à l'autre.

   La hauteur revient donc, mais SANS PLAFOND cette fois : c'est le plafond qui
   avait fait échouer ②, et il n'a plus de raison d'être puisqu'un pourcentage de
   hauteur ne peut pas dépasser la boîte. La cime est à 70 % sur les quatre
   briques, par construction.

   ⚠️ CE QUE ÇA COÛTE, ET C'EST UN ARBITRAGE : les quatre briques n'ont pas la
   même hauteur, donc leur motif n'a plus tout à fait la même ÉCHELLE DE DESSIN.
   On échange une échelle commune contre une cime commune. C'est la cime qui a été
   demandée deux fois ; si l'écart d'échelle se voit, c'est `--motif-taille` qu'il
   faut monter, jamais une exception par brique.

   ⚠️ L'ARÊTE INTÉRIEURE EST ÉPINGLÉE À L'AXE DE LA BOÎTE, et l'ancrage a changé
   pour ça : `inset-inline-start: 50%` (ou `-end` en disposition inverse) plutôt
   qu'un `calc(50% - la taille)`. La largeur n'est plus une cote écrite mais un
   résultat du rapport, donc il n'y avait plus rien à en soustraire ; épingler
   l'arête directement est de toute façon plus simple et vrai à toute taille. Tout
   le surplus sort par le côté EXTÉRIEUR, où l'enveloppe le coupe.

   Ce n'est pas un raffinement, c'est une garantie de LISIBILITÉ. Du blanc posé
   sur la série la plus claire du motif ne donne que ~1,9 : un motif qui
   traverserait vers la colonne de texte dégraderait le corps de texte sans
   qu'aucune règle ne l'annonce. Épinglé à l'axe, il ne peut pas.

   Le débord bas a disparu avec le pied collé. Il servait à ce que les quatre
   briques se ressemblent ; une arête franche commune le fait mieux.

   ⚠️ AUCUN `aspect-ratio` ICI, ET C'EST LA CORRECTION DU 25/08/2026. « Il y a
   toujours un espace entre le bas du bloc et le dessin. » Le pied de l'élément
   était bien collé — c'était l'ENCRE qui ne l'était pas : chaque dessin portait
   une marge dans son viewBox, une dizaine de pourcents en bas. Le débord de 2rem
   la cachait ; à zéro elle s'est vue. Les viewBox de partials/motif.njk serrent
   désormais l'encre, donc chaque dessin a SON rapport (de 1,00 à 2,00) et c'est
   `inline-size: auto` qui le lit. Un `aspect-ratio` écrit en dur ici rétablirait
   la marge sous une autre forme : une bande vide sur un axe.

   ⚠️ L'ANCRAGE EST LE COIN, PAS L'AXE. L'arête intérieure était épinglée à 50 %
   pour garantir que le motif ne traverse jamais vers la colonne de texte — du
   blanc sur sa série la plus claire ne donne que ~1,9. Mais un motif dont la
   largeur n'atteignait pas la demi-boîte laissait alors un blanc DANS LE COIN,
   ce qui est précisément ce qui était demandé d'éviter. Le coin gagne : c'est lui
   qui a été demandé. La garde-fou tombe, et ce qu'il protégeait reste vrai — si
   le dessin vient sous le texte, c'est `--motif-taille` qu'il faut baisser. */
.texte-panneau__motif {
  position: absolute;
  /* Part de la HAUTEUR de la boîte. Seule cote à toucher pour grossir ou réduire
     un motif : la largeur en découle du rapport de son propre viewBox.

     ⚠️ LE DÉFAUT EST DANS LE `var()`, JAMAIS DÉCLARÉ ICI. Une propriété posée SUR
     l'élément bat la même propriété héritée d'un ancêtre : écrite en déclaration,
     elle masquerait la valeur que la boîte pose par pilier et les trois réglages
     ci-dessous n'auraient aucun effet, sans la moindre alerte. Même règle que
     `--motif-base` et que `--btn-primary-bg`. */
  block-size: var(--motif-taille, 70%);
  inline-size: auto;
  inset-block-end: 0;
  inset-inline-end: 0;
}

/* ---------------------------------------------------------------
   TROIS RÉGLAGES PAR PILIER, et c'est assumé — le commentaire ci-dessus disait
   « jamais une exception par brique », il faut lire pourquoi celle-ci en est une.

   La règle générale visait à ne pas compenser à la main un écart qu'une cote
   commune pouvait corriger. Ici elle ne peut pas : les demandes vont en SENS
   CONTRAIRE. Hugo, le 25/08/2026 : « sur dictionnaire de données, le motif doit
   pas déborder sur le texte » — donc plus petit ; « sur plateforme et connecteur
   IA, le motif devrait dépasser un peu plus de l'image » — donc plus grand.

   La cause est réelle et pas un caprice : depuis que les viewBox serrent l'encre,
   chaque dessin a SON rapport. À hauteur égale, la matrice (1,62) est nettement
   plus large que les barres (1,42), donc elle avance beaucoup plus loin vers la
   colonne de texte. Une seule valeur ne peut pas servir les deux.

   ⚠️ CE QUE ÇA COÛTE, À SURVEILLER. Le motif est ancré au coin extérieur, donc
   grossir le pousse VERS LA COLONNE DE TEXTE. Du blanc sur sa série la plus claire
   ne donne que ~1,9 : à 80 %, plateforme et connecteur entrent d'une centaine de
   pixels sous leur colonne de texte. C'est le prix de « dépasser un peu plus », et
   c'est ce qui a été demandé — mais si le corps de texte s'en trouve gêné, la
   réponse est de baisser ces deux valeurs, pas d'ajouter un plafond de largeur :
   un plafond de largeur laisserait un blanc dans le coin, ce qui est précisément
   le défaut corrigé juste avant. */
.texte-panneau--dictionnaire .texte-panneau__boite {
  /* Le dessin le plus large de la page. À 70 % son arête intérieure mordait la
     fin des lignes de texte ; à 58 % elle s'arrête bien avant. */
  --motif-taille: 58%;
}

.texte-panneau--plateforme .texte-panneau__boite {
  --motif-taille: 80%;
}

.texte-panneau--connecteur .texte-panneau__boite {
  --motif-taille: 80%;
}

/* Il suit l'image, pas la boîte — comme le glyphe. Quand `disposition: inverse`
   renvoie le visuel à gauche, le motif passe au coin gauche avec lui. */
.texte-panneau--inverse .texte-panneau__motif {
  inset-inline-end: auto;
  inset-inline-start: 0;
}

/* ---------------------------------------------------------------
   LES TEINTES DU MOTIF.

   Hugo, le 25/08/2026 : « au lieu de prendre du fluo, prend la couleur de la
   shape de l'outil correspondant comme base ». La base est donc la TEINTE VIVE
   du pilier — celle que porte le glyphe — et non le fluo ; les deux séries qui
   reculent en découlent dans _components.scss, mélangées au fond de la boîte.

   Le fond est `--panneau-ton`, c'est-à-dire le fond réel de la boîte, et jamais
   le jeton du pilier écrit en clair : c'est ce qui fait vraiment reculer les
   séries secondaires. Le repli couvre `fond: sombre`, qui ne pose pas ce jeton.

   ⚠️ LA TEINTE VIVE NE PEUT PAS SERVIR TELLE QUELLE, et c'est tout le sujet.
   Chaque boîte porte la valeur SOMBRE de son pilier : la teinte vive posée
   dessus est une nuance de son propre fond. Contrastes mesurés —

     dictionnaire  #00b3a4 sur #00756c  2,12
     integrations  #f0a22e sur #ab4a00  2,67
     plateforme    #ff5d73 sur #bc4b64  1,63  ← à peine visible
     connecteur    #8b5cff sur le violet assombri  1,14  ← invisible

   La base est donc la teinte vive RELEVÉE VERS LE BLANC juste ce qu'il faut pour
   que les quatre pèsent pareil. La proportion diffère par pilier parce que leurs
   luminances diffèrent — même dispositif que `--btn-primary-bg`, qui assombrit
   deux piliers sur quatre pour la raison symétrique.

   ⚠️ LA CIBLE EST 2,80, ET C'EST UN PLAFOND, PAS UN GOÛT. C'est le plus haut
   niveau que les quatre peuvent atteindre : le fond du connecteur est le plus
   clair des quatre, et sa propre teinte ne monte pas au-delà de 2,82 même
   presque blanche. Viser plus haut demanderait de sortir de la teinte du pilier,
   ce que la demande exclut.

   Le motif est donc PLUS DISCRET que le donut, qui pèse 4,79 / 3,55 / 2,39 avec
   son fluo. C'est dans la nature d'un décor à la teinte de son propre fond, et
   ça ne se corrige pas sans changer de couleur.

   Séries obtenues, identiques d'un pilier à l'autre — 2,80 / 2,28 / 1,76. */
.texte-panneau--boite .texte-panneau__boite {
  --motif-fond: var(--panneau-ton, var(--c-blue-dark));
}

.texte-panneau--dictionnaire .texte-panneau__boite {
  --motif-base: color-mix(in srgb, var(--c-pilier-dictionnaire) 70%, var(--c-white));
}

.texte-panneau--plateforme .texte-panneau__boite {
  --motif-base: color-mix(in srgb, var(--c-pilier-plateforme) 49%, var(--c-white));
}

.texte-panneau--connecteur .texte-panneau__boite {
  --motif-base: color-mix(in srgb, var(--c-pilier-connecteur) 40%, var(--c-white));
}

.texte-panneau--integrations .texte-panneau__boite {
  --motif-base: color-mix(in srgb, var(--c-pilier-integrations) 94%, var(--c-white));
}

.texte-panneau--sombre .texte-panneau__boite {
  background-color: var(--c-blue-dark);
}

/* ---------- Les boîtes aux couleurs de pilier ----------
   Une boîte par brique produit. Le vert du dictionnaire est arrivé le
   18/08/2026, les trois autres le même jour avec la page produit2.

   ⚠️ LES QUATRE SONT SOMBRES DEPUIS LE 25/08/2026, et il n'en reste aucune de
   claire. Le connecteur y était depuis le 19/08, la plateforme et les intégrations
   depuis le 20 (« applique les couleurs sombres pour les boîtes Plateforme et
   Intégrations qu'on a décidées sur les pages propres »), le dictionnaire depuis la
   question d'Hugo du 25 : « on peut les faire passer dans une teinte plus sombre
   pour en faire des blocs sur lesquels on écrit en blanc ? » C'est la règle 10 bis
   de MAQUETTE.md, une bande que la maquette dit sombre reste sombre.

   ⚠️ CHACUNE PREND LA COULEUR DÉCIDÉE SUR SA PAGE DÉDIÉE, jamais une recalculée
   ici. C'est ce qui garantit qu'une brique produit a la même couleur sur la page
   produit et sur sa propre page :

     dictionnaire  --c-pilier-dictionnaire-sombre  #00756c  tranchée le 25/08/2026,
                                                            candidats sur le jeton
     plateforme    --c-pilier-plateforme-sombre    #bc4b64  choisie par Hugo pour
                                                            `onglets-metiers`, /plateforme/
     integrations  --c-pilier-integrations-sombre  #ab4a00  tranchée sur
                                                            /labo/orange-sombre/
     connecteur    le violet assombri de 10 %               ci-dessous, inchangé

   ⚠️ LE COMMENTAIRE QUI GARDAIT LE DICTIONNAIRE CLAIR EST LEVÉ, et il vaut de
   savoir ce qu'il disait : « aucune maquette ne dit sombre cette bande-là, et son
   vert porte l'encre du site à 5,40 — c'est “la maquette annonce-t-elle du sombre
   ici ?” qui tranche, pas la luminance de la teinte ». Le critère reste juste, mais
   il ne tranchait plus rien ici : la maquette de /dictionnaire/, elle, peint bien
   ses deux bandes en bleu nuit, et une brique produit ne peut pas être sombre sur
   sa page et claire sur la page produit — c'est la garantie du paragraphe
   précédent qui a emporté la décision, pas une mesure de luminance.

   ⚠️ `$piliers-clairs` ET SA BOUCLE ONT DISPARU AVEC, ainsi que la table
   `$tons-badge` qui ne servait plus qu'elle. Ne pas les rétablir à vide : une
   boucle qui ne parcourt rien se lit comme un dispositif encore en service. Le
   calcul qu'elles portaient — `--panneau-ton`, une version assombrie du pilier pour
   les éléments qui posent cette teinte sur du BLANC, le badge aujourd'hui — n'a
   plus d'objet : une fois la boîte sombre, sa couleur est DÉJÀ assez foncée pour
   servir d'encre sur la pilule blanche du badge. Les quatre piliers posent donc
   `--panneau-ton` égal à leur propre fond, comme le connecteur le faisait seul à
   l'origine. Pour mémoire, la dernière valeur de la table était `dictionnaire: 65%`
   — la plus GRANDE proportion de vert qui atteignait encore 4,5 sur blanc (4,87) ;
   #00756c y donne 5,58 sans rien mélanger.

   ⚠️ LES QUATRE RÈGLES SONT ÉCRITES À PART ET NON DANS UNE BOUCLE, parce que LEURS
   CORRECTIONS DE RÔLES DIFFÈRENT — c'est la règle 10 de MAQUETTE.md, et une boucle
   livrerait ici du texte illisible en silence.

   Les quatre ont en commun : `.on-dark` posé par le gabarit, le fond servant de
   référence aux boutons, et `--panneau-ton` égal au fond.

   ⚠️ CE QUI LES SÉPARE EST LE SORT DU FLUO, et c'est mesuré :

     dictionnaire #00756c blanc 5,58 ✓   casper 2,70 ✗   fluo 4,79 ✓
     plateforme   #bc4b64 blanc 4,85 ✓   casper 2,35 ✗   fluo 4,16 ✗
     integrations #ab4a00 blanc 5,66 ✓   casper 2,74 ✗   fluo 4,85 ✓
     connecteur   #724fd9 blanc 5,47 ✓   casper 2,65 ✗   fluo 4,70 ✓

   Le rose passe donc --text-accent au blanc. Le vert et l'orange GARDENT le fluo,
   qui y tient. Ne pas « harmoniser » les quatre : la différence est le seuil, pas
   le goût.

   ⚠️ LE VIOLET EST LE CAS À PART : son fluo TIENT (4,70) depuis qu'il est passé à
   80 %, et son --text-accent reste pourtant au blanc — c'est la conséquence
   disponible mais non prise décrite dans _tokens.scss, pas un oubli. */
.texte-panneau--dictionnaire .texte-panneau__boite {
  background-color: var(--c-pilier-dictionnaire-sombre);
  --section-bg: var(--c-pilier-dictionnaire-sombre);
  --bg-page: var(--c-pilier-dictionnaire-sombre);
  /* Seul le casper tombe (2,70). Le fluo tient à 4,79 et reste donc l'accent. */
  --text-muted: var(--c-white);
  /* 5,58 sur la pilule blanche du badge. Rien à assombrir de plus. */
  --panneau-ton: var(--c-pilier-dictionnaire-sombre);
  color: var(--text-strong);
}

/* ---------- Les boîtes sombres « plateforme » et « integrations » ----------
   Passées au sombre le 20/08/2026 sur consigne d'Hugo, avec les couleurs décidées
   sur leurs pages dédiées. La table des contrastes et la raison de les écrire à
   part sont dans la note du bloc précédent, qui couvre les quatre. */
.texte-panneau--plateforme .texte-panneau__boite {
  background-color: var(--c-pilier-plateforme-sombre);
  --section-bg: var(--c-pilier-plateforme-sombre);
  --bg-page: var(--c-pilier-plateforme-sombre);
  /* Les deux encres claires d'`.on-dark` tombent ici : le casper est illisible
     (2,35) et le fluo n'atteint pas le seuil du texte courant (4,16). Aucune
     demi-mesure ne marche sur le casper — un blanc voilé à 90 % plafonne à 4,24. */
  --text-muted: var(--c-white);
  --text-accent: var(--c-white);
  /* La teinte est déjà assez foncée pour servir d'encre sur la pilule blanche du
     badge : 4,85. Rien à assombrir de plus. */
  --panneau-ton: var(--c-pilier-plateforme-sombre);
  color: var(--text-strong);
}

.texte-panneau--integrations .texte-panneau__boite {
  background-color: var(--c-pilier-integrations-sombre);
  --section-bg: var(--c-pilier-integrations-sombre);
  --bg-page: var(--c-pilier-integrations-sombre);
  /* Seul le casper tombe (2,74). Le fluo tient à 4,85 et reste donc l'accent —
     c'est la seule des trois boîtes sombres de ce bloc dans ce cas. */
  --text-muted: var(--c-white);
  /* 5,66 sur la pilule blanche du badge. */
  --panneau-ton: var(--c-pilier-integrations-sombre);
  color: var(--text-strong);
}

/* ⚠️ MÊME NÉCESSITÉ DE CASCADE QUE DANS _texte-module.scss : `.item--<pilier>`
   écrit --item-ton SUR l'atome, et une propriété personnalisée posée sur un élément
   bat la même héritée d'un ancêtre. Une remise à blanc déclarée sur la boîte
   n'atteindrait aucun item. Ce bloc n'en porte pas aujourd'hui — la règle est là
   pour que le premier ajouté ne sorte pas en teinte de pilier sur teinte de
   pilier, ce qui ne se signalerait pas. */
.texte-panneau--dictionnaire .texte-panneau__boite .item,
.texte-panneau--plateforme .texte-panneau__boite .item,
.texte-panneau--integrations .texte-panneau__boite .item {
  --item-ton: var(--c-white);
  --item-encre: var(--c-white);
}

/* ---------- La boîte « connecteur » ----------
   ⚠️ LE SEUL PILIER QUI BASCULE EN CONTEXTE SOMBRE, et le seul qu'il a fallu
   retoucher. Son violet #8b5cff est bancal des deux côtés : trop sombre pour du
   texte foncé (3,43), trop clair pour du blanc (4,14 — il manque 0,36 au seuil
   de 4,5 du texte courant). Aucune encre ne passait dessus.

   Assombri vers le zodiac (le jeton --c-pilier-connecteur-sombre, #724fd9), il
   porte le blanc à 5,47. Le template lui pose donc `.on-dark`, comme au bleu nuit.

   Les deux rôles corrigés ci-dessous le sont pour la raison inverse des piliers
   clairs : sous `.on-dark`, --text-muted vaut casper (2,65 ici, illisible) et
   --text-accent vaut fluo — qui TIENT désormais (4,70) et reste au blanc par
   décision, non par nécessité. Le corps de texte, lui, passe par --text-strong et
   ne les touche pas — voir la règle dans _tokens.scss. */
.texte-panneau--connecteur .texte-panneau__boite {
  /* ⚠️ LE VIOLET SOMBRE EST UN JETON DEPUIS LE 26/08/2026, et le calcul comme son
     arbitrage (80 % et non 90 %, l'alignement des quatre boîtes sur un même poids,
     le fait qu'il NE SUIVE PAS l'aplat de bouton de `.ton-page--connecteur`) sont
     dans _tokens.scss, en un seul endroit. Cette boîte était la seule des quatre à
     porter la bonne valeur ; les trois autres l'ont rattrapée ce jour-là.

     L'indirection par `--violet-connecteur` est conservée : les quatre déclarations
     ci-dessous le lisent, et le nommer une fois évite quatre appels au jeton. */
  --violet-connecteur: var(--c-pilier-connecteur-sombre);
  background-color: var(--violet-connecteur);
  --section-bg: var(--violet-connecteur);
  --bg-page: var(--violet-connecteur);
  --text-muted: var(--c-white);
  --text-accent: var(--c-white);
  /* Le seul pilier dont la teinte de badge est DÉJÀ celle de la boîte : le
     violet assombri donne 5,47 sur blanc, il n'a rien à assombrir de plus. */
  --panneau-ton: var(--violet-connecteur);
  color: var(--text-strong);
}

/* En dessous de 96rem, `margin-inline: auto` ne ménage plus rien : la boîte
   toucherait les deux bords et cesserait d'en être une. */
@media (max-width: 96rem) {
  .texte-panneau--boite .texte-panneau__boite {
    margin-inline: var(--container-pad);
  }
}
/* =========================================================================
   Boîtes qui se suivent : elles se rapprochent.

   Chaque section porte `--sp-section` en haut ET en bas, donc deux boîtes
   consécutives s'écartaient de deux respirations de section — jusqu'à 240px.
   C'est juste entre deux sections qui traitent de sujets différents ; c'est
   beaucoup entre trois boîtes qui déclinent un même thème, et que le lecteur
   doit lire comme un groupe.

   Les deux côtés sont réduits, sinon la moitié du trou subsiste. D'où le `:has`,
   qui permet de rogner le BAS d'une boîte selon ce qui la suit — impossible
   autrement en CSS. Il est déjà dans la même famille de fonctions que le
   `color-mix` employé plus haut, donc rien de neuf côté support.

   L'intertitre entre dans le même groupe : il annonce les boîtes qui le suivent,
   il doit leur être collé et non flotter à mi-chemin.
   ========================================================================= */
.texte-panneau {
  --texte-panneau-groupe: clamp(var(--sp-6), 2.4vw, var(--sp-10));
  /* La jonction vers un intertitre respire un peu plus que celles à l'intérieur
     du groupe, et c'est voulu : là on quitte un sujet pour en ouvrir un autre,
     alors qu'entre deux boîtes on reste dans le même. */
  --texte-panneau-groupe-titre: clamp(var(--sp-8), 3vw, var(--sp-12));
}

.texte-panneau--boite + .texte-panneau--boite,
.intertitre + .texte-panneau--boite {
  padding-block-start: var(--texte-panneau-groupe);
}

.texte-panneau--boite:has(+ .texte-panneau--boite) {
  padding-block-end: var(--texte-panneau-groupe);
}

.texte-panneau--boite:has(+ .intertitre) {
  padding-block-end: var(--texte-panneau-groupe-titre);
}

/* ⚠️ LE RESSERREMENT DE GROUPE DOIT CONNAÎTRE LE DÉBORD DU GLYPHE.

   Les trois règles ci-dessus ramènent l'intervalle entre deux boîtes à deux fois
   `--texte-panneau-groupe`, soit 80px sur grand écran. Le glyphe de pilier monte
   77px au-dessus de sa boîte : il ne restait donc que 3px entre le bas d'une
   boîte et le haut de la forme suivante — les deux se touchaient presque.
   Mesuré sur /produit/ le 25/08/2026, aux quatre largeurs du banc.

   Ce n'est pas le resserrement qui est faux : il est voulu, trois briques d'un
   même thème doivent se lire comme un groupe. C'est qu'il ignorait une hauteur
   qui n'existait pas quand il a été écrit. On lui ajoute donc EXACTEMENT le
   débord, ni plus : l'espace libre au-dessus de la forme redevient les 80px du
   groupe, et le rythme de la page ne bouge pas.

   ⚠️ CES SÉLECTEURS DOIVENT RESTER APRÈS CEUX DU GROUPE. Ils ont la même
   spécificité (`:has()` prend celle de son argument le plus spécifique), donc
   seul l'ordre du source les départage — remontés plus haut, ils seraient
   annulés sans la moindre alerte, et on retomberait sur les 3px. C'est
   exactement ce qui est arrivé à la règle générale posée en tête de fichier. */
.texte-panneau--boite + .texte-panneau--boite:has(.texte-panneau__glyphe),
.intertitre + .texte-panneau--boite:has(.texte-panneau__glyphe) {
  padding-block-start: calc(var(--texte-panneau-groupe) + var(--panneau-glyphe-debord));
}

.texte-panneau--boite + .intertitre {
  padding-block-start: var(--texte-panneau-groupe-titre);
}

/* La maquette pose le texte à GAUCHE et le visuel à droite, l'inverse de
   `texte-illustre`. C'est voulu : les deux blocs se suivent parfois, et
   alterner évite deux colonnes de texte alignées l'une sous l'autre. */
.texte-panneau__inner {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--texte-panneau-gouttiere);
  align-items: center;
}

/* ---------- L'alternance ----------
   `disposition: inverse` renvoie le texte à droite et le panneau à gauche.
   Posé le 25/08/2026 sur la page produit, dont les quatre briques se suivent :
   sans alternance, ce sont quatre colonnes de texte alignées les unes sous les
   autres, et la page se lit comme une seule liste.

   C'est un champ par instance et non un calcul de rang : le bloc ne connaît pas
   sa place dans la page — les quatre briques y sont séparées par d'autres blocs,
   donc l'index de boucle de partials/blocs.njk n'alterne pas ce qu'on veut voir
   alterner. La page déclare ce qu'elle veut.

   ⚠️ BORNÉ À `lg`, ET IL LE DOIT. En dessous, la grille passe à une colonne et
   les deux enfants s'empilent : `order` ferait alors passer le panneau AVANT le
   texte, si bien qu'une brique sur deux s'ouvrirait sur son image sans titre
   au-dessus. L'ordre du DOM est le bon ordre de lecture, on ne le contredit que
   là où les deux colonnes existent vraiment.

   `order` plutôt qu'un placement explicite en `grid-column` : il n'y a que deux
   enfants, une seule déclaration suffit donc, et rien à tenir en accord avec le
   nombre de colonnes. Sans conséquence pour la navigation au clavier — le seul
   élément focusable de la section (le CTA) est dans la colonne de texte, le
   panneau n'en contient aucun. */
@media (min-width: 60rem) {
  .texte-panneau--inverse .texte-panneau__texte {
    order: 2;
  }
}
/* ---------- Colonne de texte ---------- */
.texte-panneau__texte {
  display: flex;
  flex-direction: column;
  gap: var(--sp-6);
}

/* Le titre et son badge partagent une ligne, et le badge est CENTRÉ dessus.

   `baseline` a été essayé d'abord, par analogie avec `.item__entete` : il posait
   la ligne de base du badge (14px) sur celle du titre (jusqu'à 48px), ce qui
   plaquait la pastille en bas de la hauteur de capitale. Un badge n'est pas un
   mot de la phrase, il n'a pas à partager sa ligne de base.

   Le centrage est juste ici parce que le titre est en `--lh-tight` (1,08) : son
   demi-interligne ne fait que 2px à 48px, donc le centre de la boîte de ligne
   coïncide presque avec celui des capitales. C'est ce qui rendait le même
   centrage faux sur les marqueurs d'item, réglés eux en `--lh-snug` (1,35).

   ⚠️ VALABLE TANT QUE LE TITRE BADGÉ TIENT SUR UNE LIGNE. Sur un titre replié —
   la mesure est de 20ch — `center` centrerait le badge sur le bloc entier, à
   mi-hauteur du pavé de titre. Le seul badge du site est aujourd'hui sur « Le
   connecteur IA », seize caractères. */
.texte-panneau__entete {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
}

/* Aplat blanc, libellé à la teinte assombrie de la boîte.

   ⚠️ PAS DE `margin-inline-start: auto` — il y en avait un, qui repoussait le
   badge au bord droit de la boîte. Retiré le 18/08/2026 : à cette distance il
   qualifiait la boîte entière plutôt que l'intitulé. Il est maintenant accolé au
   titre, à une gouttière près.

   `flex: none` pour qu'un titre long ne le comprime pas, et `nowrap` pour qu'il
   ne se replie jamais : « New » sur deux lignes ne veut plus rien dire.

   14px et non 11px : à la taille des eyebrows il se lisait comme une mention
   technique, pas comme une annonce. Il reste sous le seuil du grand texte, donc la
   teinte doit toujours tenir 4,5 — c'est le cas des quatre `--panneau-ton` : 5,58
   pour le vert, 5,47 pour le violet, 5,66 pour l'orange, et 4,85 pour le rose, qui
   est désormais le plus juste des quatre. */
.texte-panneau__badge {
  flex: none;
  padding: 0.25rem var(--sp-3);
  border-radius: var(--radius-full);
  background-color: var(--c-white);
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--panneau-ton, var(--c-blue-action));
}

/* Gabarit de titre du site — repris de `.panneau__titre` (sections/_sequence.scss). */
.texte-panneau__titre {
  margin: 0;
  max-width: 20ch;
  color: var(--text-strong);
  text-wrap: balance;
}

/* --text-strong et non --text-muted : règle du corps de texte, _tokens.scss.
   C'est ce qui rend le corps juste sur les trois fonds du bloc à la fois —
   crème, bleu nuit et vert dictionnaire. */
.texte-panneau__corps {
  display: flex;
  flex-direction: column;
  gap: var(--sp-5);
  max-width: 38rem;
  font-size: var(--fs-base);
  line-height: var(--lh-normal);
  color: var(--text-strong);
}

.texte-panneau__corps p {
  margin: 0;
}

/* ---------- Le CTA ----------
   Sous le texte depuis le 18/08/2026, et non plus dans le pied du panneau de
   droite. Il n'a donc plus ni filet ni gouttières propres : c'est la colonne de
   texte qui l'espace, par son `gap`.

   Le `display: flex` n'est pas décoratif — sans lui, la colonne de texte étant
   elle-même en flex-column, le bouton s'étirerait sur toute la largeur. */
/* Les CTA s'empilent plutôt que de s'aligner — demandé le 18/08/2026. Deux
   boutons côte à côte sous un paragraphe se lisaient comme un choix ; l'un sous
   l'autre, ils se lisent comme deux portes.

   `align-items: flex-start` n'est pas décoratif : sans lui, la colonne étirerait
   chaque bouton sur toute la largeur de la boîte. */
.texte-panneau__pied {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--sp-3);
}

/* ---------- Le panneau ----------
   ⚠️ PLUS DE CADRE, et c'est une décision, pas un oubli — tranché le
   18/08/2026. Le panneau a été successivement : trois bandes à filets, puis un
   cadre blanc à coins carrés (la règle du panneau fermé du site, .coeur__panneau).
   Il n'est plus rien de tout ça. Ce qui l'a vidé :

     la barre d'étiquettes n'est jamais rendue — annotations de dessin
       technique, voir le template ;
     le CTA a rejoint la colonne de texte ;
     le cadre blanc doublait celui de `.apercu`, qui porte déjà son dégradé,
       son filet et ses coins arrondis. Deux cadres concentriques autour du
       même placeholder.

   Il ne reste donc que la colonne, et `__visuel` n'est plus qu'un point
   d'accroche pour le visuel définitif. Ne pas rétablir le fond blanc ici : sur
   la boîte verte, il redessinerait exactement le cadre qu'on a retiré. */
.texte-panneau__barre {
  display: flex;
  justify-content: space-between;
  gap: var(--sp-4);
  padding: var(--sp-4) var(--sp-6);
  border-bottom: var(--filet-panneau);
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--text-muted);
}

/* `__visuel` n'a plus de gouttières : elles ménageaient le cadre blanc du
   panneau autour du placeholder, et ce cadre a été retiré. */
/* Le ratio vient de la maquette (593 × 205 pour la zone visuelle) et non
   d'une hauteur fixe : la colonne de gauche décide de la hauteur du bloc, le
   visuel s'y adapte. */
.texte-panneau__placeholder {
  aspect-ratio: 593/340;
}

/* L'illustration définitive, quand la page en déclare une. Aucun cadre ni filet
   à lui ajouter : les quatre illustrations sont détourées, elles se posent
   directement sur l'aplat teinté de la boîte.

   ---------------------------------------------------------------------------
   LE PLAFOND DE HAUTEUR, posé le 25/08/2026.

   Les quatre exports n'ont pas le même rapport largeur/hauteur, et donner toute
   la colonne à chacun les rendait de hauteurs très inégales : à largeur égale,
   la plateforme se rendait 49 % plus haute que le dictionnaire, les intégrations
   21 %, le connecteur 18 %. Quatre panneaux qui se suivent, quatre hauteurs
   d'image différentes.

   C'est la HAUTEUR qu'on plafonne, et non la largeur qu'on réduit image par
   image : la hauteur est justement ce qui variait, une seule valeur suffit donc
   là où il en aurait fallu trois — et un réexport à une autre taille est pris en
   compte tout seul, tant que le cadrage ne change pas.

   La valeur est la hauteur rendue du dictionnaire à pleine largeur (600px de
   colonne × 520/677 ≈ 461px), arrondie juste au-dessus : c'est le seul des
   quatre panneaux dont la taille convenait, il ne bouge donc pas et les trois
   autres redescendent sur lui. Elle ne joue qu'en grand écran — en dessous, la
   colonne est plus étroite que le plafond et aucune image n'est trop haute.

   ---------------------------------------------------------------------------
   ⚠️ `object-fit: contain` EST CE QUI TIENT LE RAPPORT, ET IL EST OBLIGATOIRE.

   Un plafond de hauteur ne rétrécit PAS la largeur. On pourrait le croire — la
   table des violations de contrainte de CSS 2.1 §10.4 dit que la largeur se
   recalcule en `max-height × w/h` — mais cette règle suppose une largeur `auto`,
   et `max-width: 100%` (règle générale de _base.scss) l'a déjà rendue définie :
   elle vaut la colonne. `max-block-size` rabat alors la seule hauteur, la BOÎTE
   devient disproportionnée, et `object-fit` — dont la valeur par défaut est
   `fill` — y étire l'image.

   Mesuré dans Chrome sur /produit/ le 25/08/2026 : la plateforme sortait à
   +48 % de rapport, les intégrations à +20 %, le connecteur à +18 %.

   ⚠️ RETIRER `width: 100%` NE SUFFIT PAS, c'est le deuxième essai qui a échoué :
   sans lui, `max-width: 100%` fixe la largeur à la colonne exactement pareil
   (les cinq exports sont plus larges qu'elle), et l'écart mesuré est identique.
   Ne pas refaire ce chemin — la seule chose qui garantisse le rapport ici, c'est
   `object-fit`.

   Avec `contain`, la boîte garde la largeur de la colonne mais l'image y est
   peinte proportionnellement et centrée (`object-position` vaut 50 % 50 % par
   défaut, il n'y a donc pas de `margin-inline: auto` à ajouter — il ne servirait
   à rien, la boîte fait déjà toute la largeur). Rendu mesuré, écart 0,0 % sur
   les cinq : plateforme 405×464, connecteur 510×464, intégrations 500×464,
   dictionnaire 600×461 (sous le plafond, inchangé).

   ⚠️ CONSÉQUENCE À CONNAÎTRE : la boîte est plus large que l'image peinte pour
   les trois qui touchent le plafond. Sans conséquence aujourd'hui — l'`img` n'a
   ni fond, ni filet, ni ombre. Le jour où on lui en donne un, il se dessinerait
   sur toute la largeur de la colonne et non autour de l'image ; il faudrait
   alors porter le plafond sur un conteneur plutôt que sur l'image. */
.texte-panneau__illustration {
  display: block;
  width: 100%;
  height: auto;
  max-block-size: 29rem;
  object-fit: contain;
}

.texte-panneau__lien {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  color: var(--text-accent);
  text-decoration: none;
}

.texte-panneau__lien:hover {
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.25em;
}

/* =========================================================================
   Une seule colonne.

   Le panneau passe sous le texte. Il garde son cadre et ses trois bandes :
   c'est lui qui porte l'identité de la section, le réduire à un cadre gris
   la viderait de sa substance sur mobile.
   ========================================================================= */
@media (max-width: 60rem) {
  .texte-panneau__inner {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--sp-10);
  }
  .texte-panneau__titre {
    max-width: none;
  }
}
/* =========================================================================
   LE RENDU MOBILE DES BOÎTES OUTILS — demandé par Hugo le 27/08/2026 sur
   /produit/ : « le badge toujours en haut à droite de la boîte », « les graph
   halftone collés au bas des box et prenant toute la largeur, ils ne doivent
   pas atteindre les textes », et « dans la boîte des intégrations, les deux
   boutons doivent faire la même taille ».

   ⚠️ TOUT EST DANS CE `mq-down`, ET RIEN NE DOIT EN SORTIR. Le rendu desktop
   est une référence validée : une valeur de base « ajustée » au passage
   changerait les deux côtés. Les quatre règles ci-dessous n'existent qu'en
   dessous de `lg`, c'est-à-dire là où la grille est déjà passée à une colonne.
   ========================================================================= */
@media (max-width: 60rem) {
  /* ---------- ① LE GLYPHE DE PILIER TOUJOURS AU COIN HAUT DROIT ----------
     Hugo, le 27/08/2026 : « tous les badges produits ne sont pas en haut à
     droite (plateforme et intégration) ». Le « badge produit », c'est cette
     forme-là — celle du pilier, pas la pastille « New ».

     En desktop le glyphe SUIT L'IMAGE : `disposition: inverse` le renvoie au
     coin gauche, ce qui fait alterner les quatre briques au lieu de le planter
     du même côté. Sur une colonne, cette alternance n'a plus d'objet — il n'y a
     plus de côté image — et deux briques sur quatre ouvraient donc à gauche
     quand les deux autres ouvraient à droite, sans rien pour le justifier.

     ⚠️ DEUX CLASSES ET PLACÉ APRÈS LA RÈGLE `--inverse` DU HAUT DE FICHIER,
     obligatoirement : les deux sélecteurs ont la même spécificité, donc seul
     l'ordre du source les départage. Remontée plus haut, cette règle serait
     annulée sans la moindre alerte. Même piège que le resserrement de groupe.

     Le motif, lui, n'a rien à suivre : il prend désormais toute la largeur (voir
     ③), donc son ancrage latéral n'a plus de sens à discuter. */
  .texte-panneau--inverse .texte-panneau__glyphe {
    inset-inline-start: auto;
    inset-inline-end: calc(var(--panneau-glyphe-taille) * -0.3);
  }
  /* ---------- ② La pastille « New » au coin, elle aussi ----------
     Il quitte la ligne du titre pour se ferrer au bord de la boîte. Il reste
     EN FLUX, poussé par une marge automatique, et non posé en absolu : son
     bloc conteneur serait `__inner` (le `.container` de la boîte, positionné
     pour passer au-dessus du motif) et non la boîte, si bien que l'écart
     vertical au coin devrait se calculer depuis le padding de la boîte — une
     cote de plus à tenir en accord, pour un objet que le flux place déjà bien.

     `flex-start` est le complément indispensable de la marge : l'entête est en
     `align-items: center`, ce qui centrerait le badge sur la HAUTEUR DU PAVÉ de
     titre dès qu'il se replie — l'avertissement écrit plus haut sur ce
     centrage. En haut de la première ligne, il est au coin quel que soit le
     nombre de lignes du titre. */
  .texte-panneau__entete {
    align-items: flex-start;
  }
  .texte-panneau__badge {
    margin-inline-start: auto;
  }
  /* ---------- ③ Le motif sur toute la largeur, collé en bas ----------
     Sur une colonne, l'ancrage au coin de l'image n'a plus de sens : il n'y a
     plus de colonne de texte EN REGARD à protéger, le texte est AU-DESSUS.
     C'est donc la largeur qui commande et la hauteur qui en découle.

     ⚠️ C'EST CE QUI CORRIGE LE DÉFAUT SIGNALÉ, et la cause n'était pas la
     largeur mais le RAPPORT. Le motif n'a pas de `preserveAspectRatio`, donc
     son dessin est cadré en `meet` : à hauteur imposée (58 à 80 % de la boîte,
     soit 458 à 699 px sur mobile) et largeur bridée par `max-width: 100%`
     (règle générale de _base.scss), l'encre était mise à l'échelle sur la
     LARGEUR et centrée verticalement dans une boîte deux fois trop haute.
     Mesuré à 390 px sur /produit/ : ~125 px de vide sous l'encre, et le dessin
     remontait à mi-boîte, par-dessus le corps de texte et le CTA.

     Donner la largeur et laisser la hauteur suivre le rapport intrinsèque du
     viewBox — l'inverse exact de la règle desktop, et le même raisonnement :
     on ne pose jamais d'`aspect-ratio` en dur, on n'en donne qu'UN côté.

     ⚠️ CONSÉQUENCE : les trois `--motif-taille` par pilier deviennent inertes
     ici, puisqu'ils règlent la hauteur. Ils restent nécessaires en desktop, où
     ils arbitrent deux demandes contraires ; ne pas les retirer.

     Les insets sont écrits UN PAR AXE et jamais les deux : avec une largeur
     définie, contraindre les deux arêtes rendrait la déclaration
     surcontrainte et l'une des deux serait ignorée en silence.

     ⚠️ SÉLECTEUR À DEUX CLASSES ET PLACÉ APRÈS `--inverse`, VOLONTAIREMENT.
     `.texte-panneau--inverse .texte-panneau__motif` renvoie le motif au coin
     gauche ; à une seule classe cette règle-ci perdrait sur les deux briques
     inversées, et le motif y resterait cadré au coin. Même piège que le
     resserrement de groupe plus haut. */
  .texte-panneau--boite .texte-panneau__motif {
    inline-size: 100%;
    block-size: auto;
    inset-inline-start: 0;
    inset-inline-end: auto;
    /* ⚠️ LE PLAFOND EST CE QUI TIENT LA TROISIÈME CLAUSE — « ils ne doivent pas
       atteindre les textes » — ET IL NE MORD QUE HAUT DANS LA PLAGE MOBILE.

       La hauteur découlant de la largeur, elle grandit avec la fenêtre quand la
       hauteur de la boîte, elle, DIMINUE (le texte se replie sur moins de
       lignes). Mesuré sur /produit/ : à 390 px le dessin occupe 26 à 30 % de la
       hauteur de boîte, à 940 px il en occupe 63 à 73 % — et le texte n'en
       laisse libre que 53 à 58 %. Sans plafond, les quatre motifs remontaient
       sous le CTA et le dernier paragraphe entre ~700 et 960 px.

       48 % est la part libre mesurée au plus défavorable (53 %, les intégrations
       à 940 px) avec une marge : il reste 47 px entre la cime du dessin et le
       bas de la colonne de texte aux deux largeurs hautes. C'est une cote à
       REMESURER si les paragraphes d'une brique s'allongent — c'est le texte qui
       décide de la part libre, pas le dessin.

       ⚠️ IL NE PROVOQUE PAS LE DÉFAUT QU'ON VIENT DE CORRIGER, et c'est le point
       subtil : quand il mord, c'est la HAUTEUR qui devient contraignante, donc
       le cadrage `meet` met l'encre à l'échelle sur la hauteur — elle remplit
       exactement la hauteur (donc reste collée à l'arête basse) et se centre
       horizontalement. On perd de la largeur, jamais le contact avec le bas. Le
       défaut d'origine était l'inverse : une hauteur IMPOSÉE plus grande que le
       rapport, qui laissait l'encre flotter au milieu. */
    max-block-size: 48%;
  }
  /* ---------- ④ L'IMAGE REPREND TOUTE LA LARGEUR DE SA COLONNE ----------
     Hugo, le 27/08/2026 : « les images de connecteur et plateforme ne sont pas
     centrées », puis « c'est pas centré du tout ».

     ⚠️ ELLES L'ÉTAIENT, AU PIXEL — le défaut n'était pas le centrage mais la
     TAILLE. Le plafond `max-block-size: 29rem` égalise les quatre hauteurs
     d'image sur le desktop (les quatre exports n'ont pas le même rapport) ; dès
     que la colonne dépasse 29rem × rapport, il rabat la hauteur, et comme
     `object-fit: contain` tient le rapport, l'encre devient PLUS ÉTROITE que sa
     colonne et se centre dans le vide. Mesuré sur /produit/, part de la largeur
     de boîte occupée par le dessin :

                       390 px   480 px   600 px   760 px   955 px
       plateforme      100 %     91 %     71 %     55 %     43 %
       connecteur      100 %    100 %     89 %     69 %     54 %

     C'est exactement les deux briques nommées : ce sont celles dont le plafond
     mord le plus tôt (la plateforme est le seul export en portrait, 0,87, donc
     la plus haute à largeur égale ; le connecteur suit à 1,10).

     Le plafond n'a plus de raison d'être sur une colonne : ce qu'il égalise, ce
     sont quatre images qui se lisent EN REGARD d'une colonne de texte, or ici
     elles sont à des centaines de pixels les unes des autres et rien ne les
     compare. Chaque image reprend donc sa hauteur naturelle et remplit sa
     colonne.

     ⚠️ CE QUI RESTE, ET QUI N'EST PAS DU CSS : deux exports portent une marge
     TRANSPARENTE à droite — connecteur-ia.webp 60 px sur 1046 (5,7 %),
     integrations.webp 120 px sur 1237 (9,7 %) — donc leur dessin reste optiquement
     déporté vers la gauche d'une demi-marge, soit 8 et 15 px à 390 px. Seul un
     recadrage du FICHIER corrige ça, et il grossirait l'encre sur le desktop
     aussi. dictionnaire.webp (26/26) et plateforme.webp (0/0) sont équilibrés. */
  .texte-panneau__illustration {
    max-block-size: none;
  }
  /* ---------- ④ Deux CTA de même taille ----------
     Ils s'étirent sur la largeur de la colonne, donc ils font la même taille
     sans qu'aucune largeur ne soit écrite : `.btn` centre déjà son libellé.

     ⚠️ CONDITIONNÉ À LA PRÉSENCE DE DEUX BOUTONS, et non réservé au pilier
     `integrations`. Ce qui justifie l'étirement, c'est que deux portes côte à
     côte de largeurs différentes se lisent comme une hiérarchie ; une boîte à
     CTA unique n'a rien à égaliser et garde son bouton à la taille de son
     libellé. Écrite sur le pilier, la règle aurait nommé la page au lieu de
     nommer la condition.

     C'est bien `align-items` de la colonne qu'on renverse : le
     `align-items: flex-start` de `__pied` est ce qui empêche l'étirement, et
     il est là pour le cas du bouton unique. */
  .texte-panneau__pied:has(.btn + .btn) {
    align-items: stretch;
  }
}
/* =========================================================================
   BLOC « rangees-illustrees » — blocs/rangees-illustrees.njk.
   Origine : « Vos outils au quotidien », Figma 185:6920.

   Un en-tête, puis des rangées encadrées qui alternent visuel et texte.

   La partie textuelle est l'atome `.item` de _components.scss, partagé avec
   `liste-glyphes` et `etapes`. Ce fichier ne pose que la DISPOSITION — le
   cadre, la barre d'étiquettes, les deux moitiés et leur inversion. Rien ici
   ne touche à l'intitulé ni au corps, sinon les trois blocs divergent.

   La teinte de pilier arrive par la classe `.item--<ton>` posée sur la
   rangée : elle définit --item-ton, que l'accroche et les liens lisent. Les
   jetons --c-pilier-* existaient déjà dans _tokens.scss et correspondent au
   pixel près aux couleurs de la maquette (vérifié : #f0a22e pour les
   intégrations).
   ========================================================================= */
.rangees-illustrees {
  position: relative;
  background-color: var(--bg-page);
  padding-block: var(--sp-section);
}

/* ---------- En-tête ----------
   Le cadre, sa bande de titre et son filet viennent de la primitive `.cadre`
   (_components.scss). Ce fichier n'ajoute que le chapeau, qui lui est propre. */
.rangees-illustrees__chapeau {
  margin: var(--sp-4) 0 0;
  max-width: 42rem;
  font-size: var(--fs-base);
  line-height: var(--lh-normal);
  color: var(--text-strong);
}

/* ---------- Les rangées ----------
   Un seul cadre pour toute la pile : les rangées partagent leurs filets au
   lieu d'en avoir chacune quatre. C'est la grammaire des panneaux du site —
   voir « Au cœur de Biron », qui empile ses blocs de la même façon. */
/* Aucun contour ici : c'est `.cadre` qui borde l'ensemble. Les rangées ne
   posent que le filet qui les sépare l'une de l'autre. */
.rangees-illustrees__liste {
  border-top: var(--filet-panneau);
}

.rangee + .rangee {
  border-top: var(--filet-panneau);
}

/* Le cartouche « New » n'est plus ici : il appartient à l'atome, où il se ferre
   sur la ligne de l'intitulé, dans la colonne de texte. Voir `.item__badge`
   dans _components.scss. */
/* ---------- Rythme de la colonne de texte ----------
   L'atome pose une gouttière UNIFORME de --sp-3 entre ses quatre parties. Ici
   l'intitulé et son accroche doivent rester soudés — ils se lisent comme un
   seul bloc — tandis que le corps et le CTA ont besoin de respirer. On ajoute
   donc une marge aux deux frontières concernées, qui s'additionne à la
   gouttière ; le `calc` dit la distance visée plutôt que le complément :

     intitulé → accroche   --sp-3   0,75rem   inchangé, un seul bloc
     accroche → corps      --sp-6   1,5rem
     corps    → CTA        --sp-8   2rem

   C'est au bloc hôte de le faire et non à l'atome : `partials/item.njk` ne
   décide pas du rythme, et « Nos 4 garanties » emploie le même atome sans
   accroche ni CTA — l'y toucher aurait écarté son intitulé de son corps. */
.rangee .item__corps {
  margin-block-start: calc(var(--sp-6) - var(--sp-3));
}

.rangee .item__liens {
  margin-block-start: calc(var(--sp-8) - var(--sp-3));
}

.rangee__corps {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  align-items: center;
}

/* L'inversion se fait par l'ORDRE et non par un échange de colonnes : le
   visuel reste le premier enfant dans le HTML, donc l'ordre de lecture à la
   voix et au clavier ne change pas d'une rangée à l'autre. */
.rangee--inverse .rangee__visuel {
  order: 2;
}

.rangee__visuel {
  padding: var(--sp-8);
}

.rangee__placeholder {
  aspect-ratio: 638/198;
}

/* La colonne de texte respire davantage en haut et en bas que sur les côtés :
   32px suffisaient tant que le CTA était un lien nu, mais un bouton posé au ras
   du filet du bas paraît coincé. Le `calc` dit l'intention — la gouttière du
   bloc, plus 20px — plutôt qu'une valeur opaque. Les côtés restent à --sp-8
   pour garder l'alignement avec le visuel d'en face. */
.rangee__texte {
  padding: calc(var(--sp-8) + var(--sp-5)) var(--sp-8);
}

/* Le filet qui sépare les deux moitiés. Posé sur le texte plutôt que sur la
   grille pour qu'il suive l'inversion sans qu'on ait à le redéclarer. */
.rangee__texte {
  border-inline-start: var(--filet-panneau);
}

.rangee--inverse .rangee__texte {
  border-inline-start: 0;
  border-inline-end: var(--filet-panneau);
}

/* =========================================================================
   Une seule colonne.

   Le visuel repasse systématiquement AU-DESSUS du texte, quelle que soit la
   rangée : l'alternance n'a plus de sens sur une colonne, et un visuel qui
   passerait sous le texte une fois sur deux se lirait comme une erreur.
   ========================================================================= */
@media (max-width: 60rem) {
  .rangee__corps {
    grid-template-columns: minmax(0, 1fr);
  }
  .rangee--inverse .rangee__visuel {
    order: 0;
  }
  .rangee__visuel,
  .rangee__texte {
    padding: var(--sp-6);
  }
  .rangee__texte,
  .rangee--inverse .rangee__texte {
    border-inline: 0;
    border-top: var(--filet-panneau);
  }
}
/* =========================================================================
   BLOC « liste-glyphes » — blocs/liste-glyphes.njk.
   Origine : « Nos 4 garanties qui s'appliquent en continu », Figma 185:7105.

   Des entrées développées qui se répondent en quinconce, séparées par des
   filets pleine largeur.

   L'entrée elle-même est l'atome `.item` de _components.scss, partagé avec
   `rangees-illustrees` et `etapes`. Ce fichier ne décide que de la
   DISPOSITION : où l'entrée se pose dans la largeur, et ce qui la sépare de
   la suivante. Rien ici ne doit toucher au marqueur, à l'intitulé ni au corps
   — sinon les trois blocs cousins se mettent à diverger.
   ========================================================================= */
.liste-glyphes {
  position: relative;
  background-color: var(--bg-page);
  padding-block: var(--sp-section);
  /* Les boucles du tracé (partials/chemin.njk) débordent la liste par
     construction. `clip` et non `hidden` : `hidden` ferait de la section un
     conteneur de défilement, ce qui casserait un `position: sticky` en amont.
     Seul l'axe horizontal est arrêté — une barre de défilement latérale serait
     un vrai défaut, alors qu'un débordement vertical ne coûte rien. */
  overflow-x: clip;
}

/* ⚠️ AUCUN CADRE, AUCUN SÉPARATEUR — décision du 17/08/2026.

   Partout ailleurs, un bloc fermé porte le panneau blanc bordé (`.cadre` dans
   _components.scss). Celui-ci fait exception : les quatre garanties sont
   posées à même le crème et se répondent en quinconce. C'est l'ESPACE qui les
   distingue, pas un filet — d'où l'écart vertical généreux ci-dessous, qui
   remplace à lui seul le rôle qu'un séparateur aurait tenu.

   Ne pas y remettre de `border-bottom` : sans cadre autour, un filet qui
   court sur toute la largeur ne délimite plus rien, il barre la page. */
.liste-glyphes__titre {
  margin: 0 0 var(--sp-12);
  max-width: 24ch;
  color: var(--c-blue-zodiac);
  text-wrap: balance;
}

/* ⚠️ IL N'Y A PLUS RIEN ICI SUR LE RYTHME DES ENTRÉES, et ce n'est pas un oubli :
   la grille à deux colonnes, l'alternance gauche/droite, la respiration verticale et
   leur point de rupture sont passés dans la primitive `.quinconce` de
   _components.scss le 20/08/2026, quand la liste du bloc `face-a-face` a eu besoin
   du même rythme. Le gabarit pose donc `.quinconce` / `.quinconce__entree` et il n'y
   a plus de classe `.liste-glyphes__entree`.

   Ne pas rapatrier ces règles ici « pour les avoir sous la main » : c'est exactement
   le doublon que l'extraction a supprimé, et rien ne signalerait leur divergence. */
/* =========================================================================
   BLOC « connecteurs » — blocs/connecteurs.njk.
   Origine : « Plus de 50 connecteurs, pour tous vos métiers », Figma
   185:6990.

   Une boîte bordée en deux colonnes : les compartiments métier à gauche,
   leurs sources à droite.

   ⚠️ AUCUNE RÈGLE NE MASQUE LES PANNEAUX ICI. Le repliement est posé par
   assets/js/onglets.js — le moteur d'onglets partagé avec `onglets-metiers` —
   qui écrit `hidden` sur les panneaux inactifs. Une
   règle `[role="tabpanel"] { display: none }` dans cette feuille aurait caché
   quatre compartiments sur cinq même sans JavaScript — exactement ce que le
   dispositif cherche à éviter.

   Le sélecteur `[data-onglets-actif]`, posé par le script, sert donc à
   l'inverse : il n'habille QUE l'état enrichi (la colonne d'onglets, le filet
   de séparation), et laisse la pile brute quand le script ne tourne pas.
   ========================================================================= */
.connecteurs {
  position: relative;
  background-color: var(--bg-page);
  padding-block: var(--sp-section);
  /* La trame fluo file jusqu'au bord de la fenêtre : sans ce clip elle
     ouvrirait une barre de défilement horizontale. Sur l'axe en ligne
     seulement — le débordement vertical de la trame, lui, doit se voir.
     Deux valeurs prises dans {visible, clip} ne se contraignent pas l'une
     l'autre, `overflow-block` reste donc à `visible`. */
  overflow-x: clip;
  /* L'écart entre l'en-tête et la table. Déclaré ici parce que DEUX objets le
     lisent : la marge basse de l'en-tête, et le débordement haut de la trame,
     qui ne doit pas valoir un pixel de plus (voir `.connecteurs__trame`). Deux
     `clamp()` recopiés dériveraient au premier réglage, et c'est le texte du
     chapeau qui en paierait le prix. */
  --connecteurs-entete-gap: clamp(var(--sp-6), 2.4vw, var(--sp-10));
  /* ⚠️ UN SEUL FILET POUR TOUTE LA TABLE, en bleu d'action — Hugo, le
     27/08/2026 : « il faut rendre cohérent tous les liserets du tableau en bleu
     action ».

     Le bloc en portait DEUX jusque-là, et rien ne le disait : --filet-panneau
     (le bleu pâle du site, sur le contour du cadre, le séparateur des colonnes
     et celui des panneaux empilés) et --c-blue-action (les cartes de source,
     repris de la maquette). Tant que les cartes étaient des objets isolés dans
     des gouttières, les deux bleus ne se touchaient jamais ; la mosaïque les a
     mis bord à bord, et l'écart s'est vu.

     ⚠️ C'est donc une DÉROGATION LOCALE à --filet-panneau, bornée à ce bloc :
     `.cadre` continue d'imposer le bleu pâle partout ailleurs sur le site, et
     `.connecteurs__cadre` ne surcharge que la couleur. */
  --connecteurs-filet: 1px solid var(--c-blue-action);
}

.connecteurs__cadre {
  border-color: var(--c-blue-action);
}

/* ---------- Le plateau et sa trame fluo ----------
   Demandée par Hugo le 27/08/2026 : « faire apparaître une grille fluo sur la
   droite de la section ». C'est la trame quadrillée du site (`.grid-pattern`,
   celle du hero), posée derrière la table.

   ⚠️ CE QU'ON EN VOIT N'EST QUE CE QUI DÉBORDE. Le cadre est opaque, et la
   trame passe dessous : il ne reste d'elle que le débordement en haut, en bas,
   et la bande qui file jusqu'au bord droit de la fenêtre. C'est voulu — posée
   PAR-DESSUS, elle croiserait les noms des sources et doublerait le
   quadrillage de la mosaïque, or deux grilles au même endroit cessent de se
   distinguer. C'est l'arbitrage déjà rendu pour `trame: violet`
   (texte-module).

   Le dispositif d'empilement tient en deux lignes et aucune des deux n'est
   facultative : `isolation: isolate` fait du plateau un contexte
   d'empilement — sans lui, le `z-index: -1` de la trame remonte jusqu'à la
   racine et se range SOUS l'aplat de la section, où il est simplement
   invisible. Dans ce contexte, un enfant à `z-index: -1` peint à l'étape 2,
   donc avant le fond blanc du cadre (étape 3) : le cadre le recouvre sans
   avoir à déclarer quoi que ce soit.

   Pourquoi un plateau plutôt que `__inner` : voir blocs/connecteurs.njk. */
.connecteurs__plateau {
  position: relative;
  isolation: isolate;
}

.connecteurs__trame {
  position: absolute;
  z-index: -1;
  pointer-events: none;
  /* Le flanc gauche de la MOSAÏQUE, c'est-à-dire le séparateur entre la
     colonne d'onglets et les panneaux. La boîte partage le contenu du cadre en
     1fr / 2fr, et le cadre porte 1 px de filet de chaque côté : le tiers se
     compte donc sur la largeur du plateau moins ces deux pixels.

     Sous `lg` la boîte repasse à une colonne et ce tiers ne désigne plus rien —
     la trame reste au même endroit, en décor. */
  inset-inline-start: calc(1px + (100% - 2px) / 3);
  /* Jusqu'au bord de la fenêtre. Le plateau est dans le conteneur centré, donc
     la distance de son arête droite au bord de l'écran vaut exactement
     `50vw - 50%` — aucune cote du conteneur n'est recopiée, et rien n'est à
     tenir accordé le jour où --container-max bouge. */
  inset-inline-end: calc(50% - 50vw);
  /* ⚠️ LE DÉBORDEMENT N'EST PAS LE MÊME EN HAUT ET EN BAS, et c'est la seule
     chose qui empêche la trame de mordre le texte.

     EN HAUT elle monte d'exactement l'écart qui sépare l'en-tête de la table,
     jamais plus : le chapeau est bridé à 42rem, soit bien au-delà du tiers où
     la trame commence, donc tout pixel gagné au-dessus de cet écart passe
     derrière ses dernières lignes. Elle a mordu le texte pendant une heure le
     27/08/2026, avec un débordement haut de 5rem. En lisant la MÊME variable
     que la marge de l'en-tête, la garantie est structurelle : allonger le
     chapeau, changer la marge, rien ne peut ramener le défaut.

     ⚠️ Et un `z-index` sur l'en-tête ne réglait rien : il ferait passer le
     texte DEVANT la trame, pas la trame à côté du texte — le quadrillage se
     verrait toujours entre les lettres, ce qui est exactement ce qu'Hugo
     appelle « manger le texte ».

     EN BAS il n'y a rien à protéger — que la marge de section — donc la trame
     y prend ses aises. Le plafond de 5rem (68 px) tient dans --sp-section
     (102 px au repos) : elle ne peut pas déborder sur la section suivante.
     ⚠️ Le champ `cta` du bloc, s'il était un jour déclaré, poserait un lien
     juste sous le cadre : il resterait à gauche du tiers, mais c'est le seul
     texte que ce débordement pourrait atteindre. */
  inset-block-start: calc(-1 * var(--connecteurs-entete-gap));
  inset-block-end: calc(-1 * clamp(2.5rem, 5vw, 5rem));
}

/* ---------- En-tete ----------
   HORS DU CADRE depuis le 18/08/2026, au titre de l'exception prévue par la
   règle du panneau fermé : le cadre enferme ici un module interactif, qui porte
   déjà la charge que le titre donnerait. La règle et son exception vivent dans
   `.cadre` (_components.scss) ; le détail du cas est dans
   blocs/connecteurs.njk.

   Le titre ne déclare rien de sa police : _base.scss pose déjà le titrage et la
   taille du niveau. Il reprend en revanche le bleu profond de `.cadre__titre`,
   qui reste la couleur des titres de section sur fond clair. */
.connecteurs__entete {
  margin-block-end: var(--connecteurs-entete-gap);
}

.connecteurs__titre {
  margin: 0;
  max-width: 24ch;
  color: var(--c-blue-zodiac);
  text-wrap: balance;
}

.connecteurs__chapeau {
  margin: var(--sp-4) 0 0;
  max-width: 42rem;
  font-size: var(--fs-base);
  line-height: var(--lh-normal);
  color: var(--text-strong);
}

/* ---------- La boîte ----------
   Une colonne pour les onglets, une pour les panneaux. Sans JavaScript la
   grille retombe à une seule colonne : les onglets et les panneaux s'empilent
   alors dans l'ordre du document, ce qui donne cinq blocs lisibles. */
/* `.connecteurs__boite` n'a plus aucune règle au repos, et c'est normal : le
   contour vient de `.cadre`, et le `border-top` qui vivait ici séparait le
   tableau de la bande de titre. Cette bande étant sortie du cadre, le filet ne
   faisait plus que doubler le bord haut du cadre. Retiré le 18/08/2026. */
.connecteurs__boite[data-onglets-actif] {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 2fr);
}

/* ---------- Colonne d'onglets ---------- */
.connecteurs__onglets {
  display: flex;
  flex-direction: column;
  padding-block: var(--sp-3);
}

[data-onglets-actif] .connecteurs__onglets {
  border-inline-end: var(--connecteurs-filet);
}

/* Un bouton, donc on remet à zéro ce que le navigateur y met par défaut. Le
   filet de gauche est transparent au repos et prend la couleur à la
   sélection : sans lui, l'onglet actif se décalerait de 2 px. */
.connecteurs__onglet {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  width: 100%;
  padding: var(--sp-4) var(--sp-6);
  border: 0;
  border-inline-start: 2px solid transparent;
  background: none;
  font: inherit;
  text-align: start;
  cursor: pointer;
  transition: background-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}

.connecteurs__nom {
  flex: 1;
  font-size: var(--fs-md);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--ls-tight);
  color: var(--text-muted);
}

/* ---------- La puce d'onglet ----------
   Une FORME DE MARQUE depuis le 27/08/2026 — Hugo. C'était un rond de 9 px ;
   le choix des trois formes et leur rotation dans la liste sont dans le
   gabarit.

   ⚠️ LA TEINTE PASSE PAR --shape-color, PAS PAR `background-color`. Sur un
   masque, le fond EST la forme : écrire `background-color` marcherait, mais
   court-circuiterait le jeton que `.forme` expose pour ça, et la règle de
   l'onglet actif juste en dessous n'aurait plus rien de commun avec celle-ci.

   Elle surcharge le `.forme--bleu` que le macro pose sur le même élément :
   même spécificité, c'est donc l'ordre des fichiers qui tranche, et les
   feuilles de section sont émises après _components.scss. Le `bleu` du gabarit
   n'est là que parce que le macro exige une teinte.

   `flex: none` : la puce est un élément flexible du bouton, et une forme n'a
   pas de taille intrinsèque à défendre — sans ça elle se ferait comprimer par
   un intitulé long. */
.connecteurs__puce {
  --shape-w: 0.875rem;
  --shape-color: var(--c-blue-400);
  flex: none;
}

/* La variation d'orientation, sur une période de 4 quand les formes tournent
   sur une période de 3 : les deux ne retombent en phase qu'au douzième onglet,
   donc aucune des sept puces ne répète sa voisine. Purement décoratif — la
   règle « jamais deux fois la même forme côte à côte » est déjà tenue par le
   cycle des formes, celle-ci ne fait qu'éviter l'effet de frise. */
.connecteurs__onglet:nth-child(4n+2) .connecteurs__puce {
  --shape-rot: 18deg;
}

.connecteurs__onglet:nth-child(4n+3) .connecteurs__puce {
  --shape-rot: -12deg;
}

.connecteurs__onglet:nth-child(4n) .connecteurs__puce {
  --shape-rot: 32deg;
}

.connecteurs__compte {
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  color: var(--text-muted);
}

.connecteurs__onglet[aria-selected=true] {
  border-inline-start-color: var(--c-blue-action);
  background-color: var(--c-blue-50);
}

.connecteurs__onglet[aria-selected=true] .connecteurs__nom {
  color: var(--c-blue-zodiac);
}

.connecteurs__onglet[aria-selected=true] .connecteurs__puce {
  --shape-color: var(--c-blue-action);
}

.connecteurs__onglet[aria-selected=true] .connecteurs__compte {
  color: var(--c-blue-action);
}

@media (hover: hover) {
  .connecteurs__onglet:not([aria-selected=true]):hover {
    background-color: color-mix(in srgb, var(--c-blue-50) 60%, transparent);
  }
}
.connecteurs__onglet:focus-visible {
  outline: 2px solid var(--c-blue);
  outline-offset: -2px;
}

/* ---------- Panneaux ----------
   ⚠️ LA TABLE NE CHANGE PLUS DE HAUTEUR D'UN ONGLET À L'AUTRE — Hugo, le
   27/08/2026 : « le tableau doit pas changer de taille en fonction du nombre
   d'item, on doit juste pouvoir scroller sur la partie droite ». Un
   compartiment de quatre cartes et un de onze faisaient sauter la boîte de
   150 px à chaque bascule.

   LA HAUTEUR EST CELLE DE LA COLONNE D'ONGLETS, et elle n'est écrite nulle
   part : les panneaux sont mis en position absolue, un élément hors flux ne
   pèse rien dans le calcul de la rangée de grille, donc c'est la colonne de
   gauche — sept onglets, toujours les mêmes — qui la fixe. Une cote en rem
   aurait été fausse au premier onglet ajouté, et sans rien pour le signaler.

   Elle laisse voir deux rangées et demie de cartes : la rangée coupée est ce
   qui annonce qu'il y a à faire défiler, sans qu'on ait à le dessiner.

   ⚠️ TOUT CECI EST DERRIÈRE `[data-onglets-actif]`, comme la grille elle-même.
   Sans JavaScript les panneaux s'empilent dans le flux et la table s'allonge :
   on perd la hauteur constante, jamais le contenu. C'est le contrat du bloc.

   Le `tabindex="0"` que le gabarit pose sur chaque panneau, jusqu'ici une
   simple convention ARIA, devient ici indispensable : une zone qui défile doit
   pouvoir être atteinte au clavier. */
/* Le fond de la partie droite — Hugo, le 27/08/2026 : « le fond de la partie
   droite quand il n'y a pas de connecteur doit être en bleu 50 ». Il est posé
   sur la COLONNE et non sur le panneau, pour couvrir aussi ce qui reste sous la
   mosaïque quand elle ne remplit pas la hauteur. Les cartes, elles, restent
   blanches : c'est ce contraste qui découpe la mosaïque. */
.connecteurs__panneaux {
  background-color: var(--c-blue-50);
}

[data-onglets-actif] .connecteurs__panneaux {
  position: relative;
}

[data-onglets-actif] .connecteurs__panneau {
  position: absolute;
  inset: 0;
  /* Le défilement, et le clip du filet de fin de mosaïque sur l'autre axe.
     ⚠️ `hidden` et non `clip` sur l'axe en ligne : `clip` et une valeur
     défilante ne se combinent pas, c'est `hidden` qui est la valeur clippante
     compatible. */
  overflow: hidden auto;
}

/* ⚠️ AUCUN REMBOURRAGE, depuis le passage en mosaïque le 27/08/2026 : les
   filets de la mosaïque doivent atteindre le bord du cadre. C'est ce que fait
   déjà `module.grille` (texte-module) pour la même raison.

   `overflow: clip` est l'autre moitié du dispositif de filets — voir
   `.connecteurs__sources` juste en dessous. Effet de bord accepté : les cartes
   de la dernière rangée entrent en glissant de 8 px vers le haut, donc depuis
   sous l'arête du cadre. Ça se lit comme une entrée, pas comme un défaut.

   `clip` et non `hidden` : on ne veut pas d'un conteneur de défilement, rien
   n'est à atteindre ici que le pixel de filet qu'on retire exprès. */
.connecteurs__panneau {
  padding: 0;
  overflow: clip;
}

/* Sans JavaScript, les panneaux s'empilent : un filet les sépare pour qu'on
   voie où commence chaque compartiment. Avec le script, un seul est visible
   à la fois et le filet n'a plus lieu d'être. */
.connecteurs__panneau + .connecteurs__panneau {
  border-top: var(--connecteurs-filet);
}

[data-onglets-actif] .connecteurs__panneau + .connecteurs__panneau {
  border-top: 0;
}

.connecteurs__panneau:focus-visible {
  outline: 2px solid var(--c-blue);
  outline-offset: -2px;
}

/* Il y avait ici `.connecteurs__barre`, le nom de la catégorie repris en chasse
   fixe au-dessus des cartes. Retirée avec l'élément le 18/08/2026 : l'onglet
   actif de gauche porte déjà ce nom, et le H2 de la section l'annonce encore
   avant. Le champ `mention` du compartiment (« Natif ») n'avait plus de support
   non plus, il n'est plus documenté. */
/* ---------- Les cartes de source ----------
   Origine : Figma 225:2025. La maquette donne une carte de 182 × 168, logo de
   152 × 72 posé à 15/23, nom en dessous vers y=117.

   Les deux seules variables liées à ce nœud sont `Greys/Light Grey 1` (#F9F9F5)
   et `Bleu/Action Blue` (#2340FF) : ce sont exactement --c-base-main et
   --c-blue-action. Rien n'est donc écrit en dur ici.

   La grille est en `auto-fill` et non en nombre de colonnes fixe : les
   compartiments vont de 3 à 10 sources, et une grille à 4 colonnes aurait
   laissé « Relation client » avec une ligne d'un tiers remplie. */
/* ⚠️ MOSAÏQUE : CINQ COLONNES FIXES, AUCUNE GOUTTIÈRE — demandé par Hugo le
   27/08/2026. La grille était en `auto-fill` jusque-là, précisément pour qu'un
   compartiment de trois sources ne laisse pas une rangée au tiers remplie ;
   c'est maintenant le gabarit qui s'en charge, en complétant le rectangle avec
   des cases vides (voir blocs/connecteurs.njk).

   ⚠️ LE 5 EST ÉCRIT ICI ET DANS LE GABARIT, et rien ne signalerait leur
   divergence : un `nth-child` ne lit pas une propriété personnalisée, un
   gabarit ne lit pas une feuille. Changer l'un, c'est changer l'autre.

   ---------------------------------------------------------------------------
   LES FILETS SANS DOUBLURE, ET SANS PUBLIER LE MOINDRE COMPTE.

   Chaque carte ne porte que ses arêtes de FIN (droite et bas) : entre deux
   cartes, un seul filet. Restent les arêtes de fin de la mosaïque elle-même,
   qui viendraient doubler le filet du cadre — 2 px là où il en faut 1.

   D'où la marge négative : la mosaïque déborde d'un pixel à DROITE, et le
   `overflow: clip` du panneau emporte ce filet-là. L'astuce vaut quel que soit
   le nombre de cartes, donc aucun `:nth-child(5n)` de fin de rangée n'est à
   écrire — c'est ce qui évite de publier une seconde fois le 5.

   ⚠️ RIEN D'ÉQUIVALENT EN BAS, ET C'EST VOULU. Le filet bas de la dernière
   rangée FERME la mosaïque, et il en a besoin : la colonne d'onglets est
   souvent la plus haute des deux, la mosaïque s'arrête alors en pleine hauteur
   de cadre et des cases ouvertes par le bas se liraient comme un dessin
   inachevé. Le prix accepté est le cas inverse — quand c'est la mosaïque qui
   fait la hauteur, son filet bas vient toucher celui du cadre. Deux traits de
   1 px l'un contre l'autre, dont l'un est le filet pâle du cadre : ça se lit
   comme une arête un peu plus épaisse, pas comme un doublon.

   ⚠️ Et la même marge négative en bas NE marcherait pas : `overflow: clip` ne
   crée PAS de contexte de formatage de bloc, contrairement à `hidden`, donc la
   marge basse de la mosaïque se fusionne avec celle du panneau et traverse au
   lieu de raccourcir sa boîte. Le clip n'a alors rien à emporter.

   La technique de la gouttière (`gap: 1px` sur un fond teinté, chaque cellule
   repeignant le sien), qui est l'idiome du site pour les quadrillages, ne
   convenait pas ici : une dernière rangée incomplète laisse voir le fond du
   conteneur en aplat plein, soit un carré bleu au bout de la mosaïque. */
.connecteurs__sources {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 0;
  margin: 0;
  margin-inline-end: -1px;
  padding: 0;
  list-style: none;
}

/* Coins CARRÉS et filet bleu : la carte suit la grammaire des cadres du site,
   à ceci près que son fond est le crème de la page et non le blanc — c'est ce
   qui la détache du panneau blanc qui la porte. */
/* ⚠️ DEUX RANGS DE HAUTEUR FIXE, ET NON UN CENTRAGE. C'est ce qui règle deux
   défauts d'un coup :

     TOUTES LES CARTES FONT LA MÊME TAILLE. Avec `aspect-ratio`, un nom qui se
       replie sur deux lignes faisait grandir sa carte — le ratio n'est qu'une
       indication, la boîte grandit si le contenu déborde — et toute la rangée
       suivait. Une `height` ferme la question.
     LE NOM EST TOUJOURS À LA MÊME PLACE. Un logo large et un logo carré n'ont
       pas la même hauteur rendue ; centrés en bloc avec leur nom, ils
       déplaçaient le texte d'une carte à l'autre. Ici le rang du nom a sa propre
       hauteur, en bas, et le logo se centre dans ce qui reste.

   La hauteur du rang de nom tient deux lignes à --fs-xs (2 × 18,9px < 40px) :
   un nom qui se replie reste centré dans la même bande. */
.connecteurs__source {
  display: grid;
  grid-template-rows: minmax(0, 1fr) 2.5rem;
  justify-items: center;
  align-items: center;
  /* ⚠️ CARRÉE, ET NON PLUS HAUTE DE 10,5rem. La cote fixe de la maquette n'a
     plus de sens dans une mosaïque : la largeur d'une case y vaut le cinquième
     du panneau, donc elle est fluide, et la hauteur doit la suivre.

     Le `aspect-ratio` que l'ancienne cote fixe avait justement écarté redevient
     sûr ici, mais à une condition : `min-block-size: 0`. La taille minimale
     automatique d'un élément de grille vaut `auto`, c'est-à-dire son contenu —
     un nom qui se replierait sur trois lignes ferait alors grandir la case, et
     toute la rangée avec elle, ce qui est exactement le défaut que la hauteur
     fixe corrigeait. Les deux rangs internes restent fermes (1fr pour le logo,
     2,5rem pour le nom), donc rien ne pousse. */
  aspect-ratio: 1;
  min-block-size: 0;
  padding: var(--sp-4);
  /* ⚠️ BLANC, ET NON PLUS LE CRÈME DE LA PAGE — Hugo, le 27/08/2026. Le crème
     venait de la maquette et servait à DÉTACHER la carte du panneau blanc qui
     la portait ; dans une mosaïque sans gouttière, il n'y a plus rien dont se
     détacher — ce sont les filets qui découpent, et le crème ne faisait plus
     que teinter tout le flanc droit du cadre.

     C'est aussi ce qui rend une dernière rangée incomplète invisible : la case
     absente et la case présente ont désormais le même fond, il ne manque que
     des filets. */
  background-color: var(--c-white);
  /* Arêtes de FIN seulement : voir le bloc de `.connecteurs__sources`. */
  border: 0;
  border-inline-end: 1px solid var(--c-blue-action);
  border-block-end: 1px solid var(--c-blue-action);
  border-radius: 0;
}

/* ⚠️ PAS DE SURVOL SUR UNE CARTE DE SOURCE, retiré le 25/08/2026 à la demande
   d'Hugo. Elle portait un aplat fluo, sur la grammaire de `.btn--secondary` en
   contexte clair.

   Deux raisons de ne pas le rétablir. La carte est un `<li>` : elle ne mène
   nulle part et ne répond à rien, or un objet qui réagit au survol annonce
   qu'il est actionnable — quarante-quatre cartes qui s'allument sous le
   curseur promettent quarante-quatre destinations qui n'existent pas. Et ce
   survol n'était même pas gardé par `@include mix.hover-hover`, contrairement
   à celui des onglets juste au-dessus : sur un écran tactile, l'aplat restait
   donc collé après un appui.

   La `transition: background-color` de la carte est partie avec lui : elle ne
   servait que ça. L'entrée décalée des cartes passe par `@keyframes`, pas par
   une transition — voir plus bas. Rien d'autre à retirer.

   Ce qui reste survolable dans le bloc, et doit le rester : les ONGLETS de
   compartiment (ce sont des commandes) et `.connecteurs__lien`. */
/* 72px de haut dans la maquette. `object-fit: contain` parce que les 44 exports
   n'ont pas le même rapport — un logo large comme Shopify et un logo carré
   comme Odoo doivent tenir dans la même boîte sans être déformés ni rognés. */
.connecteurs__logo {
  max-width: 100%;
  max-height: 4.5rem;
  width: auto;
  height: auto;
  object-fit: contain;
}

/* Les trois points de la carte d'appel, à la place du logo. En bleu nuit et non
   en bleu d'action : ce n'est pas un connecteur, la nuance le dit sans qu'on ait
   à l'écrire. */
.connecteurs__points {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
}

.connecteurs__points > span {
  width: 0.5rem;
  aspect-ratio: 1;
  border-radius: 50%;
  background-color: var(--c-blue-dark);
}

.connecteurs__source-nom {
  display: flex;
  align-items: center;
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  line-height: var(--lh-snug);
  text-align: center;
  text-wrap: balance;
  color: var(--c-blue-action);
}

/* ---------- Entrée des cartes au changement de compartiment ----------
   ⚠️ AUCUNE LIGNE DE JAVASCRIPT EN PLUS, et c'est ce qui rend l'effet gratuit.
   `onglets.js` bascule les panneaux par l'attribut `hidden`, donc par
   `display: none` — qui ne se transitionne pas, mais qui RELANCE les animations
   CSS de l'élément à sa réapparition. C'est ce redémarrage qu'on exploite.

   ENTRÉE SEULEMENT : faire sortir les cartes de l'ancien compartiment
   demanderait de retarder la pose du `hidden`, donc de toucher au script et à
   l'état ARIA qu'il tient. Le panneau sortant disparaît net, le nouveau se
   compose — c'est la lecture qu'on veut, et elle ne coûte rien.

   Le décalage passe par `nth-child` et non par un `--i` posé en ligne dans le
   gabarit : le site interdit les attributs `style` (voir CLAUDE.md).

   ⚠️ VINGT CRANS, ET NON PLUS DIX. Le plafond valait le compartiment le plus
   fourni, « Gestion & Logistique » et ses dix sources ; depuis le 27/08/2026 la
   carte « Votre système » s'ajoute à TOUS les compartiments — donc onze — et
   Hugo a levé la limite des dix connecteurs par catégorie. Une vingt-et-unième
   carte entrerait sans décalage, ce qui est un défaut bien plus discret qu'une
   carte restée invisible. */
@keyframes connecteurs-carte-entree {
  from {
    opacity: 0;
    transform: translateY(0.5rem);
  }
}
.connecteurs__panneau:not([hidden]) .connecteurs__source {
  /* `both` : le repli arrière tient la carte à `opacity: 0` pendant son délai,
     sinon elle serait pleinement visible puis disparaîtrait pour entrer. */
  animation: connecteurs-carte-entree var(--dur-base) var(--ease-out) both;
}

.connecteurs__panneau:not([hidden]) .connecteurs__source:nth-child(1) {
  animation-delay: 0ms;
}

.connecteurs__panneau:not([hidden]) .connecteurs__source:nth-child(2) {
  animation-delay: 30ms;
}

.connecteurs__panneau:not([hidden]) .connecteurs__source:nth-child(3) {
  animation-delay: 60ms;
}

.connecteurs__panneau:not([hidden]) .connecteurs__source:nth-child(4) {
  animation-delay: 90ms;
}

.connecteurs__panneau:not([hidden]) .connecteurs__source:nth-child(5) {
  animation-delay: 120ms;
}

.connecteurs__panneau:not([hidden]) .connecteurs__source:nth-child(6) {
  animation-delay: 150ms;
}

.connecteurs__panneau:not([hidden]) .connecteurs__source:nth-child(7) {
  animation-delay: 180ms;
}

.connecteurs__panneau:not([hidden]) .connecteurs__source:nth-child(8) {
  animation-delay: 210ms;
}

.connecteurs__panneau:not([hidden]) .connecteurs__source:nth-child(9) {
  animation-delay: 240ms;
}

.connecteurs__panneau:not([hidden]) .connecteurs__source:nth-child(10) {
  animation-delay: 270ms;
}

.connecteurs__panneau:not([hidden]) .connecteurs__source:nth-child(11) {
  animation-delay: 300ms;
}

.connecteurs__panneau:not([hidden]) .connecteurs__source:nth-child(12) {
  animation-delay: 330ms;
}

.connecteurs__panneau:not([hidden]) .connecteurs__source:nth-child(13) {
  animation-delay: 360ms;
}

.connecteurs__panneau:not([hidden]) .connecteurs__source:nth-child(14) {
  animation-delay: 390ms;
}

.connecteurs__panneau:not([hidden]) .connecteurs__source:nth-child(15) {
  animation-delay: 420ms;
}

.connecteurs__panneau:not([hidden]) .connecteurs__source:nth-child(16) {
  animation-delay: 450ms;
}

.connecteurs__panneau:not([hidden]) .connecteurs__source:nth-child(17) {
  animation-delay: 480ms;
}

.connecteurs__panneau:not([hidden]) .connecteurs__source:nth-child(18) {
  animation-delay: 510ms;
}

.connecteurs__panneau:not([hidden]) .connecteurs__source:nth-child(19) {
  animation-delay: 540ms;
}

.connecteurs__panneau:not([hidden]) .connecteurs__source:nth-child(20) {
  animation-delay: 570ms;
}

/* Le repli, quand un compartiment n'a aucune source. Il porte son rembourrage
   lui-même depuis que le panneau a perdu le sien pour la mosaïque. */
.connecteurs__vide {
  margin: 0;
  padding: var(--sp-6);
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  color: var(--text-muted);
}

/* ---------- Pied ---------- */
.connecteurs__pied {
  margin: var(--sp-6) 0 0;
}

.connecteurs__lien {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  color: var(--text-accent);
  text-decoration: none;
}

.connecteurs__lien:hover {
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.25em;
}

/* =========================================================================
   Une seule colonne.

   Les onglets passent au-dessus des panneaux plutôt qu'à côté : à cette
   largeur, une colonne d'onglets de 33 % laisserait aux sources moins de
   place qu'il n'en faut pour deux cartes côte à côte.
   ========================================================================= */
@media (max-width: 60rem) {
  .connecteurs__boite[data-onglets-actif] {
    grid-template-columns: minmax(0, 1fr);
  }
  [data-onglets-actif] .connecteurs__onglets {
    border-inline-end: 0;
    border-bottom: var(--connecteurs-filet);
  }
  /* ⚠️ LES PANNEAUX REVIENNENT DANS LE FLUX, sans quoi la partie droite
     n'aurait plus AUCUNE hauteur : les onglets sont passés au-dessus, ils ne
     sont donc plus la colonne d'en face dont le panneau hors flux empruntait la
     hauteur — il n'y a plus de rangée à remplir, et `inset: 0` se résout sur
     zéro.

     La hauteur redevient donc une cote écrite, et c'est le seul endroit du bloc
     où il y en a une. 26rem valent à peu près deux rangées de cartes à cette
     largeur : assez pour que la mosaïque se lise, assez peu pour que la liste
     d'onglets reste atteignable sans traverser toute la page. */
  [data-onglets-actif] .connecteurs__panneaux {
    position: static;
  }
  [data-onglets-actif] .connecteurs__panneau {
    position: static;
    max-block-size: 26rem;
  }
}
/* =========================================================================
   La mosaïque rend ses cinq colonnes.

   À 768 px, cinq colonnes donnent des cases d'environ 110 px de large :
   « Oracle Netsuite » en chasse fixe n'y tient plus, et un logo large y devient
   une vignette. On repasse donc en `auto-fill`, qui retire une colonne à mesure
   que la place manque — sans publier le moindre point de rupture
   supplémentaire.

   Rien d'autre ne bouge : une dernière rangée incomplète l'était déjà à cinq
   colonnes, et ce qui lui manque est du blanc, pas un trou.
   ========================================================================= */
@media (max-width: 48rem) {
  .connecteurs__sources {
    grid-template-columns: repeat(auto-fill, minmax(7.5rem, 1fr));
  }
}
/* =========================================================================
   BLOC « onglets-metiers » — blocs/onglets-metiers.njk.
   Origine : « Chaque métier suit son activité, à sa façon » de la page
   plateforme, Figma 141:8150.

   Une boîte sombre : l'en-tête, une rangée d'onglets métier, puis deux colonnes
   — les tableaux de bord du métier sélectionné à gauche, ce que la plateforme
   permet à tout le monde à droite.

   ⚠️ CETTE FEUILLE N'EMPLOIE QUE DES RÔLES SÉMANTIQUES (--text-strong,
   --text-muted, --text-accent, --border-subtle). Le bloc sait basculer en fond
   clair : une couleur écrite en dur y donnerait du blanc sur crème, illisible et
   sans la moindre alerte. C'est la même contrainte que _texte-panneau.scss et
   _texte-module.scss.

   ⚠️ AUCUNE RÈGLE NE MASQUE LES PANNEAUX ICI. Le repliement est posé par
   assets/js/onglets.js, qui écrit `hidden` sur les panneaux inactifs. Une règle
   `[role="tabpanel"] { display: none }` aurait caché quatre panneaux sur cinq
   même sans JavaScript — exactement ce que le dispositif cherche à éviter. Le
   sélecteur `[data-onglets-actif]`, posé par le script, sert donc à l'inverse :
   il n'habille QUE l'état enrichi.
   ========================================================================= */
.onglets-metiers {
  position: relative;
  background-color: var(--bg-page);
  /* La boîte porte son propre rembourrage vertical : la section n'a plus à
     respirer autant, sinon les deux se cumulent. Même arbitrage que
     `.texte-module--boite`. */
  padding-block: clamp(var(--sp-6), 2.4vw, var(--sp-10));
}

/* ---------- La boîte ----------
   Les cotes des autres boîtes du site — `.temoignages__boite`,
   `.sequence__boite`, `.texte-panneau__boite` : 96rem, rayon 28px, aucun
   contour. Sur un aplat franc posé sur crème, le contraste fait déjà l'arête.

   ⚠️ 96rem ET NON 88rem. `.texte-module__boite` est la SEULE des boîtes du site
   resserrée à 88rem (Hugo, le 18/08/2026) ; celle-ci reprend la cote de
   référence de MAQUETTE.md. Ne pas « réaligner » les deux en croyant corriger un
   oubli.

   Le rayon est écrit en dur comme dans les autres boîtes : il n'existe pas de
   jeton à 28px (--radius-lg vaut 16px). */
.onglets-metiers__boite {
  /* Les deux servent le motif, et rien d'autre — voir la section « LE MOTIF » en
     bas de feuille. `position` lui donne son repère : sans elle il se calerait sur
     la SECTION, qui court d'un bord à l'autre et n'a pas cet arrondi.

     ⚠️ `overflow: hidden` EST POSÉ SUR LA BOÎTE ELLE-MÊME, sans enveloppe de clip,
     contrairement à `.texte-panneau__decor` et `.texte-module__decor`. Ces deux-là
     existent parce que leur boîte laisse volontairement déborder quelque chose — le
     glyphe du pilier — qu'un overflow décapiterait. Celle-ci ne laisse rien
     dépasser, elle porte donc le découpage directement. Le jour où quelque chose en
     déborde, c'est l'enveloppe qu'il faudra reprendre, pas le découpage qu'il
     faudra retirer. */
  position: relative;
  overflow: hidden;
  max-width: 96rem;
  margin-inline: auto;
  border-radius: 1.75rem;
  padding-block: clamp(var(--sp-12), 4.7vw, 5.5rem);
}

/* ⚠️ IL FAUT REMETTRE LE CONTENU AU-DESSUS DU MOTIF. Le motif est positionné,
   donc il peint par-dessus le contenu en flux normal quel que soit l'ordre du
   DOM. `z-index: -1` sur le motif serait l'autre voie, mais elle l'enverrait
   derrière le fond de la boîte, où il est simplement invisible — même piège que
   `.texte-panneau__inner` et que le tracé de `.chemin`. */
.onglets-metiers__inner {
  position: relative;
  z-index: 1;
}

.onglets-metiers__boite--sombre {
  background-color: var(--c-blue-dark);
}

/* ---------- La boîte rose foncé ----------
   Demandée par Hugo le 19/08/2026, en remplacement du bleu nuit : sur cette page,
   la boîte prend la couleur du pilier plutôt que celle du site.

   ⚠️ #BC4B64 EST UNE COULEUR CHOISIE PAR HUGO, PAS UN MÉLANGE CALCULÉ. Elle a
   remplacé le même jour un `color-mix` du rose du pilier vers le bleu nuit : ne pas
   la « ramener » sur un mélange de --c-pilier-plateforme en croyant réduire une
   valeur en dur — le rapprochement serait faux, et la teinte changerait.

   ⚠️ ELLE N'EST PLUS ÉCRITE ICI DEPUIS LE 20/08/2026 : c'est le jeton
   --c-pilier-plateforme-sombre, promu quand `texte-panneau` a eu besoin de la même
   couleur pour ses boîtes de la page produit. La recopier aurait laissé deux valeurs
   à tenir accordées. `--rose-metiers` reste comme alias local, pour que les quinze
   règles de cette feuille qui la lisent n'aient pas à changer.

   ⚠️ ELLE NE PORTE PAS LES TROIS ENCRES DU CONTEXTE SOMBRE. Mesuré :

     blanc   --text-strong   4,85  ✓ le corps de texte passe
     casper  --text-muted    2,35  ✗ illisible — corrigé juste en dessous
     fluo    --text-accent   4,16  ⚠️ jamais du texte courant, voir plus bas

   C'est pour ce genre de cas que `.on-dark` ne suffit jamais seule sur une teinte
   de pilier : ses trois rôles sont calés sur le bleu nuit (#202b5c), bien plus
   sombre. Le violet `connecteur` de sections/_texte-panneau.scss a rencontré
   exactement le même mur, et s'est corrigé de la même façon.

   ⚠️ CES CHIFFRES NE CONCERNENT QUE CE QUI EST POSÉ DIRECTEMENT SUR LE ROSE :
   le titre, le chapeau et la liste commune. La carte d'onglets, elle, est un
   aplat CRÈME et remet les rôles en contexte clair — voir sa règle plus bas. */
.onglets-metiers__boite--plateforme {
  --rose-metiers: var(--c-pilier-plateforme-sombre);
  background-color: var(--rose-metiers);
  /* Le fond de la boîte devient le fond de référence : c'est lui que
     `.btn--secondary` recopie pour son aplat, et lui que le voile de survol
     mélange au fluo. Sans ça le bouton se remplirait du crème de la page et
     flotterait sur la couleur. Même raison que dans _texte-panneau.scss. */
  --section-bg: var(--rose-metiers);
  --bg-page: var(--rose-metiers);
  /* ⚠️ LE CASPER PASSE AU BLANC, et il n'y avait pas de demi-mesure possible :
     à 2,35 il est illisible, et TOUTE proportion de blanc inférieure à 100 %
     échoue aussi — un blanc voilé à 90 % ne remonte qu'à 4,24, encore sous le
     seuil. C'est le même remède que `.texte-panneau--connecteur`.

     Il ne porte plus rien sur le rose depuis que la carte d'onglets a repris la
     mention « Tableaux à compléter » et les libellés d'onglets. Il est gardé
     quand même : c'est le rôle des légendes et des états vides, et le premier
     texte secondaire ajouté sur cette boîte le prendrait sans le savoir. */
  --text-muted: var(--c-white);
}

/* ⚠️ --text-accent RESTE AU FLUO SUR LE ROSE, contrairement à la boîte
   `connecteur` qui le passe au blanc. Ce n'est pas un oubli : son seul emploi ici
   est l'intitulé « Depuis la plateforme, chacun peut »… qui prend d'ailleurs
   --text-strong. Le rôle n'est donc lu par rien aujourd'hui, et les formes fluo
   devant les phrases tirent leur couleur de `forme--fluo`, pas de lui.

   S'il reprend du service, la mesure est 4,16 : bon pour un libellé de service en
   capitales, jamais pour du texte qui porte de l'information. */
/* En dessous de 96rem, `margin-inline: auto` ne ménage plus rien : la boîte
   toucherait les deux bords et cesserait d'en être une. */
@media (max-width: 96rem) {
  .onglets-metiers__boite {
    margin-inline: var(--container-pad);
  }
}
/* ---------- La mise en colonnes ----------
   LE TITRE SUR TOUTE LA LARGEUR, puis le texte à gauche et le module d'onglets à
   droite, démarrant à la même hauteur. Demandé par Hugo le 19/08/2026, en deux
   temps : le module à droite d'abord, puis « le titre prend toute la largeur
   quand même, le tableau sera aligné au texte ».

   C'est exactement l'arbitrage de `.texte-module--colonnes`, et le mécanisme est
   le même — le pourquoi complet est dans sections/_texte-module.scss.

   ⚠️ `display: contents` FAIT DISPARAÎTRE LA BOÎTE DE L'EN-TÊTE, et c'est le but :
   ses enfants — titre, chapeau, liste commune — deviennent des enfants directs de
   la grille, donc plaçables individuellement. Sans lui l'en-tête est un bloc
   unique et le module ne peut s'aligner que sur son haut, c'est-à-dire sur le
   TITRE.

   Les alternatives écartées, les mêmes que là-bas : une marge haute sur le module
   égale à la hauteur du titre (indevinable, le titre est en `clamp()` et se replie
   selon la largeur), et `align-self` (qui aligne des bords de rangée, pas du
   contenu).

   ⚠️ `column-gap` SEUL, ET `row-gap: 0`. Ne pas revenir à un `gap` unique : le
   chapeau et la liste commune sont tous deux FACULTATIFS, et une rangée vide reste
   une rangée. La gouttière s'appliquerait de part et d'autre — deux fois 4rem
   pour un chapeau absent. À `row-gap: 0`, l'espacement vertical revient aux marges
   des éléments eux-mêmes.

   ⚠️ LA MARGE BASSE DE L'EN-TÊTE NE S'APPLIQUE PLUS, `display: contents` ayant
   supprimé sa boîte. C'est le titre qui porte désormais la respiration sous lui. */
@media (min-width: 60rem) {
  .onglets-metiers__inner {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    column-gap: clamp(var(--sp-8), 3.2vw, var(--sp-16));
    row-gap: 0;
    align-items: start;
  }
  .onglets-metiers__entete {
    display: contents;
  }
  /* ⚠️ C'EST LA CELLULE QUI PREND TOUTE LA LARGEUR, PAS LE TEXTE. Hugo l'a précisé
     le 19/08/2026 : `grid-column: 1 / -1` sert à sortir le titre de la colonne de
     gauche pour que le module s'aligne sur le CHAPEAU en dessous — pas à étirer le
     titre d'un bord à l'autre. Sa mesure reste donc celle de la règle de base
     (28ch), et il se replie au même endroit qu'avant.

     Ne pas y ajouter de `max-width` desserrée : elle a été essayée le même jour et
     retirée aussitôt. */
  .onglets-metiers__titre {
    grid-column: 1/-1;
    grid-row: 1;
    margin-block-end: clamp(var(--sp-6), 2.4vw, var(--sp-10));
  }
  .onglets-metiers__chapeau {
    grid-column: 1;
    grid-row: 2;
    margin-block-start: 0;
  }
  .onglets-metiers__commun {
    grid-column: 1;
    grid-row: 3;
  }
  /* Le module enjambe les deux rangées de texte — le chapeau et la liste commune —
     et se CENTRE dessus. Demandé par Hugo le 19/08/2026 : la carte est plus courte
     que la colonne de gauche, et calée en haut elle laissait un vide sous elle.

     ⚠️ `align-self` SUR LE MODULE, ET NON `align-items` SUR LA GRILLE. La grille
     garde `start` : le titre et les deux blocs de texte doivent rester calés en
     haut de leur rangée. Un centrage global les aurait tous déplacés. C'est le
     même partage que `.texte-module__corps > .texte-module__visuel`.

     `span 2` plutôt que `2 / -1` : si une rangée s'ajoute un jour sous la liste
     commune, le module ne la suivra pas sans qu'on l'ait décidé. */
  .onglets-metiers__module {
    grid-column: 2;
    grid-row: 2/span 2;
    align-self: center;
  }
}
/* ---------- En-tête ----------
   Le titre ne déclare rien de sa police : _base.scss pose déjà le titrage et la
   taille du niveau. Sa couleur, en revanche, doit être dite : `--c-blue-zodiac`
   est écrit en clair dans la règle des titres de section, et `.on-dark` ne le
   réaffecte pas — un titre bleu nuit sur bleu nuit disparaîtrait. */
.onglets-metiers__entete {
  margin-block-end: clamp(var(--sp-6), 2.4vw, var(--sp-10));
}

.onglets-metiers__titre {
  margin: 0;
  max-width: 28ch;
  color: var(--c-blue-zodiac);
  text-wrap: balance;
}

.on-dark .onglets-metiers__titre {
  color: var(--text-strong);
}

.onglets-metiers__chapeau {
  margin: var(--sp-4) 0 0;
  max-width: 62ch;
  font-size: var(--fs-base);
  line-height: var(--lh-normal);
  color: var(--text-strong);
}

/* =========================================================================
   LA CARTE D'ONGLETS — Figma 6:198, sélectionné par Hugo le 19/08/2026.

   Une carte CLAIRE posée dans la boîte rose : un libellé en chasse fixe, une
   rangée d'onglets soulignés, puis les tableaux de bord en pilules contournées.
   Elle remplace un premier jet fait d'onglets en pastilles et de barres bordées
   pleine largeur, sur fond sombre.

   ---------------------------------------------------------------------
   ⚠️ ELLE REMET LES RÔLES DE TEXTE EN CONTEXTE CLAIR, et c'est indispensable.

   La carte vit à l'intérieur d'un `.on-dark`, qui a passé --text-strong au blanc,
   --text-muted au casper et --border-subtle à du blanc translucide. Posés sur du
   crème, les trois sont invisibles.

   Les rôles sont donc redéclarés ici, aux valeurs du contexte clair de
   _tokens.scss. C'est le miroir exact de `.on-dark`, et c'est ce qui permet à
   tout ce qui suit de continuer à ne parler qu'en rôles — aucune couleur en dur
   dans les règles, donc rien à reprendre si la boîte change de teinte.

   L'alternative — écrire --c-text-main dans chaque règle de la carte, comme le
   fait `.fiche-equipes__nom` de _texte-module.scss pour son unique titre — ne
   passait pas l'échelle : la carte a sept encres différentes.

   Les correspondances avec la maquette tombent juste, jeton par jeton :
     fond crème         #f9f9f5  = --c-base-main
     texte             #2c2a2e  = --c-text-main, 13,46 sur le crème
     filet des pilules #e0dfdc  ≈ --border-subtle  (rgba(44,42,46,0.12))
     filet des onglets #c7c6c4  ≈ --border-strong  (rgba(44,42,46,0.24))
   ========================================================================= */
.onglets-metiers__module {
  --section-bg: var(--c-base-main);
  --bg-page: var(--c-base-main);
  --text-strong: var(--c-text-main);
  --text-muted: var(--c-fiord);
  --border-subtle: rgba(44, 42, 46, 0.12);
  --border-strong: rgba(44, 42, 46, 0.24);
  padding: clamp(var(--sp-5), 2vw, var(--sp-6));
  border-radius: var(--radius-md); /* 9px, le coin circulaire de la maquette */
  background-color: var(--c-base-main);
  color: var(--text-strong);
}

/* Le libellé qui coiffe la carte. Il prend le rose du PILIER à pleine force —
   2,82 sur le crème. C'est la catégorie des libellés de service en capitales, déjà
   arbitrée deux fois sur ce site (2,90 pour l'intitulé de la carte verte du
   dictionnaire) ; à ne pas reprendre pour du texte qui porte de l'information. */
.onglets-metiers__etiquette {
  margin: 0 0 var(--sp-3);
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--c-pilier-plateforme);
}

/* ---------- La rangée d'onglets ----------
   Des onglets SOULIGNÉS, et non plus des pastilles : le filet du bas court d'un
   bout à l'autre de la rangée, et celui de l'onglet ouvert est plus épais et
   coloré. C'est le modèle Figma.

   Ils passent à la ligne plutôt que de déborder : sur écran étroit cinq métiers
   ne tiennent pas sur une ligne, et un défilement latéral cacherait les derniers
   sans le dire.

   L'onglet est un `<button>`, donc on remet à zéro ce que le navigateur y met. */
.onglets-metiers__onglets {
  display: flex;
  flex-wrap: wrap;
  margin-block-end: var(--sp-4);
}

/* ⚠️ LE FILET EST DE 2px MÊME AU REPOS, et seule sa COULEUR change à la
   sélection. Un filet de 1px qui passerait à 2px décalerait l'onglet ouvert d'un
   pixel vers le haut, et toute la rangée avec lui. C'est le même garde-fou que le
   `border-inline-start: 2px solid transparent` des onglets de `connecteurs`.

   Le filet gris est donc posé sur 2px et ne se voit qu'à moitié : `--border-strong`
   est translucide, il ne fait pas d'arête franche à cette épaisseur. */
.onglets-metiers__onglet {
  padding: var(--sp-2) var(--sp-3);
  border: 0;
  border-block-end: 2px solid var(--border-strong);
  background: none;
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  line-height: var(--lh-normal);
  color: var(--text-strong);
  cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}

.onglets-metiers__onglet:hover {
  color: var(--rose-metiers, var(--c-blue));
}

/* L'onglet sélectionné prend la couleur de la BOÎTE qui porte la carte —
   #BC4B64, soit 4,60 sur le crème. `--rose-metiers` est déclarée sur la boîte
   rose et descend jusqu'ici ; le repli sur --c-blue sert au cas où le bloc est
   posé sur un fond `sombre` ou `clair`, qui n'en déclarent aucune. */
.onglets-metiers__onglet[aria-selected=true] {
  border-block-end-color: var(--rose-metiers, var(--c-blue));
  color: var(--rose-metiers, var(--c-blue));
}

/* ---------- Les panneaux ----------
   LA CARTE GARDE LA MÊME HAUTEUR QUEL QUE SOIT L'ONGLET OUVERT. Hugo, le
   25/08/2026 : « le tableau "Équipes" doit toujours faire la même taille quel que
   soit le nombre d'objets dedans. » Sans ça la carte se rétracte en passant de
   « Finance » (quatre pilules) aux quatre onglets qui n'affichent qu'une mention,
   et toute la colonne saute d'un onglet à l'autre.

   Les cinq panneaux sont donc EMPILÉS DANS UNE SEULE CELLULE de grille : la
   rangée se dimensionne sur le plus haut, et la carte ne bouge plus. C'est le
   dispositif des panneaux de `sequence`. Une `min-height` écrite à la main aurait
   été l'autre voie, et elle serait fausse dès la première liste ajoutée — or
   quatre le seront.

   ⚠️ LES PANNEAUX REPLIÉS DOIVENT REPRENDRE UNE BOÎTE, sinon ils ne comptent pas.
   assets/js/onglets.js pose l'attribut `hidden`, dont la règle du navigateur est
   `display: none` — un élément retiré de la grille ne pèse plus rien et la hauteur
   retomberait sur le seul panneau visible. Il est donc remis en `block` et masqué
   par `visibility`, qui le retire à la fois de la vue, du clavier, de la recherche
   dans la page et de l'arbre d'accessibilité. Le repliement continue d'être posé
   par le script, pas par cette feuille.

   ⚠️ TOUT EST ENFERMÉ DANS `[data-onglets-actif]`, l'attribut que le script écrit
   une fois monté. C'est ce qui préserve le contrat du bloc : sans JavaScript aucun
   panneau ne porte `hidden`, et les empiler dans une même cellule les rendrait
   illisibles les uns sur les autres. Hors état enrichi, ils se suivent comme
   avant. */
[data-onglets-actif] .onglets-metiers__panneaux {
  display: grid;
}

[data-onglets-actif] .onglets-metiers__panneau {
  grid-area: 1/1;
}

[data-onglets-actif] .onglets-metiers__panneau[hidden] {
  display: block;
  visibility: hidden;
}

/* ---------- Les tableaux de bord ----------
   Des PILULES CONTOURNÉES qui épousent leur libellé, empilées, ferrées à gauche —
   le modèle Figma 6:198. Elles remplacent les barres pleine largeur du premier
   jet.

   ⚠️ `align-items: start` EST CE QUI LES FAIT ÉPOUSER LEUR TEXTE. Sans lui, les
   éléments d'une colonne flex s'étirent sur toute la largeur et les quatre
   pilules deviennent quatre barres de longueur égale — exactement ce que ce
   changement remplace. */
.onglets-metiers__tableaux {
  display: flex;
  flex-direction: column;
  align-items: start;
  gap: var(--sp-3);
  margin: 0;
  padding: 0;
  list-style: none;
}

.onglets-metiers__tableau {
  padding: var(--sp-2) var(--sp-4);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-full);
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  line-height: var(--lh-normal);
  color: var(--text-strong);
}

/* La mention des onglets que la maquette ne remplit pas. Elle est en chasse fixe
   pour se lire comme un état et non comme un intitulé de tableau — et elle garde
   l'encre secondaire, qui vaut ici le fiord du contexte clair (7,17 sur le crème)
   et non le casper du contexte sombre. */
.onglets-metiers__vide {
  margin: 0;
  padding: var(--sp-2) 0;
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--text-muted);
}

/* =========================================================================
   LA LISTE COMMUNE « Depuis la plateforme, chacun peut ».

   Elle est dans l'EN-TÊTE, à la suite du chapeau — pas en colonne à droite des
   onglets. Le pourquoi est en tête du gabarit.

   Trois réglages demandés par Hugo le 19/08/2026, et ils vont ensemble : son
   intitulé passe en police de TITRAGE, ses phrases au CORPS DE TEXTE, et les
   numéros 01-03 laissent la place à des FORMES DE MARQUE FLUO. L'ensemble
   n'était jusque-là que de la chasse fixe et du petit texte — il se lisait
   comme une annotation, alors que c'est l'énoncé principal de la section.
   ========================================================================= */
.onglets-metiers__commun {
  margin-block-start: clamp(var(--sp-6), 2.4vw, var(--sp-8));
}

/* ⚠️ IL NE DÉCLARE PAS SA GRAISSE NI SON CORPS À PARTIR DE ZÉRO : c'est un
   paragraphe, donc rien ne le pose pour lui, contrairement à un `hN` que
   _base.scss habille déjà. D'où les trois déclarations, qui reprennent celles
   d'un intertitre du site.

   L'encre est --text-strong et non --text-accent : cet intitulé annonce la liste,
   il n'est pas un libellé de service. Le fluo est réservé aux formes devant les
   phrases. */
.onglets-metiers__commun-titre {
  margin: 0 0 var(--sp-5);
  font-family: var(--font-serif);
  font-weight: var(--fw-semibold);
  font-size: var(--fs-md);
  line-height: var(--lh-snug);
  color: var(--text-strong);
}

.onglets-metiers__points {
  display: grid;
  gap: var(--sp-4);
  margin: 0;
  padding: 0;
  list-style: none;
}

/* `baseline` et non `center` : la forme se cale sur la ligne de base de la
   première ligne de texte, et ne glisse donc pas au milieu quand une phrase
   passe sur deux lignes. C'est le réglage de `.item__entete`, pour la même
   raison — voir le long commentaire dans _components.scss. */
.onglets-metiers__point {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: var(--sp-3);
  align-items: baseline;
  font-size: var(--fs-base);
  line-height: var(--lh-normal);
  color: var(--text-strong);
}

/* ⚠️ LE COUPLE `baseline` (sur la ligne) + `middle` (sur la forme) EST REPRIS TEL
   QUEL DE `.item__marqueur--marque`, et il ne faut pas le défaire : `middle` centre
   la forme sur la HAUTEUR D'X de la fonte, c'est-à-dire sur le milieu optique de la
   ligne, et c'est le navigateur qui fait le calcul avec les métriques réelles. Trois
   tentatives de constante écrite à la main avaient échoué avant lui ailleurs sur le
   site — le CSS n'expose pas la position des capitales dans une ligne. Le détail est
   dans _components.scss.

   L'enveloppe garde la fonte du texte : c'est de SA fonte que le navigateur tire la
   hauteur d'x, donc elle ne doit rien redéclarer. */
.onglets-metiers__marque > .onglets-metiers__forme {
  display: inline-block;
  vertical-align: middle;
}

/* Largeur FIXE et non fluide, comme `.item__forme` : c'est une pastille de
   repérage, pas du décor. À 1,25rem elle est calée sur du corps de texte, là où
   `.item__forme` (1,75rem) annonce un intitulé en serif.

   Aucune couleur ici : `forme--fluo`, posée par le gabarit, la donne déjà. */
.onglets-metiers__forme {
  --shape-w: 1.25rem;
}

/* =========================================================================
   LE MOTIF — un graphique en trame dans le coin bas de la boîte, derrière la
   carte d'onglets. Demandé par Hugo le 25/08/2026.

   L'atome est partials/motif.njk, son apparence est dans _components.scss.
   Cette feuille ne pose que le PLACEMENT et les TEINTES — même partage que
   sections/_texte-panneau.scss, qui est l'autre hôte de cet atome.
   ========================================================================= */
/* Le pied collé à l'arête basse, la taille prise sur la HAUTEUR de la boîte.
   C'est la formulation retenue sur les briques produit après trois essais
   écartés ; le pourquoi complet est dans _texte-panneau.scss, et il ne faut pas
   revenir sur ces deux points :

     • AUCUN `aspect-ratio` ici. Depuis que les viewBox de partials/motif.njk
       serrent l'encre, chaque dessin a SON rapport ; c'est `inline-size: auto`
       qui le lit. Un rapport écrit en dur rétablirait une bande vide sur un axe,
       exactement le défaut corrigé ce jour-là.

     • AUCUN PLAFOND DE LARGEUR. Il laisserait un blanc DANS LE COIN, ce qui est
       précisément ce que l'ancrage au coin sert à éviter.

   ⚠️ CE QU'IL FAUT SURVEILLER EN LE GROSSISSANT. Le motif est ancré au coin
   extérieur, donc grossir le pousse VERS LA COLONNE DE TEXTE, qui occupe la
   moitié gauche de la boîte. Du blanc posé sur sa série la plus claire ne donne
   que ~1,9 : s'il vient sous le chapeau ou sous la liste commune, c'est
   `--motif-taille` qu'il faut baisser.

   ⚠️ LE DÉFAUT EST DANS LE `var()`, JAMAIS DÉCLARÉ ICI. Une propriété posée SUR
   l'élément bat la même propriété héritée d'un ancêtre : écrite en déclaration,
   elle masquerait la valeur que la boîte pose et le réglage ci-dessous n'aurait
   aucun effet, sans la moindre alerte. Même règle que `--motif-base`. */
.onglets-metiers__motif {
  position: absolute;
  z-index: 0;
  block-size: var(--motif-taille, 55%);
  inline-size: auto;
  inset-block-end: 0;
  inset-inline-end: 0;
  pointer-events: none;
}

/* ---------------------------------------------------------------
   LES TEINTES.

   Le fond est celui de la boîte, et jamais un jeton écrit en clair : c'est lui
   que _components.scss mélange à la base pour faire RECULER les deux séries
   secondaires. La base, elle, ne descend ici que pour le rose — sur le bleu
   nuit, le repli fluo de l'atome est déjà le bon.

   ⚠️ LA TEINTE VIVE DU PILIER NE PEUT PAS SERVIR TELLE QUELLE sur le rose
   foncé : #ff5d73 sur #bc4b64 ne pèse que 1,63, à peine visible. Elle est donc
   RELEVÉE VERS LE BLANC, dans la proportion exacte déjà mesurée pour la brique
   plateforme de /produit/ — même teinte vive, même fond, donc rien à
   recalculer, et deux valeurs différentes pour un même pilier auraient été un
   défaut. Séries obtenues : 2,80 / 2,28 / 1,76. */
.onglets-metiers__boite--sombre {
  --motif-fond: var(--c-blue-dark);
}

.onglets-metiers__boite--plateforme {
  --motif-fond: var(--rose-metiers);
  --motif-base: color-mix(in srgb, var(--c-pilier-plateforme) 49%, var(--c-white));
}

/* ⚠️ RIEN EN DESSOUS DE `lg`, où les deux colonnes s'empilent. La carte d'onglets
   y prend toute la largeur et se pose en dernier : le coin bas de la boîte n'est
   plus qu'une bande de rembourrage de quelques dizaines de pixels autour d'elle,
   et le motif s'y réduirait à un éclat de dessin le long de deux arêtes. Le décor
   perd sa prémisse plutôt que de se dégrader — même parti que le tracé de
   `.chemin`, caché à la même largeur pour la même raison. */
@media (max-width: 60rem) {
  .onglets-metiers__motif {
    display: none;
  }
}
/* =========================================================================
   BLOC « face-a-face » — blocs/face-a-face.njk.
   Origine : les sections 02 et 03 de la page du connecteur IA, Figma
   141:7290 et 141:7320.

   Deux énoncés opposés côte à côte, sous UN SEUL dessin depuis le 27/08/2026 : deux
   CARTES séparées, sable et violette. Les deux modes ne se distinguent plus que par
   ce que chaque moitié porte — une phrase étiquetée (`--cellules`) ou une liste
   titrée (`--colonnes`). Le pourquoi est en tête du gabarit et plus bas.

   ⚠️ CETTE FEUILLE N'EMPLOIE QUE DES RÔLES SÉMANTIQUES — --text-strong,
   --text-muted, --text-accent, --section-bg. La moitié droite du mode
   `colonnes` est en `.on-dark` : une couleur écrite en dur y donnerait du bleu
   nuit sur bleu nuit, illisible et sans la moindre alerte. C'est la même
   contrainte que _texte-panneau.scss et _texte-module.scss.
   ========================================================================= */
.face-a-face {
  background-color: var(--bg-page);
  padding-block: var(--sp-section);
  /* Même garde-fou que `.liste-glyphes` : les boucles du tracé posé derrière la
     suite (partials/chemin.njk) débordent la liste par construction, et rien ne
     doit pouvoir créer de barre de défilement latérale. `clip` et non `hidden`,
     qui ferait de la section un conteneur de défilement. */
  overflow-x: clip;
}

/* ---------- En-tête ----------
   Hors du couple, sur le fond de page. Le titre ne déclare rien de sa police :
   _base.scss pose déjà le titrage et la taille du niveau (règle 3 de
   MAQUETTE.md). On ne touche qu'à la couleur, la marge et la mesure. */
.face-a-face__entete {
  margin-block-end: clamp(var(--sp-6), 2.4vw, var(--sp-10));
}

/* ⚠️ L'EN-TÊTE EST CENTRÉ DEPUIS LE 27/08/2026 — demandé par Hugo pour le titre de
   la section 02 de /connecteur-ia/. Le PARAGRAPHE suit, sans qu'il l'ait demandé :
   un titre centré au-dessus d'un texte ferré à gauche est un défaut, pas une nuance,
   et la seule page qui porte les deux est /exemple-blocs/.

   `text-align` ET des marges automatiques, et non l'un des deux : ce sont les
   `max-width` (34ch, 68ch) qui rendent les marges nécessaires — sans elles les deux
   boîtes resteraient ferrées à gauche dans une bande centrée — et c'est
   `text-align` qui centre les lignes DANS ces boîtes. C'est le dispositif de
   `.texte-module--centre`, aux mêmes mesures.

   ⚠️ ET C'EST LUI QUI DÉGAGE LA FORME DU COIN. Elle monte de ~95px au-dessus de la
   carte, contre 20 à 34px de marge d'en-tête : ferré à gauche, le titre se faisait
   traverser. Bridé à 34ch et centré, sa boîte commence à 136px du bord du conteneur
   au plus large (--fs-h2 plafonne à 40,8px, donc 34ch ≈ 721px dans 1088px) contre
   90px pour la forme, et à 144px pour 68px de forme à 900px. Le dégagement tient à
   toutes les largeurs — mais il tient à la MESURE : élargir le 34ch le rouvrirait.

   Bleu nuit, comme tous les titres de section du site — et non --text-strong,
   qui tire vers le noir. Les titres NE passent PAS au ton du pilier,
   contrairement aux accents : voir `.ton-page--*` dans _tokens.scss. */
.face-a-face__titre {
  margin: 0 auto;
  max-width: 34ch;
  color: var(--c-blue-zodiac);
  text-align: center;
  text-wrap: balance;
}

/* --text-strong et non --text-muted : c'est du corps de texte, et le corps de
   texte du site est toujours à l'encre forte. */
.face-a-face__texte {
  margin-block-start: var(--sp-5);
  margin-inline: auto;
  max-width: 68ch;
  color: var(--text-strong);
  text-align: center;
}

.face-a-face__texte p {
  margin: 0;
  font-size: var(--fs-base);
  line-height: var(--lh-normal);
}

.face-a-face__texte p + p {
  margin-block-start: var(--sp-4);
}

/* ---------- Le couple ----------
   Une seule colonne sous le point de rupture : les deux camps se lisent aussi
   bien l'un sous l'autre, rien dans l'un ne dépend de la hauteur de l'autre. */
/* ⚠️ LA GOUTTIÈRE EST DÉCLARÉE ICI ET NON DANS LA REQUÊTE `md`, depuis le
   27/08/2026 : elle vaut aussi quand les deux cartes s'EMPILENT. Portée par la seule
   requête, elle laissait les deux cartes arrondies se toucher sous 900px — un défaut
   que le mode `cellules` masquait, sa gouttière d'1px étant, elle, inconditionnelle. */
.face-a-face__couple {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: clamp(var(--sp-6), 2.4vw, var(--sp-10));
}

/* ---------- LE DESSIN EST LE MÊME DANS LES DEUX MODES DEPUIS LE 27/08/2026 ----------
   « Passe au même dessin », demandé par Hugo. Le mode `cellules` rendait jusque-là UN
   panneau fermé coupé en deux par une gouttière d'1px — les deux moitiés
   appartenaient visiblement au même objet, ce qui faisait lire « deux faces d'une
   même question » plutôt que « deux affirmations ». Ce panneau a disparu : les deux
   modes rendent désormais les deux mêmes cartes séparées.

   ⚠️ CE QUI EST PARTI AVEC LUI, et qu'il ne faut pas rétablir par réflexe : le
   `.cadre` autour du couple (posé par le gabarit), le fond de filet du couple, la
   gouttière d'1px et le gris --c-blue-50 de la case dépassée. Le filet du milieu
   était fait par la GOUTTIÈRE et non par une bordure de cellule, la technique de
   `module.grille` — elle est dans git à cette date si un panneau coupé en deux
   redevenait nécessaire quelque part.

   ⚠️ CE QUI SÉPARE ENCORE LES DEUX MODES N'EST DONC PLUS UN DESSIN, c'est ce que
   chaque moitié PORTE : une phrase étiquetée d'un côté (`cellules` : un `<p>` en
   chasse fixe et un `resume`), une liste titrée de l'autre (`colonnes` : un `<h3>`
   en police de titrage et des `points`). Le rang de l'étiquette reste une vraie
   différence — deux titres de même niveau pour deux phrases de dix mots feraient
   deux entrées dans le plan du document là où il n'y a qu'une opposition — donc les
   règles d'étiquette restent portées par la classe de mode, et elles seules.

   Conséquence à connaître : ce mode pourrait aujourd'hui se DÉDUIRE du contenu (des
   `points` → une liste à titrer, un `resume` seul → une phrase à étiqueter), comme
   `temoignages` déduit son carrousel de son nombre de citations. Ça n'a pas été fait
   — ce serait changer un champ documenté sans qu'on l'ait demandé.

   ---------- Le dessin commun : deux cartes ----------
   DEUX CARTES séparées : sable à gauche, violette à droite — c'est la règle 1 de
   MAQUETTE.md : sur ce site, un aplat est une boîte arrondie, jamais une bande.

   ⚠️ LA CARTE DE GAUCHE DATE DU 27/08/2026, sur un PNG annoté d'Hugo. Le camp
   dépassé était posé à même le crème jusque-là, ce qui laissait cette moitié nue
   face à la boîte colorée d'en face — un déséquilibre que les trois petites formes
   du 26/08 compensaient. La carte le règle à la racine, et le décor a changé de
   rôle : deux grandes formes passent maintenant à cheval sur son arête.

   ⚠️ LES DEUX CARTES ONT EXACTEMENT LA MÊME GÉOMÉTRIE — même rembourrage, même
   rayon, même hauteur. C'est ce qui fait que la première ligne de chaque liste tombe
   à la même hauteur d'un camp à l'autre : sans ça, l'œil ne pourrait plus comparer
   ligne à ligne. Elles la tiennent d'une seule règle, celle du panneau. */
/* ⚠️ LA COLONNE N'EST PLUS QU'UNE ENVELOPPE DEPUIS LE 27/08/2026 : elle ne porte
   ni fond, ni rayon, ni rembourrage. Tout cela est passé au PANNEAU juste en dessous,
   et ce déplacement n'est pas cosmétique — c'est lui qui rend possible le
   chevauchement des deux formes de décor. Le pourquoi complet est sur leur règle,
   plus bas ; en deux mots : à l'étape 1 d'un contexte d'empilement c'est le fond du
   contexte lui-même qui est peint, AVANT ses enfants en z négatif. Un fond resté sur
   la colonne passerait donc SOUS les formes au lieu de les couvrir.

   `display: grid` pour que le panneau prenne toute la hauteur de la rangée : les deux
   colonnes sont des éléments de grille étirés, mais leur enfant, lui, ne l'est que
   s'il est lui-même élément d'une grille. Sans ça la carte de sable s'arrêterait à
   son texte et les deux cartes n'auraient plus la même hauteur.

   `isolation: isolate` sur LES DEUX colonnes bien qu'une seule porte des formes :
   c'est le contexte d'empilement qui retient leur `z-index: -1`. Sans contexte
   intermédiaire, un z négatif remonte jusqu'au contexte racine et se range sous
   l'aplat opaque de la section, où il est simplement invisible. */
.face-a-face__colonne {
  --colonne-pad: clamp(var(--sp-6), 2.4vw, var(--sp-10));
  /* 28px, comme les autres boîtes du site. Nommé parce que les DEUX cartes de ce
     mode le lisent désormais, et qu'il n'existe toujours pas de jeton à cette
     valeur (--radius-lg vaut 16px). */
  --carte-radius: 1.75rem;
  position: relative;
  display: grid;
  isolation: isolate;
}

/* ---------- Le panneau : la carte elle-même ----------
   Il porte le fond, le rayon et le rembourrage des DEUX cartes, dans les deux modes
   depuis le 27/08/2026.

   ⚠️ --carte-ton ET NON --carte-fond : ce dernier nom est déjà pris par les cartes
   de `texte-module`. Les deux ne peuvent pas se rencontrer aujourd'hui (un face-à-face
   n'est jamais dans une entrée de `texte-module`, ni l'inverse), mais une propriété
   personnalisée s'hérite : deux blocs qui se croiseraient un jour se passeraient leur
   fond sans le moindre signal.

   ⚠️ LE FOND ARRIVE PAR --carte-ton, DONT LE DÉFAUT EST DANS LE `var()` ET NON
   DANS UNE DÉCLARATION ICI : une propriété personnalisée posée SUR l'élément bat
   celle qu'il hérite d'un ancêtre, donc un `background-color` écrit ici masquerait
   celui que la colonne définit, sans le moindre signal. C'est la règle générale des
   boutons de commande du site (voir --btn-primary-bg). */
.face-a-face__panneau {
  padding: var(--colonne-pad);
  border-radius: var(--carte-radius);
  background-color: var(--carte-ton, transparent);
}

/* ⚠️ LE CAMP DÉPASSÉ EST UNE CARTE DE SABLE DEPUIS LE 27/08/2026 — --c-base-surface,
   choisi par Hugo. Il était posé à même le crème jusque-là, et les trois petites
   formes du 26/08 étaient là pour compenser ce déséquilibre-là : le déséquilibre
   disparaît avec la carte, et le décor a changé de rôle en même temps (voir plus bas).

   Mesurés sur ce sable : le titre du camp à --text-muted donne 5,58 (contre 6,83 sur
   le crème), les points à --text-strong 11,02. La carte elle-même se détache du fond
   de page à 1,22 — assez pour dessiner son arête sans filet, là où les cartes
   blanches de `cas-clients` à 1,05 en réclament un.

   --section-bg et --bg-page suivent le fond, comme sur la boîte d'en face : un
   `.btn--secondary` posé dedans recopierait le sable et non le crème. */
.face-a-face__colonne--moins {
  --carte-ton: var(--c-base-surface);
  --section-bg: var(--c-base-surface);
  --bg-page: var(--c-base-surface);
}

/* ⚠️ LA BOÎTE EST VIOLETTE, PAS BLEU NUIT — demandé par Hugo le 19/08/2026 : le
   camp « avec Biron » prend la couleur du pilier de la page.

   ⚠️ LE VIOLET SOMBRE EST UN JETON DEPUIS LE 26/08/2026 —
   --c-pilier-connecteur-sombre, #724fd9, celui de la brique connecteur de
   /produit/. Cette boîte était restée à 90 % (#7f56ec) : la valeur était écrite ici,
   dans _texte-module.scss, dans _chaine.scss et dans _texte-panneau.scss, seule la
   dernière avait été assombrie le 25/08, et les commentaires qui se promettaient de
   « rester d'accord » n'y ont rien pu. Le calcul et son arbitrage sont dans
   _tokens.scss, en un seul endroit désormais.

   ⚠️ ET C'EST POURQUOI --text-muted PASSE AU BLANC. Règle 11 de MAQUETTE.md :
   `.on-dark` cale ses rôles sur le bleu nuit (#202b5c), bien plus sombre. Sur ce
   violet, le casper ne donne que 2,65 — aucune demi-mesure ne le sauve.

   --text-accent RESTE AU FLUO, lui, et il est le seul des trois à ne pas être
   repris. Il ne porte ici que la coche ✓, qui est DÉCORATIVE (`aria-hidden`) : ses
   4,70 sur le violet passent même le seuil du texte courant, alors qu'un signe de
   service se contenterait de moins. Le passer au blanc tuerait la seule chose qu'il
   fait — distinguer le camp allumé du camp éteint. */
.face-a-face__colonne--sombre {
  --violet-connecteur: var(--c-pilier-connecteur-sombre);
  /* Le fond et le rayon sont portés par le PANNEAU depuis le 27/08/2026, comme sur
     la carte d'en face : les deux cartes ont ainsi exactement la même géométrie,
     et la colonne reste l'enveloppe neutre dont le décor a besoin. */
  --carte-ton: var(--violet-connecteur);
  /* Le fond de la boîte devient le fond de référence, pour qu'un
     `.btn--secondary` posé dedans recopie le violet et non le crème. */
  --section-bg: var(--violet-connecteur);
  --bg-page: var(--violet-connecteur);
  --text-muted: var(--c-white);
}

@media (min-width: 56.25rem) {
  .face-a-face__couple {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}
/* ---------- Les deux formes à cheval sur la carte du camp dépassé ----------
   Demandées par Hugo le 27/08/2026, sur un PNG annoté. Elles REMPLACENT les trois
   petites formes du 26/08, et le changement de rôle est ce qu'il faut retenir : ce
   jour-là le camp dépassé était posé à même le crème et les formes bordaient cette
   moitié nue pour la tenir face à la boîte colorée d'en face. Elle est maintenant une
   CARTE de sable : il n'y a plus de déséquilibre à compenser, et le décor traverse la
   carte au lieu de l'entourer.

   La teinte vient du gabarit (`accent`, donc --text-accent) : le violet est celui de
   la page, pas une couleur écrite ici. Cette feuille ne décide que de la taille et de
   l'ancrage — le partage habituel avec partials/forme.njk.

   ⚠️ LE CHEVAUCHEMENT TIENT À UN ORDRE DE PEINTURE, PAS À UNE COTE. La moitié
   INTÉRIEURE de chaque forme passe DERRIÈRE la carte, qui l'efface ; seule la moitié
   extérieure se voit, coupée net par l'arête. C'est la construction de `temoignages`
   et de l'ancrage `tableau` de `texte-module`, et elle tient à trois pièces :

     1. la COLONNE est le contexte d'empilement (`isolation: isolate`) et ne porte
        AUCUN fond ;
     2. les formes y sont en `z-index: -1`, donc peintes à l'ÉTAPE 2 de ce contexte,
        c'est-à-dire sur le crème de la section ;
     3. le PANNEAU porte le sable : descendant de flux non positionné, son fond est
        peint à l'ÉTAPE 3, après elles, et les recouvre sans aucun `z-index` à lui.

   ⚠️ C'EST POURQUOI LE FOND NE PEUT PAS REVENIR SUR LA COLONNE. À l'étape 1 c'est le
   fond du contexte d'empilement LUI-MÊME qui est peint, avant ses enfants en z
   négatif : posé sur la colonne, le sable passerait sous les formes au lieu de les
   couvrir. Le défaut serait exactement l'inverse de l'effet cherché, et rien ne le
   signalerait. Pour la même raison les formes ne peuvent pas être enfants du panneau.

   ⚠️ ET C'EST BIEN LA COLONNE, PAS LA SECTION. Sans contexte intermédiaire, un
   `z-index: -1` remonte jusqu'au contexte racine et se range sous l'aplat opaque de la
   section (`.face-a-face` pose --bg-page) : les formes deviendraient invisibles.

   ⚠️ IL N'Y A PLUS RIEN À CALCULER CONTRE LE BORD DU TEXTE, et toute la note du
   26/08 qui s'y employait est tombée avec l'ancrage. Ce qui protège le texte est
   désormais la carte elle-même, qui efface tout ce qui passe dessous : grossir une
   forme ne peut plus la ramener sur le texte, elle ne fait qu'élargir la part visible
   à l'extérieur. C'est ce qui rend --colonne-pad libre de bouger sans rien vérifier.

   Chaque forme est CENTRÉE SUR SON POINT D'ANCRAGE par `translate: -50% -50%`. Les
   pourcentages d'un `translate` se résolvent sur la boîte de l'élément lui-même,
   donc aucune règle n'a à connaître la hauteur de la forme — qui n'a d'ailleurs pas
   de taille intrinsèque, étant un masque. Et `translate` et NON `transform` : `.forme`
   porte déjà un `transform: rotate()`, et `translate` est une propriété séparée qui
   se compose avec lui au lieu de le remplacer.

   ⚠️ CE QUI BORNE LES DEUX TAILLES, et ce n'est pas la même chose pour les deux :

     coin  la GOUTTIÈRE DE PAGE. À 34 % de la colonne elle déborde à gauche de 17 %,
           soit ~90px quand le conteneur touche son maximum, contre 96px de gouttière
           à 1280px. Ça passe à six pixels près — d'où le point de rupture `xl`
           ci-dessous, en dessous duquel elle ne se met pas sur le coin.
     pied  le REMBOURRAGE DE SECTION (--sp-section, 54 à 102px). À 32 % elle descend
           de ~87px sous la carte quand le conteneur est à son maximum. La section
           porte `overflow-x: clip`, qui ne borne que l'horizontale : la verticale
           reste `visible` et le débordement se rend.

   ⚠️ AUCUNE ROTATION, contrairement à l'habitude du site. Une rotation de 18°
   grossit l'encombrement d'un quart, et aucun des deux dégagements ci-dessus n'a ce
   quart à donner. Les deux silhouettes suffisent à les distinguer, ce que demande
   partials/forme.njk — jamais deux fois la même forme dans la même orientation côte
   à côte.

   ⚠️ ELLES NE SONT RENDUES QU'EN MODE `colonnes`, et c'est une décision d'Hugo du
   27/08/2026 — « enlève les formes sur la partie Données brutes + IA » — et non une
   contrainte. Le dessin étant devenu commun le même jour, elles ont servi les deux
   modes pendant une heure. Le décor reste la marque de la section développée en
   listes.

   ⚠️ CE QUI EST TOMBÉ AVEC CE RETRAIT, et qu'il faudra rétablir tel quel le jour où un
   face-à-face en `colonnes` déclare une `liste` : la forme du pied descend de ~87px
   sous la carte au plus large, et le titre d'une suite — qui est PLEINE LARGEUR — la
   croise à l'horizontale. La suite s'écartait alors de `min(11vw, 8.25rem)`, dans la
   requête `md` (`:has()` ne sait pas que les formes y sont en `display: none` sous ce
   point). C'est dans git au 27/08/2026. ⚠️ Un `z-index` n'y suffirait PAS : la colonne
   est positionnée, donc tout son contexte d'empilement est peint APRÈS le contenu de
   flux de la section, quel que soit son `z-index` interne. Le remède est de la place.

   ⚠️ CE QUI PROTÈGE LA FORME DU COIN D'UN TITRE DE SECTION, en revanche, est acquis :
   le titre du bloc est CENTRÉ et bridé à 34ch depuis le 27/08/2026, donc sa boîte
   commence à 136px du bord au plus large contre 90px de forme (144 contre 68 à 900px).
   Aucune instance en `colonnes` n'en déclare aujourd'hui, mais la première qui le fera
   passera sans rien à reprendre. C'est la MESURE du titre qui tient cette garantie,
   pas une cote écrite ici — voir la note de `.face-a-face__titre`. Sans en-tête, la
   forme monte dans le rembourrage de section (54 à 102px) contre ~95px : ça tient de
   justesse.

   ⚠️ RIEN SOUS `md`, où les deux colonnes s'empilent : la carte prend alors toute la
   largeur, les gouttières se réduisent à ~35px et il ne reste plus de bande libre
   autour d'elle. Le décor perd sa condition d'existence plutôt que de se dégrader —
   même arbitrage que `.chemin` et que le motif d'`onglets-metiers` au même point. */
.face-a-face__forme {
  position: absolute;
  z-index: -1;
  display: none;
}

@media (min-width: 56.25rem) {
  .face-a-face__forme {
    display: block;
  }
  /* À cheval sur l'arête HAUTE de la carte. Sous `xl` elle est rentrée vers
     l'intérieur (22 %, soit un bord gauche à 5 % de la carte) : elle ne déborde alors
     que par le haut, où le rembourrage de section lui laisse la place que la
     gouttière de page lui refuse. */
  .face-a-face__forme--coin {
    --shape-w: 34%;
    inset-block-start: 0;
    inset-inline-start: 22%;
    translate: -50% -50%;
  }
  /* À cheval sur l'arête BASSE, aux deux tiers vers la droite — décalée du coin pour
     ne pas répondre à celle du haut. Sa position horizontale ne dépend de rien : elle
     est entièrement sous la boîte de contenu. */
  .face-a-face__forme--pied {
    --shape-w: 32%;
    inset-block-start: 100%;
    inset-inline-start: 70%;
    translate: -50% -50%;
  }
}
/* La gouttière de page ne s'ouvre qu'ici, et c'est la seule des deux formes qui en
   dépende : à partir de 1280px elle passe sur le COIN de la carte, comme sur le PNG
   d'Hugo. En dessous, elle reste sur l'arête haute (voir sa règle ci-dessus). */
@media (min-width: 80rem) {
  .face-a-face__forme--coin {
    inset-inline-start: 0;
  }
}
/* ---------- L'étiquette de chaque moitié ----------
   EN MODE `cellules` : la grammaire des intitulés techniques du site — chasse
   fixe, capitales, interlettrage élargi, teinte d'accent. Mêmes réglages que
   `.item__marqueur` et que les surtitres de section, à un cran de corps près
   (voir plus bas).

   EN MODE `colonnes` : un vrai titre, voir la règle juste en dessous.

   ⚠️ IL N'Y A PLUS DE FILET DEVANT ELLE, dans aucun des deux modes, et il n'y a
   donc plus de `.face-a-face__filet` : Hugo l'a fait retirer le 19/08/2026. Ne pas
   le rétablir « pour faire comme le surtitre de `hero-page` » — ici la case porte
   déjà son fond et son contour, le filet faisait une décoration de plus.

   ⚠️ ELLE PORTE UN `id` ET IL EST LOAD-BEARING : chaque liste de points s'y
   rattache par `aria-labelledby`, ce qui est la seule chose qui rende la
   polarité (✕ contre ✓) lisible à la voix. Voir l'en-tête du gabarit. Ne pas
   la remplacer par un `<span>` sans reporter l'`id`. */
/* ⚠️ LA MISE EN FORME DE LIBELLÉ EST PORTÉE PAR `--cellules`, PAS PAR LA RÈGLE
   COMMUNE, et ce n'est pas une élégance. Posée sur `.face-a-face__etiquette` tout
   court, elle s'appliquait aussi au `<h3>` du mode `colonnes` — et il fallait
   ensuite défaire une à une la chasse fixe, la casse, l'interlettrage et la
   graisse. Défaire la graisse voulait dire réécrire `--fw-bold`, c'est-à-dire
   redéclarer la police d'un titre, ce que la règle 3 de MAQUETTE.md interdit.

   Ici le `<h3>` n'hérite que de `margin: 0` et prend tout le reste de son niveau. */
.face-a-face__etiquette {
  margin: 0;
}

/* ⚠️ --fs-xs (14px) ET NON --fs-mono (11px), grossie d'un cran à la demande d'Hugo
   le 19/08/2026. C'est le même pas, pris pour la même raison, que
   `.hero-page__eyebrow--glyphe` et que la pastille « Nouveau » : le cran juste
   au-dessus du mono de service, sans monter jusqu'à --fs-eyebrow-lg, qui est calée
   sur une serif et rend une chasse fixe en capitales envahissante.

   ⚠️ C'EST UN RÉGLAGE LOCAL, PAS LE JETON --fs-mono. Le toucher aurait grossi du
   même coup tous les surtitres du site, les marqueurs d'item, les invites des
   cartes de cas d'usage et les mentions de champ vide. Si le corps du mono doit
   bouger partout, c'est une décision à part. */
.face-a-face--cellules .face-a-face__etiquette {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--text-accent);
}

/* ---------- L'encre de l'étiquette dans le mode « cellules » ----------
   Demandé par Hugo le 19/08/2026 : le camp dépassé passe à l'encre noire, celui de
   Biron garde la teinte d'accent. C'est la même rhétorique que le duel du mode
   `colonnes` — le camp dépassé est ÉTEINT, celui de Biron est ALLUMÉ.

   ⚠️ L'EXTINCTION NE PASSE PLUS PAR LE FOND. Jusqu'au 27/08/2026 la case dépassée
   se remplissait d'un --c-blue-50 pendant que celle d'en face restait blanche, parce
   qu'une étiquette grise sur une case blanche dans un panneau blanc ne se serait
   distinguée de rien. Les deux cartes portent maintenant chacune sa couleur — sable
   et violet — donc ce fond-là n'avait plus rien à dire et il est parti avec le
   panneau. Ne pas le rétablir : il ferait une TROISIÈME couleur de fond sur deux
   cartes qui en ont déjà chacune une.

   L'encre noire est --c-text-main (#2c2a2e, 11,02 sur le sable — c'était 12,61 sur
   l'ancien gris bleuté) et non un noir pur : le site n'a pas de jeton de noir pur en
   usage. C'est l'arbitrage que le site fait partout où il lui faut du « noir ».

   ⚠️ ET L'ÉTIQUETTE DU CAMP BIRON PASSE AU FLUO SANS QU'ON L'ÉCRIVE : sa carte porte
   `.on-dark` depuis que c'est une boîte violette, donc --text-accent y vaut le fluo,
   soit 4,70 sur ce violet — au-dessus du seuil de 4,5 que réclame un libellé de 14px.
   La teinte d'accent posée telle quelle y aurait donné 1,14, du violet sur du violet.

   ⚠️ LE RENVOI À LA BOÎTE VERTE DU DICTIONNAIRE A ÉTÉ RETIRÉ LE 25/08/2026 : cette
   boîte-là est passée au vert sombre et ramène désormais ses rôles sur le BLANC. Ne
   pas le rétablir — il n'y a plus de boîte de couleur claire sur le site, et donc
   plus de deuxième exemple de cet arbitrage. */
.face-a-face--cellules .face-a-face__colonne--moins .face-a-face__etiquette {
  color: var(--c-text-main);
}

/* ---------- L'étiquette en mode « colonnes » : un titre ----------
   Demandé par Hugo le 19/08/2026 : police de titrage, plus gros, sans filet
   devant. Le gabarit la rend en `<h3>` dans ce mode, donc `_base.scss` a déjà posé
   serif, gras, interligne, interlettrage et la taille du niveau. Il ne reste ici
   que la COULEUR à décider.

   ⚠️ LES DEUX CAMPS N'ONT PAS LA MÊME ENCRE, et c'est le point de la manœuvre.
   Règle d'Hugo du 19/08/2026 : le camp dépassé se lit dans un gris de la palette
   « pour montrer que c'est pas top / dépassé ». C'est --text-muted, donc le fiord
   #4c5772 — 6,83 sur le crème, très au-dessus du seuil de 3,0 d'un grand texte.
   Face à lui, le camp Biron est à --text-strong, blanc sur le violet (4,72).

   C'est la même rhétorique que les signes ✕ / ✓ plus bas — éteint contre allumé —
   et elle passe par les mêmes rôles, donc rien n'est écrit en dur.

   ⚠️ PAS DE --c-blue-zodiac ICI, contrairement aux autres titres du site. Ces deux
   titres-là ne sont pas des titres de section : ils sont l'un des deux termes d'une
   opposition, et c'est leur écart d'encre qui la porte. */
.face-a-face--colonnes .face-a-face__colonne--plus .face-a-face__etiquette {
  color: var(--text-strong);
}

.face-a-face--colonnes .face-a-face__colonne--moins .face-a-face__etiquette {
  color: var(--text-muted);
}

/* La phrase de résumé du mode `cellules`. --text-strong : c'est du corps de
   texte, pas une légende. */
.face-a-face__resume {
  margin: var(--sp-4) 0 0;
  font-size: var(--fs-base);
  line-height: var(--lh-normal);
  color: var(--text-strong);
}

/* ---------- Les points ----------
   ⚠️ AUCUN SÉPARATEUR ENTRE LES POINTS, retiré à la demande d'Hugo le 19/08/2026.
   Il y avait ici un filet tireté, celui de la maquette (`border-dashed` de Figma
   141:7331 et suivants). C'est désormais l'espace qui sépare les lignes.

   C'est le même parti que `liste-glyphes` pour les garanties de la page produit et
   que `texte-module` pour ses entrées, tous deux dégarnis le 18/08/2026 pour la
   même raison. Ne pas le rétablir sans le lui demander.

   Le `gap` remplace donc le `padding-block` d'avant : sans filet, un rembourrage
   qui n'encadre plus rien doublait l'espace au-dessus du premier point. */
.face-a-face__points {
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
  margin: clamp(var(--sp-5), 2vw, var(--sp-8)) 0 0;
  padding: 0;
  list-style: none;
}

.face-a-face__point {
  display: flex;
  align-items: baseline;
  gap: var(--sp-3);
  font-size: var(--fs-base);
  line-height: var(--lh-normal);
  color: var(--text-strong);
}

/* ⚠️ POURQUOI LE ✕ EST À L'ENCRE DISCRÈTE ET LE ✓ À LA TEINTE D'ACCENT.

   La maquette pose le ✕ en rose (#ff5d73) et le ✓ en vert : deux couleurs
   sémantiques que le site n'a pas. Le seul rose disponible est
   --c-pilier-plateforme, et l'employer ici le ferait lire comme un accent de
   pilier — le lecteur y verrait un renvoi vers la plateforme, pas un défaut.

   Le parti retenu tient avec les rôles existants : le camp sans Biron est
   ÉTEINT (--text-muted), celui avec Biron est ALLUMÉ (--text-accent, qui vaut
   le fluo dans la boîte sombre). L'opposition se lit au premier coup d'œil, et
   rien n'est écrit en dur.

   Les signes ne sont pas alignés sur une largeur fixe : ✕ et ✓ ne font pas la
   même chasse, et chaque colonne n'emploie qu'un seul des deux — il n'y a donc
   rien à faire coïncider d'une ligne à l'autre. */
.face-a-face__signe {
  flex: none;
  font-weight: var(--fw-semibold);
}

.face-a-face__signe--moins {
  color: var(--text-muted);
}

.face-a-face__signe--plus {
  color: var(--text-accent);
}

/* =========================================================================
   LA SUITE — l'intitulé et la liste, sous le couple.

   Elle appartient à ce bloc parce que dans la maquette elle appartient à cette
   section : « Sans fiabilisation, l'IA se trompe de quatre façons » est un
   sous-titre de « Une IA branchée sur des données brutes… », pas une section à
   part. Le pourquoi complet est en tête du gabarit.

   La liste est posée À MÊME LE FOND, sans cadre — le parti de `liste-glyphes`
   pour les garanties et de `texte-module` avec `module.cadre: false`. Et elle
   emploie l'atome partagé `.item`, dont toute l'apparence vient de
   _components.scss : cette feuille ne décide que de l'empilement.
   ========================================================================= */
.face-a-face__suite {
  margin-block-start: clamp(var(--sp-10), 4vw, var(--sp-16));
}

/* Un `<h3>`, donc `_base.scss` a déjà posé serif, gras, interligne et la taille du
   niveau. On ne touche qu'à la couleur, la marge et la mesure — comme
   `.texte-module__module-titre`, dont c'est le pendant exact.

   Centré depuis le 27/08/2026, à la demande d'Hugo, comme le titre du bloc. La
   liste en dessous reste en quinconce et ferrée : c'est un titre de section au-dessus
   d'une grille, la grammaire habituelle du site. */
.face-a-face__suite-titre {
  margin: 0 auto clamp(var(--sp-6), 2.4vw, var(--sp-10));
  max-width: 40ch;
  color: var(--c-blue-zodiac);
  text-align: center;
}

/* ⚠️ IL N'Y A AUCUNE RÈGLE ICI POUR LES ENTRÉES DE LA SUITE, et c'est voulu : elles
   emploient la primitive `.quinconce` (_components.scss), le rythme des « 4 garanties »
   de la page produit — demandé par Hugo le 19/08/2026. Grille, alternance, respiration
   et point de rupture y vivent une seule fois, partagés avec `liste-glyphes`.

   ⚠️ UN PREMIER JET RANGEAIT CES ENTRÉES DEUX PAR RANGÉE, dans une grille propre à ce
   bloc. Ne pas y revenir : c'est ce qu'Hugo a fait remplacer par le quinconce le
   19/08/2026 — « les mettre comme les items des garanties ». */
/* =========================================================================
   BLOC « comparatif » — blocs/comparatif.njk.

   Origine : la section 03 de « Pourquoi Biron » (Figma 145:7809) pour le
   contenu, et le comparatif de Function Health apporté par Hugo le 20/08/2026
   pour la forme.

   Six critères confrontés à quatre approches, et la colonne de la solution
   portée par une carte colorée qui DÉBORDE du tableau en haut et en bas.

   ⚠️ LE BUT EST DE FAIRE RESSORTIR LA COLONNE, PAS LE BLOC. Cette section a
   porté une boîte bleu nuit pendant une heure le même jour, ce qui mettait le
   TABLEAU en valeur ; Hugo a redressé la cible. Les deux emphases ne pouvaient
   pas coexister — boîte sombre, panneau clair et colonne colorée font trois
   niveaux qui se concurrencent. La boîte est partie.

   L'anatomie complète et les cinq écarts assumés aux règles sont en tête du
   gabarit. Ici : les cotes.
   ========================================================================= */
.comparatif {
  background-color: var(--bg-page);
  padding-block: var(--sp-section);
  /* ---------- Les cotes que plusieurs règles partagent ----------
     Groupées ici parce qu'elles sont LIÉES : la largeur de la colonne des
     critères sert au `<th>` ET au calcul de la position de la carte, et le
     débord sert au rembourrage du plateau ET à rien d'autre — mais il doit
     rester lisible à côté des deux autres pour qu'on voie d'un coup d'œil de
     quoi la composition est faite. Éparpillées, un changement de largeur de
     colonne aurait laissé la carte à son ancienne position. */
  /* 220 des 1190px du tableau de la maquette. */
  --comparatif-critere-w: 18.5%;
  /* ---------- Le débord de la carte, EN HAUT COMME EN BAS ----------
     ⚠️ UNE SEULE VARIABLE POUR LES DEUX CÔTÉS, et c'est ce qui les garde
     symétriques. En haut, c'est la HAUTEUR MINIMALE de la rangée d'en-têtes ; en
     bas, le rembourrage du plateau. Les deux valent donc exactement la même chose,
     et régler cette ligne règle les deux.

     ⚠️ LE HAUT N'EST PAS UN REMBOURRAGE, contrairement à ce qui a été monté d'abord.
     Le plateau réservait un bandeau au-dessus du tableau et le logo y était centré ;
     mais le débord bleu VISIBLE est plus haut que ce bandeau, puisque la rangée
     d'en-têtes est elle aussi au-dessus du panneau blanc et que sa cellule phare est
     transparente. Le logo tombait donc dans le tiers haut. En faisant du débord la
     hauteur de la rangée elle-même, le débord bleu du haut EST cette rangée : centrer
     le logo dans sa cellule le centre dans le débord.

     ⚠️ OUVERT D'UN TIERS LE 16/09/2026, demandé par Hugo en même temps que le
     passage du panneau au sable : il était à `clamp(3rem, 2.1rem + 2.8vw, 4.5rem)`.
     La PENTE est recalculée et non laissée telle quelle, pour que la rampe couvre
     le même intervalle de largeurs qu'avant — plancher vers 437 px de fenêtre,
     plafond vers 1166 px. Sans ça les deux bornes se croisent ailleurs et le
     débord saute à une largeur arbitraire. Vérifié aux deux bouts : 54,3 px à
     437 px et 81,2 px à 1166 px, pour des cibles de 54,4 et 81,6.

     ⚠️ DEUX CHOSES GRANDISSENT AVEC, ET C'EST VOULU : le logo, qui en est une
     fraction (voir `.comparatif__phare-logo`), et la RANGÉE D'EN-TÊTES, dont c'est
     la hauteur minimale. Cette seconde est précisément ce qui garde les deux
     débords égaux sans qu'aucune valeur ne soit recopiée — ne pas dissocier les
     deux côtés pour récupérer de la hauteur d'en-tête. */
  --comparatif-debord: clamp(4rem, 2.8rem + 3.7vw, 6rem);
  /* L'arrondi de la CARTE, et des cartes empilées sous 60rem. 20px, le rayon des
     cartes du site (`module.cartes`, `.texte-module__conclusion`) et non les 28px
     des boîtes de section : ce sont des cartes, pas des boîtes.

     ⚠️ IL NE SERT PLUS AU PANNEAU depuis le 16/09/2026 : celui-ci est passé aux
     coins vifs de `.cadre`, et ses quatre règles de coin ont disparu. Les deux
     objets ne partagent donc plus d'angle — c'est voulu, le cadre est le plan et
     la carte est ce qui le traverse. */
  --comparatif-radius: 1.25rem;
  /* ---------- Le fond du panneau ----------
     ⚠️ UN JETON ET NON UNE VALEUR ÉCRITE DANS CHAQUE RÈGLE, parce que DEUX règles
     le lisent : les cellules du corps (qui FONT le panneau, il n'y a pas de
     wrapper) et les cartes empilées sous le point de rupture. Éparpillé, un
     changement laissait un trou de l'ancienne couleur dans la pile.

     ⚠️ LA RANGÉE DE GROUPE NE LE LIT PLUS depuis le 16/09/2026 : elle est passée
     au crème, et c'est `--bg-page` qu'elle lit — le fond de page, jamais la crème
     écrite en clair, puisque c'est bien « le sol de la page » qu'elle laisse
     traverser le panneau.

     Le pourquoi du blanc, et le sable qui a tenu quelques heures a sa place, sont
     en tête de la section « LE PANNEAU ET SES ENCRES » plus bas — c'est là que
     vivent les mesures. */
  --comparatif-panneau: var(--c-white);
  /* ---------- Le rembourrage des cellules ----------
     ⚠️ DEUX JETONS ET NON UNE VALEUR ÉCRITE DANS CHAQUE RÈGLE, parce que SIX règles
     les lisent : les en-têtes, les intitulés de ligne, les cases, et leurs trois
     équivalents sous le point de rupture. Éparpillé, un réglage laissait les
     en-têtes désalignés de leurs valeurs — c'est le rembourrage LATÉRAL qui donne
     la verticale du texte d'une colonne.

     ⚠️ SEUL LE LATÉRAL A ÉTÉ ÉLARGI LE 20/08/2026, et c'est une précision d'Hugo
     après coup. Les deux valaient --sp-5 (20px) fixes ; « tu peux augmenter les
     paddings dans les cases, ça fait un peu tchip » — puis « je pensais au padding
     latéral, pas en haut et en bas ».

     Le vertical est donc REVENU à sa valeur d'origine. C'est le bon partage : sur ce
     tableau, ce qui serrait n'était pas la hauteur des rangées — les valeurs y tiennent
     souvent sur deux lignes, ce qui les aère déjà — mais la distance du texte au filet
     vertical et aux flancs de la carte. Ne pas « harmoniser » les deux : la différence
     est mesurée sur ce contenu-là.

     ⚠️ ET C'EST LE LATÉRAL QUI EST PARTAGÉ avec la rangée d'en-têtes, ce qui donne la
     verticale du texte d'une colonne. Le vertical, lui, ne concerne que le corps. */
  --comparatif-pad-block: var(--sp-5);
  --comparatif-pad-inline: clamp(var(--sp-6), 1.2rem + 1.3vw, var(--sp-10));
}

.comparatif__inner {
  display: flex;
  flex-direction: column;
  gap: clamp(var(--sp-8), 3vw, var(--sp-12));
}

/* Le bouton de poursuite, pour l'accueil — voir `cta` dans blocs/comparatif.njk.
   Centré comme l'en-tête, dont il reprend la logique de colonne. */
.comparatif__cta {
  display: flex;
  justify-content: center;
}

/* ---------- L'en-tête ----------
   Centré, comme la référence : le titre coiffe une composition symétrique, il
   n'y a pas de colonne de texte à laquelle se ferrer. */
.comparatif__entete {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-4);
  text-align: center;
}

/* Le titre ne déclare rien de sa police : _base.scss pose déjà le titrage et la
   taille du niveau (règle 3 de MAQUETTE.md). Même bleu profond que les autres
   titres de section sur fond clair. */
.comparatif__titre {
  margin: 0;
  max-width: 34ch;
  color: var(--c-blue-zodiac);
  text-wrap: balance;
}

.comparatif__chapeau {
  margin: 0;
  max-width: 60ch;
  font-size: var(--fs-base);
  line-height: var(--lh-normal);
  color: var(--text-strong);
}

/* =========================================================================
   LE PLATEAU ET LA CARTE.

   ⚠️ LE PLATEAU N'A AUCUN REMBOURRAGE LATÉRAL, et il ne doit pas en recevoir :
   la carte est posée en absolu et sa largeur est un POURCENTAGE de la boîte de
   rembourrage du plateau, tandis que le tableau fait 100 % de sa boîte de
   contenu. Un rembourrage latéral désaccorderait les deux, et la carte ne
   tomberait plus sur sa colonne.

   ⚠️ IL N'A DE REMBOURRAGE QU'EN BAS. La carte est en `inset-block: 0`, donc elle
   se cale sur la boîte de REMBOURRAGE du plateau : le rembourrage du bas est ce
   qu'elle dépasse sous la dernière rangée. En haut, il n'y a rien à réserver — le
   débord du haut est la rangée d'en-têtes elle-même, qui est déjà hors du panneau
   blanc (celui-ci est porté par les cellules du CORPS). Sa hauteur minimale vaut le
   même `--comparatif-debord`, donc les deux débords sont égaux.
   ========================================================================= */
.comparatif__plateau {
  position: relative;
  padding-block: 0 var(--comparatif-debord);
}

.comparatif__phare {
  position: absolute;
  inset-block: 0;
  z-index: 0;
  /* La géométrie, entièrement calculée depuis les deux variables que le gabarit
     publie en ligne — `--phare-n` (le nombre de colonnes comparées) et
     `--phare-i` (le rang de la phare, à partir de 0). Rien n'est écrit en dur :
     ajouter une colonne dans le YAML déplace la carte tout seul. */
  --comparatif-col-w: calc(
    (100% - var(--comparatif-critere-w)) / var(--phare-n)
  );
  left: calc(var(--comparatif-critere-w) + var(--phare-i) * var(--comparatif-col-w));
  width: var(--comparatif-col-w);
  border-radius: var(--comparatif-radius);
  /* ⚠️ --btn-primary-bg ET NON --c-blue EN DIRECT. C'est le TROISIÈME aplat du
     site à porter du blanc, après `.btn--primary` et `.hero-page__badge`, et la
     règle 10 de MAQUETTE.md veut que les trois partagent la même variable : sur
     une page pilier, `--c-blue` sortirait un libellé blanc à 2,85 sur le rose ou
     2,12 sur l'orange. Le repli du `var()` porte le défaut — jamais une
     déclaration à part, qui masquerait la valeur posée par `.ton-page--*`. */
  background-color: var(--btn-primary-bg, var(--c-blue));
}

/* ⚠️ LA CARTE NE PORTE PLUS RIEN, et c'est le point de la correction du 20/08/2026 :
   elle a hébergé le logo dans une tête à elle, qu'il fallait aligner à la main sur la
   rangée d'en-têtes. Le logo vit maintenant DANS la cellule d'en-tête de sa colonne,
   où il se centre tout seul. La carte est redevenue un pur aplat. */
/* ---------- Le logo, EN TEXTE ----------
   ⚠️ DU TEXTE DEPUIS LE 16/09/2026, ET NON PLUS UN MASQUE SVG — Céline : « modifie
   le logo biron par le logo biron présent sur le menu, celui en police Sera avec le
   point à la fin ». C'est le même lockup que `.site-logo` (en-tête) et `.pied__logo`
   (pied) : `--font-serif`, gras, interlettrage resserré. Écrit en clair plutôt qu'en
   masque, la couleur suit `currentColor` sans qu'aucun export ne soit à refaire si
   elle change un jour — le même raisonnement que `.pied__marque`, voir footer.njk.

   Les deux anciens exports (`logos/biron.svg`, `logos/biron-large.svg`) restent dans
   le dépôt : `biron.svg` sert encore à partials/lecteur.njk. */
.comparatif__phare-logo {
  /* `inline-block` et non `block` : c'est le `text-align: center` de la cellule qui le
     centre horizontalement, et il ne le ferait pas sur un bloc. Le centrage vertical,
     lui, vient du `vertical-align: middle` de la cellule. */
  display: inline-block;
  vertical-align: middle;
  font-family: var(--font-serif);
  /* Proportionnée à l'excroissance comme l'était la largeur du masque — même
     facteur de départ (1,3), ajusté à l'œil pour un corps de texte plutôt qu'un
     logotype dessiné. */
  font-size: calc(var(--comparatif-debord) * 0.34);
  font-weight: var(--fw-bold);
  letter-spacing: var(--ls-tight);
  line-height: 1;
  color: var(--c-white);
}

/* ⚠️ LE TABLEAU PASSE AU-DESSUS DE LA CARTE, et il le faut : un élément
   positionné se peint par-dessus le contenu non positionné, donc sans ce
   `z-index` la carte recouvrirait les cellules. C'est ensuite la TRANSPARENCE
   des cellules de la colonne phare qui laisse voir la carte. */
.comparatif__tableau {
  position: relative;
  z-index: 1;
  width: 100%;
  border-collapse: collapse;
  text-align: start;
  /* `fixed` et non `auto`, et c'est ici structurel et non cosmétique : c'est ce
     qui garantit que les colonnes valent exactement ce que la carte calcule. À
     `auto`, « Coût fixe selon le volume de données » élargirait la colonne Biron
     et la carte ne tomberait plus dessus. */
  table-layout: fixed;
}

/* ---------- La rangée d'en-têtes ----------
   Elle est HORS du panneau clair, sur le crème, et tombe dans le débord haut de
   la carte. C'est la composition de la référence, et elle vient toute seule du
   fait que le panneau est porté par les cellules du CORPS. */
.comparatif__critere-entete {
  width: var(--comparatif-critere-w);
}

/* ⚠️ LES EN-TÊTES SONT CENTRÉS, LES VALEURS FERRÉES À GAUCHE, et ce partage est un
   choix d'Hugo du 20/08/2026 — « les titres sont centrés dans leurs cellules ». Les
   trois états du tableau se sont succédé dans l'ordre :

     tout centré        le modèle de la référence, dont les cellules ne contiennent
                        qu'une coche ou deux mots.
     tout à gauche      « je pense que tous les textes doivent être ferrés à gauche » —
                        et c'est juste pour les VALEURS : nos cellules portent des
                        phrases, et une phrase centrée qui se replie sur deux lignes
                        donne un bord gauche en dents de scie.
     en-têtes centrés   retenu. Un intitulé de colonne est un mot ou trois : il n'a
                        pas de bord gauche à tenir, et centré il coiffe sa colonne
                        entière au lieu d'en désigner le bord.

   ⚠️ LE LOGO SUIT CES RÈGLES-CI, et c'est tout : il est le contenu de la cellule
   d'en-tête de sa colonne, donc le `text-align: center` et le
   `vertical-align: middle` ci-dessous le centrent sur les deux axes. Il a été ferré à
   gauche le temps que le tableau entier l'était, et il a vécu un temps dans la carte
   avec une tête à lui — voir `.comparatif__phare-logo` pour pourquoi il n'y est
   plus. */
.comparatif__col {
  /* ⚠️ C'EST CETTE HAUTEUR QUI FAIT LE DÉBORD DU HAUT. Sur une cellule de tableau,
     `height` agit comme un MINIMUM : la rangée d'en-têtes vaut donc au moins le
     débord, et exactement le débord tant que ses intitulés tiennent dedans. C'est ce
     qui rend les deux débords égaux sans qu'aucune valeur ne soit recopiée. */
  height: var(--comparatif-debord);
  /* Le rembourrage vertical reste court, et il ne suit PAS celui des cases : c'est la
     HAUTEUR de la rangée qui fait le débord, pas le rembourrage de ses cellules. Le
     latéral, en revanche, est bien celui des cases — sinon l'intitulé ne tomberait pas
     sur la verticale de ses valeurs. */
  padding: var(--sp-2) var(--comparatif-pad-inline);
  font-family: var(--font-titre);
  font-size: var(--fs-md);
  font-weight: var(--fw-bold);
  line-height: var(--lh-tight);
  color: var(--c-blue-zodiac);
  text-align: center;
  vertical-align: middle;
}

/* L'en-tête de la colonne phare n'affiche pas son libellé — il est en
   `visually-hidden` dans le gabarit — mais il porte le LOGO. Il garde donc le
   rembourrage des autres en-têtes, et ce sont le `text-align: center` et le
   `vertical-align: middle` de `.comparatif__col` qui centrent le logo sur les deux
   axes. Aucune règle propre n'est nécessaire : la cellule EST le débord bleu du haut,
   donc centrer dans la cellule centre dans le débord. */
/* =========================================================================
   LE PANNEAU ET SES ENCRES — PORTÉ PAR LES CELLULES DU CORPS.

   ⚠️ CE N'EST PAS UN `.cadre`, et c'est un écart assumé aux règles 2 et 13 —
   borné à ce bloc. Un wrapper aurait mis la rangée d'en-têtes DANS le panneau,
   n'aurait pas pu être interrompu par la colonne phare, et aurait empêché la
   carte de le dépasser. Les trois sont le motif de la référence.

   ---------------------------------------------------------------------
   ⚠️ LE PANNEAU EST BLANC, ET IL EST PASSÉ PAR LE SABLE LE 16/09/2026 AVANT DE
   REVENIR. Les trois états, dans l'ordre, le même jour pour les deux derniers :

     blanc   choix d'Hugo du 20/08/2026 — « je pense qu'elle peut être plus proche
             de base main, ou même être blanc ». Le blanc est le fond des panneaux
             du site (règle 2).
     sable   16/09/2026 — « on distingue pas très bien le blanc sur la crème ».
     blanc   16/09/2026, quelques heures plus tard — « en fait je veux garder les
             tableaux blancs ». C'est l'état actuel.

   ⚠️ ET LE BLANC NE SE SUFFIT PAS À LUI-MÊME : sur le crème #f9f9f5 il ne contraste
   qu'à 1,06, donc l'aplat ne dessine pas son arête. C'est exactement le seuil pour
   lequel le site rend le filet OBLIGATOIRE — _quotidien.scss le pose pour les quatre
   cartes de « Votre quotidien », cinq sections plus haut sur l'accueil : « le sable
   se détache du crème à 1,22, assez pour dessiner son arête seul, là où les cartes
   BLANCHES de `cas-clients` à 1,05 en réclament un ».

   Le panneau a vécu sans filet du 20/08 au 16/09/2026, en écart assumé à ce seuil.
   Il en a un depuis — voir « LE CONTOUR DU CADRE » plus bas — et la règle du site
   s'applique donc ici telle quelle : ce qui fait lire le panneau est son CONTOUR,
   pas sa couleur. C'est aussi ce qui a rendu la question du sable sans objet.

   ---------------------------------------------------------------------
   LE CHIFFRAGE DU 16/09/2026, gardé parce qu'il ferme une porte. Le commentaire
   d'alors prescrivait, si le panneau devait redevenir une surface, « un mélange de
   --c-base-surface dans --c-base-main, pas le retour au sable plein ». Mesuré :

     blanc #ffffff              1,06   l'état actuel
     30 % de sable              1,06
     50 % de sable              1,10
     sable plein #e8e2d7        1,22   le seuil du site

   Deux surfaces presque blanches ne se séparent pas par la couleur : la voie
   moyenne est un cul-de-sac, et le seul cran qui change quelque chose est le sable
   PLEIN — essayé, puis écarté par Hugo. NE PAS RETENTER LA VOIE MOYENNE, et si le
   sujet revient, c'est un arbitrage à lui reposer, pas une cote à régler.

   ---------------------------------------------------------------------
   LES DEUX ENCRES, SUR LE BLANC :

     --text-muted    #4c5772   les valeurs    7,21
     --c-blue-zodiac #0f1c3f   les critères  16,69

   (Sur le sable elles donnaient 5,59 et 12,95 — les deux tenaient aussi, donc la
   couleur du panneau ne contraint pas les encres dans un sens ni dans l'autre.)

   ⚠️ --section-bg ET --bg-page NE SONT PAS RÉAFFECTÉS sur les cellules, et c'est
   sans objet tant que le panneau est blanc : `--bg-surface` vaut déjà le blanc.
   Ça redeviendrait une question le jour où le panneau reprendrait une teinte —
   et le piège est que le panneau n'étant pas un wrapper, il faudrait les poser sur
   CHAQUE cellule puis les DÉFAIRE sur les cellules phares, qui sont transparentes
   au-dessus du bleu.
   ⚠️ AUCUN FILET VERTICAL, tranché par Hugo le 20/08/2026 : la carte délimite
   déjà sa colonne, et ce qu'on compare est une LIGNE à la fois. Seules les
   rangées portent un filet.
   ========================================================================= */
.comparatif__critere,
.comparatif__cellule {
  background-color: var(--comparatif-panneau);
  /* ⚠️ LE FILET DU SITE, ET C'EST LA CONVENTION QU'ON REJOINT — pas une teinte
     choisie ici. `editions` empile ses rangées DANS un cadre et leur donne
     exactement ce filet (`border-block-end: var(--filet-panneau)`), la gouttière
     de `module.grille` et de `reperes` se peint de sa teinte : à l'intérieur d'un
     cadre, les divisions internes portent le MÊME filet que le cadre.

     Il a été noir jusqu'au 16/09/2026 — `--border-subtle` (12 %), puis
     `--border-strong` (24 %) l'après-midi même. Le bleu les supersède :

       noir 12 %                  1,26 sur le blanc
       noir 24 %                  1,60
       --filet-panneau  #a7b0df   2,12   l'état actuel

     ⚠️ IL CHANGE DE TEINTE EN TRAVERSANT LA CARTE, et seulement de teinte :
     `.comparatif__cellule--phare` l'assombrit, l'épaisseur et le style restant
     ceux d'ici. Le raisonnement — pourquoi un filet plus CLAIR ne convient pas sur
     un aplat saturé, quel que soit son contraste — est en tête de cette règle-là. */
  border-block-start: var(--filet-panneau);
  /* ⚠️ CENTRÉ EN HAUTEUR, demandé par Hugo le 20/08/2026 — « tous les textes doivent
     être centrés en hauteur ». C'était `top`, hérité de la mise en page en colonnes de
     la maquette : sur une rangée dont une cellule tient deux lignes et sa voisine une
     seule, la voisine se collait en haut et la rangée paraissait décrochée. */
  vertical-align: middle;
}

/* =========================================================================
   LE CONTOUR DU CADRE — demandé par Hugo le 16/09/2026 : « mettre la partie
   blanche du tableau dans une boîte à coins carrés avec le liseret bleu ».

   C'est le retour à la règle 2 : coins vifs, filet bleu éclairci, fond blanc —
   l'apparence de `.cadre`. Le bloc listait « PAS de `.cadre` » parmi ses écarts
   assumés ; il ne reste de cet écart que la MÉCANIQUE.

   ⚠️ CE N'EST TOUJOURS PAS LA CLASSE `.cadre`, ET ÇA NE PEUT PAS L'ÊTRE : le
   panneau n'est pas un élément, ce sont les CELLULES DU CORPS qui le portent. Il
   n'y a donc aucun nœud sur lequel poser la classe, et un wrapper coûterait les
   trois choses que l'en-tête du gabarit énumère (la rangée d'en-têtes resterait
   dedans, le panneau ne pourrait plus être interrompu par la colonne phare, la
   carte ne pourrait plus le dépasser). On lit donc son JETON, `--filet-panneau`,
   et on reproduit ses coins vifs — même rapport que `_article.scss` avec ce
   bloc-ci, dont il emprunte la technique et non les classes.

   Le contour se compose donc en quatre morceaux posés sur les cellules
   d'extrémité. Le HAUT n'a pas de règle à lui : c'est le filet de rangée de la
   première rangée, qui ne se supprime plus (il le faisait tant qu'il n'avait
   « rien à séparer » — maintenant il EST l'arête du cadre).

   ⚠️ ET LES QUATRE RÈGLES DE COIN ONT DISPARU. Elles arrondissaient le panneau à
   `--comparatif-radius` sur les cellules d'extrémité ; les coins sont vifs
   désormais. Ce jeton reste employé par la CARTE et par les cartes empilées en
   étroit — il n'a donc pas disparu, il ne sert simplement plus au panneau.

   ⚠️ `.comparatif__groupe` N'EST DANS AUCUNE DES TROIS LISTES, ET C'EST L'INVERSE
   DE CE QUI ÉTAIT ÉCRIT ICI JUSQU'AU 16/09/2026. Il y figurait parce qu'il occupe
   la rangée entière par `colspan` et portait donc à lui seul les deux bords
   latéraux de sa rangée. Depuis que l'intertitre est posé sur le CRÈME (voir « La
   rangée de groupe » plus bas), il n'appartient plus au panneau : il le COUPE. Lui
   laisser ses bords latéraux aurait dressé deux traits verticaux de part et d'autre
   d'un titre posé à nu — exactement le défaut que la pile décrivait déjà en étroit.

   ⚠️ CONSÉQUENCE : LE PANNEAU EST EN PLUSIEURS MORCEAUX, un par famille, et chaque
   morceau se ferme tout seul. En haut par le filet de rangée de sa première rangée,
   en bas par le filet HAUT de l'intertitre suivant — ou, pour le dernier morceau,
   par la règle de bord bas ci-dessous. C'est pour ça que l'intertitre garde un filet
   en haut alors qu'il n'a plus de fond de panneau.
   ========================================================================= */
/* Le bord gauche, sur la première cellule de chaque rangée. */
.comparatif__critere {
  border-inline-start: var(--filet-panneau);
}

/* Le bord droit, sur la dernière. */
.comparatif__cellule:last-child {
  border-inline-end: var(--filet-panneau);
}

/* Le bord bas, sur la dernière rangée. */
.comparatif__ligne:last-child .comparatif__critere,
.comparatif__ligne:last-child .comparatif__cellule {
  border-block-end: var(--filet-panneau);
}

/* ⚠️ FERRÉE À GAUCHE, ÉCRIT EXPLICITEMENT, demandé par Hugo le 20/08/2026 — « dans
   la 1ère colonne, les textes sont ferrés à gauche ». C'était déjà le cas par
   héritage du `text-align: start` du tableau, mais les cellules comparées, elles,
   sont centrées : la déclaration est ici pour que la première colonne ne les suive
   pas le jour où quelqu'un centrerait le tableau entier. */
.comparatif__critere {
  padding: var(--comparatif-pad-block) var(--comparatif-pad-inline);
  font-size: var(--fs-base);
  font-weight: var(--fw-medium);
  line-height: var(--lh-normal);
  color: var(--c-blue-zodiac);
  text-align: start;
}

/* ⚠️ --fs-sm ET NON --fs-base, demandé par Hugo le 20/08/2026 — « la police des cases
   un peu plus petit ». C'est un seul cran : 15px fixes contre les 16→18 du corps de
   texte du site.

   ⚠️ ET SEULES LES CASES COMPARÉES DESCENDENT, pas la colonne des critères, qui reste
   au corps de texte. Ce n'est pas un oubli : le critère est l'INTITULÉ de sa rangée, et
   l'écart d'un cran est ce qui le distingue des valeurs qu'il coiffe. La référence les
   met au même corps, mais ses valeurs sont des coches — les nôtres sont des phrases, et
   six phrases au même corps que leur intitulé se lisent comme une bouillie.

   ⚠️ `--fs-sm` est nommé pour la navigation, et c'est le seul cran entre `--fs-base` et
   `--fs-xs` (14px, réservé aux légendes). L'employer ici est un léger étirement du nom ;
   inventer un jeton pour un cran qui existe déjà en aurait été un plus grand. */
.comparatif__cellule {
  padding: var(--comparatif-pad-block) var(--comparatif-pad-inline);
  font-size: var(--fs-sm);
  line-height: var(--lh-normal);
  color: var(--text-muted);
  /* ⚠️ LE DÉFAUT EST DANS LE `var()`, PAS DANS UNE DÉCLARATION À CÔTÉ, et c'est ce
     qui laisse un ancêtre le reprendre : une propriété personnalisée posée SUR la
     cellule battrait celle que le tableau lui transmet, et le modificateur
     `--centre` ci-dessous n'aurait aucun effet sans que rien ne le signale. */
  text-align: var(--comparatif-cellule-align, start);
}

/* ---------- Les valeurs centrées ----------
   Demandé par Hugo le 16/09/2026 pour le tableau de /tarifs/ : « le contenu des
   colonnes, je veux bien que tu les centres ». C'est un MODIFICATEUR et non le
   nouveau défaut du bloc, parce que l'arbitrage du 20/08/2026 ci-dessus reste juste
   là où il a été pris : les cellules de « Pourquoi Biron » portent des PHRASES, et
   une phrase centrée qui se replie donne un bord gauche en dents de scie. Celles de
   /tarifs/ portent des valeurs — un nombre, un mot, « illimités » — donc le cas de
   la référence, où tout est centré.

   ⚠️ LES INTITULÉS DE RANGÉE NE SUIVENT PAS, et c'est explicitement demandé : la
   première colonne reste ferrée à gauche. Le modificateur ne touche que
   `.comparatif__cellule`.

   ⚠️ AUCUNE REQUÊTE DE MÉDIA ICI, ET C'EST VOULU. En pile, la cellule devient une
   RANGÉE « intitulé / valeur » et le bloc étroit lui redonne `text-align: start` en
   dur, sur le même sélecteur et plus bas dans le fichier : à spécificité égale, la
   dernière écrite gagne, donc l'empilement se défend tout seul. Écrire le centrage
   dans un `mq-up("lg")` aurait au contraire demandé une seconde règle d'annulation —
   et les deux mixins étant inclusives, à 960px exactement les deux se seraient
   appliquées, le sélecteur du modificateur l'emportant sur l'empilement. */
.comparatif__tableau--centre {
  --comparatif-cellule-align: center;
}

/* ---------- Les cellules de la colonne phare ----------
   ⚠️ FOND TRANSPARENT, et c'est par elles que la carte se voit. Une couleur
   posée ici, même la même que la carte, casserait le débord : la carte n'aurait
   plus rien à dépasser.

   Le filet de rangée, lui, continue à travers la carte, et il CHANGE DE TEINTE le
   temps de la traversée. C'est le détail qui fait lire les rangées d'un bout à
   l'autre.

   ⚠️ IL S'ASSOMBRIT, ET LE CRITÈRE N'EST PAS LA VISIBILITÉ MAIS LA DIRECTION.
   C'est l'enseignement de la journée du 16/09/2026, où cette règle a pris trois
   formes :

     blanc voilé    tant que les filets du corps étaient NOIRS, ils disparaissaient
                    sur l'aplat ; on les repeignait en blanc (0,28 puis 0,45).
     rien du tout   les filets du corps passent au bleu `--filet-panneau`, qui donne
                    2,76 sur la carte — parfaitement visible, donc le cas spécial a
                    été retiré.
     bleu foncé     Hugo : « ça fait un effet désagréable à l'œil ». Et il avait
                    raison sur un point que le contraste ne dit pas.

   ⚠️ SUR UN APLAT SATURÉ, UN FILET PLUS CLAIR SE LIT COMME UNE RAYURE, pas comme
   une division — quelle que soit sa valeur de contraste. Une division se creuse,
   elle ne brille pas. Les 2,76 du filet clair étaient d'ailleurs PLUS forts que
   les 2,12 qu'il vaut sur le panneau blanc : le défaut n'était pas un manque, il
   était un excès, et dans le mauvais sens.

   La teinte retenue est donc la carte assombrie vers `--c-blue-zodiac`, à un taux
   choisi pour rendre au filet LE POIDS QU'IL A PARTOUT AILLEURS dans le tableau :

      0 %  (le filet clair)   2,76   l'état écarté
     50 %                     1,74
     65 %                     2,05
     70 %  #1d2e6d            2,16   retenu — 2,12 sur le panneau blanc
     100 % (zodiac plein)     2,85

   ⚠️ ET LE TAUX EST À REMESURER SUR UNE PAGE PILIER, le jour où il y en aura une
   qui porte ce bloc — aucune des quatre ne réassigne `--c-blue` aujourd'hui.
   « Assombrir » affaiblit forcément un aplat déjà sombre : à 70 %, la carte de
   `.ton-page--plateforme` (déjà assombrie à 72 % pour porter du blanc) ne
   laisserait qu'environ 1,5. C'est le taux qu'il faudrait baisser, pas la
   méthode. */
.comparatif__cellule--phare {
  background-color: transparent;
  color: var(--c-white);
  font-weight: var(--fw-medium);
  /* Le filet de rangée s'assombrit le temps de la traversée — voir l'en-tête
     ci-dessus pour le pourquoi. Seule la COULEUR est reprise : l'épaisseur et le
     style viennent du filet du corps, donc il n'y a rien à tenir en accord.

     ⚠️ DÉRIVÉ DE LA CARTE ET NON ÉCRIT EN CLAIR, pour la raison de la règle 10 :
     la carte lit `--btn-primary-bg`, que `.ton-page--*` réaffecte. Un hexadécimal
     figé sortirait un filet bleu sur un aplat rose ou orange. */
  border-block-start-color: color-mix(in srgb, var(--c-blue-zodiac) 70%, var(--btn-primary-bg, var(--c-blue)));
}

/* ---------- L'INTERRUPTION DU CONTOUR ----------
   ⚠️ C'EST ICI QUE LE CADRE S'OUVRE, et c'est un arbitrage d'Hugo du 16/09/2026
   contre un contour continu qui aurait traversé l'aplat. Ce n'est PAS une
   contrainte technique : un filet se lit très bien sur la carte, les filets de
   rangée y passent tous les jours (voir `.comparatif__cellule--phare`, qui les
   assombrit pour l'occasion). C'est le principe du bloc — « le panneau est
   INTERROMPU par la colonne phare » est l'une des trois choses que la construction
   sans wrapper achète. Le cadre est le PLAN, la carte est l'objet posé dessus et
   qui le traverse.

   ⚠️ CONSÉQUENCE SI ON REVENAIT SUR CE CHOIX : le contour hériterait ici du filet
   ASSOMBRI, pas du bleu clair — c'est la même propriété sur la même cellule. Il n'y
   aurait donc rien de plus à écrire, mais l'arête du cadre changerait de teinte sur
   la largeur de la carte.

   Seules les arêtes HAUTE et BASSE sont concernées : la colonne phare n'est ni la
   première ni la dernière, donc les deux flancs du cadre ne la rencontrent jamais.

   ⚠️ ET LES FILETS DE RANGÉE, EUX, CONTINUENT À TRAVERS LA CARTE — ce sont les
   divisions internes, pas le contour. Seules les deux rangées d'extrémité sont
   visées ici ; c'est ce qui fait lire les rangées d'un bout à l'autre pendant que
   le cadre, lui, s'ouvre.

   ⚠️ ÉCRITES APRÈS LES RÈGLES DE CONTOUR, ET C'EST OBLIGATOIRE : à spécificité
   égale (0,3,0 des deux côtés), c'est la dernière écrite qui gagne. Plus haut dans
   le fichier, elles seraient battues et le cadre se refermerait sur la carte. */
.comparatif__ligne:first-child .comparatif__cellule--phare {
  border-block-start: 0;
}

.comparatif__ligne:last-child .comparatif__cellule--phare {
  border-block-end: 0;
}

/* ---------- Le libellé répété dans chaque cellule ----------
   Il ne sert QUE sous le point de rupture, où le tableau s'écroule en cartes.
   Au large il disparaît de l'affichage ET de l'arbre d'accessibilité — c'est
   tout l'intérêt de `display: none` par rapport à un masquage visuel : les
   `<th scope="col">` sont alors la seule source d'intitulé de colonne, et rien
   n'est annoncé deux fois.

   Le raisonnement complet, avec les deux routes écartées (`content: attr()` que
   Firefox n'expose pas, et les rôles ARIA réaffirmés qui doublent l'annonce),
   est dans blocs/comparatif.njk. */
.comparatif__label {
  display: none;
}

/* =========================================================================
   Sous 60rem — UNE CARTE PAR CRITÈRE.

   Tranché par Hugo le 20/08/2026, contre le défilement horizontal : à cinq
   colonnes, un tableau qui défile latéralement demande au lecteur de retenir la
   ligne pendant qu'il cherche la colonne.

   ⚠️ LA CARTE DÉBORDANTE DISPARAÎT ICI, et il n'y a pas d'alternative : sa
   géométrie est une colonne du tableau, et il n'y a plus de colonnes. C'est la
   RANGÉE de la solution, dans chaque carte, qui reprend l'aplat — et le repère
   survit donc à la bascule.

   La bascule met le tableau en blocs. Elle lui retire ses sémantiques de tableau
   — c'est le comportement des navigateurs sur `display: block` — et c'est ici
   SANS CONSÉQUENCE : chaque cellule porte désormais son intitulé de colonne en
   clair dans le DOM, et l'ensemble se lit linéairement « Mise en service ·
   Biron, quelques semaines · Recruter en interne, 3 à 6 mois… ». Il n'y a plus
   de croisement à annoncer, donc plus rien à rattacher.

   ⚠️ NE PAS « RÉPARER » ÇA PAR DES RÔLES ARIA. Les remettre ferait annoncer
   l'intitulé de colonne deux fois. Voir blocs/comparatif.njk.
   ========================================================================= */
/* =========================================================================
   LES TROIS AJOUTS DE /tarifs/ — 10/09/2026.

   Le comparatif servait jusque-là à opposer des APPROCHES : six lignes, quatre
   colonnes, une colonne phare. La page des tarifs lui demande de comparer
   vingt et un critères entre trois formules, ce qui a fait apparaître trois
   besoins qu'aucun des cinq emplois précédents n'avait.
   ========================================================================= */
/* ---------- Le sous-titre d'une colonne ----------
   Le prix, sous le nom de la formule. Il est DANS la cellule d'en-tête — voir le
   gabarit — d'où le passage en bloc pour qu'il prenne sa propre ligne.

   Il ne prend PAS la police de titrage de l'intitulé qu'il suit : c'est une
   qualification, pas un second titre. Règle 3 de MAQUETTE.md — l'intitulé
   déclare sa police une fois, et ce qui l'accompagne s'en écarte franchement. */
.comparatif__col-note {
  display: block;
  margin-block-start: var(--sp-1);
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  font-weight: var(--fw-regular);
  letter-spacing: var(--ls-wide);
  line-height: var(--lh-normal);
  color: var(--text-muted);
  text-transform: none;
}

/* ---------- La rangée de groupe ----------
   Un intertitre dans le tableau, sur toute sa largeur.

   ⚠️ ELLE EST POSÉE SUR LE CRÈME, ET C'EST CE QUI A CHANGÉ LE 16/09/2026 —
   demandé par Hugo sur le tableau de /tarifs/, le seul qui pose des intertitres :
   « sur fond crème sans liseret sur les côtés ». Elle portait jusque-là le fond du
   panneau comme les autres cellules, et elle en portait les deux bords latéraux.

   ⚠️ LE TROU DE CRÈME EN TRAVERS DU TABLEAU EST DONC VOULU, alors que l'en-tête de
   ce fichier l'annonçait comme un défaut. C'est le même objet lu autrement :
   l'intertitre n'est plus une rangée du panneau qui se distinguerait des autres,
   c'est le FOND DE PAGE qui traverse et sépare deux morceaux de panneau. La
   composition rejoint celle de la pile en étroit, où l'intertitre sort déjà des
   cartes pour se poser sur le crème — une seule idée aux deux largeurs.

   ⚠️ IL TRAVERSE AUSSI LA CARTE PHARE, qui est derrière : la bande de crème la
   coupe en autant de tronçons que de familles. C'était déjà le cas avec le fond
   blanc — la rangée occupe toute la largeur par `colspan` et n'a pas de cellule
   phare transparente — donc seule la teinte de la coupure change.

   ⚠️ ET SON FILET DU HAUT EST REVENU À L'ÉPAISSEUR ORDINAIRE. Il était à 2px pour
   faire lire la coupure ; c'est la bande de crème qui la fait désormais, et ce
   filet a changé d'emploi : il FERME LE MORCEAU DE PANNEAU au-dessus de lui, donc
   il doit valoir exactement le filet du cadre. Une distinction qui n'a plus rien à
   distinguer s'en va plutôt que de s'attarder.

   ⚠️ SAUF SUR LA PREMIÈRE RANGÉE, où il n'y a rien à fermer : le tableau ouvre sur
   un intertitre, et au-dessus il n'y a que la rangée d'en-têtes, elle aussi sur le
   crème. Le filet y flotterait entre deux surfaces de même teinte. */
/* ⚠️ LE REMBOURRAGE EST SYMÉTRIQUE, ET C'EST LA COTE DE LA MAQUETTE. Il était
   d'abord dissymétrique — 2 rem en haut, 0,75 en bas — pour détacher la famille
   de la rangée précédente. Mais le filet appuyé juste au-dessus fait déjà cette
   coupure, et les 10 px supplémentaires par intertitre coûtaient 50 px sur les
   cinq familles du tableau de /tarifs/ sans que la lecture y gagne. */
.comparatif__groupe {
  padding: var(--sp-5) var(--comparatif-pad-inline);
  background-color: var(--bg-page);
  border-block-start: var(--filet-panneau);
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--text-muted);
  text-align: start;
}

.comparatif__ligne:first-child .comparatif__groupe {
  border-block-start: 0;
}

/* ---------- L'aide d'un critère ----------
   Un « i » qui ouvre une définition, au survol ET au clavier. Aucun script :
   voir la note dans le gabarit.

   L'enveloppe porte le repère de position de la bulle. `inline-flex` et non
   `inline-block` : le « i » doit s'aligner sur la ligne de base du texte qui le
   précède, pas sur son propre bloc. */
.comparatif__aide {
  position: relative;
  display: inline-flex;
  vertical-align: baseline;
  margin-inline-start: var(--sp-2);
}

/* ⚠️ 1,375rem DE CÔTÉ, ET C'EST UN PLANCHER ASSUMÉ EN DESSOUS DE LA CIBLE DE
   24px. Le « i » se pose au fil d'un intitulé de tableau : à 24px il ferait une
   pastille plus haute que la ligne qu'il annote. Ce qu'il ouvre n'étant JAMAIS
   nécessaire à la lecture — c'est la règle écrite dans le gabarit — le manquer
   ne coûte rien. Un contrôle dont dépendrait une information ne pourrait pas
   se le permettre. */
.comparatif__aide-declencheur {
  inline-size: 1.375rem;
  block-size: 1.375rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  line-height: 1;
  color: var(--text-muted);
  background-color: transparent;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-full);
  cursor: help;
}

.comparatif__aide-declencheur:hover,
.comparatif__aide-declencheur:focus-visible {
  color: var(--c-white);
  background-color: var(--text-accent);
  border-color: var(--text-accent);
}

/* La bulle. `visibility` et non `display` : la propriété s'anime, et surtout
   elle laisse la bulle mesurable — une bulle en `display: none` n'a pas de
   dimensions au moment où elle apparaît, donc pas de position juste.

   ⚠️ ELLE EST CENTRÉE SUR LE « i » ET BORNÉE À 22rem, mais rien ne l'empêche de
   déborder du tableau près du bord droit de l'écran. C'est le prix d'une bulle
   sans script : la replacer demanderait de mesurer la fenêtre. Le tableau étant
   dans un conteneur qui défile, le débord se voit sans rien casser. */
.comparatif__aide-bulle {
  position: absolute;
  inset-block-end: calc(100% + var(--sp-2));
  inset-inline-start: 50%;
  translate: -50% 0;
  z-index: 3;
  inline-size: max-content;
  max-inline-size: 22rem;
  padding: var(--sp-3) var(--sp-4);
  font-family: var(--font-sans);
  font-size: var(--fs-xs);
  font-weight: var(--fw-regular);
  letter-spacing: normal;
  line-height: var(--lh-normal);
  text-transform: none;
  text-align: start;
  color: var(--text-on-inverse);
  background-color: var(--c-blue-zodiac);
  border-radius: var(--radius-sm);
  visibility: hidden;
  opacity: 0;
}

/* ⚠️ LE `:focus-within` PORTE SUR L'ENVELOPPE ET NON SUR LE BOUTON, et c'est ce
   qui garde la bulle ouverte : au clavier, le focus est sur le bouton, qui est
   DANS l'enveloppe. Écrit sur le bouton seul, le sélecteur aurait marché aussi —
   mais il aurait fallu le doubler pour le survol de la bulle elle-même. */
.comparatif__aide:hover .comparatif__aide-bulle,
.comparatif__aide:focus-within .comparatif__aide-bulle {
  visibility: visible;
  opacity: 1;
}

@media (max-width: 60rem) {
  /* Plus de débord à réserver : la carte n'existe plus. */
  .comparatif__plateau {
    padding-block: 0;
  }
  .comparatif__phare {
    display: none;
  }
  /* La rangée d'en-têtes n'a plus de colonnes à coiffer. `display: none` la
     retire de l'arbre d'accessibilité en même temps que de l'écran, ce qui est
     voulu : ce sont les libellés des cellules qui prennent le relais, et deux
     sources se contrediraient. */
  .comparatif__tableau thead {
    display: none;
  }
  .comparatif__tableau,
  .comparatif__tableau tbody,
  .comparatif__ligne,
  .comparatif__critere,
  .comparatif__cellule {
    display: block;
    width: auto;
  }
  /* Une carte par critère. Le fond du panneau devient celui de la carte entière,
     et l'arrondi passe des cellules d'extrémité à la rangée.

     ⚠️ AUCUN FILET NON PLUS ICI, et c'est le même écart assumé qu'au large : le
     blanc ne se détache du crème qu'à 1,06, donc la carte ne dessine pas son
     arête — ce qui la sépare de sa voisine, c'est la gouttière. Le site impose
     pourtant un filet aux cartes BLANCHES de `cas-clients`, à 1,05 ; ici on ne le
     met pas, pour la même raison qu'au large (voir « LE PANNEAU ET SES ENCRES »).
     Ne pas « réparer ». */
  .comparatif__ligne {
    background-color: var(--comparatif-panneau);
    /* ⚠️ LE FILET ARRIVE AUSSI ICI, tranché par Hugo le 16/09/2026 en même temps
       que le cadre du large. Ces cartes sont blanches sur le crème à 1,06 —
       exactement le contraste pour lequel le site rend le filet OBLIGATOIRE
       (cartes de `cas-clients`) — et jusque-là elles n'en avaient aucun : ce qui
       les séparait était la seule gouttière.

       ⚠️ ELLES GARDENT LEUR ARRONDI, contrairement au panneau du large qui passe
       aux coins vifs. Ce ne sont pas le même objet : au large c'est UN panneau
       (un `.cadre`, coins vifs), en étroit ce sont des CARTES (20px). Le site
       distingue les deux, et le tableau n'existe plus à cette largeur. */
    border: var(--filet-panneau);
    border-radius: var(--comparatif-radius);
    padding-block-end: var(--sp-4);
    overflow: hidden;
  }
  .comparatif__ligne + .comparatif__ligne {
    margin-block-start: var(--sp-6);
  }
  /* ⚠️ LA RANGÉE DE GROUPE SORT DES CARTES au lieu d'en devenir une. En pile,
     chaque rangée est une carte blanche ; l'intertitre en aurait fait une
     vingt-deuxième, blanche et vide, au lieu de coiffer les suivantes. Il
     redevient donc ce qu'il est — un titre posé sur le crème entre deux
     groupes de cartes.

     Les trois règles qui suivent défont ce que `.comparatif__ligne` pose sur
     toutes les rangées : le fond, l'arrondi, le rembourrage bas et la
     gouttière.

     ⚠️ ELLES SONT ÉCRITES APRÈS `.comparatif__ligne`, ET C'EST OBLIGATOIRE.
     `.comparatif__ligne--groupe` a exactement la MÊME spécificité (0,1,0) : à
     égalité, c'est la dernière écrite qui gagne. Placées plus haut dans ce
     bloc — ce qu'elles étaient d'abord — elles étaient battues par le
     `background-color` de la carte, et l'intertitre gardait un bandeau opaque en
     travers du crème. */
  .comparatif__ligne--groupe {
    background-color: transparent;
    border-radius: 0;
    padding-block-end: 0;
  }
  /* ⚠️ `border: 0` RESTE ÉCRIT EN GROS, alors que l'intertitre n'a plus de bords
     latéraux depuis le 16/09/2026 : ce qui tombe ici est son filet du HAUT, qui au
     large ferme le morceau de panneau au-dessus de lui. En pile il n'y a plus de
     panneau à fermer — les rangées sont des cartes qui portent leur propre contour
     — donc le filet n'aurait rien à séparer et barrerait le crème. */
  .comparatif__groupe {
    padding: var(--sp-10) 0 var(--sp-2);
    background-color: transparent;
    border: 0;
  }
  /* L'intertitre ne prend pas la gouttière des cartes : il en a une à lui, dans
     son propre rembourrage. */
  .comparatif__ligne + .comparatif__ligne--groupe {
    margin-block-start: 0;
  }
  /* Les cellules ne portent plus ni fond ni arrondi : c'est la rangée qui les
     porte. Les filets non plus — dans une pile, ils découperaient la carte en
     tranches au lieu de séparer des rangées. */
  .comparatif__critere,
  .comparatif__cellule {
    background-color: transparent;
    border: 0;
    border-radius: 0;
    text-align: start;
  }
  /* L'intitulé du critère devient le titre de la carte. Il monte d'un cran de
     corps : c'est lui qui organise la carte, alors qu'au large il n'était qu'une
     entrée de première colonne parmi six. */
  .comparatif__critere {
    padding: var(--comparatif-pad-inline) var(--comparatif-pad-inline) var(--sp-3);
    font-family: var(--font-titre);
    font-size: var(--fs-md);
    font-weight: var(--fw-bold);
    line-height: var(--lh-tight);
  }
  /* Chaque valeur devient une ligne « intitulé / valeur ». */
  .comparatif__cellule {
    display: grid;
    grid-template-columns: minmax(0, 11rem) minmax(0, 1fr);
    gap: var(--sp-2) var(--sp-4);
    /* En pile, chaque valeur est une LIGNE de la carte et non une cellule d'un
       quadrillage : le rembourrage vertical se resserre, le latéral reste celui du
       tableau pour que tout s'aligne sur le même bord. */
    padding: var(--sp-2) var(--comparatif-pad-inline);
  }
  /* ⚠️ LA RANGÉE DE LA SOLUTION REPREND L'APLAT, et c'est ce qui remplace la
     carte débordante : sans elle, la bascule ferait disparaître le seul repère
     de la colonne Biron. Elle prend toute la largeur de la carte, d'où les
     marges latérales négatives qui annulent le rembourrage de la cellule. */
  .comparatif__cellule--phare {
    margin-inline: 0;
    padding-inline: var(--comparatif-pad-inline);
    padding-block: var(--sp-3);
    background-color: var(--btn-primary-bg, var(--c-blue));
  }
  /* ⚠️ ET SA VALEUR MONTE D'UN CRAN, EN MOBILE SEULEMENT — demandé par Hugo le
     28/08/2026 : « mettre Biron en avant dans les blocs du comparatif ». L'aplat
     bleu était le seul écart entre la rangée Biron et les trois autres ; c'est un
     écart de COULEUR, et une carte se parcourt de haut en bas, si bien que les
     quatre rangées se lisaient au même poids.

     La valeur Biron revient donc au corps de texte du site (`--fs-base`), là où
     les trois autres gardent le cran en dessous (`--fs-sm`, voir
     `.comparatif__cellule`). C'est le même dispositif que la colonne des
     critères au large : c'est l'ÉCART d'un cran qui hiérarchise, pas la taille
     en elle-même — donc ne pas « harmoniser » les quatre rangées.

     ⚠️ 600 ET NON 700 : c'est la graisse de gras du site, réglée une fois pour
     toutes sur `strong` dans _components.scss — Inter à 700 est trop lourd au
     milieu d'un texte courant, et ici la valeur peut faire deux lignes. */
  .comparatif__cellule--phare .comparatif__valeur {
    font-size: var(--fs-base);
    font-weight: var(--fw-semibold);
  }
  .comparatif__label {
    display: block;
    font-weight: var(--fw-medium);
    color: var(--c-blue-zodiac);
  }
  /* ⚠️ ET LE MOT « BIRON » MONTE AVEC ELLE — deuxième temps de la demande d'Hugo
     le 28/08/2026 : « je voulais aussi mettre le mot Biron en avant ». Un premier
     jet ne faisait monter que la valeur et laissait le nom au cran des trois
     autres libellés ; c'était un arbitrage de ma part, et il est levé.

     ⚠️ C'EST DONC LA GRAISSE, ET PLUS LA TAILLE, QUI SÉPARE LES DEUX LIGNES DU
     BANDEAU : le nom et sa valeur sont maintenant au même corps, le nom à 700 et
     la valeur à 600. Les trois autres approches gardent leur libellé un cran plus
     bas (`--fs-sm`, 500) — l'écart avec elles est intact, il a juste changé de
     support.

     ⚠️ 700 ICI ALORS QUE LA VALEUR EST À 600, et ce n'est pas une contradiction
     avec la règle du site : les 600 de `strong` valent pour du gras AU FIL D'UN
     TEXTE, où Inter à 700 s'alourdit. « Biron » est un nom seul sur un aplat, pas
     une phrase mise en valeur. */
  .comparatif__cellule--phare .comparatif__label {
    font-size: var(--fs-base);
    font-weight: var(--fw-bold);
    color: var(--c-white);
  }
}
/* Sous 30rem, la colonne des intitulés n'a plus la place de tenir à côté de sa
   valeur : les deux s'empilent. */
@media (max-width: 30rem) {
  .comparatif__cellule {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--sp-1);
  }
}
/* =========================================================================
   LES FORMES DE DÉCOR — la primitive partagée `.decor-formes*`
   (_components.scss), avec UN SEUL réglage propre à ce bloc : la forme de gauche
   est collée au BORD DE L'ÉCRAN.

   Demandé par Hugo le 26/08/2026 : « met qu'une des deux forme à gauche et met la
   vraiment sur le bord de l'écran. »

   ---------------------------------------------------------------------
   CE QUI CLOCHAIT, ET POURQUOI ÇA NE SE VOIT QUE SUR UN GRAND ÉCRAN.

   La primitive épingle l'arête INTÉRIEURE de la bande (c'est elle qui garantit le
   dégagement du texte) et lui donne une largeur en part de la section — 16 %. Les
   deux ne se rejoignent qu'à une largeur de fenêtre précise : la gouttière de page
   grandit avec la fenêtre, la bande aussi, mais pas au même rythme.

     1440 px   gouttière 135 px, bande 230 px → la forme DÉBORDE de 81 px hors écran
     1920 px   gouttière 375 px, bande 307 px → il reste 82 px de crème entre la
                 forme et le bord de l'écran

   C'est ce trou-là qu'Hugo voit. Aucune valeur de `--shape-w` ne le referme à toutes
   les largeurs, justement parce que le trou n'est pas une taille mais un ÉCART ENTRE
   DEUX RYTHMES.

   ---------------------------------------------------------------------
   LA CORRECTION : DEUX ÉPINGLES, ET LA LARGEUR EN DÉCOULE.

   L'arête intérieure reste à `--forme-arete`, l'extérieure passe à 0 — le bord
   gauche de la section, qui est pleine largeur, donc le bord de l'écran. La bande
   vaut alors exactement « la gouttière plus l'avancée », à toute largeur : la forme
   touche le bord de l'écran par construction, et son dégagement au texte ne change
   pas d'un pixel.

   ⚠️ IL FAUT REMETTRE `inline-size: auto`, sans quoi rien ne bouge. Quand un élément
   positionné a ses deux retraits ET une largeur, la spécification déclare le retrait
   de FIN surnuméraire et l'ignore : la bande serait restée à 16 %, collée à gauche
   cette fois, et son arête intérieure serait allée manger le texte sur un écran
   large. Le symptôme aurait été l'inverse du défaut corrigé, sans rien pour le dire.

   ⚠️ ET LE PLATEAU DE GRAIN REVIENT À SON DÉFAUT. La primitive le décale vers la
   moitié visible parce qu'une bonne part de la forme y est hors écran ; ici la forme
   est ENTIÈREMENT visible, donc la densité doit culminer en son cœur comme partout
   ailleurs sur le site. `initial` sur une propriété personnalisée la rend invalide,
   donc le `var()` de l'atome retombe sur son repli — recopier les deux nombres
   aurait fait un second jeu de valeurs à tenir en accord avec `.forme-trame__grain`.

   ⚠️ AUCUN POINT DE RUPTURE, et c'est la formule qui s'en charge comme dans la
   primitive : sous la largeur maximale du conteneur, `--forme-arete` dépasse la
   largeur de la section, l'arête intérieure passe à gauche de l'écran, la largeur
   calculée devient négative et la bande ne se rend plus du tout. Elle s'efface vers
   1145 px au lieu de dégrader.

   ---------------------------------------------------------------------
   CONSÉQUENCE À CONNAÎTRE : LA FORME FAIT DÉSORMAIS LA LARGEUR DE LA BANDE LIBRE,
   puisque sa largeur vaut « la gouttière plus 1rem ». Elle grandit donc avec la
   fenêtre au lieu d'en être une part fixe :

                 1280 px    1440 px    1920 px
     largeur      69 px     149 px     389 px    (était 205 / 230 / 307)
     dont visible 69 px     149 px     389 px    (était 125 / 149 / 307)

   C'est à 1920 px qu'on gagne — la forme touche le bord et grandit d'un quart — et
   vers 1280 px qu'on perd, où la gouttière ne fait que 55 px : la forme y vaut
   exactement la bande libre, ce qui est le parti de la primitive (« elle s'efface au
   lieu de dégrader ») rendu continu au lieu de se faire par sortie d'écran.

   ⚠️ RESTE UNE FENÊTRE DE ~25 px DE LARGEURS (1145 → 1170) OÙ LA FORME EST UN ÉCLAT
   de 1 à 14 px, soit un ou deux points de trame. Aucun point de rupture n'est posé
   pour autant : à 12 px de pas, un éclat de cette taille se lit comme une poussière
   dans la marge, pas comme un défaut, et une requête de largeur coûterait plus qu'elle
   ne rapporte pour 25 px de fenêtre.

      ⚠️ LA BANDE DE DROITE N'EST PAS TOUCHÉE : elle est large de 35 % de la section et
   l'arête de page la coupe déjà franchement (283 px hors écran à 1920). Son plateau
   de grain décalé reste donc nécessaire, et c'est bien pour ça que ces deux règles
   ne visent que `--gauche`.
   ========================================================================= */
.comparatif > .decor-formes__bande--gauche {
  inset-inline-start: 0;
  /* La largeur découle des deux épinglages — voir l'en-tête, elle est
     INDISPENSABLE et pas une précaution. */
  inline-size: auto;
}

/* La forme remplit la bande, quelle que soit la clé de position qui la porte : la
   primitive donne 88 % à `hautGauche` (deux formes qui partagent une bande n'ont pas
   à s'aligner sur le même flanc), et 88 % laisserait ici 12 % de crème entre la
   forme et le bord de l'écran — exactement le trou qu'on referme. */
.comparatif > .decor-formes__bande--gauche > .decor-formes__forme {
  --shape-w: 100%;
  /* Retour au plateau de l'atome : la forme est entièrement visible. */
  --grain-debut: initial;
  --grain-fin: initial;
}

/* =========================================================================
   BLOC « reperes » — blocs/reperes.njk.

   Origine : les deux rangées de chiffres de « À propos » — Figma 144:4980 (trois
   chiffres) et 144:5114 (cinq).

   Un chiffre en grand, son libellé dessous, séparés par un filet vertical.
   ========================================================================= */
.reperes {
  background-color: var(--bg-page);
  padding-block: var(--sp-section);
  padding-inline: var(--container-pad);
  /* Le filet qui sépare deux repères. Il vit ici parce que la variante sombre le
     redéfinit : le filet bleu clair du site disparaît sur le bleu nuit. */
  --reperes-filet: var(--filet-panneau-teinte);
}

/* ---------- La boîte ----------
   Elle existe dans les deux variantes pour que la mise en page soit la même des
   deux côtés ; en clair elle ne porte que la mesure. */
.reperes__boite {
  width: 100%;
  max-width: 96rem;
  margin-inline: auto;
}

/* ⚠️ COTES DE LA RÈGLE 1 DE MAQUETTE.md — 96rem, rayon 28px, aucun contour : sur du
   bleu nuit posé sur crème, le contraste fait déjà l'arête. Les mêmes que
   `.temoignages__boite`, `.chaine__boite`, `.etapes__boite`.

   ⚠️ ET LE FILET DE SÉPARATION CHANGE AVEC LE FOND. Le filet bleu éclairci du site
   est calé sur le crème ; sur le bleu nuit il disparaît. Un blanc voilé le remplace
   — c'est le même remède que `.chaine__boite` applique au filet de son cylindre. */
.reperes--boite .reperes__boite {
  padding-block: clamp(2rem, 1.2rem + 2.4vw, 3.75rem);
  padding-inline: clamp(1.5rem, -0.5rem + 8vw, 5.5rem);
  border-radius: 1.75rem; /* 28px */
  background-color: var(--c-blue-dark);
  --reperes-filet: rgba(255, 255, 255, 0.18);
}

/* ---------- L'en-tête ---------- */
.reperes__entete {
  margin-block-end: clamp(var(--sp-6), 2.4vw, var(--sp-10));
}

/* Même anatomie que l'`eyebrow` de `hero-page` : chasse fixe, capitales, teinte
   d'accent. Dans la boîte sombre, `.on-dark` a déjà passé --text-accent au fluo. */
.reperes__surtitre {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--text-accent);
}

/* Le titre ne déclare rien de sa police : _base.scss pose déjà le titrage et la
   taille du niveau (règle 3). --text-strong et non --c-blue-zodiac : le bloc peut
   être dans une boîte sombre, où le bleu profond serait illisible. C'est le rôle
   qui suit le fond, comme `.texte-module--sombre .texte-module__titre`. */
.reperes__titre {
  margin: var(--sp-3) 0 0;
  max-width: 34ch;
  color: var(--text-strong);
  text-wrap: balance;
}

/* =========================================================================
   LA RANGÉE.

   ⚠️ `auto-fit` ET NON UN NOMBRE DE COLONNES, et c'est ce qui permet au même bloc
   de servir trois chiffres et cinq sans qu'aucun compte ne soit publié. La cote
   plancher (12rem) est ce qui décide du repli : à cinq chiffres sur écran moyen, la
   rangée passe d'elle-même à deux lignes plutôt que d'écraser les libellés.

   ⚠️ LE FILET SE FAIT PAR LA GOUTTIÈRE, comme le quadrillage de `module.grille` :
   `column-gap: 1px` sur un fond à la couleur du filet, et chaque repère repeint son
   fond par-dessus. L'alternative — un `border-inline-start` sauf sur le premier —
   demandait de désigner « les repères qui ne sont pas en début de ligne » par un
   `:nth-child` dont le pas change à chaque repli, donc à chaque nombre de chiffres.
   Ici il n'y a rien à savoir.

   ⚠️ CONSÉQUENCE : LE FOND DE CHAQUE REPÈRE DOIT ÊTRE OPAQUE. `--section-bg` le
   donne — il vaut le crème sur la page, et la boîte sombre le redéfinit. Sans ça, le
   fond de gouttière traverserait toute la rangée.
   ========================================================================= */
.reperes__liste {
  margin: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
  gap: 1px;
  background-color: var(--reperes-filet);
}

.reperes__repere {
  padding: clamp(var(--sp-5), 2vw, var(--sp-8));
  background-color: var(--section-bg);
}

.reperes--boite .reperes__repere {
  background-color: var(--c-blue-dark);
}

/* ⚠️ L'APPARENCE D'UN REPÈRE N'EST PAS ICI : elle vit dans `.repere`
   (_components.scss), parce que `texte-module` porte le même atome dans sa boîte
   depuis le 20/08/2026. Ce fichier ne garde que le PLACEMENT — la rangée, ses
   filets, son fond. */
@media (max-width: 48rem) {
  /* En dessous, une colonne : la cote plancher de la grille suffirait à replier,
     mais à 12rem deux colonnes tiennent encore sur un téléphone et les libellés y
     partent sur quatre lignes. */
  .reperes__liste {
    grid-template-columns: minmax(0, 1fr);
  }
}
/* =========================================================================
   BLOC « texte-module » — blocs/texte-module.njk.
   Origine : les cinq sections de la page dictionnaire, Figma 141:6737.

   Un en-tête libre sur le fond de page, un module encadré en dessous, et
   l'illustration posée à côté sans boîte. Le patron est celui de
   `connecteurs` — le pourquoi est en tête du gabarit, pas ici.

   ⚠️ CETTE FEUILLE N'EMPLOIE QUE DES RÔLES SÉMANTIQUES (--text-strong,
   --text-muted, --text-accent, --section-bg). Le bloc existe en clair ET en
   bleu nuit pleine largeur : une couleur écrite en dur — `--c-blue-zodiac` sur
   un titre — donnerait du bleu nuit sur bleu nuit, illisible et sans la
   moindre alerte. C'est la même contrainte que sections/_texte-panneau.scss.
   ========================================================================= */
.texte-module {
  position: relative;
  background-color: var(--bg-page);
  padding-block: var(--sp-section);
  /* ⚠️ `clip` ET NON `hidden`, et la distinction est celle que coeur.njk met en
     garde : `hidden` ferait de la section un CONTENEUR DE DÉFILEMENT, ce qui casse
     un `position: sticky` à l'intérieur. `clip` rogne sans créer de boîte de
     défilement. Sur l'axe INLINE seulement — c'est le seul axe où un décor peut
     fabriquer une barre de défilement horizontale, et `visible` / `clip` est la
     seule paire mixte que la spécification autorise, donc le débordement vertical
     reste intact.

     Il est là pour la forme de trame du diagramme, qui affleure dans la marge de
     gauche et doit être coupée AU BORD DE LA PAGE — ce qui se lit comme « elle
     continue hors champ », là où un rognage au bord du conteneur ferait une coupe
     franche au milieu de l'écran. C'est le parti de `.intro` et de `.coeur`, sauf
     que ces deux-là peuvent porter l'enveloppe elles-mêmes : ici le décor doit
     rester ancré sur le DIAGRAMME, dont la position verticale dépend de la longueur
     du texte au-dessus, et une enveloppe posée sur la section ne saurait pas où le
     trouver. */
  overflow-x: clip;
}

/* =========================================================================
   LE DONUT DÉCORATIF, dans la boîte verte et derrière la carte. Demandé par
   Hugo le 18/08/2026, avec sa texture halftone.

   ⚠️ IL EST DANS LE COIN BAS-GAUCHE DE LA BOÎTE. Deux placements ont été essayés
   et écartés le même jour, ne pas y revenir :

     derrière la BOÎTE ENTIÈRE — la boîte est plafonnée à 88rem et centrée, donc il
       n'affleurait que dans la gouttière laissée sur un écran plus large, et
       disparaissait complètement en dessous de 88rem ;
     derrière la CARTE « Opération de contrôle » — visible, mais il ne restait de
       lui que deux arcs au-dessus et en dessous d'une carte qui le couvrait.

   ⚠️ EN CSS ET NON EN CANVAS, contrairement à l'anneau de l'accueil. Ce n'est
   pas une entorse à la convention, c'est que `cible.js` ne peut pas servir ici :
   il fait un `querySelector` SINGULIER sur `[data-cible-anneau]`, donc une seule
   instance par page, et il dessine une trame de points animée. Il aurait fallu le
   généraliser pour un décor fixe qui n'a besoin ni de points ni d'animation.

   Le donut est un `conic-gradient` (les secteurs) percé par un masque radial (le
   trou). Un `border-radius: 50%` avec une grosse bordure aurait donné un anneau,
   mais pas des secteurs : c'est le dégradé conique qui fait lire « graphique »
   plutôt que « cercle ».

   ⚠️ POURQUOI TROIS TONS. Un donut à secteurs d'UNE seule couleur est indiscernable
   d'un anneau — les secteurs n'existent que par leur contraste. Trois tons de la
   même famille le font lire comme un graphique tout en le gardant décoratif.

   ---------------------------------------------------------------------
   TOUT EST RÉGLABLE PAR VARIABLES, en tête de règle : diamètre, épaisseur de
   l'anneau, position, et les trois tons. Itérer sur l'allure ne demande de
   toucher qu'à ces lignes.
   ========================================================================= */
/* L'enveloppe de clip. Elle épouse la boîte et reprend SON arrondi, ce qui permet
   au donut de déborder du coin bas-gauche sans dépasser de la boîte.

   `border-radius: inherit` plutôt que la valeur recopiée : le jour où l'arrondi de
   la boîte change, le clip suit tout seul. */
.texte-module__decor {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  border-radius: inherit;
  pointer-events: none;
}

.texte-module__donut {
  /* Grossi deux fois le 18/08/2026 : 21rem, puis 36rem, puis 48rem. À cette taille
     le donut est plus HAUT que la boîte, donc l'enveloppe lui coupe le haut et le
     bas — c'est voulu, on n'en voit plus qu'un large arc dans le coin. */
  --donut-taille: clamp(26rem, 52vw, 48rem);
  --donut-trou: 54%; /* part du rayon laissée vide au centre */
  /* Débordement hors du coin. Négatifs pour que le donut sorte de la boîte et se
     fasse couper par l'enveloppe : c'est ce qui l'ancre dans le coin plutôt que de
     le poser à côté. Ils grandissent avec le diamètre — sinon un donut plus gros
     n'avance pas dans le coin, il rampe vers le centre de la boîte. */
  --donut-droite: -12rem;
  --donut-bas: -12rem;
  /* EN FLUO — demandé par Hugo le 18/08/2026, après un premier jet en blanc crème.
     Les deux tons secondaires sont du fluo MÉLANGÉ AU VERT DE LA BOÎTE, et non du
     fluo éclairci vers le blanc : mélangé au fond, un secteur recule au lieu de
     virer au jaune pâle, et les trois restent de la même famille.

     ⚠️ LE MÉLANGE LIT LE FOND DE LA BOÎTE (--boite-dictionnaire), et non le jeton du
     pilier écrit en clair. C'est ce qui donne son sens à la phrase ci-dessus : le
     25/08/2026 la boîte est passée de l'aplat #00b3a4 au vert sombre #00756c, et un
     mélange resté sur l'aplat aurait fait « reculer » les deux secteurs vers une
     couleur qui n'est plus celle du fond — ils auraient au contraire avancé, en deux
     taches plus claires que la boîte. Lire la variable fait aussi suivre le donut
     quand /labo/ surcharge `--boite-ton`. Le repli n'est là que par principe : le
     décor n'est rendu que pour `fond: dictionnaire`. */
  --donut-ton-1: var(--c-fluo);
  --donut-fond: var(--boite-dictionnaire, var(--c-pilier-dictionnaire-sombre));
  --donut-ton-2: color-mix(in srgb, var(--c-fluo) 78%, var(--donut-fond));
  --donut-ton-3: color-mix(in srgb, var(--c-fluo) 52%, var(--donut-fond));
  position: absolute;
  right: var(--donut-droite);
  bottom: var(--donut-bas);
  inline-size: var(--donut-taille);
  aspect-ratio: 1;
  /* Les quatre secteurs. Les bornes sont franches (une même valeur en fin d'un
     secteur et en début du suivant) : sans ça le dégradé conique fond les
     secteurs l'un dans l'autre et on perd la lecture « graphique ». */
  background: conic-gradient(from 200deg, var(--donut-ton-1) 0deg 128deg, var(--donut-ton-3) 128deg 212deg, var(--donut-ton-2) 212deg 300deg, var(--donut-ton-3) 300deg 360deg);
  /* ---------------------------------------------------------------
     DEUX COUCHES DE MASQUE COMPOSÉES : la trame de points ∩ l'anneau.

     C'est la technique de `.forme-trame__grain` (_components.scss), reprise
     telle quelle : la trame découpe l'intérieur, la géométrie découpe le
     contour, et `mask-composite: intersect` ne garde que l'intersection.

     ⚠️ AUCUNE PAIRE `-webkit-` ICI, ET C'EST VOLONTAIRE. Les mots-clés de
     `mask-composite` diffèrent entre la version standard et la version
     -webkit- ; les faire cohabiter casse la composition. `.forme-trame__grain`
     a tranché la même question de la même façon — voir son commentaire.

     halftone-fond.svg fait 504 × 504 et tuile sans raccord visible ; c'est la
     même taille de tuile que partout ailleurs sur le site, donc la texture a le
     même grain que le reste du décor.

     L'anneau s'éteint DES DEUX CÔTÉS : transparent au centre jusqu'à
     --donut-trou, opaque dans la bande, puis transparent à partir de 100 %.
     Sans cette dernière borne, `closest-side` laisse le masque opaque jusqu'aux
     coins du carré et le donut devient un carré troué. */
  mask-image: url("/assets/img/halftone-fond.svg"), radial-gradient(closest-side, transparent var(--donut-trou), #000 calc(var(--donut-trou) + 0.5%), #000 99.5%, transparent 100%);
  mask-size: 504px 504px, 100% 100%;
  mask-repeat: repeat, no-repeat;
  mask-composite: intersect;
}

/* =========================================================================
   LE GRAPHIQUE EN TRAME, dans un coin BAS de la boîte — à droite par défaut, à
   gauche sous `motifCoin: gauche`. Demandé par Hugo le 26/08/2026 pour « Vos
   données fiables, connectées à tous vos outils » de /integrations/.

   Ce bloc est le QUATRIÈME hôte de partials/motif.njk, après `texte-panneau`
   (les quatre briques de /produit/), `onglets-metiers` et `etapes`. L'apparence
   est dans _components.scss ; cette feuille ne pose que le PLACEMENT et les
   TEINTES — même partage que chez les trois autres.

   ⚠️ IL RÉEMPLOIE `__decor`, l'enveloppe de clip du donut, plutôt que d'en
   ouvrir une seconde. Elle épouse la boîte et reprend son arrondi, ce qui est
   exactement ce dont un dessin collé au coin a besoin : depuis le 25/08/2026
   l'encre de chaque dessin touche ses quatre bords, donc sans découpage elle
   traverserait l'arrondi et baverait sur le crème. Le donut occupe le coin
   bas-GAUCHE : les deux cohabitent dans la même enveloppe tant que le motif reste
   à droite — `motifCoin: gauche` les superposerait, et rien ne le vérifie.

   ⚠️ LE CONTENU PASSE AU-DESSUS SANS UNE LIGNE DE PLUS : `.texte-module--boite
   .texte-module__inner` est déjà relevé en `z-index: 1` pour le donut, plus bas
   dans cette feuille. Ne pas y ajouter de `z-index: -1` sur le motif — il
   passerait derrière le fond de la boîte, où il est simplement invisible (même
   piège que `.texte-panneau__inner` et que le tracé de `.chemin`).
   ========================================================================= */
/* Le pied collé à l'arête basse, la taille prise sur la HAUTEUR de la boîte.
   C'est la formulation retenue sur les briques produit après trois essais
   écartés ; le pourquoi complet est dans sections/_texte-panneau.scss, et il ne
   faut pas revenir sur ces deux points :

     • AUCUN `aspect-ratio` ici. Chaque dessin a SON rapport depuis que les
       viewBox de partials/motif.njk serrent l'encre ; c'est `inline-size: auto`
       qui le lit. Un rapport écrit en dur rétablirait une bande vide sur un axe.

     • AUCUN PLAFOND DE LARGEUR. Il laisserait un blanc DANS LE COIN, ce qui est
       précisément ce que l'ancrage au coin sert à éviter.

   70 % est la crête des briques produit, et cette boîte-là leur ressemble : elle
   est à la hauteur de son contenu, pas à celle de la page. ⚠️ CE QU'IL FAUT
   SURVEILLER EN LE GROSSISSANT — le motif est ancré à un coin extérieur, donc
   grossir le pousse VERS LE TEXTE : la colonne bornée à 68ch de la moitié gauche
   depuis le coin droit, les paragraphes de la colonne de droite depuis le coin
   gauche. Du blanc posé sur sa série la plus claire ne donne que ~1,9 : s'il
   vient sous le texte, c'est `--motif-taille` qu'il faut baisser.

   ⚠️ LE DÉFAUT EST DANS LE `var()`, JAMAIS DÉCLARÉ ICI. Une propriété posée SUR
   l'élément bat la même propriété héritée d'un ancêtre : écrite en déclaration,
   elle masquerait la valeur qu'une boîte poserait et le réglage n'aurait aucun
   effet, sans la moindre alerte. Même règle que `--motif-base`. */
.texte-module__motif {
  position: absolute;
  z-index: 0;
  block-size: var(--motif-taille, 70%);
  inline-size: auto;
  inset-block-end: 0;
  inset-inline-end: 0;
  pointer-events: none;
  /* ---------- LA SORTIE PAR LE FLANC DROIT, à zéro par défaut ----------
     Demandée par Hugo le 26/08/2026 pour « D'un constat de terrain à une
     plateforme data » de /a-propos/ : « fais grossir encore et fais dépasser sur
     le bord droit ». C'est la construction de `--gauche` en miroir — le dessin
     court sous l'arête et c'est `__decor` qui le coupe à plat dessus.

     ⚠️ C'EST UN `translate` ET NON UN `inset`, et ce n'est pas un choix de style.
     La sortie est une part du DESSIN, jamais une cote de la boîte : un
     `inset-inline-end` en pourcentage se résout sur la LARGEUR DE LA BOÎTE, alors
     que la largeur du dessin est intrinsèque (`inline-size: auto`) et donc inconnue
     de la feuille. Les pourcentages d'un `translate`, eux, se résolvent sur la
     boîte de l'élément lui-même — la seule référence juste, et elle vaut pour
     n'importe quel rapport.

     ⚠️ `translate` ET NON `transform`, pour qu'un `transform` venu d'ailleurs se
     compose au lieu de l'effacer.

     ⚠️ LA VARIANTE `--gauche` NE LIT PAS CE BOUTON : elle déclare son propre
     `translate` pour sortir de l'autre coin, et une propriété ne se cumule pas
     avec elle-même. Les deux sorties ne se composent donc pas, ce qui est sans
     objet — un dessin ne peut déborder que du flanc auquel il est ferré.

     Le défaut est dans le repli du `var()`, comme --motif-taille : écrit en
     déclaration il masquerait la valeur qu'une boîte pose, sans la moindre
     alerte. */
  translate: var(--motif-sortie-droite, 0) 0;
}

/* ---------- CENTRÉ SUR LA HAUTEUR, ET PLUS GROS ----------
   Hugo, le 26/08/2026 : « tu peux faire grossir et centrer en hauteur le chart
   dans le bloc Vos données fiables, connectées à tous vos outils. » C'est un
   TROISIÈME rendu du même dessin, pas un réglage : la règle de base colle le pied
   à l'arête basse, celle du dessus le fait déborder du coin, celle-ci le pose
   contre le FLANC droit, à mi-hauteur.

   ⚠️ `inset-block: 0` + `margin-block: auto`, ET NON `top: 50%` + un `translate`.
   Quand aucune des trois valeurs — bord haut, hauteur, bord bas — n'est `auto`,
   les deux marges automatiques se partagent le reste à parts égales : c'est le
   centrage exact, sans que la feuille ait à connaître la hauteur du dessin. La
   route par `translate` était possible, mais elle est déjà prise par la variante
   `--gauche` juste dessous pour sa sortie de coin — deux emplois de la même
   propriété sur le même axe, et la première déclaration recopiée par erreur
   suffirait à décaler l'autre.

   ⚠️ CE N'EST PAS LA HAUTEUR QUI BORNE LA TAILLE, C'EST LA LARGEUR LIBRE. Le
   dessin reste ancré au flanc droit, donc grossir le pousse vers le texte, et le
   texte s'arrête bien avant le bord de la boîte : mesuré sur /integrations/, la
   ligne d'encre la plus longue (la première du paragraphe, la colonne étant bornée
   à 68ch) s'achève à 698px sur une boîte de 1197 — il reste donc ~499px. `flux` a
   un rapport de 1,52, ce qui plafonne sa hauteur à ~328px.

     1280px   boîte 1180 × 320   →  272 de haut, 414 de large, 67px d'écart
     1440px   boîte 1197 × 344   →  292 de haut, 445 de large, 54px d'écart
     1920px   boîte 1197 × 362   →  308 de haut, 469 de large, 30px d'écart

   85 % tient donc les trois largeurs de la bande desktop. ⚠️ ET LA HAUTEUR DE LA
   BOÎTE CROÎT AVEC LA FENÊTRE alors que l'encre du texte, elle, ne bouge pas (son
   rembourrage vertical est un `clamp()` en `vw`) : c'est 1920px qui contraint, pas
   1440. À 90 % il ne reste plus que 3px d'écart là-haut. Du blanc posé sur la
   série la plus claire ne donne que ~1,9, donc c'est bien un plafond.

   ⚠️ L'AGRANDISSEMENT NE VAUT QU'À PARTIR DE `xl`, et le pourquoi n'est pas une
   affaire de goût : sous 1280px la boîte se resserre — 917px à 1024, 725 à 800 —
   pendant que la mesure du texte, écrite en `ch`, ne suit pas. La largeur libre
   tombe à 258px puis à 89, et le dessin passe sous le texte quelle que soit sa
   cote. La variante y reprend donc les 70 % de la règle de base : à cette taille
   le recouvrement est exactement celui d'aujourd'hui, seule la position verticale
   change.

   ⚠️ ELLE NE SE COMPOSE PAS AVEC `--gauche`, et rien ne le vérifie. Les deux sont
   des sélecteurs à une classe : `--gauche` étant écrite après, c'est elle qui
   l'emporterait sur la hauteur et sa sortie verticale annulerait le centrage. Cet
   ordre est délibéré — la panne est alors le rendu du coin gauche entier, ce qui se
   lit, plutôt qu'un hybride des deux.

   ⚠️ LES DÉFAUTS SONT DANS LES `var()`, JAMAIS EN DÉCLARATION — même raison que
   pour la règle de base : une propriété posée SUR l'élément masquerait la valeur
   qu'une boîte poserait, et le réglage n'aurait aucun effet sans la moindre
   alerte. */
.texte-module__motif--centre {
  inset-block: 0;
  margin-block: auto;
  block-size: var(--motif-taille, 70%);
}

@media (min-width: 80rem) {
  .texte-module__motif--centre {
    block-size: var(--motif-taille, 85%);
  }
}
/* ---------- ET RIEN EN DESSOUS DE `lg` ----------
   Hugo, le 27/08/2026 : « en mode mobile sur le bloc Vos données fiables,
   connectées à tous vos outils tu peux enlever le graph halftone de fond. »

   ⚠️ CE RENDU-LÀ PERD SA PRÉMISSE À CETTE LARGEUR, ET C'EST CE QUI LE DISTINGUE
   DES DEUX AUTRES. Ce qui borne le dessin centré n'est pas la hauteur mais la
   LARGEUR LIBRE à droite du texte (le chiffrage est juste au-dessus) : sous `lg`
   le corps s'empile en une seule colonne, la mesure prend toute la boîte et il ne
   reste plus un pixel de flanc libre. Centré sur la hauteur, le dessin se pose
   alors en plein milieu du paragraphe — là où un rendu ancré à un coin resterait,
   lui, dans son coin. Du blanc sur sa série la plus claire ne donne que ~1,9.

   Même parti que `.onglets-metiers__motif` et que le tracé de `.chemin`, cachés à
   la même largeur pour la même raison : le décor s'efface plutôt que de se
   dégrader.

   ⚠️ LES DEUX AUTRES RENDUS NE SONT PAS TOUCHÉS, délibérément — la règle de base
   (`/a-propos/`) et `--gauche` (`/pourquoi-biron/`) restent ferrées à une arête
   basse à toutes les largeurs, et rien n'a été demandé sur elles. Le jour où l'une
   des deux doit partir aussi, c'est un sélecteur à ajouter ici, pas ce
   sélecteur-ci à élargir : masquer `.texte-module__motif` tout court emporterait
   les quatre instances du bloc d'un coup. */
@media (max-width: 60rem) {
  .texte-module__motif--centre {
    display: none;
  }
}
/* L'AUTRE COIN BAS, demandé par Hugo le 26/08/2026 pour « Un outil construit sur
   votre réalité » de /pourquoi-biron/ : la disposition `duo` y laisse le bas de la
   colonne de GAUCHE vide — le titre y est seul et collé en haut — pendant que les
   paragraphes de droite descendent jusqu'au pied de la boîte. C'est donc le coin
   droit qui est occupé sur cette section, et le gauche qui est libre.

   ⚠️ LE `auto` N'EST PAS DE LA COQUETTERIE. En positionnement absolu, `inline-size:
   auto` avec les DEUX bords contraints n'est pas une taille intrinsèque : la boîte
   s'étire pour joindre les deux bords, et le dessin traverserait toute la boîte. Ce
   n'est pas le même remède que la sur-contrainte des formes décoratives — là deux
   ancrages se disputaient une même règle, ici la largeur est lue sur le viewBox et
   doit le rester.

   ---------- ET IL DÉBORDE DU COIN, là où le coin droit reste à fleur ----------
   Hugo, le 26/08/2026 : « tu peux le mettre plus gros quitte à vraiment ne faire
   dépasser que le haut du chart dans le bloc ». C'est un AUTRE rendu, pas un
   réglage : le coin droit tient tout entier dans la boîte, et sa formulation —
   « le pied collé à l'arête, aucun plafond de largeur » — existe justement pour
   qu'aucun blanc ne subsiste DANS le coin. Ici le dessin sort du coin et
   l'enveloppe le tranche : c'est la construction du donut juste au-dessus dans
   cette feuille, en miroir.

   ⚠️ LA SORTIE EST UNE PART DU DESSIN, PAS UNE COTE DE LA BOÎTE, et c'est ce qui
   évite le piège que le donut documente (« ils grandissent avec le diamètre — sinon
   un donut plus gros ne fait que dépasser plus »). Des `inset` ne pouvaient pas le
   dire : un pourcentage d'`inset-block-end` se résout sur la HAUTEUR DE LA BOÎTE, et
   sur l'axe inline sur sa LARGEUR — or la largeur du dessin est intrinsèque
   (`inline-size: auto`), donc inconnue de la feuille. Les pourcentages de `translate`
   se résolvent sur la boîte de l'ÉLÉMENT : c'est la seule référence juste, et elle
   vaut pour n'importe quel rapport.

   ⚠️ `translate` ET NON `transform`, comme partout ailleurs sur le site : ce sont
   deux propriétés séparées qui se COMPOSENT, donc un `transform` venu d'ailleurs —
   la règle de reveal.js, par exemple — ne peut pas l'écraser.

   140 % ET UNE SORTIE DE 62 / 46 %, et le plafond se calcule. Le haut du dessin
   tombe à `H − 0,38 × taille` de l'arête haute : à 140 % de la hauteur de boîte il
   arrive à 0,47 H, soit ~212px sur les ~453px mesurés à 1440px sur
   /pourquoi-biron/, contre un titre sur deux lignes qui descend à ~154px. Au-delà
   de ~165 % il passe derrière la dernière ligne du titre, et du blanc posé sur la
   trame ne donne que ~1,9 — sur `anneaux` c'est même le départ de l'arc FLUO qui
   borde le haut de l'encre, donc ne pas parier sur une frange diluée.

   ⚠️ CALIBRÉ SUR UN DESSIN PRESQUE CARRÉ. La sortie inline est une part de la
   LARGEUR : `anneaux` est le seul carré des dix, et il s'arrête à ~342px du bord
   gauche de la boîte quand la colonne de droite commence à ~621px. Un dessin large
   (`jauge`, rapport 2,00) avance deux fois plus à hauteur égale et atteindrait le
   texte : c'est `--motif-sortie-inline` qu'il faudrait alors ouvrir.

   ⚠️ LES DÉFAUTS SONT DANS LES `var()`, JAMAIS EN DÉCLARATION — même raison que
   pour `--motif-taille` plus haut : une boîte qui voudrait un autre réglage doit
   pouvoir le poser, et une déclaration ici masquerait sa valeur en silence. Le jour
   où un coin gauche devra rester à fleur, c'est une clé qu'on ajoutera au bloc. */
.texte-module__motif--gauche {
  inset-inline-start: 0;
  inset-inline-end: auto;
  block-size: var(--motif-taille, 140%);
  translate: calc(-1 * var(--motif-sortie-inline, 46%)) var(--motif-sortie, 62%);
}

/* ---------- La boîte ----------
   L'aplat n'est PAS pleine largeur : c'est une boîte posée sur le crème, aux
   cotes des trois autres boîtes du site (`.temoignages__boite`,
   `.sequence__boite`, `.texte-panneau__boite`) — 96rem, rayon 28px, aucun
   contour. Sur un aplat franc posé sur crème, le contraste fait déjà l'arête.

   C'est ce qui dispense le bloc de bandes de trame : la boîte porte sa propre
   rupture. Le pourquoi complet est en tête de blocs/texte-module.njk.

   ⚠️ LA GÉOMÉTRIE EST SÉPARÉE DE LA COULEUR — `--boite` d'un côté, `--sombre`
   de l'autre. Les souder ferait perdre largeur, rayon et gouttières au premier
   autre fond de boîte ajouté ; c'est le défaut que _texte-panneau.scss a dû
   corriger après coup.

   ⚠️ Le rayon est écrit en dur, comme dans les trois autres boîtes : il n'existe
   pas de jeton à 28px (--radius-lg vaut 16px). Quatre endroits le répètent
   maintenant — un jeton serait justifié, mais il ne s'invente pas ici tout seul. */
/* ⚠️ 88rem ET NON 96rem, DÉLIBÉRÉMENT — Hugo l'a resserrée le 18/08/2026. Cette
   boîte est donc la SEULE des quatre à ne pas faire 96rem : `.temoignages__boite`,
   `.sequence__boite` et `.texte-panneau__boite` gardent la cote d'origine. Ne pas
   « réaligner » les quatre en croyant corriger un oubli.

   88rem se raccorde à quelque chose : le contenu à l'intérieur est borné par
   `--container-max` (80rem, la grille de la maquette), donc la boîte laisse
   exactement 4rem de respiration de chaque côté du texte. */
.texte-module--boite .texte-module__boite {
  /* `relative` + z-index pour passer AU-DESSUS du donut décoratif. Sans eux la
     boîte reste non positionnée, et un élément positionné — le décor — se peint
     par-dessus, quel que soit l'ordre dans le document. */
  position: relative;
  z-index: 1;
  max-width: 88rem;
  margin-inline: auto;
  border-radius: 1.75rem; /* 28px, comme les trois autres boîtes du site */
  padding-block: clamp(var(--sp-12), 4.7vw, 5.5rem);
}

/* ---------- La boîte bleu nuit ----------
   ⚠️ LE FOND PASSE PAR UNE VARIABLE plutôt que d'être écrit deux fois. Le motif en
   trame doit reculer ses deux séries secondaires VERS LE FOND RÉEL de la boîte —
   c'est l'invariant de partials/motif.njk, et une seconde écriture du jeton se
   déferait de la première le jour où la teinte bouge, les deux séries avançant
   alors en taches plus claires que le fond. Même montage que `--boite-dictionnaire`
   et `--boite-integrations` plus bas, sans le crochet d'atelier dont elles ont
   besoin. */
.texte-module--sombre .texte-module__boite {
  --boite-sombre: var(--c-blue-dark);
  background-color: var(--boite-sombre);
  /* ---------- Les teintes du graphique en trame ----------
     Hugo, le 26/08/2026, pour « D'un constat de terrain à une plateforme data » de
     /a-propos/ : « tu peux ajouter un chart bleu clair […] à droite ».

     ⚠️ LA BASE N'EST PAS LE DÉFAUT DE L'ATOME. Sur bleu nuit, partials/motif.njk
     retombe sur le fluo, qui y pèse 11,5 — c'est ce que rendent la boîte sombre de
     `etapes` et les onglets de /plateforme/. Ici c'est un BLEU CLAIR, tranché par
     Hugo entre deux niveaux : --c-blue-400 donne 3,87 / 2,71 / 1,82 sur le fond,
     donc un décor qui se lit franchement bleu sans disputer la page au texte blanc
     (13,4) — le régime des briques produit (2,80 / 2,28 / 1,76). --c-blue-100, à
     10,41, gardait le poids du fluo mais arrivait si près du blanc du texte que les
     deux se confondaient.

     ⚠️ LA RÈGLE VAUT POUR TOUTES LES BOÎTES SOMBRES DU BLOC, et c'est voulu : la
     teinte du décor est une propriété de la boîte, pas de la page. Une seule
     section déclare un `motif` sur `fond: sombre` aujourd'hui — la ligne est là
     pour que la deuxième n'ait rien à redécouvrir.

     ⚠️ LE DÉFAUT DE `--motif-taille` RESTE DANS SON `var()`, non déclaré : voir
     `.texte-module__motif`. Le dessin est ancré au coin bas-droit, donc le grossir
     le pousse vers la colonne de repères d'en face. */
  --motif-fond: var(--boite-sombre);
  --motif-base: var(--c-blue-400);
}

/* ---------- La boîte verte sombre du dictionnaire ----------
   Demandée par Hugo le 18/08/2026 en remplacement du bleu nuit de la maquette :
   sur cette page, la boîte porte le vert du pilier.

   ⚠️ ELLE A CHANGÉ DE CAMP LE 25/08/2026, comme l'orange des intégrations cinq
   jours plus tôt. Elle a d'abord été un fond CLAIR — l'aplat #00b3a4, les trois
   rôles d'encre ramenés sur le noir du site, aucune mise en valeur possible
   dessus. Hugo : « on peut les faire passer dans une teinte plus sombre pour en
   faire des blocs sur lesquels on écrit en blanc ? » La maquette peint cette bande
   en bleu nuit, donc c'est un fond sombre — règle 10 bis de MAQUETTE.md — et la
   boîte porte `.on-dark` comme les trois autres boîtes de pilier.

   ⚠️ LA TEINTE N'EST PAS L'APLAT DU PILIER, mais sa SECONDE valeur,
   --c-pilier-dictionnaire-sombre (#00756c). Le raisonnement complet est sur le
   jeton, dans _tokens.scss. En deux mots : le vert est déjà à saturation 100 %,
   donc on tient la teinte ET la saturation et on ne descend que la clarté — il n'y
   a pas ici le piège du brun qui obligeait l'orange à tourner vers le rouge.

   ⚠️ LES TROIS RÔLES DE `.on-dark` SONT MESURÉS ICI, ET C'EST LA RÈGLE 11 DE
   MAQUETTE.md — ils sont calés sur le bleu nuit #202b5c, beaucoup plus sombre :

     --text-strong  blanc   5,58  ✓ laissé tel quel, c'est le corps de texte
     --text-accent  fluo    4,79  ✓ laissé tel quel — et c'est LUI qui rend enfin
                                    possible une mise en valeur dans cette boîte,
                                    ce que l'aplat clair interdisait (fluo à 1,82)
     --text-muted   casper  2,70  ✗ passé au BLANC PUR, comme sur les boîtes
                                    violette, rose et orange

   ⚠️ LE FLUO EST LE RÔLE QUI CONTRAINT LE JETON, pas le blanc. À un cran plus
   clair (#007a70) le blanc passerait encore (5,20) et le fluo tomberait à 4,46.
   C'est lui qu'il faut remesurer avant de toucher à la teinte.

   ⚠️ CE QUE CE PASSAGE AU SOMBRE A EFFACÉ : trois reprises d'encre qui n'ont plus
   d'objet, et qu'il ne faut pas rétablir. --text-muted et --text-accent étaient
   ramenés sur --c-text-main parce que le fiord tombait à 2,74 et le bleu à 2,22
   sur l'aplat clair — et parce que `.ton-page--dictionnaire` passait --text-accent
   au vert du pilier lui-même, soit vert sur vert. Sur le fond sombre, `.on-dark`
   reprend ces deux rôles après la classe de page et le problème disparaît de
   lui-même.

   ⚠️ `--boite-ton` EST LÀ POUR L'ATELIER, et pour rien d'autre en production :
   c'est le même point d'entrée que la boîte orange (voir /labo/orange-sombre/),
   qui permet de monter la même boîte dans plusieurs teintes candidates en
   surchargeant cette seule variable. Le jeton reste la valeur par défaut. */
.texte-module--dictionnaire .texte-module__boite {
  --boite-dictionnaire: var(--boite-ton, var(--c-pilier-dictionnaire-sombre));
  background-color: var(--boite-dictionnaire);
  /* Le fond de la boîte devient le fond de référence : c'est lui que
     `.btn--secondary` recopie pour son aplat. Sans ça le bouton se remplit du
     crème de la page et flotte sur la couleur. */
  --section-bg: var(--boite-dictionnaire);
  --bg-page: var(--boite-dictionnaire);
  /* ⚠️ LE FOND DU GRAPHIQUE EN TRAME, ET RIEN D'AUTRE. Aucune section ne déclare
     de `motif` sur cette boîte aujourd'hui ; la ligne est là pour que la première
     ne sorte pas fausse en silence. C'est ce fond que _components.scss mélange à
     la base pour faire RECULER les deux séries secondaires : laissé à son défaut
     — le bleu nuit — elles reculeraient vers une couleur qui n'est pas celle du
     fond, donc elles AVANCERAIENT, en deux taches. Même défaut que celui corrigé
     sur le donut le 25/08/2026.

     La base, elle, reste au repli fluo de l'atome, qui est le bon ici (4,79). Le
     jour où une section veut la teinte du pilier, c'est une paire à mesurer comme
     celle de la boîte orange, plus bas. */
  --motif-fond: var(--boite-dictionnaire);
  --text-muted: var(--c-white);
  color: var(--text-strong);
}

/* ⚠️ UNE FAILLE RESTE EN SOMMEIL DANS CETTE BOÎTE, et elle vaut d'être écrite :
   `.on-dark` reprend les trois RÔLES d'encre, mais pas --c-blue, que
   `.ton-page--dictionnaire` a passé à l'APLAT du pilier. Un `.btn--primary` ajouté
   dans cette boîte se remplirait donc de #00b3a4 sur #00756c — 1,44 entre les deux
   verts, le bouton s'effacerait comme forme, et son libellé blanc tomberait à 2,63.
   Aucune des trois boîtes vertes n'en porte aujourd'hui (vérifié sur le HTML
   construit le 25/08/2026) ; un `.btn--secondary` va bien, lui, puisqu'il recopie
   --section-bg. Le jour où un bouton principal arrive ici, c'est --btn-primary-bg
   qu'il faut poser sur la boîte avec une valeur mesurée — règle 10 de MAQUETTE.md —
   et non espérer qu'une reprise de rôle y suffise. */
/* ---------- Les deux rôles de l'atome, dans la boîte verte ----------
   Même nécessité de cascade que pour la boîte orange, et c'est la faille que le
   commentaire de cette dernière annonçait « en sommeil » pour celle-ci le
   20/08/2026 : `.item--dictionnaire` écrit --item-ton et --item-encre EN CLAIR sur
   l'atome, et une propriété personnalisée posée SUR un élément l'emporte toujours
   sur la même propriété héritée d'un ancêtre. Déclarée sur la boîte, une remise à
   blanc n'atteindrait donc aucun item.

   La boîte n'en porte aucun aujourd'hui — elle porte une carte d'étiquettes. La
   règle est là pour que le premier item ajouté ne sorte pas en vert du pilier sur
   vert sombre (2,18), ce qui ne se signalerait pas. */
.texte-module--dictionnaire .texte-module__boite .item {
  --item-ton: var(--c-white);
  --item-encre: var(--c-white);
}

/* Le titre aussi : il est en bleu nuit partout ailleurs sur la page, et
   `--c-blue-zodiac` est écrit en clair dans sa règle — aucune réaffectation de rôle
   ne l'atteint. Sur ce vert sombre il tombe à 1,25, illisible.

   La règle existait déjà quand la boîte était claire : Hugo y voulait alors le noir
   du site. Elle passe désormais au blanc sans changer d'écriture, puisqu'elle lit
   --text-strong et qu'`.on-dark` l'a basculé. */
.texte-module--dictionnaire .texte-module__titre,
.texte-module--dictionnaire .texte-module__sous-titre,
.texte-module--dictionnaire .texte-module__module-titre {
  color: var(--text-strong);
}

/* ---------- La boîte violette du connecteur ----------
   Demandée par Hugo le 19/08/2026 pour la section 01 de la page du connecteur IA.

   ⚠️ CELLE-CI EST UN FOND SOMBRE, contrairement à la verte juste au-dessus, et
   c'est mesuré, pas choisi. Le violet #8b5cff est le seul pilier bancal des deux
   côtés — trop sombre pour du texte foncé (3,01 pour --c-text-main), trop clair
   pour du blanc (4,14, il manque 0,36 au seuil de 4,5). Aucune encre ne passe
   dessus tel quel.

   ⚠️ LE VIOLET SOMBRE EST UN JETON DEPUIS LE 26/08/2026 —
   --c-pilier-connecteur-sombre, #724fd9. Cette boîte était restée à 90 % (#7f56ec)
   alors que la brique connecteur de /produit/ était passée à 80 % le 25/08 : les
   deux copies avaient divergé, exactement ce que le « les deux doivent rester
   d'accord » écrit ici ne pouvait pas empêcher. Le calcul, son arbitrage et le fait
   qu'il ne suive PAS l'aplat de bouton de `.ton-page--connecteur` sont dans
   _tokens.scss, en un seul endroit désormais.

   ⚠️ LES DEUX AUTRES RÔLES SONT REPRIS, ET C'EST LA RÈGLE 11 DE MAQUETTE.md :
   `.on-dark` cale ses trois rôles sur le bleu nuit (#202b5c), beaucoup plus
   sombre. Sur ce violet, mesurés —

     --text-strong  blanc   5,47  ✓ laissé tel quel, c'est le corps de texte
     --text-muted   casper  2,65  ✗ passé au BLANC PUR (aucune demi-mesure ne
                                    marche : un blanc voilé à 90 % ne remonte
                                    qu'à 4,24 même sur l'ancien violet)
     --text-accent  fluo    4,70  ✓ TIENT désormais, et reste pourtant au blanc

   ⚠️ CE DERNIER OVERRIDE A PERDU SA JUSTIFICATION AVEC LE PASSAGE À 80 %, et il est
   gardé quand même : le fluo était à 4,05 sous le seuil du texte courant, il est
   maintenant à 4,70 au-dessus. Lui rendre le fluo est une décision d'Hugo à prendre,
   pas un effet de bord à ramasser au passage — c'est ce que la brique de /produit/ a
   déjà tranché de la même façon le 25/08.

   Le titre est repris à part, plus bas : `--c-blue-zodiac` est écrit en clair
   dans sa règle et aucune réaffectation de rôle ne l'atteint. Il donne 3,05 ici. */
.texte-module--connecteur .texte-module__boite {
  --violet-connecteur: var(--c-pilier-connecteur-sombre);
  background-color: var(--violet-connecteur);
  /* Le fond de la boîte devient le fond de référence : c'est lui que
     `.btn--secondary` recopie pour son aplat. Sans ça le bouton se remplit du
     crème de la page et flotte sur la couleur. */
  --section-bg: var(--violet-connecteur);
  --bg-page: var(--violet-connecteur);
  /* ⚠️ MÊME RAISON QUE SUR LA BOÎTE VERTE : aucune section ne déclare de `motif`
     ici, et la ligne est là pour que la première ne sorte pas fausse en silence.
     Laissé à son défaut — le bleu nuit — le mélange ferait AVANCER les deux séries
     secondaires au lieu de les faire reculer. La base reste au repli fluo de
     l'atome, qui passe sur ce violet (4,70).

     ⚠️ ET CE REPLI-LÀ N'EST PAS `--text-accent`, que cette boîte passe au blanc
     juste en dessous : le motif lit `--motif-base`, dont le défaut est --c-fluo.
     Le blanc du texte d'accent ne le suit donc pas, et c'est voulu — une série
     blanche sur ce violet serait un décor plus fort que le corps de texte. */
  --motif-fond: var(--violet-connecteur);
  --text-muted: var(--c-white);
  --text-accent: var(--c-white);
  color: var(--text-strong);
}

/* Même raison que pour la boîte verte : le titre est en bleu nuit partout
   ailleurs, et sur ce violet il tombe à 3,53. Il faut le dire explicitement. */
.texte-module--connecteur .texte-module__titre,
.texte-module--connecteur .texte-module__sous-titre,
.texte-module--connecteur .texte-module__module-titre {
  color: var(--text-strong);
}

/* ---------- La boîte orange sombre des intégrations ----------
   Demandée par Hugo le 20/08/2026 pour la section 02 de /integrations/, en
   remplacement du bleu nuit de la maquette : sur ce pilier, la boîte porte
   l'orange.

   ⚠️ ELLE A CHANGÉ DE CAMP LE JOUR MÊME. Elle a d'abord été un fond CLAIR —
   l'aplat #f0a22e du pilier, encres sombres, aucune mise en valeur possible
   dessus. Hugo l'a corrigée dans la même séance : « je veux que les parties
   dites sombres soient des parties sombres avec du texte blanc, quitte à
   assombrir le orange. » C'est donc un FOND SOMBRE, et la boîte porte
   `.on-dark` comme la violette du connecteur.

   ⚠️ LA TEINTE N'EST PAS L'APLAT DU PILIER, mais sa TROISIÈME valeur,
   --c-pilier-integrations-sombre (#ab4a00, choisi par Hugo le 20/08/2026 parmi
   les candidats de /labo/orange-sombre/). Le raisonnement complet — pourquoi ce
   n'est PAS un mélange vers le bleu nuit, qui désature et donne du brun — est sur
   le jeton, dans _tokens.scss. En deux mots : la clarté descend, la saturation
   monte, la teinte tourne vers le rouge.

   ⚠️ LES TROIS RÔLES DE `.on-dark` SONT MESURÉS ICI, ET C'EST LA RÈGLE 11 DE
   MAQUETTE.md — ils sont calés sur le bleu nuit #202b5c, beaucoup plus sombre :

     --text-strong  blanc   5,66  ✓ laissé tel quel, c'est le corps de texte
     --text-accent  fluo    4,85  ✓ laissé tel quel — et c'est LUI qui fait
                                    revenir la mise en valeur de la maquette
     --text-muted   casper  2,74  ✗ passé au BLANC PUR, comme sur le violet du
                                    connecteur et le rose de la plateforme

   ⚠️ LE FLUO EST LE RÔLE QUI CONTRAINT LE JETON, pas le blanc. À un cran plus
   clair (#b45a00) le blanc passerait encore (4,77) mais le fluo tomberait à 4,09.
   C'est lui qu'il faut remesurer avant de toucher à la teinte.

   ⚠️ `--boite-ton` EST LÀ POUR L'ATELIER, et pour rien d'autre en production :
   /labo/orange-sombre/ monte la même boîte dans plusieurs teintes candidates en
   surchargeant cette seule variable. Le jeton reste la valeur par défaut. */
.texte-module--integrations .texte-module__boite {
  --boite-integrations: var(--boite-ton, var(--c-pilier-integrations-sombre));
  background-color: var(--boite-integrations);
  /* Le fond de la boîte devient le fond de référence : c'est lui que
     `.btn--secondary` recopie pour son aplat. Sans ça le bouton se remplit du
     crème de la page et flotte sur la couleur. */
  --section-bg: var(--boite-integrations);
  --bg-page: var(--boite-integrations);
  --text-muted: var(--c-white);
  /* ---------------------------------------------------------------
     LES TEINTES DU GRAPHIQUE EN TRAME.

     Recopiées MOT POUR MOT de la brique intégrations de /produit/ (voir
     sections/_texte-panneau.scss) : même teinte vive, même fond sombre, donc
     rien à recalculer — et deux valeurs différentes pour un même pilier
     auraient été un défaut à tenir d'accord sans rien pour le signaler.

     ⚠️ LA TEINTE VIVE NE PEUT PAS SERVIR TELLE QUELLE : #f0a22e sur #ab4a00 ne
     pèse que 2,67, une nuance de son propre fond. Elle est donc RELEVÉE VERS LE
     BLANC — 94 % ici, la proportion propre à ce pilier, les quatre ayant des
     luminances différentes. Séries obtenues : 2,80 / 2,28 / 1,76.

     ⚠️ LE FOND LIT `--boite-integrations`, ET JAMAIS LE JETON EN CLAIR. C'est
     lui que _components.scss mélange à la base pour faire RECULER les deux
     séries secondaires : écrit en clair il cesserait de suivre la boîte le jour
     où sa teinte bouge — les deux séries avanceraient alors en deux taches plus
     claires que le fond, exactement le défaut corrigé sur le donut le
     25/08/2026. C'est aussi ce qui fait suivre le motif quand
     /labo/orange-sombre/ surcharge `--boite-ton`. */
  --motif-fond: var(--boite-integrations);
  --motif-base: color-mix(in srgb, var(--c-pilier-integrations) 94%, var(--c-white));
  color: var(--text-strong);
}

/* ---------- Les deux rôles de l'atome, dans la boîte ----------
   ⚠️ LA RÈGLE VISE `.item` ET NON LA BOÎTE, et c'est une nécessité de cascade, pas
   un choix de style. `.item--integrations` écrit --item-ton et --item-encre EN
   CLAIR sur l'atome ; une propriété personnalisée posée SUR un élément l'emporte
   toujours sur la même propriété héritée d'un ancêtre. Déclarée sur la boîte, cette
   remise à blanc n'aurait donc jamais atteint un seul item — sans le moindre
   signal, et avec un commentaire affirmant le contraire. C'est exactement le piège
   décrit dans CLAUDE.md, « un bouton de composant qu'un ancêtre doit pouvoir
   surcharger ».

   Trois classes contre une : celle-ci gagne, sur le même élément.

   ⚠️ LES DEUX AUTRES BOÎTES DE COULEUR ONT LA MÊME FAILLE EN SOMMEIL. Ni la verte
   du dictionnaire ni la violette du connecteur ne contient d'item aujourd'hui
   (vérifié sur le HTML construit le 20/08/2026), donc rien ne se voit — mais leur
   remise à plat de --text-accent ne suffirait pas le jour où l'une en recevra un.
   À traiter comme ici, avec une règle sur `.item`, et non en croyant qu'un rôle
   redéclaré sur la boîte descend jusque-là. */
.texte-module--integrations .texte-module__boite .item {
  --item-ton: var(--c-white);
  --item-encre: var(--c-white);
}

/* Même raison que pour les deux autres boîtes de couleur : le titre est en bleu
   nuit partout ailleurs sur la page, et `--c-blue-zodiac` est écrit en clair
   dans sa règle — aucune réaffectation de rôle ne l'atteint. Sur cet orange
   sombre il tombe à 2,95, illisible. */
.texte-module--integrations .texte-module__titre,
.texte-module--integrations .texte-module__sous-titre,
.texte-module--integrations .texte-module__module-titre {
  color: var(--text-strong);
}

/* ⚠️ CETTE RÈGLE DÉCENTRAIT LA BOÎTE, et c'est corrigé le 11/09/2026.

   L'intention était juste : sous 96rem, il faut ménager une gouttière pour que
   la boîte cesse de toucher les deux bords. Mais remplacer `margin-inline: auto`
   par une valeur FIXE ne fait ça que tant que la boîte remplit la place. Dès que
   sa largeur maximale (88rem = 1 197 px) est atteinte avant les bords — c'est le
   cas entre 1 278 et 1 536 px de fenêtre — elle se colle à gauche et laisse tout
   le reste à droite. Mesuré à 1 440 px : 41 px à gauche, 187 à droite.

   Le défaut touchait les DEUX boîtes de /a-propos/ autant que celle de /tarifs/ ;
   il a été vu sur cette dernière par Céline.

   La marge reste donc `auto`, et c'est la largeur maximale qui se resserre :
   `min()` prend la plus petite des deux, donc 88rem au large et « la place
   disponible moins deux gouttières » en dessous. La boîte est centrée dans les
   deux cas. */
@media (max-width: 96rem) {
  .texte-module--boite .texte-module__boite {
    max-width: min(88rem, 100% - 2 * var(--container-pad));
  }
}
/* La section qui porte une boîte n'a plus à respirer autant : la boîte a son
   propre rembourrage vertical. Sans ça, on cumulait les deux. */
.texte-module--boite {
  padding-block: clamp(var(--sp-6), 2.4vw, var(--sp-10));
}

/* ---------- En-tête ----------
   HORS DU CADRE, au titre de l'exception prévue par la règle du panneau fermé :
   le cadre n'enferme qu'un module qui porte déjà sa charge. La règle et son
   exception vivent dans `.cadre` (_components.scss).

   Le titre ne déclare rien de sa police : _base.scss pose déjà le titrage et la
   taille du niveau. */
.texte-module__entete {
  margin-block-end: clamp(var(--sp-6), 2.4vw, var(--sp-10));
}

/* ---------- Disposition « tiers » ----------
   Tout l'en-tête — titre, texte et liens — sur les DEUX TIERS de gauche, le corps sur
   le tiers de droite. Demandé par Hugo le 18/08/2026 pour la section « cœur », où le
   corps est une animation qui accompagne un bloc de texte entier.

   ⚠️ CE N'EST PAS `colonnes` AVEC UN AUTRE RATIO, et c'est pour ça que les deux
   existent. `colonnes` fait passer le TITRE sur toute la largeur pour que le corps
   s'aligne sur le TEXTE — d'où son `display: contents` sur l'en-tête et le placement
   explicite de chaque enfant, juste en dessous. Ici le titre reste DANS la colonne de
   gauche, donc l'en-tête reste un bloc et il n'y a rien à démonter : deux enfants,
   deux colonnes.

   `align-items: center` : le visuel se cale sur le milieu du bloc de texte, qui est
   plus haut que lui. */
@media (min-width: 60rem) {
  .texte-module--tiers .texte-module__inner {
    display: grid;
    grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
    column-gap: clamp(var(--sp-8), 3.2vw, var(--sp-16));
    align-items: center;
  }
  .texte-module--tiers .texte-module__entete {
    margin-block-end: 0;
  }
}
/* ---------- Disposition en colonnes ----------
   L'en-tête à gauche, le module à droite, côte à côte — au lieu de l'en-tête en
   haut et du module dessous. C'est la disposition de la boîte verte, demandée par
   Hugo le 18/08/2026, et c'est aussi celle que la maquette donne à cette section.

   La grille est posée sur `__inner`, qui est déjà le `.container` : l'en-tête et
   le corps sont ses deux enfants directs, donc ils deviennent les deux colonnes
   sans qu'on ajoute une enveloppe.

   ⚠️ La marge basse de l'en-tête est annulée : en colonnes elle ne sépare plus
   rien verticalement, elle décalait juste la colonne de gauche vers le haut. */
@media (min-width: 60rem) {
  /* ⚠️ `column-gap` SEUL, ET `row-gap: 0`. Ne pas revenir à un `gap` unique.
     Il y en avait un jusqu'au 18/08/2026, et il creusait un trou énorme entre le
     titre et le texte : les rangées sont réservées pour le sous-titre, les liens
     et la chute, qui sont tous FACULTATIFS. Une rangée vide reste une rangée, et
     la gouttière s'applique de part et d'autre — deux gouttières de 4rem pour un
     sous-titre absent, soit 8rem de vide sans rien dedans.

     À `row-gap: 0`, une rangée vide ne coûte plus rien, et l'espacement vertical
     revient aux marges des éléments eux-mêmes — exactement comme hors colonnes. */
  .texte-module--colonnes .texte-module__inner {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    column-gap: clamp(var(--sp-8), 3.2vw, var(--sp-16));
    row-gap: 0;
    align-items: start;
  }
  /* ⚠️ `display: contents` FAIT DISPARAÎTRE LA BOÎTE DE L'EN-TÊTE, et c'est le
     but : ses enfants — titre, sous-titre, texte, liens — deviennent alors des
     enfants directs de la grille, donc plaçables individuellement. Sans ça
     l'en-tête est un bloc unique et le module ne peut s'aligner que sur son haut,
     c'est-à-dire sur le TITRE.

     Hugo a demandé le 18/08/2026 que le module s'aligne sur le TEXTE et non sur le
     titre. Le titre passe donc sur toute la largeur, en première rangée, et le
     texte et le module se partagent la seconde — ils démarrent ainsi exactement à
     la même hauteur, sans qu'aucune valeur ne soit devinée.

     Les alternatives écartées : une marge haute sur le module égale à la hauteur
     du titre (indevinable, le titre est en `clamp()` et se replie selon la
     largeur), et `align-self` (qui aligne des bords de rangée, pas le contenu).

     Chaque enfant est placé EXPLICITEMENT. Le placement automatique aurait suffi
     tant que le bloc n'a ni sous-titre ni liens — ce qui est le cas de la boîte
     verte — mais aurait glissé d'une rangée dès qu'une page en déclare un. */
  .texte-module--colonnes .texte-module__entete {
    display: contents;
  }
  /* La rangée 1 est celle du badge — voir la note de `.texte-module__badge`. */
  .texte-module--colonnes .texte-module__badge {
    grid-column: 1/-1;
    grid-row: 1;
  }
  .texte-module--colonnes .texte-module__titre {
    grid-column: 1/-1;
    grid-row: 2;
    margin-block-end: clamp(var(--sp-4), 1.6vw, var(--sp-6));
  }
  .texte-module--colonnes .texte-module__sous-titre {
    grid-column: 1/-1;
    grid-row: 3;
  }
  .texte-module--colonnes .texte-module__texte {
    grid-column: 1;
    grid-row: 4;
    margin-block-start: 0;
  }
  .texte-module--colonnes .texte-module__corps {
    grid-column: 2;
    grid-row: 4;
  }
  .texte-module--colonnes .texte-module__liens {
    grid-column: 1;
    grid-row: 5;
  }
  /* La chute reste sous les DEUX colonnes si une page en déclare une. Elle n'est
     pas employée par la boîte verte — ses deux textes sont à la suite dans
     `paragraphes` — mais le bloc ne doit pas casser si elle revient. */
  .texte-module--colonnes .texte-module__chute {
    grid-column: 1/-1;
    grid-row: 6;
  }
  .texte-module--colonnes .texte-module__chute-rangee {
    grid-column: 1/-1;
    grid-row: 7;
  }
}
/* ---------- Disposition « duo » ----------
   Le TITRE SEUL dans une colonne étroite à gauche, tout le reste du texte dans une
   colonne large à droite. Ajoutée le 19/08/2026 pour la section 01 de la page
   plateforme (Figma 141:8124), où la maquette compose « Un outil de dashboards
   affiche des chiffres. Le nôtre affiche les bons. » en serif sur cinq lignes
   étroites, face au bloc de paragraphes.

   ⚠️ CE N'EST NI `colonnes` NI `tiers`, et les trois coexistent parce qu'elles
   répondent à trois questions différentes :

     colonnes  le titre passe sur toute la largeur, pour que le CORPS s'aligne sur
               le TEXTE ;
     tiers     tout l'en-tête (titre compris) tient à gauche, le CORPS à droite ;
     duo       le titre est seul à gauche, le TEXTE passe à droite.

   `display: contents` fait disparaître la boîte de l'en-tête : ses enfants
   deviennent des enfants directs de la grille, donc plaçables un par un. C'est le
   même mécanisme que `colonnes`, et il est nécessaire pour la même raison — sans
   lui l'en-tête est un bloc unique et le titre ne peut pas quitter le texte.

   ⚠️ `column-gap` SEUL, ET `row-gap: 0` — même piège que dans `colonnes` : les
   rangées du sous-titre, des liens et de la chute sont toutes FACULTATIVES, et une
   rangée vide reste une rangée. Une gouttière unique creuserait donc des trous
   entre des éléments absents. */
@media (min-width: 60rem) {
  /* ⚠️ DEUX COLONNES ÉGALES, et non le tiers/deux tiers de la maquette. Demandé
     par Hugo le 19/08/2026. Le titre y gagne de la place pour se replier en serif
     sur moins de lignes, et les deux blocs se répondent au lieu que l'un domine. */
  .texte-module--duo .texte-module__inner {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: clamp(var(--sp-8), 3.2vw, var(--sp-16));
    row-gap: 0;
    align-items: start;
  }
  .texte-module--duo .texte-module__entete {
    display: contents;
  }
  /* Le titre couvre les trois rangées de la colonne de droite : il reste ainsi
     collé en haut (grâce à `align-items: start`) quelle que soit la longueur du
     texte en face, sans qu'aucune hauteur ne soit devinée. */
  /* ⚠️ LE BADGE RESTE DANS LA COLONNE DU TITRE, pas au-dessus des deux : c'est le
     titre qu'il annonce, et la colonne de droite est du corps de texte. */
  .texte-module--duo .texte-module__badge {
    grid-column: 1;
    grid-row: 1;
  }
  .texte-module--duo .texte-module__titre {
    grid-column: 1;
    grid-row: 2/span 3;
  }
  .texte-module--duo .texte-module__sous-titre {
    grid-column: 2;
    grid-row: 2;
  }
  .texte-module--duo .texte-module__texte {
    grid-column: 2;
    grid-row: 3;
    margin-block-start: 0;
  }
  .texte-module--duo .texte-module__liens {
    grid-column: 2;
    grid-row: 4;
  }
  /* Le corps et la chute restent sous les deux colonnes. La section 01 de la page
     plateforme n'a ni l'un ni l'autre — elle n'est que du texte — mais le bloc ne
     doit pas casser si une page en déclare. */
  .texte-module--duo .texte-module__corps,
  .texte-module--duo .texte-module__chute,
  .texte-module--duo .texte-module__chute-rangee {
    grid-column: 1/-1;
  }
  /* ⚠️ LA GOUTTIÈRE DE RANGÉE VAUT 0 DANS CETTE DISPOSITION (voir la note du
     `column-gap` seul), donc le corps se collait au texte au-dessus de lui : la
     marge basse de `.texte-module__entete` ne s'applique pas, l'en-tête étant en
     `display: contents`. Demande d'Hugo le 21/08/2026 sur la section « Le défi » de
     /cas-clients/hardloop/ — « plus d'écart entre le titre et la suite ».

     C'est la même valeur que la marge du module dans `portrait`, pour que les deux
     dispositions respirent pareil. Aucune page ne régresse : /plateforme/, seule
     autre page en `duo`, n'a pas de corps. */
  .texte-module--duo .texte-module__corps {
    grid-row: 5;
    margin-block-start: clamp(var(--sp-8), 3vw, var(--sp-12));
  }
  .texte-module--duo .texte-module__chute {
    grid-row: 6;
  }
  .texte-module--duo .texte-module__chute-rangee {
    grid-row: 7;
  }
}
/* ---------- Disposition « pleine » ----------
   L'en-tête en haut, puis le VISUEL SUR TOUTE LA LARGEUR. Ajoutée le 19/08/2026
   pour la section 04 de la page du connecteur IA (Figma 141:7385), où la
   maquette pose un cadre vidéo de 1190 × 670 sous le titre et son paragraphe.

   ⚠️ IL N'Y A AUCUNE RÈGLE DE GRILLE ICI, ET CE N'EST PAS UN OUBLI. Sans module,
   `.texte-module__corps` est déjà en une seule colonne — son passage à deux
   pistes est conditionné par un `:has()` qui exige les DEUX enfants. Le visuel
   occupe donc déjà toute la largeur, et une grille de plus ne changerait rien.

   Ce que cette disposition change vraiment, c'est le RATIO du cadre en attente :
   410 × 355 est le format d'une illustration posée de côté, et une vidéo de
   démonstration livrée dedans aurait fait sauter toute la page. C'est aussi ce
   qui rend l'intention lisible dans le YAML au lieu de la faire dépendre de
   l'absence d'un `module` — un lecteur ne devine pas qu'un champ manquant est
   une décision de mise en page.

   Le `.texte-module__visuel` reprend `align-self: stretch` : la règle générale
   plus bas le centre verticalement pour l'accoler à une liste plus haute que
   lui, ce qui n'a aucun sens quand il est seul et pleine largeur. */
.texte-module--pleine .texte-module__corps > .texte-module__visuel {
  align-self: stretch;
}

.texte-module--pleine .texte-module__placeholder,
.texte-module--pleine .texte-module__photo,
.texte-module--pleine .texte-module__video {
  aspect-ratio: 1190/670; /* les cotes de la maquette (Figma 141:7385) */
}

/* ---------- Disposition « rangee » ----------
   L'en-tête en haut, le VISUEL au milieu sur toute la largeur, et les ENTRÉES DU
   MODULE SUR UNE RANGÉE en dessous. Ajoutée le 25/08/2026 pour la section 03 du
   dictionnaire, sur sa nouvelle maquette — demande d'Hugo : « une approche centrée
   au milieu, avec les items sur une ligne plutôt que dans une colonne ».

   ⚠️ ELLE SE COMBINE AVEC `centre`, ET C'EST LA DEMANDE EN DEUX MOITIÉS : `centre`
   centre l'en-tête, `rangee` agence le corps. Aucune des deux ne fait le travail de
   l'autre, et c'est pour ça qu'elles ne sont pas une seule disposition — voir le long
   commentaire de `centre` plus bas.

   ⚠️ LE VISUEL PASSE AVANT LE MODULE DANS LE DOCUMENT, ce n'est pas une `order` :
   le gabarit l'appelle par la macro `visuelModule` avant le module. Une `order`
   aurait fait diverger ce qu'on voit de ce qu'on entend. Rien à faire ici, donc — la
   seule chose que cette feuille doit défaire, c'est la grille à deux pistes. */
/* ⚠️ LE CORPS RESTE EN UNE COLONNE, et c'est le `:not()` de la règle à deux pistes
   plus haut qui l'obtient — pas une surcharge ici. Les deux enfants (visuel, module)
   sont bien présents, donc le `:has()` de cette règle matcherait ; et il porte trois
   classes, donc une surcharge de spécificité égale aurait dépendu de l'ordre du
   fichier. L'exclusion est explicite là-bas. */
/* Le schéma ne prend pas toute la largeur : la maquette le pose sur environ 70 % du
   conteneur, centré, les quatre boîtes d'équipes définissant sa mesure. Au-delà, la
   fiche s'étire et son chiffre part très loin de son intitulé.

   `align-self` est remis à `stretch` pour la même raison que `pleine` : la règle
   générale centre le visuel VERTICALEMENT pour l'accoler à une liste plus haute que
   lui, ce qui n'a aucun sens quand il est seul sur sa rangée. */
/* ⚠️ `justify-self` ET NON `margin-inline: auto`, alors que c'est la façon de centrer
   partout ailleurs dans cette feuille : le visuel est un enfant DIRECT d'une grille,
   et des marges automatiques y absorbent l'espace libre en désactivant l'alignement —
   la largeur employée devient alors difficile à prévoir. `inline-size: 100%` borné
   par `max-width` ne laisse aucune ambiguïté : pleine colonne en dessous de 56rem,
   56rem centrées au-dessus. */
/* La mesure du visuel est une VARIABLE et non une valeur en ligne : le commentaire
   des formes de décor chiffre le dégagement qu'elle laisse de chaque côté, donc les
   deux se lisent ensemble. Recopiée, elle dériverait au premier réglage. */
.texte-module--rangee {
  --rangee-visuel: 56rem;
}

.texte-module--rangee .texte-module__corps > .texte-module__visuel {
  align-self: stretch;
  justify-self: center;
  inline-size: 100%;
  max-width: var(--rangee-visuel);
}

/* =========================================================================
   LES FORMES DE DÉCOR — l'ancrage `page`, celui des gouttières, EST UNE PRIMITIVE
   PARTAGÉE depuis le 26/08/2026 : partials/formesDecor.njk pour le balisage et les
   deux rendus, `.decor-formes*` dans _components.scss pour la géométrie — l'arête,
   les deux bandes, les tailles et les plateaux de grain, avec le pourquoi de chaque
   cote. Elle a été écrite ici le 25/08/2026 puis extraite quand `comparatif` a
   demandé le même décor : recopiée, elle aurait laissé deux jeux de cotes à tenir en
   accord sans rien pour signaler leur divergence.

   Ne reste ici que ce que la primitive ne peut pas savoir :

     `rangee`   la seule composition du bloc qui borne le côté gauche par autre
                chose que le conteneur — voir plus bas, après l'ancrage `tableau`.
     `tableau`  un SECOND ancrage, propre à ce bloc : les formes y enjambent les
                bords du PANNEAU du module et non ceux de la page.

   Les deux ancrages partagent les classes de forme de la primitive et n'ont ni le
   même parent ni la même géométrie : chaque règle est donc portée par un COMBINATEUR
   ENFANT qui la réserve à l'un des deux. Sans lui, `--droite` recevrait des retraits
   contradictoires des deux côtés à la fois.
   ========================================================================= */
/* =========================================================================
   L'ANCRAGE `tableau` — les formes à cheval sur les bords du panneau.

   Demandé par Hugo le 26/08/2026 pour « Une infrastructure… » de /plateforme/ :
   « les formes à cheval sur les bords du tableau, plutôt un vers le coin bas gauche,
   un à droite en haut et une sur le côté droit ». Chacune enjambe UN bord — le
   gauche, le haut, le droit — ce qui les répartit sans qu'aucune ne puisse en
   rencontrer une autre : rien à empêcher, donc pas de colonne comme dans l'ancrage
   des gouttières.

   ⚠️ CE QUI FAIT « À CHEVAL » EST L'EMPILEMENT, PAS LA POSITION. La moitié intérieure
   de chaque forme passe DERRIÈRE le panneau, qui l'efface ; seule la moitié
   extérieure se voit, tranchée net par l'arête. C'est la construction de
   `temoignages` — « deux formes émergent SOUS la boîte, une la coiffe ». Par-dessus,
   ces aplats couvriraient le texte des cases, et aucune teinte ne rattrape un texte
   bleu nuit sur un aplat rouge.

   Le montage tient en deux règles et une contrainte de balisage :

     — `__corps` devient un CONTEXTE D'EMPILEMENT (`isolation: isolate`). Sans lui,
       une forme en `z-index: -1` remonte jusqu'au contexte racine, où elle se range
       SOUS l'aplat opaque de la section : invisible. C'est le piège que l'ancrage
       des gouttières signale déjà pour s'être interdit tout `z-index`.

     — dans ce contexte, une forme positionnée en `z-index: -1` peint à l'étape 2,
       et le fond du panneau — bloc en flux, non positionné — à l'étape 3. Le panneau
       la recouvre donc SANS QU'ON AIT À LUI DONNER DE `z-index`, ce qui laisse
       intacte sa participation au reste.

     — les formes sont enfants de `__corps` et NON du panneau. Le panneau porte
       `data-reveal`, donc une opacité qui transite de 0 à 1 : le temps de la
       transition il fabrique son propre contexte d'empilement, et une forme en
       `z-index: -1` à l'intérieur de lui repasserait au-dessus de son fond blanc.

   ⚠️ ELLES ÉPOUSENT LES BORDS DE `__corps`, qui ne sont ceux du panneau que si le
   module en est l'ENFANT UNIQUE. C'est le cas de la seule section qui s'en sert, qui
   n'a ni paragraphes ni visuel. Rien ne le vérifie.

   ⚠️ LE DÉCALAGE PASSE PAR `translate` ET NON PAR `transform`, pour deux raisons qui
   vont dans le même sens : `.forme` emploie déjà `transform` pour sa rotation, et
   reveal.js pose `transform: none` sur tout élément révélé — ce sont les deux mêmes
   raisons qui font préférer `translate` partout ailleurs sur le site.
   ========================================================================= */
.texte-module__corps:has(> .decor-formes__forme) {
  /* Le bloc conteneur des formes — sans lui elles se mesureraient à la section.
     La trame, elle, s'ancre sur `__inner` : voir sa règle plus bas. */
  position: relative;
  /* Et le contexte d'empilement, sans lequel un `z-index: -1` remonte jusqu'à la
     racine et se range SOUS l'aplat opaque de la section. */
  isolation: isolate;
}

/* ---------- ET LE TITRE PASSE AU-DESSUS ----------
   Hugo, le 26/08/2026 : « des shapes [...] derrière le titre ». La forme qui coiffe
   l'arête HAUTE la franchit de la moitié de sa hauteur — environ 76 px à 14 % d'un
   panneau de 1088 — contre une marge basse d'en-tête de 34 à 40 px : elle entre donc
   d'une quarantaine de pixels dans la bande du titre, et en APLAT elle le couvrirait.

   ⚠️ LE `z-index: -1` DE LA FORME N'Y SUFFIT PAS, et c'est le piège. Il la range
   sous le contenu de `__corps`, mais `__corps` est lui-même positionné et porte un
   `isolation: isolate` : il peint donc EN BLOC, après le contenu en flux non
   positionné de l'en-tête. Tout ce qui déborde de lui vers le haut arrive par-dessus
   le titre, aussi bas que soit son `z-index` à l'intérieur.

   Le remède est celui de `.chemin` : un `z-index` EXPLICITE sur le frère à protéger.
   C'est aussi ce que la trame quadrillée évite en s'ancrant sur `__inner`, dont
   l'en-tête est un enfant — mais cet ancrage-ci doit épouser les bords du PANNEAU, et
   les quatre cotes verticales des formes s'y mesurent. On déplace le titre, pas les
   formes.

   ⚠️ PORTÉ PAR UN `:has()` SUR `__inner` et non posé sur l'en-tête de tout le bloc :
   l'en-tête est AVANT `__corps` dans le document, donc aucun combinateur de frère ne
   l'atteint depuis lui. Et le restreindre aux sections qui portent des formes évite
   de donner un contexte d'empilement à tous les en-têtes du site pour rien. */
.texte-module__inner:has(> .texte-module__corps > .decor-formes__forme) .texte-module__entete {
  position: relative;
  z-index: 1;
}

.texte-module__corps > .decor-formes__forme {
  position: absolute;
  z-index: -1;
  /* Une part de la largeur du PANNEAU, et non de la section : c'est le panneau que
     la forme enjambe, donc c'est à lui qu'elle doit se mesurer. */
  --shape-w: 14%;
}

/* Le bord GAUCHE, bas — « vers le coin bas gauche ». Elle n'est pas posée SUR le
   coin : centrée dessus, elle n'en montrerait qu'un quart. */
.texte-module__corps > .decor-formes__forme--bas-gauche {
  inset-inline-start: 0;
  inset-block-end: 9%;
  translate: -50% 0;
}

/* Le bord HAUT, vers la droite. */
.texte-module__corps > .decor-formes__forme--haut-droite {
  inset-inline-end: 11%;
  inset-block-start: 0;
  translate: 0 -50%;
}

/* Le bord DROIT, à mi-hauteur. */
.texte-module__corps > .decor-formes__forme--droite {
  inset-inline-end: 0;
  inset-block-start: 40%;
  translate: 50% 0;
}

/* Le bord GAUCHE, haut — déclaré pour que les quatre clés de position aient un
   ancrage, même si aucune page ne s'en sert aujourd'hui. */
.texte-module__corps > .decor-formes__forme--haut-gauche {
  inset-inline-start: 0;
  inset-block-start: 9%;
  translate: -50% 0;
}

/* ---------- Le bord BAS — deux formes qui ÉMERGENT DE SOUS le panneau ----------
   Hugo, le 26/08/2026 : « des shapes fluo sous le tableau de Ni un simple logiciel,
   ni une agence, ni un cabinet », pour la section 03 de /a-propos/.

   ⚠️ « SOUS » SE LIT COMME DANS `temoignages`, ET C'EST LE VOCABULAIRE DU SITE.
   L'en-tête de ce bloc-là le dit mot pour mot — « deux formes émergent SOUS la
   boîte » — pour exactement cette géométrie : la moitié haute passe derrière le
   panneau, la basse en sort. L'autre lecture, deux formes POSÉES dans la bande de
   crème sous la section, n'a aucun précédent : entièrement visibles, elles se
   liraient comme deux objets égarés entre deux sections, et c'est justement ce que
   l'ancrage à cheval évite.

   ⚠️ LES CLÉS S'APPELLENT `sous*` ET NON `bas*`, PARCE QUE `basGauche` EST PRIS —
   il désigne le bord GAUCHE, en bas. Le nommage de cet ancrage est hétérogène depuis
   le départ (`hautDroite` nomme le bord HAUT et une position le long de lui,
   `basGauche` l'inverse) ; `basDroite` aurait donc menti pour une lectrice sur deux.
   `sous` nomme le bord bas sans collision, et c'est le mot d'Hugo.

   ⚠️ DEUX FORMES SUR UNE MÊME ARÊTE SONT DÉPAREILLÉES SUR TROIS AXES — largeur,
   profondeur d'émergence, retrait horizontal. C'est la règle de `temoignages` :
   « à pas régulier, l'œil attrape l'alignement et la rangée se lit comme une frise ».
   Pas de rotation en plus : les trois autres bords de cet ancrage n'en portent
   aucune, et trois axes d'irrégularité suffisent.

   ⚠️ LES DEUX LARGEURS RESTENT ≥ 14 %, le plancher éprouvé de la trame expliqué plus
   bas : à 14 % du panneau la moitié visible fait 7 % de sa largeur. Les dépareiller
   VERS LE HAUT (14 et 16 %) plutôt que de part et d'autre — en dessous, la trame
   n'a plus assez de surface et la forme se lit comme un pointillé rompu.

   IL Y A LA PLACE : le panneau fait ~1088 px au large, donc ~152 et ~174 px de
   forme, dont la moitié émerge (~70 et ~90 px) dans les 102 px de padding bas de la
   section — suivis des 102 px de padding haut de la suivante avant sa boîte sombre.
   Sous 1247 px les deux rétrécissent ensemble, le padding étant un `clamp()` en `vw`
   et la forme une part de la largeur du panneau. Rien n'est rogné : `.texte-module`
   ne porte un `overflow` que sur l'axe INLINE. */
.texte-module__corps > .decor-formes__forme--sous-gauche {
  --shape-w: 16%;
  inset-inline-start: 11%;
  inset-block-end: 0;
  translate: 0 46%;
}

.texte-module__corps > .decor-formes__forme--sous-droite {
  --shape-w: 14%;
  inset-inline-end: 18%;
  inset-block-end: 0;
  translate: 0 54%;
}

/* ---------- Le plateau de grain, décalé vers la moitié VISIBLE ----------
   Écrites le 26/08/2026 pour les sections 01 et 05 de /pourquoi-biron/, qui les ont
   employées une demi-heure : Hugo a demandé « des shapes fluo autour du tableau »,
   puis « des shapes pleine et pas halftone ». Elles ont donc vécu un moment SANS
   APPELANT, gardées parce que `remplissage` vaut `trame` PAR DÉFAUT — et c'est la
   section 03 de /a-propos/ qui est passée droit dessus le même jour, « des shapes
   fluo sous le tableau », sans rien préciser d'autre que deux clés de position.
   C'était bien le chemin par défaut d'un champ vivant, pas un interrupteur sans
   câble — le même motif que `--haut-gauche` plus haut. L'aplat n'a pas de moitié
   diluée et ignore ces bornes.

   C'est la correction que l'ancrage `page` fait déjà, transposée : `formeTrame`
   culmine au CŒUR de la forme, et ici la moitié intérieure passe derrière le
   panneau. Sans décalage, la densité tombe là où personne ne la voit et la moitié
   visible se réduit à la frange diluée de la trame.

   ⚠️ CE QUI CHANGE PAR RAPPORT À L'ANCRAGE `page`, c'est que la moitié visible est
   l'EXTÉRIEURE — la forme émerge de derrière le panneau au lieu de sortir de
   l'écran — donc les bornes des deux ancrages sont en MIROIR l'une de l'autre pour
   un même côté. Ne pas les recopier d'un ancrage à l'autre.

   ⚠️ ET LE BORD HAUT DEMANDE UN AXE VERTICAL, ce dont l'ancrage `page` n'a jamais
   eu besoin : ses deux bandes sont latérales. `0deg` fait monter le dégradé, donc
   les pourcentages hauts sont EN HAUT — la moitié visible pour une forme qui coiffe
   l'arête supérieure.

   Les cotes sont celles de l'ancrage `page`, à l'identique : c'est le même atome et
   la même proportion cachée (la moitié), il n'y avait rien à remesurer.

   ⚠️ `--shape-w` NE BOUGE PAS pour autant, et c'est ce qui rend ces bornes suffisantes
   à elles seules. À 14 % du panneau la moitié visible fait 7 % de sa largeur —
   exactement ce que donne la plus petite forme en trame de l'ancrage `page` (88 %
   d'une colonne à 16 %, moitié hors écran). La trame y est donc déjà à son plancher
   éprouvé, et l'agrandir n'était pas ce qui manquait. À l'inverse, l'ancrage `page`
   RECOTE bien sa forme de droite en aplat (35 % → largeur de bande) : là c'est la
   trame qui justifiait la taille, ici c'est l'aplat. Les deux ancrages ne se
   recopient pas. */
.texte-module__corps > .decor-formes__forme:not(.decor-formes__forme--plein) {
  /* Bords gauches : le plateau à gauche. */
  --grain-debut: 8%;
  --grain-fin: 45%;
}

.texte-module__corps > .decor-formes__forme--droite:not(.decor-formes__forme--plein) {
  --grain-debut: 55%;
  --grain-fin: 92%;
}

.texte-module__corps > .decor-formes__forme--haut-droite:not(.decor-formes__forme--plein) {
  --grain-angle: 0deg;
  --grain-debut: 55%;
  --grain-fin: 92%;
}

/* Le bord BAS — axe vertical lui aussi, mais plateau EN BAS : c'est le MIROIR de
   `--haut-droite` juste au-dessus, la moitié visible étant l'inférieure. À `0deg` le
   dégradé monte, donc les pourcentages BAS sont en bas. Les deux bornes sont celles
   des bords gauches — même atome, même proportion cachée (la moitié) — seul l'axe
   change, et il est restitué en clair plutôt que hérité de la règle générique :
   l'angle et les bornes ne se lisent que l'un par rapport à l'autre. */
.texte-module__corps > .decor-formes__forme--sous-gauche:not(.decor-formes__forme--plein),
.texte-module__corps > .decor-formes__forme--sous-droite:not(.decor-formes__forme--plein) {
  --grain-angle: 0deg;
  --grain-debut: 8%;
  --grain-fin: 45%;
}

/* =========================================================================
   LA TRAME QUADRILLÉE — Hugo, le 26/08/2026, pour « Des cas d'usage concrets »
   de /connecteur-ia/ : « une grille qui couvre les 1/3 du tableau et qui va vers
   la droite. Cette grille dépasse le tableau en haut et en bas et va jusqu'au
   bord droit de l'écran. » Puis, le même jour, pour « Un plugin utile pour toutes
   vos équipes » de /excel-sheets/ : « mets-le sur 2/3 du tableau en passant bien
   derrière le titre. »

   ⚠️ ELLE PASSE DERRIÈRE LE PANNEAU, tranché par Hugo le même jour. Le tiers
   « couvert » est donc masqué ; ce qu'on voit d'elle est ce qui déborde. Par-
   dessus, elle traverserait le texte des cases et croiserait le quadrillage
   interne du panneau : deux trames au même endroit ne se distinguent plus l'une
   de l'autre.

   ⚠️ ELLE S'ANCRE SUR `__inner`, ET NON SUR `__corps` COMME LES FORMES. C'est la
   seconde demande qui l'y a mise : pour monter derrière le TITRE il aurait fallu,
   depuis `__corps`, coter la hauteur de l'en-tête — laquelle dépend du nombre de
   lignes du titre, donc fausse au premier titre sur deux lignes et sans rien pour
   le signaler. `__inner` a l'en-tête pour premier enfant : partir de son arête
   haute, c'est partir du haut du titre, sans qu'aucune hauteur soit écrite.

   ⚠️ ET CE DÉPLACEMENT SUPPRIME TOUT `z-index` AILLEURS QUE SUR ELLE. Dans le
   contexte d'empilement de `__inner`, un enfant en `z-index: -1` peint à l'étape
   2 — donc AVANT le fond du panneau ET AVANT le texte du titre, qui sont l'un et
   l'autre du contenu en flux non positionné (étapes 3 et 5). Depuis `__corps` il
   aurait fallu relever l'en-tête d'un `z-index: 1`, la parade de `.chemin` :
   `__corps` positionné peint APRÈS le texte du titre, pas avant.

   ⚠️ L'ISOLATION RESTE INDISPENSABLE : sans elle un `z-index: -1` remonte jusqu'au
   contexte racine, où il se range SOUS l'aplat opaque de la section — invisible.
   C'est le même piège que pour les formes.

   ⚠️ LES DEUX TIERS SE MESURENT SUR LE PANNEAU, PAS SUR `__inner`. `__inner` est
   le `.container` : sa boîte de padding est plus large que le panneau de deux
   fois --container-pad, et c'est sur elle que se résout le pourcentage d'un
   `inset-inline-start`. Le calcul reprend donc le padding des deux côtés — aucune
   cote n'est recopiée, c'est le jeton lui-même qui est lu, donc rien à tenir en
   accord le jour où il bouge.

   ⚠️ LE BORD DROIT DE L'ÉCRAN SE CALCULE, IL NE SE DEVINE PAS. `__inner` est
   centré dans la fenêtre (`max-width` + `margin-inline: auto`), donc la distance
   de son arête droite au bord de la fenêtre vaut 50vw moins la moitié de sa propre
   largeur, ce qu'écrit `calc(50% - 50vw)`. Aucune cote de conteneur n'est
   recopiée : rien à tenir en accord le jour où --container-max bouge.

   ⚠️ ET C'EST LA SECTION QUI ROGNE, pas un plafond. `.texte-module` porte
   `overflow-x: clip` : le 100vw compte la barre de défilement que le conteneur ne
   voit pas, donc la trame vise un ou deux pixels PLUS LOIN que le bord et se fait
   couper net dessus. Viser juste aurait laissé un liseré de crème. Corollaire :
   elle ne peut pas fabriquer de barre de défilement horizontale.

   ⚠️ LE DÉBORDEMENT VERTICAL, LUI, EST INTACT — `visible` / `clip` est la seule
   paire mixte que la spécification autorise, et c'est bien ce que la section
   déclare. Le débordement reste dans le `padding-block: --sp-section` de la
   section : il ne mord ni sur le bloc d'avant ni sur celui d'après.

   ⚠️ PAS DE FONDU AUX EXTRÉMITÉS, contrairement à `.faq__grille` et à la trame de
   l'accueil. Ce qu'Hugo a décrit est un rectangle aux arêtes franches — « elle
   dépasse le tableau en haut et en bas » —, pas une trame qui naît du fond. Le
   `--fade-both` reste disponible si l'arbitrage change.
   ========================================================================= */
.texte-module__inner:has(> .texte-module__trame) {
  /* Le bloc conteneur de la trame, ET le contexte d'empilement sans lequel son
     `z-index: -1` remonte jusqu'à la racine et se range sous l'aplat de la
     section. Voir l'en-tête. */
  position: relative;
  isolation: isolate;
}

.texte-module__inner > .texte-module__trame {
  position: absolute;
  z-index: -1;
  /* Les DEUX TIERS DROITS du panneau : la trame commence au tiers de sa largeur.
     Le padding du conteneur est repris des deux côtés — voir l'en-tête. */
  inset-inline-start: calc(var(--container-pad) + (100% - var(--container-pad) * 2) / 3);
  /* Jusqu'au bord de la fenêtre — voir l'en-tête pour le calcul. */
  inset-inline-end: calc(50% - 50vw);
  /* Le débordement en haut et en bas. En haut il se compte depuis l'arête du
     TITRE, `__inner` ayant l'en-tête pour premier enfant. Une seule cote pour les
     deux : la maquette verbale ne distingue pas, et deux `clamp()` recopiés
     dériveraient au premier réglage. */
  inset-block: calc(-1 * clamp(2rem, 4vw, 4rem));
  pointer-events: none;
}

/* ⚠️ SEULE COMPOSITION QUI BORNE LE CÔTÉ GAUCHE PAR AUTRE CHOSE QUE LE CONTENEUR :
   `rangee` bride le schéma à --rangee-visuel et le centre, ce qui élargit la bande
   de crème de (conteneur − schéma) / 2. La forme peut donc entrer bien plus avant —
   11,5rem, ce qui laisse les 54 px de dégagement chiffrés plus haut face au flanc du
   schéma. Dérivée ici plutôt qu'écrite dans la règle de la forme, parce que la même
   forme sert les deux géométries : sur une section sans visuel, 11,5rem la
   poseraient PAR-DESSUS le contenu, et sans `z-index` pour l'en empêcher. */
.texte-module--rangee > .decor-formes__bande--gauche {
  --forme-avancee: 11.5rem;
  /* La cote historique de la zone de gauche du dessin d'Hugo : large, et haute du
     tiers supérieur de la section. Une seule forme l'occupe, donc la colonne reprend
     l'épinglage que cette forme portait avant : par le HAUT, à 9 %, et la hauteur
     suit son contenu.

     ⚠️ `inset-block-start` ET SURTOUT PAS UN `padding-block-start`, qui donnerait un
     tout autre nombre : un pourcentage de retrait se mesure sur la HAUTEUR du bloc
     conteneur, un pourcentage de rembourrage sur sa LARGEUR — y compris sur l'axe
     vertical. Les 9 % du dessin sont une part de hauteur. */
  inline-size: 24%;
  inset-block-start: 9%;
  inset-block-end: auto;
}

.texte-module--rangee .decor-formes__bande--gauche > .decor-formes__forme--haut-gauche {
  --shape-w: 100%;
  align-self: stretch;
}

/* ---------- Les entrées sur une rangée ----------
   `auto-fit` et non `repeat(3, …)` : trois entrées donnent trois colonnes, quatre en
   donneraient quatre, et le repli à deux puis une colonne se fait tout seul. Aucun
   compte n'est donc publié ici — le parti de `reperes` et de la fiche d'identité.

   La borne de 16rem est ce qui garde un intitulé de deux mots sur une ligne et sa
   description sur trois : en dessous, « Une définition unique » se replie et les
   trois colonnes cessent de se lire comme une rangée. */
.texte-module--rangee .texte-module__entrees {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
  gap: clamp(var(--sp-8), 5vw, var(--sp-16));
  align-items: start;
}

/* ⚠️ ANNULE LA MARGE DE LA PILE, exactement comme le fait la grille : le sélecteur
   d'adjacence `.texte-module__entree + .texte-module__entree` s'applique encore ici,
   et sans cette remise à zéro chaque entrée sauf la première serait décalée vers le
   bas à l'intérieur de sa colonne. */
.texte-module--rangee .texte-module__entrees > .texte-module__entree {
  margin-block-start: 0;
}

/* Bleu nuit, comme tous les titres de section du site (`.cadre__titre`,
   `.connecteurs__titre`, `.hero-page__titre`) — et non --text-strong, qui tire
   vers le noir. Hugo a confirmé le 18/08/2026 que les titres NE passent PAS au
   ton du pilier, contrairement aux accents : voir `.ton-page--*` dans _tokens.scss.

   Le bleu nuit ne bascule pas tout seul dans une boîte sombre — `.on-dark` ne
   réaffecte pas --c-blue-zodiac — d'où la règle explicite juste en dessous. */
/* ---------- Le badge de section ----------
   Son habillage vit dans `.badge-section` (_components.scss), la primitive
   partagée avec `cas-clients`. Il ne reste ici que le PLACEMENT — et il y en a
   deux : cette marge basse, parce que `.texte-module__titre` n'a aucune marge
   haute, et un rang explicite dans chacune des trois dispositions en grille.

   ⚠️ LES TROIS DISPOSITIONS EN GRILLE ONT ÉTÉ DÉCALÉES D'UN RANG le 21/08/2026
   pour lui faire place. `colonnes`, `duo` et `portrait` posent `display: contents`
   sur l'en-tête et placent chaque enfant à une rangée écrite en clair : un rang de
   plus au-dessus décale tous les suivants. Le décalage est sans effet quand la
   page ne déclare pas de `surtitre`, parce que `row-gap` vaut 0 dans les trois —
   une rangée vide ne coûte alors rien, ce qui est justement la raison pour
   laquelle il vaut 0 (voir la note de `colonnes`). */
.texte-module__badge {
  margin-block-end: var(--sp-3);
}

.texte-module__titre {
  margin: 0;
  max-width: 30ch;
  color: var(--c-blue-zodiac);
  text-wrap: balance;
}

.texte-module--sombre .texte-module__titre,
.texte-module--sombre .texte-module__sous-titre,
.texte-module--sombre .texte-module__module-titre {
  color: var(--text-strong);
}

/* ---------- Le sous-titre ----------
   ⚠️ CE N'EST PLUS UNE LIGNE EN CHASSE FIXE depuis le 25/08/2026. La nouvelle
   maquette de la section 01 du dictionnaire la compose en GRAS À L'ENCRE DES TITRES,
   un cran au-dessus du corps de texte : « Un data warehouse + des règles métier. »
   est une phrase d'attaque, pas une étiquette de service. Hugo a tranché pour la
   maquette.

   ⚠️ LE RÔLE EN CHASSE FIXE N'A PAS DISPARU DU BLOC, IL A CHANGÉ D'OBJET : c'est
   `.texte-module__annonce` — l'intitulé du diagramme — qui le porte désormais, avec
   exactement l'ancienne écriture de cette règle. La maquette n'en pose jamais deux
   dans la même section, et le champ n'avait que deux usages sur le site (cette
   section et /exemple-blocs/), donc rien d'autre ne régresse.

   La cote est --fs-eyebrow-lg (17→20, puis 20→24 au-delà de 1280) et non une valeur
   écrite ici : c'est déjà l'accroche du hero (`.eyebrow--sans`), le même objet — une
   phrase courte en sans, plus grosse que le corps, à l'encre profonde. Rien de
   nouveau à calibrer.

   ⚠️ LA MESURE FAIT LE REPLI DE LA MAQUETTE, qui coupe après « warehouse ». Sans
   elle la phrase tient sur une seule ligne dans la colonne de droite d'une
   disposition `duo` (40rem), et la mise en valeur perd son détachement. 28ch avec
   `text-wrap: balance` donne les deux lignes équilibrées de la maquette sans qu'aucun
   `||` ne soit écrit dans les données — un saut forcé serait faux à toute autre
   largeur.

   ⚠️ L'ENCRE EST CELLE DES TITRES, donc écrite en clair : aucune réaffectation de
   rôle ne l'atteint. Elle doit être reprise explicitement dans chacune des boîtes de
   couleur du bloc, exactement comme le titre le fait déjà — c'est fait, et les
   quatre listes plus haut la nomment. */
.texte-module__sous-titre {
  margin: var(--sp-4) 0 0;
  max-width: 28ch;
  font-size: var(--fs-eyebrow-lg);
  font-weight: var(--fw-semibold);
  line-height: var(--lh-snug);
  color: var(--c-blue-zodiac);
  text-wrap: balance;
}

/* ⚠️ LE TEXTE RESPIRE PLUS QUAND UN SOUS-TITRE LE PRÉCÈDE — demande d'Hugo du
   25/08/2026 : « ajouter de la marge entre Un data warehouse + des règles métier et
   le texte ensuite ». Depuis que le sous-titre n'est plus une étiquette en chasse
   fixe mais une accroche en gras à l'encre des titres (voir sa règle plus haut), il
   pèse autant qu'un intertitre : les 20 px calibrés pour la suite d'un titre le
   collaient à son propre paragraphe.

   ⚠️ C'EST LA MARGE HAUTE DU TEXTE QUI CHANGE, PAS UNE MARGE BASSE SUR LE
   SOUS-TITRE, et la raison est le mode de mise en page. Dans l'en-tête ordinaire les
   marges verticales de deux frères fusionnent, mais `colonnes`, `duo` et `portrait`
   posent `display: contents` sur l'en-tête : ses enfants deviennent des cases de
   grille, où plus rien ne fusionne. Une marge basse s'y serait ADDITIONNÉE aux 20 px
   du texte, donnant deux écarts différents selon la disposition. La section 01 du
   dictionnaire, seul emploi réel du champ, est justement en `duo`.

   Le sélecteur d'adjacence marche dans les deux cas : la grille place les cases,
   elle ne change pas l'ordre du document. */
.texte-module__sous-titre + .texte-module__texte {
  margin-block-start: clamp(var(--sp-6), 2.5vw, var(--sp-10));
}

/* Le texte libre. --text-strong et non --text-muted : c'est du corps de texte,
   et le corps de texte du site est toujours à l'encre forte. */
.texte-module__texte {
  margin-block-start: var(--sp-5);
  max-width: 68ch;
  color: var(--text-strong);
}

.texte-module__texte p {
  margin: 0;
  font-size: var(--fs-base);
  line-height: var(--lh-normal);
}

.texte-module__texte p + p {
  margin-block-start: var(--sp-4);
}

/* ⚠️ FERRÉS À GAUCHE, sous le texte. `.btn-group` centre ses boutons par défaut —
   c'est ce qu'il faut au bloc `appel`, qui est centré — mais ici la colonne de texte
   est ferrée à gauche et des CTA centrés flottaient au milieu d'elle. Demandé par
   Hugo le 18/08/2026. */
.texte-module__liens {
  margin-block-start: var(--sp-6);
  justify-content: flex-start;
}

/* ---------- Corps : le module et son illustration ----------
   Une seule colonne par défaut. Le passage à deux colonnes n'a lieu QUE si les
   deux enfants existent — d'où le `:has()` plutôt qu'une grille inconditionnelle
   à deux pistes : la section sombre n'a pas d'illustration et la section
   « cœur » n'a pas de module, et l'une comme l'autre aurait laissé une piste
   vide de la moitié de la largeur.

   `:has()` est disponible dans tous les navigateurs visés ; le repli, si jamais
   il ne l'était pas, est la colonne unique — la mise en page reste lisible. */
.texte-module__corps {
  display: grid;
  gap: clamp(var(--sp-6), 2.6vw, var(--sp-10));
  align-items: start;
}

/* ⚠️ `rangee` EST EXCLUE ICI, ET NON SURCHARGÉE PLUS BAS. Dans cette disposition le
   visuel et le module sont bien tous les deux présents — le `:has()` matcherait donc —
   mais ils s'empilent au lieu de se faire face. Une surcharge aurait porté deux classes
   contre les trois de ce sélecteur, ou trois contre trois : dans les deux cas c'est
   l'ordre du fichier qui aurait tranché, ce que ce fichier documente déjà comme un
   piège (voir `--cols-2`). L'exclusion, elle, ne dépend de rien. */
@media (min-width: 60rem) {
  .texte-module:not(.texte-module--rangee) .texte-module__corps:has(> .texte-module__module):has(> .texte-module__visuel) {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  }
}
/* ---------- Le module ----------
   Sur fond clair il porte `.cadre` (fond blanc, filet bleu, coins carrés) : le
   contour vient donc de là, et rien n'est à redéclarer ici. Sur fond sombre
   `.cadre` est absent — voir l'en-tête du gabarit — et le module n'est qu'une
   pile de pastilles à même la bande. */
.texte-module__module {
  /* La cote est une variable et non une valeur en ligne : le déroulé la relit pour
     reprendre le rembourrage que la grille fait perdre au module — voir la règle du
     `:has()` plus bas. Deux `clamp()` recopiés auraient dérivé au premier réglage. */
  --module-pad: clamp(var(--sp-5), 2vw, var(--sp-8));
  padding: var(--module-pad);
}

/* Sans cadre, pas de rembourrage : le module n'est plus un panneau, ses entrées
   doivent s'aligner sur le texte au-dessus d'elles et non être renfoncées d'un
   rembourrage qui n'encadre plus rien. */
.texte-module__module--nu {
  padding: 0;
}

/* Dans une boîte, le module n'est plus un panneau : il ne porte donc aucun
   rembourrage propre, sinon il se décalerait du texte à côté de lui. La règle
   suit `--boite` et non `--sombre` — c'est « il y a une boîte » qui la commande,
   pas la couleur de cette boîte. */
.texte-module--boite .texte-module__module {
  padding: 0;
}

/* ⚠️ LE CORPS RÉDUIT VAUT À TOUTE LARGEUR, et pas seulement au-dessus de `lg` où
   le module passe réellement à mi-colonne. En dessous, la disposition `portrait`
   s'écroule en une colonne et le module reprend toute la largeur — mais il reste
   une liste de faits courts, et remonter son corps à --fs-h3 au seul repli ferait
   grossir les intitulés en rétrécissant l'écran. C'est le bouton documenté sur
   `.item__entete` dans _components.scss. */
.texte-module__module--demi {
  --item-entete-taille: var(--fs-base);
}

/* ⚠️ LES CASES D'UNE GRILLE À MI-LARGEUR SE RESSERRENT AUSSI. Le rembourrage des
   cases (20→32px) est calibré pour une case qui porte un intitulé et un paragraphe ;
   à mi-largeur, une case ne porte plus qu'une étiquette et une valeur de deux mots,
   et le vide autour d'elles pesait plus que leur contenu. Demande d'Hugo le
   21/08/2026 : « la grille dans le client est toujours trop grande par rapport aux
   infos dedans. »

   ⚠️ C'EST LE REMBOURRAGE QUI CHANGE, PAS LA HAUTEUR. Une `min-height` réduite
   aurait été fausse à la première valeur qui se replie sur deux lignes — le défaut
   que la note du `subgrid` décrit juste plus bas. Ici les cases se calent toujours
   sur leur contenu, elles le serrent seulement de plus près. */
.texte-module__module--demi .texte-module__entrees--grille > .texte-module__entree {
  padding: clamp(var(--sp-3), 1.2vw, var(--sp-5));
}

/* La gouttière entre les rangs d'une case suit : à 12px, l'étiquette et sa valeur se
   lisent comme un couple, ce qui est exactement ce qu'elles sont. */
.texte-module__module--demi .item {
  --item-rangs-gap: var(--sp-1);
}

/* ---------- Le corps réduit d'un cran ----------
   Demandé par Hugo le 21/08/2026 pour les quatre cartes de « Le défi » sur
   /cas-clients/hardloop/ — « le texte des cartes écrit plus petit ».

   ⚠️ LE CLAMP EST PLAFONNÉ, IL N'EST PAS REMPLACÉ, et la cote est celle du
   précédent : ce sont les valeurs exactes de `.cas--carte .cas__titre`, 20 → 22 px.
   --fs-h3 va de 22 à 32 ; au-delà du téléphone, ces cartes affichaient donc jusqu'à
   dix pixels de plus que ce que la maquette leur donne. Ce n'est pas le jeton qui
   était faux, c'est son amplitude.

   Reprendre la valeur du précédent plutôt qu'en inventer une troisième : deux
   échelles réduites sur le même site auraient divergé au premier réglage, sans que
   rien ne le signale.

   ⚠️ UN CHAMP ET NON UN SCOPE SUR `module.cartes`, et c'est le point à ne pas
   « simplifier ». Les autres pages à cartes — /a-propos/ (« La plateforme »,
   « L'équipe ») et /integrations/ (« Zapier », « n8n ») — portent des intitulés
   COURTS, de deux ou trois mots, et l'échelle du site leur va. Ici les quatre cartes
   portent des PHRASES ENTIÈRES, parce que la maquette ne donne qu'une phrase par
   constat (règle 8 : on ne comble pas). Cette différence n'est pas exprimable en
   CSS : elle se déclare.

   ⚠️ LA RÉDUCTION EST PLUS DOUCE QUE CELLE DE `demi`, qui descend à --fs-base
   (16→18) : là le module tient une demi-colonne, ici il tient toute la largeur en
   deux cartes. Les deux drapeaux ne visent pas le même resserrement, et `demi`
   emporte déjà le sien — les cumuler n'aurait aucun sens. */
.texte-module__module--compact {
  --item-entete-taille: clamp(1.25rem, 1.1rem + 0.45vw, 1.375rem); /* 20 → 22px */
}

/* La description suit d'un cran, comme `.cas--carte .cas__description` : un
   intitulé réduit au-dessus d'un corps de texte inchangé inverse la hiérarchie de
   la carte. */
.texte-module__module--compact .item__corps p {
  font-size: var(--fs-sm);
}

/* =========================================================================
   LA FICHE D'IDENTITÉ — des couples libellé / valeur, quadrillés.

   Demandée par Hugo le 21/08/2026 pour « Le client » de /cas-clients/hardloop/,
   après deux resserrements de la grille : « la grille peut être encore plus petite,
   ou dans un autre style qui permettrait que ça soit plus compact ».

   ⚠️ CE N'EST PAS LA GRILLE RESSERRÉE UNE TROISIÈME FOIS, et c'est ce qui la rend
   deux fois plus courte. La grille passe par l'atome `item` : chaque case y porte un
   rembourrage de panneau, un rang d'intitulé en serif à l'échelle d'un titre, un rang
   de texte, et l'alignement de tout ça en `subgrid`. Une fiche n'a que deux lignes
   par case — un libellé de service et sa valeur — et rien de cet appareil. À contenu
   égal, la hauteur passe d'environ 270 à 180 px.

   ⚠️ LE QUADRILLAGE SE FAIT PAR LA GOUTTIÈRE, comme celui de `module.grille` et de
   `reperes` : `gap: 1px` sur un fond à la couleur du filet, chaque case repeignant le
   sien. L'alternative demandait de désigner « les cases qui ne sont pas en première
   colonne » par un `:nth-child` dont le pas change à chaque repli.

   ⚠️ CONSÉQUENCE : LE FOND DE CHAQUE CASE DOIT ÊTRE OPAQUE. --section-bg le donne —
   il vaut le blanc dans un `.cadre`, le crème sans lui, et une boîte sombre le
   redéfinit. Sans ça, le fond de gouttière traverserait toute la fiche.

   ⚠️ AUCUN COMPTE DE CASES N'EST PUBLIÉ : `auto-fit` avec un plancher, comme la
   rangée de `reperes`. La fiche sert quatre couples comme six, et se replie d'elle-
   même à une colonne quand la place manque — ce qui arrive vite dans un module à
   mi-largeur, et c'est très bien : un couple par ligne y reste lisible.
   ========================================================================= */
/* ⚠️ TOUT LE QUADRILLAGE EST BORNÉ À LA FICHE QUADRILLÉE, par un `:not()`, et il faut
   le lire avec la règle du rembourrage juste en dessous : les deux ont le même piège.

   La variante `badges` emprunte `.carte-etiquettes__liste`, qui est un flex à
   enroulement — mais `display: grid` posé ici l'emportait, à spécificité égale et dans
   une feuille émise APRÈS _components.scss. Les badges restaient donc des cases de
   grille en `minmax(11rem, 1fr)` : chacune faisait 11rem de large quel que soit son
   texte, ce qui est exactement le défaut vu au rendu (« CA / 10 M€ » dans une case de
   176px).

   Seul `margin: 0` reste commun aux deux : un `<dl>` porte une marge par défaut. */
.texte-module__fiche {
  margin: 0;
}

.texte-module__fiche:not(.texte-module__fiche--badges) {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  gap: 1px;
  background-color: var(--filet-panneau-teinte);
}

/* ⚠️ LE REMBOURRAGE ET LE FOND SONT BORNÉS À LA FICHE QUADRILLÉE, par un `:not()`.
   Sans lui, ils l'emportaient sur ceux de `.carte-etiquettes__puce` dans la variante
   `badges` : même spécificité d'une classe, et cette feuille est émise APRÈS
   _components.scss, donc c'est l'ordre du fichier qui tranchait. La pilule empruntée
   se retrouvait avec le rembourrage d'une case de quadrillage.

   La pile en colonne et sa gouttière, elles, restent partagées — c'est justement ce que
   la variante réemploie pour empiler son libellé et sa valeur. */
.texte-module__fiche-couple {
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
}

.texte-module__fiche:not(.texte-module__fiche--badges) .texte-module__fiche-couple {
  padding: clamp(var(--sp-3), 1.2vw, var(--sp-5));
  background-color: var(--section-bg);
}

/* Le libellé reprend la grammaire des mentions de service du site — chasse fixe,
   capitales, interlettrage élargi, encre éteinte. La même que `.item__surtitre`, dont
   il occupe la place. */
.texte-module__fiche-libelle {
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--text-muted);
}

/* ⚠️ LA VALEUR N'EST PAS UN TITRE, et c'est tout l'intérêt du changement de forme :
   dans la grille elle tombait dans un `<h3>` à l'échelle d'un titre de section, alors
   que « 2015 » ou « ~50 » n'annoncent rien. Elle est ici du texte fort, au corps du
   site — d'où une hauteur de case qui suit son contenu au lieu de le dominer.

   `margin: 0` : un `<dd>` porte un retrait de 40px par défaut dans le navigateur. */
.texte-module__fiche-valeur {
  margin: 0;
  font-size: var(--fs-base);
  line-height: var(--lh-snug);
  color: var(--text-strong);
}

/* =========================================================================
   LA FICHE EN TABLEAU DE BADGES — la peau de la carte d'étiquettes.

   Troisième forme essayée pour les six couples de « Le client », après la grille de
   cartes et la fiche quadrillée, et réglée en trois passes le 21/08/2026 : des badges,
   puis sur deux lignes, puis « je veux le même style de conteneur et de badges que ceux
   de En coulisses ».

   ⚠️ D'OÙ L'EMPRUNT, ET C'EST L'ESSENTIEL DE CETTE SECTION : le conteneur, la liste et
   les pilules viennent des CLASSES de `carte-etiquettes` (_components.scss), posées par
   le gabarit. Aucune de leurs valeurs n'est recopiée ici — ni le fond, ni le filet, ni
   l'arrondi, ni la géométrie de la pilule, ni l'aplat bleu de `puces: bleu`. Le jour où
   « En coulisses » change, cette fiche change avec elle, ce qui est exactement ce qu'on
   demande.

   Il ne reste donc ici que ce que l'atome ne sait pas faire : une pilule à DEUX lignes,
   alors que la sienne n'en attend qu'une.

   Ce que la version d'avant déclarait et qui est PARTI avec l'emprunt — ne pas le
   rétablir, ce serait recopier l'atome :

     le fond de la pastille    deux valeurs selon le conteneur (crème dans un panneau
                               blanc, blanc sur le crème), devenues inutiles : l'aplat
                               bleu de `puces: bleu` ne dépend d'aucun contexte ;
     son filet et son arrondi  ceux de `.carte-etiquettes__puce`, désormais hérités ;
     l'enroulement de la liste celui de `.carte-etiquettes__liste`.

   ⚠️ LE BALISAGE, LUI, NE CHANGE PAS — c'est toujours un `<dl>` de couples. C'est ce
   qui a rendu les trois passes bon marché : le sens vit dans les balises, l'habillage
   au-dessus.

   ⚠️ ET LE MODULE DOIT ÊTRE EN `cadre: false` : le conteneur emprunté EST le panneau.
   Rien ne le vérifie — deux panneaux emboîtés se voient immédiatement.
   ========================================================================= */
/* La liste vient de `.carte-etiquettes__liste` — flex, enroulement, gouttière. Rien à
   défaire depuis que le quadrillage est borné par un `:not()` juste au-dessus : il ne
   reste qu'à écarter les badges.

   ⚠️ LA GOUTTIÈRE EST PLUS LARGE QUE CELLE DE L'ATOME, et c'est l'autre chose qui ne
   s'emprunte pas. Demande d'Hugo le 21/08/2026 : « mets plus d'espaces entre les
   badges. » Les pilules d'« En coulisses » portent un seul mot sur une ligne, donc
   --sp-2 les sépare ; celles-ci portent deux lignes et un libellé en capitales, et à la
   même gouttière elles se lisaient comme un pavé continu. Un objet plus haut demande
   plus d'air autour de lui. */
.texte-module__fiche--badges {
  gap: var(--sp-4);
}

/* ⚠️ LA PASTILLE PORTE DEUX LIGNES, ET C'EST TOUT CE QUI RESTE À DÉCLARER. Demande
   d'Hugo : « avec le titre, puis en dessous l'item ». `.carte-etiquettes__puce` est une
   pilule d'UNE ligne : elle pose `white-space: nowrap` sur tout son contenu et un
   rembourrage calibré pour une ligne. Deux corrections, et pas une de plus — la pile en
   colonne, elle, vient déjà de `.texte-module__fiche-couple`, que la fiche quadrillée
   établit.

   ⚠️ L'ARRONDI EST FIXE, ET C'EST LA TROISIÈME CHOSE À DÉCLARER. Règle d'Hugo, le
   21/08/2026 : « il faut pas adapter le radius à la taille. » L'atome pose
   `--radius-full` (62rem), qui se plafonne à la moitié de la hauteur de la boîte : sur
   une pilule d'une ligne ça donne la capsule attendue, mais sur un bloc de deux lignes
   la FORME CHANGE AVEC LE CONTENU — une ligne de plus, et la même pastille se lit comme
   un objet différent.

   Ce commentaire a défendu l'inverse pendant une heure, au motif que la capsule faisait
   ressembler ces badges à ceux de « En coulisses ». C'était confondre la peau et la
   géométrie : on emprunte le fond, le filet et les encres, pas un rayon calculé pour
   une seule ligne.

   --radius-cadre (12px) est l'arrondi des petits panneaux du site, et c'est bien ce
   qu'est devenu un badge à deux lignes. */
.texte-module__fiche--badges .texte-module__fiche-couple {
  /* ⚠️ `flex: 0 0 auto` — LA LARGEUR SUIT LE TEXTE, ET RIEN D'AUTRE. Demande d'Hugo le
     21/08/2026 : « adapte leur largeur en fonction de la largeur du texte dedans. »

     Le défaut du flex est `0 1 auto` : la base est bien le contenu, mais le `1` autorise
     le RÉTRÉCISSEMENT, et dès que la rangée manque de place les badges se resserrent
     sous leur texte au lieu de passer à la ligne. La valeur étant en `nowrap`, elle
     débordait alors de sa pilule. À `flex-shrink: 0`, la rangée s'enroule — ce qu'elle
     sait faire — et chaque badge garde exactement la largeur de son contenu. */
  flex: 0 0 auto;
  padding-block: clamp(var(--sp-2), 1vw, var(--sp-3));
  border-radius: var(--radius-cadre);
  white-space: normal;
}

/* ⚠️ LES DEUX ENCRES NE SE REMETTENT QUE DANS UNE PILULE BLEUE, et le sélecteur porte
   donc sur le ton du CONTENEUR. La fiche pose un libellé éteint et une valeur forte,
   tous deux calés sur un fond clair ; sur l'aplat bleu de `puces: bleu` ils tomberaient
   à 1,4 et 2,1, d'où le repli sur l'encre de la pilule — blanche — la distinction entre
   les deux lignes restant portée par la chasse fixe, les capitales et le corps.

   ⚠️ MAIS DANS UNE PILULE BLANCHE (`ton: bleu`, où l'aplat est passé sur la CARTE), les
   deux rôles sont justement ceux qu'il faut : c'est ce qui permet au libellé de rester
   éteint et à la valeur d'être forte. La règle est donc bornée, et non générale — elle
   l'a été une heure le 21/08/2026, ce qui aplatissait les deux encres dès l'inversion.

   La valeur garde son corps réduit : dans une pilule, --fs-base la faisait dépasser le
   libellé de trop. Et elle ne s'enroule pas — une valeur sur deux lignes ferait de la
   pastille un pavé, et c'est le signe qu'il faut la fiche quadrillée. */
.carte-etiquettes--puces-bleu .texte-module__fiche--badges .texte-module__fiche-libelle,
.carte-etiquettes--puces-bleu .texte-module__fiche--badges .texte-module__fiche-valeur {
  color: inherit;
}

.texte-module__fiche--badges .texte-module__fiche-valeur {
  font-size: var(--fs-sm);
  white-space: nowrap;
}

/* ---------- Le marqueur en forme de marque, dans une boîte SOMBRE ----------
   ⚠️ SANS CETTE RÈGLE LA FORME SORT EN BLEU DU SITE SUR LE BLEU NUIT, soit 2,30 —
   assez pour se voir, pas assez pour se lire comme un repère. Vu au rendu le
   21/08/2026, sur les deux sections en grille de /cas-clients/hardloop/, qui sont
   les premières du site à combiner `fond: sombre` et `marqueur: marque`.

   La cause est une question de spécificité dans _components.scss :
   `.item__forme` pose `--shape-color: var(--item-ton, var(--c-blue))`, mais
   `.forme--bleu` pose `--shape-color: var(--c-blue)` — même spécificité, et
   `.forme--bleu` est écrit plus loin dans le fichier, donc il gagne. Le marqueur
   ignore ainsi `--item-ton` quel que soit le `ton` déclaré.

   ⚠️ CORRIGÉ ICI ET NON DANS `.item__forme`, délibérément. Rendre `--item-ton`
   gagnant pour de bon ferait passer au CYAN toutes les formes de marqueur des
   pages non pilier, qui sortent bleues aujourd'hui parce que `ton` vaut
   `dictionnaire` par défaut. Ce serait un changement visible sur des pages déjà
   validées, pour un défaut qui ne se voit que sur fond sombre.

   Deux classes contre une : cette règle l'emporte sans `!important`. Et elle lit
   --text-accent, que `.on-dark` a déjà passé au fluo (11,54 sur le bleu nuit) —
   jamais le fluo en dur, qui serait faux le jour où la boîte change de teinte. */
.texte-module--sombre .item__forme {
  --shape-color: var(--text-accent);
}

.texte-module__module-titre {
  margin: 0 0 var(--sp-5);
  max-width: 40ch;
  font-size: var(--fs-h3);
  line-height: var(--lh-snug);
  color: var(--c-blue-zodiac);
  text-wrap: balance;
}

/* ---------- L'annonce du diagramme ----------
   « POUR CHAQUE INDICATEUR, UNE FICHE COMPLÈTE » — la ligne que la maquette du
   25/08/2026 pose au-dessus du diagramme. Demande d'Hugo, prioritaire sur la
   maquette : « le texte […] doit être en mono ».

   ⚠️ UN `<p>` ET NON UN `<h3>`, et c'est la règle 3 de MAQUETTE.md qui le commande :
   une feuille de bloc ne redéclare pas la police d'un titre, et un titre en chasse
   fixe capitalisée ne ressemblerait à aucun autre titre du site. L'objet que la
   maquette dessine n'est d'ailleurs pas un titre mais une ÉTIQUETTE — la grammaire
   des intitulés de service du site, celle de `.eyebrow` et du fil d'Ariane. Le
   diagramme reste annoncé par le titre de la section, qui est son `<h2>`.

   C'est mot pour mot l'écriture que `.texte-module__sous-titre` portait jusqu'à ce
   jour-là : le rôle en chasse fixe n'a pas quitté le bloc, il a changé de place. Une
   seule différence, l'encre — --text-accent et non --text-muted comme `.eyebrow`,
   parce que la maquette la pose au vert du pilier. Écrit ainsi, le fluo viendrait
   tout seul si le diagramme passait un jour dans une boîte sombre. */
.texte-module__annonce {
  /* ⚠️ --sp-3 : l'annonce est RAPPROCHÉE de la composition qu'elle annonce, demandé
     par Hugo le 25/08/2026 (« tu peux approcher le texte […] de l'image »). Elle a
     démarré à --sp-5, la marge d'un intitulé de module — mais un intitulé coiffe un
     panneau qui porte son propre bord, alors qu'ici l'étiquette n'a que le blanc pour
     se rattacher à la fiche. Trop d'écart et elle flotte entre le texte au-dessus et
     le diagramme en dessous. */
  margin: 0 0 var(--sp-3);
  font-family: var(--font-mono);
  /* ⚠️ --fs-base, ET ELLE A GROSSI EN DEUX FOIS le 25/08/2026 : --fs-mono (11px)
     d'abord, puis --fs-xs (14), puis le corps du site (16→18) — « encore un peu
     grossir la taille de la police ». C'est deux crans au-dessus de l'écriture que
     `.texte-module__sous-titre` portait avant elle, et la raison est la place de
     l'objet : l'ancien sous-titre était collé sous un titre qui le portait, celle-ci
     annonce seule une composition qui prend toute la largeur.

     L'interlettrage élargi et les capitales RESTENT : c'est ce qui la tient dans la
     grammaire des étiquettes du site (`.eyebrow`, le fil d'Ariane) plutôt que d'en
     faire un titre en chasse fixe, ce que la règle 3 de MAQUETTE.md interdit. Voir
     `.item__accroche` dans _components.scss pour l'autre parti — mono sans capitales
     ni interlettrage, pour une PHRASE et non une étiquette. */
  font-size: var(--fs-base);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  line-height: var(--lh-snug);
  color: var(--text-accent);
}

/* ---------- Les entrées numérotées ----------
   L'atome vient de partials/item.njk, partagé avec `liste-glyphes` et
   `rangees-illustrees` : sa typographie et son marqueur sont réglés dans
   _components.scss. Ce bloc ne décide que de leur EMPILEMENT et du filet qui
   les sépare — c'est ce qui garantit que les listes du site se ressemblent.

   Le filet est tireté, comme la maquette (`border-dashed` de Figma). */
.texte-module__entrees {
  margin: 0;
  padding: 0;
  list-style: none;
}

/* ⚠️ AUCUN FILET ENTRE LES ENTRÉES, retiré le 18/08/2026 à la demande d'Hugo.
   Il y avait un tireté ici. C'est désormais l'espace qui sépare les entrées — le
   même parti que `liste-glyphes` pour les garanties de la page produit, dont le
   commentaire dit exactement ça. Ne pas le rétablir sans le lui demander. */
.texte-module__entree + .texte-module__entree {
  margin-block-start: clamp(var(--sp-5), 2vw, var(--sp-8));
}

/* ---------- Les entrées en grille ----------
   Trois colonnes quadrillées de filets, au lieu d'une pile. Demandé par Hugo le
   19/08/2026 pour la section 04 de la page plateforme (Figma 141:8276), où la
   maquette range six cartes en 3 × 2.

   ⚠️ ELLE SUIT LA GRAMMAIRE DES PANNEAUX DU SITE : filet BLEU éclairci
   (--filet-panneau-teinte) et fond BLANC, pas le crème ni le gris des filets
   discrets. Hugo l'a corrigée le 19/08/2026 — « comme les autres grilles du
   site ». Le pourtour vient du `.cadre` qui l'enveloppe, les filets intérieurs de
   la gouttière : la grille est donc entièrement close, sans qu'aucun bord ne soit
   dessiné deux fois.

   ⚠️ LE QUADRILLAGE EST FAIT PAR LA GOUTTIÈRE, PAS PAR DES BORDURES DE CELLULE.
   Le fond de la liste est la couleur du filet, les cellules repeignent par-dessus,
   et le `gap: 1px` laisse voir la couleur du dessous : on obtient un filet entre
   chaque cellule, en colonne comme en rangée.

   L'alternative — une bordure sur chaque cellule — demandait de désigner « les
   cellules qui ne sont pas en première colonne » par un `:nth-child(3n+1)`, dont
   le pas change à chaque point de rupture (3 colonnes, puis 2, puis 1). Trois
   règles à tenir accordées, contre une seule ici.

   ⚠️ LES CELLULES PRENNENT --section-bg ET NON --c-white EN DUR. Dans un `.cadre`
   la variable vaut déjà le blanc ; hors cadre elle vaut le fond de la section, et
   la grille reste lisible au lieu de poser un rectangle blanc sur le crème. */
.texte-module__entrees--grille {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1px;
  background-color: var(--filet-panneau-teinte);
  /* Le nombre de rangs qu'une carte enjambe. Deux par défaut — la ligne
     d'intitulé et le texte — et c'est le gabarit qui le monte à 3 ou 4 quand les
     entrées portent un surtitre et/ou une invite. Voir les deux règles juste en
     dessous et le long commentaire du `subgrid`. */
  --rangs: 2;
}

/* ⚠️ CES DEUX RÈGLES SONT LE PENDANT DU COMPTE FAIT DANS LE GABARIT, et la
   valeur doit rester la même des deux côtés. `texte-module__entrees--rangs-2`
   n'a volontairement pas de règle : c'est déjà le défaut ci-dessus.

   `--rangs-3` n'est employé par aucune page aujourd'hui — il couvre le cas d'une
   grille qui porterait une invite sans surtitre. C'est deux lignes contre un
   désalignement muet, la grille se serait sinon repliée sur 2 rangs pour un
   atome qui en rend 3. */
.texte-module__entrees--rangs-3 {
  --rangs: 3;
}

.texte-module__entrees--rangs-4 {
  --rangs: 4;
}

/* `--rangs-5` couvre une carte qui porte surtitre + chute + invite, ce qu'aucune
   page ne fait aujourd'hui. Il est là parce que le gabarit SAIT le calculer : sans la
   classe, la grille retomberait sur 2 rangs et se désalignerait sans message. */
.texte-module__entrees--rangs-5 {
  --rangs: 5;
}

/* ⚠️ DEUX COLONNES AU LIEU DE TROIS, quand la maquette ne pose que deux cases côte à
   côte — la section 02 de la page Excel & Sheets. À trois colonnes, la troisième
   resterait vide et les deux cases n'occuperaient que les deux tiers du cadre, ce qui
   se lit comme une grille inachevée et non comme un face-à-face.

   ⚠️ ELLE DOIT RESTER AU-DESSUS DES POINTS DE RUPTURE, plus bas dans ce fichier. Les
   deux règles ont la même spécificité, donc c'est l'ORDRE DU FICHIER qui tranche :
   déplacée sous eux, elle rétablirait deux colonnes là où le repli en veut une, et la
   grille déborderait sur mobile. */
.texte-module__entrees--cols-2 {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

/* La grille remplit le cadre d'un bord à l'autre : sans ça le rembourrage du
   module la rentrerait, et les filets intérieurs s'arrêteraient à distance du
   pourtour au lieu de le rejoindre. C'est ce qui fait lire « un quadrillage » et
   non « une grille posée dans une boîte ».

   `:has()` plutôt qu'une classe de plus sur le module : c'est la liste qui porte
   le modificateur, et le gabarit n'a donc rien à savoir de cette règle.

   ⚠️ LA FICHE EST DANS LE MÊME CAS, pour la même raison : elle quadrille aussi. Les
   deux sélecteurs de cette section — celui-ci et celui qui rend son rembourrage au
   déroulé, juste en dessous — portent donc la MÊME liste, et ils doivent rester
   accordés. Une forme quadrillée ajoutée à l'un sans l'autre laisserait son déroulé
   collé au filet de gauche : c'est exactement le défaut vu au rendu le 21/08/2026.

   ⚠️ LA VARIANTE `--badges` N'EST PAS À EXCLURE, et l'enfant DIRECT est la raison : en
   badges, le `<dl>` est enveloppé dans le conteneur emprunté à `carte-etiquettes`, donc
   `> .texte-module__fiche` ne l'atteint pas. Elle l'a été explicitement pendant une
   heure le 21/08/2026, avant l'emprunt ; c'est devenu inutile, pas faux. */
.texte-module__module:has(> .texte-module__entrees--grille,
> .texte-module__fiche) {
  padding: 0;
}

/* ⚠️ LE DÉROULÉ DOIT REPRENDRE LE REMBOURRAGE QUE LE MODULE VIENT DE PERDRE.
   La règle ci-dessus vise la GRILLE, dont les filets doivent rejoindre le pourtour
   du cadre — mais elle vide le module entier, et le déroulé posé sous la grille se
   retrouvait collé au filet de gauche. Défaut vu au rendu le 21/08/2026 sur
   « La réponse Biron » de /cas-clients/hardloop/.

   ⚠️ LE REMBOURRAGE VA SUR LE CONTENU, PAS SUR LE BLOC, et la distinction est celle
   de la maquette : le filet qui SÉPARE le déroulé de la grille court d'un bord à
   l'autre du panneau (Figma 144:6152, 1278 px de large), tandis que l'intitulé, les
   étapes et leurs filets sont rentrés (144:6155, 1190 px à x=44). Poser le
   rembourrage sur `.texte-module__deroule` aurait rentré le filet du haut avec le
   reste.

   ⚠️ ET IL EST BORNÉ AUX MODULES QUI ONT UN CADRE. Un module `--nu` — dans une
   boîte, ou en `cadre: false` — n'a jamais eu de rembourrage : ses entrées
   s'alignent volontairement sur le texte au-dessus d'elles, et rentrer le déroulé
   l'en décalerait. C'est pourquoi le sélecteur exige `.cadre` et ne se contente pas
   du `:has()`.

   La cote vient de `--module-pad`, déclarée sur le module juste au-dessus : les deux
   valeurs ne peuvent donc pas dériver l'une de l'autre.

   ⚠️ ET IL EST BORNÉ AUX MODULES QUI ONT VRAIMENT UN DÉROULÉ — le second `:has()`,
   posé le 21/08/2026. Sans lui, ce rembourrage s'appliquait à TOUTE grille, y
   compris celles qui n'ont rien en dessous : le quadrillage s'arrêtait alors à
   30 px du bas du panneau, et ses filets verticaux avec lui. Défaut vu au rendu sur
   « Autres demandes » de /parler-a-un-expert/ — « les séparateurs doivent faire
   toute la hauteur du tableau ». Deux `:has()` chaînés valent un ET : grille ou
   fiche, ET un déroulé. */
.texte-module__module.cadre:has(> .texte-module__entrees--grille,
> .texte-module__fiche):has(> .texte-module__deroule) {
  padding-block-end: var(--module-pad);
}

.texte-module__module.cadre:has(> .texte-module__entrees--grille,
> .texte-module__fiche) .texte-module__deroule > * {
  padding-inline: var(--module-pad);
}

/* ---------------------------------------------------------------------
   LES DEUX RANGS DE CHAQUE CELLULE SONT ALIGNÉS D'UNE CELLULE À L'AUTRE.

   Demandé par Hugo le 19/08/2026 : tous les contenants de titre à la même
   hauteur, tous les contenants de texte aussi, pour que l'œil retrouve la même
   ligne en passant d'une carte à sa voisine.

   ⚠️ EN `subgrid`, ET C'EST LE SEUL MOYEN JUSTE. Le titre d'une carte peut tenir
   sur une ou deux lignes selon sa longueur : toute hauteur écrite à la main
   (`min-height`, un nombre de `ch`) serait fausse dès qu'un intitulé change, et
   fausse différemment à chaque largeur d'écran. Ici la rangée est dimensionnée par
   le navigateur sur le plus haut titre de la rangée, et les autres s'y alignent.

   La chaîne compte DEUX maillons, parce que le balisage a deux niveaux : la
   cellule (`li`), puis l'atome `.item` qui porte réellement les rangs. Les
   deux doivent enjamber les mêmes rangées et se déclarer `subgrid`, sinon
   l'alignement s'arrête au premier niveau et ne descend pas jusqu'au texte.

   ⚠️ LE NOMBRE DE RANGS VIENT DE --rangs, PAS D'UNE CONSTANTE. L'atome en rend
   deux, trois ou quatre selon que les entrées portent un surtitre et une invite
   (partials/item.njk), et c'est le gabarit qui compte — voir la classe
   `--rangs-N` plus haut. Écrire `span 2` en dur ici, comme c'était le cas
   jusqu'au 19/08/2026, faisait déborder les deux rangs supplémentaires de la
   zone enjambée : les invites se posaient les unes sur les autres.

   ⚠️ LE REMBOURRAGE DE LA CELLULE NE CASSE PAS L'ALIGNEMENT : il décale le premier
   rang vers le bas de la même quantité dans TOUTES les cellules, donc elles restent
   à la même hauteur entre elles. Ne pas chercher à le compenser.

   Sans `subgrid` (navigateur très ancien), les cellules retombent chacune sur leur
   propre hauteur : la grille reste entièrement lisible, elle perd juste son
   alignement. */
.texte-module__entrees--grille > .texte-module__entree {
  /* ⚠️ ANNULE LA MARGE DE LA PILE. Le sélecteur d'adjacence juste au-dessus
     s'applique encore ici : sans cette remise à zéro, chaque cellule sauf la
     première serait décalée à l'intérieur de sa case. */
  margin-block-start: 0;
  grid-row: span var(--rangs, 2);
  display: grid;
  grid-template-rows: subgrid;
  padding: clamp(var(--sp-5), 2vw, var(--sp-8));
  background-color: var(--section-bg);
}

/* L'atome passe de la pile flex à la grille pour relayer le `subgrid`. Sa
   gouttière est reprise telle quelle (--sp-3, celle de `.item`) : sans elle, le
   texte viendrait coller au titre, la grille de la cellule ayant remplacé le
   `gap` du flex. */
.texte-module__entrees--grille .item {
  grid-row: span var(--rangs, 2);
  display: grid;
  grid-template-rows: subgrid;
  row-gap: var(--sp-3);
}

/* ---------- LES CTA TOMBENT AU BAS DE LEUR CASE ----------
   Demandé par Hugo le 21/08/2026 sur « Autres demandes » de /parler-a-un-expert/ :
   « il faudrait que tous les CTA soient alignés sur le bas des cases. »

   ⚠️ UNE SEULE DÉCLARATION, ET PAS UNE CHAÎNE DE `subgrid` DE PLUS. Le rang du
   texte est déjà à la même hauteur d'une case à l'autre — c'est ce que fait le
   `subgrid` au-dessus — donc il suffit de pousser les liens au bas de ce rang pour
   qu'ils atterrissent tous sur la même ligne, quelle que soit la longueur des
   descriptions. Ajouter un rang au compte de `--rangs` aurait marché aussi, au prix
   d'une valeur de plus à tenir accordée entre le gabarit et cette feuille — le
   piège que ce fichier documente déjà trois fois.

   C'est le même dispositif que `.cas__chiffres`, `.grille-articles__pied` et
   `.rendez-vous__mention`, pour la même raison.

   `.item__texte` est bien étiré sur toute la hauteur de son rang : c'est le
   comportement par défaut d'un élément de grille, et c'est ce qui donne au `auto`
   de quoi mordre. */
.texte-module__entrees--grille .item__liens {
  margin-block-start: auto;
}

/* =========================================================================
   LA CHUTE PREND LE DERNIER RANG DE LA CARTE.

   ⚠️ ELLE EST ENFANT DU `<li>`, PAS DE L'ATOME, et c'est ce qui la fait s'aligner.
   Trois essais le 20/08/2026 :

     1. dans `.item__texte`   elle suivait les puces : deux cartes aux listes de
                              hauteurs différentes avaient leurs chutes décalées.
     2. rang de `.item`       toujours décalées, et c'est la leçon — `.item` est
                              LUI-MÊME une sous-grille du `<li>`, donc la chute se
                              trouvait à DEUX niveaux d'imbrication, avec deux
                              gouttières de rangée différentes en travers (1px héritée
                              du conteneur pour le `<li>`, --sp-3 redéclarée par
                              `.item`). Des sous-grilles imbriquées aux gouttières
                              divergentes ne partagent pas leurs pistes de façon
                              fiable.
     3. rang du `<li>`        retenu. UN SEUL niveau, la gouttière du conteneur, et
                              rien à recompter.

   ⚠️ `1 / -2` ET `-2 / -1` PLUTÔT QU'UN COMPTE. L'atome prend du premier rang à
   l'avant-dernier, la chute le dernier : aucune des deux règles n'a besoin de savoir
   COMBIEN il y a de rangs. C'est ce qui évite un second compte à tenir accordé avec
   `--rangs` — le piège que ce fichier documente déjà deux fois.

   ⚠️ `align-self: end` ET NON `start`, parce que la demande est un ancrage par le
   BAS : « j'aimerais que les deux soient à la même distance du bas de leur boîte ».
   Le dernier rang est le dernier de la carte, donc son bas est à distance constante
   du bord — quel que soit le nombre de lignes de chaque chute.
   ========================================================================= */
.texte-module__entrees--chute .item {
  grid-row: 1/-2;
}

.texte-module__entrees--chute > .texte-module__entree > .item__chute {
  grid-row: -2/-1;
}

/* ⚠️ RIEN À DÉCLARER POUR ALIGNER LES FILETS DES INVITES, et c'est le `subgrid`
   qui l'offre : le rang de la description est dimensionné sur la plus longue de
   la rangée, donc le rang de l'invite commence à la même hauteur dans toutes les
   cartes. Son filet, qui est son bord haut, tombe donc sur la même ligne.

   Ne pas ajouter d'`align-self: end` en croyant « coller l'invite en bas » : ça
   alignerait les BAS des invites au lieu de leurs filets, et une invite de deux
   lignes ferait alors décrocher le filet de sa voisine. */
/* Deux colonnes, puis une. Les six cartes de la maquette se lisent aussi bien en
   pile : rien dans leur contenu ne dépend de leur voisine. */
@media (max-width: 60rem) {
  .texte-module__entrees--grille {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}
@media (max-width: 48rem) {
  .texte-module__entrees--grille {
    grid-template-columns: minmax(0, 1fr);
  }
}
/* ---------------------------------------------------------------------
   LES ENTRÉES EN CARTES ARRONDIES.

   Deux blocs arrondis à l'aplat du pilier, au lieu du quadrillage fermé de
   `grille` — demandé par Hugo le 20/08/2026 pour la section 03 de
   /integrations/ : « deux blocs arrondis orange avec la description des outils ».

   ⚠️ ELLES RÉEMPLOIENT `--grille` ET N'EN CHANGENT QUE L'HABILLAGE. Le gabarit
   émet les deux classes : colonnes, `subgrid` (donc l'alignement des rangs d'une
   carte à l'autre) et les deux replis viennent de la grille, telle quelle. Écrire
   une seconde grammaire de grille ici l'aurait fait dériver de la première au
   premier réglage de gouttière.

   ⚠️ CES TROIS DÉCLARATIONS DOIVENT RESTER SOUS LES RÈGLES DE `--grille`, points
   de rupture compris : elles ont la même spécificité (deux classes), donc c'est
   l'ORDRE DU FICHIER qui tranche. Remontées, le fond de filet et le rembourrage
   de cellule de la grille reprendraient le dessus.

   ⚠️ L'ARRONDI EST 20px ET NON LES 28px DES BOÎTES DE SECTION. Une carte n'est
   pas une boîte : à 28px, deux cartes côte à côte dans une section qui en fait
   déjà 28 donnent deux arrondis concentriques qui se concurrencent. C'est la même
   échelle que --radius-lg (16px) d'un cran au-dessus ; il n'y a pas de jeton à
   cette valeur, comme pour les 28px des boîtes. */
.texte-module__entrees--cartes {
  /* Une vraie gouttière à la place du filet d'1px : les cartes sont SÉPARÉES,
     c'est ce qui les distingue des cellules d'un quadrillage. */
  gap: clamp(var(--sp-4), 1.6vw, var(--sp-6));
  /* Le fond de filet de la grille n'a plus rien à montrer : la gouttière laisse
     voir le fond de la section, et non une bande de couleur. */
  background-color: transparent;
}

.texte-module__entrees--cartes > .texte-module__entree {
  background-color: var(--carte-fond, var(--section-bg));
  border-radius: 1.25rem; /* 20px — voir l'avertissement ci-dessus */
}

/* ---------- Les cartes orange sombre du pilier intégrations ----------
   ⚠️ UNE CLASSE PAR PILIER, JAMAIS UNE BOUCLE, et c'est la règle 10 de
   MAQUETTE.md : les quatre teintes n'ont pas la même luminance, donc chacune
   demande son propre arbitrage d'encre. Seule celle qui sert existe.

   ⚠️ ELLES ONT CHANGÉ DE CAMP LE 20/08/2026, comme les deux boîtes de ce pilier.
   D'abord l'aplat #f0a22e à encres noires ; Hugo a étendu aux cartes la consigne
   qu'il avait donnée pour les boîtes — « les parties dites sombres [sont] des
   parties sombres avec du texte blanc ». Elles portent donc
   --c-pilier-integrations-sombre (#ab4a00) et `.on-dark`.

   ⚠️ `.on-dark` EST POSÉ SUR LA CARTE PAR LE GABARIT, pas ici — la classe
   redéfinit --bg-page, donc elle doit aller sur l'élément qui EST le fond, et ces
   cartes ne sont dans aucune boîte. Le détail est dans blocs/texte-module.njk.
   C'est lui qui fournit le blanc du corps de texte (5,66) et le fluo de l'accent
   (4,85) ; cette règle-ci ne corrige que ce qu'il laisse faux.

   ⚠️ TROIS RÔLES SONT REPRIS, et chacun pour une raison distincte :

     --text-muted   le casper d'`.on-dark` tombe à 2,74 — règle 11 de MAQUETTE.md,
                    passé au blanc pur comme sur le violet et le rose ;
     --item-ton     écrit EN CLAIR par `.item--integrations`, donc hors d'atteinte
                    d'une réaffectation de --text-accent. Sans lui, un marqueur ou
                    une accroche sortirait en orange clair sur orange sombre ;
     --item-encre   l'autre moitié de la paire (voir _components.scss).

   ⚠️ LA PASTILLE FLUO N'EST PAS CONCERNÉE, et c'est ce qui la rend précieuse : elle
   porte son propre couple fond + encre (fluo, encre du site) et traverse donc le
   passage du clair au sombre sans une ligne à changer. Le bloc blanc du logo non
   plus — il se détache même mieux sur ce fond.

   ⚠️ LE FILET DU BOUTON VIENT DE `.on-dark` LUI AUSSI : `.btn--secondary` prend
   --section-bg pour aplat (donc l'orange sombre) et currentColor pour filet (donc
   le blanc). Rien à déclarer. */
.texte-module__entrees--cartes-integrations > .texte-module__entree {
  --carte-fond: var(--c-pilier-integrations-sombre);
  /* Le fond de la carte devient le fond de référence, pour le `.btn--secondary`
     qu'elles portent — même raison que sur les boîtes du bloc. */
  --section-bg: var(--c-pilier-integrations-sombre);
  --bg-page: var(--c-pilier-integrations-sombre);
  --text-muted: var(--c-white);
  color: var(--text-strong);
}

/* ⚠️ MÊME NÉCESSITÉ DE CASCADE QUE POUR LA BOÎTE, plus haut dans ce fichier :
   `.item--integrations` écrit --item-ton SUR l'atome, et une propriété
   personnalisée posée sur un élément bat la même héritée d'un ancêtre. Déclarée
   sur la carte, la remise à blanc n'atteindrait aucun item. */
.texte-module__entrees--cartes-integrations > .texte-module__entree .item {
  --item-ton: var(--c-white);
  --item-encre: var(--c-white);
}

/* ---------- La carte d'étiquettes ----------
   Toute son apparence vient de `.carte-etiquettes` (_components.scss), l'atome
   partagé avec l'accueil. Il n'y a donc RIEN à déclarer de son fond, de son
   filet ni de ses pilules ici : le faire la désaccorderait de celle de l'accueil
   en silence, ce qui est exactement ce que l'extraction du 18/08/2026 a évité.

   Une seule chose appartient à cet emploi : dans la boîte verte, la carte occupe
   toute la largeur de sa colonne, alors que sur l'anneau de l'accueil elle est
   bridée à 15rem pour ne pas couvrir la trame. */
.texte-module__carte {
  width: 100%;
}

/* =========================================================================
   LE DÉROULÉ — la liste numérotée qui ferme le module.

   Origine : « Le déroulé » du gabarit d'étude de cas, Figma 144:6152, que la
   maquette pose DANS le panneau de la section, sous les trois cartes.

   ⚠️ IL EST SÉPARÉ DES ENTRÉES PAR UN FILET, et c'est la seule exception au « c'est
   l'espace qui sépare » de ce bloc (voir la note de `.texte-module__entree +
   .texte-module__entree`). La raison est qu'il ne s'agit pas de deux entrées de la
   même liste mais de DEUX OBJETS dans le même panneau : sans trait, la liste
   numérotée se lit comme la suite de la grille. La maquette en dessine un.

   ⚠️ LE FILET SUIT LE FOND. Dans un panneau blanc c'est le filet bleu éclairci du
   site ; dans une boîte sombre il disparaîtrait, d'où le blanc voilé — même remède
   que `.reperes--boite`.
   ========================================================================= */
.texte-module__deroule {
  margin-block-start: clamp(var(--sp-6), 2.4vw, var(--sp-8));
  padding-block-start: clamp(var(--sp-6), 2.4vw, var(--sp-8));
  border-block-start: var(--filet-panneau);
}

/* =========================================================================
   LE DÉROULÉ À CÔTÉ DES ENTRÉES, DANS UN BLOC SOMBRE.

   Demandé par Hugo le 21/08/2026 pour « La réponse Biron » de
   /cas-clients/hardloop/ : « met les trois items verticalement à gauche et le
   déroulé dans un bloc sombre à droite ».

   ⚠️ LE MODULE PERD SON CADRE, ET IL LE DOIT. Un bloc arrondi posé dans un panneau
   fermé à angles vifs y fait un trou de forme étrangère : c'est mot pour mot ce
   qu'interdit la règle 17 de MAQUETTE.md pour la carte de conclusion, et pour la même
   raison. La page déclare donc `cadre: false`, et les entrées se posent à même le
   crème — le parti de `liste-glyphes`.

   ⚠️ ET C'EST LE MÊME OBJET QUE LA `conclusion` DU BLOC, à un champ près : une carte
   arrondie sombre à mi-largeur, son intitulé en fluo au-dessus de son texte. Les deux
   ne fusionnent pas parce que leur CONTENU diffère — `conclusion` porte des
   paragraphes, le déroulé une liste numérotée — mais les cotes sont reprises de là,
   pour que les deux se ressemblent d'une page à l'autre.
   ========================================================================= */
/* ⚠️ LE RANGEMENT CÔTE À CÔTE EST BORNÉ À `lg`, l'habillage NE L'EST PAS. En dessous,
   les deux colonnes s'empilent — mais le bloc reste sombre et arrondi, sinon il
   changerait de nature au repli. C'est ce partage qui explique les deux règles :
   celle-ci pose l'habillage à toute largeur, celle du `mq-up` la disposition.

   Le rembourrage haut et le filet de la règle générale sont ANNULÉS et non
   simplement absents : elle sépare le déroulé de la grille qu'il suit, et à côté il
   n'y a plus rien à séparer. La marge, en revanche, reste — empilé, le bloc a besoin
   d'air ; c'est la disposition en colonnes qui la retire. */
.texte-module__module--deroule-cote .texte-module__deroule {
  padding: clamp(var(--sp-6), 2.4vw, var(--sp-10));
  padding-block-start: clamp(var(--sp-6), 2.4vw, var(--sp-10));
  border-block-start: 0;
  border-radius: 1.25rem; /* 20px — l'arrondi des cartes, pas celui des boîtes */
  background-color: var(--c-blue-dark);
}

@media (min-width: 60rem) {
  .texte-module__module--deroule-cote {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    column-gap: clamp(var(--sp-8), 3.2vw, var(--sp-16));
    align-items: start;
  }
  /* À côté, il n'y a plus de rangée précédente dont s'écarter. */
  .texte-module__module--deroule-cote .texte-module__deroule {
    margin-block-start: 0;
  }
}
/* ⚠️ PLUS DE FILET ENTRE LES ÉTAPES ICI NON PLUS. Cette règle repassait le filet au
   blanc voilé, le filet bleu éclairci du site disparaissant sur le bleu nuit ; les
   séparateurs ayant été retirés le 21/08/2026, il n'y a plus rien à rattraper. La
   note est gardée pour le jour où on les rétablirait : le remède serait celui-là. */
.texte-module--sombre .texte-module__deroule {
  border-block-start: 1px solid rgba(255, 255, 255, 0.18);
}

/* L'intitulé reprend exactement celui du module — même niveau, même registre. Il ne
   déclare rien de sa police : _base.scss pose déjà le titrage du niveau, et la règle
   n'écrit que l'écart (règle 3 de MAQUETTE.md). La chasse fixe et les capitales, en
   revanche, sont un écart réel : c'est un libellé de service, pas un titre de
   section, et la maquette le compose ainsi. */
.texte-module__deroule-titre {
  margin: 0 0 var(--sp-5);
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--text-accent);
}

.texte-module__deroule-liste {
  margin: 0;
  padding: 0;
  list-style: none;
}

/* ⚠️ LES DEUX COLONNES SONT UNE GRILLE ET NON UN FLEX, et c'est ce qui aligne les
   textes entre eux quand une étape se replie sur deux lignes : la colonne du numéro
   est dimensionnée une fois pour toutes les étapes. En flex, chaque ligne aurait
   calculé sa propre largeur de numéro — invisible à quatre étapes numérotées 01 à
   04, faux dès la dixième. */
.texte-module__deroule-etape {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  column-gap: var(--sp-4);
  align-items: baseline;
  padding-block: clamp(var(--sp-3), 1.2vw, var(--sp-4));
}

/* ⚠️ AUCUN SÉPARATEUR ENTRE LES ÉTAPES, retiré le 21/08/2026 à la demande d'Hugo —
   « sur le déroulé, enlève les séparateurs ». Il y avait un filet ici, et un second en
   blanc voilé pour les fonds sombres.

   C'est le même parti que la pile d'entrées de ce bloc, dont le tireté a sauté le
   18/08/2026 (« c'est l'espace qui sépare »), et que les garanties de `liste-glyphes`.
   Ne pas le rétablir sans le lui demander : le rembourrage vertical des étapes suffit,
   et les numéros 01→04 font déjà le découpage à l'œil. */
/* `font-variant-numeric: tabular-nums` : les chiffres de même largeur, sinon « 01 »
   et « 04 » n'occupent pas la même place et la colonne de texte tremble d'une ligne
   à l'autre. C'est déjà ce que fait le marqueur numéroté de `item`. */
.texte-module__deroule-numero {
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  font-variant-numeric: tabular-nums;
  letter-spacing: var(--ls-wide);
  color: var(--text-accent);
}

.texte-module__deroule-texte {
  font-size: var(--fs-base);
  line-height: var(--lh-normal);
  color: var(--text-strong);
}

/* ⚠️ TOUT LE CONTENU DE LA BOÎTE PASSE AU-DESSUS DU DÉCOR. Sans ces deux lignes le
   `__inner` reste non positionné, et le donut — qui l'est — se peint par-dessus le
   texte, quel que soit l'ordre dans le document. Le donut est réglé pour tenir dans
   le coin, mais il chevauche la colonne de gauche sur écran étroit. */
.texte-module--boite .texte-module__inner {
  position: relative;
  z-index: 1;
}

/* ---------- La carte dans la boîte verte ----------
   Intitulé vert, et pilules en APLAT VERT au ton du pilier. Trois états successifs
   le 18/08/2026, tous demandés par Hugo : contour fluo, puis aplat fluo, puis
   aplat vert. Ne pas remettre de filet — c'est le fond OU le filet, pas les deux.

   ⚠️ CES RÈGLES SONT ICI ET NON DANS L'ATOME. `.carte-etiquettes`
   (_components.scss) est partagé avec l'accueil : y toucher repeindrait aussi les
   cartes « Secteurs » et « Métiers » du graphique. C'est une teinte propre à CET
   emploi, donc elle est portée par la classe hôte.

   ⚠️ L'intitulé vert sur le crème de la carte donne 2,90 de contraste, sous le
   seuil de 4,5. C'est un choix assumé d'Hugo pour un libellé de service en
   capitales ; à ne pas reprendre pour du texte qui porte de l'information.

   Le libellé des pilules passe au NOIR — demandé par Hugo le 18/08/2026. C'est
   --c-text-main et non un noir pur, pour la même raison qu'expliquée plus haut : le
   site n'a pas de jeton de noir pur. L'atome le posait en bleu nuit ; sur le vert
   des pilules le noir donne 5,41, donc la lisibilité est tenue. Si le fond des
   pilules change encore, c'est ce chiffre qu'il faut revérifier.

   ⚠️ CES TROIS VALEURS ONT SURVÉCU AU PASSAGE DE LA BOÎTE AU SOMBRE, le 25/08/2026,
   et c'est la raison qu'il faut retenir : elles se mesurent contre LA CARTE, pas
   contre la boîte. La carte est `ton: clair`, donc un panneau crème — le même motif
   que les panneaux clairs de `temoignages`, `connecteurs` et `onglets-metiers` dans
   leurs boîtes bleu nuit. Rien à recalculer tant que la carte reste claire ; le jour
   où elle passerait à `ton: sombre`, les trois le sont à refaire d'un coup.

   ⚠️ LA PHRASE « comme le reste des encres de cette boîte » A ÉTÉ RETIRÉE ICI :
   elle était vraie quand la boîte ramenait ses trois rôles sur le noir, elle est
   fausse depuis que la boîte est sombre. Le noir des pilules ne se justifie plus par
   parenté avec la boîte, mais seulement par sa mesure sur le vert de la pilule. */
.texte-module--dictionnaire .texte-module__carte .carte-etiquettes__titre {
  color: var(--c-pilier-dictionnaire);
}

.texte-module--dictionnaire .texte-module__carte .carte-etiquettes__puce {
  background-color: var(--c-pilier-dictionnaire);
  color: var(--c-text-main);
  /* `transparent` et non `border: none` : l'atome pose un filet d'1px, et le
     retirer ferait rétrécir la pilule de 2px en largeur comme en hauteur. On
     garde donc la géométrie et on éteint seulement la couleur. */
  border-color: transparent;
}

/* ---------- L'illustration ----------
   HORS BOÎTE : aucun contour, aucun fond. C'est la consigne de la maquette —
   l'illustration est posée à côté du module, pas encadrée avec lui. Le seul cas
   où un cadre apparaît est le `.apercu` en attente du visuel définitif, et
   c'est un placeholder, pas une décision de mise en page. */
/* Pas de règle d'étiquette « Fig. 0X » ici : le bloc n'en rend aucune. Voir
   l'avertissement dans blocs/texte-module.njk et la règle dans MAQUETTE.md. */
/* LE SCHÉMA EST CENTRÉ SUR LA LISTE, pas aligné en haut — demandé par Hugo le
   18/08/2026. Le corps est une grille dont les deux colonnes n'ont pas la même
   hauteur ; sans ça, le schéma restait collé en haut et la liste, plus longue,
   descendait bien plus bas que lui. */
.texte-module__corps > .texte-module__visuel {
  align-self: center;
}

/* ⚠️ LES DEUX FORMES PORTENT LE MÊME RATIO, et c'est la condition de la règle 6 :
   le cadre en attente et la photo qui le remplace doivent occuper exactement la
   même place, sinon l'échange déplace la page — précisément ce que la règle
   cherche à empêcher.

   Le défaut a été introduit et corrigé le 28/08/2026, en une mesure : la photo,
   sans ratio, prenait celui de son fichier (435 × 326) là où le cadre tenait
   435 × 377. Cinquante pixels de saut, invisibles tant qu'on ne compare pas les
   deux états. La leçon vaut pour toute nouvelle forme ajoutée ici — un
   `aspect-ratio` posé sur le cadre seul est une moitié de règle. */
.texte-module__placeholder,
.texte-module__photo,
.texte-module__video {
  aspect-ratio: 410/355; /* les cotes de la maquette (Figma 167:8414) */
}

/* La photo de section remplit sa case, qui porte le ratio ci-dessus.

   ⚠️ `object-fit: cover` EST OBLIGATOIRE : le fichier livré est au ratio de la
   maquette (519 / 390, soit 1,33) alors que la case rend 1,155 — un `fill` la
   déformerait. Elle est donc recadrée d'environ 13 % sur les côtés. */
.texte-module__photo {
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  border-radius: var(--radius-cadre);
}

/* LA VIDÉO DE DÉMO — Figma 447:11724 (section 04), 15/09/2026. Même case que
   la photo ci-dessus : `object-fit: cover` pour la même raison (le fichier
   livré, 1280 × 720, n'a pas le ratio exact de sa case), et le même filet
   arrondi. Elle n'a ni `src` ni lecture au chargement — voir
   `assets/js/video-differe.js`, chargé par le gabarit de base. */
.texte-module__video {
  display: block;
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  border-radius: var(--radius-cadre);
  background-color: var(--c-base-main); /* le temps que la vidéo s'attache */
}

/* =========================================================================
   LA FIGURE « UNE DÉFINITION, QUATRE ÉQUIPES » — Figma 141:6910.

   Une fiche d'indicateur en haut, quatre équipes en bas, et les tiges qui
   descendent de l'une vers les autres. Construite en HTML parce qu'elle n'est
   que des boîtes et du texte : elle suit donc l'échelle typographique du site
   et se traduit, ce qu'une image n'aurait pas fait.

   Elle vit dans cette feuille et non dans _components.scss : un seul bloc
   l'emploie. Si une deuxième page en a besoin, c'est là qu'elle devra monter.
   ========================================================================= */
.fiche-equipes {
  margin: 0;
}

.fiche-equipes__fiche {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--sp-4);
  padding: var(--sp-5) clamp(var(--sp-5), 2vw, var(--sp-6));
  border: var(--filet-panneau);
  background-color: var(--c-white);
}

/* ---------- Le badge « KPI-014 · ✓ certifié » ----------
   Demandé par Hugo le 18/08/2026 : la référence et son statut réunis dans une seule
   pastille verte, avec une coche devant le statut. C'était deux lignes de texte nu.

   ⚠️ LE VERT EST ASSOMBRI, il n'est pas posé tel quel. Le vert du pilier sur du blanc
   ne donne que 2,63 de contraste ; mélangé à 65 % vers le bleu nuit il atteint 4,87.
   C'est exactement la proportion retenue par `--panneau-ton` dans
   _texte-panneau.scss, où le calcul est écrit — ne pas la remonter, le libellé
   deviendrait illisible sur son propre fond.

   Le fond, lui, est un voile du vert PUR : c'est ce qui garde la pastille reconnaissable
   comme « verte » alors que son encre est presque bleu nuit. */
.fiche-equipes__badge {
  grid-column: 1;
  justify-self: start;
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1);
  margin: 0;
  padding: var(--sp-1) var(--sp-3);
  border-radius: var(--radius-full);
  background-color: color-mix(in srgb, var(--c-pilier-dictionnaire) 12%, var(--c-white));
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: color-mix(in srgb, var(--c-pilier-dictionnaire) 65%, var(--c-blue-zodiac));
  white-space: nowrap;
}

/* La coche est à pleine force, elle : c'est un signe et non du texte, donc les
   seuils de contraste du texte courant ne s'y appliquent pas de la même façon, et
   elle gagne à trancher sur le libellé qui la suit. */
.fiche-equipes__coche {
  color: var(--c-pilier-dictionnaire);
  font-weight: var(--fw-semibold);
}

.fiche-equipes__separateur {
  opacity: 0.5;
}

/* EN POLICE DE DISPLAY — demandé par Hugo le 18/08/2026 pour tous les textes du
   schéma sauf le chiffre. Elle n'en déclarait aucune et héritait donc du sans. */
.fiche-equipes__nom {
  grid-column: 1;
  margin: var(--sp-1) 0 0;
  font-family: var(--font-serif);
  font-weight: var(--fw-semibold);
  font-size: var(--fs-md);
  line-height: var(--lh-snug);
  /* La fiche est un aplat BLANC posé sur une bande qui peut être sombre : son
     encre ne peut donc pas venir de --text-strong, qui passerait au blanc dans
     `.on-dark` et disparaîtrait. Elle est écrite en dur, et c'est le seul
     endroit de la feuille où c'est justifié. */
  color: var(--c-blue-zodiac);
}

/* LE CHIFFRE EN MONO — c'est le seul texte du schéma qui n'est pas en display, et
   c'est voulu : une valeur numérique se lit mieux en chasse fixe, et `tabular-nums`
   aligne ses colonnes. */
.fiche-equipes__valeur {
  grid-column: 2;
  grid-row: 1/span 2;
  margin: 0;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--fs-h3);
  line-height: 1;
  color: var(--c-blue-zodiac);
  white-space: nowrap;
}

/* Les quatre tiges. Elles occupent la même grille que les équipes en dessous,
   pour tomber au milieu de chacune. */
.fiche-equipes__tiges {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  block-size: var(--sp-6);
}

/* POINTILLÉS VERTS ANIMÉS — demandés par Hugo le 18/08/2026, en remplacement d'un
   filet bleu continu.

   Le pointillé est un dégradé RÉPÉTÉ et non une bordure `dashed` : une bordure ne
   peut pas être animée, alors qu'un fond se déplace. `background-size` fixe le pas
   du motif, et l'animation décale le fond d'exactement un pas — la boucle est donc
   invisible.

   Le sens va de la fiche VERS les équipes, ce qui donne au schéma sa direction de
   lecture. */
.fiche-equipes__tiges > span {
  inline-size: 2px;
  justify-self: center;
  background-image: repeating-linear-gradient(to bottom, var(--c-pilier-dictionnaire) 0 4px, transparent 4px 9px);
  background-size: 100% 9px;
  animation: fiche-equipes-flux 1.1s linear infinite;
}

@keyframes fiche-equipes-flux {
  to {
    background-position-y: 9px;
  }
}
/* ⚠️ L'ANIMATION S'ARRÊTE EN MOUVEMENT RÉDUIT, et les pointillés restent : le
   schéma garde son dessin, il cesse seulement de bouger. C'est la règle du site —
   chaque module de mouvement court-circuite son animation, jamais son contenu. */
@media (prefers-reduced-motion: reduce) {
  .fiche-equipes__tiges > span {
    animation: none;
  }
}
.fiche-equipes__equipes {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--sp-2);
  margin: 0;
  padding: 0;
  list-style: none;
}

.fiche-equipes__equipe {
  padding: var(--sp-4) var(--sp-2);
  border: var(--filet-panneau);
  background-color: var(--c-white);
  font-family: var(--font-serif);
  font-weight: var(--fw-semibold);
  font-size: var(--fs-xs);
  text-align: center;
  color: var(--c-blue-zodiac);
}

/* =========================================================================
   LE COMPOSEUR DE KPI — Figma 185:10948, ajouté le 26/08/2026 pour la section
   « Chacun construit la lecture dont il a besoin » de /plateforme/.

   ⚠️ C'EST UN DESSIN AUX COORDONNÉES DE LA PLANCHE, pas une recomposition.
   Demande d'Hugo le 26/08/2026 : « fais en sorte que la composition ait le même
   rendu que sur l'image ». Un premier jet la rejouait en grille de flux — deux
   colonnes, un rail par famille, une tige unique à la place des six connecteurs.
   Il tenait à toute largeur, mais ce n'était pas le dessin ; ne pas y revenir.
   La liste complète de ce que la planche fait et que cette figure ne fait pas est
   en tête de la branche, dans blocs/texte-module.njk.

   ---------------------------------------------------------------------------
   L'ESPACE DE DESSIN

   443,2 × 290,45 unités, les cotes de la planche — 442,51 de contenu plus les
   0,67 que le cap arrondi du dernier pointillé déborde à droite. Tout enfant du
   plan est absolu, et TOUTE cote est un multiple de `--u`, la largeur du plan
   divisée par 443,2 : positions, tailles, filets, rayons, corps de texte.

   `--u` est en `cqi` et non en `%` parce qu'une largeur en pourcentage ne se
   transmet pas à un `font-size`. Le conteneur de requête est LE PLAN lui-même,
   pas la figure : une propriété personnalisée est substituée là où elle est
   LUE, donc chaque enfant résout `cqi` contre son plus proche ancêtre conteneur,
   qui est bien le plan.

   ⚠️ LES POLICES SUIVENT L'ÉCHELLE DU DESSIN, et ce n'est pas une entorse à
   l'échelle du site. La planche est large de 442 px avec des libellés de
   9,751 px : c'est sa taille de rendu prévue, l'image partagée n'en est qu'un
   agrandissement. À la largeur que cette colonne donne réellement (~450 px en
   deux colonnes, ~950 px quand la page repasse en une seule), ces 9,751 px
   tombent entre 9,9 et 21 px, et à ~616 px ils valent 13,6 px — soit --fs-xs à
   un dixième près, --fs-mono pour les 7,978 px de « Afficher » et « Par ». Ce
   qui a été adapté, ce sont les FAMILLES (Poppins → Inter), ce que demandait la
   première consigne et qu'un export SVG aurait figé dans le fichier.

   ⚠️ AUCUNE LARGEUR MINIMALE, donc aucune barre de défilement : sous ~440 px le
   dessin devient une vignette. C'est la dégradation choisie — une figure
   décorative qui rétrécit vaut mieux qu'un décor qui se défile.

   ⚠️ LES LARGEURS DES JETONS SONT CELLES DE LA PLANCHE, pas celles de leur
   texte, et c'est ce qui fait tomber les connecteurs pile sur les bords : le
   trait bleu part du flanc droit de « Nouveaux clients ». Inter étant plus
   étroite que Poppins à cote égale, le texte tient.

   ⚠️ LE DESSIN PUBLIE SON COMPTE — trois métriques plus celle qui est retenue,
   deux dimensions plus celle qui est retenue. Une entrée de plus dans les
   données n'aurait aucune position où se poser. C'est assumé : un dessin n'est
   pas une liste.

   Elle vit dans cette feuille et non dans _components.scss : un seul bloc
   l'emploie. Même règle que `.fiche-equipes` et `.orbite` plus haut.
   ========================================================================= */
/* Une cote de la planche, en unités du plan. Écrire `calc(157.692 * var(--u))`
   cent fois rendrait le relevé illisible ; ainsi chaque nombre de la feuille est
   exactement celui de la maquette, et se retrouve à la lecture. */
.composeur {
  margin: 0;
}

.composeur__plan {
  container-type: inline-size;
  position: relative;
  inline-size: 100%;
  aspect-ratio: 443.2/290.45;
  --u: calc(100cqi / 443.2);
  /* ---------- Les encres du dessin ----------
     ⚠️ LE ROSE EST POSÉ EN DUR, comme `.fiche-equipes` pose en dur le vert du
     dictionnaire : cette figure EST celle de la page plateforme, elle montre son
     produit. Elle ne lit donc pas `--c-blue`, qui la ferait suivre n'importe
     quelle page hôte.

     ⚠️ ET LE BLEU NON PLUS N'EST PAS `--c-blue`. La planche code ses deux
     familles par la couleur — bleu pour la métrique retenue, rose pour la
     dimension —, et cette distinction est le sujet même du schéma : on affiche
     une métrique PAR une dimension. Or sur cette page `--c-blue` EST le rose :
     les deux familles auraient fondu l'une dans l'autre. C'est donc
     `--c-blue-biron`, le bleu de marque que rien ne réassigne, qui a été promu en
     jeton ce jour-là pour cet usage. Il donne 5,92 sur blanc.

     ⚠️ LE ROSE A DEUX VALEURS ET ELLES NE SE SUBSTITUENT PAS. Le contour et les
     pointillés gardent la teinte du pilier à PLEINE FORCE — c'est la règle 16, et
     c'est ce qui garde le rendu de la planche. L'ENCRE, elle, passe au rose
     sombre : #ff5d73 ne donne que 2,97 sur blanc, quand
     `--c-pilier-plateforme-sombre` en donne 4,85 et que le commentaire de ce
     jeton dit « utilisable telle quelle comme encre de badge ».
     ⚠️ Ne pas le « ramener » sur un color-mix du rose vers le bleu nuit : c'est
     une couleur choisie à la main, mélanger désature.

     `--composeur-trait` remplace le #BEC5CC de la planche pour les rails et les
     coudes gris, un cran au-dessus de `--border-subtle` — ce sont deux gris
     distincts dans la maquette et ils le restent ici. */
  --composeur-metrique: var(--c-blue-biron);
  --composeur-teinte: var(--c-pilier-plateforme);
  --composeur-encre: var(--c-pilier-plateforme-sombre);
  --composeur-trait: color-mix(in srgb, var(--c-text-main) 26%, transparent);
  --composeur-gris: color-mix(in srgb, var(--c-blue-zodiac) 5%, var(--c-white));
  /* ⚠️ IL REMET LES DEUX RÔLES D'ENCRE EN CONTEXTE CLAIR, ET IL LE DOIT. Tous ses
     fonds sont écrits en clair (`--c-white`) : c'est donc une figure claire quel
     que soit le fond sous elle. Posée dans un `.on-dark`, elle hériterait de
     libellés blancs sur son propre blanc et de filets blancs translucides —
     invisibles tous les deux, et sans le moindre signal. Règle 12 de
     MAQUETTE.md, branche « plusieurs encres », mot pour mot ce que fait
     `.apercu` dans _components.scss. Les deux valeurs sont celles de `:root` et
     doivent rester en phase avec lui. */
  --text-muted: var(--c-fiord);
  --border-subtle: rgba(44, 42, 46, 0.12);
  font-family: var(--font-sans);
  font-weight: var(--fw-medium);
  letter-spacing: -0.01em;
  color: var(--c-text-main);
  /* ⚠️ TOUT LE PLAN EST EN UNE SEULE LIGNE. Ce sont des libellés d'INTERFACE, pas
     du texte courant : un renvoi à la ligne y ferait grandir une boîte dont la
     hauteur est justement relevée sur la planche, et le connecteur qui vise son
     flanc tomberait à côté. */
  white-space: nowrap;
}

/* Aucun enfant du plan n'est dans le flux : chacun est à sa coordonnée de
   planche. C'est la règle unique, les blocs qui suivent ne posent plus que des
   cotes.

   ⚠️ LE CORPS DE TEXTE EST POSÉ ICI ET SURTOUT PAS SUR LE PLAN, et ce n'est pas
   un rangement : **une unité de requête de conteneur ne se résout pas sur le
   conteneur lui-même**. Sur `.composeur__plan`, qui EST le conteneur, `cqi`
   retombe sur son plus proche ancêtre conteneur — ici aucun, donc sur le
   viewport. Le texte prenait alors la largeur de la FENÊTRE pour échelle et
   sortait à plus du double, pendant que les boîtes, elles, restaient justes
   puisqu'elles sont posées sur les enfants. Le défaut est d'autant plus vicieux
   qu'il se voit à peine sur un banc d'essai dont la fenêtre fait la largeur de la
   colonne : il faut le vérifier dans la page, en fenêtre large.

   Tout ce qui reste sur le plan (famille, graisse, approche, encre) est donc sans
   unité de conteneur, et tout ce qui en emploie une vit ici ou plus bas. */
.composeur__plan > * {
  position: absolute;
  margin: 0;
  font-size: calc(9.751 * var(--u));
  line-height: calc(14.183 * var(--u));
}

/* ---------- Les tracés ----------
   Les neuf chemins sont les SVG exportés de la maquette — `viewBox`,
   `stroke-width` et `stroke-dasharray` compris, rien n'est redessiné. Ils sont
   posés à leur boîte exacte, débord du trait inclus (le cap arrondi déborde de
   0,665 de chaque côté, ce que la maquette compte déjà dans ses inset).

   ⚠️ AUCUNE DÉFORMATION : le plan entier est à une SEULE échelle, donc les deux
   axes du SVG sont multipliés par le même facteur. C'est la mise en garde de
   `chemin.js` prise à l'envers — ce qui déforme un pointillé, c'est un facteur
   différent sur chaque axe, jamais un facteur unique. */
.composeur__trait {
  display: block;
  color: var(--composeur-trait);
  overflow: visible;
}

.composeur__trait--haut {
  inset-inline-start: calc(111.846 * var(--u));
  inset-block-start: calc(13.927 * var(--u));
  inline-size: calc(61.5719 * var(--u));
  block-size: calc(32.3742 * var(--u));
}

.composeur__trait--milieu {
  inset-inline-start: calc(115.832 * var(--u));
  inset-block-start: calc(49.406 * var(--u));
  inline-size: calc(39.424 * var(--u));
  block-size: calc(1.32968 * var(--u));
}

.composeur__trait--bas {
  inset-inline-start: calc(117.161 * var(--u));
  inset-block-start: calc(53.841 * var(--u));
  inline-size: calc(80.619 * var(--u));
  block-size: calc(32.3742 * var(--u));
}

.composeur__rail {
  inset-inline-start: calc(9.966 * var(--u));
  inset-block-start: calc(69.807 * var(--u));
  inline-size: calc(11.0747 * var(--u));
  block-size: calc(131.717 * var(--u));
}

.composeur__trait--metrique {
  inset-inline-start: calc(123.363 * var(--u));
  inset-block-start: calc(196.646 * var(--u));
  inline-size: calc(45.1824 * var(--u));
  block-size: calc(15.078 * var(--u));
  color: var(--composeur-metrique);
}

.composeur__trait--dimension {
  inset-inline-start: calc(297.888 * var(--u));
  inset-block-start: calc(83.555 * var(--u));
  inline-size: calc(145.291 * var(--u));
  block-size: calc(126.838 * var(--u));
  color: var(--composeur-teinte);
}

/* ---------- La racine ---------- */
.composeur__racine {
  inset-inline-start: 0;
  inset-block-start: calc(28.783 * var(--u));
  inline-size: calc(124.028 * var(--u));
  block-size: calc(42.575 * var(--u));
  display: flex;
  align-items: center;
  gap: calc(7.077 * var(--u));
  padding-inline-start: calc(9.3 * var(--u));
  border: calc(1.33 * var(--u)) solid var(--border-subtle);
  border-radius: calc(8.865 * var(--u));
  background-color: var(--c-white);
}

/* ---------- Les jetons ----------
   Un jeton neutre est blanc et filet gris ; celui qui est RETENU prend le
   contour de sa famille à pleine force et son encre lisible. */
.composeur__jeton {
  display: flex;
  align-items: center;
  block-size: calc(29.183 * var(--u));
  padding-inline: calc(10.637 * var(--u));
  border: calc(0.886 * var(--u)) solid var(--border-subtle);
  border-radius: calc(3.546 * var(--u));
  background-color: var(--c-white);
  white-space: nowrap;
}

/* Le gris #F6F6F8 de la planche, qui distingue les dimensions des métriques. */
.composeur__jeton--gris {
  background-color: var(--composeur-gris);
}

.composeur__jeton--metrique {
  border-color: var(--composeur-metrique);
  color: var(--composeur-metrique);
}

.composeur__jeton--dimension {
  border-color: var(--composeur-teinte);
  color: var(--composeur-encre);
}

/* Les quatre métriques. Leurs abscisses diffèrent d'un dixième d'unité d'une
   ligne à l'autre : c'est le tremblé de la planche, relevé tel quel. */
.composeur__jeton--m1 {
  inset-inline-start: calc(20.293 * var(--u));
  inset-block-start: calc(78.055 * var(--u));
  inline-size: calc(100.275 * var(--u));
}

.composeur__jeton--m2 {
  inset-inline-start: calc(20.148 * var(--u));
  inset-block-start: calc(113.534 * var(--u));
  inline-size: calc(87.275 * var(--u));
}

.composeur__jeton--m3 {
  inset-inline-start: calc(20.635 * var(--u));
  inset-block-start: calc(149.013 * var(--u));
  inline-size: calc(94.275 * var(--u));
}

.composeur__jeton--m4 {
  inset-inline-start: calc(20.065 * var(--u));
  inset-block-start: calc(184.493 * var(--u));
  inline-size: calc(104.275 * var(--u));
}

/* Les trois dimensions. */
.composeur__jeton--d1 {
  inset-inline-start: calc(166.797 * var(--u));
  inset-block-start: 0;
  inline-size: calc(102.275 * var(--u));
}

.composeur__jeton--d2 {
  inset-inline-start: calc(152.337 * var(--u));
  inset-block-start: calc(35.48 * var(--u));
  inline-size: calc(107.275 * var(--u));
}

.composeur__jeton--d3 {
  inset-inline-start: calc(176.844 * var(--u));
  inset-block-start: calc(70.959 * var(--u));
  inline-size: calc(120.275 * var(--u));
}

/* ---------- La carte ----------
   ⚠️ PAS D'OMBRE, règle 5 de MAQUETTE.md — sur le crème elle se lirait comme une
   salissure. Un filet la remplace, sans quoi cet aplat blanc n'aurait plus de
   bord du tout.

   Elle est NUE : ses champs sont posés au plan comme tout le reste, parce que
   leurs coordonnées sont celles de la planche et non celles d'une mise en page
   interne. */
.composeur__carte {
  inset-inline-start: calc(157.692 * var(--u));
  inset-block-start: calc(113.934 * var(--u));
  inline-size: calc(263.117 * var(--u));
  block-size: calc(176.51 * var(--u));
  border: calc(0.886 * var(--u)) solid var(--border-subtle);
  border-radius: calc(8.865 * var(--u));
  background-color: var(--c-white);
}

.composeur__champ {
  display: flex;
  align-items: center;
  border-radius: calc(8.865 * var(--u));
}

.composeur__champ--indicateur {
  inset-inline-start: calc(168.323 * var(--u));
  inset-block-start: calc(124.578 * var(--u));
  inline-size: calc(107.196 * var(--u));
  block-size: calc(42.575 * var(--u));
  gap: calc(5.307 * var(--u));
  padding-inline-start: calc(9.97 * var(--u));
  border: calc(1.33 * var(--u)) solid var(--border-subtle);
}

/* La pastille déborde du bord haut de la carte et couvre sa fente : elle est
   écrite en DERNIER dans le gabarit, l'ordre du document suffisant à la poser
   par-dessus sans qu'aucun `z-index` soit nécessaire. */
.composeur__champ--periode {
  inset-block-start: calc(102.895 * var(--u));
  /* ⚠️ LARGEUR MINIMALE ET NON FIXE, seule exception du plan. La planche donne à
     cette pastille 117,8 d'encombrement pour un contenu qui en mesure 132 chez
     elle : le texte y déborde de son cadre, ce que Figma tolère et que le
     navigateur, lui, renverrait à la ligne. Elle prend donc la cote de la planche
     comme plancher et grandit vers la GAUCHE si la police l'exige. C'est son bord
     DROIT qui est donc ancré — 9,988 du bord du plan, soit le 433,212 de la
     planche —, et non son bord gauche comme tout le reste du dessin : à droite
     elle rencontrerait le trait rouge qui descend le long du plan et le
     couvrirait, alors qu'à gauche elle ne mord que sur la fente pointillée,
     qu'elle recouvre déjà aux trois quarts dans la planche. */
  inset-inline-start: auto;
  inset-inline-end: calc(9.988 * var(--u));
  inline-size: max-content;
  min-inline-size: calc(117.827 * var(--u));
  block-size: calc(43.366 * var(--u));
  justify-content: center;
  gap: calc(7.092 * var(--u));
  padding-inline: calc(10.637 * var(--u)) calc(14.183 * var(--u));
  border: calc(0.886 * var(--u)) solid var(--border-subtle);
  background-color: var(--c-white);
}

/* L'emplacement vide où la pastille se dépose. */
.composeur__fente {
  inset-inline-start: calc(296.781 * var(--u));
  inset-block-start: calc(124.578 * var(--u));
  inline-size: calc(107.196 * var(--u));
  block-size: calc(40.801 * var(--u));
  border: calc(1.33 * var(--u)) dashed var(--border-subtle);
  border-radius: calc(8.865 * var(--u));
}

/* « Afficher » et « Par ».
   ⚠️ EN SANS ET EN BAS DE CASSE, pas en chasse fixe capitalisée comme
   `.archi__fiche-label`. Ce n'est pas un libellé DU SITE mais un libellé de
   l'interface représentée : le passer en « AFFICHER » changerait le dessin, ce
   que la consigne du 26/08/2026 exclut. Seule l'encre bouge — le #BEC5CC de la
   planche ne donne que 1,9. */
.composeur__label {
  font-size: calc(7.978 * var(--u));
  color: var(--text-muted);
}

.composeur__label--afficher {
  inset-inline-start: calc(170.095 * var(--u));
  inset-block-start: calc(181.345 * var(--u));
}

.composeur__label--par {
  inset-inline-start: calc(298.553 * var(--u));
  inset-block-start: calc(181.345 * var(--u));
}

.composeur__jeton--afficher {
  inset-inline-start: calc(168.323 * var(--u));
  inset-block-start: calc(195.537 * var(--u));
  inline-size: calc(107.196 * var(--u));
  block-size: calc(28.384 * var(--u));
  padding-inline-start: calc(11.52 * var(--u));
}

.composeur__jeton--par {
  inset-inline-start: calc(295.895 * var(--u));
  inset-block-start: calc(195.137 * var(--u));
  inline-size: calc(105.424 * var(--u));
  justify-content: center;
  padding-inline: calc(3.212 * var(--u));
}

.composeur__filet {
  inset-inline-start: calc(168.323 * var(--u));
  inset-block-start: calc(238.112 * var(--u));
  inline-size: calc(235.653 * var(--u));
  block-size: calc(0.887 * var(--u));
  background-color: var(--border-subtle);
}

/* ---------- Les deux boutons DESSINÉS ----------
   Voir le gabarit : rien n'est cliquable dans une illustration.

   ⚠️ L'APLAT PRINCIPAL NE PREND PAS LE ROSE DE LA PLANCHE (#EE6873), qui ne porte
   le blanc qu'à 3,06. Il prend le rose sombre du pilier, 4,85 sous le même
   libellé blanc — et c'est aussi le plus proche visuellement, là où
   `--btn-primary-bg` (72 % vers le bleu nuit) aurait viré au bordeaux. */
.composeur__bouton {
  display: flex;
  align-items: center;
  border-radius: calc(1.773 * var(--u));
}

.composeur__bouton--secondaire {
  inset-inline-start: calc(190.471 * var(--u));
  inset-block-start: calc(249.643 * var(--u));
  inline-size: calc(94.793 * var(--u));
  block-size: calc(26.61 * var(--u));
  gap: calc(3.537 * var(--u));
  padding-inline-start: calc(7.09 * var(--u));
  background-color: var(--composeur-gris);
  color: var(--text-muted);
}

.composeur__bouton--principal {
  inset-inline-start: calc(301.21 * var(--u));
  inset-block-start: calc(249.643 * var(--u));
  inline-size: calc(88.645 * var(--u));
  block-size: calc(26.594 * var(--u));
  justify-content: center;
  background-color: var(--composeur-encre);
  font-size: calc(8.64 * var(--u));
  line-height: calc(15.716 * var(--u));
  color: var(--c-white);
}

/* ---------- Les pictogrammes ----------
   Le pictogramme accompagne son libellé, il ne le concurrence pas — c'est aussi
   ce que dit la planche, qui les pose en #BEC5CC contre du #484851. `flex: none`
   parce qu'un SVG sans dimensions intrinsèques se laisse écraser. */
.composeur__icone {
  flex: none;
  inline-size: calc(14.183 * var(--u));
  block-size: calc(14.183 * var(--u));
  color: var(--text-muted);
}

/* =========================================================================
   L'HISTOGRAMME EN TRAME — la figure `barres`, ajoutée le 26/08/2026 pour la
   section 05 de /plateforme/ : « un bar chart halftone animé […] on doit avoir un
   fonctionnement identique au donut chart de Pensé pour les entreprises qui
   pilotent par la donnée ».

   Il est DESSINÉ dans un canvas par src/assets/js/barres.js, et le pourquoi est en
   tête de ce fichier-là : garder des points entiers sous une arête nette demande de
   décider POINT PAR POINT, ce qu'un masque CSS, qui décide pixel par pixel, ne sait
   pas faire. Il ne reste donc ici que le CADRAGE et l'ENCRE — c'est le partage que
   `.texte-halftone__anneau` fait déjà avec cible.js.

   Les réglages d'allure (pas de la grille, gouttière, bruit) sont en attributs
   `data-` sur le canvas, dans blocs/texte-module.njk.

   ⚠️ CE N'EST PAS LE DONUT DE LA BOÎTE VERTE, plus haut dans ce fichier. Celui-là
   est un `conic-gradient` percé par un masque, figé, et l'ornement d'UNE boîte
   sombre. Celui-ci est un canvas animé posé sur le crème — la famille de l'anneau
   de l'accueil.

   ---------------------------------------------------------------------
   L'AMPLEUR. Demandée par Hugo le 26/08/2026 : « tu peux lui donner plus
   d'ampleur ». Elle vient de deux choses, et d'aucune retouche du dessin.

   LE CARRÉ. Le ratio était celui du cadre en attente de cette disposition
   (410 × 355, Figma 167:8414), pour que la figure et le placeholder se remplacent
   sans changer la hauteur de la page. C'est ABANDONNÉ : un décor se dimensionne
   pour ce qu'il doit peser, pas pour la cote d'un cadre qui n'est plus là, et
   `.texte-module__placeholder` garde de toute façon sa propre règle — seule la
   hauteur de la section bouge si un jour on échange les deux. Le carré est celui
   de `.texte-halftone__visuel`, où c'est déjà le disque qui commande.

   LE DÉBORD. La figure sort de sa colonne vers la DROITE, comme l'anneau de
   l'accueil sort de la sienne (`width: 128%`). C'est le seul côté possible : à
   gauche, elle entrerait dans le texte.

   ⚠️ LE DÉBORD EST BORNÉ PAR LA GOUTTIÈRE DE PAGE, ET IL LE FAUT. Une valeur en
   pourcentage comme celle de l'anneau marche pour un DISQUE, qu'on peut laisser
   couper par le bord de l'écran — c'est même l'idiome du site. Un histogramme, non :
   une barre coupée en deux se lit comme un défaut, pas comme un décor. Le débord
   vaut donc « la place réellement libre à droite », soit le `padding` du conteneur
   plus la gouttière de page, moins 2rem de dégagement — et il tombe à presque rien
   quand la gouttière n'existe pas.

   ⚠️ AUCUN POINT DE RUPTURE N'EST PUBLIÉ, la formule s'en charge : sous la largeur
   maximale du conteneur, le `max(0px, …)` annule la gouttière et il ne reste que le
   `padding`. C'est le parti de `.decor-formes__bande`
   (_components.scss).

   ⚠️ LES 2rem DE DÉGAGEMENT NE SONT PAS DU CONFORT : `100vw` compte la barre de
   défilement, que le conteneur ne voit pas. Sans marge, le débord la surestimerait
   d'une quinzaine de pixels et mordrait la dernière barre. 2rem (27 px) couvre
   n'importe quelle barre de défilement.

   ⚠️ LE PLAFOND DE 9rem EXISTE PARCE QUE LA FIGURE EST CARRÉE : sans lui, un écran
   très large lui donnerait 400 px de plus en largeur, donc 400 de plus en HAUTEUR, et
   la section deviendrait plus haute que l'écran.

   Ce que ça donne, cellule de la disposition `tiers` (conteneur à 1088 px, le
   `font-size: 85 %` de la racine rebasant le rem) :

     fenêtre    colonne   débord   figure      barre
     960 px      286 px     8 px   294 × 294    36 px
     1280 px     349 px    69 px   418 × 418    60 px
     1920 px     345 px   122 px   467 × 467    72 px

   Contre 349 × 302 et des barres de 48 px avant. La hauteur gagne le plus, et c'est
   tant mieux : plus de rangs, donc plus de points qui changent quand une barre monte.

   ---------------------------------------------------------------------
   L'ENCRE. Demandée par Hugo le 26/08/2026 : « mais en rouge plateforme foncé ».

   ⚠️ LA TEINTE SOMBRE DU PILIER ET NON LE ROSE VIF, et c'est mesuré : #bc4b64 pèse
   4,60 sur le crème, le rose vif 2,82. Une trame n'est pas un aplat — c'est de
   l'encre fine, des cercles de 3 px séparés par du fond — donc elle demande PLUS de
   contraste qu'une surface pleine, pas moins. C'est exactement l'arbitrage déjà
   consigné pour la section 03 du dictionnaire, où le vert sombre du pilier (5,29 sur
   le crème) était retenu tant qu'il s'agissait d'un GRAPHIQUE, et abandonné au
   profit du fluo le jour où le graphique est devenu une forme de marque. Un
   graphique prend la teinte sombre, une forme prend le fluo.

   ⚠️ NE PAS LA RECONSTRUIRE EN `color-mix` DU ROSE VERS LE BLEU NUIT. Le jeton
   `--c-pilier-plateforme-sombre` a été choisi à la main par Hugo le 19/08/2026
   précisément parce qu'un mélange désature — son propre commentaire dans
   _tokens.scss le dit et l'interdit.

   ⚠️ LE DÉFAUT EST DANS LE REPLI DU `var()`, jamais en déclaration : c'est la règle
   du site pour tout bouton qu'un ancêtre doit pouvoir reprendre. Une propriété posée
   SUR l'élément bat la même propriété héritée, donc un `--barres-encre` déclaré ici
   masquerait celui qu'une classe de page ou de ton poserait plus haut.

   ⚠️ ET C'EST L'ENCRE DE CET EMPLOI-LÀ, pas une propriété de la figure. La figure
   vit dans un bloc que tout le site emploie : une seconde page qui la voudrait devra
   DIRE son encre, en posant `--barres-encre`. C'est mot pour mot la note du donut
   plus haut — « l'ornement de CETTE boîte-là, pas une propriété des boîtes ».
   ========================================================================= */
.barres-trame {
  /* La place réellement libre à droite de la colonne : le `padding` du conteneur,
     plus la gouttière de page quand elle existe, moins le dégagement. Plafonnée,
     parce que la figure est carrée et que la largeur commande la hauteur. */
  --barres-debord: min(
    9rem,
    var(--container-pad) + max(
        0px,
        (100vw - var(--container-max) - var(--container-pad) * 2) / 2
      ) - 2rem
  );
  position: relative;
  inline-size: calc(100% + var(--barres-debord));
  aspect-ratio: 1;
  /* Lue par le script en `getComputedStyle`, et c'est ce qui laisse la couleur être
     un JETON : un `data-encre` en dur dans le gabarit ne serait plus rattaché au
     pilier, et un `var()` ne se résout pas dans un attribut. */
  color: var(--barres-encre, var(--c-pilier-plateforme-sombre));
}

/* Le canvas remplit la boîte. Sans dimensions il garderait ses 300 × 150 par défaut,
   et le script mesurerait cette taille-là.

   ⚠️ ÉPINGLÉ EN ABSOLU ET NON EN HAUTEUR À 100 %, et c'est le dispositif de
   `.texte-halftone__anneau` : une hauteur en pourcentage se résout contre le parent,
   dont la hauteur ici ne vient pas d'une déclaration mais de son `aspect-ratio`.
   `inset: 0` ne pose pas la question. Les deux cotes sont gardées par-dessus pour
   battre le `height: auto` que _base.scss pose sur tous les `canvas`. */
.barres-trame__toile {
  position: absolute;
  inset: 0;
  display: block;
  inline-size: 100%;
  block-size: 100%;
}

/* Sans ça, le débord de la figure ouvre une barre de défilement horizontale sur
   toute la page — même besoin et même remède que les formes de décor plus haut, mais
   dans un sélecteur à part : celui-là parle des formes, et fusionner les deux ferait
   croire que le `clip` vient d'elles.

   `overflow-x` seul : `overflow-y` reste `visible`, deux valeurs prises dans
   {visible, clip} ne se contraignant pas l'une l'autre. Rien ne dépasse
   verticalement ici, mais le principe est celui de `.chemin`. */
.texte-module:has(.barres-trame) {
  overflow-x: clip;
}

/* ⚠️ RIEN EN DESSOUS DE `lg` : le graphique n'est pas rendu du tout en mode mobile,
   demandé par Hugo le 27/08/2026. C'est la même règle que le motif de
   `onglets-metiers` et que `.chemin`, et pour la même raison — LE DÉCOR PERD SA
   PRÉMISSE, il ne se dégrade pas. La figure ne tient en regard du texte que par
   `tiers`, qui ne pose sa grille qu'à partir de `lg` ; empilée, elle tombait pleine
   largeur SOUS le corps, en un carré de la largeur de la page (342 px de côté à
   390 px de fenêtre) qui creusait un trou entre la fin du texte et la section
   suivante.

   ⚠️ MASQUER SUFFIT, IL N'Y A RIEN À FAIRE DANS LE SCRIPT : `barres.js` mesure le
   canvas au montage et abandonne sur une boîte nulle (`if (!redimensionner())
   return`), donc aucune boucle n'est lancée. Son écouteur de redimensionnement est
   posé AVANT ce garde-fou : si la fenêtre repasse au-dessus de `lg`, le graphique se
   dessine — en image fixe, l'observateur d'intersection ne rejouant pas sans
   changement de visibilité. C'est une dégradation acceptée pour un cas de figure
   (redimensionner une fenêtre à travers le point de rupture) que personne ne fait
   sur un téléphone. */
@media (max-width: 60rem) {
  .barres-trame {
    display: none;
  }
}
/* ---------- La chute ----------
   Les paragraphes qui referment la section, sous le module. Un filet les en
   sépare, comme la maquette. */
/* ⚠️ AUCUN FILET AU-DESSUS DE LA CHUTE, retiré le 18/08/2026 à la demande d'Hugo —
   deux fois, pour la section du diagramme puis pour celle du schéma. Il y en avait
   un. C'est l'espace qui sépare la chute de ce qui la précède, comme partout ailleurs
   dans ce bloc depuis que les tiretés entre entrées ont sauté eux aussi.

   Le `padding-block-start` est parti avec : il n'existait que pour décoller le texte
   du filet. Toute la respiration est maintenant dans la marge. */
.texte-module__chute {
  margin-block-start: clamp(var(--sp-8), 3vw, var(--sp-12));
  max-width: 72ch;
  color: var(--text-strong);
}

.texte-module__chute p {
  margin: 0;
  font-size: var(--fs-base);
  line-height: var(--lh-normal);
}

.texte-module__chute p + p {
  margin-block-start: var(--sp-4);
}

/* =========================================================================
   DISPOSITION « centre » — l'en-tête et la quote centrés sur la section.

   Demandée par Hugo le 20/08/2026 pour la section 01 de « Pourquoi Biron » :
   « pour la section 1 tu peux centrer le titre et la quote ». Le module, lui, reste
   un panneau pleine largeur — c'est un quadrillage, il n'y a rien à y centrer.

   ⚠️ CE N'EST PAS UNE DISPOSITION DU MÊME GENRE QUE LES SIX AUTRES, et c'est
   pour ça qu'elle ne partage aucune de leurs règles. `colonnes`, `tiers`, `duo`,
   `portrait`, `pleine` et `rangee` AGENCENT les objets de la section — où tombe le
   titre, où tombe le visuel — et posent pour ça une grille sur `__inner` ou sur le
   corps. Celle-ci ne déplace rien : elle ne change que l'alignement. Elle se combine
   donc avec n'importe quel module, alors que les six autres s'excluent entre elles.

   ⚠️ LA BARRE FLUO DE LA QUOTE RESTE À GAUCHE DE SON BLOC, et le bloc est centré —
   pas le texte dans le bloc. C'est la seule façon de garder la barre signifiante :
   une barre devant un texte centré ne touche plus son premier caractère, elle flotte
   à distance et redevient un ornement. Ce qui est centré, c'est l'objet entier.

   ⚠️ D'OÙ LA MESURE BRIDÉE. Sans `max-width`, un bloc en `fit-content` occupe toute
   la rangée dès que la phrase est longue, et le centrage ne se voit pas. 60ch est ce
   qui laisse la phrase de la section 01 sur trois lignes en gardant du crème de
   chaque côté.
   ========================================================================= */
/* `text-align` et des marges automatiques, et non `align-items` : `__entete` est un
   bloc ordinaire, pas une pile flex — `align-items` n'y ferait rien. Ce sont les
   `max-width` du titre (30ch) et du texte (68ch) qui rendent les marges nécessaires :
   sans elles, les deux resteraient ferrés à gauche dans une bande centrée. */
.texte-module--centre .texte-module__entete {
  text-align: center;
}

/* Le badge suit l'en-tête centré. `margin-inline: auto` et non `text-align` : il
   est en `width: fit-content`, donc c'est sa boîte qu'il faut centrer, pas son
   texte dans une boîte pleine largeur. */
.texte-module--centre .texte-module__badge {
  margin-inline: auto;
}

/* ⚠️ LA CARTE D'ÉTIQUETTES SE BRIDE ET SE CENTRE, et la mesure EST l'objet :
   demande d'Hugo le 21/08/2026 — « la partie en coulisse peut être centrée avec le
   bloc de tags dans un rectangle de plusieurs lignes et pas seulement une ligne de
   badges ». Sans `max-width`, sept pilules tiennent sur une seule ligne au-delà de
   1100 px et le rectangle n'est plus qu'un long filet.

   40rem est ce qui les replie sur trois lignes à pleine largeur tout en gardant deux
   pilules par ligne au repli — c'est-à-dire un rectangle à toutes les largeurs, ce
   qui est justement ce qu'on demande. */
.texte-module--centre .texte-module__carte {
  max-width: 40rem;
  margin-inline: auto;
}

/* Les pilules se centrent dans le rectangle : ferrées à gauche, la dernière ligne
   incomplète décale visuellement tout le bloc et le centrage du rectangle ne se lit
   plus. */
.texte-module--centre .texte-module__carte .carte-etiquettes__liste {
  justify-content: center;
}

.texte-module--centre .texte-module__titre,
.texte-module--centre .texte-module__texte {
  margin-inline: auto;
}

/* Les liens sont un `.btn-group`, ferré à gauche par défaut dans ce bloc — voir la
   règle de `__liens`. Aucune page centrée n'en porte aujourd'hui ; la règle est là
   pour que la disposition reste vraie si l'une en déclare. */
.texte-module--centre .texte-module__liens {
  justify-content: center;
}

.texte-module--centre .texte-module__exergue {
  max-width: 60ch;
  margin-inline: auto;
}

/* =========================================================================
   LES CARTES BLEUES ET LEUR « + ».

   Demandées par Hugo le 20/08/2026 pour la section 04 de /a-propos/ : « mets La
   plateforme et L'équipe dans deux boîtes d'une autre nuance de bleu avec des coins
   arrondis, enlève le séparateur entre les deux et ajoute un plus à la place. »

   ⚠️ C'EST `module.cartes` AVEC UN `ton` QUI N'EST PAS UN PILIER, et c'est le premier.
   La teinte est un bleu ÉCLAIRCI DEPUIS LE FOND DE LA BOÎTE — `--c-blue` mélangé au
   bleu nuit — et non une couleur choisie à part : les cartes sont DANS la boîte bleu
   nuit, donc leur nuance doit se lire comme une variation du même bleu, pas comme une
   couleur invitée. C'est aussi pourquoi le mélange part du bleu du site : à
   `--c-blue` pur les cartes sortiraient de la boîte.

   ⚠️ LE BLANC Y TIENT : 22 % de #3f58d7 dans #202b5c donne #26326b, sur lequel le
   blanc passe à 11,2 et le fluo à 9,6. `.on-dark` reste donc valable tel quel, ce qui
   n'aurait pas été le cas d'un bleu plus clair — c'est la borne à retenir si la
   nuance doit changer.
   ========================================================================= */
.texte-module__entrees--cartes-bleu > .texte-module__entree {
  background-color: color-mix(in srgb, var(--c-blue) 22%, var(--c-blue-dark));
}

/* ---------- Le « + » entre les deux cartes ----------
   ⚠️ UN PSEUDO-ÉLÉMENT ET NON UNE BALISE, et ce n'est pas un raccourci : le
   conteneur des entrées est un `<ul>`, où seul un `<li>` est valide. Un `<span>` y
   aurait été du balisage invalide, un `<li>` aurait été annoncé comme une entrée de
   la liste. Le `::before` évite les deux — et il est légitime parce que le « + » est
   purement décoratif : le titre de la section dit déjà « une plateforme ET une
   équipe ».

   ⚠️ IL EST POSÉ DANS LA GOUTTIÈRE, en absolu au centre du conteneur : à deux
   colonnes, le milieu du conteneur EST le milieu de la gouttière. Rien à calculer.

   ⚠️ ET IL NE VAUT QUE POUR DEUX CARTES. À trois, le centre du conteneur tombe au
   milieu de la carte du milieu. Le gabarit ne le vérifie pas — un « + » posé sur une
   carte se voit immédiatement. */
/* ⚠️ LA GOUTTIÈRE S'ÉLARGIT QUAND IL Y A UN « + », et c'est nécessaire, pas
   décoratif : à la gouttière courante des cartes (16→24px) le signe touchait les deux
   arêtes. Demande d'Hugo du 20/08/2026 — « il manque un peu de marge entre les deux
   boîtes et le plus, il doit plus respirer ».

   Elle est déclarée ICI et non sur `--cartes`, pour que les autres pages à cartes —
   les deux blocs Zapier / n8n de /integrations/ — gardent leur gouttière serrée :
   elles n'ont rien à loger entre leurs cartes. */
.texte-module__entrees--plus {
  position: relative;
  column-gap: clamp(var(--sp-10), 4vw, var(--sp-16));
}

.texte-module__entrees--plus::before {
  content: "+";
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 1;
  font-family: var(--font-titre);
  font-size: var(--fs-h3);
  font-weight: var(--fw-bold);
  line-height: 1;
  color: var(--text-accent);
}

@media (max-width: 60rem) {
  /* En une colonne, le « + » se retrouve à cheval sur une carte : on le retire,
     l'empilement dit l'addition tout seul. */
  .texte-module__entrees--plus::before {
    content: none;
  }
}
/* =========================================================================
   LA COLONNE DE REPÈRES — le champ `reperes`.

   Même atome que le bloc `reperes` (partials/repere.njk, apparence dans
   _components.scss), autre placement : là une rangée qui fait sa propre section,
   ici une COLONNE dans la boîte d'un texte qu'elle conclut.

   ⚠️ AUCUN FILET ENTRE LES REPÈRES, et c'est un choix d'Hugo du 20/08/2026 — « pas
   besoin de séparateurs ». La colonne a d'abord porté les filets par la gouttière,
   comme la rangée du bloc `reperes` ; en colonne ils découpaient la moitié droite de
   la boîte en tranches, alors que trois chiffres empilés se distinguent déjà par leur
   corps. C'est le même parti que la pile d'entrées de ce bloc, dont le tireté a sauté
   le 18/08/2026 : c'est l'espace qui sépare.

   ⚠️ CONSÉQUENCE : LE FOND DES REPÈRES N'A PLUS À ÊTRE OPAQUE. Il l'était pour
   repeindre par-dessus le fond de gouttière ; sans gouttière teintée, il n'y a plus
   rien à couvrir, et le laisser aurait masqué le fond de la boîte.

   ⚠️ ET ILS SONT CENTRÉS SUR LES DEUX AXES dans leur moitié — même demande. Le
   centrage HORIZONTAL est ici (`text-align`), le VERTICAL dans la disposition
   `portrait`, parce que c'est elle qui connaît la cellule de grille à occuper.
   ========================================================================= */
.texte-module__reperes {
  margin: 0;
  display: grid;
  gap: clamp(var(--sp-6), 2.4vw, var(--sp-10));
  text-align: center;
  /* ---------- LA COLONNE EST UNE PLAQUE OPAQUE ----------
     Hugo, le 27/08/2026 : « passe tous les chiffres de ce bloc dans un bloc de la
     couleur du fond, opacité 100 %, je veux un bloc unique ».

     ⚠️ ELLE RÉPOND AU SEUL VRAI DÉFAUT QU'A LAISSÉ L'AGRANDISSEMENT DU GRAPHIQUE
     EN TRAME : le dessin passe derrière les repères depuis le 26/08/2026, et si le
     fluo d'un CHIFFRE y tient encore (5,58 / 3,93 / 2,98 selon la série, à 40px de
     corps), le casper d'un LIBELLÉ tombait à 3,15 / 2,22 / 1,68. La plaque masque
     la trame sous les trois d'un coup : le libellé y retrouve 6,51 et le chiffre
     11,54. Aucune teinte d'encre n'aurait suffi — la série de base du motif est
     claire, donc c'est le FOND qu'il fallait reprendre.

     ⚠️ UNE SEULE PLAQUE, ET C'EST UN RETOUR SUR QUATRE. La colonne a porté un
     cartouche PAR OBJET — un sous chaque libellé, puis un badge voilé aux bords
     flous sous le seul « +10 ans » : quatre aplats de la même couleur dans un
     mouchoir de poche, qui découpaient le dessin en morceaux au lieu de lui opposer
     une surface. Le champ `badge` de partials/repere.njk a été retiré avec, faute
     d'emploi. Ne pas y revenir.

     ⚠️ ELLE NE SE VOIT QUE PARCE QU'IL Y A QUELQUE CHOSE DERRIÈRE, et c'est assumé.
     Un aplat à la couleur du fond, posé sur ce fond, ne se distingue de rien : ce
     qui est voulu est un TROU DANS LE DESSIN, pas une plaque qui se détache — Hugo
     l'a tranché le même jour contre --c-blue-zodiac, que la colonne portait depuis
     le 26/08 (« remplace les autres badges par la couleur du fond de la box »). Là
     où le graphique ne passe pas, la plaque est invisible.

     ⚠️ `--boite-sombre` ET NON --c-blue-dark ÉCRIT EN CLAIR : c'est le jeton que la
     boîte pose pour son propre fond, donc la plaque suit la boîte si celle-ci
     change. Le repli sert le cas d'une boîte claire, où rien ne le déclare.

     ⚠️ RAYON FIXE, JAMAIS --radius-full : un rayon plein se plafonne à la moitié de
     la hauteur, donc la FORME de la plaque changerait avec le nombre de repères et
     la longueur des libellés. C'est la règle posée le 21/08/2026 sur les badges de
     `module.fiche` — « il ne faut pas adapter le radius à la taille ».

     ⚠️ LA RÈGLE VISE LA COLONNE DE REPÈRES DE CE BLOC, pas l'atome. `partials/
     repere.njk` sert aussi la rangée du bloc `reperes` et les cartes de cas clients,
     qui n'ont pas de dessin derrière elles et ne doivent pas changer d'allure. Une
     boîte de ce bloc qui porterait des repères SANS motif hériterait de la plaque —
     c'est assumé : c'est une allure de bloc, pas une rustine locale, et sur le fond
     de la boîte elle ne se voit pas.

     ⚠️ ELLE SE REPLIE SUR SES CHIFFRES, ELLE NE PREND PAS LA COLONNE. Hugo, le
     27/08/2026 : « fais ce bloc plus proche de la colonne de chiffres, avec moins de
     marge à droite et à gauche ». La cellule de droite fait ~570px sur une boîte de
     1197, quand le plus long libellé (« Entreprises accompagnées ») en mesure ~150 :
     étirée, la plaque était surtout du vide. Elle est donc dimensionnée par son
     contenu et recentrée dans sa cellule.

     ⚠️ `justify-self` ET NON `width: fit-content` + `margin-inline: auto`. Sur un
     enfant direct de grille, les marges automatiques ABSORBENT l'espace libre et
     désactivent l'alignement, ce qui rend la largeur utilisée difficile à prévoir —
     c'est la règle déjà posée sur le visuel de `rangee`. `justify-self` fait les deux
     d'un coup : il replie la boîte sur son contenu et la centre.

     ⚠️ ET LE REMBOURRAGE N'EST PLUS CARRÉ : moins sur les flancs que dessus et
     dessous, même demande. Une fois la plaque repliée, ce sont les flancs qui se
     voient — au-dessus et en dessous, la respiration reste nécessaire pour que le
     premier chiffre ne colle pas à l'arête. */
  justify-self: center;
  padding: clamp(var(--sp-5), 2vw, var(--sp-8)) clamp(var(--sp-4), 1.2vw, var(--sp-6));
  border-radius: var(--radius-lg);
  background-color: var(--boite-sombre, var(--c-blue-dark));
}

/* =========================================================================
   DISPOSITION « portrait » — l'en-tête à gauche, le visuel à droite, le module
   pleine largeur en dessous.

   Ajoutée le 20/08/2026 pour la section 05 de /a-propos/ (Figma 144:5097), où la
   maquette pose une photo d'équipe face au texte, puis deux grandes cartes de rôles
   sous les deux.

   ⚠️ C'EST LA SEULE DISPOSITION QUI SÉPARE LE VISUEL DU MODULE. Dans les quatre
   autres, les deux sont côte à côte dans `.texte-module__corps` — et c'est
   exactement pourquoi aucune ne pouvait servir ici : le module fait deux cartes
   pleine largeur, il n'a aucune place à côté d'une photo.

   ⚠️ `display: contents` FAIT DISPARAÎTRE LA BOÎTE DU CORPS, et il le faut : ses
   deux enfants — le module et le visuel — doivent tomber dans deux rangées
   différentes de la grille de `__inner`. Dans leur enveloppe, ils ne pourraient
   qu'être côte à côte. C'est le même mécanisme que `display: contents` sur
   `__entete` dans `colonnes` et `duo`, et pour la même raison.

   Chaque enfant est placé EXPLICITEMENT plutôt que par placement automatique : le
   bloc peut n'avoir ni sous-titre ni liens, et une rangée sautée décalerait tout le
   reste d'un cran.
   ========================================================================= */
@media (min-width: 60rem) {
  .texte-module--portrait .texte-module__inner {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    column-gap: clamp(var(--sp-8), 3.2vw, var(--sp-16));
    row-gap: 0;
    align-items: start;
  }
  .texte-module--portrait .texte-module__entete,
  .texte-module--portrait .texte-module__corps {
    display: contents;
  }
  /* La rangée 1 est celle du badge — voir la note de `.texte-module__badge`. */
  .texte-module--portrait .texte-module__badge {
    grid-column: 1;
    grid-row: 1;
  }
  .texte-module--portrait .texte-module__titre {
    grid-column: 1;
    grid-row: 2;
  }
  .texte-module--portrait .texte-module__sous-titre {
    grid-column: 1;
    grid-row: 3;
  }
  .texte-module--portrait .texte-module__texte {
    grid-column: 1;
    grid-row: 4;
  }
  .texte-module--portrait .texte-module__liens {
    grid-column: 1;
    grid-row: 5;
  }
  /* Le visuel enjambe les quatre rangées du texte et se centre sur leur total :
     c'est ce qui le met en regard du bloc entier et non de son titre seul.

     ⚠️ LA COLONNE DE REPÈRES PREND EXACTEMENT LA MÊME CELLULE, et les deux ne
     peuvent donc pas coexister — c'est écrit dans le gabarit. Elle se cale en HAUT
     et non au centre : une colonne de chiffres alignée sur le haut du titre d'en
     face se lit comme sa contrepartie, centrée elle flotte. */
  .texte-module--portrait .texte-module__visuel {
    grid-column: 2;
    grid-row: 1/6;
    align-self: center;
  }
  /* ⚠️ CENTRÉE SUR LA HAUTEUR DU TEXTE D'EN FACE, et non calée en haut comme
     d'abord : Hugo l'a demandé le 20/08/2026 — « centre les 3 chiffres verticalement
     et horizontalement dans leur partie ». Elle enjambe les quatre rangées de
     l'en-tête, donc `center` la pose au milieu de leur total.

     C'est aussi ce qui justifie d'avoir retiré le calage du premier et du dernier
     repère sur les bords de la colonne : centrée, elle n'a plus de bord sur lequel
     se caler. */
  .texte-module--portrait .texte-module__reperes {
    grid-column: 2;
    grid-row: 1/6;
    align-self: center;
  }
  /* ⚠️ LE VISUEL EST BRIDÉ EN LARGEUR, réduit le 20/08/2026 à la demande d'Hugo —
     « réduis un peu la taille de la photo ». À pleine colonne, la photo pesait plus
     que le texte qu'elle accompagne. Elle reste ferrée à droite de sa cellule pour
     garder la marge de la maquette. */
  .texte-module--portrait .texte-module__visuel {
    width: 100%;
    max-width: 32rem;
    margin-inline-start: auto;
  }
  /* Le module passe sous les deux colonnes. Sa marge haute remplace la gouttière
     de rangée, mise à zéro pour que les rangées vides du texte ne coûtent rien —
     même raisonnement que dans `colonnes`. */
  .texte-module--portrait .texte-module__module {
    grid-column: 1/-1;
    grid-row: 6;
    margin-block-start: clamp(var(--sp-8), 3vw, var(--sp-12));
  }
  /* ---------- Le module à MI-LARGEUR ----------
     Demandé par Hugo le 21/08/2026 pour /cas-clients/hardloop/ : « une petite
     grille qui prend la moitié de la place à gauche quitte à ce que les chiffres
     soient plus petits ».

     Il reste dans la colonne de l'en-tête au lieu de passer sous les deux, et sa
     marge haute se resserre : à mi-largeur il conclut le texte juste au-dessus de
     lui, il ne referme plus la section entière.

     ⚠️ IL NE VAUT QU'EN `portrait`, la seule disposition qui sépare le visuel du
     module. Ailleurs les deux sont déjà côte à côte dans le corps, et il n'y a
     rien à ramener dans une colonne. Rien ne le vérifie ; la classe est simplement
     sans effet.

     ⚠️ LE VISUEL EN FACE DOIT ALORS ENJAMBER LE MODULE, sinon il se centre sur les
     seules rangées du texte et flotte au-dessus d'une grille qui descend plus bas
     que lui. Il passe donc de `1 / 6` à `1 / 7` — les deux valeurs sont accordées
     à la main, et un désaccord ne se voit qu'à l'œil. */
  .texte-module--portrait .texte-module__module--demi {
    grid-column: 1;
    margin-block-start: clamp(var(--sp-6), 2.4vw, var(--sp-8));
  }
  .texte-module--portrait .texte-module__inner:has(.texte-module__module--demi) .texte-module__visuel {
    grid-row: 1/7;
  }
  .texte-module--portrait .texte-module__chute,
  .texte-module--portrait .texte-module__chute-rangee {
    grid-column: 1/-1;
    grid-row: 7;
  }
  /* ---------- LE GRAPHIQUE EN TRAME PARTAGE SON COIN, ICI ET NULLE PART AILLEURS
     ----------
     `portrait` est la première disposition qui OCCUPE la colonne du coin où le
     motif se pose. Chez les quatre autres hôtes de partials/motif.njk, le coin
     bas-droit ne porte rien : l'encre du texte s'arrête bien avant, et 70 % de la
     hauteur de la boîte y passe sans toucher personne. Ici la colonne de droite
     porte les repères — ou le visuel — centrés sur la hauteur du texte d'en face,
     et la colonne de GAUCHE porte les paragraphes jusqu'au pied de la boîte.

     La cote a monté trois fois le 26/08/2026 : 25 % (la bande libre sous les
     repères), puis 50 %, puis 90 % — « fais grossir encore et fais dépasser sur le
     bord droit ». La sortie par le flanc est ce qui rend le reste possible : elle
     envoie hors champ la partie la plus haute et la plus dense du dessin, et laisse
     dans la boîte le reste.

     ⚠️ ELLE A BAISSÉ DE 40 À 30 % LE 27/08/2026 — « décale-le un peu vers la
     gauche, il doit toujours dépasser à droite ». La sortie est le SEUL bouton qui
     déplace le dessin latéralement, et le baisser le rentre dans la boîte : c'est
     donc aussi ce qui le pousse vers les paragraphes, d'où la mesure ci-dessous. En
     dessous de ~24 % il n'y a plus de dégagement du tout ; à 0 le dessin ne
     dépasserait plus, ce qui est exclu par la demande.

     ⚠️ CE QUI BORNE N'EST PAS LA HAUTEUR, C'EST LA COLONNE DE GAUCHE. Le dessin est
     ferré au flanc droit, donc le grossir — ou le décaler vers la gauche — le pousse
     vers les paragraphes, et l'encre de ceux-ci s'arrête à ~575px sur une boîte de
     1197 × 546. La largeur VISIBLE ne peut donc pas dépasser ~576px, quoi qu'on
     fasse — c'est un plafond, pas un réglage. À 90 % de hauteur, `aires` (rapport
     1,55) fait 491 × 759px : 30 % de sortie en laissent voir 531px, et il reste 45px
     de dégagement. Le même calcul sur la bande `lg`→`xl` (boîte ~970 × 540, 55 % de
     hauteur) donne 321px visibles pour un plafond à ~466px.

     ⚠️ ET L'AGRANDISSEMENT NE VAUT QU'À PARTIR DE `xl`, exactement comme pour
     `--centre` et pour la même raison retournée : sous 1280px la boîte se resserre
     pendant que le texte s'allonge en s'enroulant, donc la HAUTEUR grandit quand la
     largeur diminue. Une part de la hauteur y vaut beaucoup plus de largeur — à
     90 % sur une boîte de 970 × 540, le dessin repasserait sous les paragraphes.
     La bande `lg`→`xl` reste donc à 55 %.

     ⚠️ LE DESSIN PASSE MAINTENANT DERRIÈRE LES REPÈRES, et c'est le prix assumé de
     cette taille — aucune cote ne l'évite, parce que les chiffres sont centrés sur
     la hauteur et descendent aux trois quarts de la boîte. Ce qu'il faut savoir :
     le fluo d'un chiffre tient (5,58 sur la série la plus pâle, 3,93 sur la
     médiane, 2,98 sur la base — un corps de 40px), mais le casper d'un LIBELLÉ
     tombe à 3,15 / 2,22 / 1,68. Si ça se voit, la sortie n'est pas la réponse : ce
     sont les repères qu'il faut décentrer, ou --motif-base qu'il faut assombrir.

     ⚠️ LA COTE EST CALÉE SUR CE DESSIN-LÀ, pas sur la disposition seule, et LE
     RAPPORT DU CADRE EST CE QUI COMPTE : la taille est prise sur la hauteur, donc à
     hauteur égale un dessin plus large avance d'autant plus vers le texte. `aires`
     (1,55) laisse 45px de dégagement là où `courbe` (1,87), qu'il a remplacé le
     27/08/2026, en laissait 121 à la même sortie — c'est ce qui a permis de le
     décaler. Un dessin plus large qu'`aires`, ou à encre plus pleine dans son coin
     haut-gauche, remonterait sur les repères ou sous les paragraphes. Changer de
     `motif` ici oblige à refaire la mesure ; rien ne le vérifie. */
  .texte-module--portrait .texte-module__boite {
    --motif-taille: 55%;
    --motif-sortie-droite: 30%;
  }
}
@media (min-width: 80rem) {
  .texte-module--portrait .texte-module__boite {
    --motif-taille: 90%;
  }
}
/* ---------- ET RIEN DU TOUT UNE FOIS LES COLONNES EMPILÉES ----------
   Sous `lg`, `portrait` retombe en une seule colonne et la colonne de droite passe
   EN DESSOUS du texte : les repères — ou le visuel — deviennent alors le pied de la
   boîte, pleine largeur. Le coin bas-droit leur appartient entièrement, et aucune
   cote ne le libère : ce n'est plus une bande à partager, c'est la place de
   quelqu'un d'autre.

   Même arbitrage que chez `onglets-metiers`, qui masque son motif au même point : le
   décor perd sa prémisse plutôt que de se dégrader. */
@media (max-width: 60rem) {
  .texte-module--portrait .texte-module__motif {
    display: none;
  }
  /* ---------- MODE MOBILE : L'EN-TÊTE DANS UNE BOÎTE CRÈME ARRONDIE ----------

     Demande d'Hugo du 28/08/2026, pour la section « Une équipe pluridisciplinaire
     d'experts data » de /a-propos/ : « en mode mobile […] un fond crème et arrondi
     autour du titre et le texte en dessous ».

     ⚠️ RIEN NE BOUGE AU-DESSUS DE `lg` : toute la règle vit dans ce resserrage, et
     le rendu de bureau validé reste au pixel près.

     ⚠️ LA BOÎTE EST LE FOND DE PAGE RENDU OPAQUE, ET C'EST LA TRAME QUI LUI DONNE
     SA RAISON D'ÊTRE. C'est mot pour mot le dispositif de `.hero-page__texte` sous
     `lg`, dont le commentaire détaille le raisonnement, et il naît ici du même
     effondrement : la trame quadrillée part du tiers du panneau et court jusqu'au
     bord de l'écran, donc au large elle ne croise que la colonne de droite — la
     photo, opaque, qui la masque. Une fois les colonnes empilées, l'en-tête reprend
     toute la largeur et elle lui passe derrière sur ses deux tiers droits. La boîte
     la rebouche là où il y a du texte et la laisse respirer autour.

     Elle lit donc `--bg-page` et jamais `--c-base-main` en clair : c'est le fond de
     la page qu'elle restitue, pas une teinte à elle. Même raison que
     `.clients__frame`.

     Pas de filet : c'est la trame tout autour qui dessine l'arête, et un filet
     ferait une seconde annonce. Corollaire — le tiers gauche n'ayant pas de trame,
     c'est à droite que l'arrondi se lit ; à gauche la boîte se confond avec le fond,
     et il n'y a là rien dont il faille détacher le texte.

     ⚠️ ELLE EST CONDITIONNÉE À LA PRÉSENCE DE LA TRAME, et il le faut : les autres
     sections en `portrait` — dont la section 01 de cette même page — n'ont rien
     derrière leur en-tête, donc rien dont le détacher, et un aplat crème sur du
     crème n'y ajouterait que son rembourrage. */
  .texte-module--portrait:has(.texte-module__trame) .texte-module__entete {
    padding: var(--sp-10) var(--sp-8);
    /* Le rayon des boîtes du site — celui de `.temoignages__boite` et des cinq
       autres. Il n'a toujours pas de jeton ; voir CLAUDE.md. */
    border-radius: 1.75rem;
    background-color: var(--bg-page);
  }
}
/* =========================================================================
   LA RANGÉE DE CHUTE — la carte sombre à gauche, l'exergue à droite.

   Origine : la chute de la section 05 de « Pourquoi Biron » (Figma 145:7941), que
   la maquette compose en deux colonnes — un paragraphe à gauche, une phrase en
   serif bleu de 24px à droite.

   ⚠️ ELLE EST HORS DU CADRE BLANC, contrairement à ce qui a existé une heure ce
   jour-là. La carte de conclusion a occupé TROIS places le 20/08/2026, et le
   cheminement dit ce qu'il ne faut pas rétablir :

     1. boîte détachée pleine largeur   deux objets là où la maquette en montrait
                                        un. Écarté : « la partie sombre vient en
                                        conclusion du tableau au dessus ».
     2. dernière ligne DU TABLEAU       cousue au panneau, à angles vifs, contour
        (pleine largeur, dans le cadre) fluo posé par marges négatives de 1px.
     3. CARTE ARRONDIE À MI-LARGEUR     retenu — « fais en fait des bloc arondis
        (hors du cadre)                 qui prennent la moitier de la largeur avec
                                        titre et en dessous le texte ».

   ⚠️ ET C'EST CE QUI RAMÈNE L'EXERGUE À SA PLACE DE MAQUETTE. Il a lui aussi
   voyagé — pleine largeur, puis centré, puis à gauche derrière sa barre fluo — et
   Hugo l'a finalement mis « sur la droite », soit exactement le regard que la
   maquette Figma dessinait. La maquette avait raison sur la PLACE, pas sur la
   forme : ce n'est pas une seconde colonne de texte, c'est une phrase à barre.

   ⚠️ LES DEUX MOITIÉS SONT INDÉPENDANTES. La section 04 de /pourquoi-biron/ n'a
   que la carte, et sa moitié droite reste vide — c'est voulu : la carte garde alors
   la même mesure que dans la section 05, et les deux sections se répondent. Une
   carte qui s'étirerait sur toute la largeur en l'absence d'exergue casserait ce
   parallèle.
   ========================================================================= */
.texte-module__chute-rangee {
  margin-block-start: clamp(var(--sp-8), 3vw, var(--sp-12));
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  /* ⚠️ ÉLARGIE, ET SURTOUT CORRIGÉE. Elle valait
     `clamp(var(--sp-6), 3vw, var(--sp-14))`, et `--sp-14` N'EXISTE PAS dans
     _tokens.scss — l'échelle saute de 12 à 16. Une propriété personnalisée
     introuvable rend tout le `clamp()` invalide, donc la déclaration `gap` était
     purement et simplement ABANDONNÉE au calcul : la barre fluo de la quote touchait
     l'aplat de la carte. C'est le piège des jetons inventés — rien ne le signale, ni
     à la compilation Sass (qui ne résout pas les `var()`) ni à l'exécution.

     Le plancher passe à --sp-8 : à 24px, une barre de 4px reste collée à un aplat
     sombre. Demande d'Hugo, 20/08/2026 : « écarte un peu la quote pour pas que la
     bande fluo touche le bloc sombre ». */
  gap: clamp(var(--sp-8), 4vw, var(--sp-16));
  /* L'exergue se centre sur la hauteur de la carte, qui est la plus haute des deux.
     La maquette les alignait par le BAS (`items-end` sur son nœud 145:7941), ce qui
     marchait entre deux blocs de texte de hauteurs voisines ; face à une carte à
     rembourrage, le bas de la phrase tombait sur le bas de l'aplat et les deux
     paraissaient soudés. */
  align-items: center;
}

/* ---------- La carte de conclusion ----------
   ⚠️ ARRONDI 20px ET NON LES 28px DES BOÎTES DE SECTION. C'est le rayon des cartes
   du site (`module.cartes`), et la raison est la même : une carte posée sous un
   panneau, dans une page qui arrondit ses boîtes à 28, doit se lire comme une carte
   et non comme une petite boîte de section.

   ⚠️ AUCUN LISERÉ, retiré par Hugo le 20/08/2026 — « enlève les liserés autour des
   blocs Avec Biron et Chez Biron ». Le contour fluo avait un sens quand la carte
   était une LIGNE DU TABLEAU : il la cousait au quadrillage et remplaçait le filet
   bleu du cadre sur trois côtés. Hors du cadre, il n'a plus rien à coudre, et un
   anneau fluo autour d'un aplat bleu nuit posé sur crème double une arête que le
   contraste fait déjà — c'est le raisonnement de la règle 1 de MAQUETTE.md, qui
   interdit le contour sur les boîtes sombres du site pour la même raison.

   Ne pas rétablir non plus les marges négatives de 1px qui allaient avec : elles
   servaient à recouvrir le filet du cadre, et feraient maintenant dépasser la carte
   de sa colonne. */
.texte-module__conclusion {
  border-radius: 1.25rem; /* 20px */
  background-color: var(--c-blue-dark);
  padding: clamp(var(--sp-6), 2.4vw, var(--sp-10));
  /* L'intitulé AU-DESSUS du texte, et non en regard dans une colonne étroite comme
     lorsque la carte prenait toute la largeur. À mi-largeur, une colonne d'intitulé
     laisserait au texte moins de 40 caractères par ligne. */
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
}

/* Un `<p>` en police de titrage, pas un `<h3>` — voir le gabarit.

   ⚠️ EN FLUO, demandé par Hugo le 20/08/2026 — « mets le Avec Biron en fluo ». Il
   lit `--text-accent` et non le fluo en dur : sur ce fond, `.on-dark` a déjà mis ce
   rôle au fluo (11,54 sur le bleu nuit), et une couleur écrite en dur serait fausse
   le jour où la carte prendrait un autre fond — c'est exactement ce qui est arrivé à
   `.etapes__boite`, dont l'accent a dû passer par une variable quand la boîte est
   devenue verte (2,25 dessus, illisible). */
.texte-module__conclusion-titre {
  margin: 0;
  font-family: var(--font-titre);
  font-size: var(--fs-h3);
  font-weight: var(--fw-bold);
  line-height: var(--lh-tight);
  letter-spacing: var(--ls-display);
  color: var(--text-accent);
  text-wrap: balance;
}

/* ⚠️ --fs-base ET NON --fs-xs, corrigé le 20/08/2026 : « le texte de la même taille
   que le reste du texte de la page ». C'est le corps de texte du site — celui de
   `.item__corps` dans les cases juste au-dessus, et de `.texte-module__texte p`
   partout ailleurs. La maquette compose d'ailleurs les deux au même corps. */
.texte-module__conclusion-texte p {
  margin: 0;
  font-size: var(--fs-base);
  line-height: var(--lh-normal);
}

.texte-module__conclusion-texte p + p {
  margin-block-start: var(--sp-4);
}

/* ---------- L'exergue ----------
   ⚠️ SA FORME A ÉTÉ FIXÉE PAR UNE MAQUETTE D'HUGO le 20/08/2026, après trois essais
   écartés le même jour — et l'écart entre eux mérite d'être connu avant d'y
   retoucher :

     1. en regard du paragraphe    la maquette Figma, en serif bleu de 24px.
     2. pleine largeur, à gauche   puis CENTRÉE, en --fs-quote (24→40). Écarté : à
                                   cette échelle, centrée et sans attache, la phrase
                                   se lisait comme un titre de section concurrent.
     3. UNE BARRE FLUO À GAUCHE    retenu pour la forme.
     4. remise EN REGARD           retenu pour la place : « mets la quote sur la
                                   droite ». Soit la place de la maquette.

   La leçon : la maquette avait raison sur la PLACE, pas sur la forme.

   ⚠️ C'EST LA BARRE QUI DÉTACHE LA PHRASE, pas sa taille ni son centrage — d'où le
   fer à gauche (une barre devant un bloc centré serait une décoration flottante, pas
   une attache) et le cran de corps en moins.

   ⚠️ L'ENCRE EST CELLE DES TITRES (--c-blue-zodiac) ET NON LA TEINTE D'ACCENT, et
   c'est un changement de rôle, pas de couleur. Tant que la phrase portait toute la
   mise en valeur, l'accent était juste ; maintenant que la barre la porte, la phrase
   est du TITRAGE — et les titres du site restent bleu nuit, y compris sur une page
   pilier (voir --text-accent dans _tokens.scss). D'où la règle qui suit pour un fond
   sombre. */
.texte-module__exergue {
  margin: 0;
  /* La barre. 4px et non 1px : ce n'est pas un filet de séparation mais une attache,
     et à 1px sur le crème elle disparaîtrait à côté d'un corps de 32px. */
  border-inline-start: 4px solid var(--c-fluo);
  padding-inline-start: clamp(var(--sp-4), 1.6vw, var(--sp-6));
  font-family: var(--font-titre);
  font-size: var(--fs-h3);
  font-weight: var(--fw-bold);
  /* --lh-snug et non --lh-tight : sur plusieurs lignes de 32px, 1.08 colle les
     jambages d'une ligne aux capitales de la suivante. Les titres du site peuvent se
     resserrer parce qu'ils tiennent souvent sur une ligne. */
  line-height: var(--lh-snug);
  letter-spacing: var(--ls-display);
  color: var(--c-blue-zodiac);
  text-wrap: balance;
}

/* ⚠️ SEULE, LA QUOTE PREND TOUTE LA RANGÉE — et c'est le seul endroit où les deux
   moitiés ne se comportent pas pareil en l'absence de l'autre. La raison n'est pas
   symétrique, elle est dans le rôle de chaque objet :

     la CARTE seule garde sa moitié   la section 04 de /pourquoi-biron/ n'a pas
                                      d'exergue, et sa carte doit garder la mesure de
                                      celle de la section 05 : les deux sections
                                      portent le même objet au même endroit, et ce
                                      parallèle est ce qui les fait lire ensemble.
     la QUOTE seule prend tout        la section 01 n'a aucune section jumelle à
                                      laquelle s'accorder. À mi-largeur sa phrase
                                      tombait sur quatre lignes et laissait une
                                      moitié vide qui se lisait comme un oubli ; sur
                                      la rangée entière elle tient les deux lignes de
                                      la maquette d'Hugo, à 78 caractères par ligne.

   `:only-child` plutôt qu'une classe : le gabarit n'a rien à savoir de cette
   règle, et le seul état qui compte est « y a-t-il quelque chose en face ». */
.texte-module__exergue:only-child {
  grid-column: 1/-1;
}

/* Dans une boîte sombre, l'encre de titre du site n'est plus lisible — c'est le
   remède de la règle 12 de MAQUETTE.md, appliqué au seul rôle que cet objet écrit en
   clair. La barre fluo, elle, tient sur les deux fonds. */
.texte-module__boite.on-dark .texte-module__exergue {
  color: var(--text-strong);
}

/* En une colonne, la carte passe au-dessus de l'exergue. Le point de rupture est
   `md` et non `lg` : à mi-largeur la carte tient encore ses 40 caractères par ligne
   jusqu'à 900px environ. */
@media (max-width: 56.25rem) {
  .texte-module__chute-rangee {
    grid-template-columns: minmax(0, 1fr);
  }
}
/* =========================================================================
   LE DIAGRAMME D'ARCHITECTURE « .archi » — blocs/texte-module.njk.
   Origine : Figma 3:164 (18/08/2026), RECOMPOSÉ le 25/08/2026 sur la nouvelle
   maquette de la section 01 du dictionnaire.

   La fiche d'un indicateur à gauche, ses quatre rubriques empilées à droite, et un
   trait court qui rattache chacune au flanc de la fiche.

   Il vit ici et non dans _components.scss parce qu'un seul bloc l'emploie — même
   règle que `.fiche-equipes` plus haut. S'il sert une deuxième page, il monte.

   ---------------------------------------------------------------------
   ⚠️ CE QUI A ÉTÉ JETÉ LE 25/08/2026, et qu'il ne faut pas rétablir sans le
   demander à Hugo, qui a tranché ce jour-là pour la maquette :

     la fiche AU CENTRE, entre quatre légendes posées aux quatre coins
       (« l1 fiche l2 » / « l3 fiche l4 » sur trois colonnes) ;
     chaque légende dans un `.cadre` — panneau blanc, filet bleu, coins vifs ;
     quatre CONNECTEURS EN DIAGONALE, portés par un SVG absolu en coordonnées
       relatives (`viewBox="0 0 100 100"` + `preserveAspectRatio="none"` +
       `vector-effect="non-scaling-stroke"`), qui reliait chaque coin au flanc de
       la fiche.

   Le SVG est parti AVEC la disposition, et pas par préférence : quatre traits
   HORIZONTAUX de même longueur, tous logés dans la même gouttière, n'ont plus rien
   à exprimer en pourcentages de la composition. Ce sont donc des pseudo-éléments,
   ce qui supprime du même coup le besoin d'un `z-index` sur les rubriques — le trait
   est à elles, il ne les traverse plus.

   ---------------------------------------------------------------------
   ⚠️ LES RANGÉES SONT CELLES DE LA FICHE, ET C'EST UN `subgrid`. Demande d'Hugo le
   26/08/2026 : « tu peux aligner les 4 items La définition métier etc… avec la bonne
   case du schéma à gauche ? »

   Les rubriques se répartissaient jusque-là sur QUATRE rangées automatiques que la
   fiche couvrait — donc à peu près en face de la ligne qu'elles décrivent, mais
   jamais exactement : quatre rubriques s'étalaient sur l'EN-TÊTE PLUS LES CINQ
   LIGNES, d'où un décalage qui grandissait de la première à la dernière.

   Aucun réglage de cote ne pouvait le corriger, et c'est le point à retenir : deux
   colonnes indépendantes ne partagent des frontières de rangée que si elles
   partagent la grille. La fiche SUBDIVISE donc les rangées du diagramme
   (`grid-template-rows: subgrid`), et sa liste de lignes les subdivise à son tour —
   une rangée par ligne de fiche, la rubrique de même rang posée dans la même. La
   correspondance n'est plus dessinée, elle est structurelle.

   Trois choses vont avec, et aucune n'est cosmétique :

     GOUTTIÈRE DE RANGÉE À ZÉRO   les lignes de la fiche se touchent, séparées par un
       filet ; une gouttière les écarterait. Et en axe subdivisé c'est la gouttière de
       la grille parente qui s'applique, donc les rubriques ne peuvent pas en avoir une
       autre. Ce qui les sépare est leur REMBOURRAGE — `--archi-rang-pad`, la même
       valeur que le rembourrage vertical d'une ligne de fiche, ce qui fait coïncider
       les deux textes sans qu'aucune cote ne soit recopiée.

     PAS D'`overflow: hidden` SUR LA FICHE   par précaution : une boîte forcée dans un
       contexte de formatage indépendant est un cas limite de la sous-grille, où elle
       peut retomber en grille autonome — et la panne serait alors silencieuse, les
       lignes de la fiche se recadençant sur elles-mêmes sans qu'aucune erreur le dise.
       Il ne rognait de toute façon rien : aucun enfant de la carte ne porte de fond qui
       atteigne les coins arrondis, et ses filets sont à mi-carte.

     LA HAUTEUR DE LA FICHE SUIT CELLE DES RUBRIQUES   une rangée se dimensionne sur
       le plus grand de ses deux occupants, donc une rubrique plus longue que la ligne
       en face d'elle étire cette ligne. C'est le prix de l'alignement exact, et c'est
       l'arbitrage que le site fait partout ailleurs (voir `onglets-metiers`) : une
       hauteur écrite à la main serait fausse au premier texte réécrit.

   ⚠️ LE COMPTE DE RANGÉES N'EST PLUS ÉCRIT ICI : c'est `--archi-rangs`, posé en clair
   sur `.archi` par le gabarit — l'en-tête, plus une rangée par ligne de fiche. Une
   ligne de fiche en plus ne demande donc rien dans cette feuille. Le compte des
   RUBRIQUES reste publié, lui, par les quatre règles `.archi__legende--N` plus bas.

   ---------------------------------------------------------------------
   ⚠️ LA LÉGENDE EST HORS DES QUATRE RANGÉES, ET C'EST LA CORRECTION D'UN DÉFAUT
   VISIBLE. Hugo, 25/08/2026 : « les 4 parties La définition métier et tout débordent
   de l'image, ça rend bizarre. »

   La cause n'était ni la taille du texte ni la gouttière : la `<figure>` couvrait les
   quatre rangées, et elle contient la fiche ET sa légende. Les rangées se dimensionnaient
   donc sur fiche + légende, et les rubriques se répartissaient sur cette hauteur-là —
   la dernière tombait en face de la légende, sous le bord de la fiche. Le remède est
   de sortir la légende de la zone couverte, pas de rogner des cotes.

   D'où trois choses qui vont ensemble :

     la figure passe en `display: contents`   son BOÎTIER disparaît, la fiche et la
       légende deviennent des cases de la grille et se placent une par une. Le lien
       figure / figcaption reste porté par le DOM, donc l'annonce à la voix tient. Si
       un vieux moteur perd la sémantique, la légende redevient un paragraphe — une
       dégradation bénigne, ce qui est ce qui a fait préférer cette voie au `subgrid`
       (six déclarations et une gymnastique d'alignement pour le même résultat).
     PLUS DE `grid-template-areas`   chaque case est placée par ses lignes. Les aires
       auraient demandé une rangée de plus écrite en clair, donc une rangée VIDE — et
       une gouttière morte — sur un diagramme sans légende. Ici la rangée qui suit la
       fiche est IMPLICITE : elle n'existe que si la légende existe. La légende ne
       nomme d'ailleurs plus sa rangée du tout depuis le passage en `subgrid` — elle
       demande la colonne 1 et le placement automatique la met sous la fiche, quel que
       soit le nombre de lignes de celle-ci.
     la légende porte sa propre marge   c'était la gouttière de rangée qui l'écartait
       de la fiche jusqu'au 26/08/2026 ; cette gouttière est désormais à zéro (voir la
       note sur les rangées ci-dessus), donc sans marge propre la phrase collerait au
       bord de la carte.
   ========================================================================= */
.archi {
  /* La longueur du trait de rattachement, et la gouttière qui doit le contenir. Les
     deux sont liées par le calcul : le trait est posé DANS la gouttière, donc une
     gouttière plus courte que lui le ferait passer sous la fiche. Deux `clamp()`
     recopiés auraient dérivé au premier réglage. */
  --archi-trait: clamp(var(--sp-4), 1.6vw, var(--sp-8));
  --archi-trait-marge: clamp(var(--sp-3), 1.2vw, var(--sp-5));
  /* Le rembourrage vertical d'UNE RANGÉE, partagé par la ligne de la fiche et la
     rubrique posée en face d'elle. C'est lui, et non une cote d'alignement, qui fait
     tomber les deux textes à la même hauteur : les deux cases commencent au haut de la
     rangée, donc un même rembourrage les décale d'autant. Recopié dans les deux
     règles, il aurait dérivé au premier réglage. */
  --archi-rang-pad: clamp(var(--sp-3), 1.2vw, var(--sp-4));
  /* Le repère du décor, qui est hors flux. */
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  /* Les rangées de la fiche : son en-tête, puis une par ligne. Le compte vient du
     gabarit (`--archi-rangs`), pas de cette feuille — voir la note en tête. Le
     repli à 6 est l'état actuel du dictionnaire, pour qu'un diagramme dont le gabarit
     ne poserait pas la variable reste lisible. */
  grid-template-rows: repeat(var(--archi-rangs, 6), auto);
  column-gap: calc(var(--archi-trait) + var(--archi-trait-marge) + clamp(var(--sp-4), 1.6vw, var(--sp-8)));
  /* À zéro, et c'est le `subgrid` qui l'exige : cette gouttière est aussi celle qui
     sépare les lignes de la fiche, qui se touchent. Voir la note en tête. */
  row-gap: 0;
  /* En haut de leur rangée et non centrées dedans : c'est le HAUT d'une ligne de fiche
     qu'une rubrique doit retrouver, et une rangée plus haute que la ligne ne doit pas
     faire glisser la rubrique vers son milieu. */
  align-items: start;
}

/* ---------- Le décor ----------
   Une forme de marque remplie de trame, posée derrière la fiche et qui affleure dans
   la marge de gauche. Demandée par Hugo le 25/08/2026.

   ⚠️ LA TRAME EST UNIFORME ICI, comme dans `coeur` : la règle de _components.scss
   compose DEUX couches de masque, les points et une rampe de densité qui éteint la
   forme sur ses deux bords. Cette rampe n'a pas de sens quand la moitié visible de
   la forme est justement un de ses bords — elle n'y laisserait qu'un voile. On ne
   garde donc que les points, et le contour est celui du masque de forme.

   ⚠️ `mask-composite: add` ET NON `intersect`. `intersect` vient de la règle à deux
   couches ; avec une seule il ne reste rien à intersecter et le masque s'annule —
   la forme disparaît entièrement. Ce n'est pas cosmétique. Même remède que
   `.coeur .forme-trame__grain`, dont le commentaire dit la même chose.

   ⚠️ SA PLACE : ANCRÉE SUR LE FLANC GAUCHE DE LA FICHE, à mi-hauteur, et dont la
   moitié seulement sort dans la marge. Elle ne descend PAS sous la fiche et ne monte
   PAS au-dessus, et c'est délibéré : au-dessus il y a l'annonce, en dessous la
   légende, et des points derrière un texte de 14px en encre éteinte lui coûteraient
   sa lisibilité. Tout ce qui déborde à gauche est rogné par la section (voir son
   `overflow-x: clip`).

   ⚠️ ELLE DISPARAÎT SOUS LE POINT DE RUPTURE, avec la composition : en une colonne
   la fiche prend toute la largeur, il n'y a plus de flanc où la loger et la forme
   passerait devant le texte. */
.archi__forme {
  --shape-w: clamp(12rem, 24vw, 21rem);
  --shape-rot: -10deg;
  position: absolute;
  /* Un peu plus de la moitié sort à gauche ; le reste passe derrière la fiche.
     Exprimé depuis la largeur de la forme et non en % de la grille : c'est la part
     CACHÉE qui doit rester constante, pas une distance au bord. */
  inset-inline-start: calc(var(--shape-w) * -0.58);
  inset-block-start: 50%;
  translate: 0 -50%;
  pointer-events: none;
}

.archi__forme .forme-trame__grain {
  /* 504px : la taille du pavé, fixée par `cote` dans le générateur (voir
     buildHalftoneFond dans eleventy.config.js). En dur ici comme dans
     _components.scss et _coeur.scss — c'est la seule valeur qui doive s'accorder
     entre les trois, et elle ne bouge jamais. */
  mask-image: url("/assets/img/halftone-fond.svg");
  mask-size: 504px 504px;
  mask-repeat: repeat;
  mask-composite: add;
}

/* La figure n'est plus un boîtier : la fiche et sa légende sont placées une par une
   dans la grille. Voir la note en tête du diagramme. */
.archi__figure {
  display: contents;
}

/* La rangée 1 est celle de l'EN-TÊTE de la fiche : les rubriques commencent donc à la
   rangée 2, en face de la première ligne. La dernière ligne de la fiche (« Tests ») n'a
   pas de rubrique en regard, et c'est juste — elle n'est pas une des quatre.

   C'est ici, et nulle part ailleurs, que le compte de quatre est publié. */
.archi__legende--1 {
  grid-column: 2;
  grid-row: 2;
}

.archi__legende--2 {
  grid-column: 2;
  grid-row: 3;
}

.archi__legende--3 {
  grid-column: 2;
  grid-row: 4;
}

.archi__legende--4 {
  grid-column: 2;
  grid-row: 5;
}

/* ---------- Les rubriques ----------
   Le titre, puis le texte. Rien autour : elles ont porté le `.cadre` du site — fond
   blanc, filet bleu, coins vifs — jusqu'au 25/08/2026, et la maquette les compose en
   TEXTE NU. Un panneau fermé autour de chacune ferait quatre cellules là où la
   maquette pose une liste, ce qui est exactement la limite que décrit la règle 13 de
   MAQUETTE.md.

   ⚠️ LE REMBOURRAGE VERTICAL EST CELUI D'UNE LIGNE DE FICHE, et il fait deux choses
   à la fois : il décale le titre du haut de la rangée exactement comme le libellé en
   face de lui, ce qui les aligne ; et il écarte deux rubriques consécutives, puisque
   la gouttière de rangée est à zéro. Voir la note en tête.

   ⚠️ PLUS DE `position: relative` ICI : le repère du trait de rattachement est passé
   sur le TITRE, sans quoi le rembourrage ci-dessus se glisserait entre le haut du
   repère et le haut du titre — et le trait ne tomberait plus sur sa première ligne. */
.archi__legende {
  padding-block: var(--archi-rang-pad);
}

/* Le trait de rattachement, posé dans la gouttière à gauche de la rubrique.

   ⚠️ IL EST PORTÉ PAR LE TITRE ET NON PAR LA RUBRIQUE, et c'est ce qui le cale sans
   aucune cote écrite à la main : `0.62em` se mesure alors sur le corps du TITRE, donc
   le trait tombe à mi-hauteur de sa première ligne à toutes les largeurs — le titre
   est en `clamp()`. Porté par la rubrique, l'em aurait été celui du corps de texte et
   le trait aurait glissé à chaque palier.

   Son repère est LE TITRE LUI-MÊME depuis le 26/08/2026, et non plus `.archi__legende`
   qui portait `position: relative` : la rubrique a maintenant un rembourrage vertical,
   qui se serait ajouté à `0.62em` et aurait fait glisser le trait sous la première
   ligne. Le titre est un bloc large de tout le contenu de la rubrique, donc
   `inset-inline-end: 100%` le sort au même endroit qu'avant — par la gauche du texte.
   Le `position: relative` est dans la règle du titre, plus bas. */
.archi__legende-titre::before {
  content: "";
  position: absolute;
  inset-inline-end: 100%;
  inset-block-start: 0.62em;
  inline-size: var(--archi-trait);
  margin-inline-end: var(--archi-trait-marge);
  border-block-start: 1px solid var(--c-pilier-dictionnaire);
}

/* ⚠️ AUCUN FILET SOUS LE TITRE, retiré le 18/08/2026 à la demande d'Hugo. La maquette
   en pose un (Figma 3:140), et il a été rendu pendant deux itérations — d'abord
   arrêté au rembourrage, puis étendu d'un bord à l'autre par des marges négatives.
   Les deux sont abandonnés : c'est l'espace qui sépare le titre du texte.

   C'est aussi ce qui a rendu inutile la variable `--archi-legende-pad` : elle
   n'existait que pour que la marge négative du filet vaille exactement le
   rembourrage de la boîte. */
.archi__legende-titre {
  /* Le repère du trait de rattachement — voir son pseudo-élément juste au-dessus. */
  position: relative;
  /* ⚠️ --sp-2 ET NON --sp-3, demandé par Hugo le 25/08/2026 : « il doit y avoir un
     peu moins de marge avec le titre ». Le texte est passé au corps du site le même
     jour, donc il pèse davantage — l'écart calibré pour un texte de 14px paraissait
     un blanc à 16-18. */
  margin: 0 0 var(--sp-2);
  font-family: var(--font-serif);
  font-size: clamp(1.0625rem, 0.9rem + 0.4vw, 1.375rem);
  font-weight: var(--fw-semibold);
  line-height: var(--lh-snug);
  color: var(--c-blue-zodiac);
}

/* ⚠️ --fs-base ET NON --fs-xs, demandé par Hugo le 25/08/2026 : « doivent être en
   taille de corps normale ». Les rubriques ne sont plus des légendes de cadre depuis
   qu'elles ont perdu leur panneau : ce sont quatre blocs de texte courant, et le
   corps de texte du site est --fs-base partout ailleurs. --fs-xs reste la cote de la
   légende de la fiche, plus bas, qui elle en est bien une.

   La fiche les a rejointes le 26/08/2026 — voir la note sur son échelle, plus bas. */
.archi__legende-texte {
  margin: 0;
  font-size: var(--fs-base);
  line-height: var(--lh-normal);
  color: var(--text-strong);
}

/* ---------- La fiche ----------
   Le panneau blanc du centre. Coins arrondis et ombre portée, comme la carte
   d'étiquettes de l'accueil : elle doit se détacher du fond sans filet épais.

   ---------------------------------------------------------------------
   ⚠️ TOUTE LA CARTE EST MONTÉE D'UN CRAN LE 26/08/2026, et c'est un mouvement d'ensemble
   plutôt que quatre réglages. Demande d'Hugo : « tu peux augmenter un peu la police des
   titres et des contenus de la carte pour que ça fasse un peu moins vide ? » — le
   passage en sous-grille la veille avait étiré ses lignes sur la hauteur des rubriques
   en face, et un texte de 11 et 14px dans des lignes plus hautes creusait du blanc.

   Chaque cote prend le BARREAU SUIVANT de l'échelle du site, aucune n'est inventée :

     référence, libellés et pastilles   --fs-mono  ->  --fs-xs    (11  ->  14px)
     valeurs                             --fs-xs    ->  --fs-base  (14  ->  16-18px)

   Le NOM de la fiche ne bouge pas : c'est un titre, il est déjà le plus gros texte de
   la carte, et son `clamp()` est le même que celui de `.archi__legende-titre` — les
   deux grades de titre du diagramme. Le monter ici les ferait diverger.

   Deux conséquences à connaître. La valeur est désormais À LA MÊME COTE QUE LE TEXTE
   DES RUBRIQUES (--fs-base), ce qui va dans le sens de la mise en regard demandée la
   veille : la ligne et la rubrique qui la nomme se lisent au même corps. Et la piste
   du libellé a dû s'ouvrir avec lui — voir la note sur `.archi__fiche-ligne`. */
/* La fiche couvre les quatre rangées des rubriques — et seulement elles : sa légende
   est en rangée 5, hors de la zone sur laquelle les rubriques se répartissent. */
.archi__fiche {
  grid-column: 1;
  /* Toutes les rangées explicites, et elle les SUBDIVISE : son en-tête en prend une,
     sa liste de lignes prend les autres. C'est ce partage qui met chaque rubrique en
     face de sa ligne — voir la note en tête. `-1` suit le compte que le gabarit pose,
     donc rien à recompter ici si une ligne s'ajoute. */
  grid-row: 1/-1;
  display: grid;
  grid-template-rows: subgrid;
  position: relative;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-cadre);
  background-color: var(--c-white);
  box-shadow: 0 4px 40px 4px rgb(var(--shadow-tint)/0.28);
  /* ⚠️ PAS D'`overflow: hidden`, retiré le 26/08/2026 avec le passage en sous-grille —
     voir la note en tête. Il ne rognait rien : aucun enfant de la carte ne porte de
     fond qui atteigne les coins arrondis, et ses filets sont à mi-carte. */
}

.archi__fiche-entete {
  grid-row: 1;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--sp-4);
  padding: clamp(var(--sp-4), 1.6vw, var(--sp-5));
  border-block-end: 1px solid var(--border-subtle);
}

.archi__fiche-reference {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--text-muted);
}

.archi__fiche-nom {
  margin: var(--sp-1) 0 0;
  font-family: var(--font-serif);
  font-size: clamp(1.0625rem, 0.9rem + 0.4vw, 1.375rem);
  font-weight: var(--fw-semibold);
  line-height: var(--lh-snug);
  color: var(--c-blue-zodiac);
}

/* La pastille « Certifié », et celle de la ligne « Tests ». Même forme, donc même
   règle : le fond est un voile du vert du pilier, l'encre est ce vert à pleine
   force. Sur du blanc, le vert donne 2,63 — c'est pourquoi il est ASSOMBRI vers le
   bleu nuit, exactement comme `--panneau-ton` de _texte-panneau.scss, ce qui le
   porte à 4,87.

   ⚠️ LE SÉLECTEUR DE LA PASTILLE EST COMPOSÉ, ET C'EST LA CORRECTION D'UN DÉFAUT
   SILENCIEUX trouvé le 26/08/2026. À spécificité égale c'est l'ordre de la feuille qui
   tranche — et `.archi__fiche-valeur`, écrite plus bas, reprenait à la pastille TOUT
   ce que les deux règles ont en commun : sa taille et, plus grave, son encre. La
   pastille « Tests » s'affichait donc en encre forte sur son voile vert, au lieu du
   vert assombri du badge du haut dont elle est censuément la jumelle.

   La composition la fait gagner quelle que soit sa place dans la feuille : la règle
   reste donc ici, avec le badge dont elle partage la recette, et non près de la ligne
   dont elle est une valeur. Recopier la formule d'encre plus bas aurait laissé deux
   valeurs à tenir d'accord. */
.archi__fiche-badge,
.archi__fiche-valeur.archi__fiche-valeur--ok {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-1) var(--sp-3);
  border-radius: var(--radius-full);
  background-color: color-mix(in srgb, var(--c-pilier-dictionnaire) 12%, var(--c-white));
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: color-mix(in srgb, var(--c-pilier-dictionnaire) 65%, var(--c-blue-zodiac));
  white-space: nowrap;
}

.archi__fiche-badge {
  margin: 0;
  flex: none;
}

.archi__puce {
  flex: none;
  inline-size: 0.3125rem;
  block-size: 0.3125rem;
  border-radius: var(--radius-full);
  background-color: var(--c-pilier-dictionnaire);
}

/* ---------- Les lignes de la fiche ----------
   Une liste de DÉFINITIONS et non une table : chaque ligne est un terme et sa
   description, ce qui est exactement ce que `<dl>` décrit. Le balisage dit donc le
   rapport que la mise en page montre.

   `<dt>`/`<dd>` sont enveloppés d'un `<div>` par ligne — c'est autorisé dans une
   `<dl>` depuis HTML 5.2, et c'est ce qui permet de faire de chaque couple une
   rangée de grille sans perdre la sémantique. */
/* Elle prend toutes les rangées sauf celle de l'en-tête, et les subdivise à son tour :
   une ligne par rangée, dans l'ordre du document. Les sous-grilles s'imbriquent, donc
   les frontières qui arrivent ici sont bien celles du diagramme. */
.archi__fiche-lignes {
  grid-row: 2/-1;
  display: grid;
  grid-template-rows: subgrid;
  margin: 0;
}

/* ⚠️ LA PISTE DU LIBELLÉ EST PASSÉE DE 10 À 12rem AVEC LA TAILLE DU LIBELLÉ, et les
   deux ne peuvent pas bouger séparément : en chasse fixe capitalisée à 0,14em de
   traînage, « DÉFINITION MÉTIER » — le plus long des cinq — mesure environ 176px à
   14px, contre 138px à 11px. Elle débordait donc de la piste de 160px et repassait à
   la ligne. 12rem (192px) laisse la marge d'un caractère.

   Un libellé plus long que « Définition métier » demandera donc de rouvrir cette
   piste. `max-content` ne peut PAS s'en charger : chaque ligne est sa propre grille,
   donc la piste se calerait ligne par ligne et les libellés ne s'aligneraient plus
   d'une ligne à l'autre. C'est aussi pour ça que la valeur est écrite en clair. */
.archi__fiche-ligne {
  display: grid;
  grid-template-columns: minmax(0, 12rem) minmax(0, 1fr);
  gap: var(--sp-4);
  /* Le vertical est `--archi-rang-pad`, partagé avec la rubrique en face — c'est lui
     qui aligne les deux textes. Voir sa déclaration sur `.archi`. */
  padding: var(--archi-rang-pad) clamp(var(--sp-4), 1.6vw, var(--sp-5));
}

.archi__fiche-ligne + .archi__fiche-ligne {
  border-block-start: 1px solid var(--border-subtle);
}

.archi__fiche-label {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--text-muted);
}

.archi__fiche-valeur {
  margin: 0;
  min-width: 0;
  font-size: var(--fs-base);
  line-height: var(--lh-normal);
  color: var(--text-strong);
  /* La règle de calcul est une FORMULE : elle doit rester en chasse fixe pour que
     les `@` et les `+` s'alignent. La maquette la compose ainsi, et c'est aussi ce
     qui la distingue d'une phrase. */
  overflow-wrap: anywhere;
}

/* ---------- La légende de la fiche ----------
   La phrase posée sous la fiche, demandée par Hugo le 25/08/2026 : « est la légende
   de l'image et doit être écrit en plus petit en dessous ». Elle était jusque-là un
   paragraphe de `chute`, donc pleine largeur et au corps du site, sous la
   composition entière.

   --fs-xs et --text-muted : c'est ce que --fs-xs EST sur ce site (« 14px —
   légendes », voir _tokens.scss), et une légende n'est pas du corps de texte — la
   règle de l'encre forte vaut pour le corps, pas pour la mention qui accompagne une
   figure. Même partage que `.archi__fiche-label` juste au-dessus. */
.archi__mention {
  /* ⚠️ ELLE NE NOMME PAS SA RANGÉE : la colonne 1 est occupée de bout en bout par la
     fiche, donc le placement automatique la pose dans la rangée IMPLICITE qui suit —
     quel que soit le nombre de lignes de la fiche, et sans qu'une rangée vide soit
     jamais payée par un diagramme sans légende.

     Sa marge est à elle depuis que la gouttière de rangée est à zéro (voir la note en
     tête) : sans elle, la phrase collerait au bord de la carte. */
  grid-column: 1;
  margin: clamp(var(--sp-4), 1.6vw, var(--sp-5)) 0 0;
  font-size: var(--fs-xs);
  line-height: var(--lh-normal);
  color: var(--text-muted);
}

/* ---------- Écroulement ----------
   ⚠️ LES TRAITS DISPARAISSENT ICI, ET ILS DOIVENT. En une seule colonne les
   rubriques ne sont plus en regard de la fiche mais sous elle : un trait horizontal
   ne rattacherait plus rien et pendrait dans la marge de gauche.

   L'ordre de la pile est celui du document — la fiche, puis ses quatre rubriques.
   C'est l'ordre de lecture, et il ne coûte aucune `order` : le gabarit écrit déjà la
   fiche en premier, précisément pour ça. */
@media (max-width: 60rem) {
  .archi {
    grid-template-columns: minmax(0, 1fr);
    /* ⚠️ LA SOUS-GRILLE EST DÉFAITE ICI, DONC SES DEUX CONSÉQUENCES AUSSI. Les rangées
       explicites n'ont plus rien à cadencer : en une colonne la fiche n'est plus en
       regard de personne, et la laisser subdiviser six rangées qui n'existent plus
       tasserait ses lignes dans la première. Les rangées redeviennent automatiques,
       et la gouttière — qui était à zéro pour que les lignes de la fiche se touchent —
       reprend le rythme d'origine entre les blocs empilés. */
    grid-template-rows: none;
    row-gap: clamp(var(--sp-5), 2vw, var(--sp-8));
  }
  /* ⚠️ TOUT REPASSE EN PLACEMENT AUTOMATIQUE, et c'est l'ordre du DOM qui décide :
     la fiche, sa légende, puis les quatre rubriques. Soit l'ordre de lecture, sans
     qu'aucune `order` ni aucune aire ne soit écrite — c'est pour ça que le gabarit
     écrit la fiche en premier. */
  .archi__fiche,
  .archi__mention,
  .archi__legende {
    grid-column: 1;
    grid-row: auto;
  }
  /* La carte redevient une boîte ordinaire : son en-tête et sa liste s'empilent, et la
     liste retrouve ses propres rangées automatiques — une par ligne, comme avant. */
  .archi__fiche {
    display: block;
  }
  .archi__fiche-lignes {
    display: block;
  }
  /* C'est la gouttière rendue ci-dessus qui écarte les rubriques les unes des autres ;
     leur rembourrage n'avait de sens qu'en regard des lignes de la fiche. La légende
     perd sa marge propre pour la même raison. */
  .archi__legende {
    padding-block: 0;
  }
  .archi__mention {
    margin-block-start: 0;
  }
  .archi__legende-titre::before {
    display: none;
  }
  /* Voir la note du décor : en une colonne il n'y a plus de flanc pour l'accueillir. */
  .archi__forme {
    display: none;
  }
}
/* La fiche passe en une colonne elle aussi : à cette largeur, un libellé de 10rem
   en face d'une valeur ne laisse plus assez de place à la valeur. */
@media (max-width: 48rem) {
  .archi__fiche-ligne {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--sp-1);
  }
}
/* =========================================================================
   L'ORBITE — la forme du dictionnaire au centre, les trois briques de
   l'infrastructure qui tournent autour. Demandée par Hugo le 18/08/2026.

   ---------------------------------------------------------------------
   COMMENT LA ROTATION EST FAITE, ET POURQUOI EN DEUX COUCHES.

   Chaque satellite est un calque qui occupe TOUT l'anneau et tourne sur lui-même ;
   sa lune est posée en haut de ce calque, donc la rotation la promène sur le cercle.
   La lune tourne alors en SENS INVERSE, à la même vitesse : elle reste droite au lieu
   de basculer la tête en bas à mi-course. Une seule couche ne pouvait pas faire les
   deux.

   ⚠️ LES TROIS SATELLITES PARTAGENT UNE SEULE ANIMATION ET SE DÉCALENT PAR LE DÉLAI,
   à un tiers de tour d'écart. C'est ce qui évite d'écrire un angle de départ dans les
   keyframes : une propriété personnalisée y serait figée à sa valeur de compilation
   dans certains navigateurs, et les trois se seraient superposées.

   ⚠️ ROTATE / TRANSLATE / SCALE SÉPARÉS, jamais un `transform` unique. Les trois
   propriétés individuelles se composent ; réunies dans un `transform`, la rotation
   animée écraserait le centrage de la lune et son échelle à la première image.
   ========================================================================= */
.orbite {
  --orbite-duree: 32s;
  position: relative;
  inline-size: min(100%, 26rem);
  aspect-ratio: 1;
  margin-inline: auto;
}

/* L'orbite matérialisée. `inset` définit le rayon : c'est la même valeur que celle
   des satellites, et les deux doivent bouger ENSEMBLE — sinon les lunes ne suivent
   plus le pointillé. */
.orbite__anneau,
.orbite__satellite {
  position: absolute;
  inset: 14%;
}

/* ---------- Le second anneau ----------
   Ajouté le 28/08/2026 pour la section « Vos données fiables » de /integrations/
   (Figma 203:18003). Les deux rayons sont ceux de la maquette : 469,56 et 293,48
   de diamètre pour un cadre de 532 une fois la demi-tuile débordante comptée.

   ⚠️ LES DEUX `inset` DOIVENT RESTER ACCORDÉS À LEURS SATELLITES, exactement
   comme l'anneau simple plus haut : c'est l'inset qui définit le rayon, et les
   lunes ne suivent le pointillé que si les deux valeurs bougent ensemble.

   Le rapport 55,2 / 88 = 0,627 reproduit celui de la maquette (0,625). */
/* ⚠️ LES DEUX ANNEAUX SONT BLANCS SUR LA BOÎTE OCRE, et ce n'est pas le
   comportement par défaut. L'anneau lit `--text-accent`, qui vaut le FLUO sur
   cette boîte-là — la section s'en sert justement pour ses mises en valeur
   `((…))`. Rendu tel quel, le pointillé sortait en fluo.

   Le blanc est le choix de Céline du 28/08/2026, et il se tient : les neuf tuiles
   sont blanches, et un pointillé fluo aurait introduit une troisième couleur dans
   une figure qui n'en porte que deux. Le blanc donne 5,66 sur cet ocre, le fluo
   4,85 — les deux passent, c'est la cohérence qui tranche.

   ⚠️ ET C'EST BORNÉ À LA BOÎTE INTÉGRATIONS, pas posé sur `.orbite--double` :
   une orbite double sur un autre fond doit continuer de suivre l'accent de sa
   page, comme l'anneau simple le fait depuis le 26/08/2026.

   ⚠️ LE FILET DU NOYAU SUIT, pour la même raison. Il est fluo par défaut — c'est
   ce qui donne son bord au rond violet du connecteur IA — et il aurait été le
   seul trait fluo d'une figure devenue blanche. Le disque en a besoin : l'ocre
   clair sur l'ocre sombre ne donne que 2,67, visible mais mou. C'est la réponse
   déjà en place pour les cartes blanches de /cas-clients/ — un aplat qui ne se
   détache pas franchement gagne un bord, il ne change pas de couleur. */
.texte-module--integrations .orbite__anneau,
.texte-module--integrations .orbite__noyau {
  border-color: var(--c-white);
}

.orbite--double .orbite__anneau--ext,
.orbite--double .orbite__satellite--ext {
  inset: 6%;
}

.orbite--double .orbite__anneau--int,
.orbite--double .orbite__satellite--int {
  inset: 22.4%;
}

/* ⚠️ L'ANNEAU LIT LE RÔLE D'ACCENT, PAS UNE TEINTE DE PILIER ÉCRITE EN CLAIR. Il a
   porté `--c-pilier-dictionnaire` jusqu'au 26/08/2026, du temps où la figure ne servait
   qu'à cette page : la section 01 de /connecteur-ia/ l'aurait rendu VERT sur son violet.

   Le rendu au dictionnaire ne change pas d'un pixel — `.ton-page--dictionnaire` affecte
   justement `--text-accent` à `--c-pilier-dictionnaire`, et la section « cœur » est sur
   le crème. Sur la boîte violette du connecteur, ce même rôle est passé au BLANC (le
   fluo n'y donne que 4,05), et l'anneau suit sans que la feuille ait à nommer la page.

   Le violet du pilier, lui, y aurait donné 1,14 : invisible comme couleur, parfaitement
   visible comme bord. C'est le pire des deux. */
.orbite__anneau {
  border: 1px dashed var(--text-accent);
  border-radius: var(--radius-full);
}

.orbite__centre {
  position: absolute;
  top: 50%;
  left: 50%;
  translate: -50% -50%;
  block-size: 34%;
  inline-size: auto;
}

/* ---------- Le noyau : un rond à la teinte d'accent, le logo en masque dedans ----------
   La seconde forme du centre, demandée par Hugo le 26/08/2026 pour le logo MCP de
   /connecteur-ia/. Mêmes cotes que `.orbite__centre` ci-dessus — c'est le même
   emplacement, seul son contenu change.

   ⚠️ LE ROND PREND `--c-blue` ET NON UNE COULEUR ÉCRITE EN CLAIR. `.ton-page--*`
   réaffecte ce jeton au ton du pilier, donc le rond est violet sur cette page-ci et
   suivrait sa page ailleurs. C'est le rôle, pas le ton.

   ⚠️ IL EST CERCLÉ D'UN FILET FLUO, ET CE FILET FAIT TOUT LE TRAVAIL. Le rond tombe sur
   la boîte violette de la section, dont le fond est ce même violet assombri de 10 % : le
   violet plein n'y donne que 1,14 de contraste, soit un aplat invisible comme couleur et
   parfaitement visible comme bord — le pire des deux. Le filet lui rend un bord franc
   (le fluo donne 4,05 sur le fond de la boîte, 3,55 sur le violet du rond), et il
   désigne le centre de la figure là où les trois lunes sont blanches. Tranché par Hugo
   le 26/08/2026, contre un rond blanc à logo violet : à quatre objets blancs, le centre
   ne se serait plus distingué que par sa forme.

   C'est la réponse déjà en place pour les cartes blanches à 1,05 sur le bleu de
   /cas-clients/ — un aplat qui ne se détache pas de son fond gagne un filet, il ne change
   pas de couleur. */
.orbite__noyau {
  position: absolute;
  top: 50%;
  left: 50%;
  translate: -50% -50%;
  display: grid;
  place-items: center;
  inline-size: 34%;
  aspect-ratio: 1;
  background-color: var(--c-blue);
  border: 1px solid var(--c-fluo);
  border-radius: var(--radius-full);
}

/* ⚠️ EN MASQUE ET NON EN `<img>`, exactement comme `.chaine__logo` — l'export du
   protocole MCP est un tracé NOIR, qui posé en image resterait noir sur le violet, à
   3,01 de contraste. En masque, sa teinte vient des jetons du site.

   Le blanc donne 4,14 sur ce violet. C'est sous le seuil de 4,5 du texte courant, et
   c'est accepté ici pour la même raison que dans `chaine` : le logo est DÉCORATIF (le
   rond est `aria-hidden`, « MCP » étant écrit dans le paragraphe d'à côté) et aucune
   information ne repose sur lui seul.

   `--logo` est posée en ligne par le gabarit : le nom du fichier vient des données. */
.orbite__noyau-logo {
  inline-size: 52%;
  aspect-ratio: 1;
  background-color: var(--c-white);
  mask-image: var(--logo);
  mask-size: contain;
  mask-repeat: no-repeat;
  mask-position: center;
  -webkit-mask-image: var(--logo);
  -webkit-mask-size: contain;
  -webkit-mask-repeat: no-repeat;
  -webkit-mask-position: center;
}

/* ⚠️ NOTRE PROPRE MARQUE, EN TEXTE ET NON EN MASQUE, depuis le 16/09/2026 — voir
   blocs/texte-module.njk, `centreBiron`. Le même lockup que `.site-logo`, sans
   `--logo` ni `aspect-ratio` à tenir : la police se dimensionne elle-même. */
.orbite__noyau-logo--texte {
  inline-size: auto;
  aspect-ratio: auto;
  background-color: transparent;
  mask-image: none;
  -webkit-mask-image: none;
  font-family: var(--font-serif);
  font-size: 0.85rem;
  font-weight: var(--fw-bold);
  letter-spacing: var(--ls-tight);
  line-height: 1;
  color: var(--c-white);
}

/* ⚠️ LA PHASE VIENT DU RANG ET DE L'EFFECTIF, posés en ligne par le gabarit
   (`--i` et `--n`). Trois règles écrites à la main les remplaçaient jusqu'au
   28/08/2026 — une par satellite — et un quatrième satellite se serait superposé
   au premier sans que rien ne le signale.

   À trois satellites le calcul rend exactement les anciennes valeurs (0, −d/3,
   −2d/3) : /connecteur-ia/ ne bouge pas d'un pixel.

   Les délais sont NÉGATIFS : l'animation démarre déjà avancée, au lieu
   d'attendre avant de partir. */
.orbite__satellite {
  --orbite-phase: calc(var(--orbite-duree) * var(--i, 0) / var(--n, 1) * -1);
  animation: orbite-tour var(--orbite-duree) linear infinite;
  animation-delay: var(--orbite-phase);
}

/* ---------- Deux vitesses, deux sens ----------
   L'anneau externe est plus lent que l'interne, comme une orbite réelle, et il
   tourne dans l'autre sens — choix de Céline le 28/08/2026. La maquette est
   figée et ne dit rien du mouvement ; c'est le seul endroit de la figure qui ne
   soit pas relevé sur elle.

   ⚠️ LA LUNE HÉRITE DE `--orbite-duree` ET DE `--orbite-phase` de son satellite,
   donc les deux ne peuvent pas se désaccorder — c'est pour ça que la durée est
   posée sur le SATELLITE et non sur les deux séparément. */
.orbite--double .orbite__satellite--int {
  --orbite-duree: 60s;
}

.orbite--double .orbite__satellite--ext {
  --orbite-duree: 96s;
}

/* Le satellite interne tourne à l'envers ; sa lune revient donc à l'endroit. */
.orbite--double .orbite__satellite--int {
  animation-direction: reverse;
}

.orbite--double .orbite__satellite--int .orbite__lune {
  animation-direction: normal;
}

/* La lune, posée en haut de l'anneau et centrée sur le pointillé. Elle contre-tourne
   pour rester droite, avec le MÊME délai que son satellite — héritée par
   `var(--orbite-phase)`, donc les deux ne peuvent pas se désaccorder. */
.orbite__lune {
  position: absolute;
  top: 0;
  left: 50%;
  translate: -50% -50%;
  block-size: 26%;
  inline-size: auto;
  /* `--orbite-echelle` rattrape une inégalité des EXPORTS et non un choix de mise
     en page : un fichier dont la silhouette n'occupe pas toute sa zone de dessin
     rend plus petit que ses voisins à hauteur de boîte égale. Le facteur vient des
     données (clé `echelle` d'un satellite).

     ⚠️ PLUS AUCUN USAGE NE L'EMPLOIE depuis le 25/08/2026. Le seul était le
     connecteur IA, à 81 % de remplissage ; son viewBox a été recadré sur son
     dessin, ce qui a supprimé la cause. Le bouton reste, avec 1 pour défaut — mais
     avant d'y remettre un facteur, vérifier que le défaut n'est pas dans le
     fichier, où il se corrige une fois pour toutes les pages. */
  scale: var(--orbite-echelle, 1);
  animation: orbite-tour var(--orbite-duree) linear infinite reverse;
  animation-delay: var(--orbite-phase, 0s);
}

.orbite--double {
  container-type: inline-size;
}

/* ⚠️ LE NOYAU EST PLUS PETIT SUR UNE ORBITE DOUBLE — 24,5 % contre 34 %, la cote
   du connecteur IA juste au-dessus. Ce n'est pas un goût : à 34 % le disque
   n'aurait laissé que 13 px entre son bord et les lunes de l'anneau interne, qui
   n'existe pas sur l'orbite simple. C'est aussi la proportion de la maquette
   (130,43 sur 532) et celle qu'a validée la prévisualisation du 28/08/2026. */
.orbite--double .orbite__noyau {
  inline-size: 24.5%;
}

/* ⚠️ LA LUNE SE MESURE EN `cqi` SUR UNE ORBITE DOUBLE, ET C'EST STRUCTUREL. Sa
   cote par défaut est 30 % — un pourcentage du CALQUE du satellite, dont l'`inset`
   diffère d'un anneau à l'autre : 30 % de l'externe fait 92 px quand 30 % de
   l'interne en fait 58. Les neuf lunes rendaient donc en deux tailles, et les
   externes venaient à 7 px du disque central.

   L'unité de conteneur les ramène toutes à la mesure de la FIGURE, qui est la même
   pour les deux anneaux. L'orbite simple garde ses 30 %, où la question ne se pose
   pas — un seul calque, une seule taille. */
.orbite--double .orbite__lune--forme {
  inline-size: 13.5cqi;
}

/* ---------- La légende, SOUS la figure ----------
   « biron × Zapier / n8n ». La maquette l'écrit dans le disque ; elle en sort
   depuis le 28/08/2026, sur arbitrage de Céline entre quatre pistes montrées en
   prévisualisation.

   Le raisonnement est en tête du gabarit : la figure rend ~350 px dans la colonne
   là où la maquette en dessine 532, et la mention y occupait 89 % du diamètre sur
   deux lignes. Sous la figure elle dispose de toute sa largeur, et sa taille ne
   dépend plus de la place laissée par la colonne.

   ⚠️ LE LOGOTYPE EST EN MASQUE, PAS EN IMAGE. Le fichier livré est blanc ; ici il
   se pose sur l'ocre sombre de la boîte, où le blanc donne 5,66 — c'est donc bien
   du blanc qu'on veut, mais par le rôle plutôt que par le fichier, pour qu'il
   suive le jour où la figure servirait sur un fond clair. */
.orbite__legende {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.4em;
  margin-block: var(--sp-5) 0;
  font-family: var(--font-titre);
  font-weight: var(--fw-bold);
  font-size: var(--fs-base);
  color: var(--text-strong);
}

.orbite__legende-logo {
  inline-size: 4.25em;
  aspect-ratio: 64/15; /* le viewBox du logotype */
  background-color: var(--text-strong);
  mask-image: var(--logo);
  mask-size: contain;
  mask-repeat: no-repeat;
  mask-position: center;
  -webkit-mask-image: var(--logo);
  -webkit-mask-size: contain;
  -webkit-mask-repeat: no-repeat;
  -webkit-mask-position: center;
}

/* Notre marque, en texte — même lockup que `.site-logo`, voir
   `.orbite__noyau-logo--texte` juste au-dessus pour le pourquoi. */
.orbite__legende-logo--texte {
  inline-size: auto;
  aspect-ratio: auto;
  background-color: transparent;
  mask-image: none;
  -webkit-mask-image: none;
  font-family: var(--font-serif);
  font-weight: var(--fw-bold);
  letter-spacing: var(--ls-tight);
}

.orbite__legende-x {
  font-weight: var(--fw-regular);
  opacity: 0.7;
}

/* ---------- La lune en FORME DE MARQUE, un logo tiers posé dessus ----------
   La seconde forme d'une lune, demandée par Hugo le 26/08/2026 pour les trois
   assistants de /connecteur-ia/. Elle occupe la même place que la lune en `<img>`
   ci-dessus, et hérite d'elle son ancrage, son contre-tour et son délai.

   ⚠️ LES DEUX AXES SONT INVERSÉS PAR RAPPORT À LA LUNE EN `<img>`, et il le faut : une
   image a un ratio intrinsèque, donc une hauteur suffit à la dimensionner. Une forme de
   marque est un MASQUE, qui n'en a aucun — c'est `--shape-ratio` qui le lui donne, et il
   part de la LARGEUR. On fixe donc la largeur et on laisse la hauteur suivre le ratio de
   l'export, sans quoi la boîte serait plate et la forme écrasée.

   La grille à une seule cellule superpose la forme et le logo sans positionnement
   absolu : les deux occupent `1 / 1`. C'est la construction de `.chaine__forme` et de
   `.item__pastille`, et elle est OBLIGATOIRE ici — un masque découpe tout son
   sous-arbre, donc un logo écrit dans la forme serait découpé avec elle. */
.orbite__lune--forme {
  display: grid;
  place-items: center;
  inline-size: 30%;
  block-size: auto;
}

.orbite__forme,
.orbite__logo {
  grid-area: 1/1;
}

.orbite__forme {
  --shape-w: 100%;
}

/* ⚠️ EN `<img>` ET NON EN MASQUE, à l'inverse du logo MCP du noyau. Ces trois marques
   portent leur propre couleur — le terracotta de Claude, le noir de ChatGPT, les cinq
   jaunes-rouges de Mistral — et la forme sous elles est BLANCHE : il n'y a rien à
   recolorer, et un masque écraserait justement ces couleurs. C'est la règle de
   `.item__logo` : la couleur d'une marque tierce n'est pas à nous.

   ⚠️ 46 % ET NON UNE COTE PLUS GÉNÉREUSE. Les trois formes de marque ont des flancs
   incurvés et des pointes : un logo qui remplit sa largeur dépasse du dessin sur deux
   des trois. La cote est celle de `.chaine__logo`, calibrée sur la même question.

   ⚠️ UNE BOÎTE CARRÉE ET `object-fit`, PAS UNE HAUTEUR `auto`. Deux raisons, et la
   seconde suffirait :

     · les trois marques n'ont pas le même rapport (ChatGPT 320×320, Claude 148×148,
       Mistral 129×91). À largeur égale et hauteur libre, Mistral rendrait plus large
       que haute et les trois lunes ne se répondraient plus. La boîte commune les cadre
       toutes les trois, et `contain` fait que chacune y tient sans être déformée.

     · une hauteur `auto` sur une `<img>` se déduit du rapport INTRINSÈQUE du fichier,
       donc après son chargement : la cellule sauterait à l'arrivée de l'image. La boîte
       carrée est entièrement déterminée par la feuille, ce qui la réserve d'avance —
       le service que rend `lib/image.js` ailleurs sur le site, sans avoir à lire les
       fichiers ici. */
.orbite__logo {
  /* ⚠️ LE LOGO DOIT ÊTRE POSITIONNÉ, SINON LA FORME LUI PASSE DESSUS. Défaut vu au
     rendu le 26/08/2026 — « il manque les logo des IA » : les trois logos étaient bien
     dans le DOM, chargés, à la bonne taille, `opacity: 1`, et invisibles.

     C'est EXACTEMENT le défaut de `.item__pastille-numero`, dont l'avertissement dans
     _components.scss explique le mécanisme au long : `.forme` porte
     `transform: rotate(var(--shape-rot, 0deg))`, et un `transform` autre que `none` —
     même une rotation de zéro degré — en fait un contexte d'empilement peint à l'étape
     8, après les blocs ordinaires du même parent. L'ordre du DOM n'y change rien.

     La leçon, puisque le piège a désormais mordu deux fois : dès qu'un frère est
     superposé à une forme de marque, il lui faut ces deux lignes. */
  position: relative;
  z-index: 1;
  /* ⚠️ LE 46 % CARRÉ RESTE LE DÉFAUT, et il vaut pour les trois marques d'IA du
     connecteur : elles sont de même famille, une boîte commune les cadre toutes.

     Les neuf logos de /integrations/ ne le sont pas — de Salesforce, un logotype
     qui porte son mot, à Brevo, une lettre étroite. Une boîte commune y laisserait
     le premier illisible, alors ils déclarent leur cote dans les données. C'est la
     règle que la première tentative avait enfreinte, et qui s'est vue tout de
     suite : « le logo hubspot est trop petit par rapport aux autres ». */
  inline-size: var(--logo-l, 46%);
  block-size: var(--logo-h, auto);
  aspect-ratio: var(--logo-ratio, 1);
  object-fit: contain;
}

/* Une cote déclarée fixe les DEUX axes : le rapport n'a plus à être imposé. */
.orbite__logo[style*="--logo-h"] {
  aspect-ratio: auto;
}

@keyframes orbite-tour {
  to {
    rotate: 360deg;
  }
}
/* ⚠️ `paused` ET NON `animation: none` EN MOUVEMENT RÉDUIT. Une animation en pause
   s'affiche à l'instant de son délai : les trois satellites restent donc répartis à un
   tiers de tour d'écart, et les lunes restent droites. Avec `none`, elles se seraient
   toutes empilées en haut du cercle — la composition n'aurait plus rien voulu dire. */
@media (prefers-reduced-motion: reduce) {
  .orbite__satellite,
  .orbite__lune {
    animation-play-state: paused;
  }
}
/* =========================================================================
   BLOC « etapes » — blocs/etapes.njk.
   Origine : « Une méthode en 8 étapes », Figma 185:7157.

   Une boîte bleu nuit posée sur le crème, épinglée le temps de la section. Les
   huit étapes occupent la même cellule et se succèdent sur place ; le sommaire
   de gauche les liste toutes et surligne celle qu'on lit.

   Les cotes de la boîte et de la scène sont celles de `.sequence__boite` et
   `.sequence__scene` — c'est le même dispositif, animé par le même script.

   ⚠️ AUCUN FILET DANS CE BLOC, et c'est une demande explicite (18/08/2026).
   Ni bande d'en-tête soulignée, ni séparateur entre les colonnes, ni trait
   entre les étapes. Le contraste de la boîte sur le crème suffit à la
   détacher, et les étapes étant superposées, un trait entre elles n'aurait de
   toute façon rien séparé.

   ⚠️ LE SENS DES FONDS EST INVERSÉ DEPUIS LE 18/08/2026 : c'était un panneau
   BLANC sur une section SOMBRE. Le repère de l'étape courante passe au fluo, et
   le fluo ne tient pas sur un fond clair — 1,17 de contraste sur du blanc, 1,10
   sur le crème, contre 11,54 sur ce bleu nuit. Ne pas rétablir le panneau blanc
   sans changer aussi la couleur du repère.
   ========================================================================= */
.etapes {
  position: relative;
  /* Un écran plein, et il le faut : la section est épinglée en HAUT de la
     fenêtre (`start: "top top"` dans sequence.js). Toute hauteur inférieure
     décollerait la boîte du centre de l'écran. Même construction que
     `.sequence`. */
  min-height: 100svh;
  display: grid;
  align-content: center;
  padding-block: var(--sp-12);
  padding-inline: var(--container-pad);
  background-color: var(--bg-page);
}

/* Coins arrondis et AUCUN contour, comme les autres boîtes sombres du site
   (.sequence__boite, .temoignages__boite, .texte-panneau__boite) : sur du bleu
   nuit posé sur crème, le contraste fait déjà l'arête. */
.etapes__boite {
  position: relative;
  width: 100%;
  max-width: 96rem;
  margin-inline: auto;
  padding-block: clamp(2rem, 1.2rem + 2.4vw, 3.75rem);
  padding-inline: clamp(1.5rem, -0.5rem + 8vw, 5.5rem);
  border-radius: 1.75rem;
  /* ⚠️ L'ACCENT PASSE PAR UNE VARIABLE, il ne s'écrit plus en dur. Le fluo était
     posé directement sur l'étape active du sommaire et sur le badge « Étape N ».
     Il tient 11,54 sur le bleu nuit, mais seulement 2,25 sur le vert du pilier —
     illisible. Chaque variante de fond règle donc `--etapes-accent`, et les deux
     règles qui l'emploient suivent sans être dupliquées. */
  --etapes-accent: var(--c-fluo);
}

.etapes__boite--sombre {
  background-color: var(--c-blue-dark);
}

/* ---------- La boîte verte sombre du dictionnaire ----------
   Demandée par Hugo le 18/08/2026 à la place du bleu nuit de la maquette : sur ce
   pilier, la boîte porte le vert.

   ⚠️ ELLE A CHANGÉ DE CAMP LE 25/08/2026, comme celle de `texte-module` et pour la
   même consigne. Elle a d'abord été un fond CLAIR — l'aplat #00b3a4, accent au noir
   du site, inactives du sommaire en noir dilué à 58 %. Hugo : « on peut les faire
   passer dans une teinte plus sombre pour en faire des blocs sur lesquels on écrit
   en blanc ? » La maquette dit sombre, donc c'est sombre : la boîte porte maintenant
   `.on-dark` (voir blocs/etapes.njk) et --c-pilier-dictionnaire-sombre.

   ⚠️ LA TEINTE EST LA SECONDE VALEUR DU PILIER, #00756c, pas son aplat. Le vert est
   déjà à saturation 100 %, donc on tient la teinte et on descend la clarté — pas de
   rotation de teinte à faire, contrairement à l'orange juste en dessous. Tout est
   sur le jeton dans _tokens.scss.

   Les contrastes sur #00756c, mesurés :

     blanc           5,58  ← le corps de texte et l'étape active
     fluo            4,79  ← l'accent
     casper #aab4d2  2,70     inutilisable, c'est le défaut d'`.on-dark`
     --c-text-main   2,55     inutilisable — l'ancienne encre de cette boîte

   ⚠️ L'ACCENT EST LE FLUO, comme sur la boîte bleu nuit et sur l'orange — et non
   plus le noir du site. Il ne pouvait pas l'être sur l'aplat clair (le fluo n'y
   donnait que 1,82) ; sur le fond sombre il redevient la valeur naturelle, et les
   trois boîtes de ce bloc parlent enfin la même langue.

   ⚠️ LES INACTIVES DU SOMMAIRE SONT UN BLANC DILUÉ DANS LE FOND, l'exacte inversion
   de ce qu'elles étaient. Le casper d'`.on-dark` tombe à 2,70 : c'est la règle 11 de
   MAQUETTE.md, et ici la demi-mesure marche — à 78 % de blanc on obtient 4,05, soit
   trait pour trait le niveau que l'ancienne dilution noire donnait (4,05 elle aussi)
   et celui de la boîte orange (4,08). Les trois pourcentages diffèrent (58 % de noir,
   78 % de blanc ici et sur l'orange) parce que les fonds diffèrent ; c'est le 4,05
   qui est la constante, pas le 78 %.

   ⚠️ NE PAS PASSER --text-muted AU BLANC PUR ICI, contrairement à la boîte de
   `texte-module` : ce rôle ne porte pas des légendes mais les entrées INACTIVES d'un
   sommaire, dont le métier est de reculer. Au blanc pur elles seraient à égalité
   avec l'active et seule la taille les distinguerait.

   ⚠️ LE TITRE ET LE TEXTE NE SONT PAS DANS CETTE BOÎTE en mode `liste`, et cette
   page est en mode `liste`. Ils restent sur le crème et gardent les rôles de la
   page — donc rien de ce qui est réglé ici ne les atteint. Voir la note de
   blocs/etapes.njk sur cette inversion. */
.etapes__boite--dictionnaire {
  background-color: var(--c-pilier-dictionnaire-sombre);
  /* Le fond de la boîte devient le fond de référence, pour que `.btn--secondary`
     recopie la bonne couleur plutôt que le crème de la page. */
  --section-bg: var(--c-pilier-dictionnaire-sombre);
  --bg-page: var(--c-pilier-dictionnaire-sombre);
  --etapes-accent: var(--c-fluo);
  --text-muted: color-mix(in srgb, var(--c-white) 78%, var(--c-pilier-dictionnaire-sombre));
  color: var(--text-strong);
}

/* ---------- La boîte orange sombre des intégrations ----------
   Demandée par Hugo le 20/08/2026 pour la section 03 de /excel-sheets/, à la place du
   bleu nuit de la maquette : sur ce pilier, la boîte porte l'orange.

   ⚠️ ELLE A CHANGÉ DE CAMP LE JOUR MÊME, comme celle de `texte-module`. Elle a
   d'abord été un fond CLAIR — l'aplat #f0a22e, encres noires, inactives du sommaire
   en noir dilué à 72 %. Hugo l'a corrigée dans la même séance : « je veux que les
   parties dites sombres soient des parties sombres avec du texte blanc, quitte à
   assombrir le orange. » La maquette dit sombre, donc c'est sombre : la boîte porte
   maintenant `.on-dark` (voir blocs/etapes.njk) et --c-pilier-integrations-sombre.

   ⚠️ LA TEINTE EST LA TROISIÈME VALEUR DU PILIER, #ab4a00, pas son aplat. Elle
   descend la clarté à saturation pleine et laisse la teinte tourner vers le rouge —
   surtout pas un mélange vers le bleu nuit, qui désature et donne du brun. Tout est
   sur le jeton dans _tokens.scss, et les candidats sur /labo/orange-sombre/.

   Les contrastes sur #ab4a00, mesurés :

     blanc           5,66  ← le corps de texte et l'étape active
     fluo            4,85  ← l'accent
     casper #aab4d2  2,74     inutilisable, c'est le défaut d'`.on-dark`
     --c-text-main   2,51     inutilisable — l'ancienne encre de cette boîte

   ⚠️ L'ACCENT EST LE FLUO, comme sur la boîte bleu nuit — et non plus le noir du
   site. Il ne pouvait pas l'être sur l'aplat clair (le fluo n'y donnait que 1,82) ;
   sur le fond sombre il redevient la valeur naturelle, et les deux boîtes de ce bloc
   parlent enfin la même langue.

   ⚠️ LES INACTIVES DU SOMMAIRE SONT UN BLANC DILUÉ DANS LE FOND, l'exacte inversion
   de ce qu'elles étaient. Le casper d'`.on-dark` tombe à 2,74 : c'est la règle 11 de
   MAQUETTE.md, et ici la demi-mesure marche — à 78 % de blanc on obtient 4,08, soit
   très exactement le niveau que l'ancienne dilution noire donnait (4,05). L'entrée
   inactive recule donc derrière l'active (blanc plein, taille pleine) autant
   qu'avant, dans l'autre sens.

   ⚠️ NE PAS PASSER --text-muted AU BLANC PUR ICI, contrairement aux boîtes violette
   et rose : ce rôle ne porte pas des légendes mais les entrées INACTIVES d'un
   sommaire, dont le métier est de reculer. Au blanc pur elles seraient à égalité
   avec l'active et seule la taille les distinguerait.

   ⚠️ LES CADRES EN ATTENTE N'ONT RIEN À REPRENDRE. `.apercu` remet lui-même ses deux
   rôles d'encre en contexte clair (voir _components.scss) : son dégradé blanc se
   détache mieux sur ce fond sombre qu'il ne le faisait sur l'aplat orange, qui était
   la raison d'être de l'abandon de `visuelTon` — cette raison a disparu avec le fond
   clair, mais le champ reste inutile pour autant. */
.etapes__boite--integrations {
  background-color: var(--c-pilier-integrations-sombre);
  /* Le fond de la boîte devient le fond de référence, pour que `.btn--secondary`
     recopie la bonne couleur plutôt que le crème de la page. */
  --section-bg: var(--c-pilier-integrations-sombre);
  --bg-page: var(--c-pilier-integrations-sombre);
  --etapes-accent: var(--c-fluo);
  --text-muted: color-mix(in srgb, var(--c-white) 78%, var(--c-pilier-integrations-sombre));
  color: var(--text-strong);
}

/* La respiration sous le titre est comptée, pas généreuse : la section est
   épinglée sur une hauteur de fenêtre, et tout ce qu'on donne ici est pris au
   sommaire, qui réserve la place de huit entrées à 32px. */
.etapes__entete {
  margin-block-end: clamp(var(--sp-8), 3.4vw, var(--sp-12));
}

/* Le titre ne déclare rien de sa police : _base.scss pose déjà le titrage et la
   taille du niveau. La couleur vient de --text-strong, que `.on-dark` a
   basculée en blanc. */
.etapes__titre {
  margin: 0;
  max-width: 28ch;
  color: var(--text-strong);
  text-wrap: balance;
}

/* La colonne du sommaire a été élargie de 300fr à 380fr le 18/08/2026 : elle ne
   porte plus une simple table des matières mais LE titre de l'étape lue, à
   24px. À 300fr, « Modélisation avancée » se repliait dès les largeurs
   moyennes, et la liste changeait de hauteur d'un cran de fenêtre à l'autre. */
.etapes__suite {
  display: grid;
  grid-template-columns: minmax(0, 380fr) minmax(0, 820fr);
  gap: clamp(var(--sp-8), 4vw, var(--sp-16));
  align-items: start;
}

/* =========================================================================
   MODE SANS SOMMAIRE — le patron de la séquence de l'accueil.

   Demandé par Hugo le 20/08/2026 : « un scroll plus comme celui de la homepage, une
   seule étape à la fois, titre et texte ». La table des matières disparaît, et le nom
   de l'étape revient dans son panneau en intitulé — le gabarit ne le rend QUE dans ce
   mode, voir blocs/etapes.njk.

   ⚠️ TROIS CHOSES CHANGENT, ET AUCUNE N'EST DÉCORATIVE :

     la première colonne n'est plus le sommaire mais LE SEUL RAIL. Elle passe donc de
       380fr à `auto` : à 380fr, la colonne gardait la largeur d'une table des matières
       absente et ouvrait un vide de 30 % de la boîte ;
     le panneau gagne un titre, donc sa colonne de texte porte trois rangs au lieu de
       deux — le badge, l'intitulé, le corps ;
     le badge « Étape N » passe en PASTILLE. En texte nu il était le seul repère de
       position avec le rail, et il se perdait ; en pastille fluo il ancre le haut du
       panneau, ce que faisait le sommaire avant lui.

   ⚠️ CE MODE NE CONVIENT PAS À HUIT ÉTAPES. Sans carte du parcours, on sait qu'on est
   à l'étape 5 mais pas ce qui reste — c'est précisément ce que le sommaire de la page
   produit résout. Le choix se fait sur le nombre, pas sur le goût.
   ========================================================================= */
@media (min-width: 60rem) {
  .etapes--solo .etapes__suite {
    grid-template-columns: auto minmax(0, 1fr);
  }
}
/* Le rail est seul dans sa colonne : plus de seconde piste à réserver. */
.etapes--solo .etapes__pistes {
  grid-template-columns: auto;
}

/* ⚠️ LE BADGE EN PASTILLE FLUO, ET SON LIBELLÉ EN ENCRE SOMBRE. Le fluo ne sert ici
   que d'aplat : il ne donne que 1,82 de rapport avec l'orange de la boîte, donc il ne
   s'en détache pas par la luminance mais par la TEINTE — un jaune-vert sur un orange.
   C'est admis pour une pastille, ça ne le serait pas pour du texte.

   Le libellé, lui, est ce qui doit se lire : --c-text-main sur le fluo donne 15,8. Il
   est écrit en clair et non pris à un rôle, parce que la pastille est une carte claire
   quel que soit le fond de la boîte — bleu nuit sur la page produit, orange ici.

   `align-self: start` : sans lui la pastille s'étire sur toute la largeur de la
   colonne de texte, qui est une pile flex. */
.etapes--solo .etape__numero {
  align-self: start;
  padding: 0.25rem var(--sp-4);
  border-radius: var(--radius-full);
  background-color: var(--c-fluo);
  color: var(--c-text-main);
}

/* L'intitulé de l'étape. Un `<h3>`, donc _base.scss a déjà posé le titrage, la graisse
   et la taille du niveau : on ne touche qu'à la couleur, la marge et la mesure — c'est
   la règle 3 de MAQUETTE.md.

   La mesure est en `ch` comme celle du corps juste en dessous : les deux se calent
   ainsi sur la même colonne, quel que soit le corps du texte. */
.etape__titre {
  margin: 0;
  max-width: 24ch;
  color: var(--text-strong);
  text-wrap: balance;
}

/* Le titre quand c'est une PHRASE et non un mot — le champ `accroche`, posé le
   25/08/2026 sur /produit/ (« comme titre de chaque étape, la première phrase de
   chaque étape »).

   ⚠️ SEULE LA MESURE CHANGE, et c'est la règle 3 de MAQUETTE.md qui l'impose : une
   feuille de bloc ne déclare que l'écart réel — la couleur, la marge, la mesure — et
   surtout pas un `font-size`, qui produirait un titre ne ressemblant à rien d'autre
   sur le site. La tentation était réelle ici, puisqu'une phrase de 85 signes à
   l'échelle d'un h3 est un objet lourd ; c'est la MESURE qui règle le problème, pas
   le corps. À 24ch la plus longue des huit tenait sur quatre lignes, à 42 elle en
   fait deux.

   `text-wrap: balance` est déjà posé au-dessus et devient vraiment utile ici : il
   égalise les deux lignes au lieu d'en laisser une pleine et un mot seul dessous. */
.etape__titre--accroche {
  max-width: 42ch;
}

/* ---------- Le rail de suivi, à gauche du sommaire ----------
   Demandé par Hugo le 20/08/2026 pour /excel-sheets/. C'est le même objet que
   `.etapes__rail` du mode liste — un rail et son pouce, dont la position vient de
   `--etapes-progression`. Le mode panneaux ne publiait pas cette variable ; il le fait
   depuis le même jour (voir assets/js/etapes.js).

   ⚠️ LE RAIL EST FRÈRE DU SOMMAIRE, PAS SON ENFANT. Les entrées inactives sont
   réduites en `transform` : un rail posé dans la liste serait mis à l'échelle avec
   elles et deviendrait une suite de segments d'épaisseurs différentes. Il vit donc à
   côté, sur toute la hauteur — exactement comme dans le mode liste.

   Purement décoratif : la position est déjà portée par `aria-current` sur l'entrée
   lue, qui est le jeton prévu pour ça. */
.etapes__pistes {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: clamp(var(--sp-4), 1.6vw, var(--sp-6));
}

/* ---------- Le sommaire ---------- */
.etapes__sommaire {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
}

/* ⚠️ C'EST L'ENTRÉE INACTIVE QUI RÉTRÉCIT, PAS L'ACTIVE QUI GROSSIT — et
   l'inversion n'est pas cosmétique. Trois contraintes se contredisaient :

     le sommaire fait maintenant office de titre, donc l'entrée active doit
       être NETTE — un texte agrandi en `transform` est rastérisé à sa taille
       d'origine puis étiré, il perd son piqué, et c'est l'entrée qu'on lit le
       plus qui en souffrait ;
     la liste est immobile et l'œil la fixe, donc AUCUN saut n'est tolérable ;
     deux noms font deux mots (« Modélisation socle », « Modélisation
       avancée ») et se replient sur une colonne étroite — une vraie croissance
       en `font-size` change alors le nombre de lignes, donc la hauteur, donc
       pousse les sept autres entrées.

   La sortie est de réserver la place au GRAND corps pour toutes les entrées :
   le repli se calcule une fois pour toutes, identique dans les deux états, donc
   la hauteur ne bouge jamais. L'entrée active reste alors à sa taille naturelle
   — nette — et ce sont les inactives qu'on réduit en `transform`, où la perte
   de piqué ne coûte rien.

   Corollaire : la graisse ne change PAS entre les deux états. Un passage en
   semibold élargirait les glyphes, pourrait faire basculer un nom sur deux
   lignes, et ferait revenir le saut par la fenêtre.

   `left center` comme origine, sinon l'entrée rétrécit vers son centre et sa
   marge de gauche paraît se dérober. */
.etapes__sommaire-item {
  display: flex;
  align-items: baseline;
  gap: var(--sp-3);
  padding-block: var(--sp-2);
  color: var(--text-muted);
  /* ⚠️ LE CORPS EST PORTÉ PAR L'ENTRÉE, PAS PAR LE LIBELLÉ. C'est ce qui permet
     au numéro de s'exprimer en `em` et de garder son rapport au nom à toutes les
     largeurs : à 32px de nom, un numéro resté à 11px paraissait perdu.

     20px au plus étroit, 32px au plus large. Le plafond n'est pas arbitraire :
     c'est la place réservée par CHAQUE entrée, huit fois, dans une section dont
     la hauteur est celle de la fenêtre. À 32px les huit entrées pèsent ~475px,
     ce qui laisse encore de la marge ; à 40px elles dépassent dès qu'un nom se
     replie. */
  font-size: clamp(1.25rem, 0.6rem + 1.6vw, 2rem);
  transform: scale(0.68);
  transform-origin: left center;
  transition: color var(--dur-base) var(--ease-out), transform var(--dur-base) var(--ease-out);
}

.etapes__sommaire-num {
  flex: none;
  font-family: var(--font-mono);
  font-size: 0.45em;
  font-weight: var(--fw-medium);
  font-variant-numeric: tabular-nums;
  letter-spacing: var(--ls-wide);
}

.etapes__sommaire-label {
  min-width: 0;
  font-weight: var(--fw-semibold);
  line-height: var(--lh-snug);
}

/* L'étape en cours : à sa taille pleine, et à la couleur d'accent de la boîte —
   fluo sur le bleu nuit (11,54), noir sur le vert (5,41). Voir `--etapes-accent`
   en tête de `.etapes__boite`, qui explique pourquoi ce n'est plus écrit en dur. */
.etapes__sommaire-item[aria-current=step] {
  color: var(--etapes-accent);
  transform: none;
}

/* ---------- La scène ----------
   Les huit étapes occupent la même cellule : elles se succèdent au même
   endroit, exactement comme les trois panneaux de la séquence. */
.etapes__scene {
  display: grid;
}

.etape {
  grid-area: 1/1;
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: clamp(var(--sp-5), 2vw, var(--sp-8));
}

.etape__numero {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--etapes-accent);
}

/* « / 8 · Fiabilisation », accroché au badge et RÉSERVÉ AU MODE ÉTROIT, où le
   sommaire est masqué. Voir le bloc `mq-down("lg")` plus bas, et l'avertissement
   de blocs/etapes.njk sur la source unique du nom dans l'arbre
   d'accessibilité — c'est `display` et rien d'autre qui doit le masquer, sinon
   le nom serait annoncé deux fois au large.

   ⚠️ AUCUNE MISE EN FORME PROPRE, ET C'EST VOULU : ces repères sont une suite du
   badge, pas un objet. Ils héritent donc de son mono, de son interlettrage, de
   ses capitales et de son encre d'accent. Une pastille fluo avait été essayée —
   celle du mode solo — puis écartée : à 360 px « ÉTAPE 6 / 8 · MODÉLISATION
   AVANCÉE » se replie sur deux lignes, et une pilule qui se replie cesse d'être
   une pilule. En texte nu, le repli ne coûte rien. */
.etape__reperes {
  display: none;
}

/* Il y avait ici `.etape__nom`, le nom de l'étape répété en gros dans le
   panneau. Retiré le 18/08/2026 avec l'élément : le sommaire porte ce nom, en
   fluo et à taille pleine dès qu'il est actif, et les deux titres se
   disputaient la lecture. Voir blocs/etapes.njk. */
/* Corps de texte : --text-strong, donc blanc ici. Règle du site, voir
   _tokens.scss — le texte courant ne passe jamais par --text-muted. */
.etape__corps {
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
  max-width: 46rem;
  font-size: var(--fs-base);
  line-height: var(--lh-normal);
  color: var(--text-strong);
}

.etape__corps p {
  margin: 0;
}

/* =========================================================================
   MODE ÉTROIT — LE SOMMAIRE CÈDE LA PLACE AU RAIL SEUL.

   Demandé par Hugo le 27/08/2026 : « si on fait comme le scroll de la landing
   page ? En affichant le nom de l'étape, le titre et un rail à gauche ». C'est
   donc le patron du mode `solo` — celui de /excel-sheets/ — appliqué sous `lg` à
   une instance qui garde son sommaire au large. Le desktop ne bouge pas d'un
   pixel : tout ce qui suit vit dans cette media query.

   ---------------------------------------------------------------------------
   ⚠️ CE N'EST PAS UN CHOIX DE GOÛT, C'EST UNE SOUSTRACTION. Sur un écran de
   660 px, la boîte laisse 524 px de place utile (660 − 81,6 de rembourrage de
   section − 54,4 de boîte). Le titre de section en prend 81. Restait donc 443 px
   pour le panneau — et le sommaire à plat en dépensait 240 à lui seul, laissant
   205 px là où le plus long des huit textes en demande 250. Trois textes sur
   huit débordaient, et l'épinglage (`start: "top top"`) rendait ce débordement
   INATTEIGNABLE : ~100 px perdus à 390 × 660, ~170 à 360 × 640, toujours la fin
   du paragraphe.

   Le rail, lui, ne coûte que de la LARGEUR : 3 px de trait plus sa gouttière,
   zéro pixel de hauteur. Le panneau récupère les 240 px, et les huit textes
   passent intégralement — sans en réécrire un seul. Mesuré : ~130 px de marge à
   390 × 660, ~20 px à 360 × 640.

   ⚠️ L'OBJECTION DU 20/08/2026 RESTE VRAIE, ET ELLE EST ASSUMÉE. Le bloc écrit
   plus haut que le mode sans sommaire « ne convient pas à huit étapes », parce
   qu'on sait alors où on en est mais pas ce qui reste. Cette objection a été
   posée POUR LE DESKTOP, où la carte tient dans la largeur. À 390 px elle coûte
   240 des 443 px disponibles, donc l'arbitrage s'inverse. Ce qui est rendu au
   lecteur, c'est le compte : le badge devient « Étape 4 / 8 · Fiabilisation »
   (voir blocs/etapes.njk) et le pouce du rail montre la part parcourue. On perd
   les NOMS des étapes à venir, pas la position ni le reste à faire.

   ⚠️ LE SOMMAIRE EST MASQUÉ, JAMAIS RETIRÉ DU DOM. assets/js/etapes.js exige en
   mode `panneaux` que le nombre d'entrées de sommaire égale celui des panneaux,
   sous peine de ne plus rien surligner DU TOUT — et il monte la scène à toutes
   les largeurs. `display: none` le sort de l'affichage et de l'arbre
   d'accessibilité en le laissant dans le DOM : les deux comptes restent égaux.

   ⚠️ LA DURÉE D'ÉPINGLAGE NE CHANGE PAS (0.55, soit ~4,4 écrans). Descendre
   sous 0.5 fait se marcher dessus les transitions — c'est la borne indiquée par
   sequence.js — et le moteur, les transitions et leur amplitude sont les mêmes
   aux deux largeurs. Un second réglage propre à l'étroit demanderait un crochet
   de plus dans un script partagé par toutes les séquences du site, pour un gain
   marginal. Une seule cadence, donc.
   ========================================================================= */
/* La famille visée : le mode `panneaux` QUI A un sommaire. Écrite une fois pour
   ne pas pouvoir divorcer d'elle-même — `.etapes--solo` est posé par le gabarit
   quand `sommaire: false`, et le mode `liste` n'a ni `__suite` ni `__pistes`
   (c'est un autre balisage entier, voir blocs/etapes.njk). */
@media (max-width: 60rem) {
  /* Le rembourrage se resserre, et c'est ce qui fait tenir les petits écrans.
     À 360 × 640 le panneau demande 423 px : sans ces deux réglages il n'en
     reste que 396, avec eux 443. Rien de cosmétique donc — c'est la différence
     entre un texte entier et un texte coupé. */
  .etapes:not(.etapes--solo) {
    padding-block: var(--sp-8);
  }
  .etapes:not(.etapes--solo) .etapes__boite {
    padding-block: var(--sp-5);
  }
  /* Deux colonnes, mais ce ne sont plus les mêmes : le rail et la scène. La
     gouttière est celle de `.etapes__pistes` et non celle de `.etapes__suite`
     (32 à 64 px) — au large le rail est posé loin du texte, à 390 px cette
     gouttière mangerait un dixième de la colonne de lecture.

     ⚠️ `align-items: stretch` EST OBLIGATOIRE, PAS UN DÉTAIL DE FINITION. Le
     rail n'a aucune hauteur propre : il la tient de son étirement dans la
     rangée. Au large c'est le SOMMAIRE, à côté de lui, qui donne sa hauteur à
     `.etapes__pistes` ; le sommaire masqué, la colonne retombe à zéro et le
     rail disparaît purement et simplement, `.etapes__suite` étant en
     `align-items: start`. Étiré, il prend la hauteur de la scène.

     Effet de bord heureux : la scène fait la hauteur du PLUS HAUT des panneaux,
     tous superposés dans la même cellule. Le rail garde donc une hauteur unique
     quelle que soit l'étape lue, et rien ne saute d'un palier à l'autre — même
     dispositif que les cinq panneaux empilés d'`onglets-metiers`. */
  .etapes:not(.etapes--solo) .etapes__suite {
    grid-template-columns: auto minmax(0, 1fr);
    gap: var(--sp-4);
    align-items: stretch;
  }
  /* Le rail est seul dans sa colonne : plus de seconde piste à réserver. */
  .etapes:not(.etapes--solo) .etapes__pistes {
    grid-template-columns: auto;
  }
  /* ⚠️ SÉLECTEUR NU À DESSEIN : `.etapes__sommaire` n'existe QUE dans cette
     famille. Le gabarit ne le rend que sous `{% if aSommaire %}`, dans la
     branche `panneaux` — donc ni en mode `liste`, ni en mode `solo`, qui est
     précisément l'absence de sommaire. */
  .etapes__sommaire {
    display: none;
  }
  /* Le badge reprend la position et le nom que portait le sommaire. */
  .etape__reperes {
    display: inline;
  }
  /* Le mode solo garde exactement ce qu'il avait : une colonne, le rail
     au-dessus de la scène. C'est /excel-sheets/, et rien ne demandait de le
     changer. */
  .etapes--solo .etapes__suite {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--sp-8);
  }
}
/* =========================================================================
   L'ÉTAPE 0 — le chapeau de la section et son visuel, devenus le premier temps
   du défilement. Ajoutée le 18/08/2026 pour la page dictionnaire.

   Deux colonnes quand il y a la place : le texte à gauche, le visuel à droite.
   En dessous, elles s'empilent — le panneau doit tenir dans un écran, la section
   étant épinglée sur une hauteur de fenêtre.
   ========================================================================= */
.etape--intro {
  gap: clamp(var(--sp-5), 2.4vw, var(--sp-10));
}

/* ⚠️ LA RANGÉE VAUT POUR TOUT PANNEAU QUI PORTE UN VISUEL, pas seulement pour
   l'intro. Ces règles étaient calées sur `.etape--intro` jusqu'au 20/08/2026, parce
   que l'intro était le seul panneau à pouvoir en porter un ; les étapes le peuvent
   depuis qu'Hugo l'a demandé pour la page Excel & Sheets (« à gauche le texte de
   l'étape et à droite le placeholder de l'image »).

   `:has()` plutôt qu'une classe de plus sur le panneau : c'est la PRÉSENCE du visuel
   qui commande, et le gabarit n'a donc rien à savoir de cette règle. Un panneau sans
   visuel reste une simple colonne, ce qui est le cas de toutes les étapes de la page
   produit.

   ⚠️ LA COLONNE DE TEXTE EST `.etape__texte` POUR UNE ÉTAPE ET `.etape__corps` POUR
   L'INTRO, et c'est pour ça que les deux sont visées. L'intro n'a pas de badge, donc
   son corps est directement l'enfant de la rangée ; une étape en a un, et il doit
   rester AU-DESSUS de son texte — d'où l'enveloppe. Voir blocs/etapes.njk. */
/* ⚠️ `align-items: center` DEPUIS LE 26/08/2026 — « tu peux centrer les textes en
   hauteur par rapport aux images ? » (Hugo). C'est le retour exact de ce qu'il avait
   fait retirer le 20/08 (« il faut que les étapes soient alignées en hauteur sur le
   texte »), et le contexte a changé deux fois entre-temps :

     la page N'A PLUS DE SOMMAIRE — c'est le mode `solo`, décidé le même 20/08 : la
       colonne de gauche n'est plus qu'un rail, qui court sur toute la hauteur. Or
       c'était lui, l'argument du `start` : le texte centré partait plus bas que la
       première entrée du sommaire, et l'œil devait descendre en passant de l'un à
       l'autre. Il n'y a plus rien à quoi s'aligner en haut ;
     le cadre CARRÉ a laissé la place aux illustrations livrées le 26/08, dont deux
       sont bien moins hautes que lui.

   ⚠️ SI UNE PAGE À SOMMAIRE POSE UN JOUR DES VISUELS, la raison du `start` redevient
   valable telle quelle — c'est la seule chose à vérifier avant de la contredire ici.
   Aucune ne le fait aujourd'hui : /produit/, qui a le sommaire, n'a pas de visuel, et
   cette règle est portée par `:has(> .etape__visuel)`.

   ---------------------------------------------------------------------------
   LES QUATRE RÉGLAGES CI-DESSOUS RÉPONDENT À UN SEUL DÉFAUT, signalé par Hugo le
   20/08/2026 : « le corps est difficilement mis en valeur, il semble très loin des
   titres et trop proche du placeholder ; il pourrait prendre moins de largeur quitte
   à être plus long ».

   Le diagnostic était géométrique, pas typographique. Le corps était à 4rem de son
   titre (la gouttière de `.etapes__suite`) et à 2rem seulement du cadre (celle du
   panneau) : il appartenait visuellement au cadre, pas au sommaire qui l'annonce. Et
   sa mesure tournait autour de 50 caractères, large pour une colonne sans repère.

     1. la gouttière sommaire ↔ scène est DIVISÉE quand les panneaux portent un
        visuel — le corps se rapproche du titre qui l'annonce ;
     2. la gouttière texte ↔ cadre est DOUBLÉE — le corps s'éloigne du cadre ;
     3. le badge et le corps se resserrent, pour se lire comme UN bloc ;
     4. la mesure descend à 38 caractères — « moins de largeur quitte à être plus
        long », mot pour mot ;
     5. la rangée SE CONSTRUIT DEPUIS LE MILIEU (26/08/2026, voir plus bas).

   ⚠️ LE CORPS DU TEXTE NE CHANGE PAS, et une tentative a été retirée le jour même :
   `font-size: var(--fs-md)` avait été posé ici pour « donner de la présence ». Il ne
   faisait RIEN au-delà de 1030px — --fs-md vaut 18px, soit exactement le plafond de
   --fs-base — et il aurait fallu écrire un `clamp()` à la main pour obtenir un vrai
   cran, donc sortir de l'échelle typographique du site pour une seule règle. La mise
   en valeur demandée est géométrique : c'est la mesure et les gouttières qui la font.

   ⚠️ LES QUATRE SONT PORTÉS PAR `:has(> .etape__visuel)` OU PAR LE `:has()` DE
   SECTION, donc ils ne touchent QUE cette disposition. Les huit étapes de la page
   produit n'ont pas de visuel : leur panneau, leur gouttière et leur corps sont
   exactement ce qu'ils étaient. Ne pas remonter ces règles d'un niveau. */
/* 1. Le corps se rapproche de son titre. La gouttière tombe de ~4rem à ~2rem, soit
      la moitié de celle du panneau — le rapport est donc inversé par rapport à
      l'état d'avant, et c'est tout le point. */
@media (min-width: 60rem) {
  .etapes:has(.etape__visuel) .etapes__suite {
    gap: clamp(var(--sp-5), 2vw, var(--sp-8));
  }
}
@media (min-width: 56.25rem) {
  .etape:has(> .etape__visuel) {
    flex-direction: row;
    align-items: center;
    /* 2. Et il s'éloigne du cadre. */
    gap: clamp(var(--sp-8), 4vw, var(--sp-16));
    /* 5. ⚠️ LA RANGÉE SE CONSTRUIT DEPUIS LE MILIEU, PAS DEPUIS LES CÔTÉS —
          « en mettant le texte et l'image plus proche du centre sans toucher à la
          taille des éléments » (Hugo, 26/08/2026).

          Le défaut : le texte était collé à l'arête gauche, l'illustration à l'arête
          droite, et les 543 px qui restaient au milieu se lisaient comme un trou. Ce
          n'était PAS le rembourrage de la boîte (resserré le même jour, sans effet
          visible) : c'était la rangée elle-même, dont les deux objets réunis ne font
          que ~690 px sur 1176.

          ⚠️ ET LE TROU NE POUVAIT PAS SE REFERMER PAR LA TAILLE. L'illustration est
          plafonnée en HAUTEUR (la scène est épinglée) : celle de l'étape 3, en
          portrait, ne peut pas dépasser ~300 px de large sur une fenêtre courante,
          quel que soit le plafond de largeur qu'on lui donne. Restait la mesure du
          texte, fixée à 38 caractères par Hugo lui-même. Aucun des deux ne bouge :
          c'est la RÉPARTITION qui change, et le vide passe au milieu vers les deux
          bords, où il ne se lit plus comme un trou mais comme une marge.

          ⚠️ `space-evenly` ET NON `center`, corrigé le même jour : « c'est possible de
          mettre le texte horizontalement entre l'image et le bord gauche ? […] au
          centre des deux ». Centrée, la PAIRE était centrée mais pas le texte — il
          restait ~214 px à sa gauche contre ~54 px jusqu'à l'illustration, donc il
          paraissait poussé contre elle. `space-evenly` donne trois espaces ÉGAUX
          (avant le texte, entre les deux, après l'image), et l'égalité des deux
          premiers est exactement « le texte au centre des deux ».

          ⚠️ L'ILLUSTRATION AVANCE DE ~53 px, ET C'EST INÉVITABLE. Sous un centrage,
          texte et image ne peuvent bouger qu'ENSEMBLE — décaler le texte vers la
          gauche pousse mécaniquement l'image vers la droite, quelle que soit la voie
          (marge négative, rembourrage, élargissement de la colonne : les trois ont
          été vérifiées). La garder au pixel demanderait d'écrire sa marge droite en
          dur, une cote qui serait fausse à la première autre largeur de fenêtre.
          `space-evenly` est la formulation qui la déplace le MOINS tout en centrant
          le texte exactement. */
    justify-content: space-evenly;
  }
  /* 3. Le badge « Étape N » et le corps se resserrent : à 2rem ils se lisaient comme
        deux objets, à --sp-3 ils forment un bloc que le sommaire annonce. */
  .etape:has(> .etape__visuel) > .etape__texte {
    gap: var(--sp-3);
  }
  /* 4. LA MESURE COMMANDE, PAS LE POURCENTAGE. Le texte gardait 55 % de la scène —
        une largeur qui suit l'écran et qu'aucune règle de lisibilité ne borne. Ici
        `max-width` en `ch` le plafonne à 38 caractères, la mesure confortable pour un
        pavé sans interligne large.

        ⚠️ `38ch` ET NON UNE VALEUR EN `rem` : l'unité `ch` suit le corps du texte, donc
        la mesure reste de 38 caractères quelle que soit la taille que prend --fs-base
        d'un point de rupture à l'autre. En `rem`, elle dériverait.

        ⚠️ IL N'Y A PLUS AUCUNE PART EN POURCENTAGE, depuis le centrage du 26/08/2026,
        et c'est ce qui le rend possible : `justify-content` ne distribue que ce qui
        RESTE, or deux bases en 40 / 60 % ne laissent rien — la colonne du visuel
        prenait toute la place rendue par le texte plafonné, et l'image collée à droite
        y ouvrait le trou. Les deux objets sont donc dimensionnés par leur CONTENU. La
        mesure du texte est portée par le seul `max-width` ci-dessous, qui reste la
        seule écriture des 38 caractères. */
  .etape:has(> .etape__visuel) > .etape__corps,
  .etape:has(> .etape__visuel) > .etape__texte {
    flex: 0 1 auto;
    min-width: 0;
  }
  .etape:has(> .etape__visuel) .etape__corps {
    max-width: 38ch;
  }
  /* Le visuel ne rétrécit pas : c'est le texte qui se replie quand la place manque,
     une image comprimée sous sa largeur d'attribut débordant au lieu de suivre. */
  .etape:has(> .etape__visuel) > .etape__visuel {
    flex: 0 0 auto;
  }
}
/* ⚠️ ET LA GOUTTIÈRE DISPARAÎT À PARTIR DE `lg`, sans quoi le centrage du texte est
   FAUX : `space-evenly` répartit trois espaces égaux, mais la gouttière s'ajoute au
   seul espace du milieu — le texte serait alors décalé d'une demi-gouttière (~27 px)
   vers la gauche de sa bande. Les ~160 px que la répartition ouvre d'elle-même font
   déjà, et bien au-delà, le travail du réglage 2 (« le corps s'éloigne du cadre »).

   Elle reste en place entre `md` et `lg`, où la boîte se resserre : la place libre y
   tombe à presque rien, la répartition n'ouvre plus d'espace, et c'est la gouttière
   qui empêche le texte de toucher l'image. Le décalage d'une demi-gouttière existe
   toujours à ces largeurs — il n'y a plus de bande où il se verrait. */
@media (min-width: 60rem) {
  .etape:has(> .etape__visuel) {
    column-gap: 0;
  }
}
/* La colonne de texte d'une étape : le badge, puis le corps. Elle reprend la
   gouttière du panneau, dont elle a pris la place.

   ⚠️ CETTE GOUTTIÈRE EST RESSERRÉE quand le panneau porte un visuel — la règle est
   plus haut dans ce fichier. Elle l'emporte malgré l'ordre, sa spécificité étant plus
   forte (`:has()` compte celle de son argument). Si un jour cette valeur-ci paraît ne
   pas s'appliquer sur /excel-sheets/, c'est là qu'il faut regarder. */
.etape__texte {
  display: flex;
  flex-direction: column;
  gap: clamp(var(--sp-5), 2vw, var(--sp-8));
}

/* Le cadre en attente du visuel définitif. Le ratio est celui des losanges
   empilés de la maquette (Figma 174:9430), pour que la mise en page ne saute pas
   quand l'export arrivera.

   ---------------------------------------------------------------------------
   ⚠️ IL EST PLAFONNÉ EN LARGEUR, et pas seulement par sa colonne. Réduit à la demande
   d'Hugo le 20/08/2026 : il prenait 60 % de la scène, et comme il est CARRÉ, sa
   largeur est aussi sa hauteur — soit plus de 450px sur un grand écran, dans une
   section épinglée sur une seule hauteur de fenêtre. C'était le plus gros
   consommateur du budget de hauteur, pour un cadre vide.

   ⚠️ RÉDUIRE SA PART DE LA RANGÉE N'AURAIT RIEN FAIT, et c'est le piège. Le texte
   d'à côté est plafonné à 38 caractères : il ne peut pas absorber la place rendue,
   donc `flex-grow` la redonnait intégralement au cadre. Il fallait donc le plafonner
   LUI, pas sa colonne.

   ⚠️ LA COTE EST DEVENUE UNE LARGEUR, ET NON PLUS UN PLAFOND, avec le centrage du
   26/08/2026. La colonne du visuel est maintenant dimensionnée par son CONTENU : un
   `max-width` seul y laisserait le cadre prendre la largeur de son propre texte
   (« [Placeholder] » et sa légende), au lieu du carré attendu. L'image définitive n'a
   pas ce problème — son attribut `width` lui donne déjà une largeur.

   `margin-inline-start: auto` ne sert plus que la mise en colonne, sous `md` : dans la
   rangée centrée, la colonne fait exactement la largeur du cadre, donc il n'y a plus
   d'espace libre à absorber et la marge est inerte. Sans elle, le cadre repasserait à
   gauche sur mobile. */
.etape__placeholder {
  aspect-ratio: 1;
  /* ⚠️ LA COTE PASSE PAR UN POMMEAU DEPUIS LE 27/08/2026, et son défaut est dans le
     `var()` : le mode étroit la réduit, et il la réduit UNE FOIS pour le cadre en
     attente comme pour l'illustration définitive — voir `.etape__visuel` sous
     `mq-down("lg")`. Écrite en déclaration ici, elle masquerait la valeur posée par
     l'enveloppe et les deux objets du même emplacement rétréciraient différemment. */
  inline-size: var(--illustration-largeur, clamp(14rem, 24vw, 22rem));
  /* ⚠️ INVERSÉE SOUS `md` DEPUIS LE 27/08/2026 : la mise en colonne CENTRE le visuel
     au lieu de le pousser à droite. Voir le bloc de mode étroit plus bas. */
  margin-inline-start: auto;
}

/* ---------- L'illustration définitive, à la place du cadre ----------
   Ajoutée le 26/08/2026 avec les trois exports de la page.

   ⚠️ LA HAUTEUR EST BORNÉE, ET C'EST LE POINT DÉLICAT DE CETTE RÈGLE. La scène est
   ÉPINGLÉE : chaque étape doit tenir dans une fenêtre — la contrainte que décrit le
   commentaire du cadre ci-dessus — et une image ne s'y plie pas toute seule. Un
   `<img>` s'affiche à sa taille INTRINSÈQUE : l'export de l'étape 2 fait 263 × 404,
   donc sous le plafond de largeur et bien au-dessus du cadre carré qu'il remplace.
   Mesuré sur /excel-sheets/ en 1280 × 720 (fenêtre de 624), sans plafond la section
   montait à 660 : la scène débordait de 36 px et était rognée pendant l'épinglage.

   ⚠️ MAIS ELLE EST BORNÉE PAR LA LARGEUR, PAS PAR `max-block-size`, ET C'EST TOUT
   L'ENJEU. Les attributs `width`/`height` posés par le gabarit valent LARGEUR
   SPÉCIFIÉE, et ce sont eux qui réservent la place de l'image avant son arrivée
   (lib/image.js). Deux impasses, toutes deux vérifiées à la mesure :

     `max-block-size` seul        rabote la hauteur sans toucher la largeur : l'image
                                  sort étirée, 263 × 338 au lieu de 220 × 338, soit
                                  19 % — sans erreur ni avertissement.
     `+ inline-size: auto`        rétablit le rapport… en annulant l'attribut `width`.
                                  L'image redevient donc sans dimensions TANT QU'ELLE
                                  N'EST PAS CHARGÉE (0 × 0 relevé sur les deux), ce
                                  qui est exactement le saut de page que les deux
                                  attributs existent pour empêcher.

   On borne donc la LARGEUR par la hauteur voulue, en passant par le rapport que le
   gabarit publie sur l'élément (`--illustration-ratio`, une donnée, comme le
   `--shape-ratio` de forme.njk). L'attribut `width` n'est jamais contredit, la
   hauteur suit toute seule, et la place reste réservée.

   ⚠️ LES 21rem SONT MESURÉS, PAS CHOISIS : ~174 px d'habillage de la boîte (titre,
   rail, rembourrage) plus 82 px de rembourrage de section, soit ~19rem, arrondis au
   rem supérieur pour la marge. Calibré sur la seule page qui pose des images ici ;
   si une autre en pose avec un sommaire, remesurer plutôt que rogner l'image. `svh`
   comme la section elle-même.

   Conséquence acceptée : sur un écran haut l'illustration garde sa taille naturelle,
   elle n'est réduite que là où il le faut.

   ⚠️ PAS D'`aspect-ratio` ICI. Le rapport vient du fichier ; en écrire un le
   contredirait dès la deuxième illustration livrée — et les deux premières le
   montrent déjà, 1,12 en paysage pour l'une, 0,65 en portrait pour l'autre. Même
   règle que pour les hôtes de partials/motif.njk. */
.etape__illustration {
  display: block;
  /* ⚠️ SEUL LE PLAFOND DE LARGEUR PASSE PAR UN POMMEAU, PAS LE TERME DE HAUTEUR. Le
     mode étroit ne réduit que le premier des deux (27/08/2026) : recopier le `min()`
     en entier dans la media query aurait laissé DEUX écritures du budget de hauteur,
     donc deux cotes à tenir d'accord sans rien pour signaler leur divergence. Le
     défaut est dans le `var()` et jamais en déclaration, comme partout ailleurs sur
     le site — une enveloppe qui veut une autre largeur doit pouvoir la poser. */
  max-inline-size: min(var(--illustration-largeur, clamp(14rem, 24vw, 22rem)), (100svh - 21rem) * var(--illustration-ratio, 1));
  block-size: auto;
  /* Inerte dans la rangée centrée — la colonne fait exactement la largeur de l'image.
     ⚠️ ET INVERSÉE SOUS `md` DEPUIS LE 27/08/2026 : la mise en colonne CENTRE le
     visuel, elle ne le pousse plus à droite. Voir le bloc de mode étroit juste en
     dessous. Même parti que `.etape__placeholder` juste au-dessus. */
  margin-inline-start: auto;
}

/* =========================================================================
   MODE ÉTROIT — LE VISUEL RÉTRÉCIT, PUIS SE CENTRE.

   Demandé par Hugo le 27/08/2026 pour /excel-sheets/ : « tu peux centrer les
   images. Les rendre un peu plus petites ». Le desktop ne bouge pas d'un pixel :
   tout ce qui suit vit dans ces deux media queries.

   ⚠️ CE N'EST PAS QU'UNE AFFAIRE DE GOÛT, C'EST DU BUDGET DE HAUTEUR. La scène est
   épinglée et chaque étape doit tenir dans une fenêtre ; en colonne, l'image ne
   partage plus la hauteur du texte, elle S'AJOUTE à elle. Mesuré sur /excel-sheets/
   avant réduction : la section demandait 692 px pour 660 à 390 × 660, et 740 px pour
   640 à 360 × 640 — la fin du dernier panneau était rognée pendant l'épinglage, donc
   INATTEIGNABLE. Après : 660 (elle tient exactement) et 680, soit 40 px encore de
   trop sur le plus petit écran. ⚠️ SI CES 40 px DOIVENT TOMBER, la sortie est le
   resserrage de rembourrage déjà écrit pour la famille à sommaire (voir `$carte`
   sous `mq-down("lg")`, qui en rend 54) — surtout pas une image plus petite encore,
   qui ne rendrait que ~20 px de plus avant de ne plus rien montrer.

   ⚠️ ET LE PANNEAU LE PLUS HAUT CHANGE AU PASSAGE : c'était l'étape 2 et son portrait
   (141 + 292), c'est maintenant l'étape 1 et son texte de sept lignes (262 + 133).
   C'est donc le TEXTE qui commande le budget désormais, et non plus l'image.

   ⚠️ ET C'EST LE TERME DE LARGEUR QUI DESCEND, PAS CELUI DE HAUTEUR. Les 21rem du
   `min()` ci-dessus sont l'habillage MESURÉ AU LARGE (titre, rail, rembourrages) ;
   en colonne, le badge, l'intitulé et le corps passent AU-DESSUS de l'image et
   l'habillage double presque — il varie même d'un panneau à l'autre (262 px de
   texte pour l'étape 1, 141 pour les deux autres). Aucune constante ne peut être
   juste pour les trois, alors que le plafond de largeur l'est pour tous : c'est
   d'ailleurs ce qu'Hugo a demandé mot pour mot.

   ~21 % au plancher (14 → 11rem) et ~17 % au plafond. Le portrait de l'étape 2, le
   plus haut des trois, passe de 292 à ~234 px.
   ========================================================================= */
@media (max-width: 60rem) {
  .etape__visuel {
    --illustration-largeur: clamp(11rem, 20vw, 17.5rem);
  }
}
/* Le centrage n'arrive qu'avec la MISE EN COLONNE, et c'est `md` qui la déclenche
   (voir `.etape:has(> .etape__visuel)` plus haut). Entre `md` et `lg` la rangée
   tient encore : la colonne du visuel y fait exactement la largeur de l'image, donc
   il n'y aurait rien à centrer — une marge automatique y serait inerte, pas fausse.
   On la pose là où elle a un sens. */
@media (max-width: 56.25rem) {
  .etape__illustration,
  .etape__placeholder {
    margin-inline: auto;
  }
}
/* ---------- Le cadre en attente, à l'aplat d'un pilier ----------
   Demandé par Hugo le 20/08/2026 pour la page Excel & Sheets : « il doit être sur
   fond orange, comme c'est toujours une page produit ». Le champ `visuelTon` du
   gabarit choisit la teinte.

   ⚠️ C'EST L'APLAT DU PILIER TEL QUEL, ET IL N'A PAS À ÊTRE FONCÉ. L'orange
   #f0a22e porte le texte sombre à 6,71 — c'est en ENCRE qu'il ne tient pas (2,01 sur
   le crème), et c'est pourquoi `.ton-page--integrations` emploie une AUTRE valeur,
   --c-pilier-integrations-encre. Les deux ne se remplacent pas : la claire est un
   fond, la foncée est une encre. Ne pas remplacer celle-ci par celle-là en croyant
   unifier.

   ⚠️ L'ENCRE DU CADRE EST REMISE EN CONTEXTE CLAIR, et il le faut : ce cadre est une
   carte claire posée dans une boîte BLEU NUIT. `.apercu` remet déjà --text-muted et
   --border-subtle pour cette raison exacte (voir son commentaire dans
   _components.scss) ; il ne reste ici que le `<strong>`, qui suit --text-strong et
   serait donc blanc sur l'orange (2,12). Règle 12 de MAQUETTE.md.

   Le dégradé clair de `.apercu` est remplacé et non recouvert : `background` d'un
   bloc écrase l'image de fond comme la couleur. */
.etape__placeholder--integrations {
  background: var(--c-pilier-integrations);
  border-color: transparent;
  --text-strong: var(--c-text-main);
}

/* ---------- La chute ----------
   Posée SOUS la boîte, sur le crème. Elle est donc hors du contexte `.on-dark` de
   la boîte sombre et prend les encres claires de la page — c'est voulu, elle
   appartient à la page et non à la scène.

   ⚠️ La section est en `display: grid` avec `align-content: center` et une hauteur
   d'un écran plein : la chute est une seconde rangée de cette grille, pas un bloc
   posé après. Elle est donc centrée avec la boîte, et c'est ce qui la garde
   solidaire d'elle pendant l'épinglage. */
.etapes__chute {
  width: 100%;
  max-width: 72rem;
  margin-inline: auto;
  margin-block-start: clamp(var(--sp-8), 3vw, var(--sp-12));
  color: var(--text-strong);
}

.etapes__chute p {
  margin: 0;
  font-size: var(--fs-base);
  line-height: var(--lh-normal);
}

.etapes__chute p + p {
  margin-block-start: var(--sp-4);
}

/* =========================================================================
   MODE LISTE — blocs/etapes.njk avec `mode: liste`.

   Demandé par Hugo le 18/08/2026 : toutes les étapes affichées, nom ET texte, et
   le défilement ne fait que grossir celle qu'on lit. Il remplace un premier essai
   en mode panneaux, où une seule étape était visible à la fois.

   L'intro tient la colonne de gauche avec son visuel, immobile. Les étapes
   occupent la droite, les unes sous les autres.
   ========================================================================= */
/* UN TIERS à gauche pour le titre et le texte, libres sur le fond de page ; DEUX
   TIERS à droite pour le bloc vert qui porte le scroll. Demandé par Hugo le
   18/08/2026, au terme de quatre dispositions essayées le même jour — deux colonnes
   égales, deux tiers / un tiers, trois tiers avec placeholder, puis boîte à gauche.
   Celle-ci est la retenue.

   ⚠️ PAS D'`align-items` ICI, ET C'EST VOULU. La valeur par défaut d'une grille est
   `stretch` : les deux colonnes prennent donc la hauteur de la rangée, c'est-à-dire
   celle de la plus haute des deux. C'est ce qui met la boîte à la même hauteur que
   le titre et son texte, comme Hugo l'a demandé le 18/08/2026.

   Il y avait `align-items: start` avant : chaque colonne prenait alors sa hauteur
   propre, et la boîte s'arrêtait où son contenu finissait. Ne pas le rétablir. */
.etapes__duo {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 2fr);
  gap: clamp(var(--sp-8), 4vw, var(--sp-16));
  width: 100%;
  max-width: 88rem;
  margin-inline: auto;
}

/* Le titre et le texte, hors boîte. Leurs encres viennent de la PAGE et non de la
   boîte verte — ils sont sur le crème. C'est pour ça que les surcharges de
   `.etapes__boite--dictionnaire` ne les concernent pas : un réglage d'encre fait
   pour la boîte n'a aucun effet ici. */
.etapes__libre {
  display: flex;
  flex-direction: column;
  gap: clamp(var(--sp-5), 2.4vw, var(--sp-8));
}

/* ---------- La boîte du scroll ----------
   Elle n'enveloppe plus que le glyphe et la liste : elle n'a donc pas les
   rembourrages généreux de la boîte pleine largeur, qui laissaient respirer un
   titre et deux colonnes.

   La liste à gauche, le glyphe à DROITE (déplacé le 18/08/2026). `auto` pour le
   glyphe : il a une largeur propre — celle que `.etapes__glyphe` lui donne — et n'a
   pas à se partager la place à égalité avec une liste de cinq entrées. C'est aussi ce
   qui fait que l'élargir se paie sur la largeur de la liste, et sur rien d'autre.

   `align-items: center` : la boîte est maintenant étirée à la hauteur du titre et de
   son texte, donc elle peut être plus haute que son propre contenu. Sans ça, la
   liste et le glyphe resteraient collés en haut d'une boîte qui respire en bas. */
.etapes__boite--scroll {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: clamp(var(--sp-5), 2.4vw, var(--sp-10));
  align-items: center;
  /* ⚠️ LE REMBOURRAGE HORIZONTAL EST DÉLIBÉRÉMENT PLUS GÉNÉREUX QUE LE VERTICAL,
     élargi le 18/08/2026 à la demande d'Hugo, et pour deux raisons distinctes :

       à GAUCHE, le texte des entrées venait toucher le bord de la boîte et ne
         respirait pas ;
       à DROITE, le glyphe frôlait le bord au point de paraître sortir de la boîte.

     C'est pourquoi `padding-block` et `padding-inline` sont séparés au lieu du
     `padding` unique qu'il y avait : les resouder ferait grandir la boîte en
     hauteur, alors qu'elle est déjà étirée sur celle du titre et de son texte. */
  padding-block: clamp(var(--sp-5), 2vw, var(--sp-8));
  padding-inline: clamp(var(--sp-6), 3.4vw, var(--sp-12));
}

/* ⚠️ LA LARGEUR COMMANDE, LA HAUTEUR SUIT — l'axe a été inversé le 25/08/2026, à la
   demande d'Hugo : « on peut grossir un peu l'animation ? Elle peut prendre plus de
   place en largeur notamment. » C'était la hauteur qui pilotait, et sur un dessin en
   portrait (155 × 245) piloter par la hauteur revient à demander le contraire de ce
   qu'on veut : à 15rem de haut, le dessin ne faisait que 152 px de large.

   Les attributs width/height du gabarit réservent la place, ils ne dimensionnent pas ;
   c'est leur rapport qui donne la hauteur ici.

   ⚠️ LE BUDGET N'EST PAS VERTICAL, IL EST HORIZONTAL. La section est épinglée, donc
   son contenu doit tenir dans un écran — mais la hauteur est déjà dictée par les cinq
   entrées (~600 px de place réservée, l'état GRAND de chacune), et le dessin plafonne
   bien en dessous : il n'ajoute pas un pixel à la section. Ce qu'il prend, il le
   prend à la COLONNE DE LA LISTE, dont la piste est en `auto`.

   ⚠️ 13rem EST UN JUSTE MILIEU CHERCHÉ EN DEUX TEMPS, le même jour. L'inversion de
   l'axe a d'abord amené le plafond à 17rem (272 × 430), qu'Hugo a trouvé « un peu
   trop gros » ; on atterrit à mi-chemin de l'ancienne taille — 208 × 329, contre
   152 × 240 avant l'inversion. Le vrai plafond technique est plus haut (~17rem reste
   la dernière valeur qui laisse les cinq descriptions sur deux lignes) : ce chiffre-ci
   est un choix d'équilibre, pas une limite. Le monter est donc sans risque jusque-là,
   le dépasser demande de vérifier le repli des descriptions.

   Le plafond en `rem` est aussi le garde-fou d'un écran large mais BAS : `vw` ignore
   la hauteur de la fenêtre, le plafond non. */
.etapes__glyphe {
  inline-size: clamp(8rem, 15vw, 13rem);
  block-size: auto;
}

/* =========================================================================
   L'ENCRE DES PLAQUES — DES BLANCS ET DU FLUO, RIEN D'AUTRE.

   Demandé par Hugo le 25/08/2026 : « est-ce que tu peux changer les couleurs ? Les
   bleus passent mal. On peut juste jouer avec des nuances de blancs et de fluo. »
   L'export porte deux bleus (#2340FF, #3F58D7) choisis pour une autre boîte que le
   vert du pilier, sur lequel ils s'éteignent.

   ⚠️ LA GÉOMÉTRIE VIENT DU FICHIER, L'ENCRE VIENT D'ICI, et c'est ce qui rend le
   changement possible sans toucher à l'export. lib/couches.js étiquette chaque tracé
   (`data-role`) selon le rôle qu'il joue dans la plaque ; les `fill` de l'export
   restent en attributs de PRÉSENTATION, donc au plus bas de la cascade — la moindre
   règle CSS les bat, et ils ne servent plus que de repli. Le pourquoi complet est en
   tête de lib/couches.js.

   ⚠️ TEINTES BRUTES ET NON RÔLES SÉMANTIQUES, contrairement à la règle générale du
   site. C'est un DÉCOR et non de l'encre sur du texte : le passer par
   `--text-strong` / `--text-accent` le ferait bouger au premier réglage d'encre de
   la boîte, pour lequel personne n'aurait pensé au dessin. Même parti que
   `.etapes__entree-nom` en fluo un peu plus bas.

   ⚠️ DEUX CHOSES DIFFÉRENCIENT LES PLAQUES, ET ELLES SE COMPLÈTENT : l'OPACITÉ, qui
   décroît régulièrement vers le bas et donne la profondeur, et la TEINTE, tirée d'un
   ordre irrégulier qui donne le désordre organique. Ni l'une ni l'autre ne suffit —
   l'opacité seule rend cinq plaques identiques, la teinte seule rend une pile plate.

   ⚠️ L'ALTERNANCE FLUO/BLANC A ÉTÉ ÉCARTÉE, et pas pour la raison qu'on croit. Elle
   avait un bon motif — les plaques arrivent une par une, chaque arrivée doit se voir —
   mais Hugo l'a refusée le 25/08/2026 comme trop mécanique : « pas une séquence
   vert-blanc-vert-blanc ». Ce que l'alternance garantissait est maintenant obtenu
   autrement, par la conjonction de l'ordre choisi et du dégradé ; la contrainte
   « chaque arrivée diffère de la précédente » est vérifiée à la main dans
   blocs/etapes.njk. Ne pas rétablir une règle de parité.

   Contrastes sur le vert du pilier (#00756c) — décoratif, aucun seuil à tenir, mais
   les teintes doivent rester franches : fluo 4,79, blanc 5,58. Une face à 0,7 donne
   (146, 213, 32) pour le fluo et (178, 213, 209) pour le blanc.
   ========================================================================= */
.etapes__couche {
  --plaque: var(--c-white);
  /* ⚠️ LE DÉGRADÉ DE PROFONDEUR — « en mettant la couche au dessus plus opaque que la
     couche en dessous » (Hugo, 25/08/2026). Le rang est posé en ligne par le gabarit
     (`--rang`, 0 pour la plaque du dessus) : c'est une donnée SUR l'élément, comme le
     `--shape-ratio` de partials/forme.njk, et non un compte recopié dans la feuille.
     Deux conséquences heureuses — le sommet vaut toujours 1 quel que soit le nombre de
     plaques, et il n'y a ni boucle Sass ni `:nth-child` à borner ici.

     À cinq plaques : 1 / 0,88 / 0,76 / 0,64 / 0,52 du haut vers le bas.

     ⚠️ C'EST BIEN `opacity` SUR LE GROUPE, et non un `fill-opacity` par tracé : le
     groupe se compose d'un bloc, donc le modelé interne de la plaque (tranche franche,
     dessus voilé, liseré) garde ses rapports au lieu de se rebattre à chaque rang.

     L'animation d'arrivée écrit `opacity: 0` avec une spécificité supérieure, donc
     elle gagne tant que la plaque est attendue, puis la plaque revient à SON rang. */
  opacity: calc(1 - var(--rang, 0) * 0.12);
}

/* ---------- LES CINQ NUANCES ----------
   Trois blancs et deux verts. Ce sont les COULEURS ; la SÉQUENCE — quelle plaque
   porte laquelle — est dans blocs/etapes.njk, avec les contraintes qu'elle respecte.

   ⚠️ LES VERTS SONT LE FLUO DU SITE, PAS UNE COULEUR À PART. Hugo dit « vert » de ce
   #d1ff00, qui est un jaune-vert : les nuances se fabriquent donc en le mêlant au
   blanc, jamais en allant chercher une autre teinte. Même procédé que les séries
   secondaires de partials/motif.njk, mêlées à leur fond pour reculer sans changer de
   couleur.

   ⚠️ ET ON NE MÊLE PAS AU FOND ICI, contrairement au motif : le vert du pilier est
   déjà sous la plaque, et l'opacité de groupe (voir plus haut) le fait remonter à
   travers. Mêler une seconde fois au vert donnerait une nuance qui s'éteint deux
   fois. Les nuances vont donc vers le BLANC, et c'est l'opacité qui donne la
   profondeur.

   Le blanc chaud est le crème du site — un vrai autre blanc, pas un blanc pâli. Il
   reste bien le crème dans la boîte : `.etapes__boite--dictionnaire` réaffecte
   `--bg-page`, pas les jetons de marque. */
.etapes__couche--blanc {
  --plaque: var(--c-white);
}

.etapes__couche--blanc-chaud {
  --plaque: var(--c-base-main);
}

/* Un blanc à peine verdi — (250, 255, 224). Il sert de voisin discret à un vert. */
.etapes__couche--blanc-vert {
  --plaque: color-mix(in srgb, var(--c-white) 88%, var(--c-fluo));
}

.etapes__couche--vert {
  --plaque: var(--c-fluo);
}

/* Un fluo éclairci — (225, 255, 89). Il tient encore la famille du vert là où
   l'opacité l'a déjà affaibli. */
.etapes__couche--vert-pale {
  --plaque: color-mix(in srgb, var(--c-fluo) 65%, var(--c-white));
}

/* Les trois nuances d'une même plaque, et c'est là que sont les « nuances de
   blancs » : la tranche franche, le dessus voilé qui laisse passer le vert, le
   liseré entre les deux. Les opacités sont celles de l'export — c'est son modelé,
   seule la teinte change. */
.etapes__couche [data-role=socle] {
  fill: var(--plaque);
  fill-opacity: 1;
}

.etapes__couche [data-role=face] {
  fill: var(--plaque);
  fill-opacity: 0.7;
}

.etapes__couche [data-role=lisere] {
  fill: none;
  stroke: var(--plaque);
  stroke-opacity: 0.85;
}

/* Les guides restent au fluo de l'export, déclaré ici pour que toute l'encre du
   dessin vive au même endroit. À 0,48 d'épaisseur et 20 % de trait, c'est un
   chuchotement — ils cadrent la pile, ils ne la disputent pas. */
.etapes__guides path {
  stroke: var(--c-fluo);
}

/* =========================================================================
   L'EMPILEMENT DES PLAQUES — le dessin « Auditable » se monte au défilement.

   Demandé par Hugo le 25/08/2026 : « au scroll on ait les layers de l'image qui
   viennent s'empiler les unes après les autres ». Une plaque par palier, EN
   PARTANT DE LA BASE — c'est ainsi qu'une pile se monte, et la plaque du dessous
   est la dernière du document (voir l'en-tête de lib/couches.js).

   ⚠️ LE DÉFILEMENT NE PILOTE PAS L'ANIMATION, IL LA DÉCLENCHE. Même parti que le
   schéma du bloc `chaine`, et c'est ce qu'Hugo a accepté explicitement — « quitte
   à ce que le scroll lance une animation préparée et que ce soit pas le scroll
   exact qui définisse tout ». assets/js/etapes.js ne publie qu'un INDEX DE
   PALIER ; toute la durée et toute la courbe sont ici. Corollaire assumé : un
   coup de molette qui saute deux paliers joue les deux arrivées ensemble.

   ⚠️ ON N'ÉCRIT QUE L'ÉTAT EN ATTENTE, jamais l'état arrivé. La pile COMPLÈTE est
   le défaut, et elle ne se replie que sous `[data-etapes-actif]` : sans
   JavaScript, en mouvement réduit ou sur une erreur de chargement, le dessin est
   entier et rien ne manque. C'est le contrat de cet attribut, décrit dans
   assets/js/etapes.js.

   ⚠️ LE COMPTE SE FAIT DEPUIS LA FIN, ET C'EST CE QUI ÉVITE DE PUBLIER LE NOMBRE
   DE PLAQUES. `data-etapes-actif` vaut -1 au palier de départ (blocs à prologue),
   puis 0, 1, 2… Au palier `i`, il en est arrivé `i + 1`, toutes prises par le bas :
   les plaques en attente sont donc celles qui sont à plus de `i + 1` rangs de la
   fin, ce que `:nth-last-child(n + i + 2)` dit exactement — sans que ni le nombre
   de plaques ni le nombre d'étapes n'apparaisse nulle part.

     i = -1  →  n + 1  →  toutes en attente, la pile est vide
     i =  0  →  n + 2  →  la base est là
     i =  1  →  n + 3  →  les deux du bas
     i =  2  →  n + 4  →  les trois du bas
     i =  3  →  aucun sélecteur ne matche, la pile est complète

   La boucle va jusqu'à 8 pour qu'une section plus longue n'ait rien à écrire de
   plus. Au-delà, il faudra monter cette borne — un palier sans sa règle laisserait
   la pile figée à son état précédent, sans le moindre message.

   ⚠️ IL FAUT AU MOINS AUTANT DE PALIERS QUE DE PLAQUES, sinon les plaques du haut
   n'arrivent jamais. Quatre plaques pour cinq étapes ici : la pile est complète au
   quatrième palier, et la dernière étape se lit devant le dessin entier.
   ========================================================================= */
.etapes__couche {
  /* `translate` et non `transform` : la propriété individuelle se compose avec un
     `transform` venu d'ailleurs au lieu de vivre dedans — c'est la règle du site
     depuis que html.reveal pose `transform: none` sur ce qu'il a révélé. Les `px`
     d'un SVG sont des unités de la viewBox : 22 sur 245, soit environ un tiers de
     l'écart entre deux plaques. Assez pour voir la plaque tomber, pas assez pour
     qu'elle empiète sur le logement de sa voisine. */
  transition: opacity var(--dur-reveal) var(--ease-editorial), translate var(--dur-reveal) var(--ease-editorial);
}

[data-etapes-actif="-1"] .etapes__couche:nth-last-child(n+1) {
  opacity: 0;
  /* Elle vient d'AU-DESSUS : une plaque se pose sur la pile, elle n'en sort pas. */
  translate: 0 -22px;
}

[data-etapes-actif="0"] .etapes__couche:nth-last-child(n+2) {
  opacity: 0;
  /* Elle vient d'AU-DESSUS : une plaque se pose sur la pile, elle n'en sort pas. */
  translate: 0 -22px;
}

[data-etapes-actif="1"] .etapes__couche:nth-last-child(n+3) {
  opacity: 0;
  /* Elle vient d'AU-DESSUS : une plaque se pose sur la pile, elle n'en sort pas. */
  translate: 0 -22px;
}

[data-etapes-actif="2"] .etapes__couche:nth-last-child(n+4) {
  opacity: 0;
  /* Elle vient d'AU-DESSUS : une plaque se pose sur la pile, elle n'en sort pas. */
  translate: 0 -22px;
}

[data-etapes-actif="3"] .etapes__couche:nth-last-child(n+5) {
  opacity: 0;
  /* Elle vient d'AU-DESSUS : une plaque se pose sur la pile, elle n'en sort pas. */
  translate: 0 -22px;
}

[data-etapes-actif="4"] .etapes__couche:nth-last-child(n+6) {
  opacity: 0;
  /* Elle vient d'AU-DESSUS : une plaque se pose sur la pile, elle n'en sort pas. */
  translate: 0 -22px;
}

[data-etapes-actif="5"] .etapes__couche:nth-last-child(n+7) {
  opacity: 0;
  /* Elle vient d'AU-DESSUS : une plaque se pose sur la pile, elle n'en sort pas. */
  translate: 0 -22px;
}

[data-etapes-actif="6"] .etapes__couche:nth-last-child(n+8) {
  opacity: 0;
  /* Elle vient d'AU-DESSUS : une plaque se pose sur la pile, elle n'en sort pas. */
  translate: 0 -22px;
}

[data-etapes-actif="7"] .etapes__couche:nth-last-child(n+9) {
  opacity: 0;
  /* Elle vient d'AU-DESSUS : une plaque se pose sur la pile, elle n'en sort pas. */
  translate: 0 -22px;
}

[data-etapes-actif="8"] .etapes__couche:nth-last-child(n+10) {
  opacity: 0;
  /* Elle vient d'AU-DESSUS : une plaque se pose sur la pile, elle n'en sort pas. */
  translate: 0 -22px;
}

/* =========================================================================
   MODE ÉTROIT — L'EMPILEMENT ANIMÉ DISPARAÎT.

   Demandé par Hugo le 27/08/2026 : « tu peux enlever en mode mobile l'illustration
   animée ». Le desktop ne bouge pas d'un pixel : tout ce qui suit vit dans les media
   queries.

   ⚠️ `display: none` NE COÛTE RIEN ICI, et c'est le dessin lui-même qui le dit : il
   est `aria-hidden` dans le gabarit parce que l'empilement ne raconte rien que le
   titre et les cinq entrées ne disent déjà. Rien ne quitte l'arbre d'accessibilité.
   Et l'arrivée des plaques étant portée par les sélecteurs `[data-etapes-actif]` de
   cette feuille, assets/js/etapes.js n'a rien à savoir de cette disparition : il
   continue de publier son palier, plus personne ne le lit.

   ⚠️ LA COLONNE PART AVEC LUI, ET IL LE FAUT. La piste du glyphe est en `auto` : le
   masquer la réduit à zéro, mais laisse la GOUTTIÈRE de la grille — une bande morte
   d'une quarantaine de pixels le long du bord droit de la boîte. La boîte repasse
   donc à une seule colonne dans la même requête, ce que le bloc `md` plus bas faisait
   déjà et dont la ligne est devenue redondante.

   ⚠️ LE SEUIL EST `lg`, comme pour `.etapes__motif` : c'est la bascule
   mobile/desktop du site, et déjà celui des décors de ce bloc. Le dépilement du `duo`
   reste à `md` — il concerne le titre et sa boîte, pas le décor. */
@media (max-width: 60rem) {
  .etapes__glyphe {
    display: none;
  }
  .etapes__boite--scroll {
    grid-template-columns: minmax(0, 1fr);
  }
}
/* Une seule colonne : le duo se dépile. La boîte, elle, l'est déjà depuis `lg`
   ci-dessus, en même temps que son glyphe. */
@media (max-width: 56.25rem) {
  .etapes__duo {
    grid-template-columns: minmax(0, 1fr);
  }
}
/* La règle `.etapes__intro` a été retirée le 18/08/2026 avec son enveloppe : le
   texte et le visuel ne partagent plus de colonne. Le texte est maintenant un
   enfant direct de `.etapes__libre`. */
.etapes__intro-texte {
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
  font-size: var(--fs-base);
  line-height: var(--lh-normal);
  color: var(--text-strong);
}

.etapes__intro-texte p {
  margin: 0;
}

/* ⚠️ `.etapes__intro-visuel` a été retirée le 18/08/2026, en même temps que le
   placeholder du mode liste. La maquette porte bien un visuel à cet endroit — les
   losanges empilés de Figma 174:9430 — mais la disposition retenue n'a plus de place
   pour lui, et c'est le SVG « Auditable » qui occupe le flanc du scroll. */
/* La suite du texte, sous le visuel. Même traitement que le chapeau au-dessus : le
   corps de texte du site ne passe jamais par --text-muted. */
.etapes__intro-suite {
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
  font-size: var(--fs-base);
  line-height: var(--lh-normal);
  color: var(--text-strong);
}

.etapes__intro-suite p {
  margin: 0;
}

/* ---------- Les entrées ----------
   ⚠️ AUCUNE N'EST MASQUÉE, JAMAIS. C'est tout le propos de ce mode : le texte de
   chaque étape reste lisible quelle que soit la position du défilement. Ne pas
   ajouter d'`opacity: 0` ni de `display: none` ici — ce serait revenir au mode
   panneaux par la petite porte. */
.etapes__liste {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: clamp(var(--sp-4), 1.6vw, var(--sp-6));
}

.etapes__entree {
  display: flex;
  gap: var(--sp-4);
  /* ⚠️ C'EST L'ENTRÉE INACTIVE QUI RÉTRÉCIT, PAS L'ACTIVE QUI GROSSIT — même
     raisonnement que le sommaire du mode panneaux, et pour la même raison
     décisive : la place est réservée au GRAND état pour toutes les entrées, donc
     le repli du texte est calculé une fois pour toutes et la hauteur de la liste
     ne bouge jamais. Faire grossir l'active pousserait les suivantes à chaque
     cran de défilement, dans une section épinglée où l'œil fixe la liste.

     `transform` et non `font-size` : la taille change sans relayout, donc sans
     aucun risque de repli différent entre les deux états.

     ⚠️ 0.84 ET NON 0.94. Le premier jet était à 0.94, soit 6 % d'écart : le
     défilement changeait bien d'entrée, mais l'écart était trop faible pour qu'on
     le voie — au point de passer pour un scroll qui ne fonctionne pas. Le sommaire
     du mode panneaux descend à 0.68 ; on reste plus haut ici parce que les entrées
     portent un PARAGRAPHE et non un simple intitulé, et qu'à 0.68 il devient
     pénible à lire. Ne pas remonter au-dessus de 0.9 : l'effet redevient invisible. */
  transform: scale(0.84);
  transform-origin: left center;
  opacity: 0.5;
  transition: transform var(--dur-base) var(--ease-out), opacity var(--dur-base) var(--ease-out);
}

/* L'entrée lue : à sa taille pleine et à pleine opacité. L'opacité seule ne
   suffisait pas à la désigner — c'est le couple taille + opacité qui la détache,
   comme le sommaire du mode panneaux couple taille + couleur. */
.etapes__entree[aria-current=step] {
  transform: none;
  opacity: 1;
}

/* Le titre de l'étape lue passe au FLUO — demandé par Hugo le 18/08/2026.
   Il vise l'intitulé seul, et non `--etapes-accent` : cette variable colore aussi
   le numéro, et sur les entrées de CETTE boîte le numéro n'a pas à changer.

   ⚠️ CONTRASTE FAIBLE, ASSUMÉ : le fluo donne 2,25 sur le vert du pilier, là où le
   texte courant demande 4,5 et un gros titre 3,0. C'est le choix d'Hugo pour un
   repère de position, mais il ne tient PAS les seuils — l'information est aussi
   portée par la taille et l'opacité, qui elles restent nettes. Si tu veux à la fois
   le fluo et le contraste : passer le fluo en FOND du titre (un surlignage) avec
   l'encre sombre par-dessus, ce qui monte à 13,1. */
.etapes__entree[aria-current=step] .etapes__entree-nom {
  color: var(--c-fluo);
}

/* ⚠️ `.etapes__entree-num` a été retirée le 18/08/2026 avec les numéros qu'elle
   habillait : c'est le rail fluo à gauche de la liste qui dit maintenant où on en
   est. Voir `.etapes__rail` plus bas. */
/* ⚠️ L'ÉCART TITRE / TEXTE EST RESSERRÉ, et il ne doit pas suivre les tailles de
   police. Il valait --sp-2 (8px) quand le titre faisait 18px ; les titres étant
   passés à 22→30px le même jour, le blanc entre les deux paraissait un trou. Hugo
   l'a fait réduire le 18/08/2026.

   `0.15em` et non une valeur en pixels : l'écart suit ainsi le corps du titre. Un
   `--sp-*` figé se serait redésaccordé au prochain changement de taille. */
.etapes__entree-texte {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 0.15em;
}

/* Grossi le 18/08/2026 : 18→24px devient 22→30px. La boîte occupe deux tiers de la
   largeur et ne porte que cinq entrées, elle avait de la place à donner.

   ⚠️ CE GROSSISSEMENT MANGE LE BUDGET DE HAUTEUR, et la contrainte est réelle : la
   section est épinglée sur UNE hauteur d'écran, donc les cinq entrées doivent y
   tenir ensemble — elles sont toutes affichées en même temps dans ce mode. Si une
   sixième entrée arrive, ou si ces tailles montent encore, c'est la première chose à
   revérifier. */
.etapes__entree-nom {
  margin: 0;
  font-family: var(--font-serif);
  font-size: clamp(1.375rem, 1.05rem + 0.8vw, 1.875rem);
  font-weight: var(--fw-semibold);
  line-height: var(--lh-snug);
  color: var(--text-strong);
}

/* Corps de texte : --text-strong, jamais --text-muted. Règle du site, voir
   _tokens.scss. Le recul des entrées inactives est porté par l'opacité de
   l'entrée entière, pas par une encre plus pâle — sinon l'entrée active et les
   autres auraient deux traitements qui se cumulent. */
/* Grossi avec le titre : --fs-sm (15px figé) passe à --fs-base (16→18px fluide).
   --fs-base est l'échelle du corps de texte du site, donc ces descriptions se lisent
   maintenant comme n'importe quel paragraphe ailleurs. */
.etapes__entree-corps {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  font-size: var(--fs-base);
  line-height: var(--lh-normal);
  color: var(--text-strong);
}

.etapes__entree-corps p {
  margin: 0;
}

/* Une seule colonne : l'intro passe au-dessus de la liste. */
@media (max-width: 56.25rem) {
  .etapes--liste .etapes__suite {
    grid-template-columns: minmax(0, 1fr);
  }
}
/* =========================================================================
   LE RAIL FLUO — la « scrollbar » du mode liste.
   Demandé par Hugo le 18/08/2026, en remplacement des numéros devant les titres.

   Un rail continu et faible, et un pouce fluo qui marque l'entrée lue.

   ⚠️ IL EST HORS DE LA LISTE, ET C'EST LA CLÉ. Les entrées sont réduites par
   `transform: scale()` : un filet posé sur elles aurait été mis à l'échelle avec, et
   le rail serait devenu une suite de segments d'épaisseurs inégales. Le rail vit donc
   à côté, dans une grille à deux colonnes, et il ignore l'échelle des entrées.

   ⚠️ SES PROPORTIONS VIENNENT DE DEUX VARIABLES POSÉES PAR assets/js/etapes.js :
   `--etapes-n` (le nombre d'entrées) et `--etapes-progression` (la fraction
   correspondant à l'entrée lue). Le CSS ne fait que les combiner — il n'y a aucune
   hauteur d'entrée mesurée ici, ce qui rend le pouce indifférent à la longueur des
   textes.

   Les deux ont un REPLI : sans JavaScript, `--etapes-n` vaut 5 et la progression 0,
   donc le pouce se pose en haut du rail à une taille plausible au lieu de disparaître
   ou d'occuper tout le rail.
   ========================================================================= */
/* La respiration en haut et en bas du scroll, demandée par Hugo le 18/08/2026 : le
   rail et la première entrée touchaient le rembourrage de la boîte.

   ⚠️ ELLE EST SUR LE SCROLL ET NON SUR LA BOÎTE. Mise sur la boîte, elle aurait aussi
   décalé le glyphe, qui est son autre enfant et qui doit rester centré sur toute la
   hauteur. Ici seule la colonne rail + liste respire. */
.etapes__scroll {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: clamp(var(--sp-4), 1.6vw, var(--sp-6));
  padding-block: clamp(var(--sp-5), 2.4vw, var(--sp-10));
}

.etapes__rail {
  position: relative;
  inline-size: 3px;
  border-radius: var(--radius-full);
  /* Le rail est un voile de l'encre courante et non une couleur en dur : sur la
     boîte verte l'encre est noire, sur une boîte bleu nuit elle serait blanche. Il
     suit donc son fond sans règle supplémentaire. */
  background-color: color-mix(in srgb, var(--text-strong) 18%, transparent);
}

.etapes__rail-pouce {
  position: absolute;
  inset-inline: 0;
  border-radius: inherit;
  background-color: var(--c-fluo);
  /* Une fraction du rail, égale à la part d'une entrée. */
  block-size: calc(100% / var(--etapes-n, 5));
  /* La course disponible est la hauteur du rail MOINS celle du pouce — sans cette
     soustraction, le pouce sortirait du rail sur la dernière entrée.

     ⚠️ ET C'EST POURQUOI LA FRACTION PUBLIÉE DOIT ALLER JUSQU'À 1. assets/js/etapes.js
     divise l'index par `n - 1` et non par `n`, précisément parce que la course est déjà
     amputée ici. Les deux calculs vont ensemble : divisée par `n`, la fraction
     plafonnait à (n−1)/n et le pouce n'atteignait jamais le bas du rail — le défaut
     signalé par Hugo le 20/08/2026. */
  top: calc(var(--etapes-progression, 0) * (100% - 100% / var(--etapes-n, 5)));
  transition: top var(--dur-base) var(--ease-out);
}

/* ⚠️ ON COUPE LES TRANSITIONS, PAS LES ÉTATS. etapes.js ne crée aucun ScrollTrigger
   en mouvement réduit, donc `[data-etapes-actif]` n'existe jamais et la pile est
   complète d'elle-même. Ces règles ne servent qu'à un cas de bord : la préférence
   activée en cours de route, l'attribut déjà posé. */
@media (prefers-reduced-motion: reduce) {
  .etapes__rail-pouce,
  .etapes__couche {
    transition: none;
  }
}
/* =========================================================================
   LE MOTIF — un graphique en trame dans le coin BAS DROIT de la boîte, derrière
   le contenu. Demandé par Hugo le 26/08/2026.

   L'atome est partials/motif.njk, son apparence est dans _components.scss. Cette
   feuille ne pose que le PLACEMENT et les TEINTES — même partage que
   sections/_texte-panneau.scss et sections/_onglets-metiers.scss, les deux autres
   hôtes de cet atome.

   ⚠️ MODE `panneaux` UNIQUEMENT. Le gabarit ne rend rien en mode `liste` : la boîte
   n'y est plus que le tiers gauche de la section, et son coin bas droit porte déjà
   le glyphe empilé de `.etapes__glyphe`.
   ========================================================================= */
/* L'ENVELOPPE DE CLIP, ET C'EST LE CHOIX INVERSE DE `onglets-metiers`.

   Là-bas la boîte porte `overflow: hidden` elle-même, parce que rien n'en déborde.
   Ici quelque chose en déborde — non pas au repos, mais EN TRANSITION : sequence.js
   fait entrer et sortir chaque partie d'un panneau en `y` de ±90 px
   (`data-sequence-amplitude`) pendant qu'elle se fond. Le rembourrage bas de la boîte
   ne fait qu'une cinquantaine de pixels, donc une partie basse traverserait l'arête
   à une opacité où elle se voit encore, et un découpage posé sur la boîte la
   rognerait en pleine animation.

   `border-radius: inherit` plutôt que la valeur recopiée : le jour où l'arrondi de
   la boîte change, le clip suit tout seul. Même dispositif que
   `.texte-panneau__decor`. */
.etapes__decor {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  border-radius: inherit;
  pointer-events: none;
}

/* ⚠️ IL FAUT REMETTRE LE CONTENU AU-DESSUS. L'enveloppe est positionnée, donc elle
   peint par-dessus le contenu en flux normal quel que soit l'ordre du DOM.
   `z-index: -1` sur elle serait l'autre voie, mais elle enverrait le motif derrière
   le fond de la boîte, où il est simplement invisible — même piège que
   `.texte-panneau__inner`, `.onglets-metiers__inner` et le tracé de `.chemin`.

   Deux sélecteurs et non une enveloppe de plus : les deux enfants de la boîte en
   mode panneaux sont l'en-tête et la suite, et les envelopper casserait la grille
   que `.etapes__suite` établit. */
.etapes__entete,
.etapes__suite {
  position: relative;
  z-index: 1;
}

/* Le pied collé à l'arête basse, la taille prise sur la HAUTEUR de la boîte. C'est
   la formulation retenue sur les briques produit après trois essais écartés ; le
   pourquoi complet est dans _texte-panneau.scss, et il ne faut pas revenir sur ces
   deux points :

     • AUCUN `aspect-ratio` ici. Depuis que les viewBox de partials/motif.njk serrent
       l'encre, chaque dessin a SON rapport ; c'est `inline-size: auto` qui le lit.
     • AUCUN PLAFOND DE LARGEUR. Il laisserait un blanc DANS LE COIN, ce qui est
       précisément ce que l'ancrage au coin sert à éviter.

   ⚠️ 70 %, LA CIME DES QUATRE BRIQUES PRODUIT, ET SÛREMENT PAS LES 55 %
   D'`onglets-metiers`. Le premier jet était à 45 %, sur une hauteur de boîte
   surestimée : la section est épinglée sur une hauteur de fenêtre, mais LA BOÎTE NE
   L'EST PAS — `.etapes` est une grille en `align-content: center`, donc la boîte
   reste à la hauteur de son contenu, ~530 px sur /excel-sheets/. Elle est bien plus
   proche d'une brique produit que de la boîte d'onglets, et elle prend donc la même
   cime que les briques. Hugo, le 26/08/2026 : « tu peux grossir le chart ».

   LA MARGE QUI RESTE, mesurée sur la disposition de /excel-sheets/ à 1440 px : la
   colonne de texte s'arrête à ~550 px du bord gauche de la boîte, et le dessin
   n'entre par la droite que jusqu'à ~700 px. Le plafond avant qu'il ne passe sous le
   texte est autour de 85 % pour un rapport de 1,6 ; au-delà, du blanc posé sur sa
   série la plus claire ne donne que ~1,9. C'est `--motif-taille` qu'il faut régler si
   le dessin vient sous le texte, jamais un plafond de largeur — et un dessin plus
   LARGE (la jauge, à 2,00) avance plus vite vers le texte à hauteur égale.

   ⚠️ LE DÉFAUT EST DANS LE `var()`, JAMAIS DÉCLARÉ ICI. Une propriété posée SUR
   l'élément bat la même propriété héritée d'un ancêtre : écrite en déclaration, elle
   masquerait la valeur qu'une boîte poserait, sans la moindre alerte. Même règle que
   `--motif-base`. */
.etapes__motif {
  position: absolute;
  block-size: var(--motif-taille, 70%);
  inline-size: auto;
  inset-block-end: 0;
  inset-inline-end: 0;
  /* ⚠️ IL SORT UN PEU PAR LA DROITE — « décale un peu le chart de fond à droite, il
     colle trop le texte » (Hugo, 26/08/2026). Le centrage de la rangée, le même jour,
     a fait avancer la colonne de texte d'environ 215 px vers le milieu : mesurée sur
     /excel-sheets/ à 1383 px, elle finissait à ~658 px du bord gauche de la boîte
     quand le dessin entrait par la droite jusqu'à ~646 px. Ils se chevauchaient.

     ⚠️ LA SORTIE EST UNE PART DU DESSIN, PAS UNE COTE DE LA BOÎTE — même raison que
     pour `.texte-module__motif--gauche`, où le pourquoi est écrit en entier. Un
     `inset-inline-end` négatif en pourcentage se résoudrait sur la LARGEUR DE LA
     BOÎTE, alors que la largeur du dessin est intrinsèque (`inline-size: auto`) et
     donc inconnue de la feuille ; les pourcentages de `translate`, eux, se résolvent
     sur la boîte de l'ÉLÉMENT. Grossir le dessin décale donc la sortie avec lui, et
     le dégagement du texte ne change pas.

     ⚠️ ET `translate` ET NON `transform`, comme partout ailleurs sur le site : deux
     propriétés séparées qui se COMPOSENT, donc un `transform` venu d'ailleurs ne peut
     pas l'écraser.

     10 % vaut ~65 px sur la disposition mesurée, ce qui porte l'arête gauche du
     dessin à ~711 px — juste sous le bord gauche de l'illustration, donc ~50 px de
     dégagement avec le texte. Ce qui sort par la droite est rogné par
     `.etapes__decor` : depuis que les viewBox de partials/motif.njk serrent l'encre,
     c'est de l'ENCRE qu'on coupe, et c'est assumé — un décor en trame coupé net par
     une arête est l'idiome du site. Au-delà d'un quart, la colonne de droite de la
     matrice y passerait entièrement.

     Le défaut est dans le `var()` et jamais en déclaration, comme `--motif-taille` et
     `--motif-base` : une boîte qui voudrait un autre réglage doit pouvoir le poser. */
  translate: var(--motif-sortie-inline, 10%) 0;
}

/* ---------------------------------------------------------------
   LES TEINTES.

   Le fond est celui de la boîte, et jamais un jeton écrit en clair : c'est lui que
   _components.scss mélange à la base pour faire RECULER les deux séries secondaires.

   ⚠️ LES DEUX VALEURS SONT RECOPIÉES TELLES QUELLES DE LA BRIQUE « INTÉGRATIONS » DE
   /produit/ — même teinte vive (#f0a22e), même fond (#ab4a00), donc rien à
   recalculer, et deux valeurs différentes pour un même pilier auraient été un défaut.
   La teinte vive ne peut pas servir seule : posée sur le fond sombre du pilier elle
   ne pèse que 2,67, une nuance de son propre fond. Relevée de 6 % vers le blanc, les
   trois séries donnent 2,80 / 2,28 / 1,76 — la cible commune aux quatre piliers, qui
   est un PLAFOND et pas un goût (voir _texte-panneau.scss).

   Les deux autres fonds du bloc n'en posent pas, et c'est correct : sur le bleu nuit
   comme sur le vert du dictionnaire, le repli fluo de l'atome tient déjà (11,54 et
   4,79). Le jour où l'un d'eux voudrait la teinte de son pilier, la proportion est
   écrite verbatim dans _texte-panneau.scss. */
.etapes__boite--integrations {
  --motif-fond: var(--c-pilier-integrations-sombre);
  --motif-base: color-mix(in srgb, var(--c-pilier-integrations) 94%, var(--c-white));
}

/* ⚠️ RIEN ENTRE `md` ET `lg`. La boîte y perd sa largeur mais garde sa RANGÉE — le
   texte à gauche, le visuel à droite — donc le coin bas droit n'est plus qu'une bande
   de rembourrage : le motif s'y réduirait à un éclat de dessin le long de deux arêtes,
   et ce qu'il en resterait passerait sous le texte. Le décor perd sa prémisse plutôt
   que de se dégrader — même parti que `.onglets-metiers__motif` et que le tracé de
   `.chemin`, cachés à la même largeur pour la même raison.

   ⚠️ IL REVIENT SOUS `md`, AUTREMENT POSÉ : voir le bloc suivant. C'est la mise en
   COLONNE qui lui rend une prémisse, et c'est elle qui bascule à `md`. */
@media (max-width: 60rem) {
  .etapes__motif {
    display: none;
  }
}
/* =========================================================================
   MODE ÉTROIT — LE MOTIF QUITTE LE COIN POUR LE BAS DE LA BOÎTE, SUR TOUTE SA
   LARGEUR.

   Demandé par Hugo le 27/08/2026 pour /excel-sheets/ : « mettre un chart halftone
   en fond centré et collé au bas de la boîte », puis « fais en sorte que le graphe
   soit sur toute la largeur du bloc ». Le desktop ne bouge pas d'un pixel : tout ce
   qui suit vit dans cette media query.

   ⚠️ CE BLOC SUIT CELUI D'AU-DESSUS, ET IL LE FAUT. Les deux règles ont la même
   spécificité : c'est l'ordre du fichier qui rend le motif sous `md`, où les deux
   media queries s'appliquent. Ne pas les intervertir.

   ⚠️ C'EST LA LARGEUR QUI COMMANDE ICI, ET LA HAUTEUR QUI EN DÉCOULE — l'inverse
   exact de la règle desktop, et c'est le même raisonnement : sur une colonne,
   l'ancrage au coin n'a plus d'objet, puisqu'il n'y a plus de colonne de texte EN
   REGARD à protéger — le texte est AU-DESSUS. Même formulation que
   `.texte-panneau--boite .texte-panneau__motif` sous `lg`, où le pourquoi est écrit
   en entier ; on n'en donne toujours QU'UN CÔTÉ, sans quoi le cadrage « meet » du
   SVG ferait flotter l'encre au milieu de sa boîte.

   Conséquence : `--motif-taille` (la part de la HAUTEUR) devient inerte dans ce
   mode. Il reste nécessaire au large, ne pas le retirer.

   ⚠️ ET `translate` DOIT ÊTRE ANNULÉ, sinon la pleine largeur est fausse. La règle
   desktop décale le dessin de 10 % de SA PROPRE largeur vers la droite pour dégager
   la colonne de texte ; à pleine largeur ce décalage sort un dixième du dessin de la
   boîte, où `.etapes__decor` le coupe — un dixième de matrice perdu, sans alerte.

   ---------------------------------------------------------------------------
   LE PLAFOND DE HAUTEUR, ET SON ARBITRAGE — la seule cote vraiment discutable de
   ce bloc.

   La hauteur découlant de la largeur, elle GRANDIT avec la fenêtre quand la hauteur
   de la boîte, elle, reste stable et que le texte, lui, se replie sur moins de
   lignes. Mesuré sur /excel-sheets/, dessin à pleine largeur puis place libre sous
   le texte de l'étape 1 — la plus longue des trois, donc celle qui descend le plus
   bas :

     fenêtre     360    390    412    500    700    880
     dessin      192    209    222    273    389    494
     place libre 177    178    178    196    198    240

   Sans plafond, le dessin remonterait donc sous TOUT le texte dès 500 px (77 px de
   recouvrement), 191 px à 700 et 254 px à 880 — et du blanc posé sur sa série la
   plus présente ne donne que ~1,9.

   ⚠️ ICI LA PLEINE LARGEUR ET LE DÉGAGEMENT DU TEXTE SONT INCOMPATIBLES, et c'est
   la différence avec les briques de /produit/, qui laissent 53 % de place libre là
   où cette boîte n'en laisse que 30 % : elle porte EN PLUS le titre de section, et
   le texte de l'étape 1 fait sept lignes. Le tableau le dit — une matrice à pleine
   largeur demande 36,5 % de la hauteur de boîte à 390 px pour 31 % de libre. Le
   plafond est donc réglé pour accorder la PLEINE LARGEUR sur toute la plage
   téléphone (jusqu'à ~410 px), ce qui a été demandé, et la dernière ligne de
   l'étape 1 traverse la PREMIÈRE RANGÉE du dessin — recouvrement relevé : 14 px à
   360 px de fenêtre, 32 à 390, 42 à 412, puis 25 à 500 et 23 à 700, où le plafond
   a commencé à mordre. C'est la rangée la plus RECULÉE de la matrice (six cases sur
   huit en série 3 ou 2, une seule en série 1), donc la moins coûteuse à traverser,
   et une seule ligne de texte est concernée — mais c'est bien un compromis, et
   c'est `--motif-plafond` qu'il faut baisser pour le reprendre : à 28 % le dessin
   dégage le texte partout, au prix de la pleine largeur.

   Au-delà de ~410 px le plafond mord, et le défaut qu'on vient de corriger ne
   revient PAS : quand la hauteur devient contraignante, `meet` met l'encre à
   l'échelle sur la HAUTEUR — elle remplit exactement la boîte, donc reste collée à
   l'arête basse, et se centre horizontalement. On perd de la largeur (81 % de la
   boîte à 500 px, 57 % à 700, 47 % à 880), jamais le contact avec le bas.
   ========================================================================= */
@media (max-width: 56.25rem) {
  .etapes__motif {
    display: block;
    inline-size: 100%;
    block-size: auto;
    /* Un inset PAR AXE et jamais les deux : avec une largeur définie, contraindre
       les deux arêtes rendrait la déclaration sur-contrainte et l'une des deux
       serait ignorée en silence. Le centrage, lui, est celui du SVG (`meet`) dès
       que le plafond mord — il n'y a rien à écrire pour lui. */
    inset-inline-start: 0;
    inset-inline-end: auto;
    translate: none;
    /* ⚠️ LE DÉFAUT EST DANS LE `var()`, comme les trois autres pommeaux de cet
       atome : une boîte qui voudrait un autre plafond doit pouvoir le poser. */
    max-block-size: var(--motif-plafond, 40%);
  }
}
/* =========================================================================
   BLOC « chaine » — blocs/chaine.njk.
   Origine : la section 05 de la page du connecteur IA, Figma 141:7391.

   Le titre et ses trois entrées à gauche, la chaîne à droite. La section
   s'épingle, et chaque palier de défilement ALLUME un maillon — le moteur est
   le mode liste d'assets/js/etapes.js, emprunté tel quel.

   ⚠️ LA CHAÎNE EST ENTIÈREMENT POSÉE DÈS LE DÉPART, EN FANTÔME, et c'est ce qui
   rend le centrage gratuit : la composition ne se déplace jamais d'un pixel, donc
   elle reste centrée face au bloc de texte sans qu'aucune hauteur ne soit
   calculée. L'alternative — n'afficher que les maillons atteints — recomposait la
   colonne à chaque palier. Le pourquoi complet est en tête du gabarit.

   ⚠️ L'ÉTAT PAR DÉFAUT EST LA CHAÎNE ALLUMÉE, et l'extinction ne s'applique que
   sous `[data-etapes-actif]`. Sans JavaScript, en mouvement réduit ou sur une
   erreur de chargement, le schéma est donc complet et rien ne manque. C'est le
   sens de lecture obligatoire de ce fichier : on n'écrit jamais « allumé quand »,
   toujours « éteint tant que ».
   ========================================================================= */
.chaine {
  position: relative;
  /* Un écran plein, et il le faut : la section est épinglée en HAUT de la fenêtre
     (`start: "top top"` dans etapes.js). Toute hauteur SUPÉRIEURE mettrait son bas
     hors d'atteinte pendant l'épinglage — on ne peut pas défiler dans une section
     épinglée. Même construction que `.etapes` et `.sequence`.

     `svh` et non `vh` : sur mobile, `vh` compte la barre d'adresse rétractée, donc
     la section dépasse l'écran réellement visible. */
  min-height: 100svh;
  display: grid;
  align-content: center;
  padding-block: var(--sp-12);
  padding-inline: var(--container-pad);
  background-color: var(--bg-page);
  /* Les deux cotes du schéma, groupées ici parce que trois règles les lisent et que
     le contour de la forme est calculé À PARTIR de la première. Éparpillées, un
     changement de taille aurait laissé le liséré à son ancienne épaisseur. */
  --chaine-forme-w: clamp(4.5rem, 5.5vw, 6rem);
  /* Épaissi de 1px à 2px le 19/08/2026 à la demande d'Hugo : à 1px sur le violet, le
     tracé du cylindre se perdait — c'est le seul objet du schéma qui n'a pas d'aplat
     pour le porter. */
  --chaine-filet-epaisseur: 2px;
  /* La durée de l'arrivée d'un maillon, partagée par les TROIS mouvements qui la
     composent — le dépliage de la rangée, le fondu du contenu, le déroulé du trait.
     Une seule variable parce qu'ils doivent finir ensemble : à durées séparées, le
     trait arrivait avant que la place ne soit faite.

     Plus longue que --dur-reveal (520ms) : ce mouvement est la charge utile de la
     section, pas un effet d'entrée. C'est le défilement qui le DÉCLENCHE, il ne le
     pilote pas — voir l'avertissement en bas de ce fichier. */
  --chaine-dur: 640ms;
}

/* ---------- La boîte ----------
   Tous les blocs qui immobilisent la page en portent une — `.sequence__boite`,
   `.etapes__boite`. Demandé par Hugo le 19/08/2026, et ce n'est pas un habillage :
   c'est ce qui explique au lecteur pourquoi le défilement « colle » ici et pas
   ailleurs.

   Cotes des autres boîtes du site : 96rem, rayon 28px, aucun contour. Sur un aplat
   franc posé sur crème, le contraste fait déjà l'arête. Le rayon est écrit en dur
   comme dans les cinq autres — il n'existe pas de jeton à 28px, et un jeton serait
   maintenant franchement justifié.

   ⚠️ LE VIOLET EST ASSOMBRI DE 10 % vers le zodiac, exactement comme
   `.texte-module--connecteur`, `.face-a-face__colonne--sombre` et `--panneau-ton`
   dans _texte-panneau.scss. Le violet brut ne porte le blanc qu'à 4,14 — il manque
   0,36 au seuil de 4,5. Cinq endroits partagent maintenant ce calcul, ils doivent
   rester d'accord.

   ⚠️ ET C'EST POURQUOI --text-muted PASSE AU BLANC. Règle 11 de MAQUETTE.md :
   `.on-dark` cale ses rôles sur le bleu nuit (#202b5c), bien plus sombre. Sur ce
   violet le casper ne donne que 2,65, et aucune demi-mesure ne le sauve.

   --text-accent RESTE AU FLUO, seul des trois à ne pas être repris. Il porte ici le
   trait pointillé et l'intitulé de l'entrée lue, qui est en --fs-h3 (22→32px) : ses
   4,70 dépassent le seuil du texte courant, et pas seulement celui du grand texte.
   Le passer au blanc effacerait la seule chose qui distingue l'entrée lue des deux
   autres. */
.chaine__boite {
  width: 100%;
  max-width: 96rem;
  margin-inline: auto;
  border-radius: 1.75rem; /* 28px, comme les autres boîtes du site */
  padding-block: clamp(2rem, 1.2rem + 2.4vw, 3.75rem);
  padding-inline: clamp(1.5rem, -0.5rem + 8vw, 5.5rem);
  --violet-connecteur: var(--c-pilier-connecteur-sombre);
  background-color: var(--violet-connecteur);
  /* Le fond de la boîte devient le fond de référence : c'est lui que le cylindre
     recopie pour son ventre, et qu'un `.btn--secondary` recopierait pour son aplat.
     Sans ça le cylindre se remplirait du crème de la page. */
  --section-bg: var(--violet-connecteur);
  --bg-page: var(--violet-connecteur);
  --text-muted: var(--c-white);
  /* Le filet du cylindre. Un blanc voilé plutôt que le filet bleu clair du site,
     qui disparaîtrait sur le violet — et plutôt que du blanc pur, qui ferait du
     cylindre l'objet le plus contrasté du schéma alors qu'il en est la partie la
     moins fiable. Voir `.chaine__cylindre`. */
  --chaine-filet: rgb(255 255 255 / 45%);
  color: var(--text-strong);
}

/* ---------- En-tête ----------
   Le titre reste visible pendant tout l'épinglage : c'est l'intérêt d'épingler la
   section entière plutôt que de coller le seul schéma. Il ne déclare rien de sa
   police, _base.scss l'a déjà fait (règle 3 de MAQUETTE.md). */
.chaine__entete {
  margin-block-end: clamp(var(--sp-6), 2.4vw, var(--sp-10));
}

/* ⚠️ --text-strong ET NON --c-blue-zodiac, contrairement à tous les autres titres de
   section du site. Ce titre-ci est DANS la boîte violette : le bleu nuit y donnerait
   3,53, et aucune réaffectation de rôle ne l'atteindrait puisqu'une couleur écrite en
   clair ne se laisse pas repeindre. C'est la même correction que
   `.texte-module--sombre .texte-module__titre`. */
.chaine__titre {
  margin: 0;
  max-width: 34ch;
  color: var(--text-strong);
  text-wrap: balance;
}

/* --text-strong et non --text-muted : c'est du corps de texte. */
.chaine__texte {
  margin-block-start: var(--sp-4);
  max-width: 68ch;
  color: var(--text-strong);
}

.chaine__texte p {
  margin: 0;
  font-size: var(--fs-base);
  line-height: var(--lh-normal);
}

.chaine__texte p + p {
  margin-block-start: var(--sp-4);
}

/* ---------- La grille de la boîte ----------
   ⚠️ ELLE EST SUR LA BOÎTE, PAS SUR UNE ENVELOPPE INTERNE, et c'est ce qui centre le
   schéma « par rapport à la boîte entière et pas seulement le texte » — demande
   d'Hugo du 19/08/2026.

   Il y avait une enveloppe `.chaine__corps` autour de la liste et du schéma. Le
   schéma ne pouvait alors se centrer que sur la rangée de la LISTE : le titre
   au-dessus le poussait vers le bas, et il paraissait décalé de toute la hauteur de
   l'en-tête. Ici l'en-tête et la liste occupent les deux rangées de la colonne de
   gauche, et le schéma ENJAMBE les deux : son `align-self: center` se calcule donc
   sur leur total.

   ⚠️ `row-gap: 0`, et l'espacement vertical revient aux marges des éléments. C'est le
   même piège que dans les dispositions de `texte-module` : l'en-tête porte déjà sa
   marge basse, et une gouttière unique aurait creusé un trou de plus. */
@media (min-width: 60rem) {
  .chaine__boite {
    display: grid;
    grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
    column-gap: clamp(var(--sp-8), 3.2vw, var(--sp-16));
    row-gap: 0;
    align-items: start;
  }
  .chaine__entete {
    grid-column: 1;
    grid-row: 1;
  }
  .chaine__scroll {
    grid-column: 1;
    grid-row: 2;
  }
  /* Les deux rangées d'un coup, et centré dessus. C'est toute la manœuvre. */
  .chaine__schema {
    grid-column: 2;
    grid-row: 1/span 2;
    align-self: center;
  }
}
/* Sous le point de rupture, le schéma NE SE REND PAS DU TOUT — demande d'Hugo du
   27/08/2026, en mode mobile. Il s'empilait sous la liste, ce qui était le bon ordre
   de lecture (on lit d'abord ce qui est dit, on voit ensuite le dessin), et il portait
   pour cela une marge haute que la disposition à deux colonnes remettait à zéro. Les
   deux déclarations sont parties avec lui : plus rien à séparer.

   Il perd sa prémisse plutôt qu'il ne se dégrade : c'est un décor animé posé EN REGARD
   de la liste, calé en hauteur sur elle, et empilé dessous il devient une frise de
   trois maillons que l'on défile après avoir lu ce qu'ils illustrent. Même arbitrage
   que `.chemin` et que le motif d'`onglets-metiers` au même point de rupture.

   ⚠️ `display: none` ET NON `visibility`, et il faut le déclarer APRÈS le
   `display: flex` du schéma (même spécificité, seul l'ordre du fichier sépare les
   deux) — voir le bloc plus bas.

   ⚠️ RIEN N'EST RETIRÉ DU DOM : le schéma est déjà `aria-hidden` dans le gabarit, donc
   rien ne se perd à la lecture, et etapes.js ne le regarde pas — il apparie les
   paliers aux ENTRÉES de la liste. L'épinglage et le rail continuent d'opérer
   identiquement aux deux largeurs. */
/* ---------- Le rail de défilement ----------
   La « barre de scroll » à gauche de la liste, demandée par Hugo le 19/08/2026.
   Reprise de `.etapes__rail`, qui remplit le même office sur /dictionnaire/ : le
   pouce fait une fraction du rail, et sa position vient de `--etapes-progression`
   qu'etapes.js publie déjà. Aucune ligne de script en plus.

   ⚠️ LE COMPTE EST CELUI DES PALIERS, PAS DES ENTRÉES, et c'est pour ça
   qu'etapes.js publie `--etapes-n` en paliers depuis l'ajout du prologue. Avec trois
   entrées et un prologue, le rail a quatre crans : le pouce occupe donc un quart du
   rail et démarre en haut, pendant le palier où rien n'est encore allumé. Calé sur
   les entrées, il aurait fait un tiers du rail et serait sorti par le bas au
   dernier palier. */
.chaine__scroll {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: clamp(var(--sp-4), 1.6vw, var(--sp-6));
}

.chaine__rail {
  position: relative;
  inline-size: 3px;
  border-radius: var(--radius-full);
  /* Un voile de l'encre courante et non une couleur en dur : dans la boîte violette
     l'encre est blanche, sur un fond clair elle serait sombre. Le rail suit donc son
     fond sans règle supplémentaire — c'est le parti de `.etapes__rail`. */
  background-color: color-mix(in srgb, var(--text-strong) 18%, transparent);
}

.chaine__rail-pouce {
  position: absolute;
  inset-inline: 0;
  border-radius: inherit;
  background-color: var(--text-accent);
  /* Une fraction du rail, égale à la part d'un palier. */
  block-size: calc(100% / var(--etapes-n, 4));
  /* La course disponible est la hauteur du rail MOINS celle du pouce — sans cette
     soustraction, le pouce sortirait du rail au dernier palier.

     ⚠️ ET C'EST POURQUOI LA FRACTION PUBLIÉE DOIT ALLER JUSQU'À 1. assets/js/etapes.js
     divise le palier par `n - 1` et non par `n`, précisément parce que la course est
     déjà amputée ici. Les deux calculs vont ensemble — voir le même avertissement dans
     sections/_etapes.scss. */
  top: calc(var(--etapes-progression, 0) * (100% - 100% / var(--etapes-n, 4)));
  transition: top var(--dur-base) var(--ease-out);
}

/* ---------- Les entrées ----------
   Une liste ordonnée sans ses numéros : l'ordre porte du sens — les trois couches
   se superposent — mais il est déjà dit par le schéma d'à côté, et un numéro de
   plus ferait un troisième repère pour la même information.

   ⚠️ AUCUN FILET ENTRE LES ENTRÉES, comme partout ailleurs sur le site depuis le
   18/08/2026 : c'est l'espace qui sépare. */
.chaine__entrees {
  display: flex;
  flex-direction: column;
  gap: clamp(var(--sp-5), 2vw, var(--sp-8));
  margin: 0;
  padding: 0;
  list-style: none;
}

/* ⚠️ LES ENTRÉES NON LUES SONT ATTÉNUÉES, PAS MASQUÉES, et jamais fondues à zéro.
   C'est la règle du site : le mode liste d'`etapes` existe précisément parce que
   sequence.js masquait ce qu'il n'affichait pas. Ici tout reste lisible en
   permanence, et le défilement ne fait que désigner.

   L'atténuation ne s'applique QUE sous `[data-etapes-actif]` : sans JavaScript,
   les trois entrées sont à pleine encre, ce qui est le bon état de repli — aucune
   n'est « en cours » puisque rien ne défile. */
.chaine__entree {
  transition: opacity var(--dur-base) var(--ease-editorial);
}

/* ⚠️ 0,55 ET NON 0,45. Sur le crème, une entrée à 45 % reste franchement lisible ;
   sur le violet de la boîte, du blanc à 45 % descend sous le seuil du texte courant.
   L'atténuation doit rester une hiérarchie, pas un effacement. */
[data-etapes-actif] .chaine__entree {
  opacity: 0.55;
}

[data-etapes-actif] .chaine__entree[aria-current=step] {
  opacity: 1;
}

/* Le nom de l'entrée. Pas un titre de niveau : ces trois intitulés sont les termes
   d'une même énumération, et le `<h2>` de la section les couvre déjà. D'où le `<p>`
   et la police de titrage déclarée ici — c'est le même parti que
   `.etapes__entree-nom`. */
.chaine__entree-nom {
  margin: 0 0 var(--sp-2);
  font-family: var(--font-serif);
  font-size: var(--fs-h3);
  font-weight: var(--fw-semibold);
  line-height: var(--lh-snug);
  letter-spacing: var(--ls-tight);
  /* Même raison que le titre : on est dans la boîte, le bleu nuit n'y tient pas. */
  color: var(--text-strong);
}

/* L'entrée lue passe son intitulé à la teinte d'accent — donc au violet du pilier
   sur cette page. Le corps de texte, lui, ne change pas de couleur : seule
   l'opacité de l'entrée entière le distingue. */
[data-etapes-actif] .chaine__entree[aria-current=step] .chaine__entree-nom {
  color: var(--text-accent);
}

.chaine__entree-texte {
  margin: 0;
  max-width: 62ch;
  font-size: var(--fs-base);
  line-height: var(--lh-normal);
  color: var(--text-strong);
}

/* ---------- Mode mobile : UNE ENTRÉE À LA FOIS ----------
   Demande d'Hugo du 27/08/2026, dans la foulée du retrait du schéma juste au-dessus :
   « qu'on ne voie qu'un bloc à la fois lors du scroll ». Le bloc passe donc, SOUS LE
   POINT DE RUPTURE SEULEMENT, du modèle DÉSIGNER au modèle REMPLACER — les deux
   modèles de défilement du site. Le rendu large ne bouge pas d'un pixel.

   ⚠️ CE N'EST PAS UN CONFORT, LA SECTION DÉBORDAIT. Elle est épinglée en HAUT de la
   fenêtre (`start: "top top"`), donc tout ce qui passe sous le bas de l'écran est
   INATTEIGNABLE — on ne défile pas dans une section épinglée. Mesuré à 390 × 660 : la
   boîte laisse 524 px utiles, l'en-tête en prend 156 et les trois entrées empilées 460,
   soit 616 px pour 524 — la fin de la troisième entrée était hors d'atteinte. À
   360 × 640 il manquait 138 px. Dans une seule cellule, les entrées ne pèsent plus que
   la plus haute des trois (166 px) : 322 px pour 524, et le compte est large.

   ⚠️ TOUT EST CLOS DERRIÈRE `[data-etapes-actif]`, L'EMPILEMENT COMPRIS, et c'est ce
   qui préserve le contrat sans JavaScript du bloc : sans moteur — script absent,
   mouvement réduit — l'attribut n'est jamais publié, etapes.js sortant AVANT de le
   poser ; les entrées restent alors en colonne et les trois sont lisibles. Empilées
   sans masquage, elles se superposeraient. C'est mot pour mot le parti de
   `[data-onglets-actif]` dans `onglets-metiers`.

   ⚠️ UNE SEULE CELLULE DE GRILLE, ET NON DES HAUTEURS QUI S'OUVRENT ET SE FERMENT.
   Sous un épinglage la hauteur de la boîte est figée au moment où le défilement se
   verrouille : une entrée qui entrerait dans le FLUX ferait grandir la boîte à chaque
   palier et sauter la section entière — la technique `1fr → 0fr` est explicitement
   réservée aux arrivées HORS épinglage (voir CLAUDE.md). Ici la rangée se cale sur la
   plus haute des trois et ne bouge plus : le dispositif du panneau d'`onglets-metiers`.

   Les entrées y sont étirées et non centrées, donc tous les textes commencent à la
   même ligne : centrées, une entrée courte se décalerait verticalement de la
   précédente et le relais sauterait.

   ⚠️ `visibility` ET PAS SEULEMENT L'OPACITÉ : à l'opacité 0, trois paragraphes
   superposés restent dans l'arbre d'accessibilité et attrapables par la recherche dans
   la page, là où l'on ne voit rien. `visibility` les retire de la vue, du clavier, de
   la recherche et de l'arbre d'un seul coup — et elle ne se retire qu'À LA FIN du
   fondu, sans quoi l'entrée disparaîtrait d'un bloc.

   ⚠️ LE RELAIS EST SÉQUENCÉ, PAS UN FONDU CROISÉ. Dans une seule cellule, deux entrées
   qui se croisent superposent leurs deux textes : illisible pendant toute la
   transition. L'entrante attend donc que la sortante ait fini — d'où le délai égal à la
   durée. Le total (440 ms) reste sous --chaine-dur (640 ms), qui est la mesure du bloc.

   ⚠️ ET LE PROLOGUE GARDE LA PREMIÈRE ENTRÉE. Son palier ne surligne rien — c'est sa
   raison d'être, il donnait au cylindre son moment seul — mais le cylindre n'est plus
   là à cette largeur : masquer tout ce qui n'est pas courant y laisserait un quart du
   défilement sur une boîte vide. La première entrée y tient donc la place, à pleine
   encre, et le palier suivant la reprend telle quelle. Elle est désignée par « aucune
   entrée n'est courante » (`:not(:has())`) et non par `[data-etapes-actif="-1"]` : le
   -1 est un rouage du moteur, la phrase dit l'intention et vaut pour tout bloc qui
   réemploierait ce mode. */
@media (max-width: 60rem) {
  .chaine {
    /* Lue par les deux règles du relais, où elle sert de durée ET de délai. Écrite deux
       fois, un ajustement en aurait laissé une des deux moitiés en arrière. */
    --chaine-relais: 220ms;
  }
  [data-etapes-actif] .chaine__entrees {
    display: grid;
  }
  [data-etapes-actif] .chaine__entree {
    grid-area: 1/1;
    opacity: 0;
    visibility: hidden;
    transition: opacity var(--chaine-relais) var(--ease-editorial), visibility 0s linear var(--chaine-relais);
  }
  [data-etapes-actif] .chaine__entree[aria-current=step],
  [data-etapes-actif] .chaine__entrees:not(:has([aria-current])) .chaine__entree:first-child {
    opacity: 1;
    visibility: visible;
    transition: opacity var(--chaine-relais) var(--ease-editorial) var(--chaine-relais), visibility 0s linear var(--chaine-relais);
  }
}
/* =========================================================================
   LE SCHÉMA — la source, puis un maillon par entrée.
   ========================================================================= */
.chaine__schema {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}

/* Le masquage annoncé plus haut. Ici, et pas là-bas, pour passer après le
   `display: flex` ci-dessus. */
@media (max-width: 60rem) {
  .chaine__schema {
    display: none;
  }
}
/* ---------- La source ----------
   Toujours allumée : c'est le point de départ, il n'appartient à aucun palier.
   Son libellé est AU-DESSUS du cylindre, comme demandé. */
.chaine__source {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-3);
}

/* --text-muted, et ce n'est pas qu'une question de hiérarchie : la source est la
   donnée BRUTE, celle qui n'est pas encore fiabilisée. L'encre discrète dit la
   même chose que le titre de la section. C'est la rhétorique éteint / allumé du
   bloc `face-a-face` de cette même page. */
.chaine__source-nom {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--text-muted);
}

/* ---------- Le cylindre ----------
   Dessiné en CSS et non posé en image : ce n'est qu'un tonneau et une ellipse,
   donc il suit les jetons de couleur du site, se met à l'échelle sans flou et ne
   demande aucun export à réclamer.

   ⚠️ LE `border-radius` EST À DEUX RAYONS (`x / y`), et c'est ce qui fait le
   tonneau : 50 % en horizontal arrondit les flancs jusqu'au demi-cercle, 22 % en
   vertical les aplatit en ellipse. Un rayon unique aurait donné une pilule.

   L'ellipse du haut est un `::before` bordé, décalé d'un pixel de chaque côté pour
   couvrir exactement la bordure du tonneau — sans ce décalage, un liseré du fond
   apparaît entre les deux tracés. */
.chaine__cylindre {
  position: relative;
  display: block;
  width: clamp(3.5rem, 4.5vw, 4.75rem);
  aspect-ratio: 9/7;
  border: var(--chaine-filet-epaisseur) solid var(--chaine-filet, var(--filet-panneau-teinte));
  border-radius: 50%/22%;
  background-color: var(--section-bg);
}

.chaine__cylindre::before {
  content: "";
  position: absolute;
  inset: -1px -1px auto -1px;
  height: 44%;
  border: var(--chaine-filet-epaisseur) solid var(--chaine-filet, var(--filet-panneau-teinte));
  border-radius: 50%;
  background-color: var(--section-bg);
}

/* ---------- Un maillon ----------
   Le trait pointillé, puis le badge (ou la forme). Les deux vivent dans
   `.chaine__maillon-contenu` et non directement dans le maillon : c'est le maillon qui
   porte la grille du dépliage, et son unique enfant doit être l'enveloppe à
   débordement caché. Les deux règles sont en bas de ce fichier, avec l'apparition. */
/* ---------- Le trait pointillé ----------
   ⚠️ RÉVÉLÉ PAR `clip-path`, JAMAIS PAR `scaleY`. Une mise à l'échelle étire les
   pointillés : ils s'allongent au lieu d'avancer, et le motif n'a plus le même pas
   en haut et en bas de la chaîne. Le rognage découvre le motif à sa taille réelle,
   ce qui donne bien un trait qui « part » vers le bloc.

   Deux éléments et non un : l'enveloppe réserve la hauteur une fois pour toutes,
   donc rien ne bouge quand le trait se déroule. */
.chaine__trait {
  display: block;
  width: 2px;
  height: clamp(var(--sp-6), 2.4vw, var(--sp-10));
}

.chaine__trait-motif {
  display: block;
  width: 100%;
  height: 100%;
  /* Le pointillé en dégradé répété plutôt qu'en `border-dashed` : la bordure ne
     sait pas régler la longueur des tirets ni leur écart, et un tireté de
     navigateur change de pas d'un moteur à l'autre. */
  background-image: repeating-linear-gradient(to bottom, var(--text-accent) 0 4px, transparent 4px 8px);
}

/* ---------- Le badge d'un maillon ----------
   ⚠️ UN BADGE ET NON UN `.cadre`, changé par Hugo le 19/08/2026. Le `.cadre` est LE
   panneau fermé du site : il annonce un contenu qu'il enferme, et il porte pour ça
   des coins carrés et un filet bleu. Un maillon n'enferme rien, c'est une étiquette
   — d'où la pilule. La règle 2 de MAQUETTE.md ne s'applique donc plus ici.

   ⚠️ LES DEUX BADGES N'ONT PAS LA MÊME ANATOMIE, et c'est demandé :

     le premier   APLAT BLANC, sans contour. L'encre y est le bleu nuit, écrite en
                  clair — c'est une pastille claire dans une boîte sombre, elle
                  hériterait sinon du blanc de `.on-dark` et son libellé serait blanc
                  sur blanc, sans le moindre signal (règle 12 de MAQUETTE.md, branche
                  « une seule encre » ; précédent `.fiche-equipes__nom`).

     le second    APLAT VERT + CONTOUR BLANC. Le vert est celui de SON pilier et non
                  celui de la page : c'est ce qui nomme le dictionnaire sans un mot
                  de plus. Son encre reste SOMBRE — le vert est un fond clair malgré
                  sa couleur (5,40 pour --c-text-main, contre 2,63 pour du blanc),
                  c'est l'arbitrage de la table des contrastes en tête de
                  sections/_texte-panneau.scss.

   Le contour blanc du second n'est pas décoratif : sans lui, le vert posé sur le
   violet n'a que 1,86 de rapport de luminance avec son fond, et le badge se dissout
   dans la boîte. Le liséré lui redonne son arête. */
.chaine__badge {
  margin: 0;
  padding: var(--sp-2) var(--sp-4);
  border: 2px solid transparent;
  border-radius: var(--radius-full);
  background-color: var(--c-white);
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  line-height: var(--lh-snug);
  color: var(--c-blue-zodiac);
}

/* ⚠️ `border-color` ET NON `border` : l'épaisseur est déjà posée au-dessus, en
   transparent. La déclarer une seconde fois ici ferait grandir le badge de 4px par
   rapport à son voisin, alors que les deux doivent s'aligner. */
.chaine__maillon--dictionnaire .chaine__badge {
  background-color: var(--c-pilier-dictionnaire);
  border-color: var(--c-white);
  color: var(--c-text-main);
}

/* ---------- Le maillon en forme de marque ----------
   Le dernier de la chaîne : la forme au lieu du bloc bordé, son libellé dessous.
   La teinte vient de `forme--bleu`, donc de --c-blue, donc du violet du pilier sur
   cette page — rien à écrire ici.

   La grille à une seule cellule superpose la forme et le logo sans positionnement
   absolu : les deux occupent `1 / 1`, et `place-items: center` les cale. */
.chaine__forme {
  display: grid;
  place-items: center;
}

.chaine__forme-contour,
.chaine__forme-fond,
.chaine__logo {
  grid-area: 1/1;
}

/* La forme elle-même, au violet du pilier (`forme--bleu` lit --c-blue, que
   `.ton-page--connecteur` a passé au violet). */
.chaine__forme-fond {
  --shape-w: var(--chaine-forme-w);
}

/* ⚠️ LE CONTOUR EST LA MÊME FORME, EN DESSOUS ET PLUS GRANDE. Une bordure était
   impossible : la forme est un masque, donc tout ce qu'on lui donne est découpé par
   le masque et disparaît ; et un `outline` aurait dessiné le rectangle de la boîte,
   pas la silhouette.

   Les 8 % de surcroît donnent un liséré d'environ 2px au corps le plus petit et 2,5px
   au plus grand — il grossit donc avec la forme, ce qu'une épaisseur en pixels
   n'aurait pas fait. En dessous de 6 % le liséré se ferme par endroits sur les
   pointes du citron, où les deux tracés se rapprochent le plus. */
.chaine__forme-contour {
  --shape-w: calc(var(--chaine-forme-w) * 1.08);
  align-self: center;
  justify-self: center;
}

/* ⚠️ LE LOGO EST UN MASQUE, PAS UNE IMAGE. L'export du protocole MCP est un tracé
   NOIR ; posé en `<img>` il resterait noir sur la forme violette, à 3,01 de
   contraste. En masque, sa teinte vient des jetons du site.

   Le blanc donne 4,14 sur le violet du pilier. C'est sous le seuil de 4,5 du texte
   courant, et c'est accepté ici : le logo est DÉCORATIF (tout le schéma est
   `aria-hidden`) et le libellé « MCP Biron » juste dessous, lui, est à pleine
   encre sur le crème. Le même arbitrage que la coche du bloc `face-a-face`.

   `--logo` est posée en ligne par le gabarit : le nom du fichier vient des
   données. C'est le précédent de `--shape-src` dans partials/forme.njk. */
.chaine__logo {
  width: 45%;
  aspect-ratio: 1;
  background-color: var(--c-white);
  mask-image: var(--logo);
  mask-size: contain;
  mask-repeat: no-repeat;
  mask-position: center;
  -webkit-mask-image: var(--logo);
  -webkit-mask-size: contain;
  -webkit-mask-repeat: no-repeat;
  -webkit-mask-position: center;
}

/* Posé sur le violet et non dans un panneau : encre claire, comme le titre. */
.chaine__maillon-nom--dessous {
  margin: var(--sp-2) 0 0;
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  line-height: var(--lh-snug);
  color: var(--text-strong);
}

/* =========================================================================
   L'APPARITION — une rangée de grille qui passe de `0fr` à `1fr`.

   ⚠️ TROIS VERSIONS SE SONT SUCCÉDÉ LE 19/08/2026, ET LES DEUX PREMIÈRES SONT DES
   IMPASSES CONNUES. Ne pas y revenir sans lire pourquoi :

     1. les maillons pas encore atteints EN FANTÔME, à 22 % d'opacité. Refusé par
        Hugo : « faire apparaître totalement les éléments au fur et à mesure plutôt
        que de jouer sur leur opacité ». Un fantôme reste un objet qu'on voit, donc
        la chaîne était entièrement lisible dès le premier palier.

     2. `display: none`. L'élément apparaît franchement, mais son entrée dans la mise
        en page est INSTANTANÉE : ses voisins sautent d'un coup, et l'animation
        d'opacité posée dessus ne rattrapait rien puisque la place était déjà prise.
        C'est ce qu'Hugo a corrigé en demandant « une apparition smooth, et que la
        position s'adapte à l'apparition ».

     3. celle-ci. Le maillon est une GRILLE À UNE RANGÉE, et c'est la rangée qui
        s'anime : `1fr` → `0fr`. Sa hauteur passe donc continûment de sa hauteur
        naturelle à zéro, ce qui donne les deux choses demandées d'un coup —
        l'arrivée du maillon est douce, ET ses voisins se repositionnent en douceur
        puisque la place se libère progressivement.

   ⚠️ POURQUOI CETTE TECHNIQUE PLUTÔT QU'UNE HAUTEUR. On ne peut pas animer vers
   `height: auto`, et les maillons n'ont pas tous la même hauteur — un badge en pilule
   et une forme surmontée de son libellé ne mesurent pas pareil. Toute valeur écrite à
   la main serait fausse pour l'un des deux. Dans une grille auto, `1fr` résout à la
   hauteur du contenu : la valeur est donc mesurée par le navigateur, jamais devinée.

   ⚠️ L'ENVELOPPE INTERNE EST OBLIGATOIRE, avec `overflow: hidden` ET `min-height: 0`.
   Sans le débordement caché, le contenu dépasse de la rangée pendant qu'elle
   grandit ; sans `min-height: 0`, la rangée refuse de descendre sous la hauteur
   minimale de son contenu et ne se replie jamais.

   ⚠️ LE DÉFILEMENT NE PILOTE PAS L'ANIMATION, IL LA DÉCLENCHE. C'est ce qu'Hugo a
   accepté explicitement — « quitte à ce que le scroll lance une animation préparée et
   que ce soit pas le scroll exact qui définisse tout ». etapes.js ne publie qu'un
   INDEX DE PALIER, discret ; toute la durée et toute la courbe sont ici, en CSS. Le
   corollaire, assumé : un coup de molette qui saute deux paliers joue les deux
   arrivées ensemble.

   ⚠️ ON N'ÉCRIT QUE L'ÉTAT REPLIÉ, jamais l'état déployé : le déployé est le défaut,
   ce qui garantit un schéma complet sans JavaScript. Voir l'en-tête du fichier.

   ⚠️ LE COMPTE EST DÉCALÉ DE DEUX, et c'est la seule subtilité des deux boucles.
   `data-etapes-actif` vaut -1 au palier de départ, puis 0, 1, 2 — c'est un index
   d'ENTRÉE, publié par etapes.js. Les maillons, eux, sont les enfants 2, 3, 4 du
   schéma, la source étant le premier. Le maillon à replier pour un index `i` est donc
   l'enfant `i + 3` et tous ceux d'après :

     i = -1  →  n + 2  →  aucun maillon déployé, seule la source est là
     i =  0  →  n + 3  →  le premier se déploie
     i =  1  →  n + 4  →  les deux premiers
     i =  2  →  aucun sélecteur ne matche, la chaîne est complète

   Les boucles vont jusqu'à 5 maillons pour qu'un schéma plus long n'ait rien à écrire
   de plus. Au-delà, il faudra monter cette borne — un maillon sans sa règle serait
   déployé dès le premier palier, sans le moindre message.
   ========================================================================= */
.chaine__maillon {
  display: grid;
  grid-template-rows: 1fr;
  transition: grid-template-rows var(--chaine-dur) var(--ease-editorial);
}

.chaine__maillon-contenu {
  /* Les deux vont ensemble, voir l'avertissement ci-dessus. */
  overflow: hidden;
  min-height: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  /* L'opacité accompagne le dépliage plutôt que de le précéder : le maillon grandit
     et se révèle en même temps. Un fondu plus court laissait apparaître le contenu
     dans une rangée encore à moitié fermée. */
  transition: opacity var(--chaine-dur) var(--ease-editorial);
}

[data-etapes-actif="-1"] .chaine__maillon:nth-child(n+2) {
  grid-template-rows: 0fr;
}

[data-etapes-actif="-1"] .chaine__maillon:nth-child(n+2) .chaine__maillon-contenu {
  opacity: 0;
}

[data-etapes-actif="-1"] .chaine__maillon:nth-child(n+2) .chaine__trait-motif {
  clip-path: inset(0 0 100% 0);
}

[data-etapes-actif="0"] .chaine__maillon:nth-child(n+3) {
  grid-template-rows: 0fr;
}

[data-etapes-actif="0"] .chaine__maillon:nth-child(n+3) .chaine__maillon-contenu {
  opacity: 0;
}

[data-etapes-actif="0"] .chaine__maillon:nth-child(n+3) .chaine__trait-motif {
  clip-path: inset(0 0 100% 0);
}

[data-etapes-actif="1"] .chaine__maillon:nth-child(n+4) {
  grid-template-rows: 0fr;
}

[data-etapes-actif="1"] .chaine__maillon:nth-child(n+4) .chaine__maillon-contenu {
  opacity: 0;
}

[data-etapes-actif="1"] .chaine__maillon:nth-child(n+4) .chaine__trait-motif {
  clip-path: inset(0 0 100% 0);
}

[data-etapes-actif="2"] .chaine__maillon:nth-child(n+5) {
  grid-template-rows: 0fr;
}

[data-etapes-actif="2"] .chaine__maillon:nth-child(n+5) .chaine__maillon-contenu {
  opacity: 0;
}

[data-etapes-actif="2"] .chaine__maillon:nth-child(n+5) .chaine__trait-motif {
  clip-path: inset(0 0 100% 0);
}

[data-etapes-actif="3"] .chaine__maillon:nth-child(n+6) {
  grid-template-rows: 0fr;
}

[data-etapes-actif="3"] .chaine__maillon:nth-child(n+6) .chaine__maillon-contenu {
  opacity: 0;
}

[data-etapes-actif="3"] .chaine__maillon:nth-child(n+6) .chaine__trait-motif {
  clip-path: inset(0 0 100% 0);
}

[data-etapes-actif="4"] .chaine__maillon:nth-child(n+7) {
  grid-template-rows: 0fr;
}

[data-etapes-actif="4"] .chaine__maillon:nth-child(n+7) .chaine__maillon-contenu {
  opacity: 0;
}

[data-etapes-actif="4"] .chaine__maillon:nth-child(n+7) .chaine__trait-motif {
  clip-path: inset(0 0 100% 0);
}

[data-etapes-actif="5"] .chaine__maillon:nth-child(n+8) {
  grid-template-rows: 0fr;
}

[data-etapes-actif="5"] .chaine__maillon:nth-child(n+8) .chaine__maillon-contenu {
  opacity: 0;
}

[data-etapes-actif="5"] .chaine__maillon:nth-child(n+8) .chaine__trait-motif {
  clip-path: inset(0 0 100% 0);
}

/* ⚠️ EN MOUVEMENT RÉDUIT, ON COUPE LES TRANSITIONS MAIS PAS LES ÉTATS. etapes.js ne
   crée aucun ScrollTrigger dans ce cas, donc `[data-etapes-actif]` n'existe jamais et
   la chaîne est déployée d'un bout à l'autre. Ces règles ne servent donc qu'à un cas
   de bord : un utilisateur qui active la préférence en cours de route, alors que
   l'attribut est déjà posé. */
@media (prefers-reduced-motion: reduce) {
  .chaine__entree,
  .chaine__rail-pouce,
  .chaine__maillon,
  .chaine__maillon-contenu,
  .chaine__trait-motif {
    transition: none;
  }
}
/* =========================================================================
   BLOC « cas-clients » — blocs/cas-clients.njk, atome partials/cas.njk.

   Origine : les sections 02 et 03 de la page des cas clients — Figma 144:5858
   (« Cas mis en avant ») et 144:5903 (« Toutes les histoires »).

   Un seul bloc pour les deux, parce que c'est le même objet à deux échelles :
   la vedette pose le visuel à gauche et le texte à droite, la carte les empile.
   Le partage est celui du site — l'atome possède l'apparence, le bloc la place.

   ---------------------------------------------------------------------
   TOUT CE QUI PORTE UN CAS EST UNE CARTE BLANCHE ARRONDIE, et c'est un
   arbitrage d'Hugo du 21/08/2026 : « fais en sorte que toutes les cartes soient
   indépendantes, fond blanc avec des bords arrondis », et pour la vedette « met
   le cas dans un cadre blanc ». Le bloc rendait d'abord le quadrillage fermé que
   la maquette dessine — voir l'en-tête du gabarit pour ce que l'épisode a appris
   et pour la troisième branche ajoutée à la règle 13 de MAQUETTE.md.

   Les cotes de la carte sont réunies dans `--cas-carte-*` pour que la vedette et
   les quatre cartes ne puissent pas diverger : elles doivent se lire comme la même
   famille d'objets d'une section à l'autre.
   ========================================================================= */
.cas-clients {
  position: relative;
  padding-block: var(--sp-section);
  /* ⚠️ AUCUN FOND PROPRE : la section prend celui de la page, --c-blue-50 depuis
     le 21/08/2026. Un essai en --c-blue-100 posé ICI a été retiré le même jour,
     quand Hugo a fait passer le fond de page au bleu pâle : deux bleus voisins l'un
     sur l'autre se lisaient comme une erreur de calage, pas comme une intention.

     ⚠️ ET C'EST CE QUI A FAIT REVENIR LE FILET DES CARTES : le blanc #ffffff sur le
     --c-blue-50 #eff1fa ne donne que 1,05, donc une carte sans contour s'effaçait.
     Voir la règle du contour plus bas — l'une des deux sorties possibles, l'autre
     étant de donner à la section un fond d'un cran plus soutenu. Pas les deux. */
  background-color: var(--bg-page);
  /* ⚠️ 20px ET NON LES 28px DES BOÎTES DE SECTION — même valeur et même raison
     que les cartes de `module.cartes` : deux arrondis concentriques se
     concurrenceraient si la carte reprenait celui d'une boîte. */
  --cas-carte-rayon: 1.25rem;
}

/* Deux sections de cas qui se suivent ne doublent pas leur respiration : la
   maquette n'en met qu'une entre le cas mis en avant et la grille. Sans ça, les
   deux rembourrages s'additionnent et la page se casse en deux. */
.cas-clients + .cas-clients {
  padding-top: 0;
}

/* ⚠️ MÊME RAISON APRÈS LE BANDEAU CLIENTS (« supprimer un peu d'espace entre la
   boîte des clients et le cas mis en avant », 21/08/2026). Les deux sections posaient
   chacune une respiration de section pleine — 4 à 7,5rem — et elles s'additionnaient.
   Le vide restant est porté pour l'essentiel par le bas du bandeau, resserré lui
   aussi : voir sections/_clients.scss. */
.clients + .cas-clients {
  padding-top: clamp(var(--sp-6), 2vw, var(--sp-8));
}

/* =========================================================================
   L'EN-TÊTE — intitulés à gauche, filtres à droite.

   `align-items: end` cale la rangée de boutons sur le bas du titre, comme la
   maquette. En repli, `flex-wrap` la passe simplement dessous.
   ========================================================================= */
.cas-clients__entete {
  display: flex;
  flex-wrap: wrap;
  align-items: end;
  justify-content: space-between;
  gap: var(--sp-6);
  margin-block-end: clamp(var(--sp-8), 3vw, var(--sp-12));
}

/* ---------- Le badge de section ----------
   ⚠️ SON APPARENCE EST PARTIE DANS `.badge-section` (_components.scss) LE
   21/08/2026, quand `texte-module` a eu besoin du même objet pour les six libellés
   de bande de /cas-clients/hardloop/. L'aplat fluo, l'encre sombre et la mesure de
   contraste sont documentés là-bas ; ce bloc n'a AUCUN placement à ajouter — la
   marge haute de `.cas-clients__titre` juste dessous suffit à l'espacer.

   C'est pour ça qu'il n'y a plus de règle ici : une classe `.cas-clients__badge`
   sans déclaration serait du poids mort. Le gabarit ne pose donc que
   `.badge-section`. */
/* Le titre ne déclare rien de sa police : _base.scss pose déjà le titrage et la
   taille du niveau (règle 3 de MAQUETTE.md). --c-blue-zodiac et non
   --text-strong : ce bloc n'est jamais dans une boîte sombre, contrairement à
   `reperes`. */
.cas-clients__titre {
  margin: var(--sp-3) 0 0;
  max-width: 30ch;
  color: var(--c-blue-zodiac);
  text-wrap: balance;
}

/* Sans badge au-dessus, le titre n'a rien à décaler. */
.cas-clients__intitules > .cas-clients__titre:first-child {
  margin-block-start: 0;
}

/* ---------- Les filtres de secteur ----------
   ⚠️ LEUR APPARENCE EST PARTIE DANS `.filtres` / `.filtre` (_components.scss) LE
   21/08/2026, quand la grille d'articles du blog a eu besoin de la même barre.
   La rangée masquée tant que `data-filtres-actif` n'est pas posé, la pilule
   contournée, l'aplat de l'actif qui lit --btn-primary-bg : tout est là-bas, avec
   le raisonnement qui écarte de les rendre en `.btn`.

   Ce bloc n'a AUCUN placement à ajouter — `.cas-clients__entete` les cale déjà à
   droite du titre. C'est pour ça qu'il ne reste pas de règle ici : une classe
   `.cas-clients__filtres` sans déclaration serait du poids mort. */
/* =========================================================================
   LES DEUX PORTEURS DE CARTE.

   La vedette et les cellules de la grille partagent le fond et l'arrondi. Elles ne
   diffèrent que par leur rembourrage : la vedette est deux fois plus large, donc
   elle respire plus.
   ========================================================================= */
/* ⚠️ LE CHROME DE LA CARTE EST SUR L'`<article>`, PAS SUR SON ENVELOPPE, et c'est
   STRUCTUREL — voir le long avertissement du survol plus bas. L'enveloppe
   (`.cas-clients__cellule`, `.cas-clients__vedette`) porte `data-reveal`, donc
   sections/_reveal.scss lui impose son `transform` ET son `transition` à (0,3,1) de
   spécificité. Tant que le chrome vivait dessus, aucun survol ne pouvait l'animer.
   L'`<article>` est libre de ces deux règles.

   ⚠️ LE CONTOUR EST REVENU LE 21/08/2026 (« ajoute un contour sur les boîtes »),
   après avoir été retiré le matin même. Ce qui a changé entre les deux, c'est le fond
   de page : sur le --c-blue-100 d'alors, le blanc se détachait sans aide ; sur le
   --c-blue-50 retenu ensuite il ne donne plus que 1,05, et la carte s'effaçait. C'est
   la limite de la règle 1 de MAQUETTE.md — « aucun contour : le contraste fait
   l'arête » ne vaut que si le contraste est là.

   ⚠️ LE FILET BLEU DU SITE, PAS --border-subtle : c'est le même que les séparateurs
   de chiffres à l'intérieur des cartes. Un gris translucide dehors et un bleu dedans
   auraient donné deux filets pour un seul objet. */
.cas--vedette,
.cas--carte {
  background-color: var(--c-white);
  border: 1px solid var(--filet-panneau-teinte);
  border-radius: var(--cas-carte-rayon);
  /* ⚠️ LA CARTE EST BLANCHE, LA PAGE EST BLEU PÂLE. Sans cette ligne, un
     `.btn--secondary` posé dedans se remplirait du fond de la page : un bouton prend
     toujours la couleur de son fond, et son fond ici c'est la carte. C'est la même
     déclaration que porte `.cadre`. */
  --section-bg: var(--c-white);
}

.cas--vedette {
  padding: clamp(var(--sp-5), 2.5vw, var(--sp-10));
}

/* Deux vedettes à la suite se séparent d'une respiration. La maquette n'en pose
   qu'une, mais le bloc rend toute la liste — voir le gabarit. */
.cas-clients__vedette + .cas-clients__vedette {
  margin-block-start: clamp(var(--sp-6), 3vw, var(--sp-10));
}

/* ---------- La vedette ---------- */
.cas--vedette {
  display: grid;
  grid-template-columns: minmax(0, 46%) minmax(0, 1fr);
  column-gap: clamp(var(--sp-6), 3vw, var(--sp-12));
  align-items: start;
}

.cas--vedette .cas__corps {
  display: flex;
  flex-direction: column;
  gap: clamp(var(--sp-4), 1.5vw, var(--sp-6));
}

/* =========================================================================
   LA GRILLE — quatre cartes indépendantes, séparées par une vraie gouttière.

   ⚠️ PLUS DE QUADRILLAGE PAR LA GOUTTIÈRE ICI (le `gap: 1px` sur un fond à la
   couleur du filet, comme `module.grille` et `reperes`). C'est ce que le bloc
   faisait jusqu'au 21/08/2026, et Hugo l'a écarté : des cartes indépendantes ne
   partagent pas leurs arêtes, chacune porte la sienne. La technique de la
   gouttière reste la bonne pour un quadrillage — elle n'a plus d'objet dès que
   les cases se détachent.
   ========================================================================= */
.cas-clients__liste {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(var(--sp-5), 2vw, var(--sp-8));
  margin: 0;
  padding: 0;
  list-style: none;
}

/* L'enveloppe n'est plus qu'une case de grille : tout l'habillage est sur
   l'`<article>` qu'elle contient. */
.cas-clients__cellule {
  display: flex;
}

/* ---------------------------------------------------------------------
   ⚠️ L'HABILLAGE EMPILÉ, ÉCRIT UNE SEULE FOIS POUR DEUX EMPLOIS : la carte de la
   grille à toutes les largeurs, ET la vedette sous `lg` (voir le bloc étroit en
   fin de fichier). Demande d'Hugo du 27/08/2026 — « les cartes doivent toutes être
   du même style qu'elles soient à la une ou non, avec tous les éléments répartis
   verticalement ».

   Le mixin plutôt que deux jeux de cotes : au téléphone les deux objets font la
   même largeur, donc ils doivent porter le même rembourrage, la même gouttière, la
   même échelle de titre et le même ratio de cadre. Recopiés dans la requête
   étroite, ces cinq valeurs auraient dérivé au premier réglage sans que rien ne le
   signale — c'est le piège que ce dépôt documente partout ailleurs.

   ⚠️ CE QUI N'Y EST PAS : `flex: 1`, qui ne vaut que pour la carte (son enveloppe
   `.cas-clients__cellule` est un conteneur flex, celle de la vedette non), et
   `--cas-zoom`, dont les deux valeurs se comparent là où elles sont écrites.

   ⚠️ REMBOURRAGE ÉLARGI LE 21/08/2026 (« tout est trop dense ») : 24 → 40px au lieu
   de 20 → 24. Sur une carte qui porte un visuel, une rangée d'étiquettes, un titre,
   un récit, deux chiffres et un bouton, c'est la marge intérieure qui décide de la
   lisibilité avant toute autre cote.
   --------------------------------------------------------------------- */
/* ---------- Le survol d'une carte ----------
   Demandé par Hugo le 21/08/2026 : « un léger effet de hover sur les cartes », puis
   « on pourrait avoir un léger effet sur le fond de la carte ? Pareil pour la carte
   à la une ».

   QUATRE GESTES, ET AUCUN N'EST UNE OMBRE (règle 5 de MAQUETTE.md) : le fond se
   teinte d'un souffle de bleu, le filet passe au bleu plein, la carte se soulève de
   2px et grandit d'un cheveu. Le soulèvement et le filet sont déjà le survol de
   `.grille-articles__carte` — un troisième vocabulaire de survol n'aurait rien dit de
   plus ; la teinte et le zoom sont venus après, à la demande.

   ⚠️ LE FOND NE PEUT QUE SE TEINTER, PAS S'ÉCLAIRCIR : la carte est déjà blanche. Et
   il ne peut pas non plus aller vers le fond de page — --c-blue-50 est à 1,05 du
   blanc, la carte s'y dissoudrait au moment même où on la désigne. D'où un mélange
   vers l'ACCENT, à 4 % : assez pour que l'œil l'attrape, pas assez pour concurrencer
   le filet qui, lui, passe au bleu plein.

   ⚠️ ET C'EST --c-blue QU'IL MÉLANGE, PAS UNE COULEUR EN DUR : sur une page pilier,
   la teinte du survol suit alors celle de la page sans qu'on ait rien à recalculer.
   C'est la même construction que --filet-panneau-teinte.

   ⚠️ LA VEDETTE RÉAGIT COMME LES CARTES, et c'est un arbitrage d'Hugo (« pareil pour
   la carte à la une »). La règle avait d'abord été bornée aux cellules de la grille,
   au motif qu'un retour de survol dit « celle-ci parmi les autres » et qu'une vedette
   seule dans sa section n'a rien à départager. Le contre-argument est plus simple :
   les deux portent un bouton, donc les deux sont des objets sur lesquels on va, et
   deux comportements différents pour deux cartes de même famille se lisent comme un
   oubli.

   ⚠️ SOUS `hover: hover` UNIQUEMENT : sur un écran tactile, l'état de survol se
   déclenche au toucher et reste collé sur la dernière carte touchée.

   ⚠️ ET LE FOCUS CLAVIER EST TRAITÉ À PART, sans anneau sur la carte : le focus tombe
   sur le BOUTON, qui porte déjà le sien. La carte se contente du filet bleu, pour
   qu'on voie laquelle tient le focus. Cela reste vrai depuis que toute sa surface est
   cliquable (voir « Le clic pleine carte » plus bas) : le calque n'est pas une seconde
   cible de tabulation, c'est la même. */
/* ---------------------------------------------------------------------
   ⚠️ LE CONFLIT AVEC LA RÉVÉLATION, EN DEUX ACTES. C'est ce qui a coûté le plus de
   temps de toute la série, et le symptôme trompe à chaque fois : la teinte et le
   filet s'appliquent, seul le MOUVEMENT manque — ce qui envoie chercher du côté de la
   valeur du zoom. Deux essais y sont passés avant qu'une capture ne montre une carte
   au filet bleu et au fond teinté, de taille inchangée.

   `sections/_reveal.scss` pose, sur tout élément `data-reveal` révélé :

     transform: none;                    ← acte 1 : le mouvement est annulé
     transition: opacity, transform …    ← acte 2 : la liste du survol est remplacée
     transition-delay: … * 60ms;         ← et le survol hérite du retard du cran

   Le tout à (0,3,1) de spécificité, contre (0,2,0) pour un `:hover` de bloc — et
   `@use`é EN DERNIER dans main.scss exprès pour gagner, son en-tête le dit.

   DEUX REMÈDES, ET IL FAUT LES DEUX :

     `scale` / `translate` au lieu de `transform` — ce sont des propriétés À PART, qui
       se COMPOSENT avec `transform` au lieu d'y être, donc un `transform: none` venu
       d'ailleurs ne les touche pas. Répond à l'acte 1 sans guerre de spécificité.
     le chrome et la transition D'UN CRAN VERS L'INTÉRIEUR, sur l'`<article>` — qui ne
       porte pas `data-reveal`. Répond à l'acte 2, que le premier remède ne couvre pas :
       une liste de transitions n'a pas d'équivalent « à part ».

   ⚠️ LA LEÇON GÉNÉRALE, la seule à retenir si tout le reste est oublié : **un élément
   qui porte `data-reveal` ne doit pas porter en plus une animation de survol.** Séparer
   les deux rôles sur deux éléments coûte un cran de balisage et supprime toute une
   classe de bugs muets.

   ⚠️ LE MÊME DÉFAUT DORMAIT DANS `.grille-articles__carte`, dont le soulèvement au
   survol n'a jamais fonctionné depuis l'arrivée de reveal.js. Corrigé par le premier
   remède le 21/08/2026 — il n'a pas besoin du second, son survol ne portant qu'un
   déplacement de 2px dont la brutalité ne se voit pas.
   --------------------------------------------------------------------- */
/* ⚠️ LA TRANSITION EST SUR L'`<article>`, ET LE `transition` DE LA RÉVÉLATION EST LA
   RAISON. C'est le deuxième acte du même conflit : `_reveal.scss` ne pose pas
   seulement `transform: none` sur un élément révélé, il pose aussi son PROPRE
   `transition` (opacity + transform, 520ms) et un `transition-delay` — le tout à
   (0,3,1). Posée sur l'enveloppe, la liste de transitions du survol était donc
   remplacée en entier : le zoom sautait d'un coup, sans animation, et il héritait
   même du retard de 60ms du cran de révélation.

   Corriger à la spécificité (réécrire la liste sous un sélecteur plus fort) aurait
   demandé de recopier les valeurs de la révélation pour ne pas casser l'entrée — deux
   jeux à tenir accordés. Déplacer le chrome d'un cran vers l'intérieur règle les deux
   actes d'un coup, et de façon définitive : l'enveloppe révèle, l'article s'habille.

   ⚠️ LA LEÇON GÉNÉRALE : un élément qui porte `data-reveal` ne doit pas porter en plus
   une animation de survol. Séparer les deux rôles sur deux éléments coûte un cran de
   balisage et supprime toute une classe de bugs muets.

   ⚠️ --dur-base ET NON --dur-fast : 150ms sur un zoom de 3 % se lit comme un à-coup,
   parce que la distance parcourue par les bords est grande devant la durée. Le jeton
   --dur-base (280ms) est justement décrit dans _tokens.scss comme « un mouvement
   qu'on regarde arriver, pas une réaction », et c'est exactement le cas ici. La teinte
   et le filet, eux, restent rapides : une couleur n'a pas de distance à parcourir, et
   les allonger ferait traîner le survol. */
.cas--vedette,
.cas--carte {
  transition: background-color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out), scale var(--dur-base) var(--ease-out), translate var(--dur-base) var(--ease-out);
}

/* ⚠️ LE ZOOM N'A PAS LA MÊME VALEUR SUR LES DEUX, ET C'EST LE POINT À RETENIR. Un
   facteur d'échelle agit sur la LARGEUR, pas sur la perception : à 3 % une carte de
   600px grandit de 18px — 9 par bord — tandis que la vedette de 1300px en gagnerait
   39. Le même chiffre se lit comme un léger zoom d'un côté et comme un sursaut de
   l'autre. La vedette prend donc la moitié, pour un déplacement de bord comparable à
   l'œil.

   ⚠️ ET IL A FALLU DEUX ESSAIS : à 1,5 % / 0,8 %, Hugo ne voyait pas l'effet —
   4,5px par bord passent inaperçus à côté d'un filet qui change de couleur en même
   temps. La leçon : sur un survol qui porte DÉJÀ deux ou trois gestes, un zoom doit
   être plus franc que s'il était seul, sinon les autres le couvrent. */
.cas--carte {
  --cas-zoom: 1.03;
}

.cas--vedette {
  --cas-zoom: 1.015;
}

@media (hover: hover) {
  .cas--vedette:hover,
  .cas--carte:hover {
    background-color: color-mix(in srgb, var(--c-blue) 4%, var(--c-white));
    border-color: var(--c-blue);
    scale: var(--cas-zoom);
    translate: 0 -2px;
  }
}
/* ⚠️ PREMIER SURVOL DU SITE À ÊTRE GARDÉ CONTRE `prefers-reduced-motion`, et la
   distinction tient : `.grille-articles__carte` ne déplace que de 2px, ce qui reste
   dans le bruit ; un zoom, lui, met en mouvement TOUT le contenu de la carte — texte
   compris — et c'est exactement ce que le réglage demande d'éviter.

   La teinte et le filet, eux, RESTENT : ce ne sont pas des mouvements, et les retirer
   priverait de tout retour de survol. */
@media (prefers-reduced-motion: reduce) {
  .cas--vedette:hover,
  .cas--carte:hover {
    scale: 1;
    translate: none;
  }
}
.cas--vedette:focus-within,
.cas--carte:focus-within {
  border-color: var(--c-blue);
}

/* ---------- Le clic pleine carte ----------
   ⚠️ RIEN ICI : le couple `.carte-cliquable` / `.lien-couvrant` est la PRIMITIVE
   PARTAGÉE de _components.scss, extraite le 29/08/2026 quand la grille d'articles
   du blog a eu besoin du même comportement. Son en-tête porte tout le
   raisonnement — pourquoi un seul lien, pourquoi le calque écrase `.btn::after`,
   pourquoi le bouton repasse en flux, et la condition à tenir : aucun autre
   élément interactif dans une carte.

   Ce que ce bloc en fait : `partials/cas.njk` pose `carte-cliquable` sur
   l'`<article>` et `lien-couvrant` sur son bouton de pied. Les deux vont ensemble. */
/* ⚠️ `hidden` NE SUFFIT PAS QUAND UNE RÈGLE D'AUTEUR POSE `display`. La règle
   `[hidden] { display: none }` vient de la feuille du navigateur, et n'importe
   quelle déclaration d'auteur la bat : sans cette ligne, une carte masquée par
   filtres.js resterait visible et le filtre n'aurait aucun effet. */
.cas-clients__cellule[hidden] {
  display: none;
}

.cas--carte {
  /* `flex: 1` reste ici : c'est le seul trait que la vedette ne reprend pas — son
     enveloppe n'est pas un conteneur flex. Tout le reste est dans le mixin. */
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: clamp(var(--sp-5), 2vw, var(--sp-8));
  padding: clamp(var(--sp-6), 2.5vw, var(--sp-10));
}
.cas--carte .cas__corps {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: var(--sp-5);
}
.cas--carte {
  /* Le ratio exact de la maquette, règle 6 : c'est ce qui empêche la mise en page
     de sauter à l'arrivée des photos.

     ⚠️ LES DEUX CLASSES ENSEMBLE, ET C'EST LA CONDITION DE LA RÈGLE 6 : le cadre
     en attente et la photo qui le remplace doivent porter le MÊME ratio, sinon
     échanger l'un pour l'autre déplace la page — exactement ce que la règle
     cherche à empêcher. Les séparer un jour, c'est rouvrir le défaut. */
}
.cas--carte .cas__apercu,
.cas--carte .cas__image {
  aspect-ratio: 570/320;
}
.cas--carte {
  /* ⚠️ LE CORPS DE LA CARTE EST D'UN CRAN PLUS PETIT QUE CELUI DU SITE, demandé le
     21/08/2026 (« peut-être un peu réduire la police des titres et du texte dans les
     cartes »).

     ⚠️ ET C'EST LE CLAMP QUI POSAIT PROBLÈME, PAS LE JETON. --fs-h3 va de 22 à 32px :
     la maquette donne 22px à ces titres, donc au-delà du téléphone la carte affichait
     jusqu'à dix pixels de plus que ce qu'elle montre. Le clamp ci-dessous PLAFONNE à
     la valeur de la maquette au lieu d'en faire son plancher — c'est le même jeton,
     borné à l'autre bout.

     Deux écarts à la règle 3 de MAQUETTE.md (« on ne redéclare jamais la police d'un
     titre »). Ils étaient bornés à la variante CARTE jusqu'au 27/08/2026 ; ils valent
     désormais aussi pour la vedette SOUS `lg`, où elle n'est plus deux fois plus
     large que rien. Au large, elle garde l'échelle du site. Précédent :
     `.grille-articles__carte-titre`, qui déclare aussi la taille de son niveau. */
}
.cas--carte .cas__titre {
  font-size: clamp(1.25rem, 1.1rem + 0.45vw, 1.375rem); /* 20 → 22px */
}
.cas--carte .cas__description {
  font-size: var(--fs-sm);
}

/* L'état vide du filtrage, à la place de la grille. Il est sur le crème et non
   dans une carte : ce n'est pas un cas, c'est un message. */
.cas-clients__vide {
  margin: 0;
  padding: clamp(var(--sp-10), 5vw, var(--sp-20));
  font-size: var(--fs-md);
  color: var(--text-muted);
  text-align: center;
}

/* =========================================================================
   L'ATOME — les rangs d'un cas.
   ========================================================================= */
/* Le ratio exact de la maquette, règle 6 : c'est ce qui empêche la mise en page de
   sauter à l'arrivée des photos. Celui de la carte est dans le mixin `cas-empile`,
   que la vedette reprend sous `lg`. */
.cas--vedette .cas__apercu,
.cas--vedette .cas__image {
  aspect-ratio: 511/357;
}

/* ---------- La photo ----------
   Elle occupe la place du cadre en attente et rien d'autre ne change.

   ⚠️ `object-fit: cover` EST STRUCTUREL. Les bannières sont livrées en 1400 × 786,
   soit le ratio des CARTES (570 / 320) ; la vedette, elle, est à 511 / 357 sur
   grand écran. Sans `cover`, la photo s'y déformerait. Avec, elle est recadrée sur
   ses côtés — 137 px de chaque bord sur 1400 — ce qui a été vérifié au pixel le
   28/08/2026 : le logo incrusté le plus large est celui de Hardloop (x 246 → 1246)
   et il tient dans la bande visible (137 → 1263), avec 17 px de marge à droite.

   ⚠️ CETTE MARGE DE 17 px EST LA COTE À CONNAÎTRE avant de remplacer une bannière :
   un logo incrusté plus large que 1126 px centrés serait rogné, et seule la vedette
   le montrerait. Le test tient en une ligne de console — comparer la boîte des
   pixels blancs à la bande [137, 1263].

   ⚠️ PAS DE FILET, CONTRAIREMENT AU CADRE EN ATTENTE. Celui-ci en porte un parce
   qu'un rectangle vide n'a pas d'arête ; une photo porte la sienne. C'est le même
   raisonnement que la règle 1 de MAQUETTE.md, appliqué à l'envers. */
.cas__image {
  display: block;
  inline-size: 100%;
  block-size: auto;
  object-fit: cover;
  border-radius: var(--radius-cadre);
}

/* ---------- Les secteurs ----------
   ⚠️ CONTOURNÉES ET NON EN APLAT — règle 16 de MAQUETTE.md. Le contour garde la
   teinte à pleine force et le libellé reste sur le fond de la carte, où il est
   déjà lisible. Un aplat aurait demandé une encre par teinte : ici la teinte est
   celle du site (la page n'est pas une page pilier), mais la règle vaut d'avance
   pour le jour où ce bloc servirait sur une page qui en porte une.

   ⚠️ ET C'EST BIEN LE CONTOUR, PAS LE BADGE FLUO DE LA SECTION. Les deux objets
   sont des pastilles et ne disent pas la même chose : le badge annonce la section
   et doit se détacher, l'étiquette de secteur classe la carte et doit rester
   discrète. Mettre les deux en fluo ferait clignoter la page. */
.cas__secteurs {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  margin: 0;
  padding: 0;
  list-style: none;
}

.cas__secteur {
  padding: 0.1875rem 0.625rem;
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--text-accent);
  border: 1px solid currentcolor;
  border-radius: var(--radius-full);
}

/* Le titre ne déclare rien de sa police (règle 3) : _base.scss pose déjà le
   niveau — --fs-h2 pour la vedette, --fs-h3 pour les cartes. */
.cas__titre {
  margin: 0;
  color: var(--c-blue-zodiac);
  text-wrap: balance;
}

/* --text-strong et non --text-muted : c'est du corps de texte. */
.cas__description {
  margin: 0;
  max-width: 62ch;
  font-size: var(--fs-base);
  line-height: var(--lh-normal);
  color: var(--text-strong);
}

/* ---------- Les chiffres ----------
   ⚠️ `margin-block-start: auto` EST LE DISPOSITIF D'ALIGNEMENT, et c'est ce qui a
   évité tout un échafaudage de `subgrid`. Les cellules d'une même rangée ont la
   même hauteur (elles s'étirent), donc pousser le pied de carte vers le bas
   suffit à ce que les rangées de chiffres et les boutons tombent à la même
   hauteur d'une carte à l'autre — c'est exactement ce que fait la maquette de son
   côté, en glissant une cale sous les récits courts.

   L'alternative pesée et écartée : une chaîne de trois `subgrid` (cellule →
   `.cas` → `.cas__corps`) enjambant SIX rangs comptés à la main. Elle alignait la
   même chose, au prix d'un compte à tenir accordé entre le gabarit et la feuille
   — le piège `--rangs-N` documenté dans CLAUDE.md — qu'une carte sans chiffres
   aurait cassé en silence. Ici une carte sans chiffres perd seulement son
   alignement de pied, et rien d'autre ne bouge.

   ⚠️ EN `auto-fit`, AUCUN COMPTE PUBLIÉ : la vedette en porte trois, les cartes
   deux, sans que rien ne le déclare. Même choix que la rangée de `reperes`.

   ⚠️ --repere-taille EST POSÉ SUR LA RANGÉE, PAS SUR L'ATOME. Les chiffres d'une
   carte sont d'un cran plus petits que ceux d'une rangée de section — « les
   chiffres clés sont un peu trop grands », Hugo le 21/08/2026 — et c'est le bouton
   que `.repere__valeur` lit dans le repli de son `var()` (_components.scss). Ne
   pas écrire un `font-size` sur `.repere__valeur` ici : `reperes` et
   `texte-module` porteraient le même atome, et une surcharge par sélecteur
   toucherait aussi les rangées de /a-propos/. */
/* ⚠️ PLUS DE FILET DE SÉPARATION, retiré le 21/08/2026 (« enlever le séparateur »).
   Il avait été passé au bleu du site le matin même, puis le contour est revenu sur la
   carte : deux traits bleus à quelques pixels l'un de l'autre, l'un cernant la carte
   et l'autre la coupant, chargeaient un objet déjà dense. C'est L'AIR qui marque
   désormais la rupture, et le rembourrage haut monte en conséquence — sans trait
   pour l'annoncer, la respiration doit être franche. */
/* ⚠️ EN `auto-fit`, AUCUN COMPTE PUBLIÉ : la vedette en porte trois, les cartes deux,
   sans que rien ne le déclare. Même choix que la rangée de `reperes`.

   ⚠️ UN PASSAGE EN RANGÉE FLEX RESSERRÉE A ÉTÉ FAIT PUIS ANNULÉ le 21/08/2026 : la
   consigne « rapprocher les chiffres » visait la page d'étude de cas, pas cet index.
   Ce qu'elle a appris reste vrai et servira là-bas : dans une grille
   `repeat(auto-fit, minmax(Xrem, 1fr))`, **baisser le plancher du `minmax` ne
   rapproche rien** — c'est le `1fr` qui étire les colonnes pour remplir la largeur, et
   deux chiffres se retrouvent chacun au milieu de sa moitié. Pour les resserrer il
   faut sortir du partage égal : rangée flex à base fixe, ou colonnes en `max-content`
   avec `justify-content: start`. */
.cas__chiffres {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(7rem, 1fr));
  gap: var(--sp-5);
  margin: auto 0 0;
  padding-block-start: clamp(var(--sp-5), 2.5vw, var(--sp-8));
  --repere-taille: var(--fs-h3);
}

/* ---------- Le bouton ----------
   ⚠️ UN `.btn--primary`, PAS UN LIEN NU. Ce fichier avait d'abord invoqué
   l'exception de la règle 4 (« un lien au pied d'une carte ») ; Hugo l'a écartée
   le 21/08/2026 — « les boutons ne sont pas à la bonne norme, ce doit être des
   boutons classiques, je les veux avec un fond bleu ». C'est donc le CTA principal
   du site, tel quel : rien de sa forme n'est redéclaré ici, seule sa place. */
/* ⚠️ LA MARGE S'AJOUTE À LA GOUTTIÈRE DU CORPS, elle ne la remplace pas : le total
   fait donc 40px entre les chiffres et le bouton, contre 20 pour les autres rangs.
   Demande d'Hugo le 21/08/2026 — « laisser plus de marge entre les chiffres clés et
   le CTA ». Un bouton collé sous un chiffre se lit comme son légende. */
.cas__pied {
  margin: var(--sp-5) 0 0;
}

/* =========================================================================
   LE MODE ÉTROIT — une seule colonne, et un seul style de carte.

   Demande d'Hugo du 27/08/2026, en deux temps : « les cartes doivent toutes être
   du même style qu'elles soient à la une ou non, avec tous les éléments répartis
   verticalement », puis « pour la mosaïque, en mode mobile, on ne veut qu'une
   colonne ». Le second point vaut aussi pour « D'autres histoires comme celle-ci »
   des études de cas — c'est la même liste, donc la même règle, sans rien à
   ajouter là-bas.

   ⚠️ RIEN NE BOUGE AU LARGE : tout tient dans cette requête, et la vedette y garde
   ses deux colonnes, son échelle de titre et son ratio de cadre.
   ========================================================================= */
@media (max-width: 60rem) {
  /* La mosaïque passe à une colonne. Une carte de moitié de téléphone porterait un
     visuel, deux étiquettes, un titre, un récit, deux chiffres et un bouton : c'est
     la largeur qui manque, pas la hauteur. */
  .cas-clients__liste {
    grid-template-columns: minmax(0, 1fr);
  }
  /* ⚠️ LA VEDETTE DEVIENT UNE CARTE. Le visuel passe au-dessus du texte, et le reste
     de l'habillage vient du mixin : même rembourrage, même gouttière, même échelle de
     titre, même ratio de cadre, même corps de texte. À cette largeur les deux objets
     font la même chose — deux styles pour un même objet se liraient comme un oubli. */
  .cas--vedette {
    /* ⚠️ INDISPENSABLE, ET RIEN NE LE SIGNALERAIT : la règle large pose
       `align-items: start` pour caler les deux colonnes en haut. Sur une colonne flex
       cette même valeur cesse d'étirer les enfants et les réduit à la largeur de leur
       contenu — le cadre en attente et le bouton se retrouveraient calés à gauche,
       chacun à sa propre largeur. `grid-template-columns`, lui, devient simplement
       inerte, et la gouttière est reprise par le `gap` du mixin. */
    align-items: stretch;
    /* ⚠️ LE ZOOM DE SURVOL PREND CELUI DE LA CARTE. Sa valeur dépend de la LARGEUR de
       l'objet (voir le long commentaire du survol plus haut) : à cette largeur la
       vedette n'est plus deux fois plus large qu'une carte, donc 1,5 % passerait
       inaperçu là où 3 % se lit. Sans effet au toucher — le survol est sous
       `hover: hover` — mais une petite fenêtre de portable y est. */
    --cas-zoom: 1.03;
    display: flex;
    flex-direction: column;
    gap: clamp(var(--sp-5), 2vw, var(--sp-8));
    padding: clamp(var(--sp-6), 2.5vw, var(--sp-10));
  }
  .cas--vedette .cas__corps {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: var(--sp-5);
  }
  .cas--vedette {
    /* Le ratio exact de la maquette, règle 6 : c'est ce qui empêche la mise en page
       de sauter à l'arrivée des photos.

       ⚠️ LES DEUX CLASSES ENSEMBLE, ET C'EST LA CONDITION DE LA RÈGLE 6 : le cadre
       en attente et la photo qui le remplace doivent porter le MÊME ratio, sinon
       échanger l'un pour l'autre déplace la page — exactement ce que la règle
       cherche à empêcher. Les séparer un jour, c'est rouvrir le défaut. */
  }
  .cas--vedette .cas__apercu,
  .cas--vedette .cas__image {
    aspect-ratio: 570/320;
  }
  .cas--vedette {
    /* ⚠️ LE CORPS DE LA CARTE EST D'UN CRAN PLUS PETIT QUE CELUI DU SITE, demandé le
       21/08/2026 (« peut-être un peu réduire la police des titres et du texte dans les
       cartes »).

       ⚠️ ET C'EST LE CLAMP QUI POSAIT PROBLÈME, PAS LE JETON. --fs-h3 va de 22 à 32px :
       la maquette donne 22px à ces titres, donc au-delà du téléphone la carte affichait
       jusqu'à dix pixels de plus que ce qu'elle montre. Le clamp ci-dessous PLAFONNE à
       la valeur de la maquette au lieu d'en faire son plancher — c'est le même jeton,
       borné à l'autre bout.

       Deux écarts à la règle 3 de MAQUETTE.md (« on ne redéclare jamais la police d'un
       titre »). Ils étaient bornés à la variante CARTE jusqu'au 27/08/2026 ; ils valent
       désormais aussi pour la vedette SOUS `lg`, où elle n'est plus deux fois plus
       large que rien. Au large, elle garde l'échelle du site. Précédent :
       `.grille-articles__carte-titre`, qui déclare aussi la taille de son niveau. */
  }
  .cas--vedette .cas__titre {
    font-size: clamp(1.25rem, 1.1rem + 0.45vw, 1.375rem); /* 20 → 22px */
  }
  .cas--vedette .cas__description {
    font-size: var(--fs-sm);
  }
}
/* =========================================================================
   BLOC « hero-cas » — blocs/hero-cas.njk.

   Origine : le hero du gabarit d'étude de cas — Figma 144:5996 (le fil d'Ariane)
   et 144:6009 (la couverture et sa bande de chiffres).

   Le fil d'Ariane sur le crème, puis une BOÎTE ARRONDIE sombre en deux parties
   soudées : la photo de couverture sous son voile avec le titre posé dessus, et la
   bande de chiffres.

   ⚠️ UNE BOÎTE, PAS UNE BANDE PLEINE LARGEUR — tranché par Hugo le 21/08/2026 :
   « il faut considérer le header sur cette page comme une section avec des bords
   arrondis sur fond sombre ». Ce bloc a porté les deux formes dans la même journée ;
   ne pas rétablir la bande.

   L'exception de la règle 18 à la règle 1 tient à une seule chose : un hero collé
   sous l'en-tête n'a ni marge, ni arrondi, ni arête possible. Ici la maquette dessine
   la couverture en largeur CONTENUE (1280 px dans une planche de 1904) : la réponse
   au test « la bande peut-elle devenir une boîte ? » est OUI, donc la règle 1 reprend
   la main et on retombe sur ses cotes — 96rem, rayon 28 px, aucun contour.

   Conséquence : `.on-dark` va sur la BOÎTE, comme partout ailleurs sur le site.
   ========================================================================= */
.hero-cas {
  background-color: var(--bg-page);
  padding-inline: var(--container-pad);
  /* ⚠️ LE RETRAIT DU CONTENU, DÉCLARÉ UNE FOIS. Il alignait déjà le titre et le
     premier chiffre, et il était recopié à trois endroits ; le fil d'Ariane, lui,
     restait collé au bord de la boîte. Demande d'Hugo le 21/08/2026 : « tous les
     textes doivent être sur la même ligne verticale dans le header. »

     Quatre éléments le lisent maintenant — le fil, le contenu de la couverture, la
     rangée de chiffres et son repli en colonne. Trois `clamp()` recopiés auraient
     dérivé au premier réglage, et c'est précisément ce qui avait laissé le fil de
     côté. */
  --hero-cas-retrait: clamp(1.5rem, -0.5rem + 8vw, 5.5rem);
  /* Resserré en haut : le bloc suit immédiatement l'en-tête collant, qui occupe
     déjà 71 px de hauteur visuelle. Même esprit que `.hero-page--sombre`, mais plus
     court encore — le premier objet est un fil d'Ariane de 11 px, pas un titre. */
  padding-block: clamp(var(--sp-5), 2.2vw, var(--sp-8)) var(--sp-section);
}

/* ---------- L'en-tête : le fil d'Ariane sur le crème ----------
   ⚠️ IL PORTE LA MESURE DE LA BOÎTE, PAS CELLE DU `.container`. Un chemin ferré à
   80rem au-dessus d'une boîte de 96rem se lirait comme un décalage. Le rembourrage
   inline, lui, est déjà posé par la section. */
.hero-cas__entete {
  width: 100%;
  max-width: 96rem;
  margin-inline: auto;
  margin-block-end: clamp(var(--sp-4), 1.6vw, var(--sp-6));
  /* ⚠️ IL REPREND LE RETRAIT DU CONTENU DE LA BOÎTE. Le fil était le seul texte du
     hero collé au bord : la boîte rentre son titre, ses pastilles et ses chiffres, et
     un chemin qui commence cinq centimètres à leur gauche décale toute l'ouverture.
     Voir la note de `--hero-cas-retrait`. */
  padding-inline-start: var(--hero-cas-retrait);
}

/* =========================================================================
   LA BOÎTE.

   ⚠️ COTES DE LA RÈGLE 1 DE MAQUETTE.md — 96rem, rayon 28 px, aucun contour : sur du
   bleu nuit posé sur crème, le contraste fait déjà l'arête. Les mêmes que
   `.temoignages__boite`, `.reperes__boite`, `.chaine__boite`.

   `overflow: hidden` fait le travail des deux arrondis intérieurs : la couverture
   est rognée en haut, la bande de chiffres en bas, et aucune des deux n'a de rayon à
   déclarer. C'est ce qui les fait lire comme UN objet et non comme deux.
   ========================================================================= */
.hero-cas__boite {
  width: 100%;
  max-width: 96rem;
  margin-inline: auto;
  overflow: hidden;
  border-radius: 1.75rem; /* 28px — règle 1 de MAQUETTE.md */
  background-color: var(--c-blue-dark);
}

/* ---------- La couverture ----------
   ⚠️ UNE HAUTEUR MINIMALE ET NON UN `aspect-ratio`. Le ratio de la maquette
   (1278 × 460) est tenu par la cote haute du `clamp()` ; en dessous, c'est le texte
   qui commande. Un `aspect-ratio` strict aurait fait déborder le titre de sa bande
   sur mobile, où quatre lignes de serif tiennent plus haut que 2,8 fois la largeur.

   Le contenu est en `flex-end` : dans la maquette il est calé sur le bas de la
   photo, là où le voile est le plus dense. Le fil d'Ariane suit donc le texte vers
   le bas — il est le premier enfant de `__inner`, pas de la couverture. */
.hero-cas__couverture {
  position: relative;
  display: flex;
  align-items: flex-end;
  min-height: clamp(20rem, 26vw, 28.75rem); /* 28.75rem = les 460px de la maquette */
}

/* Le cadre en attente de la photo, SOUS le voile. Il perd le filet et l'arrondi de
   `.apercu` : il est ici un FOND de bande pleine largeur, pas une vignette posée
   dans un flux — un filet en travers de la page se lirait comme une erreur.

   ⚠️ SA LÉGENDE EST CALÉE EN HAUT, contrairement à tous les autres `.apercu` du
   site qui la centrent. C'est là que le voile est le plus clair (15 %), donc le
   seul endroit où elle reste lisible. Le jour où la photo arrive, ce bloc entier
   disparaît — voir l'avertissement de `.apercu` dans _components.scss. */
.hero-cas__attente {
  position: absolute;
  inset: 0;
  align-content: start;
  padding-block-start: clamp(var(--sp-5), 2vw, var(--sp-8));
  border: 0;
  border-radius: 0;
}

/* La photo de couverture prend EXACTEMENT la place du cadre en attente : mêmes
   `inset: 0`, donc rien ne bouge le jour où l'une remplace l'autre.

   ⚠️ `object-fit: cover` EST OBLIGATOIRE ET LE RECADRAGE EST FORT. La couverture
   n'a pas de ratio fixe — c'est une hauteur MINIMALE, le contenu commande (voir
   l'avertissement plus haut) — donc sa forme change beaucoup : mesuré le
   28/08/2026, elle va de 3,39 de rapport sur un grand écran à 1,03 sur téléphone.
   Une photo panoramique y perd donc les deux tiers de sa largeur au téléphone.

   ⚠️ CE QUE ÇA IMPOSE À LA PHOTO : son sujet doit tenir DANS LE TIERS CENTRAL,
   sans quoi il disparaît sur mobile. C'est la cote à vérifier avant d'en poser
   une nouvelle, et elle ne se voit pas sur l'écran où l'on travaille. */
.hero-cas__photo {
  position: absolute;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
}

/* Le voile de la maquette : du bleu nuit presque transparent en haut, presque
   opaque en bas. C'est lui qui garantit le contraste du titre blanc sur une photo
   qu'on n'a pas encore vue — voir l'avertissement en tête du gabarit.

   Les trois arrêts sont ceux de la maquette (15 %, 62 % à mi-hauteur, 92 %), posés
   en `color-mix` sur --c-blue-zodiac plutôt qu'en `rgba()` en dur : la teinte suit
   alors le jeton, comme partout ailleurs sur le site. */
.hero-cas__voile {
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, color-mix(in srgb, var(--c-blue-zodiac) 15%, transparent) 0%, color-mix(in srgb, var(--c-blue-zodiac) 62%, transparent) 52%, color-mix(in srgb, var(--c-blue-zodiac) 92%, transparent) 100%);
}

/* Le rembourrage de la couverture est celui de la BOÎTE et non celui de la page :
   le contenu n'est plus dans un `.container`, puisque la boîte borne déjà la mesure.
   Les cotes sont celles des autres boîtes du site (`.reperes--boite`). */
.hero-cas__inner {
  position: relative;
  z-index: 1;
  width: 100%;
  padding-block: clamp(var(--sp-8), 3.5vw, var(--sp-12));
  padding-inline: var(--hero-cas-retrait);
}

.hero-cas__texte {
  display: flex;
  flex-direction: column;
  align-items: start;
  gap: var(--sp-4);
}

/* ---------- La pastille du client ----------
   Un aplat BLANC à encre bleu nuit. Elle n'entre donc pas dans le compte des
   aplats à libellé blanc de la règle 10 : le contraste y est celui du texte du
   site sur du blanc, et il n'y a rien à mesurer.

   Elle ne redéclare pas sa police depuis rien : c'est le titrage du site, comme la
   pastille de `texte-panneau` dont elle est le pendant. */
.hero-cas__client {
  margin: 0;
  padding: var(--sp-2) var(--sp-4);
  border-radius: var(--radius-full);
  background-color: var(--c-white);
  font-family: var(--font-titre);
  font-size: var(--fs-xs);
  font-weight: var(--fw-semibold);
  line-height: 1.18;
  letter-spacing: -0.02em;
  color: var(--c-blue-zodiac);
}

/* ---------- Les étiquettes de secteur ----------
   Des pastilles CONTOURNÉES et translucides, pas des aplats — c'est l'esprit de la
   règle 16 de MAQUETTE.md appliqué à un autre motif : le libellé reste sur le fond
   de la couverture, où le blanc tient déjà, et le contour n'est pas du texte.

   ⚠️ PAS DE `backdrop-filter`, contrairement à la maquette qui pose un flou de
   2 px. Sur un aplat sombre uni, un flou d'arrière-plan ne floute rien ; il le
   fera le jour où la photo arrive, mais il coûte alors une couche de composition
   sur toute la surface de la pastille pour un effet invisible ici. À rétablir avec
   la photo si l'écart se voit. */
.hero-cas__secteurs {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  margin: 0;
  padding: 0;
  list-style: none;
}

.hero-cas__secteur {
  padding: 0.25rem var(--sp-3);
  border: 1px solid color-mix(in srgb, var(--c-white) 30%, transparent);
  border-radius: var(--radius-full);
  background-color: color-mix(in srgb, var(--c-white) 12%, transparent);
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--c-white);
}

/* Le titre ne déclare rien de sa police : _base.scss pose déjà le titrage et la
   taille du niveau (règle 3). --text-strong et non --c-blue-zodiac : le titre est
   sur la bande sombre, où l'encre des titres donne 1,2. C'est le même arbitrage que
   `.hero-page--sombre .hero-page__titre`. */
.hero-cas__titre {
  margin: 0;
  max-width: 26ch;
  color: var(--text-strong);
  text-wrap: balance;
}

/* --text-muted et non --text-strong, et c'est le seul chapeau du site dans ce cas :
   il est posé sur le voile, sous un titre blanc de grande taille, et la maquette le
   met elle-même en gris bleuté (#c3cbe2). Sur le bleu nuit, le casper de `.on-dark`
   donne 6,4 — largement au-delà du seuil. Voir la note de `.repere__libelle`. */
.hero-cas__chapeau {
  margin: 0;
  max-width: 44ch;
  font-size: var(--fs-lead);
  line-height: var(--lh-normal);
  color: var(--text-muted);
}

/* =========================================================================
   LA BANDE DE CHIFFRES.

   Soudée sous la couverture, elle occupe toute la largeur de la BOÎTE, dont
   l'arrondi la referme par le bas.

   ⚠️ ELLE EST EN BLEU NUIT, PAS AU BLEU DE LA MAQUETTE — demande d'Hugo le
   21/08/2026 : « passer la bande du hero en bleu nuit ». La maquette la peint en
   #2340ff, c'est-à-dire le --c-blue du site ; elle prend désormais le même
   --c-blue-dark que la couverture au-dessus d'elle.

   ⚠️ ET C'EST CE QUI RAMÈNE LA RÈGLE 11 À ZÉRO. Sur le bleu du site, `.on-dark`
   n'était plus valable tel quel : le casper de --text-muted y tombait à 2,83 et la
   bande devait le repasser au blanc pur. Sur le bleu nuit, les trois rôles sont
   justement ceux pour lesquels la classe est calée — blanc 15,5, casper 6,4, fluo
   11,5 — donc l'override a été RETIRÉ. Ne pas le rétablir : il ferait perdre au
   libellé sa nuance sans rien gagner.

   ⚠️ CONSÉQUENCE : LA BANDE A BESOIN D'UN FILET EN HAUT. Tant qu'elle portait un
   bleu franc, la rupture de couleur la détachait de la couverture ; à teinte égale
   il n'y a plus d'arête, et sans trait les chiffres flottent sous le titre comme
   s'ils en faisaient partie.

   ⚠️ SEUL LE PREMIER CHIFFRE GARDE L'ACCENT FLUO, les autres passent au blanc, et
   c'est ce que dessine la maquette (« + plusieurs pts » en starship, les trois
   autres en blanc). Le repère de tête est le résultat de l'étude ; les suivants la
   dimensionnent. La règle lit `:first-child` et ne publie donc aucun compte — la
   bande sert quatre chiffres comme trois.

   ---------------------------------------------------------------------
   ⚠️ LES CHIFFRES SONT SERRÉS, ET C'EST UNE RANGÉE FLEX À BASE FIXE — PAS UNE
   GRILLE. Demande d'Hugo le 21/08/2026 : « rapprocher les chiffres de la bande,
   quitte à ce que les libellés passent sur plusieurs lignes ».

   Le piège est mesuré : la rangée était un `repeat(auto-fit, minmax(12rem, 1fr))`,
   et baisser le plancher du `minmax` NE RAPPROCHE RIEN. C'est le `1fr` qui étire les
   colonnes pour remplir la largeur — à trois chiffres, chacun se retrouve au milieu
   de son tiers, quelle que soit la cote plancher. Il faut sortir du partage égal.

   ⚠️ C'EST UNE GRILLE EN COLONNES, ET NON UNE RANGÉE FLEX. Elle l'a été une heure le
   21/08/2026, en `flex: 0 1 <base>`, et c'était juste pour le resserrement — mais le
   flex ne sait pas aligner les LIBELLÉS entre eux : « + plusieurs pts » tenant sur
   deux lignes, son libellé descendait d'une ligne par rapport aux trois autres.
   Demande d'Hugo : « le texte sous les chiffres du hero doit être aligné. »

   La grille le fait sans qu'aucune hauteur ne soit écrite : deux rangées déclarées sur
   la rangée, `grid-template-rows: subgrid` sur chaque case, et les valeurs comme les
   libellés tombent sur la même ligne quel que soit le nombre de lignes de chacun.
   C'est la technique de `module.grille`, pour la même raison — et la seule qui ne
   publie ni compte ni cote. Une `min-height` de deux lignes aurait marché aujourd'hui
   et serait fausse à la première valeur qui en prend trois.

   ⚠️ ET LE PARTAGE ÉGAL RESTE ÉCARTÉ : les pistes sont en
   `minmax(min-content, <base>)`, donc plafonnées — elles ne s'étirent pas pour remplir
   la largeur, et `justify-content: start` groupe le reste à gauche. Une piste ne
   dépasse sa base que si son contenu l'y force, ce qui est exactement le cas de la
   première (le segment insécable « + plusieurs »). Rien n'est mesuré.

   ⚠️ ET LE FILET REPASSE SUR LA CASE, il ne peut plus venir de la gouttière. Le
   `gap: 1px` sur un fond teinté suppose que les cases REMPLISSENT la rangée ; sans
   `flex-grow`, l'espace qui reste à droite laissait voir ce fond en une bande claire
   sur toute la hauteur. C'est le seul endroit du site où le raisonnement de
   `module.grille` ne s'applique pas, et la raison est là.

   `:not(:first-child)` est sans danger ici parce que la rangée NE SE REPLIE PAS : à
   base fixe et `flex-shrink: 1`, les cases se resserrent au lieu de passer à la
   ligne. En dessous de `sm` elles passent en colonne, et le filet devient horizontal.
   ========================================================================= */
.hero-cas__chiffres {
  margin: 0;
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(min-content, 12rem);
  /* Les deux rangées que les cases relaient en `subgrid` : la valeur, puis le libellé.
     La gouttière entre elles vient d'ici et non de `.repere`, dont le `gap` n'a plus
     cours dès qu'il devient une sous-grille. */
  grid-template-rows: auto auto;
  row-gap: var(--sp-2);
  justify-content: start;
  align-items: start;
  /* ⚠️ LE RETRAIT DE LA COUVERTURE EST PORTÉ PAR LA RANGÉE, pas par la première
     case, et c'est ce qui aligne le premier chiffre sur le titre au-dessus SANS
     laisser deux cotes à tenir accordées. Posé sur la case, il aurait mangé sa base
     de 8,5rem (le site est en `border-box`) : le premier chiffre aurait eu moins de
     place que ses voisins, et le rattraper demandait une seconde valeur calculée
     depuis la première.

     Le filet du haut, lui, s'étend bien sur toute la largeur : une bordure dessine
     autour de la boîte de rembourrage. */
  padding-inline-start: var(--hero-cas-retrait);
  /* ⚠️ UNE ÉCHELLE À ELLE, ENTRE --fs-h3 ET --fs-h2, et c'est assumé : le site n'a
     pas de jeton entre les deux (22→32 et 28→48). Grossis d'un cran à la demande
     d'Hugo le 21/08/2026 — « tu peux grossir un peu les chiffres » — les chiffres
     étaient à --fs-h3, qui les mettait en dessous de ce que la maquette leur donne
     (34 px pour le premier, 40 pour les trois autres).

     26→38 px est le cran juste au-dessus : assez pour que la bande porte ses
     chiffres, pas assez pour concurrencer le <h1> au-dessus d'elle, ce que --fs-h2
     aurait fait à 48. Précédent d'une échelle écrite à la main pour la même raison :
     `.cas--carte .cas__titre`. */
  --repere-taille: clamp(1.625rem, 1.35rem + 0.85vw, 2.375rem); /* 26 → 38px */
  --hero-cas-filet: rgba(255, 255, 255, 0.18);
  border-block-start: 1px solid var(--hero-cas-filet);
}

/* ⚠️ `flex: 0 1 <base>` — le `0` EST LE POINT. Il interdit à la case de grandir,
   donc les chiffres restent groupés à gauche au lieu de se répartir sur la largeur.
   Voir l'avertissement en tête de section : c'est le `1fr` de l'ancienne grille qui
   les écartait, pas la cote plancher.

   La base est étroite à dessein — les libellés passent sur deux lignes, ce qui est le
   compromis demandé. Élargie deux fois le 21/08/2026 : 8,5 → 10rem (« les chiffres
   sont trop serrés là »), puis 10 → 12rem (« les blocs de chiffres pourraient être un
   peu plus large »).

   ⚠️ AUCUNE CASE N'A DE BASE PARTICULIÈRE, et surtout pas la première, dont la
   valeur est la plus longue (« + plusieurs pts »). Une seconde cote écrite à la main
   pour elle aurait été fausse au premier changement de libellé, et fausse
   différemment à chaque largeur — c'est le défaut que la feuille de `item` décrit
   pour les hauteurs devinées.

   Ce qui tient « + plusieurs » sur une ligne est une ESPACE INSÉCABLE DANS LA DONNÉE
   (voir src/cas-clients/hardloop.md) : le segment devient indivisible, donc il fixe
   la largeur minimale de la case, et le flex l'élargit tout seul jusqu'à l'y loger.
   Rien n'est mesuré ici.

   ⚠️ LA PREMIÈRE CASE N'A PAS DE REMBOURRAGE DE DÉBUT : son chiffre tombe donc
   exactement sur le retrait porté par la rangée, c'est-à-dire sur le titre. Les
   suivantes en prennent un, pour ne pas coller au filet qui les précède — le
   rembourrage accompagne le filet, il ne se pose pas là où il n'y en a pas. */
/* ⚠️ CHAQUE CASE EST UNE SOUS-GRILLE DES DEUX RANGÉES DE LA RANGÉE, et c'est ce qui
   aligne les libellés entre eux. La case EST le `.repere` — la macro pose les deux
   classes sur le même élément — donc c'est bien ici qu'on remplace sa pile flex par
   une sous-grille : l'hôte décide du placement, l'atome garde son apparence. */
.hero-cas__chiffre {
  display: grid;
  grid-template-rows: subgrid;
  grid-row: span 2;
  padding-block: clamp(var(--sp-5), 2vw, var(--sp-6));
  padding-inline-end: clamp(var(--sp-4), 1.6vw, var(--sp-6));
}

.hero-cas__chiffre:not(:first-child) {
  padding-inline-start: clamp(var(--sp-4), 1.6vw, var(--sp-6));
  border-inline-start: 1px solid var(--hero-cas-filet);
}

.hero-cas__chiffre:not(:first-child) .repere__valeur {
  color: var(--c-white);
}

/* Le fil d'Ariane : l'atome porte son apparence (_components.scss), ce bloc ne
   garde que le placement — et il n'y en a pas, la gouttière de `__inner` s'en
   charge. La règle existe pour le dire, pas pour déclarer. */
/* ⚠️ QUATRE PISTES DE 12rem NE TIENNENT PAS SOUS `lg`, et une grille en colonnes ne
   se replie pas d'elle-même : ses pistes ne descendent jamais sous leur contenu. On
   passe donc explicitement à deux colonnes, puis à une sous `sm`. C'est le prix du
   `subgrid` — la rangée flex se resserrait toute seule, mais elle ne savait pas
   aligner les libellés.

   ⚠️ ET LA SOUS-GRILLE S'ARRÊTE AVEC LUI. Sans les deux rangées déclarées sur la
   rangée, il n'y a plus rien à relayer : la case redevient la pile flex de `.repere`.
   Les libellés ne sont alors plus alignés entre colonnes — ce qui est sans objet, à
   deux colonnes une valeur sur deux lignes est la norme et non l'exception. */
@media (max-width: 60rem) {
  .hero-cas__chiffres {
    grid-auto-flow: row;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    grid-template-rows: none;
  }
  .hero-cas__chiffre {
    display: flex;
    grid-row: auto;
    row-gap: var(--sp-2);
  }
  /* Le filet vertical ne vaut plus que pour la case de DROITE de chaque rangée : à
     deux colonnes, une case sur deux ouvre une ligne. */
  .hero-cas__chiffre:nth-child(2n+1) {
    padding-inline-start: 0;
    border-inline-start: 0;
  }
}
@media (max-width: 48rem) {
  /* Une colonne sur téléphone : à deux, « + plusieurs pts » se couperait sur trois
     lignes.

     La rangée reprend le retrait des deux côtés — en colonne, les cases occupent toute
     la largeur et il n'y a plus de premier chiffre à aligner sur un titre.

     ⚠️ LE FILET PASSE DE VERTICAL À HORIZONTAL. Les deux règles vont ensemble : c'est
     le seul endroit où `:not(:first-child)` est repris deux fois. */
  .hero-cas__chiffres {
    grid-template-columns: minmax(0, 1fr);
    padding-inline: var(--hero-cas-retrait);
  }
  .hero-cas__chiffre:not(:first-child) {
    padding-inline-start: 0;
    border-inline-start: 0;
    border-block-start: 1px solid var(--hero-cas-filet);
  }
}
/* =========================================================================
   BLOC « citation » — blocs/citation.njk.

   Origine : la section témoignage du gabarit d'étude de cas — Figma 144:6219.

   Un panneau fermé coupé en deux : le portrait à gauche, la parole à droite.
   Le pourquoi de ce bloc — et pourquoi ce n'est ni `temoignages` ni l'`exergue`
   de `texte-module` — est en tête du gabarit.
   ========================================================================= */
.citation {
  background-color: var(--bg-page);
  padding-block: var(--sp-section);
}

/* ⚠️ LE PANNEAU N'EST PLUS COUPÉ EN DEUX DEPUIS LE 31/08/2026. Il l'était :
   portrait à gauche, parole à droite, gouttière d'un pixel en guise de filet
   intérieur — le second point d'exécution de la règle 13.

   Ce qui l'a fait tomber : la case du portrait portait le ratio 299 × 399 de la
   maquette, et c'était donc ELLE qui donnait sa hauteur au panneau dès que la
   citation était courte. Le bloc mesurait 483 px de haut pour cinq lignes de
   texte, et la photo 362 px de large — trop grand, l'un et l'autre.

   Réduire la photo dans sa case n'était pas une sortie : une image plus petite
   dans une cellule de grille laisse sous elle une bande du fond du panneau, sur
   toute la hauteur restante. On aurait troqué une photo trop grande contre un
   vide que rien n'explique.

   Le portrait est donc descendu dans l'attribution (voir le gabarit), et le
   panneau redevient une seule case. Sa hauteur est maintenant celle du texte,
   ce qu'elle aurait toujours dû être. */
.citation__panneau {
  margin: 0;
  display: flex;
  /* ⚠️ `stretch` ET NON `center` : c'est ce qui fait remplir au portrait toute la
     hauteur de la rangée. Centré, il gardait son ratio et laissait au-dessus et
     en dessous de lui une bande de cadre vide — ou, s'il était assez grand pour
     porter la hauteur, c'est le texte qui flottait dans du vide. */
  align-items: stretch;
  gap: clamp(var(--sp-6), 3vw, var(--sp-10));
  /* ⚠️ LE REMBOURRAGE EST MONTÉ ICI, depuis `.citation__corps`. C'est ce qui met
     le portrait DANS le cadre plutôt qu'à fleur d'arête : il est le frère du
     corps, donc la marge du corps ne le concernait pas. */
  padding: clamp(var(--sp-8), 3.5vw, var(--sp-12)) clamp(var(--sp-6), 3vw, var(--sp-12));
}

/* ⚠️ PLUS DE MARGE NI DE FOND ICI. Les deux ont servi tant que le corps était une
   CASE du panneau : son fond blanc faisait la moitié droite, et la gouttière d'un
   pixel entre les deux cases faisait le filet intérieur. Le panneau n'a plus qu'une
   rangée, `.cadre` peint déjà le blanc, et la marge est montée sur le panneau pour
   englober le portrait. Les redéclarer peindrait du blanc sur du blanc et
   doublerait le rembourrage. */
.citation__corps {
  /* La parole prend tout ce que le portrait ne prend pas. C'est LUI qui porte
     désormais la cote — voir `.citation__photo` — parce que c'est de lui qu'on
     parle quand on règle ce bloc. Un `flex: 1` ici suffit à absorber le reste,
     et il n'y a plus deux nombres à tenir accordés. */
  flex: 1;
  min-inline-size: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: clamp(var(--sp-5), 2vw, var(--sp-8));
}

/* ---------- La parole ----------
   --fs-quote, l'échelle réservée aux citations, et le titrage du site : c'est ce
   qui la distingue d'un paragraphe. L'encre est celle des TITRES et non l'accent —
   la phrase porte la section, elle n'est pas une mise en valeur dans un texte.
   Même arbitrage que l'`exergue` de `texte-module`. */
.citation__texte {
  margin: 0;
}

.citation__texte p {
  margin: 0;
  /* ⚠️ 38ch ET NON LES 34 DE LA MAQUETTE, et c'est la conséquence directe du
     portrait réduit. La colonne de texte est passée de 729 à 801 px en récupérant
     ce que la photo a rendu ; à 34 caractères la citation s'arrêtait 84 px avant
     le bord et le vide qu'on venait de supprimer revenait — mesuré à 117 px avec
     la gouttière.

     Rallonger la mesure plutôt que grossir la police : la taille vient d'être
     réglée à 28 px après deux essais, et c'est le réglage qui a été validé. 38
     caractères remplissent la colonne à cette taille (38 × 0,756 × 27,88 = 801)
     et restent une mesure de citation, courte.

     ⚠️ CETTE COTE SUIT LA LARGEUR DU PORTRAIT. Le jour où il change encore, c'est
     ici qu'il faut reprendre — sinon le vide réapparaît sans que rien ne le dise. */
  max-width: 38ch;
  font-family: var(--font-titre);
  /* ⚠️ 28 PX AU HAUT DE L'ÉCHELLE, ET LA COTE EST CALCULÉE. Passée par deux
     essais le 31/08/2026 : 34 px, jugés trop gros, puis 17 — la moitié — jugés
     trop petits. 28 px n'est pas un compromis à vue, c'est la taille à laquelle
     la mesure de 34 caractères REMPLIT sa colonne.

     Le calcul : la colonne de texte fait 729 px dans le partage 3/4 — 1/4, et un
     caractère de la police de titre mesure 0,756 fois le corps. 729 ÷ (34 × 0,756)
     = 28,4. En dessous, le plafond de 34ch s'arrête avant le bord et laisse un
     vide — c'est exactement ce qui se voyait à 17 px : 445 px de cadre nu entre
     la fin des lignes et le portrait.

     ⚠️ LE PLANCHER RESTE `--fs-md`, non calculé : sur un téléphone la colonne est
     étroite et c'est la lisibilité qui commande, pas le remplissage.

     Le jeton `--fs-quote` n'est PAS touché : `temoignages` et l'exergue de
     `texte-module` s'en servent aussi, et rien ne leur a été reproché. */
  font-size: clamp(var(--fs-md), 0.9rem + 1.15vw, 2.05rem);
  font-weight: var(--fw-semibold);
  line-height: 1.25;
  letter-spacing: -0.02em;
  color: var(--c-blue-zodiac);
  /* ⚠️ `pretty` ET NON `balance`, DEPUIS LE 31/08/2026. `balance` ÉGALISE la
     longueur des lignes : sur une citation courte il les raccourcit TOUTES pour
     les rendre semblables, et le texte s'arrête loin du bord.

     Mesuré sur les deux extrêmes du site, dans une colonne de 800 px :
       · Hardloop, 124 caractères sur 3 lignes → la plus longue faisait 590 px,
         soit 210 px de vide avant le portrait ;
       · Lulli, 259 caractères sur 5 lignes → 768 px, l'écart ne se voyait pas.

     Ce n'était donc pas un défaut de la page Hardloop mais du réglage, d'autant
     plus visible que la citation est brève. `pretty` garde ce qu'on lui demandait
     — pas de mot esseulé sur la dernière ligne — sans rogner les précédentes.

     ⚠️ Firefox ne le connaît pas encore et retombe sur `wrap` : les lignes y
     remplissent la colonne, la dernière peut rester courte. C'est la dégradation
     acceptable ; `balance` n'y était pas mieux servi. */
  text-wrap: pretty;
}

.citation__auteur {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
}

.citation__identite {
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
  min-inline-size: 0;
}

/* ---------- Le portrait, à droite du texte ----------
   ⚠️ 9rem, SOIT LE TIERS DE CE QU'IL MESURAIT. Il faisait 362 px de large ; il en
   fait 122, ce qui était la demande — « la réduire au moins de 3 ».

   Effet de bord heureux, et il vaut d'être noté : les fichiers livrés font 414 px
   de large. À 362 px de rendu ils étaient AGRANDIS de 4 %, et tombaient à 0,57×
   sur un écran rétina — donc visiblement doux. À 122 px ils sont servis à 3,4×,
   soit largement au-dessus du 2× rétine. La réduction règle la résolution en même
   temps que l'encombrement.

   Le ratio 299 × 399 de la maquette est conservé, et `cover` avec : les portraits
   sont en 0,892 quand le cadre demande 0,749, il y a donc bien un recadrage
   horizontal à absorber. */
.citation__photo {
  /* ⚠️ UN QUART DU CADRE, ET PLUS DE COTE FIXE. Il a valu 9rem une heure durant :
     une largeur écrite ne pouvait pas tenir une PROPORTION, et le reste du cadre
     se retrouvait vide. Le `1` répond au `3` du corps.

     ⚠️ ET PLUS D'`aspect-ratio` NON PLUS. C'est lui qui déduisait la hauteur de la
     largeur — donc qui faisait du portrait le plus grand des deux et lui rendait
     la commande de la hauteur du bloc, le défaut qu'on venait de corriger. Étiré
     par la rangée, il prend celle du texte : le cadre est plein des deux côtés,
     et sa hauteur reste celle de la parole.

     `cover` porte alors tout le recadrage, et il est franc — les fichiers sont en
     0,892 quand la colonne demande autour de 1,1. C'est un cadrage serré sur le
     visage, ce que des portraits centrés supportent bien. */
  /* ⚠️ 17 % DE LA LARGEUR UTILE, ET C'EST UNE RÉDUCTION DE 30 % SUR LA COTE
     PRÉCÉDENTE. Le partage était de 3 contre 1, soit 243 px sur les 1006 px
     intérieurs du cadre ; 30 % de moins font 170, et 170 ÷ 1006 = 17 %.

     ⚠️ EXPRIMÉ EN BASE ET NON EN RAPPORT, exprès. À 3 contre 1, changer la
     largeur du portrait demandait de recalculer les DEUX nombres et de les tenir
     accordés — et le rapport ne disait plus la cote qu'on cherchait à régler.
     Ici la photo déclare sa part, le texte absorbe le reste : une seule valeur à
     toucher au prochain réglage.

     La parole en garde 80 %, la gouttière les 3 % restants. */
  flex: 0 0 17%;
  min-inline-size: 0;
  block-size: auto;
  object-fit: cover;
  border: 0;
  border-radius: 0;
}

.citation__nom {
  font-family: var(--font-titre);
  font-size: var(--fs-base);
  font-weight: var(--fw-semibold);
  color: var(--text-strong);
}

/* La chasse fixe des mentions de service du site, SANS capitales : « Co-fondateur
   · Hardloop » est une fonction, pas une étiquette, et les capitales y rendraient
   le point médian illisible. Même arbitrage que `.item__accroche`. */
.citation__role {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  line-height: var(--lh-normal);
  color: var(--text-muted);
}

@media (max-width: 56.25rem) {
  /* ⚠️ PLUS RIEN À BASCULER SUR LE PANNEAU : il n'a plus qu'une colonne à toutes
     les largeurs, et le portrait n'est plus une case pleine largeur qu'il
     faudrait recadrer en 16/10. Les deux règles qui s'en chargeaient sont parties
     le 31/08/2026 avec la case elle-même.

     Le portrait rétrécit d'un cran : à 9rem il prenait près de la moitié de la
     largeur d'attribution sur un téléphone. */
  /* ⚠️ LA RANGÉE S'EMPILE, ET LE PORTRAIT PASSE AU-DESSUS. Le partage 3/4 — 1/4
     ne survit pas à 375 px : le cadre n'offre que 297 px utiles, le quart du
     portrait tombe à 56 px de large, et comme il s'étire sur la hauteur du texte
     il devient une BANDE VERTICALE de 56 × 185 — un visage recadré à l'illisible.
     Mesuré avant de trancher.

     Élargir le quart ne sauvait rien : la colonne de texte serait alors passée
     sous 200 px, où la citation casse mot à mot.

     Le portrait passe donc au-dessus de la parole — au-dessus, et jamais
     dessous : sous elle il se lirait comme une signature, alors qu'il accompagne
     la citation. Il reprend le recadrage 16/10 d'une vignette de tête, la cote
     d'origine du bloc avant le 31/08/2026. */
  .citation__panneau {
    flex-direction: column;
    align-items: stretch;
  }
  .citation__photo {
    /* `order` plutôt qu'un déplacement dans le gabarit : l'ordre du document doit
       rester celui de la lecture — la parole, puis qui la prononce. */
    order: -1;
    flex: none;
    inline-size: 100%;
    aspect-ratio: 16/10;
    /* ⚠️ ET UN PLAFOND, QUI MANQUAIT AVANT LE 31/08/2026. `md` vaut 900 px, pas
       les 375 d'un téléphone : sans lui la vignette prenait 761 px de large à ce
       seuil, pour un panneau de 706 px de haut — le défaut qu'on venait de
       corriger, revenu par la porte de la bascule.

       26rem = 354 px, et la cote n'est pas arbitraire : les fichiers livrés font
       414 px de large. Au-delà, la vignette serait AGRANDIE — à 761 px elle
       l'était de 1,8 fois, donc franchement floue. Le plafond est la largeur
       native, arrondie vers le bas.

       Ferrée à gauche et non centrée : le texte qui suit commence au même bord. */
    max-inline-size: 26rem;
    align-self: flex-start;
  }
  /* La rangée se resserre : à 375 px, un portrait de 82 px et la parole se
     partagent 339 px de panneau, marges comprises. */
  .citation__panneau {
    gap: var(--sp-5);
  }
}
/* =========================================================================
   BLOC « une » — blocs/une.njk.

   L'ouverture du blog : le titre de rubrique, l'enseigne « À la une », puis
   l'article mis en une dans un panneau CLAIR et l'encart d'inscription à la
   newsletter à côté. Origine : Figma 144:5700.

   La maquette peint un aplat bleu nuit d'un bord à l'autre ; c'est une BOÎTE ici
   (règle 1 de MAQUETTE.md), avec les cotes partagées par toutes les boîtes du
   site — 96rem, rayon 28px, aucun contour. Le raisonnement complet, et pourquoi
   la règle 18 (les bandes sombres des pages éditoriales) ne s'applique pas à un
   index, sont en tête du gabarit.
   ========================================================================= */
.une {
  position: relative;
  background-color: var(--bg-page);
  /* ⚠️ RESPIRATION RÉDUITE EN HAUT, ET C'EST VOULU : cette boîte OUVRE la page,
     juste sous l'en-tête collant. Une respiration de section pleine (4 à 7,5rem)
     y laisserait une bande de fond vide avant le premier contenu — c'est
     exactement ce que la règle 18 reproche à une bande halftone posée après un
     hero, et la demande d'Hugo qui l'a produite : « perdre moins de place sur la
     page ». */
  padding-block: clamp(var(--sp-6), 2.5vw, var(--sp-10)) var(--sp-section);
}

/* ⚠️ UNE SEULE MESURE POUR LE REMBOURRAGE DE LA BOÎTE ET LES GOUTTIÈRES DE SA
   GRILLE — demande d'Hugo le 21/08/2026 : « la même marge entre les bords de la
   boîte et les cartes, et entre les cartes entre elles ». Elle est portée par une
   variable plutôt que recopiée : deux `clamp()` identiques auraient dérivé l'un de
   l'autre au premier réglage, et rien n'aurait signalé leur divergence.

   C'est une régularité qui se voit : dès que les deux valeurs diffèrent, l'œil lit
   les cartes comme mal calées dans leur boîte plutôt que comme une grille. */
.une {
  --une-espace: clamp(var(--sp-6), 2.5vw, var(--sp-10)); /* 24 → 40px */
}

/* Le titre de page, hors de la boîte. Il porte la MESURE DE LA BOÎTE et non celle
   du `.container` : ferré à 80rem au-dessus d'une boîte de 96rem, il se lirait
   comme un décalage. Même construction que le fil d'Ariane de `hero-cas`. */
.une__intitules,
.une__boite {
  max-width: 96rem;
  margin-inline: auto;
}

/* En dessous de 96rem, `margin-inline: auto` ne ménage plus rien : la boîte
   toucherait les deux bords de l'écran et cesserait d'en être une. Le titre suit
   exactement, pour rester ferré sur elle. */
@media (max-width: 96rem) {
  .une__intitules,
  .une__boite {
    margin-inline: var(--container-pad);
  }
}
/* Le titre ne déclare rien de sa police (règle 3) : _base.scss pose déjà serif,
   graisse, interlignage et la taille du niveau. --c-blue-zodiac comme tous les
   titres du site — il est sur le fond de page depuis qu'il est sorti de la boîte,
   l'exception de la règle 5 ne le concerne plus. */
.une__intitule {
  margin: 0 0 clamp(var(--sp-5), 2vw, var(--sp-8));
  color: var(--c-blue-zodiac);
}

/* Les cotes de la règle 1, identiques à `.temoignages__boite` et
   `.hero-cas__couverture`. Aucun contour : sur du bleu nuit posé sur le fond de
   page, le contraste fait l'arête. */
.une__boite {
  padding: var(--une-espace);
  background-color: var(--c-blue-dark);
  border-radius: 1.75rem; /* 28px */
}

/* ---------- L'enseigne « À la une » ----------
   ⚠️ SON APPARENCE EST LA PRIMITIVE PARTAGÉE `.badge-section` (_components.scss) —
   une pastille fluo à encre sombre. Cette règle ne pose donc QUE son placement,
   comme toute feuille hôte.

   ⚠️ ELLE A PERDU SON FILET le 21/08/2026 (« enlever le séparateur »). Le trait
   qui courait d'elle jusqu'au bord de la boîte avait un sens quand l'enseigne
   était une ligne nue : il la rattachait à la section. Une pastille se détache
   toute seule, et le filet doublait alors une séparation que la forme faisait
   déjà.

   ⚠️ ELLE A AUSSI CHANGÉ DE PLACE DEUX FOIS dans la journée : d'abord DANS la
   carte de la une (où elle la coiffait), puis dans l'en-tête de la boîte (« mettre
   à la une en dehors de la boîte »), où elle l'annonce. */
.une__enseigne {
  margin: 0 0 var(--une-espace);
}

/* ---------- La grille : l'article, puis l'encart ----------
   ⚠️ L'ENCART FAIT 20 % DE LA GRILLE — demandé par Hugo le 21/08/2026, après un
   « max un tiers », un « encore moins large » et un « 15 % ». C'est une piste en
   pourcentage, ce qu'il y a de plus direct, et il n'y a aucune raison de faire
   autrement.

   ⚠️ SON MINIMUM EST `min-content`, PAS 0, et c'est le seul garde-fou nécessaire.
   Sur une fenêtre étroite, 20 % de la grille peut tomber sous ce qu'exige le mot
   « NEWSLETTER » plus le rembourrage — environ 185 px. Avec un plancher à zéro le
   titre déborderait de la carte ; en `min-content`, la piste s'arrête d'elle-même
   sur ce que son contenu exige et le dépassement de la borne reste imperceptible.

   ⚠️ TROIS RÉGLAGES DE LARGEUR ONT ÉTÉ PERDUS AVANT D'EN ARRIVER LÀ, et la leçon
   n'est PAS une leçon de dimensionnement de grille. `fit-content(33% → 25% → 15%)`
   puis `minmax(min-content, clamp(…))` n'ont RIEN changé au rendu, non pas parce
   qu'ils étaient mal écrits, mais parce qu'un `</div>` manquait dans le gabarit :
   l'encart était imbriqué dans `.une__cellule` au lieu d'être une case de la
   grille. `.une__grille` n'avait donc qu'un seul enfant, et pas une seule de ses
   déclarations — colonnes, gouttière — ne pouvait agir.

   ⚠️ ET LE SYMPTÔME NE DÉSIGNAIT PAS LA CAUSE : la page restait à deux colonnes
   (c'est le `display: flex` de l'enveloppe qui les produisait) et la carte se
   comportait comme un `max-content`, ce qui ressemble trait pour trait à une borne
   de piste ignorée. **Devant un réglage de grille qui semble sans effet, vérifier
   D'ABORD que les éléments visés sont bien des enfants directs du conteneur.**

   `align-items: start` est l'autre moitié du « hug » demandé : sans lui, l'encart
   s'étirerait sur toute la hauteur de l'article, qui fait le double.

   ⚠️ LES BOUTONS DE L'ENCART DOIVENT POUVOIR SE PLIER — voir la règle sur
   `.une__newsletter .btn` plus bas. À cette largeur, « Voir les éditions → » sur
   une seule ligne dépasse la carte. */
.une__grille {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(min-content, 20%);
  /* ⚠️ GOUTTIÈRE ÉLARGIE TROIS FOIS le 21/08/2026 — « une marge par rapport à la
     carte article », « il manque toujours de la marge », « toujours collé ». Les
     deux objets ne sont plus de même nature depuis que la carte est un panneau
     clair et l'encart un bloc teinté : collés, ils se lisent comme deux moitiés
     d'un même objet. C'est LE VIDE qui les sépare — il n'y a pas de filet entre
     eux, et c'est voulu : un trait redécouperait ce que l'espace suffit à
     distinguer.

     ⚠️ `column-gap` ET NON `gap` : la grille n'a qu'une rangée, une gouttière
     verticale de 96 px n'aurait servi qu'au repli sous 60rem, où les deux cartes
     s'empilent et où cet écart-là serait un trou.

     Elle est prise sur la colonne de l'article, qui est en `1fr` : élargir la
     gouttière ne pousse donc jamais l'encart hors de sa borne. */
  column-gap: var(--une-espace);
  row-gap: var(--une-espace);
  align-items: start;
}

@media (max-width: 60rem) {
  .une__grille {
    grid-template-columns: minmax(0, 1fr);
  }
  /* ⚠️ EN MODE ÉTROIT, L'ENCART NEWSLETTER NE S'AFFICHE PAS — demande d'Hugo le
     28/08/2026. Repliée, la grille le posait sous la carte de la une, où il
     n'était plus un encart de côté mais un troisième bloc à traverser avant
     d'atteindre « Nos articles ».

     ⚠️ ON NE PERD RIEN : le MÊME couple champ + bouton est rendu une seconde
     fois par le bloc `newsletter` qui ferme la page (voir src/blog/index.njk,
     qui passe la même fiche `inscription` aux deux). L'inscription reste donc
     accessible, au pied, là où la maquette la place aussi.

     `display: none` et non `visibility` : on le retire de la vue ET de l'ordre
     de tabulation d'un coup — un champ e-mail invisible mais focusable serait
     un piège au clavier. C'est le même partage que le sommaire d'`etapes` en
     mode étroit.

     Rien ne bouge au large : toute la règle est dans le `mq-down`. */
  .une__newsletter {
    display: none;
  }
}
/* =========================================================================
   LE PANNEAU DE LA UNE — UNE CARTE CLAIRE DANS UNE BOÎTE SOMBRE.

   ⚠️ ELLE ÉTAIT SOMBRE TRANSLUCIDE (le voile blanc à 5 % de la maquette) JUSQU'AU
   21/08/2026 ; Hugo l'a passée en clair le jour même — « adapter la carte de
   l'article à la une comme on a fait d'autres blocs dans des blocs sombres ».
   C'est le motif que le site emploie déjà partout ailleurs : `temoignages`,
   `connecteurs` et `onglets-metiers` posent tous un panneau CLAIR dans leur boîte.

   Et c'est le motif de la règle 5 de MAQUETTE.md : dans une boîte sombre, c'est le
   contenu CLAIR qui ressort. Un panneau sombre sur du bleu nuit se fond — il ne
   tenait ici que par son filet.

   ⚠️ CONSÉQUENCE OBLIGATOIRE : LA CARTE REMET LES RÔLES D'ENCRE — règle 12,
   branche « plusieurs encres ». Elle en porte cinq (titre, corps, légende, accent,
   filet) : les écrire en clair une par une aurait laissé cinq valeurs à reprendre
   le jour où la boîte change de teinte. Les valeurs sont celles que `:root` donne
   à ces rôles, et elles doivent rester en phase avec _tokens.scss. Le doublon est
   assumé pour --border-subtle : sa valeur claire est une rgba littérale, il n'y a
   pas de jeton brut derrière elle. Précédent : `.onglets-metiers__module`.

   ⚠️ LE FOND EST LE BLEU PÂLE, ET IL A FAIT DEUX PAS LE 21/08/2026 : crème
   --c-base-main (ce qu'emploient les trois panneaux clairs cités plus haut), puis
   --c-blue-50 (« tu peux mettre la carte de l'article à la une en fond bleu ? »),
   puis --c-blue-100 (« une teinte plus foncée »).

   ⚠️ --c-blue-100 EST LE CRAN SUIVANT DE L'ÉCHELLE, pas une valeur inventée : le
   site n'a que trois bleus pâles (50, 100, 400) et le 400 n'est pas un fond, c'est
   le filet. Les quatre encres de la carte y tiennent toutes, mesurées : encre de
   titre 13,0 · corps 11,0 · légende 5,6 · accent 4,5. C'est l'accent qui est au
   plus juste — il ne reste rien sous ce fond-là, et un cran de plus demanderait de
   changer d'encre.

   ⚠️ À NE PAS RAPPROCHER DU FOND DE PAGE : --c-blue-100 avait été essayé comme
   fond de page en août et écarté par Hugo, jugé trop soutenu. Ici il ne se mesure
   pas contre le crème mais contre le bleu nuit de la boîte, et la question n'est
   pas la même.

   ⚠️ CE N'EST PAS LE BLEU DU SITE (--c-blue, #3f58d7), et le partage est celui de
   la règle 10 : sur un aplat saturé, les cinq encres de la carte seraient toutes à
   reprendre — l'encre des titres n'y donne que 2,4. Le bleu pâle, lui, garde la
   carte CLAIRE, donc la remise des rôles ci-dessus reste juste telle quelle.

   ⚠️ ET SON ARRONDI EST DE 16px, PAS LES 28px DE LA BOÎTE : deux arrondis
   concentriques de même rayon se concurrenceraient. Même raison et même écart que
   les cartes de `module.cartes` et de /cas-clients/.
   ========================================================================= */
/* L'enveloppe ne fait que porter `data-reveal` et étirer la carte : tout
   l'habillage est sur l'`<article>`. Voir l'avertissement du survol plus bas. */
.une__cellule {
  display: flex;
}

.une__article {
  --section-bg: var(--c-blue-100);
  --bg-page: var(--c-blue-100);
  --text-strong: var(--c-text-main);
  --text-muted: var(--c-fiord);
  --text-accent: var(--c-blue);
  --border-subtle: rgba(44, 42, 46, 0.12);
  --border-strong: rgba(44, 42, 46, 0.24);
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: clamp(var(--sp-4), 1.4vw, var(--sp-6));
  padding: clamp(var(--sp-5), 2vw, var(--sp-8));
  color: var(--text-strong);
  background-color: var(--c-blue-100);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg); /* 16px — la cote de la maquette */
  /* ⚠️ `scale` ET `translate`, JAMAIS `transform` : ce sont des propriétés à
     part, qui se COMPOSENT avec `transform` au lieu d'y être — un
     `transform: none` venu de _reveal.scss ne peut donc pas les annuler. */
  transition: background-color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out), scale var(--dur-base) var(--ease-out), translate var(--dur-base) var(--ease-out);
}

/* ---------- Le survol ----------
   Demandé par Hugo le 21/08/2026 : « la carte de l'article à la une doit aussi
   avoir les effets de hover ». Les quatre mêmes gestes que les cartes de
   /cas-clients/ et que celles de la grille juste en dessous — la carte est un
   objet de la même famille, et trois vocabulaires de survol sur une même page se
   liraient comme un oubli.

   ⚠️ LE ZOOM VAUT LA MOITIÉ DE CELUI DES CARTES DE LA GRILLE, et c'est le point à
   retenir : un facteur d'échelle agit sur la LARGEUR, pas sur la perception. À
   3 % une carte de 400px grandit de 12px, ce panneau de 1000px en gagnerait 30 —
   le même chiffre se lit comme un léger zoom d'un côté et comme un sursaut de
   l'autre. Même arbitrage que `.cas--vedette` face à `.cas--carte`.

   ⚠️ LA TEINTE DE SURVOL EST À 10 % ET NON AUX 4 % DES CARTES BLANCHES, et c'est
   la conséquence du fond bleu : mélanger du bleu dans du bleu pâle ne déplace
   presque rien, là où 4 % suffisent sur du blanc, qui est neutre. Le filet qui
   passe au bleu plein reste le signal principal — la teinte ne fait que
   l'accompagner.

   ⚠️ ET LE SURVOL EST SUR L'`<article>`, PAS SUR SON ENVELOPPE. Celle-ci porte
   `data-reveal`, donc sections/_reveal.scss lui impose son `transform: none` ET sa
   propre liste de `transition`, à (0,3,1) de spécificité et `@use`é en dernier
   exprès pour gagner. Posé sur elle, le survol perdrait son mouvement en silence —
   le défaut a coûté deux diagnostics sur /cas-clients/, où la leçon est écrite au
   long : un élément qui porte `data-reveal` ne doit pas porter en plus une
   animation de survol. */
@media (hover: hover) {
  .une__article:hover {
    background-color: color-mix(in srgb, var(--c-blue) 10%, var(--c-blue-100));
    border-color: var(--c-blue);
    scale: 1.015;
    translate: 0 -2px;
  }
}
/* Le zoom met en mouvement TOUT le contenu de la carte, texte compris : c'est
   exactement ce que `prefers-reduced-motion` demande d'éviter. La teinte et le
   filet restent — ce ne sont pas des mouvements. */
@media (prefers-reduced-motion: reduce) {
  .une__article:hover {
    scale: 1;
    translate: none;
  }
}
/* Le focus clavier se voit sur la carte, sans anneau : c'est le BOUTON qui est
   cliquable, et il porte déjà le sien. */
.une__article:focus-within {
  border-color: var(--c-blue);
}

/* ---------- Le cadre en attente ----------
   Le ratio exact de la maquette (nœud I144:5706;49:3157, 806 × 306) — règle 6.

   ⚠️ IL AVAIT UN HABILLAGE SOMBRE, RETIRÉ LE 21/08/2026 avec le passage de la
   carte en clair : `.apercu` se donne un fond blanc et remet les rôles d'encre en
   contexte clair, ce qui est désormais exactement ce qu'il faut. Une seule cote
   reste donc ici. */
.une__apercu {
  aspect-ratio: 806/306;
}

/* ---------- La ligne de métadonnées ----------
   Catégorie · date · temps de lecture. Les points sont posés par le style : dans
   le DOM, un lecteur d'écran les annoncerait un par un. */
.une__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-3);
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--text-muted);
}

.une__meta > * + *::before {
  content: "·";
  margin-inline-end: var(--sp-3);
}

.une__categorie {
  color: var(--text-accent);
}

/* ---------- Le titre ----------
   ⚠️ ÉCART À LA RÈGLE 3 DE MAQUETTE.md, borné à ce bloc et de même nature que
   celui de `.cas--carte .cas__titre` : --fs-h1 monte à 72px au-delà de 1280px,
   alors que la maquette donne 44px à ce titre. Le clamp ci-dessous PLAFONNE à la
   valeur de la maquette au lieu d'en faire son plancher. Le reste de la police —
   famille, graisse, interlignage, interlettrage — vient toujours de _base.scss.

   --c-blue-zodiac, comme tous les titres du site. L'exception de la règle 5 (« le
   titre passe à --text-strong, il est DANS la boîte ») ne s'applique plus nulle
   part dans ce bloc : la carte est claire depuis le 21/08/2026, et le titre de page
   est sorti de la boîte le même jour. Les deux sont donc à l'encre des titres. */
.une__titre {
  margin: 0;
  max-width: 24ch;
  font-size: clamp(1.75rem, 1.2rem + 1.8vw, 2.75rem); /* 28 → 44px */
  color: var(--c-blue-zodiac);
  text-wrap: balance;
}

/* --text-strong et non --text-muted : c'est du corps de texte. La maquette y met
   son casper, qui est justement ce que le site réserve aux légendes. */
.une__chapeau {
  margin: 0;
  max-width: 62ch;
  font-size: var(--fs-md);
  line-height: var(--lh-normal);
  color: var(--text-strong);
}

.une__pied-cta {
  margin: var(--sp-2) 0 0;
}

/* ---------- La signature ----------
   « Écrit par », le nom, le rôle. Séparée du corps par un filet, comme la
   maquette (nœud I144:5706;49:3182). */
.une__signature {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--sp-2) var(--sp-3);
  margin-block-start: var(--sp-2);
  padding-block-start: clamp(var(--sp-4), 1.5vw, var(--sp-5));
  border-top: 1px solid var(--border-subtle);
  font-size: var(--fs-sm);
  color: var(--text-muted);
}

.une__signature-intitule {
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
}

.une__signature-nom {
  font-weight: var(--fw-semibold);
  color: var(--text-strong);
}

/* =========================================================================
   L'ENCART NEWSLETTER.

   ⚠️ IL PORTE SON PROPRE COUPLE FOND + FILET, écrits en clair sur la teinte fluo
   adoucie, et il ne les tient d'aucun contexte. C'est ce qui le rend juste dans
   la boîte sombre d'aujourd'hui et le rendrait encore juste si elle changeait de
   teinte — même argument que `.badge-section` et que le bloc blanc de logo
   d'`item`, et c'est l'argument qui les fait préférer à une encre héritée dès
   qu'un fond est susceptible de bouger.

   Les deux valeurs sont celles de la maquette (nœud 144:5707) : l'aplat à 5,5 %,
   le filet à 24 %. Aucun texte n'est posé sur cet aplat — il est bien trop dilué
   pour porter quoi que ce soit —, donc rien à mesurer : les encres restent celles
   de `.on-dark`.
   ========================================================================= */
.une__newsletter {
  padding: var(--sp-5);
  background-color: color-mix(in srgb, var(--c-starship) 5.5%, transparent);
  border: 1px solid color-mix(in srgb, var(--c-starship) 24%, transparent);
  border-radius: var(--radius-lg);
  --section-bg: var(--c-blue-dark);
}

/* ⚠️ LES CAPITALES VIENNENT DU STYLE, pas des données : la maquette écrit
   « LA NEWSLETTER », le YAML écrit « La newsletter ». C'est la convention du site
   — un texte reste lisible dans sa fiche, et une traduction n'a pas à porter la
   casse.

   Écart à la règle 3 de même nature que celui du titre plus haut : --fs-h3 monte
   à 32px, la maquette donne 23px à ce titre dans une colonne de 290px. */
.une__newsletter-titre {
  margin: 0 0 var(--sp-3);
  font-size: clamp(1.25rem, 1.15rem + 0.35vw, 1.4375rem); /* 20 → 23px */
  text-transform: uppercase;
  color: var(--text-strong);
}

.une__newsletter-texte {
  margin: 0 0 var(--sp-5);
  font-size: var(--fs-sm);
  line-height: var(--lh-normal);
  color: var(--text-strong);
}

/* ---------- Les boutons dans une colonne étroite ----------
   ⚠️ `.btn` EST NON SÉCABLE PAR DÉFAUT, et il le doit : partout ailleurs sur le
   site un bouton dont le libellé se coupe en deux se lit comme un défaut de mise
   en page. Ici la colonne est bornée à 15 % de la boîte, et « Voir les éditions → »
   sur une seule ligne dépasse la carte — c'est la contrepartie de la borne, pas un
   accident.

   Les trois déclarations vont ensemble et n'ont de sens qu'ici :
     width       le bouton occupe la colonne au lieu de la déborder ;
     padding     le rembourrage du site (26px de chaque côté) mangeait à lui seul
                 un tiers de la largeur disponible ;
     white-space le libellé se plie plutôt que de dépasser.

   `min-height` n'est pas touché : `.btn` le pose en minimum, donc un libellé sur
   deux lignes fait simplement grandir le bouton. */
.une__newsletter .btn {
  width: 100%;
  padding-inline: var(--sp-3);
  white-space: normal;
}

/* =========================================================================
   BLOC « newsletter » — blocs/newsletter.njk.

   Le PRÉ-PIED DE PAGE du blog : une bande bleu nuit soudée au pied de page, un
   titre centré, le champ e-mail et son bouton, puis le CTA vers les éditions.
   Origine : Figma 144:5746.

   ⚠️ C'EST UNE BANDE PLEINE LARGEUR ET NON UNE BOÎTE, contrairement à ce que
   demande la règle 1 de MAQUETTE.md pour un aplat bleu nuit. La raison est en tête
   du gabarit, et elle tient en une phrase : un pré-pied n'est pas posé sur le fond
   de page, il est soudé au pied — une boîte arrondie y laisserait un liseré de
   fond clair entre elle et le pied, soit une arête là où il ne doit pas y en
   avoir. Même raisonnement que la règle 18 pour un hero, à l'autre bout de la
   page.

   Le formulaire lui-même est la primitive partagée `.inscription`
   (_components.scss) : cette feuille ne décide que de sa place.
   ========================================================================= */
.newsletter {
  position: relative;
  background-color: var(--c-blue-dark);
  padding-block: clamp(var(--sp-12), 5vw, 5.5rem);
  text-align: center;
}

/* ⚠️ AUCUNE MARGE SOUS LA BANDE, ET AUCUNE RUPTURE NON PLUS. Le pied de page est
   en --c-blue-zodiac, un cran plus profond que le --c-blue-dark de cette bande :
   le pli se lit à la nuance, sans qu'on ait à poser un filet. C'est ce qui fait du
   couple un seul objet plutôt que deux bandes sombres empilées. */
/* --text-strong et non --c-blue-zodiac : ce titre est sur le fond sombre, où
   l'encre des titres du site vaudrait 1,2. C'est l'exception que la règle 5 de
   MAQUETTE.md décrit, et la seule que ce bloc emprunte.

   Rien d'autre de la police n'est redéclaré (règle 3) : _base.scss pose déjà
   serif, graisse, interlignage et la taille du niveau. */
.newsletter__titre {
  margin: 0 auto;
  max-width: 20ch;
  color: var(--text-strong);
  text-wrap: balance;
}

.newsletter__texte {
  margin: var(--sp-5) auto 0;
  max-width: 52ch;
  font-size: var(--fs-md);
  line-height: var(--lh-normal);
  color: var(--text-strong);
}

/* Le formulaire et son CTA se centrent d'eux-mêmes : la rangée par ses pistes
   (voir `.inscription--rangee`), le bouton par le `text-align` de la section. */
.newsletter__form {
  margin-block-start: clamp(var(--sp-6), 2.5vw, var(--sp-8));
}

/* ---------- L'enveloppe ----------
   Rendue dans les deux cas par le gabarit, et sans AUCUNE déclaration en pré-pied :
   là-bas c'est la section qui porte l'aplat, et le `.container` qu'elle contient
   fait tout le travail de mesure. Elle ne devient un objet qu'en ouverture. */
/* ---------- En ouverture de page : une BOÎTE ----------
   « Met le hero dans un bloc sombre arrondi » — Hugo, 21/08/2026, troisième et
   dernier état de cette ouverture. Le récit des trois est en tête du gabarit ; ce
   qu'il faut retenir ici est que c'est la règle 1 de MAQUETTE.md appliquée sans
   exception : un aplat bleu nuit posé sur le fond de page devient une boîte.

   Les cotes sont celles de TOUTES les boîtes du site, et elles ne se recopient pas
   à la légère — `.temoignages__boite`, `.hero-cas__boite`, `.reperes__boite`,
   `.chaine__boite` portent les mêmes : 96rem, rayon 28px, aucun filet.

   ⚠️ LA SECTION PREND LE FOND DE PAGE, LA BOÎTE LE BLEU NUIT. Et `.on-dark` va sur
   la BOÎTE (posé par le gabarit) : sur la section, la classe redéfinirait
   `--bg-page` dont la section se sert comme fond, et la boîte disparaîtrait dedans.
   C'est le piège que la règle 1 signale nommément.

   ⚠️ LE RYTHME SE DÉDOUBLE. La section porte celui de la PAGE — resserré en haut,
   parce que le bloc suit immédiatement l'en-tête collant qui occupe déjà 71 px ;
   c'est la cote de `.hero-cas`, dans la même situation. La boîte porte celui de
   son CONTENU, repris de la bande de pré-pied. Le rembourrage intérieur, lui, vient
   du `.container` qu'elle enferme : 96rem de boîte pour 80rem de contenu, soit
   8rem de bleu nuit de chaque côté sur grand écran, et le retrait du `.container`
   en dessous. Aucune cote de plus à tenir accordée.

   Et une mesure : `.newsletter__titre` est borné à 20ch, calé sur les titres de
   pré-pied (« Suivez notre actualités », deux mots plus court). « Suivez notre
   newsletter » fait 23 caractères : à 20ch il se casse après « notre », alors que
   la maquette le pose sur une seule ligne.

   Les sélecteurs sont à la même spécificité que ceux qu'ils débordent (0,1,0 et
   0,2,0) : c'est l'ordre d'écriture qui tranche, donc ce bloc reste APRÈS les
   règles de base ci-dessus. */
.newsletter--ouverture {
  background-color: var(--bg-page);
  padding-inline: var(--container-pad);
  padding-block: clamp(var(--sp-5), 2.2vw, var(--sp-8)) var(--sp-section);
}

.newsletter--ouverture .newsletter__boite {
  width: 100%;
  max-width: 96rem;
  margin-inline: auto;
  padding-block: clamp(var(--sp-12), 5vw, 5.5rem);
  border-radius: 1.75rem; /* 28px — règle 1 de MAQUETTE.md */
  background-color: var(--c-blue-dark);
}

/* Le titre reste sur `--text-strong`, comme en pré-pied : il est DANS la boîte
   sombre. C'est l'exception que décrit la règle 5 de MAQUETTE.md, et il n'y a donc
   rien à déclarer — seule la mesure change. */
.newsletter--ouverture .newsletter__titre {
  max-width: 28ch;
}

/* ---------- Le raccord avec le pied de page ----------
   La bande de sortie du pré-pied, ajoutée le 21/08/2026 (« il manque la seconde
   transition halftone vers le footer »).

   ⚠️ ELLE NE VA PAS DU SOMBRE VERS LE CLAIR, contrairement à toutes les autres
   sorties du site, et c'est tout l'intérêt du bouton `--halftone-fond` de la
   primitive : la trame se fond ici dans le BLEU NUIT DU PIED (--c-blue-zodiac) et
   non dans le fond de page. Sans ça on insérerait un dégradé pâle entre la bande
   bleu nuit et le pied bleu nuit — une arête claire là où il faut un pli.

   L'encre de la trame reste `--c-blue-dark`, celle de la bande au-dessus : la
   transition va donc d'un sombre à l'autre, ce qui est exactement le pli qu'on
   voulait tenir.

   Le sélecteur passe par le frère adjacent parce que la primitive `halftone` est
   une macro sans argument de couleur : c'est l'hôte qui pose la variable, et ici
   l'hôte est la section qui précède. */
.newsletter + .halftone--sortie {
  --halftone-fond: var(--c-blue-zodiac);
}

/* =========================================================================
   BLOC « rendez-vous » — blocs/rendez-vous.njk.

   Origine : la section haute de /parler-a-un-expert/ — Figma 144:6570.

   Un panneau fermé coupé en deux : l'ordre du jour de l'échange à gauche,
   l'agenda à droite. Le pourquoi du panneau, du filet par la gouttière et du
   cadre en attente est en tête du gabarit.
   ========================================================================= */
.rendez-vous {
  background-color: var(--bg-page);
  padding-block: var(--sp-section);
}

/* Pose « compacte » au milieu d'une page — voir `compact` dans
   blocs/rendez-vous.njk. Une seule colonne à toutes les largeurs : sans
   `etapes`, l'argumentaire n'a plus la hauteur qui justifiait les deux
   colonnes égales de la pose d'ouverture. */
.rendez-vous--compact .rendez-vous__grille {
  grid-template-columns: minmax(0, 1fr);
  max-width: 34rem;
  margin-inline: auto;
}

.rendez-vous--compact .rendez-vous__titre {
  max-width: none;
}

/* L'AGENDA S'ÉLARGIT EN DESKTOP, ET C'EST UN SEUIL DE CALENDLY — pas un réglage
   de goût. Demandé par Hugo le 15/09/2026 pour l'accueil.

   Le widget Calendly embarqué empile son calendrier et ses créneaux tant que son
   cadre est étroit, et les pose CÔTE À CÔTE au-delà d'un certain seuil. À 34rem la
   colonne compacte en fait 462 px — soit bien en dessous, donc l'agenda rendait sa
   version empilée, la plus haute et la moins lisible.

   ⚠️ 54rem (734 px) N'ONT PAS SUFFI, essayés le 15/09/2026 : le seuil du widget est
   plus haut que les ~680 px annoncés un peu partout. La borne compacte tombe donc
   entièrement en desktop et l'agenda prend la MESURE DU CONTENEUR — 80rem, soit
   1088 px. C'est le maximum disponible sans sortir de la mesure du site, et
   `max-width: none` plutôt que `80rem` écrit en clair pour que la cote suive
   `--container-max` le jour où elle bouge.

   ⚠️ LES PIXELS DE CALENDLY NE SUIVENT PAS L'ÉCHELLE DU SITE. `html` est à 85 %
   (voir _base.scss), donc 1rem vaut 13,6 px ici — mais l'iframe porte son propre
   document, à 16 px, et son seuil de bascule est donc en pixels RÉELS. C'est
   pourquoi chaque cote ci-dessous est doublée de sa valeur en pixels : c'est
   celle-là qui compte pour le widget, et changer l'échelle de la racine la
   déplacerait sans rien dire.

   Le ratio portrait de la maquette (527 × 682) tombe du même coup : il décrit un
   agenda EN COLONNE, à côté de l'argumentaire de /parler-a-un-expert/. Étiré sur
   1088 px il demanderait 1408 px de haut, soit 600 px de vide sous un calendrier
   qui n'en a plus besoin une fois étalé. La hauteur est donc posée en clair — et
   posée sur le cadre en attente à l'identique, sans quoi la page sauterait le jour
   où l'adresse est retirée (règle 6 de MAQUETTE.md).

   61rem = 830 px RÉELS, ET C'EST MESURÉ, pas estimé — captures du widget en vue
   embarquée à plusieurs largeurs et hauteurs, le 15/09/2026, un jour sélectionné.

   ⚠️ LE WIDGET PLAFONNE À ~700 px DE HAUT. En dessous il se comprime et fait
   défiler sa colonne de créneaux ; au-dessus il ne grandit plus et on n'ajoute que
   du blanc. 830 px = ses ~65 px de marge haute + ses 700 px + autant de marge en
   bas : c'est le point exact où il atteint son plafond sans vide superflu. Monter
   plus haut ne supprimera AUCUN défilement, c'est la seule chose à retenir avant
   d'y retoucher.

   ⚠️ ET IL RESTE UN DÉFILEMENT, IRRÉDUCTIBLE : la colonne de créneaux est bornée
   par le plafond du widget, donc une journée à dix créneaux en montre neuf et fait
   défiler le dernier. C'est interne à Calendly, dans son propre document ; aucune
   cote de notre côté ne l'atteint.

   Tout est dans un `mq-up` : en dessous de 960 px la pose ne bouge pas d'un pixel. */
@media (min-width: 60rem) {
  .rendez-vous--compact .rendez-vous__grille {
    max-width: none;
  }
  .rendez-vous--compact .rendez-vous__agenda,
  .rendez-vous--compact .rendez-vous__attente {
    aspect-ratio: auto;
    height: 61rem; /* 830px réels — voir la note ci-dessus */
    min-height: 0;
  }
}
/* ⚠️ AUCUN PANNEAU, ET C'EST UNE DÉCISION D'HUGO du 21/08/2026 — « enlève le
   tableau pour Discutons de votre projet ». La section a été rendue en `.cadre`
   coupé par la gouttière, comme `citation` et comme la maquette le dessine ; le
   pourquoi du retrait est en tête du gabarit. Ne pas le rétablir.

   Il ne reste donc de la maquette que la RÉPARTITION en deux colonnes égales
   (639 + 639 dans 1280). `minmax(0, 1fr)` et non `1fr` : la colonne de droite
   contient un `<iframe>`, dont la largeur intrinsèque (300 px par défaut, et
   davantage une fois chargé) tirerait la colonne au-delà de sa part si le plancher
   restait `auto`.

   La gouttière est celle des grandes séparations du site et non plus 1 px : sans
   panneau, c'est le seul écart qui dise que les deux colonnes sont deux choses. */
.rendez-vous__grille {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(var(--sp-8), 4vw, var(--sp-16));
}

.rendez-vous__argumentaire,
.rendez-vous__creneau {
  display: flex;
  flex-direction: column;
}

/* Le badge qui annonce la colonne. Son apparence vit dans `.badge-section`
   (_components.scss) : ici on ne règle que sa place. */
.rendez-vous__surtitre {
  margin-block-end: clamp(var(--sp-5), 2vw, var(--sp-8));
}

/* ---------- L'argumentaire ----------
   Le titre ne déclare rien de sa police : _base.scss pose déjà le titrage, le
   gras et la taille du niveau (règle 3). On ne touche qu'à la marge, à la mesure
   et à l'encre — celle des titres du site.

   La mesure est en `ch` et volontairement courte : à 460 px la maquette tient
   « Discutons de votre projet » sur une seule ligne, et c'est ce qui donne au
   titre son aplomb. Au-delà de 20 caractères par ligne il se casse n'importe où. */
.rendez-vous__titre {
  margin: 0;
  max-width: 20ch;
  color: var(--c-blue-zodiac);
  text-wrap: balance;
}

/* ⚠️ AUCUN FILET ENTRE LES ENTRÉES — C'EST L'ESPACE QUI SÉPARE.

   La maquette en pose un au-dessus de chacune des cinq (Figma 144:6579 à
   144:6611), et ils ont été rendus tels quels avant qu'Hugo ne les retire le
   21/08/2026 : « je pense pas qu'il faille mettre la partie Discutons de votre
   projet dans un tableau ». Un filet par entrée quadrillait la colonne, ce qui la
   faisait lire comme le tableau d'à côté au lieu d'une liste.

   C'est le même parti que les entrées empilées de `texte-module` (dont le tireté a
   sauté le 18/08/2026, pour la même raison) et que les garanties de
   `liste-glyphes`. Ne pas le rétablir sans le lui demander.

   La gouttière est celle des entrées de `texte-module`, à l'identique : deux piles
   d'entrées du site qui ne respireraient pas pareil se verraient. */
.rendez-vous__etapes {
  margin: clamp(var(--sp-6), 2.4vw, var(--sp-10)) 0 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: clamp(var(--sp-5), 2vw, var(--sp-8));
}

/* ---------- Le créneau ----------
   La colonne pousse sa mention en bas : `margin-block-start: auto` sur la
   dernière ligne, et non une chaîne de `subgrid` — les deux cases d'une rangée
   sont déjà de même hauteur, donc cette seule déclaration fait tomber la mention
   au pied de la colonne quelle que soit la hauteur de l'agenda. Même dispositif
   que `.cas__chiffres` et `.grille-articles__pied`. */
.rendez-vous__mention {
  margin-block-start: auto;
  padding-block-start: clamp(var(--sp-4), 1.6vw, var(--sp-6));
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  line-height: var(--lh-normal);
  color: var(--text-muted);
}

/* L'agenda embarqué. Le ratio de la maquette (527 × 682) plutôt qu'une hauteur
   fixe : la colonne se resserre avec la page, la hauteur suit.

   ⚠️ ET UN `min-height` PAR-DESSUS, parce que Calendly a un plancher de lisibilité
   qui n'est pas celui du ratio : en dessous de ~40rem son calendrier fait défiler
   son propre mois sur deux écrans. C'est la hauteur de la maquette (682 px), qui
   est déjà la hauteur d'un cadre dans lequel Calendly fait défiler ses 978 px de
   contenu — le défilement interne est donc voulu, pas un débord.

   ⚠️ IL PORTE LE MÊME BORD QUE LE CADRE EN ATTENTE, ET IL LE FAUT : c'est ce
   cadre-là qu'il remplace le jour où l'adresse Calendly est renseignée. Sans ça,
   la page changerait de forme au branchement, alors que tout l'objet du cadre en
   attente est que rien ne bouge (règle 6 de MAQUETTE.md). Les deux règles
   doivent donc rester accordées — celle-ci et `.rendez-vous__attente` plus bas.

   CE BORD EST CELUI DU PANNEAU FERMÉ DU SITE depuis le 16/09/2026, demandé par
   Hugo : coins CARRÉS et filet BLEU éclairci, soit deux des quatre caractères de
   `.cadre` (voir _components.scss). Il portait avant le filet gris et l'arrondi
   de `.apercu` — le gris ne disait rien, et l'arrondi faisait lire l'agenda
   comme une carte alors que c'est un panneau. `--filet-panneau` plutôt qu'un
   bleu écrit en clair : c'est LE filet du site, et il bouge d'un seul endroit.

   ⚠️ L'ARRONDI EST REMIS À ZÉRO EXPLICITEMENT sur le cadre en attente, et ce
   n'est pas de la finition : `.apercu` en déclare un, donc l'omettre laisserait
   les deux cadres à des formes différentes, sans rien pour le signaler.

   La maquette borde d'ailleurs le cadre (nœud 144:6624, dont l'iframe est en
   retrait d'un pixel). Ce qu'elle ajoute et qu'on ne reprend pas, c'est l'ombre
   portée — règle 5. */
.rendez-vous__agenda {
  width: 100%;
  aspect-ratio: 527/682;
  min-height: 42.625rem; /* 682px */
  border: var(--filet-panneau);
  border-radius: 0;
}

/* Le cadre en attente de l'agenda. Il garde un filet, contrairement au portrait
   de `citation` qui le perd : là le cadre EST la case d'un panneau et son arête
   est celle de la gouttière. Ici il n'y a plus de panneau du tout — c'est le SEUL
   cadre de la section, et sans filet il n'aurait aucun bord.

   Il ne prend PLUS celui de `.apercu` : depuis le 16/09/2026 les deux cadres
   portent le bord du panneau fermé du site — coins carrés, filet bleu. Le
   pourquoi, et pourquoi l'arrondi est remis à zéro en toutes lettres, est sur
   `.rendez-vous__agenda` juste au-dessus.

   Même ratio que l'agenda : c'est tout l'objet de la règle 6 de MAQUETTE.md —
   la page ne doit pas sauter le jour où l'adresse Calendly est renseignée. */
.rendez-vous__attente {
  aspect-ratio: 527/682;
  min-height: 42.625rem; /* 682px */
  border: var(--filet-panneau);
  border-radius: 0;
}

@media (max-width: 60rem) {
  /* Une colonne : l'argumentaire au-dessus, l'agenda en dessous. */
  .rendez-vous__grille {
    grid-template-columns: minmax(0, 1fr);
  }
  /* Le titre reprend sa mesure de lecture : à pleine largeur, 20ch le laisserait
     seul au milieu d'une ligne vide. */
  .rendez-vous__titre {
    max-width: 28ch;
  }
}
@media (max-width: 48rem) {
  /* Le plancher de Calendly ne tient plus sur un téléphone en même temps que le
     ratio : à 320 px de large, 527/682 donnerait 414 px de haut là où le
     `min-height` en demande 682. C'est le `min-height` qui gagne — donc autant
     laisser tomber le ratio, qui ne décrit plus rien, et garder la seule
     hauteur utile. */
  .rendez-vous__agenda,
  .rendez-vous__attente {
    aspect-ratio: auto;
  }
}
/* L'amorce facultative au-dessus du titre (`preTitre`) : AU CORPS DU TITRE
   lui-même. L'amorce pose la question en bleu nuit, et c'est alors le TITRE
   qui passe à l'accent — la réponse (Céline, 01/10/2026 : « seul Discutons
   de votre projet est en bleu »). Le titre est un <h2> en mode compact, un
   <h1> sinon : l'amorce suit. Sans amorce, rien ne change. */
.rendez-vous__pretitre {
  margin: 0;
  font-family: var(--font-titre);
  font-size: var(--fs-h1);
  font-weight: var(--fw-bold);
  line-height: var(--lh-tight);
  letter-spacing: var(--ls-display);
  color: var(--c-blue-zodiac);
}

.rendez-vous--compact .rendez-vous__pretitre {
  font-size: var(--fs-h2);
}

.rendez-vous__pretitre + .rendez-vous__titre {
  max-width: none;
  color: var(--text-accent);
}

/* =========================================================================
   BLOC « diaporama » — blocs/diaporama.njk.

   Un manège : la slide courante au centre, en grand ; ses voisines
   rapetissent et pâlissent avec leur distance au centre. Cette distance
   (`--eloignement`, 0 au centre, 1 pour la voisine immédiate, plafonnée à 2)
   est mesurée par assets/js/diaporama.js à chaque défilement ; la feuille
   n'en tire que l'échelle et l'opacité. Sans script, elle vaut 0 partout :
   toutes les slides à leur taille, rien de masqué.
   ========================================================================= */
.diaporama {
  /* La largeur de la slide AU CENTRE. Ses voisines la prennent aussi dans la
     mise en page, puis rapetissent par `scale` — ce qui ne déplace rien et
     laisse l'accrochage natif tomber juste. */
  /* ~420 px au centre sur un écran de 1440. Essayée à 520 le 01/10/2026
     (« pas assez lisible » à 340), jugée trop grande le même jour : la lecture
     du détail passe par la loupe — un clic sur la slide centrale l'ouvre en
     plein écran (plus bas). */
  --diaporama-slide: clamp(17rem, 29vw, 31rem);
  --diaporama-gouttiere: clamp(var(--sp-2), 1vw, var(--sp-4));
  padding-block: clamp(var(--sp-12), 5vw, var(--sp-20));
}
@media (max-width: 48rem) {
  .diaporama {
    --diaporama-slide: 72vw;
  }
}

/* ---------- L'en-tête ---------- */
.diaporama__entete {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-3);
  max-width: 52rem;
  margin-inline: auto;
  text-align: center;
}

.diaporama__titre {
  margin: 0;
  font-size: var(--fs-h3);
  color: var(--c-blue-zodiac);
}

.diaporama__chapeau {
  margin: 0;
  max-width: 60ch;
  font-size: var(--fs-lead);
  line-height: var(--lh-normal);
}

/* ---------- Le manège ---------- */
.diaporama__cadre {
  margin-top: clamp(var(--sp-6), 2.5vw, var(--sp-10));
}

/* ⚠️ `.diaporama__cadre` DEVANT, ET IL LE FAUT : la remise à zéro des listes
   de _base.scss (`ol[class] { padding: 0 }`) est plus spécifique qu'une classe
   seule et effaçait le rembourrage — la première slide ne pouvait alors plus
   venir au centre, et le manège démarrait sur la troisième. */
.diaporama__cadre .diaporama__piste {
  position: relative; /* référence des `offsetLeft` que lit le script */
  display: flex;
  align-items: center;
  gap: var(--diaporama-gouttiere);
  margin: 0;
  /* Le rembourrage qui permet à la PREMIÈRE et à la DERNIÈRE slide de venir
     au centre : une demi-largeur d'écran moins une demi-slide, de chaque côté. */
  padding: var(--sp-4) calc(50% - var(--diaporama-slide) / 2);
  list-style: none;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
}
.diaporama__cadre .diaporama__piste::-webkit-scrollbar {
  display: none;
}
.diaporama__cadre .diaporama__piste:focus-visible {
  outline: 2px solid var(--c-blue);
  outline-offset: -2px;
}

.diaporama__slide {
  --eloignement: 0;
  flex: none;
  inline-size: var(--diaporama-slide);
  scroll-snap-align: center;
  /* Le zoom : 100 % au centre, 78 % pour la voisine, 56 % au-delà. */
  scale: calc(1 - 0.22 * var(--eloignement));
  opacity: calc(1 - 0.5 * min(var(--eloignement), 1.3));
}
.diaporama__slide img {
  display: block;
  inline-size: 100%;
  block-size: auto;
  border-radius: var(--radius-cadre);
  background-color: var(--c-blue-100); /* le temps que l'image arrive */
}

/* Les voisines se cliquent : le script les amène au centre. La classe n'est
   posée que par lui — sans script, rien ne promet un clic qui ne ferait rien. */
.diaporama__slide--voisine {
  cursor: pointer;
}

/* ---------- Flèches et compteur ---------- */
.diaporama__nav {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: var(--sp-3);
  margin-top: var(--sp-4);
}

.diaporama__compteur {
  min-inline-size: 3.5em;
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  text-align: center;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}
.diaporama__compteur[hidden] {
  display: none;
}

.diaporama__fleche {
  display: grid;
  place-content: center;
  inline-size: 3rem;
  block-size: 3rem;
  padding: 0;
  font-size: var(--fs-md);
  color: var(--c-blue-zodiac);
  background-color: var(--c-white);
  border: var(--filet-panneau);
  border-radius: var(--radius-full);
  cursor: pointer;
  transition: background-color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out), opacity var(--dur-fast) var(--ease-out);
}
.diaporama__fleche[hidden] {
  display: none;
}
.diaporama__fleche {
  /* Au bout du manège : visible mais éteinte, pour que la paire ne bouge pas. */
}
.diaporama__fleche:disabled {
  opacity: 0.35;
  cursor: default;
}

/* La slide centrale s'agrandit au clic : le curseur l'annonce. La classe
   n'est posée que par le script, qui seul sait ouvrir la loupe. */
.diaporama__slide--centrale {
  cursor: zoom-in;
}

/* =========================================================================
   LA LOUPE — la slide en plein écran, un <dialog> créé par diaporama.js.
   Échap, le bouton ✕ ou un clic sur le voile la ferment ; les flèches (et
   celles du clavier) changent de slide, et le manège suit.
   ========================================================================= */
.diaporama__loupe {
  inline-size: 100vw;
  block-size: 100dvh;
  max-inline-size: none;
  max-block-size: none;
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
}
.diaporama__loupe[open] {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--sp-4);
  padding: var(--sp-6);
}
.diaporama__loupe::backdrop {
  background-color: color-mix(in srgb, var(--c-blue-zodiac) 88%, transparent);
}

.diaporama__loupe-image {
  justify-self: center;
  max-inline-size: 100%;
  max-block-size: calc(100dvh - 2 * var(--sp-6));
  inline-size: auto;
  block-size: auto;
  border-radius: var(--radius-cadre);
}

.diaporama__loupe .diaporama__fleche {
  color: var(--c-white);
  background-color: color-mix(in srgb, var(--c-blue-zodiac) 70%, transparent);
  border: 1px solid rgba(255, 255, 255, 0.3);
}

.diaporama__loupe-fermer {
  position: absolute;
  inset-block-start: var(--sp-4);
  inset-inline-end: var(--sp-4);
  font-size: var(--fs-base);
}

.diaporama__loupe-compteur {
  position: absolute;
  inset-block-end: var(--sp-4);
  inset-inline: 0;
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  text-align: center;
  color: var(--c-white);
}

/* Sur téléphone, les flèches passent sous l'image : à côté, elles lui
   prendraient le tiers de la largeur. */
@media (max-width: 48rem) {
  .diaporama__loupe[open] {
    grid-template-columns: 1fr 1fr;
    grid-template-rows: minmax(0, 1fr) auto;
    padding: var(--sp-12) var(--sp-3) var(--sp-10);
  }
  .diaporama__loupe-image {
    grid-column: 1/-1;
    grid-row: 1;
    max-block-size: 100%;
  }
  .diaporama__loupe [data-loupe-prec] {
    grid-row: 2;
    justify-self: end;
  }
  .diaporama__loupe [data-loupe-suiv] {
    grid-row: 2;
    justify-self: start;
  }
}
@media (hover: hover) {
  .diaporama__loupe .diaporama__fleche:not(:disabled):hover {
    color: var(--c-blue-zodiac);
  }
  .diaporama__fleche:not(:disabled):hover {
    background-color: var(--c-fluo);
    border-color: var(--c-fluo);
  }
}
/* ---------- Le CTA, centré ---------- */
.diaporama__appel {
  display: flex;
  justify-content: center;
  margin-top: clamp(var(--sp-6), 2.5vw, var(--sp-8));
}

/* =========================================================================
   SECTION « probleme-resume » — sections/probleme-resume.njk.

   Le titre centré, puis les quatre cartes « Votre quotidien » de l'accueil
   en grille. Les cartes gardent leur dessin (`.quotidien__carte*`, dans
   _quotidien.scss) ; seules leur disposition et, dans une case plus étroite,
   la taille de leur intitulé changent ici. La section qui suit
   (`conclusion-bloc`) est incluse telle quelle.
   ========================================================================= */
.probleme-resume {
  padding-top: var(--sp-section);
}

/* 64rem et non 52rem : le titre de l'accueil y tient sur DEUX lignes au lieu
   de trois (Céline, 01/10/2026). Le chapeau garde sa propre mesure. */
.probleme-resume__entete {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-4);
  max-width: 64rem;
  margin-inline: auto;
  text-align: center;
}

.probleme-resume__titre {
  margin: 0;
  color: var(--c-blue-zodiac);
}

.probleme-resume__chapeau {
  margin: 0;
  max-width: 62ch;
  font-size: var(--fs-lead);
  line-height: var(--lh-normal);
}

.probleme-resume__sous-titre {
  margin: clamp(var(--sp-10), 4vw, var(--sp-16)) 0 var(--sp-5);
  text-align: center;
  color: var(--c-blue-zodiac);
}

.probleme-resume__cartes {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: clamp(var(--sp-3), 1.2vw, var(--sp-5));
  margin: 0;
  padding: 0;
  list-style: none;
}
@media (max-width: 60rem) {
  .probleme-resume__cartes {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}
@media (max-width: 48rem) {
  .probleme-resume__cartes {
    grid-template-columns: minmax(0, 1fr);
  }
}
.probleme-resume__cartes {
  /* Quatre cartes sur une rangée : l'intitulé descend d'un cran et la carte
     resserre son rembourrage, sinon un titre de cinq mots tenait sur quatre
     lignes. Le bouton est celui que la carte expose, pas une surcharge. */
}
.probleme-resume__cartes .quotidien__carte {
  --carte-titre-taille: var(--fs-md);
  gap: var(--sp-3);
  padding: clamp(var(--sp-5), 1.6vw, var(--sp-6));
}

/* ---------- Le pourquoi ----------
   `.conclusion-bloc` porte les mesures de la colonne ; on ne garde que son
   rembourrage haut : la section suivante pose sa propre gouttière. */
.probleme-resume__pourquoi {
  padding-bottom: 0;
}

/* L'ordre des groupes de /irritants/ — rien d'autre, voir en tête du fichier. */
/* =========================================================================
   LANDING PAGE DES POSTS LINKEDIN — src/irritants.njk.

   La page réemploie les blocs du site ; cette feuille ne règle que ce qui lui
   est propre :

     la VARIANTE DU <h1> et le MANÈGE affiché — pilotés par l'attribut que
       assets/js/irritants.js pose sur <html> d'après le lien
       (`data-irritant-entree="<rang>"`) ;
     les AUTRES PROBLÉMATIQUES en quinconce (`.irritants-autre`) ;
     les QUATRE PILIERS en cartes (`.irritants-pilier`) ;
     deux espacements entre blocs du site.

   ⚠️ LE « 3 » EST ÉCRIT EN DUR, et c'est le nombre d'irritants de
   src/_data/irritants.yaml. Un quatrième demanderait de passer $n à 4 ici.
   ========================================================================= */
/* ---------- La variante du <h1> et le manège du post ----------
   Sans paramètre : le texte par défaut, et aucun manège — la page s'ouvre
   sur le quinconce des trois problématiques. */
.irritants-rappel {
  display: none;
}

.irritants-rappel--defaut {
  display: inline;
}

.irritants-entree__manege {
  display: none;
}

[data-irritant-entree="0"] .irritants-rappel--defaut {
  display: none;
}
[data-irritant-entree="0"] .irritants-rappel[data-irritant-rang="0"] {
  display: inline;
}
[data-irritant-entree="0"] .irritants-entree__manege[data-irritant-rang="0"] {
  display: block;
}

[data-irritant-entree="1"] .irritants-rappel--defaut {
  display: none;
}
[data-irritant-entree="1"] .irritants-rappel[data-irritant-rang="1"] {
  display: inline;
}
[data-irritant-entree="1"] .irritants-entree__manege[data-irritant-rang="1"] {
  display: block;
}

[data-irritant-entree="2"] .irritants-rappel--defaut {
  display: none;
}
[data-irritant-entree="2"] .irritants-rappel[data-irritant-rang="2"] {
  display: inline;
}
[data-irritant-entree="2"] .irritants-entree__manege[data-irritant-rang="2"] {
  display: block;
}

/* Le pré-titre « Si vous êtes ici, c'est sûrement que… » : seulement quand
   on arrive d'un post — sans paramètre, le <h1> par défaut se suffit. En
   phrase et non en chasse fixe capitale : c'est le début d'une phrase que le
   <h1> termine. */
#accroche .hero-page__eyebrow {
  display: none;
  justify-content: center;
  font-family: var(--font-sans);
  font-size: var(--fs-eyebrow-lg);
  letter-spacing: normal;
  text-transform: none;
}

[data-irritant-entree] #accroche .hero-page__eyebrow {
  display: flex;
}

/* Sans le filet qui précède les surtitres du site (Céline, 01/10/2026). */
#accroche .hero-page__filet {
  display: none;
}

/* Le même pré-titre, au-dessus du titre des autres problématiques
   (« Et ce n'est pas tout… ») : les deux phrases se répondent. */
.irritants-pretitre {
  margin: 0;
  font-size: var(--fs-eyebrow-lg);
  font-weight: var(--fw-medium);
  color: var(--text-accent);
}

/* Le manège du post, rapproché du titre : il fait partie de l'ouverture.
   Sa phrase d'introduction redirait le <h1>, elle est masquée. */
.irritants-entree .diaporama {
  padding-top: var(--sp-4);
}

.irritants-entree .diaporama__entete {
  display: none;
}

.irritants-entree .diaporama__cadre {
  margin-top: 0;
}

/* ---------- Les autres problématiques, en quinconce ---------- */
.irritants-autres {
  padding-block: var(--sp-section);
  /* Les boucles du tracé débordent par construction — même garde-fou que
     `.liste-glyphes`. */
  overflow-x: clip;
}

.irritants-autres__entete {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-3);
  max-width: 52rem;
  margin: 0 auto clamp(var(--sp-10), 4vw, var(--sp-16));
  text-align: center;
}

.irritants-autres__titre {
  margin: 0;
  color: var(--c-blue-zodiac);
}

.irritants-autres__chapeau {
  margin: 0;
  max-width: 56ch;
  font-size: var(--fs-lead);
  line-height: var(--lh-normal);
}

/* Le quinconce donne à chaque entrée PLUS que la moitié de la largeur
   (3 : 2) : le visuel est grand, le texte garde une mesure lisible. */
@media (min-width: 60rem) {
  .irritants-autres .quinconce__entree--gauche {
    grid-template-columns: minmax(0, 3fr) minmax(0, 2fr);
  }
  .irritants-autres .quinconce__entree--droite {
    grid-template-columns: minmax(0, 2fr) minmax(0, 3fr);
  }
}
/* Une entrée : le visuel et le texte côte à côte. À gauche, le visuel ouvre
   la ligne ; à droite, il la ferme, et le texte se cale sur le bord droit
   (Céline, 01/10/2026 : « l'image tout à droite »). */
.irritants-autre {
  display: grid;
  grid-template-columns: minmax(0, 17rem) minmax(0, 1fr);
  gap: var(--sp-8);
  align-items: center;
}
@media (max-width: 48rem) {
  .irritants-autre {
    grid-template-columns: minmax(0, 1fr);
  }
}

.quinconce__entree--droite .irritants-autre {
  grid-template-columns: minmax(0, 1fr) minmax(0, 17rem);
}
.quinconce__entree--droite .irritants-autre .irritants-autre__visuel {
  order: 2;
}
.quinconce__entree--droite .irritants-autre .irritants-autre__texte {
  align-items: flex-end;
  text-align: right;
}
@media (max-width: 48rem) {
  .quinconce__entree--droite .irritants-autre {
    grid-template-columns: minmax(0, 1fr);
  }
  .quinconce__entree--droite .irritants-autre .irritants-autre__visuel {
    order: 0;
  }
  .quinconce__entree--droite .irritants-autre .irritants-autre__texte {
    align-items: flex-start;
    text-align: left;
  }
}

.irritants-autre__visuel {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
}

/* La slide est un bouton : elle ouvre le carrousel en plein écran. */
.irritants-autre__slide,
.irritants-autre__vignettes button {
  position: relative;
  display: block;
  inline-size: 100%;
  padding: 0;
  border: 0;
  background: none;
  cursor: zoom-in;
  transition: transform var(--dur-fast) var(--ease-out), opacity var(--dur-fast) var(--ease-out);
}
.irritants-autre__slide img,
.irritants-autre__vignettes button img {
  display: block;
  inline-size: 100%;
  block-size: auto;
  border-radius: var(--radius-cadre);
}
.irritants-autre__slide:focus-visible,
.irritants-autre__vignettes button:focus-visible {
  outline: 2px solid var(--c-blue);
  outline-offset: 3px;
  border-radius: var(--radius-cadre);
}

.irritants-autre__slide:hover {
  transform: translateY(-3px);
}

/* Les slides suivantes en vignettes, comme sous un post : elles disent qu'il
   y a une suite, et chacune ouvre la loupe à sa place. */
.irritants-autre__vignettes {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: var(--sp-2);
  margin: 0;
  padding: 0;
  list-style: none;
}
.irritants-autre__vignettes button {
  opacity: 0.7;
}
.irritants-autre__vignettes button img {
  border-radius: var(--radius-sm);
}
.irritants-autre__vignettes button:hover {
  opacity: 1;
}

/* Le compteur, en pastille fluo dans le coin de la grande slide. */
.irritants-autre__nombre {
  position: absolute;
  inset-block-start: var(--sp-3);
  inset-inline-end: var(--sp-3);
  padding: 0.2rem var(--sp-3);
  border-radius: var(--radius-full);
  background-color: var(--c-fluo);
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  letter-spacing: var(--ls-wide);
  color: var(--c-text-main);
}

/* Le texte passe AU-DESSUS du tracé en pointillé, sur le fond de la page :
   les entrées étant plus larges que dans le quinconce des garanties, le tracé
   les traversait de part en part. */
.irritants-autre__texte {
  position: relative;
  z-index: 1;
  padding: var(--sp-3);
  background-color: var(--bg-page);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--sp-3);
}

.irritants-autre__titre {
  margin: 0;
  font-size: var(--fs-h3);
  color: var(--c-blue-zodiac);
}

.irritants-autre__chapeau {
  margin: 0;
  line-height: var(--lh-normal);
}

.irritants-autre__cta {
  margin-top: var(--sp-2);
}

/* ---------- Les quatre piliers ----------
   Une carte par brique, à l'aplat sombre de son pilier — les teintes des
   modules de « Au cœur de Biron » sur l'accueil —, avec son illustration en
   tête. Quatre de front sur grand écran : l'ensemble tient sur une hauteur
   d'écran (Céline, 01/10/2026 : « il ne faut pas que ça prenne trop de
   place »). */
.irritants-piliers {
  padding-block: var(--sp-section);
}

.irritants-piliers__titre {
  margin: 0 0 clamp(var(--sp-8), 3vw, var(--sp-12));
  text-align: center;
  color: var(--c-blue-zodiac);
}

.irritants-piliers__liste {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  /* Le plancher de la rangée : sa hauteur AU REPOS, mesurée par
     assets/js/irritants.js. Sans lui, la carte la plus longue rapetissait au
     survol (ses textes réduits) et toute la rangée remontait d'un cran. */
  grid-auto-rows: minmax(var(--piliers-rang, auto), auto);
  gap: clamp(var(--sp-3), 1.2vw, var(--sp-5));
  margin: 0;
  padding: 0;
  list-style: none;
}
@media (max-width: 60rem) {
  .irritants-piliers__liste {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}
@media (max-width: 48rem) {
  .irritants-piliers__liste {
    grid-template-columns: minmax(0, 1fr);
  }
}

.irritants-pilier {
  --pilier-fond: var(--c-blue-dark);
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  padding: clamp(var(--sp-5), 1.6vw, var(--sp-6));
  border-radius: 1.25rem; /* le rayon des modules de « Au cœur de Biron » */
  background-color: var(--pilier-fond);
  --section-bg: var(--pilier-fond);
}

.irritants-pilier--dictionnaire {
  --pilier-fond: var(--c-pilier-dictionnaire-sombre);
}

.irritants-pilier--plateforme {
  --pilier-fond: var(--c-pilier-plateforme-sombre);
}

.irritants-pilier--connecteur {
  --pilier-fond: var(--c-pilier-connecteur-sombre);
}

.irritants-pilier--integrations {
  --pilier-fond: var(--c-pilier-integrations-sombre);
}

/* L'illustration : une hauteur commune aux quatre, l'image entière dedans.
   Les exports n'ont pas le même ratio (677 × 520 à 900 × 1030). */
.irritants-pilier__visuel {
  /* La zone d'image PREND LA HAUTEUR QUI RESTE dans la carte : au survol, les
     textes rapetissent et c'est elle qui récupère la place, sans que la carte
     ni la rangée ne changent de hauteur — c'est ce qui empêche les cartes
     voisines de tressauter. L'image est posée en absolu pour ne jamais
     peser sur cette hauteur. */
  position: relative;
  flex: 1 1 auto;
  min-block-size: clamp(10rem, 15vw, 14rem);
  margin-bottom: var(--sp-3);
  transition: margin var(--dur-base) var(--ease-out);
}
.irritants-pilier__visuel img {
  position: absolute;
  inset: 0;
  display: block;
  inline-size: 100%;
  block-size: 100%;
  object-fit: contain;
}

/* Le bouton qui ouvre l'illustration en plein écran. */
.irritants-pilier__visuel button {
  position: absolute;
  inset: 0;
  display: block;
  inline-size: 100%;
  block-size: 100%;
  padding: 0;
  border: 0;
  background: none;
  cursor: zoom-in;
}
.irritants-pilier__visuel button:focus-visible {
  outline: 2px solid var(--c-fluo);
  outline-offset: 3px;
}

/* AU SURVOL, LA CARTE ENTIÈRE S'AGRANDIT, par-dessus ses voisines (Céline,
   01/10/2026 : « toute la carte qui grandit au centre pour bien la voir »).
   La première grandit depuis son bord gauche et la dernière depuis son bord
   droit : elles restent ainsi dans le conteneur au lieu de sortir de l'écran.
   Un `scale` grossit tout d'un bloc — image et texte — sans rien déplacer
   autour. Seulement sur grand écran, à la souris, et sans
   `prefers-reduced-motion` ; ailleurs, un clic sur l'image l'ouvre en grand. */
.irritants-pilier {
  position: relative;
  transform-origin: center;
  transition: transform var(--dur-base) var(--ease-out);
}

.irritants-piliers__liste > .irritants-pilier:first-child {
  transform-origin: left center;
}

.irritants-piliers__liste > .irritants-pilier:last-child {
  transform-origin: right center;
}

@media (min-width: 60rem) and (hover: hover) and (prefers-reduced-motion: no-preference) {
  .irritants-pilier:hover,
  .irritants-pilier:focus-within {
    z-index: 2;
    transform: scale(1.55);
  }
  /* L'IMAGE GRANDIT PLUS QUE LA CARTE (Céline, 01/10/2026 : « grossir
     seulement plus l'image, les textes ne sont pas obligés d'être très
     gros »). Elle mange les marges latérales de la carte et prend de la
     hauteur ; les textes, eux, sont ramenés de 1/1,55 pour s'afficher à
     la taille du corps de texte malgré le zoom. La hauteur gagnée par
     l'image est à peu près rendue par les textes : la carte ne s'allonge
     presque pas. */
  .irritants-pilier:hover .irritants-pilier__visuel,
  .irritants-pilier:focus-within .irritants-pilier__visuel {
    margin-inline: calc(-1 * clamp(var(--sp-5), 1.6vw, var(--sp-6)) + var(--sp-1));
  }
  .irritants-pilier:hover .irritants-pilier__accroche,
  .irritants-pilier:focus-within .irritants-pilier__accroche {
    font-size: calc(var(--fs-mono) / 1.2);
  }
  .irritants-pilier:hover .irritants-pilier__titre,
  .irritants-pilier:focus-within .irritants-pilier__titre {
    font-size: calc(var(--fs-md) / 1.4);
  }
  .irritants-pilier:hover .irritants-pilier__texte,
  .irritants-pilier:focus-within .irritants-pilier__texte {
    font-size: calc(var(--fs-base) / 1.55);
  }
}
.irritants-pilier__accroche {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--c-fluo);
}

.irritants-pilier__titre {
  margin: 0;
  font-size: var(--fs-md);
  color: var(--c-white);
}

.irritants-pilier__texte {
  margin: 0;
  font-size: var(--fs-xs);
  line-height: var(--lh-normal);
  color: var(--c-white);
}

/* ---------- Garanties et comparatif ----------
   Céline, 01/10/2026 : de l'espace avant le comparatif data. Les `id` en
   `-lp` ne servent que sur cette page (irritants.11tydata.js). */
#garanties-lp {
  padding-bottom: var(--sp-section);
}

#comparatif-lp {
  padding-top: var(--sp-16);
}

/* ---------- Les titres coupés à la main ----------
   Les <br class="irritants-coupure"> du YAML fixent les lignes sur grand
   écran ; sous `md`, ils s'effacent et le titre s'enroule normalement. Le
   titre des autres problématiques sort de sa mesure (52rem) pour que ses
   trois lignes tiennent chacune sur une ligne. */
.irritants-autres__entete {
  max-width: 60rem;
}

.irritants-autres__titre {
  text-wrap: wrap;
}

@media (max-width: 56.25rem) {
  .irritants-coupure {
    display: none;
  }
}
/* =========================================================================
   BLOC « editions » — blocs/editions.njk.
   Origine : « Les éditions précédentes », Figma 144:6482.

   L'archive de la newsletter : un en-tête de section, puis un panneau fermé où
   chaque rangée est une édition — repère à gauche, texte au milieu, « Lire → » au
   bout.

   Le panneau, ses coins carrés, son filet bleu éclairci et son fond blanc
   viennent de la primitive `.cadre` (_components.scss) ; la pastille du
   surtitre de `.badge-section`. Cette feuille ne pose que l'en-tête, la
   disposition des rangées et leur état actif.

   ⚠️ LES FILETS NE SONT PAS FAITS PAR LA GOUTTIÈRE. Toutes les grilles du site
   simulent leurs traits avec `gap: 1px` sur un fond à la couleur du filet, parce
   qu'une grille à plusieurs colonnes ne sait pas désigner ses arêtes intérieures
   sans un `:nth-child` dont le pas change à chaque reflux. Ici les rangées sont
   empilées sur UNE colonne : chacune porte son propre filet bas, et la dernière
   n'en porte pas — celui-là, c'est la bordure du `.cadre`. Aucun compte publié,
   la règle vaut pour six éditions comme pour trente.
   ========================================================================= */
.editions {
  position: relative;
  background-color: var(--bg-page);
  padding-block: var(--sp-section);
}

/* ---------- L'en-tête de section ----------
   ⚠️ IL EST DEHORS, PAS DANS LE PANNEAU. Demande d'Hugo le 21/08/2026 ; le
   raisonnement — l'exception de la règle 2 pour un cadre qui enferme un module —
   est en tête du gabarit. Il n'y a donc plus de `.cadre__entete` ici, et le
   panneau commence directement à sa première rangée.

   La pastille, le titre et le chapeau ont tous `margin: 0` : sans `gap` ils se
   toucheraient. */
.editions__entete {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--sp-4);
  margin-block-end: clamp(var(--sp-6), 2.5vw, var(--sp-10));
}

/* Le titre ne déclare rien de sa police (règle 3) : _base.scss pose déjà serif,
   graisse, interlignage et la taille du niveau. Seules l'encre et la mesure. */
.editions__titre {
  margin: 0;
  max-width: 28ch;
  color: var(--c-blue-zodiac);
  text-wrap: balance;
}

/* --text-strong et non --text-muted : c'est du corps de texte. La maquette le
   pose en #4c5772, l'encre éteinte du site — mais la règle du projet réserve
   celle-là à ce qui n'est PAS du texte courant. */
.editions__chapeau {
  margin: 0;
  max-width: 42rem;
  font-size: var(--fs-base);
  line-height: var(--lh-normal);
  color: var(--text-strong);
}

/* ---------- La pile de rangées ---------- */
.editions__liste {
  margin: 0;
  padding: 0;
  list-style: none;
}

.editions__cellule:not(:last-child) {
  border-block-end: var(--filet-panneau);
}

/* L'état vide, à la place de la pile. Il est dans le panneau et non sur le fond
   de page : ce n'est pas une édition, c'est un message — mais il tient la place
   de la liste, donc il reste dedans. */
.editions__vide {
  margin: 0;
  padding: var(--sp-8) var(--pad-panneau-entete-inline);
  font-size: var(--fs-base);
  color: var(--text-strong);
}

/* ---------- Une rangée ----------
   Trois pistes : le repère, le texte, le lien décoratif. La maquette en compte
   quatre, dont une dernière de 130 px qui ne contient rien — voir l'avertissement
   en tête du gabarit : le lien est ferré sur le rembourrage du panneau.

   `position: relative` est structurel : c'est ce qui donne au calque de
   `.edition__lien` la surface de la rangée entière. */
.edition {
  --edition-repere: clamp(5rem, 10vw, 9.375rem); /* 80 → 150px (maquette) */
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, var(--edition-repere)) minmax(0, 1fr) auto;
  align-items: start;
  column-gap: clamp(var(--sp-6), 2.5vw, var(--sp-8));
  row-gap: var(--sp-3);
  /* 28 / 31px de rembourrage vertical dans la maquette. À l'horizontale, le
     jeton PARTAGÉ des panneaux du site (--pad-panneau-entete-inline) : c'est ce
     qui aligne les rangées entre elles et sur les autres panneaux, sans qu'une
     valeur soit écrite ici. */
  padding: clamp(var(--sp-6), 2.4vw, var(--sp-8)) var(--pad-panneau-entete-inline);
  transition: background-color var(--dur-fast) var(--ease-out);
}

/* ---------- Le repère : le numéro et son mois ----------
   ⚠️ CE N'EST PAS L'ATOME `.repere` DE _components.scss, et ce n'est pas un
   oubli. Celui-là est un couple chiffre + libellé dans un `<dl>`, et son libellé
   est en sans-serif courant ; ici le libellé est une ligne en chasse fixe et
   capitales, donc il faudrait lui réécrire police, taille, casse et
   interlettrage — c'est-à-dire n'emprunter que le chiffre. Et « N° 12 » avec
   « Juillet 2026 » ne forment pas un terme et sa valeur : ce sont deux
   identifiants du même objet, que l'`<article>` porte déjà. */
.edition__repere {
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
}

/* Le serif se déclare ici sans enfreindre la règle 3 : _base.scss ne pose la
   police des titres que sur h1..h4, et ceci est un `<span>`. */
.edition__numero {
  font-family: var(--font-titre);
  font-size: clamp(1.25rem, 1.05rem + 0.55vw, 1.625rem); /* 20 → 26px */
  font-weight: var(--fw-bold);
  line-height: var(--lh-tight);
  letter-spacing: var(--ls-display);
  color: var(--text-accent);
}

.edition__mois {
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--text-muted);
}

/* ---------- Le texte ---------- */
.edition__texte {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
}

/* Écart à la règle 3 de MAQUETTE.md, borné à ce bloc et de même nature que
   celui de `.cas--carte .cas__titre` : dans une rangée à trois colonnes, un
   --fs-h3 (22 → 32px) écrase le repère et le lien. Le clamp PLAFONNE à la valeur
   de la maquette (21px) au lieu d'en faire son plancher. */
.edition__titre {
  margin: 0;
  font-size: clamp(1.25rem, 1.1rem + 0.45vw, 1.375rem); /* 20 → 22px */
  color: var(--c-blue-zodiac);
  text-wrap: balance;
}

/* Le lien ne prend pas la couleur d'accent : c'est l'intitulé de la rangée, et
   la rangée entière étant cliquable, le souligner ou le teinter mettrait un
   signal de lien au milieu d'un objet qui en est un tout entier. */
.edition__lien {
  color: inherit;
  text-decoration: none;
}

/* Le calque qui étend le clic à toute la rangée. UN SEUL lien dans le DOM —
   l'avertissement en tête du gabarit dit pourquoi « Lire → » n'en est pas un. */
.edition__lien::after {
  content: "";
  position: absolute;
  inset: 0;
}

/* --text-strong et non --text-muted : corps de texte. */
.edition__chapeau {
  margin: 0;
  max-width: 62ch;
  font-size: var(--fs-sm);
  line-height: var(--lh-normal);
  color: var(--text-strong);
}

/* ---------- Le « Lire → » décoratif ---------- */
.edition__lire {
  margin: 0;
  align-self: center;
  justify-self: end;
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
  white-space: nowrap;
  color: var(--text-accent);
}

/* `translate` ET NON `transform` : reveal.js pose `transform: none` sur tout
   élément révélé, à une spécificité qui gagne. La flèche est ici un descendant
   du `<li>` révélé et non le révélé lui-même, donc elle survivrait — mais la
   règle du projet est d'écrire tout mouvement d'un élément révélé ou de sa
   descendance en propriétés séparées, pour qu'un déplacement du `data-reveal`
   d'un cran ne le tue pas en silence. */
.edition__lire .btn__arrow {
  transition: translate var(--dur-fast) var(--ease-out);
}

/* ---------- L'état actif : pointé ou focalisé ----------
   La maquette ne dessine aucun état de survol. Il n'en reste pas moins dû : la
   rangée entière est cliquable, et sans retour au pointage rien ne le dit. Deux
   gestes seulement — la teinte du fond et la flèche qui avance — le geste et sa
   valeur repris de `.btn__arrow`.

   ⚠️ LE FOCUS N'EST PAS DANS LE `@media (hover: hover)`, ET C'EST LE POINT À NE
   PAS DÉFAIRE. Les deux états produisent le même effet, mais seul le POINTAGE
   doit être bridé aux appareils qui pointent vraiment — sur un écran tactile,
   `:hover` reste collé après le tap. Le focus clavier, lui, doit fonctionner
   partout : l'enfermer dans cette requête le tuait sur tout appareil tactile.

   D'où un mixin local plutôt que deux sélecteurs recopiés : les valeurs de
   l'effet sont écrites UNE fois, et seule la garde diffère.

   `:focus-within` et non `:focus` sur le lien : le focus arrive sur l'intitulé,
   mais c'est la RANGÉE qu'il faut éclairer. */
.edition:focus-within {
  background-color: color-mix(in srgb, var(--c-blue) 4%, transparent);
}
.edition:focus-within .btn__arrow {
  translate: 3px 0;
}

@media (hover: hover) {
  .edition:hover {
    background-color: color-mix(in srgb, var(--c-blue) 4%, transparent);
  }
  .edition:hover .btn__arrow {
    translate: 3px 0;
  }
}
/* ---------- Étroit ----------
   Sous md, les trois pistes s'empilent : le repère passe en ligne (le numéro et
   son mois côte à côte, il n'y a plus de colonne à tenir), et le lien décoratif
   redescend sous le texte, ferré à gauche comme tout le reste. */
@media (max-width: 56.25rem) {
  .edition {
    grid-template-columns: minmax(0, 1fr);
    row-gap: var(--sp-4);
  }
  .edition__repere {
    flex-direction: row;
    align-items: baseline;
    gap: var(--sp-3);
  }
  .edition__lire {
    align-self: start;
    justify-self: start;
  }
}
/* ---------------------------------------------------------------------
   Page « rickroll » — src/rickroll.njk.

   Un titre centré, une vidéo dessous. Rien d'autre à habiller.
   --------------------------------------------------------------------- */
.rickroll {
  padding-block: var(--sp-section);
}

.rickroll__contenu {
  display: grid;
  justify-items: center;
  gap: var(--sp-10);
  text-align: center;
}

.rickroll__titre {
  max-width: 18ch;
  font-family: var(--font-serif);
  font-size: var(--fs-h1);
  /* Pas de graisse ici : voir `.page__titre` dans _page.scss — un <h1> prend
     celle de _base.scss, la --fw-regular était un reste de l'époque Recoleta. */
  line-height: var(--lh-tight);
  letter-spacing: var(--ls-display);
  color: var(--text-strong);
}

.rickroll__scene {
  display: grid;
  gap: var(--sp-3);
  width: 100%;
  max-width: 56rem;
}

/* Le ratio est porté par l'iframe elle-même : elle a des dimensions
   intrinsèques (300 × 150 par défaut) et non un ratio, d'où l'aplatissement
   si on ne le déclare pas. */
.rickroll__video {
  width: 100%;
  aspect-ratio: 16/9;
  border: 0;
  border-radius: var(--radius-lg);
  background: var(--c-black);
}

.rickroll__mention {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  color: var(--text-muted);
}

/* Le script pose l'attribut au premier geste : la mention n'a plus rien à
   dire une fois le son revenu. */
.rickroll__scene[data-rickroll-son=on] .rickroll__mention {
  display: none;
}

/* =========================================================================
   Bannière de consentement — habillage du paquet CookieConsent
   (« vanilla-cookieconsent »), dont la feuille structurelle est servie en
   brut (voir eleventy.config.js « addPassthroughCopy » et le commentaire de
   layouts/base.njk). Ce fichier, lui, passe par le pipeline Sass habituel
   du site et sort dans main.css, chargé APRÈS cette feuille vendor : c'est
   ce qui fait gagner les réaffectations ci-dessous.

   Le paquet expose son propre jeu de variables (`--cc-*`), documenté sur
   cookieconsent.orestbida.com — c'est l'extension officielle et stable, et
   tout ce qui peut passer par elles y passe plutôt que par une sélection de
   ses classes internes (`.cm__*`, `.pm__*`), plus sujettes à changer d'une
   version à l'autre du paquet.
   ========================================================================= */
:root {
  --cc-font-family: var(--font-sans);
  --cc-z-index: var(--z-consentement);
  --cc-modal-transition-duration: var(--dur-base);
  --cc-modal-border-radius: var(--radius-lg);
  --cc-modal-margin: var(--sp-6);
  --cc-btn-border-radius: var(--radius-md);
  --cc-bg: var(--bg-surface);
  --cc-primary-color: var(--text-strong);
  --cc-secondary-color: var(--text-muted);
  --cc-link-color: var(--c-blue);
  /* Voile derrière le panneau de préférences (un vrai <dialog> ouvert par
     choix, indépendant du bandeau — celui-là ne bloque jamais la page, voir
     `mode: "opt-in"` sans `disablePageInteraction` dans consentement.js). */
  --cc-overlay-bg: color-mix(in srgb, var(--c-blue-zodiac) 55%, transparent);
  /* « Tout accepter » / « Tout refuser » : mêmes cotes que `.btn--primary`,
     réaffectées et non écrites en dur — un `.ton-page--*` qui assombrit
     `--btn-primary-bg` pour un pilier doit assombrir ce bouton aussi. */
  --cc-btn-primary-bg: var(--btn-primary-bg, var(--c-blue));
  --cc-btn-primary-border-color: var(--btn-primary-bg, var(--c-blue));
  --cc-btn-primary-color: var(--c-white);
  --cc-btn-primary-hover-bg: var(--btn-primary-bg-hover, var(--c-blue-action));
  --cc-btn-primary-hover-border-color: var(--btn-primary-bg-hover, var(--c-blue-action));
  --cc-btn-primary-hover-color: var(--c-white);
  /* « Personnaliser » : même parti que `.btn--secondary` — contour à
     l'encre, fond du panneau, survol fluo. */
  --cc-btn-secondary-bg: var(--cc-bg);
  --cc-btn-secondary-border-color: var(--text-strong);
  --cc-btn-secondary-color: var(--text-strong);
  --cc-btn-secondary-hover-bg: var(--c-fluo);
  --cc-btn-secondary-hover-border-color: var(--text-strong);
  --cc-btn-secondary-hover-color: var(--c-text-main);
  --cc-footer-bg: var(--cc-bg);
  --cc-footer-color: var(--text-muted);
  --cc-footer-border-color: var(--border-subtle);
  --cc-separator-border-color: var(--border-subtle);
  --cc-section-category-border: var(--border-subtle);
  /* Les blocs de catégorie (nécessaires / mesure d'audience / marketing)
     se détachent du panneau blanc sur un fond crème — le même écart que
     `.cadre` sur la page, appliqué à l'intérieur d'une carte plutôt qu'à
     une section entière. */
  --cc-cookie-category-block-bg: var(--c-base-main);
  --cc-cookie-category-block-border: var(--border-subtle);
  --cc-cookie-category-block-hover-bg: color-mix(in srgb, var(--c-base-main) 92%, var(--c-text-main));
  --cc-cookie-category-expanded-block-bg: var(--c-base-main);
  --cc-cookie-category-expanded-block-hover-bg: color-mix(in srgb, var(--c-base-main) 92%, var(--c-text-main));
  --cc-toggle-on-bg: var(--btn-primary-bg, var(--c-blue));
  --cc-toggle-on-knob-bg: var(--c-white);
  --cc-toggle-off-bg: var(--c-casper);
  --cc-toggle-off-knob-bg: var(--c-white);
  --cc-toggle-enabled-icon-color: var(--c-white);
  --cc-toggle-disabled-icon-color: var(--text-muted);
  /* Le bloc « Nécessaires » est en lecture seule (readOnly: true dans
     consentement.js) — un bascule qu'on ne peut pas actionner ne doit pas
     avoir l'air actionnable. */
  --cc-toggle-readonly-bg: var(--c-casper);
  --cc-toggle-readonly-knob-bg: var(--c-white);
  --cc-toggle-readonly-knob-icon-color: var(--text-muted);
  --cc-pm-toggle-border-radius: var(--radius-full);
}

@media (prefers-color-scheme: dark) {
  :root {
    --cc-primary-color: var(--text-strong);
  }
}
/* Le titre du bandeau et du panneau reprend le poids des titres de panneau
   du site (`.cadre__entete-titre`) plutôt que le gras par défaut du
   paquet — la seule reprise de classe interne de ce fichier, scopée sous
   l'id du conteneur pour l'emporter sans `!important`. */
#cc-main .cm__title,
#cc-main .pm__title {
  font-family: var(--font-sans);
  font-weight: var(--fw-semibold);
  letter-spacing: normal;
}

/* =========================================================================
   Révélation au défilement — le seul bloc CSS de l'effet.

   `@use`é en dernier dans main.scss pour passer en dernier dans la
   cascade : ces règles doivent l'emporter sur les `transform` et `opacity`
   que pose une section.

   TOUT est sous `html.reveal`. Cette classe n'existe que si
   src/assets/js/reveal.js s'exécute — donc retirer sa balise <script> de
   layouts/base.njk désactive l'effet entièrement, sans résidu. Voir l'en-tête
   du script.
   ========================================================================= */
html.reveal [data-reveal] {
  opacity: 0;
  /* 16 px, pas plus : au-delà, l'entrée se remarque et devient un procédé. */
  transform: translateY(16px);
}

html.reveal [data-reveal].est-revele {
  opacity: 1;
  transform: none;
  /* Décalage de 60 ms par cran, posé par `data-reveal-step` dans les
     gabarits : le titre part, le corps suit, le visuel ferme. Sans cran
     déclaré, l'élément part immédiatement. */
  transition: opacity var(--dur-reveal, 520ms) var(--ease-editorial), transform var(--dur-reveal, 520ms) var(--ease-editorial);
  transition-delay: calc(var(--reveal-step, 0) * 60ms);
}

/* Les crans sont énumérés parce que le CSS ne sait pas encore lire un
   attribut comme un nombre (`attr()` typé n'est pas disponible partout). Un
   cran au-delà du dernier déclaré n'a simplement pas de retard — l'entrée
   reste correcte, elle perd sa place dans la cascade. */
[data-reveal-step="1"] {
  --reveal-step: 1;
}

[data-reveal-step="2"] {
  --reveal-step: 2;
}

[data-reveal-step="3"] {
  --reveal-step: 3;
}

[data-reveal-step="4"] {
  --reveal-step: 4;
}

[data-reveal-step="5"] {
  --reveal-step: 5;
}