/* ═══════════════════════════════════════════════════════════════════════════
   LA FEUILLE DE STYLE — le châssis (MANAGED par doctrine-core).

   Ce fichier appartient au core : il porte la structure de TOUT ce que le
   core rend — page, navigation, cartes, contrôles, tableaux, dialogues,
   composants. Une instance ne le modifie jamais ; elle déclare ses jetons
   dans web/instance.css (chargé APRÈS) et habille ses cartes locales dans
   web/cartes/*.css, en jetons (docs/method/methode.md, § 6). Rien ici ne
   nomme une instance, une de ses cartes ni une de ses clés — et une
   instance n'a plus à surcharger un sélecteur du core pour obtenir le
   dessin natif : la barre latérale, l'en-tête de page, la carte, le
   segment sont d'ici.

   AUCUNE COULEUR ICI — ni code hexadécimal, ni fonction rgb ou hsl, ni
   mot-clé de couleur (un test le vérifie sur le fichier entier). Les jetons attendus de
   l'instance :
     --bg --panel --panel2 --border --text --muted --accent
     --good --warn --bad --perime --serie-1…8 --police
   Facultatifs, chacun avec un défaut DÉRIVÉ ci-dessous (une instance qui
   n'a pas migré son instance.css se lit correctement, en clair comme en
   sombre) :
     encres et surfaces  --texte-2 --separateur --accent-encre
                         --encre-sur-accent --rail-fond --segment-fond
                         --segment-actif
     teintes d'état      --accent-fond --good-fond --warn-fond --bad-fond
                         --warn-vif
     ombres              --ombre-carte --ombre-flottante --segment-ombre
                         (--ombre, historique)
     forme               --radius --rayon-carte --rail-largeur
     typographie         --police-titre --police-mono --num-poids
     teintes nommées     --corail --bleu --bleu-clair --violet --orange
   Les teintes dérivées se calculent (color-mix sur ces jetons) : une ombre
   est l'encre de l'instance diluée — sa nuit, en sombre —, jamais du noir
   posé en dur. Un jeton que core.js LIT (jetons() : séries, grille,
   panneau, encres) reste un alias pur (var(--x)) ou une valeur : un
   color-mix() n'est pas résolu par getComputedStyle, et Chart.js ne le
   lirait pas.

   Le thème : `<html data-theme="light|dark">` force l'apparence choisie
   dans le menu du compte (core/web/theme.js la pose avant le premier
   rendu) ; sans attribut, celle du système. Le sombre d'une instance
   s'écrit donc sous DEUX sélecteurs, identiques :
     @media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) {…} }
     :root[data-theme="dark"] {…}

   La grammaire (mesurée sur la maquette validée) : filet 1px ; rayon
   --radius (8px) sur les contrôles, --rayon-carte (12px) sur les cartes ;
   une ombre À PEINE posée sur ce qui est dans le flux (--ombre-carte, que
   l'instance peut mettre à none), une ombre affirmée pour ce qui le quitte
   (menus, dialogues : --ombre-flottante). Toute bascule est un SEGMENT
   (le choix en relief sur un fond creux). Le survol ne déplace rien. La
   capsule est réservée à ce qui est ovale par nature. Les transitions sont
   courtes (≤ .2s), et se taisent sous prefers-reduced-motion.
   ═══════════════════════════════════════════════════════════════════════════ */

:root {
  /* La forme. `--rail` est le nom que des instances donnaient à la
     largeur de leur barre latérale maison : il reste lu. */
  --radius: 8px;
  --rayon-carte: 12px;
  --rayon-flottant: 12px;
  --rail-largeur: var(--rail, 248px);
  /* Les encres et les surfaces dérivées. */
  --texte-2: color-mix(in srgb, var(--text) 78%, var(--muted));
  --separateur: color-mix(in srgb, var(--border) 60%, var(--panel));
  --accent-encre: var(--panel);
  --encre-sur-accent: var(--accent-encre);
  --rail-fond: var(--panel);
  --segment-fond: color-mix(in srgb, var(--panel2) 85%, var(--border));
  --segment-actif: var(--panel);
  /* L'encre discrète posée sur un fond teinté — le libellé inactif d'un
     segment, une pilule de la barre : --muted y tombait sous 4,5:1 en
     clair (4,21:1 sur --segment-fond). Plus soutenue d'un cinquième vers
     l'encre du texte, elle garde son rang sous le libellé actif. */
  --encre-segment: color-mix(in srgb, var(--muted) 80%, var(--text));
  --grille: var(--border);
  /* Les teintes d'état : un fond qui se lit sous l'encre de l'état. */
  --accent-fond: color-mix(in srgb, var(--accent) 9%, var(--panel));
  --good-fond: color-mix(in srgb, var(--good) 10%, var(--panel));
  --warn-fond: color-mix(in srgb, var(--warn) 11%, var(--panel));
  --bad-fond: color-mix(in srgb, var(--bad) 10%, var(--panel));
  --warn-vif: var(--warn);
  /* Les ombres : l'encre de l'instance, diluée. En sombre (règles plus
     bas), l'encre de l'ombre devient le fond : une ombre claire sur un
     écran sombre se lit comme un halo, pas comme une profondeur. */
  --ombre-encre: var(--text);
  --ombre-carte: 0 1px 2px color-mix(in srgb, var(--ombre-encre) 5%, transparent),
                 0 1px 3px color-mix(in srgb, var(--ombre-encre) 4%, transparent);
  --ombre-flottante: 0 2px 6px -2px color-mix(in srgb, var(--ombre-encre) 10%, transparent),
                     0 14px 32px -12px color-mix(in srgb, var(--ombre-encre) 22%, transparent);
  --segment-ombre: 0 1px 2px color-mix(in srgb, var(--ombre-encre) 8%, transparent),
                   0 0 0 1px color-mix(in srgb, var(--ombre-encre) 5%, transparent);
  --ombre: var(--ombre-carte);
  --voile: color-mix(in srgb, var(--ombre-encre) 42%, transparent);
  --anneau-focus: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent);
  --police-mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, monospace;
  /* Alias historiques, appelés par d'anciens styles : l'instance peut les
     redéfinir dans instance.css (chargé après, donc prioritaire). */
  --fg: var(--text);
  --border-strong: color-mix(in srgb, var(--text) 28%, transparent);
}
/* L'encre posée sur l'accent suit le panneau — le blanc d'un thème clair.
   Une instance sombre qui ne la déclare pas (ses jetons sombres et son
   `color-scheme: dark`, souvent directement dans `:root`) y aurait le
   panneau sombre sur l'accent, illisible : elle prend alors son encre de
   texte, claire — comme le repli blanc d'avant ce jeton. Un navigateur
   sans `light-dark()` garde le panneau. */
@supports (color: light-dark(transparent, transparent)) {
  :root { --accent-encre: light-dark(var(--panel), var(--text)); }
}
/* Le sombre : le core n'y redéfinit QUE l'encre de ses ombres. Tout le
   reste vient des jetons de l'instance, qui déclare ses deux thèmes. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) { --ombre-encre: var(--bg); }
}
:root[data-theme="dark"] { --ombre-encre: var(--bg); }

* { box-sizing: border-box; margin: 0; }
html { -webkit-text-size-adjust: 100%; }
/* Les réglages typographiques d'Inter (chiffres et lettres distinctes : 1,
   l, I) — sans effet sur une autre police. */
body { background: var(--bg); color: var(--text);
  font: 14px/1.5 var(--police, "Inter", ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif);
  font-feature-settings: "cv11", "ss01"; letter-spacing: -.006em;
  -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; }
::selection { background: color-mix(in srgb, var(--accent) 22%, transparent); }

/* ── L'en-tête historique : une réserve de pièces ─────────────────────────
   L'en-tête d'instance ne s'affiche JAMAIS : c'est une réserve de pièces
   (marque, boutons propres à l'application) que le châssis déménage dans sa
   barre. Le masquer en JS seulement le laissait peint jusqu'à la réponse de
   /api/meta — l'ancien bandeau revenait une seconde à chaque rechargement.
   Ses pièces gardent leurs styles ci-dessous : elles servent, déplacées. */
header { display: none; }
.brand { display: flex; gap: 10px; align-items: center; min-width: 0; }
.brand > div { min-width: 0; }
/* Le logo : une image (web/marque/logo.png, posée par `core.marque_core`),
   sinon le monogramme du gabarit — une lettre sur l'accent. */
.brand img.logo { display: block; flex: none; height: 32px; width: auto; max-width: 150px;
  object-fit: contain; }
.brand .logo:not(img) { display: inline-grid; place-items: center; flex: none;
  width: 30px; height: 30px; border-radius: var(--radius); background: var(--accent);
  color: var(--accent-encre); font: 650 15px/1 var(--police-titre, inherit); }
.brand h1 { font-family: var(--police-titre, inherit); }
.marque-sous-titre { font-size: 11.5px; font-weight: 500; line-height: 1.3;
  color: var(--muted); letter-spacing: 0; }
.marque-sous-titre:empty { display: none; }
h1 { font-size: 17px; letter-spacing: -.015em; }
h2 { font-size: 15px; letter-spacing: -.015em; }
h3 { font-size: 13.5px; letter-spacing: -.01em; }
.ctx { color: var(--muted); font-size: 12px; display: none; }
/* `.actions` reste dans l'en-tête : une classe nue aurait aussi habillé
   toute carte d'un type « actions » (le type devient une classe). */
header .actions { display: flex; gap: 10px; align-items: center; }

/* ── Les contrôles ──────────────────────────────────────────────────────── */
button { background: var(--accent); border: 0; color: var(--accent-encre);
  padding: 8px 14px; border-radius: var(--radius); cursor: pointer;
  font-weight: 600; font-family: inherit;
  transition: background-color .15s, border-color .15s, color .15s, box-shadow .15s, filter .15s; }
button:hover { filter: brightness(1.08); }
button:disabled { cursor: default; }
button.ghost { background: transparent; border: 1px solid var(--border);
  color: var(--muted); padding: 6px 10px; font-size: 12px; line-height: 16px; }
button.ghost:hover { filter: none; border-color: var(--accent); color: var(--text); }
/* Le focus se VOIT au clavier, partout, et seulement au clavier : un
   anneau d'accent décollé du contrôle, jamais retiré sans remplaçant. */
:is(a, button, select, input, textarea, summary, [tabindex]):focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px; }
:is(input, select, textarea):focus-visible { outline-offset: 0; box-shadow: var(--anneau-focus); }
.refresh-status { color: var(--warn); font-size: 12px; }

/* ═══════════════════════════════════════════════════════════════════════════
   LA BARRE — une seule, tout dedans

   Sur un écran large ET assez haut (≥ 1024 px de large, ≥ 600 px de haut),
   c'est un RAIL fixe à gauche : la marque en haut, les entrées en liste à
   icônes — une porte qui regroupe est un bouton qui la déplie ou la replie
   (celle de la page ouverte dépliée, les autres comme le lecteur
   les a laissées), ses onglets en retrait sur un filet —, la fraîcheur, la
   version, les icônes et le compte en bas. Ailleurs (téléphone, tablette,
   fenêtre basse), une barre du haut floutée à DEUX rangées qui défilent au
   doigt : la marque, les entrées (les portes, leur icône et leur nom ; Mon
   espace, les tableaux, les pages du châssis) et le compte sur la
   première ; les onglets de la porte dépliée sur la seconde, absente
   quand aucune ne l'est. Les deux requêtes se complètent exactement
   (`not all and`) : aucune largeur ne tombe entre deux dessins. La même
   requête vit dans core.js (REQUETE_RAIL) : le placement des menus et
   l'état des portes en dépendent.

   Les entrées vivent dans `.nav-entrees`, entre la marque et la zone du
   compte. L'icône d'une entrée est un <span class="ic-nav"> posé APRÈS le
   libellé (le texte du lien reste son libellé) et remis en tête par
   `order`. Une porte qui regroupe se lit dépliée par son bouton
   (`aria-expanded`) : la feuille ne lit que lui.
   ═══════════════════════════════════════════════════════════════════════════ */
#tabs { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; padding: 0 22px;
  position: relative; z-index: 40; background: var(--rail-fond);
  border-bottom: 1px solid var(--border); }
/* La barre provisoire, posée avant /api/meta, se tait un instant (core.js,
   `barre-provisoire`, `BARRE_DELAI_MS`) : sans session, l'écran de connexion
   arrive avant elle, au lieu de la couvrir après un éclair. */
body.barre-provisoire #tabs { visibility: hidden; }
.nav-entrees { display: flex; align-items: center; gap: 4px; min-width: 0; }
#tabs .nav-entrees > a, #tabs .porte-btn, #tabs .porte-menu a { display: flex; align-items: center;
  gap: 10px; min-width: 0; text-decoration: none; color: var(--texte-2);
  font: inherit; font-size: 13.5px; font-weight: 500; line-height: 1.25;
  background: transparent; border: 0; cursor: pointer;
  transition: background-color .12s, color .12s; }
#tabs .nav-entrees > a:hover, #tabs .porte-btn:hover, #tabs .porte-menu a:hover { color: var(--text); filter: none; }
.ic-nav { order: -1; flex: none; display: inline-flex; width: 18px; height: 18px;
  color: var(--muted); transition: color .12s; }
.ic-nav svg { display: block; width: 100%; height: 100%; }
#tabs a.active > .ic-nav, #tabs .porte.active > .porte-btn > .ic-nav { color: var(--accent); }
.cockpit-marque { display: inline-flex; align-items: center; min-width: 0; margin-right: 14px; }
.cockpit-marque h1 { font-size: 15px; font-weight: 700; letter-spacing: -.02em;
  line-height: 1.15; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cockpit-marque .ctx { display: none; }
.cockpit-marque .marque-sous-titre { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.nav-portes .porte { position: relative; }
/* `font: 500 13.5px/1 inherit` n'était pas lu (le mot-clé `inherit` ne se
   glisse pas dans le raccourci) : toute porte s'affichait en gras, comme
   la porte ouverte. */
.nav-portes .porte-btn { border-radius: 0; }
/* Le chevron d'une porte qui regroupe : vers le bas repliée, vers le haut
   dépliée (celui des tableaux, dans le rail, dit un menu qui s'ouvre à
   droite). Un trait dessiné, à l'encre de l'entrée : le signe « ▾ » qu'il
   porte (caché aux lecteurs d'écran) se rendait en un point à 9 px. Il
   tourne en .15s — rien sous prefers-reduced-motion. */
.nav-portes .porte-chev { order: 2; flex: none; width: 6px; height: 6px; margin: -3px 4px 0 auto;
  border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor; font-size: 0;
  opacity: .55; transform: rotate(45deg); transition: transform .15s; }
.nav-portes .porte:not(.porte-tableaux) > .porte-btn[aria-expanded="true"] > .porte-chev {
  margin-top: 3px; transform: rotate(-135deg); }
.nav-portes .porte-menu { display: none; position: absolute; top: calc(100% + 4px);
  left: 0; z-index: 60; min-width: 210px; padding: 5px; background: var(--panel);
  border: 1px solid var(--border); border-radius: var(--rayon-flottant);
  box-shadow: var(--ombre-flottante); }
.nav-portes .porte.pop-ouvert > .porte-menu { display: block; animation: pop-entree .14s ease-out; }
.nav-portes .porte-menu .porte-q { font-size: 11px; line-height: 1.4;
  color: var(--muted); padding: 7px 10px 8px;
  border-bottom: 1px solid var(--separateur); margin-bottom: 4px; }
.nav-portes .porte-menu a { padding: 8px 10px; border-radius: var(--radius); font-size: 13px;
  color: var(--text); }
.nav-portes .porte-menu a:hover { background: var(--panel2); }
.nav-portes .porte-menu a.active { font-weight: 600; }
@keyframes porte-deplie { from { opacity: 0; transform: translateY(-4px); } }

/* ── Le rail (écran large et assez haut) ── */
@media (min-width: 1024px) and (min-height: 600px) {
  body.barre-cockpit { padding-left: var(--rail-largeur); }
  /* La liste défile DANS le rail quand elle dépasse : aucun menu n'en est
     coupé, ils se placent en position fixe (placerPopover, core.js). Rien
     ne défile en largeur : une bulle au repos (cachée, mais posée) ne doit
     pas ouvrir une barre de défilement sous le rail. */
  body.barre-cockpit #tabs { position: fixed; top: 0; bottom: 0; left: 0;
    width: var(--rail-largeur); flex-direction: column; align-items: stretch;
    flex-wrap: nowrap; gap: 2px; padding: 16px 12px 0; overflow-x: hidden; overflow-y: auto;
    overscroll-behavior: contain; scrollbar-width: thin; border-bottom: 0;
    border-right: 1px solid var(--border); }
  /* Une entrée de la liste atteinte au clavier se montre AU-DESSUS de la
     zone collée du bas : elle réserve sous elle, quand le rail défile
     jusqu'à elle, la hauteur de la zone et l'anneau du focus. Sans cette
     réserve, le navigateur ne défilait pas tant que l'entrée restait dans
     la boîte du rail, et le focus se cachait DERRIÈRE la zone (WCAG 2.4.11).
     La hauteur se mesure (`--zone-bas`, suivreZoneBas, core.js) : une barre
     de défilement classique fait passer le compte à la ligne, la fraîcheur
     se réécrit ; le repli couvre une rangée passée à la ligne. La zone, elle,
     ne réserve rien : toujours à l'écran, le focus y entre sans faire
     défiler la liste. Le bouton d'une porte est une entrée comme une autre. */
  body.barre-cockpit #tabs :is(a, button, [tabindex]) {
    scroll-margin-bottom: calc(var(--zone-bas, 136px) + 8px); }
  body.barre-cockpit #tabs .cockpit-actions :is(a, button, [tabindex]) { scroll-margin-bottom: 0; }
  #tabs .cockpit-marque { flex: none; margin: 0 0 8px; padding: 4px 8px 16px;
    border-bottom: 1px solid var(--separateur); }
  #tabs .nav-entrees { flex: none; flex-direction: column; align-items: stretch; gap: 2px; }
  #tabs .nav-entrees > a, #tabs .porte-btn { flex: none; width: 100%; padding: 8px 10px;
    border-radius: var(--radius); text-align: left; white-space: normal; }
  #tabs .nav-entrees > a:hover, #tabs .porte-btn:hover { background: var(--panel2); }
  #tabs .nav-entrees > a.active { background: var(--accent-fond); color: var(--text); font-weight: 600; }
  /* Une porte qui regroupe : un bouton qui la déplie ou la replie —
     son icône, son nom, son chevron ; dépliée, ses onglets dessous, en
     retrait sur un filet. Repliée, la porte de la page ouverte se surligne :
     son onglet, caché, ne le dit plus. */
  #tabs .porte { display: block; flex: none; }
  #tabs .porte.active > .porte-btn { color: var(--text); font-weight: 600; }
  #tabs .porte.active > .porte-btn[aria-expanded="false"] { background: var(--accent-fond); }
  #tabs .porte:not(.porte-tableaux) .porte-q { display: none; }
  #tabs .porte:not(.porte-tableaux) > .porte-menu { display: none; flex-direction: column;
    gap: 1px; position: static; min-width: 0; margin: 1px 0 6px 19px; padding: 0 0 0 10px;
    border: 0; border-left: 1px solid var(--border); border-radius: 0;
    background: transparent; box-shadow: none; }
  #tabs .porte:not(.porte-tableaux) > .porte-btn[aria-expanded="true"] + .porte-menu { display: flex;
    animation: porte-deplie .16s ease-out; }
  #tabs .porte:not(.porte-tableaux) .porte-menu a { padding: 7px 10px; font-size: 13px;
    color: var(--muted); }
  #tabs .porte:not(.porte-tableaux) .porte-menu a:hover { background: var(--panel2); color: var(--text); }
  #tabs .porte:not(.porte-tableaux) .porte-menu a.active { background: var(--accent-fond);
    color: var(--text); font-weight: 600; }
  /* Les tableaux restent un menu (leur liste peut être longue) : il
     s'ouvre à droite du rail — le chevron le dit. */
  #tabs .porte-tableaux .porte-chev { margin-top: 0; margin-right: 5px; transform: rotate(-45deg); }
  #tabs .porte-tableaux > .porte-menu { top: 0; left: calc(100% + 8px); }
  /* Ses liens prennent l'encre principale d'un menu déroulant
     (`.nav-portes .porte-menu a`) : la règle commune des entrées de la
     barre (`#tabs .porte-menu a`, plus spécifique) l'écrasait, avec son
     fond au survol. */
  #tabs .porte-tableaux .porte-menu a { color: var(--text); font-size: 13px; }
  #tabs .porte-tableaux .porte-menu a:hover { background: var(--panel2); }
  #tabs .porte-tableaux .porte-menu a.active { font-weight: 600; }
  /* En bas : la fraîcheur sur toute la largeur, puis la version, les
     icônes et le compte. La zone reste COLLÉE au bas du rail : quand les
     portes dépliées dépassent la hauteur de l'écran, la liste défile
     au-dessus d'elle — elle suivait la liste et sortait de l'écran, le
     compte avec elle, sans rien qui dise une suite (la barre de défilement
     est masquée sous macOS). Elle porte le rembourrage bas du rail et son
     fond : la liste ne se lit pas au travers ; au bout du défilement, la
     dernière entrée se lit juste au-dessus. Ses rangées se calent à
     DROITE : le compte passé seul à la ligne (une barre de défilement
     classique prend sa largeur au rail) y reste — à gauche, son menu,
     ouvert à sa droite, recouvrait le rail. La licence seule prend l'espace
     libre de sa rangée (`margin-right: auto`) : une seconde marge
     automatique, sur le compte, le partageait, et les icônes glissaient de
     4 px, plus loin du compte que l'une de l'autre. */
  #tabs .cockpit-actions { flex: none; position: sticky; bottom: 0; margin: auto 0 0;
    padding: 12px 4px; background: var(--rail-fond); justify-content: flex-end;
    border-top: 1px solid var(--separateur); flex-wrap: wrap; gap: 8px; }
  #tabs .cockpit-actions .pilule-fraicheur { flex: 1 1 100%; }
  #tabs .cockpit-actions .pilule-licence { margin-right: auto; }
  /* Au repos, la bulle ne dépasse pas son hôte (elle est posée, même
     cachée) ; placée par le script, elle reprend sa largeur. */
  #tabs .cockpit-actions .bulle { top: auto; bottom: calc(100% + 8px); left: 0; right: auto;
    width: min(250px, 100%); }
  #tabs .menu-compte { top: auto; bottom: 0; left: calc(100% + 12px); right: auto; }
}

/* ── La barre du haut (écran étroit ou fenêtre basse) — deux rangées ──
   La première : la marque, les entrées en pastilles — une rangée qui défile
   au doigt, sans barre visible, son bord estompé quand des pastilles y
   sont cachées (core.js, suivreDefilement) —, puis le compte. La seconde :
   les onglets de la porte dépliée (core.js : celle de la page ouverte, ou
   celle que le lecteur vient de toucher), une rangée qui défile aussi ;
   posée sous la première, dans le rembourrage que la barre lui réserve,
   elle ne défile pas avec elle. Une porte d'un seul onglet, Mon espace, les
   pages du châssis : des liens ; les tableaux : un menu qui s'ouvre sous
   leur pastille. La pastille de la page ouverte est amenée en vue dans sa
   rangée (core.js, amenerEnVue). Elle remplace les intertitres de porte :
   cinq portes et vingt onglets prenaient 514 px à 375 × 812 ;
   deux rangées, 90 px. */
@media not all and (min-width: 1024px) and (min-height: 600px) {
  /* Le flou vit sur un calque (::before), JAMAIS sur la barre : un
     backdrop-filter fait de son élément le repère des positions fixes, et
     les menus placés dedans seraient coupés par elle. */
  /* Elle ne colle en haut que sur un écran qui a la place (une tablette
     debout) : sur un téléphone, ou une fenêtre basse, elle défile avec la
     page. */
  body.barre-cockpit #tabs { position: relative; flex-wrap: nowrap; gap: 8px; padding: 6px 12px;
    background: transparent; }
  body.barre-cockpit #tabs::before { content: ""; position: absolute; inset: 0; z-index: -1;
    background: color-mix(in srgb, var(--rail-fond) 88%, transparent);
    -webkit-backdrop-filter: saturate(160%) blur(14px); backdrop-filter: saturate(160%) blur(14px); }
  /* La seconde rangée se loge dans le rembourrage du bas : 39 px, et le
     rembourrage de la barre. */
  body.barre-cockpit #tabs:has(.porte:not(.porte-tableaux) > .porte-btn[aria-expanded="true"]) {
    padding-bottom: 45px; }
  #tabs .cockpit-marque { flex: none; margin: 0; }
  #tabs .cockpit-actions { flex: none; flex-wrap: nowrap; gap: 6px; margin-left: 0; padding: 0; }
  #tabs .cockpit-actions .pilule-licence { display: none; }
  /* La première rangée : elle seule défile (la marque et le compte restent
     en place) ; son rembourrage garde l'anneau du focus entier. */
  #tabs .nav-entrees { flex: 1 1 auto; gap: 6px; padding: 4px; overflow-x: auto; overflow-y: hidden;
    overscroll-behavior-x: contain; scroll-snap-type: x proximity; scroll-padding-inline: 4px;
    scrollbar-width: none; }
  #tabs .porte { display: contents; }
  #tabs .nav-entrees > a, #tabs .porte-btn { flex: none; gap: 6px; padding: 6px 12px; border-radius: 999px;
    font-size: 13px; line-height: 18px; background: var(--panel2); white-space: nowrap;
    scroll-snap-align: center; }
  #tabs .nav-entrees > a .ic-nav, #tabs .porte-btn .ic-nav { width: 15px; height: 15px; }
  #tabs .nav-entrees > a.active, #tabs .porte.active > .porte-btn { background: var(--accent);
    color: var(--accent-encre); font-weight: 600; }
  #tabs .nav-entrees > a.active > .ic-nav, #tabs .porte.active > .porte-btn > .ic-nav { color: inherit; }
  /* Une porte dépliée qui n'est pas celle de la page : ses onglets sont
     dessous, un fond léger le dit. */
  #tabs .porte:not(.active) > .porte-btn[aria-expanded="true"] { background: var(--accent-fond); color: var(--text); }
  #tabs .porte-chev { margin-left: 2px; margin-right: 1px; }
  /* La question d'une porte se lit dans le rail et dans l'en-tête de page. */
  #tabs .porte:not(.porte-tableaux) .porte-q { display: none; }
  /* La seconde rangée : les onglets de la porte dépliée, sur toute la
     largeur, sous un filet. */
  #tabs .porte:not(.porte-tableaux) > .porte-btn[aria-expanded="true"] + .porte-menu { display: flex;
    align-items: center; gap: 4px; position: absolute; top: auto; right: 0; bottom: 6px; left: 0;
    z-index: 1; height: 39px; min-width: 0; margin: 0; padding: 4px 12px; overflow-x: auto;
    overflow-y: hidden; overscroll-behavior-x: contain; scroll-snap-type: x proximity;
    scroll-padding-inline: 12px; scrollbar-width: none; background: transparent; border: 0;
    border-top: 1px solid var(--separateur); border-radius: 0; box-shadow: none; animation: none; }
  #tabs .porte:not(.porte-tableaux) .porte-menu a { flex: none; padding: 6px 11px; border-radius: 999px;
    font-size: 13px; line-height: 18px; white-space: nowrap; scroll-snap-align: center; }
  #tabs .porte:not(.porte-tableaux) .porte-menu a:hover { background: var(--panel2); }
  #tabs .porte:not(.porte-tableaux) .porte-menu a.active { background: var(--accent-fond); color: var(--text);
    font-weight: 600; }
  /* Sans barre de défilement visible (le doigt, la molette, le clavier
     défilent toujours) ; un bord estompé dit qu'il y a une suite. */
  #tabs .nav-entrees::-webkit-scrollbar, #tabs .porte-menu::-webkit-scrollbar { display: none; }
  /* La seconde rangée s'estompe par un masque. La première, non : un masque
     couperait aussi la seconde, qui en descend — un voile collé à chaque
     bord, de la couleur de la barre, fait le même effet. */
  #tabs .porte-menu[data-apres] {
    -webkit-mask-image: linear-gradient(90deg, var(--text) calc(100% - 28px), transparent);
    mask-image: linear-gradient(90deg, var(--text) calc(100% - 28px), transparent); }
  #tabs .porte-menu[data-avant] {
    -webkit-mask-image: linear-gradient(90deg, transparent, var(--text) 28px);
    mask-image: linear-gradient(90deg, transparent, var(--text) 28px); }
  #tabs .porte-menu[data-avant][data-apres] {
    -webkit-mask-image: linear-gradient(90deg, transparent, var(--text) 28px, var(--text) calc(100% - 28px), transparent);
    mask-image: linear-gradient(90deg, transparent, var(--text) 28px, var(--text) calc(100% - 28px), transparent); }
  #tabs .nav-entrees::before, #tabs .nav-entrees::after { content: ""; position: sticky; z-index: 1; flex: none;
    align-self: stretch; width: 28px; pointer-events: none; opacity: 0; transition: opacity .15s; }
  #tabs .nav-entrees::before { left: -4px; margin-right: -34px;
    background: linear-gradient(90deg, var(--rail-fond), transparent); }
  #tabs .nav-entrees::after { right: -4px; margin-left: -34px;
    background: linear-gradient(270deg, var(--rail-fond), transparent); }
  #tabs .nav-entrees[data-avant]::before, #tabs .nav-entrees[data-apres]::after { opacity: 1; }
  /* Les tableaux : leur menu s'ouvre sous la pastille (placé par core.js),
     ses liens à l'encre d'un menu ; sa réserve (TABLEAUX_RETENUS) s'y lit. */
  #tabs .porte-tableaux .porte-menu a { color: var(--text); }
  .brand img.logo { height: 28px; max-width: 120px; }
  .brand .logo:not(img) { width: 28px; height: 28px; font-size: 14px; }
  .cockpit-marque .marque-sous-titre { display: none; }
}

/* Plus étroit que 900 px : la marque se réduit à son logo (son nom reste lu
   par un lecteur d'écran) — la première rangée garde la largeur. */
@media (max-width: 899.98px) {
  .cockpit-marque .brand { gap: 0; }
  .cockpit-marque h1 { position: absolute; width: 1px; height: 1px; overflow: hidden;
    clip-path: inset(50%); white-space: nowrap; }
}

/* Un téléphone (moins de 760 px) : le compte seul à droite des entrées. La
   fraîcheur et les icônes de la barre se lisent en tête de son menu
   (core.js, blocBarreDuMenu) : à côté des portes, elles ne laissaient à la
   rangée qu'une pastille et demie à 375 px — et la démonstration, presque
   rien à 640. Plus large, la zone tient sur la rangée sans élargir la page
   (520 px au plus). */
@media (max-width: 759.98px) {
  #tabs .cockpit-actions > :not(.compte) { display: none; }
  #tabs .menu-compte .menu-barre { display: block; }
  .brand img.logo { max-width: 72px; }
}

