﻿/* ==========================================================================
   Suivi RG Travel 2027 — feuille de style unique.
   Aucune dépendance externe : la CSP interdit tout chargement hors origine.
   ========================================================================== */

/* ==========================================================================
   1. JETONS
   --------------------------------------------------------------------------
   Une seule couche de définition, lue par tout le reste de la feuille. Les
   composants ne connaissent que des jetons *sémantiques* (--texte, --bord,
   --succes-fond) et jamais une rampe brute : c'est ce qui rendra un second
   thème possible sans y toucher (voir design-system/rgtsuivi/MASTER.md §8).

   Les anciens noms (--vert, --rouge, --ombre, --r-petit…) sont conservés en
   alias plus bas. Ils sont employés par près de quatre cents sélecteurs et
   une quinzaine d'écrans : les renommer d'un bloc aurait produit une refonte
   invérifiable, où une couleur oubliée ne se voit qu'à l'exécution.
   ========================================================================== */

:root {
    /* --- rampe neutre : tout gris de l'interface en descend ---------------- */
    --n-0:   #ffffff;
    --n-25:  #fbfcfd;
    --n-50:  #f7f8fa;
    --n-100: #eff1f5;
    --n-200: #e3e7ee;
    --n-300: #cfd5e0;
    --n-400: #a7b0c0;
    --n-500: #7d8798;
    /* Cran intermédiaire, ajouté pour une raison mesurée et non par symétrie : le
       texte tertiaire posé en `--n-500` sur blanc donne 3,63:1 — sous le seuil AA
       de 4,5. Il portait quatre choses qu'on lit vraiment (valeur secondaire de
       cellule, libellé d'indicateur, libellé d'horloge, texte atténué), donc pas
       de la décoration. `--n-500` reste tel quel : sur la barre latérale sombre,
       c'est le sens inverse — l'éclaircir dégraderait le contraste. */
    --n-550: #697285;
    --n-600: #5c6579;
    --n-700: #444d60;
    --n-800: #2a3243;
    --n-900: #182031;
    --n-950: #0d1424;

    /* --- marque : terre battue, recalée pour le contraste ------------------ */
    --marque-050: #fdf3ee;
    --marque-100: #f9e2d7;
    --marque-300: #e29a75;
    --marque-400: #d9764e;
    --marque-500: #b6552f;       /* fond de bouton primaire, texte blanc  4,6:1 */
    --marque-600: #a84d29;       /* texte et bordure sur blanc            4,8:1 */
    --marque-700: #8d3f21;

    /* --- états : fond / bordure / texte, trois tons par état --------------- */
    --succes-fond: #e8f6ee;
    --succes-bord: #b6ddc6;
    --succes-texte: #136c3f;
    --succes-vif: #1f8a52;

    --attention-fond: #fdf4e0;
    --attention-bord: #ecd39a;
    --attention-texte: #8a5600;
    --attention-vif: #b87d0a;

    --danger-fond: #fceceb;
    --danger-bord: #eeb9b5;
    --danger-texte: #a81f18;
    --danger-vif: #c62f26;

    --info-fond: #eaf1fb;
    --info-bord: #bed4ee;
    --info-texte: #17558f;
    --info-vif: #1e6bb8;

    --accent-fond: #f0edfb;
    --accent-bord: #d5cdf0;
    --accent-texte: #513795;

    /* --- surfaces et texte ------------------------------------------------- */
    --fond: var(--n-50);
    --surface: var(--n-0);
    --surface-alt: var(--n-25);
    --surface-creuse: var(--n-100);
    --texte: var(--n-900);
    --texte-doux: var(--n-600);
    /* `--texte-faible` est un jeton de surface CLAIRE : il vise 4,5:1 sur blanc.
       Les textes de la barre latérale sombre lisent la rampe directement
       (`--n-400`, `--n-500`), où la contrainte est inversée. */
    --texte-faible: var(--n-550);
    --bord: var(--n-200);
    --bord-fort: var(--n-300);
    --bord-discret: var(--n-100);

    /* --- typographie ------------------------------------------------------- */
    /* « Segoe UI Variable » est native sur Windows 11 / Server 2025 : elle donne
       le grain d'un Inter sans aucune requête réseau, ce que la CSP interdirait.
       Les paliers Display / Text sont ses tailles optiques — Display resserre
       l'interlettrage des grands titres, Text ouvre celui du corps. */
    --police: "Segoe UI Variable Text", "Segoe UI Variable", "Segoe UI", system-ui,
              Roboto, -apple-system, "Helvetica Neue", Arial, sans-serif;
    --police-affichage: "Segoe UI Variable Display", "Segoe UI Variable", "Segoe UI",
                        system-ui, Roboto, -apple-system, sans-serif;
    --police-mono: "Cascadia Mono", Consolas, "SF Mono", ui-monospace, monospace;

    --t-2xs: .6875rem;   /* 11 px — étiquettes de colonne, badges */
    --t-xs:  .75rem;     /* 12 px — notes, valeurs secondaires    */
    --t-sm:  .8125rem;   /* 13 px — corps dense, cellules         */
    --t-md:  .875rem;    /* 14 px — corps par défaut              */
    --t-lg:  1rem;       /* 16 px — sous-titres                   */
    --t-xl:  1.25rem;    /* 20 px — titres de section             */
    --t-2xl: 1.5625rem;  /* 25 px — titre de page                 */
    --t-3xl: 1.9375rem;  /* 31 px — valeur d'indicateur           */

    /* --- espacement : échelle 4 px, densité « dashboard » ------------------ */
    --e-1: 4px;   --e-2: 8px;   --e-3: 12px;  --e-4: 16px;
    --e-5: 20px;  --e-6: 24px;  --e-8: 32px;  --e-10: 40px;  --e-12: 48px;

    /* Trois paliers de rythme vertical, et trois seulement : dans un bloc,
       entre deux blocs, entre deux sections. */
    --rythme-serre: var(--e-3);
    --rythme: var(--e-6);
    --rythme-large: var(--e-8);

    /* --- rayons ------------------------------------------------------------ */
    --r-xs: 4px;
    --r-sm: 6px;
    --r: 8px;
    --r-md: 10px;
    --r-lg: 14px;
    --r-pilule: 999px;

    /* --- élévation : quatre crans, volontairement retenus ------------------ */
    --ombre-0: none;
    --ombre-1: 0 1px 2px rgba(13, 20, 36, .05), 0 1px 3px rgba(13, 20, 36, .04);
    --ombre-2: 0 2px 4px rgba(13, 20, 36, .06), 0 4px 12px rgba(13, 20, 36, .06);
    --ombre-3: 0 8px 16px rgba(13, 20, 36, .09), 0 16px 40px rgba(13, 20, 36, .14);

    /* --- mouvement : la sortie vaut 70 % de l'entrée ----------------------- */
    --duree-rapide: 120ms;
    --duree: 160ms;
    --duree-lente: 220ms;
    --ease-sortie: cubic-bezier(.22, .68, .36, 1);   /* ce qui entre  */
    --ease-entree: cubic-bezier(.4, 0, 1, 1);        /* ce qui sort   */

    /* --- focus : un seul anneau pour toute l'application ------------------- */
    --focus: #2563eb;
    --focus-halo: rgba(37, 99, 235, .22);
    --anneau: 0 0 0 2px var(--surface), 0 0 0 4px var(--focus-halo);

    /* --- carcasse ---------------------------------------------------------- */
    --largeur-laterale: 240px;
    /* Hauteur FIXE du bandeau, et non plus minimale : la barre d'onglets collante
       s'y positionne (`.onglets-files { top: var(--hauteur-bandeau) }`), donc un
       bandeau qui grandit la laisse flotter au mauvais endroit. */
    --hauteur-bandeau: 56px;

    /* ------------------------------------------------------------------ alias
       Anciens noms, redirigés vers les jetons ci-dessus. Ils restent le
       vocabulaire courant de la feuille ; ce qui a changé, c'est qu'ils
       pointent tous vers une rampe unique. */
    --terre: var(--marque-500);
    --terre-clair: var(--marque-400);
    --nuit: var(--n-950);
    --ardoise: var(--n-800);
    --vert: var(--succes-texte);
    --vert-clair: var(--succes-fond);
    --ambre: var(--attention-texte);
    --ambre-clair: var(--attention-fond);
    --rouge: var(--danger-texte);
    --rouge-clair: var(--danger-fond);
    --bleu: var(--info-texte);
    --bleu-clair: var(--info-fond);
    --violet: var(--accent-texte);
    --r-petit: var(--r-sm);
    --ombre: var(--ombre-1);
    --ombre-forte: var(--ombre-3);
}

*, *::before, *::after { box-sizing: border-box; }

html, body { margin: 0; padding: 0; height: 100%; }

/* La taille est posée sur `body` seul, jamais sur `html` aussi : une taille
   relative appliquée aux deux se compose — `.875rem` sur `html` puis sur `body`
   donnait 12,25 px de corps, et toute l'échelle `rem` se retrouvait calculée sur
   une racine de 14 px au lieu de 16. Mesuré au navigateur : chaque taille de la
   feuille sortait 12,5 % trop petite. */
body {
    font-family: var(--police);
    font-size: var(--t-md);
    line-height: 1.55;
    color: var(--texte);
    background: var(--fond);
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
    /* Les chiffres d'un écran de gestion vivent en colonnes : ils doivent tous
       avoir la même avance, partout, sans avoir à le redire par composant. */
    font-variant-numeric: tabular-nums;
}

/* La hiérarchie passe par la taille et la graisse, pas par la couleur
   (`visual-hierarchy`). Les grands titres prennent la taille optique Display. */
h1, h2, h3, h4 { margin: 0; font-weight: 650; letter-spacing: -.014em; line-height: 1.25; }
h1 { font-family: var(--police-affichage); font-size: var(--t-2xl); letter-spacing: -.022em; }
h2 { font-size: var(--t-xl); letter-spacing: -.017em; }
h3 { font-size: var(--t-md); }
h4 { font-size: var(--t-sm); }
p { margin: 0; }
code { font-family: var(--police-mono); font-size: .84em; }
a { color: var(--info-texte); text-decoration: none; }
a:hover { text-decoration: underline; }

/* Un titre reçoit le focus à chaque navigation (`FocusOnNavigate` dans
   Routes.razor) : c'est l'exigence WCAG `focus-on-route-change`, et elle est
   conservée. Mais Chromium dessine alors son anneau par défaut, et les quinze
   écrans de l'application affichaient leur titre encadré comme un champ de
   saisie. Le focus reste — c'est lui qui sert au lecteur d'écran ; seul son
   anneau part, parce qu'un titre n'est pas un contrôle. */
h1:focus, h2:focus, h3:focus, [role="tabpanel"]:focus { outline: none; }

/* Anneau de focus unique, posé une fois pour toute l'application. Remplace une
   douzaine de règles au cas par cas qui laissaient passer les champs
   d'autocomplétion et les cases à cocher des tableaux. */
:focus-visible {
    outline: 2px solid var(--focus);
    outline-offset: 2px;
    border-radius: var(--r-xs);
}

::selection { background: var(--marque-100); color: var(--marque-700); }

.muted { color: var(--texte-faible); }
.petit { font-size: var(--t-xs); }
.mono-petit { font-family: var(--police-mono); font-size: var(--t-xs); }
.num { text-align: right; font-variant-numeric: tabular-nums; }
.positif { color: var(--succes-texte); }
.negatif { color: var(--danger-texte); }

/* ------------------------------------------------------------------ carcasse */

/* `dvh` et non `vh` : sur un navigateur dont la barre d'outils se rétracte, `100vh` désigne
   la hauteur SANS la barre, donc la page dépasse de sa hauteur réelle (`viewport-units`). */
.appli { display: flex; min-height: 100dvh; }

.barre-laterale {
    width: var(--largeur-laterale);
    flex: 0 0 var(--largeur-laterale);
    /* Un aplat suffit : le dégradé d'avant se lisait comme un défaut de rendu sur
       les grands écrans, où la barre fait 1 000 px de haut. */
    background: var(--n-950);
    color: var(--n-300);
    display: flex;
    flex-direction: column;
    position: sticky;
    top: 0;
    height: 100dvh;
}

.zone-principale { flex: 1; min-width: 0; display: flex; flex-direction: column; }

/* La largeur de lecture est plafonnée : au-delà, un tableau de treize colonnes
   s'étire sans gagner en lisibilité et l'œil perd la ligne (`container-width`). */
.contenu {
    padding: var(--e-6) var(--e-8) var(--e-12);
    flex: 1; min-width: 0;
    width: 100%; max-width: 1680px; margin-inline: auto;
}

.layout-vide { min-height: 100vh; display: grid; place-items: center; }

/* ------------------------------------------------------------------- marque */

.marque { display: flex; gap: var(--e-3); align-items: center; }
.marque-compacte {
    padding: var(--e-4);
    border-bottom: 1px solid rgba(255, 255, 255, .07);
    min-height: var(--hauteur-bandeau);
}

/* Le logo remplace la pastille « RG » dessinée en dégradé. Trois précautions :
   - `flex: 0 0 40px` et non `width` seul, sinon le titre qui suit le comprime dès
     que la barre latérale se resserre — un logo écrasé se lit comme un défaut ;
   - `aspect-ratio` + `object-fit: contain` garantissent qu'il ne se déforme jamais,
     même si le fichier source changeait de proportions ;
   - 40 px et non 34 : la marque est un disque portant un texte circulaire, qui
     devient une tache verte en dessous. C'est la plus petite taille où le monogramme
     reste identifiable. */
.marque-logo {
    flex: 0 0 40px; width: 40px; aspect-ratio: 1;
    height: auto; object-fit: contain;
    display: block;
}

.marque-titre { font-weight: 640; color: #fff; font-size: var(--t-md); letter-spacing: -.01em; }
.marque-sous-titre { font-size: var(--t-xs); color: var(--n-500); margin-top: -1px; }

/* --------------------------------------------------------------- navigation */

/* LA ZONE QUI DÉFILE, et la seule.
   L'en-tête de marque et le pied utilisateur restent posés : ils ne défilent jamais, et leurs
   bordures délimitent visuellement la zone mobile — c'est ce qui rend le défilement lisible sans
   avoir à le souligner.

   `overscroll-behavior: contain` : arrivé en bas de la navigation, la molette ne doit pas se
   mettre à faire défiler la page derrière (`scroll-behavior` : pas de zone imbriquée qui
   interfère avec le défilement principal).

   `scrollbar-gutter: stable` : la gouttière est réservée en permanence, donc les liens ne se
   décalent pas de huit pixels selon que la barre apparaît ou non. C'est ce qui donne le même
   rendu sur un 27 pouces et sur un 14 pouces, et à tous les niveaux de zoom Windows. */
.navigation {
    flex: 1;
    padding: var(--e-3) var(--e-2);
    overflow-y: auto;
    overscroll-behavior: contain;
    scrollbar-gutter: stable;
}

/* UNE BARRE DE DÉFILEMENT DISCRÈTE, ET NON UNE BARRE MASQUÉE.
   Mesuré sur un 1366×768 : la navigation demande 634 px pour 584 px disponibles, donc elle
   défile — et la barre native de Windows fait 17 px, avec ses deux flèches. Elle se lisait comme
   un défaut de mise en page dans une barre latérale de 240 px.

   Ce qui est fait ici n'enlève RIEN d'accessible : la zone défile toujours, au clavier comme à la
   molette, et la densité réduite plus bas suffit à faire disparaître le défilement dans le cas
   courant. Seule l'apparence de la barre change.

   Deux moteurs, deux syntaxes, et les deux sont nécessaires : `scrollbar-width` pour Firefox,
   `::-webkit-scrollbar` pour Chromium et Edge. */
.navigation { scrollbar-width: thin; scrollbar-color: rgba(255, 255, 255, .18) transparent; }
.navigation::-webkit-scrollbar { width: 10px; }
.navigation::-webkit-scrollbar-track { background: transparent; }
.navigation::-webkit-scrollbar-thumb {
    background: rgba(255, 255, 255, .16);
    border-radius: var(--r-pilule);
    /* Bordure transparente + `padding-box` : le curseur paraît large de 4 px et flotte au milieu
       de sa gouttière, au lieu de coller aux bords comme un rail. */
    border: 3px solid transparent;
    background-clip: padding-box;
}
.navigation:hover::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, .28); background-clip: padding-box; }
.navigation::-webkit-scrollbar-thumb:hover { background: rgba(255, 255, 255, .4); background-clip: padding-box; }
/* Les flèches des barres classiques de Windows : deux carrés de 17 px qui n'ont aucun usage à la
   souris moderne, et qui étaient la moitié de ce qu'on voyait sur la capture. */
.navigation::-webkit-scrollbar-button { display: none; }

.separateur-nav {
    font-size: var(--t-2xs); text-transform: uppercase; letter-spacing: .08em;
    color: var(--n-500); padding: var(--e-4) var(--e-3) var(--e-1); font-weight: 700;
}
.navigation > .separateur-nav:first-child { padding-top: var(--e-1); }

