/* ============================================================================
   YNTGYST — charte graphique commune

   CHARTE-VERSION 3.14.4
   CHARTE-SHA256   99ba3ac5ee308eef91f75ade481bc64ca7439a608e5d78963e5fb6762baf888c
                   (condensat du fichier entier, cette valeur mise à blanc)

   Quatre dépôts portent ce fichier, octet pour octet : Knowledge le tient
   dans docs/, Collecte et Evidence le publient chacun dans packages/charte/,
   Console l'importe depuis docs/. Rien, dans aucune chaîne d'intégration, ne
   les compare — et pour cause : aucun ne voit les dépôts d'en face. La
   divergence ne se manifesterait donc que le jour où quelqu'un lit la
   mauvaise copie, ce qui est le pire moment.

   L'empreinte ci-dessus est ce que chaque dépôt PEUT vérifier tout seul :
   `npm run charte:check` recalcule le condensat du corps et le compare. Une
   retouche non déclarée fait rougir la barrière ; la déclarer oblige à
   changer la version, et deux versions différentes se voient à l'œil quand
   on ouvre les deux fichiers. Ce n'est pas une synchronisation, c'est un
   témoin de scellé.

   The shared visual identity for every YNTGYST application. Framework-
   agnostic: plain custom properties and a handful of component classes, no
   build step, no Tailwind required. Import it first, then style through the
   tokens — never through raw hexes.

     @import "charte.css";        — or a plain <link rel="stylesheet">

   ── 3.14.4 — la contre-lecture de la 3.14.3 ────────────────────────────
   17 octobre 2026. La 3.14.3 (D61) a été fusionnée dans quatre dépôts sur
   sept avec cinq défauts que sa contre-lecture avait trouvés. Une version
   promet UN fichier : la correction prend donc un numéro, au lieu de
   donner deux contenus à la 3.14.3. Aucune règle CSS ne change — la
   pastille de compte du menu reste du bleu des propositions, jour et
   nuit, et ses contrastes sont ceux de la 3.14.3. Ce qui change :
     · l'entrée 3.14.3 disait que les six autres produits portent des
       pastilles de compte : seuls Trust et Vendor-Connect, avec Proqr ;
       Knowledge et Console n'ont au menu que des pastilles de
       proposition, Collect et Evidence aucune ;
     · elle disait que « la forme » distingue les deux pastilles, et sa
       phrase centrale se lisait à l'envers (« …, plus la teinte ») :
       même forme, même bleu ; ce qui les sépare est le contenu et la
       police du chiffre. Corrigé en place, ainsi que la règle 3 et les
       commentaires de `.nav-item .count` et de `.badge-count` ;
     · la coquille du menu passe en 1.12.1 : sa prose réservait la marque
       des propositions aux seules propositions (« nothing else may wear
       it ») et donnait au compte une autre marque ; elle dit D61. Son code
       ne change pas : il pose `count` ou `badge-count`, et la charte peint ;
     · une porte garde la règle : la barrière de contraste, jumelle, passe
       en 1.6.0 et exige, dans la charte ET dans la feuille servie, que
       `.nav-item .count` et `.badge-count` portent le même couple de
       jetons — la framboise remise dans une seule copie la fait rougir.

   ── 3.14.3 — les pastilles du menu sont bleues ─────────────────────────
   Demande du propriétaire, 17 octobre 2026, 7 h 55 (D61) : « les pastilles
   de Proqr ne sont pas bleues sur le menu ». D42, trois heures plus tôt,
   avait passé au bleu la voix des PROPOSITIONS — les jetons `--proposed*`
   et le badge `.badge-count`. Le menu de Proqr ne porte que des pastilles
   de COMPTE (`badgeKind: "count"` : un non-lu, une attente du lecteur).
   Trust et Vendor-Connect en portent aussi ; Knowledge et Console n'ont
   au menu que des pastilles de proposition, Collect et Evidence aucune.
   La règle qui peint le compte, `.nav-item .count`, lisait
   `--critical-vif` : elle était restée framboise. D61 : la pastille de compte du menu prend le MÊME bleu que
   celle des propositions, jour et nuit, dans les sept produits. Ce qui
   change est la RÈGLE, pas un jeton :
     .nav-item .count   fond    var(--critical-vif) → var(--proposed-vif)
                        texte   var(--on-critical)  → var(--on-proposed)
     jour               fond    #985258 → #0a4fcc   texte #ffffff → #ffffff
     nuit               fond    #c57f88 → #3772c4   texte #200f12 → #ffffff
   Pourquoi les jetons de la proposition plutôt qu'un couple « compte » de
   mêmes valeurs : la décision dit « le même bleu », et deux jetons de
   mêmes valeurs sont deux jetons qui peuvent diverger ; ainsi, le jour où
   la voix des propositions change de bleu, la pastille de compte la suit
   sans qu'on y pense. `--critical-vif` et `--on-critical` ne bougent pas
   d'un octet : ils peignent toujours `.pill.critical` (un statut
   critique), les jauges et les bandeaux critiques, et ce qui est critique
   reste framboise — Raspberry = anomalie, règle 3. Mesuré (luminance
   relative WCAG ; entre parenthèses, la 3.14.2) :
     texte sur la pastille (11,5px, plancher 4,5:1)
                                     jour 6,99:1 (5,70)   nuit 4,81:1 (5,94)
     pastille sur le fond du menu (plancher 3:1 ; le verre du menu composé
     sur le papier : #ffffff en jour, #141014 la nuit)
                                     jour 6,99:1 (5,70)   nuit 3,92:1 (6,07)
     la même sur le tiroir (sous 768px ; la nuit il est #1c171c, mesuré
     au navigateur)                  jour 6,99:1 (5,70)   nuit 3,67:1 (5,69)
   La nuit, la pastille perd du contraste en devenant bleue et reste
   au-dessus des deux planchers. En jour comme en nuit, les deux pastilles
   du menu sont donc bleues, et elles ont la même forme : pleines, même
   rayon, même marge, même corps. Ce qui les distingue n'est plus la
   teinte, ni la forme : c'est le contenu — le titre de l'entrée, qui
   nomme ce qui est compté (« libellé — n … », dans le nom accessible du
   lien et dans l'infobulle) — et, à l'œil, la police du chiffre : chasse
   fixe pour la proposition, chiffres tabulaires de l'interface pour le
   compte. (Cette entrée a été corrigée en place par la 3.14.4.)

   ── 3.14.2 — en jour, les bordures se voient ; la pastille passe au bleu ──
   Décision du propriétaire, 17 octobre 2026, 4 h 05 (D41) : « cran 2 ». La
   3.14.1 avait monté d'un cran, comme demandé le 16 (« un petit peu, pas
   trop ») ; le 17, devant l'écran, elle se voyait à peine : « je ne vois
   pas ». Quatre crans ont été injectés en jour dans le tableau de bord de
   Collect et montrés ; il a choisi le deuxième. Les trois jetons de filet
   deviennent OPAQUES, dans les trois blocs clairs — `:root`,
   `:root[data-theme="light"]` et `.seuil.porte`, qui recopie le jour —, et
   les blocs sombres ne changent pas d'un octet :
     `--line`         rgb(200 205 213 / 0.7)  → rgb(188 193 201), #bcc1c9
                      sur le papier #ffffff : #d8dce2 → #bcc1c9, 1,38:1 → 1,81:1
     `--line-strong`  rgb(186 192 201 / 0.9)  → rgb(166 171 179), #a6abb3
                      #c1c6ce → #a6abb3, 1,71:1 → 2,31:1
     `--glass-line`   rgb(200 205 213 / 0.55) → rgb(200 205 213), #c8cdd5
                      #e1e4e8 → #c8cdd5, 1,28:1 → 1,60:1
   (luminance relative WCAG, sur `--paper`). Cran 2 choisi par le
   propriétaire sur aperçu (D41) ; WCAG 1.4.11 (3:1) reste non visé, le
   cran 3 à 3:1 a été montré et écarté. Opaques, les trois rendent le même
   gris sur le papier, le verre et les surfaces teintées : un filet ne se
   compose plus avec ce qui est dessous. `--glass-line` — la barre du haut,
   la barre latérale, les tiroirs — reste le plus léger des trois et vaut
   désormais `--ink-4` à l'octet : le même gris pour deux rôles, un filet et
   une encre ténue, qui restent deux jetons. `--meter-off` garde le sien :
   2,31:1 reste sous le plancher d'une jauge. Ce qui, dans ce fichier, lit
   ces jetons ailleurs que sur une bordure fonce avec eux et ne porte aucun
   texte : `.rule` (un filet plein), le fond de `.stat-strip` (il ne se voit
   que dans l'interstice d'un pixel entre des cellules opaques), le pouce de
   `.scroll`, le soulignement des liens de `.page`. Les pages de dernier
   recours (`global-error.tsx`) qui recopient `--line-strong` en dur suivent
   dans le même lot.

   Décision du propriétaire, même nuit, 4 h 12 (D42) : « le rose des
   pastilles changeons-le en bleu ». Deux variantes lui ont été montrées
   sur aperçu ; il a choisi la « A » : la voix des PROPOSITIONS MACHINE —
   la pastille du menu (`.badge-count`, « Revues 4 » chez Knowledge),
   `.pill.proposed`, `.banner.proposed`, `.insight.proposed` — passe du
   magenta au BLEU, dans les cinq blocs qui définissent ses jetons (jour,
   `[data-theme="light"]`, `.seuil.porte`, préférence système, nuit) :
     jeton                jour avant → après      nuit avant → après
     --proposed           #9c3f72 → #0a4fcc       #bc8daa → #6ca5ea
     --proposed-vif       #9c3f72 → #0a4fcc       #9c5782 → #3772c4
     --on-proposed        #ffffff, inchangé       #ffffff, inchangé
     --proposed-wash      #fcf1f4 → #eaf0fb       #2f1e2a → #1a2534
     --proposed-wash-hi   #fdf7f9 → #f3f6fd       #3a2634 → #212e40
   En JOUR, c'est le bleu du monogramme, celui de `--accent` : la pastille
   et l'accent deviennent la même couleur, par décision du propriétaire —
   ce n'est plus la teinte qui dit « une machine propose », c'est la forme
   et le mot. La NUIT, l'accent est framboise (#c93d67) et la pastille ne
   le prend pas : `--proposed-vif` y vaut #3772c4, le `--fam-regulation`
   que `.seuil.porte` porte déjà (la porte ne suit pas le thème), et
   `--proposed` #6ca5ea, le `--fam-regulation` de nuit ; les deux lavis
   gardent la clarté OKLab des lavis magenta (0,261 et 0,298), dans la
   teinte de #3772c4. Mesuré (luminance relative WCAG ; entre parenthèses,
   la 3.14.1) :
     blanc sur --proposed-vif           jour 6,99:1 (6,24)   nuit 4,81:1 (5,12)
     --proposed-vif sur --paper         jour 6,99:1 (6,24)   nuit 3,92:1 (3,68)
     --proposed sur --paper             jour 6,99:1 (6,24)   nuit 7,37:1 (6,76)
     --proposed sur --proposed-wash     jour 6,11:1 (5,65)   nuit 6,04:1 (5,62)
     --proposed sur --proposed-wash-hi  jour 6,46:1 (5,89)   nuit 5,37:1 (5,00)
   Les FAMILLES ne bougent pas : `--fam-governance` (#d95c7a, les carrés
   PO et CT) reste rose — la variante B, qui les passait en bleu, les
   aurait confondues avec IN. WCAG 1.4.11 (3:1) reste non visé pour les
   bordures, le cran 3 à 3:1 a été montré et écarté ; la variante B
   (familles en bleu) aussi.

   ── 3.14.1 — en mode jour, les bordures se voient un peu mieux ──────────
   Demande du propriétaire, 16 octobre 2026 : « en mode jour on ne voit pas
   assez les bordures, il faut les augmenter un petit peu, pas trop ». Deux
   jetons bougent, dans les trois blocs CLAIRS — `:root`,
   `:root[data-theme="light"]` et la porte `.seuil.porte`, qui recopie le
   jour —, et rien dans le sombre :
     `--line`         rgb(200 205 213 / 0.45) → rgb(200 205 213 / 0.7)
                      sur le papier #ffffff : #e6e8ec → #d8dce2, 1,22:1 → 1,38:1
     `--line-strong`  rgb(200 205 213 / 0.8)  → rgb(186 192 201 / 0.9)
                      #d3d7dd → #c1c6ce, 1,44:1 → 1,71:1
   (luminance relative WCAG, composition alpha sur `--paper`). Le trait de
   la 3.1.7 (« le contour est trop foncé ») tenait 1,22 et 1,44 ; la 3.1.5
   l'avait porté à 3:1 et c'était trop. Le propriétaire demande un cran, pas
   la cible : WCAG 1.4.11 veut 3:1 pour une bordure qui identifie un
   composant, aucune des deux valeurs ne l'atteint, et ce fichier le dit
   plutôt que de le laisser croire. Ce qui ne bouge pas, et pourquoi :
   `--glass-line` (0.55, 1,28:1 — le filet de la barre du haut, de la barre
   latérale et des tiroirs) reste le plus léger des trois, comme la 3.1.6
   l'a voulu ; `--ink-4` (#c8cdd5, le même gris) est une encre, jamais un
   filet ; `--meter-off` a son jeton depuis la 3.9.0 parce que
   `--line-strong` ne suffisait pas — il ne suffit toujours pas. Les deux
   commentaires qui disaient « ne peut pas monter (décision 3.1.7) » sont
   réécrits : il a monté d'un cran, sur décision du propriétaire, et reste
   sous 3:1. Les entrées 3.1.7 et 3.9.0 ci-dessous datent et restent. Les
   pages de dernier recours (`global-error.tsx`) de chaque produit, qui
   recopient `--line-strong` en dur faute de pouvoir charger ce fichier,
   suivent dans le même lot.
   Préparée sous le numéro 3.13.2 ; la 3.14.0 (la porte au bleu du
   monogramme) a été fusionnée avant elle dans six dépôts le 17 octobre, et
   cette version porte les deux. Elle ne touche à aucun jeton de la 3.14.0.

   ── 3.14.0 — la porte passe au bleu du monogramme ───────────────────────
   Décision du propriétaire, 16 octobre 2026 : « les portes d'entrée sign in
   des apps sont à l'ancienne charte, toujours vert forêt ». C'est la question
   que la 3.12.0 avait laissée ouverte, écrite plus bas en toutes lettres :
   « SA FAMILLE `--accent*` RESTE AU FORÊT […] décider si la porte doit passer
   au bleu du monogramme n'est pas une correction, c'est une question pour le
   propriétaire. » Elle est tranchée.

   `.seuil.porte` recopie les jetons de `:root` pour ne pas suivre le thème ;
   sa famille d'accent était la seule à ne pas les recopier. Les sept jetons
   y prennent la valeur du jour, octet pour octet :

     jeton                avant      après
     --accent             #184A45 →  #0A4FCC   6,99:1 sur --paper
     --accent-hover       #1F605A →  #0741B8   blanc dessus 8,55:1
     --accent-hi          #1F605A →  #0741B8
     --accent-active      #1F605A →  #0741B8
     --accent-hover-hi    #22665F →  #0A3D99
     --accent-ink         #123833 →  #05349A   10,69:1 sur --paper
     --accent-wash        #E4E7E4 →  #E7EDFA   l'accent à 10 % sur le papier

   CE QUE LE LECTEUR VOIT : le bouton « Se connecter », le trait sous le titre,
   le contour d'un champ qui a le focus et le lien de la porte de démonstration
   passent du vert forêt au bleu — sur les sept portes à la fois, puisque
   toutes posent `.seuil.porte`. Le bloc dit désormais vrai : il recopie
   `:root`, sans exception. `--brand-forest` y reste à #184A45, comme en
   `:root` : c'est une entrée de la palette du conseil, aucune règle ne la
   lit.

   CE QUI NE BOUGE PAS : la nuit (la porte ne la suit pas), et toute autre
   règle — `contraste:check` relit les couples de la porte avec les valeurs du
   jour, qu'il tenait déjà pour `:root`.

   ── 3.13.1 — la personne au téléphone, et ce que les sept relevaient seuls
   Décision du propriétaire, 10 octobre 2026 (D21) : sous 768px la barre du
   haut montre les INITIALES de la personne connectée — une pastille de 44px
   au doigt —, le nom ENTIER vit dans le MENU DU COMPTE qu'elle ouvre et dans
   le nom accessible du bouton ; à 768px et au-dessus, rien ne change. La
   coquille passe à 1.12.0 dans le même lot : le bouton, le menu, et une
   classe sur le lien de la marque.
   Et ce que les relevés d'écrans des sept produits (6-9 octobre 2026)
   avaient corrigé chacun dans SA feuille, alors que la cause est ici :
     1. six cibles que le bloc tactile ne nommait pas — le lien de la
        marque, le champ de la palette, le lien seul d'une cellule, d'un
        fait ou d'un fil d'Ariane, « Mot de passe oublié ? », la rangée
        d'objet qu'on touche, le champ d'édition en place ;
     2. `.chip` tenue à 24px même quand elle porte une commande ;
     3. l'ellipse sur des NOMS (`.object-row`, `.palette-row`), que la
        décision Q-B du propriétaire réserve aux jetons techniques ;
     4. un titre au mot plus long que l'écran, qui faisait défiler la page ;
     5. la pastille de statut étirée sur toute la largeur d'une carte ;
     6. le point d'une pastille qui enveloppe, écrasé en trait ;
     7. le résumé d'un repli qui dépassait de 4px sa rangée d'actes ;
     8. trois lettres sous le plancher de 11,5px — `.fi`, `.fam`,
        `.fam.sm` —, que `cible:check` exemptait.
   Chacun est dit, avec ce qui était mesuré, au § « 3.13.1 », en fin de
   fichier ; les points relevés et ÉCARTÉS sont dans la livraison. Aucune
   valeur de jeton ne bouge.

   ── 3.13.0 — la qualité d'iOS : rien ne sort, rien ne se hache ─────────
   Demande du propriétaire, 29 septembre 2026 : « la qualité d'iOS partout »
   — aucun libellé hors de son bouton, de sa pastille, de sa tuile ou de sa
   carte ; aucune page qui défile en largeur ; aucun mot haché lettre à
   lettre ; 44px au doigt ; à toutes les largeurs. Le relevé des sept
   produits a remonté six familles dont la CAUSE est dans ce fichier ou dans
   la coquille, pas dans les produits. Elles sont corrigées ensemble dans le
   § « 3.13.0 — LA QUALITÉ D'iOS », en fin de fichier, qui dit pour chacune
   ce qui était mesuré et ce qui change :
     1. `.pill` et `.chip` à hauteur fixe sans `nowrap` ;
     2. `overflow-wrap: anywhere` sur TOUTES les cellules sous 768px ;
     3. la barre du haut à 375px, la recherche réduite à « R… » ;
     4. le plancher tactile à 40px, sous les 44 d'iOS ;
     5. la carte de la porte : un courriel en ellipse, un jeton qui dépasse ;
     6. `.stat-strip` : des tuiles inégales, des chiffres décalés.
   Rien n'est retiré sans être NEUTRALISÉ par une déclaration écrite : la
   feuille s'éprouve en l'ajoutant après l'ancienne, sur la plateforme
   vivante. Aucune valeur de jeton ne bouge. La coquille passe à 1.11.0 dans
   le même lot : la recherche et la déconnexion portent leur nom sur le
   bouton, ce qui permet à la charte d'en retirer le mot sur un téléphone.

   ── 3.12.2 — les numéros de la porte d'entrée tiennent le plancher ─────
   `.seuil-index` était en `clamp(11px, 0.85vw, 13px)` : 11px entre le
   téléphone et 1 294px de large, sous le plancher de 11,5px que la charte
   se donne. `cible:check` ne lisait pas les `clamp()` et ne pouvait pas le
   voir ; CIBLE 1.6.0 les lit par leur plancher. Le plancher du `clamp`
   passe à 11,5px ; le reste de la règle ne bouge pas.

   ── 3.12.0 — le bleu redescend à l'accent, le vert remonte au succès ────
   Décision du propriétaire, 19 septembre 2026 : « en mode clair le bleu ne
   doit s'afficher qu'aux mêmes endroits que la framboise en sombre ; le reste
   reprend les couleurs du sombre aussi. »

   MESURÉ D'ABORD, sur le tableau de bord exécutif de Collect, en comparant
   les styles calculés des deux thèmes élément par élément :

     éléments portant la valeur de `--accent`      clair 14   sombre  2
     éléments portant la valeur de `--ok-wash`     clair  2   sombre  1

   QUATORZE CONTRE DEUX, et pas une règle ne s'était trompée. En clair
   `--accent`, `--ok`, `--ok-vif` et `--app-knowledge` valaient exactement le
   même pixel, #0A4FCC : douze éléments qui disent un SUCCÈS étaient peints en
   ACCENT sans que quiconque l'ait demandé, et rien ne pouvait le voir puisque
   chaque règle, prise seule, lisait le bon jeton. ΔE₀₀ entre l'accent et le
   succès : 0,00 le jour, 70,3 la nuit.

   CE N'EST PAS UNE DÉCISION NEUVE, C'EST LA RÉPARATION DE LA 3.5.0 FAITE SUR
   L'AUTRE THÈME, et ce fichier la raconte lui-même. `--ok` était un ALIAS de
   `--accent` — c'est le titre de la 3.1.4, « accent et ok sont le même vert,
   et le fichier le dit », du temps du vert forêt #184A45. La 3.4.0 a défait
   l'alias la nuit en écrivant « LE VERT RESTE AU SUCCÈS. `--ok`, `--ok-wash`,
   `--ok-vif`, `--on-ok` et `--ok-wash-hi` ne bougent pas : le propriétaire l'a
   demandé explicitement » ; la 3.5.0 a ajouté qu'un ancien accent laissé en
   place « n'était plus une décision de palette, c'était un reste ». LE CLAIR
   N'A JAMAIS REÇU CETTE CONSIGNE. Quand la 3.11.0 a porté l'accent au bleu du
   monogramme, l'alias a suivi — et avec lui `--ok-wash`, `--ok-vif` et
   `--app-knowledge`, qui suivaient l'accent sans être l'accent.

   LES SEPT JETONS `--accent*` NE BOUGENT PAS : c'est eux, le bleu voulu.

   LA MÉTHODE EST CELLE DE LA 3.5.0 — teinte du sombre, luminosité refaite
   pour le papier clair. Les valeurs du sombre NE SONT PAS recopiées : elles
   sont calibrées pour un fond sombre : #52B075 ne rend que 2,68:1 sur du
   blanc et #3FAFA0 2,67:1, très au-dessous du plancher de 4,5.

     jeton               avant       après      ce que dit la mesure
     --ok-vif (surface)  #0A4FCC  →  #52B075    LA VALEUR DE LA NUIT, à l'octet
     --on-ok  (dessus)   #FFFFFF  →  #0B1A11    LA VALEUR DE LA NUIT, à l'octet
     --app-knowledge     #0A4FCC  →  #3FAFA0    LA VALEUR DE LA NUIT, à l'octet
     --ok     (texte)    #0A4FCC  →  #2D6341    7,06:1 sur --paper
     --ok-wash           #E7EDFA  →  #EAEFEC    le pigment à 10 %
     --ok-wash-hi        #F0F0EE  →  #F2F5F3    le pigment à 6,2 %

   LA DEMANDE ÉTAIT « LA MÊME COULEUR QU'EN SOMBRE », ET ELLE EST TENUE LÀ OÙ
   LA COULEUR SE VOIT. Partout où le succès fait SURFACE — `.pill.ok` et la
   jauge `.fi.f`, les deux seuls emplois de `--ok-vif` — le jour porte
   désormais le vert de la nuit sans un octet d'écart, et le texte posé dessus
   est le sien aussi (#0B1A11, presque noir). Ce n'est plus un vert dérivé qui
   ressemble : c'est le même.

   UNE SEULE EXCEPTION, ET LA MESURE L'IMPOSE. `--ok` sert de TEXTE à un seul
   endroit du fichier, `.editable .saved .okmsg`, un « enregistré » de 11,5 px.
   #52B075 en texte sur du papier blanc rend 2,68:1 pour un plancher de 4,5 :
   `contraste:check` refuse, et il a raison — à ce contraste un texte n'est pas
   discret, il est absent. Le rôle de texte garde donc un vert descendu en
   luminosité, de même teinte (142,2° contre 142,3°). Mesuré : copier les trois
   valeurs de la nuit sans distinguer les rôles fait tomber TROIS couples à
   2,68:1 ; en distinguant surface et texte, il n'en tombe aucun.

   Les lavis restent clairs, et ce n'est pas un compromis : #1A2A1F et #1D3626
   sont des lavis de fond SOMBRE. Posés sur du papier blanc ils feraient des
   pavés presque noirs sous le texte, c'est-à-dire l'inverse d'un lavis.

   POURQUOI CE VERT-LÀ POUR LE TEXTE, ET PAS UN AUTRE. Sa teinte est 142,2° quand celle du
   vert de la nuit est 142,3°, et sa saturation 37,5 % contre 37,3 % : c'est le
   MÊME vert, descendu en luminosité jusqu'à peser sur le papier clair ce que
   celui de la nuit pèse sur le sien — 7,06:1 contre 7,03:1. Il pèse aussi ce
   que pesait le bleu qu'il remplace, 6,99:1 : une jauge d'avancement, une
   pastille de succès, un message d'enregistrement gardent EXACTEMENT la
   présence qu'ils avaient, ils cessent seulement d'être bleus. C'est le
   raisonnement de la 3.5.0 mot pour mot, luminance conservée et teinte
   corrigée.

   POURQUOI #2D7F74 POUR KNOWLEDGE, ET POURQUOI CE N'EST PAS UN RETOUR À
   #184A45. Sa teinte est 171,95° contre 172,0° pour le turquoise de la nuit :
   c'est la couleur du PRODUIT, pas celle de la maison. Sa luminosité, elle,
   est donnée par la famille et non par le goût — les trois autres `--app-*`
   du jour valent 4,74:1, 4,75:1 et 4,74:1 sur le papier, trois valeurs
   identiques à deux décimales qui ne sont pas un hasard ; #2D7F74 vaut 4,77:1
   et entre dans la famille. #184A45, qu'il portait avant la 3.11.0, en valait
   9,98 : il n'était pas un membre calibré de cette famille, il était la
   couleur de la maison empruntée à un produit. Un produit dont la couleur est
   celle d'un autre ne s'identifie plus ; ΔE₀₀ le dit aussi — 19,02 du plus
   proche voisin (`--app-collect`), au-delà du seuil de confusion de 17,15 que
   la 3.1.8 s'est donné, et 14,00 du nouveau vert de succès, là où la nuit
   sépare son turquoise de son vert de 13,18. Le jour les séparait de 0,00.

   LES DEUX LAVIS SUIVENT LA CONSTRUCTION ÉCRITE, pas une teinte choisie à
   l'œil : `--accent-wash` est l'accent à 10 % sur le papier — #0A4FCC à 10 %
   sur #FFFFFF rend #E7EDFA exactement — et `--ok-wash` est donc le vert à
   10 %. `--ok-wash-hi` est le haut du dégradé que `-wash` ferme : dans la
   famille du jour il se tient entre 0,595 et 0,703 de la distance du lavis au
   papier, et le couple `--ok` en tenait 0,603 avant la 3.11.0 ; #F2F5F3 en
   tient 0,625. L'ancienne valeur #F0F0EE était le lavis haut du VERT FORÊT,
   restée grise sous un lavis devenu bleu — un troisième reste, que la 3.11.0
   n'avait pas vu parce qu'il ne contient aucun bleu.

   LA PORTE AUSSI, ET C'EST LE CINQUIÈME SITE DU MÊME DÉFAUT. `.seuil.porte`
   recopie les jetons de `:root` et ne suit pas le thème. La 3.11.0 y a laissé
   `--ok: #184A45` et `--ok-wash: #E4E7E4` — le forêt — mais y a porté
   `--ok-vif` et `--app-knowledge` à #0A4FCC : un bloc dont l'accent est le
   vert forêt affichait donc une pastille de succès BLEUE, seul bleu d'une page
   qui n'en a aucun autre. Les cinq jetons y prennent les mêmes valeurs qu'en
   `:root`, ce que le bloc dit faire. SA FAMILLE `--accent*` RESTE AU FORÊT :
   la 3.11.0 ne l'a pas portée au bleu, et décider si la porte doit passer au
   bleu du monogramme n'est pas une correction, c'est une question pour le
   propriétaire. (Tranchée le 16 octobre 2026 : la porte passe au
   bleu, voir la 3.14.0.)

   REMESURÉ APRÈS, même page, même méthode : clair 2 et sombre 2 pour
   `--accent`, clair 1 et sombre 1 pour `--ok-wash`, et les douze éléments de
   succès portent le vert dans les deux thèmes. `contraste:check` tient ses
   129 couples par thème au-dessus de 4,5:1 dans les quatre thèmes.

   LA MESURE MENTAIT TANT QU'ON NE GELAIT PAS LES TRANSITIONS. Les barres de
   `.bg-ok` portent `transition-all` : un style calculé lu juste après avoir
   changé `data-theme` rend la couleur de DÉPART, pas celle d'arrivée. Le
   relevé était alors en retard d'un basculement et rendait 14, 8 puis 10 sur
   trois passes identiques. Injecter `*{transition:none!important}` avant de
   basculer le rend stable et reproductible. Une mesure de style calculé qui
   ne gèle pas le mouvement n'est pas une mesure.

   `jumeaux:check` ROUGIRA, ET C'EST VOULU : la propagation aux six autres
   dépôts est laissée à la relecture du propriétaire. Les sept ne portent plus
   le même fichier tant qu'elle n'a pas eu lieu.

   ── 3.11.0 — le jour passe au bleu du monogramme, la nuit ne bouge pas ─
   Décision du propriétaire, 18 septembre 2026 : le vert forêt cesse d'être
   la couleur de la maison en mode clair. Le bleu retenu est celui du
   monogramme Y du site — le favicon de www.yntgyst.com — #0A4FCC. Un
   premier bleu, #146BFF, avait été posé le matin même : 4,6:1 sur le papier,
   sans marge, et trop vif au regard du propriétaire (« quiet luxury »). Il
   est retiré partout, favicons compris.

   Onze valeurs changent, toutes dans les deux blocs de JOUR (`:root` et
   `[data-theme="light"]`), et chaque jeton descend d'un cran sur la même
   échelle que la 3.10.0 lui donnait :

     · `--accent`                  #184A45 → #0A4FCC    6,99:1 sur --paper
     · `--accent-hover`, `-hi`,
       `-active`                   #1F605A → #0741B8    blanc dessus 8,55:1
     · `--accent-hover-hi`         #22665F → #0A3D99    9,81:1 sur --paper
     · `--accent-ink`              #123833 → #05349A    10,69:1 sur --paper
     · `--accent-wash`             #E4E7E4 → #E7EDFA    l'accent à 10 % sur
                                                        le papier ; `-hi`
                                                        dessus 7,3:1
     · le succès suit, parce qu'en jour l'accent et le succès sont le même
       pixel depuis la 3.1.4 et le fichier le dit : `--ok` et `--ok-vif`
       #0A4FCC, `--ok-wash` #E7EDFA. `--on-ok` reste blanc.
     · `--app-knowledge` (jour)    #184A45 → #0A4FCC — la famille Knowledge
       portait la couleur de la maison ; elle la suit. La nuit garde #3FAFA0.

   La NUIT garde la framboise de la 3.4.0 et son vert de succès : aucun
   jeton des blocs sombres ne bouge. `--brand-forest` reste #184A45 : c'est
   une entrée de la palette du conseil, aucune règle ne la lit, et un jeton
   qui s'appelle forêt ne peut pas valoir bleu.

   Mesuré : `contraste:check` tient ses 129 couples par thème au-dessus de
   4,5:1 dans les quatre thèmes, et le bleu laisse cette fois de la marge.

   POURQUOI UNE VERSION, ET PAS UN RESCEAU. Le bleu a d'abord été posé sur la
   3.10.0 chez Collect sans toucher au sceau, et chez les six autres sur une
   copie datant de la 3.8.0 : la fusion « accepter le changement bleu » a
   gardé le côté de la branche, et six `main` ont perdu la 3.9.0 et la
   3.10.0 — mouvement, focus, états, plancher typographique, cibles au doigt
   — pendant que les sept sceaux rougissaient. C'est la seconde fois que la
   résolution d'une fusion défait ce que `main` portait (voir 3.1.7). La
   3.11.0 est la réconciliation : le corps de la 3.10.0, le bleu, un sceau
   juste, recopiée octet pour octet chez les sept. Quand une fusion touche ce
   fichier, on garde le côté `main` et on réapplique le changement dessus ;
   jamais l'inverse.

   ── 3.9.0 — quatre textes que personne ne pouvait lire ─────────────────
   Mesuré le 16 septembre 2026 : rien ne calculait les contrastes de ce
   fichier. `charte:check` constate qu'on y a touché sans le dire,
   `jumeaux:check` que les sept copies s'accordent — et sept copies peuvent
   s'accorder sur un texte que personne ne lit. C'est déjà arrivé : la 3.1.7
   a ramené `--app-planned` de 3,28:1 à 1,54:1 en résolution de fusion, les
   quatre sceaux d'accord sur la mauvaise valeur.

   Quatre couples rendaient moins de 4,5:1, le plancher AA du texte :

     · le chiffre de chapitre de la barre latérale — « 01 », « 02 », « 03 »,
       visible sur les sept applications — 3,41:1 en jour, 3,55:1 en nuit.
       `--nav-chapter` passe à #777673 / #7b7c82 : 4,54:1 et 4,53:1, les
       valeurs les plus CLAIRES qui tiennent le plancher. Le chiffre reste
       plus discret que le texte qu'il numérote, il cesse d'être absent.
     · l'accent posé en TEXTE sur son propre lavis (`.cite:hover`) : 3,60:1
       en nuit, du framboise sur du framboise à 10 % ;
     · l'accent en texte sur le papier de nuit (`.seuil-entree:hover .nom`) :
       3,90:1.
       Les deux lisent désormais `--accent-hi`, la variante haute lisibilité
       qui existait déjà pour cela — 5,85:1 et 7,29:1 en jour, 4,65:1 et
       5,03:1 en nuit. AUCUNE VALEUR DE JETON NE BOUGE pour ces deux-là :
       c'est la règle qui lisait la mauvaise variable.
     · le séparateur du fil d'Ariane, en quatrième encre : 1,60:1. La
       quatrième encre est décorative par décision, et `encres:check` le
       tient ; un séparateur à ce contraste n'est pas discret, il est absent,
       et les miettes se lisent comme une phrase continue. `--ink-3`.

   ── 3.9.0, LE RESTE — douze gestes nés de l'audit des sept produits ────
   Huit lecteurs ont relu les 181 écrans de la plateforme et relevé 327
   défauts. Ce qui suit est ce qui se corrige ICI, une fois, pour les sept —
   le reste vit dans les écrans.

   CE QUI NE CHANGE AUCUNE VALEUR :
     · Le MOUVEMENT. `prefers-reduced-motion` n'arrêtait que le spinner du
       bouton ; les treize transitions du fichier, le `translateY` du survol
       et le lien d'évitement tournaient encore. Six produits sur sept
       n'avaient rien en local (Collect seul le neutralisait chez lui).
     · Le FOCUS passe à 2px et cesse de redessiner ce qu'il entoure — une
       marque ronde devenait carrée au focus, faute d'un `border-radius`
       imposé par la règle globale. Le jour, `--accent-glow` est transparent :
       1,5px était tout ce qu'on voyait.
     · Les ÉTATS d'un champ et d'un bouton : `[disabled]` est un attribut,
       `:disabled` est un état — un bouton désactivé par un `<fieldset
       disabled>` (les quatre ActionForm de la plateforme) gardait son aplat,
       son survol et son curseur pendant tout l'envoi. Un champ en lecture
       seule se dit par un trait pointillé, pas par rien.
     · Les CELLULES s'alignent en haut. Dans une rangée haute, le lien de la
       première colonne flottait à cent pixels de son en-tête.
     · Le PLANCHER TYPOGRAPHIQUE : ce qui PORTE une information monte —
       l'étiquette d'un champ à 12px sur `--ink-2` (elle rendait 4,23:1 sur
       `--surface-3`), la marque à 11,5px. `.register` ne bouge pas : c'est
       l'identité d'une section, pas une étiquette qu'on lit en saisissant.
     · Les LIENS DE PROSE se voient sans la couleur (1.4.1) : un filet qui
       devient l'encre au survol. La page, jamais le chrome, et seulement les
       `<a>` sans classe.
     · Les CHIPS interactives disent qu'elles le sont, et la chip ACTIVE se
       distingue par l'encre pleine — elle se distinguait par `--surface-3`
       sur `--surface-2`, soit 1,07:1, soit rien.
     · La DÉCONNEXION rejoint les 28px de ses voisines : c'était la seule
       commande de la barre du haut sous le plancher de la cible.

   DEUX JETONS NEUFS :
     · `--meter-off`, la barre éteinte d'une jauge. Elle porte le
       DÉNOMINATEUR — sans elle on lit « deux barres » au lieu de « deux sur
       cinq » — et rendait 1,44:1. `--line-strong` ne peut pas monter
       (décision 3.1.7), d'où un jeton dédié tenu à 3:1.
     · `--voile`, la recette unique du voile d'un dialogue. Collect écrivait
       quatre opacités d'un noir hors jeton dans vingt-trois fichiers ; un
       noir survit au thème sombre, un papier le suit.

   QUATRE DESSINS QUE LES PRODUITS ÉCRIVAIENT CHACUN DANS SON COIN :
   `.repli` (le tiroir natif `<details>`, huit fois stylé en ligne à 17px de
   haut chez Console), `.table.cartes` (un tableau se lit en cartes sous
   640px — quinze tableaux de Collect sur dix-neuf n'avaient aucun repli),
   `.page-head` / `.section-aside` / `.card-title` / `.facts` / `.stat-strip`,
   et `.btn.langue` (six coquilles forçaient 10,5px sur deux capitales).

   ET LA CIBLE AU DOIGT. Tout tient entre 24 et 32px : le plancher de justesse,
   rien près de la recommandation. Un bloc `pointer: coarse` porte les
   commandes à 40px sans toucher au dessin de bureau, et les champs à 16px sur
   téléphone — sous cette taille, iOS zoome la page au focus et ne dézoome
   jamais.

   ET UNE BARRIÈRE, pour que cela ne revienne pas par une fusion :
   `contraste:check` lit les RÈGLES de ce fichier — toute déclaration qui
   pose une couleur de texte, avec le fond que sa règle pose — compose les
   surfaces translucides sur leur fond, et mesure les deux thèmes. 103
   couples par thème. Les exemptions y sont motivées une par une : le point
   médian de la porte est un pseudo-élément, hors de l'arbre
   d'accessibilité — 1.4.1, pas 1.4.3.

   ── 3.7.4 — une pastille peut envelopper, si on le lui demande ─────────
   `.pill` a une hauteur de 20 px depuis toujours. C'est juste pour un mot, et
   faux dès que le texte passe à la ligne : le fond ne suit pas, et les
   caractères sortent par le bas. Le défaut est resté invisible tant que
   chaque produit écrivait ses propres libellés — courts, mesurés pour la
   place. Il est apparu sur un écran qui affiche des statuts venus de QUATRE
   produits différents, chacun les ayant rédigés pour son propre écran : la
   longueur n'y était plus la sienne à contrôler.

   `.pill.wrap` est donc une variante, jamais le défaut. Les pastilles
   existantes ne bougent pas d'un pixel — `min-height` reprend exactement les
   20 px, et rien d'autre ne change tant qu'une seule ligne suffit.

   DEUX CHOSES CHANGENT QUAND ELLE ENVELOPPE, et les deux sont la conséquence
   de la première. Le RAYON se carre : une forme de stade sur deux lignes
   mange son propre texte aux extrémités, et aucune valeur de remplissage n'y
   remédie. Et la pastille de couleur remonte sur la PREMIÈRE ligne au lieu
   de se centrer sur le bloc — sur un libellé de deux lignes, elle marque le
   début, pas le milieu.

   Un écran qui affiche des mots qu'il n'a pas écrits ne doit pas les
   enfermer dans une boîte de taille fixe ; c'est à lui de demander la
   variante, et le nom de la classe le dit.

   ── 3.4.5 — la porte ne garde que le logotype ──────────────────────────
   « Enlève ça » : la ligne devise + module disparaît de l'écran d'entrée,
   après deux dispositions essayées. Le logotype suffit à dire la maison ; le
   module se lit dans l'URL, dans le titre de l'onglet et sur chaque écran
   d'après. Masqué, pas supprimé — le balisage doit demeurer et l'accueil de
   la plateforme garde sa ligne. L'air qu'elle portait passe au logotype.

   ── 3.4.4 — la devise et le module se lisent d'un trait ────────────────
   Le brief posait le nom du module « en haut à droite », et c'est ce qu'on a
   fait tant que la composition était calée à gauche. Une fois le logotype
   réduit et centré, ce nom restait SEUL à l'autre bout de la page, sans rien
   pour le tenir. Les deux forment désormais une ligne unique, centrée sous le
   logotype, séparés par un point médian : la devise dit ce que fait la maison,
   le module dit où l'on entre, et un seul centre porte les deux.

   ── 3.4.3 — le bandeau des portes à formulaire s'allège ────────────────
   Le logotype passe de 58vw (bornés 300-980) à 30vw (bornés 200-420) et se
   centre ; la devise le suit ; le nom du module reste épinglé à droite, ce
   qui demande un positionnement absolu — seule façon de centrer l'une sans
   déplacer l'autre. L'air sous le bandeau double : le vide est ici la seule
   chose entre la marque et l'acte. Rien de tout cela ne touche l'accueil de
   la plateforme, qui garde son logotype large au-dessus de sa table.

   ── 3.4.2 — le formulaire de connexion se centre ───────────────────────
   Décision du propriétaire : « the login form MUST be centered horizontally »,
   ~440px, un peu au-dessus du centre exact, sans carte ni panneau ni ombre.
   Le ciblage passait par `:has(.seuil-form)` ; depuis la 3.6.0 il passe par
   `.seuil.porte`. Le § seuil sert DEUX compositions — les portes d'entrée et
   l'accueil de la plateforme, qui pose une table de produits en pleine
   largeur. Centrer sans condition aurait rétréci cette table.
   L'en-tête ne bouge pas : le brief le redécrit à l'identique et demande de
   ne pas toucher à l'identité. Le logotype tient le cadre, le formulaire
   devient le foyer.

   ── 3.4.1 — le formulaire du seuil tient la fourchette demandée ───────
   `.seuil-form` passe de 420 à 460px. Le propriétaire a donné une fourchette
   — « approximately 440–480px » — et 420 tombait dessous. Rien d'autre ne
   bouge : la table des comptes de démonstration GARDE sa pleine largeur,
   décision antérieure du même propriétaire (« prend toute la largeur pour
   les textes, je ne vais pas le répéter »). Le caractère secondaire de cette
   section se joue sur le poids — capitales de 10px, encre tertiaire — pas
   sur la laisse.

   ── 3.4.0 — la nuit passe à la framboise, le jour ne bouge pas ─────────
   Décision du propriétaire, avec la palette à l'appui : « remplacer l'accent
   turquoise/vert par une identité framboise, mais UNIQUEMENT en dark mode »,
   « ne modifie absolument pas le light mode », « la framboise est la signature
   d'interaction, pas une couleur décorative omniprésente ».

   CE QUI CHANGE, ET SEULEMENT LÀ. La charte déclare chaque thème QUATRE fois :
   `:root`, le bloc `prefers-color-scheme: dark`, `:root[data-theme="light"]`
   et `:root[data-theme="dark"]`. Deux sont clairs, deux sont sombres. Seuls
   les deux blocs sombres ont bougé, et la bascule a été faite par un découpage
   à accolades appariées qui REFUSE d'écrire si un bloc clair diffère ensuite
   d'un seul octet. Le Deep Forest #184A45 du jour est intact.

     --accent            #3FAFA0 → #C93D67    3,90:1 sur --paper (aplat, ≥3)
     --accent-hover      #4DBFAE → #8F2948    ivoire dessus 7,84:1
     --accent-active                #6F2038   ivoire dessus 10,42:1 (nouveau)
     --accent-ink        #7FCDC0 → #E24E7A    5,03:1 sur --paper (texte, ≥4,5)
     --on-accent         #0B1A18 → #FDFAF8    4,65:1 sur l'accent
     --accent-wash       #1B292A → rgb(201 61 103 / 0.10)
     --accent-wash-hi    #1E3636 → rgb(201 61 103 / 0.18)
     --accent-glow                  rgb(201 61 103 / 0.16) (nouveau)

   POURQUOI L'ENCRE S'INVERSE. Ce n'est pas un goût, c'est de l'arithmétique.
   Sur #C93D67 le presque-noir #0B1A18 tombe à 3,70:1 et AUCUNE encre sombre
   n'atteint 4,5 — le noir pur plafonne à 4,35. L'ivoire donne 4,65:1. Le
   texte posé sur l'accent devait donc passer du noir au blanc cassé.

   POURQUOI LE SURVOL S'ASSOMBRIT. Sur le couple imposé #C93D67 / #E24E7A,
   aucune encre unique ne tient : encre claire 4,65 puis 3,61 au survol, encre
   sombre 3,70 puis 4,77. Le survol aurait rendu le texte MOINS lisible qu'au
   repos. Décision du propriétaire : le fond survolé s'assombrit (#8F2948,
   ivoire à 7,84:1) et #E24E7A devient l'accent EN PREMIER PLAN — liens,
   icônes, bordures, textes interactifs — où il vaut 5,03:1 sur le fond.

   LE VERT RESTE AU SUCCÈS. `--ok`, `--ok-wash`, `--ok-vif`, `--on-ok` et
   `--ok-wash-hi` ne bougent pas : le propriétaire l'a demandé explicitement.
   C'est la première version où l'accent et le succès CESSENT d'être le même
   pixel — mais seulement la nuit. Voir §5 `.chip.accent`, dont la doctrine a
   été refaite : le piège qu'elle décrivait est désormais invisible en sombre
   et intact en clair.

   TROIS FUITES DE VERT QUI N'ÉTAIENT PAS DES JETONS. Elles auraient survécu à
   toute retouche de palette, et deux échappaient à tout grep hexadécimal :
     · `--paper-lit` portait `rgb(63 175 160 / 0.04)` — #3FAFA0 EN DÉCIMAL.
       C'est la lueur de fond de CHAQUE page en sombre.
     · `.nav-item[aria-current]` prenait son fond de `--brand-forest`, déclaré
       une seule fois HORS des quatre blocs, donc vert la nuit par héritage —
       sous un repère devenu framboise.
     · `.btn:hover` portait une ombre `rgb(24 74 69 / 0.1)`, du Deep Forest en
       décimal, dans une règle sans garde de thème.
   Les deux dernières sont réécrites en `var(--accent)` : en clair, `--accent`
   VAUT #184A45, donc le rendu du jour est inchangé au bit près.

   ── 3.3.0 — le seuil devient éditorial, et rien n'y porte de couleur ────
   Décision du propriétaire, donnée avec la composition à l'appui : « plus
   minimaliste, plus premium, plus éditorial », « les applications ne doivent
   pas être identifiées par une couleur décorative », « ne pas chercher à
   remplir l'écran — le vide fait partie du design ».

   CADRÉ, PLUS ÉTIRÉ. La 3.2.0 tirait le logotype d'un bord à l'autre de la
   fenêtre. Le seuil tient désormais dans 1120px avec de larges marges, et le
   logotype est borné à 470px — 57px de haut contre 31px quand il était une
   vignette de coin, et contre 155px quand il touchait les deux bords. Il
   domine sans devenir une enseigne.

   C'EST LA QUATRIÈME EXCEPTION DE MESURE, ET ELLE EST DEMANDÉE. La règle de
   cette maison est que la prose occupe la page ; elle avait été refusée cinq
   fois, et `largeur:check` avait refusé la mienne en 3.2.0 — à raison, je
   l'avais posée « au cas où ». Celle-ci est écrite noir sur blanc par le
   propriétaire, et ne couvre que cinq pages sans prose longue.

   AUCUNE PASTILLE. Les points de couleur qui identifiaient les applications
   ont disparu. Ce qui les distingue est leur nom et l'ordre du registre.

   `--app-console` REPART AVEC EUX. Je l'avais ajouté en 3.1.8 pour que Console
   ait un point comme ses voisines ; ce point n'existe plus, et le jeton
   n'était employé nulle part ailleurs. Un jeton que rien ne lit est un jeton
   qui ment sur la palette, et la consigne était de ne pas y toucher — la
   retirer, c'est retirer ma propre addition. La famille revient à quatre.

   RIEN NE FLOTTE. Pas de carte, pas d'ombre, pas de conteneur autour d'un
   groupe, pas de fond différent. Ce qui sépare est un filet ; ce qui
   hiérarchise est l'échelle et l'espace.

   ── 3.2.0 — le seuil : cinq portes, une seule composition ───────────────
   Décision du propriétaire : « la page d'accueil de la plateforme et les
   pages de connexion, ce n'est pas assez minimaliste ni harmonisé, et ça ne
   met pas le logotype en valeur ».

   LE DIAGNOSTIC TENAIT, MA PREMIÈRE MESURE NON. J'avais écrit que le fût du
   logotype passait sous le pixel une fois réduit ; c'est faux — à 260px de
   large il fait 3,79px, parfaitement visible. Le vrai argument est de
   PROPORTION : le dessin est de rapport 8,225:1, donc à 260px il se tient sur
   31,6px de haut, la hauteur d'une ligne de texte. Une forme aussi allongée,
   réduite, se lit comme une étiquette. Tirée à la largeur de la page elle fait
   155px sur un écran de 1280 et redevient une architecture.

   § SEUIL. Une composition, cinq écrans : le logotype pleine largeur, la
   devise et le lieu sous lui, un filet, puis CE QUI CHANGE — les destinations
   pour l'accueil, le formulaire pour une connexion — et un pied. Les quatre
   panneaux d'image latéraux disparaissent : c'est eux qui faisaient de la
   connexion une autre maison que l'accueil.

   La devise reste, en petit, sous le logotype. Elle situe la plateforme sans
   concurrencer le nom.

   `seuil:check` refuse qu'une porte s'écarte de la composition. Une
   harmonisation sans barrière est une intention, pas un état : celle-ci s'est
   déjà défaite une fois, quand l'accueil a perdu son panneau et que les quatre
   connexions l'ont gardé.

   ── 3.1.9 — le dialogue cesse d'être une vitre, le sélecteur cesse de
   ── se justifier ────────────────────────────────────────────────────────
   Décision du propriétaire, prise à l'écran sur Collecte : « les popups sont
   toujours transparents, on ne voit pas », et « quand on sélectionne
   l'application en haut il y a la description de chaque application, enlève
   ça ».

   LE DIALOGUE. La 3.1.7 avait opacifié `.palette`, `.product-menu`, `.drawer`
   et `.fenetre`. Elle a manqué le cas le plus courant : le dialogue qu'une
   application dessine avec `.panel`. Cette classe porte `--elevated`, 72 % de
   blanc — sur le papier une carte, sur un tableau dense une vitre. Le
   sélecteur ajouté ne nomme pas une classe de plus mais un CONTRAT,
   `aria-modal`, que toute fenêtre modale déclare déjà pour les lecteurs
   d'écran : ce qui est modal est opaque, sans que personne ait à s'en
   souvenir.

   Il s'attache au panneau qui EST le dialogue, jamais à ses descendants —
   `[aria-modal] .panel` aurait aplati les cartes posées DANS la fenêtre, qui
   doivent rester distinctes de son fond.

   LE SÉLECTEUR. `.product-item .what` disparaît avec la ligne qui la
   remplissait. Un sélecteur d'application est un sélecteur : le nom est la
   réponse. La description reste dans l'annuaire de Console, où une liste a la
   place de la porter et où quelqu'un peut ne pas connaître le produit.

   ── 3.1.8 — la pastille « planifié » repasse au-dessus du plancher ──────
   `--app-planned` marque, dans le lanceur et dans les listes d'applications,
   celles qui ne sont pas encore ouvertes. C'est un GRAPHIQUE PORTEUR DE SENS :
   la pastille est la seule chose qui distingue une application disponible
   d'une application annoncée. WCAG 1.4.11 demande donc 3:1, et non le régime
   décoratif.

   Clair : #c8cdd5 → #8c8b88, soit 1,54:1 → 3,28:1 contre le papier #fdfaf8.
   Sombre : #6d666c inchangé, déjà à 3,38:1 contre #141014. Les deux thèmes
   se retrouvent ainsi à un cheveu l'un de l'autre.

   #8c8b88 reste à ΔE₀₀ 17,15 de son plus proche voisin (--app-collect), donc
   au-delà du seuil de confusion que la charte se donne.

   CE N'EST PAS UNE DÉCISION NEUVE, C'EST UNE RÉPARATION. `main` avait déjà
   monté cette valeur à #8c8b88 — mais dans src/styles/globals.css seulement,
   pas dans ce fichier. Les deux ont donc divergé, et la résolution de fusion
   de la 3.1.7 a refermé la divergence VERS LE BAS : elle a tiré la feuille
   servie sur la charte, annulant la correction. La divergence était réelle et
   visible ; le sens dans lequel on la referme ne l'était pas. C'est
   exactement ce que `tokens:check` sait voir et ce qu'un sceau ne peut pas.

   Rien d'autre ne bouge. Le trait de la 3.1.7 reste où le propriétaire l'a
   validé.

   ── 3.1.7 — le contour revient à son trait léger ────────────────────────
   Décision du propriétaire, prise à l'écran : « le contour est trop foncé,
   c'était mieux dans la version d'avant ». Les six valeurs de --line,
   --line-strong et --glass-line — jour et nuit — reviennent à ce qu'elles
   étaient avant la 3.1.5.

   LE PRIX EST CONSIGNÉ, PAS EFFACÉ. --line 1,20:1, --line-strong 1,40:1,
   --glass-line 1,26:1 contre le papier, là où WCAG 1.4.11 demande 3:1 pour
   une information visuelle nécessaire à identifier un composant. Le cas le
   moins discutable est la barre latérale, qui n'a pas de fond propre : son
   filet est la seule chose qui la sépare du contenu.

   LE RESTE DE L'AUDIT D'ACCESSIBILITÉ DE LA 3.1.5 EST INTACT — l'anneau de
   focus corail à 3:1, .kbd / .tab .count / .search-trigger passés en
   --ink-2, .chip de 20 à 24px. Seul le trait revient ; ce qui portait du
   texte ou une cible tactile reste où l'audit l'a mis.

   La voie qui rendrait la frontière lisible sans épaissir le trait est un
   écart TONAL entre les surfaces — la charte le recommande depuis sa
   section 7, et c'est là qu'il faudra regarder si la question revient.

   ── 3.1.6 — le filet de verre rejoint ses voisins au plancher ────────────
   La 3.1.5 a fait passer `--line` et `--line-strong` à 3:1 et laissé
   `--glass-line` à 1,26:1 le jour, 1,31:1 la nuit. L'omission se comprend :
   ce filet a l'air d'un ornement. Mesuré, il n'en est pas un — la barre
   latérale n'a PAS de fond propre (son `--glass` s'aplatit à 1,02:1 contre
   le papier en clair, 1,00:1 en sombre), donc il est la SEULE chose qui
   sépare la navigation du contenu. Une frontière qu'on ne voit pas n'en est
   pas une.

   Il passe à 3,05:1 le jour et 3,08:1 la nuit, en gardant la teinte du Mist
   et la forme que la 3.1.5 a posée sur ses voisins — Mist assombri à alpha
   haut le jour, Mist pur à alpha bas la nuit. Il reste le PLUS LÉGER des
   trois : verre 3,05 · ligne 3,31 · ligne forte 4,07. « Never a wall » plus
   bas dans ce fichier reste vrai : 3:1 est un filet, pas un mur.

   ── 3.1.5 — les filets tiennent enfin leur 3:1 ───────────────────────────
   `--line` mesurait 1,2:1 et `--line-strong` 1,4:1 contre le papier, dans
   les deux thèmes — loin sous le plancher que ces jetons existent pour
   porter. Trouvé par l'audit d'accessibilité d'Evidence. Assortis : `.kbd`,
   `.tab .count` et `.search-trigger` posaient `--ink-3` sur une surface trop
   proche, et l'anneau de focus corail des boutons ratait 3:1 de 0,05 en
   clair. `.chip` grandit de 20 à 24px, la seule cible tactile trouvée à
   manquer le plancher AA plutôt que seulement l'AAA.

   CETTE ENTRÉE A ÉTÉ ÉCRITE APRÈS COUP. La 3.1.5 a monté le numéro de
   version et rescellé sans rien inscrire ici : le journal est ce qu'un
   lecteur du fichier scellé voit, et un message de commit ne s'y substitue
   pas. Recomposée depuis le diff et remesurée — `--line` 3,31:1 le jour,
   3,35 la nuit ; `--line-strong` 4,07 et 4,28.

   ── 3.1.4 — accent et ok sont le même vert, et le fichier le dit ────────
   `--accent` et `--ok` portent exactement la même valeur, lavis compris, et
   rien ne l'écrivait : `.pill.accent` lisait `--ok-vif` sans un mot, et
   `--accent-vif` n'existe pas. Le prix a été payé dans Collect, où la table
   des campagnes donnait ACTIVE → accent et CLOSED → ok : deux états opposés
   portaient la même pastille. Aucune barrière ne pouvait le voir, les deux
   règles étant correctes prises séparément.

   Les deux alias sont désormais nommés comme tels, avec la règle qui en
   découle : accent et ok ne distinguent JAMAIS deux états. Un état « en
   cours » prend `.pill.info`, qui existait déjà. Aucune valeur ne change,
   aucune classe n'est ajoutée — une `.chip.info` a été écrite puis retirée
   dans la même heure, son seul appelant étant un statut, et un statut se rend
   en `pill`, jamais en `chip`.

   ── 3.1.3 — le prix de `overflow: hidden` est écrit ─────────────────────
   `.panel` coupe ce qui dépasse pour que ses enfants respectent ses coins
   arrondis, et c'est juste. Ce qui n'était écrit nulle part, c'est que le
   contenu coupé devient INATTEIGNABLE — pire qu'un débordement visible, et
   invisible à la mesure puisque le conteneur ne défile pas. Dix tableaux de
   Console perdaient leurs dernières colonnes à 375px. La règle qui va avec
   est désormais dans le fichier : un contenu qui peut dépasser se met dans
   un `.overflow-x` à l'intérieur du panneau.

   ── 3.1.2 — la charte cesse de se contredire ────────────────────────────
   Le §pastille de famille décrivait encore, en trois endroits, des états que
   les versions suivantes avaient remplacés : son EN-TÊTE annonçait le
   traitement tonal quand sa conclusion dit le pigment plein ; ses contrastes
   nommaient deux familles sous le plancher AA alors que la 3.1.1 les avait
   fait passer ; et deux paires y étaient déclarées identiques, ce qu'elles ne
   sont plus depuis qu'Opérations est passé à l'ambre. Un fichier scellé qui
   se contredit fait lire la mauvaise règle à qui s'arrête au titre. Aucune
   valeur ne change : ce sont les mesures et les titres qui rattrapent les
   couleurs.

   ── 3.1.1 — Réglementation passe le plancher ────────────────────────────
   L'azur #3A7BD5 tenait 4,06:1 sous sa lettre ivoire — la dernière famille
   sous le plancher AA, et la barrière le répétait à chaque exécution. Il
   descend à #3772C4 : ΔE₀₀ 3,78 du pigment d'origine, invisible sauf à les
   comparer côte à côte, et 4,63:1 sous l'ivoire. Il reste à ΔE₀₀ 12,4 de
   son plus proche voisin, l'indigo des Systèmes. Les neuf familles passent
   maintenant AA dans les deux thèmes, sans exception et sans constat.

   ── 3.1.0 — deux règles que l'écran a dictées ───────────────────────────
   Un CHAPITRE UNIQUE ne se numérote pas : le numéro donne un ordre, et une
   application qui n'a qu'un groupe n'a rien à ordonner. Et le plancher de
   `.field` passe en `min(220px, 100%)` : à 375px, un plancher nu poussait
   une rangée qui ne replie pas hors de l'écran. Deux lignes, trouvées en
   regardant Evidence et Console sur un téléphone.

   ── 3.0.0 — la palette officielle (décision du conseil) ─────────────────
   La 2.0 avait retiré la couleur pour donner sa place à la matière. La 3.0
   REND la couleur, et la rend VIVE — mais dans un cadre : la matière, la
   profondeur, la typographie et la retenue de la 2.0 sont conservées telles
   quelles, et c'est la palette, seule, qui change. Huit couleurs, au hex
   près, jamais approchées.

   La répartition tient en trois nombres, et c'est la règle qui empêche
   l'arc-en-ciel : environ 70 % de surfaces neutres et calmes, 20 %
   d'expression de marque (Deep Plum et Coral), 10 % de couleur sémantique
   et contextuelle. Une couleur qui ne communique ni hiérarchie, ni
   interaction, ni sens n'a rien à faire à l'écran.

   ── Les huit, verbatim du conseil ───────────────────────────────────────
     Deep Forest      #184A45   PRIMAIRE STRUCTURELLE — boutons primaires,
                                navigation active, focus, éléments
                                interactifs clés, graphes majeurs. Et le
                                succès. Il ne porte PAS la prose : le texte
                                reste noir. C'est l'accent historique de la
                                plateforme, RÉTABLI par le propriétaire à la
                                place du Deep Plum.
     Deep Plum        #411041   RETIRÉ DE L'USAGE (décision du propriétaire).
                                Il figurait comme primaire dans la table du
                                conseil ; le vert a repris sa place. Il reste
                                écrit ici pour la même raison que le
                                champagne l'était en 1.x : une valeur qu'on
                                efface sans le dire est une valeur que
                                quelqu'un réintroduit six mois plus tard.
                                Ne l'employez nulle part.
     Coral            #D37C69   SIGNATURE DE MARQUE — accents, mises en
                                lumière, actions clés, détails interactifs,
                                l'énergie du produit. Et l'attention.
     Dusty Blue       #6992B0   DONNÉE ET INFORMATION — graphes, états
                                interactifs secondaires, visualisations.
     Pale Pink        #F2A6D0   HAUTE LUMIÈRE DOUCE — sélection douce,
                                lumière ambiante, emphase légère.
     Raspberry        #985258   ANOMALIE — irrégularités, avertissements,
                                points non résolus, ce qui exige une revue.
                                Avec parcimonie.
     Deep Brown       #3A311D   NEUTRE PROFOND — emphase éditoriale, texte
                                secondaire sombre, chaleur, contraste
                                premium.
     Mist             #C8CDD5   NEUTRE CLAIR — filets, surfaces secondaires,
                                états désactivés, séparateurs.
     Soft Lime Ivory  #E3EED8   SURFACE AMBIANTE CLAIRE — teinte de surface,
                                fonds doux, zones contextuelles élevées.

   ── Les neuf familles gardent leur échelle vive (décision du propriétaire)
   Les huit du conseil sont une palette de MARQUE et de SENS. Les neuf
   familles d'objets sont autre chose : une échelle CATÉGORIELLE, dont le
   seul travail est qu'un lecteur puisse nommer une famille d'un coup d'œil
   sur une marque de 8px. Or la palette officielle n'offre que six teintes
   utilisables pour neuf cases — Mist et Soft Lime Ivory sont des surfaces —
   et la table du brief en fait donc partager deux couples (Réglementation
   avec Donnée, Exigence avec Publication). Une échelle catégorielle qui
   confond deux paires sur neuf ne fait plus son travail.

   Le propriétaire a tranché : les neuf gardent la gamme vive d'inspiration
   iOS (azur, sarcelle, cyan, framboise, ardoise, indigo, olive, corail,
   graphite), et la palette officielle gouverne tout le reste — surfaces,
   encres, actions, sémantique, marque. Deux systèmes, deux emplois, et la
   frontière est nette : une couleur de famille ne dit jamais un ÉTAT, une
   couleur sémantique ne dit jamais une CATÉGORIE.

   Ce que le traitement tonal y gagne, et qui n'existait pas avant : la
   gamme vive, posée en aplat sous des lettres claires, tenait entre 2,4:1
   et 4,5:1 — elle renonçait au plancher AA, et la charte 1.10.0 le
   consignait comme un prix connu. Les lettres passent maintenant sur un
   lavis dans une encre dérivée du même pigment : les neuf tiennent 4,6:1 au
   plus serré dans les DEUX thèmes, et le pigment pur reste visible en filet
   de 3px. On garde la vivacité et on récupère la lisibilité.

   ── Ce que la mesure impose, et que le conseil n'a pas eu à dire ────────
   Cinq des huit ne portent PAS de texte : Coral (3,06:1 sur blanc), Dusty
   Blue (3,31:1), Raspberry passe de justesse (5,70:1), tandis que Mist
   (1,60:1), Pale Pink (1,88:1) et Soft Lime Ivory (1,20:1) sont des
   matières, pas des encres. Les hex du conseil sont donc intacts pour ce
   qu'ils sont — fonds, points, filets, séries de graphes, accents — et
   chaque fois qu'une de ces couleurs doit PARLER, une encre dérivée d'elle
   porte le texte : --coral-ink pour le coral, --info pour le bleu, et ainsi
   de suite. Aucune n'est remplacée ; chacune gagne une voix lisible.

   ── Règles de maison qui voyagent avec la palette ───────────────────────
   1. 70 / 20 / 10. Si vous ne pouvez pas dire ce qu'une couleur communique,
      elle ne communique rien : remettez-la au neutre.
   2. Deep Forest est la couleur structurelle : actions primaires,
      navigation active, focus, sélection — et le succès. Coral est la
      SIGNATURE : on
      l'emploie pour l'accent qui donne vie, le détail actif, l'action
      tertiaire, jamais pour tapisser une surface.
   3. Une teinte, une phrase. Coral = attention. Raspberry = anomalie, ce
      qui exige une revue. Dusty Blue = information. Une proposition de
      machine qu'aucune personne n'a encore tranchée parlait Pale Pink ;
      depuis la 3.14.2 elle parle BLEU (décision du propriétaire, D42) —
      en jour le bleu de l'accent, la nuit #3772c4, puisque l'accent de
      nuit est framboise. En jour la proposition et l'action partagent
      donc une teinte, et la phrase est portée par la FORME (le filet, le
      bandeau) et par le mot qui l'accompagne, jamais par la seule couleur.
      La pastille pleine du menu n'est plus une de ces formes : depuis la
      3.14.3 (D61), la pastille de COMPTE (`.nav-item .count` : un non-lu,
      une attente du lecteur) a la même forme et le même bleu que celle des
      propositions, en jour comme en nuit. Sur le menu, une pastille dit
      « quelque chose attend ici » ; ce qui sépare un compte d'une
      proposition est le mot — le titre de l'entrée, « libellé — n … » — et
      la police du chiffre, jamais la teinte ni la forme. Ne dépensez pas
      ces formes en décor : la phrase cesserait d'être vraie pour toutes les
      applications à la fois.
   4. L'absence est de l'attention, jamais du critique. Un propriétaire
      manquant, un champ non renseigné, un échelon vide : ce sont des états
      en attente.
   5. Deep Brown est éditorial : une emphase, un détail premium, un
      séparateur qui le mérite. Il est aussi la voix du texte secondaire.
   6. Les catégories sont discrètes DANS LEUR EMPLOI, pas dans leur teinte.
      Les neuf pigments de famille servent de POINTS, de filets fins, de
      séries de graphes et de traitements tonals — jamais de grands aplats
      colorés, jamais de conteneurs d'icônes. L'identité vit dans les
      lettres et l'étiquette ; le pigment ne fait que seconder.
   7. Chaque paire encre/fond est mesurée AA (>= 4,5:1 pour du texte). Si
      vous ajoutez un token, mesurez-le avant de l'expédier.
   8. Les deux thèmes sont livrés. Les règles sombres ci-dessous sont
      écrites pour qu'un choix clair explicite batte une préférence système
      sombre, et réciproquement.
   9. La profondeur est tonale : quatre niveaux de surface (canvas, surface,
      elevated, floating), des filets de Mist, des ombres qui suggèrent au
      lieu d'affirmer. Le verre liquide appartient au niveau FLOTTANT et au
      chrome seulement — menus, palettes, popovers, tiroirs, modales, barre
      supérieure, barre latérale. Une grille de cartes en verre est une
      vitre de salle de bains, pas une interface. Le contenu doit réellement
      défiler sous une surface de verre : une barre translucide au-dessus de
      rien n'est qu'un variateur.
   10. Si l'espace peut séparer deux choses, on ne trace pas de trait. Les
      séparateurs restent pour les tableaux denses, les groupes de
      formulaire, les frontières majeures et la structure de navigation.
   11. Il n'y a pas de bouton rouge. Une action destructrice dit ce qu'elle
      fait avec des mots — « Retirer et signer » — et le rouge reste aux
      alertes et aux échecs.

   ── L'installer, et les trois façons dont il ne colle pas, en silence ───
   Chaque valeur ici est mesurée contre le produit : barre latérale 248,
   ligne de marque et barre supérieure 48, élément de navigation 15px,
   rayon de bouton 10, registre à 10px avec 0,14em. Si une application
   sœur diffère, ce ne sont presque jamais les valeurs — c'est l'un de ces
   trois points, et chacun se vérifie en dix secondes :

   1. LA PILE TYPOGRAPHIQUE NE SE RÉSOUT PAS PAREIL. La charte mène par la
      pile système, si bien que le matériel Apple rend SF Pro nativement
      (Display pour l'échelle de titres via --font-display, Text pour
      l'interface via --font-sans) ; ailleurs les deux piles retombent sur
      Inter, que ce fichier NOMME et ne livre pas. Chargez Inter vous-même
      (next/font, @font-face) ou les plateformes non-Apple retombent sur le
      sans du système.

   2. UN RESET HORS COUCHE BAT CELUI-CI. Les resets d'éléments ci-dessous
      vivent dans `@layer yntgyst-base` exprès, pour que vos utilitaires
      l'emportent. Mais un reset NON COUCHÉ — la preflight de Tailwind v3,
      normalize.css, votre propre `* { margin: 0 }` — bat toute règle
      couchée quel que soit l'ordre.

   3. CE FICHIER NE FOURNIT AUCUNE CLASSE UTILITAIRE. Il donne des
      propriétés personnalisées et des classes de composant. `text-ink-3`,
      `bg-surface-2` et consorts sont des utilitaires Tailwind engendrés
      depuis un bloc `@theme` dans le globals.css du produit.

   ── Couches ─────────────────────────────────────────────────────────────
   Déclarées d'entrée pour que le CSS de votre application gagne là où il
   doit. Les resets d'éléments vivent dans `base` EXPRÈS : un reset hors
   couche bat tout utilitaire, et un `button { padding: 0 }` posé hors
   couche a jadis mis à zéro, en silence, le rembourrage de chaque bouton
   de ce produit.
   ========================================================================= */

