/* COPIE AUTOMATIQUE DE public/base/coquille-retenue.css -- NE PAS EDITER ICI.
   La source est au banc ; `python tools/base/porter_reprise.py` refait
   cette copie, et son mode `verifier` signale toute divergence. */
/* =========================================================================
   LA COQUILLE
   =========================================================================

   CE QU ELLE COUVRE, ET POURQUOI ELLE PASSE EN PREMIER. Releve sur les 105
   pages du banc : la barre du haut est sur 77 pages, le menu et le pied sur 76,
   le fond a orbes sur 89, la modale de sortie sur 77. Six familles, une
   quarantaine de classes, et l'ossature de tout le produit tient dessus. Aucun
   autre lot n'a ce rapport entre ce qu'il coute et ce qu'il couvre.

   ELLE APPARTIENT A LA COUCHE `elements`, dont elle est separee le temps de la
   construction : `elements-retenus.css` porte les decisions accumulees au banc,
   celle-ci porte l'ossature. Les deux fusionneront a la promotion. L'ordre de
   chargement reste celui du plan et c'est lui qui fait tout tenir :

       jetons.css              les primitives
       socle-retenu.css        les roles          <- ce fichier ne lit QUE ca
       coquille-retenue.css    l'ossature
       elements-retenus.css    les autres recettes
       pages/<nom>.css         un seul par page

   LA REGLE DURE : pas une seule valeur de couleur ecrite ici. Ni hexadecimal,
   ni `rgba()`, ni un barreau d'echelle en clair. Si le role manque, on
   l'ajoute dans `socle-retenu.css` en disant pourquoi. C'est ce qui permet a un
   theme de ne toucher aucune de ces lignes.

   CE QUI N EST PAS ENCORE PASSE EN VARIABLE : LA GEOMETRIE. Les espacements
   releves vont de 4 a 52 px sans grille (4, 8, 9, 10, 12, 14, 16, 18, 22, 26,
   32, 40, 44, 52) et meritent la meme echelle que les voiles. Ils sont ici
   RECOPIES TELS QUELS, a dessein : deplacer la geometrie en meme temps que la
   couleur rendrait le temoin illisible, et un pixel se voit ou une opacite de
   0,02 ne se voit pas. C'est le lot suivant.

   D OU VIENNENT CES VALEURS. Du style CALCULE sur `prat-clients`, releve dans
   les deux themes (`tools/base/coquille.py`), pas de la lecture des feuilles :
   l'apparence finale est le resultat d'une cascade de 5 700 regles, et la
   source ne peut pas servir d'oracle.
   ========================================================================= */


/* -------------------------------------------------------------------------
   LE FOND — sur 89 pages
   -------------------------------------------------------------------------
   Quatre orbes floutees derriere tout le produit. Elles portent les couleurs
   du parcours, pas celles de l'interface : c'est de la DONNEE au sens de la
   regle du socle -- deux orbes n'ont pas la meme teinte, et c'est ce qui fait
   le fond. Elles gardent donc leurs jetons de teinte.
   ------------------------------------------------------------------------- */

/* LE CORPS POSE LA COULEUR, ET RIEN D AUTRE NE LA REPOSE. Treize elements de
   la coquille n'ont pas de couleur propre : ils prennent celle de la page. Leur
   en donner une remettrait treize occasions de se contredire au prochain
   theme. */
/* « SYSTEME » ET « ACTUELLE », RETENUS A L ATELIER.

   LA POLICE DU SYSTEME N EST PAS UN RENONCEMENT. Elle est deja installee, donc
   elle ne coute aucun chargement et ne fait clignoter aucun texte au premier
   affichage ; et elle est celle que la personne lit toute la journee dans son
   systeme, ce qui la rend transparente. Une interface d'outil gagne a ne pas
   se faire remarquer par sa lettre.

   TREIZE PIXELS ET UNE INTERLIGNE DE 1,45 -- « echelle serree », choisie a
   l'atelier. Le produit tenait 14 et 1,5 ; la comparaison avait deja ecarte ce
   qui montait au-dessus, parce que les tableaux de clients passaient sous la
   ligne de flottaison. Le cran choisi va dans le meme sens, d'un cran de plus.

   CE CHANGEMENT DURCIT LE SEUIL DE CONTRASTE SUR UNE PARTIE DES TEXTES : un
   texte gras de 14 px compte comme grand et se juge a 3,0 ; le meme a 13 px
   n'est plus grand et se juge a 4,5. Ce qui passait peut donc cesser de
   passer, et c'est mesure apres, pas suppose. */
body {
    /* LE NAVIGATEUR POSE HUIT PIXELS DE MARGE, et le produit les retirait. Sans
       cette ligne toute la page se decale, la barre ne touche plus les bords et
       le fond a orbes laisse voir une bande blanche : trois symptomes pour un
       seul oubli. */
    margin: 0;

    color: var(--texte-plein);
    font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
    font-size: 13px;
    line-height: 1.45;
}

/* « DOUCE », RETENU A L ATELIER. Une ombre a peine posee sous les titres : elle
   ne se voit pas, elle se sent -- le titre cesse d'etre a plat sur le fond.

   ELLE CHANGE DE SENS AVEC LE THEME et pas seulement de valeur. Sur sombre elle
   creuse en noir ; sur clair une ombre noire salirait, donc elle s'allege et se
   teinte d'encre. C'est le seul endroit de la coquille ou le theme decide
   d'autre chose que d'une primitive, et c'est assume : une ombre n'est pas une
   couleur, c'est un rapport au fond. */
h1,
.page-header__title {
}

html[data-theme="clair"] h1,
html[data-theme="clair"] .page-header__title {
}


.mimex-bg {
    position: fixed;
    inset: 0;
    overflow: hidden;
    z-index: 0;
    pointer-events: none;
}

/* ELLES SONT ENORMES ET TRES FLOUES, et c'est ce qui en fait une LUMIERE
   plutot que quatre taches. 42 % de la largeur de la fenetre chacune, un flou
   de 74 px : a cette echelle on ne voit plus des disques, on voit le fond
   prendre la couleur de leur melange.

   J'avais ecrit leur teinte et leur forme sans leur taille ni leur place :
   quatre disques de zero pixel empiles dans un coin, donc un fond plat. Sur une
   capture, un fond plat ressemble a un fond sobre -- c'est la mesure qui l'a
   dit, `width: 0px`. */
.mimex-bg-orb {
    position: absolute;
    width: 42vw;
    height: 42vw;
    min-width: 320px;
    min-height: 320px;
    border-radius: 50%;
    opacity: 0.42;
    filter: blur(74px);
}

/* LE FOND A SES PROPRES TEINTES, ET CE N EST PAS UN DOUBLON. Je les avais
   branchees sur `--journey-*-rgb`, les couleurs des ETAPES du parcours : les
   deux familles sont egales en theme sombre, donc rien ne se voyait. En clair
   elles divergent -- le fond s'assourdit pour ne pas faire quatre taches sur
   un sol clair -- et deux orbes sur quatre rendaient la mauvaise couleur.
   Deux jetons egaux ne sont pas le meme jeton. */
/* LES QUATRE A EGALITE, ET LEURS PLACES DEBORDENT DU CADRE. Chacune sort d'un
   cote different : c'est ce debordement qui fait que la lumiere vient de
   quelque part au lieu de flotter au milieu. Les quatre teintes sont au meme
   cran -- le bleu menait autrefois, et le fond tirait au bleu ; a egalite,
   aucune ne mene et le fond prend la couleur du melange. */
.mimex-bg-orb--one {
    top: -14%;
    left: -12%;
    background: rgba(var(--mimex-blue-rgb), var(--voile-8));
}

.mimex-bg-orb--two {
    top: 12%;
    right: -16%;
    background: rgba(var(--mimex-purple-rgb), var(--voile-8));
}

.mimex-bg-orb--three {
    bottom: -22%;
    left: 24%;
    background: rgba(var(--mimex-green-rgb), var(--voile-8));
}

.mimex-bg-orb--four {
    bottom: -18%;
    right: 18%;
    background: rgba(var(--mimex-gold-rgb), var(--voile-8));
}


/* -------------------------------------------------------------------------
   LA BARRE DU HAUT — sur 77 pages
   -------------------------------------------------------------------------
   ELLE FLOTTE, ELLE NE COUVRE PAS. Le flou est ce qui la detache du contenu ;
   l'opacite reste au barreau 11 pour qu'on devine ce qui passe dessous. Poussee
   plus haut, elle ferait une barre pleine et la page perdrait sa profondeur.

   SON FILET CHANGE DE TEINTE PORTEUSE, ET C EST UNE CORRECTION. Il etait bati
   sur `--slate-light-rgb`, qui vaut 148 163 184 en sombre mais 248 250 252 en
   clair : un trait presque blanc sur un fond presque blanc, donc pas de trait
   du tout. Mesure faite, la barre, la tete du menu et la pastille de compte
   perdaient toutes les trois leur filet en basculant. `--filet-neutre-*` est
   bati sur `--white-rgb`, qui se retourne vraiment.
   ------------------------------------------------------------------------- */

/* « COMPACTE », RETENU A L ATELIER. Elle repose sur la teinte du SOL
   (`--surface-sombre`) et non sur l'ardoise : posee sur le fond a orbes, elle
   les laisse passer sans les colorer. Son filet descend au barreau 4, le plus
   faible, parce qu'il court sur toute la largeur. */
/* ELLE EST COLLANTE, ET CE QUI SE COLLE SOUS ELLE DOIT CONNAITRE SA HAUTEUR.

   Un second element colle plus bas se range sous `top: 0` du VIEWPORT, pas sous
   la barre : ses premiers pixels passent DERRIERE elle, et sa hauteur calculee
   en `100vh` deborde d'autant par le bas. C'est ce qui est arrive au panneau de
   la « Repartition des ressources », ecrit avant que cette barre soit collante.

   Le jeton porte la mesure une fois. Relevee sur le rendu a 1120, 1600 et
   1920 px : 60,8 px, invariable (`min-height: 50px` + le remplissage + la
   marque). On arrondit AU-DESSUS.

   IL EST DECLARE ICI, avec la barre qu'il decrit. Sans la reprise il n'y a pas
   de barre collante : les feuilles qui s'en servent ecrivent
   `var(--barre-haut-hauteur, 0px)`, et l'ancienne geometrie tient telle
   quelle. */
:root {
    --barre-haut-hauteur: 61px;
}

.lk-top-bar {
    position: sticky;
    top: 0;
    z-index: 1500;
    /* TROIS COLONNES : la marque, la navigation, le compte. `auto 1fr auto`
       plutot que des largeurs -- le releve du produit donne 240/865/190 sur une
       page et 240/947/108 sur une autre, c'est-a-dire la meme regle vue deux
       fois. Sans colonnes, `display: grid` empile : la barre faisait 347 px de
       haut et les six onglets se suivaient verticalement. */
    display: grid;
    grid-template-columns: auto 1fr auto;
    gap: var(--e-9);
    align-items: center;
    min-height: calc(var(--haut-barre) - 11px);   /* 2 x 5 de marge, 1 de filet */

    /* PLUS DE MARGE SOUS LA BARRE (08/10/2026).
       Elle valait 18 px et poussait TOUT le contenu de la page vers le bas,
       rail compris -- releve par l'Admin : « il y a un margin de 18 sur
       lk-top-bar, la barre de navigation, qui decale tout ». Une barre colle a
       ce qu'elle surplombe ; l'ecart entre elle et le contenu se decide dans
       le contenu, pas dans la barre, sinon chaque page herite d'un blanc
       qu'elle n'a pas demande et qu'elle ne peut pas reprendre. */
    margin: 0;
    padding-block: 5px;
    padding-inline: 52px;

    background: var(--surface-barre);
    backdrop-filter: var(--flou-barre);
    -webkit-backdrop-filter: var(--flou-barre);

    /* « BARRE PLEINE », CHOISIE A L ATELIER : une surface, pas une ligne de
       partage. Son filet descend d'un cran sous celui des autres bandes --
       0,08 contre 0,09 -- parce qu'ici la surface porte deja la separation et
       que le trait n'a qu'a la fermer. Il ne passe pas par
       `--filet-neutre-large`, que le pied et les cartes partagent. */
    border-bottom: var(--filet-epaisseur) solid rgba(var(--white-rgb), 0.08);
}

.lk-top-bar__brand {
    display: flex;
    align-items: center;
    gap: var(--e-6);
    text-decoration: none;
    color: inherit;
}

/* LA MARQUE ET SON SOUS-TITRE SONT EMPILES, pas en ligne. Ecrits en ligne, le
   « Physio-Psycho Scanner » passait a cote du nom au lieu d'etre dessous, et la
   marque prenait toute la premiere colonne. */
.lk-top-bar__brand-texte {
    display: grid;
    gap: 1px;
    text-decoration: none;
    color: inherit;
}

.lk-top-bar__brand-subtitle {
    font-size: 10px;
    letter-spacing: 0.06em;
    color: var(--texte-second);
}

/* UNE GRILLE PLACE DES COLONNES, elle ne dit rien de ce qui se passe DANS une
   colonne. Sans cette ligne, le `<nav>` empile ses six liens et la barre fait
   233 px de haut au lieu de 61 -- le meme oubli que la barre elle-meme, un cran
   plus bas.

   ELLE SE CENTRE parce qu'elle a plus de place que de contenu : la marque et le
   compte sont ranges a leurs bords par la grille, et la navigation occupe le
   milieu sans s'y coller a gauche. */
.lk-top-bar__nav {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--e-2);
}

/* LES DEUX ENTREES DE TRAVAIL SE TIENNENT A PART. « Espace praticien » et
   « Administration » ne sont pas de la meme famille que « Mon parcours » : ce
   ne sont pas des pages du produit, ce sont des ESPACES. Le filet le dit sans
   un mot, et c'est pour ca qu'il vaut mieux qu'un intervalle plus large :
   l'espace se lit comme du hasard, le trait comme une decision.

   `flex` ET NON `block` : ecrit en block, ce conteneur empilait ses deux
   onglets et la barre passait de 61 a 85 px de haut. */
.lk-top-bar__travail {
    display: flex;
    align-items: center;
    gap: var(--e-2);
}

.lk-top-bar__filet {
    width: 1px;
    height: 29px;
    margin: 0 var(--e-3);
    background: var(--filet-neutre-bloc);
}

.lk-top-bar__compte {
    display: flex;
    align-items: center;
    justify-content: flex-end;

    /* TROIS COMMANDES, TROIS OBJETS, ET RIEN NE LES SEPARAIT. Le menu du
       compte, la bascule du theme et l'interrupteur du mode guide se
       suivaient sans un pixel entre eux : mesure, le theme finit a 1334 et le
       guide commence a 1334. Colles, ils se lisaient comme un seul controle a
       trois parties.

       LE REMPLISSAGE DU BOUTON DE COMPTE NE SUFFIT PAS A LES ECARTER : il
       s'arrete a son propre bord, et ce bord est invisible depuis que ce bouton
       n'a plus de plaque. Sans fond, seul l'ECART dit ou finit un objet et ou
       commence le suivant. */
    gap: var(--e-5);
}

/* LA MARQUE NE SE CASSE PAS. La colonne de gauche vaut `auto`, donc elle se
   reduit au minimum quand la place manque : sans cette ligne, « Strannik
   Online » passait sur deux lignes vers 1 000 px et la barre montait de 61 a
   96 px. Le produit l'ecrivait, la base l'avait oublie. */
.lk-top-bar__brand-name,
.lk-top-bar__brand-subtitle {
    white-space: nowrap;
}

.lk-top-bar__brand-name {
    font-size: 18px;
    font-weight: 900;
    letter-spacing: 0.07em;
}

/* C EST L IMAGE QUI DONNE SA HAUTEUR A LA MARQUE, donc a la barre : 40 px, pas
   32. Ecrite trop courte, la barre se tassait et le sous-titre touchait le
   nom. */
.portal-brand__image {
    display: block;
    height: 40px;
    width: auto;
}

/* LA SEULE CHOSE QU UN THEME NE PEUT PAS OBTENIR PAR UNE PRIMITIVE.

   Le logo est une IMAGE : aucune redefinition de couleur ne lui donnera son
   contour. Le theme clair demande la version bordee, donc la base porte ici sa
   seule regle de theme qui vise un composant -- et c'est ecrit pour qu'on ne
   croie pas avoir rate une variable.

   L'image d'origine est masquee plutot que remplacee : la balise garde son
   `src`, donc son texte de remplacement et son sens restent intacts pour qui
   ne voit pas la page. */
html[data-theme="clair"] .portal-brand__image {
    /* `content` ET NON UN FOND PLUS UNE OPACITE. La premiere version posait
       l'image claire en FOND, puis masquait la balise par `opacity: 0` pour
       cacher le `src` d'origine. Or l'opacite s'applique a l'element ENTIER :
       elle emportait le fond avec elle, et le theme clair n'avait plus de logo
       du tout -- une case vide a gauche du nom.

       CE QUI L A LAISSE PASSER : les proprietes calculees disaient exactement
       ce qu'on esperait lire, `background-image` charge et `opacity` a zero.
       Les deux etaient vraies ; leur ENSEMBLE ne l'etait pas. Il fallait
       regarder l'image.

       `content` remplace ce que la balise AFFICHE sans toucher a ce qu'elle
       EST : le `src` reste, donc le texte de remplacement et le sens aussi
       pour qui ne voit pas la page. Et l'element reste opaque. */
    content: url("/images/strannik-mark-clair.png");
}

/* L'ONGLET AU REPOS EST CALME, PAS ETEINT. Il doit se lire sans se disputer
   avec celui qui porte l'etat : la difference se joue sur un barreau, pas sur
   une couleur. */
/* « ONGLET TEXTE », CHOISI A L ATELIER : rien au repos, un voile au survol.
   La barre se lit comme une phrase, pas comme une rangee d'objets -- aucune
   pastille, aucun fond, aucun contour tant qu'on ne la touche pas.

   DIX PIXELS DE RAYON, ET NON LE RAYON DU CONTROLE. Un onglet sans fond ne
   montre son rayon qu'au survol : il n'a pas a s'aligner sur un champ, qui le
   porte en permanence. */
.lk-top-bar__link {
    display: block;
    padding: 8px 14px;
    border-radius: 10px;
    background: none;
    border: 0;
    box-shadow: none;
    text-decoration: none;
    text-transform: none;
    letter-spacing: normal;
    white-space: nowrap;
    font-size: 14px;
    color: var(--texte-calme);
    transition: background .18s ease, color .18s ease;
}

/* Le traitement choisi ne porte ni marque ni pastille : ce que d'autres
   variantes posaient en `::before` doit donc cesser d'exister, pas seulement
   devenir transparent -- une boite vide garde sa place. */
.lk-top-bar__link::before,
.lk-top-bar__link::after {
    content: none;
}

/* L'effleurement se leve (`--surface-effleuree` : un voile de blanc des deux
   cotes) ; `--surface-controle` est un voile d'encre sur clair et grisait
   l'onglet sous la main. */
.lk-top-bar__link:hover {
    background: var(--surface-effleuree);
    color: var(--texte-vif);
}

/* « GRAS », RETENU A L ATELIER : L ETAT NE PORTE PLUS DE PASTILLE.
   Il se dit par le poids et par le plein, rien d'autre. C'est le traitement le
   plus discret des dix, et il tient parce que la barre n'a que six entrees :
   sur une barre longue il faudrait un reperage plus fort.

   CE QUE CA LIBERE. La pastille d'accent etait le seul aplat de teinte de la
   coquille ; sans elle, l'accent ne sert plus qu'a ce qui MENE quelque part.
   Une couleur qui ne designe qu'une chose se lit sans qu'on l'explique. */
/* =========================================================================
   L'ONGLET DE LA CONSULTATION

   L'Admin, le 17/10/2026 : « en gras avec surbrillance coloree (couleur
   consultation) et un icone de notification, ou un point puis attirer
   l'attention ».

   ELLE EST ICI ET PAS DANS `lk.css`, ET C'EST MESURE. Ecrite la-bas, elle
   etait lue puis defaite : `.lk-top-bar__link--consultation` et
   `.lk-top-bar__link` ont la meme specificite, une classe chacune, donc c'est
   la derniere declaree qui gagne -- et la reprise est servie APRES `lk.css`.
   A l'ecran : `display: block` au lieu de `inline-flex`, fond transparent, et
   une pastille cyan heritee de l'accent de la coquille. Seul `font-weight`
   tenait, parce que la regle de base ne le repose pas.

   LA COULEUR EXISTE DEJA ET ELLE A UN NOM : `--consultation-rgb`, le violet
   que portent la banniere de seance et le bouton du praticien. En reprendre un
   autre ferait deux violets pour une meme chose.

   IL NE S'ALLUME PAS COMME LES AUTRES. `is-active` dit « vous etes sur cette
   page » et se pose sur toutes les entrees ; celle-ci doit se voir meme QUAND
   ON N'Y EST PAS, puisque tout son interet est d'appeler. Sa surbrillance est
   donc dans sa classe, pas dans son etat, et `is-active` ne fait que
   l'appuyer.
   ========================================================================= */

/* LE MEME FOND QUE « MONTRER CETTE SECTION AU CLIENT », et c'est une demande :
   « je le veux du meme fond que le bouton "montrer cette section au client",
   c'est le meme sujet ». Le degrade est repris a l'identique de
   `.consultation-section-share__button` dans `diagnostic-viewer.css` -- memes
   jetons, memes alphas -- pour que les deux gestes de la consultation se
   reconnaissent d'un coup d'oeil, de la barre du haut jusqu'au bouton de
   l'ecran. */
.lk-top-bar__link--consultation {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-weight: 700;
    color: var(--ink-50);
    background: linear-gradient(135deg, rgba(var(--consultation-deep-rgb), 0.82), rgba(147, 51, 200, 0.82));
    border: 1px solid rgba(var(--consultation-rgb), 0.62);
}

.lk-top-bar__link--consultation:hover {
    background: linear-gradient(135deg, rgba(124, 47, 214, 0.9), rgba(168, 60, 226, 0.9));
    border-color: rgba(196, 132, 252, 0.8);
    color: var(--white);
}

/* SUR SA PROPRE PAGE, IL S ETEINT, comme le bouton « Montrer » quand la
   section est deja montree : le geste est fait, il n'appelle plus. */
.lk-top-bar__link--consultation.is-active {
    background: rgba(107, 48, 163, 0.34);
    border-color: rgba(var(--consultation-rgb), 0.42);
    color: rgba(var(--violet-pale-rgb), 0.86);
}

/* `[hidden]` CONTRE UN `display` DECLARE : la regle ci-dessus pose
   `inline-flex`, et l'attribut de la page ne pese rien a cote. Sans cette
   ligne, l'onglet resterait visible hors consultation -- c'est-a-dire tout le
   temps, puisque c'est l'etat ordinaire. */
.lk-top-bar__link--consultation[hidden],
.lk-menu__lien--consultation[hidden] {
    display: none !important;
}

/* LE POINT QUI APPELLE.

   IL RESPIRE, ET IL S'ARRETE QUAND ON LE DEMANDE. Un point fixe se confond
   avec une puce de liste ; un point qui bat se voit du coin de l'oeil, ce qui
   est exactement son role. `prefers-reduced-motion` l'arrete sans le faire
   disparaitre : ce qu'il signale reste vrai pour qui ne veut pas d'animation.

   SA COULEUR EST ECRITE, PAS HERITEE. Elle l'etait, et elle sortait cyan :
   la pastille prenait l'accent de la coquille des que le fond du lien etait
   efface. */
.lk-top-bar__point-consultation {
    /* `inline-block` ET PAS LE DEFAUT, parce que ce point vit dans DEUX
       contenants.

       MESURE DU 17/10/2026, dans le menu du compte : le `span` rendait
       0 x 18 et calculait `display: inline`. Une boite en ligne ignore
       `width` et `height`, donc le point n'avait aucune largeur -- et ce
       qu'on voyait n'etait pas lui mais son HALO, sept pixels d'etalement
       autour d'une boite de zero, soit un trait violet qui battait.

       Dans la barre, le lien est en `flex` : ses enfants sont bloquifies et
       le point tombait juste. Dans le menu, le lien est en `block`.
       `inline-block` vaut dans les deux -- un enfant de flex le bloquifie en
       `block`, avec les memes huit pixels -- et il n'y a plus de contexte a
       connaitre.

       `vertical-align: middle` LE POSE SUR LA LIGNE DU MOT : en ligne, un
       `inline-block` s'aligne par defaut sur la base du texte, donc un point
       de huit pixels se retrouverait accroche sous les lettres. */
    display: inline-block;
    vertical-align: middle;

    flex: none;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: rgb(var(--consultation-rgb));
    box-shadow: 0 0 0 0 rgba(var(--consultation-rgb), 0.65);
    animation: consultation-battement 2s ease-out infinite;
}

@keyframes consultation-battement {
    0%   { box-shadow: 0 0 0 0 rgba(var(--consultation-rgb), 0.65); }
    70%  { box-shadow: 0 0 0 7px rgba(var(--consultation-rgb), 0); }
    100% { box-shadow: 0 0 0 0 rgba(var(--consultation-rgb), 0); }
}

@media (prefers-reduced-motion: reduce) {
    .lk-top-bar__point-consultation {
        animation: none;
    }
}

/* LE POINT QUI DIT QU IL Y A QUELQUE CHOSE A FAIRE, a cote de « Mes clients »
   et de « Administration ».

   L'ADMIN, LE 03/11/2026 : « Juste un point par exemple ? », et « un point
   dans la barre en haut, visible d'un coup d'oeil ».

   IL NE BAT PAS, ET C EST CE QUI LE SEPARE DU POINT DE CONSULTATION. Celui-la
   signale un evenement RARE et bref, une seance qui tourne maintenant : son
   battement se justifie parce qu'il s'eteint. Celui-ci est allume la plupart
   du temps -- il y a presque toujours un courriel en echec ou un compte sans
   referent -- et deux points qui battent en permanence dans un bandeau
   deviennent du bruit, c'est-a-dire qu'on cesse de les voir. Un point fixe se
   lit d'un coup d'oeil sans demander l'oeil en continu.

   SIX PIXELS ET PAS HUIT : il accompagne un mot, il ne l'annonce pas. Le point
   de consultation precede son libelle et porte l'onglet entier ; celui-ci est
   une marque posee en fin de mot.

   L AMBRE EST LE JETON DE LA MAISON pour cet etat, celui que les deux cartes
   « Ce qui attend » emploient (`.dashboard-card__state--attention`). Il porte
   le point SUR LA BARRE SOMBRE, ou il rend 12,01.

   MAIS IL NE SE RETOURNE PAS AU THEME CLAIR, et c'est une decision ecrite :
   `themes-retenus.css` dit « ON NE TOUCHE PAS [...] `--amber-rgb` : ils
   servent aussi de filet et d'aplat, ou 3,0 suffit, et les assombrir
   alourdirait tout ce qui les porte ». Mesure du 03/11/2026 sur la page
   reelle : 251/191/36 sur la barre claire (244/246/249) rend 1,54. Un point
   de six pixels a 1,54 n'est pas « visible d'un coup d'oeil », c'est une
   tache jaune pale qu'il faut chercher.

   LE THEME CLAIR A DEJA SON ENCRE POUR CET ETAT, et c'est elle qu'on prend :
   `--encre-etat-attention-rgb`, qui rend 7,67 sur la meme barre. C'est le
   meme couple que `html[data-theme="clair"] .dashboard-figure--attention`
   emploie vingt lignes plus loin dans `elements-retenus.css` : l'ambre comme
   aplat sur le sombre, l'encre d'etat sur le clair.

   DEUX REGLES EXPLICITES ET NON UN REPLI `var()`. Le jeton d'encre n'existe
   PAS dans le theme sombre -- mesure : chaine vide. Un
   `var(--encre-etat-attention-rgb, var(--amber-rgb))` dependrait alors de la
   difference entre « non declare » et « declare vide », et un jour ou
   quelqu'un le declarerait vide, la declaration deviendrait invalide et le
   point disparaitrait du theme sombre sans un mot.

   `inline-block` ET `vertical-align` : la lecon du point de consultation, qui
   rendait 0 x 18 dans le menu du compte. Ce point vit dans les deux memes
   contenants -- un lien en `flex` dans la barre, en `block` dans le menu --
   et `inline-block` vaut dans les deux. */
/* ET IL PREND LA COULEUR D ACCENTUATION, PAS L AMBRE.
   -------------------------------------------------------------------------
   L'ADMIN, LE 04/11/2026 : « Point pastille dans la barre et dans l'onglet,
   une preference pour le mettre dans la couleur d'accentuation, comme la
   notification (cyan/or) au lieu de jaune ».

   C'EST LA MEME FAMILLE QUE LA PASTILLE DES NOTIFICATIONS, qui prend
   `--portal-accent-rgb` : cyan sur le sombre, or sur le clair. Deux
   indicateurs voisins dans la meme barre, qui disent tous deux « il y a
   quelque chose a regarder », n'ont pas a porter deux couleurs.

   MESURE SUR LA PAGE REELLE, LE 04/11, contraste du point sur son fond :

       sombre, barre 5/8/13     accent cyan 90/230/248      13,48  (ambre : 12,01)
       clair,  barre 244/246/249 accent or 214/175/74         1,92
       clair,  barre 244/246/249 accent fonce 122/92/16       5,76  (menu : 5,06)

   L OR PLAT NE TIENT PAS SUR LE CLAIR, exactement comme l'ambre qu'il
   remplace : 1,92 pour un rond de six pixels, c'est une tache qu'il faut
   chercher. La pastille des notifications, elle, le porte sans peine parce
   qu'elle est une PILULE de 19 px avec son encre dessus -- la couleur n'y
   est pas lue seule.

   LE THEME CLAIR PREND DONC L ACCENT FONCE, qui est l'accent de ce theme
   ecrit pour etre lu (`--portal-accent-fonce-rgb`, l'or sombre des boutons
   et des liens). Meme famille, meme lecture, dans les deux themes.

   DEUX REGLES EXPLICITES ET NON UN REPLI `var()` : dans le theme sombre,
   `--portal-accent-fonce-rgb` existe et vaut un cyan sombre (0/150/200) qui
   ne tiendrait pas sur la barre noire. Chaque theme nomme donc le sien. */
.lk-top-bar__point-attente {
    display: inline-block;
    vertical-align: middle;

    flex: none;
    width: 6px;
    height: 6px;
    margin-left: 6px;
    border-radius: 50%;
    background: rgb(var(--portal-accent-rgb));
}