.lien-nav {
    display: flex; align-items: center; gap: var(--e-3);
    padding: 8px var(--e-3); margin-bottom: 1px;
    border-radius: var(--r-sm);
    color: var(--n-300); font-size: var(--t-md); font-weight: 500;
    position: relative;
    transition: background var(--duree-rapide), color var(--duree-rapide);
}
.lien-nav:hover { background: rgba(255, 255, 255, .06); color: #fff; text-decoration: none; }

/* L'élément courant se signale par un aplat *et* par un liséré à gauche : la
   couleur seule ne dit pas où l'on est (`nav-state-active`, `color-not-only`). */
.lien-nav.active {
    background: rgba(182, 85, 47, .18);
    color: #fff; font-weight: 600;
}
.lien-nav.active::before {
    content: ""; position: absolute; left: 0; top: 6px; bottom: 6px;
    width: 3px; border-radius: 0 3px 3px 0;
    background: var(--marque-400);
}
.lien-nav.active .ico { color: var(--marque-300); opacity: 1; }

.lien-nav .ico {
    width: 18px; height: 18px; flex: 0 0 18px;
    display: grid; place-items: center;
    opacity: .72;
    transition: opacity var(--duree-rapide);
}
.lien-nav:hover .ico { opacity: 1; }

.pied-laterale { padding: var(--e-3); border-top: 1px solid rgba(255, 255, 255, .07); }
.utilisateur { margin-bottom: var(--e-2); }
.utilisateur-nom { color: #fff; font-weight: 600; font-size: var(--t-sm); }
.utilisateur-roles {
    font-size: var(--t-2xs); color: var(--n-500); margin-top: 1px;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* Le bouton de déconnexion vit dans une barre sombre : les couleurs de `.btn`
   y seraient illisibles. Il est donc redéfini sur place. */
.pied-laterale .btn-discret {
    color: var(--n-400);
    border-color: rgba(255, 255, 255, .1);
    background: transparent;
    font-size: var(--t-sm);
}
.pied-laterale .btn-discret:hover:not(:disabled) {
    background: rgba(255, 255, 255, .08); color: #fff;
    border-color: rgba(255, 255, 255, .18);
}

/* ------------------------------------------- barre latérale : écrans PEU HAUTS

   LE PROBLÈME N'ÉTAIT PAS LA LARGEUR, C'ÉTAIT LA HAUTEUR — et c'est pourquoi il ne se voyait que
   sur un écran. Toute l'adaptation de la barre latérale reposait sur `max-width: 900px` ; or un
   portable 14 pouces fait 1366×768 : LARGE et COURT. Il ne recevait donc aucune adaptation, et la
   navigation débordait de 50 px, « Diagnostics » passant hors champ. Sur un 27 pouces (1080 px de
   haut et plus) il reste 200 px de marge : le même code, deux rendus, aucune règle pour l'expliquer.

   Ces paliers portent sur la HAUTEUR, la seule dimension en cause. Ils sont bornés à
   `min-width: 901px` pour ne jamais croiser le mode « navigation horizontale » ni la règle
   tactile des 44 px, qui vivent tous deux sous 900 px de large.

   Le zoom Windows et le zoom navigateur sont couverts par construction : à 125 %, un 1366×768
   présente 1093×614 pixels CSS — donc un palier plus bas est franchi de lui-même, sans qu'aucune
   détection de zoom soit nécessaire.

   Ce qui est resserré : les espaces. Ce qui ne l'est jamais : la taille du texte (14 px), le
   libellé des liens, le logo, l'identité utilisateur. Une navigation compacte reste lisible ; une
   navigation en corps 11 ne l'est plus. */
@media (min-width: 901px) and (max-height: 900px) {
    .marque-compacte { padding: var(--e-3) var(--e-4); min-height: 0; }
    .navigation { padding-block: var(--e-2); }
    .lien-nav { padding-block: 6px; }
    .separateur-nav { padding-top: var(--e-2); }
    .pied-laterale { padding: var(--e-2) var(--e-3); }
}

/* Second palier : 1366×768 à 125 % de zoom, ou une fenêtre réduite en hauteur. On resserre
   encore l'interligne des liens et l'espace des séparateurs, sans descendre sous 30 px de
   hauteur cliquable — en dessous, la cible devient imprécise même à la souris. */
@media (min-width: 901px) and (max-height: 680px) {
    .marque-compacte { padding: var(--e-2) var(--e-4); }
    .lien-nav { padding-block: 5px; }
    .separateur-nav { padding-top: var(--e-1); padding-bottom: 0; }
    .lien-nav-fils { padding-block: 4px; }
}
/* RIEN N'EST MASQUÉ, à aucun palier. Retirer le sous-titre de marque ou un séparateur ferait
   gagner quelques pixels, mais c'est du contenu en moins : sous ces hauteurs, c'est la barre de
   défilement discrète ci-dessus qui prend le relais — elle rend tout atteignable sans rien
   retirer. Le seul élément dont l'affichage change avec la taille est le séparateur de section
   en mode horizontal (sous 900 px de large), où il ne sépare plus rien. */

/* ------------------------------------------------------------------ bandeau */

/* Le fond était translucide (`rgba(255,255,255,.88)` + `backdrop-filter`). Sur un
   bandeau collant, les lignes du tableau se lisaient AU TRAVERS pendant le
   défilement : le bouton « Actualiser » et les libellés paraissaient superposés à
   un autre élément. Mesuré au navigateur, capture à l'appui. Le fond redevient
   donc opaque, et c'est une ombre — pas la transparence — qui dit que le contenu
   passe dessous.

   La hauteur est FIXE et non plus minimale : le bandeau passait de 71 à 143 px en
   dessous de 1 024 px, ce qui décalait tout le contenu et désalignait la barre
   d'onglets collante, positionnée sur `--hauteur-bandeau`. */
.bandeau {
    display: flex; align-items: center; justify-content: space-between; gap: var(--e-4);
    height: var(--hauteur-bandeau);
    padding: 0 var(--e-8);
    background: var(--surface);
    border-bottom: 1px solid var(--bord);
    box-shadow: 0 1px 3px rgba(13, 20, 36, .05);
    position: sticky; top: 0; z-index: 30;
}

/* --------------------------------------------------- indicateur de synchro */

/* Un seul contrôle porte l'état : pastille, phase, âge de la donnée. Le reste
   s'ouvre au clic. Il se lit comme un bouton discret parce que c'en est un. */
.etat-synchro {
    display: inline-flex; align-items: center; gap: var(--e-2);
    padding: var(--e-2) var(--e-3);
    max-width: 100%;
    border: 1px solid transparent; border-radius: var(--r-pilule);
    background: transparent; color: var(--texte);
    font-family: inherit; font-size: var(--t-sm); cursor: pointer;
    transition: background var(--duree-rapide), border-color var(--duree-rapide);
}
.etat-synchro:hover { background: var(--n-50); border-color: var(--bord); }
.etat-synchro[aria-expanded="true"] { background: var(--n-100); border-color: var(--bord-fort); }

.etat-synchro-phase { font-weight: 640; white-space: nowrap; }
.etat-synchro-age {
    color: var(--texte-faible); font-variant-numeric: tabular-nums; white-space: nowrap;
}
.etat-synchro-age.perime { color: var(--attention-texte); font-weight: 600; }
.etat-synchro-chevron { color: var(--texte-faible); transition: transform var(--duree-rapide); }
.etat-synchro[aria-expanded="true"] .etat-synchro-chevron { transform: rotate(90deg); }

.panneau-etat {
    position: absolute; top: calc(100% + var(--e-2)); left: 0; z-index: 40;
    width: min(560px, calc(100vw - var(--e-8)));
    padding: var(--e-4);
    background: var(--surface);
    border: 1px solid var(--bord-fort); border-radius: var(--r-md);
    box-shadow: var(--ombre-3);
    animation: apparition var(--duree) var(--ease-sortie);
}
.panneau-etat-message {
    font-size: var(--t-sm); color: var(--texte-doux);
    padding-bottom: var(--e-3); margin-bottom: var(--e-3);
    border-bottom: 1px solid var(--bord-discret);
}
.panneau-etat .bandeau-horloges {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: var(--e-3) var(--e-4);
}
.panneau-etat .horloge { padding-left: 0; border-left: none; }
.panneau-etat .menu-note { padding: var(--e-3) 0 0; margin-top: var(--e-3); }

.bandeau-statut { display: flex; align-items: center; gap: var(--e-2); min-width: 0; flex: 0 0 auto; }
.bandeau-phase { font-weight: 640; font-size: var(--t-md); letter-spacing: -.01em; }
.bandeau-detail { font-size: var(--t-xs); color: var(--texte-doux); margin-top: -2px; }

.pastille { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 8px; background: var(--texte-faible); }
.sync-ok .pastille { background: var(--succes-vif); box-shadow: 0 0 0 3px var(--succes-fond); }
.sync-warn .pastille { background: var(--attention-vif); box-shadow: 0 0 0 3px var(--attention-fond); }
.sync-error .pastille { background: var(--danger-vif); box-shadow: 0 0 0 3px var(--danger-fond); }
.sync-busy .pastille { background: var(--info-vif); animation: pulsation 1.1s ease-in-out infinite; }
.sync-warn { border-bottom-color: var(--attention-bord); }
.sync-error { border-bottom-color: var(--danger-bord); }

@keyframes pulsation { 0%, 100% { opacity: 1; } 50% { opacity: .3; } }

/* Les horloges sont une information de contexte, pas le sujet de l'écran :
   elles reculent d'un cran de contraste et sont séparées par des filets plutôt
   que par du vide, ce qui les regroupe visuellement (`whitespace-balance`). */
.bandeau-horloges { display: flex; gap: var(--e-5); flex: 1; min-width: 0; }
.horloge {
    display: flex; flex-direction: column; gap: 0;
    padding-left: var(--e-5); border-left: 1px solid var(--bord-discret);
    min-width: 0;
}
.horloge:first-child { padding-left: 0; border-left: none; }
.horloge-libelle {
    font-size: var(--t-2xs); text-transform: uppercase; letter-spacing: .06em;
    color: var(--texte-faible); font-weight: 700;
}
.horloge-valeur { font-size: var(--t-sm); font-weight: 620; font-variant-numeric: tabular-nums; }
.horloge-valeur.perime { color: var(--attention-texte); }
.horloge-relatif { font-size: var(--t-2xs); color: var(--texte-faible); }
.alerte-inline { color: var(--danger-texte); font-weight: 640; }

.bandeau-actions { display: flex; gap: var(--e-2); align-items: center; flex: 0 0 auto; }

/* ------------------------------------------------------------------ boutons */

.btn {
    display: inline-flex; align-items: center; justify-content: center; gap: var(--e-2);
    padding: 7px var(--e-3);
    min-height: 34px;
    border: 1px solid var(--bord-fort);
    border-radius: var(--r-sm);
    background: var(--surface);
    color: var(--texte);
    font-size: var(--t-sm); font-weight: 570; font-family: inherit;
    letter-spacing: -.005em;
    cursor: pointer;
    box-shadow: var(--ombre-1);
    transition: background var(--duree-rapide), border-color var(--duree-rapide),
                box-shadow var(--duree-rapide), color var(--duree-rapide);
    white-space: nowrap;
}
.btn:hover:not(:disabled) { background: var(--n-50); border-color: var(--n-400); box-shadow: var(--ombre-2); }

/* Le retour d'appui ne déplace rien autour du bouton : l'ombre s'écrase, la
   géométrie ne bouge pas (`press-feedback` sans décalage de mise en page). */
.btn:active:not(:disabled) { background: var(--n-100); box-shadow: none; }

/* Un contrôle désactivé se lit comme tel — opacité réduite *et* curseur, jamais
   l'un sans l'autre (`disabled-states`). */
.btn:disabled { opacity: .45; cursor: not-allowed; box-shadow: none; }

/* Les icônes d'un bouton reculent d'un cran : c'est le libellé qui porte
   l'action, le glyphe ne fait que l'accompagner. */
.btn .ico { opacity: .8; flex: 0 0 auto; }
.btn:hover:not(:disabled) .ico { opacity: 1; }

.btn-primaire {
    background: var(--marque-500); border-color: var(--marque-600); color: #fff;
    box-shadow: 0 1px 2px rgba(141, 63, 33, .24), inset 0 1px 0 rgba(255, 255, 255, .16);
}
.btn-primaire:hover:not(:disabled) {
    background: var(--marque-600); border-color: var(--marque-700);
    box-shadow: 0 2px 6px rgba(141, 63, 33, .3), inset 0 1px 0 rgba(255, 255, 255, .16);
}
.btn-primaire:active:not(:disabled) { background: var(--marque-700); box-shadow: none; }
.btn-primaire .ico { opacity: .92; }

.btn-secondaire { background: var(--surface); }
.btn-actif { background: var(--info-fond); border-color: var(--info-bord); color: var(--info-texte); }
.btn-actif:hover:not(:disabled) { background: var(--info-fond); border-color: var(--info-vif); }

/* Action destructive : couleur sémantique et séparée des actions ordinaires
   (`destructive-emphasis`). */
.btn-danger { background: var(--surface); border-color: var(--danger-bord); color: var(--danger-texte); }
.btn-danger:hover:not(:disabled) { background: var(--danger-fond); border-color: var(--danger-vif); }

.btn-discret {
    background: transparent; border-color: transparent; color: var(--texte-doux);
    box-shadow: none;
}
.btn-discret:hover:not(:disabled) {
    background: var(--n-100); border-color: transparent; color: var(--texte);
    box-shadow: none;
}
.btn-large { width: 100%; padding: var(--e-3); font-size: var(--t-md); min-height: 42px; }
.btn-bloc { width: 100%; }
.btn-mini { padding: 3px var(--e-2); font-size: var(--t-xs); min-height: 28px; gap: var(--e-1); }

.btn-fermer {
    display: inline-grid; place-items: center;
    background: none; border: none; cursor: pointer;
    color: var(--texte-faible);
    width: 30px; height: 30px;
    border-radius: var(--r-sm);
    transition: background var(--duree-rapide), color var(--duree-rapide);
}
.btn-fermer:hover { background: var(--n-100); color: var(--texte); }

.btn-vider {
    background: none; border: none; cursor: pointer; color: var(--texte-faible);
    padding: 2px var(--e-2); font-size: var(--t-sm); border-radius: var(--r-xs);
}
.btn-vider:hover { color: var(--danger-texte); }

.lien-mini {
    background: none; border: none; padding: 0; cursor: pointer;
    color: var(--info-texte); font-size: var(--t-xs); font-family: inherit; text-decoration: underline;
    text-underline-offset: 2px;
}

.compteur-pastille {
    background: var(--marque-500); color: #fff;
    border-radius: var(--r-pilule); padding: 1px 7px;
    font-size: var(--t-2xs); font-weight: 700; font-variant-numeric: tabular-nums;
}

/* --------------------------------------------------------------------- icônes

   Jeu unique, dessiné dans Components/Partagees/Icone.razor : trait de 1,75 px
   sur une grille de 20, `currentColor`, jamais de remplissage. Les pictogrammes
   Unicode qu'il remplace (▤ ▦ ▥ ▧ ◫ ◰) se rendaient tous comme le même carré
   flou dans la navigation, et changeaient de dessin d'une police à l'autre. */

.ico {
    display: inline-block;
    width: 1.05em; height: 1.05em;
    flex: 0 0 auto;
    vertical-align: -.16em;
    stroke: currentColor;
    fill: none;
    stroke-width: 1.75;
    stroke-linecap: round;
    stroke-linejoin: round;
}
.ico-sm { width: .9em; height: .9em; }
.ico-lg { width: 1.3em; height: 1.3em; }

/* `inline-block` explicite : le rotor est un `<span>`, et un `<span>` en `inline`
   ignore largeur et hauteur. Il ne tenait que parce que ses deux emplacements le
   posaient en élément flex — dans un `.btn` et dans une `.alerte`. L'alerte étant
   redevenue un bloc, il y aurait disparu sans laisser de trace. */
.rotor {
    display: inline-block; vertical-align: -1px;
    width: 12px; height: 12px; flex: 0 0 12px;
    border: 2px solid currentColor; border-right-color: transparent;
    border-radius: 50%; animation: rotation .65s linear infinite;
}
@keyframes rotation { to { transform: rotate(360deg); } }

/* ------------------------------------------------------------------ en-têtes */

/* Le repli est nécessaire, pas décoratif : un écran portant trois actions — « Importer »,
   « Ajouter un UpSell », « Nouvelle commande » — débordait horizontalement sur mobile, et le
   corps de page entier se mettait à défiler de côté. */
.entete-page {
    display: flex; align-items: flex-end; justify-content: space-between;
    gap: var(--e-4); margin-bottom: var(--e-5); flex-wrap: wrap;
}
.entete-titre { min-width: 0; }
.entete-actions { display: flex; gap: var(--e-2); align-items: center; flex-wrap: wrap; }
.sous-titre { color: var(--texte-doux); font-size: var(--t-sm); margin-top: 2px; }

/* Titre de section : le filet séparateur va chercher toute la largeur, ce qui
   pose la section comme un palier de la page et non comme un simple gras. */
.titre-section {
    display: flex; align-items: center; gap: var(--e-2);
    margin-bottom: var(--e-3); padding-bottom: var(--e-2);
    border-bottom: 1px solid var(--bord);
}
.titre-section h2 { font-size: var(--t-lg); }
.picto-titre { color: var(--marque-600); display: inline-flex; }
.bloc-page { margin-bottom: var(--rythme-large); }

/* ---------------------------------------------------------------- recherche */

.barre-recherche {
    display: flex; gap: var(--e-3); align-items: center;
    margin-bottom: var(--e-3); flex-wrap: wrap;
}

.champ-recherche {
    display: flex; align-items: center; gap: var(--e-2);
    flex: 1; min-width: 260px; max-width: 520px;
    padding: 0 var(--e-3);
    background: var(--surface);
    border: 1px solid var(--bord-fort);
    border-radius: var(--r-sm);
    box-shadow: var(--ombre-1);
    transition: border-color var(--duree-rapide), box-shadow var(--duree-rapide);
}
.champ-recherche:hover { border-color: var(--n-400); }
.champ-recherche:focus-within {
    border-color: var(--focus);
    box-shadow: 0 0 0 3px var(--focus-halo);
}
/* L'anneau global viendrait doubler celui du conteneur : le champ interne s'en
   dispense, c'est la boîte qui porte l'état de focus. */
.champ-recherche input:focus-visible { outline: none; }
.champ-recherche input {
    flex: 1; min-width: 0; border: none; outline: none;
    padding: 8px 0; font-size: var(--t-md); font-family: inherit; background: none;
}
.champ-recherche input::placeholder { color: var(--texte-faible); }

/* La loupe est un glyphe décoratif dans une boîte flex : elle ne doit ni grandir
   ni se replier. Le `flex: 0 0 auto` corrige un défaut constaté sur l'écran des
   commandes offline, où le champ portait aussi la classe `.champ` — dont la
   règle `.champ > span { display: block }` sortait la loupe du champ et la posait
   au-dessus, où elle se lisait comme un caractère parasite. */
.ico-recherche {
    color: var(--texte-faible);
    display: inline-flex; align-items: center;
    flex: 0 0 auto;
}
.champ-recherche > .ico-recherche { display: inline-flex; margin: 0; }

.champ-tri { display: flex; align-items: center; gap: var(--e-2); font-size: var(--t-sm); color: var(--texte-doux); }

/* Les listes déroulantes de barre d'outils reprennent la flèche dessinée en
   `data:` du reste de la feuille : la flèche native varie d'un navigateur à
   l'autre, et deux contrôles voisins n'avaient pas le même dessin. */
.champ-tri select, .pagination-taille select {
    padding: 7px var(--e-3); padding-right: 30px;
    min-height: 34px;
    border: 1px solid var(--bord-fort); border-radius: var(--r-sm);
    background: var(--surface); color: inherit;
    font-size: var(--t-sm); font-family: inherit; cursor: pointer;
    box-shadow: var(--ombre-1);
    appearance: none;
    background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath fill='none' stroke='%237d8798' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round' d='M1 1l4 4 4-4'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right var(--e-3) center;
    transition: border-color var(--duree-rapide);
}
.champ-tri select:hover, .pagination-taille select:hover { border-color: var(--n-400); }

/* -------------------------------------------------------------------- puces */

.puces-actives {
    display: flex; flex-wrap: wrap; gap: var(--e-2); align-items: center;
    padding: var(--e-2) var(--e-3); margin-bottom: var(--e-3);
    background: var(--info-fond);
    border: 1px solid var(--info-bord);
    border-radius: var(--r-md);
}
.puces-libelle {
    font-size: var(--t-2xs); text-transform: uppercase; letter-spacing: .07em;
    font-weight: 700; color: var(--info-texte); margin-right: var(--e-1);
}

/* Une puce se retire d'un clic : le survol annonce le retrait en passant au
   rouge, pour qu'on sache que le clic supprime au lieu d'ouvrir. */
.puce {
    display: inline-flex; align-items: center; gap: var(--e-1);
    padding: 3px var(--e-2);
    background: var(--surface);
    border: 1px solid var(--info-bord);
    border-radius: var(--r-pilule);
    font-size: var(--t-xs); font-family: inherit; cursor: pointer;
    transition: background var(--duree-rapide), border-color var(--duree-rapide);
}
.puce:hover { border-color: var(--danger-vif); background: var(--danger-fond); }
.puce-cle { color: var(--texte-faible); }
.puce-valeur { font-weight: 600; }
.puce-croix { color: var(--texte-faible); font-size: .7rem; }
.puce:hover .puce-croix { color: var(--rouge); }

/* ------------------------------------------------------------------ filtres */

.panneau-filtres {
    background: var(--surface);
    border: 1px solid var(--bord);
    border-radius: var(--r-md);
    padding: var(--e-4);
    margin-bottom: var(--e-4);
    box-shadow: var(--ombre-2);
}

.filtre-colonnes {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(258px, 1fr));
    gap: 16px 20px;
}

.filtre-groupe { min-width: 0; }
.filtre-entete { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; margin-bottom: 6px; }
.filtre-titre { font-size: .72rem; text-transform: uppercase; letter-spacing: .05em; font-weight: 700; color: var(--texte-doux); }
.filtre-valeurs { display: flex; flex-wrap: wrap; gap: 5px; }

.bascule {
    display: inline-flex; align-items: center; gap: var(--e-1);
    padding: var(--e-1) var(--e-3);
    background: var(--surface);
    border: 1px solid var(--bord-fort);
    border-radius: var(--r-pilule);
    color: var(--texte);
    font-size: var(--t-xs); font-weight: 550; font-family: inherit; cursor: pointer;
    transition: background var(--duree-rapide), border-color var(--duree-rapide),
                color var(--duree-rapide);
}
.bascule:hover { border-color: var(--n-400); background: var(--n-50); }
.bascule-active { background: var(--n-900); border-color: var(--n-900); color: #fff; }
.bascule-active:hover { background: var(--n-800); border-color: var(--n-800); }

/* Statut qu'un UpSell doit viser pour que son effet soit celui annoncé : mis en avant sans être
   présélectionné, car le choix reste celui de l'utilisateur. */
.bascule-suggeree { border-color: var(--vert); box-shadow: inset 0 -2px 0 var(--vert); font-weight: 600; }
.bascule-compteur { font-size: .68rem; opacity: .65; font-variant-numeric: tabular-nums; }

.filtre-plage { display: flex; align-items: center; gap: 6px; }
.filtre-plage input {
    flex: 1; min-width: 0;
    padding: 6px 8px;
    border: 1px solid var(--bord-fort); border-radius: var(--r-petit);
    font-size: .82rem; font-family: inherit;
}
.filtre-plage input:focus { outline: none; border-color: var(--bleu); box-shadow: 0 0 0 2px var(--bleu-clair); }
.fleche { color: var(--texte-faible); font-size: .8rem; }

/* ---------------------------------------------------------------- synthèse */

/* Bande d'indicateurs. Elle était en `flex` : la dernière cellule absorbait
   toute la largeur restante, et une bande de huit chiffres se terminait par une
   cellule trois fois plus large que les autres — mesuré sur les écrans
   « Commandes offline » et « Utilisateurs ». Une grille à colonnes égales
   répartit l'espace au lieu de le donner au dernier arrivé. */
.barre-synthese {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(132px, 1fr));
    background: var(--surface);
    border: 1px solid var(--bord);
    border-radius: var(--r);
    margin-bottom: var(--e-4);
    box-shadow: var(--ombre-1);
    overflow: hidden;
}
.synthese-item {
    padding: var(--e-3) var(--e-4);
    border-right: 1px solid var(--bord-discret);
    min-width: 0;
}
.synthese-item:last-child { border-right: none; }
.synthese-valeur {
    display: block; font-family: var(--police-affichage);
    font-size: var(--t-xl); font-weight: 650;
    font-variant-numeric: tabular-nums; letter-spacing: -.022em;
    line-height: 1.15;
}
.synthese-libelle {
    display: block; font-size: var(--t-xs); color: var(--texte-faible);
    margin-top: 2px; line-height: 1.35;
}

/* ------------------------------------------------------------------ cartes */

/* 190 px et non 216 : les six indicateurs de Pilotage tenaient sur cinq colonnes,
   et le sixième restait seul sur une deuxième ligne — une carte orpheline se lit
   comme une catégorie à part alors qu'elle est du même rang que les autres. */
.grille-cartes {
    display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
    gap: var(--e-3); margin-bottom: var(--rythme);
}
.grille-deux { display: grid; grid-template-columns: repeat(auto-fit, minmax(420px, 1fr)); gap: var(--e-5); }

/* --------------------------------------------------------- grille bento

   Quatre colonnes égales, deux cartes qui en occupent deux : six indicateurs
   remplissent exactement deux lignes, sans trou ni carte orpheline. Les paliers
   descendent par deux — quatre, deux, une — pour que les cartes doubles restent
   doubles aussi longtemps qu'elles tiennent. */
.grille-bento {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: var(--e-3);
    margin-bottom: var(--rythme);
}
.grille-bento > .carte-large { grid-column: span 2; }

/* La carte dominante n'est pas seulement plus large : sa valeur est plus grande,
   et elle est la seule dans ce cas. La hiérarchie passe par la taille, pas par la
   seule couleur (`visual-hierarchy`). */
.carte-valeur-xl {
    font-size: clamp(2rem, 3.4vw, 2.75rem);
    letter-spacing: -.03em;
}
.grille-bento .carte { display: flex; flex-direction: column; }
/* Le détail se cale en bas : les six cartes alignent leur dernière ligne même
   quand l'une porte deux lignes de texte et l'autre une seule. */
.grille-bento .carte-detail { margin-top: auto; padding-top: var(--e-2); }

/* Une carte double dont la valeur est courte se lisait creuse : « 24 356 € » au
   milieu de 800 px. Sa largeur sert donc à poser le détail À CÔTÉ du chiffre plutôt
   qu'en dessous — la carte est remplie, et le détail gagne d'être lu sur une seule
   ligne au lieu de deux.

   La carte dominante en est exempte : c'est justement l'air autour de son chiffre
   qui le désigne comme le plus important de l'écran. */
.grille-bento > .carte-large:not(.carte-forte) {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    grid-template-rows: auto auto;
    align-content: center;
    column-gap: var(--e-6);
}
.grille-bento > .carte-large:not(.carte-forte) > .carte-libelle { grid-column: 1; }
.grille-bento > .carte-large:not(.carte-forte) > .carte-valeur { grid-column: 1; }
.grille-bento > .carte-large:not(.carte-forte) > .carte-detail {
    grid-column: 2; grid-row: 1 / -1;
    align-self: end;
    margin-top: 0; padding-top: 0;
    padding-left: var(--e-6);
    border-left: 1px solid var(--bord-discret);
}

/* En dessous de quatre colonnes, la carte double redevient une carte comme les
   autres : à 494 px, la colonne de détail se réduirait à trois mots par ligne. */
@media (max-width: 1400px) {
    .grille-bento > .carte-large:not(.carte-forte) {
        display: flex; flex-direction: column;
    }
    .grille-bento > .carte-large:not(.carte-forte) > .carte-detail {
        margin-top: auto; padding-top: var(--e-2);
        padding-left: 0; border-left: none;
        /* `align-self: end` visait la fin de l'axe VERTICAL dans la grille à deux
           colonnes. En colonne flex, l'axe secondaire est horizontal : la même
           déclaration collait le détail à droite de la carte. Elle est donc remise
           à plat, et non simplement oubliée. */
        align-self: stretch;
    }
}

@media (max-width: 1180px) {
    .grille-bento { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 620px) {
    .grille-bento { grid-template-columns: 1fr; }
    .grille-bento > .carte-large { grid-column: span 1; }
}

.carte {
    background: var(--surface);
    border: 1px solid var(--bord);
    border-radius: var(--r-md);
    padding: var(--e-4);
    box-shadow: var(--ombre-1);
}
.carte-libelle {
    font-size: var(--t-2xs); text-transform: uppercase; letter-spacing: .07em;
    color: var(--texte-faible); font-weight: 700;
    display: flex; align-items: center; gap: var(--e-2);
}
.carte-valeur {
    font-family: var(--police-affichage);
    font-size: var(--t-3xl); font-weight: 650;
    margin: var(--e-2) 0 var(--e-1);
    font-variant-numeric: tabular-nums; letter-spacing: -.028em;
    line-height: 1.05;
}
.carte-detail { font-size: var(--t-xs); color: var(--texte-faible); line-height: 1.45; }

/* Une seule carte forte par écran : c'est le chiffre qui répond à la question
   posée par la page (`primary-action` appliqué à la donnée). */
.carte-forte {
    background: linear-gradient(150deg, var(--n-800), var(--n-950));
    border-color: var(--n-950); color: #fff;
    box-shadow: var(--ombre-2);
}
.carte-forte .carte-libelle { color: var(--marque-300); }
.carte-forte .carte-detail { color: var(--n-400); }

.carte-alerte { border-left: 3px solid var(--attention-vif); }
.carte-qualite { border-left: 3px solid var(--bord-fort); }
.carte-qualite.q-exacte { border-left-color: var(--succes-vif); }
.carte-qualite.q-calculee { border-left-color: var(--info-vif); }
.carte-qualite.q-partielle { border-left-color: var(--attention-vif); }
.carte-qualite.q-introuvable { border-left-color: var(--danger-vif); }

/* ----------------------------------------------------------------- tableaux */

.cadre-tableau {
    background: var(--surface);
    border: 1px solid var(--bord);
    border-radius: var(--r-md);
    overflow: auto;          /* le défilement reste confiné au tableau */
    box-shadow: var(--ombre-1);
}

.tableau { width: 100%; border-collapse: collapse; font-size: var(--t-sm); }

/* En-tête collant. Le filet est porté par `box-shadow` et non par `border` :
   une bordure de cellule collante se décolle au défilement dans Chromium, et
   laissait passer une ligne de contenu par-dessus le filet. */
.tableau thead th {
    position: sticky; top: 0; z-index: 5;
    background: var(--n-50);
    box-shadow: inset 0 -1px 0 var(--bord-fort);
    padding: var(--e-2) var(--e-3);
    text-align: left;
    font-size: var(--t-2xs); text-transform: uppercase; letter-spacing: .07em;
    font-weight: 700; color: var(--texte-doux);
    /* Les intitulés passent à la ligne. C'était la première cause du défilement
       horizontal, et la moins visible : en capitales avec un interlettrage de
       .07em, « COMMANDÉ LE », « PRESTATIONS » et « VENTILATION DU PRIX » forçaient
       chacun 30 à 45 px de plus que la donnée en dessous — mesuré au navigateur,
       près de 300 px sur les seize colonnes du tableau des commandes, à cause des
       seuls en-têtes. Un intitulé sur deux lignes ne coûte rien : il est lu une
       fois, la colonne est lue mille fois. */
    white-space: normal;
    line-height: 1.25;
    vertical-align: bottom;
}
.tableau thead th.num { text-align: right; }

.tableau tbody td {
    padding: var(--e-3) var(--e-3);
    border-bottom: 1px solid var(--bord-discret);
    vertical-align: middle;
}
.tableau tbody tr:last-child td { border-bottom: none; }
.tableau tbody tr { cursor: pointer; transition: background var(--duree-rapide); }

/* Le survol dit « cette ligne est cliquable » : c'est le seul indice, puisque
   rien d'autre ne bouge. Il est donc net plutôt que suggéré. */
.tableau tbody tr:hover { background: var(--marque-050); }

.tableau tfoot td {
    padding: var(--e-3); border-top: 1px solid var(--bord-fort);
    background: var(--n-50); font-variant-numeric: tabular-nums; font-weight: 620;
}

.tableau-compact tbody tr { cursor: default; }
.tableau-compact tbody tr:hover { background: var(--n-50); }
.tableau-compact tbody td { padding: var(--e-2) var(--e-3); }

.ligne-annulee { opacity: .55; }
.ligne-annulee .montant { text-decoration: line-through; }

/* La ligne retenue garde son liséré même au survol : sans le `!important`, le
   survol de la ligne voisine la faisait passer pour désélectionnée. */
.ligne-selectionnee { background: var(--info-fond) !important; box-shadow: inset 3px 0 0 var(--info-vif); }
.ligne-alerte { background: var(--danger-fond); }
.ligne-alerte:hover { background: var(--danger-fond); }
.ligne-attention { background: var(--attention-fond); }
.ligne-attention:hover { background: var(--attention-fond); }
.ligne-manquante { background: var(--n-25); }
.ligne-total td { border-top: 1px solid var(--bord-fort); }

/* Ligne d'UpSell dépliée sous sa commande mère. Décalée et teintée pour qu'on lise d'un coup
   d'œil qu'elle appartient à la ligne du dessus, et non qu'elle est une commande de plus. */
.ligne-upsell { background: #faf9fd; }
.ligne-upsell > td:first-child { box-shadow: inset 3px 0 0 var(--violet); padding-left: 18px; }
.ligne-upsell-contenu { display: flex; align-items: baseline; gap: 10px; }
.ligne-upsell-contenu .cellule-principale { flex: 1; min-width: 0; }
.ligne-upsell-contenu .montant { white-space: nowrap; }

/* Total du dossier : initial et UpSell confondus. Souligné d'un trait pour dire « somme », la
   distinction restant portée par les deux lignes au-dessus. */
.total-dossier { border-top: 1px solid var(--bord); padding-top: 2px; font-weight: 600; }

.tiroir-actions-inline { margin-top: 6px; }

/* Précision secondaire qui signale un problème — un numéro porté mais introuvable. Teintée sans
   être une alerte : elle vit dans une cellule de tableau, pas dans un bandeau. */
.alerte-texte { color: var(--rouge); }

/* Reproche d'origine déjà réglé : conservé pour mémoire, mais visiblement clos. Le barrer évite
   qu'un rattachement réussi se lise encore comme un manque. */
.liste-levee li { color: var(--texte-doux); text-decoration: line-through; }
.liste-levee li::marker { content: "✓ "; }

/* Champs manquants d'un dossier, nommés plutôt que racontés. Deux tons seulement, parce qu'il n'y
   a que deux gestes possibles : remplir un champ, ou instruire un cas. */
.grappe-manques { display: flex; flex-wrap: wrap; gap: 4px; }

.badge-manque {
    background: var(--ambre-clair); color: var(--ambre);
    border: 1px solid #eccb8a; cursor: help;
}
.badge-manque-grave {
    background: var(--rouge-clair); color: var(--rouge);
    border: 1px solid #e8a9a4; cursor: help;
}
.badge-manque::before { content: "✎ "; opacity: .7; }
.badge-manque-grave::before { content: "⚠ "; opacity: .8; }

/* Repli d'un tableau long. Même traitement que .detail-erreur, dont ce n'est pas le sujet. */
.detail-groupe { margin-top: 10px; }
.detail-groupe summary { cursor: pointer; font-size: .8rem; color: var(--bleu); }
.detail-groupe[open] summary { margin-bottom: 8px; }

.legende-tableau {
    caption-side: top; text-align: left;
    padding: 0 0 6px; font-size: .78rem; color: var(--texte-doux);
}

/* Le contenu long passe à la ligne au lieu d'être tronqué. C'est ce qui rend les
   colonnes compressibles : une cellule en `nowrap` impose sa largeur au tableau,
   donc la moindre colonne ajoutée poussait la largeur totale et déclenchait le
   défilement. Le référentiel préfère de toute façon le retour à la ligne à la
   troncature (`truncation-strategy`) — un nom coupé à « Christopher And… » ne se
   retrouve pas dans une recherche. */
.cellule-principale { font-weight: 550; overflow-wrap: anywhere; }
.cellule-secondaire {
    font-size: var(--t-xs); color: var(--texte-faible); margin-top: 1px;
    overflow-wrap: anywhere;
}
.cellule-liste { display: flex; flex-wrap: wrap; gap: var(--e-1); }
.montant { font-weight: 650; font-variant-numeric: tabular-nums; white-space: nowrap; }

/* =============================================================================
   Largeurs de colonnes : des planchers, pas des largeurs fixes
   -----------------------------------------------------------------------------
   Chaque colonne portait un `width` ferme. Additionnés, les seize `width` du
   tableau des commandes réclamaient 2 029 px pour un cadre de 1 194 : mesuré au
   navigateur, 835 px de défilement horizontal dès que les colonnes optionnelles
   étaient activées.

   Un `width` sur une cellule de tableau est un *minimum souhaité* que le moteur
   de mise en page ne sait pas réduire quand la somme dépasse le conteneur — il
   déborde. Un `min-width` associé à `table-layout: auto` et à du contenu qui sait
   passer à la ligne, lui, laisse le navigateur répartir l'espace disponible : les
   colonnes s'élargissent quand il y a de la place et se resserrent jusqu'à leur
   plancher quand il en manque.

   Les planchers ci-dessous sont mesurés sur le contenu réel : un numéro de
   commande à dix chiffres, un badge « Confirmée », une date en jj/mm/aaaa, un
   montant à cinq chiffres. En dessous, la valeur casserait au milieu.
   ============================================================================= */

/* Colonnes toujours présentes. */
.col-cmd { min-width: 100px; }
.col-statut { min-width: 78px; }
.col-date { min-width: 78px; white-space: nowrap; }
.col-client { min-width: 148px; }
.col-pack { min-width: 96px; }
.col-billets { min-width: 126px; }
.col-hotel { min-width: 130px; }
.col-total { min-width: 88px; }
.col-action { width: 26px; text-align: center; }

/* Colonnes optionnelles : plancher plus serré, car elles ne portent qu'une valeur
   courte et ce sont elles qui, cumulées, font déborder le tableau. */
.col-tel { min-width: 106px; white-space: nowrap; }
.col-ville { min-width: 82px; }
.col-presta { min-width: 58px; }
.col-assur { min-width: 70px; }
.col-ventil { min-width: 82px; }
.col-paiement { min-width: 82px; }
.col-ref { min-width: 58px; }

/* Le badge de pack est le seul dont le libellé est long — « JOURNEE ET SOIREE »
   réclamait 123 px en `nowrap`, soit 27 px de plus que le plancher de sa colonne.
   Il passe à la ligne ; les autres badges, courts, gardent le `nowrap` général qui
   évite qu'un état se coupe en deux. */
.col-pack .badge { white-space: normal; }

/* Marge intérieure resserrée quand le tableau porte beaucoup de colonnes : quatre
   pixels de moins par cellule font soixante-quatre pixels sur seize colonnes. La
   règle est portée par le tableau ancré, c'est-à-dire le seul à colonnes
   optionnelles ; les autres gardent leur respiration. */
.tableau-ancre tbody td, .tableau-ancre thead th { padding-inline: var(--e-2); }
.col-barre { width: 116px; }
.col-heure { width: 96px; }
.col-niveau { width: 108px; }
.col-source-j { width: 108px; }
.col-duree { width: 84px; }
.col-col { width: 46px; }
.col-champ { width: 210px; }
.col-qual { width: 116px; }
.col-source { max-width: 300px; }
.col-join { width: 78px; }
.col-taux { width: 116px; }
.col-limite { font-size: .78rem; color: var(--texte-doux); line-height: 1.45; }

/* =============================================================================
   Colonne d'ancrage : le numéro de commande reste visible au défilement
   -----------------------------------------------------------------------------
   Les planchers ci-dessus suppriment le défilement pour le jeu de colonnes par
   défaut et pour trois colonnes optionnelles à 1 500 px. Au-delà, seize colonnes
   ne tiennent physiquement pas dans 1 194 px sans rendre les valeurs illisibles.

   Ce qui reste de défilement est donc rendu utilisable plutôt que masqué : la
   première colonne se fige, et l'on sait toujours de quelle commande on lit la
   ligne. C'est la solution habituelle des grilles de données, et elle évite d'avoir
   à faire disparaître d'office une colonne que l'utilisateur vient de demander.

   `background` est obligatoire : une cellule collante sans fond laisse défiler
   le contenu des colonnes suivantes en dessous d'elle. La teinte de survol de la
   ligne doit donc être répétée ici, sinon la colonne figée reste blanche quand le
   reste de la ligne s'éclaire. */
.tableau-ancre thead th:first-child,
.tableau-ancre tbody td:first-child {
    position: sticky; left: 0; z-index: 6;
    background: var(--surface);
}
.tableau-ancre thead th:first-child {
    z-index: 7;                     /* au croisement de l'en-tête et de l'ancre */
    background: var(--n-50);
}
.tableau-ancre tbody tr:hover td:first-child { background: var(--marque-050); }
.tableau-ancre tbody tr.ligne-selectionnee td:first-child { background: var(--info-fond); }

/* Le filet de séparation dit où s'arrête la colonne figée. Porté par `box-shadow`
   et non par `border-right` : une bordure sur cellule collante se décolle du reste
   de la ligne au défilement. */
.tableau-ancre thead th:first-child,
.tableau-ancre tbody td:first-child {
    box-shadow: inset -1px 0 0 var(--bord);
}

.tableau-couverture tbody td { vertical-align: top; padding-top: 9px; }
.tableau-couverture .col-source code { white-space: normal; word-break: break-word; }
.tableau-journal tbody tr { cursor: default; }

/* Le chevron d'ouverture était à peine visible (couleur de bordure sur blanc) :
   la seule affordance d'une ligne cliquable passait inaperçue. Il gagne du
   contraste au repos et avance de 2 px au survol — un déplacement porté par
   `transform`, donc sans reflux de la colonne. */
.chevron {
    color: var(--n-400);
    display: inline-flex; align-items: center;
    transition: color var(--duree-rapide), transform var(--duree-rapide);
}
tr:hover .chevron { color: var(--marque-600); transform: translateX(2px); }

.pictos { display: flex; gap: var(--e-1); }
.picto { font-size: var(--t-md); cursor: help; }

/* barres et jauges */
.barre-mini { height: 6px; background: var(--n-100); border-radius: var(--r-pilule); overflow: hidden; }
.barre-mini span {
    display: block; height: 100%; background: var(--marque-500);
    border-radius: var(--r-pilule);
}

.jauge {
    position: relative; height: 18px; background: var(--n-100);
    border-radius: var(--r-xs); overflow: hidden;
}
.jauge-remplissage { position: absolute; inset: 0 auto 0 0; }
.jauge-verte { background: var(--succes-bord); }
.jauge-jaune { background: var(--attention-bord); }
.jauge-orange { background: #f0c79c; }
.jauge-rouge { background: var(--danger-bord); }
.jauge-texte {
    position: relative; display: block; text-align: right; padding-right: var(--e-2);
    font-size: var(--t-2xs); font-weight: 700; line-height: 18px;
    font-variant-numeric: tabular-nums;
}

/* =============================================================================
   Graphique de tendance
   -----------------------------------------------------------------------------
   Tracé en SVG écrit dans le document : la CSP interdit tout script hors origine,
   et une polyligne ne justifie pas d'héberger une librairie.

   Les couleurs suivent le référentiel pour ce type de graphique : la donnée doit
   ressortir à 3:1 au moins sur le fond, les lignes de repère rester très en
   retrait pour ne pas concurrencer la courbe (`gridline-subtle`), et les deux
   séries se distinguer par leur TRACÉ autant que par leur teinte
   (`pattern-texture`) — ici pleine contre pointillée.
   ============================================================================= */

.graphique {
    position: relative;
    background: var(--surface);
    border: 1px solid var(--bord);
    border-radius: var(--r-lg);
    padding: var(--e-4) var(--e-4) var(--e-2);
    box-shadow: var(--ombre-1);
}

.graphique-entete {
    display: flex; align-items: flex-start; justify-content: space-between;
    gap: var(--e-4); flex-wrap: wrap;
    margin-bottom: var(--e-3);
}

.graphique-legende { display: flex; gap: var(--e-4); flex-wrap: wrap; }
.legende-serie {
    display: inline-flex; align-items: center; gap: var(--e-2);
    font-size: var(--t-xs); color: var(--texte-doux); font-weight: 550;
}
.legende-trait { width: 22px; height: 0; border-top-width: 2.5px; border-top-style: solid; }
.legende-serie-ca .legende-trait { border-top-color: var(--marque-500); }
.legende-serie-cumul .legende-trait { border-top-color: var(--info-vif); border-top-style: dashed; }

/* Le cumul final est la valeur qu'on retient de la courbe : elle est écrite, pas
   seulement tracée (`direct-labeling`). */
.graphique-resume { text-align: right; }
.graphique-resume-valeur {
    display: block; font-family: var(--police-affichage);
    font-size: var(--t-xl); font-weight: 650; letter-spacing: -.02em;
    font-variant-numeric: tabular-nums; line-height: 1.1;
}
.graphique-resume-libelle { font-size: var(--t-2xs); color: var(--texte-faible); }

/* Le cadre porte la gouttière des étiquettes : la zone de tracé est ce qui reste
   après la colonne des montants (à gauche) et la ligne des dates (en bas). */
.graphique-cadre {
    position: relative;
    padding-left: 68px;
    padding-bottom: 24px;
}

/* La zone de tracé est le repère de tout ce qui est posé en pourcentage : le SVG,
   les points et les bandes. */
.graphique-tracé { position: relative; height: 288px; }
@media (max-width: 760px) { .graphique-tracé { height: 220px; } }

/* `preserveAspectRatio="none"` étire le tracé en largeur, ce qui évite de mesurer
   le conteneur en JavaScript pour recalculer un `viewBox`. Aucun texte ne vit dans
   ce SVG, précisément parce qu'il subirait le même étirement. */
.graphique-svg { display: block; width: 100%; height: 100%; }

.graphique-grille { stroke: var(--n-200); stroke-width: 1; vector-effect: non-scaling-stroke; }

.graphique-aire { fill: var(--marque-500); fill-opacity: .12; stroke: none; }

/* `vector-effect: non-scaling-stroke` est indispensable ici : sans lui, l'étirement
   horizontal du viewBox amincit le trait sur les écrans larges, et la courbe
   s'efface au lieu de garder son épaisseur. */
.graphique-ligne {
    fill: none; stroke-width: 2.5;
    stroke-linejoin: round; stroke-linecap: round;
    vector-effect: non-scaling-stroke;
}
.graphique-ligne-ca { stroke: var(--marque-500); }
.graphique-ligne-cumul { stroke: var(--info-vif); stroke-width: 2; stroke-dasharray: 6 5; }

/* --- axes, en HTML par-dessus le tracé ----------------------------------- */

.graphique-axe-y {
    position: absolute; left: 0; top: 0; width: 60px;
    height: 288px;
}
@media (max-width: 760px) { .graphique-axe-y { height: 220px; } }
.graphique-graduation {
    position: absolute; right: 0;
    transform: translateY(-50%);
    font-size: var(--t-2xs); color: var(--texte-faible);
    font-variant-numeric: tabular-nums; white-space: nowrap;
}

.graphique-axe-x { position: absolute; left: 68px; right: 0; bottom: 0; height: 20px; }
.graphique-date {
    position: absolute; transform: translateX(-50%);
    font-size: var(--t-2xs); color: var(--texte-faible);
    font-variant-numeric: tabular-nums; white-space: nowrap;
}

/* Le pas des étiquettes est calculé côté serveur, qui ignore la largeur de l'écran :
   il en garde huit, ce qui convient au bureau mais les entassait à 390 px — les deux
   dernières se touchaient (« 27/0703/08 »). Sur écran étroit, seules les bornes
   restent : c'est l'étendue de la période qui compte là, le détail vit dans le
   tableau replié juste en dessous. */
@media (max-width: 560px) {
    .graphique-date:not(:first-of-type):not(:last-of-type) { display: none; }
}

/* --- points et bandes de survol ------------------------------------------ */

/* Une bande pleine hauteur par semaine : viser un point de six pixels serait un
   exercice d'adresse (`touch-target-chart`). C'est un vrai bouton, donc atteignable
   au clavier — on tabule d'une semaine à l'autre et l'infobulle suit. */
.graphique-bande {
    position: absolute; top: 0; bottom: 0;
    transform: translateX(-50%);
    padding: 0; border: none; background: transparent;
    cursor: pointer;
    border-radius: var(--r-xs);
    transition: background var(--duree-rapide);
}
.graphique-bande-active { background: rgba(182, 85, 47, .07); }
.graphique-bande:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }

.graphique-point {
    position: absolute; left: 50%;
    width: 9px; height: 9px;
    transform: translate(-50%, -50%);
    border-radius: 50%;
    background: var(--surface);
    border: 2.5px solid var(--marque-500);
    transition: width var(--duree-rapide), height var(--duree-rapide), background var(--duree-rapide);
}
.graphique-point-actif {
    width: 13px; height: 13px;
    background: var(--marque-500);
    box-shadow: 0 0 0 4px rgba(182, 85, 47, .18);
}

/* =============================================================================
   Infobulle : centrée sur son point, mais jamais hors du tracé
   -----------------------------------------------------------------------------
   Un simple `left: X%` avec `translateX(-50%)` laisse la moitié de la bulle
   dépasser aux deux bouts de la courbe. Mesuré à 1 500 px sur la dernière
   semaine : la bulle allait jusqu'à 1 537 px alors que `.contenu` s'arrête à
   1 500, et ses 37 derniers pixels étaient rognés par le `overflow-x: clip` qui
   garde la page de tout défilement latéral. Les valeurs étant alignées à droite,
   c'étaient justement les CHIFFRES qui disparaissaient. Le premier point
   débordait de la même façon à gauche.

   `clamp()` borne la position au lieu de la corriger par seuils : la bulle reste
   centrée sur son point tant qu'elle le peut, puis glisse juste assez pour rester
   dans le tracé. Exact à toutes les largeurs, sans connaître la taille du
   conteneur — ce qu'un calcul côté serveur ne pourrait pas faire.

   La largeur est donc FIXE : c'est elle qui donne la demi-largeur (105 px) des
   deux bornes. Un `min-width`/`max-width` laisserait cette demi-largeur inconnue.
   ============================================================================= */
.graphique-infobulle {
    position: absolute; bottom: 28px;
    width: 210px;
    left: clamp(105px, var(--x, 50%), calc(100% - 105px));
    transform: translateX(-50%);
    padding: var(--e-2) var(--e-3);
    background: var(--n-900); color: #fff;
    border-radius: var(--r-sm);
    box-shadow: var(--ombre-3);
    pointer-events: none;      /* la bulle ne doit pas voler le survol à sa bande */
    z-index: 2;
}

/* En dessous de 760 px, le tracé n'est plus assez large pour qu'une bulle de
   210 px y tienne centrée : `clamp()` verrait sa borne basse dépasser sa borne
   haute et rendrait la main à la borne basse, donc la bulle ressortirait à droite.
   Elle occupe alors toute la largeur du tracé, où elle ne peut plus déborder. */
@media (max-width: 760px) {
    .graphique-infobulle {
        left: 0; right: 0; width: auto;
        transform: none;
    }
}
.graphique-infobulle-titre {
    font-size: var(--t-xs); font-weight: 650;
    padding-bottom: var(--e-1); margin-bottom: var(--e-1);
    border-bottom: 1px solid rgba(255, 255, 255, .16);
}
.graphique-infobulle-paires {
    display: grid; grid-template-columns: auto 1fr; gap: 1px var(--e-3);
    margin: 0; font-size: var(--t-xs);
}
.graphique-infobulle-paires dt { color: var(--n-400); }
.graphique-infobulle-paires dd {
    margin: 0; text-align: right; font-weight: 600; font-variant-numeric: tabular-nums;
}

.graphique-faits {
    display: flex; flex-wrap: wrap; gap: var(--e-2) var(--e-6);
    margin-top: var(--e-3);
    font-size: var(--t-sm); color: var(--texte-doux);
}
.graphique-faits strong { color: var(--texte); font-weight: 640; }

@media (prefers-reduced-motion: reduce) {
    .graphique-point, .graphique-bande { transition: none; }
}

/* --------------------------------------------------------------- pagination */

.pagination {
    display: flex; align-items: center; justify-content: space-between;
    gap: var(--e-4); padding: var(--e-3) 2px; flex-wrap: wrap;
}
.pagination-info { font-size: var(--t-sm); color: var(--texte-doux); }
.pagination-boutons { display: flex; align-items: center; gap: var(--e-1); }

/* Les quatre boutons de pagination ne portent plus qu'une icône : sans largeur
   minimale, ils tombaient à 26 px et devenaient plus petits que la cible
   admise. Carrés, ils forment aussi une rangée régulière. */
.pagination-boutons .btn-mini {
    min-width: 30px; padding-inline: var(--e-2);
}
.pagination-page {
    font-size: var(--t-sm); font-weight: 620; padding: 0 var(--e-3);
    font-variant-numeric: tabular-nums; white-space: nowrap;
}
.pagination-taille { display: flex; align-items: center; gap: var(--e-2); font-size: var(--t-sm); color: var(--texte-doux); }

/* ------------------------------------------------------------------- badges */

/* Chaque badge porte une bordure de la même famille que son fond : sur les
   surfaces alternées des tableaux, un aplat sans contour se dissolvait dans la
   ligne et l'état cessait de se lire. */
.badge {
    display: inline-block;
    padding: 2px var(--e-2);
    border: 1px solid transparent;
    border-radius: var(--r-xs);
    font-size: var(--t-2xs); font-weight: 680;
    letter-spacing: .005em;
    line-height: 1.5;
    white-space: nowrap;
}
.badge-neutre { background: var(--n-100); color: var(--texte-doux); border-color: var(--n-200); }
.badge-succes { background: var(--succes-fond); color: var(--succes-texte); border-color: var(--succes-bord); }
.badge-avertissement { background: var(--attention-fond); color: var(--attention-texte); border-color: var(--attention-bord); }
.badge-danger { background: var(--danger-fond); color: var(--danger-texte); border-color: var(--danger-bord); }
.badge-nuit { background: var(--accent-fond); color: var(--accent-texte); border-color: var(--accent-bord); }
.badge-info { background: var(--info-fond); color: var(--info-texte); border-color: var(--info-bord); }

/* Le canal de vente est une étiquette de code, pas un état : capitales et
   interlettrage ouvert, sans couleur d'alerte. */
.badge-canal-en, .badge-canal-fr {
    font-size: var(--t-2xs); letter-spacing: .06em; font-weight: 700;
}
.badge-canal-en { background: var(--info-fond); color: var(--info-texte); border-color: var(--info-bord); }
.badge-canal-fr { background: #f7eaf1; color: #8f3268; border-color: #e6c9d8; }

/* Badge cliquable : déplie les UpSell d'une commande sans ouvrir sa fiche. Reprend l'aspect d'un
   badge parce que c'en est un — le rendre bouton n'en fait pas un contrôle de formulaire. */
.badge-bouton {
    border: 1px solid transparent; cursor: pointer;
    font-family: inherit; font-size: inherit;
}
.badge-bouton:hover { border-color: currentColor; }

/* Provenance d'une commande. Distincte du canal : « offline » n'est pas une langue de vente. */
.badge-source-offline { background: #fdf0e8; color: var(--terre); }
.badge-source-magento { background: var(--surface-alt); color: var(--texte-doux); }

.badge-qualite {
    display: inline-grid; place-items: center;
    width: 16px; height: 16px;
    border-radius: 3px;
    font-size: .68rem; font-weight: 700;
    cursor: help; vertical-align: middle;
}
.q-exacte { background: var(--vert-clair); color: var(--vert); }
.q-calculee { background: var(--bleu-clair); color: var(--bleu); }
.q-partielle { background: var(--ambre-clair); color: var(--ambre); }
.q-introuvable { background: var(--rouge-clair); color: var(--rouge); }

.legende-qualite {
    display: flex; flex-wrap: wrap; gap: 16px; align-items: center;
    padding: 11px 14px; margin-top: 4px;
    background: var(--surface); border: 1px solid var(--bord); border-radius: var(--r);
    font-size: .78rem;
}
.legende-titre { font-size: .71rem; text-transform: uppercase; letter-spacing: .05em; font-weight: 700; color: var(--texte-doux); }
.legende-item { display: flex; align-items: center; gap: 5px; cursor: help; }

/* ------------------------------------------------------------------ alertes */

/* L'encart est un BLOC par défaut, et ne devient une ligne à deux colonnes que
   lorsqu'il porte effectivement un volet technique à pousser à droite.

   En `display: flex; justify-content: space-between` inconditionnel, une alerte
   de simple prose voyait chaque suite de texte devenir un élément réparti sur la
   ligne : la phrase de l'écran Billetterie se lisait « Le CA est calculé sur le
   ⟶ prix ramené à la journée ⟵ : un pack FINALES… », avec deux trous de plusieurs
   centimètres autour du terme en gras. `:has()` permet de distinguer les deux
   formes sans ajouter de classe à recopier sur chaque appel — donc sans risquer
   d'oublier celle qui produit le défaut. */
.alerte {
    display: block;
    padding: var(--e-3) var(--e-4); margin-bottom: var(--e-3);
    border-radius: var(--r-md);
    border: 1px solid var(--bord);
    border-left-width: 3px;
    background: var(--surface);
    font-size: var(--t-sm); line-height: 1.55;
}
/* Les alertes ont deux formes, et une seule règle les servait mal.

   Forme empilée — un titre puis un ou plusieurs détails :
       <div><strong>Titre.</strong><span>détail</span><span>détail</span></div>
   Forme prose — une phrase dont certains mots sont mis en avant :
       <div><strong>Le stock est recalculé.</strong> Les fichiers ne fournissent
            que les <strong>quantités initiales</strong> ; les colonnes…</div>

   L'ancien `display: flex; flex-direction: column` traitait *chaque suite de
   texte* comme un élément de la colonne : la forme prose se cassait en morceaux
   empilés, et l'encart du haut de l'écran Stocks se lisait sur quatre lignes
   décousues. Le conteneur redevient donc un bloc ordinaire, et c'est le détail
   qui passe à la ligne — mais seulement quand il suit un titre ou un autre
   détail, ce qui laisse le gras de milieu de phrase dans le flux. */
.alerte:has(> .alerte-technique) {
    display: flex; gap: var(--e-4); justify-content: space-between; flex-wrap: wrap;
}

.alerte > div { display: block; min-width: 0; }
.alerte > div > strong + span,
.alerte > div > span + span,
.alerte > div > strong + ul,
.alerte > div > span + ul { display: block; margin-top: 2px; }
.alerte-info { background: var(--info-fond); border-color: var(--info-bord); border-left-color: var(--info-vif); }
.alerte-succes { background: var(--succes-fond); border-color: var(--succes-bord); border-left-color: var(--succes-vif); }
.alerte-avertissement { background: var(--attention-fond); border-color: var(--attention-bord); border-left-color: var(--attention-vif); }
.alerte-erreur { background: var(--danger-fond); border-color: var(--danger-bord); border-left-color: var(--danger-vif); }
.alerte-bandeau { margin: 0; border-radius: 0; border-left: none; border-right: none; border-top: none; }

/* Un texte d'explication n'est pas un tableau : sa mesure est plafonnée pour
   rester lisible (`line-length-control` — 60 à 75 caractères). */
.alerte p, .alerte > div > span { max-width: 85ch; }

.alerte pre {
    margin: var(--e-2) 0 0; padding: var(--e-2);
    background: rgba(13, 20, 36, .05); border-radius: var(--r-sm);
    font-size: var(--t-xs); overflow-x: auto;
}

.alerte-technique { display: flex; flex-direction: column; gap: 3px; align-items: flex-end; font-size: .76rem; color: var(--texte-doux); max-width: 46%; text-align: right; }
.alerte-technique code { background: rgba(0, 0, 0, .06); padding: 1px 5px; border-radius: 3px; }

/* ------------------------------------------------------------------- tiroir */

/* Le voile isole le tiroir du tableau qui reste dessous. Assez dense pour que
   le premier plan se lise seul (`scrim`), assez clair pour qu'on garde le
   contexte de la ligne d'où l'on vient. */
.tiroir-fond {
    position: fixed; inset: 0;
    background: rgba(13, 20, 36, .42);
    z-index: 90;
    animation: apparition var(--duree) ease;
}
@keyframes apparition { from { opacity: 0; } }

.tiroir {
    position: fixed; top: 0; right: 0; bottom: 0;
    width: min(680px, 94vw);
    background: var(--fond);
    box-shadow: var(--ombre-3);
    z-index: 91;
    display: flex; flex-direction: column;
    animation: glissement var(--duree-lente) var(--ease-sortie);
}
@keyframes glissement { from { transform: translateX(32px); opacity: .6; } }

/* L'en-tête du tiroir reste visible pendant que le corps défile : sur une fiche
   de 3 000 px, on perdait sinon de vue le dossier qu'on est en train de lire. */
.tiroir-entete {
    display: flex; justify-content: space-between; gap: var(--e-4);
    padding: var(--e-5) var(--e-6) var(--e-4);
    background: var(--surface); border-bottom: 1px solid var(--bord);
    flex: 0 0 auto;
}
.tiroir-entete h2 { font-size: var(--t-xl); }
.tiroir-surtitre { display: flex; gap: var(--e-1); margin-bottom: var(--e-2); flex-wrap: wrap; }
.tiroir-sous-titre { font-size: var(--t-sm); color: var(--texte-doux); margin-top: var(--e-1); }
.tiroir-corps { flex: 1; overflow-y: auto; padding: var(--e-4) var(--e-6) var(--e-10); }

/* ---------------------------------------------------------------------------
   Sélecteur à valeur unique avec filtrage au clavier.
   La liste flotte au-dessus du contenu : le conteneur doit donc rester en
   position relative et ne jamais rogner le débordement.
   --------------------------------------------------------------------------- */
.champ-selecteur { position: relative; }
.selecteur-unique { position: relative; display: flex; align-items: center; }
.selecteur-unique input { width: 100%; padding-right: 28px; }

.selecteur-effacer {
    position: absolute; right: 6px; border: none; background: none; cursor: pointer;
    color: var(--texte-faible); font-size: .78rem; line-height: 1; padding: 3px;
}
.selecteur-effacer:hover { color: var(--rouge); }

.selecteur-liste {
    position: absolute; top: calc(100% + 3px); left: 0; right: 0; z-index: 30;
    margin: 0; padding: 4px; list-style: none; max-height: 260px; overflow-y: auto;
    background: var(--surface); border: 1px solid var(--bord-fort);
    border-radius: var(--r-petit); box-shadow: var(--ombre-forte);
}

.selecteur-option {
    display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
    padding: 6px 8px; border-radius: var(--r-petit); cursor: pointer; font-size: .84rem;
}
.selecteur-option:hover, .selecteur-option.survole { background: var(--surface-alt); }
.selecteur-option-libelle { font-weight: 600; }
.selecteur-option-detail { color: var(--texte-faible); font-size: .76rem; white-space: nowrap; }
.selecteur-vide { padding: 8px; color: var(--texte-faible); font-size: .8rem; }

/* Pied de liste annonçant la troncature. Collant en bas du déroulé : la liste défile jusqu'à
   260 px, donc une note posée en flux normal sortait du champ de vision exactement quand elle
   comptait — au bout des vingt options, là où l'on conclut que l'élément cherché n'existe pas.
   Le trait supérieur la sépare des options : ce n'en est pas une, on ne la choisit pas. */
.selecteur-note {
    position: sticky; bottom: -4px;
    margin: 4px -4px -4px; padding: 6px 8px;
    border-top: 1px solid var(--bord-discret);
    background: var(--surface-alt); color: var(--texte-doux);
    font-size: .76rem; cursor: default;
}

.bloc {
    background: var(--surface); border: 1px solid var(--bord);
    border-radius: var(--r-md); padding: var(--e-4);
    margin-bottom: var(--e-3);
    box-shadow: var(--ombre-1);
}
.bloc h3 {
    display: flex; align-items: center; gap: var(--e-2);
    margin-bottom: var(--e-3); padding-bottom: var(--e-2);
    border-bottom: 1px solid var(--bord-discret);
    color: var(--texte-doux); text-transform: uppercase;
    font-size: var(--t-2xs); letter-spacing: .07em; font-weight: 700;
}
.bloc-manquant { background: var(--n-25); border-color: var(--danger-bord); }

/* La colonne d'intitulés est resserrée et alignée en haut : à 148 px, un
   intitulé court laissait 90 px de vide entre lui et sa valeur, et l'œil
   perdait la correspondance d'une ligne à l'autre. */
.paires {
    display: grid; grid-template-columns: minmax(96px, max-content) 1fr;
    gap: var(--e-2) var(--e-4); margin: 0; font-size: var(--t-sm);
    align-items: baseline;
}
.paires dt { color: var(--texte-faible); font-size: var(--t-sm); }
.paires dd { margin: 0; display: flex; align-items: center; gap: var(--e-2); flex-wrap: wrap; min-width: 0; }

.table-imbriquee { width: 100%; border-collapse: collapse; font-size: .8rem; margin-top: 9px; }
.table-imbriquee th { text-align: left; padding: 5px 7px; border-bottom: 1px solid var(--bord); font-size: .69rem; text-transform: uppercase; letter-spacing: .04em; color: var(--texte-faible); font-weight: 700; }
.table-imbriquee th.num { text-align: right; }
.table-imbriquee td { padding: 5px 7px; border-bottom: 1px solid #f2f4f7; }
.table-imbriquee tr:last-child td { border-bottom: none; }

/* Hiérarchie mère / complément dans les contributions d'une ligne de stock.

   Le décalage et le chevron ne font que RENFORCER une information déjà écrite en
   toutes lettres sous le numéro (« complément de 3035 ») : seuls, ils ne diraient
   rien à un lecteur d'écran ni en niveaux de gris. Même règle que partout ici —
   jamais la forme seule, jamais la couleur seule.

   Le chevron est posé en `::before` absolu plutôt qu'en contenu de cellule : il
   n'a pas à être sélectionnable ni copié avec le numéro de commande. */
.table-imbriquee tr.ligne-rattachee > td:first-child { padding-left: 24px; position: relative; }
.table-imbriquee tr.ligne-rattachee > td:first-child::before {
    content: "\21b3"; position: absolute; left: 8px; top: 5px;
    color: var(--texte-faible); font-size: var(--t-xs);
}

/* Le total du dossier se colle à ses mouvements : il appartient au groupe qui le
   précède, pas à la ligne qui suit. D'où la bordure du dessous seule, et le
   retrait du filet interne au-dessus. */
.table-imbriquee tr.ligne-net-dossier > td {
    padding-top: 1px; padding-bottom: 7px; padding-left: 24px;
    font-size: var(--t-xs); color: var(--texte-faible);
    border-bottom: 1px solid var(--bord);
}
.table-imbriquee tr.ligne-rattachee > td { border-bottom: none; }
.table-imbriquee tr.ligne-net-dossier > td strong {
    color: var(--texte); font-variant-numeric: tabular-nums;
}

.table-financier td:first-child { color: var(--texte-doux); }

/* Même défaut que sur `.alerte > div` : en `display: flex`, chaque suite de texte
   devient un élément de la ligne, et un `<em>` ou un `<strong>` au milieu de la
   phrase se détachait sur sa propre colonne — « ce qui a été | commandé | . »
   dans les notes des blocs Hébergement et Financier. La note est une phrase :
   elle reste un bloc, et le marqueur de fiabilité y flotte en tête de ligne. */
.note-champ {
    margin-top: var(--e-3); padding-top: var(--e-2);
    border-top: 1px dashed var(--bord);
    font-size: var(--t-xs); color: var(--texte-doux); line-height: 1.55;
}
.note-champ > .badge-qualite { margin-right: var(--e-1); }

.note-securite {
    margin-top: var(--e-4); font-size: var(--t-xs);
    color: var(--texte-faible); line-height: 1.55; max-width: 90ch;
}

.liste-manquants { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
.liste-manquants li { display: grid; grid-template-columns: 18px 1fr; gap: 3px 8px; font-size: .82rem; }
.liste-manquants li > div { grid-column: 2; }

/* ------------------------------------------------------------------- divers */

.menu-deroulant { position: relative; }

/* Voile de fermeture d'un menu déroulant.
   -----------------------------------------------------------------------------
   Transparent et plein écran : il capte le clic « à l'extérieur » sans rien
   assombrir — un menu de barre d'outils n'est pas une boîte modale, le contenu
   derrière doit rester lisible.

   Il remplace `@onfocusout`, qui refermait le menu dès qu'on cochait une SECONDE
   case : `focusout` remonte aussi quand le focus passe d'un enfant à son voisin,
   et `FocusEventArgs` n'expose pas `relatedTarget` — rien ne permettait donc de
   distinguer « le focus quitte le menu » de « le focus se déplace dedans ».

   Son `z-index` se glisse juste sous celui du panneau (40) : le voile intercepte
   tout le reste de la page, jamais le menu qu'il sert à fermer. */
.voile-menu {
    position: fixed; inset: 0; z-index: 39;
    background: transparent;
    cursor: default;
}

.menu-contenu {
    position: absolute; top: calc(100% + var(--e-1)); right: 0; z-index: 40;
    min-width: 244px;
    background: var(--surface);
    border: 1px solid var(--bord-fort);
    border-radius: var(--r-md);
    box-shadow: var(--ombre-3);
    padding: var(--e-1);
    animation: apparition var(--duree) var(--ease-sortie);
}
.menu-ligne {
    display: flex; align-items: center; gap: 8px; width: 100%;
    padding: 7px 9px; border: none; background: none;
    font-size: .84rem; font-family: inherit; text-align: left; cursor: pointer;
    border-radius: var(--r-petit); color: var(--texte);
}
.menu-ligne:hover { background: var(--surface-alt); }
.menu-note {
    padding: var(--e-2) var(--e-3); font-size: var(--t-xs); color: var(--texte-faible);
    border-top: 1px solid var(--bord-discret); margin-top: var(--e-1); line-height: 1.5;
}
.menu-note a { margin-left: var(--e-1); }

/* Conséquence d'un choix, pas une erreur : le ton reste informatif. */
.menu-note-avertissement {
    display: block; margin-top: var(--e-2); padding-top: var(--e-2);
    border-top: 1px dashed var(--bord);
    color: var(--attention-texte);
}

/* L'état vide est une surface à part entière, pas un trou dans la page : sans
   cadre, il flottait au milieu d'un fond gris et se lisait comme un écran qui
   n'a pas fini de charger (`empty-states`). */
.etat-vide {
    text-align: center; padding: var(--e-12) var(--e-6);
    color: var(--texte-doux);
    background: var(--surface);
    border: 1px solid var(--bord);
    border-radius: var(--r-lg);
    box-shadow: var(--ombre-1);
}
.etat-vide h1, .etat-vide h2 { margin-bottom: var(--e-2); color: var(--texte); font-size: var(--t-xl); }
.etat-vide p { margin-bottom: var(--e-5); max-width: 56ch; margin-inline: auto; line-height: 1.65; }
.etat-vide-ico {
    display: grid; place-items: center;
    width: 48px; height: 48px; margin: 0 auto var(--e-4);
    border-radius: var(--r-lg);
    background: var(--marque-050); color: var(--marque-600);
    border: 1px solid var(--marque-100);
}

.detail-erreur { margin-top: 5px; }
.detail-erreur summary { cursor: pointer; font-size: .76rem; color: var(--bleu); }
.pile {
    margin: 6px 0 0; padding: 9px 11px;
    background: var(--nuit); color: #d6dce8;
    border-radius: var(--r-petit);
    font-family: "Cascadia Mono", Consolas, monospace; font-size: .72rem;
    line-height: 1.55; white-space: pre-wrap; word-break: break-word;
    max-height: 340px; overflow-y: auto;
}

/* --------------------------------------------------------------- connexion */

/* Le fond couvrait la largeur du `<main>` et non celle de l'écran : à 1 500 px,
   il restait deux bandes grises de part et d'autre du dégradé. La page prend
   donc toute la fenêtre, et le halo est posé en couches `radial-gradient`
   plutôt qu'en dégradé linéaire — ce qui donne de la profondeur sans animer
   quoi que ce soit. */
.page-connexion {
    display: grid; place-items: center;
    min-height: 100dvh; width: 100%;
    padding: var(--e-6);
    background:
        radial-gradient(1200px 600px at 15% -10%, rgba(182, 85, 47, .32), transparent 60%),
        radial-gradient(900px 500px at 100% 100%, rgba(37, 99, 235, .18), transparent 60%),
        linear-gradient(160deg, var(--n-900), var(--n-950));
}

.carte-connexion {
    width: min(410px, 100%);
    background: var(--surface);
    border-radius: var(--r-lg);
    padding: var(--e-8);
    box-shadow: 0 24px 64px rgba(0, 0, 0, .34), 0 2px 8px rgba(0, 0, 0, .18);
}
/* L'en-tête ne porte plus de marque : ni pastille, ni nom d'application, ni mention
   de l'événement couvert. L'écran étant ouvert à tous, il n'annonce que ce qu'on y
   fait. Le titre reprend donc la place et le poids qu'occupait le bloc de marque. */
.entete-connexion { margin-bottom: var(--e-6); }
.carte-connexion h1 { font-size: var(--t-xl); letter-spacing: -.01em; }
.sous-titre-connexion {
    margin-top: var(--e-1);
    font-size: var(--t-sm); color: var(--texte-faible); line-height: 1.5;
}

.champ { display: block; margin-bottom: var(--e-4); }
.champ > span {
    display: block; font-size: var(--t-sm); font-weight: 620;
    color: var(--texte-doux); margin-bottom: var(--e-1);
}
.champ input {
    width: 100%; padding: var(--e-2) var(--e-3);
    border: 1px solid var(--bord-fort); border-radius: var(--r-sm);
    font-size: var(--t-md); font-family: inherit;
    background: var(--surface); color: inherit;
    transition: border-color var(--duree-rapide), box-shadow var(--duree-rapide);
}
.champ input:hover:not(:disabled):not([readonly]) { border-color: var(--n-400); }

/* Les champs de saisie prennent l'anneau de la marque et non le bleu de focus
   global : c'est la couleur d'action de l'application, et elle distingue « je
   saisis ici » de « je suis arrivé ici au clavier ». */
.champ input:focus, .champ input:focus-visible {
    outline: none;
    border-color: var(--marque-500);
    box-shadow: 0 0 0 3px rgba(182, 85, 47, .16);
}

/* Un champ en lecture seule n'est pas un champ désactivé : il se lit, il se
   copie, il ne se modifie pas (`read-only-distinction`). */
.champ input[readonly] { background: var(--n-50); color: var(--texte-doux); }
.champ input:disabled { background: var(--n-100); color: var(--texte-faible); cursor: not-allowed; }

/* Le sélecteur de fichier est le contrôle principal de l'écran d'import, et il
   s'y rendait entièrement nu — bouton gris natif, texte à la police du système,
   sans rapport avec le reste de la page. Le bouton interne reprend donc l'aspect
   d'un `.btn` secondaire. Son libellé, lui, vient du navigateur et de sa langue :
   aucune règle CSS ne peut le traduire. */
.champ input[type="file"] {
    padding: var(--e-2);
    background: var(--n-50);
    cursor: pointer;
}
.champ input[type="file"]::file-selector-button {
    margin-right: var(--e-3);
    padding: 6px var(--e-3);
    border: 1px solid var(--bord-fort);
    border-radius: var(--r-sm);
    background: var(--surface);
    color: var(--texte);
    font: inherit; font-size: var(--t-sm); font-weight: 570;
    cursor: pointer;
    box-shadow: var(--ombre-1);
    transition: background var(--duree-rapide), border-color var(--duree-rapide);
}
.champ input[type="file"]::file-selector-button:hover {
    background: var(--n-50); border-color: var(--n-400);
}

.validation-message {
    display: block; color: var(--danger-texte); font-size: var(--t-xs);
    margin-top: var(--e-1); font-weight: 550;
}

/* ------------------------------------------------------------- responsive */

@media (max-width: 1180px) {
    .col-ville, .col-paiement { display: none; }
}

@media (max-width: 1280px) {
    .contenu { padding: var(--e-5) var(--e-5) var(--e-10); }
    .bandeau { padding-inline: var(--e-5); }
}

@media (max-width: 900px) {
    .appli { flex-direction: column; }
    .barre-laterale { width: 100%; flex: none; height: auto; position: static; }

    /* La navigation devient une barre défilante. Le liséré de l'élément courant
       passe en soulignement : à l'horizontale, un trait à gauche ne dit plus
       « ici », il se lit comme un séparateur entre deux liens. */
    .navigation { display: flex; overflow-x: auto; gap: var(--e-1); padding: var(--e-2); contain: paint; }
    .separateur-nav { display: none; }
    .lien-nav { white-space: nowrap; }
    .lien-nav.active::before {
        left: var(--e-3); right: var(--e-3); top: auto; bottom: 3px;
        width: auto; height: 2px; border-radius: 2px;
    }

    .pied-laterale { display: flex; gap: var(--e-3); align-items: center; justify-content: space-between; }
    .utilisateur { margin-bottom: 0; min-width: 0; }
    .pied-laterale form { flex: 0 0 auto; }

    /* Le bandeau garde la hauteur d'une seule ligne à toutes les largeurs : il ne
       porte plus que deux éléments, l'état et l'action. Ce sont eux qui se
       resserrent, plus le bandeau qui grandit. */
    .bandeau { padding-inline: var(--e-4); gap: var(--e-2); }
    .etat-synchro { padding-inline: var(--e-2); min-width: 0; }
    .etat-synchro-phase, .etat-synchro-age { overflow: hidden; text-overflow: ellipsis; }
    .panneau-etat { width: min(440px, calc(100vw - var(--e-6))); }

    .bandeau-actions { margin-left: auto; }

    /* Sur écran étroit, la barre latérale EST la navigation principale : ses liens
       se visent au doigt comme n'importe quel bouton. Ils restaient à 38 px, les
       règles tactiles ne portant que sur `.btn` et ses variantes. */
    .lien-nav { min-height: 44px; }
    .pied-laterale .btn { min-height: 44px; }

    .contenu { padding: var(--e-4) var(--e-4) var(--e-10); }
    .grille-deux, .grille-domaines { grid-template-columns: 1fr; }
    .paires { grid-template-columns: 1fr; gap: 0; }
    .paires dt { margin-top: var(--e-2); }
    .paires dd { margin-bottom: var(--e-1); }
}

@media (max-width: 560px) {
    .contenu { padding: var(--e-3) var(--e-3) var(--e-10); }
    .bandeau { padding-inline: var(--e-3); }
    .entete-page { align-items: flex-start; }
    .entete-actions { width: 100%; }

    /* Une bande de six chiffres à 390 px donnait six colonnes de 60 px, où le
       montant passait à la ligne au milieu. Deux colonnes lisibles valent mieux
       que six illisibles. */
    .barre-synthese { grid-template-columns: repeat(auto-fit, minmax(118px, 1fr)); }
    .synthese-valeur { font-size: var(--t-lg); }
}

/* ------------------------------------------------- administration des comptes */

.utilisateur-lien { display: block; color: inherit; text-decoration: none; border-radius: var(--r-petit); padding: 4px 6px; margin: -4px -6px 10px; }
.utilisateur-lien:hover { background: rgba(255, 255, 255, .06); text-decoration: none; }

.col-actions-u { width: 250px; }
.actions-ligne { display: flex; gap: 2px; flex-wrap: wrap; }

.tiroir-etroit { width: min(520px, 94vw); }
.tiroir-actions { display: flex; gap: 8px; align-items: center; margin-top: 4px; }
.tiroir-corps .bloc .champ:last-of-type { margin-bottom: 0; }

/* =============================================================================
   Brique Stock
   Les alertes ne reposent jamais sur la seule couleur : chaque état porte aussi
   un symbole et un libellé explicite.
   ============================================================================= */

.sync-idle .pastille { background: var(--texte-faible); }

/* ----------------------------------------------------- bandeau d'état du stock */

.bandeau-stock {
    display: flex; align-items: center; gap: 26px; flex-wrap: wrap;
    background: var(--surface); border: 1px solid var(--bord); border-left-width: 4px;
    border-radius: var(--r); padding: 11px 16px; margin-bottom: 14px;
    box-shadow: var(--ombre);
}
.bandeau-stock.sync-ok { border-left-color: var(--vert); }
.bandeau-stock.sync-busy { border-left-color: var(--bleu); }
.bandeau-stock.sync-warn { border-left-color: var(--ambre); }
.bandeau-stock.sync-error { border-left-color: var(--rouge); }
.bandeau-stock.sync-idle { border-left-color: var(--texte-faible); }
.bandeau-stock .bandeau-statut { flex: 0 0 auto; }
.bandeau-stock .bandeau-horloges { flex: 1 1 auto; }
.bandeau-stock .bandeau-actions { margin-left: auto; }

/* =============================================================================
   Les horloges sur écran étroit
   -----------------------------------------------------------------------------
   Mesuré à 390 px : le groupe d'horloges débordait de 105 px et faisait défiler
   la page entière. Deux causes, et il fallait les deux corrections.

   1. `width: 100%` ne suffit pas à faire passer un élément flex à la ligne : sa
      `flex-shrink` valant 1 par défaut, il se laisse comprimer à sa largeur
      minimale — mesuré à 110 px — et reste sur la première ligne, à côté du
      statut et du bouton. Il faut `flex: 0 0 100%` pour qu'il refuse de
      rétrécir, et provoque donc le retour à la ligne.

   2. En ligne flex, quatre horloges ne se replient pas entre elles. En grille,
      elles passent à la ligne d'elles-mêmes. Les filets verticaux qui les
      séparaient n'ont alors plus de sens et disparaissent.

   Le bloc est placé ici, et non dans la requête média générale plus haut :
   `.bandeau-stock .bandeau-horloges` compte deux classes et vit à la ligne
   précédente. À spécificité égale c'est la dernière déclaration qui gagne — une
   règle écrite plus haut aurait été muette sur l'écran des stocks, et le défaut
   n'aurait persisté que là.
   ============================================================================= */
@media (max-width: 900px) {
    /* La règle ne porte plus que sur le bandeau de stock : celui de l'application
       n'a plus d'horloges en ligne, elles vivent dans son panneau de détail — qui
       est déjà une grille et n'a pas à passer en `flex: 0 0 100%`. */
    .bandeau-stock .bandeau-horloges {
        display: grid;
        grid-template-columns: repeat(auto-fit, minmax(146px, 1fr));
        gap: var(--e-2) var(--e-4);
        flex: 0 0 100%;
        order: 3;
    }
    .bandeau-stock .horloge { padding-left: 0; border-left: none; }
}

/* ------------------------------------------------------------ badge d'état */

.badge-etat {
    display: inline-flex; align-items: center; gap: 5px;
    padding: 2px 8px 2px 6px; border-radius: 3px;
    font-size: .72rem; font-weight: 650; white-space: nowrap;
    border: 1px solid transparent;
}
.badge-etat-symbole { font-size: .68rem; line-height: 1; }
.etat-disponible { background: var(--vert-clair); color: var(--vert); border-color: #c9e6d6; }
.etat-tendu { background: var(--ambre-clair); color: var(--ambre); border-color: #f0dcae; }
.etat-epuise { background: #eceef2; color: var(--texte); border-color: var(--bord-fort); }
.etat-survente { background: var(--rouge-clair); color: var(--rouge); border-color: #efc9c6; }
.etat-inconnu { background: #f1eefa; color: var(--violet); border-color: #ddd6f2; }
.etat-nondote { background: transparent; color: var(--texte-faible); border-color: var(--bord); }

/* ------------------------------------------------------- synthèse par domaine */

.grille-domaines {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(480px, 1fr));
    gap: 18px; margin-bottom: 20px;
}
.carte-domaine {
    background: var(--surface); border: 1px solid var(--bord);
    border-radius: var(--r-lg); padding: var(--e-5); box-shadow: var(--ombre-1);
}
.carte-domaine-entete {
    display: flex; align-items: center; justify-content: space-between;
    gap: var(--e-3); margin-bottom: var(--e-4);
    padding-bottom: var(--e-3); border-bottom: 1px solid var(--bord-discret);
}
.carte-domaine-entete > div { display: flex; align-items: center; gap: var(--e-3); }
.carte-domaine-entete h2 { margin: 0; font-size: var(--t-lg); }

/* L'icône du domaine est posée dans une pastille : elle nomme la section d'un
   coup d'œil dans une page qui en porte deux côte à côte. */
.carte-domaine-ico {
    display: grid; place-items: center;
    width: 32px; height: 32px; flex: 0 0 32px;
    border-radius: var(--r);
    background: var(--marque-050); border: 1px solid var(--marque-100);
    color: var(--marque-600);
}

.barre-synthese-domaine { margin-bottom: 10px; }
.synthese-item-fort { background: var(--surface-alt); }
.synthese-item-fort .synthese-valeur { color: var(--terre); }

.jauge-domaine { margin-bottom: 14px; }
.jauge-inconnue { background: repeating-linear-gradient(135deg, #eceef2, #eceef2 4px, #e2e5ec 4px, #e2e5ec 8px); }
.barre-mini.jauge-verte span { background: var(--vert); }
.barre-mini.jauge-jaune span { background: #c99a1e; }
.barre-mini.jauge-orange span { background: #d1791f; }
.barre-mini.jauge-rouge span { background: var(--rouge); }
.barre-mini.jauge-inconnue { background: repeating-linear-gradient(135deg, #eceef2, #eceef2 3px, #e2e5ec 3px, #e2e5ec 6px); }

/* ------------------------------------------------------------- indicateurs */

.grille-indicateurs {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(146px, 1fr));
    gap: var(--e-2); margin-bottom: var(--e-4);
}

/* Un indicateur est un bouton : il filtre le tableau. L'affordance passe par le
   survol (fond blanc, bordure marquée, ombre) et non par un aspect de bouton,
   qui alourdirait une grille de six. */
.indicateur {
    display: flex; flex-direction: column; align-items: flex-start; gap: 0;
    background: var(--n-50); border: 1px solid var(--bord);
    border-radius: var(--r); padding: var(--e-3);
    font-family: inherit; text-align: left; cursor: pointer;
    transition: border-color var(--duree-rapide), background var(--duree-rapide),
                box-shadow var(--duree-rapide);
}
.indicateur:hover { border-color: var(--n-400); background: var(--surface); box-shadow: var(--ombre-1); }
.indicateur-symbole { font-size: var(--t-xs); color: var(--texte-faible); line-height: 1; }
.indicateur-valeur {
    font-family: var(--police-affichage);
    font-size: var(--t-2xl); font-weight: 650;
    font-variant-numeric: tabular-nums; line-height: 1.15; letter-spacing: -.025em;
    margin-top: 2px;
}
.indicateur-libelle { font-size: var(--t-xs); color: var(--texte-doux); font-weight: 600; line-height: 1.35; }
.indicateur-note { font-size: var(--t-2xs); color: var(--texte-faible); margin-top: 3px; line-height: 1.35; }

/* Un indicateur à zéro reste neutre : teinter « 0 survente » en rouge ferait
   lire une alerte là où tout va bien. La teinte est donc posée par la page,
   conditionnée au compte, et jamais par la classe de base. */
.indicateur-critique { background: var(--danger-fond); border-color: var(--danger-bord); }
.indicateur-critique .indicateur-valeur, .indicateur-critique .indicateur-symbole { color: var(--danger-texte); }
.indicateur-alerte { background: var(--danger-fond); border-color: var(--danger-bord); }
.indicateur-alerte .indicateur-valeur { color: var(--marque-700); }
.indicateur-attention { background: var(--attention-fond); border-color: var(--attention-bord); }
.indicateur-attention .indicateur-valeur, .indicateur-attention .indicateur-symbole { color: var(--attention-texte); }
.indicateur-inconnu { background: var(--accent-fond); border-color: var(--accent-bord); }
.indicateur-inconnu .indicateur-valeur, .indicateur-inconnu .indicateur-symbole { color: var(--accent-texte); }
.indicateur-info { background: var(--info-fond); border-color: var(--info-bord); }
.indicateur-info .indicateur-valeur, .indicateur-info .indicateur-symbole { color: var(--info-texte); }

.paires-compactes { font-size: var(--t-sm); }

/* ------------------------------------------------------ pools de chambres */

.rappel-pools {
    display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
    padding: 8px 12px; margin-bottom: 12px;
    background: var(--surface-alt); border: 1px solid var(--bord); border-radius: var(--r-petit);
}
.rappel-titre { font-size: .74rem; text-transform: uppercase; letter-spacing: .05em; font-weight: 700; color: var(--texte-doux); }

.pastille-pool {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 2px 8px 2px 4px; border-radius: 3px; white-space: nowrap;
    border: 1px solid transparent; font-size: .72rem;
}
.pastille-pool-code { font-weight: 700; font-size: .68rem; letter-spacing: .03em; }
.pastille-pool-libelle { font-weight: 600; }
.pastille-pool-sgdb { background: var(--bleu-clair); color: var(--bleu); border-color: #cfe0f2; }
.pastille-pool-tw { background: #f1eefa; color: var(--violet); border-color: #ddd6f2; }
.pastille-pool-tr { background: #eceef2; color: var(--texte-doux); border-color: var(--bord-fort); }
.pastille-pool-autre { background: var(--ambre-clair); color: var(--ambre); border-color: #f0dcae; }

/* Les quatre bascules, et pas deux. « Tr » manquait depuis l'origine : sa puce active restait
   au style par défaut, donc indiscernable de l'inactive sur le seul filtre où la couleur porte
   l'information. Le trou ne se voyait qu'en cochant Triple, ce que personne ne faisait. */
.bascule-pool-sgdb.bascule-active { background: var(--bleu); border-color: var(--bleu); }
.bascule-pool-tw.bascule-active { background: var(--violet); border-color: var(--violet); }
.bascule-pool-tr.bascule-active { background: var(--texte-doux); border-color: var(--texte-doux); }
.bascule-pool-autre.bascule-active { background: var(--ambre); border-color: var(--ambre); }

/* ------------------------------------------------------ colonnes du tableau */

.col-seance { width: 170px; }
.col-categorie { width: 120px; }
.col-capacite, .col-consomme, .col-restant { width: 104px; }
.col-etat { width: 168px; }
.col-pool { width: 148px; }
.col-hotel-stock { width: 190px; }
.col-enseigne { width: 220px; }

.marque-ajuste { color: var(--ambre); font-weight: 700; margin-left: 3px; cursor: help; }

/* --------------------------------------------- détail : calcul et ajustement */

.tableau-calcul { width: 100%; border-collapse: collapse; margin-bottom: 12px; font-size: .84rem; }
.tableau-calcul th { text-align: left; padding: 6px 8px; font-weight: 600; color: var(--texte-doux); white-space: nowrap; }
.tableau-calcul td { padding: 6px 8px; font-variant-numeric: tabular-nums; }
.tableau-calcul .num { text-align: right; width: 86px; font-weight: 650; }
.tableau-calcul .cellule-note { color: var(--texte-faible); font-size: .76rem; }
.tableau-calcul .ligne-soustraction th, .tableau-calcul .ligne-soustraction td { border-bottom: 1px solid var(--bord); }
.tableau-calcul .ligne-total { background: none; }
.tableau-calcul .ligne-total th, .tableau-calcul .ligne-total td { padding-top: 8px; font-size: .92rem; }
.tableau-calcul .ligne-total td strong { font-size: 1.1rem; }

.bloc-ajustement { border-color: #e6d9c2; background: #fffdf8; }
.grille-ajustement { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 10px 14px; }
.grille-ajustement .champ { margin-bottom: 6px; }
.champ-large { grid-column: 1 / -1; }
.champ-case { display: flex; align-items: center; gap: 7px; align-self: end; padding-bottom: 8px; font-size: .8rem; }
.champ-case > span { margin: 0; font-weight: 600; color: var(--texte-doux); }
.champ textarea {
    width: 100%; padding: 8px 10px; font-family: inherit; font-size: .87rem;
    border: 1px solid var(--bord-fort); border-radius: var(--r-petit); resize: vertical;
}
.champ textarea:focus { outline: none; border-color: var(--terre); box-shadow: 0 0 0 3px rgba(182, 85, 47, .13); }
.obligatoire { color: var(--rouge); font-weight: 700; font-size: .72rem; text-transform: uppercase; letter-spacing: .03em; }

.apercu-impact {
    background: var(--surface); border: 1px dashed var(--bord-fort);
    border-radius: var(--r-petit); padding: 10px 12px; margin: 10px 0 12px;
}
.apercu-titre { font-size: .72rem; text-transform: uppercase; letter-spacing: .05em; font-weight: 700; color: var(--texte-doux); margin-bottom: 8px; }
.apercu-lignes { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 8px 16px; }
.apercu-item { display: flex; flex-direction: column; gap: 2px; }
.apercu-libelle { font-size: .72rem; color: var(--texte-faible); font-weight: 600; }
.apercu-valeur { font-size: .86rem; font-variant-numeric: tabular-nums; display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }

.cellule-motif { max-width: 340px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bloc-motif { display: block; margin-top: 4px; font-style: italic; }

/* ------------------------------------------------------------- navigation */

.lien-nav-fils { padding-left: 26px; font-size: .82rem; }

/* Réconciliation des unités : rend visible pourquoi le total du stock diffère du nombre
   de billets ou de chambres, au lieu de laisser l'écart surprendre. */

.explication-unites {
    display: flex; flex-direction: column; gap: 4px;
    padding: 8px 11px; margin-bottom: 12px;
    background: var(--bleu-clair); border: 1px solid #cfe0f2;
    border-radius: var(--r-petit);
}
.explication-titre {
    font-size: .71rem; text-transform: uppercase; letter-spacing: .04em;
    font-weight: 700; color: var(--bleu);
}
.explication-calcul {
    font-size: var(--t-sm); color: var(--texte); line-height: 1.6;
    font-variant-numeric: tabular-nums;
}

/* Le nombre et son unité se touchaient — « 487nuitées », « 141unités de
   demi-finale », « 3 530unités ». Razor supprime l'espace en tête d'une ligne
   qui commence par une transition `@`, et l'unité est justement écrite sur la
   ligne suivante du bloc `@if`. La séparation est donc posée ici, une fois,
   plutôt que par un `&nbsp;` à recopier à chaque terme de la somme — où celui
   qu'on oublie ne se voit qu'à l'exécution, et seulement si le cas se produit. */
.explication-calcul strong { font-weight: 680; margin-right: .25em; }
.explication-op { color: var(--info-texte); font-weight: 700; margin: 0 var(--e-1); }

.explication-reserve {
    display: block; margin-top: 5px; padding-top: 5px;
    border-top: 1px solid #cfe0f2;
    font-size: .77rem; color: var(--texte-doux); line-height: 1.5;
}
.explication-reserve strong { color: var(--texte); font-weight: 660; }

/* ---------------------------------------------------------------------------
   Arbitrage d'une cellule ambiguë et complétion guidée d'un UpSell.

   Chaque arbitrage est un bloc autonome : la valeur d'origine, l'impact de
   chaque choix possible, et le choix retenu. Rien n'est présélectionné, donc
   rien ne doit avoir l'apparence d'un défaut.
   --------------------------------------------------------------------------- */
.bloc-arbitrage {
    padding: 12px 14px; margin-top: 10px;
    background: var(--surface); border: 1px solid var(--bord-fort);
    border-left: 3px solid var(--ambre);
    border-radius: var(--r-petit);
}
.bloc-arbitrage + .bloc-arbitrage { margin-top: 14px; }

/* Texte repris verbatim du tableau de suivi : information, jamais donnée d'entrée. */
.bloc-citation {
    padding: 10px 12px;
    background: var(--surface-alt); border: 1px solid var(--bord);
    border-left: 3px solid var(--texte-faible);
    border-radius: var(--r-petit);
    font-size: .86rem; line-height: 1.55; color: var(--texte-doux);
    font-style: italic;
}

/* Nuits dotées, cochées une à une : une plage libre laisserait choisir une
   nuit que personne n'a allouée. */
.grille-nuits {
    display: flex; flex-wrap: wrap; gap: 6px; margin-top: 4px;
}
.nuit {
    display: flex; flex-direction: column; align-items: center; gap: 1px;
    min-width: 78px; padding: 6px 8px;
    background: var(--surface); border: 1px solid var(--bord-fort);
    border-radius: var(--r-petit);
    font-family: inherit; font-size: .78rem; cursor: pointer;
    transition: background .1s, border-color .1s, color .1s;
}
.nuit:hover { border-color: var(--texte-faible); }
.nuit.active { background: var(--bleu); border-color: var(--bleu); color: #fff; }
.nuit-date { font-weight: 600; text-transform: capitalize; }
.nuit-restant { font-size: .69rem; opacity: .7; font-variant-numeric: tabular-nums; }
.nuit-tendue { border-color: #edd9d7; background: var(--rouge-clair); }
.nuit-tendue.active { background: var(--rouge); border-color: var(--rouge); color: #fff; }

/* ---------------------------------------------------------------------------
   Vente hors allocation : une survente dont la dotation connue vaut zéro.
   Même famille chromatique que la survente — c'en est une — mais un liséré
   plus marqué, car la cause est différente et la décision aussi.
   --------------------------------------------------------------------------- */
.etat-hors-allocation { box-shadow: inset 0 0 0 1px var(--rouge); font-weight: 660; }

/* Arbitrage d'un doublon probable : comparaison offline / Magento côte à côte. */
.bloc-arbitrage-prioritaire { border-left-color: var(--rouge); background: var(--rouge-clair); }

.entete-arbitrage {
    display: flex; align-items: center; justify-content: space-between;
    gap: 10px; flex-wrap: wrap; margin-bottom: 8px;
}
.entete-arbitrage > div { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }

.table-comparaison th:first-child {
    color: var(--texte-faible); font-weight: 600; text-transform: none;
    letter-spacing: 0; font-size: .78rem; white-space: nowrap;
}
.table-comparaison td { vertical-align: top; }
.cellule-identique { background: var(--ambre-clair); font-weight: 620; }

/* Donnée absente : c'est elle qui empêche de comparer les identités, donc elle se voit. */
.cellule-absente { color: var(--texte-faible); font-style: italic; }

/* Bilan chiffré d'un import : rendues, consommées, net. */
.table-bilan td:first-child { color: var(--texte-doux); }
.table-bilan .ligne-total td {
    border-top: 2px solid var(--bord-fort); padding-top: 8px;
    font-variant-numeric: tabular-nums;
}

/* Dérogation administrateur : jamais confondue avec une case à cocher ordinaire. */
.champ-derogation {
    display: flex; align-items: flex-start; gap: 8px; margin-top: 10px;
    padding: 9px 11px; background: var(--rouge-clair);
    border: 1px solid #edd9d7; border-radius: var(--r-petit);
    font-size: .82rem; line-height: 1.5;
}

/* ==========================================================================
   Import offline — refonte de la navigation.

   L'écran empilait quatorze sections toujours ouvertes : dix-huit mille sept
   cent cinquante-quatre pixels de haut, soit vingt écrans, et deux cents
   pixels de débordement horizontal. Le bouton d'import se trouvait vingt
   écrans après les premières cases à cocher.

   Trois leviers, dans cet ordre d'effet : une file à la fois (onglets), la
   décision toujours atteignable (barre d'action collante), le diagnostic
   replié (divulgation progressive).
   ========================================================================== */

/* Échelle de plans. Le bandeau de l'application occupe 30 : tout ce qui suit
   passe dessous, pour qu'il reste lisible en défilement. */
:root {
    --z-onglets: 20;
    --z-action: 25;
}

/* Garde-fou de débordement : aucune largeur de tableau ne doit plus pousser la
   page entière. Le défilement horizontal reste confiné à son cadre. */
.contenu { overflow-x: clip; }

/* ------------------------------------------------------------ barre de contexte */

.barre-contexte {
    display: flex; align-items: center; justify-content: space-between;
    gap: 16px; flex-wrap: wrap;
    padding: 10px 14px; margin-bottom: 12px;
    background: var(--surface);
    border: 1px solid var(--bord);
    border-radius: var(--r);
    box-shadow: var(--ombre);
}

.contexte-fichier { min-width: 0; display: flex; flex-direction: column; gap: 2px; }

.contexte-nom {
    font-weight: 600; font-size: .88rem;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    max-width: 60ch;
}

.contexte-detail { font-size: .77rem; color: var(--texte-doux); }
.contexte-actions { display: flex; gap: 6px; align-items: center; flex-shrink: 0; }

/* ------------------------------------------------------------------- onglets */

.onglets-files {
    display: flex; gap: 4px; align-items: stretch;
    padding: 4px; margin-bottom: 0;
    background: var(--surface);
    border: 1px solid var(--bord);
    border-radius: var(--r) var(--r) 0 0;
    border-bottom: none;
    overflow-x: auto;                 /* la barre défile, la page non */
    position: sticky; top: var(--hauteur-bandeau); z-index: var(--z-onglets);
}

.onglet {
    display: inline-flex; align-items: center; gap: var(--e-2);
    padding: var(--e-2) var(--e-3);
    border: 1px solid transparent;
    border-radius: var(--r-sm);
    background: transparent;
    color: var(--texte-doux);
    font-family: inherit; font-size: var(--t-sm); font-weight: 570;
    white-space: nowrap; cursor: pointer;
    transition: background var(--duree-rapide), color var(--duree-rapide),
                border-color var(--duree-rapide);
}

.onglet:hover:not(.onglet-actif) { background: var(--n-100); color: var(--texte); }

.onglet-actif {
    background: var(--n-900); border-color: var(--n-900); color: #fff;
    font-weight: 640;
    box-shadow: var(--ombre-1);
}

.onglet-picto { line-height: 1; opacity: .85; display: inline-flex; }

/* --- zones du stock hôtelier : événement / hors stock avant / hors stock après ---------
   Les deux bords portent un liseré d'attention. Jamais la couleur seule : le libellé de
   l'onglet dit déjà « Hors stock », et la colonne Période le répète sur chaque ligne. */
.onglet.periode-avant:not(.onglet-actif),
.onglet.periode-apres:not(.onglet-actif) {
    border-color: var(--attention-bord);
    color: var(--attention-texte);
}

.onglet.periode-avant.onglet-actif,
.onglet.periode-apres.onglet-actif {
    background: var(--attention-texte); border-color: var(--attention-texte);
}

.col-periode { width: 1%; white-space: nowrap; }

/* Le formulaire de création vit au-dessus de la liste, pas dans un tiroir latéral : il
   crée une ligne qui n'existe pas encore, donc il n'a aucune ligne à côté de laquelle se
   placer. */
.bloc-creation { margin-bottom: var(--e-5); }

.bloc-titre-action {
    display: flex; align-items: baseline; justify-content: space-between;
    gap: var(--e-3); margin-bottom: var(--e-3);
}

.bloc-titre-action h2 { margin: 0; font-size: var(--t-lg); }

.onglet-compteur {
    padding: 1px var(--e-2); border-radius: var(--r-pilule);
    background: var(--n-100); border: 1px solid var(--n-200);
    font-size: var(--t-2xs); font-weight: 700; font-variant-numeric: tabular-nums;
}
.onglet-actif .onglet-compteur { border-color: rgba(255, 255, 255, .14); }

.onglet-actif .onglet-compteur { background: rgba(255, 255, 255, .18); }

/* Nombre déjà retenu dans cette file : évite d'ouvrir chaque onglet pour
   savoir où l'on a coché. */
.onglet-coches {
    font-size: .7rem; font-weight: 700; color: var(--vert);
    font-variant-numeric: tabular-nums;
}
.onglet-actif .onglet-coches { color: #8fe0b4; }

/* La couleur redouble le sens porté par le libellé et la marque : jamais seule. */
.onglet-vert:not(.onglet-actif) .onglet-picto { color: var(--vert); }
.onglet-ambre:not(.onglet-actif) .onglet-picto { color: var(--ambre); }
.onglet-rouge:not(.onglet-actif) .onglet-picto { color: var(--rouge); }
.onglet-vert.onglet-actif { background: var(--vert); border-color: var(--vert); }
.onglet-rouge.onglet-actif { background: var(--rouge); border-color: var(--rouge); }

.panneau-file {
    padding: 16px;
    background: var(--surface);
    border: 1px solid var(--bord);
    border-radius: 0 0 var(--r) var(--r);
    box-shadow: var(--ombre);
    margin-bottom: 16px;
}
.panneau-file:focus-visible { outline: 2px solid var(--bleu); outline-offset: -2px; }

/* --------------------------------------------------------------------- plis */

.pli {
    border: 1px solid var(--bord);
    border-radius: var(--r);
    margin-bottom: 10px;
    background: var(--surface-alt);
}

.pli > summary {
    display: flex; align-items: center; justify-content: space-between;
    gap: 12px; flex-wrap: wrap;
    padding: 10px 13px;
    cursor: pointer;
    list-style: none;
    border-radius: var(--r);
}
.pli > summary::-webkit-details-marker { display: none; }
/* Le chevron est dessiné, pas typographié : « ▸ » n'a ni la même graisse ni le
   même centrage optique que les icônes du reste de l'interface, et son dessin
   change d'une police à l'autre. Une image `data:` reste dans le document, donc
   la CSP l'accepte. */
.pli > summary::before {
    content: ""; flex: 0 0 auto;
    width: 12px; height: 12px;
    background: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 20 20'%3E%3Cpath fill='none' stroke='%237d8798' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' d='M8 5l5 5-5 5'/%3E%3C/svg%3E") center / contain no-repeat;
    transition: transform var(--duree-rapide);
}
.pli[open] > summary::before { transform: rotate(90deg); }
.pli > summary:hover { background: var(--n-100); }

.pli-titre { flex: 1; font-weight: 600; font-size: .85rem; min-width: 0; }
.pli-resume { font-size: .76rem; color: var(--texte-doux); font-variant-numeric: tabular-nums; }
.pli-resume-alerte { color: var(--rouge); font-weight: 650; }

.pli-corps { padding: 0 13px 13px; background: var(--surface); border-radius: 0 0 var(--r) var(--r); }
.pli-imbrique { background: var(--surface); margin-top: 10px; }
.pli-aide { background: transparent; border-style: dashed; }

/* Tout tableau imbriqué reçoit son propre cadre défilant. Sans lui, une largeur
   de contenu poussait la page : c'est l'origine des deux cents pixels de
   débordement horizontal relevés à 1440 px. */
.cadre-defilant { overflow-x: auto; max-width: 100%; }

/* ------------------------------------------------------------ tri par en-tête */

.tri-entete {
    display: inline-flex; align-items: center; gap: 5px;
    background: none; border: none; padding: 0;
    font: inherit; color: inherit; letter-spacing: inherit; text-transform: inherit;
    cursor: pointer;
}
.tri-entete:hover { color: var(--texte); }
.tri-entete:focus-visible { outline: 2px solid var(--bleu); outline-offset: 2px; border-radius: 2px; }
.tri-fleche { font-size: .7rem; color: var(--terre); }

.tableau-files thead th[aria-sort="ascending"] .tri-entete,
.tableau-files thead th[aria-sort="descending"] .tri-entete { color: var(--terre); }

.col-coche { width: 44px; }
.col-mere { max-width: 220px; }
.col-traiter { max-width: 300px; }

.actions-ligne { display: flex; gap: 4px; align-items: center; justify-content: flex-end; }
.btn-plier { min-width: 30px; justify-content: center; }

.filtres-nature { display: flex; gap: 5px; align-items: center; }
.tiroir-selection { margin-bottom: 10px; }

/* ------------------------------------------------------- ligne de détail dépliée */

.ligne-depliee td { border-bottom-color: transparent; }
.ligne-depliee { background: var(--surface-alt); }

.ligne-detail { background: #fbfcfd; }
.ligne-detail > td { padding: 0 !important; }
.ligne-detail:hover { background: #fbfcfd; }

.detail-grille {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: 14px 22px;
    padding: 14px 16px 16px;
    border-left: 3px solid var(--terre-clair);
}

.detail-bloc { min-width: 0; }
.detail-bloc-large { grid-column: 1 / -1; }
.detail-bloc h4 {
    margin: 0 0 6px;
    font-size: .71rem; text-transform: uppercase; letter-spacing: .05em;
    font-weight: 700; color: var(--texte-faible);
}
.detail-bloc p { font-size: .82rem; line-height: 1.55; margin-bottom: 4px; }

.detail-liste { display: grid; grid-template-columns: auto 1fr; gap: 2px 12px; margin: 0; font-size: .82rem; }
.detail-liste dt { color: var(--texte-faible); }
.detail-liste dd { margin: 0; font-weight: 550; }

/* La note du fichier, entière. Elle était tronquée à soixante-dix caractères
   dans la cellule — or c'est souvent le seul endroit où la prestation est dite. */
.detail-note {
    margin-top: 9px; padding: 8px 10px;
    background: var(--ambre-clair);
    border-left: 3px solid var(--ambre);
    border-radius: 0 var(--r-petit) var(--r-petit) 0;
    font-size: .81rem !important; line-height: 1.55; white-space: pre-wrap;
}
.detail-note-etiquette {
    display: block;
    font-size: .68rem; text-transform: uppercase; letter-spacing: .05em;
    font-weight: 700; color: var(--ambre); margin-bottom: 2px;
}

.detail-blocages, .detail-alertes { margin: 0; padding-left: 18px; font-size: .82rem; line-height: 1.6; }
.detail-blocages li { margin-bottom: 3px; }
.detail-alertes li { color: var(--rouge); }

/* ------------------------------------------------------- barre d'action collante */

.barre-action {
    display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
    padding: 11px 16px;
    background: var(--surface);
    border: 1px solid var(--bord-fort);
    border-radius: var(--r);
    box-shadow: 0 -4px 20px rgba(16, 24, 42, .09);
    position: sticky; bottom: 12px; z-index: var(--z-action);
    margin-bottom: 12px;
}

.action-etat { display: flex; align-items: baseline; gap: 9px; flex-shrink: 0; }
.action-compte {
    font-size: 1.5rem; font-weight: 680; line-height: 1;
    font-variant-numeric: tabular-nums; letter-spacing: -.02em;
    color: var(--terre);
}
.action-detail { font-size: .78rem; color: var(--texte-doux); line-height: 1.4; max-width: 34ch; }

.action-motif { flex: 1; min-width: 240px; }
.action-motif input {
    width: 100%; padding: 8px 11px;
    border: 1px solid var(--bord-fort); border-radius: var(--r-petit);
    font-size: .84rem; font-family: inherit; background: var(--surface);
}
.action-motif input:focus-visible { outline: none; border-color: var(--bleu); box-shadow: 0 0 0 3px var(--bleu-clair); }

.action-boutons { display: flex; align-items: center; gap: 10px; flex-shrink: 0; }
.action-boutons .validation-message { max-width: 28ch; }

/* --------------------------------------------------------------- accessibilité */

/* Étiquette réservée aux lecteurs d'écran : une colonne de cases à cocher a
   besoin d'un nom, sans que ce nom encombre l'en-tête. */
.lecteur-seul {
    position: absolute; width: 1px; height: 1px;
    padding: 0; margin: -1px; overflow: hidden;
    clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

/* Anneau de focus visible partout : la navigation au clavier de cet écran passe
   par sept onglets, six en-têtes triables et une case par ligne. */
.onglet:focus-visible,
.bascule:focus-visible,
.btn:focus-visible,
.lien-mini:focus-visible,
.btn-vider:focus-visible {
    outline: 2px solid var(--bleu); outline-offset: 2px;
}
.tableau-files tbody input[type="checkbox"]:focus-visible {
    outline: 2px solid var(--bleu); outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
    .pli > summary::before, .onglet, .tri-entete { transition: none; }
}

/* ------------------------------------------------------------------ étroitesse */

/* Portable et tablette : le panneau perd ses marges intérieures superflues, la
   barre d'action passe sur deux niveaux plutôt que de repousser son bouton hors
   du cadre. */
@media (max-width: 1024px) {
    .panneau-file { padding: 12px; }
    .action-detail { max-width: none; }
    .action-motif { min-width: 180px; }
}

@media (max-width: 760px) {
    .barre-contexte { padding: 9px 11px; }
    .contexte-nom { max-width: 100%; }
    .onglets-files { position: static; border-radius: var(--r); border-bottom: 1px solid var(--bord); }
    .panneau-file { border-radius: var(--r); margin-top: 8px; padding: 10px; }

    /* La barre d'action se déploie sur toute la largeur et garde son bouton en
       pleine largeur : à trois cent quatre-vingt-dix pixels, un bouton aligné à
       droite d'un champ de motif sortait de l'écran. */
    .barre-action { flex-direction: column; align-items: stretch; gap: 9px; bottom: 0; border-radius: var(--r) var(--r) 0 0; }
    .action-motif { min-width: 0; }
    .action-boutons { flex-direction: column-reverse; align-items: stretch; }
    .action-boutons .btn { justify-content: center; }

    .detail-grille { grid-template-columns: 1fr; padding: 11px 12px; }
    .pli > summary { padding: 9px 11px; }
    .pli-corps { padding: 0 11px 11px; }
}

/* ------------------------------------------------------ cibles de pointage */

/* Mesuré dans le navigateur : le bouton de tri ne faisait que 58 × 14 px et la case à cocher
   13 × 13 px. Sur quatre-vingt-six lignes, viser treize pixels produit des clics manqués — et
   un clic manqué sur une case à cocher, c'est une commande reprise ou oubliée par accident.
   La cellule entière devient la cible, sans que la mise en page bouge. */

.th-triable { padding: 0 !important; }

.th-triable .tri-entete {
    width: 100%;
    min-height: 34px;
    padding: 9px 11px;
    justify-content: flex-start;
}
.th-triable.num .tri-entete { justify-content: flex-end; }
.th-triable .tri-entete:hover { background: rgba(16, 24, 42, .04); }
.th-triable .tri-entete:focus-visible { outline-offset: -2px; }

.case-cellule {
    display: flex; align-items: center; justify-content: center;
    min-height: 36px;
    margin: -8px -11px; padding: 8px 11px;      /* absorbe la gouttière de la cellule */
    cursor: pointer;
}
.case-cellule input[type="checkbox"] { width: 16px; height: 16px; cursor: pointer; }
.case-cellule:hover input[type="checkbox"] { outline: 2px solid var(--bleu-clair); }

/* ------------------------------------------------- confinement de la barre d'onglets */

/* Mesuré à 390 px : la barre d'onglets défile bien à l'intérieur d'elle-même (362 px de large
   pour 1134 px de contenu), mais son débordement horizontal remontait malgré tout jusqu'au
   document, qui se mettait à défiler sur 1007 px — la page entière glissait latéralement alors
   qu'aucun élément ne dépassait.

   Ni overflow-y: hidden, ni max-width: 100%, ni min-width: 0 n'y changent quoi que ce soit :
   seule une boîte de confinement arrête la propagation. `contain: paint` la crée, sans rien
   changer au rendu ni au défilement interne de la barre. */
.onglets-files { contain: paint; }

/* =============================================================================
   Tiroir long : la décision reste sous les yeux
   -----------------------------------------------------------------------------
   Le tiroir de correction porte huit sections, deux tableaux de lignes et un
   aperçu d'impact : il dépasse largement une hauteur d'écran. Les actions
   vivaient tout en bas du corps défilant, ce qui produisait deux défauts
   mesurables — il fallait parcourir tout le formulaire pour enregistrer, et
   l'état de validation (« 3 points à corriger ») n'était visible qu'à cet
   endroit-là, donc jamais pendant la saisie.

   La barre est donc collée au bas du corps défilant. Elle reprend l'idiome de
   `.barre-action` déjà en place ailleurs, et surtout son `--z-action` (25) :
   c'est ce qui laisse les listes de `.selecteur-liste` (z-index 30) flotter
   par-dessus. Un z-index plus élevé ferait passer la barre devant les listes
   déroulantes ouvertes en bas de formulaire, qui deviendraient inutilisables.
   ============================================================================= */

/* La marge latérale du corps devient une variable : les marges négatives de la
   barre collante doivent la suivre exactement à chaque palier, sinon la barre
   ne couvre plus toute la largeur et laisse voir le contenu défiler dessous. */
.tiroir-corps {
    --tiroir-marge: 22px;
    padding: 16px var(--tiroir-marge) 0;
}

.tiroir-actions-collees {
    position: sticky;
    bottom: 0;
    z-index: var(--z-action);
    margin: 18px calc(-1 * var(--tiroir-marge)) 0;
    padding: 12px var(--tiroir-marge);
    background: var(--surface);
    border-top: 1px solid var(--bord-fort);
    box-shadow: 0 -4px 20px rgba(16, 24, 42, .09);
    flex-wrap: wrap;
}

/* État de validation, repris dans la barre collante pour être lisible en
   permanence. Symbole + libellé + compte : jamais la couleur seule. */
.tiroir-etat {
    display: flex; align-items: center; gap: 7px;
    font-size: .79rem; font-weight: 650;
    padding: 5px 10px; border-radius: 999px;
    border: 1px solid var(--bord-fort);
    background: var(--fond);
    flex: 0 0 auto;
}
.tiroir-etat-symbole { font-size: .9rem; line-height: 1; }
.tiroir-etat-conforme { border-color: #cfe6d4; background: #f4faf5; color: #1f6b35; }
.tiroir-etat-bloque { border-color: #edd9d7; background: var(--rouge-clair); color: #8f2f26; }

/* Le message de validation passe à la ligne dans la barre plutôt que de
   comprimer les boutons : sur un écran étroit, un bouton rogné est pire qu'un
   message renvoyé à la ligne suivante. */
.tiroir-actions-collees .validation-message { flex: 1 1 100%; margin-top: 2px; }

/* =============================================================================
   Cibles tactiles
   -----------------------------------------------------------------------------
   Les boutons « Retirer » (.btn-mini, 4px de padding vertical ≈ 24 px de haut),
   les nuits de la grille et les bascules de nature descendaient tous sous les
   44 px recommandés. Le correctif est réservé aux pointeurs grossiers : au
   trackpad, agrandir ces boutons relâcherait inutilement une interface dense.
   ============================================================================= */

/* ---------------------------------------------------------------------------
   Anneau de focus des champs d'autocomplétion.

   Constaté au navigateur : les quatre `input[type=search]` de `.selecteur-unique`
   d'un tiroir — dont le sélecteur de commande mère, le contrôle le plus
   important de l'écran — ne recevaient aucun indicateur de focus. La règle
   `.champ input:focus` pose bien `outline: none`, mais son `box-shadow` de
   remplacement n'atteignait pas ces champs : au clavier, plus rien ne disait où
   l'on se trouvait.

   La règle est donc explicite et porte sur les deux pseudo-classes : `:focus`
   pour le focus programmatique (la liste rend le focus à l'input après un choix)
   et `:focus-visible` pour la tabulation.
   --------------------------------------------------------------------------- */
.selecteur-unique input:focus,
.selecteur-unique input:focus-visible,
/* `input[type=date]` est ajouté par précaution, pas pour corriger un défaut constaté : la
   tabulation semblait le laisser sans indicateur, mais la mesure directe a montré qu'il
   recevait déjà bordure et halo comme les autres — l'anomalie venait du test, qui lisait le
   style pendant que Chromium déplaçait le focus vers un sous-champ interne. La règle est
   conservée parce qu'elle rend explicite un cas où la cascade est difficile à suivre. */
.champ input[type="date"]:focus,
.champ input[type="date"]:focus-visible {
    outline: none;
    border-color: var(--terre);
    box-shadow: 0 0 0 3px rgba(182, 85, 47, .13);
}

@media (pointer: coarse) {
    /* Mesuré au navigateur en 390 px : « Ajouter la prestation », « Créer » et « Annuler »
       tombaient à 32 px de haut. Le défaut portait sur `.btn` lui-même, pas seulement sur sa
       variante mini — d'où la règle sur la classe de base. */
    .btn { min-height: 44px; }
    .btn-mini { min-height: 44px; padding: 8px 14px; }
    .btn-fermer { min-height: 44px; min-width: 44px; }
    .bascule { min-height: 44px; }
    .nuit { min-height: 48px; min-width: 84px; }
    .selecteur-effacer { min-height: 34px; min-width: 34px; }

    /* 8 px minimum entre deux cibles adjacentes. */
    .grille-nuits { gap: 8px; }
    .tiroir-actions { gap: 10px; }
}

/* =============================================================================
   Tiroir sur écran étroit
   -----------------------------------------------------------------------------
   À 390 px, `min(680px, 94vw)` laissait une bande de fond de 23 px à gauche,
   inutile et cliquable — donc une fermeture accidentelle du tiroir au pouce.
   La grille de champs, elle, tombe à une colonne : `minmax(200px, 1fr)` en
   produisait deux dès 420 px, avec des libellés coupés.
   ============================================================================= */

@media (max-width: 560px) {
    .tiroir { width: 100vw; }
    .tiroir-corps { --tiroir-marge: 14px; }
    .tiroir-entete { padding: 14px 14px 12px; }

    .grille-ajustement { grid-template-columns: 1fr; }
    .table-imbriquee { font-size: .78rem; }
    .table-imbriquee th, .table-imbriquee td { padding: 6px 5px; }

    /* Les actions occupent toute la largeur : viser un bouton de 90 px au pouce
       sur un écran de 390 px est une source d'erreur inutile. */
    .tiroir-actions-collees .btn { flex: 1 1 auto; justify-content: center; }

    /* Mesuré à 390 px : la pastille d'état et « Enregistrer » remplissaient la
       première ligne, et « Annuler » se retrouvait seul et centré en dessous —
       détaché du groupe d'actions auquel il appartient. La pastille prend donc sa
       propre ligne, et les deux boutons partagent la suivante : trois lignes
       nettes, aucun orphelin, et deux cibles de pleine largeur. */
    .tiroir-actions-collees .tiroir-etat {
        flex: 1 1 100%;
        justify-content: center;
    }

    .tiroir-actions-collees .btn { flex: 1 1 40%; }
}
/* =============================================================================
   Champs <select> : alignés sur les <input>
   -----------------------------------------------------------------------------
   Constaté au navigateur dans le tiroir de correction : les <select> se
   rendaient à leur largeur intrinsèque (~145 px) tandis que les <input> voisins
   de la même grille occupaient toute leur colonne (~295 px). La cause est une
   omission ancienne — `.champ input` porte largeur, marge intérieure, bordure,
   arrondi et anneau de focus, mais aucune règle n'existait pour `.champ select`.

   Le résultat n'était pas seulement inesthétique : dans une grille dense, deux
   contrôles de même nature et de largeurs différentes se lisent comme deux
   champs de nature différente. La règle réplique donc exactement `.champ input`.
   ============================================================================= */

/* Hauteur commune des contrôles de saisie.

   `input[type=date]` se rendait 2 px plus haut que ses voisins, et dans une
   grille dense deux champs mitoyens de hauteurs différentes se voient tout de
   suite. Poser une `line-height` ne suffit pas — mesuré au navigateur : Chromium
   calcule la hauteur d'un champ date depuis son sous-arbre interne
   (`::-webkit-datetime-edit`), qui ignore la hauteur de ligne de l'hôte. Seule
   une hauteur explicite s'impose à lui.

   La valeur vit donc dans une variable, et non recopiée à trois endroits : c'est
   elle qui définit la hauteur de référence, et le `min-height` des autres
   contrôles garantit qu'aucun ne descend en dessous si son contenu natif est
   plus court. `box-sizing: border-box` étant global, la valeur inclut bordures
   et marges intérieures. */
.champ {
    --h-controle: 37px;
}

.champ input:not([type="file"]),
.champ select {
    line-height: 1.35;
    min-height: var(--h-controle);
}

.champ input[type="date"] {
    height: var(--h-controle);
}

.champ select {
    width: 100%;
    padding: 9px 11px;
    border: 1px solid var(--bord-fort);
    border-radius: var(--r-petit);
    font-size: .9rem;
    font-family: inherit;
    background: var(--surface);
    color: inherit;

    /* La flèche native varie d'un navigateur à l'autre et d'un thème à l'autre.
       Elle est donc dessinée ici, en SVG inline — la CSP interdit toute requête
       hors origine, mais une donnée `data:` reste dans le document. */
    appearance: none;
    background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath fill='none' stroke='%236b7280' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round' d='M1 1l4 4 4-4'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 11px center;
    padding-right: 30px;
}

.champ select:focus,
.champ select:focus-visible {
    outline: none;
    border-color: var(--terre);
    box-shadow: 0 0 0 3px rgba(182, 85, 47, .13);
}

/* Un select désactivé doit se lire comme désactivé sans reposer sur la seule
   couleur : la flèche disparaît, signalant qu'il n'y a rien à dérouler. */
.champ select:disabled {
    background-color: var(--surface-alt, #f6f7f9);
    color: var(--texte-faible);
    background-image: none;
    cursor: not-allowed;
}

/* =============================================================================
   Listes de manques : le symbole occupe la première colonne
   -----------------------------------------------------------------------------
   `.liste-manquants li` est une grille `18px 1fr` : la première colonne attend
   un symbole, la seconde le texte. Un <li> ne contenant que du texte brut voyait
   ce texte placé dans la colonne de 18 px — et la phrase se cassait mot par mot,
   sur toute la hauteur de l'écran. C'était le défaut le plus voyant du tiroir.

   Le symbole n'est pas décoratif : la convention du projet veut qu'aucun état ne
   repose sur la seule couleur. Il est porté par `aria-hidden` parce que le texte
   qui suit dit déjà la même chose — l'annoncer deux fois alourdirait la lecture
   au lecteur d'écran.
   ============================================================================= */

.manque-symbole {
    grid-column: 1;
    font-size: .82rem;
    line-height: 1.5;
    text-align: center;
    color: var(--texte-faible);
}

.bloc-manquant .manque-symbole { color: var(--rouge); }
.liste-levee .manque-symbole { color: #1f6b35; text-decoration: none; }

/* Garde-fou : un <li> à enfant unique occupe les deux colonnes plutôt que de se
   retrouver écrasé dans les 18 px réservés au symbole. */
.liste-manquants li > *:only-child { grid-column: 1 / -1; }

/* ------------------------------------------- repli « déjà réglé » */

/* L'historique des reproches levés est replié : utile pour comprendre, jamais
   actionnable. Le laisser déployé faisait un bloc dont on ne savait quoi faire. */
.pli-leve { margin-top: 12px; }
.pli-leve > summary { font-size: .8rem; color: var(--texte-doux); }

/* Même correction d'alignement que pour les autres replis du tiroir : le chevron
   compte comme un élément de la ligne flex, donc `space-between` renvoyait le
   libellé au milieu — mesuré à 194 px du bord gauche de la section. Ici la règle
   est portée par `.pli-leve` seul : la base `.pli` sert d'autres écrans, où un
   `.pli-titre` extensible occupe déjà cette place. */
.pli-leve > summary { justify-content: flex-start; }
.pli-leve > summary > .badge { margin-left: auto; }
.pli-leve[open] > summary { margin-bottom: 4px; }
.pli-corps { padding: 2px 13px 11px; }
/* =============================================================================
   Allègement du tiroir : replis de consultation et replis d'ajout
   -----------------------------------------------------------------------------
   Mesuré au navigateur avant intervention : 3 508 px de contenu pour 624 px
   visibles, soit 5,6 écrans de défilement pour 8 sections et 37 contrôles. Deux
   postes pesaient sans rien permettre :

     — « Valeurs d'origine du fichier » : 602 px, aucun contrôle ;
     — les trois formulaires d'ajout déployés en permanence : 1 088 px.

   Les deux passent en divulgation progressive. L'anti-pattern visé est celui que
   le référentiel UX nomme « Overwhelm upfront » : tout montrer d'emblée coûte
   plus qu'il n'informe. L'échelle d'espacement reste dense (8 à 32 px), comme il
   convient à un écran de gestion, et aucune ombre n'est ajoutée.
   ============================================================================= */

/* `:is()` plutôt que trois sélecteurs répétés dix fois : un quatrième repli à
   ajouter obligerait sinon à retrouver les dix listes, et celle qu'on oublie
   produit un repli à moitié stylé — chevron absent ou bordure manquante. */
:is(.pli-origine, .pli-ajout, .pli-mere) {
    border: 1px solid var(--bord);
    border-radius: var(--r);
    background: var(--surface);
    margin-top: 12px;
}

/* `flex-start` et non `space-between` : avec `space-between`, un en-tête sans badge
   voyait son libellé poussé à l'autre bout de la ligne — « + Ajouter des nuits
   d'hôtel » se lisait collé au bord droit, loin du chevron qui l'ouvre, et les
   en-têtes de replis ne s'alignaient plus sur les titres de section. Le libellé
   suit donc le chevron, et c'est le badge qui va chercher la droite. */
:is(.pli-origine, .pli-ajout, .pli-mere) > summary {
    display: flex; align-items: center; justify-content: flex-start;
    gap: 12px; flex-wrap: wrap;
    padding: 10px 13px;
    cursor: pointer;
    list-style: none;
    font-size: .82rem; font-weight: 650; color: var(--texte-doux);
    border-radius: var(--r);
    transition: background .15s, color .15s;
}

:is(.pli-origine, .pli-ajout, .pli-mere) > summary::-webkit-details-marker { display: none; }

:is(.pli-origine, .pli-ajout, .pli-mere) > summary > .badge { margin-left: auto; }

/* Le chevron dit l'état sans recourir à la couleur. Même dessin que celui de
   `.pli`, pour la même raison : le glyphe « ▸ » ne s'aligne pas sur les icônes
   du reste de l'interface. */
:is(.pli-origine, .pli-ajout, .pli-mere) > summary::before {
    content: "";
    flex: 0 0 auto;
    width: 12px; height: 12px;
    background: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 20 20'%3E%3Cpath fill='none' stroke='%237d8798' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' d='M8 5l5 5-5 5'/%3E%3C/svg%3E") center / contain no-repeat;
    transition: transform var(--duree) ease;
}
:is(.pli-origine, .pli-ajout, .pli-mere)[open] > summary::before { transform: rotate(90deg); }

:is(.pli-origine, .pli-ajout, .pli-mere) > summary:hover {
    background: var(--surface-alt, #f6f7f9);
    color: var(--texte);
}

/* L'anneau de focus doit rester visible : un repli est un contrôle, atteint au
   clavier comme les autres. */
:is(.pli-origine, .pli-ajout, .pli-mere) > summary:focus-visible {
    outline: none;
    box-shadow: 0 0 0 3px rgba(182, 85, 47, .13);
}

/* « Ajouter » est une action : elle se lit comme telle, avec le signe en tête. */
.pli-ajout > summary > span:first-of-type::before {
    content: "+ ";
    font-weight: 700;
    color: var(--terre);
}

:is(.pli-origine, .pli-ajout, .pli-mere) > summary + * { border-top: 1px solid var(--bord); }

/* =============================================================================
   Cibles tactiles des replis
   ============================================================================= */

@media (pointer: coarse) {
    :is(.pli-origine, .pli-ajout, .pli-mere, .pli-leve) > summary { min-height: 44px; }
}

/* Sur écran étroit, le badge de l'en-tête passe sous le libellé plutôt que de
   comprimer celui-ci à deux mots par ligne. */
@media (max-width: 560px) {
    :is(.pli-origine, .pli-ajout, .pli-mere) > summary { padding: 9px 11px; }
    .pli-corps { padding: 2px 11px 11px; }
}
/* =============================================================================
   Cibles : l'écran étroit compte autant que le pointeur grossier
   -----------------------------------------------------------------------------
   Les règles de cible tactile ne portaient que sur `@media (pointer: coarse)`.
   Mesuré au navigateur à 390 px de large sans simulation tactile : 19 cibles
   repassaient sous 44 px, dont la croix d'effacement à 17 px. Un vrai téléphone
   était bien servi, un navigateur réduit ne l'était pas — et c'est le même
   utilisateur, avec le même doigt sur un écran convertible.

   La condition est donc doublée : pointeur grossier OU écran étroit.
   ============================================================================= */

@media (max-width: 560px) {
    .btn { min-height: 44px; }
    .btn-mini { min-height: 44px; padding: 8px 14px; }
    .btn-fermer { min-height: 44px; min-width: 44px; }
    .bascule { min-height: 44px; }
    .nuit { min-height: 48px; min-width: 84px; }
    .pli-origine > summary,
    .pli-ajout > summary,
    .pli-leve > summary { min-height: 44px; }

    .grille-nuits { gap: 8px; }
    .tiroir-actions { gap: 10px; }
}

/* La croix d'effacement était la plus petite cible de l'interface — 17 px — et
   elle est devenue bien plus sollicitée depuis qu'elle reste offerte pendant que
   la liste est ouverte. Elle est donc agrandie en toutes circonstances, pas
   seulement au doigt : viser 17 px à la souris est déjà pénible. */
.selecteur-effacer {
    min-height: 26px;
    min-width: 26px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

@media (pointer: coarse), (max-width: 560px) {
    .selecteur-effacer { min-height: 40px; min-width: 40px; }
    /* Le champ garde de la place pour la croix agrandie, sinon le texte passe
       dessous. */
    .selecteur-unique input { padding-right: 44px; }
}
/* La croix native de `input[type=search]` doublonnait avec la nôtre : deux boutons
   voisins, d'apparence presque identique et d'effet différent — la native ne vide
   que le texte affiché, la nôtre efface réellement la valeur retenue et prévient
   le parent. Constaté à 390 px une fois notre croix agrandie et rendue permanente.
   Chromium n'expose que ce pseudo-élément pour la retirer. */
.selecteur-unique input::-webkit-search-cancel-button,
.selecteur-unique input::-webkit-search-decoration {
    -webkit-appearance: none;
    appearance: none;
    display: none;
}

/* =============================================================================
   Choix de nature : un groupe de bascules qui se nomme
   -----------------------------------------------------------------------------
   Les groupes de bascules du tiroir n'avaient aucun intitulé — sept boutons posés
   sous un tableau, sans rien qui dise ce qu'ils décidaient. `fieldset`/`legend`
   les nomme pour l'œil comme pour le lecteur d'écran, ce qu'aucun `<span>` ne
   fait : la légende est annoncée avec chaque bouton du groupe.

   Le `fieldset` par défaut porte une bordure et des marges héritées des anciens
   navigateurs, incompatibles avec la densité du tiroir : elles sont remises à
   plat, et seule la légende garde du poids.
   ============================================================================= */
.choix-nature {
    border: 0;
    margin: 0 0 14px;
    padding: 0;
    min-width: 0;              /* sans quoi un fieldset refuse de rétrécir en grille */
}

.choix-nature > legend {
    padding: 0;
    margin-bottom: 6px;
    font-size: .79rem;
    font-weight: 600;
    color: var(--texte-faible);
    display: flex;
    align-items: center;
    gap: 7px;
}

.choix-nature .note-champ,
.choix-nature .validation-message { margin-top: 7px; }

/* =============================================================================
   Modification d'une ligne déjà saisie
   -----------------------------------------------------------------------------
   Corriger une ligne demandait de la retirer puis de tout ressaisir : quatre
   choix en cascade pour changer une quantité, et la ligne perdue si l'on
   renonçait en route. Le formulaire d'ajout sert donc aussi de formulaire de
   modification, et prend alors la place du repli — une modification en cours ne
   doit pas pouvoir se refermer par mégarde.

   Le panneau se distingue par son liseré et par le ✎ de son titre, jamais par la
   couleur seule : c'est le marqueur « champ à saisir » du projet.
   ============================================================================= */
.bloc-edition {
    margin-top: 12px;
    padding: 12px 13px;
    border: 1px solid var(--bord-fort);
    border-left: 3px solid var(--terre);
    border-radius: var(--r);
    background: var(--surface);
}

.bloc-edition > h4 {
    margin: 0 0 10px;
    font-size: .82rem;
    font-weight: 650;
    color: var(--terre);
}

/* Marqueur porté par la ligne du tableau en cours de modification. La ligne reste
   à sa place et garde ses valeurs : c'est la version corrigée qui la remplacera,
   et renoncer ne doit rien changer. */
.marque-edition {
    font-size: .76rem;
    font-weight: 650;
    color: var(--terre);
    white-space: nowrap;
}

/* Deux boutons par cellule au lieu d'un : ils gardent un espace entre eux et
   passent à la ligne d'eux-mêmes si la colonne se resserre.

   L'écart est porté par les boutons et non par un `display: flex` sur la cellule :
   changer le `display` d'un `<td>` le sort de la mise en page du tableau, et les
   colonnes ne s'alignent plus d'une ligne à l'autre. */
.cellule-action .btn { margin: 2px 6px 2px 0; }

/* =============================================================================
   Confinement du tableau d'impact dans le tiroir
   -----------------------------------------------------------------------------
   Mesuré à 390 px : le corps du tiroir défilait latéralement de 62 px alors
   qu'aucun élément ne dépassait. `.cadre-tableau` fait bien son travail — 328 px
   de large pour 712 px de contenu, défilement interne — mais son débordement
   horizontal remontait jusqu'au conteneur défilant du tiroir.

   Même phénomène que pour `.onglets-files`, et même remède : seule une boîte de
   confinement arrête la propagation. La règle est portée par le tiroir, et non
   par `.cadre-tableau` en général : les autres écrans n'ont pas ce défaut, et une
   règle globale changerait le contexte de superposition de tableaux qui, eux, ne
   demandaient rien.
   ============================================================================= */
.tiroir-corps .cadre-tableau {
    contain: paint;
    box-shadow: none;      /* le référentiel proscrit l'ombre sur cet écran de gestion */
}

/* Le repli de la commande mère est un contrôle comme les autres replis : il lui
   manquait dans la variante « écran étroit », et il restait donc à 36 px. */
@media (max-width: 560px) {
    .pli-mere > summary { min-height: 44px; }

    /* La croix d'effacement suit la hauteur du champ, passée à 44 px au doigt.
       À 40 px dans un champ de 44, elle redevenait la plus petite cible. */
    .selecteur-effacer { min-height: 44px; min-width: 44px; }
}

@media (pointer: coarse) {
    .selecteur-effacer { min-height: 44px; min-width: 44px; }
}

/* =============================================================================
   Hauteur des contrôles au doigt
   -----------------------------------------------------------------------------
   Mesuré à 390 px : vingt-six contrôles sous 44 px, tous à 37 px — c'est-à-dire
   tous les champs de saisie du tiroir. Les règles tactiles ne portaient que sur
   les boutons, les bascules et les replis ; les champs, eux, gardaient la hauteur
   dense du bureau. C'est le `touch-friendly-input` du référentiel : sur mobile,
   un champ se vise avec un doigt comme un bouton.

   La hauteur passe par la variable de référence, pas par une règle de plus : le
   champ date la lit en hauteur explicite, les autres en hauteur minimale, et les
   trois restent donc alignés.
   ============================================================================= */
@media (pointer: coarse), (max-width: 560px) {
    .champ { --h-controle: 44px; }

    /* Les contrôles de la barre d'outils — champ de recherche et listes de tri —
       ne vivent pas dans un `.champ` et échappaient donc à la règle ci-dessus.
       Mesuré à 390 px : 35 px pour la recherche, 34 px pour les deux listes, sur
       des écrans où ce sont les premiers contrôles qu'on touche. */
    .champ-recherche { min-height: 44px; }
    .champ-recherche input { min-height: 42px; }
    .champ-tri select, .pagination-taille select { min-height: 44px; }
}

/* =============================================================================
   Commande mère retenue : une ligne d'identité, le reste en repli
   -----------------------------------------------------------------------------
   Remplace un tableau de huit colonnes. Ce qu'on relit sur un dossier retenu,
   c'est « est-ce bien celui-là » : numéro, client, dates, montant. Le reste ne
   sert qu'une fois, et il éloignait les champs à corriger de plusieurs centaines
   de pixels.
   ============================================================================= */
.mere-retenue {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    flex-wrap: wrap;
    margin: 10px 0 8px;
    padding: 10px 13px;
    border: 1px solid var(--bord);
    border-left: 3px solid var(--vert);
    border-radius: var(--r);
    background: var(--surface);
}

.mere-identite {
    display: flex;
    align-items: baseline;
    gap: 6px 12px;
    flex-wrap: wrap;
    min-width: 0;
    font-size: .84rem;
}

.mere-numero { font-weight: 650; font-variant-numeric: tabular-nums; }
.mere-client { font-weight: 550; }
.mere-dates { color: var(--texte-doux); font-size: .8rem; }
.mere-montant { margin-left: auto; font-weight: 650; }

.pli-mere { margin-bottom: 12px; }

@media (max-width: 560px) {
    /* Le montant remonte contre les dates plutôt que de rester poussé à droite :
       en colonne, `margin-left: auto` le laissait seul sur sa ligne. */
    .mere-montant { margin-left: 0; }
}

/* ---------------------------------------------------------------------------
   Bilan de reprise, et marque des dossiers déjà repris

   Le bilan se pose juste au-dessus de la barre d'action, là où le regard est
   déjà. Il est teinté du vert de succès sur son bord gauche seulement : un
   panneau entièrement vert se lit comme une alerte inversée et écrase les
   tableaux qu'il contient, qui sont l'information principale.
   --------------------------------------------------------------------------- */

.bloc-bilan {
    border-left: 4px solid var(--succes-vif);
    background: var(--surface);
    scroll-margin-top: 16px;
}

/* Le focus y est posé par programme après un import ; l'anneau du navigateur sur
   un panneau entier n'apprend rien de plus que son apparition. */
.bloc-bilan:focus { outline: none; }
.bloc-bilan:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }

.bilan-titre {
    display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
    margin-top: 0;
}

.bilan-compte {
    font-size: 1.6rem; font-weight: 680; line-height: 1;
    font-variant-numeric: tabular-nums; letter-spacing: -.02em;
    color: var(--succes-texte);
}

/* Étapes numérotées : l'ordre est l'information, donc `ol` et non `ul`. */
.liste-etapes { margin: 6px 0 12px; padding-left: 22px; font-size: .86rem; line-height: 1.65; }
.liste-etapes li { margin-bottom: 3px; }

/* Un dossier déjà repris : la coche cède la place à une marque d'état. La teinte
   reste discrète — la ligne n'est ni en alerte, ni sélectionnée, elle est faite. */
.ligne-reprise { background: var(--succes-fond); }
.ligne-reprise:hover { background: var(--succes-fond); }

.marque-reprise {
    display: inline-flex; align-items: center; justify-content: center;
    width: 20px; height: 20px; border-radius: 50%;
    background: var(--succes-vif); color: #fff;
    font-size: .72rem; font-weight: 700; line-height: 1;
}

.texte-succes { color: var(--succes-texte); }
.texte-alerte { color: var(--danger-texte); }

/* ---------------------------------------------------------------------------
   Remplacement : ce que la mère perd, en face de ce que la commande prend

   Le vis-à-vis EST l'information : un remplacement se lit comme un échange, et
   deux blocs empilés le feraient lire comme deux opérations sans rapport. Les
   colonnes se replient l'une sous l'autre en dessous de 900 px, où elles
   n'auraient plus la largeur d'un tableau.
   --------------------------------------------------------------------------- */

.bloc-remplacement { border-left: 4px solid var(--violet, #6b46c1); }

.face-a-face {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 16px;
    align-items: start;
}

.face { min-width: 0; }
.face h4 { margin: 0 0 4px; font-size: .9rem; }

/* La teinte distingue les deux sens sans les crier : un liseré, pas un fond. */
.face-quittee { border-left: 2px solid var(--attention-bord, var(--bord-fort)); padding-left: 12px; }
.face-nouvelle { border-left: 2px solid var(--succes-bord); padding-left: 12px; }

/* Le texte du fichier n'est pas une consigne : il est cité, jamais interprété. */
.note-reference {
    border-left: 3px solid var(--bord-fort);
    padding-left: 10px;
    font-style: italic;
}
.note-reference .muted { font-style: normal; display: block; margin-top: 3px; }

/* Le bilan net vient APRÈS le vis-à-vis : il en est la conclusion chiffrée, donc il
   se détache du dernier tableau sans se poser en nouvelle section. */
.titre-bilan { margin: 16px 0 6px; font-size: .9rem; }

/* =====================================================================================
   Remplacement — une carte par ligne saisie : ancien → nouveau → impact net.

   Le vis-à-vis antérieur posait deux colonnes sans jamais apparier leurs lignes : le
   rapprochement se faisait de tête. Ici chaque remplacement est UNE ligne de lecture,
   dans l'ordre où on la raconte.
   ===================================================================================== */
.liste-remplacements { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; }

.remplacement {
    border: 1px solid var(--bord);
    border-left: 3px solid var(--succes-bord);
    border-radius: var(--r-petit);
    padding: 10px 12px;
    background: var(--n-0, #fff);
}

/* Un remplacement à trancher n'est pas une erreur : c'est une question. L'ambre le dit
   sans la rougeur d'un refus. */
.remplacement-a-trancher { border-left-color: var(--attention-bord, var(--bord-fort)); }

.remplacement-flux {
    display: grid;
    grid-template-columns: 1fr auto 1fr auto auto;
    gap: 10px;
    align-items: center;
}

/* Choix de la portée : les mêmes bascules que la nature, mais à deux lignes — le libellé, et
   ce que le domaine recouvre. Sans l'exemple, « Prestations » et « Billetterie » se ressemblent
   assez pour qu'on hésite, et hésiter ici fait saisir dans le mauvais bloc. */
.choix-portee { margin-top: 14px; }
.choix-portee .bascule {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 1px;
    text-align: left;
    line-height: 1.25;
}
.portee-libelle { font-weight: 600; }
.portee-exemple {
    font-size: .75rem;
    color: var(--texte-doux, var(--n-500));
}
/* Sur la bascule active, l'exemple hérite du contraste du fond retenu : la couleur douce
   d'origine y tomberait sous le seuil de lisibilité. */
.choix-portee .bascule-active .portee-exemple { color: inherit; opacity: .85; }

.remplacement-cote { min-width: 0; display: flex; flex-direction: column; gap: 2px; }

/* Rang d'unité : il ne sert qu'à distinguer deux cartes issues de la même ligne de vente —
   deux nuits d'un séjour, deux billets d'une séance. Il se lit avant le vis-à-vis, discrètement,
   sans peser autant que les valeurs qu'il aide à situer. */
.remplacement-rang {
    margin: 0 0 6px;
    font-size: .75rem;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--texte-doux, var(--n-500));
}

.cote-titre {
    font-size: .7rem;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--texte-discret, var(--n-500));
}

.cote-valeur { font-size: .875rem; overflow-wrap: anywhere; }
.cote-net { text-align: right; }

/* Zéro d'impact net = la place rendue paie la place prise. C'est le chiffre rassurant
   du parcours : il mérite la couleur de succès, pas le gris des autres. */
.net-neutre { color: var(--succes-texte, var(--succes-bord)); }

.remplacement-fleche { color: var(--texte-discret, var(--n-500)); font-size: 1.1rem; }

.remplacement-choix { margin-top: 10px; display: grid; gap: 6px; }

/* Le bouton porte DEUX lignes : ce qui est rendu, et pourquoi il est proposé. La raison
   est ce qui permet de trancher — sans elle, deux nuits identiques à l'œil. */
.btn-choix {
    display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
    text-align: left; width: 100%; padding: 8px 10px;
}
.choix-libelle { font-size: .875rem; }
.choix-raison { font-size: .75rem; color: var(--texte-discret, var(--n-500)); }

/* Un état vide qui dit quoi faire : c'est le point de départ du parcours, pas une absence. */
/* Invite de saisie : une phrase encadrée à l'intérieur d'un bloc, qui dit par quoi commencer.
   À NE PAS confondre avec `.etat-vide`, l'écran vide pleine largeur des vingt listes — c'est
   d'ailleurs le nom qu'elle portait, et comme les deux règles ont la même spécificité, celle-ci,
   écrite plus bas dans la feuille, gagnait partout : les vingt états vides perdaient leur
   centrage, leur icône et leur taille de titre. Voir CLAUDE.md §Pièges, « à spécificité égale,
   c'est la dernière déclaration qui gagne ». */
.invite-saisie {
    border: 1px dashed var(--bord-fort);
    border-radius: var(--r-petit);
    padding: 12px 14px;
    color: var(--texte-discret, var(--n-600));
    font-size: .875rem;
}
.invite-saisie strong { display: block; color: var(--texte, var(--n-900)); margin-bottom: 2px; }

/* L'état vide qui annonce une réussite : même gabarit, l'accent vert au lieu de la marque. */
.etat-vide-succes .etat-vide-ico {
    background: var(--succes-fond, var(--marque-050));
    color: var(--succes-texte, var(--marque-600));
    border-color: var(--succes-bord, var(--marque-100));
}

.pli-inventaire { margin-top: 14px; }

@media (max-width: 900px) {
    /* En colonne, les flèches horizontales ne décrivent plus rien : elles tournent. */
    .remplacement-flux { grid-template-columns: 1fr; }
    .remplacement-fleche { transform: rotate(90deg); justify-self: start; }
    .cote-net { text-align: left; }
}

@media (max-width: 900px) {
    .face-a-face { grid-template-columns: 1fr; }
    /* En colonne, le liseré vertical ne sépare plus rien : il devient un filet du haut. */
    .face-quittee, .face-nouvelle {
        border-left: none; padding-left: 0;
        border-top: 2px solid var(--bord); padding-top: 10px;
    }
}

/* Bandeau de synthèse posé DANS un bloc, et non en tête de page : la valeur descend
   d'un cran pour ne pas concurrencer le titre du bloc qui la surplombe. */
.barre-synthese-compacte { margin-bottom: 12px; }
.barre-synthese-compacte .synthese-valeur { font-size: 1.25rem; }
/* =====================================================================================
   Contrôle de saisie : le doublon sous son champ, le bilan sous le bouton
   =====================================================================================

   Trois blocs nés du même défaut : un reproche affiché loin de ce qu'il reproche n'est
   pas lu. Le tiroir de saisie mesure plusieurs milliers de pixels — un message posé en
   tête de page est hors champ au moment où il compte. */

/* Message d'erreur DANS la cellule du champ, sous l'input. Pleine largeur de la
   cellule, corps réduit : il complète l'étiquette, il ne concurrence pas le titre du
   bloc. `display: block` parce que le porteur est un <span> — un <div> dans un <label>
   passerait, mais un <span> ne peut pas contenir de <div>. */
.alerte-champ {
    display: block;
    margin-top: 6px;
    padding: 8px 10px;
    font-size: var(--t-xs);
    line-height: 1.5;
}
.alerte-champ strong { display: block; }
.alerte-champ span { display: block; margin-top: 2px; }

/* Le champ lui-même porte l'état, pas seulement le message : sans cela, un formulaire
   long ne dit pas OÙ chercher quand on revient dessus. */
.champ-invalide input { border-color: var(--danger-vif, var(--rouge)); }
.champ-invalide input:focus { box-shadow: 0 0 0 3px rgba(190, 62, 52, .16); }

/* Une case à cocher de confirmation n'est pas une case de filtre : elle engage. Le fond
   la distingue sans employer le rouge, réservé aux dérogations — offrir une place est un
   geste ordinaire, pas un contournement de règle. */
.champ-confirmation {
    display: flex; align-items: flex-start; gap: 8px;
    margin-top: 10px; padding: 9px 11px;
    background: var(--surface);
    border: 1px solid var(--bord-fort);
    border-radius: var(--r-petit);
    font-size: var(--t-sm); line-height: 1.5;
    align-self: stretch;
}
.champ-confirmation input { margin-top: 2px; flex: 0 0 auto; }
.champ-confirmation > span { margin: 0; font-weight: 400; color: var(--texte); }

/* « Où en est cette saisie » : l'état courant et la prochaine action, énoncés juste
   au-dessus du bouton. Les points déjà réglés restent affichés — c'est ce qui en fait
   une progression et non un rappel de ce qui va mal. */
.bilan-saisie {
    margin: var(--e-4) 0 0;
    padding: var(--e-4);
    background: var(--surface-alt);
    border: 1px solid var(--bord);
    border-radius: var(--r);
}
.bilan-saisie-entete {
    display: flex; align-items: baseline; justify-content: space-between;
    gap: var(--e-3); flex-wrap: wrap; margin-bottom: var(--e-3);
}
.bilan-saisie-entete h3 { margin: 0; }

.liste-controles { list-style: none; margin: 0; padding: 0; display: grid; gap: 2px; }

.controle {
    display: grid;
    grid-template-columns: 20px 1fr auto;
    align-items: start;
    gap: 2px var(--e-2);
    padding: 7px 8px;
    border-radius: var(--r-petit);
    font-size: var(--t-sm);
}
.controle + .controle { border-top: 1px solid var(--bord); }

/* Le symbole porte l'état au même titre que la couleur : lisible en niveaux de gris
   comme au lecteur d'écran, où c'est le mot « à saisir » qui l'énonce. */
.controle-symbole {
    font-size: .95rem; line-height: 1.4; text-align: center;
    color: var(--texte-discret, var(--n-500));
}
.controle-corps { min-width: 0; }
.controle-libelle { display: block; font-weight: 600; }
.controle-etat {
    margin-left: 6px; font-weight: 500; font-size: var(--t-xs);
    text-transform: lowercase; color: var(--texte-discret, var(--n-500));
}
.controle-detail {
    display: block; margin-top: 1px;
    font-size: var(--t-xs); line-height: 1.5;
    color: var(--texte-doux, var(--n-600));
    overflow-wrap: anywhere;
}

.controle-fait .controle-symbole { color: var(--succes-vif, var(--vert)); }
.controle-asaisir { background: var(--surface); }
.controle-asaisir .controle-symbole { color: var(--danger-vif, var(--rouge)); }
.controle-aconfirmer { background: var(--avertissement-fond, var(--orange-clair)); }
.controle-aconfirmer .controle-symbole { color: var(--avertissement-vif, var(--orange)); }
.controle-facultatif .controle-libelle { font-weight: 500; }

/* Un doublon probable se juge dossier par dossier : une carte par dossier, avec ses deux
   gestes possibles et leur conséquence énoncée. Le tableau à huit colonnes qu'elle
   remplace faisait lire un score là où c'est l'identité qui décide. */
.carte-doublon {
    padding: var(--e-3);
    border: 1px solid var(--bord);
    border-radius: var(--r);
    background: var(--surface);
    margin-bottom: var(--e-2);
}
.carte-doublon-a-trancher { border-color: var(--avertissement-bord, var(--orange)); }
.carte-doublon-entete {
    display: flex; align-items: flex-start; justify-content: space-between;
    gap: var(--e-3); flex-wrap: wrap;
}
.carte-doublon-actions {
    display: flex; gap: var(--e-2); flex-wrap: wrap; margin-top: var(--e-3);
}
.carte-doublon-consequence {
    margin: 6px 0 0;
    font-size: var(--t-xs); line-height: 1.5;
    color: var(--texte-doux, var(--n-600));
}
.pli-doublon { margin: var(--e-3) 0 0; }
.pli-ecartes { margin-top: var(--e-3); }

.liste-indices { list-style: none; margin: 0; padding: var(--e-3); display: grid; gap: 4px; }
.liste-indices li { font-size: var(--t-xs); line-height: 1.5; overflow-wrap: anywhere; }

/* « Prochaine action » : la phrase qui dit quoi faire, distincte du texte explicatif qui
   dit pourquoi. Pas en flex — une phrase mise en flex se disloque à chaque <strong>. */
.etat-controle {
    margin: 0 0 var(--e-3);
    font-size: var(--t-sm); line-height: 1.55;
    color: var(--texte-doux, var(--n-600));
}
.etat-controle strong { color: var(--texte, var(--n-900)); }

@media (max-width: 640px) {
    /* Le bouton passe sous le libellé : à 390 px, une colonne « auto » à droite écrase le
       détail à une largeur illisible. */
    .controle { grid-template-columns: 20px 1fr; }
    .controle > .btn { grid-column: 2; justify-self: start; margin-top: 4px; }
}

/* Un doublon de référence ne se coche pas : il se corrige. Le symbole ✖ et le rouge le
   séparent des points « à saisir », qui n'attendent qu'une frappe. */
.controle-bloque { background: var(--danger-fond, var(--rouge-clair)); }
.controle-bloque .controle-symbole { color: var(--danger-vif, var(--rouge)); }

/* =====================================================================================
   ASSISTANT GUIDÉ — fil d'étapes, en-tête, pied, cartes de choix, résumé permanent.

   Le parcours de saisie d'un complément posait seize sections d'un bloc, 3 508 px de
   contenu pour 624 px visibles. Le remède n'est pas de replier : c'est de ne demander
   qu'une chose à la fois, et de dire à chaque instant où l'on en est. Les composants
   ci-dessous portent cette mise en scène ; les règles métier n'ont pas bougé.

   Deux garde-fous de mise en page valables partout ici :
     — le fil colle en HAUT du corps du tiroir et le pied colle en BAS, tous deux sous
       `--z-action` (25) : au-delà, ils passeraient devant les listes de `.selecteur-liste`
       (z-index 30), qui deviendraient inutilisables en bas de formulaire ;
     — aucune valeur hexadécimale : tout descend des jetons de `:root`, préalable au
       thème sombre.
   ===================================================================================== */

/* ------------------------------------------------------------------ fil d'étapes */

.fil-etapes {
    position: sticky;
    top: 0;
    z-index: var(--z-onglets);
    margin: 0 calc(-1 * var(--tiroir-marge, var(--e-4)));
    padding: var(--e-3) var(--tiroir-marge, var(--e-4)) var(--e-3);
    background: var(--surface);
    border-bottom: 1px solid var(--bord);
}

.fil-barre {
    height: 4px;
    border-radius: var(--r-pilule);
    background: var(--surface-creuse);
    overflow: hidden;
    margin-bottom: var(--e-2);
}

.fil-barre-remplie {
    height: 100%;
    border-radius: var(--r-pilule);
    background: var(--marque-500);
    /* La largeur est le seul élément animé : `transform` serait plus économe mais
       déformerait le rayon de l'extrémité. La barre change une fois par étape, pas
       soixante fois par seconde. */
    transition: width var(--duree-lente) var(--ease-sortie);
}

.fil-entete {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--e-3);
    flex-wrap: wrap;
    margin-bottom: var(--e-2);
}

.fil-position { font-weight: 650; font-size: var(--t-sm); }
.fil-compte { font-size: var(--t-xs); color: var(--texte-faible); }

.fil-liste {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-wrap: wrap;
    gap: var(--e-1) var(--e-2);
}

.fil-etape { min-width: 0; }

.fil-lien {
    display: inline-flex;
    align-items: center;
    gap: var(--e-2);
    /* 44 px de haut : c'est une cible tactile, pas une étiquette. */
    min-height: 44px;
    padding: var(--e-1) var(--e-2);
    border: 1px solid transparent;
    border-radius: var(--r-sm);
    background: none;
    font-family: inherit;
    text-align: left;
    color: var(--texte-doux);
    cursor: pointer;
    transition: background var(--duree-rapide), border-color var(--duree-rapide);
}

.fil-lien:hover:not(.fil-inerte) { background: var(--surface-creuse); }
.fil-lien:focus-visible { outline: 2px solid var(--focus); outline-offset: 1px; }

/* Inerte : ni bouton, ni curseur de clic. Un bouton désactivé invite à cliquer et ne
   dit pas pourquoi il ne répond pas. */
.fil-inerte { cursor: default; color: var(--texte-faible); }

.fil-pastille {
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    width: 26px; height: 26px;
    border-radius: var(--r-pilule);
    border: 1px solid var(--bord-fort);
    background: var(--surface);
    font-size: var(--t-xs);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    color: var(--texte-doux);
}

.fil-corps { min-width: 0; display: flex; flex-direction: column; }
.fil-titre { font-size: var(--t-sm); font-weight: 570; overflow-wrap: anywhere; }
.fil-etat {
    font-size: var(--t-2xs);
    text-transform: lowercase;
    color: var(--texte-faible);
}

/* --- états. Le symbole et le mot portent déjà l'information : la couleur ne fait que
   la répéter, elle ne la crée jamais. */
.fil-fait .fil-pastille {
    background: var(--succes-fond); border-color: var(--succes-bord); color: var(--succes-texte);
}
.fil-fait .fil-etat { color: var(--succes-texte); }

.fil-aconfirmer .fil-pastille {
    background: var(--attention-fond); border-color: var(--attention-bord); color: var(--attention-texte);
}
.fil-aconfirmer .fil-etat { color: var(--attention-texte); }

.fil-bloque .fil-pastille {
    background: var(--danger-fond); border-color: var(--danger-bord); color: var(--danger-texte);
}
.fil-bloque .fil-etat { color: var(--danger-texte); }

.fil-facultatif .fil-pastille { border-style: dashed; }

.fil-courante .fil-lien {
    background: var(--marque-050);
    border-color: var(--marque-300);
    color: var(--texte);
}
.fil-courante .fil-pastille {
    background: var(--marque-500); border-color: var(--marque-600); color: var(--n-0);
}
.fil-courante .fil-titre { font-weight: 650; }

/* ------------------------------------------------------------------ en-tête d'étape */

.etape-entete { margin: var(--rythme-serre) 0 var(--e-4); }

.etape-rang {
    margin: 0 0 var(--e-1);
    display: flex; align-items: center; gap: var(--e-2);
    font-size: var(--t-2xs);
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--texte-faible);
}

/* La QUESTION est le titre de l'étape. L'anneau de focus est retiré : le focus y arrive
   à chaque changement d'étape (exigence WCAG `focus-on-route-change` appliquée aux
   étapes), et un titre encadré se lit comme un champ de saisie. */
.etape-question { margin: 0; font-size: var(--t-xl); }
.etape-question:focus { outline: none; }
.etape-question:focus-visible { outline: none; }

.etape-aide {
    margin: var(--e-2) 0 0;
    max-width: 68ch;
    font-size: var(--t-sm);
    color: var(--texte-doux);
}

/* ------------------------------------------------------------------ cartes de choix */

.grille-choix {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
    gap: var(--e-3);
    margin: 0 0 var(--rythme-serre);
}

/* Une seule colonne quand les options se lisent en liste plutôt qu'en damier — les
   actions d'un domaine, où l'ordre compte (ce qui ajoute, puis ce qui remplace, puis ce
   qui retire). */
.grille-choix-liste { grid-template-columns: 1fr; gap: var(--e-2); }

.carte-choix {
    display: grid;
    grid-template-columns: auto 1fr auto;
    align-items: start;
    gap: var(--e-3);
    width: 100%;
    min-height: 44px;
    padding: var(--e-3);
    border: 1px solid var(--bord-fort);
    border-radius: var(--r-md);
    background: var(--surface);
    font-family: inherit;
    text-align: left;
    color: var(--texte);
    cursor: pointer;
    transition: border-color var(--duree-rapide), background var(--duree-rapide),
                box-shadow var(--duree-rapide);
}

.carte-choix:hover:not(:disabled) {
    border-color: var(--marque-300);
    background: var(--marque-050);
}
.carte-choix:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.carte-choix:disabled { opacity: .55; cursor: not-allowed; }

.carte-choix-active {
    border-color: var(--marque-600);
    background: var(--marque-050);
    box-shadow: inset 0 0 0 1px var(--marque-600);
}

.carte-choix-icone {
    display: grid; place-items: center;
    width: 34px; height: 34px;
    border-radius: var(--r-sm);
    background: var(--surface-creuse);
    color: var(--texte-doux);
}
.carte-choix-active .carte-choix-icone {
    background: var(--marque-100); color: var(--marque-700);
}

/* `.carte-choix-corps` n'est PAS en flex : une phrase mise en flex se disloque — chaque
   suite de texte devient un élément de ligne, et un <strong> en milieu de phrase se
   détache sur sa propre colonne. */
.carte-choix-corps { min-width: 0; display: block; }

.carte-choix-titre { display: block; font-weight: 620; font-size: var(--t-md); }

.carte-choix-marqueur {
    margin-left: var(--e-2);
    padding: 1px 6px;
    border-radius: var(--r-pilule);
    background: var(--info-fond);
    border: 1px solid var(--info-bord);
    color: var(--info-texte);
    font-size: var(--t-2xs);
    font-weight: 570;
    text-transform: none;
    white-space: nowrap;
}

.carte-choix-aide {
    display: block;
    margin-top: 2px;
    font-size: var(--t-xs);
    line-height: 1.5;
    color: var(--texte-doux);
    overflow-wrap: anywhere;
}

.carte-choix-complement { display: block; margin-top: var(--e-2); }

.carte-choix-coche {
    align-self: center;
    width: 20px;
    text-align: center;
    font-weight: 700;
    color: var(--marque-600);
}

/* ------------------------------------------------------------------ pied d'étape */

.pied-etape {
    position: sticky;
    bottom: 0;
    z-index: var(--z-action);
    margin: var(--rythme) calc(-1 * var(--tiroir-marge, var(--e-4))) 0;
    padding: var(--e-3) var(--tiroir-marge, var(--e-4));
    background: var(--surface);
    border-top: 1px solid var(--bord-fort);
    /* C'est l'ombre qui dit qu'on passe dessous, jamais la transparence : un fond
       translucide laissait lire le contenu au travers et faisait croire à deux
       éléments superposés. */
    box-shadow: 0 -4px 20px rgba(13, 20, 36, .09);
}

/* Le motif du blocage, juste AU-DESSUS des boutons. En tête d'un tiroir de plusieurs
   milliers de pixels, il serait hors champ au moment exact où il compte. */
.pied-blocage {
    display: grid;
    grid-template-columns: 20px 1fr;
    gap: var(--e-1) var(--e-2);
    margin-bottom: var(--e-3);
    padding: var(--e-2) var(--e-3);
    border: 1px solid var(--bord);
    border-radius: var(--r-sm);
    background: var(--surface-alt);
}

.pied-blocage-symbole { text-align: center; color: var(--texte-faible); }
.pied-blocage-corps { min-width: 0; }
.pied-blocage-titre { margin: 0; font-size: var(--t-sm); font-weight: 620; }

.pied-blocage-liste {
    margin: 2px 0 0;
    padding-left: var(--e-4);
    font-size: var(--t-xs);
    line-height: 1.55;
    color: var(--texte-doux);
}
.pied-blocage-liste li { overflow-wrap: anywhere; }

.pied-blocage-asaisir { background: var(--surface-alt); }
.pied-blocage-asaisir .pied-blocage-symbole { color: var(--danger-vif); }

.pied-blocage-aconfirmer {
    background: var(--attention-fond); border-color: var(--attention-bord);
}
.pied-blocage-aconfirmer .pied-blocage-symbole { color: var(--attention-vif); }

.pied-blocage-bloque { background: var(--danger-fond); border-color: var(--danger-bord); }
.pied-blocage-bloque .pied-blocage-symbole { color: var(--danger-vif); }

.pied-actions {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--e-3);
    flex-wrap: wrap;
}

/* L'action principale est à droite et seule de son bord : c'est elle qu'on cherche
   (`primary-action`). « Retour » reste un bouton discret, à gauche. */
.pied-actions-fin {
    display: flex;
    align-items: center;
    gap: var(--e-3);
    flex-wrap: wrap;
    margin-left: auto;
}

.pied-reste {
    font-size: var(--t-xs);
    color: var(--texte-faible);
    overflow-wrap: anywhere;
}

/* ------------------------------------------------------------------ résumé permanent */

.recap-parcours {
    margin: var(--rythme-serre) 0 0;
    padding: var(--e-3) var(--e-4);
    background: var(--surface-alt);
    border: 1px solid var(--bord);
    border-radius: var(--r);
}

.recap-entete {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--e-3);
    flex-wrap: wrap;
    margin-bottom: var(--e-2);
}
.recap-entete h3 { margin: 0; }

/* ------------------------------------------------------------------ écran étroit */

@media (max-width: 640px) {
    /* Le fil garde sa place mais perd son mot d'état : à 390 px, six pastilles avec
       titre et mot débordent sur quatre lignes et repoussent la question hors de
       l'écran. Le symbole seul reste, doublé par `aria-current` et par la couleur de
       la pastille. */
    .fil-etat { display: none; }
    .fil-titre { font-size: var(--t-xs); }
    .fil-lien { padding: var(--e-1); gap: var(--e-1); }

    /* Une seule colonne : `minmax(260px, 1fr)` tient encore à 390 px, mais deux
       colonnes de 180 px hacheraient les libellés d'action. */
    .grille-choix { grid-template-columns: 1fr; }

    /* `flex: 0 0 100%` et non `width: 100%` : avec une flex-shrink à 1, l'élément se
       laisse comprimer à sa largeur minimale et reste sur la première ligne. */
    .pied-actions-fin { flex: 0 0 100%; margin-left: 0; }
    .pied-actions-fin .btn { flex: 1 1 auto; }
}

/* Le fil est le seul repère de position : sur un écran très court, le rendre non
   collant libère la hauteur nécessaire pour voir la question et le premier champ
   ensemble, ce qui compte davantage. */
@media (max-height: 520px) {
    .fil-etapes { position: static; }
}

@media (prefers-reduced-motion: reduce) {
    .fil-barre-remplie { transition: none; }
}

/* ------------------------------------------------ nature de la vente, au-dessus du fil */

/* Elle décide du parcours entier — un package n'a ni objet ni action à choisir — donc elle
   n'est pas une étape de ce parcours. D'où sa place : au-dessus du fil, séparée par un
   filet, comme le type d'un document et non comme une de ses questions. */
.bandeau-nature { margin-bottom: var(--e-3); }

.bandeau-nature-titre {
    margin: 0 0 var(--e-2);
    font-size: var(--t-2xs);
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--texte-faible);
}

/* Une seule nature possible : c'est un fait, pas un choix. Une liste déroulante à une
   entrée se lit comme un choix qu'on aurait oublié de faire. */
.bandeau-nature-fait {
    margin: 0;
    padding: var(--e-2) var(--e-3);
    background: var(--surface-alt);
    border: 1px solid var(--bord);
    border-radius: var(--r-sm);
    font-size: var(--t-sm);
}
.bandeau-nature-fait strong { display: block; }
.bandeau-nature-fait span { color: var(--texte-doux); font-size: var(--t-xs); }

/* Le détail par champ, replié sous le récapitulatif d'étapes : il porte des reproches plus
   fins que l'étape — un contact manquant, une capacité non renseignée. Déplié par défaut,
   il doublerait le récapitulatif. */
.pli-controles > summary { font-size: var(--t-sm); }

/* ------------------------------------------------ synthèse avant → après */

.synthese-ap {
    margin: 0 0 var(--rythme-serre);
    padding: var(--e-4);
    background: var(--surface);
    border: 1px solid var(--bord);
    border-radius: var(--r);
    box-shadow: var(--ombre-1);
}

.synthese-ap-colonnes {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: var(--e-4);
}

.synthese-ap-volet h4 {
    margin: 0 0 var(--e-2);
    font-size: var(--t-2xs);
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--texte-faible);
}

/* Ce qui est rendu se distingue par un filet, pas par une couleur d'alerte : rendre une
   place n'est ni un succès ni un incident, c'est l'autre moitié d'un échange. */
.synthese-ap-rendu {
    padding-left: var(--e-4);
    border-left: 2px solid var(--bord-fort);
}

.synthese-ap-liste {
    margin: 0;
    padding-left: var(--e-4);
    font-size: var(--t-sm);
    line-height: 1.6;
}
.synthese-ap-liste li { overflow-wrap: anywhere; }

.synthese-ap-rien { margin: 0; font-size: var(--t-sm); color: var(--texte-doux); }

.synthese-ap-note {
    margin: var(--e-2) 0 0;
    font-size: var(--t-xs);
    color: var(--texte-faible);
}

.synthese-ap-net {
    display: flex;
    align-items: baseline;
    gap: var(--e-2);
    flex-wrap: wrap;
    margin: var(--e-4) 0 0;
    padding-top: var(--e-3);
    border-top: 1px solid var(--bord);
}

.synthese-ap-net-libelle { font-size: var(--t-sm); font-weight: 570; }
.synthese-ap-net-valeur { font-size: var(--t-xl); font-variant-numeric: tabular-nums; }

/* Le signe est la variation du restant. Zéro est NEUTRE et se rend sans signe : un
   remplacement parfait ne perd rien, et « −0 » ferait lire l'inverse du chiffre. */
.synthese-ap-neutre { color: var(--texte-doux); }
.synthese-ap-positif { color: var(--succes-texte); }
.synthese-ap-negatif { color: var(--texte); }

.synthese-ap-net-phrase {
    flex: 1 1 100%;
    font-size: var(--t-xs);
    color: var(--texte-doux);
}

@media (max-width: 640px) {
    /* `flex: 0 0 100%` et non `width: 100%` : avec flex-shrink à 1, la valeur se laisserait
       comprimer et resterait sur la première ligne. */
    .synthese-ap-rendu { padding-left: 0; border-left: none; padding-top: var(--e-3); border-top: 1px solid var(--bord-fort); }
}

/* Enregistrer sans attendre la dernière étape : discret, sous le pied du parcours, et il dit
   ce qu'il fait. La correction n'engage aucune dotation — un dossier repris à moitié doit
   pouvoir être posé pour être fini plus tard, et l'imposer de traverser sept étapes pour un
   seul champ corrigé serait une régression sur l'écran d'avant. */
.note-enregistrer {
    display: flex;
    align-items: baseline;
    gap: var(--e-2);
    flex-wrap: wrap;
    margin-top: var(--e-3);
}
.note-enregistrer > span { flex: 1 1 240px; }

/* Client d'un complément, replié : l'en-tête porte le nom, donc la ligne doit rester lisible
   même quand le badge est long. */
.pli-client > summary { font-size: var(--t-sm); }

/* ==================================================================== comptes

   ADMINISTRATION DES COMPTES.

   Aucune valeur hexadécimale ici : tout passe par les jetons de `:root`, préalable au thème
   sombre. Les états reprennent les tons déjà vérifiés en contraste — un cinquième ton propre
   aux comptes aurait été un ton de plus à mesurer.
   -------------------------------------------------------------------------------------------- */

/* Les compteurs de synthèse deviennent des FILTRES : lire « 2 verrouillés » et devoir ensuite les
   retrouver à la main serait un travail que l'écran peut faire. Ce sont donc des boutons, et ils
   doivent en avoir l'air au survol sans crier au repos. */
.barre-synthese-cliquable .synthese-item {
    border: 1px solid transparent;
    background: transparent;
    cursor: pointer;
    text-align: left;
    font: inherit;
    border-radius: var(--r-sm);
    transition: background var(--duree-rapide), border-color var(--duree-rapide);
}
.barre-synthese-cliquable button.synthese-item:hover {
    background: var(--surface-alt);
    border-color: var(--bord);
}
.barre-synthese-cliquable .synthese-active {
    border-color: var(--marque-400);
    background: var(--marque-050);
}
/* Le filtre retenu se dit aussi par le texte, jamais par la seule teinte. */
.barre-synthese-cliquable .synthese-active .synthese-libelle::after {
    content: " — filtré";
    color: var(--marque-600);
    font-weight: 600;
}
.valeur-attention { color: var(--attention-texte); }

/* « Qui a accès à quoi » : le compte par rôle, au-dessus de la liste. */
.acces-par-role {
    padding: var(--e-4);
    border: 1px solid var(--bord);
    border-radius: var(--r);
    background: var(--surface);
    margin-bottom: var(--e-4);
}
.titre-bloc-compact {
    font-size: var(--t-xs); text-transform: uppercase; letter-spacing: .06em;
    color: var(--texte-faible); font-weight: 700; margin: 0 0 var(--e-3);
}
.bascule-compte {
    display: inline-grid; place-items: center;
    min-width: 22px; height: 20px; padding: 0 6px; margin-left: var(--e-2);
    border-radius: var(--r-pilule);
    background: var(--n-100); color: var(--texte-doux);
    font-size: var(--t-2xs); font-weight: 700;
    font-variant-numeric: tabular-nums;
}
.bascule-active .bascule-compte { background: var(--marque-500); color: #fff; }

/* Filtres compacts de la barre de recherche : le libellé reste VISIBLE au-dessus du contrôle
   (`input-labels`), il n'est pas remplacé par un texte d'invite. */
.champ-inline { flex: 0 0 auto; min-width: 130px; margin: 0; }
.champ-inline > span { font-size: var(--t-2xs); }
.champ-inline select { min-height: 34px; }
.etiquette-visuelle { display: none; }

.tableau-comptes td { vertical-align: middle; }
.tableau-comptes .cellule-secondaire {
    display: flex; gap: var(--e-2); align-items: center; margin-top: 2px;
}

/* Phrase d'état en tête de fiche : ce que l'état EMPÊCHE, ce qu'un badge ne dit pas. */
.phrase-etat {
    margin: 0 0 var(--e-4);
    padding: var(--e-3);
    border-left: 3px solid var(--bord-fort);
    background: var(--surface-alt);
    border-radius: 0 var(--r-sm) var(--r-sm) 0;
    color: var(--texte-doux); font-size: var(--t-sm);
}

/* Champ en LECTURE SEULE : distinct d'un champ désactivé (`read-only-distinction`). Un champ
   désactivé se lit « momentanément indisponible » ; celui-ci ne sera jamais modifiable. */
.champ-lecture > output {
    display: block;
    padding: 8px var(--e-3);
    background: var(--surface-alt);
    border: 1px dashed var(--bord);
    border-radius: var(--r-sm);
    color: var(--texte-doux);
}

/* Rôles en cartes, et non en cases à cocher : la description tient sur la carte, donc le choix se
   fait sans infobulle. `aria-pressed` porte l'état pour le lecteur d'écran. */
.liste-roles { display: grid; gap: var(--e-2); }
.carte-role {
    display: grid; gap: 2px;
    padding: var(--e-3);
    border: 1px solid var(--bord);
    border-radius: var(--r-sm);
    background: var(--surface);
    text-align: left; cursor: pointer; font: inherit;
    transition: border-color var(--duree-rapide), background var(--duree-rapide);
}
.carte-role:hover { border-color: var(--bord-fort); background: var(--surface-alt); }
.carte-role-active { border-color: var(--marque-400); background: var(--marque-050); }
.carte-role-nom {
    font-weight: 600; color: var(--texte);
    display: flex; align-items: center; gap: var(--e-2);
}
.carte-role-desc { font-size: var(--t-xs); color: var(--texte-faible); }
/* La coche occupe sa place même absente : sans cela, cocher décalait le libellé d'un cran. */
.carte-role-coche {
    display: inline-grid; place-items: center;
    width: 16px; height: 16px; flex: 0 0 16px;
    border: 1px solid var(--bord-fort); border-radius: var(--r-xs);
    font-size: var(--t-2xs); color: #fff;
}
.carte-role-active .carte-role-coche { background: var(--marque-500); border-color: var(--marque-500); }

/* Fil des trois étapes de création. Volontairement distinct de `.fil-etapes`, qui appartient à
   l'assistant de vente et attend son propre moteur de parcours. */
.fil-creation {
    display: flex; gap: var(--e-2); list-style: none;
    margin: 0 0 var(--e-4); padding: 0;
}
.fil-creation li {
    flex: 1; display: grid; gap: 4px; justify-items: center;
    padding: var(--e-2) var(--e-1);
    border-top: 3px solid var(--bord);
    color: var(--texte-faible); font-size: var(--t-2xs); text-align: center;
}
.fil-creation-faite { border-top-color: var(--succes-vif); color: var(--succes-texte); }
.fil-creation-courante {
    border-top-color: var(--marque-500); color: var(--marque-600); font-weight: 700;
}
.fil-creation-rang {
    display: inline-grid; place-items: center;
    width: 20px; height: 20px; border-radius: var(--r-pilule);
    background: var(--n-100); color: inherit; font-weight: 700;
}
.fil-creation-faite .fil-creation-rang { background: var(--succes-fond); }
.fil-creation-courante .fil-creation-rang { background: var(--marque-500); color: #fff; }

.bloc-recap { background: var(--surface-alt); border-radius: var(--r-sm); padding: var(--e-3); }

/* ACTIONS SENSIBLES : séparées, et pas seulement colorées. Un trait et un espace suffisent à dire
   « ce bloc-ci ne modifie pas une donnée, il change un accès » (`destructive-nav-separation`). */
.bloc-sensible {
    margin-top: var(--e-6);
    padding-top: var(--e-4);
    border-top: 2px solid var(--bord);
}
.actions-sensibles { display: flex; flex-wrap: wrap; gap: var(--e-2); }
.actions-sensibles .btn { flex: 1 1 auto; min-width: 180px; justify-content: center; }

/* Un bouton destructif au repos ne hurle pas : il porte la couleur de danger sur le texte et la
   bordure, jamais un aplat rouge — un aplat rouge dans une colonne d'actions se clique par
   réflexe. Il ne devient plein qu'au moment de confirmer. */
.btn-danger-discret {
    background: var(--surface); border-color: var(--danger-bord); color: var(--danger-texte);
}
.btn-danger-discret:hover:not(:disabled) {
    background: var(--danger-fond); border-color: var(--danger-vif);
}

/* Le panneau de confirmation REMPLACE les boutons, au même endroit. Une boîte modale aurait
   déplacé la décision ailleurs que là où le geste a été demandé — et le projet remplace de toute
   façon les confirmations par l'énoncé de ce qui va se produire. */
.bloc-confirmation { margin-bottom: var(--e-3); }
.bloc-confirmation .champ-confirmation { margin-top: var(--e-3); }
.actions-confirmation { display: flex; gap: var(--e-2); flex-wrap: wrap; margin-top: var(--e-3); }

.tiroir-actions-inline {
    position: static; padding: 0; margin-bottom: var(--e-4); border: 0; background: none;
}

/* Journal d'audit. La colonne de détail porte des phrases : elle passe à la ligne plutôt que de
   pousser le tableau en largeur (`truncation-strategy` — envelopper plutôt que tronquer). */
.pli-audit { margin-top: var(--e-6); }
.cellule-detail {
    font-size: var(--t-xs); color: var(--texte-doux);
    overflow-wrap: anywhere; min-width: 220px;
}

.liste-audit { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--e-2); }
.liste-audit li {
    display: grid; gap: 2px;
    padding: var(--e-2) var(--e-3);
    border-left: 2px solid var(--bord);
    background: var(--surface-alt);
    border-radius: 0 var(--r-xs) var(--r-xs) 0;
    font-size: var(--t-xs);
}
.audit-quand { color: var(--texte-faible); font-variant-numeric: tabular-nums; }
.audit-quoi { font-weight: 600; color: var(--texte); }
.audit-qui { color: var(--texte-doux); }
.audit-detail { color: var(--texte-faible); }

/* La carte de la page « définir votre mot de passe » est plus large que celle de la connexion :
   elle porte trois champs, une alerte et une aide par champ. À 420 px, l'aide passait sur trois
   lignes et la carte devenait plus haute que l'écran. */
.carte-connexion-large { max-width: 520px; }
.sortie-connexion { margin-top: var(--e-4); text-align: center; }

@media (max-width: 900px) {
    /* Les compteurs deviennent une bande défilante : empilés, ils repoussaient la liste
       sous deux écrans de haut. */
    .barre-synthese-cliquable { overflow-x: auto; contain: paint; }
    .barre-synthese-cliquable .synthese-item { min-height: 44px; }
    .acces-par-role { padding: var(--e-3); }
    .actions-sensibles .btn { flex: 1 1 100%; min-height: 44px; }
    .actions-confirmation .btn { flex: 1 1 100%; min-height: 44px; }
    .champ-inline { flex: 1 1 140px; }

    /* Le titre d'étape disparaît, le RANG reste, et l'étape courante est nommée juste en dessous
       par « Étape 2 sur 3 — Rôles » : l'information n'est pas perdue, elle cesse d'être répétée. */
    .fil-creation { gap: var(--e-1); }
    .fil-creation-titre { display: none; }
}

/* Qualification d'une prestation Magento (écran Prestations) : un formulaire en ligne, sous la
   ligne qu'il concerne — c'est là que le regard se trouve au moment du geste. `.ligne-detail`
   retire le rembourrage de sa cellule : le formulaire porte le sien. */
.formulaire-qualification {
    display: flex; flex-wrap: wrap; align-items: flex-end;
    gap: var(--e-2) var(--e-4); padding: var(--e-3) var(--e-4);
}
.formulaire-qualification > p { flex: 1 0 100%; margin: 0; }
.actions-inline { display: flex; flex-wrap: wrap; gap: var(--e-2); }