/* Une tablette debout : la barre a la place de rester en haut — ses deux
   rangées la gardent compacte. Ce qu'on atteint au clavier, ou qu'un saut
   amène (`scrollIntoView` : le sommaire de Mon compte, le filtre d'une
   table), s'arrête SOUS elle : le document réserve en haut la hauteur de
   la barre et l'anneau du focus. Sans cette réserve, le navigateur jugeait
   visible un élément passé derrière la barre, et le focus s'y cachait
   (WCAG 2.4.11) — un bouton de carte sur deux à 900 × 700, Maj+Tab dans le
   contenu. La hauteur se mesure (`--barre-haut`, suivreBarreHaut,
   core.js) : la seconde rangée paraît et s'en va, la fraîcheur se réécrit ;
   le repli vaut les deux rangées. Les entrées de la barre, elles, ne
   réservent rien : collées, toujours à l'écran, elles annulent la réserve
   (une marge de défilement de signe contraire, exactement). Sans cela, le
   navigateur jugeait chacune « sous la réserve » et faisait défiler la
   page pour la descendre, la barre immobile : chaque Tab dans la barre, le
   focus rendu au bouton du compte (Échap) ou à un lien (⌘K refermé)
   remontait la page — à 900 × 700, de 1 996 px à 0 en six Maj+Tab. */
@media (min-width: 760px) and (max-width: 1023.98px) and (min-height: 700px) {
  body.barre-cockpit #tabs { position: sticky; top: 0; }
  html:has(> body.barre-cockpit) { scroll-padding-top: calc(var(--barre-haut, 90px) + 8px); }
  body.barre-cockpit #tabs :is(a, button, [tabindex]) {
    scroll-margin-top: calc(-8px - var(--barre-haut, 90px)); }
}

/* La zone des actions : fraîcheur, version, icônes, compte. */
.cockpit-actions { display: flex; align-items: center; gap: 8px; margin-left: auto; }
.pilule-fraicheur { position: relative; display: inline-flex; align-items: center;
  gap: 6px; padding: 6px 10px; border: 1px solid transparent;
  border-radius: var(--radius); background: var(--panel2); font-size: 11.5px;
  font-weight: 500; color: var(--text); cursor: default;
  font-variant-numeric: tabular-nums; }
.pilule-fraicheur svg { width: 12px; height: 12px; flex: none; color: var(--accent); }
/* L'attente : la même pilule, en sourdine — elle tient la place et ne
   prétend à aucune donnée tant que META n'est pas là. */
.pilule-attente { color: var(--muted); }
.pilule-attente svg { color: var(--muted); animation: tourne 1.1s linear infinite; }
@keyframes tourne { to { transform: rotate(360deg); } }
.pilule-fraicheur.alerte svg { color: var(--bad); }
/* La licence : la même capsule, en sourdine tant qu'elle court (le
   numéro de version, rien de plus) ; pendant le mois de grâce, la teinte de
   l'alerte des vues (`grace`, « Licence à renouveler ») ; suspendue, le
   refus (`alerte`) — mais un cockpit que la licence arrête montre l'écran
   de licence, pas la barre : la pilule ne fait que dire le statut. Ni marge
   ni largeur ici : dans le rail, seule la règle de la zone du bas lui donne
   l'espace libre. Les teintes se dérivent des jetons : aucune couleur posée
   ici. */
.pilule-licence { display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 10px; border: 1px solid transparent; border-radius: var(--radius);
  background: var(--panel2); font-size: 11.5px; font-weight: 500;
  color: var(--encre-segment); cursor: default; white-space: nowrap;
  font-variant-numeric: tabular-nums; }
.pilule-licence.alerte { color: var(--bad); font-weight: 700;
  border-color: color-mix(in srgb, var(--bad) 40%, transparent); background: var(--bad-fond); }
.pilule-licence.grace { color: var(--vue-encre-alerte); font-weight: 600;
  border-color: var(--vue-filet-alerte); background: var(--vue-teinte-alerte); }
.pilule-licence:empty { display: none; }
/* La bulle : l'explication d'une pilule ou d'une icône, au survol et au
   focus. Placée par le script (position fixe, jamais coupée par le rail) ;
   la position ci-dessous est le repli sans script. */
.bulle { position: absolute; top: calc(100% + 8px); right: 0; width: 250px;
  padding: 9px 11px; border-radius: var(--radius); z-index: 70; text-align: left;
  background: var(--text); color: var(--panel); font: 400 11.5px/1.5 var(--police, inherit);
  letter-spacing: 0; white-space: normal; box-shadow: var(--ombre-flottante);
  opacity: 0; visibility: hidden; transition: opacity .12s; pointer-events: none; }
:is(.pilule-fraicheur, .btn-icone):hover .bulle,
:is(.pilule-fraicheur, .btn-icone):focus-visible .bulle,
.pilule-fraicheur:focus-within .bulle { opacity: 1; visibility: visible; }
.btn-icone { position: relative; width: 32px; height: 32px; padding: 0;
  border-radius: var(--radius); cursor: pointer; box-sizing: border-box;
  flex: none; display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid transparent; background: var(--panel2); color: var(--texte-2); }
.btn-icone:hover { background: var(--border); color: var(--text); filter: none; }
.btn-icone svg { width: 15px; height: 15px; }
.btn-compte { width: 32px; height: 32px; padding: 0; border-radius: 999px;
  cursor: pointer; box-sizing: border-box; flex: none;
  border: 1px solid transparent; background: var(--panel2); color: var(--texte-2);
  font: 600 11px/1 var(--police, inherit); letter-spacing: .02em; display: inline-flex;
  align-items: center; justify-content: center; }
.btn-compte:hover { background: var(--border); color: var(--text); filter: none; }
.compte { position: relative; }
/* Le menu du compte : jamais plus large que l'écran moins une
   gouttière, et JAMAIS de défilement horizontal — un contenu trop large se
   replie ou se tronque, il ne pousse pas le panneau. */
.menu-compte { display: none; position: absolute; top: calc(100% + 6px); right: 0;
  width: min(300px, calc(100vw - 16px)); padding: 5px; z-index: 80; background: var(--panel);
  border: 1px solid var(--border); border-radius: var(--rayon-flottant);
  box-shadow: var(--ombre-flottante); overflow-x: hidden; }
.compte.pop-ouvert .menu-compte { display: block; animation: pop-entree .14s ease-out; }
/* L'identité : l'avatar, le nom affiché, l'email dessous, puis les étiquettes
   du profil — des faits, pas des boutons (ni survol, ni curseur de clic). */
.menu-compte .qui { display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: center;
  gap: 2px 10px; padding: 10px 10px 11px; }
.menu-compte .qui-texte { min-width: 0; }
.menu-compte .qui b { display: block; font-size: 13.5px; font-weight: 600; line-height: 1.3;
  overflow-wrap: anywhere; }
.menu-compte .qui-email { display: block; font-size: 11.5px; color: var(--muted);
  margin-top: 1px; overflow-wrap: anywhere; }
/* Les étiquettes sous le NOM, dans sa colonne — pas sous l'avatar. */
.menu-compte .etiquettes { grid-column: 2; display: flex; flex-wrap: wrap; gap: 5px;
  margin-top: 6px; min-width: 0; }
.menu-compte .etiquettes:empty { display: none; }
.menu-compte .etq { font-size: 10.5px; font-weight: 600; letter-spacing: .02em; line-height: 1.4;
  padding: 2px 8px; border-radius: 5px; color: var(--texte-2); background: var(--panel2);
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  cursor: default; user-select: none; }
.menu-compte .etq-admin { color: var(--accent); background: var(--accent-fond); }
.menu-compte hr { border: 0; border-top: 1px solid var(--separateur); margin: 4px -1px; }
/* Le titre d'un groupe (« Administration ») : un intertitre, pas une entrée. */
.menu-compte .menu-titre { padding: 8px 10px 3px; font-size: 10.5px; font-weight: 600;
  letter-spacing: .08em; text-transform: uppercase; color: var(--muted); }
.menu-compte .item { display: flex; align-items: center; gap: 9px; width: 100%;
  padding: 8px 10px; border: 0; border-radius: var(--radius);
  background: transparent; font: inherit; font-size: 13px; color: var(--text);
  cursor: pointer; text-align: left; font-weight: 400; overflow-wrap: anywhere; }
.menu-compte .item:hover { background: var(--panel2); filter: none; }
.menu-compte .item .ic { width: 14px; height: 14px; flex: none; color: var(--muted); }
.menu-compte .item span.ic svg { display: block; width: 100%; height: 100%; }
.menu-compte .item .raccourci { margin-left: auto; font-size: 10.5px;
  color: var(--muted); font-variant-numeric: tabular-nums; }
/* Un raccourci clavier ne se montre pas à un écran tactile (« ⌘K » sur un
   téléphone), dans le menu comme dans une phrase d'aide. */
@media (hover: none) {
  .menu-compte .item .raccourci, .raccourci-clavier { display: none; }
}
/* Le statut du recalcul, sous « Tout recalculer » : aligné sur son libellé. */
.menu-compte #refresh-status { display: block; padding: 0 10px 6px 33px; font-size: 11.5px; }
.menu-compte #refresh-status:empty { display: none; }
.menu-compte .avert { padding: 8px 10px; font-size: 11.5px; line-height: 1.5;
  color: var(--muted); }
.menu-compte .pastille { display: inline-grid; place-items: center; width: 14px;
  height: 14px; border-radius: 50%; margin-right: 6px; vertical-align: -2px;
  font: 700 9px/1 var(--police, inherit); color: var(--panel); background: var(--warn); }
/* L'apparence, dans le menu du compte : Auto (le système), Clair, Sombre —
   un segment, comme toute bascule. Le libellé sur sa ligne, le segment sur
   TOUTE la suivante : ses trois boutons se partagent la largeur et
   rétrécissent plutôt que de déborder (mesuré : à 3 px près sur une ligne,
   le libellé écrasé dès qu'une barre de défilement paraissait). */
.menu-compte .apparence { display: flex; flex-wrap: wrap; align-items: center; gap: 7px;
  padding: 9px 10px 10px; margin-top: 2px; border-top: 1px solid var(--separateur); }
.apparence-lab { flex: 1 1 100%; min-width: 0; font-size: 11.5px; font-weight: 600;
  color: var(--muted); }
/* La barre, dans le menu d'un téléphone (core.js, blocBarreDuMenu) :
   la fraîcheur et son explication, la console et la projection. Cachée
   ailleurs — la barre les porte —, montrée sous 760 px, où la barre ne
   garde que le compte. */
.menu-compte .menu-barre { display: none; margin-top: 2px; padding: 2px 0 4px;
  border-top: 1px solid var(--separateur); }
.menu-compte .menu-fraicheur { display: flex; align-items: flex-start; gap: 9px; padding: 9px 10px 8px;
  font-size: 13px; line-height: 1.4; color: var(--text); }
.menu-compte .menu-fraicheur .ic { display: inline-flex; align-items: center; justify-content: center;
  width: 14px; height: 14px; margin-top: 2px; flex: none; color: var(--accent); }
.menu-compte .menu-fraicheur .ic svg { display: block; width: 100%; height: 100%; }
.menu-compte .menu-fraicheur.alerte .ic { color: var(--bad); }
.menu-compte .menu-fraicheur .point { width: 7px; height: 7px; border-radius: 50%; background: var(--warn); }
.menu-compte .menu-fraicheur-texte { min-width: 0; overflow-wrap: anywhere; }
.menu-compte .menu-fraicheur-texte > strong { display: block; font-weight: 600; font-variant-numeric: tabular-nums; }
.menu-compte .menu-fraicheur.demo .menu-fraicheur-texte > strong { color: var(--vue-encre-alerte); }
.menu-compte .menu-fraicheur small { display: block; margin-top: 2px; font-size: 11.5px; line-height: 1.5;
  color: var(--muted); }

/* Un menu placé par le script : position fixe calculée contre les bords de
   la fenêtre (placerPopover) — ni le rail, ni une carte, ni l'écran ne le
   coupent. Il défile lui-même s'il est plus haut que la place. */
.pop-place { position: fixed !important; right: auto !important; bottom: auto !important;
  margin: 0 !important; z-index: 90; overflow-y: auto; overscroll-behavior: contain; }
@keyframes pop-entree { from { opacity: 0; transform: translateY(-3px); } }

/* ── La connexion — le logo de l'instance, puis le formulaire ─────────────
   Le logo et le nom viennent de la coquille (`.brand` de index.html) : le
   châssis les lit au démarrage et les pose en propriétés sur <body>
   (--marque-logo, --marque-mono, --marque-nom) — l'écran de connexion,
   celui de l'activation et celui du premier compte les portent sans en
   connaître aucun. */
#connexion { position: fixed; inset: 0; z-index: 130; display: grid;
  place-items: center; align-content: center; gap: 14px; padding: 24px 16px; overflow-y: auto;
  background: radial-gradient(1200px 600px at 50% -10%, var(--accent-fond), var(--bg) 70%); }
#connexion > * { order: 3; }
body.marque-mono #connexion::before { order: 1; content: var(--marque-mono, ""); display: grid;
  place-items: center; width: 52px; height: 52px; border-radius: 14px; background: var(--accent);
  color: var(--accent-encre); font: 650 24px/1 var(--police-titre, var(--police, inherit)); }
body.marque-image #connexion::before { order: 1; content: ""; display: block;
  width: min(220px, 70vw); height: 60px; margin-bottom: 12px;
  background: var(--marque-logo) center / contain no-repeat; }
/* Le nom accompagne le monogramme ; une image de logo porte déjà le sien. */
body.marque-mono.marque-nom #connexion::after { order: 2; content: var(--marque-nom, ""); display: block;
  margin-top: -4px; font: 700 15px/1.3 var(--police-titre, var(--police, inherit));
  letter-spacing: -.015em; color: var(--text); text-align: center; }
#connexion form { width: min(380px, 100%); box-sizing: border-box;
  padding: 28px 28px 24px; display: grid; gap: 12px; background: var(--panel);
  border: 1px solid var(--border); border-radius: 16px;
  box-shadow: var(--ombre-flottante); }
#connexion h1 { font-size: 19px; font-weight: 700; margin: 0; text-align: center; }
#connexion p { margin: 0 0 6px; font-size: 12.5px; line-height: 1.55; color: var(--muted);
  text-align: center; }
#connexion label { display: grid; gap: 5px; font-size: 12px; font-weight: 500; color: var(--texte-2); }
#connexion input, #connexion select { font: inherit; font-size: 14px; padding: 10px 12px;
  border-radius: var(--radius); border: 1px solid var(--border); background: var(--panel);
  color: var(--text); transition: border-color .15s, box-shadow .15s; }
#connexion input:focus, #connexion select:focus { border-color: var(--accent); outline: none;
  box-shadow: var(--anneau-focus); }
#connexion button { margin-top: 4px; padding: 11px 12px; border-radius: var(--radius); font-weight: 600; }
#connexion button:disabled { opacity: .6; cursor: wait; }
#connexion .erreur { min-height: 1.2em; font-size: 12px; color: var(--bad); }
#connexion .erreur:empty { min-height: 0; }
/* Le premier compte (les comptes partout) : l'avis d'un compte né entre-
   temps, en bandeau ; la phrase du serveur qui dit d'aller sur le poste, en
   encre pleine — c'est le seul contenu de l'écran ; « Réessayer » en bouton
   discret, puisque rien ne change tant que la page n'est pas ouverte en
   local. */
#connexion .connexion-avis { padding: 9px 11px; border-radius: var(--radius); text-align: left;
  background: var(--warn-fond); color: var(--text); }
#connexion .premier-ailleurs { color: var(--texte-2); font-size: 13px; }
#connexion .premier-reessayer { background: var(--panel2); color: var(--text);
  border: 1px solid var(--border); }
#connexion .premier-reessayer:hover { background: var(--border); filter: none; }

/* ── « Trouver un chiffre » ─────────────────────────────────────────────── */
#rech-overlay { display: none; position: fixed; inset: 0; z-index: 140;
  background: var(--voile);
  align-items: flex-start; justify-content: center; padding: 12vh 16px 0; }
.rech-boite { width: min(640px, 100%); max-height: 66vh; display: flex;
  flex-direction: column; overflow: hidden; border-radius: 16px;
  background: var(--panel); border: 1px solid var(--border);
  box-shadow: var(--ombre-flottante); }
.rech-boite input { font: inherit; font-size: 16px; padding: 17px 19px; border: 0;
  border-bottom: 1px solid var(--separateur); outline: none; background: transparent;
  color: var(--text); }
.rech-boite input:focus-visible { box-shadow: none; }
.rech-res { overflow-y: auto; padding: 7px; }
.rech-res button { display: flex; width: 100%; gap: 12px; align-items: baseline;
  background: transparent; border: 0; border-radius: var(--radius);
  padding: 10px 12px; font: inherit; font-size: 13.5px; font-weight: 500;
  text-align: left; color: var(--text); cursor: pointer; }
.rech-res button:hover, .rech-res button.actif { background: var(--panel2);
  filter: none; }
.rech-res .rech-ou { margin-left: auto; font-size: 11.5px; font-weight: 400;
  color: var(--muted); white-space: nowrap; }
.rech-vide { padding: 15px 12px; font-size: 13px; color: var(--muted); }
.rech-pied { border-top: 1px solid var(--separateur); padding: 9px 19px;
  font-size: 11px; color: var(--muted); }

/* ── L'EN-TÊTE DE PAGE — le nom de l'onglet et sa question ────────────────
   Posé par le châssis au-dessus du plateau (hors de #board : le plateau
   ne porte que des cartes), aligné sur lui. Une page du châssis et un
   tableau ont leur propre titre : l'en-tête s'y cache. `.page-outils`
   accueille ce qui commande TOUTE la page (la période, par exemple). */
.page-tete { max-width: 1500px; margin: 0 auto; padding: 24px 28px 0; display: flex;
  flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 12px 24px; }
.page-tete[hidden] { display: none; }
.page-tete-texte { min-width: 0; flex: 1 1 360px; }
.page-fil { font-size: 12px; font-weight: 600; color: var(--muted); margin-bottom: 3px; }
.page-fil:empty { display: none; }
/* LE TITRE DE PAGE, une seule échelle partout : un onglet, Mon
   espace et ses tableaux, Mon compte, l'Administration, « Règles et
   cap » — 28 px, 24 px sur un téléphone, au même poids. Un
   surtitre facultatif au-dessus, un chapô dessous. Chaque page avait la
   sienne (24, 26, 32, 40 px) : on changeait d'échelle en changeant de page. */
.page-titre { font: 650 28px/1.15 var(--police-titre, var(--police, inherit)); letter-spacing: -.02em;
  color: var(--text); overflow-wrap: anywhere; }
.page-question { margin-top: 4px; font-size: 14px; line-height: 1.5; color: var(--muted);
  max-width: 72ch; }
.page-question:empty { display: none; }
.page-outils { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.page-outils:empty { display: none; }
.page-tete:not([hidden]) + main { padding-top: 18px; }

/* ── LE PLATEAU ─────────────────────────────────────────────────────────── */
main { padding: 24px 28px 56px; display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr)); gap: 18px;
  max-width: 1500px; margin: 0 auto; }
/* La note d'onglet : un bandeau, son icône, sa phrase. */
.tab-note { grid-column: span 12; display: flex; align-items: flex-start; gap: 12px;
  background: var(--accent-fond);
  border: 1px solid color-mix(in srgb, var(--accent) 16%, transparent);
  border-radius: var(--rayon-carte); padding: 13px 18px 13px 15px;
  color: var(--texte-2); font-size: 13px; line-height: 1.6; }
.tab-note strong { color: var(--text); }
.tab-note-ic { flex: none; display: inline-flex; width: 18px; height: 18px; margin-top: 2px;
  color: var(--accent); }
.tab-note-ic svg { width: 100%; height: 100%; }
/* Le texte d'une note tient dans CE bloc : posés à même le bandeau, ses
   éléments (strong, code, br, a) et ses morceaux de texte en devenaient
   autant de colonnes (la note d'échec du démarrage, bootErrorHtml). */
.tab-note-texte { min-width: 0; overflow-wrap: break-word; }
/* Un lien de note, à l'encre de l'accent — jamais le bleu du navigateur. */
.tab-note a { color: var(--accent); font-weight: 600; text-underline-offset: 2px; }
/* UN SEUL style d'intertitre : le titre appartient à ce qui le SUIT — en
   casse normale, à la taille d'un titre de section, pas d'une étiquette. */
.section-head, .a-regarder h2 { grid-column: span 12;
  font-size: 15px; font-weight: 650; letter-spacing: -.015em;
  text-transform: none; color: var(--text); border-bottom: 0;
  padding: 0; margin: 28px 0 0; }
.section-head:first-child { margin-top: 2px; }
/* Sous la note ou les sous-onglets, le premier intertitre ne rouvre pas un
   grand blanc : la note introduit déjà la page. */
:is(.tab-note, .subtab-nav, .periode-barre) + .section-head { margin-top: 8px; }
/* Le surtitre, DANS une carte : il la situe, discret. */
.surtitre { display: block; font-size: 11.5px; font-weight: 600; letter-spacing: 0;
  text-transform: none; color: var(--muted); margin: 0 0 6px; }
/* Les sous-onglets : un SEGMENT — poids constant, la sélection se dit par
   le relief, le bouton ne change jamais de largeur. La même grammaire vaut
   pour les vues d'une carte (.rm-vues) et l'apparence. */
.subtab-nav, .rm-vues, .apparence-seg { display: inline-flex; flex-wrap: wrap; gap: 2px;
  padding: 3px; border-radius: 10px; background: var(--segment-fond); border: 0; }
.subtab-nav { grid-column: span 12; justify-self: start; margin: 2px 0 2px; }
.subtab, .rm-vue, .apparence-seg button { border: 0; background: transparent;
  color: var(--encre-segment); padding: 7px 13px; font: inherit; font-size: 13px; line-height: 1;
  font-weight: 500; border-radius: var(--radius); cursor: pointer;
  transition: color .12s, background-color .12s, box-shadow .12s; }
.subtab:hover, .rm-vue:hover, .apparence-seg button:hover { color: var(--text); filter: none; }
.subtab.active, .rm-vue.active, .apparence-seg button[aria-pressed="true"] {
  background: var(--segment-actif); color: var(--text); font-weight: 600;
  box-shadow: var(--segment-ombre); }
.apparence-seg { flex: 1 1 100%; flex-wrap: nowrap; min-width: 0; }
.apparence-seg button { flex: 1 1 0; min-width: 0; display: inline-flex; align-items: center;
  justify-content: center; gap: 5px; padding: 6px 6px; font-size: 12px; border-radius: 7px; }
.apparence-seg button .ic { width: 13px; height: 13px; flex: none; }
.apparence-seg button span { min-width: 0; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; }

/* ── LA CARTE — rayon 12, une ombre à peine posée ───────────────────────── */
/* Une carte ne fait jamais défiler la page de côté : un mot plus
   long que sa ligne (une adresse, un identifiant) se coupe — ce qui est
   large par nature (un tableau, une grille, une cascade) défile DANS la
   carte, et les grilles de ses composants passent à une colonne avant de
   déborder. */
.card { background: var(--panel); border: 1px solid var(--border);
  border-radius: var(--rayon-carte); padding: 18px 20px; grid-column: span 6;
  min-width: 0; min-height: 0; box-shadow: var(--ombre-carte); overflow-wrap: break-word; }
.card.scalar { grid-column: span 3; }
.card.table { grid-column: span 12; }
/* La ligne du haut : un LIBELLÉ, pas un titre — puis les pastilles de la
   carte (« Exemple », le delta, « hors couloir »), et le menu ⋯, TOUJOURS
   dans le coin haut droit, hors du flux (la réserve de droite de la tête) :
   les têtes d'une même rangée gardent leur ⋯ au même endroit. Les
   pastilles suivent le libellé quand elles tiennent sur sa ligne ; sinon
   elles passent dessous, ALIGNÉES À GAUCHE sous le libellé — jamais un
   trou sous le titre, jamais un mot cassé lettre par lettre (le retour à
   la ligne se décide sur le texte entier du libellé, `flex-basis: auto` ;
   seul sur sa ligne, il se replie entre ses mots). « Jours actifs » et sa
   pastille tiennent sur une ligne d'une carte de trois colonnes. */
.ck-tete { display: flex; align-items: flex-start; justify-content: flex-start;
  flex-wrap: wrap; gap: 6px 8px; margin-bottom: 12px; position: relative; padding-right: 30px;
  min-height: 18px; }
.ck-label { flex: 1 1 auto; min-width: 0; font-size: 13px; font-weight: 600;
  color: var(--texte-2); line-height: 1.35; letter-spacing: -.005em; overflow-wrap: break-word; }
.ck-actions { display: inline-flex; align-items: center; gap: 6px; flex: none; min-height: 18px; }
/* Rien à montrer à côté du libellé (ni badge, ni delta posé) : la rangée
   des pastilles ne prend aucune place — pas même une ligne vide sous un
   long libellé. Le ⋯ reste, lui, dans son coin. */
.ck-delta-hote:empty { display: none; }
.ck-actions:not(:has(.badge-exemple, .ck-couloir, .ck-delta-hote > *)) { display: contents; }
.ck-tete .ck-menu-btn { position: absolute; top: -4px; right: -6px; }
.ck-menu-btn { width: 26px; height: 26px; padding: 0; border: 0;
  background: transparent; color: var(--muted); cursor: pointer;
  border-radius: 7px; font: 600 14px/1 var(--police, inherit); flex: none;
  display: inline-flex; align-items: center; justify-content: center; }
.ck-menu-btn:hover, .ck-menu-btn[aria-expanded="true"] { background: var(--panel2);
  color: var(--text); filter: none; }
.ck-menu { display: none; position: absolute; right: 0; top: calc(100% + 6px);
  z-index: 60; min-width: 210px; padding: 5px; background: var(--panel);
  border: 1px solid var(--border); border-radius: var(--rayon-flottant);
  box-shadow: var(--ombre-flottante); }
.ck-actions.pop-ouvert .ck-menu { display: block; animation: pop-entree .14s ease-out; }
.ck-menu-meta { padding: 7px 10px 8px; font-size: 11px; color: var(--muted);
  border-bottom: 1px solid var(--separateur); margin-bottom: 4px;
  font-variant-numeric: tabular-nums; }
.ck-item { display: block; width: 100%; text-align: left; padding: 8px 10px;
  border: 0; border-radius: var(--radius); background: transparent;
  color: var(--text); font: inherit; font-size: 13px; font-weight: 400; cursor: pointer; }
.ck-item:hover { background: var(--panel2); filter: none; }
/* Le chiffre, l'unité sur la ligne de base, le delta en pilule teintée. */
.big { display: flex; align-items: baseline; flex-wrap: wrap; gap: 6px; font-size: 30px;
  font-weight: var(--num-poids, 650); letter-spacing: -.03em; line-height: 1.1;
  color: var(--text); font-variant-numeric: tabular-nums; }
.big .unit { font-size: 13px; font-weight: 500; letter-spacing: -.005em;
  color: var(--muted); margin: 0; }
.subtitle { color: var(--muted); font-size: 12px; line-height: 1.5;
  margin-top: 9px; }
.delta { display: inline-flex; align-items: center; font-size: 11.5px;
  font-weight: 600; padding: 2px 8px; border-radius: 999px;
  font-variant-numeric: tabular-nums; border: 0; background: var(--panel2);
  color: var(--muted); white-space: nowrap; }
/* Le recul, son encre foncée vers le texte : --bad sur --bad-fond ne faisait
   que 4,41:1 en clair (AA demande 4,5:1 pour ce petit texte). */
.delta.good { color: var(--good); background: var(--good-fond); }
.delta.bad { color: color-mix(in srgb, var(--bad) 88%, var(--text)); background: var(--bad-fond); }
.card .desc { color: var(--muted); font-size: 12.5px; line-height: 1.6;
  margin-top: 10px; }
.card .desc[hidden] { display: none; }
.card .meta-line { display: flex; justify-content: space-between;
  align-items: center; color: var(--muted); font-size: 10.5px; margin-top: 10px; }
/* Le « je ne sais pas » d'un KPI : une réponse, pas une panne — un visage
   sobre, sa phrase lisible. */
.sans-reponse { background: var(--panel2); border: 1px dashed var(--border);
  border-radius: var(--radius); padding: 14px 16px; margin: 4px 0 2px;
  font-size: 12.5px; line-height: 1.6; color: var(--muted); }
.sans-reponse strong { display: block; color: var(--text); font-weight: 650;
  font-size: 13.5px; margin-bottom: 3px; }
.chart-wrap { position: relative; height: 260px; }
/* Le chargement : un mot discret qui respire — et, dans une carte, un
   squelette sobre à sa place, la forme de ce qui arrive (le mot reste lu
   par un lecteur d'écran). */
.loading { color: var(--muted); font-size: 12.5px; animation: respire 1.6s ease-in-out infinite; }
@keyframes respire { 50% { opacity: .55; } }
.card .loading, .pres-kpi .loading { position: relative; min-height: 64px;
  border-radius: var(--radius); color: transparent; overflow: hidden; user-select: none;
  background: linear-gradient(90deg, var(--panel2) 0%, var(--separateur) 50%, var(--panel2) 100%);
  background-size: 200% 100%; animation: squelette 1.4s ease-in-out infinite; }
.card.scalar .loading { min-height: 46px; max-width: 70%; }
@keyframes squelette { from { background-position: 100% 0; } to { background-position: -100% 0; } }
/* Un état vide : centré, discret, jamais un trou. */
.etat-vide { grid-column: span 12; display: grid; place-items: center; align-content: center;
  gap: 6px; min-height: 140px; padding: 18px;
  text-align: center; color: var(--muted); font-size: 13px; line-height: 1.5;
  border: 1px dashed var(--border); border-radius: var(--radius); }
.etat-vide strong { color: var(--text); font-weight: 600; }

/* ── DIALOGUES, PRÉSENTATION, CONSOLE SQL — les surfaces qui quittent le
   plan : elles portent l'ombre affirmée, sur un voile de l'encre de
   l'ombre (sa nuit en sombre : jamais un halo clair). ── */
/* ------ Dialog « Comprendre ce KPI » ------ */
.card h3 { display: flex; justify-content: space-between; align-items: flex-start; gap: 8px; }

#info-overlay { display: none; position: fixed; inset: 0; background: var(--voile); z-index: 100; align-items: center; justify-content: center; padding: 16px; }
.info-dialog { background: var(--panel); border: 1px solid var(--border); box-shadow: var(--ombre-flottante); border-radius: 16px; width: min(680px, 94vw); max-height: 86vh; display: flex; flex-direction: column; overflow: hidden; }
.info-head { display: flex; justify-content: space-between; align-items: center; gap: 12px; padding: 14px 18px; border-bottom: 1px solid var(--separateur); }
.info-head h2 { font-size: 15px; }
.info-body { padding: 16px 18px; overflow-y: auto; font-size: 13.5px; line-height: 1.6; }
.info-section { margin-bottom: 16px; }
.info-section h4 { font-size: 12px; color: var(--accent); letter-spacing: .05em; text-transform: uppercase; margin-bottom: 6px; }
.info-date { color: var(--muted); font-size: 11.5px; margin-top: 8px; font-style: italic; }
/* La carte dépliée : on vérifie un chiffre sans quitter sa ligne —
   tout ce qui le justifie au même endroit (maquette du 01/09/2026). */
#kpi-overlay { display: none; position: fixed; inset: 0; background: var(--voile); z-index: 110; align-items: center; justify-content: center; padding: 16px; }
.kpi-dialog { background: var(--panel); border: 1px solid var(--border); box-shadow: var(--ombre-flottante); border-radius: 16px; width: min(1060px, 94vw); max-height: 90vh; display: flex; flex-direction: column; overflow: hidden; }
.kpi-pied { color: var(--muted); font-size: 11.5px; line-height: 1.55; }
.kpi-pied p + p { margin-top: 6px; }
/* L'explication ⓘ dans la fenêtre : lisible comme du texte, pas comme une
   note de bas de page — c'est souvent ce qu'on est venu chercher. */
