/* =============================================================================
   PLATEFORMA REAL ESTATE — feuille de style applicative
   Extraite de design/design-system.html. Les tokens sont la SEULE source de
   vérité : toute couleur écrite en dur ailleurs est un bug.
   ========================================================================== */
:root {
  /* LES DEUX MODES SONT DÉCLARÉS ICI, et pas seulement pour la forme :
     `color-scheme` est ce qui fait basculer les contrôles de formulaire, les
     barres de défilement et le sélecteur de date du navigateur. Sans lui, un
     `<input type="date">` resterait un rectangle blanc au milieu d'une page
     sombre — et c'est le champ le plus employé de la plateforme. */
  color-scheme: light dark;

  /* LES TROIS NIVEAUX D'ENCRE, RÉÉQUILIBRÉS — et ce n'est pas une question de
     goût. Le premier relevé de contraste jamais fait sur ce projet, en
     RE-Phase 11, a montré que `--ink-faint` valait 2,44:1 sur blanc là où la
     WCAG en demande 4,5 pour du texte de moins de 18,66 px. Il portait `.muted`,
     `.lab`, `.sub`, `.tag` et `h4` — le texte secondaire de TOUS les écrans, et
     cette valeur datait du lot 1.

     Relever le seul `--ink-faint` l'aurait amené au niveau de `--ink-soft` et
     aurait écrasé deux des trois niveaux : les trois ont donc bougé ensemble.
     Mesuré sur blanc — 10,14 / 6,50 / 5,19, contre 7,66 / 4,53 / 2,44 avant.

     LA HIÉRARCHIE NE TIENT PLUS À LA SEULE LUMINANCE, et elle n'y tenait déjà
     pas : sous un plancher de 4,5:1 sur du blanc, trois crans de gris ne
     peuvent plus s'écarter beaucoup. Ce qui distingue les niveaux est aussi la
     TAILLE et la FAMILLE — `.muted` à 13 px, `h4` en petites capitales, `.tag`
     en chasse fixe à 11 px. La couleur accompagne, elle ne porte plus seule. */
  --ink:#2C4457; --ink-soft:#48607A; --ink-faint:#587080;
  --paper:#FDFBF7; --paper-raised:#FFFFFF;
  --fill:#EDE3D5; --fill-soft:#F6F0E7; --line:#E2D9CC;
  /* LE TRAIT N'EST PAS L'ENCRE, et cette distinction est ce qui rend le thème
     sombre possible. `--ink` servait à DEUX choses : la couleur du texte et le
     trait des bordures, sur une vingtaine de déclarations. Inverser l'encre
     aurait donc peint des bordures presque blanches de 2 px sur chaque carte,
     chaque bouton et chaque tableau.

     `--stroke` porte désormais le trait STRUCTUREL — cartes, boutons, filets,
     barres, panneaux. `--ink` reste sur ce qui doit CRIER : les anneaux de
     focus, le jour courant, l'onglet actif, la barre de progression.

     En clair les deux valent la même chose : ce partage ne change rien à
     l'écran, et c'est précisément ce qui permet de le vérifier. */
  --stroke: var(--ink);
  /* LE SEUL TEXTE QUE LA FEUILLE NE COLORAIT PAS. Sans règle `::placeholder`,
     le navigateur applique son propre gris — #757575 — calibré pour un champ
     CLAIR. Sur un champ sombre il tombe à 3,49:1, et surtout il ne suit aucun
     thème : c'était la seule couleur de texte de l'application hors des tokens.

     C'est l'encre ATTÉNUÉE, dans les deux thèmes : une valeur à part aurait été
     une quatrième couleur de gris à tenir à jour. Mesuré 5,19:1 en clair,
     5,32:1 en sombre — et le texte SAISI reste bien plus soutenu (10,14 et
     13,86), sans quoi on ne distinguerait pas un champ rempli d'un champ qui
     attend. */
  --placeholder: var(--ink-faint);
  --critical:#B4453A; --critical-bg:#FBEDEA;
  /* L'ambre et le vert ont bougé pour la même raison, et eux seuls : mesurés
     sur LEUR fond teinté, ils donnaient 3,07 et 4,42. Le rouge et le bleu
     passaient déjà. */
  --warn:#8A6013;     --warn-bg:#FBF3E4;
  --ok:#3C674D;       --ok-bg:#EDF4EF;
  --info:#3E6F91;     --info-bg:#EAF2F7;
  --font-body: ui-sans-serif,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",sans-serif;
  --font-mono: ui-monospace,"SF Mono",Menlo,Consolas,monospace;
  --sp-1:4px; --sp-2:8px; --sp-3:12px; --sp-4:16px; --sp-5:24px; --sp-6:32px; --sp-7:48px;
  --shadow: 0 1px 2px rgba(53,86,110,.06), 0 4px 14px rgba(53,86,110,.05);

  /* ------------------------------------------------------- LIQUID GLASS
     LA TEINTE VIENT DU PAPIER DE L'APPLICATION, et non d'une valeur écrite en
     dur. C'est ce que demande la spécification — « la couleur du matériau doit
     provenir principalement du contenu situé derrière lui » — et c'est aussi ce
     qui fait que le jour où la palette changera, le verre suivra sans qu'on y
     pense. Les composantes sont séparées parce que l'alpha se règle à part :
     `rgb(var(--lg-teinte) / var(--lg-opacite))`.

     ELLES SUIVENT LE THÈME DE L'APPLICATION, jamais le réglage système à part.
     C'est pourquoi elles sont redéfinies DANS le bloc sombre plus bas, avec le
     reste de la palette, et jamais dans un bloc à elles : un verre sombre sur
     une page claire serait un contresens, et deux blocs finiraient par se
     désaccorder. */
  --lg-teinte: 253 251 247;      /* --paper, en composantes */
  --lg-opacite: .82;             /* variante Regular : le texte prime */
  --lg-opacite-clear: .30;       /* variante Clear : commandes courtes */
  --lg-flou: 22px;
  --lg-saturation: 175%;
  /* Le contour lumineux et l'ombre de séparation. Deux choses distinctes : le
     premier détache le bord du matériau, la seconde le détache du contenu. */
  --lg-liseré: rgba(255,255,255,.62);
  --lg-ombre: 0 10px 34px rgba(53,86,110,.20);
  --lg-voile: rgba(53,86,110,.38);
  --lg-duree: .18s;
}
*{box-sizing:border-box}