@layer yntgyst-base, yntgyst-components, app;

/* ============================================================================
   Tokens

   CES BLOCS SONT ENGENDRÉS, PAS RECOPIÉS. La 1.x déclarait la palette dans
   quatre blocs tenus à la main (`:root`, la requête média sombre, et les deux
   jumeaux `[data-theme]`) et son propre commentaire disait que « c'est ainsi
   qu'un token disparaît — un bloc est modifié, l'autre non ». C'est arrivé :
   `--proposed` a manqué d'un bloc pendant des mois. Depuis la 3.0.0 les quatre
   blocs sortent d'une seule table de valeurs (le banc de mesure du chantier),
   si bien qu'ils ne PEUVENT plus diverger. Pour changer une valeur : changez-la
   dans la table, réengendrez, rescellez. Ne modifiez jamais un seul bloc.

   Ce que porte chaque famille de tokens :
     --brand-*      les huit du conseil, au hex près, jamais approchées
     --paper/-lit   le canvas et sa lumière ambiante
     --surface/
       --elevated/
       --floating   les trois niveaux posés, translucides (§ surfaces)
     --surface-solid l'exception opaque : en-tête collant sur texte défilant
     --surface-2/-3 remplissages tonals (encre brune à 5 % / 8,5 %) — survol,
                    enfoncement, creux ; ils marchent sur n'importe quel niveau
     --line/-strong les filets, dérivés du Mist (§ « si l'espace suffit,
                    pas de trait »)
     --ink..--ink-4 la voix du texte, NEUTRE : un quasi-noir, deux gris, puis
                    le Mist — décoratif. Le texte reste NOIR (décision du
                    propriétaire) : le Deep Plum est STRUCTUREL — boutons,
                    navigation active, focus, succès — et ne teinte pas la
                    prose. Une interface dont chaque mot tire sur le violet
                    n'est plus retenue, elle est costumée.
     --accent*      Deep Plum : boutons primaires, sélection, focus
     --coral*       Coral : la signature de marque, accents et micro-détails
     --ok/--info/--attention/--critical/--proposed/--editorial + leurs lavis
     --fam-*        par famille : le PIGMENT (points, filets, séries de
                    graphes), son LAVIS et son ENCRE (traitement tonal).
                    ÉCHELLE CATÉGORIELLE À PART — voir la note ci-dessous.
     --app-*        une encre par application
   ========================================================================= */