.kpi-pied h4 { font-size: 11px; color: var(--accent); letter-spacing: .05em;
  text-transform: uppercase; margin-bottom: 4px; }
.kpi-pied p:has(h4) { color: var(--text); font-size: 13px; line-height: 1.6;
  margin-top: 12px; }
.info-tech summary { color: var(--muted); font-size: 12px; cursor: pointer; }
.info-tech p { color: var(--muted); font-size: 12px; margin-top: 6px; }

/* ------ Mode présentation ------ */
body.pres-open { overflow: hidden; }
#pres-overlay { display: none; position: fixed; inset: 0; background: var(--bg); z-index: 120; flex-direction: column; }
.pres-top { flex: none; display: flex; justify-content: space-between; align-items: center; gap: 14px; padding: 12px 22px; border-bottom: 1px solid var(--border); background: var(--panel); }
.pres-context { color: var(--text); font-size: 14px; font-weight: 700; }
.pres-counter { color: var(--muted); font-size: 13px; margin-left: auto; font-variant-numeric: tabular-nums; }
.pres-stage { flex: 1; min-height: 0; display: flex; gap: 28px; padding: 22px 80px 26px; }
.pres-left { flex: 1.4; min-width: 0; display: flex; flex-direction: column; gap: 16px; overflow-y: auto; }
.pres-left.grid { display: grid; grid-template-columns: 1fr 1fr; align-content: start; }
.pres-left.grid .pres-section { grid-column: 1 / -1; }
.pres-section { flex: none; color: var(--accent); font-size: 12.5px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; }
.pres-kpi { background: var(--panel); border: 1px solid var(--border); border-radius: 16px; padding: 20px 24px; box-shadow: var(--ombre-carte); }
.pres-kpi h3 { font-size: 18px; margin-bottom: 14px; }
.pres-kpi .big { font-size: 54px; }
.pres-kpi .big .unit { font-size: 20px; }
.pres-kpi .subtitle { font-size: 14px; margin-top: 8px; }
.pres-kpi .delta { font-size: 16px; }
.pres-kpi.solo { flex: 1; min-height: 320px; display: flex; flex-direction: column; }
.pres-kpi.solo .pres-kpi-body { flex: 1; min-height: 0; display: flex; flex-direction: column; }
.pres-kpi.solo .chart-wrap { flex: 1; height: auto; min-height: 280px; }
#pres-overlay .table-scroll { max-height: calc(100vh - 320px); }
#pres-overlay table { font-size: 14px; }
.pres-right { flex: 1; min-width: 0; max-width: 620px; overflow-y: auto; border-left: 1px solid var(--border); padding-left: 28px; font-size: 15.5px; line-height: 1.7; }
.pres-exp { margin-bottom: 26px; }
.pres-exp h3 { font-size: 15px; margin-bottom: 8px; color: var(--text); }
.pres-exp h4 { font-size: 12.5px; color: var(--accent); letter-spacing: .05em; text-transform: uppercase; margin: 14px 0 6px; }
.pres-exp h4:first-child { margin-top: 0; }
.pres-exp p { margin-bottom: 4px; }
.pres-arrow { position: fixed; top: 50%; transform: translateY(-50%); width: 54px; height: 54px; border-radius: 50%; background: var(--panel); border: 1px solid var(--border); color: var(--text); font-size: 28px; line-height: 1; padding: 0; z-index: 121; box-shadow: var(--ombre-carte); }
.pres-arrow:hover { border-color: var(--accent); color: var(--accent); filter: none; }
#pres-prev { left: 14px; }
#pres-next { right: 14px; }

/* ------ Console SQL ------ */
#sql-overlay { display: none; position: fixed; inset: 0; background: var(--voile); z-index: 100; align-items: center; justify-content: center; padding: 16px; }
.sql-dialog { background: var(--panel); border: 1px solid var(--border); box-shadow: var(--ombre-flottante); border-radius: 16px; width: min(1280px, 94vw); height: min(820px, 90vh); display: flex; flex-direction: column; overflow: hidden; }
.sql-head { display: flex; justify-content: space-between; align-items: center; padding: 12px 16px; border-bottom: 1px solid var(--separateur); }
.sql-hint { color: var(--muted); font-size: 11.5px; margin-left: 10px; }
.sql-body { flex: 1; display: flex; min-height: 0; }
.sql-tree { width: 290px; flex: none; overflow: auto; border-right: 1px solid var(--separateur); padding: 10px 8px; font-size: 12px; }
.sql-tree details { margin-bottom: 2px; }
.sql-tree summary { cursor: pointer; padding: 3px 6px; border-radius: 6px; list-style-position: inside; }
.sql-tree summary:hover { background: var(--panel2); }
.sql-tree .sql-table { margin-left: 14px; }
.sql-tree .sql-table > summary { color: var(--text); }
.sql-insert { background: transparent; border: 1px solid var(--border); color: var(--accent); padding: 0 6px; font-size: 11px; border-radius: 5px; margin-left: 4px; cursor: pointer; }
.sql-insert:hover { border-color: var(--accent); }
.sql-cols { margin: 2px 0 6px 26px; }
.sql-col { color: var(--muted); padding: 1px 6px; border-radius: 5px; cursor: pointer; }
.sql-col:hover { background: var(--panel2); color: var(--text); }
.dim { color: var(--muted); font-size: 10.5px; }
.sql-main { flex: 1; display: flex; flex-direction: column; min-width: 0; padding: 12px 14px; gap: 10px; }
#sql-input { height: 130px; flex: none; background: var(--bg); color: var(--text); border: 1px solid var(--border); border-radius: 9px; padding: 10px 12px; font: 12.5px/1.5 var(--police-mono); resize: vertical; }
#sql-input:focus { border-color: var(--accent); }
.sql-actions { display: flex; align-items: center; gap: 10px; }
.sql-actions .ghost { font-size: 12px; padding: 6px 12px; }
.sql-info { color: var(--muted); font-size: 11.5px; }
.sql-result { flex: 1; min-height: 0; overflow: hidden; display: flex; flex-direction: column; }
.sql-scroll { max-height: none; flex: 1; border: 1px solid var(--border); border-radius: 9px; }
/* La bascule de l'arbre n'existe que sur un téléphone (plus bas). */
.sql-arbre-bascule { display: none; }
/* Sur un téléphone, l'arbre des tables à côté de la saisie lui laissait
   quelques pixels, et « Exécuter » sortait de la fenêtre : l'arbre passe
   AU-DESSUS, replié derrière sa bascule (`aria-expanded`), la saisie et le
   bouton prennent toute la largeur. */
@media (max-width: 760px) {
  #sql-overlay { padding: 8px; }
  .sql-dialog { width: 100%; height: 100%; border-radius: var(--rayon-flottant); }
  .sql-head { gap: 8px; align-items: flex-start; padding: 10px 12px; }
  .sql-hint { display: block; margin: 2px 0 0; }
  #sql-close { flex: none; white-space: nowrap; }
  .sql-body { flex-direction: column; }
  .sql-arbre-bascule { display: flex; flex: none; align-items: center; justify-content: space-between;
    gap: 8px; margin: 10px 12px 0; }
  /* Le chevron, dessiné à 16 px : le signe « ▾ » à 11 px se rendait sur 5 px. */
  .sql-arbre-chev { display: inline-flex; flex: none; width: 16px; height: 16px;
    transition: transform .15s; }
  .sql-arbre-chev svg { width: 100%; height: 100%; }
  .sql-body.sql-arbre-ouvert .sql-arbre-chev { transform: rotate(180deg); }
  /* L'arbre ouvert laisse la saisie et « Exécuter » à l'écran, même sur un
     écran bas : au plus 40 % de la hauteur, et pas plus que ce qu'il reste
     au-dessus d'eux (la tête, la bascule, la saisie, les boutons : 340 px). */
  .sql-tree { display: none; width: auto; max-height: min(40vh, max(64px, calc(100vh - 340px)));
    max-height: min(40dvh, max(64px, calc(100dvh - 340px))); margin: 6px 12px 0;
    border: 1px solid var(--separateur); border-radius: var(--radius); }
  .sql-body.sql-arbre-ouvert .sql-tree { display: block; }
  .sql-main { min-height: 0; padding: 10px 12px 12px; }
  .sql-actions { flex-wrap: wrap; }
  #sql-run { flex: 1 1 100%; }
}

/* ── LES TABLEAUX DES CARTES — lignes aérées, filet allégé ─────────────── */
table { width: 100%; border-collapse: collapse; font-size: 13px; }
th { text-align: left; color: var(--muted); font-size: 12px; font-weight: 500;
  padding: 9px 12px; border-bottom: 1px solid var(--border); position: sticky; top: 0;
  background: var(--panel); white-space: nowrap; }
td { padding: 9px 12px; white-space: nowrap;
  border-bottom: 1px solid var(--separateur); }
tbody tr:not(.grp):hover > td { background: color-mix(in srgb, var(--panel2) 80%, transparent); }
tbody tr:last-child > td { border-bottom: 0; }
.table-scroll { max-height: 420px; overflow: auto; }
td a { color: var(--accent); text-decoration: none; }
td a:hover { text-decoration: underline; }
.filter-chip { display: inline-flex; align-items: center; gap: 8px;
  background: var(--accent-fond);
  border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
  border-radius: 999px; padding: 3px 10px; font-size: 12px; margin-bottom: 8px; }
.filter-chip .chip-x { background: transparent; border: 0; color: var(--muted);
  cursor: pointer; padding: 0 2px; font-size: 12px; }
.filter-chip .chip-x:hover { color: var(--bad); filter: none; }
.pager { display: flex; align-items: center; justify-content: center; gap: 12px;
  margin-top: 10px; }
.pager .pg-info { color: var(--muted); font-size: 11.5px; font-variant-numeric: tabular-nums; }
.pager button { font-size: 12px; padding: 4px 12px;
  border-radius: var(--radius); }
.error { color: var(--bad); font-size: 12px; padding: 8px 0; }

/* ── L'impression : la page, ses cartes, rien du poste ──────────────────
   Une page imprimée (ou enregistrée en PDF pour une réunion) garde son
   en-tête, ses intertitres et ses cartes ; la barre, les menus et les
   boutons restent à l'écran. Une carte ne se coupe pas entre deux pages. */
@media print {
  body.barre-cockpit { padding-left: 0 !important; }
  /* Le bouton ⋯ et son menu seulement : `.ck-actions` porte aussi le delta
     de la carte et, en démonstration, son badge « Exemple » — une page
     imprimée ne perd ni l'un ni l'autre. */
  #tabs, .bandeau-sante, .bandeau-licence, .voyant-cache, .ck-menu-btn, .ck-menu, .subtab-nav, .pop-place,
  #connexion, #rech-overlay, .pres-arrow, .vc-btn { display: none !important; }
  /* La pilule « Démonstration » vit dans la barre, qui ne s'imprime pas :
     la page le dit en tête. */
  body.mode-demo #board::before { content: "Démonstration · chiffres inventés"; display: block;
    margin: 0 0 12px; font-size: 12px; font-weight: 600; color: var(--warn); }
  body { background: none; }
  main { display: block; padding: 0; max-width: none; }
  .page-tete { padding: 0 0 12px; max-width: none; }
  .card, .tab-note { break-inside: avoid; box-shadow: none; margin-bottom: 12px; }
  .table-scroll { max-height: none; overflow: visible; }
  a { text-decoration: none; }
}

/* ── Le magasin, dans la carte dépliée ──────────────────────────────────── */
/* ── La carte dépliée — les composants (maquette « huit briques ») ── */
.d-head { display: flex; align-items: flex-start; gap: 16px; padding: 18px 22px;
  border-bottom: 1px solid var(--border); }
.d-head h2 { margin: 0; font-size: 17px; font-weight: 700; letter-spacing: -.3px; }
.d-head .per { font-size: 12.5px; color: var(--muted); margin-top: 2px; }
.d-id { min-width: 0; }
.d-num { margin-left: auto; text-align: right; flex: none; }
.d-num b { display: block; font-size: 32px; font-weight: 700; letter-spacing: -1.1px;
  line-height: 1.05; font-variant-numeric: tabular-nums; }
.d-num .unit { font-size: 14px; font-weight: 500; color: var(--muted); letter-spacing: 0; }
.d-num > span { font-size: 12px; color: var(--muted); }
/* Le composant ne doit RIEN hériter : boîte et padding explicites. */
.x { width: 28px; height: 28px; padding: 0; border-radius: 7px;
  border: 1px solid var(--border); background: var(--panel2); color: var(--muted);
  display: grid; place-items: center; cursor: pointer; font-size: 14px; flex: none; }