/* =============================================================================
   LE MATÉRIAU LIQUID GLASS
   =============================================================================
   OÙ IL VA, ET SURTOUT OÙ IL NE VA PAS. Le matériau est réservé aux éléments
   FONCTIONNELS qui flottent AU-DESSUS du contenu : menus, panneaux temporaires,
   commandes détachées. Jamais un fond de page, jamais une carte de contenu,
   jamais un tableau. La règle n'est pas esthétique — c'est ce qui garantit que
   les montants et les dates restent lus sur un fond plein.

   DANS CETTE PLATEFORME, CELA FAIT DEUX ENDROITS, ET DEUX SEULEMENT :

     · la fenêtre de détail (`.fen`), qui flotte réellement sur la page ;
     · le menu de navigation replié sous 900 px, et le bouton qui l'ouvre.

   LA BARRE LATÉRALE DE BUREAU N'EN REÇOIT PAS, et c'est une décision, pas un
   oubli. `.shell` est une grille à deux colonnes : le contenu ne passe PAS
   derrière l'`aside`, il est à côté. Un flou d'arrière-plan n'y montrerait que
   la couleur du papier — c'est-à-dire rien. La spécification demande que le
   matériau « laisse apparaître partiellement le contenu situé derrière lui » et
   qu'on « évite les effets décoratifs excessifs » : sur cette colonne, le verre
   serait exactement l'un sans l'autre.

   DEUX VARIANTES, ET LEUR PARTAGE :

     `.lg`        Regular — flou et contraste renforcés. Tout ce qui porte du
                  TEXTE. C'est le défaut, et c'est presque toujours le bon.
     `.lg-clear`  Clear — très transparent. Réservé à une commande COURTE posée
                  sur une image. La plateforme n'a aujourd'hui aucun écran dont
                  le fond soit une photo : la classe existe pour le jour où un
                  aperçu de document en aura un, et elle porte son propre voile
                  sombre, sans quoi la lisibilité dépendrait de l'image.

   TROIS REPLIS, ET ILS SONT TOUS OBLIGATOIRES. Transparence réduite, contraste
   renforcé, et navigateur sans `backdrop-filter` : dans les trois cas le
   matériau devient OPAQUE de lui-même. Un effet qui se dégrade en illisible est
   pire qu'un effet absent. */
.lg, .lg-clear {
  background: rgb(var(--lg-teinte) / var(--lg-opacite));
  -webkit-backdrop-filter: blur(var(--lg-flou)) saturate(var(--lg-saturation));
  backdrop-filter: blur(var(--lg-flou)) saturate(var(--lg-saturation));
  /* Le liseré est une ombre INTERNE et non une bordure : il se pose sur le bord
     déjà dessiné par le trait du projet, sans en occuper l'épaisseur. */
  box-shadow: inset 0 1px 0 var(--lg-liseré), var(--lg-ombre);
  transition: background-color var(--lg-duree) ease,
              box-shadow var(--lg-duree) ease,
              transform var(--lg-duree) ease;
}
.lg-clear {
  background: rgb(var(--lg-teinte) / var(--lg-opacite-clear));
  --lg-flou: 14px;
}

/* LE REPLI DU NAVIGATEUR. `@supports not` couvre les moteurs sans
   `backdrop-filter` : sans cette règle, le fond translucide resterait, et l'on
   lirait le texte à travers la page. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .lg, .lg-clear { background: var(--paper-raised); }
}

/* LES DEUX REPLIS D'ACCESSIBILITÉ. « Transparence réduite » est un réglage
   système explicite : on ne le contourne pas, on l'applique. « Contraste
   renforcé » va plus loin et rend aussi le trait au trait plein. */
@media (prefers-reduced-transparency: reduce) {
  .lg, .lg-clear {
    background: var(--paper-raised);
    -webkit-backdrop-filter: none; backdrop-filter: none;
  }
}
@media (prefers-contrast: more) {
  .lg, .lg-clear {
    background: var(--paper-raised);
    -webkit-backdrop-filter: none; backdrop-filter: none;
    box-shadow: var(--shadow);
  }
}

/* LE MOUVEMENT, ET SA SUPPRESSION. La règle porte sur TOUT le document et non
   sur le seul matériau : la plateforme n'avait aucune prise en charge de
   « Réduire les animations », et `html{scroll-behavior:smooth}` en est une —
   c'est même celle qui gêne le plus, puisqu'elle s'applique à chaque ancre.
   `!important` est ici légitime : on annule des déclarations d'auteur qu'on ne
   connaît pas toutes. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}

/* =============================================================================
   LE THÈME SOMBRE
   =============================================================================
   IL SUIT LE RÉGLAGE DU SYSTÈME, et rien d'autre. Pas de bouton, pas de réglage
   en base : c'est une préférence d'affichage, pas une règle métier, et
   l'utilisateur l'a déjà exprimée une fois pour toutes dans son système
   d'exploitation. Lui redemander serait un troisième endroit où la même
   question se pose.

   IL NE REDÉFINIT QUE DES TOKENS. Pas une règle, pas un sélecteur : c'est ce
   que promettait l'en-tête de ce fichier — « les tokens sont la SEULE source de
   vérité » — et ce bloc est l'épreuve de cette promesse. Il n'a été rendu
   possible que par la scission de `--stroke` juste au-dessus.

   LE CARACTÈRE EST GARDÉ, PAS INVERSÉ. Le thème clair est un papier chaud et
   une encre bleue ; le sombre est une ardoise bleue, pas un noir pur — un noir
   absolu sous un trait esquissé donne un rendu de terminal, et cet outil sert à
   lire des contrats. Les fonds sémantiques restent teintés de leur couleur
   (le rouge sur un brun sombre, le vert sur un vert sombre) : c'est ce qui fait
   qu'une pastille se reconnaît sans lire son texte. */