:root {
  /* --- brand, verbatim from the board — the eight, and only these ---- */
  --brand-forest: #184a45;
  --brand-coral: #d37c69;
  --brand-blue: #6992b0;
  --brand-pink: #f2a6d0;
  --brand-raspberry: #985258;
  --brand-brown: #3a311d;
  --brand-mist: #c8cdd5;
  --brand-lime: #e3eed8;

  /* --- deux compagnons FIXES, hors des huit ----------------------------
     La palette du conseil ne contient ni papier ni encre qui NE SUIVENT PAS
     le thème, et trois surfaces en ont besoin : un logotype posé sur une
     photographie claire, une devise posée sur une photographie sombre, la
     porte de connexion des deux produits. Une image ne bascule pas avec le
     thème du lecteur ; le texte qui la chevauche ne le doit pas non plus.
     Ce sont des ancrages, pas une neuvième et une dixième couleur : le
     premier EST le Deep Plum, le second EST le blanc cassé chaud du canvas
     clair. Ils ne changent JAMAIS avec le thème — c'est tout leur emploi. */
  --brand-ink: #1c1d1d;
  --brand-ivory: #fdfaf8;


  /* --- surfaces, ink, semantics (light) ------------------------------- */
  --paper: #ffffff;
  --paper-stone: #ffffff;
  --paper-blush: #ffffff;
  --surface: rgb(255 255 255 / 0.55);
  --elevated: rgb(255 255 255 / 0.72);
  --floating: rgb(255 255 255 / 0.78);
  --surface-solid: #ffffff;
  --surface-2: rgb(58 49 29 / 0.05);
  --surface-3: rgb(58 49 29 / 0.085);
  --line: rgb(188 193 201);
  --line-strong: rgb(166 171 179);
  --ink: #1c1d1d;
  --ink-2: #4f5053;
  --ink-3: #6f7073;
  --ink-4: #c8cdd5;
  --nav-chapter: #777673;
  /* 3.9.0 — LA BARRE ÉTEINTE D'UNE JAUGE PORTE LE DÉNOMINATEUR (1.4.11) :
     `--line-strong` rendait 1,44:1 — 1,71:1 à la 3.14.1, 2,31:1 depuis la
     3.14.2 —, toujours sous le 3:1 d'un élément non textuel : la jauge
     garde son jeton. */
  --meter-off: color-mix(in srgb, var(--ink) 48%, transparent);
  /* Le voile d'un dialogue : une seule recette, lue par .scrim, .palette-scrim
     et .voile. Le noir en dur de vingt-trois fichiers de Collect survivait au
     thème sombre ; celui-ci est du PAPIER, il suit le thème. */
  --voile: color-mix(in srgb, var(--paper) 60%, transparent);
  --accent: #0a4fcc;
  --accent-hover: #0741b8;
  --accent-ink: #05349a;
  --accent-wash: #e7edfa;
  --on-accent: #ffffff;
  --accent-hi: #0741b8;
  --accent-hover-hi: #0a3d99;
  --accent-active: #0741b8;
  --accent-glow: transparent;
  --coral: #d37c69;
  --coral-ink: #945d4a;
  --coral-wash: #f8ece8;
  --ok: #2d6341;
  --ok-wash: #eaefec;
  --info: #516e82;
  --info-wash: #edeff0;
  --attention: #945d4a;
  --attention-wash: #f8ece8;
  --critical: #985258;
  --critical-wash: #f2e8e6;
  --proposed: #0a4fcc;
  --proposed-wash: #eaf0fb;
  --editorial: #3a311d;
  --editorial-wash: #e8e4e0;
  --ok-vif: #52b075; --on-ok: #0b1a11;
  --info-vif: #516e82; --on-info: #ffffff;
  --attention-vif: #d37c69; --on-attention: #1c1d1d;
  --critical-vif: #985258; --on-critical: #ffffff;
  --proposed-vif: #0a4fcc; --on-proposed: #ffffff;
  --editorial-vif: #3a311d; --on-editorial: #ffffff;
  --neutral-vif: #c8cdd5; --on-neutral: #1c1d1d;
  --ok-wash-hi: #f2f5f3;
  --info-wash-hi: #f3f4f5;
  --attention-wash-hi: #fbf3f0;
  --critical-wash-hi: #f7f0ee;
  --proposed-wash-hi: #f3f6fd;
  --editorial-wash-hi: #efece9;
  --accent-wash-hi: #f0f0ee;
  --glass: rgb(255 255 255 / 0.58);
  --glass-line: rgb(200 205 213);
  --glass-edge: rgb(255 255 255 / 0.7);
  --edge-light: inset 0 1px 0 rgb(255 255 255 / 0.75);

  --fam-regulation: #3772c4;
  --fam-regulation-wash: #e3e8f1;
  --fam-regulation-ink: #3367ae;
  --fam-requirement: #2bb3a3;
  --fam-requirement-wash: #e2f1ed;
  --fam-requirement-ink: #25756c;
  --fam-data: #35a7d8;
  --fam-data-wash: #e3eff4;
  --fam-data-ink: #2b718f;
  --fam-governance: #d95c7a;
  --fam-governance-wash: #f8e5e8;
  --fam-governance-ink: #a44a60;
  --fam-operations: #96681c;
  --fam-operations-wash: #f0e7db;
  --fam-operations-ink: #875f1c;
  --fam-systems: #6c63d9;
  --fam-systems-wash: #eae6f4;
  --fam-systems-ink: #6159bf;
  --fam-assurance: #8aa64a;
  --fam-assurance-wash: #eeefe1;
  --fam-assurance-ink: #5f7138;
  --fam-output: #e67a45;
  --fam-output-wash: #fae9e1;
  --fam-output-ink: #9b5836;
  --fam-glossary: #6b6f77;
  --fam-glossary-wash: #eae8e7;
  --fam-glossary-ink: #63676e;
  --on-family: #ffffff;
  --on-fam-regulation: #ffffff;
  --on-fam-requirement: #1c1d1d;
  --on-fam-data: #1c1d1d;
  --on-fam-governance: #1c1d1d;
  --on-fam-operations: #ffffff;
  --on-fam-systems: #ffffff;
  --on-fam-assurance: #1c1d1d;
  --on-fam-output: #1c1d1d;
  --on-fam-glossary: #ffffff;

  --app-collect: #57778e;
  --app-knowledge: #3fafa0;
  --app-evidence: #6c63d9;
  --app-proqr: #ab5f39;
  --app-planned: #8c8b88;
  --paper-lit:
    radial-gradient(1100px 700px at 84% -8%, rgb(242 166 208 / 0.055) 0%, rgb(242 166 208 / 0) 62%),
    radial-gradient(900px 620px at -6% 104%, rgb(24 74 69 / 0.035) 0%, rgb(65 16 65 / 0) 58%),
    radial-gradient(780px 520px at 50% 50%, rgb(211 124 105 / 0.028) 0%, rgb(211 124 105 / 0) 70%),
    linear-gradient(168deg, var(--paper) 0%, var(--paper-stone) 55%, var(--paper-blush) 100%);

  --shadow-sm: 0 1px 2px rgb(24 74 69 / 0.05), var(--edge-light);
  --shadow-md: 0 12px 32px rgb(24 74 69 / 0.08), 0 1px 2px rgb(24 74 69 / 0.04), var(--edge-light);
  --shadow-lg: 0 24px 64px rgb(24 74 69 / 0.14), 0 2px 6px rgb(24 74 69 / 0.05), var(--edge-light);

  /* --- type ------------------------------------------------------------
     Two optical voices of one system face: SF Pro Display for the heading
     ladder and hero figures, SF Pro Text for the interface. The system
     stack leads so Apple hardware gets them natively; Inter is the named
     fallback everywhere else (load it yourself — install note 1).
     Typography makes hierarchy before colour is allowed to. */
  --font-sans: -apple-system, BlinkMacSystemFont, "SF Pro Text",
    var(--font-inter, "Inter"), "Segoe UI", system-ui, "Helvetica Neue",
    sans-serif;
  --font-display: -apple-system, BlinkMacSystemFont, "SF Pro Display",
    var(--font-inter, "Inter"), "Segoe UI", system-ui, "Helvetica Neue",
    sans-serif;
  --font-mono: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Consolas,
    monospace;
  /* The serif is reserved for verbatim quoted source text. Nothing else. */
  --font-serif: "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, serif;

  /* --- structure: the radius scale, named by ROLE ----------------------
     Architectural, not bubbly. "Which radius" is a question every hand
     answers differently when the scale only has sizes.
       --r-xs     6   tiny marks: fi chips, cites, kbd
       --r-sm     8   small controls: inputs, nav items, icon buttons
       --r-btn   10   buttons
       --r-md    12   intermediate groupings
       --r-card  14   cards
       --r-lg    16   panels (--r-panel is the same value saying its role)
       --r-float 18   floating surfaces: menus, palettes, popovers
       --r-modal 20   modal layers
     Pills and dots stay full rounds. Nothing rounds beyond 20. */
  --r-xs: 6px;
  --r-sm: 8px;
  /* La porte de sortie, en masque SVG : une icône dans un pseudo-élément ne
     peut pas être un <svg>, et un masque garde `currentColor` — donc le
     survol et le focus continuent de la teinter comme le texte qu'elle
     remplace. */
  --icone-sortie: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4'/%3E%3Cpolyline points='16 17 21 12 16 7'/%3E%3Cline x1='21' y1='12' x2='9' y2='12'/%3E%3C/svg%3E");
  --r-btn: 10px;
  --r-md: 12px;
  --r-card: 14px;
  --r-lg: 16px;
  --r-panel: 16px;
  --r-float: 18px;
  --r-modal: 20px;

  --blur-glass: 24px;

  /* --- icons -----------------------------------------------------------
     One icon language: thin architectural strokes, Lucide-grade geometry,
     rounded caps. 1.5 is the default stroke EVERYWHERE — hand-drawn marks
     and icon libraries alike (set it: library defaults of 2 read
     cartoonish at this scale). Colours are the board's: Deep Plum is the
     default (--ink), Deep Brown the secondary (--ink-2), Mist the muted
     (--ink-4), Coral the ACTIVE mark, Raspberry the attention one. Never
     a filled colour container around an icon unless the colour carries a
     stated meaning. */
  --icon-stroke: 1.5;
  --icon-sm: 14px;
  --icon: 16px;
  --icon-lg: 18px;

  /* --- motion -----------------------------------------------------------
     Precise and calm: surface and colour move at 120ms, structure at
     200ms, one easing. Micro-movement tops out at 1px. Nothing bounces. */
  --ease: cubic-bezier(0.25, 0.1, 0.25, 1);
  --t-fast: 0.12s;
  --t-slow: 0.2s;

}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --paper: #141014;
    --paper-stone: #181218;
    --paper-blush: #1d151c;
    --surface: rgb(255 255 255 / 0.05);
    --elevated: rgb(255 255 255 / 0.08);
    --floating: rgb(255 255 255 / 0.10);
    --surface-solid: #1c171c;
    --surface-2: rgb(255 255 255 / 0.055);
    --surface-3: rgb(255 255 255 / 0.09);
    --line: rgb(200 205 213 / 0.14);
    --line-strong: rgb(200 205 213 / 0.28);
    --ink: #f4f4f5;
    --ink-2: #b3b4b6;
    --ink-3: #8b8c8f;
    --ink-4: #6d666c;
    --nav-chapter: #7b7c82;
    --meter-off: color-mix(in srgb, var(--ink) 36%, transparent);
    --voile: color-mix(in srgb, var(--paper) 60%, transparent);
    --accent: #c93d67;
    --accent-hover: #8f2948;
    --accent-ink: #e24e7a;
    --accent-wash: rgb(201 61 103 / 0.10);
    --on-accent: #ffffff;
    --accent-hi: #e24e7a;
    --accent-hover-hi: #f184a3;
    --accent-active: #6f2038;
    --accent-glow: rgb(201 61 103 / 0.16);
    --coral: #de9e90;
    --coral-ink: #de9e90;
    --coral-wash: #342728;
    --ok: #52b075;
    --ok-wash: #1a2a1f;
    --info: #9cb7cb;
    --info-wash: #2a2b31;
    --attention: #de9e90;
    --attention-wash: #342728;
    --critical: #c59ea1;
    --critical-wash: #30272b;
    --proposed: #6ca5ea;
    --proposed-wash: #1a2534;
    --editorial: #aca8a0;
    --editorial-wash: #2c282a;
    --ok-vif: #52b075; --on-ok: #0b1a11;
    --info-vif: #6992b0; --on-info: #10141b;
    --attention-vif: #de9e90; --on-attention: #231412;
    --critical-vif: #c57f88; --on-critical: #200f12;
    --proposed-vif: #3772c4; --on-proposed: #ffffff;
    --editorial-vif: #aca8a0; --on-editorial: #171512;
    --neutral-vif: #4a4c52; --on-neutral: #f4f4f5;
    --ok-wash-hi: #1d3626;
    --info-wash-hi: #35363d;
    --attention-wash-hi: #403032;
    --critical-wash-hi: #3c3136;
    --proposed-wash-hi: #212e40;
    --editorial-wash-hi: #383334;
    --accent-wash-hi: rgb(201 61 103 / 0.18);
    --glass: rgb(20 16 20 / 0.62);
    --glass-line: rgb(200 205 213 / 0.13);
    --glass-edge: rgb(255 255 255 / 0.12);
    --edge-light: inset 0 1px 0 rgb(255 255 255 / 0.06);

    --fam-regulation: #6ca5ea;
    --fam-regulation-wash: #242b3b;
    --fam-regulation-ink: #6ca5ea;
    --fam-requirement: #4bcdbd;
    --fam-requirement-wash: #1e3232;
    --fam-requirement-ink: #4bcdbd;
    --fam-data: #64c4ee;
    --fam-data-wash: #22303b;
    --fam-data-ink: #64c4ee;
    --fam-governance: #ee85a0;
    --fam-governance-wash: #3b252d;
    --fam-governance-ink: #ee85a0;
    --fam-operations: #d9a94a;
    --fam-operations-wash: #372c1e;
    --fam-operations-ink: #d9a94a;
    --fam-systems: #9b93f0;
    --fam-systems-wash: #2c283c;
    --fam-systems-ink: #9b93f0;
    --fam-assurance: #acc763;
    --fam-assurance-wash: #2f3122;
    --fam-assurance-ink: #acc763;
    --fam-output: #f79a6b;
    --fam-output-wash: #3d2924;
    --fam-output-ink: #f79a6b;
    --fam-glossary: #a8a8a6;
    --fam-glossary-wash: #2f2b2e;
    --fam-glossary-ink: #a8a8a6;
    --on-family: #141014;
    --on-fam-regulation: #141014;
    --on-fam-requirement: #141014;
    --on-fam-data: #141014;
    --on-fam-governance: #141014;
    --on-fam-operations: #141014;
    --on-fam-systems: #141014;
    --on-fam-assurance: #141014;
    --on-fam-output: #141014;
    --on-fam-glossary: #141014;

    --app-collect: #6992b0;
    --app-knowledge: #3fafa0;
    --app-evidence: #766edc;
    --app-proqr: #e67a45;
    --app-planned: #6d666c;
    --paper-lit:
      radial-gradient(1100px 700px at 84% -8%, rgb(242 166 208 / 0.05) 0%, rgb(242 166 208 / 0) 62%),
      radial-gradient(900px 620px at -6% 104%, rgb(201 61 103 / 0.04) 0%, rgb(201 169 201 / 0) 58%),
      radial-gradient(780px 520px at 50% 50%, rgb(211 124 105 / 0.025) 0%, rgb(211 124 105 / 0) 70%),
      linear-gradient(168deg, var(--paper) 0%, var(--paper-stone) 55%, var(--paper-blush) 100%);

    --shadow-sm: 0 1px 2px rgb(0 0 0 / 0.5), var(--edge-light);
    --shadow-md: 0 12px 32px rgb(0 0 0 / 0.55), var(--edge-light);
    --shadow-lg: 0 24px 64px rgb(0 0 0 / 0.7), 0 2px 6px rgb(0 0 0 / 0.5), var(--edge-light);
  }
}

:root[data-theme="light"] {
  --paper: #ffffff;
  --paper-stone: #ffffff;
  --paper-blush: #ffffff;
  --surface: rgb(255 255 255 / 0.55);
  --elevated: rgb(255 255 255 / 0.72);
  --floating: rgb(255 255 255 / 0.78);
  --surface-solid: #ffffff;
  --surface-2: rgb(58 49 29 / 0.05);
  --surface-3: rgb(58 49 29 / 0.085);
  --line: rgb(188 193 201);
  --line-strong: rgb(166 171 179);
  --ink: #1c1d1d;
  --ink-2: #4f5053;
  --ink-3: #6f7073;
  --ink-4: #c8cdd5;
  --nav-chapter: #777673;
  /* 3.9.0 — LA BARRE ÉTEINTE D'UNE JAUGE PORTE LE DÉNOMINATEUR (1.4.11) :
     `--line-strong` rendait 1,44:1 — 1,71:1 à la 3.14.1, 2,31:1 depuis la
     3.14.2 —, toujours sous le 3:1 d'un élément non textuel : la jauge
     garde son jeton. */
  --meter-off: color-mix(in srgb, var(--ink) 48%, transparent);
  /* Le voile d'un dialogue : une seule recette, lue par .scrim, .palette-scrim
     et .voile. Le noir en dur de vingt-trois fichiers de Collect survivait au
     thème sombre ; celui-ci est du PAPIER, il suit le thème. */
  --voile: color-mix(in srgb, var(--paper) 60%, transparent);
  --accent: #0a4fcc;
  --accent-hover: #0741b8;
  --accent-ink: #05349a;
  --accent-wash: #e7edfa;
  --on-accent: #ffffff;
  --accent-hi: #0741b8;
  --accent-hover-hi: #0a3d99;
  --accent-active: #0741b8;
  --accent-glow: transparent;
  --coral: #d37c69;
  --coral-ink: #945d4a;
  --coral-wash: #f8ece8;
  --ok: #2d6341;
  --ok-wash: #eaefec;
  --info: #516e82;
  --info-wash: #edeff0;
  --attention: #945d4a;
  --attention-wash: #f8ece8;
  --critical: #985258;
  --critical-wash: #f2e8e6;
  --proposed: #0a4fcc;
  --proposed-wash: #eaf0fb;
  --editorial: #3a311d;
  --editorial-wash: #e8e4e0;
  --ok-vif: #52b075; --on-ok: #0b1a11;
  --info-vif: #516e82; --on-info: #ffffff;
  --attention-vif: #d37c69; --on-attention: #1c1d1d;
  --critical-vif: #985258; --on-critical: #ffffff;
  --proposed-vif: #0a4fcc; --on-proposed: #ffffff;
  --editorial-vif: #3a311d; --on-editorial: #ffffff;
  --neutral-vif: #c8cdd5; --on-neutral: #1c1d1d;
  --ok-wash-hi: #f2f5f3;
  --info-wash-hi: #f3f4f5;
  --attention-wash-hi: #fbf3f0;
  --critical-wash-hi: #f7f0ee;
  --proposed-wash-hi: #f3f6fd;
  --editorial-wash-hi: #efece9;
  --accent-wash-hi: #f0f0ee;
  --glass: rgb(255 255 255 / 0.58);
  --glass-line: rgb(200 205 213);
  --glass-edge: rgb(255 255 255 / 0.7);
  --edge-light: inset 0 1px 0 rgb(255 255 255 / 0.75);

  --fam-regulation: #3772c4;
  --fam-regulation-wash: #e3e8f1;
  --fam-regulation-ink: #3367ae;
  --fam-requirement: #2bb3a3;
  --fam-requirement-wash: #e2f1ed;
  --fam-requirement-ink: #25756c;
  --fam-data: #35a7d8;
  --fam-data-wash: #e3eff4;
  --fam-data-ink: #2b718f;
  --fam-governance: #d95c7a;
  --fam-governance-wash: #f8e5e8;
  --fam-governance-ink: #a44a60;
  --fam-operations: #96681c;
  --fam-operations-wash: #f0e7db;
  --fam-operations-ink: #875f1c;
  --fam-systems: #6c63d9;
  --fam-systems-wash: #eae6f4;
  --fam-systems-ink: #6159bf;
  --fam-assurance: #8aa64a;
  --fam-assurance-wash: #eeefe1;
  --fam-assurance-ink: #5f7138;
  --fam-output: #e67a45;
  --fam-output-wash: #fae9e1;
  --fam-output-ink: #9b5836;
  --fam-glossary: #6b6f77;
  --fam-glossary-wash: #eae8e7;
  --fam-glossary-ink: #63676e;
  --on-family: #ffffff;
  --on-fam-regulation: #ffffff;
  --on-fam-requirement: #1c1d1d;
  --on-fam-data: #1c1d1d;
  --on-fam-governance: #1c1d1d;
  --on-fam-operations: #ffffff;
  --on-fam-systems: #ffffff;
  --on-fam-assurance: #1c1d1d;
  --on-fam-output: #1c1d1d;
  --on-fam-glossary: #ffffff;

  --app-collect: #57778e;
  --app-knowledge: #3fafa0;
  --app-evidence: #6c63d9;
  --app-proqr: #ab5f39;
  --app-planned: #8c8b88;
  --paper-lit:
    radial-gradient(1100px 700px at 84% -8%, rgb(242 166 208 / 0.055) 0%, rgb(242 166 208 / 0) 62%),
    radial-gradient(900px 620px at -6% 104%, rgb(24 74 69 / 0.035) 0%, rgb(65 16 65 / 0) 58%),
    radial-gradient(780px 520px at 50% 50%, rgb(211 124 105 / 0.028) 0%, rgb(211 124 105 / 0) 70%),
    linear-gradient(168deg, var(--paper) 0%, var(--paper-stone) 55%, var(--paper-blush) 100%);

  --shadow-sm: 0 1px 2px rgb(24 74 69 / 0.05), var(--edge-light);
  --shadow-md: 0 12px 32px rgb(24 74 69 / 0.08), 0 1px 2px rgb(24 74 69 / 0.04), var(--edge-light);
  --shadow-lg: 0 24px 64px rgb(24 74 69 / 0.14), 0 2px 6px rgb(24 74 69 / 0.05), var(--edge-light);
}

:root[data-theme="dark"] {
  --paper: #141014;
  --paper-stone: #181218;
  --paper-blush: #1d151c;
  --surface: rgb(255 255 255 / 0.05);
  --elevated: rgb(255 255 255 / 0.08);
  --floating: rgb(255 255 255 / 0.10);
  --surface-solid: #1c171c;
  --surface-2: rgb(255 255 255 / 0.055);
  --surface-3: rgb(255 255 255 / 0.09);
  --line: rgb(200 205 213 / 0.14);
  --line-strong: rgb(200 205 213 / 0.28);
  --ink: #f4f4f5;
  --ink-2: #b3b4b6;
  --ink-3: #8b8c8f;
  --ink-4: #6d666c;
  --nav-chapter: #7b7c82;
  --meter-off: color-mix(in srgb, var(--ink) 36%, transparent);
  --voile: color-mix(in srgb, var(--paper) 60%, transparent);
  --accent: #c93d67;
  --accent-hover: #8f2948;
  --accent-ink: #e24e7a;
  --accent-wash: rgb(201 61 103 / 0.10);
  --on-accent: #ffffff;
  --accent-hi: #e24e7a;
  --accent-hover-hi: #f184a3;
  --accent-active: #6f2038;
  --accent-glow: rgb(201 61 103 / 0.16);
  --coral: #de9e90;
  --coral-ink: #de9e90;
  --coral-wash: #342728;
  --ok: #52b075;
  --ok-wash: #1a2a1f;
  --info: #9cb7cb;
  --info-wash: #2a2b31;
  --attention: #de9e90;
  --attention-wash: #342728;
  --critical: #c59ea1;
  --critical-wash: #30272b;
  --proposed: #6ca5ea;
  --proposed-wash: #1a2534;
  --editorial: #aca8a0;
  --editorial-wash: #2c282a;
  --ok-vif: #52b075; --on-ok: #0b1a11;
  --info-vif: #6992b0; --on-info: #10141b;
  --attention-vif: #de9e90; --on-attention: #231412;
  --critical-vif: #c57f88; --on-critical: #200f12;
  --proposed-vif: #3772c4; --on-proposed: #ffffff;
  --editorial-vif: #aca8a0; --on-editorial: #171512;
  --neutral-vif: #4a4c52; --on-neutral: #f4f4f5;
  --ok-wash-hi: #1d3626;
  --info-wash-hi: #35363d;
  --attention-wash-hi: #403032;
  --critical-wash-hi: #3c3136;
  --proposed-wash-hi: #212e40;
  --editorial-wash-hi: #383334;
  --accent-wash-hi: rgb(201 61 103 / 0.18);
  --glass: rgb(20 16 20 / 0.62);
  --glass-line: rgb(200 205 213 / 0.13);
  --glass-edge: rgb(255 255 255 / 0.12);
  --edge-light: inset 0 1px 0 rgb(255 255 255 / 0.06);

  --fam-regulation: #6ca5ea;
  --fam-regulation-wash: #242b3b;
  --fam-regulation-ink: #6ca5ea;
  --fam-requirement: #4bcdbd;
  --fam-requirement-wash: #1e3232;
  --fam-requirement-ink: #4bcdbd;
  --fam-data: #64c4ee;
  --fam-data-wash: #22303b;
  --fam-data-ink: #64c4ee;
  --fam-governance: #ee85a0;
  --fam-governance-wash: #3b252d;
  --fam-governance-ink: #ee85a0;
  --fam-operations: #d9a94a;
  --fam-operations-wash: #372c1e;
  --fam-operations-ink: #d9a94a;
  --fam-systems: #9b93f0;
  --fam-systems-wash: #2c283c;
  --fam-systems-ink: #9b93f0;
  --fam-assurance: #acc763;
  --fam-assurance-wash: #2f3122;
  --fam-assurance-ink: #acc763;
  --fam-output: #f79a6b;
  --fam-output-wash: #3d2924;
  --fam-output-ink: #f79a6b;
  --fam-glossary: #a8a8a6;
  --fam-glossary-wash: #2f2b2e;
  --fam-glossary-ink: #a8a8a6;
  --on-family: #141014;
  --on-fam-regulation: #141014;
  --on-fam-requirement: #141014;
  --on-fam-data: #141014;
  --on-fam-governance: #141014;
  --on-fam-operations: #141014;
  --on-fam-systems: #141014;
  --on-fam-assurance: #141014;
  --on-fam-output: #141014;
  --on-fam-glossary: #141014;

  --app-collect: #6992b0;
  --app-knowledge: #3fafa0;
  --app-evidence: #766edc;
  --app-proqr: #e67a45;
  --app-planned: #6d666c;
  --paper-lit:
    radial-gradient(1100px 700px at 84% -8%, rgb(242 166 208 / 0.05) 0%, rgb(242 166 208 / 0) 62%),
    radial-gradient(900px 620px at -6% 104%, rgb(201 61 103 / 0.04) 0%, rgb(201 169 201 / 0) 58%),
    radial-gradient(780px 520px at 50% 50%, rgb(211 124 105 / 0.025) 0%, rgb(211 124 105 / 0) 70%),
    linear-gradient(168deg, var(--paper) 0%, var(--paper-stone) 55%, var(--paper-blush) 100%);

  --shadow-sm: 0 1px 2px rgb(0 0 0 / 0.5), var(--edge-light);
  --shadow-md: 0 12px 32px rgb(0 0 0 / 0.55), var(--edge-light);
  --shadow-lg: 0 24px 64px rgb(0 0 0 / 0.7), 0 2px 6px rgb(0 0 0 / 0.5), var(--edge-light);
}


/* ============================================================================
   Base — element resets, layered so your utilities always win
   ========================================================================= */

@layer yntgyst-base {
  *,
  *::before,
  *::after {
    box-sizing: border-box;
    border-color: var(--line);
  }
  html,
  body {
    height: 100%;
  }
  body {
    margin: 0;
    /* `fixed` : la lumière appartient à la fenêtre, pas au document — sans
       lui, une page longue rejoue l'ambiance à chaque écran. */
    background: var(--paper-lit) fixed;
    background-color: var(--paper);
    color: var(--ink);
    font-family: var(--font-sans);
    font-size: 13.5px;
    line-height: 1.55;
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
  }
  button {
    font: inherit;
    color: inherit;
    background: none;
    border: 0;
    padding: 0;
    cursor: pointer;
    text-align: left;
  }
  input,
  select,
  textarea {
    font: inherit;
    color: inherit;
  }
  a {
    color: inherit;
    text-decoration: none;
  }
  h1,
  h2,
  h3,
  h4 {
    margin: 0;
    font-weight: 500;
    text-wrap: balance;
  }
  p {
    margin: 0;
  }
  /* 3.9.0 — L'ANNEAU GLOBAL PASSE À 2px, ET CESSE DE REDESSINER CE QU'IL
     ENTOURE.

     DEUX MESURES. L'anneau lui-même tient largement le plancher non textuel
     de 3:1 : --accent sur --paper rend 9,98:1 en jour et 3,90:1 en nuit. Ce
     n'est donc pas le CONTRASTE qui manquait, c'est l'ÉPAISSEUR — et elle ne
     manquait qu'en jour. En nuit, --accent-glow pose 3px de framboise à 16 %
     autour du trait ; en jour ce jeton vaut `transparent` (voir les quatre
     blocs), le halo n'existe pas, et 1,5px de trait était littéralement tout
     ce que le clavier obtenait. 2px est la valeur que les champs du §4 et le
     bouton primaire portent déjà depuis la 3.7.1 : l'anneau global rejoint
     ses propres exceptions au lieu de rester sous elles.

     `border-radius` PART. Posée dans une règle d'ÉTAT, elle ne décorait pas
     l'anneau : elle imposait 6px à l'élément focalisé lui-même. Une `.chip`
     ou une `.pill` (999px) se CARRAIT en recevant le focus, et revenait
     ronde en le perdant — la forme changeait sous le doigt qui la visait.
     L'outline suit déjà le rayon de son élément dans les navigateurs cibles :
     il n'y avait rien à corriger, seulement quelque chose à cesser de faire.

     Visible au clavier seulement : le rendu à la souris ne bouge pas d'un
     pixel. 2.4.7 et 2.4.11. */
  :focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    box-shadow: 0 0 0 3px var(--accent-glow);
  }
  ::selection {
    background: var(--accent-wash);
    color: var(--ink);
  }
}

/* ============================================================================
   Components
   ========================================================================= */