.x:hover { color: var(--text); }
.d-corps { overflow-y: auto; min-height: 0; }
.d-cols { display: grid; grid-template-columns: minmax(0, 1.45fr) minmax(0, 1fr); }
.d-left { border-right: 1px solid var(--border); min-width: 0; }
.d-right { min-width: 0; }
.d-block { padding: 18px 22px; border-bottom: 1px solid var(--border); }
.d-left > .d-block:last-child, .d-right > .d-block:last-child { border-bottom: 0; }
.d-lab { font: 600 10.5px/1 var(--police); letter-spacing: .1em;
  text-transform: uppercase; color: var(--muted); margin-bottom: 11px;
  display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.d-vue[hidden] { display: none; }
.chart-note { font-size: 11.5px; color: var(--muted); margin-top: 9px; line-height: 1.5; }
/* La bascule Graphe / Tableau — un segment, pas des boutons. */
.tog { display: inline-flex; gap: 2px; padding: 2px; background: var(--panel2);
  border: 1px solid var(--border); border-radius: 7px; }
.tog button { font: 500 11.5px/1 var(--police); padding: 5px 10px; border: 0;
  border-radius: 5px; background: transparent; color: var(--muted); cursor: pointer; }
.tog button[aria-selected="true"] { background: var(--panel); color: var(--text);
  font-weight: 600; }
/* Sans tableau jumeau (un refus, une table, un graphique sans libellé), la
   bascule reste cachée : sans cette règle, `display: inline-flex` passait
   devant l'attribut hidden. */
.tog[hidden] { display: none; }
/* Le tableau de la carte dépliée (jumeau, historique). */
.tbl { width: 100%; border-collapse: collapse; font-size: 12.5px; }
.tbl th { text-align: left; font: 500 11px/1 var(--police); color: var(--muted);
  padding: 0 8px 8px; border-bottom: 1px solid var(--border); }
.tbl td { padding: 7px 8px; border-bottom: 1px solid var(--border); }
.tbl td.n { font-family: var(--police-mono);
  font-size: 12px; font-variant-numeric: tabular-nums; }
.tbl tr:last-child td { border-bottom: 0; }
.tbl tr.def-change td { border-left: 3px solid var(--warn);
  background: color-mix(in srgb, var(--warn) 6%, transparent); }
.carte-depliee .table-scroll { max-height: 300px; overflow-y: auto; }
/* La provenance : clé/valeurs, puis le code sous dépliant. */
.meta-kv { display: grid; grid-template-columns: auto 1fr; gap: 5px 14px;
  font-size: 12.5px; margin: 0; }
.meta-kv dt { color: var(--muted); }
.meta-kv dd { margin: 0; font-variant-numeric: tabular-nums; }
.prov { margin-top: 11px; }
.prov summary { cursor: pointer; font: 500 12.5px/1.4 var(--police);
  color: var(--accent); list-style: none; padding: 2px 0; }
.prov summary::-webkit-details-marker { display: none; }
.prov summary::before { content: "▸ "; }
.prov[open] summary::before { content: "▾ "; }
.prov .in { margin-top: 11px; }
.prov pre { padding: 12px 14px; overflow: auto; max-height: 380px;
  font: 11.5px/1.55 var(--police-mono);
  background: var(--panel2); border: 1px solid var(--border);
  border-radius: var(--radius); }
/* Les notes signées : avatar du badge, en-tête, texte. L'avatar se borne à la
   note : `.av` nomme aussi la barre de frise qui déborde à gauche (`.rm-barre.av`). */
.ann { display: flex; gap: 10px; padding: 11px 0; border-bottom: 1px solid var(--border); }
.ann:last-of-type { border-bottom: 0; }
.ann .av { width: 25px; height: 25px; padding: 0; border-radius: 999px;
  background: var(--panel2); border: 1px solid var(--border); display: grid;
  place-items: center; flex: none;
  font: 600 9.5px/1 var(--police-mono); color: var(--muted); }
.ann-b { min-width: 0; flex: 1; }
.ann-b .hd { font-size: 12px; color: var(--muted); margin-bottom: 2px; }
.ann-b .hd b { color: var(--text); font-weight: 600; }
.ann-b .hd u { text-decoration: none;
  font: 400 11px/1 var(--police-mono); }
.ann-b p { margin: 0; font-size: 12.5px; line-height: 1.55; }
.note-btn { background: transparent; border: 0; color: var(--muted);
  cursor: pointer; padding: 0 3px; font-size: 12px; }
.note-btn:hover { color: var(--text); filter: none; }
.ann-new { display: grid; gap: 8px; margin-top: 12px; }
.ann-new textarea, .ann-new input { font: inherit; font-size: 12.5px;
  color: var(--text); background: var(--panel); border: 1px solid var(--border);
  border-radius: 7px; padding: 8px 11px; resize: vertical; min-width: 0; }
.ann-new textarea::placeholder, .ann-new input::placeholder { color: var(--muted); }
/* Un placeholder est un exemple, jamais une valeur : maigre, à l'encre
   discrète — celle du thème, qui garde 4,5:1 sur le panneau en clair comme
   en sombre (le gris par défaut du navigateur tombait à 4:1 en sombre). */
input::placeholder, textarea::placeholder { color: var(--muted); font-weight: 400; opacity: 1; }
.ann-new-pied { display: flex; gap: 8px; }
.ann-new-pied input { flex: 1; }
.ann-b textarea { width: 100%; font: inherit; font-size: 12.5px;
  color: var(--text); background: var(--panel); border: 1px solid var(--border);
  border-radius: 7px; padding: 8px 11px; margin-bottom: 6px; }
/* Le pied d'actions : copier (avec la période), CSV, le domaine, la version. */
.d-foot { display: flex; align-items: center; gap: 8px; padding: 12px 22px;
  border-top: 1px solid var(--border); flex-wrap: wrap; }
.d-foot .sp { margin-left: auto; font: 600 10px/1 var(--police);
  letter-spacing: .08em; text-transform: uppercase; color: var(--muted); }
.btn { font: 500 12.5px/1 var(--police); padding: 8px 13px; border-radius: 7px;
  border: 1px solid var(--border); background: var(--panel2); color: var(--text);
  cursor: pointer; white-space: nowrap; }
.btn:hover { border-color: var(--accent); filter: none; }
.btn.solid { background: var(--text); color: var(--panel); border-color: var(--text); }
/* Un bouton inerte (« Copier le chiffre » sans chiffre montré) : il se voit
   éteint par ses jetons — pas par `opacity`, qui éteignait aussi l'anneau
   du focus (2,3:1 sur fond clair) —, ne réagit plus au pointeur, et garde
   le focus du clavier, dont l'anneau reste plein. */
.btn[aria-disabled="true"] { color: var(--muted); background: var(--panel2); border-color: var(--border);
  cursor: default; pointer-events: none; }
/* Un texte pour le seul lecteur d'écran (l'annonce de la carte dépliée,
   `#kpi-annonce`) : hors de la vue, pas hors de la page — ni
   `display: none` ni `visibility: hidden`, qu'un lecteur d'écran tait. */
.lecteur-seul { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0; }
@media (max-width: 860px) {
  .d-cols { grid-template-columns: 1fr; }
  .d-left { border-right: 0; border-bottom: 1px solid var(--border); }
}
.magasin-note { color: var(--muted); font-size: 11.5px; line-height: 1.5;
  margin-bottom: 8px; }
/* Le badge de couloir : une valeur hors de sa bande déclarée. */
.ck-couloir { display: inline-flex; align-items: center; gap: 4px;
  font-size: 10.5px; font-weight: 600; padding: 2px 8px; border-radius: 999px;
  color: var(--warn); background: color-mix(in srgb, var(--warn) 9%, transparent);
  border: 1px solid color-mix(in srgb, var(--warn) 38%, transparent);
  white-space: nowrap; }

/* ── « À regarder » et les domaines (le cap) ────────────────────────────── */
.a-regarder { grid-column: span 12; }
.a-regarder h2 { margin: 0 0 10px; }
.ar-ligne { display: flex; gap: 11px; align-items: flex-start; padding: 13px 15px;
  border-radius: calc(var(--radius) + 2px); margin-bottom: 8px;
  border: 1px solid color-mix(in srgb, var(--warn) 36%, transparent);
  background: color-mix(in srgb, var(--warn) 7%, transparent); }
.ar-ligne .ar-point { width: 6px; height: 6px; border-radius: 50%; flex: none;
  margin-top: 7px; background: var(--warn); }
.ar-ligne p { margin: 0; font-size: 13.5px; line-height: 1.5; }
.ar-ligne a { color: var(--text); font-weight: 600; white-space: nowrap; }
.domaines-titre { grid-column: span 12; font-size: 17px; font-weight: 700;
  letter-spacing: -.2px; margin: 26px 0 0; }
.domaines { grid-column: span 12; display: grid; gap: 12px;
  grid-template-columns: repeat(auto-fit, minmax(min(230px, 100%), 1fr)); }
.domaine { display: grid; grid-template-columns: 1fr auto; gap: 3px 10px;
  padding: 15px 17px; border-radius: var(--rayon-carte); cursor: pointer;
  text-align: left; font: inherit; color: var(--text); background: var(--panel);
  border: 1px solid var(--border); box-shadow: var(--ombre-carte); transition: border-color .15s; }
.domaine:hover { border-color: var(--accent); transform: none; filter: none; }
.domaine .do-t { font-weight: 700; font-size: 14px; }
.domaine .do-q { grid-column: 1; font-size: 12.5px; line-height: 1.45;
  color: var(--muted); }
.domaine .do-f { grid-column: 2; grid-row: 1 / span 2; align-self: center;
  font-size: 17px; color: var(--muted); }

/* ── Le bandeau de santé et le voyant de poste ──────────────────────────── */
.bandeau-sante { display: flex; flex-direction: column; gap: 6px;
  background: var(--bad); color: var(--panel); padding: 12px 16px;
  border-radius: var(--rayon-carte); font-size: 14px; }
/* Hors du plateau (ils survivent à la navigation), alignés sur lui. */
.bandeau-sante, .bandeau-licence, .voyant-cache { box-sizing: border-box; width: calc(min(1500px, 100%) - 56px);
  margin: 14px auto 0; }
.bandeau-sante[hidden] { display: none; }
.bs-titre { font-weight: 800; font-size: 15px; display: flex;
  align-items: center; gap: 8px; }
.bs-point { width: 10px; height: 10px; border-radius: 50%; background: var(--panel);
  flex: none; animation: bs-pulse 1.6s ease-in-out infinite; }
@keyframes bs-pulse { 0%, 100% { opacity: 1 } 50% { opacity: .35 } }
.bs-motifs { margin: 0; padding-left: 20px; }
.bs-motifs li { margin: 2px 0; }
.bandeau-sante a { color: var(--panel); text-decoration: underline; font-weight: 700; }

/* Le mouvement se tait pour qui l'a demandé au système : ni transition, ni
   squelette qui ondule, ni roue qui tourne — l'état se lit, immobile. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { transition: none !important; animation: none !important;
    scroll-behavior: auto !important; }
}
/* Un écran étroit : le plateau et l'en-tête se resserrent. */
@media (max-width: 760px) {
  main { padding: 16px 16px 40px; gap: 14px; }
  .page-tete { padding: 18px 16px 0; }
  .page-titre { font-size: 24px; }
  .page-tete:not([hidden]) + main { padding-top: 14px; }
  .bandeau-sante, .bandeau-licence, .voyant-cache { width: calc(100% - 32px); }
  .card { padding: 16px; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   ⑨ ROADMAP — surface de présentation en réunion.
   Couleurs : des jetons, aucune couleur de marque en dur.
   Parti pris : aucun tableau et aucun scroll interne — des BOARDS de cartes
   qui se lisent en un coup d'œil et se projettent en réunion.
   ═══════════════════════════════════════════════════════════════════════════ */
.card.roadmap, .card.board, .card.onglets, .card.charge,
.card.orgchart { grid-column: span 12; }

/* ── Les 4 chiffres de tête : l'anatomie vit désormais dans le socle
   (ck-tete / ck-label / big) — l'ancien réordonnancement par h3 et le
   cercle décoratif sont retirés avec elle. ── */
/* ── La frise ───────────────────────────────────────────────────────────── */
/* Large par nature, la frise défile DANS sa carte : chaque mois
   garde au moins 40 px, et la page ne défile plus de côté sur un
   téléphone. */
.rm-frise { position: relative; font-size: 12px; overflow-x: auto; overscroll-behavior-x: contain; }
.rm-ligne, .rm-entete { display: grid; grid-template-columns: var(--label-w) 1fr; align-items: center; gap: 14px;
  min-width: calc(var(--label-w) + 14px + var(--n) * 40px); }
.rm-entete { margin-bottom: 10px; }
/* `minmax(0, 1fr)` et non `1fr` (= `minmax(auto, 1fr)`) : la largeur de contenu d'une barre
   d'un seul mois deviendrait le minimum de SA colonne, et la ligne se décalerait de l'en-tête —
   la barre se lirait sous le mauvais mois. De même pour le plan de charge (.ch-piste). */
.rm-piste { display: grid; grid-template-columns: repeat(var(--n), minmax(0, 1fr)); align-items: center; min-height: 30px; }
.rm-cell { grid-row: 1; height: 100%; border-left: 1px solid var(--border); }
.rm-cell.now { background: color-mix(in srgb, var(--corail, var(--warn)) 13%, transparent); border-left: 2px solid var(--corail, var(--warn)); }
.rm-mois-row { min-height: 40px; }
.rm-mois { grid-row: 1; text-align: center; color: var(--muted); font-size: 11px; font-weight: 600; line-height: 1.25; text-transform: capitalize; }
.rm-mois .an { font-size: 9.5px; opacity: .6; font-weight: 500; }
.rm-mois.now { color: var(--corail, var(--warn)); font-weight: 700; }
.rm-legende { color: var(--muted); font-size: 11px; font-style: italic; }

.rm-cat { display: flex; align-items: center; gap: 9px; margin: 18px 0 6px; color: var(--text);
  font-size: 11.5px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; }
.rm-pastille { width: 11px; height: 11px; border-radius: 50%; background: var(--c); flex: none; }
.rm-cat-n { color: var(--muted); font-weight: 500; letter-spacing: 0; font-size: 11px; }
.rm-titre { color: var(--text); font-size: 12.5px; font-weight: 500; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; padding-left: 20px; }

/* Barres en CAPSULE */
.rm-barre { grid-row: 1; height: 24px; border-radius: 999px; display: flex; align-items: center;
  min-width: 0; overflow: hidden;
  justify-content: flex-end; gap: 6px; padding: 0 10px; cursor: default;
  transition: transform .12s, box-shadow .12s;
  box-shadow: 0 1px 3px color-mix(in srgb, var(--ombre-encre) 12%, transparent); }
.rm-barre:hover { transform: translateY(-1px) scale(1.012);
  box-shadow: 0 4px 12px color-mix(in srgb, var(--ombre-encre) 20%, transparent); }
.rm-barre.av { border-top-left-radius: 4px; border-bottom-left-radius: 4px; }
.rm-barre.ap { border-top-right-radius: 4px; border-bottom-right-radius: 4px; }
.rm-barre.st-en_cours { background: var(--c); }
.rm-barre.st-suivi { background: color-mix(in srgb, var(--c) 72%, var(--panel)); }
.rm-barre.st-livre { background: color-mix(in srgb, var(--c) 34%, var(--panel)); box-shadow: none; }
.rm-barre.st-livre::before { content: '✓'; margin-right: auto; color: var(--c); font-weight: 800; font-size: 12px; }
.rm-barre.st-prevu { background: color-mix(in srgb, var(--c) 12%, var(--panel)); border: 1.5px dashed var(--c); box-shadow: none; }
.rm-barre.st-pause { background: repeating-linear-gradient(45deg, var(--panel2), var(--panel2) 5px, transparent 5px, transparent 10px);
  border: 1.5px solid var(--border); box-shadow: none; }
.rm-barre.st-abandonne { background: transparent; border: 1px solid var(--border); opacity: .5; box-shadow: none; }
.rm-taille { font-size: 10px; font-weight: 800; color: var(--encre-sur-accent); letter-spacing: .03em; }
/* La charge SUR la barre : « 120 JO · 3 pers. ». `min-width: 0` + ellipse pour
   qu'une barre d'un seul mois tronque proprement au lieu de déborder. Rien
   n'est écrit quand l'équipe n'est pas renseignée : sur une roadmap sans
   responsables, ce serait autant de fois le même bruit. Le trou se signale
   au bon endroit — la ligne « NON chiffré » de la synthèse, qui les nomme. */
.rm-charge { font-size: 10px; font-weight: 600; color: var(--encre-sur-accent); opacity: .92;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0;
  font-variant-numeric: tabular-nums; }
.rm-barre.st-prevu .rm-taille, .rm-barre.st-pause .rm-taille,
.rm-barre.st-livre .rm-taille, .rm-barre.st-abandonne .rm-taille,
.rm-barre.st-prevu .rm-charge, .rm-barre.st-pause .rm-charge,
.rm-barre.st-livre .rm-charge, .rm-barre.st-abandonne .rm-charge {
  color: var(--text); opacity: .75; }
/* Piloté par un tiers */
.rm-barre.ext { outline: 2px dotted var(--violet, var(--serie-3)); outline-offset: 2px; }
.rm-barre.ext::after { content: 'EXT'; font-size: 8.5px; font-weight: 800; color: var(--encre-sur-accent); opacity: .9; }
.rm-barre.st-prevu.ext::after { color: var(--violet, var(--serie-3)); opacity: 1; }

.rm-pied { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; margin-top: 20px;
  padding-top: 12px; border-top: 1px solid var(--border); color: var(--muted); font-size: 11px; }
.rm-lg { display: inline-flex; align-items: center; gap: 6px; }
.rm-lg::before { content: ''; width: 20px; height: 11px; border-radius: 999px; background: var(--muted); }
.rm-lg.st-en_cours::before { background: var(--bleu, var(--serie-1)); }
.rm-lg.st-suivi::before { background: color-mix(in srgb, var(--bleu, var(--serie-1)) 72%, var(--panel)); }
.rm-lg.st-prevu::before { background: color-mix(in srgb, var(--bleu, var(--serie-1)) 12%, var(--panel)); border: 1.5px dashed var(--bleu, var(--serie-1)); }
.rm-lg.st-livre::before { background: color-mix(in srgb, var(--bleu, var(--serie-1)) 34%, var(--panel)); }
.rm-lg.st-pause::before { background: repeating-linear-gradient(45deg, var(--panel2), var(--panel2) 4px, transparent 4px, transparent 8px); border: 1px solid var(--border); }
.rm-sep { flex: 1; }

/* Carte de détail (frise, boards, plan de charge) : survol = aperçu, clic = épinglé */
.rm-carte { position: absolute; z-index: 20; width: 400px; max-width: 92%; background: var(--panel);
  border: 1px solid var(--border); border-radius: var(--rayon-flottant); padding: 14px 16px;
  box-shadow: var(--ombre-flottante); display: flex; flex-direction: column;
  max-height: var(--carte-place, none); }
.rm-carte[hidden] { display: none; }
/* Un détail peut être long : il défile ici, ce qui borne la hauteur de la
   carte (donc son placement) et garde le ✕ au coin. `--carte-place`, posé à
   chaque pose (installerCarteFlottante), borne la carte à la place du côté
   choisi : elle défile, et ne recouvre l'élément survolé qu'en dernier
   recours. */
.rm-carte-in { max-height: min(62vh, 540px); min-height: 0; overflow-y: auto; }
.rm-carte-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  border-left: 4px solid; padding-left: 11px; margin-bottom: 10px; padding-right: 22px; }
.rm-carte-head strong { font-size: 14px; }
/* L'état de la ligne, juste sous le titre : créneau et étapes à gauche,
   échéance à droite — la date se cherche toujours au même endroit. */
.rm-avct { display: flex; align-items: baseline; justify-content: space-between;
  gap: 10px; flex-wrap: wrap; font-size: 11.5px; margin-bottom: 9px; }
.rm-pourquoi { color: var(--text); font-size: 12.5px; line-height: 1.55; margin-bottom: 10px; }
.rm-carte-metr { margin-bottom: 10px; padding-top: 8px; border-top: 1px solid var(--border); }

.rm-carte a { color: var(--accent); text-decoration: none; font-weight: 600; }
.rm-carte a:hover { text-decoration: underline; }

/* ── Jetons partagés (chips, métriques, étapes) ─────────────────────────── */
.rm-chip { background: var(--panel2); border: 1px solid var(--border); border-radius: 999px;
  padding: 2px 10px; font-size: 10.5px; font-weight: 600; color: var(--muted); white-space: nowrap; }
.rm-chip.ext { color: var(--violet, var(--serie-3)); border-color: color-mix(in srgb, var(--violet, var(--serie-3)) 45%, transparent); background: color-mix(in srgb, var(--violet, var(--serie-3)) 10%, transparent); }
.rm-chip.st-en_cours { color: color-mix(in srgb, var(--corail, var(--warn)) 70%, var(--text)); border-color: color-mix(in srgb, var(--corail, var(--warn)) 50%, transparent); background: color-mix(in srgb, var(--corail, var(--warn)) 14%, transparent); }
.rm-chip.st-livre { color: var(--bleu, var(--serie-1)); border-color: color-mix(in srgb, var(--bleu, var(--serie-1)) 40%, transparent); background: color-mix(in srgb, var(--bleu, var(--serie-1)) 10%, transparent); }

.rm-step { font-size: 12px; line-height: 1.5; padding: 2px 0; color: var(--text); }
.rm-step.done { color: var(--muted); text-decoration: line-through; opacity: .6; font-size: 11px; }
.rm-step.next { font-weight: 600; }
.rm-t-muted { color: var(--muted); font-style: italic; opacity: .85; }
.rm-metr { font-size: 12px; line-height: 1.5; padding: 2px 0; }
.rm-metr-lib { color: var(--muted); }
.rm-metr strong { color: var(--text); font-weight: 700; }
.rm-live { color: var(--bleu, var(--serie-1)); font-size: 10px; vertical-align: middle; }
.rm-live.off { color: var(--muted); opacity: .55; }
.rm-metr-src { color: var(--muted); font-size: 9px; text-transform: uppercase; letter-spacing: .07em;
  margin-left: 7px; opacity: .8; font-weight: 600; }
.rm-delta { color: var(--good); font-size: 11px; font-weight: 700; }
.rm-dl { font-size: 11.5px; font-weight: 600; white-space: nowrap; color: var(--muted); }
.rm-dl.retard { color: var(--bad); }

/* ── BOARD : le composant qui remplace les tableaux ─────────────────────── */
/* ⚠ Le conteneur ne doit PAS s'appeler « board » : la carte porte déjà la
   classe du TYPE de KPI (card board), et la grille s'appliquerait à elle. */
.rm-board { display: grid; gap: 16px; grid-template-columns: repeat(var(--cols, 4), minmax(0, 1fr)); align-items: start; }
.rm-bcol { min-width: 0; }
.rm-bcol-head { display: flex; align-items: center; gap: 8px; padding: 0 2px 9px;
  margin-bottom: 12px; border-bottom: 2px solid var(--c, var(--border)); }
.rm-bcol-head .pt { width: 10px; height: 10px; border-radius: 50%; background: var(--c, var(--muted)); flex: none; }
.rm-bcol-head h4 { font-size: 11.5px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--text); }
.rm-bcol-head .n { margin-left: auto; color: var(--muted); font-size: 11.5px; font-weight: 700; }
.rm-bcol-head .somme { color: var(--muted); font-size: 10.5px; font-weight: 600; }
.bcards { display: flex; flex-direction: column; gap: 10px; }
.bcard { background: var(--panel); border: 1px solid var(--border); border-radius: 14px;
  padding: 12px 14px; box-shadow: var(--ombre, 0 0 transparent); border-left: 4px solid var(--c, var(--border));
  transition: transform .12s, box-shadow .12s; }
.bcard:hover { transform: translateY(-2px);
  box-shadow: 0 8px 22px color-mix(in srgb, var(--ombre-encre) 11%, transparent); }
.bcard-head { display: flex; align-items: flex-start; gap: 8px; margin-bottom: 4px; }
.bcard h5 { font-size: 13px; font-weight: 700; line-height: 1.35; color: var(--text); flex: 1; min-width: 0; }
.bcard .obj { color: var(--muted); font-size: 11.5px; font-style: italic; line-height: 1.45; margin-bottom: 8px; }
.bcard .pourquoi { color: var(--muted); font-size: 11.5px; line-height: 1.5; }
.bcard-chips { display: flex; gap: 5px; flex-wrap: wrap; margin-bottom: 8px; }
.bcard-bloc { border-top: 1px dashed var(--border); padding-top: 8px; margin-top: 8px; }
.bcard-pied { display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  border-top: 1px solid var(--border); padding-top: 8px; margin-top: 9px; }
.bcard-resp { display: inline-flex; align-items: center; gap: 6px; color: var(--muted); font-size: 11.5px; font-weight: 600; }
.bcard-resp .ini { width: 20px; height: 20px; border-radius: 50%; background: var(--c, var(--accent));
  color: var(--encre-sur-accent); font-size: 9px; font-weight: 800; display: inline-flex; align-items: center; justify-content: center; }
.bcard-pied .rm-dl { margin-left: auto; }
/* Colonne compacte : des libellés, pas des fiches. Divise la hauteur par ~3
   sur les listes longues (rétrospective, backlog) — objectif « sans scroll ». */
.rm-bcol.compact .bcards { gap: 6px; }
.rm-bcol.compact .bcard { padding: 7px 11px; border-radius: 10px; border-left-width: 3px; box-shadow: none; }
.rm-bcol.compact .bcard:hover { transform: none; box-shadow: var(--ombre, 0 0 transparent); }
.rm-bcol.compact .bcard-head { margin-bottom: 0; }
.rm-bcol.compact .bcard h5 { font-size: 12px; font-weight: 600; line-height: 1.3; }
.rm-bcol.compact .bcard-chips { margin: 5px 0 0; gap: 4px; }
.rm-bcol.compact .rm-chip { font-size: 9.5px; padding: 1px 7px; }
.rm-bcol.compact .pourquoi { display: none; }
/* Carte survolable : on annonce l'interaction sans alourdir le repos. */
.bcard.surv { cursor: help; }
.bcard.surv:hover { border-color: var(--c);
  box-shadow: 0 4px 18px color-mix(in srgb, var(--ombre-encre) 24%, transparent); }

/* Sorti du plan : présent, mais qui ne vole pas la vedette au livré. */
.rm-sortis { margin-top: 14px; padding-top: 12px; border-top: 1px dashed var(--border);
  display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.rm-sortis-t { width: 100%; color: var(--muted); font-size: 10.5px;
  text-transform: uppercase; letter-spacing: .5px; margin-bottom: 2px; }
.rm-parque { display: inline-flex; align-items: baseline; gap: 6px; cursor: help;
  padding: 3px 10px; border-radius: 11px; border: 1px solid var(--border);
  background: color-mix(in srgb, var(--muted) 8%, transparent);
  color: var(--muted); font-size: 11.5px; }
.rm-parque:hover { color: var(--fg); border-color: var(--muted); }
.rm-parque em { font-style: normal; opacity: .6; font-size: 10px; }

/* Détail au survol : les étapes du chantier telles que Notion les porte. */
.rm-carte-steps { margin: 6px 0; display: grid; gap: 2px; }

/* Le pilotage du lundi en tableau : on descend la colonne « prochaine étape »,
   l'œil ne saute pas d'un bloc à l'autre comme avec des cartes. */
.rm-t-init { display: flex; align-items: baseline; gap: 7px; }
.rm-t-init strong { font-size: 12.5px; line-height: 1.35; }
.rm-pt { width: 8px; height: 8px; border-radius: 50%; background: var(--c);
  flex: 0 0 8px; }
.rm-t-chips { display: flex; flex-wrap: wrap; gap: 4px; margin: 4px 0 3px 15px; }
.rm-t-init + .rm-t-chips + .rm-t-muted { margin-left: 15px; display: block; }
.rm-t-alerte { color: var(--bad); font-size: 11px; margin-top: 5px; line-height: 1.4; }
.rm-t-resp { font-size: 11.5px; white-space: nowrap; }
/* Largeurs FIXÉES : laissées libres, les colonnes s'étalaient à 2400 px pour
   1680 disponibles. L'étape du lundi prend la plus large part, l'échéance la
   plus étroite — c'est l'ordre d'importance du rituel. */
/* Pas de scroll interne : le lundi, on descend la liste ENTIÈRE d'un regard.
   Un cadre qui défile oblige à chercher ce qui reste dessous — exactement ce
   qu'un rituel hebdomadaire ne doit pas demander. */
.rm-vue-pilotage .table-scroll { max-height: none; overflow: visible; }
.rm-vue-pilotage table { table-layout: fixed; width: 100%; }
/* Les tableaux du BI sont en `nowrap` (colonnes de chiffres) ; celui-ci porte
   du TEXTE, il doit revenir à la ligne — sans ça les cellules se chevauchent. */
.rm-vue-pilotage td, .rm-vue-pilotage td * { white-space: normal; }
.rm-vue-pilotage td { vertical-align: top; overflow-wrap: anywhere; }
.rm-vue-pilotage th:nth-child(1), .rm-vue-pilotage td:nth-child(1) { width: 24%; }
.rm-vue-pilotage th:nth-child(2), .rm-vue-pilotage td:nth-child(2) { width: 32%; }
.rm-vue-pilotage th:nth-child(3), .rm-vue-pilotage td:nth-child(3) { width: 26%; }
.rm-vue-pilotage th:nth-child(4), .rm-vue-pilotage td:nth-child(4) { width: 11%; }
.rm-vue-pilotage th:nth-child(5), .rm-vue-pilotage td:nth-child(5) { width: 7%; }

/* La charge en jours ouvrés : le chiffre doit se lire avant le texte qui
   l'explique. `fort` = le semestre ne rentre pas ; `manque` = pas chiffrable. */
.rm-jo { font-size: 17px; font-weight: 700; font-variant-numeric: tabular-nums;
  white-space: nowrap; }
.rm-jo.fort { color: var(--bad); }
.rm-jo.manque { color: var(--muted); font-size: 13px; font-weight: 600; }

/* Onglets de vue DANS une carte : la même donnée sous deux angles, à la façon
   des vues d'une base Notion. Volontairement plus discrets que les sous-onglets
   de page — c'est une bascule de lecture, pas un changement de sujet. */
.rm-vues { margin-bottom: 14px; }

/* Séparateur de catégorie : le pilotage porte les mêmes en-têtes que la frise,
   dans le même ordre — c'est ce qui rend la bascule entre les deux gratuite. */
tr.grp td { background: var(--bg); font-size: 11px; letter-spacing: .06em;
  text-transform: uppercase; color: var(--muted); font-weight: 700;
  padding: 10px 8px 6px; border-bottom: 1px solid var(--border); }
tr.grp .rm-pt { display: inline-block; vertical-align: middle; margin-right: 7px; }
/* La ligne du total, épinglée en tête : un sous-total, pas un mois de plus. */
tr.ligne-total td { font-weight: 600; background: var(--panel2); border-bottom: 1px solid var(--border); }
.rm-grp-n { margin-left: 8px; color: var(--muted); font-weight: 400;
  letter-spacing: 0; text-transform: none; }
/* Le libellé d'un KPI mène au KPI : une métrique doit être vérifiable. */
a.rm-metr-lib { color: inherit; text-decoration: underline;
  text-decoration-style: dotted; text-underline-offset: 2px; cursor: pointer; }
a.rm-metr-lib:hover { color: var(--accent); }

/* Carte épinglée : elle reste, donc on peut lancer un média, suivre un lien,
   la fermer. Sans épinglage, tout média serait décoratif. */
.rm-carte.epingle { box-shadow: 0 12px 40px color-mix(in srgb, var(--ombre-encre) 40%, transparent);
  border-color: var(--accent); max-height: min(70vh, var(--carte-place, 70vh)); overflow-y: auto; }
.rm-carte-x { position: absolute; top: 6px; right: 6px; width: 22px; height: 22px;
  border: 0; border-radius: 50%; cursor: pointer; font-size: 11px; line-height: 1;
  background: var(--border); color: var(--fg); display: none; }
.rm-carte.epingle .rm-carte-x { display: block; }
.rm-carte-x:hover { background: var(--accent); color: var(--encre-sur-accent); }
.surv-actif { outline: 2px solid var(--accent); outline-offset: 1px; }

/* Plan de charge : grille personne × mois. Le CREUX doit se voir autant que
   la surcharge — c'est lui qui dit à qui confier la ligne suivante. */
.ch-legende { display: flex; flex-wrap: wrap; gap: 14px; margin-bottom: 10px;
  color: var(--muted); font-size: 11px; align-items: center; }
.ch-legende span { display: inline-flex; align-items: center; gap: 5px; }
.ch-legende i.lg { width: 13px; height: 13px; border-radius: 3px;
  border: 1px solid var(--border); display: inline-block; }
/* Le plan de charge aussi défile DANS sa carte : 36 px par mois
   au moins. */
.ch-grille { display: grid; gap: 3px; overflow-x: auto; overscroll-behavior-x: contain; }
.ch-ligne { display: grid; grid-template-columns: var(--lw) 1fr; gap: 8px;
  align-items: center; min-width: calc(var(--lw) + 8px + var(--n) * 36px); }
.ch-nom { font-size: 12px; font-weight: 600; text-align: right; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
.ch-piste { display: grid; grid-template-columns: repeat(var(--n), minmax(0, 1fr)); gap: 3px; }
.ch-head .ch-mois { text-align: center; font-size: 10px; color: var(--muted);
  line-height: 1.25; }
.ch-head .ch-mois.now { color: var(--accent); font-weight: 700; }
.ch-mois .an { opacity: .6; font-size: 9px; }
.ch-cell { height: 30px; border-radius: 5px; border: 1px solid var(--border);
  display: flex; align-items: center; justify-content: center; gap: 3px;
  font-size: 11px; font-weight: 700; }
.ch-cell[data-survol] { cursor: help; }
.ch-cell[data-survol]:hover { outline: 2px solid var(--accent); outline-offset: -2px; }
/* Une ligne de la carte de survol : pastille de catégorie + le chantier. */
.ch-item { display: flex; gap: 8px; align-items: flex-start; padding: 6px 0;
  border-top: 1px solid var(--border); font-size: 12px; line-height: 1.4; }
.ch-item:first-of-type { border-top: 0; }
.ch-item .rm-pt { margin-top: 5px; }
.ch-cell.now { outline: 2px solid var(--accent); outline-offset: -2px; }
.ch-cell.libre { background: repeating-linear-gradient(45deg,
  transparent 0 4px, color-mix(in srgb, var(--muted) 10%, transparent) 4px 8px); }
.ch-cell.ok { background: color-mix(in srgb, var(--bleu, var(--serie-1)) 26%, transparent); }
.ch-cell.tendu { background: color-mix(in srgb, var(--corail, var(--warn)) 52%, transparent); }
.ch-cell.surcharge { background: color-mix(in srgb, var(--bad) 62%, transparent);
  color: var(--encre-sur-accent); }
.ch-legende i.lg.libre { background: repeating-linear-gradient(45deg,
  transparent 0 3px, color-mix(in srgb, var(--muted) 22%, transparent) 3px 6px); }
.ch-legende i.lg.ok { background: color-mix(in srgb, var(--bleu, var(--serie-1)) 26%, transparent); }
.ch-legende i.lg.tendu { background: color-mix(in srgb, var(--corail, var(--warn)) 52%, transparent); }
.ch-legende i.lg.surcharge { background: color-mix(in srgb, var(--bad) 62%, transparent); }
/* Une pastille par chantier : la couleur dit la catégorie d'investissement. */
.ch-cell i { width: 5px; height: 5px; border-radius: 50%; background: var(--c);
  display: inline-block; }
.ch-n { opacity: .85; }

/* Médias d'une initiative : la preuve visuelle de ce qui a été livré. */
.rm-medias { display: flex; flex-wrap: wrap; gap: 6px; margin: 8px 0; }
.rm-media { max-width: 100%; max-height: 190px; border-radius: 6px;
  border: 1px solid var(--border); display: block; background: var(--panel2); }
.rm-media-fic { font-size: 11px; color: var(--accent); text-decoration: none;
  border: 1px solid var(--border); border-radius: 6px; padding: 4px 9px; }

.bcard.vide { border-left-color: var(--border); text-align: center; color: var(--muted);
  font-style: italic; font-size: 12px; padding: 18px 14px; box-shadow: none; background: transparent;
  border-style: dashed; }
.board-total { display: flex; align-items: baseline; gap: 8px; margin-top: 14px; padding-top: 12px;
  border-top: 1px solid var(--border); color: var(--muted); font-size: 11.5px;
  /* Les blocs « sorti du plan » s'empilent sous le total plutôt que de se
     tasser en colonnes à côté de lui. */
  flex-wrap: wrap; }
.board-total .rm-sortis { flex: 1 0 100%; }
.board-total strong { color: var(--text); font-size: 15px; font-weight: 700; }

.card.flash { animation: flash 1.6s ease-out; }
@keyframes flash { 0%, 100% { box-shadow: var(--ombre, 0 0 transparent); } 15% { box-shadow: 0 0 0 3px var(--accent); } }

/* Mode présentation : tout grossit */
.rm-frise.big { font-size: 14px; }
.rm-frise.big .rm-titre { font-size: 14.5px; }
.rm-frise.big .rm-barre { height: 28px; }
.rm-frise.big .rm-mois { font-size: 12.5px; }
.rm-frise.big .rm-cat { font-size: 13px; }
#pres-overlay .bcard h5 { font-size: 15px; }
#pres-overlay .rm-board { gap: 18px; }

@media (max-width: 1400px) { .rm-board { --cols: 2 !important; } }
@media (max-width: 900px) { .rm-board { --cols: 1 !important; } }
/* Sur un téléphone, la colonne des libellés de la frise et du plan de charge
   se resserre : la place va aux mois, le reste défile dans la carte. */
@media (max-width: 640px) {
  .rm-frise { --label-w: 140px !important; }
  .ch-grille { --lw: 110px !important; }
}

@media (max-width: 1100px) { .card { grid-column: span 12; } .card.scalar { grid-column: span 6; } }
/* Un téléphone : un chiffre par ligne — deux côte à côte serraient le
   libellé contre son delta. */
@media (max-width: 560px) { .card.scalar { grid-column: span 12; } }

/* ─── Organigramme (type "orgchart") ──────────────────────────────────────
   Cartes reliées par des traits, reconstruites depuis le SIRH à chaque
   calcul. Les connecteurs sont en CSS pur (bordures
   sur ::before/::after des <li>) : aucune librairie, et ça reste net au zoom. */
.og-barre { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 10px; }
.og-chip { display: inline-flex; align-items: center; gap: 6px; cursor: pointer;
  font: inherit; font-size: 11.5px; font-weight: 600; color: var(--muted);
  background: var(--panel); border: 1px solid var(--border); border-radius: 999px;
  padding: 5px 11px; transition: color .12s, border-color .12s, background .12s; }
.og-chip i { width: 8px; height: 8px; border-radius: 50%; background: var(--c, var(--muted)); }
.og-chip:hover { color: var(--text); border-color: var(--muted); }
.og-chip.active { color: var(--text); background: var(--panel2); border-color: var(--c, var(--accent)); }
.og-chip-n { color: var(--muted); font-weight: 500; }
.og-info { font-size: 11.5px; margin-bottom: 6px; }

/* L'arbre déborde en largeur par construction : il défile DANS sa carte,
   jamais la page (règle de responsive du dashboard). */
.og-scroll { overflow-x: auto; overflow-y: hidden; padding: 4px 0 10px; }
.og-scroll.grand { font-size: 13px; }
.og-tree { display: inline-block; min-width: 100%; }
.og-tree ul { display: flex; justify-content: center; padding: 0; margin: 0;
  list-style: none; position: relative; }
.og-tree li { position: relative; list-style: none; padding: 16px 5px 0;
  display: flex; flex-direction: column; align-items: center; }
/* Les deux demi-traits horizontaux qui relient une fratrie */
.og-tree li::before, .og-tree li::after { content: ''; position: absolute; top: 0;
  width: 50%; height: 16px; border-top: 1px solid var(--border); }
.og-tree li::before { right: 50%; }
.og-tree li::after { left: 50%; }
.og-tree li:only-child::before, .og-tree li:only-child::after { display: none; }
.og-tree li:first-child::before, .og-tree li:last-child::after { border-top: 0; }
.og-tree li:last-child::before { border-right: 1px solid var(--border);
  border-radius: 0 6px 0 0; }
.og-tree li:first-child::after { border-left: 1px solid var(--border);
  border-radius: 6px 0 0 0; }
/* Le trait vertical qui descend d'un parent vers sa fratrie d'enfants */
.og-tree ul ul { padding-top: 16px; }
.og-tree ul ul::before { content: ''; position: absolute; top: 0; left: 50%;
  height: 16px; border-left: 1px solid var(--border); }
.og-tree > ul > li { padding-top: 0; }
.og-tree > ul > li::before, .og-tree > ul > li::after { display: none; }

.og-carte { position: relative; min-width: 132px; max-width: 190px;
  background: var(--panel); border: 1px solid var(--border);
  border-top: 3px solid var(--c, var(--accent)); border-radius: 8px;
  padding: 7px 10px 8px; box-shadow: var(--ombre, 0 0 transparent); text-align: center; }
.og-carte[data-pliable] { cursor: pointer; }
.og-carte[data-pliable]:hover { border-color: var(--muted); }
/* Encadrant d'une autre direction, affiché pour le contexte de rattachement */
.og-carte.hors { background: var(--panel2); box-shadow: none; opacity: .72; }
.og-carte.ferme { border-bottom: 3px double var(--border); }
.og-nom { font-size: 12px; font-weight: 700; color: var(--text); line-height: 1.25; }
.og-poste { font-size: 10.5px; color: var(--muted); line-height: 1.3; margin-top: 2px; }
.og-meta { display: flex; flex-wrap: wrap; gap: 4px; justify-content: center; margin-top: 5px; }
.og-badge { font-size: 9.5px; font-weight: 600; color: var(--warn);
  background: var(--panel2); border: 1px solid var(--border); border-radius: 999px;
  padding: 1px 6px; }
.og-equipe { font-size: 9.5px; font-weight: 700; color: var(--panel);
  background: var(--c, var(--accent)); border-radius: 999px; padding: 1px 7px; }
.og-abs { font-size: 10px; color: var(--accent); margin-left: 4px; vertical-align: 1px; }
.og-plus { position: absolute; bottom: -9px; left: 50%; transform: translateX(-50%);
  font-size: 9.5px; font-weight: 700; color: var(--muted); background: var(--panel);
  border: 1px solid var(--border); border-radius: 999px; padding: 0 6px; }

/* Le bouton d'export du support. Poussé à droite de la barre de filtres :
   c'est une action, pas un filtre — le regrouper avec les puces de direction
   le ferait lire comme une vue de plus. */
.og-pdf { margin-left: auto; cursor: pointer; font: inherit; font-size: 11.5px;
  font-weight: 700; color: var(--accent-encre); background: var(--accent);
  border: 1px solid var(--accent); border-radius: 999px; padding: 5px 13px;
  transition: opacity .12s; }
.og-pdf:hover { opacity: .88; }
.og-pdf:disabled { opacity: .55; cursor: progress; }

/* Linter de vérité sur la console SQL (celui de l'instance) :
   il n'empêche jamais la requête — il dit ce que le chiffre ne dit pas. */
.sql-doctrine { margin: 0 0 10px; font-size: 11.5px; }
.sql-doctrine ul { margin: 0; padding-left: 18px; }
.sql-doctrine .sd-warn { color: var(--warn); }
.sql-doctrine .sd-warn b { font-weight: 600; }
.sql-doctrine .sd-note { margin-top: 4px; }

/* --- Entonnoir (type `funnel`) -------------------------------------------
   Barres décroissantes plutôt que trapèze : sur un entonnoir qui perd
   neuf dixièmes de son entrée, un trapèze à l'échelle écrase les dernières
   étapes jusqu'à l'illisible. La piste garde la largeur pleine, la barre porte
   la proportion — on compare donc à l'œil sans perdre le bas de l'entonnoir.
   `:not(.card)` : la carte de type `funnel` porte aussi la classe nue de son
   type — sans lui, elle perdait son padding et passait en colonne. */
.funnel:not(.card) { display: flex; flex-direction: column; gap: 7px; padding: 2px 0; }
.fn-ligne { display: grid; grid-template-columns: 74px 1fr 132px;
  align-items: center; gap: 10px; }
.fn-nom { font-size: 12px; font-weight: 600; color: var(--text); }
.fn-piste { position: relative; background: var(--border); border-radius: 5px;
  height: 26px; overflow: hidden; }
.fn-barre { height: 100%; border-radius: 5px; transition: width .2s; }
.fn-val { position: absolute; inset: 0; display: flex; align-items: center;
  padding-left: 9px; font-size: 12px; font-weight: 700; color: var(--text); }
.fn-taux { display: flex; align-items: baseline; justify-content: flex-end;
  gap: 7px; font-size: 11px; }
/* Le taux de passage depuis l'étape précédente : c'est LUI qui dit où l'on
   perd. En dessous de 50 %, il se signale — une étape qui divise par deux. */
.fn-chute { font-weight: 700; color: var(--text); }
.fn-alerte { color: var(--bad); }
.fn-part { color: var(--muted); }
.fn-note { color: var(--muted); font-size: 11px; margin-top: 9px;
  line-height: 1.45; }
.funnel-grand .fn-ligne { grid-template-columns: 110px 1fr 190px; }
.funnel-grand .fn-piste { height: 38px; }
.funnel-grand .fn-nom, .funnel-grand .fn-val { font-size: 16px; }
.funnel-grand .fn-taux { font-size: 14px; }
/* Sur un téléphone, une étape tient sur deux lignes : son nom et
   ses taux, puis sa barre sur toute la largeur — entre une colonne de nom
   et une colonne de taux, la barre gardait 28 px à 320 px de large, et son
   chiffre ne se lisait plus. La présentation aussi, si on la lance là. */
@media (max-width: 640px) {
  .fn-ligne, .funnel-grand .fn-ligne { grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas: "nom taux" "piste piste"; row-gap: 4px; }
  .fn-nom { grid-area: nom; }
  .fn-piste { grid-area: piste; }
  .fn-taux { grid-area: taux; }
}

.lf-controls { display: flex; flex-direction: column; gap: 11px;
  margin-bottom: 12px; padding-bottom: 13px; border-bottom: 1px solid var(--border); }
/* Un bloc = une ligne « intitulé + contrôles ». `hidden` doit battre le
   display du bloc, sinon la plage personnalisée reste visible. */
.lf-bloc { display: flex; flex-wrap: wrap; gap: 8px 12px; align-items: center; }
.lf-bloc[hidden] { display: none; }
.lf-lab { font-size: 10.5px; font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase; color: var(--muted); }
.lf-presets { display: flex; flex-wrap: wrap; gap: 6px; }
.lf-chip { cursor: pointer; font: inherit; font-size: 11.5px; font-weight: 600;
  color: var(--muted); background: var(--panel); border: 1px solid var(--border);
  border-radius: 999px; padding: 5px 12px;
  transition: color .12s, border-color .12s, background .12s; }
.lf-chip:hover { color: var(--text); border-color: var(--muted); }
.lf-chip.active { color: var(--text); background: var(--panel2); border-color: var(--accent); }
.lf-controls label { display: flex; flex-direction: column; gap: 4px; font-size: 10.5px;
  font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--muted);
  min-width: 0; max-width: 100%; }
.lf-controls select, .lf-controls input[type="month"] { font-family: inherit;
  font-size: 13px; padding: 7px 10px; border: 1px solid var(--border);
  border-radius: 8px; background: var(--panel); color: var(--text); cursor: pointer; }
/* Un `select` se dimensionne sur son option la plus large : un libellé
   long suivi de son compteur faisait déborder la carte en largeur sur petit
   écran. Le max-width le ramène dans la carte, le texte est tronqué à
   l'affichage mais entier dans la liste ouverte. */
.lf-controls select { min-width: 150px; max-width: 100%; }
.lf-controls select:focus, .lf-controls input[type="month"]:focus {
  outline: none; border-color: var(--accent); }
/* Les avertissements de plage : discrets par défaut, colorés quand la plage
   retenue censure une étape de l'entonnoir. */

/* ─── Bloc d'alerte (type "alerte") ───────────────────────────────────────
   Le rouge (--bad), réservé à ce qui empêche une mesure d'être ce qu'elle
   paraît. La NATURE de chaque risque (mesuré / lu dans le code / non mesurable)
   est affichée en tête de bloc : un bandeau rouge qui ne dit pas d'où il tient
   ses affirmations est une alarme, pas une information. */
.card.alerte { grid-column: span 12; }
/* Étroit, le bandeau passe à la ligne : son compte et « détail »
   se rangent sous le titre, entiers, au lieu de s'écraser à côté de lui. */
.al-bandeau { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; padding: 9px 13px;
  border-radius: 10px; margin-bottom: 12px; font-size: 13.5px;
  border: 1px solid var(--al-c); background: color-mix(in srgb, var(--al-c) 12%, var(--panel)); }
.al-bandeau.niveau-critique { --al-c: var(--bad); }
.al-bandeau.niveau-attention { --al-c: var(--warn); }
.al-pastille { font-size: 15px; color: var(--al-c); }
.al-bandeau strong { flex: 1 1 12em; min-width: 0; }
.al-bandeau .al-n { margin-left: auto; font-size: 11px; color: var(--muted); white-space: nowrap; }
.al-bandeau .al-plier { flex: none; white-space: nowrap; }
.al-liste { list-style: none; margin: 0; padding: 0; display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr)); gap: 10px; }
.al-risque { border: 1px solid var(--border); border-left: 3px solid var(--g-c, var(--muted));
  border-radius: 10px; padding: 10px 12px; background: var(--panel); }