@media (prefers-color-scheme: dark) {
  :root {
    --ink:#E9EFF4; --ink-soft:#BDCCD8; --ink-faint:#8497A5;
    --paper:#121820; --paper-raised:#1A222B;
    --fill:#2C3742; --fill-soft:#222B35; --line:#313D49;
    /* Le trait ne suit PAS l'encre ici : à #E9EFF4 il ferait des bordures
       presque blanches. C'est toute la raison d'être de ce token.

       ET IL NE SE CHOISIT PAS À L'ŒIL. Le premier essai, #4A5A69, donnait un
       trait plaisant et un contraste de 2,26:1 avec le fond des cartes —
       au-dessous des 3:1 que la WCAG demande aux CONTOURS DE COMPOSANTS, qui ne
       sont pas du texte et ont leur propre seuil. Une carte dont on ne
       distingue pas le bord n'est plus une carte. Mesuré : 3,19:1 sur une
       carte, 3,54:1 sur le papier. */
    --stroke:#5D7185;
    --critical:#F08C80; --critical-bg:#3B2320;
    --warn:#E3B366;     --warn-bg:#382C19;
    --ok:#82C69C;       --ok-bg:#1E3228;
    --info:#84B9DB;     --info-bg:#1C2D39;
    /* Une ombre colorée à l'encre ne se voit pas sur un fond sombre : elle
       repasse au noir, et gagne en opacité pour rester perceptible. */
    --shadow: 0 1px 2px rgba(0,0,0,.45), 0 4px 14px rgba(0,0,0,.35);

    /* Le matériau suit le papier, comme en clair. La teinte est celle de
       `--paper-raised`, et le liseré passe de .62 à .14 : un contour blanc à
       62 % sur une ardoise ne serait plus un liseré mais un trait. */
    --lg-teinte: 26 34 43;
    --lg-liseré: rgba(255,255,255,.14);
    --lg-ombre: 0 10px 34px rgba(0,0,0,.48);
    --lg-voile: rgba(6,10,14,.58);
  }
}

html{scroll-behavior:smooth}
body{margin:0;background:var(--paper);color:var(--ink-soft);
  font-family:var(--font-body);font-size:15px;line-height:1.6;-webkit-font-smoothing:antialiased}
h1,h2,h3,h4{color:var(--ink);margin:0;font-weight:650;letter-spacing:-.01em}
h1{font-size:29px} h2{font-size:21px} h3{font-size:16px}
h4{font-size:12px;text-transform:uppercase;letter-spacing:.07em;color:var(--ink-faint);font-weight:600}
a{color:var(--info);text-decoration:none} a:hover{text-decoration:underline}
code,.mono{font-family:var(--font-mono);font-size:.86em}
.tag{font-family:var(--font-mono);font-size:11px;color:var(--ink-faint)}
.muted{color:var(--ink-faint);font-size:13px}
.sr-only{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0)}

/* ---------------------------------------------------------------- structure */
/* minmax(0,1fr) et non 1fr : la taille minimale automatique d'une piste 1fr est
   min-content. Avec 1fr, un tableau large — les en-têtes sont en nowrap —
   empêche la colonne de se réduire, élargit la page entière et fait défiler le
   document horizontalement, barre latérale comprise. minmax(0,1fr) autorise la
   colonne à rétrécir, et les conteneurs .scroll-x prennent le relais. */
.shell{display:grid;grid-template-columns:232px minmax(0,1fr);min-height:100vh}
/* Et minmax(0,1fr) ICI AUSSI. La règle ci-dessus était juste, la requête média
   la défaisait : sous 900 px la piste redevenait 1fr, donc min-content, et
   /quittancement défilait de 477 px à 375 px de large — barre latérale comprise,
   exactement le défaut que le commentaire ci-dessus décrit. Il n'était visible
   que sur mobile, où rien n'avait été mesuré hors des écrans de l'assistant. */
@media (max-width:900px){.shell{grid-template-columns:minmax(0,1fr)}}
aside{background:var(--paper-raised);border-right:2px solid var(--stroke);padding:var(--sp-5) var(--sp-4)}
@media (max-width:900px){aside{border-right:none;border-bottom:2px solid var(--stroke)}}

/* LE MENU REPLIABLE SOUS 900 PX. Vingt-quatre entrées font plus de 2 000 px de
   bandeau supérieur : le rendu était correct — mesuré, sans débordement — et il
   fallait défiler deux écrans avant d'atteindre le contenu de la page.

   Aucune JavaScript : `script-src 'self'` interdit un gestionnaire en ligne. La
   case à cocher reste FOCALISABLE (`.sr-only` la déplace, ne la supprime pas),
   sans quoi le menu deviendrait inatteignable au clavier. */
.menu-bouton{display:none}
@media (max-width:900px){
  /* Le fond ne se déclare PAS ici : il vient de `.lg`, posée sur ce bouton dans
     base.html. Le redéclarer gagnerait par l'ordre du fichier et le matériau ne
     se verrait pas — la règle serait juste, et sans effet. */
  .menu-bouton{display:inline-flex;align-self:flex-start;align-items:center;
    gap:10px;padding:10px 14px;
    border:1.5px solid var(--stroke);border-radius:11px 13px 12px 14px;
    font-size:14px;font-weight:600;color:var(--ink);cursor:pointer}
  /* « réagir doucement au toucher » : une variation d'échelle courte, et rien
     de plus. Elle disparaît sous « Réduire les animations ». */
  .menu-bouton:active{transform:scale(.97)}
  .menu-bascule:focus-visible+.menu-bouton{outline:2px solid var(--ink);
    outline-offset:2px}
  aside nav{display:none}

  /* LE MENU OUVERT FLOTTE AU-DESSUS DU CONTENU, et c'est ce qui donne un sens
     au matériau : il y a enfin quelque chose derrière lui. Il était jusqu'ici
     DANS le flux, et vingt-quatre entrées repoussaient la page de deux écrans
     vers le bas — c'est le défaut que RE-Phase 10 avait corrigé en le repliant,
     et l'ouvrir le ramenait tel quel.

     `max-height` et non une hauteur libre : sur un téléphone en paysage, un
     menu de vingt-quatre entrées dépasse l'écran, et un panneau qu'on ne peut
     pas faire défiler est un panneau dont les dernières entrées n'existent
     pas. */
  .menu-bascule:checked~nav{
    /* `top:100%` — le panneau tombe sous LA BARRE, pas sous le bouton, et c'est
       le motif standard d'une barre d'application : la barre porte la marque,
       la commande et l'identité, le panneau se déroule dessous.

       CE N'EST PAS LE PREMIER CHOIX ESSAYÉ, et l'écart mérite d'être noté :
       retirer `top` pour laisser la boîte à sa position STATIQUE — juste sous
       le bouton — ne marche pas ici. `aside` est un conteneur FLEX, et la
       position statique d'un enfant absolu y est le coin du bloc conteneur, pas
       l'endroit où il serait tombé dans le flux : le panneau remontait sur la
       marque. L'ancrer au bouton demanderait de l'envelopper dans un conteneur
       positionné, ce qui casserait `aside nav{flex:1 1 auto;overflow-y:auto}`
       au-delà de 900 px — un défaut réel pour un gain d'esthétique. */
    display:block;position:absolute;z-index:30;
    left:var(--sp-4);right:var(--sp-4);top:100%;
    max-height:min(70vh,560px);overflow-y:auto;overscroll-behavior:contain;
    padding:var(--sp-3);
    border:1.5px solid var(--stroke);
    border-radius:16px 19px 17px 20px / 19px 16px 20px 17px;
    animation:lg-apparition var(--lg-duree) ease-out}
  aside{position:relative}
  .brand{margin-bottom:var(--sp-4)}
}