html[data-theme="clair"] .lk-top-bar__point-attente {
    background: rgb(var(--portal-accent-fonce-rgb));
}

/* L ADMINISTRATION GARDE SON JAUNE, ET LES DEUX POINTS SE DISTINGUENT.
   -------------------------------------------------------------------------
   L'ADMIN, LE 04/11/2026 : « Point jaune pour Administration finalement ».
   Les deux points voisinent dans la meme barre et ne parlent pas du meme
   espace : l'un dit que SON travail de praticien attend, l'autre que le
   produit attend. Une seule couleur pour les deux obligeait a lire quelle
   entree porte le point avant de savoir ce qui attend.

   L ACCENT POUR SOI, L AMBRE POUR LA MACHINE. L'accent est la couleur du
   compte -- c'est celle de la pastille des notifications, juste a cote. Ce
   qui releve de l'administration n'appartient a personne, et l'ambre est
   deja, partout dans le produit, la couleur de ce qui demande attention.

   LE THEME CLAIR REPREND SON ENCRE D ETAT, pour la raison mesuree quinze
   lignes plus haut : l'ambre plat rend 1,54 sur la barre claire, et
   `--encre-etat-attention-rgb` 7,67. */
.lk-top-bar__point-attente[data-point-attente="administration"] {
    background: rgb(var(--amber-rgb));
}

html[data-theme="clair"] .lk-top-bar__point-attente[data-point-attente="administration"] {
    background: rgb(var(--encre-etat-attention-rgb));
}

/* `[hidden]` CONTRE UN `display` DECLARE, meme piege que pour l'onglet de
   consultation : la regle ci-dessus pose `inline-block`, et l'attribut de la
   page ne pese rien a cote. Sans cette ligne, le point resterait visible
   quand rien n'attend -- c'est-a-dire qu'il ne dirait plus rien. */
.lk-top-bar__point-attente[hidden] {
    display: none !important;
}

.lk-top-bar__link.is-active {
    color: var(--texte-vif);
    font-weight: 700;

    /* LA DECISION ETAIT ECRITE, ELLE N ETAIT PAS APPLIQUEE. Le commentaire
       ci-dessus dit depuis l'atelier que l'etat ouvert ne porte plus de
       pastille ; la regle ne retirait pourtant pas le fond, et `lk.css` posait
       toujours son voile d'accent a 0,22. Sur la capture, l'onglet courant
       gardait sa gelule doree. Une decision qui n'est pas dans une declaration
       n'est pas une decision, c'est une note. */
    background: none;
    box-shadow: none;
}

/* LA PASTILLE DE COMPTE EST LA POUR ETRE TROUVEE, PAS POUR ETRE VUE. Elle porte
   un nom propre et une photo : deux choses qui attirent l'oeil toutes seules.
   Son fond reste donc au barreau 4, et son filet est celui d'un controle. */
.lk-top-bar__compte-bouton {
    display: inline-flex;
    align-items: center;
    gap: 9px;
    height: 42px;
    padding: 4px 12px 4px 4px;

    /* NI FOND NI FILET AU REPOS. Tout ce qu'il contient dit deja qu'il se
       clique : un AVATAR -- le seul objet colore de la barre -- un nom, et un
       chevron qui annonce un menu. Trois signes valent mieux qu'une boite, et
       la boite pesait dans une barre ou tout le reste est du texte nu.

       LE FOND REVIENT QUAND IL DIT QUELQUE CHOSE DE VRAI : au survol, la main
       est dessus ; ouvert, le menu est la. Une surface permanente dit « ceci
       est un bouton », ce que personne ne se demande.

       LE REMPLISSAGE NE BOUGE PAS, donc la cible non plus : on allege le
       dessin, pas la zone cliquable. */
    background: none;
    border: var(--filet-epaisseur) solid transparent;

    /* IL SUIT L AXE DU RAYON, comme les autres boutons. J'avais garde la gelule
       en disant qu'un rayon complet sur un objet de cette taille n'est pas un
       coin arrondi mais une forme -- l'argument tient, mais ce n'est pas le
       mien qui decide : l'atelier a ete regle et valide avec le rayon applique
       partout. */
    border-radius: var(--rayon-bouton);
    font-size: 14px;
    font-weight: 600;
    cursor: pointer;
    transition: background var(--duree-effleurement, .16s) ease;

    /* L ENCRE EST UN ROLE, PAS UNE CLARTE. `lk.css` pose ici `--ink-200`,
       c'est-a-dire un BARREAU de la palette : en theme sombre il donne un gris
       clair qui convient, en clair il donne (51, 65, 85), soit un cran sous
       l'encre courante. Le nom du compte -- un nom propre, et le seul repere
       personnel de la barre -- s'ecrivait donc plus pale que le reste, sur les
       quarante pages. Le modele de l'atelier le rend en encre pleine.

       DEPUIS QUE LE BOUTON N A PLUS DE PLAQUE, ca compte davantage : c'est le
       nom qui porte le bloc, il n'a plus de fond pour le designer. */
    color: var(--texte-plein);
}

/* NI FOND NI FILET, DANS LES TROIS ETATS. Le fond revenait au survol et menu
   ouvert ; retire aussi : l'avatar, le nom et le chevron disent deja ou est la
   main, et le chevron se retourne quand le menu est la. Ce qui reste du survol
   est le nom qui passe au vif. */
.lk-top-bar__compte-bouton:hover,
.lk-top-bar__compte-bouton[aria-expanded="true"] {
    background: none;
    border-color: transparent;
    color: var(--texte-vif);
}


.lk-top-bar__compte-marque,
.lk-top-bar__prenom {
    font-size: 14px;
    font-weight: 600;
}

/* LE CHEVRON EST UN CARRE TOURNE, PAS UN CARACTERE. Sept pixels de cote, deux
   bords, 45 degres ; le `<span>` est vide.

   LE DECALAGE EST UN `translate` ET NON UNE MARGE, et l'ordre des deux
   transformations compte : le conteneur est en `align-items: center`, donc
   toute marge etait aussitot recentree et chaque correction n'agissait qu'a
   moitie. C'est ecrit dans le produit et on le reprend tel quel.

   L OPACITE PASSE SUR LA COULEUR DU BORD. Le produit eteint l'element entier
   avec `opacity: .62` ; le resultat est le meme sur un objet vide, mais un
   barreau pose sur la couleur se retourne avec le theme et survit au jour ou
   l'on met quelque chose dans ce span. */
.lk-top-bar__chevron {
    width: 7px;
    height: 7px;
    border-right: 1.6px solid var(--texte-second);
    border-bottom: 1.6px solid var(--texte-second);
    transform: translateY(-2.5px) rotate(45deg);
    transform-origin: center;
    transition: transform .18s ease, border-color .18s ease;
}

.lk-top-bar__compte-bouton[aria-expanded="true"] .lk-top-bar__chevron {
    transform: translateY(2.5px) rotate(-135deg);
    border-right-color: var(--texte-fort);
    border-bottom-color: var(--texte-fort);
}

/* L AVATAR PORTE LA TEINTE DU PRODUIT parce qu'il designe la personne connectee,
   c'est-a-dire le seul objet de la barre qui parle de QUI regarde. Son filet est
   celui des petites surfaces, deja pose dans le socle. */
.avatar,
.lk-top-bar__initiale {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    overflow: hidden;

    background: var(--surface-pleine);
    border: var(--filet-epaisseur) solid var(--filet-accent);
    border-radius: 50%;
    color: var(--texte-accent);
    font-size: 13px;
    font-weight: 700;
    line-height: 1;
}


/* -------------------------------------------------------------------------
   LE MENU — sur 76 pages
   ------------------------------------------------------------------------- */

.lk-menu__tete {
    padding: 10px 12px 12px;
    border-bottom: var(--filet-epaisseur) solid var(--filet-neutre-bloc);
}