.al-risque.grav-haute { --g-c: var(--bad); }
.al-risque.grav-moyenne { --g-c: var(--warn); }
.al-tete { display: flex; align-items: baseline; gap: 8px; margin-bottom: 4px; }
.al-nature { font-size: 9.5px; font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase; color: var(--muted); border: 1px solid var(--border);
  border-radius: 999px; padding: 1px 7px; white-space: nowrap; cursor: help; }
.al-chiffre { margin-left: auto; font-size: 15px; font-weight: 700;
  color: var(--g-c, var(--text)); font-variant-numeric: tabular-nums; }
.al-risque h4 { font-size: 12.5px; font-weight: 700; color: var(--text);
  margin: 0 0 4px; line-height: 1.35; }
.al-risque p { font-size: 11.5px; color: var(--muted); line-height: 1.55; margin: 0; }
/* Replié = les paragraphes disparaissent, les titres et les chiffres restent :
   l'alerte garde sa force sans manger l'écran. */
.al-liste.replie .al-risque p { display: none; }
.al-liste.replie .al-risque { padding-bottom: 8px; }
.lf-note.replie { display: none; }
.al-plier { margin-left: 10px; font: inherit; font-size: 11px; font-weight: 700;
  cursor: pointer; color: var(--al-c); background: transparent;
  border: 1px solid var(--al-c); border-radius: 999px; padding: 2px 10px; }
.al-plier:hover { background: color-mix(in srgb, var(--al-c) 12%, transparent); }

/* ─── Synthèse d'onglet (type "insights") ─────────────────────────────────
   Trois familles, trois couleurs, trois rôles de lecture. Un constat
   MESURÉ et un point À VÉRIFIER ne doivent jamais se ressembler : le lecteur
   pressé repartirait avec une fausse certitude. D'où des colonnes distinctes,
   une pastille de nature, et un liseré de couleur par famille. */
.card.insights { grid-column: span 12; }
.ins-grille { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; }
.ins-grille.solo { grid-template-columns: repeat(auto-fit, minmax(min(280px, 100%), 1fr)); }
.ins-col > header { display: flex; align-items: center; gap: 8px; margin-bottom: 10px;
  padding-bottom: 8px; border-bottom: 2px solid var(--ins-c, var(--border)); }
.ins-col > header h3 { font-size: 13px; font-weight: 700; color: var(--text);
  margin: 0; letter-spacing: -.01em; }
.ins-pastille { font-size: 9.5px; font-weight: 700; letter-spacing: .05em;
  text-transform: uppercase; color: var(--panel); background: var(--ins-c, var(--muted));
  border-radius: 999px; padding: 2px 8px; }
.ins-n { margin-left: auto; font-size: 11px; color: var(--muted);
  font-variant-numeric: tabular-nums; }
.ins-col-constat { --ins-c: var(--bleu, var(--serie-1)); }
.ins-col-reco { --ins-c: var(--violet, var(--serie-3)); }
.ins-col-verif { --ins-c: var(--warn); }
.ins-bloc { border: 1px solid var(--border); border-left: 3px solid var(--ins-c, var(--border));
  border-radius: 10px; padding: 11px 13px; margin-bottom: 10px; background: var(--panel); }
.ins-bloc.poids-fort { background: color-mix(in srgb, var(--ins-c) 7%, var(--panel)); }
.ins-bloc.poids-faible { opacity: .82; }
.ins-bloc h4 { font-size: 12.5px; font-weight: 700; color: var(--text);
  margin: 0 0 5px; line-height: 1.35; }
.ins-bloc p { font-size: 11.5px; color: var(--muted); line-height: 1.55; margin: 0; }
.ins-chiffre { font-size: 17px; font-weight: 700; color: var(--ins-c, var(--text));
  font-variant-numeric: tabular-nums; line-height: 1.2; margin-bottom: 3px; }
/* L'en-tête d'une analyse : figée (chiffres arrêtés à une date, une source
   hors DWH) ou vivante (recalculée à chaque synchro) — sans ce mot, un
   chiffre figé lu sur un dashboard synchronisé toutes les heures passe pour
   du jour. La pastille `mesure` dit d'où vient chaque bloc. */
.ins-entete { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px 12px;
  margin-bottom: 12px; padding: 10px 12px; border: 1px solid var(--border);
  border-radius: var(--radius); }
.ins-entete b { font-size: 13px; color: var(--text); }
.ins-etat { font-size: 10px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  padding: 2px 8px; border-radius: 999px; border: 1px solid var(--accent); color: var(--accent); }
.ins-entete.fige .ins-etat { border-color: var(--warn); color: var(--warn); }
.ins-meta { font-size: 11px; color: var(--muted); }
.ins-mesure { display: inline-block; font-size: 9.5px; font-weight: 700; letter-spacing: .05em;
  text-transform: uppercase; color: var(--muted); border: 1px solid var(--border);
  border-radius: 999px; padding: 1px 6px; margin-bottom: 4px; }
@media (max-width: 900px) { .ins-grille { grid-template-columns: minmax(0, 1fr); } }

/* ─── Voyant « à recalculer » (poste de développement) ────────────────────
   Une couleur qui n'est utilisée QUE pour ça : ni le bleu des séries, ni le
   rouge des erreurs — l'écran n'est pas cassé, il est en retard. L'orange de
   l'instance porte ce sens, et rien d'autre ne l'utilise en fond plein. */
:root { --perime: var(--orange, var(--warn)); }
.voyant-cache { display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 10px 14px; border-radius: var(--rayon-carte);
  border: 1px solid var(--perime); background: color-mix(in srgb, var(--perime) 14%, var(--panel));
  font-size: 13px; color: var(--text); }
.voyant-cache[hidden] { display: none; }
.vc-point { width: 9px; height: 9px; border-radius: 50%; background: var(--perime);
  flex: none; box-shadow: 0 0 0 4px color-mix(in srgb, var(--perime) 25%, transparent); }
.vc-btn { font: inherit; font-size: 12px; font-weight: 700; cursor: pointer;
  color: var(--panel); background: var(--perime); border: 1px solid var(--perime);
  border-radius: 999px; padding: 5px 13px; }
.vc-btn:hover { opacity: .9; }
.vc-btn:disabled { opacity: .7; cursor: progress; }
.vc-lien { font-size: 12px; color: var(--muted); }

/* La carte concernée porte la même couleur, en filet : on la retrouve dans la
   grille sans que la page devienne orange. */
.card.perime { border-color: var(--perime);
  box-shadow: inset 3px 0 0 var(--perime), var(--ombre, 0 0 transparent); }
.perime-badge { font-size: 10.5px; font-weight: 700; color: var(--perime);
  border: 1px solid var(--perime); border-radius: 999px; padding: 1px 8px;
  margin-right: 8px; white-space: nowrap; }
button.ghost.urgent { color: var(--perime); border-color: var(--perime); font-weight: 700; }

/* Carte du voyant (type fraicheur_cache) */
.card.fraicheur_cache { grid-column: span 12; }
.fc-tete { display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  margin-bottom: 12px; }
.fc-chiffre { font-size: 30px; font-weight: 700; line-height: 1;
  font-variant-numeric: tabular-nums; }
.fc-chiffre.alerte { color: var(--perime); }
.fc-chiffre.ok { color: var(--good); }
.fc-lab { font-size: 11px; font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase; color: var(--muted); }
.fc-sub { font-size: 11.5px; color: var(--muted); line-height: 1.5; }

.lf-controls input[type="date"] { font-family: inherit; font-size: 13px;
  padding: 7px 10px; border: 1px solid var(--border); border-radius: 8px;
  background: var(--panel); color: var(--text); cursor: pointer; max-width: 100%; }
.lf-controls input[type="date"]:focus { outline: none; border-color: var(--accent); }

.lf-flag { font-size: 11px; color: var(--muted); }
.lf-flag strong { color: var(--text); font-variant-numeric: tabular-nums; }
.lf-flag.warn { color: var(--warn); }

.lf-note { margin-top: 16px; padding-top: 11px; border-top: 1px solid var(--border);
  color: var(--muted); font-size: 11px; line-height: 1.55; }

/* ─── Sélecteur de variante (jeux de cartes sur un même gabarit) ─────────
   Plus affirmé que les chips internes aux cartes : il commande TOUTE la page,
   il doit donc se distinguer d'un filtre local. Posé sous la barre de
   sous-onglets, avant la première section. */
.vert-nav { grid-column: span 12; display: flex; align-items: center;
  flex-wrap: wrap; gap: 8px; margin: 2px 0 6px; padding-bottom: 10px;
  border-bottom: 1px solid var(--border); }
.vert-lab { font-size: 10.5px; font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase; color: var(--muted); margin-right: 4px; }
.vert-chip { font: inherit; font-size: 12.5px; font-weight: 600; cursor: pointer;
  color: var(--muted); background: var(--panel); border: 1px solid var(--border);
  border-radius: 999px; padding: 6px 15px;
  transition: color .12s, border-color .12s, background .12s; }
.vert-chip:hover { color: var(--text); border-color: var(--muted); }
.vert-chip.active { color: var(--accent-encre); background: var(--accent);
  border-color: var(--accent); }

/* ── Carte « non mesuré » (châssis) ────────────
   Le vide doit se VOIR : une cellule « non mesuré » ne s'écrit pas de la
   même encre qu'un chiffre. C'est la règle de rendu de la doctrine
   métier, appliquée au pixel. */
/* Le type de carte vaut aussi comme classe CSS (core.js : `card ${k.type}`)
   — comme une table, cette carte prend la largeur. */
.card.non_mesure { grid-column: span 12; }
.nm-wrap { overflow-x: auto; }
.nm-table { width: 100%; border-collapse: collapse; font-size: 12.5px; }
.nm-table th { text-align: left; padding: 7px 10px; color: var(--muted);
  font-size: 10.5px; text-transform: uppercase; letter-spacing: .07em;
  border-bottom: 1px solid var(--border); white-space: nowrap; }
.nm-table td { padding: 8px 10px; border-bottom: 1px solid var(--panel2);
  vertical-align: top; }
.nm-table tr:last-child td { border-bottom: 0; }
.nm-sujet { font-weight: 600; color: var(--text); }
.nm-vide { font-weight: 700; color: var(--warn); white-space: nowrap;
  background: repeating-linear-gradient(135deg, transparent 0 6px,
    color-mix(in srgb, var(--warn) 10%, transparent) 6px 12px); border-radius: 5px; }
.nm-note { margin-top: 12px; padding: 10px 13px; border-radius: 9px;
  background: var(--panel2); border-left: 3px solid var(--accent);
  color: var(--muted); font-size: 11.5px; line-height: 1.55; }

.fm-lg.gris::before { border-top: 2px dashed var(--muted); }

.fm-taux.pire { background: color-mix(in srgb, var(--bad) 7%, var(--panel)); }

.fm-tip i.gris { background: var(--muted); }

.fm-cell-zoom .fm-cwrap.grande { flex: 1 1 auto; height: auto; min-height: 220px; }

.fm-d.down { color: var(--bad); font-weight: 600; }

.fm-dl.mauvais { border-color: color-mix(in srgb, var(--bad) 45%, var(--border)); }

.fm-sc-t tr.pire { background: color-mix(in srgb, var(--bad) 7%, var(--panel)); }
.fm-sc-t tr.bout { font-weight: 700;
  background: color-mix(in srgb, var(--accent) 6%, var(--panel)); }

/* — Responsive : sous 1100 px la colonne courbe se replie, le large scrolle
     dans son conteneur — la page, elle, ne défile jamais horizontalement. — */

/* ═══════════════════════════════════════════════════════════════════════════
   LA CONFORMITÉ — la liste des mois, et le rapport déplié

   Une ligne par mois, pliée : le niveau se lit d'un coup d'œil (pastille), le
   texte ne s'ouvre qu'à la demande — douze rapports dépliés feraient un mur
   de six écrans pour une question qui tient en un mot. Les deux teintes de
   niveau sont des JETONS sémantiques (--good, --warn) : aucune couleur ici,
   comme partout dans cette feuille.
   ═══════════════════════════════════════════════════════════════════════════ */
.conf-liste { list-style: none; margin: 0; padding: 0; }
.conf-ligne + .conf-ligne { border-top: 1px solid var(--border); }
.conf-mois { display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 10px 4px; border: 0; border-radius: calc(var(--radius) - 2px);
  background: transparent; font: inherit; font-size: 13px; color: var(--text);
  text-align: left; cursor: pointer; font-weight: 400; }
.conf-mois:hover { background: var(--panel2); filter: none; }
.conf-pastille { width: 8px; height: 8px; border-radius: 50%; flex: none;
  background: var(--muted); }
.conf-pastille.niveau-ok { background: var(--good); }
.conf-pastille.niveau-alert { background: var(--warn); }
.conf-libelle { font-weight: 600; font-variant-numeric: tabular-nums; }
.conf-etat { color: var(--muted); font-size: 12px; }
.conf-att { margin-left: auto; font-size: 11px; color: var(--muted);
  border: 1px solid var(--border); border-radius: 999px; padding: 2px 8px;
  white-space: nowrap; }
.conf-mois[aria-expanded="true"] { background: var(--panel2); }
.conf-detail { padding: 2px 4px 14px; }
.conf-rapport { font-size: 13px; line-height: 1.6; color: var(--text); }
.conf-rapport h3 { font-size: 14px; margin-bottom: 6px; }
.conf-rapport h4 { font-size: 11.5px; color: var(--accent); letter-spacing: .05em;
  text-transform: uppercase; margin: 14px 0 4px; }
.conf-rapport p { margin: 4px 0; }
.conf-rapport ul { margin: 4px 0 4px 1.1rem; }
.conf-rapport li { margin: 2px 0; }
.conf-pied { margin-top: 12px; }
.conf-vide { color: var(--muted); font-size: 12.5px; line-height: 1.6; padding: 4px 0; }

/* ── LES COMPTES — mes jetons, l'administration (dans le dialogue) ─────── */
.comptes-nav { margin-bottom: 14px; }
.comptes-form { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 10px;
  margin: 0 0 14px; padding: 12px; border: 1px solid var(--border);
  border-radius: var(--radius); background: var(--panel2); }
.comptes-form label { display: grid; gap: 4px; font-size: 11.5px; color: var(--muted);
  flex: 1 1 160px; }
.comptes-form label.case { display: flex; align-items: center; gap: 6px; flex: 0 0 auto;
  font-size: 12.5px; color: var(--text); }
.comptes-form input, .comptes-form select { font: inherit; font-size: 13px; padding: 7px 9px;
  border-radius: 7px; border: 1px solid var(--border); background: var(--bg); color: var(--text); }
.comptes-form input:focus, .comptes-form select:focus { border-color: var(--accent); outline: none; }
.comptes-form button { flex: 0 0 auto; }
.form-msg { font-size: 12px; color: var(--bad); min-height: 1.2em; align-self: center; }
/* Un succès se dit dans la couleur du succès, jamais dans celle du refus. */
.form-msg.ok { color: var(--good); }
button.mini { font-size: 11.5px; padding: 4px 9px; }
.comptes-table > tbody > tr > td { white-space: normal; vertical-align: middle; }
.comptes-table select { font: inherit; font-size: 12.5px; padding: 3px 6px; border-radius: 6px;
  border: 1px solid var(--border); background: var(--bg); color: var(--text); }
/* Les gestes d'une ligne : côte à côte quand la place le permet, l'un sous
   l'autre sinon — chacun sur une ligne, jamais coupé en deux. */
.admin-gestes { display: flex; flex-wrap: wrap; gap: 6px; }
.admin-gestes .vue-btn { white-space: nowrap; }
.compte-ferme td:first-child { color: var(--muted); text-decoration: line-through; }
/* Le fond des jetons dépliés : doux, pour que l'encre discrète (--muted) y
   garde 4,5:1 (sur --panel2, 4,40:1 en clair). */
.jetons-de > td, .admin-table > tbody > tr.jetons-de:hover > td { background: var(--vue-fond-doux); }
.jetons-de > td { padding: 10px 12px; }
/* Les jetons d'un compte, dépliés sous sa ligne (l'administration). */
.admin-jetons { display: flex; flex-direction: column; gap: 10px; white-space: normal; }
.admin-jetons .compte-jeton { padding: 10px 0; }
.admin-jetons .compte-jeton:first-child { padding-top: 0; }
.admin-jetons-eteints summary { cursor: pointer; font-size: 12.5px; color: var(--muted); }
.admin-jetons-eteints[open] summary { margin-bottom: 10px; }
.admin-jetons-pied { display: flex; justify-content: flex-start; }
.journal-refus td { color: var(--bad); }
/* Un secret montré une fois (la clé d'API neuve, le lien d'activation) :
   masqué par défaut ; révélé, un clic le sélectionne en entier — le recours
   à la main quand le presse-papiers manque. */
.secret-une-fois { margin: 0 0 14px; padding: 12px; border-radius: var(--radius);
  border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
  background: color-mix(in srgb, var(--accent) 8%, transparent); }
.secret-ligne { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin: 8px 0 4px; }
.secret-ligne code { font: 12.5px/1.5 var(--police-mono); word-break: break-all;
  padding: 4px 8px; border-radius: 6px; background: var(--bg); border: 1px solid var(--border);
  user-select: all; }
/* Le conseil se lit (12,5 px, pas 10,5) ; la commande ou l'en-tête qu'il
   cite est du code, sélectionné d'un clic. */
.secret-une-fois p.dim { margin-top: 6px; font-size: 12.5px; line-height: 1.55; color: var(--texte-2); }
.secret-code { font: 12px/1.45 var(--police-mono); padding: 1px 5px; border-radius: 4px;
  background: var(--bg); border: 1px solid var(--border); user-select: all; overflow-wrap: anywhere; }
/* Vide, l'état ne laisse aucun blanc au pied du bloc (la zone reste, pour
   être lue à voix haute quand elle se remplit). */
.secret-etat { margin: 4px 0 0; min-height: 1.2em; font-size: 12px; color: var(--text); }
.secret-etat:empty { min-height: 0; margin: 0; }

/* ═══════════════════════════════════════════════════════════════════════════
   LES VUES DU COCKPIT — « Règles et cap », « Mon espace » et les tableaux,
   et ce qu'elles posent dans la barre

   La maquette « Cockpit de démonstration », en jetons : aucune couleur ici,
   les teintes se dérivent des jetons de l'instance (color-mix), et suivent
   donc son thème, clair ou sombre. Tout est plat : l'ombre reste le
   privilège de ce qui quitte le plan. Les titres prennent la police de
   titre de l'instance si elle en déclare une (--police-titre, facultatif).

   Les cinq états ont chacun un habit, le même partout — puce, badge :
   Déjà là (le bon), À confirmer (l'accent), Exemple (l'alerte), À remplir
   (le pointillé), Renseigné (le filet du bon, sur fond neutre).
   ═══════════════════════════════════════════════════════════════════════════ */
:root {
  --vue-teinte-accent: var(--accent-fond);
  --vue-filet-accent: color-mix(in srgb, var(--accent) 42%, var(--border));
  --vue-teinte-bon: var(--good-fond);
  --vue-filet-bon: color-mix(in srgb, var(--good) 40%, var(--border));
  --vue-teinte-alerte: var(--warn-fond);
  --vue-filet-alerte: color-mix(in srgb, var(--warn) 48%, var(--border));
  --vue-encre-alerte: color-mix(in srgb, var(--warn) 70%, var(--text));
  --vue-filet-vide: color-mix(in srgb, var(--muted) 55%, var(--border));
  --vue-filet-doux: color-mix(in srgb, var(--border) 55%, var(--panel));
  --vue-fond-doux: color-mix(in srgb, var(--panel2) 45%, var(--panel));
  --vue-encre-accent: var(--accent-encre, var(--panel));
}

/* ── Dans la barre : la démonstration, le badge « Exemple » ──────────────── */
.pilule-fraicheur.pilule-demo { color: var(--vue-encre-alerte); white-space: nowrap;
  background: var(--vue-teinte-alerte); border-color: var(--vue-filet-alerte); }
.pilule-demo .point { width: 7px; height: 7px; border-radius: 50%; flex: none;
  background: var(--warn); }
/* La bulle hérite du `nowrap` de la pilule : sans ceci, son texte courrait
   sur une seule ligne, hors de la page. */
.pilule-demo .bulle { white-space: normal; }

.badge-etat { display: inline-flex; align-items: center; height: 20px; padding: 0 7px;
  border-radius: 4px; border: 1px solid var(--border); background: var(--panel);
  color: var(--muted); font-size: 10.5px; font-weight: 600; letter-spacing: .07em;
  text-transform: uppercase; white-space: nowrap; flex: none; line-height: 1;
  vertical-align: middle; }
.badge-deja { color: var(--good); background: var(--vue-teinte-bon); border-color: var(--vue-filet-bon); }
.badge-renseigne { color: var(--good); border-color: var(--vue-filet-bon); }
.badge-confirmer { color: var(--accent); background: var(--vue-teinte-accent);
  border-color: var(--vue-filet-accent); }
.badge-exemple { color: var(--vue-encre-alerte); background: var(--vue-teinte-alerte);
  border-color: var(--vue-filet-alerte); }
.badge-remplir { border-style: dashed; border-color: var(--vue-filet-vide); background: transparent; }
.ck-actions .badge-exemple { height: 18px; font-size: 9.5px; padding: 0 6px; }
.pres-kpi h3 .badge-exemple { margin-left: 8px; }

/* ── La page ─────────────────────────────────────────────────────────────── */
/* `.vue:not(.card)` : une carte d'un type « vue » porte aussi la classe nue
   de son type — elle ne prend pas la mise en page des pages du châssis. */
.vue:not(.card) { grid-column: span 12; min-width: 0; display: flex; flex-direction: column; gap: 34px;
  padding: 8px 0 32px; }
.vue:not(.card) a { color: var(--accent); }
.vue:not(.card) code { font: 12.5px/1.4 var(--police-mono); padding: 1px 5px;
  border-radius: 4px; background: var(--panel2); border: 1px solid var(--border);
  overflow-wrap: anywhere; }
/* Dans une phrase discrète, le code garde l'encre du texte : --muted sur le
   fond du code tombait à 4,40:1. */
.vue:not(.card) .vue-doux code { color: var(--texte-2); }
.vue-titre { font-family: var(--police-titre, inherit); font-size: 28px; font-weight: 650;
  line-height: 1.15; letter-spacing: -.02em; }

.vue-chapo { font-size: 16px; line-height: 1.55; max-width: 44em; color: var(--text); }
.vue-doux { font-size: 13.5px; line-height: 1.55; color: var(--muted); }
.vue-eyebrow { font-size: 11px; font-weight: 600; letter-spacing: .1em; text-transform: uppercase;
  color: var(--muted); }
.vue-surtitre { font-size: 11.5px; font-weight: 600; letter-spacing: .1em;
  text-transform: uppercase; color: var(--accent); }
.vue-sous-titre { font-size: 12.5px; font-weight: 600; color: var(--muted); }

.vue-vide { font-size: 13.5px; line-height: 1.55; color: var(--muted); }
.vue-alerte { font-size: 13px; line-height: 1.5; padding: 9px 12px; border-radius: var(--radius);
  color: var(--vue-encre-alerte); background: var(--vue-teinte-alerte);
  border: 1px solid var(--vue-filet-alerte); }
.vue-avis { flex: 0 1 420px; display: flex; gap: 10px; align-items: flex-start;
  padding: 12px 14px; border-radius: var(--radius); font-size: 13px; line-height: 1.5;
  color: var(--vue-encre-alerte); background: var(--vue-teinte-alerte);
  border: 1px solid var(--vue-filet-alerte); }
.vue-avis .badge-etat { background: var(--panel); }
.vue-entete { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between;
  gap: 20px 36px; }
.vue-entete > div:first-child { flex: 1 1 460px; min-width: 0; display: flex;
  flex-direction: column; gap: 10px; }
.vue-carte { background: var(--panel); border: 1px solid var(--border);
  border-radius: var(--rayon-carte); padding: 20px 22px; min-width: 0;
  box-shadow: var(--ombre-carte); }
.vue-carte > h2 { font-size: 16px; font-weight: 600; margin-bottom: 12px; }
.vue-carte h3 { display: flex; align-items: center; gap: 9px; font-size: 15px; font-weight: 600; }
.vue-carte h3 .ic { width: 16px; height: 16px; color: var(--accent); flex: none; }
.vue-carte > p + p { margin-top: 8px; }
.vue-carte-tete { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; }
.vue-carte-tete h2 { font-size: 16px; font-weight: 600; }

.vue-section { display: flex; flex-direction: column; gap: 14px; }
.vue-section-tete { display: flex; justify-content: space-between; align-items: baseline;
  gap: 20px; flex-wrap: wrap; }
.vue-actions { display: flex; flex-wrap: wrap; gap: 10px; padding-top: 4px; }
.vue-btn { display: inline-flex; align-items: center; gap: 8px; min-height: 42px; padding: 0 18px;
  border-radius: calc(var(--radius) - 2px); border: 1px solid var(--accent);
  background: var(--accent); color: var(--vue-encre-accent); font-family: inherit;
  font-size: 14px; font-weight: 600; line-height: 1.2; text-decoration: none; cursor: pointer; }
.vue:not(.card) a.vue-btn { color: var(--vue-encre-accent); }
.vue-btn:hover { filter: brightness(1.08); }
.vue-btn.second, .vue:not(.card) a.vue-btn.second { background: var(--panel); color: var(--text);
  border-color: var(--border-strong); font-weight: 500; }
.vue-btn.second:hover { filter: none; border-color: var(--accent); }
.vue-btn.petit { min-height: 32px; padding: 0 12px; font-size: 13px; }
.vue-btn:disabled { opacity: .6; cursor: progress; }
.vue-btn .ic, .vue-lien .ic { width: 15px; height: 15px; flex: none; }
.vue-lien { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; font-weight: 500;
  text-decoration: none; }
.vue-lien:hover { text-decoration: underline; }
.vue-tuile { flex: none; width: 30px; height: 30px; border-radius: 7px; display: inline-flex;
  align-items: center; justify-content: center; color: var(--accent);
  background: var(--vue-teinte-accent); }
.vue-tuile .ic { width: 16px; height: 16px; }
.vue-tuile.pleine { background: var(--accent); color: var(--vue-encre-accent); }

/* ── Les puces d'état ───────────────────────────────────────────────────── */
.vue-puces { display: flex; flex-wrap: wrap; gap: 6px; min-width: 0; }
.vue-puces.colonne { flex-direction: column; align-items: flex-start; }
.puce { display: inline-flex; align-items: center; gap: 5px; min-height: 28px; padding: 3px 10px;
  border-radius: calc(var(--radius) - 2px); border: 1px solid var(--border);
  background: var(--panel2); color: var(--text); font-size: 12.5px; font-weight: 500;
  line-height: 1.3; max-width: 100%; overflow-wrap: anywhere; }
.puce .ic { width: 12px; height: 12px; flex: none; }
.puce-deja { color: var(--good); background: var(--vue-teinte-bon); border-color: var(--vue-filet-bon); }
.puce-renseigne { background: var(--panel); border-color: var(--vue-filet-bon); }
.puce-renseigne .ic { color: var(--good); }
.puce-confirmer { color: var(--accent); background: var(--vue-teinte-accent);
  border-color: var(--vue-filet-accent); }
.puce-exemple { color: var(--vue-encre-alerte); background: var(--vue-teinte-alerte);
  border-color: var(--vue-filet-alerte); }
.puce-remplir { color: var(--muted); background: transparent; border-style: dashed;
  border-color: var(--vue-filet-vide); font-weight: 400; }

/* ── « Règles et cap » ──────────────────────────────────────────────────── */
.vue-ancres { display: flex; flex-wrap: wrap; gap: 2px 4px; margin-top: -14px;
  border-bottom: 1px solid var(--border); }
.vue-ancres a { display: inline-flex; align-items: center; min-height: 36px; padding: 0 12px;
  font-size: 13.5px; font-weight: 500; color: var(--muted); text-decoration: none; }
.vue-ancres a:hover { color: var(--text); }
.dv-grille { display: grid; grid-template-columns: minmax(0, 1fr) 380px; gap: 28px; align-items: start; }
.dv-principal { display: flex; flex-direction: column; gap: 18px; min-width: 0; }
.dv-cote { display: flex; flex-direction: column; gap: 16px; min-width: 0; }
.dv-section { display: flex; flex-direction: column; gap: 12px; padding: 20px 22px;
  background: var(--panel); border: 1px solid var(--border); border-radius: var(--rayon-carte);
  box-shadow: var(--ombre-carte);
  scroll-margin-top: 16px; min-width: 0; }
.dv-tete { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between;
  gap: 8px 14px; }
