/* ============================================================================
   SAWAKONEKT — Système de design des APPLICATIONS
   Contrat de sortie T7 → T2 : docs/2026-08-24_SWK_SPC_da-systeme-applications-sawakonekt_v1-0.html

   Distinct de `partners.css` (landing + pages partenaires) et le restant : une
   application impose des contraintes qu'une page de présentation n'a pas —
   densité, répétition, petits corps, états d'erreur. Les tokens de marque sont
   IDENTIQUES ; ce fichier ajoute ce que la landing ne peut pas donner.

   ── ARBITRAGE DU RÉGENT : OPTION A (spec DA §3.1) ───────────────────────────
   L'action principale porte une ENCRE SOMBRE sur le safou plein, pas du blanc.
     blanc sur --safou  → ÉCHOUE au texte courant (4,5) ET au grand texte (3)
     --ink sur --safou  → PASSE, avec deux fois la marge
   Zéro token ajouté, couleur de marque intacte. C'est un parti pris assumé :
   le libellé sombre rend le bouton plus dense, ce qui sert une interface de travail.

   ✔ DIVERGENCE FERMÉE le 2026-08-25, sur ordre du Régent : la landing porte
   désormais la même option A. `partners.css`, le <style> d'index.html et la
   règle inline de devenir-hote-locker sont passés à `color:var(--ink)`.
   Douze règles au total, y compris les états de survol, les pastilles de
   pagination et les icônes sociales — pas seulement le bouton principal.
   Recalculé à l'application, pas repris — et AUCUN de ces ratios n'est inscrit
   ici : la porte du domaine les produit. Ne jamais revenir au blanc sur
   cette couleur : elle est trop claire pour le porter.

   ── POLICES ────────────────────────────────────────────────────────────────
   L'application ne prend que DEUX familles : Sora (titres, chiffres, libellés)
   et Inter (lecture). Unbounded est une voix d'affiche — elle sert une accroche,
   aucun écran de travail, et coûterait un fichier de plus sur une connexion
   mobile. La landing la garde, l'application ne la charge pas.
============================================================================ */