.lk-menu__groupe {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.lk-menu__lien {
    display: block;
    width: 100%;
    padding: 9px 12px;
    border-radius: var(--rayon-controle);
    text-decoration: none;
    font-size: 14px;
    color: var(--texte-fort);
    transition: background .18s ease;
}

.lk-menu__lien:hover {
    background: var(--surface-controle);
}

.lk-menu__lien[aria-current="page"] {
    background: var(--surface-active);
    color: var(--texte-vif);
}

/* LA DECONNEXION SE DISTINGUE, ET ELLE AVAIT CESSE DE LE FAIRE.

   `lk.css` lui donne un rouge depuis toujours, avec sa raison ecrite : « on ne
   la vise pas par erreur en cherchant un lien de navigation ». Les deux regles
   pesent une classe, mais la regle generique ci-dessus est lue apres : le lien
   de sortie prenait donc l'encre de ses voisins. Mesure, modele contre
   produit : `rgb(252, 165, 165)` d'un cote, `rgba(226, 232, 240, .94)` de
   l'autre, sur trente et une pages.

   UNE DECISION QUI NE SE VOIT PLUS N EST PLUS UNE DECISION. On la repose ici,
   apres la regle qui l'effacait.

   `--couleur-danger-doux` PLUTOT QUE `--red-soft` : c'est le meme rouge, mais
   calibre par theme -- pale sur l'ardoise, franc sur le blanc. Le rouge de
   `lk.css` ne connait qu'un fond. */
.lk-menu__lien--sortie {
    color: var(--couleur-danger-doux);
}

/* -------------------------------------------------------------------------
   LA BASCULE DE THEME EST UNE ENTREE DU MENU DU COMPTE
   -------------------------------------------------------------------------
   ELLE ETAIT UN BOUTON ROND DE 36 PX DANS LA BARRE, a droite du compte. Elle
   a demenage dans le menu le 25/10/2026, a la demande de l'Admin : « on peut
   aussi mettre le theme comme un onglet dans le menu compte, c'est pas comme
   si on allait changer toutes les 10 minutes le theme ».

   ET C EST LA BARRE QUI LE DEMANDAIT. Mesure a 360 px : 336 px utiles pour
   398 px de contenu, donc le bloc du compte tombait a la ligne suivante --
   barre a 112 px de haut au lieu de 61 -- et son menu, ancre a droite d'un
   bouton revenu a gauche, sortait de l'ecran de 152 px. Les deux commandes du
   compte coutaient 90 px ; il n'en reste qu'une.

   LES REGLES DU BOUTON ROND SONT PARTIES AVEC LUI. `lk-top-bar__theme` n'est
   plus emise par aucun gabarit : les garder aurait laisse un dessin complet
   pour un element qui n'existe pas, c'est-a-dire la prochaine fausse piste de
   celui qui cherchera pourquoi le theme ne ressemble pas a ca.

   LES DEUX TRACES RESTENT, EUX, et avec leurs noms : ils disent le DESSIN, pas
   l'endroit. Seul leur ancrage change -- de `.lk-top-bar__theme` au bouton du
   menu --, et c'etait le piege : une regle descendante dont le parent a perdu
   sa classe ne tombe pas, elle cesse simplement de s'appliquer, et les deux
   icones se seraient affichees l'une sous l'autre.

   ICI, ET PAS A LA PLACE DU BLOC RETIRE. C'est la troisieme regle de cette
   feuille a devoir etre ecrite APRES `.lk-menu__lien` -- la sortie et la
   consultation l'ont precedee, chacune avec sa mesure. Les deux pesent une
   classe, donc la derniere lue gagne : posee plus haut, celle-ci declarait
   `display: flex` pour un `display: block` calcule, le `gap` n'ecartait rien
   et l'icone se posait au-dessus du mot. Mesure dans le produit avant d'etre
   corrigee, pas devinee.
   ------------------------------------------------------------------------- */
.lk-menu__lien--theme {
    display: flex;
    align-items: center;
    gap: var(--e-3);

    /* ET `flex-start`, PARCE QUE LE NAVIGATEUR CENTRE LE CONTENU D UN
       `<button>`. Ce n'est aucune regle du produit -- cherchee dans les trois
       feuilles, elle n'y est pas --, c'est la feuille de l'agent utilisateur,
       et `text-align: left` ne l'annule pas : en flex c'est `justify-content`
       qui place, et il valait `center`. Mesure dans le menu : les neuf liens
       commencent leur texte a 92 px, le mot du theme a 153. Une entree seule
       decalee de soixante pixels dans une liste se lit comme un defaut, pas
       comme un choix. */
    justify-content: flex-start;

    /* UN `<button>` N HERITE NI DE LA FONTE NI DE L ENCRE de son parent, et il
       arrive avec un fond, un filet et un texte centre. Sans ces quatre lignes
       il se lisait comme un controle pose au milieu d'une liste de liens, au
       lieu de la ligne suivante de cette liste. */
    background: none;
    border: 0;
    font: inherit;
    text-align: left;
    cursor: pointer;
}

/* 18 PX ET NON 20 : l'icone accompagne un mot de 14 px au lieu de le
   remplacer. A 20 elle pesait plus que le texte qu'elle annonce. */
.lk-menu__theme-icone {
    flex: none;
    width: 18px;
    height: 18px;
}

/* LES DEUX ICONES SONT LA, LE THEME EN MONTRE UNE.
   -------------------------------------------------------------------------
   Le gabarit emet les deux traces et c'est cette feuille qui choisit, d'apres
   `data-theme` sur `<html>` : le dessin d'un bouton n'a pas besoin d'un script
   pour suivre un attribut que la feuille lit deja.

   `display` ET PAS `[hidden]` : un `hidden` pose sur un element auquel une
   regle d'auteur donne un `display` ne cache rien du tout.

   L ICONE DIT OU LE CLIC MENE, pas ou l'on est : un soleil dans le sombre veut
   dire « passer au clair ». */
.lk-menu__lien--theme .lk-top-bar__theme-lune {
    display: none;
}

html[data-theme="clair"] .lk-menu__lien--theme .lk-top-bar__theme-soleil {
    display: none;
}

html[data-theme="clair"] .lk-menu__lien--theme .lk-top-bar__theme-lune {
    display: block;
}


/* ET L ENTREE DE CONSULTATION EST ICI POUR LA MEME RAISON QUE SA VOISINE.

   Elle etait ecrite deux-cent-soixante lignes plus haut, donc AVANT
   `.lk-menu__lien`. Les deux pesent une classe : a poids egal, la derniere lue
   gagne, et le lien restait en bloc. Mesure du 17/10/2026 :
   `display: flex` declare, `display: block` calcule.

   CE QUE CA COUTAIT : en bloc, le `gap` ne s'applique pas. Le point se
   retrouvait colle au mot -- quand il avait une largeur, ce qui est une autre
   histoire, racontee a la regle du point. */
.lk-menu__lien--consultation {
    display: flex;
    align-items: center;
    gap: 8px;
    font-weight: 700;
}


/* -------------------------------------------------------------------------
   LA COQUE ET LE PIED — sur 76 pages
   -------------------------------------------------------------------------
   LE PIED DESCEND DE DEUX BARREAUX, pas d'un. Ses liens sont au barreau 10, sa
   mention au 9 : c'est la seule zone du produit ou l'on veut qu'on ne lise
   qu'en cherchant. Le filet qui l'ouvre est au barreau 4, le plus faible, parce
   qu'il court sur toute la largeur.
   ------------------------------------------------------------------------- */

/* -------------------------------------------------------------------------
   LE CADRE DE PAGE — sur 76 pages
   -------------------------------------------------------------------------
   TROIS ELEMENTS, ET UNE SEULE IDEE : la coque fait de la place au rail quand
   il y en a un, le contenu se centre, le pied se centre SUR LE CONTENU et non
   sur la fenetre.

   LE DECALAGE S ACCROCHE A LA PRESENCE DU RAIL. Il etait pose sans condition
   par la feuille du rail, donc la coque faisait de la place meme la ou il n'y
   en a pas : il fallait ne charger cette feuille qu'au bon endroit pour que ce
   soit juste, et rien ne le verifiait. `body:has(.prat-rail)` rend la chose
   impossible a rater -- l'espace existe si et seulement si le rail existe.

   LE CONTENU SE CENTRE PAR SES MARGES AUTOMATIQUES, pas par une marge fixe. Le
   produit ecrivait `margin: 0 20px` sur les pages sans rail et rien sur les
   autres : deux regles pour un seul comportement, et 20 px qui ne valaient que
   pour une fenetre de 1 440. `margin: 0 auto` avec une largeur maximale donne
   exactement le meme rendu sur les deux, a toutes les largeurs.

   LE PIED RESTE EN `content-box`, ET C EST VOULU. Son remplissage a gauche vaut
   la largeur du rail : ajoute a sa largeur au lieu d'etre pris dedans, il
   recentre le pied sur la zone de contenu au lieu de la fenetre. Ecrit en
   `border-box`, le pied se decalerait vers la gauche de la moitie du rail.
   ------------------------------------------------------------------------- */

.lk-wrapper,
.lk-content {
    position: relative;
    z-index: 1;
}

/* --- LE PIED RESTE EN BAS, MEME QUAND LA PAGE EST COURTE ------------------
   -------------------------------------------------------------------------
   RELEVE PAR L ADMIN LE 17/10/2026 : « il se retrouve parfois au milieu ou en
   haut si la page est vide ». Mesure sur `/notifications` : 152 px de vide
   SOUS le pied, sur une page qui ne defile meme pas. Un pied qui flotte au
   milieu d'un ecran a moitie vide se lit comme un contenu interrompu.

   LE PIED SUIT LE FLUX, il n'est pas colle : `position: fixed` le poserait
   par-dessus le contenu des pages longues, ce qui est pire. On etire
   l'enveloppe a la hauteur de la fenetre et on pousse le pied vers le bas avec
   la marge automatique -- il descend quand la page est courte et reste a sa
   place quand elle est longue.

   `100dvh` ET NON `100vh` : sur un telephone, `vh` compte la barre d'adresse
   retractee, donc l'enveloppe depasse de sa hauteur et fabrique un defilement
   d'une barre d'outils sur toutes les pages. `dvh` suit la fenetre reelle.

   C EST LE BODY QU ON ETIRE, ET J AVAIS D ABORD ETIRE LE WRAPPER. Le pied
   n'est pas dans `.lk-wrapper`, il est son FRERE : le body porte le fond, la
   barre du haut, le wrapper, PUIS le pied. Pose sur le wrapper, le `min-height`
   lui donnait toute la hauteur de la fenetre et repoussait le pied JUSTE EN
   DESSOUS -- mesure sur `/courses` : pied a 1001 px dans une fenetre de 900, et
   une page qui se mettait a defiler alors qu'elle n'en avait pas besoin. La
   reparation avait empire le defaut qu'elle visait.

   LE RAIL DU VISUALISEUR PORTE SON PROPRE PIED (voir `$railPorteLePied`) : sur
   ces pages-la il n'y a pas de `.lk-app-footer` dans le flux, et la regle ne
   trouve rien a pousser. Elle n'a donc pas a les exclure.

   LES ENFANTS HORS FLUX NE GENENT PAS : le calque de fond est `fixed`, les
   scripts et la modale sont `display: none`, et un conteneur flex les ignore.
   La barre du haut reste `sticky`, ce qui fonctionne sur un enfant de flex. */
body.lk-body {
    /* `border-box` PARCE QUE LE BODY A UN PADDING BAS DE 15 px. En `content-box`
       -- le defaut -- le `min-height` vise le CONTENU et le padding s'AJOUTE :
       mesure sur `/courses`, document de 915 px dans une fenetre de 900, donc
       une barre de defilement permanente de quinze pixels sur toutes les pages
       courtes. Le pied etait bien en bas et la page defilait quand meme. */
    box-sizing: border-box;
    display: flex;
    flex-direction: column;
    min-height: 100dvh;
}

body.lk-body > .lk-app-footer {
    margin-top: auto;
}

.lk-content {
    max-width: var(--large-page);
    margin: 0 auto;
    padding: var(--espace-page);
}

/* LES PAGES DE RESULTATS RESPIRENT MOINS HAUT ET MOINS BAS QUE LE RESTE.

   Demande de l'Admin le 08/10/2026 : « l'ecart entre les bords rail/droite,
   padding 32px, et entre la barre de navigation et le bas de la page, padding
   20px, sur l'ensemble des pages de resultats du visualiseur Online, soit les
   memes proportions que le visuel des resultats dans expert. ».

   LES 32 HORIZONTAUX SONT DEJA LA : `--espace-page` les pose. Ce qui changeait
   etait le vertical -- 32 en haut, 40 en bas, parce que le bas portait le pied
   de page. Le visualiseur n'en a pas : son rail porte le pied (voir
   `$railPorteLePied` dans le gabarit), donc le cran de plus en bas ne protege
   plus rien et ne fait que repousser le contenu.

   LA REGLE EST BORNEE PAR LE RAIL, pas par une route : `body:has(.viz-rail)`
   est vrai sur une page de resultats et sur elle seule. Une liste de routes se
   serait desaccordee de la condition du pied a la premiere planche ajoutee. */
body:has(.viz-rail) .lk-content {
    padding-block: 20px;
}

/* ET LE RETRAIT DU BAS SE POSE EN BLOC, PARCE QU UN REMPLISSAGE S EVAPORE.

   L'ADMIN, LE 31/10/2026 : « quand le bandeau de consultation est la, la
   marge du bas disparait », capture avec la barre de defilement en butee et
   le contenu colle au bord.

   CE QUI SE PASSE, ET C EST LE NAVIGATEUR. Ce conteneur defile
   (`overflow-y: auto`, pose par `seuils-retenus.css` sur le meme
   `body:has(.viz-rail)`). Le `padding-bottom` d'une boite qui defile N EST
   PAS compte dans sa zone defilable : tant que le contenu tient, on le voit ;
   des qu'il deborde, il s'evapore. Mesure sur la Personnalite, consultation
   active : `scrollHeight` 730 = la place du bloc (157) plus sa hauteur (573),
   et zero pixel apres lui.

   LE BANDEAU NE CAUSE RIEN, IL FAIT DEBORDER : il prend 79 px plus 20 de
   marge, et fait passer la page du cote ou le remplissage ne se voit plus.
   C'est pour ca que le defaut n'apparait qu'avec lui.

   UNE BOITE VIDE, ELLE, COMPTE. Elle vaut exactement ce que le remplissage
   valait, donc rien ne bouge quand le contenu tient ; et elle tient sa place
   quand il deborde. Le remplissage du bas part en meme temps, sinon les deux
   s'additionneraient dans le premier cas. */
body:has(.viz-rail) .lk-content {
    padding-bottom: 0;
}

body:has(.viz-rail) .lk-content::after {
    content: '';
    display: block;
    height: 20px;
}

/* ET LE BLOC PREND CE QUI RESTE, PAS TOUTE LA HAUTEUR.

   L'ADMIN, LE 31/10/2026 : « fais en sorte que le contenu des pages ne soit
   pas colle en bas de la page ».

   CE QUI SE PASSAIT. La coquille de cette page recoit une hauteur -- 100 %
   de la zone de contenu -- et le bloc de l'ecran prend 100 % D ELLE. Tant
   qu'il est seul, les deux coincident. Des qu'un bandeau s'ajoute au-dessus,
   le bloc continue de demander la hauteur ENTIERE, et le total passe
   au-dessus du cadre. Mesure sur la Personnalite, consultation active :
   coquille 593, bandeau 79 plus 20 de marge, bloc 593 -- soit 692 de contenu
   pour 593 de place, et 99 px qui sortent par le bas. C'est exactement la
   hauteur du bandeau, ce qui dit d'ou vient le defaut.

   UNE COLONNE FLEXIBLE LE DIT MIEUX QU UN CALCUL. `flex: 1 1 auto` sur le
   porteur du bloc veut dire « ce qui reste apres les autres », quels que
   soient le nombre et la hauteur de ces autres : un bandeau aujourd'hui, un
   deuxieme demain, aucun sur les pages qui n'en ont pas. Un `calc()` aurait
   fallu tenir a jour a chaque bandeau ajoute.

   `min-height: 0` PARCE QU UN ELEMENT FLEX NE DESCEND PAS SOUS SON CONTENU
   SANS LUI, et le bloc en porte beaucoup. Sans cette ligne, il reprendrait
   la hauteur qu'on vient de lui retirer.

   ON VISE LE PORTEUR PAR CE QU IL CONTIENT, pas par son rang : il n'a pas de
   classe, et `:last-child` designerait autre chose le jour ou la page gagne
   un pied. */
body:has(.viz-rail) .lk-content > .page-shell {
    display: flex;
    flex-direction: column;
}

body:has(.viz-rail) .lk-content > .page-shell > :has(> .vx-bloc) {
    flex: 1 1 auto;

    /* VINGT PIXELS DU BAS SE PRENAIENT ICI, ET ILS S'AJOUTAIENT A CEUX DE LA
       BOITE DE FIN.

       CE QU'ILS FAISAIENT. Le porteur demandait la hauteur ENTIERE, il restait
       38 px de debordement et le contenu touchait le bord ; un `padding-bottom`
       en `border-box` prenait le retrait SUR la hauteur de l'ecran au lieu de
       l'ajouter apres.

       CE QUI LES A RENDUS INUTILES. Le `min-height: 0` pose plus bas sur les
       trois ecrans qui se contraignent -- `#viewOrgans`, `#viewPersonality`,
       `#viewTherapy` -- fait ce travail-la depuis, et il le fait mieux : il
       laisse le porteur descendre sous son contenu au lieu de lui rogner vingt
       pixels.

       CE QU'ILS COUTAIENT. L'air sous le bloc valait 20 (la boite de fin,
       `.lk-content::after`) PLUS 20 (ce retrait), contre 20 en haut. Releve par
       l'Admin le 12/11/2026 sur les Organes et la Personnalite, mesure sur la
       page reelle : 20 en haut, 40 en bas. Sans ce retrait, 20 et 20.

       ET IL N'Y A RIEN A CRAINDRE AILLEURS. Ce retrait ne deplacait le bloc que
       la ou le bloc REMPLIT son porteur, c'est-a-dire sur ces trois ecrans-la.
       Mesure sur les deux autres familles : Interconnexions 93 px d'air avec ou
       sans, Synthese experte bloc de 2 461 px avec ou sans. Rien ne bouge, et
       aucun debordement n'apparait sur aucun des cinq ecrans mesures.

       LA BOITE DE FIN, ELLE, RESTE : c'est elle qui donne de l'air au BAS D'UNE
       PAGE QUI DEFILE, ou le porteur n'a plus rien a rogner. */
}

/* LA ZONE DE CONTENU EST UNE COLONNE, pour que la boite de fin PRENNE sa
   place au lieu de la demander.

   Tant que cette zone etait un bloc ordinaire, la coquille reclamait 100 %
   de sa hauteur et la boite de fin s'ajoutait APRES : le total depassait, et
   le defilement revenait par la porte qu'on venait de fermer. En colonne,
   les deux se partagent : la boite garde ses vingt pixels, la coquille prend
   ce qui reste.

   MESURE SUR LA PERSONNALITE, consultation active, fenetre de 1 536 :
   `scrollHeight` 651 pour 613 de hauteur visible avant, 613 pour 613 apres.
   Plus rien ne depasse. */
body:has(.viz-rail) .lk-content {
    display: flex;
    flex-direction: column;
}

body:has(.viz-rail) .lk-content > .page-shell {
    flex: 1 1 auto;
}

/* ET `min-height: 0` NE VAUT QUE POUR LES TROIS ECRANS QUI SE CONTRAIGNENT.

   Il autorise un element flex a descendre SOUS la hauteur de son contenu.
   C'est ce qu'il faut quand le contenu sait suivre -- `#viewOrgans`,
   `#viewPersonality` et `#viewTherapy` recoivent `height: 100%` dans
   `vue-expert-bloc.css` et s'ajustent a la boite qu'on leur donne.

   POUR LES AUTRES, C EST L INVERSE QUI SE PRODUIT. Leur contenu a sa taille
   propre : le porteur se faisait ecraser a la hauteur disponible et l'ecran
   sortait par le bas. Mesure sur la Synthese experte, consultation active :
   le bloc demande 710 px, le porteur tombait a 453, et il ne restait aucun
   air sous lui. Sans cette ligne, il prend ses 710, la page defile, et la
   boite de fin tient sa place -- 40 px d'air mesures, la aussi bien que sur
   les Interconnexions.

   LA LISTE EST CELLE DE `vue-expert-bloc.css`, et c'est volontaire : ces
   trois identifiants y sont deja nommes pour la meme raison. Deux listes de
   la meme chose se desaccordent ; celle-ci renvoie a l'autre. */
body:has(.viz-rail) .lk-content > .page-shell:has(#viewOrgans.view-section.active),
body:has(.viz-rail) .lk-content > .page-shell:has(#viewPersonality.view-section.active),
body:has(.viz-rail) .lk-content > .page-shell:has(#viewTherapy.view-section.active) {
    min-height: 0;
}

body:has(.viz-rail) .lk-content > .page-shell:has(#viewOrgans.view-section.active) > :has(> .vx-bloc),
body:has(.viz-rail) .lk-content > .page-shell:has(#viewPersonality.view-section.active) > :has(> .vx-bloc),
body:has(.viz-rail) .lk-content > .page-shell:has(#viewTherapy.view-section.active) > :has(> .vx-bloc) {
    min-height: 0;
}

body:has(.viz-rail) .lk-content::after {
    flex: 0 0 20px;
}

.lk-app-footer {
    box-sizing: content-box;
    position: relative;
    z-index: 1;

    /* LE RETRAIT DU RAIL SE RETIRE AUSSI DE LA LARGEUR, et c'est `content-box`
       qui l'exige : le creux de gauche s'AJOUTE a la largeur au lieu de la
       manger. Sans ce terme, a 900 px de fenetre le pied mesurait 868 + 256 =
       1124 et debordait de 224 px. A 1340 il ne se voyait pas -- 1258 tiennent
       dans 1340 -- ce qui est la pire facon d'avoir raison.

       Sans rail, `--retrait-rail` n'existe pas et le repli vaut zero : la regle
       s'applique partout sans avoir a demander s'il y en a un. */
    width: min(var(--large-pied), calc(100% - var(--e-11) - var(--retrait-rail, 0px)));
    margin: var(--e-12) auto 0;
    padding: var(--e-9) 0 var(--e-10);
    border-top: var(--filet-epaisseur) solid var(--filet-neutre-large);
}

/* L UN REMPLACE L AUTRE, ET C EST LA FEUILLE QUI LE DIT. « Seul » veut dire
   que sur une page de resultats le rail du visualiseur prend la place de celui
   de l'espace. Ecrite seulement dans le gabarit, cette regle laissait les deux
   se recouvrir a `left: 0` des qu'un balisage rendait les deux -- ce que fait
   le banc, qui fabrique le second par script sur une page portant deja le
   premier. L'un cachait l'autre selon l'ordre d'empilement, ce qui est la
   pire facon d'avoir raison. */
body:has(.viz-rail) .prat-rail {
    display: none;
}


/* LE RETRAIT SE CALCULE UNE FOIS, PAS UNE FOIS PAR RAIL. Il y en a deux, et
   ils ne sont jamais la ensemble : le corps annonce la largeur a retirer, la
   coque et le pied la lisent. Une regle de retrait par rail ferait deux
   endroits a corriger au prochain seuil, et rien ne dirait laquelle gagne le
   jour ou les deux seraient rendus par erreur.

   Sans rail, la variable n'existe pas et le repli vaut zero : la regle
   s'applique partout sans avoir a demander s'il y a un rail. */
body:has(.prat-rail),
body:has(.viz-rail) {
    --retrait-rail: var(--rail);
}

/* Ils suivent le pli a la meme vitesse que lui : c'est la meme largeur qui
   change de cote. */
.lk-wrapper,
.lk-app-footer {
    padding-left: var(--retrait-rail, 0px);
    transition: padding-left var(--duree-depli, .22s) var(--courbe, ease);
}

@media (prefers-reduced-motion: reduce) {
    .prat-rail,
    .viz-rail,
    .lk-wrapper,
    .lk-app-footer {
        transition: none;
    }
}

/* Le pied est decrit une seule fois, avec le cadre de page : deux regles pour
   un objet, et c'est la seconde qui gagne sans qu'on l'ait voulu. */

/* LA HIERARCHIE DU PIED NE VIENT PLUS DE L OPACITE SEULE. Elle reposait sur
   deux crans eteints -- 0,42 pour la rangee, 0,52 pour les liens -- dont aucun
   ne tenait le seuil du texte courant. Les liens montent donc a `calme` et la
   mention a `second` : deux crans qui se lisent, et une difference de corps
   par-dessus. La couleur n'avait pas a porter seule ce que la taille et le
   poids portent mieux. */
.lk-app-footer__links {
    display: flex;
    flex-wrap: wrap;
    gap: 16px;
    color: var(--texte-second);
}

.lk-app-footer__links a {
    color: var(--texte-calme);
    text-decoration: none;
}

.lk-app-footer__links a:hover {
    color: var(--texte-fort);
    text-decoration: underline;
}


/* -------------------------------------------------------------------------
   LA MODALE DE SORTIE — sur 77 pages
   -------------------------------------------------------------------------
   ELLE INTERROMPT, DONC ELLE EST OPAQUE. C'est le seul objet de la coquille qui
   ne laisse rien transparaitre, et le seul qui porte une ombre portee : les
   deux disent la meme chose, qu'il n'y a rien a lire derriere elle.
   ------------------------------------------------------------------------- */

.confirmation-sortie {
    width: 420px;
    padding: 22px 22px 18px;

    background: var(--surface-modale);
    border: var(--filet-epaisseur) solid var(--filet-neutre-bloc);
    border-radius: var(--rayon-bloc);
    box-shadow: var(--ombre-modale);

    /* `<dialog>` RECOIT `color: CanvasText` DU NAVIGATEUR, soit du blanc pur
       sous `color-scheme: dark`. Elle ne peut donc pas se contenter d'heriter
       comme le reste de la coquille : il faut la lui reposer. */
    color: var(--texte-plein);
}

.confirmation-sortie__titre {
    margin: 0 0 8px;
    font-size: 18px;
    font-weight: 700;
}

.confirmation-sortie__texte {
    margin: 0 0 14px;
    font-size: 14.5px;
    line-height: 1.5;
    color: var(--texte-calme);
}

/* CE CHAMP N A NI FOND NI FILET SUR LE PRODUIT, et je lui en avais donne sans
   mesurer. Le controle l'a rendu. On recopie ce qui est, et on le note : un
   champ de saisie sans delimitation dans une modale sombre est probablement un
   defaut, mais le corriger appartient au lot des champs, pas a celui de
   l'ossature. */
.confirmation-sortie__saisie {
    width: 100%;
    font: inherit;
}

.confirmation-sortie__ecart {
    height: 14px;
}

.confirmation-sortie__actions {
    display: flex;
    justify-content: flex-end;
    gap: 10px;
}


/* -------------------------------------------------------------------------
   LES BOUTONS — sur 86 et 93 pages
   -------------------------------------------------------------------------
   `.btn` et `.btn-secondary` rendent AUJOURD HUI la meme chose : meme fond,
   meme filet, meme texte, mesure sur la page. Le second nom ne designe donc
   aucune difference, il en promet une.

   ON NE LA TRANCHE PAS ICI. L'echelle des actions est deja posee dans
   `elements-retenus.css` -- l'aplat de teinte pour ce qui fait AVANCER,
   l'ardoise bordee pour ce qui ENGAGE, le lien pour ce qui mene AILLEURS, le
   rouge pour ce qui DETRUIT -- et c'est la qu'elle se decide, pas dans
   l'ossature. La coquille pose la forme commune ; la difference viendra de
   l'echelle des actions.
   ------------------------------------------------------------------------- */

/* LA FORME EST A TOUS LES BOUTONS, LA PEAU NE L EST PAS.

   CE QUI N ALLAIT PAS, mesure du 08/10/2026 sur la fiche utilisateur de
   l'administration : le bouton « Supprimer l'utilisateur » rendait
   `rgba(255, 255, 255, 0.14)`, c'est-a-dire la peau du bouton NEUTRE. Releve
   par l'Admin : « bouton Supprimer en rouge, danger ».

   LA CAUSE EST UN EX AEQUO TRANCHE PAR L ORDRE. `lk.css` pose
   `.btn-danger { background: #dc2626 }` et cette feuille posait
   `.btn, .btn-secondary { background: var(--surface-controle) }` : DEUX
   CLASSES CONTRE DEUX, donc c'est l'ordre de chargement qui decide, et la
   reprise passe apres. Le rouge etait ecrase sans qu'aucune regle ne soit
   fausse.

   CE N EST PAS LA PREMIERE FOIS, ET C EST LE POINT. `elements-retenus.css`
   porte deja l'exclusion -- `.lk-body .btn:not(:where(.btn-danger, ...))` --
   avec le commentaire qui dit pourquoi : « le rouge d'une action destructrice
   n'est pas une decoration, c'est l'avertissement lui-meme ». Elle avait ete
   posee la et oubliee ici. Une peau neutre ecrite a deux endroits demande
   deux exclusions.

   ON SCINDE PLUTOT QU ON EXCLUT EN BLOC : le danger garde la FORME du bouton
   -- sa boite, son rembourrage, son rayon, sa graisse -- et ne reprend que sa
   PEAU. Exclu de tout, il serait redevenu un `<button>` de navigateur.

   `:where()` PESE ZERO, donc la regle garde exactement ses deux classes :
   elle continue de battre ce qui la precede pour les boutons ordinaires, et
   rien d'autre ne bouge dans le produit. */

.btn,
.btn-secondary {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 9px 16px;

    /* LA MEME HAUTEUR QUE LES CHAMPS, ET ELLE EST DECIDEE AILLEURS. Sans elle
       le bouton tombait a 36,6 px pendant que le champ d'a cote en faisait
       45,2 : neuf pixels d'ecart sur une rangee ou les deux se suivent. Voir
       `--controle-hauteur` dans le socle, qui porte le pourquoi. */
    min-height: var(--controle-hauteur);

    border-radius: var(--rayon-bouton);
    font-size: 14px;
    font-weight: 600;
    text-decoration: none;
    cursor: pointer;
    transition: background .18s ease, border-color .18s ease;
}

.btn:not(:where(.btn-danger)),
.btn-secondary:not(:where(.btn-danger)) {
    background: var(--surface-controle);
    border: var(--filet-epaisseur) solid var(--filet-neutre-controle);
    color: var(--texte-fort);
}

/* LE SURVOL SUIT LA PEAU, PAS LA FORME. `lk.css` pose
   `.btn-danger:hover { background: #b91c1c }` ; sans cette exclusion le
   survol ramenait le gris que l'etat au repos venait de perdre, ce qui est
   la pire des deux moities. */
.btn:not(:where(.btn-danger)):hover,
.btn-secondary:not(:where(.btn-danger)):hover {
    background: var(--surface-controle-survol);
}

/* -------------------------------------------------------------------------
   LES DEUX RAILS — celui de l'espace sur 20 pages, celui du visualiseur sur 11
   -------------------------------------------------------------------------
   ILS NE SONT JAMAIS LA ENSEMBLE, et c'est le choix retenu : sur une page de
   resultats, le rail du visualiseur REMPLACE celui de l'espace. Lire des
   resultats est un mode, pas une page -- la question n'y est plus « ou suis-je
   dans mon espace » mais « de quelle planche parle-t-on ». Le retour en tete du
   rail et la barre du haut gardent les deux chemins de sortie.

   UNE SEULE PEAU POUR LES DEUX. Position, largeur, fond, flou, filet, ombre,
   defilement : tout ce qui fait un rail est ecrit une fois. Deux peaux
   separees se seraient desaccordees au premier reglage, et l'ecran qui les
   montre l'une apres l'autre est justement celui ou ca se verrait.

   « GROUPE » ET « CREUX », RETENUS A L ATELIER.

   IL COMMENCE OU LA BARRE FINIT, et cette hauteur est un JETON, pas un nombre
   recopie. Le produit ecrivait `top: 63px` quand la barre en faisait 61 : deux
   pixels de fente, invisibles jusqu'au jour ou l'on change la barre et ou la
   fente devient un chevauchement. Les deux lisent maintenant la meme valeur, et
   ne peuvent plus deriver l'une de l'autre.

   `box-sizing: border-box`, SANS QUOI IL DEBORDE. 232 declares plus deux fois
   14 de marge interieure font 261 px a l'ecran, alors que la coque ne s'ecarte
   que de 232 : le rail recouvrait 29 px du contenu, et rien ne le signalait --
   c'est le genre de chevauchement qu'on ne voit qu'au moment ou un bouton
   devient incliquable.
   ------------------------------------------------------------------------- */

.prat-rail,
.viz-rail {
    box-sizing: border-box;
    position: fixed;
    top: var(--haut-barre);
    left: 0;
    bottom: 0;
    z-index: 40;

    display: flex;
    flex-direction: column;
    gap: 4px;
    width: var(--rail);
    padding: 22px 14px 18px;
    overflow-y: auto;

    /* IL COUPE CE QUI DEPASSE PENDANT LE PLI. Les libelles reviennent des le
       debut du depliage, dans un rail qui n'en fait encore que 62 : sans cela
       ils se renvoient a la ligne, le rail double de hauteur et retombe, et ce
       sursaut se voit plus que le glissement.

       ECRIT `clip`, IL CALCULE `hidden`, et c'est normal : le rail defile deja
       verticalement, et `clip` sur un axe devient `hidden` des que l'autre
       n'est ni `visible` ni `clip`. Aucune barre horizontale pour autant --
       seuls `auto` et `scroll` en font apparaitre une. On garde `clip` ecrit :
       c'est l'intention, et elle vaudra si le defilement vertical disparait. */
    overflow-x: clip;

    background: var(--surface-rail);
    backdrop-filter: var(--flou-rail);
    -webkit-backdrop-filter: var(--flou-rail);
    border-right: var(--filet-epaisseur) solid var(--filet-rail);
    box-shadow: var(--ombre-rail);

    /* IL SE REPLIE, IL NE SE TELEPORTE PAS. Le pli fait passer 170 px d'un cote
       a l'autre : sans mouvement, le contenu saute sans que rien ne dise d'ou
       il vient. La coque recule avec la meme duree et la meme courbe, plus bas
       -- decalees, l'une decouvrirait du vide pendant que l'autre avance. */
    transition: width var(--duree-depli, .22s) var(--courbe, ease);
}

/* LE CONTEXTE EN TETE, PAS EN PIED. C'est la premiere chose qu'on cherche en
   arrivant : suis-je bien dans mon espace de travail. */
.prat-rail__qui {
    margin-bottom: 14px;
    padding: 0 10px;
}

.prat-rail__surtitre {
    color: var(--texte-second);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.09em;
    text-transform: uppercase;
}

.prat-rail__nom {
    margin-top: 3px;
    color: var(--texte-vif);
    font-size: 15px;
    font-weight: 700;
    line-height: 1.25;
}

/* LES SURTITRES DE GROUPE REMPLACENT LES SEPARATEURS. Un trait dit « ces deux
   choses ne vont pas ensemble » ; un surtitre dit LAQUELLE est laquelle. A
   nombre d'entrees egal, le second se parcourt sans etre relu.

   ILS MONTENT AU CRAN `second`. Ecrits a 34 % comme dans le produit, ils
   rendaient 2,9 de contraste sur dix pixels et demi de capitales : le seul
   endroit du rail qu'il fallait deviner. */
.prat-rail__groupe {
    margin: 16px 10px 5px;
    color: var(--texte-second);
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: 0.1em;
    text-transform: uppercase;
}

/* -------------------------------------------------------------------------
   LE BOUTON QUI REPLIE LE RAIL
   -------------------------------------------------------------------------
   IL EST DISCRET AU REPOS ET FRANC AU SURVOL, parce qu'il commande la FORME de
   la navigation et non la navigation elle-meme : il ne doit pas se disputer le
   regard avec les entrees. C'est le meme raisonnement que le bouton du compte
   dans la barre du haut -- un fond permanent dit « ceci est un bouton », ce que
   personne ne se demande d'un chevron.

   IL SE RANGE A DROITE QUAND LE RAIL EST OUVERT : c'est de ce cote que le rail
   se referme, donc c'est la que la main va le chercher. Replie, il se centre
   avec les icones -- il n'y a plus de cote.

   LE CHEVRON SE RETOURNE AVEC L ETAT, et c'est tout ce qui change de dessin :
   il pointe vers ou le clic emmene le bord du rail.
   ------------------------------------------------------------------------- */

/* LA TETE : LE BLOC D IDENTITE A GAUCHE, LE CHEVRON DANS LE COIN.
   -------------------------------------------------------------------------
   Le bouton avait sa rangee a lui, au-dessus du bloc d'identite : vingt-huit
   pixels de haut plus sa marge, sur toute la largeur du rail, pour un chevron
   de quinze. C'est de la hauteur prise au seul endroit ou elle compte -- tout
   ce qui est au-dessus des entrees les pousse toutes vers le bas.

   `align-items: flex-start` ET NON `center` : le bloc d'identite fait deux
   lignes, le bouton une ; centre, le chevron tombait entre le surtitre et le
   nom au lieu de tenir le coin. Il s'aligne sur la PREMIERE ligne, celle du
   surtitre, qui est le haut visible du bloc.

   `min-width: 0` SUR LE BLOC D IDENTITE, sans quoi un nom long refuse de se
   couper et pousse le bouton hors du rail : un element de boite flexible ne
   descend pas sous la taille de son contenu tant qu'on ne le lui dit pas. */
.rail-tete {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--e-4);
}

.rail-tete > .prat-rail__qui {
    min-width: 0;
}

.rail-bascule {
    flex: none;

    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;

    padding: 0;
    border: var(--filet-epaisseur) solid transparent;
    border-radius: var(--rayon-controle);
    background: none;
    color: var(--texte-calme);
    cursor: pointer;

    transition: background var(--duree-effleurement, .16s) ease,
                color var(--duree-effleurement, .16s) ease;
}

.rail-bascule:hover {
    background: var(--surface-effleuree);
    color: var(--texte-vif);
}

.rail-bascule:focus-visible {
    outline: 2px solid rgba(var(--accent-surface-rgb), 0.8);
    outline-offset: 2px;
}

.rail-bascule__chevron {
    width: 15px;
    height: 15px;
    transition: rotate .18s ease;
}

/* REPLIE, LE CHEVRON POINTE DANS L AUTRE SENS. `rotate` plutot qu'un second
   trace : un seul dessin dans le gabarit, et la rotation s'anime. */
html[data-rail="replie"] .rail-bascule__chevron {
    rotate: 180deg;
}

/* LE RAIL DU VISUALISEUR MET SON CHEVRON SUR LA LIGNE DU TITRE, comme le rail
   praticien met le sien sur celle de « ESPACE PRATICIEN ». Le titre tient la
   gauche, le chevron le coin droit, et la fiche en dessous n'a plus d'intitule.

   `align-items: flex-start` COMME CHEZ LE PRATICIEN, et c'est mesure des deux
   cotes : la-bas le surtitre tombe en y=83, ici il tombait en y=89 parce que la
   rangee le centrait sur un bouton de vingt-huit. L'Admin demande le meme
   endroit, donc on cale en haut. */
.viz-rail__qui {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--e-4);
}


/* CE QUE L ENREGISTREMENT DE LA NOTE REPOND, DANS SA MODALE.

   Elle y est parce que c'est la surface ou le geste a eu lieu, et parce que
   c'est la seule ou il se voie : la fenetre de choix de la demande couvre la
   page tant qu'aucune demande n'est prise.

   DISCRET PAR DEFAUT, ET ROUGE SEULEMENT A L ECHEC : une confirmation qui
   crie autant qu'une panne apprend a ne plus distinguer les deux. */
.viz-note__message {
    margin: 10px 0 0;
    font-size: .875rem;
    color: var(--texte-calme);
}

.viz-note__message--error {
    color: var(--danger, #f87171);
}


/* LES DEUX GESTES DE LA TETE DU RAIL, SUR UNE LIGNE.

   L'engrenage mene a la gestion des resultats, la bascule replie le rail.
   `align-items: center` les cale l'un sur l'autre : poses cote a cote sans
   cela, ils se seraient alignes sur le haut de la rangee, qui n'est pas le
   meme pour un lien et pour un bouton.

   `flex: none` PARCE QUE LE TITRE A COTE PEUT ETRE LONG : sans lui, la
   rangee prend sa place sur eux avant de couper le titre. */
.viz-rail__actions {
    flex: none;

    display: flex;
    align-items: center;
    gap: 2px;
}


/* L ENGRENAGE PORTE LA PEAU DE LA BASCULE, et n'ajoute que ce qui change :
   c'est un lien, donc il faut lui retirer le soulignement que les liens du
   rail portent. Lui ecrire une peau a lui ferait deux habillages a tenir
   pour deux gestes voisins de meme poids. */
.viz-rail__settings,
.viz-rail__settings:hover {
    text-decoration: none;
}


/* Sur une seule ligne, pour la meme raison : c'est le bord du rail qui les
   decouvre, pas eux qui se replient en attendant la place. */
.prat-rail__lien,
.prat-rail__groupe {
    white-space: nowrap;
}

.prat-rail__groupe:first-of-type {
    margin-top: 2px;
}

/* Les separateurs ne servent plus : les surtitres font le meme travail en
   disant quelque chose. */
.prat-rail__separateur {
    display: none;
}

.prat-rail__lien {
    display: flex;
    align-items: center;
    gap: 11px;
    padding: 9px 10px;
    border: var(--filet-epaisseur) solid transparent;
    border-radius: var(--rayon-controle);
    color: var(--texte-calme);
    font-size: 14px;
    text-decoration: none;
    transition: background 0.15s ease, color 0.15s ease;
}

.prat-rail__lien:hover {
    background: var(--surface-effleuree);
    color: var(--texte-vif);
}

.prat-rail__lien:focus-visible {
    outline: 2px solid rgba(var(--accent-surface-rgb), 0.8);
    outline-offset: 2px;
}

/* L ETAT SE CREUSE. La section ou l'on se trouve s'enfonce d'un cran, refermee
   en bas par une arete de lumiere : c'est ce qui lui donne son epaisseur.
   La profondeur vient du theme -- 28 % sur sombre, 9 % sur clair -- parce que
   la place disponible sous la surface n'est pas la meme.

   RENDUE LE 17/09/2026, APRES AVOIR DISPARU SANS QUE PERSONNE NE LE DISE.
   Le commit qui a ajoute le bouton presentiel juste en dessous (`4ee1df1e`) l'a
   emportee au passage, et son message n'en parle pas : l'insertion du nouveau
   bloc a recouvert celui-ci. Le COMMENTAIRE de l'icone active, lui, est reste
   -- il dit toujours « le creux dit OU l'on est » -- et c'est ce decalage entre
   ce que le fichier explique et ce qu'il fait qui a permis de retrouver le
   coupable. Une regle qui part sans son commentaire laisse une trace. */
.prat-rail__lien[aria-current="page"] {
    background: var(--creux-fond);
    box-shadow: var(--creux-relief);
    color: var(--texte-vif);
    font-weight: 700;
}

/* LE MODE PRESENTIEL SE DETACHE, PARCE QU IL NE VA PAS AU MEME ENDROIT.
   Les sept autres entrees menent a un ecran de l'espace ; celle-ci en SORT,
   pour une interface plein ecran ou le rail n'existe plus. Elle est donc
   posee au-dessus des groupes, cerclee de l'accent, et separee du reste par
   un filet -- c'est le seul endroit du rail ou un trait dit quelque chose que
   les surtitres ne disent pas.

   SA PEAU EST ICI, PAS AVEC LE MODE. Le mode presentiel a sa feuille
   (`public/css/presentiel.css`), chargee par sa coque et par elle seule ;
   mais CE BOUTON n'appartient pas au mode, il appartient au rail. Il se rend
   dans l'espace ordinaire, avec les sept autres entrees, et se lit avec
   elles. Une feuille a part l'aurait separe de ce qu'il complete.

   PAS D ETAT ACTIF : on n'est jamais dans le presentiel en voyant ce rail. */
.prat-rail__lien--presentiel {
    margin-bottom: 10px;
    padding: 11px 12px;
    border-color: transparent;

    /* LE DEGRADE PORTE L ACCENT DU PRODUIT, pas un cyan ecrit a la main. Sur
       le theme sombre -- celui du produit -- les deux jetons rendent le cyan ;
       sur le clair, ils rendent l'or, et le bouton reste coherent au lieu de
       trancher. Un hexadecimal fige ici aurait donne un bouton cyan sur une
       interface doree. */
    background: linear-gradient(135deg,
        rgba(var(--cyan-rgb), 0.94) 0%,
        rgba(var(--accent-surface-rgb), 0.66) 100%);

    /* L ENCRE SE DERIVE DE L APLAT plutot que d'etre posee en dur : elle
       fonce l'accent jusqu'a porter le texte, sur les deux themes. */
    color: color-mix(in srgb, rgb(var(--accent-surface-rgb)) 14%, #041016);
    font-weight: 700;

    /* PAS D OMBRE PORTEE. Un aplat cyan au milieu de liens sans fond est deja
       le seul objet colore du rail : le halo n'ajoutait pas de l'evidence, il
       ajoutait du flou sous le bouton. */
    box-shadow: none;
}

/* ========================================================================
   LE LIEN ETEINT : PRESENT, LISIBLE, ET QUI NE REPOND PAS
   ------------------------------------------------------------------------
   L'ADMIN, LE 23/10/2026 : « Visible et desactive oui ». Le mode presentiel
   DISPARAISSAIT du rail quand l'acces etait ferme ; il reste maintenant, sans
   `href`, avec sa raison dans l'infobulle.

   SANS CETTE REGLE IL EN AVAIT ENCORE L AIR. Mesure du 24/10/2026 sur la page
   reelle : `opacity: 1`, `cursor: auto`, le degrade cyan intact. Un bouton qui
   semble cliquable et ne fait rien est PIRE qu'un bouton absent -- l'absence
   se comprend, l'inertie silencieuse se prend pour une panne.

   LE DEGRADE PART, ET C EST LE POINT. L'aplat cyan est ce qui dit « entre
   ici » ; le garder eteint reviendrait a crier l'invitation en coupant la
   porte. On retombe sur le fond des autres liens du rail, avec l'encre
   affaiblie des elements inertes -- la meme que `.viz-rail__action`
   desactive, pour que les deux rails se ressemblent.
   ======================================================================== */
.prat-rail__lien[aria-disabled="true"] {
    background: rgba(var(--white-rgb), 0.06);
    border-color: rgba(var(--white-rgb), 0.14);
    color: rgba(var(--white-rgb), 0.38);
    box-shadow: none;
    cursor: not-allowed;
}

/* ET IL NE S ECLAIRE PAS AU SURVOL : sans cette regle il promettrait au doigt
   ce qu'il refuse au clic. */
.prat-rail__lien[aria-disabled="true"]:hover {
    background: rgba(var(--white-rgb), 0.06);
    border-color: rgba(var(--white-rgb), 0.14);
    color: rgba(var(--white-rgb), 0.38);
}

/* LE BOUTON EST UN `<button>` : il ouvre une modale, il ne mene nulle part.
   On lui retire ce que le navigateur donne a un bouton (police, largeur,
   alignement) pour qu'il reste identique aux liens du rail. */
button.prat-rail__lien {
    width: 100%;
    font: inherit;
    text-align: left;
    /* `lk.css` centre le contenu de tout `button` : le rail aligne a gauche.
       Rail replie, `seuils-retenus.css` recentre, et son selecteur pese plus. */
    justify-content: flex-start;
    cursor: pointer;
}

button.prat-rail__lien--presentiel {
    font-weight: 700;
}

.prat-rail__lien--presentiel:hover,
.prat-rail__lien--presentiel:focus-visible {
    border-color: transparent;
    background: linear-gradient(135deg,
        rgba(var(--cyan-rgb), 1) 0%,
        rgba(var(--accent-surface-rgb), 0.8) 100%);
    box-shadow: none;

    /* L ENCRE NE PASSE PAS AU BLANC. Le survol des liens du rail eclaircit le
       texte, ce qui va sur un fond sombre et rate completement sur un aplat
       cyan : le libelle disparaissait a moitie au moment ou on le vise. Elle
       reste celle de l'etat au repos. */
    color: color-mix(in srgb, rgb(var(--accent-surface-rgb)) 14%, #041016);
}

/* L ICONE SUIT L ENCRE, et gagne un cran d'epaisseur : sur un aplat clair, un
   trait de 1,9 px se dilue la ou il tenait sur une surface sombre. */
.prat-rail__lien--presentiel .prat-rail__icone {
    stroke-width: 2.1;
}

/* ========================================================================
   EN THEME CLAIR, L ENCRE DU BOUTON PLEIN PASSE AU BLANC
   ========================================================================
   L'ADMIN, LE 30/10/2026, capture a l'appui : « texte et icone en blanc dans
   le bouton plein ».

   POURQUOI ILS NE L ETAIENT PAS. L'encre de ce bouton se derive de l'aplat
   (`color-mix(... --accent-surface-rgb 14%, #041016)`), ce qui donne une
   encre tres sombre : juste sur le cyan clair du theme sombre, trop sombre
   sur l'or du clair.

   ET POURQUOI LE FOND BOUGE AUSSI, alors que seule l'encre etait demandee.
   Mesure du 30/10 sur les teintes rendues : le degrade du clair va de
   (152, 118, 33) a (209, 183, 117). Du blanc y rend 4,24 a gauche et 1,96 a
   DROITE -- il aurait disparu sur la moitie du bouton. L'encre demandee
   oblige donc l'aplat ; l'inverse aurait ete livrer un bouton illisible en
   disant qu'on a fait ce qui etait demande.

   LES JETONS EXISTAIENT DEJA, ET POUR CETTE RAISON EXACTE.
   `atelier-modeles.css` ecrit en toutes lettres : « LA VERSION PLEINE
   SURFACE RESTE PLUS SOMBRE ENCORE (`--accent-aplat`, 138/106/30) : c'est du
   blanc qui se pose dessus, et il lui faut 5. » Mesure : 5,05 sur
   `--accent-aplat`, 6,59 sur `--accent-aplat-survol`. On prend les deux, le
   bouton reste or, et le blanc tient d'un bout a l'autre.

   LE THEME SOMBRE N EST PAS TOUCHE : son cyan clair porte une encre sombre,
   et c'est juste ainsi.
   ======================================================================== */
html[data-theme="clair"] .prat-rail__lien--presentiel {
    background: linear-gradient(135deg, var(--accent-aplat) 0%, var(--accent-aplat-survol) 100%);
    color: #fff;
}

/* AU SURVOL, L APLAT S ECLAIRCIT D UN CRAN SANS QUITTER LE SEUIL : le bout
   le plus clair rend encore 5,05, celui que le produit s'est donne. */
html[data-theme="clair"] .prat-rail__lien--presentiel:hover,
html[data-theme="clair"] .prat-rail__lien--presentiel:focus-visible {
    background: linear-gradient(135deg, var(--accent-aplat-survol) 0%, var(--accent-aplat) 100%);
    color: #fff;
}

.prat-rail__lien--presentiel + .prat-rail__groupe {
    margin-top: 0;
    padding-top: 12px;
    border-top: var(--filet-epaisseur) solid var(--filet-couleur);
}

.prat-rail__icone {
    flex: none;
    width: 17px;
    height: 17px;
    opacity: 0.72;
}

/* L ICONE DE LA SECTION ACTIVE PREND LA TEINTE DU PRODUIT, a pleine opacite.
   C'est le seul accent du rail : le creux dit OU l'on est, l'icone le confirme
   sans ajouter un deuxieme signal de meme nature.

   ELLE LIT LE ROLE DU TEXTE, PAS CELUI DE LA SURFACE, et c'est mesure : l'or
   de surface rend 1,56 sur le creux clair, tres en dessous des 3,0 qu'un objet
   graphique demande. Un pictogramme de dix-sept pixels doit etre IDENTIFIE, pas
   seulement apercu : il est du cote du texte, pas du cote de l'aplat. Avec le
   role du texte il rend 5,67. */
.prat-rail__lien[aria-current="page"] .prat-rail__icone {
    opacity: 1;
    color: rgb(var(--accent-texte-rgb));
}

/* Le compte se range a droite, et ne pousse pas le libelle. */
.prat-rail__compte {
    margin-left: auto;
    padding: 1px 7px;
    border-radius: var(--rayon-gelule);
    background: var(--surface-discrete);
    color: var(--texte-calme);
    font-size: 11px;
    font-variant-numeric: tabular-nums;
}


/* -------------------------------------------------------------------------
   CE QUI EST PROPRE AU RAIL DU VISUALISEUR
   -------------------------------------------------------------------------
   IL PORTE LE PATIENT EN TETE, ET C EST CE QUI REND LE REMPLACEMENT TENABLE.
   Retirer le rail de l'espace ne coute rien tant qu'on sait de qui l'on lit les
   resultats et par ou revenir : le retour est la premiere chose du rail, le nom
   la deuxieme.

   SES ENTREES N ONT PAS D ICONE. Neuf pictogrammes pour neuf notions abstraites
   -- « dynamique emotionnelle », « repartition des ressources » -- seraient
   neuf devinettes. Le libelle est le seul reperage honnete ici, et en hauteur
   il a la place de s'ecrire en entier : c'est tout le gain de la forme.

   ET IL NE SE REPLIE PAS, pour la meme raison. Il est la en entier, ou pas du
   tout -- voir les seuils.
   ------------------------------------------------------------------------- */

/* LA TETE PORTE LE TITRE ET LE CHEVRON. Le nom, l'age et le poids restent dans
   la fiche en dessous : « de qui » et « laquelle de ses seances » sont une
   seule question, et deux blocs qui se suivent ne le disaient pas. Mais le
   TITRE, lui, n'est pas une donnee de la fiche -- c'est le nom de la page.

   PAS DE RETRAIT A DROITE : le chevron tient le bord du rail. Le retrait de dix
   est celui du TEXTE, pas de la rangee -- chez le praticien c'est le bloc de
   texte qui le porte et le bouton tient le coin. Porte par la rangee, il
   rentrait le chevron de dix pixels : mesure, x=203 ici contre x=213 la-bas. */
.viz-rail__qui {
    margin-bottom: 4px;
    padding: 0 0 0 10px;
}

/* LE RETOUR N'A PLUS SA RANGEE : il est passe sur celle du titre.

   L'ADMIN, LE 15/10/2026 : « mettre le lien vers "Mes clients" dans l'espace
   sous "Resultats de test" en haut du rail pour gagner un grand espace ». Sa
   rangee coutait 41 px pour un lien de 73.

   MESURE AVANT DE DEPLACER, A 227 PX : titre 119, retour 73, chevron 28, soit
   220 plus les gouttieres de 8 -- trois pixels de trop. D'ou les deux reglages
   ci-dessous : le retour prend la taille du titre, et le titre peut retrecir.

   LA REGLE DE SON ANCIENNE RANGEE EST RETIREE AVEC ELLE : une peau qui survit
   a son element attend le jour ou quelqu'un reutilise le nom. */

/* LE TITRE ET LE RETOUR S EMPILENT DANS LA HAUTEUR DU CHEVRON. Celui-ci fait
   28 px, le titre 16 : la rangee portait douze pixels de vide, et c'est la
   place que l'Admin a vue. Les deux y tiennent pour 34 px, contre 28 + 41
   quand le retour avait sa propre rangee. */
.viz-rail__tete {
    display: flex;
    flex-direction: column;
    /* DEUX PIXELS COLLAIENT LE LIEN AU TITRE. L'Admin, le 19/10/2026 :
       « certains elements du bloc identite en haut du rail sont trop colles
       entre eux verticalement ». Mesure a l'appui : 2 px entre le bas de
       « RESULTATS DE TEST » et le haut de « Mes clients », contre 6 a 10
       partout ailleurs dans la fiche.

       CINQ ET NON HUIT : la pile doit rester dans la hauteur du chevron
       (28 px), qui est ce qui a permis de rapatrier le lien ici. 16 + 5 + 13
       font 34, soit les deux pixels de depassement que la rangee acceptait
       deja ; a 8 elle en prendrait trois de plus et pousserait la fiche. */
    gap: 5px;
    min-width: 0;
}

/* IL PREND LA TAILLE DU TITRE : il est la navigation, pas le titre, et plus
   gros que lui il se lisait comme l'element principal de la rangee. */
.viz-rail__tete .viz-rail__retour {
    margin: 0;
    font-size: 11px;
    line-height: 1.2;
    white-space: nowrap;
}

/* LE TITRE EST CELUI QUI CEDE, et c'est le bon ordre : il nomme la page, le
   lien en sort. En francais, en anglais et en allemand il ne cede rien ; en
   russe « Результаты тестирования » se coupe avec des points de suite.

   EMPILE SOUS LE TITRE, IL NE LUI PREND PLUS SA LARGEUR : l'interlettrage et
   les points de suite qu'il avait fallu poser pour les faire tenir cote a cote
   sont retires avec cette disposition. Le titre retrouve sa forme, la meme que
   chez le praticien. */
.viz-rail__qui .viz-rail__surtitre {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* LA FICHE SERRE SON CONTENU. Elle avait dix pixels de marge en haut et en bas
   PLUS son propre remplissage : vingt-deux pixels d'air au-dessus du nom, pour
   une fiche de trois lignes posee dans une colonne de 256. Le titre parti,
   l'air restait.

   LE REMPLISSAGE LATERAL RESTE A DIX, et ce n'est pas un oubli : c'est lui qui
   aligne le nom sur le titre de la tete, qui a le meme retrait. Le ramener a
   zero collerait le texte au bord du creux et desalignerait les deux.

   `.viz-rail` DEVANT, POUR UN POIDS DE PLUS. `.viz-rail__bloc` est ecrite deux
   cents lignes plus bas et pose `margin` et `padding` en RACCOURCI : a poids
   egal c'est la derniere qui gagne, et un raccourci ecrase les quatre cotes
   d'un coup. Ecrite sans cet ancetre, cette regle-ci etait servie, lisible dans
   la feuille, et sans effet -- mesure : 10 / 10 / 12 rendus malgre elle. On ne
   deplace pas la regle de base, elle est a sa place au milieu des blocs du
   rail. */
.viz-rail .viz-rail__bloc--qui {
    /* HUIT ET NON SIX, DEPUIS LE 19/10/2026. Quatre rangees se suivaient a
       six pixels -- nom, mesures, seance, gestes -- sauf les mesures, posees a
       dix par leur propre marge : une fiche dont un seul ecart respire se lit
       comme une fiche serree. A huit, les quatre sont du meme ordre.

       LE REMPLISSAGE SUIT, EN HAUT COMME EN BAS : c'est la regle posee le
       16/10 (« equilibre l'espace entre le selecteur de seance et le bord bas
       sous les 3 boutons »), et elle tient parce que les trois valeurs sont le
       MEME jeton. Les changer ensemble la garde vraie. */
    gap: var(--e-4);
    margin-top: 0;
    margin-bottom: var(--e-4);
    padding-top: var(--e-4);
    /* LE BAS REPOND AU HAUT DE LA RANGEE DES GESTES.

       L'Admin : « equilibre l'espace entre le selecteur de seance et le bord
       bas sous les 3 boutons ». Mesure du 16/10/2026 : 14 px au-dessus de la
       rangee, 6 px en dessous -- la fiche se terminait trop court sous ses
       propres gestes.

       Le haut vaut le `gap` de la fiche, le bas ce remplissage : en posant le
       meme jeton, les deux font 6 px. Mesure apres : 6 et 6. Puis 8 et 8 le
       19/10, quand le `gap` est passe a `--e-4` : l'egalite est ce qui compte,
       pas la valeur. */
    padding-bottom: var(--e-4);
}

/* LA MARGE DE LA LIGNE DES MESURES S'AJOUTE AU `gap`, ELLE NE LE REMPLACE PAS.
   Mesure du 19/10/2026 : 10 px sous le nom, contre 6 entre les trois rangees
   suivantes -- quatre pixels de marge propre par-dessus le `gap` de la fiche.
   C'etait le seul ecart different des autres, et c'est ce qui donnait a la
   fiche son air irregulier. Les deux marges a zero, le `gap` decide seul. */
.viz-rail .viz-rail__bloc--qui .viz-rail__meta {
    margin-top: 0;
    margin-bottom: 0;
}

/* LE NOM ET LA BULLE SUR UNE LIGNE, la bulle poussee au bord droit. C'est le
   seul bouton de ce coin -- la note de travail ou le mot recu, jamais les deux
   -- donc un seul `margin-left: auto` suffit a le ranger. */
.viz-rail__nomligne {
    display: flex;
    align-items: center;
    gap: var(--e-3);
}

/* Le nom ne pousse pas la bulle hors de la fiche : sans `min-width: 0`, un
   element de boite flexible refuse de descendre sous la taille de son contenu. */
.viz-rail__nomligne > .viz-rail__nom {
    min-width: 0;
    margin-top: 0;
}

.viz-rail__surtitre {
    color: var(--texte-second);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.09em;
    text-transform: uppercase;
}

.viz-rail__nom {
    margin-top: 3px;
    color: var(--texte-vif);
    font-size: 15px;
    font-weight: 700;
    line-height: 1.25;
}

/* DEUX VALEURS COLLEES SE LISENT COMME UNE SEULE. Un point les separe, comme
   partout ailleurs dans le produit. */
.viz-rail__meta {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--e-2) var(--e-4);
    margin-top: 4px;
    color: var(--texte-second);
    font-size: 12px;
}

.viz-rail__meta > span + span::before {
    content: "·";
    margin-right: var(--e-4);
    opacity: 0.6;
}

/* LE RETOUR EST EN PREMIER, AVANT MEME LE NOM. C'est lui qui autorise le
   remplacement : cache, la lecture serait sans issue. */
.viz-rail__retour {
    display: inline-block;
    margin-bottom: 12px;
    color: var(--texte-second);
    font-size: 12px;
    text-decoration: none;
}

.viz-rail__retour:hover {
    color: var(--texte-vif);
    text-decoration: underline;
}

.viz-rail__planches {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

/* MEME LANGAGE QUE L AUTRE RAIL : meme remplissage, meme rayon, meme role de
   texte, meme creux pour la planche ouverte. Ce qui change est ce qui doit
   changer -- le libelle a le droit de passer a la ligne, ce que le rail de
   l'espace s'interdit parce qu'il se replie.

   « PAS D ICONE » : C ETAIT ECRIT ICI, ET CE N EST PLUS VRAI DEPUIS LE
   08/10/2026. Le raisonnement tenait tant que ce rail ne se repliait pas. Il se
   replie, et repli voulait dire PLUS DE PLANCHES DU TOUT : une colonne de 62 px
   avec un seul chevron, dix-neuf sections derriere, et rien pour y aller.
   Chaque planche a donc son dessin (`partials/icone-planche.blade.php`), rendu
   dans le document a tout moment et MONTRE par la feuille au seul repli --
   c'est `seuils-retenus.css` qui en decide, avec la mesure qui l'a motive.

   DEPLOYE, RIEN NE CHANGE : l'icone est `display: none`, le libelle garde toute
   la largeur, et le rail se lit comme avant. */
.viz-rail__icone {
    /* LES MEMES VALEURS QUE `.prat-rail__icone`, AU PIXEL : deux rails, un seul
       langage. 17 px, `flex: none` pour qu'aucune contrainte de place ne la
       rogne, et la meme encre en retrait que celle du rail de l'espace. */
    flex: none;
    width: 17px;
    height: 17px;
    opacity: 0.72;
}

.viz-rail__lien {
    /* UNE RANGEE, COMME CHEZ LE PRATICIEN : l'icone, puis le nom. C'etait
       `display: block` tant qu'il n'y avait que du texte. */
    display: flex;
    align-items: center;
    gap: 11px;
    padding: 9px 10px;
    border: var(--filet-epaisseur) solid transparent;
    border-radius: var(--rayon-controle);
    color: var(--texte-calme);
    font-size: 14px;
    line-height: 1.3;
    text-decoration: none;
    transition: background var(--duree-effleurement, .16s) ease,
                color var(--duree-effleurement, .16s) ease;
}

.viz-rail__lien:hover {
    background: var(--surface-effleuree);
    color: var(--texte-vif);
}

.viz-rail__lien:focus-visible {
    outline: 2px solid rgba(var(--accent-surface-rgb), 0.8);
    outline-offset: 2px;
}

/* LE LIEN NE PEINT PLUS DE FOND. Cette regle date du premier jour, quand le
   lien ETAIT l'onglet ; depuis, la peau est passee sur la RANGEE et le lien
   s'arrete avant la case a cocher. Le fond s'arretait avec lui : une boite
   sombre dans un onglet vert, coupee net avant la case.

   DEPLACER UNE PEAU D UN CRAN NE CONSISTE PAS A L ECRIRE AU NOUVEL ENDROIT, il
   faut la RETIRER de l'ancien. J'ai fait la premiere moitie trois fois -- ici
   et dans deux traitements d'essai -- et la seconde jamais.

   Il garde ce qui lui appartient encore : la couleur et la graisse de son
   texte, qui sont du texte et non de la surface. */
.viz-rail__lien[aria-current="page"] {
    color: var(--texte-vif);
    font-weight: 700;
}

/* L ICONE DE LA PLANCHE OUVERTE PREND L ACCENT, DANS LES DEUX ETATS.

   RELEVE PAR L ADMIN le 09/10/2026 : « icone colore aussi, deplie et
   replie ». Et avant cela : « meme rendu visuel que pour les rails
   admin/partner ».

   CE QUI MANQUAIT. Le rail de l'espace porte cette recette depuis toujours --
   `.prat-rail__lien[aria-current="page"] .prat-rail__icone`, deux cents
   lignes plus haut : opacite pleine et encre d'accent. Le rail du visualiseur
   n'avait que `color` et `font-weight` sur le LIEN, qui ne touchent pas une
   icone en `currentColor`... et qui, repliee, est la seule chose que le rail
   montre. Mesure : `rgb(255, 255, 255)` a 0,72 d'opacite, c'est-a-dire le
   meme gris que les dix-sept autres.

   ELLE EST ICI ET HORS DE TOUT SEUIL, parce que l'etat actif ne depend pas
   de la largeur. J'avais d'abord pose la couleur dans le bloc
   `html[data-rail="replie"]` de `seuils-retenus.css` : le replie etait juste,
   le DEPLOYE restait gris, et c'est ce que l'Admin a vu. Un etat qui vaut
   dans les deux formes du rail n'a rien a faire sous un seuil.

   LES MEMES JETONS QUE LE RAIL DE L ESPACE, pas les memes valeurs : une
   seule echelle d'accent pour les deux rails, donc rien a tenir d'accord. */
.viz-rail__lien[aria-current="page"] .viz-rail__icone {
    opacity: 1;
    color: rgb(var(--accent-texte-rgb));
}

/* -------------------------------------------------------------------------
   CE QUE LA PAGE REND AU RAIL
   -------------------------------------------------------------------------
   L'encadre du haut portait cinq choses de nature differente : qui l'on lit,
   quelle seance, la consultation en direct, les programmes, un mot au client.
   465 px avant le premier resultat, a chaque visite, pour des gestes qu'on ne
   fait qu'une fois.

   TOUT CELA EST DU CONTEXTE ET DE L ACTION, PAS DU RESULTAT -- et le rail est
   deja l'endroit du contexte, puisqu'il porte le nom du client et les neuf
   planches. Les y ranger ne les cache pas : ca les met la ou l'on va deja les
   chercher, et ca rend la page entiere a ce qu'on est venu lire.

   TROIS ZONES, ET L ORDRE EST CELUI DE LA QUESTION POSEE :

       qui, et quelle seance     le contexte -- ce qu'on lit
       les neuf planches         la navigation -- ou l'on est
       programmes, note          l'action -- ce qu'on fait

   LE PIED EST COLLE EN BAS. Les actions sont rares et doivent etre toujours au
   meme endroit ; posees a la suite des planches, elles se deplaceraient avec
   leur nombre.
   ------------------------------------------------------------------------- */

/* UN ELEMENT DEPLACE EMPORTE SA MISE EN PAGE. L'aide etait en position absolue,
   calee dans un coin de l'encadre ; arrivee dans le rail elle s'est calee dans
   le coin du rail, par dessus le lien de retour. On la remet dans le flux.

   ELLE EST MAINTENANT SUR LA LIGNE DU NOM et non du titre : elle parle de la
   personne qu'on lit, pas du nom de la page. */
.viz-rail__nomligne .help-trigger {
    position: static;
    margin-left: auto;
}

/* RIEN ICI POUR UN SECOND SIGNE, ET C EST VOULU. Ce coin ne porte QUE la note
   (le crayon ou la bulle, jamais les deux) depuis que le « ? » en est parti :
   une regle qui rangerait un deuxieme bouton ne pourrait jamais s'appliquer.
   Elle a existe une heure, et la retirer vaut mieux que la laisser dormir --
   la prochaine personne se demanderait quel cas elle couvre.

   Pour memoire, si un second signe revenait : `margin-left: auto` sur CHACUN
   ne les range pas a droite, chacun reclame sa part de l'espace libre et les
   deux glyphes s'ecartent de part et d'autre de la ligne. Seul le premier doit
   pousser. */

/* LA BULLE EST UN DESSIN, ET ELLE PREND LA TAILLE DU GLYPHE VOISIN.

   U+1F4AC est emoji par defaut et n'a pas de forme TEXTE : contrairement au
   crayon, aucun selecteur de variante ne la fait suivre `currentColor`. Peinte
   par la police d'emoji, elle garderait ses couleurs au survol et en theme
   clair. Dessinee, elle herite de la couleur du bouton comme le « ? ». */
.viz-rail__nomligne .viz-rail__bulle {
    display: block;
    width: 13px;
    height: 13px;
}

/* UN BLOC EST UN CREUX, PAS UNE CARTE. Le rail est deja une surface posee sur
   la page ; y empiler des cartes ferait une surface sur une surface sur une
   page. Un creux leger separe sans ajouter d'epaisseur. */
.viz-rail__bloc {
    display: flex;
    flex-direction: column;
    gap: var(--e-4);
    margin: var(--e-5) 0;
    padding: var(--e-5) var(--e-5) var(--e-6);
    /* LA CARTE CLIENT EST EN RELIEF DANS LES DEUX THEMES. `--surface-discrete`
       (un voile de l'encre) la rendait plus claire que le rail en sombre et
       plus foncee en clair : le contraste changeait de sens d'un theme a
       l'autre (mesure : 14/24/37 sur 5/7/12, puis 220/227/237 sur 240/243/248).
       `--surface-relief-rail` est plus claire que le rail dans les deux themes. */
    background: var(--surface-relief-rail);
    border-radius: var(--rayon-bloc);
}

.viz-rail__etiquette {
    color: var(--texte-second);
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: 0.1em;
    text-transform: uppercase;
}

/* CE QUI ARRIVE DE LA PAGE PREND LA LARGEUR DU RAIL. Ces elements ont ete
   dessines pour une rangee horizontale ou ils se rangeaient les uns a cote des
   autres ; empiles dans 204 px, ils doivent occuper toute la largeur, sans
   quoi chacun se retracte sur son texte et la colonne devient un escalier. */
.viz-rail__bloc select,
.viz-rail__bloc .diagnostic-viewer-tool-btn,
.viz-rail__bloc .diagnostic-viewer-topbar__commande,
.viz-rail__note {
    box-sizing: border-box;
    width: 100%;
}

.viz-rail__bloc select {
    padding: var(--e-4) var(--e-5);
    background-color: var(--surface-controle);
    border: var(--filet-epaisseur) solid var(--filet-neutre-controle);
    border-radius: var(--rayon-controle);
    color: var(--texte-fort);
    font: inherit;
    font-size: 13px;
}

/* LE COMPTEUR EST UNE PHRASE, PAS UN CONTROLE : il dit un etat, il ne se
   clique pas. Il prend donc le role du texte calme, et rien de plus. */
.viz-rail__bloc .viewer-compteur {
    padding: 0;
    background: none;
    border: 0;
    color: var(--texte-calme);
    font-size: 12.5px;
    line-height: 1.35;
    text-align: left;
}

/* « Ecarter ? » est une action rare et defaisable : elle se dit en petit, sous
   ce sur quoi elle porte. */
.viz-rail__bloc .test-judgement__trigger {
    margin-top: calc(var(--e-2) * -1);
    padding: 0;
    background: none;
    border: 0;
    color: var(--texte-second);
    font-size: 12px;
    text-align: left;
    text-decoration: underline;
    cursor: pointer;
}

.viz-rail__bloc .test-judgement__trigger:hover {
    color: var(--texte-vif);
}

/* C EST LA LISTE QUI DEFILE, PAS LE RAIL. Mesure : 955 px de contenu pour 939
   de hauteur -- seize pixels, et « Envoyer une note » passait sous le bord ; sur
   un ecran de 800, c'etait la moitie du pied. Un pied colle en bas qu'il faut
   faire defiler pour atteindre n'est plus un pied.

   La tete et le pied gardent leur taille, les planches prennent ce qui reste et
   glissent entre les deux. `min-height: 0` sans quoi un element flexible refuse
   de descendre sous la taille de son contenu et ne defile jamais. */
.viz-rail {
    overflow: hidden;
}

/* LE MAILLON DU MILIEU, SANS QUOI RIEN DE TOUT CECI NE SERT.
 *
 * La liste des planches n'est pas l'enfant du rail : entre les deux il y a
 * `<nav class="diagnostic-viewer-nav">`. C'est LUI l'element flexible du rail,
 * et il valait `flex: 0 1 auto` avec `min-height: auto` -- autrement dit il
 * refusait de descendre sous la hauteur de son contenu. La liste avait beau
 * savoir defiler, on ne lui demandait jamais de retrecir : le rail debordait,
 * son `overflow: hidden` coupait, et le PIED devenait inatteignable.
 *
 * CE QUE CA DONNAIT, mesure du 12/09/2026 sur « Bilan emotionnel » : a 830 px
 * de fenetre tout tenait, a 760 le rail debordait de 53 px et les deux
 * dernieres planches plus le pied disparaissaient sans le moindre ascenseur.
 * L'Admin l'a vu sur son ecran a 100 % et pas a 80 % : a l'echelle Windows de
 * 125 %, une fenetre de 1080 pixels n'offre que ~700 px de CSS.
 *
 * UNE CHAINE DE DEFILEMENT SE VERIFIE SUR TOUS SES MAILLONS. `min-height: 0`
 * sur le dernier ne vaut rien si l'avant-dernier garde `auto` : le defaut ne
 * se voit qu'a partir d'une certaine hauteur de fenetre, donc jamais sur un
 * grand ecran de developpement. */
.viz-rail > .diagnostic-viewer-nav {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
}

.viz-rail__planches {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;

    /* LA BARRE NE PREND PLUS RIEN, DANS AUCUN DES DEUX ETATS (09/10/2026).

       CE QU IL Y AVAIT, ET POURQUOI CA NE SUFFISAIT PAS. La regle reservait la
       gouttiere (`scrollbar-gutter: stable`) pour que les icones repliees ne
       soient pas poussees quand la liste deborde. Le defaut d origine etait
       reel, mais le remede deplacait le probleme : `stable` prend la place EN
       PERMANENCE. Mesure au deploye, dix-huit planches : 9 px manges sur une
       colonne de 33, soit le quart. La barre ne poussait plus par a-coups,
       elle poussait tout le temps.

       Releve par l'Admin : « je ne veux pas que la barre de scroll pousse le
       design et casse le carre des icones ».

       ON RETIRE LE DESSIN, PAS LE DEFILEMENT. `overflow-y: auto` reste, donc
       la liste se parcourt toujours ; c'est la barre VISIBLE qui s'efface, et
       avec elle la place qu'elle prenait. Le rail de l'espace ne montre pas
       la sienne non plus : deux rails, un seul langage.

       ET C ETAIT DEJA LA RECETTE DU REPLI, ecrite dans `seuils-retenus.css`
       sous `html[data-rail="replie"]`. Elle remonte ici, hors de tout seuil :
       ce qui est vrai des icones l'est des libelles, et une recette ecrite a
       deux endroits finit par diverger. */
    scrollbar-width: none;
}

.viz-rail__planches::-webkit-scrollbar {
    width: 0;
    height: 0;
}

.viz-rail__pied {
    padding-top: var(--e-6);
    border-top: var(--filet-epaisseur) solid var(--filet-neutre-bloc);
}

/* LA MENTION SE POSE SOUS LE VERBE, plus petite et sans graisse : elle
   accompagne l'action, elle ne la nomme pas. Sur la meme ligne elle ferait un
   libelle en deux morceaux. */
.viz-rail__mention {
    display: block;
    margin-top: 1px;
    font-size: 11px;
    font-weight: 400;
    opacity: 0.78;
}

/* `hidden` NE VAUT QUE PAR LA REGLE `[hidden] { display: none }` du navigateur,
   et celle-la perd contre n'importe quel `display` pose par une de nos classes.
   Le `display: block` ci-dessus la battait : le bouton de commande affichait
   « 0 en cours » sous son verbe, c'est-a-dire exactement la phrase que la
   maquette avait retiree parce qu'elle repete ce que le bouton propose deja.
   Meme piege que les confirmations du produit (`lk.css`, « `hidden` NE VAUT
   QUE PAR LA REGLE »). */
.viz-rail__mention[hidden] {
    display: none;
}

/* IL PREND LA TAILLE D UN ONGLET. Les boutons faisaient quarante-quatre pixels
   quand un onglet en fait trente-huit, avec une graisse de plus et un rayon a
   eux. Dans une colonne ou tout est empile, deux tailles de bloc se lisent
   comme deux echelles : on croit que les boutons pesent plus que la navigation,
   alors qu'ils sont juste plus gros. Ce qui les distingue est leur FOND. */
.viz-rail__note {
    padding: 9px 10px;
    /* TROIS APLATS, ET C EST LE GRIS QUI LA RANGE TROISIEME (choix de l'Admin
       sur la planche des etats, 08/09). Elle avait le filet seul, pour ne pas
       faire un troisieme aplat gris sous l'or ; le bloc se lit mieux en trois
       aplats dont la TEINTE donne le rang -- or plein, or pale, gris -- qu'en
       deux aplats suivis d'un contour. Elle est au cran de l'etat pose
       (`--voile-6`), celui de la colonne retenue. */
    background: rgba(var(--white-rgb), var(--dose-note-repos));
    border: var(--filet-epaisseur) solid transparent;
    border-radius: var(--rayon-controle);
    color: var(--texte-calme);
    font: inherit;
    font-size: 14px;
    font-weight: 600;
    line-height: 1.3;
    cursor: pointer;
}

/* SES DEUX ETATS NE BOUGENT QUE D UN CRAN (« une legere variation », Admin).
   Un aplat FONCE quand on le touche, dans les deux themes : c'est la loi des
   deux autres boutons du bloc (l'or passe de 82 a 74 puis 66 %, le voile d'or
   de 0,28 a 0,36 puis 0,44). Le gris suit la meme echelle de voiles. */
.viz-rail__note:hover {
    background: rgba(var(--white-rgb), var(--dose-note-survol));
    border-color: transparent;
    color: var(--texte-vif);
}

.viz-rail__note:active {
    background: rgba(var(--white-rgb), var(--dose-note-appui));
    border-color: transparent;
    color: var(--texte-vif);
}

/* LA TETE DE L ENCADRE S EN VA, LA RANGEE D ONGLETS RESTE DANS LE BALISAGE.
   `display: contents` sur l'encadre : il cesse d'etre une boite -- plus de
   fond, plus de marge, plus de carte vide -- sans que ses enfants disparaissent
   avec lui. Le retirer du DOM emporterait les onglets, dont on a encore besoin
   sous 760. */
body:has(.viz-rail) .diagnostic-viewer-topbar {
    display: contents;
}

body:has(.viz-rail) .diagnostic-viewer-topbar__head {
    display: none;
}

/* LA TETE RESTE VISIBLE LA OU IL N Y A PAS DE RAIL, et c'est la seule page qui
   la montre : `client-vue-expert`. Le mot d'attente du praticien y est ecrit
   `rgba(var(--white-rgb), 0.44)` par `diagnostic-viewer.css` -- encore une
   opacite posee a la main, encore sous le seuil dans les DEUX themes : 2,70 en
   clair, 4,36 en sombre.

   C'est un texte en attente, pas un texte eteint : il dit au client que son
   praticien pourra ecrire ici. L'italique et le pointille du cadre disent deja
   qu'il n'y a pas encore de mot ; l'encre n'a pas a le redire en s'effacant. */
.viewer-note-praticien__texte--vide {
    color: var(--texte-second);
}


/* -------------------------------------------------------------------------
   LA PLANCHE PORTE SON INTERRUPTEUR
   -------------------------------------------------------------------------
   LES NEUF INTERRUPTEURS NE SONT PAS UN BLOC A COTE DES ONGLETS : ILS SONT LES
   ONGLETS. Le produit pose l'interrupteur DANS l'onglet, si bien que la rangee
   du haut faisait deux metiers avec un seul jeu de libelles -- naviguer, et
   decider ce que le client garde. En hauteur, chaque planche porte le sien sur
   sa propre ligne, et les 141 px de libelles repetes s'en vont avec la rangee :
   ils en etaient la copie.

   ILS NE SE MONTRENT PAS TOUT LE TEMPS. Huit glissieres allumees en permanence
   feraient d'une navigation un tableau de bord ; on ne lit plus « ou suis-je »
   mais « qu'est-ce qui est allume ». Ils apparaissent quand on ouvre le
   depliant du partage, et le rail passe alors de la navigation au partage.
   C'est un mode, et c'est le depliant qui le dit.
   ------------------------------------------------------------------------- */

/* LA RANGEE EST L ONGLET. La peau vivait sur le LIEN et la case restait
   dehors, sur le fond du rail, collee au bord : une case posee hors de l'onglet
   qu'elle commande se lit comme un controle a part.

   ELLE PARTAGE LA REGLE DU LIEN SIMPLE, plus haut : meme remplissage, meme
   rayon, memes roles de couleur. Une seule declaration peint les deux, donc
   changer l'onglet les change ensemble -- c'est ce qu'on demande a une base.

   SEULE LA COULEUR INTERNE CHANGE avec l'etat. Forme, taille et remplissage ne
   bougent pas d'un etat a l'autre : c'est ce qui permet de lire la teinte comme
   une information et non comme un autre composant. */
.viz-rail__planche {
    display: flex;
    align-items: center;

    /* L ECART ET LE REMPLISSAGE DE DROITE SONT COMPTES, pas choisis : chaque
       pixel repris ici est un pixel rendu au libelle, et c'est le libelle qui
       decide si la ligne se replie. Huit du bord droit, six autour de la case. */
    gap: var(--e-3);

    padding: 9px 8px 9px 10px;
    border: var(--filet-epaisseur) solid transparent;
    border-radius: var(--rayon-controle);
    color: var(--texte-calme);
    font-size: 14px;

    /* UNE HAUTEUR DE LIGNE EN NOMBRE ENTIER. A 1,3 sur 14 px elle vaut 18,2, et
       la rangee 38,2 : chaque ligne decale la suivante d'un cinquieme de pixel,
       si bien que les neuf pastilles ne tombent pas au meme endroit de la
       grille et ne sont pas adoucies pareil. Des que l'affichage est mis a
       l'echelle, l'ecart s'amplifie et certaines paraissent aplaties.

       A 18 px la rangee fait 38 pile. C'est la raison qui fait ecrire la
       hauteur de barre et celle du rail en entiers depuis le debut : ce qui se
       repete doit tomber juste, sinon l'erreur s'accumule. */
    line-height: 18px;
    transition: background var(--duree-effleurement, .16s) ease,
                color var(--duree-effleurement, .16s) ease;
}

.viz-rail__planche:hover {
    background: var(--surface-effleuree);
    color: var(--texte-vif);
}

/* Le lien redevient du texte : la boite est la rangee. */
/* LE LIEN N EST PLUS QU UN TEXTE. La rangee centre ses enfants, mais un lien
   qui garde son propre remplissage vertical se centre en tant que BOITE, et sa
   ligne de texte se retrouve decalee dans une rangee plus haute que lui. Sans
   remplissage ni hauteur propre, il n'y a plus qu'une ligne a centrer. */
.viz-rail__planche .viz-rail__lien {
    flex: 1 1 auto;
    min-width: 0;
    height: auto;
    min-height: 0;
    padding: 0;
    border: 0;
    border-radius: 0;
    color: inherit;
    font-size: inherit;
    line-height: inherit;
}

.viz-rail__planche .viz-rail__lien:hover {
    background: none;
}

/* LE CADENAS PREND LA PLACE DU TEMOIN DE PARTAGE, IL NE S AJOUTE PAS A LUI.

   L'ADMIN, LE 03/11/2026 : « c'est a la place, par dessus : un onglet ne peut
   pas etre a la fois verrouille et partageable. »

   LA RANGEE A UNE GRAMMAIRE : a gauche le dessin qui NOMME la planche, au
   milieu son nom, a droite la marque de son ETAT -- le point de partage, ou la
   case a cocher quand on decide. Un cadenas pose entre le dessin et le nom
   mettait deux pictos dans la zone qui en attend un seul ; un cadenas pose au
   bord droit du LIEN s'arretait 27 px avant le bord de la rangee, parce que la
   colonne du temoin est derriere lui. L'Admin a vu les deux : « il y a de
   l'espace vide et le texte est coupe ».

   LE GABARIT L A DONC SORTI DU LIEN -- dernier enfant de la rangee, apres la
   case -- et la regle d'en face eteint le point sur une planche fermee. La
   marge de 2 px est celle du point, pour que les deux tombent dans la meme
   colonne : une planche fermee et une planche partagee ont leur marque au meme
   endroit, et on les compare d'un coup d'oeil sur la verticale.

   CE QUE CA REND AU NOM. Dans le lien, le cadenas coutait 24 px au libelle EN
   PLUS des 18 px que le temoin coute a toute la rangee : trois planches sur
   onze passaient a deux lignes (mesure du 03/11/2026, rail a 241 px). En
   prenant la place du temoin il ne coute plus que la difference, 3 px, et les
   onze noms tiennent a nouveau sur une ligne. */
.viz-rail__planche .diagnostic-viewer-nav__cadenas {
    margin-right: 2px;
}

/* UNE PLANCHE FERMEE N A PAS DE TEMOIN DE PARTAGE : le cadenas est a sa place.

   `is-ferme` couvre les deux cadenas -- l'ecran en maquette, qui n'a rien
   derriere lui, et la planche hors acces, qui ne s'ouvre plus. Aucun des deux
   ne se partage, donc aucun des deux n'a d'etat de partage a montrer.

   LE CAS QUI RESTE, ET OU IL EST : une planche partagee AVANT de passer hors
   acces porte encore un partage, qu'on ne peut plus que reprendre. Il ne se lit
   plus sur le point mais sur la case, en mode partage -- ce qui suit la regle
   deja ecrite plus bas, « en mode partage la case dit tout ; hors de ce mode,
   le point ». Ici, hors de ce mode, le cadenas. */
.viz-rail__planche.is-ferme::after {
    display: none;
}

/* ET LE NOM NE DEBORDE PAS, QUOI QU ON AJOUTE A LA RANGEE.

   LA RANGEE DE 38 PX EST UNE DECISION ECRITE plus haut : la hauteur de ligne y
   est en nombre entier pour que les pastilles tombent toutes au meme endroit de
   la grille. Une rangee qui grandit n'est pas une adaptation, c'est un defaut
   -- la regle de la case a cocher existe deja pour empecher « une rangee de 56
   px au lieu de 38 ».

   PLUS AUCUN NOM NE SE COUPE AUJOURD HUI, depuis que le cadenas a pris la place
   du temoin : le plus long, « Dynamique emotionnelle », demande 156 px pour 174
   disponibles. Cette regle est donc une GARDE, pas un remede : elle decide a
   l'avance de ce qui arrivera au nom plus long de demain -- il se coupe, et la
   colonne garde son rythme.

   `min-width: 0` EST CE QUI REND LA COUPE POSSIBLE : un element flex refuse de
   se contracter sous son contenu tant qu'il garde `min-width: auto`, et sans
   lui le texte deborde au lieu de se couper. Le nom reste dit en entier dans
   l'infobulle du lien, dans la mention `visually-hidden` et dans le titre de la
   planche quand on l'ouvre. */
.viz-rail__planche .viz-rail__libelle {
    min-width: 0;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}

/* LA CASE NE TOUCHE PAS LE BORD. Elle est DANS l'onglet, sur son fond, avec
   l'ecart qui la separe du libelle a gauche et du bord a droite. */
.viz-rail__planche .partage-interrupteur {
    margin-right: 0;
}

/* LA CASE, PARTOUT OU L ON DECIDE DU PARTAGE — ET DANS LA BASE, PAS DANS L AXE.

   Sans reglage, c'est-a-dire DANS LE PRODUIT, le rail rendait la glissiere du
   produit ET le mot « Partager » a cote de chaque libelle : une rangee de 56 px
   au lieu de 38, parce que le libelle plus le mot ne tiennent pas sur une ligne.
   Ce reglage vivait dans la feuille d'essai, ou l'axe est toujours pose sur les
   pages du banc -- le defaut ne pouvait donc pas s'y voir. C'est la troisieme
   fois : UN AXE PROPOSE DES VARIANTES D UN DESSIN QUI EXISTE.

   UNE CASE PLUTOT QU UNE GLISSIERE, ET C EST MESURE : le plus long libelle fait
   181 px pour 204 de rangee. La glissiere en prend 34 plus 6 d'ecart, il reste
   144 px de texte, donc deux lignes ; la case en prend 16, il en reste 172. Et
   elle ne ment pas sur le geste : c'est la TEINTE DE LA RANGEE qui porte
   l'etat, et elle change a l'instant, sur toute la ligne.

   LE MOT « PARTAGER » NE SE REPETE PAS NEUF FOIS. Dans la rangee d'onglets du
   produit il disait ce que la glissiere commandait ; sur une ligne qui porte
   deja le nom de la planche, il ne dirait plus que le nom de la commande.
   L'intitule du depliant le dit une fois pour toutes. */
/* LA GLISSIERE PART PARTOUT, LE MOT SEULEMENT DANS LE RAIL.

   Sur un bloc de resultats, la case est seule a cote d'un titre : sans le mot,
   elle ne dit pas ce qu'elle commande. Dans le rail, l'intitule du depliant le
   dit une fois pour les neuf, et le repeter ferait neuf fois le meme mot dans
   une colonne de 204 px. Meme controle, meme forme ; ce qui change est ce que
   le CONTEXTE dit deja. */
.partage-interrupteur__glissiere,
.viz-rail .partage-interrupteur__texte {
    display: none;
}

/* SUR LA PAGE, LA CASE PUIS SON MOT — ET RIEN AUTOUR.

   La pastille verte que portait ce controle avait ete dessinee pour ENTOURER
   une glissiere : fond vert a 16 %, filet vert, rayon de gelule. Sans la
   glissiere elle serre un mot et une case nue, et se lit comme un jeton
   cliquable pose a cote de la case qu'on doit cliquer -- deux cibles pour un
   geste.

   LA CASE PASSE DEVANT LE MOT : on lit la marque, puis ce qu'elle commande.
   C'est l'ordre d'une case a cocher partout ailleurs, et celui du balisage. */
.partage-interrupteur {
    display: inline-flex;

    /* LA RANGEE N EST PLUS INVERSEE. Le produit pose `row-reverse`, ce qui
       etait juste avec une glissiere -- on la veut apres le mot. Avec une case,
       la marque se lit avant ce qu'elle commande. Et tant que la rangee restait
       retournee, `order: 1` sur la case l'envoyait A DROITE : j'ai verifie
       l'ordre calcule au lieu de la position peinte, et les deux disaient des
       choses differentes. */
    align-items: center;
    gap: var(--e-3);
    padding: 0;
    background: none;
    border: 0;
    color: var(--texte-calme);
}

/* LA RANGEE N EST PLUS INVERSEE, ET LA REGLE SE REPREND LA OU ELLE EST ECRITE.
   Le produit pose `row-reverse` sur le controle des en-tetes de bloc, avec sa
   raison : « l'intitule passe a gauche ». Elle est juste avec une GLISSIERE --
   on veut le mot d'abord et la glissiere apres. Avec une case, la marque se lit
   avant ce qu'elle commande.

   ET TANT QUE LA RANGEE RESTAIT RETOURNEE, `order: 1` sur la case l'envoyait A
   DROITE : j'ai verifie l'ordre calcule -- qui etait bien 1 -- au lieu de la
   position peinte. Les deux disaient des choses differentes, et c'est la
   position qui ne ment pas. */
.diagnostic-section-head__actions .partage-interrupteur {
    flex-direction: row;
}

/* DEUX CLASSES, PARCE QUE LE PRODUIT EN A DEUX. Sa regle pose l'ordre et la
   couleur du mot ; a poids egal c'est l'ordre des feuilles qui tranche, et il
   ne suffisait pas ici. */
.partage-interrupteur .partage-interrupteur__texte {
    order: 2;
    color: var(--texte-calme);
    font-size: 13px;
}

.partage-interrupteur input[type="checkbox"] {
    order: 1;
}

/* LE MODIFICATEUR AUSSI. « Partager tout le test » porte
   `partage-interrupteur--onglet`, que les cases des planches n'ont pas : il pose
   sa propre hauteur, et une regle sur la classe de base ne la voit pas. La
   rangee faisait 56 px au lieu de 38 pour une case de 16.

   C'est le troisieme `height` ecrit par le produit que je trouve dans ce rail,
   toujours pour la meme raison : une cible tactile reglee pour une rangee
   horizontale, alors qu'ici la cible est la LIGNE entiere. */
/* TROIS CLASSES, PARCE QUE LA REGLE DU PRODUIT PORTE `body` DEVANT ELLE :
   `body .diagnostic-viewer-nav__item .partage-interrupteur--onglet` pese un
   element et deux classes. Ce `body` n'est pas decoratif, c'est un poids, mis
   la pour gagner contre une autre regle -- et il gagnait aussi contre la
   mienne. La case faisait 38 px de haut pour une case de 16, et la rangee 56
   au lieu de 38.

   C'est le troisieme `height` du produit que je trouve dans ce rail, toujours
   pour la meme raison : une cible tactile reglee pour une rangee horizontale,
   alors qu'ici la cible est la LIGNE entiere. */
/* TROIS CLASSES POUR LE MODIFICATEUR, parce que la regle du produit porte
   `body` devant elle : `body .diagnostic-viewer-nav__item
   .partage-interrupteur--onglet` pese un element et deux classes. Ce `body`
   n'est pas decoratif, c'est un poids, mis la pour gagner contre une autre
   regle -- et il gagnait aussi contre la mienne.

   C'est le troisieme `height` du produit trouve dans ce rail, toujours pour la
   meme raison : une cible tactile reglee pour une rangee horizontale, alors
   qu'ici la cible est la LIGNE entiere. */
.partage-interrupteur,
.viz-rail .viz-rail__planche .partage-interrupteur,
.viz-rail .viz-rail__depli__dedans .partage-interrupteur--onglet {
    height: auto;
    min-height: 0;
    margin: 0;
    padding: 0;
}

/* LA CASE EST VISIBLE, PARTOUT. Le produit la cache -- un pixel, opacite zero,
   en position absolue -- parce que la glissiere la remplacait ; sans glissiere
   il faut bien qu'on la voie. `accent-color` lui donne le vert du partage, donc
   la case cochee dit la meme chose que la pastille de sa ligne. */
.partage-interrupteur input {
    position: static;
    width: 16px;
    height: 16px;
    margin: 0;
    opacity: 1;
    accent-color: rgb(var(--share-line-rgb));
    cursor: pointer;
}


/* CELLE QUI N EST PAS ENCORE PROPOSEE se montre eteinte plutot que d'etre
   cachee : ce qui viendra se dit, et une case absente aurait laisse croire que
   cette planche n'est pas partageable par nature. */
.viz-rail__a-venir {
    cursor: default;
}

.viz-rail__a-venir input {
    opacity: 0.45;
}

/* Cachees, elles ne prennent aucune place : la navigation reste une
   navigation tant qu'on ne lui demande pas autre chose. */
.viz-rail__planche .partage-interrupteur {
    display: none;
    flex: none;
}

.viz-rail:has(.viz-rail__depli--partage[open]) .viz-rail__planche .partage-interrupteur {
    display: inline-flex;
    align-items: center;
}

/* -------------------------------------------------------------------------
   CE QUE LE CLIENT RECOIT SE LIT SANS RIEN OUVRIR
   -------------------------------------------------------------------------
   LA TEINTE REPOND A LA QUESTION, LA CASE SERT A CHANGER LA REPONSE.
   « Qu'est-ce que le client voit ? » doit se lire d'un coup d'oeil sur la
   liste ; « changer ce qu'il voit » est un geste, et un geste se demande. La
   couleur est donc toujours la, les cases seulement quand on ouvre le
   depliant du partage.

   DEUX CRANS, ET RIEN D AUTRE :

       rien de partage      le fond d'un onglet ordinaire
       une partie           legerement teinte
       tout, ou coche       teinte complet

   J'avais ajoute des liseres, des barres internes et un fond de liste : trois
   signaux de plus pour une information qui n'en demande qu'un. Deux crans se
   comparent d'un coup d'oeil sur neuf lignes alignees ; une barre de trois
   pixels demande de la chercher.

   LE VERT S AFFIRME, IL NE CHANGE PAS DE TEINTE, ce qui permet de lire
   l'intensite comme une quantite. L'or dit autre chose -- un bloc ACHETE ne se
   reprend pas -- donc lui change de teinte et garde les deux crans.

   C EST DE LA DONNEE, PAS DE L INTERFACE : ces teintes ne suivent pas l'accent
   du produit, elles disent un etat du dossier.
   ------------------------------------------------------------------------- */

/* UN POINT, PAS UN APLAT. Neuf rangees teintees font un tableau ; neuf points
   font une liste avec une colonne d'etat, et la navigation reste une
   navigation. La rangee garde le fond d'un onglet ordinaire.

   DEUX ETATS, PAS DEUX OPACITES : creux pour une partie, plein pour tout. A
   sept pixels, deux opacites du meme vert ne se distinguent pas -- c'est la
   FORME qui porte ce que la transparence ne peut pas a cette taille. */
/* DEUX CANAUX SUR UNE MARQUE DE DIX PIXELS :

       le REMPLISSAGE dit ce que le client garde   rien / une part / tout
       l ANNEAU dit ce qu'il a achete              or, ou rien

   Ils sont independants, et c'est tout l'interet. Avant, l'achat ECRASAIT le
   partage : une planche achetee ET partagee ne montrait que l'or, et l'on
   perdait l'information qui compte le plus. Les six combinaisons se lisent
   maintenant sur le meme point.

   `box-sizing: border-box` ET `background-clip: content-box`, ET C EST CE QUI
   FAIT MARCHER LES DEUX CANAUX. Le filet est pris DANS la boite, le
   remplissage ne peint que le dedans : l'anneau ne pousse pas le disque, le
   disque ne mange pas l'anneau, et la marque fait dix pixels quel que soit son
   etat -- donc la colonne reste droite.

   ELLE EN FAISAIT ONZE POUR L ANNEAU ET SEPT POUR LE PLEIN. En `content-box`,
   les deux pixels du filet s'ajoutaient a la largeur : les deux marques
   n'avaient jamais eu la meme taille, et mon calcul d'encre comparait deux
   cercles differents en les croyant egaux. Le socle pose pourtant cette regle
   partout dans la coquille -- « sans quoi il deborde » -- mais un
   pseudo-element n'herite pas de ce qu'on n'a pas mis sur lui.

   LE PARTIEL EST UN PLEIN PLUS PETIT, pas un anneau : l'anneau est pris, il
   dit l'achat. La quantite passe par la TAILLE du disque, et une taille se
   compare sans avoir a etre identifiee.

   QUATRE PIXELS CONTRE SIX, MESURES SUR LES PIXELS PEINTS -- pas quatre contre
   huit comme je l'avais ecrit : `background-clip: content-box` fait que le
   remplissage n'occupe que la zone de contenu, soit dix moins deux fois deux.
   Le rapport d'encre reste net, 12,6 contre 28,3 px², un peu plus du double. */
.viz-rail__planche::after {
    content: "";
    box-sizing: border-box;
    flex: none;
    width: 10px;
    height: 10px;
    margin-right: 2px;
    border: 2px solid transparent;
    border-radius: var(--rayon-gelule);
    background-clip: content-box;
}

/* UNE PART : un disque de quatre pixels au centre. Le degrade radial le dessine
   sans ajouter d'element -- un pseudo-element n'a pas d'enfant. */
.viz-rail__planche.is-partage-partiel::after {
    background-image: radial-gradient(circle,
        rgb(var(--share-line-rgb)) 0 2px, transparent 2px);
}

.viz-rail__planche.is-partage-total::after {
    background-color: rgb(var(--share-line-rgb));
}

/* L ACHAT PREND L ANNEAU, et il ne touche plus au remplissage : c'est ce qui
   permet de dire « achete ET partage » d'un seul point. */
.viz-rail__planche.is-achat::after {
    border-color: rgb(var(--achat-line-rgb));
}

/* LE POINT S EFFACE QUAND LES CASES ARRIVENT. Deux marques pour un seul etat,
   du meme cote de la ligne, feraient hesiter sur laquelle se clique. En mode
   partage la case dit tout ; hors de ce mode, le point. */
.viz-rail:has(.viz-rail__depli--partage[open]) .viz-rail__planche::after {
    display: none;
}

/* UN ECRAN QUI S ANNONCE SANS S OUVRIR. On le montre eteint plutot que de le
   cacher : ce qui arrive se dit, il n'apparaitra pas un jour sans prevenir. */
.viz-rail__planche.is-ferme {
    opacity: 0.5;
}

.viz-rail__planche.is-ferme .viz-rail__lien {
    cursor: default;
}


/* -------------------------------------------------------------------------
   LE CREUX S AJOUTE A LA TEINTE, IL NE LA REMPLACE PAS
   -------------------------------------------------------------------------
   Ou l'on est et ce que le client recoit sont DEUX questions : un fond ne peut
   pas repondre aux deux. Le produit le dit dans sa propre feuille -- « il garde
   la couleur de son etat, c'est son trait qui s'epaissit » -- et je l'ai perdu
   deux fois : d'abord en posant un fond de creux qui recouvrait la teinte,
   ensuite en le retirant, ce qui faisait disparaitre le creux.

   LA PLANCHE OUVERTE GARDE SON FOND ET PREND LE RELIEF : l'ombre interne du
   creux, qui se voit sur n'importe quelle teinte puisqu'elle l'assombrit sans
   la remplacer, et la graisse du libelle. Deux signaux pour deux questions.
   ------------------------------------------------------------------------- */

/* UN CREUX, C EST UN ASSOMBRISSEMENT PLUS UNE OMBRE HAUTE. PAS UN TRAIT.

   J'ai fait le tour complet de l'erreur : un FOND de creux, qui recouvrait la
   teinte de l'etat ; puis rien, et l'onglet ouvert devenait invisible ; puis un
   FILET colore -- « c'est son trait qui s'epaissit » -- qui donne exactement ce
   qu'on ne voulait pas, une boite cerclee.

   LA CITATION VISAIT UN AUTRE OBJET. Dans le produit c'est un onglet de rangee
   horizontale, pose sur une carte, ou le trait est le seul signal disponible.
   Ici la rangee est dans un rail, et le rail sait faire un creux : il en fait
   un pour les liens de l'espace.

   DEUX OMBRES INTERNES, ET RIEN D AUTRE :

     la premiere couvre TOUTE la boite -- rayon nul, etalement enorme -- donc
     elle assombrit le fond sans le repeindre, quelle que soit sa teinte ;

     la seconde est le relief : l'ombre haute et le trait de lumiere bas, qui
     donnent l'epaisseur.

   La teinte de l'etat reste dessous et reste lisible ; l'onglet ouvert est
   simplement plus bas que les autres. Une declaration repond aux deux questions
   sans que l'une efface l'autre. */
.viz-rail__planche:has(.viz-rail__lien[aria-current="page"]) {
    box-shadow: inset 0 0 0 100px var(--creux-fond), var(--creux-relief);
}

.viz-rail__planche:has(.viz-rail__lien[aria-current="page"]) .viz-rail__lien {
    color: var(--texte-vif);
    font-weight: 700;
}


/* LE MOT « PARTAGER » NE SE REPETE PAS HUIT FOIS. Dans la rangee du haut il
   disait ce que la glissiere commandait ; sur une ligne qui porte deja le nom
   de la planche, il ne dirait plus que le nom de la commande. L'intitule du
   depliant le dit une fois pour les huit. */
.viz-rail__planche .partage-interrupteur__texte,
.viz-rail__planche .partage-interrupteur__glissiere {
    display: none;
}

/* UNE CASE, PAS UNE GLISSIERE, ET C EST MESURE. Le plus long libelle fait
   181 px pour 204 de rangee. La glissiere en prend 34 plus 6 d'ecart : il reste
   144 px de texte, donc DEUX lignes. La case en prend 16 : il en reste 172, et
   la ligne tient. Neuf planches a deux lignes font 504 px au lieu de 342, et le
   depliant du partage ouvert n'en montrait plus que cinq -- on reglait ce qu'on
   ne voyait pas.

   ELLE NE MENT PAS SUR LE GESTE. Une case laisse croire qu'un bouton viendra
   valider, alors que le partage s'applique tout de suite ; mais c'est la TEINTE
   DE LA RANGEE qui porte l'etat, et elle change a l'instant, sur toute la
   ligne. La case n'est plus que la cible. */
.viz-rail__planche .partage-interrupteur input {
    position: static;
    width: 16px;
    height: 16px;
    margin: 0;
    opacity: 1;
    accent-color: rgb(var(--share-line-rgb));
    cursor: pointer;
}

/* LE CONTROLE DU PRODUIT EST HAUT DE 48 PX : il vise le doigt dans une rangee
   d'onglets. Dans une liste ou la CIBLE est la ligne entiere, cette hauteur ne
   sert plus qu'a faire grandir chaque rangee de dix pixels, neuf fois. */
.viz-rail__planche .partage-interrupteur {
    height: auto;
    min-height: 0;
    margin: 0;
    padding: 0 var(--e-3) 0 0;
}

/* Le libelle rend ses vingt pixels de remplissage : c'est ce qui fait tenir la
   ligne. Le creux de la planche ouverte est porte par la rangee, pas par lui. */
.viz-rail:has(.viz-rail__depli--partage[open]) .viz-rail__planche .viz-rail__lien {
    padding-right: var(--e-2);
}

/* PAS DE FOND DERRIERE LES ONGLETS. J'en avais pose un pour signaler le mode
   partage ; le rail est deja une surface, et une seconde dessous fait une
   surface sur une surface sur une page. Le mode se signale bien assez par les
   cases qui apparaissent -- neuf marques valent mieux qu'un aplat. */


/* -------------------------------------------------------------------------
   LES DEUX DEPLIANTS
   -------------------------------------------------------------------------
   CE QUI RESTE A PLAT EST CE DONT ON SE SERT A CHAQUE VISITE : l'identite, la
   seance, la consultation, les planches. Ce qui se replie est ce qu'on fait a
   la fin d'une seance -- decider ce que le client garde, lui commander des
   programmes, lui ecrire.

   `<details>` PLUTOT QU UN BOUTON ET UNE CLASSE : l'etat ouvert/ferme est deja
   dans l'element, il se dit au clavier et a qui ne voit pas l'ecran, et il se
   lit en CSS -- c'est lui qui fait apparaitre les interrupteurs sur les
   planches, sans une ligne de script.
   ------------------------------------------------------------------------- */

.viz-rail__depli {
    border-top: var(--filet-epaisseur) solid var(--filet-neutre-bloc);
}

.viz-rail__depli:first-child {
    border-top: 0;
}

/* CELUI DU PARTAGE EST AU-DESSUS DE LA LISTE, donc il en est le titre autant
   que sa commande : un filet en bas plutot qu'en haut, et il ne pousse pas la
   liste d'un ecart de plus. */
.viz-rail__depli--partage {
    border-top: 0;
    border-bottom: var(--filet-epaisseur) solid var(--filet-neutre-bloc);
}

/* ONZE PIXELS DE CAPITALES A 56 % SONT A DEVINER, et c'est exactement le
   defaut deja corrige sur les surtitres du rail de l'espace, revenu par une
   autre porte. Ces titres-la se CLIQUENT en plus de se lire : ils montent au
   cran `calme`, celui du texte qu'on suit. */
/* LA MEME BOITE QUE LES ONGLETS. Les deux resumes vivent dans la colonne
   des planches : meme retrait a gauche, meme hauteur, meme bord droit. Ils
   mesuraient 36,5 et 39,5 pour des onglets de 38, et leur texte partait a 22
   quand celui des onglets part a 25 -- un decalage de trois pixels sur toute
   la hauteur du rail.

   Le filet transparent n'est pas une decoration : c'est le pixel que l'onglet
   reserve a sa bordure, et sans lui le texte ne tomberait pas au meme endroit. */
.viz-rail__depli__titre {
    display: flex;
    align-items: center;
    gap: var(--e-3);
    box-sizing: border-box;
    min-height: 38px;
    padding: 0 var(--e-4) 0 var(--e-5);
    border: var(--filet-epaisseur) solid transparent;

    color: var(--texte-calme);
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    cursor: pointer;
    list-style: none;
}

.viz-rail__depli__titre::-webkit-details-marker {
    display: none;
}

/* LE CHEVRON EST DESSINE, PAS ECRIT. Le triangle par defaut du navigateur ne
   suit ni la couleur ni la taille du texte : c'est le seul signe de la
   coquille qui refuserait le theme. */
/* ELLE SE CENTRE, ET LA CALE DE SIX PIXELS TOMBE. Cette cale descendait la
   fleche a hauteur de la premiere ligne pendant que le titre etait en
   `flex-start` ; en `center`, un titre d'une ou de deux lignes se centre tout
   seul et la cale ne ferait plus que le decaler.

   CINQ PIXELS DE MARGE A DROITE, ET CE N EST PAS LA MARGE DE LA PASTILLE. Le
   chevron est un carre de six pixels tourne de quarante-cinq degres : son encre
   mesure six fois racine de deux, soit 8,49, et deborde donc de sa boite. Lui
   donner les deux pixels de la pastille alignait les BOITES et laissait l'encre
   trois pixels plus a droite. Mesure sur les pixels peints : les deux bords
   droits tombent maintenant au meme endroit a un quart de pixel pres. */
/* ELLE EST A DROITE, APRES CE QU ELLE OUVRE. A gauche on lit d'abord un signe,
   puis ce qu'il commande ; a droite on lit l'etat -- « Partage avec le
   client », « 8/9 » -- et le signe vient dire qu'il y a quelque chose dessous.
   C'est l'ordre de la lecture, et celui des autres depliants du produit. */
.viz-rail__depli__titre::after {
    content: "";
    order: 3;
    flex: none;
    margin-left: var(--e-3);
    margin-right: 5px;
    width: 6px;
    height: 6px;
    border-right: 1.5px solid currentColor;
    border-bottom: 1.5px solid currentColor;
    transform: rotate(-45deg);
    transition: transform var(--duree-effleurement, .16s) var(--courbe, ease);
}

/* Le compte se colle a la fleche plutot qu'au bord : les deux forment la partie
   droite de la ligne, l'intitule prend le reste. */
.viz-rail__depli__titre > :first-child {
    flex: 1 1 auto;
    min-width: 0;
}

.viz-rail__depli[open] .viz-rail__depli__titre::after {
    transform: rotate(45deg);
}

.viz-rail__depli__titre:hover {
    color: var(--texte-vif);
}

/* Le compte se range a droite : il resume le depliant ferme. */
.viz-rail__compte {
    margin-left: auto;
    padding: 1px 7px;
    border-radius: var(--rayon-gelule);
    background: var(--surface-discrete);
    color: var(--texte-calme);
    font-size: 11px;
    font-variant-numeric: tabular-nums;
    letter-spacing: 0;
}

/* SIX PIXELS ENTRE DEUX BOUTONS, pas douze. Dans un depliant qui n'en contient
   que deux, douze les faisaient flotter ; six suffisent a dire qu'ils sont
   distincts sans les separer. C'est l'ecart des onglets, pour la meme raison. */
.viz-rail__depli__dedans {
    display: flex;
    flex-direction: column;
    gap: var(--e-3);

    /* QUATRE PIXELS EN HAUT, POUR LE HALO DE L APPEL. Le contenu d'un
       `<details>` ouvert est clos par sa propre boite : le halo du premier
       bouton, qui deborde de trois pixels, etait coupe net sur son bord haut
       (mesure : un anneau rouge opaque ne peignait pas une seule ligne
       au-dessus du bouton, alors qu'il peignait sur les trois autres cotes).
       La marge lui rend sa place, dans la boite. */
    padding: var(--e-2) var(--e-4) var(--e-6);
}

.viz-rail__depli__dedans select,
.viz-rail__depli__dedans .partage-duo__principal,
.viz-rail__depli__dedans .diagnostic-viewer-topbar__commande,
.viz-rail__depli__dedans .diagnostic-viewer-tool-btn {
    box-sizing: border-box;
    width: 100%;
}

/* LA MARGE DU BAS SE RETIRE, et c'est le depliant qui espace.

   L'Admin, le 15/10/2026 : « reduit l'espace entre le selecteur et "Partager
   tout le test" ». Mesure : 22 px, dont 16 viennent de `lk.css`, qui pose
   `margin-bottom: 16px` sur TOUT `select` de l'application -- une marge faite
   pour des formulaires empiles, pas pour un depliant qui a deja son `gap`.
   Les six qui restent sont celui du depliant, le meme qu'entre ses boutons.

   LE SELECTEUR DU DEPLIANT SEULEMENT : la regle de `lk.css` sert partout
   ailleurs, et la retirer la-bas recollerait les champs de toutes les pages. */
.viz-rail__depli__dedans select {
    padding: var(--e-4) var(--e-5);
    margin-bottom: 0;
    background-color: var(--surface-controle);
    border: var(--filet-epaisseur) solid var(--filet-neutre-controle);
    border-radius: var(--rayon-controle);
    color: var(--texte-fort);
    font: inherit;
    font-size: 13px;
}

/* « Mettre a jour » et son arret restent sur une ligne : l'arret n'est pas une
   action de meme rang, c'est le retrait de la premiere. */
.viz-rail__depli__dedans .partage-duo {
    display: flex;
    align-items: stretch;
    gap: var(--e-3);
}

.viz-rail__depli__dedans .partage-duo__arret {
    flex: none;
}

/* « TOUT LE TEST » COMMANDE LES HUIT AUTRES, donc il se detache d'elles : sous
   les reglages de duree, au-dessus de la liste qu'il gouverne, et non dans la
   liste ou il passerait pour une neuvieme planche. Il en prend la FORME -- meme
   hauteur, meme case -- parce que c'est le meme geste a une autre portee. */
/* « TOUT LE TEST » PREND LA FORME EXACTE D UN ONGLET, parce que c'est le meme
   geste a une autre portee : le libelle a gauche, la case a droite, meme
   remplissage, meme rayon, meme taille de texte. Ce qui le distingue est sa
   PLACE -- au-dessus de la liste, dans le depliant -- et son filet, qui dit
   qu'il commande ce qui suit. Lui donner une forme a part en plus aurait dit
   deux fois la meme chose avec deux vocabulaires. */
/* IL N EST PAS DANS LA LISTE, IL LA COMMANDE. Il portait la forme d'un onglet
   parce que c'est le meme geste ; mais une boite de plus au-dessus de neuf
   boites en faisait dix, et celle du haut n'est pas une planche. Elle perd donc
   son cadre et son fond, s'aligne sur le bord du depliant, et garde la hauteur
   des autres lignes -- ce qui la distingue est sa PLACE, pas une forme a elle. */
.viz-rail__depli__dedans .diagnostic-viewer-nav__item--tout {
    display: flex;
    align-items: center;
    gap: var(--e-4);

    /* IL S ALIGNE SUR LES ONGLETS, pas sur le depliant. Il etait a 22 px du
       bord quand ils sont a 14 : le depliant ajoute son propre retrait, qui a
       du sens pour un bloc de reglages et pas pour une ligne qui commande la
       liste juste dessous. */
    margin: 0 calc(var(--e-4) * -1);

    /* LE MEME REMPLISSAGE QU UNE PLANCHE, AUX QUATRE COTES : 9 en haut et en
       bas, 10 a gauche pour le texte, 8 a droite pour la colonne des pastilles.
       Il portait 10 des deux cotes, ce qui posait sa case deux pixels avant
       celle des neuf rangees qu'il commande. */
    padding: 9px 8px 9px 10px;
    background: none;

    /* UN FILET TRANSPARENT, PAS `border: 0` : sans lui il rend 36 px quand les
       onglets en font 38. Il n'a pas de trait a montrer, il en a un a occuper --
       le meme raisonnement que pour les aplats des boutons. */
    border: var(--filet-epaisseur) solid transparent;
    color: var(--texte-calme);
    font-size: 14px;
    line-height: 18px;
}

/* IL RENDAIT VINGT PIXELS DE PLUS QU UN ONGLET : le libelle du produit porte
   son propre remplissage, qui s'ajoutait a celui de la rangee. Meme forme veut
   dire meme hauteur. */
/* CE N ETAIT PAS MON REMPLISSAGE, C ETAIT SON `min-height`. Le produit pose
   38 px sur le libelle d'onglet -- une cible tactile, juste dans une rangee
   horizontale -- et il s'ajoutait ici a la hauteur que la rangee porte deja :
   58 px contre 38. */
.viz-rail__depli__dedans .diagnostic-viewer-nav__item--tout .diagnostic-viewer-nav__link {
    flex: 1 1 auto;
    min-width: 0;

    /* LE TEXTE PART A GAUCHE, COMME CELUI DES NEUF RANGEES. Le libelle du
       produit est un `inline-flex` centre -- juste dans une rangee d'onglets ou
       il remplit sa case -- et etire sur toute la largeur il posait son texte a
       58 quand les planches commencent a 25. */
    justify-content: flex-start;

    /* ET IL PREND L ENCRE DE SA RANGEE. Le produit lui donne 0,78 de blanc pur
       quand les planches sont au cran calme : deux gris pour des rangees de la
       meme famille, a deux pixels l'une de l'autre. */
    color: inherit;

    /* `min-height` NE SUFFISAIT PAS : le libelle porte aussi une HAUTEUR, pas
       seulement un minimum. Remise a `auto`, c'est sa ligne de texte qui decide,
       comme dans les autres onglets. */
    height: auto;
    min-height: 0;
    padding: 0;
    border: 0;
    background: none;
    font-size: inherit;
    line-height: inherit;
}

/* IL PREND TOUTE LA LARGEUR, ET C EST CE QUI MET SA CASE AU BORD DROIT.

   `body .diagnostic-viewer-nav__item--tout { align-self: flex-start }` lui vient
   de la barre du haut, ou il se cale seul sous son intitule -- et la, c'est
   juste. Dans le depliant, il commande la liste qui suit : sa case doit tomber
   dans la colonne des pastilles, comme celle de chaque planche. Serre sur son
   texte, il mesurait 165 px pour 227 de colonne, et le `flex: 1` pose sur son
   libelle ne mordait sur rien.

   TROIS CRANS, PAS DEUX : la regle du produit vaut deux classes plus un
   element (`body`), donc `.viz-rail__depli__dedans .…--tout` perdait. */
.viz-rail .viz-rail__depli__dedans .diagnostic-viewer-nav__item--tout {
    align-self: stretch;

    /* ET IL DOIT POUVOIR DEPASSER SA BOITE. Le retrait negatif ci-dessus le
       fait sortir du remplissage du depliant pour retrouver le bord des
       onglets ; `max-width: 100%` le rendait a la largeur du CONTENU du
       depliant, donc decale a gauche : bord gauche a 14 comme les planches,
       bord droit a 225 quand le leur est a 241, et sa case 22 px avant la
       colonne des pastilles. */
    max-width: none;
}

/* SANS MARGE A DROITE : le remplissage de la rangee la place deja. Les quatre
   pixels qu'elle portait venaient de l'epoque ou cette rangee se serrait sur son
   texte, au milieu du depliant ; etiree jusqu'au bord des onglets, ils
   decalaient sa case de la colonne des pastilles.
   Mesure : bord droit de la case a 186 contre 192 pour celle d'une planche ;
   apres, les deux tombent a 232. */
.viz-rail__depli__dedans .diagnostic-viewer-nav__item--tout .partage-interrupteur {
    margin-right: 0;
}

.viz-rail__depli__dedans .partage-interrupteur {
    height: auto;
    min-height: 0;
    margin: 0;
}

.viz-rail__depli__dedans .partage-interrupteur__texte {
    display: none;
}

/* -------------------------------------------------------------------------
   LE DEPLIANT NE DOIT PAS ECRASER CE QU IL REGLE
   -------------------------------------------------------------------------
   Ouvert, il prenait deux cents pixels et la liste ne montrait plus que cinq
   planches sur neuf : on reglait ce qu'on ne voyait pas.

   TROIS CHOSES DE TROP. Son intitule, que le titre du depliant dit deja. La
   duree, la mise a jour et l'arret sur trois lignes, alors qu'ils forment un
   seul geste : combien de temps, et applique. Et la hauteur tactile du
   controle du produit, qui vise le doigt dans une rangee d'onglets et ne sert
   plus a rien dans une liste ou la cible est la ligne.
   ------------------------------------------------------------------------- */

/* EN DESCENDANT, PAS EN ENFANT DIRECT. La maquette est construite par un
   script qui DEPLACE la pile de commandes hors de son panneau ; le produit, lui,
   doit garder ce panneau : c'est `.partage-durable` qui porte les adresses que
   le script appelle et l'etat publie. La pile est donc un petit-enfant du
   depli, et `>` ne mordait sur rien -- l'intitule reapparaissait, la duree et
   les deux gestes reprenaient trois lignes. */
.viz-rail__depli__dedans .viewer-tool-stack > .viewer-tool-field__label {
    display: none;
}

/* Combien de temps, et applique : un seul geste, une seule ligne. */
.viz-rail__depli__dedans .viewer-tool-stack {
    display: grid;
    grid-template-columns: 1fr auto;
    gap: var(--e-3);
}

.viz-rail__depli__dedans .viewer-tool-stack select {
    grid-column: 1 / -1;
}

.viz-rail__depli__dedans .partage-duo__principal {
    padding: var(--e-4) var(--e-5);
    font-size: 13px;
    line-height: 1.2;
}

/* L OUVERTURE SE DEROULE, elle ne saute pas -- meme argument que le volet de la
   liste de clients. `interpolate-size` rend `height: auto` interpolable, et
   `::details-content` est la boite que `<details>` cache : ensemble ils
   animent une ouverture que rien ne savait animer. La ou ils manquent,
   l'ouverture reste instantanee et rien d'autre ne change. */
@supports (interpolate-size: allow-keywords) {
    :root {
        interpolate-size: allow-keywords;
    }

    .viz-rail__depli::details-content {
        height: 0;
        overflow: hidden;
        transition: height var(--duree-depli, .22s) var(--courbe, ease),
                    content-visibility var(--duree-depli, .22s) allow-discrete;
    }

    .viz-rail__depli[open]::details-content {
        height: auto;
    }
}

@media (prefers-reduced-motion: reduce) {
    .viz-rail__depli::details-content,
    .viz-rail__depli__titre::after {
        transition: none;
    }
}


/* -------------------------------------------------------------------------
   LA SEANCE ET SON JUGEMENT, SUR UNE LIGNE
   -------------------------------------------------------------------------
   « Ecarter ? » juge la seance CHOISIE. Empile sous le selecteur, il redevient
   une question posee a la cantonade -- ecarter quoi ? Sur sa ligne, il n'a plus
   besoin d'etre explique : ce qui est a gauche est ce qu'il juge.

   IL SE REDUIT A UNE MARQUE, PAS A RIEN. Le libelle reste dans le balisage,
   donc le nom accessible du bouton et son infobulle disent toujours la phrase
   entiere. Une croix seule pourrait se lire comme une suppression ; c'est la
   modale du produit, qui s'ouvre au clic et rappelle que le geste est
   reversible, qui l'en empeche.
   ------------------------------------------------------------------------- */

/* LA LIGNE EST LE REPERE, PAS UNE COLONNE DE PLUS. Un `<select>` natif ne peut
   rien contenir -- ni bouton, ni icone -- donc la croix se POSE dessus : la
   ligne se declare comme repere, la croix s'y place en absolu, et le selecteur
   recule son texte de la place qu'elle prend.

   ELLE SE RANGE AVANT LA FLECHE DU NAVIGATEUR, qui vit dans les vingt derniers
   pixels du cadre et qu'on ne peut ni deplacer ni mesurer. */
.viz-rail__ligne {
    position: relative;
}

/* ON REPREND LA FLECHE AU NAVIGATEUR. La sienne vit dans les derniers pixels
   du cadre, on ne peut ni la mesurer ni la deplacer, et elle prenait une
   vingtaine de pixels dont le texte manquait : la coupe tombait au milieu de
   « 12:43 ». Dessinee, elle en prend douze et on sait ou elle est.

   C'est le meme carre tourne que la coquille emploie pour le menu du compte :
   un glyphe ne suivrait ni la couleur ni la taille du texte. */
/* DEUX CLASSES, PARCE QUE LA BASE ELLE-MEME GAGNAIT. `elements-retenus.css`
   pose `.lk-body select { font-size: 14px }` -- une classe et un element,
   exactement le poids de cette regle -- et il est charge apres la coquille : a
   poids egal, la derniere gagne. Le texte demandait alors 170 px pour 135, et
   la coupe tombait au milieu de « 12:43 ». */
.viz-rail .viz-rail__ligne select {
    appearance: none;
    -webkit-appearance: none;

    /* HUIT A GAUCHE, TRENTE-QUATRE A DROITE : c'est ce qui reste apres la croix
       et la fleche, et ca rend 141 px au texte pour 145 demandes. La DATE et
       l'HEURE tiennent en entier ; ce qui se coupe est la fin de la mention
       « (partage) », qui se relit dans la liste ouverte. Ce qu'on cherche est
       en tete, et c'est la seule chose qui ne doit jamais etre rognee. */
    padding-left: var(--e-4);
    padding-right: 34px;
    font-size: 12px;
}

.viz-rail__ligne::after {
    content: "";
    position: absolute;
    top: 50%;
    right: 12px;
    width: 6px;
    height: 6px;
    margin-top: -5px;
    border-right: 1.5px solid var(--texte-second);
    border-bottom: 1.5px solid var(--texte-second);
    transform: rotate(45deg);
    pointer-events: none;
}

/* TROIS CLASSES, ET C EST LA RAISON. La garde anti-debordement pose
   `width: 100%` sur tout ce qui arrive de la rangee horizontale, `.test-judgement`
   compris, et elle est ecrite plus bas : a deux classes contre deux, elle
   gagnait. La boite du jugement faisait donc 183 px de large, collee a droite,
   avec sa croix rangee tout a gauche -- c'est-a-dire de l'autre cote du
   selecteur. */
.viz-rail .viz-rail__ligne .test-judgement {
    position: absolute;
    top: 50%;
    right: 30px;
    width: auto;
    min-width: 0;
    transform: translateY(-50%);
}

.viz-rail__bloc .viz-rail__ligne .test-judgement__trigger {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    margin: 0;
    padding: 0;

    background: none;
    border: var(--filet-epaisseur) solid transparent;
    border-radius: var(--rayon-controle);
    color: var(--texte-second);

    /* Le mot s'efface, il ne part pas : `font-size: 0` le garde dans l'arbre
       d'accessibilite, la ou `display: none` l'en retirerait. */
    font-size: 0;
    text-decoration: none;
    cursor: pointer;
}

/* UN PIXEL DE CORRECTION OPTIQUE, ET C EST BIEN OPTIQUE : la boite etait deja
   centree -- 247 a 269 dans un cadre de 237 a 279 -- mais le glyphe `×` ne
   remplit pas sa ligne, il se pose au-dessus de son centre. Ce qui se mesure
   etait juste ; ce qui se voit ne l'etait pas. */
.viz-rail__bloc .viz-rail__ligne .test-judgement__trigger::before {
    content: "\00d7";
    margin-top: -1px;
    font-size: 17px;
    line-height: 1;
}

.viz-rail__bloc .viz-rail__ligne .test-judgement__trigger:hover {
    background: var(--surface-effleuree);
    border-color: var(--filet-neutre-controle);
    color: var(--texte-vif);
}


/* -------------------------------------------------------------------------
   LE STATUT EST CE QUI OUVRE LE PARTAGE
   -------------------------------------------------------------------------
   Deux lignes disaient la meme chose : la pastille « Partage avec le client »
   sous l'identite, et l'intitule « Ce que le client garde » plus bas. L'une
   disait l'etat sans mener nulle part, l'autre ouvrait ce qui le change.

   LA PASTILLE EST DEVENUE L INTITULE. Une seule ligne dit ou l'on en est et
   s'ouvre sur ce qui le decide -- et elle garde les mots du produit, que le
   produit continue de mettre a jour tout seul puisque c'est le meme element.
   ------------------------------------------------------------------------- */

.viz-rail__depli--partage .partage-durable__etat {
    display: flex;
    align-items: center;

    /* ELLE REVIENT AU BORD GAUCHE. `justify-content: flex-end` lui vient de la
       version ou elle se posait au COIN de la carte, hors du flux : la, pousser
       le contenu a droite etait juste. Devenue l'intitule d'un depliant, elle
       occupe toute la ligne, et son voyant partait 28 px a droite du bord de la
       colonne pendant que le titre voisin et les neuf onglets commencent a 25.
       Mesure : voyant a 53,6 ; apres, 25,9. */
    justify-content: flex-start;

    gap: var(--e-3);
    margin: 0;
    padding: 0;
    background: none;
    border: 0;

    /* LE STATUT PERD SES CAPITALES, ET C EST CE QUI LE REND LISIBLE.
       « Partage avec le client » est une PHRASE, pas un intitule : en
       capitales espacees elle demandait 190 px pour 183 disponibles et se
       coupait a « PARTAGE AVEC L... ». En casse normale elle en demande 105 et
       se lit en entier. Sa casse la distingue en plus du titre voisin : l'un
       est un libelle de rangement, l'autre dit ou en est ce dossier. */
    font-size: 12.5px;
    font-weight: 600;
    letter-spacing: normal;
    text-transform: none;

    /* LA TEINTE VA SUR LE VOYANT, PAS SUR LA PHRASE. Vert a 92 % sur la surface
       du rail rendait 3,96 en clair. Le point est la pour porter l'etat -- il
       ne fait que ca -- et la phrase le nomme : elle prend l'encre du theme, qui
       se retourne avec lui. Meme regle que les boutons, et on ne perd rien. */
    color: var(--texte-plein);
}

/* SAUF QUAND LE PARTAGE EST TERMINE : LA, LA PHRASE PORTE AUSSI L ETAT.

   La regle au-dessus envoie la teinte sur le voyant et laisse la phrase a
   l'encre du theme, et elle a raison pour les trois etats qui decrivent une
   situation en cours. Celui-ci n'en decrit pas une : il annonce que ce qu'on
   avait donne n'est plus donne. L'Admin, le 29/10/2026, a demande le rouge
   sur le mot -- « rajouter en rouge "Partage termine" » -- et un point de
   huit pixels ne le porte pas tout seul dans une colonne de 204 px.

   `--red-rgb` EST STABLE DANS LES DEUX THEMES, c'est pour ca qu'il est pris
   ici : l'alerte ne doit pas changer de teinte selon l'habillage. */
.viz-rail__depli--partage .partage-durable__etat.is-expired {
    color: rgb(var(--red-rgb));
}

.viz-rail__depli--partage .partage-durable__voyant {
    flex: none;
}

/* L INTITULE CEDE AVANT LE COMPTE. « Partage avec le client » plus « 8/8 » ne
   tiennent pas toujours sur 204 px : c'est la phrase qui se coupe, pas le
   chiffre. Une phrase tronquee se devine encore -- « Partage avec le cli… » --
   la ou un compte tronque ne veut plus rien dire. */
/* SANS `overflow`, PARCE QUE LE POINT BAT. Je l'avais pose pour couper la
   phrase trop longue ; elle passe a la ligne depuis, la coupe ne sert plus, et
   elle mangeait la moitie du point qui pulse -- son battement le fait deborder
   de sa boite, c'est le principe meme de l'animation. */
.viz-rail__depli--partage .partage-durable__etat {
    min-width: 0;
}

/* ELLE PASSE A LA LIGNE PLUTOT QUE DE SE COUPER. Elle tient sur 139 px et la
   ligne en offre 183, mais le compte lui en prend une trentaine : l'ellipse
   mangeait la fin de la phrase. Deux lignes dans un titre de depliant coutent
   quinze pixels ; une phrase tronquee coute son sens. */
.viz-rail__depli--partage .partage-durable__etat > span:not(.partage-durable__voyant) {
    line-height: 1.25;
}

.viz-rail__depli--partage .viz-rail__compte {
    flex: none;
}


/* -------------------------------------------------------------------------
   CE QUE LES CONTROLES RAMASSENT EN CHEMIN
   -------------------------------------------------------------------------
   Ils viennent d'une rangee horizontale ou chacun avait ete regle pour son
   voisin de droite ; empiles dans une colonne, ils apportent ces reglages avec
   eux. Le releve sur le rail entier trouvait HUIT tailles de texte -- 10,5, 11,
   12, 12,5, 13, 14, 15, 18 -- et SIX rayons : 2, 8, 12, 18, 50 % et la gelule.

   La base en declare un de chaque famille. Douze, dix-huit et deux ne sont pas
   des choix faits ici : ils ont ete faits ailleurs, pour un autre contexte, et
   ils ont voyage. On les ramene, une fois, a l'entree du rail -- corriger
   chaque exemplaire serait recommencer au prochain controle deplace.
   ------------------------------------------------------------------------- */

/* RIEN NE DEPASSE DU RAIL, ET C EST UNE REGLE, PAS UNE CORRECTION. Ces
   controles arrivent d'une rangee horizontale avec des largeurs minimales
   posees pour elle : le formulaire du selecteur de seance porte
   `min-width: 200px`, pour 183 de place. Il debordait de dix-sept pixels, par
   dessus le bord du rail. Corriger l'exemplaire aurait laisse passer le
   suivant ; on pose la contrainte a l'entree, une fois. Ce qui est trop long
   se coupe -- un texte tronque se lit encore, un bouton hors du rail ne se
   clique plus. */
.viz-rail *,
.viz-note * {
    max-width: 100%;
}

.viz-rail .diagnostic-viewer-topbar__picker,
.viz-rail .viewer-tool-field,
.viz-rail .consultation-control,
.viz-rail .consultation-control__form,
.viz-rail .test-judgement {
    box-sizing: border-box;
    width: 100%;
    min-width: 0;
}

/* TOUT FAIT TRENTE-HUIT PIXELS DANS CETTE COLONNE, et il a fallu trois
   corrections pour une seule mesure :

     l'onglet          9 + 9 de remplissage, 18,2 de ligne, 2 de filet   38
     « Envoyer »       la meme chose                                     38
     « Commander »     un `min-height: 42px` pose par le produit         42
     « Mettre a jour » le meme                                           42
     l'arret           le meme, sans texte pour le contredire            42
     l'appel           la meme chose SANS filet                          36

   LE `min-height` EST UNE CIBLE TACTILE, et il a raison dans une rangee
   horizontale ou le bouton n'a que sa hauteur pour se laisser viser. Dans une
   colonne ou tout fait 38, il fabrique deux echelles. Le remplissage suffit, et
   38 px reste au-dessus de ce qu'un doigt demande.

   L APLAT PREND UN FILET TRANSPARENT : il n'en a pas a montrer, mais il en a un
   a OCCUPER, sinon il rend deux pixels de moins que ses voisins. C'est le genre
   d'ecart qu'on ne voit pas seul et qui saute aux yeux dans une pile.

   L ARRET, LUI, S ECRIT. Sans texte, aucun remplissage ne lui donnera la
   hauteur d'un bouton. */
/* `min-height` SEUL NE SUFFIT PAS, ET C EST LA DEUXIEME FOIS. Le produit pose
   `min-height: 42px` ET `height: 42px` : remettre le minimum a zero laisse la
   hauteur ecrite, qui tient toute seule. La meme chose m'avait deja arrete sur
   « Tout le test », et je n'avais corrige que l'exemplaire.

   Dans cette colonne, la hauteur d'un controle vient de son remplissage, pas
   d'un nombre ecrit ailleurs pour une rangee horizontale. */
.viz-rail .diagnostic-viewer-tool-btn,
.viz-rail .partage-duo__principal,
.viz-rail .partage-duo__arret,
.viz-rail .viewer-note-client__retrait {
    height: auto;
    min-height: 0;
}

/* TROIS CLASSES POUR « Commander », parce que la regle du produit en a deux et
   qu'elle est ecrite apres. Deux ecritures pour la meme chose ne se departagent
   pas au merite : c'est l'ordre qui tranche, et l'ordre etait contre moi. */
.viz-rail .viz-rail__depli__dedans .diagnostic-viewer-topbar__commande {
    height: auto;
    min-height: 0;
}

/* LE SELECTEUR REPREND UN MINIMUM. C'est un champ : son texte est plus petit,
   et le remplissage seul le laissait a 36 quand tout le reste fait 38. */
.viz-rail select {
    min-height: 38px;
}

.viz-rail .diagnostic-viewer-tool-btn--primary {
    border: var(--filet-epaisseur) solid transparent;
}

/* « TERMINER LA CONSULTATION » ETAIT ILLISIBLE EN CLAIR.
   -------------------------------------------------------------------------
   Son aplat est un violet fonce ECRIT EN DUR, `#402879`, le meme des deux
   cotes du theme -- et c'est juste : ce bouton dit qu'une consultation est en
   cours, et cet etat ne change pas de couleur parce qu'on a change de fond.

   Son encre, elle, lisait `--ink-200`, c'est-a-dire un BARREAU de la palette et
   non un role. En sombre ce barreau donne un gris clair, qui se lit sur le
   violet ; en clair il donne (51, 65, 85), donc un gris SOMBRE sur un violet
   sombre. Le bouton portait un texte qu'on ne pouvait pas lire.

   UN APLAT QUI NE SE RETOURNE PAS VEUT UNE ENCRE QUI NE SE RETOURNE PAS :
   `--texte-sur-aplat-sombre` existe exactement pour ca. C'est le meme piege que
   le nom du compte dans la barre du haut, a ceci pres qu'ici il rendait le
   bouton inutilisable au lieu de seulement pale. */
.diagnostic-viewer-tool-btn--secondary {
    color: var(--texte-sur-aplat-sombre);
}

.viz-rail .partage-duo__arret {
    height: 38px;
}

/* PAS DE HALO SOUS LES BOUTONS. Le produit pose une ombre portee COLOREE --
   `0 8px 18px` de cyan sous l'appel, de vert sous la mise a jour, d'or sous la
   commande. Dans une rangee horizontale large elle donne du relief ; dans une
   colonne de 256 px, trois halos colores empiles font du neon. L'aplat se
   suffit, et le rail est deja une surface qui flotte sur la page. */
.viz-rail .diagnostic-viewer-tool-btn,
.viz-rail .partage-duo__principal,
.viz-rail .partage-duo__arret,
.viz-rail .diagnostic-viewer-topbar__commande,
.viz-rail__note {
    box-shadow: none;
}

.viz-rail select,
.viz-rail button,
.viz-rail input,
.viz-rail textarea,
.viz-rail a.diagnostic-viewer-topbar__commande,
.viz-rail .partage-duo__principal,
.viz-rail .partage-duo__arret,
.viz-rail .diagnostic-viewer-nav__item {
    border-radius: var(--rayon-controle);
}

/* La gelule et le rond sont des FORMES, pas des coins : ils ne suivent pas
   l'axe des rayons, le ronger ferait un rectangle a coins mous. */
.viz-rail .help-trigger,
.viz-rail .viz-rail__compte {
    border-radius: var(--rayon-gelule);
}

/* LE SELECTEUR DE SEANCE COUPAIT SON TEXTE. « 04.09.2026 12:43 (partage) » fait
   163 px pour 160 disponibles : la mention entre parentheses perdait sa
   fermeture, et c'est elle qui dit que ce resultat est partage. A 13 px, la
   taille des autres controles du rail, il en fait 151. */
.viz-rail select {
    font-size: 13px;
}

/* DEUX TAILLES A UN DEMI-PIXEL D ECART NE SONT PAS DEUX ROLES. Le surtitre de
   l'identite et les intitules de bloc nomment tous les deux ce qui suit. */
.viz-rail__etiquette,
.viz-rail__depli__titre {
    font-size: 11px;
}

.viz-rail__bloc .viewer-compteur {
    font-size: 12px;
}


/* -------------------------------------------------------------------------
   LES BOUTONS DU RAIL — leur dessin appartient a la base
   -------------------------------------------------------------------------
   IL N Y ETAIT PAS. Il vivait dans la feuille d'essai, derriere
   `html[data-boutons]` : sans l'axe -- c'est-a-dire DANS LE PRODUIT -- le rail
   rendait les boutons bruts, gris et plats, et « Commander » restait un lien
   bleu souligne. Sur les pages du banc l'axe etait toujours pose, donc le
   defaut ne pouvait pas s'y voir ; la planche d'etats, qui ne charge que la
   base, l'a montre d'un coup.

   C EST LE TRAITEMENT « VOILE » : celui qu'on affine depuis le debut, et le
   seul qui passe le contraste dans les deux themes. L'axe garde ses trois
   choix, mais il REMPLACE maintenant un dessin qui existe au lieu d'etre le
   seul a en avoir un.

   TROIS RANGS, UN SEUL APLAT :
       l'appel de l'ecran   aplat d'accent       « Demarrer la consultation »
       ce qui engage        voile de SA teinte   le partage, la commande
       ce qui accompagne    voile neutre         la note, l'arret
   ------------------------------------------------------------------------- */

.viz-rail .diagnostic-viewer-tool-btn,
.viz-rail .partage-duo__principal,
.viz-rail .diagnostic-viewer-topbar__commande,
.viz-rail__note {
    box-sizing: border-box;
    display: block;
    width: 100%;
    padding: 9px 10px;
    border: var(--filet-epaisseur) solid transparent;
    border-radius: var(--rayon-controle);
    font: inherit;
    font-size: 14px;
    font-weight: 600;
    line-height: 1.3;
    text-align: center;
    text-decoration: none;
    cursor: pointer;
    transition: background var(--duree-effleurement, .16s) ease,
                color var(--duree-effleurement, .16s) ease;
}

/* La note est un aplat : elle OCCUPE le filet transparent du bloc commun,
   comme les deux boutons au-dessus d'elle. */
.viz-rail__note { border-color: transparent; }

/* L UNIQUE APPEL DE L ECRAN. Un seul bouton par ecran a droit a l'aplat, et sur
   une page de resultats c'est celui-la : ouvrir la seance a quelqu'un. Il
   descend d'un cran sous l'accent pur -- a pleine teinte, un cyan sature de
   236 px dans une colonne sombre se voit avant la page. */
.viz-rail .diagnostic-viewer-tool-btn--primary {
    background: color-mix(in srgb, rgb(var(--accent-surface-rgb)) 82%,
                          rgb(var(--surface-sombre-rgb)));
    color: var(--texte-sur-accent);
    font-weight: 700;
}

/* SES DEUX AUTRES ETATS, qui manquaient (mesure : le fond ne bougeait ni au
   survol ni a l'appui). Comme l'aplat du socle, il FONCE d'un cran, puis d'un
   second : aller vers la teinte pure irait vers le clair et coûterait du
   contraste au libelle. La teinte ne change pas, seule l'intensite. */
/* VERS L'ENCRE, PAS VERS LA SURFACE : `--surface-sombre-rgb` est claire dans
   le theme clair, et l'aplat s'y ECLAIRCISSAIT au survol (mesure : 0,86 ->
   0,87 -> 0,88 de rouge). L'encre de l'aplat est sombre des deux cotes ;
   melanger vers elle fonce toujours. */
/* IL GARDE SA COULEUR ET PREND UN HALO (planche des etats, retenu B). Rien ne
   bouge dans le bouton : c'est autour de lui que la reponse arrive. Le fond du
   repos est redit ici, sinon la teinte s'assombrissait SOUS le halo et l'on
   lisait deux reponses a un seul geste. */
.viz-rail .diagnostic-viewer-tool-btn--primary:hover {
    background: color-mix(in srgb, rgb(var(--accent-surface-rgb)) 82%,
                          rgb(var(--surface-sombre-rgb)));
    border-color: transparent;
    box-shadow: 0 0 0 3px rgba(var(--accent-surface-rgb), var(--voile-8));
    color: var(--texte-sur-accent);
}

/* L APPUI, LUI, L ENFONCE : le halo s'efface et la teinte descend d'un cran,
   vers l'encre et non vers la surface (elle est claire dans le theme clair, et
   l'aplat s'y eclaircissait). */
.viz-rail .diagnostic-viewer-tool-btn--primary:active {
    background: color-mix(in srgb, rgb(var(--accent-surface-rgb)) 72%,
                          var(--texte-sur-accent));
    border-color: transparent;
    box-shadow: none;
    color: var(--texte-sur-accent);
}

/* CE QUI ENGAGE GARDE SA TEINTE, EN VOILE. Le vert du partage et l'or de
   l'achat sont des ETATS du dossier, pas de l'interface : ils restent
   reconnaissables, mais au fond et au filet -- pose en texte, l'or rendait
   2,37 de contraste en clair. */
.viz-rail .partage-duo__principal {
    background: rgba(var(--share-fill-rgb), var(--voile-6));
    border-color: rgba(var(--share-line-rgb), var(--voile-8));
    color: var(--texte-plein);
}

.viz-rail .partage-duo__principal:hover:not(:disabled) {
    background: rgba(var(--share-fill-rgb), var(--voile-8));
}

.viz-rail .partage-duo__principal:disabled {
    opacity: 0.45;
    cursor: default;
}

/* LE DORE, PAS L OLIVE. `--achat-*` est un vert-jaune : il dit « achete » sur
   une planche, ou il vit a cote d'un vert de partage et doit s'en distinguer.
   Sur un bouton de commande, seul, il rend un vert delave. Le produit a son or
   -- `--teinte-or`, celui du bouton d'origine -- et c'est lui qu'on reprend.

   DEUX ROLES, DEUX TEINTES, et ce n'est pas une incoherence : l'ETAT « achete »
   reste olive parce qu'il se lit CONTRE le vert du partage ; l'ACTION
   « commander » est doree parce qu'elle se lit seule. */
.viz-rail .diagnostic-viewer-topbar__commande {
    /* LE PRODUIT LUI DONNE 28 PX DE MARGE HAUTE, pour l'ecarter de la rangee
       d'onglets qu'il suivait dans l'encadre. Dans le depliant, cette marge
       s'ajoutait aux six pixels d'ecart : 34 px au-dessus, 6 en dessous, et le
       bouton flottait entre ses deux voisins. */
    margin: 0;
    background: rgba(var(--teinte-action-rgb), var(--dose-cmd-repos));
    border-color: transparent;
    color: rgb(var(--encre-action-rgb));
    font-weight: 600;
}

/* DEUX APLATS SOURDS, UNE SEULE CONSTRUCTION (planche des etats, retenu B).
   La commande et la note partagent tout -- remplissage, rayon, corps, graisse,
   loi d'etats -- et ne different que par leur teinte : le bronze de
   `--teinte-action` pour l'une, le neutre pour l'autre. L'encre fonce avec le
   fond, comme la note passe de son encre calme a son encre vive. */
.viz-rail .diagnostic-viewer-topbar__commande:hover {
    background: rgba(var(--teinte-action-rgb), var(--dose-cmd-survol));
    border-color: transparent;
    color: rgb(var(--encre-action-vive-rgb));
}

.viz-rail .diagnostic-viewer-topbar__commande:active {
    background: rgba(var(--teinte-action-rgb), var(--dose-cmd-appui));
    border-color: transparent;
    color: rgb(var(--encre-action-vive-rgb));
}

/* L ARRET N EST PAS UNE ACTION DE MEME RANG : c'est le retrait de celle d'a
   cote. Il en prend la hauteur et pas la largeur, et son rouge ne s'allume
   qu'au survol -- un carre rouge plein et permanent criait plus fort que le
   partage lui-meme. */
.viz-rail .partage-duo {
    display: flex;
    align-items: stretch;
    gap: var(--e-3);
}

.viz-rail .partage-duo__principal {
    flex: 1 1 auto;
}

.viz-rail .partage-duo__arret {
    display: flex;
    flex: none;
    align-items: center;
    justify-content: center;
    width: 38px;
    padding: 0;
    background: var(--surface-controle);
    border: var(--filet-epaisseur) solid var(--filet-neutre-controle);
    border-radius: var(--rayon-controle);
    color: rgb(var(--red-rgb));
    cursor: pointer;
}

.viz-rail .partage-duo__arret:hover {
    background: rgba(var(--red-rgb), var(--voile-5));
}

/* L AIDE EST UN SIGNE, PAS UN BOUTON. Trente-deux pixels de rond bleu a cote
   d'un surtitre de onze : elle pesait plus que ce qu'elle explique.

   ET DEPUIS QU ELLE OUVRE LA NOTE, ELLE EST NUE. Le glyphe seul, le
   remplissage au survol : c'est la meme decision que pour le bouton du compte,
   pour la meme raison -- un fond permanent pese sur un bloc qui n'a rien
   d'autre a porter, et il pese le plus en theme clair.

   ELLE S ECRIT ICI ET NULLE PART AILLEURS. J'avais ajoute une regle sur
   `.viz-rail__note-bouton`, une classe seule, quand ces deux-la peignent deja
   le meme element : la nouvelle perdait la cascade et le bouton gardait son
   fond. Deplacer une peau ne consiste pas a l'ecrire au nouvel endroit, il
   faut la retirer de l'ancien. */
.viz-rail .help-trigger {
    width: 22px;
    min-width: 0;
    height: 22px;
    padding: 0;
    background: none;
    border: var(--filet-epaisseur) solid transparent;
    border-radius: var(--rayon-gelule);
    color: var(--texte-calme);
    font-size: 13px;
    font-weight: 700;
    line-height: 1;
}

.viz-rail .help-trigger:hover,
.viz-rail .help-trigger:focus-visible {
    background: var(--surface-effleuree);
    color: var(--texte-vif);
}


/* -------------------------------------------------------------------------
   LA MODALE DU MOT AU CLIENT
   -------------------------------------------------------------------------
   UN CHAMP QU ON REMPLIT UNE FOIS SUR DIX VISITES ne merite pas un tiers de
   l'encadre en permanence. Appele par un bouton, il ne coute qu'une ligne, et
   quand il s'ouvre il a enfin la place d'ecrire plus de trois mots.

   Elle porte la meme peau que la modale de sortie -- meme surface, meme filet,
   meme ombre -- parce que c'est le meme objet : quelque chose qui interrompt.
   ------------------------------------------------------------------------- */

.viz-note {
    width: min(560px, calc(100vw - var(--e-11)));
    padding: var(--e-9);

    background: var(--surface-modale);
    border: var(--filet-epaisseur) solid var(--filet-neutre-bloc);
    border-radius: var(--rayon-bloc);
    box-shadow: var(--ombre-modale);
    color: var(--texte-plein);
}

.viz-note__titre {
    margin: 0 0 var(--e-4);
    font-size: 18px;
    font-weight: 700;
}

.viz-note__aide {
    margin: 0 0 var(--e-6);
    color: var(--texte-calme);
    font-size: 14px;
    line-height: 1.5;
}

.viz-note .viewer-note-client__champ {
    box-sizing: border-box;
    width: 100%;
    min-height: 140px;
    padding: var(--e-5);
    background: var(--surface-controle);
    border: var(--filet-epaisseur) solid var(--filet-neutre-controle);
    border-radius: var(--rayon-controle);
    color: var(--texte-plein);
    font: inherit;
    resize: vertical;
}

/* Les deux actions de la note passent SOUS le champ : au-dessus, on demandait
   d'envoyer avant d'avoir ecrit. */
.viz-note .viewer-note-client {
    display: flex;
    flex-direction: column-reverse;
    gap: var(--e-6);
}

/* LES DEUX ACTIONS SUR UNE LIGNE. Le produit les empile -- `flex-direction:
   column` -- parce que dans la bande du haut elles tiennent une colonne de
   200 px ; dans une modale de 560, empilees et centrees, le bouton flottait au
   milieu et le retrait tombait a la ligne suivante. */
.viz-note .viewer-note-client__cote {
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: flex-start;
    gap: var(--e-5);
}

.viz-note .viewer-tool-field__label {
    display: none;
}

/* LA MODALE DE LA NOTE DU PRATICIEN. Elle porte la meme peau que celle du mot
   au client -- meme surface, meme filet, meme ombre -- parce que c'est le meme
   objet : quelque chose qui interrompt pour ecrire.

   LE BLOC ARRIVE OUVERT ET PERD SON RESUME : dans la page c'etait un depliant,
   pour ne pas couter 64 px a chaque visite ; dans une modale qu'on ouvre
   expres, un depliant demanderait un second clic pour la meme chose. */
/* TROIS CRANS, ET TOUT CE QUI PEINT. Ecrite en deux classes, la regle ne
   retirait que la couleur de fond : le DEGRADE restait, avec le remplissage,
   l'ombre et le rayon -- une carte dans une carte, mesure a 560 x 283 dans une
   modale de 560. Un fond transparent ne prouve pas qu'il n'y a rien de peint.

   ET LE FORMULAIRE AUSSI : il porte sa propre surface, posee pour vivre dans la
   page. Dans la modale, elle faisait une troisieme boite. */
.viz-note.viz-note--praticien .diagnostic-viewer-notes-card,
.viz-note.viz-note--praticien .expert-notes-form {
    margin: 0;
    padding: 0;
    background: none;
    border: 0;
    border-radius: 0;
    box-shadow: none;
    backdrop-filter: none;
}

.viz-note--praticien .expert-notes-card__summary {
    display: none;
}

/* LE BOUTON DE L IDENTITE. Il garde la taille et la forme de l'aide qu'il
   remplace : c'est le meme emplacement, la meme cible, et seul ce qu'il ouvre
   a change. */
/* Le dessin du bouton est dans `.viz-rail .help-trigger`, plus haut : c'est le
   meme element, et deux endroits pour une peau, c'est un jour ou ils
   divergent. */

/* -------------------------------------------------------------------------
   LA MODALE DU MOT RECU
   -------------------------------------------------------------------------
   Meme peau que ses deux soeurs -- meme surface, meme filet, meme ombre --
   parce que c'est le meme objet : quelque chose qui interrompt. Celle-ci ne
   fait que rendre : pas de champ, pas de formulaire, un seul bouton.
   ------------------------------------------------------------------------- */

/* LES RETOURS A LA LIGNE SONT CEUX QU ON A TAPES. `pre-line` honore les sauts
   et replie les espaces en trop : c'est du texte pur, et il se rend de la meme
   facon ici et sur le parcours. */
.viz-note__mot {
    margin: 0;
    white-space: pre-line;
    font-size: 15px;
    line-height: 1.6;
    color: var(--texte-plein);
}

/* RIEN A LIRE SE LIT AUTREMENT QU UN MOT. Le pointille du rail disparait avec
   le cadre ; dans la modale, c'est le gris et l'italique qui disent que la
   phrase vient du produit et non de quelqu'un. */
.viz-note__mot--vide {
    color: var(--texte-second);
    font-style: italic;
}

/* DE QUOI REPONDRE, SOUS LE MOT.

   UNE SEULE CIBLE, PAS UN VISAGE PUIS UN LIEN : le visage, le nom et l'action
   sont le meme geste, donc la meme surface cliquable. Deux cibles cote a cote
   pour la meme destination font hesiter.

   LE FILET AU-DESSUS LE SEPARE DU MOT sans en faire une carte : ce qui suit
   n'est plus ce que le praticien a ecrit. */
.viz-note__contact {
    display: flex;
    align-items: center;
    gap: var(--e-5);
    margin-top: var(--e-7);
    padding-top: var(--e-6);
    border-top: var(--filet-epaisseur) solid var(--filet-neutre-bloc);
    color: inherit;
    text-decoration: none;
}

.viz-note__contact:hover .viz-note__contact__action,
.viz-note__contact:focus-visible .viz-note__contact__action {
    color: var(--texte-vif);
    text-decoration: underline;
}

.viz-note__contact__dit {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.viz-note__contact__nom {
    font-weight: 700;
    font-size: 14px;
}

.viz-note__contact__action {
    color: var(--texte-calme);
    font-size: 13px;
}

.viz-note__fermer {
    margin-top: var(--e-6);
    padding: var(--e-4) var(--e-7);
    background: none;
    border: var(--filet-epaisseur) solid var(--filet-neutre-controle);
    border-radius: var(--rayon-bouton);
    color: var(--texte-calme);
    font: inherit;
    cursor: pointer;
}

.viz-note::backdrop {
    background: rgba(var(--black-rgb), 0.52);
}


/* -------------------------------------------------------------------------
   IL ENTRE PAR LA GAUCHE
   -------------------------------------------------------------------------
   IL NE SE SUBSTITUE PAS A L ENCADRE D UNE IMAGE A L AUTRE : il entre, et le
   mouvement dit d'ou il vient. C'est le meme argument que pour le volet de la
   liste de clients -- une apparition instantanee ne se lit pas, elle se
   constate.

   LA COQUE NE L ATTEND PAS. Son retrait est deja pose au chargement : le rail
   glisse par-dessus une page deja a sa place, donc rien ne se remet en page
   pendant le mouvement. Animer les deux ferait defiler le texte sous les yeux
   pour rien.
   ------------------------------------------------------------------------- */

/* IL ENTRE DE QUARANTE PIXELS, PAS DE SA LARGEUR ENTIERE, et c'est une mesure
   de surete autant qu'un choix de dessin.

   UNE ANIMATION GELEE TIENT SA PREMIERE IMAGE. Retirer `both` ne suffit pas :
   la ou les animations ne progressent pas -- un onglet en arriere-plan, un
   panneau masque, une impression -- l'animation est bien declaree, elle
   n'avance simplement jamais, et l'element reste a son point de depart. Avec un
   depart a `-100%`, ce point de depart etait hors de l'ecran : mesure a
   `left: -232` sur une fenetre de 1440, et une capture entierement vide que
   j'ai d'abord mise sur le compte de l'outil.

   A quarante pixels, le pire cas est un rail decale de quarante pixels et un
   peu pale. Il se lit, il se clique. LE MOUVEMENT NE DOIT JAMAIS ETRE CE QUI
   REND UN ELEMENT VISIBLE.

   Et le geste ne perd rien : ce qui dit « ca vient de la gauche » est la
   DIRECTION et la decelération, pas la distance parcourue. */
@keyframes viz-rail-entre {
    from { transform: translateX(-40px); opacity: 0.4; }
    to   { transform: translateX(0); opacity: 1; }
}

/* PAS DE `both`, ET C EST LA CHOSE IMPORTANTE DE CE BLOC.

   Avec `both`, l'element tient son image de DEPART tant que l'animation n'a
   pas commence -- c'est-a-dire, ici, entierement hors de l'ecran. Partout ou
   les animations ne partent pas -- un onglet en arriere-plan, un panneau
   masque, une impression, quelqu'un qui a coupe le mouvement autrement que par
   la requete de media -- le rail ne s'affichait jamais. Mesure : `left: -260`
   sur une fenetre de 1440, et une capture d'ecran entierement vide.

   UNE ANIMATION NE DOIT JAMAIS ETRE CE QUI REND UN ELEMENT VISIBLE. Sans
   `both`, l'etat au repos est la position normale : si l'animation joue, elle
   raconte l'arrivee ; si elle ne joue pas, le rail est simplement la. */
.viz-rail {
    animation: viz-rail-entre var(--duree-depli, .22s) var(--courbe, ease);
}

@media (prefers-reduced-motion: reduce) {
    .viz-rail {
        animation: none;
    }
}


/* LA RANGEE D ONGLETS S EN VA AVEC LUI, et c'est tout le gain : neuf libelles
   longs ne tenaient pas sur une ligne et se repliaient sur deux, 104 px avant
   le premier resultat. Les garder ferait deux navigations pour une seule
   question. Ils reviennent sous 760, quand le rail s'efface -- c'est ecrit
   dans les seuils, la ou il disparait, pour que les deux ne puissent pas
   manquer en meme temps. */
body:has(.viz-rail) .diagnostic-viewer-topbar__tabs {
    display: none;
}


/* LE DEDANS DU PIED — sur 76 pages, comme lui.

   TROIS ZONES SUR UNE LIGNE : l'etat des services a gauche, les liens au
   centre, la mention de propriete a droite. La colonne du milieu est celle qui
   s'etire, donc les liens restent centres sur la page et non sur ce qui reste
   apres les deux autres. */
.lk-app-footer__bas {
    /* IL NE PEUT PAS ETRE PLUS LARGE QUE LE PIED QUI LE PORTE. Le pied est en
       `content-box` pour que son remplissage a gauche le recentre sur la zone
       de contenu ; sa rangee, elle, heritait d'une largeur du produit et
       depassait de 91 px. La page prenait alors une barre de defilement
       horizontale, et toute capture d'ecran se decalait : j'ai d'abord pris ce
       decalage pour un defaut de l'outil de capture. */
    box-sizing: border-box;
    max-width: 100%;

    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    gap: var(--e-5);
    padding: 0 var(--e-10);
    font-size: 12px;
}

.lk-app-footer__etat {
    display: flex;
    align-items: center;
    gap: var(--e-3);
    font-size: 12px;
}

/* SOUS 620 PX LES TROIS PISTES NE TIENNENT PLUS, et cette bascule doit vivre
   ICI pour exister.

   ELLE ETAIT ECRITE DANS `lk.css` ET NE S APPLIQUAIT PLUS : la regle de base
   de cette couche-ci, sans media, est lue APRES elle et reposait les trois
   pistes a toutes les largeurs. Mesure du 17/10/2026 a 390 px, les trois
   declarations en presence : `minmax(0,1fr) auto minmax(0,1fr)` (lk.css),
   `minmax(0,1fr)` (lk.css, sous 620) et `1fr auto 1fr` (ici). C'est la
   derniere qui gagnait, et la piste de l'etat tombait a 83 px, ou sa phrase
   s'empilait sur six lignes.

   LE DEFAUT NE SE VOYAIT PAS TANT QUE L ETAT PRENAIT LA LIGNE ENTIERE : la
   regle `grid-column: 1 / -1` de `lk.css` le sortait des pistes des qu'une
   phrase tombait, donc leur largeur n'avait plus d'importance. En la retirant
   pour rendre l'etat a sa piste, on a decouvert ce que son ombre couvrait. */
@media (max-width: 620px) {
    .lk-app-footer__bas {
        grid-template-columns: minmax(0, 1fr);
        justify-items: center;
        gap: var(--e-4);
    }

    .lk-app-footer__etat {
        justify-self: center;
    }
}

.lk-app-footer__legal {
    display: grid;
    gap: var(--e-5);
    text-align: center;
    font-size: 12px;
}

/* LE PROPRIETAIRE AVAIT ETE OUBLIE PAR LA REPRISE, ET IL EST ILLISIBLE DEPUIS
   TOUJOURS. Cette peau avait redit l'encre des liens legaux et du sous-titre de
   marque, mais pas celle-ci : `lk.css` garde donc le dernier mot avec
   `rgba(var(--white-rgb), 0.34)`, une opacite inventee sur place. Elle rend
   2,13 en clair et 2,99 en SOMBRE -- ce n'est pas un defaut de theme, c'est une
   ligne qui n'a jamais tenu le seuil dans aucun des deux.

   Le socle a supprime le role `--texte-faible` a 40 % pour cette raison exacte
   (« il rend 3,31 sur sombre, et le pied de page le porte depuis toujours ») ;
   il ne restait qu'a debrancher le pied de la valeur qu'on avait condamnee.
   `--texte-second` rend 7,6 en clair et 11,5 en sombre. */
.lk-app-footer__owner {
    text-align: right;
    font-size: 12px;
    color: var(--texte-second);
}

/* LE MENU DU COMPTE. Il est cache jusqu'a ce qu'on l'ouvre, et c'est le seul
   endroit de la coquille ou un etat est porte par un attribut plutot que par
   une classe : `aria-expanded` dit deja la chose au navigateur et a qui n'y
   voit pas, donc en ajouter une le dirait deux fois. */
.lk-menu {
    display: none;
    position: absolute;
    top: calc(100% + var(--e-4));
    right: 0;
    z-index: 1600;
    min-width: 240px;
    padding: var(--e-3);

    background: var(--surface-modale);
    border: var(--filet-epaisseur) solid var(--filet-neutre-bloc);
    border-radius: var(--rayon-bloc);
    box-shadow: var(--ombre-modale);
}

.lk-top-bar__compte-bouton[aria-expanded="true"] + .lk-menu {
    display: block;
}


/* -------------------------------------------------------------------------
   CE QUI NE S AFFICHE PAS ICI
   -------------------------------------------------------------------------
   Deux blocs que le balisage rend et que le dessin retenu n'emploie pas. Les
   cacher dans la base plutot que compter sur un gabarit taille : une base qui
   suppose un balisage propre casse le jour ou il ne l'est pas.
   ------------------------------------------------------------------------- */

/* L ETIQUETTE DE COLONNE, DANS LA CELLULE. Chaque cellule porte le nom de sa
   colonne en plus de sa valeur : c'est ce qui permet au tableau de se replier
   en cartes sur telephone, ou il n'y a plus d'en-tete pour dire ce qu'on lit.
   Au-dessus du seuil l'en-tete existe, donc l'etiquette ferait doublon -- elle
   est cachee par defaut et montree en dessous, jamais l'inverse. */
.responsive-table__label {
    display: none;
    margin-bottom: var(--e-2);
    color: var(--texte-second);
    font-size: 12px;
}

/* LE PIED DU RAIL ET LE BLOC DOSSIER sont la matiere du traitement « dossier »,
   celui qui suit le CLIENT qu'on accompagne au lieu de suivre l'application.
   « Groupe » a ete retenu : ces deux blocs n'ont plus d'objet. A retirer du
   gabarit ensuite, c'est du balisage mort et pas seulement du style. */
.prat-rail__dossier,
.prat-rail__pied {
    display: none;
}


/* LES CONTROLES N HERITENT NI LA POLICE NI LA COULEUR.

   La police, c'est connu : `button`, `input`, `select` et `textarea` prennent
   celle du systeme si on ne la leur donne pas -- la barre changeait de police
   en changeant d'axe, sauf sur ses boutons.

   LA COULEUR EST LE MEME PIEGE, EN PLUS DISCRET. La feuille du navigateur pose
   `color: buttontext` sur les boutons, ce qui sous `color-scheme: dark` vaut
   BLANC PUR. Une valeur posee bat un heritage : retirer `color` d'un bouton ne
   le fait donc pas heriter du corps, ca le rend blanc. Sur fond sombre le
   resultat reste lisible, et rien ne signale l'ecart. */
button,
input,
select,
textarea {
    font-family: inherit;
    color: inherit;
}


/* -------------------------------------------------------------------------
   LA BARRE DU HAUT S ALIGNE SUR L INTERFACE, PAS SUR UNE BOITE DE 1400
   -------------------------------------------------------------------------
   Le produit lui donne `max(20px, (100% - 1400px) / 2 + 32px)` de chaque cote :
   52 px a 1440, calcules pour centrer un contenu de 1400 qui n'existe plus
   depuis que le rail occupe la gauche. Mesure : le logo partait a 52 quand le
   rail commence a 14, et le compte finissait a 1388 quand les cartes finissent
   a 1408. Deux bords qui ne tombent sur rien.

   A gauche, le remplissage du rail (14) : le logo tombe sur le bord interieur
   du rail. A droite, celui du contenu (32) : le compte et la bascule finissent
   sur le bord des cartes. */
.lk-top-bar {
    padding-left: 14px;
    padding-right: 32px;
}

/* LE SURVOL DU PRODUIT REMETTAIT LE VERT sur « Partager » : `--section:hover`
   et `--page:hover` posent un fond et un filet de leur teinte, a deux classes,
   donc devant la remise a plat de la classe seule. Le controle est une case et
   un mot ; au survol, le mot passe au vif, et c'est tout. */
.partage-interrupteur--section:hover,
.partage-interrupteur--page:hover,
.partage-interrupteur--onglet:hover {
    background: none;
    border-color: transparent;
}

.partage-interrupteur:hover .partage-interrupteur__texte {
    color: var(--texte-vif);
/* PAS D OMBRE SOUS LES TITRES. Elle venait de `lk.css`, ou `h1` porte un halo
   de 20 et 40 px : sur un titre de 21 px ce n'est pas du relief, c'est du flou,
   et le bord des lettres s'epaissit sans qu'on gagne en lisibilite. Le titre se
   detache par sa taille et sa graisse, pas par une ombre.

   L ATELIER GARDE SES VARIANTES (`data-titre=net|douce|ombre`) : on peut donc
   toujours comparer, mais l'etat RETENU est sans ombre. */
}


/* --- LES COMMANDES DU PRATICIEN, RANGEES EN COLONNE ----------------------
   -------------------------------------------------------------------------
   ELLES ONT ETE DESSINEES POUR UNE BARRE DE QUATRE COLONNES, et le rail leur
   en donne une de 227 px. Trois choses le trahissaient a l'oeil des que le
   depli s'ouvrait :

     `__tools` est en `display: contents` : ses trois piles ne formaient donc
     aucune boite et tombaient dans la grille du GRAND-PARENT, avec les vides
     de ses rangees -- deux cents pixels de blanc entre le selecteur de duree
     et « Partager les resultats ».

     `.viewer-note-client` est une rangee flex : le bouton et le champ se
     mettaient COTE A COTE dans 227 px, et la zone de texte tombait a soixante
     de large, un mot par ligne.

   Dans le rail, chacune reprend une boite et s'empile. Rien n'est repeint :
   on ne change que la direction. --------------------------------------------- */

.viz-rail__depli__dedans .diagnostic-viewer-topbar__tools {
    display: block;
}

.viz-rail__depli__dedans .viewer-tool-stack + .viewer-tool-stack {
    margin-top: var(--e-5, 10px);
}

.viz-rail__depli__dedans .viewer-note-client,
.viz-rail__depli__dedans .viewer-note-client__cote {
    display: block;
}

.viz-rail__depli__dedans .viewer-note-client__champ {
    width: 100%;
    box-sizing: border-box;
    margin-top: var(--e-4, 8px);
    /* SA HAUTEUR VENAIT D UNE COLONNE DEUX FOIS PLUS LARGE : la meme invite y
       tenait en trois lignes, elle en prend cinq ici, et la derniere etait
       coupee en deux par le bord. Un texte tranche a mi-hauteur est le defaut
       qu'on cherche justement a ne pas fabriquer en repondant a un
       debordement. */
    min-height: 9.5rem;
}



/* ==========================================================================
   LES TROIS ACTIONS DU PRATICIEN, SUR UNE LIGNE, DANS LA FICHE

   L'ADMIN, LE 15/10/2026 : « les 3 boutons de "Actions praticien", je pense a
   les transformer en 3 boutons sur 1 ligne, dans la carte de profil ».

   CE QUE CA REMPLACE : un depli ferme par defaut, 38 px de rangee pour cacher
   trois boutons pleine largeur, soit quatre rangees pour trois gestes.

   POURQUOI DES SIGNES. La fiche fait 207 px : trois boutons cote a cote en font
   63 chacun, et « Demarrer la consultation » en demande 150. Le mot entier vit
   dans l'infobulle et dans `aria-label`.

   ILS SE PARTAGENT LA LARGEUR EN PARTS EGALES (`flex: 1`) : trois boites de
   meme taille se lisent comme un groupe, trois boites dimensionnees sur leur
   dessin se liraient comme trois choses sans rapport.
   ========================================================================== */
/* L ESPACE AU-DESSUS ET EN DESSOUS SE REPONDENT.

   L'Admin : « equilibre l'espace entre le selecteur de seance et le bord bas
   sous les 3 boutons ». Mesure du 16/10/2026 : 14 px au-dessus de la rangee,
   6 px en dessous. La fiche se terminait trop court sous ses propres gestes.

   Les deux valent 10 px maintenant, et elles sont ecrites l'une a cote de
   l'autre pour qu'on les voie se repondre : la marge de la rangee porte le
   haut, le remplissage bas de la fiche porte le bas. */
.viz-rail__actions {
    display: flex;
    gap: var(--e-3);
    /* PAS DE MARGE ICI : le `gap` de la fiche espace deja ses enfants, et une
       marge en plus faisait 14 px d'un cote contre 6 de l'autre. */
    margin-top: 0;
}


/* LE FORMULAIRE N'EST PLUS UNE BOITE DU TOUT : il porte le POST de la
   consultation, et son bouton devient directement un element de la rangee.

   LA CORRECTION D AVANT AVAIT DEPLACE L ECART D UN CRAN. Le 16/10 on lui a
   donne un filet transparent pour que sa PART vaille celle des deux autres,
   et ca marchait : mesure du 17/10 dans un rail ouvert, trois parts de 65 px
   exactement. Mais le bouton VISIBLE vit a l'interieur, et en `border-box` il
   perd justement les deux pixels de ce filet : 63 contre 65 pour ses voisins.
   On avait egalise ce qu'on ne voit pas et desaligne ce qu'on voit.

   `display: contents` ENLEVE LA BOITE AU LIEU DE LA COMPENSER. Le formulaire
   ne se rend plus, son bouton herite du `flex: 1` de la rangee comme les deux
   autres, et les trois mesurent 65. Plus rien a compenser, donc plus rien qui
   puisse se desaccorder a la prochaine largeur.

   CE QUI NE BOUGE PAS : le DOM. La soumission part du meme formulaire, et les
   regles qui visent `.viz-rail__actions__forme .viz-rail__action` (le violet
   de la consultation, son gris au repos) continuent de l'atteindre, comme
   celles en `>` continuent de ne pas l'atteindre. Les deux champs caches de
   `@csrf` et `@method` ne pesent rien : un `input[type=hidden]` ne se rend
   pas, donc il ne prend ni part ni `gap`. */
.viz-rail__actions__forme {
    display: contents;
}

/* LA PEAU D UNE LIGNE DE CLIENT, MAIS PAS SA BOITE.

   L'ADMIN, LE 15/10/2026 : « les 3 boutons dans le visualiseur, dans le cadre
   d'identite, je les veux avec ce style la, comme dans les lignes des
   clients », puis, quand ils ont pris la boite carree de la-bas : « je parlais
   du style design, mais la forme reste la meme, pour prendre la longueur de
   l'espace (taille dynamique, un tiers, et s'il y en a un seul, toute la
   place, ou la moitie si deux) ».

   C EST DONC `flex: 1` QUI PORTE LA REGLE, et il la porte entiere : trois
   boutons prennent un tiers chacun, deux la moitie, un seul toute la largeur.
   Rien a compter dans le gabarit -- la fiche n'en rend pas toujours trois, et
   une largeur ecrite aurait laisse un trou le jour ou il n'y en a qu'un.

   CE QUI VIENT DE LA LIGNE DE CLIENT est la PEAU : fond neutre, filet a la
   teinte, et le halo du survol. Voir les regles de couleur plus bas. */
.viz-rail__action {
    display: flex;
    flex: 1;
    align-items: center;
    justify-content: center;
    position: relative;
    box-sizing: border-box;
    min-width: 0;
    height: 30px;
    padding: 0;
    border: var(--filet-epaisseur) solid var(--filet-neutre-controle);
    border-radius: var(--rayon-controle);
    background: var(--surface-controle);
    color: var(--texte-second);
    cursor: pointer;
    text-decoration: none;
}

/* LE SURVOL EST PLUS BAS, avec la couleur du geste : une seule regle pour les
   trois etats, comme la famille des boutons de ligne. Celle qui etait ici
   posait `filter: brightness`, qui survivait a toute reprise de couleur
   puisqu'elle ne peint pas -- elle eclaircit ce qui est deja peint. */

/* ==========================================================================
   UNE COULEUR PAR GESTE

   L'ADMIN, LE 15/10/2026 : « bouton violet pour la consultation, vert pour
   Commander, bleu pour la note ».

   TROIS SIGNES SE RESSEMBLENT TOUS. Les mots sont partis dans l'infobulle ;
   sans couleur, il restait trois carres identiques a distinguer par leur
   dessin seul, a dix-sept pixels. La couleur est ce qui se lit avant la forme.

   ELLES SONT POSEES SUR LE TEXTE (`color`), et le reste en decoule :
   l'encre du dessin l'herite par `currentColor`, le fond et le filet la
   reprennent en transparence. Une seule valeur a changer par bouton. */
/* DEUX BARREAUX PAR GESTE, ET PAS UN.

   L'ADMIN, LE 17/10/2026 : « violet comme "montrer" pour le fond, vert et
   bleu dans le meme style mais couleur differente ».

   `--vx-geste` GARDE SON ROLE -- le filet, le halo et l'encre du survol le
   lisent -- et deux barreaux s'ajoutent pour le FOND. Ce sont ceux que le
   produit a deja pour chacune de ces familles : rien d'invente. */
.viz-rail__actions__forme .viz-rail__action {
    --vx-geste: 168, 85, 247;
    --vx-fond-a: var(--consultation-deep-rgb);
    --vx-fond-b: 147, 51, 200;
}

.viz-rail__actions > a.viz-rail__action {
    --vx-geste: 52, 211, 153;
    --vx-fond-a: var(--green-state-rgb);
    --vx-fond-b: var(--green-rgb);
}

.viz-rail__actions > button.viz-rail__action {
    --vx-geste: 96, 165, 250;
    --vx-fond-a: var(--blue-deep-rgb);
    --vx-fond-b: var(--blue-rgb);
}

/* ========================================================================
   LE FOND PREND LA COULEUR EN PLEIN, COMME LE BOUTON « COPIER »
   ========================================================================
   L'Admin, le 16/10/2026, en montrant ce bouton-la : « je pense a ce style
   pour les 3 boutons dans le visualiseur, sous l'identite client, icone en
   blanc, un vrai bouton consistant, avec le halo au survol ».

   CE QU ILS ETAIENT, ET POURQUOI C ETAIT JUSTE A L EPOQUE : un fond blanc a
   6 %, un filet a la teinte, l'icone peinte de cette teinte. C'etait la peau
   d'une LIGNE DE CLIENT, qu'il avait lui-meme demandee le 15/10 -- elle
   convenait a des pastilles posees parmi d'autres pastilles. Sous la fiche
   d'identite, ces trois-la sont les trois GESTES de l'ecran, et une peau de
   pastille les faisait lire comme des etiquettes.

   LA RECETTE EST CELLE DE `.bouton-programme--depot` : un degrade plein dans
   la teinte, un filet de la meme teinte, l'encre en BLANC, et un halo qui se
   leve au survol. Chacun garde SA teinte -- `--vx-geste`, posee juste
   au-dessus -- parce que c'est elle qui dit lequel des trois c'est.
   ======================================================================== */
.viz-rail__action {
    /* L ENCRE EST BLANCHE, pas la teinte : sur un fond plein, une icone de la
       couleur de son fond ne se lit plus. */
    color: rgb(var(--white-rgb));
    border-color: rgba(var(--vx-geste, 148, 163, 184), 0.62);

    /* LA RECETTE EST CELLE DE « MONTRER AU CLIENT », mesuree en production le
       17/10/2026 : deux teintes PROFONDES a 0,82, decalees de 135 degres.
       L'ancienne etalait une seule teinte claire de 0,92 a 0,66 et rendait
       une pastille pastel.

       LE REPLI GARDE L ANCIEN GESTE : sans paire declaree, les deux bouts
       retombent sur `--vx-geste`, donc sur un degrade de la meme teinte. Un
       bouton qu'on ajouterait sans y penser reste peint. */
    background: linear-gradient(135deg,
        rgba(var(--vx-fond-a, var(--vx-geste, 148, 163, 184)), 0.82),
        rgba(var(--vx-fond-b, var(--vx-geste, 148, 163, 184)), 0.82));
    /* LE HALO AU REPOS, discret : il dit que le bouton est vivant, comme
       `--depot:enabled` chez son modele. */
    box-shadow: 0 0 14px rgba(var(--vx-geste, 148, 163, 184), 0.18);
    transition: filter 0.18s, box-shadow 0.18s, border-color 0.18s;
}

/* LE MOT DU BOUTON, QUAND IL A LA PLACE DE SE LIRE.

   L'ADMIN, LE 17/10/2026 : « marquer "Commander un programme" en version large
   comme ici, et garder l'icone en version courte de taille ».

   DEUX LARGEURS, ET C EST `flex: 1` QUI LES FAIT, pas un etat a nommer : sur
   ses propres resultats, la consultation et la note ne sont pas rendues, le
   bouton reste seul et prend les 207 px de la rangee ; devant un client ils
   sont trois a 65 px. Mesure du 17/10 dans un rail ouvert de 256 px. La regle
   lit donc la STRUCTURE (`:only-child`), ce qui la rend juste aussi pour deux
   boutons a 103 px, ou le mot ne tiendrait pas davantage.

   LE RAIL REPLIE NE L A PAS A CONNAITRE : a 62 px il ne rend pas la rangee du
   tout, mesure a zero. Il n'y a donc rien a eteindre de ce cote.

   L ECART N EST POSE QUE QUAND LE MOT SE VOIT : un `gap` permanent ecarterait
   l'icone du centre dans les trois boutons etroits, ou elle est seule. */
.viz-rail__action__mot {
    display: none;
    white-space: nowrap;
    font-size: 13px;
    font-weight: 600;
}

.viz-rail__actions > .viz-rail__action:only-child {
    gap: var(--e-2, 8px);
}

.viz-rail__actions > .viz-rail__action:only-child .viz-rail__action__mot {
    display: inline;
}

/* LE HALO SE LEVE AU SURVOL, et le degrade s'eclaircit : les deux ensemble,
   comme `.bouton-programme:hover`.

   IL EST DANS L OMBRE ET PAS DANS LE FILET : un filet qui grossirait
   deplacerait le bouton, et ils vont par trois dans une rangee. */
.viz-rail__action:hover {
    filter: brightness(1.10);
    border-color: rgba(var(--vx-geste, 148, 163, 184), 0.88);
    box-shadow: 0 0 24px rgba(var(--vx-geste, 148, 163, 184), 0.40);
}

/* ========================================================================
   DESACTIVE : GRIS, SANS HALO, ET LE CURSEUR LE DIT
   ========================================================================
   L'Admin : « peut-etre griser un peu "Lancer la consultation" quand
   desactive ? ». Oui, et pour la meme raison que son modele gate son halo sur
   `:enabled` : un geste qu'on ne peut pas faire ne doit pas briller comme un
   geste qu'on peut faire.

   `:disabled` POUR LE BOUTON, `[aria-disabled]` POUR LE LIEN : un `<a>` ne se
   desactive pas, il se marque. Les deux formes vivent dans cette rangee -- la
   consultation et la note sont des boutons, la commande est un lien.
   ======================================================================== */
.viz-rail__action:disabled,
.viz-rail__action[aria-disabled="true"] {
    background: rgba(var(--white-rgb), 0.06);
    border-color: rgba(var(--white-rgb), 0.14);
    color: rgba(var(--white-rgb), 0.38);
    box-shadow: none;
    filter: none;
    cursor: not-allowed;
}

/* ET IL NE REPOND PAS AU SURVOL : sans cette regle il s'eclaircirait sous la
   souris en pretendant repondre a un clic qu'il refuse. */
.viz-rail__action:disabled:hover,
.viz-rail__action[aria-disabled="true"]:hover {
    filter: none;
    border-color: rgba(var(--white-rgb), 0.14);
    box-shadow: none;
}

/* ========================================================================
   LA CONSULTATION : SOURDE AU REPOS, ALLUMEE QUAND ELLE TOURNE
   ========================================================================
   L'ADMIN, LE 17/10/2026 : « inverse actif/inactif, couleur vive quand mode
   consultation actif ».

   CE QUI ETAIT EN PLACE LE MATIN MEME, ET POURQUOI. Il avait demande que ce
   bouton ressemble a « Montrer au client » : plein au repos, sourd une fois
   allume. C'est la grammaire d'un bouton qui PROPOSE un geste puis le RETIRE
   -- « Montrer » est vif, « Ne plus montrer » est sourd.

   ET IL LA RETIRE ICI, POUR UNE RAISON QUI TIENT. Ce bouton ne commande pas
   seulement, il INDIQUE : une consultation qui tourne est un etat du produit
   que le praticien doit voir d'un coup d'oeil depuis le rail, sans lire. Un
   etat se signale en s'allumant, pas en s'eteignant. « Montrer au client »,
   lui, ne dit rien d'autre que son geste, et garde donc sa grammaire.

   LES DEUX PEAUX SONT ECHANGEES, PAS REECRITES. Les valeurs sont celles
   mesurees sur « Montrer au client » en production le 17/10/2026 :
     - le fond sourd, `rgba(107, 48, 163, 0.34)` a plat, filet
       `rgba(168, 85, 247, 0.42)`, passe au REPOS ;
     - le plein revient a l'etat EN COURS, et ce n'est meme plus une regle :
       c'est le degrade partage de `.viz-rail__action`, celui du vert et du
       bleu, avec le halo que `.viz-rail__action.is-active` leve plus bas.

   LE DESACTIVE RESTE GRIS. La regle de repos exclut nommement `:disabled` et
   `[aria-disabled]` : elle pese plus lourd qu'eux, et sans ces deux gardes
   elle aurait peint en violet sourd un geste qu'on ne peut pas faire.
   ======================================================================== */
.viz-rail__actions__forme .viz-rail__action:not(.is-active):not(:disabled):not([aria-disabled="true"]) {
    background: rgba(107, 48, 163, 0.34);
    border-color: rgba(168, 85, 247, 0.42);
    color: rgba(var(--white-rgb), 0.88);
    box-shadow: none;
}

/* AU SURVOL, LE SOURD S ECLAIRCIT SANS S ALLUMER : le halo reste a zero,
   sinon le repos survole se confondrait avec la consultation en cours. */
.viz-rail__actions__forme .viz-rail__action:not(.is-active):not(:disabled):not([aria-disabled="true"]):hover {
    background: rgba(107, 48, 163, 0.46);
    border-color: rgba(168, 85, 247, 0.62);
    color: rgb(var(--white-rgb));
    box-shadow: none;
    filter: none;
}

/* ALLUME, LE BOUTON GARDE SON HALO LEVE : c'est le meme geste, en cours.

   POUR LA CONSULTATION, le halo s'ajoute depuis le 17/10/2026 au degrade
   plein que la regle de repos ne lui retire plus -- les deux ensemble disent
   « ca tourne ». Pour les deux autres boutons, qui menent a une page, il dit
   « tu y es » et le fond ne bouge pas. */
.viz-rail__action.is-active {
    border-color: rgba(var(--vx-geste, 148, 163, 184), 0.95);
    box-shadow: 0 0 24px rgba(var(--vx-geste, 148, 163, 184), 0.46);
}

/* EN THEME CLAIR, L ENCRE DES PASTILLES RESTE BLANCHE.

   LA REGLE DE BASE PLUS HAUT LE DIT DEJA -- « L ENCRE EST BLANCHE, pas la
   teinte » -- mais elle l'ecrit `rgb(var(--white-rgb))`, et ce jeton est de
   l'encre : sous `data-theme="clair"` il vaut 17, 24, 39. Mesure du
   29/10/2026 sur la page reelle : les trois icones etaient tracees en
   #111827 sur leurs degrades pleins, qui, eux, sont ecrits en dur et ne
   s'inversent pas. Le mot du bouton (`__mot`) herite de la meme encre et
   avait le meme defaut.

   LA CORRECTION SE POSE SUR L ELEMENT, pas sur le jeton, qui est lu partout.

   LE DESACTIVE EST EXCLU, et c'est pour ca que les deux gardes sont la :
   son fond est a 0,06, du blanc ne s'y lirait pas.

   LA CONSULTATION AU REPOS NE PASSE PAS PAR ICI, et c'est voulu : son
   selecteur pese plus lourd que celui-ci, et son fond sourd (0,34) ne
   porterait pas de blanc. Elle garde l'encre qu'elle avait. */
html[data-theme="clair"] .viz-rail__action:not(:disabled):not([aria-disabled="true"]) {
    color: #fff;
}

/* `is-active` NE DIT PAS LA MEME CHOSE SUR LES TROIS BOUTONS, et c'est voulu.

   Les deux qui menent a une page l'emploient pour « tu y es ». La
   consultation l'emploie pour « une seance tourne ». Le halo convient aux
   deux lectures : dans un cas il souligne l'onglet ouvert, dans l'autre il
   signale l'etat en cours. */

.viz-rail__action:focus-visible {
    outline: 2px solid var(--filet-accent, #22D3EE);
    outline-offset: 1px;
}

/* LE NOMBRE DE PROGRAMMES EN ATTENTE, POSE SUR SON BOUTON. Il ne pousse rien :
   hors du flux, dans le coin, et absent tant qu'il vaut zero. */
.viz-rail__action__compte {
    position: absolute;
    top: -5px;
    right: -5px;
    min-width: 15px;
    height: 15px;
    padding: 0 3px;
    box-sizing: border-box;
    border-radius: 999px;
    background: #22D3EE;
    color: #042F2E;
    font-size: 10px;
    font-weight: 700;
    line-height: 15px;
    text-align: center;
}

.viz-rail__action__compte[hidden] {
    display: none;
}

/* CE BLOC A ETE REECRIT LE 29/10/2026 PUIS REMIS EN L ETAT, le meme jour.

   J'avais pose les teintes du theme sombre a plat sur ces trois boutons, en
   lisant « les pastilles colorees, c est sombre, remettre la meme couleur
   que celle en theme sombre » comme si ca les visait. L'Admin parlait des
   VOYANTS -- « les pastilles "partage" et le statut du site » -- et il avait
   dit du bouton, deux messages plus haut : « met l ecriture en blanc, c est
   tout ».

   CE QUI RESTE DE CE PASSAGE : l'encre blanche, plus haut dans ce fichier,
   et rien d'autre. Le fond, le filet et le halo sont ceux d'avant.

   CE QUE LA MESURE DISAIT, et qui ne disparait pas pour autant : sur le vert
   pastel rendu en clair, du blanc donne un contraste de 2,0. C'est faible.
   C'est l'etat demande, et il est tenu. */

/* EN THEME CLAIR, LES MEMES TEINTES EN PLUS SOMBRE : les trois pastels du
   theme sombre passent sous le seuil de lecture sur un fond blanc. */
/* CES REGLES ETAIENT MORTES : ELLES CITAIENT LE MOT DE L EXPERT.
   -------------------------------------------------------------------------
   Corrigees le 24/10/2026. La coque Online ecrit `data-theme="clair"` et
   `"sombre"` (`public/js/theme.js`) ; la coque Expert ecrit `"light"` et
   `"dark"`. Cette feuille est servie DIRECTEMENT par le Online, sans passer
   par `RessourcesExpertController::recadrer()` qui traduit le mot pour les
   feuilles de `resources/expert/` -- `light` n'y a donc jamais rien vise.

   LE DEFAUT NE SE VOYAIT PAS COMME UN DEFAUT : la regle existe, elle est
   lisible, son commentaire explique ce qu'elle corrige, et le theme clair
   gardait simplement les valeurs du sombre. C'est le troisieme cas de cette
   famille en deux jours, apres les encres du graphe et le voile de la modale.

   `OnlineScriptsKnowTheOnlineThemeTest` balaie desormais les FEUILLES autant
   que les scripts, pour que le quatrieme tombe tout seul. */
html[data-theme="clair"] .viz-rail__actions__forme .viz-rail__action {
    --vx-geste: 109, 40, 217;
}

html[data-theme="clair"] .viz-rail__actions > a.viz-rail__action {
    --vx-geste: 4, 120, 87;
}

html[data-theme="clair"] .viz-rail__actions > button.viz-rail__action {
    --vx-geste: 29, 78, 216;
}

/* ========================================================================
   SUR CLAIR, LE HALO N EST PLUS UNE LUEUR : C EST UNE SALISSURE
   ========================================================================
   L'ADMIN, LE 29/10/2026, capture a l'appui : « quelque chose ne va pas en
   theme clair avec le rendu de ces boutons », puis, en designant la cause :
   « il y a un cote "sombre", l'ombre ? la bordure ? ».

   LES DEUX, ET VOICI POURQUOI. Ces boutons portent un halo de leur propre
   teinte, `0 0 14px rgba(--vx-geste, .18)`, pose pour dire « ce bouton est
   vivant ». En sombre `--vx-geste` vaut une teinte CLAIRE -- 52, 211, 153
   pour le vert -- donc le halo eclaire autour du bouton et il rayonne. Les
   trois declarations ci-dessus retournent `--vx-geste` vers une teinte
   PROFONDE pour le clair -- 4, 120, 87 -- ce qui est juste pour un filet et
   une icone, et faux pour un halo : quatorze pixels de vert fonce a 18 %
   etales sur une carte blanche ne sont plus une lueur, ce sont des cernes.

   LE FOND, LUI, N A PAS ETE RETOURNE : c'est le meme degrade vert sature des
   deux cotes. Mesure du 29/10 sur la page reelle : fond identique, bord de
   (52,211,153) a (4,120,87), halo de meme, encre du blanc au (17,24,39). Un
   aplat pense pour un fond sombre, entoure d'un halo sombre, sur une carte
   blanche.

   ET L ENCRE NOIRE N EST PAS LA FAUTE, contrairement a ce qu'on croit en
   regardant : elle mesure 9,1 sur ce vert, et c'est le BLANC qui y tomberait
   a 2,0. Le defaut est de registre, pas de lisibilite -- raison de plus pour
   ne toucher ni au fond ni a l'encre.

   CE QU ON RETIRE, ET CE QU ON GARDE. Le halo part sur les trois etats --
   repos, survol, `is-active` -- parce qu'il est faux sur clair dans les
   trois. Ce qui portait l'etat reste entier : le filet va de .34 a .88 au
   survol et a .95 en cours, et `filter: brightness` repond toujours. On ne
   perd donc aucun signal, on perd l'ombre qui les salissait.

   ET LE FILET DESCEND DE .62 A .34 : a .62 un vert profond dessine un anneau
   dur autour d'un aplat clair. A .34 il delimite sans cerner. Les etats, eux,
   gardent leurs valeurs d'origine -- c'est le REPOS qui etait trop appuye,
   pas la reponse au geste.

   CE QUI N EST PAS VISE : la consultation. `.viz-rail__actions__forme
   .viz-rail__action:not(…)` pese (0,5,0) contre (0,2,1) ici, et elle a deja
   son propre traitement a plat, sans halo. Elle reste ou elle est.
   ======================================================================== */
html[data-theme="clair"] .viz-rail__action,
html[data-theme="clair"] .viz-rail__action:hover,
html[data-theme="clair"] .viz-rail__action.is-active {
    box-shadow: none;
}

html[data-theme="clair"] .viz-rail__action {
    border-color: rgba(var(--vx-geste, 148, 163, 184), 0.34);
}

/* ========================================================================
   DANS LE RAIL, LES TROIS VERTS N EN FONT QU UN
   ========================================================================
   L'ADMIN, LE 29/10/2026 : « les points colores sont sombres aussi, pas les
   memes ». Les deux mots comptent, et le second nomme le vrai defaut.

   TROIS PASTILLES VERTES SE SUIVENT ICI : le voyant « Partage », les points
   des planches, et celle de « Tous les services fonctionnent ». Les deux
   premieres lisent `--share-line-rgb`, que le theme retourne. La troisieme
   lit `--share-bright-rgb`. Mesure du 29/10 : en clair, (21,128,61) pour les
   deux premieres et (34,197,94) pour la derniere ; en sombre, (52,211,153)
   contre (34,197,94). Deux verts pour ce qui se lit comme la meme idee, a
   huit lignes d'ecart a l'ecran, et dans les DEUX themes.

   ON ALIGNE DANS LE RAIL, ET NULLE PART AILLEURS, et c'est tout l'objet de
   ce selecteur. `--share-bright-rgb` est STABLE DANS LES DEUX THEMES par
   decision ecrite -- voir `modele.css`, « la teinte globale habille
   l'interface, la donnee garde la sienne » -- et il sert de `--pastille-rgb`
   a tous les etats « ok » du tableau de bord et de l'administration. Le
   retourner globalement repeindrait tout cela pour un defaut qui ne se voit
   que dans une colonne de 256 px. Premiere version de cette correction, le
   matin meme : l'alias avait ete pose sur le jeton, donc partout. Corrige en
   relisant le commentaire qui disait deja pourquoi ne pas le faire.

   PAR LA VARIABLE, POSEE SUR L ELEMENT : la pastille lit
   `rgb(var(--share-bright-rgb))` pour son fond et sa lueur, et les etats
   « attention » et « panne » lisent d'autres jetons. Redefinir la variable
   ici ne touche donc que le vert, et laisse les deux autres etats dire ce
   qu'ils disent.
   ======================================================================== */
.viz-rail .etat-systeme__pastille,
.viz-rail .etat-systeme__geste {
    --share-bright-rgb: var(--share-line-rgb);
}

/* ========================================================================
   ET EN THEME CLAIR, CE VERT EST CELUI DU THEME SOMBRE
   ========================================================================
   L'ADMIN, LE 29/10/2026 : « les pastilles colorees, c'est sombre, remettre
   la meme couleur que celle en theme sombre », en designant « les pastilles
   "partage" et le statut du site ».

   LA REGLE AU-DESSUS A ALIGNE LES TROIS VERTS ENTRE EUX ; elle les a alignes
   sur `--share-line-rgb`, que le theme retourne : (52, 211, 153) en sombre,
   (21, 128, 61) en clair. Les trois disaient donc la meme chose, en vert
   bouteille, et c'est ce vert-la qui se lit comme « sombre » sur une carte
   blanche.

   CE SONT DES VOYANTS, PAS DU TEXTE : huit pixels pleins qui disent « c'est
   partage », « ca tourne ». Ils n'ont pas a suivre l'encre, ils ont a se
   reconnaitre d'un theme a l'autre, et c'est la teinte du sombre que l'oeil
   a apprise.

   LES TROIS PORTEURS SONT NOMMES, et la variable est posee SUR EUX :
   `--share-line-rgb` est lu bien au-dela de ce rail -- cases a cocher,
   filets, fonds voiles -- et le retourner ici pour tout le rail repeindrait
   ce qu'on n'a pas regarde. Voir `modele.css` : la teinte globale habille
   l'interface, la donnee garde la sienne.

   MESURE DU 29/10, page reelle, theme clair : voyant « Partage », les trois
   points de planche et la pastille d'etat passent de (21, 128, 61) a
   (52, 211, 153). */
html[data-theme="clair"] .viz-rail .partage-durable__voyant,
html[data-theme="clair"] .viz-rail .viz-rail__planche,
html[data-theme="clair"] .viz-rail .etat-systeme__pastille,
html[data-theme="clair"] .viz-rail .etat-systeme__geste {
    --share-line-rgb: 52, 211, 153;
    --share-bright-rgb: 52, 211, 153;
}

/*
 * LES INTERRUPTEURS DE SECTION NE SORTENT QU EN SELECTION DE PARTAGE.
 *
 * L'ADMIN, LE 19/10/2026 : « les boutons "Partager" sont présents même quand
 * on n'est pas en sélection de partage depuis le rail ».
 *
 * CE QU ILS SONT : un interrupteur par section partageable, posé dans la page
 * à côté de ce qu'il accorde. Ils ne servent qu'à une chose, composer ce que
 * le client gardera, et cette composition a son endroit : le dépli « Partage
 * avec le client » du rail, qui porte l'état, le compte et le bouton de mise
 * à jour. Hors de ce moment, ils ajoutent une case à cocher sur chaque bloc
 * d'une page qu'on est en train de LIRE.
 *
 * `:has()` PLUTOT QU UNE CLASSE POSEE PAR UN SCRIPT : le dépli est un
 * `<details>`, son ouverture est déjà portée par l'attribut `open`, et c'est
 * la seule source. Une classe sur `body` ferait un second état à tenir
 * d'accord avec le premier.
 *
 * CEUX DU RAIL (`--onglet`) NE SONT PAS TOUCHES : ils vivent DANS le dépli,
 * et c'est eux qu'on vient y chercher.
 */
.viz-rail:not(:has(.viz-rail__depli--partage[open])) ~ * .partage-interrupteur--section,
body:not(:has(.viz-rail__depli--partage[open])) .partage-interrupteur--section {
    display: none;
}


/* -------------------------------------------------------------------------
   L INTERRUPTEUR D UNE PARTIE DE LA SYNTHESE, DANS SON EN-TETE
   -------------------------------------------------------------------------
   L'Admin, le 06/11/2026 : « Oui pour les sections ».

   OU IL EST POSE, ET POURQUOI LA. Chacune des six cartes numerotees de la
   synthese recoit son interrupteur DANS SON EN-TETE, a cote du titre qu'il
   commande. C'est la seule place qui dise sans un mot sur quoi il porte : au
   pied de la carte, il se lirait comme un geste sur le dernier item -- ce qui
   est justement le geste d'a cote, celui des cartes de la partie 6.

   `margin-left: auto` LE POUSSE AU BOUT DE LA RANGEE. Les en-tetes des
   parties 2 a 6 sont des `flex` qui portent deja le texte a gauche et le
   compteur a droite ; l'interrupteur se range apres le compteur. L'en-tete de
   la partie 1 est un titre, d'ou le `display: flex` qui lui est rendu ici et
   nulle part ailleurs.

   IL SUIT LE MEME VOYANT QUE SES FRERES : `partage-interrupteur--section` est
   porte par le partial, donc il disparait hors du depli « Partage » du rail,
   et il n'y a rien a redire ici.
   ------------------------------------------------------------------------- */
/* L ENFANT DIRECT, ET C EST CE QUI FAIT GAGNER LA REGLE. `theme.css` pose une
   remise a zero `.vx-bloc *` -- meme poids qu'une classe seule -- et il est
   servi APRES cette feuille sur les ecrans X : `.vx-partage-partie` seul
   perdait sa marge a poids egal, en silence. Mesure du 06/11/2026 :
   `margin-left` calcule a 0, l'interrupteur de la partie 1 colle au titre au
   lieu d'aller au bout de la rangee. */
.expert-col-head > .vx-partage-partie,
.expert-section-heading > .vx-partage-partie {
    display: inline-flex;
    align-items: center;
    gap: var(--e-6, 8px);
    flex: 0 0 auto;
}

/* LA LIGNE DU TITRE EST RANGEE DANS `vue-expert-bloc.css`, et pas ici.

   Cette feuille est la coquille du visualiseur ; elle est servie AVANT celles
   de l'Expert, et ses selecteurs y perdent a poids egal. Mesure du
   06/11/2026 : `flex-wrap` passait, `align-items` non, et `display: contents`
   non plus -- la moitie d'une regle appliquee, ce qui est pire que rien. Les
   regles qui doivent gagner contre l'Expert vivent dans sa feuille a lui. */


/* LE TITRE DE LA PARTIE 1 DEVIENT UNE RANGEE, pour accueillir le sien. Les
   cinq autres en-tetes en sont deja une. Il n'a pas de compteur, donc c'est
   `space-between` qui ecarte ses deux enfants -- et non une marge, que la
   remise a zero ci-dessus emporterait. */
.expert-consultation-section .expert-section-heading:has(.vx-partage-partie) {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--e-6, 8px);
}