.dv-tete h2 { font-size: 17px; font-weight: 600; }
.dv-etats { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.nature { display: inline-flex; align-items: center; height: 20px; padding: 0 7px; border-radius: 4px;
  border: 1px solid var(--border); background: var(--panel2); color: var(--muted);
  font-size: 11px; font-weight: 500; white-space: nowrap; cursor: help; }
.nature-hypothese { color: var(--accent); background: var(--vue-teinte-accent);
  border-color: var(--vue-filet-accent); }
.dv-pied { display: flex; justify-content: flex-end; padding-top: 10px;
  border-top: 1px solid var(--vue-filet-doux); }
.dv-journal td { white-space: normal; vertical-align: top; }
.dv-journal td.n { white-space: nowrap; font-variant-numeric: tabular-nums; color: var(--muted); }
.vue-coches { list-style: none; margin: 0; padding: 0; display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 9px 24px; }
.vue-coches li { display: flex; gap: 9px; align-items: flex-start; font-size: 13.5px; line-height: 1.5; }
.vue-coches .ic { width: 14px; height: 14px; flex: none; margin-top: 3px; color: var(--good); }
.dv-familles { display: flex; flex-direction: column; gap: 8px; padding-top: 12px;
  border-top: 1px solid var(--vue-filet-doux); }
.dv-proposer { border-color: var(--accent); display: flex; flex-direction: column; gap: 8px; }
.dv-proposer > h2 { margin-bottom: 0; font-size: 17px; }
.dv-proposer p { font-size: 13.5px; line-height: 1.55; color: var(--muted); }
.dv-propositions { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.dv-propositions li { display: flex; flex-direction: column; gap: 6px; padding: 12px 0;
  border-top: 1px solid var(--vue-filet-doux); }
.dv-prop-tete { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.dv-prop-tete .vue-doux { font-size: 12px; }
.dv-prop-texte { font-size: 13.5px; line-height: 1.55; white-space: pre-wrap; overflow-wrap: anywhere; }
.dv-reponse { display: flex; flex-direction: column; gap: 3px; padding: 9px 11px;
  border-radius: var(--radius); background: var(--vue-fond-doux); font-size: 13px; }
.dv-reponse p { white-space: pre-wrap; overflow-wrap: anywhere; }
.dv-form { display: flex; flex-direction: column; gap: 12px; }
.dv-form label { display: flex; flex-direction: column; gap: 6px; font-size: 12.5px; font-weight: 600;
  color: var(--muted); }
.dv-form textarea { font: inherit; font-size: 14px; font-weight: 400; line-height: 1.5; padding: 10px 12px;
  border-radius: var(--radius); border: 1px solid var(--border-strong); background: var(--panel);
  color: var(--text); resize: vertical; min-height: 120px; }
.dv-form textarea:focus { border-color: var(--accent); outline: none; }
.dv-form-pied { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; }
.vue-envoyee { display: flex; gap: 10px; align-items: flex-start; padding: 12px 14px; margin-bottom: 12px;
  border-radius: var(--radius); background: var(--vue-teinte-bon); border: 1px solid var(--vue-filet-bon); }
.vue-envoyee .ic { width: 16px; height: 16px; flex: none; margin-top: 2px; color: var(--good); }
.vue-envoyee p { margin-top: 2px; font-size: 13px; color: var(--muted); }

/* La doctrine vivante : les entrées en base par genre, la fenêtre
   d'écriture et l'historique d'une ancre. Une entrée en conflit porte le
   filet de l'accent, comme son badge « À confirmer ». */
.dv-genre { display: flex; flex-direction: column; gap: 10px; padding-top: 14px;
  border-top: 1px solid var(--vue-filet-doux); }
.dv-genre-tete { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px; }
.dv-genre-tete h3 { font-size: 15px; font-weight: 600; }
.dv-genre-tete .vue-btn { margin-left: auto; }
.dv-entrees, .dv-versions, .dv-existant { list-style: none; margin: 0; padding: 0; display: flex;
  flex-direction: column; }
.dv-entree, .dv-version, .dv-existant li { display: flex; flex-direction: column; gap: 8px; min-width: 0;
  padding: 12px 0; border-top: 1px solid var(--vue-filet-doux); }
.dv-entree:first-child, .dv-version:first-child { border-top: 0; padding-top: 4px; }
.dv-entree.en-conflit { padding-left: 12px; border-left: 3px solid var(--vue-filet-accent); }
.dv-entree-tete { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 8px; min-width: 0; }
.dv-entree-titre { font-size: 14.5px; font-weight: 600; overflow-wrap: anywhere; }
.dv-entree-pied { display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 12px;
  font-size: 12px; line-height: 1.5; color: var(--muted); overflow-wrap: anywhere; }
.dv-entree-pied code { font-size: 11.5px; }
.dv-lien { font: inherit; font-size: 12px; font-weight: 500; color: var(--accent); background: none;
  border: 0; padding: 0; cursor: pointer; text-decoration: underline; text-underline-offset: 2px; }
.dv-mesure { font-size: 13px; }
.dv-mesure summary { cursor: pointer; color: var(--muted); overflow-wrap: anywhere; }
.dv-mesure .md { margin-top: 8px; }
.dv-version-remplacee .dv-entree-titre, .dv-version-retiree .dv-entree-titre,
.dv-version-retrait .dv-entree-titre { color: var(--muted); }
.dv-historique { display: flex; flex-direction: column; gap: 10px; }
/* Un champ caché le reste : `label` et `fieldset` sont en flex dans la fenêtre. */
.dv-form [hidden] { display: none; }
/* Le champ ne prend PAS le gras de son libellé (`font: inherit` héritait le
   600 du <label>) : une valeur saisie, ou un exemple en placeholder, se
   lisait comme un titre — et l'exemple comme une valeur déjà saisie. */
.dv-form input, .dv-form select { font: inherit; font-size: 14px; font-weight: 400; min-height: 38px; padding: 7px 10px;
  border-radius: var(--radius); border: 1px solid var(--border-strong); background: var(--panel);
  color: var(--text); min-width: 0; }
.dv-form input:focus, .dv-form select:focus { border-color: var(--accent); outline: none; }
.dv-form input[type="checkbox"], .dv-form input[type="radio"] { min-height: 0; padding: 0; margin: 3px 0 0;
  flex: none; accent-color: var(--accent); }
.dv-form fieldset { display: flex; flex-direction: column; gap: 10px; min-width: 0; margin: 0;
  padding: 10px 12px 12px; border: 1px solid var(--border); border-radius: var(--radius); }
.dv-form legend { padding: 0 4px; font-size: 12.5px; font-weight: 600; color: var(--muted); }
.dv-deux { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
.dv-aide { font-size: 12px; font-weight: 400; line-height: 1.45; color: var(--muted); }
.dv-form label.dv-choix { flex-direction: row; align-items: flex-start; gap: 8px; font-size: 13px;
  font-weight: 400; color: var(--text); }
.dv-confronte { display: flex; flex-direction: column; gap: 10px; }

/* Le Markdown de l'instance : lisible comme un document, sans rien de brut. */
.md { font-size: 14px; line-height: 1.6; color: var(--text); min-width: 0; overflow-wrap: anywhere; }
.md > * + * { margin-top: 10px; }
.md h3 { font-size: 16px; font-weight: 600; }
.md h4 { font-size: 14.5px; font-weight: 600; }
.md h5 { font-size: 13.5px; font-weight: 600; }
.md h6 { font-size: 11.5px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase;
  color: var(--muted); }
.md ul, .md ol { padding-left: 1.3em; }
.md li + li, .md li > ul, .md li > ol { margin-top: 3px; }
.md blockquote { padding: 8px 14px; color: var(--muted); background: var(--vue-fond-doux);
  border-left: 3px solid var(--border-strong); border-radius: 0 var(--radius) var(--radius) 0; }
.md blockquote > * + * { margin-top: 6px; }
.md pre { padding: 12px 14px; overflow: auto; border-radius: var(--radius);
  background: var(--panel2); border: 1px solid var(--border); }
.md pre code { padding: 0; border: 0; background: transparent; white-space: pre; }
.md hr { border: 0; border-top: 1px solid var(--border); }
.md-table { overflow-x: auto; }
.md-table table { font-size: 13px; }
.md-table th { position: static; }
.md-table td { white-space: normal; vertical-align: top; }
.md-adapter { display: inline-flex; align-items: center; height: 20px; padding: 0 7px;
  border-radius: 4px; border: 1px dashed var(--vue-filet-vide); color: var(--muted);
  font-size: 10.5px; font-weight: 600; letter-spacing: .07em; text-transform: uppercase;
  vertical-align: 1px; }

/* ── Plus étroit : une colonne ──────────────────────────────────────────── */
@media (max-width: 1100px) {
  .dv-grille { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 760px) {
  .vue:not(.card) { gap: 26px; }
  .vue-titre { font-size: 24px; }
  .vue-coches { grid-template-columns: minmax(0, 1fr); }
  .vue-carte, .dv-section { padding: 16px; }
  .dv-deux { grid-template-columns: minmax(0, 1fr); }
}

/* ═══════════════════════════════════════════════════════════════════════════
   LES PROFILS DE DROITS — l'onglet « Profils » de l'administration

   Une carte par profil, dans la grammaire des pages du châssis (vue-carte,
   puce, dv-form, vue-btn) : ce qu'il lit, ses cartes, ses comptes, sa
   connexion, puis ses gestes. Le formulaire et le plan s'ouvrent DANS la
   page, au-dessus des cartes — jamais un dialogue. Ce qui s'ajoute se lit
   sur le fond du bon, ce qui se retire sur celui du mauvais ; supprimer
   porte l'encre du mauvais. En jetons : aucune couleur ici. Sur un écran
   étroit, une colonne ; un long nom ou un ordre SQL ne poussent jamais la
   page de côté (le SQL défile dans son cadre).
   ═══════════════════════════════════════════════════════════════════════════ */
.profils { display: flex; flex-direction: column; gap: 22px; min-width: 0; }
.profils[aria-busy="true"] { opacity: .6; }
.profils svg { width: 16px; height: 16px; flex: none; }
.profils code { font: 12px/1.45 var(--police-mono); padding: 1px 6px; border-radius: 5px;
  background: var(--panel2); border: 1px solid var(--border); overflow-wrap: anywhere; }
/* Vide, l'annonce (role=status) reste rendue, hors de la vue (voir `.tdb-msg:empty`). */
.profils-annonce:empty { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0; }
.profils-annonce .vue-envoyee { margin-bottom: 0; }
.profils-annonce .vue-envoyee > div { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.profils-annonce .vue-envoyee svg { margin-top: 2px; color: var(--good); }
/* L'échec d'un geste après la confirmation : l'habit de l'alerte, le constat du serveur. */
.profils-echec { display: flex; gap: 10px; align-items: flex-start; }
.profils-echec svg { margin-top: 2px; }
.profils-echec > div { display: flex; flex-direction: column; gap: 4px; min-width: 0; overflow-wrap: anywhere; }
/* La page qui ne se lit pas : le refus, et de quoi la relire (le segment actif ne relit rien). */
.profils-illisible { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px; }
.profils-illisible p { flex: 1 1 18rem; min-width: 0; margin: 0; overflow-wrap: anywhere; }
.profils-capacites { display: flex; flex-direction: column; gap: 8px; }
.profils-avis { display: flex; gap: 10px; align-items: flex-start; padding: 11px 14px; font-size: 13px;
  line-height: 1.55; border-radius: var(--radius); border: 1px solid var(--border); color: var(--text); }
.profils-avis svg { margin-top: 2px; }
.profils-avis-info { background: var(--vue-teinte-accent); border-color: var(--vue-filet-accent); }
.profils-avis-info svg { color: var(--accent); }
.profils-avis-alerte { background: var(--vue-teinte-alerte); border-color: var(--vue-filet-alerte); }
.profils-avis-alerte svg { color: var(--vue-encre-alerte); }
.profils-annonce .vue-avis { max-width: none; }
.profils-orphelins { display: flex; gap: 10px; align-items: flex-start; }
.profils-orphelins svg { margin-top: 2px; }
.profils-orphelins .vue-btn { margin-left: 6px; vertical-align: middle; }
/* Le gabarit commun des onglets de l'administration : un titre de section
   à 16 px, son geste en petit bouton à sa droite (comme « Comptes » et
   « MCP ») — plus un titre de 27 px et un bouton de 42 px pour celui-ci. */
.profils-tete { align-items: flex-start; }
.profils-section-titre { font-size: 16px; font-weight: 600; line-height: 1.3; }
.profils-tete > div { flex: 1 1 320px; min-width: 0; display: flex; flex-direction: column; gap: 6px; }
.profils-tete .vue-doux { max-width: 46em; }
.profils-tete > .vue-btn { flex: none; }
.profils .vue-btn:disabled { cursor: not-allowed; opacity: .5; }
.profils .vue-btn[aria-busy="true"] { cursor: progress; }
.profils-grille { display: grid; gap: 16px; align-items: stretch;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 360px), 1fr)); }
.profil-carte { display: flex; flex-direction: column; gap: 14px; }
.profil-tete { display: flex; align-items: flex-start; gap: 12px; min-width: 0; }
.profil-tete .vue-tuile { margin-top: 1px; }
.profil-nom { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.profil-nom h3 { font-size: 15.5px; font-weight: 600; line-height: 1.3; overflow-wrap: anywhere; }
.profil-cle { font: 12px/1.4 var(--police-mono); color: var(--muted); overflow-wrap: anywhere; }
.profil-tete > .puce { flex: none; min-height: 24px; font-size: 12px; cursor: help; }
.profils-faits { display: grid; grid-template-columns: 84px minmax(0, 1fr); gap: 8px 12px; margin: 0;
  font-size: 13.5px; line-height: 1.5; }
.profils-faits > div { display: contents; }
.profils-faits dt { color: var(--muted); font-size: 12.5px; font-weight: 600; padding-top: 1px; }
.profils-faits dd { margin: 0; min-width: 0; overflow-wrap: anywhere; }
.profils-sources-lues { display: flex; flex-wrap: wrap; gap: 5px 10px; }
/* Un schéma entier : son nom, puis « (tout le schéma) » en mots. */
.profils-source { display: inline-flex; flex-wrap: wrap; align-items: baseline; gap: 2px 5px; min-width: 0; }
/* Dans une phrase (« Il lit aussi …, … »), le texte courant : une boîte
   atomique laisserait la ligne se couper juste avant la virgule. */
.vue-alerte .profils-source { display: inline; }
.profils-portee { font-size: 12px; color: var(--muted); }
.profils-absente { color: var(--vue-encre-alerte); font-weight: 500; }
.profils details > summary { cursor: pointer; color: var(--texte-2); }
.profils-masquees ul, .profils-liste { margin: 6px 0 0; padding-left: 18px; }
.profils-masquees li, .profils-liste li { margin-top: 2px; font-size: 13px; color: var(--texte-2); }
.profil-constat { display: flex; flex-direction: column; gap: 6px; overflow-wrap: anywhere; }
.profil-constat details p { margin-top: 6px; color: var(--text); }
.profil-constat details p.dv-aide { color: var(--muted); }
/* La note d'une preuve « ok » : à reprendre, sans alarme. */
.profil-note { font-size: 13px; line-height: 1.5; padding: 9px 12px; border-radius: var(--radius);
  color: var(--texte-2); background: var(--panel); border: 1px solid var(--vue-filet-doux); }
.profil-note b { font-weight: 600; color: var(--text); }
/* La consigne de l'agent : citée telle quelle, elle s'adresse à lui. */
.profils-consigne { margin: 6px 0 0; padding: 8px 12px; border-left: 3px solid var(--vue-filet-accent);
  border-radius: 0 var(--radius) var(--radius) 0; background: var(--panel); color: var(--text);
  font: 12px/1.55 var(--police-mono); overflow-wrap: anywhere; }
.profil-gestes { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: auto;
  padding-top: 12px; border-top: 1px solid var(--vue-filet-doux); }
.profil-raison { flex-basis: 100%; font-size: 12px; line-height: 1.45; color: var(--muted); }
.profil-raison b { font-weight: 600; color: var(--texte-2); }
/* La zone des messages d'un geste de la carte (supprimer : l'attente, un
   refus) : présente dès le rendu — une zone live qui naît remplie ne
   s'annonce pas —, sans place tant qu'elle se tait. */
.profil-msg { flex-basis: 100%; margin: 0; align-self: auto; line-height: 1.45; }
.profil-msg:empty { min-height: 0; margin-top: -8px; }
.vue-btn.profils-danger { color: var(--bad); border-color: color-mix(in srgb, var(--bad) 40%, var(--border)); }
.vue-btn.profils-danger:not(:disabled):hover { border-color: var(--bad); }
.vue-btn.profils-danger.plein { background: var(--bad); border-color: var(--bad); color: var(--accent-encre); }
.vue-btn svg { width: 15px; height: 15px; }

/* Le formulaire et le plan : un panneau dans la page, l'accent pour filet. */
.profils-zone:empty { display: none; }
.profils-panneau { display: flex; flex-direction: column; gap: 16px; border-color: var(--vue-filet-accent); }
.profils-panneau .vue-carte-tete { align-items: flex-start; }
.profils-panneau .vue-carte-tete h2 { font-size: 18px; font-weight: 600; overflow-wrap: anywhere; }
/* Le titre reçoit le focus pour que les lecteurs d'écran lisent le plan
   d'abord : ce n'est pas un contrôle, il ne porte pas d'anneau. */
.profils-panneau h2[tabindex="-1"]:focus, .profils-panneau h2[tabindex="-1"]:focus-visible { outline: none; }
.profils-plan .profils-faits { grid-template-columns: 112px minmax(0, 1fr); }
.profils-sources { gap: 10px; }
.profils-filtre input { width: 100%; }
.profils-schema { border: 1px solid var(--border); border-radius: var(--radius); background: var(--vue-fond-doux); }
.profils-schema > summary { display: flex; align-items: center; gap: 8px; padding: 9px 12px; color: var(--text);
  font-size: 13.5px; font-weight: 600; list-style: none; border-radius: var(--radius); }
.profils-schema > summary::-webkit-details-marker { display: none; }
.profils-schema > summary:hover { background: var(--panel2); }
.profils-chevron { display: inline-flex; color: var(--muted); transition: transform .15s; }
.profils-schema[open] > summary .profils-chevron { transform: rotate(90deg); }
.profils-nombre { font-size: 12px; font-weight: 400; }
.profils-schema-nom { font-family: var(--police-mono); font-size: 13px; overflow-wrap: anywhere; min-width: 0; }
.profils-compte { margin-left: auto; font-size: 12px; font-weight: 500; color: var(--accent); white-space: nowrap; }
.profils-schema-corps { display: flex; flex-direction: column; gap: 10px; padding: 2px 12px 12px; }
.profils-tables { display: grid; gap: 6px 16px; grid-template-columns: repeat(auto-fill, minmax(min(100%, 190px), 1fr)); }
.profils-tables .dv-choix span { font: 12.5px/1.4 var(--police-mono); overflow-wrap: anywhere; min-width: 0; }
.profils-tables .dv-choix[hidden] { display: none; }
.profils-tout b { font-weight: 600; }
.profils-resume { font-size: 14.5px; line-height: 1.6; max-width: 52em; }
.profils-ecarts { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 4px; }
.profils-ecarts li { display: flex; gap: 8px; align-items: baseline; padding: 6px 10px; font-size: 13.5px;
  border-radius: var(--radius); overflow-wrap: anywhere; }
.profils-ecarts li b { flex: none; width: 1em; text-align: center; font-weight: 700; }
.profils-ecarts .ajoute { background: var(--good-fond); }
.profils-ecarts .ajoute b { color: var(--good); }
.profils-ecarts .retire { background: var(--bad-fond); }
.profils-ecarts .retire b { color: var(--bad); }
.profils-sql-texte { margin: 8px 0 0; padding: 12px 14px; max-width: 100%; overflow-x: auto;
  border-radius: var(--radius); background: var(--panel2); border: 1px solid var(--border);
  font: 12px/1.6 var(--police-mono); white-space: pre; color: var(--text); }
.profils-reporter { padding: 11px 14px; border-radius: var(--radius); font-size: 13px; line-height: 1.55;
  background: var(--vue-teinte-accent); border: 1px solid var(--vue-filet-accent); overflow-wrap: anywhere; }
.profils-reporter ul { margin: 6px 0 0; padding-left: 18px; }
.profils-avertissements .profils-liste { margin: 0; }
.profils-avertissements .profils-liste li { color: inherit; }
.profils-confirmer { padding-top: 14px; border-top: 1px solid var(--vue-filet-doux); }
.profils-confirmer label { max-width: 420px; }
.profils-confirmer label > span code { font-size: 12.5px; color: var(--text); }
.profils .form-msg { font-size: 12.5px; }
/* Une attente n'est pas un refus : la couleur neutre, pas l'encre du mauvais. */
.profils .form-msg.attente { color: var(--muted); }
.profil-renommer { display: flex; flex-direction: column; gap: 8px; padding: 12px; border-radius: var(--radius);
  background: var(--vue-fond-doux); border: 1px solid var(--vue-filet-accent); }
.profil-renommer label { display: flex; flex-direction: column; gap: 6px; font-size: 12.5px; font-weight: 600;
  color: var(--muted); }
.profil-renommer input { min-width: 0; min-height: 36px; padding: 6px 10px; font: inherit; font-size: 14px;
  font-weight: 400; border-radius: var(--radius); border: 1px solid var(--border-strong);
  background: var(--panel); color: var(--text); }
.profil-renommer-pied { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.profils-cache { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
@media (prefers-reduced-motion: no-preference) {
  .profils-panneau { animation: profils-entree .18s ease-out; }
}
@keyframes profils-entree { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
@media (max-width: 760px) {
  .profils { gap: 18px; }
  .profils-faits, .profils-plan .profils-faits { grid-template-columns: minmax(0, 1fr); gap: 1px; }
  .profils-faits dd { margin-bottom: 8px; }
  .profils-tete > .vue-btn { width: 100%; justify-content: center; }
  .profil-gestes .vue-btn { flex: 1 1 auto; justify-content: center; }
  .profils-panneau .dv-form-pied .vue-btn { flex: 1 1 auto; justify-content: center; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   L'ADMINISTRATION DU MCP — l'onglet « MCP » de la page Administration

   Des cartes du châssis (.vue-carte), des puces, des segments : rien de neuf
   à apprendre, et en jetons, comme le reste. Deux colonnes quand la place le
   permet ; sur un téléphone, une seule, et les tables se lisent en fiches —
   jamais de défilement horizontal. Le succès d'un geste dans la couleur du
   succès, son refus dans celle du refus.
   ═══════════════════════════════════════════════════════════════════════════ */
/* La racine garde sa zone d'annonce (`.mcp-cache`, lue à voix haute) ; seul
   `.mcp-contenu` se redessine après un geste. */
.mcp-admin { position: relative; min-width: 0; }
.mcp-contenu { display: flex; flex-direction: column; gap: 18px; min-width: 0; }
.mcp-deux { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 340px), 1fr));
  gap: 18px; align-items: start; min-width: 0; }
.mcp-carte { display: flex; flex-direction: column; gap: 12px; }
.mcp-carte > .vue-carte-tete { align-items: center; flex-wrap: wrap; gap: 4px 12px; }
.mcp-carte h2 { scroll-margin-top: 16px; }
.mcp-carte h2:focus { outline: none; }
.mcp-carte h2:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 4px; }
.mcp-carte p { margin: 0; }
.mcp-carte h3 { display: block; font-size: 13px; font-weight: 600; margin: 0 0 6px; }
.mcp-carte h3 .vue-doux { font-size: 12px; font-weight: 400; }
/* Un avis DANS une carte : un bloc de texte, pas la rangée d'un en-tête. */
.mcp-carte .vue-avis { display: block; flex: none; }
.mcp-carte .vue-doux { font-size: 12.5px; }
.mcp-msg { align-self: stretch; margin: 0; font-size: 12.5px; line-height: 1.5; }
.mcp-msg:empty { display: none; }
.mcp-msg.ok { color: var(--good); }
/* L'attente (« Enregistrement… ») : neutre — `.form-msg` est rouge, la couleur d'un refus. */
.mcp-msg.encours { color: var(--muted); }
.mcp-ident { font-size: 12.5px; }
.mcp-texte { font-size: 13.5px; line-height: 1.55; }
.mcp-maj { font-size: 12px; }
.mcp-cache { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%);
  white-space: nowrap; }
/* Un état vide se dit comme dans Mon compte (`.compte-vide`) : une phrase
   discrète, pas une boîte pointillée centrée — le même composant partout. */
.mcp-vide { font-size: 13.5px; line-height: 1.55; color: var(--muted); }
.mcp-secret { display: flex; flex-direction: column; gap: 8px; padding-top: 12px;
  border-top: 1px solid var(--vue-filet-doux); }
.mcp-secret .vue-actions { padding-top: 0; }
/* Une commande à recopier (le recours sans moteur) : un bloc à part, sélectionné d'un clic ; trop
   longue pour un téléphone, elle passe à la ligne DANS son bloc — jamais la page ne défile. */
.mcp-commande { display: block; width: fit-content; max-width: 100%; margin: 6px 0; padding: 6px 9px;
  border-radius: var(--radius);
  font: 12.5px/1.5 var(--police-mono); color: var(--text); background: var(--panel);
  border: 1px solid var(--border); overflow-wrap: anywhere; user-select: all; }
/* L'adresse à coller dans Claude : lisible, sélectionnable d'un clic. */
.mcp-admin .mcp-url code { flex: 1 1 220px; min-width: 0; font: 13px/1.5 var(--police-mono);
  padding: 7px 10px; border-radius: var(--radius); background: var(--panel2);
  border: 1px solid var(--border); overflow-wrap: anywhere; user-select: all; }
.mcp-url { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 10px; }
.mcp-etapes { margin: 0; padding-left: 1.3rem; display: flex; flex-direction: column; gap: 5px;
  font-size: 13.5px; line-height: 1.5; }
/* La bascule : le segment commun, resserré, calé à sa largeur. */
.mcp-admin .mcp-seg { flex: none; flex-wrap: nowrap; align-self: flex-start; margin: 0; }
.mcp-seg .subtab { padding: 6px 12px; font-size: 12.5px; }
.mcp-seg .subtab:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* Un geste en cours : la page se dit occupée, ses boutons pâlissent — sauf le bouton pressé, qui
   garde le focus (`aria-disabled`, pas `disabled`) : l'opacité pâlirait aussi son anneau, sous le
   contraste qu'un focus exige (3:1). */
.mcp-contenu[aria-busy="true"] button { cursor: progress; }
.mcp-contenu[aria-busy="true"] button:not([aria-disabled="true"]) { opacity: .55; }
/* Les outils : le nom, ce qu'il fait, sa bascule — une ligne chacun. */
.mcp-outils, .mcp-clients, .mcp-barres { list-style: none; margin: 0; padding: 0;
  display: flex; flex-direction: column; }
/* Ceux qui lisent et ceux qui écrivent, côte à côte quand la place le permet. */
.mcp-groupes { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 440px), 1fr));
  gap: 8px 36px; align-items: start; }
.mcp-outil, .mcp-clients li { display: flex; flex-wrap: wrap; align-items: center;
  justify-content: space-between; gap: 8px 16px; padding: 10px 0;
  border-top: 1px solid var(--vue-filet-doux); }
.mcp-outil:first-child, .mcp-clients li:first-child { border-top: 0; }
.mcp-outil-texte, .mcp-client-texte { flex: 1 1 170px; min-width: 0; display: flex;
  flex-direction: column; gap: 3px; overflow-wrap: anywhere; }
.mcp-outil-nom { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 8px; }
.mcp-outil-texte p { font-size: 12.5px; line-height: 1.45; color: var(--muted); }
.mcp-outil.ferme .mcp-outil-texte { opacity: .62; }
.mcp-client-texte .vue-doux { font-size: 12px; line-height: 1.5; }
/* Les tables : en lignes sur un écran large, en fiches sur un téléphone. */
.mcp-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.mcp-table th { text-align: left; font-size: 11.5px; font-weight: 600; color: var(--muted);
  padding: 0 12px 8px 0; border-bottom: 1px solid var(--border); white-space: nowrap; }
/* `td` ne passe pas à la ligne dans le cockpit (les tables de données) : ici, si — un email
   long ne pousse pas la page. Seul un texte LIBRE (un compte, un client, un profil) se coupe
   n'importe où ; une date jamais au milieu d'un nombre (« 09/01/202 | 7 08:51 » se lirait
   fausse) : elle passe à la ligne entre le jour et l'heure, et laisse la place aux textes. Les
   chiffres tabulaires, pour les dates seulement : sur un texte libre, ils élargissent le trait
   d'union d'Inter de 41 % — « marie-christine » se lirait « marie - christine ». */
.mcp-table td { padding: 10px 12px 10px 0; vertical-align: middle; white-space: normal;
  border-bottom: 1px solid var(--vue-filet-doux); }
.mcp-table td.mcp-libre { overflow-wrap: anywhere; font-variant-numeric: normal; }
.mcp-table td.mcp-date { overflow-wrap: normal; word-break: normal; font-variant-numeric: tabular-nums; }
/* Large, la place suffit : le jour et l'heure sur une ligne. */
@media (min-width: 1100px) { .mcp-table td.mcp-date { white-space: nowrap; } }
.mcp-table tbody tr:last-child td { border-bottom: 0; }
.mcp-table td.mcp-geste { text-align: right; padding-right: 0; white-space: nowrap; }
.mcp-qui { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.mcp-qui b { font-weight: 600; }
.mcp-qui .vue-doux, .mcp-qui code { font-size: 11.5px; align-self: flex-start; }
/* L'usage : des barres pleines de l'accent, la valeur écrite à côté. */
.mcp-deux-col { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 200px), 1fr));
  gap: 16px 24px; }
.mcp-barres { gap: 9px; }
.mcp-barres li { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 4px 10px;
  font-size: 13px; align-items: baseline; }
/* Le nom entier, quitte à passer à la ligne : deux emails proches se distinguent. */
.mcp-barre-nom { min-width: 0; overflow-wrap: anywhere; }
.mcp-barre-n { color: var(--muted); font-variant-numeric: tabular-nums; }
.mcp-barre { grid-column: 1 / -1; height: 6px; border-radius: 999px; background: var(--panel2);
  overflow: hidden; }
.mcp-barre > span { display: block; height: 100%; width: var(--part, 0%); border-radius: inherit;
  background: var(--accent); transition: width .2s ease; }
button.mcp-vers-profils { background: none; border: 0; padding: 0; cursor: pointer;
  color: var(--accent); font-family: inherit; }
button.mcp-vers-profils:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px;
  border-radius: 4px; }
@media (max-width: 640px) {
  .mcp-contenu { gap: 14px; }
  .mcp-table, .mcp-table tbody, .mcp-table tr, .mcp-table td { display: block; width: 100%; }
  .mcp-table thead { display: none; }
  .mcp-table tr { padding: 10px 0; border-top: 1px solid var(--vue-filet-doux); }
  .mcp-table tbody tr:first-child { border-top: 0; padding-top: 0; }
  /* Une fiche : le libellé | LA valeur (un seul enfant par cellule), calée à gauche — une puce
     épouse son texte. Le geste, plus spécifique, sort de la grille sur chaque ligne. */
  .mcp-table td { display: grid; grid-template-columns: 8.5em minmax(0, 1fr); justify-items: start;
    gap: 10px; padding: 3px 0; border: 0; }
  .mcp-table td::before { content: attr(data-label); font-size: 11.5px; color: var(--muted); }
  .mcp-table tbody td.mcp-geste { display: block; text-align: left; padding-top: 8px; }
  .mcp-table tbody td.mcp-geste::before { content: none; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   LES KPI EN SQL — proposés, non officiels

   Une marque, la même partout où l'un d'eux s'affiche (⌘K, la carte
   dépliée) : l'habit de « À confirmer », l'accent de l'instance — un
   chiffre proposé, pas un module du dépôt. En jetons, comme le reste.
   ═══════════════════════════════════════════════════════════════════════════ */
.badge-sql { color: var(--accent); background: var(--vue-teinte-accent);
  border-color: var(--vue-filet-accent); }
.rech-res .badge-sql { height: 18px; font-size: 9.5px; padding: 0 6px; align-self: center; }
/* Celui qui SUIT la période (sa requête porte :debut et :fin) en porte une
   seconde, neutre : une propriété du chiffre, pas un état. Sur une carte
   étroite, elle se replie entière, comme la première. */
.badge-periode { color: var(--texte-2, var(--muted)); background: var(--panel);
  border-color: var(--border); }
.ck-marque .badge-periode { height: auto; min-height: 18px; padding: 2px 6px; font-size: 9.5px;
  line-height: 1.35; white-space: normal; overflow-wrap: anywhere; }
/* Deux marques sur une carte étroite : elles passent à la ligne sans se
   toucher, et aucune ne déborde de la carte. */
.ck-marque { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; }
.ck-marque .badge-etat { max-width: 100%; }
/* L'explication de « suit la période » : hors de la vue, lue par un lecteur
   d'écran (le `title` ne se lit qu'au survol de la souris). Sans les
   capitales de la marque : Chrome expose le texte transformé, et un
   lecteur d'écran lirait « CE CHIFFRE SE RECALCULE… » en capitales. */
.badge-explique { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0;
  text-transform: none; letter-spacing: normal; }
/* La requête : sa définition entière, lue d'emblée — les longues lignes se replient. */
.prov-sql pre { white-space: pre-wrap; overflow-wrap: anywhere; }
/* Coupée à 500 lignes : la fenêtre le dit, dans l'encre de l'alerte. */
.kpi-coupe { color: var(--vue-encre-alerte); font-weight: 600; }
.btn-retirer { color: var(--bad); }
.btn-retirer:hover { border-color: var(--bad); }
.btn-retirer:disabled { opacity: .6; cursor: default; }
.kpi-retrait-msg { font-size: 12px; color: var(--muted); }
.kpi-retrait-msg.refus { color: var(--bad); }

/* ═══════════════════════════════════════════════════════════════════════════
   LES TABLEAUX ET « MON ESPACE »

   Un en-tête qui prend la largeur du plateau — le titre, la portée, le
   propriétaire, les cartes comptées, les gestes permis —, puis les cartes
   du mécanisme des onglets, telles quelles. Chaque portée a son habit, en
   jetons : Officiel le filet du bon (il engage : calibré, sous mandat —
   vérifié à chaque lecture), Officiel · suspendu celui de l'alerte (il
   n'engage plus), Partagé l'accent, Personnel le badge neutre. Un KPI en
   SQL garde sa marque sur la carte elle-même : une capture d'écran ne la
   perd pas.
   ═══════════════════════════════════════════════════════════════════════════ */
.tdb-entete { grid-column: span 12; min-width: 0; display: flex; flex-wrap: wrap; align-items: flex-end;
  justify-content: space-between; gap: 12px 24px; padding: 4px 0 8px; }
.tdb-entete-texte { flex: 1 1 420px; min-width: 0; display: flex; flex-direction: column; gap: 8px; }
.tdb-titre { font-family: var(--police-titre, inherit); font-size: 28px; font-weight: 650; line-height: 1.15;
  letter-spacing: -.02em; overflow-wrap: anywhere; }
.tdb-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 12px; font-size: 12.5px;
  color: var(--muted); overflow-wrap: anywhere; }