/* AU-DELÀ DE 900 PX, LA NAVIGATION NE FLOTTE PLUS : `.shell` est une grille à
   deux colonnes, le contenu est À CÔTÉ de l'aside et non derrière. Le matériau
   n'y montrerait que la couleur du papier. On le retire donc explicitement,
   plutôt que de retirer la classe du gabarit — ainsi les replis
   d'accessibilité de `.lg` continuent de la couvrir si elle redevient
   flottante un jour. */
@media (min-width:901px){
  nav.lg{background:none;box-shadow:none;
    -webkit-backdrop-filter:none;backdrop-filter:none}
}

/* Apparition et disparition progressives, courtes. Le déplacement est de trois
   pixels : au-delà, le panneau « saute » au lieu de se poser. */
@keyframes lg-apparition{
  from{opacity:0;transform:translateY(-3px) scale(.99)}
  to{opacity:1;transform:none}
}
.brand{display:flex;align-items:center;gap:10px;margin-bottom:var(--sp-6)}
.brand svg{width:34px;height:34px;flex:none}
.brand b{color:var(--ink);font-size:15px;line-height:1.2;font-weight:680}
.brand span{display:block;font-size:11px;color:var(--ink-faint);font-weight:400}
nav a{display:flex;align-items:center;gap:10px;padding:9px 11px;border-radius:11px 13px 12px 14px;
  color:var(--ink-soft);font-size:14px;font-weight:550;text-decoration:none;margin-bottom:2px}
nav a:hover{background:var(--fill-soft);color:var(--ink);text-decoration:none}
nav a[aria-current]{background:var(--fill);color:var(--ink);border:1.5px solid var(--stroke)}
nav a svg{width:21px;height:21px;flex:none}
nav .grp{margin:var(--sp-5) 0 var(--sp-2);padding-left:11px}
main{padding:var(--sp-6) var(--sp-6) var(--sp-7);max-width:1400px}
@media (max-width:700px){main{padding:var(--sp-4)}}
.head{display:flex;align-items:flex-start;gap:var(--sp-4);flex-wrap:wrap;margin-bottom:var(--sp-5)}
.head .grow{flex:1;min-width:220px}
.rule{position:relative;height:9px;margin:var(--sp-5) 0}
.rule::before,.rule::after{content:"";position:absolute;left:0;right:0;height:2px;
  background:var(--stroke);border-radius:2px}
.rule::before{top:2px;transform:rotate(-.1deg);opacity:.85}
.rule::after{top:5px;opacity:.28;transform:rotate(.14deg) scaleX(.985)}

/* -------------------------------------------------------------------- cartes */
.card{background:var(--paper-raised);border:2px solid var(--stroke);
  border-radius:18px 22px 20px 24px / 22px 18px 24px 20px;padding:var(--sp-5);box-shadow:var(--shadow)}
.card.soft{border-width:1.5px;border-color:var(--line)}
.grid{display:grid;gap:var(--sp-4)}
/* minmax(0,1fr) partout, et pour la raison écrite plus haut à propos de .shell :
   la taille minimale automatique d'une piste 1fr est min-content. Une carte qui
   contient une longue étiquette insécable élargit donc sa colonne, et la grille
   dépasse son parent. Sur le tableau de bord à 375 px, la carte « À traiter »
   sortait de 75 px de la page — la piste unique de .g2 valait 435 px pour une
   grille de 343. */