:root{
  /* ── tokens de marque, relevés sur la landing — AUCUNE valeur modifiée ── */
  --safou:#FF6B35; --safou-600:#E85620; --teal:#0A9396; --teal-700:#07747A;
  --gold:#F4A261; --sable:#FEFBF6; --ink:#141B26; --ink-soft:#3A4453;
  --muted:#6B7686; --line:#ECE4D7; --surface:#FFFFFF;
  --midnight:#0A1320; --midnight-soft:#111E2E;
  --radius:18px; --radius-sm:12px; --radius-lg:28px;

  /* ── ajouts application, dérivés sous contrainte (teinte+saturation figées) ── */
  /* ⚠ CHIFFRES RECALCULÉS le 2026-08-26 (signalé par T2, mesuré ici). L'annotation
     portait le chiffre de l'ANCIEN hex #6A7585,
     survivant à la valeur qu'il décrivait. Un chiffre a été recalculé lors du relevé,
     l'autre a survécu — « seuil juste, à côté d'une valeur fausse », dans la feuille
     qui prétend fermer cette classe.
     UN SEUL NOMBRE POUR DEUX FONDS EST DÉJÀ UN AVEU : deux fonds différents ne peuvent
     pas rendre le même ratio. Le tell était lisible sans rien mesurer. */
  --muted-600:#677281;   /* texte secondaire sur clair.
                            ⚠ DOMAINE (cf. bloc @encre) : blanc, sable, gold-wash SEULS.
                            Exclue de safou-wash, teal-wash et rouge-wash.
                            Le PIRE fond d'une encre sombre est le lavis le plus SOMBRE
                            (rouge-wash), jamais le plus clair. */
  --safou-700:#DB3B00;   /* variante portant un libellé BLANC — non employée sous option A */
  --rouge:#DB242A;       /* état d'échec, sur blanc. La landing n'en a aucun.
                            CONSTANTE ΔE(safou)=24 — arbitrage du Régent, 2026-08-27 : ce
                            n'est PAS un ratio. Ses deux entrées sont des hex LITTÉRAUX
                            FIXES de ce fichier, donc elle ne peut pas se désynchroniser ;
                            un ratio de contraste, lui, dépend d'un FOND qui varie. Une
                            constante inscrite ne ment jamais — c'est ce qui l'autorise
                            à rester là où tous les ratios sont partis. */
  --rouge-wash:#FBE9EA; --teal-wash:#E6F1F2; --gold-wash:#FEF6EF; --safou-wash:#FFF0EB;
  /* DÉCLARÉES au bloc @encre depuis le 2026-08-27 : la porte les recalcule à chaque
     passage. `--encre-warn` est exclue de trois lavis, `--encre-err` tient sur les six
     fonds clairs du kit — et ces deux faits se relisent à la porte, pas ici. */
  --encre-warn:#B5570D;  /* encre d'attente — domaine : son lavis (gold-wash), sable, blanc */
  --encre-err:#D12228;   /* encre d'échec — domaine : son lavis (rouge-wash), sable, blanc */
  /* UNE SEULE VALEUR POUR UN SEUL NOM. --encre-safou valait #CF3700 ici et
     #AE3C12 sur la landing : deux vérités pour un token, c'est zéro token, et
     la divergence était invisible parce que les deux feuilles ne se croisent
     jamais. #CF3700 passait SOUS le seuil sur le lavis safou de la landing — sous le
     seuil, à un centième près, donc indétectable à l'œil. La valeur retenue est
     la plus sûre des deux, portée aux deux endroits. */

/* ═══════════════════════════════════════════════════════════════════════════
   DÉCLARATION CANONIQUE DES ENCRES — ordre du Régent, 2026-08-25.
   SOURCE UNIQUE ET SEULE AUTORITÉ. T2 branche sur ces valeurs ; toute nouvelle
   teinte, ou tout fond à ajouter à un domaine, remonte ici avec son cas mesuré.

   Chaque ligne @encre est MACHINE-LISIBLE et vérifiée par la porte
   `tools/sonde/…_domaine-encres_v1-0.mjs` — sans quoi elle ne serait qu'un
   commentaire, c'est-à-dire une intention.

   POURQUOI UN DOMAINE, ET PAS SEULEMENT UNE VALEUR. Une encre n'est jamais juste
   ou fausse en soi : elle l'est SUR UN FOND. `--muted` était calibrée contre le
   sable et ÉCHOUAIT posée sur `--midnight` ; `--muted-600` est calibrée contre
   le sable et ÉCHOUE sur le lavis teal. Deux fois la même classe de défaut :
   BONNE VALEUR, MAUVAIS FOND. Aucune porte d'unicité ne l'attrape — le token n'a
   qu'une valeur, elle est simplement employée là où elle ne tient pas.

   Syntaxe :  @encre <token> <hex> | seuil <r> | fonds <tokens…> | exclus <tokens…>
   « exclus --neant » = AUCUNE exclusion, dit explicitement. Laisser le champ vide
   serait indiscernable d'un oubli, et un oubli qui ressemble à une décision est
   la forme la plus tenace du Label≠Contenu.

   @encre --encre-safou  #AE3C12 | seuil 4.5 | fonds --surface --sable --safou-wash --gold-wash --teal-wash --rouge-wash | exclus --neant
   @encre --muted-600    #677281 | seuil 4.5 | fonds --surface --sable --gold-wash | exclus --safou-wash --teal-wash --rouge-wash
   @encre --encre-neutre #636D7C | seuil 4.5 | fonds --surface --sable --safou-wash --gold-wash --teal-wash | exclus --rouge-wash

   FAMILLE SOMBRE — ajoutée le 2026-08-26 sur ordre du Régent. Le cas FONDATEUR
   de toute cette porte (`span.c`, --muted posée sur --midnight) vivait sur
   un fond sombre, et jusqu'ici AUCUNE ligne ne couvrait ces fonds : la porte
   gardait tout sauf l'endroit d'où venait l'accusation. Une encre branchée hors
   domaine déclaré est une encre non gardée.
   ENCRES D'ÉTAT — déclarées le 2026-08-27 sur ordre du Régent. Elles étaient les
   DEUX SEULES encres du kit hors du bloc : la porte ne les calculait donc pas, et
   c'est très exactement par là que quatre chiffres faux avaient survécu dans
   `partners.css`. Ancrer un ratio à son hex ne suffit pas — ce qui protège, c'est
   qu'une PORTE le recalcule. Un token hors déclaration est un token hors garde.
   @encre --encre-warn      #B5570D | seuil 4.5 | fonds --surface --sable --gold-wash | exclus --safou-wash --teal-wash --rouge-wash
   @encre --encre-err       #D12228 | seuil 4.5 | fonds --surface --sable --safou-wash --gold-wash --teal-wash --rouge-wash | exclus --neant
   FAMILLE SOMBRE COMPLÉTÉE le 2026-08-27. Les FILETS entrent au bloc avec leur PROPRE
   seuil : la grammaire porte déjà `seuil <r>`, donc un filet se vérifie exactement comme
   une encre. Le mot « encre » est alors un peu large — mais un token gardé sous un nom
   imparfait vaut mieux qu'un token juste et non gardé, et c'est très exactement la leçon
   que --encre-warn vient de coûter.
   ⚠ CE QUE LA PORTE NE VÉRIFIE PAS, et qu'il faut savoir : elle contrôle un PLANCHER, pas
   un plafond. Un filet posé à 5:1 passerait un seuil de 1,6 en étant visuellement faux.
   Le rang se lit dans le nom ; seule sa borne basse est mesurée.
   @encre --rouge-sombre      #E9767A | seuil 4.5 | fonds --midnight --midnight-soft --midnight-carte --midnight-soft-carte | exclus --surface --sable --safou-wash --gold-wash --teal-wash --rouge-wash
   @encre --line-carte-sombre #3E4F68 | seuil 1.6 | fonds --midnight --midnight-soft --midnight-carte --midnight-soft-carte | exclus --neant
   @encre --line-fort-sombre  #5F799F | seuil 3.0 | fonds --midnight --midnight-soft --midnight-carte --midnight-soft-carte | exclus --neant
   @encre --ink-soft-sombre #BFC2C5 | seuil 4.5 | fonds --midnight --midnight-soft --midnight-carte --midnight-soft-carte | exclus --surface --sable --safou-wash --gold-wash --teal-wash --rouge-wash
   @encre --muted-sombre    #909AA6 | seuil 4.5 | fonds --midnight --midnight-soft --midnight-carte --midnight-soft-carte | exclus --surface --sable --safou-wash --gold-wash --teal-wash --rouge-wash

   LECTURE DU TABLEAU, mesurée le 2026-08-25 :
   · --encre-safou tient sur TOUS les fonds clairs du kit. Elle est
     la seule des trois sans exclusion.
   · --muted-600 est exclue de QUATRE fonds sur sept — pas du seul lavis teal que
     j'avais nommé. Je l'avais qualifiée trop bas d'un cran.
   · --encre-neutre, POURTANT PLUS SOMBRE de quatre points, passe là où --muted-600
     échoue. Sur un lavis, c'est elle qu'il faut employer — pas --muted-600.

   LECTURE DE LA FAMILLE SOMBRE, mesurée le 2026-08-26 sur les 5 pages à sections
   nuit (relevés de la sonde, pas lecture de feuille) :
   · 37 fonds sombres distincts portent réellement du texte — pas deux. La plupart
     sont des dégradés ou des voiles sur photo, hors kit et hors déclaration ; les
     quatre tokens déclarés bornent la partie du kit, et cette borne est dite ici
     plutôt que sous-entendue.
   · 7 encres sont peintes sur nuit ; QUATRE ne sont pas des tokens (#9FE6B0,
     #CFE8EA, #CFEFF0, #9FE6E8 — blocs de code et faux terminaux). Elles passent
     toutes largement (10,5 à 14,5), mais aucune porte ne les tient : un littéral
     n'a pas de domaine. Dette nommée, pas fermée — cf. rapport du jour.
   · L'exclusion des six fonds clairs n'est pas prudentielle : les deux encres sombres y sont TRÈS
     loin du seuil. Une exclusion qui passerait serait une peur.
   ═══════════════════════════════════════════════════════════════════════════ */
  /* ⚠ CORRIGÉ le 2026-08-26 : ces deux chiffres étaient faux, ET la phrase qui les
     portait raisonnait à l'envers. « Sur le lavis le plus CLAIR » était présenté comme
     le cas défavorable — c'est l'inverse : pour une encre sombre, le plus clair des
     fonds donne le MEILLEUR contraste, et le pire est le lavis le plus sombre. Le
     commentaire contredisait de surcroît la lecture du tableau vingt lignes plus haut,
     qui annonçait le pire cas : deux affirmations incompatibles sur la même encre,
     dans la même feuille. Et le chiffre annoncé le plus FLATTEUR était aussi le plus
     faux — le biais n'était pas aléatoire. */
  --encre-safou:#AE3C12; /* tient sur TOUT le domaine clair du kit — pire cas : rouge-wash, le plus SOMBRE */
  /* ── FAMILLE SOMBRE ────────────────────────────────────────────────────────
     Elle MIROITE la famille claire, rang pour rang : --ink / --ink-soft /
     --muted-600  ↔  blanc / --ink-soft-sombre / --muted-sombre. Un nom qui dit
     son rang se pose sans réfléchir ; un nom qui dit son fond (« --sur-sombre »)
     se lit comme un rôle générique et attire tout ce qui traîne.

     LES DEUX FONDS DE CARTE SONT DES TOKENS, PAS DES VOILES. Les surfaces
     posaient `rgba(255,255,255,.06→.08)` par-dessus le midnight pour détacher une
     carte. Un voile blanc ÉCLAIRCIT le fond, donc ABAISSE le contraste du texte
     posé dessus — et il le fait sans qu'aucune valeur écrite ne change, ce qui le
     rend invisible à toute relecture de feuille. Rendus opaques, ces deux fonds
     se mesurent et se déclarent. Ils sont le BORD CLAIR de la famille sombre :
     un voile réel (.06) tombe entre le token nu et son token de carte (.08),
     donc toute carte sombre réellement peinte est encadrée par la déclaration. */
  --midnight-carte:#1E2632;      /* = --midnight      + voile blanc .08, opacifié */
  --midnight-soft-carte:#24303F; /* = --midnight-soft + voile blanc .08, opacifié */
  --ink-soft-sombre:#BFC2C5;     /* rang 2 sur nuit — tient du fond nu au pire bord de carte */
  /* Rang 3 sur nuit. --muted (#6B7686) y ÉCHOUAIT — calibré pour le sable,
     employé sur le midnight : une encre juste au mauvais endroit. Dérivée en
     ÉCLAIRCISSANT, teinte et saturation figées.
     ⚠ VALEUR RELEVÉE le 2026-08-26 : #7D8897 → #909AA6. L'ancienne tenait sur les
     deux fonds NUS et tombait SOUS le seuil dès le premier voile de carte.
     Elle était donc vraie là où on l'avait mesurée et fausse là où on allait la
     poser : exactement la classe « bonne valeur, mauvais fond ». Le commentaire
     qui l'accompagnait portait deux chiffres qui n'étaient ceux
     d'aucun fond réel. Un chiffre appartient au hex de sa ligne (règle T2). */
  --muted-sombre:#909AA6; /* rang 3 sur nuit — tient de --midnight au pire fond de carte sombre */
  /* ── LES DEUX MANQUES DE LA FAMILLE SOMBRE, fermés le 2026-08-27 (ordre du Régent) ──
     ① AUCUNE ENCRE D'ÉCHEC. `--rouge` (#DB242A) rend 3,81 sur --midnight et 2,74 sur le
        pire fond de carte : il ÉCHOUE partout sur la nuit. D'où le contournement visible
        dans la feuille — une pastille PLEINE (blanc sur --rouge) là où le clair se
        contente d'une encre. Un contournement n'est pas un rang : il impose une forme
        (le plein) là où le besoin était une couleur, et il ne se décline pas.
     ② AUCUN RANG DE FILET. Le clair en a trois ; la nuit n'en avait aucun.
        `--line-carte-sombre` était NOMMÉ dans un commentaire de cette feuille et défini
        nulle part — un token qui n'existe que dans sa propre mention.
     Dérivés sous contrainte : teinte et saturation de la famille figées, seule la clarté
     bouge, jusqu'au rang visé sur le PIRE fond sombre (--midnight-soft-carte, le plus
     clair : pour une encre claire sur nuit, c'est le fond clair qui est défavorable). */
  --rouge-sombre:#E9767A;      /* encre d'ÉCHEC sur nuit — la pastille pleine cesse d'être obligatoire */
  --line-carte-sombre:#3E4F68; /* rang LISIBILITÉ — bord d'une carte non interactive */
  --line-fort-sombre:#5F799F;  /* rang CONTRÔLE — WCAG 1.4.11 exige 3:1 pour la limite d'un contrôle */
  /* Le rang DÉCORATIF n'a PAS de token neuf : `--midnight-carte` posé en bord sur
     `--midnight` le tient déjà. Ajouter une couleur pour un rang déjà servi, c'est
     fabriquer la fourche de demain. */
  --encre-neutre:#636D7C; /* état neutre sur #F1EEE7 — teinte et saturation de --muted-600, inchangées */

  /* trois filets, UNE teinte (37,1°) et UNE saturation (35,6 %), trois seuils :
       --line       rang DÉCORATIF — séparateurs À L'INTÉRIEUR d'une carte (décoratif, hors 1.4.11)
       --line-carte rang LISIBILITÉ — bord d'une carte non interactive (lisibilité, pas norme)
       --line-fort  rang CONTRÔLE (1.4.11 exige 3:1) — CONTRÔLES : champ, case, carte sélectionnable, bouton fantôme */
  --line-carte:#D8C7AC; --line-fort:#AF8D56;

  --f-t:'Sora',system-ui,sans-serif;
  --f-b:'Inter',system-ui,sans-serif;

  --app-maxw:480px;      /* l'hôte tient sa boutique avec un téléphone */
  --ease:cubic-bezier(.22,1,.36,1);
}