@layer yntgyst-components {
  /* ======================================================================
     1 · TYPE REGISTERS
     Two optical voices of one face — Display for the ladder, Text for
     the interface — and typography makes hierarchy before colour is
     allowed to. The serif is not a voice: it is reserved for text quoted
     verbatim from a source (§9 below).
     ====================================================================== */

  /* The small-caps label. Names a thing without competing with it — use it
     for section asides, field labels, and metadata, never for a heading.
     The ONLY sanctioned uppercase: compact metadata and section markers. */
  .register {
    font-size: 11.5px;
    font-weight: 500;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--ink-3);
    font-variant-numeric: tabular-nums;
  }
  /* Light display type. Large headings and hero figures only. */
  .display {
    font-family: var(--font-display);
    font-weight: 300;
    letter-spacing: -0.022em;
    line-height: 1.12;
  }
  /* --- the heading ladder -----------------------------------------------
     Three roles, not five accidental sizes. The product had 32, 30, 34, 28
     and 38 in circulation and no rule saying which was which, so a sibling
     app had nothing to copy and every page invented its own.

     .page-hero    38px  the one heading that opens a product's home. One per
                         application, and it is allowed to be loud.
     .page-title   32px  every other page heading. If you are unsure, this.
     .object-title 30px  a THING's name rather than a page's — an object, a
                         record, something the reader can edit in place. It
                         sits below the page ladder on purpose: the object is
                         the subject, not the poster.

     Each carries the display treatment, so a heading needs one class. */
  .page-hero,
  .page-title,
  .object-title {
    font-family: var(--font-display);
    font-weight: 300;
    letter-spacing: -0.022em;
    line-height: 1.12;
    color: var(--ink);
    text-wrap: balance;
  }
  /* PAS DE MESURE SUR UN TITRE. Elle y était, à 22ch et 26ch, et elle
     produisait des titres qui se cassaient au tiers d'une ligne vide sur un
     grand écran. `text-wrap: balance` ci-dessus fait déjà le travail quand un
     titre passe à deux lignes : il équilibre les lignes au lieu de laisser un
     mot seul en bas. C'est ce qu'on voulait ; la mesure, elle, coupait aussi
     les titres qui tenaient. La mesure appartient à la PROSE — .page-lead la
     garde à 66ch, parce qu'un paragraphe de 200 caractères de large ne se lit
     pas. Un titre de 200 caractères de large n'existe pas. */
  /* LES TROIS ÉCHELONS INTERPOLENT. 38px sur un téléphone de 375px, c'est
     un titre qui mange l'écran avant qu'on ait lu la première ligne ; 28px
     sur un 2200px, c'est un titre qui n'ouvre plus rien. Les bornes sont les
     mêmes valeurs qu'avant — ce sont les paliers qui disparaissent, pas les
     décisions. */
  .page-hero {
    font-size: clamp(27px, 1.7vw + 20.6px, 38px);
  }
  .page-title {
    font-size: clamp(23px, 1.4vw + 17.8px, 32px);
  }
  .object-title {
    font-size: clamp(22px, 1.25vw + 17.3px, 30px);
  }

  /* The paragraph under a heading: one measure, one colour, everywhere. It
     was 62ch, 64ch and 66ch depending on which page you landed on. */
  .page-lead {
    margin-top: 12px;
    font-size: clamp(13.5px, 0.15vw + 13.1px, 14.5px);
    line-height: 1.6;
    color: var(--ink-2);
  }

  /* --- the page container, and how wide a page is ------------------------
     THE PAGE FILLS. THE MEASURE LIVES ON THE TEXT.

     That is the whole rule, and it resolves an argument that otherwise gets
     re-had on every screen. A capped, centred column is the right shape for
     PROSE and the wrong shape for WORK: a table, a graph, a grid of cards
     wants every pixel it is given. Knowledge capped its pages at 1180px and
     threw away 776px — forty per cent of the usable area — on a 2200px
     screen. Measured.

     ET LA PROSE NE SE PLAFONNE PLUS NON PLUS (décision du propriétaire,
     août 2026, confirmée à l'écran). Les titres, sous-titres, descriptions
     et explications occupent la page. Deux tentatives ont échoué avant
     celle-ci et méritent d'être écrites, parce qu'elles reviendront : une
     MESURE (66ch, 90ch) laisse une colonne étroite au milieu d'un grand
     écran, et DEUX COLONNES coupent la phrase en son milieu — on lit « …de
     ce que les » en bas de la première et « collègues verront » en haut de
     la seconde, ce qui se lit comme un texte tronqué parce que, pour l'œil,
     c'en est un. Ne réintroduisez ni l'une ni l'autre.

     Il reste exactement trois mesures dans ce fichier, et chacune a sa
     raison : .page.form et .page.notice ci-dessous, et .verbatim au §9 —
     une citation réglementaire en serif à 1900px de large ne se lit pas,
     et c'est du texte SOURCE, pas une explication de l'interface.

     Padding scales with the viewport rather than staying still: 16px is
     right on a phone and mean on a desktop.

     Two exceptions, and only two:
       .page.form    a form. A field 1900px wide is unusable — the eye loses
                     the line between label and control. Measured column,
                     centred.
       .page.notice  a page that is one message: not-found, an error. It is
                     a card in the middle of nothing, not a screen. */
  .page {
    width: 100%;
    /* AUCUN PALIER. Une marge qui saute de 16 à 24 à 32 se voit sauter, et
       elle est fausse entre deux paliers — sur une fenêtre de 900px on
       obtient la marge du téléphone. `clamp` interpole : 16px en bas, 32px
       en haut, et tout ce qu'il faut entre les deux. */
    padding: clamp(16px, 2.2vw, 32px);
  }
  /* Un formulaire garde une MESURE, mais elle n'est pas un nombre de pixels :
     `ch` suit le corps du texte, donc elle grandit si le lecteur grossit sa
     police, et `min()` la rend inopérante dès que la fenêtre est plus étroite
     qu'elle. Un champ de 1900px n'est pas un champ — l'œil perd la ligne
     entre l'étiquette et le contrôle. */
  .page.form {
    max-width: min(100%, 68ch);
    margin-inline: auto;
  }
  /* Une page qui n'est qu'un message : une carte au milieu de rien. */
  .page.notice {
    max-width: min(100%, 60ch);
    margin-inline: auto;
    padding-block: clamp(32px, 8vh, 80px);
  }

  /* Figures that line up in columns. Put it on every table of numbers. */
  .tnum {
    font-variant-numeric: tabular-nums;
  }
  /* A hairline that runs to the margin, filling whatever space is left. */
  .rule {
    height: 1px;
    background: var(--line);
    flex: 1;
  }

  /* Section head: title, hairline, aside. The hairline does the work a box
     would otherwise do — this product groups with lines, not with cards. */
  .section-head {
    display: flex;
    align-items: baseline;
    gap: 12px;
    margin-bottom: 12px;
  }
  .section-head h2,
  .section-head .title {
    margin: 0;
    font-size: 12.5px;
    font-weight: 500;
    letter-spacing: -0.01em;
    color: var(--ink);
  }

  /* ======================================================================
     2 · CHROME
     The shell: sidebar, topbar, drawer. All glass, and the topbar overlays
     the scrolling content — a translucent bar over nothing is a dimmer.
     The sidebar's separation from the canvas is TONAL (the glass), not a
     wall: its hairline is --glass-line at 7% ink. Selected navigation
     feels softly elevated, never like a coloured block.
     ====================================================================== */

  /* ─── `clip`, ET SURTOUT PAS `hidden` ───────────────────────────────────
     `overflow: hidden` CLIPPE MAIS CRÉE QUAND MÊME UN CONTENEUR DÉFILABLE.
     La coque n'a donc pas de barre — c'est voulu — mais elle garde un
     `scrollTop` que le navigateur, lui, peut déplacer. Et il le fait : quand
     le focus va sur un élément, il défile TOUS les ancêtres défilables pour
     l'amener à l'écran, coque comprise. Une fois décalée, plus rien ne la
     ramène : ni barre, ni molette, ni clavier.

     MESURÉ le 19 septembre 2026, sur Nouvelle campagne. Les cartes de
     référentiel portent leur bouton radio en `sr-only` — masqué à l'œil,
     présent pour le focus. Un clic sur une carte donnait :

         .app-shell scrollTop      0  →  526,5
         main getBoundingClientRect().top   0  →  −526
         elementFromPoint(400, 800)   du contenu  →  DIV.app-shell

     c'est-à-dire l'application entière poussée hors champ et 526px de coque
     nue en bas de la fenêtre. L'écran devenait blanc, définitivement, et
     aucune erreur nulle part. Remettre `scrollTop` à 0 à la main rendait la
     page intacte : la mise en page n'était pas cassée, elle était DÉCALÉE.

     `overflow: clip` clippe SANS créer de conteneur de défilement. Il n'y a
     donc plus de `scrollTop` à déplacer, et la classe entière disparaît —
     pas seulement ce clic-ci, mais tout focus sur tout élément hors champ,
     dans les sept applications. Vérifié sur place : scrollTop 0, coque à 0,
     du contenu jusqu'en bas, et la carte se coche normalement.

     La charte s'était DÉJÀ fait mordre par cette propriété, sans nommer la
     cause : voir plus bas la note « sous 480px, la sortie se replie », où
     « `.app-shell { overflow: hidden }` ne laisse aucune barre de défilement
     pour aller le chercher » décrit le même piège par son autre face. */
  .app-shell {
    display: flex;
    height: 100dvh;
    overflow: clip;
  }
  /* ══════════════════════════════════════════════════════════════════════
     3.7.1 — TROIS GESTES D'ACCESSIBILITÉ, NÉS DE L'AUDIT DES 119 ÉCRANS.

     L'audit du 28 août 2026 a mesuré ce que la charte promettait sans le
     tenir : aucun des cinq porteurs n'avait de lien d'évitement (WCAG
     2.4.1 — quinze tabulations de chrome avant le contenu, sur chaque
     page) ; les champs de formulaire éteignaient l'anneau global et ne
     signalaient le focus que par une bordure de 1px (2.4.7), quand le
     seuil, lui, restaurait déjà un outline de 2px ; et le spinner de
     bouton tournait sans égard pour prefers-reduced-motion. Trois règles
     — aucune valeur de jeton ne bouge, aucun bloc de thème n'est touché.

     Le lien d'évitement : premier focusable de la coquille (COQUILLE
     1.8.0 le rend, cible #main), invisible au repos, posé au-dessus du
     chrome quand le clavier l'atteint. */
  .skip-link {
    position: fixed;
    inset-block-start: 10px;
    inset-inline-start: 10px;
    z-index: 200;
    padding: 8px 14px;
    border-radius: var(--r-sm);
    background: var(--accent);
    color: var(--on-accent);
    font-size: 12.5px;
    font-weight: 500;
    text-decoration: none;
    transform: translateY(calc(-100% - 14px));
  }
  .skip-link:focus-visible {
    transform: none;
  }
  .sidebar {
    display: flex;
    flex-direction: column;
    /* 248, et non 244 : les quatre pixels payent le chiffre de chapitre du
       §2.1 sans reprendre un pixel au contenu. C'est le seul ajustement de
       proportion du raffinement — la structure ne bouge pas. */
    width: 248px;
    flex-shrink: 0;
    border-right: 1px solid var(--glass-line);
  }
  /* The brand row is exactly the topbar's height, so the two align across
     the seam and the shell reads as one horizon rather than two. */
  .sidebar-brand {
    display: flex;
    flex-shrink: 0;
    align-items: center;
    justify-content: space-between;
    height: 48px;
    padding: 0 16px;
    border-bottom: 1px solid var(--line);
  }
  /* Le rythme vertical est PRÉCIS, pas généreux : 16px de haut, 12px de
     gouttière. La barre doit sembler intentionnelle, pas vide. Le compteur
     de chapitres se remet à zéro ici — voir §2.1. */
  .sidebar-nav {
    flex: 1;
    padding: 16px 12px;
    counter-reset: nav-chapter;
  }
    /* ── L'IDENTITÉ, EN HAUT À DROITE ────────────────────────────────────────
     Décision du propriétaire : elle vivait au pied de la barre latérale, en
     bloc de quatre lignes — sur-titre, nom, rôle, bouton. Elle passe dans la
     barre du haut.

     IL NE RESTE QUE LE NOM. Le sur-titre « connecté » est parti avec le
     déménagement — il annonçait ce que le nom dit déjà. Le rôle est parti
     ensuite, sur décision du propriétaire : « juste le nom ». Il a été retiré
     du CONTRAT de la coquille et pas seulement de son rendu, sans quoi quatre
     produits continueraient de calculer une étiquette que personne ne lit.

     LE NOM S'EFFACE SOUS 768px, pas la déconnexion. La barre y porte déjà le
     menu, la marque et la recherche. Personne n'a besoin qu'on lui rappelle
     son nom sur un téléphone — mais tout le monde doit pouvoir sortir.
     3.13.1 (décision D21 du propriétaire) : sous 768px, les INITIALES le
     remplacent, et ouvrent le menu du compte où vivent le nom entier et la
     sortie — § « 3.13.1 », en fin de fichier. Au-dessus, rien ne change. */
  .topbar-identite {
    display: flex;
    align-items: center;
    gap: 12px;
    min-width: 0;
  }
  .topbar-identite .name {
    margin: 0;
    font-size: 12.5px;
    font-weight: 500;
    letter-spacing: -0.005em;
    color: var(--ink);
    /* largeur:ok — CE N'EST PAS DE LA PROSE, c'est un nom propre dans une
       chrome de 48 pixels de haut. Sans borne, un nom long pousse « se
       déconnecter » hors de la barre : la seule commande qu'on doit toujours
       pouvoir atteindre. La coupure se fait par ellipse, le nom entier reste
       lisible au survol par l'attribut natif du navigateur. */
    max-width: 22ch;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  /* La déconnexion n'est pas un bouton tant qu'on ne la vise pas. Au repos
     c'est une ligne de texte ; au survol et au focus elle prend un sol et
     redevient une commande. Un bouton permanent attire l'œil vers la seule
     action qu'on ne veut pas encourager. */
  /* 3.9.0 — LE PLAN SE TROMPAIT SUR CE BOUTON, ET IL RESTAIT UNE CHOSE À
     CORRIGER.

     Le plan 3.9.0 l'annonçait « la seule commande de la barre sous 28 px
     (≈24–25 px, au plancher exact 2.5.8) ». Mesuré le 16 septembre 2026 dans
     un moteur, à 1280px : 28,59 px. `font: inherit` reprend la hauteur de
     ligne du corps — 1,55 sans unité — donc 12 × 1,55 = 18,6 px, plus les
     10 px de remplissage. Le bouton n'a jamais été au plancher : il était
     déjà, de six dixièmes, le plus haut des trois contrôles de la barre.
     Sous 480px, où il se replie en icône, il fait 28 px pile.

     `min-height` RESTE ÉCRIT, comme un PLANCHER et non comme une réparation.
     Il ne change rien au rendu d'aujourd'hui — il interdit à demain de
     descendre sous les 28 px de `.icon-btn` et `.search-trigger` si
     quelqu'un touche au corps ou au remplissage. Ce qui se mesure en 2.5.8,
     ici, est déjà tenu.

     CE QUI CHANGE VRAIMENT TIENT DANS LA DERNIÈRE LIGNE : `140ms ease` était
     la seule durée brute du chrome, et la maison dit 120 ms et UNE courbe
     (§0 mouvement). 20 ms et une courbe d'écart ne se voient pas seuls ; ils
     se voient à côté du survol de la barre latérale, qui part et arrive
     autrement. La valeur littérale rejoint ses jetons. */
  .topbar-identite .signout {
    border: 0;
    background: none;
    min-height: 28px;
    padding: 5px 9px;
    border-radius: var(--r-sm);
    font: inherit;
    font-size: 12px;
    color: var(--ink-3);
    cursor: pointer;
    white-space: nowrap;
    transition: background-color var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
  }
  .topbar-identite .signout:hover,
  .topbar-identite .signout:focus-visible {
    background: var(--surface-2);
    color: var(--ink);
  }
  @media (max-width: 767px) {
    .topbar-identite .name { display: none; }
  }

  /* ─── SOUS 480px, LA SORTIE SE REPLIE PLUTÔT QUE DE DISPARAÎTRE ─────────
     La règle d'au-dessus efface le nom à 768px et dit pourquoi : « tout le
     monde doit pouvoir sortir ». Elle était juste et ne suffisait pas.

     MESURÉ, pas déduit. Sur Evidence à 320px, le nom bien masqué, la barre
     d'identité tombait quand même à 376px de bord droit — « Se déconnecter »
     entièrement hors champ, et `.app-shell { overflow: hidden }` ne laisse
     aucune barre de défilement pour aller le chercher. Sur Vendor Connect à
     375px, même chose : le bouton se dessinait de 433 à 541.

     Le compte est arithmétique. À 320px la moitié gauche — menu, marque,
     recherche — prend 180px ; il reste 140px pour trois contrôles qui en
     réclament 180 : la langue (46), le thème (26), la déconnexion (108).
     Aucune des trois ne cédait : `.btn` est `flex-shrink: 0` et `.signout`
     est `nowrap`.

     LE LIBELLÉ SE REPLIE, LE BOUTON RESTE. La déconnexion devient une cible
     carrée de 28px portant la même icône que ses voisines — et son nom
     accessible ne bouge pas : le balisage porte le mot dans le bouton, que
     `.signout-mot` masque VISUELLEMENT sans le retirer de l'arbre
     d'accessibilité. Un lecteur d'écran entend « Se déconnecter » ; un doigt
     trouve une cible de 28px ; la barre tient dans 320.

     Pourquoi replier plutôt que masquer un voisin : la langue et le thème
     sont des préférences qu'on peut chercher ailleurs, la sortie ne se
     cherche pas. Et masquer un contrôle sans remplacement est précisément ce
     que cette charte reproche ailleurs. */
  @media (max-width: 479px) {
    .topbar-identite { gap: 8px; }
    .topbar-identite .signout {
      width: 28px;
      height: 28px;
      padding: 0;
      display: inline-flex;
      align-items: center;
      justify-content: center;
    }
    /* Masqué à l'œil, gardé pour l'oreille — la technique de `.sr-only`,
       appliquée au seul mot. Un `display: none` le retirerait de l'arbre
       d'accessibilité et le bouton deviendrait muet.
       3.13.0 : il le peut désormais — la coquille 1.11.0 porte le nom sur le
       bouton (`aria-label`), et le § 3 de « LA QUALITÉ D'iOS », en fin de
       fichier, retire le mot par `display: none`. */
    .topbar-identite .signout .signout-mot {
      position: absolute;
      width: 1px;
      height: 1px;
      padding: 0;
      margin: -1px;
      overflow: hidden;
      clip-path: inset(50%);
      white-space: nowrap;
      border: 0;
    }
    .topbar-identite .signout::after {
      content: "";
      width: 14px;
      height: 14px;
      background: currentColor;
      -webkit-mask: var(--icone-sortie) center / contain no-repeat;
      mask: var(--icone-sortie) center / contain no-repeat;
    }
  }
/* --- the product switcher ---------------------------------------------
     Two applications, one platform. The switcher lives in the brand row
     because that is where a reader already looks to answer "where am I" —
     and the honest answer is a product name, not a logo alone.

     It shows the current product and opens a list of the others. A portal
     page can still exist for signed-out entry, but it must not be the only
     way across: once you are inside, going back out to change application
     is a detour nobody takes twice. */
  .product-switch {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    height: 24px;
    padding: 0 6px;
    border: 0;
    border-radius: var(--r-sm);
    background: transparent;
    color: var(--ink);
    font: inherit;
    font-size: 13.5px;
    font-weight: 500;
    letter-spacing: -0.008em;
    cursor: pointer;
    transition: background-color var(--t-fast) var(--ease);
  }
  .product-switch:hover {
    background: var(--surface-3);
  }
  .product-switch .chevron {
    color: var(--ink-3);
    transition: transform var(--t-fast) var(--ease);
  }
  .product-switch[aria-expanded="true"] .chevron {
    transform: rotate(180deg);
  }

  /* The list. Floating level: glass, a white inner rim, a soft shadow —
     anchored under the brand row rather than centred: it belongs to that
     corner. */
  .product-menu {
    position: absolute;
    top: 44px;
    left: 12px;
    z-index: 45;
    min-width: 208px;
    padding: 4px;
    border: 1px solid var(--glass-edge);
    border-radius: var(--r-float);
    box-shadow: var(--shadow-lg);
  }
  .product-item {
    display: block;
    padding: 7px 10px;
    border-radius: var(--r-sm);
    color: var(--ink-2);
    text-decoration: none;
    font-size: 12.5px;
  }
  .product-item:hover {
    background: var(--surface-2);
    color: var(--ink);
  }
  /* The one you are in is named, not hidden: a switcher that omits the
     current product makes you count the list to work out where you are. */
  .product-item[aria-current="page"] {
    background: var(--surface-2);
    color: var(--ink);
    font-weight: 500;
  }

  /* Navigation is grouped, and every group is named and set apart. An
     unnamed run of fourteen destinations is a list nobody reads twice, and
     groups separated by margin alone read as one long list with gaps. The
     hairline is what makes them groups — this product divides with lines,
     not with boxes. */
  /* --- 2.1 · les chapitres ----------------------------------------------
     La navigation n'est pas une liste : c'est une TABLE DES MATIÈRES. Chaque
     groupe porte un numéro discret — 01, 02, 03 — engendré par un compteur
     CSS et non écrit dans le balisage : aucune application n'a à retoucher
     son menu pour en hériter, et personne ne peut renuméroter à la main un
     ordre que le DOM connaît déjà.

     Le numéro ne domine jamais. Il est en Mist, sans graisse, tabulaire, et
     il s'aligne sur le titre par la ligne de base. Ce qu'on veut faire
     sentir, c'est un ordre de chapitres — pas une nomenclature. */
  .nav-group {
    counter-increment: nav-chapter;
  }
  .nav-group + .nav-group {
    margin-top: 32px;
    padding-top: 24px;
    border-top: 1px solid var(--line);
  }
  .nav-group > .register {
    display: flex;
    align-items: baseline;
    gap: 10px;
    padding: 0 10px 16px;
    color: var(--ink-3);
  }
  .nav-group > .register::before {
    content: counter(nav-chapter, decimal-leading-zero);
    flex-shrink: 0;
    font-variant-numeric: tabular-nums;
    font-weight: 400;
    letter-spacing: 0.04em;
    /* Ni --ink-3 (il concurrencerait le titre) ni --ink-4 (le Mist mesure
       1,54:1 sur le canvas : un chiffre qu'on ne voit pas n'ordonne rien).
       Un token à lui, mesuré au plancher des éléments non textuels — 3,28:1
       le jour, 3,55:1 la nuit : présent, jamais dominant. */
    color: var(--nav-chapter);
  }
  /* UN CHAPITRE UNIQUE NE SE NUMÉROTE PAS. Le numéro existe pour donner un
     ORDRE — 01 puis 02 puis 03 — et une application qui n'a qu'un groupe
     n'a rien à ordonner : « 01 » y est du bruit qui promet une suite qui
     n'existe pas. Le jour où elle grandit, les numéros reviennent seuls,
     sans que personne n'ait à y penser. */
  .nav-group:first-child:last-child > .register::before {
    content: none;
  }

  /* The column the topbar floats over — it must be positioned, or an
     absolute topbar escapes to the viewport. */
  .main-col {
    position: relative;
    display: flex;
    flex: 1;
    min-width: 0;
    flex-direction: column;
  }
  .topbar {
    position: absolute;
    inset-inline: 0;
    top: 0;
    z-index: 20;
    display: flex;
    align-items: center;
    gap: 12px;
    height: 48px;
    padding: 0 16px;
    border-bottom: 1px solid var(--glass-line);
  }
  /* The scroll container the topbar floats over. Its padding is the topbar's
     height, so content starts below the glass and passes under it. */
  .main {
    flex: 1;
    min-width: 0;
    padding-top: 48px;
  }

  /* Same navigation, as a drawer, where the sidebar has no room. Not a
     different menu — the same one, so nobody has to learn two. */
  .scrim {
    position: fixed;
    inset: 0;
    z-index: 40;
    border: 0;
    padding: 0;
    background: var(--voile);
    -webkit-backdrop-filter: blur(4px);
    backdrop-filter: blur(4px);
  }
  /* 3.9.0 — LE VOILE D'UN DIALOGUE OU D'UN TIROIR D'APPLICATION. Même recette
     que `.scrim`, sans `z-index` : c'est le conteneur `fixed` de l'écran qui
     le pose. Collect écrivait `bg-black/50`, `/30`, `/25` et `/20` dans
     vingt-trois fichiers — quatre opacités d'un noir hors jeton, qui survit
     au thème sombre et qu'aucune porte ne voyait. */
  .voile {
    position: absolute;
    inset: 0;
    border: 0;
    padding: 0;
    background: var(--voile);
    -webkit-backdrop-filter: blur(4px);
    backdrop-filter: blur(4px);
  }
  .drawer {
    position: fixed;
    inset-block: 0;
    left: 0;
    z-index: 41;
    display: flex;
    width: 268px;
    flex-direction: column;
    border-right: 1px solid var(--glass-line);
    box-shadow: var(--shadow-lg);
  }

  /* One breakpoint: below it the sidebar becomes the drawer. */
  @media (max-width: 767px) {
    .sidebar { display: none; }
  }

  /* A navigation item. The active one is marked twice — a rail on the left
     and a softly elevated ground — because colour alone must never carry
     state. Radius 8, a quiet forest accent, no coloured block. */
  /* --- 2.2 · les entrées, et le repère de position -----------------------
     Purement typographiques : aucune icône devant les libellés. Ce qui
     distingue une entrée d'une autre, c'est le mot ; ce qui distingue
     l'entrée COURANTE, c'est un repère, pas une pastille.

     L'ÉTAT ACTIF N'EST PLUS UN BOUTON SÉLECTIONNÉ. La 1.x peignait un
     rectangle gris arrondi — le réflexe SaaS, qui fait ressembler une
     navigation à une barre d'outils. La 3.0 marque la position comme le
     ferait un document soigné : un filet vertical de 2px à l'extrême
     gauche, le libellé qui passe au Deep Plum et prend une demi-graisse, et
     un voile de plum à 5 % qu'on ne doit pas pouvoir nommer. Trois signaux
     faibles valent mieux qu'un aplat : la couleur seule ne porte jamais un
     état, et le filet survit au daltonisme comme au niveau de gris. */
  .nav-item {
    position: relative;
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 7px 10px;
    border-radius: var(--r-sm);
    /* 12,5px : la taille de TOUTE l'interface — lignes d'objet, onglets,
       éléments du sélecteur de produit. Le raffinement était monté à 15px ;
       une navigation plus grosse que le contenu qu'elle sert se met en
       avant, et la barre ne doit pas concurrencer la page. */
    font-size: 12.5px;
    font-weight: 400;
    letter-spacing: -0.004em;
    color: var(--ink-2);
    text-decoration: none;
    transition: background-color var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
  }
  .nav-item + .nav-item {
    margin-top: 2px;
  }
  .nav-item:hover {
    background: var(--surface-2);
    color: var(--ink);
  }
  .nav-item[aria-current="page"] {
    background: color-mix(in srgb, var(--accent) 6%, transparent);
    font-weight: 500;
    color: var(--ink);
  }
  /* Le repère. `inset-inline-start: -10px` le pose dans la gouttière de la
     barre, aligné sur le bord du texte du chapitre au-dessus — un repère
     qui flotte à l'intérieur du bloc marquerait le bloc, pas la position. */
  .nav-item[aria-current="page"]::before {
    content: "";
    position: absolute;
    inset-block: 4px;
    inset-inline-start: -10px;
    width: 2px;
    border-radius: 2px;
    background: var(--accent);
  }
  /* Un compte non lu ou une attente sur le menu : PLEIN et VIF (décision du
     propriétaire) — le chiffre posé sur la pastille, mesuré. Framboise
     (`--critical-vif`) jusqu'à la 3.14.2 ; BLEU depuis la 3.14.3 (D61,
     « les pastilles de Proqr ne sont pas bleues sur le menu ») : la règle
     prend les jetons de la voix des propositions, ceux que `.badge-count`
     porte depuis D42, pour que les deux pastilles du menu aient le même
     bleu en jour comme en nuit PAR CONSTRUCTION — deux jetons de mêmes
     valeurs pourraient diverger. Les deux ont la même forme ; ce qui les
     distingue est le contenu — le titre de l'entrée, qui nomme ce qui est
     compté — et la police du chiffre (chiffres tabulaires de l'interface
     ici, chasse fixe pour la proposition), jamais la teinte. La barrière
     de contraste (1.6.0) exige que les deux règles portent le même couple
     de jetons. `--critical-vif` ne bouge pas : un statut critique
     reste framboise. Mesuré : blanc sur la pastille 6,99:1 en jour, 4,81:1
     la nuit ; la pastille sur le menu 6,99:1 et 3,92:1 — 3,67:1 sur le
     tiroir de nuit (sous 768px, #1c171c). */
  .nav-item .count {
    margin-left: auto;
    min-width: 18px;
    padding: 1px 6px;
    border-radius: 999px;
    background: var(--proposed-vif);
    color: var(--on-proposed);
    text-align: center;
    font-size: 11.5px;
    font-weight: 500;
    font-variant-numeric: tabular-nums;
  }

  /* ======================================================================
     3 · ACTIONS
     ONE shape: 30px tall, 14px of air each side, radius 10, 11.5px
     medium. The primary wears the forest and a subtle inner top light —
     the one place outside surfaces the light is allowed, because a
     primary action is a small surface you press. Hover brightens and
     lifts by half a pixel; active settles back. Micro-movement tops out
     at 1px. The default IS the primary: one per view, everything else
     .secondary.

     .secondary is quiet glass: the translucent ivory surface, a fine
     hairline, ink text. No backdrop blur by default — a row of buttons
     is not a floating layer; a .secondary that genuinely floats (in a
     topbar, over content) may add .glass and earn the blur.

     .ghost is the tertiary: no container, forest text, a whisper of
     ground on hover.

     There is no red button. A destructive action says what it does in
     words — "Remove and sign" — and red stays with alerts and failures.
     Verbs are the commitment, not the mechanism: "Accept and sign", never
     "OK". Disabled means "not yet valid", with the reason visible nearby.

     The 30px was DECLARED and not TRUE until 1.5.0. A button placed in a
     flex row is a flex item like any other: it is compressed below its
     content, the label wraps to a second line, and that line spills out of
     a box whose height does not move. It is the sentence beside the button
     that must reflow — prose has a natural second line, a 30px control has
     none.
     ====================================================================== */

  .btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    height: 30px;
    /* What makes the height above true rather than merely stated. */
    flex-shrink: 0;
    white-space: nowrap;
    padding: 0 14px;
    border: 1px solid var(--accent);
    border-radius: var(--r-btn);
    background: var(--accent);
    color: var(--on-accent);
    font-family: inherit;
    font-size: 11.5px;
    font-weight: 500;
    line-height: 1;
    cursor: pointer;
    text-decoration: none;
    box-shadow: inset 0 1px 0 rgb(255 255 255 / 0.12);
    transition: background-color var(--t-fast) var(--ease),
      border-color var(--t-fast) var(--ease),
      box-shadow var(--t-fast) var(--ease),
      transform var(--t-fast) var(--ease);
  }
  .btn:hover {
    background: var(--accent-hover);
    border-color: var(--accent-hover);
    box-shadow: inset 0 1px 0 rgb(255 255 255 / 0.14), 0 1px 2px color-mix(in srgb, var(--accent) 10%, transparent);
    transform: translateY(-0.5px);
  }
  .btn:active {
    transform: translateY(0);
    background: var(--accent-active);
    border-color: var(--accent-active);
    box-shadow: inset 0 1px 0 rgb(255 255 255 / 0.08);
  }
  /* Le Coral en micro-détail d'interaction, exactement comme le demande le
     conseil : un anneau de focus sur l'action primaire, et rien de plus.
     Le ton reste celui du conseil — --brand-coral n'est pas retouché,
     seul cet anneau reçoit une pointe de noir, sous le seuil où l'œil
     la remarque, pour que le Coral tienne enfin son 3:1 en clair (il
     ratait à 2,95:1). */
  .btn:focus-visible {
    outline: 2px solid color-mix(in srgb, var(--brand-coral) 92%, black);
    outline-offset: 2px;
  }
  /* Le secondaire : une surface de verre calme, bordée de Mist — la
     couleur que le conseil donne aux filets. Texte sombre, profondeur
     minimale. */
  .btn.secondary {
    border-color: var(--line-strong);
    background: var(--surface);
    color: var(--ink);
    box-shadow: var(--edge-light);
  }
  .btn.secondary:hover {
    border-color: var(--ink-3);
    background: var(--elevated);
    transform: none;
  }
  /* Le tertiaire porte le CORAL, pas le plum : c'est la signature de
     marque employée là où le conseil la veut — une emphase menée par le
     texte. --coral-ink est la voix lisible du coral (le pigment lui-même
     mesure 3,06:1 et ne porte pas de texte). */
  .btn.ghost {
    border-color: transparent;
    background: transparent;
    color: var(--coral-ink);
    box-shadow: none;
  }
  .btn.ghost:hover {
    background: var(--coral-wash);
    color: var(--coral-ink);
    transform: none;
    box-shadow: none;
  }
  /* 3.9.0 — `[disabled]` EST UN ATTRIBUT, `:disabled` EST UN ÉTAT, ET LA
     DIFFÉRENCE SE VOYAIT À L'ÉCRAN.

     Un sélecteur d'attribut ne matche que si l'attribut est écrit sur
     l'élément lui-même. Or quatre porteurs — Console, Trust, Proqr,
     Vendor-Connect — désactivent leurs formulaires par un
     `<fieldset disabled>` pendant l'envoi : le bouton hérite de l'ÉTAT sans
     recevoir l'ATTRIBUT. Il matchait donc `:disabled` et `[disabled]` non.
     Résultat mesuré : pendant tout l'envoi, le bouton gardait son aplat
     d'accent, son survol qui se soulève et son curseur de main. L'attente
     était annoncée aux lecteurs d'écran et muette à l'œil — exactement
     l'inverse du défaut qu'on corrige d'habitude.

     Les deux s'écrivent, parce que les deux arrivent. Aucune valeur ne
     change : c'est le même dessin, offert à un cas de plus. */
  .btn[disabled],
  .btn:disabled,
  .btn[aria-disabled="true"] {
    opacity: 0.4;
    cursor: not-allowed;
    transform: none;
  }
  /* Loading: the button holds its place and says so. Set aria-busy and
     KEEP the label — a spinner replacing words is a button that stopped
     explaining itself. */
  .btn[aria-busy="true"] {
    pointer-events: none;
    opacity: 0.75;
  }
  .btn[aria-busy="true"]::before {
    content: "";
    width: 10px;
    height: 10px;
    flex-shrink: 0;
    border-radius: 999px;
    border: 1.5px solid currentColor;
    border-top-color: transparent;
    animation: btn-spin 0.7s linear infinite;
  }
  @keyframes btn-spin {
    to { transform: rotate(360deg); }
  }
  /* 3.9.0 — LE FORMULAIRE PORTE L'ATTENTE, PAS SEULEMENT LE BOUTON.
     `aria-busy` sur le <form> est ce que les ActionForm savent déjà poser
     (`aria-busy={pending}`) ; la charte ne l'écoutait que sur le bouton, si
     bien qu'il fallait le poser DEUX fois pour que l'écran le dise une.

     `button.btn:not([type])` ET NON `.btn:not([type])` : un <button> sans
     type dans un formulaire EST un bouton de soumission, mais un
     `<a class="btn">` n'a pas de type non plus. La forme large du plan
     3.9.0 aurait éteint le lien « Annuler » posé à côté du bouton pendant
     tout l'envoi — on aurait enfermé le lecteur dans un formulaire parti.
     Le même geste, restreint à ce qui soumet. */
  form[aria-busy="true"] .btn[type="submit"],
  form[aria-busy="true"] button.btn:not([type]) {
    pointer-events: none;
    opacity: 0.75;
  }
  @media (prefers-reduced-motion: reduce) {
    /* 3.7.1 : la seule animation continue de la charte s'arrête quand le
       lecteur l'a demandé — aria-busy et l'opacité disent déjà l'attente. */
    .btn[aria-busy="true"]::before { animation: none; }
  }
  /* 3.9.0 — « LA SEULE ANIMATION CONTINUE » ÉTAIT VRAI, ET NE SUFFISAIT PAS.
     La 3.7.1 a arrêté le spinner parce que c'est ce qui TOURNE. Compté le
     16 septembre 2026 dans ce fichier : 13 autres transitions et deux
     transformations au survol continuaient de jouer — le bouton qui se
     soulève, le chevron du sélecteur de produit qui bascule, le fond des
     items de navigation. Six produits sur sept les recevaient ; seul Collect
     les neutralisait chez lui (globals.css l.141-148), c'est-à-dire que la
     charte laissait à chaque porteur le soin de réparer la charte.

     LE `*` PORTE LE GROS DU TRAVAIL, ET NE PEUT PAS TOUT. Une durée à zéro
     supprime le trajet, pas la destination : `.btn:hover` pose un
     `translateY(-0.5px)` STATIQUE, qui saute instantanément au lieu de
     glisser — un mouvement plus brusque qu'avant, pas moins. Il faut le
     nommer. Le chevron à `rotate(180deg)`, lui, RESTE : il dit un état
     ouvert, ce n'est pas une décoration animée.

     DEUX DÉCLARATIONS DU PLAN 3.9.0 NE SONT PAS ÉCRITES ICI. `.skip-link`
     et `.product-switch .chevron` n'avaient rien à arrêter — le premier ne
     déclare aucune transition, la seconde est déjà couverte par la durée
     universelle ci-dessous. Une règle qui ne fait rien fait croire qu'elle
     garde quelque chose.

     Aucune valeur de repos ne change : au repos, rien ne bouge déjà. */
  @media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
      animation-duration: 0.001ms !important;
      animation-iteration-count: 1 !important;
      transition-duration: 0.001ms !important;
      scroll-behavior: auto !important;
    }
    .btn:hover,
    .btn:active {
      transform: none;
    }
  }
  /* Actions sit in a row with one gap, and wrap rather than overflow. */
  .btn-row {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
  }

  /* An icon-only control: square, quiet, and never the primary action. */
  .icon-btn {
    display: grid;
    place-items: center;
    width: 28px;
    height: 28px;
    border: 0;
    border-radius: var(--r-sm);
    background: transparent;
    color: var(--ink-2);
    cursor: pointer;
    transition: background-color var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
  }
  .icon-btn:hover {
    background: var(--surface-3);
    color: var(--ink);
  }

  /* ======================================================================
     4 · INPUTS
     The guardrail is the form: what cannot be entered cannot be wrong.
     ====================================================================== */

  .field {
    display: flex;
    flex-direction: column;
    gap: 4px;
    /* `min()` et pas une valeur nue : à 375px, un plancher de 220px dans une
       rangée qui ne replie pas pousse la ligne hors de l'écran. Le champ
       garde sa mesure quand la place existe, et cède quand elle n'existe
       pas — ce qui est exactement ce qu'un plancher doit faire. */
    min-width: min(220px, 100%);
  }
  .field > span {
    /* 3.9.0 — 12px ET --ink-2 : c'est l'ÉTIQUETTE d'un champ, pas un
       sur-titre. À 11px sur --ink-3 elle rendait 4,23:1 sur `--surface-3`,
       sous le plancher du texte, et se lisait plus mal que ce qu'elle nomme.
       `.register` (10px, 0,14em) reste à sa taille : c'est une mesure de
       maison pour l'identité d'une section, pas pour une étiquette qu'on lit
       en remplissant un formulaire. */
    font-size: 12px;
    color: var(--ink-2);
  }
  .field.error > span {
    color: var(--critical);
  }
  .field :is(input, select, textarea) {
    height: 32px;
    padding: 0 8px;
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    background: var(--surface-2);
    color: var(--ink);
    font: inherit;
    font-size: 12.5px;
    outline: none;
    transition: border-color var(--t-fast) var(--ease);
  }
  .field :is(input, select, textarea):focus {
    border-color: var(--accent);
  }
  /* 3.7.1 : le § seuil restaurait déjà l'outline de 2px que le
     `outline: none` ci-dessus éteint ; les champs du produit ne signalaient
     le focus clavier que par la bordure de 1px — visible, mais faible.
     Même geste ici. */
  .field :is(input, select, textarea):focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 1px;
  }
  .field :is(input, textarea)::placeholder {
    /* Never --ink-4: a placeholder carrying guidance is text, and ink-4
       fails AA at any size. */
    color: var(--ink-3);
  }
  .field textarea {
    height: auto;
    padding: 8px;
    line-height: 1.55;
  }
  /* 3.9.0 — TROIS ÉTATS QUE LE CHAMP VIVAIT SANS JAMAIS LES DIRE.

     DÉSACTIVÉ. Même mesure que le bouton juste au-dessus : les formulaires
     des quatre porteurs à ActionForm passent par `<fieldset disabled>`, et
     un champ dont l'état vient de son ancêtre gardait très exactement
     l'apparence d'un champ qu'on peut remplir. 0,55 et non 0,4 comme le
     bouton : le contenu d'un champ désactivé se LIT encore — c'est souvent
     la valeur qu'on est venu vérifier — là où l'étiquette d'un bouton
     éteint n'a plus rien à dire.

     LECTURE SEULE. Elle se dessine par le RETRAIT du sol et un trait
     pointillé : ce qui a l'air d'une boîte remplissable et refuse la frappe
     est plus coûteux qu'un texte posé.
     `[readonly]` ET NON `:read-only` — le plan 3.9.0 proposait la
     pseudo-classe, et elle est fausse ici. Par spécification, `:read-only`
     matche tout ce qui n'est pas `:read-write`, donc AUSSI les cases à
     cocher, les boutons radio, les curseurs, les sélecteurs de couleur et
     de fichier : aucun de ces types n'accepte l'attribut `readonly`, tous
     sont `:read-only` en permanence. La règle aurait mis un contour
     pointillé et retiré le sol de chaque case à cocher de la plateforme.
     Elle matche aussi un champ désactivé, qui aurait alors reçu les deux
     dessins à la fois. L'attribut dit exactement ce qu'on vise.

     INVALIDE. 3.3.1 : aucun des sept ne rattachait le refus au champ
     fautif, faute d'une règle à qui l'accrocher — seule l'étiquette
     rougissait (`.field.error > span`), c'est-à-dire que dans une rangée de
     six champs, la couleur désignait un mot et pas une boîte. Le contour
     porte maintenant le même --critical, et `.erreur` pose le message sous
     le champ qu'il concerne (5,70:1 en jour, 7,88:1 en nuit sur le papier).

     12px ET NON 11,5px comme l'écrivait le plan : la même version monte
     `.field > span` à 12px. Un message d'erreur rendu PLUS PETIT que
     l'étiquette du champ qu'il corrige inverse l'ordre de ce qu'il faut
     lire. Les deux se lisent à la même taille sous le même champ. */
  .field :is(input, select, textarea):disabled {
    opacity: 0.55;
    cursor: not-allowed;
  }
  .field :is(input, textarea)[readonly] {
    background: transparent;
    border-style: dashed;
  }
  .field :is(input, select, textarea)[aria-invalid="true"] {
    border-color: var(--critical);
  }
  .field .erreur {
    margin-top: 4px;
    font-size: 12px;
    line-height: 1.5;
    color: var(--critical);
  }

  /* The search affordance in the topbar: a button that looks like a field,
     because search is a layer over the page, not a destination. */
  .search-trigger {
    display: flex;
    align-items: center;
    gap: 8px;
    height: 28px;
    min-width: 0;
    padding: 0 10px;
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    background: var(--surface-2);
    color: var(--ink-2);
    font-size: 12.5px;
    text-align: left;
    cursor: pointer;
    transition: border-color var(--t-fast) var(--ease), background-color var(--t-fast) var(--ease);
  }
  .search-trigger:hover {
    border-color: var(--line-strong);
    background: var(--surface);
  }

  .kbd {
    padding: 1px 6px;
    border: 1px solid var(--line);
    border-radius: var(--r-xs);
    background: var(--surface-3);
    color: var(--ink-2);
    font-family: var(--font-mono);
    font-size: 11.5px;
  }

  /* LE RACCOURCI QUI DÉBORDAIT SUR SON VOISIN.
     `.search-trigger` porte `min-width: 0` — il le faut, sinon la barre du
     haut ne se resserre pas — et `overflow` restait à `visible`. Sous 768px
     le déclencheur tombe à 22px de large ; son `<kbd>⌘K</kbd>`, poussé par
     `margin-left: auto`, se dessinait alors 18px AU-DELÀ de son propre bord
     droit, c'est-à-dire par-dessus le bouton de langue. Mesuré sur Proqr à
     375px : le kbd occupait x 167→193, le bouton « EN » x 161→208. Les deux
     glyphes se superposaient et aucun des deux n'était lisible.
     Les clics, eux, atteignaient bien le bouton — c'est un défaut d'image,
     pas de cible, et le dire évite de chercher un bug d'interaction.

     Deux lignes, et chacune fait la moitié du travail. `overflow: hidden`
     est la garantie : quoi qu'on mette un jour dans ce bouton, rien n'en
     sortira. Retirer le raccourci du flux sous 768px est la finition : un
     ⌘K coupé au tiers n'apprend rien, et une machine à écran de 375px n'a
     de toute façon pas de touche Commande. */
  .search-trigger { overflow: hidden; }
  @media (max-width: 767px) {
    .search-trigger .kbd { display: none; }
  }

  /* Editing in place. Three states, and the third is the important one: a
     save is an event, so it is announced, and undoing it is another event
     rather than a rollback. */
  .editable .view {
    padding: 1px 2px;
    border-radius: var(--r-sm);
    cursor: text;
  }
  .editable .view:hover {
    background: var(--surface-2);
  }
  .editable .editing {
    width: 100%;
    padding: 3px 6px;
    border: 1px solid var(--accent);
    border-radius: var(--r-sm);
    background: var(--surface-solid);
    color: var(--ink);
    font: inherit;
  }
  .editable .hint {
    margin-top: 2px;
    font-size: 11.5px;
    color: var(--ink-3);
  }
  .editable .saved {
    display: flex;
    gap: 8px;
    margin-top: 4px;
    font-size: 11.5px;
  }
  .editable .saved .okmsg {
    color: var(--ok);
  }

  /* ======================================================================
     5 · MARKS
     Meaning first, and quiet. A chip labels an object; a pill states its
     condition; a glyph says which family it belongs to. No large
     saturated pills — soft tonal grounds, compact dimensions, restrained
     semantic accents.
     ====================================================================== */

  .chip {
    display: inline-flex;
    align-items: center;
    height: 24px;
    padding: 0 9px;
    border: 1px solid var(--line);
    border-radius: 999px;
    background: var(--surface-2);
    color: var(--ink-2);
    /* 3.9.0 — 11,5px : une marque NOMME un objet, on la lit. La hauteur de
       24px ne bouge pas (décision 3.1.5, la cible tactile). */
    font-size: 11.5px;
  }
  /* 3.9.0 — UNE CHIP QUI EST UN LIEN OU UN BOUTON LE MONTRE. Les filtres de
     Knowledge, Evidence, Proqr et Vendor-Connect, les réponses à choix
     multiples de Trust : toutes des chips cliquables sans curseur, sans
     survol, sans état choisi lisible. Et la chip ACTIVE se distinguait par
     `--surface-3` sur `--surface-2` — 1,07:1 mesuré, c'est-à-dire rien.
     Elle prend l'encre et le contour PLEINS ; le trait actif est `--ink` et
     non `--line`, la décision 3.1.7 n'est pas touchée. */
  a.chip, button.chip {
    cursor: pointer;
    transition: border-color var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
  }
  a.chip:hover, button.chip:hover { border-color: var(--line-strong); color: var(--ink); }
  .chip[aria-current], .chip.actif, button.chip[aria-pressed="true"] {
    border-color: var(--ink);
    color: var(--ink);
    font-weight: 500;
  }

  /* `.chip.accent` A CESSÉ D'ÊTRE `.chip.ok` — MAIS SEULEMENT LA NUIT.
     Jusqu'à la 3.4.0 elle lisait `--ok-vif`, parce que `--accent` et `--ok`
     portaient exactement la même valeur : #184A45 le jour, #3FAFA0 la nuit.
     L'accent structurel et le vert du succès étaient le même vert, et c'était
     une décision de palette, pas un accident.

     La 3.4.0 donne à l'accent une identité framboise EN SOMBRE UNIQUEMENT.
     Cette classe lit donc `--accent` / `--on-accent`, sans quoi vingt-deux
     marques de Collect seraient restées VERTES sous un nom qui promet
     l'accent. En clair le changement est un no-op au bit près : `--accent` y
     vaut #184A45 comme `--ok-vif`, et `--on-accent` #FDFAF8 comme `--on-ok`.

     LA RÈGLE QUI EN DÉCOULAIT SURVIT, ET C'EST LE PIÈGE : en CLAIR, `accent`
     et `ok` restent le même pixel, donc ils ne peuvent toujours JAMAIS
     distinguer deux états l'un de l'autre. Le prix a été payé dans Collect,
     où la table des campagnes donnait `ACTIVE → accent` et `CLOSED → ok` :
     une campagne en cours et une campagne close portaient la même pastille,
     et aucune barrière ne pouvait le voir puisque les deux règles étaient
     correctes prises séparément. Depuis la 3.4.0 ce défaut est INVISIBLE EN
     SOMBRE et INTACT EN CLAIR — donc plus dangereux qu'avant, parce que qui
     développe la nuit ne le verra plus.

     3.5.0 — CE QUE LA 3.4.0 AVAIT LAISSÉ DERRIÈRE ELLE. Déplacer l'accent vers
     la framboise n'a pas déplacé `--ok`, qui est resté à #3FAFA0 : c'est-à-dire
     à L'ANCIEN ACCENT, devenu orphelin. Ce n'était plus une décision de
     palette, c'était un reste. Et il se voyait partout où la nuit peint un
     succès — `.gauge .fill`, les jauges d'avancement, les cellules de
     couverture, les pastilles d'avance : le propriétaire l'a signalé comme
     « du vert resté au lieu de la framboise », ce qui décrit exactement
     l'impression que produit un turquoise dont la raison d'être a disparu.

     La 3.5.0 donne donc à `--ok` un VRAI VERT en sombre, #52B075. La mesure
     dit pourquoi celui-là et pas un autre : sa teinte est 142° quand l'ancien
     était à 172° — turquoise contre vert, la différence que l'œil réclamait —
     mais son contraste sur `--paper` vaut 7,03:1 là où l'ancien valait 7,05:1.
     À un centième près, RIEN NE CHANGE DE POIDS : une barre d'avancement garde
     exactement la présence qu'elle avait, elle cesse seulement d'être bleue-
     verte. Toute la famille suit la même règle — luminance conservée, teinte
     corrigée : `--on-ok` #0B1A11 rend 6,69:1 sur le vert quand #0B1A18 rendait
     6,66:1 sur le turquoise, et les deux lavis gardent leurs 1,25:1 et 1,44:1.

     ET LE PIÈGE DU CLAIR SE REFERME ENFIN. `accent` et `ok` ne sont plus le
     même pixel en sombre : ΔE 101,8 les sépare. En clair ils le restent
     (#184A45 tous les deux), donc la règle du dessus tient toujours pour le
     jour — mais qui développe la nuit VOIT désormais la différence, au lieu
     de la deviner.

     Deux états voisins prennent deux FAMILLES SÉMANTIQUES distinctes. Pour
     un état « en cours », `.pill.info`.

     ET LA FORME COMPTE AUTANT QUE LA COULEUR. Une `chip` NOMME un objet, une
     `pill` ÉNONCE sa condition — c'est le titre de ce paragraphe. Une chip au
     milieu d'une table de statuts se lit comme une catégorie différente, ce
     qu'elle n'est pas. Une variante `.chip.info` a été écrite ici puis
     retirée le jour même : le seul appelant qu'on lui voyait était un statut,
     et un statut se rend en `pill`. Une classe sans appelant est une dette
     qui se paie au prochain lecteur. */
  .chip.accent {
    border-color: transparent;
    background: var(--accent);
    color: var(--on-accent);
    font-weight: 500;
  }
  /* Editorial emphasis — house rule 5: once per screen, at most. */
  .chip.editorial {
    border-color: transparent;
    background: var(--editorial-vif);
    color: var(--on-editorial);
    font-weight: 500;
  }

  .pill {
    /* 22px, pas 20 : la hauteur suit le texte. La pastille porte un mot entier
       — « Approuvé », « Brouillon » — et non un glyphe ; elle a remonté de 11
       à 11,5px avec le reste de la charte. */
    display: inline-flex;
    align-items: center;
    gap: 5px;
    height: 22px;
    padding: 0 8px;
    border-radius: 999px;
    font-size: 11.5px;
  }
  .pill .dot {
    width: 5px;
    height: 5px;
    border-radius: 999px;
    background: currentColor;
  }
  /* LA VARIANTE QUI ENVELOPPE — voir 3.7.4. Elle n'est jamais le défaut :
     `min-height` reprend les 20 px à l'identique, donc une pastille d'une
     ligne est rigoureusement inchangée.

     Le rayon se carre parce qu'une forme de stade sur deux lignes mange son
     texte aux extrémités ; la pastille de couleur se cale sur la première
     ligne parce que, sur un libellé de deux lignes, elle en marque le début
     et non le milieu. */
  .pill.wrap {
    height: auto;
    min-height: 20px;
    align-items: flex-start;
    padding: 2px 9px;
    border-radius: 5px;
    text-align: left;
  }
  .pill.wrap .dot { margin-top: 6px; }
  /* LES STATUTS SONT VIFS (décision du propriétaire, à l'écran) : pastille
     PLEINE, texte posé dessus, plus de lavis pastel sur les marques. Chaque
     paire pastille/texte est mesurée AA dans les deux thèmes — c'est ce que
     les tokens --X-vif / --on-X garantissent, et c'est la différence avec
     les initiales de famille, où le pigment vif prime sur le plancher. Les
     lavis restent définis pour les SURFACES (alertes, bandeaux, fonds). */
  .pill.ok { background: var(--ok-vif); color: var(--on-ok); }
  .pill.attention { background: var(--attention-vif); color: var(--on-attention); }
  .pill.critical { background: var(--critical-vif); color: var(--on-critical); }
  .pill.proposed { background: var(--proposed-vif); color: var(--on-proposed); }
  .pill.info { background: var(--info-vif); color: var(--on-info); }
  /* PLUS UN ALIAS LA NUIT. Voir §5 `.chip.accent` : jusqu'à la 3.4.0 cette
     ligne et `.pill.ok` produisaient le même pixel dans les deux thèmes.
     Depuis, elle porte la framboise en sombre et reste le vert du succès en
     clair. Elle demeure réservée aux appelants qui NOMMENT un objet ; elle ne
     doit toujours jamais servir à opposer un état à un autre, puisqu'en CLAIR
     elle reste indiscernable de `.pill.ok`. */
  .pill.accent { background: var(--accent); color: var(--on-accent); }
  .pill.editorial { background: var(--editorial-vif); color: var(--on-editorial); }
  .pill.neutral { background: var(--neutral-vif); color: var(--on-neutral); }

  /* The family glyph: two letters on a muted family ink. Colour never
     carries meaning alone — the letters survive greyscale and colour
     blindness, and since 2.0 the pigment is mineral: an identity
     whisper, not a poster. */
  /* La pastille de famille — LE PIGMENT PLEIN, PLAT.

     CE BLOC S'ÉTAIT MIS À MENTIR EN TROIS ENDROITS, et c'est le défaut le
     plus coûteux qu'un fichier scellé puisse avoir : il se lit comme un
     journal, chaque état s'ajoutant sous le précédent, et personne ne
     remonte corriger le titre ni les chiffres quand la décision change. Un
     lecteur qui s'arrête au premier paragraphe — c'est-à-dire presque tout
     le monde — repartait avec la règle d'il y a deux versions.

     Les trois strates, pour qu'on reconnaisse la forme si elle revient :
     l'en-tête annonçait « TRAITEMENT TONAL depuis la 3.0 » quand le dernier
     paragraphe du même bloc conclut au pigment plein ; les contrastes
     nommaient Réglementation à 4,06 et Opérations à 4,21 « sous le
     plancher », état d'avant la 3.1.1 qui les a fait passer ; et deux
     paires étaient déclarées « identiques par décision, ΔE₀₀ 0,0 », ce qui
     n'est plus vrai depuis qu'Opérations est passé à l'ambre.

     ── CE QUI A ÉTÉ ESSAYÉ ─────────────────────────────────────────────────
     Un premier état rendait la pastille en carré plein portant deux lettres
     CLAIRES, une seule encre pour les neuf. La mesure a tranché contre :
     sur la palette du conseil, quatre des neuf pigments ne portent de texte
     AA ni en clair ni en sombre.

     Un second état est passé au TONAL — lettre sombre sur lavis de la
     famille. Il lisait mieux et il a été refusé à l'écran : il rendait les
     familles PASTEL, l'inverse de ce que la gamme vive existe pour faire
     (« les initiales sont pastel au lieu de vif »).

     ── CE QUI EST ────────────────────────────────────────────────────────
     La pastille porte la teinte vive elle-même, à plat, sans relief ni
     filet, et chaque famille a SA PROPRE LETTRE (`--on-fam-*`). C'est ce
     dernier point qui rend le pigment plein tenable : `--on-family` était
     une valeur unique — ivoire le jour — et l'audit l'a démentie, sept
     familles lisant mieux en encre sombre et deux seulement en ivoire.

     LES NEUF PASSENT AA, dans les deux thèmes et sur les deux appariements.
     Recalculé sur ce fichier :

       pigment sous sa lettre — jour 4,57 (Systèmes) à 6,50 (Exigence) ;
                                nuit 7,02 à 9,97.
       encre sur lavis        — jour 4,63 (Réglementation) à 4,70 ;
                                nuit 5,30 à 7,01.

     Aucune paire sous 4,5. Le chiffre à surveiller est le 4,57 des
     Systèmes : c'est la marge la plus mince de la gamme, et toute retouche
     de l'indigo passe par là.

     LES NEUF SONT DISTINCTES, ce qui n'a pas toujours été le cas. Le brief
     du conseil offrait huit teintes pour neuf familles, et deux paires
     étaient donc littéralement le même hex. Depuis qu'Opérations a quitté
     l'ardoise pour l'ambre bronze `#96681C`, la paire la plus serrée est
     Réglementation/Systèmes à ΔE₀₀ 12,44, puis Réglementation/Glossaire à
     18,75. Rien d'identique, rien de confusable.

     ET ELLE EST VUE. Les trois tailles sont 20 / 28 / 36 avec des lettres
     de 11,5 / 11,5 / 13 en 600 (8 / 10 / 13 jusqu'à la 3.13.1) — ce contrat
     existait déjà, et le produit en rendait un tiers de moins (20px de côté,
     lettre de 8px en 500) sans que rien ne le dise. Une pastille d'identité
     qu'on ne remarque pas ne fait pas son travail : c'est la première chose
     que l'œil doit trouver dans une ligne. `npm run geometrie:check`
     compare désormais les deux.

     Les LAVIS et ENCRES de famille restent définis pour les emplois tonals
     — fonds de survol, chips discrètes, légendes — et ce sont eux que
     mesure la seconde colonne ci-dessus. Enfin, une pastille de 16 à 28px
     n'est jamais le seul porteur du sens : le nom est toujours à côté, et
     la couleur n'a jamais porté un sens à elle seule dans cette charte. */
  .fam {
    display: inline-grid;
    place-items: center;
    width: 28px;
    height: 28px;
    border-radius: var(--r-sm);
    background: var(--fam-pigment, var(--surface-3));
    color: var(--on-family);
    font-family: var(--font-mono);
    font-size: 11.5px;
    font-weight: 600;
  }
  /* 11,5px DANS 20px COMME DANS 28px — 3.13.1. Les lettres étaient à 8px
     dans le carré de 20 et à 10px dans celui de 28, « exemptées » comme
     marqueurs et non comme texte. La décision D11/Q-C du propriétaire
     (6 octobre 2026) ne connaît qu'un plancher : 0 texte rendu sous 11,5px,
     et deux lettres sont du texte qu'on lit. Elles tiennent : un code de
     type est de DEUX lettres, deux capitales en chasse fixe à 11,5px font
     14px dans un carré de 20 (la pastille de Knowledge, `Glyph`, les écrit
     ainsi depuis #257). `cible:check` n'exempte plus rien. */
  .fam.sm {
    width: 20px;
    height: 20px;
    border-radius: var(--r-xs);
    font-size: 11.5px;
  }
  .fam.lg {
    width: 36px;
    height: 36px;
    border-radius: var(--r-md);
    font-size: 13px;
  }
  /* Une classe par famille : le produit pose `.fam.data`, la charte connaît
     les trois valeurs. Aucune application n'a à savoir quel hex va où. */
  .fam.regulation { --fam-pigment: var(--fam-regulation); --fam-wash: var(--fam-regulation-wash); --fam-ink: var(--fam-regulation-ink); --on-family: var(--on-fam-regulation); }
  .fam.requirement { --fam-pigment: var(--fam-requirement); --fam-wash: var(--fam-requirement-wash); --fam-ink: var(--fam-requirement-ink); --on-family: var(--on-fam-requirement); }
  .fam.data { --fam-pigment: var(--fam-data); --fam-wash: var(--fam-data-wash); --fam-ink: var(--fam-data-ink); --on-family: var(--on-fam-data); }
  .fam.governance { --fam-pigment: var(--fam-governance); --fam-wash: var(--fam-governance-wash); --fam-ink: var(--fam-governance-ink); --on-family: var(--on-fam-governance); }
  .fam.operations { --fam-pigment: var(--fam-operations); --fam-wash: var(--fam-operations-wash); --fam-ink: var(--fam-operations-ink); --on-family: var(--on-fam-operations); }
  .fam.systems { --fam-pigment: var(--fam-systems); --fam-wash: var(--fam-systems-wash); --fam-ink: var(--fam-systems-ink); --on-family: var(--on-fam-systems); }
  .fam.assurance { --fam-pigment: var(--fam-assurance); --fam-wash: var(--fam-assurance-wash); --fam-ink: var(--fam-assurance-ink); --on-family: var(--on-fam-assurance); }
  .fam.output { --fam-pigment: var(--fam-output); --fam-wash: var(--fam-output-wash); --fam-ink: var(--fam-output-ink); --on-family: var(--on-fam-output); }
  .fam.glossary { --fam-pigment: var(--fam-glossary); --fam-wash: var(--fam-glossary-wash); --fam-ink: var(--fam-glossary-ink); --on-family: var(--on-fam-glossary); }

  /* Le POINT de catégorie : la forme que le conseil demande en priorité —
     un petit indicateur sémantique, jamais un panneau. */
  .fam-dot {
    display: inline-block;
    width: 8px;
    height: 8px;
    flex: none;
    border-radius: 999px;
    background: var(--fam-pigment, var(--ink-4));
  }
  .fam-dot.regulation { --fam-pigment: var(--fam-regulation); }
  .fam-dot.requirement { --fam-pigment: var(--fam-requirement); }
  .fam-dot.data { --fam-pigment: var(--fam-data); }
  .fam-dot.governance { --fam-pigment: var(--fam-governance); }
  .fam-dot.operations { --fam-pigment: var(--fam-operations); }
  .fam-dot.systems { --fam-pigment: var(--fam-systems); }
  .fam-dot.assurance { --fam-pigment: var(--fam-assurance); }
  .fam-dot.output { --fam-pigment: var(--fam-output); }
  .fam-dot.glossary { --fam-pigment: var(--fam-glossary); }

  /* A reference, clickable, never decorative. */
  .cite {
    display: inline-block;
    padding: 1px 4px;
    border-radius: var(--r-xs);
    background: var(--surface-3);
    color: var(--ink-2);
    font-family: var(--font-mono);
    font-size: 11.5px;
    text-decoration: none;
  }
  .cite:hover {
    background: var(--accent-wash);
    /* --accent-hi, ET NON --accent : mesure du 16 septembre 2026 — l'accent
       posé en TEXTE sur son propre lavis rend 3,60:1 en sombre, du framboise
       sur du framboise a 10 %. La variante haute lisibilite existe pour cela
       et tient dans les deux themes : 5,85:1 en jour, 4,65:1 en nuit. Aucune
       valeur de jeton ne bouge ; c'est la regle qui lisait la mauvaise. */
    color: var(--accent-hi);
  }

  /* Fact and inference, typed. What the model read (F) is not what it
     derived (I), and the reader must be able to tell without asking. */
  /* 11,5px DEPUIS LA 3.13.1 — PLUS UNE EXEMPTION. La lettre était à 8px,
     « un marqueur, pas une phrase », et la 3.13.1 l'avait d'abord laissée là
     en la croyant posée par aucun produit. Elle l'est : Knowledge rend `.fi`
     sur ses fiches d'objet (« Explain this », ExplainClauses.tsx), 4 à 7
     lettres à 8px par fiche relevée (contre-lecture du 10 octobre 2026). Or la
     décision D11/Q-C du propriétaire ne garde aucun texte sous 11,5px. Une
     lettre en chasse fixe à 11,5px avance de 7px : elle tient dans la boîte
     de 12, sur la même hauteur de ligne de 14px. */
  .fi {
    display: inline-block;
    width: 12px;
    margin-right: 6px;
    border-radius: var(--r-xs);
    text-align: center;
    font-family: var(--font-mono);
    font-size: 11.5px;
    font-weight: 600;
    line-height: 14px;
    vertical-align: 1px;
  }
  .fi.f {
    background: var(--ok-vif);
    color: var(--on-ok);
  }
  .fi.i {
    background: var(--surface-3);
    color: var(--ink-2);
  }

  /* A count of things a machine proposed — the only number on a filled
     proposal pill: rose until charte 3.14.2, blue since (D42). Since 3.14.3
     (D61) the menu's count pill (.nav-item .count) wears this same pair of
     tokens and this same shape: the monospace digit and the entry's title
     tell the two apart, never the hue. */
  .badge-count {
    padding: 1px 6px;
    border-radius: 999px;
    background: var(--proposed-vif);
    color: var(--on-proposed);
    font-family: var(--font-mono);
    font-size: 11.5px;
  }

  /* ======================================================================
     6 · DATA DISPLAY
     Enterprise density is the point: whitespace and type hierarchy do
     the separating, hairlines only where rows genuinely need them.
     Semantic colour appears only when it communicates status.
     ====================================================================== */

  /* The workhorse row: glyph, name, type and reference, an optional
     predicate, a state. Titles truncate; the row never grows a second line.
     3.13.1 : PLUS MAINTENANT. Un nom n'est pas un jeton technique, et la
     décision Q-B du propriétaire ne laisse l'ellipse qu'aux jetons : le nom
     et l'aide passent à la ligne, la rangée grandit — § « 3.13.1 », 3. */
  .object-row {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 8px 12px;
    border-bottom: 1px solid var(--line);
    text-decoration: none;
    transition: background-color var(--t-fast) var(--ease);
  }
  .object-row:last-child {
    border-bottom: 0;
  }
  .object-row:hover {
    background: var(--surface-2);
  }
  .object-row .body {
    min-width: 0;
    flex: 1;
  }
  .object-row .name {
    display: block;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 12.5px;
    font-weight: 500;
    color: var(--ink);
  }
  .object-row .sub {
    display: block;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 11.5px;
    color: var(--ink-3);
  }
  .object-row .predicate {
    flex-shrink: 0;
    font-family: var(--font-mono);
    font-size: 11.5px;
    text-transform: lowercase;
    color: var(--ink-3);
  }

  /* A headline figure. The label names it, the number carries it, the note
     says what it means — a number with no note is a number nobody acts on. */
  .stat {
    padding: 14px 16px;
    background: var(--surface);
  }
  .stat .value {
    margin-top: 4px;
    font-family: var(--font-display);
    font-size: 26px;
    font-weight: 300;
    line-height: 1;
    letter-spacing: -0.02em;
    font-variant-numeric: tabular-nums;
    color: var(--ink);
  }
  .stat .value.attention { color: var(--attention); }
  .stat .value.critical { color: var(--critical); }
  .stat .note {
    margin-top: 6px;
    font-size: 11.5px;
    color: var(--ink-3);
  }

  /* A proportion, drawn. Blunt on purpose: it drives a bar, not a decision. */
  .gauge {
    display: flex;
    align-items: center;
    gap: 8px;
  }
  .gauge .track {
    width: 64px;
    height: 3px;
    overflow: hidden;
    border-radius: 999px;
    background: var(--surface-3);
  }
  .gauge .fill {
    display: block;
    height: 100%;
    border-radius: 999px;
    background: var(--ok);
  }
  .gauge .fill.attention { background: var(--attention); }
  .gauge .fill.critical { background: var(--critical); }
  .gauge .value {
    font-size: 11.5px;
    font-variant-numeric: tabular-nums;
    color: var(--ink-2);
  }

  /* Confidence as a five-step meter. A rank people can argue about beats a
     badge they cannot, so it is drawn rather than named. */
  .confidence {
    display: inline-flex;
    align-items: flex-end;
    gap: 1.5px;
  }
  /* 3.9.0 — « DEUX BARRES » AU LIEU DE « DEUX SUR CINQ ». La barre éteinte
     d'une jauge n'est pas un filet : elle porte le DÉNOMINATEUR, et sans elle
     on lit une quantité sans son échelle. À `--line-strong` elle rendait
     1,44:1 — invisible le jour ; la 3.14.1 le porte à 1,71:1, la 3.14.2 à
     2,31:1, toujours sous le plancher des éléments non textuels (1.4.11,
     3:1) — d'où un jeton dédié, tenu à ce plancher. */
  .confidence i {
    width: 3px;
    border-radius: var(--r-xs);
    background: var(--meter-off);
  }
  .confidence i.on { background: var(--accent); }
  .confidence i:nth-child(1) { height: 6px; }
  .confidence i:nth-child(2) { height: 7px; }
  .confidence i:nth-child(3) { height: 9px; }
  .confidence i:nth-child(4) { height: 10px; }
  .confidence i:nth-child(5) { height: 12px; }

  /* Data tables: hairlines, no zebra, tabular figures. */
  .table {
    width: 100%;
    border-collapse: collapse;
    font-size: 12.5px;
  }
  .table th {
    padding: 0 12px 8px;
    border-bottom: 1px solid var(--line);
    text-align: left;
    font-size: 11.5px;
    font-weight: 500;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--ink-3);
  }
  /* 3.9.0 — UNE RANGÉE HAUTE ALIGNAIT SON LIEN SUR RIEN. Par défaut une
     cellule centre son contenu verticalement : dans une rangée qui porte un
     formulaire replié, une pile de motifs ou un bloc de raisons, le lien de la
     première colonne flottait au milieu, à cent pixels de l'en-tête qui le
     nomme. Trust posait `verticalAlign: top` à la main sur vingt cellules —
     une par une, et seulement là où quelqu'un l'avait remarqué. Les rangées
     d'une seule ligne ne bougent pas d'un pixel : tout y est déjà en haut. */
  .table td,
  .table tbody th {
    padding: 8px 12px;
    border-bottom: 1px solid var(--line);
    vertical-align: top;
  }
  /* 3.9.0 — UN LIEN DANS LA PROSE SE VOIT SANS LA COULEUR (1.4.1). La charte
     posait `a { color: inherit; text-decoration: none }` : dans un paragraphe,
     rien ne distinguait un lien du texte autour — ni la couleur, ni le trait.
     Trust compensait par soixante `fontWeight: 500` et vingt-cinq
     `color: var(--ink)` écrits en ligne, Evidence laissait ses liens de prose
     indiscernables de ses nombres.
     PORTÉE : la page, jamais le chrome, et seulement les `<a>` SANS CLASSE —
     `.btn`, `.tab`, `.nav-item`, `.chip`, `.cite`, `.breadcrumb a` gardent
     leur dessin. Le trait est un filet (--line-strong) qui devient l'encre au
     survol : présent au repos, affirmé quand on le vise. */
  .page a:not([class]) {
    text-decoration: underline;
    text-decoration-color: var(--line-strong);
    text-decoration-thickness: 1px;
    text-underline-offset: 0.18em;
  }
  .page a:not([class]):hover,
  .page a:not([class]):focus-visible { text-decoration-color: currentColor; }

  .table .num {
    font-variant-numeric: tabular-nums;
  }

  /* Tabs. The active one is marked by weight and by a rail beneath it. */
  .tabs {
    display: flex;
    gap: 4px;
    overflow-x: auto;
    border-bottom: 1px solid var(--line);
  }
  .tab {
    position: relative;
    flex-shrink: 0;
    padding: 8px 12px;
    font-size: 12.5px;
    color: var(--ink-3);
    text-decoration: none;
    transition: color var(--t-fast) var(--ease);
  }
  .tab:hover { color: var(--ink); }
  .tab[aria-current="page"] {
    font-weight: 500;
    color: var(--ink);
  }
  .tab[aria-current="page"]::after {
    content: "";
    position: absolute;
    inset-inline: 8px;
    bottom: -1px;
    height: 2px;
    background: var(--accent);
  }
  .tab .count {
    margin-left: 6px;
    padding: 1px 6px;
    border-radius: 999px;
    background: var(--surface-3);
    font-family: var(--font-mono);
    font-size: 11.5px;
    color: var(--ink-2);
  }

  /* A trail that describes where a thing sits, not how you got here — two
     people arriving by different routes must read the same crumbs. */
  .breadcrumb {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
    font-size: 11.5px;
    color: var(--ink-3);
  }
  .breadcrumb a {
    color: inherit;
    text-decoration: none;
  }
  .breadcrumb a:hover { color: var(--ink); }
  /* --ink-3, ET NON --ink-4 : la quatrieme encre est DECORATIVE par decision
     de la charte, et `encres:check` le tient — 1,60:1 en jour. Un separateur
     de fil d'Ariane a ce contraste n'est pas discret, il est absent, et les
     miettes se lisent alors comme une phrase continue. --ink-3 rend 4,95:1 en
     jour et 5,61:1 en nuit, tout en restant plus clair que le texte des
     miettes : discret veut dire lisible et second, pas invisible. */
  .breadcrumb .sep { color: var(--ink-3); }

  /* ======================================================================
     7 · SURFACES
     The four levels, drawn. A .panel is ELEVATED: a card that matters.
     Structural grounds take --surface; floating layers take .glass and
     §8's geometry. Depth is tonal difference, a hairline, a soft shadow
     — never a wall.
     ====================================================================== */

  /* `overflow: hidden` EST VOULU, ET IL A UN COÛT QU'IL FAUT CONNAÎTRE.
     Il est là pour que les enfants respectent le rayon de la carte : sans
     lui, un tableau à fond plein déborde des quatre coins arrondis et la
     carte n'en est plus une.

     Le prix : un contenu PLUS LARGE que la carte n'est pas mis en défilement,
     il est COUPÉ — et ce qui dépasse devient inatteignable, ce qui est pire
     qu'un débordement visible. On l'a payé dans Console, où dix tableaux
     perdaient leurs dernières colonnes à 375px sans que rien ne l'indique :
     mesurer `scrollWidth` ne le voit pas non plus, puisque le conteneur ne
     défile pas.

     LA RÈGLE QUI VA AVEC : tout contenu qui peut être plus large que sa
     carte — un tableau, un diagramme, un bloc de code — se met dans un
     `.overflow-x` À L'INTÉRIEUR du panneau. Le panneau garde ses coins, et
     c'est l'enfant qui défile. */
  .panel {
    border: 1px solid var(--line);
    border-radius: var(--r-card);
    background: var(--elevated);
    overflow: hidden;
  }
  .panel.raised {
    box-shadow: var(--shadow-md);
  }

  /* Something the system noticed. The left rail is the whole design: accent
     when it reports a fact, --proposed when it reports a machine proposal.
     By day the two are the same blue since 3.14.2 (D42, the owner's
     decision): the label says which, the hue no longer does. */
  .insight {
    padding: 14px 16px;
    border-left: 2px solid var(--accent);
    background: var(--surface);
  }
  .insight.proposed { border-left-color: var(--proposed); }
  .insight .title {
    font-size: 13px;
    font-weight: 500;
    color: var(--ink);
  }
  .insight .body {
    margin-top: 6px;
    font-size: 12.5px;
    line-height: 1.6;
    color: var(--ink-2);
  }

  /* The proposal border — rose until 3.14.2, blue since (D42) — is
     load-bearing: it says a machine proposed this and
     nobody has ruled on it. Once a person signs, it is no longer a proposal
     and the banner speaks critical, like any other established defect. */
  .banner {
    width: 100%;
    border: 1px solid var(--line);
    border-radius: var(--r-card);
    background: var(--elevated);
    overflow: hidden;
  }
  .banner .bar {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 14px;
    border-bottom: 1px solid var(--line);
    background: var(--surface-2);
  }
  .banner .bar .dot {
    width: 6px;
    height: 6px;
    border-radius: 999px;
    background: var(--accent);
  }
  .banner.proposed { border-color: var(--proposed); }
  .banner.proposed .bar {
    background-color: var(--proposed-wash);
    background-image: linear-gradient(180deg, var(--proposed-wash-hi), var(--proposed-wash));
  }
  .banner.proposed .bar .dot { background: var(--proposed); }
  .banner.critical { border-color: var(--critical); }
  .banner.critical .bar {
    background-color: var(--critical-wash);
    background-image: linear-gradient(180deg, var(--critical-wash-hi), var(--critical-wash));
  }
  .banner.critical .bar .dot { background: var(--critical); }
  .banner .body {
    padding: 12px 14px;
    font-size: 13px;
    color: var(--ink-2);
  }
  .banner .fine {
    padding: 0 14px 12px;
    font-size: 11.5px;
    color: var(--ink-3);
  }

  /* Errors say what went wrong and what to do — no apology, no vagueness.
     Absence is not an error: what is merely not recorded yet takes
     .attention, which is the rule the whole platform follows. */
  .alert {
    padding: 8px 12px;
    border-left: 2px solid var(--critical);
    border-radius: var(--r-sm);
    background-color: var(--critical-wash);
    background-image: linear-gradient(180deg, var(--critical-wash-hi), var(--critical-wash));
    color: var(--critical);
    font-size: 12px;
    line-height: 1.55;
  }
  .alert.attention {
    border-left-color: var(--attention);
    background-color: var(--attention-wash);
    background-image: linear-gradient(180deg, var(--attention-wash-hi), var(--attention-wash));
    color: var(--attention);
  }

  /* An empty state names what is missing and how to fill it. */
  .empty {
    padding: 18px;
    border: 1px dashed var(--line-strong);
    border-radius: var(--r-card);
  }
  .empty .t { font-size: 13px; font-weight: 600; }
  .empty .b {
    margin-top: 4px;
    font-size: 12.5px;
    line-height: 1.6;
    color: var(--ink-3);
  }

  /* LES TROIS FILETS SONT REVENUS À LEUR VALEUR LÉGÈRE, sur décision du
     propriétaire prise à l'écran — « le contour est trop foncé, c'était
     mieux dans la version d'avant ». La 3.1.5 les avait montés au plancher
     de 3:1 et la 3.1.6 y avait ajouté le filet de verre ; la 3.1.7 rend les
     six valeurs, jour et nuit.

     LE PRIX EST MESURÉ ET CONSIGNÉ, parce qu'il ne disparaît pas d'être
     accepté : --line mesure 1,20:1 contre le papier, --line-strong 1,40:1,
     --glass-line 1,26:1. WCAG 1.4.11 demande 3:1 pour une information
     visuelle NÉCESSAIRE à identifier un composant. La question de savoir si
     un contour de carte en fait partie se discute — le contenu de la carte
     l'identifie aussi — mais deux cas sont moins discutables et méritent
     d'être nommés :

       · la barre latérale n'a pas de fond propre (son --glass s'aplatit à
         1,02:1 le jour, 1,00:1 la nuit), donc son filet est la SEULE chose
         qui la sépare du contenu ;
       · en thème sombre, --line tombe à 0,14 d'alpha, la plus faible des
         six valeurs.

     Ce que la charte dit d'elle-même reste vrai et explique la décision :
     « Depth is tonal difference, a hairline, a soft shadow — never a wall ».
     À 3:1, le contour cessait d'être un filet. Si la lisibilité de la
     frontière doit revenir, la voie qui ne rouvre pas ce débat est de
     donner aux surfaces un vrai écart TONAL plutôt que d'épaissir le trait
     — c'est ce que ce paragraphe recommande depuis le début. */

  /* Controlled liquid glass — the floating level and the chrome: topbar,
     sidebar, drawers, menus, palettes, popovers, modals. Give the
     surface something to blur — overlay it on the scrolling content. */
  .glass {
    background: var(--glass);
    border-color: var(--glass-line);
    -webkit-backdrop-filter: blur(var(--blur-glass)) saturate(1.4);
    backdrop-filter: blur(var(--blur-glass)) saturate(1.4);
  }
  @supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
    .glass { background: var(--surface-solid); }
  }

  /* ======================================================================
     8 · COMMAND PALETTE
     One flat list, one selection. Enter acts, Esc leaves, arrows move.
     ====================================================================== */

  /* The dialog itself: glass, floating a little above centre, never full
     screen. It is a layer over what you were doing, not a page you went to. */
  .palette-scrim {
    position: fixed;
    inset: 0;
    z-index: 50;
    border: 0;
    padding: 0;
    background: var(--voile);
    -webkit-backdrop-filter: blur(4px);
    backdrop-filter: blur(4px);
  }
  /* UNE FENÊTRE N'EST PAS DU CHROME, ET NE SE REND PLUS EN VERRE.
     `.glass` sert deux choses que rien ne distinguait : le CHROME — barre du
     haut, barre latérale, tiroir — qui borde le contenu et gagne à le laisser
     transparaître ; et les FENÊTRES — palette, menu de produits, tout ce qui
     se pose PAR-DESSUS ce qu'on était en train de lire.

     Pour une fenêtre, la translucidité coûte ce qu'elle donne. Mesuré : en
     thème sombre `--glass` vaut `rgb(20 16 20 / 0.62)`, donc 38 % du texte
     d'en dessous traverse le fond de la boîte, flou mais présent ; en clair
     c'est 42 %. Le propriétaire l'a dit plus court : « les pop-up et autres
     fenêtres sont transparentes, on ne voit rien ».

     Les fenêtres prennent donc `--surface-solid` — le jeton qui existait déjà
     pour ce cas, employé jusqu'ici par le seul repli `@supports` quand le
     navigateur ne sait pas flouter. Et elles perdent le `backdrop-filter` :
     derrière un fond opaque il ne rend rien et coûte une couche de
     composition à chaque défilement.

     Le chrome, lui, garde son verre — c'est là que l'effet dit quelque chose
     de vrai : la barre du haut EST au-dessus du contenu qui passe dessous.

     `.fenetre` est là pour les fenêtres qu'une application dessine
     elle-même, avec ses propres utilitaires de position : elle s'ajoute à
     `.glass`, qui garde le filet, et n'apporte que l'opacité. C'est le cas
     de la palette de Knowledge, qui ne peut pas prendre `.palette` — celle-ci
     porte un positionnement fixe qui entrerait en conflit avec le sien. */
  .palette,
  .product-menu,
  .drawer,
  .fenetre,
  /* ET LE DIALOGUE QU'UNE APPLICATION DESSINE AVEC `.panel`. Cette classe
     porte `--elevated`, 72 % de blanc : posée sur le papier c'est une carte,
     posée sur un tableau dense c'est une vitre — on lit les lignes de dessous
     À TRAVERS le dialogue, ce qui a été constaté à l'écran dans le journal
     d'audit de Collecte.

     Les quatre sélecteurs au-dessus nomment des fenêtres. Celui-ci nomme un
     CONTRAT — `aria-modal`, que toute fenêtre modale déclare déjà parce que
     les lecteurs d'écran en dépendent. Une application n'a donc rien de neuf à
     se rappeler : ce qui est modal est opaque.

     ATTACHÉ AU PANNEAU, PAS À SES DESCENDANTS. `[aria-modal] .panel` aurait
     aussi opacifié les cartes POSÉES DANS le dialogue, qui doivent rester
     distinctes de son fond. Le panneau qui EST le dialogue, et lui seul. */
  .panel[aria-modal="true"] {
    background: var(--surface-solid);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }

  .palette {
    position: fixed;
    left: 50%;
    top: 12vh;
    z-index: 51;
    width: min(640px, 92vw);
    transform: translateX(-50%);
    overflow: hidden;
    border: 1px solid var(--glass-edge);
    border-radius: var(--r-float);
    box-shadow: var(--shadow-lg);
  }
  .palette-head {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 14px 16px;
    border-bottom: 1px solid var(--line);
  }
  .palette-head input {
    flex: 1;
    min-width: 0;
    border: 0;
    background: transparent;
    color: var(--ink);
    font: inherit;
    font-size: 15px;
    outline: none;
  }
  .palette-head input::placeholder { color: var(--ink-3); }
  /* A written answer, when the query supports one. Never invented: it is
     what the app's own search returned, shown before the list of hits. */
  .palette-answer {
    padding: 10px 16px;
    border-bottom: 1px solid var(--line);
    background: var(--surface-2);
    font-size: 12.5px;
    line-height: 1.6;
    color: var(--ink-2);
  }
  .palette-list {
    max-height: 52vh;
    overflow-y: auto;
    padding: 6px 0;
  }
  .palette-list > .register { display: block; padding: 8px 16px 4px; }
  .palette-foot {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 8px 16px;
    border-top: 1px solid var(--line);
    font-size: 11.5px;
    color: var(--ink-3);
  }

  .palette-row {
    display: flex;
    align-items: baseline;
    gap: 10px;
    /* No width: 100% here — with the inline margin it made every row 12px
       wider than the list, and the selected row's ground ran under the
       dialog's right edge. A block fills what is left of its container. */
    margin-inline: 6px;
    padding: 7px 10px;
    border-radius: var(--r-sm);
    font-size: 12.5px;
    cursor: pointer;
  }
  .palette-row.active { background: var(--surface-2); }
  .palette-row .name { min-width: 0; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--ink); }
  .palette-row .hint { flex-shrink: 0; font-size: 11.5px; color: var(--ink-3); }
  .palette-row .ref {
    font-family: var(--font-mono);
    font-size: 11.5px;
    color: var(--ink-3);
  }
  .palette-row .type {
    margin-left: auto;
    font-size: 11.5px;
    color: var(--ink-3);
  }

  /* ======================================================================
     9 · QUOTED SOURCE
     The single sanctioned use of the serif: text quoted verbatim from a
     source document, with its citation attached. Never headings, never UI.
     ====================================================================== */

  .verbatim {
    padding-left: 14px;
    max-width: 66ch;
    border-left: 2px solid var(--fam-regulation);
  }
  .verbatim p {
    margin: 0;
    font-family: var(--font-serif);
    font-size: 14.5px;
    line-height: 1.7;
  }
  .verbatim cite {
    display: block;
    margin-top: 8px;
    font-family: var(--font-mono);
    font-size: 11.5px;
    font-style: normal;
    color: var(--ink-3);
  }

  /* ======================================================================
     10 · UTILITIES
     ====================================================================== */

  .scroll {
    overflow-y: auto;
    scrollbar-width: thin;
    scrollbar-color: var(--line-strong) transparent;
  }
  .scroll::-webkit-scrollbar { width: 9px; height: 9px; }
  .scroll::-webkit-scrollbar-thumb {
    background: var(--line-strong);
    border: 3px solid transparent;
    background-clip: content-box;
    border-radius: 9px;
  }
  .scroll::-webkit-scrollbar-track { background: transparent; }

  /* Wide content — tables, diagrams, code — scrolls inside its own box so
     the page body never scrolls sideways. */
  .overflow-x {
    overflow-x: auto;
  }

  /* Shown only below the breakpoint. It lives HERE, at the end, and not
     beside the chrome it serves: a display utility must be declared after
     every component that also sets display, or the component wins on equal
     specificity. That is exactly how the mobile-only hamburger stayed
     visible on desktop — .icon-btn { display: grid } came later and won. */
  @media (min-width: 768px) {
    .mobile-only { display: none; }
  }
  /* ════════════════════════════════════════════════════════════════════════
     § SEUIL — les portes de la plateforme
     ════════════════════════════════════════════════════════════════════════

     CINQ ÉCRANS, UNE COMPOSITION. L'accueil de la plateforme et les quatre
     écrans de connexion sont la première chose qu'on voit de YNTGYST. Ils
     partagent la même, et `seuil:check` refuse qu'une porte s'en écarte.

     LA TABLE. Direction retenue par le propriétaire parmi trois états rendus à
     la taille réelle de l'écran : un index, deux colonnes titrées, un rythme
     tabulaire. C'est la forme d'un INSTRUMENT DE RELEVÉ, et c'est ce que
     YNTGYST fabrique — des pièces, des registres, des chaînes de garde. Une
     page d'accueil qui a la forme du produit vaut mieux qu'une page d'accueil
     qui a la forme d'un site.

     TROIS COMPOSITIONS ONT ÉTÉ REFUSÉES AVANT CELLE-CI, et la raison était la
     même chaque fois : je les validais sur des maquettes rendues à 800 pixels
     de large, où tout paraît juste, alors qu'elles ne tenaient pas sur un
     écran réel. Le logotype fait ici 1296 × 158 sur un 1440 — il touche les
     deux marges et redevient l'architecture de la page, contre 47 pixels de
     haut quand il vivait dans une colonne de 430.

     AUCUNE COULEUR N'IDENTIFIE UNE APPLICATION. Les pastilles ont disparu sur
     décision explicite. Ce qui les distingue est leur nom et leur rang.

     RIEN NE FLOTTE. Pas de carte, pas d'ombre, pas de conteneur autour d'un
     groupe, pas de fond différent. Ce qui sépare est un filet. */
  .seuil {
    /* PLEIN PIED : la page tient dans la fenêtre, elle ne défile pas. `height`
       et non `min-height` — avec `min-height`, un contenu un peu trop grand
       déborde en silence et rend la barre de défilement. */
    height: 100dvh;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    padding: clamp(24px, 3.4vh, 48px) 5vw;
    background: var(--paper);
  }

  /* Le logotype touche les deux marges. Le dessin est de rapport 8,225:1 :
     sa largeur fixe sa hauteur, et c'est pourquoi il faut la lui donner. */
  /* LE LOGOTYPE RESPIRE. Il prenait toute la largeur — 1296px sur un écran de
     1440, 158 de haut — et le propriétaire l'a jugé trop gros : « ce n'est pas
     fluide et minimaliste ». Il occupe désormais une FRACTION de la largeur,
     qui reste fluide : 58vw, bornés pour ne pas devenir un timbre sur un
     grand écran ni un filet sur un petit.

     `min(100%, …)` garde le pied sur terre en dessous de 550px environ : là,
     58vw serait plus étroit que le plancher, et le plancher plus large que
     l'écran — c'est la marge de la page qui gagne, jamais le débordement. */
  .seuil-marque {
    display: block;
    width: min(100%, clamp(300px, 58vw, 980px));
    color: var(--ink);
  }
  .seuil-marque svg { display: block; width: 100%; height: auto; }

  /* La ligne qui situe : la devise à gauche, le lieu — ou la langue — à
     droite. Deux repères en capitales espacées, jamais une phrase. */
  .seuil-sous {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 20px;
    margin-top: clamp(10px, 1.6vh, 16px);
    /* L'AIR ENTRE LE TITRE ET LA TABLE. Il en manquait : le filet arrivait
       presque sous la devise, et la table commençait dans la foulée. Le vide
       est ici la seule chose qui sépare l'identité de son contenu. */
    padding-bottom: clamp(28px, 6vh, 88px);
    font-size: 11.5px;
    font-weight: 500;
    letter-spacing: 0.3em;
    text-transform: uppercase;
    color: var(--ink-3);
  }
  .seuil-filet { border: 0; border-top: 1px solid var(--line); margin: 0; }

  .seuil-corps {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
  }

  /* ── LA COMPOSITION CENTRÉE DU FORMULAIRE ──────────────────────────────
     Décision du propriétaire, 3.4.2 : « the login form MUST be centered
     horizontally », largeur ~440px, « slightly above the exact vertical
     center », sans carte, sans panneau, sans ombre — seules les bordures
     des champs.
  
     LE CIBLAGE PASSAIT PAR `:has(.seuil-form)`, ET C'ÉTAIT UNE ERREUR — 3.6.0
     la répare. Le § seuil sert bien DEUX compositions : les portes d'entrée, et
     l'accueil de la plateforme qui pose une TABLE de produits en pleine
     largeur. Centrer `.seuil-corps` sans condition aurait rétréci cette table,
     il fallait donc distinguer. Mais distinguer PAR LA PRÉSENCE D'UN
     FORMULAIRE, c'est faire dépendre la composition d'une page de ce qu'elle
     contient à l'exécution.

     Le prix s'est payé chez Evidence. Sa porte ne pose pas de champs : elle
     offre un bouton d'entreprise et une liste d'identités, et son seul
     `.seuil-form` est à l'intérieur d'un `{sso && …}`. SSO non configuré, plus
     de formulaire ; plus de formulaire, plus de porte — la page retombait sur
     la composition de la TABLE, avec le logotype pleine largeur, la devise à
     gauche et le nom du module à droite, c'est-à-dire exactement les deux
     choses que le propriétaire avait fait retirer en 3.4.5. Trois portes dans
     une maison, la quatrième dans une autre, et la maison changeait selon
     qu'une variable d'environnement était posée ou non.

     `seuil:check` ne pouvait rien y voir : elle lit le balisage, et le
     balisage était correct — les quatre parts, dans l'ordre. Ce qui manquait
     n'était pas dans le fichier, il était dans l'exécution.

     D'OÙ `.seuil.porte`. Une porte DÉCLARE qu'elle en est une, au lieu de le
     laisser deviner. C'est une classe de plus dans quatre fichiers, et c'est
     le prix juste : une composition est une intention, et une intention
     s'écrit. La barrière peut désormais l'exiger, ce qu'elle fait.
  
     L'EN-TÊTE NE BOUGE PAS. Le brief le redécrit à l'identique — logotype,
     devise, nom du module à droite, filet — et demande de ne pas toucher à
     l'identité. Le logotype reste donc à sa marge : c'est lui qui tient le
     cadre, et c'est le formulaire qui devient le foyer. */
  /* ══════════════════════════════════════════════════════════════════════
     3.7.0 — LA CARTE. Un revirement, et il faut le dire comme tel.

     Le § seuil affirmait « RIEN NE FLOTTE : pas de carte, pas d'ombre, pas de
     conteneur autour d'un groupe, pas de fond différent — ce qui sépare est un
     filet », et la 3.4.2 le redisait dans les mots du propriétaire : « no outer
     card, no surrounding panel, no shadow ». Le propriétaire a montré une
     référence et demandé l'inverse, pour les quatre portes. La règle tombe donc
     ICI ET SEULEMENT ICI : sur les portes. L'accueil de la plateforme garde sa
     table à plat, et le reste du produit reste sans cartes — c'est toujours ce
     qui distingue un instrument de relevé d'un site.

     TRADUIT, PAS COPIÉ. La référence pose une carte claire sur une photographie
     de ciel, avec une pastille d'icône, des champs pleins à icône, un bouton
     sombre et trois boutons sociaux. Ce qui traverse dans la langue YNTGYST :
     la CARTE, les CHAMPS PLEINS, le bouton pleine largeur. Ce qui ne traverse
     pas, et pourquoi :

       · LE DÉCOR PHOTOGRAPHIQUE. `seuil:check` refuse une image de décor sur
         une porte, et la refuse depuis que les quatre portes en avaient une
         chacune. Le fond reste `--paper-lit`, le dégradé à peine teinté
         d'accent que la charte porte déjà.
       · LES ICÔNES DANS LES CHAMPS. Une enveloppe devant « Email » redit
         l'étiquette en dessin. La charte n'a jamais doublé un mot par une
         image, et ce n'est pas ici qu'elle commencera.
       · LES BOUTONS SOCIAUX. Google, Facebook et Apple ne signent pas l'entrée
         d'une plateforme de conformité d'entreprise. Le « ou » reste, et ce
         qu'il introduit est l'identité d'ENTREPRISE — OIDC — là où elle
         existe.
       · LA PASTILLE D'ICÔNE EN HAUT DE CARTE. Le logotype est déjà au-dessus.
         Une marque dite deux fois à quarante pixels d'écart n'ajoute rien.

     LA CARTE EST LE `.seuil-corps` LUI-MÊME, et non un conteneur de plus : il
     enveloppait déjà exactement le bon contenu, et l'envelopper une seconde
     fois aurait demandé de toucher aux quatre portes pour rien.
     ══════════════════════════════════════════════════════════════════════ */
  .seuil.porte .seuil-corps {
    /* largeur:ok — 520 et non 480 : la carte porte désormais la table des
       comptes de démonstration, deux colonnes dont la seconde est un rôle qui
       peut être long (« Contributor · Energy »). À 480 elles se serraient. */
    width: min(100%, 520px);
    padding: clamp(28px, 3.4vw, 44px);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    background: var(--surface);
    box-shadow: var(--shadow-md);
    /* `hidden` SEUL, et plus `hidden auto` : la carte ne défile pas — elle
       grandit, et la page défile pour elle. Le mot-clé reste néanmoins, parce
       que c'est lui qui découpe les fonds d'enfants au rayon du cadre : sans
       lui, le premier bloc coloré posé en tête de carte laisserait dépasser
       ses deux coins carrés, exactement comme l'encart de conflit le faisait
       sur la page d'objet. */
    overflow: hidden;
  }
  /* Les enfants remplissent la carte : leur largeur propre était la mesure du
     PAGE, elle devient celle de la carte. Les trois largeurs qui suivent —
     440 pour les enfants, 620 pour la table, 460 pour le formulaire — n'ont
     plus de raison d'être : la carte les borne toutes, et les laisser aurait
     produit des colonnes de largeurs différentes à l'intérieur d'un même
     cadre. */
  .seuil.porte .seuil-corps > *,
  .seuil.porte .seuil-corps > .seuil-demo,
  .seuil.porte .seuil-corps:not(:has(.seuil-form)) > .seuil-titre,
  .seuil.porte .seuil-corps:not(:has(.seuil-form)) > .seuil-chapo,
  .seuil.porte .seuil-corps:not(:has(.seuil-form)) > .seuil-trait,
  .seuil.porte .seuil-form { width: 100%; }

  /* Le titre se centre DANS la carte, et la barre d'accent avec lui : une
     carte est un objet symétrique, et un titre calé à gauche dans un cadre
     centré lit comme un décalage plutôt que comme un alignement. */
  .seuil.porte .seuil-corps > .seuil-titre,
  .seuil.porte .seuil-corps > .seuil-chapo { text-align: center; }
  .seuil.porte .seuil-corps > .seuil-trait {
    background: linear-gradient(var(--accent), var(--accent)) center / 36px 100% no-repeat;
  }
  .seuil.porte .seuil-corps > .seuil-titre { margin-top: 0; }

  /* LES CHAMPS SE REMPLISSENT. Sur le papier, un champ bordé et transparent
     se lit très bien ; posé sur une carte qui a elle-même une bordure, il
     produit deux traits parallèles à trois pixels l'un de l'autre. Le
     remplissage règle la lecture sans ajouter de couleur : `--surface-2` est
     la matière que la charte pose déjà sur `--surface`. */
  .seuil.porte .seuil-form input,
  .seuil.porte .seuil-form select {
    border-color: transparent;
    background-color: var(--surface-2);
  }
  .seuil.porte .seuil-form input:focus,
  .seuil.porte .seuil-form select:focus { border-color: var(--ink-3); }

  /* L'ancien réglage de répartition disparaît AVEC sa raison d'être : il
     centrait le formulaire dans un corps qui prenait toute la hauteur libre.
     La carte se dimensionne à son contenu, et c'est `.seuil.porte` qui centre
     le groupe entier — un centrage de plus ne ferait que décentrer. Ce qui
     reste est l'alignement des enfants dans la carte. */
  .seuil.porte .seuil-corps { align-items: center; }
  .seuil.porte .seuil-corps > * {
    /* largeur:ok — CE N'EST PAS DE LA PROSE, c'est la MESURE de la
       composition centrée : titre, chapô, trait, champs et lien secondaire
       partagent une seule colonne pour former un bloc, et un bloc dont
       chaque ligne aurait sa propre largeur ne serait pas un bloc.
       440px est la valeur demandée. */
    width: min(100%, 440px);
    flex-shrink: 0;
  }
  /* QUAND LA LISTE EST LA PORTE, LE TITRE S'ALIGNE SUR ELLE. Les enfants du
     corps font 440, la table des comptes 620 : trois portes sur quatre s'en
     accommodent, parce que leur contenu principal EST le formulaire de 440 et
     que la table n'arrive qu'en dessous, secondaire. Chez Evidence il n'y a pas
     de formulaire — on entre en choisissant une identité dans la liste — et le
     titre se retrouvait rentré de quatre-vingt-dix pixels par rapport à la
     seule chose qu'il annonce.

     La condition porte sur la présence du formulaire et non sur le produit :
     c'est bien la nature du contenu principal qui décide de la verticale, et
     une porte qui gagnerait un formulaire demain reprendrait le 440 sans qu'on
     y touche. Elle ne décide plus de la COMPOSITION — cela, c'est `.porte` qui
     le fait maintenant, et rien d'autre. */
  .seuil.porte .seuil-corps:not(:has(.seuil-form)) > .seuil-titre,
  .seuil.porte .seuil-corps:not(:has(.seuil-form)) > .seuil-chapo,
  .seuil.porte .seuil-corps:not(:has(.seuil-form)) > .seuil-trait {
    width: min(100%, 620px);
  }
  .seuil.porte .seuil-corps > .seuil-demo {
    /* largeur:ok — la table des comptes porte DEUX colonnes, un courriel et
       un rôle, qui se toucheraient à 440. Elle reste sur la même verticale
       et respire un peu plus large. Sa place au-dessous et son poids —
       capitales de 10px, encre tertiaire — la gardent secondaire, ce que
       le brief demande explicitement. */
    width: min(100%, 620px);
  }
  /* ── LE BANDEAU DES PORTES À FORMULAIRE ────────────────────────────────
     Décision du propriétaire : « diminue le logo et centre-le », « mets des
     espaces ». Même condition que la composition centrée ci-dessous : cela
     ne vaut QUE pour les portes à formulaire. L'accueil de la plateforme
     garde son logotype large et calé à gauche, au-dessus de sa table.
  
     Le logotype passe de 58vw (bornés 300-980) à 30vw (bornés 200-420) : il
     établit la marque sans devenir une enseigne au-dessus d'un formulaire de
     440. La devise se centre avec lui ; le nom du module reste ÉPINGLÉ À
     DROITE, comme le brief le demande — d'où le positionnement absolu,
     seule façon de centrer l'une sans déplacer l'autre. */
  .seuil.porte .seuil-marque {
    width: min(100%, clamp(200px, 30vw, 420px));
    margin-inline: auto;
  }
  .seuil.porte .seuil-sous {
    position: relative;
    justify-content: center;
    /* L'air demandé : le double de la porte à table, parce qu'ici le vide
       est ce qui sépare la marque de l'acte, et qu'il n'y a rien d'autre
       entre les deux. */
    padding-bottom: clamp(44px, 10vh, 132px);
  }
    /* LA DEVISE ET LE NOM DU MODULE FORMENT UNE SEULE LIGNE, centrée.
     Le brief d'origine posait le module « en haut à droite », et c'est ce
     qu'on a fait tant que la composition était calée à gauche. Une fois le
     logotype réduit et centré, ce nom restait SEUL à l'autre bout de la
     page, sans rien pour le tenir — le propriétaire l'a vu : « Data.
     Clarity. Impact. et Collect ne sont pas bien disposés ».
  
     Ils se lisent maintenant d'un trait, séparés par un point médian, dans
     la même casse et le même interlettrage : la devise dit ce que fait la
     maison, le module dit où l'on entre, et un centre unique porte les
     deux. Le séparateur est décoratif — `aria-hidden` par construction,
     puisqu'un pseudo-élément n'entre pas dans l'arbre d'accessibilité. */
  .seuil.porte .seuil-sous > :last-child::before {
    content: "·";
    margin-inline-end: 0.75em;
    color: var(--ink-4);
  }

  /* ET FINALEMENT, LA LIGNE ENTIÈRE DISPARAÎT DE LA PORTE.
     Décision du propriétaire après deux dispositions essayées — à droite,
     puis centrée avec la devise : « enlève ça ». Le logotype suffit à dire
     la maison ; le module se lit dans l'URL, dans le titre de l'onglet et
     sur chaque écran d'après. Un écran d'entrée n'a pas à se présenter deux
     fois.
  
     MASQUÉ, PAS SUPPRIMÉ, et les deux règles au-dessus restent : le balisage
     doit demeurer — `seuil:check` exige les quatre parts dans l'ordre — et
     l'accueil de la plateforme, qui partage ce seuil, garde sa devise à
     gauche et son repère à droite au-dessus de sa table. Un seul sélecteur
     sépare les deux compositions.
  
     L'AIR QU'ELLE PORTAIT passe au logotype : sans ce report, le filet
     remonterait coller le dessin. */
  .seuil.porte .seuil-sous { display: none; }

  /* ══════════════════════════════════════════════════════════════════════
     3.6.0 — UN BLOC, ET NON TROIS BANDES.

     CE QUI N'ALLAIT PAS, dit par le propriétaire en une phrase — « je n'aime
     pas les pages logins » — et visible dès qu'on regarde la page entière au
     lieu du formulaire seul : le logotype était ÉPINGLÉ EN HAUT de la colonne,
     le filet le suivait sur TOUTE LA LARGEUR, et `.seuil-corps` prenait
     `flex: 1` pour centrer le formulaire dans ce qui restait. À l'écran cela
     donnait trois bandes sans rapport — une marque, un trait qui coupe la page
     en deux, un grand vide, un formulaire qui flotte, un vide plus grand
     encore. L'œil n'avait rien à suivre entre l'identité et l'action.

     Le vide n'était pas un choix : il était le RESTE d'une soustraction. La
     3.4.5 a masqué `.seuil-sous` sur demande, et le filet s'est retrouvé à
     séparer le logotype de rien. Un trait qui ne sépare rien ne compose pas,
     il coupe.

     CE QUI CHANGE, ET RIEN D'AUTRE. Les quatre parts restent, dans l'ordre —
     `seuil:check` l'exige, et elle a raison de l'exiger : les quatre portes
     avaient déjà divergé une fois. Le centrage horizontal reste, décision
     explicite du propriétaire en 3.4.2. Ce qui change est la RÉPARTITION
     VERTICALE : marque, filet et corps cessent d'être trois bandes réparties
     sur la hauteur pour devenir un seul groupe, centré comme un tout.

       · `.seuil-corps` rend son `flex: 1`. Tant qu'il l'avait, il absorbait
         toute la hauteur libre et fabriquait lui-même le vide qu'on lui
         reproche ; sizé à son contenu, il laisse le groupe se centrer.
       · Le filet cesse d'être pleine largeur. Trente-quatre pixels, centrés,
         dans l'accent : il APPARTIENT au bloc au lieu de le trancher. C'est
         la même mesure que le filet du panneau de l'apex — un souffle entre
         l'identité et l'action, pas une frontière.
       · Le logotype rétrécit encore et se rapproche. Il faisait jusqu'à
         420px avec 132px d'air en dessous ; il en fait 300 avec 26. Une page
         d'entrée n'est pas une affiche : la marque y dit où l'on est, elle
         n'y est pas le sujet.

     `safe center` DEUX FOIS, ici comme ailleurs : `.seuil` est
     `height: 100dvh; overflow: hidden`, et un centrage nu y envoie le haut du
     bloc au-dessus du point de départ quand le contenu dépasse — un
     formulaire dont les comptes de démonstration sont dépliés, par exemple.
     Le mot-clé aligne au début plutôt que de centrer dès que centrer coûterait
     du contenu ; la déclaration nue reste pour qui ne le connaît pas.

     RIEN NE FLOTTE TOUJOURS : pas de carte, pas d'ombre, pas de conteneur.
     Ce qui sépare reste un filet — il est seulement redevenu de la taille de
     ce qu'il sépare. */
  /* ══════════════════════════════════════════════════════════════════════
     LA PORTE PORTE LA COULEUR DE LA PLATEFORME, ET NE SUIT PLUS LE THÈME.

     Décision du propriétaire : « mets tous la couleur de la plateforme, pas le
     dark mode. » Une porte d'entrée n'est pas un écran de travail. Un écran de
     travail suit la préférence de qui l'ouvre, parce qu'on y passe des heures ;
     une porte se traverse en dix secondes, et elle doit dire la même chose à
     tout le monde — la première image d'une marque ne se négocie pas avec un
     réglage de système d'exploitation.

     POURQUOI CETTE FORME PLUTÔT QU'UNE AUTRE. Le mécanisme prévu par la charte
     serait `data-theme="light"` sur la racine du document, et il serait plus
     propre — sauf qu'il faudrait l'écrire dans la mise en page RACINE de quatre
     applications Next dont les portes ne sont que des routes parmi d'autres,
     ou l'appliquer côté client, ce qui ferait clignoter la page en sombre avant
     de la repeindre. La porte couvre déjà `100dvh` avec son propre `--paper` :
     redéclarer la palette SUR ELLE suffit, et rien ne clignote.

     LES CENT TRENTE ET UN JETONS SONT RECOPIÉS DEPUIS `:root`, sans en choisir
     aucun, et c'est délibéré. Une sélection — « la porte n'a besoin que de
     `--paper`, `--surface`, `--ink` et l'accent » — aurait tenu jusqu'au jour où
     un composant imbriqué lit `--attention-wash` : un seul jeton oublié, et
     c'est un bloc sombre au milieu d'une carte claire. Recopier tout est
     ennuyeux et ne peut pas être à moitié faux.

     `color-scheme: light` va avec : sans lui, le menu déroulant du sélecteur,
     les barres de défilement et le remplissage automatique du navigateur
     resteraient sombres à l'intérieur d'une page claire.
     ══════════════════════════════════════════════════════════════════════ */
  .seuil.porte {
    color-scheme: light;
    --brand-forest: #184a45;
    --brand-coral: #d37c69;
    --brand-blue: #6992b0;
    --brand-pink: #f2a6d0;
    --brand-raspberry: #985258;
    --brand-brown: #3a311d;
    --brand-mist: #c8cdd5;
    --brand-lime: #e3eed8;
    --brand-ink: #1c1d1d;
    --brand-ivory: #fdfaf8;
    --paper: #ffffff;
    --paper-stone: #ffffff;
    --paper-blush: #ffffff;
    --surface: rgb(255 255 255 / 0.55);
    --elevated: rgb(255 255 255 / 0.72);
    --floating: rgb(255 255 255 / 0.78);
    --surface-solid: #ffffff;
    --surface-2: rgb(58 49 29 / 0.05);
    --surface-3: rgb(58 49 29 / 0.085);
    --line: rgb(188 193 201);
    --line-strong: rgb(166 171 179);
    --ink: #1c1d1d;
    --ink-2: #4f5053;
    --ink-3: #6f7073;
    --ink-4: #c8cdd5;
    --nav-chapter: #777673;
    --accent: #0a4fcc;
    --accent-hover: #0741b8;
    --accent-ink: #05349a;
    --accent-wash: #e7edfa;
    --on-accent: #ffffff;
    --accent-hi: #0741b8;
    --accent-hover-hi: #0a3d99;
    --accent-active: #0741b8;
    --accent-glow: transparent;
    --coral: #d37c69;
    --coral-ink: #945d4a;
    --coral-wash: #f8ece8;
    --ok: #2d6341;
    --ok-wash: #eaefec;
    --info: #516e82;
    --info-wash: #edeff0;
    --attention: #945d4a;
    --attention-wash: #f8ece8;
    --critical: #985258;
    --critical-wash: #f2e8e6;
    --proposed: #0a4fcc;
    --proposed-wash: #eaf0fb;
    --editorial: #3a311d;
    --editorial-wash: #e8e4e0;
    --ok-vif: #52b075; --on-ok: #0b1a11;
    --info-vif: #516e82; --on-info: #ffffff;
    --attention-vif: #d37c69; --on-attention: #1c1d1d;
    --critical-vif: #985258; --on-critical: #ffffff;
    --proposed-vif: #0a4fcc; --on-proposed: #ffffff;
    --editorial-vif: #3a311d; --on-editorial: #ffffff;
    --neutral-vif: #c8cdd5; --on-neutral: #1c1d1d;
    --ok-wash-hi: #f2f5f3;
    --info-wash-hi: #f3f4f5;
    --attention-wash-hi: #fbf3f0;
    --critical-wash-hi: #f7f0ee;
    --proposed-wash-hi: #f3f6fd;
    --editorial-wash-hi: #efece9;
    --accent-wash-hi: #f0f0ee;
    --glass: rgb(255 255 255 / 0.58);
    --glass-line: rgb(200 205 213);
    --glass-edge: rgb(255 255 255 / 0.7);
    --edge-light: inset 0 1px 0 rgb(255 255 255 / 0.75);
    --fam-regulation: #3772c4;
    --fam-regulation-wash: #e3e8f1;
    --fam-regulation-ink: #3367ae;
    --fam-requirement: #2bb3a3;
    --fam-requirement-wash: #e2f1ed;
    --fam-requirement-ink: #25756c;
    --fam-data: #35a7d8;
    --fam-data-wash: #e3eff4;
    --fam-data-ink: #2b718f;
    --fam-governance: #d95c7a;
    --fam-governance-wash: #f8e5e8;
    --fam-governance-ink: #a44a60;
    --fam-operations: #96681c;
    --fam-operations-wash: #f0e7db;
    --fam-operations-ink: #875f1c;
    --fam-systems: #6c63d9;
    --fam-systems-wash: #eae6f4;
    --fam-systems-ink: #6159bf;
    --fam-assurance: #8aa64a;
    --fam-assurance-wash: #eeefe1;
    --fam-assurance-ink: #5f7138;
    --fam-output: #e67a45;
    --fam-output-wash: #fae9e1;
    --fam-output-ink: #9b5836;
    --fam-glossary: #6b6f77;
    --fam-glossary-wash: #eae8e7;
    --fam-glossary-ink: #63676e;
    --on-family: #ffffff;
    --on-fam-regulation: #ffffff;
    --on-fam-requirement: #1c1d1d;
    --on-fam-data: #1c1d1d;
    --on-fam-governance: #1c1d1d;
    --on-fam-operations: #ffffff;
    --on-fam-systems: #ffffff;
    --on-fam-assurance: #1c1d1d;
    --on-fam-output: #1c1d1d;
    --on-fam-glossary: #ffffff;
    --app-collect: #57778e;
    --app-knowledge: #3fafa0;
    --app-evidence: #6c63d9;
    --app-proqr: #ab5f39;
    --app-planned: #8c8b88;
    --shadow-sm: 0 1px 2px rgb(24 74 69 / 0.05), var(--edge-light);
    --shadow-md: 0 12px 32px rgb(24 74 69 / 0.08), 0 1px 2px rgb(24 74 69 / 0.04), var(--edge-light);
    --shadow-lg: 0 24px 64px rgb(24 74 69 / 0.14), 0 2px 6px rgb(24 74 69 / 0.05), var(--edge-light);
    --font-serif: "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, serif;
    --r-xs: 6px;
    --r-sm: 8px;
    --r-btn: 10px;
    --r-md: 12px;
    --r-card: 14px;
    --r-lg: 16px;
    --r-panel: 16px;
    --r-float: 18px;
    --r-modal: 20px;
    --blur-glass: 24px;
    --icon-stroke: 1.5;
    --icon-sm: 14px;
    --icon: 16px;
    --icon-lg: 18px;
    --ease: cubic-bezier(0.25, 0.1, 0.25, 1);
    --t-fast: 0.12s;
    --t-slow: 0.2s;
  }
  .seuil.porte {
    justify-content: center;
    justify-content: safe center;
    /* SUR L'AXE CROISÉ AUSSI. `.seuil` est une colonne flex sans
       `align-items` : ses enfants s'étirent, ce qui allait tant qu'ils
       prenaient toute la largeur. Depuis que la carte a une largeur propre,
       « s'étirer » devient « se caler au début » — et la carte partait à
       gauche de l'écran. */
    align-items: center;

    /* LA PAGE DÉFILE, PAS LA CARTE. `.seuil` est `height: 100dvh;
       overflow: hidden` — un choix juste pour l'ACCUEIL, dont la table doit
       tenir dans l'écran, et dont le commentaire d'origine explique qu'un
       `min-height` laisserait un contenu à peine trop grand déborder en
       silence. Sur une PORTE, la même règle produit l'inverse d'un service :
       la carte ne pouvant pas dépasser la fenêtre, elle tronque son propre
       contenu. Mesuré sur un écran de 800 pixels de haut : 645 visibles pour
       788, c'est-à-dire la moitié de la liste des comptes coupée net, avec une
       barre de défilement à l'intérieur d'une carte — le genre de chose qu'on
       ne voit jamais si on n'ouvre pas la page sur un portable.

       Une carte est un objet : elle a la taille de ce qu'elle contient. C'est
       la page qui s'allonge, et c'est ce que tout le monde attend d'un écran
       de connexion. */
    height: auto;
    min-height: 100dvh;
    overflow: visible;

    /* LES MARGES DE LA PORTE, et elles ne sont pas celles de l'accueil.
       `.seuil` pose `clamp(24px, 3.4vh, 48px) 5vw` — mesuré pour une TABLE qui
       doit tenir dans un écran, où chaque pixel de marge est un pixel de moins
       pour les lignes. Une porte n'a rien à faire tenir : elle porte un objet
       unique au milieu du vide, et 27 pixels entre cet objet et le bord de
       l'écran lisent comme un débordement rattrapé de justesse, pas comme une
       composition.

       La mesure verticale suit la hauteur disponible plutôt qu'une constante :
       sur un portable elle se replie à 40 pixels, sur un grand écran elle
       s'ouvre à 96. Le plancher compte autant que le plafond — c'est lui qui
       tient quand la page défile, et c'est là que la marge disparaissait. */
    padding: clamp(40px, 7vh, 96px) clamp(24px, 5vw, 80px);
  }
  /* `0 0 auto` ET NON `0 1 auto` : le troisième mot dit « peut grandir », le
     DEUXIÈME dit « peut rétrécir », et c'est celui-là qui coupait le texte. La
     porte a `min-height: 100dvh` ; dès que le contenu de la carte dépasse cette
     hauteur, un enfant qui accepte de rétrécir se fait comprimer par le
     conteneur — et `overflow: hidden`, qui est là pour découper les fonds au
     rayon, découpait du coup la dernière ligne du texte. Le contenu n'était pas
     trop long : la carte avait accepté d'être trop courte. */
  .seuil.porte .seuil-corps { flex: 0 0 auto; }
  .seuil.porte .seuil-marque {
    width: min(100%, clamp(168px, 22vw, 300px));
    margin-block-end: clamp(18px, 2.6vh, 26px);
  }
  .seuil.porte .seuil-filet {
    width: 34px;
    margin-inline: auto;
    margin-block-end: clamp(30px, 5vh, 56px);
  }

  /* LA BARRE D'ACCENT RETROUVE SA MESURE. `.seuil-trait` est déclarée
     `width: 36px; height: 2px` — une barre courte sous le titre, le seul
     endroit où l'accent apparaît sur cette page. Mais elle est enfant direct
     de `.seuil-corps`, et la règle de centrage au-dessus donne
     `width: min(100%, 440px)` à TOUS les enfants directs : elle l'étirait donc
     à 440 pixels. Une barre de 2px de haut sur 440 de large n'est plus une
     barre, c'est un trait plein — et la page se retrouvait avec deux longues
     horizontales à soixante-dix pixels d'écart, dont personne ne pouvait dire
     laquelle séparait quoi.

     Aucune barrière ne pouvait le voir : les deux règles sont justes prises
     séparément, et c'est leur RENCONTRE qui déforme. C'est exactement le même
     mécanisme que la confusion `accent`/`ok` de la table des campagnes.

     D'où le partage des rôles, maintenant lisible : le filet est un cheveu
     NEUTRE qui ferme le bloc d'identité, la barre est un accent COURT qui
     ouvre l'action. Deux traits, deux poids, deux métiers — au lieu de deux
     traits identiques qui se répétaient. */
  .seuil.porte .seuil-corps > .seuil-trait {
    /* LA BOÎTE GARDE LA LARGEUR DE LA COLONNE, LE TRAIT N'EN PEINT QUE
       TRENTE-SIX PIXELS. Rétrécir la boîte à 36px semblait la correction
       évidente et en produisait une autre : `.seuil-corps` centre ses enfants,
       si bien qu'une barre de 36 pixels se retrouvait au MILIEU de la page
       pendant que le titre et les champs s'alignaient à gauche de leur colonne.
       Une barre censée souligner un titre ne peut pas commencer ailleurs que
       lui.

       Le dégradé garde donc la boîte à la largeur des autres enfants — même
       bord gauche que le titre, sans exception au centrage — et n'y peint que
       les trente-six premiers pixels. Aucun balisage à changer dans les quatre
       portes, et rien à exempter de la règle de largeur. */
    background: linear-gradient(var(--accent), var(--accent)) center / 36px 100% no-repeat;
  }
  /* L'ancien décalage vers le haut a disparu AVEC la raison qui l'avait fait
     naître : il compensait un corps qui se centrait seul dans une hauteur qu'il
     s'était attribuée. Le groupe entier se centre désormais.

     IL A SURVÉCU UNE VERSION DE TROP, ET ÇA SE VOYAIT. Écrit
     `padding-bottom: 0`, il annulait le rembourrage bas de la CARTE née en
     3.7.0 : la dernière ligne du texte finissait à un pixel de la bordure,
     collée au cadre. Un rembourrage qui n'est pas symétrique sur une carte se
     lit comme un défaut de rendu, pas comme une intention. La règle est
     retirée plutôt que remise à zéro : le rembourrage de la carte suffit, et
     une déclaration qui ne fait plus rien est une dette pour le prochain
     lecteur. */

  /* ── La table ──────────────────────────────────────────────────────────
     Trois colonnes, et la même définition pour l'en-tête et les rangs : une
     seule source, donc aucune chance qu'ils se désalignent. */
  /* LA GRILLE EST FLUIDE, pas figée. La colonne d'index suit la largeur au
     lieu d'occuper 56 pixels quelle que soit la fenêtre, et les deux colonnes
     de texte se partagent le reste sans jamais déborder — `minmax(0, …)` est
     ce qui le garantit : sans lui, une colonne de grille refuse de descendre
     sous la largeur de son contenu et pousse la table hors de la page.

     Une seule définition pour l'en-tête et les rangs : ils ne peuvent donc pas
     se désaligner. */
  .seuil-liste,
  .seuil-tete {
    --colonnes: clamp(34px, 3.6vw, 62px) minmax(0, 1fr) minmax(0, 1.2fr);
    --gouttiere: clamp(14px, 2.4vw, 40px);
  }
  .seuil-tete {
    display: grid;
    grid-template-columns: var(--colonnes);
    gap: var(--gouttiere);
    padding: clamp(20px, 3.4vh, 44px) 0 10px;
    border-bottom: 1px solid var(--line);
    font-size: 11.5px;
    font-weight: 500;
    letter-spacing: 0.26em;
    text-transform: uppercase;
    color: var(--ink-3);
  }
  .seuil-liste {
    flex: 1;
    min-height: 0;
    display: flex;
    flex-direction: column;
  }
  /* LES RANGS SE PARTAGENT LA HAUTEUR QUI RESTE, au lieu d'une hauteur fixe qui
     ferait déborder la page sur un portable et laisserait un vide mort sur un
     grand écran. C'est ce qui rend le plein pied vrai à toutes les tailles. */
  .seuil-entree {
    flex: 1;
    min-height: clamp(44px, 7vh, 96px);
    display: grid;
    grid-template-columns: var(--colonnes);
    gap: var(--gouttiere);
    align-items: center;
    padding: clamp(6px, 1vh, 14px) 0;
    border-bottom: 1px solid var(--line);
    color: inherit;
    text-decoration: none;
  }
  .seuil-index {
    font-size: clamp(11.5px, 0.85vw, 13px);
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.04em;
    color: var(--ink-3);
  }
  .seuil-entree .nom {
    font-size: clamp(17px, 1.6vw, 26px);
    font-weight: 500;
    letter-spacing: -0.012em;
    line-height: 1.2;
    color: var(--ink);
  }
  .seuil-entree .clause {
    font-size: clamp(13px, 1.05vw, 16.5px);
    line-height: 1.45;
    color: var(--ink-2);
  }
  /* Meme mesure que `.cite:hover` : l'accent en TEXTE rend 3,90:1 sur le
     papier de nuit. --accent-hi : 7,29:1 en jour, 5,03:1 en nuit. */
  .seuil-entree:hover .nom,
  .seuil-entree:focus-visible .nom { color: var(--accent-hi); }
  .seuil-entree[aria-disabled="true"] { pointer-events: none; }
  .seuil-entree[aria-disabled="true"] .nom { color: var(--ink-3); font-weight: 400; }
  .seuil-entree[aria-disabled="true"] .clause,
  .seuil-entree[aria-disabled="true"] .seuil-index { color: var(--ink-3); }

  /* ── Le bloc de titre des portes de connexion ──────────────────────────
     L'accueil n'en a pas : sa table se lit d'elle-même. Une connexion doit
     dire où l'on est. */
  .seuil-titre {
    font-size: clamp(22px, 2vw, 30px);
    font-weight: 400; line-height: 1.18; letter-spacing: -0.018em;
    color: var(--ink); margin: clamp(16px, 3vh, 34px) 0 0;
    text-wrap: balance;
  }
  .seuil-chapo { margin: 10px 0 0; font-size: 14.5px; line-height: 1.6; color: var(--ink-2); }
  .seuil-trait {
    display: block; width: 36px; height: 2px; margin-top: 16px;
    background: var(--accent); border: 0;
  }

  /* ── Le formulaire ─────────────────────────────────────────────────────
     Posé dans la page : pas de carte, pas de fond, pas d'ombre.

     Les champs sont dessinés PAR ÉLÉMENT plutôt que par classe : les quatre
     produits n'ont pas la même écriture de formulaire, et leur demander à tous
     une classe commune serait quatre retouches à refaire au prochain
     changement. La portée reste close — rien ne sort de `.seuil-form`. */
  .seuil-form {
    /* largeur:ok — LA SEULE MESURE DU § SEUIL, et elle ne borne pas de la
       prose : elle borne une SAISIE. Un champ large de toute la page fait
       perdre à l'œil la ligne entre l'étiquette et sa valeur, et le curseur
       part chercher son début à un mètre de son libellé. La table, elle, prend
       toute la largeur — c'est le texte qui l'occupe, comme demandé.

       460 et non 420 depuis la 3.4.1 : le propriétaire a donné une fourchette,
       « approximately 440–480px », et 420 tombait dessous. La table de
       démonstration, elle, GARDE sa pleine largeur — c'est une décision
       antérieure du même propriétaire, donnée une fois et assez fermement pour
       qu'on ne la repose pas. Le caractère secondaire de cette section se joue
       sur le poids, pas sur la laisse : titre de 10px en capitales espacées,
       rangs en encre tertiaire. */
    width: min(100%, 460px);
    margin-top: clamp(16px, 3vh, 30px);
    display: flex;
    flex-direction: column;
    gap: 15px;
  }
  .seuil-form label,
  .seuil-form .etiquette {
    display: flex; flex-direction: column; gap: 6px;
    font-size: 11.5px; font-weight: 500; letter-spacing: 0.18em;
    text-transform: uppercase; color: var(--ink-3);
  }
  .seuil-form input,
  .seuil-form select {
    width: 100%; height: 44px; padding: 0 12px;
    border: 1px solid var(--line); border-radius: var(--r-sm);
    background: transparent; color: var(--ink);
    font: inherit; font-size: 14px; letter-spacing: normal; text-transform: none;
    outline: none; transition: border-color 140ms ease;
  }
  /* LE SÉLECTEUR CESSE D'ÊTRE UN CONTRÔLE DU SYSTÈME. La règle au-dessus lui
     donnait déjà la boîte du champ, mais pas son APPARENCE : `appearance` restait
     à `auto`, si bien que le navigateur continuait de dessiner sa propre flèche
     par-dessus. Sur la porte de Console — le seul écran d'entrée qui demande
     « en tant que qui » — cela produisait un contrôle qui ne parlait la langue
     d'aucun autre champ de la plateforme, et qui changeait de dessin d'un
     système à l'autre.

     LA FLÈCHE EST DESSINÉE EN GRIS FIXE, et c'est un compromis assumé : une
     `background-image` ne lit pas les variables CSS, donc elle ne peut pas
     suivre `--ink-3` d'un thème à l'autre. #8B8C8F est la valeur sombre de
     `--ink-3` et passe pour un gris moyen sur le papier clair — un chevron de
     dix pixels de large n'a pas à porter de sens, il a à ne pas se remarquer.
     La solution propre serait un masque coloré par `currentColor`, mais elle
     masquerait aussi le texte du contrôle : un `select` n'accepte pas de
     pseudo-élément où poser la flèche à part.

     `padding-right` fait la place : sans lui, une option longue passerait sous
     le chevron au lieu de s'arrêter avant. */
  .seuil-form select {
    appearance: none;
    -webkit-appearance: none;
    padding-right: 34px;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 10'%3E%3Cpath d='m2 4 3 3 3-3' fill='none' stroke='%238B8C8F' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 12px center;
    background-size: 10px 10px;
  }
  .seuil-form input::placeholder { color: var(--ink-3); }
  .seuil-form input:focus,
  .seuil-form select:focus { border-color: var(--ink-3); }
  .seuil-form input:focus-visible,
  .seuil-form select:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
  /* Le bouton garde la couleur primaire de la charte, sans exception. */
  .seuil-form button[type="submit"],
  .seuil-form .btn-primaire {
    width: 100%; height: 44px;
    display: inline-flex; align-items: center; justify-content: center; gap: 10px;
    border: 0; border-radius: var(--r-sm);
    background: var(--accent); color: var(--on-accent);
    font: inherit; font-size: 14px; font-weight: 500;
    letter-spacing: normal; text-transform: none;
    cursor: pointer; transition: opacity 140ms ease;
  }
  .seuil-form button[type="submit"]:hover,
  .seuil-form .btn-primaire:hover { opacity: 0.9; }
  .seuil-form button[type="submit"]:focus-visible,
  .seuil-form .btn-primaire:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

  .seuil-second { margin-top: 16px; font-size: 13px; color: var(--ink-2); }
  .seuil-second a {
    color: var(--ink-2); text-decoration: none; border-bottom: 1px solid var(--line);
  }
  .seuil-second a:hover,
  .seuil-second a:focus-visible { color: var(--ink); border-bottom-color: var(--ink-3); }

  /* ── Les comptes de démonstration ──────────────────────────────────────
     Une liste éditoriale, pas un tableau encadré : aucune bordure extérieure,
     aucun fond. Elle prend le rythme tabulaire de la page, à une échelle qui
     ne dispute rien au formulaire. */
  .seuil-demo { margin-top: clamp(20px, 4vh, 40px); }
  .seuil-demo-titre {
    /* 11,5px — le plancher de la charte. À 10px, capitalisé et espacé de
       0,3em, c'était le texte le plus petit de la PORTE D'ENTRÉE de chaque
       produit : la ligne qui dit à quoi sert la liste en dessous. */
    font-size: 11.5px; font-weight: 500; letter-spacing: 0.3em; text-transform: uppercase;
    color: var(--ink-3); margin: 0 0 4px;
  }
  .seuil-demo-liste { display: flex; flex-direction: column; border-top: 1px solid var(--line); }
  .seuil-demo-rang {
    display: grid; grid-template-columns: 1fr auto; align-items: baseline; gap: 24px;
    width: 100%; padding: 9px 0;
    border: 0; border-bottom: 1px solid var(--line); background: none;
    color: inherit; font: inherit; text-align: left; text-decoration: none;
    cursor: pointer; transition: color 140ms ease;
  }
  .seuil-demo-rang .qui {
    font-size: 13px; color: var(--ink-2);
    min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .seuil-demo-rang .role { font-size: 12px; color: var(--ink-3); }
  .seuil-demo-rang:hover .qui,
  .seuil-demo-rang:focus-visible .qui { color: var(--ink); }
  .seuil-demo-note { margin: 12px 0 0; font-size: 11.5px; color: var(--ink-3); }

  /* ── Le pied ───────────────────────────────────────────────────────────── */
  .seuil-pied {
    flex: 0 0 auto;
    padding: clamp(14px, 2.2vh, 22px) 0 0;
    display: flex; align-items: baseline; justify-content: space-between; gap: 20px;
    font-size: 11.5px; font-weight: 500; letter-spacing: 0.3em; text-transform: uppercase;
    color: var(--ink-3);
  }
  .seuil-pied-liens { display: flex; gap: clamp(16px, 2vw, 28px); }
  .seuil-pied a { color: inherit; text-decoration: none; }
  .seuil-pied a:hover, .seuil-pied a:focus-visible { color: var(--ink-2); }

  /* SOUS 720px, LA TABLE SE REPLIE SANS RIEN PERDRE. Trois colonnes dans 375
     pixels ne sont plus une table, ce sont trois mots coupés — mais jeter la
     colonne d'index jetterait une information que la page affichait. Elle
     passe donc au-dessus du nom, en petit : deux colonnes, le rang tenant sur
     deux lignes.

     L'en-tête disparaît, lui, parce qu'il ne titre plus rien : une seule
     colonne de contenu n'a pas besoin qu'on lui dise qu'elle est la colonne
     du nom. */
  @media (max-width: 720px) {
    /* Sous cette largeur, la fraction de 58vw laisserait une marge morte à
       droite du logotype alors que la place manque partout ailleurs : il
       reprend toute la colonne. */
    .seuil-marque { width: 100%; }
    .seuil-tete { display: none; }
    .seuil-entree {
      grid-template-columns: minmax(0, 1fr);
      gap: 3px;
      align-items: start;
      align-content: center;
      padding: clamp(12px, 2vh, 20px) 0;
    }
    .seuil-index {
      font-size: 11.5px;
      letter-spacing: 0.24em;
      order: -1;
    }
    .seuil-sous,
    .seuil-pied { flex-direction: column; align-items: flex-start; gap: 8px; }
    .seuil-pied-liens { flex-wrap: wrap; }
  }

  /* ══════════════════════════════════════════════════════════════════════
     3.9.0 — CE QUE LES SEPT ÉCRIVAIENT CHACUN DANS SON COIN

     Ces sept dessins n'étaient nulle part et partout : recopiés en styles
     en ligne dans Console, Trust, Proqr et Vendor-Connect — vingt-deux
     `.panel` refaits, huit listes de définitions, cinq asides
     « marginLeft: auto, 11px », une bande de statistiques en huit propriétés.
     Les nommer une fois, c'est cesser de les corriger sept fois.
     ══════════════════════════════════════════════════════════════════════ */

  /* LE REPLI NATIF. `<details>`/`<summary>` est le tiroir de contenu de la
     maison — « l'acte irréversible se replie » (Console), « la raison
     n'apparaît qu'au geste » (Trust, Proqr). Huit replis de Console étaient
     stylés en ligne à 17–19px de haut : sous le plancher de la cible (2.5.8),
     et sans marqueur de dévoilement quand un `summary` porte `.btn`. */
  .repli > summary {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    min-height: 24px;
    padding: 2px 4px;
    margin-inline: -4px;
    border-radius: var(--r-sm);
    font-size: 12.5px;
    color: var(--ink-2);
    cursor: pointer;
    list-style: none;
  }
  .repli > summary::-webkit-details-marker { display: none; }
  .repli > summary::before {
    content: "";
    width: 7px;
    height: 7px;
    flex-shrink: 0;
    border-right: 1.5px solid currentColor;
    border-bottom: 1.5px solid currentColor;
    transform: rotate(-45deg);
    transition: transform var(--t-fast) var(--ease);
  }
  .repli[open] > summary::before { transform: rotate(45deg); }
  .repli > summary:hover { color: var(--ink); }
  summary.btn { list-style: none; }
  summary.btn::-webkit-details-marker { display: none; }

  /* LE TABLEAU QUI SE LIT EN CARTES. Sous 640px, une rangée devient un bloc :
     l'en-tête disparaît de l'œil (pas du lecteur d'écran — il reste dans le
     flux, seulement retiré visuellement), et chaque cellule porte son
     étiquette. DÉCLARATIF : l'écran pose `class="table cartes"` et un
     `data-label` sur chaque `<td>`. Les produits qui ont déjà leur pli
     (`.col-fold`, FoldStack — Console, Trust, Proqr, Vendor-Connect à 720px)
     le gardent ; ce motif sert Collect, dont quinze tableaux sur dix-neuf
     n'avaient aucun repli et débordaient à droite. */
  /* UN MOT INSÉCABLE FIXE LA LARGEUR DU TABLEAU ENTIER.
     Mesuré le 16 septembre 2026 sur `/people` chez Console : le tableau
     faisait 421px dans une fenêtre de 375, et la cause tenait en un mot —
     `a.diallo@example.com`, 192px, qu'aucune règle ne coupe parce qu'un
     courriel ne contient pas d'espace. Il fixait la largeur MINIMALE de sa
     colonne, donc celle du tableau. « Statut », la colonne qu'on vient lire,
     commençait hors de l'écran.

     `anywhere` ET PAS `break-word` : seul `anywhere` entre dans le calcul de
     la largeur min-content, et c'est cette largeur-là qui fixait le tableau.
     Mesuré dans les deux sens sur la page servie — avec `break-word` le
     tableau reste à 421px et le texte coupe simplement plus tôt : la
     correction AURAIT EU L'AIR de marcher.

     Posée sur la CELLULE et non sur le courriel : un plan de contrôle est
     plein de mots insécables — un identifiant, une empreinte, une clé de
     service, une URL — et le prochain arrivera par un autre écran.

     RIEN ICI NE TIENT CETTE RÈGLE : une barrière de charte lit du CSS, pas
     une largeur rendue. C'est `tableaux:check` chez Console qui la tient là
     où elle se mesure. Le trou est nommé plutôt que tu. */
  @media (max-width: 767px) {
    .table :is(td, th) { overflow-wrap: anywhere; }
  }

  @media (max-width: 639px) {
    .table.cartes thead {
      position: absolute;
      width: 1px;
      height: 1px;
      overflow: hidden;
      clip-path: inset(50%);
      white-space: nowrap;
    }
    .table.cartes tr {
      display: grid;
      grid-template-columns: minmax(0, 1fr);
      padding: 10px 0;
      border-bottom: 1px solid var(--line);
    }
    .table.cartes td,
    .table.cartes tbody th {
      display: grid;
      grid-template-columns: minmax(0, 8rem) minmax(0, 1fr);
      gap: 4px 12px;
      padding: 4px 0;
      border: 0;
      text-align: left;
    }
    .table.cartes td::before,
    .table.cartes tbody th::before {
      content: attr(data-label);
      color: var(--ink-3);
      font-size: 11.5px;
    }
    .table.cartes td:not([data-label])::before,
    .table.cartes tbody th:not([data-label])::before { content: none; }
  }

  /* L'EN-TÊTE DE PAGE ET SON APARTÉ. L'aparté s'aligne sur le TITRE, pas sur
     le chapô : posé sans marge, il flottait au milieu d'un bloc de deux
     lignes. Et `.section-head` enveloppe désormais : les asides `nowrap` de
     Trust débordaient à 375px. */
  .page-head {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    justify-content: space-between;
    gap: 16px;
    margin-bottom: 36px;
  }
  .page-head > .aside { margin-top: 6px; }
  .section-head { flex-wrap: wrap; }
  .section-head .section-aside {
    margin-left: auto;
    display: inline-flex;
    align-items: center;
    gap: 10px;
    align-self: center;
    font-size: 12px;
    color: var(--ink-3);
  }

  /* LE TITRE D'UNE CARTE, LES FAITS, LA BANDE DE STATISTIQUES. */
  .card-title {
    font-size: 13px;
    font-weight: 550;
    color: var(--ink);
    letter-spacing: -0.005em;
  }
  .facts {
    display: grid;
    grid-template-columns: minmax(0, 10rem) minmax(0, 1fr);
    gap: 8px 16px;
    margin: 0;
    font-size: 12.5px;
  }
  .facts dt { color: var(--ink-3); }
  .facts dd { margin: 0; color: var(--ink-2); }
  @media (max-width: 479px) {
    .facts { grid-template-columns: minmax(0, 1fr); gap: 2px 0; }
    .facts dd { margin-bottom: 8px; }
  }
  .stat-strip {
    display: flex;
    flex-wrap: wrap;
    gap: 1px;
    background: var(--line);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    overflow: hidden;
  }
  .stat-strip > * {
    flex: 1 1 8rem;
    background: var(--surface-solid);
    padding: 12px 14px;
  }

  /* LA BASCULE DE LANGUE. Six coquilles forçaient `fontSize: 10.5` sur un
     `.btn.ghost` pour « EN » / « FR » : le seul texte de bouton sous 11,5px
     de la plateforme. La chasse est voulue — deux lettres capitales ont
     besoin d'air —, la taille ne l'était pas. */
  .btn.langue { letter-spacing: 0.1em; }

  /* ══════════════════════════════════════════════════════════════════════
     3.9.0 — LA CIBLE AU DOIGT

     Tout tient aujourd'hui entre 24 et 32px : le plancher de la WCAG (2.5.8,
     24px) de justesse, et rien n'approche la recommandation de 44px. Le
     dessin de bureau ne bouge pas — « ONE shape: 30px tall » est une décision
     — mais un pointeur GROSSIER n'est pas une souris : il n'a ni la précision
     ni le survol qui rattrapent une cible étroite.
     ══════════════════════════════════════════════════════════════════════ */
  /* 3.13.0 — 44px, LA RECOMMANDATION QUE CETTE NOTE CITAIT SANS L'ATTEINDRE.
     La même liste vaut sous 768px quel que soit le pointeur : voir le § 4
     de « 3.13.0 — LA QUALITÉ D'iOS », en fin de fichier. */
  @media (pointer: coarse) {
    .btn, .search-trigger { min-height: 44px; }
    .btn, .chip:is(a, button), .tab, .repli > summary { min-width: 44px; }
    .icon-btn { width: 44px; height: 44px; }
    .product-switch { height: 44px; }
    .topbar-identite .signout { min-height: 44px; min-width: 44px; }
    .chip:is(a, button), .tab { min-height: 44px; }
    .tab { display: inline-flex; align-items: center; }
    /* LES RANGÉES DE LA PORTE. Mesurées à 39px au doigt le 16 septembre 2026,
       sur les sept produits : un pixel sous le plancher, parce que leur
       hauteur vient de leur contenu (`padding: 9px 0`) et que cette liste
       n'était nommée nulle part ici. C'est la PREMIÈRE chose qu'on touche en
       arrivant, et sur certains produits la seule. */
    .seuil-demo-rang { min-height: 44px; align-content: center; }
    .field :is(input, select) { height: 44px; }
    .nav-item, .product-item, .palette-row { padding-block: 12px; min-height: 44px; }
    .repli > summary, summary.btn { min-height: 44px; }
    /* 3.13.1 — SIX CIBLES QUE CE BLOC NE NOMMAIT PAS, et que chaque produit
       relevait dans sa propre feuille. Mesurées au doigt par les relevés des
       sept (6-9 octobre 2026) : le lien de la marque 17×16 à 130×19 ; le
       champ de la palette 225×25 à 520×23 ; le lien seul d'une cellule,
       d'un fait ou d'un fil d'Ariane 8×15 à 283×15 ; « Mot de passe
       oublié ? » 132×17 ; la rangée d'objet rendue en lien 35 à 38px ; le
       champ d'édition en place 343×33. `:not([class])` : un lien qui porte
       une classe (`.btn`, `.chip`, `.cite`, `.pill`…) a déjà son dessin et sa
       règle — un `min-height` posé sur une pastille de 22px la redessinerait
       à 44. La même liste vaut sous 768px : § « 3.13.1 », 2. */
    .brand-link,
    :is(.table td, .table th, .table li, .facts dd, .breadcrumb) > a:not([class]) {
      display: inline-flex;
      align-items: center;
      min-height: 44px;
      min-width: 44px;
    }
    .seuil-second a { display: inline-flex; align-items: flex-end; min-height: 44px; }
    .palette-head input { min-height: 44px; }
    .object-row:is(a, button, label),
    .editable .editing { min-height: 44px; }
  }
  /* iOS ZOOME LA PAGE au focus d'un champ sous 16px, et ne dézoome jamais :
     le lecteur se retrouve dans un écran deux fois trop grand, à faire
     glisser la page pour lire ce qu'il tape. Téléphone seulement — sur une
     tablette la place existe, et 16px y déformerait les formulaires denses. */
  @media (pointer: coarse) and (max-width: 767px) {
    /* TOUS LES CHAMPS, PAS SEULEMENT CEUX DE `.field`. La règle ne nommait
       que le formulaire type, et manquait les deux champs que l'on rencontre
       en premier : celui de la PALETTE DE COMMANDE (15px — la recherche de
       toute la maison) et le `select` de la PORTE (14px — la seule commande
       de la page d'entrée de Trust et de Proqr). Un champ qui zoome à
       l'entrée laisse le lecteur dans un écran deux fois trop grand avant
       même qu'il soit entré. */
    .field :is(input, select, textarea),
    .palette-head input,
    .seuil-form :is(input, select, textarea) { font-size: 16px; }
    /* LE LIEN D'ACCUEIL DU TÉLÉPHONE. Mesuré à 101×19px au doigt.
       LA NORME EST TENUE, ET CE N'EST PAS LA RAISON : la WCAG 2.5.8 AA
       exempte une cible sous 24px quand rien d'autre n'est à moins de 24px
       de son centre, et le voisin le plus proche est à 83px. Ce qui est en
       cause est le plancher de CETTE MAISON, 44px au doigt depuis la 3.13.0 — et l'objet :
       c'est le lien d'accueil, celui qu'on vise du pouce en tenant
       l'appareil, sur l'écran où l'on a le moins de place pour viser.
       Il n'existe QUE sur téléphone (`.mobile-only`), donc aucun écran large
       ne le montre et aucune relecture faite au bureau ne pouvait le voir. */
    a.mobile-only { display: inline-flex; align-items: center; min-height: 44px; min-width: 44px; }
  }

}

