/*
 * Projet : argos
 * Auteur : Azziz EL KHIATI
 * Date : 31/07/2026
 * Edi : PhpStorm
 * Licence : AGPL v3
 *
 * L'identité d'argos, née de la maquette du 29 juillet : fond nuit,
 * encre ivoire, l'or et la sentinelle, Georgia. Cette feuille est chargée après
 * Bootstrap et reprend la main sur ses variables, sans quoi la nuit
 * tournerait au blanc.
 *
 * Deux thèmes depuis la bêta 3, et aucune couleur n'est écrite ailleurs
 * que dans ces deux blocs : c'est la seule façon d'en tenir un second
 * sans doubler la feuille. La nuit reste la signature et le défaut ;
 * le jour se demande.
 */

:root,
[data-theme="nuit"] {
    --fond: #101418;
    --carte: #171d24;
    --carte-haute: #1c232c;
    --bord: #242d38;
    --encre: #e8e4da;
    --sourdine: #9aa4ae;
    --or: #c9a84c;
    --vert: #4cae6e;
    --vert-fond: #16281d;
    --ambre: #d3a24a;
    --ambre-fond: #2b2214;
    /* Le rouge de la nuit rendait 3,92 sur son propre fond — mesuré au
       #503 sur le mot « tiré » du panoptique. Éclairci, il tient 4,78
       sur son fond, 4,76 sur une carte et 5,19 sur la page. Son jumeau
       du jour tenait déjà 5,28 : c'est la nuit seule qui manquait. */
    --rouge: #e05f44;
    --rouge-fond: #2a1714;
    --ombre: none;
    --flanc: 236px;
    --flanc-replie: 62px;

    --bs-body-bg: #101418;
    --bs-body-color: #e8e4da;
    --bs-border-color: #242d38;
    --bs-emphasis-color: #e8e4da;
    --bs-secondary-color: #9aa4ae;
}

[data-theme="jour"] {
    --fond: #f4f2ec;
    --carte: #ffffff;
    --carte-haute: #faf8f3;
    --bord: #ddd8cc;
    --encre: #24282c;
    /* **Mesuré, pas choisi** (#503) : la sourdine rendait 4,45 sur le
       fond de page et 4,98 sur une carte — le même mot lisible ou non
       selon l'endroit. Trois unités plus bas, elle tient 4,66 partout. */
    --sourdine: #686d74;
    /* L'or perd de sa lumière sur fond clair : il se fonce pour rester
       lisible, et c'est la seule concession du thème à la signature. */
    --or: #8a6d18;
    --vert: #2f7a4a;
    --vert-fond: #e6f1e9;
    /* L'ambre s'écrit sur son propre fond, où il rendait 4,10 quand son
       jumeau vert tenait 4,53. Il porte les mots « en veille » et les
       avertissements : ce sont les derniers qu'on peut laisser pâles. */
    --ambre: #8e6211;
    --ambre-fond: #f8efdc;
    --rouge: #b23a26;
    --rouge-fond: #fdeeeb;
    --ombre: 0 1px 2px rgba(36, 40, 44, .06);

    --bs-body-bg: #f4f2ec;
    --bs-body-color: #24282c;
    --bs-border-color: #ddd8cc;
    --bs-emphasis-color: #24282c;
    --bs-secondary-color: #686d74;
}

* { box-sizing: border-box; }

html, body {
    margin: 0; padding: 0;
    background: var(--fond);
    color: var(--encre);
    font-family: Georgia, "Times New Roman", serif;
    line-height: 1.5;
    min-height: 100vh;
}

h1, h2, h3 { font-weight: normal; margin: 0; }
a { color: var(--or); text-decoration: none; }
a:hover { color: var(--encre); }
code, pre { font-family: ui-monospace, "Cascadia Code", Consolas, monospace; }

/* ---- La charpente : flanc + scène ---- */

/* **Rien ne dépasse en largeur.** Un ascenseur horizontal sur une
   application de tableau de bord est toujours un défaut, jamais un
   choix : ce qui est trop large se replie, se tronque ou défile dans
   son propre cadre. Cette ligne est la garde de dernier recours, pas
   la solution — chaque débordement se corrige à sa source. */
.cadre { display: flex; min-height: 100vh; max-width: 100%; overflow-x: clip; }

.flanc {
    width: var(--flanc); flex: 0 0 var(--flanc);
    background: var(--carte);
    border-right: 1px solid var(--bord);
    display: flex; flex-direction: column;
    position: sticky; top: 0; height: 100vh; overflow-y: auto; overflow-x: auto;
}

.marque {
    display: flex; align-items: center; gap: .6rem;
    padding: 1.2rem 1.2rem 1rem;
    border-bottom: 1px solid var(--bord);
    color: var(--encre);
}
.marque:hover { color: var(--or); }
.marque-sentinelle { font-size: 1.5rem; line-height: 1; }
.marque-nom { font-size: 1.5rem; letter-spacing: .14em; }
/* La lignée en exposant après le nom : l'or quand elle sert, l'ambre
   quand elle s'éprouve. Plus petite que le nom, sans son espacement de
   lettres — la maquette marque-lignee.svg tranche la forme (#288). */
.marque-lignee {
    font-size: .72rem; font-weight: bold; color: var(--or);
    align-self: flex-start; margin: .2rem 0 0 -.3rem;
    letter-spacing: 0; white-space: nowrap;
}
.marque-lignee-etape { color: var(--ambre); }

.menu { flex: 1; padding: .8rem 0; }
.menu-titre {
    display: block; padding: 1rem 1.2rem .4rem;
    font-size: .68rem; letter-spacing: .18em; text-transform: uppercase;
    color: var(--sourdine); opacity: .75;
}
.menu-lien {
    display: flex; align-items: center; gap: .7rem;
    padding: .5rem 1.2rem;
    color: var(--sourdine); font-size: .92rem;
    border-left: 2px solid transparent;
}
.menu-lien i { width: 1.1rem; text-align: center; font-size: .9rem; }
.menu-lien:hover { color: var(--encre); background: var(--carte-haute); }
.menu-lien.actif {
    color: var(--or); border-left-color: var(--or); background: var(--carte-haute);
}

.menu-clos { color: var(--sourdine); cursor: default; }
.menu-clos:hover { background: none; color: var(--sourdine); }

.flanc-pied {
    border-top: 1px solid var(--bord); padding: .8rem 0 .4rem;
}
.menu-entrer { color: var(--or); }
.menu-entrer:hover { color: var(--encre); }
.flanc-usager {
    display: flex; align-items: center; gap: .7rem;
    padding: .4rem 1.2rem; font-size: .82rem; color: var(--sourdine);
}
.flanc-usager i { width: 1.1rem; text-align: center; }
.version {
    display: flex; align-items: center; gap: .5rem;
    padding: .6rem 1.2rem; font-size: .78rem; color: var(--sourdine);
    letter-spacing: .08em;
}
.version:hover { color: var(--or); }

.scene { flex: 1; min-width: 0; display: flex; flex-direction: column; }

.bandeau {
    padding: 1.5rem 2rem 1.2rem;
    border-bottom: 1px solid var(--bord);
    background: var(--fond);
    position: sticky; top: 0; z-index: 20;
}
.bandeau-titre { font-size: 1.6rem; letter-spacing: .04em; }
.bandeau-note { margin: .2rem 0 0; color: var(--sourdine); font-style: italic; font-size: .92rem; }
.bandeau-note:empty { display: none; }

.bascule {
    display: none; background: none; border: none; color: var(--sourdine);
    font-size: 1.2rem; padding: 0 0 .6rem; cursor: pointer;
}

.scene-corps { flex: 1; padding: 1.6rem 2rem 3rem; }

/* ---- Les blocs ---- */

.bloc {
    background: var(--carte); border: 1px solid var(--bord);
    border-radius: 10px; padding: 1.3rem 1.5rem; margin-bottom: 1.2rem;
    min-width: 0;
}
.bloc > h2 {
    font-size: .82rem; letter-spacing: .14em; text-transform: uppercase;
    color: var(--or); margin-bottom: 1rem;
}
.bloc > h2:not(:first-child) { margin-top: 1.6rem; }

.verdict-sain, .verdict-veille { text-align: center; }
.verdict-sain { background: var(--vert-fond); border-color: var(--vert); }
.verdict-sain strong { color: var(--vert); font-size: 1.2rem; letter-spacing: .06em; }
.verdict-veille { background: var(--ambre-fond); border-color: var(--ambre); }
.verdict-veille strong { color: var(--ambre); font-size: 1.2rem; letter-spacing: .06em; }

/* ---- Les tuiles ---- */

.tuiles {
    display: grid; gap: 1.1rem;
    grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
    margin-bottom: 1.2rem;
}
.tuile {
    background: var(--carte); border: 1px solid var(--bord);
    border-radius: 10px; padding: 1.1rem 1.3rem; min-width: 0;
}
.tuile-large { grid-column: 1 / -1; }
.tuile-titre {
    font-size: .72rem; letter-spacing: .14em; text-transform: uppercase;
    color: var(--or); margin-bottom: .7rem;
}
.tuile-valeur { font-size: 2.1rem; line-height: 1.1; }
.tuile-unite { font-size: .95rem; color: var(--sourdine); }
.tuile-pied { color: var(--sourdine); font-size: .76rem; margin-top: .6rem; }
.graphe-cadre { position: relative; height: 210px; }

/* Ce qu'une courbe couvre, sous la courbe : une note, pas un titre. */
.graphe-echelle {
    display: block;
    margin: .45rem 0 0;
    font-size: .74rem;
    color: var(--sourdine);
    letter-spacing: .01em;
}
.gantt-cadre { position: relative; width: 100%; min-height: 260px; }

/* ---- Les tables, qui ne débordent jamais ---- */

/* **Déclarer un seul axe les décide tous les deux** (#498). Quand
   `overflow-x` vaut autre chose que `visible`, la spécification calcule
   `overflow-y` à `auto` — même si personne ne l'a écrit. Ce cadre rognait
   donc en hauteur sans le dire, et le menu de gestes d'une table courte
   tombait dessous : mesuré à 77 px de cadre pour 111 px de menu, aucun de
   ses trois gestes atteignable. L'axe vertical s'écrit désormais, pour
   qu'il se lise. */
.table-cadre { overflow-x: auto; overflow-y: auto; margin: 0 -.4rem; }
table { width: 100%; border-collapse: collapse; font-size: .9rem; }
th, td {
    text-align: left; padding: .55rem .7rem;
    border-bottom: 1px solid var(--bord);
    vertical-align: top;
}
th {
    color: var(--sourdine); font-weight: normal;
    letter-spacing: .1em; font-size: .72rem; text-transform: uppercase;
    white-space: nowrap;
}
tbody tr:last-child td { border-bottom: none; }
tbody tr:hover { background: var(--carte-haute); }
/* **Un identifiant technique ne se coupe pas** (#504) : une etiquette
   de version se lisait « v3.47.0- » sur une ligne et « beta » sur la
   suivante, et chaque ligne du tableau doublait de hauteur. Le cadre
   de table defile deja, donc rien ne deborde. */
td > code { white-space: nowrap; }

/* Une date ne se coupe pas davantage : « 27/08/2026 à » sur une ligne
   et l'heure sur la suivante double la hauteur de chaque ligne du
   tableau, pour rien. */
td.quand { white-space: nowrap; }