*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
body{
  margin:0; font-family:var(--f-b); color:var(--ink); background:var(--sable);
  line-height:1.55; -webkit-font-smoothing:antialiased;
}
h1,h2,h3{font-family:var(--f-t); line-height:1.12; letter-spacing:-.02em; margin:0}
p{margin:0} a{color:inherit} img,svg{display:block;max-width:100%}
:focus-visible{outline:3px solid var(--teal-700); outline-offset:2px; border-radius:6px}

/* ── coquille d'application ─────────────────────────────────────────────── */
.app{max-width:var(--app-maxw); margin-inline:auto; min-height:100dvh; display:flex; flex-direction:column; background:var(--sable)}
.app__corps{flex:1; padding:0 18px 96px}          /* 96px = hauteur de la barre + marge */
.app__corps--simple{padding-bottom:32px}          /* écran sans barre d'onglets */

.app__barre-haut{
  display:flex; align-items:center; gap:10px;
  padding:14px 18px; background:var(--surface);
  border-bottom:1px solid var(--line-carte); position:sticky; top:0; z-index:20;
}
.app__retour{
  /* 44×44 : cible tactile. Le glyphe reste petit, c'est la BOÎTE qui doit être
     saisissable — 2.5.5 (AAA) et les guides mobiles disent 44 ; 2.5.8 (AA) n'exige
     que 24. On tient 44 : sur un écran de retrait, un pouce ne vise pas un rond de 36. */
  display:inline-flex; align-items:center; justify-content:center;
  width:44px; height:44px; border-radius:50%; border:1px solid var(--line-fort);
  background:var(--surface); color:var(--ink); cursor:pointer; flex:0 0 auto;
  font-size:1.1rem; line-height:1; text-decoration:none;
}
.app__retour:hover{background:var(--sable)}
.app__titre-barre{font-family:var(--f-t); font-size:1.05rem; font-weight:600}

.marque{font-family:var(--f-t); font-weight:700; font-size:1.15rem; letter-spacing:-.02em;
  --wcag-exempt:logotype}   /* l'exemption est déclarée DANS la règle qui cause l'écart ;
  la propriété personnalisée hérite, donc elle couvre le mot-marque sans toucher le markup */