.g2{grid-template-columns:repeat(2,minmax(0,1fr))}
.g3{grid-template-columns:repeat(3,minmax(0,1fr))}
.g4{grid-template-columns:repeat(4,minmax(0,1fr))}
@media (max-width:1000px){.g3,.g4{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media (max-width:640px){.g2,.g3,.g4{grid-template-columns:minmax(0,1fr)}}
.stack{display:flex;flex-direction:column;gap:var(--sp-4)}
.row{display:flex;align-items:center;gap:var(--sp-3)}
.row+.row{border-top:1px solid var(--line);padding-top:var(--sp-3);margin-top:var(--sp-3)}
/* Sur un écran étroit, une rangée qui ne rentre pas passe à la ligne au lieu de
   sortir de sa carte. La légende du calendrier — un titre et quatre pastilles —
   dépassait de 10 px à 375 px. Le retour à la ligne ne se déclenche que quand le
   contenu ne rentre PAS : ailleurs, rien ne change. */
@media (max-width:640px){.row{flex-wrap:wrap}}
.grow{flex:1;min-width:0}
.trunc{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* ------------------------------------------------------------- indicateurs */
.kpi{padding:var(--sp-4);border:1.5px solid var(--line);
  border-radius:16px 19px 17px 20px;background:var(--paper-raised)}
.kpi .lab{font-size:11.5px;color:var(--ink-faint);font-weight:600;
  text-transform:uppercase;letter-spacing:.05em}
.kpi .val{font-size:28px;font-weight:680;color:var(--ink);line-height:1.15;margin-top:4px;
  font-variant-numeric:tabular-nums}
.kpi .val small{font-size:15px;color:var(--ink-faint);font-weight:500}
.kpi .sub{font-size:12px;color:var(--ink-faint);margin-top:2px}
.kpi.alert{border-color:var(--critical);background:var(--critical-bg)}
.kpi.alert .val{color:var(--critical)}
.kpi.good{border-color:var(--ok);background:var(--ok-bg)}
.kpi.good .val{color:var(--ok)}

/* -------------------------------------------------------------------- états */
.pill{display:inline-flex;align-items:center;gap:5px;font-size:11.5px;font-weight:600;
  padding:3px 10px;border-radius:999px;border:1.5px solid;white-space:nowrap}
.p-crit{color:var(--critical);background:var(--critical-bg);border-color:var(--critical)}
.p-warn{color:var(--warn);background:var(--warn-bg);border-color:var(--warn)}
.p-ok{color:var(--ok);background:var(--ok-bg);border-color:var(--ok)}
.p-info{color:var(--info);background:var(--info-bg);border-color:var(--info)}
.p-none{color:var(--ink-faint);background:var(--fill-soft);border-color:var(--line)}
.sev1{color:var(--critical)} .sev2{color:var(--warn)} .sev3{color:var(--info)}

/* -------------------------------------------------------------------- barres */
.bar{height:8px;background:var(--fill-soft);border:1.5px solid var(--line);
  border-radius:999px;overflow:hidden}
.bar>i{display:block;height:100%;background:var(--ink);border-radius:999px}
.bar.warn>i{background:var(--warn)} .bar.crit>i{background:var(--critical)}
.bar.ok>i{background:var(--ok)}

/* ------------------------------------------------------------------ boutons */
.btn{display:inline-flex;align-items:center;gap:7px;cursor:pointer;text-decoration:none;
  font:inherit;font-size:14px;font-weight:600;color:var(--ink);background:var(--fill);
  border:2px solid var(--stroke);border-radius:13px 16px 14px 17px / 16px 13px 17px 14px;
  padding:8px 15px;box-shadow:2px 2px 0 var(--stroke);
  transition:transform .08s ease,box-shadow .08s ease}
.btn:hover{transform:translate(-1px,-1px);box-shadow:3px 3px 0 var(--stroke);text-decoration:none}
.btn:active{transform:translate(1px,1px);box-shadow:1px 1px 0 var(--stroke)}
.btn.ghost{background:transparent;box-shadow:none;border-color:var(--line);color:var(--ink-soft)}
.btn.ghost:hover{border-color:var(--ink);color:var(--ink);transform:none;box-shadow:none}
.btn.sm{font-size:12.5px;padding:5px 11px;box-shadow:1px 1px 0 var(--stroke)}
.btn svg{width:17px;height:17px}

/* ----------------------------------------------------------------- tableaux */
table{width:100%;border-collapse:collapse;font-size:13.5px}
th{text-align:left;font-size:11px;text-transform:uppercase;letter-spacing:.06em;
  color:var(--ink-faint);padding:8px 10px;border-bottom:2px solid var(--stroke);white-space:nowrap}
td{padding:9px 10px;border-bottom:1px solid var(--line);vertical-align:middle}
tbody tr:last-child td{border-bottom:none}
tbody tr:hover{background:var(--fill-soft)}
.num{text-align:right;font-variant-numeric:tabular-nums;white-space:nowrap}

/* Un tableau plus large que sa carte doit défiler DANS la carte. Sans cela il
   pousse la mise en page entière : les en-têtes sont en white-space:nowrap, donc
   la largeur minimale du tableau devient celle de la page, et c'est le document
   qui défile horizontalement — barre latérale comprise. */
.scroll-x{overflow-x:auto;max-width:100%}

/* SOUS 900 PX, TOUT TABLEAU DÉFILE DE LUI-MÊME. Les .scroll-x sont posés à la
   main dans les gabarits, et seuls neuf des vingt-trois qui portent un tableau en
   ont un : sur un écran large la colonne principale est assez grande pour que
   l'oubli ne se voie pas. Sur un téléphone, chacun de ces tableaux pousse la page.
   Une règle plutôt que quatorze corrections de gabarit : elle ne dépend pas de la
   mémoire de qui ajoutera le vingt-quatrième.
   width:100% est nécessaire — sans lui, display:block fait rétrécir au contenu et
   les tableaux clé-valeur, eux, cesseraient de remplir leur carte. */
@media (max-width:900px){table{display:block;width:100%;overflow-x:auto}}
.empty{padding:var(--sp-6);text-align:center;color:var(--ink-faint);font-size:14px}

/* Tableau clé-valeur. Les <th> y étiquettent une ligne et non une colonne :
   sans cette classe ils héritent du filet épais des tableaux de données, ce qui
   produit un soulignement sombre sous chaque libellé. */
.kv th{width:190px;border-bottom:1px solid var(--line);vertical-align:middle}
.kv td{text-align:left}
/* La règle générale ne dispense que les td de la dernière ligne : sans ceci, le
   filet du dernier libellé s'arrête à mi-largeur. */
.kv tbody tr:last-child th{border-bottom:none}

/* ------------------------------------------------------------------ formulaire */
/* LA LISTE DES TYPES EST À COMPLÉTER À CHAQUE NOUVEAU CHAMP, et deux y
   manquaient. « password » n'existait nulle part avant l'écran de connexion.
   « number » sert depuis le lot 6 — délai de paiement d'un décompte, durée et
   préavis d'un contrat fournisseur — et ces quatre champs s'affichaient donc à
   la largeur par défaut du navigateur, deux fois plus étroits que leurs voisins
   et sans le trait du reste des formulaires. Une largeur ne se remarque pas dans
   une revue de code ; elle se voit à l'écran. */
input[type=text],input[type=search],input[type=date],input[type=email],
input[type=password],input[type=number],
select,textarea{font:inherit;font-size:14px;color:var(--ink);
  background:var(--paper-raised);border:1.5px solid var(--line);
  border-radius:11px 13px 12px 14px;padding:8px 12px;width:100%}
textarea{resize:vertical;line-height:1.5}
/* Le placeholder suit le thème, comme tout le reste. Il reste plus pâle que le
   texte SAISI — `color:var(--ink)` juste au-dessus — sans quoi on ne
   distinguerait pas un champ rempli d'un champ qui attend. */
input::placeholder,textarea::placeholder{color:var(--placeholder);opacity:1}
input:focus,select:focus,textarea:focus{outline:none;border-color:var(--ink)}
label.check{display:inline-flex;align-items:center;gap:7px;font-size:13.5px;cursor:pointer}

/* Un champ par ligne : à cinq baux par an, la densité ne vaut pas la peine de
   se tromper de case sur un écran qui engage juridiquement. */
.field{margin-bottom:var(--sp-4);max-width:520px}
.field>label{display:block;font-size:12px;font-weight:650;text-transform:uppercase;
  letter-spacing:.05em;color:var(--ink-faint);margin-bottom:5px}
/* SIXIÈME FOIS que les petites capitales de ce projet abîment un texte, et la
   première où la cause est la CASCADE et non le gabarit : `label.check` (plus
   haut) et `.field>label` ont la MÊME spécificité — une classe et un type — donc
   la seconde, écrite après, gagnait. Toute case à cocher posée dans un `.field`
   sortait donc en capitales à 12 px, sur des libellés qui sont des phrases :
   « RÉCUPÉRABLE AUPRÈS DES PRENEURS », « SEULEMENT CE QUI BLOQUE UN DÉCOMPTE ».
   Trois des huit dataient du lot 6 et ne s'étaient jamais vues.
   Les petites capitales conviennent aux NOMS de champ français, à rien d'autre. */
.field>label.check{display:inline-flex;align-items:center;gap:7px;font-size:13.5px;
  font-weight:500;text-transform:none;letter-spacing:normal;color:var(--ink);
  cursor:pointer}
input[type=file]{font:inherit;font-size:13px;color:var(--ink-soft);width:100%;
  border:1.5px dashed var(--line);border-radius:11px 13px 12px 14px;padding:9px 12px;
  background:var(--fill-soft);cursor:pointer}
input[type=file]:hover{border-color:var(--ink-faint)}
form.inline{display:flex;gap:6px;align-items:center}
form.inline input{width:auto;min-width:170px;font-size:12.5px;padding:5px 10px}
form .note{margin:calc(-1 * var(--sp-2)) 0 var(--sp-4);max-width:520px}

/* ---------------------------------------------------------------- messages */
/* Le résultat d'une écriture doit se voir sans être cherché : ces bandeaux
   passent avant le contenu, et le refus porte le message de PostgreSQL tel
   quel — il a été écrit pour être lu. */
.flash{display:flex;align-items:flex-start;gap:9px;font-size:13.5px;line-height:1.5;
  padding:11px 14px;margin-bottom:var(--sp-4);border:1.5px solid;
  border-radius:12px 15px 13px 16px}
.flash svg{flex:0 0 auto;margin-top:1px}
.flash.ok{background:var(--ok-bg);border-color:var(--ok);color:var(--ok)}
.flash.ko{background:var(--critical-bg);border-color:var(--critical);color:var(--critical)}
.flash.info{background:var(--info-bg);border-color:var(--info);color:var(--info)}

/* Le bandeau d'étage — voir base.html. Assez visible pour qu'on ne confonde pas
   l'acceptance avec la production, assez sobre pour ne pas manger l'écran.

   `border-radius` valait `var(--radius)`, et CETTE VARIABLE N'EXISTE PAS : le
   projet nomme ses rayons en dur, coin par coin, pour le trait esquissé. Une
   variable inconnue rend la déclaration invalide au calcul, donc le rayon
   retombait à zéro — un bandeau à angles droits au milieu d'un écran qui n'en a
   aucun. Trouvé en relisant la feuille, pas à l'écran : c'est le genre de détail
   qu'on cesse de voir au bout de deux jours. */
.etage{display:flex;flex-wrap:wrap;align-items:baseline;gap:10px;
  font-size:12.5px;line-height:1.5;padding:7px 14px;margin-bottom:var(--sp-4);
  border:1px solid;border-radius:12px 15px 13px 16px}
.etage b{font-size:11px;letter-spacing:.09em;flex:0 0 auto}
.etage span{opacity:.85}
.etage code{font-size:11.5px}
.etage-pastille{border:1px solid currentColor;border-radius:99px;padding:0 8px}
.etage-qas{background:var(--warn-bg);border-color:var(--warn);color:var(--warn)}
.etage-dev{background:var(--info-bg);border-color:var(--info);color:var(--info)}
.etage-nul{background:var(--critical-bg);border-color:var(--critical);color:var(--critical)}

/* ---------------------------------------------------------------- calendrier */
.cal{display:grid;grid-template-columns:repeat(7,1fr);gap:4px}
.cal .dow{font-size:10px;text-transform:uppercase;letter-spacing:.07em;color:var(--ink-faint);
  text-align:center;font-weight:650;padding-bottom:4px}
/* La case d'un jour EST UN BOUTON depuis RE-Phase 9 : on clique un jour pour
   voir ce qu'il porte et y ajouter une tâche ou un rendez-vous. D'où la remise à
   zéro du style de bouton — un `<button>` hérite d'une police, d'un centrage et
   d'un fond que la grille ne veut pas. `display:block` et non le `flex` par
   défaut d'un bouton : les événements s'empilent, comme dans la div d'avant. */
.cal .d{display:block;width:100%;text-align:left;font:inherit;color:inherit;
  min-height:82px;border:1.5px solid var(--line);border-radius:9px 11px 10px 12px;
  padding:4px 5px;background:var(--paper-raised);overflow:hidden;cursor:pointer}
.cal .d:hover{border-color:var(--ink-faint);background:var(--fill-soft)}
.cal .d:focus-visible{outline:2px solid var(--ink);outline-offset:1px}
.cal .d.out{opacity:.4} .cal .d.today{border-color:var(--ink);border-width:2px}
.cal .d>b{font-size:11px;color:var(--ink-faint);font-variant-numeric:tabular-nums}
.cal .d.today>b{color:var(--ink)}
.ev{display:block;font-size:9.5px;font-weight:600;line-height:1.25;margin-top:3px;
  padding:2px 4px;border-radius:5px;border-left:3px solid;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap;text-decoration:none}
.ev:hover{text-decoration:none;filter:brightness(.96)}
.ev-1{background:var(--critical-bg);border-color:var(--critical);color:var(--critical)}
.ev-2{background:var(--warn-bg);border-color:var(--warn);color:var(--warn)}
.ev-3{background:var(--info-bg);border-color:var(--info);color:var(--info)}
.ev-4,.ev-5{background:var(--fill-soft);border-color:var(--ink-faint);color:var(--ink-soft)}

/* ------------------------------------------------------------- la fenêtre */
/* CE QU'ELLE EST, ET CE QU'ELLE N'EST PAS. Un `<dialog>` natif — pas un div
   posé par-dessus la page : le navigateur y met le piège de tabulation, la
   touche Échap, le fond inerte et le retour du focus, quatre choses qu'une
   réimplémentation rate toujours l'une ou l'autre.

   `display:flex` est posé sur `.fen[open]` ET PAS SUR `.fen`, et c'est le piège
   de cet élément : la feuille du navigateur cache un dialog fermé par
   `dialog:not([open]){display:none}`. Une règle d'auteur sur `.fen` gagnerait
   contre elle — la fenêtre serait alors visible en permanence, ouverte ou non. */
/* LE FOND ET L'OMBRE VIENNENT DE `.lg`, posée sur le `<dialog>` dans base.html.
   C'est le seul élément de la plateforme qui flotte VRAIMENT sur le contenu :
   la page est derrière lui, entière, et c'est elle que le flou laisse deviner.
   L'opacité de la variante Regular est haute — .82 — parce que cette fenêtre
   porte des formulaires et des montants : le matériau y sert à situer, jamais à
   décorer. */
.fen{width:min(780px,94vw);max-height:88vh;padding:0;
  border:2px solid var(--stroke);
  border-radius:18px 22px 20px 24px / 22px 18px 24px 20px;
  color:var(--ink-soft);
  font-family:var(--font-body);font-size:15px;line-height:1.6}
.fen[open]{display:flex;flex-direction:column;
  animation:lg-fenetre var(--lg-duree) ease-out}
/* Le voile, et rien de plus. Il ne floute PAS : la spécification veut que le
   contenu reste perceptible derrière une commande, et c'est le matériau de la
   fenêtre elle-même qui porte le flou. Deux flous superposés effaceraient la
   page au lieu de la mettre en retrait. */
.fen::backdrop{background:var(--lg-voile);
  animation:lg-voile var(--lg-duree) ease-out}
@keyframes lg-fenetre{
  from{opacity:0;transform:scale(.98)}
  to{opacity:1;transform:none}
}
@keyframes lg-voile{from{opacity:0} to{opacity:1}}
.fen-corps{overflow-y:auto;padding:var(--sp-5);min-height:0}
.fen-tete{display:flex;align-items:flex-start;gap:var(--sp-3);
  margin-bottom:var(--sp-4)}
.fen-tete .grow{flex:1;min-width:0}
.fen-tete h3{font-size:18px}
.fen-pied{margin-top:var(--sp-5);padding-top:var(--sp-4);
  border-top:1px solid var(--line);display:flex;gap:var(--sp-2);flex-wrap:wrap}
/* Dans une fenêtre, un champ à 520 px laisserait la moitié de la largeur vide :
   la contrainte de .field vise une PAGE, dont la colonne fait 1400 px. */
.fen .field{max-width:none}
.fen h4{margin-top:var(--sp-5);margin-bottom:var(--sp-3)}
.fen h4:first-child{margin-top:0}
/* Le titre reçoit le focus à l'ouverture, pour que la fenêtre s'annonce et
   surtout pour que le navigateur ne cercle pas le `<dialog>` entier. Il n'est
   pas atteignable au clavier — tabindex="-1" — donc l'anneau n'apprend rien à
   personne et n'a pas à être dessiné. */
.fen h3[tabindex]:focus{outline:none}

/* CE QUI OUVRE LA FENÊTRE EST UN VRAI BOUTON, et non une ligne rendue cliquable
   par un gestionnaire. Un bouton se tabule, s'active à Entrée et s'annonce comme
   tel — et surtout, la politique de contenu du Caddyfile n'autorise que
   `script-src 'self'` : aucun `onclick` en ligne, et aucun filtre d'événement
   HTMX, qui passe par `new Function`. */
.ouvre{font:inherit;color:inherit;background:none;border:none;padding:0;
  margin:0;text-align:left;cursor:pointer;width:100%;min-width:0}
.ouvre:hover{background:var(--fill-soft)}
.ouvre:focus-visible{outline:2px solid var(--ink);outline-offset:2px;
  border-radius:8px}
/* Une rangée entière qui ouvre la fenêtre. Reprend .row sans son padding-top,
   que la bordure de séparation gère déjà. */
button.row.ouvre{display:flex;align-items:center;gap:var(--sp-3);
  padding:var(--sp-2) var(--sp-2);margin-left:calc(-1 * var(--sp-2));
  width:calc(100% + 2 * var(--sp-2));border-radius:10px 12px 11px 13px}
button.row.ouvre+button.row.ouvre{border-top:1px solid var(--line);
  padding-top:var(--sp-3);margin-top:var(--sp-3)}
/* Le libellé cliquable d'une cellule de tableau : il doit ressembler à un lien,
   parce que c'en est un du point de vue de qui lit.

   white-space:nowrap N'EST PAS COSMÉTIQUE, et il a fallu regarder l'écran pour
   le voir : un `<button>` accepte de se réduire jusqu'à son plus long mot, ce
   qu'un nœud de texte dans un `<td>` ne fait pas de la même façon. La référence
   « L-2024-002 » du tableau des indexations, qui tenait sur une ligne, se
   coupait en trois — « L- / 2024- / 002 » — dès qu'on l'a mise dans un bouton.
   Une référence est un identifiant : elle ne se coupe pas. */
.ouvre.lien{width:auto;color:var(--info);font-weight:600;white-space:nowrap}
.ouvre.lien:hover{text-decoration:underline;background:none}

/* --------------------------------------------------------------------- divers */
.note{border-left:3px solid var(--fill);padding-left:var(--sp-4);font-size:13px;color:var(--ink-faint)}
/* Indication SOUS un champ de saisie, et non note à part. Le filet vertical de
   .note, répété sous quarante champs d'un formulaire d'encodage, dessinait une
   colonne de traits qui se lisait comme une structure — alors qu'il ne s'agit
   que d'aides à la saisie. */
.hint{font-size:12px;color:var(--ink-faint);margin-top:3px;line-height:1.35}
.diff{font-family:var(--font-mono);font-size:12px}
.diff s{color:var(--critical)} .diff b{color:var(--ok)}
.tabs{display:flex;gap:4px;border-bottom:2px solid var(--stroke);margin-bottom:var(--sp-4);
  overflow-x:auto}
.tab{padding:8px 14px;font-size:13.5px;font-weight:600;color:var(--ink-faint);cursor:pointer;
  background:none;border:1.5px solid transparent;border-bottom:none;
  border-radius:11px 13px 0 0;white-space:nowrap}
.tab[aria-selected=true]{color:var(--ink);background:var(--paper-raised);
  border-color:var(--ink);margin-bottom:-2px}
.htmx-request .spin{opacity:.45;transition:opacity .15s}
footer{margin-top:var(--sp-7);padding-top:var(--sp-4);border-top:1px solid var(--line);
  font-size:12px;color:var(--ink-faint);display:flex;gap:var(--sp-4);flex-wrap:wrap}

/* ------------------------------------------------- connexion et utilisateur */
/* LA BARRE LATÉRALE TIENT DÉSORMAIS LA HAUTEUR DE LA FENÊTRE, et il a fallu la
   mesurer pour s'en apercevoir. Avec « margin-top:auto » seul, le bloc
   utilisateur tombait au bas de l'aside — dont la hauteur suit celle de la
   colonne principale, puisque c'est une même ligne de grille. Sur le tableau de
   bord, le bouton « Se déconnecter » était à 1227 px dans une fenêtre de 720, et
   sur l'échéancier bien plus bas encore : un bouton qu'il faut chercher en
   défilant deux écrans n'existe pas.

   La barre est donc collée, haute d'une fenêtre, et c'est la NAVIGATION qui
   défile en son sein quand ses quinze liens ne tiennent pas. Effet de bord
   bienvenu : les liens restent atteignables au milieu d'un long tableau.

   Uniquement au-delà de 900 px : en dessous, .shell passe à une colonne et
   l'aside devient un bandeau supérieur, où une hauteur de fenêtre n'aurait
   aucun sens. */
aside{display:flex;flex-direction:column}
@media (min-width:901px){
  aside{position:sticky;top:0;height:100vh}
  aside nav{flex:1 1 auto;min-height:0;overflow-y:auto;scrollbar-width:thin}
}
.me{margin-top:auto;flex:none;padding-top:var(--sp-4);border-top:1px solid var(--line);
  display:flex;flex-direction:column;gap:var(--sp-2);align-items:flex-start}
.me>span{display:flex;align-items:center;gap:7px;font-size:12.5px;font-weight:600;
  color:var(--ink-soft)}
.me svg{flex:none}
@media (max-width:900px){.me{margin-top:var(--sp-4)}}

/* L'écran de connexion n'a pas de barre latérale : il se centre lui-même. */
.login{display:flex;align-items:center;justify-content:center;
  min-height:100vh;padding:var(--sp-5)}
.login .card{width:100%;max-width:430px}
.login .field{max-width:none}

/* ---------------------------------------------------------------- assistant */
/* Le fil de conversation. Deux alignements et non deux couleurs vives : ce qui
   distingue une question d'une réponse est sa position, comme dans n'importe
   quelle messagerie. Le trait esquissé reste sur les contours, jamais sur le
   texte — c'est la règle de style du projet, et un montant lu dans une bulle
   doit se lire aussi nettement que dans un tableau. */
.chat{display:flex;flex-direction:column;gap:var(--sp-4)}
.msg{max-width:min(720px,92%);padding:var(--sp-4);background:var(--paper-raised);
  border:1.5px solid var(--line);border-radius:16px 19px 17px 20px / 19px 16px 20px 17px}
.msg .who{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;
  color:var(--ink-faint);margin-bottom:6px;display:flex;align-items:center;gap:6px}
/* white-space:pre-wrap : la réponse du modèle contient de vrais sauts de ligne,
   et sans cela ses listes et ses paragraphes se collent en un bloc. */
.msg .txt{white-space:pre-wrap;font-size:14px;line-height:1.55}
.msg.me{align-self:flex-end;background:var(--fill-soft);border-color:var(--fill)}
.msg.ai{align-self:flex-start;border-color:var(--stroke)}
/* Un appel d'outil n'est ni une question ni une réponse : c'est la trace de ce
   que l'assistant est allé lire. Pleine largeur, en gris, repliée par défaut —
   elle doit être consultable et ne pas encombrer la lecture. */
.msg.tool{align-self:stretch;max-width:100%;background:var(--fill-soft);
  border-style:dashed;padding:var(--sp-3) var(--sp-4)}
.msg.tool summary{cursor:pointer;font-size:12px;color:var(--ink-faint);
  font-family:var(--font-mono)}
.msg.tool pre{margin:var(--sp-3) 0 0;font-size:11.5px;line-height:1.5;
  max-height:320px;overflow:auto;white-space:pre-wrap;word-break:break-word;
  color:var(--ink-soft)}
/* Le jeton d'anonymisation, quand on le montre volontairement — écran des
   outils, aperçu d'un plan. Dans le fil, l'utilisateur lit des noms réels. */
.jeton{font-family:var(--font-mono);font-size:12px;padding:1px 5px;
  border:1px dashed var(--info);border-radius:5px;color:var(--info);
  background:var(--info-bg);white-space:nowrap}
/* Le plan d'action, posé dans le fil à l'endroit où il a été proposé. Bordure
   pleine et non esquissée : c'est le seul endroit de l'écran où un clic écrit
   dans le patrimoine, et il ne doit pas ressembler au reste. */
.plan{align-self:stretch;max-width:100%;border:2px solid var(--info);
  background:var(--info-bg);border-radius:16px 19px 17px 20px;padding:var(--sp-4)}
.plan.legal{border-color:var(--critical);background:var(--critical-bg)}
.plan.fait{border-color:var(--ok);background:var(--ok-bg)}
.plan.mort{border-color:var(--line);background:var(--fill-soft);opacity:.75}
.plan .etape{border-top:1px solid var(--line);padding-top:var(--sp-3);margin-top:var(--sp-3)}
.plan .etape:first-of-type{border-top:none;padding-top:0;margin-top:var(--sp-3)}
/* Une case à cocher dont le libellé est une PHRASE, et non un nom de champ.
   .field>label est en petites capitales : juste pour « MOTIF », illisible sur
   « Cette action a une conséquence juridique… ». Le projet s'est déjà fait
   prendre par des petites capitales prises pour un libellé de champ. */
.consent{display:flex;gap:var(--sp-3);align-items:flex-start;margin-bottom:var(--sp-4);
  font-size:13.5px;line-height:1.5;color:var(--ink);cursor:pointer;max-width:620px;
  text-transform:none;letter-spacing:normal;font-weight:400}
.consent input[type=checkbox]{width:auto;margin:2px 0 0;flex:none}
/* Une touche du clavier, citée dans une explication. Le projet n'en avait
   jamais eu besoin : la dictée du système est la première chose qu'on explique
   en nommant une touche. */
kbd{font-family:var(--font-mono);font-size:11.5px;padding:1px 5px;
  border:1px solid var(--line);border-bottom-width:2px;border-radius:5px;
  background:var(--paper-raised);color:var(--ink-soft);white-space:nowrap}