td.brut {
    font-family: ui-monospace, Consolas, monospace; font-size: .78rem;
    color: var(--sourdine); max-width: 34ch;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* ---- Badges & pastilles ---- */

.badge {
    display: inline-block; font-size: .68rem; letter-spacing: .1em;
    padding: .18rem .6rem; border-radius: 20px; text-transform: uppercase;
    white-space: nowrap;
}
.sain { background: var(--vert-fond); color: var(--vert); border: 1px solid var(--vert); }
/* watch est l'alias anglais de veille (renommage du 6 août) : une
   dizaine d'écrans le portent déjà, l'unification du mot appartient à
   la passe de langue (#371). */
.veille, .watch { background: var(--ambre-fond); color: var(--ambre); border: 1px solid var(--ambre); }

/* **Seule l'exception se marque, et la marque ne se confond pas avec
   l'etat** (#526) : l'etat porte une pastille pleine, l'age une marque
   creuse. « seuil » dit ce que la sentinelle a vu, « vieilli » dit
   qu'elle l'a vu il y a trop longtemps, et les deux tiennent sur la
   meme ligne sans se contredire. */
.badge.creuse { background: transparent; color: var(--ambre); border: 1px dashed var(--ambre); }
.mesure-vieillie { color: var(--ambre); }

/* **L'etat neutre s'ecrit, il ne se deduit pas de l'absence** (#499). Le
   vocabulaire des etats en compte trois — sain, veille, neutre — et le
   troisieme n'avait pas de regle : un badge neutre rendait le badge nu,
   par hasard plutot que par decision. Les gabarits l'ecrivaient
   pourtant, et une expression le calcule encore. */
.neutre { background: var(--carte-haute); color: var(--sourdine); border: 1px solid var(--bord); }

/* **Un nom technique se lit sous son libelle, jamais a sa place** : en
   italique et en sourdine, plus petit que ce qu'il precise. La regle
   existait dans la maniere de faire et nulle part dans la feuille : le
   slug prenait la couleur de son parent, donc du gris quand il tombait
   dans une aide, et du texte plein partout ailleurs. Mesure dans la
   fiche d'un moule : rgb(36,40,44) a 16px, la meme encre que le titre
   (#499). */
.slug { font-style: italic; font-size: .85rem; color: var(--sourdine); }

.pastille {
    display: block; text-align: center; font-size: .88rem;
    padding: .6rem 1rem; border-radius: 8px; margin-bottom: 1rem;
}
.pastille-ambre { background: var(--ambre-fond); color: var(--ambre); border: 1px solid var(--ambre); }
.pastille-verte { background: var(--vert-fond); color: var(--vert); border: 1px solid var(--vert); }

/* **L'annonce d'un geste vit au-dessus de la part** (#527), qui se
   recharge et perdrait ce qu'on y pose ; le geste qu'elle propose reste
   dans la phrase, sa forme ne saute pas de ligne. */
.annonce { margin-bottom: 1rem; }
.annonce form { display: inline; }

/* **La puce est un choix, la pastille est un bandeau.** Les maquettes du
   plan et du calendrier alignent des rangées de petits choix cerclés,
   périodes et sources, à hauteur de texte. La puce est cette mesure-là :
   l'état se dit par le liseré, or quand le choix est retenu, vert quand
   la source est vivante — jamais par la taille. */
.puce {
    display: inline-flex; align-items: center; gap: .35rem;
    padding: .12rem .7rem; border-radius: 999px;
    background: var(--carte); color: var(--sourdine);
    border: 1px solid var(--bord); cursor: pointer;
    font-family: inherit; font-size: .78rem; line-height: 1.5;
    text-decoration: none; white-space: nowrap;
}
.puce:hover { border-color: var(--or); color: var(--encre); }
.puce-or { border-color: var(--or); color: var(--encre); }
.puce-verte { border-color: var(--vert); color: var(--encre); }

/* Les bornes libres : deux dates à hauteur de puce, pas des champs de
   formulaire pleins. */
.bornes { display: inline-flex; align-items: center; gap: .4rem; }
.bornes input[type="date"] {
    width: auto; padding: .12rem .5rem;
    background: var(--fond); color: var(--sourdine);
    border: 1px solid var(--bord); border-radius: 5px;
    font-family: inherit; font-size: .78rem;
}
.bornes input[type="date"]:focus { border-color: var(--or); color: var(--encre); }

/* ---- Formulaires ---- */

label {
    display: block; font-size: .7rem; letter-spacing: .14em;
    text-transform: uppercase; color: var(--sourdine);
    margin: 1rem 0 .35rem;
}
input[type="text"], input[type="password"], input[type="email"],
select, textarea {
    width: 100%; padding: .55rem .8rem;
    background: var(--fond); color: var(--encre);
    border: 1px solid var(--bord); border-radius: 8px;
    font-family: inherit; font-size: .95rem;
}
input:focus, select:focus, textarea:focus { outline: none; border-color: var(--or); }
textarea { font-family: ui-monospace, Consolas, monospace; font-size: .85rem; }

/* **Les boutons prennent l'échelle des attributs.** Ils étaient gros et
   appuyés — capitales espacées, rembourrage large, marge haute — hérités
   d'un temps où l'écran de connexion était le seul à en porter. Dans une
   carte, une barre ou un pied de modale, ils écrasaient tout ce qui les
   entourait et donnaient à chaque geste l'air d'être le geste principal.

   La mesure juste est celle de la pastille d'attribut : de quoi se
   cliquer sans hésiter, pas de quoi dominer sa ligne. */
/* **Un geste ne sort jamais au style natif du navigateur** (#446).

   Les gestes de ce produit s'habillent par leur classe — `.bouton-or`,
   `.bouton-sobre`, `.lien`. Un bouton écrit sans classe traversait donc
   toutes les relectures sans rien déclencher : il était valide, il
   fonctionnait, et il sortait en pavé clair à bordure système, en plein
   thème sombre. Mesuré au menu d'un indicateur, où deux gestes sur
   quatre étaient dans ce cas.

   Cette base ne donne aucune apparence : **elle retire seulement celle
   du navigateur**, et chaque classe pose la sienne par-dessus. Un geste
   ne dépend plus de la mémoire de qui l'écrit. */
button {
    font: inherit;
    color: inherit;
    background: none;
    border: none;
    padding: 0;
    cursor: pointer;
}

.bouton-or {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    padding: .32rem .75rem;
    /* **Le bouton fonce, la signature ne bouge pas** (#503). L'encre
       claire sur l'or du jour rendait 4,38 : c'est le geste principal
       de chaque modale, donc le controle le plus clique de la
       plateforme. Fonce de six pour cent, il tient 4,86, et l'or reste
       celui des titres et des liens. */
    background: color-mix(in srgb, var(--or) 94%, black); color: var(--fond);
    border: 1px solid color-mix(in srgb, var(--or) 94%, black); border-radius: 5px; cursor: pointer;
    font-family: inherit; font-size: .78rem; line-height: 1.4;
    letter-spacing: .02em;
}
.bouton-or:hover { filter: brightness(1.1); }

/* Le seuil garde son bouton d'apparat : c'est le seul écran où le geste
   est unique, et où il n'écrase rien puisqu'il est seul. */
.carte-connexion .bouton-or {
    margin-top: 1.2rem;
    padding: .65rem 1.4rem;
    font-size: .85rem;
    letter-spacing: .16em;
    text-transform: uppercase;
}

.bouton-large { width: 100%; justify-content: center; }

.lien {
    display: inline-flex; align-items: center; gap: .3rem;
    background: none; border: none; padding: 0; cursor: pointer;
    color: var(--sourdine); font-family: inherit; font-size: .78rem;
    text-decoration: underline; text-underline-offset: 3px; white-space: nowrap;
}
.lien:hover { color: var(--or); }

/* ---- L'écran de connexion, seul écran sans flanc ---- */

.page-connexion {
    min-height: 100vh; display: flex; flex-direction: column;
    align-items: center; justify-content: center; padding: 2rem 1.5rem;
}
.page-connexion .entete { text-align: center; margin-bottom: 1.8rem; }
.page-connexion .sentinelle { font-size: 1.8rem; }
.page-connexion h1 { font-size: 2.4rem; letter-spacing: .12em; margin: .4rem 0 .2rem; }
.page-connexion .sous-titre { color: var(--sourdine); font-style: italic; }
.carte-connexion { width: 100%; max-width: 400px; }

/* ---- Le studio ---- */

/* ---- Les onglets, partout les mêmes ----

   **Un onglet est un onglet, quel que soit ce qu'il ouvre.** La barre
   d'un domaine, celle d'une fabrique et celles d'une modale portaient
   trois dessins différents : des pastilles détachées ici, des onglets
   rattachés là. Un même geste doit avoir une même forme, sans quoi on
   réapprend l'écran à chaque page.

   C'est le dessin des panneaux qui l'emporte, et pour une raison : un
   onglet rattaché à son contenu par le trait du bas **montre ce qu'il
   ouvre**. Une pastille détachée pourrait aussi bien être un bouton. */

nav.onglets,
.panneaux-onglets {
    display: flex;
    flex-wrap: wrap;
    gap: .3rem;
    margin-bottom: 1.1rem;
    border-bottom: 1px solid var(--bord);
}

.onglet,
.panneaux-onglets [data-panneau] {
    background: none;
    border: 1px solid transparent;
    border-bottom: 0;
    border-radius: 7px 7px 0 0;
    padding: .45rem .9rem;
    margin-bottom: -1px;
    color: var(--sourdine);
    font-size: .88rem;
    cursor: pointer;
}

.onglet:hover,
.panneaux-onglets [data-panneau]:hover { color: var(--encre); }

/* L'onglet courant se rattache à son panneau : le trait du bas s'ouvre
   sous lui, et l'on voit ce qu'on lit. */
.onglet.actif,
.panneaux-onglets [data-panneau].actif {
    color: var(--or);
    border-color: var(--bord);
    background: var(--carte);
    border-bottom: 1px solid var(--carte);
}

/* Ce qui est fermé se montre fermé, comme au flanc : un menu qui change
   de forme selon le rôle donne le sentiment d'un outil qui cache. */
.onglet-clos { color: var(--sourdine); cursor: default; }
.onglet-clos:hover { color: var(--sourdine); }

/* **Une boîte posée sur la page, pas une seconde page** (#303). Le
   voile centre dans les deux sens, et la marge est franche : une modale
   qui touche presque les bords cesse d'être un aparté. */
.voile {
    position: fixed; inset: 0; background: rgba(16, 20, 24, .84);
    display: flex; align-items: center; justify-content: center;
    padding: clamp(1.5rem, 5vh, 3.5rem) 1.5rem;
    z-index: 60;
}
.voile[hidden] { display: none; }
/* **Une modale étroite fait mentir son contenu.** À 470 pixels, un
   tableau de transitions se repliait sur trois lignes par cellule et
   une fresque n'entrait pas : on ouvrait un détail pour finir par le
   lire moins bien qu'en liste. La largeur suit désormais ce qu'on y
   met, et l'écran garde toujours sa marge.

   La confirmation reste étroite : elle porte deux phrases et deux
   boutons, et une boîte large ferait chercher le bouton. */
.modale {
    width: 100%;
    max-width: min(56rem, calc(100vw - 3rem));
    max-height: min(82vh, 54rem);
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

/* Ce qui porte une fresque ou deux tableaux prend toute la place
   disponible : un circuit de trente pas ne se lit pas en colonne. */
.modale-large { max-width: min(78rem, calc(100vw - 3rem)); background: var(--carte); }

/* ---- Les gestes ---- */

.circuit { padding: .9rem 0; border-bottom: 1px solid var(--bord); }
.circuit:last-child { border-bottom: none; }
.circuit h3 { font-size: 1.05rem; color: var(--encre); }
.ouvrir-circuit { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; margin: .9rem 0; }
.ouvrir-circuit input { max-width: 11rem; }
.ouvrir-circuit .bouton-or { margin-top: 0; }

.bloc-prepare {
    background: var(--fond); border: 1px solid var(--bord); border-radius: 8px;
    padding: .9rem 1.1rem; overflow-x: auto; overflow-y: auto; margin: .9rem 0;
    font-size: .82rem; line-height: 1.55; white-space: pre;
}
.bloc-prepare.petite { font-size: .74rem; margin: .4rem 0 0; max-width: 60ch; }

/* ---- Le studio : entêtes de part, formulaires, détails ---- */

.part-tete {
    display: flex; align-items: flex-start; justify-content: space-between;
    gap: 1rem; margin-bottom: 1.2rem; flex-wrap: wrap;
}
.part-tete h2 { margin-bottom: .2rem; }
.aide { color: var(--sourdine); font-size: .85rem; margin: 0; }

/* **Un libellé de champ est en petites capitales ; ce qui l'accompagne
   ne l'est pas.** L'aide vit dans le label — c'est ce qui la rattache à
   son champ — et héritait donc de sa casse et de son interlettrage,
   sans jamais descendre de ligne : « SON NOM LAISSÉ VIDE, IL SE DÉRIVE
   DE LA CIBLE » se lisait comme un seul libellé, et le nom technique
   d'un réglage se collait au sien en majuscules.

   C'est la règle du nom technique sous le libellé, étendue à tout ce
   qui accompagne un champ : sous lui, et dans sa propre casse. */
label .aide {
    display: block;
    text-transform: none;
    letter-spacing: normal;
    font-size: .78rem;
    margin: .2rem 0 0;
}
/* **Une couleur écrite en dur ne suit pas le thème** (#503). Celle-ci
   valait #5b6570 : bonne le jour, elle tombait à 2,9 la nuit — et le
   texte dont le métier est d'expliquer un écran vide devenait le moins
   lisible de la page. Trente-trois gabarits la portent. */
.vide { color: var(--sourdine); }
.requis { color: var(--or); font-size: .62rem; letter-spacing: .1em; }
.attente { color: var(--sourdine); }

/* **Un champ dit ce qu'il attend avant qu'on le remplisse** (#521) : sa
   nature en petit sous le libellé, l'exemple en indication grisée dans
   le champ vide, l'aide en italique dessous. Un défaut préremplit et se
   dit « proposé », à droite du champ : on le lit avant d'ouvrir, pas
   après un refus. */
.champ-nature {
    display: block; font-size: .7rem; font-style: italic;
    color: var(--sourdine); text-transform: none; letter-spacing: 0;
}
.champ-porte { display: flex; align-items: center; gap: .5rem; }
.champ-porte > input, .champ-porte > select { flex: 1 1 auto; min-width: 0; }
.champ-propose { flex: 0 0 auto; font-size: .7rem; color: var(--sourdine); }

/* **Le refus se lit sous le champ qu'il nomme** (#521) : rendu par le
   serveur après envoi, jamais en tête de formulaire, où il aurait parlé
   loin de ce qu'il faut corriger. */
.champ-refus {
    margin: .2rem 0 0; font-size: .78rem; color: var(--rouge);
}
.champ-refus::before { content: "\25B2"; margin-right: .35rem; font-size: .7em; }

.vide-parlant {
    text-align: center; padding: 2.5rem 1rem;
    border: 1px dashed var(--bord); border-radius: 10px;
}
.vide-parlant p { color: var(--sourdine); margin: 0 0 1rem; }

td.actions, th.actions { text-align: right; white-space: nowrap; }
td.actions .lien { margin-left: .8rem; }
tr.endormie { opacity: .5; }
.lien-danger:hover { color: var(--ambre); }
.en-ligne { display: inline-flex; gap: .4rem; align-items: center; margin: 0; }
.saisie-courte { max-width: 11rem; padding: .3rem .5rem; font-size: .85rem; }

/* Un intertitre de formulaire se lit comme les autres : sans classe, il
   sortirait au style natif du gabarit, gros et sérif au milieu de labels
   discrets. Les sections partagent donc la règle plutôt que de la copier. */
.parametres,
.affichages {
    margin-top: 1.2rem; padding-top: .8rem; border-top: 1px solid var(--bord);
}
.parametres h3,
.affichages h3 {
    font-size: .7rem; letter-spacing: .14em; text-transform: uppercase;
    color: var(--or); margin-bottom: .4rem;
}
.sous-modale { text-align: center; margin-top: .9rem; }

.formulaire-large .grille-champs {
    display: grid; gap: .8rem 1.2rem;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}
.formulaire-large label { margin-top: 0; }

/* Le détail d'un relevé, en clair plutôt qu'en JSON tronqué */
.releve {
    border: 1px solid var(--bord); border-radius: 8px;
    padding: .8rem 1rem; margin-bottom: .8rem;
}
.releve-dernier { border-color: var(--or); }
.releve-tete {
    display: flex; gap: .7rem; align-items: center;
    color: var(--sourdine); font-size: .8rem; margin-bottom: .5rem;
}
table.detail { font-size: .85rem; margin: .3rem 0; }
table.detail th {
    width: 12rem; color: var(--sourdine); font-size: .78rem;
    text-transform: none; letter-spacing: 0; padding: .25rem .5rem;
}
table.detail td { padding: .25rem .5rem; }
table.detail table.detail { margin-left: 0; }

/* ---- Les circuits au studio ---- */

.circuit-fiche {
    border: 1px solid var(--bord); border-radius: 10px;
    padding: 1.2rem 1.4rem; margin-bottom: 1.4rem;
}
.circuit-fiche h3 { font-size: 1.1rem; }
.circuit-fiche h4 {
    font-size: .7rem; letter-spacing: .14em; text-transform: uppercase;
    color: var(--or); margin: 1.4rem 0 .5rem;
}
.graphe-circuit {
    background: var(--fond); border: 1px solid var(--bord); border-radius: 8px;
    padding: 1rem; margin: .8rem 0; overflow-x: auto; overflow-y: auto;
}
.graphe-circuit pre {
    margin: 0; font-size: .75rem; color: var(--sourdine); white-space: pre;
}

/* ---- La rangée de tête ---- */

.rangee {
    display: grid; gap: .8rem; margin-bottom: 1.2rem;
    grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
}
.tuile-synthese {
    display: flex; flex-direction: column; gap: .1rem;
    padding: .9rem 1rem; box-shadow: var(--ombre);
}
.tuile-synthese .tuile-titre {
    font-size: .72rem; letter-spacing: .1em; text-transform: uppercase;
    color: var(--sourdine);
}
.tuile-synthese .tuile-chiffre { font-size: 1.75rem; }
.tuile-veille, .tuile-synthese.tuile-veille { border-color: var(--ambre); }
.pastille-etat {
    display: inline-block; width: .5rem; height: .5rem; border-radius: 50%;
    background: var(--vert); margin-right: .35rem; vertical-align: middle;
}
.pastille-veille { background: var(--ambre); }

/* ---- La marque ---- */

.marque-sentinelle {
    width: 26px; height: 26px; flex: 0 0 26px;
    color: var(--or);
}
/* La sentinelle monte à hauteur de capitale : la marque lui réserve son air. */
.marque { display: flex; align-items: center; gap: .55rem; padding-top: .2rem; }

.marque-grande {
    width: 15rem; max-width: 80%; height: auto;
    color: var(--encre); display: block; margin: 0 auto .4rem;
}

/* ---- La barre haute, compacte ---- */

.bandeau { display: flex; align-items: flex-start; gap: 1rem; }
.bandeau-texte { min-width: 0; }
.bandeau-note {
    margin: .2rem 0 0; color: var(--sourdine); font-size: .84rem;
}

/* ---- Le flanc qui se replie ---- */

.flanc-replie {
    width: var(--flanc-replie); flex: 0 0 var(--flanc-replie);
}
.flanc-replie .marque-nom,
.flanc-replie .marque-lignee,
.flanc-replie .menu-titre,
.flanc-replie .menu-lien span,
.flanc-replie .flanc-usager,
.flanc-replie .version { display: none; }
.flanc-replie .menu-lien,
.flanc-replie .marque { justify-content: center; padding-left: 0; padding-right: 0; }
.flanc-replie .menu-lien i { margin: 0; }

/* L'intitulé revient au survol, sans pousser la scène.

   **Une opacité nulle ne retire pas la boîte.** L'infobulle sortait du
   flanc replié en permanence, invisible mais large, et le document
   gagnait un ascenseur horizontal que personne ne reliait au repli.
   `display: none` la retire vraiment ; on y perd le fondu, ce qui est
   peu de chose contre un ascenseur qui traverse toutes les pages. */
.flanc-replie .menu-lien { position: relative; }
.flanc-replie .menu-lien::after {
    content: attr(data-libelle);
    display: none;
    position: absolute; left: calc(100% + .4rem); top: 50%;
    transform: translateY(-50%);
    padding: .3rem .6rem; border-radius: 6px;
    background: var(--carte-haute); border: 1px solid var(--bord);
    color: var(--encre); font-size: .78rem; white-space: nowrap;
    pointer-events: none; z-index: 50;
}
.flanc-replie .menu-lien:hover::after { display: block; }

.replier {
    display: flex; align-items: center; justify-content: center; gap: .4rem;
    width: 100%; margin-top: auto; padding: .55rem 0;
    background: none; border: none; border-top: 1px solid var(--bord);
    color: var(--sourdine); cursor: pointer; font-size: .8rem;
    font-family: inherit;
}
.replier:hover { color: var(--or); }

.theme-bascule {
    background: none; border: 1px solid var(--bord); border-radius: 999px;
    color: var(--sourdine); cursor: pointer; font-size: .9rem;
    width: 2.1rem; height: 2.1rem; display: grid; place-items: center;
    margin-left: auto;
}
.theme-bascule:hover { color: var(--or); border-color: var(--or); }

/* ---- Les sections repliées du studio ---- */

.repli { border-top: 1px solid var(--bord); }
.repli:first-of-type { border-top: none; }
.repli summary {
    display: flex; align-items: center; gap: .7rem;
    padding: .8rem .2rem; cursor: pointer; list-style: none;
}
.repli summary::-webkit-details-marker { display: none; }
.repli summary::before {
    content: '\f105'; font-family: 'Font Awesome 6 Free'; font-weight: 900;
    color: var(--sourdine); transition: transform .15s ease; width: .7rem;
}
.repli[open] summary::before { transform: rotate(90deg); color: var(--or); }
.repli-titre { font-size: 1rem; }
.repli-compte { color: var(--sourdine); font-size: .78rem; }
.repli-etat { margin-left: auto; }
.repli-corps { padding: .2rem .2rem 1.2rem; }

/* ---- Le studio en lecture seule ---- */

.studio-lecture .bouton-or,
.studio-lecture [data-modale],
.studio-lecture [data-regler-sentinel],
.studio-lecture .formulaire-ligne,
.studio-lecture form[data-geste] { display: none; }

/* ---- Le secret qui ne passe qu'une fois ---- */

.bloc-alerte { border-color: var(--or); }
.secret {
    margin: .8rem 0; padding: .9rem 1rem; border-radius: 6px;
    background: var(--fond); border: 1px solid var(--or);
    color: var(--or); font-size: .82rem; word-break: break-all; white-space: pre-wrap;
}

/* ---- Le fil d'une instance ---- */

.frise {
    display: flex; flex-wrap: wrap; gap: .35rem; align-items: center;
    list-style: none; margin: .9rem 0 1.4rem; padding: 0;
}
.frise-place {
    padding: .28rem .7rem; border-radius: 999px; font-size: .76rem;
    border: 1px solid var(--bord); color: var(--sourdine);
}
.frise-franchie { border-color: var(--vert); color: var(--vert); }
.frise-courante {
    border-color: var(--or); color: var(--fond); background: var(--or); font-weight: 500;
}

/* **Une liste qui sert à choisir n'a pas de puces.** Le choix se fait
   sur le libellé, et une puce n'ajoute rien qu'un cran d'indentation.
   Vérifié avant de la poser : rien d'équivalent n'existait, chaque
   liste nue de cette feuille était nommée pour son propre objet. */
.liste-nue { list-style: none; margin: .6rem 0 0; padding: 0; }
.liste-nue li { padding: .3rem 0; }

.parcours { list-style: none; margin: 0; padding: 0; }
.pas {
    border-left: 2px solid var(--bord); padding: .1rem 0 1.1rem 1rem;
    margin-left: .4rem; position: relative;
}
.pas::before {
    content: ''; position: absolute; left: -5px; top: .45rem;
    width: 8px; height: 8px; border-radius: 50%; background: var(--bord);
}
.pas-franchi { border-left-color: var(--vert); }
.pas-franchi::before { background: var(--vert); }
.pas-courant { border-left-color: var(--or); }
.pas-courant::before { background: var(--or); }
/* **Un pas en échec se voit comme une panne** (#502) : le parcours le
   rendait vert, du seul fait qu'une ligne de journal existait. Un pas
   engagé sans conclure prend le ton de l'attente, celui du pas courant. */
.pas-echec { border-left-color: var(--rouge); }
.pas-echec::before { background: var(--rouge); }
.pas-attente { border-left-color: var(--ambre); }
.pas-attente::before { background: var(--ambre); }
.pas-a-venir, .pas-obsolete { opacity: .55; }
.pas-tete { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; }
.pas-quand { color: var(--sourdine); font-size: .76rem; margin-left: auto; }
.pas-executant { color: var(--sourdine); font-size: .8rem; margin-top: .3rem; }
.pas-sortie { margin-top: .5rem; }
.pas-sortie summary { cursor: pointer; color: var(--sourdine); font-size: .78rem; }
.pas-sortie pre {
    margin: .4rem 0 0; padding: .7rem; border-radius: 6px;
    background: var(--fond); border: 1px solid var(--bord);
    font-size: .74rem; color: var(--sourdine); white-space: pre-wrap;
}
.pas-motif { margin: .5rem 0 0; font-size: .8rem; color: var(--encre); }
.pas-main { margin-top: .7rem; display: grid; gap: .5rem; }
.pas-main textarea { width: 100%; }

.fiche { display: grid; grid-template-columns: auto 1fr; gap: .3rem .9rem; margin: 0 0 1rem; }
.fiche dt { color: var(--sourdine); font-size: .8rem; }
.fiche dd { margin: 0; font-size: .85rem; }

/* ---- Les mesures, lisibles ---- */

/* **La grille appartient à la liste, pas à la ligne.** Chaque mesure
   était un flex isolé qui se calait sur sa propre largeur : les valeurs
   s'alignaient à droite de leur ligne, jamais entre elles. On ne pouvait
   pas comparer deux mesures sans les lire une par une.

   Les lignes s'effacent, leurs deux cellules tombent dans les colonnes de
   la liste, et toutes les valeurs partagent enfin la même colonne. */
.mesures {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: .18rem .8rem;
    align-items: baseline;
    font-size: .82rem;
    line-height: 1.4;
}

.mesure { display: contents; }

.mesure-nom {
    color: var(--sourdine);
    /* Un nom trop long coupe plutôt que d'écarter la colonne des
       valeurs : c'est la valeur qu'on vient lire. */
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Les chiffres en chasse fixe et calés à droite : sans cela « 9 » et
   « 11 » ne tombent pas sous le même bord, et la colonne se relit. */
.mesure-valeur {
    justify-self: end;
    text-align: right;
    color: var(--encre);
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}
.mesure-valeur small { color: var(--sourdine); }
.mesure-seuil .mesure-valeur { color: var(--ambre); }
.mesure-groupe { justify-self: end; }
.mesure-groupe summary {
    cursor: pointer; color: var(--sourdine); white-space: nowrap;
    list-style-position: inside;
}
.mesure-groupe[open] summary { color: var(--or); margin-bottom: .3rem; }
.mesure-groupe .mesures { border-left: 1px solid var(--bord); padding-left: .6rem; }

/* ---- Le vocabulaire et les tableaux réglables ---- */

.saisie-mini { max-width: 5.5rem; padding: .3rem .45rem; font-size: .85rem; }
.bloc-entete {
    display: flex; align-items: baseline; justify-content: space-between;
    gap: 1rem; margin-bottom: .2rem; flex-wrap: wrap;
}
.bloc-entete h2 { margin: 0; }
/* **Ce qui agit sur une section vit dans son en-tête**, pas sous ce
   qu'elle contient : un bouton posé après un tableau passe sous la
   ligne de flottaison dès que le tableau s'allonge. Le geste existe
   encore, mais il ne se voit plus. */
.bloc-entete .bloc-outils { display: flex; gap: .5rem; align-items: center; }
.formulaire-ligne {
    display: flex; flex-wrap: wrap; gap: .5rem; align-items: center;
    margin-top: .9rem; padding-top: .9rem; border-top: 1px dashed var(--bord);
}
.actions-ligne { display: flex; gap: .7rem; align-items: center; white-space: nowrap; }
.ligne-eteinte { opacity: .55; }
.tuile-chiffre { font-size: 1.9rem; line-height: 1.15; display: block; }
.tuile-chiffre small { font-size: .95rem; color: var(--sourdine); }
.tuile-note { color: var(--sourdine); font-size: .76rem; margin-top: .5rem; display: block; }
.tuile-veille { border-color: var(--ambre); }

/* ---- La porte close, vue de l'intérieur ---- */

.avis-maintenance {
    display: flex; align-items: center; gap: .8rem;
    padding: .7rem 1.6rem;
    background: var(--ambre-fond); border-bottom: 1px solid var(--ambre);
    color: var(--encre); font-size: .85rem;
}
.avis-maintenance i { color: var(--ambre); font-size: 1.1rem; }
.avis-maintenance strong { color: var(--ambre); }
.avis-maintenance a {
    margin-left: auto; white-space: nowrap;
    color: var(--ambre); text-decoration: none; border-bottom: 1px solid currentColor;
}

.avis-incarnation {
    display: flex; align-items: center; gap: .8rem;
    padding: .7rem 1.6rem;
    background: var(--carte-haute); border-bottom: 1px solid var(--or);
    color: var(--encre); font-size: .85rem;
}
.avis-incarnation i { color: var(--or); font-size: 1.1rem; }
.avis-incarnation strong { color: var(--or); }
.avis-incarnation a {
    margin-left: auto; white-space: nowrap;
    color: var(--or); text-decoration: none; border-bottom: 1px solid currentColor;
}

/* ---- Étroit ---- */

@media (max-width: 900px) {
    .flanc {
        position: fixed; z-index: 40; left: 0; top: 0;
        transform: translateX(-100%); transition: transform .18s ease;
    }
    .flanc.ouvert { transform: translateX(0); }
    .bascule { display: block; }
    .bandeau, .scene-corps { padding-left: 1.2rem; padding-right: 1.2rem; }
    .tuiles { grid-template-columns: 1fr; }
}

/* ---- La matrice des droits ---- */

.colonne-profil { text-align: center; width: 6rem; }
.pastille-droit {
    width: 15px; height: 15px; border-radius: 50%; cursor: pointer;
    background: none; border: 2px solid var(--bord); padding: 0;
}
.pastille-droit.accorde { background: var(--vert); border-color: var(--vert); }
.pastille-droit:hover { border-color: var(--or); }
.ligne-orpheline { background: color-mix(in srgb, var(--ambre) 10%, transparent); }

/* ---- La connexion, avant qu'on sache qui vient ---- */

/* Le document sort sans attribut de thème : c'est la préférence du
   système qui tranche, en CSS pur, donc sans JavaScript et sans saut.
   Les variables du jour sont recopiées ici plutôt que réutilisées :
   `[data-theme="jour"]` ne s'applique pas à un document qui n'a pas
   l'attribut, et une requête de média ne sait pas hériter d'un
   sélecteur. */
@media (prefers-color-scheme: light) {
    html:not([data-theme]) {
        --fond: #f4f2ec;
        --carte: #ffffff;
        --carte-haute: #faf8f3;
        --bord: #ddd8cc;
        --encre: #24282c;
        --sourdine: #686d74;
        --or: #8a6d18;
        --vert: #2f7a4a;
        --vert-fond: #e6f1e9;
        --ambre: #8e6211;
        --ambre-fond: #f8efdc;
        --ombre: 0 1px 2px rgba(36, 40, 44, .06);

        --bs-body-bg: #f4f2ec;
        --bs-body-color: #24282c;
        --bs-border-color: #ddd8cc;
        --bs-emphasis-color: #24282c;
        --bs-secondary-color: #686d74;

        color-scheme: light;
    }
}

/* Le miroir : ce qui appartient à son occupant.
   Deux colonnes sur grand écran, une seule dès qu'on descend, et le
   bloc des jetons qui prend toute la largeur parce qu'il porte une
   table. */
.miroir {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(22rem, 1fr));
    gap: 1rem;
    align-items: start;
}

.miroir-large { grid-column: 1 / -1; }

.miroir-visage {
    display: flex;
    gap: 1.2rem;
    align-items: flex-start;
    margin-bottom: 1.2rem;
    padding-bottom: 1.2rem;
    border-bottom: 1px solid var(--bord);
}

.miroir-portrait { flex: 0 0 auto; }
.miroir-charger { flex: 1 1 auto; min-width: 12rem; }
.miroir-charger input[type="file"] { font-size: .82rem; }

/* Prenom et nom vont ensemble : deux champs courts sur une ligne, une
   colonne des que l'ecran se serre. */
.miroir-paire {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
    gap: 0 .8rem;
}

/* Les reseaux : autant de lignes que le vocabulaire en declare, jamais
   une de plus. L'icone tient la colonne de gauche pour que la sentinelle suive
   la liste sans lire. */
.miroir-liens {
    display: flex;
    flex-direction: column;
    gap: .4rem;
    margin-bottom: .8rem;
}

.miroir-lien {
    display: grid;
    grid-template-columns: 1.6rem 6.5rem 1fr;
    align-items: center;
    gap: .5rem;
}

.miroir-lien-icone {
    color: var(--sourdine);
    text-align: center;
}

.miroir-lien label { margin: 0; }
.miroir-lien input { margin: 0; }

@media (max-width: 30rem) {
    .miroir-lien { grid-template-columns: 1.6rem 1fr; }
    .miroir-lien label { grid-column: 2; }
    .miroir-lien input { grid-column: 1 / -1; }
}

.miroir-reglage {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: .5rem 0;
    border-bottom: 1px solid var(--bord);
}

.miroir-reglage span { color: var(--encre); }
.miroir-reglage form { margin: 0; }

/* Un champ qui ne se change pas se montre comme tel : ni bordure ni
   fond, pour qu'on n'essaie pas d'y écrire. */
.fige {
    margin: .2rem 0 .8rem;
    display: flex;
    flex-direction: column;
    gap: .2rem;
}

.bouton-sobre {
    background: transparent;
    color: var(--encre);
    border: 1px solid var(--bord);
    border-radius: 5px;
    padding: .3rem .8rem;
    cursor: pointer;
    font-family: inherit;
    font-size: .9rem;
}

.bouton-danger {
    background: transparent;
    color: var(--rouge);
    border: 1px solid var(--rouge);
    border-radius: 5px;
    padding: .3rem .8rem;
    cursor: pointer;
    font-family: inherit;
    font-size: .9rem;
}

.bouton-sobre:hover { border-color: var(--or); color: var(--or); }
.bouton-danger:hover { border-color: var(--or);  }

/* Le visage, engendré ou chargé : la même place dans les deux cas, sans
   quoi les lignes d'une table sauteraient selon qui a un portrait. */
.visage {
    display: inline-flex;
    vertical-align: middle;
    border-radius: 50%;
    overflow: hidden;
    flex: 0 0 auto;
}

.visage svg { display: block; }
img.visage { object-fit: cover; }

.flanc-usager .visage { margin-right: .5rem; }
.flanc-usager { display: flex; text-decoration: none; }
.flanc-usager:hover { color: var(--or); }

td .visage, th .visage { margin-right: .5rem; }

/* La bibliothèque : les lots à gauche, ce qu'ils proposent à droite. */
.biblio {
    display: grid;
    grid-template-columns: 15rem 1fr;
    gap: 1rem;
    align-items: start;
}

@media (max-width: 860px) {
    .biblio { grid-template-columns: 1fr; }
}

.biblio-lots {
    background: var(--carte);
    border: 1px solid var(--bord);
    border-radius: 10px;
    padding: 1rem;
    position: sticky;
    top: 1rem;
}

.biblio-lots h3 {
    margin: 0 0 .6rem;
    font-size: .78rem;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: var(--sourdine);
    font-weight: normal;
}

.biblio-lot {
    display: block;
    padding: .5rem .6rem;
    margin-bottom: .4rem;
    border: 1px solid var(--bord);
    border-radius: 6px;
    text-decoration: none;
    color: var(--encre);
}

.biblio-lot span { display: block; font-size: .8rem; color: var(--sourdine); }
.biblio-lot:hover { border-color: var(--or); }
.biblio-lot.actif { border-color: var(--or); background: var(--carte-haute); }
.biblio-lot.actif strong { color: var(--or); }

.biblio-proposition { margin-bottom: .8rem; }

.biblio-tete {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: 1rem;
}

.biblio-tete form { margin: 0; flex: 0 0 auto; }
.biblio-type { color: var(--sourdine); font-size: .82rem; margin: .4rem 0 0; }

.biblio-apercu {
    margin-top: .7rem;
    padding-top: .7rem;
    border-top: 1px solid var(--bord);
}

.biblio-apercu-titre {
    font-size: .72rem;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: var(--or);
}

.biblio-apercu pre {
    background: var(--fond);
    border: 1px solid var(--bord);
    border-radius: 6px;
    padding: .6rem .8rem;
    margin: .4rem 0 0;
    font-size: .8rem;
    overflow-x: auto;
    overflow-y: auto;
}

/* Ce qui est déjà ouvert se montre sans s'offrir : on le voit, on ne
   peut pas l'activer deux fois. */
.biblio-proposition.endormie { opacity: .55; }

/* Le premier jour : un accueil vide passerait pour une panne. */
.premier-pas {
    display: flex;
    gap: 1.4rem;
    align-items: center;
    border-color: var(--or);
}

.premier-pas-sentinelle { color: var(--or); flex: 0 0 auto; }
.premier-pas strong { font-size: 1.15rem; color: var(--encre); }
.premier-pas p { margin: .4rem 0 0; color: var(--sourdine); }
.premier-pas p:last-child { display: flex; gap: .8rem; align-items: center; flex-wrap: wrap; }
.premier-pas .bouton-or { text-decoration: none; display: inline-block; }

@media (max-width: 640px) {
    .premier-pas { flex-direction: column; align-items: flex-start; }
}

/* L'aperçu de ce qui sera posé : lisible, pas du JSON recraché. */
.biblio-valeurs {
    margin: .5rem 0 0;
    display: grid;
    grid-template-columns: auto 1fr;
    gap: .25rem .9rem;
    font-size: .84rem;
}

.biblio-valeurs dt { color: var(--sourdine); }
.biblio-valeurs dd { margin: 0; color: var(--encre); }
.biblio-valeurs code { font-size: .8rem; }

/* Le portail : ce qu'un domaine porte, d'un regard. */
.portail {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
    gap: .8rem;
}

.portail-tuile {
    display: flex;
    flex-direction: column;
    gap: .15rem;
    padding: .9rem 1rem;
    background: var(--carte);
    border: 1px solid var(--bord);
    border-radius: 10px;
    text-decoration: none;
}

.portail-tuile:hover { border-color: var(--or); }

.portail-libelle { color: var(--encre); font-size: .95rem; }
.portail-chiffre { color: var(--or); font-size: 1.7rem; line-height: 1.1; }
.portail-unite { color: var(--sourdine); font-size: .82rem; }
.portail-note { color: var(--sourdine); font-size: .78rem; margin-top: .2rem; }

.portail-sain .portail-note { color: var(--vert); }
.portail-veille { border-color: var(--ambre); }
.portail-veille .portail-note { color: var(--ambre); }

/* Le menu à trois niveaux : le troisième se déplie quand on est dans le
   domaine, sinon quinze lignes tiendraient dans un flanc de 236 pixels. */
.menu-domaine {
    display: flex;
    align-items: center;
    justify-content: space-between;
}

/* **Le DOM porte tous les sous-menus, le style choisit lequel se
   montre.** Le gabarit ne rendait que celui du groupe courant : replié,
   le flanc n'avait donc rien à déplier au survol des autres (#251).
   Déplié, la règle est la même qu'avant — on voit où l'on est, et rien
   d'autre. */
.menu-sous {
    display: none;
    flex-direction: column;
    margin: .1rem 0 .4rem 1.1rem;
    padding-left: .7rem;
    border-left: 1px solid var(--bord);
}

.menu-groupe.dedans .menu-sous { display: flex; }

.menu-sous a {
    color: var(--sourdine);
    text-decoration: none;
    font-size: .84rem;
    padding: .22rem 0;
}

.menu-sous a:hover { color: var(--or); }
.menu-sous a.actif { color: var(--encre); border-left: 2px solid var(--or); margin-left: -.72rem; padding-left: .6rem; }

/* Le pied se replie comme le reste : il gardait sa largeur et provoquait
   un ascenseur en bas. */
.flanc-replie .flanc-pied { align-items: center; }
.flanc-replie .flanc-usager span:not(.visage),
.flanc-replie .menu-lien span,
.flanc-replie .version span { display: none; }
.flanc-replie .flanc-pied .menu-lien,
.flanc-replie .flanc-usager { justify-content: center; padding-left: 0; padding-right: 0; }
/* **Replier fait gagner de la place, jamais perdre la navigation.**
   Une seule règle — `display: none` — faisait disparaître les
   sous-entrées, y compris celles du groupe où l'on se trouve : depuis
   un atelier, on ne voyait plus les autres ateliers (#251).

   Le volet reprend le mécanisme de l'infobulle des libellés plutôt que
   d'en inventer un second : sorti à droite, en `display: none` et non
   en opacité nulle — une boîte invisible mais large créait un
   ascenseur horizontal sur toutes les pages, et c'est la leçon que le
   fichier porte déjà plus haut. */
/* **Replié, même le groupe courant passe par le volet.** Sans cette
   spécificité, la règle du dépli (`.menu-groupe.dedans .menu-sous`,
   trois classes) l'emportait sur la règle du repli (deux classes) : le
   sous-menu du groupe où l'on se trouve restait ouvert en permanence,
   écrasé dans soixante-deux pixels. */
.flanc-replie .menu-groupe .menu-sous,
.flanc-replie .menu-groupe.dedans .menu-sous {
    display: none;
    position: absolute;
    left: calc(100% + .4rem);
    top: 0;
    min-width: 190px;
    margin: 0;
    padding: .5rem .8rem .6rem;
    border-left: 0;
    border-radius: 8px;
    background: var(--carte-haute);
    border: 1px solid var(--bord);
    z-index: 60;
}

/* Le titre du groupe coiffe le volet : une icône seule ne dit pas d'où
   l'on vient. */
.flanc-replie .menu-sous::before {
    content: attr(data-groupe);
    display: block;
    margin-bottom: .35rem;
    padding-bottom: .3rem;
    border-bottom: 1px solid var(--bord);
    color: var(--or);
    font-size: .72rem;
    letter-spacing: .07em;
    text-transform: uppercase;
}

.flanc-replie .menu-sous a { white-space: nowrap; }

/* L'entrée qui porte le volet devient son ancrage, et le volet
   s'ouvre au survol comme au focus — le clavier ne perd rien de ce que
   la souris gagne. */
.flanc-replie .menu-groupe { position: relative; }
.flanc-replie .menu-groupe:hover .menu-sous,
.flanc-replie .menu-groupe:focus-within .menu-sous,
.flanc-replie .menu-groupe.dedans:hover .menu-sous,
.flanc-replie .menu-groupe.dedans:focus-within .menu-sous { display: block; }

/* ---- Piloter : toujours le meme ecran ----
   Recherche, filtres declares, une seule colonne qui agit. On l'apprend
   une fois, il sert partout. */
.pilotage-tete {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1rem;
    margin-bottom: .8rem;
}

.pilotage-tete h2 { margin: 0; }
.pilotage-outils { flex: 0 0 auto; }

.pilotage-barre {
    display: flex;
    align-items: center;
    gap: .5rem;
    flex-wrap: wrap;
    margin-bottom: .7rem;
}

.pilotage-recherche {
    display: flex;
    align-items: center;
    gap: .45rem;
    padding: 0 .6rem;
    background: var(--fond);
    border: 1px solid var(--bord);
    border-radius: 6px;
    color: var(--sourdine);
}

.pilotage-recherche input {
    border: 0;
    background: transparent;
    padding: .38rem 0;
    min-width: 14rem;
    color: var(--encre);
}

.pilotage-recherche input:focus { outline: none; }
.pilotage-recherche:focus-within { border-color: var(--or); }

.pilotage-barre select {
    padding: .38rem .5rem;
    background: var(--fond);
    border: 1px solid var(--bord);
    border-radius: 6px;
    color: var(--encre);
    font-size: .86rem;
}

.pilotage-compte {
    margin-left: auto;
    color: var(--sourdine);
    font-size: .82rem;
}

/* **L'arbre du plan se lit à son décalage** (#301). Un « └ » disait
   qu'un nœud avait un parent, jamais lequel, et il valait le même à
   tous les niveaux. Le filet part de la marge et s'allonge d'un cran
   par génération : la parenté se voit sans se lire. */
.plan-filet {
    display: inline-block;
    width: calc(var(--niveau, 1) * 1.15rem);
    border-bottom: 1px solid var(--bord);
    margin-right: .45rem;
    vertical-align: middle;
}

/* ─── Les groupes d'un tableau de pilotage (#304) ─────────────────

   Le propos se dit une fois, en tête ; les lignes ne portent plus que
   leur différence. Un seul tableau, des colonnes alignées d'un bout à
   l'autre : un tableau par groupe aurait donné autant de grilles que de
   moules, et rien à comparer. */
.pilotage-groupe {
    cursor: pointer;
    user-select: none;
    background: var(--carte-haute);
}

.pilotage-groupe:hover { background: var(--carte-haute); }
.pilotage-groupe:focus-visible { outline: 2px solid var(--or); outline-offset: -2px; }

/* **Un td en display flex cesse d'etre une cellule de tableau**, et son
   colspan est purement ignore : la tete de groupe retombait a la largeur de
   la premiere colonne, 149 px sur un cadre de 1252, et les libelles se
   cassaient en vertical. Le flex appartient a un conteneur INTERIEUR a la
   cellule, jamais a la cellule elle-meme (#419). */
.pilotage-groupe td {
    padding: .5rem .8rem;
}

.pilotage-groupe-corps {
    display: flex;
    align-items: center;
    gap: .6rem;
}

/* La flèche dit l'état avant qu'on lise le compte. */
.pilotage-groupe-fleche {
    width: 0; height: 0;
    border-left: 5px solid transparent;
    border-right: 5px solid transparent;
    border-top: 6px solid var(--or);
    flex: 0 0 auto;
    transition: transform .12s ease;
}

.pilotage-groupe.replie .pilotage-groupe-fleche {
    transform: rotate(-90deg);
    border-top-color: var(--sourdine);
}

.pilotage-groupe-nom { font-weight: 600; color: var(--encre); }

.pilotage-groupe-compte {
    font-size: .76rem;
    color: var(--sourdine);
    background: var(--fond);
    border-radius: 9px;
    padding: .1rem .55rem;
}

/* Une recherche qui ne trouve rien le dit : un tableau qui se vide sans
   un mot passe pour une panne. */
.pilotage-rien {
    margin: .8rem 0 0;
    padding: .8rem;
    text-align: center;
    color: var(--sourdine);
    border: 1px dashed var(--bord);
    border-radius: 8px;
}

/* Le coin d'action : un bouton, un menu. Cinq liens alignes se relisent
   a chaque ligne ; un coin se lit une fois. */
.coin-action { position: relative; display: inline-block; }

.coin-bouton {
    border: 1px solid transparent;
    background: transparent;
    color: var(--sourdine);
    border-radius: 5px;
    padding: .1rem .45rem;
    cursor: pointer;
    line-height: 1.2;
}

tr:hover .coin-bouton, .coin-bouton:focus-visible {
    color: var(--or);
    border-color: var(--bord);
}

.coin-menu {
    position: absolute;
    right: 0;
    top: calc(100% + .25rem);
    z-index: 40;
    min-width: 12rem;
    padding: .3rem;
    background: var(--carte-haute);
    border: 1px solid var(--or);
    border-radius: 8px;
    display: flex;
    flex-direction: column;
    text-align: left;
}

.coin-menu a {
    padding: .38rem .55rem;
    border-radius: 5px;
    color: var(--encre);
    text-decoration: none;
    font-size: .87rem;
}

/* **Le geste principal ne se lit pas comme un geste éteint.** Un bouton
   dans ce menu — « Le publier » — héritait du gris de `.lien` quand ses
   voisins étaient des liens de menu : il paraissait désactivé alors qu il
   fonctionnait (#435).

   **Et il s'habille du fait d'être là, sans classe à poser** (#446). La
   règle ne visait que `button.lien` : un geste écrit sans classe sortait
   au style natif du navigateur — pavé clair, bordure système — en plein
   thème sombre, et rien dans le gabarit ne le montrait. Un geste ne
   dépend pas de la mémoire de qui l'écrit ; la classe reste pour ce qui
   s'écarte, jamais pour obtenir l'ordinaire. */
.coin-menu button {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    width: 100%;
    justify-content: flex-start;
    padding: .38rem .55rem;
    border: none;
    border-radius: 5px;
    background: none;
    color: var(--encre);
    font-family: inherit;
    font-size: .87rem;
    text-align: left;
    text-decoration: none;
    cursor: pointer;
}

.coin-menu button:hover { background: var(--carte); color: var(--or); }

.coin-menu a:hover { background: var(--carte); color: var(--or); }

/* **Un menu qui n'a rien à offrir dit pourquoi** (#440). La mention se
   lit comme un geste, sans en être un : même retrait, même hauteur, et
   pas de curseur qui promette un clic. Elle portait un style en
   attribut dans deux gabarits, avec deux valeurs différentes. */
.coin-menu .coin-dire { display: block; padding: .38rem .55rem; font-size: .87rem; }
.coin-menu a.lien-danger:hover { color: var(--rouge); }

/* ---- La fresque d'un moule ----
   Ce qu'un pas attend de la cible : le contrat de l'instanciation, qui
   ne se lisait nulle part avant.

   **Elle se centre et s'encadre, partout où on la consulte.** Collée à
   gauche et sans bord, elle se confondait avec le texte qui la précède :
   on ne voyait pas où le circuit commence ni où il finit. DNA cadre sa
   frise à quatre-vingt-dix pour cent et la centre — c'est ce qui la fait
   lire comme un objet et non comme un paragraphe de plus.

   Le cadre vaut d'autant plus qu'elle paraît à trois endroits : au moule
   dans la fabrique, à l'instance dans son détail, et dans une modale.
   Trois contextes, une seule forme. */
.fresque {
    width: min(100%, 62rem);
    margin: 0 auto 1.2rem;
    padding: 1.1rem 1.3rem;
    background: var(--fond);
    border: 1px solid var(--bord);
    border-radius: 10px;
}

/* Le fil des places, en tête, se centre aussi : c'est le résumé du
   circuit, et un résumé aligné à gauche sous un cadre centré donne
   l'impression de deux objets. */
.fresque > .frise {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: .4rem;
    list-style: none;
    margin: 0 0 1rem;
    padding: 0 0 .9rem;
    border-bottom: 1px dashed var(--bord);
}

.fresque .frise-place {
    padding: .25rem .6rem;
    background: var(--carte-haute);
    border: 1px solid var(--bord);
    border-radius: 999px;
    font-size: .82rem;
}

/* Chaque pas dans sa boîte, bordé du côté de son état : c'est le motif
   de DNA, et il rend le parcours lisible sans lire les libellés. */
/* **Le fil qui fait la frise.** Encadrer chaque pas a coupé la ligne
   verticale en segments : on avait des boîtes empilées, plus un
   parcours. Or c'est le fil continu qui dit que ces pas se suivent — et
   c'est ce que DNA fait depuis cinq ans avec sa timeline.

   Le fil passe derrière les pastilles, les pastilles le percent : on lit
   un chemin, et chaque arrêt s'y accroche. */
.fresque .parcours {
    list-style: none;
    margin: 0;
    padding: 0 0 0 1.6rem;
    position: relative;
}

.fresque .parcours::before {
    content: '';
    position: absolute;
    left: .34rem;
    top: .55rem;
    bottom: .55rem;
    width: 2px;
    background: var(--bord);
}

/* La pastille d'un pas, posée sur le fil. Elle prend la couleur de son
   état : le parcours se lit de haut en bas sans lire les libellés. */
.fresque .pas::before {
    content: '';
    position: absolute;
    left: -1.6rem;
    top: .85rem;
    width: .78rem;
    height: .78rem;
    border-radius: 999px;
    background: var(--fond);
    border: 2px solid var(--sourdine);
}

.fresque .pas-franchi::before { border-color: var(--vert); background: var(--vert); }
.fresque .pas-courant::before { border-color: var(--or); background: var(--or); }
.fresque .pas-a-venir::before { border-color: var(--bord); }

.fresque .pas {
    background: var(--carte);
    border: 1px solid var(--bord);
    border-left: 3px solid var(--sourdine);
    border-radius: 8px;
    padding: .65rem .85rem;
    margin-bottom: .5rem;
}
.fresque .pas:last-child { margin-bottom: 0; }

.fresque .pas-franchi { border-left-color: var(--vert); }
.fresque .pas-courant { border-left-color: var(--or); background: var(--carte-haute); }
.fresque .pas-a-venir { border-left-color: var(--bord); }
.fresque .pas-echec { border-left-color: var(--rouge); background: var(--rouge-fond); }
.fresque .pas-echec::before { border-color: var(--rouge); background: var(--rouge); }
.fresque .pas-attente { border-left-color: var(--ambre); }
.fresque .pas-attente::before { border-color: var(--ambre); background: var(--ambre); }

.fresque .pas-tete {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: .8rem;
    flex-wrap: wrap;
}

.fresque .pas-quand { font-size: .78rem; color: var(--sourdine); }

.pas-jetons {
    margin: .35rem 0 0;
    font-size: .82rem;
    color: var(--sourdine);
}

.pas-jetons code {
    background: var(--fond);
    border: 1px solid var(--bord);
    border-radius: 4px;
    padding: .05rem .3rem;
    color: var(--or);
}

/* Les pas d'un moule n'ont pas d'histoire : leur bandeau d'etat serait
   quatre fois « a venir », ce qui n'apprend rien. */
.fresque .pas-a-venir { opacity: 1; }

/* Une epreuve n est pas une panne : couleur d attente, pas de danger. */
.verdict-epreuve {
    border-left: 3px solid var(--or);
    background: var(--carte-haute);
}

/* Le menu d actions de la barre, declare par le domaine. Les boutons de
   creation vivaient dans les parts : on ne savait pas ou chercher. */
.barre-gestes {
    margin-left: auto; display: flex; align-items: center; gap: .6rem;
}
.barre-gestes .lien { font-size: .82rem; }
.barre-gestes .bouton-or { padding: .35rem .8rem; font-size: .82rem; }
.barre-gestes i { margin-right: .35rem; }

/* La modale qui dit ce qu un geste va faire avant de le faire. Un geste
   irreversible ne se distingue pas d un autre au clic : meme taille,
   meme distance du pouce, et l un des deux ne se defait pas. */
.modale-alerte { max-width: 26rem; }
/* Le titre d'une alerte vit dans `.modale-tete` depuis le #477, et cette
   tete-la met ses titres a zero. La regle qui donnait ici une marge basse
   au h2 ne s'appliquait plus a rien : retiree au #499. */
/* **Le corps d'une alerte s'aligne sur sa tete** (#499). Le corps commun
   ecarte un demi-pas de plus que la tete, pour donner sa mesure de
   lecture a un long texte ; sur vingt-six rem il ne reste qu'un
   decalage, 35 pixels contre 28, entre le titre et la phrase. */
.modale-alerte .modale-corps { padding: 1.4rem 1.75rem; }

/* Ce bloc declarait ici un pied de modale entier — position, ecart, et
   une marge haute de 1.2rem — au temps ou l'alerte n'avait pas le cadre
   commun. Le vrai pied se regle plus bas, avec son fond et son padding ;
   seule la marge haute survivait a la redeclaration, et elle s'ajoutait
   au padding bas du corps : 47 pixels de vide sous le texte, dans toutes
   les modales. Retiree au #499. */

/* ══════════════════════════════════════════════════════════════════
   L'ÉCRAN D'ÉTAT
   Voir la page « L'écran d'état » du wiki pour l'étude dont cette
   disposition sort.
   ══════════════════════════════════════════════════════════════════ */

/* Le bandeau : le premier objet de la page, lisible sans lire.
   La couleur porte l'information, le texte la confirme — jamais
   l'inverse. */
.etat-verdict {
    display: flex;
    align-items: center;
    gap: 1.1rem;
    padding: 1.1rem 1.3rem;
    margin-bottom: 1.3rem;
    border-radius: 12px;
    border: 1px solid var(--bord);
    border-left-width: 5px;
    background: var(--carte);
}

.etat-sentinelle { flex: 0 0 auto; width: 42px; height: 42px; }
.etat-sentinelle svg { width: 100%; height: 100%; display: block; }

.etat-dire { flex: 1 1 auto; min-width: 0; }
.etat-titre { display: block; font-size: 1.18rem; line-height: 1.25; }
.etat-detail { margin: .3rem 0 0; font-size: .88rem; color: var(--sourdine); }

/* L'âge, à droite : un écran de supervision qui ne dit pas son âge ne
   distingue pas une plateforme calme d'une collecte morte. */
.etat-age {
    flex: 0 0 auto;
    text-align: right;
    padding-left: 1rem;
    border-left: 1px solid var(--bord);
}
.etat-age-chiffre { display: block; font-size: .95rem; }
.etat-age-note { display: block; font-size: .76rem; color: var(--sourdine); margin-top: .15rem; }

.etat-sain   { border-left-color: var(--vert); }
.etat-sain .etat-sentinelle { color: var(--vert); }
.etat-veille { border-left-color: var(--ambre); }
.etat-veille .etat-sentinelle { color: var(--ambre); }
.etat-panne  { border-left-color: var(--rouge); }
.etat-panne .etat-sentinelle { color: var(--rouge); }
.etat-muet   { border-left-color: var(--sourdine); }
.etat-muet .etat-sentinelle { color: var(--sourdine); }

/* La frise : une ligne par sentinelle, une barre par relevé.
   **La hauteur ne varie pas.** Seule la couleur porte l'information :
   une hauteur inviterait à comparer des grandeurs qui ne se comparent
   pas d'une sentinelle à l'autre. */
/* **Deux frises portaient le même nom.** Le fil d'une instance, plus
   haut, est une rangée de pastilles : il se déclare `flex` avec retour
   à la ligne. Celle-ci est une pile de lignes, et ne déclarait pas son
   display — elle héritait donc de celui de l'autre. Ses lignes, qui
   sont des grilles à trois colonnes, cessaient de s'étirer : trois par
   rangée, et la colonne des barres écrasée à vingt-cinq pixels.
   **Une disposition ne se laisse pas au voisin.** */
.frise { display: block; list-style: none; margin: .9rem 0 0; padding: 0; }

/* **Des colonnes qui flottent ne se comparent pas.** Chaque ligne
   calculait sa largeur de nom, et les frises commençaient à des
   abscisses différentes : on ne pouvait plus lire une colonne de haut
   en bas, ce qui est pourtant le seul geste qu'on fait devant quinze
   sentinelles. */
.frise-ligne {
    display: grid;
    grid-template-columns: 18rem minmax(0, 1fr) 8.5rem;
    align-items: center;
    gap: 1rem;
    padding: .42rem 0;
    border-bottom: 1px solid var(--bord);
}
.frise-ligne:last-child { border-bottom: 0; }

.frise-nom {
    flex: 0 0 15rem;
    display: flex;
    align-items: center;
    gap: .5rem;
    font-size: .9rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.frise-barres {
    flex: 1 1 auto;
    display: flex;
    align-items: flex-end;
    gap: 2px;
    min-height: 20px;
}

.frise-barre {
    flex: 0 0 auto;
    width: 7px;
    height: 20px;
    border-radius: 2px;
    background: var(--sourdine);
    opacity: .85;
}
.frise-barre:hover { opacity: 1; }

.frise-sain   { background: var(--vert); }
.frise-veille { background: var(--ambre); }
.frise-panne  { background: var(--rouge); }

.frise-vide { font-size: .8rem; color: var(--sourdine); font-style: italic; }

.frise-age {
    flex: 0 0 auto;
    font-size: .76rem;
    color: var(--sourdine);
    white-space: nowrap;
}

/* Le gris compte autant que le rouge : une sentinelle ouverte qui n'a jamais rien
   vu n'est ni sain ni en panne, et c'est souvent le premier signe que la
   collecte ne tourne pas. */
.pastille-muet { background: var(--sourdine); }
.pastille-panne { background: var(--rouge); }
.badge.panne { background: var(--rouge-fond); color: var(--rouge); border-color: var(--rouge); }

/* Sous 900 pixels, le nom passe au-dessus de sa frise : tronquer un
   libellé à trois mots vaut moins qu'une ligne de plus. */
@media (max-width: 900px) {
    .etat-verdict { flex-wrap: wrap; }
    .etat-age { border-left: 0; padding-left: 0; text-align: left; width: 100%; }
    .frise-ligne { flex-wrap: wrap; gap: .35rem; }
    .frise-nom { flex: 1 1 100%; }
    .frise-age { flex: 1 1 100%; }
}

/* ══════════════════════════════════════════════════════════════════
   LE SOCLE : LA CARTE, LES ATTRIBUTS, L'INFOBULLE
   Repris du catalogue de DnaBundle, sans Bootstrap ni jQuery : ce sont
   des dispositions, des couleurs et deux comportements.
   Voir la page « Le soin de l'écran » du wiki.
   ══════════════════════════════════════════════════════════════════ */

/* La grille de cartes : elles se rangent d'elles-mêmes selon la place,
   sans qu'aucun écran ait à choisir un nombre de colonnes. */
.cartes {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(20rem, 1fr));
    gap: .9rem;
    margin-bottom: 1.4rem;
}

/* La carte à trois zones : tête, corps, pied. Le pied reste en bas quel
   que soit le corps, sans quoi une grille de cartes inégales fait danser
   les attributs d'une ligne à l'autre. */
/* **Une carte ne coupe pas ce qui en sort.** Elle portait
   `overflow: hidden` pour que ses coins tiennent, et les pastilles de
   son pied y perdaient leur explication, tronquée au ras du cadre.
   Les coins se tiennent autrement : la tête et le pied portent les
   leurs. */
.carte {
    display: flex;
    flex-direction: column;
    background: var(--carte);
    border: 1px solid var(--bord);
    border-radius: 10px;
}

.carte > .carte-tete { border-radius: 9px 9px 0 0; }
.carte > .carte-pied { border-radius: 0 0 9px 9px; }

.carte-tete {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: .7rem;
    padding: .7rem .9rem;
    background: var(--carte-haute);
    border-bottom: 1px solid var(--bord);
}

/* **Un titre ne cède jamais le premier.** La tête d'une carte est
   une rangée, et le titre y était le seul élément qui pouvait
   rétrécir : deux commandes le tronquaient, trois l'effaçaient —
   mesuré à zéro pixel. Il garde sa part, et ce sont les commandes qui
   passent à la ligne. */
.carte-titre {
    flex: 1 1 8rem;
    min-width: 0;
    font-size: .98rem;
    line-height: 1.25;
    overflow: hidden;
    text-overflow: ellipsis;
}


/* **Un titre de section et son aide ne se soudent pas.** L'aide suit le
   titre dans le même bloc : sans règle, elle se collait au dernier mot —
   « Les tableauxce qui se regarde d'un coup d'oeil ». */
.biblio-famille {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: .6rem;
    margin: 1.4rem 0 .7rem;
}

.biblio-famille .aide { flex: 1 1 12rem; }


/* **Une absence de mesure se lit comme une absence.** Elle prend la place
   du chiffre, en plus petit et en sourdine : rien ne doit pouvoir se lire
   comme une valeur relevée. */
.tuile-muette {
    display: inline-block;
    font-size: .82rem;
    font-style: italic;
    color: var(--sourdine);
}

.carte-corps { flex: 1 1 auto; padding: .8rem .9rem .6rem; }
.carte-dire { margin: 0; font-size: .86rem; color: var(--sourdine); line-height: 1.45; }

.carte-signes { margin: .6rem 0 0; font-size: .78rem; color: var(--sourdine); }
.carte-sep { margin: 0 .35rem; opacity: .6; }

/* Le détail replié : ce qu'on vient chercher d'abord, c'est de quoi il
   s'agit. Une liste de valeurs par carte fait d'un lot de douze
   propositions un mur de texte. */
.carte-apercu { padding: 0 .9rem .6rem; }
.carte-apercu > summary {
    cursor: pointer;
    font-size: .78rem;
    color: var(--sourdine);
    padding: .2rem 0;
    user-select: none;
}
.carte-apercu > summary:hover { color: var(--encre); }
.carte-apercu[open] > summary { color: var(--encre); margin-bottom: .3rem; }

.carte-pied {
    padding: .55rem .9rem;
    border-top: 1px solid var(--bord);
    background: var(--carte-haute);
}

/* Une carte endormie se voit endormie : elle est là, elle ne demande
   rien. L'estomper vaut mieux que la cacher — savoir qu'un objet est
   déjà ouvert évite de le chercher ailleurs. */
.carte-endormie { opacity: .72; }
.carte-endormie:hover { opacity: 1; }

/* ─── La rangée d'attributs ───────────────────────────────────────
   Chaque pastille dit son état par une icône ET une couleur.
   **Jamais d'icône sans explication** : c'est ce qui permet à un écran
   dense de rester abordable. */
.attributs { display: inline-flex; flex-wrap: wrap; gap: .4rem; }

.attribut {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    padding: .2rem .45rem;
    border-radius: 5px;
    border: 1px solid transparent;
    font-size: .78rem;
    cursor: help;
}
.attribut:focus { outline: 2px solid var(--or); outline-offset: 1px; }

.attribut-sain   { background: var(--vert-fond);  color: var(--vert);  border-color: var(--vert); }
.attribut-veille, .attribut-watch { background: var(--ambre-fond); color: var(--ambre); border-color: var(--ambre); }
.attribut-panne  { background: var(--rouge-fond); color: var(--rouge); border-color: var(--rouge); }
.attribut-neutre { background: var(--fond); color: var(--sourdine); border-color: var(--bord); }

.attribut-mot { font-variant-numeric: tabular-nums; }

/* **La naissance en lot rend compte de sa première passe** (#528) : le
   bandeau prend la tête de l'écran des sentinelles, au-dessus des
   compteurs. Rouge quand le lot est jugé et porte un échec ; sans
   couleur tant qu'il attend, il n'a ni tort ni raison. La cause commune
   se lit seule, en monospace, entre guillemets, telle que le relevé l'a
   écrite. « Voir le lot » est un bouton au contour doré, à droite du
   titre dans les deux états, comme sur la planche ; pressé, il prend
   l'or plein. Le filtre qu'il positionne est une liste du mécanisme
   commun, tenue cachée : l'écran n'a pas deux barres d'onglets. */
.lot-naissance {
    margin: 0 0 .8rem; padding: .7rem .9rem;
    border: 1px solid var(--bord); border-radius: 8px; background: var(--carte);
}
.lot-naissance-echec { border-color: var(--rouge); }
.lot-naissance-attente { border-color: var(--bord); }
.lot-naissance-tete { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; }
.lot-naissance-titre { font-size: .95rem; }
.lot-naissance-dire { margin: .4rem 0 0; font-size: .85rem; color: var(--sourdine); }
.lot-naissance-cause { display: block; margin: .3rem 0 0; color: var(--rouge); font-size: .85rem; }
.lot-naissance-voir {
    margin-left: auto; padding: .3rem .8rem;
    border: 1px solid var(--or); border-radius: 7px;
    background: var(--carte-haute); color: var(--or);
    font-size: .78rem; white-space: nowrap;
}
.lot-naissance-voir:hover { filter: brightness(1.1); }
.lot-naissance-voir[aria-pressed="true"] { background: var(--or); color: var(--fond); }
.lot-naissance-filtre[hidden] { display: none; }

/* L'infobulle : un bandeau qui nomme, une phrase qui explique. Deux
   parties, comme chez DNA — le nom seul ne suffit pas, et l'explication
   sans nom ne se rattache à rien.
   Elle sort au survol **et au focus** : un survol ne se fait pas au
   clavier. */
/* Même règle qu'au flanc replié : une boîte cachée par l'opacité occupe
   toujours sa place, et une infobulle de dix-sept rem posée près du bord
   droit élargit le document. */
.attribut[data-titre]::before,
.attribut[data-titre]::after {
    display: none;
    position: absolute;
    bottom: calc(100% + 8px);
    left: 0;
    width: 17rem;
    pointer-events: none;
    z-index: 40;
}

.attribut[data-titre]::before {
    content: attr(data-titre);
    transform: translateY(0);
    padding: .3rem .55rem;
    background: var(--or);
    color: var(--fond);
    font-size: .74rem;
    letter-spacing: .04em;
    border-radius: 6px 6px 0 0;
    bottom: calc(100% + 8px + 3.1rem);
}

.attribut[data-titre]::after {
    content: attr(data-dire);
    padding: .45rem .55rem;
    background: var(--carte-haute);
    color: var(--encre);
    border: 1px solid var(--bord);
    border-top: 0;
    border-radius: 0 0 6px 6px;
    font-size: .76rem;
    line-height: 1.4;
    text-align: left;
    box-shadow: 0 6px 18px rgba(0, 0, 0, .35);
    min-height: 3.1rem;
}

.attribut:hover::before, .attribut:hover::after,
.attribut:focus::before, .attribut:focus::after { display: block; }

/* Près du bord droit, l'infobulle se recale : une explication qui sort
   de l'écran ne s'est jamais lue. */
.cartes .carte:nth-child(3n) .attribut[data-titre]::before,
.cartes .carte:nth-child(3n) .attribut[data-titre]::after { left: auto; right: 0; }

@media (max-width: 700px) {
    .attribut[data-titre]::before,
    .attribut[data-titre]::after { width: 14rem; }
}

/* ─── Les listes ──────────────────────────────────────────────────
   L'état en tête de ligne, la frise au même sens de lecture qu'à
   l'accueil, la fraîcheur en relatif avec l'absolu dessous. */

.table-sentinelles td { vertical-align: middle; }

.sentinelle-nom { display: flex; align-items: flex-start; gap: .55rem; }
.sentinelle-nom .pastille-etat { margin-top: .38rem; flex: 0 0 auto; }

.sentinelle-signes {
    display: block;
    font-size: .74rem;
    color: var(--sourdine);
    margin-top: .1rem;
}
.sentinelle-signes code { font-size: .72rem; }

/* La frise menue : le même motif qu'à l'accueil, plus discret parce
   qu'ici elle accompagne une ligne au lieu de la porter. */
.frise-menue .frise-barre { width: 5px; height: 15px; }

.colonne-age { white-space: nowrap; font-size: .84rem; }

/* Une sentinelle en sommeil se voit endormi. L'estomper vaut mieux que le
   cacher : savoir qu'il existe évite de le rouvrir en double. */
.ligne-endormie { opacity: .55; }
.ligne-endormie:hover { opacity: 1; }

/* La barre de gestes sans ses onglets : elle se range à droite,
   là où elle a toujours été, pour qu'on la retrouve au même
   endroit d'un écran à l'autre. */
.barre-gestes-seule {
    display: flex;
    justify-content: flex-end;
    margin-bottom: 1.1rem;
}

/* ─── Le tri des tableaux ─────────────────────────────────────────
   **Une colonne triable qui ne le dit pas ne se trie jamais** :
   personne ne clique un en-tête au hasard pour voir. La flèche paraît
   au survol, s'affirme quand la colonne porte le tri. */

.th-triable { cursor: pointer; user-select: none; position: relative; padding-right: 1.2rem; }

.th-triable::after {
    content: '↕';
    position: absolute;
    right: .35rem;
    opacity: .25;
    font-size: .82em;
}
.th-triable:hover::after { opacity: .6; }
.th-triable[data-sens="asc"]::after  { content: '↑'; opacity: 1; color: var(--or); }
.th-triable[data-sens="desc"]::after { content: '↓'; opacity: 1; color: var(--or); }

/* Une ligne de tableau qui ouvre un détail le dit. */
.ligne-ouvrante { cursor: pointer; }
.ligne-ouvrante:hover { background: var(--carte-haute); }

/* ─── La modale à trois zones ─────────────────────────────────────
   Une tête qui nomme et qui ferme, un corps qui défile, et rien qui
   bouge d'une modale à l'autre. La croix se cherchait au bas d'un long
   contenu, ou n'existait pas et il fallait deviner qu'un clic sur le
   voile ferme. */

/* **Un même latéral pour la tête, le corps et le pied.** Trois
   rembourrages qui diffèrent font trois morceaux ; les bords tombent sur
   la même verticale et la boîte se lit d'un seul tenant. Le geste sur
   une entité est le plus fréquent du produit : il ne se fait pas au
   bord (#286). */
.modale-tete {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: .85rem 1.75rem;
    background: var(--carte-haute);
    flex: 0 0 auto;
}

/* **Ce qui nomme ne quitte jamais la vue** (#303). Le conteneur le
   tenait déjà par sa mécanique ; le déclarer le garantit, quel que soit
   ce qui défile en dessous. */
.modale-tete { position: sticky; top: 0; z-index: 2; }

.modale-tete h2 {
    margin: 0;
    font-size: 1.05rem;
    line-height: 1.3;
    display: flex;
    align-items: center;
    gap: .6rem;
    min-width: 0;
}

/* L'icône dit de quoi il s'agit avant qu'on lise le titre. Elle
   prend l'or de la maison : c'est la couleur de ce qui nomme. */
.modale-tete h2 i { color: var(--or); flex: 0 0 auto; font-size: .95em; }
.modale-tete h2 span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.modale-fermer {
    flex: 0 0 auto;
    width: 2rem;
    height: 2rem;
    display: flex;
    align-items: center;
    justify-content: center;
    background: none;
    border: 1px solid transparent;
    border-radius: 6px;
    color: var(--sourdine);
    font-size: 1.4rem;
    line-height: 1;
    cursor: pointer;
}
.modale-fermer:hover { color: var(--encre); border-color: var(--bord); background: var(--fond); }
.modale-fermer:focus { outline: 2px solid var(--or); outline-offset: 1px; }

/* **C'est le corps qui défile, pas la boîte.** Une modale entière qui
   défile emmène son titre hors de vue : on ne sait plus ce qu'on lit. */
.modale-corps {
    flex: 1 1 auto;
    overflow-y: auto;
    overflow-x: auto;
    padding: 1.75rem 2.1rem;
    min-height: 0;
}

/* **La modale respire** (#290). Le tassement se règle une fois, ici,
   jamais gabarit par gabarit : les champs y prennent l'air qu'un
   écran plein leur refuse, et le geste final se détache de ce qu'il
   engage. */
.modale-corps label { margin: 1.35rem 0 .45rem; }

/* **Le jeu de réglages d'un exécutant ne se voit pas comme une boîte**
   (#501). Un `fieldset` porte une bordure et un écart que le navigateur
   donne d'office ; ici il ne sert qu'à grouper ce qui apparaît et
   disparaît ensemble, et ses champs doivent couler comme les autres. */
.reglages-executant {
    border: 0; margin: 0; padding: 0; min-width: 0;
}
.reglages-executant > label:first-child { margin-top: 1.35rem; }
.modale-corps input[type="text"], .modale-corps input[type="password"],
.modale-corps input[type="email"], .modale-corps input[type="number"],
.modale-corps select, .modale-corps textarea { padding: .65rem .9rem; }
.modale-corps .bouton-or[type="submit"] { margin-top: 1.4rem; }
.modale-corps .aide { margin-bottom: .4rem; }
.modale-corps:has([data-geste="sonde-neuve"]) {
    padding: 0rem 2.1rem;
}

/* Le premier champ d'un formulaire ne double pas l'air du corps. */
.modale-corps form > label:first-child { margin-top: 0; }

/* **Un formulaire se borne et se centre** (#303). La boîte va jusqu'à
   cinquante-six rem parce qu'un tableau de pas en a besoin ; un champ
   de saisie, non. Étiré sur toute la largeur, il se lit moins bien —
   l'œil traverse la boîte entre l'étiquette et la valeur — et il ne se
   remplit pas mieux.

   Ce qui demande la largeur la garde : une fresque, un tableau, une
   grille de cases. C'est le contenu qui demande, jamais la boîte qui
   impose. */
.modale-corps > form,
.modale-corps > p,
.modale-corps > .aide {
    max-width: 40rem;
    margin-inline: auto;
}

.modale-corps > form:has(table),
.modale-corps > form:has(.fresque),
.modale-corps > form:has([data-panneaux]) {
    max-width: none;
}

/* **Une largeur relâchée pour un membre du groupe le laisse aux
   autres** (#500). La règle au-dessus vise trois sélecteurs — le
   formulaire, le paragraphe, l'aide — et la libération n'en reprenait
   qu'un. Dans une modale large, le formulaire s'étalait pendant que les
   paragraphes restaient centrés sur quarante rem : mesuré sur la fiche
   d'un moule, 304 pixels pour le slug et la promesse quand les tableaux
   et les listes commençaient à 35.

   **La mesure de lecture reste, le centrage part.** Un texte de mille
   pixels de large ne se lit pas ; mais dans une fiche, il s'aligne sur
   ce qui l'entoure au lieu de flotter au milieu. */
.modale-large .modale-corps > form { max-width: none; }
.modale-large .modale-corps > p,
.modale-large .modale-corps > .aide { margin-inline: 0; }

/* **Un geste important n'a pas besoin d'être gros** (#303). Il a besoin
   d'être seul et bien placé. Un bouton étiré sur cinquante-six rem
   barre l'écran et ne se vise pas mieux qu'un bouton à sa taille. */
.modale-corps .bouton-large {
    width: auto;
    min-width: 11rem;
    padding: .5rem 1.6rem;
    font-size: .82rem;
}

.modale-corps form > .bouton-large {
    display: flex;
    margin-inline: auto;
}

/* Sous les petites largeurs, l'air se resserre : la lisibilité
   d'abord, la respiration ensuite. */
@media (max-width: 640px) {
    .modale-tete, .modale-pied { padding-left: 1.1rem; padding-right: 1.1rem; }
    .modale-corps { padding: 1.1rem; }
    .modale-corps:has([data-panneaux]) { padding: 1.1rem 1.1rem 0; }
}

/* **Dès qu'il porte des panneaux, le corps cesse de défiler.** Le
   défilement descend d'un cran : les onglets restent en vue, et
   c'est le contenu du panneau qui glisse sous eux. Autrement on
   perd ses onglets en lisant, et l'on ne sait plus dans lequel on
   est. */
.modale-corps:has([data-panneaux]) {
    overflow: hidden;
    display: flex;
    flex-direction: column;
    padding: 1.75rem 2.1rem 0;
}

.modale-corps > :first-child { margin-top: 0; }
.modale-corps > :last-child { margin-bottom: 0; }

/* ─── Les panneaux à onglets ──────────────────────────────────────
   **Une modale qui empile trois sections se lit mal.** Le détail d'un
   circuit porte sa fresque, ses places et ses pas : à la file, il faut
   défiler pour comparer deux choses qui ne tiennent pas ensemble à
   l'écran, et l'on perd le fil à chaque aller-retour. */


.panneaux-compte {
    margin-left: .35rem;
    font-size: .76rem;
    opacity: .7;
    font-variant-numeric: tabular-nums;
}

/* Le chiffre d'un atelier, sur l'écran d'entrée : c'est la seule chose
   qu'on vient y chercher, et il se lit sans lire. */
.atelier-chiffre {
    margin: 0 0 .4rem;
    font-size: 1.9rem;
    line-height: 1;
    font-variant-numeric: tabular-nums;
}

.atelier-unite {
    font-size: .82rem;
    color: var(--sourdine);
    margin-left: .25rem;
}

/* Le pied d'une modale : les actions, toujours au même endroit.
   Posées au fil du contenu, elles se cherchaient — un bouton d'envoi au
   milieu d'un long formulaire se manque, et l'on referme en croyant que
   rien n'était possible.

   L'action majeure à droite, comme partout où l'on valide : c'est le
   dernier endroit que la sentinelle balaie avant de cliquer. */
/* **Le pied se dessine comme la tête.** Les deux ferment la même boîte :
   un trait et un fond d'un côté, un aplat de l'autre, et l'on ne lit plus
   une modale mais trois morceaux. */
.modale-pied {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    flex-wrap: wrap;
    gap: .6rem;
    padding: .85rem 1.75rem;
    background: var(--carte-haute);
    flex: 0 0 auto;
}

/* **Le pied non plus ne s'en va pas** (#303) : le geste qui valide se
   cherchait au bas d'un long défilé. */
.modale-pied { position: sticky; bottom: 0; z-index: 2; }

/* Ce qui explique reste à gauche, ce qui agit va à droite. */
.modale-pied .aide { margin: 0 auto 0 0; font-size: .8rem; }

/* Les onglets d'un domaine : le paradigme d'affichage de la maison. Un
   domaine ouvre sur son état, et ses écrans se prennent en onglets — pas
   en remontant au flanc à chaque fois. */
.onglets-domaine { margin-bottom: 1.3rem; }
.onglets-domaine .onglet i { margin-right: .4rem; opacity: .8; }

/* ─── Le défilé d'un panneau, et son pied ─────────────────────────
   **Le défilement se confine au panneau, et les validations restent
   visibles.** Un pied qui défile avec le contenu se retrouve sous la
   ligne de flottaison d'un long formulaire : on remplit, on cherche, on
   renonce. */

.modale-corps [data-panneaux] {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    min-height: 0;
}

.modale-corps [data-panneaux] > .panneaux-onglets { flex: 0 0 auto; }

/* **C'est le panneau qui défile.** Envelopper chaque contenu dans un
   cadre de défilement aurait demandé à chaque écran de le faire, et le
   premier qui l'oublie perd son pied sans que rien ne le dise. */
.modale-corps [data-panneaux] > [data-panneau-corps] {
    flex: 1 1 auto;
    overflow-y: auto;
    overflow-x: auto;
    min-height: 0;
    padding-bottom: .2rem;
}

/* Le pied d'un panneau porte ses validations et les siennes seules : un
   pied commun montrerait sous « Les places » un bouton qui enregistre un
   pas.

   **Il colle au bas du défilé.** Un pied qui défile avec le contenu se
   retrouve sous la ligne de flottaison d'un long formulaire : on
   remplit, on cherche, on renonce. Le motif `sticky` ne demande rien au
   gabarit sinon d'être le dernier enfant du panneau. */
.panneau-pied {
    position: sticky;
    bottom: 0;
    z-index: 2;
    display: flex;
    align-items: center;
    justify-content: flex-end;
    flex-wrap: wrap;
    gap: .6rem;
    margin: .9rem -1rem -1rem;
    padding: .85rem 1rem;
    background: var(--carte-haute);
    border-radius: 0 0 8px 8px;
}

.panneau-pied .aide { margin: 0 auto 0 0; font-size: .8rem; }

/* ─── L'écran des menus ───────────────────────────────────────────
   La hiérarchie telle qu'elle est : le décalage montre la filiation, et
   chaque entrée s'ouvre sur ce qui se change. */

.menu-item {
    border: 1px solid var(--bord);
    border-radius: 8px;
    margin-bottom: .4rem;
    background: var(--carte);
}

.menu-item > summary {
    display: flex;
    align-items: center;
    gap: .8rem;
    flex-wrap: wrap;
    padding: .55rem .8rem;
    cursor: pointer;
    user-select: none;
}
.menu-item > summary:hover { background: var(--carte-haute); }
.menu-item[open] > summary { border-bottom: 1px solid var(--bord); }

.menu-item-nom { display: flex; align-items: center; gap: .45rem; font-size: .92rem; }
.menu-item-nom i { color: var(--or); }

.menu-item-signes {
    font-size: .76rem;
    color: var(--sourdine);
    margin-right: auto;
}

/* Le formulaire d'un item : deux colonnes tant qu'il y a la place, une
   seule ensuite. Les champs se lisent dans l'ordre où on les remplit. */
.menu-item-forme {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
    gap: .8rem;
    padding: .9rem .8rem 0;
}

.menu-item-forme label { display: flex; flex-direction: column; gap: .25rem; font-size: .82rem; }
.menu-item-forme label > span:first-child { color: var(--sourdine); }
.menu-item-forme label.case { flex-direction: row; align-items: center; gap: .45rem; }

/* **Le sujet prend la largeur.** Une cible par ligne se relit mal dans
   une demi-colonne, et c'est le seul champ qu'on modifie vraiment avant
   d'activer : le reste se règle ensuite, objet par objet. */
.menu-item-forme label.cibles { grid-column: 1 / -1; }
.menu-item-forme label.cibles textarea { resize: vertical; min-height: 5.4rem; }

/* Une phrase adressée au formulaire entier ne se range pas dans une
   colonne : qu'il n'y a rien à régler, ou ce que les cibles engagent. */
.menu-item-forme > .aide { grid-column: 1 / -1; margin: 0; }

/* Le pied traverse la grille : les validations ne se rangent pas dans
   une colonne, elles closent le formulaire. */
.menu-item-forme .panneau-pied {
    grid-column: 1 / -1;
    margin: .3rem -0.8rem 0;
    position: static;
}

/* ─── Le hint, et le panneau qui tient à son onglet ───────────────

   **Une étiquette nomme, une explication éclaire.** Mises bout à bout,
   l'étiquette s'allonge et l'explication se lit avant qu'on ait besoin
   d'elle. La pastille garde la seconde à portée sans la mettre en
   travers. */

.hint {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.05rem;
    height: 1.05rem;
    margin-left: .3rem;
    border-radius: 999px;
    border: 1px solid var(--bord);
    background: var(--fond);
    color: var(--sourdine);
    font-size: .68rem;
    line-height: 1;
    cursor: help;
    vertical-align: middle;
}
.hint:hover { color: var(--or); border-color: var(--or); }
.hint:focus { outline: 2px solid var(--or); outline-offset: 1px; }

/* La même infobulle en deux parties que les attributs : il n'y a qu'une
   façon d'expliquer dans cette maison. */
.hint[data-titre]::before,
.hint[data-titre]::after {
    display: none;
    position: absolute;
    bottom: calc(100% + 8px);
    left: 0;
    width: 17rem;
    pointer-events: none;
    z-index: 40;
    text-align: left;
    font-size: .76rem;
    line-height: 1.4;
}

.hint[data-titre]::before {
    content: attr(data-titre);
    padding: .3rem .55rem;
    background: var(--or);
    color: var(--fond);
    border-radius: 6px 6px 0 0;
    bottom: calc(100% + 8px + 3.1rem);
    letter-spacing: .04em;
}

.hint[data-titre]::after {
    content: attr(data-dire);
    padding: .45rem .55rem;
    background: var(--carte-haute);
    color: var(--encre);
    border: 1px solid var(--bord);
    border-top: 0;
    border-radius: 0 0 6px 6px;
    box-shadow: 0 6px 18px rgba(0, 0, 0, .35);
    min-height: 3.1rem;
}

.hint:hover::before, .hint:hover::after,
.hint:focus::before, .hint:focus::after { display: block; }

/* Dans une modale, le panneau défile : son cadre ne doit pas défiler
   avec lui. */
.modale-corps [data-panneaux] > [data-panneau-corps] {
    margin-top: -1.1rem;
    padding: 1rem;
}

/* **Le panneau tient à son onglet.** Le contenu flottait sous la barre :
   rien ne reliait ce qu'on lit à l'onglet qui l'a ouvert. Le cadre
   reprend le fond de l'onglet actif, et les deux se lisent comme un seul
   objet.

   La barre perd sa marge basse : c'est le cadre qui la remplace, et un
   espace entre l'onglet et son panneau les séparerait de nouveau. */
[data-panneaux] > .panneaux-onglets { margin-bottom: 0; }

[data-panneaux] > [data-panneau-corps] {
    background: var(--carte);
    border: 1px solid var(--bord);
    border-top: 0;
    border-radius: 0 0 8px 8px;
    padding: 1rem;
}

/* **Ce qui précède une barre d'onglets est encadré.** Un texte posé nu
   au-dessus des onglets flotte : il n'appartient à aucun panneau, et rien
   ne dit qu'il vaut pour tous. Le cadre le rattache à l'objet entier
   plutôt qu'à ce qui suit — et sans lui, on le lit comme l'introduction
   du premier onglet, ce qu'il n'est pas.

   Il se distingue du panneau par son fond : ce qui explique n'est pas ce
   qu'on manipule. */
.chapeau {
    margin: 0 0 1.1rem;
    padding: .7rem .9rem;
    background: var(--fond);
    border: 1px solid var(--bord);
    border-radius: 8px;
    color: var(--sourdine);
    font-size: .84rem;
    line-height: 1.5;
}

/* ─── Deux natures d'onglet ───────────────────────────────────────

   **Un onglet de panneau et un onglet de navigation ne se dessinent pas
   pareil.** Le premier ouvre un contenu juste dessous : son fond se
   raccorde à ce contenu, et le trait du bas s'ouvre sous lui. Le second
   mène ailleurs — il n'a rien dessous à quoi se raccorder, et son fond
   flotte.

   Celui-là se marque par le trait, pas par la boîte : c'est le motif que
   tout le monde reconnaît sur une barre de navigation. */
.onglets-domaine .onglet {
    border: 0;
    border-bottom: 2px solid transparent;
    border-radius: 0;
    margin-bottom: -1px;
    padding: .5rem .9rem;
}

.onglets-domaine .onglet.actif {
    background: none;
    border-bottom-color: var(--or);
    color: var(--or);
}

.onglets-domaine .onglet:hover { border-bottom-color: var(--bord); }
.onglets-domaine .onglet-clos { border-bottom-color: transparent; }

/* Un bloc qui suit une barre d'onglets s'y raccorde : le coin haut
   gauche s'efface et la marge disparaît, sinon l'onglet actif flotte
   au-dessus d'un vide et l'on ne voit plus ce qu'il ouvre. */
/* Le panneau tient à sa barre : coin carré à gauche et marge haute
   supprimée, pour que l'onglet actif et son contenu ne fassent qu'un. */
.panneau-raccorde {
    margin-top: 0;
    border-top-left-radius: 0;
}


/* ───────────────────────────────────────────────────────────────────
   Le filtre en onglets

   **Un filtre à trois valeurs ne se cache pas.** Une liste déroulante
   demande un clic pour savoir ce qu'elle contient ; l'onglet le montre,
   et son compte dit ce qu'il y a derrière avant qu'on y aille. C'est la
   forme retenue partout où les valeurs se comptent sur une main.

   Il se pose au-dessus de la barre parce qu'il découpe : ce qui réduit
   — la recherche, les listes — reste en dessous.
   ─────────────────────────────────────────────────────────────────── */
.pilotage-onglets {
    display: flex;
    flex-wrap: wrap;
    gap: .2rem;
    margin: 0;
    border-bottom: 1px solid var(--bord);
}

.pilotage-onglet {
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    padding: .5rem .9rem;
    margin-bottom: -1px;
    border: 1px solid transparent;
    border-radius: 6px 6px 0 0;
    background: none;
    color: var(--sourdine);
    font: inherit;
    font-size: .9rem;
    cursor: pointer;
    transition: color .12s, background .12s;
}

.pilotage-onglet:hover { color: var(--encre); background: var(--carte-haute); }

/* L'onglet actif tient à son tableau : son bord bas s'efface pour que
   l'onglet et ce qu'il montre ne fassent qu'un. */
.pilotage-onglet.actif {
    color: var(--encre);
    background: var(--carte);
    border-color: var(--bord);
    border-bottom-color: var(--carte);
    font-weight: 600;
}

.pilotage-onglet-compte {
    min-width: 1.4rem;
    padding: 0 .35rem;
    border-radius: 999px;
    background: var(--carte-haute);
    color: var(--sourdine);
    font-size: .76rem;
    font-variant-numeric: tabular-nums;
    text-align: center;
}

.pilotage-onglet.actif .pilotage-onglet-compte {
    background: var(--or);
    color: var(--fond);
}

/* Vide, l'onglet s'éteint sans disparaître : ce qui bouge d'une frappe
   à l'autre se relit à chaque fois. */
.pilotage-onglet.creux { opacity: .45; }

.pilotage-onglets + .pilotage-barre { padding-top: .8rem; }

/* **Une classe dit son rôle, pas sa valeur.** « suite » nomme le
   paragraphe qui prolonge ce qui précède ; « mt-3 » n'aurait été qu'un
   style en dur ayant changé de place. */
.aide.suite { margin-top: .8rem; }

/* Une sentinelle partagee le dit la ou on la regle : ce qui se pose
   ici change l ecran des autres (#450). Le ton de l ambre, celui de
   ce qui demande une attention sans etre une faute. */
.aide.partagee {
    color: var(--ambre);
    background: var(--ambre-fond);
    border-radius: .3rem;
    padding: .5rem .7rem;
    margin-bottom: .6rem;
}

.aide.partagee strong { color: var(--ambre); }

/* **Un libelle nomme ce qu on mesure, une aide dit pourquoi** (#454).
   Elle se pose sous le nom, sourdine, et ne crie pas : ce qui
   explique ne doit pas peser autant que ce qui nomme. */
/* **Un mode dit ce que la sonde est, pas ce qu elle a vu** (#447).
   Il se pose pres du nom, sobre : ce n est ni une alerte ni une
   mesure, c est une propriete. */
.badge-mode {
    display: inline-block;
    margin-left: .4rem;
    padding: .05rem .35rem;
    border: 1px solid var(--sourdine);
    border-radius: .25rem;
    color: var(--sourdine);
    font-size: .7rem;
    letter-spacing: .02em;
    vertical-align: middle;
}

.mesure-aide {
    display: block;
    color: var(--sourdine);
    font-size: .78rem;
    font-style: italic;
    line-height: 1.35;
}

/* ---- Le coin d'une tuile ----
   Discret jusqu'au survol, comme le coin des lignes de pilotage dont il
   reprend le mécanisme (data-coin). Il vit en absolu dans la tuile, qui
   devient son repère. Au clavier, le focus le montre : un réglage qui
   n'existe qu'à la souris n'existe pas pour tout le monde. */
.tuile, .tuile-synthese { position: relative; }

.widget-coin {
    position: absolute;
    top: .35rem;
    right: .35rem;
    opacity: 0;
    transition: opacity .15s ease;
    z-index: 3;
}

.tuile:hover .widget-coin,
.tuile-synthese:hover .widget-coin,
.widget-coin:focus-within { opacity: 1; }

.widget-coin .coin-menu {
    right: 0;
    left: auto;
    min-width: 15rem;
    text-align: left;
}

.coin-form {
    display: grid;
    gap: .3rem;
    padding: .45rem .6rem;
    border-bottom: 1px solid var(--bord);
}

.coin-form input[type="number"] { max-width: 9rem; }

.widget-regle {
    position: absolute;
    bottom: .35rem;
    right: .35rem;
    font-size: .62rem;
    z-index: 2;
}

/* ---- L'atelier d'un circuit : le fil des étapes (#191) ----
   **On habille un circuit, on ne dessine pas un graphe.** Les étapes se
   lisent l'une sous l'autre, reliées par un trait ; le pas qui mène à la
   suivante vit sous la place dont il part. C'est la frise de DNA, celle
   où l'on voit le chemin entier. */
.atelier-fil { list-style: none; margin: 0; padding: 0; position: relative; }
.atelier-fil::before {
    content: ''; position: absolute; left: 13px; top: 14px; bottom: 14px;
    width: 2px; background: var(--bord);
}
.atelier-etape { display: flex; gap: 1rem; position: relative; padding: .4rem 0 1rem; }
.atelier-puce {
    flex: 0 0 28px; height: 28px; border-radius: 50%;
    background: var(--or); color: var(--fond);
    display: flex; align-items: center; justify-content: center;
    font-size: .8rem; position: relative; z-index: 1;
}
.atelier-humain .atelier-puce { background: var(--or); }
.atelier-automate .atelier-puce { background: #8250df; }
.atelier-aboutie .atelier-puce { background: var(--vert); }
.atelier-refus .atelier-puce { background: var(--rouge); }
.atelier-passage .atelier-puce { background: var(--sourdine); }
.atelier-puce-vide {
    background: var(--carte); border: 1px dashed var(--or); color: var(--or);
}
.atelier-corps { flex: 1; min-width: 0; }
.atelier-tete { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; }
.atelier-dit { color: var(--sourdine); font-size: .82rem; margin: .2rem 0 0; }
.atelier-pas {
    display: flex; align-items: center; gap: .6rem; flex-wrap: wrap;
    margin: .6rem 0 0; padding: .45rem .8rem;
    background: var(--fond); border: 1px dashed var(--bord); border-radius: 8px;
    font-size: .85rem;
}
.atelier-pas-refus { border-color: var(--rouge); background: var(--rouge-fond); }
.atelier-pas-refus .atelier-fleche { color: var(--rouge); }
.atelier-fleche { color: var(--or); }
.atelier-executant { color: var(--sourdine); font-size: .8rem; }
.atelier-vers { color: var(--sourdine); font-size: .8rem; font-style: italic; }
.atelier-gestes { margin: .5rem 0 0; }

/* Le portail des fabriques range en deux groupes (#185) : le titre du
   groupe se lit avant les cartes, et respire entre les deux. */
.portail-groupe {
    margin: 1.6rem 0 .2rem;
    font-size: 1.05rem; letter-spacing: .08em; text-transform: uppercase;
    color: var(--sourdine);
}
.portail-groupe:first-of-type { margin-top: 0; }

/* La rangée d'attributs d'un écran de pilotage (#192) : sous l'aide,
   avant la liste — on survole avant de lire. */
.pilotage-attributs { margin-top: .5rem; }

/* ---- La fiche d'un objet en modale (#256) ----

   **Une étiquette et sa valeur restent liées.** La grille `auto 1fr`
   étirait la seconde colonne jusqu'au bord de la modale : « Compte » à
   gauche, sa valeur à neuf cents pixels de là, et la sentinelle ne reliait plus
   les deux. La valeur prend la place qu'il lui faut, pas toute celle
   qui reste. */
.fiche { display: grid; grid-template-columns: auto max-content; justify-content: start; }

/* Un titre de section vit sous le titre de la modale, jamais au-dessus.
   Ceux-ci sortaient en Times 30 px, plus gros que le nom de l'objet
   qu'on était venu regarder. */
.modale .fiche-titre {
    font-size: .95rem;
    font-weight: 600;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--sourdine);
    margin: 1.2rem 0 .5rem;
    padding-bottom: .3rem;
    border-bottom: 1px solid var(--bord);
}
.modale .fiche-titre:first-of-type { margin-top: .4rem; }

/* ─── Déclarer un moule de sonde, une étape à la fois ──────────────

   **Sept sections ne tiennent pas dans une boîte** (#349). Le formulaire
   d'un seul tenant a été écrit, servi, essayé, et jugé trop chargé ;
   cinq corrections de rendu n'y ont rien changé. Le découpage était
   faux, pas le rendu.

   **Le split reste, mais il ne porte plus qu'une étape.** À gauche ce
   qu'on déclare, à droite ce que ça veut dire : l'axe était juste. Ce
   qui était faux, c'était d'y mettre sept choses à la fois. Le volet
   droit devient l'aide de l'étape courante et ne change plus au survol.

   **Un seul or.** Les titres structurent en sourdine, l'or est réservé
   à ce qui engage. « Continuer » et « Créer » ne paraissent jamais
   ensemble — l'un sur les six premières étapes, l'autre sur la dernière
   —, et le fil ne marque l'étape courante que d'un anneau. */

/* ── Le fil, qui est un état et non une barre de navigation ──────── */

/* **Le fil ne quitte pas l'écran.** Une étape dont l'aide est longue fait
   défiler le corps, et le repère de l'endroit où l'on est partait avec.
   Le décalage négatif fait couvrir au fil la marge haute du corps : sans
   lui, le contenu qui remonte réapparaîtrait au-dessus de lui. */
/* **Une bande collante couvre toute la largeur, marges comprises.**
   Le corps d'une modale a 2,1rem de marge intérieure de chaque côté :
   une bande qui s'arrête au bord du contenu laisse deux gouttières
   par lesquelles on voit défiler ce qu'elle est censée masquer. Elle
   déborde donc de la marge et la repose en rembourrage. */
.fil {
    position: sticky; top: 0; z-index: 3;
    display: flex; flex-wrap: wrap; gap: 0;
    list-style: none; background: var(--carte);
    margin: -1.75rem -2.1rem 1.4rem; padding: 1.75rem 2.1rem 1rem;
    border-bottom: 1px solid var(--bord);
}

/* **Le trait ne traverse jamais un libellé.** Posé à hauteur des noms, il
   les barrait : une étape se lisait « faite » parce qu'un trait la
   traversait, et le barré des étapes sans objet ne voulait plus rien
   dire. La pastille est donc seule sur sa ligne, le nom en dessous —
   c'est ce que le dessin montrait, et l'écran s'en était écarté.

   **Et rien ne bouge quand un état change.** L'écho n'apparaissait qu'une
   fois l'étape franchie : il ajoutait une ligne, et tout le fil se
   décalait au fil de la saisie. Sa place est désormais réservée, vide ou
   pleine. Ce qui distingue un état est la couleur et l'alpha, jamais la
   position. */
.fil-pas {
    position: relative; flex: 1 1 0; min-width: 5rem;
    display: grid; grid-template-rows: 1.45rem 1.1rem .95rem;
    justify-items: center; row-gap: .1rem; padding: 0 .3rem;
}

.fil-pas:not(:last-child)::after {
    content: ''; position: absolute;
    left: calc(50% + 1rem); right: calc(-50% + 1rem); top: .72rem; height: 1px;
    background: var(--bord);
}

.fil-puce {
    grid-row: 1; width: 1.45rem; height: 1.45rem;
    display: flex; align-items: center; justify-content: center;
    border-radius: 999px; border: 1px solid var(--bord);
    background: var(--carte); color: var(--sourdine); font-size: .7rem;
}
.fil-nom {
    grid-row: 2; align-self: center; text-align: center;
    font-size: .74rem; color: var(--sourdine); line-height: 1.15;
    max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.fil-echo {
    grid-row: 3; align-self: start; max-width: 100%;
    font-size: .66rem; color: var(--sourdine); opacity: .8;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

.fil-pas[data-etat="franchie"] .fil-puce { border-color: var(--vert); color: var(--vert); }
.fil-pas[data-etat="franchie"] .fil-nom { color: var(--encre); }
.fil-pas[data-etat="courante"] .fil-puce {
    border-color: var(--or); color: var(--or);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--or) 16%, transparent);
}
.fil-pas[data-etat="courante"] .fil-nom { color: var(--encre); font-weight: 500; }
.fil-pas[data-etat="sans-objet"] .fil-puce { border-style: dashed; }
.fil-pas[data-etat="sans-objet"] .fil-nom { text-decoration: line-through; }
/* Ce qui n'est pas encore atteint s'efface, il ne se déplace pas. */
.fil-pas[data-etat="avenir"], .fil-pas[data-etat="sans-objet"] { opacity: .55; }

.fil-pas[data-vers] { cursor: pointer; }
.fil-pas[data-vers]:hover .fil-puce { border-color: var(--or); color: var(--or); }
.fil-pas[data-vers]:hover .fil-nom { color: var(--encre); }

/* ── L'étape et son vis-à-vis ────────────────────────────────────── */

.declarer-split {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 1rem minmax(0, var(--assistance, 24rem));
    align-items: start;
}

.declarer-split[data-plie] { grid-template-columns: minmax(0, 1fr) 1rem 0; }
.declarer-split[data-plie] .declarer-assistance { display: none; }
.declarer-split[data-plie] .ascenseur-plier { transform: scaleX(-1); }

.declarer-volet {
    display: grid; grid-template-columns: minmax(0, 1fr);
    align-content: start; padding: 2rem 0.1rem 0 0; min-height: 17rem;
}

.declarer-etape[hidden] { display: none; }
.declarer-etape h3 {
    font-size: 1rem; color: var(--encre); font-weight: 500;
    letter-spacing: 0; text-transform: none; margin: 0 0 .3rem;
}
.etape-intention {
    font-size: .82rem; line-height: 1.55; color: var(--sourdine);
    margin: 0 0 1.1rem; max-width: 42rem;
}
.declarer-etape label { margin-top: .7rem; }
.declarer-etape .vide { font-size: .82rem; color: var(--sourdine); }

/* Un choix court se dit en pastille, jamais en étiquette : une étiquette
   de cette maison est en petites capitales espacées, et une phrase y
   devient un mur. */
.choix { display: flex; flex-wrap: wrap; gap: .4rem; margin: .5rem 0 0; }
.choix-pastille { display: inline-flex; margin: 0; text-transform: none; letter-spacing: 0; }
.choix-pastille input { position: absolute; opacity: 0; pointer-events: none; }
.choix-pastille span {
    display: inline-block; padding: .35rem .9rem;
    border: 1px solid var(--bord); border-radius: 999px;
    color: var(--sourdine); font-size: .88rem; cursor: pointer;
}
.choix-pastille input:checked + span { border-color: var(--or); color: var(--or); }
.choix-pastille input:focus-visible + span { outline: 2px solid var(--or); outline-offset: 1px; }

.choix-case {
    display: inline-flex; align-items: center; gap: .3rem; margin: 0;
    text-transform: none; letter-spacing: 0; font-size: .78rem;
}
.choix-case input { width: auto; }

/* Une source est une liste : elle se saisit en lignes. Un signe de shell
   se signale alors sur la ligne fautive, pas sur un bloc entier. */
.lignes { margin: .45rem 0 0; }
.ligne { display: grid; gap: .35rem; align-items: center; margin: 0 0 .35rem; }
.ligne[data-ligne="argument"] { grid-template-columns: 1.1rem minmax(0, 1fr) 1.5rem; }
.ligne-reglage { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(0, .9fr) 3.4rem 1.5rem; }
.ligne-champ { grid-template-columns: minmax(0, 1fr) minmax(0, 1.2fr) minmax(0, 1fr) 4.5rem 1.5rem; }
.ligne-pleine, .tirage, .options-choix { grid-column: 1 / -1; }

/* **Un champ de circuit se règle en ligne, comme un argument de sonde**
   (#393) : le libellé et son nom technique empilés dans une même
   cellule — la règle du nom technique sous son libellé —, le type,
   l'obligation, le défaut, puis le retrait. Le type CHOIX déplie son
   sous-panneau, imbriqué, avec le même motif d'ajout et de retrait. */
.ligne-champ-circuit {
    grid-template-columns: 1.1rem minmax(0, 1.6fr) 7rem 5rem minmax(0, 1fr) 1.5rem;
}
.champ-circuit-entete {
    font-size: .7rem; letter-spacing: .08em; text-transform: uppercase; color: var(--sourdine);
}
.champ-circuit-nom { display: flex; flex-direction: column; gap: .2rem; min-width: 0; }
.champ-circuit-nom input { margin: 0; }

/* **Ce que le champ dit de lui-même** (#521) : l'exemple, montré dans
   le champ vide à l'ouverture, et l'aide, lue dessous. Sous la ligne,
   pleine largeur, parce que ce sont deux phrases et non deux mots. */
.champ-circuit-dit {
    grid-column: 1 / -1;
    display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.6fr); gap: .35rem;
    margin: .1rem 0 .2rem 1.9rem;
}
.champ-circuit-dit input { margin: 0; font-size: .8rem; }
.ligne-champ-circuit input[type="checkbox"] { justify-self: center; margin: 0; }
/* **Quatre pistes, parce que la ligne porte quatre enfants** (#523) :
   la poignée, la valeur, le libellé, l'ôte. La grille en déclarait
   trois quand la case du libellé est arrivée, et le bouton d'ôte
   tombait sur une rangée implicite, sous la poignée — l'entête, qui
   porte la même classe et ses quatre titres, se disloquait pareil.
   La planche « champs-de-circuit » donne les largeurs : la valeur est
   un jeton, le libellé une phrase. */
.ligne-option { grid-template-columns: 1.1rem minmax(0, 1fr) minmax(0, 3fr) 1.5rem; }
.options-choix {
    margin: .5rem 0 .3rem 1.9rem; padding: .5rem .7rem .65rem;
    border: 1px solid var(--bord); border-radius: 8px; background: var(--fond);
}
.options-choix .demi-titre { margin-top: 0; }

.ligne-poigne { color: var(--bord); font-size: .72rem; text-align: center; cursor: grab; }
.ligne-oter {
    width: 1.5rem; height: 1.7rem; padding: 0; line-height: 1;
    border-radius: 6px; border: 1px solid var(--bord);
    background: transparent; color: var(--sourdine); cursor: pointer;
}
.ligne-oter:hover { border-color: var(--ambre); color: var(--ambre); }
.ligne-ajout { margin: .1rem 0 0; }
.ligne-ajout .lien {
    background: none; border: none; padding: 0; cursor: pointer;
    font-size: .82rem; font-family: inherit;
}

/* **Le tirage ne se tape pas, il se choisit.** Il a d'abord été un champ
   texte où il fallait écrire « motif /(\d+)%/ » ou « ligne 2 colonne 2 » :
   un mini-langage à saisir à la main, dans un formulaire dont le métier
   est justement de remplacer le JSON brut. Le verbe se choisit, et seul
   son argument se montre. */
.tirage { display: flex; flex-wrap: wrap; gap: .35rem; align-items: center; margin-top: .1rem; }
.tirage select { max-width: 15rem; }
.tirage input { flex: 1 1 8rem; }
.tirage-cellule { display: flex; gap: .35rem; }
.tirage-cellule input { width: 4.5rem; flex: none; }
.tirage [data-champ-motif], .tirage [data-champ-chemin],
.tirage .tirage-cellule, .tirage [data-champ-colonnes] { display: none; }
.tirage[data-tirage="pattern"] [data-champ-motif],
.tirage[data-tirage="pattern_count"] [data-champ-motif],
.tirage[data-tirage="table"] [data-champ-motif] { display: block; }
.tirage[data-tirage="json"] [data-champ-chemin] { display: block; }
.tirage[data-tirage="column"] .tirage-cellule { display: flex; }
.tirage[data-tirage="table"] [data-champ-colonnes] { display: block; }

.etroit { max-width: 6rem; }
.mono { font-family: Consolas, monospace; font-size: .86rem; }

/* **Un réglage est à fournir, ou bien il a un défaut** — c'est tout ce
   qui les distingue, et en base c'est un défaut à NULL. Les deux moitiés
   vivent donc dans la même étape, chacune sous son titre. Celle qui vient
   des trous de la source ne se saisit pas : la redemander la déclarerait
   deux fois. Mais ne pas la montrer laissait chercher où saisir ce qui
   était déjà là. */
.demi-titre {
    font-size: .64rem; letter-spacing: .16em; text-transform: uppercase;
    color: var(--sourdine); margin: 1.3rem 0 .1rem;
}
.venus { margin-bottom: .2rem; }
.reglage-venu {
    grid-template-columns: minmax(0, 14rem) minmax(0, 1fr) auto;
    padding: .35rem .6rem; border-radius: 6px;
    background: var(--carte-haute); border: 1px dashed var(--bord);
}
.reglage-venu .venu-dit { color: var(--sourdine); font-size: .74rem; }
.reglage-venu .requis { justify-self: end; }

.seuil { grid-template-columns: minmax(0, 1fr) 5rem 2.5rem minmax(0, 8rem); }
.seuil .venu { color: var(--sourdine); font-size: .72rem; }

/* L'ascenseur : une poignée qu'on glisse, un chevron qui replie. */
.declarer-ascenseur {
    position: relative; align-self: stretch; min-height: 10rem;
    display: flex; flex-direction: column; align-items: center;
    justify-content: center; cursor: col-resize;
}
.declarer-ascenseur::before {
    content: ''; position: absolute; top: 0; bottom: 0; left: 50%;
    width: 1px; background: var(--bord);
}
.ascenseur-poigne {
    position: relative; width: .95rem; height: 2.6rem;
    border-radius: 999px; background: var(--fond);
    border: 1px solid var(--bord);
}
.ascenseur-poigne::before {
    content: ''; position: absolute; inset: .55rem .32rem;
    border-top: 1px solid var(--sourdine); border-bottom: 1px solid var(--sourdine);
}
.declarer-ascenseur:hover .ascenseur-poigne { border-color: var(--or); }
.ascenseur-plier {
    position: relative; margin-top: .4rem;
    width: 1.15rem; height: 1.15rem; padding: 0;
    border-radius: 5px; border: 1px solid var(--bord);
    background: var(--fond); color: var(--sourdine);
    font-size: .58rem; cursor: pointer;
}
.ascenseur-plier:hover { border-color: var(--or); color: var(--or); }

/* Le vis-à-vis rend les listes fermées du moteur : ce qui s'y ajoute
   apparaît ici sans qu'on touche à l'écran. */
.declarer-assistance { padding: 2rem 0.1rem 0 1rem; overflow: hidden; }
.assiste-fiche h4 {
    font-size: .8rem; color: var(--encre); font-weight: 500;
    margin: 0 0 .5rem;
}
.assiste-fiche h5 {
    font-size: .64rem; letter-spacing: .16em; text-transform: uppercase;
    color: var(--sourdine); margin: 1rem 0 .3rem;
}
.assiste-fiche p { font-size: .82rem; line-height: 1.6; margin: 0 0 .6rem; color: var(--encre); }
.assiste-note { color: var(--sourdine); }
.assiste-refus { color: var(--sourdine); }
.assiste-refus code {
    background: var(--fond); border: 1px solid var(--bord); border-radius: 4px;
    padding: 0 .25rem; font-size: .78rem;
}
.assiste-liste { margin: 0 0 .6rem; font-size: .8rem; line-height: 1.5; }
.assiste-liste dt {
    font-family: Consolas, monospace; font-size: .78rem;
    color: var(--or); margin-top: .35rem;
}
.assiste-liste dd { margin: 0; color: var(--sourdine); }
.assiste-exemple {
    background: var(--fond); border: 1px solid var(--bord); border-radius: 8px;
    padding: .5rem .7rem; margin: 0 0 .6rem;
    font-family: Consolas, monospace; font-size: .78rem; line-height: 1.5;
    white-space: pre-wrap; overflow-wrap: anywhere;
}
.assiste-lecture {
    margin-top: 1.2rem; padding-top: .8rem; border-top: 1px solid var(--bord);
}
.assiste-lecture p {
    font-family: Consolas, monospace; font-size: .76rem; line-height: 1.55;
    color: var(--sourdine); white-space: pre-wrap; overflow-wrap: anywhere;
    margin: 0;
}

/* ── L'épreuve, qui est une étape et non un bouton de pied ───────── */

.epreuve {
    display: flex; flex-wrap: wrap; align-items: center; gap: .6rem;
    margin-bottom: .7rem;
}
.epreuve .bouton-sobre { margin: 0; }
.epreuve .bouton-danger { margin: 0; }
.epreuve-trous { display: inline-flex; flex-wrap: wrap; gap: .4rem; }
.epreuve-trous span { display: inline-flex; align-items: center; gap: .3rem; }
.epreuve-trous label { margin: 0; font-size: .68rem; }
.epreuve-trous input { width: 8rem; padding: .25rem .45rem; font-size: .8rem; }
.epreuve-dit { font-size: .8rem; color: var(--sourdine); }
.epreuve-vu { color: var(--vert); }
.epreuve-rate { color: var(--ambre); }
.epreuve-rendu {
    background: var(--fond); border: 1px solid var(--bord); border-radius: 8px;
    padding: .5rem .7rem; margin: 0 0 .8rem;
    font-family: Consolas, monospace; font-size: .78rem; line-height: 1.55;
    white-space: pre-wrap; overflow-wrap: anywhere;
}

/* ── Le pied, qui reste dans le formulaire ───────────────────────── */

/* `data-modale-pied` déplace les actions hors de la balise `<form>` : un
   bouton d'envoi y perd son formulaire, et un geste délégué n'y retrouve
   plus le sien. Juste pour une modale d'une seule scène, faux pour un
   formulaire — et plus encore pour celui-ci, dont le pied navigue. */
/* **Et le pied non plus ne quitte pas l'écran.** C'est ce que le pied de
   modale apportait, et la seule chose qu'il apportait : on le garde en
   collant le pied au bas du corps qui défile, sans le sortir du <form>. */
.declarer-pied {
    position: sticky; bottom: 0; z-index: 3;
    display: flex; flex-wrap: wrap; align-items: center; gap: .7rem;
    background: var(--carte);
    /* Même débordement que le fil : sans lui, le contenu se voit
       défiler dans les deux gouttières de la marge du corps. */
    margin: 1.5rem -2.1rem 0; padding: .9rem 2.1rem 1.75rem;
    border-top: 1px solid var(--bord);
}
.declarer-pied [hidden] { display: none; }
.declarer-pied .bouton-sobre { margin: 0; }
.declarer-pied .bouton-danger { margin: 0; }
.declarer-pied .bouton-or, .declarer-pied .bouton-or[type="submit"] { margin: 0; }
.pied-rang {
    font-size: .7rem; letter-spacing: .1em; text-transform: uppercase;
    color: var(--sourdine); white-space: nowrap;
}

/* **Une porte fermée dit pourquoi elle l'est.** Un bouton éteint et muet
   est un cul-de-sac : on referme en croyant que rien n'était possible. */
.pied-pourquoi { flex: 1 1 14rem; font-size: .8rem; color: var(--sourdine); }

.bouton-suite {
    padding: .45rem 1.15rem; border-radius: 8px; cursor: pointer;
    background: transparent; border: 1px solid var(--or); color: var(--or);
    font-family: inherit; font-size: .86rem; white-space: nowrap;
}
.bouton-suite:hover { background: color-mix(in srgb, var(--or) 12%, transparent); }
.bouton-suite:focus-visible { outline: 2px solid var(--or); outline-offset: 2px; }
.bouton-suite:disabled {
    border-color: var(--bord); color: var(--sourdine);
    cursor: not-allowed; background: transparent;
}

/* ── L'échelle d'une étape ───────────────────────────────────────── */

/* **La modale respire quand elle porte trois champs ; une étape qui en
   porte quinze se serre.** La règle commune (#290) donne à tout champ de
   modale `.65rem .9rem` de rembourrage et `1.35rem` au-dessus de chaque
   étiquette : posée sur une ligne de champ à cinq colonnes, elle produit
   des surfaces de saisie énormes, et l'œil ne trouve plus la structure.
   L'échelle se resserre donc ici, et nulle part ailleurs.

   **Et un champ prend la largeur de ce qu'il attend.** `df` dans une
   boîte de neuf cents pixels ment sur ce qu'on lui demande. */
.modale-corps .declarer-etape input[type="text"],
.modale-corps .declarer-etape input[type="number"],
.modale-corps .declarer-etape select,
.modale-corps .declarer-etape textarea {
    padding: .35rem .55rem;
    font-size: .84rem;
}
.modale-corps .declarer-etape label { margin: .95rem 0 .25rem; }
.modale-corps .declarer-etape .ligne label,
.modale-corps .declarer-etape .choix-pastille { margin: 0; }

.declarer-etape input[name="commande"] { max-width: 22rem; }
.declarer-etape input[name="libelle"] { max-width: 28rem; }
.declarer-etape textarea { max-width: 34rem; }
.declarer-etape select[name="sujet"],
.declarer-etape select[name="lot"],
.declarer-etape select[name="cadence"] { max-width: 18rem; }
.declarer-etape .ligne[data-ligne="argument"] { max-width: 30rem; }
.declarer-etape .reglage-venu { max-width: 30rem; }
.declarer-etape .seuil { max-width: 26rem; }
.declarer-etape .ligne-reglage, .declarer-etape .ligne-champ { max-width: 46rem; }
.declarer-etape .ligne-oter { height: 1.5rem; }

@media (max-width: 62rem) {
    .declarer-split { grid-template-columns: minmax(0, 1fr); }
    .declarer-ascenseur { display: none; }
    .declarer-assistance {
        padding-left: 0; margin-top: 1rem; padding-top: 1rem;
        border-top: 1px solid var(--bord);
    }
    .fil-pas .fil-nom, .fil-pas .fil-echo { display: none; }
    .fil-pas[data-etat="courante"] .fil-nom { display: block; }
    .fil-pas { flex: 0 0 auto; min-width: 0; }
    .fil { gap: .1rem; }
}

/* ── Une partie de commande porte sa nature (#350) ───────────────── */

/* **Cochée, une partie devient un paramètre** : ce qu'on y écrit est son
   nom et non plus un littéral. Les deux se ressemblaient trait pour
   trait, il fallait relire la case pour savoir ce qu'on lisait. */
.ligne-entete {
    grid-template-columns: 1.1rem minmax(0, 1fr) 9rem 11rem 1.5rem;
    font-size: .62rem; letter-spacing: .16em; text-transform: uppercase;
    color: var(--sourdine); margin-bottom: .2rem;
}

/* **Une ligne n'est pas un argument** (#519). La colonne de plus porte
   « collé au précédent », et le liseré de gauche groupe les lignes qui
   n'en font qu'un : trois arguments et trois parties d'un seul se
   ressemblaient trait pour trait. Il est transparent partout ailleurs,
   pour que rien ne se décale quand une case se coche. */
.ligne-entete, .ligne-partie { border-left: 2px solid transparent; padding-left: .4rem; }
.ligne-partie { grid-template-columns: 1.1rem minmax(0, 1fr) 9rem 11rem 1.5rem; max-width: 52rem; }
.ligne-partie[data-groupe] { border-left-color: color-mix(in srgb, var(--or) 40%, transparent); }
.ligne-partie[data-fournir] [data-partie] {
    color: var(--or); border-color: color-mix(in srgb, var(--or) 45%, var(--bord));
}
.ligne-partie[data-fournir] .choix-case:not(.choix-collee) span { color: var(--or); }

/* La première ligne n'a pas de précédente : sa case garde sa place et se
   tait, sans quoi la colonne se décalerait d'une ligne à l'autre. */
.ligne-partie[data-premiere] .choix-collee { visibility: hidden; }
.ligne-partie[data-collee] .choix-collee span { color: var(--vert); }

.parametre {
    grid-template-columns: minmax(0, 12rem) minmax(0, 9rem) minmax(0, 1fr) 3.4rem minmax(0, 11rem);
    max-width: 51rem;
    padding: .4rem .6rem; border-radius: 7px;
    background: var(--carte-haute); border: 1px solid var(--bord);
}

/* Le nom d'un paramètre et son nombre d'usages tiennent la même cellule
   (#519) : la pastille se lit contre le nom, et non dans une colonne à
   elle qui aurait décalé toute la ligne. */
.param-tete { display: flex; align-items: center; gap: .4rem; min-width: 0; }
.param-nom { color: var(--or); font-size: .82rem; }
.param-usages {
    font-size: .64rem; color: var(--sourdine); white-space: nowrap;
    background: var(--fond); border-radius: 8px; padding: .05rem .4rem;
}
.param-dit { font-size: .7rem; color: var(--sourdine); justify-self: end; text-align: right; }
.param-dit[data-pose] { color: var(--vert); }
.param-dit[data-pose] { color: var(--vert); }

/* La remise à zéro vit dans le pied, discrète : c'est un geste rare et
   destructeur, il ne se met pas à côté de ce qui engage. */
.pied-raz { font-size: .78rem; color: var(--sourdine); white-space: nowrap; }
.pied-raz:hover { color: var(--ambre); }

/* L ecart d une reprise se lit a cote du bouton qui la pose. */
.pied-ecart { font-size: .78rem; color: var(--sourdine); flex: 1 1 12rem; }

/* **La part dit son titre et sa rangée sans style en attribut** (#408) :
   trois `style=` vivaient dans le gabarit des tableaux, et un quatrième
   habillait l'adresse sous le titre. */
.part-tete-lien { float: right; }
.part-rangee { margin: .4rem 0 1rem; }

/* **Le geste sur une sélection** (#409). La barre n'apparaît que lorsque
   quelque chose est coché : une barre vide en permanence apprend à ne
   plus la voir. */
.pilotage-coche { width: 1.6rem; text-align: center; }
.pilotage-coche input { cursor: pointer; }
/* **La barre garde une place pour l'écran** (#520). Un geste qui connaît
   la nature de ce qu'il touche ne se déduit d'aucun coin d'action : il se
   pose ici, avant ceux que le mécanisme a déduits. */
.pilotage-selection-ecran {
    display: flex;
    gap: .5rem;
    align-items: center;
    flex-wrap: wrap;
}

/* **Ce qui ne se défait pas se voit avant de se cliquer** (#520) : le
   geste irréversible du lot porte la couleur du refus, et non celle des
   gestes qui se rejouent sans conséquence. */
.pilotage-selection .lien-danger {
    color: var(--rouge);
    text-decoration: none;
    border: 1px solid var(--rouge);
    border-radius: 5px;
    padding: .18rem .6rem;
}
.pilotage-selection .lien-danger:hover { color: var(--rouge); filter: brightness(1.2); }

/* **Le lot se prend aussi par groupe** (#520). Le lien s'aligne à droite
   de la tête, loin de la flèche qui replie ; ce qu'il a coché se lit dans
   la tête même, parce qu'un groupe replié compte encore. */
.pilotage-groupe-tout { margin-left: auto; }
.pilotage-groupe-coches { font-size: .78rem; color: var(--or); }
.pilotage-groupe:has(.pilotage-groupe-coches:not(:empty)) td {
    box-shadow: inset 3px 0 0 var(--or);
}

.pilotage-selection {
    display: flex;
    align-items: center;
    gap: .7rem;
    flex-wrap: wrap;
    margin: .4rem 0 .2rem;
    padding: .45rem .7rem;
    border: 1px solid var(--or);
    border-radius: 6px;
    background: color-mix(in srgb, var(--or) 6%, transparent);
}
.pilotage-selection-compte { color: var(--or); font-size: .85rem; }
.pilotage-selection-gestes { display: flex; gap: .5rem; align-items: center; flex-wrap: wrap; }
.pilotage-selection-dire { color: var(--sourdine); font-size: .82rem; }
.pilotage-selection [data-tout-decocher] { margin-left: auto; }