.marque span:first-child{color:var(--ink)}
.marque span:last-child{color:var(--safou)}
/* SOUS le seuil sur blanc, et DELIBEREMENT conserve.
   WCAG 1.4.3 exempte explicitement les logotypes : « le texte qui fait partie
   d'un logo ou d'un nom de marque n'a pas d'exigence de contraste minimum ».
   Le mot-marque reste donc a la couleur de marque, comme sur la landing.
   ⚠ CORRECTION DE MOI-MEME : dans le kit de maquette v1-1 j'avais « corrige » ce
   meme mot-marque en --encre-safou, en le comptant comme un defaut. C'etait
   qualifier UN CRAN TROP HAUT — le biais pessimiste coute autant que le flatteur,
   et il decredibilise les vrais defauts qu'on annonce a cote. La maquette v1-2
   s'aligne sur ce fichier, pas l'inverse.
   Si le Regent veut la marque lisible malgre l'exemption : --encre-safou #CF3700
   franchit le seuil avec la teinte conservee. C'est un choix de marque, pas de norme. */
.app__langue{
  margin-left:auto; font-family:var(--f-t); font-size:.78rem; font-weight:600;
  color:var(--ink-soft); border:1px solid var(--line-fort); border-radius:100px; padding:4px 10px;
}

/* ── barre d'onglets ────────────────────────────────────────────────────── */
.onglets{
  position:fixed; bottom:0; left:50%; transform:translateX(-50%);
  width:100%; max-width:var(--app-maxw); display:grid; grid-template-columns:repeat(4,1fr);
  background:var(--surface); border-top:1px solid var(--line-carte); z-index:30;
  padding-bottom:env(safe-area-inset-bottom);
}
.onglet{
  display:flex; flex-direction:column; align-items:center; gap:3px;
  padding:9px 4px 10px; font-family:var(--f-t); font-size:.75rem; font-weight:600;
  color:var(--muted-600); text-decoration:none; border:0; background:none; cursor:pointer;
}
.onglet[aria-current="page"]{color:var(--encre-safou)}
.onglet__pastille{width:18px;height:18px;border-radius:5px;background:currentColor;opacity:.9}

/* ── titres d'écran ─────────────────────────────────────────────────────── */
.ecran__titre{font-size:1.72rem; font-weight:700; margin:22px 0 8px}
.ecran__sous{color:var(--ink-soft); font-size:.95rem}

/* ── cartes ─────────────────────────────────────────────────────────────── */
.carte{
  background:var(--surface); border:1px solid var(--line-carte);
  border-radius:var(--radius); padding:18px; margin-top:16px;
}
.carte__sep{height:1px; background:var(--line); border:0; margin:14px 0}