/* ══════════════════════════════════════════════════════════════════════════
   3.13.0 — LA QUALITÉ D'iOS

   Une couche, la même que tout ce qui précède, posée APRÈS lui : chaque
   règle d'ici l'emporte sur celle qu'elle remplace par l'ORDRE, et ce
   qu'elle retire, elle le retire par une déclaration écrite
   (`overflow-wrap: normal`, `display: grid`, `background: transparent`) —
   jamais par une absence. C'est ce qui permet d'éprouver cette feuille en
   l'AJOUTANT à la page servie, sur la plateforme vivante, sans rien
   reconstruire : ce que l'ajout mesure est ce que la feuille fera.

   Chaque paragraphe dit ce que la sonde d'écrans a mesuré sur les sept
   produits (sept largeurs, de 375 à 1 440, tous les chemins servis) et ce
   qui change. Les nombres complets sont dans la livraison de cette version.
   ══════════════════════════════════════════════════════════════════════════ */
@layer yntgyst-components {

  /* ── 1 · UNE PASTILLE NE SE COUPE PAS ────────────────────────────────────
     `.pill` (22px) et `.chip` (24px) ont une hauteur FIXE, et rien ne leur
     interdisait de passer à la ligne : dans une cellule étroite, « Reçue en
     entier » se posait sur deux ou trois lignes DANS une forme d'une ligne,
     et sortait par le haut et par le bas — jusqu'à 94px chez Proqr, 833
     occurrences chez Trust, « Acti/ve » chez Console. La charte a tiré cette
     leçon pour `.btn` en 1.5.0 (« The 30px was DECLARED and not TRUE ») ;
     elle ne l'avait pas tirée pour les marques.
     Une marque est un mot, pas une phrase : elle ne passe jamais à la ligne
     et ne se laisse pas écraser par ses voisines. `.pill.wrap` reste le SEUL
     chemin vers deux lignes, et le dit désormais en toutes lettres. */
  .pill,
  .chip { white-space: nowrap; flex-shrink: 0; }
  .pill.wrap { white-space: normal; flex-shrink: 1; }

  /* ── 2 · UN TABLEAU ÉTROIT NE HACHE PLUS SES MOTS ────────────────────────
     Sous 768px, `overflow-wrap: anywhere` était posé sur TOUTES les cellules
     pour qu'un courriel insécable cesse de fixer la largeur d'un tableau
     (voir sa note, plus haut). Il tenait sa promesse et en cassait une
     autre : `anywhere` ramène la largeur minimale de chaque cellule à UN
     glyphe, et l'algorithme du tableau automatique donne alors la place au
     contenu le plus long. Les colonnes courtes tombaient à une lettre —
     « STATU/S », « OVERA/LL », « R/e/l/e/a/s/e » sur 29 × 140px, « Acti/ve »
     sous sa pastille : 3 674 mots coupés chez Trust, 938 écrasements chez
     Proqr.
     LA CELLULE GARDE `anywhere` : c'est là que vivent les courriels, les
     URL, les empreintes, et le prochain arrivera par un autre écran. CE QUI
     N'EST PAS DU TEXTE LIBRE LE PERD — l'en-tête, qui nomme la colonne et en
     devient le PLANCHER (une colonne ne descend plus sous le plus long mot
     de son titre), la pastille, la marque, le bouton, le repli, la touche,
     le registre, la date, le nombre, et la cellule que le produit déclare
     serrée (`.tight`, la convention de Proqr et de Trust).
     CE QUI RESTE TROP LARGE DÉFILE, AU LIEU DE S'ÉCRASER : dans sa
     `.data-scroll`, région focalisable. C'est un état intermédiaire assumé ;
     le vrai remède d'un tableau-liste au téléphone est `.table.cartes`
     (plus haut), que chaque produit pose écran par écran. */
  @media (max-width: 767px) {
    .table th,
    .table :is(td, th):is(.num, .tnum, .tight),
    .table :is(td, th) :is(.pill, .chip, .btn, .kbd, .register, .tnum, summary, time) {
      overflow-wrap: normal;
    }
  }
  /* Le résumé d'un repli est une COMMANDE — « Release », « Révoquer » — et
     il était le seul à pouvoir passer à la ligne : `inline-flex`, sans
     `nowrap`. Dans une colonne serrée il s'écrivait une lettre par ligne. */
  .repli > summary { white-space: nowrap; }

  /* ── 3 · LA BARRE DU HAUT TIENT DANS UN TÉLÉPHONE ────────────────────────
     À 375px la barre porte jusqu'à sept commandes dans 343px : menu, marque,
     recherche, puis ce que le produit pose — cloche, langue, thème — et la
     sortie. Le déclencheur de recherche, seul élément compressible
     (`min-width: 0`), tombait entre 24 et 93px, et son libellé à « R… » ou
     « Sea… » — sur chaque page de chaque produit, 749 fois chez Knowledge.
     Une ellipse qui ne laisse qu'une lettre n'est plus un libellé.

     LA BARRE DÉCIDE SUR SA PROPRE LARGEUR, PAS SUR CELLE DE L'ÉCRAN. Sous
     600px de barre — tout téléphone, et l'iPad en portrait, où la barre
     latérale n'en laisse que 520 —, la recherche devient ce qu'elle est sur
     iOS : une loupe de 44px. Le mot et le ⌘K quittent l'ÉCRAN, pas
     l'oreille : depuis la coquille 1.11.0 le nom est porté par le bouton
     lui-même (`aria-label`, le même mot), si bien qu'ils peuvent sortir par
     `display: none` sans rendre la commande muette. La déconnexion repliée
     suit la même voie ; son mot n'a plus besoin de la technique `.sr-only`,
     qu'aucun outil de mesure ne distingue d'un texte coupé.

     LE SEUL `!important` DE CETTE CHARTE HORS DU MOUVEMENT RÉDUIT, et sa
     raison. La coquille rend le déclencheur élastique par un style EN LIGNE
     (`flex: 1`, `maxWidth: 420`) — c'est lui qui donne au bureau sa grande
     recherche, et Knowledge, qui sert une copie de cette feuille, en dépend.
     Un style en ligne bat toute feuille ; seul `!important` le bat. Il ne
     vise qu'une propriété, sous une seule condition : sous 600px de barre,
     la loupe ne s'étire pas.

     LES CIBLES DE 44px DÉBORDENT SUR L'AIR QUI LES SÉPARE, comme les
     boutons d'une barre d'iOS : 44px pour le doigt, 32 dans la rangée
     (`margin-inline: -6px`). Sept commandes de 44px et leurs écarts
     réclament 372px ; il en existe 343. Deux cibles voisines se partagent
     au plus 4px, pris dans l'écart qui les séparait déjà. */
  .topbar { container: barre / inline-size; }
  @media (max-width: 479px) {
    .topbar { gap: 8px; }
    .topbar-identite .signout .signout-mot { display: none; }
  }
  @container barre (max-width: 599px) {
    .search-trigger {
      flex: 0 0 auto !important;
      width: 28px;
      min-width: 28px;
      max-width: none;
      padding: 0;
      justify-content: center;
    }
    .search-trigger > :not(svg) { display: none; }
  }
  /* Au bureau (pointeur fin, 768px et plus), la loupe a la taille de ses
     voisines, 28px, et rien ne déborde : c'est au doigt et sous 768px
     seulement que les cibles passent à 44 et empiètent sur l'air. */
  @media (pointer: coarse), (max-width: 767px) {
    @container barre (max-width: 599px) {
      .search-trigger { width: 44px; min-width: 44px; }
      .topbar :is(.icon-btn, .search-trigger, .btn, .signout) { margin-inline: -6px; }
    }
  }

  /* ── 4 · 44px AU DOIGT — ET SOUS 768px ───────────────────────────────────
     Le bloc tactile de la 3.9.0 (plus haut) montait les commandes à 40px et
     citait lui-même la recommandation de 44 sans l'atteindre ; les marques
     et les onglets restaient à 32, la sortie repliée à 28 de large. Mesuré
     sur les sept : 1 951 cibles sous 44 chez Trust, 1 551 chez Proqr, 1 144
     chez Knowledge. Le bloc tactile passe à 44, et la même liste vaut sous
     768px quel que soit le pointeur : une fenêtre de téléphone est tenue au
     doigt, et la densité du bureau n'y a rien à faire. Les deux listes sont
     la même et se lisent ensemble ; `cible:check` lit la première. */
  @media (max-width: 767px) {
    .btn, .search-trigger { min-height: 44px; }
    .btn, .chip:is(a, button), .tab, .repli > summary { min-width: 44px; }
    .icon-btn { width: 44px; height: 44px; }
    .product-switch { height: 44px; }
    .topbar-identite .signout { min-height: 44px; min-width: 44px; }
    .chip:is(a, button), .tab { min-height: 44px; }
    .tab { display: inline-flex; align-items: center; }
    .seuil-demo-rang { min-height: 44px; align-content: center; }
    .field :is(input, select) { height: 44px; }
    .nav-item, .product-item, .palette-row { padding-block: 12px; min-height: 44px; }
    .repli > summary, summary.btn { min-height: 44px; }
    a.mobile-only { display: inline-flex; align-items: center; min-height: 44px; min-width: 44px; }
  }

  /* ── 5 · LA CARTE DE LA PORTE NE COUPE RIEN ──────────────────────────────
     La rangée « se connecter en tant que » était une grille `1fr auto` à
     24px d'écart : le rôle gardait sa largeur, le courriel partait en
     ellipse — « a.diallo@exa… » entre 375 et 430px, sur la première page de
     chaque produit. Et la note de la porte, où vit un jeton insécable
     (`EVIDENCE_ALLOW_DEMO_IN_PRODUCTION=true`), dépassait de 5px d'une
     carte en `overflow: hidden`, qui la coupait.
     Sous 480px le rôle passe SOUS le nom — la rangée d'une liste d'iOS, un
     titre et son sous-titre — et le courriel ne se coupe plus : il a la
     largeur de la carte, et un mot trop long pour elle se replie au lieu de
     disparaître. */
  .seuil.porte .seuil-corps { overflow-wrap: break-word; }
  .seuil-demo-note { overflow-wrap: anywhere; }
  .seuil-demo-rang { grid-template-columns: minmax(0, 1fr) auto; }
  .seuil-demo-rang .qui {
    overflow: visible;
    text-overflow: clip;
    white-space: normal;
    overflow-wrap: anywhere;
  }
  @media (max-width: 479px) {
    .seuil-demo-rang {
      grid-template-columns: minmax(0, 1fr);
      align-items: start;
      gap: 2px;
    }
  }

  /* ── 6 · UNE BANDE DE CHIFFRES A DES TUILES ÉGALES ───────────────────────
     `.stat-strip` était un `flex-wrap` à base de 8rem : un montant de 26px
     (« 1 712 037,40 € ») poussait sa tuile sur sa propre ligne — trois
     tuiles donnaient 2 + 1, quatre 1 + 2 + 1 — et la dernière s'étirait sur
     toute la largeur ; un libellé qui passait à deux lignes décalait son
     chiffre de 20px sous ceux des voisines.
     La bande devient une GRILLE de colonnes égales — autant qu'il en tient
     de 10rem, soit deux sur tout téléphone — et chaque tuile y occupe trois
     rangées PARTAGÉES (`subgrid`) : libellé, chiffre, note. Les chiffres
     d'une même rangée tombent sur la même ligne, quel que soit le libellé.
     Le filet d'un pixel n'est plus le fond de la bande vu entre les tuiles
     — une case vide l'aurait peint en aplat — mais le contour de chaque
     tuile, dans l'écart. Et le chiffre se règle sur SA tuile (13 % de sa
     largeur, entre 18 et 26px), comme un chiffre d'iOS qui rétrécit pour
     tenir plutôt que de sortir. */
  .stat-strip {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 10rem), 1fr));
    background: transparent;
  }
  .stat-strip > * {
    flex: none;
    min-width: 0;
    grid-row: span 3;
    box-shadow: 0 0 0 1px var(--line);
  }
  .stat-strip > .stat { container-type: inline-size; }
  .stat-strip > .stat:not(:has(> :nth-child(4))) {
    display: grid;
    grid-template-rows: subgrid;
    row-gap: 0;
    align-content: start;
  }
  .stat-strip > .stat .value { font-size: clamp(18px, 13cqi, 26px); }

  /* ── ET UN FILET : UN CHAMP NE DÉPASSE PAS SON FORMULAIRE ─────────────────
     Un `select` prend la largeur de sa plus longue option : chez Proqr, des
     phrases de 80 à 100 caractères poussaient la page 479px au-delà de
     l'écran. Le produit raccourcit ses options ; la charte borne le champ.
     `max-width` seul ne suffit pas : un `select` dont la largeur est un
     POURCENTAGE cesse de compter sa plus longue option dans la largeur
     minimale de son conteneur, et c'est elle qui poussait la page. */
  .field :is(input, select, textarea) { max-width: 100%; }
  .field select { width: 100%; }

  /* ── ET LES ONGLETS DISENT QU'IL EN RESTE ────────────────────────────────
     `.tabs` défile en largeur, et c'est voulu ; mais rien ne disait qu'il
     restait des onglets à droite — « Evidence ( » coupé au milieu de son
     compteur, l'étape « Assess… » d'un assistant. Le bord s'estompe quand le
     composant d'onglets du produit pose `.deborde`, c'est-à-dire tant qu'il
     reste à défiler (Trust a déjà `TabsRecenter` pour le savoir). Un masque
     et non une couleur : le fondu vaut sur tous les fonds et les deux
     thèmes. */
  .tabs.deborde {
    -webkit-mask-image: linear-gradient(to right, currentColor calc(100% - 40px), transparent);
    mask-image: linear-gradient(to right, currentColor calc(100% - 40px), transparent);
  }
}