.tdb-note { font-size: 13px; line-height: 1.55; color: var(--muted); max-width: 62em; }
.tdb-actions { display: flex; flex-wrap: wrap; gap: 8px; }
.vue-btn.second.tdb-supprimer { color: var(--bad); }
.vue-btn.second.tdb-supprimer:hover { border-color: var(--bad); }
.tdb-msg { flex-basis: 100%; font-size: 12.5px; line-height: 1.5; color: var(--muted); }
.tdb-msg.refus { color: var(--bad); }
/* Vide, la zone qui parle (role=status) reste RENDUE, hors de la vue : masquée en
   `display: none`, elle entrait dans l'arbre d'accessibilité AVEC son texte, et une
   zone vivante qui y entre avec son texte ne s'annonce pas — le refus d'un
   rangement se taisait (mesuré dans Chromium). Le
   patron de `.lecteur-seul` : hors du flux, pas de trou dans l'en-tête. */
.tdb-msg:empty { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0; }
.tdb-plein { grid-column: span 12; min-width: 0; }
.tdb-message { display: flex; flex-direction: column; gap: 10px; padding: 8px 0 16px; }
.tdb-message .vue-lien { color: var(--accent); }
.tdb-vide { display: flex; flex-direction: column; gap: 10px; }
.tdb-vide > h2 { margin-bottom: 0; }
.tdb-comment { margin: 0; padding-left: 1.2em; display: flex; flex-direction: column; gap: 4px;
  font-size: 13.5px; line-height: 1.55; }
.badge-portee-officiel { color: var(--good); background: var(--vue-teinte-bon); border-color: var(--vue-filet-bon); }
.badge-portee-suspendu { color: var(--warn); background: var(--vue-teinte-alerte);
  border-color: var(--vue-filet-alerte); }
.badge-portee-partage { color: var(--accent); background: var(--vue-teinte-accent);
  border-color: var(--vue-filet-accent); }
/* L'entrée « Tableaux » : un titre venu de la base peut être long — il passe
   à la ligne, le menu ne sort pas de l'écran. */
.nav-portes .porte-tableaux .porte-menu { max-width: min(360px, calc(100vw - 32px)); }
.nav-portes .porte-tableaux .porte-menu a { display: flex; align-items: center; justify-content: space-between;
  gap: 10px; overflow-wrap: anywhere; }
.porte-menu .badge-portee { height: 18px; font-size: 9.5px; padding: 0 6px; }
.ck-marque { margin: -4px 0 10px; min-width: 0; }
/* Une carte étroite (deux chiffres côte à côte sur un téléphone) : la marque se replie, entière. */
.ck-marque .badge-sql { height: auto; min-height: 18px; padding: 2px 6px; font-size: 9.5px;
  line-height: 1.35; white-space: normal; overflow-wrap: anywhere; }
.ck-item.refus { color: var(--bad); }
.kpi-epingle-msg { font-size: 12px; color: var(--muted); }
.kpi-epingle-msg.refus { color: var(--bad); }
.tdb-calibrage { font-size: 13px; font-weight: 600; color: var(--text); }
.tdb-non-calibrees { margin: 0; padding-left: 1.2em; font-size: 13px; line-height: 1.5; color: var(--muted); }
@media (max-width: 760px) {
  .tdb-titre { font-size: 24px; }
}

/* ── Ranger les cartes (Mon espace, un tableau modifiable) ──
   La poignée vit dans la marge gauche de la carte, à hauteur du libellé :
   elle n'apparaît qu'au survol de la carte et au focus (le libellé ne se
   décale jamais) ; sur un écran tactile ou étroit, elle prend sa place en
   tête de ligne, toujours visible, à la taille d'un doigt. Saisie au
   clavier, la carte porte l'anneau d'accent ; glissée, elle se soulève
   (l'ombre des surfaces flottantes) et un trait d'accent dit où elle
   tombera — dans le blanc de la grille, au-dessus ou au-dessous, sur le
   côté quand deux cartes partagent la ligne. Posée, un anneau s'efface ;
   refusée, un anneau rouge, et une note sous le libellé. Un appui à la
   souris ne montre pas l'anneau du focus (celui du clavier) : la poignée
   reste affichée quand le focus se VOIT dans la carte, pas après un clic. */
.card.ordonnable .ck-tete { position: relative; }
.ck-poignee { position: absolute; left: -20px; top: -4px; width: 16px; height: 26px; padding: 0; border: 0;
  border-radius: 6px; background: transparent; color: var(--muted); cursor: grab; touch-action: none;
  user-select: none; -webkit-user-select: none; -webkit-touch-callout: none;
  display: inline-flex; align-items: center; justify-content: center; opacity: 0;
  transition: opacity .12s, color .12s, background-color .12s; }
.ck-poignee svg { width: 14px; height: 14px; display: block; }
.card.ordonnable:hover .ck-poignee, .card.saisie .ck-poignee, .card.en-deplacement .ck-poignee { opacity: 1; }
.card.ordonnable:has(:focus-visible) .ck-poignee { opacity: 1; }
@supports not selector(:has(*)) {
  .card.ordonnable:focus-within .ck-poignee { opacity: 1; }
}
.ck-poignee:hover, .ck-poignee[aria-pressed="true"] { color: var(--text); background: var(--panel2); filter: none; }
/* L'anneau du focus se pose EN DEDANS de la poignée : dehors, il touchait
   la première lettre du libellé. */
.ck-poignee:focus-visible { opacity: 1; outline-offset: -2px; }
.ck-poignee:active, .card.en-deplacement .ck-poignee { cursor: grabbing; }
.card.saisie { outline: 2px solid var(--accent); outline-offset: 2px; box-shadow: var(--ombre-flottante); }
.card.en-deplacement { position: relative; z-index: 30; box-shadow: var(--ombre-flottante); transition: none;
  will-change: transform; }
#board.ordre-glisse, #board.ordre-glisse * { user-select: none; -webkit-user-select: none; cursor: grabbing; }
/* La carte soulevée peut sortir du plateau (le pointeur l'emmène) : sans
   borne, la page devenait défilable à l'horizontale le temps du geste — une
   barre apparaissait, un défilement latéral décalait tout. `clip` coupe ce
   débordement sans faire du plateau un conteneur qui défile : le défilement
   vertical reste celui de la page, et le défilement près des bords aussi. */
#board.ordre-glisse { overflow-x: clip; }
.card.cible-avant, .card.cible-apres { position: relative; }
.card.cible-avant::after, .card.cible-apres::after { content: ""; position: absolute; left: 0; right: 0;
  height: 3px; border-radius: 999px; background: var(--accent); pointer-events: none;
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 18%, transparent); }
.card.cible-avant::after { top: -11px; }
.card.cible-apres::after { bottom: -11px; }
.card.cible-cote::after { top: 0; bottom: 0; width: 3px; height: auto; }
.card.cible-cote.cible-avant::after { left: -11px; right: auto; }
.card.cible-cote.cible-apres::after { right: -11px; left: auto; }
.card.posee { animation: ordre-posee .9s ease-out; }
@keyframes ordre-posee {
  from { box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 40%, transparent), var(--ombre-carte); }
  to { box-shadow: var(--ombre-carte); }
}
.card.refusee { animation: ordre-refusee 1.4s ease-out; }
@keyframes ordre-refusee {
  from { box-shadow: 0 0 0 3px color-mix(in srgb, var(--bad) 45%, transparent), var(--ombre-carte); }
  to { box-shadow: var(--ombre-carte); }
}
.ordre-refus { margin: -6px 0 10px; font-size: 12px; line-height: 1.45; color: var(--bad);
  overflow-wrap: anywhere; }
.ck-menu-sep { height: 1px; margin: 4px 6px; background: var(--separateur); }
.ck-item:disabled { color: var(--muted); opacity: .5; cursor: default; background: transparent; }
/* La zone qui annonce une position : lue par un lecteur d'écran, invisible. */
.ordre-annonce { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0; }
/* Sur un écran tactile ou étroit, la poignée, toujours visible, vit dans le
   coin haut droit, à côté du ⋯ : en tête de ligne, elle décalait le
   libellé, qui n'était plus aligné sur le surtitre ni sur la valeur. */
