/* =========================================================================
   LE DOCUMENT EST SOMBRE, ET LE NAVIGATEUR DOIT LE SAVOIR.

   `color-scheme: dark` fait dessiner au navigateur ses propres commandes en
   sombre : icone du selecteur de date, menu d'un <select>, barres de
   defilement. Sans elle il suppose un fond clair, quelle que soit la couleur
   qu'on peint derriere.

   `jetons.css` la pose deja a la racine, mais cinq gabarits d'authentification
   chargent cette feuille SANS jetons.css : connexion, mot de passe oublie,
   reinitialisation, inscription praticien, inscription sur invitation. Ils
   restaient donc en clair, et il fallait rattraper chaque commande une par
   une. On la declare ici aussi : la feuille se suffit a elle-meme.
   ========================================================================= */
:root {
    color-scheme: dark;
}

body {
    margin: 0;
    font-family: Arial, sans-serif;
    background: rgb(var(--slate-rgb));
    color: var(--ink-200);
}

/* LES CONTROLES N HERITENT PAS DE LA POLICE : la feuille du navigateur leur en
   donne une, et elle gagne parce que rien ne la contredit. Mesure du
   06/09/2026 : en changeant la pile du corps, 322 elements suivaient et onze
   restaient -- cinq champs et six boutons, ce qu'on regarde le plus sur une
   page de travail.

   `font-family` SEUL, PAS `font` : le raccourci emporterait aussi la taille, et
   un champ n'a pas la taille du texte courant. Ce serait un changement visuel
   deguise en correctif. */
button,
input,
select,
textarea {
    font-family: inherit;
}

/* Layout */
.lk-wrapper {
    display: flex;
    
    gap: 10px;
}

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

/* Sidebar */
.lk-logo {
    font-size: 20px;
    margin-bottom: 30px;
    font-weight: bold;
}

/* Header */
.page-header--with-action {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    margin-bottom: 20px;
    flex-wrap: wrap;
}

/* `page-title` rejoint le canonique : trois gabarits la posaient sans regle,
   leur titre gardait donc la marge par defaut du navigateur. */
.page-title,
.page-header__title {
    margin: 0;
}

@media (max-width: 640px) {
    .page-header--with-action {
        align-items: stretch;
    }

    .page-header--with-action .btn {
        width: 100%;
        text-align: center;
    }
}

/* Content */
.lk-content {
    flex: 1;
    padding: 30px;
    box-sizing: border-box;
}

.course-title {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
}

.course-organ-info {
    color: rgba(var(--white-rgb), 0.7);
    margin-bottom: 20px;
    font-size: 14px;
}

/* Видеофон */
.lk-video-bg {
    position: fixed;
    inset: 0;
    z-index: 0;
    overflow: hidden;
    pointer-events: none;
    background: rgb(var(--slate-rgb));
}

.lk-video-bg::before {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(to bottom, rgba(var(--slate-rgb), 0.2), rgba(var(--slate-rgb), 0.1)),
        rgba(var(--slate-rgb), 0.18);
    z-index: 1;
}

.lk-video-bg__media {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
    opacity: 1;
    transform: scale(1.015);
}

.lk-video-bg__media.is-loaded {
    opacity: 1;
}

/* Typography */
h1 {
    margin-top: 0;
    margin-bottom: 20px;
    font-size: 32px;
    /* Halo de 20 et 40 px retire : sur un titre de 21 px ce n\'est pas du
       relief, c\'est du flou. Le titre se detache par sa taille. */
}

h2 {
    margin-top: 0;
    margin-bottom: 18px;
    font-size: 24px;
}

h3 {
    margin-top: 0;
    margin-bottom: 14px;
    font-size: 20px;
}

p {
    line-height: 1.5;
}

.memorial {
    text-align: center;
    font-size: 14px;
    color: var(--texte-discret);
    font-style: italic;
    opacity: 0.85;
    line-height: 1.5;
}

/* Statuses */
.status {
    display: inline-block;
    padding: 4px 10px;
    border-radius: 999px;
    font-size: 12px;
    font-weight: bold;
}

.status.waiting {
    background: rgba(var(--etat-vigilance-rgb), 0.2);
    /* Meme raison : l'ambre du theme, qui s'assombrit en clair, pas la teinte
       d'etat qui reste vive. */
    color: var(--amber-mid);
}

.status.active {
    background: rgba(var(--share-bright-rgb), 0.2);
    /* UNE ENCRE N EST PAS UN APLAT, et je l'ai appris deux fois. `--green-soft`
       rendait 4,16 sur une carte claire ; j'ai d'abord mis la teinte d'etat
       stable, `--etat-tenu`, et le rapport est TOMBE a 1,89 -- un vert vif sur
       du blanc est pire qu'un vert profond. Les teintes d'etat sont faites
       pour peindre un APLAT, pas pour ecrire dessus.
       `--couleur-succes-doux` est le jeton prevu pour ca : menthe pale sur
       sombre, vert profond sur clair. Il bascule, et il tient des deux cotes. */
    color: var(--couleur-succes-doux);
}

.status.expired,
.status.finished,
.status.disabled {
    background: rgba(var(--slate-light-rgb), 0.16);
    color: var(--ink-300);
}

/* Buttons */
.btn {
    display: inline-block;
    align-items: center;
    /*
     * UN SEUL MODELE DE BOITE, quel que soit l'element qui porte la classe.
     *
     * Sans cette ligne, chaque element gardait celui de son navigateur : un
     * `<button>` en `border-box`, un `<label>` ou un `<a>` en `content-box`.
     * Deux boutons voisins, l'un fait d'un libelle et l'autre d'un bouton
     * vrai, se retrouvaient decales de quatre pixels en hauteur, et personne
     * ne voyait pourquoi. C'etait le cas sous la photo du profil.
     */
    box-sizing: border-box;
    /*
     * 10 px de rembourrage horizontal, quelle que soit la longueur : le texte
     * venait coller les bords des que l'intitule depassait deux mots. Mesure
     * sur « Enregistrer les modifications » : 176 px de texte dans 196 px de
     * bouton, soit 10 px de chaque cote. Ces valeurs venaient d'intitules
     * russes, souvent moitie plus courts qu'en francais.
     */
    padding: 9px 16px;
    /* 12 px comme `.portal-btn` et comme les 100 autres emplois du produit.
       Ce bouton-ci etait le seul a 8 px, et c'est le plus repandu de
       l'interface : pose a cote d'un bouton du portail, l'ecart se voyait. */
    border-radius: 12px;
    background: var(--couleur-accent);
    color: var(--white);
    font-size: 14px;
    /*
     * UN INTERLIGNE DECLARE, pour que la hauteur ne dependeplus des caracteres
     * du libelle. Sans lui, `normal` laisse la police decider : un intitule
     * commencant par un emoji (« 📊 Resultats des tests ») mesurait 37 px la
     * ou le meme bouton sans emoji en faisait 35, parce que le glyphe est plus
     * haut que les lettres. Deux boutons voisins, memes reglages, deux
     * hauteurs.
     */
    line-height: 17px;
    text-decoration: none;
    border: none;
    cursor: pointer;
    transition: .18s;
}

.btn-primary {
    background: var(--ink-600);
    color: white;
}

.btn-secondary {
    background: #3a4e58;
}

.btn-secondary:hover {
    background: #497188;
}

/* Le bouton le plus employe du produit virait au VERT sous le pointeur.
   La valeur posee etait `#25eb8869`, un vert a 41 % d'opacite, sur un bouton
   bleu : 67 degres d'ecart de teinte, mesures sur la roue chromatique. Le vert
   annonce une confirmation, si bien qu'un simple survol laissait croire a une
   action qui n'etait pas celle du bouton, et l'opacite laissait en plus
   transparaitre le fond.
   Le voisin `.btn-danger` faisait deja les choses correctement : rouge, puis
   rouge plus fonce. On aligne. */

/* =========================================================================
   LA TAILLE DU BOUTON AU DOIGT.

   `.btn` mesure 35 px de haut : 9 px de rembourrage, 17 px de ligne, 9 px. Ca
   se vise tres bien a la souris, et mal au doigt. Apple comme Google donnent
   44 px pour minimum, et l'ecart n'est pas theorique : on rate, on recommence,
   et sur un ecran dense on touche le bouton d'a cote.

   La revue du 18 aout 2026 a compte les cibles sous 40 px, page par page, a
   768 et a 375 px : 17 sur le tableau de bord, 14 sur le journal des
   connexions, 12 sur le profil, 11 sur les notifications, et ainsi de suite
   sur presque chaque ecran. Ce n'etaient pas des defauts separes : c'etait
   cette regle-ci, appliquee 327 fois dans les gabarits.

   ON NE TOUCHE QU'AU MOBILE. En dessous de 768 px, la hauteur s'ouvre a 44 px
   et le rembourrage horizontal ne bouge pas : le bouton grandit dans le seul
   sens ou le doigt en a besoin, et les rangees de boutons gardent leur rythme.
   Au-dela, la souris n'a besoin de rien et le dessin reste celui qu'on a
   valide.
   ========================================================================= */
@media (max-width: 768px) {
    .btn {
        min-height: 44px;
        padding-top: 12px;
        padding-bottom: 12px;
    }
}

.btn:hover {
    background: var(--couleur-accent-fort);
}

.btn-danger {
    background: #dc2626;

    /* L ENCRE MANQUAIT, et le bouton heritait de celle du bouton generique :
       en theme clair elle vaut un bronze, pose sur ce rouge plein il rend 1,40
       (mesure, sept pages). Un aplat rouge FIXE demande une encre FIXE : elle
       ne bascule pas plus que lui. Le blanc y rend 4,92. */
    color: #ffffff;
}

.btn-danger:hover {
    background: #b91c1c;
}

/* Upload button and field */
.therapy-upload-form {
    display: inline-flex;
    align-items: stretch;
    margin: 0;
}

.therapy-upload-combo {
    display: inline-flex;
    align-items: center;
    gap: 12px;
    min-width: 320px;
    height: 46px;
    padding: 0 14px;
    border-radius: 12px 0 0 12px;
    border: 2px solid rgba(22,163,74,0.60);
    border-right: none;
    background: rgba(var(--slate-rgb), 0.72);
    backdrop-filter: blur(10px);
    box-sizing: border-box;
    cursor: pointer;
}

.file-picker__input {
    display: none;
}

.therapy-upload-combo__pick {
    display: inline-flex;
    align-items: center;
    height: 32px;
    padding: 0 14px;
    border-radius: 10px;
    background: rgba(var(--blue-rgb), 0.16);
    color: var(--blue-pale);
    font-weight: 600;
    white-space: nowrap;
    transition: .18s;
}

.therapy-upload-combo:hover .therapy-upload-combo__pick {
    background: rgba(var(--blue-rgb), 0.24);
    color: var(--blue-mist);
}

.therapy-upload-combo__name {
    color: var(--texte-discret);
    font-size: 14px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 220px;
}

.therapy-upload-combo__submit {
    appearance: none;
    border: none;
    height: 46px;
    padding: 0 18px;
    border-radius: 0 12px 12px 0;
    background: linear-gradient(135deg, #16a34a, var(--couleur-succes));
    color: #f0fdf4;
    font-size: 16px;
    cursor: pointer;
    box-shadow: 0 8px 22px rgba(var(--share-bright-rgb), 0.22);
    transition: .18s;
}

.therapy-upload-combo__submit:hover {
    background: linear-gradient(135deg, #15803d, #16a34a);
    box-shadow: 0 10px 26px rgba(var(--share-bright-rgb), 0.28);
}

@media (max-width: 640px) {
    .therapy-upload-form {
        display: flex;
        flex-direction: column;
        width: 100%;
    }

    .therapy-upload-combo {
        width: 100%;
        min-width: 0;
        border-radius: 12px;
        border-right: 1px solid rgba(var(--white-rgb), 0.10);
    }

    .therapy-upload-combo__name {
        max-width: none;
        flex: 1;
    }

    .therapy-upload-combo__submit {
        width: 100%;
        border-radius: 12px;
        margin-top: 8px;
    }
}

/* Forms */
label {
    display: block;
    margin-bottom: 8px;
    font-weight: 600;
}

/*
 * UN LIBELLE QUI EST UN BOUTON N'EST PAS UN LIBELLE DE CHAMP.
 *
 * La regle ci-dessus donne 8 px sous chaque `<label>`, ce qui est juste
 * au-dessus d'un champ de saisie. Mais un champ de fichier se cache derriere
 * son intitule pour avoir l'allure d'un bouton, et cet intitule heritait donc
 * d'une marge que son voisin n'avait pas : dans une rangee centree, les deux
 * boutons se retrouvaient decales de quatre pixels sans raison visible.
 * C'etait le cas sous la photo du profil.
 */
label.btn {
    margin-bottom: 0;
    font-weight: inherit;
}

/* `search` est dans la liste : sans lui, un champ de recherche tombe sur le
   rendu par defaut du navigateur — bord en creux, fond gris, coins droits —
   au milieu d une page qui a sa grammaire. C est arrive sur la carte. */
input[type="text"],
input[type="search"],
input[type="email"],
input[type="password"],
input[type="tel"],
input[type="url"],
input[type="number"],
input[type="date"],
input[type="number"],
select,
textarea {
    width: 100%;
    padding: 12px 14px;
    margin-bottom: 16px;
    border-radius: 10px;
    border: 1px solid rgba(var(--white-rgb), 0.12);
    background: rgba(var(--slate-rgb), 0.85);
    color: var(--ink-200);
    box-sizing: border-box;
    font-size: 15px;
}

textarea {
    min-height: 110px;
    resize: vertical;
}

input::placeholder,
textarea::placeholder {
    color: var(--texte-discret);
}

.checkbox-row {
    display: flex;
    align-items: center;
    gap: 12px;
    cursor: pointer;
    font-weight: 600;
    margin-bottom: 0;
}

.checkbox-row input[type="checkbox"] {
    width: 18px;
    height: 18px;
    margin: 0;
    accent-color: var(--couleur-accent);
    flex: 0 0 auto;
}
/* Alerts / messages */
.alert {
    padding: 12px 16px;
    border-radius: 10px;
    margin-bottom: 18px;
}

.alert-success {
    background: rgba(var(--share-bright-rgb), 0.6);
    color: var(--couleur-succes-doux);
    border: 1px solid rgba(var(--share-bright-rgb), 0.28);
}

.alert-error {
    background: rgba(var(--red-mid-rgb), 0.6);
    color: var(--couleur-danger-doux);
    border: 1px solid rgba(var(--red-rgb), 0.28);
}

.error-text {
    color: var(--red-soft);
    font-size: 14px;
    margin: -10px 0 14px;
}

/* Tables */
table {
    width: 100%;
    border-collapse: collapse;
}

th,
td {
    padding: 12px;
    border-bottom: 1px solid rgba(var(--white-rgb), 0.08);
    text-align: left;
    vertical-align: top;
}

th {
    color: var(--texte-discret);
}

/* ===== Diagnostics form ===== */
.diag-card {
    max-width: 1180px;
    margin: 0 auto;
}

.diag-row {
    display: grid;
    grid-template-columns:
        fit-content(190px)
        fit-content(170px)
        minmax(180px, 220px)
        minmax(260px, 1fr)
        max-content
        max-content;
    gap: 16px;
    align-items: end;
}

.diag-item {
    min-width: 0;
}

.diag-item label {
    display: block;
    margin-bottom: 8px;
    font-weight: 600;
    color: var(--white);
}

.diag-item-demo {
    min-width: 160px;
}
/* `.diag-item-submit` etait declare deux fois. Ce premier bloc
   etait entierement reecrit l.590 : a selecteur et poids egaux,
   la derniere regle ecrite gagne, celle-ci n'imposait donc rien. */

.diag-item-submit .btn {
    font-weight: 700;
}
.diag-item-demo .btn,
.diag-item-back .btn {
    height: 42px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    white-space: nowrap;
    margin: 0;
}

.diag-item-demo .btn {
    width: auto;
    padding: 0 18px;
    background: linear-gradient(135deg, #6d28d9, #8b5cf6);
    color: var(--encre-sur-fond-fixe);
}

.diag-item-demo .btn:hover {
    background: linear-gradient(135deg, var(--consultation-deep), #7c3aed);
}

.diag-spacer {
    visibility: hidden;
}

.diag-static-value {
    height: 42px;
    display: flex;
    align-items: center;
    padding: 0 14px;
    border-radius: 10px;
    background: rgba(var(--slate-rgb), 0.55);
    border: 1px solid rgba(var(--white-rgb), 0.08);
    color: rgba(var(--white-rgb), 0.92);
    font-size: 15px;
    cursor: default;
    user-select: none;
    white-space: nowrap;
}

.diag-item-weight input {
    width: 100%;
    height: 42px;
    margin: 0;
}

.diag-item-submit {
    min-width: 260px;
}

.diag-item-submit .btn {
    width: 100%;
    height: 42px;
    display: flex;
    align-items: center;
    justify-content: center;
    margin: 0;
    white-space: nowrap;
}

.diag-item-back {
    justify-self: start;
}

.diag-item-back .btn {
    width: auto;
    min-width: 0;
    height: 42px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    margin: 0;
    white-space: nowrap;
    padding: 0 22px;
}

@media (max-width: 1100px) {
    .diag-row {
        grid-template-columns: 1fr 1fr;
    }

    .diag-item-submit,
    .diag-item-back {
        grid-column: auto;
    }
}

@media (max-width: 700px) {
    .diag-row {
        grid-template-columns: 1fr;
    }

    .diag-item-submit .btn,
    .diag-item-back .btn {
        width: 100%;
    }
}

/* ===== Diagnostics fullscreen ===== */

body.diagnostic-player-page {
    background: var(--black) !important;
    overflow: hidden;
}

body.diagnostic-player-page .lk-video-bg,
body.diagnostic-player-page .lk-bottom-nav {
    display: none !important;
}

body.diagnostic-player-page .diagnostic-app {
    position: fixed;
    inset: 0;
    width: 100vw;
    height: 100vh;
    background: var(--black);
    overflow: hidden;
}

body.diagnostic-player-page .diagnostic-screen {
    position: absolute;
    inset: 0 0 0 0;
    background: var(--black);
    overflow: hidden;
}

body.diagnostic-player-page #diagCanvas {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    display: block;
    z-index: 1;
    background: var(--black);
    transition: filter .22s ease, opacity .22s ease;
}

/*
 * PENDANT LA COMPARAISON, L'IMAGE DE TRAVAIL RECULE.
 *
 * Les deux vignettes sont posees SUR l'image que l'on vient de regler, qui
 * reste nette derriere elles. Trois images du meme sujet a l'ecran, dont la
 * plus grande n'est pas celle qu'on doit regarder : l'oeil ne sait plus ou
 * se poser, au moment precis ou la comparaison doit se lire d'un coup.
 *
 * Le flou est LEGER et accompagne d'un assombrissement. Un flou seul, plus
 * fort, aurait donne une bouillie de couleurs qui attire encore l'oeil ;
 * ici l'image reste reconnaissable et cesse simplement de rivaliser.
 *
 * `transform: translateZ(0)` force une couche a part : sans elle, certains
 * navigateurs refont le filtre a chaque image dessinee sur la toile, et
 * l'ecran saccade pendant la transition.
 */
body.diagnostic-demo-page.is-comparing #diagCanvas {
    filter: blur(7px) brightness(0.62) saturate(0.85);
    transform: translateZ(0);
}

body.diagnostic-player-page .diagnostic-topbar {
    position: absolute;
    top: 16px;
    left: 16px;
    right: 16px;
    z-index: 20;
    display: flex;
    gap: 10px;
    flex-wrap: wrap;
    align-items: center;
}

body.diagnostic-player-page .diagnostic-pill {
    padding: 8px 12px;
    border-radius: 12px;
    background: rgba(var(--black-rgb), 0.45);
    color: rgba(var(--white-rgb), 0.95);
    border: 1px solid rgba(var(--white-rgb), 0.14);
    backdrop-filter: blur(10px);
    font-size: 14px;
}

/* C EST L OVERLAY QU IL FALLAIT PEINDRE, pas le conteneur. Trois couches
   recouvraient mon fond : `.diagnostic-screen` en noir, un `<canvas>` noir, et
   un voile `rgba(noir,0.82)`. J'ai d'abord cru a une bataille de specificite,
   puis j'ai liste les surfaces empilees : la reponse etait dans le DOM, pas
   dans la cascade.

   ET LES TROIS N ONT PAS LE MEME ROLE. `.diagnostic-overlay` porte le titre et
   le texte : c'est l'ECRAN D ACCUEIL, et il disparait des que la procedure
   commence. Le `<canvas>` et son ecran, eux, doivent rester NOIRS pendant le
   test, sinon un fond bleute fausserait la perception des couleurs, qui est
   tout l'objet de la mesure.

   L'accueil prend donc le fond du produit, la procedure garde son noir, et
   personne n'a besoin de savoir dans quel etat on se trouve : c'est le meme
   element qui apparait et disparait. */
/* Le fond passe au-dessus du conteneur noir mais SOUS l'ecran d'accueil, qui
   porte le texte et les boutons. */
body.diagnostic-player-page .diagnostic-overlay {
    position: absolute;
    inset: 0;
    z-index: 10002;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    gap: 16px;
    background: var(--surface-sombre);
    color: var(--white);
    text-align: center;
    padding: 24px;
}

/* --- D5. Le titre perd son halo gris -------------------------------------
   -------------------------------------------------------------------------
   `text-shadow: rgb(51,51,51) 0 0 20px, rgb(85,85,85) 0 0 40px`, deux halos
   GRIS. Ils avaient un sens sur du noir absolu, ou ils detachaient le texte
   sans rien colorer. Sur le fond du produit, qui est bleute, un halo gris fait
   une tache sale autour de chaque lettre.
   ------------------------------------------------------------------------- */
body.diagnostic-player-page .diagnostic-overlay__title {
    margin: 0;
    font-size: 36px;
    text-shadow: 0 2px 18px rgba(0, 0, 0, 0.55);
}

body.diagnostic-player-page .diagnostic-overlay__text {
    max-width: 860px;
    line-height: 1.55;
    color: rgba(var(--white-rgb), 0.9);
}

body.diagnostic-player-page .diagnostic-controls-bar {
    position: absolute;
    left: 50%;
    bottom: calc(env(safe-area-inset-bottom, 0px) + 16px);
    transform: translateX(-50%);
    z-index: 40;

    display: inline-flex;
    justify-content: flex-start;
    align-items: stretch;
    gap: 16px;

    width: max-content;
    max-width: calc(100vw - 32px);
    padding: 14px 16px;
    box-sizing: border-box;

    border-radius: 18px;
    background: rgba(var(--black-rgb), 0.5);
    border: 1px solid rgba(var(--white-rgb), 0.14);
    backdrop-filter: blur(14px);

    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    transition:
        opacity 0.28s ease,
        transform 0.28s ease,
        visibility 0.28s ease;
}

body.diagnostic-player-page .diagnostic-controls-bar.is-hidden {
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transform: translateX(-50%) translateY(16px);
}

body.diagnostic-player-page .diagnostic-controls-bar.is-immediate {
    transition: none;
}

body.diagnostic-player-page .diagnostic-controls-grid {
    display: grid;
    grid-template-columns: repeat(6, minmax(62px, 62px));
    grid-template-rows: repeat(2, auto);

    /*
     * Colonne par colonne, et non ligne par ligne.
     *
     * Le DOM liste les commandes par paires (R+ R- J+ J- ...). Rempli par
     * ligne, cela donnerait « R+ R- J+ J- V+ V- » sur la premiere rangee : les
     * deux boutons d'une couleur se retrouveraient cote a cote au lieu d'etre
     * l'un sous l'autre. Ici, chaque couleur tient sa colonne, a toute largeur.
     */
    grid-auto-flow: column;

    gap: 10px;
    align-items: stretch;
    flex: 0 0 auto;
}

body.diagnostic-player-page .diagnostic-btn {
    width: 62px;
    min-width: 62px;
    padding: 10px 0;
}

/* La colonne d'actions REMPLIT la part que la barre lui donne, elle ne se
   fixe plus une largeur a elle. La proportion se decide une seule fois, sur
   la barre : voir « UNE SEULE PROPORTION » plus haut. */
body.diagnostic-player-page .diagnostic-actions {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: repeat(2, 1fr);
    gap: 10px;
    align-items: stretch;
    min-width: 0;
}

body.diagnostic-player-page .diagnostic-actions .btn {
    width: 100%;
    min-width: 0;
    min-height: 100%;
    box-sizing: border-box;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

body.diagnostic-player-page .diagnostic-saving-overlay {
    position: absolute;
    inset: 0;
    z-index: 35;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px;

    background: rgba(var(--black-rgb), 0.26);
    backdrop-filter: blur(8px);

    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    transition:
        opacity .18s ease,
        visibility .18s ease;
}

body.diagnostic-player-page .diagnostic-saving-overlay.is-hidden {
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
}

body.diagnostic-player-page .diagnostic-saving-overlay__box {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 16px;
    min-width: min(420px, calc(100vw - 40px));
    max-width: 520px;
    padding: 24px 28px;
    border-radius: 18px;

    background: rgba(var(--black-rgb), 0.58);
    border: 1px solid rgba(var(--white-rgb), 0.14);
    box-shadow: 0 20px 50px rgba(var(--black-rgb), 0.35);

    text-align: center;
}

body.diagnostic-player-page .diagnostic-saving-overlay__text {
    color: rgba(var(--white-rgb), 0.96);
    font-size: 22px;
    font-weight: 600;
    line-height: 1.35;
    text-wrap: balance;
}

body.diagnostic-player-page .diagnostic-saving-overlay__spinner {
    width: 44px;
    height: 44px;
    border-radius: 999px;
    border: 3px solid rgba(var(--white-rgb), 0.24);
    border-top-color: var(--couleur-succes);
    animation: diagnosticSpinner 0.9s linear infinite;
}

@keyframes diagnosticSpinner {
    to {
        transform: rotate(360deg);
    }
}

@media (min-width: 901px) {
    /* Le placement vient de `grid-auto-flow: column`, sur la regle de base :
       douze zones nommees et autant de `nth-child` ne faisaient que redire, sur
       une seule largeur d'ecran, ce qui vaut desormais partout. */
    body.diagnostic-player-page .diagnostic-controls-grid {
        grid-template-columns: repeat(6, 1fr);
    }
}

@media (max-width: 900px) {
    body.diagnostic-player-page .diagnostic-screen {
        inset: 0 0 0 0;
    }

    body.diagnostic-player-page .diagnostic-controls-bar {
        /*
         * En ligne, pas en colonne. Faire descendre les actions sous la palette
         * coutait une rangee entiere, ce qui manque en paysage. Elles gardent
         * donc leur colonne a droite, comme sur grand ecran.
         */
        flex-direction: row;
        align-items: stretch;
        width: calc(100vw - 20px);
        max-width: calc(100vw - 20px);
    }

    body.diagnostic-player-page .diagnostic-controls-grid {
        /* Six colonnes ici aussi : quatre donneraient trois rangees et les
           deux boutons d'une couleur cesseraient d'etre voisins. */
        grid-template-columns: repeat(6, minmax(0, 1fr));
    }

    body.diagnostic-player-page .diagnostic-btn {
        width: 100%;
        min-width: 0;
    }

    body.diagnostic-player-page .diagnostic-actions {
        /* Colonne unique, comme sur grand ecran : voir `--desktop`. */
        display: grid;
        grid-template-columns: minmax(66px, 92px);
        grid-template-rows: repeat(2, 1fr);
    }

    body.diagnostic-player-page .diagnostic-actions .btn {
        width: 100%;
        min-width: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;        
    }
}

@media (max-width: 640px) {
    body.diagnostic-player-page .diagnostic-topbar {
        top: 10px;
        left: 10px;
        right: 10px;
        gap: 8px;
    }

    body.diagnostic-player-page .diagnostic-pill {
        font-size: 13px;
        padding: 7px 10px;
    }

    body.diagnostic-player-page .diagnostic-overlay__title {
        font-size: 28px;
    }

    body.diagnostic-player-page .diagnostic-controls-bar {
        left: 50%;
        bottom: calc(env(safe-area-inset-bottom, 0px) + 10px);
        width: calc(100vw - 20px);
        max-width: calc(100vw - 20px);
        padding: 10px;
        border-radius: 16px;
    }

    body.diagnostic-player-page .diagnostic-controls-grid {
        /* Six colonnes ici aussi : quatre donneraient trois rangees et les
           deux boutons d'une couleur cesseraient d'etre voisins. */
        grid-template-columns: repeat(6, minmax(0, 1fr));
    }
    
    body.diagnostic-player-page .diagnostic-saving-overlay__box {
        min-width: 0;
        width: 100%;
        padding: 20px 18px;
        border-radius: 16px;
        gap: 14px;
    }

    body.diagnostic-player-page .diagnostic-saving-overlay__text {
        font-size: 18px;
    }

    body.diagnostic-player-page .diagnostic-saving-overlay__spinner {
        width: 40px;
        height: 40px;
    }    
}

/* ===== Diagnostic color buttons ===== */

body.diagnostic-player-page .diagnostic-controls-grid .diagnostic-btn {
    color: var(--white);
    border: 1px solid rgba(var(--white-rgb), 0.18);
    box-shadow: inset 0 1px 0 rgba(var(--white-rgb), 0.18),
        0 8px 20px rgba(var(--black-rgb), 0.25);
    font-weight: 700;
}

body.diagnostic-player-page .diagnostic-controls-grid .diagnostic-btn:disabled {
    /*
     * Ni voile ni desaturation : pendant une demonstration, ces couleurs sont
     * precisement ce qu'on demande de regarder, et certaines s'allument pour
     * montrer ce qui agit. Le curseur suffit a dire qu'on ne peut pas cliquer.
     */
    cursor: not-allowed;
}

body.diagnostic-player-page .diagnostic-controls-grid .diagnostic-btn:not(:disabled):hover {
    transform: translateY(-1px);
    filter: brightness(1.06);
}

/*
 * L'appui, pose par le script sur `pointerdown` / `touchstart`.
 *
 * `:active` ne suffit pas : au tactile il arrive tard, dure peu, et certains
 * navigateurs ne l'appliquent pas du tout. Comme le script sait deja quand
 * l'appui commence et finit (il gere le maintien), autant qu'il porte l'etat.
 * Le rendu est alors le meme au doigt et a la souris, et il dure aussi
 * longtemps que le doigt reste pose.
 */
body.diagnostic-player-page .diagnostic-controls-grid .diagnostic-btn:not(:disabled).is-appuye,
body.diagnostic-player-page .diagnostic-mobile-channel:not(:disabled).is-appuye,
body.diagnostic-player-page .diagnostic-mobile-action:not(:disabled).is-appuye {
    /* Instantane : un appui bref est plus court que la transition, qui ne
       sert donc qu'au relachement. */
    transition: none;

    transform: scale(0.96);
    filter: brightness(1.25) saturate(1.15);

    /* Le contour est la partie qui compte au tactile: il deborde du doigt,
       donc il reste visible quand le bouton ne l'est plus. */
    box-shadow:
        0 0 0 3px rgba(var(--white-rgb), 0.92),
        0 0 20px rgba(var(--white-rgb), 0.4),
        inset 0 3px 12px rgba(var(--black-rgb), 0.4);
    z-index: 2;
}

/* Le carre gris que pose Android par-dessus nos propres etats. */
body.diagnostic-player-page .diagnostic-btn,
body.diagnostic-player-page .diagnostic-mobile-channel,
body.diagnostic-player-page .diagnostic-mobile-action {
    -webkit-tap-highlight-color: transparent;
    touch-action: manipulation;
}

/* R */
.diagnostic-btn:nth-child(1),
.diagnostic-btn:nth-child(2) {
    background: linear-gradient(135deg, var(--red-mid), var(--couleur-danger));
}

/* Y */
.diagnostic-btn:nth-child(3),
.diagnostic-btn:nth-child(4) {
    background: linear-gradient(135deg, #a16207, var(--amber-bright));
    color: #111;
}

/* G */
.diagnostic-btn:nth-child(5),
.diagnostic-btn:nth-child(6) {
    background: linear-gradient(135deg, #166534, var(--couleur-succes));
}

/* C */
.diagnostic-btn:nth-child(7),
.diagnostic-btn:nth-child(8) {
    background: linear-gradient(135deg, #0f766e, rgb(var(--portal-accent-rgb)));
    color: #082f49;
}

/* B */
.diagnostic-btn:nth-child(9),
.diagnostic-btn:nth-child(10) {
    background: linear-gradient(135deg, #1d4ed8, rgb(var(--blue-rgb)));
}

/* M */
.diagnostic-btn:nth-child(11),
.diagnostic-btn:nth-child(12) {
    background: linear-gradient(135deg, #a21caf, #e879f9);
}

/* ===== Diagnostic mobile controls ===== */

body.diagnostic-player-page .diagnostic-mobile-controls {
    display: none;
}

body.diagnostic-player-page .diagnostic-mobile-channel,
body.diagnostic-player-page .diagnostic-mobile-action,
body.diagnostic-player-page .diagnostic-btn {
    user-select: none;
    -webkit-user-select: none;
    -webkit-touch-callout: none;
}

body.diagnostic-player-page .diagnostic-mobile-channel,
body.diagnostic-player-page .diagnostic-mobile-action {
    touch-action: manipulation;
}

body.diagnostic-player-page .diagnostic-mobile-channels,
body.diagnostic-player-page .diagnostic-mobile-actions {
    display: grid;
    gap: 8px;
}

body.diagnostic-player-page .diagnostic-mobile-channels {
    grid-template-columns: repeat(6, minmax(0, 1fr));
}

body.diagnostic-player-page .diagnostic-mobile-actions {
    grid-template-columns: repeat(3, minmax(0, 1fr));
}

body.diagnostic-player-page .diagnostic-mobile-channel,
body.diagnostic-player-page .diagnostic-mobile-action {
    min-height: 48px;
    padding: 10px 8px;
    border-radius: 12px;
    font-weight: 700;
    box-sizing: border-box;
    border: 1px solid rgba(var(--white-rgb), 0.18);
    box-shadow: inset 0 1px 0 rgba(var(--white-rgb), 0.18),
        0 8px 20px rgba(var(--black-rgb), 0.25);
}

body.diagnostic-player-page .diagnostic-mobile-channel:active,
body.diagnostic-player-page .diagnostic-mobile-action:active {
    transform: scale(0.98);
}

body.diagnostic-player-page .diagnostic-mobile-channel[data-channel="R"] {
    background: linear-gradient(135deg, var(--red-mid), var(--couleur-danger));
    color: var(--white);
}
body.diagnostic-player-page .diagnostic-mobile-channel[data-channel="Y"] {
    background: linear-gradient(135deg, #a16207, var(--amber-bright));
    color: #111;
}
body.diagnostic-player-page .diagnostic-mobile-channel[data-channel="G"] {
    background: linear-gradient(135deg, #166534, var(--couleur-succes));
    color: var(--encre-sur-fond-fixe);
}
body.diagnostic-player-page .diagnostic-mobile-channel[data-channel="C"] {
    background: linear-gradient(135deg, #0f766e, rgb(var(--portal-accent-rgb)));
    color: #082f49;
}
body.diagnostic-player-page .diagnostic-mobile-channel[data-channel="B"] {
    background: linear-gradient(135deg, #1d4ed8, rgb(var(--blue-rgb)));
    color: var(--encre-sur-fond-fixe);
}
body.diagnostic-player-page .diagnostic-mobile-channel[data-channel="M"] {
    background: linear-gradient(135deg, #a21caf, #e879f9);
    color: var(--encre-sur-fond-fixe);
}

body.diagnostic-player-page .diagnostic-mobile-channel.is-active {
    box-shadow: 0 0 0 2px rgba(var(--white-rgb), 0.95),
        0 0 24px rgba(var(--white-rgb), 0.16),
        inset 0 1px 0 rgba(var(--white-rgb), 0.24);
}

body.diagnostic-player-page .diagnostic-mobile-controls[data-active-channel="R"] .diagnostic-mobile-action--less,
body.diagnostic-player-page .diagnostic-mobile-controls[data-active-channel="R"] .diagnostic-mobile-action--more {
    background: linear-gradient(135deg, var(--red-mid), var(--couleur-danger));
    color: var(--white);
}

body.diagnostic-player-page .diagnostic-mobile-controls[data-active-channel="Y"] .diagnostic-mobile-action--less,
body.diagnostic-player-page .diagnostic-mobile-controls[data-active-channel="Y"] .diagnostic-mobile-action--more {
    background: linear-gradient(135deg, #a16207, var(--amber-bright));
    color: #111;
}

body.diagnostic-player-page .diagnostic-mobile-controls[data-active-channel="G"] .diagnostic-mobile-action--less,
body.diagnostic-player-page .diagnostic-mobile-controls[data-active-channel="G"] .diagnostic-mobile-action--more {
    background: linear-gradient(135deg, #166534, var(--couleur-succes));
    color: var(--encre-sur-fond-fixe);
}

body.diagnostic-player-page .diagnostic-mobile-controls[data-active-channel="C"] .diagnostic-mobile-action--less,
body.diagnostic-player-page .diagnostic-mobile-controls[data-active-channel="C"] .diagnostic-mobile-action--more {
    background: linear-gradient(135deg, #0f766e, rgb(var(--portal-accent-rgb)));
    color: #082f49;
}

body.diagnostic-player-page .diagnostic-mobile-controls[data-active-channel="B"] .diagnostic-mobile-action--less,
body.diagnostic-player-page .diagnostic-mobile-controls[data-active-channel="B"] .diagnostic-mobile-action--more {
    background: linear-gradient(135deg, #1d4ed8, rgb(var(--blue-rgb)));
    color: var(--encre-sur-fond-fixe);
}

body.diagnostic-player-page .diagnostic-mobile-controls[data-active-channel="M"] .diagnostic-mobile-action--less,
body.diagnostic-player-page .diagnostic-mobile-controls[data-active-channel="M"] .diagnostic-mobile-action--more {
    background: linear-gradient(135deg, #a21caf, #e879f9);
    color: var(--encre-sur-fond-fixe);
}
body.diagnostic-player-page .diagnostic-mobile-action--done {
    background: linear-gradient(135deg, var(--blue-deep), rgb(var(--blue-light-rgb)));
    color: var(--white);
}

body.diagnostic-player-page .diagnostic-mobile-action:disabled,
body.diagnostic-player-page .diagnostic-mobile-channel:disabled {
    /* Meme raison que pour la palette : voir plus haut. */
    cursor: not-allowed;
}

/* mobile layout switch */
@media (max-width: 900px), (pointer: coarse) {
    body.diagnostic-player-page {
        overflow: hidden;
    }

    body.diagnostic-player-page .diagnostic-app,
    body.diagnostic-player-page .diagnostic-screen {
        height: var(--diag-visible-height, 100svh);
        min-height: var(--diag-visible-height, 100svh);
    }

    body.diagnostic-player-page .diagnostic-controls-bar {
        position: fixed;
        left: 10px;
        right: 10px;
        top: auto;
        bottom: calc(var(--diag-browser-bottom, 0px) + 10px);
        transform: none;
        width: auto;
        max-width: none;
        padding: 8px;
        gap: 8px;
        border-radius: 16px;
        z-index: 80;
        box-sizing: border-box;
    }

    /*
     * La grille des douze boutons RESTE, meme en ecran restreint : deux
     * boutons par couleur se lisent d'un coup d'oeil, la ou un selecteur de
     * canal impose de choisir avant de pousser.
     */
    body.diagnostic-player-page .diagnostic-controls-grid--desktop {
        display: grid;
        grid-template-columns: repeat(6, 1fr);
        grid-template-rows: repeat(2, auto);
        gap: 6px;

        /* Elle occupe ce que la colonne d'actions laisse. */
        flex: 1 1 auto;
        min-width: 0;
        width: auto;
    }

    body.diagnostic-player-page .diagnostic-controls-grid--desktop .diagnostic-btn {
        width: auto;
        min-width: 0;
        min-height: 42px;
        padding: 8px 2px;
        font-size: 14px;
    }

    /* Ce qui ne rentre pas a cote passe dessous, sur toute la largeur. */
    body.diagnostic-player-page .diagnostic-actions--desktop {
        display: grid;

        /*
         * Une colonne, deux rangees : Quitter en haut, Valider en bas. La
         * largeur reste modeste pour laisser respirer les douze couleurs, mais
         * assez large pour rester lisible et cliquable.
         */
        grid-template-columns: minmax(66px, 92px);
        grid-template-rows: repeat(2, 1fr);

        gap: 6px;
        flex: 0 0 auto;
        width: auto;
        margin-top: 0;
    }

    /*
     * `min-width: 0` N'EST PAS DECORATIF, il repare une tablette large.
     *
     * La regle de base impose `width: 120px; min-width: 120px` aux deux
     * actions. Le bloc « moins de 900px » l'annule ; celui-ci, qui vaut aussi
     * pour un POINTEUR GROSSIER quelle que soit la largeur, posait `width`
     * sans toucher au plancher. Une tablette de plus de 900px recevait donc
     * les deux regles a la fois : une colonne de 92px, et dedans deux boutons
     * qui refusent de descendre sous 120. Ils depassaient de 28px, hors de la
     * barre et hors de l'ecran, et « Quitter » comme « Valider » se
     * retrouvaient coupes dans le bord droit.
     *
     * Mesure a 1006px de large : depassement de 28px avant, nul apres.
     */
    body.diagnostic-player-page .diagnostic-actions--desktop .btn {
        width: 100%;
        min-width: 0;
        min-height: 42px;
        text-align: center;
    }

    /* L'ancienne disposition par canal n'a plus lieu d'etre. */
    body.diagnostic-player-page .diagnostic-mobile-controls {
        display: none !important;
    }

    body.diagnostic-player-page .diagnostic-controls-bar {
        /* Voir plus haut : la colonne d'actions reste a droite. */
        flex-direction: row;
        align-items: stretch;
    }

    /* Les deux regles qui ancraient ici la carte de demonstration par le bas
       ont ete retirees : la regle de base « ===== Diagnostics demo ===== »,
       ecrite plus bas dans ce fichier, pose `top` et `bottom: auto` sans media
       query et les annulait toutes les deux. Elles avaient l'air vivantes et
       ne servaient a rien ; c'est plus bas que la carte se positionne. */
}

@media (max-width: 640px) {
    body.diagnostic-player-page .diagnostic-mobile-channel,
    body.diagnostic-player-page .diagnostic-mobile-action {
        min-height: 46px;
        font-size: 15px;
        padding: 10px 6px;
    }
}

/* ===== Diagnostics demo ===== */

body.diagnostic-demo-page .diagnostic-topbar {
    justify-content: space-between;
}

body.diagnostic-demo-page .diagnostic-demo-card {
    position: absolute;
    left: 24px;
    top: 84px;

    /*
     * Indispensable : cette regle est ecrite APRES les media queries qui
     * ancrent la carte par le bas. Sans `bottom: auto`, leur `bottom` survit,
     * cohabite avec ce `top`, et la carte se retrouve etiree entre les deux
     * au lieu de faire la hauteur de son texte.
     */
    bottom: auto;

    z-index: 32;

    width: min(460px, calc(100vw - 48px));
    padding: 18px 18px 16px;
    border-radius: 18px;

    background: rgba(var(--slate-rgb), 0.72);
    border: 1px solid rgba(var(--white-rgb), 0.12);
    box-shadow: 0 18px 50px rgba(var(--black-rgb), 0.36),
        inset 0 1px 0 rgba(var(--white-rgb), 0.08);
    backdrop-filter: blur(16px);

    opacity: 1;
    visibility: visible;
    transform: translateY(0);
    transition:
        opacity .18s ease,
        transform .18s ease,
        visibility .18s ease;
}

@media (max-width: 900px) {
    body.diagnostic-demo-page .diagnostic-demo-card.is-compare-mode {
        left: 10px;
        right: 10px;

        /* En haut, comme a toutes les autres etapes. Ancree en bas, la carte
           se collait au sol et repoussait les images vers le haut de l'ecran. */
        top: 64px;
        bottom: auto;

        width: auto;
        padding: 12px 14px 14px;
        border-radius: 16px;
    }

    body.diagnostic-demo-page .diagnostic-demo-card.is-compare-mode .diagnostic-demo-card__step,
    body.diagnostic-demo-page .diagnostic-demo-card.is-compare-mode .diagnostic-demo-card__title {
        display: none;
    }

    body.diagnostic-demo-page .diagnostic-demo-card.is-compare-mode .diagnostic-demo-card__text {
        display: block;
        margin: 0 0 12px;
        font-size: 13px;
        line-height: 1.45;
    }

    body.diagnostic-demo-page .diagnostic-demo-card.is-compare-mode .diagnostic-demo-card__timer {
        display: none !important;
    }

    body.diagnostic-demo-page .diagnostic-demo-card.is-compare-mode .diagnostic-demo-card__actions {
        margin-top: 0;
    }

    body.diagnostic-demo-page .diagnostic-demo-compare {
        left: 10px;
        right: 10px;
        top: calc(var(--diag-carte-bas, 64px) + 14px);
        bottom: 148px;
        gap: 12px;
        grid-template-columns: auto;
        justify-content: center;
        align-content: center;
        overflow: hidden;
    }

    body.diagnostic-demo-page .diagnostic-demo-compare__item {
        padding: 10px;
        border-radius: 14px;
    }
}

body.diagnostic-demo-page .diagnostic-demo-card.is-hidden {
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transform: translateY(-8px);
}

body.diagnostic-demo-page .diagnostic-demo-card__step {
    font-size: 12px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: rgba(var(--white-rgb), 0.54);
    margin-bottom: 10px;
}

body.diagnostic-demo-page .diagnostic-demo-card__title {
    font-size: 22px;
    line-height: 1.2;
    font-weight: 700;
    color: var(--ink-50);
    margin-bottom: 10px;
}

body.diagnostic-demo-page .diagnostic-demo-card__text {
    font-size: 15px;
    line-height: 1.55;
    color: rgba(var(--white-rgb), 0.9);
}

body.diagnostic-demo-page .diagnostic-demo-card__actions {
    display: flex;
    gap: 10px;
    margin-top: 16px;
    flex-wrap: wrap;
}

body.diagnostic-demo-page .diagnostic-demo-card__actions .btn {
    width: auto;
    min-width: 120px;
}

/* La classe etait posee sur « Entrainement libre » mais rien ne la traduisait
   ici : le bouton restait visible des la premiere etape. */
body.diagnostic-demo-page .diagnostic-demo-card__actions .btn.is-hidden {
    display: none;
}

body.diagnostic-demo-page .diagnostic-demo-card__timer {
    margin-top: 14px;

    /* `flex` et non `inline-flex` : la boite garde sa largeur quand le
       decompte passe de deux chiffres a un. Avec `inline-flex` elle suivait
       le texte et retrecissait en cours de route, ce qui donnait
       l'impression que la taille des caracteres changeait. */
    display: flex;
    width: fit-content;

    /* Indispensable : c'est ce qui donne la meme largeur a tous les chiffres,
       et donc a l'espace-chiffre qui complete le decompte cote script. */
    font-variant-numeric: tabular-nums;

    align-items: center;
    gap: 8px;
    min-height: 34px;
    padding: 8px 12px;

    /* 12px comme les pastilles et les boutons. La pilule complete jurait
       avec le reste de la carte, qui est a 18px. */
    border-radius: 12px;
    background: rgba(99, 102, 241, 0.18);
    border: 1px solid rgba(165, 180, 252, 0.26);
    color: var(--ink-200);
    font-size: 14px;
    font-weight: 700;
    box-shadow: inset 0 1px 0 rgba(var(--white-rgb), 0.08),
        0 8px 18px rgba(var(--black-rgb), 0.18);
}

body.diagnostic-demo-page .diagnostic-demo-card__timer.is-hidden {
    display: none;
}

body.diagnostic-demo-page .diagnostic-demo-card__timer::before {
    content: "";
    width: 10px;
    height: 10px;
    border-radius: 999px;
    background: var(--violet-soft);
    box-shadow: 0 0 12px rgba(var(--violet-soft-rgb), 0.9);
    animation: demoTimerPulse 1s ease-in-out infinite;
}

@keyframes demoTimerPulse {
    0% {
        opacity: 1;
        transform: scale(1);
    }
    50% {
        opacity: 0.45;
        transform: scale(0.8);
    }
    100% {
        opacity: 1;
        transform: scale(1);
    }
}

body.diagnostic-demo-page .diagnostic-btn.is-demo-target,
body.diagnostic-demo-page .diagnostic-mobile-channel.is-demo-target,
body.diagnostic-demo-page .diagnostic-mobile-action.is-demo-target {
    position: relative;
    box-shadow: 0 0 0 3px rgba(var(--white-rgb), 0.96),
        0 0 22px rgba(var(--white-rgb), 0.28),
        0 0 42px rgba(139, 92, 246, 0.48),
        0 0 70px rgba(139, 92, 246, 0.20);
    transform: translateY(-1px) scale(1.04);
    filter: brightness(1.08) saturate(1.1);
    z-index: 2;
}

body.diagnostic-demo-page .diagnostic-btn.is-demo-target::after,
body.diagnostic-demo-page .diagnostic-mobile-channel.is-demo-target::after,
body.diagnostic-demo-page .diagnostic-mobile-action.is-demo-target::after {
    content: "";
    position: absolute;
    inset: -7px;
    border-radius: 16px;
    border: 2px solid rgba(216, 180, 254, 0.95);
    animation: demoPulse 1.1s ease-in-out infinite;
    pointer-events: none;
}

body.diagnostic-demo-page .diagnostic-btn.is-demo-soft,
body.diagnostic-demo-page .diagnostic-mobile-channel.is-demo-soft,
body.diagnostic-demo-page .diagnostic-mobile-action.is-demo-soft {
    box-shadow: 0 0 0 1px rgba(var(--white-rgb), 0.68),
        0 0 20px rgba(var(--white-rgb), 0.16);
    filter: brightness(1.03);
}

body.diagnostic-demo-page .diagnostic-btn.is-demo-pressed,
body.diagnostic-demo-page .diagnostic-mobile-channel.is-demo-pressed,
body.diagnostic-demo-page .diagnostic-mobile-action.is-demo-pressed {
    transform: scale(0.93);
    box-shadow: 0 0 0 3px rgba(var(--white-rgb), 0.98),
        0 0 28px rgba(var(--white-rgb), 0.36),
        0 0 56px rgba(139, 92, 246, 0.58),
        inset 0 0 28px rgba(var(--white-rgb), 0.24);
    filter: brightness(1.2) saturate(1.22);
    z-index: 3;
}

body.diagnostic-demo-page .diagnostic-pill.is-demo-accent {
    background: rgba(var(--consultation-deep-rgb), 0.32);
    border-color: rgba(var(--violet-soft-rgb), 0.28);
    color: var(--violet-pale);
}

/*
 * La comparaison descend sous la carte d'explication.
 *
 * La carte est ancree en haut a gauche avec un z-index superieur, et rien ne
 * la deplacait en mode comparaison sur ordinateur : elle couvrait donc
 * exactement les images qu'elle commente. La version mobile le gerait deja,
 * pas le bureau.
 *
 * `top` place la comparaison sous la carte plutot que de deplacer la carte :
 * l'explication reste ou l'oeil l'a apprise pendant quatorze etapes.
 */
body.diagnostic-demo-page .diagnostic-demo-compare {
    position: absolute;
    left: 24px;
    right: 24px;
    /*
     * Sous la carte, avec un ecart franc. `46vh` tombait pile ou le texte se
     * termine, et les deux se touchaient ; la hauteur de la carte change en
     * plus a chaque etape, donc aucun pourcentage ne peut convenir.
     */
    top: calc(var(--diag-carte-bas, 46vh) + 20px);

    /* Il n'y a rien sous le comparatif a cette etape : cette hauteur revient
       aux images plutot que de rester vide. La barre de manche, elle, reprend
       sa place en mode libre (voir `is-free-compare`). */
    bottom: 48px;
    z-index: 31;
    align-content: center;

    display: grid;

    /*
     * `auto` et non `1fr` : chaque cadre prend la largeur de son image au lieu
     * de s'etaler sur la moitie de l'ecran. Le comparatif garde ses
     * proportions et se centre, plutot que d'imposer deux grands cadres a
     * moitie vides.
     */
    grid-template-columns: auto auto;
    justify-content: center;
    gap: 18px;

    /* Meme raison, en hauteur : sans cela la grille etire les cadres. */
    align-items: center;

    opacity: 1;
    visibility: visible;
    transition: opacity .18s ease, visibility .18s ease;
}

body.diagnostic-demo-page .diagnostic-demo-compare.is-hidden {
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
}

body.diagnostic-demo-page .diagnostic-demo-compare__item {
    padding: 14px;
    border-radius: 18px;

    /*
     * Le fond translucide et son flou : c'est ce qui pose les cadres sur
     * l'image sans les en couper. Je les avais remplaces par un fond opaque
     * pour homogeneiser la teinte ; le remede etait pire que le mal, l'ecran
     * y perdait tout son relief.
     */
    background: rgba(var(--slate-rgb), 0.72);
    backdrop-filter: blur(16px);
    border: 1px solid rgba(var(--white-rgb), 0.12);
    box-shadow: 0 18px 50px rgba(var(--black-rgb), 0.36),
        inset 0 1px 0 rgba(var(--white-rgb), 0.08);
}

body.diagnostic-demo-page .diagnostic-demo-compare__label {
    margin-bottom: 10px;
    font-size: 14px;
    font-weight: 700;
    color: #e9d5ff;
}

body.diagnostic-demo-page .diagnostic-demo-compare__item {
    /* Colonne : la legende en haut, la toile dessous, et `min-height: 0`
       pour que la toile puisse effectivement retrecir. Sans lui, un enfant
       de grille refuse de passer sous sa taille naturelle et deborde. */
    display: flex;
    flex-direction: column;
    min-height: 0;
}

body.diagnostic-demo-page .diagnostic-demo-compare__item canvas {
    /*
     * Plus d'`aspect-ratio` : il imposait du 16/9 a des images qui n'en sont
     * pas, et surtout il fixait une hauteur que le cadre n'avait pas. La toile
     * porte deja ses proportions dans ses attributs `width` et `height` ; il
     * suffit de la borner dans les deux sens et de la laisser tenir.
     */
    /*
     * La toile fait la taille de l'image, et rien de plus : pas d'etirement,
     * donc pas de bande noire a combler. Le script lui donne deja les bonnes
     * proportions dans ses attributs ; ici on se contente de l'empecher de
     * depasser le cadre.
     */
    width: auto;
    height: auto;
    max-width: 100%;
    max-height: 100%;
    flex: 0 1 auto;
    min-height: 0;
    margin: 0 auto;

    display: block;
    border-radius: 12px;
    background: var(--black);
}

@media (max-width: 900px) {
    body.diagnostic-demo-page .diagnostic-demo-compare {
        grid-template-columns: auto;
        justify-content: center;
        left: 10px;
        right: 10px;
        top: calc(var(--diag-carte-bas, 64px) + 14px);
        bottom: 92px;
        gap: 12px;
        align-content: center;
        overflow: hidden;
    }

    body.diagnostic-demo-page .diagnostic-demo-compare__item {
        padding: 10px;
        border-radius: 14px;
    }
}

@keyframes demoPulse {
    0% {
        opacity: 0.95;
        transform: scale(1);
    }
    50% {
        opacity: 0.35;
        transform: scale(1.04);
    }
    100% {
        opacity: 0.95;
        transform: scale(1);
    }
}

@media (max-width: 900px) {
    body.diagnostic-demo-page .diagnostic-demo-card {
        top: 78px;
        left: 16px;
        width: min(420px, calc(100vw - 32px));

        /* Ancree par le haut ici, elle grandit vers le bas avec son texte : sur
           une tablette, une etape traduite en russe pouvait descendre jusque
           dans la palette. Elle s'arrete donc juste avant, et defile. */
        max-height: calc(100vh - 78px - var(--diag-palette-height, 0px) - 28px);
        overflow: auto;
    }
}

@media (max-width: 640px) {
    body.diagnostic-demo-page .diagnostic-demo-card {
        left: 10px;
        right: 10px;

        /* Meme piege en sens inverse : une regle precedente pose `top: 78px`.
           On l'annule ici, sinon la carte s'etire de nouveau. */
        top: auto;
        bottom: calc(var(--diag-browser-bottom, 0px) + var(--diag-palette-height, 150px) + 14px);
        width: auto;
        padding: 16px 14px 14px;
        border-radius: 16px;
    }

    body.diagnostic-demo-page .diagnostic-demo-card__title {
        font-size: 19px;
    }

    body.diagnostic-demo-page .diagnostic-demo-card__text {
        font-size: 14px;
        line-height: 1.5;
    }

    body.diagnostic-demo-page .diagnostic-demo-card__actions .btn {
        flex: 1 1 0;
        min-width: 0;
    }
}

/* Utility */
.grid-2 {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 20px;

    /* Les colonnes commencent en haut et gardent la hauteur de leur contenu.
       Etirees, la plus courte se remplissait de vide pour rejoindre l autre. */
    align-items: start;
}

.actions {
    display: flex;
    gap: 10px;
    flex-wrap: wrap;
    align-items: center;
}

.muted {
    color: var(--texte-discret);
}

.subtext {
    color: var(--black);
}

.mt-20 {
    margin-top: 20px;
}

.mb-20 {
    margin-bottom: 20px;
}

.responsive-table th a {
    color: inherit;
    text-decoration: none;
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

.responsive-table th a:hover {
    opacity: 0.85;
}

.table-toolbar {
    display: flex;
    justify-content: flex-end;
    margin-bottom: 16px;
}
/* `.table-toolbar__form` etait declare deux fois. Ce premier bloc
   etait entierement reecrit l.1940 : a selecteur et poids egaux,
   la derniere regle ecrite gagne, celle-ci n'imposait donc rien. */

.table-toolbar select {
    min-width: 90px;
}

/* Пагинация*/
.table-pagination {
    margin-top: 20px;
    display: flex;
    justify-content: center;
}
/*
 * ELLE SE REPLIE.
 *
 * Neuf numeros de 40 px plus leurs ecarts font 450 px : sur un ecran de 375,
 * la pagination poussait le document et toute la page se baladait
 * horizontalement. Mesure avant correction sur `/admin/translations` : 387 px
 * de debordement a 375, 191 a 768, 63 a 1024.
 */
.pagination {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    align-items: center;
    gap: 8px 10px;
    margin-top: 0;
}

.pagination__btn {
    min-width: 40px;
    height: 30px;
    padding: 0 14px;
    border-radius: 12px;

    display: inline-flex;
    align-items: center;
    justify-content: center;

    text-decoration: none;
    color: var(--white);

    border: 1px solid rgba(var(--white-rgb), 0.12);
    background: rgba(var(--white-rgb), 0.04);

    transition: .18s ease;
}

.pagination__btn:hover {
    background: rgba(var(--white-rgb), 0.10);
    border-color: rgba(var(--white-rgb), 0.25);
}

.pagination__btn.active {
    background: rgba(var(--portal-accent-rgb), 0.28);
    border-color: rgba(var(--portal-accent-rgb), 0.5);
    box-shadow: 0 0 14px rgba(var(--portal-accent-rgb), 0.25);
}

.pagination__btn.disabled {
    /* 0,4 REND 3,77, ET CE N EST PAS UN CONTROLE DESACTIVE. C'est un `<span>` :
       rien a activer, donc l'exemption que la norme accorde aux commandes
       inactives ne s'applique pas. La fleche dit qu'il n'y a PAS de page
       precedente -- une information, pas un bouton eteint -- et elle doit se
       lire. A 0,58 elle rend 5,1 et reste nettement en retrait des numeros,
       qui sont a 1.

       L INSTRUMENT RESTE STRICT : j'aurais pu ajouter `.disabled` a sa liste
       de marqueurs d'inactivite et faire disparaitre l'alerte. Ecarter un
       texte parce qu'il derange, c'est regler le thermometre. */
    opacity: 0.58;
    pointer-events: none;
}

.pagination__dots {
    padding: 0 6px;

    /* UNE OPACITE DE 0,5 SUR L ENCRE COURANTE REND 4,43 pour 4,5 exiges : ces
       points disent qu'il MANQUE des pages entre deux numeros, ils ne sont pas
       une decoration qu'on peut eteindre. A 0,62 ils rendent 6,4 et restent
       plus discrets que les numeros, qui sont a 1. */
    opacity: 0.62;
}

/* Meme raison que pour les boutons : 30 px se visent mal au doigt. On ouvre la
   hauteur sans toucher a la largeur, pour que la rangee garde son rythme. */
@media (max-width: 768px) {
    .pagination__btn {
        height: 44px;
        min-width: 44px;
    }
}

/* Поиск */
.table-toolbar {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 16px;
    gap: 12px;
    flex-wrap: wrap;
}

.table-toolbar__form {
    display: flex;
    justify-content: space-between;
    align-items: center;
    width: 100%;
    gap: 12px;
    flex-wrap: wrap;
}

.table-toolbar__left {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
}

.table-toolbar__right {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
}

.table-toolbar__right label {
    margin-bottom: 0;
    white-space: nowrap;
}

/* Un selecteur de filtre, a cote de la recherche : meme hauteur qu'elle,
   sinon la rangee se lit de travers. */
.table-toolbar__select {
    margin-bottom: 0;
    width: auto;
    min-width: 12ch;
}

.table-toolbar__search {
    min-width: 260px;
    max-width: 420px;
    flex: 1 1 260px;
}

.table-toolbar__form input,
.table-toolbar__form select {
    width: auto;
    margin-bottom: 0;
}

.table-toolbar__form .btn {
    width: auto;
}

.table-toolbar__clear {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    border-radius: 10px;
    text-decoration: none;
    color: var(--ink-300);
    background: rgba(var(--white-rgb), 0.04);
    border: 1px solid rgba(var(--white-rgb), 0.12);
    transition: .18s ease;
    box-sizing: border-box;
}

.table-toolbar__clear:hover {
    color: var(--white);
    background: rgba(var(--white-rgb), 0.10);
    border-color: rgba(var(--white-rgb), 0.24);
}

.table-toolbar__clear.is-hidden {
    display: none;
}

#users-table-container {
    transition: opacity .18s ease;
}

#users-table-container.is-loading {
    opacity: 0.55;
    pointer-events: none;
}

.courses-count {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-weight: 600;
}

.courses-count__divider {
    color: var(--texte-discret);
    font-weight: 400;
}

.courses-count__active {
    color: var(--green);
}

.courses-count__inactive {
    color: var(--ink-300);
}
/* Media */
.client-video {
    width: 100%;
    margin-top: 10px;
    border-radius: 10px;
    background: var(--black);
}

/* Responsive table cards */
.responsive-table {
    width: 100%;
    border-collapse: collapse;
}

.responsive-table__label {
    display: none;
    color: var(--texte-discret);
    font-size: 12px;
    margin-bottom: 4px;
}

/* --- 8. Deux boutons courts tiennent sur une ligne ------------------------
   -------------------------------------------------------------------------
   Sur la page d'aide, « Modifier » et « Supprimer » sont empiles l'un sous
   l'autre dans la colonne des actions. Chaque ligne du tableau fait donc 107 px
   de haut pour deux lignes de texte, et une liste de quinze fiches remplit deux
   ecrans la ou elle en remplirait un.

   PREMIERE VERSION FAUSSE, deux fois. J'avais vise `.admin-help-actions` et
   `.admin-help-table__actions`, deux classes qui n'existent pas : le conteneur
   s'appelle `.responsive-table__actions`. Et j'avais suppose qu'ils etaient
   empiles par un conteneur en colonne, alors qu'il est deja en `flex row` avec
   un `gap` de 8.

   LA VRAIE CAUSE EST UNE MESURE : les deux boutons demandent 200 px, la colonne
   en offre 164, et le conteneur est en `wrap`. Ils passent donc a la ligne,
   comme n'importe quel contenu trop large. « Supprimer » a lui seul fait 104 px
   parce qu'il est rendu a 16 px la ou son voisin est a 14.

   Ils deviennent compacts, a la taille des boutons de ligne du reste de
   l'administration, et la rangee cesse de se replier.
   ------------------------------------------------------------------------- */
.responsive-table__actions {
    display: flex;
    gap: 8px;
    flex-wrap: nowrap;
    align-items: center;
}

.progress {
    position: relative;
    height: 22px;
    background: rgba(var(--white-rgb), 0.06);
    border-radius: 999px;
    overflow: hidden;
}

/*
 * ELLE EST ANCREE A DROITE, ET C'EST VOULU.
 *
 * Les deux barres d'une carte ne disent pas la meme chose. Celle des seances
 * COMPTE CE QUI EST FAIT : elle part de la gauche et grandit. Celle-ci compte
 * CE QUI RESTE : elle est collee au bord droit, et le vide qui grandit a sa
 * gauche est le temps deja passe. Elle se vide vers la droite au fil des
 * jours, ce qu'aucune barre partant de la gauche ne montrerait.
 *
 * Les deux sens se distinguent donc d'un coup d'oeil, sans lire les libelles.
 *
 * Le temps qui reste n'appartient a aucun domaine : il prend le cyan du
 * produit, pas le bleu, qui dit « poster, envoyer ». Meme franchise que la
 * barre d'or, et le meme halo sous le libelle pour la rendre possible.
 */
.progress--reverse .progress__bar {
    margin-left: auto;
    background: linear-gradient(90deg,
        rgba(var(--portal-accent-rgb), 0.70), rgb(var(--portal-accent-rgb)));
}

.progress__bar {
    height: 100%;
    background: linear-gradient(90deg, var(--couleur-accent), rgb(var(--portal-accent-clair-rgb)));
    border-radius: 999px;
    transition: width 0.4s ease;
}

/* === Рекомендованные сеансы (фиолетовый) === */
/*
 * LA BARRE D'AVANCEMENT D'UN PROGRAMME EST OR, comme tout ce qui touche aux
 * programmes. Elle etait violette, teinte qui appartient aux consultations :
 * la meme page montrait du violet, du bleu et du vert pour trois choses qui
 * parlent toutes du meme programme.
 *
 * UN VOILE D'OR PLUTOT QUE L'OR PLEIN. Le libelle est ecrit en blanc PAR-DESSUS
 * la barre : sur de l'or plein il descend sous trois de rapport des que la
 * barre se remplit.
 *
 * L'OR EST FRANC, ET LE TEXTE PORTE SON HALO.
 *
 * Premiere tentative : un voile a 0,50, calcule pour que le blanc garde 4,5 de
 * rapport par-dessus. Le compte etait bon et la barre etait terne -- on ne
 * lisait plus une barre d'or mais une barre grise legerement chaude.
 *
 * Le halo sombre pose sous le libelle change le calcul : le texte ne se lit
 * plus contre la barre mais contre son propre contour, et la teinte redevient
 * libre. Voir `.progress__text`.
 *
 * L'or PLEIN est garde pour la barre achevee, juste en dessous, avec son texte
 * en noir : c'est ce qui distingue « en cours » de « atteint ».
 */
.progress--recommended .progress__bar {
    background: linear-gradient(90deg,
        rgba(var(--teinte-or), 0.72), rgb(var(--teinte-or)));
}

/* === Достигнуты рекомендации (золото) === */
.progress--recommended-done .progress__bar {
    background: linear-gradient(90deg, rgb(var(--amber-deep-rgb)), var(--amber-soft));
    box-shadow: 0 0 10px rgba(var(--amber-deep-rgb), 0.6);
}
.progress--recommended-done {
    animation: progressGlow 1.2s ease;
}

@keyframes progressGlow {
    0% { box-shadow: 0 0 0 rgba(var(--amber-deep-rgb), 0); }
    50% { box-shadow: 0 0 20px rgba(var(--amber-deep-rgb), 0.6); }
    100% { box-shadow: 0 0 0 rgba(var(--amber-deep-rgb), 0); }
}

/* Читаемый текст на золотом фоне */
.progress--recommended-done .progress__text {
    color: var(--black);
    text-shadow: 0 1px 2px rgba(var(--white-rgb), 0.25);
}

/*
 * LE LIBELLE SE LIT SUR LES DEUX MOITIES DE LA BARRE.
 *
 * Il est ecrit PAR-DESSUS, donc il traverse le remplissage ET la piste vide :
 * aucune couleur de texte ne tient sur les deux a la fois des que le
 * remplissage est franc. Le halo sombre resout les deux cas d'un coup -- il
 * disparait sur le fond sombre, et il detache le blanc sur l'or comme sur le
 * cyan.
 *
 * Deux ombres : une large qui pose l'assise, une serree qui redessine le
 * contour des lettres. Une seule des deux laisse les fines baver.
 */
.progress__text {
    text-shadow: 0 1px 3px rgba(var(--black-rgb), 0.85),
                 0 0 2px rgba(var(--black-rgb), 0.75);
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 13px;
    font-weight: 500;
    color: var(--white);
    pointer-events: none;
}

.progress--danger .progress__bar {
    background: linear-gradient(90deg, var(--couleur-danger), rgb(var(--teinte-rouge)));
}

/* Стили плеера */

html, body {
    margin: 0;
    padding: 0;
}

body.therapy-player-page {
    background: var(--black) !important;
    overflow: hidden;
}

body.therapy-player-page #app {
    position: fixed;
    inset: 0;
    width: 100vw;
    height: 100vh;
    background: var(--black);
    z-index: 9999;
    overflow: hidden;
}
       
.controls {
    display: flex;
    gap: 10px;
    flex-wrap: wrap;
    justify-content: center;
}

.hint {
    font-size: 14px;
    opacity: 0.85;
    max-width: 700px;
    line-height: 1.45;
}

body.therapy-player-page #screen {
    position: absolute;
    inset: 0;
    background: var(--black);
    will-change: background-color;
    z-index: 1;
    pointer-events: none;
}

body.therapy-player-page #overlay {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    gap: 16px;
    background: rgba(var(--black-rgb), 0.88);
    color: white;
    z-index: 20;
    text-align: center;
    padding: 24px;
}

/* LE TEMPS VIT DANS LA PASTILLE, il n'a plus de coin a lui. */
body.therapy-player-page #status {
    color: rgba(var(--white-rgb), 0.9);
    font-size: 15px;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    user-select: none;
}

/* LA PASTILLE DE LA SEANCE, DU MEME DESSIN QUE LA PALETTE DU TEST.
   Meme place, meme fond, meme filet, meme rayon, meme fondu : ce sont deux
   moments ou l'on regarde un ecran en plein, et les deux commandes doivent se
   reconnaitre. Voir `.diagnostic-controls-bar`. */
body.therapy-player-page .player-bar {
    position: absolute;
    left: 50%;
    bottom: calc(env(safe-area-inset-bottom, 0px) + 16px);
    transform: translateX(-50%);
    z-index: 50;

    width: max-content;
    max-width: calc(100vw - 32px);
    padding: 12px 16px;
    box-sizing: border-box;

    display: flex;
    flex-direction: column;
    gap: 10px;

    border-radius: 18px;
    background: rgba(var(--black-rgb), 0.5);
    border: 1px solid rgba(var(--white-rgb), 0.14);
    backdrop-filter: blur(14px);

    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    transition:
        opacity 0.28s ease,
        transform 0.28s ease,
        visibility 0.28s ease;
}

/* ELLE S'EFFACE POUR LAISSER L'ECRAN. C'est le point : pendant une seance on
   regarde la couleur, pas la commande. Un mouvement la rappelle. */
body.therapy-player-page .player-bar.is-hidden {
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transform: translateX(-50%) translateY(16px);
}

body.therapy-player-page .player-bar__frise {
    display: flex;
    gap: 3px;
    height: 5px;
    min-width: 220px;
}

/* LA PART VIENT DU FICHIER : `flex` est pose en ligne depuis la duree de
   chaque phase, et une base de zero fait que seule la part compte. */
body.therapy-player-page .timeline__seg {
    flex: 0 1 0%;
    position: relative;
    border-radius: 3px;
    overflow: hidden;
    background: rgba(var(--white-rgb), 0.16);
}

body.therapy-player-page .timeline__fill {
    position: absolute;
    inset: 0 auto 0 0;
    width: 0;
    border-radius: 3px;
}

body.therapy-player-page .player-bar__ligne {
    display: flex;
    align-items: center;
    gap: 14px;
}

body.therapy-player-page .player-bar__phase {
    font-size: 9px;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: rgba(var(--white-rgb), 0.38);
    white-space: nowrap;
    user-select: none;
}

body.therapy-player-page .player-bar__temps {
    margin-right: auto;
}

body.therapy-player-page .player-bar__actions {
    display: flex;
    align-items: center;
    gap: 8px;
}

/* LE CURSEUR PART AVEC LA PASTILLE. Une fleche immobile au milieu d'un ecran
   de couleur est exactement la distraction qu'on retire. */
body.therapy-player-page.sans-curseur {
    cursor: none;
}

/* LE DECOMPTE DE PREPARATION. Plein ecran, au centre, sur le noir : c'est le
   seul moment de la seance ou l'on demande de regarder un chiffre. */
body.therapy-player-page #prepare {
    position: absolute;
    inset: 0;
    z-index: 40;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 18px;
    color: rgba(var(--white-rgb), 0.92);
    user-select: none;
    pointer-events: none;
}

body.therapy-player-page .prepare__mot {
    font-size: 17px;
    letter-spacing: 0.04em;
    opacity: 0.8;
    text-align: center;
    padding: 0 24px;
}

body.therapy-player-page .prepare__compte {
    font-size: clamp(64px, 18vw, 140px);
    font-weight: 300;
    line-height: 1;
    font-variant-numeric: tabular-nums;
}

body.therapy-player-page #playerControls {
    display: flex;
    align-items: center;
    gap: 8px;
}

/* TRENTE-SIX PIXELS, ET PAS QUARANTE-QUATRE. Ils ne sont plus seuls dans un
   coin : ils vivent dans la pastille, a cote du temps et de la frise, et un
   bouton qui pese autant que le reste de la barre la desequilibre. */
body.therapy-player-page .player-icon-btn {
    width: 36px;
    height: 36px;
    min-width: 36px;
    min-height: 36px;
    padding: 0;

    display: inline-flex;
    align-items: center;
    justify-content: center;

    border: 1px solid rgba(var(--white-rgb), 0.12);
    border-radius: 16px;
    background: rgba(var(--white-rgb), 0.14);
    color: var(--white);
    text-decoration: none;
    cursor: pointer;
    box-sizing: border-box;

    transition:
        transform 0.18s ease,
        background 0.18s ease,
        border-color 0.18s ease,
        box-shadow 0.18s ease;
}

body.therapy-player-page .player-icon-btn span {
    display: block;
    font-size: 24px;
    line-height: 1;
    transform: translateY(-1px);
}

body.therapy-player-page .player-icon-btn:hover {
    background: rgba(var(--white-rgb), 0.20);
    border-color: rgba(var(--white-rgb), 0.24);
    box-shadow: 0 0 18px rgba(var(--white-rgb), 0.08);
}

body.therapy-player-page .player-icon-btn:active {
    transform: scale(0.96);
}

body.therapy-player-page .player-icon-btn:focus-visible {
    outline: none;
    box-shadow: 0 0 0 2px rgba(var(--portal-accent-rgb), 0.45),
        0 0 20px rgba(var(--portal-accent-rgb), 0.18);
}

@media (max-width: 640px) {
    /* LA PASTILLE PREND LA LARGEUR, sans toucher aux bords. Sa frise a besoin
       de place pour que la part de chaque phase reste lisible. */
    body.therapy-player-page .player-bar {
        left: 12px;
        right: 12px;
        transform: none;
        width: auto;
        max-width: none;
    }

    body.therapy-player-page .player-bar.is-hidden {
        transform: translateY(16px);
    }

    body.therapy-player-page .player-bar__frise {
        min-width: 0;
    }

    /* AU DOIGT, UN PEU PLUS GRANDS. On vise 40 px, pas les 52 d'avant : la
       pastille tient sur une ligne avec le temps. */
    body.therapy-player-page .player-icon-btn {
        width: 40px;
        height: 40px;
        min-width: 40px;
        min-height: 40px;
        border-radius: 12px;
    }

    body.therapy-player-page #status {
        font-size: 13px;
    }
}

.btn-therapy {
    background: #16a34a;
}

.btn-therapy:hover {
    background: #15803d;
}

/* ===== Bottom navigation ===== */

.lk-body {
    padding-bottom: calc(15px + env(safe-area-inset-bottom, 0px));
}

.lk-wrapper {
    display: block;

}

.lk-content {
    max-width: 1400px;
    width: 100%;
    margin: 0 auto;
    padding: 24px 24px 110px;
    box-sizing: border-box;
}

@media (min-width: 901px) {
    .lk-content {
        padding: 32px 32px 120px;
    }
}

@media (max-width: 640px) {
    .lk-content {
        padding: 16px 16px 108px;
    }
}

/* --- LA GRILLE DES PROGRAMMES, ET POURQUOI ELLE EST ICI ------------------
   -------------------------------------------------------------------------
   ELLE MANQUAIT, ET LE DEFAUT SE VOYAIT SANS SE COMPRENDRE. Sur « Mes
   programmes », les cartes s'empilaient une par ligne, chacune large de
   seize cents pixels pour un contenu qui en demande cinq cents : la
   description tenait sur deux lignes etirees, et les barres de progression
   affichaient un remplissage de cent soixante pixels dans une piste large de
   toute la page, ce qui ne se lit plus comme une proportion.

   LA CAUSE. La regle de base (`display: grid` et trois colonnes) vivait dans
   `journey.css`, que six vues chargent mais PAS `materials/index`. Ce qui se
   trouvait ici, dans la feuille que la page charge vraiment, ce sont
   uniquement les REPLIS responsives ci-dessous. Or ils ne posent que
   `grid-template-columns`, qui ne fait rien sans `display: grid` : les deux
   media queries etaient donc inertes depuis le debut.

   LE MOTIF EST CONNU. Une decision ecrite dans une feuille que la page ne
   charge pas, c'est la meme chose qu'une decision non prise. On pose donc la
   base a cote de ses replis, la ou tout le monde la trouvera.
   ------------------------------------------------------------------------- */
.courses-grid {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 20px;

    /* LES CARTES D UNE RANGEE ONT LA MEME HAUTEUR, et c'est un changement par
       rapport a `journey.css`, qui posait `align-items: start`. En pile pleine
       largeur cela ne se voyait pas ; en grille, les descriptions n'ont pas la
       meme longueur et les cartes finissaient a des hauteurs differentes, ce
       qui donne une rangee en dents de scie. */
    align-items: stretch;
}

/* LE BOUTON TOMBE AU BAS DE SA CARTE. Sans cela, une carte etiree garde son
   bouton colle au contenu et laisse un vide dessous : les boutons d'une meme
   rangee ne s'alignent plus, alors qu'ils font le meme geste.

   ET IL GARDE SA LARGEUR, ce que j'avais casse en posant la colonne. Un enfant
   de colonne s'etire sur toute la largeur par defaut : le bouton, qui fait
   normalement la taille de son libelle, devenait un aplat d'or large de toute
   la carte. Trois par rangee, cela faisait beaucoup d'or, et j'ai cru un
   instant que la couleur etait en cause alors que c'etait ma regle.

   LA CORRECTION VISE LE BOUTON SEUL, par `align-self`. Premier essai :
   `align-items: flex-start` sur la carte, ce qui rend sa largeur au bouton
   mais la retire A TOUS LES AUTRES. Les barres de progression, qui vivent dans
   une enveloppe sans largeur propre, se sont reduites a rien et ont disparu de
   l'ecran. Une regle posee sur le conteneur touche tout ce qu'il contient ;
   quand on ne veut qu'un enfant, on le dit sur l'enfant. */
.courses-grid > .course-card {
    display: flex;
    flex-direction: column;
}

.courses-grid > .course-card > .bouton-programme {
    margin-top: auto;
    align-self: flex-start;
}

/* LA BARRE PREND TOUTE LA LARGEUR, LE TEXTE PASSE AU-DESSUS.
   Partager la ligne avec son libelle laissait deux choses trop petites : une
   barre reduite au tiers de la carte, ou une barre dont la longueur variait
   avec le nombre de lettres ecrites a cote -- « 4:07 » donnait une bande plus
   large que « 17:35 ». Sur deux lignes, la mesure est la MEME partout et
   toujours la plus grande disponible : la carte. */
.course-seance {
    display: grid;
    grid-template-columns: 1fr;
    gap: 5px;
    margin: 0 0 14px;
}

.course-seance__duree {
    grid-row: 1;
}

.course-phases {
    grid-row: 2;
}

/* LE DETAIL DES PHASES, EN FACE DE SES COULEURS.
   Les colonnes sont posees en ligne, a la part de chaque phase, pour tomber
   sous leur segment ; le plancher `min-content` les empeche de couper leur
   texte quand la carte est etroite. La pastille garde le lien avec la bande
   dans ce cas-la. */
/* LE MEME ECART QUE LA FRISE, sinon rien ne tombe en face : la gouttiere est
   prise sur la largeur AVANT le partage des parts, et huit pixels contre trois
   decalaient chaque colonne un peu plus que la precedente. */
.course-phases-detail {
    grid-row: 3;
    display: grid;
    gap: 2px 3px;
    margin-top: 1px;
    font-size: 11px;
    color: rgba(var(--white-rgb), 0.5);
    font-variant-numeric: tabular-nums;
}

/* `hidden` DOIT GAGNER. La classe pose `display: grid` ; l'attribut ne fait
   que `display: none` sur la valeur par defaut, et la grille restait a
   l'ecran, bouton ferme. */
.course-phases-detail[hidden] {
    display: none;
}

/* LES DEUX VALEURS L'UNE SOUS L'AUTRE. Cote a cote, une colonne mesurait plus
   que la part de sa phase : elle poussait les suivantes, et le chiffre ne
   tombait plus en face de sa bande. */
/* CE QU'UNE CELLULE MESURE AU PLUS ETROIT DECIDE DE L'ALIGNEMENT.
   Tant que la place qu'elle reclame depasse la part de sa phase, elle pousse
   les suivantes et plus rien ne tombe en face. La premiere phase ne pese que
   onze a dix-huit pour cent de la seance : sur une carte a trois par rangee,
   cela fait cinquante-six pixels. Tout ce qui n'est pas le chiffre lui-meme
   -- la marge a droite, l'air autour -- sort donc de la cellule. */
.course-phases-detail__part {
    display: flex;
    flex-direction: column;
    gap: 1px;
    line-height: 1.35;
}

.course-phases-detail__part .course-detail__pastille {
    width: 7px;
    height: 7px;
    border-radius: 2px;
    margin-right: 4px;
    vertical-align: 0;
}

.course-phases-detail__temps {
    color: rgba(var(--white-rgb), 0.66);
    white-space: nowrap;
}

/* ALIGNEE SOUS LE TEMPS, pas sous la pastille : les deux chiffres d'une phase
   partent du meme bord, la pastille deborde a leur gauche. */
.course-phases-detail__freq {
    padding-left: 11px;
    white-space: nowrap;
}

/* LES TROIS COULEURS A LEUR PART.
   `flex` est pose en ligne depuis la duree de chaque phase ; une base de zero
   fait que seule la part compte. Trois tiers egaux montreraient un decoupage
   qui n'existe dans aucun programme. */
.course-phases {
    display: flex;
    gap: 3px;
    height: 8px;
    min-width: 0;
}

.course-phases__part {
    flex: 0 1 0%;
    border-radius: 3px;
}

.course-seance__duree {
    font-size: 13px;
    color: rgba(var(--white-rgb), 0.62);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* ═══ LES RAILS DE LA CARTE ═══════════════════════════════════════════════
   Les deux barres faisaient 22 px avec leur texte ECRIT DEDANS. Rien d'autre
   dans le produit ne se dessine ainsi, et deux barres empilees pesaient plus
   que le contenu de la carte.

   Le rail garde ce qu'il sait faire -- montrer une part -- et le chiffre se lit
   AU-DESSUS, sur sa propre ligne, pour que la barre garde toute la largeur de
   la carte. Deux colonnes : le libelle, et la mention qui se range a droite ;
   la piste passe sous les deux. */
.course-rail {
    display: grid;
    grid-template-columns: 1fr auto;
    align-items: center;
    gap: 5px 10px;
    margin-bottom: 12px;
    font-size: 13px;
    color: rgba(var(--white-rgb), 0.72);
}

.course-rail__mot {
    grid-row: 1;
    grid-column: 1;
    min-width: 0;
    font-variant-numeric: tabular-nums;
}

/* LA PISTE REPREND `.progress`, MAIS FINE. On ne redefinit que ce qui change :
   la hauteur, et le fait qu'elle tienne toute la largeur. Le remplissage, ses
   teintes et le sens inverse des jours restent ou ils sont ecrits. */
.course-rail__piste {
    grid-row: 2;
    grid-column: 1 / -1;
    display: flex;
    height: 8px;
}

/* « Recommandé atteint » perd son encadre et sa ligne : c'est une mention, elle
   se range au bout de la ligne de texte. */
.course-rail__atteint {
    grid-row: 1;
    grid-column: 2;
    font-size: 12px;
    color: rgba(var(--amber-deep-rgb, 217, 164, 6), 0.9);
    white-space: nowrap;
}

/* LE BOUTON DE LANCEMENT SE RANGE A LA DENSITE DE LA CARTE. Quarante-six
   pixels de haut, il dominait une carte dont tout le reste vient de passer en
   lignes fines. */
/* DANS LA CARTE, LE MEME BOUTON EN PLUS PETIT. La facture -- or plat, pas de
   lueur -- est posee sur `--seul` ; il ne reste ici que la mesure, celle d'une
   carte dont tout le reste est a treize pixels. */
.courses-grid > .course-card > .bouton-programme {
    height: 36px;
    padding: 0 16px;
    font-size: 13.5px;
}

/* LE SUIVI, EN TETE DE PAGE.
   Ce n'est pas une carte : il portait le gabarit `.card`, donc la meme boite
   que les programmes eux-memes, pour trois mots. Une bande fine, posee au-
   dessus de la grille, qui se replie au lieu de deborder. */
.suivi-seances {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px 14px;
    margin-bottom: 20px;
    padding: 9px 14px;
    border-radius: 12px;
    background: rgba(var(--white-rgb), 0.035);
    border: 1px solid rgba(var(--white-rgb), 0.07);
}

/* LA SERIE EST UN COMPTE, ET UN COMPTE SE LIT DANS UNE PASTILLE. En texte nu
   a cote d'une autre phrase, elle se confondait avec elle. */
.suivi-seances__serie {
    padding: 3px 10px;
    border-radius: 999px;
    font-size: 12.5px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    color: rgba(var(--couleur-accent-doux-rgb), 0.95);
    background: rgba(var(--couleur-accent-doux-rgb), 0.11);
    border: 1px solid rgba(var(--couleur-accent-doux-rgb), 0.22);
}

.suivi-seances__derniere {
    color: rgba(var(--white-rgb), 0.6);
    font-size: 13.5px;
}

/* PAS DE SOULIGNE AU REPOS. Le seul lien de la bande n'a pas besoin d'etre
   souligne pour se donner a voir ; la fleche le fait, et le souligne revient
   au survol, la ou il sert. */
.suivi-seances__suite {
    margin-left: auto;
    color: rgba(var(--couleur-accent-doux-rgb), 0.95);
    font-size: 13.5px;
    text-decoration: none;
}

.suivi-seances__suite::after {
    content: ' \2192';
}

.suivi-seances__suite:hover {
    text-decoration: underline;
    text-underline-offset: 3px;
}

/* QUAND LA BANDE SE REPLIE, LE LIEN NE FLOTTE PLUS A DROITE. Pousse par
   `margin-left: auto`, il se retrouvait seul sur sa ligne et colle au bord,
   sans rien en face : il ne se lisait plus comme la suite de la phrase. */
@media (max-width: 640px) {
    .suivi-seances__suite {
        margin-left: 0;
    }
}

/* LE BOUTON QUI OUVRE CE QUI EST AU PRATICIEN.
   Il se range dans la ligne du titre, apres l'etiquette d'etat, et se tient en
   retrait : ce qu'il ouvre est un second niveau, il ne doit pas concurrencer
   le nom de l'element. Le chevron dit dans quel sens il va. */
.course-detail-bascule {
    margin-left: auto;
    padding: 2px 4px;
    border: 0;
    background: none;
    color: rgba(var(--white-rgb), 0.45);
    font: inherit;
    font-size: 12px;
    font-weight: 400;
    line-height: 1.3;
    white-space: nowrap;
    cursor: pointer;
}

.course-detail-bascule::after {
    content: ' \203A';
    display: inline-block;
    margin-left: 4px;
    transform: rotate(90deg);
    transition: transform .14s ease;
}

.course-detail-bascule[aria-expanded="true"]::after {
    transform: rotate(-90deg);
}

.course-detail-bascule:hover {
    color: rgba(var(--white-rgb), 0.72);
}

/* LA LIGNE DU PRATICIEN, EN PIED DE CARTE. Ce fut un depliant : il portait le
   detail des phases, qui se lit maintenant sous la frise. Il ne restait qu'une
   commande, et une commande ne merite pas un panneau a ouvrir. */
.course-detail {
    margin-top: 14px;
    font-size: 13px;
}

.course-detail__pastille {
    display: inline-block;
    width: 10px;
    height: 10px;
    border-radius: 3px;
    margin-right: 6px;
    vertical-align: -1px;
}

.course-detail__ajuster {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: 12px;
    color: rgba(var(--white-rgb), 0.72);
}

/* DEUX CARRES, ET ILS RESTENT COTE A COTE. `.btn` passe a 100 % sous 640 px :
   les deux commandes se retrouvaient empilees de part et d'autre du chiffre,
   ce qui ne se lit plus comme un reglage. */
.course-detail__pas {
    width: 32px;
    height: 32px;
    min-width: 32px;
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 17px;
    line-height: 1;
    color: rgba(var(--white-rgb), 0.85);
    background: rgba(var(--white-rgb), 0.07);
    border: 1px solid rgba(var(--white-rgb), 0.14);
    border-radius: 8px;
    cursor: pointer;
}

.course-detail__pas:hover {
    background: rgba(var(--white-rgb), 0.12);
}

.course-detail__pas:disabled {
    opacity: 0.45;
    cursor: default;
}

.course-detail__ajuster [data-role="compteur"] {
    min-width: 2ch;
    text-align: center;
    font-variant-numeric: tabular-nums;
}

/* Мобильная адаптация */
@media (max-width: 1200px) {
    .courses-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

@media (max-width: 700px) {
    .courses-grid {
        grid-template-columns: 1fr;
        gap: 16px;
    }
}

@media (max-width: 900px) {
    .lk-wrapper {
        flex-direction: column;
        gap: 0;
    }

    .lk-sidebar {
        width: 100%;
        border-right: none;
        border-bottom: 1px solid rgba(var(--white-rgb), 0.05);
        padding: 16px 20px;
        order: 2;
    }

    .lk-logo {
        margin-bottom: 16px;
    }

    .lk-sidebar nav a {
        margin-bottom: 10px;
    }

    .lk-content {
        padding: 16px 18px;
        order: 1;
    }

    .grid-2 {
        grid-template-columns: 1fr;
    }
}

@media (max-width: 640px) {
    body {
        font-size: 15px;
    }

    .lk-sidebar {
        padding: 14px 16px;
    }

    .lk-content {
        padding: 16px 18px;
    }

    h1 {
        font-size: 28px;
        margin-bottom: 16px;
    }

    h2 {
        font-size: 22px;
        margin-bottom: 14px;
    }

    h3 {
        font-size: 18px;
        margin-bottom: 12px;
                
    }

    /* `.card` a quitte ce bloc : voir plus bas, apres sa regle de base. */
    .btn {
        width: 100%;
        text-align: center;
        box-sizing: border-box;
    }

    .actions {
        flex-direction: column;
        align-items: stretch;
    }

    th,
    td {
        padding: 10px;
    }

    input[type="text"],
    input[type="email"],
    input[type="password"],
    input[type="tel"],
    input[type="url"],
    input[type="number"],
    select,
    textarea {
        padding: 11px 12px;
        font-size: 16px;
    }

    .client-video {
        border-radius: 8px;
    }
    
}

/* ===== Diagnostic viewer ===== */

.diagnostic-viewer__picker {
    max-width: 420px;
}

.diagnostic-viewer__meta {
    margin-top: 6px;
    color: var(--ink-200);
    font-size: 15px;
}

.diagnostic-viewer__tabs {
    gap: 12px;
}

/* ===== Diagnostic viewer: expert separated block ===== */

.card {
    background: rgba(var(--slate-mid-rgb), 0.7);
    padding: 20px;
    border-radius: 12px;
    margin-bottom: 20px;
    box-sizing: border-box;
    /* LE POINT-VIRGULE MANQUAIT, et le prefixe aussi. Ca passait parce que
       c'etait la derniere declaration du bloc ; la premiere ligne ajoutee
       apres elle aurait emporte les deux, sans erreur. */
    backdrop-filter: blur(14px);
    -webkit-backdrop-filter: blur(14px);
}

/* Sans `backdrop-filter`, le flou n'existe pas et le fond translucide laisse
   lire le decor a travers la carte. On rend alors le fond presque opaque.
   Ce repli vivait dans `journey.css`, loin du composant qu'il rattrape. */
@supports not ((backdrop-filter: blur(14px)) or (-webkit-backdrop-filter: blur(14px))) {
    .card,
    .lk-bottom-nav {
        background: rgba(var(--slate-rgb), 0.88);
    }
}

/*
 * LA CARTE SE RESSERRE SUR TELEPHONE.
 *
 * Cette regle vivait dans le grand bloc `@media (max-width: 640px)` situe plus
 * HAUT dans le fichier. Une requete media n'ajoute aucune specificite : a
 * egalite, c'est la derniere ecrite qui gagne, et la regle de base ci-dessus
 * l'ecrasait donc a toutes les largeurs.
 *
 * Mesure a 375 px, sur une carte nue posee dans la page : 20 px de marge
 * interieure et 12 px de rayon, la ou le repli mobile en demande 16 et 10.
 * `journey.css` en portait en plus une copie hors media, ce qui masquait la
 * cause : la copie retiree, le defaut restait.
 */
@media (max-width: 640px) {
    .card {
        padding: 16px;
        border-radius: 10px;
        margin-bottom: 16px;
    }
}

.diagnostic-viewer-nav-layout {
    display: flex;
    align-items: stretch;
    gap: 10px;
    margin-bottom: 20px;
}

.diagnostic-viewer-expert-card {
    flex: 0 0 auto;
    width: 150px;
    margin-bottom: 0;
    padding: 10px;
    display: flex;
    align-items: center;
    justify-content: center;

    background:
        linear-gradient(135deg, rgba(41, 29, 70, 0.38), rgba(51, 34, 34, 0.54));
    border: 1px solid rgba(var(--gold-soft-rgb), 0.42);
    box-shadow: inset 0 1px 0 rgba(var(--white-rgb), 0.08),
        0 12px 28px rgba(var(--black-rgb), 0.16);
}

@media (max-width: 900px) {
    .diagnostic-viewer-nav-layout {
        flex-direction: column;
    }

    .diagnostic-viewer-expert-card {
        width: 100%;
        box-sizing: border-box;
    }

    .diagnostic-viewer-tabs-card {
        width: 100%;
        box-sizing: border-box;
    }
}

.diagnostic-viewer-notes-card {
    margin-bottom: 14px;
}

.diagnostic-viewer-notes-card .expert-notes-card__summary {
    padding: 10px 16px;
}

.diagnostic-viewer-notes-card .expert-notes-form--compact {
    padding: 12px 16px 14px;
}

.diagnostic-bars-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: 18px;
    align-items: stretch;
}

.diag-mini-card {
    background: rgba(var(--slate-rgb), 0.45);
    border: 1px solid rgba(var(--white-rgb), 0.08);
    border-radius: 14px;
    padding: 16px;
    min-height: 320px;
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.diag-mini-card__title {
    font-weight: 700;
    line-height: 1.35;
    min-height: 44px;
}

.diag-mini-chart {
    height: 220px;
    display: flex;
    align-items: flex-end;
    justify-content: center;
    gap: 24px;
    padding: 12px 0 8px;
    border-radius: 12px;
    background: linear-gradient(to top, rgba(var(--white-rgb), 0.04), rgba(var(--white-rgb), 0.02)),
        rgba(var(--white-rgb), 0.02);
}

.diag-mini-chart__col {
    width: 56px;
    height: 100%;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    align-items: center;
    gap: 8px;
}

.diag-mini-chart__value {
    font-size: 14px;
    font-weight: 700;
    line-height: 1;
    background: rgba(var(--white-rgb), 0.92);
    color: var(--texte-fort);
    padding: 4px 8px;
    border-radius: 8px;
    box-shadow: 0 2px 8px rgba(var(--black-rgb), 0.18);
}

.diag-mini-chart__bar {
    width: 100%;
    min-height: 10px;
    border-radius: 10px 10px 4px 4px;
    box-shadow: inset 0 1px 0 rgba(var(--white-rgb), 0.18),
        0 8px 18px rgba(var(--black-rgb), 0.18);
    transition: height .18s ease;
}

.diag-mini-chart__bar--red {
    background: linear-gradient(to top, var(--surface-fond) 0%, var(--red-soft) 28%, var(--couleur-danger) 72%, #b91c1c 100%);
    border: 1px solid rgba(var(--red-deep-rgb), 0.65);
}

.diag-mini-chart__bar--blue {
    background: linear-gradient(to top, var(--surface-fond) 0%, #a5b4fc 28%, #4f46e5 72%, #1e1b4b 100%);
    border: 1px solid rgba(30, 27, 75, 0.72);
}

.diag-mini-chart__legend {
    margin-top: auto;
    display: flex;
    justify-content: center;
    gap: 12px;
    flex-wrap: wrap;
    font-size: 12px;
    color: var(--ink-300);
}

.diag-mini-chart__legend-item {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

.diag-mini-chart__legend-item::before {
    content: "";
    width: 10px;
    height: 10px;
    border-radius: 999px;
    display: inline-block;
}

.diag-mini-chart__legend-item--red::before {
    background: var(--couleur-danger);
}

.diag-mini-chart__legend-item--blue::before {
    background: #4f46e5;
}

@media (max-width: 640px) {
    .diagnostic-bars-grid {
        grid-template-columns: 1fr;
    }

    .diag-mini-card {
        min-height: 280px;
    }

    .diag-mini-chart {
        height: 190px;
        gap: 18px;
    }

    .diag-mini-chart__col {
        width: 48px;
    }
}

/* ===== Filters ===== */

.filters-layout {
    display: grid;
    grid-template-columns: 1fr;
    gap: 28px;
    margin-top: 28px;
    padding-top: 20px;
    border-top: 1px solid rgba(var(--white-rgb), 0.08);    
    
}

/* Вариант filters-layout без верхней линии, когда пояснение уже вынесено отдельно */
.filters-layout--plain {
    margin-top: 18px;
    padding-top: 0;
    border-top: none;
}

.filters-section__title {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 16px;
    padding: 6px 12px;
    border-radius: 999px;
    background: rgba(var(--white-rgb), 0.04);
    border: 1px solid rgba(var(--white-rgb), 0.08);
    color: rgba(var(--white-rgb), 0.92);
    font-size: 18px;
    font-weight: 700;
    line-height: 1.2;
}

.filters-section__title::before {
    content: "";
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: rgba(var(--blue-light-rgb), 0.95);
    box-shadow: 0 0 10px rgba(var(--blue-light-rgb), 0.45);
}

.filters-section--real .filters-section__title::before {
    background: rgba(var(--blue-light-rgb), 0.95);
    box-shadow: 0 0 10px rgba(var(--blue-light-rgb), 0.45);
}

.filters-section--imaginary .filters-section__title::before {
    background: rgba(var(--amber-deep-rgb), 0.95);
    box-shadow: 0 0 10px rgba(var(--amber-deep-rgb), 0.45);
}

/* LA COLONNE TIENT LE PLUS LONG LIBELLE, ET C EST CE QUI FIXE SON MINIMUM.

   A 78 px de minimum, une fenetre large rangeait onze barres sur une ligne et
   leur donnait 84 px chacune ; « Изобретательность » en demande 107 a 12 px de
   corps, et le navigateur le cassait en plein milieu -- « Изобретатель /
   ность ». Mesure : quatre libelles casses a 1340, deux a 390, AUCUN a 900 --
   la ou la grille se replie deja d'elle-meme et donne 110 px aux colonnes.

   ON GENERALISE CE QUI MARCHE DEJA. Le minimum passe a la largeur que la grille
   se donne quand elle se replie : sept a huit barres par ligne sur un grand
   ecran au lieu de onze, et la seconde ligne que le produit rend deja sur un
   ecran moyen. L'echelle est commune aux deux lignes, la comparaison tient.

   CE NOMBRE DEPEND DE LA LANGUE. Il est mesure sur le russe, qui porte les mots
   les plus longs des quatre ; l'allemand est a verifier quand ses pages seront
   rendues. Voir `A_FAIRE.md`. */
.filters-bars {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(110px, 1fr));
    gap: 14px;
    align-items: end;
}

.filters-layout,
.filters-section,
.filters-bars {
    min-width: 0;
    max-width: 100%;
}

.filter-bar-card {
    min-width: 0;
}

/*
 * LE RAYON EST POUR L'ANNEAU DE MISE EN AVANT, PAS POUR LA CARTE.
 *
 * Cette carte est un contenant transparent : ni fond, ni bordure. Son rayon
 * ne se voit donc jamais... sauf quand `.is-expert-focused` dessine un anneau
 * autour d'elle, en `box-shadow` a etalement. Une ombre epouse le rayon de la
 * boite : a zero, l'anneau sortait carre autour d'un contenu tout en coins
 * arrondis (le graphe est a 10 px).
 *
 * 12 plutot que 10 : l'anneau se pose 3 px en dehors, son bord exterieur
 * tombe donc a 15 et reste plus rond que le coin qu'il entoure.
 *
 * Toute nouvelle cible de mise en avant doit porter son propre rayon : la
 * regle `.is-expert-focused` n'en impose pas, pour ne pas ecraser celui des
 * cibles qui en ont deja un (`.stage-balance-card` est a 14).
 */
.filter-bar-card {
    display: flex;
    flex-direction: column;
    gap: 10px;
    position: relative;
    border-radius: 12px;
}

.filter-bar-card__chart {
    position: relative;
    height: 140px;
    padding-top: 30px;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    align-items: center;
    background: rgba(var(--slate-rgb), 0.28);
    border: 1px solid rgba(var(--white-rgb), 0.08);
    border-radius: 10px;
    overflow: visible;
}

.filter-bar-card__value {
    position: absolute;
    top: 8px;
    z-index: 4;
    font-size: 13px;
    font-weight: 700;
    line-height: 1;
    background: rgba(var(--white-rgb), 0.92);
    color: var(--texte-fort);
    padding: 4px 7px;
    border-radius: 8px;
    box-shadow: 0 2px 8px rgba(var(--black-rgb), 0.18);
}

.filter-bar-card__stem {
    position: absolute;
    top: 34px;
    bottom: 12px;
    width: 1px;
    border-left: 1px dashed rgba(var(--white-rgb), 0.18);
}

.filter-bar-card__bar {
    position: relative;
    z-index: 1;
    width: 68px;
    min-height: 8px;
    max-height: 100%;
    border-radius: 10px 10px 3px 3px;
    box-shadow: inset 0 1px 0 rgba(var(--white-rgb), 0.18),
        0 8px 18px rgba(var(--black-rgb), 0.18);
}

.filter-bar-card__bar--dark {
    background: linear-gradient(to top, var(--ink-900) 0%, rgb(var(--black-rgb)) 85%);
    border: 1px solid rgba(var(--white-rgb), 0.24);
}

.filter-bar-card__bar--light {
    background: linear-gradient(to top, var(--surface-fond) 0%, var(--ink-200) 85%);
    border: 1px solid rgba(var(--slate-light-rgb), 0.55);
}

.filter-bar-card__title {
    font-size: 13px;
    line-height: 1.3;
    text-align: center;
    color: var(--ink-200);
    min-height: 34px;
    word-break: normal;
    overflow-wrap: anywhere;
    hyphens: auto;
}

@media (max-width: 640px) {
    /* LES TROIS COLONNES DU TELEPHONE SONT PARTIES. Elles forcaient 97 px de
       largeur a 390 de fenetre, et les deux libelles les plus longs s'y
       cassaient encore. La regle de base decide desormais toute seule -- elle
       demande 110 px minimum et en rend deux ici -- donc un nombre ecrit a la
       main ne pouvait que la contredire. */

    .filter-bar-card__chart {
        height: 200px;
    }

    .filter-bar-card__bar {
        width: 100%;
        max-width: 58px;
    }
}

/* ===== QUALITIES ===== */

/* QUATRE-VINGT-SEIZE PIXELS DE COLONNE, ET C EST LE MOT LE PLUS LONG QUI LE
   DIT. « Necessite » se dit « Необходимость » en russe : 93 px a 12,5 px de
   corps, dans une case qui en offrait 92. Le navigateur le cassait donc en
   plein milieu -- « Необходи мость » -- et un mot coupe ne se lit plus.

   CE NOMBRE EXISTAIT DEJA : la regle du telephone, plus bas, pose 96 px pour
   ses trois colonnes. Il ne coute AUCUNE colonne : 9 rangees de 96 tiennent
   dans les 960 px utiles comme 9 de 92, et 5 dans les 576 d'une colonne a
   rail. Mesure faite aux deux largeurs avant de le changer. */
.qualities-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(96px, 96px));
    justify-content: center;
    gap: 10px 8px;
    margin-top: 10px;
}

/* Meme raison que `.filter-bar-card` juste au-dessus : contenant transparent,
   le rayon ne sert qu'a arrondir l'anneau de mise en avant. */
.quality-card {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    border-radius: 12px;
}

.quality-card__label {
    width: 100%;
    min-height: 44px;
    padding: 8px 8px;
    border-radius: 14px;
    background: linear-gradient(180deg, rgba(var(--blue-rgb), 0.16), rgba(var(--white-rgb), 0.04));
    border: 1px solid rgba(var(--white-rgb), 0.14);
    box-shadow: inset 0 1px 0 rgba(var(--white-rgb), 0.08),
        0 8px 18px rgba(var(--black-rgb), 0.14);
    color: rgba(var(--white-rgb), 0.92);
    font-size: 12px;
    font-weight: 600;
    line-height: 1.15;
    text-align: center;
    display: flex;
    align-items: center;
    justify-content: center;
    box-sizing: border-box;
    word-break: break-word;
    hyphens: auto;
    transition:
        transform 0.18s ease,
        border-color 0.18s ease,
        background 0.18s ease,
        box-shadow 0.18s ease;
}

.quality-card:hover .quality-card__label {
    transform: translateY(-1px);
    border-color: rgba(var(--blue-light-rgb), 0.28);
    background: linear-gradient(180deg, rgba(var(--blue-rgb), 0.26), rgba(var(--white-rgb), 0.14));
    box-shadow: inset 0 1px 0 rgba(var(--white-rgb), 0.1),
        0 10px 22px rgba(var(--black-rgb), 0.18);
}

.quality-card.is-empty .quality-card__label {
    background: linear-gradient(180deg, rgba(var(--amber-deep-rgb), 0.20), rgba(var(--white-rgb), 0.03));
    border: 1px solid rgba(var(--amber-deep-rgb), 0.34);
    color: var(--amber-pale);
    box-shadow: inset 0 1px 0 rgba(var(--white-rgb), 0.06),
        0 8px 18px rgba(var(--black-rgb), 0.14);
}

.quality-card__value {
    font-size: 12px;
    line-height: 1;
    background: rgba(var(--ink-50-rgb), 0.94);
    color: var(--texte-fort);
    padding: 3px 8px;
    border-radius: 999px;
    box-shadow: 0 2px 8px rgba(var(--black-rgb), 0.18);
}

.quality-card__bar {
    width: 60px;
    height: 132px;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    border-radius: 10px;
    border: 1px solid rgba(var(--white-rgb), 0.18);
    background: rgba(var(--slate-rgb), 0.24);
    box-shadow: inset 0 1px 0 rgba(var(--white-rgb), 0.06),
        0 8px 18px rgba(var(--black-rgb), 0.14);
}

.quality-card__part--real {
    background: linear-gradient(180deg, var(--surface-fond) 0%, var(--ink-200) 100%);
}

.quality-card__part--imaginary {
    background: linear-gradient(180deg, rgb(var(--black-rgb)) 0%, rgb(var(--black-rgb)) 100%);
}

.quality-card.is-empty .quality-card__bar {
    opacity: 0.45;
    filter: grayscale(0.45);
}

@media (max-width: 640px) {
    .qualities-grid {
        grid-template-columns: repeat(3, 96px);
        gap: 16px 10px;
    }

    .quality-card__bar {
        width: 56px;
        height: 140px;
    }

    .quality-card__label {
        min-height: 48px;
        font-size: 11px;
        padding: 8px 6px;
    }
}

.quality-card__bar {
    flex: 0 0 auto;
}

.stage-balance-panel {
    margin-top: 18px;
    padding: 14px 16px;
    border-radius: 16px;
    background: linear-gradient(135deg, rgba(var(--slate-rgb), 0.34), rgba(var(--white-rgb), 0.025));
    border: 1px solid rgba(var(--white-rgb), 0.08);
}

.stage-balance-panel--consultation {
    margin-top: 16px;
}

.stage-balance-panel__head {
    display: flex;
    justify-content: space-between;
    gap: 14px;
    align-items: flex-start;
    margin-bottom: 12px;
}

.stage-balance-panel__title {
    color: var(--ink-50);
    font-size: 18px;
    line-height: 1.25;
}

.stage-balance-panel__subtitle {
    margin-top: 5px;
    color: rgba(var(--ink-300-rgb), 0.78);
    font-size: 13px;
    line-height: 1.4;
}

.stage-balance-list {
    display: grid;
    gap: 10px;
}

.stage-balance-card {
    position: relative;
    padding: 12px 14px;
    border-radius: 14px;
    background: rgba(var(--white-rgb), 0.04);
    border: 1px solid rgba(var(--white-rgb), 0.08);
    overflow: hidden;
}

.stage-balance-card::before {
    content: "";
    position: absolute;
    inset: 0 auto 0 0;
    width: 4px;
    background: rgba(var(--slate-light-rgb), 0.55);
}

.stage-balance-card--real {
    border-color: rgba(var(--ink-200-rgb), 0.26);
    background: linear-gradient(135deg, rgba(var(--ink-50-rgb), 0.10), rgba(var(--white-rgb), 0.025));
}

.stage-balance-card--real::before {
    background: rgba(var(--ink-50-rgb), 0.88);
}

.stage-balance-card--imaginary {
    border-color: rgba(var(--slate-rgb), 0.88);
    background: linear-gradient(135deg, rgba(var(--black-rgb), 0.48), rgba(var(--slate-rgb), 0.46));
    box-shadow: inset 0 0 0 1px rgba(var(--white-rgb), 0.06),
        0 8px 18px rgba(var(--black-rgb), 0.14);
}

.stage-balance-card--imaginary::before {
    background: linear-gradient(180deg, var(--surface-sombre), rgb(var(--black-rgb)));
    box-shadow: 0 0 0 1px rgba(var(--white-rgb), 0.22);
}

.stage-balance-card__text {
    color: rgba(var(--ink-50-rgb), 0.94);
    font-size: 14px;
    line-height: 1.55;
}

.stage-balance-card__meta {
    display: flex;
    flex-wrap: wrap;
    gap: 7px 10px;
    margin-top: 8px;
    color: rgba(var(--ink-300-rgb), 0.72);
    font-size: 12px;
    line-height: 1.25;
}

.stage-balance-card__meta span {
    display: inline-flex;
    align-items: center;
}

.stage-balance-card__meta span + span::before {
    content: "•";
    margin-right: 10px;
    color: rgba(var(--slate-light-rgb), 0.55);
}

.stage-balance-panel__empty {
    padding: 11px 12px;
    border-radius: 12px;
    background: rgba(var(--white-rgb), 0.04);
    border: 1px dashed rgba(var(--slate-light-rgb), 0.22);
    color: rgba(var(--ink-300-rgb), 0.82);
    font-size: 13px;
    line-height: 1.45;
}

@media (max-width: 640px) {
    .stage-balance-panel {
        padding: 12px;
    }

    .stage-balance-card {
        padding: 11px 12px;
    }

    .stage-balance-card__meta {
        display: grid;
        gap: 5px;
    }

    .stage-balance-card__meta span + span::before {
        display: none;
    }
}

.qualities-intro {
    margin: 0 0 18px;
    padding: 14px 16px;
    border-radius: 16px;
    background: linear-gradient(135deg, rgba(var(--blue-rgb), 0.12), rgba(var(--white-rgb), 0.035));
    border: 1px solid rgba(var(--blue-light-rgb), 0.22);
}

.qualities-intro__title {
    margin-bottom: 8px;
    color: var(--couleur-accent-doux);
    font-size: 15px;
    font-weight: 800;
}

.qualities-intro__text,
.qualities-intro__summary {
    font-size: 14px;
    line-height: 1.55;
    color: rgba(var(--ink-200-rgb), 0.92);
}

.qualities-intro__summary {
    margin-top: 10px;
    padding-top: 10px;
    border-top: 1px solid rgba(var(--white-rgb), 0.08);
    color: var(--couleur-accent-doux);
}

.qualities-insights-grid {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 12px;
    margin: 0;
}

.qualities-insights-grid--filters {
    margin: -8px 0 2px;
}
.qualities-filters-intro {
    margin: -4px 0 -8px;
    padding: 14px 16px;
    border-radius: 16px;
    background: linear-gradient(135deg, rgba(var(--slate-rgb), 0.34), rgba(var(--white-rgb), 0.025));
    border: 1px solid rgba(var(--white-rgb), 0.08);
}

.qualities-filters-intro__title {
    margin-bottom: 8px;
    color: var(--ink-50);
    font-size: 15px;
    font-weight: 800;
}

.qualities-filters-intro__text,
.qualities-filters-intro__summary {
    font-size: 14px;
    line-height: 1.55;
    color: rgba(var(--ink-200-rgb), 0.88);
}

.qualities-filters-intro__summary {
    margin-top: 10px;
    padding-top: 10px;
    border-top: 1px solid rgba(var(--white-rgb), 0.08);
    color: var(--couleur-accent-doux);
}

.qualities-insight {
    padding: 14px;
    border-radius: 16px;
    background: rgba(var(--slate-rgb), 0.30);
    border: 1px solid rgba(var(--white-rgb), 0.08);
}

.qualities-insight--resources {
    border-color: rgba(var(--blue-light-rgb), 0.28);
    background: linear-gradient(180deg, rgba(var(--blue-rgb), 0.13), rgba(var(--white-rgb), 0.025));
}

.qualities-insight--attention {
    border-color: rgba(var(--etat-vigilance-rgb), 0.28);
    background: linear-gradient(180deg, rgba(var(--etat-vigilance-rgb), 0.13), rgba(var(--white-rgb), 0.025));
}

.qualities-insight--over-limit {
    border-color: rgba(var(--orange-rgb), 0.42);
    background: linear-gradient(180deg, rgba(var(--orange-rgb), 0.14), rgba(var(--white-rgb), 0.025));
}

.qualities-insight--low-resources {
    border-color: rgba(var(--slate-light-rgb), 0.24);
}

.qualities-insight__title {
    margin-bottom: 8px;
    font-size: 16px;    
    color: var(--ink-50);
}

.qualities-insight__text {
    margin-bottom: 10px;
    font-size: 13px;
    line-height: 1.45;
    color: rgba(var(--ink-200-rgb), 0.82);
}

.qualities-insight__items {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

.qualities-chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 9px;
    border-radius: 999px;
    background: rgba(var(--white-rgb), 0.06);
    border: 1px solid rgba(var(--white-rgb), 0.10);
    color: var(--ink-200);
    font-size: 12px;
    line-height: 1.2;
}

.qualities-chip strong {
    color: var(--white);
}

.qualities-chip--over-limit {
    background: rgba(var(--orange-rgb), 0.16);
    border-color: rgba(var(--orange-rgb), 0.42);
    color: #fed7aa;
}

.filter-bar-card.is-over-limit .filter-bar-card__chart {
    border-color: rgba(var(--orange-rgb), 0.66);
    box-shadow:
        0 0 0 1px rgba(var(--orange-rgb), 0.20) inset,
        0 0 18px rgba(var(--orange-rgb), 0.18);
}

.filter-bar-card.is-over-limit .filter-bar-card__bar {
    outline: 2px solid rgba(var(--orange-rgb), 0.95);
    outline-offset: 2px;
}

.filter-bar-card.is-over-limit .filter-bar-card__value {
    background: #fb923c;
    color: var(--texte-fort);
}

@media (max-width: 1100px) {
    .qualities-insights-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

@media (max-width: 640px) {
    .qualities-insights-grid {
        grid-template-columns: 1fr;
    }
}

/* ===== Connections ===== */

.connections-layout {
    display: grid;
    grid-template-columns: 1.15fr 1.15fr 0.9fr;
    gap: 18px;
    align-items: start;
}

.connections-side {
    display: grid;
    gap: 18px;
}

.connections-panel {
    background: rgba(var(--slate-rgb), 0.28);
    border: 1px solid rgba(var(--white-rgb), 0.08);
    border-radius: 14px;
    padding: 16px;
}

.connections-panel__title {
    text-align: center;
    margin-bottom: 14px;
}

.connections-general {
    text-align: center;
    font-size: 22px;
    font-weight: 700;
    color: var(--amber-bright);
    margin-bottom: 18px;
}

.connections-general--sub {
    font-size: 20px;
}

.connections-list {
    display: grid;
    gap: 10px;
}

.connections-list__item {
    display: flex;
    justify-content: space-between;
    gap: 12px;
    padding: 10px 12px;
    border-radius: 10px;
    background: rgba(var(--white-rgb), 0.04);
    border: 1px solid rgba(var(--white-rgb), 0.06);
}

.connections-list__name {
    font-weight: 600;
}

.connections-list__meta {
    color: var(--amber-bright);
    white-space: nowrap;
}

@media (max-width: 1100px) {
    .connections-layout {
        grid-template-columns: 1fr;
    }
}

/* ===== Resource Bio/Cal age battery ===== */
.resource-battery {
    display: flex;
    flex-direction: column;
    gap: 10px;
    justify-content: flex-start;
}

.resource-battery__topline {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 12px;
}

.resource-battery__title {
    font-size: 15px;
    font-weight: 700;
    line-height: 1.2;
    color: var(--ink-50);
}

.resource-battery__percent {
    font-size: 22px;
    font-weight: 800;
    line-height: 1;
    color: var(--ink-50);
}

.resource-battery__shell {
    display: flex;
    align-items: center;
    gap: 8px;
}

.resource-battery__body {
    position: relative;
    flex: 1;
    height: 30px;
    padding: 3px;
    border-radius: 10px;
    background: rgba(var(--black-rgb), 0.22);
    border: 1px solid rgba(var(--white-rgb), 0.12);
    overflow: hidden;
    transition: border-color .18s ease, box-shadow .18s ease;
}

.resource-battery__cap {
    width: 10px;
    height: 16px;
    border-radius: 0 4px 4px 0;
    background: rgba(var(--white-rgb), 0.2);
    border: 1px solid rgba(var(--white-rgb), 0.12);
    transition: border-color .18s ease, box-shadow .18s ease, background .18s ease;
}

.resource-battery__fill {
    height: 100%;
    border-radius: 8px;
    transition: width 0.35s ease, box-shadow .18s ease, background .18s ease;
}

.resource-battery__fill--reserve {
    background: linear-gradient(90deg, #d4a72c, var(--gold-soft) 55%, #fff2b2 100%);
    box-shadow: 0 0 18px rgba(var(--gold-soft-rgb), 0.38);
}

.resource-battery__fill--green {
    background: linear-gradient(90deg, var(--couleur-succes), var(--green));
}

.resource-battery__fill--yellow {
    background: linear-gradient(90deg, var(--amber-mid), var(--amber-bright));
}

.resource-battery__fill--orange {
    background: linear-gradient(90deg, rgb(var(--amber-deep-rgb)), #fb923c);
}

.resource-battery__fill--red {
    background: linear-gradient(90deg, var(--couleur-danger), rgb(var(--teinte-rouge)));
}

.resource-battery__shell--reserve .resource-battery__body {
    border-color: rgba(var(--gold-bright-fixe-rgb), 0.42);
    box-shadow: 0 0 18px rgba(var(--gold-bright-fixe-rgb), 0.18);
}

.resource-battery__shell--reserve .resource-battery__cap {
    border-color: rgba(var(--gold-bright-fixe-rgb), 0.42);
    background: rgba(var(--gold-bright-fixe-rgb), 0.26);
    box-shadow: 0 0 12px rgba(var(--gold-bright-fixe-rgb), 0.16);
}

.resource-battery__shell--green .resource-battery__body {
    border-color: rgba(var(--share-bright-rgb), 0.28);
}

.resource-battery__shell--yellow .resource-battery__body {
    border-color: rgba(var(--etat-vigilance-rgb), 0.28);
}

.resource-battery__shell--orange .resource-battery__body {
    border-color: rgba(var(--amber-deep-rgb), 0.32);
}

.resource-battery__shell--red .resource-battery__body {
    border-color: rgba(var(--red-rgb), 0.34);
}

.age-compare-card__summary {
    line-height: 1.35;
}

.age-compare-card__summary--tight {
    margin-top: 4px;
}
/* `.age-compare-card__summary strong` etait declare deux fois. Ce premier bloc
   etait entierement reecrit l.4221 : a selecteur et poids egaux,
   la derniere regle ecrite gagne, celle-ci n'imposait donc rien. */

.age-level-scale {
    margin-top: 6px;
}

.age-level-scale__title {
    font-size: 14px;
    font-weight: 700;
    color: var(--ink-200);
    margin-bottom: 10px;
}

.age-level-scale__track {
    display: grid;
    grid-template-columns: repeat(6, minmax(0, 1fr));
    gap: 8px;
    align-items: stretch;
}

.age-level-scale__cell {
    position: relative;
    min-height: 56px;
    padding: 10px 8px 8px;
    border-radius: 12px;
    background: rgba(var(--white-rgb), 0.04);
    border: 1px solid rgba(var(--white-rgb), 0.08);
    display: flex;
    align-items: flex-end;
    justify-content: center;
    text-align: center;
    transition:
        border-color 0.18s ease,
        background 0.18s ease,
        box-shadow 0.18s ease,
        transform 0.18s ease;
}

.age-level-scale__cell-label {
    font-size: 13px;
    line-height: 1.15;
    font-weight: 700;
    color: var(--ink-300);
}

.age-level-scale__cell.is-calendar {
    background: linear-gradient(180deg, rgba(var(--blue-rgb), 0.20), rgba(var(--white-rgb), 0.05));
    border-color: rgba(var(--blue-light-rgb), 0.45);
    box-shadow: 0 0 0 1px rgba(var(--blue-light-rgb), 0.12) inset,
        0 0 14px rgba(var(--blue-rgb), 0.12);
}

.age-level-scale__cell.is-calendar .age-level-scale__cell-label {
    color: var(--ink-50);
}

.age-level-scale__marker {
    position: absolute;
    top: -8px;
    left: 50%;
    transform: translateX(-50%);
    width: 22px;
    height: 22px;
    display: flex;
    align-items: center;
    justify-content: center;
    pointer-events: none;
}

.age-level-scale__marker-dot {
    width: 10px;
    height: 10px;
    border-radius: 999px;
    background: var(--surface-fond);
    border: 2px solid rgba(var(--slate-rgb), 0.9);
    box-shadow: 0 0 0 2px rgba(var(--white-rgb), 0.18),
        0 0 8px rgba(var(--white-rgb), 0.18);
}

.age-level-scale__legend {
    display: flex;
    flex-wrap: wrap;
    gap: 12px 18px;
    margin-top: 10px;
    font-size: 13px;
    color: var(--ink-300);
}

.age-level-scale__legend-item {
    display: inline-flex;
    align-items: center;
    gap: 8px;
}

.age-level-scale__legend-box {
    width: 14px;
    height: 14px;
    border-radius: 8px;
    background: linear-gradient(180deg, rgba(var(--blue-rgb), 0.20), rgba(var(--white-rgb), 0.05));
    border: 1px solid rgba(var(--blue-light-rgb), 0.45);
    box-shadow: 0 0 10px rgba(var(--blue-rgb), 0.10);
    transform: scale(0.9);
}

.age-level-scale__legend-marker {
    position: relative;
    width: 14px;
    height: 14px;
    border-radius: 999px;
    background: var(--surface-fond);
    border: 2px solid rgba(var(--slate-rgb), 0.9);
    transform: scale(0.9);
    
    box-shadow: 0 0 0 2px rgba(var(--white-rgb), 0.20),
        0 0 10px rgba(var(--white-rgb), 0.18);
}

.age-level-scale--compact {
    margin-top: 0;
    padding: 12px 14px 10px;
    border-radius: 14px;
    background: rgba(var(--white-rgb), 0.04);
    border: 1px solid rgba(var(--white-rgb), 0.08);
}

.age-level-scale__track--compact {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(52px, 1fr));
    gap: 0;
    overflow: hidden;
    border-radius: 12px;
    border: 1px solid rgba(var(--white-rgb), 0.08);
    background: rgba(var(--slate-rgb), 0.26);
}

.age-level-scale__cell--compact {
    min-height: 44px;
    padding: 6px 4px;
    border: 0;
    border-right: 1px solid rgba(var(--white-rgb), 0.06);
    border-radius: 0;
    background: transparent;
    justify-content: center;
    align-items: center;
}

.age-level-scale__cell--compact:last-child {
    border-right: 0;
}

.age-level-scale__cell--compact .age-level-scale__cell-label {
    font-size: 11px;
    line-height: 1;
    font-weight: 700;
    color: var(--ink-200);
}

.age-level-scale__cell--compact.is-calendar {
    background: linear-gradient(180deg, rgba(var(--blue-rgb), 0.22), rgba(var(--white-rgb), 0.04));
    box-shadow: inset 0 0 0 1px rgba(var(--blue-light-rgb), 0.22),
        0 0 16px rgba(var(--blue-rgb), 0.10);
}

.age-level-scale__cell--compact:not(.is-calendar) .age-level-scale__cell-label {
    color: rgba(var(--white-rgb), 0.0);
}

.age-level-scale__cell--compact .age-level-scale__marker {
    top: -4px;
}

.age-level-scale__legend--compact {
    margin-top: 8px;
    gap: 8px 14px;
    font-size: 12px;
}

@media (max-width: 640px) {
    .age-level-scale__track--compact {
        grid-template-columns: repeat(auto-fit, minmax(52px, 1fr));
    }

    .age-level-scale__cell--compact {
        min-height: 66px;
        padding: 10px 4px 8px;
    }

    .age-level-scale__cell--compact .age-level-scale__cell-label {
        font-size: 11px;
    }
}

@media (max-width: 640px) {
    .age-level-scale__track {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }

    .age-level-scale__cell {
        min-height: 52px;
    }
}

.brain-functions-age-layout {
    width: 100%;
    display: grid;
    gap: 14px;
}

.brain-functions-age-layout__top {
    display: grid;
    grid-template-columns: minmax(260px, 1fr) minmax(260px, 1fr);
    gap: 14px;
    align-items: stretch;
}

.brain-functions-age-layout__bottom {
    width: 100%;
}

.brain-functions-age-layout__top .age-compare-card,
.brain-functions-age-layout__top .brain-metric {
    margin: 0;
    height: 100%;
    box-sizing: border-box;
}

@media (max-width: 900px) {
    .brain-functions-age-layout__top {
        grid-template-columns: 1fr;
    }
}
/* ===== Therapy order buttons ===== */
.brain-system-block__topline {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: 16px;
    flex-wrap: wrap;
    /* Sans cette marge, le bouton d'action finissait exactement la ou
       commencait la liste en dessous : zero pixel entre les deux. */
    margin-bottom: 16px;
}

/*
 * LE RECAPITULATIF ET LE DOSAGE SE PARTAGENT LA MEME LIGNE.
 *
 * Le bandeau enveloppe, et le bloc de dosage retombait dessous : le libelle du
 * bouton, passe de « Appliquer a tous » a « Appliquer ce dosage a tous les
 * programmes », ne tenait plus dans ce qui restait. La place existe pourtant a
 * droite du cadre vert -- c'est le recapitulatif qui refusait de se serrer.
 *
 * `flex: 1 1 …` sur le recapitulatif le rend compressible, et l'alignement en
 * haut fait commencer les deux blocs au meme niveau. Centres, ils se calaient
 * l'un sur l'autre par leur milieu, ce qui ne veut rien dire quand ils n'ont
 * pas la meme hauteur.
 */
.therapy-draft-head {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: 16px;
    flex-wrap: wrap;
}

.therapy-order-summary {
    /* Compressible : sans cela il garde sa largeur de confort et pousse le
       dosage a la ligne suivante. */
    flex: 1 1 360px;
    min-width: 0;
    display: grid;
    gap: 5px;
    padding: 12px 14px;
    border-radius: 14px;
    border: 1px solid rgba(var(--white-rgb), 0.10);
    background: rgba(var(--slate-rgb), 0.30);
    line-height: 1.35;
}

.therapy-order-summary--success {
    background: linear-gradient(135deg, rgba(var(--share-bright-rgb), 0.18), rgba(var(--white-rgb), 0.035));
    border-color: rgba(var(--share-bright-rgb), 0.30);
}

.therapy-order-summary--danger {
    background: linear-gradient(135deg, rgba(var(--red-rgb), 0.22), rgba(var(--white-rgb), 0.035));
    border-color: rgba(var(--red-rgb), 0.42);
}

.therapy-order-summary__line {
    display: flex;
    flex-wrap: wrap;
    gap: 6px 5px;
    align-items: baseline;
    color: rgba(var(--ink-200-rgb), 0.92);
    font-size: 14px;
}

.therapy-order-summary__line strong {
    color: var(--white);
}

.therapy-order-summary__line span {
    color: rgba(var(--ink-200-rgb), 0.76);
}

.therapy-order-summary__warning {
    margin-top: 3px;
    color: var(--couleur-danger-doux);
    font-size: 13px;
    font-weight: 700;
}

.therapy-submit-actions {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
}

.therapy-submit-actions form {
    margin: 0;
}

@media (max-width: 760px) {
    .therapy-submit-actions {
        align-items: stretch;
        flex-direction: column;
    }

    .therapy-submit-actions form,
    .therapy-submit-actions button {
        width: 100%;
    }
}

.therapy-draft-head__meta {
    display: flex;
    flex-direction: column;
    justify-content: center;
}

.therapy-draft-head__dose {
    display: flex;
    align-items: center;
    flex: 0 1 auto;
    margin-left: auto;
}

.therapy-bulk-dose-form {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: nowrap;
}

.therapy-bulk-dose-form label {
    margin-bottom: 0;
    white-space: nowrap;
    line-height: 1;
}

.therapy-bulk-dose-form input[type="number"] {
    width: 96px;
    min-width: 96px;
    margin-bottom: 0;
    border-radius: 10px;
    padding: 8px 10px;
    line-height: 1;
}

.therapy-dose-help {
    white-space: nowrap;
    line-height: 1;
    color: rgba(var(--white-rgb), 0.78);
}

/*
 * CELUI-CI N'EST PAS DORE, ET C'EST LA SEULE EXCEPTION.
 *
 * L'or de `.therapy-action-btn` dit « ceci engage la commande » : ajouter un
 * programme, en retirer un. Ce bouton-la ne touche pas au contenu de la
 * commande, il recopie un reglage sur les lignes deja choisies. Il porte donc
 * le vert de ce qui s'applique et se constate.
 */
/*
 * IL CHANGE DE COULEUR EN ENTIER, pas seulement de fond.
 *
 * Le fond etait repeint en vert et le bouton gardait la bordure doree et la
 * lueur doree de `.therapy-action-btn` : un bouton vert cercle d'or, eclaire
 * en or. Une couleur ne se change pas par morceaux.
 */
.therapy-bulk-dose-form .therapy-action-btn {
    flex: 0 0 auto;
    white-space: nowrap;
    /* LE VERT DESCEND D UN CRAN, troisieme fois dans ce depot : le pixel peint
       rendait (22, 158, 72), ou le blanc mesure 3,49 pour 4,5 exiges. Le cran
       existait deja dans le fichier, c'etait celui du SURVOL ; le repos le
       prend, et le survol descend d'un cran de plus. */
    background: linear-gradient(135deg, #15803d, #116b33);
    border-color: rgba(var(--teinte-verte), 0.52);
    box-shadow: 0 8px 18px rgba(22, 163, 74, 0.28);
    color: var(--encre-sur-fond-fixe);
}

.therapy-bulk-dose-form .therapy-action-btn:hover {
    background: linear-gradient(135deg, #116b33, #0d5527);
    border-color: rgba(var(--teinte-verte), 0.72);
    box-shadow: 0 10px 22px rgba(22, 163, 74, 0.38);
}

.therapy-bulk-dose-form--combined {
    flex-wrap: wrap;
    justify-content: flex-end;
}

/*
 * LA RECOMMANDATION SE LIT SOUS LE CHAMP, PAS A COTE.
 *
 * Elle etait posee sur la meme ligne, entre le champ et le bouton : elle se
 * lisait comme un troisieme element de la commande alors qu'elle ne fait que
 * renseigner celui de gauche. Sous le couple « Dosage » + champ, elle est ce
 * qu'elle est : la valeur conseillee pour ce champ-la.
 *
 * ALIGNEE SUR « Dosage : », pas centree sous la rangee. Elle renseigne le
 * champ, donc elle commence la ou commence son intitule ; centree, elle se
 * rapportait a la ligne entiere, bouton compris.
 */
/*
 * LE CHAMP ET SA RECOMMANDATION SONT UNE COLONNE, le bouton est a cote.
 *
 * La recommandation mise a la ligne se calait sur le bord gauche du
 * formulaire, qui est loin a gauche de « Dosage : » puisque la rangee est
 * alignee a droite : elle flottait sous le vide. Dans une colonne avec le
 * couple qu'elle renseigne, elle commence exactement ou il commence.
 */
.therapy-dose-champ {
    display: flex;
    flex-direction: column;
    gap: 6px;
    align-items: flex-start;
}

.therapy-dose-champ__ligne {
    display: flex;
    align-items: center;
    gap: 12px;
}

.therapy-bulk-dose-form--combined .therapy-dose-help {
    margin: 0;
    text-align: left;
    white-space: normal;
}

@media (max-width: 900px) {
    .therapy-bulk-dose-form--combined {
        justify-content: flex-start;
    }
}

.therapy-action-btn,
.therapy-submit-btn,
.therapy-remove-btn {
    border: 0;
    border-radius: 12px;
    padding: 10px 14px;
    cursor: pointer;
    font: inherit;
}

/*
 * L'OR POUR CE QUI TOUCHE A LA COMMANDE DE PROGRAMMES.
 *
 * Ce bouton ajoute ou retire un programme de la commande. Il portait le vert
 * employe partout ailleurs pour dire « fait, valide, envoye » : le geste qui
 * ENGAGE une commande se lisait comme un geste qui CONSTATE.
 *
 * Le texte passe en sombre sur le fond plein. Du blanc sur cet or ne tient pas
 * les 4,5 de contraste qu'il faut a un libelle de cette taille ; le sombre le
 * porte a 7.
 */
.therapy-action-btn {
    /*
     * MEME FAMILLE QUE « Commander des programmes », EN PLUS SOBRE.
     *
     * Ce bouton-la va du dore au brun, soit 2,5 de rapport entre ses deux
     * bouts : c'est un bouton unique en haut de page, il peut se permettre
     * cet effet. Celui-ci se repete a chaque zone du resultat, et le meme
     * degrade repete dix fois fatigue la lecture. L'ecart tombe donc a 1,45.
     *
     * ET LE BLANC Y GAGNE : 5,20 de contraste sur le bout le plus clair,
     * contre 4,41 pour la reference, qui passe juste sous le seuil. Un
     * degrade plus clair etait exclu pour cette raison, pas par gout.
     */
    background: linear-gradient(135deg, #926D19, #624A11);
    /* La regle groupee plus haut met `border: 0` : sans la declarer en
       entier, la bordure ne s'affiche pas. `border-box` evite que les deux
       pixels ajoutes ne decalent le bouton de son voisin. */
    box-sizing: border-box;
    border: 0.8px solid rgba(var(--teinte-or), 0.52);
    color: var(--encre-sur-fond-fixe);
    font-weight: 500;
    /*
     * VARIANTE F : la lueur PLEINE, plus la moitie.
     *
     * Elle etait divisee par deux parce que ce bouton se repete a chaque
     * zone du resultat, et qu'un effet repete dix fois fatigue. L'Admin a
     * tranche : les actions directes sonnent pareil partout, meme repetees.
     */
    box-shadow: 0 8px 18px rgba(152, 114, 27, 0.28);
}

/*
 * LE SURVOL NE REPEINT PLUS RIEN.
 *
 * Il avait son propre degrade, ce qui obligeait a maintenir deux paires de
 * couleurs par bouton et laissait passer un blanc a 4,48 sur le dore
 * eclairci. Le geste est desormais celui de la fiche : le bouton monte d'un
 * pixel et gagne huit pour cent de clarte. Une seule ligne, valable sur
 * n'importe quel fond, degrade compris.
 */
.therapy-action-btn:hover {
    border-color: rgba(var(--teinte-or), 0.62);
}

/*
 * RETIRER N'EST PAS AJOUTER : le bouton se creuse au lieu de se remplir et
 * reprend le poids normal. L'action de retrait ne doit pas peser autant que
 * celle qui engage la commande.
 *
 * L'anneau lui rend sa forme : creux, il ne se detache de son fond qu'a 1,4 de
 * rapport, et il ne resterait que du texte flottant. Il est INTERIEUR, donc il
 * n'agrandit pas le bouton et ne le desaligne pas de son voisin plein.
 */
.therapy-action-btn--active {
    background: rgba(var(--teinte-or), 0.16);
    color: rgb(var(--teinte-or));
    font-weight: 400;
    box-shadow: inset 0 0 0 1px rgba(var(--teinte-or), 0.42);
}

.therapy-action-btn--active:hover {
    background: rgba(var(--teinte-or), 0.26);
    color: rgb(var(--teinte-or));
}

/*
 * IL ENGAGE LA COMMANDE, DONC IL EST DORE.
 *
 * « Ajouter l'ensemble a la commande » faisait exactement ce que fait
 * « Ajouter le programme a la commande » juste a cote, en dix exemplaires, et
 * les deux n'avaient pas la meme couleur : l'un vert, l'autre or. Le vert dit
 * « fait, valide » ; ajouter n'est ni l'un ni l'autre.
 *
 * Meme degrade que `.therapy-action-btn`, pour la meme raison de contraste :
 * le blanc y tient 5,20 sur le bout le plus clair.
 */
.therapy-submit-btn {
    background: linear-gradient(135deg, #926D19, #624A11);
    color: var(--encre-sur-fond-fixe);
}

/* LE SURVOL N ECLAIRCIT PAS L OR, ET LE COMMENTAIRE D AVANT DISAIT LE
   CONTRAIRE DE CE QUE FAIT SON VOISIN. `.therapy-action-btn:hover` ne touche
   qu'a son BORD ; celui-ci eclaircissait son aplat jusqu'a `#B08820`, ou le
   blanc mesure 3,29 pour 4,5 exiges. L'or au repos est deja a son plafond --
   4,75 -- donc il n'y a pas de place pour l'eclaircir sans perdre le texte.

   Le survol se dit donc par le bord, comme chez son voisin, et les deux
   boutons se repondent enfin. Trouve en forcant l'etat survole : le releve des
   etats ne regarde qu'une liste de selecteurs ecrite a la main, et celui-ci
   n'y figurait pas. */
.therapy-submit-btn:hover {
    border: 1px solid rgba(var(--teinte-or), 0.62);
    color: var(--encre-sur-fond-fixe);
}

.therapy-remove-btn {
    background: rgba(var(--red-rgb), 0.46);
    color: var(--couleur-danger-doux);
}

.therapy-remove-btn:hover {
    background: rgba(var(--red-rgb), 0.26);
    color: var(--couleur-danger-doux);
}

/* ===== Therapy help button in header ===== */

.therapy-order-card-head__actions {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 10px;
    flex-wrap: wrap;
    margin-left: auto;
}

.therapy-order-card-head__actions .btn {
    width: auto;
}

@media (max-width: 640px) {
    .therapy-order-card-head__actions {
        width: 100%;
        justify-content: flex-start;
        align-items: stretch;
    }

    .therapy-order-card-head__actions .btn {
        width: 100%;
    }

    .therapy-order-card-head__actions .help-trigger {
        width: 34px;
        min-width: 34px;
    }
}
/* `.organ-card` etait declare deux fois. Ce premier bloc
   etait entierement reecrit l.4448 : a selecteur et poids egaux,
   la derniere regle ecrite gagne, celle-ci n'imposait donc rien. */

.organ-card__process-map {
    display: flex;
    align-items: stretch;
    width: 100%;
    max-width: 100%;
    height: 9px;
    margin-top: 10px;
    overflow: hidden;
    border-radius: 999px;
    background: rgba(var(--white-rgb), 0.08);
}

.organ-card__process-segment {
    display: block;
    flex-basis: 0;
    min-width: 0;
    height: 100%;
    opacity: 0.95;
}

.organ-card__process-segment + .organ-card__process-segment {
    margin-left: 2px;
}

.organ-card__process-segment--danger {
    background: rgba(var(--red-rgb), 0.95);
}

.organ-card__process-segment--warning {
    background: rgba(var(--etat-vigilance-rgb), 0.95);
}

.organ-card__process-segment--safe {
    background: rgba(var(--share-bright-rgb), 0.95);
}

/* LE RAIL DES PROCESSUS NE S ECLAIRCIT PLUS AU SURVOL. Il passait de 0,08 a
   0,13 : ce qui se voyait n'etait pas la carte qui repond, c'etait la bande
   VIDE derriere les barres colorees qui grisait. Le survol de la carte se dit
   deja par sa lueur ; ce rail-la porte une mesure, il n'a pas a bouger avec
   l'humeur du pointeur. */

.organ-card__therapy-mark {
    position: absolute;
    top: 8px;
    right: 8px;
    width: 20px;
    height: 20px;
    border-radius: 999px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 12px;
    background: rgba(var(--share-bright-rgb), 0.8);
    color: var(--green-pale);
}

.organ-card.is-therapy-selected {
    box-shadow: 0 0 0 1px rgba(var(--share-bright-rgb), 0.35) inset;
}

.organ-side-card__therapy {
    margin-top: 12px;
}

.therapy-items {
    display: grid;
    gap: 12px;
    margin-top: 16px;
}

.therapy-item {
    display: flex;
    justify-content: space-between;
    gap: 16px;
    align-items: center;
    padding: 14px;
    border-radius: 14px;
    background: rgba(var(--white-rgb), 0.04);
}

.therapy-item__title {
    font-weight: 600;
    margin-bottom: 6px;
}

.therapy-item__meta {
    display: flex;
    gap: 14px;
    flex-wrap: wrap;
    font-size: 14px;
    opacity: 0.9;
}

.therapy-item__sublist {
    margin-top: 10px;
    font-size: 14px;
}

.therapy-item__chips {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: 8px;
}

.therapy-item__chip {
    display: inline-flex;
    align-items: center;
    padding: 6px 10px;
    border-radius: 999px;
    background: rgba(100, 148, 86, 0.6);
    font-size: 15px;
    line-height: 1;
}

.therapy-item__notice {
    margin-top: 8px;
    font-size: 13px;
    color: var(--amber-pale);
    opacity: 0.95;
}

.therapy-submit-wrap {
    margin-top: 18px;
}

@media (max-width: 900px) {
    .therapy-draft-head {
        align-items: flex-start;
    }

    .therapy-draft-head__dose {
        width: 100%;
        margin-left: 0;
    }

    .therapy-bulk-dose-form {
        flex-wrap: wrap;
        align-items: flex-start;
    }

    .therapy-dose-help {
        white-space: normal;
    }
}

/* ===== Resource Bio/Cal age OLD ===== */
.brain-functions-metrics--age {
    margin-top: 0;
}

.brain-metric__subvalue {
    display: inline-block;
    margin-left: 6px;
    font-size: 0.9rem;
    opacity: 0.75;
}

.brain-metric__hint {
    margin-top: 8px;
    font-size: 0.9rem;
    line-height: 1.4;
    opacity: 0.8;
}

.brain-metric--good {
    border-color: rgba(var(--share-bright-rgb), 0.35);
    box-shadow: 0 0 0 1px rgba(var(--share-bright-rgb), 0.08) inset;
}

.brain-metric--warn {
    border-color: rgba(var(--etat-vigilance-rgb), 0.35);
    box-shadow: 0 0 0 1px rgba(var(--etat-vigilance-rgb), 0.08) inset;
}

.brain-metric--danger {
    border-color: rgba(var(--red-rgb), 0.35);
    box-shadow: 0 0 0 1px rgba(var(--red-rgb), 0.08) inset;
}

.age-compare-card {
    margin-top: 16px;
    padding: 16px 18px;
    border-radius: 18px;
    background: rgba(var(--white-rgb), 0.04);
    border: 1px solid rgba(var(--white-rgb), 0.08);
}

.age-compare-card__row + .age-compare-card__row {
    margin-top: 14px;
}

.age-compare-card__label {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 12px;
    margin-bottom: 8px;
    font-size: 14px;
    color: var(--ink-300);
}

.age-compare-card__label span {
    color: var(--ink-50);
    font-size: 15px;
    font-weight: 700;
}

.age-bar {
    position: relative;
    width: 100%;
    height: 18px;
    border-radius: 999px;
    overflow: hidden;
    background: linear-gradient(to right, rgba(var(--white-rgb), 0.06), rgba(var(--white-rgb), 0.1));
    border: 1px solid rgba(var(--white-rgb), 0.08);
}

.age-bar__fill {
    height: 100%;
    border-radius: 999px;
    transition: width 0.35s ease;
}

.age-bar__fill--calendar {
    background: linear-gradient(90deg, rgba(var(--slate-light-rgb), 0.9), rgba(var(--blue-light-rgb), 0.9));
}

.age-bar__fill--good {
    background: linear-gradient(90deg, rgba(var(--green-rgb),0.95), rgba(var(--share-bright-rgb), 0.95));
}

.age-bar__fill--warn {
    background: linear-gradient(90deg, rgba(var(--etat-vigilance-rgb), 0.95), rgba(var(--etat-vigilance-rgb), 0.95));
}

.age-bar__fill--danger {
    background: linear-gradient(90deg, rgba(var(--teinte-rouge), 0.95), rgba(var(--red-rgb), 0.95));
}

.age-compare-card__summary {
    margin-top: 14px;
    font-size: 14px;
    color: var(--ink-300);
}

.age-compare-card__summary strong {
    color: var(--ink-50);
}

/* ===== PFS Main ===== */
.brain-system-block {
    margin-top: 22px;
    padding-top: 20px;
    border-top: 1px solid rgba(var(--white-rgb), 0.08);
}

.brain-system-block__title {
    margin: 0 0 6px;
    font-size: 1.25rem;
    font-weight: 700;
}

.brain-system-block__hint {
    margin-bottom: 14px;
    line-height: 1.45;
}

.brain-system-organ__header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 10px;
}

.brain-system-organ__title {
    position: relative;
    text-shadow: 0 0 8px rgba(var(--black-rgb), 0.38);
}

/* чтобы плашки не уезжали вниз */
.brain-system-organ__stats {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    justify-content: flex-end;
}

.brain-system-organs {
    display: flex;
    flex-direction: column;
    gap: 16px;
}

.brain-system-organ {
    position: relative;
    border-radius: 18px;
    padding: 16px 18px;
    background: rgba(var(--white-rgb), 0.04);
    border: 1px solid rgba(var(--white-rgb), 0.08);
    overflow: hidden;
}

.brain-system-organ::before {
    content: "";
    position: absolute;
    inset: 0 auto 0 0;
    width: 4px;
    border-radius: 18px 0 0 18px;
    background: rgba(var(--white-rgb), 0.18);
}

.brain-system-organ--danger::before {
    background: rgba(var(--red-rgb), 0.85);
}

.brain-system-organ--warning::before {
    background: rgba(var(--etat-vigilance-rgb), 0.85);
}

.brain-system-organ--safe::before {
    background: rgba(var(--share-bright-rgb), 0.85);
}

.brain-system-organ__stats {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-bottom: 12px;
}

.brain-pill {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 10px;
    border-radius: 999px;
    font-size: 14px;
    line-height: 1.2;
    background: rgba(var(--white-rgb), 0.06);
    color: var(--ink-200);
}

.brain-pill--red {
    background: rgba(var(--red-rgb), 0.24);
    border: 1px solid rgba(var(--red-rgb), 0.24);
}

.brain-pill--blue {
    background: rgba(var(--blue-rgb), 0.24);
    border: 1px solid rgba(var(--blue-rgb), 0.24);
}

.brain-pill--neutral {
    background: rgba(var(--white-rgb), 0.06);
    border: 1px solid rgba(var(--white-rgb), 0.08);
}

.brain-system-organ__main {
    min-width: 0;
}

.brain-system-organ {
    transition: all .18s ease;
}

.brain-system-organ:hover {
    transform: translateY(-2px);
    box-shadow: 0 12px 28px rgba(var(--black-rgb), 0.25);
}

.brain-system-organ__title {
    margin: 0 0 10px;
    font-size: 1.1rem;
    line-height: 1.35;
    font-weight: 700;
    color: var(--ink-50);
}

.brain-system-organ__meta {
    color: var(--ink-300);
    font-size: 0.98rem;
    line-height: 1.45;
    margin-bottom: 12px;
}

.brain-system-organ__psycho {
    margin-top: 10px;
    padding: 12px 14px;
    border-radius: 14px;
    background: rgba(var(--slate-rgb), 0.22);
    border: 1px solid rgba(var(--white-rgb), 0.05);
}

.brain-system-organ__eyebrow {
    margin-bottom: 8px;
    font-size: 0.78rem;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--texte-discret);
}

.brain-system-organ__psycho-text {
    font-size: 0.98rem;
    line-height: 1.6;
    color: var(--ink-200);
}

@media (max-width: 980px) {
    .brain-system-organ__top {
        grid-template-columns: 1fr;
    }
}

/* ===== Organs ===== */

.organs-intro {
    margin: 8px 0 16px;
    display: grid;
    gap: 6px;
}

.organs-intro .organ-side-card__hint {
    margin: 0;
}

.organs-color-note {
    max-width: 980px;
    font-size: 13px;
    line-height: 1.45;
    color: rgba(197, 241, 208, 0.82);
}

@media (max-width: 640px) {
    .organs-color-note {
        font-size: 12px;
    }
}

/*
 * TROIS COLONNES TANT QU'IL Y A LA PLACE, PAS AU-DELA.
 *
 * Aucune regle media n'existait : les 27 cartes d'organes gardaient leurs trois
 * colonnes a 375 px, et leur contenu se retrouvait tronque, chiffres compris —
 * le bloc « Charge / Ressource » demandait 97 px et en recevait 54, sous un
 * `overflow: hidden`. On perdait donc la valeur qu'on vient lire.
 */
.organs-grid {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 14px;
}

@media (max-width: 900px) {
    .organs-grid,
    /*
 * LE LIEN VERS LA FICHE, sous le texte du repere.
 *
 * Il ne se deguise pas en bouton : c'est une phrase qu'on peut ouvrir, et le
 * souligne pointille dit « il y a plus a lire ici » sans peser autant qu'un
 * bouton de plus dans un panneau qui en porte deja trois.
 *
 * LE TRAIT EST UNE EXCEPTION ASSUMEE. Ailleurs, les elements interrogeables
 * n'en portent pas : sur une grille de cinquante-six, cinquante-six soulignes
 * balisent au lieu d'indiquer. Ici il y en a UN, isole sous un paragraphe, et
 * sans trait personne ne penserait a cliquer une phrase.
 */
.organ-fiche-lien {
    margin: 10px 0 0;
    font-size: 14px;
    line-height: 1.5;
}

.organ-fiche-lien__declencheur {
    color: rgba(var(--blue-light-rgb), 0.92);
    border-bottom: 1px dotted rgba(var(--blue-light-rgb), 0.5);
    cursor: pointer;
}

.organ-fiche-lien__declencheur:hover,
.organ-fiche-lien__declencheur:focus-visible {
    color: rgba(var(--blue-light-rgb), 1);
    border-bottom-color: rgba(var(--blue-light-rgb), 0.9);
}

.organs-grid--grouped {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

@media (max-width: 560px) {
    .organs-grid,
    .organs-grid--grouped {
        grid-template-columns: minmax(0, 1fr);
    }
}

.organ-card {
    appearance: none;
    width: 100%;
    text-align: left;
    cursor: pointer;
    display: flex;
    flex-direction: column;
    gap: 12px;
    text-decoration: none;
    color: inherit;
    position: relative;
    overflow: hidden;

    border-radius: 18px;
    padding: 16px 18px;
    border: 1px solid rgba(var(--white-rgb), 0.08);
    background: rgba(var(--white-rgb), 0.04);

    transition:
        transform 0.18s ease,
        border-color 0.18s ease,
        box-shadow 0.18s ease,
        background 0.18s ease;
}

.organ-card::before {
    content: "";
    position: absolute;
    inset: 0 auto 0 0;
    width: 4px;
    border-radius: 18px 0 0 18px;
    background: rgba(var(--white-rgb), 0.18);
}

.organ-card:hover {
    transform: translateY(-2px);
    box-shadow: 0 12px 28px rgba(var(--black-rgb), 0.25);
}

.organ-card.is-active {
    box-shadow: 0 0 0 1px rgba(var(--white-rgb), 0.08) inset,
        0 0 0 3px rgba(var(--white-rgb), 0.16) inset,
        0 12px 28px rgba(var(--black-rgb), 0.28);
}

.organ-card__name {
    font-size: 15px;
    line-height: 1.35;
    color: var(--ink-50);

    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
    min-height: 4.05em;
}

.organ-card__related {
    display: block;
    color: rgba(var(--ink-300-rgb), 0.82);
    font-size: 12px;
    line-height: 1.25;
    font-weight: 500;

    text-align: left;
}

.organ-card__meta {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: auto;
}

.organ-card__meta span {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 10px;
    border-radius: 999px;
    font-size: 0.92rem;
    line-height: 1.2;
    color: var(--ink-200);
    background: rgba(var(--white-rgb), 0.06);
    border: 1px solid rgba(var(--white-rgb), 0.08);
    font-size: 14px;   
}

.organ-card__meta span:first-child {
    background: rgba(var(--blue-rgb), 0.14);
    border-color: rgba(var(--blue-rgb), 0.24);
    color: var(--blue-mist);
 
}

.organ-card__meta span:last-child {
    background: rgba(var(--red-rgb), 0.14);
    border-color: rgba(var(--red-rgb), 0.24);
    color: var(--couleur-danger-doux);
}

.organ-card__meta span.organ-card__meta-value--blue {
    background: rgba(var(--blue-rgb), 0.14);
    border-color: rgba(var(--blue-rgb), 0.24);
    color: var(--blue-mist);
}

.organ-card__meta span.organ-card__meta-value--red {
    background: rgba(var(--red-rgb), 0.14);
    border-color: rgba(var(--red-rgb), 0.24);
    color: var(--couleur-danger-doux);
}
/* `.organ-description` etait declare deux fois. Ce premier bloc
   etait entierement reecrit l.4979 : a selecteur et poids egaux,
   la derniere regle ecrite gagne, celle-ci n'imposait donc rien. */

.organ-card--danger::before {
    background: rgba(var(--red-rgb), 0.85);
}

.organ-card--warning::before {
    background: rgba(var(--etat-vigilance-rgb), 0.85);
}

.organ-card--safe::before {
    background: rgba(var(--share-bright-rgb), 0.85);
}

.organ-card--neutral::before {
    background: rgba(var(--slate-light-rgb), 0.55);
}

.organ-card--danger {
    border-color: rgba(var(--red-rgb), 0.18);
    background: linear-gradient(180deg, rgba(var(--red-rgb), 0.14), rgba(var(--white-rgb), 0.02));
}

.organ-card--warning {
    border-color: rgba(var(--etat-vigilance-rgb), 0.18);
    background: linear-gradient(180deg, rgba(var(--etat-vigilance-rgb), 0.14), rgba(var(--white-rgb), 0.02));
}

.organ-card--safe {
    border-color: rgba(var(--share-bright-rgb), 0.18);
    background: linear-gradient(180deg, rgba(var(--share-bright-rgb), 0.14), rgba(var(--white-rgb), 0.02));
}

.organ-card--neutral {
    border-color: rgba(var(--slate-light-rgb), 0.12);
    background: rgba(var(--white-rgb), 0.04);
    opacity: 0.75;
}

.organ-card[data-tooltip] {
    overflow: visible;
}

.organ-card[data-tooltip]:hover {
    z-index: 60;
}

.organ-card[data-tooltip]::after {
    content: attr(data-tooltip);
    position: absolute;
    bottom: calc(100% + 8px);
    left: 50%;
    transform: translateX(-50%) translateY(4px);

    max-width: min(320px, calc(100vw - 32px));
    width: max-content;

    background: rgba(var(--slate-rgb), 0.96);
    color: var(--white);

    padding: 7px 10px;
    border-radius: 8px;
    font-size: 12px;
    font-weight: 600;
    line-height: 1.35;
    white-space: normal;

    opacity: 0;
    pointer-events: none;
    transition: 0.18s ease;
    z-index: 200;

    box-shadow: 0 6px 20px rgba(var(--black-rgb), 0.3);
}

.organ-card[data-tooltip]:hover::after {
    opacity: 1;
    transform: translateX(-50%) translateY(0);
}

.organ-card[data-tooltip=""]::after {
    display: none;
}

/* complexes */

.complexes-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: 12px;
    padding-top: 10px;
}

.complex-card {
    border-radius: 14px;
    padding: 14px 16px;
    border: 1px solid rgba(var(--white-rgb), 0.08);
    display: flex;
    justify-content: space-between;
    position: relative;    
    gap: 14px;
    align-items: flex-start;
    background: rgba(var(--white-rgb), 0.04);
    transition: transform 0.18s ease, box-shadow 0.18s ease, border-color 0.18s ease;
    cursor: default;
    user-select: none;
    -webkit-user-select: none;
    -webkit-touch-callout: none;    
}

.complex-card:hover {
    transform: translateY(-1px);
    box-shadow: 0 10px 24px rgba(var(--black-rgb), 0.18);
}

.complex-card::after {
    content: attr(data-tooltip);
    position: absolute;
    bottom: 90%;
    left: 50%;
    transform: translateX(-50%);
    
    background: rgba(var(--slate-rgb), 0.95);
    color: var(--white);
    
    padding: 6px 10px;
    border-radius: 8px;
    font-size: 12px;
    white-space: nowrap;
    
    opacity: 0;
    pointer-events: none;
    transition: .18s ease;
    
    box-shadow: 0 6px 20px rgba(var(--black-rgb), 0.3);
}

.complex-card:hover::after {
    opacity: 1;
}

.complex-card__name {
    line-height: 1.35;
    color: var(--ink-50);
}

.complex-card__desc {
    margin-top: 6px;
    font-size: 13px;
    line-height: 1.45;
    color: var(--ink-300);
    opacity: 0.9;
}

.complex-card__value {
    font-weight: 700;
    white-space: nowrap;
    padding: 4px 8px;
    border-radius: 999px;
    background: rgba(var(--white-rgb), 0.06);
    border: 1px solid rgba(var(--white-rgb), 0.08);
}

.complex-card--danger {
    background: linear-gradient(180deg, rgba(var(--red-rgb), 0.15), rgba(var(--white-rgb), 0.02));
    border-color: rgba(var(--red-rgb), 0.22);
}

.complex-card--danger .complex-card__value {
    color: var(--couleur-danger-doux);
    background: rgba(var(--red-rgb), 0.14);
    border-color: rgba(var(--red-rgb), 0.24);
}

.complex-card--warning {
    background: linear-gradient(180deg, rgba(var(--etat-vigilance-rgb), 0.15), rgba(var(--white-rgb), 0.02));
    border-color: rgba(var(--etat-vigilance-rgb), 0.22);
}

.complex-card--warning .complex-card__value {
    color: var(--amber-pale);
    background: rgba(var(--etat-vigilance-rgb), 0.14);
    border-color: rgba(var(--etat-vigilance-rgb), 0.24);
}

.complex-card--safe {
    background: linear-gradient(180deg, rgba(var(--share-bright-rgb), 0.15), rgba(var(--white-rgb), 0.02));
    border-color: rgba(var(--share-bright-rgb), 0.22);
}

.complex-card--safe .complex-card__value {
    color: var(--couleur-succes-doux);
    background: rgba(var(--share-bright-rgb), 0.14);
    border-color: rgba(var(--share-bright-rgb), 0.24);
}

.has-soft-tooltip {
    position: relative;
    cursor: help;
}

.quality-card.has-soft-tooltip:hover,
.filter-bar-card.has-soft-tooltip:hover,
.qualities-chip.has-soft-tooltip:hover {
    z-index: 80;
}

/* L INFOBULLE N EXISTE QU AU SURVOL, ET C EST UNE CORRECTION DE MISE EN PAGE.

   Elle etait POSEE EN PERMANENCE : son texte etait rendu des le chargement, en
   `width: max-content` et jusqu'a 320 px, centre sur son element. Sur une
   mesure emotionnelle de 113 px elle debordait donc de 103 px a droite.
   Invisible (`opacity: 0`), sans prise au clic (`pointer-events: none`), mais
   PRESENTE dans la mise en page -- et la mise en page compte dans la largeur
   defilable du document.

   C'est ce qui donnait a « Evolution personnelle » une barre de defilement
   horizontale a TOUTES les largeurs : +37 px a 1440, +28 a 1024, +73 a 768,
   +75 a 390. Quatre autres pistes ont ete essayees et refutees avant celle-ci
   -- la coupure des mots, le repli des tableaux, la longueur des barres
   couchees, le repli de la rangee -- chacune a zero pixel de gain.

   LA BOITE RESTE, VIDE. Seul son contenu attend le survol : elle vaut donc
   zero de large au repos, et le fondu a l'apparition ne change pas, puisque
   c'est l'opacite qui l'anime et non le contenu. */
.has-soft-tooltip::after {
    content: "";
    position: absolute;
    bottom: calc(100% + 8px);
    left: 50%;
    transform: translateX(calc(-50% + var(--tooltip-shift-x, 0px))) translateY(4px);

    max-width: min(320px, calc(100vw - 32px));
    max-width: var(--tooltip-max-width, min(320px, calc(100vw - 32px)));
    width: max-content;

    background: rgba(var(--slate-rgb), 0.96);
    color: var(--white);

    padding: 7px 10px;
    border-radius: 8px;
    font-size: 12px;
    font-weight: 600;
    line-height: 1.35;
    white-space: normal;

    opacity: 0;
    pointer-events: none;
    transition: 0.18s ease;
    z-index: 200;

    box-shadow: 0 6px 20px rgba(var(--black-rgb), 0.3);
}

.has-soft-tooltip::before {
    content: "";
    position: absolute;
    bottom: calc(100% + 3px);
    left: 50%;
    transform: translateX(-50%) translateY(4px);

    width: 8px;
    height: 8px;
    background: rgba(var(--slate-rgb), 0.96);
    rotate: 45deg;

    opacity: 0;
    pointer-events: none;
    transition: 0.18s ease;
    z-index: 199;
}

/* Le texte n'arrive qu'ici. Le focus clavier l'obtient aussi : une infobulle
   qui ne repond qu'a la souris n'existe pas pour qui navigue au clavier. */
.has-soft-tooltip:hover::after,
.has-soft-tooltip:focus-visible::after {
    content: attr(data-tooltip);
}

.has-soft-tooltip:hover::after,
.has-soft-tooltip:focus-visible::after {
    opacity: 1;
    transform: translateX(calc(-50% + var(--tooltip-shift-x, 0px))) translateY(0);
}

.has-soft-tooltip:hover::before,
.has-soft-tooltip:focus-visible::before {
    opacity: 1;
    transform: translateX(-50%) translateY(0);
}

.has-soft-tooltip:not([data-tooltip])::after,
.has-soft-tooltip:not([data-tooltip])::before,
.has-soft-tooltip[data-tooltip=""]::after,
.has-soft-tooltip[data-tooltip=""]::before {
    display: none;
}

.quality-card.has-soft-tooltip[data-tooltip]::after {
    display: none;

    width: var(--tooltip-max-width, min(520px, calc(100vw - 48px))) !important;
    max-width: var(--tooltip-max-width, min(520px, calc(100vw - 48px))) !important;
    box-sizing: border-box;

    white-space: pre-line !important;
    text-align: left;
    font-weight: 500;
    line-height: 1.45;
    font-size: 12px;

    overflow-wrap: break-word;
    word-break: normal;
}
/* CES 2 REGLES ETAIENT DECLAREES DEUX FOIS.

   Ce premier jeu etait entierement reecrit plus bas (l.4867).
   A selecteur et poids egaux, la derniere ecrite gagne : rien de ce qui
   vivait ici ne s'appliquait, et le lire donnait une fausse idee de ce
   que l'ecran fait.

     `.quality-card.has-soft-tooltip[data-tooltip]::before`
     `.quality-card.has-soft-tooltip[data-tooltip]:hover::after, .quality-card.has-soft-tooltip[data-tooltip]:hover::before` */

.quality-card.has-soft-tooltip[data-tooltip]::before {
    display: none;
}

.quality-card.has-soft-tooltip[data-tooltip]:hover::after,
.quality-card.has-soft-tooltip[data-tooltip]:hover::before {
    display: block;
}

@media (max-width: 640px) {
    .quality-card.has-soft-tooltip[data-tooltip]::after {
        width: min(360px, calc(100vw - 32px)) !important;
        max-width: min(360px, calc(100vw - 32px)) !important;
    }
}

/* ===== Organs split layout ===== */

.organs-layout {
    display: grid;
    grid-template-columns: minmax(0, 2fr) minmax(320px, 1fr);
    gap: 18px;
    align-items: start;
}

.organs-layout__main,
.organs-layout__side {
    position: sticky;
    top: 18px;
    align-self: start;
}

.organ-side-card {
    display: flex;
    flex-direction: column;
    min-height: 0;
    max-height: calc(100vh - 36px);
    margin-bottom: 0;
}

.organ-side-card__header {
    flex: 0 0 auto;
    padding-bottom: 14px;
    border-bottom: none;
    margin-bottom: 14px;
}

.organ-side-card__title {
    margin: 0 0 12px;
    font-size: 22px;
    line-height: 1.2;
    font-weight: 700;
    color: rgba(var(--white-rgb), 0.96);
}

.organ-side-card__related {
    margin-top: 18px;
}

.organ-side-card__eyebrow {
    font-size: 12px;
    line-height: 1.2;
    letter-spacing: 0.08em;
    color: rgba(var(--white-rgb), 0.48);
    margin-bottom: 6px;
    text-transform: uppercase;
}

.organ-side-card__organ-name {
    font-size: 17px;
    line-height: 1.35;
    font-weight: 700;
    color: rgba(var(--white-rgb), 0.9);
    margin-bottom: 0;
}

.organ-side-card__legend {
    margin-top: 8px;
    display: flex;
    justify-content: center;
    gap: 14px;
    font-size: 12px;
    opacity: 0.75;
    flex-wrap: wrap;
}

.legend-item {
    position: relative;
    padding-left: 12px;
}

.legend-item::before {
    content: "";
    position: absolute;
    left: 0;
    top: 5px;
    width: 6px;
    height: 6px;
    border-radius: 50%;
}

.legend-item--red::before {
    background: var(--couleur-danger);
}

.legend-item--yellow::before {
    background: var(--amber-bright);
}

.legend-item--green::before {
    background: var(--couleur-succes);
}
.legend-item:hover {
    opacity: 1;
}

.organ-description {
    margin-top: 14px;
    padding: 12px 14px;
    border-radius: 14px;
    background: rgba(var(--slate-rgb), 0.22);
    border: 1px solid rgba(var(--white-rgb), 0.05);
    font-size: 15px;
    line-height: 1.6;
    color: rgba(var(--white-rgb), 0.9);
}

#organ-complexes-empty {
    margin-bottom: 12px;
}

#organ-complexes-grid {
    overflow-y: auto;
    min-height: 0;
    padding-right: 6px;

    scrollbar-width: thin;
    scrollbar-color: var(--barre-pouce) var(--barre-piste);
}

#organ-complexes-grid::-webkit-scrollbar {
    width: 10px;
}

#organ-complexes-grid::-webkit-scrollbar-track {
    background: transparent;
}

#organ-complexes-grid::-webkit-scrollbar-thumb {
    background: linear-gradient(
        180deg,
        rgba(var(--white-rgb), 0.18),
        rgba(var(--white-rgb), 0.08)
    );
    border-radius: 999px;
    border: 2px solid transparent;
    background-clip: padding-box;
}

#organ-complexes-grid::-webkit-scrollbar-thumb:hover {
    background: linear-gradient(
        180deg,
        rgba(var(--white-rgb), 0.28),
        rgba(var(--white-rgb), 0.14)
    );
    border: 2px solid transparent;
    background-clip: padding-box;
}

.organs-side-header {
    align-items: flex-start;
    gap: 12px;
}

.organs-side-nav {
    display: flex;
    gap: 8px;
    flex-shrink: 0;
}

.complexes-grid--side {
    grid-template-columns: 1fr;
}

@media (max-width: 1100px) {
    .organs-layout {
        grid-template-columns: 1fr;
    }

    .organs-layout__side {
        position: static;
    }
}
.organs-toolbar {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: 16px;
    flex-wrap: wrap;
}

.organs-toolbar__title {
    margin: 0;
    flex: 1 1 auto;
    padding-top: 4px;
}

.organs-toolbar__controls {
    display: flex;
    flex-direction: row;
    gap: 10px;
    align-items: center;
    justify-content: flex-end;
    flex: 0 1 auto;
    flex-wrap: wrap;
}

.organs-toolbar__controls .consultation-section-share__form {
    margin: 0;
}

.organs-toolbar__controls .consultation-section-share__button {
    min-height: 34px;
    padding: 7px 10px;
    white-space: nowrap;
}

.organs-toolbar__select {
    width: auto;
    min-width: 172px;
    max-width: 190px;
    height: 34px;
    margin-bottom: 0;
    padding: 7px 10px;
    border-radius: 10px;
    font-size: 14px;
}

@media (max-width: 900px) {
    .organs-toolbar__controls {
        width: 100%;
        flex-wrap: wrap;
        justify-content: flex-start;
    }

    .organs-toolbar__select {
        min-width: 0;
        max-width: none;
        flex: 1 1 180px;
    }
}

@media (max-width: 640px) {
    .organs-toolbar__controls {
        align-items: stretch;
    }

    .organs-toolbar__controls .consultation-section-share__form,
    .organs-toolbar__controls .consultation-section-share__button {
        width: 100%;
    }

    .organs-toolbar__select {
        width: 100%;
        flex: 1 1 100%;
    }

    .organs-toolbar__controls .help-trigger {
        width: 34px;
        min-width: 34px;
    }
}

.organs-groups {
    display: flex;
    flex-direction: column;
    gap: 22px;
}

.organs-system-section {
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.organs-system-title {
    position: relative;
    margin: 0 0 2px;
    padding: 10px 14px 10px 16px;
    border-radius: 14px;
    background: rgba(var(--slate-rgb), 0.26);
    border: 1px solid rgba(var(--white-rgb), 0.08);
    color: rgba(var(--white-rgb), 0.96);
    font-size: 20px;
    font-weight: 700;
    line-height: 1.25;
    box-shadow: inset 0 1px 0 rgba(var(--white-rgb), 0.04);
}

.organs-system-title::before {
    content: "";
    position: absolute;
    inset: 8px auto 8px 0;
    width: 4px;
    border-radius: 0 8px 8px 0;
    background: linear-gradient(180deg, rgba(var(--blue-light-rgb), 0.9), rgba(var(--blue-rgb), 0.55));
}

/*
 * COMMENT LIRE LES SYSTEMES, une fois au-dessus de la vue.
 *
 * Elle porte la regle du manuel -- tout vert n'est pas l'objectif -- et se lit
 * AVANT les groupes, pas apres : une regle de lecture posee en fin de page
 * arrive quand la lecture est faite.
 */
.organs-lecture {
    margin: 0 0 18px;
    padding: 12px 16px;
    max-width: 88ch;
    border-radius: 12px;
    background: rgba(var(--slate-rgb), 0.20);
    border-left: 2px solid rgba(var(--blue-light-rgb), 0.45);
    color: rgba(var(--white-rgb), 0.72);
    font-size: 14px;
    line-height: 1.55;
}

/*
 * CE QUE LE SYSTEME TIENT, sous le titre du groupe.
 *
 * Elle se cale sur le padding gauche du titre, trait compris, pour se lire
 * comme la suite du meme bloc et non comme un element de plus. La largeur est
 * bornee : une ligne de mesure de texte se lit, une ligne pleine largeur se
 * saute.
 */
.organs-system-note {
    margin: -6px 0 2px;
    padding: 0 14px 0 16px;
    max-width: 78ch;
    color: rgba(var(--white-rgb), 0.62);
    font-size: 14px;
    line-height: 1.55;
}

.organs-grid--grouped {
    grid-template-columns: repeat(3, minmax(0, 1fr));
}

@media (max-width: 900px) {
    .organs-toolbar__controls {
        width: 100%;
        flex-wrap: wrap;
        justify-content: stretch;
    }

    .organs-toolbar__select {
        width: 100%;
        min-width: 0;
    }
}

/* ===== App footer: menu + legal ===== */

/* Le pied de page ne porte plus la navigation, seulement quatre mentions
   legales. Il en gardait pourtant l'habillage : degrade, bordure, ombre portee
   de 48 px, flou d'arriere-plan et rayon de 18 px, c'est-a-dire le poids d'une
   carte de contenu pour quatre liens de 11 px. Une pilule flottante en bas de
   page attire l'oeil sur ce qu'on lit en dernier.

   Un simple filet suffit a marquer la fin de la page. Le trait s'arrete a la
   largeur du contenu, sinon il coupe l'ecran en deux. */
.lk-app-footer {
    width: min(920px, calc(100% - 32px));
    margin: 44px auto 0;
    padding: 18px 0 26px;

    position: relative;
    z-index: 1;

    border-top: 1px solid rgba(var(--white-rgb), 0.08);
    color: var(--ink-200);
}

.lk-app-footer__menu {
    display: flex;
    justify-content: center;
    gap: 10px;
    flex-wrap: wrap;

    padding-bottom: 12px;
    margin-bottom: 10px;

    border-bottom: 1px solid rgba(var(--slate-light-rgb), 0.18);
}

.lk-app-footer__menu a,
.lk-bottom-nav__link {
    min-width: 132px;
    min-height: 42px;
    padding: 10px 16px;
    border-radius: 14px;

    display: inline-flex;
    align-items: center;
    justify-content: center;

    box-sizing: border-box;
    text-align: center;
    text-decoration: none;

    font-size: 14px;
    font-weight: 700;
    line-height: 1.2;

    /* L ACCENT DOUX DEVIENT BRONZE EN CLAIR et rend 3,49 a 12 px (mesure).
       Un lien de pied de page est du texte avant d'etre un accent : il prend
       l'encre courante, et c'est le survol qui l'allume. */
    color: rgba(var(--white-rgb), var(--voile-mention, 0.76));
    background: rgba(var(--portal-accent-rgb), 0.12);
    border: 1px solid rgba(var(--portal-accent-clair-rgb), 0.22);

    box-shadow: inset 0 1px 0 rgba(var(--white-rgb), 0.06),
        0 8px 18px rgba(var(--black-rgb), 0.12);

    transition:
        transform 0.18s ease,
        background 0.18s ease,
        border-color 0.18s ease,
        color 0.18s ease,
        box-shadow 0.18s ease;
}

.lk-app-footer__menu a:hover,
.lk-bottom-nav__link:hover {
    color: var(--white);
    background: rgba(var(--portal-accent-rgb), 0.24);
    border-color: rgba(var(--portal-accent-clair-rgb), 0.42);
    transform: translateY(-1px);
    box-shadow: inset 0 1px 0 rgba(var(--white-rgb), 0.08),
        0 10px 22px rgba(var(--couleur-accent-fort-rgb), 0.18);
}

.lk-app-footer__menu a.is-active,
.lk-bottom-nav__link.is-active {
    color: var(--white);
    /* L'onglet actif suit l'ACCENT, pas un bleu fixe : c'est de l'interface.
       Il etait parti en bleu avec le bouton « Terminer » du lecteur parce que
       les deux portaient le meme degrade, au caractere pres. Un motif
       identique ne veut pas dire une intention identique. */
    background: linear-gradient(135deg, var(--couleur-accent-fort), rgb(var(--portal-accent-rgb)));
    border-color: rgba(var(--portal-accent-clair-rgb), 0.55);
    box-shadow: inset 0 1px 0 rgba(var(--white-rgb), 0.18),
        0 10px 26px rgba(var(--couleur-accent-fort-rgb), 0.34);
    transform: none;
}

.lk-app-footer__legal {
    display: grid;

    /* `minmax(0, 1fr)` ET NON `1fr` : sans le zero, la colonne se dimensionne
       sur son contenu le plus large. La derniere ligne sort volontairement de
       la boite du pied pour reprendre la largeur du contenu, et la colonne
       grandissait avec elle : tout le pied, liens legaux compris, se
       retrouvait pousse vers la droite. Le zero la borne, et le debordement
       redevient purement visuel. */
    grid-template-columns: minmax(0, 1fr);
    gap: 10px;

    font-size: 12px;
    line-height: 1.35;
    color: rgba(var(--white-rgb), var(--voile-mention-basse, 0.42));
    text-align: center;
}

.lk-app-footer__owner {
    /* 0,34 est un cran de mention pris trop bas : mesure, 2,99 en sombre et
       2,13 en clair. La mention reste une mention, mais elle doit se lire.
       `--voile-mention` bascule par theme ; le repli sert aux pages qui ne
       chargent pas les couches retenues, et elles sont nombreuses. */
    color: rgba(var(--white-rgb), var(--voile-mention, 0.62));
}

/* Les liens etaient bleus et soulignes en permanence : quatre traits colores
   pour des mentions qu'on consulte une fois par an. Ils prennent la teinte du
   texte alentour et ne se soulignent qu'au survol, ou ils doivent en revanche
   repondre franchement. */
.lk-app-footer__legal a {
    /* 0,52 rend 3,49 a 12 px en clair (mesure, reproduite). La correction
       existait deja, mais seulement dans `coquille-retenue.css`, que la
       moitie des pages ne charge pas : elle vit donc ici, ou tout le monde
       la voit. `--voile-mention` porte la bascule par theme. */
    color: rgba(var(--white-rgb), var(--voile-mention, 0.76));
    text-decoration: none;
    transition: color 0.18s ease;
}

/* L ETAT DES SERVICES SE LIT, ou il ne sert a rien. Il herite de l'encre du
   pied (`color: inherit` sur son bouton), donc du meme cran de mention trop
   bas : mesure, 3,94 en clair et 4,03 en sombre, sous le seuil des deux cotes.
   C'est la seule ligne de la page qui dit si le produit repond ; elle merite
   le cran au-dessus. */
.etat-systeme__texte {
    color: rgba(var(--white-rgb), var(--voile-mention, 0.76));
}

.lk-app-footer__legal a:hover,
.lk-app-footer__legal a:focus-visible {
    color: rgba(var(--white-rgb), 0.92);
    text-decoration: underline;
    text-underline-offset: 3px;
}

.lk-app-footer__links {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 5px 12px;
}

/* LA DERNIERE LIGNE DU PIED DE PAGE : l'etat a gauche, l'editeur au centre.

   ELLE SORT DE LA BOITE DU PIED. Celui-ci fait 920 px et se centre, le
   contenu en fait 1400 avec 24 px de marge : range dans le pied, l'etat
   tombait loin a droite du bord gauche des cartes. Le decalage de moitie
   recentre cette ligne sur la PAGE, et non sur son parent.

   TROIS COLONNES et non deux, la troisieme vide : avec deux, la mention
   d'editeur se decalerait des que l'etat change de libelle, et un texte
   centre qui bouge tout seul se remarque. */
/*
 * LA DERNIERE LIGNE DU PIED DE PAGE S'ETEND SUR TOUTE LA LARGEUR.
 *
 * Son parent est borne a 920 px ; elle en sort par le milieu pour aller
 * chercher les bords. `100vw` COMPTE LA BARRE DE DEFILEMENT : sur cette page
 * elle fait dix pixels, la ligne depassait donc de cinq de chaque cote et la
 * page entiere gagnait cinq pixels de defilement horizontal. Mesure : corps
 * 1270, ligne -5..1275.
 *
 * On retire donc la barre de la largeur. Sa mesure est posee une fois par
 * `etat-systeme.js`, qui tourne deja sur toutes les pages portant ce pied :
 * aucune unite CSS ne donne la largeur de la zone visible SANS la barre.
 * Le repli a zero couvre le cas ou le script ne s'est pas execute.
 */
.lk-app-footer__bas {
    position: relative;
    left: 50%;
    transform: translateX(-50%);
    width: min(1400px, calc(100vw - var(--barre-defilement, 0px)));
    padding: 0 24px;
    box-sizing: border-box;

    display: grid;

    /* `minmax(0, 1fr)` ET NON `1fr`, meme raison qu'au-dessus pour
       `.lk-app-footer__legal` : une piste `1fr` a pour minimum automatique le
       min-content de son contenu, donc elle GRANDIT quand la phrase d'etat est
       longue, et la mention d'editeur cesse d'etre au centre de la page.
       Mesure a 700 px avec trois gestes empeches : les deux pistes laterales
       tombaient a 256 et 223 px au lieu d'etre egales, et l'editeur se
       retrouvait 17 px a droite du centre. Le zero borne le minimum. */
    grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
    align-items: center;
    gap: 10px;
}

/* LES GESTES EMPECHES S EMPILENT, ILS NE SE RANGENT PAS COTE A COTE.

   Trois rouages peuvent tomber ensemble, et le pied rendait alors les trois
   phrases A LA FILE dans une seule ligne flex, sans ecart et sans separateur.
   Mesure a 1440 : trois colonnes de 225, 197 et 182 px, neuf lignes de texte
   au total, la premiere phrase hachee en quatre morceaux d'environ 180 px. A
   700 px, neuf lignes dans des colonnes de 96 px.

   C'est le jour ou plusieurs choses tombent que ce coin de page compte le
   plus, et c'est exactement ce jour-la qu'il devenait illisible.

   `min-width: 0` accompagne la piste bornee ci-dessus : sans lui, l'element
   refuse de descendre sous son min-content et la borne ne sert a rien. */
.lk-app-footer__etat {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 6px;
    justify-self: start;
    min-width: 0;
}

/* Sous 620 px les trois colonnes ne tiennent plus : l'etat repasse au-dessus
   et les deux se centrent. */
@media (max-width: 620px) {
    .lk-app-footer__bas {
        grid-template-columns: minmax(0, 1fr);
        justify-items: center;
        gap: 8px;
    }

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

/* Le panneau s'ouvre VERS LE HAUT : sous le dernier element de la page, un
   panneau qui descend s'ouvrirait hors de l'ecran et forcerait a derouler
   pour lire ce qu'on vient de demander.

   Et vers la DROITE, puisque le bouton est a gauche : cale a droite comme
   dans le bandeau, il sortirait de la page par la gauche. */
.lk-app-footer__etat .etat-systeme__panneau {
    top: auto;
    bottom: calc(100% + 8px);
    right: auto;
    left: 0;
    /* La largeur fixe de 290 px deborderait sur un telephone etroit. */
    width: min(290px, calc(100vw - 32px));
    text-align: left;
}

/* Футер находится в потоке страницы, поэтому большой padding-bottom не нужен */
.lk-content {
    padding-bottom: 40px;
}

@media (max-width: 640px) {
    .lk-app-footer {
        width: calc(100% - 20px);
        margin: 24px auto 16px;
        padding: 10px;
        border-radius: 18px;
    }

    .lk-app-footer__menu {
        gap: 7px;
        padding-bottom: 10px;
        margin-bottom: 9px;
    }

    .lk-app-footer__menu a,
    .lk-bottom-nav__link {
        flex: 1 1 calc(50% - 7px);
        min-width: 0;
        min-height: 42px;
        padding: 9px 10px;
        border-radius: 12px;
        font-size: 13px;
    }

    .lk-app-footer__legal {
        font-size: 10px;
    }

    .lk-app-footer__links {
        gap: 4px 9px;
    }

    .lk-content {
        padding-bottom: 28px;
    }
}

.profile-form-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0 16px;
}

/* Un champ qui traverse les deux colonnes. Certains textes ne se laissent pas
   couper en deux : une specialite libre s'ecrit en toutes lettres. */
.profile-form-grid__pleine-largeur {
    grid-column: 1 / -1;
}

.profile-linked-partner {
    margin-bottom: 18px;
    padding: 16px;
    border-radius: 12px;
    background: rgba(var(--blue-rgb), 0.10);
    border: 1px solid rgba(var(--blue-rgb), 0.28);
}

.profile-linked-partner__title {
    margin-bottom: 12px;
    font-size: 16px;
    font-weight: 700;
    color: var(--couleur-accent-doux);
}

.profile-linked-partner__grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 12px 16px;
}

.profile-linked-partner__item {
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.profile-linked-partner__item strong {
    font-size: 13px;
    color: var(--blue-soft);
}

.profile-linked-partner__item span {
    color: var(--ink-200);
    line-height: 1.4;
}

.profile-referral-row {
    display: flex;
    gap: 10px;
    align-items: center;
    margin-bottom: 8px;
    flex-wrap: wrap;
}

.profile-referral-row input {
    margin-bottom: 0 !important;
    flex: 1 1 260px;
}

.profile-referral-hint {
    margin-top: 0;
    margin-bottom: 14px;
}

@media (max-width: 900px) {
    .profile-form-grid,
    .profile-linked-partner__grid {
        grid-template-columns: 1fr;
    }
}

.profile-side-stack {
    display: grid;
    gap: 20px;
    align-content: start;
}
/* =========================================
   La commande de retour
   -----------------------------------------
   Elle existait sur quelques pages seulement, et sous huit formulations
   differentes. Un seul style desormais, pose par partials/retour.blade.php.
   Discrete au repos, elle avance vers la gauche au survol : le geste dit le
   sens avant que le mot ne soit lu.
   ----------------------------------------- */
/* --- Le retour redevient un lien -----------------------------------------
   -------------------------------------------------------------------------
   « Retour vers Espace praticien » etait rendu en BOUTON : ardoise pleine
   `rgb(55, 71, 80)`, filet clair, rayon 8 px, libelle blanc plein. Il pesait
   donc autant que « Tableau de bord praticien » juste en dessous, qui lui est
   une vraie action. Deux objets au meme poids dont l'un revient en arriere et
   l'autre avance.

   PAS DE CYAN NON PLUS. Le cyan des liens est pris par le pied de bloc, qui
   dit « aller voir plus loin ». Revenir n'est pas aller plus loin, et surtout
   ce lien est le premier element de la page : lui donner la teinte du produit
   en ferait le point d'entree du regard, alors qu'il est une sortie.

   Il prend donc le blanc a 0,72, celui des textes secondaires, et remonte a
   0,96 au survol. Il reste lisible, il cesse de reclamer.
   ------------------------------------------------------------------------- */
.lk-retour {
    /*
     * Meme habillage que .btn-secondary : meme fond, meme rayon, meme
     * rembourrage, meme taille de texte. C'etait une pastille translucide
     * arrondie a 999, qui ne ressemblait a aucun autre bouton du site.
     * On garde seulement la fleche et son deplacement, qui disent le sens.
     */
    display: inline-flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 16px;
    padding: 0;
    border-radius: 0;
    /*
     * VARIANTE E : LA TEINTE PLEINE, SANS DEGRADE NI LUEUR.
     *
     * Se deplacer n'est pas agir. Ce bouton partageait la grammaire des
     * actions directes — degrade et relief — et sonnait donc aussi fort
     * qu'elles, alors qu'il ne fait que ramener en arriere.
     *
     * Il garde son bord, sans quoi un aplat sombre sur une page sombre ne se
     * lit plus comme un bouton : c'est ce qui manquait a l'ancien #3a4e58,
     * qui ne se detachait de la page qu'a 1,87. Celui-ci se detache a 2,42 et
     * porte le blanc a 9,63.
     */
    border: 0;
    box-sizing: border-box;
    background: none;
    color: rgba(var(--white-rgb), 0.72);
    font-size: 14px;
    line-height: 1;
    text-decoration: none;
    transition: background .18s ease;
    box-shadow: none;
}

.lk-retour:hover {
    color: rgba(var(--white-rgb), 0.96);
    background: none;
    transform: none;
    filter: none;
}

/* --- La fleche du retour, centree sur les capitales -----------------------
   -------------------------------------------------------------------------
   Les boites sont deja centrees : le conteneur est en `flex` avec
   `align-items: center`, et l'ecart entre le centre de la boite de la fleche et
   celui du libelle vaut exactement zero. Le defaut n'est donc pas dans la mise
   en page, il est dans le GLYPHE.

   MESURE EN PIXELS SUR LA CAPTURE, et c'est la seule facon de le voir : la
   fleche peinte occupe les lignes 105 a 110, centre 107,5 ; la capitale « R »
   de « Retour » occupe 102 a 111, centre 106,5. La fleche est un pixel plus
   bas.

   POURQUOI. « ← » est un caractere mathematique : son trait est cale sur la
   barre des operateurs, c'est-a-dire au milieu de la hauteur d'x. Une capitale,
   elle, monte plus haut. Centrer les boites aligne donc deux choses qui ne sont
   pas au meme endroit dans leur boite.

   ATTENTION AU TRANSFORM QUI EN REMPLACE UN AUTRE. `lk.css` pose
   `transform: translateX(-3px)` au survol : une propriete `transform` REMPLACE
   la precedente, elle ne s'y ajoute pas. Poser le decalage vertical au repos
   seul l'aurait donc annule des qu'on survole, et la fleche serait redescendue
   d'un pixel au passage de la souris. Les deux etats portent les deux
   translations.
   ------------------------------------------------------------------------- */
.lk-retour__fleche {
    /*
     * « ← » ne s'assied pas sur la ligne de base comme une lettre : son
     * dessin est cale plus haut dans sa boite, et le centrage du parent ne
     * centre que la boite. Dans sa propre boite flexible, le trait lui-meme
     * se retrouve au milieu, a cote de son libelle.
     */
    display: inline-flex;
    align-items: center;
    justify-content: center;
    /* Sans hauteur de ligne a 1, la boite reprend celle du texte courant et
       le decalage revient par la fenetre. */
    line-height: 1;
    font-size: 16px;
    transition: transform .18s ease;
    transform: translateY(-1px);
}

.lk-retour:hover .lk-retour__fleche {
    transform: translateX(-3px) translateY(-1px);
}

/* Le clavier doit voir ou il est. */
.lk-retour:focus-visible {
    outline: 2px solid rgba(var(--portal-accent-rgb), 0.55);
    outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
    .lk-retour,
    .lk-retour__fleche {
        transition: none;
    }
}

/* Le selecteur de date natif.

   PLUS D'INVERSION ICI. Elle datait du temps ou le document ne declarait pas
   `color-scheme: dark` : l'icone arrivait noire, et on la retournait. Depuis
   que la declaration existe, le navigateur la dessine DEJA en clair, et
   l'inversion la repeignait en noir. Invisible a nouveau, pour la raison
   inverse : deux corrections justes chacune a sa date, fausses ensemble.

   Ne restent que l'opacite, qui range une commande secondaire, et le curseur,
   qui dit qu'on peut cliquer. */
input[type="date"]::-webkit-calendar-picker-indicator,
input[type="time"]::-webkit-calendar-picker-indicator,
input[type="datetime-local"]::-webkit-calendar-picker-indicator,
input[type="month"]::-webkit-calendar-picker-indicator {
    opacity: .65;
    cursor: pointer;
}

input[type="date"]::-webkit-calendar-picker-indicator:hover,
input[type="time"]::-webkit-calendar-picker-indicator:hover,
input[type="datetime-local"]::-webkit-calendar-picker-indicator:hover,
input[type="month"]::-webkit-calendar-picker-indicator:hover {
    opacity: 1;
}

/* La salutation ouvre la page, l'onglet actif dit deja ou l'on est. Elle
   passe devant, sans prendre la plus grande taille : elle est identique a
   chaque visite, et le plus gros caractere revient a ce qui change. */
.journey-title__salutation {
    margin: 0 0 14px;
    color: rgba(var(--white-rgb), 0.82);
    font-size: 20px;
    font-weight: 500;
}

/* L'enonce de l'objectif, sous son titre. Le titre nomme l'ecran, l'enonce
   dit ce qu'on vise : deux roles, deux niveaux. */
.business-goal__statement {
    margin: -4px 0 10px;
    color: rgba(var(--white-rgb), 0.88);
    font-size: 17px;
    font-weight: 600;
}

/* =========================================
   La navigation en deux couches
   -----------------------------------------
   En haut ce qui appartient a la personne, en bas ce qui vient du role. Les
   secondes portent une couleur distincte parce qu'elles ne parlent pas de soi
   mais des autres : ses clients, l'ensemble des utilisateurs.
   ----------------------------------------- */

/* La rangee de role n'a pas de trait sous elle : c'est la derniere. */
.lk-app-footer__menu--role {
    padding-bottom: 0;
    margin-bottom: 10px;
    border-bottom: none;
}

.lk-bottom-nav__link--role {
    color: var(--green-pale);
    background: rgba(var(--share-bright-rgb), 0.16);
    border-color: rgba(var(--couleur-succes-doux-rgb), 0.28);
}

.lk-bottom-nav__link--role:hover {
    color: var(--white);
    background: rgba(var(--share-bright-rgb), 0.28);
    border-color: rgba(var(--couleur-succes-doux-rgb), 0.44);
}

.lk-bottom-nav__link--role.is-active {
    color: var(--white);
    background: rgba(var(--share-bright-rgb), 0.52);
    border-color: rgba(var(--couleur-succes-doux-rgb), 0.40);
}

/* Le lien vers l'abonnement, dans la carte du solde de points. */
/* La colonne pose son propre espacement : la marge s'y ajoutait. */
.processing-balance-card__buy {
    margin-top: 0;
}

/* --- Les deux actions d'un résultat, dans l'historique ------------------- */

/* Côte à côte tant qu'il y a la place, empilées ensuite : deux boutons qui
   débordent d'une carte valent moins qu'un bouton par ligne. */
/* LES TROIS RACCOURCIS S EMPILENT, comme les deux autres colonnes. Cote a cote
   ils poussaient la carte au-dela de sa largeur des que les intitules
   s'allongeaient, ce qui arrive a chaque traduction. `stretch` leur donne la
   meme largeur : trois boutons de largeurs differentes font une colonne en
   escalier. */
.diagnostic-history__actions {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 8px;
}

/* LE RENVOI EST UN FORMULAIRE, PAS UN LIEN, et sans cela il faisait le seul
   bouton etroit de la colonne : `stretch` etire le formulaire, pas le bouton
   qu'il contient. */
.diagnostic-history__actions > form {
    display: flex;
}

.diagnostic-history__actions > form > .btn {
    flex: 1;
}

/*
 * L'HISTORIQUE DES TESTS REPREND LA CHARTE.
 *
 * Ces deux pages s'appuyaient sur onze classes utilitaires empruntees a
 * Bootstrap (`d-flex`, `p-2`, `border`, `rounded`, `my-3`...) qui ne sont
 * declarees dans AUCUNE feuille du produit. Le gabarit annoncait donc une mise
 * en page qui ne se produisait pas : le bloc d'actions se posait sous les
 * informations au lieu de leur faire face, et la carte faisait 1 336 px de
 * large pour 90 px de texte.
 *
 * On ne declare pas ces utilitaires globalement : une vingtaine d'autres
 * gabarits les portent aussi, dont les onglets du visualiseur qui sont figes.
 * Leur donner un effet retroactif ferait bouger des ecrans valides. Les deux
 * pages d'historique recoivent donc leurs propres classes, nommees par ce
 * qu'elles font.
 */

/*
 * TROIS COLONNES, CHACUNE EMPILEE : LES FAITS, L IMPORT, LES RACCOURCIS.
 *
 * La carte etait en deux etages, les faits et les boutons en haut, l import
 * sous un filet en bas : un second etage pour un seul champ, et douze cartes a
 * la page en devenaient interminables. Les trois blocs parlent du meme test et
 * partagent maintenant une rangee.
 *
 * DANS CHAQUE COLONNE, ON EMPILE. Mettre les faits sur une seule ligne a ete
 * essaye : dans une colonne, la ligne se replie, et le separateur se retrouve
 * en tete des lignes suivantes, ce qui se lit comme une liste a puces mal
 * formee. Un fait par ligne se lit d'un coup d'oeil et se traduit sans piege.
 *
 * LES FAITS PRENNENT LA PLACE QUI RESTE, `minmax(0, 1fr)`, les deux autres
 * colonnes se dimensionnent sur leur contenu. Le `0` du minmax n'est pas
 * decoratif : sans lui, une colonne de grille refuse de descendre sous la
 * largeur de son contenu, et une date longue pousserait les boutons hors de la
 * carte au lieu de laisser la colonne se resserrer.
 *
 * ALIGNEES EN HAUT, parce que les trois colonnes n'ont pas la meme hauteur et
 * qu'un import centre face a six faits flotterait sans raison.
 */
.diagnostic-history__grille {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto auto;
    align-items: start;
    gap: 12px 24px;
}

/*
 * LA PREMIERE LIGNE DES FAITS S ALIGNE SUR LE TEXTE DES BOUTONS.
 *
 * Les trois colonnes commencent a la meme hauteur, c'est mesure : le haut des
 * trois blocs tombe au pixel pres. Ce qui ne l'est pas, c'est le TEXTE. Un
 * bouton enfonce son intitule de son rembourrage, dix pixels ; une ligne de
 * texte nu commence tout en haut. Cale sur les boites, l'oeil lit un decalage
 * qui n'existe pas dans la grille mais qui existe bel et bien a l'ecran.
 *
 * Dix pixels, donc, la valeur du rembourrage haut du bouton plus sa bordure,
 * moins le demi-ecart d'interligne. Mesure sur la page, pas devinee.
 */
.diagnostic-history__faits {
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding-top: 10px;
}

/* Empilees, les colonnes se suivent : ce calage n'aurait plus rien a caler. */
@media (max-width: 1100px) {
    .diagnostic-history__faits {
        padding-top: 0;
    }
}

/* Le champ de fichier, le nom du fichier choisi, puis le bouton : trois lignes,
   dans l'ordre du geste. Le libelle « Import du resultat » a disparu, le bouton
   dit deja ce qu'il fait et dans quel test. */
/* CALEE A GAUCHE, PAS ETIREE. Etires sur toute leur colonne, le champ et son
   bouton faisaient deux barres larges face aux faits, et la carte se lisait
   comme trois blocs sans rapport. A leur largeur propre et cales a gauche, ils
   se rangent contre la colonne des faits : le geste d'import se lit comme la
   suite du detail qu'il complete, pas comme un panneau separe. */
.diagnostic-history__import {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 8px;
}

/* LE NOM DU FICHIER PASSE SOUS LE CHAMP, dans cette colonne seulement. Le
   composant les met cote a cote, ce qui est juste dans un formulaire pleine
   largeur ; ici la colonne fait un tiers de la carte, et « Aucun fichier
   choisi » repoussait le bouton d'ouverture. On ne touche pas au composant,
   qui sert ailleurs : on habille sa disposition dans cette colonne. */
.diagnostic-history__import .champ-fichier {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 6px;
    margin: 0;
}

/*
 * LE CHAMP DE FICHIER PREND LES MESURES DE LA FAMILLE, ICI SEULEMENT.
 *
 * Le composant est dessine pour un formulaire : 46 px de haut, corps 16,
 * interligne libre, parce qu'il y est seul de son espece et qu'un champ se
 * vise. Pose dans cette colonne, il fait face a des boutons de 37 px en corps
 * 14 : il depassait de neuf pixels et son intitule tombait quatorze pixels plus
 * bas que ceux d'a cote. Les trois colonnes commencaient bien a la meme hauteur,
 * mais rien ne s'alignait a l'oeil.
 *
 * On ne touche pas au composant, qui sert ailleurs et y a raison : on lui donne
 * les mesures du bouton DANS cette colonne, pour que les trois premieres lignes
 * de la carte tombent sur la meme.
 */
.diagnostic-history__import .champ-fichier__bouton {
    padding: 9px 16px;
    font-size: 14px;
    line-height: 17px;
    min-height: 0;
}

/* Sous cette largeur, trois colonnes deviennent trois bandes trop etroites
   pour leur contenu : on empile, dans l'ordre de lecture. */
@media (max-width: 1100px) {
    .diagnostic-history__grille {
        grid-template-columns: minmax(0, 1fr);
    }
}

/* Le filet entre les faits du test et le geste d'import. `<hr>` nu rendait un
   trait gris de navigateur, 2 px, sur une carte sombre. */
.diagnostic-history__trait {
    border: 0;
    border-top: 1px solid rgba(var(--slate-light-rgb), 0.22);
    margin: 16px 0;
}

/* L'encart qui isole une version de resultat ou un bloc de donnees. */
.diagnostic-history__encart {
    border: 1px solid rgba(var(--slate-light-rgb), 0.22);
    border-radius: 10px;
    padding: 12px 14px;
    margin-top: 10px;
    background: rgba(var(--slate-light-rgb), 0.05);
}

.diagnostic-history__encart > div + div {
    margin-top: 2px;
}

/* Le titre d'une section de la page technique. */
.diagnostic-history__section-titre {
    margin: 0 0 12px;
    font-size: 17px;
}

/* --- Le champ de fichier, habille ---------------------------------------
 * Le champ natif est un bouton du systeme d'exploitation : 21 px de haut, fond
 * transparent, police de l'OS. Il ne ressemblait a rien d'autre dans le
 * produit. On masque le champ et on habille son libelle, qui reste un vrai
 * `<label for>` : le clic et le clavier continuent de fonctionner.
 */
.champ-fichier {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px;
    margin: 10px 0 14px;
}

.champ-fichier input[type="file"] {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

.champ-fichier__bouton {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 44px;
    padding: 0 18px;
    border-radius: 10px;
    border: 1px dashed rgba(var(--slate-light-rgb), 0.45);
    background: rgba(var(--slate-light-rgb), 0.08);
    color: inherit;
    font-weight: 600;
    cursor: pointer;
    margin: 0;
}

.champ-fichier__bouton:hover {
    border-color: rgba(var(--blue-rgb), 0.6);
    background: rgba(var(--blue-rgb), 0.12);
}

/* Le clavier doit voir ou il est : le champ etant masque, c'est le libelle qui
   porte l'anneau de mise au point. */
.champ-fichier input[type="file"]:focus-visible + .champ-fichier__bouton {
    outline: 2px solid rgba(var(--portal-accent-rgb), 0.8);
    outline-offset: 2px;
}

/* Le nom du fichier choisi, ecrit par le script a cote du bouton. */
.champ-fichier__nom {
    min-width: 0;
    opacity: .8;
    font-size: 13px;
    overflow-wrap: anywhere;
}

/* Cette regle datait de la rangee : `flex: 1 1 100%` y donnait aux boutons
   toute la largeur. La colonne le fait deja par `stretch`, et la meme valeur
   les ferait desormais grandir EN HAUTEUR pour remplir la colonne. Seule la
   largeur du bloc reste utile. */
@media (max-width: 560px) {
    .diagnostic-history__actions {
        width: 100%;
    }
}

/* ===== Entraînement libre ================================================ */

/* « Passer les explications », au même niveau que le bandeau. Même forme que
   la pastille, pour qu'on lise les deux d'un même coup d'œil. */
body.diagnostic-demo-page .diagnostic-pill--action {
    border: 1px solid rgba(var(--white-rgb), 0.24);
    background: rgba(var(--slate-rgb), 0.6);
    color: rgba(var(--white-rgb), 0.85);
    cursor: pointer;
    font: inherit;
    transition: background 0.18s ease, border-color 0.18s ease;
}

body.diagnostic-demo-page .diagnostic-pill--action:hover {
    background: rgba(var(--slate-mid-rgb), 0.85);
    border-color: rgba(var(--white-rgb), 0.34);
}

body.diagnostic-demo-page .diagnostic-pill--action.is-hidden {
    display: none;
}

/* La barre de fin de manche : elle remplace l'encadré, elle ne s'y ajoute
   pas. En entraînement, on ne relit pas une explication, on recommence. */
body.diagnostic-demo-page .diagnostic-free-bar {
    position: absolute;
    left: 50%;
    transform: translateX(-50%);
    /* Sous les images, pas par-dessus. La comparaison s'arrete au-dessus
       (voir `is-free-compare`), et les toiles sont bornees en hauteur pour ne
       pas deborder de leur cadre : sans cela, elles repassaient devant. */
    /* La reserve du bas, comme la barre de commandes : sans elle, ces boutons
       passent sous l'interface du navigateur sur telephone. */
    bottom: calc(var(--diag-browser-bottom, 0px) + 40px);
    z-index: 40;

    display: flex;
    align-items: center;
    gap: 12px;
    padding: 12px 16px;
    border-radius: 16px;

    background: rgba(var(--slate-rgb), 0.78);
    border: 1px solid rgba(var(--white-rgb), 0.12);
    backdrop-filter: blur(16px);
}

body.diagnostic-demo-page .diagnostic-free-bar.is-hidden {
    display: none;
}

@media (max-width: 900px) {
    body.diagnostic-demo-page .diagnostic-free-bar {
        left: 10px;
        right: 10px;
        transform: none;
        justify-content: stretch;
    }

    body.diagnostic-demo-page .diagnostic-free-bar .btn {
        flex: 1 1 0;
        text-align: center;
    }
}

/*
 * En entrainement libre, il n'y a pas de carte d'explication au-dessus : le
 * `top: 46vh` du didacticiel reservait une place a quelque chose qui n'existe
 * pas ici. On rend cette hauteur aux images, ce qui les agrandit sans rien
 * changer a l'apparence des cadres. En bas, on s'arrete au-dessus de la barre
 * de manche.
 */
body.diagnostic-demo-page.is-free-compare .diagnostic-demo-compare {
    top: 76px;
    bottom: 116px;
    align-content: center;
    overflow: hidden;
}

/* Le bornage vit desormais avec le reste du style de la toile, plus haut :
   `max-width`, `max-height`, et surtout pas d'`aspect-ratio`. */

@media (max-width: 900px) {
    body.diagnostic-demo-page .diagnostic-free-bar {
        bottom: calc(var(--diag-browser-bottom, 0px) + 16px);
    }

    body.diagnostic-demo-page.is-free-compare .diagnostic-demo-compare {
        top: 64px;
        bottom: 84px;
        overflow: auto;
    }
}

/* Le compte à rebours d'observation, en entraînement libre. Centré, sobre :
   il annonce un délai, il ne commente pas l'image. */
/*
 * Discret, et pour cause : pendant l'observation, l'oeil doit aller sur
 * l'image. Ce decompte rassure sur la duree, il ne demande pas l'attention.
 */
body.diagnostic-demo-page .diagnostic-free-countdown {
    /* Dans le bandeau, a droite, au niveau de la pastille. Le bandeau etant
       en `space-between`, il s'y place sans autre reglage. */
    margin-left: auto;
    padding: 6px 14px;
    border-radius: 999px;
    background: rgba(var(--slate-rgb), 0.44);
    border: 1px solid rgba(var(--white-rgb), 0.08);
    backdrop-filter: blur(10px);

    color: rgba(var(--white-rgb), var(--voile-mention, 0.62));
    font-size: 13px;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;

    opacity: 1;
    transition: opacity 0.4s ease;
}

body.diagnostic-demo-page .diagnostic-free-countdown.is-fading {
    opacity: 0;
}

body.diagnostic-demo-page .diagnostic-free-countdown.is-hidden {
    display: none;
}

/* =========================================================================
   La fenetre avant / apres
   -------------------------------------------------------------------------
   Elle s'ouvre par-dessus deux pages tres differentes : le lecteur de test,
   plein ecran et sombre, et le detail technique du praticien, qui est une
   page d'application ordinaire. D'ou un voile sombre et une fenetre a elle :
   elle ne depend d'aucun des deux contextes.

   Aucun chiffre ici, et il ne faut pas en ajouter. Ces vignettes montrent ce
   qui a ete fait, elles ne mesurent rien.
   ========================================================================= */

/* --- D11. La fenetre de comparaison --------------------------------------
   -------------------------------------------------------------------------
   Elle n'avait jamais ete reprise, pour une raison simple : elle ne s'ouvrait
   pas. Le bouton de fin de test la posait bien sur la page, mais le lecteur
   monte a 9999 et la peignait par-dessus. Corrige dans `lk.css` ; ce qui suit
   ne traite que son aspect.

   ELLE FLOTTE, DONC ELLE EST OPAQUE. Meme regle que le panneau des cibles de
   coupon : une surface translucide posee SUR du contenu laisse lire ce qu'il
   y a dessous, et deux textes superposes ne se lisent ni l'un ni l'autre. Le
   fond du produit, puis la teinte du bloc de base par-dessus.

   LE VOILE RESTE SOMBRE ET FLOU. Il ne decore pas : il coupe la page du test,
   qui est une image plein ecran. Sans lui, les couleurs de l'image passeraient
   derriere les vignettes qu'on demande de comparer.
   ------------------------------------------------------------------------- */
.comparaison-popup {
    position: fixed;
    inset: 0;

    /*
     * AU-DESSUS DU LECTEUR, ET C'EST TOUTE L'HISTOIRE DE CE PLAN.
     *
     * A 3000, la fenetre s'ouvrait VRAIMENT : `is-hidden` retire, `display:
     * flex`, 1280 x 800, posee sur le corps de la page. Elle etait simplement
     * INVISIBLE, parce que `body.therapy-player-page #app` monte le lecteur a
     * 9999 et le peint en noir sur tout l'ecran. Mesure au centre de la page,
     * fenetre ouverte : l'element du dessus etait `diagnostic-overlay__text`,
     * a l'interieur du lecteur.
     *
     * Le defaut ne se voyait QUE la, et c'est ce qui l'a fait durer : le meme
     * bloc sert au praticien depuis le detail technique, ou la page ne porte
     * pas `therapy-player-page`, donc le lecteur ne monte pas, donc la fenetre
     * s'ouvrait normalement. Un bouton mort d'un cote, vivant de l'autre, avec
     * le meme code.
     *
     * 10050 et non 10000 : `etat-systeme__panneau` occupe deja 10001.
     */
    z-index: 10050;

    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px;

    background: rgba(0, 0, 0, 0.76);
    backdrop-filter: blur(6px);
}

.comparaison-popup.is-hidden {
    display: none;
}

body.comparaison-ouverte {
    overflow: hidden;
}

.comparaison-fenetre {
    display: flex;
    flex-direction: column;
    width: min(820px, 100%);
    max-height: min(88vh, 100%);

    background: linear-gradient(135deg,
            rgba(var(--portal-accent-rgb), 0.05),
            rgba(var(--portal-accent-rgb), 0) 60%),
        rgb(var(--mimex-bg-rgb, 7 17 31));
    color: rgba(var(--white-rgb), 0.92);
    border: var(--filet-epaisseur) solid var(--filet-petit);
    border-radius: 18px;
    box-shadow: 0 28px 70px rgba(0, 0, 0, 0.55);
    overflow: hidden;
}

.comparaison-entete {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    padding: 18px 22px;
    border-bottom: 1px solid rgba(var(--white-rgb), 0.10);
}

.comparaison-titre {
    margin: 0;
    font-size: 19px;
    font-weight: 700;
    color: var(--ink-50);
}

/* --- La croix de fermeture -----------------------------------------------
   Elle prend la matiere du bouton ordinaire, et surtout son ANNEAU DE FOCUS
   passe du violet `#a5b4fc` au cyan. Il se voyait a chaque ouverture : la
   fenetre donne le focus a cette croix, donc la premiere chose qu'on voyait
   du produit repris etait un cercle violet. */
.comparaison-fermer {
    flex: 0 0 auto;
    width: 32px;
    height: 32px;
    display: inline-flex;
    align-items: center;
    justify-content: center;

    background: rgba(var(--slate-mid-rgb), 0.92);
    border: var(--filet-epaisseur) solid var(--filet-petit);
    border-radius: 12px;
    color: rgba(var(--white-rgb), 0.88);
    font-size: 22px;
    line-height: 1;
    cursor: pointer;
    transition: background .18s ease;
}

.comparaison-fermer:hover {
    background: rgba(var(--slate-mid-rgb), 1);
    border-color: rgba(var(--portal-accent-rgb), 0.42);
    color: rgba(var(--white-rgb), 0.98);
}

.comparaison-fermer:focus-visible {
    outline: 2px solid rgb(var(--portal-accent-rgb));
    outline-offset: 2px;
}

.comparaison-corps {
    flex: 1 1 auto;
    overflow-y: auto;
    padding: 20px 22px;

    display: flex;
    flex-direction: column;
    gap: 26px;
}

/* Le numero de l'essai est une etiquette : meme valeur que partout ailleurs. */
.comparaison-ligne__titre {
    margin-bottom: 10px;
    font-size: 12px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: rgba(var(--white-rgb), 0.54);
}

/* --- Les vignettes -------------------------------------------------------
   TROIS ESSAIS EST LE CAS NORMAL, pas deux : `sessions_planned` vaut 3. A
   460 px de large chacune, deux essais remplissaient deja la fenetre et le
   troisieme demandait de faire defiler sans savoir qu'il existait. Elles
   descendent a 380, ce qui fait tenir deux essais et demi a l'ecran : on VOIT
   qu'il y a une suite.

   Le filet passe a l'echelle du socle, et le fond du creux disparait : une
   image opaque n'a pas besoin qu'on lui prepare une surface. */
.comparaison-paire {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 14px;
}

.comparaison-colonne {
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.comparaison-colonne__legende {
    font-size: 13px;
    color: rgba(var(--white-rgb), 0.72);
}

.comparaison-canvas {
    width: 100%;
    height: auto;
    display: block;
    border-radius: 12px;
    border: var(--filet-epaisseur) solid var(--filet-large);
    background: none;
}

.comparaison-vide {
    margin: 0;
    padding: 18px 0;
    color: rgba(var(--white-rgb), var(--voile-mention, 0.6));
    font-size: 14px;
}

/* Le pied porte la meme surface que le reste : une bande plus claire ferait
   croire a un troisieme bloc, la ou il n'y a qu'un bas de fenetre. */
.comparaison-pied {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 18px;
    padding: 16px 22px;
    border-top: 1px solid rgba(var(--white-rgb), 0.10);
    background: none;
}

/* La phrase qui empeche de lire un resultat la ou il n'y en a pas. */
.comparaison-note {
    margin: 0;
    font-size: 13px;
    line-height: 1.5;
    color: rgba(var(--white-rgb), var(--voile-mention, 0.6));
}

.comparaison-pied .btn {
    flex: 0 0 auto;
}

@media (max-width: 760px) {
    .comparaison-popup {
        padding: 12px;
    }

    .comparaison-fenetre {
        max-height: 94vh;
    }

    .comparaison-paire {
        grid-template-columns: 1fr;
    }

    .comparaison-pied {
        flex-direction: column;
        align-items: stretch;
    }

    .comparaison-pied .btn {
        width: 100%;
    }
}

/* Le selecteur de langue de la demo ne vit que sur l'ecran d'intro. Au-dela,
   la place en haut a droite est celle de « Passer les explications ». */
body.diagnostic-demo-page.demo-lancee .locale-switcher {
    display: none;
}

/* Il appartient au bandeau : meme hauteur que « Test d'entrainement », meme
   fond, meme arrondi. A l'ecart, il flottait sans se rattacher a rien. */
body.diagnostic-demo-page .locale-switcher {
    top: 16px;
    right: 16px;

    /* Le fond du bandeau, mais les proportions d'origine : c'est un reglage
       discret, pas un element de navigation. Il ne doit pas peser autant que
       « Test d'entrainement ». */
    padding: 3px;
    gap: 3px;
    border-radius: 12px;
    background: rgba(var(--black-rgb), 0.45);
    border: 1px solid rgba(var(--white-rgb), 0.14);
    backdrop-filter: blur(10px);
    box-shadow: none;
}

body.diagnostic-demo-page .locale-switcher__button {
    border-radius: 10px;
    padding: 5px 10px;
    font-size: 12px;
    color: rgba(var(--white-rgb), 0.95);
}

body.diagnostic-demo-page .locale-switcher__button.is-active {
    background: rgba(var(--white-rgb), 0.92);
    color: rgb(var(--slate-rgb));
}

@media (max-width: 900px) {
    body.diagnostic-demo-page .locale-switcher {
        top: 12px;
        right: 12px;
    }

    body.diagnostic-demo-page .locale-switcher__button {
        padding: 5px 9px;
        font-size: 12px;
    }
}

/* Les deux actions sont desormais dans une colonne etroite : le libelle doit
   pouvoir se couper sans deborder. */
body.diagnostic-player-page .diagnostic-actions .btn {
    padding-left: 6px;
    padding-right: 6px;
    font-size: 13px;
    line-height: 1.2;
    white-space: normal;
    overflow-wrap: anywhere;
}

/* ===== La confirmation avant de quitter ===== */

.confirmation-sortie {
    width: min(420px, calc(100vw - 32px));
    padding: 22px 22px 18px;
    border: 1px solid rgba(var(--white-rgb), 0.14);
    border-radius: 16px;
    background: var(--ink-900);
    color: var(--ink-200);
    box-shadow: 0 24px 60px rgba(var(--black-rgb), 0.55);
}

.confirmation-sortie::backdrop {
    background: rgba(var(--black-rgb), 0.62);
    backdrop-filter: blur(2px);
}

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

.confirmation-sortie__texte {
    margin: 0 0 18px;
    font-size: 14.5px;
    line-height: 1.55;
    color: rgba(var(--ink-200-rgb), 0.72);
}

/* La saisie exigee pour les gestes graves : retaper le nom de ce qu'on
   efface. Elle n'apparait que dans ce cas, et prend alors le focus. */
.confirmation-sortie__saisie {
    display: flex;
    flex-direction: column;
    gap: 6px;
    margin: 0 0 16px;
}

/*
 * `hidden` NE VAUT QUE PAR LA REGLE `[hidden] { display: none }`, et celle du
 * navigateur perd contre n'importe quel `display` pose par une de nos classes.
 * Le bloc de saisie ci-dessus etant en `display: flex`, et `.btn` en
 * `inline-block`, l'attribut ne les cachait pas.
 *
 * Consequence mesuree : TOUTE confirmation du produit affichait un champ de
 * saisie vide sous sa question, y compris celles qui ne demandent rien a
 * retaper (49 px de haut, releves en direct). Et le bouton « Confirmer »
 * restait visible sur une simple information, qui n'a pourtant rien a
 * confirmer.
 */
.confirmation-sortie__saisie[hidden],
.confirmation-sortie__actions .btn[hidden] {
    display: none;
}

.confirmation-sortie__saisie label {
    margin: 0;
    font-size: 13px;
    color: rgba(var(--ink-200-rgb), 0.78);
}

.confirmation-sortie__saisie input {
    margin: 0;
}

.confirmation-sortie__ecart {
    color: rgba(var(--red-rgb), 0.92);
    font-size: 12.5px;
}

.confirmation-sortie__actions {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 10px;
}

.confirmation-sortie__actions .btn {
    min-height: 42px;
}

/* ===== Les deux cas limites du lecteur ===== */

/*
 * Tres petite largeur : a 360 px, les douze boutons tombaient a 32 px de large,
 * sous le seuil ou l'on vise juste. On reprend la place sur la colonne
 * d'actions et sur les ecarts, qui sont les seuls postes compressibles.
 */
@media (max-width: 409px) {
    body.diagnostic-player-page .diagnostic-controls-bar {
        padding: 6px;
        gap: 5px;
    }

    body.diagnostic-player-page .diagnostic-controls-grid--desktop {
        gap: 4px;
    }

    body.diagnostic-player-page .diagnostic-actions--desktop {
        grid-template-columns: minmax(52px, 62px);
        gap: 4px;
    }

    body.diagnostic-player-page .diagnostic-actions .btn {
        font-size: 11px;
        padding-left: 3px;
        padding-right: 3px;
    }

    body.diagnostic-player-page .diagnostic-controls-grid--desktop .diagnostic-btn {
        font-size: 12px;
        padding: 6px 1px;
    }
}

/*
 * Paysage sur telephone : la hauteur utile descend sous 470 px et la carte,
 * qui fait la hauteur de son texte, ne tient plus au-dessus de la palette.
 * Plutot que de la laisser recouvrir les commandes, on la borne a la place
 * reellement disponible et on la laisse defiler.
 */
@media (max-height: 470px) {
    body.diagnostic-demo-page .diagnostic-demo-card {
        max-height: calc(100vh - var(--diag-palette-height, 112px) - 96px);
        overflow: auto;
        padding: 12px 14px 12px;
    }

    body.diagnostic-demo-page .diagnostic-demo-card__title {
        font-size: 17px;
    }

    body.diagnostic-demo-page .diagnostic-demo-card__text {
        font-size: 13px;
    }

    /* Le bandeau et la carte se resserrent : chaque pixel compte ici. */
    body.diagnostic-demo-page .diagnostic-demo-card {
        top: 62px;
    }

    /*
     * A l'etape de comparaison, la palette disparait et la carte prend toute
     * la largeur : sans borne, elle mange la hauteur des deux images. Elle
     * cede donc la place et defile si son texte est long.
     */
    body.diagnostic-demo-page .diagnostic-demo-card.is-compare-mode {
        max-height: 42vh;
        overflow: auto;
    }
}

/*
 * Ecran plus large que haut : les deux images se placent cote a cote, meme sous
 * 900 px. Les empiler consomme la hauteur, qui est justement ce qui manque.
 */
@media (max-width: 900px) and (orientation: landscape) {
    body.diagnostic-demo-page .diagnostic-demo-compare {
        grid-template-columns: auto auto;
        justify-content: center;
        align-content: center;
    }

    /* La carte laisse la moitie basse aux images. */
    body.diagnostic-demo-page .diagnostic-demo-card.is-compare-mode {
        max-height: 38vh;
    }
}

/* ===== Garde de largeur pour la barre de commandes =====
   Ecrite en dernier, donc gagnante a specificite egale. Elle rend le
   depassement impossible quelle que soit la combinaison de media queries
   qui s'applique. */

body.diagnostic-player-page .diagnostic-controls-bar {
    max-width: calc(100vw - 20px);
    box-sizing: border-box;
    overflow: hidden;
}

/* Un enfant de flex ne retrecit pas sous son contenu tant qu'on ne l'y
   autorise pas : sans ces trois lignes, la grille pousse la barre au-dela
   de la largeur de l'ecran. */
body.diagnostic-player-page .diagnostic-controls-grid,
body.diagnostic-player-page .diagnostic-actions {
    min-width: 0;
}

body.diagnostic-player-page .diagnostic-controls-grid .diagnostic-btn {
    min-width: 0;
}

/* LES DEUX ACTIONS DE LA PALETTE SE DECIDENT AILLEURS, ET UNE SEULE FOIS.

   Il y avait ici une regle qui posait « Quitter » en blanc translucide. Elle
   etait MORTE : la section « D3 bis », plus bas dans cette feuille, vise
   `.diagnostic-app .diagnostic-controls-bar .btn...`, cinq classes contre
   quatre, et c'est elle qui peint reellement les deux boutons, en ardoise
   pleine et en vert plein. Mesure a l'ecran : `#4d6775` et `#15803d`, aucune
   transparence, sur le test principal comme sur l'entrainement.

   On ne remet donc rien ici. Deux endroits qui decident la meme couleur
   finissent par ne plus dire la meme chose, et c'est le premier lu qui a l'air
   d'avoir raison. La decision, ses etats et ses raisons sont dans D3 bis. */

/* ===== Code promotionnel ===== */

.billing-promo {
    display: grid;
    grid-template-columns: minmax(0, 320px) auto;

    /* `stretch` et non `center` : le bouton prend la hauteur du champ, ce qui
       aligne leurs deux bords plutot que leurs seuls milieux. */
    align-items: stretch;

    gap: 10px;
    margin-bottom: 18px;
}

/* Le libelle, le lien de retrait et le message occupent toute la largeur :
   seuls le champ et le bouton partagent une ligne. */
.billing-promo > label,
.billing-promo__clear,
.billing-promo__state {
    grid-column: 1 / -1;
}

.billing-promo > label {
    font-weight: 600;
    margin-bottom: -2px;
}

.billing-promo > input {
    width: 100%;
    box-sizing: border-box;
    min-height: 42px;
}

.billing-promo > .btn {
    min-height: 42px;
    padding-top: 0;
    padding-bottom: 0;
    white-space: nowrap;
}

.billing-promo__clear {
    justify-self: start;
    font-size: 13px;
}

.billing-promo__state {
    margin: 0;
    font-size: 14px;
}

@media (max-width: 560px) {
    .billing-promo {
        grid-template-columns: 1fr;
    }
}

/* ===== /admin/billing : deux niveaux de grille =====

   Premier niveau : les elements d'une liste (formules, lots, coupons) se
   rangent en autant de colonnes que la largeur le permet. `auto-fill` decide
   seul : deux colonnes sur un portable, quatre sur un grand ecran.

   Second niveau : a l'interieur d'un element, chaque champ prend la place que
   son contenu merite. Un prix et une date n'ont pas besoin d'une ligne entiere. */

.admin-billing-liste {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(270px, 1fr));
    gap: 14px;
    margin-top: 14px;
    align-items: start;
}

/* Les coupons ont deux fois plus de champs : leurs cartes demandent plus de
   largeur avant de se diviser. */
.admin-billing-liste--large {
    grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
}

.admin-billing-item {
    display: flex;
    flex-direction: column;
    gap: 10px;
    padding: 14px 16px 16px;
    border: var(--filet-epaisseur) solid var(--filet-large);
    border-radius: 12px;
    background: rgba(var(--white-rgb), 0.04);
    box-shadow: none;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
}

/* La carte de creation se distingue de celles qui modifient l'existant. */
.admin-billing-item--nouveau {
    border-style: dashed;
    border-color: rgba(var(--slate-light-rgb), 0.4);
    background: transparent;
}

.admin-billing-item h3 {
    margin: 0;
    font-size: 16px;
    line-height: 1.35;
}

.admin-billing-item h3 small {
    display: block;
    font-weight: 400;
    opacity: .72;
    margin-top: 2px;
}

.admin-billing-item p {
    margin: 0;
}

/* Le bouton reste en bas, aligne d'une carte a l'autre meme quand les textes
   au-dessus n'ont pas la meme longueur. */
.admin-billing-item > .btn {
    margin-top: auto;
    align-self: flex-start;
}

/* --- Les champs d'un element --- */

.admin-billing-champs {
    display: grid;
    /* 92 px et non 110 : mesure a l'appui, ce seuil fait passer une carte de
       coupon de deux a trois colonnes internes et lui fait gagner 96 px de
       hauteur, sans rien coûter aux cartes plus simples. */
    grid-template-columns: repeat(auto-fit, minmax(92px, 1fr));
    gap: 10px 12px;
    align-items: end;
}

.admin-billing-champs .champ {
    display: flex;
    flex-direction: column;
    gap: 4px;
    min-width: 0;
}

.admin-billing-champs .champ label {
    font-size: 13px;
    font-weight: 600;
}

.admin-billing-champs .champ input,
.admin-billing-champs .champ select {
    width: 100%;
    box-sizing: border-box;
    min-height: 42px;
}

/* Un nombre ou une date : une colonne suffit. */
.admin-billing-champs .champ--court { grid-column: span 1; }

/* Une liste deroulante ou un code : deux colonnes quand la place existe. */
.admin-billing-champs .champ--moyen { grid-column: span 2; }

/* Un texte libre prend toute la largeur de la carte. */
.admin-billing-champs .champ--large { grid-column: 1 / -1; }

/* Une case a cocher n'a pas de libelle au-dessus : elle s'aligne sur la ligne
   des champs plutot que de creer sa propre rangee. */
.admin-billing-champs .champ--case {
    grid-column: 1 / -1;
    flex-direction: row;
    align-items: center;
}

.admin-billing-champs .champ--case .promo-checkbox {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 0;
    font-size: 14px;
}

.admin-billing-champs .champ--case input {
    width: auto;
    min-height: 0;
}

/* Sur une carte etroite, les gabarits larges cessent d'avoir un sens. */
@media (max-width: 520px) {
    .admin-billing-champs {
        grid-template-columns: 1fr 1fr;
    }

    .admin-billing-champs .champ--moyen {
        grid-column: 1 / -1;
    }
}

/* ===== Coupons : une ligne par code =====

   Sept champs repetes en cartes, cela faisait sept libelles par element. En
   lignes, chaque colonne est nommee une fois en haut et les valeurs
   s'alignent : deux coupons se comparent d'un coup d'oeil.

   Chaque coupon reste un `<form>` : `display: contents` le retire de la mise
   en page pour que ses cellules deviennent celles de la grille, sans lui
   enlever sa capacite a etre soumis seul. */

.admin-billing-tableau {
    display: grid;
    grid-template-columns: repeat(6, minmax(90px, 1fr));    /* action */
    gap: 8px 10px;
    align-items: center;
    margin-top: 14px;
    row-gap: 6px;
}

/* --- 10. Un coupon tient sur deux lignes ---------------------------------
   -------------------------------------------------------------------------
   Le tableau des coupons pose DOUZE colonnes sur une seule rangee : le code, la
   remise, le type, la duree, le nombre d'utilisations, le maximum par compte,
   la validite, les cibles, l'etat, l'action. Elles demandent 1136 px et en ont
   1166, donc « ca tient » au sens de la grille ; mais chaque colonne fait 64 px,
   et un champ de 62 px y voisine son etiquette au point que « mc » et « 1 » se
   chevauchent a l'oeil.

   SIX COLONNES, DEUX RANGEES PAR COUPON. Un coupon est un objet, pas une ligne
   de tableau : rien n'oblige a le mettre a plat. Sur deux rangees, chaque champ
   a le double de largeur, les etiquettes tiennent sans se couper, et la lecture
   reste horizontale.

   L EN-TETE GLOBAL DISPARAIT, et c'est ce que le passage a deux rangees a
   revele : un en-tete de tableau suppose UNE rangee par ligne. Sur deux, ses
   douze libelles se regroupaient en haut, six et six, et « Nombre maximal
   d'utilisations » se retrouvait au-dessus de « Code ». Le lien entre un nom et
   son champ etait rompu.

   CHAQUE CELLULE PORTE DEJA SON `<label>`, verifie dans le balisage : ils
   etaient simplement masques parce que l'en-tete les repetait. Il n'y a donc
   rien a ecrire, seulement a reveler ce qui existe. Un coupon devient une
   FICHE, ou chaque valeur est nommee a cote d'elle, ce qui est la bonne forme
   pour un objet qu'on modifie et non qu'on compare.
   ------------------------------------------------------------------------- */
.admin-billing-tableau__entete {
    display: none;
}

.admin-billing-tableau__entete > span {
    font-size: 12px;
    font-weight: 700;
    letter-spacing: .03em;
    opacity: .72;
    padding-bottom: 2px;
    border-bottom: 1px solid rgba(var(--slate-light-rgb), 0.22);
}

.admin-billing-ligne {
    display: contents;
}

.admin-billing-tableau .cellule {
    min-width: 0;

    /* La cellule sert de repere a son libelle masque : sans ancetre positionne,
       celui-ci se calait sur le bloc englobant de la page. */
    position: relative;
}

/*
 * LES LIBELLES VIVENT DANS L'ENTETE.
 *
 * Ceux des champs restent presents pour les lecteurs d'ecran, mais ne prennent
 * plus de place a l'oeil.
 *
 * `left` ET `top`, et non `position: absolute` seul : sans coordonnees, une
 * boite absolue reste a sa position statique et peut etendre la zone defilable.
 * Correction de forme, prise ici par precaution.
 *
 * Elle n'est PAS la cause des douze pixels de debordement de la page a 375 px :
 * a cette largeur ces libelles ne sont plus masques du tout, le repli en cartes
 * plus bas les fait reapparaitre. La cause etait le `white-space` ci-dessous,
 * que ce repli oubliait de remettre. Mesure en session reelle, page « Tarifs »,
 * trois coupons : document 377 pour 365 de fenetre, le texte du libelle
 * s'arretant a 376.
 */
/* LE SELECTEUR EST RECOPIE MOT POUR MOT, `:not(.promo-checkbox)` compris.
   Cinquieme fois dans ce chantier : ma version sans le `:not` avait une
   pseudo-classe de moins, donc elle perdait malgre l'ordre de chargement.

   ET LE PRODUIT PREVOYAIT DEJA CETTE FORME : la meme regle existe dans une
   media query de `lk.css`, qui rend ces libelles visibles en ecran etroit,
   quand le tableau passe en cartes. Ce que je fais ici revient a adopter la
   forme etroite en ecran large, parce qu'un coupon sur deux rangees EST une
   carte. Les valeurs sont donc reprises de la, pas inventees. */
.admin-billing-tableau .cellule > label:not(.promo-checkbox) {
    position: static;
    left: 0;
    top: 0;
    width: auto;
    height: auto;
    padding: 0;
    margin: 0 0 5px;
    overflow: visible;
    clip-path: none;
    white-space: normal;
    clip: auto;
    display: block;
    font-size: 11.5px;
    font-weight: var(--graisse-moyenne);
    color: rgba(var(--white-rgb), 0.54);
    line-height: 1.3;
}

/* `input` sans qualifier prenait AUSSI la case a cocher : elle heritait de
   `width: 100%` et de 42 px de haut, et s'affichait en carre geant au milieu
   de sa colonne. On vise les champs de saisie. */
.admin-billing-tableau .cellule input:not([type="checkbox"]):not([type="radio"]),
.admin-billing-tableau .cellule select {
    width: 100%;
    box-sizing: border-box;
    min-height: 42px;
}

/* La cellule s'aligne sur la ligne de saisie de ses voisines, pas sur leur
   libelle : c'est le seul objet de la rangee qui n'en a pas au-dessus. */
/* --- 15. « Coupon actif », et son compteur juste dessous ------------------
   -------------------------------------------------------------------------
   DEUX CHOSES QUI SE CHERCHAIENT. « Utilise 0 fois sur ∞ » vivait sous le
   bouton « Enregistrer », c'est-a-dire sous une ACTION, alors qu'il ne dit rien
   de l'enregistrement : il dit l'usage du coupon. Et l'interrupteur d'a cote
   annoncait « Actif, donc vendable », qui explique une consequence au lieu de
   nommer un etat.

   Le compteur passe donc sous l'interrupteur, dont il est la suite naturelle :
   « ce coupon est actif », « il a servi tant de fois ». Les deux se lisent
   ensemble, et le bouton retrouve sa colonne pour lui seul.

   LE LIBELLE DEVIENT « Coupon actif ». « Donc vendable » etait un raisonnement,
   pas un nom : il repondait a une question que personne ne pose devant un
   interrupteur. Ce qu'on veut savoir est si le coupon marche.

   A L APPLICATION : la chaine `Actif, donc vendable` devient `Coupon actif`
   dans les quatre langues, et le `<small>` du compteur passe dans la cellule de
   l'interrupteur.
   ------------------------------------------------------------------------- */
.admin-billing-tableau .cellule--case {
    display: flex;
    justify-content: center;
    align-self: flex-end;
    padding-bottom: 0;
    flex-direction: column;
    align-items: flex-start;
    gap: 6px;
}

/* La case reprend la facture de `.checkbox-row`, qui est celle du produit :
   sortie de la regle des champs, elle retombait sinon a la taille native du
   navigateur, treize pixels, a cote de champs hauts de quarante-deux. */
.admin-billing-tableau .cellule input[type="checkbox"],
.admin-billing-champs .champ--case input[type="checkbox"] {
    width: 18px;
    height: 18px;
    margin: 0;
    accent-color: var(--couleur-accent);
    flex: 0 0 auto;
}

.admin-billing-tableau .cellule--case .promo-checkbox {
    margin: 0;
    display: flex;
    align-items: center;
    gap: 10px;
}

/* Le libelle « Actif » est deja dans l'entete : la case se suffit. */
/* --- 14. Le switch cesse d'etre seul -------------------------------------
   -------------------------------------------------------------------------
   Il n'avait AUCUN libelle visible : « Actif, donc vendable » vit dans un
   `<span>` que `lk.css` masque par la meme technique accessible que les autres
   libelles. Un interrupteur sans mot, pose au milieu d'une rangee de champs
   nommes, ne dit ni ce qu'il commande ni ce qu'il vaut.

   Son texte revient a cote de lui, et non au-dessus comme les autres libelles :
   un interrupteur se lit « ceci est actif », d'un seul tenant, alors qu'un champ
   se lit « voici le nom, voici la valeur ».
   ------------------------------------------------------------------------- */
.admin-billing-tableau .cellule--case .promo-checkbox span {
    position: static;
    width: auto;
    height: auto;
    overflow: visible;
    clip-path: none;
    margin: 0;
    white-space: normal;
    font-size: 12.5px;
    font-weight: var(--graisse-moyenne);
    color: rgba(var(--white-rgb), 0.82);
}

/* L'action n'a pas de libelle : elle s'aligne sur le bas des champs voisins. */
.admin-billing-tableau .cellule--action {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 2px;
    justify-content: flex-end;
}

.admin-billing-ligne__compteur {
    font-size: 11px;
    opacity: .7;
    text-align: center;
}

/* La duree tient en une cellule : le choix, et le nombre de mois qu'il
   commande. Deux colonnes de tableau pour deux champs indissociables auraient
   laisse un nombre de mois orphelin a cote d'une remise sans fin. */
.coupon-duree {
    display: flex;
    gap: 6px;
    align-items: center;
}

.admin-billing-tableau .cellule .coupon-duree select {
    flex: 1 1 auto;
    min-width: 0;
}

.admin-billing-tableau .cellule .coupon-duree__mois {
    flex: 0 0 62px;
    width: 62px;
    text-align: center;
}

/* Desactive, il reste lisible mais dit qu'il ne sert pas : un champ eteint
   sans signe se lit comme un champ qu'on a oublie de remplir. */
.coupon-duree__mois:disabled {
    opacity: .45;
    cursor: not-allowed;
}

/* --- Les categories visees : un panneau qui se pose par-dessus --- */

.coupon-cibles {
    position: relative;
}

.coupon-cibles > summary {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 6px;
    min-height: 42px;
    padding: 0 10px;
    box-sizing: border-box;
    border: 1px solid rgba(var(--slate-light-rgb), 0.32);
    border-radius: 8px;
    font-size: 13px;
    cursor: pointer;

    /* Le triangle natif se place mal dans une boite en `flex` ; on le remplace
       par un chevron qui suit l'etat ouvert. */
    list-style: none;
}

.coupon-cibles > summary::-webkit-details-marker { display: none; }

.coupon-cibles > summary::after {
    content: '';
    width: 7px;
    height: 7px;
    border-right: 2px solid currentColor;
    border-bottom: 2px solid currentColor;
    transform: rotate(45deg) translate(-2px, -2px);
    opacity: .6;
    flex: 0 0 auto;
}

.coupon-cibles[open] > summary::after {
    transform: rotate(-135deg) translate(-2px, -2px);
}

/*
 * Le panneau sort du flux. Sans cela il etirerait sa ligne de tableau, et
 * pousserait les coupons suivants de cent pixels a chaque ouverture.
 */
/* LE PANNEAU DE CHOIX RESTE OPAQUE, et c'est une correction de ma propre
   erreur : je l'avais range avec les cartes de formule et passe a
   `rgba(blanc,0.04)`, alors qu'il FLOTTE au-dessus du contenu. Resultat, on
   lisait le tableau au travers de la liste des categories.

   Une surface translucide suppose qu'on sait ce qu'il y a derriere. Un panneau
   flottant est justement la pour qu'on ne le sache plus. Il garde donc une
   ardoise pleine, et gagne l'ombre qui le decolle de ce qu'il recouvre. */
.coupon-cibles__panneau {
    position: absolute;
    z-index: 20;
    top: calc(100% + 4px);
    left: 0;
    min-width: 240px;
    padding: 10px 12px;
    box-sizing: border-box;
    border: var(--filet-epaisseur) solid var(--filet-petit);
    border-radius: 12px;

    /* Opaque, sinon les champs places dessous restent lisibles au travers.
       La carte qui l'entoure pose la meme teinte a 70 % ; le panneau la reprend
       pleine, pour qu'il se detache sans changer de famille. */
    background: rgb(var(--slate-mid-rgb));
    box-shadow: 0 14px 38px rgba(0, 0, 0, 0.52);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
}

.coupon-cibles__famille {
    margin: 8px 0 4px;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: .04em;
    text-transform: uppercase;
    opacity: .66;
}

.coupon-cibles__famille:first-child {
    margin-top: 0;
}

.coupon-cibles__case {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 3px 0;
    font-size: 13px;
    cursor: pointer;
    white-space: nowrap;
}

/* La ligne de creation se distingue de celles qui modifient l'existant. */
.admin-billing-ligne--nouveau .cellule input,
.admin-billing-ligne--nouveau .cellule select {
    border-style: dashed;
}

/* --- Repli en cartes quand les colonnes n'ont plus la place --- */
@media (max-width: 1100px) {
    .admin-billing-tableau {
        grid-template-columns: 1fr;
        gap: 14px;
    }

    .admin-billing-tableau__entete {
        display: none;
    }

    /* Chaque coupon redevient une carte, et ses libelles reapparaissent. */
    .admin-billing-ligne {
        display: grid;
        grid-template-columns: repeat(auto-fit, minmax(92px, 1fr));
        gap: 10px 12px;
        align-items: end;
        padding: 14px 16px;
        border: 1px solid rgba(var(--slate-light-rgb), 0.22);
        border-radius: 12px;
        background: rgba(var(--slate-light-rgb), 0.05);
    }

    .admin-billing-tableau .cellule > label:not(.promo-checkbox),
    .admin-billing-tableau .cellule--case .promo-checkbox span {
        position: static;
        width: auto;
        height: auto;
        margin: 0;
        overflow: visible;
        clip-path: none;
        font-size: 13px;
        font-weight: 600;

        /* Le `nowrap` vient du masquage accessible, ou il empeche un libelle
           haut d'un pixel de se couper en deux lignes. Ici le libelle est
           REDEVENU VISIBLE et le nowrap le suivait : « Nombre maximal
           d'utilisations » mesure 188 px et refusait de se couper dans une
           piste de 92, donc il sortait de l'ecran et emportait la page avec
           lui. Mesure a 375 px : document 377 pour 365 de fenetre. */
        white-space: normal;
    }

    .admin-billing-tableau .cellule {
        display: flex;
        flex-direction: column;
        gap: 4px;
    }

    .admin-billing-tableau .cellule--libelle,
    .admin-billing-tableau .cellule--cible,
    .admin-billing-tableau .cellule--case,
    .admin-billing-tableau .cellule--action {
        grid-column: 1 / -1;
    }

    /* En cartes, rien ne suit horizontalement : le panneau peut reprendre sa
       place dans le flux, ce qui evite qu'il deborde de la carte a droite. */
    .admin-billing-tableau .coupon-cibles__panneau {
        position: static;
        min-width: 0;
        box-shadow: none;
    }

    .admin-billing-tableau .cellule--case {
        flex-direction: row;
        align-items: center;
        justify-content: flex-start;
    }

    .admin-billing-tableau .cellule--action {
        flex-direction: row;
        align-items: center;
        gap: 10px;
    }
}

/* ===== Fiche profil : accorder un acces, attribuer un code =====

   Ces deux formulaires n'avaient aucun style : chaque champ prenait toute la
   largeur pour y poser une liste de trois valeurs, et le bouton se collait
   sous le dernier champ. Une grille suffit a leur rendre des proportions. */

.admin-billing-grant {
    display: grid;
    grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
    gap: 12px 14px;

    /* Les champs s'alignent par le bas : le bouton se retrouve ainsi au meme
       niveau que le champ qu'il conclut, et non au milieu de sa cellule. */
    align-items: end;

    margin: 12px 0 24px;
    max-width: 720px;
}

.admin-billing-grant .champ {
    display: flex;
    flex-direction: column;
    gap: 4px;
    min-width: 0;

    /*
     * Le champ colle au BAS de sa cellule.  aligne les
     * cellules entre elles, pas leur contenu : quand une cellule est plus
     * haute que son libelle et son champ reunis, le champ flottait 16 px
     * au-dessus du bouton place a cote.
     */
    justify-content: flex-end;
}

.admin-billing-grant .champ label {
    font-size: 13px;
    font-weight: 600;
}

.admin-billing-grant .champ input,
.admin-billing-grant .champ select {
    width: 100%;
    box-sizing: border-box;
    min-height: 42px;
}

/* Une duree en mois n'a pas besoin de la largeur d'un intitule de formule. */
.admin-billing-grant .champ--etroit {
    max-width: 220px;
}

/* La liste des codes est longue : elle prend la ligne, le bouton la ferme. */
.admin-billing-grant .champ--large {
    grid-column: 1;
}

.admin-billing-grant > .btn {
    min-height: 42px;
    align-self: end;
    white-space: nowrap;
}

/* Le formulaire de retrait n'a qu'un bouton : il ne merite pas une grille. */
.admin-billing-grant:has(> .btn:only-child) {
    display: block;
    margin-top: -12px;
}

@media (max-width: 640px) {
    .admin-billing-grant {
        grid-template-columns: 1fr;
    }

    .admin-billing-grant .champ--etroit {
        max-width: none;
    }
}

/* ===== Les grilles gerent leur propre espacement =====
   La regle groupee des champs pose `margin-bottom: 16px`, juste dans un
   formulaire en flux mais parasite dans une grille : elle s'ajoute au `gap`
   et decale les champs par rapport aux boutons places a cote. */

.admin-billing-grant .champ input,
.admin-billing-grant .champ select,
.admin-billing-grant .champ textarea,
.admin-billing-champs .champ input,
.admin-billing-champs .champ select,
.admin-billing-champs .champ textarea,
.admin-billing-tableau .cellule input:not([type="checkbox"]):not([type="radio"]),
.admin-billing-tableau .cellule select,
.billing-promo > input {
    margin-bottom: 0;
}

/* ===== Les listes deroulantes suivent le theme =====
   Meme raison que dans le lecteur : sans `color-scheme`, le menu natif d'un
   `<select>` s'ouvre en blanc par-dessus une interface sombre. On le declare
   sur les champs plutot que sur la racine, pour ne rien imposer aux pages qui
   ne seraient pas sombres. */
body.lk-page select,
body.lk-page input[type="date"],
body.lk-page input[type="time"],
body.lk-page input[type="datetime-local"],
body.diagnostic-player-page select,
body.diagnostic-demo-page select {
    color-scheme: dark;
}

/* Meme raison que dans le lecteur : des que le champ a un fond personnalise,
   le navigateur redessine sa liste en clair. Les options doivent donc porter
   leurs couleurs, et en opaque. */
body.lk-page select option,
body.diagnostic-player-page select option,
body.diagnostic-demo-page select option {
    background-color: var(--slate-mid);
    color: var(--ink-50);
}

/* =========================================================================
   La barre du haut
   -------------------------------------------------------------------------
   Marque a gauche, navigation au centre, compte a droite.

   ELLE EST COLLANTE, MAIS SEULEMENT AU-DESSUS DE 900 PX. Cette feuille a
   longtemps porte l'inverse, et la raison qu'elle donnait reste vraie pour le
   telephone : la hauteur y est precieuse. Elle ne l'est pas sur un grand
   ecran, et six entrees de navigation qu'il faut aller rechercher en haut de
   page a chaque changement d'espace coutent un aller-retour a chaque fois.

   SOUS 900 PX ELLE REDEVIENT ORDINAIRE. La navigation a deja quitte la barre
   pour le menu a cette largeur : il ne reste que la marque et la pastille, et
   coller cela prendrait 63 px de hauteur en permanence pour presque aucun
   service. La borne est celle qui existait deja, pas une seconde inventee a
   cote : la bascule se fait dans le meme bloc `@media`, plus bas.

   RIEN D AUTRE N A BOUGE. Le menu du compte se positionne contre
   `.lk-top-bar__compte`, qui porte son propre `position: relative` : il ne
   depend pas de celui de la barre. Et `sticky` cree un contexte d'empilement
   comme `relative`, donc le `z-index` ci-dessous garde exactement son sens.
   ========================================================================= */

.lk-top-bar {
    /* Trois colonnes : la marque, la navigation, le compte.
       
       LA NAVIGATION SE CENTRE ENTRE LES DEUX, pas sur la barre. Centree sur la
       barre (`1fr auto 1fr`), sa colonne prenait toute la largeur qu'elle
       demandait et les deux autres, qui ne savent pas retrecir, passaient
       dessous : le nom de la marque se lisait a travers « Mon parcours ».
       
       `minmax(0, 1fr)` au milieu donne l'inverse : les deux extremes prennent
       ce qu'il leur faut, la navigation prend le reste et jamais plus. Quand
       ce reste devient trop petit, elle se raccourcit puis disparait, mais
       elle ne recouvre rien. */
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: center;
    gap: 20px;
    padding: 10px 20px;
    margin-bottom: 18px;

    /*
     * La barre porte le plan, pas le menu : elle cree ainsi son propre
     * contexte d'empilement et tout son contenu monte avec elle. Au-dessus du
     * contenu des pages, en dessous des dialogues, qui doivent la couvrir.
     */
    position: sticky;
    top: 0;
    z-index: 1500;

    background: rgba(var(--slate-rgb), 0.72);
    border-bottom: 1px solid rgba(var(--slate-light-rgb), 0.16);
    backdrop-filter: blur(14px);
}

.lk-top-bar__brand {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    text-decoration: none;
    color: inherit;
    flex: 0 0 auto;
    justify-self: start;
}

/* La marque sur deux lignes, comme sur la vitrine. Le sous-titre y est en
   monospace : ce n'est pas une fantaisie, il porte le nom technique du
   procede, et la chasse fixe le distingue du nom commercial au-dessus. */
.lk-top-bar__brand-texte {
    display: grid;
    gap: 1px;
    min-width: 0;
}

.lk-top-bar__brand-subtitle {
    font-family: var(--lv-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
    font-size: 10px;
    font-weight: 400;
    letter-spacing: 0.06em;
    line-height: 1.2;
    color: rgba(var(--white-rgb), 0.5);
    text-transform: none;
    white-space: nowrap;
}
/* `.lk-top-bar__brand img, .lk-top-bar__brand svg` etait declare deux fois. Ce premier bloc
   etait entierement reecrit l.7558 : a selecteur et poids egaux,
   la derniere regle ecrite gagne, celle-ci n'imposait donc rien. */

.lk-top-bar__brand-name {
    white-space: nowrap;
}

.lk-top-bar__nav {
    display: flex;
    align-items: center;
    gap: 4px;

    /* Centree dans SA colonne, celle qui reste entre la marque et le compte. */
    justify-content: center;
    min-width: 0;
}

.lk-top-bar__link {
    padding: 8px 14px;
    border-radius: 10px;
    color: rgba(var(--ink-200-rgb), 0.82);
    text-decoration: none;
    font-size: 14px;
    white-space: nowrap;
    transition: background .18s ease, color .18s ease;
}

.lk-top-bar__link:hover {
    background: rgba(var(--slate-light-rgb), 0.12);
    color: var(--white);
}

.lk-top-bar__link.is-active {
    background: rgba(var(--portal-accent-rgb), 0.22);
    color: var(--white);
    font-weight: 600;
}

/* --- Le compte --- */

.lk-top-bar__compte {
    position: relative;
    flex: 0 0 auto;
    /* `margin-left: auto` poussait le bloc a droite tant que la barre etait en
       flex. En grille il occupe la troisieme colonne, et c'est `justify-self`
       qui le cale au bout ; la marge automatique y elargirait la colonne. */
    justify-self: end;
}

.lk-top-bar__compte-bouton {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 8px 12px;
    border-radius: 10px;
    border: 1px solid rgba(var(--slate-light-rgb), 0.24);
    background: rgba(var(--slate-light-rgb), 0.08);
    color: var(--ink-200);
    font: inherit;
    font-size: 14px;
    cursor: pointer;
}

.lk-top-bar__compte-bouton:hover {
    background: rgba(var(--slate-light-rgb), 0.16);
}

.lk-top-bar__prenom {
    max-width: 18ch;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.lk-top-bar__chevron {
    font-size: 12px;
    opacity: .7;
    transition: transform .18s ease;
}
/* `.lk-top-bar__compte-bouton[aria-expanded="true"] .lk-top-bar__chevron` etait declare deux fois. Ce premier bloc
   etait entierement reecrit l.7667 : a selecteur et poids egaux,
   la derniere regle ecrite gagne, celle-ci n'imposait donc rien. */

/* --- Le menu deroulant --- */

.lk-menu {
    position: absolute;
    top: calc(100% + 8px);
    right: 0;

    /* Dans le contexte de la barre : il lui suffit de passer devant ses freres
       immediats, la barre se charge du reste. */
    z-index: 1;

    min-width: 232px;
    padding: 6px;
    border-radius: 14px;
    background: var(--ink-900);
    background: rgba(var(--ink-900-rgb), 0.98);
    border: 1px solid rgba(var(--slate-light-rgb), 0.2);
    box-shadow: 0 18px 44px rgba(var(--black-rgb), 0.42);
}

.lk-menu[hidden] {
    display: none;
}

.lk-menu__groupe + .lk-menu__groupe {
    margin-top: 6px;
    padding-top: 6px;
    border-top: 1px solid rgba(var(--slate-light-rgb), 0.16);
}

/* La navigation n'apparait dans le menu que sur ecran etroit : ailleurs elle
   est dans la barre, et l'y repeter ferait doublon. */
.lk-menu__groupe--navigation {
    display: none;
}

/* Un groupe masque laisse quand meme sa separation : `+` regarde le DOM, pas
   l'affichage. Une barre flottait donc en haut du menu, au-dessus de la
   premiere entree, sans rien separer. */
.lk-menu__groupe--navigation + .lk-menu__groupe {
    margin-top: 0;
    padding-top: 0;
    border-top: 0;
}

.lk-menu__lien {
    display: block;
    width: 100%;
    box-sizing: border-box;
    padding: 9px 12px;
    border: 0;
    border-radius: 10px;
    background: transparent;
    color: rgba(var(--ink-200-rgb), 0.9);
    text-decoration: none;
    text-align: left;
    font: inherit;
    font-size: 14px;
    cursor: pointer;
}

.lk-menu__lien:hover {
    background: rgba(var(--slate-light-rgb), 0.14);
    color: var(--white);
}

.lk-menu__lien.is-active {
    color: var(--white);
    font-weight: 600;
}

/* La deconnexion se distingue : on ne la vise pas par erreur en cherchant un
   lien de navigation. */
.lk-menu__lien--sortie {
    color: var(--red-soft);
}

.lk-menu__lien--sortie:hover {
    background: rgba(var(--red-rgb), 0.16);
    color: var(--couleur-danger-doux);
}

.lk-menu__groupe--sortie form {
    margin: 0;
}

/* --- Ecran etroit : la navigation passe dans le menu --- */

@media (max-width: 900px) {
    /* La navigation disparait ici : la colonne du milieu n'a plus d'occupant,
       et la grille placerait le menu du compte au centre. On revient donc a
       deux blocs aux deux bouts. */
    .lk-top-bar {
        display: flex;
        justify-content: space-between;
        gap: 12px;
        padding: 10px 14px;

        /* Elle ne colle plus : voir l'en-tete de section. Ici il ne reste que
           la marque et la pastille, et la hauteur est comptee. */
        position: relative;
        top: auto;
    }

    /* La navigation quitte la barre et rejoint le menu. */
    .lk-top-bar__nav {
        display: none;
    }

    .lk-menu__groupe--navigation {
        display: block;
    }

    /* Elle est visible ici, donc elle separe vraiment quelque chose. */
    .lk-menu__groupe--navigation + .lk-menu__groupe {
        margin-top: 6px;
        padding-top: 6px;
        border-top: 1px solid rgba(var(--slate-light-rgb), 0.16);
    }

    /* LA PASTILLE RESTE, SEULE. Le prenom et le chevron s'effacent : le nom
       ne tient plus, et le chevron n'apprend rien. La pastille, elle, dit de
       qui il s'agit, ce qu'une icone a trois barres ne dit pas.

       Une autre regle, plus loin dans cette feuille, annoncait deja « il ne
       reste que la pastille » tout en la masquant ici : selon l'ordre, on
       obtenait l'icone, la pastille, ou les deux dans un bouton de quarante
       pixels. C'est cette superposition qui decentrait le contenu. */
    .lk-top-bar__prenom,
    .lk-top-bar__chevron {
        display: none;
    }

    .lk-top-bar__compte-bouton {
        width: 40px;
        height: 40px;
        /* Aucun rembourrage ni ecart : un seul enfant, centre dans les deux
           sens. Le rembourrage asymetrique venu du mode large decalait la
           pastille vers la gauche. */
        padding: 0;
        gap: 0;
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }

    /* L'icone a trois barres est retiree : elle cohabitait avec la pastille. */
    .lk-top-bar__compte-bouton::before {
        content: none;
    }

    .lk-menu {
        min-width: 248px;
    }
}

@media (max-width: 420px) {
    .lk-top-bar__brand-name {
        display: none;
    }
}

/* ===== Le lien d'invitation =====
   Visible et selectionnable, avec son bouton de copie a cote. Il n'existait
   que dans l'attribut d'un bouton : on pouvait le copier sans jamais le voir. */

/* Le bloc s'arrete avant de traverser l'ecran : passe la longueur du lien,
   l'espace en plus n'affiche rien de mieux. */
.partner-invite {
    display: flex;
    flex-direction: column;
    gap: 6px;
    min-width: 0;
    max-width: 680px;
}

.partner-invite__label {
    font-size: 12px;
    color: rgba(var(--ink-300-rgb), 0.7);
}

.partner-invite__ligne {
    display: flex;
    align-items: stretch;
    gap: 8px;
    min-width: 0;
}

/* `input[type="text"]` est plus specifique qu'une classe : sans le nom de
   balise ici, la marge basse commune de 16 px restait, s'ajoutait a la hauteur
   de la ligne, et etirait le bouton voisin a 59 px contre 36 naturels. */
input.partner-invite__champ {
    /* Le champ prend la place que le bouton laisse : c'est la largeur du bloc
       qui decide, et elle est faite pour montrer le lien en entier. Pas de
       base fixe ici : en colonne etroite la ligne bascule en pile, et une base
       en pixels y deviendrait une HAUTEUR de 440 px. */
    flex: 1 1 auto;
    min-width: 0;

    min-height: 42px;
    margin: 0;
    padding: 8px 12px;
    border-radius: 10px;
    border: 1px solid rgba(var(--slate-light-rgb), 0.26);
    background: rgba(var(--slate-light-rgb), 0.08);
    color: var(--ink-200);
    font-size: 13px;

    /* Un lien se lit caractere par caractere quand on le verifie. */
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}

.partner-invite__ligne .btn {
    flex: 0 0 auto;
    white-space: nowrap;
}

@media (max-width: 640px) {
    .partner-invite__ligne {
        flex-direction: column;
    }

    .partner-invite__ligne .btn {
        width: 100%;
    }
}

/* ===== Indicatif et numero =====
   Le composant vit dans `champ-telephone.css`, chargee par `layouts/app` et
   par la vitrine : il est rendu dans les deux mondes, et sa mecanique etait
   copiee dans les deux feuilles. Ici, plus rien. */

/* ======================================================================
   AUCUN INTITULÉ NE TOUCHE LE BORD DE SON CADRE
   ======================================================================
   Le rembourrage d'un bouton ne se fait jamais écraser : c'est le TEXTE qui
   déborde de sa boîte quand on lui interdit de passer à la ligne et qu'on
   plafonne la largeur. Il glisse alors sous le rembourrage et vient coller
   les bords.

   Nos intitulés font souvent le double en français de ce qu'ils font en
   russe, langue d'origine de ces écrans : le cas est fréquent, pas
   exceptionnel. La règle vaut donc partout.
   ====================================================================== */
.btn,
button.btn,
a.btn {
    white-space: normal;
    overflow-wrap: break-word;
}

/* ======================================================================
   UN BOUTON CENTRE SON TEXTE, DANS LES DEUX SENS
   ======================================================================
   `display: inline-block` laissait le centrage vertical au hasard de la
   hauteur de ligne : dès qu'un bouton avait une hauteur imposée, ou un
   intitulé sur deux lignes, le texte se calait en haut. Le centrage
   horizontal, lui, dépendait d'un `text-align` posé ou non selon l'endroit.

   En flex, les deux sont dits une fois pour toutes.
   ====================================================================== */
/* Sans effet sur ce qui n'est pas en flex : la regle peut donc etre large. */
button,
.btn,
[class*="-btn"] {
    align-items: center;
    justify-content: center;
    text-align: center;
}

/* Le passage en flex, lui, ne vise que les familles ou il est sur. */
.btn,
button.btn,
a.btn,
.diagnostic-viewer-tool-btn,
.therapy-action-btn,
.therapy-remove-btn,
.journey-support-icon-btn,
.diagnostic-viewer-nav__link {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    text-align: center;
}

/* La mention du programme, côté client, à la place du bouton du praticien. */
/*
 * LA MEME TEINTE QUE LE BOUTON DU PRATICIEN, pour le meme sujet.
 *
 * Cette mention occupe, chez le client, la place ou le praticien a son bouton
 * d'ajout a la commande. Les deux parlent du meme programme : ils portent donc
 * la meme couleur, et le client reconnait ce dont son praticien lui parlera.
 */
/*
 * LE MOT DE VERIFICATION, MEME FORME QUE LA MENTION DES PROGRAMMES.
 *
 * C'etait un grand encadre plein en haut de page, plus lourd que le resultat
 * qu'il commente : il se lisait comme une panne. Il dit une chose a verifier,
 * pas une alerte a traiter en urgence.
 *
 * Meme mesures que `brain-system-block__programme`, en rouge. Le bouton se
 * pose dedans, discret : c'est un accuse de lecture, pas une action.
 */
.brain-review-notice {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 10px 14px;
    margin: 0 0 16px;
    padding: 8px 14px;
    border-radius: 10px;
    border: 1px solid rgba(var(--teinte-rouge), 0.30);
    background: rgba(var(--teinte-rouge), 0.10);
    color: rgba(var(--teinte-rouge), 0.94);
    font-size: 13px;
    line-height: 1.45;
}

.brain-review-notice__texte {
    margin: 0;
    min-width: 0;
    flex: 1 1 32ch;
}

.brain-review-notice__form {
    margin: 0;
    flex: 0 0 auto;
}

.brain-review-notice__bouton {
    padding: 5px 12px;
    border-radius: var(--rayon-pilule);
    border: 1px solid rgba(var(--teinte-rouge), 0.34);
    background: rgba(var(--teinte-rouge), 0.14);
    color: rgba(var(--teinte-rouge), 0.94);
    font: inherit;
    line-height: 1.2;
    cursor: pointer;
}

.brain-review-notice__bouton:hover {
    background: rgba(var(--teinte-rouge), 0.22);
}

.brain-system-block__programme {
    margin: 0;
    padding: 8px 14px;
    border-radius: 10px;
    border: 1px solid rgba(var(--teinte-or), 0.26);
    background: rgba(var(--teinte-or), 0.10);
    color: rgba(var(--teinte-or), 0.94);
    font-size: 13px;
    line-height: 1.45;
}

/* ======================================================================
   LES CONTRÔLES NATIFS SUIVENT LE THÈME
   ======================================================================
   La liste ouverte d'un `<select>` n'est pas dessinée par la page : c'est le
   système qui la rend. Aucune règle sur `option` ne la maîtrise vraiment, et
   les tentatives donnent des résultats différents d'un navigateur à l'autre.

   `color-scheme` est la seule prise qu'on ait dessus, et elle est officielle :
   elle dit au navigateur que cette page est sombre, et il rend TOUS ses
   contrôles en conséquence — la liste déroulante, les barres de défilement,
   les sélecteurs de date, les cases à cocher.

   Une déclaration, et le blanc du système disparaît partout.
   ====================================================================== */
/* `color-scheme` et les barres de défilement sont désormais déclarés une seule
   fois, dans `jetons.css`, qui est chargée partout — y compris sur la vitrine
   et sur les pages du parcours de test, que cette feuille-ci n'atteint pas.

   Ce bloc faisait exactement la même chose, avec une teinte différente, et il
   gagnait par ordre de chargement : la barre de tous les écrans connectés
   était ardoise pendant que celle des panneaux internes était blanche. C'était
   le seul écart qui restait, et il venait d'une règle en double, pas d'un
   désaccord de charte.

   Le pouce garde ici sa bordure transparente, qui l'amincit sans réduire la
   zone d'attrapage : la charte reprend ce détail. */

/* Le compteur de notifications, dans le menu. Il ne compte QUE les
   notifications : gonfle par des nouvelles non lues, il cesserait
   d'etre regarde. */
/* =========================================================================
   LA PASTILLE DU BANDEAU.

   Elle se pose SUR l'avatar, pas a cote : le bandeau est etroit, et une marque
   posee dessus se lit comme appartenant a ce compte plutot que comme un
   element de plus dans la rangee.

   TAILLE. Un chiffre tient dans un cercle de 18 px ; c'est un cercle, pas une
   pastille etiree, tant qu'il n'y a qu'un chiffre. Deux chiffres l'allongent
   juste ce qu'il faut, et au-dela on ecrit « 9+ » : ici on repond a « ai-je
   quelque chose a lire », le compte exact est dans le menu. Un nombre a trois
   chiffres deformerait la marque pour une precision dont on ne fait rien a cet
   endroit.

   COULEUR. Ni le bleu, ni le vert, ni le jaune, ni le violet, ni le rouge :
   ces cinq-la disent une ACTION dans notre grammaire (envoyer, resultats,
   programmes, consultations, irreversible), et une pastille n'est pas une
   action. L'ambre du produit ne sert qu'a attirer l'oeil, ce qui est
   exactement son role ici, et il tranche sur un bandeau ardoise.

   LE LISERE prend la teinte du bandeau et non du blanc : il detache la marque
   de l'avatar sans dessiner un anneau clair autour.
   ========================================================================= */
.lk-top-bar__compte-marque {
    position: relative;
    display: inline-flex;
    flex: none;
}

.lk-top-bar__pastille {
    position: absolute;
    top: -2px;
    right: -3px;

    display: inline-flex;
    align-items: center;
    justify-content: center;
    box-sizing: border-box;
    min-width: 16px;
    height: 16px;
    padding: 0 4px;

    border-radius: 999px;

    /*
     * PAS UN APLAT, PAS DE LISERE FRANC.
     *
     * Un disque de couleur pleine cercle d'un trait de 2 px se lisait comme un
     * autocollant pose sur la photo. Trois retouches le posent dans l'image :
     * un degrade tres court, qui donne une matiere plutot qu'une surface ; un
     * anneau de la couleur du bandeau, en ombre portee et non en bordure, donc
     * sans arete ; et un halo ambre tres dilue, qui fait tenir la marque sur
     * une photo claire comme sur une sombre.
     */
    background: linear-gradient(160deg, var(--amber-soft), var(--amber-mid));

    box-shadow:
        0 0 0 2px rgba(var(--slate-rgb), 0.96),
        0 1px 3px rgba(var(--black-rgb), 0.45),
        0 0 10px rgba(var(--amber-bright-rgb), 0.35);

    /* Sur de l'ambre, une encre sombre se lit ; du blanc s'y noierait. Pas du
       noir pur : il durcit un chiffre de 10 px. */
    color: rgba(var(--black-rgb), 0.82);
    font-size: 10px;
    font-weight: 700;
    line-height: 1;
    letter-spacing: 0;
    font-variant-numeric: tabular-nums;
    pointer-events: none;

    transition: box-shadow .18s ease, transform .18s ease;
}

/* Au survol du compte, la marque avance d'un cheveu et son halo s'ouvre : le
   bouton entier repond au geste, pas seulement son fond. */
.lk-top-bar__compte-bouton:hover .lk-top-bar__pastille {
    transform: translateY(-1px);

    box-shadow:
        0 0 0 2px rgba(var(--slate-light-rgb), 0.9),
        0 2px 5px rgba(var(--black-rgb), 0.45),
        0 0 14px rgba(var(--amber-bright-rgb), 0.5);
}

@media (prefers-reduced-motion: reduce) {
    .lk-top-bar__pastille {
        transition: none;
    }

    .lk-top-bar__compte-bouton:hover .lk-top-bar__pastille {
        transform: none;
    }
}

/* LA MEME MARQUE, DANS LE MENU.
   Elle etait violette et cerclee de violet, quand celle du bandeau est ambre :
   deux couleurs pour un seul compte, et on cherchait le rapport entre les
   deux. Meme matiere, meme encre ; seul l'anneau tombe, inutile sur un fond
   plat, et la taille suit celle d'une ligne de menu. */
.lk-menu__pastille {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    box-sizing: border-box;
    min-width: 19px;
    height: 19px;
    margin-left: 8px;
    padding: 0 5px;
    border-radius: 999px;

    background: linear-gradient(160deg, var(--amber-soft), var(--amber-mid));
    box-shadow: 0 1px 3px rgba(var(--black-rgb), 0.35);

    color: rgba(var(--black-rgb), 0.82);
    font-size: 11px;
    font-weight: 700;
    line-height: 1;
    font-variant-numeric: tabular-nums;
}

/* ======================================================================
   LA MARQUE DU BANDEAU, A LA MEME TAILLE QUE SUR LA PAGE D'ACCUEIL
   ======================================================================
   Elle etait la plus petite des deux : 30 px de logo contre 38, 15 px de nom
   contre 16. Une marque qui retrecit quand on entre dans l'outil se lit comme
   une marque qu'on quitte.

   Le nom passe en capitales, comme sur la page d'accueil : c'est la meme
   marque et elle s'ecrit pareil.
   ====================================================================== */
.lk-top-bar__brand {
    gap: 12px;
}

.lk-top-bar__brand img,
.lk-top-bar__brand svg {
    width: 40px;
    height: 40px;
}

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

@media (max-width: 900px) {
    .lk-top-bar__brand img,
    .lk-top-bar__brand svg {
        width: 32px;
        height: 32px;
    }

    .lk-top-bar__brand-name {
        font-size: 16px;
    }
}

/* Le nom disparaissait sous 420 px. Il tient maintenant plus longtemps : c'est
   le nom de la marque, il part en dernier. */
@media (max-width: 420px) {
    .lk-top-bar__brand-name {
        display: inline;
        font-size: 15px;
        letter-spacing: 0.04em;
    }
}

@media (max-width: 340px) {
    .lk-top-bar__brand-name {
        display: none;
    }
}

/* ======================================================================
   L'ONGLET DU COMPTE
   ======================================================================
   Il avait la forme, la taille et la teinte d'un onglet de navigation : rien
   ne disait que celui-la, c'est vous. Et son chevron etait le caractere « ⌄ »,
   qui se pose bas dans sa ligne et manque a beaucoup de polices, d'ou ce petit
   signe de travers a droite du prenom.
   ====================================================================== */
.lk-top-bar__compte-bouton {
    gap: 9px;
    padding: 4px 12px 4px 4px;
    height: 42px;
    border-radius: 999px;
    background: rgba(var(--slate-light-rgb), 0.10);
    border-color: rgba(var(--slate-light-rgb), 0.28);
    font-weight: 600;
    transition:
        background 0.18s ease,
        border-color 0.18s ease;
}

.lk-top-bar__compte-bouton:hover,
.lk-top-bar__compte-bouton[aria-expanded="true"] {
    background: rgba(var(--slate-light-rgb), 0.20);
    border-color: rgba(var(--slate-light-rgb), 0.44);
}

/* La pastille d'initiale : ronde, pleine, aux couleurs de la marque. */
.lk-top-bar__initiale,
.lk-menu__tete-initiale {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 32px;
    height: 32px;
    border-radius: 50%;
    background: linear-gradient(135deg, rgba(var(--blue-rgb), 0.92), rgba(var(--consultation-deep-rgb), 0.92));
    color: var(--ink-50);
    font-size: 14px;
    font-weight: 800;
    line-height: 1;
    letter-spacing: 0;
}

.lk-top-bar__prenom {
    font-size: 14px;
    line-height: 1;
}

/* Le chevron est dessine par deux bords : il tombe donc toujours juste, quelle
   que soit la police, et sa rotation part de son vrai centre. */
.lk-top-bar__chevron {
    width: 7px;
    height: 7px;
    border-right: 1.6px solid currentColor;
    border-bottom: 1.6px solid currentColor;
    /* L'ordre compte : la rotation d'abord, puis le decalage dans le repere du
       bouton. Et c'est un `translate` et non une marge, parce que le conteneur
       est en `align-items: center` : toute marge etait aussitot recentree, et
       chaque correction n'agissait qu'a moitie. */
    transform: translateY(-2.5px) rotate(45deg);
    transform-origin: center;
    opacity: .62;
    transition: transform .18s ease, opacity .18s ease;
}

.lk-top-bar__compte-bouton[aria-expanded="true"] .lk-top-bar__chevron {
    transform: translateY(2.5px) rotate(-135deg);
    opacity: .9;
}

/* --- La tete du menu : de qui est ce compte --- */
.lk-menu__tete {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 12px 12px;
    margin-bottom: 6px;
    border-bottom: 1px solid rgba(var(--slate-light-rgb), 0.16);
}

.lk-menu__tete-texte {
    display: grid;
    gap: 2px;
    min-width: 0;
}

.lk-menu__tete-nom {
    font-weight: 700;
    font-size: 14px;
    line-height: 1.2;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* L'adresse est une VERIFICATION, pas une information a lire : elle se pose en
   retrait, et se coupe plutot que d'elargir le menu. */
.lk-menu__tete-mail {
    font-size: 12px;
    line-height: 1.2;
    opacity: .62;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Sous 900 px, le prenom s'efface et il ne reste que la pastille : elle suffit
   a designer le compte, la ou le nom ne tiendrait plus. */
@media (max-width: 900px) {
    .lk-top-bar__compte-bouton {
        padding: 4px;
        gap: 6px;
    }
}

@media (prefers-reduced-motion: reduce) {
    .lk-top-bar__compte-bouton,
    .lk-top-bar__chevron {
        transition: none;
    }
}

/* ======================================================================
   LE VISAGE, OU L'INITIALE
   ======================================================================
   Les deux occupent exactement la même place et la même forme : une photo
   qui apparaît ne doit pas décaler ce qui l'entoure, sinon la page saute au
   moment où quelqu'un ajoute la sienne.
   ====================================================================== */
.avatar {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    border-radius: 50%;
    line-height: 1;
    overflow: hidden;
}

.avatar--photo {
    object-fit: cover;
    background: rgba(var(--slate-light-rgb), 0.14);
}

/* --- L'initiale, quand il n'y a pas de photo ------------------------------
   -------------------------------------------------------------------------
   Elle portait `linear-gradient(135deg, rgba(bleu,0.92), rgba(violet,0.92))`,
   soit deux teintes qui n'existent nulle part ailleurs dans la charte, sur le
   seul objet que TOUS les ecrans affichent : le menu du haut, le profil, les
   lignes clients, la messagerie.

   CE QUE LE GABARIT DEMANDE, et il a raison : « l'initiale n'est pas un
   pis-aller, c'est le repli normal, et elle doit avoir l'air voulue ». Un
   simple gris l'aurait fait passer pour une image qui n'a pas charge.

   POURQUOI PAS UN AVATAR CYAN. Le cyan dit « le produit ». Le poser en aplat
   sur le visage de chaque personne ferait de chaque personne un element du
   produit, et surtout il entrerait en concurrence avec les teintes de ligne
   la ou les avatars s'alignent dans une liste.

   L ARDOISE PORTE, LE CYAN NOMME. La surface reste neutre, comme une photo
   sombre le serait ; c'est la LETTRE qui prend l'accent. L'objet a l'air
   dessine, il ne prend la place de rien, et il reste lisible a 24 px comme a
   96 px puisque seule la lettre est coloree.
   ------------------------------------------------------------------------- */
.avatar--initiale {
    background: rgba(var(--slate-mid-rgb), 0.92);
    color: var(--texte-accent, rgba(var(--portal-accent-rgb), 0.78));
    font-weight: 700;
    letter-spacing: 0;
    border: var(--filet-epaisseur) solid var(--filet-petit);
    box-sizing: border-box;
}

/* --- Le dépôt, sur le profil --- */
.profile-photo__titre {
    margin: 0 0 4px;
    font-size: 17px;
}

.profile-photo__texte {
    margin: 0 0 16px;
    opacity: .78;
}

.profile-photo__ligne {
    display: flex;
    align-items: flex-start;
    gap: 18px;
    flex-wrap: wrap;
}

/* Les deux formulaires se suivent, donc les deux liens s'alignent. */
.profile-photo__actions {
    display: grid;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    min-width: 0;
}

/* --- 7. La photo : le choix de fichier redevient un champ (F2) ------------
   -------------------------------------------------------------------------
   RETENU SUR `photo-profil.html`. Trois defauts corriges d'un coup.

   LE CHOIX DE FICHIER ETAIT DEGUISE EN BOUTON. `<input type=file>` masque, et
   un `<label class="btn">` par-dessus. C'est un CHAMP : il prend donc le creux
   retenu, avec un filet en tirets qui dit « on depose ici ». Il accepte alors
   aussi le glisser-deposer, ce qu'un bouton ne permet pas.

   LES HAUTEURS ETAIENT INEGALES. Trois boutons cote a cote dont un seul passe
   a deux lignes : 19 px d'ecart. En descendant les deux actions au rang de
   liens, la question disparait au lieu d'etre rattrapee.

   LES BOUTONS ETAIENT ACTIFS SANS RAISON. Verifie dans le gabarit :
   « Enregistrer la photo » porte DEJA `disabled` tant qu'aucun fichier n'est
   choisi. Le produit le savait, rien ne le montrait. Il suffisait de le
   dessiner.

   CE QUE L APPLICATION DEVRA FAIRE AU GABARIT : le libelle du depot dit
   encore « Choisir une photo ». « Glissez une photo, ou choisissez un
   fichier » dit ce que la zone accepte vraiment, et la mention du format a sa
   place dedans plutot qu'en note dessous. Deux chaines a traduire.
   ------------------------------------------------------------------------- */
.profile-photo__form {
    display: grid;
    align-items: center;
    gap: 12px;
    margin: 0;
}

/* Le champ natif est illisible et impossible à styler : on le masque sans le
   retirer du clavier, et c'est son intitulé qui devient le bouton. */
.profile-photo__fichier {
    position: absolute;
    width: 1px;
    height: 1px;
    opacity: 0;
    overflow: hidden;
}

.profile-photo__fichier:focus-visible + label {
    outline: 2px solid rgba(var(--consultation-rgb), 0.8);
    outline-offset: 2px;
}

/* La note d'origine disait la meme chose, plus bas et hors de la zone. */
.profile-photo__aide {
    flex: 1 1 100%;
    margin: 0;
    font-size: 12.5px;
    opacity: .62;
    display: none;
}

/*
 * L'APERCU DE LA PHOTO CHOISIE.
 *
 * L'enveloppe ne fait que tenir la place : le script y remplace l'avatar par
 * l'image choisie, sans avoir a savoir si c'etait une photo ou une initiale.
 * Sa taille est fixee pour que rien ne saute au moment de l'echange.
 */
.profile-photo__apercu-zone {
    flex: 0 0 auto;
    width: 88px;
    height: 88px;
}

/*
 * L'APERCU MONTRE LE MEME CADRAGE QUE CE QUI SERA GARDE.
 *
 * Le serveur recadre en carre et centre sur le TIERS SUPERIEUR, la ou se
 * trouve un visage sur une photo en pied. Un apercu centre au milieu aurait
 * promis un cadrage que le resultat n'aurait pas tenu.
 */
.profile-photo__apercu {
    object-fit: cover;
    object-position: center 33%;
}

/* Le nom du fichier choisi, sur sa propre ligne. */
.profile-photo__choisi {
    flex: 1 1 100%;
    margin: 0;
    font-size: 12.5px;
    opacity: .82;
    word-break: break-all;
}

/* ======================================================================
   LA PALETTE : DISPOSITION D, ET ELLE SEULE
   ======================================================================
   Ecrite en dernier, donc gagnante a specificite egale sur les quatre
   reglages qui la precedaient.

   CE QU'ON REPARE. La barre tenait quatre geometries selon la largeur :
   boutons de 62 px et actions de 120 sur grand ecran, colonne d'actions
   bornee a 92 px sous 900 px, la meme sous pointeur grossier, 62 px sous
   409. La palette changeait donc de proportions a chaque palier, et le test
   d'entrainement comme le test reel en heritaient sans qu'on puisse dire
   laquelle etait la bonne.

   CE QU'ON POSE. Une seule grille de SEPT colonnes : six parts egales pour
   les couleurs, 1,25 part pour les actions. Deux rangees, remplies colonne
   par colonne, donc chaque couleur garde son « + » au-dessus de son « - ».
   Quitter occupe le haut de la septieme, Valider le bas : le pouce va en
   bas, et une erreur de visee tombe alors sur la sortie plutot que sur
   l'action qu'on repete.

   Le rapport tient a toutes les largeurs. Les paliers ne reglent plus que
   ce qui doit vraiment changer : l'ecart, le rembourrage, le corps du texte.

   COMMENT. Les deux conteneurs intermediaires passent en `display: contents`
   et s'effacent : leurs quatorze boutons deviennent les cellules directes de
   la barre. Le gabarit n'a pas bouge, ni pour la demonstration ni pour le
   test reel, ce qui garantit qu'ils rendent la meme chose.
   ====================================================================== */

body.diagnostic-player-page .diagnostic-controls-bar {
    display: grid;
    grid-template-columns: repeat(6, minmax(0, 1fr)) 1.25fr;
    grid-template-rows: repeat(2, auto);
    grid-auto-flow: column;
    gap: 6px;
    align-items: stretch;

    /*
     * CENTREE, A TOUTES LES LARGEURS.
     *
     * Sous 900 px et au pointeur grossier, la barre etait ancree par
     * `left: 10px; right: 10px` : deux ancrages plus une largeur, c'est une
     * contrainte de trop, et le navigateur laisse alors tomber `right`. La
     * barre restait donc collee a gauche des qu'elle cessait de remplir
     * l'ecran. On revient a un seul ancrage, le milieu.
     */
    left: 50%;
    right: auto;
    transform: translateX(-50%);

    /*
     * Une seule largeur, centree, du telephone au grand ecran.
     *
     * 700 et non 960 : un libelle fait deux caracteres, donc a 960 le bouton
     * mesurait 125 px pour afficher « R+ ». C'etait de la place vide, et le
     * pouce parcourait toute la largeur de l'ecran entre le rouge et le
     * magenta. A 700, le bouton fait 89 px et la colonne d'actions 111, soit
     * encore le double de la cible tactile minimale.
     */
    width: min(700px, calc(100vw - 20px));
    max-width: calc(100vw - 20px);
    padding: 8px;
    box-sizing: border-box;

    border-radius: 14px;
    background: rgba(var(--black-rgb), 0.42);
    border: 1px solid rgba(var(--white-rgb), 0.10);

    /*
     * PAS DE COUPE. Le depassement n'est plus possible, puisque plus aucune
     * cellule n'a de largeur plancher : la coupe ne protegeait plus rien et
     * mutilait la seule chose qui doit deborder. L'appui pose un contour
     * blanc de 3 px et un halo de 20 : c'est ce halo qui rend l'appui visible
     * sous le doigt, et pendant la demonstration automatique c'est lui qui
     * montre quel bouton agit. Coupe au bord de la barre, il disparaissait
     * exactement sur les boutons du pourtour.
     */
    overflow: visible;
}

/* ======================================================================
   L'ORDRE DES PLANS DANS LA BARRE
   ======================================================================
   Tout ce qui met en evidence deborde du bouton : l'anneau violet de la
   demonstration vit a `inset: -7px` quand l'ecart entre deux boutons n'est
   que de 6, et le halo de l'appui porte jusqu'a 56 px. Ces debordements se
   marchaient dessus, chacun avec un plan pris au hasard de l'ecriture.

   On les ordonne une fois pour toutes, du plus bas au plus haut :

     4  la teinte douce des commandes parentes
     5  l'appui reel, au doigt ou a la souris
     6  l'appui joue par la demonstration
    12  L'ANNEAU DE LA CIBLE, au-dessus de tout

   L'anneau vient en dernier parce qu'il ne decrit pas un etat du bouton :
   c'est un calque pose par-dessus l'interface pour designer quelque chose a
   l'ecran. Il etait a 2, sous l'appui joue qui etait a 3 : la demonstration
   effacait donc son propre pointeur au moment precis ou elle appuyait.

   `position: relative` accompagne chaque plan : sans elle, `z-index` reste
   sans effet des que le bouton n'est plus un element de grille.
   ====================================================================== */
/* --- D6. Le pointeur de la demonstration, retabli et neutre --------------
   -------------------------------------------------------------------------
   IL NE RENDAIT PAS CE QU'IL DECLARAIT, et la mesure l'a dit avant l'oeil.
   `lk.css` donne aux trois etats de la demonstration un anneau blanc de 3 px
   et un halo, puis donne 6 800 lignes plus bas une facture commune a toutes
   les cellules de la barre, `box-shadow: inset 0 1px 0 rgba(255,255,255,0.18)`.
   Quatre classes chacune, meme poids : la seconde, plus tardive, l'emporte et
   efface l'anneau comme le halo. Ombre calculee des trois etats avant
   correction : `inset 0 1px 0 rgba(255,255,255,0.18)`, c'est-a-dire le lisere
   d'une cellule ordinaire. Il ne restait que l'anneau pulsant du `::after`,
   qui vient d'un autre element et survit.

   Les regles ci-dessous ajoutent une classe au selecteur, ce qui suffit a
   reprendre la main sans toucher a `lk.css`.

   BLANC, ET PAS LA TEINTE DE L INTERFACE. C'est la seule exception a la
   doctrine dans tout le produit, et elle tient a ce que fait cette page :
   elle demande de JUGER DES COULEURS. Un halo cyan, ou violet, pose a cote
   d'un canal cyan ou magenta, deteint sur ce qu'on demande de percevoir. Le
   blanc n'ajoute aucune teinte, seulement de la lumiere.
   ------------------------------------------------------------------------- */
body.diagnostic-player-page .diagnostic-controls-bar .diagnostic-btn.is-demo-soft {
    position: relative;
    z-index: 4;
    box-shadow: 0 0 0 1px rgba(var(--white-rgb), 0.68),
        0 0 20px rgba(var(--white-rgb), 0.16);
}

body.diagnostic-player-page .diagnostic-controls-bar .diagnostic-btn.is-appuye {
    position: relative;
    z-index: 5;
}

body.diagnostic-player-page .diagnostic-controls-bar .diagnostic-btn.is-demo-pressed {
    position: relative;
    z-index: 6;
    box-shadow: 0 0 0 3px rgba(var(--white-rgb), 0.98),
        0 0 28px rgba(var(--white-rgb), 0.42),
        inset 0 0 28px rgba(var(--white-rgb), 0.24);
}

/* ======================================================================
   LA CIBLE GARDE SON RELIEF, ET CE QUE CA COUTE
   ======================================================================
   La cible se souleve d'un pixel, grossit de 4 % et s'eclaircit. C'est cette
   animation qui la fait vivre, et elle est gardee telle quelle.

   Ce qu'il faut savoir avant d'y toucher : `transform` et `filter` enferment
   chacun leur element dans un plan clos, dont son anneau ne peut plus sortir.
   Tant qu'elles sont la, l'anneau passe par-dessus les colonnes voisines,
   mais quand les DEUX boutons d'une meme colonne sont cibles, le second
   recouvre une part de l'anneau du premier. Aucun ordre de plans n'y change
   rien : si l'un passe devant, l'anneau de l'autre passe derriere.

   Essaye et rejete : retirer les deux proprietes libere les anneaux, qui
   passent alors tous au-dessus de tous les boutons. On y perd l'animation,
   et l'animation vaut mieux que ce recouvrement partiel.

   Le plan 12 reste : il place la cible au-dessus de l'appui joue, sans quoi
   la demonstration effacait son propre pointeur au moment ou elle appuyait.
   ====================================================================== */
body.diagnostic-player-page .diagnostic-controls-bar .diagnostic-btn.is-demo-target {
    position: relative;
    z-index: 12;
    box-shadow: 0 0 0 3px rgba(var(--white-rgb), 0.96),
        0 0 22px rgba(var(--white-rgb), 0.34);
}

/* L'anneau passe devant le fond et le libelle de son propre bouton. */
/* L'anneau pulsant quitte le violet lui aussi : il fait partie du pointeur. */
body.diagnostic-player-page .diagnostic-controls-bar .diagnostic-btn.is-demo-target::after {
    z-index: 1;
    border-color: rgba(var(--white-rgb), 0.90);
}

/* Les deux conteneurs s'effacent : leurs boutons deviennent les cellules
   de la grille ci-dessus. */
body.diagnostic-player-page .diagnostic-controls-grid,
body.diagnostic-player-page .diagnostic-controls-grid--desktop,
body.diagnostic-player-page .diagnostic-actions,
body.diagnostic-player-page .diagnostic-actions--desktop {
    display: contents;
}

/* Les quatorze cellules ont la meme facture : seule la couleur les separe. */
body.diagnostic-player-page .diagnostic-controls-bar .diagnostic-btn,
body.diagnostic-player-page .diagnostic-controls-bar .btn {
    width: auto;
    min-width: 0;
    min-height: 34px;
    margin: 0;
    padding: 6px 4px;
    box-sizing: border-box;

    display: inline-flex;
    align-items: center;
    justify-content: center;

    border-radius: 8px;
    border: 1px solid rgba(var(--white-rgb), 0.18);
    box-shadow: inset 0 1px 0 rgba(var(--white-rgb), 0.18);

    font-size: 12px;
    font-weight: 700;
    line-height: 1.15;
    color: var(--white);
    text-align: center;
    text-decoration: none;
    white-space: nowrap;
}

/*
 * SAUF LES DEUX ACTIONS, QUI PORTENT DES MOTS ET NON DES SIGNES.
 *
 * `nowrap` va aux douze couleurs : « R+ » ne se coupe nulle part, et une
 * coupure y serait illisible. Les deux cellules de droite portent un libelle,
 * et sa longueur ne depend plus de nous : « Quitter » sur le parcours client,
 * « Passer au test » chez le visiteur, « Перейти к тесту » en russe. La
 * colonne, elle, est taillee pour le plus court.
 *
 * Mesure a 390 px de large, « Passer au test » : 66 px de texte dans 57 px de
 * boite avant, sortis de la barre et imprimes sur la grille des couleurs ; 57
 * dans 57 apres, sur deux lignes, la hauteur de cellule ne bougeant pas.
 */
body.diagnostic-player-page .diagnostic-controls-bar .diagnostic-actions .btn {
    white-space: normal;
}

/* Au doigt, la cible passe a 44 px. C'est la seule chose qu'un pointeur
   grossier change : la hauteur, jamais les proportions. */
@media (pointer: coarse) {
    body.diagnostic-player-page .diagnostic-controls-bar .diagnostic-btn,
    body.diagnostic-player-page .diagnostic-controls-bar .btn {
        min-height: 42px;
        font-size: 14px;
    }
}

/*
 * QUITTER SE DETACHE, VALIDER SE LIT, ET NI L'UN NI L'AUTRE NE BOUGE.
 *
 * Trois mesures, prises sur le fond noir du lecteur :
 *
 *   - Quitter etait a #3a4e58, soit 2,41 de rapport avec la page. En dessous
 *     de 3, une commande ne se distingue plus de son fond : on ne voyait pas
 *     qu'il y avait un bouton la. Il passe a 3,51.
 *
 *   - Valider portait du blanc sur #16a34a, soit 3,3. Il en faut 4,5 pour du
 *     texte de cette taille : le mot etait dur a lire sur le vert. Le fond
 *     passe au vert plus sombre qui servait deja au survol, ce qui monte le
 *     texte a 5,02.
 *
 * LES TROIS ETATS DESCENDENT EN TEINTE, jamais l'inverse. Aucun vert plus
 * clair que #15803d ne garde le blanc au-dessus de 4,5 : eclaircir au survol
 * aurait rendu le mot moins lisible au moment precis ou l'on vise le bouton.
 * Base 5,02, survol 6,61, appui 9,07 : le texte gagne en lisibilite a chaque
 * cran, et l'ecart entre deux crans reste visible.
 *
 *   - Valider desactive gardait le MEME vert a 55 % d'opacite. Il avait donc
 *     l'air actionnable en permanence, et rien ne signalait le moment ou il
 *     le devenait vraiment. Desactive, il n'est plus vert du tout.
 *
 * AUCUNE DES DEUX COMMANDES NE SE DEPLACE. Ces boutons se cliquent des
 * dizaines de fois d'affilee, toujours au meme endroit ; un enfoncement qui
 * decale la cible ferait rater le clic suivant. Ce qui change est la couleur,
 * jamais la geometrie.
 */
body.diagnostic-player-page .diagnostic-controls-bar .btn-secondary,
body.diagnostic-player-page .diagnostic-controls-bar .btn-therapy {
    transition: background .14s ease, box-shadow .14s ease, color .14s ease;
}

body.diagnostic-player-page .diagnostic-controls-bar .btn-secondary {
    background: #4d6775;
}

body.diagnostic-player-page .diagnostic-controls-bar .btn-secondary:hover {
    background: #405663;
}

body.diagnostic-player-page .diagnostic-controls-bar .btn-therapy {
    background: #15803d;
}

body.diagnostic-player-page .diagnostic-controls-bar .btn-therapy:hover:not(:disabled) {
    background: #116b33;
}

/*
 * L'APPUI SE VOIT DEUX FOIS : la teinte descend d'un cran de plus, ET un
 * anneau clair entoure le bouton.
 *
 * L'anneau n'est pas decoratif. Ces boutons sont poses sur l'image du test,
 * dont la clarte change a chaque manche : un changement de teinte seul peut
 * passer inapercu sur un fond clair. L'anneau, lui, se lit sur n'importe quel
 * fond. Il est INTERIEUR (`inset`), donc il n'agrandit pas le bouton et ne
 * decale pas son voisin.
 */
body.diagnostic-player-page .diagnostic-controls-bar .btn-secondary:active {
    background: #34464f;
    box-shadow: inset 0 0 0 2px rgba(255, 255, 255, 0.55);
}

body.diagnostic-player-page .diagnostic-controls-bar .btn-therapy:active:not(:disabled) {
    background: #0d5427;
    box-shadow: inset 0 0 0 2px rgba(255, 255, 255, 0.62);
}

body.diagnostic-player-page .diagnostic-controls-bar .btn-therapy:disabled {
    /* Ni vert ni translucide : eteint. L'opacite gardait la couleur de
       l'action et la rendait seulement plus pale. */
    background: #2b3640;
    color: rgba(255, 255, 255, 0.48);
    opacity: 1;
    cursor: default;
}

/* Sous 410 px, on reprend la place sur les ecarts et le texte, jamais sur
   le rapport entre les colonnes. */
@media (max-width: 409px) {
    body.diagnostic-player-page .diagnostic-controls-bar {
        gap: 4px;
        padding: 6px;
        border-radius: 12px;
    }

    body.diagnostic-player-page .diagnostic-controls-bar .diagnostic-btn,
    body.diagnostic-player-page .diagnostic-controls-bar .btn {
        font-size: 11px;
        padding: 6px 2px;
    }
}

/* ======================================================================
   LA BARRE DU HAUT S'ALIGNE SUR LA COLONNE DE CONTENU
   ======================================================================
   Elle avait 20 px de rembourrage et toute la largeur de la fenetre, pendant
   que le contenu vit dans une colonne de 1400 px centree. La marque debordait
   donc de 75 px a gauche et le menu de 75 px a droite : rien ne tombait sur
   les memes verticales, et l'oeil le voit meme sans savoir pourquoi.

   Le fond reste pleine largeur, ce qui est voulu : c'est le rembourrage qui
   suit la colonne. `max()` garde 20 px de garde sur les petits ecrans, la ou
   la colonne touche deja les bords.
   ====================================================================== */
.lk-top-bar {
    padding-left: max(20px, calc((100% - 1400px) / 2 + 32px));
    padding-right: max(20px, calc((100% - 1400px) / 2 + 32px));
}

/* La ligne d'ajustement d'un solde : le nombre, le motif, le bouton. Elle se
   replie sous 720 px plutot que de comprimer les trois champs. */
.processing-soldes__form {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    align-items: center;
}

.processing-soldes__form input[type="number"] { width: 92px; flex: 0 0 auto; }

/* LE MOTIF SE RETRECIT, IL N IMPOSE PLUS SA LARGEUR A LA TABLE.
   Sa base de 180 px etait un MINIMUM pour la cellule, et une cellule qui exige
   180 px pousse toute la table : elle mesurait 860 px dans un conteneur de 596.
   Le conteneur defile, donc rien n'etait perdu, mais le bouton « Appliquer »
   tombait a 886 px alors que la carte s'arrete a 650 : il fallait faire defiler
   pour atteindre l'action principale de la ligne, et a l'ecran elle paraissait
   coupee.
   Base a zero : le champ prend ce qui reste apres le nombre et le bouton, et ne
   reclame plus rien. La table tient alors dans sa carte. */
.processing-soldes__form input[type="text"] { flex: 1 1 0; min-width: 0; }

/* Une ligne de commande s'aligne par le bas comme par le haut.
   Deux heritages la disloquaient : les champs prennent 43 px et le bouton 35,
   et surtout les champs portent une marge basse de 16 px que le bouton n'a
   pas. Avec `align-items: center`, cette marge decale le bouton de 8 px vers
   le bas, un decalage qui se voit et qu'aucune des deux regles ne laisse
   deviner. On remet donc la meme hauteur et aucune marge, ici seulement :
   ailleurs, la marge sous les champs est utile.
   Selecteur en balise + classe : une classe seule ne l'emporterait pas sur les
   regles de champ de cette feuille. */
.processing-soldes__form input,
.processing-soldes__form button.btn,
.processing-soldes__form .btn {
    min-height: 42px;
    height: 42px;
    margin-bottom: 0;
}

.processing-soldes__meta {
    margin-top: 2px;
    font-size: 12px;
    color: rgba(var(--ink-300-rgb), 0.72);
}

.processing-soldes__nombre {
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

@media (max-width: 720px) {
    .processing-soldes__form { flex-direction: column; align-items: stretch; }
    .processing-soldes__form input[type="number"] { width: 100%; }
}

/* --- Le parrainage : le compteur et son insigne ---------------------------
   L'insigne d'abord, le chiffre ensuite : c'est l'image qu'on reconnait d'un
   coup d'oeil dans une liste, pas un nombre. */
.parrainage {
    display: flex;
    align-items: center;
    gap: 18px;
}

.parrainage__insigne {
    flex: 0 0 64px;
    width: 64px;
    height: 64px;
    display: grid;
    place-items: center;
}

/* Tant qu'aucun palier n'est atteint, la place reste tenue : sans elle, la
   carte se decale le jour ou l'insigne arrive. */
.parrainage__vide {
    width: 44px;
    height: 44px;
    border-radius: 50%;
    border: 1px dashed rgba(var(--white-rgb), 0.16);
}

.parrainage__nombre {
    font-size: 17px;
    font-weight: 700;
    color: var(--texte-sur-sombre, var(--ink-50));
}

.parrainage__palier {
    margin-top: 2px;
    font-size: 13px;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: rgba(var(--portal-gold-rgb), 0.9);
}

.parrainage__suite {
    margin-top: 6px;
    font-size: 13px;
    color: rgba(var(--ink-300-rgb), 0.72);
}

/* Dans une liste, l'insigne se reduit et se pose contre le nombre. */
.parrainage__pastille {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    white-space: nowrap;
}

/* ======================================================================
   LE FILET DE LA BARRE DU HAUT
   ======================================================================
   Il separe ce qui parle du parcours de la personne de ce qui parle du
   travail sur celui des autres. Deux registres, pas deux rubriques : un
   filet le dit sans mot, et sans le poids d'un titre de groupe.

   Discret par construction : un pixel, moins d'un dixieme d'opacite, et il
   ne prend que la hauteur d'un lien pour ne pas depasser de la rangee.
   ====================================================================== */
.lk-top-bar__filet {
    width: 1px;
    align-self: stretch;
    margin: 4px 8px;
    background: rgba(255, 255, 255, 0.14);
    flex: 0 0 1px;
}

/* ======================================================================
   LA BARRE DU HAUT NE SE SUPERPOSE JAMAIS
   ======================================================================
   Trois colonnes : la marque, la navigation, le compte. Les deux extremes
   refusaient de retrecir, et la navigation, plus large que sa colonne,
   passait par-dessus l'une et l'autre : le prenom se lisait a travers
   « Administration ».

   Ce qui manquait, c'est la permission de retrecir. Une case de grille a
   une largeur minimale automatique, egale a son contenu : sans
   `min-width: 0`, elle ne descend jamais en dessous et deborde a la place.

   L'ORDRE DE DISPARITION est celui de l'utilite. Le second groupe part
   d'abord : ce sont deux lieux de travail, atteignables aussi par le menu
   du compte. Puis la navigation entiere, qui le rejoint dans ce menu.
   ====================================================================== */
.lk-top-bar > * {
    min-width: 0;
}

.lk-top-bar__nav {
    overflow: hidden;
}

/* Le second groupe se retire d'un bloc, filet compris. */
.lk-top-bar__travail {
    display: inline-flex;
    align-items: center;
    gap: 4px;
}

/* LA BORNE PASSE DE 1180 A 1240, ET C EST UNE CORRECTION.

   `.lk-top-bar__nav` porte `overflow: hidden` juste au-dessus : quand la
   navigation ne tient plus, elle ne deborde pas, elle COUPE. Le second groupe
   se retirait a 1180, mais la coupure commence AVANT. Mesure : propre a
   1240 px, trois liens rognes a 1220 (« Mon parcours », « Espace praticien
   Administration », « Administration »), et jusqu'a 13 px perdus a 1200.

   Le defaut ne se voyait pas : un lien tronque de 3 px ressemble a un lien.
   C'est le temoin du reactif qui l'a nomme, en cherchant les ROGNAGES et pas
   seulement les debordements -- il n'y avait aucun debordement, justement,
   puisque `overflow: hidden` les avale tous.

   1240 est la premiere largeur ou tout tient encore. On retire donc le groupe
   juste avant que la coupure ne commence, au lieu de 60 px trop tard. */
@media (max-width: 1240px) {
    .lk-top-bar__travail {
        display: none;
    }
}

/* -------------------------------------------------------------------------
   LE SURVOL, UN SEUL GESTE POUR TOUS LES BOUTONS PLEINS
   -------------------------------------------------------------------------

   Releve sur la fiche : le bouton monte d'un pixel et gagne huit pour cent
   de clarte. C'est tout.

   `filter: brightness()` plutot qu'un second degrade : il vaut sur n'importe
   quel fond, y compris un degrade, sans qu'on ait a maintenir une deuxieme
   paire de couleurs par bouton. Chaque paire en double etait une occasion de
   voir les deux diverger, et c'est ce qui s'est produit : plusieurs boutons
   avaient un survol identique a leur etat normal, donc invisible.

   L'elevation est d'UN pixel. Ces boutons sont dans des piles serrees ; deux
   pixels et le voisin semble bouger aussi.
   ------------------------------------------------------------------------- */

.therapy-action-btn,
.lk-retour {
    transition: transform .14s ease, filter .14s ease, border-color .14s ease;
}

.therapy-action-btn:hover,
.lk-retour:hover {
    transform: translateY(-1px);
    filter: brightness(1.08);
}

/* -------------------------------------------------------------------------
   L'APPUI, LE GESTE INVERSE DU SURVOL
   -------------------------------------------------------------------------

   Le survol approche le bouton : il monte d'un pixel et gagne huit pour cent
   de clarte. L'appui fait exactement le contraire : il repose le bouton au
   sol et lui RETIRE ces huit pour cent. Le doigt enfonce ce que la souris
   avait souleve.

   Une seule valeur a retenir, 8 %, dans un sens puis dans l'autre.

   Ce bloc vient APRES celui du survol, a specificite egale : un bouton
   enfonce est aussi survole, et c'est le dernier ecrit qui l'emporte.

   Assombrir eloigne du texte blanc, donc le contraste MONTE a l'appui, la
   ou le survol le faisait baisser. L'etat le plus bref est le plus lisible :
   c'est le bon sens pour un geste qu'on confirme.
   ------------------------------------------------------------------------- */

.therapy-action-btn:active,
.lk-retour:active {
    transform: translateY(0);
    filter: brightness(0.92);
}

/* Une langue annoncee mais pas encore offerte : on la montre, on ne la propose
   pas. Le bouton reste lisible — il dit que la langue arrive — mais il ne
   repond pas, et son drapeau s'attenue pour que la difference se voie sans
   qu'on ait a lire l'infobulle. */
.locale-switcher__button.is-bientot {
    cursor: default;
    opacity: 0.5;
    position: relative;
}

.locale-switcher__button.is-bientot:hover {
    opacity: 0.62;
}

.locale-switcher__button.is-bientot::after {
    content: "•";
    position: absolute;
    top: -1px;
    right: -1px;
    font-size: 15px;
    line-height: 1;
    color: rgb(var(--teinte-or));
}

/* =========================================================================
   LES NOTIFICATIONS DU PROFIL.

   Ce bloc etait ecrit en styles en ligne. Le defaut visible en venait
   directement : le bouton « Enregistrer » touchait l'encart suivant, a zero
   pixel, parce qu'aucun des deux ne portait de marge et qu'il n'existait aucun
   endroit ou en poser une POUR LES DEUX. Une regle en ligne ne se corrige qu'a
   l'endroit ou elle est ; une classe se corrige une fois.
   ========================================================================= */
.profil-notifs__intro {
    margin: 0 0 16px;
}

.profil-notifs__liste {
    display: grid;
    gap: 12px;
}

.profil-notifs__precision {
    display: block;
    margin-top: 4px;
    /* UNE OPACITE N EST PAS UNE ENCRE : 0,75 du texte courant rendait 3,88 en
       sombre a 13,3 px, et elle se serait multipliee a tout voile pose plus
       tard. La precision porte son propre cran de mention. */
    color: rgba(var(--white-rgb), var(--voile-mention, 0.68));
}

/* L'air sous le bouton appartient au bouton, pas a ce qui le suit : c'est lui
   qui ferme le formulaire, et il doit s'en detacher quel que soit le bloc
   d'apres. */
.profil-notifs__valider {
    margin-top: 16px;
    margin-bottom: 20px;
}

.profil-notifs__encart {
    padding: 14px;
    border-radius: 16px;
    border: 1px solid rgba(var(--white-rgb), 0.12);
    background: rgba(var(--white-rgb), 0.04);
}

.profil-notifs__encart + .profil-notifs__encart {
    margin-top: 12px;
}

.profil-notifs__encart-tete {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: 12px;
}

.profil-notifs__filet {
    border: 0;
    border-top: 1px solid rgba(var(--white-rgb), 0.12);
    margin: 24px 0 20px;
}

.profil-notifs__titre {
    margin: 0 0 10px;
}

/* ===================================================================
   SORTI DU BLOC 640 px OU IL ETAIT IMBRIQUE.

   Une requete media imbriquee dans une autre ne peut pas etre plus
   large que celle qui la contient : ce bloc annoncait 880 px et
   restait plafonne a 640, donc le tableau gardait ses colonnes entre
   les deux et debordait de 143 px a 768.
   =================================================================== */
/*
 * LE TABLEAU PASSE EN CARTES A 880 px, ET NON A 640.
 *
 * Entre les deux, il gardait ses colonnes sans avoir la place : mesure sur
 * `/admin/help` a 768 px, 863 px de tableau dans 758 px de fenetre, donc
 * 143 px de debordement et toute la page qui se balade horizontalement.
 *
 * 880 px, parce que c'est la largeur en dessous de laquelle ces tableaux
 * cessent de tenir. La tablette est justement la largeur d'un praticien qui
 * consulte entre deux rendez-vous.
 */
@media (max-width: 880px) {
    .responsive-table,
    .responsive-table thead,
    .responsive-table tbody,
    .responsive-table tr,
    .responsive-table th,
    .responsive-table td {
        display: block;
        width: 100%;
    }

    .responsive-table thead {
        display: none;
    }

    .responsive-table tbody {
        display: grid;
        gap: 14px;
    }

    .responsive-table tr {
        box-sizing: border-box;
        transition: .18s;
        border-bottom: 1px solid rgba(var(--white-rgb));
        padding-bottom: 10px;
    }

    .responsive-table td {
        border: none;
        padding: 0;
        margin-bottom: 12px;
        white-space: normal;
    }

    .responsive-table td:last-child {
        margin-bottom: 0;
    }

    .responsive-table__label {
        display: block;
    }

    .responsive-table__actions {
        flex-direction: column;
        align-items: stretch;
    }

    .responsive-table__actions .btn {
        width: 100%;
    }
}    

/* ===== Le choix de la langue, au profil =====

   Une langue se reconnait a son drapeau avant de se lire. La liste deroulante
   obligeait a ouvrir, dechiffrer « Русский » ou « Deutsch », puis refermer.
   Des boutons radio deguises en pastilles gardent le clavier et l'envoi sans
   script, et montrent ce qu'on choisit. */

.champ-langue__intitule {
    display: block;
    margin-bottom: 6px;
    font-weight: 600;
}

.champ-langue {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-bottom: 6px;
}

/* --- 3. Le selecteur de langue : quatre choix, une seule forme ------------
   -------------------------------------------------------------------------
   Quatre gelules a drapeau, rayon 999 px, dont l'active en bleu
   `rgba(37,99,235,0.16)`. Deux choses a corriger et une a garder.

   LA FORME. Une rangee de gelules se lit comme un semis d'etiquettes posees a
   cote ; ce sont ici quatre valeurs EXCLUSIVES d'un meme champ, dont une seule
   peut etre vraie. Le rectangle les aligne comme une rangee d'options, ce
   qu'elles sont. Meme raisonnement que pour la pastille, et meme rayon que les
   champs qu'elles accompagnent.

   LA COULEUR. Le bleu de l'active devient le cyan de l'accent. Elle prend en
   plus le filet appuye : sur cette page, c'est le seul endroit ou un choix est
   deja fait et visible, donc il merite le seul cadre fort du bloc.

   CE QU ON GARDE : les drapeaux. Ils font le travail plus vite que le nom de
   la langue, surtout quand le nom est ecrit dans une langue qu'on ne lit pas.
   ------------------------------------------------------------------------- */
.champ-langue__choix {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 8px 14px;
    border: var(--filet-epaisseur) solid var(--filet-large);
    border-radius: 10px;
    cursor: pointer;
    transition: .16s;
    background: none;
    color: rgba(var(--white-rgb), 0.82);
}

/* Le bouton radio lui-meme ne sert plus a rien a l'oeil : c'est la pastille
   qui dit l'etat. Il reste dans le document, sinon le clavier et les lecteurs
   d'ecran perdent le groupe. */
.champ-langue__choix input[type="radio"] {
    position: absolute;
    width: 1px;
    height: 1px;
    opacity: 0;
    pointer-events: none;
}

.champ-langue__choix:hover {
    border-color: var(--filet-petit);
    color: rgba(var(--white-rgb), 0.96);
}

/* L ETAT ACTIF NE PORTE PAS DE CLASSE. Verifie sur la page : le choix est un
   `<label>` sans autre attribut que son `title`, et l'etat vient du bouton
   radio qu'il contient. Mes trois selecteurs de classe ne matchaient donc
   rien, et « Francais » restait bleu. */
.champ-langue__choix:has(input:checked) {
    border-color: var(--filet-action);
    background: rgba(var(--portal-accent-rgb), 0.16);
    font-weight: 600;
    color: rgba(var(--white-rgb), 0.98);
}

/* Le focus clavier doit se voir : sans lui, on tabule dans le groupe sans
   savoir ou l'on est. */
.champ-langue__choix:has(input:focus-visible) {
    outline: 2px solid var(--couleur-accent);
    outline-offset: 2px;
}

.champ-langue__drapeau {
    display: inline-flex;
    width: 20px;
    height: 15px;
    border-radius: 2px;
    overflow: hidden;

    /* Le lisere passe A L'INTERIEUR et en sombre : blanc, il disparaissait
       dans la bande blanche du drapeau francais. Meme raison qu'au selecteur
       du coin de l'ecran. */
    box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.24);
    flex: 0 0 auto;
}

.champ-langue__drapeau .flag-svg {
    display: block;
    width: 100%;
    height: 100%;
}

/* Une langue qui ARRIVE se montre, mais ne se choisit pas. Elle est grisee et
   le dit : un choix simplement absent laisserait croire qu'elle n'existe pas. */
.champ-langue__choix.is-bientot {
    opacity: .5;
    cursor: not-allowed;
}

.champ-langue__bientot {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: .04em;
    opacity: .8;
}

/* ===== La boutique client =====

   Un panier, pas un catalogue : on coche ce qu'on veut lire et le total suit.
   La base est posee a part, en tete, parce qu'elle n'est pas un choix. */

.boutique-client {
    max-width: 860px;
    margin: 0 auto;
}

.boutique-client__entete {
    margin-bottom: 18px;
}

.boutique-client__intro {
    max-width: 62ch;
    line-height: 1.55;
    opacity: .84;
}

.boutique-panier {
    display: flex;
    flex-direction: column;
    gap: 12px;
}

/* La base n'est pas cochable : sans test il n'y a rien a lire. La presenter
   comme une option laisserait croire qu'on peut acheter un resultat seul. */
.boutique-base {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 14px;
    padding: 14px 16px;
    border-radius: 12px;
    border: 1px solid rgba(var(--slate-light-rgb), 0.28);
    background: rgba(var(--slate-light-rgb), 0.07);
}

.boutique-base > div {
    display: flex;
    flex-direction: column;
    gap: 3px;
    min-width: 0;
}

.boutique-base__prix,
.boutique-produit__prix {
    font-variant-numeric: tabular-nums;
    font-weight: 600;
    white-space: nowrap;
}

.boutique-produits {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.boutique-produit {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    padding: 14px 16px;
    border-radius: 12px;
    border: 1px solid rgba(var(--slate-light-rgb), 0.28);
    cursor: pointer;
    transition: .16s;
}

.boutique-produit:hover {
    border-color: rgba(var(--slate-light-rgb), 0.55);
}

/* Coche : la ligne entiere le dit, pas seulement la case. */
.boutique-produit:has(input:checked) {
    border-color: var(--couleur-accent);
    background: rgba(var(--couleur-accent-fort-rgb), 0.12);
}

.boutique-produit:has(input:focus-visible) {
    outline: 2px solid var(--couleur-accent);
    outline-offset: 2px;
}

.boutique-produit input[type="checkbox"] {
    width: 18px;
    height: 18px;
    margin: 2px 0 0;
    accent-color: var(--couleur-accent);
    flex: 0 0 auto;
}

.boutique-produit__corps {
    display: flex;
    flex-direction: column;
    gap: 5px;
    min-width: 0;
    flex: 1 1 auto;
}

.boutique-produit__nom {
    font-weight: 600;
}

.boutique-produit__resume {
    font-size: .9rem;
    opacity: .78;
    line-height: 1.45;
}

/* Ce que l'achat ouvre, nomme comme le lecteur le verra sur ses onglets : un
   code de page ne dit rien a qui achete. */
.boutique-produit__pages {
    display: flex;
    flex-wrap: wrap;
    gap: 5px;
    margin-top: 2px;
}

.boutique-produit__page {
    padding: 2px 9px;
    border-radius: 999px;
    border: 1px solid rgba(var(--slate-light-rgb), 0.3);
    font-size: .76rem;
    white-space: nowrap;
}

/* Un produit inactif n'est visible que par l'Admin, et il ne se coche pas. */
.boutique-produit--inactif {
    opacity: .55;
    cursor: not-allowed;
    border-style: dashed;
}

.boutique-total {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 14px;
    padding: 14px 16px;
    border-top: 1px solid rgba(var(--slate-light-rgb), 0.24);
    font-size: 1.06rem;
}

.boutique-total strong {
    font-variant-numeric: tabular-nums;
    font-size: 1.24rem;
}

@media (max-width: 560px) {
    .boutique-produit {
        flex-wrap: wrap;
    }

    .boutique-produit__prix {
        margin-left: 30px;
    }
}

/* ===== Une page de resultats qu'on n'a pas achetee =====

   CE QU'ON VOIT ICI N'EST PAS UN RESULTAT FLOUTE. Les blocs ont ete retires de
   la collection avant le rendu : rien de la personne n'est dans le document. Le
   style ne cache donc rien, il presente. */

.viewer-a-vendre {
    max-width: 62ch;
    margin: 0 auto 22px;
    padding: 24px 26px;
    border-radius: 16px;
    border: 1px solid rgba(var(--slate-light-rgb), 0.26);
    background: rgba(var(--slate-light-rgb), 0.07);
}

.viewer-a-vendre__chapeau {
    font-size: 12px;
    font-weight: 700;
    letter-spacing: .1em;
    text-transform: uppercase;
    opacity: .68;
    margin-bottom: 6px;
}

.viewer-a-vendre__titre {
    margin: 0 0 10px;
    line-height: 1.24;
}

.viewer-a-vendre__texte {
    line-height: 1.6;
    opacity: .88;
    margin-bottom: 18px;
}

/*
 * LE CODE QUI NE S'AFFICHE PAS.
 *
 * Il n'est annonce nulle part et n'a pas vocation a l'etre. Qui inspecte cette
 * page pour comprendre comment elle est faite le trouvera ; les autres ne
 * perdent rien. `display: none` et non un commentaire HTML : un commentaire se
 * lit dans le source, une regle de style se decouvre en inspectant, ce qui
 * demande exactement la curiosite qu'on veut recompenser.
 *
 * Rien de sensible ici : c'est une remise, pas une donnee de personne.
 */
.viewer-a-vendre__curiosite {
    display: none;
}

/* L'onglet a vendre : il s'OUVRE, contrairement a l'onglet ferme d'un ecran
   emotionnel qui ne mene nulle part. Le pointille dit « il y a quelque chose,
   mais pas encore a vous ». */
.diagnostic-viewer-nav__item.is-a-vendre .diagnostic-viewer-nav__link {
    border-style: dashed;
    opacity: .82;
}

.diagnostic-viewer-nav__item.is-a-vendre.is-vue .diagnostic-viewer-nav__link {
    opacity: 1;
}

/* Deux chemins, cote a cote : un nouveau test, ou un complement sur un test
   deja traite. Le second n'apparait que s'il y a un test : sans lui, il n'y
   aurait rien a ouvrir. */
.boutique-chemin {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin-bottom: 16px;
}

.boutique-chemin__choix {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    padding: 12px 16px;
    border-radius: 12px;
    border: 1px solid rgba(var(--slate-light-rgb), 0.28);
    cursor: pointer;
}

.boutique-chemin__choix:has(input:checked) {
    border-color: var(--couleur-accent);
    background: rgba(var(--couleur-accent-fort-rgb), 0.12);
}

.boutique-chemin__choix:has(input:focus-visible) {
    outline: 2px solid var(--couleur-accent);
    outline-offset: 2px;
}

.boutique-chemin__choix input[type="radio"] {
    margin: 3px 0 0;
    accent-color: var(--couleur-accent);
    flex: 0 0 auto;
}

.boutique-chemin__choix > span {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

/* ===== La declaration de contre-indications =====

   RIEN N'INDIQUE ICI CE QU'UNE CASE ENTRAINE. Pas de pastille, pas de couleur,
   pas de compteur qui s'allume : une case cochee et une case vide se
   ressemblent tant qu'on n'a pas valide. C'est la mesure qui rend la
   declaration honnete, et elle se tient aussi dans le style. */

.declaration {
    max-width: 660px;
    margin: 0 auto;
}

.declaration__intro {
    max-width: 60ch;
    line-height: 1.6;
    opacity: .86;
}

/* L'annonce vient APRES validation. Elle explique, elle ne reproche pas : d'ou
   une teinte d'attention et non de danger. */
.declaration__annonce {
    margin: 16px 0;
    padding: 16px 18px;
    border-radius: 12px;
    border-left: 3px solid rgba(var(--amber-rgb), 0.75);
    background: rgba(var(--amber-rgb), 0.10);
    line-height: 1.6;
}

.declaration__formulaire {
    display: flex;
    flex-direction: column;
    gap: 16px;
    margin-top: 18px;
}

.declaration__liste {
    margin: 0;
    padding: 16px 18px;
    border: 1px solid rgba(var(--slate-light-rgb), 0.28);
    border-radius: 12px;
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.declaration__liste legend {
    padding: 0 6px;
    font-weight: 600;
}

.declaration__case,
.declaration__confirme {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 7px 0;
    cursor: pointer;
    line-height: 1.45;
}

/* Toutes les cases se ressemblent, cochees ou non : aucune ne s'allume, aucune
   ne s'assombrit. Voir l'entete de cette section. */
.declaration__case input[type="checkbox"],
.declaration__confirme input[type="checkbox"] {
    width: 18px;
    height: 18px;
    margin: 2px 0 0;
    accent-color: var(--couleur-accent);
    flex: 0 0 auto;
}

.declaration__passe {
    margin-top: 28px;
    padding-top: 20px;
    border-top: 1px solid rgba(var(--slate-light-rgb), 0.22);
}

.declaration__passe-liste {
    margin: 8px 0 0;
    padding-left: 20px;
    line-height: 1.6;
}

.declaration__bloquants {
    margin-top: 12px;
    opacity: .8;
}

/* ===== La demande d'accompagnement =====

   C'est la sortie de tout ce que la boutique refuse. Le ton visuel n'est jamais
   celui d'un recours : la personne demande quelque chose de normal. */

.accompagnement {
    max-width: 680px;
    margin: 0 auto;
}

.accompagnement__intro {
    max-width: 58ch;
    line-height: 1.6;
    opacity: .86;
}

/* Qui va recevoir. Une demande qui part sans qu'on sache vers qui se lit comme
   une bouteille a la mer. */
.accompagnement__vers {
    margin: 14px 0 18px;
    padding: 10px 14px;
    border-radius: 10px;
    background: rgba(var(--slate-light-rgb), 0.08);
    font-size: .92rem;
}

.accompagnement__formulaire {
    display: flex;
    flex-direction: column;
    gap: 16px;
}

.accompagnement__motifs {
    margin: 0;
    padding: 14px 16px;
    border: 1px solid rgba(var(--slate-light-rgb), 0.28);
    border-radius: 12px;
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.accompagnement__motifs legend {
    padding: 0 6px;
    font-weight: 600;
}

.accompagnement__choix {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 9px 10px;
    border-radius: 10px;
    cursor: pointer;
}

.accompagnement__choix:has(input:checked) {
    background: rgba(var(--couleur-accent-fort-rgb), 0.12);
}

.accompagnement__choix:has(input:focus-visible) {
    outline: 2px solid var(--couleur-accent);
    outline-offset: 2px;
}

.accompagnement__choix input[type="radio"] {
    margin: 3px 0 0;
    accent-color: var(--couleur-accent);
    flex: 0 0 auto;
}

.accompagnement__choix > span {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.accompagnement__historique {
    margin-top: 28px;
    padding-top: 20px;
    border-top: 1px solid rgba(var(--slate-light-rgb), 0.22);
}

.accompagnement__demande {
    padding: 12px 14px;
    margin-bottom: 8px;
    border-radius: 10px;
    background: rgba(var(--slate-light-rgb), 0.06);
}

.accompagnement__demande-tete {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
}

.accompagnement__demande-mot,
.accompagnement__demande-reponse {
    margin: 8px 0 0;
    line-height: 1.55;
}

/* La reponse se detache du mot : ce sont deux voix, et on doit voir laquelle
   parle. */
.accompagnement__demande-reponse {
    padding-left: 12px;
    border-left: 2px solid rgba(var(--share-bright-rgb), 0.6);
}

/* ----- La pile, cote praticien ----- */

.demande__tete {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
}

.demande__qui {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

/* La reserve visee, en clair : repondre sans savoir ce qu'on leve serait
   signer a l'aveugle. */
.demande__reserve {
    margin: 12px 0 0;
    padding: 8px 12px;
    border-radius: 8px;
    border-left: 3px solid rgba(var(--amber-rgb), 0.7);
    background: rgba(var(--amber-rgb), 0.09);
}

.demande__mot {
    margin: 12px 0 0;
    line-height: 1.55;
    opacity: .9;
}

.demande__reponse {
    margin-top: 14px;
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.demande__gestes {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
}

/* ===== L'etat des rouages =====

   Vert par defaut, il ne devient interessant que quand quelque chose cloche.
   La pastille porte l'etat, le texte le dit : qui ne distingue pas les couleurs
   lit la meme chose. */

.etat-systeme {
    position: relative;
    display: flex;
    align-items: center;
}

.etat-systeme__bouton {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 6px 10px;
    /*
     * PAS DE CONTOUR. La pastille et la couleur du texte disent deja l'etat ;
     * un anneau autour en faisait un bouton d'action, alors que c'est un
     * temoin. Le pied de page en portait donc un cadre rouge permanent des
     * qu'un rouage allait mal, et il attirait l'oeil plus que le message.
     */
    border: 0;
    border-radius: 999px;
    background: transparent;
    color: inherit;
    font: inherit;
    font-size: 12px;
    line-height: 1;
    cursor: pointer;
    transition: .16s;
}

/*
 * PAS DE FOND AU SURVOL.
 *
 * J'avais mis un voile clair en compensation du contour retire. Mais ce n'est
 * pas une commande qu'on vise : c'est un temoin, pose dans le pied de page, que
 * le pointeur traverse en allant ailleurs. Un fond qui s'allume au passage
 * annonce une action a chaque fois qu'on ne fait que passer.
 *
 * Il reste cliquable, et le curseur le dit.
 */

.etat-systeme__bouton:focus-visible {
    outline: 2px solid var(--couleur-accent);
    outline-offset: 2px;
}

/* Sous 900 px la barre est deja pleine : le texte s'efface, la pastille
   reste. Elle suffit a dire qu'il y a quelque chose a regarder. */
@media (max-width: 900px) {
    .etat-systeme__texte {
        position: absolute;
        width: 1px;
        height: 1px;
        overflow: hidden;
        clip-path: inset(50%);
    }

    .etat-systeme__bouton {
        padding: 6px 8px;
    }
}

.etat-systeme__pastille {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    flex: 0 0 auto;
    background: rgb(var(--share-bright-rgb));
    box-shadow: 0 0 0 3px rgba(var(--share-bright-rgb), 0.18);
}

.etat-systeme__bouton--attention .etat-systeme__pastille,
.etat-systeme__ligne--attention .etat-systeme__pastille {
    background: rgb(var(--etat-vigilance-rgb));
    box-shadow: 0 0 0 3px rgba(var(--etat-vigilance-rgb), 0.18);
}

.etat-systeme__bouton--panne .etat-systeme__pastille,
.etat-systeme__ligne--panne .etat-systeme__pastille {
    background: rgb(var(--red-light-rgb));
    box-shadow: 0 0 0 3px rgba(var(--red-rgb), 0.22);
}

/*
 * L'ETAT PASSE PAR LA COULEUR DU TEXTE, plus par celle d'un contour.
 *
 * « Attention » ne portait QUE la teinte de son cadre : sans cadre, il
 * redevenait un temoin gris comme les autres. Il prend donc l'ambre lui aussi.
 */
.etat-systeme__bouton--panne {
    color: rgb(var(--red-light-rgb));
}

.etat-systeme__bouton--attention {
    color: var(--amber-soft);
}

/* -------------------------------------------------------------------------
   LES GESTES : LA PHRASE QUE LIT UNE PERSONNE ORDINAIRE.

   Elle n'existe QUE lorsqu'un geste est empeche. Quand tout fonctionne, il n'y
   a rien du tout dans le pied de page : un voyant vert allume en permanence est
   du mobilier, il apprend a l'oeil a le sauter, et il rate donc le jour ou il
   passe au rouge.

   CE N EST PAS UN BOUTON, et rien ne doit le laisser croire : pas de curseur
   qui change, pas de contour, pas de fond au survol. Il n'y a rien a ouvrir de
   ce cote, et l'absence de panneau n'est pas une frustration puisque rien
   n'annonce qu'il en existe un.

   L'etat passe par la couleur du texte et par la pastille, jamais par un cadre.
   Meme regle que le temoin, et pour la meme raison : un cadre rouge permanent
   dans le pied de page attirait l'oeil plus que le message.
   ------------------------------------------------------------------------- */

/* LA PHRASE TIENT RAREMENT SUR UNE LIGNE, ET C EST INEVITABLE.

   « L'envoi des tests au traitement est interrompu. Vos donnees sont
   conservees et partiront des le retablissement. » demande environ 950 px sur
   une ligne. La case ne peut pas les donner : la derniere ligne du pied est
   une grille `1fr auto 1fr` qui garde la mention d'editeur au centre de la
   page, donc la case vaut 603 px a 1440. La phrase fait donc deux lignes, et
   il faut qu'elle les fasse BIEN.

   DEUX CORRECTIONS, MESUREES SUR `base/profil-client.html`.

   `text-align: left`. Le pied centre son contenu (`.lk-app-footer__legal`),
   et la phrase en heritait : la seconde ligne flottait au milieu de la case,
   loin du debut de la premiere. Mesure a 1440 : ligne 1 a x=87, ligne 2 a
   x=299. A 700 px la phrase passe a TROIS lignes, chacune decalee
   differemment. Le panneau d'etat, juste en dessous dans cette feuille,
   reprend deja `text-align: left` pour cette meme raison.

   `align-items: flex-start`. Centre, la pastille se posait au milieu du BLOC
   de deux lignes, donc entre les deux : elle se lisait comme detachee de sa
   phrase, a 49 px du premier mot alors que l'ecart demande est de 7. Calee en
   haut, elle retrouve sa premiere ligne, et le decalage de 4 px la remet au
   milieu de cette ligne-la : (12 x 1,35 - 8) / 2. */
.etat-systeme__geste {
    display: inline-flex;
    align-items: flex-start;
    gap: 7px;
    font-size: 12px;
    line-height: 1.35;
    text-align: left;
}

.etat-systeme__geste .etat-systeme__pastille {
    margin-top: 4px;
}

.etat-systeme__geste--attention {
    color: var(--amber-soft);
}

.etat-systeme__geste--panne {
    /* LE ROUGE CLAIR EST CALIBRE POUR UN SOL SOMBRE : sur le sol clair il rend
       2,44. `--couleur-danger-doux` est le meme fait dit dans les deux themes
       -- rouge pale en sombre, rouge profond en clair -- et le danger garde sa
       teinte, il ne change que de profondeur. */
    color: var(--couleur-danger-doux, rgb(var(--red-light-rgb)));
}

.etat-systeme__geste--attention .etat-systeme__pastille {
    background: rgb(var(--etat-vigilance-rgb));
    box-shadow: 0 0 0 3px rgba(var(--etat-vigilance-rgb), 0.18);
}

.etat-systeme__geste--panne .etat-systeme__pastille {
    background: rgb(var(--red-light-rgb));
    box-shadow: 0 0 0 3px rgba(var(--red-rgb), 0.22);
}

/* Dans le panneau de l'Admin, les memes phrases se posent en liste, au-dessus
   du detail des rouages. Elles y perdent leur alignement en ligne. */
.etat-systeme__gestes {
    list-style: none;
    margin: 0 0 12px;
    padding: 0 0 12px;
    border-bottom: var(--filet-epaisseur) solid rgba(var(--white-rgb), 0.10);
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.etat-systeme__gestes .etat-systeme__geste {
    align-items: flex-start;
}

.etat-systeme__gestes .etat-systeme__pastille {
    margin-top: 5px;
}

/* Le panneau se pose PAR-DESSUS : dans le flux, il pousserait la barre du haut
   et ferait sauter toute la page a chaque ouverture. */
.etat-systeme__panneau {
    position: absolute;
    top: calc(100% + 8px);
    right: 0;
    z-index: 10001;
    width: 290px;
    padding: 14px 16px;
    border-radius: 12px;
    border: 1px solid rgba(var(--white-rgb), 0.14);
    background: rgb(var(--slate-mid-rgb));
    box-shadow: 0 16px 40px rgba(0, 0, 0, 0.42);
    color: var(--white);
}

.etat-systeme__tete {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    margin-bottom: 10px;
}

.etat-systeme__fermer {
    border: 0;
    background: transparent;
    color: inherit;
    font-size: 20px;
    line-height: 1;
    cursor: pointer;
    opacity: .6;
}

.etat-systeme__fermer:hover {
    opacity: 1;
}

.etat-systeme__liste {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.etat-systeme__ligne {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 7px 0;
    font-size: 13px;
    border-bottom: 1px solid rgba(var(--white-rgb), 0.06);
}

.etat-systeme__ligne:last-child {
    border-bottom: 0;
}

.etat-systeme__nom {
    flex: 1 1 auto;
    min-width: 0;
}

.etat-systeme__valeur {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 1px;
    font-size: 12px;
    opacity: .8;
    white-space: nowrap;
}

/* Le chiffre n'apparait que pour l'Admin : il ne change rien pour un
   praticien, et un nombre sans echelle se lit comme une panne. */
.etat-systeme__mesure {
    font-size: 11px;
    opacity: .7;
    font-variant-numeric: tabular-nums;
}


/* ===== L'abonnement, dans la fiche produit =====

   Les deux champs qui n'ont de sens qu'avec un abonnement ne s'affichent que
   si la case est cochee. Le masquage est DEMANDE par le script, jamais pose
   par le HTML : sans `est-pilote`, la suite reste visible et le formulaire
   fonctionne quand meme. Cachee d'office, elle serait invisible et
   inatteignable le jour ou le script ne part pas. */
.bloc-abonnement {
    margin-top: 18px;
    padding: 14px 16px;
    border: 1px solid rgba(var(--white-rgb), 0.10);
    border-radius: 10px;
    background: rgba(var(--white-rgb), 0.02);
}

.bloc-abonnement legend {
    padding: 0 6px;
    font-size: 12px;
    font-weight: 650;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: rgba(var(--white-rgb), var(--voile-mention, 0.55));
}

.bloc-abonnement__suite {
    display: grid;
    gap: 10px;
    margin-top: 12px;
    padding-top: 12px;
    border-top: 1px solid rgba(var(--white-rgb), 0.08);
}

.bloc-abonnement.est-pilote .bloc-abonnement__suite { display: none; }
.bloc-abonnement.est-pilote.est-ouvert .bloc-abonnement__suite { display: grid; }

/* ======================================================================
   LE BOUTON DES PROGRAMMES
   ======================================================================
   Il vit ICI et non dans `admin-dashboard.css` : sept vues montent leur
   propre `<head>` sans heriter du gabarit, dont le lecteur de therapie et
   les ecrans du test. Une couleur qui doit etre la meme partout ne peut
   pas dependre d'une feuille qui n'est pas chargee partout.
   ====================================================================== */
/* ---- Le bouton des programmes : or plein, or creux ---- */
.bouton-programme {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    height: 46px;
    padding: 0 20px;
    box-sizing: border-box;
    /* Bordure et graisse reprises de `.therapy-action-btn` : c'est le meme
       bouton, il doit se reconnaitre d'un ecran a l'autre. */
    border: 0.8px solid rgba(var(--teinte-or), 0.52);
    border-radius: 0 12px 12px 0;
    /*
     * LE MEME OR QUE DANS LE VISUALISEUR.
     *
     * Cette famille inventait sa propre teinte, un or clair a texte sombre,
     * pendant que « Ajouter le programme a la commande » et « Commander des
     * programmes » portaient deja un or fonce a texte blanc. Deux ors pour la
     * meme idee, sur deux ecrans que la meme personne enchaine.
     *
     * Celui-ci est repris tel quel de `.therapy-action-btn`, ou le choix est
     * documente : le blanc y tient 5,20 de contraste sur le bout le plus
     * clair, la ou un or plus clair passait sous le seuil.
     */
    background: linear-gradient(135deg, #926D19, #624A11);
    /*
     * LE HALO ENTOURE LE BOUTON, IL NE TOMBE PAS DESSOUS.
     *
     * Il etait decale de huit pixels vers le bas : sur un fond sombre, une
     * lueur qui ne deborde que par en bas se lit comme une ombre portee, pas
     * comme la couleur du bouton qui rayonne. Sans decalage, elle sort de tous
     * les cotes et c'est bien le bouton qui s'allume.
     */
    /*
     * LA MEME LUEUR QUE LA REFERENCE, decalee vers le bas.
     *
     * J'avais centre le halo de cette famille. La consigne de centrage visait
     * le bouton de depot, vert ; celui-ci doit reprendre la facture de
     * `.therapy-action-btn`, ou la lueur portee est un choix deja tranche.
     * Deux boutons qui font la meme chose ne peuvent pas s'eclairer
     * differemment selon l'ecran.
     */
    box-shadow: 0 8px 18px rgba(152, 114, 27, 0.28);
    color: var(--encre-sur-fond-fixe);
    font: inherit;
    font-weight: 500;
    white-space: nowrap;
    text-decoration: none;
    cursor: pointer;
    /*
     * CE BOUTON NE SE SOULEVE PAS, ET C'EST UNE EXCEPTION ASSUMEE.
     *
     * Le geste du produit est un pixel d'elevation au survol. Ici, le bouton
     * est colle au champ de fichier : ils forment un seul objet, coins
     * arrondis a gauche pour l'un, a droite pour l'autre. Le soulever d'un
     * pixel decale cette moitie-la et la jointure se rompt sous le curseur,
     * au moment precis ou l'oeil est dessus.
     *
     * Le survol se lit donc par la clarte et par l'ombre, l'appui par un cran
     * de clarte en moins. Rien ne bouge. Meme raisonnement que la barre du
     * lecteur de test, ou les commandes ne se deplacent pas non plus.
     */
    transition: filter .14s ease, box-shadow .14s ease, background .14s ease,
                border-color .14s ease;
}

.bouton-programme:hover {
    filter: brightness(1.08);
    box-shadow: 0 10px 22px rgba(152, 114, 27, 0.38);
}

/*
 * ETEINT, IL NE S'ECLAIRE PLUS.
 *
 * Une lueur sur un bouton qui ne repond pas le fait passer pour une commande
 * disponible.
 *
 * `transition: none` AVEC : le bouton anime `box-shadow`, et l'etat desactive
 * est pose des le chargement par le script. La valeur lue restait celle de
 * l'etat actif alors qu'aucune regle correspondante ne s'appliquait plus --
 * mesure faite dans un onglet neuf, donc pas un artefact de lecture. Sans
 * transition a interpoler, la valeur finale s'applique tout de suite.
 */
.bouton-programme:disabled {
    box-shadow: none;
    transition: none;
}

.bouton-programme:active {
    filter: brightness(0.92);
}

/*
 * LE DEPOT D'UNE THERAPIE EST VERT, ET C'EST UNE EXCEPTION ASSUMEE.
 *
 * L'or vaut pour les programmes, et ce bouton en cree un. Mais il n'est pas
 * une commande parmi d'autres : c'est LE geste de l'ecran, celui qu'on vient
 * faire, et l'Admin le veut vert comme il l'a toujours ete. La regle des
 * couleurs garde son sens partout ailleurs -- « + Programme video » reste en
 * or creux juste a cote, et rien d'autre sur cet ecran n'est vert.
 *
 * Le vert descend vers le fonce plutot que de monter vers le clair : du blanc
 * sur #16a34a tient 3,3 de rapport, sur #15803d il en tient 5,0. Le meme
 * calcul avait ete fait pour « Valider » dans la barre du lecteur de test.
 */
/*
 * IL CHANGE DE COULEUR EN ENTIER, pas seulement de fond.
 *
 * La classe de base pose une bordure et une lueur DOREES. Repeindre le seul
 * fond laissait un bouton vert cercle d'or et eclaire en or -- la meme faute
 * que sur « Appliquer ce dosage », corrigee deux fois pour la meme raison :
 * une couleur ne se change pas par morceaux.
 */
.bouton-programme--depot {
    /*
     * LE BOUT LE PLUS CLAIR DECIDE, comme sur le jumeau dore juste au-dessus.
     * Le blanc tenait 5,05 sur `#15803d` et seulement 3,30 sur `#16a34a` : le
     * libelle passait ou non selon le coin du bouton ou il tombe. Le degrade
     * descend donc d'un cran, du vert 700 au vert 800, et le blanc tient 5,05
     * sur toute sa course.
     */
    background: linear-gradient(135deg, #15803d, #116233);
    border-color: rgba(var(--teinte-verte), 0.52);
    color: var(--encre-sur-fond-fixe);
}

/*
 * LE HALO N'EXISTE QUE QUAND LE BOUTON PEUT AGIR.
 *
 * Il etait pose sur le bouton et retire dans la regle « desactive ». Deux
 * regles pour un seul etat, et la seconde ne l'emportait pas : le bouton
 * eteint gardait sa lueur verte, ce qui le faisait ressembler a une commande
 * disponible alors qu'il ne repond pas.
 *
 * `:enabled` le pose SEULEMENT dans l'etat ou il a un sens. Il n'y a plus rien
 * a annuler, donc plus rien qui puisse rater.
 */
/*
 * SA LUEUR EST CENTREE, ET C'EST UNE EXCEPTION VOULUE.
 *
 * La famille doree porte une lueur decalee vers le bas, reprise de
 * `.therapy-action-btn`. Celui-ci la porte CENTREE : c'est la demande de
 * l'Admin sur ce bouton precis, et elle se defend -- une lueur qui ne deborde
 * que par en bas se lit comme une ombre portee, pas comme la couleur du bouton
 * qui rayonne.
 *
 * Je l'avais deja centree une fois, puis defaite en alignant la famille sur la
 * reference doree. La consigne ne portait pas sur la famille, elle portait sur
 * ce bouton.
 *
 * Et elle n'existe que quand le bouton peut agir : voir l'etat eteint.
 */
.bouton-programme--depot:enabled {
    box-shadow: 0 0 22px rgba(22, 163, 74, 0.34);
}

.bouton-programme--depot:enabled:hover {
    border-color: rgba(var(--teinte-verte), 0.72);
    box-shadow: 0 0 28px rgba(22, 163, 74, 0.46);
}

/*
 * ETEINT, PAS PALI.
 *
 * Une opacite reduite garde la couleur de l'action et la rend seulement plus
 * fade : le bouton continue de dire « vas-y » en plus faible. Desactive, il
 * n'est plus vert du tout, il perd son halo et son curseur de commande. Meme
 * traitement que « Valider » dans la barre du lecteur de test.
 */
.bouton-programme:disabled,
.bouton-programme--depot:disabled {
    background: rgba(var(--white-rgb), 0.06);
    border-color: rgba(var(--white-rgb), 0.10);
    box-shadow: none;
    color: var(--texte-discret);
    opacity: 1;
    cursor: default;
    filter: none;
}

/*
 * SEUL, donc arrondi des quatre cotes. Le bouton de base ferme la ligne du
 * depot de fichier : ses coins gauches sont droits pour se souder au champ.
 * Pose ailleurs, il lui faut ses quatre coins.
 */
/*
 * SEUL SUR SA LIGNE : PAS DE HALO, PAS DE DEGRADE.
 *
 * La facture de cette famille -- quatre coins inegaux, degrade, lueur portee
 * -- vient du bouton COLLE a un champ de fichier, ou elle fait tenir les deux
 * moities ensemble et signale la commande principale d'un ecran entier.
 * Detache, il ne reste de tout cela qu'un bouton qui brille plus fort que ce
 * qu'il lance : sur une carte de programme devenue huit lignes fines, la lueur
 * pesait plus que le programme.
 *
 * L'or reste, c'est lui qui dit ou l'on clique. Il devient plat : une seule
 * teinte, prise au milieu du degrade d'origine, ou le blanc tient son
 * contraste.
 */
.bouton-programme--seul {
    height: 40px;
    padding: 0 18px;
    border-radius: 10px;
    background: #85631A;
    border-color: rgba(var(--teinte-or), 0.42);
    box-shadow: none;
}

.bouton-programme--seul:hover {
    filter: none;
    box-shadow: none;
    background: #98721E;
    border-color: rgba(var(--teinte-or), 0.62);
}

.bouton-programme--seul:active {
    filter: none;
    background: #755716;
}

/*
 * Presque creux : il mene a un formulaire, il n'agit pas tout de suite, donc
 * il ne rivalise pas avec le depot. Mais il garde UN VOILE de sa propre teinte
 * derriere lui : un contour seul sur fond sombre se lit comme un cadre vide,
 * pas comme une commande.
 */
.bouton-programme--creux {
    border-radius: 12px;
    background: rgba(152, 114, 27, 0.30);
    border-color: rgba(var(--teinte-or), 0.45);
    box-shadow: none;
    color: rgb(var(--teinte-or));
}

.bouton-programme--creux:hover {
    background: rgba(var(--teinte-or), 0.22);
    border-color: rgba(var(--teinte-or), 0.62);
    box-shadow: none;
}

/* ----------------------------------------------------------------------
   LA NOTE SOUS UN CHAMP
   ----------------------------------------------------------------------
   Ce qu'il faut savoir AVANT de modifier une valeur, la ou on la modifie.
   Elle remonte contre le champ : posee a distance, elle se lit comme le
   commentaire du champ suivant.
   ---------------------------------------------------------------------- */
.champ-note {
    margin: -6px 0 12px;
    color: var(--texte-discret);
    font-size: 13px;
    line-height: 1.45;
}

/* Celle qui previent d'une casse porte un filet de sa couleur, a gauche :
   un simple texte gris se lit comme une precision, pas comme un garde-fou. */
/* --- 6. L'avertissement et son bouton s'alignent sur le champ -------------
   -------------------------------------------------------------------------
   Les trois objets de la colonne de droite disent la meme chose et doivent
   partir du meme point. Mesure avant reprise, colonne de 269 px :

     le champ e-mail       bord 337, texte a 352, va jusqu'a 605
     l'avertissement       bord 337, texte a 349, va jusqu'a 605
     le bouton             bord 337, texte a 354, s'arrete a 582

   DEUX DEFAUTS, ET LE SECOND EST LE PIRE. Les textes partent a trois abscisses
   differentes, ce qui se voit sur trois lignes empilees. Et le bouton s'arrete
   23 px avant le bord : trop peu pour qu'on y lise une intention, assez pour
   qu'on y lise un ratage. Un objet doit etre franchement plus court, ou
   exactement aussi long.

   LE BOUTON PREND LA LARGEUR DE SA COLONNE. Il ne devient pas pour autant le
   bouton principal de l'ecran : ce qui donne ce rang a un bouton pleine
   largeur, c'est de barrer LE BLOC, or celui-ci ne remplit qu'une demi-colonne
   de formulaire, sous le champ qu'il concerne. Il se lit comme ce qu'il est,
   la suite du champ e-mail.

   L AVERTISSEMENT RECULE DE 3 px. Son filet fait 2 px et son rembourrage 10,
   donc son texte partait 3 px avant celui du champ. Treize les remet ensemble.
   ------------------------------------------------------------------------- */
.champ-note--avertit {
    padding-left: 13px;
    border-left: 2px solid rgba(var(--amber-rgb), 0.45);
    color: var(--amber-pale);
}


/* ==== elements-retenus : ce que le produit n'avait pas ====
   Ces regles n'ont pas d'equivalent plus haut : elles ne remplacent rien,
   elles ajoutent. Elles restent donc ou elles sont. ==== */

/* --- Le pied de bloc : un lien, pas un bouton ----------------------------
   -------------------------------------------------------------------------
   Retenu sur `pied-de-bloc.html`, cas F5.

   POURQUOI PLUS DE BOUTON. Un aplat gris pese autant qu'un vrai bouton
   d'action alors que ces liens-la ne font que DEPLACER. Sur le tableau de bord
   ils ajoutaient des surfaces pleines a onze deja colorees, et ne se
   rattachaient a rien : ni au contenu du bloc, ni aux boutons de ligne, ni au
   cyan du produit.

   POURQUOI LE CYAN ET PAS LE BLANC. Le blanc a 74 % tombe exactement sur la
   valeur des libelles d'activite voisins : le pied s'y noyait. Le cyan est la
   seule chose cyan du bas de page, donc reperable d'un coup, et il ne peut pas
   etre pris pour un statut : les statuts sont bleu, turquoise et vert, et ils
   vivent sur les rails de ligne, jamais en texte nu.

   ET LA REGLE DU CYAN RESERVE TIENT. Elle vise ce qui est CERCLE : une seule
   chose cerclee de cyan par page. En texte, l'accent ne couvre que des
   jambages, c'est son usage le moins couteux.
   ------------------------------------------------------------------------- */

.dashboard-card__foot .dashboard-card__link {
    background: none;
    border: 0;
    padding-left: 0;
    padding-right: 0;
    color: rgb(var(--accent-texte-rgb, var(--portal-accent-rgb)));
    font-weight: var(--graisse-moyenne);
}

/* L ECART EST UN PADDING, PAS UNE MARGE. Une marge appartiendrait a la ligne du
   parent, et un soulignage la traverserait pour aller mourir sous la fleche. Un
   padding est a l'interieur du bloc en ligne atomique, ou le trait n'entre pas.
   La fleche est un signe de direction, pas une lettre du libelle. */

.dashboard-card__foot .dashboard-card__link::after {
    content: "›";
    display: inline-block;
    padding-left: .38em;
    opacity: .8;
    transition: transform .16s;
}

/* DEUX SIGNAUX AU SURVOL, et il en faut deux : un chevron qui avance de 3 px
   est trop discret seul. Le libelle PALIT vers le blanc, parce que le cyan pur
   est deja au maximum de sa vivacite : monter ne peut vouloir dire qu'aller
   vers le blanc, ce qui sur fond sombre est un gain de contraste. La teinte ne
   change pas, seule sa saturation baisse.

   PAS DE SOULIGNAGE : il rendait le pied plus bavard que le contenu qu'il
   termine, et ramenait une ligne horizontale de plus sur une page qui vient
   d'en perdre partout. */

.dashboard-card__foot .dashboard-card__link:hover {
    color: color-mix(in srgb, rgb(var(--portal-accent-rgb)) 68%, #ffffff);
}

.dashboard-card__foot .dashboard-card__link:hover::after {
    transform: translateX(3px);
    opacity: 1;
}

/* L enfoncement est le seul moment ou un element a le droit de PERDRE en
   presence : il confirme que le clic est pris, il n'appelle plus. */

.dashboard-card__foot .dashboard-card__link:active {
    color: color-mix(in srgb, rgb(var(--portal-accent-rgb)) 78%, #0b2a38);
}

@media (prefers-reduced-motion: reduce) {
.dashboard-card__foot .dashboard-card__link::after { transition: none; }
}

@media (prefers-reduced-motion: reduce) {
.dashboard-card__foot .dashboard-card__link:hover::after { transform: none; }
}

/* --- « Enregistrer » : PERIME, la famille ordinaire s'en charge -----------
   -------------------------------------------------------------------------
   CE QUI ETAIT ECRIT ICI posait une ardoise opaque a 92 %, avec un argument qui
   tenait : plusieurs ecrans portent DEUX OU TROIS « Enregistrer » (facturation,
   reglages, traductions, profil, boutique), ce n'est pas l'action vedette d'une
   page mais la validation d'UN formulaire, et l'aplat cyan ferait trois vedettes
   sur une page de reglages.

   CET ARGUMENT VISAIT L APLAT, ET IL RESTE JUSTE CONTRE LUI. Mais la hierarchie
   arretee le 28/08 compte TROIS niveaux : l'aplat presque plein pour ce qui fait
   avancer, le cyan VOILE a 16 % pour le secondaire important, le voile blanc
   pour le secondaire discret. « Enregistrer » prend le deuxieme. Il engage sans
   faire avancer, et un voile a 16 % ne fait pas une vedette.

   ELLE ETAIT DEJA MORTE AVANT D ETRE RETIREE, et c'est le point a retenir. La
   famille ordinaire, plus bas dans cette feuille (`.lk-body .btn:not(...)`), a
   la MEME specificite et vient APRES : elle gagnait donc deja, et les trente
   « Enregistrer » du produit rendaient le cyan pendant que cette regle-ci
   annoncait l'ardoise. La planche `boutons-retenus` le montrait sans qu'on le
   lise : son texte disait l'ardoise, sa propre vignette rendait le cyan.

   « PUBLIER » N EST PAS DANS CETTE FAMILLE, et le croire a coute une passe.
   `project-news.css` le peint en degrade bleu par une regle de meme poids
   chargee APRES `lk.css` : elle gagne, quoi qu'on ecrive ici. Et ce n'est pas
   un oubli, c'est une doctrine assumee dans cette feuille-la, ou la couleur dit
   L ACTION et non son rang : bleu pour poster et envoyer, vert pour les
   resultats, or pour les programmes, rouge pour l irreversible. Les deux
   systemes se croisent sur ce bouton ; tant que ce n'est pas arbitre, on ne
   laisse pas ici un selecteur qui n'a jamais rien peint.
   ------------------------------------------------------------------------- */

.partner-client-notifications__actions .btn[type="submit"]:not(.btn-danger) {
    background: rgba(var(--portal-accent-rgb), 0.16);
    border: var(--filet-epaisseur) solid rgba(var(--portal-accent-rgb), 0.38);
    color: rgba(var(--white-rgb), 0.96);
    font-weight: var(--graisse-moyenne);
}

.partner-client-notifications__actions .btn[type="submit"]:not(.btn-danger):hover {
    background: rgba(var(--portal-accent-rgb), 0.22);
    border-color: rgba(var(--portal-accent-rgb), 0.52);
    color: rgba(var(--white-rgb), 0.98);
}

.partner-client-notifications__actions .btn[type="submit"]:not(.btn-danger):active {
    background: rgba(var(--portal-accent-rgb), 0.28);
    border-color: rgba(var(--portal-accent-rgb), 0.66);
    color: rgba(var(--white-rgb), 1);
}

/* --- L'interrupteur : celui du produit, piste cyan -----------------------
   -------------------------------------------------------------------------
   Retenu sur `interrupteur.html`, cas I3.

   CE QU IL REMPLACE. Les quatre cases du bloc « Notifications » etaient
   NATIVES : `appearance: auto`, donc le navigateur dessinait la boite, le
   crochet et le rayon des angles, et `accent-color` etait le seul levier. Le
   rendu changeait d'un systeme a l'autre alors que tout le reste de la page est
   dessine par nous. Et une case DECOCHEE y etait presque invisible sur fond
   sombre, alors qu'elle porte autant d'information que la cochee.

   ON NE REDESSINE RIEN : la recette du curseur vient de `.partage-interrupteur`
   (`diagnostic-viewer.css`), l'interrupteur du partage, valide et fige. On
   reprend ses classes telles quelles, y compris le rebond du pouce. Une copie
   aurait derive de son modele des la premiere retouche.

   CE QUI CHANGE : la piste allumee passe du vert au CYAN. Le vert du partage
   veut dire « visible par le client » dans le viewer ; ici il se serait ajoute
   a la pastille « ACTIVEES » et au chiffre du bloc, soit trois verts disant
   trois choses. Le cyan separe proprement : le vert dit l'ETAT du bloc, le cyan
   dit ce qu'on MANIPULE.

   CE QUE CETTE RECETTE DEMANDE AU BALISAGE. Un interrupteur ne se fabrique pas
   en CSS seul : il faut `<input>` suivi de `<span class="…__glissiere">`. Le
   changement est donc a porter dans les gabarits au moment de l'application ;
   il est deja fait dans la maquette.
   ------------------------------------------------------------------------- */

.partner-client-notifications__option.partage-interrupteur {
    /* La classe apporte aussi une mise en page en `inline-flex` prevue pour une
       rangee d'onglets. La ligne garde la sienne. */
    display: flex;
    gap: 12px;
    align-items: flex-start;
    font-size: inherit;
    color: inherit;
    white-space: normal;
}

.partner-client-notifications__option .partage-interrupteur__glissiere {
    margin-top: 2px;
}

.partner-client-notifications__option input:checked + .partage-interrupteur__glissiere {
    background: rgb(var(--portal-accent-rgb));
    border-color: rgb(var(--portal-accent-rgb));
}

/* --- La pastille : une geometrie, deux formes ----------------------------
   -------------------------------------------------------------------------
   Retenu sur `pastille.html` : rectangle 7 px, fond teinte 0,16.

   CE QU ELLE REMPLACE. 150 noms de classes dans cette famille a travers les
   feuilles (badge, pastille, chip, tag, puce). Les seules gelules ambre etaient
   onze, chacune avec ses valeurs : rembourrage 2, 3, 4 ou 5 px, fond a 0,13 /
   0,14 / 0,16 / 0,18, filet a 0,24 / 0,42 / 0,55. Et sur la seule page de
   l'espace praticien, TROIS registres coexistaient pour trois metiers qu'on ne
   distinguait justement pas.

   POURQUOI LE RECTANGLE. La gelule dit « mention posee a cote » : ses bouts
   ronds la detachent de tout alignement. Le rectangle dit « element parmi
   d'autres » et s'aligne sur les champs, boutons et cartes, qui ont tous 10 a
   12 px de rayon ici. Une rangee de tags rectangulaires se lit comme une LISTE,
   la meme en gelules comme un semis. 7 px et pas 10 : la pastille fait la
   moitie de la hauteur d'un champ, donc a rayon egal elle paraitrait bien plus
   ronde ; 7 sur 24 px donne le meme rapport que 12 sur 42.

   POURQUOI PAS L ARDOISE PLEINE, l'autre candidate. Elle pese presque autant
   qu'un bouton, or « Enregistrer » porte exactement cette ardoise. Deux objets
   au meme poids visuel dont l'un se clique et l'autre pas.
   ------------------------------------------------------------------------- */

.pastille, .dashboard-card__state, .journey-pill, .partner-client-activity-state, .partner-journey-step__status {
    /* LE LIBELLE ECLAIRCI DE 18 %, ET C EST UNE MESURE.

       La teinte pleine sur son propre fond a 16 % donne, quand une orbe du fond
       eclaircit le bloc dessous : rouge 3,97, violet 3,93, bleu 4,18. Trois
       teintes sous le seuil AA de 4,5, et ce sont celles qui portent le plus de
       sens ; les cinq autres passaient entre 5,1 et 7,1.

       Corriger ces trois-la une par une, c'est exactement ce qui a produit les
       onze gelules ambre. 82 % de teinte amene le plus faible a 4,75 au pire
       cas, sans qu'on lise un changement de couleur, et la regle couvre les
       teintes qu'on n'a pas encore inventees.

       LECON PLUS LARGE : sur un fond anime, un contraste ne se calcule pas sur
       le fond nominal. L'orbe deplace le resultat de 0,7 point. Une capture ne
       le montre jamais, parce qu'elle fige l'orbe a un endroit. */
    /* UNE TEINTE PAR DEFAUT, ET CE N EST PAS UN CONFORT.

       `rgba(var(--pastille-rgb), 0.16)` avec une variable NON DEFINIE n'est pas
       une couleur invalide qu'on verrait : la declaration entiere est jetee, en
       silence, et l'element se retrouve sans fond ni filet. C'est arrive des la
       premiere application, sur `.journey-pill--done` du parcours, qui ne
       nomme pas de teinte : les quatre pastilles « Effectue » se sont videes.

       Le defaut neutre garantit qu'une pastille sans teinte declaree reste une
       pastille. Toute variante qui veut sa couleur n'a plus qu'a nommer
       `--pastille-rgb`, et celle qui n'en veut pas ne casse rien. */
    --pastille-rgb: 226, 232, 240;

    --pastille-libelle: color-mix(in srgb, rgb(var(--pastille-rgb)) 82%, #ffffff);

    /* Une pastille est une note en marge, pas un titre : assez petite pour ne
       pas deteindre sur ce qu'elle qualifie, quelle que soit sa teinte. Une
       couleur ne domine pas par son intensite mais par son AIRE, c'est la meme
       regle que l'accent en aplat contre l'accent en filet. */

    /* LE CENTRAGE VERTICAL SE FAIT SUR LA BANDE CAPITALE, PAS SUR LA BOITE.

       CE QUI SE PASSAIT. Avec un rembourrage egal le texte tombait haut, et
       aucun reglage de `padding` ne pouvait le corriger proprement : mesure au
       canvas, la bande « haut des capitales -> ligne de base » demandait 1,69 px
       de descente pour le tag et 0,56 px pour l'etat. Deux formes, deux
       corrections OPPOSEES, parce que leurs hauteurs de capitale different
       (9 px contre 8).

       POURQUOI `align-items: center` NE SUFFIT PAS. Il centre la BOITE DE
       LIGNE, qui reserve la place d'un jambage (le p, le g) meme quand le mot
       n'en a pas, et cette reserve depasse la marge au-dessus des capitales. La
       boite est donc bien centree, le texte ne l'est pas. Et l'ecart change avec
       le MOT : « Test » tombait 1,77 px trop haut, « Suivi programme » etait
       juste. Aucune valeur fixe ne pouvait convenir aux deux.

       LA REPONSE EXACTE. `text-box: trim-both cap alphabetic` coupe la boite au
       ras du haut des capitales et de la ligne de base : il ne reste que la
       bande que l'oeil lit, et un rembourrage EGAL la centre vraiment. Plus de
       correction a regler, plus de dependance a la police ni au mot.

       IL FAUT `inline-block`, et c'est mesure : en `inline-flex` la coupe ne
       s'applique pas, le texte y devenant un element de flex anonyme. Meme
       reglage, la boite tombait a 19,84 px en `inline-block` et restait a
       25,98 en `inline-flex`. On perd `gap`, dont aucune pastille ne se sert :
       elles ne portent que du texte.

       5,7 px de rembourrage rend a la pastille les 22 px de haut qu'elle avait
       avant la coupe. */
    display: inline-block;
    text-box: trim-both cap alphabetic;
    padding: 5.7px 9px;
    min-height: 0;
    border-radius: 7px;
    border: var(--filet-epaisseur) solid transparent;
    line-height: 1.25;
    white-space: nowrap;
}

/* L ETAT : capitales. LES CAPITALES NE SONT PAS UN ORNEMENT, elles distinguent
   l'etat du tag meme sans couleur, donc en noir et blanc, a l'impression, et
   pour qui distingue mal les teintes. Une difference qui ne tient qu'a la
   couleur n'en est pas une.

   ELLE NE S EMPLOIE QUE LA OU LA PASTILLE EST SEULE : en tete de bloc, ou une
   mention en capitales se detache du titre. Voir la regle d'une seule forme par
   ligne, plus bas. */

.pastille--etat, .dashboard-card__state {
    font-size: 10.5px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .06em;
    background: rgba(var(--pastille-rgb), 0.16);
    border-color: rgba(var(--pastille-rgb), 0.42);
    color: var(--pastille-libelle);
}

/* Les variantes existantes ne font plus QUE nommer une teinte : chacune
   declarait ses trois valeurs, elles pouvaient donc diverger une a une. */

.dashboard-card__state--ok { --pastille-rgb: var(--share-bright-rgb); }

.dashboard-card__state--attention { --pastille-rgb: var(--amber-rgb); }

.dashboard-card__state--alerte { --pastille-rgb: var(--red-rgb); }

/* LES COMPTEURS DES NOUVELLES EN SONT SORTIS, et le rapprochement etait faux.
   Un tag NOMME quelque chose : un etat, une etape, un role, et c'est pour cela
   qu'il porte une surface, il faut le distinguer du texte autour. « Affiche
   2 », « Lu 0 », « Reactions 0 » ne nomment rien : ce sont trois MESURES,
   c'est-a-dire du texte de la meme nature que le titre au-dessus.

   Les enfermer chacun dans une gelule faisait trois petits objets la ou il y a
   trois chiffres, sur une ligne qui porte deja un rail et une mention d'etat.
   Ils redeviennent du texte, et l'oeil retrouve un seul objet : la ligne. */

.admin-news-draft__latest-stats > span {
    background: none;
    border: 0;
    border-radius: 0;
    padding: 0;
    font-size: 12px;
    color: rgba(var(--white-rgb), 0.54);
}

.admin-news-draft__latest-stats > span strong {
    color: rgba(var(--white-rgb), 0.82);
    font-weight: var(--graisse-moyenne);
}

.pastille--tag, .journey-pill {
    font-size: 11.5px;
    font-weight: var(--graisse-moyenne);
    text-transform: none;
    letter-spacing: normal;
    background: rgba(var(--pastille-rgb), 0.16);
    border-color: rgba(var(--pastille-rgb), 0.42);
    color: var(--pastille-libelle);
}

/* La teinte d'un tag d'etape vient de l'etape elle-meme, celle que porte deja
   le rail de la ligne. Les deux autres variantes du parcours nommaient leur
   couleur en dur, elles ne nomment plus qu'une teinte. */

.journey-pill--theme { --pastille-rgb: var(--journey-accent-rgb); }

.journey-pill--done { --pastille-rgb: var(--green-state-rgb); }

.journey-pill--muted {
    --pastille-rgb: 226, 232, 240;
    background: rgba(var(--white-rgb), 0.06);
    border-color: rgba(var(--white-rgb), 0.14);
    color: rgba(var(--white-rgb), 0.82);
}

/* UNE CLASSE SEULE NE GAGNE PAS, troisieme fois dans ce chantier.
   `admin-dashboard.css` reprend le tag des lignes clients avec

       .partner-clients-table tbody tr.partner-client-row .journey-pill--theme

   soit trois classes et deux elements. Ma regle `.journey-pill` fait une
   classe : elle perdait en silence, et la pastille gardait ses 0,18 / 0,34 et
   son libelle blanc pendant que tout le reste passait a la nouvelle recette.

   On RECOPIE le selecteur a l'identique. Ecrire plus court, meme en croyant
   viser la meme chose, c'est se garantir une divergence qu'aucune mesure ne
   signale. */

.partner-clients-table tbody tr.partner-client-row .journey-pill--theme {
    --pastille-rgb: var(--row-rgb, var(--teinte-bleue));
    background: rgba(var(--pastille-rgb), 0.16);
    border-color: rgba(var(--pastille-rgb), 0.42);
    color: var(--pastille-libelle);
}

/* LA VARIANTE NEUTRE. Ce n'est pas un repli : un tag sans teinte est un tag qui
   ne ment pas sur une classification qui n'existe pas. */

.pastille--neutre, .dashboard-card__state--muet, .partner-client-activity-state, .partner-journey-step__status {
    font-size: 11.5px;
    font-weight: var(--graisse-moyenne);
    text-transform: none;
    letter-spacing: normal;
    background: rgba(var(--white-rgb), 0.06);
    border-color: rgba(var(--white-rgb), 0.14);
    color: rgba(var(--white-rgb), 0.82);
}

/* UNE SEULE FORME PAR LIGNE. Sur une ligne client, l'etat d'activite cotoie le
   tag d'etape a quinze pixels : a cette distance, l'ecart entre capitales et
   casse normale ne se lit plus comme un ROLE, il se lit comme une inconstance.
   L'etat y prend donc la forme du tag, et ce sont les FAMILLES DE TEINTE qui
   distinguent : semantique pour l'etat, d'identite pour le tag.

   `.dashboard-card__state--muet` garde ses capitales, lui : il est seul en tete
   de bloc. La regle ne vise que la cohabitation. */

.dashboard-card__state--muet {
    font-size: 10.5px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .06em;
}

/* UN TAG N EST JAMAIS ROUGE. Les teintes SEMANTIQUES portent un jugement (vert
   tout va bien, ambre il faut regarder, rouge c'est casse) et sont reservees
   aux etats. Les teintes D IDENTITE ne jugent rien, elles nomment, et vont aux
   tags. Sans cette separation, la couleur d'alerte finit sur une simple
   classification et plus rien ne se distingue d'urgent. */

.pastille--ok { --pastille-rgb: var(--share-bright-rgb); }

.pastille--attente { --pastille-rgb: var(--amber-rgb); }

.pastille--probleme { --pastille-rgb: var(--red-rgb); }

/* --- La barre de progression : le trace suffit ---------------------------
   -------------------------------------------------------------------------
   Elle portait un fond blanc a 0,08 ET un filet blanc a 0,08 de 1 px, sur les
   deux pages a la fois. Trois surfaces empilees pour dire un pourcentage : le
   bloc, la piste, la portion faite.

   LE FILET NE SERVAIT RIEN. Sur une hauteur de 14 px, 1 px de bord fait un
   septieme de l'objet, et il est de la MEME couleur et de la MEME valeur que la
   piste qu'il cerne. Il ne la delimitait donc pas, il l'epaississait.

   LA PISTE RESTE, MAIS CHANGE DE CAMP. Premiere version : fond retire aussi.
   Vu en capture, le resultat ne dit pas « zero pour cent », il dit « il n'y a
   pas de barre » : elle occupe 12 px ou rien ne se passe, et le bloc se termine
   sur 36 px de vide. Une barre de progression a zero doit montrer LE CHEMIN
   RESTANT, sinon elle ne montre rien.

   Elle revient donc, mais EN CREUX. Deuxieme version essayee : cyan a 0,16,
   soit plus CLAIR que le bloc. Vu en capture sur les deux pages, a zero sur
   onze la barre se lit comme PLEINE : l'oeil prend la teinte claire pour la
   progression. Un vide qui parait rempli est pire qu'un vide invisible.

   Une piste doit s'enfoncer, pas ressortir : le sillon est plus sombre que la
   surface, la portion faite est plus claire. C'est la seule facon que l'etat a
   zero et l'etat a cent se distinguent sans lire le chiffre.
   ------------------------------------------------------------------------- */

.partner-journey-widget__bar {
    background: rgba(0, 0, 0, 0.22);
    border: 0;
}

/* LA PORTION FAITE PARLE CYAN. Elle etait un degrade bleu clair vers vert,
   c'est-a-dire deux teintes etrangeres dans un bloc cyan, et le vert y disait
   « acquis » alors qu'il dit « effectue » sur les pastilles trois lignes plus
   bas. Une seule teinte, celle de la page : sur 12 px de haut l'aire est trop
   faible pour que le cyan plein vire au fluo. */

.partner-journey-widget__bar span {
    background: rgb(var(--portal-accent-rgb));
}

/* --- Le repli « En savoir plus » : un lien, dans TOUS ses etats -----------
   -------------------------------------------------------------------------
   TROIS NOMS POUR LE MEME OBJET selon l'ecran : `__step__details` (onze fois
   sur le parcours praticien), `__now__details` et `__widget__details`. Les
   reprendre page par page a produit exactement ce qu'on evite : le repos
   corrige a trois endroits, et le SURVOL oublie aux trois.

   CE QUI RESTAIT. `admin-dashboard.css` pose
   `summary:hover { background: rgba(var(--white-rgb), 0.06); color: var(--white) }`.
   Au repos le lien etait donc propre, et il reprenait sa gelule des qu'on
   passait dessus : le seul etat ou l'objet redevenait un bouton etait celui ou
   on s'appretait a cliquer.

   LE COMPORTEMENT EST CELUI DU PIED DE BLOC, la reference des liens de sortie
   du produit : au survol il PALIT vers le blanc, a l'appui il FONCE. Jamais de
   surface, a aucun etat. Un lien se signale par sa couleur ; s'il lui faut un
   fond pour se faire remarquer, c'est un bouton.
   ------------------------------------------------------------------------- */

.partner-journey-step__details, .partner-journey-now__details, .partner-journey-widget__details {
    background: none;
    border: 0;
    padding: 0;
    /* ET LE FILTRE, QUI N EST PAS UN FOND. `backdrop-filter: blur(14px)
       saturate(1.2)` ne declare aucune couleur : il SATURE de 20 % ce qui est
       derriere. Le repli ouvert avait donc une zone plus claire, avec un bord
       net, sans qu'aucune regle de fond ne l'explique.

       DEUXIEME FOIS DANS CE CHANTIER, apres la carte de progression. La lecon
       est la meme : quand une teinte se voit sans qu'une regle de fond la
       porte, chercher `backdrop-filter` avant de conclure qu'il n'y a rien. */
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
}

.partner-journey-step__details > summary, .partner-journey-now__details > summary, .partner-journey-widget__details > summary {
    padding: 0;
    background: none;
    border: 0;
    border-radius: 0;
    color: rgb(var(--accent-texte-rgb, var(--portal-accent-rgb)));
    font-weight: var(--graisse-moyenne);
    font-size: 12.5px;
}

/* Les trois etats, et la surface reste absente aux trois. */

.partner-journey-step__details > summary:hover, .partner-journey-now__details > summary:hover, .partner-journey-widget__details > summary:hover {
    background: none;
    color: color-mix(in srgb, rgb(var(--portal-accent-rgb)) 68%, #ffffff);
}

.partner-journey-step__details > summary:active, .partner-journey-now__details > summary:active, .partner-journey-widget__details > summary:active {
    background: none;
    color: color-mix(in srgb, rgb(var(--portal-accent-rgb)) 78%, #0b2a38);
}

/* OUVERT N EST PAS SURVOLE. `details[open] > summary` heritait lui aussi d'un
   fond dans la feuille d'origine : le repli ouvert restait donc une gelule
   tant qu'on ne le refermait pas. */

.partner-journey-step__details[open] > summary, .partner-journey-now__details[open] > summary, .partner-journey-widget__details[open] > summary {
    background: none;
    margin-bottom: 8px;
}

/* --- 5. Les blocs passent en surface levée (B4) -------------------------
   RETENU APRES COMPARAISON de quatorze traitements sur `blocs.html`.

   Aucune bordure, un fond clair a 4 %, une ombre portee. Trois raisons l'ont
   emporte :

     il supporte d'etre REPETE. Sept blocs sur cette page : sept cadres se
     disputent l'attention, sept surfaces posees ne se disputent rien ;

     il est PREVISIBLE. La vraie carte porte `backdrop-filter: blur(14px)`,
     qui floute et sature ce qui passe derriere : sa couleur depend donc de
     l'orbe du fond qui passe dessous, et deux cartes cote a cote n'ont pas la
     meme teinte. Un fond fixe leur rend la meme.

     il coute moins cher a peindre. Six cartes floutees par page, plus la
     barre du haut : le navigateur recompose la zone derriere chacune a
     chaque image.

   LE CYAN N EST PAS ICI. Il reste a ce qui appelle un geste, et un bloc de
   contenu n'en appelle pas.
   ------------------------------------------------------------------------ */

.dashboard-card {
    /* UNE LUMIERE, PAS UNE COULEUR.

       Un aplat de cyan teinte la surface uniformement : le bloc devient
       « legerement cyan », donc quelque chose qu'on remarque. Un degrade qui
       s'eteint donne une lumiere : le coin haut gauche est effleure, le reste
       ne l'est pas, et l'oeil lit une surface eclairee, pas coloree.

       C'est le geste des cartes de support du parcours, repris a 4 % au lieu
       de 13 : celles-la font 350 px et signalent un contact, ces blocs-ci en
       font 1300 et ne signalent rien. La meme intensite sur une surface
       quatre fois plus grande cesserait d'etre un effleurement.

       L'extinction a 60 % : au-dela, le degrade se lit comme une transition
       entre deux couleurs, alors qu'on veut une source qui s'eteint. Le blanc
       a 3 % dessous garde au bloc son elevation, que le cyan seul ne donne
       pas. */
    background:
        linear-gradient(135deg,
            rgba(var(--portal-accent-rgb), 0.04),
            rgba(var(--portal-accent-rgb), 0) 60%),
        rgba(var(--white-rgb), 0.03);
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.28);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;

    /* PAS DE FILET : L OMBRE FAIT LE BORD.

       Le filet avait ete pose parce que le bloc « flottait » : le fond n'a pas
       la meme clarte d'un bout de page a l'autre, et un coin se perd la ou une
       orbe passe. Mais c'est l'ombre qui repond deja a ce probleme, et elle y
       repond mieux : elle assombrit le fond AUTOUR du bloc, donc l'ecart se
       creuse la ou le fond est clair, exactement la ou le bord risquait de se
       perdre. Un filet, lui, a la meme opacite partout : il compense le moins
       la ou il faudrait le plus.

       Reste que l'ombre est portee vers le BAS (0 10px 30px). Le bord haut
       n'en recoit presque rien, et c'est le seul endroit ou l'on peut encore
       voir le bloc s'arreter sans transition. C'est ce qu'il faut regarder
       pour juger.

       Pour revenir en arriere : `border: var(--filet-epaisseur) solid
       var(--filet-large);`. Le palier reste defini dans `socle-retenu.css`,
       il sert toujours aux cartes d'etape du parcours. */
    border: 0;
}

/* La peau des actions de ligne ne s'ecrit plus ici : elles sont devenues une
   BRANCHE de la famille du bouton second, plus bas dans cette feuille. Ce
   qu'elles avaient de propre, la teinte de leur ligne, est justement ce que la
   famille sait faire ; ce qu'elles avaient de different, un fond TEINTE plutot
   que blanc, est ce que le banc a ecarte. Reste ici la seule chose qui leur
   appartienne vraiment : le halo du survol, qui dit « cette ligne repond ». */

.btn.ligne-action:hover, .admin-latest-user__actions .btn:hover {
    box-shadow: 0 0 0 3px rgba(var(--row-rgb, var(--teinte-bleue)), 0.10);
}

/* --- Le champ de saisie : un creux, pas une surface ----------------------
   -------------------------------------------------------------------------
   RETENU SUR `champs.html` (C1) apres comparaison de cinq matieres.

   CE QU IL REMPLACE. 225 regles visent un champ dans les dix-huit feuilles du
   produit, et elles portent 41 recettes distinctes de fond, filet et rayon.
   Quelques-unes, relevees telles quelles : `rgba(black,0.34)` filet blanc 0,12
   rayon 12 dans `portal.css`, `rgba(white,0.07)` filet 0,12 rayon 10 dans
   `business.css`, `rgba(white,0.04)` filet 0,10 rayon 12 dans
   `admin-dashboard.css`, `rgba(ink-900,0.36)` avec un rayon `12px 0 0 12px`.
   Aucune ne reprend la precedente. Sur la seule page d'administration, six
   champs en portaient deja deux.

   POURQUOI LE CREUX. Un champ doit dire « on peut ecrire ici », un bouton dit
   « on peut agir ici », et les deux vivent cote a cote dans les memes blocs.
   Les quatre autres candidats les distinguaient par le DEGRE : un peu plus
   clair, un peu moins clair, un filet plus fin. Le creux les distingue par le
   SENS : le bouton ressort, le champ s'enfonce. C'est la seule des cinq qui ne
   puisse pas etre confondue de loin, et elle ne peut pas deriver, parce qu'il
   n'y a pas de demi-creux.

   Elle reprend en outre une decision deja prise la veille, celle de la piste
   de la barre de progression : ce qui attend d'etre rempli se creuse. Une
   regle de plus qui redit une regle existante coute moins qu'une regle neuve.

   CE QUE LE SELECTEUR NE TOUCHE PAS : les cases a cocher, les boutons radio,
   les curseurs et les selecteurs de fichier, qui ne sont pas des champs de
   saisie et que le navigateur dessine autrement.
   ------------------------------------------------------------------------- */

.lk-body input[type="text"], .lk-body input[type="search"], .lk-body input[type="email"], .lk-body input[type="password"], .lk-body input[type="number"], .lk-body input[type="tel"], .lk-body input[type="url"], .lk-body input[type="date"], .lk-body input[type="time"], .lk-body input[type="datetime-local"], .lk-body input:not([type]), .lk-body select, .lk-body textarea {
    background: rgba(0, 0, 0, 0.22);
    border: var(--filet-epaisseur) solid var(--filet-large);
    border-radius: 12px;
    color: rgba(var(--white-rgb), 0.94);
    /* LA POLICE EST HERITEE, ET CE N EST PAS UN DETAIL. Un `textarea` sans
       cette ligne prend `monospace`, la valeur par defaut du navigateur : sur
       l'administration, la zone « Court message sur les changements » etait
       rendue en chasse fixe au milieu d'un formulaire en Arial. Un champ dont
       le texte n'a pas la police de la page se lit comme un bloc de code. */
    font-family: inherit;
    box-shadow: none;
    /* Le flou de verre part avec le reste : il rendait la teinte du champ
       dependante de l'orbe du fond qui passait derriere, donc deux champs
       cote a cote n'avaient pas la meme. */
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
}

.lk-body input[type="text"]::placeholder, .lk-body input[type="search"]::placeholder, .lk-body input[type="email"]::placeholder, .lk-body input[type="password"]::placeholder, .lk-body input[type="number"]::placeholder, .lk-body input[type="tel"]::placeholder, .lk-body input:not([type])::placeholder, .lk-body textarea::placeholder {
    color: rgba(var(--white-rgb), 0.38);
}

/* AU SURVOL, LE FILET SEUL BOUGE. Le fond ne change pas : un creux qui
   s'eclaircit au passage de la souris se lit comme un bouton qui s'allume. */

.lk-body input[type="text"]:hover, .lk-body input[type="search"]:hover, .lk-body input[type="email"]:hover, .lk-body input[type="password"]:hover, .lk-body input[type="number"]:hover, .lk-body input[type="tel"]:hover, .lk-body input:not([type]):hover, .lk-body select:hover, .lk-body textarea:hover {
    border-color: var(--filet-petit);
}

/* EN SAISIE, LE FILET PREND L ACCENT ET UN HALO DE 3 px. C'est le seul endroit
   du produit ou `--filet-action` apparait sans etre le cadre appuye d'un bloc,
   et c'est justifie : pendant qu'on ecrit, le champ EST l'action de la page. */

.lk-body input[type="text"]:focus, .lk-body input[type="search"]:focus, .lk-body input[type="email"]:focus, .lk-body input[type="password"]:focus, .lk-body input[type="number"]:focus, .lk-body input[type="tel"]:focus, .lk-body input:not([type]):focus, .lk-body select:focus, .lk-body textarea:focus {
    outline: none;
    border-color: var(--filet-action);
    box-shadow: 0 0 0 3px rgba(var(--portal-accent-rgb), 0.12);
}

/* La liste deroulee d'un `select` est dessinee par le systeme : le fond doit y
   etre opaque, sinon Windows la rend en blanc sur blanc. */

.lk-body select option {
    background: rgb(var(--slate-mid-rgb));
    color: rgba(var(--white-rgb), 0.94);
}

/* --- La ligne d'une liste : le rail dit de quoi il s'agit ----------------
   -------------------------------------------------------------------------
   RETENU SUR `actualite.html` (N2).

   L'administration porte DEUX listes dans la meme colonne : les derniers
   comptes et les dernieres actualites. La premiere avait deja sa recette,
   posee en reprenant l'espace praticien : fond a la teinte de la ligne, filet
   a 0,14, rail gauche de 4 px a 0,76. La seconde etait une carte grise dans la
   carte, `rgba(blanc,0.04)` filet `rgba(blanc,0.043)` rayon 12, soit trois
   niveaux d'emboitement pour un titre, un etat et trois compteurs.

   CE QUI CHANGE N EST PAS LA FORME, C EST LA SOURCE DE LA TEINTE. La ligne de
   compte lit l'etape du client, la ligne d'actualite lit son etat. Le rail
   fait donc le meme travail dans les deux listes : dire d'un coup d'oeil de
   quoi il s'agit, sans qu'on ait a lire le mot ecrit a droite.

   POURQUOI L AMBRE POUR LE BROUILLON ET PAS POUR L ARCHIVE. L'ambre est la
   couleur d'attention du produit. Un brouillon attend une action, donc il la
   merite ; une actualite archivee n'attend rien, elle prend le gris neutre.
   Inverser les deux ferait signaler ce qui est termine et taire ce qui reste a
   faire.

   CE QUE CETTE RECETTE DEMANDE AU BALISAGE. La ligne doit porter son etat en
   classe, ce que le gabarit ne fait pas encore :

       <div class="admin-news-draft__latest-item est-{{ $item->status }}">

   avec `draft`, `published` ou `archived`, les trois valeurs du modele. Sans
   cette classe la ligne prend le cyan, qui est le defaut : une actualite
   publiee est le cas courant, donc l'oubli reste juste.
   ------------------------------------------------------------------------- */

.admin-news-draft__latest-item {
    --n-rgb: var(--portal-accent-rgb);
    background: rgba(var(--n-rgb), 0.07);
    border: var(--filet-epaisseur) solid rgba(var(--n-rgb), 0.14);
    border-left: 4px solid rgba(var(--n-rgb), 0.76);
    border-radius: 14px;
    padding: 12px 14px;
}

.admin-news-draft__latest-item.est-archived { --n-rgb: 148, 163, 184; }

.admin-news-draft__latest-item.est-draft { --n-rgb: var(--amber-rgb); }

/* Les lignes se suivent avec l'ecart interieur d'un bloc, pas colle a colle. */

.admin-news-draft__latest-item + .admin-news-draft__latest-item {
    margin-top: var(--espace-s);
}

/* --- La case a cocher : l'interrupteur, dessine par nous -----------------
   -------------------------------------------------------------------------
   RETENU SUR `interrupteur.html` (I3), et applique ici aux cases NATIVES.

   Le banc avait deja tranche pour les quatre cases du bloc « Notifications par
   e-mail » de l'espace praticien. La page « Mon compte » en porte cinq de plus,
   dans le meme role exactement : activer ou desactiver un envoi. Elles etaient
   restees natives, `appearance: auto`, avec `accent-color` en bleu : le
   navigateur dessine la boite, le crochet et le rayon, et le rendu change entre
   Windows, macOS et Firefox alors que tout le reste de la page est dessine par
   nous.

   POURQUOI L INTERRUPTEUR ET PAS UNE CASE REDESSINEE. Une case dit « cochez ce
   qui vous concerne » dans une liste ou plusieurs reponses coexistent ; un
   interrupteur dit « ceci est allume ou eteint », ce qui est exactement ce que
   font ces reglages. Le produit en possede deja un, valide et fige, celui du
   partage dans le viewer : on reprend sa forme et sa couleur au lieu d'en
   inventer une seconde.

   CE QUE CETTE VERSION NE DEMANDE PAS AU BALISAGE. L'interrupteur du viewer a
   besoin d'un `<span>` en plus de l'`<input>`. Celui-ci se dessine sur l'input
   seul : `appearance: none` le sort du rendu natif, la piste est son fond, le
   curseur est son `::before`. Aucun gabarit a toucher, donc aucune page ou on
   pourrait oublier de le faire.

   L EXCLUSION EST NECESSAIRE : la ou l'interrupteur dessine existe deja, son
   input est masque derriere le `<span>`. Lui redonner une taille le ferait
   reapparaitre a cote de lui-meme.
   ------------------------------------------------------------------------- */

.lk-body input[type="checkbox"]:not(.partage-interrupteur input):not([hidden]):not(.case-a-cocher) {
    appearance: none;
    -webkit-appearance: none;
    flex: none;
    width: 34px;
    height: 20px;
    margin: 0;
    border-radius: 999px;
    background: rgba(0, 0, 0, 0.28);
    border: var(--filet-epaisseur) solid var(--filet-petit);
    position: relative;
    cursor: pointer;
    transition: background .16s, border-color .16s;
}

.lk-body input[type="checkbox"]:not(.partage-interrupteur input):not([hidden]):not(.case-a-cocher)::before {
    content: "";
    position: absolute;
    top: 2px;
    left: 2px;
    width: 14px;
    height: 14px;
    border-radius: 50%;
    background: rgba(var(--white-rgb), 0.62);
    /* LE MEME MOUVEMENT PARTOUT. Trois interrupteurs coexistaient avec trois
       courses : 0,16 s lineaire ici, 0,2 s avec depassement sur la planche,
       0,42 s avec rebond sur le partage. Celui-ci prend la courbe retenue --
       elle depasse legerement a l'arrivee, donc le bouton se POSE au lieu de
       s'arreter net. La correction vit dans la feuille du PRODUIT : la moitie
       des ecrans ne charge pas les couches retenues. */
    transition: transform .2s cubic-bezier(.32, .72, .32, 1.1), background .16s;
}

/* ALLUME : la piste prend le cyan PLEIN. Sur 34 par 20 px l'aire est trop
   faible pour que le cyan vire au fluo, et c'est la valeur retenue au banc. */

.lk-body input[type="checkbox"]:not(.partage-interrupteur input):not([hidden]):not(.case-a-cocher):checked {
    background: rgb(var(--portal-accent-rgb));
    border-color: rgb(var(--portal-accent-rgb));
}

.lk-body input[type="checkbox"]:not(.partage-interrupteur input):not([hidden]):not(.case-a-cocher):checked::before {
    transform: translateX(14px);
    background: #ffffff;
}

/* A l'appui le bouton s'allonge vers sa destination : le geste se sent avant
   d'etre fini. */
.lk-body input[type="checkbox"]:not(.partage-interrupteur input):not([hidden]):not(.case-a-cocher):active::before {
    width: 17px;
}

.lk-body input[type="checkbox"]:not(.partage-interrupteur input):not([hidden]):not(.case-a-cocher):checked:active::before {
    transform: translateX(11px);
}

.lk-body input[type="checkbox"]:not(.partage-interrupteur input):not([hidden]):not(.case-a-cocher):disabled {
    opacity: .5;
    cursor: default;
}

.lk-body input[type="checkbox"]:not(.partage-interrupteur input):not([hidden]):not(.case-a-cocher):focus-visible {
    outline: none;
    box-shadow: 0 0 0 3px rgba(var(--portal-accent-rgb), 0.18);
}

@media (prefers-reduced-motion: reduce) {
.lk-body input[type="checkbox"], .lk-body input[type="checkbox"]::before { transition: none; }
}

/* TROIS ENDROITS, PAS DEUX. Trouve en ouvrant le menu du compte : le meme
   compteur y apparait une troisieme fois, a cote de « Mon compte », sous le nom
   `lk-menu__pastille`. Il remplace celui de l'avatar tant que le menu est
   ouvert. Les trois portent la meme recette, sans quoi le compteur changerait
   d'apparence entre le moment ou on le voit et celui ou on va le chercher. */

.notifications-count, .lk-top-bar__pastille, .lk-menu__pastille {
    --pastille-rgb: var(--portal-accent-rgb);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 21px;
    height: 21px;
    padding: 0 6px;
    box-sizing: border-box;
    border-radius: 7px;
    background: rgb(var(--pastille-rgb));
    border: var(--filet-epaisseur) solid rgb(var(--pastille-rgb));
    color: #06222b;
    font-size: 11.5px;
    font-weight: 700;
    line-height: 1;
    /* Les chiffres ont tous la meme chasse : un compteur qui passe de 9 a 10 ne
       doit pas faire sauter ce qui le suit. Repris de la regle retiree de
       `notifications.css`, ou il etait le seul reglage a ne pas faire double
       emploi. */
    font-variant-numeric: tabular-nums;
}

/* Sur l'avatar elle mord le bord du rond : elle reste ronde, sinon un
   rectangle pose sur un cercle fait un accident de forme. */

.lk-top-bar__pastille {
    border-radius: 999px;
    min-width: 18px;
    height: 18px;
    padding: 0 4px;
    font-size: 10.5px;
}

/* --- Le compteur du menu : un seul a la fois ------------------------------
   -------------------------------------------------------------------------
   DEUX CORRECTIONS, trouvees en ouvrant le menu et en survolant l'onglet.

   1. L ANNEAU CLAIR AU SURVOL. `lk.css` pose
   `box-shadow: 0 0 0 2px rgba(slate-light, 0.9)` sur la pastille des que
   l'onglet est survole. Au repos l'anneau vaut `rgba(15,23,42,0.96)`,
   c'est-a-dire la couleur de la barre : il sert a DETACHER la pastille de la
   photo qu'elle chevauche, et il fait son travail sans se voir. En passant au
   gris clair il cesse de detacher pour se montrer, et il enferme la pastille
   cyan dans un cerne qui n'appartient a rien.

   L'anneau garde donc sa valeur de repos dans tous les etats. Ce qui change au
   survol est l'onglet, pas ce qu'il contient.

   2. LE DOUBLE COMPTEUR. Le meme chiffre est affiche deux fois des que le menu
   s'ouvre : sur l'avatar, et a cote de « Notifications » dans le menu. Deux
   fois le meme nombre au meme instant, a trois centimetres l'un de l'autre,
   pose la question de savoir s'il s'agit du meme.

   CELUI DU HAUT S EFFACE. Il sert a signaler tant qu'on ne regarde pas ; des
   que le menu est ouvert, on regarde, et c'est celui du menu qui dit OU aller.
   Les deux portent la meme recette, donc l'oeil suit le meme objet qui se
   deplace au lieu d'en voir deux.
   ------------------------------------------------------------------------- */

.lk-top-bar__compte-bouton:hover .lk-top-bar__pastille, .lk-top-bar__compte-bouton[aria-expanded="true"] .lk-top-bar__pastille {
    box-shadow:
        0 0 0 2px rgba(var(--slate-rgb), 0.96),
        0 1px 3px rgba(0, 0, 0, 0.45);
}

.lk-top-bar__compte-bouton[aria-expanded="true"] .lk-top-bar__pastille, .lk-top-bar__compte.is-open .lk-top-bar__pastille, .lk-top-bar__compte:has(.lk-menu.is-open) .lk-top-bar__pastille {
    display: none;
}

/* --- LE BLOC DE BASE, pour de bon --------------------------------------
   -------------------------------------------------------------------------
   RETENU AU BANC `blocs.html` (B4), et pose ici APRES l'avoir corrige trois
   fois page par page. C'est la vraie lecon de la journee.

   CE QUI S EST PASSE. Le traitement etait ecrit sur `.dashboard-card`, la
   classe de l'espace praticien et de l'administration. Toutes les autres pages
   emploient `.card` nu, donc elles gardaient l'ANCIEN traitement, ardoise
   `rgba(30,41,59,0.7)` et `blur(14px)`, sans que rien ne le signale : un bloc
   en retard n'a pas de couleur fautive, il a juste la mauvaise matiere. Je l'ai
   donc redecouvert sur « Mon compte », puis sur les notifications, puis sur la
   liste des comptes, en le corrigeant localement chaque fois.

   TROIS CORRECTIONS LOCALES VALENT UN AVEU : la regle etait au mauvais etage.

   POURQUOI JE NE L AVAIS PAS FAIT PLUS TOT, et pourquoi c'est possible
   maintenant : `.card` accompagne, sur les pages deja validees, des classes qui
   ont leur propre decision, et deux d'entre elles ne declarent pas leur ombre.
   Elles en auraient donc herite. Les exclusions ci-dessous les nomment une par
   une, ce qui est verifiable, plutot que de laisser la regle au mauvais etage,
   ce qui ne l'est pas.

   DETTE A RESORBER : chaque classe exclue devrait declarer son bloc en entier,
   fond ET ombre, dans sa propre feuille. Le jour ou elles le feront, ces
   `:not()` tomberont d'eux-memes.
   ------------------------------------------------------------------------- */

.lk-body .card:not(:where(.btn-danger, .journey-stage-card, .journey-section-card, .journey-testing-start-section, .partner-journey-widget, .partner-journey-summary-card)) {
    background:
        linear-gradient(135deg,
            rgba(var(--portal-accent-rgb), 0.04),
            rgba(var(--portal-accent-rgb), 0) 60%),
        rgba(var(--white-rgb), 0.03);

    border: 0;
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.28);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
}

/* --- LE CREUX CYAN, POUR CE QUI ENGAGE ------------------------------------
   -------------------------------------------------------------------------
   RETENU SUR `banc-secondaire.html` (R5), puis deplace d'un cran.

   IL ETAIT D ABORD ALLE AU BOUTON SECOND, et l'Admin l'a repris en regardant
   « Mon compte » : le bouton qui ACCOMPAGNE et le bouton qui ENGAGE ne peuvent
   pas porter la meme peau. Le creux est donc monte d'un rang.

   TROIS RANGS SUR LE FOND DE BASE, ET C'EST UNE ECHELLE DE CYAN, ce qui la
   rend lisible sans qu'on ait a l'apprendre :

     le second     aucun cyan, un blanc a 13 %      il accompagne
     l'ordinaire   un creux a 16 %, filet a 38 %    il engage
     l'appel       l'aplat plein a 62 %             il fait avancer

   « Enregistrer », « Publier », « Appliquer » sont au rang du milieu : ils
   engagent quelque chose sans faire avancer, on reste sur la page.

   CE QU'IL REMPLACE, ET POURQUOI. L'ardoise opaque a 92 %. Etant OPAQUE, son
   ecart avec son fond dependait du fond : 1,55 sur un bloc colore, 1,26 sur le
   fond de page, et 1,00 sur une tuile ambre, c'est-a-dire la luminance exacte
   de sa carte. Un bouton qu'on ne voyait plus. Pire, l'ecart entre elle et le
   bouton second tombait a 1,13 sur le fond de page : les deux rangs se
   confondaient la ou ils sont le plus souvent voisins.

   38 % ET NON 52. La peau vient du bouton « Enregistrer » d'une carte de
   coupon, qui portait 52 %, c'est-a-dire `--filet-action`. Ce palier designe
   LA chose importante d'un ecran ; trente boutons de soumission le videraient
   de son sens. A 38 % le trait garde son dessin sans confisquer le palier, et
   52 ne revient qu'a l'appui, le temps d'un clic.
   ------------------------------------------------------------------------- */

.lk-body .btn:not(:where(.btn-danger, .dashboard-card__link)) {
    background: rgba(var(--portal-accent-rgb), 0.16);
    border: var(--filet-epaisseur) solid rgba(var(--portal-accent-rgb), 0.38);
    color: rgba(var(--white-rgb), 0.96);
    font-weight: var(--graisse-moyenne);
}

.lk-body .btn:not(:where(.btn-danger, .dashboard-card__link)):hover {
    background: rgba(var(--portal-accent-rgb), 0.22);
    border-color: rgba(var(--portal-accent-rgb), 0.52);
    color: rgba(var(--white-rgb), 0.98);
}

/* L'appui, qui manquait a cette famille comme il manquait a l'autre. */

.lk-body .btn:not(:where(.btn-danger, .dashboard-card__link)):active {
    background: rgba(var(--portal-accent-rgb), 0.28);
    border-color: rgba(var(--portal-accent-rgb), 0.66);
    color: rgba(var(--white-rgb), 1);
}

/* --- LES BOUTONS D UNE CARTE DE L HISTORIQUE, PLUS LEGERS ----------------
   -------------------------------------------------------------------------
   Trois boutons de suite en tete de chaque carte, et douze cartes a la page :
   le filet a 38 % et le voile a 16 % font, repetes trente-six fois, une grille
   de pastilles qui pese plus que les faits qu'elle accompagne. Ce sont des
   raccourcis, pas des appels a l'action.

   ON DESCEND D UN PALIER, celui qui existe deja : `--filet-petit`, 24 %. Le
   trait reste lisible, il cesse de crier. Le voile passe a 8 %, moitie moins,
   pour que le bouton se lise comme un contour et non comme un aplat. Au survol
   et a l'appui, on retrouve les valeurs de la famille : c'est au repos que le
   bouton doit s'effacer, pas sous le doigt.

   PLACEE ICI, JUSTE APRES LA FAMILLE, ET C EST LE POINT QUI COMPTE.
   `.diagnostic-history__carte .btn` pese exactement autant que
   `.lk-body .btn:not(...)` : deux classes chacune. A specificite egale, c'est
   l'ordre qui tranche. Posee plus haut dans la feuille, pres des autres regles
   de l'historique, elle n'aurait jamais rien peint et on aurait cherche
   ailleurs.
   ------------------------------------------------------------------------- */

/*
 * QUATRE CLASSES, ET CE N EST PAS DE LA SURENCHERE.
 *
 * `champ-telephone.css` porte `.lk-content .card form .btn:not(...)`, trois
 * classes, dans une feuille chargee APRES celle-ci. Ecrite a deux classes, la
 * regle ci-dessous peignait les raccourcis, qui sont des liens, et laissait
 * intacts les boutons places dans un formulaire : le bouton d'import et celui
 * du renvoi gardaient le dessin de la famille pendant que leurs voisins
 * s'allegeaient. Trois cartes sur la meme rangee, deux dessins.
 *
 * `.lk-content` + `.card` + la classe de la carte + `.btn` fait quatre, ce qui
 * tranche sans dependre de l'ordre des feuilles.
 */
.lk-content .card.diagnostic-history__carte .btn:not(:where(.btn-danger, .dashboard-card__link)) {
    background: rgba(var(--portal-accent-rgb), 0.08);
    border: var(--filet-epaisseur) solid var(--filet-petit);
}

.lk-content .card.diagnostic-history__carte .btn:not(:where(.btn-danger, .dashboard-card__link)):hover {
    background: rgba(var(--portal-accent-rgb), 0.16);
    border-color: rgba(var(--portal-accent-rgb), 0.38);
}

/* --- LES PASTILLES D ETAT DES MODULES ------------------------------------
   -------------------------------------------------------------------------
   Chaque module a ecrit la sienne, et toutes sont des gelules : l'etat d'un
   courriel, celui d'un parcours de praticien, le type d'une transaction, la
   capacite d'un praticien, l'etat d'une connexion. Les TEINTES sont deja
   justes, vert pour ce qui est parti, gris pour ce qui est annule : c'est de la
   donnee, elle garde sa couleur. Seule la forme rejoint la famille.
   ------------------------------------------------------------------------- */

.status, .admin-email-status, .admin-partner-journey-status, .processing-transaction-type, .capacites-pastille, .connexions-ligne__etat, .liste-carte__etat {
    border-radius: 7px;
    padding: 5.7px 9px;
    font-size: 11.5px;
    font-weight: var(--graisse-moyenne);
    line-height: 1;
    min-height: 0;
}

/* --- LE TEXTE SECONDAIRE -------------------------------------------------
   -------------------------------------------------------------------------
   `.muted` porte `rgb(148,163,184)`, un gris BLEUTE opaque, et l'audit le
   trouve 620 fois sur huit pages. Notre echelle de texte secondaire est du
   blanc translucide : les deux se ressemblent assez pour qu'on ne les distingue
   pas isolement, et assez peu pour qu'une page qui melange les deux paraisse
   avoir deux gris.

   0,54 de blanc : la meme valeur que les sous-titres de bloc, mesuree a 5,9 de
   contraste sur la surface levee, donc AA large.
   ------------------------------------------------------------------------- */

.lk-content .muted {
    /* La mention courante : 0,54 rend 3,64 sur une carte teintee en clair.
       `--voile-mention` porte la bascule par theme ; le repli sert aux pages
       qui ne chargent pas les couches retenues. */
    color: rgba(var(--white-rgb), var(--voile-mention, 0.54));
}

/* --- LA TAILLE DES CHAMPS ------------------------------------------------
   -------------------------------------------------------------------------
   L'audit signale `13,3333px` sur neuf pages, soit `0.8333em` non arrondi, et
   `12,16px` ailleurs. Aucune des deux n'appartient a une echelle : ce sont des
   restes de calculs relatifs empiles.

   14 px, la taille du texte courant des formulaires. La recette du champ ne la
   fixait pas, ce qui laissait chaque page en heriter autrement.
   ------------------------------------------------------------------------- */

.lk-body input[type="text"], .lk-body input[type="search"], .lk-body input[type="email"], .lk-body input[type="password"], .lk-body input[type="number"], .lk-body input[type="tel"], .lk-body input[type="url"], .lk-body input[type="date"], .lk-body input:not([type]), .lk-body select, .lk-body textarea {
    font-size: 14px;
}

/* --- LES ETIQUETTES DE TABLEAU ET DE FORMULAIRE ---------------------------
   -------------------------------------------------------------------------
   TROUVE PAR AUDIT sur sept pages, environ quatre cents fois : les en-tetes de
   colonne (`TH`), les etiquettes de champ (`LABEL`) et les libelles que le
   tableau replie affiche en mode etroit (`.responsive-table__label`) portent
   `rgb(148,163,184)`, le meme gris BLEUTE opaque que `.muted`.

   Ce sont tous des textes secondaires : ils nomment ce qu'on regarde, ils ne
   sont pas ce qu'on regarde. Ils prennent la valeur de l'echelle, 0,54 de
   blanc, la meme que les sous-titres de bloc.

   POURQUOI PAS AVEC `.muted` : `.muted` est une classe posee a la main, les
   trois autres sont structurelles. Une page peut employer les unes sans les
   autres, donc les regrouper dans une seule regle aurait laisse passer les
   pages qui n'utilisent pas `.muted`, ce qui est exactement ce qui vient
   d'arriver.
   ------------------------------------------------------------------------- */

.lk-content th, .lk-content label, .lk-content .responsive-table__label,
/* LE CORPS DE L APPLICATION, PAS SEULEMENT SON CONTENU. Les libelles des
   formulaires d'administration vivent hors de `.lk-content` : la correction ne
   les atteignait pas et ils rendaient encore 3,84 en clair (mesure sur
   `adm-lots`). `.lk-body` est pose sur toutes les pages du produit. */
.lk-body label,
.lk-body th,
.lk-body .bascule{
    /* LE CRAN 0,54 EST CALIBRE SUR UN SOL SOMBRE. Il y rend 5,42 ; sur le sol
       clair la meme valeur tombe a 3,54 pour un libelle de 13 px. Mesure sur
       soixante-dix pages du banc : c'est la famille la plus fautive, soixante
       cas sur cinq pages.

       ELLE VIT ICI ET PLUS DANS LA COUCHE RETENUE. La correction existait,
       ecrite dans `elements-retenus.css`, que quarante-trois pages sur
       soixante-dix ne chargent pas : le defaut restait, et il etait repute
       regle. Une correction de contraste se range la ou toutes les pages la
       voient (`REGLES_CONSTRUCTION.md`, section 6).

       Le repli porte la valeur du sombre, ou 0,54 passe deja. */
    color: rgba(var(--white-rgb), var(--voile-mention, 0.54));
}

/* --- LES DERNIERES PASTILLES DE MODULE -----------------------------------
   -------------------------------------------------------------------------
   Trois modules avaient garde la leur, en bleu vif et en gelule : les langues
   de l'aide, les capacites d'un praticien, l'etat d'un parcours. Meme geste que
   pour les etats de courriel : la forme rejoint la famille, la teinte suit ce
   qu'elle dit.

   LE BLEU VIF SORT, parce qu'ici il ne distingue rien : les trois pastilles de
   langue sont bleues, les six de capacite aussi. Une couleur qui ne varie pas
   entre deux exemplaires du meme objet est de l'interface, donc elle prend la
   teinte de la page.
   ------------------------------------------------------------------------- */

.admin-help-locale-pill, .capacites-pastille, .connexions-fenetre__choix, .admin-dashboard__badge {
    border-radius: 7px;
    padding: 5.7px 9px;
    font-size: 11.5px;
    font-weight: var(--graisse-moyenne);
    line-height: 1;
    min-height: 0;
    background: rgba(var(--white-rgb), 0.06);
    border: var(--filet-epaisseur) solid rgba(var(--white-rgb), 0.14);
    color: rgba(var(--white-rgb), 0.82);
}

.admin-help-locale-pill.is-active, .capacites-pastille.is-active, .connexions-fenetre__choix.is-active, .admin-help-locale-pill[aria-pressed="true"] {
    background: rgba(var(--portal-accent-rgb), 0.16);
    border-color: var(--filet-action);
    color: rgba(var(--white-rgb), 0.98);
}

/* --- LES DEUX DERNIERS FLOUS DE VERRE ------------------------------------
   -------------------------------------------------------------------------
   `.editor-main` et `.admin-partner-journey-card`. Le flou a ete retire partout
   ailleurs pour la raison mesuree au banc : il rend la teinte d'une surface
   dependante de l'orbe du fond qui passe derriere, donc deux surfaces cote a
   cote n'ont pas la meme couleur, et le navigateur recompose la zone a chaque
   image.
   ------------------------------------------------------------------------- */

.editor-main, .admin-partner-journey-card {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
}

/* --- Le champ colle a son bouton -----------------------------------------
   -------------------------------------------------------------------------
   Le lien d'invitation, le lien de parrainage, le lien a envoyer au candidat :
   partout ou un champ est suivi d'un bouton « Copier », les deux sont accoles
   pour former un seul objet. Le bouton avait bien ses angles coupes a gauche,
   `0 12px 12px 0` ; le champ gardait ses QUATRE angles a 12 px.

   L'arrondi du champ passait donc DERRIERE le bouton, et la jointure montrait
   une courbe qui ne menait nulle part. Un objet fusionne n'a d'angles qu'a ses
   extremites.

   ET DEUX FILETS SE SUPERPOSAIENT a la jointure, celui de droite du champ et
   celui de gauche du bouton, soit deux pixels de trait entre deux surfaces qui
   se touchent. Le champ rend le sien : c'est le bouton qui porte la separation,
   parce que son filet est toujours le plus marque des deux.

   LE SELECTEUR EST GENERAL, par `:has(+ …)`, pour que le cas suivant n'ait pas
   a etre nomme. C'est exactement le genre de detail qu'on ne voit qu'une fois
   sur trois et qu'on corrige alors localement.
   ------------------------------------------------------------------------- */

.lk-body input:has(+ button), .lk-body input:has(+ .btn), .lk-body input:has(+ .bouton-programme) {
    border-top-right-radius: 0;
    border-bottom-right-radius: 0;
    border-right: 0;
}

/* Le symetrique, quand le bouton precede le champ. */

.lk-body button + input, .lk-body .btn + input {
    border-top-left-radius: 0;
    border-bottom-left-radius: 0;
    border-left: 0;
}

/* --- LA LARGEUR D UN CHAMP SUIT CE QU ON Y ECRIT -------------------------
   -------------------------------------------------------------------------
   MESURE SUR LA PAGE DES PARAMETRES, et les chiffres se passent d'argument :

     « Jours d'acces complet »     565 px pour ecrire « 30 »
     « Sous ce chiffre : rouge »   382 px pour ecrire « 50 »
     « Nombre minimal d'elements » 221 px pour ecrire « 1 »

   Soit plus de cinq cents pixels perdus sur le plus large. Un champ qui fait
   dix fois la taille de son contenu ne dit pas « ecrivez ici », il dit
   « ecrivez beaucoup », et le lecteur cherche ce qu'il devrait mettre de plus.
   La largeur d'un champ est une INFORMATION : elle annonce l'ordre de grandeur
   attendu.

   `max-width` PLUTOT QU UNE LARGEUR FIXE : les champs deja plus etroits que la
   borne ne bougent pas, et ceux qui vivent dans une colonne serree gardent la
   leur. La regle ne peut donc qu'ameliorer, jamais casser une mise en page qui
   tenait.

   LES BORNES sont en `em`, donc elles suivent la taille du texte : 9 pour un
   nombre, 12 pour une date, qui doit loger « 11.08.2026 » et son icone de
   calendrier.

   SEPT `em` NE SUFFISAIENT PAS, et c'est un prix de catalogue qui l'a montre :
   « 12500,00 » fait huit signes, virgule et decimales comprises, et se
   retrouvait rogne dans la carte d'un coupon. La borne d'origine avait ete
   calculee pour cinq chiffres et les fleches du navigateur ; elle passe a 9,
   ce qui loge le plus long montant du produit sans qu'un champ de quantite,
   toujours plus court que sa borne, ne bouge d'un pixel.
   ------------------------------------------------------------------------- */

.lk-body input[type="number"] {
    width: auto;
    max-width: 9em;
}

.lk-body input[type="date"], .lk-body input[type="time"], .lk-body input[type="datetime-local"] {
    width: auto;
    max-width: 12em;
}

/* =========================================================================
   LE BOUTON SECOND, ET LA REGLE QUI DECIDE DE SA PEAU
   =========================================================================
   RETENU SUR `banc-secondaire.html`, quatre peaux sur trois fonds.

   CE QUI A TRANCHE N EST PAS UNE COULEUR, C EST UNE CONSTANCE. Le banc mesure,
   sous chaque case, l'ecart entre la surface du bouton et son propre fond :

     peau                     bloc d'appel   tuile teintee   fond de page
     ardoise opaque               1,55           1,00            1,26
     blanc a 6 %                  1,18           1,20            1,14
     blanc a 13 %                 1,42           1,52            1,42

   L'ardoise opaque est celle qui VARIE, et son pire cas est une disparition :
   a 1,00 sur la tuile ambre, le bouton a exactement la luminance de sa carte.
   Un blanc translucide, lui, garde le meme ecart partout, parce qu'il ne se
   pose pas SUR un fond, il l'eclaircit.

   DEUX DOSAGES, ET LA RAISON EST LA MEME QUE DANS TOUT LE SOCLE.

     Sur un fond DEJA COLORE, il y a une couleur a emprunter : le bouton la
     prend par son filet, a 42 %, et sa surface reste neutre et discrete, un
     blanc a 6 %. C'est la tuile d'etape, la ligne teintee, le bloc d'appel.

     Sur le fond DE BASE, il n'y a rien a emprunter : le bouton se detache par
     sa surface, un blanc a 13 %, et son filet reste blanc. C'est le cas le
     plus frequent du produit, donc c'est le repli de la regle.

   LE CREUX CYAN A ETE ESSAYE ICI, ET IL EST PARTI UN CRAN PLUS HAUT. Compare
   au banc sur les trois fonds, il ne se detache pas mieux qu'un blanc, plutot
   moins : 1,34 a 1,38 d'ecart contre 1,42 a 1,52. Ce qui le distingue est son
   FILET, qui atteint 2,60 de contraste la ou le filet blanc plafonne a 1,49 :
   il ne se pose pas, il se DESSINE.

   C'est precisement ce qu'il ne faut pas pour un bouton qui ACCOMPAGNE. Il est
   donc alle au bouton ordinaire, celui qui engage. Voir la regle du bouton
   ordinaire, plus bas dans cette feuille.

   COMMENT LE BOUTON LE SAIT. Il ne le sait pas : ce sont les SURFACES qui le
   disent, en posant `--second-*`. Le bouton lit ces variables avec un repli,
   et le repli est le cas du fond de base. Une surface coloree qu'on ajouterait
   demain n'a qu'une ligne a declarer ; le bouton, lui, ne bouge plus.
   ========================================================================= */

.lk-body .btn.btn-secondary:not(:where(.btn-danger, .dashboard-card__link)), .lk-body .btn.ligne-action, .lk-body .btn.journey-step__action, .lk-body .admin-latest-user__actions .btn {
    background: rgba(var(--second-fond, var(--white-rgb)),
                     var(--second-surface, 0.13));
    border: var(--filet-epaisseur) solid
            rgba(var(--second-teinte, var(--white-rgb)),
                 var(--second-filet, 0.24));
    color: rgba(var(--white-rgb), 0.94);
    font-weight: var(--graisse-moyenne);
}

.lk-body .btn.btn-secondary:not(:where(.btn-danger, .dashboard-card__link)):hover, .lk-body .btn.ligne-action:hover, .lk-body .btn.journey-step__action:hover, .lk-body .admin-latest-user__actions .btn:hover {
    background: rgba(var(--second-fond, var(--white-rgb)),
                     var(--second-surface-survol, 0.18));
    border-color: rgba(var(--second-teinte, var(--white-rgb)),
                       var(--second-filet-survol, 0.34));
    color: rgba(var(--white-rgb), 0.98);
}

/* --- L'APPUI, QUI MANQUAIT --------------------------------------------
   Un bouton second clique ne rendait RIEN : la famille n'avait que deux etats
   sur trois. Le troisieme monte d'un cran de plus, surface et filet ensemble.

   MONTER PLUTOT QUE DESCENDRE, et c'est l'inverse de ce qu'on a fait pour
   l'aplat cyan. La raison est mesurable : un cyan est une couleur CLAIRE,
   l'eclaircir rapproche son libelle blanc du fond et coute du contraste ; un
   blanc translucide sur un fond tres sombre part de si loin qu'il a de la
   marge. Contraste du libelle sur le fond de base : 12,2 a 0,13 ; encore 8,9
   a 0,24.

   RIEN NE SE DEPLACE. Ces boutons vont souvent par trois dans une ligne de
   tableau : une cible qui bouge sous le doigt fait rater le clic suivant.
   ------------------------------------------------------------------------- */

.lk-body .btn.btn-secondary:not(:where(.btn-danger, .dashboard-card__link)):active, .lk-body .btn.ligne-action:active, .lk-body .btn.journey-step__action:active, .lk-body .admin-latest-user__actions .btn:active {
    background: rgba(var(--second-fond, var(--white-rgb)),
                     var(--second-surface-appui, 0.24));
    border-color: rgba(var(--second-teinte, var(--white-rgb)),
                       var(--second-filet-appui, 0.52));
    color: rgba(var(--white-rgb), 1);
}

/* --- Les surfaces qui ont une couleur a preter ---------------------------
   Chacune declare SA teinte et le dosage du fond colore. La liste est courte
   et nommee a dessein : une surface qui n'y est pas garde le cas de base, ce
   qui est le bon defaut. */

.journey-stage-card, .partner-journey-widget, .dashboard-card--large {
    --second-teinte: var(--portal-accent-rgb);
    --second-surface: 0.06;
    --second-surface-survol: 0.10;
    --second-surface-appui: 0.15;
    --second-filet: 0.42;
    --second-filet-survol: 0.62;
    --second-filet-appui: 0.86;
}

.journey-step {
    --second-teinte: var(--step-rgb);
    --second-surface: 0.06;
    --second-surface-survol: 0.10;
    --second-surface-appui: 0.15;
    --second-filet: 0.42;
    --second-filet-survol: 0.62;
    --second-filet-appui: 0.86;
}

.admin-latest-user, .liste-carte__ligne {
    --second-teinte: var(--row-rgb, var(--teinte-bleue));
    --second-surface: 0.06;
    --second-surface-survol: 0.10;
    --second-surface-appui: 0.15;
    --second-filet: 0.42;
    --second-filet-survol: 0.62;
    --second-filet-appui: 0.86;
}



/* ==== test-essai : ce que le produit n'avait pas ====
   Ces regles n'ont pas d'equivalent plus haut : elles ne remplacent rien,
   elles ajoutent. Elles restent donc ou elles sont. ==== */

/* --- 1. Ce qui ne se saisit pas ne se creuse pas --------------------------
   -------------------------------------------------------------------------
   TROIS CHAMPS COTE A COTE, DEUX RECETTES. « Poids » portait le creux retenu,
   `rgba(0,0,0,0.22)` sur filet cyan ; « Date de naissance » et « Sexe »
   portaient `rgba(blanc,0.05)` avec un filet a 0,07 et un rayon de 10.

   ET C EST JUSTE, une fois qu'on regarde ce qu'ils sont. Ces deux-la ne sont
   PAS des champs : ce sont des valeurs en lecture seule, tirees du profil, dans
   des `<div class="journey-testing-static">`. Les mettre en creux les rendrait
   identiques a une zone de saisie, donc les ferait cliquer pour rien.

   LE CREUX EST LE PENDANT DE LA SURFACE, et c'est la regle qui manquait :

     ce qui attend d'etre REMPLI se creuse ;
     ce qui est deja RENSEIGNE et ne bouge plus ressort.

   Ils prennent donc la surface levee de la famille, blanc a 0,04 sur le filet
   large. Le rayon passe de 10 a 12 pour s'aligner sur le champ voisin : trois
   objets sur une meme rangee, deux matieres assumees, une seule geometrie.
   ------------------------------------------------------------------------- */

.journey-testing-static {
    background: rgba(var(--white-rgb), 0.04);
    border: var(--filet-epaisseur) solid var(--filet-large);
    border-radius: 12px;
    padding: 10px 14px;
    box-sizing: border-box;
    min-height: 42px;
    display: flex;
    align-items: center;
}

/* --- 2. Le bouton d'appel prend l'aplat ----------------------------------
   -------------------------------------------------------------------------
   « Commencer le test » etait en `rgb(59,130,246)`, le meme bleu plein retire
   trois fois ailleurs dans la journee. C'est pourtant le seul bouton d'appel de
   la page, et la page n'existe que pour lui : il a droit a l'aplat retenu,
   celui de « Continuer » et de « Passer le test de suivi ».
   ------------------------------------------------------------------------- */

/* --- 2. L'APLAT SE PORTE, IL NE SE TROUVE PAS ----------------------------
   -------------------------------------------------------------------------
   CE QUI ETAIT ECRIT ICI : `.lk-content .btn:not(.btn-secondary)`, avec pour
   intention un SEUL bouton. Le commentaire d'origine le disait mot pour mot :
   « Commencer le test [...] c'est pourtant le seul bouton d'appel de la page,
   et la page n'existe que pour lui ». Venue de la feuille d'essai de l'ecran du
   test, ou `.lk-content` ne designait qu'un ecran, la regle a vise le produit
   entier une fois fondue.

   CE QU ELLE FAISAIT, MESURE LE 29/08 : une soixantaine de boutons en aplat,
   dont quarante bascules de langue sur `adm-courriels`, dix « Modifier » sur
   `adm-traitements` et deux « Enregistrer » sur `admin-reglages`. L'aplat
   designe ce qui fait avancer ; a soixante exemplaires il ne designe plus rien.

   ELLE ACCORDAIT L APLAT SELON LA POSITION DANS LE DOCUMENT, pas selon ce que
   le bouton fait. Un bouton place dans `.lk-content` devenait la vedette de son
   ecran, sans que personne l'ait decide. Desormais il le DECLARE : `btn-appel`.

   L AFFORDANCE DES FILTRES SURVIT SANS L APLAT. Sur `adm-lectures`, l'onglet
   actif portait `.btn` nu et les autres `.btn-secondary` : l'aplat servait
   d'etat selectionne. Apres ce changement l'actif prend le niveau 2 et les
   autres le niveau 3, donc l'ecart demeure, une marche plus bas. Un etat
   selectionne n'a pas besoin du signal reserve a ce qui fait avancer.
   ------------------------------------------------------------------------- */

/* --- L'ETAT SELECTIONNE SE VOIT, LUI AUSSI --------------------------------
   -------------------------------------------------------------------------
   `is-active` existait deja dans les gabarits, avec `aria-current` a cote,
   et RIEN ne le peignait : sur l'ecran d'un modele de courriel, la langue en
   cours d'edition etait indiscernable des trois autres. Le lecteur d'ecran
   savait, l'oeil non.

   IL PREND LE NIVEAU 2, pas l'aplat. Un onglet actif n'est pas ce qui fait
   avancer l'ecran, c'est un reperage. Le meme ecart que sur les filtres de
   `adm-lectures` : l'actif au niveau 2, les autres au niveau 3.

   LE SELECTEUR PORTE `.btn-secondary` pour passer devant la famille du niveau
   3, qui a le meme poids et vient plus haut dans cette feuille. */

.lk-body .btn.btn-secondary.is-active {
    background: rgba(var(--portal-accent-rgb), 0.16);
    border-color: rgba(var(--portal-accent-rgb), 0.38);
    color: rgba(var(--white-rgb), 0.96);
}

.lk-body .btn.btn-secondary.is-active:hover {
    background: rgba(var(--portal-accent-rgb), 0.22);
    border-color: rgba(var(--portal-accent-rgb), 0.52);
}

/* LA PORTE N EST PAS UN BOUTON D APPEL ORDINAIRE. Le bouton qui ouvre une
   seance de couleurs porte son propre contrat, dans `journey.css` : spectre
   sur le filet, halo dessous, pave inverse. Cette regle-ci pese autant que
   lui, donc l'ordre de chargement decidait -- et il n'est pas le meme d'une
   page a l'autre. On l'ecarte explicitement plutot que de compter dessus. */
.lk-body .btn.btn-appel:not(.journey-testing-field--submit .btn) {
    background: var(--accent-aplat);
    border: var(--filet-epaisseur) solid rgba(var(--portal-accent-rgb), 0.58);
    /* L APLAT EST PROFOND DANS LES DEUX THEMES : cyan assombri en sombre, or
       profond en clair. Son libelle ne suit donc pas le retournement -- ecrit
       `rgba(var(--white-rgb), .98)` il devenait ENCRE SOMBRE sur l'or et
       tombait a 3,46. Il reste blanc, fixe, comme l'aplat qu'il traverse. */
    color: var(--encre-sur-fond-fixe);
    font-weight: var(--graisse-moyenne);
}

.lk-body .btn.btn-appel:not(.journey-testing-field--submit .btn):hover {
    background: var(--accent-aplat-survol);
    border-color: rgba(var(--portal-accent-rgb), 0.72);
}

.lk-body .btn.btn-appel:not(.journey-testing-field--submit .btn):active {
    background: var(--accent-aplat-appui, var(--accent-aplat-survol));
    border-color: rgba(var(--portal-accent-rgb), 0.86);
}

/* `!important` CONTRE `!important`, et c'est le seul endroit du chantier ou je
   l'emploie. `lk.css` ecrit `background: var(--black) !important` sur le body :
   aucune specificite ne peut le battre, seul un autre `!important` le peut. Je
   ne le pose donc pas par confort mais parce que la regle d'en face a ferme la
   porte.

   LE CONTENEUR, LUI, se bat a la loyale : `body.diagnostic-player-page
   .diagnostic-app` porte deux classes, ma regle en avait une. Elle les reprend.
   Cinquieme fois de la journee que le selecteur doit etre recopie. */

body.diagnostic-player-page, body.therapy-player-page {
    background: var(--surface-sombre) !important;
}

body.diagnostic-player-page .diagnostic-app, body.therapy-player-page #app {
    background: var(--surface-sombre);
}

/* --- D2. La pastille ne parait qu'une fois l'entrainement lance -----------
   -------------------------------------------------------------------------
   « Test d'entrainement », en haut a gauche, est un RAPPEL : il dit ou l'on se
   trouve pendant que les images defilent en plein ecran et qu'aucun autre
   repere ne subsiste.

   Sur l'ecran d'accueil, il ne rappelle rien : le titre au centre dit deja
   « Test d'entrainement », en 36 px. Deux fois la meme phrase sur le meme
   ecran, dont l'une en tout petit dans un coin.

   Elle est donc masquee tant que la procedure n'a pas commence, et reparait
   avec elle.

   LE CRITERE A ETE CORRIGE APRES COUP, ET LA LECON VAUT D'ETRE GARDEE. La
   premiere version visait l'ecran d'accueil : « tant que `.diagnostic-overlay`
   n'a pas la classe `is-hidden` ». J'avais ecrit « en vrai, le script la
   pose », sans le verifier. Il ne la pose pas : il masque l'ecran par
   `overlay.style.display = 'none'`. Le selecteur restait donc vrai pendant
   toute la procedure, et les deux pastilles ne revenaient jamais.

   Le forçage que j'employais au banc posait justement `display: none` sur
   l'ecran d'accueil : il reproduisait le defaut sans que je le voie.

   On s'appuie desormais sur `demo-lancee`, que le script ajoute au corps de la
   page a la ligne suivante et dont `lk.css` se sert deja.

   QUAND ELLE REPARAIT, elle prend la matiere d'un bouton ordinaire : sur un
   fond devenu image, une surface opaque est la seule qui reste lisible.
   ------------------------------------------------------------------------- */

body.diagnostic-demo-page:not(.demo-lancee) .diagnostic-pill {
    display: none;
}

body.diagnostic-demo-page .diagnostic-pill--action, body.diagnostic-player-page .diagnostic-pill--action {
    position: relative;
    z-index: 10000;
    background: rgba(var(--slate-mid-rgb), 0.92);
    border: var(--filet-epaisseur) solid var(--filet-petit);
    color: rgba(var(--white-rgb), 0.96);
    font-weight: var(--graisse-moyenne);
    border-radius: 12px;
}

body.diagnostic-demo-page .diagnostic-pill--action:hover, body.diagnostic-player-page .diagnostic-pill--action:hover {
    background: rgba(var(--slate-mid-rgb), 1);
    border-color: rgba(var(--portal-accent-rgb), 0.42);
}

/* --- D3. Les boutons de la demo ------------------------------------------
   -------------------------------------------------------------------------
   Cinq peaux pour des actions de trois rangs :

     vert `rgb(22,163,74)`     « Demarrer l'entrainement », « Valider »
     bleu `rgb(59,130,246)`    « Suivant »
     ardoise `rgb(51,65,85)`   « Nouvelle image »
     ardoise `rgb(58,78,88)`   « Retour », « Quitter »

   CE QUI AVANCE PREND L APLAT. « Demarrer », « Suivant » et « Valider » font
   progresser la procedure : c'est l'action d'appel de leur ecran, une seule a
   la fois, donc l'aplat retenu. Le vert est retire ici parce qu'il ne dit
   aucun resultat, il dit « allez-y ».

   CE QUI ACCOMPAGNE PREND L ARDOISE. « Nouvelle image » relance sans faire
   avancer.

   CE QUI SORT DEVIENT UN LIEN. « Retour » et « Quitter l'entrainement » ne
   peuvent pas avoir la meme forme que ce qui avance, sous peine qu'on clique
   l'un pour l'autre au milieu d'une procedure.

   LES BOUTONS DE CANAUX NE BOUGENT PAS. Les vingt boutons `R+`, `J-`, `V+`
   portent des degrades rouge, jaune, vert, cyan, bleu, magenta : ce sont les
   CANAUX du test, donc de la donnee. Leur couleur est ce qu'ils mesurent.
   ------------------------------------------------------------------------- */

.diagnostic-app .btn.btn-therapy:not(.diagnostic-btn), .diagnostic-app .btn:not([class*="diagnostic-"]):not(.btn-secondary):not(.btn-primary), .diagnostic-mobile-action--valider {
    background: var(--accent-aplat);
    border: var(--filet-epaisseur) solid rgba(var(--portal-accent-rgb), 0.58);
    background-image: none;
    /* L APLAT EST PROFOND DANS LES DEUX THEMES : cyan assombri en sombre, or
       profond en clair. Son libelle ne suit donc pas le retournement -- ecrit
       `rgba(var(--white-rgb), .98)` il devenait ENCRE SOMBRE sur l'or et
       tombait a 3,46. Il reste blanc, fixe, comme l'aplat qu'il traverse. */
    color: var(--encre-sur-fond-fixe);
    font-weight: var(--graisse-moyenne);
}

.diagnostic-app .btn.btn-therapy:not(.diagnostic-btn):hover, .diagnostic-app .btn:not([class*="diagnostic-"]):not(.btn-secondary):not(.btn-primary):hover, .diagnostic-mobile-action--valider:hover {
    background: var(--accent-aplat-survol);
    border-color: rgba(var(--portal-accent-rgb), 0.72);
}

.diagnostic-app .btn.btn-primary:not(.diagnostic-btn) {
    background: rgba(var(--slate-mid-rgb), 0.92);
    border: var(--filet-epaisseur) solid var(--filet-petit);
    color: rgba(var(--white-rgb), 0.96);
    font-weight: var(--graisse-moyenne);
}

/* Les sorties : un lien, pas un bouton. */

/* LE SECONDAIRE DE L ECRAN D ACCUEIL EST UN BOUTON, PAS UN LIEN NU.
   `background: none` en faisait un libelle flottant a cote de « Commencer » :
   sur cet ecran pose au-dessus de l'image, un texte sans fond ne se distingue
   pas d'une legende, et rien ne dit qu'on peut cliquer. C'est la seule commande
   de ces deux tests qui restait sans fond, la palette ayant deja ete traitee
   (voir « D3 bis »).

   MEME ARDOISE QUE « QUITTER », pour que la porte de sortie ait la meme couleur
   d'un ecran a l'autre : celle de l'accueil et celle du test sont le meme geste.
   Le rang se lit sur la teinte et non sur l'absence de fond, l'action portant
   le vert. */
.diagnostic-app .btn.btn-secondary:not(.diagnostic-btn):not([class*="mobile"]) {
    background: #4d6775;
    border: 0;
    padding: 9px 16px;
    color: rgba(var(--white-rgb), 0.96);
    font-weight: var(--graisse-moyenne);
}

.diagnostic-app .btn.btn-secondary:not(.diagnostic-btn):not([class*="mobile"]):hover {
    background: #405663;
    color: var(--white);
}

.diagnostic-app .btn.btn-secondary:not(.diagnostic-btn):not([class*="mobile"]):active {
    background: #34464f;
    /* L'anneau interieur, comme pour les commandes de la palette : sur une
       image claire, un changement de teinte seul passe inapercu. */
    box-shadow: inset 0 0 0 2px rgba(255, 255, 255, 0.55);
}

/* --- D3 bis. Les deux actions de la palette ------------------------------
   -------------------------------------------------------------------------
   LA DECISION EXISTAIT DEJA, ET C'EST NOTRE FEUILLE QUI L'AVAIT EFFACEE.

   `lk.css` pose depuis le debut « Quitter » en ardoise bleutee `#4d6775` et
   « Valider » en vert `#15803d`, avec leurs survols et leurs appuis. La regle
   generale de la section D3 vise `.diagnostic-app .btn.btn-therapy`, cinq
   classes contre quatre : elle gagnait, et repeignait les deux en aplat cyan
   et en lien nu. Le defaut n'etait pas qu'une couleur manquait, c'est qu'une
   couleur juste avait ete recouverte.

   ON REND LES DEUX A LEUR REGLE D ORIGINE, avec ses valeurs exactes, plutot
   que d'en inventer d'autres : elles ont ete choisies pour tenir sur une
   IMAGE, dont la clarte change a chaque manche, et non sur le fond sombre du
   produit. Une ardoise a 92 % y ferait un trou, un vert plus sombre s'y
   perdrait.

   POURQUOI CES DEUX-LA ECHAPPENT AU CYAN. La barre est une grille de sept
   colonnes dont six portent les canaux du test, c'est-a-dire de la donnee.
   Une commande cyan y serait la treizieme couleur, a trois colonnes du canal
   `C+` qui est cyan lui aussi. Le vert dit en plus ce que fait le geste : il
   termine.
   ------------------------------------------------------------------------- */

.diagnostic-app .diagnostic-controls-bar .btn.btn-therapy:not(.diagnostic-btn) {
    background: #15803d;
    background-image: none;
    border: 0;
    color: rgba(var(--white-rgb), 0.98);
    font-weight: var(--graisse-moyenne);
}

.diagnostic-app .diagnostic-controls-bar .btn.btn-therapy:not(.diagnostic-btn):hover:not(:disabled) {
    background: #116b33;
}

/* --- Les deux etats qui manquaient, et pourquoi ils manquaient -----------
   -------------------------------------------------------------------------
   REPRENDRE UNE FAMILLE, C'EST REPRENDRE TOUS SES ETATS. La regle ci-dessus
   est plus specifique que celles de `lk.css` : cinq classes contre quatre.
   Elle gagne donc AUSSI quand le bouton est enfonce et quand il est eteint,
   et sans ces deux blocs elle y imposait le vert du repos. Le defaut ne se
   voyait pas au repos, c'est-a-dire dans quatre-vingt-dix pour cent des
   captures, et c'est exactement ce qui le rendait facile a laisser passer.

   L ANNEAU INTERIEUR N EST PAS DECORATIF. Ces boutons sont poses sur l'image
   du test, dont la clarte change a chaque manche : un changement de teinte
   seul peut passer inapercu sur un fond clair. L'anneau se lit sur n'importe
   quel fond, et comme il est `inset`, il n'agrandit pas le bouton et ne
   decale pas son voisin.

   AUCUN CONTOUR NON PLUS ICI. Les deux commandes portaient un anneau interieur
   a l'appui, pour la meme raison que les canaux : se voir sur n'importe quel
   fond. L'anneau clair appartient desormais au POINTEUR de la demonstration,
   et a lui seul (voir D6 et D7). Ces deux boutons descendent donc d'un cran de
   teinte, et rien d'autre. Les trois crans ont ete choisis pour le contraste
   du libelle : base 5,02, survol 6,61, appui 9,07. L'appui reste donc le plus
   lisible des trois, ce qui suffit a le voir.

   ETEINT, PAS PALI. Valider n'est cliquable qu'une fois la manche jouee.
   Une opacite garderait la couleur de l'action en la rendant seulement plus
   pale, ce qui se lit comme « vert, mais mal imprime ». Le gris dit qu'il
   n'y a rien a faire.
   ------------------------------------------------------------------------- */

.diagnostic-app .diagnostic-controls-bar .btn.btn-therapy:not(.diagnostic-btn):active:not(:disabled) {
    background: #0d5427;
    box-shadow: none;
}

.diagnostic-app .diagnostic-controls-bar .btn.btn-therapy:not(.diagnostic-btn):disabled {
    background: #2b3640;
    color: rgba(255, 255, 255, 0.48);
    opacity: 1;
    cursor: default;
}

.diagnostic-app .diagnostic-controls-bar .btn.btn-secondary:not(.diagnostic-btn) {
    background: #4d6775;
    border: 0;
    padding: 8px 10px;
    color: rgba(var(--white-rgb), 0.96);
    font-weight: var(--graisse-moyenne);
}

.diagnostic-app .diagnostic-controls-bar .btn.btn-secondary:not(.diagnostic-btn):hover {
    background: #405663;
    color: rgba(var(--white-rgb), 0.98);
}

.diagnostic-app .diagnostic-controls-bar .btn.btn-secondary:not(.diagnostic-btn):active {
    background: #34464f;
    box-shadow: none;
}

/* --- D4. Le fond du produit, et pas seulement sa couleur -----------------
   -------------------------------------------------------------------------
   PREMIERE VERSION INSUFFISANTE, et l'Admin l'a vu avant moi. J'avais pose
   `background: rgb(7,17,31)` sur l'ecran d'accueil, ce qui donne la bonne
   COULEUR mais un aplat uni. Le fond du produit n'est pas une couleur : c'est
   une couleur PLUS quatre orbes qui la font vivre, declarees dans
   `fond-retenu.css` sur `.mimex-bg-orb`.

DEUX MANQUES, PAS UN, et j'ai trouve le second en lisant la feuille plutot
   qu'en tatonnant. Le DOM n'avait pas les quatre orbes : ajoutees. Mais elles
   sont restees invisibles, hautes de zero pixel, parce que `fond-retenu.css` ne
   retouche que leur COULEUR : leur taille, leur position, leur flou et leur
   animation viennent de `background.css`, que la demo ne chargeait pas.

   Une feuille de reprise suppose la feuille d'origine. C'est vrai de toutes
   celles du banc, et c'est ce qui explique que la demo n'ait rien herite : elle
   chargeait six feuilles quand les autres pages en chargent dix.

   L'ecran d'accueil devient transparent pour laisser voir le fond ; il gardait
   sa couleur, il n'en a plus besoin.
   ------------------------------------------------------------------------- */

body.diagnostic-player-page .diagnostic-overlay {
    background: none;
}

/* --- D4 bis. TROIS SURFACES NOIRES COUVRAIENT ENCORE LE FOND --------------
   -------------------------------------------------------------------------
   Le fond du produit etait bien pose, et l'ecran restait noir. La cause n'etait
   pas dans la cascade mais dans la PILE : `.diagnostic-screen` peint
   `var(--black)`, le `<canvas>` peint `var(--black)`, et tous deux sont dans
   `.diagnostic-app`, au-dessus de la couleur que celui-ci porte. Le fond
   existait, sous deux couches de noir pur.

   Ce que l'on voyait alors n'etait donc pas « pas de fond » mais les quatre
   orbes seules, posees sur du noir au lieu du bleu nuit : `.mimex-bg` a cesse
   de peindre sa couleur le 23/08, comptant desormais sur celle de `body`, et
   `body` est ici recouvert.

   ELLES SE TAISENT TANT QUE LA PROCEDURE N'A PAS COMMENCE, et reprennent leur
   noir des qu'elle commence : sur les images, un fond bleute fausserait la
   perception des couleurs, qui est tout l'objet de la mesure. `procedure-lancee`
   est posee par les deux scripts au moment ou l'accueil disparait.
   ------------------------------------------------------------------------- */

body.diagnostic-player-page:not(.procedure-lancee) .diagnostic-screen, body.diagnostic-player-page:not(.procedure-lancee) #diagCanvas {
    background: none;
}

/* --- D7. L'appui de la main rend l'anneau a la demonstration -------------
   -------------------------------------------------------------------------
   L'anneau blanc disait deux choses a la fois : « la demonstration designe ce
   bouton » et « votre doigt appuie ici ». Une seule marque pour deux emetteurs
   se lit mal au moment ou la demonstration joue devant quelqu'un qui appuie
   aussi. L'anneau reste au pointeur ; l'appui de la main garde le reste.

   CE QUI RESTE SUFFIT A LE VOIR : le bouton rentre de 4 % (`scale(.96)`),
   s'eclaircit d'un quart et se sature, et son interieur se creuse par le haut.
   Trois marques, dont deux geometriques, sur un objet qu'on regarde au moment
   ou on le pousse.

   CE QU'ON PERD, ET C'ETAIT ECRIT. `lk.css` avait choisi cet anneau parce
   qu'il DEBORDE du doigt : au tactile, il reste visible quand le bouton ne
   l'est plus. On accepte la perte parce que ces boutons se poussent en rafale
   et qu'on en voit le resultat sur l'image, pas sur le bouton.
   ------------------------------------------------------------------------- */

body.diagnostic-player-page .diagnostic-app .diagnostic-controls-bar .diagnostic-btn.is-appuye:not(:disabled), body.diagnostic-player-page .diagnostic-app .diagnostic-controls-bar .diagnostic-btn:active:not(:disabled) {
    box-shadow: inset 0 3px 12px rgba(var(--black-rgb), 0.4);
}

/* --- L'appui vaut aussi sans le script -----------------------------------
   -------------------------------------------------------------------------
   `is-appuye` est pose par le script sur `pointerdown`, et c'est la bonne
   facon de le faire : au doigt, `:active` arrive tard, dure peu, et certains
   navigateurs ne l'appliquent pas. Mais cela veut dire qu'un bouton sans
   script ne s'enfonce pas du tout, ce qui s'est vu sur la maquette : les
   douze touches de couleur ne repondaient a rien.

   `:active` vient donc EN PLUS, jamais a la place. Les deux posent le meme
   rendu ; celui des deux qui arrive en premier gagne, et le resultat est le
   meme. On y gagne un produit ou l'appui a la souris se voit meme si le
   script n'a pas encore pris la main.
   ------------------------------------------------------------------------- */

body.diagnostic-player-page .diagnostic-controls-grid .diagnostic-btn:active:not(:disabled) {
    transition: none;
    transform: scale(0.96);
    filter: brightness(1.25) saturate(1.15);
}

/* --- D8. A2 : la barre se lit en deux temps ------------------------------
   -------------------------------------------------------------------------
   Six colonnes de canaux, une colonne de commandes, rien entre les deux : les
   quatorze boutons se lisaient comme une seule serie, et le vert de Valider
   tombait a deux colonnes du vert de `V+`.

   UNE PISTE, PAS UN ELEMENT. Premiere version au banc : un `<span>` glisse
   entre les deux groupes. La barre est une GRILLE remplie colonne par colonne
   et les deux conteneurs intermediaires sont en `display: contents` ; le span
   y a pris une cellule, et Quitter comme Valider ont glisse d'un cran. Meme
   piege que les coupons de l'administration, ou l'ordre ne connaissait pas
   les groupes.

   Le filet est donc une PISTE de 1 px, occupee par le `::after` de la barre,
   place a la main sur la septieme colonne pour ne pas se retrouver en fin de
   flux. Les deux gouttieres de 6 px de part et d'autre font l'air. Aucun
   ajout au corps de la page.
   ------------------------------------------------------------------------- */

body.diagnostic-player-page .diagnostic-app .diagnostic-controls-bar {
    grid-template-columns: repeat(6, minmax(0, 1fr)) 1px 1.25fr;
}

body.diagnostic-player-page .diagnostic-app .diagnostic-controls-bar::after {
    content: "";
    grid-column: 7;
    grid-row: 1 / -1;
    background: rgba(var(--white-rgb), 0.10);
}

/* --- D9. B3 : le rappel n'est plus un objet ------------------------------
   -------------------------------------------------------------------------
   « Test d'entrainement » etait une pastille : noir a 45 %, filet blanc, flou
   de verre. Une surface, un cadre et une matiere pour un texte qui ne se
   clique pas, juste a cote d'un bouton qui, lui, se clique.

   Il passe en capitales espacees a 54 %, sans surface. Un rappel n'est pas un
   objet de l'interface : c'est une legende.

   Le bouton d'action garde sa pastille, et l'ecart entre les deux devient la
   difference entre ce qui se lit et ce qui se clique.
   ------------------------------------------------------------------------- */

body.diagnostic-player-page .diagnostic-pill:not(.diagnostic-pill--action) {
    background: none;
    border: 0;
    padding: 8px 2px;
    backdrop-filter: none;

    /* S2, RETENU LE 26/08 CONTRE TROIS AUTRES. Le rappel est pose SUR l'image
       du test, dont la couleur change a chaque etape : aucune valeur fixe de
       blanc n'est lisible sur toutes, et a 0,54 il disparaissait sur les
       images claires.

       L'OMBRE NE DEPEND PAS DE CE QU IL Y A DESSOUS, et c'est tout son
       interet : un lisere sombre colle aux lettres, plus un halo large qui
       eteint le fond autour. Le contraste est fabrique par le texte lui-meme.

       Les deux autres pistes tombent pour ce qu'elles ajoutent : le negatif
       (`mix-blend-mode: difference`) reste lisible mais CHANGE DE COULEUR avec
       l'image, ce qui attire l'oeil sur ce qui doit rester un repere ; une
       petite surface floutee ajoute une surface de plus a un ecran qui n'en
       veut aucune. */
    color: rgba(var(--white-rgb), 0.96);
    text-shadow: 0 1px 2px rgba(0, 0, 0, 0.9), 0 0 10px rgba(0, 0, 0, 0.7);
    font-size: 11.5px;
    font-weight: var(--graisse-forte);
    text-transform: uppercase;
    letter-spacing: 0.09em;
}

/* --- D10. D3 : quinze segments, un par etape -----------------------------
   -------------------------------------------------------------------------
   Le compteur « Etape 6 / 15 » etait seul : quinze etapes defilaient sans
   qu'on voie ce qui restait. Un segment par etape le dit sans qu'on ait a
   lire, et se compte quand on veut le savoir exactement.

   LE COMPTEUR CHANGE DE COULEUR EN MEME TEMPS. Il etait en `#a5b4fc`, un
   bleu-violet clair, dernier survivant de la famille que la reprise retire
   partout ailleurs. Il devient une etiquette comme les autres, blanc a 54 %.
   La couleur passe aux segments, qui sont ce qu'on regarde.
   ------------------------------------------------------------------------- */

body.diagnostic-demo-page .diagnostic-demo-card__jauge {
    display: grid;
    grid-template-columns: repeat(15, 1fr);
    gap: 3px;
    margin: 0 0 10px;
}

body.diagnostic-demo-page .diagnostic-demo-card__jauge span {
    height: 3px;
    border-radius: 999px;

    /* Clair et non creuse : la carte est deja une surface translucide posee
       sur l'image, et le creux du socle, `rgba(0,0,0,0.22)`, s'y perd
       entierement. Mesure au rendu : aucune piste visible. */
    background: rgba(var(--white-rgb), 0.12);
}

body.diagnostic-demo-page .diagnostic-demo-card__jauge span.est-fait {
    background: rgb(var(--portal-accent-rgb));
}

/* Les deux traits internes descendent a l'echelle des filets. */

.comparaison-entete, .comparaison-pied {
    border-color: var(--filet-large);
}


/* --- L ECRAN DE FIN : L IMAGE SE RETIRE, LE BOUTON DE COMPARAISON EXISTE --
   -------------------------------------------------------------------------
   DEUX CHOSES SE REGLENT ICI, et la premiere n'etait pas un choix.

   L IMAGE. L'ecran de fin est transparent expres, pour laisser voir le fond du
   produit. Pendant le test ce fond est masque, a raison : une teinte mouvante
   par-dessus les images fausserait la perception des couleurs. Personne ne le
   rendait a la fin, et c'est donc la derniere image du test qui restait en
   pleine page derriere le titre. Le fond revient (voir `background.css`), et
   l'image se retire.

   `opacity` plutot que `display: none` : la toile garde ses dimensions, et la
   fenetre de comparaison, qui se pose PAR-DESSUS, n'a pas a etre recalculee.
   Elle est de toute facon autonome et ne lit pas la toile.

   LE BOUTON. « Voir la comparaison » portait `btn-secondary`, une classe que
   les regles de cet ecran excluent explicitement : il ne recevait donc aucun
   style et sortait en texte nu a cote de deux vrais boutons. On lui donne le
   sien, en contour plutot qu'en aplat : c'est une curiosite, pas une action du
   parcours, et sa forme doit le dire.
   ------------------------------------------------------------------------- */

body.diagnostic-player-page.procedure-terminee #diagCanvas {
    opacity: 0;
    pointer-events: none;
}

/* Et le noir de la procedure s'en va avec elle. Sans cette ligne, les orbes se
   poseraient sur du noir pur au lieu du bleu nuit du produit : c'est le meme
   defaut que celui deja corrige plus haut pour l'ecran d'accueil, a la seule
   difference que la classe qui le declenche est l'inverse. */
body.diagnostic-player-page.procedure-terminee .diagnostic-screen,
body.diagnostic-player-page.procedure-terminee #diagCanvas {
    background: none;
}

.diagnostic-app .btn.diagnostic-btn-contour {
    background: transparent;
    background-image: none;
    border: var(--filet-epaisseur) solid rgba(var(--portal-accent-rgb), 0.45);
    color: rgba(var(--white-rgb), 0.92);
    font-weight: var(--graisse-moyenne);
}

.diagnostic-app .btn.diagnostic-btn-contour:hover {
    background: rgba(var(--portal-accent-rgb), 0.14);
    border-color: rgba(var(--portal-accent-rgb), 0.68);
    color: rgba(var(--white-rgb), 0.98);
}

/* -------------------------------------------------------------------------
   LA PAGE D'ERREUR
   -------------------------------------------------------------------------
   Volontairement maigre : elle doit s'afficher quand le reste ne s'affiche
   pas. Trois feuilles, aucun script, aucune session.

   LE CODE DU STATUT EST UNE MENTION, PAS UN TITRE. « 404 » en gros chiffres
   nomme la panne dans NOTRE langue ; ce que la personne doit lire en premier,
   c'est la phrase qui dit ce qui s'est passe.
   ------------------------------------------------------------------------- */

.erreur {
    min-height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px;
}

.erreur__carte {
    max-width: 560px;
    padding: 34px 32px 30px;
    border-radius: var(--rayon-carte, 18px);
    background: rgba(var(--white-rgb), 0.04);
    border: var(--filet-epaisseur) solid rgba(var(--white-rgb), 0.10);
    text-align: center;
}

.erreur__code {
    margin: 0 0 10px;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: .14em;
    color: rgba(var(--white-rgb), 0.38);
}

.erreur__titre {
    margin: 0 0 12px;
    font-size: 26px;
    line-height: 1.25;
    text-wrap: balance;
    color: rgba(var(--white-rgb), 0.94);
}

.erreur__texte {
    margin: 0 0 26px;
    font-size: 15px;
    line-height: 1.55;
    color: rgba(var(--white-rgb), 0.68);
}

.erreur .btn {
    display: inline-flex;
}


/* --- Les deux boutons de fin de test ---------------------------------------
   -------------------------------------------------------------------------
   Meme habillage que les boutons de la boutique, celui qui plait : un fond
   tres transparent, un filet plus marque, le libelle en blanc. Aucun aplat.

   L'ECRAN DU TEST VIT HORS DE `.lk-content`, donc les regles qui donnent aux
   boutons du produit leur fond et leur survol ne l'atteignent pas. Il faut
   donc les reposer ici, et non compter dessus.

   Le tableau de bord prend le cyan, la comparaison reste neutre : l'un est ce
   qu'on veut faire ensuite, l'autre un detour facultatif. */
body.diagnostic-player-page .diagnostic-overlay .btn.btn-primary {
    background: rgba(var(--portal-accent-rgb), 0.16);
    border: 1px solid rgba(var(--portal-accent-rgb), 0.42);
    box-shadow: none;
    color: rgba(var(--white-rgb), 0.96);
    font-weight: 600;
}

body.diagnostic-player-page .diagnostic-overlay .btn.btn-primary:hover {
    background: rgba(var(--portal-accent-rgb), 0.26);
    border-color: rgba(var(--portal-accent-rgb), 0.62);
    color: rgba(var(--white-rgb), 0.98);
}

body.diagnostic-player-page .diagnostic-overlay .btn.diagnostic-btn-contour {
    background: rgba(var(--white-rgb), 0.06);
    border: 1px solid rgba(var(--white-rgb), 0.18);
    box-shadow: none;
    color: rgba(var(--white-rgb), 0.96);
    font-weight: 600;
}

body.diagnostic-player-page .diagnostic-overlay .btn.diagnostic-btn-contour:hover {
    background: rgba(var(--white-rgb), 0.12);
    border-color: rgba(var(--white-rgb), 0.30);
    color: rgba(var(--white-rgb), 0.98);
}

/* LA RESERVE SOUS LE SOLDE, et seulement quand elle n'est pas nulle.
   Elle avait une colonne a elle, toujours a zero : une colonne permanente pour
   une valeur transitoire coute une colonne sur chaque ligne et apprend a ne
   plus la regarder. Quand elle n'est pas nulle, elle signale un envoi reste en
   l'air, et c'est la qu'elle merite d'etre vue. */
.processing-soldes__reserve {
    margin-top: 3px;
    font-size: 12px;
    color: rgb(var(--amber-rgb));
    white-space: nowrap;
}

/* LE TEXTE SE CENTRE DANS UN CHAMP DE HAUTEUR FIXE. Les 42 px imposes plus
   haut alignent la ligne de commande, mais le remplissage vertical herite
   d'ailleurs poussait le texte vers le haut : le champ paraissait rogne, avec
   un vide sous le libelle. On pose donc le remplissage ici, horizontal
   seulement, et la hauteur fait le centrage. */
.processing-soldes__form input[type="number"],
.processing-soldes__form input[type="text"] {
    padding-top: 0;
    padding-bottom: 0;
    padding-left: 12px;
    padding-right: 12px;
    line-height: 1.2;
}

/* =========================================================================
   UN CHAMP QUI N EST PAS COLLE A SON BOUTON GARDE SON BORD
   =========================================================================
   Une regle generale de cette feuille soude tout champ suivi d'un bouton :

       .lk-body input:has(+ button), .lk-body input:has(+ .btn) { ... }

   Elle lui retire son bord droit et n'arrondit que sa gauche, pour que les deux
   ne fassent qu'une piece. C'est juste QUAND ILS SE TOUCHENT.

   ELLE SUPPOSE QU ILS SE TOUCHENT, et le CSS ne sait pas verifier cette
   supposition : `:has(+ .btn)` voit la SUCCESSION, pas l'ESPACE. Dans un
   conteneur qui espace ses enfants, le champ s'arrete donc a vif quelques
   pixels avant le bouton, sans bord et avec un coin carre. Il parait coupe.

   LE PIEGE SE REPETE, et c'est pour ca que cette classe existe plutot qu'une
   correction par endroit : il a ete paye deux fois en une heure, sur la ligne
   d'ajustement d'un solde puis sur la case du cout du tableau de bord, ecrite
   apres avoir diagnostique la premiere. La signature est toujours la meme :
   bord droit absent, coin carre, un ecart avant le bouton.

   A POSER SUR LE CONTENEUR, pas sur le champ : c'est le conteneur qui decide
   d'espacer ses enfants, donc c'est lui qui sait que rien n'est colle.
   ========================================================================= */
.champs-espaces input:has(+ .btn),
.champs-espaces input:has(+ button),
.champs-espaces input:has(+ .bouton-programme) {
    border-right: var(--filet-epaisseur) solid var(--filet-large);
    border-top-right-radius: 12px;
    border-bottom-right-radius: 12px;
}

/* =========================================================================
   L EXPLICATION D UN ELEMENT : l infobulle au survol, la fiche au clic.

   ELLE REPREND LA GRAMMAIRE DE LA FENETRE DE COMPARAISON -- meme voile, meme
   rayon, meme filet, meme 10050 -- parce que c'est la fenetre que le produit
   ouvre deja au milieu de l'ecran. Deux fenetres centrales qui ne se
   ressemblent pas donnent l'impression de deux produits.
   ========================================================================= */

/* --- Ce qu on peut interroger --------------------------------------------
   L element porte une explication : il doit le DIRE avant qu'on le survole,
   sinon personne ne pense a cliquer. Un souligne pointille sous le libelle
   suffit, et il ne pousse rien : `text-decoration` ne prend pas de place. */
[data-interp] {
    cursor: pointer;
}

/* AUCUN TRAIT SOUS LE LIBELLE. Le curseur main dit deja que l'element repond,
   et le survol le montre en clair. Un souligne sous chacun des cinquante-six
   noms d'une grille ajoutait une ligne graphique de plus dans une page qui en
   a deja beaucoup : il balisait au lieu d'indiquer.

   L accent au survol reste : il repond au geste, il ne l'anticipe pas. */
/* LE NOM PEUT ETRE L ELEMENT LUI-MEME. Sur les Interconnexions il n y a
   pas de libelle enfant a colorer : le nom de l organe porte l attribut.
   Sans ces deux selecteurs le curseur devenait une main et rien ne
   bougeait -- une invitation sans reponse. */
[data-interp] .quality-card__label,
[data-interp] .psy-trait__nom,
.connections-list__name[data-interp],
.connections-general[data-interp] {
    transition: color 0.15s ease;
}

[data-interp]:hover .quality-card__label,
[data-interp]:focus-visible .quality-card__label,
[data-interp]:hover .psy-trait__nom,
[data-interp]:focus-visible .psy-trait__nom,
.connections-list__name[data-interp]:hover,
.connections-list__name[data-interp]:focus-visible,
.connections-general[data-interp]:hover,
.connections-general[data-interp]:focus-visible {
    color: var(--ink-50);
}

[data-interp]:focus-visible {
    outline: 2px solid rgb(var(--portal-accent-rgb));
    outline-offset: 3px;
    border-radius: 8px;
}

/* --- L infobulle ---------------------------------------------------------
   ELLE EST POSEE EN COORDONNEES DE FENETRE (`fixed`), et le JavaScript la
   place. Une bulle en `absolute` dans une case suivrait les transformations
   et les debordements de sa colonne : sur les blocs couches, elle se serait
   retrouvee tournee avec eux. */
.interp-bulle {
    position: fixed;
    z-index: 10060;
    left: 0;
    top: 0;

    width: min(340px, calc(100vw - 24px));
    padding: 11px 13px;
    box-sizing: border-box;

    background: rgb(var(--mimex-bg-rgb, 7 17 31));
    border: 1px solid rgba(var(--white-rgb), 0.14);
    border-radius: 12px;
    box-shadow: 0 16px 40px rgba(0, 0, 0, 0.55);

    /* Elle explique ce qu'on survole : elle ne doit jamais intercepter la
       souris, sinon le survol s'interrompt des qu'elle apparait sous elle. */
    pointer-events: none;
}

.interp-bulle[hidden] {
    display: none;
}

.interp-bulle__nom {
    display: block;
    font-size: 13.5px;
    font-weight: 700;
    color: var(--ink-50);
}

/* LA DYNAMIQUE SITUE D UN COUP D OEIL : « Amour — Ouverture / Fermeture » dit
   la polarite avant meme qu'on lise la phrase. */
.interp-bulle__dyn {
    display: block;
    margin-top: 2px;
    font-size: 11.5px;
    color: rgba(var(--white-rgb), var(--voile-mention, 0.58));
}

.interp-bulle__phrase {
    display: block;
    margin-top: 7px;
    font-size: 12.5px;
    line-height: 1.5;
    color: rgba(var(--white-rgb), 0.82);
}

/* --- La fenetre ---------------------------------------------------------- */
.interp-fenetre {
    position: fixed;
    inset: 0;

    /* 10050, comme la fenetre de comparaison : `etat-systeme__panneau` occupe
       deja 10001, et la barre du haut est collante. */
    z-index: 10050;

    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px;

    background: rgba(0, 0, 0, 0.76);
    backdrop-filter: blur(6px);
}

.interp-fenetre[hidden],
.interp-fenetre.is-hidden {
    display: none;
}

body.interp-ouverte {
    overflow: hidden;
}

.interp-fenetre__voile {
    position: absolute;
    inset: 0;
}

.interp-fenetre__boite {
    position: relative;
    display: flex;
    flex-direction: column;

    /* PLUS LARGE QUE LA FENETRE DE COMPARAISON, parce qu'on y lit du texte
       suivi et non deux vignettes cote a cote. 62 caracteres par ligne est la
       largeur de lecture confortable ; au-dela, l'oeil perd sa ligne. */
    width: min(760px, 100%);

    /* LA HAUTEUR VISIBLE N EST PAS LA HAUTEUR DE MISE EN PAGE SUR UN
       TELEPHONE. Une couche `fixed` s'etale sur le viewport de mise en page,
       qui vaut l'ecran BARRE D ADRESSE MASQUEE. Tant qu'elle est affichee, le
       bas de la fenetre passe dessous : le dernier bloc et la fin du
       defilement sont hors de vue sans que rien ne le montre.

       `dvh` suit la hauteur reellement visible. La ligne en `vh` reste pour
       les navigateurs qui ne la connaissent pas ; celle du dessous gagne
       partout ailleurs. */
    max-height: min(86vh, 100%);
    max-height: min(86dvh, 100%);

    background: linear-gradient(135deg,
            rgba(var(--portal-accent-rgb), 0.05),
            rgba(var(--portal-accent-rgb), 0) 60%),
        rgb(var(--mimex-bg-rgb, 7 17 31));
    color: rgba(var(--white-rgb), 0.92);
    border: var(--filet-epaisseur) solid var(--filet-petit);
    border-radius: 18px;
    box-shadow: 0 28px 70px rgba(0, 0, 0, 0.55);
    overflow: hidden;
}

.interp-fenetre__fermer {
    position: absolute;
    top: 12px;
    right: 12px;
    z-index: 2;

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

    font-size: 20px;
    line-height: 1;
    color: rgba(var(--white-rgb), 0.72);
    background: rgba(var(--white-rgb), 0.06);
    border: 1px solid rgba(var(--white-rgb), 0.12);
    border-radius: 9px;
    cursor: pointer;
}

.interp-fenetre__fermer:hover {
    color: var(--ink-50);
    background: rgba(var(--white-rgb), 0.11);
}

.interp-fenetre__fermer:focus-visible {
    outline: 2px solid rgb(var(--portal-accent-rgb));
    outline-offset: 2px;
}

.interp-fenetre__corps {
    padding: 28px 32px 34px;
    overflow-y: auto;
    overscroll-behavior: contain;
}

.interp-fenetre__attente,
.interp-fenetre__echec {
    margin: 0;
    padding: 26px 0;
    text-align: center;
    font-size: 13px;
    color: rgba(var(--white-rgb), var(--voile-mention, 0.55));
}

/* --- La fiche ------------------------------------------------------------ */
.interp__tete {
    padding-right: 40px;
    margin-bottom: 20px;
}

.interp__ref {
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: rgb(var(--accent-texte-rgb, var(--portal-accent-rgb)));
}

.interp__nom {
    margin: 3px 0 0;
    font-size: 22px;
    font-weight: 700;
    color: var(--ink-50);
}

.interp__dyn {
    margin-top: 3px;
    font-size: 13.5px;
    color: rgba(var(--white-rgb), var(--voile-mention, 0.62));
}

.interp__groupe {
    margin-top: 9px;
    font-size: 11px;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: rgba(var(--white-rgb), 0.38);
}

.interp p {
    margin: 0 0 12px;
    font-size: 14px;
    line-height: 1.68;
    color: rgba(var(--white-rgb), 0.84);
}

.interp p:last-child {
    margin-bottom: 0;
}

.interp__def {
    font-size: 14.5px !important;
    color: rgba(var(--white-rgb), 0.90) !important;
}

/* --- Comment le lire -----------------------------------------------------
   LE BLOC DE LECTURE EST LE COEUR DE LA FICHE : c'est la question qu'on se
   pose devant une barre blanche et une barre noire. Il est donc mis en avant,
   et non range parmi les autres blocs.

   SA PORTEE SE VOIT A SA COULEUR autant qu'a son libelle : ce qui vaut pour
   l'element precis est accentue, ce qui vaut pour toute une ligne ou tout un
   tableau est plus sobre. Faire passer une regle de tableau pour la lecture
   d'un element serait plus grave que de ne rien dire. */
.interp__lecture {
    margin: 20px 0 0;
    padding: 17px 19px;

    background: rgba(var(--slate-mid-rgb), 0.55);
    border: var(--filet-epaisseur) solid var(--filet-large);
    border-radius: 12px;
}

.interp__lecture--element {
    border-color: var(--filet-petit);
}

.interp__lecture-titre {
    margin: 0 0 11px;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.10em;
    text-transform: uppercase;
    color: rgba(var(--white-rgb), var(--voile-mention, 0.62));
}

.interp__lecture--element .interp__lecture-titre {
    color: rgb(var(--accent-texte-rgb, var(--portal-accent-rgb)));
}

.interp__portee {
    margin-left: 8px;
    font-weight: 500;
    letter-spacing: 0.04em;
    text-transform: none;
    color: rgba(var(--white-rgb), 0.44);
}

.interp__suite {
    color: rgba(var(--white-rgb), 0.74) !important;
}

/* --- Les blocs de la fiche -----------------------------------------------
   LA COULEUR VIENT DU TYPE DE BLOC ET DE RIEN D AUTRE : une ressource est
   verte et une perturbation rouge dans toutes les fiches. Une teinte choisie
   a l'affichage ferait dire deux choses differentes au meme vert. */
.interp__bloc {
    margin-top: 18px;
    padding: 17px 19px;

    background: rgba(var(--slate-mid-rgb), 0.55);
    border: var(--filet-epaisseur) solid var(--filet-large);
    border-radius: 12px;
}

.interp__bloc-titre {
    margin: 0 0 10px;
    font-size: 12.5px;
    font-weight: 700;
    letter-spacing: 0.03em;
    color: var(--ink-50);
}

/* LA COULEUR DIT LE TYPE DU BLOC, et rien d'autre. Une ressource est verte et
   une perturbation rouge dans toutes les fiches : c'est ce qui permet de
   balayer une fiche sans la lire. Elle tient au filet et au titre, pas a un
   fond -- un fond colore par bloc ferait une fiche en drapeau. */
.interp__bloc--ressource {
    border-color: rgba(74, 222, 128, 0.30);
}

.interp__bloc--ressource .interp__bloc-titre {
    color: rgb(134, 239, 172);
}

.interp__bloc--perturbation {
    border-color: rgba(248, 113, 113, 0.30);
}

.interp__bloc--perturbation .interp__bloc-titre {
    color: rgb(252, 165, 165);
}

.interp__bloc--emotion {
    border-color: rgba(var(--portal-accent-rgb), 0.30);
}

.interp__bloc--emotion .interp__bloc-titre {
    color: rgb(var(--accent-texte-rgb, var(--portal-accent-rgb)));
}

.interp__bloc--organe {
    border-color: rgba(251, 191, 36, 0.28);
}

.interp__bloc--organe .interp__bloc-titre {
    color: rgb(253, 224, 71);
}

/* LE VOLET : un repere que le manuel traite en DEUX fiches.

   L hypophyse en est le seul cas. Les deux fiches se lisent a la suite, et
   elles portent les MEMES huit titres de bloc : sans un separateur qui nomme
   le volet, on ne sait plus laquelle on lit des le troisieme bloc.

   IL NE RESSEMBLE PAS AUX AUTRES BLOCS, et c est voulu : il n est pas un
   contenu de plus, il est une coupure. Pas de cadre, un trait plein au-dessus,
   et le nom du volet a la taille d un titre. */
.interp__bloc--volet {
    margin-top: 30px;
    padding: 18px 0 0;
    background: none;
    border: 0;
    border-top: 2px solid rgba(var(--portal-accent-rgb), 0.45);
    border-radius: 0;
}

.interp__bloc--volet .interp__bloc-titre {
    margin-bottom: 4px;
    font-size: 18px;
    letter-spacing: 0;
    color: rgba(var(--white-rgb), 0.95);
}

.interp__bloc--volet p {
    margin: 0;
    color: var(--ink-50);
    font-size: 13px;
    letter-spacing: 0.02em;
}

/* Le premier volet ouvre la fiche : il n a rien a couper au-dessus de lui. */
.interp__bloc--volet:first-of-type {
    margin-top: 14px;
    padding-top: 0;
    border-top: 0;
}

/* --- A l etroit -----------------------------------------------------------

   SOUS 640, LA FENETRE PREND TOUTE LA LARGEUR : une boite centree de 760 px
   sur un telephone laisserait deux bandes de voile et un texte a 60 % de la
   place.

   MAIS ELLE NE PREND PAS TOUTE LA HAUTEUR, et c'est ce que la premiere version
   faisait. Avec `height: 100%`, la fiche d'« Union » -- trois lignes -- occupait
   l'ecran entier : une page presque vide, qui se lit comme un defaut de
   chargement. Le contenu decide desormais de la hauteur, et le plafond
   n'intervient que pour les fiches longues, qui defilent alors comme avant. */
@media (max-width: 640px) {
    .interp-fenetre {
        padding: 12px;
    }

    .interp-fenetre__boite {
        width: 100%;
        height: auto;
        max-height: calc(100% - 24px);
        max-height: calc(100dvh - 24px);
    }

    .interp-fenetre__corps {
        padding: 22px 18px 26px;
    }

    .interp__nom {
        font-size: 19px;
    }
}

/* LE REPLI DU BLOC DE LECTURE A ETE RETIRE avec ce qu'il repliait : la regle
   generale d'un tableau n'est plus dans la fiche, elle est dans l'aide. Ne
   reste ici que ce qui est propre a l'element ou a sa ligne, et cela n'a
   jamais eu besoin d'etre range. */


/* LE RECAPITULATIF D UNE COMMANDE VIDE.

   Il montre les memes cases que celui d'une commande en cours, a zero. Il ne
   doit pas pour autant se donner l'air d'un resultat : la teinte verte du
   « tout va bien » ne veut rien dire tant qu'il n'y a rien a compter, et le
   bandeau se lit alors plus fort que l'invitation posee juste dessous. */
.therapy-draft-head--vide { margin-bottom: 12px; }

.therapy-draft-head--vide .therapy-order-summary {
    border-color: var(--filet-large);
    background: rgba(var(--white-rgb), 0.035);
    color: rgba(var(--white-rgb), var(--voile-mention, 0.55));
}

.therapy-draft-head--vide .therapy-order-summary strong {
    color: rgba(var(--white-rgb), 0.72);
}


/* =========================================================================
   LE MODE GUIDE : l'aide de lecture sous le titre de chaque bloc
   =========================================================================

   DEUX MODES, UN REGLAGE DU PROFIL. `data-mode="guide"` sur `<html>` montre
   sous chaque titre de bloc trois phrases : ce que le bloc montre, comment on
   le lit avec son echelle, ce qu'on en fait. En expert, les blocs restent nus.

   L ABSENCE D ATTRIBUT MASQUE. Le bandeau est dans le balisage des deux
   cotes ; c'est la feuille qui decide. Une page servie sans son attribut rend
   donc ce qu'elle rendait avant le mode guide, et non des blocs amputes.

   IL SE RANGE SOUS LE FILET DU TITRE, pas contre la donnee : plus d'air en
   dessous qu'au-dessus, pour qu'il appartienne au titre qu'il explique. */
/* L'AFFICHAGE EST DANS `mode-guide.css`, pas ici : c'est elle qui decide ce
   que l'interrupteur allume, et depuis le 18/09/2026 il n'allume plus que le
   COMMENTAIRE. Le rapport, lui, fait partie de la page. Garder un
   `display: none` ici en doublon le ferait disparaitre au premier changement
   de poids entre les deux feuilles. */
.guide-bandeau {
    margin: 10px 0 16px;
    padding: 0 0 0 12px;
    border-left: 2px solid rgba(var(--portal-accent-rgb), 0.45);

    max-width: 96ch;
    color: var(--texte-discret);
    font-size: 13px;
    line-height: 1.55;
}

/* UN SEUL FILET, POUR LES DEUX VOIX. Une barre par voix en faisait deux,
   paralleles et a dix pixels l'une de l'autre : le regard y lisait deux blocs
   sans rapport, la ou il y a un seul commentaire en deux registres. Ce sont les
   pastilles qui separent les voix, pas des filets. */
.guide-bandeau__voix {
    display: block;
}

/*
   LES DEUX VOIX : COTE A COTE QUAND LA PLACE EXISTE, EMPILEES SINON.

   Le bouton « Partager » est sur chaque bloc : au moment ou le praticien decide
   de pousser ce bloc a l'ecran du client, ce qu'il a besoin de savoir c'est ce
   que le client va y lire.

   CE QUI A CHANGE, ET POURQUOI. Elles etaient empilees partout, parce qu'en
   deux colonnes les cartes de demi-largeur tombaient a 260 px par voix. C'est
   vrai de ces cartes-la, et faux des blocs qui tiennent la ligne entiere :
   mesure du 18/09/2026, un bandeau de bloc large s'arretait a 686 px dans un
   conteneur de 1206, sur neuf lignes de haut, avec 520 px inutilises a cote.

   LE SEUIL N'EST PAS ECRIT, IL SE DEDUIT. `flex-wrap` avec une base de 30rem
   par voix : deux voix tiennent cote a cote a partir d'environ 1000 px, et
   passent l'une sous l'autre en dessous. Aucune largeur de bloc a maintenir,
   aucun point de rupture a tenir a jour, et une carte de demi-largeur retombe
   d'elle-meme sur l'empilement.

   LE PLAFOND DE LECTURE SE DEPLACE SUR LA VOIX. Le bandeau le portait
   (`max-width: 96ch`) et empechait justement les deux colonnes de s'ouvrir. Il
   passe a chaque voix, qui garde donc sa mesure de lecture : en deux colonnes
   chacune fait environ 590 px, soit 82 signes, exactement la meme lecture
   qu'avant sur la moitie de la hauteur.
*/
/* LE DUO EST DANS `public/base/mode-guide.css`, pas ici : cette feuille-la est
   chargee APRES lk.css et porte la regle qui allume le bandeau, avec sa classe
   redite. Ecrit ici, le duo ne s'ouvrait pas, et rien ne le signalait. */

/* Hors du duo, une seule voix : l'espacement reste un margin. */
.guide-bandeau:not(.guide-bandeau--duo) .guide-bandeau__voix + .guide-bandeau__voix {
    margin-top: 10px;
}

/* Sur deux voix, la pastille prend sa ligne : collee devant le texte, on ne
   voyait plus ou l'une finissait et ou l'autre commencait. */
.guide-bandeau--duo .guide-bandeau__texte {
    display: block;
    margin-top: 3px;
}

/*
   LA VOIX CLIENTE EST UNE REFERENCE, PAS UNE CONSIGNE.

   Meme taille et meme largeur que celle du praticien : c'est le meme niveau
   d'information. Mais une seule des deux lui est adressee, et il doit voir
   laquelle sans avoir a lire. D'ou un filet neutre et une encre un cran plus
   basse.

   PAS DE ROUGE, ET CE N'EST PAS UN GOUT. Dans cet ecran le rouge dit la charge :
   le « +3 » de l'age de ressource, la pastille de la tranche calculee, les
   cartes de zone, les processus non compenses. Une voix cliente en rouge en
   poserait sur tous les blocs, tout le temps, sans rapport avec le bilan.
*/
.guide-bandeau__commentaire {
    opacity: 0.82;
}

.guide-bandeau__commentaire .guide-bandeau__pour {
    background: rgba(148, 163, 184, 0.16);
}

/* Aucune version cliente ecrite : la colonne le DIT. Vide, elle se lirait comme
   un defaut d'affichage. */
.guide-bandeau__commentaire--sans .guide-bandeau__texte {
    font-style: italic;
}

/* LE NOM DU LECTEUR, quand deux versions coexistent : sur les pages partagees,
   le praticien et le client ne lisent pas la meme chose du meme bloc. */
.guide-bandeau__pour {
    display: inline-block;
    margin-right: 6px;
    padding: 1px 6px;
    border-radius: 999px;
    background: rgba(var(--portal-accent-rgb), 0.14);
    color: var(--texte-discret);
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    vertical-align: 1px;
}

/* =========================================================================
   LE BOUTON DE SUPPRESSION GARDE SON ENCRE
   =========================================================================

   `.btn-danger` ne posait qu'un fond rouge ; son encre venait donc de la regle
   generique des boutons de formulaire, qui vaut un bronze en theme clair.
   Mesure : 1,40 sur ce rouge plein, sur sept pages.

   AJOUTER `.btn-danger` AUX LISTES D EXCLUSION N A PAS SUFFI : la regle qui
   gagne enchaine `.lk-content .card form .btn` et pese plus qu'une classe
   nue. On pose donc la regle a une specificite qui tranche, en dernier.

   L ENCRE EST FIXE PARCE QUE L APLAT L EST. Ce rouge ne bascule pas d'un theme
   a l'autre ; son encre ne doit pas basculer non plus. Le blanc y rend 4,92.
   ========================================================================= */
.lk-body .btn.btn-danger,
.lk-content .btn.btn-danger,
.lk-content .card form .btn.btn-danger {
    color: #ffffff;
}
