/* ---
 * Version : V2.9 · 2026-07-20
 * Changelog V2.9 (LOT DE CONTRASTE — LE ROUGE DE MARQUE QUITTE LE TEXTE, ET
 *   --success DESCEND D'UN CRAN. Deux chantiers, aucun changement de mise en page.
 *
 *   (1) --accent EN TEXTE → --accent-active, sur 7 RÈGLES. C'est l'aboutissement de
 *   la doctrine déjà posée à la déclaration du jeton (« --accent reste la couleur de
 *   MARQUE : il n'est plus jamais porteur de texte ») et déjà appliquée par étapes à
 *   .eyebrow .hot, .guidelink, .oc-cta, .oc-flag, .mt-topic, .omenu-flagtag et
 *   .omenu-go. Les 7 dernières : .hero .sub · .convmeta .metahi · .convmeta .burnt ·
 *   .chip b · .chip.hi .hitag · pre.demo-body .k · .metrics-fold:hover.
 *   Chaque règle porte sa propre mesure. Deux relevés méritent d'être retenus :
 *   · les FONDS RÉELS ne sont pas les fonds supposés. `.chip.hi .hitag` ne vit pas
 *     sur --surface mais sur --accent-soft (la classe .hi repeint la pastille) :
 *     3,30:1 et non 3,65:1. Et `.hero .sub` n'a AUCUN ancêtre opaque — le dégradé
 *     ambiant .page-bg compose réellement sous lui, portant le fond à #FFF9F9
 *     (mesuré au pixel, texte rendu transparent) : 3,50:1, plus bas que les 3,60:1
 *     du calcul sur jeton.
 *   · TROIS RÈGLES DE LA LISTE D'ORIGINE N'ONT PAS ÉTÉ TOUCHÉES, à dessein :
 *     `.chip .up` et `.convmeta .up` ne stylent PAS du texte mais un <svg>
 *     (ICON.up, tracé en currentColor, textContent vide) : seuil SC 1.4.11 de 3:1,
 *     tenu à 3,30 et 3,44. Les assombrir aurait été une régression de sens, pas une
 *     correction. Leur `font-weight` — sans effet sur un <svg> — trahit la méprise
 *     d'origine. `.nav .links a.nav-merge` n'a QUANT À LUI aucun balisage dans tout
 *     le dépôt : règle morte, traitée à part.
 *
 *   (2) --success #16845B → #157A55. 4,19:1 sur son propre --success-soft, plus un
 *   second fond hors seuil non signalé jusqu'ici (--bg2, 4,42:1). Contrairement à
 *   --ink3, le jeton n'est PAS scindé : aucun emploi sur surface sombre, et les
 *   aplats portant du blanc s'améliorent aussi. Détail complet à la déclaration.
 *   Portée réelle : .demo-hd .demo-tag, .bt-brief-tag et .pc-lead. Les pastilles
 *   Guide / How-to des 5 hubs de blog RESTENT hors seuil — elles ne chargent pas
 *   cette feuille et redéclarent le jeton chez elles ; hors périmètre, signalé.
 *
 *   AUCUNE RÈGLE DE MISE EN PAGE, AUCUN AUTRE JETON. L'échelle --cta et --accent2
 *   sont intactes ; --accent garde tous ses emplois d'ICÔNE et de FOND.)
 * Version précédente : V2.8 · 2026-07-19
 * Changelog V2.8 (ORANGE LIBÉRÉ — DOCUMENTATION SEULE DANS CETTE FEUILLE.
 *   Arbitrage fondateur home v4 : --accent2 (#D9662E) N'EST PLUS RÉSERVÉ à Human
 *   Brief. Motif : une palette crème+rouge seule est trop maigre, la marque a
 *   besoin d'un second accent qui vit ; et « orange = Human Brief » était une
 *   décision de circonstance, pas de principe. Human Brief reste le phare par sa
 *   taille, son sur-titre et sa position centrale — jamais par un monopole de
 *   couleur. Emploi désormais autorisé : dégradés, survols, détails, et
 *   différenciation d'UNE carte ; parcimonieux, pas de sapin de Noël.
 *   ⚠️ AUCUNE RÈGLE DE STYLE N'EST MODIFIÉE ICI — trois blocs de COMMENTAIRE
 *   seulement. C'est délibéré : le commentaire périmé avait une conséquence
 *   mesurable (un agent a refusé de colorer une carte en s'appuyant dessus, alors
 *   que le fondateur venait d'autoriser ce geste). Les trois blocs touchés :
 *   · déclaration du jeton --accent2 → bloc « ORANGE LIBÉRÉ », doctrine de
 *     référence, avec les seuils de contraste de l'orange ;
 *   · bloc « POINT 2 (Kevin) » (pilule snapshot) → marqué SUPERSÉDÉ ; son choix
 *     local (--ink, 17,9:1) est INCHANGÉ, seule sa justification est corrigée ;
 *   · bloc « SR-20·D » (zone C) → l'orange y reste, mais comme rang SECONDAIRE et
 *     non comme étiquette de Human Brief.
 *   L'application effective de la libération vit dans templates/home.html — bloc
 *   d'icône de la carte Migration — conformément à la consigne de série stricte
 *   sur cette feuille pendant le chantier.
 *   Relevés d'orange consignés une fois pour toutes, à réutiliser :
 *     #D9662E sur blanc = 3,57:1 → INTERDIT en texte normal (seuil 4,5:1)
 *     #FFF sur #D9662E  = 3,57:1 ✓ ≥ 3:1 (SC 1.4.11, composants non textuels)
 *     #FFF sur #C25422  = 4,58:1 ✓
 *   Le libellé du méga-menu passe par ailleurs de « Products » à « Tools » — mais
 *   c'est une chaîne i18n servie par build.js, rien n'en descend dans cette
 *   feuille.)
 * Version précédente : V2.7 · 2026-07-19
 * Changelog V2.7 (MOUVEMENT RÉDUIT — UN TROU BOUCHÉ SUR `.da-stage`. Chantier
 *   home v3 : l'instrument animé est REMONTÉ dans le hero de la home (le nœud de
 *   montage avait disparu en v2 ; ni la feuille ni le moteur n'avaient bougé —
 *   rien n'a donc été réécrit ici pour le restaurer, la remise en service tient
 *   entièrement dans templates/home.html).
 *   La SEULE correction de fond descendue ici, parce qu'elle touche les 18 pages
 *   qui servent l'instrument et pas la seule home :
 *   · `@media(prefers-reduced-motion:reduce)` — la liste ne coupait que les
 *     animations INFINIES. Elle laissait donc passer `daPop` et `daFlash`, en une
 *     itération, portées par `.instrument.ready .di-handoff`. Et c'est précisément
 *     l'état où instrument.js ENVOIE ces utilisateurs : sous mouvement réduit il
 *     sort en `renderFinal()`, qui pose `.ready` d'emblée. Celui qui demandait
 *     moins de mouvement était le seul à recevoir le rebond, et sans transition
 *     pour l'amortir. Les animations à itération unique ET les transitions qui
 *     déplacent (panneau de reprise, jauge `.dn-fill` 0 → 88 % en 1,1 s, éclosion
 *     du curseur) sont désormais coupées elles aussi. L'état final reste PEINT :
 *     immobile, pas absent.
 *   · `.mt-topic` — CONTRÔLE RÉEL, DEUX ÉTATS HORS SEUIL, corrigés. La pastille
 *     de sujet (bannière multi-sujets de la home + /extract/) portait du BLANC sur
 *     --accent #FF2D55 : 3,65:1 à 12px/600, sans dispense possible. C'est l'oubli
 *     du passage de juillet qui avait fait descendre l'échelle --cta pour cette
 *     raison même — --accent est la MARQUE, il ne porte pas de texte. Elle passe à
 *     --accent-active : 5,75:1. Et l'état RETIRÉ cumulait --ink2 avec
 *     `opacity:.75`, qui compose sur le fond : couleur rendue #898380 → 3,38:1.
 *     L'opacité tombe, --ink2 retrouve ses 5,82:1. Les deux états restent
 *     distingués par le remplissage, le trait pointillé, la coche et le libellé
 *     barré — quatre canaux, dont aucun n'était l'opacité.
 *   AUCUN AUTRE JETON DE COULEUR N'EST TOUCHÉ. L'échelle --cta reste intacte, --accent2
 *   garde sa charge HUMAN BRIEF [⚠️ PHRASE SUPERSÉDÉE EN V2.8 — la réservation est
 *   levée ; texte d'époque conservé tel quel, voir le changelog V2.8 ci-dessus]
 *   — le dégradé rouge/orange de la bande des sorties
 *   vit dans le <style> de templates/home.html, pas ici, conformément à la consigne
 *   de série stricte sur cette feuille pendant le chantier.)
 * Changelog V2.6 (BARRE DE HEADER v2 — deux raccourcis de sortie + pastille de
 *   déclencheur. Un SEUL composant nouveau descend ici, et il n'avait pas le choix :
 *   la barre est servie sur 24 pages, dont 19 n'ont AUCUN <style> de gabarit où se
 *   loger. La styler depuis templates/home.html l'aurait laissée nue partout
 *   ailleurs — soit exactement la dérive que le générateur unique existe pour
 *   empêcher.)
 *   · `.nav-shortcut` — Human Brief et Handoff, les deux meilleures ventes, en
 *     accès direct dans la barre sans ouvrir le méga-menu. Rendus en PASTILLE
 *     BORDÉE et non en bouton plein : la barre porte déjà un bouton d'action
 *     (`.btn-primary`, « Uncook my chat ») et un troisième et quatrième aplat
 *     auraient mis quatre appels de même poids côte à côte — plus aucun n'aurait
 *     été le principal. La pastille se lit comme cliquable sans concurrencer le CTA.
 *     MESURÉ : --ink #171716 sur --surface #FFFFFF = 16,95:1 (13,5px/600, seuil
 *     4,5:1). Au survol, --accent-active #C9153A sur --accent-soft #FFF0F3 =
 *     5,21:1 — tenu lui aussi. Aucun rouge vif sous du texte, nulle part.
 *   · `.omenu-lead` — la pastille du déclencheur « Products ». Purement décorative
 *     (aria-hidden, redondante avec le mot), en --ink3 : aucun seuil de texte ne
 *     s'y applique, et elle ne porte aucune information seule.
 *   · REPLI ÉTROIT — les deux raccourcis s'effacent sous 1080px. LE PALIER EST
 *     MESURÉ, sur la home ALLEMANDE (cas contraignant : « So funktioniert's » et
 *     « Meinen Chat uncooken » sont les libellés les plus longs des cinq langues),
 *     raccourcis FORCÉS visibles pour éprouver la barre complète :
 *         1024px → tient, mais 10px seulement entre le logo et « Produkte »
 *          960px → tient encore, toujours 10px
 *          880px → CASSE : scrollWidth 952 pour clientWidth 880, soit 72px de
 *                  débordement horizontal
 *     Le point de rupture est donc entre 880 et 960px. On coupe à 1080px et non au
 *     ras de la rupture : à 1024px la barre « tient » avec 10px d'air, ce qui est
 *     une collision visuelle même si aucune assertion ne la voit. Sous ce palier
 *     les deux sorties restent servies par le méga-menu ET par la feuille mobile,
 *     qui listent les cinq — on ne perd aucun accès, on perd un raccourci.
 *   · ⚠️ DÉFAUT PRÉEXISTANT CONSTATÉ EN PASSANT, NON CORRIGÉ ICI : entre ~760 et
 *     ~1000px, la barre ALLEMANDE déborde DÉJÀ sans aucun raccourci — 927px de
 *     scrollWidth pour 880 de clientWidth. Vérifié sur la branche `da-home` (V1) :
 *     927/880 À L'IDENTIQUE. Ce lot ne le crée pas et ne l'aggrave pas ; il ne le
 *     répare pas non plus, la barre étant hors du périmètre arbitré. À traiter à
 *     part (le palier de masquage `.nav .links > a:not(:last-child)` est à 760px
 *     alors que l'allemand a besoin d'environ 1000).
 *   L'ÉCHELLE `--cta` (#C9153A / #B31232 / #9E0F2D) N'EST PAS TOUCHÉE.
 *   Tout le reste du lot v2 (hero, bande pleine laize, cartes) vit dans le <style>
 *   de templates/home.html : la consigne de série sur cette feuille est tenue.
 * Version précédente : V2.5 · 2026-07-19
 *
 * Changelog V2.5 (NOUVELLE DA — dette de contraste soldée sur le méga-menu et le
 *   bandeau de consentement. AUCUNE règle de mise en page n'est touchée.)
 *   Ce lot restyle la home (hero en deux colonnes, blocs d'icône XL, cartes avec
 *   aura) et TOUT ce travail vit dans le <style> du gabarit templates/home.html —
 *   la consigne de série sur cette feuille est donc tenue. Trois libellés seulement
 *   descendent ici, parce qu'ils appartiennent à des composants PARTAGÉS qu'aucun
 *   gabarit ne peut corriger depuis sa page :
 *   · `.omenu-flagtag` — le sur-titre « La plupart commencent ici », en --accent
 *     #FF2D55 sur le fond --accent-soft #FFF0F3 de la rangée phare : 3,30:1 MESURÉ,
 *     là où SC 1.4.3 AA exige 4,5:1. À 10px, l'exemption grand texte (24px, ou
 *     18,66px gras) ne s'applique évidemment pas. → --accent-active #C9153A =
 *     5,21:1. Corrigé en bureau ET dans la feuille mobile.
 *   · `.omenu-go` — même fond, même faute, 13px/600 : 3,30:1 → 5,21:1.
 *     C'est la correction que `.oc-cta` et `.oc-flag` avaient déjà reçue sur les
 *     cartes de la home ; le méga-menu ne l'avait jamais reçue, alors qu'il montre
 *     le MÊME libellé phare par-dessus ces mêmes cartes.
 *   · `.consent-link` (« Personnaliser ») — --ink3 #938D88 sur --surface : 3,28:1
 *     → --ink2 #625E5A = 6,43:1. ⚠️ PAS en rouge, et c'est délibéré : voir la note
 *     posée sur la règle elle-même. Le fautif ici est un gris, pas le rouge de
 *     marque, et repeindre en rouge le chemin vers le refus granulaire lui donnerait
 *     le poids du bouton « Tout accepter ». On corrige le seuil, pas la hiérarchie.
 *     MISE À JOUR 19/07 — le seuil était corrigé, la GÉOMÉTRIE ne l'était pas :
 *     16px de haut contre 38px pour « Tout accepter ». Arbitrage Emil (CNIL/CEPD :
 *     refuser doit être aussi simple qu'accepter) → le contrôle prend la géométrie
 *     de `.consent-btn` et la peau de `.consent-reject`, ce qui porte au passage sa
 *     couleur à --ink #171716 = 17,94:1. La hiérarchie, elle, ne bouge toujours pas.
 *   · RESTE OUVERT, signalé et NON corrigé ici : « Tout accepter » est en #FFFFFF
 *     sur --accent #FF2D55 = 3,65:1, sous les 4,5:1 de SC 1.4.3 à 13,5px/600.
 *     C'est le chemin d'ACCEPTATION, hors du périmètre d'un lot dont l'objet est
 *     l'égalité des chemins ; le corriger le rendrait plus lisible, donc plus
 *     saillant, à rebours de l'arbitrage. À traiter avec l'échelle --accent.
 *   L'ÉCHELLE `--cta` (#C9153A / #B31232 / #9E0F2D) N'EST PAS TOUCHÉE — elle avait
 *   soldé un échec AA de portée site, on n'y revient pas.
 *   RESTE OUVERT, non corrigé ici et volontairement : `.eyebrow .hot` est en
 *   --accent sur fond clair = 3,65:1, sous le seuil. Il sert des pages d'action que
 *   ce lot n'a pas le droit de faire bouger avant validation du fondateur. La home
 *   se protège seule en surclassant `.hot` dans son propre contexte `.hero-eyebrow`.
 * Version précédente : V2.4 · 2026-07-19
 *
 * Changelog V2.4 (ZONE A — RÉVISION : le titre redevient une négation, l'allusion
 *   « collez un lien » descend d'un cran) :
 *   V2.3 avait fait de la 1re pastille un GESTE (« Collez un lien ») là où les trois
 *   sœurs sont des NÉGATIONS (« Sans compte », « Rien n'est conservé »). La pastille
 *   de confiance est un genre : elle retire une objection, elle ne décrit pas un
 *   mode d'emploi. Le titre repasse donc en négation courte — « Sans installation »
 *   — et le parallélisme des quatre est rétabli. L'allusion n'est pas perdue : elle
 *   descend en DESCRIPTION.
 *   · À QUEL NIVEAU LA DESCRIPTION ? Trois lectures construites et MESURÉES ; c'est
 *     la note DE GROUPE qui est retenue (`BADGES_SUBTEXT_MODE = 'group'` dans
 *     build.js — une constante à changer pour basculer). Hauteur du composant,
 *     identique dans les 5 langues, référence = le groupe sans sous-texte :
 *       (aucun) 32,3px / 64,0px · 'one' 43,4 / 74,0 · 'all' 43,4 / 84,0 ·
 *       'group' 59,4 / 89,4   (à 1280px / à 375px ; 1 rang puis 2 rangs partout)
 *     À 1280 la pastille la plus haute fixe le rang : dès qu'UNE pastille passe à
 *     deux lignes, les trois autres sont gratuites — 'one' coûte donc exactement
 *     'all' tout en produisant une pastille à 2 lignes parmi trois à 1 ligne, soit
 *     un défaut de gabarit payé au prix fort. 'one' est éliminé.
 *   · ET IL FAUT LE DIRE NET : 'group' est l'option la PLUS HAUTE (+16,0px sur
 *     'all' à 1280, +5,4px à 375). Ce n'est pas la hauteur qui tranche mais la
 *     SILHOUETTE : la convention trust-badge — celle que cette révision vient de
 *     rétablir — est une pastille d'UNE ligne. À deux lignes le rang devient une
 *     grille de 4 mini-cartes, et on ré-abandonne pour 16px la convention qu'on
 *     venait de restaurer. 'group' garde les 4 pastilles à 29/32px et pose la
 *     description en légende. Repli si l'espace vertical prime : 'all'.
 *   · CONTRASTE — la hiérarchie se fait PAR LE HAUT. Éclaircir le sous-texte était
 *     exclu par la mesure : --ink3 sur --accent-soft = 2,97:1, sous les 4,5:1 de
 *     SC 1.4.3. Le sous-texte de pastille garde donc --ink2 (5,82:1) et c'est le
 *     TITRE qui fonce à --ink (16,24:1). La note de groupe est sur le fond de page
 *     et non sur la pastille : --ink2 y mesure 6,34:1. Aucune couleur nouvelle.
 *   · Les 5 dictionnaires reçoivent le titre raccourci + 4 sous-textes + la note.
 *     Aucune clé laissée en anglais.
 * Version précédente : V2.3 · 2026-07-19
 *
 * Changelog V2.3 (ZONE A — le bandeau différenciants devient un GROUPE DE PASTILLES) :
 *   `.diffband` était une bande CONTINUE pleine laize, à fond --accent-soft, vivant
 *   dans le <style> inline de templates/home.html ET recopiée à la main dans 7 pages
 *   autonomes (handoff, merge, migration ×5) — 8 copies, 8 endroits à ne pas oublier.
 *   Exactement le mécanisme du méga-menu, au stade juste avant la dérive.
 *   · `.trustbadges` remplace `.diffband` : le fond --accent-soft passe de la BANDE
 *     aux PASTILLES elles-mêmes (le conteneur devient transparent). La bande continue
 *     disparaît donc par construction, pas par réglage.
 *   · Pastilles : rayon 999px (précédent maison `.da-stage .inst-status`), fond
 *     --accent-soft, filet #EFBECB, libellé --ink2, icône --accent contour 1.5.
 *     MESURÉ : libellé --ink2 #625E5A sur #FFF0F3 = 5,82:1 — c'est LA mesure qui
 *     engage, SC 1.4.3 AA exigeant 4,5:1 pour du texte normal (13px << 24px).
 *     --ink3 (2,97:1) et --accent en TEXTE (3,30:1) sont écartés pour cette raison.
 *     L'icône --accent est à 3,30:1 mais n'a AUCUNE obligation de contraste : elle
 *     est aria-hidden et redouble le libellé. SC 1.4.11 ne s'applique pas non plus
 *     à la pastille — il ne régit que les COMPOSANTS D'INTERFACE, et rien ici n'est
 *     interactif. Le filet ci-dessous est donc un choix de lisibilité, pas une
 *     mise en conformité : ne pas le justifier par un seuil qu'il ne doit pas tenir.
 *   · Le groupe porte `.container` → il hérite de la laize, des gouttières ET du
 *     `position:relative;z-index:1` de la règle `.container,footer,section` : le
 *     correctif z-index manuel que `.diffband` (un <nav>) devait porter disparaît.
 *   · Sémantique : <ul aria-label> et non <nav>. Ce ne sont PAS des liens ; un
 *     landmark de navigation sans navigation polluait la liste des repères AT.
 *   CSS rapatriée ici, markup dans le générateur badgesBar() de build.js : les
 *   23 pages servies la reçoivent d'une source unique.
 * Version précédente : V2.2 · 2026-07-18
 *
 * Changelog V2.2 (ASSEMBLAGE DE LIVRAISON — méga-menu de header étendu à TOUT le
 *   site) : deux lignées V2.x se rejoignent ici, aucune n'est écrasée. Celle du
 *   méga-menu portait le numéro V2.0 sur sa branche ; elle est renumérotée V2.2 à
 *   l'assemblage, la V2.1 (overlay) étant déjà prise.
 *   Le composant vivait dans le <style> inline de templates/home.html — donc sur
 *   les 5 homes UNIQUEMENT. Sa CSS est rapatriée ici (section en fin de fichier),
 *   son comportement dans site/header-menu.js, son markup dans le générateur de
 *   build.js : les 17 pages servies le reçoivent d'une source unique.
 *   · ANCRAGE CORRIGÉ : le panneau s'appuie désormais sur `.nav` (`.nav-shell .nav
 *     {position:relative}` + `right:0`) et non plus sur `.container`. Sur /pro/ et
 *     /everyday/ ce conteneur enveloppe aussi le hero (757px mesurés) : le panneau
 *     se dépliait 757px sous le header, hors écran. Géométrie inchangée là où elle
 *     était validée (780×168, 10px sous la barre).
 *   · POLICE : les noms de sorties (.omenu-name, bureau + mobile) passent en
 *     --font-brief (décision fondateur, option A — zéro octet réseau).
 * Version précédente : V2.1 · 2026-07-18
 *
 * Changelog V2.1 (FONDATION OVERLAY #0) : la bande de reprise (.clr-strip) devient
 *   la cible primaire de retour au travail depuis le ✕ de l'overlay → min-height
 *   44px sous `pointer:coarse` UNIQUEMENT (rendu souris strictement inchangé).
 *   Aucune autre règle touchée : les mesures de la V2.0 ci-dessous restent valides.
 *   Re-mesuré 18/07, 1280×800, CTA « Turn it into a human brief » rect.bottom :
 *   668,2 avec bandeau sujets (= le chiffre de la V2.0, reproduit à l'identique)
 *   et 611,2 en mono (pas de bandeau) — les deux ≤ 800, acceptation tenue.
 *   Déplacement dx/dy=0 sur .result-main / #handoff-block / .result-side /
 *   #zone-c à 1280 ET à 375 ; #mt-banner dx=0/dy=0 à 1280. À 375, #mt-banner
 *   bouge de dx=2/dy=71,3 à l'ouverture de la box — comportement PRÉ-EXISTANT,
 *   mesuré identique sur la branche de base (chemin feuille de signaux SR-22).
 * Version précédente : V2.0 · 2026-07-18
 * Changelog V2.0 (« la box snapshot est TROP HAUTE » — le CTA phare passait sous
 *   la ligne de flottaison, retour fondateur mesuré sur la preview) :
 *   MESURÉ AVANT, 1280×800 : « Turn it into a human brief » à rect.bottom=801,1
 *   pour innerHeight=800 → HORS ÉCRAN de 1,1px, sans scroll. Deux causes, dont
 *   une que personne n'avait vue :
 *   (1) 62px de VIDE MORT EN HAUT, desktop seulement : `.result-topbar` est
 *       déclarée position:fixed (l.~477) mais la règle d'empilement du dégradé
 *       `#result > *:not(.page-bg){position:relative}` l'écrasait → la barre
 *       tombait DANS LE FLUX, empilée sous les 58px de padding-top qui existent
 *       justement pour passer derrière elle. Corrigé (position rendue à la barre,
 *       padding-top porté à 76px pour dégager la pilule d'angle) : net −44px.
 *   (2) LA BOX ELLE-MÊME, 297,3px en six lignes pleine largeur. Elle passe en
 *       GRILLE sur desktop (≥901px, aucun nœud DOM déplacé) : le message d'état
 *       et le décompte messages/mots partagent désormais UNE rangée sous la
 *       jauge, et le rythme vertical est porté par les gouttières de grille au
 *       lieu de marges cumulées. Bandeau sujets, jauge et puce « ↓ Détails »
 *       resserrés. Aucune information retirée.
 *   MESURÉ APRÈS : box 297,3 → 208,4px (−29,9%) · CTA rect.bottom 801,1 → 668,2
 *   (668,2 ≤ 800 ✓, marge +131,8px) · idem 1280×900 (marge +231,8).
 *   NON RÉGRESSÉ, RE-MESURÉ : déplacement nul (dx/dy=0) sur .result-main /
 *   #handoff-block / .result-side / #zone-c / #mt-banner à l'ouverture ET à la
 *   fermeture du panneau, desktop et 375 · plafond 3 chips + « +N autres »
 *   inchangé et fermé au repos · œil-de-bœuf d'angle et sommaire « In this
 *   handoff » intacts · mobile 375 : foldMobile()/#compact-band/feuille SR-22
 *   inchangés (la carte reste en display:block sous 901px), zéro débord
 *   horizontal (scrollWidth == clientWidth).
 * Version précédente : V1.9 · 2026-07-18
 *
 * Changelog V1.9 (« la box qui décale » — repli EN PLACE, retour fondateur) :
 *   .summary-details (nouveau conteneur posé par app.js/setupSnapshotBox) —
 *   sur desktop il s'ouvre en PANNEAU HORS-FLUX (position:absolute sous la carte),
 *   donc la carte, la colonne, la ligne de grille et le handoff gardent une
 *   hauteur IDENTIQUE ouvert/fermé (delta 0px mesuré). #summary.is-condensed
 *   remplace le repli par `hidden` : la carte se condense, elle ne disparaît plus.
 *   Les sujets (#mt-banner) gardent une densité compacte CONSTANTE — visibles
 *   dans les deux états, jamais masqués (c'était le défaut le plus grave : sous
 *   900px ils disparaissaient entièrement). Sous 900px, la carte condensée ne
 *   garde QUE les sujets (titre/jauge sont déjà dans #compact-band).
 *   .metrics-fold : min-height 44px au pointeur grossier (SR-04·A).
 *   ⚠️ Piste écartée après mesure, documentée en place : plafonner .result-side
 *   (height:0/min-height:100%) figeait bien le handoff mais coupait #zone-c de
 *   159px derrière un scroll sans affordance.
 * Version précédente : V1.8 · 2026-07-16
 * Changelog V1.8 (grille 2 colonnes écran résultat desktop, validé
 *   _result-2col-mockup.html) : .result-grid/.result-main/.result-side —
 *   #handoff-block en colonne principale 2/3, #summary+#compact-band+.verdict+
 *   #zone-c en colonne latérale 1/3, desktop uniquement (>900px). .rescuewrap
 *   920→1180px (cap porté par .result-grid). Sélecteurs #result > #handoff-block/
 *   #summary/#compact-band re-scopés en .result-main > /.result-side >
 *   (apply3Zones() dans site/app.js les enveloppe désormais dans les nouveaux
 *   conteneurs au lieu de les insérer à plat dans #result).
 * Version : V1.7 · 2026-07-14
 * Changelog V1.7 : merge de main dans result-refonte — reprend le correctif hero
 *   desktop (V1.4.1, ci-dessous) sous la refonte écran résultat (V1.5/V1.6).
 *   Zones disjointes : le fix hero touche .hero-da et .da-stage, la refonte touche
 *   #result, .sub- (email), .zone-c et .post-copy. Aucun conflit fonctionnel.
 *   NB : ne JAMAIS écrire un glob CSS du type point-sub-etoile-slash dans ce
 *   header — la séquence étoile+slash ferme le commentaire et tue TOUTE la
 *   feuille (0 règle parsée, site entièrement dénudé). Bug introduit puis
 *   corrigé le 14/07 pendant la résolution du merge.
 * Version : V1.6 · 2026-07-14
 * Changelog V1.6 (RÉALIGNEMENT EMAIL sur le VALIDÉ) : le bloc email suivait la
 *   Variante 1 (champ + envoi accolés) — or le fondateur avait validé la
 *   VARIANTE 2 (« champ et envoi EMPILÉS, largeur identique ») + l'encadré
 *   discret de la maquette _fin-mockup (état C). Corrigé : (a) .sub-block devient
 *   un ENCADRÉ DISCRET (fond surface, bordure grise fine, ombre légère, icône
 *   enveloppe neutre — plus aucune bordure/rouge alarmant) ; (b) Google, champ et
 *   Envoyer partagent UNE largeur (--sub-w 320) et UNE hauteur (--sub-h 46),
 *   EMPILÉS ; (c) l'accent est réservé au SEUL bouton Envoyer ; (d) + .sub-google-cta,
 *   bouton Google « click-to-load » affiché avant consentement (même gabarit que
 *   l'officiel) → fini le « champ Google vide ». Remplace .sub-google-gate.
 * Version : V1.5 · 2026-07-14
 * Changelog V1.5 : refonte écran résultat 3 zones + email V3 + itinéraire post-copie.
 *   (1) email V3 : champ + bouton Google même largeur dans un cadre unique
 *   (.sub-form / .sub-google-slot / .sub-google-gate), cases à cocher légères,
 *   désencombrement. (2) hiérarchie 3 zones (#result > label+#handoff-block /
 *   #summary / #zone-c email en bas séparé) + .zone-c / .zone-c-label. (3)
 *   itinéraire post-copie numéroté (.post-copy / .pc-lead / .pc-steps / .pc-num)
 *   révélé après la copie, calé sur les marges du copybar, replié en colonne ≤560px.
 * Version : V1.4.1 · 2026-07-14
 * Changelog V1.4.1 (correction fondateur) : le masquage de la scène animée du hero
 *   pendant un uncooking (.hero-da.flow-started .da-stage) s'appliquait PARTOUT.
 *   Il est désormais scopé @media(max-width:900px) — c'est-à-dire au seul cas où le
 *   hero est empilé (scène SOUS le formulaire → deux « Uncooking » à l'écran).
 *   Sur DESKTOP (>900px, 2 colonnes) la scène RESTE VISIBLE pendant le vrai
 *   uncooking : aucune confusion côte à côte, c'est la signature de la page.
 * Version : V1.4 · 2026-07-13
 * Changelog V1.4 : sélecteur langue mobile renforcé (pastille nav ≥44px de cible
 *   tactile, présence nette ≤560px) + footer aéré (padding/gaps, air autour du
 *   sélecteur de pied) + lien extension en chip (icône + fond, plus visible).
 * Version : V1.3 · 2026-07-13
 * Changelog V1.3 : + sélecteur de langue pastille (.langwrap/.langbtn/.langmenu,
 *   header light + footer dark, menu build-time). Remplace la rangée .footlang
 *   plate par la pastille sombre au pied (menu ouvert vers le haut). + suggestion
 *   de langue redesignée en toast (.lang-toast : titre + phrase + 2 choix,
 *   bas-droite, gaté sur le consentement pour ne pas concurrencer le bandeau
 *   légal) — remplace l'ancien .lang-suggest (lien nu).
 * Version : V1.2 · 2026-07-12
 * Changelog V1.2 : + styles du bandeau de suggestion de langue (.lang-suggest,
 *   bottom toast z-25 sous le sticky-copy) et du sélecteur permanent en pied
 *   (.footlang) — détection auto langue approche B (app.js V1.30).
 * Changelog V1.1 : fix critique — le commentaire d'en-tête contenait la
 *   séquence de fermeture de commentaire CSS en toutes lettres (dans un nom de
 *   sélecteur cité entre guillemets), ce qui fermait le commentaire prématurément
 *   et faisait disparaître le bloc de variables racine entier (--accent etc.
 *   non définies) → boutons/CTA invisibles (transparent sur fond clair) sur les
 *   4 pages. Détecté en QA visuelle avant commit, jamais parti en prod.
 * Changelog V1.0 : consolidation CSS (décision fondateur, priorité absolue) — source
 *   unique partagée pour les 4 pages (root EN/FR, /pro, /everyday), qui
 *   embarquaient chacune ~380 lignes de <style> quasi identique (copié-collé).
 *   C'était la cause-racine des bugs récurrents (cadre de carte, bouton ×,
 *   header débordant) : un correctif posé sur une page, oublié sur les 3
 *   autres. Rapatrie aussi le CSS que site/app.js injectait au runtime en 3
 *   IIFE séparées (.mt-banner, .fbm-x/.g413, .sub-x/.compact-band/.sticky-copy)
 *   — supprime l'axe de désync statique/runtime et le risque de FOUC pointés
 *   par l'audit mobile. Chaque page ne garde plus en inline que ses règles
 *   VRAIMENT propres (pro : .ext-card/.personas · everyday :
 *   .prose/.cases/.cta-card/.readmore) — tout le reste (y compris .illus-ba,
 *   partagé root+fr) vit ici désormais.
 * --- */
  :root{
    /* ⚠️ SR-24 (17/07) — LA SOURCE UNIQUE DU POINT DE RUPTURE « ÉTROIT ».
       Ce nombre vivait en dur DEUX fois : dans `@media(max-width:900px)` ici, et
       dans `isMobile` (app.js). Deux nombres magiques identiques dans deux
       fichiers = la façon exacte dont naît notre famille de bugs : le jour où l'un
       bouge, l'autre reste, et plus rien ne le signale.
       Il est déclaré ICI, et app.js le LIT (getPropertyValue('--bp-narrow')).
       ⚠️ Une limite du langage, à connaître : une `@media` NE PEUT PAS consommer une
       var() CSS — les media queries sont évaluées avant la cascade. Le `900px` reste
       donc écrit en clair dans les `@media` ci-dessous, et ce token en est la
       DÉCLARATION DE RÉFÉRENCE côté JS. Ce n'est pas une source unique parfaite ;
       c'est la meilleure possible sans build step. Si tu changes le seuil : change
       ce token ET les `@media(max-width:900px)`. Le JS, lui, suivra tout seul. */
    --bp-narrow:900px;
    /* ══ HERO SYSTEM (lot hero-system, 22/07) — LA SOURCE UNIQUE DE LA ZONE FIXE ═══
       Le hero unifié (.hero-sys) repose sur UNE grille commune : colonne gauche
       fluide (parole identique page à page) + une ZONE DROITE À FOOTPRINT FIXE,
       de mêmes largeur ET hauteur sur les 6 pages, quel que soit ce qu'elle porte
       (animation en disposition A, saisie lourde en disposition B). Ces trois
       tokens sont la définition de ce footprint : les changer ici les change
       partout, à la fois côté shared.css (pages outils) et côté gabarit home. */
    --hero-zone-w:560px;
    --hero-zone-h:468px;
    /* Hauteur de la barre de navigation (nav 22px×2 + logo 34px). Sert au calcul
       de `min-height` du hero plein-écran. Mesurée sur le rendu réel. */
    --nav-h:86px;
    /* ⚠️ SR-16 (17/07) — CETTE DÉCLARATION DIT DÉSORMAIS LA VÉRITÉ.
       Elle disait `"Neue Montreal","Inter",sans-serif` depuis toujours, et
       PERSONNE n'a jamais vu Neue Montreal : le <link> vers api.fontshare.com
       part bien à chaque chargement (83 pages), mais Fontshare répond HTTP 200
       avec 1 OCTET — Neue Montreal est une fonte commerciale Pangram Pangram
       qu'il n'a jamais hébergée (témoin : satoshi → 597 o, l'API marche).
       Aucun 404, aucune erreur console : le repli sur Inter était SILENCIEUX.
       Vérifié sur la prod : document.fonts ne contient qu'Inter + JetBrains Mono,
       et forcer un <h1> en Inter ne change pas sa largeur d'un pixel.
       Arbitrage Kevin : « le fil rouge ne se répare pas en ajoutant du poids —
       il se répare en disant vrai. » Donc on nomme ce qui rend réellement.
       (Ce que Switzer achèterait : 73,6 ko — chiffré, à rouvrir si on veut.) */
    --font-display:"Inter",ui-sans-serif,system-ui,-apple-system,sans-serif;
    --font-sans:"Inter",ui-sans-serif,system-ui,-apple-system,"Segoe UI",sans-serif;
    --font-mono:"JetBrains Mono",ui-monospace,SFMono-Regular,Menlo,monospace;
    /* Corps du brief — PILE SYSTÈME, arbitrée Kevin (17/07) après mesure :
       Source Serif 4 coûtait 217,7 ko (latin+latin-ext) pour UN écran. `ui-serif`
       donne New York sur Apple (dessiné pour l'écran) et Georgia ailleurs : 0 octet,
       zéro requête tierce, zéro question RGPD. Charter en secours (Android/Linux).
       Le brief doit rester VISUELLEMENT DISTINCT du handoff (mono) : c'est le
       contraste serif↔mono qui porte le sens (« à lire », pas « à recoller »),
       pas la marque de la fonte. */
    --font-brief:ui-serif,Charter,"Bitstream Charter",Georgia,serif;
    --accent:#FF2D55; --accent-hover:#E61F48; --accent-active:#C9153A; --accent-soft:#FFF0F3; --accent-shadow:rgba(255,45,85,.20); --accent-ring:rgba(255,45,85,.28);
    /* ÉCHELLE DU CTA (19/07) — le bouton primaire portait du blanc sur --accent :
       3,65:1, sous le seuil AA de 4,5:1. Mesuré sur la prod, 14px/500, donc PAS de
       dispense « grand texte » (qui exige ≥24px, ou ≥18,66px en gras). L'échelle
       entière descend d'un cran : un simple échange base←--accent-active aurait
       rendu le SURVOL plus clair que le repos et l'APPUI identique au repos —
       l'état pressé aurait disparu. Les trois niveaux s'assombrissent donc
       monotonement, et les trois passent AA. --accent reste la couleur de MARQUE
       (traits d'icône, fonds) : il n'est plus jamais porteur de texte. */
    --cta:#C9153A; --cta-hover:#B31232; --cta-active:#9E0F2D;
    /* ══ ORANGE LIBÉRÉ — DOCTRINE EN VIGUEUR (v4, 2026-07-19) ═══════════════════
       ⚠️ CE BLOC SUPERSÈDE L'ARBITRAGE « pas le même orange que HUMAN BRIEF » du
       15/07 (son texte est conservé plus bas dans ce fichier, marqué SUPERSÉDÉ, au
       bloc « POINT 2 (Kevin) »). Si les deux se contredisent, C'EST CELUI-CI QUI
       FAIT FOI. Il est écrit ici, sur la déclaration du jeton, pour qu'on ne puisse
       pas lire --accent2 sans lire sa règle.

       ORIGINE DU JETON (inchangée) : retour fondateur 15/07 — « tout est rouge ».
       Ambre-terracotta, pas inventé : formalise le ton déjà présent dans le dégradé
       décoratif du hero (.page-bg .b2/.b4, ~#FF7A3D/#FF9F78).

       CE QUI CHANGE — --accent2 N'EST PLUS RÉSERVÉ À HUMAN BRIEF.
       L'ancienne règle disait : « la palette n'a aucune teinte chromatique libre,
       --accent2 porte HUMAN BRIEF », et interdisait donc tout autre étiquetage.
       Arbitrage fondateur v4, motivé :
       · une palette crème + rouge seule est TROP MAIGRE ; la marque a besoin d'un
         second accent qui VIT réellement, pas d'un jeton mis sous cloche ;
       · « orange = Human Brief » était une décision de CIRCONSTANCE (il fallait
         sortir un CTA secondaire du tout-rouge), pas une décision de PRINCIPE.
       CONSTAT QUI CONFIRME : la réservation était déjà fictive dans ce fichier même.
       --accent2 y sert le bouton de veille du pied de page (.foot-watch-btn),
       l'icône e-mail des destinations (--c-mail), le raccourci .bt-next-go et la
       puce .note-spec-dot — aucun de ces emplois ne nomme Human Brief. La règle
       écrite décrivait une exclusivité que le code ne tenait plus.

       EMPLOIS AUTORISÉS : dégradés et fonds, survols, détails, ET différenciation
       d'une carte (Extract ou Migration explicitement permis par le fondateur).
       EMPLOI EFFECTIF EN v4 : le bloc d'icône de la carte MIGRATION sur la home
       (voir templates/home.html, bloc « MIGRATION PASSE EN ORANGE »).

       ⚠️ HUMAN BRIEF RESTE LE PHARE — PAR D'AUTRES MOYENS, ET ILS SONT INTACTS :
       sa taille, son sur-titre « Most people start here », sa position centrale,
       sa bordure 2px et son ombre. Aucun de ces signaux n'était la couleur. Le
       phare ne perd donc rien à la libération : il n'a jamais tenu par un monopole
       chromatique, seulement par un commentaire qui le prétendait.

       ⚠️ MESURE, PAS SAPIN DE NOËL. La règle est l'emploi PARCIMONIEUX : en v4,
       UNE carte sur cinq. Colorer trois cartes de trois teintes annulerait le
       bénéfice — on hiérarchise en distinguant UNE chose, pas toutes.

       ⚠️ SEUILS — L'ORANGE EST PLUS CLAIR QUE LE ROUGE, IL PARDONNE MOINS.
       Relevés à réutiliser plutôt qu'à recalculer :
         --accent2 #D9662E sur blanc #FFFFFF        = 3,57:1
           → INTERDIT pour du texte normal (seuil 4,5:1). Pour du texte orange
             conforme il faut --accent2-hover #C25422 = 4,58:1.
         #FFF sur --accent2 #D9662E                 = 3,57:1  ✓ ≥ 3:1 (SC 1.4.11)
         #FFF sur --accent2-hover #C25422           = 4,58:1  ✓
         #FFF sur des éclaircies d'orange candidates : #E07A3A = 2,99:1 ✗,
           #E27B33 = 2,95:1 ✗, #E8863F = 2,65:1 ✗ — un dégradé orange doit donc
           partir de --accent2 et DESCENDRE, jamais s'éclaircir au-dessus.
       Tout dégradé se balaie à son PIRE point, jamais en moyenne.
       --accent2 ne remplace jamais --accent sur un CTA principal, et l'échelle
       --cta n'est pas concernée par cette libération. */
    --accent2:#D9662E; --accent2-hover:#C25422; --accent2-soft:#FFF1E6; --accent2-shadow:rgba(217,102,46,.22);
    --bg:#FFFDFC; --bg2:#FAF8F7; --surface:#FFFFFF; --border:#E9E4E1; --divider:#F1EEEC;
    /* 19/07 — CONTRASTE AA DE --ink3. Ancienne valeur #938D88 : 3.23:1 sur --bg,
       3.10:1 sur --bg2, 3.28:1 sur --surface — SOUS le seuil AA 4.5:1 alors que le
       jeton sert de couleur de TEXTE courant (.trust, .gb-note, .dn-cap, .hint,
       .privacy-line, .stepcap, .mg-hint, details.q .plus, placeholders…).
       Nouvelle valeur #756E6A : 4.94 / 4.73 / 5.01. Teinte conservée (gris chaud,
       hue 27°, saturation ~5%) — le registre secondaire est intact, --ink3 reste
       nettement plus clair que --ink2 (6.07:1 sur --bg2) : l'échelle
       --ink 16.94 > --ink2 6.07 > --ink3 4.73 reste ordonnée et lisible.
       ⚠️ --ink3 était AUSSI utilisé sur le PIED DE PAGE, qui est SOMBRE
       (footer{background:var(--ink)} = #171716). Là, l'ancien #938D88 passait déjà
       (5.47:1) et l'assombrir le CASSERAIT (#756E6A n'y vaut que 3.58:1).
       Aucune valeur unique ne peut passer 4.5:1 à la fois sur #FAF8F7 et #171716 :
       le plafond mathématique est 4.11:1 (gris 121). D'où le jeton dédié
       --ink3-on-dark ci-dessous, qui conserve l'ancienne valeur pour les surfaces
       sombres. Ne jamais utiliser --ink3 sur fond sombre, ni l'inverse. */
    --ink:#171716; --ink2:#625E5A; --ink3:#756E6A; --ink3-on-dark:#938D88;
    /* 20/07 — CONTRASTE AA DE --success. Ancienne valeur #16845B : 4,19:1 sur son
       propre fond --success-soft #E7F6EE, sous les 4,5:1 de SC 1.4.3. Un SECOND
       fond était hors seuil et n'avait pas été relevé : --bg2 #FAF8F7 = 4,42:1.
       Nouvelle valeur #157A55 (même teinte 158°, même saturation ; deux crans de
       clarté en moins) : 4,77:1 sur --success-soft, 5,03:1 sur --bg2, 5,32:1 sur
       --surface, 5,25:1 sur --bg.
       ⚠️ JETON GLOBAL — BALAYAGE COMPLET FAIT AVANT DE TOUCHER, à cause du
       précédent --ink3 (qui, lui, servait AUSSI le pied de page sombre et avait dû
       être scindé en --ink3 / --ink3-on-dark, aucune valeur unique ne passant sur
       les deux surfaces). Ici la conclusion est INVERSE, et c'est pourquoi on ne
       scinde PAS : les 19 emplois de --success se répartissent en deux familles,
       (a) TEXTE sur fond clair et (b) APLAT portant du BLANC (.rf-check,
       .ready-flag, .pc-check, .di-flag, .inst-status, .btn-*.copied). Assombrir
       AMÉLIORE les deux : le blanc sur --success passe de 4,68:1 à 5,32:1. Et
       AUCUN emploi ne pose --success sur une surface sombre — vérifié règle par
       règle sur le pied de page (qui n'utilise que --ink3-on-dark, #c9c2bc,
       #8a837d, #fff). Le piège --ink3 ne se rejoue donc pas : une valeur unique
       sert toutes les surfaces, et un jeton -on-dark serait du bruit.
       ⚠️ CE CORRECTIF N'ATTEINT PAS LES HUBS DE BLOG — vérifié sur le rendu, pas
       sur la règle. Leurs pastilles Guide / How-to s'écrivent bien en
       `color:var(--success)`, ce qui laisse croire qu'elles héritent d'ici ; mais
       les 5 hubs (site/blog + fr|es|de|pt) NE CHARGENT PAS cette feuille (seulement
       page-bg.css) et REDÉCLARENT `--success:#16845B` dans leur propre <style>.
       Le var() y résout donc sur leur copie locale, et elles restent à 4,19:1.
       Non corrigé ici À DESSEIN : l'arbitrage de couleur des pastilles de catégorie
       est ouvert avec le fondateur, et ces 5 fichiers sont hors du périmètre de ce
       lot. À traiter à part — c'est un échec AA réel, pas un faux positif. */
    --success:#157A55; --success-soft:#E7F6EE; --success-border:#B9E6CE;
    --err-bg:#FDECEA; --err-border:#C0392B; --err-ink:#7C2418;
    --r-sm:.5rem; --r-md:.75rem; --r-lg:1rem; --r-xl:1.5rem;
    --sh-sm:0 1px 2px rgba(23,23,22,.04); --sh-card:0 8px 24px rgba(23,23,22,.07); --sh-cardh:0 18px 40px rgba(23,23,22,.12); --sh-accent:0 10px 24px var(--accent-shadow);
    /* ⚠️ TOKENS DE LA FAMILLE .sub-* (Google + champ + Envoyer : UNE largeur, UNE
       hauteur). DÉFINIS ICI, AU ROOT, ET PAS SUR .sub-block — c'est le correctif
       du bug iPhone du 16/07 : ils vivaient sur .sub-block, or .sub-form/.sub-email/
       .sub-btn sont AUSSI réutilisés hors de ce conteneur (tunnel .bt-stage, mur
       gate.js .uck-gate-modal). Sans ancêtre .sub-block les deux var() résolvaient
       VIDE → height:var(--sub-h) invalide → height:auto → bouton Continue à 14,5px
       (« affaissé ») et max-width:var(--sub-w) → none → cap 320px disparu, textes
       hors cadre. Au root, TOUTE réutilisation future de .sub-* est correcte par
       défaut : la classe de bug meurt ici, on ne la re-patche pas au cas par cas. */
    --sub-w:320px; --sub-h:46px;
  }
  *{box-sizing:border-box}
  html,body{margin:0;background:var(--bg);color:var(--ink);font-family:var(--font-sans);text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased}
  [hidden]{display:none!important} /* fix : bat les .btn/.modal display:flex qui écrasaient l'attribut hidden */
  ::selection{background:#FFD6DE;color:var(--ink)}
  .display{font-family:var(--font-display)}
  /* largeur remontée 1120→1250px (retour fondateur 08/07 : site trop étroit vs
     standards modernes Linear/Stripe/Notion — haut de la fourchette 1140-1280px
     citée, mesuré + validé sur site/_width-mockup.html) */
  /* Laize élargie (lot hero-system, 22/07) : le hero unifié demande plus d'air et
     des titres plus grands. `min(1640px, 93vw)` va vers les bords sans jamais
     compter la barre de défilement (pas de `100vw` → `scrollWidth===clientWidth`
     préservé). Padding latéral fluide `clamp(28px,3vw,56px)` : 28px au plancher
     (inchangé sous ~933px), jusqu'à 56px sur grand écran. */
  .container{max-width:min(1640px,93vw);margin:0 auto;padding:0 clamp(28px,3vw,56px)}
  svg.i{width:1em;height:1em;stroke:currentColor;stroke-width:1.5;fill:none;stroke-linecap:round;stroke-linejoin:round;flex:none}
  .env{position:fixed;bottom:12px;left:12px;font-size:10.5px;color:var(--ink3);border:1px solid var(--border);background:#fff;border-radius:6px;padding:3px 8px;text-transform:uppercase;letter-spacing:.08em;z-index:20;box-shadow:var(--sh-sm)}

  /* nav */
  .nav{display:flex;align-items:center;gap:10px;padding:22px 0}
  .logo{display:inline-flex;align-items:baseline;gap:.14em;font-family:var(--font-display);font-weight:700;font-size:24px;letter-spacing:-.03em;color:var(--ink);text-decoration:none}
  .logo .dot{width:8px;height:8px;border-radius:50%;background:var(--accent);align-self:center;transform:translateY(1px)}
  .nav .logo{font-size:34px}
  a.logo:focus-visible{outline:2px solid currentColor;outline-offset:6px;border-radius:6px} /* F17 (15/09) : le logo est un lien, son focus se voit */
  .nav .logo .dot{width:10px;height:10px}
  /* ══ LA BARRE EST EN DEUX GROUPES, PAS EN UN BLOC ══════════════════════════════
     Arbitrage fondateur v4 : « Tools · Human Brief · Handoff » collés au LOGO, à
     gauche ; « Comment ça marche · FAQ · langue · CTA » restent à droite.
     MESURÉ AVANT (1440px, en) : `.links` était en `margin-left:auto`, donc collé à
     DROITE en un seul bloc — le déclencheur Tools démarrait à x=522 alors que le
     logo se referme à x=255. 267px de vide séparaient le logo de la première
     entrée : c'est ce vide que l'arbitrage supprime (38px après changement).

     COMMENT — et pourquoi PAS `margin-left:auto` déplacé ailleurs : `.links` prend
     désormais toute la largeur restante (`flex:1`) et le DERNIER raccourci porte un
     `margin-right:auto`. Une marge auto en flex absorbe tout l'espace libre à SON
     point : ce qui la précède (Tools, Human Brief, Handoff) va au début, ce qui la
     suit (How it works, FAQ, langue, CTA) va à la fin. Un seul point de bascule,
     et il suit la liste des raccourcis au lieu d'être accroché à un lien nommé.

     ⚠️ LE REPLI EST LA RAISON DU `justify-content:flex-end`. Sous 1080px les deux
     raccourcis passent en `display:none` (règle plus bas) — la marge auto DISPARAÎT
     avec eux. Sans `flex-end`, la barre entière retomberait à GAUCHE sur toute la
     plage 761-1080px, où elle était historiquement à droite. `flex-end` est donc le
     comportement de repli exact d'avant ce changement, pas une décoration.
     `min-width:0` : sans lui, `flex:1` refuse de descendre sous la largeur
     min-content du groupe et rouvrirait un débordement horizontal en allemand. */
  .nav .links{margin-left:28px;flex:1 1 auto;min-width:0;
    display:flex;align-items:center;justify-content:flex-end;gap:22px}
  .nav .links .nav-shortcut--last{margin-right:auto}
  .nav .links a{color:var(--ink2);text-decoration:none;font-size:14px;font-weight:500}
  @media(hover:hover){.nav .links a:hover{color:var(--ink)}}
  /* MERGE (Kevin) — l'onglet de la feature « doit être vu » : accent + ✦, distinct des
     liens d'ancre. Nom = i18n NAV_MERGE (« Fusion » par défaut, à trancher par Kevin). */
  .nav .links a.nav-merge{color:var(--accent);font-weight:600;display:inline-flex;align-items:center;gap:5px}
  .nav .links a.nav-merge::before{content:"✦";font-size:11px}
  @media(hover:hover){.nav .links a.nav-merge:hover{color:var(--accent-hover)}}
  /* ══ SR-04·A « LA RÉPONSE » (17/07, arbitré Kevin) ═══════════════════════════
     Le constat du fondateur (« site statique des années 2010, les boutons ne
     répondent pas ») était JUSTE en effet et FAUX en cause : le hover existe
     depuis toujours (translateY + transition .14s). Le vrai défaut est pire —
     il n'était protégé par AUCUN `@media(hover:hover)` (0 occurrence pour 44
     règles `:hover`). Sur iOS, un tap laisse donc le bouton COLLÉ en état
     survolé (soulevé, foncé) jusqu'au tap suivant ailleurs. Mobile n'était pas
     inerte : il était CASSÉ. Et `:active` n'existait que sur 1 bouton /~153 —
     donc au doigt, rien ne confirmait jamais le geste.

     ⚠️ POURQUOI ON ENVELOPPE PLUTÔT QUE D'ÉNUMÉRER UN RESET `hover:none` :
     un bloc de reset devrait lister chaque règle existante, et toute règle
     `:hover` future y échapperait EN SILENCE — c'est exactement le défaut qui a
     produit le bug `--sub-w` (énumérer corrige l'instance, pas la classe). On
     enveloppe : le hover n'existe QUE là où un vrai curseur existe.
     « Pas un truc lent » (contrainte Kevin) : press 90ms, hover 140ms. Le press
     est plus RAPIDE que le hover — une confirmation doit être instantanée. ══ */
  .btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;border-radius:var(--r-md);font-family:var(--font-sans);font-weight:600;cursor:pointer;transition:.14s ease;border:none;line-height:1;-webkit-tap-highlight-color:transparent}
  .btn-primary{background:var(--cta);color:#fff;box-shadow:var(--sh-accent)}
  .btn-secondary{background:var(--surface);color:var(--ink);border:1px solid var(--border);box-shadow:var(--sh-sm)}
  @media(hover:hover){
    .btn-primary:hover{background:var(--cta-hover);transform:translateY(-1px)}
    .btn-secondary:hover{background:var(--bg2);border-color:var(--ink)}
  }
  /* La réponse au geste — souris ET doigt. C'est CE bloc qui répond vraiment au
     « les boutons auraient dû être animés » : le mouvement dit « je t'ai eu »,
     il ne décore pas. */
  .btn:active{transform:translateY(1px) scale(.988);transition-duration:90ms}
  .btn-primary:active{background:var(--cta-active)}
  .btn-secondary:active{background:var(--bg2)}
  /* Le clavier : il n'avait aucun anneau de focus visible. */
  .btn:focus-visible{outline:none;box-shadow:0 0 0 3px var(--accent-ring),var(--sh-accent)}
  @media(prefers-reduced-motion:reduce){
    .btn:active{transform:none} /* on garde la réponse COULEUR, on retire le mouvement */
  }
  .btn-sm{height:38px;padding:0 15px;font-size:14px}
  .btn-lg{height:56px;padding:0 24px;font-size:16px}

  /* HERO — mix B (respiration verticale eyebrow→H1→H2) + C (carte unique
     champ+CTA+trust+aide), décision fondateur 08/07 après 2 tours de mock
     (site/_hero-mockup.html section 5 « Variante MIX — retenue »). H3
     (hero-lead) retiré : le mécanisme est déjà montré par le placeholder de
     l'input + la section How it works plus bas. Coches vertes (checkrow) +
     encadré privacy fusionnés en UNE trust-line non encadrée (déjà portée
     par .privacy-line, juste déboxée ici) — plus d'objet de réassurance en
     double. Carte 640px = largeur déjà utilisée par .inputwrap (aucune
     largeur nouvelle) ; le calage retenu (vs 560px recentré écarté) tombe
     verticalement sous les extrémités du H1, reliant visuellement carte et
     texte au lieu d'un second objet plaqué à part. */
  .hero{text-align:center;padding:60px 0 52px}
  /* 20/07 — CADRE BLANC (décision fondateur : « que ça ressorte »), sur les 19
     instances de `.eyebrow` — TOUTES portent le même énoncé « Works with ChatGPT
     and Claude » (localisé) et toutes portent `.hot` : le composant est homogène,
     la règle globale ne déborde donc sur aucun autre contenu. (Vérifié au parseur
     DOM sur classe exacte : `msheet-eyebrow` / `bt-eyebrow` sont d'autres objets
     et ne sont PAS touchés.)
     ⚠️ MESURÉ : le blanc SEUL ne peut pas « ressortir » ici. --surface #FFFFFF
     contre le fond RÉELLEMENT composité sous le hero (#FFF9F9 — le dégradé
     `.page-bg` compose, cf. note du `.sub` ci-dessous) ne fait que 1,04:1. Ce
     n'est donc pas l'aplat qui détache la pastille mais le FILET + l'ombre
     portée ; l'aplat blanc, lui, sert à découpler le texte du dégradé (et
     améliore au passage les deux mesures de texte : --ink2 6,17 → 6,43:1,
     `.hot` --accent-active 5,52 → 5,75:1).
     `border-radius:999px` et le filet reprennent la langue déjà posée par les
     pastilles de confiance du même hero — aucune forme nouvelle n'est introduite.
     La marge basse passe de 28 à 22px : le cadre ajoute ~20px de hauteur au bloc,
     et la respiration eyebrow→H1 réglée en mix B se mesure sur l'objet, pas sur
     la ligne de base. */
  .eyebrow{font-size:18px;font-weight:700;letter-spacing:-.01em;color:var(--ink2);text-transform:none;margin:0 0 22px;display:inline-flex;align-items:center;gap:9px;font-family:var(--font-display);
    background:var(--surface);border:1px solid var(--border);border-radius:999px;
    padding:9px 20px;box-shadow:0 2px 8px rgba(23,23,22,.06)}
  /* 19/07 — mesuré 16px/700 sur /pro/, /extract/, /everyday/, /migration/,
     /handoff/, /mask/ (× 5 langues) : 3,60:1 sur --bg. 16px gras N'EST PAS
     du « grand texte » (seuil ≥18,66px en gras), donc le seuil applicable est
     4,5:1 — échec. Passage à --accent-active (5,67:1), conformément à la règle
     déjà posée au root : --accent n'est jamais porteur de texte. */
  .eyebrow .hot{color:var(--accent-active);font-weight:700}
  .hero h1{font-family:var(--font-display);font-weight:700;font-size:clamp(2.5rem,5vw,4.4rem);line-height:1.06;letter-spacing:-.035em;margin:0 auto 20px;max-width:19ch}
  /* ⚠️ CONTRASTE — MESURÉ EN PIXELS, PAS SUR LE JETON. Le sous-titre du hero n'a
     AUCUN ancêtre opaque : la marche d'ascendance s'arrête sur <body>, donc le
     dégradé ambiant `.page-bg` (4 spans radiaux roses/orangés, flou 84px,
     opacité .4) compose RÉELLEMENT sous ce texte. Le fond effectif n'est donc pas
     --bg #FFFDFC mais #FFF9F9 (moyenne relevée sur les 16 632 pixels de la boîte,
     texte rendu transparent) — plus rose, donc PIRE que ce que le jeton laissait
     croire : --accent y mesure 3,50:1 (3,43:1 au pixel le moins favorable), pas
     les 3,60:1 du calcul sur jeton.
     PAS DE DISPENSE GRAND TEXTE : relevé 23,2px / 600. L'exemption exige ≥24px,
     ou ≥18,66px en GRAS — et 600 n'est pas gras au sens de la règle. Le seuil
     applicable reste donc 4,5:1.
     → --accent-active #C9153A = 5,52:1 (5,40:1 au pire pixel).
     Borne de sûreté, parce que les 4 spans DÉRIVENT en boucle et qu'aucune capture
     ne prouve la phase la plus défavorable : en composant les QUATRE spans à leur
     alpha maximal SIMULTANÉMENT (impossible physiquement, ils sont à des positions
     distinctes — c'est donc un majorant strict), le fond le plus teinté atteignable
     est #FFE0DB. Même là, --accent-active tient à 4,64:1, quand --accent tombe à
     2,94:1. La correction passe dans TOUTES les phases de l'animation. */
  /* ══ MOTIF A2 (23/07, Kevin) — le sous-titre n'est PLUS un mur de rouge ═══════
     Le corps du sous-titre lit sombre/assuré (--ink, 500) ; SEULS les mots
     porteurs qu'un agent enveloppe dans <b> passent en rouge (--accent-active,
     700). L'ancien réglage peignait toute la phrase en --accent-active/600. */
  .hero .sub{color:var(--ink);font-weight:500;font-size:clamp(1.15rem,2.5vw,1.45rem);letter-spacing:-.01em;margin:0 0 36px}
  .hero .sub b{color:var(--accent-active);font-weight:700}
  .hero h1.h1-duo{max-width:30ch;font-size:clamp(2.2rem,4.3vw,3.6rem)}
  @media(max-width:560px){.hero h1 br{display:none}}
  /* retour fondateur 08/07 (5e tour) : le cadre de carte (bg2+border+shadow)
     créait un effet de double encadrement — le champ blanc arrondi À
     L'INTÉRIEUR d'une carte grise elle-même arrondie, lu comme un bug plus
     qu'un choix de design. Retiré : simple conteneur de largeur, plus de
     fond/bordure/ombre propres. */
  .inputwrap{max-width:640px;margin:0 auto}

  /* ═══════════ DA « The Instrument » — hero product-led (home) ═══════════
     Fond mouvant B propagé à toute la page + hero 2 colonnes (message/action à
     gauche, l'instrument animé à droite). Tout l'instrument est SCOPÉ sous
     .da-stage → zéro collision avec l'écran résultat réel (.handoff, .btn-copy,
     .flag sont renommés .di-* dans instrument.js par sécurité). Décoratif. */
  .page-bg{position:fixed;inset:-22%;z-index:0;pointer-events:none;filter:blur(84px);opacity:.3}
  .page-bg span{position:absolute;width:48%;height:54%;border-radius:50%}
  /* ══ FOND AMBIANT — DÉSATURÉ ET FORTEMENT ATTÉNUÉ (lot cohérence, 21/07) ═══════
     Grief Kevin : « le fond est trop saturé ». Les quatre halos passaient à
     alpha .26–.50 sur des teintes vives (rouge #FF2D55, orange #FF7A3D) : composés
     sur le crème de page, ils posaient des blooms roses/pêche FRANCS aux angles
     (b3 haut-gauche, b4 bas-gauche). On désature vers un rosé très pâle quasi
     blanc cassé et on abaisse fortement l'alpha (.26/.50 → .06/.10) : le fond
     RESPIRE au lieu de crier. Teintes rapprochées du crème (pas de rouge/orange
     vif), stops resserrés (60→58 %) pour éteindre les blooms d'angle. Règle GLOBALE
     (toutes pages) → geste conservateur : structure, positions et animations
     inchangées, seule l'intensité chromatique baisse. */
  /* v2 (lot correctif) : halos ENCORE abaissés (opacité du calque .4→.3 ci-dessus,
     et alphas .10/.07 → .06/.04) + stops resserrés — les halos d'angle deviennent
     quasi invisibles, le fond est un blanc cassé qui respire à peine. */
  .page-bg .b1{background:radial-gradient(circle,rgba(255,182,196,.06),transparent 58%);top:-6%;left:44%;animation:daDrift1 34s ease-in-out infinite}
  .page-bg .b2{background:radial-gradient(circle,rgba(255,210,188,.04),transparent 58%);top:38%;left:60%;animation:daDrift2 40s ease-in-out infinite}
  .page-bg .b3{background:radial-gradient(circle,rgba(255,228,233,.06),transparent 54%);top:6%;left:0%;animation:daDrift3 38s ease-in-out infinite}
  .page-bg .b4{background:radial-gradient(circle,rgba(255,220,202,.04),transparent 54%);top:46%;left:10%;animation:daDrift1 44s ease-in-out infinite reverse}
  @keyframes daDrift1{0%,100%{transform:translate(0,0) scale(1)}50%{transform:translate(-7%,6%) scale(1.12)}}
  @keyframes daDrift2{0%,100%{transform:translate(0,0) scale(1.05)}50%{transform:translate(-10%,-7%) scale(.93)}}
  @keyframes daDrift3{0%,100%{transform:translate(0,0) scale(1)}50%{transform:translate(8%,5%) scale(1.1)}}
  .container,footer,section{position:relative;z-index:1}  /* contenu au-dessus du fond */

  /* hero 2 colonnes */
  .hero-da{display:grid;grid-template-columns:1.04fr .96fr;gap:52px;align-items:center;text-align:left;padding:52px 0 60px}

  /* ── REMONTÉE DU HERO POUR PASSER LA RANGÉE AU-DESSUS DE LA LIGNE DE FLOTTAISON
     (20/07, DESKTOP UNIQUEMENT) ───────────────────────────────────────────────
     DEMANDE : « sur desktop la rangée de badges est encore trop basse ». On ne
     refait PAS la hauteur du hero — on récupère juste de quoi franchir la ligne
     de flottaison, par le HAUT. Le bas a déjà été traité au lot précédent
     (`.hero-da + .trustbadges{margin-top:-8px}`, cf. bloc l.~3450) : ne pas
     cumuler un second geste par le bas, la rangée décollerait du hero.

     LE DÉFICIT EST MESURÉ, PAS ESTIMÉ (viewport réel via setDeviceMetricsOverride,
     bas de `.trustbadges` vs `innerHeight`, /brief/) :
         1440×900  → bas de rangée 821,1px pour 900px : DÉJÀ VISIBLE (+78,9px)
         1280×800  → bas de rangée 813,0px pour 800px : IL MANQUE 13px  ← la cible
     La rangée se translate 1:1 avec ce `padding-top` (vérifié par balayage
     52/40/38/36/34). 52 − 16 = 36px laisse donc 3px sous la ligne à 1280×800 et
     ne coûte rien à 1440. C'est le plus petit pas franc qui couvre les 13px
     mesurés ; 34px (le palier ≤900px) donnerait 5px si on veut plus de marge.

     ⚠️ POURQUOI `:has()` ET PAS `.hero-da` TOUT COURT — la borne est le sujet du
     geste. `.hero-da` est porté par 20 pages (parsé sur le markup, pas déduit) :
     les 19 à badges + /mask/. Or la famille réellement trop basse est celle
     où la rangée SUIT le hero dans le flux, `.hero-da + .trustbadges` = 11 pages
     (/brief/, /pro/ ×5, /everyday/ ×5). Les 8 autres (/migration/ ×5, /merge/,
     /handoff/, /extract/) portent bien un `.hero-da` mais leurs badges sont
     ailleurs dans l'arbre, et /mask/ n'en a pas : un `padding-top` nu sur
     `.hero-da` remonterait 9 pages que personne n'a demandé de toucher. C'est
     exactement la borne déjà retenue plus bas pour la marge des badges — même
     famille, même sélecteur, ici retourné vers le PARENT, ce que seul `:has()`
     permet (le frère adjacent ne sait viser que le suivant).
     SUPPORT : `:has()` est Baseline « widely available » (Chrome 105 / Safari
     15.4 / Firefox 121). Un moteur qui ne le connaît pas JETTE LA RÈGLE ENTIÈRE
     et retombe sur les 52px d'origine — dégradation propre, rien ne casse. D'où
     aussi la règle ISOLÉE : mise en liste avec d'autres sélecteurs, elle les
     entraînerait tous dans sa chute.

     ⚠️ POURQUOI `@media(min-width:901px)` ET PAS UNE RÈGLE NUE. `.hero-da` l.725
     n'est PAS dans une media query : c'est le palier ≤900px (l.~819, `padding:
     34px 0 40px`, spécificité 0,1,0) qui l'écrase plus bas. Une règle nue
     `.hero-da:has(+ .trustbadges)` pèse 0,2,0 : elle GAGNERAIT aussi sous 900px
     et repousserait le hero mobile de 34 → 36px. C'est le même piège de
     spécificité que celui documenté pour `.trustbadges` (cf. bloc l.~3545). On
     borne donc explicitement au desktop. 901 et non 900 : `max-width:900px` et
     `min-width:900px` se recouvrent à 900px pile — disjoint, on ne se demande
     plus qui gagne. Le mobile et le 768px ne bougent donc d'aucun pixel. */
  /* 36 → 34px (20/07, arbitrage fondateur). MOTIF : à 36px, le coussin réel sous
     la ligne de flottaison à 1280×800 n'était que de 1,5px — et NON les 3,0px
     mesurés sur ce lot pris isolément. L'écart vient de l'ALLONGEMENT DU VISUEL
     de /brief/ (`.hb-doc-body{min-height:424px}`, posé le même jour) qui en
     reprend la moitié : aucun des deux lots ne pouvait le voir depuis son côté,
     seule la mesure du COMBINÉ le montre. 1,5px est sous le bruit — un fallback
     de police, un autre moteur, un facteur d'échelle, et la rangée repasse sous
     la ligne. 34px double le coussin sans autre effet : c'est déjà la valeur du
     palier ≤900px, donc les deux paliers convergent au lieu de diverger.
     ⚠️ À RE-MESURER SUR LE COMBINÉ si l'un des deux lots bouge — pas sur celui-ci
     seul, c'est précisément l'erreur qui a produit le 36. */
  /* 34 → 26px (20/07, 3e passe, arbitrage fondateur : « remonte la ligne de badges
     de quelques px de plus — encore un peu trop bas »). On CONTINUE le geste par
     le HAUT, celui-là même du palier précédent : le levier du bas
     (`.hero-da + .trustbadges{margin-top:-8px}`) reste intact, ne pas l'empiler.
     MESURÉ SUR LE COMBINÉ (main + le lot /brief/ en cours), viewport réel, polices
     stabilisées, bas de `.trustbadges` vs `innerHeight` :
         1280×800  coussin  4,97 → 12,97px   (la laize critique)
         1440×900  coussin 96,86 → 104,86px  (jamais en cause)
     La rangée se translate 1:1 avec ce padding (re-balayé 34/32/30/28/26/24/20 :
     2px de padding = 2px de coussin, sans exception). 26px est le plus petit pas
     qui sorte franchement du bruit : à 4,97px un fallback de police ou un autre
     moteur repasse la rangée sous la ligne — c'est exactement la panne du 36.
     CE QU'ON PAIE, et pourquoi c'est acceptable : la respiration nav→h1 tombe de
     97 à 89px. Elle reste très au-dessus de ce qui serrerait le hero contre la
     nav, et c'est le seul poste débité — le bloc hero+badges se translate d'un
     seul tenant, l'écart hero↔badges ne bouge pas d'un pixel.
     ⚠️ LA CONVERGENCE DES DEUX PALIERS EST ABANDONNÉE ICI, SCIEMMENT. Le palier
     ≤900px vaut toujours 34px (l.~819) : 26 ≠ 34, les deux paliers divergent de
     nouveau. C'était l'argument de confort du passage 36→34, pas une contrainte —
     et le fondateur demande explicitement de remonter davantage, ce qui ne peut
     pas se faire en restant collé à la valeur mobile. Le ≤900px ne bouge PAS :
     la borne `min-width:901px` est disjointe, mobile et 768px inchangés au pixel.
     ⚠️ Le 768×1024 (coussin 2,16px) N'EST PAS ADRESSÉ par ce geste et ne peut pas
     l'être ici : 768 < 901, la règle ne s'y applique pas. C'est un autre palier,
     à traiter séparément si le fondateur le demande. */
  @media(min-width:901px){
    .hero-da:has(+ .trustbadges){padding-top:26px}
  }

  /* ══ B4 — HERO HANDOFF : DIMENSIONNEMENT RÉSOLUTION-INDÉPENDANT (lot 21/07) ═════
     Grief Kevin : sur grand écran le hero handoff est « trop petit / tassé »,
     calibré pour un laptop atypique. Référence de PROPORTION : neon-free.ch/en —
     un hero qui occupe franchement l'espace et rend IDENTIQUE d'un 13" à un 27".
     La recette de cette référence est un dimensionnement RÉSOLUTION-INDÉPENDANT :
       · titre en clamp() dont la borne haute et la pente vw sont relevées (le
         h1-duo plafonnait à 3,6rem dès ~1000px puis ne grandissait plus — d'où le
         « tassé » sur 1512/1920) ;
       · espacements verticaux clés en unités VIEWPORT (vh) bornées par clamp, pour
         que le hero gagne de l'air proportionnellement à la fenêtre au lieu d'un
         nombre de pixels figé ;
       · sous-titre et largeur de colonne maîtrisés (max-width en ch).
     ⚠️ SCOPÉ À HANDOFF SEUL. `body[data-intent="home"]` ne cible QUE /handoff/
     (vérifié : unique porteur de cette valeur sur les 24 pages ; les homes ont un
     <body> nu, les autres pages d'action ont merge/migration/brief/pro/everyday).
     Le hero des autres pages reste STRICTEMENT inchangé — Kevin valide la capture
     handoff avant toute propagation. Spécificité `body[data-intent] .hero-da`
     = 0,2,1 : passe au-dessus du `.hero-da:has(+ .trustbadges)` (0,2,0) ci-dessus,
     donc le padding-top scopé gouverne bien sur handoff. */
  /* ══════════════════════════════════════════════════════════════════════════════
     ██  LE HERO SYSTEM (.hero-sys) — UNE GRILLE, DEUX DISPOSITIONS  ████████████████
     ══════════════════════════════════════════════════════════════════════════════
     PROBLÈME RÉSOLU (mots de Kevin) : chaque page avait un hero DIFFÉRENT (carte à
     droite, sélecteur à droite, champ à gauche…) → le produit se lisait comme 6
     histoires au lieu d'UN geste. Ici on définit UN système et on y coule les 6
     pages (home · handoff · brief · extract en dispo A ; merge · migration en B).

     LA GRILLE COMMUNE, IDENTIQUE PARTOUT :
       · COLONNE GAUCHE fluide — eyebrow → titre → sous-titre → (frise/badges).
         Mêmes tailles de titre, mêmes hauteurs, mêmes positions verticales sur
         TOUTES les pages. C'est la constante qui rend la marque reconnaissable.
       · COLONNE DROITE = une ZONE À FOOTPRINT FIXE (--hero-zone-w × --hero-zone-h),
         de mêmes largeur ET hauteur sur les 6 pages. Elle porte, selon la page :
           — dispo A : l'animation/la démo (#instrument-mount, ou le doc /brief/) ;
           — dispo B : la SAISIE lourde (multi-liens /merge/, sélecteur /migration/).

     RÈGLE D'OR : la zone droite occupe le MÊME footprint dans les deux dispositions.
     Seul CHANGE le côté où vit le champ de saisie :
       · dispo A → champ + CTA à GAUCHE (sous le sous-titre), démo à droite ;
       · dispo B → champ à DROITE (il a besoin de la place), gauche sans champ.
     Parce que la zone est de taille FIXE (non étirée au viewport), il n'y a pas de
     vide-intérieur-de-carte, et /migration/ (dispo B) remplit le hero comme les
     autres : sa saisie tient dans la zone standard, plus bornée par la hauteur
     naturelle du formulaire.

     ⚠️ CE BLOC REMPLACE l'ancien B4 scopé `body[data-intent="home"]` (handoff seul).
     B4 était « le traitement validé, plus grand/plus plein » — Kevin demande de
     l'ÉTENDRE à toutes les pages sans jamais le rapetisser. On le généralise donc
     à `.hero-sys` : même échelle typographique, mêmes espacements, pour les 6.
     ⚠️ OPT-IN par CLASSE : seules les pages portant `.hero-sys` sont touchées.
     /pro/, /everyday/, /mask/ gardent `.hero-da` nu et ne bougent pas. */

  /* — Zone fixe : mêmes largeur/hauteur partout (mesurable au pixel). Déclarée hors
       media pour le repli mobile, mais la hauteur fixe n'est imposée qu'en 2 colonnes
       (≥901px) — voir plus bas. */
  .hero-sys .hero-zone{min-width:0;position:relative}

  /* — FRISE DE PARCOURS (/migration/) : rangée de pastilles sous le sous-titre en
       dispo B, où la gauche n'a pas de champ. `--ink2` #625E5A sur `--surface` = 6,4:1
       (12,5px, seuil 4,5:1) ; la flèche en `--accent-active` = 5,75:1. */
  .hero-frieze{display:flex;flex-wrap:wrap;gap:8px;margin:8px 0 0;padding:0;list-style:none}
  .hero-frieze li{display:inline-flex;align-items:center;gap:7px;
    font:600 12.5px/1 var(--font-sans);color:var(--ink2);background:var(--surface);
    border:1px solid var(--border);border-radius:999px;padding:8px 13px;box-shadow:var(--sh-sm)}
  .hero-frieze .ar{color:var(--accent-active);font-weight:700}
  @media(max-width:900px){.hero-frieze{justify-content:center}}

  /* ── HERO « COMME LA HOME » POUR /merge/ ET /migration/ (lot harmonisation) ─────
     Ces deux pages demandent une action multi-étapes (2–5 liens ; cible + lien) qui
     NE VIT PLUS dans le hero. Le hero devient dispo A : eyebrow + H1 + H2 + démo à
     droite + un vrai bouton CTA ; le CTA défile vers une SECTION D'ACTION pleine
     laize, centrée, sous le hero, qui héberge la vraie UI au large. */
  .hero-sys .hero-actions{display:flex;align-items:center;gap:14px;flex-wrap:wrap;margin:2px 0 20px}
  .hero-cta-scroll{text-decoration:none}
  .hero-cta-scroll .hc-i{width:17px;height:17px;flex:none;fill:none;stroke:currentColor;
    stroke-width:2.2;stroke-linecap:round;stroke-linejoin:round;transition:transform .25s cubic-bezier(.2,.8,.3,1)}
  @media(hover:hover){.hero-cta-scroll:hover .hc-i{transform:translateY(3px)}}
  @media(prefers-reduced-motion:reduce){.hero-cta-scroll .hc-i{transition:none}}
  /* La section d'action, pleine laize, centrée, aérée. `scroll-margin-top` dégage
     la hauteur de nav quand le CTA y défile. */
  .hero-action{padding:8px 0 8px;scroll-margin-top:96px}
  .hero-action > .container > .mg-card,
  .hero-action > .container > .hero-action-form{max-width:760px;margin-left:auto;margin-right:auto}
  /* Titre de la section d'action (rappel de l'étape). */
  .hero-action-head{max-width:760px;margin:0 auto 18px;text-align:center}
  .hero-action-head h2{font-family:var(--font-display);font-weight:700;font-size:28px;
    line-height:1.1;letter-spacing:-.02em;color:var(--ink);margin:0 0 6px}
  .hero-action-head p{font-size:16px;line-height:1.5;color:var(--ink2);margin:0}

  @media(min-width:901px){
    /* LA GRILLE : colonne gauche fluide + zone droite EXACTEMENT --hero-zone-w.
       `minmax(0,1fr)` et non `1fr` : sans le min 0, un enfant intrinsèquement large
       (un <input>, un transcript) élargirait la piste et ferait déborder la page.
       ⚠️ SPÉCIFICITÉ 0,2,0 (`.hero-da.hero-sys`, `.hero-grid.hero-sys`) ET NON
       `.hero-sys` NU : les <style> de page se chargent APRÈS shared.css et gagnent
       les égalités. Plusieurs pages posent `.hero-da{padding…}` (0,1,0) : un
       `.hero-sys` nu (0,1,0) leur céderait par l'ordre de source. On borne donc au
       double-sélecteur, imbattable par un `.hero-da` de page. */
    .hero-da.hero-sys[data-hero-dispo]{
      display:grid;
      grid-template-columns:minmax(0,1fr) var(--hero-zone-w);
      /* RANGÉE À HAUTEUR FIXE = hauteur de zone. C'EST CE QUI TIENT « même Y de
         titre » : sans track de rangée figé, la hauteur de rangée = max(contenu
         gauche, zone). En dispo A la gauche (champ compris) DÉPASSE la zone, la
         rangée grandit, et `align-content:center` la remonte → le titre montait de
         ~110px vs dispo B (mesuré). En figeant la rangée à --hero-zone-h, le bloc
         centré est IDENTIQUE partout : seul l'eyebrow (hauteurs propres à chaque
         page) introduit un delta résiduel de l'ordre d'une ligne. */
      /* RANGÉE AU MOINS ÉGALE À LA ZONE (468), MAIS QUI GRANDIT pour épouser la
         colonne la plus haute. En dispo A la gauche porte champ + CTA + note de
         confidentialité + lien « First time? » : elle DÉPASSE la zone (~640px). Le
         `minmax(zone,auto)` fait grandir la rangée — donc le hero — jusqu'à la
         contenir. C'est ce qui empêche tout chevauchement : la rangée de badges est
         un FRÈRE du hero, elle suit donc TOUJOURS le bas réel du hero. */
      grid-template-rows:minmax(var(--hero-zone-h),auto);
      gap:clamp(40px,4.4vw,72px);
      align-items:start;
      align-content:start;
      /* ⚠️ PAS DE min-height PLEIN-ÉCRAN, ET C'EST LE CŒUR DU CORRECTIF (lot
         coordination). Un plancher figé (calc(100vh - nav - 132)) plus COURT que le
         contenu dense de dispo A à hauteur de viewport réaliste (820–900) laissait la
         colonne gauche DÉBORDER sous la boîte hero et CHEVAUCHER les badges
         (« No install · No account… ») : ça se lisait cassé.
         DOCTRINE v3 RÉAFFIRMÉE — « la respiration prime » : les badges SUIVENT le
         contenu à ~52px (34 de padding-bas + 22 de marge `.trustbadges`), et leur Y
         ABSOLU a le droit de varier d'une page à l'autre — dispo B (colonne courte)
         → badges plus haut ; dispo A dense (handoff) → badges plus bas. Ce qui reste
         VERROUILLÉ page à page, ce sont les deux ancres du système : le Y DU TITRE
         (haut de rangée, padding-haut fixe) et la ZONE (position + 560×468). Le hero
         épouse sa colonne la plus haute, jamais un plancher qui pourrait être plus
         court que le contenu — donc AUCUN chevauchement possible, quelle que soit la
         hauteur de viewport. */
      /* FIL ROUGE badges-visibles (lot 4-pages) : padding-haut resserré (5vh→3.4vh,
         plafond 72→48) pour remonter toute la colonne gauche et ramener la rangée de
         badges DANS le viewport à 1512×820. Padding-bas 34→30 (breathing badges ~52
         conservé : 30 + 22 de marge). Home n'est PAS touchée (elle a son propre
         padding dans le gabarit). */
      padding:clamp(22px,3vh,44px) 0 30px;
    }
    /* ══ FIL ROUGE — REMPLISSAGE PAR LE BAS, RESPONSIVE (lot LOT 2 · 23/07) ═════════
       GRIEF Kevin (Neon/Sofi) : le hero ne REMPLISSAIT que sur ~une taille. Les
       planchers ÉTAIENT FIGÉS EN PIXELS (628px ici, 624 côté home) → calés pour du
       ~1440/1512. Sur 1920 et surtout 2560+, ce plancher est BIEN plus court que le
       viewport : grand vide sous la carte, rangée de confiance qui flotte très haut
       (mesuré avant ce lot : trust-line à 616–647px DU BAS à 2560), la section
       suivante pleinement visible. Un nombre magique en px ne tient QUE sur l'écran où
       on l'a réglé — la ligne de flottaison dépend de la fenêtre du visiteur.
       LE CORRECTIF : le plancher devient VIEWPORT-RELATIF. Il vaut, à chaque taille,
       « la hauteur qui pose le BAS de la rangée de confiance à ~55px du bas du
       viewport » — donc le hero grandit avec l'écran et la trust-line tombe bas
       PARTOUT (1440 → 2560), pas seulement à la taille de calibrage.
       LE CALCUL, refaisable (mesuré au pixel, cf. rapport) :
         · haut du hero  ≈ --nav-h (la barre, en flux, 84–86px) ;
         · sous le bas de la boîte hero vivent la rangée + sa note = ~81px (22 de
           marge `.hero-da + .trustbadges` + pastilles + note) ;
         · respiration visée SOUS la note : ~55px (distance honnête au bas, pas collé).
         → hauteur hero cible = 100svh − nav − 81 − 55 ≈ 100svh − nav − 134.
       `svh` (et non `vh`) : la petite hauteur de viewport, stable même là où une barre
       d'UI mobile se rétracte — ici la règle est ≥901px (desktop), donc svh≈vh, mais
       c'est la valeur honnête. Aucun rapport avec la largeur : svh ne touche pas
       `scrollWidth`, `scrollWidth === clientWidth` est préservé.
       ⚠️ C'EST UN PLANCHER (`min-height`), PAS UNE HAUTEUR FIGÉE — et c'est ce qui
       tient la DOCTRINE v3 « la respiration prime » : quand le contenu de gauche est
       PLUS HAUT que ce plancher (dispo A dense sur un viewport court — handoff à
       1280×720 : champ + CTA + repli + confidentialité + « First time? »), c'est LE
       CONTENU qui gagne, la boîte grandit à sa hauteur, la rangée le suit à +52px :
       AUCUN chevauchement possible, quelle que soit la hauteur de fenêtre. Le
       `max(628px, …)` garde le plancher validé comme borne basse pour les fenêtres
       très courtes où le calc passerait sous 628. Si une taille ne peut PAS remplir
       sans écraser le contenu, on préfère la respiration au remplissage (consigne).
       ⚠️ ÉTENDU À /brief/ : il partait sans plancher (hauteur naturelle) et flottait
       donc lui aussi sur grand écran (trust-line à 616px du bas à 2560). Il rejoint le
       mécanisme commun. /merge/ et /migration/ ne sont PAS pilotés ici (hors des 4
       pages du lot, hauteur naturelle conservée) — la règle reste scopée par page. */
    /* ⚠️ MOTIF A1 (23/07, Kevin) — /migration/ REJOINT le plancher plein-écran.
       Il en était exclu → son hero restait à hauteur de contenu et sa trust-line
       flottait 300–800px trop haut, non uniforme (mesuré). En l'ajoutant ici, son
       hero se remplit comme handoff/extract/brief et sa trust-line tombe au MÊME
       niveau. (Migration porte data-intent="migration".) */
    body[data-page="extract"] .hero-da.hero-sys[data-hero-dispo],
    body[data-page="brief"] .hero-da.hero-sys[data-hero-dispo],
    body[data-intent="migration"] .hero-da.hero-sys[data-hero-dispo],
    /* /merge/ REJOINT le lot (24/07, Kevin) : son hero (mg-hero.hero-da.hero-sys)
       restait à hauteur de contenu (~585–604px fixe) → la trust-line remontait de
       270px (1920) à 670px (2560) au-dessus du niveau des autres pages. Le sélecteur
       .hero-da.hero-sys[data-hero-dispo] matche mg-hero → il se remplit désormais
       comme migration (dispo B) et la trust-line tombe au MÊME niveau. */
    body[data-intent="merge"] .hero-da.hero-sys[data-hero-dispo],
    body[data-intent="home"] .hero-da.hero-sys[data-hero-dispo]{
      min-height:max(628px, calc(100svh - var(--nav-h) - 134px))}
    /* ── SÉPARATION HERO ↔ SECTION SUIVANTE (même lot) ─────────────────────────────
       Le plancher ci-dessus pose le BAS de la note de confiance à ~55px du bas du
       viewport : la trust-line respire, mais la section suivante — `#how`/`#not-a-
       summarizer` (`.alt-bg`, fond teinté + filet haut) — commence pile sous la note
       (elles sont collées dans le flux) et sa bande teintée AFFLEURAIT donc dans ces
       55px. Consigne Kevin : « la section suivante N'EST PAS visible ». On glisse donc
       un blanc SOUS la note, égal à la respiration : la trust-line garde ses ~55px
       d'air, mais ce sont désormais 55px de BLANC, et le haut de la section suivante
       retombe au ras (ou juste sous) la ligne de flottaison — plus de lisière teintée.
       Ce blanc vit après la ligne de flottaison sur grand écran : au défilement, il se
       lit comme la respiration normale avant la section. /pro/ /everyday/
       restent hors du lot ; /merge/ REJOINT le lot (24/07, viewport) — même blanc
       sous la note pour que #mg-action ne perce pas au-dessus de la flottaison.
       /migration/ REJOINT le lot (MOTIF A1, 23/07) : puisque son
       hero se remplit désormais comme les autres, il lui faut le même blanc sous la
       note pour que la section suivante ne perce pas au-dessus de la ligne de
       flottaison. Home (gabarit propre) reste piloté par son <style>. */
    body[data-page="extract"] .trustbadges-note,
    body[data-page="brief"] .trustbadges-note,
    body[data-intent="migration"] .trustbadges-note,
    body[data-intent="merge"] .trustbadges-note,
    body[data-intent="home"] .trustbadges-note{margin-bottom:60px}
    /* ⚠️ La HOME (`.hero-grid.hero-sys`) N'EST PAS pilotée ici : son point de rupture
       2 col→1 col est 980px et non 900px, et son <style> de gabarit porte déjà cette
       logique. La conduire par ce bloc (min-width:901) lui imposerait 2 colonnes
       entre 901 et 980px, là où elle veut déjà s'empiler. La home reçoit donc la
       MÊME grille/zone dans templates/home.html, à SON palier. Les règles de ZONE et
       d'instrument ci-dessous (génériques `.hero-sys`) restent partagées avec elle. */
    /* COLONNE GAUCHE — contenu ANCRÉ EN HAUT : le titre commence au même Y sur
       toutes les pages (dispo A comme B), parce que la ligne de grille fait la
       hauteur de la zone fixe et que la parole s'y aligne en haut. C'est ce qui
       tient « mêmes positions verticales » quel que soit le contenu de gauche. */
    .hero-sys.hero-da[data-hero-dispo] > .hero-left{
      max-width:none;min-width:0;align-self:stretch;
      display:flex;flex-direction:column;justify-content:flex-start}
    /* LA ZONE FIXE — même footprint dans les deux dispositions, centrée dans la
       ligne (si la gauche est plus haute, la zone reste centrée en regard). */
    /* align-self:START — la zone se cale en HAUT de la rangée. En dispo A la rangée
       grandit sous la colonne gauche : `center` ferait alors DESCENDRE la zone et
       casserait son Y verrouillé. `start` maintient le haut de la zone au même Y que
       le titre, sur les 6 pages (position + 560×468 identiques). */
    .hero-sys[data-hero-dispo] > .hero-zone{
      width:100%;height:var(--hero-zone-h);align-self:start}

    /* ── TYPOGRAPHIE UNIFIÉE — UN SEUL GABARIT (lot harmonisation) ──────────────
       Cible fondateur : H1 = 60px, H2 = 25px sur les 6 pages. `clamp` plafonné à
       3.75rem (=60px) : atteint 60px dès ~1364px de large (donc 60px pile à 1512 ET
       1920) et redescend en dessous pour ne pas tasser la colonne gauche étroite.
       ⚠️ SPÉCIFICITÉ 0,4,1 (`.hero-da.hero-sys[data-hero-dispo] h1.display`) : il
       FAUT battre /brief/ qui impose son propre `.hero h1.h1-duo.hb-h1` (0,3,2,
       36px). Un `.hero-sys h1.display` (0,2,1) lui aurait cédé. La home n'est pas
       `.hero-da` : son H1 est réglé au même 60px dans templates/home.html. */
    .hero-da.hero-sys[data-hero-dispo] h1.display{
      font-size:clamp(2.5rem,4.4vw,3.75rem);line-height:1.02;letter-spacing:-.035em;
      /* 16ch→22ch : 16ch (~480px) forçait un retour de trop (extract passait à 3
         lignes, handoff à 3). À 22ch (~660px, tient dans la colonne à 1512/1920) le
         titre respire sur MOINS de lignes → colonne plus courte → badges plus haut. */
      max-width:22ch;margin-bottom:14px}
    .hero-da.hero-sys[data-hero-dispo] .sub{
      font-size:25px;line-height:1.3;font-weight:600;max-width:40ch;margin-bottom:12px}
    /* FIL ROUGE (lot 4-pages) — RYTHME VERTICAL RESSERRÉ de la colonne gauche des
       pages outils (dispo A), pour faire remonter la rangée de badges dans le
       viewport à 1512×820 sans toucher au gabarit (H1 60 / H2 25 / eyebrow HB). */
    .hero-da.hero-sys[data-hero-dispo] .eyebrow{margin-bottom:8px}
    .hero-da.hero-sys .hero-left .inputwrap{margin-top:2px}
    .hero-da.hero-sys .hero-left .inputcard{gap:8px}
    .hero-da.hero-sys .hero-left .privacy-line{margin-top:10px;line-height:1.4}
    .hero-da.hero-sys .hero-left .trust{margin-top:8px;padding-top:8px}
    .hero-sys .eyebrow,
    .hero-sys .hero-eyebrow,
    .hero-sys .mg-eyebrow{margin-bottom:20px}
    /* EYEBROW = celui de /brief/ (18px, pastille COURTE dimensionnée au contenu) :
       c'est LA référence. Les autres pages portaient l'eyebrow en enfant direct de
       la colonne flex → il s'étirait sur toute la laize (« pastille trop longue »).
       `align-self:flex-start` le ramène à la largeur du texte, comme /brief/. */
    .hero-sys .eyebrow,
    .hero-sys .mg-eyebrow{align-self:flex-start}
    /* /merge/ portait un eyebrow PLAT (13px) : on lui donne la MÊME pastille 18px
       que `.eyebrow`, couleur accent propre conservée (contraste --accent-active). */
    .hero-sys .mg-eyebrow{font-size:18px;font-weight:700;letter-spacing:-.01em;
      display:inline-flex;align-items:center;gap:9px;
      background:var(--surface);border:1px solid var(--border);border-radius:999px;
      padding:9px 20px;box-shadow:0 2px 8px rgba(23,23,22,.06)}
    /* Champ à gauche (dispo A) : largeur de saisie confortable, bornée. */
    .hero-sys .hero-left .inputwrap{max-width:520px}

    /* ── DISPOSITION A — l'animation/démo remplit la zone (pas de vide) ────────
       #instrument-mount (handoff/extract/home) et le doc /brief/ : la carte
       s'étire à la hauteur fixe de la zone, corps en flex-grow. Pas de carte
       rapetissée, pas de trou. */
    .hero-sys .da-stage.hero-zone #instrument-mount,
    .hero-sys .da-stage.hero-zone .instrument{height:100%}
    .hero-sys .da-stage.hero-zone .instrument{display:flex;flex-direction:column}
    .hero-sys .da-stage.hero-zone .inst-body{flex:1 1 auto;display:flex;flex-direction:column}
    /* Le corps de la démo remplit la hauteur fixe de la zone : la boîte de
       transcript (`.src`) s'ÉTIRE (au lieu d'une hauteur figée 238px) et les lignes
       ajoutées dans instrument.js la remplissent → plus de vide sous la conversation.
       La carte NE change ni de taille ni de position (zone 560×468), on ne remplit
       que son intérieur (consigne fondateur « fill the internal void, not the frame »). */
    .hero-sys .da-stage.hero-zone .scan-area{flex:1 1 auto;display:flex;flex-direction:column;min-height:0}
    .hero-sys .da-stage.hero-zone .src{height:auto;flex:1 1 auto;min-height:0}
    .hero-sys .hb-herovisual.hero-zone .hb-doc{height:100%;display:flex;flex-direction:column}
    .hero-sys .hb-herovisual.hero-zone .hb-doc-body{flex:1 1 auto}

    /* ── DISPOSITION B — la saisie lourde vit dans la zone, centrée pour remplir ──
       merge (.mg-card) et migration (.inputwrap) : la carte de saisie prend toute
       la hauteur de la zone et centre son contenu, si bien que /migration/ atteint
       le même remplissage que les autres — sa saisie n'est plus bornée par la
       hauteur naturelle du formulaire. */
    .hero-sys[data-hero-dispo="b"] .hero-zone > .mg-card,
    .hero-sys[data-hero-dispo="b"] .hero-form.hero-zone > .inputwrap{
      height:100%;display:flex;flex-direction:column;justify-content:center}
    /* la piste de saisie (rows, chips) ne doit pas s'étirer verticalement : on
       centre le BLOC, on ne dilate pas ses composants. */
    .hero-sys[data-hero-dispo="b"] .hero-zone > .mg-card > *{flex:none}
  }

  /* ══ v3 (lot correctif Kevin 22/07) — LE VERROU DE 900px EST RETIRÉ ═════════════
     RAPPEL DE LA v2 (verrou) : pour aligner la rangée de badges au pixel sur les 6
     pages, TOUS les `.hero-da` étaient forcés à `min-height:900px` (calé sur le
     hero handoff B4). Le contenu restait centré (`align-items:center`) dans cette
     boîte de 900px, et la rangée de badges — FRÈRE du hero, donc HORS de la boîte —
     tombait au plancher des 900px.
     LE DÉFAUT MESURÉ (1512, avant ce lot) : l'alignement était parfait (badges à
     y=1006 partout) MAIS le contenu réel s'arrêtait bien plus haut selon la page —
     handoff 949 (écart sain 57px) mais brief 812 (194px de vide), merge 698 (308px),
     migration 678 (328px). Sur les pages à contenu court, la rangée flottait donc
     très loin sous le hero, SCOTCHÉE AU PLANCHER DE LA SECTION, un grand trou blanc
     entre le contenu et les badges. C'est le grief Kevin n°1 de ce lot : « distance
     honnête SOUS le hero, pas collée au bas de la section ».
     ⚠️ TENSION IRRÉDUCTIBLE, SIGNALÉE AU RAPPORT (arbitrage coordinateur : la
     RESPIRATION PRIME, l'alignement approximatif suffit). Les badges sont un FRÈRE
     du hero : leur Y = bas-de-boîte-hero + marge. Pour qu'ils COLLENT au contenu
     (respiration nette), il faut que la boîte hero = hauteur du contenu, donc PAS de
     min-height qui la gonfle. Or c'est exactement ce min-height qui garantissait le
     même Y partout. Les deux objectifs sont géométriquement incompatibles tant que
     la rangée reste hors de la boîte. On tranche pour la respiration : le verrou
     saute, chaque hero reprend sa hauteur naturelle, la rangée suit le contenu avec
     une marge nette (voir `.trustbadges` plus bas). L'ALIGNEMENT DEVIENT APPROXIMATIF
     (les heros n'ont pas tous la même hauteur) — c'est le prix assumé et demandé.
     ⚠️ CE N'EST PAS UNE RÉDUCTION DU HERO (contrainte Kevin « ne rapetisse pas le
     B4 ») : on ne touche NI au clamp du titre, NI aux espacements, NI au contenu —
     on retire seulement du VIDE mort sous le contenu. Le hero B4 handoff garde sa
     taille et ses proportions au pixel. */

  /* ══ RESPIRATION UNIFIÉE HERO → BADGES (lot 22/07) ═════════════════════════════
     L'alignement au pixel étant abandonné (voir ci-dessus), ce qui doit rester
     COHÉRENT d'une page à l'autre, c'est le GESTE : la même marge nette entre le bas
     du contenu du hero et la rangée de badges partout. « respiration franche, même
     traitement sur les 6 pages » (Kevin).
     Le padding-bas des `.hero-da` divergeait (base 60px, handoff 34px, etc.) → des
     écarts contenu→badges de 56 à 82px selon la page. On l'uniformise à 30px sur
     desktop : additionné aux 22px de marge de `.trustbadges` (l.~3550), l'écart vaut
     ~52px sur TOUTES les pages — une respiration franche et identique. Le hero
     handoff B4 (`body[data-intent="home"] .hero-da`, 0,2,1) porte déjà 34px par sa
     propre règle et n'est pas rabaissé (4px près, imperceptible, et on ne touche pas
     au bloc B4 validé). Le padding-HAUT n'est pas touché (breathing du hero
     préservé). L'accueil (`.hero-hub`) reçoit le même 30px côté template. */
  @media(min-width:901px){
    .hero-da{padding-bottom:30px}
  }

  .hero-da .hero-left{text-align:left;min-width:0}
  .hero-da .hero-left .eyebrow{margin-left:0;margin-right:0}
  .hero-da .hero-left h1{margin-left:0;text-align:left}
  .hero-da .hero-left .sub{text-align:left}
  .hero-da .hero-left .inputwrap{margin:0;max-width:460px}
  .hero-da .hero-left .privacy-line{text-align:left;justify-content:flex-start;margin-left:0}
  .hero-da .hero-left .trust{text-align:left}
  .da-stage{position:relative;min-width:0}
  #instrument-mount{position:relative}
  /* dès que le vrai uncooking démarre (app.js : .flow-started) : on retire
     l'instrument déco et le hero repasse en 1 colonne — plus de double « Uncooking ». */
  /* Uncooking en cours (.flow-started) — MASQUAGE DE LA SCÈNE : MOBILE UNIQUEMENT
     (correction fondateur 14/07). Sous 900px le hero est empilé : la scène animée
     tombe SOUS le formulaire → deux « Uncooking… » à l'écran = confusion. On la
     masque donc là, et LÀ SEULEMENT. Sur DESKTOP (>900px, 2 colonnes) l'animation
     RESTE VISIBLE et continue de tourner pendant le vrai uncooking — aucune
     confusion possible côte à côte, c'est la signature de la page. */
  @media(max-width:900px){
    .hero-da.flow-started{grid-template-columns:1fr}
    .hero-da.flow-started .da-stage{display:none}
  }

  /* ---- l'instrument (scopé) ---- */
  .da-stage .instrument{position:relative;background:var(--surface);border:1px solid var(--border);border-radius:var(--r-xl);box-shadow:var(--sh-cardh);padding:18px 18px 20px}
  .da-stage .inst-head{display:flex;align-items:center;gap:10px;padding-bottom:12px;border-bottom:1px solid var(--divider)}
  .da-stage .probe{position:relative;width:12px;height:12px;flex:none}
  .da-stage .probe i{position:absolute;inset:0;border-radius:50%;background:var(--accent)}
  .da-stage .probe i.ping{animation:daPing 1.5s ease-out infinite}
  @keyframes daPing{0%{transform:scale(1);opacity:.55}70%,100%{transform:scale(2.6);opacity:0}}
  .da-stage .inst-title{font:600 13.5px var(--font-sans);color:var(--ink)}
  .da-stage .inst-status{margin-left:auto;font:600 11px var(--font-sans);letter-spacing:.04em;text-transform:uppercase;color:var(--accent-active);background:var(--accent-soft);border-radius:999px;padding:4px 10px}
  .da-stage .inst-body{position:relative;margin-top:14px;min-height:296px}
  .da-stage .scan-area{position:relative}
  .da-stage .src{border:1px solid var(--divider);border-radius:var(--r-md);background:var(--bg2);padding:10px 12px;height:238px;overflow:hidden;position:relative;transition:filter .4s,opacity .4s,border-color .3s}
  .da-stage .src.scanning{border-color:var(--accent-shadow)}
  .da-stage .src::after{content:"";position:absolute;left:0;right:0;bottom:0;height:26px;background:linear-gradient(transparent,var(--bg2));pointer-events:none;z-index:2}
  .da-stage .src .line{font:12.5px/1.5 var(--font-sans);color:var(--ink2);margin:0 0 6px}
  .da-stage .src .who{font-weight:700;color:var(--ink)}
  .da-stage .src .who.ai{color:var(--accent-active)}
  .da-stage .src .scanline{position:absolute;left:0;right:0;top:-48px;height:48px;z-index:3;pointer-events:none;opacity:0;background:linear-gradient(to bottom,transparent,rgba(255,45,85,.16) 60%,transparent)}
  .da-stage .src .scanline::after{content:"";position:absolute;left:6px;right:6px;bottom:8px;height:2px;border-radius:2px;background:var(--accent);box-shadow:0 0 10px 1px var(--accent-shadow)}
  .da-stage .src.scanning .scanline{opacity:1;animation:daSweep 1.35s cubic-bezier(.5,0,.5,1) infinite}
  @keyframes daSweep{0%{top:-48px}100%{top:238px}}
  .da-stage .obs{list-style:none;position:absolute;inset:0;margin:0;padding:11px 13px;display:flex;flex-direction:column;justify-content:center;gap:10px;background:rgba(255,253,252,.84);backdrop-filter:blur(2px);border:1px solid var(--border);border-radius:var(--r-md);box-shadow:var(--sh-card);z-index:4;opacity:0;transform:translateY(6px);transition:opacity .4s,transform .4s;pointer-events:none}
  .da-stage .obs li{display:flex;align-items:center;gap:9px;font:12.5px/1.3 var(--font-sans);color:var(--ink);opacity:0;transition:opacity .25s}
  .da-stage .obs li.show{opacity:1}
  .da-stage .obs .mk{flex:none;width:19px;height:19px;border-radius:50%;background:var(--accent-soft);color:var(--accent-active);display:grid;place-items:center;font-size:11px;font-weight:700}
  .da-stage .obs .n{margin-left:auto;font:700 12px var(--font-mono);color:var(--accent-active)}
  .da-stage .obs li.typing .txt::after{content:"";display:inline-block;width:6px;height:13px;background:var(--accent);vertical-align:-2px;margin-left:1px;animation:daBlink 1s step-end infinite}
  @keyframes daBlink{50%{opacity:0}}
  .da-stage .instrument.uncooking .src{filter:blur(1.5px) saturate(.55);opacity:.4}
  .da-stage .instrument.uncooking .obs{opacity:1;transform:none;border-color:var(--accent-shadow);animation:daHeat 1.7s ease-in-out infinite}
  @keyframes daHeat{0%,100%{box-shadow:var(--sh-card),inset 0 0 34px rgba(255,45,85,.06)}50%{box-shadow:0 10px 30px var(--accent-shadow),inset 0 0 54px rgba(255,45,85,.18)}}
  .da-stage .instrument.uncooking .probe{width:17px;height:17px}
  .da-stage .instrument.uncooking .probe i:first-child{box-shadow:0 0 16px 5px var(--accent-shadow)}
  .da-stage .instrument.uncooking .probe i.ping{animation:daPing .95s ease-out infinite}
  .da-stage .instrument.uncooking .inst-title{color:var(--accent-active)}
  /* jauge vert→rouge */
  .da-stage .doneness{position:absolute;left:12px;right:12px;bottom:12px;z-index:5;background:rgba(255,253,252,.94);border:1px solid var(--border);border-radius:var(--r-md);padding:9px 12px;box-shadow:var(--sh-card);opacity:0;transform:translateY(8px);transition:opacity .45s,transform .45s}
  .da-stage .doneness.show{opacity:1;transform:none}
  .da-stage .dn-row{display:flex;justify-content:space-between;align-items:baseline;margin-bottom:6px}
  .da-stage .dn-cap{font:600 10.5px var(--font-sans);letter-spacing:.09em;text-transform:uppercase;color:var(--ink3)}
  .da-stage .dn-verdict{font:700 12.5px var(--font-display);color:var(--accent-active);letter-spacing:.02em;text-transform:uppercase}
  .da-stage .dn-track{position:relative;height:8px;border-radius:999px;background:linear-gradient(90deg,#E7F6EE,#FFF3D9 45%,#FFD9D9)}
  .da-stage .dn-fill{position:absolute;left:0;top:0;bottom:0;width:0;border-radius:999px;background:linear-gradient(90deg,var(--success) 0%,#E8A93B 52%,var(--accent) 100%);transition:width 1.1s ease .1s}
  .da-stage .doneness.show .dn-fill{width:88%}
  .da-stage .dn-cursor{position:absolute;top:50%;left:88%;width:13px;height:13px;border-radius:50%;background:var(--accent);border:2px solid #fff;box-shadow:var(--sh-sm);transform:translate(-50%,-50%) scale(0);transition:transform .3s ease .55s}
  .da-stage .doneness.show .dn-cursor{transform:translate(-50%,-50%) scale(1)}
  /* handoff overlay (di-handoff pour éviter la collision avec l'écran résultat) */
  .da-stage .di-handoff{position:absolute;inset:0;z-index:6;display:flex;flex-direction:column;border:1px solid var(--border);border-radius:var(--r-lg);background:var(--surface);box-shadow:var(--sh-sm);overflow:hidden;opacity:0;transform:translateY(18px) scale(.97);pointer-events:none;transition:opacity .5s ease,transform .5s ease,box-shadow .5s ease,border-color .5s ease}
  .da-stage .hd-top{display:flex;align-items:center;gap:8px;padding:11px 13px;border-bottom:1px solid var(--divider);flex:none}
  .da-stage .hd-top .spark{width:16px;height:16px;color:var(--accent)}
  .da-stage .hd-top .t{font:600 13px var(--font-sans);color:var(--ink)}
  .da-stage .di-flag{margin-left:auto;display:inline-flex;align-items:center;gap:5px;font:600 11.5px var(--font-sans);color:#fff;background:var(--success);border-radius:999px;padding:4px 10px}
  .da-stage .di-flag svg{width:12px;height:12px}
  .da-stage .hd-body{flex:1;padding:12px 14px;font:12.5px/1.6 var(--font-mono);color:var(--ink);white-space:pre-wrap;overflow:auto}
  .da-stage .hd-body b{color:var(--accent-active);font-weight:700}
  .da-stage .hd-body b.ho-h{display:block;color:var(--ink);font-family:var(--font-sans);font-size:11.5px;letter-spacing:.02em;margin:8px 0 2px}
  .da-stage .hd-foot{display:flex;gap:10px;align-items:center;padding:0 14px 14px;flex:none}
  .da-stage .di-copy{display:inline-flex;align-items:center;gap:8px;height:42px;padding:0 16px;border:none;border-radius:var(--r-md);background:var(--accent);color:#fff;font:600 13.5px var(--font-sans);cursor:default;transition:.2s}
  .da-stage .di-copy svg{width:15px;height:15px}
  .da-stage .hint{font:12px var(--font-sans);color:var(--ink3)}
  @keyframes daCopyPulse{0%{box-shadow:0 0 0 0 var(--accent-ring)}70%,100%{box-shadow:0 0 0 11px rgba(255,45,85,0)}}
  /* état READY : handoff par-dessus (wow) + carte de fond qui grise → détachement */
  .da-stage .instrument.ready{background:var(--bg2)}
  .da-stage .instrument.ready .inst-status{color:#fff;background:var(--success)}
  .da-stage .instrument.ready .probe i.ping{animation:none;opacity:0}
  .da-stage .instrument.ready .di-handoff{opacity:1;transform:translateY(-5px) scale(1.02);pointer-events:auto;box-shadow:0 30px 70px rgba(23,23,22,.18);border-color:var(--success);animation:daPop .62s cubic-bezier(.2,.85,.3,1.25) 1,daFlash 1.1s ease-out 1}
  @keyframes daPop{0%{opacity:0;transform:translateY(24px) scale(.95)}62%{opacity:1;transform:translateY(-10px) scale(1.03)}100%{transform:translateY(-5px) scale(1.02)}}
  @keyframes daFlash{0%{box-shadow:0 0 0 3px rgba(22,132,91,.5),0 30px 70px rgba(23,23,22,.18)}100%{box-shadow:0 0 0 18px rgba(22,132,91,0),0 30px 70px rgba(23,23,22,.18)}}
  .da-stage .instrument.ready .di-copy{animation:daCopyPulse 1.8s ease-out infinite}

  @media(max-width:900px){
    .hero-da{grid-template-columns:1fr;gap:30px;padding:34px 0 40px;text-align:center}
    .hero-da .hero-left{text-align:center;display:flex;flex-direction:column;align-items:center}
    /* ⚠️ `.hero-left` est `display:flex;flex-direction:column;align-items:center` :
       ses enfants se dimensionnent donc sur leur MAX-CONTENT, pas sur la largeur du
       conteneur. `width:100%` avait été posé sur `.inputwrap` (règle suivante) et
       OUBLIÉ sur `h1` et `.sub` — qui débordaient donc dès que leur texte dépassait
       la colonne : à 320px sur /fr/everyday/, `.sub` mesurait 369px pour 284px de
       conteneur et se retrouvait rogné DES DEUX CÔTÉS (L-24,5 / R+24,5), `h1` à
       +26,4px. Cas long en FR/ES ; le texte EN, plus court, ne le déclenchait pas.
       `width:100%` les ramène à la largeur du conteneur — `text-align:center`
       continue de centrer le texte, le rendu voulu est inchangé. */
    .hero-da .hero-left h1,.hero-da .hero-left .sub{text-align:center;width:100%}
    .hero-da .hero-left .inputwrap{margin:0 auto;max-width:560px;width:100%}
    .hero-da .hero-left .privacy-line{justify-content:center}
    .da-stage{max-width:520px;margin:0 auto;width:100%}
  }
  /* ══ MOUVEMENT RÉDUIT — LA LISTE ÉTAIT INCOMPLÈTE, ET ELLE L'ÉTAIT SUR L'ÉTAT
     QUE CES UTILISATEURS ATTEIGNENT EN PREMIER (V2.7) ═══════════════════════════
     L'ancienne liste ne coupait que les boucles INFINIES (pulsation, scanner,
     curseur de frappe, pouls du bouton). Le raisonnement paraît bon — ce qui tourne
     sans fin est ce qui gêne — mais il rate le cas réel :
       instrument.js, sous `prefers-reduced-motion`, sort de `run()` en
       `if (reduce) { renderFinal(); return; }`. Or `renderFinal()` POSE `.ready`
       IMMÉDIATEMENT. Et `.instrument.ready .di-handoff` déclenche `daPop` — un
       rebond translateY + scale — plus `daFlash`. Toutes deux sont en `1`
       itération, donc absentes de la liste des infinies.
     RÉSULTAT : l'utilisateur qui demande MOINS de mouvement était le seul à voir le
     rebond, et à le voir d'entrée, sans même la séquence qui le prépare. On coupe
     donc AUSSI les animations à une seule itération, et les TRANSITIONS qui
     déplacent (le panneau de reprise, la jauge de cuisson dont `.dn-fill` file de
     0 à 88 % en 1,1 s, le curseur qui éclôt). L'état final reste PEINT — rien n'est
     masqué, tout est immobile. C'est la règle : « reduce » veut dire sans
     mouvement, pas sans information. */
  @media(prefers-reduced-motion:reduce){
    .page-bg span,.da-stage .probe i.ping,.da-stage .src .scanline,.da-stage .obs li.typing .txt::after,.da-stage .instrument.ready .di-copy,.da-stage .instrument.ready .di-handoff,.da-stage .instrument.uncooking .obs{animation:none!important}
    .da-stage .di-handoff,.da-stage .instrument.ready .di-handoff{transition:none!important;transform:none!important}
    .da-stage .doneness,.da-stage .dn-fill,.da-stage .dn-cursor,.da-stage .obs,.da-stage .obs li,.da-stage .src{transition:none!important}
    .da-stage .doneness.show .dn-cursor{transform:translate(-50%,-50%) scale(1)!important}
  }

  /* carte 640px = trop juste pour input+bouton en ligne (le texte FR
     « Collez un lien de partage ChatGPT ou Claude » + « Relancer ma
     conversation → » y coupait le placeholder — même traitement ici pour
     la parité de structure FR/EN demandée) — empilé, même largeur de carte. */
  .inputcard{display:flex;flex-direction:column;gap:10px;background:transparent;border:none;padding:0;box-shadow:none}
  .inputcard input{width:100%;height:56px;padding:0 18px;font:16px/1 var(--font-sans);color:var(--ink);text-align:left;border:1.5px solid var(--border);background:var(--surface);border-radius:var(--r-lg);box-shadow:var(--sh-sm);outline:none}
  .inputcard input:focus{border-color:var(--accent);box-shadow:0 0 0 4px var(--accent-ring)}
  .inputcard input::placeholder{color:var(--ink3)}
  .inputcard .btn-lg{width:100%}
  /* ── LOT 5 (③, Kevin 23/07 « le CTA est trop court sur /handoff/ et /extract/ »).
     Sur ces deux pages, #cta a QUITTÉ .inputcard (LOT 3 : pour poser le lien de
     collage SOUS le champ) et enfant DIRECT de .inputwrap — il a donc perdu la
     pleine largeur que `.inputcard .btn-lg` donnait, et s'était réduit à son
     contenu. On la rétablit : le CTA reprend la largeur de la colonne de saisie
     (.inputwrap plafonné à 460px sur .hero-da → largeur du champ #link, référence
     de /brief/ #hb-go). Le `>` restreint aux CTA sortis dans .inputwrap : le
     collage-link n'est pas un bouton, migration (#cta dans .mig-step-body) et les
     pages où #cta est resté dans .inputcard ne sont pas touchés. */
  .hero-da .hero-left .inputwrap > #cta{width:100%}
  .tick{display:none!important}
  @keyframes pulse-once{0%{transform:scale(1)}50%{transform:scale(1.03)}100%{transform:scale(1)}}
  .pulse{animation:pulse-once .3s ease}
  .trust{margin:10px 0 0;padding-top:10px;border-top:1px solid var(--divider);font-size:13.5px;color:var(--ink3)}
  .trust + .trust{margin-top:6px}
  /* trust-line unifiée (ex-encadré privacy + ex-coches vertes) — non encadrée,
     une seule ligne desktop */
  .privacy-line{display:flex;align-items:center;justify-content:center;gap:7px;width:fit-content;max-width:100%;margin:16px auto 0;font-size:13px;line-height:1.5;color:var(--ink3);text-align:center}
  .privacy-line .pl-lock{width:13px;height:13px;color:var(--ink3);flex:none}
  .privacy-line b{color:var(--ink);font-weight:600}
  .privacy-line a{color:var(--ink3);font-weight:500;text-decoration:underline;text-underline-offset:2px;white-space:nowrap}
  .privacy-line a:hover{color:var(--ink)}
  /* illustration picto-ligne (direction A) — section démo */
  .illus-ba{display:block;width:min(340px,86vw);height:auto;margin:0 auto 34px;color:var(--ink2)}
  .nb{white-space:nowrap}
  /* `.nb` empêche une fin de phrase de se briser (veuve typographique) — une AISANCE,
     jamais une contrainte de mise en page. Sur mobile elle s'inversait en défaut : la
     phrase liée de `.sub` mesure ~350px, soit plus que les 284px de conteneur à
     320px, et `nowrap` la rendait INCASSABLE — elle sortait donc de l'écran. Tant que
     `.sub` prenait sa largeur max-content, le débordement était porté par `.sub` et
     rogné des deux côtés ; une fois `.sub` ramené à 100% (voir le bloc ≤900px), il
     ressortait sur `.nb` seul, à droite. Les deux symptômes ont la même cause : une
     phrase incassable plus large que l'écran. Sous 560px la commodité cède donc.
     `white-space:normal` AUTORISE la coupure sans la forcer : là où la phrase tient
     déjà (414-560px), le rendu est strictement inchangé. */
  @media(max-width:560px){.nb{white-space:normal}}
  /* 19/07 — mesuré 13,5px/500 et 14,5px/500 : 3,60:1 sur --bg, 3,65:1 sur --bg2.
     Aucune dispense « grand texte » possible à cette taille → seuil 4,5:1, échec.
     Passage à --accent-active (5,67:1). ⚠️ Le lien vit DANS `.trust`, qui est en
     --ink3 (3,23:1, également sous le seuil) : le paragraphe porteur reste donc
     non conforme après ce correctif — traité à part, --ink3 est un token global. */
  .guidelink{color:var(--accent-active);text-decoration:none;font-weight:500}
  .guidelink:hover{text-decoration:underline}
  /* modale guide */
  .modal-backdrop{position:fixed;inset:0;background:rgba(23,23,22,.44);display:flex;align-items:center;justify-content:center;padding:20px;z-index:50}
  .modal-backdrop[hidden]{display:none}
  .modal{position:relative;background:var(--surface);border:1px solid var(--border);border-radius:var(--r-xl);box-shadow:var(--sh-cardh);max-width:880px;width:100%;max-height:90vh;overflow:auto;padding:38px 40px 34px}
  .modal-close{position:absolute;top:16px;right:16px;width:34px;height:34px;border:1px solid var(--border);background:var(--surface);border-radius:var(--r-sm);color:var(--ink2);font-size:16px;cursor:pointer;display:grid;place-items:center}
  .modal-close:hover{background:var(--bg2);color:var(--ink)}
  .modal h2{font-family:var(--font-display);font-weight:700;font-size:26px;letter-spacing:-.03em;margin:0 0 6px}
  .modal-sub{color:var(--ink2);font-size:14.5px;margin:0 0 20px}
  /* modale waitlist extension (chip footer) — compacte, centrée */
  .wl-modal{max-width:440px;text-align:center;padding:34px 30px 28px}
  .wl-modal .modal-sub{margin:0 auto;max-width:36ch}
  .wl-ico{width:52px;height:52px;margin:2px auto 16px;border-radius:50%;background:var(--accent-soft);color:var(--accent-active);display:grid;place-items:center}
  .wl-ico svg{width:26px;height:26px}
  .wl-form{display:flex;flex-direction:column;gap:10px;margin:20px auto 12px;max-width:320px}
  .wl-email{width:100%;height:48px;padding:0 16px;font:15px/1 var(--font-sans);color:var(--ink);border:1.5px solid var(--border);background:var(--surface);border-radius:var(--r-md);outline:none;text-align:center}
  .wl-email:focus{border-color:var(--accent);box-shadow:0 0 0 4px var(--accent-ring)}
  .wl-email::placeholder{color:var(--ink3)}
  .wl-btn{width:100%;height:48px;font-size:15px}
  .wl-note{font-size:12.5px;color:var(--ink3);margin:0 auto;max-width:34ch;line-height:1.5}
  .wl-ok{font-size:15px;font-weight:600;color:var(--success);margin:16px 0 0}
  .guideblock{border:1px solid var(--border);border-radius:var(--r-lg);padding:24px 26px;margin-bottom:22px}
  .guideblock.muted{background:var(--bg2)}
  .gb-head{font-family:var(--font-display);font-weight:600;font-size:19px;letter-spacing:-.02em;margin-bottom:14px;display:flex;align-items:baseline;gap:10px}
  .gb-plat{font-size:11px;letter-spacing:.06em;text-transform:uppercase;color:var(--ink3);font-family:var(--font-sans);font-weight:500}
  .gb-shots{display:grid;grid-template-columns:1fr 1fr;gap:16px;margin:6px 0 20px}
  .gb-shots img{width:100%;aspect-ratio:660/440;border:1px solid var(--border);border-radius:var(--r-md);background:var(--bg2);display:block}
  @media(max-width:640px){.gb-shots{grid-template-columns:1fr}}
  .guideblock ol{margin:0;padding-left:22px}
  .guideblock li{font-size:15px;color:var(--ink);margin:10px 0;line-height:1.6;letter-spacing:-.005em}
  .guideblock li.gb-warn{background:var(--warning-soft);border:1px solid #F2D38A;border-radius:var(--r-sm);padding:12px 14px;list-style:none;margin-left:-22px}
  .gb-note{font-size:13px;color:var(--ink3);margin:10px 0 0}
  .gb-foot{font-size:13px;color:var(--ink3);text-align:center;margin:6px 0 0}
  code{background:var(--bg2);border:1px solid var(--border);border-radius:5px;padding:0 5px;font-family:var(--font-mono);font-size:12px}

  .divlabel{text-align:center;font-size:11px;letter-spacing:.15em;text-transform:uppercase;color:var(--ink3);margin:40px 0 16px;position:relative}
  .divlabel::before,.divlabel::after{content:"";position:absolute;top:50%;width:calc(50% - 92px);height:1px;background:var(--divider)}
  .divlabel::before{left:0}.divlabel::after{right:0}

  /* conv line + chips */
  /* max-width relevé 920→1180 (V1.8, grille 2 colonnes desktop, validé
     _result-2col-mockup.html) : .rescuewrap n'est utilisé QUE par #result
     (grep vérifié) — le relever ici ne touche aucun autre écran. Le cap réel
     de la grille 2 colonnes est porté par .result-grid{max-width:1180px} plus
     bas ; sous 900px .result-grid repasse à max-width:920px, donc le rescuewrap
     à 1180 ne change rien visuellement en mobile (viewport toujours < 1180). */
  /* §2 PARCOURS-REEL (16/07) : le bas de #result COLLAIT le début de « How it
     works » — mesuré, l'écart valait exactement 0px (section#how top = #result
     bottom = 1298). Aucune respiration entre le livrable et la page marketing.
     margin-bottom ici plutôt qu'un padding sur #how : c'est le résultat qui doit
     se détacher de ce qui suit, quelle que soit la section suivante (elle change
     selon la page — /pro, /everyday n'ont pas la même). */
  .rescuewrap{max-width:1180px;margin:0 auto 56px}

  /* ══ SR-26 — L'ÉCRAN RÉSULTAT EN OVERLAY PLEIN ÉCRAN (17/07) ═══════════════════
     Constat (étude SR-07) : le résultat s'affiche DANS la page marketing → sous le
     handoff il y a « How it works », la FAQ, le footer — du contenu écrit pour
     quelqu'un qui NE CONNAÎT PAS le produit, servi à quelqu'un qui VIENT DE
     l'utiliser. C'est une part de « l'écran trop chargé » qui a lancé tout le
     chantier. Et ça résout SR-06 gratuitement : dans un espace dédié on contrôle
     ce qui tient à l'écran ; dans la page marketing on lutte contre tout ce qui suit.
     MÉCANIQUE (la plus sûre) : on ne déplace PAS le DOM (parité HTML ×7 intacte),
     on PROMEUT #result en couche plein écran via une classe body. Tout ce qui vit
     dedans (apply3Zones, le repli, la sticky) continue de marcher — on ne change
     que le conteneur. Sans la classe → comportement d'avant, à l'identique
     (dégradation gracieuse totale : si le JS ne pose pas la classe, rien ne change). */
  body.result-open{overflow:hidden}
  /* SR-26 FIX (empilement) — `.container,footer,section{position:relative;z-index:1}`
     (l.239, « contenu au-dessus du fond ») met TOUT le contenu de page sur un même
     plan z:1. #result est en position:fixed;z-index:60 mais PIÉGÉ dans le contexte
     d'empilement de son .container parent (z:1) → au niveau racine l'overlay reste à
     z:1, à égalité avec les sections marketing (#how/#demo/#faq/footer, z:1 elles
     aussi) qui, venant APRÈS dans le DOM, se peignent PAR-DESSUS. On sort le
     conteneur hôte de ce plan (classe posée en JS sur le .container de #result) :
     z:60 bat les sections (z:1) et reste SOUS la feuille SR-22 (racine z:70/71). */
  body.result-open .result-host{z-index:60}
  body.result-open .rescuewrap#result:not([hidden]){
    position:fixed;inset:0;z-index:60;background:var(--bg);
    overflow-y:auto;overflow-x:hidden;overscroll-behavior:contain;
    max-width:none;margin:0;padding:58px 20px 44px;
    animation:resultOverlayIn .24s ease}
  @keyframes resultOverlayIn{from{opacity:0}to{opacity:1}}
  /* la grille interne garde sa largeur lisible, centrée dans l'overlay */
  body.result-open #result .result-grid{margin-left:auto;margin-right:auto}
  /* barre du haut (injectée en JS) : fermer + reprendre une autre conversation */
  .result-topbar{position:fixed;top:0;left:0;right:0;z-index:61;display:flex;
    align-items:center;justify-content:space-between;gap:12px;
    padding:12px 16px;background:linear-gradient(var(--bg),var(--bg) 62%,rgba(255,253,252,0));
    pointer-events:none}
  .result-topbar > *{pointer-events:auto}
  .result-again{display:inline-flex;align-items:center;gap:7px;min-height:36px;
    padding:8px 14px;border:1px solid var(--border);border-radius:999px;background:var(--surface);
    font:600 13px var(--font-sans);color:var(--ink);cursor:pointer;box-shadow:var(--sh-sm);
    -webkit-tap-highlight-color:transparent;transition:border-color .14s,background .14s}
  @media(hover:hover){.result-again:hover{border-color:var(--ink2);background:var(--bg2)}}
  .result-again:active{transform:translateY(1px)}
  .result-again svg{width:14px;height:14px}
  .result-x{flex:none;width:38px;height:38px;display:grid;place-items:center;
    border:1px solid var(--border);border-radius:var(--r-sm);background:var(--surface);
    color:var(--ink2);font-size:17px;cursor:pointer;-webkit-tap-highlight-color:transparent;
    transition:background .14s,color .14s}
  @media(hover:hover){.result-x:hover{background:var(--bg2);color:var(--ink)}}
  @media(max-width:560px){
    body.result-open .rescuewrap#result:not([hidden]){padding:54px 14px 40px}
    .result-again{font-size:12.5px;padding:7px 12px}
  }
  @media(prefers-reduced-motion:reduce){
    body.result-open .rescuewrap#result:not([hidden]){animation:none}
  }

  /* ══ LOT2 · POINT 7 (Kevin) — LE FOND « IA » ANIMÉ DERRIÈRE LES 3 ÉCRANS ═════
     DEMANDE : un dégradé animé derrière l'écran de uncooking, la page brief et
     l'écran résultat.

     CE QU'ON N'A PAS FAIT, ET POURQUOI : on n'a PAS écrit un second système de
     dégradé. Il en existe déjà un, en prod, réglé : `.page-bg` (4 spans radiaux
     qui dérivent en daDrift1/2/3, blur 84px, opacity .4), rendu par
     templates/home.html sur toutes les pages d'accueil.

     LE VRAI PROBLÈME ÉTAIT UN PROBLÈME D'EMPILEMENT, PAS DE DÉGRADÉ : les deux
     écrans plein écran (#result promu en overlay, et .brief-tunnel-backdrop)
     peignent un `background:var(--bg)` OPAQUE par-dessus toute la page — donc
     par-dessus le .page-bg du body, qui existait toujours mais était couvert.
     D'où : on RÉINSTANCIE le même composant DANS chaque overlay (mountPageBg(),
     app.js), et on reprend la convention d'empilement que page-bg.css établit
     déjà au niveau body (`body > *:not(.page-bg){position:relative;z-index:1}`)
     appliquée cette fois aux enfants de l'overlay. Aucun keyframe nouveau,
     aucune couleur nouvelle : les spans héritent des règles `.page-bg` d'origine.

     LES TROIS GARDE-FOUS, TENUS ICI :
     (a) DESKTOP UNIQUEMENT — `display:none` par défaut, `display:block` seulement
         au-dessus de 901px. Sous 901px le nœud est injecté mais ne peint RIEN :
         zéro pixel, zéro composite, zéro coût sur mobile.
     (b) PREFERS-REDUCED-MOTION — l'animation est coupée (règle explicite plus bas,
         en plus de celle qui existe déjà l.364 pour `.page-bg span`). Le dégradé
         reste PEINT, simplement immobile : on retire le mouvement, pas le décor.
     (c) GPU-SAFE — les keyframes daDrift1/2/3 n'animent QUE `transform`
         (translate+scale) ; la seule autre propriété en jeu est `opacity`, posée
         une fois et jamais animée. Aucune propriété déclenchant un reflow.
     Et `pointer-events:none` vient de la règle `.page-bg` de base : la couche est
     strictement décorative, elle n'intercepte aucun clic. */
  .page-bg--overlay{display:none}
  @media(min-width:901px){
    /* Plus discret que sur la home (.4) : ici il passe derrière du texte de
       travail, pas derrière un hero marketing. */
    .page-bg--overlay{display:block;opacity:.26}
    body.result-open .rescuewrap#result:not([hidden]) > *:not(.page-bg),
    .brief-tunnel-backdrop > *:not(.page-bg){position:relative;z-index:1}
    /* ══ COMPACT-SNAPSHOT (18/07) — LA BARRE DU HAUT ÉTAIT DANS LE FLUX ═════════
       MESURÉ, 1280×800, résultat révélé : `.result-topbar` déclarée l.477 en
       `position:fixed;top:0` se calculait en `position:relative` — la règle
       `> *:not(.page-bg){position:relative}` juste au-dessus l'écrase (même
       spécificité de position, elle vient après). Conséquence : la barre
       « New conversation / × » occupait 62px DE FLUX, empilés SOUS les 58px de
       padding-top de l'overlay (padding qui existe précisément pour passer
       DERRIÈRE une barre fixe). Le contenu démarrait donc à y=120 au lieu de
       y=58 : 62px de vide mort en haut de l'écran résultat, tous les jours,
       sur desktop uniquement (la règle est dans ce @media). C'est ce vide qui
       poussait « Turn it into a human brief » sous la ligne de flottaison.
       On rend à la barre la position que son propre bloc déclare. z-index:61
       (l.477) > 1, donc le motif d'empilement du dégradé reste satisfait :
       la barre passe toujours au-dessus de .page-bg--overlay.
       ⚠️ Portée : la règle générique reste inchangée pour TOUS les autres
       enfants de l'overlay — on ne ré-autorise QUE la barre. */
    body.result-open .rescuewrap#result:not([hidden]) > .result-topbar{position:fixed}
    /* La barre redevenant FIXE, elle couvre y=0..62. Le padding-top de l'overlay
       (58px, l.471) passait JUSTE en dessous — mesuré : l'œil-de-bœuf
       « CONVERSATION SNAPSHOT », enfant absolu à top:-8px, atterrissait à y=51,
       donc SOUS la barre. On dégage 76px : 62 de barre + 8 de morsure de pilule
       + 6 de respiration. Net sur l'écran : on reprend quand même 44px des 62
       qu'occupait la barre en flux. */
    body.result-open .rescuewrap#result:not([hidden]){padding-top:76px}
  }
  @media(prefers-reduced-motion:reduce){
    .page-bg--overlay span{animation:none!important}
  }
  /* CADRAGE LOADING (Kevin) — même geste que le brief : pendant le uncooking,
     l'overlay ne montre que la carte #loading (orb + lead + worklog), courte. Sans
     cadrage elle est collée en haut d'un overlay plein écran → ~1000px de vide dessous
     (« un trou »). On centre la carte VERTICALEMENT dans l'overlay et on la cape, pour
     qu'elle respire en haut ET en bas. Le multitopic banner, s'il apparaît sous le
     worklog pendant le loading, est un 2e enfant flex → centré avec, pas de trou.
     ⚠️ Desktop uniquement (≥901px) : sur mobile l'overlay scrolle naturellement, on ne
     force pas de centrage/verrou (viewport court + doctrine SR-15). Scopé
     body.result-loading → n'affecte QUE le loading, jamais l'écran résultat (qui garde
     alignement haut + scroll interne au cadre). */
  @media(min-width:901px){
    body.result-loading .rescuewrap#result:not([hidden]){
      display:flex;flex-direction:column;align-items:center;justify-content:center}
    body.result-loading #loading{width:100%;max-width:560px;margin:0;flex:none}
  }
  /* SR-22 — FEUILLE DES SIGNAUX (bottom sheet, pattern A de l'étude SR-21).
     Sur écran étroit, « ↓ Détails » ne déplie plus les signaux INLINE (accordéon
     daté « 2010 ») : il fait monter une feuille depuis le bas — poignée + voile —
     qu'on referme d'un glissé vers le bas / tap sur le voile / Échap / ×. Le brief
     NE bouge pas : il reste carte+CTA (SR-06·B — il est payant, il se demande, il
     ne se frôle pas). Feuille + voile injectés au niveau <body> → ils échappent à
     tout bloc conteneur du #result. Dégradation : sans body.signal-open, aucun de
     ces styles ne s'applique et l'accordéon d'avant reste le repli garanti. */
  body.signal-open{overflow:hidden}
  .signal-scrim{position:fixed;inset:0;z-index:70;background:rgba(20,16,14,.42);
    opacity:0;transition:opacity .22s ease;-webkit-tap-highlight-color:transparent}
  .signal-scrim.on{opacity:1}
  .signal-sheet{position:fixed;left:0;right:0;bottom:0;z-index:71;
    max-height:88vh;display:flex;flex-direction:column;
    background:var(--bg);border-radius:20px 20px 0 0;
    box-shadow:0 -12px 40px rgba(20,16,14,.18);
    transform:translateY(100%);transition:transform .26s cubic-bezier(.32,.72,0,1);
    touch-action:none;overscroll-behavior:contain;
    padding-bottom:max(14px,env(safe-area-inset-bottom))}
  .signal-sheet.on{transform:translateY(0)}
  /* la zone poignée+entête est la surface de drag (touch-action:none) ; seul le
     corps défile (pan-y) → un glissé qui commence sur la poignée ferme, un glissé
     qui commence dans le corps fait défiler. */
  .signal-grab{flex:none;display:flex;justify-content:center;padding:10px 0 4px}
  .signal-grab::before{content:"";width:38px;height:4px;border-radius:3px;background:var(--border)}
  .signal-sheet-head{flex:none;display:flex;align-items:center;justify-content:space-between;
    gap:12px;padding:2px 18px 12px}
  .signal-sheet-title{margin:0;font-family:var(--font-display);font-weight:600;font-size:16px;
    letter-spacing:-.01em;color:var(--ink)}
  .signal-sheet-x{flex:none;width:34px;height:34px;display:grid;place-items:center;
    border:1px solid var(--border);border-radius:var(--r-sm);background:var(--surface);
    color:var(--ink2);font-size:16px;cursor:pointer;-webkit-tap-highlight-color:transparent;
    transition:background .14s,color .14s}
  @media(hover:hover){.signal-sheet-x:hover{background:var(--bg2);color:var(--ink)}}
  .signal-sheet-body{flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain;
    -webkit-overflow-scrolling:touch;touch-action:pan-y;padding:0 16px 6px}
  /* les cartes rapatriées gardent leur look, sans marge parasite héritée de la grille */
  .signal-sheet-body > #summary,.signal-sheet-body > .verdict{margin:0 0 14px}
  .signal-sheet-body > :last-child{margin-bottom:0}
  @media(prefers-reduced-motion:reduce){
    .signal-sheet{transition:none}
    .signal-scrim{transition:none}
  }
  .convcard{background:var(--bg2);border:1px solid var(--border);border-radius:var(--r-md);padding:18px 16px;text-align:center}
  .convtitle{display:inline-flex;align-items:center;justify-content:center;gap:9px;font-family:var(--font-display);font-weight:600;font-size:20px;letter-spacing:-.02em;color:var(--ink)}
  .convtitle .ic{color:var(--ink3);font-size:19px}
  .convmeta{margin-top:7px;font-size:13.5px;color:var(--ink2);display:flex;align-items:center;justify-content:center;gap:7px;flex-wrap:wrap}
  .convmeta .sep{color:var(--border)}
  /* `.up` reste en --accent DÉLIBÉRÉMENT : ce n'est pas du texte. La règle vise
     `ICON.up` (app.js), un <svg> tracé en currentColor, sans contenu textuel —
     relevé isSVG:true, textContent vide. Il relève donc de SC 1.4.11 (composants
     non textuels, seuil 3:1) et non de SC 1.4.3. Sur le fond réel de la carte
     (--bg2 #FAF8F7, ancêtre .convcard OPAQUE) il mesure 3,44:1 ≥ 3:1 : conforme.
     L'assombrir aurait été une faute — et le `font-weight` de sa règle sœur
     `.chip .up` trahit qu'on l'a jadis prise pour du texte. */
  .convmeta .up{color:var(--accent);vertical-align:-1px}
  /* CONTRASTE — les deux libellés voisins, EUX, sont bien du texte (SPAN, 11px/700
     et 13,5px/600), sur ce même fond opaque --bg2 #FAF8F7 : 3,44:1 en --accent,
     sous les 4,5:1 exigés. → --accent-active #C9153A = 5,43:1. */
  .convmeta .metahi{color:var(--accent-active);font-weight:700;text-transform:uppercase;font-size:11px;letter-spacing:.03em;margin-left:1px}
  .convmeta .burnt{color:var(--accent-active);font-weight:600;display:inline-flex;align-items:center;gap:5px}
  /* jauge d'essence : échelle COMPLÈTE toujours visible + curseur qui marque la position */
  .gauge{max-width:300px;margin:16px auto 4px}
  .gauge-track{position:relative;height:10px;border-radius:6px;background:linear-gradient(90deg,#16845B,#6DA82B,#C08A1A,#E06A1E,#FF2D55)}
  .gauge-cursor{position:absolute;top:50%;left:10%;width:18px;height:18px;border-radius:50%;background:#fff;border:3px solid var(--ink);box-shadow:0 2px 5px rgba(23,23,22,.3);transform:translate(-50%,-50%);transition:left .55s cubic-bezier(.34,1.15,.5,1),border-color .3s}
  .gauge-ends{display:flex;justify-content:space-between;margin-top:7px;font-size:10px;letter-spacing:.05em;text-transform:uppercase;color:var(--ink3);font-weight:600}
  .gauge-label{max-width:340px;margin:9px auto 2px;font-family:var(--font-display);font-weight:700;font-size:15px;line-height:1.3;letter-spacing:-.01em;text-wrap:balance}
  @media(prefers-reduced-motion:reduce){.gauge-cursor{transition:none}}
  /* flash « ✓ prêt » — moment d'accomplissement bref entre loading et récap */
  .ready-flash{text-align:center;padding:44px 30px 40px;margin-top:18px;animation:rfin .3s ease}
  .rf-check{width:64px;height:64px;border-radius:50%;background:var(--success);color:#fff;display:grid;place-items:center;margin:0 auto 14px;box-shadow:0 8px 22px rgba(22,132,91,.3);animation:rfpop .45s cubic-bezier(.34,1.56,.5,1)}
  .rf-check .i{width:34px;height:34px;stroke-width:3}
  .rf-label{font-family:var(--font-display);font-weight:700;font-size:22px;color:var(--success);letter-spacing:-.02em}
  @keyframes rfin{from{opacity:0}to{opacity:1}}
  @keyframes rfpop{0%{transform:scale(.4);opacity:0}60%{transform:scale(1.12)}100%{transform:scale(1);opacity:1}}
  @media(prefers-reduced-motion:reduce){.ready-flash,.rf-check{animation:none}}
  /* message adaptatif (nommer le problème + geste répétable, selon la zone) */
  .verdict{max-width:600px;margin:20px auto 6px;text-align:center;animation:handoffIn .45s ease}
  .v-title{font-family:var(--font-display);font-weight:600;font-size:18px;letter-spacing:-.01em;color:var(--ink);margin:0}
  .v-sub{font-size:14.5px;line-height:1.55;color:var(--ink2);margin:7px auto 0;max-width:54ch}
  @media(prefers-reduced-motion:reduce){.verdict{animation:none}}
  /* signal « handoff prêt ↓ » — guide l'œil vers le bloc en dessous */
  .ready-cue{text-align:center;margin:12px auto 0;font-size:13.5px;font-weight:600;color:var(--accent-active);display:flex;align-items:center;justify-content:center;gap:7px}
  .ready-cue .rc-arrow{display:inline-block;font-size:16px;animation:rcbob 1.3s ease-in-out infinite}
  @keyframes rcbob{0%,100%{transform:translateY(0)}50%{transform:translateY(4px)}}
  @media(prefers-reduced-motion:reduce){.ready-cue .rc-arrow{animation:none}}
  /* marqueur succès (B : plus vert, visible) + apparition handoff */
  #handoff-block{animation:handoffIn .45s ease}
  @keyframes handoffIn{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}
  .ready-flag{display:inline-flex;align-items:center;gap:9px;background:var(--success);color:#fff;border:none;border-radius:999px;padding:9px 17px 9px 12px;font-size:14.5px;font-weight:700;margin:0 0 16px;box-shadow:0 4px 14px rgba(22,132,91,.28);animation:readyPop .4s ease}
  @keyframes readyPop{from{opacity:0;transform:scale(.9)}to{opacity:1;transform:scale(1)}}
  .ready-flag .rc{width:22px;height:22px;border-radius:50%;background:rgba(255,255,255,.22);display:grid;place-items:center}
  .ready-flag .i{width:15px;height:15px;stroke-width:2.6}
  @media(prefers-reduced-motion:reduce){.ready-flag{animation:none}}
  @media(prefers-reduced-motion:reduce){#handoff-block{animation:none}}
  .chips-head{text-align:center;font-weight:600;font-size:11px;color:var(--ink3);text-transform:uppercase;letter-spacing:.1em;margin-top:18px;padding-top:16px;border-top:1px solid var(--divider)}
  .chips{display:flex;gap:10px;flex-wrap:wrap;margin-top:12px;justify-content:center}
  .chip{display:inline-flex;align-items:center;gap:8px;height:42px;padding:0 17px;border-radius:9999px;border:1px solid var(--accent-soft);background:var(--surface);font-size:14.5px;font-weight:600;color:var(--ink);box-shadow:var(--sh-sm);transition:.16s;animation:chipIn .4s ease backwards}
  .chip:hover{transform:translateY(-2px);box-shadow:var(--sh-card)}
  .chip .ic{font-size:17px;color:var(--accent)}
  /* CONTRASTE — le chiffre en gras est du TEXTE (<b>, 14,5px/700). Son fond réel
     dépend de l'état de la pastille : --surface #FFFFFF sur une `.chip` normale
     (3,65:1), --accent-soft #FFF0F3 dès qu'elle porte `.hi` (3,30:1). Les deux
     sont sous 4,5:1. → --accent-active : 5,75:1 et 5,21:1. */
  .chip b{color:var(--accent-active);font-weight:700;font-family:var(--font-display)}
  .chip:nth-child(2){animation-delay:.06s}.chip:nth-child(3){animation-delay:.12s}.chip:nth-child(4){animation-delay:.18s}
  @keyframes chipIn{from{opacity:0;transform:translateY(8px) scale(.96)}to{opacity:1;transform:none}}
  @media(prefers-reduced-motion:reduce){.chip{animation:none}.chip:hover{transform:none}}
  /* chips miroir (neutres) + indice « élevé » (uniquement volume/durée) */
  .chip .lab{font-size:10.5px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--ink3)}
  /* `.up` INCHANGÉ, même motif que `.convmeta .up` : la règle ne touche qu'un
     <svg> (ICON.up), pas du texte — le `font-weight:700` qu'elle porte est sans
     effet et signale justement la méprise d'origine. Fond réel relevé : la pastille
     concernée est TOUJOURS `.chip.hi`, donc --accent-soft #FFF0F3 → 3,30:1, au-dessus
     des 3:1 de SC 1.4.11. Conforme : on n'y touche pas. */
  .chip .up{color:var(--accent);font-weight:700}
  .chip.hi{border-color:var(--accent);background:var(--accent-soft)}
  /* CONTRASTE — `.hitag` est du TEXTE (SPAN « HIGH », 10,5px/700). Son fond n'est PAS
     --surface : `.chip.hi` ci-dessus repeint la pastille en --accent-soft #FFF0F3,
     d'où 3,30:1 et non 3,65:1. → --accent-active = 5,21:1. */
  .chip.hi .hitag{font-size:10.5px;font-weight:700;text-transform:uppercase;letter-spacing:.03em;color:var(--accent-active)}
  .chip.neutral b{color:var(--ink)}
  .chip.neutral .ic{color:var(--ink3)}
  /* comparaison mots toi/IA — 2 lignes labellisées, l'écart raconte l'histoire */
  .bal-cap{font-size:11px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;color:var(--ink3);text-align:center;margin:14px 0 8px}
  .balance{margin:0 auto;max-width:380px;display:flex;flex-direction:column;gap:9px}
  .bal-row{display:grid;grid-template-columns:72px 1fr auto;align-items:center;gap:10px}
  .bal-name{font-size:13px;font-weight:700;color:var(--ink);text-align:right}
  .bal-name.ai{color:var(--accent-active)}
  .bal-track{height:12px;border-radius:6px;background:var(--divider);overflow:hidden}
  .bal-fill{display:block;height:100%;border-radius:6px;transition:width .55s cubic-bezier(.34,1.1,.5,1)}
  .bal-fill.you{background:var(--ink)}
  .bal-fill.ai{background:var(--accent)}
  .bal-num{font-size:12.5px;font-weight:600;color:var(--ink2);white-space:nowrap}
  .bal-num.ai{color:var(--accent-active)}
  @media(prefers-reduced-motion:reduce){.bal-fill{transition:none}}
  /* encart context rot sourcé (bloc 5) */
  .rot-line{text-align:center;font-size:12.5px;color:var(--ink3);margin:14px 22px 2px;line-height:1.5}
  .rot-line a{color:var(--accent-active);font-weight:600;text-decoration:none;border-bottom:1px solid var(--accent-soft)}
  .rot-line a:hover{border-color:var(--accent)}
  .chip:hover{border-color:var(--accent);transform:translateY(-1px)}
  details.proof{margin-top:10px}
  details.proof summary{list-style:none;cursor:pointer;color:var(--accent-active);font-size:13px;font-weight:600}
  details.proof .ev{font-size:13px;color:var(--ink2);border-left:2px solid var(--border);padding:5px 12px;margin:8px 0}

  /* HANDOFF */
  .handoff{margin-top:20px;background:var(--surface);border:1px solid var(--border);border-radius:var(--r-xl);overflow:hidden;box-shadow:var(--sh-card)}
  .handoff .hd{display:flex;align-items:center;gap:10px;padding:20px 22px 12px}
  .handoff .hd .ic{color:var(--accent);font-size:20px}
  .handoff .hd .t{font-family:var(--font-display);font-weight:600;font-size:21px;letter-spacing:-.02em}
  .handoff .hd .s{font-size:12.5px;color:var(--ink2);margin-left:auto}
  /* PT2 (Kevin) : le titre « Your handoff » + son icône disparaissent (la bannière verte
     EST désormais le titre du bloc). On garde la SEULE mention utile : « paste as
     message #1 · editable » (elle seule dit que c'est éditable), remise à gauche. */
  .handoff .hd .ic,.handoff .hd .t{display:none}
  .handoff .hd .s{margin-left:0}
  .handoff .hd{padding-top:10px}
  .promptwrap{position:relative;margin:0 18px}
  textarea.prompt{display:block;width:100%;max-height:340px;overflow:auto;resize:none;background:var(--bg2);border:1px solid var(--border);border-radius:var(--r-md);
    padding:20px;margin:0;font-family:var(--font-mono);font-size:13px;line-height:1.7;color:#2b2925;white-space:pre-wrap}
  textarea.prompt:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 4px var(--accent-ring)}
  .copybar{display:flex;gap:10px;align-items:center;margin:14px 18px 0;padding:0}
  .btn-copy{flex:1;height:56px;font-size:17px}
  /* Hiérarchie Open/Copy (petite conv) : empilé, Open dominant en haut, Copy
     discret dessous — fix test iPhone 13/07 (Open préremplit l'app en 1 clic). */
  .copybar-stack{flex-direction:column;align-items:stretch;gap:8px}
  .copybar-stack .btn{width:100%;flex:none}
  .btn-copy-sec{height:46px;font-size:14.5px}
  /* ══ LOT2 · POINT 5 — LE SOMMAIRE DU LIVRABLE (voir app.js/renderHandoffMap) ══
     Il habite le vide sous la barre de copie SANS prétendre être une carte de
     plus : pas de fond, pas d'ombre, un simple filet haut qui le rattache à la
     barre. Il ANNONCE, il n'agit pas — donc aucun état interactif, aucun :hover
     (rien à survoler : ce ne sont pas des boutons, ce sont des noms de sections).
     Les en-têtes arrivent DÉJÀ en capitales depuis le livrable : pas de
     text-transform ici, on n'ajoute rien à ce que le document dit de lui-même. */
  .handoff-map{display:flex;flex-wrap:wrap;align-items:baseline;gap:7px 10px;
    margin:14px 18px 0;padding:13px 2px 0;border-top:1px solid var(--divider)}
  .hm-lead{flex:none;font:600 11.5px/1.4 var(--font-sans);letter-spacing:.04em;color:var(--ink3)}
  .hm-list{display:flex;flex-wrap:wrap;gap:6px 7px;min-width:0}
  .hm-i{font:600 10.5px/1 var(--font-sans);letter-spacing:.06em;color:var(--ink2);
    background:var(--bg2);border:1px solid var(--border);border-radius:var(--r-sm);
    padding:5px 8px;white-space:nowrap}
  @media(max-width:560px){.handoff-map{margin:12px 12px 0}}

  .fnote-toggle{display:flex;align-items:center;justify-content:center;gap:8px;margin:12px 22px 0;font-size:12.5px;color:var(--ink3);cursor:pointer;user-select:none}
  .fnote-toggle input{accent-color:var(--accent);width:15px;height:15px;cursor:pointer;flex:none}
  .fnote-toggle:hover{color:var(--ink2)}
  .handoff .foot{padding:12px 22px 20px;font-size:12px;color:var(--ink3);text-align:center}
  /* loading + erreur */
  /* POINT 7 (Kevin) — « malgré le cadrage, je ne le sens pas ». Diagnostic à l'œil
     neuf, mesuré : la carte était en --bg2 (#FAF8F7) POSÉE SUR --bg (#FFFDFC), soit
     ~1 % d'écart, bordure 1px --border et AUCUNE ombre — alors que toutes les autres
     cartes du produit (.handoff, .inputcard) portent --sh-card. Résultat : l'objet ne
     se détachait pas du fond, on ne voyait pas une carte mais du texte qui flotte.
     C'est ÇA le « cadre pleine largeur » ressenti : pas une largeur, une absence de
     bord. On la fait exister comme les autres cartes — surface blanche + ombre. */
  .loading{background:var(--surface);border:1px solid var(--border);border-radius:var(--r-xl);padding:34px 30px;margin-top:18px;text-align:center;box-shadow:var(--sh-card)}
  .loading .orb{width:44px;height:44px;border-radius:50%;background:var(--accent);margin:0 auto 16px;box-shadow:0 0 0 0 var(--accent-ring);animation:orb 1.8s ease-in-out infinite}
  @keyframes orb{0%,100%{box-shadow:0 0 0 0 rgba(255,45,85,.35);transform:scale(1)}50%{box-shadow:0 0 0 16px rgba(255,45,85,0);transform:scale(1.08)}}
  .loading .lead{font-weight:700;font-size:22px;font-family:var(--font-display);letter-spacing:-.02em}
  .loading .lead .dots::after{content:"…";animation:dots 1.4s steps(4,end) infinite;display:inline-block;width:1.2em;text-align:left;overflow:hidden;vertical-align:bottom}
  @keyframes dots{0%{width:0}100%{width:1.2em}}
  /* ══ POINT 7 + POINT 8 (Kevin) — LA JAUGE D'AVANCEMENT, UN SEUL SYSTÈME ═══════
     Deuxième moitié du diagnostic « worklog sans vie » : les 9 étapes s'affichaient
     TOUTES d'emblée, dont 4 à 5 en opacity .4 — donc le bas de la carte était un
     bloc de gris fantôme, ce qui se lit « vide / pas fini », pas « ça travaille ».
     Et rien ne disait OÙ ON EN EST ni qu'il y a une fin.
     On ajoute donc UN rail d'avancement (injecté par app.js, pas par le HTML : le
     bloc #loading est répliqué dans ~40 index.html construits + templates/home.html
     qui est hors périmètre — le JS est la seule source unique possible ici).
     ⚠️ HONNÊTETÉ DE LA JAUGE : elle suit les étapes RÉELLEMENT franchies
     (done / total), jamais un faux pourcentage d'API. Quand la dernière étape est
     en cours, elle plafonne donc à (n-1)/n et n'annonce JAMAIS 100 % pendant qu'on
     attend encore le modèle. Elle ne peut pas mentir.
     POINT 8 : ce rail et ce worklog sont posés par la MÊME fonction (setWorklog) et
     stylés par les MÊMES règles pour les deux écrans (uncooking + rédaction du
     brief). Un seul geste « l'IA travaille », écrit une fois, utilisé deux fois. */
  .loading .wl-progress{position:relative;height:3px;border-radius:999px;background:var(--divider);
    max-width:440px;margin:22px auto 0;overflow:hidden}
  .loading .wl-progress i{position:absolute;left:0;top:0;bottom:0;width:0;border-radius:999px;
    background:var(--accent);transition:width .5s cubic-bezier(.4,0,.2,1)}
  /* worklog animé (R5) — lignes qui se remplacent, chips colorées à micro-anim */
  .loading .worklog{margin:14px auto 0;padding:0;list-style:none;font-size:14.5px;max-width:440px;text-align:left}
  /* opacité de repos remontée .4 → .55 : les étapes à venir doivent se lire comme
     un PLAN annoncé (« voilà ce qui va se passer »), pas comme du texte mort. */
  .loading .worklog li{display:flex;align-items:center;gap:11px;padding:7px 11px;margin:0 -11px;border-radius:var(--r-sm);
    position:relative;color:var(--ink3);opacity:.55;background:transparent;
    transition:opacity .4s ease,color .4s ease,transform .4s ease,background-color .4s ease;transform:translateY(2px)}
  .loading .worklog .wl-dot{flex:none;width:10px;height:10px;border-radius:50%;background:var(--ink3);position:relative;transition:background .35s ease;box-shadow:0 0 0 0 rgba(255,45,85,0)}
  /* ligne en cours : texte encré + dot accent qui pulse + LIT teinté (--accent-soft,
     token existant) — l'étape courante devient un objet repérable d'un coup d'œil
     au lieu d'une ligne de texte un peu plus foncée que ses voisines. */
  .loading .worklog li.active{opacity:1;color:var(--ink);transform:none;background:var(--accent-soft)}
  .loading .worklog li.active .wl-dot{background:var(--accent);animation:wlpulse 1.3s ease-in-out infinite}
  @keyframes wlpulse{0%,100%{box-shadow:0 0 0 0 rgba(255,45,85,.45)}50%{box-shadow:0 0 0 6px rgba(255,45,85,0)}}
  /* ligne terminée : dot succès + tick qui se dessine */
  .loading .worklog li.done{opacity:1;color:var(--ink2)}
  .loading .worklog li.done .wl-dot{background:var(--success);animation:none}
  .loading .worklog li.done .wl-dot::after{content:"";position:absolute;left:3px;top:1.5px;width:3px;height:6px;border:solid #fff;border-width:0 1.6px 1.6px 0;transform:rotate(45deg) scale(0);transform-origin:center;animation:wltick .3s ease forwards}
  @keyframes wltick{to{transform:rotate(45deg) scale(1)}}
  /* apparition/entrée d'une nouvelle ligne */
  @keyframes wlrise{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
  .loading .worklog li.enter{animation:wlrise .4s ease}
  @media(prefers-reduced-motion:reduce){.loading .orb{animation:none}.loading .worklog li{opacity:1;color:var(--ink);transform:none;transition:none}.loading .worklog li .wl-dot{animation:none!important}.loading .worklog li.done .wl-dot::after{animation:none;transform:rotate(45deg) scale(1)}.loading .worklog li.enter{animation:none}.loading .lead .dots::after{animation:none;content:"…";width:auto}
    /* ⚠️ En reduced-motion, setWorklog pose `.active` sur TOUTES les lignes (pas de
       défilement : on montre la liste entière d'un coup). Avant le POINT 7 c'était
       sans conséquence — `.active` ne changeait qu'opacité et couleur. Maintenant
       qu'il porte un fond teinté, « tout actif » donnerait 9 lignes roses pleines.
       On neutralise donc le fond ici : la liste redevient une liste. */
    .loading .worklog li.active{background:transparent}
    /* Et la jauge n'a plus rien à raconter (aucune étape ne progresse) : on la
       retire plutôt que de la laisser figée à 0 %, ce qui se lirait « cassé ». */
    .loading .wl-progress{display:none}}
  /* erreur qui tranche : ⚠ + rouge franc, distinct du rose de marque */
  .errmsg{margin-top:16px;background:var(--err-bg);border:1.5px solid var(--err-border);color:var(--err-ink);border-radius:var(--r-md);padding:14px 16px;font-size:14px;line-height:1.5;text-align:left}
  .errmsg::before{content:"⚠ "}
  .err-guide{display:inline-block;margin-top:8px;color:var(--err-ink);font-weight:600;text-decoration:underline;cursor:pointer}
  .copied{color:#fff!important}
  .btn-secondary.copied{background:var(--success)!important;border-color:var(--success)!important}
  .btn-primary.copied{background:var(--success)!important;box-shadow:none!important}

  /* SECTIONS — rythme vertical GLOBAL : une seule cadence sur toute la page */
  section{padding:84px 0}
  #how{padding:84px 0}
  .alt-bg{background:var(--bg2);border-top:1px solid var(--divider);border-bottom:1px solid var(--divider)}
  .sechead{text-align:center;margin-bottom:56px}
  .sechead h2{font-family:var(--font-display);font-weight:700;font-size:clamp(2rem,4vw,2.9rem);letter-spacing:-.03em;margin:0 0 8px}
  .sechead p{color:var(--ink2);font-size:16px;margin:0}
  .steps3{display:grid;grid-template-columns:repeat(3,1fr);gap:24px}
  .step{background:var(--surface);border:1px solid var(--border);border-radius:var(--r-xl);padding:32px 28px 32px;box-shadow:var(--sh-sm);transition:.2s;text-align:center}
  .step:hover{box-shadow:var(--sh-cardh);transform:translateY(-5px);border-color:var(--accent-soft)}
  /* vignette picto-ligne + microcap d'étape (remplacent le rond chiffré) */
  .step .illus{display:block;height:96px;max-width:100%;margin:0 auto 16px;color:var(--ink2)}
  .step .stepcap{font-size:10.5px;font-weight:700;letter-spacing:.12em;text-transform:uppercase;color:var(--ink3);margin:0 0 8px}
  .step h3{font-family:var(--font-display);font-size:21px;margin:0 0 8px;font-weight:600;letter-spacing:-.02em}
  .step p{font-size:14.5px;color:var(--ink2);margin:0;line-height:1.6}
  /* #11 démo handoff (R2 : chrome + ombre + largeur maîtrisée) */
  .demo-wrap{max-width:680px;margin:0 auto;background:var(--surface);border:1px solid var(--border);border-radius:16px;overflow:hidden;box-shadow:0 30px 70px -20px rgba(255,45,85,.16),0 18px 44px -18px rgba(43,41,37,.26),0 4px 14px -6px rgba(43,41,37,.12);transition:transform .25s ease,box-shadow .25s ease}
  .demo-wrap:hover{transform:translateY(-4px);box-shadow:0 42px 92px -22px rgba(255,45,85,.24),0 24px 56px -18px rgba(43,41,37,.30)}
  .demo-hd{display:flex;align-items:center;gap:8px;padding:13px 18px;border-bottom:1px solid var(--divider);background:var(--bg2)}
  .demo-hd .demo-tag{margin-left:auto;font-family:var(--font-mono);font-size:11px;color:var(--success);background:var(--success-soft);border:1px solid var(--success-border);border-radius:999px;padding:2px 10px;letter-spacing:.02em}
  .demo-hd .dd{width:11px;height:11px;border-radius:50%;background:var(--border)}
  .demo-hd .dd:nth-child(1){background:#ff5f57}.demo-hd .dd:nth-child(2){background:#febc2e}.demo-hd .dd:nth-child(3){background:#28c840}
  .demo-hd .demo-title{margin-left:10px;font-family:var(--font-mono);font-size:12.5px;color:var(--ink3)}
  pre.demo-body{margin:0;padding:28px 30px;font-family:var(--font-mono);font-size:13px;line-height:1.75;color:#2b2925;white-space:pre-wrap;overflow-x:auto}
  pre.demo-body b{font-weight:600;color:var(--ink)}
  /* CONTRASTE — les mots-clés du bloc de démo (« ROOT CONTEXT », « LOCKED
     DECISIONS »…) sont du TEXTE, 13px/500. Le fond est bien --surface #FFFFFF :
     `.demo-wrap` est un ancêtre OPAQUE, le dégradé ambiant ne l'atteint pas.
     3,65:1 en --accent → 5,75:1 en --accent-active. */
  pre.demo-body .k{color:var(--accent-active);font-weight:500}
  .demo-note{text-align:center;font-size:13px;color:var(--ink3);margin:16px auto 0;max-width:560px}
  /* #17 sound familiar — marquee horizontal 2 rangées (R6) */
  .pains{position:relative;overflow:hidden;-webkit-mask-image:linear-gradient(90deg,transparent,#000 9%,#000 91%,transparent);mask-image:linear-gradient(90deg,transparent,#000 9%,#000 91%,transparent)}
  .pain-row{display:flex;gap:18px;width:max-content;will-change:transform}
  .pain-row + .pain-row{margin-top:18px}
  .pain-row.r1{animation:marquee-l 46s linear infinite}
  .pain-row.r2{animation:marquee-r 60s linear infinite}
  .pains:hover .pain-row{animation-play-state:paused}
  @keyframes marquee-l{from{transform:translateX(0)}to{transform:translateX(-50%)}}
  @keyframes marquee-r{from{transform:translateX(-50%)}to{transform:translateX(0)}}
  .pain{flex:0 0 auto;width:min(440px,82vw);box-sizing:border-box;margin:0;background:var(--surface);border:1px solid var(--border);border-left:3px solid var(--accent);border-radius:var(--r-lg);padding:26px 28px;font-size:16.5px;line-height:1.6;color:var(--ink);font-style:normal;box-shadow:var(--sh-sm)}
  .pain cite{display:block;margin-top:15px;font-size:13px;color:var(--ink3);font-style:normal;letter-spacing:.02em;font-weight:500}
  @media(prefers-reduced-motion:reduce){.pain-row{animation:none!important;flex-wrap:wrap;width:auto;justify-content:center}.pains{-webkit-mask-image:none;mask-image:none;overflow:visible}.pain-row [aria-hidden="true"]{display:none}}
  .personas{display:grid;grid-template-columns:repeat(3,1fr);gap:18px}
  .persona{border:1px solid var(--border);border-radius:var(--r-lg);padding:24px;background:var(--surface);box-shadow:var(--sh-sm);transition:.2s}
  .persona:hover{box-shadow:var(--sh-cardh);transform:translateY(-4px) scale(1.012)}
  .persona .icwrap{width:44px;height:44px;border-radius:var(--r-md);background:var(--accent-soft);display:grid;place-items:center;color:var(--accent-active);font-size:22px}
  .persona h3{font-family:var(--font-display);font-size:16px;margin:14px 0 4px;font-weight:600;letter-spacing:-.01em}
  .persona p{font-size:13.5px;color:var(--ink2);margin:0;line-height:1.55}
  .persona p b{color:var(--ink);font-weight:600}
  .who-foot{text-align:center;max-width:620px;margin:40px auto 0;font-size:16.5px;color:var(--ink2);line-height:1.6}
  .who-foot b{color:var(--ink);font-weight:600}
  .faq{max-width:720px;margin:0 auto}
  details.q{border-bottom:1px solid var(--divider);padding:18px 4px}
  details.q summary{list-style:none;cursor:pointer;font-weight:600;font-size:16px;display:flex;justify-content:space-between;gap:10px;align-items:center;font-family:var(--font-display);letter-spacing:-.01em}
  details.q summary .plus{color:var(--ink3);font-size:22px;transition:.2s;font-weight:400}
  details.q[open] summary .plus{transform:rotate(45deg);color:var(--accent)}
  details.q p{color:var(--ink2);font-size:14.5px;margin:12px 0 2px;line-height:1.6}

  /* FOOTER refait */
  footer{background:var(--ink);color:#e9e4e1;padding:72px 0 48px;margin-top:48px}
  /* 19/07 — DÉBORDEMENT HORIZONTAL DE, ~760–1000px (signalé comme un défaut
     d'en-tête : ce n'en est pas un, l'en-tête ne déborde à AUCUNE largeur, mesuré
     scrollWidth==clientWidth sur .nav en 760/860/960/1000 × 5 langues). La source
     est ICI. Une piste `1fr` vaut `minmax(auto,1fr)` : son plancher est le
     min-content, donc le MOT le plus long. « Nutzungsbedingungen » mesure 151px
     alors que la piste calculée tombe à ~115px en 860px → la grille refuse de
     rétrécir et pousse le document 52px au-delà du viewport (seul l'allemand
     dépasse ; les 4 autres langues ont un min-content déjà sous la piste).
     `minmax(0,1fr)` retire ce plancher, `hyphens:auto` (règle ci-dessous) coupe
     le mot devenu plus large que sa colonne. Vérifié : EN/ES/PT au pixel près
     inchangés, FR ±1px (arrondi), DE débordement 52px → 0.
     ⚠️ Cette règle doit rester AVANT la media query 760px (2 colonnes mobile),
     sinon elle l'écrase et le pied repasse à 4 colonnes de 57px sur téléphone. */
  .footgrid{display:grid;grid-template-columns:1.6fr repeat(4,minmax(0,1fr));gap:40px}
  /* `overflow-wrap:break-word` a été ESSAYÉ ici et RETIRÉ : il passait toutes les
     assertions numériques (scrollWidth==clientWidth partout) en cassant les mots
     n'importe où — capture 860px DE : « Datenschut/z », « Cookie-Einstellunge/n »,
     « UNTERNEH/MEN », lettres orphelines seules sur leur ligne. `hyphens:auto`
     coupe aux syllabes (les pages portent bien lang="de"), avec césure visible. */
  .footcol h4,.footcol a{hyphens:auto}
  .footbrand .logo{color:#fff;font-size:30px}
  .footbrand .tag{font-family:var(--font-display);font-size:19px;color:#fff;margin:16px 0 0;letter-spacing:-.02em;max-width:16ch;line-height:1.2}
  /* point de capture extension : chip visible (ouvre la modale guide / bloc 413) */
  .footnotify{margin-top:26px;max-width:320px}
  .footnotify .fn-link{display:inline-flex;align-items:center;gap:8px;color:#fff;background:rgba(255,255,255,.06);
    border:1px solid rgba(255,255,255,.16);border-radius:999px;padding:10px 16px;font-size:13.5px;font-weight:600;line-height:1.2;text-decoration:none;transition:.15s}
  .footnotify .fn-link:hover{background:rgba(255,255,255,.12);border-color:rgba(255,255,255,.34);transform:translateY(-1px)}
  .footnotify .fn-ico{width:16px;height:16px;flex:none;color:var(--accent)}
  /* Encart « préviens-moi » (CHANTIER-ALLÉGEMENT 16/07) — RELOCALISATION de
     l'ancienne case B (api/subscribe.js purpose conversation-watch, ex-#sub-block)
     vers le pied de page, hors du chemin critique du handoff, jamais un mur.
     Injecté en JS (mountWatch(), site/app.js) dans .footbrand, après .footnotify —
     zéro édition HTML par page (même philosophie que la capture email d'origine).
     ⚠️ Ces règles peignent le pied SOMBRE. Depuis le 19/07 le MÊME composant est
     aussi monté dans la feuille mobile, qui est claire : voir le bloc
     `.msheet .foot-watch` (re-thème complet) près des styles .msheet. Toute
     modification ici doit être relue là-bas. */
  .foot-watch{margin-top:22px;max-width:320px}
  .foot-watch-t{color:#fff;font-size:13.5px;font-weight:600;line-height:1.35;margin:0 0 10px}
  .foot-watch-form{display:flex;gap:8px}
  .foot-watch-email{flex:1;min-width:0;height:40px;padding:0 12px;font:inherit;font-size:13px;
    color:#fff;background:rgba(255,255,255,.06);border:1px solid rgba(255,255,255,.16);border-radius:var(--r-sm)}
  .foot-watch-email::placeholder{color:rgba(255,255,255,.42)}
  .foot-watch-email:focus{outline:none;border-color:rgba(255,255,255,.4)}
  .foot-watch-btn{flex:none;height:40px;padding:0 16px;font:600 13px var(--font-sans);color:#fff;
    background:var(--accent2);border:none;border-radius:var(--r-sm);cursor:pointer}
  .foot-watch-btn:hover{background:var(--accent2-hover)}
  .foot-watch-err{margin:8px 0 0;font-size:11.5px;color:#ff8a8a}
  .foot-watch-ok{margin:8px 0 0;font-size:12.5px;color:#fff;display:flex;align-items:center;gap:6px}
  .foot-watch-ok::before{content:"✓";color:var(--accent2)}
  .foot-watch-note{margin:8px 0 0;font-size:10.5px;line-height:1.4;color:rgba(255,255,255,.48)}
  /* air autour du sélecteur de langue du pied */
  footer .footlang{margin-top:44px}
  /* --ink3-on-dark (et non --ink3) : le pied est sur fond sombre, cf. note du
     bloc de jetons. Ici #938D88 vaut 5.47:1 sur #171716 ; --ink3 n'y vaudrait
     que 3.58:1. */
  .footcol h4{font-size:12px;letter-spacing:.1em;text-transform:uppercase;color:var(--ink3-on-dark);margin:0 0 14px;font-weight:600}
  .footcol a{display:block;color:#c9c2bc;text-decoration:none;font-size:14px;margin-bottom:10px}
  .footcol a:hover{color:#fff}
  .footsoon{display:block;color:#8a837d;font-size:14px;margin-bottom:10px}
  .footsoon em{font-style:normal;font-size:10.5px;letter-spacing:.06em;text-transform:uppercase;color:var(--accent);border:1px solid rgba(255,45,85,.4);border-radius:5px;padding:1px 6px;margin-left:4px}
  .footbottom{display:flex;justify-content:space-between;align-items:center;gap:12px;flex-wrap:wrap;border-top:1px solid #2c2825;margin-top:40px;padding-top:22px;font-size:12.5px;color:var(--ink3-on-dark)}

  /* scroll reveal */
  .reveal{opacity:0;transform:translateY(16px);transition:opacity .5s ease,transform .5s ease}
  .reveal.in{opacity:1;transform:none}
  @media(prefers-reduced-motion:reduce){.reveal{opacity:1;transform:none;transition:none}.btn-primary:hover,.step:hover,.persona:hover,.chip:hover,.demo-wrap:hover{transform:none}}
  @media(max-width:760px){.steps3,.personas{grid-template-columns:1fr}.footgrid{grid-template-columns:1fr 1fr}.nav .links > a:not(:last-child){display:none}}
  /* --- MOBILE (≤560px) : audit tech 2026-07-03 — pas de scroll latéral, hero/CTA lisibles, cibles ≥44px --- */
  @media(max-width:560px){
    html,body{overflow-x:hidden}
    img,svg{max-width:100%}
    .container{padding:0 18px}
    section{padding:56px 0}
    #how{min-height:auto;padding:64px 0}
    #how .sechead{margin-bottom:36px}
    .hero{padding:32px 0 24px}
    /* bug re-test 08/07 mobile réel : chevauchement H1/H2 signalé — espacement
       vertical insuffisant. Wording inchangé, marge H1 remontée pour respirer. */
    .hero h1{margin-bottom:28px}
    /* Le cadre suit en petit, resserré (l'allemand est le plus long : le cadre
       doit pouvoir passer sur deux lignes sans toucher les gouttières). */
    .eyebrow{font-size:16px;margin-bottom:16px;padding:7px 15px}
    .privacy-line{white-space:normal}
    /* input = zone de collage ÉVIDENTE et confortable (l'élément principal) ·
       bouton SÉPARÉ dessous, présent mais pas démesuré */
    .inputcard{flex-direction:column;gap:12px;padding:0;background:transparent;border:none;box-shadow:none}
    .inputcard input{flex:none;width:100%;height:66px;padding:0 18px;font-size:16px;text-align:left;border:1.5px solid var(--border);background:var(--surface);border-radius:var(--r-lg);box-shadow:var(--sh-card)}
    .inputcard input:focus{border-color:var(--accent);box-shadow:0 0 0 4px var(--accent-ring)}
    .inputcard .btn-lg{width:100%;height:52px;font-size:15.5px}
    /* le tick n'a pas de sens en colonne : on le masque proprement */
    .tick{display:none!important}
    /* bug re-test 08/07 mobile réel : "Relancer ma conversation" débordait à
       droite — à 375px exact, logo+bouton touchaient le bord SANS aucune
       marge de sécurité (357px pile sur 357px de conteneur utile), donc la
       moindre variance de rendu police (fallback système pendant le
       chargement des web fonts) faisait déborder. Resserré avec une vraie
       marge + garde-fou overflow (jamais de débordement même si un texte
       plus long arrive un jour). */
    .nav{padding:18px 0;gap:8px}
    .nav .logo{font-size:22px}
    .nav .links{min-width:0}
    .btn-sm{height:44px;padding:0 12px;font-size:12.5px;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
    .divlabel::before,.divlabel::after{width:calc(50% - 62px)}
    /* démo terminal : padding réduit, scroll interne conservé */
    pre.demo-body{padding:20px 18px;font-size:12.5px}
    .demo-hd{padding:11px 14px}
    /* modale : padding resserré, fermeture atteignable */
    .modal{padding:30px 20px 26px}
    .modal-close{top:12px;right:12px}
    .gb-shots{grid-template-columns:1fr}
    /* handoff : marges internes réduites */
    .handoff .hd{flex-wrap:wrap;padding:18px 16px 10px}
    .handoff .hd .s{margin-left:0;flex-basis:100%}
    .promptwrap{margin:0 12px}
    textarea.prompt{padding:16px}
    .copybar{margin:12px 12px 0}
    .btn-copy{font-size:16px}
    /* footer : 1 colonne sur petit écran */
    .footgrid{grid-template-columns:1fr;gap:26px}
    footer{padding:44px 0 32px}
    .footbottom{flex-direction:column;align-items:flex-start;gap:6px}
    /* cibles tactiles chips / faq */
    .chip{height:40px}
    details.q summary{padding:2px 0}
  }

  /* ===== compléments partagés (ex-tail /pro + /everyday) ===== */
  /* --- page-outil : compléments (partagé /pro + /everyday) --- */
  a.btn{text-decoration:none}
  .nav .links a.btn-primary{color:#fff}
  .nav .links a.btn-primary:hover{color:#fff}

  /* ===== CSS ex-runtime (ex-injectMultitopicCss, app.js) ===== */
  .mt-banner{display:flex;align-items:center;gap:12px 14px;flex-wrap:wrap;margin:18px 0 14px;padding:14px 18px;
    border-radius:var(--r-lg);background:linear-gradient(100deg,var(--accent-soft),#FFF8FA);
    border:1px solid #FFD6DE;box-shadow:var(--sh-sm);text-align:left}
  .mt-banner .mt-sp{color:var(--accent);font-size:17px;flex:none}
  .mt-banner .mt-bt{flex:1 1 160px;font-family:var(--font-display);font-weight:600;font-size:15.5px;letter-spacing:-.01em;color:var(--ink)}
  .mt-banner .mt-bl{display:flex;gap:6px;flex-wrap:wrap}
  .mt-banner .mt-hint{flex:1 0 100%;font-size:12px;color:var(--ink2);margin:-4px 0 0}

  /* EXTRACT — chips SÉLECTIONNABLES (vrais <button aria-pressed>).
     L'état coché ne repose PAS que sur la couleur (test daltonien) : il porte
     AUSSI une coche ✓ visible et un trait plein, quand l'état décoché est en
     pointillés, sans coche et en retrait. Forme + remplissage + glyphe.
     ══ V2.7 — DEUX ÉCHECS DE CONTRASTE CORRIGÉS SUR UN CONTRÔLE RÉEL ══════════
     Ces pastilles ne sont pas décoratives : ce sont les sujets que l'utilisateur
     COCHE, sur la bannière multi-sujets de la home et sur /extract/. Les deux
     états échouaient, et aucun relevé ne les avait attrapés parce que les deux
     échecs se cachent derrière une apparence « déjà rouge, donc déjà validée ».
     1. ÉTAT COCHÉ — il portait `color:#fff` sur `--accent` (#FF2D55) : 3,65:1,
        à 12px/600. Aucune dispense « grand texte » ne s'applique (elle exige
        ≥24px, ou ≥18,66px en gras), le seuil est donc bien 4,5:1. C'est
        EXACTEMENT la faute déjà corrigée en juillet sur le CTA principal, qui
        avait fait descendre toute l'échelle --cta : --accent est la couleur de
        MARQUE et ne porte plus jamais de texte. La pastille avait été oubliée
        dans ce passage. Elle prend --accent-active (#C9153A) → 5,75:1.
        Le remplissage reste plein, le rouge reste du rouge : rien ne change dans
        la lecture de l'état, seul le cran de la teinte descend.
     2. ÉTAT RETIRÉ — `--ink2` PLUS `opacity:.75`. L'opacité ne s'arrête pas au
        texte : elle compose l'élément entier sur le fond de la bannière. La
        couleur RENDUE n'était donc pas #625E5A mais #898380 → 3,38:1 (et non les
        ~3,6 estimés à vue : il faut composer sur le VRAI fond, un dégradé
        `--accent-soft → #FFF8FA`, et prendre son extrémité la plus défavorable).
        `opacity` TOMBE. --ink2 rendu à sa vraie valeur mesure 5,82:1.
     ⚠️ ET LA VRAIE QUESTION : EST-CE QUE LES DEUX ÉTATS RESTENT DISTINGUABLES ?
        Oui, et l'opacité n'était pas ce qui les distinguait. Le retrait se lit sur
        QUATRE canaux indépendants, tous conservés : remplissage plein vs
        transparent · trait continu vs POINTILLÉ · coche ✓ présente vs absente ·
        libellé barré vs non barré. L'opacité n'était qu'un cinquième signal,
        redondant, et c'était le seul qui coûtait le seuil. On perd un effet de
        retrait, on ne perd aucune information — et la règle daltonienne posée en
        tête de ce bloc s'en trouve renforcée, pas affaiblie.
     NON-TEXTE (seuil 3:1) : trait pointillé --ink2 sur bannière = 5,82:1 ; trait
     plein --accent-active = 5,21:1 ; la coche est blanche sur le remplissage,
     donc la même paire que le texte (5,75:1). Tous tenus. ═════════════════════ */
  .mt-topic{display:inline-flex;align-items:center;gap:5px;font:inherit;font-size:12px;font-weight:600;
    color:#fff;background:var(--accent-active);border:1.5px solid var(--accent-active);border-radius:999px;
    padding:5px 11px;white-space:nowrap;cursor:pointer;transition:background .14s,color .14s,border-color .14s}
  .mt-topic .mt-tick{display:inline-flex;width:12px;height:12px;flex:none}
  .mt-topic .mt-tick svg{width:12px;height:12px;fill:none;stroke:currentColor;stroke-width:3.2;stroke-linecap:round;stroke-linejoin:round}
  .mt-topic[aria-pressed="false"]{color:var(--ink2);background:transparent;border-style:dashed;border-color:var(--ink2)}
  .mt-topic[aria-pressed="false"] .mt-tick{display:none} /* la coche disparaît : l'état se lit sans couleur */
  .mt-topic[aria-pressed="false"] .mt-tl{text-decoration:line-through;text-decoration-thickness:1px}
  .mt-topic:focus-visible{outline:none;box-shadow:0 0 0 3px var(--accent-ring)}
  /* SR-04·A — tout :hover est sous @media(hover:hover) : sinon iOS colle l'état
     survolé après le tap et la chip décochée reste peinte comme cochée. */
  @media(hover:hover){
    .mt-topic:hover{border-color:var(--accent-hover)}
    .mt-topic[aria-pressed="false"]:hover{color:var(--ink)} /* `opacity:1` retiré avec l'opacité de l'état lui-même (V2.7) — il n'annulait plus rien. --ink sur bannière = 16,24:1 */
    .mt-regen:not(:disabled):hover{background:var(--accent-hover);border-color:var(--accent-hover)}
    .mt-more:hover{color:var(--ink)}
  }

  .mt-banner .mt-act{flex:1 0 100%;display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-top:2px}
  .mt-regen{font:inherit;font-size:12.5px;font-weight:600;color:#fff;background:var(--accent);
    border:1.5px solid var(--accent);border-radius:999px;padding:7px 14px;cursor:pointer;white-space:nowrap}
  .mt-regen:disabled{opacity:.45;cursor:not-allowed}
  .mt-regen:focus-visible{outline:none;box-shadow:0 0 0 3px var(--accent-ring)}
  .mt-note{font-size:11.5px;line-height:1.35;color:var(--ink2);flex:1 1 180px}
  .mt-note.is-warn{color:var(--ink);font-weight:600}

  /* ══ MERGE 18/07 — LE RELIQUAT « +N autres » DEVIENT UN DÉPLIANT ══════════════
     LOT2 · POINT 3 l'avait posé comme un COMPTE inerte (pas de bordure, pas de
     fond, encre atténuée) pour qu'il ne se lise pas comme un sujet. On garde
     exactement cette discrétion visuelle — c'est elle qui tient la promesse
     « box courte au repos » — mais il devient CLIQUABLE en mode EXTRACT.
     Il reste délibérément HORS de `.mt-topic` : pas de coche, pas d'état pressé,
     donc aucun risque qu'il se lise ou se compte comme un sujet cochable. */
  .mt-more{font:inherit;font-size:11px;font-weight:600;color:var(--ink3);background:none;border:0;
    padding:3px 4px;white-space:nowrap;align-self:center;cursor:pointer;text-decoration:underline;
    text-underline-offset:2px;text-decoration-thickness:1px}
  .mt-more:focus-visible{outline:none;box-shadow:0 0 0 3px var(--accent-ring);border-radius:999px}

  /* ⚠️ INDISPENSABLE — `.mt-topic` est en `display:inline-flex`, ce qui BAT la
     règle `[hidden]{display:none}` de l'UA. Sans cette ligne les chips au-delà
     du plafond s'afficheraient malgré `hidden` et la box s'allongerait au repos :
     tout le contrat fondateur tomberait. Ne pas retirer. */
  .mt-topic[hidden]{display:none}

  /* Cible tactile ≥44px (doctrine SR) — pointeur grossier ou écran étroit. */
  @media(max-width:900px),(pointer:coarse){
    .mt-topic{min-height:44px;padding:6px 14px;font-size:13px}
    .mt-regen{min-height:44px;padding:8px 16px;font-size:13.5px}
    .mt-more{min-height:44px;padding:6px 10px;font-size:12.5px}
  }

  /* ===== CSS ex-runtime (ex-injectUx14Css, app.js) ===== */
  .fbm-back{position:fixed;inset:0;z-index:60;background:rgba(23,23,22,.45);display:flex;justify-content:center;align-items:center;padding:20px}
  .fbm-card{background:var(--surface);border-radius:var(--r-lg);box-shadow:var(--sh-card);padding:20px 20px 18px;
    max-width:400px;width:100%;position:relative;text-align:left;animation:fbmIn .18s ease-out}
  @keyframes fbmIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
  .fbm-x{position:absolute;top:10px;right:10px;width:32px;height:32px;border-radius:50%;border:1px solid var(--border);
    background:var(--bg2);color:var(--ink2);font-size:15px;line-height:1;cursor:pointer;display:grid;place-items:center}
  .fbm-x:hover{color:var(--ink);border-color:var(--ink)}
  .fbm-t{font-family:var(--font-display);font-weight:600;font-size:17px;letter-spacing:-.01em;margin:0 0 14px;padding-right:34px}
  .fbm-chips{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:12px}
  .fbm-chip{font:600 13px/1 var(--font-sans);color:var(--ink2);background:var(--bg2);border:1px solid var(--border);border-radius:999px;padding:10px 14px;cursor:pointer}
  .fbm-chip:hover{border-color:var(--ink)}
  .fbm-chip.on{background:var(--accent-soft);border-color:var(--accent);color:var(--accent-active)}
  .fbm-send{width:100%;height:46px;font-size:14.5px}
  .fbm-note{font-size:11.5px;color:var(--ink3);margin:10px 0 0;text-align:center}
  .fbm-thanks{display:flex;align-items:center;justify-content:center;gap:8px;font-weight:600;font-size:14.5px;color:var(--ink2);padding:18px 0 6px}
  .fbm-grab{display:none}
  body.is-mobile .fbm-back{align-items:flex-end;padding:0}
  body.is-mobile .fbm-card{max-width:none;border-radius:18px 18px 0 0;box-shadow:0 -12px 30px rgba(23,23,22,.2);
    padding-bottom:max(18px,env(safe-area-inset-bottom));animation:fbmUp .22s ease-out}
  @keyframes fbmUp{from{transform:translateY(40px);opacity:.6}to{transform:none;opacity:1}}
  body.is-mobile .fbm-grab{display:block;width:38px;height:4px;border-radius:2px;background:var(--border);margin:0 auto 12px}
  @media(prefers-reduced-motion:reduce){
    .fbm-card,body.is-mobile .fbm-card{animation:none}
  }

  /* ===== CSS ex-runtime (ex-injectCaptureCss, app.js) ===== */
  .fb-line{display:flex;flex-wrap:wrap;align-items:center;gap:10px;margin:10px 4px 0;font-size:13px;color:var(--ink3)}
  .fb-btn{background:var(--bg2);border:1px solid var(--border);border-radius:var(--r-md);cursor:pointer;
    font-size:14px;line-height:1;padding:5px 9px;transition:transform .12s,border-color .12s}
  .fb-btn:hover{transform:translateY(-1px);border-color:var(--accent)}
  .fb-thanks{font-weight:600;color:var(--ink2)}
  /* Composant email V2 « empilement minimal » (redesign 08/07, validé fondateur
     sur maquette site/_email-mockup.html) : plus de pavé pointillé — un simple
     filet de séparation (padfoot), largeurs resserrées (~320-360px, jamais pleine
     largeur du résultat), bouton Google discret, champ + envoi empilés pleine
     largeur du composant, cases après le formulaire. */
  /* ===== Bloc capture email — ALIGNÉ SUR LE VALIDÉ (maquette _fin-mockup état C
     + _email-mockup VARIANTE 2 « champ et envoi EMPILÉS, largeur identique » —
     feu fondateur). Trois règles tenues :
     (1) ENCADRÉ DISCRET : fond surface, bordure grise fine, ombre légère, icône
         enveloppe NEUTRE. Aucune bordure accent, aucun rouge alarmant.
     (2) UNE largeur (--sub-w) et UNE hauteur (--sub-h) pour Google, champ et
         Envoyer — empilés, jamais accolés, jamais deux largeurs différentes.
     (3) L'ACCENT EST RÉSERVÉ AU SEUL BOUTON ENVOYER. ===== */
  .sub-block{margin:20px auto 0;padding:18px;max-width:calc(var(--sub-w) + 36px);background:var(--surface);border:1px solid var(--border);border-radius:14px;box-shadow:var(--sh-sm)}
  .sub-t{margin:0;display:flex;align-items:center;gap:8px;font-family:var(--font-display);font-weight:700;font-size:15.5px;letter-spacing:-.01em;color:var(--ink)}
  .sub-t .sub-ic{width:18px;height:18px;color:var(--ink3);flex:none}
  .sub-google{width:100%;max-width:var(--sub-w);margin:14px 0 0}
  /* ══ POINT 6 (Kevin) — LE BOUTON GOOGLE COUPÉ À DROITE. CAUSE TROUVÉE, MESURÉE ═══
     Le bouton FONCTIONNE (la fenêtre Google s'ouvre) : le défaut est purement visuel.
     GIS ne rend PAS d'iframe pour le bouton — il rend un <div role="button"> avec une
     largeur FIGÉE EN STYLE INLINE au moment du renderButton :
         style="width:400px; max-width:400px; min-width:min-content;"
     Cette largeur n'est JAMAIS recalculée ensuite. Donc dès que le slot rétrécit
     après le rendu (redimensionnement de fenêtre, rotation, tout reflow qui change
     la colonne), le bouton garde son ancienne largeur et `overflow:hidden` AMPUTAIT
     silencieusement le débord. Reproduit et mesuré sur le mur email du tunnel :
       — rendu à 1280px → largeur figée 400px ;
       — fenêtre ramenée à 375px → slot 331px, bouton toujours 400px ;
       — débord 69px, scrollWidth 400 vs clientWidth 331 → 69px coupés à droite.
     Correctif STRUCTUREL, en deux temps, qui tue la classe de bug entière :
       1. `max-width:100%` sur le nœud rendu par GIS bat son `width` inline (en CSS
          max-width l'emporte toujours sur width) → le bouton ne PEUT plus dépasser
          son slot, quelle que soit la largeur que GIS a figée. Sa largeur min-content
          mesurée est de 200,5px, très en dessous de tous nos slots (≥320px) : il a
          donc toujours la place de se réduire sans écraser son libellé.
       2. `overflow:hidden` → `visible` + centrage flex : on retire le mécanisme même
          de l'amputation silencieuse. Si un jour quelque chose dépasse malgré (1),
          ça se VERRA au lieu d'être coupé sans bruit — un défaut visible se corrige,
          un défaut masqué se re-livre. Le centrage règle au passage le cas inverse
          (slot élargi après rendu : bouton trop étroit collé à gauche, 69px de trou). */
  .sub-google-slot{width:100%;min-height:var(--sub-h);border-radius:var(--r-md);overflow:visible;line-height:0;
    display:flex;justify-content:center;align-items:center}
  .sub-google-slot > div,.sub-google-slot div[role="button"]{max-width:100%!important}
  .sub-google-slot iframe{display:block;margin:0!important}
  /* Bouton Google « click-to-load » : affiché AVANT consentement (même gabarit que
     le bouton officiel : pill blanc, bordure grise, logo G, largeur du champ).
     Rien de Google n'est chargé tant qu'il n'est pas cliqué → règle Emil tenue,
     et l'encadré n'a plus jamais de trou « champ Google vide ». */
  .sub-google-cta{display:flex;align-items:center;justify-content:center;gap:10px;width:100%;height:var(--sub-h);padding:0 14px;background:var(--surface);border:1px solid var(--border);border-radius:var(--r-md);cursor:pointer;font:600 14.5px/1 var(--font-sans);color:var(--ink);box-shadow:var(--sh-sm);transition:border-color .14s,background .14s}
  .sub-google-cta:hover{border-color:var(--ink3);background:var(--bg2,#faf8f6)}
  .sub-google-cta svg{width:18px;height:18px;flex:none}
  .sub-google-err{margin:6px 0 0;font-size:12.5px;font-weight:600;color:#C03D2E}
  .sub-or{display:flex;align-items:center;gap:10px;margin:12px 0;font-size:12px;color:var(--ink3);width:100%;max-width:var(--sub-w)}
  .sub-or::before,.sub-or::after{content:"";flex:1;height:1px;background:var(--border)}
  /* VARIANTE 2 : champ PUIS envoi, empilés, même largeur, même hauteur. */
  .sub-form{display:flex;flex-direction:column;gap:8px;width:100%;max-width:var(--sub-w)}
  .sub-email{width:100%;height:var(--sub-h);padding:0 14px;font:inherit;font-size:14.5px;color:var(--ink);background:var(--surface);border:1px solid var(--border);border-radius:var(--r-md);transition:border-color .14s,box-shadow .14s}
  .sub-email:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-ring)}
  .sub-email::placeholder{color:var(--ink3)}
  .sub-form .sub-btn{width:100%;height:var(--sub-h);margin:0;padding:0;font:600 14.5px/1 var(--font-sans);border:none;border-radius:var(--r-md);background:var(--accent);color:#fff;box-shadow:var(--sh-accent);transition:background .14s}
  .sub-form .sub-btn:hover{background:var(--accent-hover);transform:none}
  .sub-form .sub-btn:active{background:var(--accent-active)}
  .sub-cases{display:flex;flex-direction:column;gap:7px;margin:14px 0 0;width:100%;max-width:var(--sub-w);text-align:left}
  .sub-case{display:flex;align-items:flex-start;gap:9px;font-size:12.5px;line-height:1.45;color:var(--ink2);cursor:pointer}
  .sub-case input{appearance:none;-webkit-appearance:none;flex:none;width:16px;height:16px;margin:1px 0 0;border:1.5px solid var(--border);border-radius:5px;background:var(--surface);cursor:pointer;position:relative;transition:background .12s,border-color .12s}
  .sub-case input:checked{background:var(--accent);border-color:var(--accent)}
  .sub-case input:checked::after{content:"";position:absolute;left:4.5px;top:1.5px;width:4px;height:8px;border:solid #fff;border-width:0 2px 2px 0;transform:rotate(45deg)}
  .sub-case input:focus-visible{outline:2px solid var(--accent-ring);outline-offset:1px}
  .sub-err{margin:10px 0 0;font-size:12.5px;font-weight:600;color:#C03D2E}
  .sub-note{margin:12px 0 0;font-size:11.5px;color:var(--ink3);line-height:1.5;max-width:var(--sub-w)}
  .sub-ok{margin:12px 0 0;font-size:13px;font-weight:600;color:var(--success)}
  /* Fix équilibre visuel écran principal (retour fondateur 08/07) : le handoff
     au-dessus prend toute la largeur du résultat, mais la colonne resserrée
     (320-360px, volontaire — cf. commentaire plus haut) du composant email
     restait collée à gauche par défaut (aucun élément n'était centré), ce qui
     lisait comme un décalage/oubli plutôt qu'un choix. Centre la colonne dans
     #sub-block SEULEMENT (scoping par id, PAS par classe) pour ne rien changer
     à l'écran de fin (.endmail, encore au stade maquette _fin-mockup.html, pas
     encore intégré) qui partage les mêmes classes .sub-*. */
  #sub-block{text-align:center}
  #sub-block .sub-form,#sub-block .sub-google,#sub-block .sub-or,#sub-block .sub-note,#sub-block .sub-cases{margin-left:auto;margin-right:auto}
  #sub-block .sub-t{justify-content:center}

  /* ===== HIÉRARCHIE 3 ZONES écran résultat (validé _result-mobile-v2-mockup.html) :
     Zone A (#handoff-block) héros en haut · Zone B (#summary) analyse repliée
     (foldMetrics/foldMobile inchangé) · Zone C (#zone-c ⊃ #sub-block) options
     secondaires séparées. Réordonnancement DOM par apply3Zones() ; ici le rythme.
     V1.8 : #handoff-block/#summary/#compact-band ne sont plus des enfants
     DIRECTS de #result (grille 2 colonnes desktop plus bas) mais de
     .result-main/.result-side — sélecteurs re-scopés en conséquence. Restent
     valables par id (spécificité) quel que soit le conteneur réel. ===== */
  #result > #divlabel{margin:28px 0 14px}
  .result-main > #handoff-block{margin-top:0}
  .result-side > #summary{margin-top:28px}
  .result-side > #compact-band{margin-top:24px}
  .result-side > #summary .convtitle{font-size:17px}
  .result-side > #summary .convtitle .ic{font-size:16px}
  /* CHANTIER-ALLÉGEMENT (16/07) : Zone C n'héberge plus la box email (supprimée
     §2) mais le brief humain (.note-inline) — plus de filet séparateur ni de
     label « Options secondaires » : c'est désormais un contenu à part entière de
     la colonne latérale, pas une annexe. La carte .note-inline porte sa propre
     bordure/ombre, la Zone C ne fait que la positionner. */
  /* ⚠️ PIÈGE TROUVÉ EN MESURANT LE §3 (16/07) : #zone-c est créé en JS via
     document.createElement("section") (apply3Zones) → il HÉRITE de la règle
     globale `section{padding:84px 0}` (ligne ~496), pensée pour les sections
     MARKETING de la page, pas pour un wrapper injecté. La règle desktop
     .result-side .zone-c{padding-top:0} ne tuait que le HAUT → il restait
     84px de padding-BAS invisibles sous la box premium. C'est ce qui
     l'empêchait d'atteindre le bas du handoff (mesuré : 84px pile), et ça
     ajoutait 84px de vide mort sous la box SUR MOBILE AUSSI — donc ça allonge
     l'écran que tout ce chantier cherche à raccourcir (cf. §1).
     padding:0 ici, une fois pour toutes : ce n'est pas une section marketing.
     Le padding-top:16px voulu sur mobile est réappliqué plus bas explicitement. */
  .zone-c{margin-top:0;padding:0}
  .zone-c-label{margin:0 0 14px;font-size:10.5px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:var(--ink3);text-align:left}
  /* PT4 — œil-de-bœuf du bloc signaux (même grammaire que zone-c-label) */
  /* PT4 + CORRECTION #1 (Kevin) : le titre du snapshot = LE MÊME composant que HUMAN
     BRIEF (pilule accent, texte blanc), qui MORD le bord haut de la carte (marge
     négative). Un seul composant, deux usages. position:relative+z-index pour rester
     au-dessus ; white-space:nowrap. Mobile-safe (sibling, pas ::before de la carte). */
  /* ══ POINT 2 (Kevin) — « pas le même orange que HUMAN BRIEF » ══════════════════
     ⚠️⚠️ ARBITRAGE SUPERSÉDÉ EN v4 — CE BLOC EST CONSERVÉ POUR L'HISTORIQUE.
     Il énonçait : « la palette :root n'a AUCUNE teinte chromatique libre : --accent
     (#FF2D55) est le CTA principal, --accent2 (#D9662E) PORTE HUMAN BRIEF,
     --success (#16845B) porte la bannière prêt du handoff ; prendre l'un des trois
     = recréer la collision qu'on corrige. »
     ══ CE QUI EST TOUJOURS EN VIGUEUR ═════════════════════════════════════════════
     LE CHOIX LOCAL, ci-dessous, NE CHANGE PAS. La pilule de titre du snapshot reste
     en --ink (#171716) et le relevé tient : #FFF sur #171716 = 17,9:1 (exigé 4,5:1).
     La raison qui le justifie n'a jamais été la réservation de l'orange, c'est la
     HIÉRARCHIE de registre : l'encre neutre dit « ceci est une SECTION / un relevé
     de faits » (snapshot, handoff), le vert dit « statut : prêt ». Cet argument
     survit intact à la libération de l'orange.
     ══ CE QUI EST TOMBÉ — « --accent2 PORTE HUMAN BRIEF » ═════════════════════════
     Voir le bloc « ORANGE LIBÉRÉ » en tête de fichier (section des jetons). La
     réservation est levée par le fondateur. NE PAS relire la phrase ci-dessus comme
     une interdiction en vigueur : elle en a déjà produit une — un agent a refusé de
     colorer une carte de la home en s'appuyant sur ce paragraphe, alors que le
     fondateur venait d'autoriser exactement ce geste.
     ⚠️ CORRECTION DE FORME, MESURÉE, QUE PERSONNE N'AVAIT VUE : le fondateur demande
     « deux labels MÊME FORME, couleurs distinctes ». Or ils n'avaient PAS la même
     forme. `.result-side` est un flex column ; un enfant `inline-flex` y est
     blockifié puis ÉTIRÉ par le align-items:stretch implicite → la pilule snapshot
     mesurait 367,3px de large (une BARRE pleine colonne) quand la pilule HUMAN BRIEF,
     elle, mesurait 96,5px (une vraie pilule qui épouse son texte). `align-self:start`
     rétablit la forme commune — c'est la moitié « même forme » de la demande. */
  .zone-b-label{position:relative;z-index:2;display:inline-flex;align-items:center;align-self:flex-start;
    margin:0 0 -11px 16px;font:700 9.5px/1 var(--font-sans);letter-spacing:.1em;
    text-transform:uppercase;color:#fff;background:var(--ink);border-radius:999px;
    padding:5px 10px;box-shadow:0 3px 8px rgba(23,23,22,.20);white-space:nowrap}
  /* POINT 3 — variante « posée au-dessus d'un bloc » : même pilule, mais sans la
     morsure de -11px, parce que le bloc handoff commence par la bannière verte
     (.ready-flag) et qu'une pilule qui chevauche une autre pilule est illisible. */
  /* ══ LOT2 · POINT 4 (Kevin) — « YOUR HANDOFF » DOIT ASSUMER LE BLOC ═════════
     Le handoff est la porte d'entrée de l'écran ; son étiquette était à la MÊME
     taille (9,5px) que celle d'un bloc secondaire. Elle n'annonçait rien, elle
     accompagnait. On la monte franchement — taille, chasse, rembourrage — en
     gardant EXACTEMENT la géométrie de pilule (inline-flex + border-radius:999px
     + align-self:flex-start) : c'est la même famille d'objet, en plus affirmé.
     La variante `is-standalone` est portée par le SEUL #handoff-label : la
     montée ne fuit donc pas sur les autres œils-de-bœuf. */
  .zone-b-label.is-standalone{margin:0 0 12px 2px;font-size:13px;letter-spacing:.085em;padding:8px 15px}

  /* MOTIF B2 (23/07) — en-tête nommé du résultat de MIGRATION, posé par app.js en
     tête de #handoff-block (« Your migration handoff — from ChatGPT to Claude »).
     Registre PHRASE (pas capitales) : il NOMME le livrable, là où l'œil-de-bœuf
     .zone-b-label au-dessus ÉTIQUETTE la section. Discret mais lisible. */
  .mig-result-head{margin:0 0 10px;font:600 15.5px/1.4 var(--font-sans);letter-spacing:-.01em;color:var(--ink)}

  /* ══ LOT2 · POINT 2 (Kevin) — « CONVERSATION SNAPSHOT » MORD SA PROPRE BOX ═══
     Avant : pilule posée AU-DESSUS de la carte, rattachée par une marge négative
     de -11px — elle flottait. Après : elle est un enfant ABSOLU de #summary et
     reprend trait pour trait la géométrie de `.note-inline-tag` (l'étiquette
     HUMAN BRIEF explicitement citée en référence) : top:-8px, left:16px, même
     rembourrage, même rayon. Seule l'ENCRE diffère (blanc sur --ink, l'existant
     de prod) — « même forme, couleurs distinctes », la règle déjà en vigueur.
     `margin:0` est indispensable : la marge mordante de la classe de base
     (0 0 -11px 16px) déplacerait la pilule absolue de 16px vers la droite. */
  #summary{position:relative}
  .zone-b-label.is-corner{position:absolute;top:-8px;left:16px;margin:0;z-index:3}
  @media(max-width:560px){
    #result > #divlabel{margin:20px 0 12px}
    .result-side > #summary{margin-top:22px}
    .result-side > #compact-band{margin-top:20px}
    /* ⚠️ SR-20 (17/07) — 10e OCCURRENCE DU FIL ROUGE : `padding-top:16px` était un
       VESTIGE. Son rôle était d'espacer la carte du FILET séparateur et du label
       « Options secondaires »… tous deux SUPPRIMÉS le 16/07 — le commentaire
       ci-dessus (l.821-825) l'acte noir sur blanc. Il paddait donc contre RIEN :
       16px de vide mort sur mobile, au-dessus de la carte premium, exactement là
       où on se bat pour des pixels. Le margin-top:22px, lui, reste légitime : il
       sépare la carte du bloc au-dessus. Rien n'errait, rien ne le signalait —
       une règle juste dont la raison a disparu. */
    .zone-c{margin-top:22px}
  }

  /* ===== GRILLE 2 COLONNES ÉCRAN RÉSULTAT DESKTOP (proposition fondateur
     validée — source de vérité site/_result-2col-mockup.html) : colonne
     principale 2/3 (#handoff-block) + colonne latérale 1/3 (#summary +
     #compact-band + .verdict en haut, #zone-c juste en dessous). DESKTOP
     UNIQUEMENT (>900px, précédent déjà utilisé pour .hero-da/.da-stage plus
     haut) — sous ce point de rupture, retour identique à l'empilement actuel
     pleine largeur, même ordre (apply3Zones()/site/app.js n'y touche pas).
     NB (écart avec le mockup) : .verdict (#verdict, message adaptatif affiché
     ~9s après le récap, replié par foldMetrics) n'était pas couvert par le
     mockup — il vit dans le DOM réel entre #summary et #zone-c. Rejoint
     .result-side pour préserver l'ordre visuel actuel (sinon il s'afficherait
     pleine largeur SOUS toute la grille, un comportement jamais vu ni validé).
     Fix hauteur (retour fondateur #1) : align-items:stretch (comportement Grid
     par défaut) + .result-main en flex column, .handoff (flex:1) absorbe la
     hauteur de ligne, .rot-line se pousse en bas (margin-top:auto) → les deux
     colonnes finissent à la même ligne de base, sans étirer le textarea. */
  .result-grid{display:grid;grid-template-columns:minmax(0,2fr) minmax(260px,1fr);gap:30px;align-items:stretch;max-width:1180px;margin:0 auto}
  .result-main{min-width:0;display:flex;flex-direction:column}
  .result-main .handoff{flex:1;display:flex;flex-direction:column}
  .result-main .rot-line{margin-top:auto}
  /* COMPACT-SNAPSHOT (18/07) — `top` relevé 24→70px : la barre du haut redevient
     FIXE (voir bloc l.538) et couvre les 62 premiers pixels. Une colonne collée à
     24px passerait DERRIÈRE elle une fois collée. N'a AUCUN effet sur la position
     de repos (sticky ne décale rien tant que l'élément n'est pas collé) — donc
     zéro impact sur la mesure hors-scroll. */
  .result-side{display:flex;flex-direction:column;gap:18px;position:sticky;top:70px}
  @media(min-width:901px){
    /* Le rail latéral gère déjà l'espacement via gap:18px : on neutralise les
       marges/le filet pensés pour l'empilement pleine largeur (règles plus
       haut), pour ne pas les cumuler avec le gap. Sous 900px ces overrides ne
       s'appliquent plus : les marges/le filet d'origine reprennent leur rôle
       telles quelles, empilement identique à l'existant. */
    .result-side > #summary,
    .result-side > #compact-band,
    .result-side .verdict{margin-top:0}
    .result-side .zone-c{margin-top:0;padding-top:0;border-top:none}
    .result-side .zone-c-label{text-align:left}
    /* §3 PARCOURS-REEL (16/07) : la box brief s'arrêtait 189px avant le bas du
       handoff (mesuré) — elle avait l'air d'une note de bas de page à côté de
       l'asset principal. Or c'est l'ASSET PREMIUM : il doit peser autant que le
       handoff, pas moins. La colonne (.result-side) était déjà étirée par la
       grille (align-items:stretch), mais ses enfants gardaient leur hauteur
       naturelle → 189px de vide en bas. flex:1 sur le DERNIER enfant (#zone-c)
       lui fait absorber ce vide, et .note-inline le remplit à son tour.
       align-content:space-between (et pas center) : le conteneur est en
       flex-wrap, donc align-content distribue les LIGNES sur l'axe vertical →
       icône+titre en haut, CTA poussé en bas.
       ⚠️ align-content:flex-start + margin-top:auto sur le CTA, et PAS
       space-between : avec space-between les 3 lignes se répartissaient à
       intervalles égaux → deux trous de ~110px (titre │ vide │ pile │ vide │
       CTA), 58% de la carte en vide mesuré. Motif standard « contenu groupé en
       haut, CTA ancré en bas » : un seul vide, en bas, qui se lit comme une
       respiration et non comme un bug. Le vide résiduel reste un vrai sujet
       (remonté à Kevin) : étirer ne suffit pas à donner du POIDS, il faudra du
       contenu — la carte est trop maigre pour 383px. */
    .result-side .zone-c{flex:1;display:flex;flex-direction:column}
    .result-side .zone-c .note-inline{flex:1;align-content:flex-start}
    .result-side .zone-c .note-inline-cta{margin-top:auto}
  }
  /* ══ SR-06·B « LE BUDGET D'ÉCRAN » (17/07, arbitré Kevin) ════════════════════
     Constat mesuré : sur mobile la carte premium démarrait à 732px (82px SOUS le
     pli de 650px) et son CTA à 917px (267px sous le pli). Le parcours réel étant
     coller → copier → PARTIR dans ChatGPT, elle n'était jamais vue : le premium
     n'existait pas sur téléphone. Ce n'est pas un défaut de design de la carte —
     c'est un problème de BUDGET : le handoff mangeait tout l'écran.
     Deux leviers, zéro nouvelle classe :
       · on plafonne le textarea (on n'a pas besoin de 340px de prompt pour
         comprendre qu'on l'a ; il scrolle déjà, `overflow:auto` l.479) ;
       · on remonte #zone-c AVANT les signaux (order:-1) — le brief est une
         ACTION, la jauge est du contexte ; l'action passe devant.
     ⚠️ `gap:0` est OBLIGATOIRE : .result-side a `gap:18px` en base (grille
     desktop). En repassant `display:flex` ici pour obtenir `order`, le gap
     ressusciterait et rajouterait 18px × 3 — exactement le vide qu'on chasse. ══ */
  @media(max-width:900px){
    .result-grid{display:block;max-width:920px}
    .result-main{display:block}
    .result-main .handoff{flex:none}
    .result-side{display:flex;flex-direction:column;gap:0;position:static}
    .result-side .zone-c{order:-1}
  }
  /* ══ SNAPSHOT-BOX — REPLI « EN PLACE » (retour fondateur : « la box qui décale »)
     Mesuré AVANT fix (1280×800, parcours réel) : au clic sur « ↓ Détails », la
     colonne principale passait de 767px à 1170px (+403px) et les chips de sujets
     sautaient de y=241 à y=524 (+283px). Deux causes distinctes, deux remèdes :

     (a) LE SAUT DE COLONNE. `hidden` retirait les détails DU FLUX ; la colonne
         latérale changeait de hauteur, dictait la hauteur de ligne, et
         `.result-main .handoff{flex:1}` la répercutait sur le handoff.
         Remède : sur desktop les détails s'ouvrent en PANNEAU HORS-FLUX
         (position:absolute, ancré sous la carte). La carte — donc la colonne,
         donc la ligne, donc le handoff — garde EXACTEMENT la même hauteur
         ouverte ou fermée. Rien ne pousse rien.

         ⚠️ TENTATIVE PRÉCÉDENTE, ÉCARTÉE APRÈS MESURE (à ne pas refaire) :
         `.result-side{height:0;min-height:100%;overflow-y:auto}` pour caler la
         ligne sur la colonne principale. Ça figeait bien le handoff, MAIS ça
         plafonnait la latérale à 580px alors que son contenu CONDENSÉ mesure
         739px → #zone-c (la carte brief premium, SR-06·B) se retrouvait coupée
         de 159px derrière un scroll interne sans affordance, DÈS l'état par
         défaut. On échangeait un défaut contre un autre. Le panneau hors-flux
         n'a pas ce coût : la colonne garde sa hauteur naturelle.
     (b) LE SAUT DES SUJETS. Réglé côté JS (app.js/setupSnapshotBox) : #mt-banner
         est remonté AU-DESSUS de #summary-details, donc le dépliage s'ouvre
         SOUS lui. Ici on ne fait que le RÉDUIRE quand la carte est condensée —
         « plus discret », JAMAIS masqué : pas de display:none, pas de hidden,
         pas de height:0 sur les sujets. C'est le critère dur du fondateur. ══ */
  @media(min-width:901px){
    #summary{position:relative}
    /* Le panneau hors-flux : il pend sous la carte comme sa continuation, et
       passe AU-DESSUS de ce qui suit dans la colonne (d'où le z-index et le
       fond opaque — sans fond, les détails se superposeraient à #zone-c). */
    #summary .summary-details{position:absolute;left:-1px;right:-1px;top:100%;z-index:6;
      background:var(--bg2);border:1px solid var(--border);border-top:none;
      border-radius:0 0 var(--r-md) var(--r-md);box-shadow:0 16px 34px rgba(0,0,0,.11)}
    #summary:not(.is-condensed) .summary-details{padding:2px 18px 16px}
  }

  /* ══ COMPACT-SNAPSHOT (18/07, retour fondateur) — LA BOX EST TROP HAUTE ═══════
     MESURÉ AVANT, 1280×800, colonne latérale 383px, 5 sujets détectés :
     #summary = 297,3px de haut, empilé en SIX lignes pleine largeur —
       18 (padding) · titre 20,5 · +8,8 · meta 16,5 · +16 · jauge 29 ·
       +9 · message d'état 19,5 · +10 · bandeau sujets 87,5 · +22 · « ↓ Détails »
       30,5 · 18 (padding).
     Deux lignes n'avaient AUCUNE raison d'être deux lignes : « 26 messages ·
     10k mots » (fait brut, discret) et « This conversation is overcooked »
     (l'état, l'information payante). Elles disent la même chose à deux échelles,
     et elles encadraient la jauge au lieu de la légender.

     CE QU'ON FAIT : la carte passe en GRILLE sur desktop, et ces deux lignes
     partagent la MÊME rangée, sous la jauge — l'état à gauche (il porte le sens,
     il garde la graisse), le décompte à droite (il documente). Puis on resserre
     le rythme partout (gouttières de grille au lieu des marges cumulées).

     POURQUOI UNE GRILLE ET PAS UN WRAPPER JS : recomposer l'ordre visuel sans
     toucher au DOM. Aucun nœud déplacé → `#conv-meta` / `#gauge-label` restent
     des ENFANTS DIRECTS de #summary, donc :
       · `body.is-mobile #summary.is-condensed > .convmeta` (l.~1360) continue de
         matcher — le repli mobile n'est pas touché d'un pixel ;
       · syncCompactBand() lit toujours `$("gauge-label").textContent` ;
       · la feuille SR-22 déplace `#summary` entier, elle ne voit rien changer.
     ET C'EST DESKTOP SEULEMENT (≥901px) : sous ce seuil la carte reste en bloc,
     exactement comme aujourd'hui.

     ⚠️ LES ENFANTS ABSOLUS NE SONT PAS PLACÉS : `.zone-b-label.is-corner`
     (l'œil-de-bœuf, top:-8px) et `.summary-details` (panneau hors-flux, l.1296)
     sont en position:absolute → hors flux de grille. Leur géométrie est
     inchangée, et la garantie « hauteur identique ouvert/fermé » avec.
     Les rangées sont donc numérotées sur les SEULS enfants en flux. ══════════ */
  @media(min-width:901px){
    .result-side > #summary{
      display:grid;grid-template-columns:minmax(0,1fr) auto;
      align-items:center;row-gap:8px;column-gap:10px;padding:14px 14px}
    /* rangée 1 — le titre, pleine largeur, toujours centré */
    .result-side > #summary > .convtitle{grid-area:1/1/2/3;margin:0}
    /* rangée 2 — la jauge, pleine largeur (elle reste le pivot visuel) */
    .result-side > #summary > .gauge{grid-area:2/1/3/3;margin:0;max-width:none}
    /* rangée 3 — LA LIGNE FUSIONNÉE : état à gauche, décompte à droite */
    /* ⚠️ `text-wrap:auto` OBLIGATOIRE ici : la règle de base (l.615) pose
       `text-wrap:balance`, qui ÉQUILIBRE — donc coupe volontairement en deux
       lignes de largeur égale même quand une seule suffirait. Mesuré : la ligne
       fusionnée pesait 33,8px au lieu de 17. Pertinent pour un message centré
       pleine largeur, contre-productif pour une demi-colonne. */
    .result-side > #summary > .gauge-label{grid-area:3/1/4/2;margin:0;max-width:none;
      font-size:13px;line-height:1.3;text-align:left;min-width:0;text-wrap:auto}
    /* La colonne de droite est dimensionnée par SON contenu (`auto`) : chaque
       pixel qu'elle prend est un pixel retiré au message d'état, qui repasserait
       alors sur deux lignes. D'où 11,5px + `white-space:nowrap` — le décompte est
       court et fixe par nature (« 26 messages · 10k words »), c'est LUI qui doit
       céder la place, pas la phrase qui porte le sens. */
    .result-side > #summary > .convmeta{grid-area:3/2/4/3;margin:0;font-size:11.5px;
      justify-content:flex-end;gap:5px;min-width:0;white-space:nowrap}
    /* rangée 4 — les sujets · rangée 5 — la puce « ↓ Détails » */
    .result-side > #summary > #mt-banner{grid-area:4/1/5/3;margin:0}
    .result-side > #summary > .metrics-fold{grid-area:5/1/6/3;margin:0;padding-top:7px}
    /* la jauge : mêmes bornes, moins de rembourrage */
    .result-side > #summary > .gauge .gauge-ends{margin-top:4px;font-size:9px}
  }
  /* Le repliable : il reste DANS le flux et s'anime en hauteur — on n'enlève
     plus le nœud, on le referme. */
  .summary-details{overflow:hidden;max-height:1500px;opacity:1;
    transition:max-height .3s ease,opacity .22s ease}
  #summary.is-condensed .summary-details{max-height:0;opacity:0;pointer-events:none}
  @media(prefers-reduced-motion:reduce){.summary-details{transition:none}}
  /* CONDENSÉ = plus discret, pas plus vide. Les sujets restent lisibles, en plus petit. */
  /* LES SUJETS DANS LA CARTE : densité RÉDUITE, et surtout CONSTANTE.
     ⚠️ Deux pièges déjà payés ici, dans cet ordre :
       1. `flex-basis:100%` sur .mt-bt forçait un retour à la ligne → l'état
          « réduit » mesurait 122px contre 118px déplié : plus GRAND que ce
          qu'il réduisait. Supprimé.
       2. Réduire les sujets SEULEMENT en condensé faisait varier la hauteur de
          la carte de 30px entre les deux états (mesuré) — donc la colonne, donc
          le handoff. « Réduit » redevenait « ça bouge ».
     D'où ce choix, assumé : dans la carte, les sujets gardent TOUJOURS la
     densité compacte (ils y sont un rappel, pas le sujet principal). Réduits par
     rapport au bandeau pleine largeur d'origine, identiques d'un état à l'autre
     → delta de mise en page nul. Et jamais masqués : ni hidden, ni display:none. */
  /* LOT2 · POINT 3 — un cran plus compact ENCORE dans la carte (12→10,5px), et
     surtout : le PLAFOND DUR est côté JS (MT_CAP=3 + « +N autres »), pas ici.
     C'est ce plafond qui garantit la hauteur proportionnée quel que soit le
     nombre de sujets trouvés — un plafond CSS (max-height/overflow) aurait
     masqué des chips sans le dire, ce que le fondateur refuse explicitement. */
  /* COMPACT-SNAPSHOT (18/07) — un cran de densité de plus DANS la carte (le
     bandeau y pesait 87,5px mesurés sur 297,3, soit 29% de la box à lui seul).
     On ne retire AUCUN sujet et on ne masque rien : le plafond dur reste
     MT_CAP=3 + « +N autres » côté JS (validé fondateur), on ne touche qu'au
     rembourrage, aux gouttières et à la chasse. Le bandeau PLEINE LARGEUR
     (hors carte) garde ses valeurs d'origine — ces règles sont scopées #summary. */
  #summary .mt-banner{margin:8px 0 0;padding:7px 10px;gap:5px 8px}
  #summary .mt-banner .mt-bt{font-size:12.5px;flex:1 1 140px}
  #summary .mt-banner .mt-sp{font-size:15px}
  #summary .mt-banner .mt-topic{font-size:10px;padding:2px 6px}
  #summary .mt-banner .mt-more{font-size:10px;padding:2px 3px}
  /* MERGE 18/07 — RATTRAPAGE DE CIBLE TACTILE. Ces trois règles sont scopées
     `#summary` (1 ID + 2 classes) : elles BATTENT le bloc ≥44px de la doctrine SR
     plus haut, qui n'est qu'une classe. Tant que les sujets étaient des <span>
     inertes c'était sans conséquence ; depuis EXTRACT ce sont de VRAIS boutons,
     et une cible de 10px/2px dans une carte serait sous le seuil. On rétablit
     donc le plancher ICI, à la même spécificité, sans rendre la carte moins dense
     au pointeur fin (desktop 1280 ne matche pas ce bloc). */
  @media(pointer:coarse){
    #summary .mt-banner .mt-topic{min-height:44px;padding:4px 10px;font-size:12px}
    #summary .mt-banner .mt-more{min-height:44px;padding:4px 8px;font-size:11.5px}
  }
  /* Sur écran ÉTROIT, le bandeau compact (#compact-band) reprend déjà titre +
     jauge + message : les répéter dans la carte condensée ferait doublon. On les
     escamote DONC ici, et SEULEMENT ici — ce qui reste de #summary, c'est
     exactement les SUJETS. Avant ce fix, foldMobile() cachait toute la carte et
     les sujets partaient avec (mesuré : offsetParent null, rect 0×0). */
  /* SR-04·A — la puce « ↓ Détails » mesurait 313×30 au doigt (mesuré à 375px) :
     sous la cible de 44px. C'est LE geste de cet écran, il doit être atteignable. */
  @media(pointer:coarse){
    .metrics-fold{min-height:44px}
  }
  @media(max-width:900px){
    body.is-mobile #summary.is-condensed{margin-top:0;padding-top:0;padding-bottom:0;border-top:none}
    body.is-mobile #summary.is-condensed > .convtitle,
    body.is-mobile #summary.is-condensed > .convmeta,
    body.is-mobile #summary.is-condensed > .gauge,
    body.is-mobile #summary.is-condensed > .gauge-label{display:none}
  }
  @media(max-width:560px){
    textarea.prompt{max-height:190px}
  }

  /* ===== ITINÉRAIRE POST-COPIE (levier 1) — révélé après Copier, mobile + desktop ===== */
  .post-copy{margin:14px 18px 0;background:var(--success-soft);border:1px solid var(--success-border);border-radius:var(--r-md);padding:13px 15px;counter-reset:pc;animation:readyPop .32s ease}
  .pc-lead{display:flex;align-items:center;gap:8px;margin:0 0 11px;font-size:13.5px;font-weight:700;color:var(--success)}
  .pc-check{flex:none;width:20px;height:20px;border-radius:50%;background:var(--success);color:#fff;display:grid;place-items:center}
  .pc-check .i{width:12px;height:12px;stroke-width:3}
  .pc-steps{list-style:none;margin:0;padding:0;display:flex;flex-wrap:wrap;gap:9px 18px}
  .pc-step{display:flex;align-items:center;gap:9px;font-size:13.5px;font-weight:500;color:var(--ink)}
  .pc-num{counter-increment:pc;flex:none;width:22px;height:22px;border-radius:50%;background:var(--surface);border:1.5px solid var(--success-border);color:var(--success);font-size:12px;font-weight:700;display:grid;place-items:center}
  .pc-num::before{content:counter(pc)}
  /* Ligne d'agnosticité de sortie : rappel discret que le livrable est collable
     dans n'importe quelle IA. Sous les étapes, séparée par un filet léger. */
  .pc-agnostic{margin:11px 0 0;padding-top:10px;border-top:1px solid var(--success-border);font-size:13px;font-weight:500;color:var(--ink)}
  @media(prefers-reduced-motion:reduce){.post-copy{animation:none}}
  @media(max-width:560px){.post-copy{margin:12px 12px 0}.pc-steps{flex-direction:column;gap:10px}}
  .metrics-fold{display:block;margin:10px auto 0;background:none;border:none;cursor:pointer;
    font:inherit;font-size:13px;font-weight:600;color:var(--ink3);padding:4px 8px;letter-spacing:-.01em}
  /* CONTRASTE — l'état SURVOL du bouton « Détails » (texte, 13px/600). Le survol ne
     change que la couleur, pas le fond : celui-ci reste --bg2 #FAF8F7, porté par
     l'ancêtre opaque .convcard. 3,44:1 en --accent → 5,43:1 en --accent-active.
     Le repos est en --ink3 (4,73:1) et n'est pas touché. */
  .metrics-fold:hover{color:var(--accent-active)}
  /* QA-MOBILE-REELLE (16/07) — « ↓ Détails » vit DANS la carte signaux, en pied,
     plus en orphelin entre deux cartes. Deux hôtes selon le viewport :
       · desktop → dernier enfant de #summary (.convcard) : séparateur fin + pleine
         largeur de carte, l'affordance est explicite (la carte dit « à bout de
         souffle », le pied de CETTE carte propose de déplier) ;
       · mobile  → dernier enfant de #compact-band, qui est une ligne flex
         (coche | titre | mini-jauge) : d'où flex-wrap + flex-basis:100% sur le
         bouton, sinon il se collerait en 4e colonne à droite de la mini-jauge
         au lieu de passer en pied de bandeau. */
  #summary > .metrics-fold{width:100%;margin:12px 0 0;padding-top:10px;
    border-top:1px solid var(--divider);border-radius:0}
  .compact-band{flex-wrap:wrap}
  .compact-band > .metrics-fold{flex-basis:100%;width:100%;margin:10px 0 0;padding-top:9px;
    border-top:1px solid var(--border)}
  /* ALLÈGEMENT MOBILE (décision fondateur 08/07 4e tour, validé sur maquette
     site/_result-mobile-mockup.html) : bandeau compact (coche+titre+mini-jauge)
     qui REMPLACE la carte de métriques complète dès le récap sur mobile — plus
     d'attente 9s. Diagnostic ayant motivé le fix : la carte seule pesait
     ≈565px sur ~650px utiles (Safari iPhone) = 87% du 1er écran. */
  .compact-band{display:none;align-items:center;gap:10px;padding:14px 16px;
    background:var(--bg2);border:1px solid var(--border);border-radius:var(--r-lg)}
  body.is-mobile .compact-band.on{display:flex}
  .compact-band .cb-check{flex:none;width:26px;height:26px;border-radius:50%;
    background:var(--success-soft);color:var(--success);display:grid;place-items:center}
  .compact-band .cb-check svg{width:15px;height:15px;stroke-width:2.6}
  .compact-band .cb-mid{flex:1;min-width:0}
  .compact-band .cb-title{margin:0;font-family:var(--font-display);font-weight:600;
    font-size:14px;letter-spacing:-.01em;color:var(--ink);white-space:nowrap;
    overflow:hidden;text-overflow:ellipsis}
  .compact-band .cb-sub{margin:1px 0 0;font-size:11.5px;color:var(--ink2);
    white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .compact-band .cb-gauge{flex:none;width:40px}
  .compact-band .cb-gauge .gauge-track{position:relative;height:6px;border-radius:4px;
    background:linear-gradient(90deg,#16845B,#6DA82B,#C08A1A,#E06A1E,#FF2D55)}
  .compact-band .cb-gauge .gauge-cursor{position:absolute;top:50%;left:10%;width:10px;
    height:10px;border-radius:50%;background:#fff;border:2px solid var(--ink);
    transform:translate(-50%,-50%)}
  /* CTA COPIER STICKY MOBILE (proposition B, même maquette) : tant que le
     handoff est prêt et non copié, un bouton "Copier" reste fixé en bas
     d'écran — visible sans le moindre scroll, quel que soit l'endroit où on
     se trouve dans la page. Disparaît après clic. */
  .sticky-copy{display:none}
  body.is-mobile .sticky-copy.on{display:block;position:fixed;left:0;right:0;bottom:0;z-index:30;
    padding:10px 16px max(10px,env(safe-area-inset-bottom));
    background:linear-gradient(to top,var(--bg) 65%,transparent)}
  /* retour fondateur 08/07 (validé, seul ajustement demandé) : texte un peu
     petit — taille explicite plutôt que l'héritage sans size-modifier
     (.btn seul n'a pas de font-size propre). Proportions/padding inchangés. */
  .sticky-copy button{width:100%;height:52px;font-size:17px}
  /* bug re-test 08/07 : le × flottait mal en sibling flex de l'input (pensé
     pour l'ancien .inputcard en ligne) — overlay absolu ancré dans le champ
     lui-même, robuste que .inputcard soit en ligne ou en colonne. */
  .link-shell{position:relative}
  .link-shell input{padding-right:38px}
  .clr-btn{position:absolute;top:50%;right:8px;transform:translateY(-50%);flex:none;
    width:26px;height:26px;border-radius:50%;border:1px solid var(--border);
    background:var(--bg2);color:var(--ink3);font-size:16px;line-height:1;cursor:pointer;padding:0}
  .clr-btn:hover{color:var(--ink);border-color:var(--accent)}
  .clr-strip{display:flex;flex-wrap:wrap;align-items:center;gap:10px;margin:10px auto 0;padding:12px 16px;
    background:var(--accent-soft);border:1px solid var(--accent);border-radius:var(--r-md);
    font-size:13.5px;color:var(--ink2);text-align:left}
  .clr-strip .clr-q{flex:1;min-width:200px;font-weight:600}
  .clr-strip button{font:inherit;font-size:13px;font-weight:600;padding:7px 12px;border-radius:var(--r-md);
    cursor:pointer;border:1px solid var(--border);background:var(--surface);color:var(--ink)}
  .clr-strip .clr-mail{border-color:var(--accent);color:var(--accent-active)}
  /* FONDATION OVERLAY (#0, 18/07) — la bande de reprise n'est plus seulement une
     bannière de reload : depuis le ✕, « Resume » est LE seul chemin de retour vers
     le travail en cours. Elle devient donc une cible primaire → 44px au doigt.
     MESURÉ AVANT (375) : 31,5px de haut. Gaté sur `pointer:coarse` : le rendu
     souris (desktop, et 375 en émulation navigateur) est strictement inchangé. */
  @media(pointer:coarse){
    .clr-strip button{min-height:44px;padding:10px 16px}
  }
  .g413{margin:18px 0 0;padding:14px 16px;background:#FBF4E1;border:1px solid #F2D38A;border-radius:var(--r-md);text-align:left}
  .g413-head{font-weight:700;font-size:14px;margin:0 0 6px;color:var(--ink)}
  .g413 p{font-size:13.5px;color:var(--ink2);line-height:1.55;margin:0 0 10px}
  /* ⚠️ SR-24 / D1 — CAPACITÉ, PAS LARGEUR. Le texte 413 route sur l'APPAREIL
     (`is-device-*`), pas sur `is-mobile` (devenu la largeur avec SR-24) : le mobile
     est invité à « reprendre sur son ordinateur » et reçoit un pont email
     (`bridge:isMobileDevice`). Gater sur la largeur ferait promettre ce pont à un
     desktop en fenêtre étroite, sans que la charge suive. */
  body.is-device-desktop .g413-mobile{display:none}
  body.is-device-mobile .g413-desktop{display:none}
  .g413-form{display:flex;gap:8px;flex-wrap:wrap}
  .g413-form input{flex:1;min-width:180px;padding:8px 12px;font:inherit;font-size:14px;color:var(--ink);
    background:var(--surface);border:1px solid var(--border);border-radius:var(--r-md)}
  .g413-form input:focus{outline:none;border-color:var(--accent)}
  .g413 .g413-note{font-size:12px;color:var(--ink3);margin:8px 0 0}
  .g413 .g413-ok{font-weight:600;color:var(--ink2);margin:0}
  @media(prefers-reduced-motion:reduce){.fb-btn{transition:none}}

  /* ===== suggestion de langue redesignée — toast (app.js langSuggest, approche B) =====
     Non intrusif (aucun layout shift sur le hero), fermable, refus en localStorage.
     Redesign V1.3 : titre qui VEND l'offre dans la langue cible + 1 phrase + 2 choix
     nets (aller / rester), au lieu d'un lien nu. Bas-droite, plafonné 380px.
     z-index 25 < consent-bar (60) : si coexistence, le bandeau légal prime. */
  .lang-toast{position:fixed;right:12px;bottom:12px;left:auto;z-index:25;max-width:380px;width:calc(100% - 24px);
    display:flex;align-items:flex-start;gap:12px;background:var(--surface);border:1px solid var(--border);
    border-left:3px solid var(--accent);border-radius:var(--r-lg);box-shadow:var(--sh-cardh);padding:14px 14px 14px 15px;
    animation:handoffIn .3s ease}
  .lang-toast .tg{flex:none;width:34px;height:34px;border-radius:50%;background:var(--accent-soft);display:grid;place-items:center;color:var(--accent-active)}
  .lang-toast .tg svg{width:18px;height:18px}
  .lang-toast .tbody{flex:1;min-width:0}
  .lang-toast .tt{font-family:var(--font-display);font-weight:600;font-size:14.5px;letter-spacing:-.01em;color:var(--ink);margin:1px 0 3px;line-height:1.25}
  .lang-toast .ts{font-size:13px;color:var(--ink2);margin:0 0 11px;line-height:1.45}
  .lang-toast .tact{display:inline-flex;align-items:center;gap:6px;background:var(--accent);color:#fff;text-decoration:none;
    font-weight:600;font-size:13.5px;border-radius:var(--r-md);padding:8px 13px;box-shadow:var(--sh-accent)}
  .lang-toast .tact:hover{background:var(--accent-hover)}
  .lang-toast .tkeep{display:inline-block;margin-left:6px;color:var(--ink3);font-size:13px;font-weight:500;background:none;border:none;cursor:pointer;padding:8px 6px}
  .lang-toast .tkeep:hover{color:var(--ink)}
  .lang-toast .tx{position:absolute;top:9px;right:9px;width:26px;height:26px;border:none;background:transparent;color:var(--ink3);
    border-radius:var(--r-sm);cursor:pointer;font-size:14px;display:grid;place-items:center;line-height:1}
  .lang-toast .tx:hover{background:var(--bg2);color:var(--ink)}

  /* ===== sélecteur de langue permanent — pastille header + pied (V1.3) ===== */
  /* Composant partagé (build.js langSwitcher) : bouton pastille « 🌐 Autonyme ▾ »
     + menu déroulant des 5 langues. Rendu build-time (langue courante marquée
     server-side, zéro flash). app.js ne gère que l'ouverture/fermeture. */
  .langwrap{position:relative;display:inline-block}
  .langbtn{display:inline-flex;align-items:center;gap:8px;padding:8px 12px;border:1px solid var(--border);background:var(--surface);
    border-radius:999px;font-family:var(--font-sans);font-weight:600;font-size:14px;color:var(--ink);cursor:pointer;box-shadow:var(--sh-sm);transition:.14s ease;line-height:1}
  .langbtn:hover{border-color:var(--ink);background:var(--bg)}
  .langbtn .globe{width:16px;height:16px;color:var(--ink2);flex:none}
  .langbtn .flag{font-size:15px;line-height:1}
  .langbtn .chev{width:12px;height:12px;color:var(--ink3);transition:transform .16s ease}
  .langbtn[aria-expanded="true"]{border-color:var(--ink)}
  .langbtn[aria-expanded="true"] .chev{transform:rotate(180deg)}
  .langmenu{position:absolute;top:calc(100% + 8px);right:0;min-width:210px;background:var(--surface);border:1px solid var(--border);
    border-radius:var(--r-lg);box-shadow:var(--sh-cardh);padding:6px;z-index:40}
  .langmenu[hidden]{display:none}
  .langmenu .mh{font-size:10.5px;letter-spacing:.08em;text-transform:uppercase;color:var(--ink3);font-weight:600;padding:8px 10px 6px}
  .langmenu a{display:flex;align-items:center;gap:10px;padding:9px 10px;border-radius:var(--r-sm);text-decoration:none;color:var(--ink);font-size:14.5px;font-weight:500}
  .langmenu a .flag{font-size:16px;line-height:1;width:20px;text-align:center;flex:none}
  .langmenu a:hover{background:var(--bg2)}
  .langmenu a[aria-current="true"]{background:var(--accent-soft);color:var(--accent-active);font-weight:600}
  .langmenu a[aria-current="true"] .check{margin-left:auto;color:var(--accent);width:15px;height:15px}
  /* déclinaison sombre pour le pied + menu qui s'ouvre vers le HAUT (bas de page) */
  .langbtn.dark{background:rgba(255,255,255,.06);border-color:#2c2825;color:#fff}
  .langbtn.dark:hover{background:rgba(255,255,255,.12);border-color:#4a4440}
  .langbtn.dark .globe{color:#c9c2bc}
  .langbtn.dark .chev{color:#8a837d}
  .footlang{display:flex;flex-wrap:wrap;gap:8px 14px}
  .footlang .langmenu{top:auto;bottom:calc(100% + 8px);left:0;right:auto}
  /* Mobile : loger la pastille validée dans la nav. On retire le bouton CTA
     redondant de la barre (le grand CTA du hero est juste dessous) — cf. la
     maquette validée dont la nav mobile = logo + pastille. La pastille s'ancre
     à droite. ≤560px seulement ; au-dessus, CTA + pastille cohabitent. */
  @media(max-width:560px){
    .nav{gap:8px}
    .nav .links{gap:10px;flex:none}
    .nav .links > .btn{display:none}
    .nav .langwrap{margin-left:auto}
    /* pastille langue mobile : cible tactile ≥44px + présence nette (site 5
       langues, trafic majoritairement mobile — le sélecteur doit sauter aux yeux). */
    .nav .langbtn{min-height:44px;padding:0 14px;font-size:15px;box-shadow:var(--sh-card)}
    .nav .langbtn .flag{font-size:17px}
    .nav .langbtn .globe{width:17px;height:17px}
    /* ⚠️ LARGEUR DE LA PASTILLE = LONGUEUR DU LIBELLÉ → la nav débordait dans les 5
       langues. MESURÉ à 320px : budget réel entre le burger et le bord de nav =
       104,8px, pour une pastille de 156,9px (English) à 179,1px (Português) —
       +34,1px en EN, +41,4px en DE, +56,3px en PT. Le portugais, libellé le plus
       long du catalogue, était le SEUL encore cassé à 375px (la largeur mobile la
       plus courante). `.nav .links{flex:none}` (ci-dessus) empêchant tout
       rétrécissement, le dépassement sortait de l'écran au lieu d'être absorbé.
       On retire le LIBELLÉ, pas le contrôle : globe + drapeau + chevron restent,
       et la cible tactile garde ses 44px. La pastille tombe à 96px DANS LES CINQ
       LANGUES — elle ne dépend plus du libellé, donc plus aucune langue ne peut la
       faire déborder à nouveau. Le nom reste porté par `aria-label` (lecteurs
       d'écran) et par le sélecteur de PIED DE PAGE, qui garde le libellé complet. */
    .nav .langbtn .langbtn-name{display:none}
    /* POURQUOI PAS `flex:0 1 auto` SUR `.links` — la piste « rendre `.links`
       rétractable » a été essayée et écartée sur mesure : `.links` se serait bien
       contracté à 158,8px, mais il aurait alors comprimé `.langwrap` puis
       `.langbtn` (tous deux `flex:0 1 auto`) sur ~104,8px, pour un contenu de 156,9
       à 179,1px. Le débordement n'aurait pas disparu : il serait passé du bord de
       l'écran à l'intérieur de la pastille, en écrasant globe/drapeau/libellé.
       `flex:none` n'est pas la cause, c'est ce qui rend la cause VISIBLE — la cause
       est une pastille dont la largeur suit le libellé. On traite la largeur.
       (`min-width:0` sur `.links` serait par ailleurs inopérant sous `flex:none` :
       sans facteur de rétrécissement, il ne s'applique jamais.) */
    .nav .langmenu{min-width:220px}
    .nav .langmenu a{padding:12px 12px;font-size:15.5px}
  }

  /* ===== bandeau de consentement (consent.js — spec Emil §4) ===== */
  .consent-bar{position:fixed;left:12px;right:12px;bottom:12px;z-index:60;max-width:720px;margin:0 auto;
    background:var(--surface);border:1px solid var(--border);border-radius:var(--r-lg);box-shadow:var(--sh-card);
    padding:16px 18px;font-size:13.5px;color:var(--ink2)}
  .consent-main{display:flex;align-items:center;gap:16px;flex-wrap:wrap}
  .consent-txt{flex:1;min-width:220px;line-height:1.5}
  .consent-txt b{color:var(--ink)}
  .consent-txt a{color:var(--accent-active);text-decoration:none;font-weight:600}
  .consent-txt a:hover{text-decoration:underline}
  .consent-actions{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
  .consent-btn{height:40px;padding:0 18px;border-radius:var(--r-md);font:inherit;font-weight:600;font-size:13.5px;cursor:pointer;border:1px solid transparent;white-space:nowrap}
  .consent-reject{background:var(--surface);color:var(--ink);border-color:var(--border)}
  .consent-reject:hover{background:var(--bg2);border-color:var(--ink)}
  .consent-accept{background:var(--accent);color:#fff;box-shadow:var(--sh-accent)}
  .consent-accept:hover{background:var(--accent-hover)}
  /* ⚠️ CONTRASTE — « Personnaliser » du bandeau de consentement. En --ink3 #938D88
     sur --surface #FFFFFF il mesurait 3,28:1, sous les 4,5:1 de SC 1.4.3 (13px).
     Il passe en --ink2 #625E5A = 6,43:1.
     ET IL FAUT LE DIRE NET : la consigne reçue proposait --accent-active, « même
     cause, même correctif » que la pastille phare. La cause N'EST PAS la même — ici
     le fautif est un GRIS, pas le rouge de marque — et le correctif rouge serait
     nuisible : « Personnaliser » est le chemin qui mène au refus granulaire, face à
     un « Tout accepter » déjà en aplat rouge. Le peindre en rouge lui donnerait le
     poids visuel du bouton d'acceptation et brouillerait une hiérarchie que le RGPD
     demande justement de tenir claire. --ink2 corrige le seuil sans toucher au rang. */
  /* « Personnaliser » — ÉGALITÉ DES DEUX CHEMINS (arbitrage Emil, CNIL/CEPD).
     MESURÉ AVANT : ce contrôle faisait 16px de haut / 73,1×16 = 1 169 px² de cible,
     face aux 38px / 133×38 = 5 054 px² de « Tout accepter » — 2,4× plus court et
     4,3× plus petit en surface, sur les 184 occurrences (24 pages × 5 langues).
     Refuser finement était donc matériellement plus difficile qu'accepter en bloc :
     motif trompeur susceptible de vicier le consentement.
     Le bouton était DÉJÀ un <button> (pas un <a>) : le clavier et le focus
     fonctionnaient — seule la géométrie était en cause. On lui donne donc la
     géométrie exacte de `.consent-btn` + la peau de `.consent-reject`.
     ⚠️ « Même poids » ≠ « même style que Tout accepter » : repeindre ce chemin en
     --accent le mettrait en rouge à côté d'un « Tout accepter » rouge (hiérarchie
     de consentement brouillée), et --accent #FF2D55 n'est de toute façon jamais une
     couleur de texte (3,65:1). On reprend --ink #171716 sur --surface = 17,94:1,
     le couple déjà validé sur « Tout refuser » — au lieu de --ink2 6,43:1. */
  .consent-link{height:40px;padding:0 18px;border-radius:var(--r-md);font:inherit;font-weight:600;font-size:13.5px;
    cursor:pointer;background:var(--surface);color:var(--ink);border:1px solid var(--border);white-space:nowrap}
  .consent-link:hover{background:var(--bg2);border-color:var(--ink)}
  .consent-cats{margin-top:14px;padding-top:14px;border-top:1px solid var(--divider);display:flex;flex-direction:column;gap:10px}
  .consent-cat{display:flex;align-items:flex-start;justify-content:space-between;gap:14px}
  .consent-cat > span:first-child{display:flex;flex-direction:column;gap:2px}
  .consent-cat b{color:var(--ink);font-size:13.5px}
  .consent-cat small{color:var(--ink3);font-size:12px;line-height:1.45}
  .consent-cat input[type=checkbox]{width:20px;height:20px;flex:none;margin-top:2px;accent-color:var(--accent);cursor:pointer}
  .consent-always{color:var(--success);font-size:12px;font-weight:600;flex:none;margin-top:2px}
  /* Compactage mobile du bandeau (option A validée fondateur 15/07) : ~15-20px de
     hauteur gagnés (padding resserré, texte 12.5px, boutons 38px) pour libérer le bas
     de l'écran au 1er chargement. AUCUN texte / catégorie / bouton retiré → zéro impact
     légal (validé sans Emil). */
  @media(max-width:560px){
    .consent-bar{left:8px;right:8px;bottom:8px;padding:12px 13px;font-size:12.5px}
    .consent-txt{line-height:1.45}
    .consent-main{align-items:stretch;gap:11px}
    .consent-actions{width:100%}
    .consent-reject,.consent-accept{flex:1;height:38px}
    /* « Personnaliser » passait déjà en 2e rangée par `flex-wrap` (Tout refuser +
       Tout accepter saturent la 1re : 133+133+10 = 276 pour 278 de contenu à 320px).
       Il y reste, mais en pleine largeur et à la MÊME hauteur que les deux autres —
       `flex:1 0 100%` force le retour et occupe la rangée. Coût réel : +22px de
       bandeau (38 au lieu de 16), ce que le compactage mobile du 15/07 peut payer ;
       l'égalité des chemins de consentement n'est pas négociable, elle. */
    .consent-link{flex:1 0 100%;height:38px}
  }

  /* ── Sortie humaine : brief humain (point d'entrée + tunnel + destinations) ──
     Refonte 15/07 (mockups _note-refonte/_note-button/_note-wall/_brief-tunnel,
     4/4 validés par Kevin) :
     (a) .note-inline = entrée visible sous .copybar, carte élevée (option C
     retenue, palette secondaire --accent2 ambre-terracotta pour sortir du
     "tout est rouge") ; (b) revealPostCopy() relocalise CE MÊME élément après
     .post-copy (.is-nudge = relance, pas un doublon) ; (c) le clic ouvre un
     TUNNEL PLEIN ÉCRAN à 2 étapes (email → brief) au lieu d'empiler un mur puis
     un bloc de résultat inline dans la page — .note-actions-primary/-secondary
     (Copier pleine largeur + grille 3 icônes) INCHANGÉS, déjà validés, migrent
     tels quels à l'intérieur de l'étape 2 du tunnel. */
  /* CHANTIER-ALLÉGEMENT (16/07) : .note-inline vit désormais dans Zone C
     (.result-side > #zone-c), plus sous .copybar — la box grossit et empile
     icône+titre / formats / CTA (elle a toute la largeur de la colonne latérale).
     ⚠️ QA-MOBILE-REELLE (16/07) : c'était `flex-direction:column`, ce qui
     contredisait le commentaire d'origine (« flex-wrap plutôt que flex-row ») et
     mettait l'ICÔNE SEULE sur sa ligne, titre+sous-titre dessous — à toutes les
     largeurs, desktop compris. C'est la régression du fix déjà demandé le 15/07
     (« titre/sous-titre À CÔTÉ de l'icône ») : il avait été posé sur .note-inline
     en @media 560 puis perdu quand la box a migré dans .zone-c.
     Correctif : on RESTE en ligne (row) + flex-wrap. Icône (flex:none) et corps
     (flex:1 1 0%) tiennent la 1re ligne ; formats et CTA passent en dessous via
     flex-basis:100%. Pas de flex-direction:column — c'est lui qui isolait l'icône. */
  .zone-c .note-inline{flex-wrap:wrap;align-items:center;margin:0;padding:20px 20px 18px}
  .zone-c .note-inline-body{flex:1 1 0%;min-width:0}
  .zone-c .note-inline-cta{flex-basis:100%;width:100%;justify-content:center;margin-top:14px;padding:11px 16px}
  .note-inline{display:flex;align-items:center;gap:9px;margin:14px 18px 0;padding:14px 16px;
    background:var(--surface);border:1px solid var(--border);border-radius:var(--r-lg);
    box-shadow:var(--sh-card),0 0 0 1px rgba(255,45,85,.05);
    transform:translateY(-1px);transition:transform .18s ease,box-shadow .18s ease}
  /* SR-04·A — même maladie que les .btn : au doigt, la carte restait SOULEVÉE
     après le tap. Le lift n'a de sens qu'avec un curseur qui survole. */
  @media(hover:hover){
    .note-inline:hover{transform:translateY(-3px);box-shadow:var(--sh-cardh),0 0 0 1px rgba(255,45,85,.07)}
  }

  /* ══ SR-20·D — LA CARTE PREMIUM SE DÉTACHE (17/07, reco du sous-agent) ════════
     Constat Kevin : « elle ressemble à la carte handoff — même blanc, même rayon,
     même ombre. Rien ne dit que c'est autre chose. » Elle était VISIBLE (SR-06·B)
     mais pas DISTINCTE.
     La reco compose DEUX leviers orthogonaux, sans une seule règle nouvelle :
     · LA MATIÈRE (--aside) : la carte cesse d'être une carte blanche posée sur du
       blanc. Fond teinté, plus de bordure, plus d'ombre — elle n'est plus du même
       MATÉRIAU que le handoff. C'est la piste que Kevin n'avait pas demandée, et
       c'est la plus forte : on ne l'a pas décorée, on a changé sa substance.
     · LE NOM (--tag) : une étiquette qui CHEVAUCHE le bord haut (grammaire de la
       <legend> d'un <fieldset>). Une carte dont l'étiquette mord le contour n'est
       plus lue comme la même ESPÈCE d'objet. Et surtout elle NOMME la zone au lieu
       de la peindre : « POUR UN HUMAIN » dit exactement ce que le handoff n'est pas.
     Le liseré (piste initiale de Kevin) n'est PAS repris : sur fond teinté sans
     bordure, un rail de 3px redevient une décoration — il peindrait au lieu de dire.
     Elle reste SECONDAIRE : aucun rouge --accent, le CTA garde --accent2.
     ⚠️ NOTE v4 — CE CHOIX RESTE VALIDE, SA JUSTIFICATION A CHANGÉ. Tant que
     --accent2 était RÉSERVÉ à Human Brief, l'orange était ici quasi obligatoire :
     c'était « la couleur du brief ». La réservation est levée (voir le bloc
     « ORANGE LIBÉRÉ » sur la déclaration du jeton) : l'orange est désormais un
     accent de marque libre, employé ailleurs sans rapport avec le brief.
     Ce que la boîte garde donc, c'est le rang SECONDAIRE — « pas un CTA principal,
     pas de rouge --accent » — et non un étiquetage « orange = Human Brief ». Ne pas
     déduire de cette règle que l'orange appartient au brief ; c'est l'inverse qui
     est vrai depuis la v4. ══════════════════════════════════════════════════════ */
  .zone-c .note-inline{position:relative;background:var(--accent2-soft);border-color:transparent;
    box-shadow:none;transform:none;margin-top:8px;padding-top:26px}
  @media(hover:hover){
    .zone-c .note-inline:hover{transform:none;box-shadow:inset 0 0 0 1px var(--accent2-shadow)}
  }
  .zone-c .note-inline .note-inline-ic{background:var(--surface)}
  .zone-c .note-inline .note-pile-d{border-color:var(--accent2-soft)} /* la pile garde son chevauchement sur le nouveau fond */
  /* ══ SR-03 « LE SPÉCIMEN » — l'aperçu dans la carte (GO Kevin) ═══════════════
     La carte MONTRE ce qu'est un brief au lieu de le promettre — le principe qui a
     fait gagner la pile, appliqué au niveau au-dessus. Il comble le vide AVEC DU
     SENS, pas avec du padding : mon constat était « étirer une carte trop maigre
     ne donne pas du poids, ça donne du vide ».
     🔒 Contrainte dure : ne jamais laisser croire que le brief est généré. Des
     BARRES, zéro texte réel, une légende qui dit « UN brief ». Inclinaison + fondu
     de coupe = « spécimen », pas « livrable » — le fondu EST l'argument, il empêche
     la vignette de se lire comme un document entier. AUCUNE animation : le shimmer
     est la grammaire du CHARGEMENT, il dirait « votre brief s'écrit ».
     ⚠️ DESKTOP UNIQUEMENT, et c'est mesuré : le vide qu'il comble est causé par
     l'étirement `flex:1` qui n'existe QU'À ≥901px (l.905). Sur mobile la carte a sa
     hauteur naturelle — aucun vide à combler — et la hauteur y est précieuse :
     SR-06·B vient de faire remonter la carte au-dessus du pli, validé sur l'iPhone
     de Kevin. Ajouter ~130px de vignette là annulerait ce gain. On ne paie pas un
     problème desktop avec les pixels du mobile. ══════════════════════════════ */
  .note-prev{display:none} /* mobile : rien — voir ci-dessus */
  @media(min-width:901px){
    .zone-c .note-prev{display:block;width:100%;margin:14px 0 0;box-sizing:border-box}
    .note-spec{position:relative;border:1px solid var(--border);border-radius:var(--r-sm);
      background:var(--surface);box-shadow:0 6px 16px rgba(23,23,22,.09);
      padding:11px 12px 0;transform:rotate(-1.4deg);transform-origin:left center;
      max-height:132px;overflow:hidden}
    .note-spec::after{content:"";position:absolute;left:0;right:0;bottom:0;height:40px;
      background:linear-gradient(rgba(255,255,255,0),var(--surface));pointer-events:none}
    .note-spec-bar{display:flex;align-items:center;gap:5px;margin:0 0 9px}
    .note-spec-dot{flex:none;width:5px;height:5px;border-radius:50%;background:var(--accent2)}
    /* PT6 (Kevin / SR-03 « le spécimen ») : DÉCIDÉ / À CONFIRMER redeviennent LISIBLES
       (des labels de STRUCTURE générique, pas le contenu réel du brief — la contrainte
       « zéro texte réel » visait le contenu, pas les en-têtes). Le reste = barres grises. */
    .note-spec-t{flex:none;font:600 9.5px/1 var(--font-sans);letter-spacing:.02em;color:var(--ink3)}
    .note-spec-s + .note-spec-s{margin-top:9px}
    .note-spec-h{display:flex;align-items:center;gap:5px;margin:0 0 6px}
    .note-spec-d{flex:none;width:6px;height:6px;border-radius:50%}
    .note-spec-s--ok .note-spec-d{background:var(--success,#2e9e5b)}
    .note-spec-s--tbc .note-spec-d{background:var(--accent2)}
    .note-spec-n{font:700 9px/1 var(--font-sans);text-transform:uppercase;letter-spacing:.06em}
    .note-spec-s--ok .note-spec-n{color:var(--success,#2e9e5b)}
    .note-spec-s--tbc .note-spec-n{color:#8a4a20}
    .note-prev-l{height:5px;border-radius:3px;background:var(--border);margin-top:5px}
    .note-prev-cap{margin:9px 0 0;font:400 11px/1.45 var(--font-sans);color:var(--ink3)}
  }
  .note-inline-tag{position:absolute;top:-8px;left:16px;display:inline-flex;align-items:center;
    font:700 9.5px/1 var(--font-sans);letter-spacing:.1em;text-transform:uppercase;
    color:#fff;background:var(--accent2);border-radius:999px;padding:5px 10px;
    box-shadow:0 3px 8px var(--accent2-shadow);white-space:nowrap}
  .note-inline.is-nudge{animation:handoffIn .35s ease .1s backwards}
  .note-inline-ic{flex:none;width:22px;height:22px;color:var(--accent-active);background:var(--accent-soft);
    border-radius:50%;padding:5px;box-sizing:content-box;animation:noteIcBreathe 2.6s ease-in-out infinite}
  .note-inline-body{flex:1;min-width:0}
  .note-inline-t{margin:0;font-size:13px;font-weight:600;color:var(--ink);letter-spacing:-.005em}
  .note-inline-s{margin:1px 0 0;font-size:12px;color:var(--ink2);line-height:1.4}
  /* SR-04·A — ce CTA n'est PAS un `.btn` (c'est un <button class="note-inline-cta">),
     donc il n'héritait NI de la transition NI de rien : il changeait de couleur
     d'un coup sec au survol, et restait collé au doigt. Le seul CTA du chemin
     premium était le moins réactif du site. On lui donne la même grammaire que
     les .btn — sans en faire un .btn (il garde --accent2, son rang secondaire). */
  .note-inline-cta{flex:none;display:inline-flex;align-items:center;gap:5px;font:600 13px var(--font-sans);
    color:#fff;background:var(--accent2);border:none;border-radius:var(--r-sm);cursor:pointer;
    padding:9px 14px;white-space:nowrap;box-shadow:0 10px 24px var(--accent2-shadow);
    transition:background .14s ease,transform .14s ease;-webkit-tap-highlight-color:transparent}
  @media(hover:hover){
    .note-inline-cta:hover{background:var(--accent2-hover);color:#fff;transform:translateY(-1px)}
  }
  .note-inline-cta:active{background:var(--accent2-hover);transform:translateY(1px) scale(.988);transition-duration:90ms}
  .note-inline-cta:focus-visible{outline:none;box-shadow:0 0 0 3px var(--accent2-soft),0 10px 24px var(--accent2-shadow)}
  @media(prefers-reduced-motion:reduce){.note-inline-cta:active{transform:none}}
  .note-inline-cta svg{width:14px;height:14px;flex:none}
  .note-inline-cta:disabled{opacity:.6;cursor:default}
  @keyframes noteIcBreathe{0%,100%{box-shadow:0 0 0 0 var(--accent-soft)}50%{box-shadow:0 0 0 5px var(--accent-soft)}}
  @media(prefers-reduced-motion:reduce){.note-inline-ic{animation:none}.note-inline{transition:none}}

  /* ── « Voilà ce que vous obtenez » — DIRECTION B « LA PILE » (GO fondateur
     16/07, validé sur site/_share-formats-mockup.html contre 2 autres directions).
     Remplace la 1re version (pictos gris alignés) : verdict fondateur « c'est pas
     désirable, c'est sage […] Pas Uncook style » — elle informait sans donner envie.

     LEVIER : grammaire d'affichage empruntée au FACEPILE (pile d'avatars). Des
     pastilles rondes QUI SE CHEVAUCHENT ne sont lues par personne comme 4 boutons
     — le chevauchement rend le tap physiquement ambigu, donc l'œil sait avant même
     d'essayer que c'est de l'AFFICHAGE, pas de l'action. C'est ce qui permet
     d'encaisser le VRAI vert WhatsApp à pleine saturation sans fabriquer une
     barre d'outils : la contrainte « montré, jamais cliquable » est payée par la
     forme elle-même, au lieu d'être combattue.

     ⚠️ CONTRAINTE DURE — MONTRÉ, JAMAIS CLIQUABLE : aucun :hover, aucun
     cursor:pointer, aucune bordure-bouton, aucun <a>/<button> dans le markup
     (renderNoteFormats). Un seul CTA (.note-inline-cta) ouvre le tunnel. Raison
     fondateur : cliquer « WhatsApp » et tomber sur un mur email piégerait
     l'utilisateur — la promesse est visible, le chemin reste unique et honnête.
     Ne JAMAIS rendre ces pastilles interactives « puisqu'elles ont l'air de ».

     Couleurs tierces en variables LOCALES à .note-outs — jamais dans :root : ce
     ne sont pas des tokens Uncook, ce sont les couleurs de marques tierces. */
  .note-outs{width:100%;margin:12px 0 0;box-sizing:border-box;
    --c-copy:#6B6560; --c-mail:var(--accent2); --c-wa:#25D366; --c-pdf:#D8232A}
  .note-outs--pile{display:flex;align-items:center;gap:11px;flex-wrap:wrap}
  .note-pile{display:flex;flex:none;padding-left:2px}
  .note-pile-d{box-sizing:border-box;width:30px;height:30px;border-radius:50%;
    display:grid;place-items:center;margin-left:-8px;color:#fff;
    border:2px solid var(--surface);box-shadow:0 2px 5px rgba(23,23,22,.13)}
  .note-pile-d:first-child{margin-left:0}
  .note-pile-d svg{width:14px;height:14px}
  .note-pile-d--copy{background:var(--c-copy)}
  .note-pile-d--mail{background:var(--c-mail)}
  .note-pile-d--wa{background:var(--c-wa)}
  .note-pile-d--pdf{background:var(--c-pdf)}
  .note-outs-cap{flex:1;min-width:0;margin:0;font:400 11.5px/1.45 var(--font-sans);color:var(--ink2)}
  .note-outs-cap b{font-weight:600;color:var(--ink)}

  /* ── Tunnel du brief humain (plein écran, 2 étapes) — remplace l'ancien
     empilement inline .note-wall→.note-block. Même famille que .modal-backdrop
     (gate.js) mais dimensionné plein écran plutôt que carte centrée à 880px. */
  .brief-tunnel-backdrop{position:fixed;inset:0;z-index:65;background:var(--bg);
    display:flex;flex-direction:column;animation:btFadeIn .28s ease}
  @keyframes btFadeIn{from{opacity:0}to{opacity:1}}
  .bt-topbar{flex:none;display:flex;align-items:center;justify-content:space-between;padding:18px 20px 6px}
  .bt-steps{display:inline-flex;align-items:center;gap:2px;background:var(--surface);border:1px solid var(--border);
    border-radius:999px;padding:5px;box-shadow:var(--sh-sm)}
  .bt-step{display:inline-flex;align-items:center;gap:6px;padding:6px 13px;border-radius:999px;
    font:600 12px var(--font-sans);color:var(--ink3);transition:background .2s,color .2s}
  .bt-step-dot{flex:none;width:7px;height:7px;border-radius:50%;background:var(--border)}
  .bt-step.is-current{background:var(--accent-soft);color:var(--accent-active)}
  .bt-step.is-current .bt-step-dot{background:var(--accent);box-shadow:0 0 0 3px var(--accent-ring)}
  .bt-step.is-done{color:var(--ink2)}
  .bt-step.is-done .bt-step-dot{background:var(--success)}
  .bt-close{flex:none;width:32px;height:32px;border:1px solid var(--border);background:var(--surface);
    border-radius:var(--r-sm);color:var(--ink2);font-size:15px;cursor:pointer;display:grid;place-items:center}
  .bt-close:hover{background:var(--bg2);color:var(--ink)}
  /* ⚠️ SR-15 (16/07) — NE JAMAIS REMETTRE `justify-content:center` ICI.
     `justify-content:center` + `overflow:auto` est un piège CSS classique : quand
     le contenu déborde, le débordement par le bord de DÉBUT (le haut) n'est JAMAIS
     scrollable — le navigateur clampe `scrollTop` à 0. Le haut du contenu est donc
     peint hors du conteneur et INATTEIGNABLE : ni scroll, ni molette, ni clavier.
     Mesuré sur le rendu réel (scrollTop reste 0 dans tous les cas) :
       640px de haut → OK · 600 → déborde mais rien de coupé
       560 → 14px inatteignables · 520 → 34px · 480 → 54px · 400 → 94px
     Qui tombe sous 560px : le TÉLÉPHONE EN PAYSAGE (iPhone 13 couché = ~390px de
     viewport → 94px du titre inatteignables), les petites fenêtres, le split-screen.
     Pas un cas exotique : il suffit de tourner son téléphone sur le mur premium.
     LE FIX : centrage par MARGES AUTO. `margin:auto` centre exactement comme
     justify-content quand ça tient, MAIS laisse le débordement scrollable dans les
     deux sens quand ça ne tient pas. C'est la seule façon de centrer un conteneur
     scrollable sans perdre du contenu.
     `:not(.is-brief)` : l'étape 2 (le brief) est déjà en justify-content:flex-start
     + align-items:stretch — les marges auto la casseraient. */
  .bt-stage{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:flex-start;
    padding:8px 34px 40px;overflow:auto;text-align:center}
  /* ⚠️ SR-01 (17/07) — MON PROPRE FIX SR-15 CAUSAIT CE DÉFAUT.
     SR-15 avait remplacé `justify-content:center` (qui rendait le haut du contenu
     INATTEIGNABLE au scroll) par un centrage via marges auto : ça centrait quand
     ça tenait ET laissait scroller quand ça débordait. Correct — mais le centrage
     reste un centrage : sur un écran haut, le titre part au MILIEU, et le tunnel
     « s'ouvre sur du rien » (constat Kevin, titre à ~700px du haut).
     On ancre donc EN HAUT. Ça satisfait les deux exigences à la fois :
       · le titre + le champ email sont dans le viewport À L'OUVERTURE, sans scroll
         (demande Kevin) ;
       · rien n'est jamais inatteignable, en paysage comme ailleurs (l'objectif de
         SR-15) — `justify-content:flex-start` + `overflow:auto` scrolle vers le
         BAS, jamais vers un haut coupé.
     Le centrage vertical était une coquetterie ; l'accès immédiat est un besoin. */
  /* ══ SR-02·B « L'ÉTABLI » (17/07, arbitré Kevin) ═════════════════════════════
     Constat du fondateur : « la fenêtre qui présente le brief est là comme si elle
     n'était pas là » — le brief EST le livrable, il devrait être l'objet principal
     de l'écran. Diagnostic du sous-agent, et il est plus juste que « il manque de
     la place » : le vrai coupable est `max-width:640px`. À 640px, TOUTE explication
     ajoutée se paie AVEC le livrable — le brief rétrécit à mesure qu'on l'explique.
     L'établi rend l'explication GRATUITE en vertical : les actions et le contexte
     passent dans un rail latéral, le brief récupère la largeur. Mesuré par l'agent :
     190 → 300px de colonne de lecture.
     Sous 900px, l'établi se replie exactement sur la direction A (une colonne) —
     ce n'est pas deux designs, c'est le même qui respire. ══════════════════════ */
  .bt-stage.is-brief{align-items:stretch;text-align:left;justify-content:flex-start;padding:6px 30px 30px;
    max-width:640px;margin:0 auto;width:100%;box-sizing:border-box}
  @media(min-width:901px){
    /* CORRECTION #3 (Kevin) : le brief ne touche plus le bas — centré verticalement,
       équilibré (le cadre est plafonné + scroll interne → le stage ne déborde jamais,
       donc justify-content:center est sûr ici, contrairement au mur email). */
    .bt-stage.is-brief{max-width:1000px;justify-content:center}
    .bt-bench{display:flex;gap:24px;align-items:flex-start}
    .bt-bench-main{flex:1 1 0%;min-width:0}
    /* sticky : le rail suit la lecture d'un brief long au lieu de décrocher */
    .bt-bench-rail{flex:0 0 300px;position:sticky;top:12px}
    .bt-bench-rail .note-actions-primary{margin-top:0}
  }
  /* PT7 (Kevin) — le mur email « prenait pas assez de place » : titre riquiqui dans un
     overlay plein écran. On rend l'ensemble MASSIF (titre bien plus grand, œil-de-bœuf
     + lead + champ email à l'échelle). Ancrage haut conservé (SR-01, jamais center). */
  .bt-eyebrow{font:700 12px var(--font-sans);letter-spacing:.08em;text-transform:uppercase;color:var(--accent-active);margin:0 0 16px}
  .bt-h1{font-family:var(--font-display);font-weight:700;font-size:clamp(30px,4.2vw,44px);letter-spacing:-.035em;margin:0 0 14px;max-width:16ch;line-height:1.08}
  .bt-lead{color:var(--ink2);font-size:15.5px;line-height:1.6;margin:0 0 30px;max-width:42ch}
  /* CORRECTION #2 (Kevin) — largeur : le bouton Google (GIS) est plafonné à 400px par
     Google ; on aligne donc le champ email sur 400 pour qu'ils fassent la MÊME largeur.
     (Le bouton vide = origine uncook.app non déclarée en Cloud Console — config Kevin,
     pas du code ; le fix largeur ne se voit qu'une fois l'origine ajoutée.) */
  .bt-stage:not(.is-brief) .sub-form{max-width:400px;width:100%}
  .bt-stage:not(.is-brief) .sub-google,.bt-stage:not(.is-brief) .sub-or{max-width:400px;width:100%;margin-left:auto;margin-right:auto}
  .bt-stage:not(.is-brief) .sub-email{min-height:54px;font-size:16px}
  .bt-stage:not(.is-brief) .sub-btn{min-height:54px;font-size:15.5px}
  .bt-stage .sub-form,.bt-stage .sub-cases,.bt-stage .sub-google,.bt-stage .sub-or{margin-left:auto;margin-right:auto}
  /* ⚠️ §4 PARCOURS-REEL (16/07) — LE font-size EST OBLIGATOIRE ICI, NE PAS LE RETIRER.
     Retour fondateur : « la ligne One-click unsubscribe est trop grosse par rapport
     au reste ». Ce n'était PAS une question de goût : `.nw-note` n'avait AUCUNE
     règle font-size, nulle part → elle retombait sur le 16px par défaut du
     navigateur, pendant que `.bt-lead` (le message principal) est à 14px. La
     mention de réassurance était donc littéralement le PLUS GROS texte courant de
     l'écran (1,14× le message qu'elle accompagne) — la hiérarchie exactement à
     l'envers, sur un écran de confiance. Un défaut par omission : personne n'a
     écrit « rends la mention légale énorme », il a suffi de ne rien écrire du tout. */
  .bt-stage .nw-note{margin:16px auto 0;max-width:40ch;font-size:12px;line-height:1.5}
  .bt-orb{width:42px;height:42px;border-radius:50%;background:var(--accent);margin:0 auto 18px;animation:orb 1.8s ease-in-out infinite}
  .bt-loading-lead{font-family:var(--font-display);font-weight:700;font-size:21px;letter-spacing:-.02em;margin:0 0 8px}
  /* POINT 4 — la carte de rédaction du brief EST une `.loading` (même composant que
     l'uncooking, cf. renderTunnelLoading) ; il ne reste ici qu'à la caper et la
     centrer dans le stage, exactement comme l'overlay le fait pour #loading. */
  .bt-loading-card{width:100%;max-width:560px;margin:0 auto;box-sizing:border-box;text-align:center}
  @media(min-width:901px){.bt-stage.is-writing{justify-content:center}}
  .bt-loading-sub{color:var(--ink2);font-size:13px;max-width:34ch;margin:18px auto 0}
  .bt-brief-tag{display:inline-flex;align-items:center;gap:6px;font:700 10.5px var(--font-sans);letter-spacing:.06em;
    text-transform:uppercase;color:var(--success);background:var(--success-soft);border:1px solid var(--success-border);
    border-radius:999px;padding:4px 10px;margin:0 0 12px;align-self:flex-start}
  .bt-brief-title{font-family:var(--font-display);font-weight:700;font-size:26px;letter-spacing:-.03em;
    margin:0 0 16px;color:var(--ink)}
  /* ══ SR-19 — LE BRIEF EST UN DOCUMENT, PLUS UN CHAMP (17/07, décidé Kevin) ═══
     Il restait un <textarea> : bordure, bague de focus, poignée de resize — donc
     un champ de saisie, à zéro hiérarchie. Le titre disait « un brief à lire, pas
     à recoller dans une IA » et la FORME disait l'inverse. Un textarea est
     plain-text par nature : il ne PEUT pas porter de titres. D'où le vrai DOM.
     On retire les 3 affordances de champ ; le brief devient l'objet de l'écran. */
  .bt-brief-text{width:100%;box-sizing:border-box;min-height:190px;max-height:300px;overflow:auto;
    padding:22px 24px;background:var(--surface);border:1px solid var(--border);border-radius:var(--r-md);
    font:16px/1.72 var(--font-brief);letter-spacing:.002em;color:#2b2925;white-space:pre-wrap}
  /* PT6 (Kevin) — CADRE REFAIT : un vrai contenant de document avec un LABEL en haut à
     gauche (bandeau d'en-tête), le corps composé (titres gras/serif) en dessous. Le
     cadre porte la bordure/l'ombre ; le corps ne porte plus que le texte. */
  /* POINT 5 (Kevin) — « le cadre manque d'épaisseur et de couleur… sobre, juste plus
     affirmé ». Mesuré avant : 1px #E9E4E1 (--border) + --sh-sm, soit le trait le plus
     léger de la charte pour l'objet le plus important de l'écran. Après : 2px et une
     teinte plus présente (#DCD3CC, taupe chaud — un cran plus dense que --border, pris
     dans la même famille chaude, PAS un gris froid) + --sh-card pour que le document
     se pose vraiment. On reste sous le trait des états actifs (--accent) : le cadre
     s'affirme, il ne crie pas. */
  /* LOT2 · POINT 8 (Kevin) — « encore un peu plus épais ». 2px → 3px, même teinte
     (#DCD3CC). Le cadre est l'objet le plus important de la page brief ; à 3px il
     se lit comme un DOCUMENT encadré et non comme une carte de plus. On reste sur
     un trait neutre — c'est l'épaisseur qui parle, pas la couleur. */
  .bt-brief-frame{display:flex;flex-direction:column;border:3px solid #DCD3CC;
    background:var(--surface);border-radius:var(--r-lg);box-shadow:var(--sh-card);overflow:hidden}
  .bt-brief-framehead{flex:none;display:flex;align-items:center;padding:10px 18px;
    border-bottom:1px solid var(--divider);background:var(--bg2)}
  .bt-brief-framelabel{font:700 10px/1 var(--font-sans);letter-spacing:.11em;
    text-transform:uppercase;color:var(--ink2)}
  .bt-brief-doc{white-space:normal;max-height:none;overflow:visible;
    border:none;background:transparent;border-radius:0;box-shadow:none;padding:22px 24px}
  /* PT5 (Kevin) — desktop : 0 scroll de PAGE, le corps du cadre scrolle EN INTERNE.
     Plafond de hauteur → overflow interne. (Mobile : rien — flux naturel, SR-15 sauf :
     jamais d'overflow:hidden qui rend le titre du mur inatteignable en paysage.) */
  @media(min-width:901px){
    .bt-brief-doc{max-height:calc(100vh - 340px);overflow-y:auto} /* CORRECTION #3 : cadre plus court (ne descend plus jusqu'en bas) */
  }
  /* Les en-têtes — le CUMUL demandé par Kevin : le traitement structurel (œil
     composé, filet, espacement) ET le gras par-dessus. Ils doivent « frapper ».
     Sans-serif sur les titres / serif sur le corps : le contraste dit « ceci est
     une charpente, cela est de la prose ». */
  .bf-h{margin:26px 0 8px;font:700 11.5px/1.3 var(--font-sans);letter-spacing:.1em;
    text-transform:uppercase;color:var(--ink);display:flex;align-items:center;gap:10px}
  .bf-h::after{content:"";flex:1;height:1px;background:var(--divider)}
  .bf-h:first-child{margin-top:0}
  .bf-p{margin:0 0 12px;font:16px/1.72 var(--font-brief);color:#2b2925}
  .bf-ul{margin:0 0 14px;padding:0;list-style:none}
  .bf-ul li{position:relative;padding-left:18px;margin:0 0 7px;font:16px/1.68 var(--font-brief);color:#2b2925}
  .bf-ul li::before{content:"";position:absolute;left:3px;top:.62em;width:5px;height:5px;border-radius:50%;
    background:var(--accent2)}
  .bt-brief-sub{margin:-8px 0 18px;font:14px/1.5 var(--font-sans);color:var(--ink2);max-width:52ch}

  /* ══ SR-17·B « ET APRÈS ? » — la sortie du brief (17/07, arbitré Kevin) ═══════
     La page était un CUL-DE-SAC : aucune sortie hors « fermer ». Constat mesuré
     par le sous-agent, et il est pire que « pas engageants » : l'action UNIQUE
     était à ×0,75 de la duplicate — 25% PLUS PETITE. La hiérarchie n'était pas
     absente, elle était INVERSÉE, et c'est la LONGUEUR DE CHAÎNE qui l'avait
     décidé (13 caractères contre 22). Personne ne l'a choisi.
     POIDS INÉGAL, DÉCIDÉ PAR LE CODE : « Revenir » = closeTunnel(), déjà offert
     3 fois (×, backdrop, Échap). « Reprendre une autre conversation » = la SEULE
     porte vers un 2e usage, zéro affordance ailleurs. Une 4e copie d'une sortie
     ne peut pas primer sur l'unique chemin vers une deuxième utilisation.
     Tuile teintée et NON pastille pleine : les pastilles pleines sont la famille
     des DESTINATIONS (marques tierces, cf. .note-pile). Une sortie n'est pas une
     destination — un disque en ferait un 4e destinataire. Même famille, rang
     différent. Cibles tactiles ≥44px (elles étaient à 27px). ══════════════════ */
  .bt-next{margin:22px 0 0;padding:16px 0 0;border-top:1px solid var(--divider)}
  /* LOT2 · POINT 7 (corollaire de lisibilité, MESURÉ) — « WHAT NOW ? » est le SEUL
     texte de la page brief qui ne repose pas sur une carte opaque : il est posé
     directement sur le fond de l'overlay, donc désormais sur le dégradé ambiant.
     Mesuré (composite pire cas, blob rgba(255,45,85,.34) × opacity .26) :
     --ink3 tombait de 3,23:1 à 2,87:1 — sous le plancher de 3:1. Il était DÉJÀ
     sous AA avant le lot (3,23 < 4,5) ; on ne laisse pas le dégradé l'aggraver.
     --ink2 le remonte à 5,62:1 sur ce même pire cas, donc au-dessus d'AA. Seul
     ce libellé change : c'est le seul mesuré en dessous du plancher. */
  .bt-next-h{margin:0 0 10px;font:700 11px var(--font-sans);letter-spacing:.09em;
    text-transform:uppercase;color:var(--ink2)}
  .bt-next-go{display:flex;align-items:center;gap:12px;width:100%;min-height:44px;
    padding:13px 14px;text-align:left;cursor:pointer;border:1px solid var(--accent2-soft);
    border-left:3px solid var(--accent2);border-radius:var(--r-md);background:var(--accent2-soft);
    transition:background .14s ease,transform .14s ease,border-color .14s ease;
    -webkit-tap-highlight-color:transparent}
  @media(hover:hover){.bt-next-go:hover{background:#FFE7D6;border-color:var(--accent2);transform:translateY(-1px)}}
  .bt-next-go:active{transform:translateY(1px) scale(.994);transition-duration:90ms}
  .bt-next-go:focus-visible{outline:none;box-shadow:0 0 0 3px var(--accent2-shadow)}
  .bt-next-go-ic{flex:none;width:30px;height:30px;border-radius:50%;display:grid;place-items:center;
    background:var(--accent2);color:#fff}
  .bt-next-go-ic svg{width:15px;height:15px}
  .bt-next-go-b{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}
  .bt-next-go-t{font:600 14px var(--font-sans);color:var(--ink)}
  .bt-next-go-s{font:12.5px/1.4 var(--font-sans);color:var(--ink2)}
  .bt-next-go-ar{flex:none;width:15px;height:15px;color:var(--accent2)}
  /* La sortie discrète : un lien, pas un bouton — c'est sa 4e occurrence. */
  /* PT9 (Kevin) — « Back to my result » n'est plus du texte nu : flèche + cadre (pilule). */
  .bt-next-back{display:flex;align-items:center;justify-content:center;gap:7px;width:fit-content;min-height:40px;margin:8px auto 0;padding:9px 18px;
    background:var(--surface);border:1px solid var(--border);border-radius:999px;cursor:pointer;font:600 13px var(--font-sans);color:var(--ink2);
    transition:color .14s ease,border-color .14s ease,background .14s ease;-webkit-tap-highlight-color:transparent}
  .bt-next-back svg{width:15px;height:15px;flex:none}
  @media(hover:hover){.bt-next-back:hover{color:var(--ink);border-color:var(--ink2);background:var(--bg2)}}
  .bt-next-back:focus-visible{outline:none;color:var(--ink);border-color:var(--ink2);box-shadow:0 0 0 3px var(--accent-ring)}
  @media(prefers-reduced-motion:reduce){.bt-next-go:active,.bt-next-go:hover{transform:none}}
  .bt-brief-foot{margin-top:16px}
  .bt-brief-foot .note-actions-primary{margin:0 0 8px}
  body.tunnel-open{overflow:hidden}
  /* PT5 (Kevin) — DESKTOP UNIQUEMENT : verrouille le scroll de PAGE quand le brief est
     ouvert (la racine de scroll est <html>, pas <body> → body:overflow:hidden ne suffit
     pas). Le corps du cadre scrolle en interne (max-height + overflow:auto). ⚠️ JAMAIS
     sur mobile (SR-15 : overflow:hidden en paysage rend le titre inatteignable). */
  @media(min-width:901px){html.tunnel-open{overflow:hidden}}

  .note-actions-primary{display:flex;margin:12px 0 8px}
  .note-actions-primary .btn{width:100%;height:48px;font-size:14.5px;display:inline-flex;align-items:center;justify-content:center;gap:8px}
  .note-actions-primary svg{width:16px;height:16px;flex:none}
  .note-actions-secondary{display:grid;grid-template-columns:repeat(4,1fr);gap:8px}
  .note-act{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:6px;
    padding:12px 8px;background:var(--surface);border:1px solid var(--border);border-radius:var(--r-md);
    color:var(--ink);font:600 12.5px var(--font-sans);cursor:pointer;text-decoration:none;
    transition:border-color .14s,background .14s,transform .14s}
  .note-act:hover{border-color:var(--ink2);background:var(--bg2);transform:translateY(-1px)}
  /* PT8 (Kevin) — sur la page du brief les boutons SONT cliquables : la couleur est
     LÉGITIME ici (l'inverse exact de la contrainte de la pile). Couleurs de marque AU
     REPOS (maquette « L'établi ») : email --accent2, WhatsApp #25D366, PDF #D8232A. */
  .note-act-ic{width:19px;height:19px;color:var(--ink2)}
  .note-act.mail .note-act-ic{color:var(--accent2)}
  .note-act.wa .note-act-ic{color:#25D366}
  .note-act.pdf .note-act-ic{color:#D8232A}
  .note-deliver{width:100%;margin:10px 0 0}
  #note-email{margin:14px 0 0}
  .note-sent{margin:10px 0 0;font-size:12.5px;line-height:1.45;color:var(--ink2);display:flex;align-items:center;gap:6px}
  .note-sent::before{content:"✓";color:var(--accent);font-weight:700}
  @media(max-width:560px){
    /* ⚠️ AUTO-ZOOM iOS — RÈGLE DURE, NE PAS DESCENDRE SOUS 16px.
       Safari iOS zoome AUTOMATIQUEMENT le viewport au focus de TOUT contrôle de
       formulaire (input, textarea, select) dont le font-size est < 16px. Sur un
       overlay position:fixed;inset:0 (tunnel, mur gate.js), le panneau ne suit PAS
       le visual viewport quand Safari zoome → il devient plus large que l'écran
       zoomé → bord droit coupé net. C'est un débordement qu'AUCUN viewport simulé
       (375px dans un moteur desktop) ne peut reproduire — d'où l'iPhone de Kevin
       qui le voit et pas notre QA.
       16px n'est PAS un choix typographique ici : c'est le seuil exact qui désarme
       le zoom. Tout passage sous 16px ré-ouvre le bug.

       ⚠️⚠️ CORRECTIF DU CORRECTIF (16/07, même journée) : la V1 de cette règle
       ÉNUMÉRAIT les sélecteurs (`.sub-email`, `input[type=email]`) — et ratait donc
       le <textarea id="note-text"> du brief, à 14,5px, dans le MÊME overlay, sur le
       MÊME chemin premium : taper dedans sur iPhone rouvrait exactement le bug que
       cette règle prétendait tuer. iOS auto-zoome les textarea aussi.
       LA LEÇON, la même que `--sub-w` scopé sur `.sub-block` : une énumération
       corrige l'INSTANCE, un sélecteur large corrige la CLASSE. On cible donc
       TOUT contrôle de formulaire des overlays — pas une liste qu'il faudra
       penser à rallonger au prochain champ ajouté (personne n'y pensera). */
    .brief-tunnel-backdrop input,.brief-tunnel-backdrop textarea,.brief-tunnel-backdrop select,
    .uck-gate-modal input,.uck-gate-modal textarea,.uck-gate-modal select,
    .note-wall input,.note-wall textarea,.sub-email{font-size:16px}
    .note-inline{margin:12px 12px 0;flex-wrap:wrap;padding:13px 14px}
    .note-inline-body{flex:1 1 0%;min-width:0} /* basis 0%, PAS auto : avec un flex-wrap actif, un basis "auto" prend la largeur max-content du texte pour la décision de retour à la ligne (min-width:0 seul ne suffit pas) — vérifié en QA réelle 375px, l'icône et le texte finissaient sur 2 lignes distinctes avec "auto" */
    .note-inline-cta{flex-basis:100%;justify-content:center;margin-top:6px;padding:9px 4px}
    .note-actions-secondary{gap:7px}
    .note-act{padding:10px 4px;font-size:11.5px}
    .bt-topbar{padding:16px 16px 4px}
    .bt-step{font-size:11px;padding:5px 10px}
    .bt-close{width:28px;height:28px}
    .bt-stage{padding:8px 22px 30px}
    .bt-stage.is-brief{padding:6px 18px 22px}
    .bt-h1{font-size:22px}
    .bt-lead{font-size:13px}
    .bt-brief-title{font-size:20px}
    /* SR-19 : le brief garde 16px sur mobile (spec Kevin « 16px/1.72 »). Il était
       rabaissé à 14,5px pour la densité — mais c'est le LIVRABLE, pas de la chrome.
       Bonus : ce n'est plus un contrôle de formulaire (<article>), donc l'auto-zoom
       iOS de SR-13 ne peut plus s'y déclencher — le 16px est ici un choix de
       lisibilité, plus une contrainte. */
    .bt-brief-text{padding:16px}
    .bf-p,.bf-ul li{font-size:15.5px;line-height:1.66}
    .bf-h{margin-top:22px}
    .bt-brief-sub{font-size:13px}
  }
  /* ⚠️ SR-19 — LE PDF IMPRIMAIT UNE PAGE BLANCHE, ET C'ÉTAIT MA FAUTE.
     Cette règle masque TOUT (`body *{visibility:hidden}`) puis ne révèle que
     `#note-block`… un élément QUI N'EXISTE PLUS : mon propre refactor du tunnel
     l'a remplacé par `.bt-stage`/`#note-text`, et j'ai laissé le sélecteur pointer
     dans le vide. Résultat : le bouton PDF — une des 4 destinations du chemin
     premium — ouvrait une impression VIDE. Aucune erreur, aucun warning : un
     sélecteur qui ne matche rien est parfaitement légal en CSS. 8e occurrence du
     fil rouge, et la seule que j'ai écrite moi-même.
     On cible désormais l'objet réel, et on masque la chrome (barre d'étapes,
     actions) qui n'a aucun sens sur papier. */
  @media print{
    body *{visibility:hidden}
    .bt-stage.is-brief,.bt-stage.is-brief *{visibility:visible}
    .bt-stage.is-brief{position:absolute;left:0;top:0;width:100%;max-width:none;padding:0;overflow:visible}
    .note-inline,.brief-tunnel-backdrop .bt-topbar,.bt-brief-foot,
    .note-deliver,#note-email,.note-sent{display:none!important}
    .bt-brief-text{border:none;min-height:auto;max-height:none;overflow:visible;padding:0;background:none}
    .bf-h::after{display:none} /* le filet ne sert à rien à l'impression */
  }

  /* ══════════════════════════════════════════════════════════════════════════════
     MÉGA-MENU DE HEADER — DÉPLACÉ ICI DEPUIS templates/home.html (V2.0).
     Il vivait dans le <style> INLINE du template des homes : par construction, il
     n était donc servi QUE sur les 5 homes. /handoff/, /merge/, /migration/, /pro/
     et /everyday/ chargent toutes cette feuille — poser le composant ICI est ce qui
     le rend disponible partout SANS recopier une ligne de CSS dans 16 fichiers.
     C est exactement la dérive qui a fait disparaître le sélecteur de langue de
     /pro/ : un composant du template qui n atteint jamais les pages autonomes.
     Le markup, lui, est injecté par build.js (marqueurs HEADER_MENU / HEADER_BURGER
     / HEADER_SHEET) et le comportement par /header-menu.js. Un composant, trois
     fichiers, zéro copie.
     ⚠️ Les renvois « shared.css:NNN » dans les commentaires ci-dessous désignent
     désormais CE fichier (règles .container/.nav .links, plus haut).
     ══════════════════════════════════════════════════════════════════════════════ */
  /* ══ MÉGA-MENU « SORTIES » — LE HEADER DEVIENT UN CHOIX, PAS UN RENVOI ═════════
     Fondateur : « "Outputs" qui renvoie vers une PAGE où il faut re-cliquer = une
     étape pour rien, aucun sens sur un produit dont toute la promesse est "tu
     choisis ta sortie" ». L'ancre <a href="#outputs"> est donc remplacée par un
     vrai <button> qui ouvre un panneau SOUS le header : on choisit sa sortie
     depuis la barre, sans navigation ni défilement.

     ⚠️ ANCRAGE (C1, correctif Kevin) — le panneau est `position:absolute` et vit
     DANS `.omenu-wrap`, l'emballage posé juste autour du déclencheur (build.js,
     headerMenu). Deux raisons, les deux nécessaires :
       · ORDRE DE TABULATION : après le déclencheur, la tabulation entre dans le
         panneau. Posé ailleurs (après .nav), on tabulerait d'abord tous les autres
         liens — le panneau serait ouvert mais inatteignable au clavier.
       · BLOC CONTENEUR : c'est `.omenu-wrap` LUI-MÊME, à qui l'on donne
         `position:relative` (voir sa règle plus bas). Le panneau étant hors flux,
         l'emballage a EXACTEMENT la largeur du bouton : `top:calc(100% + 10px)`
         vaut 10px sous LE BOUTON, et `left:0;right:auto` aligne le bord GAUCHE du
         panneau sur le bord gauche du bouton « Tools ». Le panneau se déroule donc
         DIRECTEMENT sous le déclencheur.

         ⚠️ POURQUOI PAS `.nav` NI `.container` (l'ancrage précédent) : le panneau
         s'ancrait avant sur `.nav` en `right:0`, ce qui l'ouvrait tout à DROITE de
         la barre — à des centaines de px du bouton, qui vit à GAUCHE après le logo.
         C'est le bug remonté par Kevin. Ancrer sur `.container` était pire encore :
         sa hauteur n'est pas celle du header sur /pro/ et /everyday/ (le conteneur
         y enveloppe aussi le hero, ~757px), et `top:calc(100% + 10px)` déroulait le
         panneau en plein milieu de la page. L'emballage supprime les DEUX hypothèses
         d'un coup : sa géométrie ne dépend ni de la largeur de la nav ni de la
         hauteur d'un conteneur — seulement du bouton, qui est l'ancre voulue.
     ⚠️ Sorti du flux flex, le panneau ne consomme ni largeur ni `gap` de `.links`.
     ⚠️ `.nav .links a{font-size:14px;color:var(--ink2)}` (shared.css:175) frappe
     par héritage TOUT lien du panneau. Les règles ci-dessous sont donc écrites en
     `.nav .links .omenu-…` (0,3,0) pour passer devant (0,2,1) SANS toucher à la
     feuille partagée ni employer !important. */

  /* Le premier `.container` (celui de la nav) est remonté d'un cran : sans cela,
     le bandeau différenciants — `position:relative;z-index:1`, et POSTÉRIEUR dans
     le DOM — passerait AU-DESSUS du panneau déroulé (même plan z, le dernier
     gagne). Classe additive : `.container` n'est pas redéfinie. */
  .nav-shell{z-index:30}
  /* Le repère d'ancrage du panneau : LA BARRE, pas le conteneur (voir la note
     ci-dessus). Scopé sous `.nav-shell` pour ne rien changer aux headers qui ne
     portent pas le menu. Sans risque : le seul autre absolu de la barre est
     `.langmenu`, déjà ancré à `.langwrap{position:relative}` — il ne remonte donc
     jamais jusqu'ici. */
  .nav-shell .nav{position:relative}
  /* ⚠️ ANCRAGE DU PANNEAU (C1) — le bloc conteneur du `.omenu` absolu est CET
     emballage, posé autour du seul déclencheur (le panneau y est out-of-flow, donc
     l'emballage a EXACTEMENT la largeur du bouton). `position:relative` ici + le
     `left:0;top:100%` du panneau plus bas = le panneau se déroule sous le bord
     GAUCHE du bouton « Tools ». Avant, faute d'emballage, le panneau s'ancrait sur
     `.nav` en `right:0` et s'ouvrait tout à droite de la barre, à des centaines de
     px du bouton (bug remonté par Kevin). `inline-flex` : l'emballage se dimensionne
     sur le bouton et se comporte, dans le flex de `.links`, comme le faisait le
     bouton seul — même gouttière, même `flex:none`. */
  .nav .links .omenu-wrap{position:relative;display:inline-flex;align-items:center;flex:none}

  .nav .links .omenu-trigger{appearance:none;-webkit-appearance:none;background:none;
    border:0;padding:0;margin:0;cursor:pointer;font-family:var(--font-sans);
    font-size:14px;font-weight:500;line-height:1;color:var(--ink2);
    display:inline-flex;align-items:center;gap:5px;
    transition:color .16s ease}
  .nav .links .omenu-trigger .chev{width:13px;height:13px;flex:none;fill:none;
    stroke:currentColor;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round;
    color:var(--ink3);transition:transform .2s cubic-bezier(.2,.8,.3,1)}
  .nav .links .omenu-trigger[aria-expanded="true"]{color:var(--ink)}
  .nav .links .omenu-trigger[aria-expanded="true"] .chev{transform:rotate(180deg);color:var(--ink2)}
  .nav .links .omenu-trigger:focus-visible{outline:none;border-radius:var(--r-sm);
    box-shadow:0 0 0 3px var(--accent-ring)}
  /* Pastille du déclencheur — grille de quatre tuiles, « le catalogue ». Décorative
     (aria-hidden) : le mot « Products » porte seul le sens. En --ink3, un cran sous
     le libellé, pour ancrer sans peser. */
  .nav .links .omenu-trigger .omenu-lead{width:15px;height:15px;flex:none;fill:none;
    stroke:currentColor;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round;
    color:var(--ink3);transition:color .16s ease}
  .nav .links .omenu-trigger[aria-expanded="true"] .omenu-lead{color:var(--accent-active)}
  @media(hover:hover){.nav .links .omenu-trigger:hover .omenu-lead{color:var(--accent-active)}}

  /* ══ RACCOURCIS DE BARRE — Human Brief · Handoff · Migration ═══════════════════
     Les sorties phares, atteignables sans ouvrir le méga-menu. Écrits par
     navShortcuts() dans build.js, ENTRE les marqueurs HEADER_MENU (voir le
     commentaire là-bas : c'est ce qui les tient hors de la feuille mobile, qui
     liste déjà les cinq sorties).
     ⚠️ `.nav .links a{font-size:14px;font-weight:500;color:var(--ink2)}` frappe ces
     liens avec une spécificité (0,2,1) ; on passe donc par `.nav .links .nav-shortcut`
     (0,3,0) — même procédé que pour `.omenu-…`, et aucune règle existante n'est
     modifiée.
     MESURÉ : --ink #171716 sur --surface #FFFFFF = 16,95:1 (13,5px/600).
     Au survol : --accent-active #C9153A sur --accent-soft #FFF0F3 = 5,21:1. */
  /* ══ PLUS DE PASTILLE : ICÔNE + LIBELLÉ ROUGE, RIEN D'AUTRE ════════════════════
     Arbitrage fondateur v4 : « pas de fond, pas de bordure — juste du texte rouge
     cliquable, avec la petite icône de l'outil devant ».
     MESURÉ AVANT : fond --surface #FFFFFF plein, bordure 1px #E9E4E1, box-shadow
     --sh-sm, rayon 999px, padding 0 13px — un bouton-pastille. Tout est retiré ;
     après changement, computed = background rgba(0,0,0,0), border-width 0px,
     box-shadow none.

     ⚠️ LA COULEUR EST --accent-active #C9153A, JAMAIS --accent #FF2D55.
     Retirer le fond CHANGE LA QUESTION DU CONTRASTE : ce n'était pas un problème
     tant que le libellé était --ink #171716 sur le blanc de la pastille (16,95:1),
     ça en devient un dès que le texte se pose directement sur le fond de page.
     --accent #FF2D55 sur --bg #FFFDFC mesure 3,62:1 — SOUS le 4,5:1 qu'exige
     SC 1.4.3 AA pour du texte normal, et 13,5px est très loin des 24px de
     l'exemption grand texte. --accent-active #C9153A sur --bg #FFFDFC : 5,67:1 ✔
     (valeur mesurée au rendu, pas calculée sur le papier).
     Ne jamais rebasculer ces deux liens sur --accent : ce serait un échec AA.

     LA HAUTEUR DE 32px EST CONSERVÉE bien qu'il n'y ait plus ni fond ni padding
     vertical — c'est la cible de pointage, et elle seule. Sans elle le lien
     retomberait à la hauteur de sa ligne de texte (~17px) : la classe de défaut
     « bouton écrasé » qu'on a déjà vue deux fois sur ce site.

     SURVOL SANS FOND : --accent-soft n'est plus utilisable (il ferait réapparaître
     la pastille qu'on vient de retirer). Le survol passe donc par le SOULIGNEMENT
     — signal de lien standard, et qui ne touche pas au contraste. */
  .nav .links .nav-shortcut{display:inline-flex;align-items:center;gap:6px;flex:none;
    height:32px;padding:0;border:0;border-radius:0;white-space:nowrap;
    background:none;box-shadow:none;
    font-size:13.5px;font-weight:600;color:var(--accent-active);text-decoration:none;
    transition:color .16s ease}
  .nav .links .nav-shortcut-ico{display:inline-flex;align-items:center;justify-content:center;
    width:16px;height:16px;flex:none}
  .nav .links .nav-shortcut-ico svg{width:16px;height:16px;fill:none;stroke:currentColor;
    stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round}
  @media(hover:hover){
    .nav .links .nav-shortcut:hover{color:var(--accent-active);
      text-decoration:underline;text-underline-offset:3px}
  }
  .nav .links .nav-shortcut:focus-visible{outline:none;
    box-shadow:0 0 0 3px var(--accent-ring),var(--sh-sm)}
  /* Palier MESURÉ sur la home allemande, raccourcis forcés visibles : 960px tient
     (10px d'air sous le logo), 880px casse (scrollWidth 952 > clientWidth 880).
     On coupe à 1080px pour ne pas servir la barre « au ras » — le relevé complet
     est dans l'en-tête de version. Les deux sorties restent accessibles par le
     méga-menu et par la feuille mobile : un raccourci perdu, aucun accès perdu. */
  @media(max-width:1080px){.nav .links .nav-shortcut{display:none}}

  /* ⚠️ FOND CRÈME, JAMAIS BLANC PUR (charte). `--surface` vaut #FFFFFF : il est
     donc ÉCARTÉ ici au profit de `--bg` (#FFFDFC), même si toutes les cartes du
     site utilisent --surface. Le panneau est le seul élément posé DIRECTEMENT sur
     le fond de page : le crème s'y lit, et la charte prime sur l'uniformité. */
  .nav .links .omenu{position:absolute;top:calc(100% + 10px);left:0;right:auto;
    width:min(780px,calc(100vw - 56px));z-index:44;text-align:left;
    background:var(--bg);border:1px solid var(--border);border-radius:var(--r-xl);
    box-shadow:var(--sh-cardh);padding:14px}
  /* ⚠️ REPLI D'ANCRAGE SOUS 1080px (C1) — même palier que la disparition des
     raccourcis ci-dessus, et CE N'EST PAS une coïncidence : dès que les raccourcis
     tombent, la marge `margin-right:auto` qui les portait disparaît, la barre se
     repacke en `flex-end` et le bouton « Tools » GLISSE vers la droite. Ancré à
     GAUCHE du bouton, le panneau de 780px déroulerait alors hors de l'écran à droite
     (mesuré : ~160px de débordement à 1024/900/768, et une partie du menu hors de
     portée). Sous 1080px on rebascule donc sur l'ancrage `right:0` d'origine —
     jamais de débordement, panneau toujours entièrement atteignable. Au-dessus de
     1080px les raccourcis tiennent le bouton à gauche : `left:0` le place pile sous
     lui (le correctif voulu par Kevin aux largeurs bureau 1512/1920). */
  @media(max-width:1080px){.nav .links .omenu{left:auto;right:0}}
  .nav .links .omenu[hidden]{display:none}
  /* L'état de BASE est l'état VISIBLE (opacity/transform neutres). L'animation
     n'AJOUTE qu'une entrée : si elle est supprimée (mouvement réduit, ou keyframes
     non supportées), le panneau reste pleinement visible. Jamais de piège à
     opacity:0 en attente d'un déclencheur.
     ⚠️ L'ENTRÉE N'ANIME QUE `transform`. JAMAIS `opacity`. Mesuré sur ce chantier,
     en deux temps :
       1. avec `animation-…​ both`, le panneau reste épinglé sur l'image-clé `from` ;
       2. et RETIRER le fill-mode ne suffit PAS — l'horloge des animations est GELÉE
          quand `document.visibilityState` vaut "hidden" : relevé ici, `playState`
          "running" mais `currentTime` figé à 0, donc on reste À L'INTÉRIEUR de
          l'intervalle actif, où le fill-mode n'a aucune prise. Panneau ouvert,
          rect 780×168, aria-expanded="true"… et opacity 0. Invisible.
     La seule parade robuste n'est pas un réglage, c'est un CHOIX DE PROPRIÉTÉ :
     l'opacité a pour mode de défaillance la disparition totale, la translation a
     pour mode de défaillance 6px de décalage. On n'anime donc que la seconde.
     Horloge gelée, moteur ancien, keyframes ignorées : le pire cas reste un panneau
     parfaitement lisible, posé 6px trop haut. L'entrée décore, elle ne conditionne
     jamais la visibilité. */
  @keyframes omenu-in{from{transform:translateY(-6px)}to{transform:none}}
  .nav .links .omenu{animation:omenu-in .19s cubic-bezier(.2,.8,.3,1)}

  /* — STRUCTURE PRÉVUE POUR 5-6 SORTIES (Extract, Anonymize, Migration arrivent) —
     La colonne de gauche porte la SORTIE PHARE, seule et invariable. La colonne de
     droite est une grille `auto-fill` : ajouter une 4e, 5e ou 6e sortie = coller un
     `.omenu-row` de plus dans `.omenu-rest`, ZÉRO ligne de CSS à toucher — la
     grille passe d'elle-même en 2 colonnes × N lignes et le panneau grandit vers le
     bas. C'est ce qui évite la soupe : le phare ne se noie jamais dans le nombre,
     et les secondaires restent une liste régulière au lieu d'une rangée qui s'étire. */
  .nav .links .omenu-body{display:grid;grid-template-columns:minmax(215px,.85fr) 1.35fr;
    gap:14px;align-items:start}
  .nav .links .omenu-rest{display:grid;grid-template-columns:repeat(auto-fill,minmax(190px,1fr));
    gap:8px;align-content:start}

  /* — LA RANGÉE : icône Lucide (contour 1.5, currentColor, inline — la CSP bloque
       tout hôte tiers) + nom + une ligne de description. Aucun emoji. — */
  .nav .links .omenu-row{display:grid;grid-template-columns:auto 1fr;gap:12px;
    align-items:start;padding:11px 12px;border-radius:var(--r-lg);
    border:1px solid transparent;background:transparent;text-decoration:none;
    color:inherit;font-size:inherit;font-weight:400;
    transition:background .18s ease,border-color .18s ease,transform .18s cubic-bezier(.2,.8,.3,1)}
  .nav .links .omenu-ico{width:36px;height:36px;border-radius:10px;flex:none;
    display:inline-flex;align-items:center;justify-content:center;
    background:var(--bg2);border:1px solid var(--border);color:var(--accent);
    transition:background .18s ease,border-color .18s ease}
  .nav .links .omenu-ico svg{width:18px;height:18px;fill:none;stroke:currentColor;
    stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round}
  .nav .links .omenu-name{display:block;font-family:var(--font-brief);font-weight:600;
    font-size:15px;letter-spacing:-.015em;line-height:1.25;color:var(--ink)}
  .nav .links .omenu-desc{display:block;margin-top:3px;font-family:var(--font-sans);
    font-size:12.8px;font-weight:400;line-height:1.45;color:var(--ink2)}
  .nav .links .omenu-row:focus-visible{outline:none;border-color:var(--accent-soft);
    box-shadow:0 0 0 3px var(--accent-ring)}

  /* ══ HUMAN BRIEF = LE PHARE, DE FAÇON MESURABLE ════════════════════════════════
     Fondateur : « Human Brief doit être visiblement le produit phare — pas trois
     rangées identiques ». Six écarts cumulés et tous mesurables, aucun décoratif :
       1. colonne dédiée, à elle seule (les autres se partagent une grille) ;
       2. fond `--accent-soft` (#FFF0F3) contre transparent ;
       3. bordure teintée visible contre 1px transparent ;
       4. sur-titre en accent (« la plupart commencent ici ») que nul autre ne porte ;
       5. pastille d'icône plus grande (44 contre 36) et fond accent-soft ;
       6. nom plus grand (16.5 contre 15) + flèche d'appel en accent.
     ⚠️ Toujours AUCUN aplat de #FF2D55 : l'accent plein ne sert qu'au trait des
     icônes, au sur-titre et à la flèche (règle 90/10). Le fond phare est le rose
     SOFT, celui-là même que la charte autorise déjà pour le bandeau. */
  .nav .links .omenu-row--flag{background:var(--accent-soft);border-color:#FFD6DE;
    padding:16px 15px 15px;height:100%;align-content:start}
  .nav .links .omenu-row--flag .omenu-ico{width:44px;height:44px;border-radius:12px;
    background:var(--bg);border-color:#FFD6DE}
  .nav .links .omenu-row--flag .omenu-ico svg{width:22px;height:22px}
  .nav .links .omenu-row--flag .omenu-name{font-size:16.5px}
  .nav .links .omenu-row--flag .omenu-desc{font-size:13.2px;color:#7a5560}
  /* ⚠️ CONTRASTE — LES DEUX LIBELLÉS DE LA RANGÉE PHARE ÉTAIENT SOUS LE SEUIL.
     Ils vivent sur le fond --accent-soft #FFF0F3 de `.omenu-row--flag`. En --accent
     #FF2D55 ils mesuraient 3,30:1, là où SC 1.4.3 AA exige 4,5:1 pour du texte
     normal — et 10px comme 13px sont très en deçà des 24px de l'exemption grand
     texte. En --accent-active #C9153A ils mesurent 5,21:1 sur ce même fond.
     C'est exactement la correction déjà appliquée à `.oc-cta` et `.oc-flag` sur les
     cartes de la home : le menu ne l'avait pas reçue, alors qu'il montre LE MÊME
     libellé phare, par-dessus les cartes, à qui ouvre le panneau. */
  .nav .links .omenu-flagtag{display:block;margin:0 0 5px;font-family:var(--font-sans);
    font-size:10px;font-weight:700;letter-spacing:.12em;text-transform:uppercase;
    color:var(--accent-active)}
  .nav .links .omenu-go{display:inline-flex;align-items:center;gap:5px;margin-top:10px;
    font-family:var(--font-sans);font-size:13px;font-weight:600;color:var(--accent-active)}
  .nav .links .omenu-go svg{width:15px;height:15px;flex:none;fill:none;
    stroke:currentColor;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round;
    transition:transform .18s cubic-bezier(.2,.8,.3,1)}

  /* ══ LE SURVOL A LA MÊME AMPLEUR SUR LES CINQ RANGÉES ══════════════════════════
     Arbitrage fondateur v4 : « les autres entrées doivent grandir d'AUTANT que
     Human Brief au survol ».

     MESURÉ AVANT (1440px, panneau ouvert, hover réel vérifié par `:hover`) —
     et le constat contredit l'énoncé : AUCUNE rangée ne grandissait, phare comprise.
       Human Brief  284,36×192,23 → 284,36×192,23   transform: none → none
       Handoff      221,81×101,39 → 221,81×101,39   transform: none → none
       Merge        221,83×101,39 → 221,83×101,39   transform: none → none
       Extract      221,81× 82,84 → 221,81× 82,84   transform: none → none
       Migration    221,83× 82,84 → 221,83× 82,84   transform: none → none
     `.omenu-row` déclarait pourtant `transition:...,transform .18s` — une
     transition sur une propriété que RIEN ne changeait jamais. Du code mort.

     CE QUI DIVERGEAIT RÉELLEMENT, c'est l'ampleur de la réaction colorée, et elle
     était inversée : la rangée PHARE réagissait MOINS que les secondaires.
       · secondaires : fond transparent → #FAF8F7 (un aplat APPARAÎT), bordure
         transparente → #E9E4E1 (une bordure APPARAÎT), pastille #FAF8F7 → #FFF0F3
         (l'icône ROSIT) = trois changements visibles ;
       · phare : fond #FFF0F3 → #FFE7EC (nuance), bordure #FFD6DE → #FFF0F3 —
         elle s'ÉCLAIRCIT, donc le contour se DISSOUT au survol, à rebours de tout
         le reste — et pastille inchangée = un seul changement, et à contresens.
     C'est cet écart que l'œil lit comme « elles ne bougent pas pareil ».

     CORRECTION — deux volets, tous deux uniformisants :
       1. un `scale(1.015)` IDENTIQUE sur les cinq rangées : le « grandir » demandé
          existe enfin, et il est le même pour toutes. 1,015 est délibérément petit
          (≈3,3px sur une rangée de 221px, ≈4,3px sur le phare de 284px) : les
          gouttières valent 8px entre secondaires et 14px autour du phare, aucune
          rangée n'en recouvre donc une autre.
       2. la bordure du phare FONCE au survol (#EFBECB) au lieu de pâlir, et sa
          pastille reçoit le même traitement : le phare gagne en définition comme
          les autres, au lieu d'en perdre.

     ⚠️ Le `transform` reste neutralisé sous `prefers-reduced-motion` — voir le bloc
     dédié plus bas, où `.omenu-row` était déjà listé pour la transition et reçoit
     désormais aussi l'annulation du transform lui-même. */
  @media(hover:hover){
    .nav .links .omenu-trigger:hover{color:var(--ink)}
    .nav .links .omenu-row:hover{background:var(--bg2);border-color:var(--border);
      transform:scale(1.015)}
    .nav .links .omenu-row:hover .omenu-ico{background:var(--accent-soft);border-color:#FFD6DE}
    .nav .links .omenu-row--flag:hover{background:#FFE7EC;border-color:#EFBECB;
      transform:scale(1.015)}
    .nav .links .omenu-row--flag:hover .omenu-ico{background:var(--bg);border-color:#EFBECB}
    .nav .links .omenu-row--flag:hover .omenu-go svg{transform:translateX(3px)}
  }

  /* ══ BURGER + FEUILLE MOBILE ═══════════════════════════════════════════════════
     ~80% du trafic est mobile : ce n'est pas un livrable moindre. Sous 760px,
     shared.css:838 masque DÉJÀ les ancres de la nav (`.nav .links > a:not(:last-child)`)
     — il n'existait donc AUCUN accès à Sorties/Pourquoi/Comment/FAQ sur téléphone.
     Le burger le rétablit, avec le même soin graphique que le panneau desktop.
     ⚠️ PLACEMENT DU BOUTON dans `.links` : il est posé AVANT le sélecteur de langue,
     donc AVANT le CTA qui reste `:last-child`. Le poser en dernier aurait fait
     tomber le CTA sous `a:not(:last-child)` → CTA masqué entre 561 et 760px, une
     régression que personne n'a demandée. C'est un <button> : le sélecteur `a` de
     shared.css ne l'atteint pas, et il n'a PAS la classe `.btn` (shared.css:1414
     masque `.nav .links > .btn` sous 560px). */
  .nav .links .mburger{appearance:none;-webkit-appearance:none;display:none;
    width:44px;height:44px;padding:0;margin:0;flex:none;align-items:center;
    justify-content:center;cursor:pointer;color:var(--ink);
    background:var(--surface);border:1px solid var(--border);border-radius:999px;
    box-shadow:var(--sh-sm)}
  .nav .links .mburger svg{width:20px;height:20px;fill:none;stroke:currentColor;
    stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round}
  .nav .links .mburger:focus-visible{outline:none;box-shadow:0 0 0 3px var(--accent-ring)}
  @media(max-width:760px){
    /* Le déclencheur desktop et son panneau disparaissent : le burger prend le relais.
       `display:none` sur le panneau prime sur son état ouvert — pas de fantôme.
       On masque `.omenu-wrap` lui-même (et pas seulement ses enfants) : un emballage
       flex vide laisserait une gouttière `gap:22px` fantôme dans la barre mobile. */
    .nav .links .omenu-wrap,.nav .links .omenu-trigger,.nav .links .omenu{display:none}
    .nav .links .mburger{display:inline-flex}
  }

  /* La feuille vit en FIN DE <body>, hors `.container` : elle échappe ainsi au
     contexte d'empilement `z-index:1` du conteneur et se pose franchement au-dessus
     de la page. Le focus y est déplacé à l'ouverture et rendu au burger à la
     fermeture — l'ordre DOM tardif n'est donc jamais subi. */
  .msheet{position:fixed;inset:0;z-index:80}
  .msheet[hidden]{display:none}
  /* Toujours SANS `fill-mode` — même raison qu'au panneau de bureau (voir la note :
     horloge d'animation gelée = feuille ouverte et invisible). L'état de repos de
     ces deux boîtes EST l'état visible. */
  .msheet-scrim{position:absolute;inset:0;background:rgba(23,23,22,.42);
    animation:msheet-fade .2s ease}
  .msheet-panel{position:absolute;top:0;left:0;right:0;max-height:100dvh;
    overflow-y:auto;-webkit-overflow-scrolling:touch;
    background:var(--bg);border-bottom:1px solid var(--border);
    border-radius:0 0 var(--r-xl) var(--r-xl);box-shadow:var(--sh-cardh);
    padding:14px 18px 22px;animation:msheet-in .24s cubic-bezier(.2,.8,.3,1)}
  /* Le VOILE peut, lui, se permettre un fondu : gelé à opacity 0 il reste un voile
     transparent qui capte toujours le clic de fermeture — défaillance bénigne.
     Le PANNEAU, jamais : même règle que le panneau de bureau, translation seule. */
  @keyframes msheet-fade{from{opacity:0}to{opacity:1}}
  @keyframes msheet-in{from{transform:translateY(-14px)}to{transform:none}}
  .msheet-hd{display:flex;align-items:center;gap:10px;margin:0 0 14px}
  .msheet-hd .logo{font-size:24px}
  .msheet-x{appearance:none;-webkit-appearance:none;margin-left:auto;flex:none;
    width:44px;height:44px;display:inline-flex;align-items:center;justify-content:center;
    background:var(--surface);border:1px solid var(--border);border-radius:999px;
    color:var(--ink);cursor:pointer;box-shadow:var(--sh-sm)}
  .msheet-x svg{width:19px;height:19px;fill:none;stroke:currentColor;stroke-width:1.5;
    stroke-linecap:round;stroke-linejoin:round}
  .msheet-x:focus-visible{outline:none;box-shadow:0 0 0 3px var(--accent-ring)}
  .msheet-eyebrow{margin:0 0 8px;font-size:10.5px;font-weight:700;letter-spacing:.12em;
    text-transform:uppercase;color:var(--ink3)}
  /* Les rangées mobiles réemploient TEL QUEL le composant du panneau desktop
     (.omenu-row/.omenu-ico/.omenu-name/.omenu-desc), re-scopé sous `.msheet` : une
     seule définition visuelle pour les deux surfaces, donc aucune dérive possible
     entre le menu de bureau et celui du téléphone. */
  /* ⚠️ Fond `--bg2` (#FAF8F7) et NON `--surface` (#FFFFFF) : la charte proscrit le
     blanc pur, et la feuille elle-même est en `--bg` (#FFFDFC). Les deux crèmes se
     distinguent assez (13 points de luminance sur le bleu) pour que chaque rangée
     se lise comme une carte posée, sans jamais introduire de blanc pur.
     La pastille d'icône passe donc en `--surface`… non : elle prend le blanc CASSÉ
     via --bg, pour la même raison. */
  .msheet .omenu-row{display:grid;grid-template-columns:auto 1fr;gap:12px;
    align-items:start;padding:12px;border-radius:var(--r-lg);
    border:1px solid var(--border);background:var(--bg2);text-decoration:none;
    color:inherit;margin:0 0 8px;min-height:56px}
  .msheet .omenu-ico{width:38px;height:38px;border-radius:10px;flex:none;
    display:inline-flex;align-items:center;justify-content:center;
    background:var(--bg);border:1px solid var(--border);color:var(--accent)}
  .msheet .omenu-ico svg{width:19px;height:19px;fill:none;stroke:currentColor;
    stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round}
  .msheet .omenu-name{display:block;font-family:var(--font-brief);font-weight:600;
    font-size:15.5px;letter-spacing:-.015em;line-height:1.25;color:var(--ink)}
  .msheet .omenu-desc{display:block;margin-top:3px;font-size:13px;line-height:1.45;
    color:var(--ink2)}
  /* Même hiérarchie qu'au bureau : le phare garde son fond soft, sa bordure teintée,
     son sur-titre, sa pastille plus grande et son nom plus grand. */
  .msheet .omenu-row--flag{background:var(--accent-soft);border-color:#FFD6DE;padding:15px 13px}
  .msheet .omenu-row--flag .omenu-ico{width:44px;height:44px;background:var(--bg);border-color:#FFD6DE}
  .msheet .omenu-row--flag .omenu-ico svg{width:22px;height:22px}
  .msheet .omenu-row--flag .omenu-name{font-size:17px}
  .msheet .omenu-row--flag .omenu-desc{color:#7a5560}
  /* Même correction qu'en bureau, même mesure : 3,30:1 → 5,21:1 sur --accent-soft. */
  .msheet .omenu-flagtag{display:block;margin:0 0 5px;font-size:10px;font-weight:700;
    letter-spacing:.12em;text-transform:uppercase;color:var(--accent-active)}
  .msheet .omenu-go{display:inline-flex;align-items:center;gap:5px;margin-top:9px;
    font-size:13px;font-weight:600;color:var(--accent-active)}
  .msheet .omenu-go svg{width:15px;height:15px;flex:none;fill:none;stroke:currentColor;
    stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round}
  .msheet-links{display:flex;flex-direction:column;margin:14px 0 4px;
    border-top:1px solid var(--divider);padding-top:6px}
  .msheet-links a{display:flex;align-items:center;min-height:48px;
    text-decoration:none;color:var(--ink2);font-size:15px;font-weight:500}
  /* ⚠️ `.btn` DE BASE N'A NI HAUTEUR NI PADDING (L326) : sa hauteur vient TOUJOURS
     d'un modificateur de taille (.btn-sm 38px, .btn-lg 56px) ou d'un helper qui en
     fournit une (.wl-btn 48px, .fbm-send 46px, .sub-btn var(--sub-h)). `.msheet-cta`
     était le SEUL helper `.btn` du site à n'en fournir aucune — et build.js (L440)
     REMPLACE `btn-sm` par `msheet-cta` sur les pages autonomes, donc il retirait le
     seul modificateur de taille présent. Résultat MESURÉ avant correction, identique
     dans les 5 langues et à toutes les largeurs (320/360/375/390/430) : bouton à
     **16px de haut** (= `line-height:1` seul), libellé débordant hors de son fond
     accent. Lu à l'œil en bas de feuille, sous « Comment ça marche » / « FAQ », ce
     bandeau accent écrasé se lit comme un dégradé qui bave vers le haut — d'où le
     signalement « chevauchement ». Il n'y avait AUCUN chevauchement : les 3 liens
     mesurent 48px, ne se recouvrent pas et passent le test au point central.
     `min-height` et non `height` : en allemand (« Meinen Chat uncooken ») comme dans
     toute langue plus longue, le libellé doit pouvoir passer à deux lignes en
     poussant le bouton, jamais en le faisant déborder. D'où aussi le line-height
     lisible et le white-space normal (le `.btn` de base fige line-height:1). */
  .msheet-cta{width:100%;margin-top:12px;min-height:52px;padding:14px 18px;
    font-size:15.5px;line-height:1.25;text-align:center;white-space:normal}
  /* Encoche iOS : la feuille peut défiler (mesuré : elle défile dès 320px de large,
     et systématiquement une fois l'encart email en pied). Sans ce padding, le
     dernier élément atterrit sous l'indicateur d'accueil. `max()` garde les 22px
     d'origine partout où l'inset vaut 0 — donc aucun changement hors iOS. */
  .msheet-panel{padding-bottom:max(22px,env(safe-area-inset-bottom,0px))}

  /* ENCART « PRÉVIENS-MOI » DANS LA FEUILLE (19/07) — MÊME COMPOSANT que le pied
     (mountWatch(), site/app.js) : même markup, même endpoint, même consentement.
     Seul le THÈME change ici, et il le DOIT : `.foot-watch` est peint pour le pied
     SOMBRE (texte #fff, champ en rgba(255,255,255,.06), bordures blanches). Posé
     tel quel dans la feuille, qui est en --bg (#FFFDFC), il aurait donné du blanc
     sur crème — donc un titre de consentement et une mention de désinscription
     ILLISIBLES. Un texte de consentement invisible n'est pas un consentement : ce
     ré-habillage n'est pas cosmétique, il conditionne la réutilisation.
     Séparateur en haut : l'encart est une proposition SECONDAIRE, posée après le
     CTA, il ne doit pas se lire comme la suite du menu. */
  .msheet .foot-watch{margin-top:18px;max-width:none;
    border-top:1px solid var(--divider);padding-top:16px}
  .msheet .foot-watch-t{color:var(--ink);font-size:14px}
  /* Le pied garde champ+bouton sur une ligne quoi qu'il arrive (flex:none sur le
     bouton). Mesuré à 320px en allemand — « Benachrichtigen » laisse 130px au
     champ et TRONQUE le placeholder (« du@beispiel.d »). On autorise donc le repli :
     base 150px, donc une seule ligne tant qu'elle tient (vérifié : 375px dans les
     5 langues) et empilement plein largeur seulement quand elle ne tient plus. */
  .msheet .foot-watch-form{flex-wrap:wrap}
  .msheet .foot-watch-email{color:var(--ink);background:var(--bg2);
    border:1px solid var(--border);height:44px;font-size:16px;flex:1 1 150px}
  .msheet .foot-watch-btn{flex:1 1 auto}
  /* 16px sur le champ, PAS 13px : sous 16px, iOS Safari ZOOME sur le focus — la
     feuille se retrouve recadrée et le reste du menu sort de l'écran. Le pied peut
     rester à 13px (page normale, le zoom y est bénin) ; dans une surface fixée en
     inset:0, c'est une sortie de route. */
  .msheet .foot-watch-email::placeholder{color:var(--ink3)}
  .msheet .foot-watch-email:focus{outline:none;border-color:var(--ink2);
    box-shadow:0 0 0 3px var(--accent-ring)}
  .msheet .foot-watch-btn{height:44px;font-size:14px}
  /* --err-ink (#7C2418) et non le #ff8a8a du pied : ce rose est calibré pour un
     fond sombre et tombe à ~1,9:1 sur la crème de la feuille. */
  .msheet .foot-watch-err{color:var(--err-ink);font-size:12.5px}
  .msheet .foot-watch-ok{color:var(--ink);font-size:13px}
  /* --ink2 (4,90:1) plutôt que --ink3 : depuis le passage de --ink3 à #756E6A
     (4,94:1) les deux passent AA, le choix n'est donc plus une question de
     conformité mais de hiérarchie — cette mention porte la désinscription et le
     « jamais revendu », elle est au même rang de lecture que le titre, pas au rang
     d'une légende. Elle passe aussi de 10,5 à 11,5px : c'est la ligne qu'on ne peut
     pas se permettre de rendre décorative. */
  .msheet .foot-watch-note{color:var(--ink2);font-size:11.5px}
  @media(min-width:761px){.msheet{display:none}}

  /* Mouvement réduit : on retire l'ANIMATION D'ENTRÉE et les micro-transitions.
     L'état de base des deux surfaces étant déjà l'état visible, elles restent
     entièrement lisibles et utilisables — rien ne peut rester bloqué à opacity:0. */
  @media(prefers-reduced-motion:reduce){
    .nav .links .omenu{animation:none}
    .msheet-panel,.msheet-scrim{animation:none}
    .nav .links .omenu-row,.nav .links .omenu-ico,.nav .links .omenu-go svg,
    .nav .links .omenu-trigger,.nav .links .omenu-trigger .chev{transition:none}
    /* ⚠️ `transition:none` NE SUFFIT PAS pour le grandissement de survol ajouté en
       v4 : sans transition le `scale(1.015)` s'appliquerait toujours, simplement
       d'un coup — c'est-à-dire le saut brutal que ce mode cherche justement à
       éviter. Il faut annuler le TRANSFORM lui-même, pas seulement son animation. */
    @media(hover:hover){
      .nav .links .omenu-row:hover,
      .nav .links .omenu-row--flag:hover{transform:none}
      .nav .links .omenu-row--flag:hover .omenu-go svg{transform:none}
    }
  }

  /* Pointeur grossier : toute rangée cliquable atteint 44px de haut (SR-04·A). */
  @media(pointer:coarse){
    .nav .links .omenu-row{min-height:56px}
    .msheet .omenu-row{min-height:56px}
    .msheet-links a{min-height:48px}
  }

  /* ══════════════════════════════════════════════════════════════════════════════
     PASTILLES DE CONFIANCE — DÉPLACÉES ICI DEPUIS templates/home.html (V2.3)
     ══════════════════════════════════════════════════════════════════════════════
     Remplace `.diffband`. Le grief fondateur porte sur la FORME : « une seule bande
     continue, ce n'est pas bon ; il faut des pastilles ». La correction n'est donc
     pas cosmétique, elle est structurelle — le fond --accent-soft change de porteur.

       V2.3 : conteneur = TRANSPARENT ; chaque <li> = --accent-soft  → N pastilles.
       V2.5 : conteneur TOUJOURS transparent ; la pastille TOMBE      → N items nus.

     ⚠️ LA PASTILLE PAR ITEM A ÉTÉ RETIRÉE (20/07). Ce n'est PAS un retour à la
     bande — c'est l'objectif d'origine poussé plus loin. L'objectif écrit ici est
     « il devient IMPOSSIBLE de relire ça comme une bande » ; il tenait par le fait
     que le fond de page respire entre les items. Retirer la pastille SUPPRIME de la
     surface teintée et fait respirer ENCORE PLUS de fond de page : l'objectif est
     mieux servi, pas contourné. Ce qui reste interdit, et le reste :
       · aucun fond sur le conteneur `.trustbadges` ;
       · aucune bordure basse ni filet pleine laize sous le groupe ;
       · rien qui referme les quatre items dans une forme unique.
     MESURE QUI A FORCÉ LA MAIN : le disque d'icône #FAF8F7 sur la pastille
     --accent-soft #FFF0F3 ne faisait que 1,04:1 — le disque était littéralement
     invisible, seul son filet enregistrait (1,14:1). Tant que la pastille reste,
     le disque se bat contre un fond quasi identique au sien ; c'est la pastille,
     pas le disque, qui était la cause. Les deux références fournies par le
     fondateur (SoFi, neon.ch/Hume) partagent exactement deux traits : un disque
     qui CONTRASTE FORT avec son fond, et AUCUNE pastille autour de chaque item. */
  /* AUTONOME, PAS `.container` — mesuré : `container nav-shell` se referme L119 sur
     /handoff/ (le groupe serait au niveau <body>) mais L241 sur /pro/ et L285 sur
     /brief/ (le groupe serait DEDANS). Emprunter `.container` aurait donc doublé les
     gouttières sur la moitié des pages, et laissé l'autre moitié sans le
     `z-index:1` de `.container,footer,section` — donc SOUS `.page-bg`.
     On porte les deux nous-mêmes : le composant se comporte pareil aux deux
     profondeurs, et ne dépend plus de l'endroit où on l'insère. */
  /* LAIZE RÉELLEMENT OCCUPÉE. `justify-content:center` sur un flex laisse le groupe
     COLLER À SON CONTENU : mesuré sur la prod, 599px utilisés sur 1138px de laize
     disponible en anglais (53 %), 714px en français (63 %). Le `max-width:1250px`
     ne servait donc à rien — il plafonnait une largeur jamais atteinte.
     `gap:8px` reste ici la valeur de REPLI (elle s'applique sous 900px, voir plus
     bas) ; au-delà, la respiration passe par un gap franchement plus large. */
  /* ⚠️ RÈGLE DE PROXIMITÉ — LA CONTRAINTE QUE LA PASTILLE MASQUAIT. Tant qu'un
     item était enfermé dans une capsule, sa frontière était DESSINÉE : le gap
     entre items pouvait valoir 8px sans qu'on confonde jamais un libellé avec le
     disque suivant. Sans pastille, plus rien ne borne l'item — seule la proximité
     groupe le disque avec SON libellé. Il faut donc, à toutes les laizes :
         gap ENTRE items  ≥  ~2 × gap disque→libellé
     sinon l'œil hésite sur l'appartenance et la rangée redevient une soupe de
     huit objets au lieu de quatre. C'est ce rapport, et non une valeur absolue,
     qui doit être maintenu si l'un des deux nombres bouge :
         base   30 / 13 = 2,3     ≥900px  72 / 13 = 5,5     ≤560px  18 / 8 = 2,25 */
  .trustbadges{list-style:none;max-width:min(1640px,93vw);margin:22px auto 0;padding:0 clamp(28px,3vw,56px);
    display:flex;flex-wrap:wrap;align-items:center;justify-content:center;
    gap:14px 30px;position:relative;z-index:1}

  /* ≥900px : les quatre items TIENNENT sur une ligne dans les 5 langues (mesuré :
     l'allemand, le plus long, occupe 701px de contenu à 1280). On peut donc les
     ÉTALER sans risque de créer une ligne 2 déséquilibrée. Le gap monte à 72px :
     c'est ce qui porte l'anglais de 599 → 791px et l'allemand de 701 → 893px sur
     1138px de laize (69 % / 78 %), au lieu d'un `space-between` qui aurait collé
     les deux items extrêmes aux gouttières et creusé des vides inégaux entre des
     libellés de longueurs différentes. Un gap CONSTANT garde un rythme régulier —
     c'est la structure des deux références, pas quatre capsules jetées aux coins. */
  @media(min-width:900px){
    .trustbadges{gap:72px}
  }

  /* ── REMONTÉE DE LA RANGÉE SOUS UN HERO 2 COLONNES (20/07) ──────────────────
     DEMANDE : « la rangée est trop basse dans le hero » (constaté sur /brief/).
     Le style des badges n'est PAS en cause et ne bouge pas — c'est la POSITION.

     ⚠️ LA MARGE DE `.trustbadges` N'ÉTAIT PAS LA COUPABLE PRINCIPALE. Mesuré à
     1440×900 sur /brief/ : entre le dernier pixel de contenu du hero (bas de
     `.hero-left`, 737,1px) et le haut de la rangée (819,1px) il y a 82px, qui se
     décomposent en :
         60px = `padding-bottom` de `.hero-da` (l.725)   → 73 % de l'écart
         22px = `margin-top` de `.trustbadges`            → 27 % de l'écart
     Corriger la seule marge des badges n'aurait donc adressé qu'un quart du
     problème. C'est le `padding-bottom` du hero qui creuse l'écart — mais on ne
     peut pas le réduire à la source : `.hero-da` est porté par 20 pages, soit les
     19 à badges PLUS /mask/, qui n'en a pas et où ces 60px sont le bas de
     hero voulu. On absorbe donc une partie de ce padding PAR LE BAS, côté badges,
     là et seulement là.
     (CORRECTION 20/07 — cette phrase disait « les 5 homes » : c'était FAUX et
     c'est parti en prod. Les 5 homes portent `hero-hub`, pas `hero-da` ; le seul
     `hero-da` qu'on lise dans leur source est une MENTION EN COMMENTAIRE, qu'un
     grep sur la chaîne compte comme une occurrence. Vérifier la classe sur le
     markup, commentaires et blocs <style> retirés — pas la sous-chaîne.)

     ⚠️ POURQUOI `.hero-da +` ET PAS `.trustbadges` TOUT COURT. Les 19 pages qui
     portent les badges se répartissent en DEUX familles — mesuré, pas déduit :
       · frère d'un `.hero-da` (padding-bottom 60px) → écart réel 82px : /brief/,
         /pro/ ×5, /everyday/ ×5 = 11 pages. C'EST LA FAMILLE QUI EST TROP BASSE.
       · frère d'un `.container` (padding-bottom 0px) → écart réel 22px :
         /migration/ ×5, /merge/, /handoff/, /extract/ = 8 pages. Celles-ci sont
         DÉJÀ serrées ; une marge négative globale les aurait collées au contenu
         précédent (22 → 14px), c'est-à-dire cassé les 8 pages saines pour en
         corriger 11. Le sélecteur d'adjacence borne le geste à la cause réelle.
     Sélecteur frère adjacent : supporté partout, aucun `:has()` requis.

     VALEUR : −8px porte l'écart total à 60 − 8 = 52px, soit EXACTEMENT le `gap`
     interne de `.hero-da` (l.725). La rangée cesse d'être plus loin du hero que
     les morceaux du hero ne le sont entre eux — c'est ce rapport, et pas une
     valeur absolue, qui la fait lire comme une ligne DU bloc hero. Si le `gap` du
     hero bouge, cette valeur se re-dérive (gap − padding-bottom).
     Sous 900px le hero passe à padding-bottom 40 / gap 30 (l.818-819) : le même
     −8px donne 32px pour un rythme interne de 30px — cohérent, aucune règle en
     plus. Le ≤560px est repris plus bas (le hero y tombe à 24px de bas).
     MARGE NÉGATIVE SANS RISQUE : −8px se prend dans 60px (ou 40px) de padding du
     hero — la rangée remonte DANS le blanc du hero, jamais dans son contenu.
     `.trustbadges-note` n'a rien à changer : elle suit le `<ul>` dans le flux et
     conserve ses 9px, tout le bloc se translate d'un seul tenant. */
  .hero-da + .trustbadges{margin-top:-8px}

  /* ── REMONTÉE SUPPLÉMENTAIRE + LA LÉGENDE PASSE LA LIGNE (20/07, lot 4) ──────
     DEMANDE : « il reste de la place sous le bloc, ils peuvent monter davantage »
     ET « la légende doit rentrer proprement dans l'alignement ».

     ⚠️ CES DEUX DEMANDES N'ONT QU'UN SEUL CORRECTIF, et c'est mesuré, pas déduit.
     `.trustbadges-note` suit le `<ul>` dans le flux : le `<ul>` + sa note se
     translatent d'un seul tenant (cf. bloc au-dessus). Il n'y a donc RIEN à faire
     porter à la note elle-même — lui donner sa propre marge la décollerait du
     groupe qu'elle légende et casserait le seul lien qui l'y rattache. Un unique
     `margin-top` règle la remontée ET la légende. Mesuré sur /brief/, polices
     chargées, à 1280×800 (la laize la plus contrainte au-dessus de 900px) :
         −8px  → rangée +13,0px · note −14,2px  (la note NE FRANCHIT PAS)
         −20px → rangée +25,0px · note  −2,2px  (toujours pas)
         −30px → rangée +35,0px · note  +7,8px  ← RETENU
         −44px → rangée +49,0px · note +21,8px  (mais gap hero→rangée à 16px)

     ⚠️ POURQUOI −30px ET PAS PLUS, ALORS QUE LA DEMANDE DIT « DAVANTAGE ». Le
     frein n'est pas la ligne de flottaison, c'est le RATIO DE PROXIMITÉ qui régit
     tout ce composant (cf. bloc du `<ul>`). Le geste mange le blanc entre le
     dernier contenu du hero et la rangée : 52px → 30px. La rangée a un rythme
     interne de 14px (row-gap) ; à 30px l'écart au hero vaut encore ≈2,1× ce
     rythme, la rangée se lit comme un bloc AUTONOME sous le hero. À −36px on
     tombe à 24px (≈1,7×) et à −44px à 16px (≈1,1×) : la rangée cesse d'être un
     bloc séparé et se recolle au hero comme une 5e ligne de son contenu. C'est
     cette bascule, et non un coussin cible, qui borne la valeur.

     ⚠️ POURQUOI `@media(min-width:901px)` — MÊME BORNE QUE LE `padding-top` DU
     HERO, ET POUR UNE RAISON PLUS FORTE ICI. La règle de base `.hero-da +
     .trustbadges{margin-top:-8px}` juste au-dessus est NUE : elle porte à TOUTES
     les laizes, mobile compris. Y toucher aurait donc bougé le mobile, contre la
     consigne. On empile un override borné au lieu de modifier la valeur nue —
     le mobile (390/844, 375/667) reste rigoureusement à −8px, vérifié au pixel.
     Les 8 pages « famille container » sont hors d'atteinte par le SÉLECTEUR et
     non par la borne : leurs badges ne sont pas frère adjacent d'un `.hero-da`.

     ⚠️ CE QUE CE GESTE NE RÈGLE PAS : à 768×1024 la note reste à −25px. Cette
     laize est SOUS la borne 901px et n'est donc pas touchée. Descendre la borne
     pour l'attraper est possible (le sélecteur protège déjà les pages container),
     mais personne ne l'a demandé et ça bougerait la tablette portrait sans
     mesure d'accompagnement. Laissé tel quel, sciemment. */
  /* ⚠️ v3 — MOTIF A1 (23/07, Kevin) : TRUST-LINE UNIFORME ET REMONTÉE D'UN CRAN.
     RÉFÉRENCE = la home (« il est parfait, ne pas toucher ») : sa note de confiance
     tombe à 51px du bas du viewport (mesuré, constant à 1512/1920/2560). Les pages
     outils (handoff/extract/migration/brief) tombaient 4px plus bas, à 55px — trop
     bas, et /migration/ ne se remplissait même pas (corrigé par le plancher plus haut).
     POURQUOI un sélecteur PAR ATTRIBUT DE BODY et pas `.hero-da + .trustbadges` : sur
     handoff/extract/migration la trust-line SORT du `.container` (frère du conteneur,
     pas du hero) → l'adjacent-sibling ne matchait QUE /brief/. Un seul chemin de code
     énumérant les 4 pages porte donc la MÊME marge à toutes, là où l'ancien `.hero-da +`
     n'en touchait qu'une.
     · Spécificité 0,2,1 : bat la base `.trustbadges` (0,1,0) ET l'ancien `.hero-da +
       .trustbadges` (0,2,0, conservé plus haut pour le SEUL mobile de /brief/ à −8px).
     · Scopé ≥901px : le mobile ne bouge sur AUCUNE page.
     · La HOME (index.html) porte un <body> NU (pas data-intent="home" — c'est /handoff/
       qui le porte) : elle n'est PAS matchée ici et garde son gabarit propre. Intacte.
     VALEUR = 26px : mesuré, la note tombait à 55px du bas sur les pages outils contre
     51px sur la home (les outils étaient donc 4px PLUS HAUT que la référence). La note
     suit le <ul> dans le flux, donc note_du_bas = 77 − margin-top (relation linéaire
     vérifiée empiriquement : 22→55, 18→59). 26px → 51px, PILE au niveau de la home :
     les 4 pages outils DESCENDENT de 4px pour rejoindre la référence. Uniforme. */
  @media(min-width:901px){
    body[data-intent="home"] .trustbadges,
    body[data-page="extract"] .trustbadges,
    body[data-page="brief"] .trustbadges,
    body[data-intent="migration"] .trustbadges{margin-top:26px}
  }

  /* L'ITEM — PLUS DE PASTILLE (20/07). Ce qui saute : `background:--accent-soft`,
     `border:1px solid #EFBECB`, `border-radius:999px` et le `padding` qui les
     portait. Il reste un disque + un libellé posés sur le fond de page, exactement
     la structure des deux références. Trois raisons, dans cet ordre :
       1. MESURE. Le disque d'icône ne pouvait pas contraster sur --accent-soft
          (1,04:1, cf. bloc de tête). Tant que la pastille tenait le fond, le
          disque était condamné à être invisible ou à ne vivre que par un filet.
       2. LAIZE. Une pastille COLLE à son contenu. Le fondateur demande plus
          d'espacement ET une rangée qui occupe plus de laize : étalés, quatre
          capsules teintées se lisent comme quatre objets sans rapport. Un disque
          + du texte s'étalent sans jamais donner cette impression.
       3. DOCTRINE. Moins de surface teintée = plus de fond de page entre les
          items = l'anti-bande est renforcé, pas affaibli (cf. bloc de tête).
     `white-space:nowrap` est CONSERVÉ : le motif disque+libellé ne se coupe pas en
     deux, il passe entier à la ligne suivante. C'est ce qui fait tenir la lecture
     quand l'allemand rallonge les libellés — la raison d'origine survit à la
     pastille, parce qu'elle portait sur le MOTIF, pas sur la forme pill.
     ⚠️ Le gras partiel des mots-clés N'EST PAS tenté : abandonné par le fondateur,
     et aucune des deux références ne le fait (SoFi met des titres entiers en gras,
     Hume est tout en régulier). Le libellé reste d'un seul poids. */
  .trustbadges li{display:inline-flex;align-items:center;gap:13px;
    background:none;border:0;border-radius:0;padding:0;
    font-size:15px;font-weight:600;line-height:1.3;
    color:var(--ink);white-space:nowrap}

  /* Icône : DISQUE PLEIN SOMBRE, glyphe blanc. Toujours pas --accent — le rouge
     reste réservé aux vrais CTA, et le fondateur a explicitement exclu des icônes
     rouges ici. C'est le geste de SoFi (disque plein bleu nuit sur fond clair)
     transposé aux encres maison : le disque prend --ink #171716, le trait passe en
     blanc pur.
     LE FILET DISPARAÎT, ET C'EST LE POINT. L'ancien filet #E9E4E1 n'était pas un
     ornement : il compensait un aplat qui ne se voyait pas (disque #FAF8F7 sur
     pastille #FFF0F3 = 1,04:1, le filet seul portait la forme à 1,14:1). Un aplat
     --ink sur le fond de page ne fait plus 1,04:1 mais ~15:1 — il n'a plus rien à
     compenser. Détourer un disque déjà franc l'aurait seulement sali.
     MESURES — relevées sur le rendu, en composant les fonds réels (le fond de page
     est un dégradé vivant : le token ne suffit pas, il faut lire le pixel composé) :
       · glyphe #FFFFFF sur disque #171716 ......... 17,94:1
         SC 1.4.11 exige 3:1 pour un objet graphique porteur de sens : très large.
       · disque #171716 vs son fond composé #FFFDFC  17,69:1
         C'ÉTAIT LA PANNE : ce rapport valait 1,04:1 en prod (et 1,14:1 pour son
         seul filet). Le disque existait dans le DOM sans exister à l'œil.
     TAILLE : 26 → 32px, glyphe 15 → 16px (32 − 2×8 de padding). Les deux
     références posent un disque SUBSTANTIEL ; à 26px sur un libellé de 15px le
     disque redevenait une puce. `box-sizing:border-box` conservé pour que le
     padding se prenne DANS les 32px et non autour. `stroke-width:1.5` inchangé
     (« 1.5 comme partout ») : sur fond sombre un trait blanc paraît déjà plus
     épais qu'il n'est, l'épaissir aurait bouché les glyphes fermés. */
  .trustbadges li svg{width:32px;height:32px;flex:none;fill:none;box-sizing:border-box;
    padding:8px;border-radius:50%;background:var(--ink);border:0;
    stroke:#FFFFFF;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round}

  /* ── SURVOL (20/07) — « c'est trop plat, ça manque de vie », subtil, pas clinquant.
     ⚠️ PAS DE FOND AU SURVOL, ET C'EST LE POINT DE DOCTRINE. La demande cite « un
     changement de fond » comme piste ; c'est précisément ce que ce composant a
     PERDU ce matin (`background:--accent-soft` + `border` + `border-radius:999px`,
     cf. bloc de l'item juste au-dessus). Le réintroduire au survol ferait revenir
     la pastille par intermittence : la rangée redeviendrait quatre capsules, une
     fraction de seconde à la fois, et les deux références du fondateur (SoFi,
     Hume) n'en portent aucune. L'effet passe donc par le DISQUE et le RELIEF.

     ⚠️ POURQUOI L'ITEM ENTIER SE SOULÈVE, ET NON LE SEUL DISQUE. Depuis le retrait
     de la pastille, l'item n'a plus de frontière dessinée : la PROXIMITÉ est la
     seule chose qui relie un disque à SON libellé (cf. règle de proximité, bloc
     du `<ul>`). Soulever le disque seul l'arracherait de son libellé pendant tout
     le survol — on casserait au survol l'unique liaison que le composant possède.
     Le couple monte donc d'un bloc, et c'est l'OMBRE PORTÉE SUR LE DISQUE qui
     fabrique la profondeur. Le libellé ne change pas de couleur : il est déjà à
     --ink #171716 (16,24:1), tout changement ne pourrait que l'ÉCLAIRCIR, donc
     dégrader son contraste pour « faire un effet ».

     GÉOMÉTRIE INTACTE : `transform` et `box-shadow` ne participent PAS au layout.
     Aucun gap n'est touché, les ratios de proximité restent 5,54 / 2,31 / 2,25 et
     la position de la rangée (donc le coussin sous la ligne de flottaison) est
     rigoureusement la même au survol qu'au repos — vérifié, delta 0,00px.
     CONTRASTE INCHANGÉ : ni le fond du disque (--ink) ni le glyphe (#FFFFFF) ne
     bougent. Le disque reste à 17,69:1 sur le fond composé et le glyphe à 17,94:1,
     très au-dessus des 3:1 de SC 1.4.11. L'ombre est POSÉE AUTOUR du disque, elle
     n'entre pas dans le rapport disque/fond.

     ⚠️ BORNÉ À `hover:hover` ET `pointer:fine`. Au doigt, `:hover` reste « collé »
     après un tap : le badge tapé garderait son relief jusqu'au tap suivant, un
     état actif permanent sur un élément qui n'est pas un contrôle. Les items ne
     sont ni liens ni boutons — il n'y a donc pas de `:focus-visible` à apparier,
     rien n'est rendu inatteignable au clavier par cette borne. */
  /* ── ACCENTUATION DU SURVOL (20/07, lot 4) ──────────────────────────────────
     DEMANDE : « l'effet actuel est tellement léger qu'on le voit à peine, rends-le
     franchement perceptible ». Le −2px/ombre .26 venait d'une consigne « subtil,
     pas clinquant » qui a trop serré. On monte sur les TROIS registres déjà
     ouverts, sans en ouvrir un quatrième :
         lift    −2px            → −5px
         ombre   0 4px 9px .26   → 0 9px 20px .34
         disque  (rien)          → scale(1.07)
     Le lift seul ne pouvait pas suffire : à −2px le déplacement est sous le seuil
     de perception d'un œil qui ne fixe pas l'item. C'est le GROSSISSEMENT DU
     DISQUE qui fait le gros du travail perceptif — il se produit à l'endroit que
     l'œil regarde déjà (la seule forme pleine de l'item), là où le lift déplace
     un ensemble majoritairement blanc.

     🔴 TOUJOURS AUCUN FOND SUR L'ITEM, et la borne est plus serrée qu'avant, pas
     moins. La pastille (`background:--accent-soft` + `border` + `border-radius`)
     a été retirée ce matin parce qu'elle concurrençait le disque ; un fond au
     survol la ferait revenir par intermittence. Rien de ce qui suit ne pose de
     surface : on ne touche qu'au relief et à l'échelle. La règle survit à
     l'accentuation — c'est justement quand on « monte le volume » qu'on est
     tenté d'ajouter un fond, et c'est là qu'il faut le refuser.

     ⚠️ LE SCALE PORTE SUR LE DISQUE, LE LIFT SUR L'ITEM — pas l'inverse, et pas
     les deux sur le disque. Depuis le retrait de la pastille, la PROXIMITÉ est le
     seul lien entre un disque et SON libellé. Un scale grandit le disque autour de
     son propre centre : il reste à sa place, l'écart disque→libellé ne perd que
     ~1px sur 13 (32px × 0,07 / 2), le couple ne se disloque pas. Un translate sur
     le seul disque, lui, l'arracherait de son libellé — c'est pourquoi le
     déplacement reste porté par l'item entier, qui monte d'un bloc.

     GÉOMÉTRIE INTACTE : `transform` et `box-shadow` ne participent pas au layout.
     Aucun gap ne bouge, les ratios de proximité restent 5,54 / 2,31 / 2,25 et la
     position de la rangée (donc le coussin) est identique au survol et au repos —
     re-vérifié après accentuation, delta 0,00px aux 5 laizes.
     CONTRASTE : ni le fond du disque (--ink #171716) ni le glyphe (#FFFFFF) ne
     changent de couleur. Le disque reste à 17,69:1 sur le fond composé (mesuré
     après accentuation : #171716 sur #FFFDFC), très
     au-dessus des 3:1 de SC 1.4.11. Un scale ne modifie pas un rapport de
     luminance, et l'ombre est POSÉE AUTOUR du disque : elle n'entre pas dans le
     rapport disque/fond. Le libellé reste à --ink et ne s'éclaircit pas.

     ⚠️ BORNÉ À `hover:hover` ET `pointer:fine`. Au doigt, `:hover` reste « collé »
     après un tap : le badge tapé garderait son relief jusqu'au tap suivant, un
     état actif permanent sur un élément qui n'est pas un contrôle. L'effet étant
     maintenant BIEN plus visible, cette borne compte davantage qu'avant. Les items
     ne sont ni liens ni boutons — pas de `:focus-visible` à apparier, rien n'est
     rendu inatteignable au clavier par cette borne. */
  @media(hover:hover) and (pointer:fine){
    .trustbadges li{transition:transform .16s ease-out}
    .trustbadges li svg{transition:box-shadow .16s ease-out,transform .16s ease-out}
    .trustbadges li:hover{transform:translateY(-5px)}
    .trustbadges li:hover svg{box-shadow:0 9px 20px rgba(23,23,22,.34);transform:scale(1.07)}
  }
  /* Coupure NOMMÉE sous `prefers-reduced-motion` — le trou déjà documenté ici est
     qu'une liste générique ne rattrape pas ce qu'elle ne nomme pas. On coupe le
     déplacement ET la transition ; l'ombre, elle, RESTE : ce n'est pas du
     mouvement, et c'est ce qui laisse un retour de survol aux utilisateurs qui
     demandent moins d'animation, au lieu de les priver de l'affordance entière.
     ⚠️ LE `transform` DU DISQUE EST NOMMÉ À PART (20/07). Le scale ajouté ce lot
     est un SECOND transform, sur un SECOND sélecteur (`li:hover svg`) : la ligne
     `li:hover{transform:none}` ne l'attrape pas — elle ne parle que de l'item.
     Sans la ligne ajoutée ci-dessous, le disque aurait continué à grossir sous
     `prefers-reduced-motion`. C'est exactement le trou que ce bloc documente
     depuis le début, rencontré une fois de plus en ajoutant l'effet. */
  @media(prefers-reduced-motion:reduce){
    .trustbadges li,.trustbadges li svg{transition:none}
    .trustbadges li:hover{transform:none}
    .trustbadges li:hover svg{transform:none}
  }

  /* ── SOUS-TEXTE DE PASTILLE (modes 'all' / 'one' du générateur) ──────────────
     LA HIÉRARCHIE SE FAIT PAR LE HAUT, PAS PAR LE BAS. Le réflexe est d'éclaircir
     la ligne secondaire ; ici il est INTERDIT par la mesure : --ink3 #938D88 sur
     --accent-soft #FFF0F3 ne fait que 2,97:1, très en dessous des 4,5:1 exigés par
     SC 1.4.3 (le sous-texte est du texte normal, 11,5px << 24px). On garde donc
     --ink2 (5,82:1, la mesure déjà engagée par le titre en V2.3) pour le SOUS-texte
     et on FONCE le titre à --ink #171716 (16,24:1). La hiérarchie vient de l'écart
     titre/sous-texte — poids, taille, valeur — sans qu'aucune des deux lignes ne
     descende sous le seuil. Aucune couleur nouvelle n'est introduite. */
  .trustbadges li.has-sub{align-items:center;padding:0}
  .trustbadges .tb-t{display:flex;flex-direction:column;line-height:1.2}
  .trustbadges .tb-t b{font-weight:600;color:var(--ink)}
  .trustbadges .tb-t i{font-style:normal;font-size:11.5px;font-weight:400;color:var(--ink2)}

  /* ── NOTE DE GROUPE (mode 'group', RETENU) ───────────────────────────────────
     La description porte sur les quatre pastilles à la fois : elle vit donc SOUS
     le groupe, centrée sur la même laize, et non dans une pastille. Elle est sur
     le fond de PAGE (#FFFDFC) et non sur --accent-soft : --ink2 y mesure 6,34:1.
     Pas de fond, pas de filet, pas de pastille — sinon on ajoute une 5e forme au
     groupe et la rangée de confiance redevient un bloc. */
  .trustbadges-note{max-width:1250px;margin:9px auto 0;padding:0 28px;
    text-align:center;font-size:12.5px;line-height:1.45;color:var(--ink2);
    position:relative;z-index:1}

  /* 560px : mêmes gouttières que `.container` (18px, l.983). Les items
     rétrécissent mais NE passent PAS en défilement horizontal — un groupe de
     confiance qui déborde hors écran cache la moitié de ses arguments.
     ⚠️ C'EST ICI QUE SE JOUE L'ALLEMAND. Mesuré sur la prod AVANT ce lot : à 375px
     l'allemand tenait déjà en 3 LIGNES (117px), tout comme le français et
     l'espagnol ; seuls l'anglais et le portugais tenaient en 2 (76px). À 320px,
     4 langues sur 5 étaient à 4 lignes (158px). Le budget horizontal était donc
     déjà saturé, et un gap plus large seul l'aurait fait exploser.
     Ce qui sauve la mise : la pastille rendait 20px de chrome PAR ITEM
     (18px de padding + 2px de filet). Retirée, elle libère ~80px sur la rangée —
     bien plus que ce que coûtent le disque agrandi (+3px × 4) et le gap élargi
     (6 → 16px). Le retrait de la pastille est donc NET POSITIF sur mobile, ce qui
     est contre-intuitif pour un lot qui grossit tout le reste. Chiffres avant/après
     par langue consignés dans le rapport de ce lot — à re-mesurer, pas à déduire,
     si l'un des cinq dictionnaires change de libellé. */
  @media(max-width:560px){
    .trustbadges{gap:12px 18px;margin-top:14px;padding:0 18px}
    /* ⚠️ REPRISE OBLIGATOIRE de la remontée sous hero 2 colonnes (cf. l.3410).
       Ce n'est PAS une redite : `.hero-da + .trustbadges` (0,2,0) est PLUS
       SPÉCIFIQUE que `.trustbadges` (0,1,0), donc le −8px desktop l'emporterait
       ici quel que soit l'ordre des règles — la ligne ci-dessus ne le rattrape
       pas. Il faut le neutraliser à spécificité égale, sinon les pages hero-da
       partent à 24 − 8 = 16px de leur hero sur mobile.
       Le hero tombe à `padding-bottom:24px` sous 560 (c'est `.hero{padding:32px
       0 24px}` l.1489 qui gagne sur `.hero-da` l.819 — même spécificité, déclaré
       plus tard). Pour retrouver le rythme interne du hero (gap 30px, l.819) :
       24 + 6 = 30px. La marge redevient POSITIVE — à 24px de padding il n'y a
       plus de blanc à emprunter, une valeur négative mordrait le contenu. */
    .hero-da + .trustbadges{margin-top:6px}
    /* Le libellé garde son écart d'1px avec le desktop (15 → 14px). Le disque
       tombe à 28px pour un glyphe de 14px : le rapport disque/texte du desktop est
       conservé, la rangée ne change pas de silhouette en rétrécissant.
       `gap:8px` (et non 10) tient la règle de proximité ci-dessus à l'étroit :
       18/8 = 2,25. MESURÉ — c'est ce couple, et pas un autre, qui ramène le
       portugais à 2 lignes à 375px : à 18/10 il basculait à 3 lignes (108px),
       seule langue à RÉGRESSER contre la prod. Ne pas rouvrir sans re-mesurer
       les cinq langues à 375 ET 320. */
    .trustbadges li{padding:0;font-size:14px;gap:8px}
    .trustbadges li svg{width:28px;height:28px;padding:7px}
    .trustbadges li.has-sub{padding:0}
    .trustbadges .tb-t i{font-size:10.5px}
    /* La note reprend les gouttières de `.container` (18px) comme le groupe.
       `text-wrap:balance` évite le rang orphelin d'un seul mot quand l'allemand
       déborde sur deux lignes ; ignoré sans dommage là où il n'est pas supporté. */
    .trustbadges-note{margin-top:8px;padding:0 18px;font-size:12px;
      text-wrap:balance}
  }