@media (hover: none), (max-width: 760px) {
  .ck-poignee { left: auto; right: 24px; top: -6px; opacity: 1; width: 28px; height: 28px; }
  .card.ordonnable .ck-tete { padding-right: 58px; }
  .ck-poignee svg { width: 16px; height: 16px; }
}
@media (max-width: 760px) {
  .card.cible-avant::after { top: -9px; }
  .card.cible-apres::after { bottom: -9px; }
  .card.cible-cote.cible-avant::after { left: -9px; }
  .card.cible-cote.cible-apres::after { right: -9px; }
}
@media (prefers-reduced-motion: reduce) {
  .ck-poignee { transition: none; }
  .card.posee, .card.refusee { animation: none; }
}
@media print {
  .ck-poignee, .ck-menu-sep, .ordre-refus { display: none !important; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   LA PÉRIODE — la barre en tête d'un onglet qui la suit, et ce que les
   cartes en disent. Un SEGMENT de raccourcis (la sélection se dit par le
   fond, le bouton ne change jamais de largeur), « Personnalisé » et ses
   deux mois, puis le résumé des bornes. Tout en jetons : les replis des
   `var()` sont d'autres jetons, pour une instance qui n'a pas encore les
   jetons du segment.
   ═══════════════════════════════════════════════════════════════════════════ */
.periode-barre { grid-column: span 12; display: flex; align-items: center; flex-wrap: wrap;
  gap: 8px 12px; margin: 2px 0 6px; min-width: 0; }
.periode-lab { font-size: 12.5px; font-weight: 600; color: var(--texte-2, var(--muted)); }
/* Le segment des raccourcis : la grammaire des sous-onglets (.subtab-nav). */
.periode-chips { display: inline-flex; flex-wrap: wrap; gap: 2px; padding: 3px; min-width: 0;
  border-radius: 10px; background: var(--segment-fond, var(--panel2)); border: 0; }
.periode-chip { font: inherit; font-size: 13px; line-height: 1; font-weight: 500; cursor: pointer;
  border: 0; background: transparent; color: var(--encre-segment, var(--muted)); padding: 7px 13px; white-space: nowrap;
  border-radius: var(--radius); transition: color .12s, background-color .12s, box-shadow .12s; }
.periode-chip:hover { color: var(--text); filter: none; }
.periode-chip.active { background: var(--segment-actif, var(--panel)); color: var(--text);
  font-weight: 600;
  box-shadow: var(--segment-ombre, 0 1px 2px color-mix(in srgb, var(--text) 8%, transparent)); }
.periode-chip:focus-visible, .periode-mois:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.periode-perso { display: inline-flex; align-items: center; gap: 6px; color: var(--muted); font-size: 12.5px; }
.periode-perso[hidden] { display: none; }
.periode-mois { font: inherit; font-size: 12.5px; color: var(--text); background: var(--panel);
  border: 1px solid var(--border); border-radius: calc(var(--radius) - 2px); padding: 5px 8px;
  color-scheme: inherit; }
/* Une saisie refusée (hors fenêtre, premier mois après le dernier) : le champ
   le porte, le résumé dit pourquoi ; la période, elle, n'a pas changé. */
.periode-mois[aria-invalid="true"] { border-color: var(--bad); }
.periode-resume { font-size: 12.5px; color: var(--texte-2, var(--muted)); font-variant-numeric: tabular-nums;
  margin-left: auto; }
/* Une carte qui attend sa période : son dernier rendu reste, estompé. */
.periode-calcul { opacity: .45; transition: opacity .15s; }
/* Une carte vide sur la période : le visage du « je ne sais pas »
   (`.sans-reponse`) — un seul composant d'état vide de carte, scalaire ou
   graphique, côte à côte. */
.periode-vide { background: var(--panel2); border: 1px dashed var(--border); border-radius: var(--radius);
  padding: 14px 16px; margin: 4px 0 2px; font-size: 12.5px; line-height: 1.6; color: var(--muted); }
/* Un KPI en SQL sous une barre de période : sa fenêtre est celle de sa requête.
   Une carte mensuelle à qui manquent des mois pleins de la période : lesquels
   elle couvre. */
.periode-hors, .periode-couverture { color: var(--muted); font-size: 12px; margin-top: 8px; }
/* Mon espace ou un tableau dont aucune carte ne suit la période : à la
   place de la barre, une phrase douce, rapprochée de ce qui la précède (le
   blanc de la grille en partie repris), jamais une alerte. */
.periode-aucune { grid-column: span 12; min-width: 0; margin: -8px 0 0; font-size: 12.5px;
  line-height: 1.5; color: var(--muted); }
.mensuel-prevu, .mensuel-lu { color: var(--muted); font-weight: 400; }
@media (max-width: 760px) {
  /* Sur un téléphone, les raccourcis restent UN segment : une rangée qui
     défile de côté dans son cadre (la page, elle, ne défile pas), un fondu
     au bord qui a une suite (core.js : periodeBordsChips), le raccourci
     actif ramené dans le champ. « Personnalisé » y reste. */
  .periode-chips { position: relative; flex: 1 1 0; flex-wrap: nowrap; overflow-x: auto;
    scrollbar-width: none; overscroll-behavior-x: contain; }
  .periode-chips::-webkit-scrollbar { display: none; }
  .periode-chips.suite-droite { -webkit-mask-image: linear-gradient(90deg, currentColor calc(100% - 28px), transparent);
    mask-image: linear-gradient(90deg, currentColor calc(100% - 28px), transparent); }
  .periode-chips.suite-gauche { -webkit-mask-image: linear-gradient(90deg, transparent, currentColor 28px);
    mask-image: linear-gradient(90deg, transparent, currentColor 28px); }
  .periode-chips.suite-gauche.suite-droite {
    -webkit-mask-image: linear-gradient(90deg, transparent, currentColor 28px, currentColor calc(100% - 28px), transparent);
    mask-image: linear-gradient(90deg, transparent, currentColor 28px, currentColor calc(100% - 28px), transparent); }
  .periode-chip { padding: 7px 10px; flex: none; }
  .periode-resume { margin-left: 0; width: 100%; }
}
@media (prefers-reduced-motion: reduce) {
  .periode-chip, .periode-calcul { transition: none; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   « MON COMPTE » ET L'ADMINISTRATION — deux pages du châssis

   Des pages de réglages, hors de la barre : chaque section porte son titre
   et sa phrase à gauche, son contenu à droite — une colonne sur un écran
   étroit. Les formulaires sont ceux des pages (`dv-form`), les cartes
   aussi (`vue-carte`), les listes des lignes aérées ; l'administration
   ajoute un segment d'onglets et des tables qui deviennent des fiches sur
   un téléphone. L'avatar aux initiales est le même dans le menu, la page
   et la liste des comptes. En jetons, comme le reste.
   ═══════════════════════════════════════════════════════════════════════════ */
.avatar-compte { display: inline-grid; place-items: center; flex: none; width: 34px; height: 34px;
  border-radius: 50%; font: 600 12px/1 var(--police, inherit); letter-spacing: .02em;
  color: var(--accent); background: var(--accent-fond); user-select: none; }
.avatar-compte.petit { width: 30px; height: 30px; font-size: 11px; }
.avatar-compte.grand { width: 64px; height: 64px; font-size: 22px; }
.vue-compte, .vue-admin { max-width: 1080px; }

/* ── Mon compte ── */
.compte-entete { align-items: center; }
/* L'avatar à gauche du nom : la règle des en-têtes de page empile leur premier bloc. */
.compte-entete > div.compte-tete { display: flex; flex-direction: row; align-items: center; gap: 18px;
  min-width: 0; }
.compte-tete-texte { min-width: 0; display: flex; flex-direction: column; gap: 6px; }
/* Le nom à la taille de tout titre de page ; un email se coupe à son
   arobase (`<wbr>`, htmlTeteCompte), jamais avant sa dernière lettre. */
.vue-titre.compte-nom { overflow-wrap: break-word; }
.compte-email { font-size: 14px; color: var(--muted); overflow-wrap: anywhere; }
.compte-tete .vue-puces { margin-top: 4px; }
.compte-sommaire { display: flex; flex-wrap: wrap; gap: 6px; margin-top: -12px; }
.compte-ancre { background: transparent; color: var(--texte-2); border: 1px solid var(--border);
  border-radius: 999px; padding: 5px 12px; font-size: 12.5px; font-weight: 500; line-height: 1.3; }
.compte-ancre:hover { filter: none; border-color: var(--accent); color: var(--text); }
.compte-jetons-hote { display: flex; flex-direction: column; gap: 34px; }
.compte-bloc { display: grid; grid-template-columns: minmax(0, 250px) minmax(0, 1fr); gap: 14px 40px;
  padding-top: 26px; border-top: 1px solid var(--vue-filet-doux); scroll-margin-top: 16px; }
.compte-bloc-tete { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.compte-bloc-tete h2 { font-size: 16px; font-weight: 600; }
.compte-bloc-tete h2:focus-visible { outline-offset: 4px; }
.compte-bloc-tete .vue-doux { font-size: 13px; }
.compte-bloc-corps { display: flex; flex-direction: column; gap: 14px; min-width: 0; }
.dv-form.compte-form { gap: 14px; }
.compte-faits { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px 18px;
  margin: 2px 0 0; padding-top: 14px; border-top: 1px solid var(--vue-filet-doux); }
.compte-faits dt { font-size: 12px; font-weight: 600; color: var(--muted); }
.compte-faits dd { margin: 3px 0 0; font-size: 13.5px; line-height: 1.45; overflow-wrap: anywhere; }
.vue:not(.card) code.compte-cle { font-size: 11.5px; margin-left: 2px; }
.dv-form.compte-cle-form { flex-direction: row; flex-wrap: wrap; align-items: flex-end; gap: 12px; }
.compte-cle-form label { flex: 1 1 260px; min-width: 0; }
.compte-cle-form .dv-form-pied { flex: 0 1 auto; }
.compte-cle-form .vue-btn.petit { min-height: 38px; }
/* Une carte pour les clés : la liste sous le formulaire, après un filet. */
.compte-cles-carte .compte-cle-form + .compte-jetons, .compte-cles-carte .compte-cle-form + .compte-vide,
.compte-cles-carte .compte-cle-form + .compte-panne { margin-top: 16px; padding-top: 16px;
  border-top: 1px solid var(--vue-filet-doux); }
button.compte-vers-mcp { background: none; border: 0; padding: 0; cursor: pointer; color: var(--accent);
  font: inherit; font-weight: 500; }
button.compte-vers-mcp:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }
.compte-jetons { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.compte-jeton { display: flex; align-items: center; gap: 12px; padding: 12px 0; min-width: 0;
  border-top: 1px solid var(--vue-filet-doux); }
.compte-jeton:first-child { border-top: 0; padding-top: 0; }
.compte-jeton-ic { flex: none; width: 34px; height: 34px; border-radius: 9px; display: inline-grid;
  place-items: center; color: var(--texte-2); background: var(--panel2); }
.compte-jeton-ic .ic { width: 16px; height: 16px; }
.compte-jeton.courant .compte-jeton-ic { color: var(--accent); background: var(--accent-fond); }
.compte-jeton-texte { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.compte-jeton-nom { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; font-size: 14px;
  font-weight: 600; overflow-wrap: anywhere; }
.compte-jeton-meta { font-size: 12.5px; line-height: 1.45; color: var(--muted); overflow-wrap: anywhere; }
.compte-jeton .vue-btn { flex: none; }
/* Un jeton qui n'ouvre plus (révoqué, échu) : en retrait. */
.compte-jeton.eteint .compte-jeton-nom, .compte-jeton.eteint .compte-jeton-ic { color: var(--muted); }
.compte-liste-pied { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: 10px 16px; margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--vue-filet-doux); }
.compte-liste-pied .vue-doux { flex: 1 1 260px; font-size: 12.5px; }
.compte-liste > .vue-doux { margin-top: 12px; font-size: 12.5px; }
.compte-liste p.form-msg { margin-top: 10px; font-size: 12.5px; }
/* Le refus d'un jeton, sur sa ligne : sous ses dates, à gauche. */
.compte-jeton .compte-jeton-texte > p.form-msg { margin: 2px 0 0; font-size: 12.5px; min-height: 0;
  align-self: flex-start; }
.compte-vide { font-size: 13.5px; line-height: 1.55; color: var(--muted); }
/* Une liste illisible : le refus et « Réessayer », dans sa section. */
.compte-panne { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px; }
.compte-panne p { flex: 1 1 240px; margin: 0; }
/* Sous la panne, l'effet d'un geste abouti avant la relecture (« Révoqué… »). */
.compte-panne + p.form-msg { margin-top: 10px; font-size: 12.5px; align-self: flex-start; }
.compte-sous-titre { font-size: 14px; font-weight: 600; margin-bottom: 10px; }
.compte-url { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin: 8px 0 4px; }
.vue:not(.card) code.compte-url-valeur { flex: 1 1 240px; min-width: 0; padding: 8px 10px; font-size: 13px;
  line-height: 1.4; user-select: all; overflow-wrap: anywhere; }
.compte-url-etat { font-size: 12.5px; color: var(--muted); }
.compte-etapes { margin: 12px 0 0; padding-left: 1.3em; display: flex; flex-direction: column; gap: 6px;
  font-size: 13.5px; line-height: 1.5; }
.compte-connecter .vue-alerte { margin-top: 14px; }

/* ── L'administration ── */
.admin-onglets { align-self: flex-start; margin: -10px 0 0; max-width: 100%; }
.admin-corps { min-width: 0; }
.admin-onglet { display: flex; flex-direction: column; gap: 18px; min-width: 0; }
/* Vide, l'onglet attend son rendu : la feuille le dit, le rendu le remplace. */
.admin-onglet:empty::before { content: "chargement…"; color: var(--muted); font-size: 12.5px;
  animation: respire 1.6s ease-in-out infinite; }
.admin-comptes, .admin-journal { display: flex; flex-direction: column; gap: 18px; min-width: 0; }
.admin-creer .vue-carte-tete h2, .admin-liste .vue-carte-tete h2 { font-size: 15px; }
.admin-creer > .vue-doux { margin-top: 4px; font-size: 13px; }
.admin-creer-form { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 12px; margin-top: 14px; }
.admin-creer-form label { display: flex; flex-direction: column; gap: 6px; flex: 1 1 200px; min-width: 0;
  font-size: 12.5px; font-weight: 600; color: var(--muted); }
.admin-creer-form label.case { flex: 0 0 auto; flex-direction: row; align-items: center; gap: 8px;
  min-height: 38px; font-size: 13px; font-weight: 500; color: var(--text); }
.admin-creer-form input[type="email"], .admin-creer-form input[type="text"], .admin-creer-form select,
.admin-renommer input[type="text"] { font: inherit; font-size: 14px; font-weight: 400;
  min-height: 38px; padding: 7px 10px; min-width: 0; border-radius: var(--radius);
  border: 1px solid var(--border-strong); background: var(--panel); color: var(--text); }
.admin-creer-form input[type="email"]:focus, .admin-creer-form input[type="text"]:focus,
.admin-creer-form select:focus, .admin-renommer input[type="text"]:focus { border-color: var(--accent);
  outline: none; }
/* Le prénom et le nom, facultatifs : plus étroits que l'email, qui identifie. */
.admin-creer-form label.admin-champ-nom { flex: 1 1 150px; }
/* Le profil prend la largeur de son plus long libellé : il passe à la ligne
   plutôt que de se couper (« lecture — couche modélise »). */
.admin-creer-form label.admin-champ-profil { flex: 0 1 auto; }
.admin-creer-form input[type="checkbox"], .admin-table input[type="checkbox"] { width: 16px; height: 16px;
  margin: 0; accent-color: var(--accent); }
.admin-creer-form .form-msg { flex: 1 1 100%; align-self: auto; }
.admin-liste .vue-carte-tete { margin-bottom: 6px; flex-wrap: wrap; }
.admin-liste .vue-carte-tete .vue-doux { font-size: 12.5px; }
.table-scroll.admin-table-scroll { max-height: none; }
/* Les tables de l'administration se jugent à la largeur de LEUR carte, pas
   de la fenêtre : avec le rail, une fenêtre de 1024 px ne laisse que 674 px
   à la carte. Au-dessus du seuil (plus bas), la table tient dans sa carte —
   le profil en entier, l'email sur une ligne, les gestes côte à côte,
   l'étiquette « en attente » sur deux lignes au besoin : 920 px au plus
   serré ; en dessous, chaque ligne devient une fiche. */
.admin-liste { container: admin-liste / inline-size; }
.admin-table > thead > tr > th, .admin-table > tbody > tr > td { padding-left: 8px; padding-right: 8px; }
.admin-table > thead > tr > th { white-space: normal; }
.admin-personne { display: flex; align-items: center; gap: 10px; min-width: 0; }
.admin-personne-texte { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; min-width: 0;
  white-space: normal; }
.admin-nom { font-size: 13.5px; font-weight: 600; overflow-wrap: anywhere; }
/* L'email, sur UNE ligne, coupé d'une ellipse (le titre le donne entier) :
   un seul email long étirait la colonne, et toutes les lignes avec. */
.admin-email { display: block; max-width: 100%; font-size: 12px; color: var(--muted); white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis; }
.admin-personne-texte { max-width: 100%; }
/* La largeur d'une colonne de table se mesure sur son contenu : sans borne
   en unités, l'ellipse ne couperait rien. En fiche, la largeur de la carte. */
.admin-table .admin-email { max-width: 20ch; }
.admin-personne .badge-etat { margin-top: 3px; height: auto; min-height: 20px; padding-top: 3px;
  padding-bottom: 3px; line-height: 1.25; white-space: normal; }
.admin-lien-etat { font-size: 11.5px; color: var(--muted); }
.admin-jamais { color: var(--muted); }
/* Le profil se lit en entier : le choix prend la largeur de son plus long
   libellé (borné à 260 px ; au-delà, le libellé entier est en bulle) — à
   150 px, « lecture — couche brute » et « lecture — couche modélisée » se
   lisaient tous deux « lecture — couche ». */
.admin-table select { font: inherit; font-size: 13px; padding: 5px 8px; max-width: 260px;
  border-radius: 7px; border: 1px solid var(--border); background: var(--panel); color: var(--text); }
/* Les en-têtes courts ne se coupent pas ; le dernier accès se lit le jour,
   puis l'heure dessous. En table, les gestes d'une ligne, plus compacts,
   tiennent sur UNE ligne : un email long ne les fait plus passer en grille
   sur toutes les lignes (en fiche, ils passent à la ligne, plus bas). */
.admin-table > thead > tr > th.admin-th-court, .admin-acces-jour, .admin-acces-heure { white-space: nowrap; }
.admin-acces-jour, .admin-acces-heure { display: inline-block; }
.admin-table .admin-gestes .vue-btn.petit { min-height: 30px; padding: 0 8px; font-size: 12.5px; }
.admin-table > tbody > tr > td.admin-actions .admin-gestes { flex-wrap: nowrap; }
/* Un compte fermé : son nom barré — pas son avatar. */
.admin-table .compte-ferme td:first-child { text-decoration: none; }
.compte-ferme .admin-nom, .compte-ferme .admin-email { color: var(--muted); text-decoration: line-through; }
.compte-ferme .avatar-compte { color: var(--muted); background: var(--panel2); }
.admin-liste p.form-msg { margin-top: 10px; font-size: 12.5px; }
/* Les zones qui parlent de Mon compte et de l'administration (role=status,
   aria-live), VIDES : rendues, hors de la vue — jamais `display: none`. Masquées,
   elles entraient dans l'arbre d'accessibilité avec leur texte, et ne
   s'annonçaient pas quand le geste gardait le focus (« Créer le compte » refusé,
   une révocation refusée, « Adresse copiée »… : mesuré dans Chromium). Hors du flux, elles ne laissent ni trou ni écart de gap (le
   message ne pousse pas « Créer la clé » à 12 px de la marge). Le patron de
   `.lecteur-seul`. */
.compte-cle-form .form-msg:empty, .compte-liste p.form-msg:empty,
.compte-jeton .compte-jeton-texte > p.form-msg:empty, .compte-panne + p.form-msg:empty,
.compte-url-etat:empty, .admin-creer-form .form-msg:empty, .admin-liste p.form-msg:empty { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0; }
/* Le message d'un geste de ligne : sous la ligne de son compte (collé à
   elle, sans filet entre les deux), ou dans ses jetons dépliés — jamais
   sous toute la table, hors de vue. Sur --vue-fond-doux, --bad garde
   4,63:1 en clair. */
.admin-table > tbody > tr.ligne-msg > td { padding-top: 0; }
.admin-table > tbody > tr:has(+ tr.ligne-msg > td > p.form-msg:not(:empty)) > td {
  border-bottom-color: transparent; }
.admin-liste .ligne-msg p.form-msg, .admin-liste .admin-jetons p.form-msg { margin-top: 0; align-self: flex-start; }
/* « Renommer » : le prénom et le nom d'un compte, dépliés sous sa ligne
   comme ses jetons — sur le même fond doux (l'encre discrète y garde
   4,5:1) ; les deux champs côte à côte, l'un sous l'autre sur un écran
   étroit, puis les deux gestes. */
.admin-table > tbody > tr.renommer-de > td, .admin-table > tbody > tr.renommer-de:hover > td {
  background: var(--vue-fond-doux); }
.admin-table > tbody > tr.renommer-de > td { padding: 10px 12px; white-space: normal; }
.admin-renommer { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 10px 12px; }
.admin-renommer label { display: flex; flex-direction: column; gap: 6px; flex: 1 1 160px; min-width: 0;
  font-size: 12.5px; font-weight: 600; color: var(--muted); }
.admin-renommer-gestes { display: flex; flex-wrap: wrap; gap: 6px; }
.admin-liste .admin-renommer p.form-msg { flex: 1 1 100%; margin-top: 0; align-self: auto; }
.admin-renommer-aide { flex: 1 1 100%; font-size: 12px; }
/* L'aide sous la table : trois puces courtes, pas un mur de onze lignes. */
.admin-aide { margin: 0; padding-left: 1.2em; display: flex; flex-direction: column; gap: 6px;
  font-size: 12.5px; line-height: 1.55; color: var(--muted); max-width: 62em; }
.admin-barre { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: 10px 16px; }
.subtab-nav.admin-jours { margin: 0; }
@media (max-width: 760px) {
  /* Les quatre fenêtres du journal sur une ligne d'un téléphone. */
  .subtab-nav.admin-jours .subtab { padding: 7px 10px; }
}
.admin-journal-choix { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 10px; }
/* « Refus seulement » : une bascule, enfoncée quand le filtre est posé. */
.vue-btn.second.admin-filtre[aria-pressed="true"] { color: var(--accent); background: var(--vue-teinte-accent);
  border-color: var(--vue-filet-accent); }
.admin-journal-resume { flex: 1 1 320px; font-size: 13px; margin: 0 0 10px; }
.admin-journal-carte .vue-carte-tete h2:focus-visible { outline-offset: 4px; }
/* Le jour, en intertitre des lignes qu'il porte : la ligne ne dit plus que l'heure. */
.admin-table-journal > tbody > tr.journal-jour > th { padding: 14px 8px 6px; text-align: left; font-size: 12px;
  font-weight: 600; letter-spacing: .01em; color: var(--texte-2); border-bottom: 1px solid var(--separateur); }
.admin-table-journal > tbody > tr.journal-jour:first-child > th { padding-top: 4px; }
.admin-table-journal > tbody > tr.journal-jour:hover > th { background: transparent; }
.journal-plus { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; margin-top: 12px; }
.journal-plus[hidden] { display: none; }
.comptes-table > tbody > tr > td.admin-quand { white-space: nowrap; font-variant-numeric: tabular-nums; }
.admin-detail { white-space: normal; overflow-wrap: anywhere; min-width: 160px; }
.comptes-table > tbody > tr > td.admin-qui { overflow-wrap: anywhere; }
/* L'encre du refus foncée vers le texte : 10,5 px en --bad sur --bad-fond ne
   faisait que 4,41:1 en clair (AA demande 4,5:1 pour un petit texte). */
.badge-refus { color: color-mix(in srgb, var(--bad) 72%, var(--text)); background: var(--bad-fond);
  border-color: color-mix(in srgb, var(--bad) 35%, var(--border)); }
/* « Le cockpit redémarre… » : un geste du poste a réécrit sa configuration. */
.admin-redemarre { display: flex; align-items: flex-start; gap: 14px; padding: 16px 18px;
  border-radius: var(--rayon-carte); border: 1px solid var(--vue-filet-accent);
  background: var(--vue-teinte-accent); }
.admin-redemarre .vue-doux { margin-top: 2px; }
.admin-redemarre .vue-btn { margin-top: 10px; }
.admin-roue { flex: none; width: 20px; height: 20px; margin-top: 1px; border-radius: 50%;
  border: 2px solid var(--vue-filet-accent); border-top-color: var(--accent);
  animation: admin-tourne .9s linear infinite; }
@keyframes admin-tourne { to { transform: rotate(360deg); } }

@media (max-width: 900px) {
  .compte-bloc { grid-template-columns: minmax(0, 1fr); gap: 12px; padding-top: 22px; }
}
@media (max-width: 760px) {
  /* Sur un téléphone, l'avatar passe AU-DESSUS du nom : à sa gauche, centré
     sur un bloc de six lignes, il prenait 70 px de la largeur du nom. */
  .compte-entete > div.compte-tete { flex-direction: column; align-items: flex-start; gap: 12px; }
  .vue-titre.compte-nom { font-size: clamp(20px, 6vw, 24px); }
  .avatar-compte.grand { width: 44px; height: 44px; font-size: 16px; }
  .compte-sommaire { margin-top: -6px; }
  .compte-faits { grid-template-columns: minmax(0, 1fr); }
  /* Une ligne de jeton : le pictogramme à gauche, le texte puis son geste à droite. */
  .compte-jeton { display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: start; gap: 8px 12px; }
  .compte-jeton .vue-btn { grid-column: 2; justify-self: start; }
  .admin-creer-form label { flex-basis: 100%; }
}
/* Une carte plus étroite que la table (un téléphone, une demi-fenêtre, un
   portable sous 1 270 px environ avec le rail) : chaque compte devient une
   fiche, chaque valeur après son libellé, chaque ligne du journal une
   fiche de deux lignes — rien ne défile de côté. La table tient en 920 px
   (mesuré dans Chrome : le profil en entier, l'email sur une ligne, les
   trois gestes côte à côte) ; plus serrée, ses gestes s'empilaient sur
   trois lignes. Des combinateurs d'ENFANT : ce qui est déplié dans une
   ligne (les jetons d'un compte) ne relève pas de ces règles. */
@container admin-liste (max-width: 919.98px) {
  .admin-table > thead { display: none; }
  .admin-table, .admin-table > tbody, .admin-table > tbody > tr, .admin-table > tbody > tr > td { display: block;
    width: 100%; }
  .admin-table > tbody > tr[hidden] { display: none; }
  .admin-table > tbody > tr { padding: 10px 0; border-top: 1px solid var(--vue-filet-doux); }
  .admin-table > tbody > tr:first-child { border-top: 0; padding-top: 0; }
  .admin-table > tbody > tr.jetons-de, .admin-table > tbody > tr.ligne-msg,
  .admin-table > tbody > tr.renommer-de { border-top: 0; padding-top: 0; }
  /* Le libellé dans une colonne de largeur fixe, la valeur juste après : sur
     une carte large, la valeur ne part plus au bord opposé. */
  .admin-table > tbody > tr > td { display: flex; align-items: center; justify-content: flex-start; gap: 12px;
    padding: 4px 0; border: 0; white-space: normal; }
  .admin-table > tbody > tr > td[data-libelle]::before { content: attr(data-libelle); flex: 0 0 110px; font-size: 12px;
    color: var(--muted); }
  .admin-table > tbody > tr > td[data-libelle="Personne"]::before { content: none; }
  .admin-table > tbody > tr > td[data-libelle="Détail"] { display: block; }
  .admin-table > tbody > tr > td[data-libelle="Détail"]::before { display: block; margin-bottom: 2px; }
  .admin-table > tbody > tr > td.admin-actions { justify-content: flex-start; padding-top: 8px; }
  .admin-table .admin-email { max-width: 100%; }
  .admin-table > tbody > tr > td.admin-actions .admin-gestes { flex-wrap: wrap; }
  .admin-table > tbody > tr.jetons-de > td, .admin-table > tbody > tr.renommer-de > td { display: block;
    padding: 10px 12px; }
  .admin-table > tbody > tr.ligne-msg > td { display: block; padding: 0 0 6px; }
  /* En fiche, le profil a la largeur de la carte : son libellé entier, pas les 150 px de la table —
     sous son libellé quand il ne tient pas à côté. */
  .admin-table > tbody > tr > td > select { max-width: none; min-width: 0; }
  .admin-table > tbody > tr > td[data-libelle="Profil"] { flex-wrap: wrap; row-gap: 6px; }
  .admin-table > tbody > tr:hover > td { background: transparent; }
  .admin-detail { min-width: 0; }
  /* Le journal, en fiches COMPACTES — sans libellés répétés, deux lignes par
     événement : « 20:32 · carte · L'activité de la période », puis qui et
     sous quel profil, en discret. Cinq rangées libellées faisaient 155 px
     par ligne. Le jour est l'intertitre. */
  .admin-table-journal > tbody > tr:not(.journal-jour) { display: flex; flex-wrap: wrap; align-items: baseline;
    row-gap: 2px; padding: 8px 0; }
  .admin-table-journal > tbody > tr:not(.journal-jour)::after { content: ""; order: 4; flex-basis: 100%; height: 0; }
  .admin-table-journal > tbody > tr > td[data-libelle] { display: block; width: auto; padding: 0; font-size: 13px;
    line-height: 1.45; }
  .admin-table-journal > tbody > tr > td[data-libelle]::before { content: none; }
  .admin-table-journal > tbody > tr > td.admin-quand { order: 1; font-variant-numeric: tabular-nums; }
  .admin-table-journal > tbody > tr > td.journal-quoi { order: 2; }
  /* Le détail : son « · » à part, le texte replié sous lui-même, pas sous le point. */
  .admin-table-journal > tbody > tr > td.admin-detail { order: 3; display: flex; align-items: baseline; gap: 0;
    flex: 1 1 0; min-width: 0; }
  /* La seconde ligne tient sur UNE ligne : l'email (au plus 55 %) puis le profil, qui prend le reste — chacun
     abrégé d'une ellipse, jamais renvoyé à une troisième ligne. */
  .admin-table-journal > tbody > tr > td.admin-qui, .admin-table-journal > tbody > tr > td.journal-profil {
    min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .admin-table-journal > tbody > tr > td.admin-qui { order: 5; flex: 0 1 auto; max-width: 55%; }
  .admin-table-journal > tbody > tr > td.journal-profil { order: 6; flex: 1 1 0; }
  .admin-table-journal > tbody > tr > td.journal-quoi::before,
  .admin-table-journal > tbody > tr > td.admin-detail::before,
  .admin-table-journal > tbody > tr > td.journal-profil::before { content: "·"; display: inline; flex: none; margin: 0 6px;
    color: var(--muted); }
  .admin-table-journal > tbody > tr > td.admin-qui, .admin-table-journal > tbody > tr > td.journal-profil {
    font-size: 12px; color: var(--muted); }
  .admin-table-journal > tbody > tr.journal-jour { display: block; padding: 0; border: 0; }
  .admin-table-journal > tbody > tr.journal-jour > th { display: block; padding: 12px 0 4px; }
}
@media (prefers-reduced-motion: reduce) {
  .admin-roue, .admin-onglet:empty::before { animation: none; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   LA LICENCE — l'écran d'arrêt, le bandeau du mois de grâce, l'onglet
   « Licence » de l'administration

   L'écran d'arrêt est celui de la connexion (#connexion, la marque de
   l'instance en tête) : la phrase du serveur en encre pleine — c'est le
   contenu de l'écran —, le champ de la clé en chasse fixe (une clé se
   relit signe à signe, et se coupe n'importe où plutôt que d'élargir la
   carte), les gestes secondaires discrets. Le bandeau prend la teinte de
   l'alerte des vues, pas le rouge de la santé : rien n'est encore arrêté.
   L'onglet reprend les cartes, les puces, les faits et le formulaire des
   pages du châssis. En jetons, comme le reste.
   ═══════════════════════════════════════════════════════════════════════════ */
#connexion.licence-arret .licence-arret-message { color: var(--texte-2); font-size: 13px; }
#connexion.licence-arret .licence-arret-qui { color: var(--text); font-size: 13px; }
#connexion.licence-arret textarea { font: 12.5px/1.5 var(--police-mono); padding: 10px 12px; resize: vertical;
  min-height: 92px; border-radius: var(--radius); border: 1px solid var(--border); background: var(--panel);
  color: var(--text); overflow-wrap: anywhere; word-break: break-all;
  transition: border-color .15s, box-shadow .15s; }
#connexion.licence-arret textarea:focus { border-color: var(--accent); outline: none;
  box-shadow: var(--anneau-focus); }
#connexion.licence-arret .licence-arret-relire, #connexion.licence-arret .licence-arret-sortir {
  background: var(--panel2); color: var(--text); border: 1px solid var(--border); }
#connexion.licence-arret .licence-arret-relire:hover, #connexion.licence-arret .licence-arret-sortir:hover {
  background: var(--border); filter: none; }

/* Le bandeau : la phrase, puis le geste (un superadmin) ou à qui s'adresser. */
.bandeau-licence { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 16px; padding: 10px 16px;
  border-radius: var(--rayon-carte); font-size: 13.5px; line-height: 1.5;
  color: var(--vue-encre-alerte); background: var(--vue-teinte-alerte); border: 1px solid var(--vue-filet-alerte); }
.bandeau-licence[hidden] { display: none; }
.bandeau-licence .bl-texte { flex: 1 1 280px; min-width: 0; margin: 0; overflow-wrap: break-word; }
.bandeau-licence .bl-qui { flex: 1 1 220px; min-width: 0; font-size: 12.5px; }
.bandeau-licence .bl-geste { flex: none; padding: 6px 12px; border-radius: var(--radius); font-size: 12.5px;
  font-weight: 600; color: var(--text); background: var(--panel); border: 1px solid var(--vue-filet-alerte); }
.bandeau-licence .bl-geste:hover { border-color: var(--accent); filter: none; }

/* L'onglet : la racine garde sa zone d'annonce (lue à voix haute, hors de
   la vue) ; seul `.licence-contenu` se redessine après un geste. */
.licence-admin { position: relative; min-width: 0; }
.licence-contenu { display: flex; flex-direction: column; gap: 18px; min-width: 0; }
.licence-annonce { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%);
  white-space: nowrap; }
.licence-carte { display: flex; flex-direction: column; gap: 12px; }
.licence-carte h2:focus { outline: none; }
.licence-carte h2:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 4px; }
.licence-carte p { margin: 0; }
.licence-message { font-size: 13.5px; line-height: 1.55; color: var(--text); overflow-wrap: break-word; }
.licence-faits code { font: 12.5px/1.45 var(--police-mono); overflow-wrap: anywhere; }
.puce-arret { color: var(--bad); background: var(--bad-fond);
  border-color: color-mix(in srgb, var(--bad) 40%, var(--border)); }
.licence-form textarea { font-family: var(--police-mono); font-size: 12.5px; overflow-wrap: anywhere;
  word-break: break-all; }
.licence-msg { margin: 0; font-size: 12.5px; line-height: 1.5; }
.licence-msg:empty { display: none; }
.licence-msg.ok { color: var(--good); }
/* L'attente (« Demande à Doctrine… ») : neutre — `.form-msg` est rouge, la couleur d'un refus. */
.licence-msg.encours { color: var(--muted); }
.licence-contenu[aria-busy="true"] button:disabled { opacity: .6; }

/* La contribution au chapeau (core.js, même titre) : un bloc, en trois
   places — l'onglet, l'écran d'arrêt, le relevé. Le champ du montant est
   court : un nombre d'euros, rien d'autre. */
.contribution { display: flex; flex-direction: column; gap: 10px; min-width: 0; }
.contribution[hidden] { display: none; }
.contribution p { margin: 0; font-size: 13px; line-height: 1.55; overflow-wrap: break-word; }
.contribution-titre { margin: 0; font-size: 14px; }
.contribution-form input[name="montant"] { max-width: 12em; }
.contribution-msg.ok { color: var(--good); }
.contribution-paiement[hidden] { display: none; }
#connexion.licence-arret .licence-arret-contribuer { background: var(--panel2); color: var(--text);
  border: 1px solid var(--border); }
#connexion.licence-arret .licence-arret-contribution { margin-top: 14px; }

/* Les invitations (Administration › Invitations) : les cartes de l'onglet
   Licence ; une ligne par invitation — le nom, l'état daté, la chaîne en
   nombre, le geste. */
.invitations-admin { position: relative; display: flex; flex-direction: column; gap: 18px; min-width: 0; }
.invitations-contenu { display: flex; flex-direction: column; gap: 18px; min-width: 0; }
.invitations-lien[hidden] { display: none; }
.invitations-liste { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.invitation { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 14px; padding: 9px 0;
  border-top: 1px solid var(--border); font-size: 13px; }
.invitation:first-child { border-top: 0; }
.invitation-nom { font-weight: 600; min-width: 0; overflow-wrap: anywhere; }
.invitation-etat { color: var(--muted); }
.invitation-annuler { margin-left: auto; }
.invitations-emise .secret-bloc { display: flex; flex-direction: column; gap: 6px; }

/* La carte des invitations, au premier tableau en ligne : sous la barre,
   alignée sur les bandeaux ; une proposition, pas une alerte. */
.carte-invitations { box-sizing: border-box; width: calc(min(1500px, 100%) - 56px); margin: 14px auto 0;
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; padding: 10px 16px;
  border-radius: var(--rayon-carte); font-size: 13.5px; line-height: 1.5; color: var(--text);
  background: var(--panel); border: 1px solid var(--border); }
.carte-invitations .ci-texte { flex: 1 1 320px; min-width: 0; margin: 0; overflow-wrap: break-word; }
.carte-invitations button { flex: none; padding: 6px 12px; border-radius: var(--radius); font-size: 12.5px;
  font-weight: 600; color: var(--text); background: var(--panel2); border: 1px solid var(--border); }
.carte-invitations .ci-voir:hover { border-color: var(--accent); filter: none; }
@media (max-width: 760px) { .carte-invitations { width: calc(100% - 32px); } }
@media print { .carte-invitations { display: none !important; } }

/* Le relevé : la contribution et les invitations, sous l'usage du mois. */
.conf-releve { display: flex; flex-direction: column; gap: 14px; margin: 14px 0; padding: 14px 0;
  border-top: 1px solid var(--border); border-bottom: 1px solid var(--border); }
.conf-releve h4 { margin: 0 0 6px; }
.conf-invitations p { margin: 0 0 8px; }

/* ═══════════════════════════════════════════════════════════════════════════
   LES CARTES GÉNÉRIQUES — la largeur déclarée, la jauge, la cascade, la
   chaleur, le classement, le compte de résultat (cards.js, même titre)

   En jetons seulement : `--c` est la couleur d'état que le rendu pose
   (varEtat, un `var(--…)`), ses teintes des color-mix. Les jetons
   facultatifs (--texte-2, --separateur, --rayon-carte, --segment-*…) ont ici
   leur repli : une instance qui ne les déclare pas rend quand même. Tout
   est préfixé `rendu-` : un type de carte ne prend plus le style d'un
   composant qui porterait son nom.
   ═══════════════════════════════════════════════════════════════════════════ */

/* La largeur d'une carte : celle que son type déclare (registerCard), ou
   celle de son KPI. Après les règles par type, qu'elle remplace ; une
   feuille d'instance chargée après garde le dernier mot. */
.card[data-largeur="3"] { grid-column: span 3; }
.card[data-largeur="4"] { grid-column: span 4; }
.card[data-largeur="6"] { grid-column: span 6; }
.card[data-largeur="8"] { grid-column: span 8; }
.card[data-largeur="12"] { grid-column: span 12; }
@media (max-width: 1100px) {
  .card[data-largeur] { grid-column: span 12; }
  .card[data-largeur="3"], .card[data-largeur="4"] { grid-column: span 6; }
}
@media (max-width: 640px) {
  .card[data-largeur] { grid-column: span 12; }
}

/* ── Les pièces communes ── */
.rendu-vide { padding: 14px 16px; color: var(--muted); font-size: 12.5px; line-height: 1.6;
  background: var(--panel2); border: 1px dashed var(--border); border-radius: var(--radius); }
.rendu-note { margin-top: 10px; color: var(--muted); font-size: 11.5px; line-height: 1.5; }
.rendu-total { margin-top: 12px; padding-top: 9px; border-top: 1px solid var(--separateur, var(--border));
  font-size: 12px; font-weight: 600; color: var(--text); }
.rendu-pastille { display: inline-flex; align-items: center; gap: 6px; max-width: 100%; padding: 3px 9px 3px 7px;
  border-radius: 999px; font-size: 11.5px; font-weight: 600; line-height: 1.3; color: var(--c, var(--muted));
  background: color-mix(in srgb, var(--c, var(--muted)) 12%, transparent); white-space: nowrap; }
.rendu-pastille i { width: 7px; height: 7px; border-radius: 50%; background: var(--c, var(--muted)); flex: none; }
.rendu-puce { display: inline-flex; padding: 1px 7px; border-radius: 999px; font-size: 11px; font-weight: 600;
  color: var(--c, var(--muted)); background: color-mix(in srgb, var(--c, var(--muted)) 12%, transparent);
  white-space: nowrap; }
.rendu-legende { display: flex; flex-wrap: wrap; gap: 6px 14px; margin-top: 12px; font-size: 11px;
  color: var(--muted); }
.rendu-legende span { display: inline-flex; align-items: center; gap: 6px; }
.rendu-legende i { width: 12px; height: 10px; border-radius: 3px; background: var(--c); }
.rendu-legende [data-forme="visee"] i { background: repeating-linear-gradient(135deg,
  color-mix(in srgb, var(--c) 40%, transparent) 0 3px, color-mix(in srgb, var(--c) 14%, transparent) 3px 6px);
  box-shadow: inset 0 0 0 1px var(--c); }
/* Un choix dans la carte (une vue, un scénario) : un segment, comme les
   sous-onglets — la sélection se dit par le fond. */
.rendu-segment { display: inline-flex; flex-wrap: wrap; gap: 2px; padding: 3px;
  border-radius: calc(var(--radius) + 2px); background: var(--segment-fond, var(--panel2)); }
.rendu-segment button { border: 0; background: transparent; color: var(--muted); font: inherit;
  font-size: 12.5px; font-weight: 500; line-height: 1.2; padding: 6px 11px;
  border-radius: calc(var(--radius) - 2px); cursor: pointer; transition: color .12s, background-color .12s; }
.rendu-segment button:hover { color: var(--text); filter: none; }
.rendu-segment button[aria-pressed="true"] { background: var(--segment-actif, var(--panel)); color: var(--text);
  font-weight: 600; box-shadow: var(--segment-ombre, 0 1px 2px color-mix(in srgb, var(--text) 8%, transparent)); }

/* ── L'étincelle ── */
.rendu-etincelle { flex: none; overflow: visible; }
.rendu-etincelle path { fill: none; stroke: var(--c); stroke-width: 2; stroke-linejoin: round;
  stroke-linecap: round; }
.rendu-etincelle circle { fill: var(--c); }
.rendu-etincelle-objectif { stroke: var(--muted); stroke-width: 1; stroke-dasharray: 3 3; opacity: .7; }

/* ── La jauge ── */
.rendu-tuile { display: flex; flex-direction: column; gap: 9px; min-width: 0; }
.rendu-tuile-haut { display: flex; align-items: flex-end; justify-content: space-between; gap: 10px; }
.rendu-tuile .big { flex-wrap: wrap; min-width: 0; }
.rendu-tuile .rendu-pastille { align-self: flex-start; white-space: normal; }
.rendu-tuile .subtitle { margin-top: 0; }
.rendu-tuile[data-grand] .big { font-size: 44px; }
.rendu-jauge { margin-top: 2px; }
.rendu-jauge-piste { position: relative; height: 8px; border-radius: 999px; background: var(--panel2);
  box-shadow: inset 0 0 0 1px var(--border); }
.rendu-jauge-rempli { position: absolute; left: 0; top: 0; bottom: 0; border-radius: 999px; background: var(--c);
  transition: width .3s ease; }
.rendu-jauge-objectif { position: absolute; top: -4px; bottom: -4px; width: 2px; margin-left: -1px;
  border-radius: 2px; background: var(--text); }
.rendu-jauge-echelle { position: relative; display: flex; justify-content: space-between; height: 15px;
  margin-top: 4px; font-size: 10.5px; color: var(--muted); font-variant-numeric: tabular-nums; }
/* La cible se lit SOUS les bornes de l'échelle : sur leur ligne,
   « objectif 80 % » chevauchait « 100 % » dans une carte étroite. Ancrée au
   bord qu'elle approche (`data-ancre`, cards.js), bornée à la place qui lui
   reste (`max-width`), elle ne sort jamais de la piste. */
.rendu-jauge-echelle:has(> .rendu-jauge-cible:not(:empty)) { height: 31px; }
.rendu-jauge-cible { position: absolute; top: 16px; transform: translateX(-50%); font-weight: 600;
  color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.rendu-jauge-cible[data-ancre="debut"] { transform: none; }
.rendu-jauge-cible[data-ancre="fin"] { transform: translateX(-100%); }

/* ── La cascade ── */
.rendu-cascade { margin-top: 6px; }
/* Les colonnes s'alignent par le HAUT : toutes les zones ont la même
   hauteur, un libellé sur deux lignes descend sans décaler sa barre. Le zéro
   court d'une colonne à l'autre, sans déborder aux deux bouts. */
.rendu-cascade-cols { display: flex; align-items: flex-start; gap: 10px; padding-top: 24px; overflow-x: auto; }
/* Une colonne n'est jamais plus étroite que son mot le plus long :
   à 52 px, « consommés » se coupait en deux sur un téléphone. Trop de pas
   pour la carte : la cascade défile dans la carte. */
.rendu-cascade-col { flex: 1 1 0; min-width: min-content; display: flex; flex-direction: column; }
.rendu-cascade-zone { position: relative; }
.rendu-cascade-zero { position: absolute; left: -5px; right: -5px; height: 0;
  border-top: 1.5px solid var(--border); }
.rendu-cascade-col:first-child .rendu-cascade-zero { left: 0; }
.rendu-cascade-col:last-child .rendu-cascade-zero { right: 0; }
.rendu-cascade-barre { position: absolute; left: 12%; right: 12%; border-radius: 7px; background: var(--c);
  overflow: hidden; }
.rendu-cascade-col[data-nature="bout"] .rendu-cascade-barre { left: 6%; right: 6%; }
.rendu-cascade-barre[data-forme="visee"] { background: repeating-linear-gradient(135deg,
  color-mix(in srgb, var(--c) 30%, transparent) 0 6px, color-mix(in srgb, var(--c) 14%, transparent) 6px 12px);
  box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--c) 70%, transparent); }
.rendu-cascade-acquis { position: absolute; left: 0; right: 0; bottom: 0; background: var(--c); }
.rendu-cascade-val { position: absolute; left: 0; right: 0; text-align: center; font-size: 12.5px;
  font-weight: 700; color: var(--text); font-variant-numeric: tabular-nums; white-space: nowrap; }
/* La valeur flotte au-dessus de sa barre : hors du flux, elle ne réservait
   aucune largeur, et deux valeurs voisines se chevauchaient dans une carte
   étroite. Son gabarit, invisible et sans hauteur, donne sa
   largeur à la colonne — trop de pas pour la carte : elle défile. */
.rendu-cascade-gabarit { display: block; height: 0; overflow: hidden; visibility: hidden; padding: 0 2px;
  font-size: 12.5px; font-weight: 700; font-variant-numeric: tabular-nums; white-space: nowrap; }
.rendu-cascade-lib { margin-top: 8px; text-align: center; font-size: 11.5px; font-weight: 600;
  color: var(--text); line-height: 1.3; overflow-wrap: break-word; }
.rendu-cascade-lib small { display: block; margin-top: 2px; font-size: 10.5px; font-weight: 500;
  color: var(--muted); }

/* ── La chaleur ── */
.rendu-chaleur-defile { overflow-x: auto; }
.rendu-chaleur { display: grid; grid-template-columns: minmax(90px, 140px) repeat(var(--n), minmax(64px, 1fr));
  gap: 6px; }
.rendu-chaleur-col { padding-bottom: 2px; text-align: center; font-size: 11px; font-weight: 600;
  color: var(--muted); }
.rendu-chaleur-ligne { display: flex; align-items: center; font-size: 13px; font-weight: 600; color: var(--text);
  overflow-wrap: anywhere; }
.rendu-chaleur-case { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: center;
  gap: 2px 7px; padding: 11px 8px; border-radius: 10px;
  background: color-mix(in srgb, var(--c) 16%, var(--panel));
  border: 1px solid color-mix(in srgb, var(--c) 34%, transparent); }
.rendu-chaleur-case b { font-size: 17px; font-weight: 700; color: var(--c); font-variant-numeric: tabular-nums; }
.rendu-chaleur-case small { font-size: 11px; color: var(--muted); }
.rendu-chaleur-case[data-vide] { align-items: center; font-size: 11px; color: var(--muted);
  background: var(--panel2); border: 1px dashed var(--border); }

/* ── Le classement ── */
.rendu-classement { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 9px; }
.rendu-classement li { display: flex; gap: 10px; align-items: flex-start; }
.rendu-classement-rang { flex: none; width: 20px; padding-top: 2px; text-align: right; font-size: 11px;
  font-weight: 700; color: var(--muted); font-variant-numeric: tabular-nums; }
.rendu-classement-corps { flex: 1 1 auto; min-width: 0; }
.rendu-classement-haut { display: flex; align-items: center; gap: 8px; }
.rendu-classement-lib { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; font-size: 12.5px; font-weight: 500; color: var(--text); }
.rendu-classement-haut b { font-size: 12.5px; font-weight: 700; color: var(--text);
  font-variant-numeric: tabular-nums; white-space: nowrap; }
.rendu-classement-piste { height: 6px; margin-top: 4px; border-radius: 999px; background: var(--panel2);
  overflow: hidden; }
.rendu-classement-piste span { display: block; height: 100%; border-radius: 999px; background: var(--c);
  transition: width .3s ease; }
.rendu-classement-detail { margin-top: 3px; font-size: 11px; color: var(--muted); }

/* ── Le compte de résultat ── */
.rendu-cr-tete { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: 8px 16px; margin-bottom: 12px; }
.rendu-cr-resume { font-size: 13px; font-weight: 600; color: var(--text); font-variant-numeric: tabular-nums; }
.rendu-cr-ecartes { color: var(--warn); }   /* un mois hors des totaux : la lecture doit le voir */
.rendu-cr-choix { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 6px 8px; }
.rendu-cr-lab { font-size: 12px; font-weight: 600; color: var(--muted); }
.rendu-cr-defile { overflow-x: auto; }
.rendu-cr-table { width: 100%; border-collapse: separate; border-spacing: 0; font-size: 13px;
  font-variant-numeric: tabular-nums; }
.rendu-cr-table th { position: static; padding: 9px 12px; text-align: right; font-size: 12px; font-weight: 500;
  color: var(--muted); white-space: nowrap; background: transparent; border-bottom: 1px solid var(--border); }
.rendu-cr-table th:first-child, .rendu-cr-table td:first-child { text-align: left; }
.rendu-cr-table th small { display: block; font-size: 10px; font-weight: 500; color: var(--muted); }
.rendu-cr-table td { padding: 9px 12px; text-align: right; white-space: nowrap; color: var(--text);
  border-bottom: 1px solid var(--separateur, var(--border)); }
.rendu-cr-table td:first-child small { margin-left: 6px; font-size: 10.5px; font-weight: 500; color: var(--muted); }
.rendu-cr-table .rendu-cr-repere { text-align: left; }
.rendu-cr-part { color: var(--texte-2, var(--muted)); }
.rendu-cr-table [data-prevu] { font-style: italic;
  background: color-mix(in srgb, var(--bleu, var(--accent)) 6%, transparent); }
.rendu-cr-table tr[data-style="produit"] td { font-weight: 700; }
.rendu-cr-table tr[data-style="dont"] td { font-size: 12px; color: var(--muted); }
.rendu-cr-table tr[data-style="dont"] td:first-child { padding-left: 26px; }
.rendu-cr-table tr[data-style="groupe"] td { padding-top: 14px; font-size: 11.5px; font-weight: 600;
  color: var(--muted); border-bottom: 0; }
.rendu-cr-table tr[data-style="sous_total"] td { font-weight: 650; background: var(--panel2); }
.rendu-cr-table tr[data-style="resultat"] td { font-size: 14px; font-weight: 700;
  border-top: 1px solid var(--border); background: var(--accent-fond, var(--panel2)); }
.rendu-cr-table tr[data-style="sous_total"] td:first-child,
.rendu-cr-table tr[data-style="resultat"] td:first-child { border-radius: 8px 0 0 8px; }
.rendu-cr-table tr[data-style="sous_total"] td:last-child,
.rendu-cr-table tr[data-style="resultat"] td:last-child { border-radius: 0 8px 8px 0; }
.rendu-cr-table [data-signe="pos"] { color: var(--good); }
.rendu-cr-table [data-signe="neg"] { color: var(--bad); }

@media (prefers-reduced-motion: reduce) {
  .rendu-jauge-rempli, .rendu-classement-piste span, .rendu-segment button { transition: none; }
}