/* ── ACTIONS — option A ─────────────────────────────────────────────────── */
.btn{
  display:inline-flex; align-items:center; justify-content:center; gap:8px;
  font-family:var(--f-t); font-weight:600; font-size:1rem;
  padding:14px 22px; border-radius:100px; border:0; cursor:pointer;
  transition:background .2s, transform .2s var(--ease), opacity .2s;
}
.btn--bloc{width:100%}
/* PRINCIPALE — option A : encre sur safou. Ne jamais repasser en blanc. */
.btn--principale{background:var(--safou); color:var(--ink)}
.btn--principale:hover:not(:disabled){background:var(--safou-600)}
.btn--secondaire{background:var(--teal-700); color:#fff}          /* libellé blanc sur teal-700 */
.btn--tertiaire{background:transparent; color:var(--ink); border:1.5px solid var(--line-fort)}
.btn:disabled{opacity:.5; cursor:not-allowed; transform:none; --wcag-exempt:composant-inactif}
.btn[data-envoi="1"]{cursor:progress; opacity:1}

/* ── UN CONTRÔLE QUI N'A AUCUN CHEMIN — un ÉTAT, jamais une PANNE ────────────
   ⚠ ZÉRO UTILISATEUR AU 2026-08-26, et c'est dit plutôt que laissé à découvrir.
   Son unique usage — les deux boutons de `app-locker-casiers` — a duré quatre
   heures : m28/m29 ont ouvert le chemin d'INSERT le soir même, et le refus a été
   retiré parce qu'un refus qui a cessé d'être vrai dissuade d'un geste devenu
   possible. La règle est CONSERVÉE : elle encode une distinction que le kit
   n'avait pas (inactif ≠ en panne) et qu'il redemandera au prochain contrôle sans
   chemin. Un composant sans usage se déclare ; il ne se maquille pas en composant
   utilisé, et il ne se supprime pas non plus parce que son premier cas a mûri vite.

   Distincte de `.btn:disabled` seule, et la distinction est le sujet.
   Un `disabled` nu se lit « c'est cassé, réessayez » : l'usager attend, recharge,
   appelle. Il faut qu'il lise « ce n'est pas encore ouvert », et qu'il sache
   QUOI manque. La forme reprend la grammaire que ce kit emploie déjà pour
   « non actionnable / pas de donnée » — TRAIT TIRETÉ (`.case--inconnu`,
   `.piece--bloquee`) — au lieu d'inventer un signe de plus.

   ⚠ `opacity:1` EN DUR, contre `.btn:disabled{opacity:.5}`. Une atténuation
   composite l'encre ET le fond : c'est le défaut mesuré au pixel le 2026-08-25
   sur `.piece--bloquee` (--muted-600 tombé sous le seuil). Ici elle coûterait
   deux fois — la lisibilité, et le sens : un bouton à demi effacé EST le signal
   « panne » qu'on cherche justement à ne pas émettre.

   ⚠ ET L'EXEMPTION EST RETIRÉE, délibérément. `.btn:disabled` déclare
   `--wcag-exempt:composant-inactif` — légitime en général (WCAG 1.4.3 exempte les
   contrôles inactifs), mais une exemption est un ANGLE MORT : la sonde cesse de
   mesurer. Ce bouton-ci n'en a pas besoin, donc il reste MESURÉ. On ne
   dépense une exemption que là où l'on ne peut pas faire autrement. */
.btn--indisponible{
  background:var(--surface); color:var(--muted-600);         /* MESURÉ au pixel, pas
     recopié : la déclaration de `--muted-600` (l.42) porte une annotation qui a survécu à
     son propre hex — elle décrit la valeur PRÉCÉDENTE, et sur la même ligne le chiffre du
     lavis teal, lui, correspond au hex actuel. Un chiffre a été recalculé, l'autre non.
     Signalé à T7, dont la ligne c'est. Le relevé chiffré est au rapport du 2026-08-26,
     daté et signé — il n'est PAS recopié ici : ce serait graver une deuxième fois le
     nombre que ce commentaire reproche à la l.42 d'avoir gravé une première. */
  border:1.5px dashed var(--line-fort); cursor:not-allowed}  /* filet de CONTRÔLE — WCAG 1.4.11 exige 3:1 */
.btn--indisponible:disabled{opacity:1; --wcag-exempt:initial}
.btn--indisponible__note{
  display:block; font-family:var(--f-b); font-weight:400; font-size:.76rem;
  color:var(--muted-600); margin-top:8px; line-height:1.5}
/* PAS d'opacite sur l'etat « envoi en cours ». Mesure du 2026-08-25 :
   `opacity:.7` fait tomber le libellé SOUS le seuil sur le
   bouton principal comme sur le secondaire.
   Ce n'est PAS une violation — le bouton est `disabled` pendant l'envoi, et
   WCAG 1.4.3 exempte les composants inactifs. C'est un défaut de conception au
   PIRE moment : le visiteur vient de cliquer, il attend, et le libellé est la
   seule chose qui lui dise où il en est. Sur une 4G lente, cet instant dure.
   L'attente se signale par le CURSEUR et par le TEXTE, jamais en effaçant le
   texte. Troisième occurrence de la même faute dans ce système (cartes
   inactives, étiquette de taille, ici) : l'opacité est le réflexe qu'on a
   chaque fois qu'on veut « atténuer », et elle détruit toujours la même chose.

   ⚠ `opacity:1` est ÉCRIT, il n'est pas sous-entendu. Le bouton est `disabled`
   pendant l'envoi : sans cette déclaration, `.btn:disabled{opacity:.5}` reprend
   la main et le fondu passe de 0,7 à 0,5 — PIRE qu'avant correctif. Mesuré, et
   trouvé seulement en réobservant l'état d'envoi réel après la correction.
   Retirer une règle fautive ne suffit pas quand une autre attend dessous. */

/* ── ÉTATS ──────────────────────────────────────────────────────────────── */
.etat{
  display:inline-flex; align-items:center; gap:6px;
  font-family:var(--f-t); font-size:.78rem; font-weight:600;
  padding:4px 11px; border-radius:100px;
}
.etat::before{content:''; width:7px; height:7px; border-radius:50%; background:currentColor}
.etat--confirme{background:var(--teal-wash); color:var(--teal-700)}      /* encre appariée à son lavis */
.etat--attente{background:var(--gold-wash); color:var(--encre-warn)}     /* encre d'attente sur son lavis */
.etat--echec{background:var(--rouge-wash); color:var(--encre-err)}       /* encre d'échec sur son lavis */
.etat--non-commence{background:#F1EEE7; color:var(--encre-neutre)}
/* AVANT : `--muted-600`, SOUS le seuil. Exactement la faute déjà corrigée sur
   `.cout__nature` — un token calibré contre le sable (#FEFBF6) posé sur un gris
   plus clair (#F1EEE7). J'avais corrigé le CAS DÉRIVÉ et laissé la CLASSE SOURCE,
   qui sert dans toute l'application. Corriger un symptôme ne ferme pas sa classe. */

/* ── SAISIE — bord au rang CONTRÔLE : un champ doit se voir sans être survolé ───── */
.champ{margin-top:14px}
.champ__label{
  display:block; font-family:var(--f-t); font-size:.82rem; font-weight:600;
  color:var(--ink-soft); margin-bottom:6px;
}
.champ__label .req{color:var(--encre-err)}
.champ input, .champ textarea, .champ select{
  width:100%; font-family:var(--f-b); font-size:1rem; color:var(--ink);
  background:var(--surface); border:1.5px solid var(--line-fort);
  border-radius:var(--radius-sm); padding:12px 14px;
}
.champ textarea{min-height:88px; resize:vertical}
.champ input::placeholder, .champ textarea::placeholder{color:var(--muted-600)}
.champ input:focus-visible, .champ textarea:focus-visible{border-color:var(--teal-700)}
.champ--invalide input, .champ--invalide textarea{border-color:var(--rouge)}

/* min-height:44px — la case mesure 19px, mais c'est le LABEL qui la bascule quand on
   le touche : la cible effective est cette boîte. Sur DEUX lignes elle rendait déjà
   44,66px et passait ; sur UNE seule (app-locker-trouver) elle rendait 24px. Un
   plancher ne déplace rien là où la boîte est déjà assez haute. Mesuré à 390px. */
.consentement{
  display:flex; gap:11px; align-items:flex-start; margin-top:18px;
  min-height:44px;
  font-size:.9rem; color:var(--ink-soft);
}
.consentement input{margin-top:2px; width:19px; height:19px; accent-color:var(--teal-700); flex:0 0 auto}

/* ── VÉRACITÉ (règles R3/R4 de la spec DA) ──────────────────────────────── */
.veracite{
  display:flex; gap:10px; align-items:flex-start;
  background:var(--teal-wash); border:1px solid rgba(10,147,150,.22);
  border-radius:var(--radius-sm); padding:13px 15px; margin-top:16px;
  font-size:.88rem; color:var(--ink-soft);
}
.veracite__titre{font-family:var(--f-t); font-weight:600; color:var(--teal-700); display:block; margin-bottom:2px}
/* R3 — toute valeur d'illustration se déclare DANS LE STYLE, pas en note de bas de page */
.illustration{text-decoration:underline dotted var(--muted-600); text-underline-offset:3px}

/* ── retour de formulaire — trois issues distinctes, jamais confondues ──── */
.retour{display:none; border-radius:var(--radius-sm); padding:14px 16px; margin-top:16px; font-size:.92rem}
.retour.montre{display:block}
.retour--ok{background:var(--teal-wash); color:var(--teal-700); border:1px solid rgba(10,147,150,.28)}
.retour--echec{background:var(--rouge-wash); color:var(--encre-err); border:1px solid rgba(219,36,42,.28)}
.retour--inerte{background:var(--gold-wash); color:var(--encre-warn); border:1px solid rgba(244,162,97,.4)}
.retour b{font-weight:700}

.erreur-champ{display:none; color:var(--encre-err); font-size:.86rem; margin-top:10px}
.erreur-champ.montre{display:block}

/* ── carte vide assumée (L1) — R4 : l'état « aujourd'hui » est de premier rang ── */
.carte-vide{
  position:relative; border:1px dashed var(--line-fort); border-radius:var(--radius);
  background:
    linear-gradient(0deg, rgba(10,147,150,.03), rgba(10,147,150,.03)),
    repeating-linear-gradient(0deg, transparent 0 27px, var(--line) 27px 28px),
    repeating-linear-gradient(90deg, transparent 0 27px, var(--line) 27px 28px);
  min-height:190px; display:flex; flex-direction:column; align-items:center; justify-content:center;
  text-align:center; padding:24px 20px; margin-top:18px;
}
.carte-vide__titre{font-family:var(--f-t); font-weight:600; font-size:1rem; color:var(--ink)}
.carte-vide__sous{font-size:.88rem; color:var(--encre-neutre); margin-top:4px}
/* AVANT : `--muted-600`, SOUS le seuil sur la partie claire du quadrillage —
   et pire sur ses traits. Le fond n'est pas uni : c'est une texture (lavis teal +
   deux trames en --line). Un token calibre sur un fond UNI n'a aucune garantie
   sur un fond TEXTURE, ou le pire pixel decide. Quatrieme occurrence du meme
   defaut aujourd'hui : la classe change, la cause ne change pas. */

@media (prefers-reduced-motion: reduce){
  *{animation:none !important; transition:none !important}
}

/* ════════════════════════════════════════════════════════════════════════════
   COÛT RENDU — la décomposition ligne à ligne, et le total qui REFUSE d'exister

   Adossé à la vue `landed_cost` (m6), jamais à une colonne. Le total y est NULL
   tant que les QUATRE postes obligatoires — usine, fret, douane, dernier_km —
   ne sont pas tous présents, et `manquants` les nomme. La conséquence de dessin
   est directe : quand le total est NULL, **l'écran n'affiche pas de nombre à sa
   place**. Ni zéro, ni le partiel grossi, ni un « à partir de ». Un total qui
   masque un poste manquant EST le prix opaque que ce produit prétend supprimer.
   ══════════════════════════════════════════════════════════════════════════ */
.cout__ligne{display:flex; align-items:flex-start; gap:12px; padding:10px 0}
.cout__ligne + .cout__ligne{border-top:1px solid var(--line)}
.cout__poste{font-family:var(--f-t); font-weight:600; font-size:.94rem; color:var(--ink)}
.cout__source{font-size:.76rem; color:var(--muted-600); margin-top:2px; line-height:1.4}
.cout__montant{margin-left:auto; font-family:var(--f-t); font-weight:700;
  font-size:1rem; white-space:nowrap; color:var(--ink)}
/* Un poste obligatoire non coté : il occupe SA ligne, il ne disparaît pas.
   L'effacer donnerait une liste qui a l'air complète. */
.cout__ligne--a-coter .cout__poste{color:var(--ink-soft)}
.cout__montant--absent{margin-left:auto; font-family:var(--f-t); font-weight:600;
  font-size:.84rem; white-space:nowrap; color:var(--encre-warn)}      /* encre d'attente sur blanc */

.cout__total{margin-top:4px}
.cout__chiffre{font-family:var(--f-t); font-weight:700; font-size:2rem;
  letter-spacing:-.02em; color:var(--ink); display:block}
/* Le total indisponible n'est pas un grand nombre grisé : c'est une PHRASE.
   Un chiffre au même corps, même pâle, se lit comme un montant. */
.cout__chiffre--absent{font-family:var(--f-t); font-weight:600; font-size:1.08rem;
  color:var(--encre-warn); display:block; line-height:1.35}
.cout__partiel{font-family:var(--f-t); font-weight:700; font-size:1.15rem; color:var(--ink-soft)}
.manquants{display:flex; flex-wrap:wrap; gap:6px; margin-top:9px}

/* ════════════════════════════════════════════════════════════════════════════
   CASIERS — quatre états, dont « inconnu », qui est le plus important

   Adossé à `cases` et à la vue `locker_occupancy` (m6). `casiers_reels` y vaut
   NULL quand aucun casier n'est enregistré, et `taux_occupation` aussi — «  jamais
   0, qui se lirait casier vide au lieu de casiers inconnus  » (commentaire de la
   migration). Une case sans ligne en base n'est donc PAS libre : elle est
   inconnue, et elle le dit. Sans ce quatrième état, une grille vierge annonce un
   parc entièrement disponible à un hôte qui n'a rien installé.

   Les quatre états se distinguent par le REMPLISSAGE **et** par la FORME du bord
   (plein contre tireté) : la grille reste lisible sans percevoir les couleurs.
   ══════════════════════════════════════════════════════════════════════════ */
.casiers{display:grid; grid-template-columns:repeat(4, 1fr); gap:7px}
.case{aspect-ratio:1; border-radius:10px; display:grid; place-items:center; gap:1px;
  font-family:var(--f-t); font-size:.75rem; font-weight:600; text-align:center;
  border:1px solid var(--line-carte); background:var(--surface); color:var(--muted-600)}
.case__taille{font-size:.75rem; font-weight:600}
/* PAS d'opacite ici. Mesure du 2026-08-25 : `opacity:.85` faisait tomber la
   taille SOUS le seuil sur un casier libre comme sur un casier occupe. Une
   opacite affaiblit le texte AUTANT que le fond — elle ne nuance pas un
   contraste, elle le detruit. Le second plan se marque par le CORPS, pas par la
   transparence. Meme faute que celle deja fermee sur les cartes inactives :
   elle revient partout ou l'on veut « attenuer » quelque chose. */
.case--libre{background:var(--surface); border-color:var(--line-fort); color:var(--muted-600)}   /* encre secondaire sur blanc */
.case--occupe{background:var(--teal-700); border-color:var(--teal-700); color:#fff}              /* libellé blanc sur teal-700 */
.case--hors-service{background:var(--gold); border-color:var(--gold); color:var(--ink)}          /* encre sombre sur or */
.case--inconnu{background:var(--sable); border:1.5px dashed var(--line-fort); color:var(--muted-600)} /* encre secondaire sur sable */

.legende{display:flex; flex-wrap:wrap; gap:14px; margin-top:12px; font-size:.78rem; color:var(--ink-soft)}
.legende span{display:inline-flex; align-items:center; gap:6px}
.legende i{width:12px; height:12px; border-radius:3px; flex:none; border:1px solid var(--line-carte)}
.legende .l-libre{background:var(--surface); border-color:var(--line-fort)}
.legende .l-occupe{background:var(--teal-700); border-color:var(--teal-700)}
.legende .l-hs{background:var(--gold); border-color:var(--gold)}
.legende .l-inconnu{background:var(--sable); border:1.5px dashed var(--line-fort)}

/* Une mesure qui n'a pas eu lieu — ni « 0 », ni « — » muet : le mot, et sa cause. */
.indispo{font-family:var(--f-t); font-weight:600; color:var(--encre-warn)}
.indispo__cause{display:block; font-family:var(--f-b); font-weight:400; font-size:.78rem;
  color:var(--muted-600); margin-top:2px; line-height:1.4}
/* La déclaration de l'hôte n'est pas une mesure : elle s'affiche comme telle. */
.declare{font-family:var(--f-t); font-weight:700; color:var(--ink-soft)}
.declare__note{font-size:.76rem; font-weight:400; font-family:var(--f-b); color:var(--muted-600)}

/* ── `nature` : devis ou réalisé ─────────────────────────────────────────────
   SEUL ajout de T2 à ce chapitre : tout le reste du vocabulaire de coût existait
   déjà ci-dessus et est réemployé tel quel (.cout__poste, .cout__montant,
   .cout__chiffre--absent, .manquants…). Dupliquer ces classes les aurait écrasées
   en silence — leurs contrastes sont mesurés, pas choisis.

   T7 le demande explicitement dans la spec v1-1 : « la colonne `nature` ∈
   quoted | actual permet de distinguer un devis d'un réalisé — l'écran devra la
   rendre visible, il ne le fait pas encore. » Un devis affiché comme un réalisé
   est la même faute que le total opaque, un cran plus bas. */
.cout__nature{font-family:var(--f-t); font-size:.75rem; font-weight:600; text-transform:uppercase;
  letter-spacing:.06em; padding:2px 7px; border-radius:100px; margin-left:6px;
  background:var(--gold-wash); color:var(--encre-warn)}
/* « DEVIS » — une RÉSERVE plutôt qu'un neutre — même lavis que
   `.etat--attente`, parce qu'un montant au devis est exactement cela.
   ⚠ La valeur précédente annonçait un ratio qu'elle ne rendait pas :
   --muted-600 a été calibré contre le sable (#FEFBF6), il était posé sur un
   gris plus clair (#F1EEE7). Un seuil vérifié sur UN fond ne vaut pas sur un
   autre — c'est la faute la plus discrète de tout ce système, parce qu'elle
   laisse un chiffre juste à côté d'une valeur fausse. */
.cout__nature--actual{background:var(--teal-wash); color:var(--teal-700)}  /* réalisé — encre appariée à son lavis */

/* ════════════════════════════════════════════════════════════════════════════
   PIÈCES JUSTIFICATIVES — le champ de fichier, et ce qu'il promet

   Un champ de fichier PROMET un envoi. C'est sa seule sémantique, et le visiteur
   ne lit pas la note en dessous : il voit un bouton « Choisir », il clique, il
   croit avoir envoyé. Tant qu'aucun dépôt n'existe (SWK_DEPOT_BASE vide), la
   promesse est fausse — avec une pièce d'identité et un RIB dedans.

   D'où la grammaire ci-dessous : la pièce a QUATRE états visibles, et celui qui
   compte est `--retenue` — le fichier est choisi, vérifié SUR L'APPAREIL, et
   n'est PAS parti. Il ne se déguise pas en `--transmise` : deux classes
   distinctes, deux couleurs distinctes, deux mots distincts.
   ══════════════════════════════════════════════════════════════════════════ */
.piece{border:1px solid var(--line-carte); border-radius:var(--radius-sm);
  padding:12px 14px; background:var(--surface)}
.piece + .piece{margin-top:9px}
.piece__tete{display:flex; align-items:flex-start; gap:10px}
.piece__nom{font-family:var(--f-t); font-weight:600; font-size:.94rem; color:var(--ink)}
.piece__pourquoi{font-size:.79rem; color:var(--muted-600); margin-top:2px; line-height:1.45}
.piece__tete .etat{margin-left:auto; flex:none}
.piece__champ{margin-top:10px}
/* min-height:44px — mesuré 43,0px : il ratait la borne d'UN pixel. La hauteur d'un champ
   de fichier est décidée par le navigateur ; seul un plancher explicite la tient. */
.piece__champ input[type=file]{width:100%; min-height:44px; font-family:var(--f-b); font-size:.86rem; color:var(--ink);
  background:var(--sable); border:1.5px dashed var(--line-fort);
  border-radius:var(--radius-sm); padding:10px 12px}
.piece__champ input[type=file]:focus-visible{border-style:solid; border-color:var(--teal-700)}
.piece__meta{font-family:var(--f-t); font-size:.78rem; color:var(--ink-soft); margin-top:7px}
.piece__refus{font-size:.8rem; color:var(--encre-err); margin-top:7px; line-height:1.45}
.piece--retenue{border-color:var(--teal-700)}
.piece--refusee{border-color:var(--rouge)}
/* « Retenue sur l'appareil » n'est PAS un succès : lavis d'attente, pas de teal.
   Le vert dirait « c'est fait » à quelqu'un qui n'a rien envoyé. */
.etat--retenue{background:var(--gold-wash); color:var(--encre-warn)}
.etat--transmise{background:var(--teal-wash); color:var(--teal-700)}

/* Avis légal : il n'est pas décoratif, il conditionne la collecte. */
.avis{border:1px solid var(--line-carte); border-left:3px solid var(--rouge);
  background:var(--rouge-wash); border-radius:0 var(--radius-sm) var(--radius-sm) 0;
  padding:11px 14px; margin-top:12px}
.avis__titre{font-family:var(--f-t); font-weight:600; font-size:.85rem;
  color:var(--encre-err); display:block; margin-bottom:3px}
.avis__corps{font-size:.8rem; color:var(--ink-soft); line-height:1.5}

/* Étiquette obligatoire sur tout montant de rémunération partenaire — ordre
   du Régent 2026-08-25. Un chiffre de rémunération sans cette réserve est la
   seule sur-promesse de la façade qui se conteste devant un juge et pas devant
   un client : ne JAMAIS retirer ce style sans retirer les montants avec. */
.reserve-modele{display:block;margin-top:6px;font-size:.82rem;font-style:italic;color:var(--muted-600)}

/* ── Une pièce EN REFUS — ni « à fournir », ni « facultative » ───────────────
   L'attestation de police est fermée par décision (RGPD art. 10, cadre juridique
   non posé). Elle reste VISIBLE : masquer la pièce masquerait aussi la raison, et
   le candidat doit savoir ce qui n'est pas demandé, et pourquoi. Aucun champ de
   fichier n'est rendu — un champ désactivé se réactive d'un clic dans
   l'inspecteur, un champ absent n'a rien à réactiver. */
.piece--bloquee{border-style:dashed; background:var(--sable)}
/* ⚠ PAS d'`opacity` ici, et c'est une correction, pas une omission. La règle
   portait `opacity:.92` : elle composite l'encre ET le fond vers le ton de la
   page, et faisait passer `--muted-600` SOUS son seuil — mesuré au pixel peint,
   réception du 2026-08-25 ; l'écart chiffré est au rapport de ce jour. La feuille
   annonçait donc un seuil juste à côté d'une valeur fausse : le contraste écrit
   passait, le contraste rendu non. Une atténuation ne se paie jamais en
   lisibilité — surtout sur le seul écran qui explique un REFUS de collecte.
   Le signal « cette pièce n'est pas actionnable » ne perd rien : bordure
   pointillée, fond sable au lieu du blanc des autres pièces, nom en encre
   secondaire, phrase explicite, état « Non collectée », et AUCUN champ de
   fichier rendu. Six marques ; l'opacité était la septième, et la seule
   qui coûtait. */
.piece--bloquee .piece__nom{color:var(--muted-600)}

/* ═══════════════════════════════════════════════════════════════════════════
   L2 · LE RETRAIT — le seul écran sombre des deux applications (T2, 2026-08-26)

   Bâti sur la maquette T7 (`docs/2026-08-25_SWK_SPC_maquettes-plateforme-lockers_v1-2.html`,
   bloc « L2 · Le retrait ») et sur AUCUNE couleur hors kit : les quatre fonds de
   nuit et les trois rangs d'encre sombre sont ceux que la DÉCLARATION CANONIQUE
   DES ENCRES borne plus haut. Rien n'est dérivé ici.

   ⚠ POURQUOI CES RÈGLES SONT ICI ET PAS DANS UN `<style>` DE LA PAGE. Aucun des
   cinq écrans `app-*` ne porte de style local — mais la raison qui compte n'est
   pas la coutume : une règle écrite dans une page échappe aux portes qui lisent
   le kit (`unicite-tokens`, `domaine-encres`). C'est exactement la dette que la
   déclaration nomme plus haut — « un littéral n'a pas de domaine ». Y ajouter un
   écran entier de nuit aurait doublé cette dette au lieu de la tenir.

   ── CE QUE LA MAQUETTE POSE ET QUE LE SCHÉMA INTERDIT ───────────────────────
   Elle dessine SIX CHIFFRES AFFICHÉS. Le clair ne peut pas être affiché : m23
   écrit « LE CLAIR SORT ICI ET NULLE PART AILLEURS. Jamais journalisé, jamais en
   base » — la base ne stocke qu'un bcrypt. Le code arrive au destinataire par SMS.
   Le très grand corps est donc conservé POUR LA SAISIE, pas pour l'affichage :
   l'intention de T7 (« se lit à bras tendu ») est tenue, la donnée n'est pas
   fabriquée. Cas remonté à T7 avec cette note.

   ── L'ÉTAT D'ÉCHEC PASSE PAR UNE PASTILLE PLEINE, JAMAIS PAR DE L'ENCRE ─────
   `--rouge` EN ENCRE sur la nuit ÉCHOUE le seuil du texte courant ; posé en fond
   sous du blanc, il le tient. L'état d'échec passe donc par une pastille pleine —
   ce n'est pas un contournement, c'est l'idiome que la maquette emploie déjà
   (`chip solid-warn`). La famille sombre n'a pas d'encre d'échec : dette nommée
   pour T7, non fermée ici — inventer une nuance serait créer une teinte hors kit.

   ⚠ CE BLOC PORTAIT UNE TABLE DE SEPT RATIOS, retirée le 2026-08-26 sur ordre du
   Régent. Chacun de ces chiffres existait DÉJÀ sur la règle qui le peint, quelques
   lignes plus bas — donc deux écritures d'une même mesure, dont une seule bouge
   quand la valeur bouge. C'est le mode d'échec que ce fichier documente ailleurs.
   Les mesures vivent sur la règle, ou dans le rapport daté. Pas en prose. */
body.nuit{background:var(--midnight)}   /* hors coquille : sur large écran, le sable
   encadrerait de blanc un écran conçu pour la nuit — l'éblouissement revient par le bord. */
.app--nuit{background:var(--midnight)}
/* L'anneau de focus global est `--teal-700`, calibré sur fond CLAIR. Sur la nuit il PASSE
   le seuil non-textuel de 1.4.11, mais de peu — et l'écrire autrement serait se donner
   raison à bon compte : ce n'est pas un défaut. `--safou` y est franchement au-dessus, sur
   le seul écran qu'on utilise debout, de nuit, au clavier d'une borne. On remplace donc une
   conformité juste par une lisibilité franche — et on qualifie l'écart au bon cran.
   Portée limitée à la coquille de nuit : les autres écrans gardent le teal. */
.app--nuit :focus-visible{outline-color:var(--safou)}
.app--nuit .app__barre-haut{background:var(--midnight-soft); border-bottom-color:var(--midnight-soft-carte)}
.app--nuit .app__titre-barre{color:#fff}
.app--nuit .app__retour{border-color:var(--muted-sombre); color:#fff}   /* contrôle — WCAG 1.4.11 exige 3:1 */
.app--nuit .app__retour:hover{background:var(--midnight-soft-carte)}
.app--nuit .ecran__sous{color:var(--muted-sombre)}

.retrait{display:flex; flex-direction:column; gap:18px; text-align:center; padding-top:20px}
.retrait__lieu{font-size:.85rem; color:var(--muted-sombre)}
.retrait__casier{font-family:var(--f-t); font-weight:600; font-size:1.2rem; color:#fff; margin-top:4px}

/* LA SAISIE — très grand corps, parce qu'elle se fait debout, dehors, de nuit,
   à bout de bras. Six champs d'UN caractère : un seul champ de six chiffres
   oblige à viser un curseur sur une borne mal éclairée. `inputmode=numeric`
   fait monter le pavé chiffré, pas le clavier complet. */
.code-saisie{display:flex; justify-content:center; gap:8px}
.code-saisie input{
  width:44px; height:60px; text-align:center; font-family:var(--f-t);
  font-size:1.9rem; font-weight:700; color:#fff;                    /* blanc sur nuit */
  background:var(--midnight-soft-carte); border:1.5px solid var(--muted-sombre);
  border-radius:var(--radius-sm); caret-color:var(--safou); padding:0}
.code-saisie input:focus-visible{outline:2px solid var(--safou); outline-offset:2px; border-color:var(--safou)}
.code-saisie input::placeholder{color:var(--muted-sombre)}
.code-saisie--fige input{opacity:1; background:var(--midnight); border-style:dashed}

/* ⚠ BORD DE CARTE SOMBRE — DETTE NOMMÉE, PAS FERMÉE. La famille claire a trois rangs de
   filet — décoratif, bord de carte, contrôle — déclarés et mesurés à leur ligne (l.147) ;
   la famille SOMBRE n'en a AUCUN. `--midnight-carte` posé en bord sur `--midnight` tombe au
   rang DÉCORATIF, un rang sous ce que le kit prescrit pour le bord d'une carte non
   interactive. Le fermer demande un `--line-carte-sombre` déclaré et mesuré : c'est une
   VALEUR, donc T7. Inventer ici une nuance intermédiaire serait fabriquer une teinte hors
   kit pour masquer un manque — précisément ce que la règle interdit. */
.retrait__carte{
  background:var(--midnight-soft-carte); border:1px solid var(--midnight-carte);
  border-radius:var(--radius); padding:14px 16px; text-align:left}
.retrait__ligne{display:flex; align-items:center; gap:10px}
.retrait__lbl{font-size:.85rem; color:var(--muted-sombre)}
.retrait__csq{font-size:.8rem; color:var(--muted-sombre); line-height:1.5; margin-top:6px}

/* PASTILLES PLEINES — le seul chemin admis sur la nuit (cf. en-tête). */
.pastille{margin-left:auto; display:inline-flex; align-items:center; gap:6px;
  font-family:var(--f-t); font-weight:600; font-size:.8rem;
  padding:5px 11px; border-radius:999px}
.pastille--attention{background:var(--gold); color:var(--ink)}      /* encre sombre sur or */
.pastille--absente{background:var(--midnight-carte); color:var(--ink-soft-sombre); /* rang 2 sur fond de carte sombre */
  border:1px dashed var(--muted-sombre)}

/* LE VERDICT — trois issues, trois fonds pleins, aucune encre de couleur sur nuit. */
.verdict{display:none; border-radius:var(--radius-sm); padding:13px 15px;
  text-align:left; font-size:.9rem; line-height:1.5}
.verdict.montre{display:block}
.verdict b{font-family:var(--f-t); font-weight:700; display:block; margin-bottom:3px}
.verdict--remis{background:var(--teal-700); color:#fff}             /* libellé blanc sur teal-700 */
.verdict--refus{background:var(--rouge); color:#fff}                /* blanc sur rouge */
.verdict--inerte{background:var(--gold); color:var(--ink)}          /* encre sombre sur or — rien n'est parti */
.verdict__issue{display:block; margin-top:8px; font-size:.84rem; opacity:1}