/* ══════════════════════════════════════════════════════════════════════════
   3.13.1 — LA PERSONNE AU TÉLÉPHONE, ET CE QUE LES SEPT RELEVAIENT SEULS

   Même forme que le § 3.13.0 au-dessus : une couche, la même, posée APRÈS
   tout ce qui précède ; ce qu'elle retire, elle le retire par une
   déclaration écrite. Le 1 est une décision du propriétaire ; les 2 à 7
   sont des règles que les relevés d'écrans des sept produits (sonde iOS,
   sept largeurs, valeurs longues, 6-9 octobre 2026) avaient posées chacun
   dans SA feuille — Collect, Console, Evidence, Knowledge, Proqr, Trust,
   Vendor-Connect —, parce que la cause est ici et que le produit ne pouvait
   pas toucher la charte. Chacune remonte une fois, au lieu d'être recopiée
   sept fois ou oubliée par ceux qui ne l'avaient pas encore mesurée.
   ══════════════════════════════════════════════════════════════════════════ */
@layer yntgyst-components {

  /* ── 1 · SOUS 768px, LES INITIALES — LE NOM ENTIER DANS LE MENU DU COMPTE ─
     Décision du propriétaire, 10 octobre 2026 (D21). Jusqu'ici la barre
     effaçait le nom sous 768px et gardait la seule déconnexion : personne ne
     voyait plus qui était connecté sur un téléphone, et au-dessus le nom
     passait à l'ellipse à 22 caractères (« Nina Falk Telekommunikationsdienste
     de Saint-Rémy-… », 366 à 404px cachés chez Vendor-Connect ; « Engagement ·
     Déclaration de durabilité… », 500px chez Evidence).
     SOUS 768px, la coquille 1.12.0 rend un bouton de 44px (`.compte`) qui
     porte les INITIALES dans une pastille de 32px, et dont le NOM ACCESSIBLE
     est le nom entier. Il ouvre le MENU DU COMPTE : le nom entier, qui passe
     à la ligne au lieu de se couper, puis la déconnexion, une rangée de 44px.
     La déconnexion de la barre s'y range : la barre garde le même nombre de
     commandes qu'avant, la pastille prenant la place de la sortie repliée.
     À 768px ET AU-DESSUS, RIEN NE CHANGE : le nom et la sortie restent dans
     la barre, et le bouton comme le menu ne s'y montrent jamais.
     LE MENU S'ACCROCHE À LA BARRE, PAS AU BOUTON. `.topbar-identite` n'est
     pas positionnée : le bloc conteneur du menu est `.topbar` (absolue), si
     bien que `right: 8px` le tient dans l'écran quelle que soit la place de
     la pastille, et `max-width` l'empêche d'en sortir. */
  .topbar-identite .compte {
    display: none;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    width: 44px;
    height: 44px;
    padding: 0;
    border: 0;
    border-radius: 999px;
    background: none;
    font: inherit;
    cursor: pointer;
  }
  .compte-initiales {
    display: grid;
    place-items: center;
    width: 32px;
    height: 32px;
    border-radius: 999px;
    background: var(--surface-3);
    color: var(--ink);
    font-size: 12px;
    font-weight: 600;
    letter-spacing: 0.02em;
    line-height: 1;
    transition: box-shadow var(--t-fast) var(--ease);
  }
  .topbar-identite .compte:hover .compte-initiales,
  .topbar-identite .compte[aria-expanded="true"] .compte-initiales {
    box-shadow: 0 0 0 1.5px var(--ink-3);
  }
  /* Une fenêtre, donc opaque — la règle des fenêtres de la 3.11, plus haut :
     `--surface-solid`, le filet de `--glass-edge`, l'ombre du niveau
     flottant. */
  .compte-menu {
    position: absolute;
    top: calc(100% + 4px);
    right: 8px;
    z-index: 45;
    width: max-content;
    min-width: 208px;
    max-width: calc(100% - 16px);
    padding: 4px;
    border: 1px solid var(--glass-edge);
    border-radius: var(--r-float);
    background: var(--surface-solid);
    box-shadow: var(--shadow-lg);
  }
  /* LE NOM ENTIER, jamais coupé : il passe à la ligne, et un mot plus long
     que le menu (un nom de famille de 35 lettres) se replie au lieu de
     sortir. */
  .compte-nom {
    margin: 0;
    padding: 8px 10px 6px;
    font-size: 12.5px;
    font-weight: 500;
    line-height: 1.4;
    color: var(--ink);
    white-space: normal;
    overflow-wrap: break-word;
  }
  .compte-menu form {
    margin: 4px 0 0;
    padding-top: 4px;
    border-top: 1px solid var(--line);
  }
  .compte-sortie {
    display: flex;
    align-items: center;
    width: 100%;
    min-height: 44px;
    padding: 0 10px;
    border: 0;
    border-radius: var(--r-sm);
    background: none;
    font: inherit;
    font-size: 12.5px;
    color: var(--ink-2);
    text-align: start;
    cursor: pointer;
    transition: background-color var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
  }
  .compte-sortie:hover,
  .compte-sortie:focus-visible {
    background: var(--surface-2);
    color: var(--ink);
  }
  @media (max-width: 767px) {
    .topbar-identite > form { display: none; }
    .topbar-identite .compte { display: inline-flex; }
  }
  @media (min-width: 768px) {
    .compte-menu { display: none; }
  }
  /* La pastille suit ses voisines de la barre (§ 3 de la 3.13.0) : 44px pour
     le doigt, 32 dans la rangée. */
  @media (pointer: coarse), (max-width: 767px) {
    @container barre (max-width: 599px) {
      .topbar .compte { margin-inline: -6px; }
    }
  }

  /* ── 2 · LES SIX CIBLES DU BLOC TACTILE, SOUS 768px AUSSI ─────────────────
     Les six cibles que la 3.13.1 ajoute au bloc `pointer: coarse` (3.9.0,
     plus haut, où `cible:check` les lit), et la même liste sous 768px quel
     que soit le pointeur — la règle du § 4 de la 3.13.0. Collect, Console,
     Knowledge, Proqr, Trust et Vendor-Connect les relevaient chacun dans sa
     feuille ; Evidence relevait sa marque par le CONTENU qu'il passe au lien.
     LE LIEN DE LA MARQUE PORTE UNE CLASSE (`.brand-link`, coquille 1.12.0) :
     `.sidebar-brand a`, la forme que les produits écrivaient, atteint aussi
     les liens du menu des produits (`.product-item`, `display: block`), que
     le même `inline-flex` remettrait en ligne. */
  @media (max-width: 767px) {
    .brand-link,
    :is(.table td, .table th, .table li, .facts dd, .breadcrumb) > a:not([class]) {
      display: inline-flex;
      align-items: center;
      min-height: 44px;
      min-width: 44px;
    }
    .seuil-second a { display: inline-flex; align-items: flex-end; min-height: 44px; }
    .palette-head input { min-height: 44px; }
    .object-row:is(a, button, label),
    .editable .editing { min-height: 44px; }
  }
  /* LE CHAMP DE LA PALETTE MONTE DANS SON REMBOURRAGE : 44 + 2 × 4 = 52, la
     tête garde la hauteur qu'elle avait (53px) au lieu de passer à 73.
     ET UNE `.chip` QUI PORTE UNE COMMANDE GRANDIT AVEC ELLE. `.chip` a une
     hauteur FIXE de 24px ; la croix qui retire un élément, portée à 44px au
     doigt par le produit, en débordait et recouvrait celle de la rangée
     d'en dessous (Collect, 17 chevauchements de 32 %, relevé du 6 octobre
     2026). Au doigt et sous 768px, la hauteur devient un plancher ; au
     bureau, rien ne bouge. */
  @media (pointer: coarse), (max-width: 767px) {
    .palette-head { padding-block: 4px; }
    .chip:has(> :is(a, button)) { height: auto; min-height: 24px; }
  }

  /* ── 3 · UN NOM NE PASSE PAS À L'ELLIPSE ─────────────────────────────────
     Décision Q-B du propriétaire (6 octobre 2026) : seuls les jetons
     techniques — courriel, identifiant, empreinte, URL — passent à
     l'ellipse. La charte la posait sur des NOMS : le nom et l'aide d'une
     rangée d'objet (les quatre aides des réglages de Proqr coupées à 375px,
     « The documents a supplier has to produce », 77px cachés), et le nom
     d'un résultat de la palette (« Établissements Camara-Delacroix,
     Ébénisterie… », 304 à 329px cachés à 375px chez Proqr, 9 à 34 encore à
     1 440). Ils passent à la ligne, et la rangée grandit.
     DANS LA PALETTE, LE NOM SE LIT EN COLONNE, ET LA RANGÉE NE SE REPLIE
     QUE S'IL LE FAUT. Le nom avait une base nulle (`flex: 1`) ET une
     largeur minimale nulle (`min-width: 0`) : une référence de 68
     caractères en chasse fixe (≈ 470px) prenait la rangée entière à
     375px, et le nom était écrasé à côté d'elle. Il garde sa base nulle —
     il prend la place que la référence et le type lui laissent, et s'y
     replie entre ses mots, en colonne —, mais plus sa largeur nulle : il
     ne descend jamais sous son plus long mot (`min-width: auto`). Quand ce
     mot ne tient pas à côté de la référence, la rangée se replie et le nom
     passe sur la ligne suivante ; la référence se replie à ses tirets
     plutôt que de sortir. Un mot n'est coupé que s'il est plus large que
     la rangée.
     NI UNE BASE FIXE, NI LA LONGUEUR DU NOM. La première écriture de ce §
     demandait 14rem au nom, quel qu'il soit : 224px, plus la référence et
     le type, dépassent la rangée d'un téléphone, et CHAQUE résultat
     passait sur deux lignes, nom court compris — « Business Term » seul en
     ligne 2 chez Knowledge, 44 → 71,6px par rangée, huit résultats
     visibles devenus cinq et demi (contre-lecture du lot, 10 octobre
     2026). La deuxième lui donnait pour base sa propre longueur (`auto`) :
     un nom court tenait, mais un nom plus long que la place laissée
     passait sur SA ligne, la référence seule au-dessus, le type seul
     dessous — chez Proqr, « Casques de visioconférence pour l'équipe
     données » 62,8 → 99px, la liste « PR » de 657 à 782px (reprise, 10
     octobre 2026). En colonne, elle revient à 657. */
  .object-row .name,
  .object-row .sub {
    overflow: visible;
    text-overflow: clip;
    white-space: normal;
    overflow-wrap: break-word;
  }
  .palette-row { flex-wrap: wrap; }
  .palette-row .name {
    flex: 1 1 0%;
    min-width: auto;
    overflow: visible;
    text-overflow: clip;
    white-space: normal;
    overflow-wrap: break-word;
  }
  .palette-row .ref {
    min-width: 0;
    max-width: 100%;
    overflow-wrap: break-word;
  }

  /* ── 4 · UN TITRE NE FAIT PAS DÉFILER LA PAGE ────────────────────────────
     Un élément souple garde pour largeur minimale celle de son plus long
     mot. « Solvik Energi Informationssicherheitsmanagementsystem » en
     `.page-title` (≈ 410px dans une colonne de 343, Trust),
     « Nachhaltigkeitsberichterstattungsverpflichtungen » (≈ 700px, Evidence),
     un nom de famille de 35 lettres (Console) : la page défilait en largeur
     de 375 à 768px, et l'en-tête entier sortait de l'écran.
     `break-word` NE COUPE QUE LE MOT QUI NE TIENT SUR AUCUNE LIGNE, et ne
     touche pas la largeur minimale : aucun titre ordinaire ne bouge. Le
     bloc du titre d'une `.page-head` peut désormais céder (`min-width: 0`).
     Un produit qui préfère réduire la police d'un tel mot plutôt que le
     replier (Trust, `TitreAjuste`) le garde : ce plancher ne joue alors
     jamais. */
  .page-head > :not(.aside) { min-width: 0; }
  .page-hero,
  .page-title,
  .object-title { overflow-wrap: break-word; }
  .section-head h2,
  .section-head .title {
    min-width: 0;
    overflow-wrap: break-word;
  }

  /* ── 5 · EN CARTE, UNE MARQUE GARDE SA LARGEUR ───────────────────────────
     Une cellule de `.table.cartes` est une grille sans `justify-items` :
     une pastille de statut seule s'y étirait en ruban sur toute la colonne
     de la valeur (Collect, réserve de #432 ; Proqr ; Trust). Les marques et
     les commandes se posent au début de leur piste ; le texte et les blocs
     gardent la piste entière. */
  @media (max-width: 639px) {
    .table.cartes :is(td, tbody th) > :is(.pill, .chip, .badge-count, .count, .cite, .kbd, .btn, a, button, time) {
      justify-self: start;
    }
  }

  /* ── 6 · LE POINT D'UNE PASTILLE GARDE SA FORME ──────────────────────────
     Quand le texte d'une `.pill.wrap` réclame plus que sa ligne, la flexbox
     retire la place aux DEUX enfants au prorata de leur base : le point de
     5px tombait à un trait de 2 à 3px (Vendor-Connect, « En attente de votre
     confirmation » à 768px). Même chose pour le point d'un bandeau. */
  .pill .dot,
  .banner .bar .dot { flex-shrink: 0; }

  /* ── 7 · LE RÉSUMÉ D'UN REPLI, DANS UNE RANGÉE D'ACTES ───────────────────
     `margin-inline: -4px` aligne le chevron sur le texte d'une PHRASE. Dans
     une `.btn-row`, le résumé est une commande parmi des boutons : quand il
     finit la ligne, sa boîte dépassait de 4px ce qui la contient, et le
     cadre d'un tableau défilait pour 4px (Trust, /remediation à 390px, 362
     pour 358). */
  .btn-row .repli > summary { margin-inline: 0; }

  /* ── 8 · AUCUNE LETTRE SOUS 11,5px ───────────────────────────────────────
     Rien ici : les trois règles changent EN PLACE, plus haut, parce que
     `cible:check` lit la règle elle-même et ne doit plus rien exempter.
     `.fi` (la lettre F ou I d'une clause, fait ou inférence) passe de 8 à
     11,5px dans sa boîte de 12 ; `.fam.sm` de 8 à 11,5px dans son carré de
     20 ; `.fam` de 10 à 11,5px dans celui de 28. Décision D11/Q-C du
     propriétaire : 0 texte rendu sous 11,5px. `.fi` est rendue par
     Knowledge sur ses fiches d'objet ; CIBLE 1.7.0 retire les trois
     exemptions, si bien qu'une lettre qui redescend rougit chez les sept. */
}
