/* v0.14.0 — UI moderne high-tech (palette electric violet + cyan + deep space)
   Theme switch via [data-theme="light"] sur <html>. Default = dark. */
:root, :root[data-theme="dark"] {
  --bg-0: #050714;
  /* Marque HeXaControl. Le X vient du logo Foxchip IA ; ces deux valeurs
     sont mesurees au pixel sur son favicon et ne doivent pas etre
     approximees ailleurs — d'ou les jetons. */
  --brand-x: #4FD1FF;
  --brand-navy: #0B223A;
  --bg-1: #0a0f24;
  --bg-2: #0f1530;
  --surface: rgba(255, 255, 255, 0.035);
  --surface-2: rgba(255, 255, 255, 0.07);
  --surface-3: rgba(255, 255, 255, 0.11);

  /* === Echelle z-index (revue 01/06) — hierarchie UNIQUE et documentee. ===
     Tout nouveau z-index DOIT reutiliser une de ces variables (fini 50 vs 1200
     vs 9999 vs 999999 au hasard). REGLE D'OR : un popover/menu qui descend SOUS
     un conteneur a backdrop-filter (cartes, topbar, widgets) est PIEGE par le
     stacking context de ce backdrop-filter quelle que soit sa valeur z-index —
     il faut le portaliser vers <body> en position:fixed via portalPopover()
     (common.js). Les valeurs existantes migreront progressivement vers ces vars. */
  --topbar-h: 88px;    /* hauteur topbar fixe (surchargee a 64px en mobile) */
  --z-base: 1;
  --z-sticky: 30;      /* en-tetes collants, resize handles */
  --z-topbar: 50;      /* barre de navigation fixe */
  --z-dropdown: 1000;  /* menus/popovers ancres dans le flow */
  --z-overlay: 8000;   /* voiles de drawer */
  --z-modal: 9000;     /* modales, FAB */
  --z-toast: 9500;     /* avis fixes des pages (le toast de la console passe au-dessus des fenêtres : .toast) */
  --z-portal: 999999;  /* popovers portalises vers <body> (au-dessus de tout) */

  /* === Tokens semantiques de couleur (revue 01/06) — mapper les hex repandus
     (#dc2626 / #f59e0b / #16a34a / #3b82f6, 323+ occurrences hardcodees) vers
     ces variables pour unifier la palette et le theme clair/sombre. Migration
     progressive ; lint stylelint a brancher en CI. */
  --color-crit: #dc2626;  --color-crit-soft: rgba(220, 38, 38, 0.12);
  --color-warn: #f59e0b;  --color-warn-soft: rgba(245, 158, 11, 0.12);
  --color-ok:   #16a34a;  --color-ok-soft:   rgba(22, 163, 74, 0.12);
  --color-info: #3b82f6;  --color-info-soft: rgba(59, 130, 246, 0.12);
  /* Bandeau plein « attention » (« Quelqu'un attend ») : l'orange d'avant le lot L00, le meme dans les deux themes. */
  --color-attention: #ea580c;  --color-attention-fonce: #c2410c;  --on-attention: #ffffff;
  --on-attention-voile: rgba(255, 255, 255, 0.16);  --on-attention-voile-fort: rgba(255, 255, 255, 0.26);  --on-attention-filet: rgba(255, 255, 255, 0.4);
  /* Couleurs de module (12/10/2026, Jérôme : « on a perdu toutes les couleurs ») : RMM = l'accent (le bleu de la
     marque depuis le 27/08 ; une marque blanche le repeint), PSA ambre, DOC vert.
     --mod-*       : les APLATS et les filets -- liseré de la barre, bouton du module actif et son trait ;
     --mod-*-texte : libellés et icônes de la barre et de ses menus, au moins 4,5:1 sur la barre ;
     --on-mod-*    : le texte posé SUR l'aplat. Le thème clair ne fonce que les -texte (bloc clair). */
  --mod-rmm:  var(--accent);  --mod-rmm-texte:  var(--accent);  --on-mod-rmm:  var(--accent-on, #ffffff);
  --mod-psa:  #f59e0b;        --mod-psa-texte:  #f59e0b;        --on-mod-psa:  #1a1206;
  --mod-glue: #16a34a;        --mod-glue-texte: #16a34a;        --on-mod-glue: #04180b;
  /* Teintes des icônes de la barre du haut (12/10/2026, 15:20 ; Jérôme, capture du module DOC à l'appui : « on avait
     des icônes de couleurs avant et c'était mieux ! »). Chaque entrée de la barre, de ses menus et du tiroir, et chaque
     outil de droite, a SA teinte, posée par data-teinte (index.php, app.js, noms-bascule.js) : celle de l'emoji d'avant
     le 05/08 quand il était coloré, sinon une teinte qui s'accorde ; deux voisines toujours bien distinctes (écart
     CIEDE2000 d'au moins 15, banc tests/bench_barre_couleurs_rendu.js). Au moins 3:1 sur la barre, ses menus, le
     tiroir, les survols et le voile de l'onglet actif ; le thème clair les fonce (bloc clair). Des repères, pas des
     états : compteurs et gravité gardent --rollup-* et --color-*. */
  --ico-bleu:   #0091ea;  --ico-ciel:    #22c3ee;  --ico-sarcelle: #2dd4bf;  --ico-vert:    #04aa46;
  --ico-lime:   #b2cf18;  --ico-jaune:   #fad04e;  --ico-orange:   #f29657;  --ico-rouge:   #f44838;
  --ico-corail: #d06b68;  --ico-rose:    #fcacac;  --ico-magenta:  #d946ef;  --ico-violet:  #a78bfa;
  --ico-indigo: #8c9eff;  --ico-ardoise: #90a4ae;  --ico-acier:    #83afc1;  --ico-brun:    #d4a373;
  /* 05/10/2026 (revue Gestion NAS) : fond des pastilles de compteur du menu. Le blanc sur l'ambre
     #ca8a04 ne faisait que 2,9:1 (seuil 4,5:1) ; #b45309 donne 5,0:1, #dc2626 4,8:1. */
  --rollup-crit: #dc2626;
  --rollup-warn: #b45309;
  --border: rgba(148, 163, 220, 0.10);
  --border-strong: rgba(148, 163, 220, 0.22);
  --text: #f1f5fc;
  --muted: #97a3c4;
  --dim: #8290b4;

  /* Accents : violet electric → cyan, plus moderne que le bleu basique */
  /* Accent d'interface HeXaControl. #1B8FC7 est le cyan du signe DESCENDU
     jusqu'a tenir en aplat : blanc sur cette valeur donne 4,6 pour 1, au-dessus
     du seuil de 4,5. Le cyan de marque brut (#4FD1FF) tombe, lui, a 1,8 — d'ou
     la charte : « ce n'est pas une couleur d'aplat ».
     --accent   : les APLATS (boutons, pastilles, barres de progression)
     --accent-2 : ce qui n'est PAS un aplat (liens, onglets, survols) — c'est
                  la le cyan de marque, qui garde tout son eclat. */
  --accent: #1B8FC7;
  --accent-2: #4FD1FF;
  --accent-cyan: #4FD1FF;  /* aligne sur le cyan de marque : un seul cyan */
  /* Triplets numeriques : ils permettent d'ecrire rgba(var(--accent-rgb), .15)
     au lieu de rgba(var(--accent-rgb), .15). Sans eux, 203 transparences restaient
     bleues quand --accent changeait — une marque blanche qui ne repeint que la
     moitie de l'ecran est pire que pas de marque blanche.
     Meme mecanisme que --w-accent-rgb, deja en place pour les widgets. */
  --accent-rgb: 27, 143, 199;
  --accent-2-rgb: 79, 209, 255;
  --brand-navy-rgb: 11, 34, 58;
  --accent-glow: rgba(var(--accent-rgb), 0.45);
  /* 25/10/2026 (revue R24-02) : le dégradé se calcule sur les jetons que brand_css() réécrit (--accent, --accent-cyan).
     Écrit en hexadécimal, il restait au bleu de HeXaControl sous une marque blanche : boutons « primary », trait de la
     page de connexion. Pour la marque du produit, le milieu passe de #14719E à l'accent assombri d'un cinquième
     (#16729F) et la fin de #22d3ee au cyan de marque (#4FD1FF : un seul cyan, voir --accent-cyan). Navigateur sans
     color-mix() : repli juste après le bloc du thème clair. Les voiles de la feuille suivent la même règle : l'ancien
     cyan rgba(34,211,238) y devient rgba(var(--accent-2-rgb)), le cyan de marque en sombre, et les bleus #3b82f6 qui
     tenaient lieu d'accent (sélection du sélecteur de client, onglet actif des Paramètres, bulle du technicien dans
     HeXaIA) prennent l'accent. */
  --gradient-primary: linear-gradient(135deg, var(--accent) 0%, color-mix(in srgb, var(--accent) 80%, #000) 50%, var(--accent-cyan) 100%);
  --gradient-card: linear-gradient(135deg, rgba(var(--accent-rgb),0.06), rgba(var(--accent-2-rgb),0.04));

  --green: #22c55e;
  --green-glow: rgba(34, 197, 94, 0.4);
  --red: #ef4444;
  --red-glow: rgba(239, 68, 68, 0.4);
  --amber: #f59e0b;
  --amber-glow: rgba(245, 158, 11, 0.4);

  /* v0.15.2 — Palette d'etats KPI/widget : ok / warn / crit / info / neutral.
     Utilisee via [data-state="..."] sur les .stat / .dash-comp-card. */
  --state-ok:        #10b981;
  --state-ok-soft:   rgba(16,185,129,0.10);
  --state-ok-glow:   rgba(16,185,129,0.35);
  --state-warn:      #f59e0b;
  --state-warn-soft: rgba(245,158,11,0.10);
  --state-warn-glow: rgba(245,158,11,0.35);
  --state-crit:      #ef4444;
  --state-crit-soft: rgba(239,68,68,0.10);
  --state-crit-glow: rgba(239,68,68,0.40);
  --state-info:      #3b82f6;
  --state-info-soft: rgba(59,130,246,0.10);
  --state-info-glow: rgba(59,130,246,0.35);
  /* 05/10/2026 (revue Gestion NAS) : #64748b ne faisait que 3,6:1 sur les fonds sombres ; #7c8aa3, 5,2:1. */
  --state-neutral:      #7c8aa3;
  --state-neutral-soft: rgba(100,116,139,0.10);
  --state-neutral-glow: rgba(100,116,139,0.25);

  --radius-sm: 6px;
  --radius: 10px;
  --radius-lg: 14px;

  /* v0.15 (harmonisation 02/06) — Échelle d'espacement. Comblait le seul trou des
     échelles : avant, tous les gap/padding/margin étaient en dur. À utiliser via
     var(--space-*) dans les nouveaux composants et les migrations. */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 20px;
  --space-6: 24px;

  /* v0.13.30 (2026-05-14) — Tokens font-size pour uniformiser (20 valeurs distinctes
     trouvees dans app.js inline-styles). À utiliser via var(--fs-*) dans les nouveaux composants. */
  --fs-xs:   0.72rem;  /* badges, hint, secondary muted */
  --fs-sm:   0.82rem;  /* labels, table cells, descriptions */
  --fs-base: 0.95rem;  /* body text */
  --fs-md:   1rem;     /* boutons primaires, h4 */
  --fs-lg:   1.15rem;  /* h3 */
  --fs-xl:   1.5rem;   /* h2 */
  --fs-kpi:  4.5rem;   /* chiffre des tuiles d'indicateurs du tableau de bord PSA (23/10/2026) */

  /* v0.13.30 — Alias semantiques pour les hex hardcodes (323 occurrences) */
  --color-text-soft: #fca5a5;  /* utilise avec fond rouge transparent (badges critiques) */
  --color-accent-soft-bg: rgba(var(--accent-rgb),0.18);

  /* ── Surfaces qui DOIVENT suivre le theme ───────────────────────────────
     Ces valeurs etaient ecrites en dur un peu partout (champs de saisie,
     popovers, voiles, blocs de code...). Ecrites ici une seule fois, elles se
     redefinissent dans le bloc clair et tout suit — plus besoin d'ajouter une
     surcharge !important a chaque composant. */
  --input-bg:       rgba(10,15,36,0.6);
  --input-bg-focus: rgba(10,15,36,0.85);
  --popover-bg:     rgba(13,19,38,0.97);
  --popover-inset:  rgba(0,0,0,0.18);
  --overlay-bg:     rgba(2,6,23,0.55);
  --code-bg:        #020618;
  --code-text:      #cbd5e1;
  --menu-bg:        #1e293b;
  --toast-bg:       linear-gradient(135deg, rgba(15,21,48,0.92), rgba(10,15,36,0.92));
  --switcher-bg:    rgba(0, 0, 0, .28);

  /* Barre de defilement — 02/09/2026.
     Le curseur valait rgba(var(--accent-rgb),0.18) : 18 % d un bleu moyen sur un
     fond #050714 presque noir, soit un contraste de 1.24:1. Invisible, constate
     par Jerome. L accent est trop sombre pour ce role : atteindre 3:1 aurait
     demande un alpha de 0.91, donc une couleur quasi opaque.
     Mesure : blanc a 0.35 sur --bg-0 donne 3.10:1, le seuil des elements non
     textuels. Le theme clair etait PIRE (1.25:1) sans que personne le signale. */
  --scrollbar-thumb:       rgba(255, 255, 255, 0.35);
  --scrollbar-thumb-hover: rgba(255, 255, 255, 0.52);

  --scheme: dark;
}

/* Light theme — meme identite violet/cyan mais sur fond clair */
:root[data-theme="light"] {
  /* Mesure : un neutre fonce a 0.47 sur #f6f7fb donne 3.07:1. */
  --scrollbar-thumb:       rgba(15, 23, 42, 0.47);
  --scrollbar-thumb-hover: rgba(15, 23, 42, 0.62);
  --bg-0: #f6f7fb;
  --bg-1: #eef0f8;
  --bg-2: #ffffff;
  --surface: rgba(255, 255, 255, 0.85);
  --surface-2: rgba(241, 244, 252, 0.92);
  --surface-3: rgba(231, 236, 248, 0.95);
  --border: rgba(var(--accent-rgb), 0.12);
  --border-strong: rgba(var(--accent-rgb), 0.22);
  --text: #0f172a;
  --muted: #475569;
  /* --dim du thème clair : au socle (« SOCLE VISUEL HeXaControl », :root[data-theme="light"], #56657f depuis L17).
     25/10/2026 (revue R14-14) : le #5a6a85 écrit ici, que le socle écrasait toujours, est retiré. */

  /* Theme clair : meme accent, mais le survol FONCE au lieu de s'eclaircir —
     un #4FD1FF sur blanc ne se verrait plus. */
  --accent: #14719E;
  --accent-2: #126488;
  --accent-cyan: #1B8FC7;
  /* ⚠ --accent est le meme dans les deux themes, --accent-2 non : le cyan
     clair du theme sombre disparaitrait sur du blanc. Le triplet doit donc
     etre redeclare ici, sinon le theme clair herite du cyan. */
  --accent-rgb: 20, 113, 158;
  --accent-2-rgb: 18, 100, 136;
  --brand-navy-rgb: 11, 34, 58;
  --accent-glow: rgba(var(--accent-rgb), 0.18);
  /* 25/10/2026 (revue R24-02) : sur les jetons, comme dans le thème sombre ; pour la marque du produit, mêmes arrêts
     (#14719E, #126488), la fin à #0F5574 au lieu de #0F5674. */
  --gradient-primary: linear-gradient(135deg, var(--accent) 0%, var(--accent-2) 50%, color-mix(in srgb, var(--accent-2) 85%, #000) 100%);
  --gradient-card: linear-gradient(135deg, rgba(var(--accent-rgb),0.04), rgba(var(--accent-rgb),0.03));

  --green: #16a34a;
  --green-glow: rgba(22, 163, 74, 0.18);
  --red: #dc2626;
  --red-glow: rgba(220, 38, 38, 0.18);
  --amber: #d97706;
  --amber-glow: rgba(217, 119, 6, 0.18);

  /* Couleurs d'état lisibles sur fond clair (AA sur blanc) — sobres, pas de fantaisie.
     Sans ça, les --state-* du thème sombre (ambre/vert vifs) sont illisibles sur blanc. */
  --state-ok:   #047857;
  --state-warn: #a94d08;
  --state-crit: #b91c1c;
  --state-info: #1d4ed8;

  /* Les --color-* et les variantes -soft/-neutral n'avaient PAS d'equivalent
     clair : les composants qui s'en servent (badges, boutons danger, puces
     d'etat) restaient calibres pour le fond sombre. */
  --color-crit: #b91c1c;  --color-crit-soft: rgba(185,28,28,0.10);
  --color-warn: #a94d08;  --color-warn-soft: rgba(169,77,8,0.10);
  --color-ok:   #047857;  --color-ok-soft:   rgba(4,120,87,0.10);
  --color-info: #1d4ed8;  --color-info-soft: rgba(29,78,216,0.10);
  /* Bandeau plein « attention » (« Quelqu'un attend ») : l'orange d'avant le lot L00, le meme dans les deux themes. */
  --color-attention: #ea580c;  --color-attention-fonce: #c2410c;  --on-attention: #ffffff;
  --on-attention-voile: rgba(255, 255, 255, 0.16);  --on-attention-voile-fort: rgba(255, 255, 255, 0.26);  --on-attention-filet: rgba(255, 255, 255, 0.4);
  /* Couleurs de module, thème clair : les aplats ne changent pas ; libellés et icônes foncent d'un cran pour tenir
     4,5:1 sur la barre blanche ET sur le voile de l'onglet actif (l'accent #14719E n'y tient que 4,1:1).
     PSA : #9c4707 (vérification du 12/10) -- #a94d08 tombait à 4,36:1 sur le pire arrêt du voile de l'entrée active
     du tiroir (360 px) et à 4,41:1 sous le voile de survol du sélecteur ; 4,9:1 et 5,0:1 avec #9c4707. */
  --mod-rmm-texte: var(--accent-2);  --mod-psa-texte: #9c4707;  --mod-glue-texte: #166534;
  /* Teintes des icônes de la barre, thème clair : les mêmes teintes, foncées pour tenir 3:1 sur la barre blanche, les
     menus, le tiroir et le voile de l'onglet actif (le plus sombre : celui du RMM dans le tiroir). */
  --ico-bleu:   #0277bd;  --ico-ciel:    #0e7490;  --ico-sarcelle: #00796b;  --ico-vert:    #15803d;
  --ico-lime:   #4d7c0f;  --ico-jaune:   #a16207;  --ico-orange:   #c2410c;  --ico-rouge:   #c62828;
  --ico-corail: #b4443f;  --ico-rose:    #ad1457;  --ico-magenta:  #a21caf;  --ico-violet:  #6d28d9;
  --ico-indigo: #3949ab;  --ico-ardoise: #546e7a;  --ico-acier:    #37718a;  --ico-brun:    #8d5524;
  --color-text-soft: #b91c1c;
  --color-accent-soft-bg: rgba(var(--accent-rgb),0.12);

  --state-ok-soft:   rgba(4,120,87,0.10);    --state-ok-glow:   rgba(4,120,87,0.18);
  --state-warn-soft: rgba(169,77,8,0.10);    --state-warn-glow: rgba(169,77,8,0.18);
  --state-crit-soft: rgba(185,28,28,0.10);   --state-crit-glow: rgba(185,28,28,0.18);
  --state-info-soft: rgba(29,78,216,0.10);   --state-info-glow: rgba(29,78,216,0.18);
  --state-neutral: #475569;
  --state-neutral-soft: rgba(71,85,105,0.10); --state-neutral-glow: rgba(71,85,105,0.15);

  /* Surfaces : les memes roles, en clair. */
  --input-bg:       #ffffff;
  --input-bg-focus: #ffffff;
  --popover-bg:     rgba(255,255,255,0.98);
  --popover-inset:  rgba(15,23,42,0.04);
  --overlay-bg:     rgba(15,23,42,0.35);
  --code-bg:        #f1f5f9;
  --code-text:      #334155;
  --menu-bg:        #ffffff;
  --toast-bg:       linear-gradient(135deg, #ffffff, #f8fafc);
  --switcher-bg:    rgba(15,23,42,0.07);

  --scheme: light;
}

/* 25/10/2026 (revue R24-02) : navigateur sans color-mix() (Chrome et Edge 109 sous Windows 7 et 8.1, Safari avant
   16.2, Firefox avant 113). Sans ce repli, --gradient-primary y devenait invalide et le fond des boutons « primary »
   et du trait de la page de connexion tombait à transparent sous leur texte. Mêmes jetons, sans mélange ;
   même condition que le repli du socle. */
@supports not (color: color-mix(in srgb, currentColor 50%, transparent)) {
  :root, :root[data-theme="dark"] { --gradient-primary: linear-gradient(135deg, var(--accent) 0%, var(--accent) 50%, var(--accent-cyan) 100%); }
  :root[data-theme="light"] { --gradient-primary: linear-gradient(135deg, var(--accent) 0%, var(--accent-2) 100%); }
}

:root[data-theme="light"] body {
  background:
    linear-gradient(rgba(var(--accent-rgb),0.04) 1px, transparent 1px) 0 0 / 60px 60px,
    linear-gradient(90deg, rgba(var(--accent-rgb),0.04) 1px, transparent 1px) 0 0 / 60px 60px,
    radial-gradient(circle at 15% -5%, rgba(var(--accent-rgb),0.10), transparent 45%),
    radial-gradient(circle at 85% 100%, rgba(var(--accent-rgb),0.08), transparent 45%),
    var(--bg-0) !important;
}
:root[data-theme="light"] select option { background-color: #ffffff; color: var(--text); }
:root[data-theme="light"] ::selection { background: rgba(var(--accent-rgb),0.25); color: #0f172a; }
:root[data-theme="light"] .topbar,
:root[data-theme="light"] .sidebar,
:root[data-theme="light"] .card { background: rgba(255,255,255,0.85) !important; }

/* Fix theme clair (16/06) : les items de menu ACTIFS avaient color:#fff hardcode,
   invisible sur le surlignage translucide quand le fond passe en clair. Texte
   violet fonce = lisible sur le highlight clair, et distingue bien l'item actif.
   12/10/2026 : dans la barre du haut, le texte prend le ton du module ouvert (--mod-ton-texte, au moins 4,5:1
   sur le voile de l'onglet actif) ; la barre laterale garde son marine. */
:root[data-theme="light"] .nav-top a.active,
:root[data-theme="light"] .nav-dd-trigger.active {
  color: var(--mod-ton-texte) !important;
}
:root[data-theme="light"] .nav-side a.active {
  color: #0B223A !important;
}
:root[data-theme="light"] .topbar { background: linear-gradient(90deg, rgba(255,255,255,0.98), rgba(250,251,255,0.97), rgba(255,255,255,0.98)) !important; background-image: linear-gradient(90deg, rgba(255,255,255,0.98), rgba(250,251,255,0.97), rgba(255,255,255,0.98)), linear-gradient(90deg, transparent 0%, var(--accent) 30%, var(--accent-cyan) 50%, var(--accent) 70%, transparent 100%) !important; }

/* v0.13.86 (28/05) : fix widgets light mode — fond sombre hardcode non override.
   Le selector .widget definissait background rgba(20,25,50,...) calibre dark mode,
   donc les widgets restaient sombres en light avec texte clair illisible.
   Override : fond clair + orb glow attenue + texte sombre. */
:root[data-theme="light"] .widget {
  background:
    radial-gradient(ellipse at 100% 0%, rgba(var(--w-accent-rgb), 0.12) 0%, transparent 55%),
    radial-gradient(ellipse at 0% 100%, rgba(var(--w-accent-rgb), 0.06) 0%, transparent 50%),
    linear-gradient(180deg, rgba(255,255,255,0.97), rgba(248,250,255,0.94)) !important;
  border: 1px solid rgba(var(--w-accent-rgb), 0.25) !important;
  box-shadow:
    0 4px 16px -4px rgba(15, 23, 42, 0.08),
    0 0 0 1px rgba(var(--w-accent-rgb), 0.05),
    inset 0 1px 0 rgba(255,255,255,0.6) !important;
}
:root[data-theme="light"] .widget::after {
  opacity: 0.45;
}
:root[data-theme="light"] .widget-clickable:hover {
  box-shadow:
    0 18px 50px -10px rgba(var(--w-accent-rgb), 0.30),
    0 6px 20px -4px rgba(15, 23, 42, 0.12),
    0 0 0 1px rgba(var(--w-accent-rgb), 0.30),
    inset 0 1px 0 rgba(255,255,255,0.7) !important;
}

* { margin: 0; padding: 0; box-sizing: border-box; }
html, body { height: 100%; }
body {
  font-family: 'Inter', 'Segoe UI', system-ui, sans-serif;
  background:
    /* Subtle grid pattern for "tech" feel */
    linear-gradient(rgba(var(--accent-rgb),0.025) 1px, transparent 1px) 0 0 / 60px 60px,
    linear-gradient(90deg, rgba(var(--accent-rgb),0.025) 1px, transparent 1px) 0 0 / 60px 60px,
    /* Gradient orbs */
    radial-gradient(circle at 15% -5%, rgba(var(--accent-rgb),0.18), transparent 45%),
    radial-gradient(circle at 85% 100%, rgba(var(--accent-2-rgb),0.12), transparent 45%),
    radial-gradient(circle at 50% 50%, rgba(91,111,255,0.08), transparent 60%),
    var(--bg-0);
  background-attachment: fixed;
  color: var(--text);
  min-height: 100vh;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* v0.14.5 — Filter drawer slide-in style Datto RMM */
.filter-drawer-overlay {
  position: fixed; inset: 0;
  background: var(--overlay-bg);
  backdrop-filter: blur(2px);
  z-index: 8999;
  opacity: 0; pointer-events: none;
  transition: opacity 0.2s ease-out;
}
.filter-drawer-overlay.open { opacity: 1; pointer-events: auto; }
.filter-drawer {
  position: fixed; top: 0; right: 0;
  width: 480px; max-width: 100vw;
  height: 100vh;
  background: var(--bg-1);
  border-left: 1px solid var(--border-strong);
  box-shadow: -10px 0 32px rgba(0,0,0,0.55);
  z-index: 9000;
  transform: translateX(100%);
  transition: transform 0.25s ease-out;
  display: flex; flex-direction: column;
}
.filter-drawer.open { transform: translateX(0); }
.filter-drawer .fd-head {
  padding: 16px 20px;
  border-bottom: 1px solid var(--border);
  display: flex; align-items: center; justify-content: space-between;
}
.filter-drawer .fd-body { flex:1; overflow-y:auto; padding:16px 20px; }
.filter-drawer .fd-foot {
  padding: 12px 20px;
  border-top: 1px solid var(--border);
  display: flex; align-items: center; gap: 10px; justify-content: space-between;
  background: var(--surface);
}

/* Animations globales - utiliser .fadeIn pour les nouvelles pages */
@keyframes fadeIn {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: translateY(0); }
}
@keyframes pulseGlow {
  0%, 100% { box-shadow: 0 0 0 0 var(--accent-glow); }
  50%      { box-shadow: 0 0 0 8px transparent; }
}
@keyframes shimmer {
  0%   { background-position: -1000px 0; }
  100% { background-position: 1000px 0; }
}
main > * { animation: fadeIn .25s ease both; }
main > *:nth-child(2) { animation-delay: .04s; }
main > *:nth-child(3) { animation-delay: .08s; }
main > *:nth-child(4) { animation-delay: .12s; }
main > *:nth-child(5) { animation-delay: .16s; }

/* Custom scrollbar */
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: var(--scrollbar-thumb);
  border-radius: 10px;
  border: 2px solid transparent;
  background-clip: padding-box;
}
::-webkit-scrollbar-thumb:hover { background: var(--scrollbar-thumb-hover); background-clip: padding-box; border: 2px solid transparent; }
/* Firefox n applique PAS ::-webkit-scrollbar : sans ces deux lignes il gardait la
   barre par defaut du systeme, donc une apparence differente de Chrome et Edge. */
html { scrollbar-color: var(--scrollbar-thumb) transparent; scrollbar-width: thin; }
::selection { background: rgba(var(--accent-rgb),0.35); color: #fff; }

a { color: inherit; text-decoration: none; }
button, input, textarea, select { font-family: inherit; font-size: 0.9rem; color: var(--text); }
/* Force la lisibilité des <option> dans les dropdowns natifs (Windows par défaut
   les rend en blanc-sur-blanc sur fond sombre, ce qui les rend invisibles). */
select option { background-color: var(--menu-bg); color: var(--text); }
select option:checked, select option:hover { background-color: var(--surface-3); color: var(--accent); }

/* Indicateur calendrier des champs date/mois — l'icône native (noire ou blanche)
   disparaissait sur le thème. On la teinte en rouge pour qu'elle reste visible
   quel que soit le fond.
   25/10/2026 (revue DES1-13) : en gris, plus en rouge (le rouge dit « ça va mal », pas « calendrier »). invert(50%)
   ramène chaque point de l'icône, quelle que soit sa couleur native, au gris du milieu : lisible sur un champ sombre
   (4,7:1) comme sur un champ blanc (4,0:1). Il vaut aussi pour date, date et heure, heure : leur règle v0.21.4, plus
   bas, n'a plus de filtre propre (son invert(1) rendait l'icône blanche sur le champ blanc du thème clair). Elle garde
   son opacité de 0,85 : 3,7:1 et 3,1:1, au-dessus des 3:1 d'une icône. */
input[type="month"]::-webkit-calendar-picker-indicator,
input[type="date"]::-webkit-calendar-picker-indicator,
input[type="datetime-local"]::-webkit-calendar-picker-indicator,
input[type="time"]::-webkit-calendar-picker-indicator,
input[type="week"]::-webkit-calendar-picker-indicator {
  cursor: pointer;
  filter: invert(50%);
}

/* Topbar — FIXÉE en haut. v0.13.2 : glassmorphism + gradient border bottom */
.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 28px;
  background: linear-gradient(90deg, rgb(10,15,36), rgb(15,21,48), rgb(10,15,36));
  /* Gradient border-bottom : pseudo via box-shadow + image */
  background-image:
    linear-gradient(90deg, rgb(10,15,36), rgb(15,21,48), rgb(10,15,36)),
    linear-gradient(90deg, transparent 0%, var(--accent) 30%, var(--accent-cyan) 50%, var(--accent) 70%, transparent 100%);
  background-size: 100% calc(100% - 1px), 100% 1px;
  background-position: 0 0, 0 100%;
  background-repeat: no-repeat;
  backdrop-filter: blur(20px) saturate(140%);
  -webkit-backdrop-filter: blur(20px) saturate(140%);
  position: fixed; top: 0; left: 0; right: 0; z-index: 50;
  min-height: var(--topbar-h); height: auto;
  box-shadow: 0 4px 24px -8px rgba(0,0,0,0.6);
}
/* Compense la hauteur de la topbar pour que le contenu commence en dessous */
body { padding-top: var(--topbar-h); }
.brand { display: flex; align-items: center; color: inherit; }
.brand-logo {
  width: auto; display: block;
  filter: drop-shadow(0 0 4px rgba(0,0,0,0.4));
}
/* v0.15 — Indicateur visuel du mode actif via une bordure inferieure
   coloree de la topbar. Bleu = RMM (l'accent), Orange = PSA, Vert = DOC (v0.21.5).
   12/10/2026 : le ton du module ouvert est un jeton posé sur <body> d'après data-module -- --mod-ton (aplat,
   filet) et --mod-ton-texte (libellés, icônes) ; la barre et ses menus le lisent : liseré, onglet actif,
   icône d'une entrée sans teinte attitrée (data-teinte : chaque entrée a la sienne, voir « Icones de navigation », plus bas).
   Valeurs : --mod-* en tête de feuille, dans les deux thèmes. Le liseré garde ses couleurs (en thème clair,
   celui du RMM prend l'accent du thème, #14719E, comme hors session ; le halo du DOC passe de 20 à 18 %). */
.topbar { border-bottom: 3px solid transparent; transition: border-color .3s ease, box-shadow .3s ease; }
body                     { --mod-ton: var(--mod-rmm);  --mod-ton-texte: var(--mod-rmm-texte); }
body[data-module="psa"]  { --mod-ton: var(--mod-psa);  --mod-ton-texte: var(--mod-psa-texte); }
body[data-module="glue"] { --mod-ton: var(--mod-glue); --mod-ton-texte: var(--mod-glue-texte); }
/* ⚠ Hors session : le bleu de la marque, pas la couleur d'un module. Le liseret
   restait ORANGE sur la page de connexion parce que data-module gardait le
   dernier module ouvert avant la fin de la session.
   Cette regle a EXACTEMENT la meme specificite que les deux ci-dessus —
   (0,1,1) de part et d'autre : body + une classe ou un attribut.
   C'est donc sa POSITION, apres elles, qui la fait gagner. La deplacer plus
   haut la rendrait sans effet, en silence. */
body.hors-session        { --mod-ton: var(--accent);   --mod-ton-texte: var(--accent); }
body[data-module] .topbar, body.hors-session .topbar {
  border-bottom-color: var(--mod-ton); box-shadow: 0 2px 8px color-mix(in srgb, var(--mod-ton) 18%, transparent);
}
/* Logo : effet hover pour signaler qu'il est cliquable. */
.brand:hover .brand-logo { filter: drop-shadow(0 0 8px rgba(255,255,255,0.6)); transform: scale(1.03); }
.brand-logo { transition: transform .15s ease, filter .15s ease; }
/* v0.22 — Switcher 3 boutons RMM/PSA/DOC cote a cote sous le logo.
   Chaque bouton garde sa couleur (bleu/orange/vert) en permanence.
   L'actif est distingue par un trait colore sous le bouton.
   12/10/2026 : ces couleurs sont les jetons --mod-* (tête de feuille), plus des dégradés écrits ici. Le libellé
   d'un module inactif était en --muted sur un dégradé voilé à 55 % : PSA et DOC ne se lisaient plus (1,2:1 en
   sombre). Le segmenté (plus bas) pose le libellé, l'aplat de l'actif et le focus. */
.brand-mod-switcher {
  align-items: stretch;
  width: 100%;
}
/* 25/10/2026 (revue R14-14) : sans les déclarations que le segmenté (plus bas) écrasait. ⚠ font-size et font-weight
   restent : son « font: 700 10.5px/1 inherit » est invalide (inherit n'est pas un nom de police), le navigateur l'ignore. */
.brand-mod-btn {
  flex: 1;
  font-size: 0.7rem;
  font-weight: 700;
  position: relative;
}
/* Chaque bouton porte la couleur de SON module, quel que soit le module ouvert. */
.brand-mod-btn[data-set-mod="rmm"]  { --mod-btn: var(--mod-rmm);  --mod-btn-texte: var(--mod-rmm-texte);  --on-mod-btn: var(--on-mod-rmm); }
.brand-mod-btn[data-set-mod="psa"]  { --mod-btn: var(--mod-psa);  --mod-btn-texte: var(--mod-psa-texte);  --on-mod-btn: var(--on-mod-psa); }
.brand-mod-btn[data-set-mod="glue"] { --mod-btn: var(--mod-glue); --mod-btn-texte: var(--mod-glue-texte); --on-mod-btn: var(--on-mod-glue); }
.brand-mod-btn:hover { transform: translateY(-1px); }
.brand-mod-btn.active { box-shadow: 0 0 10px color-mix(in srgb, var(--mod-btn) 55%, transparent); }
/* Trait colore sous le bouton actif (selon mode). */
.brand-mod-btn.active::after {
  content: '';
  position: absolute;
  left: 8px; right: 8px; bottom: -4px;
  height: 3px;
  border-radius: 2px;
  background: var(--mod-btn); box-shadow: 0 0 8px color-mix(in srgb, var(--mod-btn) 70%, transparent);
}
/* v0.21.5 Phase 2 — éditeur de documents DOC */
#gd-editor h1 { font-size: 1.6rem; margin: 14px 0 8px; color: var(--accent); }
#gd-editor h2 { font-size: 1.3rem; margin: 12px 0 6px; color: var(--text); }
#gd-editor h3 { font-size: 1.1rem; margin: 10px 0 4px; color: var(--text); }
#gd-editor p { margin: 8px 0; }
#gd-editor ul, #gd-editor ol { margin: 8px 0 8px 22px; }
#gd-editor blockquote { border-left: 3px solid var(--accent); margin: 10px 0; padding: 4px 12px; color: var(--muted); background: rgba(var(--accent-rgb),0.05); }
#gd-editor pre { background: var(--code-bg); color: var(--text); padding: 10px 14px; border-radius: 5px; overflow-x: auto; font-family: monospace; font-size: 0.85rem; }
#gd-editor code { background: rgba(var(--accent-rgb),0.15); padding: 1px 5px; border-radius: 3px; font-family: monospace; font-size: 0.88rem; }
#gd-editor a { color: var(--accent); }
#gd-toolbar button { background: transparent; border: 1px solid var(--border); color: var(--text); border-radius: 4px; cursor: pointer; font-size: 0.85rem; min-width: 30px; }
#gd-toolbar button:hover { background: rgba(var(--accent-rgb),0.15); border-color: var(--accent); }
.brand-name { font-weight: 800; letter-spacing: -0.3px; font-size: 1.1rem; }

/* (le toggle segmented control retire en v0.15.1 : on bascule en cliquant le logo) */

/* v0.17 sprint 7/10 — Mode wallboard / kiosque (URL ?kiosk=1).
   Active via body.kiosk-mode → masque topbar + sidebar + scrollbars,
   agrandit les widgets, force fullscreen layout pour ecran TV salle des
   operations. Auto-refresh 30s force par le JS quand kiosk-mode actif. */
body.kiosk-mode .topbar,
body.kiosk-mode .sidebar,
body.kiosk-mode #user-zone,
body.kiosk-mode #btn-edit-dash,
body.kiosk-mode #dash-rename,
body.kiosk-mode #dash-delete,
body.kiosk-mode #dash-switch,
body.kiosk-mode .tabs,
body.kiosk-mode #ai-bubble {
  display: none !important;
}
body.kiosk-mode {
  padding-top: 0 !important;
  overflow: hidden;
}
body.kiosk-mode .layout {
  margin-left: 0 !important;
  padding: 0 !important;
}
body.kiosk-mode main#view {
  padding: 24px 32px !important;
  height: 100vh;
  overflow-y: auto;
  scrollbar-width: none;        /* Firefox */
}
body.kiosk-mode main#view::-webkit-scrollbar {
  display: none;                /* Chrome/Safari */
}
body.kiosk-mode h1 {
  font-size: 2.2rem !important;
  margin-bottom: 4px !important;
}
body.kiosk-mode .subtitle {
  font-size: 1rem !important;
}
body.kiosk-mode #dash-widgets-area {
  grid-auto-rows: 140px !important;     /* widgets plus grands */
}
body.kiosk-mode .stat .val {
  font-size: 3.5rem !important;
}
body.kiosk-mode .stat .lbl {
  font-size: 0.95rem !important;
}
body.kiosk-mode .card h2 {
  font-size: 1.3rem !important;
}
/* Bouton "Sortir kiosque" discret en bas-droite (toujours visible pour pouvoir sortir) */
.kiosk-exit {
  position: fixed; bottom: 12px; right: 12px; z-index: 9999;
  background: rgba(var(--accent-rgb),0.15); border: 1px solid var(--accent);
  color: var(--accent); padding: 4px 10px; border-radius: 6px;
  font-size: 0.74rem; cursor: pointer; opacity: 0.5;
  transition: opacity .2s;
}
.kiosk-exit:hover { opacity: 1; }

/* v0.15.4 — Dashboard responsive 4/2/1 cols + lignes a hauteur fixe pour
   permettre le row-span (style Datto). Chaque widget peut s'etaler sur N
   colonnes (data-size) ET sur M rows (data-rows). align-items:start pour
   que les widgets ne soient PLUS forces a la hauteur du plus grand de la row. */
#dash-widgets-area {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-auto-rows: 105px;    /* unite de hauteur : 1 row = 105px. v0.15.4 (80→110→140→120→105). */
  align-items: stretch;     /* widget rempli verticalement dans son span */
  gap: 16px;
  margin-top: 18px;
  width: 100%;
  max-width: 100%;
  box-sizing: border-box;
}
@media (max-width: 1100px) {
  #dash-widgets-area { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 600px) {
  #dash-widgets-area { grid-template-columns: 1fr; }
}
/* Sizing widget : data-size pilote le grid-column.
   "small"  = 1 col  → KPI compact
   "medium" = 2 cols → cartes moyennes (charts, cards)
   "large"  = full   → tableaux pleine largeur */
#dash-widgets-area > [data-size="small"]  { grid-column: span 1; min-width: 0; }
#dash-widgets-area > [data-size="medium"] { grid-column: span 2; min-width: 0; }
#dash-widgets-area > [data-size="large"]  { grid-column: 1 / -1; min-width: 0; }
/* v0.15.4 — Row span : chaque widget choisit sa hauteur en multiples de
   80px. Defaut 2 (160px). Permet hauteur differente sur la meme ligne
   (style Datto). Le drag-resize au coin permet de bouger col ET row. */
#dash-widgets-area > [data-rows="1"] { grid-row: span 1; }
#dash-widgets-area > [data-rows="2"] { grid-row: span 2; }
#dash-widgets-area > [data-rows="3"] { grid-row: span 3; }
#dash-widgets-area > [data-rows="4"] { grid-row: span 4; }
#dash-widgets-area > [data-rows="5"] { grid-row: span 5; }
#dash-widgets-area > [data-rows="6"] { grid-row: span 6; }
#dash-widgets-area > [data-rows="7"] { grid-row: span 7; }
#dash-widgets-area > [data-rows="8"] { grid-row: span 8; }
@media (max-width: 1100px) {
  /* En 2 cols, medium == large == full */
  #dash-widgets-area > [data-size="medium"],
  #dash-widgets-area > [data-size="large"]  { grid-column: 1 / -1; }
}
/* 13/08/2026 — [hidden] FAIT AUTORITE, PARTOUT.
   L attribut hidden ne tient son display:none que de la feuille du navigateur :
   la moindre regle d auteur posant un display le bat. Le defaut est deja connu
   ici (correctif ci-dessous, limite aux widgets) et documente dans app.js:541
   pour .nav-side, contourne au cas par cas en doublant hidden d un
   style.display — contournement qu il faut penser a refaire a chaque fois.
   Signale le 13/08 sur mobile : la sidebar (client actif, menus) restait
   affichee par-dessus l ecran de connexion apres expiration de session.
   Une regle generale rend la classe entiere impossible. */
[hidden] { display: none !important; }

/* 13/08/2026 — TABLEAUX LARGES : le contenu defile, les ACTIONS restent.
   Signale sur « Synology / Problemes actifs ». Le conteneur de defilement etait
   deja correct : le defaut n'etait pas le debordement mais le fait que les
   boutons devenaient inatteignables, la barre horizontale etant invisible sous
   Windows tant qu'on ne la cherche pas.
   A reutiliser sur tout tableau a colonne d'actions, plutot que de recopier des
   styles en dur. */
.tbl-scroll, .hx-defile { overflow-x: auto; -webkit-overflow-scrolling: touch; }
/* Periodicite de facturation — listing des contrats PSA.
   nowrap : un libelle qui passe a la ligne casse l'alignement de la rangee.
   --dim (et non --muted) pour la valeur IMPLICITE : elle est vraie, mais elle
   n'a pas ete saisie. Voir psaPeriodiciteCell() dans ui-helpers.js. */
.psa-periodicite            { font-size: var(--fs-sm); white-space: nowrap; }
.psa-periodicite--implicite { color: var(--dim); }
/* ---- Liste unifiée des automatisations (workflows.js ; automation-unifiee.js n'existe plus) ----
   Une ligne se lit comme une phrase : QUAND -> SUR -> ALORS. Le gain n'est pas
   d'avoir tout au meme endroit, c'est que « — rien — » se VOIE : avant, il
   fallait ouvrir les vingt regles une par une pour decouvrir que treize ne
   declenchaient rien. 25/10/2026 (revue R25-10) : sans les classes au-* que plus
   aucun écran ne pose (en-tête, colonnes, interrupteur .au-tog). */

/* ⚠ UNE SEULE LIGNE (demande du proprietaire). Le style global des <select>
   les etire en pleine largeur, ce qui les empilait : on reprend la main ici.
   La recherche absorbe l espace restant, les selecteurs gardent leur taille. */
.au-filtres         { display: flex; gap: 8px; flex-wrap: nowrap; align-items: center;
                      margin-bottom: 16px; }
.au-filtres input   { flex: 1 1 220px; min-width: 140px; }
.au-filtres select  { flex: 0 1 auto; width: auto; min-width: 0; }
/* En-tetes filtrantes de la liste des automatisations : le filtre vit DANS la
   colonne. Chevron = « je me clique » ; accent + compteur = « un filtre est
   pose ici ». Le panneau est un enfant du <th> (position:relative). */
.u-th-f             { cursor: pointer; user-select: none; position: relative; white-space: nowrap; }
.u-th-f:hover       { color: var(--text); }
.u-th-f--on         { color: var(--accent); }
.u-th-ch            { width: 10px; height: 10px; margin: 0 0 0 4px; vertical-align: -1px; }
.u-th-n             { margin-left: 4px; padding: 0 6px; border-radius: 999px;
                      background: var(--accent-glow); color: var(--accent);
                      font-size: var(--fs-xs); font-variant-numeric: tabular-nums; }

.u-fpop             { position: absolute; top: 100%; left: 0; z-index: 60;
                      min-width: 240px; max-width: 340px; max-height: 320px; overflow: auto;
                      margin-top: 4px; padding: 6px;
                      background: var(--bg-1); border: 1px solid var(--border-strong);
                      border-radius: 8px; box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
                      text-align: left; font-weight: normal; text-transform: none;
                      letter-spacing: normal; cursor: default; }
.u-fpop-s           { padding: 6px 8px 2px; color: var(--muted); font-size: var(--fs-xs);
                      text-transform: uppercase; letter-spacing: 0.6px; }
.u-fpop-l           { display: flex; align-items: center; gap: 8px; padding: 4px 8px;
                      border-radius: 5px; cursor: pointer; font-size: var(--fs-sm);
                      color: var(--text); }
.u-fpop-l:hover     { background: var(--bg-2); }
.u-fpop-t           { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.u-fpop-n           { color: var(--muted); font-size: var(--fs-xs);
                      font-variant-numeric: tabular-nums; }
.u-fpop-r           { width: 100%; margin-top: 4px; }


/* Le choix du declencheur : la porte unique. Deux boutons, deux phrases. */
.au-choix           { display: flex; flex-direction: column; gap: 8px; padding: 16px;
                      margin-bottom: 16px; border: 1px solid var(--border-strong);
                      border-radius: 8px; background: var(--bg-1); }
.au-choix-q         { margin: 0 0 4px; font-weight: 600; }
.au-choix button    { display: block; width: 100%; text-align: left; padding: 12px 16px; }
.au-choix button span { display: block; font-size: var(--fs-sm); color: var(--muted);
                        font-weight: normal; margin-top: 2px; }

.au-table td        { vertical-align: top; }

/* Nature du declencheur. PAS une severite : deux neutres qui se distinguent
   par la forme, pas par une couleur d'etat. */
.au-type            { display: inline-block; padding: 2px 8px; border-radius: 4px;
                      font-size: var(--fs-xs); white-space: nowrap; border: 1px solid var(--border-strong); }
.au-type--evenement { color: var(--muted); }
.au-type--condition { color: var(--text); border-style: dashed; }

/* Pastille d action : pilule, icone du sprite + libelle. L icone est en
   --muted pour que le LIBELLE reste ce qu on lit ; la pastille entiere suit le
   theme via les tokens. Espacement par le gap du conteneur .au-acts. */
.au-acts            { display: flex; flex-wrap: wrap; gap: 4px; align-items: center; }
.au-act             { display: inline-flex; align-items: center; gap: 5px;
                      padding: 3px 10px; border-radius: 999px;
                      font-size: var(--fs-xs); line-height: 1.3; white-space: nowrap;
                      background: var(--bg-2); color: var(--text);
                      border: 1px solid var(--border); }
.au-act .ni,
.au-muet .ni        { width: 12px; height: 12px; margin: 0; vertical-align: -2px;
                      color: var(--muted); }

/* La pastille « alerte » d'une regle : PERMANENTE, non cliquable. Une regle
   qui bascule leve toujours une alerte visible (page Alertes, compteur) ; le
   pointille la distingue des interrupteurs cochables qui la suivent. */
.au-act--implicite  { background: none; border: 1px dashed var(--border-strong);
                      color: var(--muted); }

/* « silencieux » : une façon de ne prévenir personne.
   Il porte l'ambre, parce que c'est un état à regarder — pas une erreur. */
.au-muet            { display: inline-flex; align-items: center; gap: 5px;
                      padding: 3px 10px; border-radius: 999px;
                      font-size: var(--fs-xs); line-height: 1.3; white-space: nowrap;
                      background: var(--state-warn-soft); color: var(--state-warn); }

.au-alerte          { display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
                      padding: 12px 16px; margin-bottom: 16px; border-radius: 8px;
                      background: var(--state-warn-soft); color: var(--state-warn);
                      font-size: var(--fs-sm); }


/* Survol des cartes du tableau de bord.

   ⚠⚠ C'ETAIT DU JAVASCRIPT, et cela posait deux problemes a la fois. Quatre
   addEventListener ecrivaient element.style.transform et element.style.boxShadow
   a la main : des styles en dur, que la charte interdit parce qu'ils echappent
   au theme clair/sombre — et surtout des gestionnaires qui S'ACCUMULENT des
   lors qu'on cesse de reconstruire le DOM a chaque rafraichissement. Les cartes
   KPI avaient deja une garde (dataset._hoverBound), signe que quelqu'un s'y
   etait deja brule ; les cartes de conformite, elles, n'en avaient aucune.
   En CSS, la question ne se pose plus : il n'y a rien a lier, donc rien a
   empiler. */
.dash-kpi-card,
a.dash-comp-card {
  transition: transform 0.14s ease, box-shadow 0.14s ease;
}
.dash-kpi-card:hover,
a.dash-comp-card:hover {
  transform: translateY(-2px);
  /* Jeton, pas une couleur en dur : une ombre noire fixe ignore le theme
     clair/sombre — c'est precisement ce que ce bloc reprochait au JS qu'il
     a remplace. */
  box-shadow: 0 10px 28px -10px var(--stat-color-glow, rgba(0, 0, 0, 0.25));
}
@media (prefers-reduced-motion: reduce) {
  .dash-kpi-card,
  a.dash-comp-card { transition: none; }
  .dash-kpi-card:hover,
  a.dash-comp-card:hover { transform: none; }
}


.tbl-scroll > table { width: 100%; border-collapse: collapse; }

/* ⚠ Le fond est OBLIGATOIRE : sans lui, le contenu qui defile se lit par
   transparence sous les boutons. */
.tbl-scroll th.col-actions,
.tbl-scroll td.col-actions {
  position: sticky; right: 0;
  background: var(--surface);
  box-shadow: -8px 0 8px -8px rgba(0, 0, 0, .45);
  white-space: nowrap;
  text-align: right;
}
.tbl-scroll thead th.col-actions { background: var(--surface-2); }


/* v0.15.2 fix : un widget en attribut [hidden] doit rester invisible meme avec
   nos overrides display:flex. Cette regle prend le pas sur tout le reste. */
#dash-widgets-area > [data-widget][hidden] { display: none !important; }
/* v0.15.4 — Datto-style : le widget remplit son cadre (la cellule grid).
   align-items: stretch (default grid) etire la cellule a la hauteur de la row.
   Le widget DOIT remplir cette cellule entierement, peu importe son contenu.
   Pour cela : display:flex column + height:100%, et les enfants directs
   s'etirent (flex:1) pour absorber l'espace vertical. */
#dash-widgets-area > [data-widget]:not([hidden]) {
  min-width: 0;
  position: relative;
  display: flex;
  flex-direction: column;
  height: 100%;
  box-sizing: border-box;
}
/* v0.15.4 — Le label des KPI personnalises peut etre long (nom de filtre).
   .stat .lbl est en display:flex (icone + texte sur une row) ce qui empeche
   le wrap. On force display:block + word-break + letter-spacing reduit.
   IMPORTANT : la kpi-filter-card est imbriquee dans #dash-custom-kpis (pas
   un enfant direct de #dash-widgets-area), donc on drop le `>` direct. */
.kpi-filter-card .lbl {
  display: block !important;
  white-space: normal !important;
  word-break: break-word !important;
  overflow-wrap: anywhere !important;
  line-height: 1.25 !important;
  /* v0.23 — aligne la typo du titre sur .widget-title (widgets standard) :
     avant, le titre du KPI perso etait en petites majuscules legacy .stat,
     incoherent avec les titres des autres widgets du dashboard. */
  font-size: 0.92rem !important;
  font-weight: 700 !important;
  text-transform: none !important;
  letter-spacing: -0.01em !important;
  color: var(--text) !important;
}
/* Idem pour le header compliance-style des KPI personnalises (donut/progress) */
.kpi-filter-card strong {
  white-space: normal !important;
  word-break: break-word !important;
  overflow-wrap: anywhere !important;
  min-width: 0 !important;
}
/* Cible les enfants de contenu uniquement, PAS les overlays d'edition
   (.widget-edit-toolbar et .widget-resize-handle qui sont absolute et ont
   leur propre flex-direction:row pour leurs boutons).
   min-height:0 + min-width:0 = override le default flex (auto) qui empechait
   le shrink en dessous de la taille du contenu → sans ca le .card debordait. */
#dash-widgets-area > [data-widget]:not([hidden]) > a:not(.widget-edit-toolbar):not(.widget-resize-handle),
#dash-widgets-area > [data-widget]:not([hidden]) > div:not(.widget-edit-toolbar):not(.widget-resize-handle) {
  width: 100%;
  flex: 1 1 auto;
  min-height: 0;
  min-width: 0;
  box-sizing: border-box;
}
/* v0.15.4 — Widgets liste : la card doit etre flex column pour que le tbody
   prenne le reste de la hauteur dispo (apres header + footer pagination).
   overflow:hidden au niveau widget pour clip definitif. */
#dash-widgets-area > [data-widget="kpi_devices_list"],
#dash-widgets-area > [data-widget="recent_alerts"],
#dash-widgets-area > [data-widget="agent_versions"],
#dash-widgets-area > [data-widget="top_tenants"],
#dash-widgets-area > [data-widget="task_failures"] {
  overflow: hidden;
}
#dash-widgets-area > [data-widget="kpi_devices_list"] > .card,
#dash-widgets-area > [data-widget="recent_alerts"] > .card,
#dash-widgets-area > [data-widget="agent_versions"] > .card {
  display: flex;
  flex-direction: column;
  height: 100%;
  min-height: 0;
}
#dash-widgets-area > [data-widget="kpi_devices_list"] #dash-devices {
  flex: 1 1 auto;
  min-height: 0;
  overflow: auto;
}
/* v0.15.4 — recent_alerts contient parfois une .grid intermediaire avec 1-2
   .card. On propage le height:100% a travers cette grid pour que la card
   fille remplisse l'espace alloue par data-rows (et que le tbody s'etende
   au lieu de laisser un espace vide en bas du cadre pointille).
   IMPORTANT : .grid a align-items:start par default ce qui empeche
   .card{height:100%} de prendre effet. On force align-items:stretch +
   align-self:stretch pour que les cards prennent toute la hauteur dispo. */
#dash-widgets-area > [data-widget="recent_alerts"] > .grid {
  height: 100%;
  align-items: stretch;
  margin-bottom: 0 !important;
}
#dash-widgets-area > [data-widget="recent_alerts"] .card {
  display: flex;
  flex-direction: column;
  height: 100%;
  min-height: 0;
  align-self: stretch;
}
/* La table dans la card s'etire verticalement pour combler. La
   pagination footer (last child) reste collee en bas. */
#dash-widgets-area > [data-widget="recent_alerts"] .card > table {
  flex: 1 1 auto;
}
#dash-widgets-area > [data-widget="recent_alerts"] .card > div:last-child {
  flex: 0 0 auto;
  margin-top: auto;
}
/* Pour les KPI cards (.stat est sur le data-widget lui-meme), on force le
   flex column pour que les .lbl/.val/.sub/.spark soient distribues. */
#dash-widgets-area > [data-widget].stat {
  display: flex;
  flex-direction: column;
}
#dash-widgets-area > [data-widget].stat .spark { margin-top: auto; }
/* Force les SVG inline à respecter la largeur du parent */
#dash-widgets-area > [data-widget] svg { max-width: 100%; }
/* Le main view ne déborde jamais sur la largeur disponible */
main#view { flex: 1 1 0; max-width: 100%; min-width: 0; box-sizing: border-box; overflow-x: clip; padding-right: 16px; }
/* clip (et non hidden) : clippe le debordement horizontal SANS creer de conteneur
   de scroll -> n'emprisonne plus les enfants position:sticky (rail Vue 360). */
body.kiosk-mode main#view { overflow-x: hidden; }

/* v0.15.2 — Mode édition : meilleure affordance drag & drop */
body.dashboard-edit-mode #dash-widgets-area {
  background: repeating-linear-gradient(
    45deg,
    rgba(var(--accent-rgb),0.025),
    rgba(var(--accent-rgb),0.025) 10px,
    transparent 10px,
    transparent 20px
  );
  padding: 8px;
  border-radius: 12px;
  border: 2px dashed rgba(var(--accent-rgb),0.25);
  transition: background .2s ease, border .2s ease;
}
body.dashboard-edit-mode [data-widget]:not([hidden]) {
  cursor: grab;
}
body.dashboard-edit-mode [data-widget]:not([hidden]):active { cursor: grabbing; }
/* Drop indicator : ligne pulse violet quand on survole une zone valide */
.drop-indicator-before, .drop-indicator-after {
  position: relative;
}
.drop-indicator-before::before {
  content: ''; position: absolute; left: -8px; right: -8px; top: -8px; height: 4px;
  background: var(--accent); border-radius: 2px;
  box-shadow: 0 0 12px var(--accent-glow);
  animation: dropPulse 0.8s ease-in-out infinite alternate;
}
.drop-indicator-after::after {
  content: ''; position: absolute; left: -8px; right: -8px; bottom: -8px; height: 4px;
  background: var(--accent); border-radius: 2px;
  box-shadow: 0 0 12px var(--accent-glow);
  animation: dropPulse 0.8s ease-in-out infinite alternate;
}
@keyframes dropPulse {
  from { opacity: .6; transform: scaleX(0.95); }
  to   { opacity: 1;  transform: scaleX(1); }
}

/* Toolbar widget en mode edition : plus visuel + fond opaque */
.widget-edit-toolbar {
  background: var(--bg-1) !important;
  backdrop-filter: blur(8px);
}

/* v0.15.2 — Bouton "+" flottant en mode edition pour ajouter un widget rapidement */
.fab-add-widget {
  position: fixed; bottom: 24px; right: 24px; z-index: 9000;
  width: 56px; height: 56px; border-radius: 50%;
  background: linear-gradient(135deg, var(--accent), var(--accent-cyan));
  color: #fff; border: none; cursor: pointer;
  font-size: 1.6rem; font-weight: 300; line-height: 1;
  box-shadow: 0 6px 20px var(--accent-glow), 0 2px 6px rgba(0,0,0,0.3);
  transition: transform .15s ease, box-shadow .15s ease;
}
.fab-add-widget:hover { transform: translateY(-2px) scale(1.05); box-shadow: 0 10px 28px var(--accent-glow); }
.fab-add-widget:active { transform: translateY(0) scale(0.98); }

/* v0.15.4 — Resize handle style Datto : toujours visible en mode edition,
   plus grand, plus contrastee, et hover -> cyan pour affordance evidente. */
.widget-resize-handle {
  position: absolute;
  right: 2px; bottom: 2px;
  width: 22px; height: 22px;
  cursor: nwse-resize;
  z-index: 30;
  border-radius: 0 0 6px 0;
  background:
    linear-gradient(135deg,
      transparent 0%, transparent 45%,
      var(--accent) 45%, var(--accent) 55%,
      transparent 55%, transparent 65%,
      var(--accent) 65%, var(--accent) 75%,
      transparent 75%, transparent 85%,
      var(--accent) 85%, var(--accent) 95%,
      transparent 95%);
  opacity: 0.85;
  transition: opacity .15s ease, transform .15s ease;
}
.widget-resize-handle:hover {
  opacity: 1;
  transform: scale(1.15);
  background:
    linear-gradient(135deg,
      transparent 0%, transparent 45%,
      var(--accent-cyan) 45%, var(--accent-cyan) 55%,
      transparent 55%, transparent 65%,
      var(--accent-cyan) 65%, var(--accent-cyan) 75%,
      transparent 75%, transparent 85%,
      var(--accent-cyan) 85%, var(--accent-cyan) 95%,
      transparent 95%);
}

/* Onglets Configuration */
.config-tab-btn:hover { color: var(--text); background: var(--surface-2) !important; }
.config-tab-btn.active {
  color: var(--accent) !important;
  border-bottom-color: var(--accent) !important;
  background: rgba(var(--accent-rgb),0.08) !important;
}

/* v0.21.4 — Picker moderne (client + site) : bouton-card + popover animée.
   Remplace l'ancien typeahead input + native select. */
.picker-label {
  font-size: 0.62rem; color: var(--muted);
  text-transform: uppercase; letter-spacing: 1.4px;
  margin-bottom: 6px; font-weight: 600;
}
.picker-wrap { position: relative; }
.picker-btn {
  width: 100%; display: flex; align-items: center; gap: 9px;
  padding: 9px 12px; font-size: 0.88rem; font-weight: 500;
  background: linear-gradient(135deg, rgba(255,255,255,0.04), rgba(255,255,255,0.01));
  border: 1px solid var(--border); border-radius: 8px;
  color: var(--text); cursor: pointer; outline: none;
  transition: border-color .15s ease, background .15s ease, transform .08s ease, box-shadow .2s ease;
  text-align: left;
}
.picker-btn:hover {
  border-color: rgba(var(--accent-rgb),0.45);
  background: linear-gradient(135deg, rgba(var(--accent-rgb),0.10), rgba(var(--accent-2-rgb),0.04));
}
.picker-btn:active { transform: scale(0.985); }
.picker-btn:focus-visible {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(var(--accent-rgb),0.20);
}
.picker-btn.is-open {
  border-color: var(--accent);
  background: linear-gradient(135deg, rgba(var(--accent-rgb),0.15), rgba(var(--accent-2-rgb),0.06));
  box-shadow: 0 0 0 3px rgba(var(--accent-rgb),0.18);
}
.picker-btn.has-value {
  background: linear-gradient(135deg, rgba(var(--accent-rgb),0.16), rgba(var(--accent-rgb),0.06));
  border-color: rgba(var(--accent-rgb),0.55);
  color: var(--text);
  font-weight: 600;
}
.picker-btn-icon { font-size: 1rem; flex-shrink: 0; line-height: 1; }
.picker-btn-text {
  flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.picker-btn-chevron {
  font-size: 0.85rem; color: var(--muted); flex-shrink: 0;
  transition: transform .18s ease, color .15s ease;
}
.picker-btn:hover .picker-btn-chevron { color: var(--text); }
.picker-btn.is-open .picker-btn-chevron { transform: rotate(180deg); color: var(--accent); }

.picker-popover {
  /* Inline dans le flow : pousse les elements suivants vers le bas plutot que les recouvrir */
  margin-top: 6px;
  background: var(--popover-bg);
  border: 1px solid rgba(var(--accent-rgb),0.30); border-radius: 10px;
  box-shadow:
    0 6px 20px rgba(0,0,0,0.40),
    0 0 0 1px rgba(255,255,255,0.03);
  overflow: hidden;
  animation: picker-pop 0.14s cubic-bezier(0.16,1,0.3,1);
  -webkit-overflow-scrolling: touch;
}
@keyframes picker-pop {
  from { opacity: 0; transform: translateY(-6px) scale(0.97); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}
/* 25/10/2026 (revue DES1-13) : le filet sous la recherche et la pastille du nombre de postes (.picker-item-meta, plus
   bas) suivent le thème. En blanc translucide, ils disparaissaient en thème clair (blanc sur blanc) ; en sombre, rien ne
   change. */
.picker-search {
  padding: 8px; border-bottom: 1px solid var(--border);
  background: var(--popover-inset);
}
.picker-search input {
  width: 100%; padding: 7px 10px; font-size: 0.85rem;
  background: rgba(255,255,255,0.04); border: 1px solid rgba(255,255,255,0.08);
  border-radius: 6px; color: var(--text); outline: none;
}
.picker-search input:focus {
  border-color: var(--accent);
  background: rgba(var(--accent-rgb),0.10);
}
.picker-list {
  max-height: 320px; overflow-y: auto;
  padding: 4px;
}
.picker-list::-webkit-scrollbar { width: 8px; }
.picker-list::-webkit-scrollbar-track { background: transparent; }
.picker-list::-webkit-scrollbar-thumb {
  background: var(--scrollbar-thumb); border-radius: 4px;
}
.picker-list::-webkit-scrollbar-thumb:hover { background: var(--scrollbar-thumb-hover); }
.picker-item {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 10px; cursor: pointer; font-size: 0.85rem;
  border-radius: 6px; color: var(--text);
  transition: background .12s ease, color .12s ease;
  touch-action: manipulation;
}
.picker-item-icon { font-size: 0.95rem; flex-shrink: 0; opacity: 0.85; }
.picker-item-text { flex: 1; min-width: 0; white-space: normal; word-break: break-word; line-height: 1.25; }
.picker-item-meta {
  font-size: 0.7rem; color: var(--muted);
  background: color-mix(in srgb, var(--text) 5%, transparent); padding: 2px 7px; border-radius: 10px;
  flex-shrink: 0;
}
@supports not (color: color-mix(in srgb, currentColor 50%, transparent)) { .picker-item-meta { background: rgba(255,255,255,0.05); } }
.picker-item-check {
  width: 16px; height: 16px; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  color: var(--accent); font-weight: 700; font-size: 0.95rem;
  opacity: 0;
}
.picker-item:hover,
.picker-item.is-keyboard {
  background: rgba(var(--accent-rgb),0.16); color: var(--text);
}
.picker-item.is-active {
  background: linear-gradient(90deg, rgba(var(--accent-rgb),0.20), rgba(var(--accent-rgb),0.10));
  color: var(--accent); font-weight: 600;
}
.picker-item.is-active .picker-item-check { opacity: 1; }
.picker-empty {
  padding: 18px 14px; color: var(--muted); font-size: 0.82rem; text-align: center;
}

/* iOS : input ≥16px sinon zoom auto */
@media (max-width: 900px) {
  .picker-search input { font-size: 16px; padding: 10px 12px; }
  .picker-item { padding: 12px 12px; font-size: 0.95rem; min-height: 44px; }
  .picker-list { max-height: 60vh; }
}
.nav { display: flex; gap: 4px; }
/* fix login : l'attribut HTML `hidden` doit cacher le menu du haut. Sans cette
   regle, `.nav { display:flex }` override `[hidden]` (UA stylesheet) -> le menu
   reste visible sur l'ecran de connexion alors que viewLogin() fait nav.hidden=true. */
.nav-top[hidden] { display: none !important; }
.nav a {
  padding: 8px 14px; border-radius: var(--radius); color: var(--muted); font-size: 0.9rem; font-weight: 500;
  transition: all .2s ease; position: relative;
}
.nav a:hover { background: var(--surface-2); color: var(--text); }
.nav a.active {
  background: linear-gradient(135deg, rgba(var(--accent-rgb),0.18), rgba(var(--accent-2-rgb),0.10));
  color: #fff;
  box-shadow: 0 0 0 1px rgba(var(--accent-rgb),0.30), 0 4px 14px -4px var(--accent-glow);
}

/* Deroulant topbar "Supervision" (nuit UX 2026-06, facon maquette) */
.nav-dd { position: relative; display: inline-flex; align-items: center; }
.nav-dd-trigger {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 8px 14px; border: none; background: transparent; font: inherit;
  color: var(--muted); font-size: 0.9rem; font-weight: 500; cursor: pointer;
  border-radius: var(--radius); transition: all .2s ease; white-space: nowrap;
}
.nav-dd-trigger:hover { background: var(--surface-2); color: var(--text); }
.nav-dd-trigger.active {
  background: linear-gradient(135deg, rgba(var(--accent-rgb),0.18), rgba(var(--accent-2-rgb),0.10));
  color: #fff;
  box-shadow: 0 0 0 1px rgba(var(--accent-rgb),0.30), 0 4px 14px -4px var(--accent-glow);
}
.nav-dd-caret { font-size: 0.7rem; opacity: .75; transition: transform .15s ease; }
.nav-dd.open .nav-dd-caret { transform: rotate(180deg); }
.nav-dd-rollup {
  background: var(--rollup-crit); color: #fff; padding: 1px 6px; border-radius: 10px;
  font-size: 0.72rem; font-weight: 600; margin-left: 2px;
  /* Un compteur qui passe de 9 a 14 ne fait pas sauter ce qui le suit (charte, « un badge, une forme »). */
  font-variant-numeric: tabular-nums;
}
.nav-dd-rollup--crit { background: var(--rollup-crit); color: #fff; }  /* bloquants (failed/offline + RustDesk) */
.nav-dd-rollup--warn { background: var(--rollup-warn); color: #fff; }  /* retards (late) ; 5,0:1 (#ca8a04 : 2,9:1) */
/* 06/10/2026 (barre du haut) : un compteur qui n'est pas une alerte (tickets ouverts) -- l'accent,
   pas une severite. Blanc sur --accent : 5,4:1 en clair (#14719E), mais 3,6:1 seulement en sombre
   (#1B8FC7), sous les 4,5:1 du petit texte (relecture 166, calcul WCAG). C'etait deja la couleur du
   compteur des tickets (style en ligne) : rien ne change a l'ecran ; le fond est a revoir avec la charte. */
.nav-dd-rollup--info { background: var(--accent); color: #fff; }
.nav-dd-panel {
  position: absolute; top: calc(100% + 6px); left: 0; z-index: 1200;
  min-width: 210px; padding: 6px; display: flex; flex-direction: column; gap: 2px;
  background: var(--surface); border: 1px solid var(--border); border-radius: 10px;
  box-shadow: 0 12px 32px -8px rgba(0,0,0,0.55);
}
.nav-dd-panel[hidden] { display: none; }
.nav-dd-panel a {
  display: flex; align-items: center; gap: 6px; padding: 8px 12px;
  border-radius: 6px; color: var(--text); text-decoration: none;
  font-size: 0.88rem; font-weight: 500; white-space: nowrap;
}
.nav-dd-panel a:hover { background: var(--surface-2); }
.nav-dd-panel a.active { background: var(--surface-2); }

.user-zone { display: flex; align-items: center; gap: 10px; font-size: 0.85rem; color: var(--muted); }
.user-zone .name { color: var(--text); font-weight: 500; }
.user-zone button {
  background: var(--surface); border: 1px solid var(--border); padding: 6px 12px;
  border-radius: var(--radius-sm); cursor: pointer; transition: all .15s;
}
.user-zone button:hover { background: var(--surface-2); border-color: var(--border-strong); }

/* Layout 2 colonnes : sidebar + main */
.layout { display: flex; min-height: calc(100vh - var(--topbar-h)); }
.sidebar {
  width: 230px; flex-shrink: 0;
  background:
    linear-gradient(180deg, rgba(15,21,48,0.5) 0%, rgba(10,15,36,0.3) 100%);
  border-right: 1px solid var(--border);
  padding: 20px 14px;
  position: sticky; top: var(--topbar-h); height: calc(100vh - var(--topbar-h));
  overflow-y: auto;
  backdrop-filter: blur(8px);
}
.sidebar-tenant-picker {
  padding: 0 0 12px 0;
  margin-bottom: 6px;
  background: transparent;
  border: none;
  border-radius: 0;
  border-bottom: 1px solid var(--border);  /* 25/10/2026 (revue DES1-13) : blanc à 5 %, invisible en thème clair */
  position: static;
  overflow: visible;
}
.nav-side a {
  padding: 10px 14px; border-radius: var(--radius); color: var(--muted); font-size: 0.88rem; font-weight: 500;
  gap: 8px;
  transition: all .2s ease; position: relative;
  border-left: 3px solid transparent;
}
.nav-side a:hover { background: var(--surface-2); color: var(--text); }
.nav-side a.active {
  background: linear-gradient(90deg, rgba(var(--accent-rgb),0.18), rgba(var(--accent-rgb),0.05));
  color: #fff;
  border-left-color: var(--accent);
  box-shadow: inset 4px 0 12px -4px var(--accent-glow);
}
.nav-side a.active::after {
  content: ''; position: absolute; right: 8px; top: 50%; transform: translateY(-50%);
  width: 4px; height: 4px; border-radius: 50%;
  background: var(--accent-cyan);
  box-shadow: 0 0 8px var(--accent-cyan);
}

/* View */
main {
  flex: 1; min-width: 0;
  max-width: 1280px; margin: 0 auto; padding: 32px 24px 60px;
}
@media (max-width: 900px) {
  .layout { flex-direction: column; }
  /* overflow:visible : sinon la dropdown du tenant typeahead (position:absolute, max-height 60vh)
     dépasse le sidebar (height:auto) et se fait clipper → items non tappables sur mobile. */
  .sidebar { width: 100%; height: auto; position: static; overflow: visible; border-right: none; border-bottom: 1px solid var(--border); }
  .nav-side { flex-direction: row; flex-wrap: wrap; }
  .nav-side a.active { border-left: none; border-bottom: 3px solid var(--accent); padding-left: 14px; }
}

/* v0.13.65 — menu dropdown des onglets fiche device : fond solide forcé +
   position fixed (positionne par JS via getBoundingClientRect) pour s
   echapper des stacking contexts parents qui le poussent derriere les
   widgets malgre un z-index 9999. 25/10/2026 (revue R24-02) : le filet prend l'accent (marque blanche). */
.tab-group-menu {
  background-color: var(--menu-bg) !important;
  background-image: none !important;
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
  position: fixed !important;
  z-index: 999999 !important;
  border: 1px solid var(--accent) !important;
  border-radius: 8px !important;
  box-shadow: 0 12px 32px rgba(0,0,0,0.8) !important;
  min-width: 220px;
  padding: 5px;
}

h1 {
  font-size: 1.7rem; font-weight: 800; letter-spacing: -0.6px; margin-bottom: 6px;
  color: var(--text);
  display: inline-block;
}
h2 { font-size: 1.18rem; font-weight: 700; margin-bottom: 12px; letter-spacing: -0.2px; }
.subtitle { color: var(--muted); margin-bottom: 24px; font-size: 0.92rem; }

/* v0.15 (harmonisation 02/06) — En-tête de page standard.
   Wrapper flex : titre h1 + sous-titre .subtitle à gauche, actions à droite. */
.page-head, .hx-entete-page { display: flex; justify-content: space-between; align-items: flex-start; gap: var(--space-3); flex-wrap: wrap; margin-bottom: var(--space-4); }
.page-head h1, .hx-entete-page h1 { margin: 0; }
.page-head .subtitle { margin: 4px 0 0; }

/* Cards — v0.13.2 : glassmorphism + gradient border subtil + hover lift */
.card {
  background: linear-gradient(135deg, rgba(255,255,255,0.04), rgba(255,255,255,0.015));
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 20px;
  backdrop-filter: blur(12px) saturate(120%);
  -webkit-backdrop-filter: blur(12px) saturate(120%);
  position: relative;
  transition: border-color .2s ease, box-shadow .2s ease, transform .2s ease;
}
/* Hairline en haut de la card pour un effet "tech panel" */
.card::before {
  content: ''; position: absolute; top: 0; left: 14px; right: 14px; height: 1px;
  background: linear-gradient(90deg, transparent, rgba(var(--accent-rgb),0.40), rgba(var(--accent-2-rgb),0.30), transparent);
  opacity: .6;
  pointer-events: none;
}
/* v0.15 (harmonisation 02/06) — Modificateur de .card : remplace les border-left de
   statut en hex arbitraires. */
.card--accent { border-left: 3px solid var(--state-neutral); }
.card--accent[data-state="ok"]   { border-left-color: var(--state-ok); }
.card--accent[data-state="warn"] { border-left-color: var(--state-warn); }
.card--accent[data-state="crit"] { border-left-color: var(--state-crit); }
.card--accent[data-state="info"] { border-left-color: var(--state-info); }
/* Note : pas de `.card + .card { margin-top }` global — les cards utilisent
   explicitement margin-bottom quand elles sont empilées en flux normal, et
   les conteneurs grid/flex gèrent leur propre `gap`. Évite les décalages
   visuels en grille auto-fill (cf. Packages). */

/* v0.21.4b — Widgets modernes DRAMATIC : grosse orb glowing, gradient mesh visible,
   stripe coloré top, halo coloré au repos. Chaque widget définit --w-accent.
   25/10/2026 (revue R24-02) : sans ton choisi, la tuile prend l'accent du thème, comme .dash-ton--accent et
   .psa-tuile (en dur, elle restait au bleu de HeXaControl sous une marque blanche ; en clair, #14719E au lieu de #1B8FC7). */
.widget {
  --w-accent: var(--accent);
  --w-accent-rgb: var(--accent-rgb);
  position: relative;
  background:
    radial-gradient(ellipse at 100% 0%, rgba(var(--w-accent-rgb), 0.22) 0%, transparent 55%),
    radial-gradient(ellipse at 0% 100%, rgba(var(--w-accent-rgb), 0.10) 0%, transparent 50%),
    linear-gradient(180deg, rgba(20, 25, 50, 0.85), rgba(15, 18, 35, 0.70));
  border: 1px solid rgba(var(--w-accent-rgb), 0.30);
  border-radius: 18px;
  padding: 18px 20px;
  backdrop-filter: blur(18px) saturate(160%);
  -webkit-backdrop-filter: blur(18px) saturate(160%);
  overflow: hidden;
  box-shadow:
    0 4px 16px -4px rgba(0,0,0,0.35),
    0 0 0 1px rgba(var(--w-accent-rgb), 0.06),
    inset 0 1px 0 rgba(255,255,255,0.04);
  transition: transform .2s cubic-bezier(0.16,1,0.3,1),
              border-color .2s ease, box-shadow .25s ease;
}
/* Stripe colorée TOP visible au repos (pas une hairline) */
.widget::before {
  content: '';
  position: absolute; top: 0; left: 0; right: 0; height: 3px;
  background: linear-gradient(90deg,
    rgba(var(--w-accent-rgb), 0.0) 0%,
    rgba(var(--w-accent-rgb), 1.0) 30%,
    rgba(var(--w-accent-rgb), 1.0) 70%,
    rgba(var(--w-accent-rgb), 0.0) 100%);
  box-shadow: 0 0 18px rgba(var(--w-accent-rgb), 0.7);
  pointer-events: none;
}
/* Grosse orb glowing en coin bas-droit */
.widget::after {
  content: ''; position: absolute; bottom: -60px; right: -60px;
  width: 200px; height: 200px; border-radius: 50%;
  background: radial-gradient(circle, rgba(var(--w-accent-rgb), 0.40), transparent 65%);
  filter: blur(30px);
  pointer-events: none;
  opacity: 1;
  transition: opacity .3s ease, transform .4s ease;
}
.widget-clickable { cursor: pointer; }
.widget-clickable:hover {
  transform: translateY(-4px);
  border-color: rgba(var(--w-accent-rgb), 0.65);
  box-shadow:
    0 18px 50px -10px rgba(var(--w-accent-rgb), 0.50),
    0 6px 20px -4px rgba(0,0,0,0.50),
    0 0 0 1px rgba(var(--w-accent-rgb), 0.40),
    inset 0 1px 0 rgba(255,255,255,0.08);
}
.widget-clickable:hover::after {
  transform: scale(1.30);
}
.widget-clickable:active { transform: translateY(-1px); }

/* Header widget : grosse orb d'icône avec halo glow */
.widget-head {
  display: flex; align-items: center; gap: 12px;
  margin-bottom: 14px;
  position: relative; z-index: 1;
}
.widget-head .widget-icon {
  /* Cadre, fond et halo retirés à la demande utilisateur — l icone seule. */
  flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  font-size: 1.8rem; line-height: 1;
}
.widget-head .widget-title {
  flex: 1; min-width: 0;
  font-size: 1.0rem; font-weight: 700; margin: 0;
  color: var(--text);
  letter-spacing: -0.01em;
}
.widget-head .widget-meta {
  font-size: 0.7rem; color: var(--muted); font-weight: 400;
  text-transform: none; letter-spacing: 0;
  display: block; margin-top: 2px;
}
.widget-head .widget-jump {
  font-size: 1.0rem; color: var(--muted); opacity: 0;
  transition: opacity .2s ease, transform .2s ease;
}
.widget-clickable:hover .widget-jump {
  opacity: 1; transform: translateX(4px); color: rgba(var(--w-accent-rgb), 1.0);
}
/* Big metric inside widget — bigger and bolder */
.widget-metric {
  font-size: 2.2rem; font-weight: 800; line-height: 1;
  color: var(--text);
  letter-spacing: -0.03em;
  position: relative; z-index: 1;
}
.widget-metric.is-warn { color: var(--state-warn); text-shadow: 0 0 24px var(--state-warn-glow); }
.widget-metric.is-crit { color: var(--state-crit); text-shadow: 0 0 24px var(--state-crit-glow); }
.widget-metric.is-ok   { color: #4ade80; text-shadow: 0 0 24px rgba(74, 222, 128, 0.40); }
/* 05/10/2026 (revue Gestion NAS) : #4ade80 sur blanc ne faisait que 1,7:1 (le « 0 » vert des tuiles). */
:root[data-theme="light"] .widget-metric.is-ok { color: var(--state-ok); text-shadow: none; }
.widget-metric.is-info { color: rgb(var(--w-accent-rgb)); text-shadow: 0 0 24px rgba(var(--w-accent-rgb), 0.40); }
.widget-metric-label {
  font-size: 0.68rem; color: var(--muted);
  text-transform: uppercase; letter-spacing: 1.6px; font-weight: 600;
  margin-top: 6px;
}

/* v0.21.4 : en mode édition, le widget doit pouvoir afficher sa toolbar (top:-12px)
   et son resize handle qui sortent du rectangle visuel — on retire overflow:hidden.
   On garde le radial-gradient mais sans le clipper (rounded-corners suffisent). */
body.dashboard-edit-mode #dash-widgets-area > .widget {
  overflow: visible !important;
}
/* Le widget-edit-toolbar et resize-handle doivent être au-dessus du ::after orb */
body.dashboard-edit-mode .widget-edit-toolbar,
body.dashboard-edit-mode .widget-resize-handle {
  z-index: 50;
}
/* La pseudo-orb ::after peut casser le hit-target du drag — on l'atténue en édition */
body.dashboard-edit-mode #dash-widgets-area > .widget::after {
  opacity: 0.4;
}
/* En édition : le hover "lift+glow" du .widget-clickable est trompeur (suggère un clic)
   alors qu'on veut drag/resize. On le désactive pendant l'édition. */
body.dashboard-edit-mode #dash-widgets-area > .widget-clickable:hover {
  transform: none;
  box-shadow: 0 4px 16px -4px rgba(0,0,0,0.35), 0 0 0 1px rgba(var(--w-accent-rgb), 0.06), inset 0 1px 0 rgba(255,255,255,0.04);
}
body.dashboard-edit-mode #dash-widgets-area > .widget-clickable {
  cursor: grab;
}
body.dashboard-edit-mode #dash-widgets-area > .widget-clickable:active {
  cursor: grabbing;
}

/* v0.21.4 : compact mode pour les widgets en data-size="small" (KPI dashboard).
   Le pattern .widget par défaut est dimensionné pour des cards medium/large.
   En small (1 col × 2 rows = 210px), on doit réduire padding/orb/metric pour
   que tout rentre proprement sans clipping. */
#dash-widgets-area > [data-size="small"].widget {
  padding: 12px 14px;
}
#dash-widgets-area > [data-size="small"] .widget-head {
  margin-bottom: 8px;
  gap: 9px;
}
#dash-widgets-area > [data-size="small"] .widget-head .widget-icon {
  font-size: 1.4rem;
}
#dash-widgets-area > [data-size="small"] .widget-head .widget-title {
  font-size: 0.92rem;
}
#dash-widgets-area > [data-size="small"] .widget-metric {
  font-size: 1.7rem; letter-spacing: -0.02em;
}
#dash-widgets-area > [data-size="small"] .widget-metric-label {
  font-size: 0.62rem; letter-spacing: 1.2px; margin-top: 3px;
}

/* ============================================================================
   v0.22 — Composants partagés donut + légende + chips (grande échelle).
   Centralise le style autrefois inline dans charts.js/views.js pour que tout
   widget futur (donut conformité, KPI custom, santé flotte) en bénéficie.
   ========================================================================== */

/* Donut avec légende latérale : SVG à gauche, légende à droite ; passe en
   colonne empilée quand le widget est étroit (< ~360px). */
.fox-donut {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 6px 16px;
  padding: 6px 0;
}
.fox-donut .fox-donut-svg { flex: 0 0 auto; }

/* Légende : une ligne par catégorie (pastille + label + valeur + %). */
.fox-legend {
  display: flex;
  flex-direction: column;
  gap: 5px;
  min-width: 0;
  flex: 1 1 130px;
  font-size: 0.8rem;
}
.fox-legend-row {
  display: flex;
  align-items: center;
  gap: 7px;
  line-height: 1.25;
}
.fox-legend-dot {
  width: 9px; height: 9px;
  border-radius: 50%;
  flex: 0 0 auto;
}
.fox-legend-label {
  color: var(--muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  flex: 1 1 auto;
  min-width: 0;
}
.fox-legend-val {
  color: var(--text);
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  flex: 0 0 auto;
}
.fox-legend-pct {
  color: var(--dim);
  font-size: 0.72rem;
  font-variant-numeric: tabular-nums;
  flex: 0 0 auto;
  min-width: 30px;
  text-align: right;
}

/* Chip / pill réutilisable : couleur pilotée par la var --chip.
   Remplace les <span> inline de fhPill() et des collectors. */
.fox-chip {
  display: inline-flex;
  align-items: center;
  padding: 2px 8px;
  border-radius: 10px;
  background: color-mix(in srgb, var(--chip) 14%, transparent);
  color: var(--chip);
  font-size: 0.78rem;
  font-weight: 600;
  white-space: nowrap;
  max-width: 100%;
}
/* Fallback navigateurs sans color-mix : background semi-transparent simple. */
@supports not (background: color-mix(in srgb, red 10%, transparent)) {
  .fox-chip { background: rgba(148,163,184,0.13); }
}

/* Rangée de chips à hauteur bornée + wrap propre : évite le débordement
   vertical/horizontal des cellules de versions du widget Santé flotte. */
.fox-chip-row {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  max-width: 320px;
  max-height: 96px;
  overflow-y: auto;
  scrollbar-width: thin;
}

.grid { display: grid; gap: 16px; align-items: start; }
.grid-2 { grid-template-columns: 1fr 1fr; }
.grid-3 { grid-template-columns: repeat(3, 1fr); }
.grid-4 { grid-template-columns: repeat(4, 1fr); }
@media (max-width: 800px) { .grid-2, .grid-3, .grid-4 { grid-template-columns: 1fr; } }

/* Stat cards — v0.15.2 : palette d'etats + hierarchie typo + sparkline + hover.
   Etats : data-state="ok|warn|crit|info|neutral" sur le .stat (default neutral). */
.stat {
  --stat-color:      var(--state-neutral);
  --stat-color-soft: var(--state-neutral-soft);
  --stat-color-glow: var(--state-neutral-glow);
  background: linear-gradient(135deg, var(--stat-color-soft), rgba(255,255,255,0.01));
  border: 1px solid var(--border);
  border-left: 3px solid var(--stat-color);
  border-radius: var(--radius-lg);
  padding: 16px 18px;
  position: relative;
  overflow: hidden;
  transition: border-color .2s ease, transform .2s ease, box-shadow .2s ease, background .2s ease;
}
.stat[data-state="ok"]      { --stat-color: var(--state-ok);      --stat-color-soft: var(--state-ok-soft);      --stat-color-glow: var(--state-ok-glow); }
.stat[data-state="warn"]    { --stat-color: var(--state-warn);    --stat-color-soft: var(--state-warn-soft);    --stat-color-glow: var(--state-warn-glow); }
.stat[data-state="crit"]    { --stat-color: var(--state-crit);    --stat-color-soft: var(--state-crit-soft);    --stat-color-glow: var(--state-crit-glow); }
.stat[data-state="info"]    { --stat-color: var(--state-info);    --stat-color-soft: var(--state-info-soft);    --stat-color-glow: var(--state-info-glow); }
.stat[data-state="neutral"] { --stat-color: var(--state-neutral); --stat-color-soft: var(--state-neutral-soft); --stat-color-glow: var(--state-neutral-glow); }
.stat::before {
  content: ''; position: absolute; top: 0; left: 0; right: 0; height: 2px;
  background: var(--stat-color); opacity: .65;
}
.stat::after {
  content: ''; position: absolute; bottom: -40%; right: -20%;
  width: 180px; height: 180px;
  background: radial-gradient(circle, var(--stat-color-soft), transparent 70%);
  pointer-events: none;
}
.stat:hover {
  border-color: var(--stat-color);
  transform: translateY(-2px);
  box-shadow: 0 10px 28px -10px var(--stat-color-glow);
  background: linear-gradient(135deg, var(--stat-color-soft), rgba(255,255,255,0.02));
}
.stat .lbl {
  color: var(--muted); font-size: 0.72rem; text-transform: uppercase; letter-spacing: 1.4px; font-weight: 600;
  position: relative; z-index: 1; display: flex; align-items: center; gap: 6px;
}
.stat .val {
  font-size: 3.1rem; font-weight: 800; margin-top: 4px; letter-spacing: -1.8px; line-height: 1;
  color: var(--stat-color);
  position: relative; z-index: 1;
  font-variant-numeric: tabular-nums;
}
.stat .sub {
  font-size: 0.78rem; color: var(--muted); margin-top: 4px;
  position: relative; z-index: 1;
}
/* 23/10/2026 (Jerome : « le chiffre est plus gros que les autres ») : sur le tableau de bord RMM (conteneur sans
   data-tableau="psa"), le chiffre d'un KPI personnalise a la taille de celui des tuiles standard de meme taille
   (.widget-metric : 2,2 rem, 1,7 rem en petite tuile) au lieu des 3,1 rem de .stat .val. Le tableau de bord PSA,
   le mode kiosque et le petit ecran gardent leurs tailles (regles !important plus loin). */
#dash-widgets-area:not([data-tableau="psa"]) .kpi-filter-card .val { font-size: 2.2rem; letter-spacing: -0.03em; }
#dash-widgets-area:not([data-tableau="psa"]) .kpi-filter-card[data-size="small"] .val { font-size: 1.7rem; letter-spacing: -0.02em; }
/* Sparkline slot v0.15.2 : mini-chart 7j en bas de la card. */
.stat .spark {
  margin-top: 10px; height: 28px; position: relative; z-index: 1;
  opacity: .85;
}
.stat .spark svg { width: 100%; height: 100%; display: block; }
.stat .spark path.line { fill: none; stroke: var(--stat-color); stroke-width: 1.6; stroke-linejoin: round; stroke-linecap: round; }
.stat .spark path.area { fill: var(--stat-color); opacity: .12; }
.stat .spark .dot      { fill: var(--stat-color); }

/* Login — v0.13.2 : carte avec gradient border + glow */
.login-page { display: grid; place-items: center; min-height: 70vh; }
.login-box {
  width: 100%; max-width: 420px; padding: 36px;
  background: linear-gradient(135deg, rgba(var(--accent-rgb),0.05), rgba(var(--accent-2-rgb),0.025));
  border: 1px solid rgba(var(--accent-rgb),0.20);
  border-radius: 18px;
  box-shadow:
    0 30px 90px -30px var(--accent-glow),
    inset 0 1px 0 rgba(255,255,255,0.05);
  position: relative;
  backdrop-filter: blur(12px);
}
.login-box::before {
  content: ''; position: absolute; top: 0; left: 20px; right: 20px; height: 2px;
  background: var(--gradient-primary);
  border-radius: 999px;
  filter: blur(.5px);
}
.login-box h1 { margin-bottom: 4px; }
/* Le mot-symbole sur la page de connexion. .hx-mot fixe sa propre taille
   (--fs-md, taille de barre laterale) : il faut donc la reprendre ici, sinon
   le logo arrive minuscule au milieu d'une carte de 380 px. */
/* Le nom du produit sous forme de logo, jamais ecrit en toutes lettres.
   Le CONTENEUR porte la taille, le mot-symbole s'y conforme : sans la regle
   ci-dessous, .hx-mot garderait son --fs-md (taille de barre laterale) et le
   logo arriverait minuscule au milieu d'une carte. */
.marque-produit { display: flex; align-items: center; }
.marque-produit .hx-mot { font-size: inherit; }
/* Un logo depose par un client peut avoir n'importe quel rapport : on borne les
   deux dimensions plutot que la hauteur seule. */
.marque-produit img { max-height: 1.5em; max-width: 8em; width: auto; object-fit: contain; display: block; }
.login-marque { justify-content: center; font-size: 2rem; margin-bottom: 4px; }
.about-marque { font-size: 2rem; margin: 0 0 2px; }
/* « À propos » : le mot-symbole du LOGICIEL, décliné par thème — blanc sur le
   sombre (défaut), bleu marine sur le clair. Sélecteurs à deux classes : la
   règle « .marque-produit img { display:block } » l'emporterait sinon. */
.marque-produit .logo-hx--clair { display: none; }
:root[data-theme="light"] .marque-produit .logo-hx--sombre { display: none; }
:root[data-theme="light"] .marque-produit .logo-hx--clair { display: block; }
/* « À propos » (12/10/2026) : la présentation du produit. Les trois modules portent chacun le ton de leur module
   (jetons --mod-*, et --mod-*-texte pour le texte, lisible dans les deux thèmes) ; la liste de ce qu'ils font est
   une liste à puces sobre ; les liens utiles reprennent la teinte de leur entrée de menu (data-teinte, voir
   « Icones de navigation » plus bas : la couleur n'est relayée que dans la barre et ses menus). */
.about-presentation p { margin: 0; line-height: 1.6; }
.about-presentation p + p { margin-top: var(--space-2); }
.about-module { --hx-ton: var(--mod-rmm); --about-texte: var(--mod-rmm-texte); border-left: 3px solid var(--hx-ton); }
.about-module--psa  { --hx-ton: var(--mod-psa);  --about-texte: var(--mod-psa-texte); }
.about-module--glue { --hx-ton: var(--mod-glue); --about-texte: var(--mod-glue-texte); }
.about-module .hx-titre, .about-module .hx-titre .ni { color: var(--about-texte); }
.about-liste { margin: 0; padding-left: 1.2em; font-size: var(--fs-sm); line-height: 1.6; }
.about-liste li { margin: 0; }
.about-liste li + li { margin-top: var(--space-1); }
.about-liens [data-teinte] > .ni { color: var(--teinte); }
/* « À propos › Doc commerciale » (19/10/2026) : le texte de docs/commercial/HeXaControl.md sous le mot-symbole du
   produit. À l'impression, la présentation SEULE (même geste que le rapport de l'Atelier) ; about.js passe la page au
   thème clair le temps de la boîte d'impression : les jetons suivent, aucune couleur n'est écrite ici. */
.about-commercial .about-marque { margin-bottom: var(--space-4); }
@media print {
  body.about-impression * { visibility: hidden !important; }
  body.about-impression #about-commercial, body.about-impression #about-commercial * { visibility: visible !important; }
  body.about-impression #about-commercial { position: absolute; left: 0; top: 0; width: 100%; border: 0; box-shadow: none; }
  body.about-impression #about-commercial .hx-defile { overflow: visible; }
  body.about-impression #about-commercial .md-h2, body.about-impression #about-commercial .md-h3 { break-after: avoid; }
  body.about-impression #about-commercial tr, body.about-impression #about-commercial li { break-inside: avoid; }
}
.login-box .subtitle { margin-bottom: 24px; }

label { display: block; font-size: 0.72rem; color: var(--muted); margin-top: 14px; margin-bottom: 5px; text-transform: uppercase; letter-spacing: 1.2px; font-weight: 600; }
/* v0.21.4 : datetime-local picker — rend l'icône calendar visible et cliquable
   25/10/2026 (revue DES1-13) : sans filtre propre. Son invert(1) blanchissait l'icône noire du thème clair (blanc sur le
   champ blanc) : celui du haut (invert(50%), le gris du milieu) vaut pour les deux thèmes. */
input[type="datetime-local"], input[type="date"], input[type="time"] {
  color-scheme: var(--scheme); /* utilise les widgets natifs sombres en cohérence avec le theme */
}
input[type="datetime-local"]::-webkit-calendar-picker-indicator,
input[type="date"]::-webkit-calendar-picker-indicator,
input[type="time"]::-webkit-calendar-picker-indicator {
  opacity: 0.85;
  cursor: pointer;
  font-size: 1.1rem;
  margin-left: 8px;
  padding: 2px 4px;
  border-radius: 4px;
  transition: background .15s ease, opacity .15s ease;
}
input[type="datetime-local"]::-webkit-calendar-picker-indicator:hover,
input[type="date"]::-webkit-calendar-picker-indicator:hover,
input[type="time"]::-webkit-calendar-picker-indicator:hover {
  background: rgba(var(--accent-rgb), 0.20);
  opacity: 1;
}

/* v0.21.4 : un label qui contient un checkbox/radio = label INLINE descriptif,
   pas un label de form field. On retire l'uppercase pour qu'il reste lisible.
   :has() couvre tous les labels concernés sans les éditer un par un. */
label:has(> input[type="checkbox"]),
label:has(> input[type="radio"]) {
  text-transform: none !important;
  letter-spacing: 0 !important;
  font-weight: normal !important;
  color: var(--text) !important;
  font-size: 0.85rem !important;
  display: flex !important;
  align-items: center;
  gap: 8px;
  margin-top: 8px;
  cursor: pointer;
  line-height: 1.4;
}
/* Sélecteur défensif : couvre TOUS les types d'input sauf checkbox/radio
   pour éviter les fonds blancs natifs des input[tel|search|url|...]. */
input:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]):not([type=file]),
textarea, select {
  width: 100%; padding: 10px 14px;
  background: var(--input-bg);
  border: 1px solid var(--border-strong);
  color: var(--text); border-radius: var(--radius);
  font-family: inherit;
  transition: border-color .15s ease, box-shadow .15s ease, background .15s ease;
}
input:focus, textarea:focus, select:focus {
  outline: none;
  border-color: var(--accent);
  background: var(--input-bg-focus);
  box-shadow: 0 0 0 3px rgba(var(--accent-rgb),0.15), 0 0 20px -5px var(--accent-glow);
}

/* Bloque le fond blanc/jaune que Chrome/Edge applique sur les champs auto-rempli
   (gestionnaire de mots de passe). Force notre fond sombre + texte clair. */
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus,
input:-webkit-autofill:active {
  -webkit-box-shadow: 0 0 0 1000px var(--bg-1) inset !important;
  -webkit-text-fill-color: var(--text) !important;
  caret-color: var(--text) !important;
  transition: background-color 5000s ease-in-out 0s;
}

/* Buttons v0.13.2 : primary gradient + glow hover, ghost subtle, danger marked */
/* 05/09/2026 — UN BOUTON SANS CLASSE ETAIT INVISIBLE EN THEME SOMBRE.
   La regle groupee `button, input, textarea, select { color: var(--text) }` (l.370)
   donne au bouton la couleur de texte du theme mais AUCUN fond : il gardait le gris
   clair par defaut du navigateur, d'ou du texte clair sur fond clair. Constate par
   Jerome sur les boutons « Desactiver » de la page Extensions, mais present sur une
   centaine de boutons a travers l'application (MDM, workflows, edition de tableau de
   bord, facturation PSA, automatisation...).

   ⭐ `:where()` a une specificite NULLE : ce selecteur pese donc autant qu'un simple
   `button`. Toute regle existante — `button.primary`, `button.ghost`, `#gd-toolbar
   button`, `.au-choix button`, `.user-zone button` — reste prioritaire. Aucun bouton
   deja style ne change d'apparence.

   L'apparence retenue est celle de `.ghost` : une action secondaire neutre. Pas
   `.danger`, dont le rouge signifie « ca va mal » et non « action secondaire ». */
button:where(:not([class]), [class=""]) {
  background: var(--surface);
  color: var(--muted);
  border: 1px solid var(--border);
  padding: 8px 14px;
  border-radius: var(--radius);
  cursor: pointer;
  transition: all .15s ease;
  font-weight: 500;
}
button:where(:not([class]), [class=""]):hover {
  background: var(--surface-2);
  color: var(--text);
  border-color: var(--border-strong);
}
button:where(:not([class]), [class=""]):disabled {
  opacity: .4;
  cursor: not-allowed;
}

/* 25/10/2026 (revue R11-07, R24-04) : comme button.danger depuis le 09/10, les anciennes classes ne reprennent plus
   la main sur un bouton du socle. button.primary, button.ghost et button.btn-sm pèsent (0,1,1) contre (0,1,0) pour
   .hx-bouton et ses variantes : les ~260 boutons repris qui gardent l'ancienne classe à côté (« primary hx-bouton
   hx-bouton--primaire », « ghost hx-bouton hx-bouton--petit ») restaient au dégradé, au halo, au rayon de 10 px et
   aux marges d'avant. :where() garde à l'ancienne règle son poids pour tous les autres boutons.
   25/10/2026 (revue R24-02) : le dégradé suit l'accent de la marque blanche, le texte aussi : --accent-on, l'encre
   que brand_css() choisit sur cet accent (blanc pour la marque du produit, qui ne la définit pas). */
button.primary:not(:where(.hx-bouton)) {
  background: var(--gradient-primary);
  background-size: 200% 200%;
  background-position: 0% 50%;
  color: var(--accent-on, #fff); border: 0;
  padding: 10px 20px; border-radius: var(--radius); font-weight: 600; cursor: pointer;
  transition: all .25s ease;
  box-shadow: 0 4px 14px -4px var(--accent-glow), inset 0 1px 0 rgba(255,255,255,0.15);
  letter-spacing: 0.2px;
}
button.primary:not(:where(.hx-bouton)):hover {
  background-position: 100% 50%;
  box-shadow: 0 6px 22px -4px var(--accent-glow), inset 0 1px 0 rgba(255,255,255,0.2);
  transform: translateY(-1px);
}
button.primary:not(:where(.hx-bouton)):active { transform: translateY(0); }
button.primary:not(:where(.hx-bouton)):disabled { opacity: .4; cursor: not-allowed; transform: none; box-shadow: none; }

button.ghost:not(:where(.hx-bouton)) {
  background: var(--surface);
  color: var(--muted); border: 1px solid var(--border);
  padding: 8px 14px; border-radius: var(--radius); cursor: pointer;
  transition: all .15s ease;
  font-weight: 500;
}
button.ghost:not(:where(.hx-bouton)):hover {
  background: var(--surface-2); color: var(--text); border-color: var(--border-strong);
}
button.danger:not(:where(.hx-bouton)) {
  background: var(--color-crit-soft); color: var(--color-crit);
  border: 1px solid rgba(239,68,68,0.32);
  padding: 8px 14px; border-radius: var(--radius); cursor: pointer;
  transition: all .15s ease;
  font-weight: 500;
}
button.danger:not(:where(.hx-bouton)):hover {
  background: rgba(239,68,68,0.22); color: var(--color-crit);
  box-shadow: 0 4px 14px -4px var(--red-glow);
}

/* v0.15 (harmonisation 02/06) — Tailles de bouton + bouton-icône, pour remplacer
   les padding/font-size forcés en inline (4×10/5×11/6×14, 0.72-0.82rem). */
button.btn-sm:not(:where(.hx-bouton)), .btn-sm { padding: 4px 10px; font-size: var(--fs-sm); border-radius: var(--radius-sm); }
button.btn-icon, .btn-icon {
  padding: 0; width: 30px; height: 30px; display: inline-flex; align-items: center;
  justify-content: center; border-radius: var(--radius-sm); line-height: 1;
}
button.btn-sm.btn-icon, .btn-sm.btn-icon { width: 26px; height: 26px; }

/* v0.13.30 — Classe .toolbar pour les boutons compacts d'action dans les barres outils,
   cards d'action rapide, chips de filtre. Remplace ~313 styles inline `padding:5px 11px;
   font-size:0.78rem; background:transparent; border:1px solid var(--border)...`. */
button.toolbar {
  padding: 5px 11px; font-size: var(--fs-sm);
  background: transparent; color: var(--text);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  cursor: pointer; transition: all .15s ease;
  font-weight: 500;
}
button.toolbar:hover {
  background: var(--surface-2); border-color: var(--border-strong); color: var(--text);
}
button.toolbar.active,
button.toolbar[data-on="1"] {
  border-color: var(--accent); background: var(--color-accent-soft-bg); color: var(--accent);
}
button.toolbar[disabled] { opacity: .4; cursor: not-allowed; }

/* v0.13.30 — Classe .fox-table : style cohérent <th> + <td> pour les tables d'app.js
   (106 <th> répétés trouvés). À utiliser via <table class="fox-table"> au lieu de
   répéter les styles inline. */
table.fox-table {
  width: 100%; border-collapse: collapse; font-size: var(--fs-sm);
}
table.fox-table > thead > tr > th {
  text-align: left; color: var(--muted); font-size: var(--fs-xs);
  padding: 8px 10px; border-bottom: 1px solid var(--border);
  text-transform: uppercase; letter-spacing: 0.5px; font-weight: 600;
}
table.fox-table > tbody > tr {
  border-bottom: 1px solid var(--border); transition: background .12s ease;
}
table.fox-table > tbody > tr:hover { background: var(--surface); }
table.fox-table > tbody > tr > td {
  padding: 8px 10px; vertical-align: middle;
}
table.fox-table.compact > tbody > tr > td { padding: 5px 8px; }
table.fox-table.compact > thead > tr > th { padding: 5px 8px; }

/* Tables — v0.13.2 : header sticky-ish, hover row gradient, transitions */
table { width: 100%; border-collapse: collapse; font-size: 0.88rem; }

/* ── Page Enrôler v0.22 — sélecteur d'intention + méthode recommandée ── */
.dp-grouplbl { font-size:0.7rem; text-transform:uppercase; letter-spacing:0.08em; color:var(--muted); font-weight:700; margin:16px 0 8px; display:flex; align-items:center; gap:8px; }
.dp-grouplbl::after { content:""; flex:1; height:1px; background:var(--border); }
.dp-cards { display:grid; grid-template-columns:repeat(auto-fit,minmax(190px,1fr)); gap:10px; }
.dp-cards-2 { grid-template-columns:repeat(auto-fit,minmax(240px,1fr)); }
.dp-card { border:1px solid var(--border); background:var(--surface); border-radius:10px; padding:12px; cursor:pointer; transition:.15s; position:relative; }
.dp-card:hover { border-color:var(--border-strong); background:var(--surface-2); }
.dp-card.dp-card-on { border-color:var(--accent); background:rgba(var(--accent-rgb),0.10); box-shadow:0 0 0 1px var(--accent); }
.dp-card:focus-visible { outline:2px solid var(--accent); outline-offset:2px; }
.dp-card-dot { position:absolute; top:11px; right:12px; width:9px; height:9px; border-radius:50%; border:1.5px solid var(--border-strong); }
.dp-card.dp-card-on .dp-card-dot { background:var(--accent); border-color:var(--accent); }
.dp-card-ico { font-size:1.15rem; }
.dp-card-title { font-weight:700; margin:5px 0 2px; font-size:0.92rem; }
.dp-card-sub { font-size:0.76rem; color:var(--muted); line-height:1.4; }
.dp-hero { border:1px solid var(--accent); border-left-width:4px; background:linear-gradient(110deg, rgba(var(--accent-rgb),0.10), var(--surface) 70%); border-radius:10px; padding:16px; margin-bottom:14px; }
.dp-reco { font-size:0.7rem; font-weight:700; padding:4px 8px; border-radius:7px; background:rgba(var(--accent-rgb),0.15); color:var(--accent-2); white-space:nowrap; }
.dp-details { border:1px solid var(--border); border-radius:9px; margin:8px 0; background:var(--surface); }
.dp-details > summary { list-style:none; cursor:pointer; padding:12px; font-size:0.85rem; font-weight:600; color:var(--text); }
.dp-details > summary::-webkit-details-marker { display:none; }
.dp-details > summary .dp-chev { color:var(--muted); display:inline-block; transition:.15s; font-size:0.8rem; }
.dp-details[open] > summary .dp-chev { transform:rotate(90deg); }
.dp-details > div { padding:0 12px 12px; font-size:0.85rem; color:var(--muted); line-height:1.6; }
.dp-details > div code { color:var(--text); }
.fox-table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; max-width: 100%; }
thead th {
  text-align: left; padding: 12px 14px; color: var(--muted);
  font-size: 0.7rem; text-transform: uppercase; letter-spacing: 1.5px; font-weight: 600;
  border-bottom: 1px solid var(--border-strong);
  background: rgba(var(--accent-rgb),0.03);
}
tbody td { padding: 12px 14px; border-bottom: 1px solid var(--border); }
tbody tr {
  transition: background .15s ease;
}
tbody tr:hover {
  background: linear-gradient(90deg, rgba(var(--accent-rgb),0.06), rgba(var(--accent-2-rgb),0.03));
  cursor: pointer;
}
tbody tr:last-child td { border-bottom: none; }

/* Badges — v0.13.2 : pulse subtil sur "online" pour effet "live" */
/* 25/10/2026 (revue DES3-04) : un badge tient sur une ligne et ses chiffres sont tabulaires, comme .hx-badge (charte,
   « un badge, une forme ») : vingt règles d'écran (.syno-*, .np-*, .tk-pg-*…) le recousaient, et un badge posé ailleurs
   passait à la ligne (« Pare-feu : Non conforme ») ou décalait la ligne quand son compteur passait de 9 à 14. */
.badge {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 3px 10px; border-radius: 999px;
  font-size: 0.7rem; font-weight: 600; letter-spacing: 0.5px;
  border: 1px solid transparent;
  white-space: nowrap; font-variant-numeric: tabular-nums;
}
.badge.online   {
  background: var(--state-ok-soft); color: var(--state-ok);
  border-color: rgba(34,197,94,0.25);
}
.badge.online::before {
  content: ''; width: 6px; height: 6px; border-radius: 50%;
  background: #22c55e; box-shadow: 0 0 8px var(--green-glow);
  animation: livePulse 2s ease-in-out infinite;
}
@keyframes livePulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: .55; }
}
.badge.offline  {
  background: var(--state-neutral-soft); color: var(--state-neutral);
  border-color: rgba(148,163,184,0.20);
}
.badge.disabled {
  background: var(--color-crit-soft); color: var(--color-crit);
  border-color: rgba(239,68,68,0.25);
}
.badge.windows  {
  background: var(--state-info-soft); color: var(--state-info);
  border-color: rgba(59,130,246,0.22);
}
/* 29/08/2026 — « Super Admin » n'est pas un rôle mais un NIVEAU au-dessus
   d'admin (users.is_super). Aplat d'accent, jamais une couleur de sévérité :
   rouge/ambre/vert disent un état, ici il s'agit d'une identité. La bordure
   pleine le sépare du badge « admin », qui a une bordure faible. */
/* 29/08/2026 — mention du moteur d'inference pour qui n'a pas le droit d'en
   changer : le choix appartient au super administrateur. */
.ia-moteur {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: var(--fs-sm, 0.85rem); color: var(--muted);
}
/* 12/09/2026 — barre d'actions d'un panneau : un bouton et ses réglages sur UNE
   ligne. ⚠ Deux règles globales se liguaient pour casser l'alignement :
   `label` met l'uppercase et 1,2 px d'interlettrage, et `select` impose
   width:100%. Le select écrasait donc le texte du libellé, qui passait à la
   ligne — « MODÈLE : » devenait « MODÈLE » puis « : » — et décalait toute la
   barre. Corriger l'un sans l'autre ne suffit pas. */
.barre-actions {
  display: flex; align-items: center; gap: 12px;
  flex-wrap: wrap; margin-top: 12px;
}
.barre-actions > * { margin-top: 0; margin-bottom: 0; }
.barre-actions .champ-barre {
  display: flex; align-items: center; gap: 8px;
  margin: 0; white-space: nowrap; cursor: default;
}
.barre-actions .champ-barre > select { width: auto; min-width: 180px; }
/* Un réglage que le moteur actif n'utilise pas : lisible, mais visiblement éteint. */
.barre-actions .champ-barre > select:disabled { opacity: 0.6; cursor: not-allowed; }
.barre-actions .ia-moteur { white-space: nowrap; }
.badge.super {
  background: rgba(var(--accent-rgb), 0.18);
  color: var(--accent-2);
  border-color: var(--accent);
}
.badge.android  {
  background: var(--state-ok-soft); color: var(--state-ok);
  border-color: rgba(34,197,94,0.22);
}
.badge.linux    {
  background: var(--state-warn-soft); color: var(--state-warn);
  border-color: rgba(245,158,11,0.22);
}
.badge.ios      {
  background: var(--color-accent-soft-bg); color: var(--accent);
  border-color: rgba(var(--accent-2-rgb),0.22);
}
.badge.macos    {
  background: var(--color-accent-soft-bg); color: var(--accent);
  border-color: rgba(var(--accent-2-rgb),0.22);
}

/* v0.15 (harmonisation 02/06) — Variantes de badge de statut GÉNÉRIQUES câblées sur
   les tokens --state-*. Remplacent les <span style="background:#xxx;color:#xxx"> ad hoc
   disséminés dans les vues. Helper JS unique : badge(label, state) (common.js). */
.badge--ok      { background: var(--state-ok-soft);      color: var(--state-ok);      border-color: var(--state-ok-glow); }
.badge--warn    { background: var(--state-warn-soft);    color: var(--state-warn);    border-color: var(--state-warn-glow); }
.badge--crit    { background: var(--state-crit-soft);    color: var(--state-crit);    border-color: var(--state-crit-glow); }
.badge--info    { background: var(--state-info-soft);    color: var(--state-info);    border-color: var(--state-info-glow); }
.badge--neutral { background: var(--state-neutral-soft); color: var(--state-neutral); border-color: var(--state-neutral-glow); }
/* 05/10/2026 — Une MARQUE, pas un etat : le contact « principal » de la fiche
   client. Accent du produit, donc, et non une couleur de severite. Le texte est
   en --accent-2 : --accent sur son propre voile ne donne que 4,18:1 en theme
   sombre ; --accent-2 y donne 8,57:1, et 5,50:1 en clair (mesures dans Chromium
   par tests/bench_psa_fiche_contacts_rendu.js). */
.badge--accent  { background: var(--color-accent-soft-bg); color: var(--accent-2); border-color: rgba(var(--accent-rgb), 0.35); }

/* Provenance d'une fiche de connaissance : de quel ticket sort ce brouillon IA.
   Bandeau de la modale de relecture (public/js/kb.js). Tokens uniquement -> clair + sombre. */
.kb-provenance { line-height: 1.5; }
.kb-provenance a { color: var(--accent); text-decoration: none; font-weight: 600; }
.kb-provenance a:hover { text-decoration: underline; }

/* Toolbar */
.toolbar { display: flex; gap: 10px; align-items: center; margin-bottom: 16px; flex-wrap: wrap; }
.toolbar input, .toolbar select { padding: 8px 12px; }

/* Console */
.console {
  background: var(--code-bg); border: 1px solid var(--border);
  border-radius: 10px; padding: 14px;
  font-family: 'Cascadia Code', 'Consolas', 'Courier New', monospace;
  font-size: 0.82rem; line-height: 1.5;
  color: #cbd5e1; max-height: 480px; overflow-y: auto; white-space: pre-wrap;
}
.console .ok  { color: var(--color-ok); }
.console .err { color: var(--color-crit); }
.console .dim { color: var(--dim); }

/* AI Chat */
.chat { display: flex; flex-direction: column; gap: 14px; }
.bubble {
  padding: 12px 16px; border-radius: 12px;
  background: var(--surface); border: 1px solid var(--border);
  max-width: 90%; white-space: pre-wrap;
}
.bubble.user { align-self: flex-end; background: rgba(var(--accent-rgb),0.12); border-color: rgba(var(--accent-rgb),0.25); }
.bubble.assistant { align-self: flex-start; }
.bubble.tool {
  background: var(--code-bg); border: 1px solid var(--border-strong);
  font-family: 'Cascadia Code', monospace; font-size: 0.78rem; color: var(--code-text);
  max-width: 100%;
}
.bubble.tool .head { color: var(--accent); margin-bottom: 6px; font-weight: 600; }
.bubble.tool pre { white-space: pre-wrap; max-height: 280px; overflow-y: auto; }
.bubble .role { color: var(--muted); font-size: 0.72rem; text-transform: uppercase; letter-spacing: 1.5px; margin-bottom: 4px; }
.tool-call-list { display: flex; flex-direction: column; gap: 8px; margin-top: 8px; }

/* Toast — v0.13.2 : glassmorphism + glow par type */
/* 25/10/2026 (revue DES1-01) : le toast passe AU-DESSUS des fenêtres. À z-index 100, il s'affichait sous le voile d'une
   fenêtre (showModal le pose à --z-portal et plus, 10 rangs par fenêtre empilée : ui-helpers.js) et sous le tiroir des
   filtres (9000) : « Commande SSH copiée », « Champ requis : … », « Nom requis » restaient cachés. 1 000 rangs de marge
   sur la pile des fenêtres ; --z-toast reste aux avis fixes des pages. */
.toast {
  position: fixed; bottom: 24px; right: 24px;
  padding: 14px 20px; border-radius: var(--radius);
  background: var(--toast-bg);
  border: 1px solid var(--border-strong); color: var(--text);
  opacity: 0; transform: translateY(20px); transition: all .25s ease;
  font-size: 0.88rem; z-index: calc(var(--z-portal) + 1000); max-width: min(380px, calc(100vw - 32px));
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  box-shadow: 0 12px 40px -10px rgba(0,0,0,0.6);
  font-weight: 500;
}
.toast.show { opacity: 1; transform: translateY(0); }
/* 14/10/2026 (contrôle de la session 269) : caché (opacité 0), le toast garde sa boîte et son dernier texte en bas à droite de la
   fenêtre, et il recevait les clics destinés à ce qui est dessous — sur la page d'un banc, à 1 366 × 657, le milieu de « Envoyer »
   (HeXaIA) ne répondait plus après un toast. Caché, il laisse passer les clics.
   25/10/2026 (revue DES1-01) : affiché aussi. Passé au-dessus des fenêtres, il couvrait, sur un téléphone, le pied d'une
   fenêtre en plein écran (« Enregistrer », « Annuler ») pendant 3,5 à 6 s. Il ne porte que du texte (toast(), common.js). */
.toast { pointer-events: none; }
/* 25/10/2026 (revue DES1-01) : le texte d'erreur mélange 70 % du rouge au texte du thème, comme le socle (TON) :
   --color-crit seul ne faisait que 3,7:1 sur le fond sombre du toast, sous les 4,5:1 du petit texte ; 5,3:1 ainsi
   (9,2:1 en clair). Navigateur sans color-mix() : le rouge seul, juste après. */
.toast.error {
  border-color: rgba(239,68,68,0.45); color: color-mix(in srgb, var(--color-crit) 70%, var(--text));
  box-shadow: 0 12px 40px -10px var(--red-glow);
}
@supports not (color: color-mix(in srgb, currentColor 50%, transparent)) { .toast.error { color: var(--color-crit); } }
.toast.ok {
  border-color: rgba(34,197,94,0.45); color: var(--color-ok);
  box-shadow: 0 12px 40px -10px var(--green-glow);
}
/* 05/10/2026 : avertissement et information avaient le style neutre (relecture). */
.toast.warn {
  border-color: var(--state-warn); color: var(--state-warn);
  box-shadow: 0 12px 40px -10px var(--state-warn-glow);
}
.toast.info {
  border-color: var(--state-info); color: var(--state-info);
  box-shadow: 0 12px 40px -10px var(--state-info-glow);
}

/* Section */
.section-title { display: flex; justify-content: space-between; align-items: center; margin: 24px 0 12px; }
.section-title h2 { margin: 0; }

/* Detail */
.detail-grid { display: grid; grid-template-columns: 1fr 360px; gap: 20px; align-items: start; }
.detail-grid .card + .card { margin-top: 0; }   /* siblings dans le grid : pas de margin parasite */
@media (max-width: 1080px) { .detail-grid { grid-template-columns: 1fr; } }

.kv { display: grid; grid-template-columns: 140px 1fr; gap: 8px 16px; font-size: 0.88rem; }
.kv dt { color: var(--muted); }
.kv dd { color: var(--text); word-break: break-word; }

/* Spinner */
.spinner {
  display: inline-block; width: 14px; height: 14px;
  border: 2px solid rgba(255,255,255,0.2); border-top-color: var(--accent);
  border-radius: 50%; animation: spin 0.8s linear infinite;
  vertical-align: middle;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* Metrics */
/* Container queries : la grille s'adapte à la LARGEUR DU PARENT (carte Métriques),
   pas du viewport. Évite les colonnes trop étroites quand la card est en grid-2. */
.metrics-grid {
  container-type: inline-size;
  container-name: metricsbox;
  display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px;
}
@container metricsbox (max-width: 480px) {
  .metrics-grid { grid-template-columns: 1fr; }
}
@media (max-width: 1080px) { .metrics-grid { grid-template-columns: 1fr; } }
.metric-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 14px 16px;
  min-width: 0;
}
.metric-card h3 {
  font-size: 0.72rem;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 1.5px;
  font-weight: 600;
  margin: 0;
}
.metric-value {
  display: flex; align-items: baseline; gap: 6px; margin: 6px 0 8px;
  flex-wrap: wrap; min-width: 0;
}
.metric-value .v { font-size: 1.7rem; font-weight: 700; letter-spacing: -1px; }
.metric-value .u { color: var(--muted); font-size: 0.85rem; }
.metric-value .sub {
  color: var(--dim); font-size: 0.72rem;
  flex: 1 1 100%;        /* passe sur une nouvelle ligne si la card est étroite */
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.metric-canvas { width: 100%; height: 110px; display: block; }
.metric-status {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 0.72rem; color: var(--muted); margin-top: 4px;
}
.metric-status .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--green); box-shadow: 0 0 6px var(--green); }
.metric-status .dot.idle { background: var(--dim); box-shadow: none; }

/* Tabs — v0.13.2 : indicator avec glow */
.tabs { display: flex; gap: 2px; border-bottom: 1px solid var(--border); margin-bottom: 16px; }
.tabs button {
  background: transparent; border: 0; color: var(--muted); padding: 11px 18px;
  cursor: pointer; border-bottom: 2px solid transparent;
  transition: all .2s ease; font-weight: 500;
}
.tabs button:hover { color: var(--text); }
.tabs button.active {
  color: var(--accent); border-bottom-color: var(--accent);
  text-shadow: 0 0 16px var(--accent-glow);
}

/* Spinner amélioré v0.13.2 */
.spinner, .hx-spinner {
  display: inline-block; width: 14px; height: 14px;
  border: 2px solid rgba(var(--accent-rgb),0.15); border-top-color: var(--accent);
  border-radius: 50%; animation: spin 0.7s cubic-bezier(0.4, 0, 0.6, 1) infinite;
  vertical-align: middle;
}

/* Code & Console — v0.13.2 : font Cascadia Code + accent border */
code {
  background: rgba(var(--accent-rgb),0.10);
  border: 1px solid rgba(var(--accent-rgb),0.18);
  padding: 1px 6px;
  border-radius: 4px;
  font-family: 'Cascadia Code', 'JetBrains Mono', 'Consolas', monospace;
  font-size: 0.84em;
  color: var(--accent);
}
pre code { background: transparent; border: none; padding: 0; color: inherit; }

/* v0.13.86 : utility class pour masquer un widget/section si l integration
   parente est desactivee (via JS isIntegrationEnabled). */
.integration-disabled-hide { display: none !important; }

/* V1 Cockpit : menu deroulant 'Gerer' de la fiche equipement */
.dev-manage-dd > summary { list-style:none; }
.dev-manage-dd > summary::-webkit-details-marker { display:none; }
#dev-manage-menu .dev-mn-item { display:flex; align-items:center; gap:8px; width:100%; text-align:left; padding:8px 10px; background:transparent; border:0; border-radius:7px; color:var(--text); cursor:pointer; font-size:0.84rem; font-family:inherit; }
#dev-manage-menu .dev-mn-item:hover { background:var(--surface-2); }

/* Sidebar regroupee en sections repliables (nuit UX) */
.sidebar-sec{margin-bottom:3px}
.sidebar-sec>summary{list-style:none;cursor:pointer;font-size:0.66rem;text-transform:uppercase;letter-spacing:0.07em;color:var(--muted);font-weight:700;padding:7px 10px;display:flex;align-items:center;justify-content:space-between}
.sidebar-sec>summary::-webkit-details-marker{display:none}
.sidebar-sec>summary:hover{color:var(--text)}
.sidebar-sec>summary .sec-chev{transition:transform .15s;opacity:.5;font-size:.7rem}
.sidebar-sec[open]>summary .sec-chev{transform:rotate(90deg)}


/* === Accessibilite (revue 01/06, P2 quick-wins) ===
   - prefers-reduced-motion : coupe animations/transitions pour les utilisateurs
     sensibles au mouvement (WCAG 2.3.3) ;
   - focus-visible global : anneau de focus clavier coherent sur tous les
     interactifs (WCAG 2.4.7). :where() = specificite 0, n'ecrase pas un focus
     deja stylise localement. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}
:where(a, button, [role="button"], input, select, textarea, summary, [tabindex]):focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 4px;
}
/* 05/10/2026 — Cases a cocher et boutons radio : l'anneau ci-dessus ne leur
   parvenait pas. « input:focus { outline: none } » (champs de saisie) les vise
   aussi, et sa specificite l'emporte sur :where() ; il ne leur restait qu'un
   halo a 15 %, invisible sur une case de 16 px (relecture charte, case
   « Contact principal »). :focus-visible : au clavier seulement. */
input[type="checkbox"]:focus-visible, input[type="radio"]:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* ============================================================
   Responsive mobile topbar (2026-06-10) — burger + drawer + accordeon.
   La topbar n'avait AUCUNE adaptation mobile (flex nowrap, ~9 items
   = ~1000px de min-content, hauteur figee, barre fixed sans overflow) :
   sur telephone (app TWA Android) le menu debordait hors ecran,
   definitivement inaccessible. Breakpoint 900px = celui de la sidebar.
   ============================================================ */
/* 06/10/2026 (barre du haut) : 36 px et l'icone du sprite (i-menu) a la place du caractere de menu --
   la barre tient a 360 px avec l'avatar. Ses compteurs : voir « Barre du haut » plus bas. */
.nav-burger {
  display: none; align-items: center; justify-content: center; position: relative;
  width: 36px; height: 36px; padding: 0; flex-shrink: 0;
  background: transparent; border: 1px solid var(--border); border-radius: var(--radius-sm);
  color: var(--text); cursor: pointer;
}
.nav-burger .ni { width: 18px; height: 18px; }
/* Gate login : nav-top[hidden] (= deconnecte) masque aussi le burger.
   Burger et nav sont freres directs dans .topbar -> combinateur ~. */
.nav-top[hidden] ~ .nav-burger { display: none !important; }

@media (max-width: 900px) {
  :root { --topbar-h: 64px; }
  .topbar { padding: 6px 12px; gap: 8px; }
  .brand-logo { height: 26px; }
  .brand-mod-btn { padding: 1px 7px; font-size: 0.66rem; }
  /* (06/10/2026 : la poussee a droite est portee par .tb-outils, plus par #btn-theme) */
  .nav-burger { display: inline-flex; }
  /* ⌘K : palette orientee clavier, sans interet au doigt (display inline sur
     le bouton -> !important obligatoire) */
  #btn-cmdk { display: none !important; }
  /* user-zone condensee : nom ellipse, role + separateur masques */
  .user-zone { gap: 6px; }
  .user-zone > span { display: none; }
  .user-zone .name { max-width: 90px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

  /* Le menu du haut devient un tiroir sous la barre, ouvert par le burger
     (classe .open — PAS l'attribut hidden, reserve au gate login). */
  .nav.nav-top {
    display: none;
    position: fixed; top: var(--topbar-h); left: 0; right: 0;
    flex-direction: column; align-items: stretch; gap: 2px;
    background: var(--bg-1);
    border-bottom: 1px solid var(--border-strong);
    box-shadow: 0 24px 48px -12px rgba(0,0,0,0.7);
    max-height: calc(100vh - var(--topbar-h)); overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    padding: 10px 12px 14px;
    z-index: 49;
  }
  .nav.nav-top.open { display: flex; }
  .nav-top a { padding: 12px 14px; min-height: 44px; display: flex; align-items: center; font-size: 1rem; }
  /* MEME piege que .nav (cf l.842) : une regle auteur display:flex bat la
     feuille UA [hidden] -> sans ceci, les liens gates par role (Parametres,
     Automatisation, Audit : el.hidden=true) reapparaitraient en mobile. */
  .nav-top a[hidden] { display: none !important; }

  /* Dropdown Supervision -> accordeon inline dans le tiroir (le JS ne
     portalise PAS en mobile, cf setupSupervisionDropdown app.js).
     06/10/2026 (barre du haut) : limite aux menus DU TIROIR (.nav-top). Le menu de l'avatar, hors du
     tiroir, s'ouvrait dans la barre elle-meme, en colonne, et sortait de l'ecran a droite : il reste
     un menu flottant, aligne sur le bord droit (setupNavDropdowns). */
  .nav-top .nav-dd { width: 100%; flex-direction: column; align-items: stretch; }
  .nav-top .nav-dd-trigger { width: 100%; justify-content: flex-start; min-height: 44px; font-size: 1rem; }
  .nav-top .nav-dd-panel { position: static; min-width: 0; border: none; box-shadow: none; background: transparent; padding-left: 18px; }
  .nav-top .nav-dd-panel a { white-space: normal; }

  /* Contenu : marges reduites (32/24 -> 16/12) */
  main { padding: 16px 12px 48px; }
  main#view { padding-right: 12px; }
  /* iOS : zoom auto au focus si la police d'un champ est < 16px
     (generalise le fix existant limite a .picker-search) */
  input, select, textarea { font-size: 16px; }
  /* Bloc Self-Service (#deploy) : 5 colonnes inline -> empilees (inline style
     bat la classe, d'ou !important, uniquement <=900px) */
  .deploy-ss-grid { grid-template-columns: 1fr !important; }
}

@media (max-width: 900px) {
  /* DOC mots de passe : colonnes secondaires masquees (Username / URL /
     Tags / Device lie / Expire) -> il reste Titre + Actions, lisible sans
     scroll horizontal. Detail complet via Modifier (ou copie via l'oeil). */
  .gp-table th:nth-child(2), .gp-table td:nth-child(2),
  .gp-table th:nth-child(3), .gp-table td:nth-child(3),
  .gp-table th:nth-child(4), .gp-table td:nth-child(4),
  .gp-table th:nth-child(5), .gp-table td:nth-child(5),
  .gp-table th:nth-child(6), .gp-table td:nth-child(6) { display: none; }
  .gp-table td.gp-actions { white-space: normal !important; }
  .gp-table td:first-child { word-break: break-word; }
}

/* ============================================================
   Compteurs nav en OVERLAY au-dessus du label (2026-06-11) — gain de largeur
   top-nav : les badges (Supervision crit/warn, Alertes top) ne sont plus inline
   a droite (qui elargissait l'item) mais positionnes en absolu au coin haut-droit,
   au-dessus du texte. L'item ne fait plus que la largeur de son libelle.
   ============================================================ */
.nav-dd-trigger { position: relative; }
.nav-dd-rollups {
  position: absolute; top: 0; right: 8px;
  display: flex; gap: 2px; pointer-events: none; z-index: 2;
}
.nav-dd-rollups .nav-dd-rollup {
  margin-left: 0; padding: 0 4px; font-size: 0.6rem; line-height: 1.5;
  min-width: 13px; text-align: center; box-shadow: 0 0 0 1.5px var(--bg-1, #0a0f24);
}
#nav-alerts-top { position: relative; }
/* 06/10/2026 (barre du haut) : le badge est un .nav-dd-rollup (plus de style en ligne a neutraliser,
   d'ou la fin des !important) ; il garde EXACTEMENT la taille des rollups Supervision. */
#nav-alerts-top-badge {
  position: absolute; top: 0; right: 4px;
  margin-left: 0; padding: 0 4px; font-size: 0.6rem;
  line-height: 1.5; min-width: 13px; text-align: center;
  box-shadow: 0 0 0 1.5px var(--bg-1, #0a0f24);
}

/* ==========================================================================
 * SOCLE RESPONSIVE MOBILE GLOBAL  (ajoute 2026-06-25)
 * --------------------------------------------------------------------------
 * Cible le CONTENU des vues genere en JS (styles INLINE) que les @media
 * existantes (toutes basees sur des CLASSES nommees) ne touchent pas.
 * Methode : selecteurs d'attribut [style*="..."] + !important, EXCLUSIVEMENT
 * sous @media (max-width:...). Aucune regle desktop ici, aucun !important
 * hors media query. La chrome (topbar/burger/sidebar/inputs) est deja geree
 * par les @media 900px existantes — on NE LA TOUCHE PAS.
 *
 * Ce bloc est APPENDE en fin de style.css : il y a deja un @media 600px
 * (ligne ~484, dashboard) ; ces deux blocs cohabitent (CSS autorise les
 * media queries multiples de meme condition, cascade par ordre source —
 * le socle, plus bas, gagne la ou il recouvre).
 *
 * Paliers : 768px (tablette etroite, surtout grilles lourdes) et 600px
 * (smartphone, socle principal).
 * ========================================================================== */

/* ------------------------------------------------------------------ 768px
 * Palier intermediaire : on attaque uniquement les grilles a colonnes
 * lourdes (minmax >= 300px) qui debordent deja une tablette portrait.
 * Le reste est traite a 600px.
 * ------------------------------------------------------------------------ */
@media (max-width: 768px) {

  /* Grilles inline minmax(>=300px) -> piste mini plafonnee (fluidite gardee). */
  [style*="minmax(300px"], [style*="minmax(310px"], [style*="minmax(320px"],
  [style*="minmax(330px"], [style*="minmax(340px"],
  [style*="minmax(300px,"], [style*="minmax(310px,"], [style*="minmax(320px,"],
  [style*="minmax(330px,"], [style*="minmax(340px,"] {
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr)) !important;
  }
}

/* ------------------------------------------------------------------ 600px
 * SOCLE SMARTPHONE PRINCIPAL.
 * ------------------------------------------------------------------------ */
@media (max-width: 600px) {

  /* ---- 1. Densite generale : paddings & titres resserres -------------- */
  main { padding: 12px 12px 48px !important; }
  .card { padding: 12px !important; }
  h1 { font-size: 1.25rem !important; letter-spacing: -0.4px !important; }
  .stat .val { font-size: 2rem !important; letter-spacing: -1px !important; }

  /* ---- 2. Grilles inline -> 1 colonne -------------------------------- */
  [style*="grid-template-columns"] {
    grid-template-columns: 1fr !important;
  }
  [style*="grid-auto-flow:column"], [style*="grid-auto-flow: column"] {
    grid-auto-flow: row !important;
  }
  /* Filet pour les classes nommees structurantes */
  .grid, .grid-2, .grid-3, .grid-4,
  .detail-grid, .metrics-grid, #dash-widgets-area {
    grid-template-columns: 1fr !important;
  }

  /* ---- 3. Tableaux -> scroll horizontal interne ---------------------- */
  .fox-table-wrap { overflow-x: auto !important; -webkit-overflow-scrolling: touch !important; }
  table, .fox-table {
    display: block !important;
    width: 100% !important;
    max-width: 100% !important;
    overflow-x: auto !important;
    -webkit-overflow-scrolling: touch !important;
  }
  table th, table td, .fox-table th, .fox-table td {
    padding: 7px 8px !important;
    font-size: 13px !important;
  }

  /* ---- 4. Onglets / barres de navigation interne -> scroll horizontal */
  .tabs {
    flex-wrap: nowrap !important;
    overflow-x: auto !important;
    -webkit-overflow-scrolling: touch !important;
    scrollbar-width: none;
  }
  .tabs::-webkit-scrollbar { display: none; }
  .tabs button { flex: 0 0 auto !important; padding: 10px 12px !important; white-space: nowrap !important; }
  .cfg-menubar { flex-wrap: wrap !important; }
  .config-tab-btn { padding: 8px 10px !important; }

  /* ---- 5. Rangees flex inline -> empilement ------------------------- */
  [style*="display:flex"]:not([style*="flex-direction:column"]):not([style*="flex-direction: column"]),
  [style*="display: flex"]:not([style*="flex-direction:column"]):not([style*="flex-direction: column"]) {
    flex-wrap: wrap !important;
  }
  [style*="display:flex"] > *, [style*="display: flex"] > * { min-width: 0; }

  /* ---- 6. Largeurs fixes inline -> fluides --------------------------- */
  [style*="min-width:300"], [style*="min-width: 300"],
  [style*="min-width:320"], [style*="min-width: 320"],
  [style*="min-width:340"], [style*="min-width: 340"],
  [style*="min-width:360"], [style*="min-width: 360"],
  [style*="min-width:380"], [style*="min-width: 380"],
  [style*="min-width:400"], [style*="min-width: 400"],
  [style*="min-width:420"], [style*="min-width: 420"],
  [style*="min-width:480"], [style*="min-width: 480"],
  [style*="min-width:500"], [style*="min-width: 500"],
  [style*="min-width:560"], [style*="min-width: 560"],
  [style*="min-width:600"], [style*="min-width: 600"] {
    min-width: 0 !important;
  }
  /* tout element a width px inline ne depasse pas le viewport.
     NB : max-width:100% sur une petite icone (24/32px) est inoffensif
     (100% du parent >> taille de l'icone) ; seules les hauteurs sont
     laissees intactes. */
  [style*="width:"] { max-width: 100% !important; }

  /* images & medias jamais debordants.
     10/10/2026 (verification de l'integration) : sauf les icones du sprite (svg.ni, taille fixe de 12 a 48 px).
     Depuis la reprise, une icone est un <svg> dans un bouton a icone seule ou un indicateur de tri : la, ce
     max-width en % tombait a 0 px (dessin hors de son cadre, a 360 et 600 px : lots L03 et L08). */
  img, svg:not(.ni), canvas, video { max-width: 100% !important; height: auto; }

  /* ---- 7. Modales / overlays inline -> quasi plein ecran ------------- */
  /* Pattern canonique (verifie dans glue.js/synology.js) :
        <div class="modal-backdrop" style="position:fixed; inset:0; ... display:flex; center; padding:20px">
          <div class="card" style="max-width:920px; width:100%; max-height:92vh; overflow:auto"> ... </div>
     On reduit le padding de l'overlay et on rend la carte interieure quasi
     plein ecran + scroll interne. On cible inset:0 (ou top:0&left:0)
     UNIQUEMENT -> les popovers 'position:fixed; left:Xpx; top:Ypx' restent. */
  [style*="position:fixed"][style*="inset:0"],
  [style*="position: fixed"][style*="inset:0"],
  [style*="position:fixed"][style*="inset: 0"],
  [style*="position: fixed"][style*="inset: 0"] {
    padding: 0 !important;
    align-items: stretch !important;
  }
  /* la carte interieure (classe .card OU surface inline) prend l'ecran.
     .modal-backdrop > .card couvre le pattern reel (class="modal-backdrop"
     + carte .card) ; les variantes inline var(--surface) sont aussi prises. */
  .modal-backdrop > .card,
  [style*="position:fixed"][style*="inset:0"] > .card,
  [style*="position: fixed"][style*="inset: 0"] > .card,
  [style*="position:fixed"][style*="inset:0"] > [style*="var(--surface)"],
  [style*="position: fixed"][style*="inset: 0"] > [style*="var(--surface)"],
  [style*="position:fixed"][style*="inset:0"] > [style*="background:var(--surface)"],
  [style*="position: fixed"][style*="inset: 0"] > [style*="background:var(--surface)"] {
    width: 100% !important;
    max-width: 100% !important;
    min-width: 0 !important;
    max-height: 100vh !important;
    height: auto !important;
    margin: 0 !important;
    border-radius: 0 !important;
    overflow-y: auto !important;
    -webkit-overflow-scrolling: touch !important;
  }

  /* ---- 8. Garde-fou anti-scroll horizontal de page ------------------ */
  html, body { overflow-x: hidden; }
  main, .page, .view { max-width: 100vw; }
}
/* ===================== FIN SOCLE RESPONSIVE MOBILE ====================== */
/* ====================================================================== *
 *  PATCH RESPONSIVE v2 — CORRECTIFS POST-AUDIT (append apres le socle)
 *  Place APRES le socle 600px pour gagner par ordre de cascade.
 * ====================================================================== */

/* ---------------------------------------------------------------- 768px
 * Tablette / petit portrait : neutralise le rail sticky de la Vue 360
 * avant meme le palier smartphone (sinon il pige l'ecran des ~900px).
 * -------------------------------------------------------------------- */
@media (max-width: 900px) {
  /* Rail lateral Vue 360 : seul <aside position:sticky> des vues device.
     On le repasse dans le flux pour qu'il ne mange plus 1 ecran entier. */
  aside[style*="position:sticky"],
  aside[style*="position: sticky"] {
    position: static !important;
    max-height: none !important;
    overflow: visible !important;
    top: auto !important;
  }
}

/* ====================================================================== *
 *  SMARTPHONE 600px — correctifs
 * ====================================================================== */
@media (max-width: 600px) {

  /* ===== 1. CORRECTIF MAJEUR TABLES ================================= *
   * Le socle force display:block -> la table se TASSE (perd l'algo
   * tableau, thead/tbody se desalignent). On la repasse en display:table
   * + nowrap : elle prend sa largeur naturelle, DEBORDE son wrapper,
   * et le wrapper overflow-x:auto declenche enfin le scroll horizontal.
   * C'est l'inverse du comportement actuel (scroll mort).
   * ----------------------------------------------------------------- */
  table, .fox-table {
    display: table !important;
    width: auto !important;
    min-width: 100% !important;   /* petites tables = pleine largeur     */
    max-width: none !important;
    overflow: visible !important; /* le scroll passe au WRAPPER, pas a la table */
  }
  /* 09/10/2026 (verification du socle) : un tableau a colonnes fixes (.hx-tableau--fixe, socle) garde sa
     largeur minimale de 640 px et defile dans son .hx-defile : la regle ci-dessus, en !important, le
     ramenait a 100 % sous 600 px et ecrasait ses colonnes, contre ce que le socle decrit. */
  table.hx-tableau--fixe { min-width: 640px !important; }
  /* Les wrappers (classe + inline) deviennent les zones de scroll. */
  .fox-table-wrap,
  div[style*="overflow-x:auto"], div[style*="overflow-x: auto"],
  div[style*="overflow:auto"],   div[style*="overflow: auto"] {
    overflow-x: auto !important;
    -webkit-overflow-scrolling: touch !important;
    max-width: 100% !important;
  }
  /* nowrap : chaque colonne prend sa largeur naturelle -> debordement. */
  table th, table td, .fox-table th, .fox-table td {
    white-space: nowrap !important;
    padding: 7px 8px !important;
    font-size: 13px !important;
  }
  /* Exceptions : colonnes a texte long qui doivent pouvoir wrapper. */
  td.td-wrap, .td-wrap {
    white-space: normal !important;
    min-width: 160px;
  }

  /* ===== 2. TABLES "table-layout:fixed" (facturation contrats/tickets) =
   * Enfants DIRECTS de .card, sans wrapper overflow -> ecrasees.
   * On leur donne une largeur mini + on rend la .card scrollable.
   * :has() est large (toutes versions iOS/Chrome 2023+) ; si support
   * insuffisant, voir le wrapper JS en §4 (psa-billing L620/L662).
   * ----------------------------------------------------------------- */
  [style*="table-layout:fixed"], [style*="table-layout: fixed"] {
    min-width: 640px !important;
  }
  .card:has(> table[style*="table-layout:fixed"]),
  .card:has(> table[style*="table-layout: fixed"]) {
    overflow-x: auto !important;
    -webkit-overflow-scrolling: touch !important;
  }

  /* ===== 3. Tables larges nommees : largeur mini lisible =========== *
   * Classes a AJOUTER cote JS (voir §4). min-width force le debordement
   * -> scroll horizontal via le wrapper/card existant.
   * ----------------------------------------------------------------- */
  .unyc-table     { min-width: 980px !important; width: max-content !important; } /* 12 col + double en-tete */

  /* ===== 4. DASHBOARD widgets : tuer les hauteurs verrouillees ===== *
   * En 1 colonne, grid-auto-rows:105px + grid-row:span N laissent des
   * centaines de px de vide. On laisse les widgets se dimensionner au
   * contenu. (Ne touche ni au desktop ni au kiosk-mode.)
   * ----------------------------------------------------------------- */
  #dash-widgets-area { grid-auto-rows: auto !important; }
  #dash-widgets-area > [data-rows] { grid-row: auto !important; }

  /* ===== 5. Export EBP dry-run : sous-tables + descriptions ======== */
  #ebp-content [style*="max-width:300px"],
  #ebp-content [style*="max-width: 300px"] {
    max-width: none !important;
    white-space: normal !important;
  }
  #ebp-content table { min-width: 560px !important; }
  #ebp-content .card { overflow-x: auto !important; -webkit-overflow-scrolling: touch !important; }

  /* ===== 6. Blocs <pre>/<code> de commande (deploiement) =========== *
   * URL token 64 car. sans espace -> coupure forcee au milieu de l'URL.
   * ----------------------------------------------------------------- */
  #dp-content pre, #dp-os-content pre {
    white-space: pre-wrap !important;
    word-break: break-all !important;
    overflow-wrap: anywhere !important;
    max-width: 100% !important;
    overflow-x: auto !important;
  }
  #dp-content code, #dp-os-content code {
    word-break: break-all !important;
    overflow-wrap: anywhere !important;
  }
  /* Champs/selecteurs deploiement : paliers bas manquants au socle. */
  [style*="min-width:220"], [style*="min-width: 220"],
  [style*="min-width:240"], [style*="min-width: 240"],
  [style*="min-width:260"], [style*="min-width: 260"],
  [style*="min-width:280"], [style*="min-width: 280"] {
    min-width: 0 !important;
  }

  /* ===== 7. Configuration : dropdowns menubar cales sur le viewport = *
   * .cfg-menu-dropdown en position:absolute;left:0;min-width:260 deborde
   * a droite. En passant le parent en static, le panneau se cale sur le
   * viewport (left/right 8px) au lieu de partir du declencheur.
   * ----------------------------------------------------------------- */
  .cfg-menu-item { position: static !important; }
  .cfg-menu-dropdown {
    position: absolute !important;
    left: 8px !important;
    right: 8px !important;
    min-width: 0 !important;
    width: auto !important;
    max-width: calc(100vw - 16px) !important;
  }

  /* ===== 8. Toolbar de recherche : empilement propre ============== */
  .toolbar { flex-direction: column !important; align-items: stretch !important; }
  .toolbar > * { width: 100% !important; min-width: 0 !important; }
  /* ⚠⚠ 09/09/2026 — LE CORRECTIF VIT ICI, CONTRE LA REGLE QUI LE REND
     NECESSAIRE. La ligne au-dessus fait passer l axe principal en VERTICAL :
     tout flex-basis exprime en pixels y devient une HAUTEUR. .champ-recherche
     porte « flex: 1 1 260px » (largeur voulue au-dela de 600px) et occupait
     donc 260 px de HAUT sur telephone — un quart d ecran. Le conteneur etant
     a hauteur auto, l espace libre est nul et flex-grow ne rattrape rien.
     `auto` renvoie le champ a la hauteur de son contenu.
     ⛔ Pas de !important : .toolbar .champ-recherche vaut (0,2,0) contre
        (0,1,0) — la specificite suffit, l ordre du fichier est indifferent. */
  .toolbar .champ-recherche { flex-basis: auto; }

  /* ===== 9. Inventaire fiche device : .kv 2 col -> 1 col =========== */
  .kv { grid-template-columns: 1fr !important; gap: 2px 0 !important; }
  .kv dt { margin-top: 6px; }

  /* ===== 10. MDM : boutons d'actions wrappent dans la cellule ====== */
  #mdm-body td[style*="nowrap"], #mdm-apple-body td[style*="nowrap"] {
    white-space: normal !important;
  }

  /* ===== 11. Saisie conso (Photocopieur/Box) : anti-zoom iOS ======= */
  .cp-units, .cp-price { width: 84px !important; font-size: 16px !important; }

  /* ===== 12. Barres "margin-left:auto" (Export EBP, bandeaux) ====== */
  [style*="margin-left:auto"], [style*="margin-left: auto"] { margin-left: 0 !important; }
  #ebp-send, #ebp-reload { width: 100% !important; }

  /* ===== 13. Modales a fond var(--bg-1) (foxConfirm/foxPrompt...) === */
  [style*="position:fixed"][style*="inset:0"] > [style*="background:var(--bg-1)"],
  [style*="position: fixed"][style*="inset: 0"] > [style*="background:var(--bg-1)"] {
    width: 100% !important; max-width: 100% !important; min-width: 0 !important;
    max-height: 100vh !important; height: auto !important; margin: 0 !important;
    border-radius: 0 !important; overflow-y: auto !important;
  }
}

/* ====================================================================== *
 *  PALIER ETROIT 420px — topbar : action Deconnexion hors-ecran
 * ====================================================================== */
@media (max-width: 420px) {
  /* 06/10/2026 (barre du haut) : « Déconnexion » n'est plus dans la barre mais dans le menu de
     l'avatar (05/08), ou il a la place de son libelle -- les regles qui le reduisaient a un symbole,
     et celle du nom (.name, disparu), sont retirees. 8 px de marge : la barre tient a 360 px. */
  .topbar { padding-left: var(--space-2); padding-right: var(--space-2); }
  .brand-mod-btn { padding: 2px 5px !important; }
}
/* ==================== FIN PATCH RESPONSIVE v2 ========================= */

/* 29/07 — fenetre dediee au chat technicien (#chatwin/<deviceId>).
   La fenetre reutilise la modale telle quelle : on masque simplement tout le
   reste de la console, et le voile de la modale devient le fond de la fenetre. */
body[data-chatwin="1"] .topbar,
body[data-chatwin="1"] .sidebar,
body[data-chatwin="1"] #view { display: none !important; }
body[data-chatwin="1"] { overflow: hidden; }


/* ── Icones de navigation (option A, 05/08/2026) ──────────────────────────
   Remplacent les emoji du bandeau et de la barre laterale. Un emoji est
   dessine par le SYSTEME : trait, couleurs et taille changent d'un poste a
   l'autre, et douze emoji cote a cote font douze styles differents dans la
   meme barre. Ces icones partagent un trait unique et n'ont aucune couleur
   propre -- elles prennent celle du texte, donc elles s'eteignent avec lui. */
.ni {
  width: 16px; height: 16px; flex: none;
  vertical-align: -3px; margin-right: 8px;
  /* ⚠ SANS ces quatre lignes les icones sont INVISIBLES : un <path> sans
     stroke est rempli en noir par defaut, donc perdu sur fond sombre. Elles
     etaient dans la page -- 55 occurrences -- et aucune ne se voyait. */
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
  opacity: .85;
  transition: opacity .14s ease;
}
/* Icone SEULE dans un bouton, sans libelle a sa droite : la marge de 8px
   de .ni decalerait le glyphe hors du centre du bouton. */
/* ── Chat : pieces jointes ───────────────────────────────────────────────
   14/09/2026. Aucune valeur litterale : uniquement des jetons. Le fichier
   views-console-ai.js porte deja des styles en dur par centaines, on n'en
   ajoute pas un de plus. */
.ch-barre        { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.ch-barre__fin   { margin-left: auto; display: flex; align-items: center; gap: var(--space-2); }
.ch-pj           { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-top: var(--space-2); }
.ch-pj__img      { display: block; max-width: 240px; max-height: 180px; width: auto; height: auto;
                   border: 1px solid var(--border); border-radius: 6px; cursor: zoom-in; }
.ch-pj__lien     { display: inline-flex; align-items: center; gap: var(--space-2);
                   padding: var(--space-2) var(--space-3); border: 1px solid var(--border);
                   border-radius: 6px; background: var(--bg-2); color: var(--text);
                   text-decoration: none; font-size: var(--fs-sm); max-width: 100%; }
.ch-pj__lien:hover, .ch-pj__lien:focus-visible { border-color: var(--accent); }
.ch-pj__nom      { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 240px; }
.ch-pj__taille   { color: var(--muted); font-variant-numeric: tabular-nums; }
/* Le fil pendant un glisser-deposer. */
.ch-depot        { outline: 2px dashed var(--accent); outline-offset: -6px; }
/* Le fichier choisi, en attente d'envoi. */
.ch-choisi       { display: inline-flex; align-items: center; gap: var(--space-2);
                   font-size: var(--fs-sm); color: var(--text); }
.ch-choisi__x    { background: none; border: 0; color: var(--muted); cursor: pointer;
                   padding: 0 var(--space-1); font-size: var(--fs-md); line-height: 1; }
.ch-choisi__x:hover { color: var(--text); }
.ch-motif        { color: var(--muted); font-size: var(--fs-xs); }

.ni--seule { width: 14px; height: 14px; margin-right: 0;
             vertical-align: -2px; opacity: 1; }

.nav a:hover .ni,
.nav a.active .ni,
.nav-side a:hover .ni,
.nav-side a.active .ni,
.nav-dd-trigger:hover .ni,
.nav-dd-trigger.active .ni { opacity: 1; }

/* 12/10/2026 (Jérôme : « on a perdu toutes les couleurs ») : les icônes de la barre du haut et de ses menus avaient
   pris le ton du module ouvert (--mod-ton-texte, posé sur <body>), une seule couleur pour toutes.
   12/10/2026, 15:20 (Jérôme, capture du module DOC à l'appui : « on avait des icônes de couleurs avant et c'était
   mieux ! ») : chaque entrée a SA teinte, comme du temps des emoji d'avant le 05/08 -- data-teinte sur le lien ou le
   bouton, une des teintes --ico-* (tête de feuille, deux thèmes), relayée par la propriété --teinte. L'icône d'Alertes
   est rouge : le rouge de son emoji d'avant, voulu par Jérôme ; compteurs et gravité gardent leurs couleurs
   (--rollup-*). Une entrée sans data-teinte garde le ton du module. Les menus ouverts sont portés dans <body>
   (.nav-dd-panel, hors de .nav-top) ; le tiroir (900 px et moins) montre les mêmes liens. Les outils de droite
   (.tb-outils) ont aussi leur teinte ; le bouton du tiroir et la barre latérale (.nav-side) ne changent pas.
   L'icône garde sa teinte, pleine (opacité 1), au survol et sur l'entrée active : seuls le fond et le libellé changent. */
.nav-top .ni, .nav-dd-panel .ni { color: var(--teinte, var(--mod-ton-texte)); }
.tb-outils [data-teinte] > .ni { color: var(--teinte); }
[data-teinte] > .ni { opacity: 1; }
[data-teinte="bleu"]     { --teinte: var(--ico-bleu); }
[data-teinte="ciel"]     { --teinte: var(--ico-ciel); }
[data-teinte="sarcelle"] { --teinte: var(--ico-sarcelle); }
[data-teinte="vert"]     { --teinte: var(--ico-vert); }
[data-teinte="lime"]     { --teinte: var(--ico-lime); }
[data-teinte="jaune"]    { --teinte: var(--ico-jaune); }
[data-teinte="orange"]   { --teinte: var(--ico-orange); }
[data-teinte="rouge"]    { --teinte: var(--ico-rouge); }
[data-teinte="corail"]   { --teinte: var(--ico-corail); }
[data-teinte="rose"]     { --teinte: var(--ico-rose); }
[data-teinte="magenta"]  { --teinte: var(--ico-magenta); }
[data-teinte="violet"]   { --teinte: var(--ico-violet); }
[data-teinte="indigo"]   { --teinte: var(--ico-indigo); }
[data-teinte="ardoise"]  { --teinte: var(--ico-ardoise); }
[data-teinte="acier"]    { --teinte: var(--ico-acier); }
[data-teinte="brun"]     { --teinte: var(--ico-brun); }
/* L'onglet actif de la barre : voile et filet du ton du module -- en RMM, l'accent, le bleu d'avant ; en PSA et en
   DOC, il restait bleu sous un liseré ambre ou vert. Son texte : blanc en sombre, --mod-ton-texte en clair (règle du
   thème clair, en tête de feuille). L'entrée active d'un menu ouvert : le même ton. */
.nav-top > a.active, .nav-dd-trigger.active {
  background: linear-gradient(135deg, color-mix(in srgb, var(--mod-ton) 18%, transparent), color-mix(in srgb, var(--mod-ton) 10%, transparent));
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--mod-ton) 30%, transparent), 0 4px 14px -4px color-mix(in srgb, var(--mod-ton) 45%, transparent);
}
.nav-dd-panel a.active { color: var(--mod-ton-texte); }

/* La barre laterale est verticale : l'icone y sert de point d'alignement,
   les libelles doivent demarrer sur la meme colonne quelle que soit sa forme. */
.nav-side a { display: flex; align-items: center; }
.nav-side .ni { margin-right: 9px; }

/* Une entree de menu tient sur UNE ligne. Les libelles ont ete raccourcis
   pour cela ; ce garde-fou evite qu'un ajout futur ne recasse l'alignement. */
.nav-top a, .nav-dd-trigger { white-space: nowrap; }

/* ── Logo : plus discret ──────────────────────────────────────────────────
   Il reste l'element identitaire mais cesse de dominer une barre dont la
   fonction est la navigation, pas la marque. */
.brand-logo { height: 26px; }

/* ── Selecteur de module : segmente ───────────────────────────────────────
   Trois pastilles en degrade ressemblaient a des etiquettes de version. Un
   segmente dit ce qu'il est : un choix, dont une seule valeur est active.
   12/10/2026 : chaque libellé a la couleur de son module (--mod-btn-texte), l'actif est un aplat de cette couleur
   (--mod-btn) et son texte --on-mod-btn ; avant, des couleurs écrites en dur et des libellés en --muted. */
.brand-mod-switcher {
  display: flex; gap: 2px;
  background: var(--switcher-bg);
  padding: 2px; border-radius: 7px;
}
/* 12/10/2026 (Jérôme : « toujours pas les icônes remis ») : les TROIS pastilles sont de nouveau pleines, chacune dans le dégradé de
   son module, comme avant le 05/08 ; l'inactif est atténué (opacité), l'actif garde son halo et son trait. Texte --on-mod-btn. */
.brand-mod-btn {
  border: 0;
  background: linear-gradient(135deg, var(--mod-btn, var(--accent)), color-mix(in srgb, var(--mod-btn, var(--accent)) 72%, #000));
  font: 700 10.5px/1 inherit; letter-spacing: .05em;
  color: var(--on-mod-btn, #fff); padding: 6px 9px; border-radius: 5px; opacity: .6;
  cursor: pointer; transition: opacity .14s, transform .14s;
}
.brand-mod-btn:hover { opacity: .85; color: var(--on-mod-btn, #fff); }
.brand-mod-btn.active { opacity: 1; color: var(--on-mod-btn, #fff); }
/* L'anneau de focus prend le ton de TEXTE du module (vérification du 12/10) : l'aplat ambre ne faisait que 1,7 à
   2,1:1 sur la barre claire, le vert 2,9:1 ; il faut 3:1. En sombre, c'est le même ton que l'aplat. */
.brand-mod-btn:focus-visible { outline: 2px solid var(--mod-btn-texte, var(--accent)); outline-offset: 1px; }


/* ── Menu utilisateur (05/08/2026) ────────────────────────────────────────
   Remplace le nom complet + role + bouton « Deconnexion » affiches en clair,
   qui occupaient ~400 px et coupaient le bouton a droite sur un portable. */
.nav-user-trigger {
  display: flex; align-items: center; gap: 4px;
  background: none; border: 0; cursor: pointer;
  padding: 4px 8px 4px 4px; border-radius: 20px;
}
/* 25/10/2026 (revue DES1-13) : les survols du menu du compte (l'avatar, ses entrées, « Déconnexion ») prennent le texte
   du thème à 6-7 % au lieu d'un blanc translucide, invisible en thème clair ; en sombre, rien ne change. Sur l'avatar,
   la règle .user-zone (plus bas) effaçait ce survol : le sélecteur renforcé le rend. Navigateur sans color-mix() :
   --surface-2, après « Déconnexion ». */
.nav-user-trigger:hover, .user-zone .nav-user-trigger:hover { background: color-mix(in srgb, var(--text) 7%, transparent); }
.nav-user-trigger:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* Les initiales, pas une photo : aucune n'est stockee, et un rond vide serait
   pire que pas de rond du tout. */
.nav-user-av {
  width: 28px; height: 28px; border-radius: 50%;
  display: grid; place-items: center;
  /* 12/10/2026 : l'aplat d'accent (plus un dégradé vers un bleu écrit en dur) ; le texte, celui que la marque
     blanche choisit sur son accent (--accent-on), blanc sinon. */
  background: var(--accent);
  color: var(--accent-on, #fff); font-size: 11px; font-weight: 600; letter-spacing: .02em;
}

.nav-user-panel { min-width: 232px; padding: 8px; }
.nav-user-panel a {
  display: flex; align-items: center; gap: 8px;
  padding: 8px 12px; border-radius: 8px;
  color: var(--dim); text-decoration: none; font-size: var(--fs-sm);
}
.nav-user-panel a:hover { background: color-mix(in srgb, var(--text) 6%, transparent); color: var(--text); }

.nav-user-head { flex-direction: column; align-items: flex-start !important; gap: 2px !important; }
.nav-user-full { color: var(--text); font-weight: 600; font-size: var(--fs-sm); }
.nav-user-role { color: var(--dim); font-size: var(--fs-xs); }
.nav-user-sep { height: 1px; background: var(--border); margin: 8px 4px; }

/* 26/10/2026 (revue DES1-13) : « Déconnexion » GARDE son rouge : le banc des couleurs de la barre, posé à la demande de
   Jérôme le 12/10, le range parmi ce qui ne doit pas changer (tests/bench_barre_couleurs_rendu.js, contrôle 6). */
.nav-user-out {
  width: 100%; text-align: left;
  display: flex; align-items: center; gap: 8px;
  padding: 8px 12px; border-radius: 8px;
  background: none; border: 0; cursor: pointer;
  color: var(--color-crit); font-size: var(--fs-sm); font-family: inherit;
}
.nav-user-out:hover { background: rgba(220, 38, 38, .12); }
@supports not (color: color-mix(in srgb, currentColor 50%, transparent)) {
  .nav-user-trigger:hover, .user-zone .nav-user-trigger:hover, .nav-user-panel a:hover { background: var(--surface-2); }
}

/* Un menu ancre a droite doit s'aligner sur son bord droit, sinon il sort de
   l'ecran. Le calcul est fait en JS (setupNavDropdowns) ; ce marqueur le declare. */
.nav-dd--right .nav-dd-panel { right: 0; left: auto; }

/* ── Barre du haut : elle tient de 1 280 a 1 920 px, et a 360 px (06/10/2026) ──────────────────
   Jerome, plusieurs fois : « deborde de l'ecran ». Mesure sur main (tests/bench_barre_haut_rendu.js) :
   a 1 280 px l'avatar sortait de l'ecran de 230 px (302 en PSA), a 1 440 px de 70 px (142 en PSA),
   a 360 px de 61 px -- avec lui le menu utilisateur, le theme et les post-its. Trois moyens :
     1. les boutons de droite deviennent des icones d'un meme gabarit (.tb-btn), sans emoji ;
     2. de 901 a 1 599 px, la barre se resserre (marges ; retrait des entrees : 8 px) ;
     3. ce qui ne tient toujours pas se range dans « Plus » (barreHautAjuster, app.js), compteurs
        compris : ils remontent sur le declencheur.
   Les choix arretes ne bougent pas : navigation horizontale, accent, couleurs de module, compteurs. */

/* Un ecart minimal entre les blocs : quand la place manque, la recherche ne colle plus a la derniere
   entree (sa marge automatique tombait a zero). Logo : 16 + 8 = les 24 px d'avant. */
.topbar { column-gap: var(--space-2); }
.brand { margin-right: var(--space-4); }

/* Recherche, post-its, theme : un groupe pousse a droite, des boutons-icones de 32 px. */
.tb-outils { display: flex; align-items: center; gap: var(--space-1); margin-left: auto; flex: none; }
.tb-btn {
  display: inline-flex; align-items: center; justify-content: center; flex: none;
  width: 32px; height: 32px; padding: 0;
  background: transparent; border: 1px solid var(--border); border-radius: var(--radius-sm);
  color: var(--muted); cursor: pointer;
  transition: color .14s ease, border-color .14s ease, background .14s ease;
}
.tb-btn:hover { color: var(--text); border-color: var(--border-strong); background: var(--surface-2); }
.tb-btn .ni--seule { width: 16px; height: 16px; }
/* Post-its affiches : le bouton le dit (aria-pressed), sans couleur ecrite en dur. 12/10/2026 : l'icone garde sa teinte
   (data-teinte, plus haut) ; le bouton presse se lit a son filet d'accent et a son fond. */
.tb-btn.is-actif { border-color: var(--accent); background: var(--surface-2); }

/* L'avatar : la regle « .user-zone button » (celle du bouton « Deconnexion » d'avant le menu) lui
   donnait un cadre et 12 px de retrait de chaque cote, au lieu de la pastille ronde du 05/08. */
.user-zone .nav-user-trigger { background: none; border: 0; padding: 4px 8px 4px 4px; border-radius: 20px; }
/* Le menu de l'avatar tient dans l'ecran, meme avec un nom tres long : au-dela, points de suspension
   et le nom entier en infobulle (title pose par renderUserZone). */
.nav-user-panel { max-width: min(360px, calc(100vw - 16px)); }
.nav-user-full { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* « Plus » : une entree rangee y montre son compteur en bout de ligne, comme dans Supervision. */
#nav-plus-panel a > .nav-dd-rollup { margin-left: auto; }
#nav-plus-panel #nav-alerts-top-badge { position: static; padding: 1px 6px; font-size: 0.72rem; line-height: normal; box-shadow: none; }

/* Le bouton du tiroir (sous 900 px) porte, en coin, les compteurs des entrees qu'il cache. */
.nav-burger .nav-dd-rollups { top: -6px; right: -6px; }

/* De 901 a 1 599 px, la barre se resserre : 8 px de retrait par entree (14 au-dela), 16 px de marge
   (28 au-dela). Mesure : a 1 440 px, il manquait 13 px au module RMM avec 12 px de retrait, 23 avec la
   barre de defilement de la page ; avec 8, tout y tient. Au-dessus de 1 600 px, rien ne change : la
   barre de Jerome (1 680 px) reste celle qu'il connait. */
@media (min-width: 901px) and (max-width: 1599px) {
  .topbar { padding-left: var(--space-4); padding-right: var(--space-4); }
  .brand { margin-right: var(--space-2); }
  .nav-top > a, .nav-top > .nav-dd > .nav-dd-trigger { padding-left: var(--space-2); padding-right: var(--space-2); }
}
@media (max-width: 900px) {
  .brand { margin-right: 0; }
  /* Les outils, puis le menu : 16 px entre les deux groupes -- la place des compteurs du bouton, qui
     debordent a sa gauche (« 1407 344 ») sans recouvrir le bouton du theme. */
  .nav-burger { margin-left: var(--space-2); }
}



/* ── Logo colle en bas a gauche (06/08/2026) ──────────────────────────────
   Deux corrections d'un coup :

   1. `hidden` sur .brand-logo-wrap etait sans effet -- la classe declare
      `display: inline-block`, et toute regle `display` ecrase `hidden`, qui
      n'est qu'un display:none de faible priorite. Le bloc est desormais retire
      du HTML, pas masque.

   2. « En bas a gauche » veut dire COLLE EN BAS DE L'ECRAN. En simple pied de
      liste, il fallait derouler tous les menus pour l'apercevoir. `sticky` le
      maintient a vue pendant que la liste defile derriere. */

.side-brand {
  position: sticky; bottom: 0; z-index: 2;
  margin-top: auto;
  margin-left: -8px; margin-right: -8px;   /* pleine largeur de la colonne */
  padding: 12px 16px;
  display: flex; align-items: center; gap: 8px;
  border-top: 1px solid var(--border);
  /* Opaque : la liste defile DERRIERE lui, elle ne doit pas transparaitre. */
  background: var(--bg-1);
  text-decoration: none;
  opacity: .78; transition: opacity .14s ease;
}
.side-brand:hover { opacity: 1; }
.side-brand img { height: 22px; width: auto; display: block; }

/* Mot-symbole HeXaControl — inline, pas une image.
   Les lettres heritent de currentColor : le mot reste lisible en theme clair
   comme en theme sombre, ce que l'ancien logo (aplat bleu clair) ne faisait
   pas. Seul le X garde ses couleurs de marque, et sa barre descendante suit
   elle aussi currentColor pour rester contrastee sur les deux fonds. */
.hx-mot {
  display: inline-flex; align-items: center; gap: 1px;
  font-size: var(--fs-md); font-weight: 700; letter-spacing: -0.01em;
  color: var(--text); line-height: 1; white-space: nowrap;
}
.hx-x {
  /* ⚠ Le viewBox du X est RECADRE sur sa propre boite (18.75 18.75 62.5 62.5),
     pas sur le carre de l'icone : sans ce recadrage, le X ne remplissait que
     62,5 % de sa boite et se lisait comme un signe « multiplie » maigre a cote
     de lettres en 700. Constate sur un rendu Chromium, pas suppose.
     0,70 em = hauteur de capitale, l'epaisseur des barres suit alors la meme
     proportion que dans le logo d'origine. */
  width: 0.70em; height: 0.70em; display: block;
  margin: 0 0.08em;
  /* ⚠ Le X DESCEND, et il deborde volontairement sous la ligne de base.
     Centre sur la ligne de texte il flottait : les capitales H et C montent
     de la ligne de base a la hauteur de capitale, leur centre optique est
     donc plus bas que celui de la boite em.
     En em et non en px : la descente suit la taille du texte, du grand titre
     a la barre laterale. Valeur validee sur rendu compare (choix 2 sur 5). */
  transform: translateY(0.09em);
}
.side-brand .side-ver {
  margin-left: auto;
  font-size: var(--fs-xs); color: var(--dim);
  font-variant-numeric: tabular-nums;
}


/* ── Classes de contenu (06/08/2026) ──────────────────────────────────────
   Posees pour la page Sites, ecrites pour TOUS les ecrans. Les ~9 000 styles
   en dur des fichiers JS ne baisseront que si chaque ecran repris trouve ici
   de quoi ne plus en ecrire. */

/* Titres --------------------------------------------------------------- */
.h1-sub, .h2-sub {
  color: var(--dim); font-weight: 400;
}
.h1-sub { font-size: var(--fs-md); }
.h2-sub { font-size: var(--fs-xs); }

.sec { margin-bottom: 24px; }
.sec-head, .hx-entete {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap; margin-bottom: 12px;
}
.sec-title, .hx-titre {
  margin: 0; font-size: var(--fs-md); font-weight: 600;
  display: flex; align-items: center; gap: 8px;
}

/* Grille de cartes ------------------------------------------------------ */
.card-grid { display: grid; gap: 12px; }

/* Pastille d'etat -------------------------------------------------------
   Remplace les emoji 🟢 🔴 : un rond de 7px, dessine par le navigateur, qui
   ne change pas d'un poste a l'autre et se colore par token. */
.stat-dot {
  display: inline-block; width: 7px; height: 7px; border-radius: 50%;
  margin-right: 6px; vertical-align: 1px; flex: none;
}
.stat-dot--ok   { background: var(--color-ok); }
.stat-dot--off  { background: var(--dim); }
.stat-dot--crit { background: var(--color-crit); }
.stat-dot--warn { background: var(--color-warn); }

/* Ligne de metadonnees -------------------------------------------------- */
.meta {
  display: flex; flex-wrap: wrap;
  font-size: var(--fs-xs); color: var(--dim);
}
.meta-item { display: inline-flex; align-items: center; white-space: nowrap; }

/* Boutons ---------------------------------------------------------------
   .btn-sm : action secondaire, texte + icone. .icon-btn : action d'edition,
   icone seule -- toujours avec un title ET un aria-label, sinon l'action est
   muette pour qui ne devine pas le pictogramme. */
.btn-sm {
  display: inline-flex; align-items: center;
  border-radius: 6px;
  border: 1px solid var(--border); background: transparent;
  color: var(--text); font-size: var(--fs-xs); font-family: inherit;
  text-decoration: none; cursor: pointer;
  transition: background .14s ease, border-color .14s ease;
}
.btn-sm:hover { background: var(--surface-2); border-color: var(--border-strong); }
.btn-sm:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* 28/09/2026 : un .btn-sm desactive se voit (la rangee d'actions de la fiche NAS
   desactive « MAJ paquets » sans paquet a mettre a jour ; les boutons le sont
   aussi le temps d'une requete). Le survol ne le rallume pas. */
.btn-sm:disabled, .btn-sm:disabled:hover { opacity: .4; cursor: not-allowed; background: transparent; border-color: var(--border); }

.icon-btn {
  display: grid; place-items: center;
  width: 28px; height: 28px; border-radius: 6px;
  border: 0; background: transparent; color: var(--dim); cursor: pointer;
  transition: background .14s ease, color .14s ease;
}
.icon-btn:hover { background: var(--surface-2); color: var(--text); }
.icon-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.icon-btn--danger:hover { background: rgba(220, 38, 38, .12); color: var(--color-crit); }
.icon-btn .ni { margin: 0; }

.empty, .hx-vide { color: var(--dim); font-size: var(--fs-sm); padding: 12px 0; }

/* Carte de site --------------------------------------------------------- */
.site-card { display: flex; flex-direction: column; }
.site-card--default { border-left: 3px solid var(--accent); }
.site-head { display: flex; align-items: center; gap: 8px; }
.site-name { display: flex; align-items: center; gap: 6px; }
.site-star { color: var(--color-warn); margin: 0; opacity: 1; }
.site-actions { margin-left: auto; display: flex; gap: 4px; }
.site-alert {
  display: flex; align-items: center; gap: 6px;
  color: var(--color-crit); font-size: var(--fs-xs); font-weight: 600;
}
.site-alert .ni { margin: 0; opacity: 1; }
.site-notes {
  padding-top: 8px; border-top: 1px dashed var(--border);
  font-size: var(--fs-xs); color: var(--dim); white-space: pre-wrap;
}
.site-foot { display: flex; gap: 8px; margin-top: 4px; }


/* ── Correctifs de densite (06/08/2026) ───────────────────────────────────
   Sur la premiere version, les deux boutons d'une carte de site passaient a la
   ligne : .btn-sm etait trop large pour une colonne de 280 px. On resserre le
   bouton et on elargit la colonne -- une carte doit tenir ses actions sur une
   seule rangee, sinon la grille perd son alignement. */
.card-grid { grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); }
.btn-sm { padding: 5px 10px; gap: 5px; }
.btn-sm .ni { width: 14px; height: 14px; margin-right: 0; }
.site-foot { flex-wrap: nowrap; }

/* La carte respire moins : sur un ecran de supervision, la densite compte plus
   que le confort de lecture d'un texte long. */
.site-card { padding: 12px 14px; gap: 6px; }
.site-name { font-size: var(--fs-sm); }
.meta { gap: 2px 12px; }

/* L etoile suit le nom du site : elle qualifie ce qui precede (« Principal,
   site par defaut »), au lieu de s intercaler entre l icone et le libelle. */
.site-star { margin-left: 2px; margin-right: 0; width: 13px; height: 13px; }


/* ── Boutons : independants de la balise (06/08/2026) ─────────────────────
   .ghost / .primary / .danger etaient definis `button.ghost` : un
   <a class="ghost"> n'heritait de rien. On compensait alors a cote --
   `class="ghost" style="padding:8px 14px; border-radius:6px; ..."` -- et c'est
   ainsi qu'on arrive a 9 000 styles en dur, un par contournement.

   ⚠ On AJOUTE ces selecteurs, on ne retire rien : les 770 <button> existants
   gardent exactement leur rendu. Les <a> et <div> qui portaient la classe sans
   effet vont enfin s'afficher -- c'est le but.

   `display: inline-flex` pour que les <a> alignent leur icone comme les
   <button>, et `text-decoration: none` parce qu'un lien reste souligne par
   defaut la ou un bouton ne l'est pas.

   25/10/2026 (revue R24-04) : gardés par :not(:where(.hx-bouton)), comme les règles sur <button> -- un
   <a class="primary hx-bouton hx-bouton--primaire"> ou un <label class="ghost hx-bouton"> prend le bouton du socle.
   Texte en --accent-on, comme button.primary (revue R24-02). */

.primary:not(button):not(:where(.hx-bouton)) {
  background: var(--gradient-primary);
  background-size: 200% 200%;
  background-position: 0% 50%;
  color: var(--accent-on, #fff); border: 0;
  padding: 10px 20px; border-radius: var(--radius); font-weight: 600; cursor: pointer;
  transition: all .25s ease;
  box-shadow: 0 4px 14px -4px var(--accent-glow), inset 0 1px 0 rgba(255,255,255,0.15);
  letter-spacing: 0.2px;
}
.primary:hover:not(button):not(:where(.hx-bouton)) {
  background-position: 100% 50%;
  box-shadow: 0 6px 22px -4px var(--accent-glow), inset 0 1px 0 rgba(255,255,255,0.2);
  transform: translateY(-1px);
}
.primary:active:not(button):not(:where(.hx-bouton)) { transform: translateY(0); }
.primary:disabled:not(button):not(:where(.hx-bouton)) { opacity: .4; cursor: not-allowed; transform: none; box-shadow: none; }
.ghost:not(button):not(:where(.hx-bouton)) {
  background: var(--surface);
  color: var(--muted); border: 1px solid var(--border);
  padding: 8px 14px; border-radius: var(--radius); cursor: pointer;
  transition: all .15s ease;
  font-weight: 500;
}
.ghost:hover:not(button):not(:where(.hx-bouton)) {
  background: var(--surface-2); color: var(--text); border-color: var(--border-strong);
}
.danger:not(button):not(:where(.hx-bouton)) {
  background: var(--color-crit-soft); color: var(--color-crit);
  border: 1px solid rgba(239,68,68,0.32);
  padding: 8px 14px; border-radius: var(--radius); cursor: pointer;
  transition: all .15s ease;
  font-weight: 500;
}
.danger:hover:not(button):not(:where(.hx-bouton)) {
  background: rgba(239,68,68,0.22); color: var(--color-crit);
  box-shadow: 0 4px 14px -4px var(--red-glow);
}

.ghost:not(button):not(:where(.hx-bouton)), .primary:not(button):not(:where(.hx-bouton)), .danger:not(button):not(:where(.hx-bouton)) {
  display: inline-flex; align-items: center; gap: 6px;
  text-decoration: none; font-family: inherit; cursor: pointer;
}

/* ── Selecteur de langue (06/08/2026) ─────────────────────────────────────
   Reserve aux administrateurs tant que le contenu des ecrans nest pas

/* ── Selecteur de langue (06/08/2026) ─────────────────────────────────────
   Reserve aux administrateurs tant que le contenu des ecrans n'est pas
   traduit : la navigation l'est (42 libelles), les ~2 700 textes des ecrans
   ne le sont pas. La note sous les boutons le dit -- une bascule qui ne
   previent pas de son perimetre fait passer le produit pour casse. */
.nav-user-lang {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; padding: 8px 12px;
}
.nav-user-lang-lbl { font-size: var(--fs-xs); color: var(--dim); }
.nav-user-lang-btns { display: flex; gap: 2px; background: var(--bg-0); padding: 2px; border-radius: 6px; }
.nav-user-lang-btns button {
  border: 0; background: none; cursor: pointer; font-family: inherit;
  font-size: 11px; font-weight: 600; letter-spacing: .04em;
  color: var(--dim); padding: 4px 10px; border-radius: 4px;
}
.nav-user-lang-btns button:hover { color: var(--text); }
.nav-user-lang-btns button.on { background: var(--accent); color: #fff; }
.nav-user-lang-note {
  padding: 0 12px 8px; font-size: 10.5px; line-height: 1.4;
  color: var(--dim); opacity: .8;
}


/* ── Barre laterale : hauteur sans debordement (06/08/2026) ───────────────
   Une barre de defilement apparaissait alors que le contenu tenait largement.
   Cause : j'avais pose `min-height: 100%` sur .nav-side pour que
   `margin-top: auto` pousse le logo en bas. Cette regle reclame la hauteur
   PLEINE du parent, en plus des ~60 px du selecteur de client au-dessus --
   total superieur a 100 %.

   Le bon moyen n'est pas d'imposer une hauteur a l'enfant : on fait du parent
   une colonne et on laisse la navigation occuper ce qui reste.

   ⚠ `min-height: 0` est indispensable : un enfant flex vaut `min-height: auto`
   par defaut et refuse de retrecir sous la taille de son contenu. C'est la
   cause classique des debordements en colonne flex. */
.sidebar {
  display: flex; flex-direction: column;
  box-sizing: border-box;
}
.nav-side {
  flex: 1 1 auto; min-height: 0;
  display: flex; flex-direction: column; gap: 3px;
}


/* ── Fiche client renseignee depuis le SIRET ───────────────────────────
   L'annuaire public des entreprises remplit nom, adresse et TVA. Le compte
   rendu doit rester lisible dans les deux themes : rien qu'des tokens. */
.field-action { display: flex; gap: var(--space-2); align-items: flex-start; }
/* Sans min-width:0, un input en flex refuse de descendre sous sa largeur
   intrinseque et pousse le bouton hors de sa colonne. */
.field-action > input   { flex: 1 1 auto; min-width: 0; }
.field-action > .btn-sm { flex: 0 0 auto; white-space: nowrap; }
/* .ni porte un margin-right pour la navigation ; dans un bouton, le gap du
   flex s'en charge deja et les deux s'additionneraient. */
.btn-sm > .ni { margin-right: 0; }

.siret-box {
  margin-top: var(--space-3);
  padding: var(--space-3);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius-sm);
  font-size: var(--fs-sm);
  line-height: 1.6;
}
.siret-box--warn { border-left-color: var(--color-warn); }
.siret-box--err  { border-left-color: var(--color-crit); }

.siret-box__title {
  display: flex; align-items: center; gap: var(--space-2);
  font-weight: 600;
}
.siret-box__meta { color: var(--muted); font-size: var(--fs-xs); }
.siret-box__list { margin: var(--space-2) 0 0; padding-left: var(--space-4); }
.siret-box__warn { color: var(--color-warn); margin-top: var(--space-2); }
.siret-box__diff { margin-top: var(--space-2); }
.siret-box__actions {
  margin-top: var(--space-3);
  display: flex; gap: var(--space-2); flex-wrap: wrap;
}


/* ── Renouvellement du parc : un client par ligne, depliable ───────────
   L'ecran etait en deux tableaux et le texte redige s'inserait entre les
   deux : sur un client en bas de liste, « Rediger » semblait sans effet.
   Tout tient maintenant dans la ligne qu'on ouvre. */
.renouv-table { width: 100%; border-collapse: collapse; font-size: var(--fs-sm); }
.renouv-table thead th {
  background: var(--surface-2);
  padding: var(--space-2) var(--space-3);
  text-align: left; font-weight: 600;
}
.renouv-col-n { text-align: center; width: 96px; }
.renouv-col-a { text-align: right;  width: 96px; }

.renouv-row { border-top: 1px solid var(--border); cursor: pointer; }
.renouv-row > td { padding: var(--space-2) var(--space-3); }
.renouv-row:hover { background: var(--surface-2); }
.renouv-row:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.renouv-hint { color: var(--muted); font-size: var(--fs-xs); }

.renouv-chevron { transition: transform .15s ease; }
.renouv-row[aria-expanded="true"] .renouv-chevron { transform: rotate(90deg); }

.renouv-detail > td {
  padding: 0 var(--space-3) var(--space-3);
  background: var(--bg-1);
  border-top: 1px solid var(--border);
}
/* Les postes du client : une carte par poste, en grille qui se replie seule
   sur les ecrans etroits. */
.renouv-postes {
  display: grid; gap: var(--space-2);
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  padding: var(--space-3) 0;
}
.renouv-poste {
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
}
.renouv-poste-nom { font-weight: 600; }
.renouv-off { color: var(--muted); font-size: var(--fs-xs); margin-left: var(--space-2); }
.renouv-poste-signes {
  margin-top: var(--space-2);
  display: flex; flex-wrap: wrap; gap: var(--space-1);
}
.renouv-actions { padding-bottom: var(--space-3); }

/* Vide, la zone ne doit pas laisser d'espace mort sous le bouton. */
.renouv-texte:empty { display: none; }
.renouv-bloc {
  padding: var(--space-3);
  border: 1px solid var(--border);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius-sm);
  background: var(--surface);
  margin-bottom: var(--space-3);
}
.renouv-bloc-tete {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: var(--space-3); flex-wrap: wrap; margin-bottom: var(--space-2);
}
.renouv-txt { white-space: pre-wrap; line-height: 1.6; }
.renouv-bloc .btn-sm { margin-top: var(--space-3); }


/* ── Fiche equipement : toutes les sections a la meme largeur ──────────
   Les sections sont empilees dans un conteneur flex. Un flex item vaut
   `min-width: auto` : il REFUSE de retrecir sous la largeur de son contenu.
   Une seule section portant un tableau large elargissait donc la colonne,
   et l'ecart se voyait en descendant -- ces sections sont chargees plus bas.
   Meme nature que le `min-height: auto` de la barre laterale (07/08), sur
   l'autre axe. */
#tab-content > div { min-width: 0; }
#tab-content main { min-width: 0; }
#tab-content main > section { min-width: 0; }

/* Un tableau large defile DANS sa section au lieu de l'elargir. On borne le
   bloc charge paresseusement, pas la section entiere : celle-ci porte des
   <details> et des infobulles qu'un contexte de defilement rognerait. */
#tab-content .sec-detail { min-width: 0; overflow-x: auto; }
#tab-content main > section table { max-width: 100%; }


/* ── Schéma d'architecture (À propos › Architecture, admin) — 12/10/2026 ──────────────────────────
   Un SVG dessiné par about.js (saDessinerArchitecture, saDessinerCycle) à la largeur de son cadre : les
   positions sont des attributs, TOUTE l'apparence est ici, par les jetons du socle — le thème clair comme
   le sombre sont servis. Les textes sont des <text> : sélectionnables, mesurés avant le dessin dans la
   police de la page (.sa-mesure est le <svg> de mesure, posé le temps du dessin). Le cadre défile en
   largeur (.hx-defile) si le dessin est plus large que lui : jamais la page. */
.sa-cadre { overflow-x: auto; }
.sa-svg { display: block; max-width: 100%; height: auto; font-family: inherit; }
.sa-mesure { position: absolute; left: 0; top: 0; width: 0; height: 0; overflow: hidden; visibility: hidden; pointer-events: none; }

/* Les couches (groupes) et leurs titres */
.sa-groupe { fill: var(--bg-1); stroke: var(--border-strong); stroke-width: 1; }
.sa-groupe--serveur { stroke: var(--accent); stroke-width: 1.5; }
.sa-groupe--canal { fill: color-mix(in srgb, var(--accent) 8%, var(--bg-1)); stroke: var(--accent); }
.sa-groupe--ext { stroke-dasharray: 5 4; }
.sa-titre { font-size: var(--fs-xs); font-weight: 700; letter-spacing: .05em; text-transform: uppercase; fill: var(--dim); }
.sa-sous-titre { font-size: var(--fs-xs); fill: var(--dim); }
.sa-legende-section { font-size: var(--fs-xs); font-weight: 600; fill: var(--dim); }

/* Les boîtes : un composant HeXaControl (trait d'accent), un service tiers (tirets), un module (sa teinte),
   HeXaIA (cyan de marque). Le texte reste --text / --dim : les teintes ne portent que le trait et le fond. */
.sa-boite { fill: var(--bg-2); stroke: var(--border-strong); stroke-width: 1.2; }
.sa-boite--hx { stroke: var(--accent); }
.sa-boite--ext { stroke-dasharray: 4 3; }
.sa-boite--rmm { stroke: var(--mod-rmm); fill: color-mix(in srgb, var(--mod-rmm) 12%, var(--bg-2)); }
.sa-boite--psa { stroke: var(--mod-psa); fill: color-mix(in srgb, var(--mod-psa) 12%, var(--bg-2)); }
.sa-boite--glue { stroke: var(--mod-glue); fill: color-mix(in srgb, var(--mod-glue) 12%, var(--bg-2)); }
.sa-boite--ia { stroke: var(--accent-cyan); fill: color-mix(in srgb, var(--accent-cyan) 8%, var(--bg-2)); }   /* 8 % : à 12 %, le sous-titre (--dim) tombait à 4,44:1 en sombre */
.sa-lbl { font-size: var(--fs-sm); font-weight: 600; fill: var(--text); }
.sa-sous { font-size: var(--fs-xs); fill: var(--dim); }
.sa-puce { font-size: var(--fs-xs); font-weight: 600; fill: var(--text); }
/* L'icône d'une boîte : le sprite de la page, même trait que .ni, posé dans un <svg> imbriqué. */
.sa-ico { fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; color: var(--dim); overflow: visible; }
.sa-ico--hx { color: var(--accent-2); }
.sa-ico--rmm { color: var(--mod-rmm-texte); }
.sa-ico--psa { color: var(--mod-psa-texte); }
.sa-ico--glue { color: var(--mod-glue-texte); }
.sa-ico--ia { color: color-mix(in srgb, var(--accent-cyan) 70%, var(--text)); }

/* Les flèches, leurs étiquettes, les numéros d'étape */
.sa-fleche { fill: none; stroke: var(--dim); stroke-width: 1.6; }
.sa-pointe { fill: var(--dim); }
.sa-etiquette { font-size: var(--fs-xs); font-weight: 600; fill: var(--dim); }
.sa-etiquette-fond { fill: var(--bg-2); stroke: var(--border); }
.sa-numero { fill: var(--accent-2); }
.sa-numero-texte { font-size: var(--fs-xs); font-weight: 700; fill: var(--bg-2); }

/* La légende, en HTML sous le dessin */
.sa-legende { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-4); margin-top: var(--space-3); font-size: var(--fs-sm); color: var(--dim); }
.sa-legende__item { display: inline-flex; align-items: center; gap: var(--space-2); }
.sa-legende__item .ni { margin-right: 0; }
.sa-cle { display: inline-block; width: 18px; height: 12px; border-radius: 3px; border: 1.5px solid var(--border-strong); background: var(--bg-2); }
.sa-cle--hx { border-color: var(--accent); }
.sa-cle--ext { border-style: dashed; }
.sa-cle--rmm { border-color: var(--mod-rmm); background: color-mix(in srgb, var(--mod-rmm) 12%, var(--bg-2)); }
.sa-cle--psa { border-color: var(--mod-psa); background: color-mix(in srgb, var(--mod-psa) 12%, var(--bg-2)); }
.sa-cle--glue { border-color: var(--mod-glue); background: color-mix(in srgb, var(--mod-glue) 12%, var(--bg-2)); }
.sa-cle--ia { border-color: var(--accent-cyan); background: color-mix(in srgb, var(--accent-cyan) 8%, var(--bg-2)); }

/* ── Jauge de charge (Paramètres → Foxchip IA) ────────────────────────── */
.jauge { display: flex; flex-direction: column; gap: 4px; }
.jauge-piste {
  height: 8px;
  background: var(--bg-0);
  border: 1px solid var(--border);
  border-radius: 8px;
  overflow: hidden;
}
.jauge-barre { height: 100%; background: var(--color-ok); transition: width .3s ease; }
.jauge-barre--warn { background: var(--color-warn); }
.jauge-barre--crit { background: var(--color-crit); }
.jauge-tete { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; }
.jauge-val { font-size: var(--fs-lg); font-weight: 600; font-variant-numeric: tabular-nums; }
.jauge-lib { font-size: var(--fs-xs); color: var(--dim); }

/* Liens de contenu. La regle `a { color: inherit }` plus haut rend un lien
   indistinguable du texte : sans ces deux classes, chaque nouveau lien
   re-invente un style="color:..." en dur. */
.link-accent, .hx-lien { color: var(--accent); }
.link-accent:hover, .hx-lien:hover { text-decoration: underline; }
/* 19/10/2026 (Jérôme, page Pilotes : « pourquoi c'est tout blanc! ») : un <button class="hx-lien"> — le titre d'un pilote de la
   bibliothèque, qui ouvre son détail — gardait le fond blanc, la bordure et les marges du navigateur. Un bouton-lien se lit comme un lien. */
button.hx-lien { background: none; border: 0; padding: 0; margin: 0; font: inherit; line-height: inherit; text-align: inherit; cursor: pointer; }
.text-dim, .hx-attenue { color: var(--dim); }

/* --- Documentation developpeur (vue #doc-ia) ---------------------------
   Injectee dans <main id="view">, donc soumise au theme clair/sombre comme
   le reste : aucune couleur en dur, uniquement des tokens. */
.doc-wrap { max-width: 900px; }
.doc-wrap h1 { margin: 0 0 24px; }
.doc-wrap h2 { margin: 48px 0 16px; padding-bottom: 8px; border-bottom: 1px solid var(--border-strong); }
.doc-wrap h3 { margin: 32px 0 12px; color: var(--accent); }
.doc-wrap h4 { margin: 24px 0 8px; }
.doc-wrap p, .doc-wrap li { line-height: 1.65; }
.doc-wrap ul, .doc-wrap ol { margin: 0 0 16px; padding-left: 24px; }
.doc-wrap li { margin-bottom: 4px; }
.doc-wrap hr { border: 0; border-top: 1px solid var(--border); margin: 32px 0; }
.doc-retour { display: inline-block; margin-bottom: 16px; }
.doc-toc { background: var(--bg-1); border: 1px solid var(--border); border-radius: 8px; padding: 16px 24px; margin: 0 0 32px; }
.doc-toc h2 { margin: 0 0 12px; padding: 0; border: 0; font-size: var(--fs-md); }
.doc-toc ol { margin: 0; }
.doc-pre { background: var(--bg-0); border: 1px solid var(--border); border-radius: 8px;
           padding: 16px; overflow-x: auto; font-size: var(--fs-xs); line-height: 1.55; margin: 0 0 16px; }
.doc-note { border-left: 3px solid var(--color-info); background: var(--bg-1);
            padding: 12px 16px; margin: 0 0 16px; border-radius: 0 8px 8px 0; }
.doc-table-wrap { overflow-x: auto; margin: 0 0 16px; }
.doc-table { width: 100%; border-collapse: collapse; font-size: var(--fs-sm);
             font-variant-numeric: tabular-nums; }
.doc-table th { text-align: left; padding: 8px 12px; color: var(--dim);
                border-bottom: 2px solid var(--border-strong); white-space: nowrap; }
.doc-table td { padding: 8px 12px; border-bottom: 1px solid var(--border); vertical-align: top; }

/* ── Bloc EBP de la fiche client ───────────────────────────────────────────
   Trois classes plutot que des styles en dur : le formulaire client en
   comptait deja, et chacun echappe au theme clair/sombre. */
.ebp-actions { margin-top: 8px; }
.check-inline {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: var(--fs-sm);
  cursor: pointer;
}
.check-inline input[type="checkbox"] { margin: 0; cursor: pointer; }
.ebp-status { font-size: var(--fs-sm); margin-left: 8px; }
.field-note {
  margin: 4px 0 0;
  color: var(--muted);
  font-size: var(--fs-xs);
}

/* Choix des dossiers EBP (01/10/2026) : une case par dossier, sous la case
   « Creer aussi ce client dans EBP » (retrait de 24 px), ou au-dessus du bouton
   de synchronisation d'une fiche. Un dossier dont il manque un reglage reste
   visible, attenue : le taire laisserait croire que le client y a ete cree. */
.ebp-dossiers {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 16px;
  margin: 8px 0 0 24px;
}
.ebp-dossiers:empty { display: none; }
.ebp-dossiers--fiche { margin: 0 0 8px; }
.check-inline--off { color: var(--muted); cursor: not-allowed; }
.check-inline--off input[type="checkbox"] { cursor: not-allowed; }
/* Une case PRETE mais grisee par la case maitresse (« Creer aussi ce client dans
   EBP » decochee) s'attenue aussi : sans cela, rien ne distinguait un choix
   inactif d'un choix actif. */
.check-inline:has(input:disabled) { color: var(--muted); cursor: not-allowed; }
.check-inline input[type="checkbox"]:disabled { cursor: not-allowed; }

/* Bouton « Tester » (SNMP) de la fiche client et son retour, sous le champ des
   communautes (05/10/2026, tenants.js tfSnmpTester). Un refus « l'agent de la
   sonde ne sait pas encore » est un etat, pas une panne : ambre, pas rouge.
   ⚠ Le retour vide reste affiche (pas de :empty -> display:none) : c'est une
   zone aria-live, un lecteur d'ecran ne lit pas ce qui apparait dans une zone
   qui n'existait pas. */
button.snmp-test-btn    { white-space: nowrap; padding: var(--space-2) var(--space-3); font-size: var(--fs-sm); }
.snmp-test-retour       { margin-top: var(--space-2); font-size: var(--fs-sm); color: var(--muted); }
.snmp-test-retour--ok   { color: var(--state-ok); }
.snmp-test-retour--warn { color: var(--state-warn); }
.snmp-test-retour--crit { color: var(--state-crit); }

/* ── Règle d'alerte « Menace SentinelOne » (04/10/2026) ─────────────────────
   Le filtre propre à ce type, sous la grille Type / Seuil de l'éditeur
   (public/js/app-alertrules.js, arS1Bloc). Masqué par l'attribut hidden pour
   tout autre type — [hidden] fait autorité, cf. plus haut. */
.ar-s1 {
  margin: var(--space-3) 0 0;
  padding: var(--space-2) var(--space-4) var(--space-3);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  min-inline-size: 0;
}
.ar-s1 > legend {
  padding: 0 var(--space-1);
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--dim);
}
.ar-s1__cases {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-4);
}
.ar-s1 .check-inline { margin-top: 0; }
/* Aperçu du filtre : ce qu'il lèverait maintenant (revues du 04/10). En
   couleur d'avertissement quand il créerait beaucoup de tickets d'un coup. */
.ar-s1__apercu {
  margin: var(--space-2) 0 0;
  font-size: var(--fs-sm);
  color: var(--dim);
  font-variant-numeric: tabular-nums;
}
.ar-s1__apercu:empty { display: none; }
.ar-s1__apercu--alerte { color: var(--color-warn); font-weight: 600; }
/* Précision discrète dans un libellé de l'éditeur de règle. */
.ar-indication { color: var(--muted); font-size: var(--fs-xs); font-weight: normal; }
/* Galerie : un modèle déjà couvert par une règle en place. */
.ar-galerie__deja { margin: 0 0 var(--space-2); font-size: var(--fs-sm); color: var(--dim); }
.ar-galerie__bouton { width: 100%; }

/* ── Verdict d'un test de modèle d'export EBP ──────────────────────────────
   La sévérité passe par une classe, pas par un style en dur : un style en dur
   échappe au thème clair/sombre. Le liseré porte le sens, le fond reste neutre. */
.ebp-verdict {
  border-left: 3px solid var(--border-strong);
  padding: 10px 12px;
  background: var(--surface-2);
  border-radius: var(--radius-sm);
}
.ebp-verdict--ok   { border-left-color: var(--state-ok); }
.ebp-verdict--warn { border-left-color: var(--state-warn); }
.ebp-verdict--crit { border-left-color: var(--state-crit); }
.ebp-verdict--ok   > strong { color: var(--state-ok); }
.ebp-verdict--warn > strong { color: var(--state-warn); }
.ebp-verdict--crit > strong { color: var(--state-crit); }
.ebp-verdict code { word-break: break-all; }

/* ── État d'un envoi de fiche client vers EBP ─────────────────────────────── */
.ebp-etat--ok   { color: var(--state-ok); }
.ebp-etat--warn { color: var(--state-warn); }
.ebp-etat--crit { color: var(--state-crit); }

/* ── Réglages → EBP, rangés PAR DOSSIER (01/10/2026) ───────────────────────
   Un bloc commun aux deux dossiers, puis un bloc par dossier avec ses propres
   tests. Tokens uniquement (clair + sombre, espacements --space-*). */
.ebp-reglages {
  display: grid;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
  padding: var(--space-4);
  background: var(--surface-2);
  border-radius: var(--radius-sm);
}
.ebp-reglages__titre { display: flex; align-items: center; gap: var(--space-2); margin: 0; font-size: var(--fs-md); }
/* La marge d'une icone .ni s'ajouterait au gap du titre (16 px au lieu de 8). */
.ebp-reglages__titre .ni { margin-right: 0; }
.ebp-reglages > .field-note { margin: 0; }
.ebp-reglages__pied { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); }
.ebp-reglages__etat { color: var(--muted); font-size: var(--fs-xs); font-weight: 400; }
.ebp-groupe {
  padding: var(--space-3) var(--space-4) var(--space-4);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
.ebp-groupe__tete { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--space-1) var(--space-2); margin: 0 0 var(--space-3); }
.ebp-groupe__titre { margin: 0; font-size: var(--fs-sm); font-weight: 700; }
.ebp-groupe__role { color: var(--muted); font-size: var(--fs-xs); }
.ebp-groupe__large { grid-column: 1 / -1; }
.ebp-groupe > .field-note { margin: var(--space-3) 0 0; }
.ebp-groupe__actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); margin-top: var(--space-3); }
.ebp-groupe__resultat { margin-top: var(--space-3); }
/* Annonce du verdict pour un lecteur d'ecran : toujours dans l'arbre
   d'accessibilite (une region cachee pendant qu'on la remplit n'est pas lue),
   invisible a l'ecran (le meme texte est dans le rapport). */
.ebp-groupe__annonce {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
.ebp-etapes { display: grid; gap: var(--space-2); margin: var(--space-2) 0 0; padding: 0; list-style: none; }
.ebp-etape { display: grid; grid-template-columns: auto 1fr; align-items: start; gap: var(--space-2); }
/* Meme largeur pour « OK », « À vérifier » et « Échec » : les noms d'etape s'alignent. */
.ebp-etape .badge { justify-content: center; min-width: 5.5rem; }
.ebp-etape__nom { font-weight: 600; }
@media (max-width: 600px) {
  .ebp-reglages { padding: var(--space-3); }
  .ebp-groupe { padding: var(--space-2) var(--space-3) var(--space-3); }
}

/* ── Visionneuse d'images ────────────────────────────────────────────────── */
/* ⚠ Le centrage se fait par margin:auto sur l'image, PAS par align-items sur le
   conteneur : un enfant plus grand que son conteneur flex centre voit son bord
   HAUT rogne et devient inatteignable au defilement. margin:auto est la seule
   forme qui centre ET laisse defiler. */
.visionneuse {
  position: fixed; inset: 0; z-index: 2000;
  background: rgba(3, 8, 20, 0.93);
  display: flex; overflow: auto;
  padding: 52px 24px 24px;
}
.vsn-img {
  margin: auto; display: block;
  max-width: 100%; max-height: calc(100vh - 88px);
  object-fit: contain; cursor: zoom-in;
  background: #fff; border-radius: 4px;
}
/* Taille reelle : l'image peut deborder, le conteneur defile. */
.vsn-img.vsn-agrandi { max-width: none; max-height: none; cursor: zoom-out; }
.vsn-barre {
  position: fixed; top: 0; left: 0; right: 0;
  display: flex; align-items: center; gap: 14px;
  padding: 10px 16px; color: #fff; font-size: 0.82rem;
  background: linear-gradient(180deg, rgba(0,0,0,0.62), rgba(0,0,0,0));
}
.vsn-titre { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.vsn-compteur { font-variant-numeric: tabular-nums; opacity: 0.85; }
.vsn-onglet { color: #fff; opacity: 0.85; text-decoration: none; border-bottom: 1px solid currentColor; }
.vsn-onglet:hover { opacity: 1; }
.vsn-fermer { background: transparent; border: 0; color: #fff; font-size: 1.25rem; line-height: 1;
              cursor: pointer; padding: 2px 8px; border-radius: 6px; }
.vsn-nav {
  position: fixed; top: 50%; transform: translateY(-50%);
  background: transparent; border: 0; color: #fff; font-size: 2.6rem; line-height: 1;
  cursor: pointer; padding: 4px 16px; border-radius: 8px;
}
.vsn-prec { left: 6px; }
.vsn-suiv { right: 6px; }
.vsn-fermer:hover, .vsn-nav:hover { background: rgba(255, 255, 255, 0.16); }
.vsn-fermer:focus-visible, .vsn-nav:focus-visible, .vsn-onglet:focus-visible {
  outline: 2px solid var(--accent-2); outline-offset: 2px;
}
@media (max-width: 640px) {
  .visionneuse { padding: 48px 8px 8px; }
  .vsn-nav { font-size: 2rem; padding: 4px 8px; }
}

/* ── Prise en main RustDesk : bandeau de secours ────────────────────────────
   ⚠⚠ Aucun navigateur ne sait dire si un gestionnaire de protocole existe, et
   la boite « Ouvrir RustDesk ? » de Chrome est TAB-MODALE : elle ne fait pas
   perdre le focus a la fenetre. Toute detection fondee sur « blur » conclut donc
   a tort que rien ne s'est lance. Plutot que de deviner, on lance et on laisse
   la sortie de secours a un clic.
   NON BLOQUANT par construction : ce bandeau n'empeche jamais de travailler. */
.rd-bandeau {
  position: fixed;
  bottom: 24px;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 12px 16px;
  max-width: min(560px, calc(100vw - 32px));
  background: var(--bg-2);
  border: 1px solid var(--border-strong);
  border-left: 3px solid var(--accent);
  border-radius: 8px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
  font-size: var(--fs-sm);
  z-index: 9050;            /* sous la fenetre RustDesk (9100) : elle prime */
}
.rd-bandeau__txt   { color: var(--text); line-height: 1.45; }
.rd-bandeau__sub   { color: var(--dim); font-size: var(--fs-xs); margin-top: 4px; }
.rd-bandeau__btn   { white-space: nowrap; }
.rd-bandeau__fermer{ padding: 4px 8px; }

/* ============================================================================
   Extensions (plugins.js). Depuis le lot L17, l'écran porte les composants du
   socle (hx-carte, hx-badge, hx-bouton, hx-grille). 25/10/2026 (revue R25-10) :
   les classes ext-* de l'ancienne place de marché, sans emploi, sont retirées.
   ============================================================================ */

.ext-grille {
  display: grid; gap: var(--space-4);
  grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
}

/* ---- Evolution croisee de la facturation (psa-billing.js) ----
   Les deux graphiques d'evolution donnent le TOTAL par mois ; ce tableau dit QUI
   le fait bouger. Tableau et non faisceau de courbes : a quinze series sur treize
   mois, des courbes superposees ne se lisent pas, et ce qu'on vient chercher ici
   est un chiffre precis, pas une impression.
   La ligne de total est l'ANCRE DE CONFIANCE : le detail totalise exactement la
   courbe du dessus (verifie en base, ecart 0,00 EUR sur les deux axes). Si elle
   ne correspondait plus, tout le tableau serait a jeter. */
.evo-tete       { display: flex; justify-content: space-between; align-items: center;
                  gap: 16px; flex-wrap: wrap; margin-bottom: 16px; }
.evo-titre      { margin: 0; font-size: var(--fs-md); display: flex; align-items: center; gap: 8px; }
.evo-controles  { display: flex; gap: 12px; flex-wrap: wrap; }

.evo-seg        { display: inline-flex; border: 1px solid var(--border);
                  border-radius: 8px; overflow: hidden; }
.evo-seg button { appearance: none; border: 0; background: var(--bg-2); color: var(--dim);
                  font: inherit; font-size: var(--fs-sm); padding: 6px 12px;
                  cursor: pointer; white-space: nowrap; }
.evo-seg button + button              { border-left: 1px solid var(--border); }
.evo-seg button:hover                 { color: var(--text); }
.evo-seg button[aria-pressed="true"]  { background: var(--accent); color: #fff; }
.evo-seg button:focus-visible         { outline: 2px solid var(--accent); outline-offset: -2px; }

/* tabular-nums : sans lui, une colonne qui passe de 9 a 14 fait sauter tout le reste. */
.evo-tbl        { width: 100%; border-collapse: collapse; font-size: var(--fs-sm);
                  font-variant-numeric: tabular-nums; }
.evo-tbl th,
.evo-tbl td                   { padding: 6px 10px; white-space: nowrap; text-align: right; }
.evo-tbl th:first-child,
.evo-tbl td:first-child       { text-align: left; }
.evo-tbl thead th             { color: var(--dim); font-weight: 600;
                                border-bottom: 1px solid var(--border); }
.evo-tbl tbody td             { color: var(--text); border-bottom: 1px solid var(--border); }
.evo-tbl tbody td:first-child { max-width: 24ch; overflow: hidden; text-overflow: ellipsis; }
.evo-tbl tbody tr:hover td    { background: var(--bg-2); }
.evo-tbl tfoot td             { font-weight: 700; color: var(--text);
                                border-top: 1px solid var(--border-strong); }
.evo-tbl .evo-col-total       { font-weight: 600; }
.evo-nul        { color: var(--dim); }
.evo-spark      { display: inline-block; width: 70px; height: 22px; vertical-align: middle; }
.evo-var--haut  { color: var(--color-ok); }
.evo-var--bas   { color: var(--color-crit); }
.evo-var--plat  { color: var(--dim); }
.evo-note       { margin: 12px 0 0; font-size: var(--fs-xs); color: var(--muted); }

/* ---- Modale de deploiement de paquet (packages.js) ----
   Le champ de recherche et son compteur de correspondances. Le compteur existe
   parce qu'une liste vide ne dit pas si le filtre n'a rien trouve ou s'il n'a
   pas compris : « 0 / 485 » repond a la question. */
.pdp-recherche       { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
.pdp-recherche input { flex: 1; min-width: 0; }
.pdp-count           { font-size: var(--fs-xs); color: var(--muted);
                       font-variant-numeric: tabular-nums; white-space: nowrap; }

/* Lignes de la liste d'equipements de la modale de deploiement (packages.js).
   ⚠ Le display vit ICI et pas dans un style en ligne : un style en ligne
     l'emporte sur l'attribut hidden, et le filtre ne masquait donc rien. */
.pdp-ligne          { display: flex; align-items: center; gap: 8px;
                      padding: 4px 6px; cursor: pointer;
                      border-bottom: 1px solid var(--border); }
.pdp-ligne[hidden]  { display: none !important; }
.pdp-ligne-nom      { flex: 1; }

/* Avertissement en ligne dans le bandeau de sante d'une fiche machine
   (views-device-detail.js). Reprend telles quelles les valeurs qui etaient
   ecrites en dur juste a cote, mais par des jetons : rien ne bouge a l ecran,
   le theme reprend la main. --crit est reserve a ce qui est PROUVE (agent
   muet), l ambre a ce qui est suspect (versions melangees) : la severite doit
   rester lisible, sinon on apprend a ignorer le rouge. */
.dev-alerte-inline       { font-weight: 600; color: var(--state-warn); }
.dev-alerte-inline--crit { color: var(--state-crit); }

/* Bloc replie « Ce qui est livre » de l'onglet Roadmap (about.js).
   Le contenu ne se deploie qu'a la demande : il fait 175 lignes et n'a rien a
   faire sous les yeux de qui vient lire ce qui RESTE a faire. */
.roadmap-livre           { padding: 18px 32px; margin-top: 18px; line-height: 1.65; }
.roadmap-livre > summary { cursor: pointer; font-weight: 600; list-style: revert; }
.roadmap-livre[open] > summary { margin-bottom: 12px;
                                 border-bottom: 1px solid var(--border);
                                 padding-bottom: 12px; }

/* ---- Recherche instantanee dans une liste deja rendue (tenants.js) ----
   Le champ absorbe la place restante de la .toolbar.
   ⚠ C est `flex` qui dimensionne, PAS `width` : la regle globale des <input>
     (input:not([type=checkbox])... plus haut) les etire a 100 % avec une
     specificite de (0,5,1) qu une classe ne peut pas battre. Un flex-basis
     defini rend `width` sans effet sur l axe principal — d ou le 260px ici. */
.champ-recherche     { flex: 1 1 260px; min-width: 140px;
                       appearance: none; -webkit-appearance: none; }

/* Case a cocher de filtrage posee dans une .toolbar. Reprend telles quelles
   les valeurs qui etaient ecrites en dur dans tenants.js (--fs-sm vaut
   justement 0.82rem), mais par des jetons : le theme reprend la main.
   ⚠ .toolbar input impose un padding de 8px 12px, monstrueux sur une case a
     cocher — d ou la remise a zero, plus specifique. */
.case-filtre               { display: inline-flex; align-items: center; gap: 8px;
                             cursor: pointer; font-size: var(--fs-sm); color: var(--muted); }
.toolbar .case-filtre input { padding: 0; margin: 0; width: auto; }

/* Compteur de correspondances — meme raison d etre que .pdp-count ci-dessus :
   une liste vide ne dit pas si le filtre n a rien trouve ou s il n a pas
   compris. tabular-nums pour que « 9 / 152 » et « 11 / 152 » ne sautent pas. */
.liste-compte        { font-size: var(--fs-xs); color: var(--muted);
                       font-variant-numeric: tabular-nums; white-space: nowrap; }

/* Message affiche quand le filtre ne laisse plus rien. Aligne sur le message
   « vide » que rend renderTable(), qui lui ne sort que si la donnee est vide. */
.liste-vide          { margin: 0; padding: 16px; text-align: center; color: var(--muted); }


/* ============================================================================
   ECRAN CLIENTS (tenants.js, viewTenants) — 09/09/2026
   Sort les 30 declarations CSS qui vivaient en ligne dans le HTML genere par le
   JS (14 attributs style=) et les 5 emoji employes comme icone. Aucune valeur
   nouvelle : tout est repris aux jetons deja definis dans les DEUX themes.
   ============================================================================ */

/* Texte secondaire d une cellule : « Actif », les tirets « — » de valeur
   absente, « N archives ». SŒUR de .text-dim, qui existe pour la meme raison.
   ⛔ NE PAS employer .text-dim a la place : elle vaut var(--dim), un AUTRE
      jeton. Les spans concernes sont en --muted ; basculer sur --dim baisse le
      contraste dans les deux themes et laisserait deux gris pour un seul role
      sur la meme ligne.
   ⛔ Aucun font-size : table.fox-table donne deja --fs-sm aux cellules, et en
      poser un empecherait la regle mobile « table td { font-size:13px } »
      d atteindre le span par heritage. */
.text-muted        { color: var(--muted); }

/* Deuxieme ligne d une cellule, sous la valeur principale : le site web sous le
   nom du client, la date d archivage sous le badge. Un seul role, donc UNE
   seule taille — le fichier melangeait 0.78rem, qui n est aucun des six jetons,
   et 0.72rem recopie a la main.
   --fs-xs et pas --fs-sm : la cellule vaut deja --fs-sm ; mettre le sous-texte
   a la taille du nom du client detruirait la hierarchie.
   Le saut de ligne reste porte par le <br> du JS : pas de display:block ici,
   sinon la mise en page bouge (le badge « Archive » est en inline-flex). */
.cellule-sous      { color: var(--muted); font-size: var(--fs-xs); }

/* Cellule dont la valeur porte des chiffres alignes en colonne sur 152 lignes :
   code postal (Adresse) et numero (Telephone). Charte 6. tabular-nums n agit
   que sur les chiffres : sur l adresse il aligne les codes postaux sans rien
   changer a « Paris France ».
   ⛔ AUCUN font-size : le 0.85rem qui etait en ligne etait hors echelle ET sans
      effet visible (la cellule est a --fs-sm = 0.82rem). Le retirer rend en
      prime le retrecissement mobile a 13px, que le style en ligne bloquait.
   ⛔ Pas de white-space:nowrap : l adresse concatene CP + ville + pays et
      elargirait la table. */
.cellule-chiffres  { font-variant-numeric: tabular-nums; }

/* Compteur precede de son pictogramme : colonnes « Equipements » et
   « Contacts ». Meme montage que .au-act / .au-act .ni, le patron maison pour
   « icone du sprite + libelle » : la geometrie vit ICI, le balisage n emet
   qu un .ni nu.
   ⛔ Ne PAS utiliser .ni--seule : elle est reservee a une icone SEULE dans un
      bouton ; ici un nombre la suit.
   L icone prend --muted parce que c est le NOMBRE qu on lit ; le pictogramme
   dit seulement de quoi il s agit. */
.cellule-compte     { display: inline-flex; align-items: center;
                      gap: var(--space-1);
                      font-variant-numeric: tabular-nums; white-space: nowrap; }
.cellule-compte .ni { width: 14px; height: 14px; margin: 0; color: var(--muted); }

/* Groupe de boutons d action en fin de ligne de tableau (colonne admin).
   ⛔ Pas de white-space:nowrap : dans un conteneur flex c est flex-wrap qui
      gouverne, la declaration serait morte.
   Le gap remplace les nœuds de texte blanc entre les boutons, qui disparaissent
   en mise en page flex. */
.cellule-actions   { display: inline-flex; align-items: center;
                     gap: var(--space-1); }

/* Ligne de la liste des clients : un clic ouvre le tableau de bord PSA du
   client, ou sa Vue 360 s il est archive.
   ⚠⚠ LE NOM NE DOIT CONTENIR NI « tenant », NI « group », NI « section »,
      NI « nas », NI « subheader ». tablesort.js:368 (GROUP_CLASS_RE) lit dans
      une telle classe de <tr> une table GROUPEE, marque la table initialisee et
      n arme AUCUN en-tete : le tri des 152 clients disparaitrait sans erreur ni
      message. « ligne-tenant » et « tenant-row » — les deux noms naturels —
      correspondent a la regex ; « ligne-client » non.
   ⛔ NE JAMAIS DECLARER display ICI. Le filtre de recherche masque par
      tr.hidden ; toute regle display posee la rouvrirait le debat a chaque
      evolution de la feuille — c est ce qui a oblige .pdp-ligne a ajouter son
      rattrapage .pdp-ligne[hidden]. */
.ligne-client          { cursor: pointer; }

/* Client archive : attenue, pas efface. C est la colonne « Etat » qui porte le
   sens, l attenuation ne fait qu appuyer.
   ⚠ 0.85 et NON 0.72, la valeur qui etait en ligne — et que j avais moi-meme
     remontee de 0.55 hier en croyant regler la lisibilite. Mesures de contraste
     sur le texte --muted que cette ligne contient justement (date d archivage,
     « N archives », tirets) :
       0.72 -> 3,74:1 en clair et 4,41-4,49:1 en sombre : SOUS le seuil AA de
               4,5:1 dans LES DEUX themes, et le sombre est le theme par defaut ;
       0.85 -> 5,10:1 en clair et 5,69-5,87:1 en sombre.
     0.80 est refuse : le clair y retombe a 4,51:1, marge nulle sur un fond de
     carte composite qu aucune mesure n approche exactement. Aucune echelle
     d opacite n existe dans la feuille : la valeur nue est legitime ici. */
.ligne-client--archive { opacity: 0.85; }

/* Code de compte EBP affiche en pastille.
   ⛔ PLUS DE VERT. Le style en ligne peignait rgba(34,197,94,0.12) — couleur
      litterale, hors jetons — avec color:var(--state-ok). Un numero de compte
      comptable n est pas un ETAT « OK » : c est de la severite posee sur une
      donnee neutre, ce que la feuille refuse par ecrit (« Severite != accent »).
   ⚠ Cela corrige AU PASSAGE le defaut le plus visible : la regle globale `code`
     impose « border: 1px solid rgba(var(--accent-rgb),0.18) » que le style en
     ligne ne surchargeait PAS — la pastille etait verte A BORDURE BLEUE.
   ⛔ NE RIEN REDECLARER D AUTRE : padding 1px 6px et border-radius 4px sont
      deja ceux de `code` ; les remplacer par --space-2 (8px) et --radius-sm
      (6px) changerait le dessin et desynchroniserait cette pastille de tous les
      autres <code> du produit.
   font-size est en revanche OBLIGATOIRE : `code` la donne en 0.84em, relatif a
   la cellule (0.82rem) = 0.689rem — sans cette ligne la pastille retrecit de
   16 % sur un code qu on recopie a la main dans EBP. */
.code-ebp          { font-size: var(--fs-sm); }

/* Variante accent de .btn-sm : une action de NAVIGATION mise en avant (le lien
   « 360° » de la liste des clients). Ne cree aucune couleur : le triplet est
   celui, deja en service, de button.toolbar.active.
   ⚠ DOIT rester APRES .btn-sm et .btn-sm:hover : specificite egale, c est
     l ordre du fichier qui decide.
   Le :hover est obligatoire, sinon .btn-sm:hover repeindrait la pastille en
   neutre au survol. On n y touche QUE la bordure : passer le fond a
   --accent-glow donnerait 45 % d accent en theme sombre, soit ~2,6:1 pour un
   libelle a 0.72rem. */
.btn-sm--accent        { border-color: var(--accent);
                         background: var(--color-accent-soft-bg);
                         color: var(--accent);
                         white-space: nowrap; }
.btn-sm--accent:hover  { border-color: var(--accent-2);
                         background: var(--color-accent-soft-bg); }

/* Bouton « Ouvrir un chat » de la fiche ticket (tickets.js).
   Memes dimensions que ses voisins, qui portent encore les leurs en dur — a
   reprendre pour eux quand on repassera sur ce bandeau. */
.tk-btn-chat            { padding: 8px 16px; font-size: var(--fs-sm);
                          display: inline-flex; align-items: center; gap: 6px; }
.tk-btn-chat[disabled]  { opacity: 0.5; cursor: not-allowed; }

/* ---------- Rendu Markdown (roadmap, guide, notes de version) -------------
   ⚠ Le parseur (public/js/about.js) regroupe desormais les lignes d'un meme
   paragraphe : une ligne du fichier .md n'est PAS un paragraphe. Ces regles
   habillent les blocs qu'il produit.
   ⛔ PAS DE `max-width` ICI. La regle de confort de lecture (~65 a 90
   caracteres) voudrait qu'on borne la ligne ; le proprietaire a demande
   explicitement le contraire le 10/09/2026 — « pourquoi ca ne prend pas toute
   la largeur ». Le paragraphe occupe donc la carte. Si la ligne devient trop
   longue sur un tres grand ecran, c'est la CARTE qu'on bornera, pas le
   paragraphe : brider le texte ici redonnerait exactement le defaut signale. */
.md-p {
  margin: 8px 0;
}

.md-quote {
  margin: 14px 0;
  padding: 2px 0 2px 16px;
  border-left: 3px solid var(--accent-2);
  background: rgba(var(--accent-rgb), 0.05);
  border-radius: 0 6px 6px 0;
  color: var(--muted);
}
.md-quote .md-p { margin: 6px 0; }
.md-quote .md-p:first-child { margin-top: 0; }
.md-quote .md-p:last-child  { margin-bottom: 0; }

.md-code {
  margin: 14px 0;
  padding: 12px 16px;
  background: var(--bg-0);
  border: 1px solid var(--border);
  border-radius: 8px;
  /* ⚠ Un bloc de code ne se replie pas : il defile dans SON conteneur, sinon
     c'est la page entiere qui defile lateralement. */
  overflow-x: auto;
  font-size: 0.86rem;
  line-height: 1.55;
}
.md-code code {
  background: none;
  padding: 0;
  color: var(--text);
  white-space: pre;
}
/* La langue annoncee par ``` sert d'etiquette discrete. */
.md-code[data-langue]::before {
  content: attr(data-langue);
  display: block;
  margin-bottom: 8px;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 1px;
  color: var(--dim);
}

/* ── Anneaux de déploiement des correctifs ────────────────────────────────
   13/09/2026. Un écran qui doit DIRE quand un anneau ne sert à rien : un
   niveau d'une machine avec un quorum de deux paraît normal dans une liste,
   alors qu'il ne retiendra jamais rien et gèlera tout l'aval. */
.anneaux { display: flex; flex-direction: column; gap: 8px; }

.anneau {
  display: grid;
  grid-template-columns: 2.5rem minmax(9rem, 1.4fr) minmax(16rem, 2fr) minmax(8rem, 1fr) auto;
  gap: 12px;
  align-items: center;
  padding: 12px 14px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
/* Un anneau désactivé reste LISIBLE : on ne cache pas un niveau, on le montre éteint. */
.anneau--inactif { opacity: .6; }
.anneau--inactif .anneau__nom::after {
  content: ' — désactivé';
  color: var(--muted);
  font-weight: normal;
  font-size: var(--fs-xs, .75rem);
}
.anneau__pos {
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  color: var(--muted);
  text-align: center;
}
.anneau__nom { font-weight: 600; }
.anneau__slug {
  display: block;
  font-size: var(--fs-xs, .75rem);
  color: var(--muted);
  font-weight: normal;
}
.anneau__reglages { display: flex; gap: 12px; flex-wrap: wrap; align-items: center; }
.anneau__reglages label {
  display: flex; align-items: center; gap: 6px;
  margin: 0; white-space: nowrap;
  font-size: var(--fs-xs, .75rem);
}
.anneau__reglages input[type="number"] { width: 5.5rem; }
.anneau__effectif { font-size: var(--fs-sm, .85rem); font-variant-numeric: tabular-nums; }
.anneau__actions { display: flex; gap: 6px; align-items: center; }
.anneau__actions button { padding: 4px 10px; font-size: var(--fs-xs, .75rem); }

/* ⚠ L'avertissement qui compte : sans lui, l'écran ment par omission. */
.anneau__alerte {
  grid-column: 1 / -1;
  margin: 0;
  padding: 8px 12px;
  border-radius: var(--radius);
  background: var(--state-warn-soft);
  color: var(--state-warn);
  font-size: var(--fs-xs, .75rem);
  line-height: 1.5;
}
.anneau__note {
  grid-column: 1 / -1;
  margin: 0;
  font-size: var(--fs-xs, .75rem);
  color: var(--muted);
}

/* Compléments de l'écran des anneaux — introduits pour ne PAS poser de style en
   ligne dans du code neuf (la charte l'interdit : un style en ligne échappe au
   thème clair/sombre et se recopie d'écran en écran). */
.card--suite { margin-top: 16px; }
.barre-actions--titre { margin-top: 0; justify-content: space-between; }
.bloc-titre { flex: 1; min-width: 280px; }
.bloc-titre h2 { margin: 0 0 6px; }
.bloc-titre .subtitle { margin: 0; font-size: var(--fs-sm, 0.85rem); }
.anneau__alerte--bandeau,
.anneau__note--bandeau { margin-bottom: 12px; }
.anneau__prouvantes { color: var(--muted); }
.anneau__suppr { color: var(--color-crit); }

/* ── Réglage d'anneau sur la fiche équipement ─────────────────────────────
   Reproduit la présentation des cases voisines, mais en classe : du code neuf
   n'ajoute pas de style en ligne (la charte l'interdit). */
.dev-reglage {
  display: inline-flex; align-items: center; gap: 6px;
  cursor: default; line-height: 1.3;
  font-size: var(--fs-sm, 0.88rem); color: var(--text);
  text-transform: none; letter-spacing: 0; margin: 0;
}
.dev-reglage__select { width: auto; min-width: 12rem; padding: 4px 8px; font-size: var(--fs-sm, 0.85rem); }

/* ── Choisir des machines pour un anneau ──────────────────────────────────
   ⚠ La recherche est exigée avant d'afficher quoi que ce soit : 565 machines
   dans une liste ne se choisissent pas, elles se subissent. */
.anneau__picker { grid-column: 1 / -1; }
/* 25/10/2026 (revue R11-19) : --bg n'existe dans aucun thème, la liste n'avait pas de fond (propriété ignorée) ;
   --bg-1, le fond d'un panneau posé dans la ligne de l'anneau. */
.picker {
  margin-top: 10px; padding: 12px;
  background: var(--bg-1); border: 1px solid var(--border); border-radius: var(--radius);
}
.picker__q { width: 100%; margin-bottom: 8px; }
.picker__resultats {
  max-height: 16rem; overflow-y: auto;
  font-size: var(--fs-sm, 0.85rem); color: var(--muted);
}
.picker__ligne {
  display: grid;
  grid-template-columns: 1.5rem minmax(8rem, 2fr) minmax(6rem, 1.5fr) minmax(5rem, 1fr);
  gap: 8px; align-items: center;
  padding: 5px 6px; margin: 0;
  border-radius: var(--radius);
  text-transform: none; letter-spacing: 0; font-weight: normal;
  color: var(--text); cursor: pointer;
}
.picker__ligne:hover { background: var(--surface-2); }
/* Une machine déjà dans cet anneau reste VISIBLE : la masquer ferait croire
   qu'elle n'existe pas, et on la chercherait ailleurs. */
.picker__ligne--deja { opacity: .55; cursor: default; }
.picker__nom { font-weight: 600; }
.picker__client,
.picker__anneau { color: var(--muted); font-size: var(--fs-xs, 0.75rem); }
.picker__pied {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; margin-top: 10px; padding-top: 10px;
  border-top: 1px solid var(--border);
}
.picker__compte { font-size: var(--fs-sm, 0.85rem); color: var(--muted); }

/* Les boutons d une barre d actions restent groupes a droite. */
.barre-actions__boutons { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }

:root                      { --gp-dossier-teinte: #e0a43c; }
:root[data-theme="light"]  { --gp-dossier-teinte: #a9701a; }

/* GLUE - coffre : dossiers repliables --------------------------------------
   L'en-tete est un <button>, pas un <h3> avec un onclick : elle doit etre
   atteignable au CLAVIER et annoncer son etat (aria-expanded). Un <div>
   cliquable n'est ni l'un ni l'autre.
   Le corps porte overflow-x:auto - le tableau a sept colonnes et debordait
   deja avant ce changement. */
.gp-barre-dossiers {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-2);
  margin-top: var(--space-4);
}
.gp-dossier { margin-bottom: var(--space-3); }
.gp-dossier-tete {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  width: 100%;
  padding: 0;
  margin: 0;
  background: none;
  border: 0;
  color: var(--accent);
  font-size: var(--fs-base);
  font-weight: 700;
  line-height: 1.4;
  text-align: left;
  cursor: pointer;
}
.gp-dossier-tete:hover { color: var(--accent-2); }
.gp-dossier-tete:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 4px;
}
.gp-dossier-tete .ni { margin-right: 0; }
.gp-icone-dossier { color: var(--gp-dossier-teinte); }
.gp-chevron { transition: transform 0.15s ease; }
.gp-dossier-tete[aria-expanded="true"] .gp-chevron { transform: rotate(90deg); }
/* Le nom tient sur UNE ligne : un dossier qui passe a la ligne decale le
   compteur et casse l'alignement de toute la colonne. */
.gp-dossier-nom {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.gp-dossier-compte {
  flex: none;
  color: var(--muted);
  font-weight: normal;
  font-size: var(--fs-sm);
  font-variant-numeric: tabular-nums;
}
.gp-dossier-corps { margin-top: var(--space-3); overflow-x: auto; }
.gp-dossier-corps[hidden] { display: none; }
@media (prefers-reduced-motion: reduce) {
  .gp-chevron { transition: none; }
}

/* NAS - encadre reseau ------------------------------------------------------
   Ecrit en classes et non en styles en dur : ce fichier JS en porte deja pres
   de mille, et la charte veut que ce nombre baisse a chaque intervention.
   Le tableau des interfaces vit dans son propre conteneur a defilement : six
   colonnes ne tiennent pas sur un portable, et la page ne doit jamais glisser
   lateralement. */
.nas-reseau-tete {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0 0 var(--space-3);
  font-size: var(--fs-base);
  font-weight: 700;
}
.nas-reseau-tete .ni { margin-right: 0; }
.nas-wan {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3) var(--space-6);
  padding: var(--space-3);
  background: var(--bg-2);
  border-radius: 6px;
  margin-bottom: var(--space-3);
}
.nas-champ { display: flex; flex-direction: column; gap: var(--space-1); min-width: 0; }
.nas-etiq {
  font-size: var(--fs-xs);
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 1px;
  white-space: nowrap;
}
.nas-val { font-size: var(--fs-sm); }
.nas-mono {
  font-family: 'Cascadia Code', 'Consolas', 'Courier New', monospace;
  font-variant-numeric: tabular-nums;
}
.nas-wan-ip { font-size: var(--fs-md); font-weight: 700; }

.nas-if-zone { overflow-x: auto; }
.nas-if { width: 100%; border-collapse: collapse; font-size: var(--fs-sm); }
.nas-if th {
  text-align: left;
  padding: var(--space-2);
  font-size: var(--fs-xs);
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 1px;
  white-space: nowrap;
  border-bottom: 1px solid var(--border);
}
.nas-if td { padding: var(--space-2); border-bottom: 1px solid var(--border); vertical-align: top; }
.nas-if tr:last-child td { border-bottom: 0; }
.nas-if-nom { font-weight: 600; white-space: nowrap; }
/* Un membre d'agregat n'a pas d'adresse a lui : il porte le lien physique.
   On l'attenue pour que la hierarchie se lise sans avoir a la deduire. */
.nas-if--membre .nas-if-nom { font-weight: 400; padding-left: var(--space-4); }
.nas-if--membre td { color: var(--muted); }
.nas-if-etat { display: inline-flex; align-items: center; gap: var(--space-1); font-weight: 600; white-space: nowrap; }
.nas-if-etat--up      { color: var(--state-ok); }
.nas-if-etat--down    { color: var(--state-crit); }
.nas-if-etat--inconnu { color: var(--state-neutral); }
.nas-if-role { display: block; font-size: var(--fs-xs); color: var(--muted); }
.nas-if-adr { display: block; white-space: nowrap; }
.nas-reseau-vide { color: var(--muted); font-size: var(--fs-sm); margin: 0; }
.nas-wan--bas { margin: var(--space-3) 0 0; }
/* 03/10/2026 — ligne « IPv6 active sur ... » + bouton de desactivation
   (administrateurs). Le texte peut passer a la ligne, le bouton jamais. */
.nas-ipv6 {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2) var(--space-3);
  margin-top: var(--space-3);
  font-size: var(--fs-sm);
}
.nas-ipv6-texte { color: var(--dim); min-width: 0; }
.nas-ipv6 .btn-sm { white-space: nowrap; }
/* 05/10/2026 — CMS Synology de la fiche NAS (agent 1.0.49) : meme gabarit que la ligne
   IPv6 ; le motif passe a la ligne, les deux boutons jamais. */
.nas-cms-carte { margin: var(--space-3) 0 0; }
.nas-cms {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2) var(--space-3);
  font-size: var(--fs-sm);
}
.nas-cms-texte { color: var(--dim); min-width: 0; }
.nas-cms-motif { display: block; margin-top: var(--space-1); font-size: var(--fs-xs); color: var(--dim); }
.nas-cms-actions { display: inline-flex; flex-wrap: wrap; gap: var(--space-2); }
.nas-cms .btn-sm { white-space: nowrap; }

/* ── Pièces jointes d'une réponse de ticket ──────────────────────────────────
   Le technicien colle (Ctrl+V), dépose, ou choisit un fichier ; chaque pièce
   déjà téléversée apparaît en jeton jusqu'à l'envoi.                          */
.tk-pj-barre {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin-top: 8px;
}
.tk-pj-aide {
  color: var(--dim);
  font-size: var(--fs-xs);
}
.tk-pj-liste {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 8px;
}
.tk-pj-liste:empty { margin-top: 0; }

.tk-pj {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  max-width: 100%;
  padding: 4px 8px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg-2);
  font-size: var(--fs-xs);
}
.tk-pj .ni { flex: 0 0 auto; width: 14px; height: 14px; color: var(--dim); }
.tk-pj-nom {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 220px;
}
/* tabular-nums : sans lui, une taille qui passe de 9 ko à 14 ko fait sauter
   tout ce qui suit sur la ligne. */
.tk-pj-taille { color: var(--dim); font-variant-numeric: tabular-nums; }

.tk-pj-retirer {
  display: inline-flex;
  align-items: center;
  padding: 0;
  border: 0;
  background: none;
  color: var(--dim);
  cursor: pointer;
  line-height: 1;
}
.tk-pj-retirer:hover { color: var(--color-crit); }
.tk-pj-retirer:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* En cours de téléversement : la pièce est là mais pas encore acquise. */
.tk-pj--envoi { opacity: 0.55; }
.tk-pj--erreur {
  border-color: var(--state-crit-glow);
  background: var(--state-crit-soft);
  color: var(--state-crit);
}
.tk-pj--erreur .ni { color: var(--state-crit); }

/* Survol d'un fichier au-dessus de la zone de réponse. */
.tk-reponse--depot { outline: 2px dashed var(--accent); outline-offset: -4px; }

/* Adresses portées par une interface réseau découverte en SNMP.
   tabular-nums : sans lui, une colonne d'adresses de longueurs différentes
   danse d'une ligne à l'autre et devient pénible à parcourir. */
.dd-iface-adr {
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  padding: 4px 6px;
}

/* Complement d'information dans une fiche NAS : dit ce qui se passera sans
   qu'un reglage explicite existe (ex. plages deduites des interfaces). */
.nas-val-doux { color: var(--dim); font-size: var(--fs-xs); }

/* Taille d'un volume a cote de son nom, dans la liste des sauvegardes Synology.
   Classe plutot que style en dur : un style en dur echappe au theme clair/sombre
   et se recopie a chaque nouvel ecran. */
.syno-el-taille { color: var(--muted); font-size: var(--fs-xs); }

/* Fiche NAS - bandeau « Derniere commande » (28/09/2026).
   Remplace un bloc a styles en dur. La bordure gauche porte l'etat de la
   commande (memes tokens que les badges), le badge le dit en toutes lettres :
   une relance acceptee par DSM n'est pas une sauvegarde reussie. */
.syno-cmd {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-1) var(--space-2);
  margin-top: var(--space-3);
  padding: var(--space-2) var(--space-3);
  /* 28/09 (relecture) : --surface-2, comme les encadres voisins de la fiche.
     --bg-2 vaut #fff en theme clair : le bandeau se fondait dans la carte. */
  background: var(--surface-2);
  border-left: 3px solid var(--border-strong);
  border-radius: 4px;
  font-size: var(--fs-sm);
}
.syno-cmd--ok      { border-left-color: var(--state-ok); }
.syno-cmd--info    { border-left-color: var(--state-info); }
.syno-cmd--warn    { border-left-color: var(--state-warn); }
.syno-cmd--crit    { border-left-color: var(--state-crit); }
.syno-cmd--neutral { border-left-color: var(--state-neutral); }
.syno-cmd-etiq,
.syno-cmd-origine,
.syno-cmd-quand { color: var(--muted); white-space: nowrap; }
/* 28/09 (relecture) : plus de nowrap. Un nom de 57 caracteres (« …CODIAL22 →
   backup.foxchip.com:5566 ») sortait de la carte a 360 px, et la partie rognee
   etait la destination, celle qui distingue deux plans. */
.syno-cmd-cible { color: var(--text); min-width: 0; overflow-wrap: anywhere; }
.syno-cmd-quand { font-variant-numeric: tabular-nums; }
.syno-cmd-note { color: var(--dim); font-size: var(--fs-xs); }
.syno-cmd .badge,
.syno-relance .badge { white-space: nowrap; font-variant-numeric: tabular-nums; }
.syno-cmd-detail { flex-basis: 100%; }
.syno-cmd-detail > summary { cursor: pointer; color: var(--muted); }
.syno-cmd-detail > pre {
  margin: var(--space-1) 0 0;
  white-space: pre-wrap;
  font-size: var(--fs-xs);
  color: var(--muted);
}

/* Fiche NAS - relance automatique d'une sauvegarde, ligne sous le job
   (28/09/2026). Dit ce que le filet a fait (relances depuis le dernier succes,
   et leur issue) ou pourquoi il ne fera rien. Les heures en tabular-nums :
   une liste de relances ne doit pas danser d'une ligne a l'autre. */
.syno-relance > td {
  padding: var(--space-1) var(--space-3) var(--space-2);
  border-bottom: 1px solid var(--border);
  font-size: var(--fs-xs);
}
.syno-relance-ligne {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-1) var(--space-2);
}
.syno-relance-etiq {
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 1px;
  white-space: nowrap;
}
.syno-relance-raison { color: var(--text); }
.syno-relance-heures { color: var(--dim); font-variant-numeric: tabular-nums; }
.syno-relance-regle { color: var(--muted); min-width: 0; overflow-wrap: anywhere; }

/* Fiche NAS - diagnostic de replication (28/09/2026). Bouton .btn-sm sur la
   carte NAS et sur chaque ligne Snapshot Replication, lien du bandeau vers la
   page du rapport. Libelles sur une ligne : a cote de « Relancer », un
   « Diagnostic » coupe en deux decalerait toute la ligne du job. Le lien porte
   aussi .link-accent : sous `a { color: inherit }`, il ne se distinguait pas
   du texte du bandeau. */
.syno-diag { white-space: nowrap; }
.syno-relaunch + .syno-diag { margin-left: var(--space-2); }
.syno-cmd-lien { white-space: nowrap; }

/* Synology - tri par colonne de « Problemes actifs » et « Toutes les
   sauvegardes » (02/10/2026). Chaque en-tete triable contient un vrai
   <button> : atteignable au clavier, et aria-sort sur le th dit l'ordre aux
   lecteurs d'ecran. Le bouton se fond dans l'en-tete (meme police, memes
   majuscules) : seul l'indicateur dit « je me trie ».
   Le padding horizontal reste celui de `thead th` (aligne sur les cellules) ;
   seul le vertical est repris, comme le faisait le style en dur remplace. */
.syno-tri-th { padding-top: var(--space-2); padding-bottom: var(--space-2); }
.syno-tri-th--droite { text-align: right; }
/* Colonne de nombres, alignee a droite : l'indicateur passe a gauche du
   libelle, qui reste ainsi au droit des chiffres. */
.syno-tri-th--droite .syno-tri { flex-direction: row-reverse; }
.syno-tri {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  white-space: nowrap;
  cursor: pointer;
}
.syno-tri:hover { color: var(--text); }
.syno-tri:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Chevron du sprite (#i-chevron, pointe a droite) tourne vers le haut :
   croissant. Invisible tant que la colonne n'est pas triee, mais sa place est
   gardee — un tri ne fait pas sauter la largeur des colonnes. Au survol et au
   focus, il annonce ce que fera le clic. */
.syno-tri-ind {
  width: 12px;
  height: 12px;
  margin: 0;
  opacity: 0;
  transform: rotate(-90deg);
  pointer-events: none;
}
.syno-tri:hover .syno-tri-ind,
.syno-tri:focus-visible .syno-tri-ind { opacity: .5; }
/* Colonne triee : apres les regles de survol, a specificite egale, pour que
   l'indicateur ne palisse pas sous la souris. */
.syno-tri-th[aria-sort] .syno-tri { color: var(--text); }
.syno-tri-th[aria-sort] .syno-tri-ind { opacity: 1; color: var(--accent-2); }
.syno-tri-th[aria-sort="descending"] .syno-tri-ind { transform: rotate(90deg); }

/* Synology « Problemes actifs » (02/10/2026, « probleme affichage ») : le tableau doit tenir
   dans l'ecran. La colonne Actions n'y est plus collee a droite -- elle recouvrait le badge d'etat
   des que le tableau debordait. Relecture 154 (06/10/2026) : les largeurs maximales de Client, Type
   et Cadence partent (largeurs fixes du tableau, bloc « Gestion NAS -- tableaux a largeurs fixes »
   en fin de feuille) : sous 600 px, en nowrap, leur contenu sortait de la cellule. */
.syno-col-client { color: var(--dim); }
#syno-issues .tbl-scroll th.col-actions,
#syno-issues .tbl-scroll td.col-actions { position: static; box-shadow: none; }

/* Synology - etat disque/RAID d'un volume (05/10/2026, agent 1.0.48) : rouge si la grappe
   est degradee ou plantee, ambre pendant une reconstruction (encore exposee). Problemes
   actifs et fiche NAS. Le detail (« md2 raid1 [2/1] [U_] ») passe sous le type de volume. */
.syno-raid-etat { font-weight: 700; }
.syno-raid-etat--crit { color: var(--state-crit); }
.syno-raid-etat--warn { color: var(--state-warn); }
.syno-raid-detail { display: block; margin: 0 0 var(--space-1); color: var(--dim); font-size: var(--fs-xs); font-weight: 400; overflow-wrap: anywhere; }

/* Supervision > Microsoft 365 (03/10/2026) : les problemes M365 de tous les clients
   en un tableau, a la place des tickets d'alerte. */
.m365pb-sous-titre { margin: -4px 0 16px; }
.m365pb-etat { padding: 16px; }
.m365pb-erreur { color: var(--state-crit); }
.m365pb-filtres { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-bottom: 16px; }
.m365pb-puce {
  display: inline-flex; align-items: center; gap: 8px; padding: 4px 12px;
  border: 1px solid var(--border); border-radius: 999px; background: var(--surface);
  color: var(--text); font-size: var(--fs-sm); white-space: nowrap; cursor: pointer;
}
.m365pb-puce:hover { border-color: var(--border-strong); }
.m365pb-puce:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.m365pb-puce.is-actif { border-color: var(--accent); background: var(--accent-glow); }
.m365pb-puce-n { font-weight: 600; font-variant-numeric: tabular-nums; }
.m365pb-puce--crit .m365pb-puce-n { color: var(--state-crit); }
.m365pb-puce--warn .m365pb-puce-n { color: var(--state-warn); }
.m365pb-recherche { margin-left: auto; min-width: 220px; }
.m365pb-client { white-space: nowrap; }
.m365pb-objet code { font-size: var(--fs-sm); word-break: break-all; }
.m365pb-detail { max-width: 32rem; color: var(--dim); font-size: var(--fs-sm); }
.m365pb-depuis { white-space: nowrap; color: var(--dim); font-variant-numeric: tabular-nums; }

/* Supervision > Acces distants (03/10/2026) : outils de prise en main et RMM tiers
   releves par l'inventaire logiciel. Meme gabarit que Microsoft 365. */
.acct-sous-titre { margin: -4px 0 16px; }
.acct-etat { padding: 16px; }
.acct-erreur { color: var(--state-crit); }
.acct-filtres { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-bottom: 16px; }
.acct-puce {
  display: inline-flex; align-items: center; gap: 8px; padding: 4px 12px;
  border: 1px solid var(--border); border-radius: 999px; background: var(--surface);
  color: var(--text); font-size: var(--fs-sm); white-space: nowrap; cursor: pointer;
}
.acct-puce:hover { border-color: var(--border-strong); }
.acct-puce:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.acct-puce.is-actif { border-color: var(--accent); background: var(--accent-glow); }
.acct-puce-n { font-weight: 600; font-variant-numeric: tabular-nums; }
.acct-puce--warn .acct-puce-n { color: var(--state-warn); }
.acct-recherche { margin-left: auto; min-width: 220px; }
.acct-client, .acct-outil { white-space: nowrap; }
.acct-n { text-align: right; font-variant-numeric: tabular-nums; }
.acct-postes summary { cursor: pointer; color: var(--accent); }
.acct-postes summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.acct-postes ul { margin: 8px 0 0; padding-left: 16px; display: grid; gap: 4px; }
.acct-logiciel { color: var(--dim); font-size: var(--fs-sm); word-break: break-word; }

/* Acces distants : desinstaller (03/10/2026, Datto RMM a la demande de Jerome). */
.acct-barre { display: flex; justify-content: flex-end; margin-bottom: 12px; }
.acct-action { text-align: right; white-space: nowrap; }
.acct-faits { display: block; color: var(--dim); font-size: var(--fs-sm); font-weight: 400; }
.acct-postes li { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }

/* Synology : barre d'onglets (05/10/2026) -- en classes, sans emoji ni style en dur
   (renderSynologyTabs). L'onglet « Politique » porte deux compteurs (.nav-dd-rollup). */
.syno-onglets { margin-top: 12px; }
.syno-onglet .nav-dd-rollup { font-variant-numeric: tabular-nums; }
/* Fiche NAS : etat de la tache root (renderSynoCanalRoot), en classes (05/10/2026). */
.syno-canal { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 8px; }
.syno-canal-lib { min-width: 56px; color: var(--dim); font-size: var(--fs-xs); }
.syno-canal-etat { font-weight: 600; }
.syno-canal-etat--ok { color: var(--state-ok); }
.syno-canal-etat--warn { color: var(--state-warn); }
.syno-canal-etat--crit { color: var(--state-crit); }
.syno-canal-etat--neutral { color: var(--dim); }
.syno-canal-conseil { color: var(--dim); }
/* 05/10/2026 — Paquet et tache root HeXaControl-PrivExec (page Enroler, aide « Deployer
   l'agent Synology »). La commande a coller se selectionne d'un clic. */
.syno-aide-titre { margin: 0 0 8px; font-size: var(--fs-lg); }
.syno-root-proc { margin-top: 16px; padding: 16px; border: 1px solid var(--border-strong); border-radius: 8px; background: var(--bg-1); }
.syno-root-proc-titre { margin: 0 0 8px; font-size: var(--fs-md); }
.syno-root-proc-texte { margin: 0 0 8px; color: var(--dim); font-size: var(--fs-sm); }
.syno-root-proc-etapes { margin: 0 0 8px; padding-left: 24px; font-size: var(--fs-sm); line-height: 1.7; }
.syno-root-proc-etapes a { color: var(--accent); }
.syno-root-proc-script { display: block; margin-top: 4px; padding: 8px 12px; border-radius: 4px; background: var(--code-bg); font-size: var(--fs-xs); word-break: break-all; user-select: all; }
/* 07/10/2026 (lot 3, D4-06, D4-07) : le script de repli des paquets d'avant le 05/10, sous l'etape 4. */
.syno-root-proc-repli { display: block; margin-top: 8px; color: var(--dim); }

/* Gestion NAS -- revue du 05/10/2026 : en-tete commun des onglets (« Gestion NAS — client »),
   fenetres ouvertes par showModal() (Echap, croix, pas de fermeture sur un clic a cote),
   champs. Jetons et echelle 4/8/12/16/24 seulement. */
.syno-tete { display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 12px; }
.syno-tete > * { min-width: 0; }
.syno-titre { margin: 0; }
.syno-titre-portee { color: var(--dim); font-size: var(--fs-md); font-weight: 400; }
.syno-sous-titre { margin: 4px 0 0; }
.syno-tete-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.syno-tete-actions .btn-sm { white-space: nowrap; }
.syno-recherche {
  min-width: 220px; padding: 4px 12px; border: 1px solid var(--border); border-radius: 6px;
  background: var(--surface-2); color: var(--text); font: inherit; font-size: var(--fs-sm);
}
.syno-recherche:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.syno-fenetre-titre { margin: 0 0 4px; font-size: var(--fs-xl); }
.syno-fenetre-sous { margin: 0 0 16px; }
.syno-fenetre-encart { margin: 0 0 16px; padding: 8px 12px; border-radius: 6px; background: var(--surface-2); font-size: var(--fs-sm); }
.syno-fenetre-note { display: block; margin-top: 4px; color: var(--dim); font-size: var(--fs-xs); }
p.syno-fenetre-note { margin: 0 0 16px; font-size: var(--fs-sm); }
.syno-fenetre-message { margin: 0 0 16px; padding: 12px 16px; border-left: 3px solid var(--state-crit); border-radius: 6px; background: var(--state-crit-soft); }
.syno-fenetre-message pre { margin: 4px 0 0; white-space: pre-wrap; overflow-wrap: anywhere; font-size: var(--fs-sm); color: var(--state-crit); }
.syno-fenetre-etiq { display: block; color: var(--dim); font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: .05em; }
.syno-fenetre-grille { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 12px; margin: 0; font-size: var(--fs-sm); }
.syno-fenetre-grille dt { color: var(--dim); font-size: var(--fs-xs); }
.syno-fenetre-grille dd { margin: 0; }
.syno-fenetre-ia { margin-top: 16px; padding: 12px 16px; border-left: 3px solid var(--accent); border-radius: 6px; background: var(--surface-2); font-size: var(--fs-sm); }
.syno-fenetre-ia p { margin: 4px 0 8px; }
.syno-fenetre-ia ol { margin: 0; padding-left: 24px; display: grid; gap: 4px; }
.syno-fenetre-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 8px; margin-top: 16px; }
.syno-champ { display: block; margin: 0 0 4px; font-size: var(--fs-sm); font-weight: 600; }
.syno-saisie { display: block; width: 100%; margin: 0 0 12px; padding: 8px; font: inherit; }
.syno-saisie:disabled { opacity: 0.6; cursor: not-allowed; }
/* 07/10/2026 (choix 12 de la revue Gestion NAS) : fenetre « Accepter… » / « Retirer l'exception… » d'une exception datee --
   l'exposition acceptee (une chaine sans espace : coupee n'importe ou), le motif (agrandi en hauteur seulement), le refus. */
.np-exc-expo { display: block; margin-top: 4px; font-weight: 600; overflow-wrap: anywhere; }
.np-exc-fenetre .syno-saisie { resize: vertical; min-height: 72px; box-sizing: border-box; }
.np-exc-erreur { margin: 0 0 12px; color: var(--state-crit); font-size: var(--fs-sm); }
/* Fiche d'un NAS (revue du 05/10/2026) : etat en tete (en ligne ou non, alertes ouvertes),
   raisons des boutons grises, age de la derniere reussite colore par l'etat de la sauvegarde,
   badge d'erreur devenu un vrai bouton, barre de remplissage visible dans les deux themes. */
.syno-retour { color: var(--dim); font-size: var(--fs-sm); text-decoration: none; }
.syno-retour:hover { color: var(--text); }
.syno-etat-nas { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 12px 0 0; }
.syno-etat-nas .badge { font-variant-numeric: tabular-nums; }
.syno-hors-ligne { margin: 8px 0 0; }
.syno-alertes { margin: 8px 0 0; font-size: var(--fs-sm); }
.syno-alertes > summary { cursor: pointer; color: var(--dim); }
.syno-alertes > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.syno-alertes ul { margin: 8px 0 0; padding-left: 0; list-style: none; display: grid; gap: 4px; }
.syno-alertes li { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; overflow-wrap: anywhere; }
.syno-alerte-quand { color: var(--dim); font-size: var(--fs-xs); font-variant-numeric: tabular-nums; }
.syno-aide-survol { cursor: help; border-bottom: 1px dotted var(--dim); }
.syno-vol-barre { margin-top: 8px; height: 8px; border-radius: 4px; overflow: hidden; background: var(--border-strong); }
.syno-paquets-note { margin: 8px 0 4px; }
.syno-age { white-space: nowrap; font-variant-numeric: tabular-nums; }
.syno-age--ok { color: var(--state-ok); }
.syno-age--warn { color: var(--state-warn); font-weight: 600; }
.syno-age--crit { color: var(--state-crit); font-weight: 600; }
/* 06/10/2026 (D5-04, relecture) : sauvegarde que le NAS ne remonte plus (« introuvable »), gardée et marquée. */
.syno-job-introuvable > td { padding: 4px 12px 8px; }
.syno-job-introuvable-ligne { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; padding: 8px 12px; border-left: 3px solid var(--state-warn);
  border-radius: 4px; background: var(--state-warn-soft); color: var(--text); font-size: var(--fs-xs); line-height: 1.4; }
.syno-job-introuvable-ligne > .ni { color: var(--state-warn); flex: none; }
.syno-job-introuvable-texte { flex: 1 1 16rem; min-width: 0; overflow-wrap: anywhere; }
.syno-job-introuvable-ligne .syno-job-retirer { flex: none; white-space: nowrap; }
.syno-at-introuvable { margin-top: 4px; max-width: 240px; color: var(--state-warn); font-size: var(--fs-xs); font-weight: 600; overflow-wrap: anywhere; }
button.syno-job-detail { cursor: pointer; font-family: inherit; }
button.syno-job-detail:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.syno-raison { flex-basis: 100%; margin: 4px 0 0; color: var(--dim); font-size: var(--fs-xs); }
.syno-raisons { margin: 8px 0 0; padding-left: 16px; color: var(--dim); font-size: var(--fs-xs); }
.syno-proc-root { white-space: nowrap; }
.syno-produit tbody tr:hover { cursor: default; }
.np-carte-actions { display: flex; flex-wrap: wrap; gap: 8px; }
/* Enroler > Synology et aide « Ajouter un NAS » (revue du 05/10/2026) : une seule procedure,
   jeton du client, liens au nom reel des pages. */
.dp-jeton-nas { margin-top: 12px; }
.dp-jeton-nas > summary { cursor: pointer; color: var(--dim); font-size: var(--fs-sm); }
.dp-jeton-ligne { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 8px; }
.dp-jeton-valeur { flex: 1 1 240px; min-width: 0; padding: 8px 12px; border-radius: 4px; background: var(--code-bg); font-size: var(--fs-xs); word-break: break-all; user-select: all; }
.dp-suite-nas { margin: 12px 0 0; font-size: var(--fs-sm); }
/* 08/10/2026 (lot 7 de la revue du parc NAS, D4-09) : Enrôler > Synology en classes (plus de style en dur). */
.dp-nas { margin-top: 16px; }
.dp-nas-intro { margin: 0 0 8px; }
.dp-nas-liste { margin: 0; padding-left: 24px; line-height: 1.6; font-size: var(--fs-sm); }
/* Tableau de bord, widget « Gestion NAS » (05/10/2026) : NAS hors ligne et disques en defaut. */
.widget-nas-etat { margin: 8px 0 0; color: var(--state-crit); font-size: var(--fs-sm); font-weight: 600; }
/* Page d'aide du paquet DSM (synology/help, 05/10/2026) : statique, la feuille de la console. */
.syno-aide-page { max-width: 760px; margin: 0 auto; padding: 24px 16px; display: grid; gap: 16px; }
/* Lignes de tableaux NAS qui ne s'ouvrent pas au clic : pas de curseur en main (la regle generale
   tbody tr:hover le pose partout). */
#syno-fleet tbody tr:hover, #syno-issues tbody tr:hover, #syno-backups tbody tr:hover { cursor: default; }
@media (max-width: 600px) {
  .syno-recherche { min-width: 0; flex: 1 1 100%; }
}

/* Gestion NAS -- navigation et onglets (05/10/2026, revue « Gestion NAS », choix de Jerome). Une
   entree dans la barre laterale (pastilles du client actif) ; onglet « À traiter » (deux familles
   de compteurs qui filtrent la liste), « Mises à jour » (administrateurs). Jetons et echelle
   4/8/12/16/24 seulement, aucun style en dur. */
.nav-side #nav-syno-side-crit,
.nav-side #nav-syno-side-crit[hidden] + #nav-syno-side-warn { margin-left: auto; }
/* Une ligne : le libelle ne passe pas sous ses pastilles (compactes, comme celles du menu du haut). */
.nav-side #nav-syno-side { white-space: nowrap; }
.nav-side #nav-syno-side.active { padding-right: 24px; }   /* le point de l'entree active ne touche pas les pastilles */
.nav-side #nav-syno-side .nav-dd-rollup { padding: 0 4px; min-width: 16px; font-size: var(--fs-xs); line-height: 1.5; text-align: center; font-variant-numeric: tabular-nums; }
.nav-side #nav-syno-side .nav-dd-rollup + .nav-dd-rollup { margin-left: 4px; }
.syno-erreur { margin: 0; color: var(--state-crit); }
.syno-vide { color: var(--dim); }
/* Titres et textes des onglets et de la fiche d'un NAS. */
.syno-fiche-titre { display: flex; align-items: center; gap: 8px; margin: 0 0 8px; font-size: var(--fs-lg); }
.syno-fiche-aide { margin: 0 0 8px; color: var(--dim); font-size: var(--fs-sm); }
.syno-fiche-rien { margin: 0; color: var(--dim); font-size: var(--fs-sm); }

/* « À traiter » : cartes-filtres au gabarit de la Politique (06/10/2026, bloc « Cartes-filtres »
   plus bas) ; restent la page vide, la ligne « rien dans cette categorie » et les gestes. */
.syno-at-vide { text-align: center; }
.syno-at-vide-titre { margin: 0 0 4px; color: var(--state-ok); font-size: var(--fs-lg); }
.syno-at-vide-texte { margin: 0; color: var(--dim); font-size: var(--fs-sm); }
.syno-at-rien { padding: 16px; color: var(--dim); text-align: center; }
.syno-at-actions { display: inline-flex; flex-wrap: wrap; gap: 4px; }
.syno-at-actions .btn-sm { white-space: nowrap; }
/* 07/10/2026 (choix 12) : « Accepter… » (un bouton : --fs-sm) a cote du lien « Politique du NAS » (un a.btn-sm : --fs-xs)
   prend la taille du lien -- le lien agrandi sortait de sa cellule a 1 280 px. */
.syno-at-actions .btn-sm[data-syno-at="accepter"], .syno-fiche-at-action .btn-sm[data-syno-at="accepter"] { font-size: var(--fs-xs); }

/* Onglet « Mises à jour » (administrateurs) : plans nominatifs, agent, tableau des NAS. */
.syno-maj-plans { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 16px; margin-bottom: 16px; }
.syno-maj-plan { padding: 16px; display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.syno-maj-plan .syno-fiche-titre { margin: 0; }
.syno-maj-texte { margin: 0; font-size: var(--fs-sm); }
.syno-maj-noms > summary { cursor: pointer; color: var(--accent-2); font-size: var(--fs-sm); }
.syno-maj-noms > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.syno-maj-noms ul { margin: 8px 0 0; padding-left: 16px; display: grid; gap: 4px; font-size: var(--fs-sm); }
.syno-maj-client { margin-left: 8px; color: var(--dim); font-size: var(--fs-xs); }
.syno-maj-geste { margin-top: auto; padding-top: 8px; }
.syno-maj-geste .btn-sm { white-space: nowrap; }
.syno-maj-table-carte { padding: 0; overflow: hidden; }
.syno-maj-table .badge { white-space: nowrap; }
.syno-maj-nas { font-weight: 600; }
.syno-maj-version { font-variant-numeric: tabular-nums; }
/* 07/10/2026 (lot 3 de la revue du parc NAS) : « (réglée le 07/10 à 10:38 par …) » de la version du parc. */
.syno-maj-quand { color: var(--dim); font-size: var(--fs-xs); }
/* Relecture 154 (06/10/2026, « déborde de l'écran ») : le nom du client se replie. En nowrap, les noms
   reels (jusqu'a 41 caracteres : « REGIE DE QUARTIER LES PORTES DE L'ESSONNE ») faisaient sortir le
   tableau de son cadre a 1280 px (1143 pour 1008) : interrupteurs « Pilote » hors champ. */
.syno-maj-client-col { color: var(--dim); }

/* Gestion NAS -- cartes-filtres des onglets (06/10/2026 ; Jerome : « sur tous les onglets da nas je
   veux ce type d'affichage pour les types de defauts en haut de tableau »). « À traiter », « NAS »,
   « Sauvegardes » et « Mises à jour » reprennent le gabarit des cases de la Politique (.np-compteurs,
   .np-compteur, plus haut) ; un trait separe les groupes de cartes (etat, produit, relance) ; la
   mention du filtre est .np-note. Tableau de l'onglet « NAS » en classes (ses styles en dur sont
   partis). Jetons et echelle 4/8/12/16/24 seulement, aucun style en dur. */
.syno-cartes-sep { flex: 0 0 1px; align-self: stretch; margin: 4px; background: var(--border-strong); }
.syno-vide-centre { padding: 16px 0; text-align: center; }
.syno-parc-carte { padding: 0; overflow: hidden; }
.syno-parc-table .syno-num { text-align: right; }
.syno-parc-table .badge { white-space: nowrap; font-variant-numeric: tabular-nums; }
.syno-parc-table tr.syno-parc-client td { padding: 8px 12px; border-top: 2px solid var(--accent); background: var(--surface-2); color: var(--accent-2); font-weight: 600; }
.syno-parc-client-n { color: var(--dim); font-size: var(--fs-xs); font-weight: 400; }
/* Relecture 147 (point 2) : a 1280 px, la colonne « Alertes » sortait du cadre -- nom du NAS a
   12rem (14 avant), et le badge « à mettre à jour » de l'agent passe SOUS la version (plus de
   nowrap sur la cellule). */
.syno-parc-nas { min-width: 12rem; }
.syno-parc-nom { display: block; color: var(--text); font-weight: 600; text-decoration: none; }
.syno-parc-nom:hover { color: var(--accent-2); text-decoration: underline; }
.syno-parc-nom:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.syno-parc-acces { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; margin-top: 4px; font-size: var(--fs-xs); }
.syno-parc-acces > * { white-space: nowrap; }
.syno-parc-ip { display: block; color: var(--dim); font-size: var(--fs-xs); font-variant-numeric: tabular-nums; }
.syno-parc-dsm { color: var(--dim); white-space: nowrap; }
.syno-parc-n { font-weight: 600; font-variant-numeric: tabular-nums; }
.syno-parc-n--ok { color: var(--state-ok); }
.syno-parc-n--warn { color: var(--state-warn); }
.syno-parc-n--crit { color: var(--state-crit); }
.syno-parc-sauv { white-space: nowrap; }
.syno-parc-defaut { display: block; color: var(--state-crit); font-size: var(--fs-xs); }
.syno-parc-agent { white-space: normal; }
.syno-parc-agent .badge { display: table; margin: 4px 0 0; }
.syno-parc-etat { display: flex; flex-wrap: wrap; gap: 4px; min-width: 8rem; }
/* Onglet « Mises à jour » : les gestes de chaque ligne (06/10/2026, Jerome : « on ne peut pas lancer
   les mises a jour depuis la page ! ») -- l'etat, le bouton, et dessous la raison d'un bouton grise. */
.syno-maj-cellule { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; }
.syno-maj-ligne-btn { white-space: nowrap; }
/* white-space: normal -- relecture 154 : sous 600 px, le nowrap des cellules faisait sortir la raison
   (bornee a 16rem) de sa cellule, sur la colonne voisine. */
.syno-maj-cellule .syno-raison { flex-basis: auto; max-width: 16rem; margin: 0; white-space: normal; }

/* Gestion NAS -- fiche d'un NAS (05/10/2026, revue « Gestion NAS ») : en-tete (identite, acces),
   etat, gestes, « À traiter sur ce NAS », puis les sections ; carte « Commandes » (les 10
   dernieres, lecture seule). Jetons et echelle 4/8/12/16/24 seulement, aucun style en dur. */
.syno-fiche-tete { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: flex-start; gap: 12px; margin-top: 8px; }
.syno-fiche-ident { min-width: 0; }
.syno-fiche-nom { margin: 0; overflow-wrap: anywhere; }
.syno-fiche-sous { margin: 4px 0 0; }
.syno-fiche-liens { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.syno-fiche-liens .btn-sm { white-space: nowrap; }
.syno-fiche-liens-lib { color: var(--dim); font-size: var(--fs-sm); white-space: nowrap; }
.syno-etat-info { color: var(--dim); font-size: var(--fs-xs); font-variant-numeric: tabular-nums; }
.syno-fiche-actions { margin: 16px 0 0; padding: 16px; display: flex; flex-direction: column; gap: 8px; }
.syno-maj-ligne { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 24px; font-size: var(--fs-sm); }
.syno-maj-item { display: inline-flex; align-items: center; gap: 8px; }
.syno-maj-lib { min-width: 56px; color: var(--dim); font-size: var(--fs-xs); }
.syno-maj-val { color: var(--dim); }
.syno-maj-val--ok { color: var(--state-ok); }
.syno-maj-val--warn { color: var(--state-warn); font-weight: 600; }
.syno-maj-verif { color: var(--dim); font-size: var(--fs-xs); }
.syno-maj-note { margin: 0; padding: 8px 12px; border-left: 3px solid var(--state-warn); border-radius: 4px; background: var(--state-warn-soft); color: var(--dim); font-size: var(--fs-xs); line-height: 1.5; }
/* 06/10/2026 (NAS pilotes) : carte « Agent » de l'onglet « Mises à jour » -- notes de la diffusion
   (ambre : etendue au parc ; rouge : version publiee pendant une extension ; bleu : liste reglee hors
   de l'ecran), gestes « Étendre à tout le parc » et « Revenir aux pilotes » avec leur raison dessous,
   interrupteur « Pilote » de chaque ligne, sous la version de l'agent (bouton role="switch" : anneau
   de focus des boutons ; libelle visible, plein quand le NAS est pilote). */
.syno-maj-note--crit { border-left-color: var(--state-crit); background: var(--state-crit-soft); color: var(--text); }
.syno-maj-note--info { border-left-color: var(--state-info); background: var(--state-info-soft); }
.syno-maj-diffusion { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.syno-maj-agent .syno-fiche-titre:focus { outline: none; }
.syno-maj-agent .syno-fiche-titre:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.syno-pilotes-gestes { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 8px; }
.syno-pilotes-geste { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; min-width: 0; }
.syno-pilotes-geste .btn-sm { white-space: nowrap; }
.syno-pilotes-geste .syno-raison { flex-basis: auto; max-width: 16rem; margin: 0; }
/* « hors ligne » sous le nom du NAS : colle au nom, il empechait la colonne de se resserrer et le
   tableau sortait de 13 px de son cadre a 1280 px (deja avant les pilotes : 1021 pour 1008). */
.syno-maj-nas-etat { display: block; color: var(--dim); font-size: var(--fs-xs); }
.syno-inter { display: inline-flex; align-items: center; gap: 8px; padding: 4px; margin-left: -4px; border: 0; border-radius: var(--radius-sm); background: none; color: var(--dim); font: inherit; font-size: var(--fs-xs); cursor: pointer; }
.syno-inter[aria-checked="true"] { color: var(--text); font-weight: 600; }
.syno-inter-texte { white-space: nowrap; }
.syno-inter-piste { display: inline-flex; align-items: center; box-sizing: border-box; width: 40px; height: 24px; padding: 0 4px; border: 1px solid var(--border-strong); border-radius: 12px; background: var(--surface-2); transition: background-color .15s ease, border-color .15s ease; }
.syno-inter-piste::after { content: ''; width: 14px; height: 14px; border-radius: 50%; background: var(--dim); transition: transform .15s ease, background-color .15s ease; }
.syno-inter[aria-checked="true"] .syno-inter-piste { border-color: var(--accent); background: var(--accent); }
.syno-inter[aria-checked="true"] .syno-inter-piste::after { transform: translateX(16px); background: var(--bg-0); }
.syno-inter:hover .syno-inter-piste { border-color: var(--accent); }
.syno-inter:disabled { cursor: progress; opacity: .6; }
.syno-fiche-actions .syno-canal { margin-top: 0; font-size: var(--fs-sm); }
.syno-fiche-boutons { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 4px; }
.syno-fiche-boutons-admin { display: contents; }
.syno-fiche-boutons .btn-sm { white-space: nowrap; }
.syno-fiche-section { margin-top: 24px; }
.syno-fiche-compte { color: var(--dim); font-size: var(--fs-sm); font-weight: 400; font-variant-numeric: tabular-nums; }
.syno-fiche-at { padding: 12px 16px; }
.syno-fiche-at-liste { margin: 0; padding: 0; list-style: none; display: grid; gap: 8px; }
.syno-fiche-at-ligne { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; padding-bottom: 8px; border-bottom: 1px solid var(--border); }
.syno-fiche-at-ligne:last-child { padding-bottom: 0; border-bottom: 0; }
.syno-fiche-at-ligne .badge { white-space: nowrap; }
.syno-fiche-at-texte { flex: 1 1 240px; min-width: 0; font-size: var(--fs-sm); overflow-wrap: anywhere; }
.syno-fiche-at-action { display: inline-flex; flex-wrap: wrap; gap: 8px; margin-left: auto; }
.syno-fiche-at-action .btn-sm { white-space: nowrap; }
.syno-fiche-at .syno-alertes { margin-top: 12px; }
/* 07/10/2026 (choix 12) : « Exception jusqu'au JJ/MM (motif) » sous la liste de la fiche, hors du compte. */
.syno-fiche-exception { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 12px 0 0; padding-top: 12px; border-top: 1px solid var(--border); }
.syno-fiche-exception .badge { white-space: nowrap; }
.syno-fiche-pli { margin-top: 24px; }
.syno-fiche-pli > summary {
  display: flex; align-items: center; gap: 8px; padding: 8px 12px; cursor: pointer;
  border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface-2); font-weight: 600;
}
.syno-fiche-pli > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.syno-fiche-pli[open] > summary { margin-bottom: 8px; }
.syno-vols { gap: 12px; }
.syno-vol-tete { display: flex; justify-content: space-between; align-items: flex-start; gap: 8px; }
.syno-vol-nom { margin: 0; }
.syno-vol-type { margin: 4px 0; color: var(--dim); font-size: var(--fs-xs); }
.syno-vol-pct { font-size: var(--fs-xl); font-weight: 700; font-variant-numeric: tabular-nums; }
.syno-vol-pct--ok { color: var(--state-ok); }
.syno-vol-pct--warn { color: var(--state-warn); }
.syno-vol-pct--crit { color: var(--state-crit); }
/* Lot 7 (D7-13, 06/10/2026) : la barre est un SVG -- sa largeur est un attribut du rectangle, plus un
   style ecrit dans le code ; la couleur passe par fill. */
svg.syno-vol-barre { display: block; width: 100%; }
.syno-vol-rempli--ok { fill: var(--state-ok); }
.syno-vol-rempli--warn { fill: var(--state-warn); }
.syno-vol-rempli--crit { fill: var(--state-crit); }
.syno-vol-taille { margin: 8px 0 0; font-size: var(--fs-sm); font-variant-numeric: tabular-nums; }
.syno-paquets { margin-top: 8px; }
.syno-paquets-grille { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 8px; font-size: var(--fs-sm); }
.syno-paquet { padding: 4px 8px; border: 1px solid transparent; border-radius: 4px; background: var(--surface-2); overflow-wrap: anywhere; }
.syno-paquet--maj { border-color: var(--state-warn); }
.syno-paquet-version { color: var(--dim); font-size: var(--fs-xs); }
.syno-paquet-maj { display: block; margin-top: 4px; color: var(--state-warn); font-size: var(--fs-xs); font-weight: 600; }
.syno-cmds-carte { padding: 0; overflow: hidden; }
.syno-cmds-carte > .syno-fiche-rien, .syno-cmds-carte > .syno-erreur { padding: 16px; }
.syno-cmds { width: 100%; border-collapse: collapse; font-size: var(--fs-sm); }
.syno-cmds th { padding: 8px 12px; text-align: left; color: var(--dim); font-size: var(--fs-xs); font-weight: 600; white-space: nowrap; border-bottom: 1px solid var(--border); background: var(--surface-2); }
.syno-cmds td { padding: 8px 12px; vertical-align: top; border-bottom: 1px solid var(--border); }
.syno-cmds tr:last-child td { border-bottom: 0; }
.syno-cmds tbody tr:hover { cursor: default; }
.syno-cmds-quand { white-space: nowrap; font-variant-numeric: tabular-nums; color: var(--dim); }
.syno-cmds-action { min-width: 12rem; }
.syno-cmds-cible { display: block; color: var(--dim); font-size: var(--fs-xs); overflow-wrap: anywhere; }
.syno-cmds-qui { white-space: nowrap; }
.syno-cmds-etat .badge { white-space: nowrap; }
.syno-cmds-note { display: block; margin-top: 4px; color: var(--dim); font-size: var(--fs-xs); font-variant-numeric: tabular-nums; }
.syno-cmds-resultat { min-width: 12rem; max-width: 28rem; overflow-wrap: anywhere; }
.syno-cmds-doux { color: var(--dim); }
/* 13/10/2026 (session 260) — diagnostic IA d'un NAS : bouton de l'en-tête et sa raison écrite, fenêtre de démarrage
   (zone, sujets d'un clic, reprise), section « Diagnostics IA » de la fiche, ordre d'un diagnostic dans « Commandes ». */
.syno-ia-raison { flex-basis: 100%; color: var(--dim); font-size: var(--fs-xs); }
.syno-ia-libelle { display: block; margin: 0 0 4px; font-size: var(--fs-sm); font-weight: 600; }
.syno-ia-choix { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
.syno-ia-choix .btn-sm { white-space: nowrap; }
.syno-ia-reprise { margin: 0 0 16px; }
.syno-ia-carte { padding: 0; overflow: hidden; }
.syno-ia-carte > .syno-fiche-rien { padding: 16px; }
.syno-ia-table { width: 100%; border-collapse: collapse; font-size: var(--fs-sm); }
.syno-ia-table th { padding: 8px 12px; text-align: left; color: var(--dim); font-size: var(--fs-xs); font-weight: 600; white-space: nowrap; border-bottom: 1px solid var(--border); background: var(--surface-2); }
.syno-ia-table td { padding: 8px 12px; vertical-align: top; border-bottom: 1px solid var(--border); }
.syno-ia-table tr:last-child td { border-bottom: 0; }
.syno-ia-quand { white-space: nowrap; font-variant-numeric: tabular-nums; color: var(--dim); }
.syno-ia-qui { white-space: nowrap; }
.syno-ia-sujet { min-width: 14rem; overflow-wrap: anywhere; }
.syno-ia-cause { display: block; margin-top: 4px; color: var(--dim); font-size: var(--fs-xs); }
.syno-ia-etat .badge { white-space: nowrap; }
.syno-cmds-ia { display: block; font-size: var(--fs-xs); white-space: nowrap; }
@media (max-width: 600px) {
  .syno-fiche-at-action { margin-left: 0; }
  .syno-fiche-liens { width: 100%; }
}

/* Politique NAS (05/10/2026) : reglages a trois niveaux (parc, client, NAS), etat des
   NAS et envoi des ordres. Prefixe .np-. */
.np-sous-titre { margin: -4px 0 16px; }
.np-niveaux { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-bottom: 16px; }
.np-niveau {
  display: inline-flex; align-items: center; gap: 8px; padding: 4px 12px; border: 1px solid var(--border);
  border-radius: 999px; color: var(--text); font-size: var(--fs-sm); text-decoration: none; white-space: nowrap;
}
.np-niveau.is-actif { border-color: var(--accent); background: var(--accent-glow); }
.np-niveau:focus-visible, .np-select:focus-visible, .np-saisie:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.np-select, .np-saisie {
  padding: 4px 8px; border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--surface-2); color: var(--text); font-size: var(--fs-sm);
}
.np-saisie { min-width: 240px; }
.np-etat { padding: 16px; }
.np-erreur { color: var(--state-crit); display: flex; flex-wrap: wrap; align-items: center; gap: 12px; }

/* Fil d'Ariane Parc > Client > NAS (relecture du 05/10). */
.np-fil { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; margin: 4px 0 8px; color: var(--dim); font-size: var(--fs-sm); }
.np-fil a { color: var(--accent); text-decoration: none; }
.np-fil a:hover { text-decoration: underline; }
.np-fil a:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.np-fil [aria-current] { color: var(--text); font-weight: 600; }

/* Bilan du dernier envoi : reste affiche, refus et ecarts nommes (relecture du 05/10). */
.np-bilan {
  margin: 0 0 16px; padding: 12px 16px; border: 1px solid var(--border); border-left-width: 3px;
  border-radius: var(--radius-sm); background: var(--surface);
}
.np-bilan--ok { border-left-color: var(--state-ok); }
.np-bilan--warn { border-left-color: var(--state-warn); }
.np-bilan--crit { border-left-color: var(--state-crit); }
.np-bilan--neutral { border-left-color: var(--state-neutral); }
.np-bilan-tete { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px; }
.np-bilan-liste { margin: 8px 0 0; padding-left: 16px; color: var(--dim); font-size: var(--fs-sm); display: grid; gap: 4px; }
.np-note { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: -8px 0 16px; color: var(--dim); font-size: var(--fs-sm); }

.np-compteurs { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 16px; }
/* Les cases filtrent le tableau : ce sont des boutons. */
.np-compteur {
  display: flex; flex-direction: column; gap: 4px; min-width: 112px; padding: 8px 12px; text-align: left;
  border: 1px solid var(--border); border-left-width: 3px; border-radius: var(--radius-sm); background: var(--surface);
  color: var(--text); font: inherit; cursor: pointer;
}
.np-compteur:hover { border-color: var(--border-strong); }
.np-compteur:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* 25/10/2026 (revue DES3-03) : la carte choisie prend le voile du bouton-filtre du socle (16 % d'accent, comme
   .hx-filtre[aria-pressed=true]) au lieu de --accent-glow (45 % en sombre), sur lequel son nombre rouge tombait à 2,6:1
   et son libellé à 3,0:1. Au pire arrêt du fond sombre, le nombre (24 px) tient 3,3:1 ; libellé et sous-libellé passent
   au texte du thème (11:1), l'atténué n'y tenant que 3,9:1. */
.np-compteur.is-actif { background: rgba(var(--accent-rgb), 0.16); border-top-color: var(--accent); border-right-color: var(--accent); border-bottom-color: var(--accent); }
.np-compteur.is-actif .np-compteur-l, .np-compteur.is-actif .np-compteur-sous { color: var(--text); }
.np-compteur-n { font-size: var(--fs-xl); font-weight: 600; line-height: 1; font-variant-numeric: tabular-nums; }
.np-compteur-l { color: var(--dim); font-size: var(--fs-sm); white-space: nowrap; }
.np-compteur-sous { color: var(--dim); font-size: var(--fs-xs); white-space: nowrap; font-variant-numeric: tabular-nums; }
.np-compteur--ok { border-left-color: var(--state-ok); }
.np-compteur--warn { border-left-color: var(--state-warn); }
.np-compteur--crit { border-left-color: var(--state-crit); }
.np-compteur--info { border-left-color: var(--state-info); }
.np-compteur--neutral { border-left-color: var(--state-neutral); }
.np-compteur--ok .np-compteur-n { color: var(--state-ok); }
.np-compteur--warn .np-compteur-n { color: var(--state-warn); }
.np-compteur--crit .np-compteur-n { color: var(--state-crit); }
.np-compteur.is-vide .np-compteur-n { color: var(--dim); }

.np-reglages { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 16px; margin-bottom: 16px; }
/* 05/10/2026 (revue Gestion NAS) : le pare-feu prend toute la largeur (la colonne Action de ses
   sources etait masquee, meme a 1 920 px) ; la procedure de la tache root dans le bilan. */
.np-section--large { grid-column: 1 / -1; }
.np-bilan-proc { margin: 8px 0 0; }
.np-section { padding: 16px; display: flex; flex-direction: column; gap: 12px; min-width: 0; }
.np-section-titre { display: flex; align-items: center; gap: 8px; margin: 0; font-size: var(--fs-lg); }
.np-ligne { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.np-libelle { min-width: 160px; color: var(--dim); font-size: var(--fs-sm); }
.np-valeur { font-weight: 600; }
.np-source {
  display: inline-flex; align-items: center; padding: 0 8px; border-radius: 999px; border: 1px solid var(--border);
  font-size: var(--fs-xs); font-weight: 600; line-height: 1.6; white-space: nowrap; color: var(--dim);
}
.np-source--parc { color: var(--accent-2); border-color: var(--accent); }
.np-source--client { color: var(--state-info); border-color: var(--state-info); }
/* Source « NAS » : l'accent du produit, plein (l'ambre est reserve a la gravite). */
.np-source--nas { color: var(--text); border-color: var(--accent); background: var(--accent-glow); }
a.np-source { text-decoration: none; }
a.np-source:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.np-specifique { margin: 4px 0 0; white-space: normal; }
.np-sous { display: block; color: var(--dim); font-size: var(--fs-xs); font-weight: 400; }
.np-ligne .np-sous { display: inline; }
.np-aide { margin: 0; color: var(--dim); font-size: var(--fs-sm); display: flex; align-items: center; gap: 8px; }
.np-elements td { vertical-align: middle; }
.np-element { display: inline-flex; align-items: center; gap: 8px; font-variant-numeric: tabular-nums; }
.np-exclu .np-val { text-decoration: line-through; color: var(--dim); }
.np-refus td { color: var(--dim); }
.np-ajout { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.np-pays-nom { margin-left: 8px; color: var(--dim); }
.np-apercu { margin: 0; color: var(--dim); font-size: var(--fs-sm); }
.np-apercu code { font-size: var(--fs-sm); }
.np-detail summary { cursor: pointer; color: var(--accent); font-size: var(--fs-sm); }
.np-detail summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.np-regles { margin: 8px 0 0; padding: 8px 12px; overflow-x: auto; background: var(--surface-2); border-radius: var(--radius-sm); font-size: var(--fs-xs); }
.np-liste { margin: 0; padding: 0; list-style: none; display: grid; gap: 8px; }
.np-liste li { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.np-element--bloc { flex-direction: column; align-items: flex-start; gap: 0; min-width: 160px; }
/* 05/10/2026 — modele des notifications du parc (Politique NAS), en lecture seule. */
.np-modele { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: 4px 12px; margin: 0; font-size: var(--fs-sm); }
.np-modele dt { color: var(--dim); }
.np-modele dd { margin: 0; overflow-wrap: anywhere; font-variant-numeric: tabular-nums; }
.np-modele-titre { margin: 0; font-size: var(--fs-sm); font-weight: 600; }
/* 06/10/2026 (cahier 156, V1b) -- le modele des notifications se modifie a l'ecran (parc, administrateur) :
   trois groupes (envoi, presentation, destinataires) cote a cote, chaque champ « libelle au-dessus, saisie
   pleine largeur » ; puis l'apercu de la pose (bandeau chiffre, tableau dans son cadre), le frein et
   l'historique. Jetons du theme, echelle 4 / 8 / 12 / 16. */
/* Trois colonnes au plus (le motif, sur toute la largeur, occuperait sinon une quatrième colonne vide). */
.np-notif-form { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, max(280px, calc((100% - 32px) / 3))), 1fr)); gap: 16px; align-items: start; }
.np-notif-groupe {
  margin: 0; padding: 8px 16px 16px; min-width: 0; border: 1px solid var(--border); border-radius: var(--radius-sm);
  display: flex; flex-direction: column; gap: 8px;
}
.np-notif-groupe legend { display: inline-flex; align-items: center; gap: 8px; padding: 0 4px; color: var(--text); font-size: var(--fs-sm); font-weight: 600; }
/* Le style global des `label` (majuscules, interlettrage, marge haute) ne s'applique pas aux champs : leurs
   libellés sont ceux des autres lignes des Réglages. */
.np-notif-champ { display: flex; flex-direction: column; gap: 4px; min-width: 0; margin: 0; text-transform: none; letter-spacing: 0; font-weight: 400; font-size: inherit; color: inherit; }
.np-notif-champ .np-libelle { min-width: 0; display: inline-flex; align-items: center; gap: 4px; }
.np-notif-champ .np-saisie, .np-notif-champ .np-select { width: 100%; min-width: 0; box-sizing: border-box; }
.np-notif-groupe .np-valeur { overflow-wrap: anywhere; }
.np-notif-note { grid-column: 1 / -1; }
.np-notif-autre { display: flex; flex-direction: column; gap: 8px; margin-top: 8px; }
.np-case input:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* 07/10/2026 (réunion 172) : les cases du formulaire des notifications (SSL, authentification, certificat) n'héritent
   plus des marges globales des « label » (14 et 5 px, puis 8 px pour une case) : la rangée suit l'espacement de la
   ligne (gap 8 px), dans l'échelle. */
.np-notif-form label.np-case { margin: 0; }
.np-notif-apercu, .np-notif-frein { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.np-notif-frein .np-ajout .np-saisie { flex: 1 1 240px; max-width: 480px; }
.np-notif-bandeau { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 0; color: var(--dim); font-size: var(--fs-sm); font-variant-numeric: tabular-nums; }
.np-notif-table th, .np-notif-table td { vertical-align: top; }
/* 06/10/2026 (cahier 156-paquets) : l'aperçu d'une liste de paquets à installer (chiffres alignés). */
.np-install-apercu .np-num { text-align: right; font-variant-numeric: tabular-nums; }
/* 07/10/2026 (relecture 172) : les chiffres sur une ligne ; les en-têtes passent à la ligne s'il le faut -- en nowrap,
   « NAS de <société> » (le nom de l'instance) élargissait le tableau au-delà de la carte à 1 280 px. */
.np-install-apercu td.np-num { white-space: nowrap; }
.np-notif-table .badge { white-space: nowrap; }
.np-notif-nas { display: block; font-weight: 600; white-space: nowrap; }
.np-notif-hist .np-liste li { font-size: var(--fs-sm); }
.np-notif-quand { color: var(--dim); white-space: nowrap; font-variant-numeric: tabular-nums; }
.np-modele-titre code { color: var(--dim); font-weight: 400; }

.np-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-bottom: 16px; }
.np-fenetre { display: inline-flex; align-items: center; gap: 8px; color: var(--dim); font-size: var(--fs-sm); white-space: nowrap; }
.np-restaurer { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-left: auto; }
.np-case { display: inline-flex; align-items: center; gap: 4px; color: var(--dim); font-size: var(--fs-sm); white-space: nowrap; }
.np-aspects { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.np-aspects-titre { color: var(--dim); font-size: var(--fs-sm); }
.np-interrupteur .np-aide { flex: 1 1 240px; }
.np-lecture-seule { display: flex; align-items: center; gap: 8px; margin: 0 0 16px; color: var(--dim); font-size: var(--fs-sm); }

/* 06/10/2026 (session 155) -- Jérôme : « il faut revoir l'affichage du tableau Politique NAS car ce
   n'est pas clair, pas pratique sur les actions a faire, trop de bla bla ». État et envois : les NAS
   groupés par chose à faire (sections, la plus urgente en haut, bord à la couleur de la gravité) ;
   une ligne par NAS : nom, client, la raison sur UNE ligne (coupée, texte entier au survol), un
   point par domaine, le(s) bouton(s) de l'action ; le détail sous la ligne. Jetons et échelle
   4/8/12/16/24 seulement, clair et sombre, focus visible. Sous 760 px, la ligne s'empile.
   Finition (même jour) : UN gabarit de colonnes (--np-grille) pour TOUTES les lignes de TOUTES les
   sections et pour leurs en-têtes -- chevron | NAS | client | raison (1fr, coupée) | sept points
   (largeur fixe) | actions (largeur fixe, boutons à droite). Avant : une grille par section, aux
   colonnes « auto » taillées sur son seul contenu, et les points commençaient à 1 175, 1 258 ou
   1 346 px selon la section. */
.np-legende { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 12px; margin: 0 0 12px; color: var(--dim); font-size: var(--fs-xs); }
.np-legende-item { display: inline-flex; align-items: center; gap: 4px; white-space: nowrap; }
.np-legende-item > .ni { width: 14px; height: 14px; margin: 0; }
.np-legende-sep { align-self: stretch; width: 1px; background: var(--border-strong); }
/* 08/10/2026 (lot 7 de la revue du parc NAS, D7-12) : la légende et « Trier par » sur une même rangée, le choix du tri
   au bord droit ; sur un téléphone, il passe sous la légende. */
.np-liste-tete { display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: space-between; gap: 8px 16px; margin: 0 0 12px; }
.np-liste-tete > .np-legende { margin: 0; flex: 1 1 24rem; min-width: 0; }
.np-tri { display: inline-flex; align-items: center; gap: 8px; color: var(--dim); font-size: var(--fs-sm); white-space: nowrap; }
.np-tri > .ni { width: 14px; height: 14px; margin: 0; }
.np-tri .np-select { width: auto; }
/* « Tester » grisé sur la carte de la fiche : sa raison, dessous. */
.np-carte-raison { margin: 4px 0 0; }
.np-groupe {
  /* Colonnes fixes : le chevron (24 px), le nom et le client (12rem, coupés au-delà, une quinzaine de
     caractères pour le nom), les huit points (8 x 24 px et 7 écarts de 4 px ; 06/10/2026, cahier 156-paquets : le
     domaine Installations), les actions (18rem :
     « Voir », « Réappliquer » et « Relire » en demandent 272 px, « Procédure de la tâche root » 204 ;
     au-delà, elles passent à la ligne dans leur colonne). La raison prend le reste. */
  --np-col-tog: 24px;
  --np-col-id: 12rem;
  --np-col-pts: 220px;
  --np-col-act: 18rem;
  --np-grille: var(--np-col-tog) var(--np-col-id) var(--np-col-id) minmax(0, 1fr) var(--np-col-pts) var(--np-col-act);
  margin: 0 0 16px; border: 1px solid var(--border); border-left: 3px solid var(--state-neutral);
  border-radius: var(--radius-sm); background: var(--surface);
}
.np-groupe--ok { border-left-color: var(--state-ok); }
.np-groupe--warn { border-left-color: var(--state-warn); }
.np-groupe--crit { border-left-color: var(--state-crit); }
.np-groupe--info { border-left-color: var(--state-info); }
/* L'en-tête sur la grille des lignes (lignes de grille comptées depuis la fin : le même placement à
   cinq et à six colonnes) : le titre sous le chevron et le NAS (jusqu'à la raison s'il est seul),
   l'aide au droit des raisons, les boutons de la section dans les colonnes des points et des actions,
   au bord droit des boutons des lignes. */
.np-groupe-tete { display: grid; grid-template-columns: var(--np-grille); align-items: center; gap: 8px 12px; padding: 8px 12px; }
.np-groupe-titre { grid-column: 1 / -3; min-width: 0; margin: 0; font-size: var(--fs-md); line-height: 1.3; }
.np-groupe-tete--aide .np-groupe-titre { grid-column: 1 / -4; }
.np-groupe-pli {
  display: inline-flex; align-items: center; gap: 8px; padding: 4px 0; border: 0; border-radius: var(--radius-sm);
  background: none; color: var(--text); font: inherit; font-weight: 600; white-space: nowrap; cursor: pointer;
}
.np-groupe-pli:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Le chevron de la section dans une case de 24 px, comme celui des lignes ; le titre commence au droit
   du nom des NAS (24 px de chevron et 12 px d'écart). */
.np-groupe-pli > .ni { margin: 0 8px 0 4px; transition: transform .15s ease; }
.np-tog > .ni { margin: 0; transition: transform .15s ease; }
.np-groupe-pli[aria-expanded="true"] > .ni, .np-tog[aria-expanded="true"] > .ni { transform: rotate(90deg); }
.np-groupe-n { color: var(--dim); font-size: var(--fs-sm); font-weight: 400; font-variant-numeric: tabular-nums; }
.np-groupe-aide { grid-column: -4 / -2; min-width: 0; margin: 0; color: var(--dim); font-size: var(--fs-sm); }
.np-groupe-gestes { grid-column: -3 / -1; display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 8px; }
.np-groupe-tete--aide .np-groupe-gestes { grid-column: -2 / -1; }
/* Un lien en forme de bouton (« Mettre à jour », « Régler ce NAS ») a la taille des boutons voisins
   (.btn-sm seul le met en --fs-xs, button.btn-sm en --fs-sm). */
.np-groupe-gestes .btn-sm, .np-ln-actions .btn-sm { font-size: var(--fs-sm); white-space: nowrap; text-decoration: none; }
.np-groupe-liste { margin: 0; padding: 0; list-style: none; border-top: 1px solid var(--border); }
.np-groupe-liste[hidden] { display: none; }
.np-item { border-top: 1px solid var(--border); }
.np-item:first-child { border-top: 0; }
.np-ln {
  display: grid; grid-template-columns: var(--np-grille); align-items: center; column-gap: 12px;
  padding: 8px 12px; cursor: pointer;
}
.np-ln:hover, .np-item.is-ouvert > .np-ln { background: var(--surface-2); }
.np-tog {
  display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px; padding: 0;
  border: 0; border-radius: var(--radius-sm); background: none; color: var(--dim); cursor: pointer;
}
.np-tog:hover { color: var(--text); }
.np-tog:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.np-ln-id { display: contents; }
.np-ln-nom { min-width: 0; overflow: hidden; color: var(--text); font-weight: 600; text-decoration: none; text-overflow: ellipsis; white-space: nowrap; }
.np-ln-nom:hover { text-decoration: underline; }
.np-ln-nom:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.np-ln-client { min-width: 0; overflow: hidden; color: var(--dim); font-size: var(--fs-xs); text-overflow: ellipsis; white-space: nowrap; }
.np-ln-raison { display: flex; align-items: center; gap: 8px; min-width: 0; }
.np-raison { min-width: 0; overflow: hidden; font-size: var(--fs-sm); text-overflow: ellipsis; white-space: nowrap; }
.np-puce {
  flex: none; padding: 0 8px; border: 1px solid var(--border-strong); border-radius: 999px;
  color: var(--dim); font-size: var(--fs-xs); line-height: 1.6; white-space: nowrap;
}
/* Un point par domaine : la forme dit aussi l'état (pointillé : non géré ; trait : conforme ou en
   cours ; trait épais : à faire ; plein : échec), pas seulement la couleur. */
.np-points { display: inline-flex; gap: 4px; }
.np-point {
  display: inline-flex; flex: none; align-items: center; justify-content: center; width: 24px; height: 24px;
  border: 1px dashed var(--state-neutral); border-radius: 999px; color: var(--state-neutral);
}
.np-point > .ni { width: 14px; height: 14px; margin: 0; opacity: 1; }
.np-point--ok { border: 1px solid var(--state-ok-glow); background: var(--state-ok-soft); color: var(--state-ok); }
.np-point--info { border: 1px solid var(--state-info-glow); background: var(--state-info-soft); color: var(--state-info); }
.np-point--warn { border: 2px solid var(--state-warn); background: var(--state-warn-soft); color: var(--state-warn); }
.np-point--crit { border: 2px solid var(--state-crit); background: var(--state-crit); color: var(--bg-2); }
.np-point--mini { width: 12px; height: 12px; }
.np-ln-actions { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: 8px; min-width: 0; }
.np-voir:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Le détail : tout ce que disait l'ancien tableau, domaine par domaine (au droit du nom des NAS). */
.np-det { padding: 4px 16px 16px 48px; border-top: 1px dashed var(--border); }
.np-det[hidden] { display: none; }
.np-det-liste { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: 8px 16px; margin: 12px 0 0; font-size: var(--fs-sm); }
.np-det-ligne { display: contents; }
.np-det-ligne > dt { display: inline-flex; align-items: center; gap: 8px; color: var(--dim); white-space: nowrap; }
.np-det-ligne > dt > .ni { margin: 0; }
.np-det-ligne > dd { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; min-width: 0; margin: 0; overflow-wrap: anywhere; }
.np-det-motif { color: var(--text); }
.np-det .np-sous { display: inline; font-size: var(--fs-sm); }
.np-det .badge { white-space: nowrap; }
.np-lien { color: var(--accent-2); }
.np-lien:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.np-det-gestes { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 16px; }
/* Sous 1 600 px (1 280 px avec la barre latérale), le client passe sous le nom : une colonne de moins,
   la raison garde la largeur de la ligne. */
@media (max-width: 1599px) {
  .np-groupe { --np-grille: var(--np-col-tog) var(--np-col-id) minmax(0, 1fr) var(--np-col-pts) var(--np-col-act); }
  .np-ln-id { display: flex; flex-direction: column; min-width: 0; line-height: 1.25; }
}
/* Sous 1 200 px (barre latérale encore là jusqu'à 900 px), la ligne tient sur deux rangées : le NAS
   et la raison, puis les points (sous le NAS) et les boutons (à droite) -- les mêmes colonnes fixes
   dans toutes les sections. L'en-tête de section redevient une rangée souple : le titre, l'aide au
   droit des raisons tant qu'elle tient à côté (dessous sinon), les boutons à droite. */
@media (max-width: 1199px) {
  .np-ln { grid-template-columns: var(--np-col-tog) var(--np-col-id) minmax(0, 1fr); grid-template-areas: "tog id raison" "tog pts act"; row-gap: 8px; }
  .np-tog { grid-area: tog; align-self: start; }
  .np-ln-id { grid-area: id; }
  .np-ln-raison { grid-area: raison; }
  .np-points { grid-area: pts; }
  .np-ln-actions { grid-area: act; }
  .np-groupe-tete { display: flex; flex-wrap: wrap; gap: 8px 12px; }
  .np-groupe-tete--aide .np-groupe-titre { flex: 0 0 calc(var(--np-col-tog) + 12px + var(--np-col-id)); }
  .np-groupe-aide { flex: 1 1 280px; }
  .np-groupe-gestes { margin-left: auto; }
}
@media (max-width: 760px) {
  .np-ln {
    grid-template-columns: var(--np-col-tog) minmax(0, 1fr); grid-template-areas: "tog id" "tog raison" "tog pts" "tog act";
    row-gap: 8px; padding: 12px;
  }
  .np-ln-id { flex-direction: row; flex-wrap: wrap; align-items: baseline; gap: 4px 8px; }
  .np-ln-raison { flex-wrap: wrap; }
  .np-raison { white-space: normal; }
  .np-points { flex-wrap: wrap; }
  .np-ln-actions { justify-content: flex-start; }
  /* Empilé : l'aide et les boutons de la section chacun sur sa rangée, au droit du nom des NAS, à gauche
     comme ceux des lignes. */
  .np-groupe-tete--aide .np-groupe-titre { flex: 0 1 auto; }
  .np-groupe-aide, .np-groupe-gestes { flex: 1 1 100%; margin-left: calc(var(--np-col-tog) + 12px); }
  .np-groupe-gestes { justify-content: flex-start; }
  .np-det { padding: 4px 12px 16px; }
  .np-det-liste { grid-template-columns: minmax(0, 1fr); gap: 4px; }
  .np-det-ligne > dt { margin-top: 8px; }
}

.np-carte { margin: 16px 0 0; padding: 16px; display: flex; flex-direction: column; gap: 12px; }
.np-carte-tete { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; }
.np-carte-tete .btn-sm { margin-left: auto; }
.np-carte-aspects { display: flex; flex-wrap: wrap; gap: 16px; }
.np-carte-aspect { display: inline-flex; align-items: center; gap: 8px; }
.np-carte-aspect .np-libelle { min-width: 0; }
/* 07/10/2026 (choix 12) : un badge long (« Exception jusqu'au JJ/MM ») ne se coupe plus, ni le nom du domaine : le motif
   prend la place qui reste et passe a la ligne. */
.np-carte-aspect > .np-libelle, .np-carte-aspect > .badge { flex-shrink: 0; white-space: nowrap; }
/* Relecture du 08/10 : un motif sans espace (nom de domaine, alias de portail) se coupe au lieu de sortir de la carte ;
   sur un telephone, le motif passe sous le domaine et son badge (a 360 px, le pare-feu en exception debordait de 91 px). */
.np-carte-aspect > .np-sous { min-width: 0; overflow-wrap: anywhere; }
@media (max-width: 600px) {
  .np-carte-aspect { flex-wrap: wrap; }
  .np-carte-aspect > .np-sous { flex: 1 1 100%; }
}

/* 05/10/2026 (revue Gestion NAS, vague 2) : la Politique en deux sous-onglets, « État et envois »
   et « Réglages » ; barre d'actions et barre d'enregistrement qui restent visibles sous la barre
   du produit, bilan juste dessous ; brouillon signale « Non enregistré » ; journal technique
   (codes de retour) en infobulle. */
.np-sous-onglets { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 16px; }
.np-sous-onglet {
  display: inline-flex; align-items: center; gap: 8px; padding: 4px 12px; border: 1px solid var(--border);
  border-radius: 999px; color: var(--dim); font-size: var(--fs-sm); text-decoration: none; white-space: nowrap;
}
.np-sous-onglet:hover { color: var(--text); border-color: var(--border-strong); }
.np-sous-onglet:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.np-sous-onglet.is-actif { color: var(--text); font-weight: 600; border-color: var(--accent); background: var(--accent-glow); }
.np-onglet-modifs { font-variant-numeric: tabular-nums; }
.np-barre {
  position: sticky; top: var(--topbar-h); z-index: 3; margin: 0 0 16px; padding: 12px 16px;
  background: var(--bg-1); border: 1px solid var(--border); border-radius: var(--radius-sm);
}
.np-barre .np-actions { margin-bottom: 0; }
.np-barre .np-note { margin: 8px 0 0; }
.np-barre .np-lecture-seule { margin: 0; }
.np-barre .np-bilan { margin: 12px 0 0; max-height: 40vh; overflow-y: auto; }
/* « Annulation automatique si non confirmé sous » : une phrase, pas un intitulé de champ (le style
   general des label la mettait en capitales espacees). */
.np-fenetre { margin: 0; text-transform: none; letter-spacing: normal; font-weight: 400; }
.np-elements .np-modifie { margin-left: 4px; }
.np-note--warn { color: var(--text); }
.np-note--warn > .ni { color: var(--state-warn); }
.np-lien-vue { color: var(--accent); }
.np-lien-vue:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.np-enregistrement-barre { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; }
.np-enregistrement-etat { margin: 0; display: inline-flex; flex-wrap: wrap; align-items: center; gap: 8px; color: var(--dim); font-size: var(--fs-sm); }
.np-enregistrement-barre.is-modifie .np-enregistrement-etat { color: var(--text); }
.np-enregistrement-barre.is-modifie .np-enregistrement-etat > .ni { color: var(--state-warn); }
.np-enregistrement-note { margin-left: auto; }
.np-ligne.is-modifie { padding-left: 8px; border-left: 3px solid var(--state-warn); }
.np-modifie { white-space: nowrap; }
.np-retire .np-val { text-decoration: line-through; color: var(--dim); }
.np-journal {
  display: inline-flex; align-items: center; justify-content: center; margin-left: 4px; padding: 0; border: 0;
  background: none; color: var(--dim); cursor: help; vertical-align: middle;
}
.np-journal:hover { color: var(--text); }
.np-journal:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--radius-sm); }
.np-journal .ni { width: 14px; height: 14px; margin: 0; }
@media (max-width: 600px) {
  /* Sur un telephone, les barres ne restent pas en haut : elles couvriraient la moitie de l'ecran
     (le bilan est alors amene sous les yeux par le script). */
  .np-barre { position: static; }
  .np-fenetre { white-space: normal; }
  .np-enregistrement-note { margin-left: 0; }
}

/* 06/10/2026 -- Gestion NAS : relance des sauvegardes (lot R2, synology_relance.js). Etat de relance
   de chaque tache (colonnes « Relance auto » d'À traiter et « Relance » de Sauvegardes, ligne de la
   fiche), menu de la ligne (suspendre, reprendre, journaux), filtres par etat, Journal des relances
   et son bilan, section « Relance des sauvegardes » de Politique › Réglages et interrupteur du parc.
   Jetons et echelle 4/8/12/16/24 seulement, aucun style en dur ; libelles sur une ligne, focus
   visible ; tableaux dans .tbl-scroll. Prefixe .syno-rl-. */
.syno-rl-td { vertical-align: top; min-width: 12rem; }
/* white-space: normal -- sous 600 px, « table td { white-space: nowrap !important } » se transmet
   aux phrases : rognees dans la fiche, elles recouvraient la colonne voisine dans Sauvegardes. */
.syno-rl-cellule { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; max-width: 22rem; white-space: normal; }
.syno-rl-tete { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; }
.syno-rl-tete .badge, .syno-rl-ligne-tete .badge, .syno-rl-interrupteur .badge, .syno-rl-j-origine .badge { white-space: nowrap; font-variant-numeric: tabular-nums; }
.syno-rl-raison { color: var(--dim); font-size: var(--fs-xs); line-height: 1.4; overflow-wrap: anywhere; }
.syno-rl-gestes { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 4px; }
.syno-rl-gestes .btn-sm, .syno-rl-relancer, .syno-rl-menu-item { white-space: nowrap; }
.syno-rl-raison-bouton { display: block; flex-basis: 100%; max-width: 16rem; margin-top: 4px; color: var(--dim); font-size: var(--fs-xs); line-height: 1.4; white-space: normal; text-align: left; }
/* « À traiter » : le bouton « Relancer » en classes (avant : styles en dur et « ▶ »). */
.syno-issues-row .syno-relaunch {
  display: inline-flex; align-items: center; gap: 4px; padding: 4px 8px; border: 1px solid var(--accent); border-radius: var(--radius-sm);
  background: transparent; color: var(--accent); font: inherit; font-size: var(--fs-xs); font-weight: 600; white-space: nowrap; cursor: pointer;
}
.syno-issues-row .syno-relaunch:hover { background: var(--accent-glow); }
.syno-issues-row .syno-relaunch:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.syno-issues-row .syno-relaunch:disabled { opacity: .4; cursor: not-allowed; background: transparent; }
.syno-issues-row .syno-relaunch .ni { width: 14px; height: 14px; margin-right: 0; }
/* Menu de la ligne : un <details> qui s'ouvre dans la cellule (rien n'est coupe par le conteneur
   qui defile). */
.syno-rl-menu-zone { display: inline-block; margin-left: 4px; vertical-align: top; text-align: left; }
.syno-rl-menu { display: inline-block; text-align: left; }
.syno-rl-menu > summary { list-style: none; cursor: pointer; }
.syno-rl-menu > summary::-webkit-details-marker { display: none; }
.syno-rl-menu[open] > summary { border-color: var(--accent); background: var(--accent-glow); }
.syno-rl-menu-liste {
  display: flex; flex-direction: column; align-items: stretch; gap: 4px; margin-top: 4px; padding: 8px;
  border: 1px solid var(--border-strong); border-radius: var(--radius-sm); background: var(--bg-1);
}
/* Boutons et liens du menu au meme corps (un <a class="btn-sm"> et un <button class="btn-sm">
   n'ont pas la meme taille dans la feuille generale). */
.syno-rl-menu-item { justify-content: flex-start; font-size: var(--fs-sm); }
/* « À traiter » : colonne plus etroite (le menu est sur la ligne du badge). */
#syno-issues .syno-rl-td { min-width: 10rem; }
#syno-issues .syno-rl-cellule { max-width: 16rem; }
/* Fiche d'un NAS : la ligne « Relance automatique » sous chaque sauvegarde. */
.syno-rl-tr > td { padding: 8px 12px 12px; font-size: var(--fs-sm); }
.syno-rl-ligne { display: flex; flex-direction: column; gap: 8px; white-space: normal; }
.syno-rl-ligne-tete { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.syno-rl-etiq { color: var(--muted); font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: .05em; white-space: nowrap; }
.syno-rl-phrase { min-width: 0; color: var(--text); overflow-wrap: anywhere; }
.syno-rl-faits { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 4px 16px; margin: 0; }
.syno-rl-faits dt { color: var(--dim); font-size: var(--fs-xs); }
.syno-rl-faits dd { margin: 0; overflow-wrap: anywhere; }
.syno-rl-lien { margin-left: 4px; white-space: nowrap; }
.syno-rl-cause { margin: 0; padding: 8px 12px; border-left: 3px solid var(--state-warn); border-radius: var(--radius-sm); background: var(--state-warn-soft); color: var(--text); overflow-wrap: anywhere; }
.syno-rl-cause-lib { margin-right: 8px; font-weight: 600; }
.syno-rl-quefaire { display: block; margin-top: 4px; }
.syno-rl-chronique { margin: 0; display: flex; align-items: center; gap: 8px; color: var(--state-warn); font-weight: 600; }
.syno-rl-chronique .ni { margin-right: 0; }
.syno-rl-fiche-journal { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.syno-rl-fiche-journal .btn-sm { white-space: nowrap; }
/* Sauvegardes : filtres par etat de relance (compteurs toujours visibles, meme a zero). */
.syno-rl-filtres { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 0 0 16px; }
.syno-rl-filtres-titre { margin-right: 4px; color: var(--dim); font-size: var(--fs-sm); font-weight: 600; white-space: nowrap; }
.syno-rl-filtre {
  display: inline-flex; align-items: center; gap: 4px; padding: 4px 12px; border: 1px solid var(--border); border-radius: 999px;
  background: var(--surface-2); color: var(--text); font: inherit; font-size: var(--fs-sm); white-space: nowrap; cursor: pointer;
}
.syno-rl-filtre:hover { border-color: var(--border-strong); }
.syno-rl-filtre:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.syno-rl-filtre[aria-pressed="true"] { border-color: var(--accent); background: var(--accent-glow); }
.syno-rl-filtre-n { font-weight: 600; font-variant-numeric: tabular-nums; }
.syno-rl-filtre--crit .syno-rl-filtre-n { color: var(--state-crit); }
.syno-rl-filtre--warn .syno-rl-filtre-n { color: var(--state-warn); }
.syno-rl-filtre--info .syno-rl-filtre-n { color: var(--state-info); }
.syno-rl-filtre--ok .syno-rl-filtre-n { color: var(--state-ok); }
.syno-rl-filtre--neutral .syno-rl-filtre-n { color: var(--state-neutral); }
.syno-rl-filtre-n.is-zero { color: var(--dim); }
.syno-rl-sous-onglets { margin-top: -4px; }
/* Journal des relances : bandeau de l'interrupteur, filtres, bilan, lignes. */
.syno-rl-bandeau { margin: 0 0 16px; }
.syno-rl-j-filtres { display: flex; flex-direction: column; gap: 12px; margin-bottom: 16px; padding: 12px 16px; }
.syno-rl-j-rangee { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.syno-rl-j-lib { color: var(--dim); font-size: var(--fs-sm); }
.syno-rl-j-champs { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 12px; }
.syno-rl-j-champ {
  display: inline-flex; flex-direction: column; gap: 4px; margin: 0; color: var(--dim); font-size: var(--fs-xs);
  font-weight: 400; text-transform: none; letter-spacing: normal;
}
.syno-rl-j-dates { display: inline-flex; flex-wrap: wrap; align-items: flex-end; gap: 12px; }
.syno-rl-j-puce { display: inline-flex; align-items: center; gap: 4px; padding: 4px 4px 4px 12px; border: 1px solid var(--accent); border-radius: 999px; background: var(--accent-glow); font-size: var(--fs-sm); }
.syno-rl-j-retirer { padding: 4px; }
.syno-rl-bilan { display: flex; flex-direction: column; gap: 12px; margin-bottom: 16px; padding: 16px; }
.syno-rl-bilan-titre { display: flex; align-items: center; gap: 8px; margin: 0; font-size: var(--fs-lg); }
.syno-rl-bilan-table, .syno-rl-j-table { width: 100%; border-collapse: collapse; font-size: var(--fs-sm); }
.syno-rl-bilan-table th, .syno-rl-bilan-table td, .syno-rl-j-table th, .syno-rl-j-table td { padding: 8px 12px; border-bottom: 1px solid var(--border); text-align: left; }
.syno-rl-bilan-table thead th, .syno-rl-j-table thead th { color: var(--dim); font-size: var(--fs-xs); letter-spacing: .04em; white-space: nowrap; background: var(--surface-2); }
/* Bilan : une ligne par mesure, une colonne par produit (leur nom, sans capitales) et le total. */
.syno-rl-bilan-table thead th.syno-rl-n { text-transform: none; letter-spacing: normal; }
.syno-rl-bilan-table tbody th { font-weight: 600; white-space: nowrap; }
.syno-rl-bilan-table tbody tr.syno-rl-bilan-sous > th { padding-left: 24px; font-weight: 400; color: var(--dim); }
.syno-rl-bilan-table tbody tr:hover, .syno-rl-j-table tbody tr:hover, .syno-rl-pol-table tbody tr:hover { cursor: default; }
.syno-rl-n { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.syno-rl-bilan-table td.syno-rl-n, .syno-rl-bilan-table th.syno-rl-n { text-align: right; }
.syno-rl-taux { color: var(--dim); }
.syno-rl-bilan-table .syno-rl-bilan-total { font-weight: 600; border-left: 2px solid var(--border-strong); }
.syno-rl-bilan-table .syno-rl-bilan-zero { color: var(--dim); }
.syno-rl-chroniques > summary { cursor: pointer; color: var(--state-warn); font-size: var(--fs-sm); font-weight: 600; }
.syno-rl-chroniques > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.syno-rl-chroniques ul { margin: 8px 0 0; padding-left: 16px; display: grid; gap: 4px; font-size: var(--fs-sm); }
.syno-rl-j-carte { padding: 0; overflow: hidden; }
.syno-rl-j-vide { padding: 16px; }
.syno-rl-j-table td { vertical-align: top; }
.syno-rl-j-quand { white-space: nowrap; font-variant-numeric: tabular-nums; color: var(--dim); }
.syno-rl-j-nas { font-weight: 600; overflow-wrap: anywhere; }
/* Le client, en sous-ligne du NAS (comme le produit sous la sauvegarde). */
.syno-rl-j-nas .syno-rl-j-sous { font-weight: 400; }
.syno-rl-j-sauvegarde { overflow-wrap: anywhere; }
/* Les phrases du journal reviennent a la ligne, y compris sous 600 px (nowrap herite des cellules). */
.syno-rl-j-sauv, .syno-rl-j-produit, .syno-rl-j-sous, .syno-rl-j-rep, .syno-rl-j-cause-texte { white-space: normal; }
.syno-rl-j-sauv { display: block; }
.syno-rl-j-produit, .syno-rl-j-sous { display: block; color: var(--dim); font-size: var(--fs-xs); }
.syno-rl-j-essai { white-space: nowrap; font-variant-numeric: tabular-nums; text-align: center; }
.syno-rl-j-cause { color: var(--dim); overflow-wrap: anywhere; }
/* Trois lignes au plus : le texte entier est dans l'infobulle. */
.syno-rl-j-cause-texte { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; overflow: hidden; }
.syno-rl-j-reponse { overflow-wrap: anywhere; }
/* Relecture 154 (06/10/2026, session 158 ; Jerome : « déborde de l'écran ») : le journal tient dans son
   cadre des 1 280 px (1 220 px pour 1 008 avant) -- largeurs fixes en %, en-tetes sur deux lignes au
   plus, le NAS et le resultat reviennent a la ligne (plus de nowrap ni de largeur maximale sur les
   cellules : sous 600 px, en nowrap, la reponse du NAS sortait de la sienne). Sous 1 280 px, le cadre
   defile ; sous 600 px, la regle des telephones (largeur naturelle) reprend la main. */
.syno-rl-j-table { table-layout: fixed; min-width: 1000px; }
.syno-rl-j-table thead th { white-space: normal; vertical-align: bottom; }
.syno-rl-j-table th, .syno-rl-j-table td { padding: 8px; }
.syno-rl-j-table th:nth-child(1) { width: 9%; }
.syno-rl-j-table th:nth-child(2) { width: 14%; }
.syno-rl-j-table th:nth-child(3) { width: 15%; }
.syno-rl-j-table th:nth-child(4) { width: 14%; }
.syno-rl-j-table th:nth-child(5) { width: 6%; }
.syno-rl-j-table th:nth-child(6) { width: 14%; }
.syno-rl-j-table th:nth-child(7) { width: 12%; }
.syno-rl-j-table th:nth-child(8) { width: 16%; }
.syno-rl-j-resultat { overflow-wrap: anywhere; }
.syno-rl-res { font-weight: 600; }
.syno-rl-res--ok { color: var(--state-ok); }
.syno-rl-res--warn { color: var(--state-warn); }
.syno-rl-res--crit { color: var(--state-crit); }
.syno-rl-res--info { color: var(--state-info); }
.syno-rl-res--neutral { color: var(--dim); }
.syno-rl-drapeau { font-weight: 700; letter-spacing: .02em; }
.syno-rl-drapeau--crit { color: var(--state-crit); }
.syno-rl-drapeau--warn { color: var(--state-warn); }
.syno-rl-pages { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: 8px; padding: 12px 16px; }
.syno-rl-pages-texte { margin-right: auto; color: var(--dim); font-size: var(--fs-sm); font-variant-numeric: tabular-nums; }
.syno-rl-pages .btn-sm { white-space: nowrap; }
/* Politique › Réglages : section « Relance des sauvegardes » et interrupteur du parc. */
.syno-rl-pol-tete { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 8px 16px; }
.syno-rl-pol-note { margin: 0; color: var(--dim); font-size: var(--fs-sm); }
.syno-rl-interrupteur {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; padding: 12px 16px;
  border: 1px solid var(--border); border-left: 3px solid var(--state-ok); border-radius: var(--radius-sm); background: var(--surface);
}
.syno-rl-interrupteur.is-suspendu { border-left-color: var(--state-warn); }
.syno-rl-inter-lib { font-size: var(--fs-sm); font-weight: 600; white-space: nowrap; }
.syno-rl-inter-quand { color: var(--dim); font-size: var(--fs-xs); font-variant-numeric: tabular-nums; }
.syno-rl-inter-aide { flex-basis: 100%; margin: 0; color: var(--dim); font-size: var(--fs-xs); }
.syno-rl-interrupteur .btn-sm { white-space: nowrap; }
.syno-rl-reserve { color: var(--dim); font-size: var(--fs-xs); }
.syno-rl-pol-table td, .syno-rl-pol-table th { white-space: nowrap; }
.syno-rl-pol-produit { font-weight: 600; text-align: left; }
.syno-rl-pol-ligne.is-modifie > th { box-shadow: inset 3px 0 0 var(--state-warn); }
.syno-rl-pol-reglage { min-width: 14rem; white-space: normal; }
.syno-rl-pol-source { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 4px; margin-right: 8px; }
.syno-rl-pol-reglage .np-sous { margin: 4px 0; }
.syno-rl-pol-reglage .btn-sm { margin-top: 4px; white-space: nowrap; }
.syno-rl-plage { display: inline-flex; flex-wrap: nowrap; align-items: center; gap: 4px; }
.syno-rl-plage-a { color: var(--dim); font-size: var(--fs-sm); }
.syno-rl-pol-pied { display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: space-between; gap: 12px; }
.syno-rl-pol-pied > .btn-sm { white-space: nowrap; }
.syno-rl-causes { flex: 1 1 320px; min-width: 0; }
.syno-rl-causes > summary { cursor: pointer; color: var(--accent-2); font-size: var(--fs-sm); }
.syno-rl-causes > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.syno-rl-causes-liste { margin: 8px 0 0; padding: 0; list-style: none; display: grid; gap: 8px; font-size: var(--fs-sm); }
.syno-rl-causes-liste li { padding: 8px 12px; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface); overflow-wrap: anywhere; }
.syno-rl-cause-tete { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 8px; }
.syno-rl-cause-tete .np-sous { display: inline; }
/* Fenetre « Suspendre la relance automatique ». */
.syno-rl-durees { display: flex; flex-wrap: wrap; gap: 8px 16px; margin: 0 0 12px; padding: 0; border: 0; }
.syno-rl-durees > legend { margin-bottom: 8px; padding: 0; }
.syno-rl-duree {
  display: inline-flex; align-items: center; gap: 8px; margin: 0; color: var(--text); font-size: var(--fs-sm);
  font-weight: 400; text-transform: none; letter-spacing: normal; cursor: pointer;
}
.syno-rl-duree.is-reserve { color: var(--dim); cursor: not-allowed; }
.syno-rl-erreur { margin: 0 0 12px; color: var(--state-crit); font-size: var(--fs-sm); }
/* Le motif : meme intitule que « Durée » (pas les capitales espacees des label de formulaire). */
.syno-rl-champ { margin-top: 0; color: var(--text); text-transform: none; letter-spacing: normal; }
@media (max-width: 900px) {
  .syno-rl-cellule { max-width: 16rem; }
  .syno-rl-pol-reglage { min-width: 12rem; }
  .syno-rl-j-champs { gap: 8px; }
  /* Fiche sur un telephone : la ligne « Relance automatique » reste sous les yeux -- collee a gauche
     du tableau qui defile, a la largeur de l'ecran (sinon sa phrase sortait a droite, a lire en
     faisant defiler le tableau). */
  .syno-rl-tr > td > .syno-rl-ligne { position: sticky; left: 0; max-width: calc(100vw - 72px); }
}
@media (max-width: 600px) {
  .syno-rl-td { min-width: 10rem; }
  .syno-rl-j-table { table-layout: auto; }
  .syno-rl-pages-texte { flex-basis: 100%; }
  .syno-rl-interrupteur { padding: 8px 12px; }
  .syno-rl-j-champ { flex: 1 1 140px; }
  .syno-rl-j-champ .np-select { width: 100%; }
  .syno-rl-filtres-titre { flex-basis: 100%; }
}

/* ── PSA > Fiche client (psa-client.js, viewPsaClientFiche) — 05/10/2026 ──────
   Demande de Jerome : « on doit pouvoir editer une fiche client, ajouter des
   contacts ». La carte Contacts porte ses actions (ajouter, modifier,
   supprimer) et sa fenetre de saisie. Les styles en dur de la page passent
   ici, en jetons des deux themes ; espacements pris dans l'echelle. */
.fc-titre-client { color: var(--muted); font-size: var(--fs-md); font-weight: 400; }
.fc-entete { display: flex; justify-content: space-between; align-items: center;
             flex-wrap: wrap; gap: var(--space-3); margin-bottom: var(--space-3); }
.fc-entete h2 { margin: 0; font-size: var(--fs-md); }
.fc-entete .btn-sm, .fc-vide + .btn-sm { white-space: nowrap; }
.fc-compte { color: var(--muted); font-weight: 400; font-size: var(--fs-sm);
             font-variant-numeric: tabular-nums; }

/* Identite : libelle a gauche, valeur a droite ; l'un sous l'autre sur telephone. */
.fc-ligne { display: flex; gap: var(--space-3); padding: var(--space-2) 0;
            border-bottom: 1px solid var(--border); }
.fc-ligne:last-child { border-bottom: 0; }
.fc-libelle { flex: 0 0 150px; color: var(--muted); font-size: var(--fs-sm); }
.fc-valeur { min-width: 0; font-size: var(--fs-sm); line-height: 1.5; overflow-wrap: anywhere; }
@media (max-width: 600px) {
  .fc-ligne { flex-direction: column; gap: var(--space-1); }
  .fc-libelle { flex-basis: auto; }
}

/* Fiche client : carte « Configuration » — reglages d'exploitation en lecture seule (24/10/2026). */
.fc-conf-aide { margin: 0 0 var(--space-3); color: var(--muted); font-size: var(--fs-xs); }
.fc-conf-ligne { display: flex; align-items: center; gap: var(--space-2); padding: var(--space-2) 0;
                 border-bottom: 1px solid var(--border); font-size: var(--fs-sm); }
.fc-conf-ligne:last-child { border-bottom: 0; }
.fc-conf-ligne .ni { flex: 0 0 auto; }
.fc-conf-sous { padding-left: var(--space-5); }
.fc-conf-on { color: var(--color-ok); }
.fc-conf-off { color: var(--color-crit); }
.fc-conf-btn { width: 100%; border: 0; border-bottom: 1px solid var(--border); background: none; color: inherit; font: inherit; font-size: var(--fs-sm); text-align: left; cursor: pointer; }
.fc-conf-btn:hover:not(:disabled) { background: var(--bg-2); }
.fc-conf-btn:disabled { cursor: default; opacity: 0.6; }

/* Contacts : une liste et non un tableau. Mesure dans Chromium (bench _rendu) :
   l'ancien tableau, avec la colonne d'actions qu'il lui aurait fallu, elargissait
   sa carte et ecrasait l'Identite (540 -> 381 px a 1366 px) ; sur un telephone
   de 360 px, ses boutons sortaient de l'ecran (bord droit a 971 px). */
.fc-liste { list-style: none; margin: 0; padding: 0; }
.fc-contact { padding: var(--space-3) 0; border-bottom: 1px solid var(--border); }
.fc-contact:first-child { padding-top: 0; }
.fc-contact:last-child { padding-bottom: 0; border-bottom: 0; }
.fc-contact-tete { display: flex; justify-content: space-between; align-items: flex-start; gap: var(--space-2); }
.fc-contact-nom { min-width: 0; overflow-wrap: anywhere; }
/* ⚠ overflow-wrap: anywhere sur tout texte saisi : une fonction ou un numero
   sans espace ni tiret (« ResponsableAdministratifEtFinancier… ») elargissait
   les deux cartes a 1015 px sur un ecran de 360 px, et poussait boutons,
   crayons et corbeilles hors de l'ecran (relecture charte du 05/10). */
.fc-contact-role { margin-top: var(--space-1); color: var(--muted); font-size: var(--fs-sm);
                   overflow-wrap: anywhere; }
.fc-contact-coord { display: flex; flex-wrap: wrap; gap: var(--space-1) var(--space-4);
                    margin-top: var(--space-1); font-size: var(--fs-sm); }
.fc-contact-coord > * { min-width: 0; overflow-wrap: anywhere; }
/* Notes : trois lignes au plus dans la carte ; le texte entier est au survol
   (title) et dans la fenetre de modification. */
.fc-notes { margin-top: var(--space-1); color: var(--muted); font-size: var(--fs-sm);
            white-space: pre-line; overflow-wrap: anywhere; overflow: hidden;
            display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; }
.fc-vide { margin: 0 0 var(--space-3); color: var(--muted); font-size: var(--fs-sm); }
.fc-erreur { margin: var(--space-3) 0 0; padding: var(--space-2) var(--space-3);
             border: 1px solid var(--state-crit-glow); border-radius: var(--radius-sm);
             background: var(--state-crit-soft); color: var(--state-crit); font-size: var(--fs-sm); }

/* Fenetre d'un contact (showModal). Le titre laisse la place de la croix de
   fermeture, posee en absolu dans le coin. */
.fc-modale-titre { margin: 0; padding-right: var(--space-6); font-size: var(--fs-lg); }
.fc-modale-client { margin: var(--space-1) 0 0; color: var(--muted); font-size: var(--fs-sm); }
.fc-form-grille { display: grid; grid-template-columns: 1fr 1fr; gap: 0 var(--space-4); }
.fc-form-grille > .fc-large { grid-column: 1 / -1; }
.fc-form textarea { resize: vertical; }
.fc-modale-actions { display: flex; justify-content: flex-end; flex-wrap: wrap; gap: var(--space-2);
                     margin-top: var(--space-6); }
@media (max-width: 600px) {
  .fc-form-grille { grid-template-columns: 1fr; }
  /* Au doigt, le crayon et la corbeille ne se touchent plus : 8 px entre eux. */
  .fc-contact .cellule-actions { gap: var(--space-2); }
}

/* 05/10/2026 -- fiche du poste, onglet Secrets : une ligne par secret et, sous la
   valeur actuelle, ses valeurs precedentes (cle BitLocker remplacee, sequestre
   historise). La valeur revelee reste masquee par [hidden] tant qu'on ne l'a pas demandee. */
.secret-bloc { padding: 8px 0; border-bottom: 1px solid var(--border); }
.secret-ligne { display: flex; align-items: center; gap: 12px; }
.secret-info { flex: 1; min-width: 0; }
.secret-nom { font-weight: 600; font-size: var(--fs-base); }
.secret-nom em { color: var(--dim); font-weight: 400; }
.secret-sous { margin-top: 4px; color: var(--dim); font-size: var(--fs-xs); font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
.secret-sous--fort { color: var(--text); font-weight: 600; }
.secret-valeur {
  margin-top: 8px; padding: 8px 12px; background: var(--code-bg); border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm); font-family: 'Cascadia Code', 'Consolas', 'Courier New', monospace;
  font-size: var(--fs-sm); word-break: break-all; user-select: all;
}
.secret-historique { display: flex; flex-direction: column; gap: 8px; margin: 8px 0 0 12px; padding-left: 12px; border-left: 2px solid var(--border-strong); }
.secret-historique-titre { color: var(--dim); font-size: var(--fs-xs); font-weight: 600; white-space: nowrap; font-variant-numeric: tabular-nums; }

/* 05/10/2026 -- onglet Mises a jour du poste : failles activement exploitees (Microsoft ou
   CISA KEV) encore ouvertes, et les KB qui les ferment. Lecture seule. */
.wu-kev { margin-bottom: 16px; border-left: 3px solid var(--state-neutral); }
.wu-kev--crit { border-left-color: var(--state-crit); }
.wu-kev--ok { border-left-color: var(--state-ok); }
.wu-kev-titre { display: flex; align-items: center; gap: 8px; margin: 0 0 8px; font-size: var(--fs-lg); white-space: nowrap; }
.wu-kev-titre .ni { margin-right: 0; color: var(--state-crit); }
.wu-kev--ok .wu-kev-resume .ni { color: var(--state-ok); }
.wu-kev-n { font-variant-numeric: tabular-nums; }
.wu-kev-resume { margin: 0 0 8px; font-size: var(--fs-sm); line-height: 1.5; }
.wu-kev-note { margin: 0 0 12px; font-size: var(--fs-sm); color: var(--dim); line-height: 1.5; }
.wu-kev-desc { color: var(--dim); font-size: var(--fs-xs); }
.wu-kev-table td { vertical-align: top; }
.wu-kev-kb, .wu-kev-constat { white-space: nowrap; font-variant-numeric: tabular-nums; }
.wu-kev-selon .badge, .wu-kev-marque { white-space: nowrap; }
.wu-kev-sources { margin: 8px 0 0; color: var(--dim); font-size: var(--fs-xs); }
.wu-kev-sources--erreur { color: var(--state-warn); }

/* 05/10/2026 -- fiche du poste, onglet Inventaire : securite du poste (agent Windows
   1.0.106, public/js/poste_securite.js). Jetons uniquement -> clair + sombre. */
.ps-badge { font-variant-numeric: tabular-nums; white-space: nowrap; }
.ps-note { margin: 4px 0 0; color: var(--dim); font-size: var(--fs-xs); }
.ps-note--erreur { color: var(--color-warn); }
.ps-chiffre { margin: 0 0 12px; font-size: var(--fs-xl); font-weight: 700; font-variant-numeric: tabular-nums; }
.ps-unite { color: var(--dim); font-size: var(--fs-sm); font-weight: 400; }
.ps-liste { margin: 0; padding: 0; list-style: none; display: flex; flex-wrap: wrap; gap: 4px 12px; }
.ps-liste li { font-variant-numeric: tabular-nums; word-break: break-word; }
.ps-detail { color: var(--dim); }
.ps-defile { overflow-x: auto; }
.ps-etats { display: inline-flex; flex-wrap: wrap; gap: 4px; }
.ps-num { font-variant-numeric: tabular-nums; white-space: nowrap; }

/* 06/10/2026 -- Gestion NAS : tableaux a largeurs fixes (relecture 154, session 158 ; Jerome, deux fois :
   « déborde de l'écran »). A ~1 670 px, « Sauvegardes » faisait 1 593 px pour un cadre de 1 398, et
   main#view (overflow-x: clip) en rognait la fin sans barre ni bruit ; « À traiter » defilait encore a
   1 440 px. Les deux tiennent maintenant dans leur cadre des 1 280 px (barre laterale ouverte) :
   table-layout: fixed, largeurs en % ; textes secondaires sous le texte principal ; en-tetes sur deux
   lignes au plus ; tout revient a la ligne, rien ne sort de sa cellule (les badges, eux, restent sur
   une ligne : leurs colonnes sont assez larges). Sous 1 280 px, le cadre defile (1 000 px au moins) ;
   sous 600 px, la regle des telephones (cellules en nowrap, largeur naturelle, plus haut) reprend la
   main. Jetons et echelle 4/8/12/16/24 seulement, aucun style en dur. */
.syno-table { width: 100%; min-width: 1000px; border-collapse: collapse; table-layout: fixed; font-size: var(--fs-sm); }
.syno-table thead th {
  padding: 8px; color: var(--dim); font-size: var(--fs-xs); letter-spacing: .04em; white-space: normal;
  vertical-align: bottom; background: var(--surface-2); border-bottom: 1px solid var(--border);
}
.syno-table .syno-tri { white-space: normal; text-align: left; }
.syno-table td { padding: 8px; vertical-align: top; overflow-wrap: anywhere; border-bottom: 1px solid var(--border); }
.syno-table td.col-actions { white-space: normal; }
.syno-table .syno-num { text-align: right; }
.syno-table .badge { white-space: nowrap; font-variant-numeric: tabular-nums; }
.syno-bk-carte { padding: 0; overflow: hidden; }
.syno-date { white-space: nowrap; font-variant-numeric: tabular-nums; }
/* Lignes de client et sous-en-tetes de NAS (« Sauvegardes » et « À traiter ») : le nom du NAS ouvre sa
   fiche, le client a cote, puis « Ouvrir le DSM » et « Identifiants ». */
.syno-table tr.syno-bk-client > td { padding: 8px; border-top: 2px solid var(--accent); background: var(--color-accent-soft-bg); color: var(--accent-2); font-weight: 700; }
.syno-table tr.syno-bk-nas > td, .syno-table tr.syno-issues-subheader > td { padding: 8px; border-top: 1px solid var(--border-strong); background: var(--surface-2); }
.syno-bk-nas-nom { color: var(--accent-2); font-weight: 600; text-decoration: none; }
.syno-bk-nas-nom:hover { text-decoration: underline; }
.syno-bk-nas-nom:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.syno-bk-nas-client { margin-left: 8px; color: var(--dim); font-size: var(--fs-xs); }
.syno-bk-nas-acces { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 4px; margin-left: 12px; font-size: var(--fs-xs); vertical-align: middle; }
/* « Sauvegardes » : le nom, et dessous le produit et la destination ; la duree sous la taille. */
.syno-table tr.syno-bk-ligne--retard > td { background: var(--state-warn-soft); }
.syno-bk-nom { display: block; font-weight: 600; }
.syno-bk-sous { display: block; margin-top: 4px; color: var(--dim); font-size: var(--fs-xs); font-weight: 400; }
.syno-bk-jamais { color: var(--state-crit); font-weight: 600; }
/* Cadence : le mode et sa source, puis la cadence estimee (ou la prochaine execution), le reglage
   manuel et le bouton « Alerte de retard ». */
.syno-cadence { color: var(--dim); font-size: var(--fs-xs); }
.syno-cad-mode, .syno-cad-suite { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; }
.syno-cad-suite { margin-top: 4px; }
.syno-cad-source { display: inline-flex; align-items: center; gap: 4px; padding: 0 4px; border-radius: var(--radius-sm); background: var(--state-neutral-soft); color: var(--state-neutral); white-space: nowrap; }
.syno-cad-source--dsm { background: var(--state-info-soft); color: var(--state-info); }
.syno-cad-source--manuelle { background: var(--color-accent-soft-bg); color: var(--accent-2); }
.syno-cad-source .ni { width: 12px; height: 12px; margin: 0; }
.syno-bk-override.ghost { display: inline-flex; align-items: center; justify-content: center; padding: 4px; border-radius: var(--radius-sm); background: transparent; color: var(--dim); vertical-align: middle; }
.syno-bk-override.ghost:hover { color: var(--text); }
.syno-bk-override.ghost:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* « À traiter » : l'element, et dessous le message de DSM (deux lignes au plus, le texte entier au
   survol) ; l'etat d'une sauvegarde en echec est un badge qui ouvre le detail de l'erreur. */
.syno-at-nas { color: inherit; text-decoration: none; }
.syno-at-nas:hover { text-decoration: underline; }
.syno-at-nas:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.syno-at-element { display: block; font-weight: 600; }
.syno-at-erreur {
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; overflow: hidden;
  margin-top: 4px; color: var(--state-crit); font-size: var(--fs-xs);
}
.syno-at-fs { display: block; }
.syno-err-detail { font-family: inherit; cursor: pointer; }
.syno-err-detail:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Largeurs (en-tetes triables : leur data-col). « Sauvegardes » : 7 colonnes avec « Relance » ; sans
   elle, les autres se partagent sa place. */
.syno-bk-table th[data-col="job"] { width: 21%; }
.syno-bk-table th[data-col="statut"] { width: 12%; }
.syno-bk-table th[data-col="relance"] { width: 21%; }
.syno-bk-table th[data-col="cadence"] { width: 14%; }
.syno-bk-table th[data-col="dernier_run"], .syno-bk-table th[data-col="dernier_succes"] { width: 11%; }
.syno-bk-table th[data-col="taille"] { width: 10%; }
/* « À traiter » : avec « Relance auto », Type, Élément, État, Relance auto, Cadence, Dernière réussite,
   Actions ; sans elle (serveur d'avant), Client, NAS, Type, Élément, Cadence, Dernière réussite, État,
   Actions. */
.syno-at-table th[data-col="client"] { width: 10%; }
.syno-at-table th[data-col="nas"] { width: 11%; }
/* 07/10/2026 (relecture 172) : 11 % pour « Type » (2 de plus, pris à « Élément ») -- une ligne « À examiner » y
   nomme son domaine : à 9 % (74 px de texte à 1 280 px), « Notifications » se coupait en « Notification / s »
   (« Alimentation », « Installations » au ras). */
.syno-at-table th[data-col="type"] { width: 11%; }
.syno-at-table th[data-col="element"] { width: 16%; }
.syno-at-table th[data-col="cadence"] { width: 13%; }
.syno-at-table th[data-col="dernier_ok"] { width: 12%; }
.syno-at-table th[data-col="etat"] { width: 12%; }
.syno-at-table th.col-actions { width: 15%; }
.syno-at-table--relance th[data-col="relance"] { width: 22%; }
.syno-at-table--relance th.col-actions { width: 14%; }
@media (max-width: 600px) {
  .syno-table { table-layout: auto; }
}

/* 06/10/2026 -- Gestion NAS, lot 7 de la revue du parc NAS (« À traiter ») : bandeaux de tete (une ligne
   chacun, seulement s'il y a lieu : HeXaControl n'entend plus le parc, coupure de site), et la note grise
   sous l'element d'une ligne (equipements muets d'un site coupe, motif d'un NAS « À examiner » : deux
   lignes au plus, le texte entier au survol). Jetons et echelle 4/8/12/16 seulement. */
.syno-bandeau { margin: 0 0 12px; }
.syno-bandeau-lien:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.syno-at-note {
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; overflow: hidden;
  margin-top: 4px; color: var(--dim); font-size: var(--fs-xs);
}
/* Lot 7 (« Sauvegardes ») : la cause lisible sous le statut d'une sauvegarde en echec (deux lignes au plus,
   le texte entier et la reference technique au survol) ; sur la fiche, la ligne de la cause sous la
   sauvegarde ; dans la fenetre d'erreur, la cause avant le message brut de DSM. */
.syno-bk-cause {
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; overflow: hidden;
  margin-top: 4px; color: var(--dim); font-size: var(--fs-xs); overflow-wrap: anywhere;
}
.syno-job-cause-tr > td { padding: 4px 12px 8px; }
.syno-job-cause { color: var(--state-crit); font-size: var(--fs-xs); overflow-wrap: anywhere; }
.syno-fenetre-cause { margin: 0 0 12px; font-size: var(--fs-sm); }

/* ═══ Agents SentinelOne (cahier 159, 06/10/2026) — public/js/s1-agents.js ════════════════════════
   Page Sécurité, onglet « Agents SentinelOne » : liste de la console S1, cartes-filtres (gabarit
   .np-compteur), tableau dans son cadre (il défile seul quand il ne tient pas, la page jamais),
   menu « Agir », barre de lot, fenêtre de confirmation. Jetons de thème seulement, échelle
   4 / 8 / 12 / 16 / 24. */
.s1ag-tete { margin: 0 0 4px; }
.s1ag-titre { margin: 0; }
.s1ag-titre-portee { font-size: var(--fs-md); font-weight: 400; }
.s1ag-sous-titre { margin: 4px 0 0; }
.s1ag-masque { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
.s1ag-chargement { color: var(--dim); font-size: var(--fs-sm); }
.s1ag-entete { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px 16px; margin: 0 0 16px; }
.s1ag-resume { margin: 0; color: var(--dim); font-size: var(--fs-sm); font-variant-numeric: tabular-nums; }
.s1ag-entete .btn-sm { white-space: nowrap; }
.s1ag-bandeau { margin: 0 0 12px; padding: 8px 12px; border-left: 3px solid var(--state-crit); border-radius: var(--radius-sm); background: var(--state-crit-soft); color: var(--text); font-size: var(--fs-sm); }
.s1ag-note { margin: 0 0 16px; padding: 8px 12px; border-left: 3px solid var(--state-info); border-radius: var(--radius-sm); background: var(--state-info-soft); color: var(--text); font-size: var(--fs-sm); }
.s1ag-vide { margin: 16px 0; color: var(--dim); font-size: var(--fs-sm); }
.s1ag-outils { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 16px; margin: 0 0 16px; }
.s1ag-outil { display: inline-flex; align-items: center; gap: 8px; margin: 0; color: var(--dim); font-size: var(--fs-sm); font-weight: 400; text-transform: none; letter-spacing: 0; white-space: nowrap; min-width: 0; }
.s1ag-outil select, .s1ag-outil input { width: auto; min-width: 0; max-width: 100%; padding: 4px 8px; font-size: var(--fs-sm); border-radius: var(--radius-sm); }
.s1ag-outil--recherche { flex: 1 1 200px; }
.s1ag-outil--recherche .s1ag-recherche { flex: 1 1 auto; min-width: 160px; }
.s1ag-lot { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 0 0 12px; padding: 8px 12px; border: 1px solid var(--accent); border-radius: var(--radius-sm); background: var(--accent-glow); font-size: var(--fs-sm); }
.s1ag-lot-n { font-weight: 600; font-variant-numeric: tabular-nums; white-space: nowrap; }
.s1ag-lot .btn-sm { white-space: nowrap; }
.s1ag-lot-raison { color: var(--text); font-size: var(--fs-xs); }

.s1ag-cadre { overflow-x: auto; -webkit-overflow-scrolling: touch; max-width: 100%; border: 1px solid var(--border); border-radius: var(--radius-sm); }
.s1ag-table { width: 100%; border-collapse: collapse; font-size: var(--fs-sm); }
.s1ag-table th { padding: 8px; text-align: left; color: var(--dim); font-size: var(--fs-xs); font-weight: 600; letter-spacing: .04em; white-space: nowrap; border-bottom: 1px solid var(--border-strong); }
.s1ag-table td { padding: 8px; border-bottom: 1px solid var(--border); vertical-align: top; }
/* La ligne ne s'ouvre pas au clic (le nom, si) : pas de main, pas de reflet sur la ligne dépliée. */
.s1ag-table tbody tr { cursor: default; }
.s1ag-table tbody tr.s1ag-detail:hover { background: none; }
.s1ag-table tbody tr.s1ag-ligne:hover { background: var(--surface); }
/* 25/10/2026 (revue R14-14) : repli des navigateurs sans color-mix(), qui ignorent la règle de la ligne cochée du lot
   L08 (plus bas, même voile de 8 % d'accent). Ce n'est pas un doublon à retirer. */
.s1ag-table tr.is-choisie { background: rgba(var(--accent-rgb), 0.08); }
.s1ag-num { font-variant-numeric: tabular-nums; white-space: nowrap; }
.s1ag-col-case { width: 32px; }
.s1ag-col-machine { min-width: 152px; }
.s1ag-col-client, .s1ag-col-fiche { min-width: 120px; overflow-wrap: anywhere; }
.s1ag-col-sys { min-width: 88px; }
.s1ag-col-etat { min-width: 112px; }
.s1ag-col-agir { width: 1%; white-space: nowrap; text-align: right; }
.s1ag-deplier { display: inline-flex; align-items: center; gap: 4px; padding: 0; border: 0; background: transparent; color: var(--text); font: inherit; font-weight: 600; text-align: left; cursor: pointer; }
.s1ag-deplier .ni { flex: none; width: 14px; height: 14px; color: var(--dim); transition: transform .15s ease; }
/* Le chevron du sprite pointe à droite : fermé à droite, ouvert vers le bas. */
.s1ag-deplier[aria-expanded="true"] .ni { transform: rotate(90deg); }
.s1ag-nom { overflow-wrap: anywhere; }
/* « ..3753 » : l'agent, pas la machine (session 168) — sous le nom, devant les badges. */
.s1ag-fin { color: var(--dim); font-size: var(--fs-xs); font-variant-numeric: tabular-nums; white-space: nowrap; }
.s1ag-col-machine .s1ag-badges { align-items: center; }
.s1ag-badges { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 4px; }
.s1ag-col-etat .s1ag-badges { margin-top: 0; }
.s1ag-badge { font-variant-numeric: tabular-nums; white-space: nowrap; }
.s1ag-sous { display: block; margin-top: 4px; color: var(--dim); font-size: var(--fs-xs); }
.s1ag-lien { color: var(--accent-2); text-decoration: none; }
.s1ag-lien:hover { text-decoration: underline; }
/* « Sans fiche HeXaControl » : un agent sans fiche, jamais une machine disparue (session 168). */
.s1ag-sans-fiche { color: var(--dim); }
.s1ag-machine { display: block; }
.s1ag-client { display: inline; margin-right: 4px; }

/* Session 200 (09/10) : UN geste par agent, « Retirer SentinelOne » (le menu « Agir » à trois entrées a disparu de
   l'écran). Sous le bouton, la voie que le serveur a choisie (« par la console », « décommission »…) ou « impossible » ;
   la phrase entière est dans l'infobulle, pour le lecteur d'écran et dans le détail de la ligne (.s1ag-annonce). */
.s1ag-retirer { display: inline-flex; flex-direction: column; align-items: flex-end; gap: 4px; }
.s1ag-retirer-bouton { white-space: nowrap; }
.s1ag-retirer-voie { color: var(--dim); font-size: var(--fs-xs); white-space: nowrap; }
.s1ag-annonce { margin: 0 0 12px; color: var(--text); font-size: var(--fs-sm); font-weight: 600; overflow-wrap: anywhere; }

.s1ag-detail > td { padding: 12px 16px 16px; background: var(--surface); }
.s1ag-faits { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 8px 16px; margin: 0 0 12px; }
.s1ag-fait dt { color: var(--dim); font-size: var(--fs-xs); }
.s1ag-fait dd { margin: 0; overflow-wrap: anywhere; font-variant-numeric: tabular-nums; }
.s1ag-detail-titre { margin: 0 0 4px; color: var(--dim); font-size: var(--fs-xs); font-weight: 600; text-transform: uppercase; letter-spacing: .04em; }
.s1ag-preuves, .s1ag-alertes { margin: 0 0 12px; padding-left: 16px; display: grid; gap: 4px; }
.s1ag-preuves .s1ag-sous { display: inline; }
.s1ag-alertes li { color: var(--text); }
.s1ag-detail-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.s1ag-activite { color: var(--dim); font-size: var(--fs-xs); overflow-wrap: anywhere; }
.s1ag-console { text-decoration: none; }

.s1ag-gestes { margin-top: 24px; }
.s1ag-section-titre { margin: 0 0 8px; font-size: var(--fs-lg); }
.s1ag-geste-agent { display: inline-block; margin-right: 12px; }
.s1ag-geste-agent .s1ag-sous { display: inline; }
.s1ag-renvoi { margin: 8px 0 0; font-size: var(--fs-sm); }
.s1ag-renvoi a { color: var(--accent-2); }

.s1ag-fenetre { display: block; }
.s1ag-f-titre { margin: 0 32px 8px 0; font-size: var(--fs-xl); }
.s1ag-f-texte { margin: 0 0 16px; color: var(--text); font-size: var(--fs-sm); line-height: 1.5; }
.s1ag-f-groupe { margin: 12px 0 4px; font-size: var(--fs-sm); font-weight: 600; }
.s1ag-f-liste { margin: 0 0 8px; padding-left: 16px; display: grid; gap: 4px; font-size: var(--fs-sm); overflow-wrap: anywhere; }
.s1ag-f-ecartes li { color: var(--dim); }
/* Ce que le geste fait à CET agent et ce que sa machine garde : lisible, pas en gris. */
.s1ag-f-consequence { display: block; margin-top: 4px; color: var(--text); }
/* Session 171 (A2) : « La machine X existe dans HeXaControl (fiche #N, vue le …) » — la phrase qui retient la main. */
.s1ag-f-existe { font-weight: 600; }
.s1ag-f-champ { display: block; margin: 16px 0 4px; color: var(--text); font-size: var(--fs-sm); font-weight: 400; text-transform: none; letter-spacing: 0; }
.s1ag-f-saisie { display: block; width: 100%; margin: 0 0 12px; padding: 8px 12px; font: inherit; }
.s1ag-f-case { display: flex; align-items: flex-start; gap: 8px; margin: 0 0 12px; font-size: var(--fs-sm); font-weight: 600; }
.s1ag-f-case input { margin-top: 4px; flex: none; }
.s1ag-f-note { margin: 8px 0 0; color: var(--dim); font-size: var(--fs-xs); }
.s1ag-f-erreur { margin: 12px 0 0; padding: 8px 12px; border-left: 3px solid var(--state-crit); border-radius: var(--radius-sm); background: var(--state-crit-soft); color: var(--text); font-size: var(--fs-sm); }
.s1ag-f-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 8px; margin-top: 16px; }
.s1ag-f-actions button { white-space: nowrap; }
.s1ag-f-actions button.danger:disabled { opacity: .4; cursor: not-allowed; box-shadow: none; }
.s1ag-f-resultat { margin: 0; padding: 0; list-style: none; display: grid; gap: 8px; font-size: var(--fs-sm); }
.s1ag-f-res { padding: 8px 12px; border-left: 3px solid var(--state-neutral); border-radius: var(--radius-sm); background: var(--surface-2); overflow-wrap: anywhere; }
.s1ag-f-res--ok { border-left-color: var(--state-ok); }
.s1ag-f-res--warn { border-left-color: var(--state-warn); }
.s1ag-f-res--crit { border-left-color: var(--state-crit); }
/* Session 200 : ce que « Retirer SentinelOne » va faire, en une phrase (plan), et la fenêtre d'archivage d'un poste. */
.s1ag-f-annonce { display: block; margin-top: 4px; color: var(--text); font-weight: 600; }
.s1ag-arch { margin: 0 0 8px; padding: 12px 16px; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface); }
.s1ag-arch .s1ag-f-annonce { margin: 0 0 8px; font-size: var(--fs-sm); }
.s1ag-arch-case { display: flex; align-items: flex-start; gap: 8px; margin: 0 0 8px; color: var(--text); font-size: var(--fs-sm); font-weight: 600; text-transform: none; letter-spacing: 0; }
.s1ag-arch-case input { margin-top: 4px; flex: none; }
.s1ag-arch-texte { margin: 0 0 8px; color: var(--text); font-size: var(--fs-sm); line-height: 1.5; overflow-wrap: anywhere; }
.s1ag-arch-liste { margin: 0 0 8px; padding-left: 16px; display: grid; gap: 4px; font-size: var(--fs-sm); overflow-wrap: anywhere; }
.s1ag-arch-actions { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 8px; }
.s1ag-arch .s1ag-f-saisie { margin-bottom: 8px; }
/* Session 200 : « Retirer SentinelOne » est plus large que l'ancien « Agir ». Sous 1 440 px, les cellules du tableau des
   agents se serrent (4 px de côté, comme les tableaux de Gestion NAS) : il tient dans son cadre à 1 280 px, bouton compris. */
@media (max-width: 1439px) {
  .s1ag-table--agents > thead th, .s1ag-table--agents .s1ag-ligne > td { padding-left: 4px; padding-right: 4px; }
  .s1ag-table--agents > thead th:first-child, .s1ag-table--agents .s1ag-ligne > td:first-child { padding-left: 8px; }
  .s1ag-table--agents > thead th:last-child, .s1ag-table--agents .s1ag-ligne > td:last-child { padding-right: 8px; }
}
@media (max-width: 600px) {
  .s1ag-outil { white-space: normal; }
  .s1ag-outil--recherche { flex-basis: 100%; }
}

/* ═══ Gestion NAS, lot 7 -- finitions (06/10/2026) ═══════════════════════════════════════════════
   Fiche : tableau des sauvegardes en classes (plus de style en dur), triable ; cartes « Mise en
   service » et « Santé ». Sous-en-tetes : lien DSM et « Identifiants » en classes (plus de fleche ni
   d'emoji). Jetons et echelle 4/8/12/16 seulement. */
.syno-produit { margin-bottom: 16px; }
.syno-produit-titre { margin: 0 0 8px; }
.syno-produit-n { color: var(--dim); font-size: var(--fs-sm); font-weight: 400; }
.syno-fiche-table { width: 100%; border-collapse: collapse; font-size: var(--fs-sm); }
/* Cellules serrees (4 px de cote) : le tableau tient dans la fiche des 1 280 px, indicateurs de tri compris. */
.syno-fiche-table th { padding: 8px 4px; text-align: left; color: var(--dim); font-size: var(--fs-xs); font-weight: 600; white-space: nowrap; border-bottom: 1px solid var(--border); }
.syno-fiche-table td { padding: 8px 4px; vertical-align: top; border-bottom: 1px solid var(--border); }
.syno-fiche-table th:first-child, .syno-fiche-table td:first-child { padding-left: 8px; }
.syno-fiche-table th:last-child, .syno-fiche-table td:last-child { padding-right: 8px; }
/* Relecture 167 : sous 1 280 px, les en-tetes triables passent a la ligne (deux lignes au plus, comme ceux
   des autres tableaux de Gestion NAS). En nowrap, « DERNIÈRE EXÉCUTION » et son indicateur elargissaient
   le tableau au-dela de l'ecran a 1 100 px : « Taille » et « Relancer » hors champ, alors qu'avant le lot 7
   le tableau tenait (bord droit 1 076 px). Sur un telephone, la regle commune (nowrap) reprend la main. */
@media (max-width: 1279px) {
  .syno-fiche-table th { white-space: normal; }
  .syno-fiche-table .syno-tri { white-space: normal; text-align: left; }
  /* L'indicateur ne garde sa place que sur la colonne triee : reserve sur les six en-tetes, il ajoutait
     encore ~40 px, et « Relancer » restait rogne a 1 100 px. */
  .syno-fiche-table .syno-tri-th:not([aria-sort]) .syno-tri { gap: 0; }
  .syno-fiche-table .syno-tri-th:not([aria-sort]) .syno-tri-ind { display: none; }
}
.syno-fiche-table .syno-num { text-align: right; }
.syno-fiche-table .syno-dim { color: var(--dim); }
.syno-fiche-ligne--retard > td { background: var(--state-warn-soft); }
.syno-dsm-lien { color: var(--accent); text-decoration: none; }
.syno-dsm-lien:hover { text-decoration: underline; }
.syno-dsm-lien:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.syno-glue-mini { display: inline-flex; align-items: center; gap: 4px; margin: 0; padding: 0 8px; border: 1px solid var(--border); border-radius: 4px;
  background: transparent; color: var(--dim); font: inherit; font-size: var(--fs-xs); line-height: 1.6; white-space: nowrap; cursor: pointer; }
.syno-glue-mini:hover { color: var(--text); border-color: var(--border-strong); }
.syno-glue-mini:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.syno-glue-mini .ni { width: 12px; height: 12px; }
.syno-mes-liste { margin: 0; padding: 0; list-style: none; display: grid; gap: 8px; }
.syno-mes-ligne { display: grid; grid-template-columns: minmax(8rem, 12rem) auto minmax(0, 1fr) auto; align-items: center; gap: 8px 12px; }
.syno-mes-ligne > .badge { justify-self: start; white-space: nowrap; }
.syno-mes-etape { font-weight: 600; }
.syno-mes-detail { min-width: 0; color: var(--dim); font-size: var(--fs-sm); overflow-wrap: anywhere; }
.syno-mes-action { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 8px; }
@media (max-width: 700px) {
  .syno-mes-ligne { grid-template-columns: minmax(0, 1fr) auto; }
  .syno-mes-detail, .syno-mes-action { grid-column: 1 / -1; justify-content: flex-start; }
}
/* La valeur a cote de son libelle, les libelles en une colonne (celle du plus long), pas au bout de la ligne ;
   sous 600 px, le libelle au-dessus de sa valeur. */
.syno-sante-liste { margin: 0; display: grid; grid-template-columns: max-content minmax(0, 1fr); align-items: baseline; gap: 8px 16px; }
.syno-sante-ligne { display: contents; }
.syno-sante-ligne > dt { color: var(--dim); font-size: var(--fs-sm); }
.syno-sante-ligne > dd { margin: 0; font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
@media (max-width: 600px) {
  .syno-sante-liste { grid-template-columns: minmax(0, 1fr); gap: 4px; }
  .syno-sante-ligne > dd { margin-bottom: 8px; }
}
.syno-sante-val--warn { color: var(--state-warn); font-weight: 600; }
.syno-sante-val--crit { color: var(--state-crit); font-weight: 600; }
/* « À traiter » (D7-13) : sous 1 280 px, le tableau defile dans son cadre -- la colonne Actions y reste
   visible, collee a droite sur un fond opaque, un filet la separe (au-dela, le tableau tient : rien ne defile). */
@media (max-width: 1279px) {
  #syno-issues .tbl-scroll th.col-actions,
  #syno-issues .tbl-scroll td.col-actions { position: sticky; right: 0; border-left: 1px solid var(--border-strong); }
  /* Relecture 167 : le fond est OPAQUE. --surface et --surface-2 sont translucides (3,5 et 7 % de blanc en
     sombre) : le texte qui defile dessous se lisait au travers des boutons (captures a 360 px). Le meme voile,
     pose sur le fond de la page : la colonne garde la teinte de la carte, en clair comme en sombre. */
  #syno-issues .tbl-scroll td.col-actions { background: linear-gradient(var(--surface), var(--surface)), var(--bg-0); }
  #syno-issues .tbl-scroll thead th.col-actions { background: linear-gradient(var(--surface-2), var(--surface-2)), var(--bg-0); }
}
/* Relecture 167 : sur un telephone, la regle commune des tableaux met toutes les cellules en nowrap
   (!important) : la colonne collee prenait la largeur de sa plus longue raison (« aucun poste Windows en
   ligne dans … ») et couvrait presque tout le tableau. Ses boutons et sa raison passent a la ligne. */
@media (max-width: 600px) {
  #syno-issues .tbl-scroll td.col-actions { white-space: normal !important; }
}

/* Gestion NAS, lot 7 (D6-09) -- SMS en attente d'envoi : la ligne sous les bandeaux d'« À traiter » (le
   bouton a droite), et la fenetre qui les liste. */
.syno-bandeau-bouton { flex: none; align-self: center; }
.syno-sms-quand, .syno-sms-dest, .syno-sms-nas { white-space: nowrap; font-variant-numeric: tabular-nums; }
.syno-sms-texte { min-width: 12rem; overflow-wrap: anywhere; }
/* Relecture 167 : la raison d'un SMS qui ne s'annule plus, SOUS le message (dans une cellule, le span restait collé au texte). */
.syno-sms-texte .syno-raison { display: block; }
.syno-sms-coche:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ── Pré-analyse d'un ticket (07/10/2026) ─────────────────────────────────────
   La carte « Pré-analyse » de la fiche (public/js/tickets.js, carteSPregestion)
   et son réglage par client (public/js/users.js, Réglages → HeXaIA). Tokens
   seulement ; espacements pris dans l'échelle --space-*. */
.tk-pg { padding: var(--space-3); margin-bottom: var(--space-3); border-left: 3px solid var(--accent); }
.tk-pg-tete { display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-2); }
.tk-pg-titre { display: inline-flex; align-items: center; gap: var(--space-1); color: var(--dim); font-size: var(--fs-xs);
  font-weight: 600; text-transform: uppercase; letter-spacing: 0.06em; white-space: nowrap; }
.tk-pg-tete .badge { white-space: nowrap; }
.tk-pg-sous { margin-left: auto; color: var(--dim); font-size: var(--fs-xs); white-space: nowrap; }
.tk-pg-texte { margin: var(--space-2) 0 0; font-size: var(--fs-sm); line-height: 1.55; white-space: pre-wrap; overflow-wrap: anywhere; }
.tk-pg-attenue { color: var(--dim); }
.tk-pg-liste { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: var(--space-1) var(--space-3);
  margin: var(--space-3) 0 0; font-size: var(--fs-sm); }
.tk-pg-liste dt { color: var(--dim); white-space: nowrap; }
.tk-pg-liste dd { margin: 0; min-width: 0; overflow-wrap: anywhere; }
.tk-pg-bloc { margin-top: var(--space-3); font-size: var(--fs-sm); }
.tk-pg-libelle { display: block; color: var(--dim); font-size: var(--fs-xs); font-weight: 600; text-transform: uppercase; letter-spacing: 0.06em; }
.tk-pg-puces { margin: var(--space-1) 0 0; padding-left: var(--space-4); }
.tk-pg-puces li + li { margin-top: var(--space-1); }
.tk-pg-brouillon { margin-top: var(--space-3); padding: var(--space-3); border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--bg-1); }
.tk-pg-brouillon-tete { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2);
  color: var(--dim); font-size: var(--fs-xs); font-weight: 600; text-transform: uppercase; letter-spacing: 0.06em; }
.tk-pg-actions { display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-2); margin-top: var(--space-3); }
.tk-pg-actions .primary { white-space: nowrap; }
.tk-pg-aide { color: var(--dim); font-size: var(--fs-xs); }
.tk-pg-details { margin-top: var(--space-3); font-size: var(--fs-sm); }
.tk-pg-details summary { cursor: pointer; color: var(--dim); }
.tk-pg-details summary:focus-visible, .tk-pg-actions button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.tk-pg-sonde { margin-top: var(--space-2); }
.tk-pg-sonde-titre { display: block; color: var(--dim); font-size: var(--fs-xs); font-weight: 600; }
.tk-pg-sortie { max-height: 320px; overflow: auto; margin: var(--space-1) 0 0; padding: var(--space-2); background: var(--bg-2);
  border: 1px solid var(--border); border-radius: 6px; font-size: var(--fs-xs); white-space: pre-wrap; overflow-wrap: anywhere; }
.tk-pg-pied { margin: var(--space-3) 0 0; color: var(--dim); font-size: var(--fs-xs); font-variant-numeric: tabular-nums; }
@media (max-width: 600px) {
  .tk-pg-sous { margin-left: 0; }
  .tk-pg-liste { grid-template-columns: minmax(0, 1fr); }
  .tk-pg-liste dt { margin-top: var(--space-2); }
}
/* Réglage : la fonction, puis les clients concernés (liste filtrable). */
.pg-reglage { margin: 0 0 var(--space-2) 0; }
.pg-reglage > label { display: flex; gap: var(--space-2); align-items: flex-start; cursor: pointer; }
.pg-reglage > label > input { margin-top: 3px; flex: none; }
.pg-reglage-titre { font-size: var(--fs-base); font-weight: 600; }
.pg-reglage-aide { display: block; margin-top: 2px; color: var(--dim); font-size: var(--fs-xs); }
.pg-clients { margin: var(--space-2) 0 0 var(--space-6); max-width: 32rem; }
.pg-clients input[type="search"] { width: 100%; }
.pg-liste { max-height: 220px; overflow-y: auto; margin-top: var(--space-2); border: 1px solid var(--border); border-radius: 6px; padding: var(--space-1); }
.pg-client { display: flex; align-items: center; gap: var(--space-2); padding: var(--space-1) var(--space-2); cursor: pointer; border-radius: 4px; }
.pg-client:hover { background: var(--bg-2); }
.pg-client input:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.pg-client-depuis { margin-left: auto; color: var(--dim); font-size: var(--fs-xs); white-space: nowrap; font-variant-numeric: tabular-nums; }
/* 20/10/2026 (assistant IA, relecture des écrans) : l'état daté d'un client de l'assistant (activé depuis…, avenant…,
   salariés informés…, CSE…) est long : il passe à la ligne, aligné à droite, au lieu d'être coupé par la liste. */
.pg-client-depuis--long { min-width: 0; white-space: normal; text-align: right; overflow-wrap: anywhere; }
/* 20/10/2026 (assistant IA, seconde relecture des écrans) : à côté d'un état daté long, le nom du client tient sur UNE
   ligne (règle des libellés) : il ne rétrécit pas, et un nom très long finit en points de suspension. */
.pg-client-nom { flex: none; max-width: 60%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.pg-etat { margin: var(--space-1) 0 0; color: var(--dim); font-size: var(--fs-xs); min-height: 1em; }
.pg-etat--erreur { color: var(--color-crit); }
/* 08/10/2026 — l'essai à blanc et les suggestions en un clic (tickets.js, carteSPregestion),
   et l'écran Tickets → Essais IA (viewTicketsEssais). Même carte, bord en pointillé : rien
   n'est parti. Tokens seulement ; espacements --space-*. */
.tk-pg--essai { border-left-style: dashed; }
.tk-pg-sugg { list-style: none; margin: var(--space-1) 0 0; padding: 0; display: grid; gap: var(--space-1); }
.tk-pg-sugg li { display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-1) var(--space-2); }
.tk-pg-sugg-valeur { font-weight: 600; }
.tk-pg-sugg .ghost { padding: var(--space-1) var(--space-2); font-size: var(--fs-xs); white-space: nowrap; }
.tk-pg-sugg button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.tk-es-intro { max-width: 60rem; margin-bottom: 0; }
.tk-es-filtres { display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--space-2) var(--space-4); margin-bottom: var(--space-3); }
.tk-es-champ { display: flex; flex-direction: column; gap: var(--space-1); color: var(--dim); font-size: var(--fs-xs); }
.tk-es-aide { margin: 0; color: var(--dim); font-size: var(--fs-xs); overflow-wrap: anywhere; }
.tk-es-carte { margin-bottom: var(--space-3); }
.tk-es-titre { margin: 0 0 var(--space-2); font-size: var(--fs-md); }
.tk-es-table td { vertical-align: top; }
.tk-es-num { text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }
.tk-es-sujet { min-width: 14rem; overflow-wrap: anywhere; }
.tk-es-client { white-space: nowrap; }
.tk-es-message { min-width: 10rem; }
.tk-es-total td { font-weight: 600; }
.tk-es-vide, .tk-es-erreur { margin: 0; font-size: var(--fs-sm); }
.tk-es-erreur { color: var(--color-crit); }
/* 08/10/2026 (session 187) — la PISTE DU TECHNICIEN dans la carte « Pré-analyse »
   (tickets.js, cartePgPiste) : causes classées, étapes de diagnostic et de résolution,
   l'outil du RMM de chacune, la commande sous la pastille « à vérifier ». Une commande
   longue passe à la ligne (jamais de défilement horizontal, même à 360 px). Tokens
   seulement ; espacements --space-*. */
.tk-pg-piste { margin-top: var(--space-3); padding: var(--space-3); border: 1px solid var(--border); border-radius: var(--radius-sm); }
.tk-pg-piste-tete { display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-2); }
.tk-pg-piste .badge { white-space: nowrap; }
.tk-pg-causes, .tk-pg-etapes { margin: var(--space-1) 0 0; padding-left: var(--space-4); font-size: var(--fs-sm); }
.tk-pg-causes li, .tk-pg-etapes li { overflow-wrap: anywhere; }
.tk-pg-causes li + li, .tk-pg-etapes li + li { margin-top: var(--space-2); }
.tk-pg-indice { display: block; margin-top: var(--space-1); color: var(--dim); font-size: var(--fs-xs); }
.tk-pg-outil { display: inline-block; padding: 0 var(--space-2); border: 1px solid var(--border); border-radius: 999px;
  color: var(--dim); font-size: var(--fs-xs); white-space: nowrap; }
.tk-pg-cmd-ligne { display: flex; align-items: baseline; flex-wrap: wrap; gap: var(--space-1) var(--space-2); margin-top: var(--space-1); }
.tk-pg-cmd { min-width: 0; white-space: pre-wrap; overflow-wrap: anywhere; }
.tk-pg-garde { display: block; margin-top: var(--space-1); color: var(--color-warn); font-size: var(--fs-xs); }
.tk-pg-vigilance { display: flex; align-items: baseline; flex-wrap: wrap; gap: var(--space-2); }
.tk-es-piste { white-space: nowrap; }
/* 08/10/2026 (session 193) — les TICKETS SIMILAIRES déjà résolus dans la carte « Pré-analyse »
   (tickets.js, cartePgSimilaires) : le lien vers chaque ticket, puis le client et la date, puis
   la résolution que la piste a lue. Un sujet long passe à la ligne (jamais de défilement
   horizontal, même à 360 px). Tokens seulement ; espacements --space-*. */
.tk-pg-sim { list-style: none; margin: var(--space-1) 0 0; padding: 0; display: grid; gap: var(--space-2); }
.tk-pg-sim li { min-width: 0; overflow-wrap: anywhere; }
.tk-pg-sim-lien { color: var(--accent); font-weight: 600; text-decoration: none; }
.tk-pg-sim-lien:hover { text-decoration: underline; }
.tk-pg-sim-lien:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.tk-pg-sim-meta { display: block; margin-top: var(--space-1); color: var(--dim); font-size: var(--fs-xs); font-variant-numeric: tabular-nums; }
.tk-pg-sim-resolution { display: block; margin-top: var(--space-1); line-height: 1.55; }

/* 07/10/2026 -- fiche du poste : par ou il est relie au reseau, Wi-Fi, Ethernet, VPN...
   (public/js/poste_connexion.js ; vue 360 « Inventaire & reseau » et carte Identite).
   Jetons uniquement (couleurs, tailles, espacements --space-*) -> clair + sombre. Le type est
   un badge neutre : c'est une nature, pas un etat ; seul un signal Wi-Fi faible prend une
   couleur d'etat (badge--warn). */
.pc-resume { display: inline-flex; flex-wrap: wrap; align-items: center; gap: var(--space-1) var(--space-2); }
.pc-badge { font-variant-numeric: tabular-nums; white-space: nowrap; }
.pc-detail { color: var(--text); font-variant-numeric: tabular-nums; }
.pc-note { margin: var(--space-1) 0 0; color: var(--dim); font-size: var(--fs-xs); font-weight: 400; }
.pc-cle { padding: var(--space-1) var(--space-3) var(--space-1) 0; color: var(--muted); white-space: nowrap; vertical-align: top; }
/* ⚠ Sous 600 px, toute cellule de tableau passe en nowrap (!important) : la cellule porte aussi
   .td-wrap, l'exception prevue, sinon la note ne revient pas a la ligne et sort de l'ecran. */
.pc-valeur { padding: var(--space-1) 0; }
/* ⚠ .kv est une grille a deux colonnes (libelle fixe, valeur 1fr) : sans min-width: 0, la colonne s'elargit a la largeur du
   tableau des interfaces, qui ne defile plus et que main#view (overflow-x: clip) rogne en silence. */
.pc-kv { min-width: 0; }
.pc-interfaces { margin-top: var(--space-2); }
.pc-interfaces > summary { cursor: pointer; color: var(--dim); font-size: var(--fs-sm); white-space: nowrap; }
.pc-defile { overflow-x: auto; margin-top: var(--space-2); }
.pc-principale > td { font-weight: 600; }
.pc-num { font-variant-numeric: tabular-nums; white-space: nowrap; }

/* ── Gestion NAS, lot 5 de la revue du parc NAS (08/10/2026) ────────────────────────────────────────────
   Politique › Réglages : le MOTIF d'un réglage propre sous le réglage (« Motif manquant » en ambre), et le champ
   « Motif » de la barre d'enregistrement. Page « Exceptions » (synology_exceptions.js) : cinq cartes (réglages
   propres, exceptions datées, exceptions de fait, régime du prestataire, sauvegardes « ne plus signaler »), le
   bandeau des motifs manquants, la fenêtre des formulaires. Carte « Commandes » : « Annuler… » sous l'état.
   Jetons uniquement (couleurs, tailles, espacements --space-*) -> clair + sombre ; aucun style dans le HTML. */
.np-motif { flex-basis: 100%; display: flex; align-items: flex-start; gap: var(--space-2); color: var(--dim); font-size: var(--fs-sm); }
.np-motif .ni { flex: none; margin-top: 2px; }
.np-motif-texte { min-width: 0; overflow-wrap: anywhere; }
.np-enregistrement-motif { display: inline-flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); flex: 1 1 320px; min-width: 0; }
.np-enregistrement-motif .np-saisie { flex: 1 1 240px; min-width: 0; }
.syno-cmds-etat .syno-cmd-annuler { display: flex; width: max-content; margin-top: var(--space-1); white-space: nowrap; }
.syno-exc-bandeau { margin: 0 0 var(--space-4); }
.syno-exc-carte { margin-bottom: var(--space-4); min-width: 0; }
.syno-exc-tete { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-2); }
.syno-exc-tete .syno-fiche-titre { margin: 0; }
.syno-exc-tete .btn-sm { white-space: nowrap; }
.syno-exc-n { font-variant-numeric: tabular-nums; }
.syno-exc-table tr.is-manquant > td:first-child { box-shadow: inset 3px 0 0 var(--state-warn); }
.syno-exc-table tr.is-retiree { color: var(--dim); }
.syno-exc-sous { display: block; margin-top: var(--space-1); color: var(--dim); font-size: var(--fs-xs); overflow-wrap: anywhere; }
.syno-exc-motif { overflow-wrap: anywhere; }
.syno-exc-nas { font-weight: 600; white-space: nowrap; }
.syno-exc-col-nas { min-width: 10rem; }
.syno-exc-col-motif { min-width: 14rem; max-width: 32rem; }
.syno-exc-col-qui, .syno-exc-col-etat { white-space: nowrap; font-variant-numeric: tabular-nums; }
.syno-exc-col-gestes { white-space: nowrap; }
.syno-exc-col-gestes .btn-sm + .btn-sm { margin-left: var(--space-2); }
.syno-exc-dl { display: grid; grid-template-columns: minmax(10rem, max-content) minmax(0, 1fr); gap: var(--space-2) var(--space-4); margin: 0; font-size: var(--fs-sm); }
.syno-exc-dl dt { color: var(--dim); }
.syno-exc-dl dd { margin: 0; min-width: 0; overflow-wrap: anywhere; }
.syno-exc-liste { margin: 0; padding-left: var(--space-4); display: grid; gap: var(--space-1); }
.syno-exc-liste--icones { padding-left: 0; list-style: none; }
.syno-exc-liste--icones li { display: flex; align-items: center; gap: var(--space-2); }
.syno-exc-fenetre .syno-saisie { box-sizing: border-box; }
.syno-exc-fenetre textarea.syno-saisie { resize: vertical; min-height: 48px; }
.syno-exc-form-grille { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 0 var(--space-4); }
.syno-exc-seuils { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 0 var(--space-3); }
.syno-exc-seuils .syno-champ .syno-saisie { margin-top: var(--space-1); font-weight: 400; }
@media (max-width: 600px) {
  .syno-exc-dl { grid-template-columns: minmax(0, 1fr); gap: var(--space-1); }
  .syno-exc-dl dd { margin-bottom: var(--space-2); }
}

/* ── Gestion NAS, lot 5b de la revue du parc NAS (08/10/2026) ───────────────────────────────────────────
   Politique › Réglages (Parc) : la FILE DE TRAVAIL -- son état, son dernier passage (listes par catégorie), le formulaire
   (mode, plafonds, motif) et le journal ; « État et envois » : sa ligne. Onglet « Contrôles » (synology_controles.js) :
   le tableau des états en lecture seule et la carte des certificats du prestataire suivis par DOC.
   Jetons uniquement (couleurs, tailles, espacements --space-*) -> clair + sombre ; aucun style dans le HTML. */
.np-file-ligne { margin: 0 0 var(--space-3); }
.np-file-ligne .badge { font-variant-numeric: tabular-nums; }
.np-file-ligne .np-sous { display: inline; }
#np-file .np-section-titre .badge { margin-left: var(--space-2); }
.np-file-passage { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); margin: var(--space-3) 0 var(--space-2); font-size: var(--fs-sm); }
.np-file-passage .np-sous { display: inline; }
.np-file-liste { margin: 0; padding-left: 0; list-style: none; display: grid; gap: var(--space-2); font-size: var(--fs-sm); }
.np-file-cat { font-weight: 600; }
.np-file-items { margin: var(--space-1) 0 0; padding-left: var(--space-4); display: grid; gap: var(--space-1); }
.np-file-items .np-sous { display: inline; overflow-wrap: anywhere; }
.np-file-apercu { margin-top: var(--space-3); padding: var(--space-2) var(--space-3); border-left: 3px solid var(--color-info); border-radius: 6px; background: var(--surface-2); }
.np-file-form { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-3); margin-top: var(--space-4);
  padding-top: var(--space-3); border-top: 1px solid var(--border); }
.np-file-champ { display: inline-flex; align-items: center; gap: var(--space-2); color: var(--dim); font-size: var(--fs-sm); white-space: nowrap; }
.np-file-form .np-saisie { flex: 1 1 240px; min-width: 0; }
.np-file-form .btn-sm { white-space: nowrap; }
.np-file-journal { width: 100%; font-size: var(--fs-sm); }
.np-file-journal td, .np-file-journal th { white-space: nowrap; }
.syno-ctl-certs { margin-bottom: var(--space-4); min-width: 0; }
.syno-ctl-certs .syno-fiche-titre { margin: 0 0 var(--space-2); }
.syno-ctl-liste { margin: 0 0 var(--space-2); padding-left: 0; list-style: none; display: grid; gap: var(--space-2); font-size: var(--fs-sm); }
.syno-ctl-liste li { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
.syno-ctl-liste .badge { font-variant-numeric: tabular-nums; }
.syno-ctl-hote { font-weight: 600; font-variant-numeric: tabular-nums; }
.syno-ctl-sous { display: block; color: var(--dim); font-size: var(--fs-xs); overflow-wrap: anywhere; }
.syno-ctl-liste .syno-ctl-sous { display: inline; }
.syno-ctl-table { width: 100%; border-collapse: collapse; font-size: var(--fs-sm); }
.syno-ctl-table th { padding: var(--space-2); text-align: left; color: var(--dim); font-size: var(--fs-xs); font-weight: 600; white-space: nowrap;
  border-bottom: 1px solid var(--border); background: var(--surface-2); }
.syno-ctl-table td { padding: var(--space-2); vertical-align: top; border-bottom: 1px solid var(--border); }
.syno-ctl-pile { display: grid; gap: var(--space-1); justify-items: start; }
.syno-ctl-table .badge { white-space: nowrap; font-variant-numeric: tabular-nums; }
.syno-ctl-nas { min-width: 10rem; }
.syno-ctl-redir { display: inline-flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
.syno-ctl-verifier { white-space: nowrap; }
.np-file-dl { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: var(--space-1) var(--space-3); margin: var(--space-2) 0 0; font-size: var(--fs-sm); }
.np-file-dl dt { color: var(--dim); }
.np-file-dl dd { margin: 0; overflow-wrap: anywhere; font-variant-numeric: tabular-nums; }

/* ── Fiche d'une alerte (09/10/2026, session 201) ─────────────────────────────────────
   Jerome : « quand on clique sur une alerte, il faut ouvrir l'alerte pour voir le
   contenu ». La LIGNE d'une alerte (tableau de bord, page Alertes) s'ouvre au clic et au
   clavier ; sa fiche vit dans la fenetre commune (showModal) -- public/js/alerte_fiche.js.
   Jetons uniquement (couleurs, tailles, --space-*) : clair et sombre ; aucun style dans le
   HTML. */
.al-ligne { cursor: pointer; }
/* Tableau de bord : le filet sous chaque alerte, la derniere comprise (tbody tr:last-child td
   retire celui de ses cellules). Il etait ecrit dans le HTML de la ligne ; il vit ici. */
#dash-alerts .al-ligne { border-bottom: 1px solid var(--border); }
/* Le focus clavier se voit sur la ligne entiere : l'anneau DEDANS (la carte d'un widget
   coupe ce qui depasse), et le fond des cellules. */
.al-ligne:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; border-radius: 0; }
.al-ligne:focus-visible > td { background: var(--surface-2); }
/* En edition du tableau de bord, on deplace les blocs : la ligne n'ouvre rien. */
body.dashboard-edit-mode .al-ligne { cursor: inherit; }

.al-fiche { display: grid; gap: var(--space-4); min-width: 0; }
/* La croix de la fenetre commune est posee en haut a droite, par-dessus le contenu : la
   premiere rangee lui laisse sa place. */
.al-fiche > :first-child { padding-right: var(--space-6); }
.al-fiche-tete { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
.al-fiche-tete .badge { white-space: nowrap; }
.al-fiche-titre { margin: 0; font-size: var(--fs-lg); line-height: 1.3; color: var(--text); overflow-wrap: anywhere; }
/* Le message de l'agent, en entier : un chemin Windows sans espace passe a la ligne. */
.al-fiche-message { margin: 0; padding: var(--space-3); border-left: 3px solid var(--border-strong); border-radius: var(--radius-sm);
  background: var(--surface-2); color: var(--text); font-size: var(--fs-sm); line-height: 1.5;
  white-space: pre-wrap; overflow-wrap: anywhere; }
.al-fiche[data-gravite="crit"] .al-fiche-message { border-left-color: var(--state-crit); }
.al-fiche[data-gravite="warn"] .al-fiche-message { border-left-color: var(--state-warn); }
.al-fiche[data-gravite="info"] .al-fiche-message { border-left-color: var(--state-info); }
.al-fiche-dl { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: var(--space-2) var(--space-4);
  margin: 0; font-size: var(--fs-sm); }
.al-fiche-dl dt { color: var(--dim); white-space: nowrap; }
.al-fiche-dl dd { margin: 0; color: var(--text); overflow-wrap: anywhere; font-variant-numeric: tabular-nums; }
.al-fiche-sous { color: var(--dim); }
.al-fiche-lien { color: var(--accent-2); }
.al-fiche-ia { display: grid; gap: var(--space-2); padding: var(--space-3); border: 1px solid var(--border);
  border-radius: var(--radius-sm); background: var(--surface); }
.al-fiche-ia-titre { margin: 0; font-size: var(--fs-sm); font-weight: 600; color: var(--text); }
.al-fiche-ia-resume { margin: 0; font-size: var(--fs-sm); font-weight: 600; color: var(--text); overflow-wrap: anywhere; }
.al-fiche-ia-texte { margin: 0; font-size: var(--fs-sm); line-height: 1.5; color: var(--text); white-space: pre-wrap; overflow-wrap: anywhere; }
.al-fiche-ia-meta { margin: 0; font-size: var(--fs-xs); color: var(--dim); overflow-wrap: anywhere; }
.al-fiche-gestes { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: var(--space-2);
  padding-top: var(--space-3); border-top: 1px solid var(--border); }
/* « Ouvrir le poste » est un lien, les deux autres des boutons : button.btn-sm (plus haut) leur
   donne une autre taille de texte et une autre marge qu'a .btn-sm sur un lien. Ici, les trois
   se ressemblent. */
.al-fiche-gestes .btn-sm { white-space: nowrap; font-size: var(--fs-sm); padding: var(--space-2) var(--space-3); }
/* Le geste principal : la bordure d'accent, le texte reste --text (lisible dans les deux themes ;
   .btn-sm--accent, texte d'accent sur fond teinte, n'atteint pas 4,5:1 sur la fenetre commune). */
.al-fiche-gestes .al-fiche-principal { border-color: var(--accent); }
.al-fiche-etat { margin: 0; color: var(--dim); font-size: var(--fs-sm); }
.al-fiche-erreur { margin: 0; padding: var(--space-3); border-left: 3px solid var(--state-crit); border-radius: var(--radius-sm);
  background: var(--state-crit-soft); color: var(--text); font-size: var(--fs-sm); overflow-wrap: anywhere; }

/* ═══ SOCLE VISUEL HeXaControl — début (09/10/2026, branche visuel-socle) ═══════════════════════════════
   Les composants et utilitaires communs qui remplacent les 30 motifs de styles en dur les plus frequents
   (inventaire du 09/10/2026 : 9 301 styles en dur, 2 289 emoji-icones dans les ecrans).
   Guide de migration : docs/DESIGN_MIGRATION.md · demonstration : public/design.html (non liee au menu) ·
   banc : tests/bench_socle_design_rendu.js.

   ⚠ POSE SANS RIEN CHANGER A L'ECRAN. Toutes les classes de ce bloc sont NEUVES (prefixe hx-, et ni--NN
     pour les tailles d'icone) : aucun ecran ne les porte encore, aucune regle existante n'est modifiee.
     Les classes existantes deja conformes sont gardees comme ALIAS, dans leur regle d'origine (la meme
     declaration, donc rien ne bouge) : .empty = .hx-vide · .sec-title = .hx-titre · .sec-head = .hx-entete ·
     .page-head = .hx-entete-page · .text-dim = .hx-attenue · .link-accent = .hx-lien ·
     .tbl-scroll = .hx-defile · .spinner = .hx-spinner.

   Les regles du bloc sont celles de la charte : jetons seuls (--bg-*, --text, --dim, --border*, --accent*,
   --color-*, --fs-*, et les jetons de surface deja definis pour un role : --code-bg, --overlay-bg) ;
   espacements 4 · 8 · 12 · 16 · 24 · 32 · 48 (--space-1 a -4, --space-6, 32 et 48 px ; jamais --space-5,
   20 px, hors echelle) ; aucun !important ; clair ET sombre par les jetons ; focus visible sur tout ce qui
   se clique. La couche mobile s'accroche au TEXTE des styles en dur ([style*="display:flex"]…) : chaque
   classe qui remplace un tel style porte ici son propre @media (parite web / PWA, fin du bloc).

   TON. Un seul mecanisme pour la severite : un composant lit --hx-ton et en tire son texte, son fond et son
   filet ; les variantes --crit, --warn, --ok, --info, --neutre, --accent ne posent que --hx-ton.
   Le TEXTE melange 70 % de la couleur d'etat au texte du theme : --color-crit ne donne que 3,9:1 sur les
   fonds sombres, sous les 4,5:1 du petit texte ; melange, il passe 5:1 en sombre et 7:1 en clair, sans
   jeton nouveau. Le filet, le point et la jauge gardent la couleur pure (3:1 suffit a un element graphique).
   ══════════════════════════════════════════════════════════════════════════════════════════════════════ */

/* ── Disposition : pile, grappe, grille ───────────────────────────────────────────────────────────────────
   Pile : des blocs l'un sous l'autre, ecartes par le gap (12 px). Grappe : des elements cote a cote qui
   passent a la ligne quand la place manque (8 px). Les marges verticales des enfants sont annulees :
   c'est le gap qui espace, pas les marges des titres et des libelles (h2, label en posent). */
.hx-pile { display: flex; flex-direction: column; gap: var(--space-3); min-width: 0; }
.hx-grappe { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); min-width: 0; }
.hx-pile > *, .hx-grappe > * { margin-top: 0; margin-bottom: 0; }
.hx-grappe--entre { justify-content: space-between; }
.hx-grappe--fin { justify-content: flex-end; }
.hx-grappe--haut { align-items: flex-start; }
.hx-grappe--base { align-items: baseline; }
.hx-grappe--sans-retour { flex-wrap: nowrap; }
/* L'element qui prend la place restante d'une grappe (un champ de recherche, un texte). Base de 12rem :
   quand il n'a plus cette place, il passe a la ligne et la prend entiere, au lieu de s'ecraser a zero. */
.hx-flex-1 { flex: 1 1 12rem; min-width: 0; }
/* Pousse ce qui suit au bout de la grappe (boutons d'une barre). */
.hx-pousse { margin-left: auto; }

/* Grille de fiches : autant de colonnes de 18rem qu'il en tient, une seule sur un telephone, jamais plus
   large que l'ecran (min(100 %, 18rem)). --2col / --3col / --4col : nombre fixe, une colonne sous 800 px
   (comme .grid-2 / -3 / -4). */
.hx-grille { display: grid; gap: var(--space-4); grid-template-columns: repeat(auto-fill, minmax(min(100%, 18rem), 1fr)); min-width: 0; }
.hx-grille--2col { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.hx-grille--3col { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.hx-grille--4col { grid-template-columns: repeat(4, minmax(0, 1fr)); }
/* Vignettes (icones, petits indicateurs) : des colonnes de 9rem. */
.hx-grille--etroite { gap: var(--space-2); grid-template-columns: repeat(auto-fill, minmax(min(100%, 9rem), 1fr)); }
.hx-grille > * { min-width: 0; }
/* Un enfant de grille sur toute la largeur. */
.hx-pleine { grid-column: 1 / -1; }

/* ── Titres et en-tetes ───────────────────────────────────────────────────────────────────────────────────
   .hx-titre (alias .sec-title, regle d'origine plus haut) : titre de section, --fs-md, icone a gauche.
   .hx-entete (alias .sec-head) : le titre a gauche, ses actions a droite. .hx-entete-page (alias
   .page-head) : la meme chose pour le haut d'une page. */
.hx-titre .ni { margin: 0; opacity: 1; }
.hx-titre--lg { font-size: var(--fs-lg); font-weight: 700; }
.hx-sous-titre { margin: 0; color: var(--dim); font-size: var(--fs-sm); line-height: 1.5; }

/* ── Texte ────────────────────────────────────────────────────────────────────────────────────────────────
   .hx-attenue (alias .text-dim) : texte secondaire. .hx-note : note ou aide sous un bloc (--fs-sm ; les
   0.78rem et 0.85rem en dur s'y rabattent). Tailles : uniquement les jetons --fs-*. */
.hx-note { margin: 0; color: var(--dim); font-size: var(--fs-sm); line-height: 1.5; }
.hx-t-xs { font-size: var(--fs-xs); }
.hx-t-sm { font-size: var(--fs-sm); }
.hx-t-base { font-size: var(--fs-base); }
.hx-t-md { font-size: var(--fs-md); }
.hx-t-lg { font-size: var(--fs-lg); }
.hx-t-xl { font-size: var(--fs-xl); }
.hx-gras { font-weight: 600; }
.hx-fort { color: var(--text); font-weight: 600; }
/* Chiffres alignes en colonne : un compteur qui passe de 9 a 14 ne fait rien sauter. */
.hx-chiffres { font-variant-numeric: tabular-nums; }
.hx-une-ligne { white-space: nowrap; }
/* Texte coupe par une ellipse : le texte entier va dans un title sur l'element. */
.hx-tronque { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Une chaine sans espace (adresse, empreinte, nom de domaine) se coupe au lieu de sortir de son cadre. */
.hx-coupe { min-width: 0; overflow-wrap: anywhere; }
/* Texte d'etat : la couleur porte le sens, le melange garde le contraste (voir TON, en tete du bloc). */
.hx-texte-crit, .hx-texte-warn, .hx-texte-ok, .hx-texte-info { color: color-mix(in srgb, var(--hx-ton) 70%, var(--text)); }
.hx-texte-accent { color: var(--accent); }
/* Lu par un lecteur d'ecran, invisible a l'ecran (une annonce, un libelle de tableau). */
.hx-lecteur-seul {
  position: absolute; width: 1px; height: 1px; margin: 0; padding: 0; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap; border: 0;
}
.hx-lien:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Valeur technique en ligne (adresse IP, version, chemin). Neutre : l'accent identifie le produit. */
.hx-code {
  padding: 0 var(--space-1); border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--code-bg); color: var(--text);
  font-family: 'Cascadia Code', 'Consolas', 'Courier New', monospace; font-size: var(--fs-xs);
  overflow-wrap: anywhere;
}
/* Bloc de code ou de journal : il defile dans son cadre, jamais la page. */
.hx-bloc-code {
  margin: 0; padding: var(--space-3) var(--space-4); overflow-x: auto; max-width: 100%;
  border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--code-bg); color: var(--text);
  font-family: 'Cascadia Code', 'Consolas', 'Courier New', monospace; font-size: var(--fs-xs); line-height: 1.55;
  white-space: pre;
}
.hx-bloc-code code { padding: 0; border: 0; background: none; color: inherit; font-size: inherit; }
.hx-separateur { height: 0; margin: var(--space-4) 0; border: 0; border-top: 1px solid var(--border); }

/* ── Boutons ──────────────────────────────────────────────────────────────────────────────────────────────
   Un seul gabarit, sur <button> comme sur <a> : .hx-bouton (secondaire, par defaut), --primaire (l'action
   attendue de l'ecran, une par zone), --discret (sans cadre), --danger (la CONFIRMATION d'un geste
   destructif ; le rouge dit « ca va mal », jamais « important »). Tailles : --petit (28 px), moyen (36 px),
   --grand (44 px, une cible au doigt). --icone : icone seule, carree — toujours avec title ET aria-label. */
.hx-bouton {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2);
  min-height: 36px; padding: var(--space-2) var(--space-4);
  border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
  background: var(--bg-2); color: var(--text);
  font-family: inherit; font-size: var(--fs-sm); font-weight: 500; line-height: 1.25;
  white-space: nowrap; text-decoration: none; cursor: pointer;
  transition: background-color .14s ease, border-color .14s ease, color .14s ease;
}
.hx-bouton .ni { width: 16px; height: 16px; margin: 0; flex: none; }
.hx-bouton:hover:not(:disabled):not([aria-disabled="true"]) {
  border-color: var(--accent); background: color-mix(in srgb, var(--accent) 8%, var(--bg-2));
}
.hx-bouton:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.hx-bouton:disabled, .hx-bouton[aria-disabled="true"] { opacity: .5; cursor: not-allowed; }
/* Primaire, theme sombre : l'accent assombri d'un cinquieme par le fond de page. Blanc sur l'accent brut
   ne donne que 3,6:1 ; ici le texte du theme passe 4,7:1 (et 5,7:1 au survol). */
.hx-bouton--primaire {
  border-color: transparent; color: var(--text); font-weight: 600;
  background: color-mix(in srgb, var(--accent) 80%, var(--bg-0));
}
.hx-bouton--primaire:hover:not(:disabled):not([aria-disabled="true"]) {
  border-color: transparent; background: color-mix(in srgb, var(--accent) 70%, var(--bg-0));
}
/* Theme clair : l'accent est deja fonce (5,4:1 avec le blanc du theme), le survol passe a --accent-2. */
:root[data-theme="light"] .hx-bouton--primaire { background: var(--accent); color: var(--bg-2); }
:root[data-theme="light"] .hx-bouton--primaire:hover:not(:disabled):not([aria-disabled="true"]) { background: var(--accent-2); }
.hx-bouton--discret { border-color: transparent; background: transparent; color: var(--dim); }
.hx-bouton--discret:hover:not(:disabled):not([aria-disabled="true"]) {
  border-color: transparent; background: color-mix(in srgb, var(--text) 8%, transparent); color: var(--text);
}
.hx-bouton--danger {
  border-color: color-mix(in srgb, var(--color-crit) 45%, transparent);
  background: color-mix(in srgb, var(--color-crit) 12%, transparent);
  color: color-mix(in srgb, var(--color-crit) 70%, var(--text));
}
.hx-bouton--danger:hover:not(:disabled):not([aria-disabled="true"]) {
  border-color: var(--color-crit); background: color-mix(in srgb, var(--color-crit) 20%, transparent);
}
.hx-bouton--petit { min-height: 28px; padding: var(--space-1) var(--space-3); gap: var(--space-1); }
.hx-bouton--petit .ni { width: 14px; height: 14px; }
.hx-bouton--grand { min-height: 44px; padding: var(--space-3) var(--space-6); font-size: var(--fs-base); }
.hx-bouton--icone { width: 36px; padding: 0; }
.hx-bouton--icone.hx-bouton--petit { width: 28px; }
.hx-bouton--icone.hx-bouton--grand { width: 44px; }
/* 25/10/2026 (revue DES3-02) : un bouton qui porte un ton de texte garde sa couleur (« Mitiger » en ambre et « FP » en
   vert dans Sécurité, « Isoler du réseau », « Désactiver le compte » M365, les liens vers la console S1 en accent).
   .hx-bouton, de même poids que .hx-texte-* et écrit après eux, le ramenait au texte du thème. Sur le fond du bouton,
   au repos comme au survol, chaque ton tient 4,5:1 dans les deux thèmes (le plus juste : l'accent en sombre, 4,5:1 au
   survol). Les variantes de bandeau (.hx-bandeau--plein .hx-bouton, plus bas) l'emportent encore. Navigateur sans
   color-mix() : bloc @supports, en fin de socle. */
.hx-bouton.hx-texte-crit, .hx-bouton.hx-texte-warn, .hx-bouton.hx-texte-ok, .hx-bouton.hx-texte-info { color: color-mix(in srgb, var(--hx-ton) 70%, var(--text)); }
.hx-bouton.hx-texte-accent { color: var(--accent); }

/* Bouton-filtre (pilule) : une case qui filtre une liste et dit combien elle en garde. Etat par
   aria-pressed="true" (ou .is-actif) ; le nombre dans .hx-filtre__n, colore par --crit / --warn / --ok /
   --info quand il signale un etat. Remplace le bouton-pilule ecrit 41 fois en dur et les puces d'ecran
   (m365pb-puce, acct-puce, syno-rl-filtre, np-sous-onglet, np-niveau). */
.hx-filtre {
  display: inline-flex; align-items: center; gap: var(--space-2);
  min-height: 28px; padding: var(--space-1) var(--space-3);
  border: 1px solid var(--border-strong); border-radius: 999px;
  background: transparent; color: var(--dim);
  font-family: inherit; font-size: var(--fs-sm); font-weight: 500; line-height: 1.25;
  white-space: nowrap; text-decoration: none; cursor: pointer;
  transition: background-color .14s ease, border-color .14s ease, color .14s ease;
}
.hx-filtre .ni { width: 14px; height: 14px; margin: 0; flex: none; }
.hx-filtre:hover { border-color: var(--accent); color: var(--text); }
.hx-filtre:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.hx-filtre[aria-pressed="true"], .hx-filtre.is-actif {
  border-color: var(--accent); background: color-mix(in srgb, var(--accent) 16%, transparent); color: var(--text);
}
.hx-filtre__n { color: var(--text); font-weight: 600; font-variant-numeric: tabular-nums; }
.hx-filtre--crit .hx-filtre__n, .hx-filtre--warn .hx-filtre__n, .hx-filtre--ok .hx-filtre__n,
.hx-filtre--info .hx-filtre__n { color: color-mix(in srgb, var(--hx-ton) 70%, var(--text)); }

/* ── Badge ────────────────────────────────────────────────────────────────────────────────────────────────
   UNE forme (pilule, --fs-xs, 20 px de haut, chiffres tabulaires), seule la couleur porte le sens :
   --crit, --warn, --ok, --info, --neutre (par defaut), --accent (une marque, pas un etat). Non cliquable :
   un badge qui agit est un .hx-filtre ou un .hx-bouton. Un compteur de NAVIGATION reste .nav-dd-rollup. */
.hx-badge {
  --hx-ton: var(--dim);
  display: inline-flex; align-items: center; gap: var(--space-1);
  max-width: 100%; padding: 0 var(--space-2);
  border: 1px solid color-mix(in srgb, var(--hx-ton) 35%, transparent); border-radius: 999px;
  background: color-mix(in srgb, var(--hx-ton) 12%, transparent);
  color: color-mix(in srgb, var(--hx-ton) 70%, var(--text));
  font-size: var(--fs-xs); font-weight: 600; line-height: 1.6; letter-spacing: .02em;
  font-variant-numeric: tabular-nums; white-space: nowrap; vertical-align: middle; overflow: hidden;
}
.hx-badge .ni { width: 12px; height: 12px; margin: 0; flex: none; opacity: 1; }

/* ── Point d'etat ─────────────────────────────────────────────────────────────────────────────────────────
   Un rond de 8 px dessine par le navigateur : il remplace les ronds de couleur emoji (rouge, orange, jaune,
   vert, bleu, blanc, noir) et .stat-dot.
   Jamais seul : .hx-statut l'accole a son libelle (la couleur ne suffit pas a dire l'etat). */
.hx-point { --hx-ton: var(--dim); display: inline-block; flex: none; width: 8px; height: 8px; border-radius: 50%; background: var(--hx-ton); vertical-align: middle; }
.hx-point--off { background: transparent; box-shadow: inset 0 0 0 1px var(--dim); }
.hx-statut { display: inline-flex; align-items: center; gap: var(--space-2); white-space: nowrap; }
.hx-statut .ni { margin: 0; }

/* ── Bandeau d'etat ───────────────────────────────────────────────────────────────────────────────────────
   Un message dans la page : liseré et fond du ton, icone, texte, actions en bout. Il passe a la ligne,
   ne deborde jamais. Remplace les encadres en dur (fond + filet gauche) et leurs doubles d'ecran
   (syno-bandeau, s1ag-bandeau, np-bilan, apidoc-note, doc-note, au-alerte, anneau__alerte...).
   L'icone occupe la hauteur d'une ligne de texte : elle s'aligne sur la premiere ligne sans marge. */
.hx-bandeau {
  --hx-ton: var(--dim);
  display: flex; flex-wrap: wrap; align-items: flex-start; gap: var(--space-2) var(--space-3);
  padding: var(--space-3) var(--space-4);
  border: 1px solid color-mix(in srgb, var(--hx-ton) 30%, transparent); border-left: 3px solid var(--hx-ton);
  border-radius: var(--radius-sm); background: color-mix(in srgb, var(--hx-ton) 10%, transparent);
  color: var(--text); font-size: var(--fs-sm); line-height: 1.5;
}
.hx-bandeau > .ni { flex: none; width: 16px; height: 1.5em; margin: 0; opacity: 1; color: color-mix(in srgb, var(--hx-ton) 70%, var(--text)); }
.hx-bandeau__texte { flex: 1 1 12rem; min-width: 0; overflow-wrap: anywhere; }
.hx-bandeau__titre { display: block; font-weight: 600; }
.hx-bandeau__actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); margin-left: auto; }

/* ── Formes du lot L00 (09/10/2026) : la coque de l'application ──────────────────────────────────────────
   Ce que les aides communes ecrivaient en ligne. Le code n'ecrit plus que la GEOMETRIE (top, left, right,
   width, height) et le rang d'une note (--hx-rang) ; l'apparence est ici. */

/* Panneau d'un menu de la barre, porte dans <body> (la barre est un contexte d'empilement : backdrop-filter).
   Opaque : hors de la barre, --surface est transparent. */
.nav-dd-panel.is-porte { position: fixed; z-index: var(--z-portal); background: var(--bg-1); }

/* Bandeau fixe en tete de page (« Nouvelle version disponible », « Quelqu'un attend »). Le second se place
   sous le premier : son top est ecrit par le code. Fond opaque, sinon la page se lit a travers. */
.hx-bandeau--tete {
  position: fixed; top: 0; left: 0; right: 0; z-index: calc(var(--z-portal) + 1);
  justify-content: center; align-items: center;
  padding: var(--space-2) var(--space-4); border-width: 0 0 1px; border-left-width: 0; border-radius: 0;
  border-bottom-color: var(--hx-ton);
  background: color-mix(in srgb, var(--hx-ton) 16%, var(--bg-2));
  box-shadow: 0 2px 14px var(--overlay-bg);
}
.hx-bandeau--tete > .hx-bandeau__texte { flex: 0 1 auto; }

/* Bandeau PLEIN (12/10/2026, Jerome : « pourquoi le bandeau n'est plus orange ? »). Le lot L00 avait passe « Quelqu'un
   attend » au ton du socle (16 % de l'orange sur le fond : du brun en theme sombre) : un client qui attend doit sauter
   aux yeux. Degrade orange et texte blanc dans les deux themes, boutons voiles de blanc, comme avant le 10/10.
   Double classe : le repli sans color-mix() (plus bas) remet .hx-bandeau en transparent et passerait apres. */
.hx-bandeau.hx-bandeau--plein {
  background: linear-gradient(90deg, var(--color-attention-fonce), var(--color-attention));
  color: var(--on-attention); border-bottom-color: var(--color-attention-fonce);
}
.hx-bandeau--plein .hx-bouton { background: var(--on-attention-voile); border-color: var(--on-attention-filet); color: var(--on-attention); }
.hx-bandeau--plein .hx-bouton:hover:not(:disabled):not([aria-disabled="true"]) { background: var(--on-attention-voile-fort); color: var(--on-attention); }
.hx-bandeau--plein .hx-bouton--discret { background: transparent; border-color: transparent; color: var(--on-attention); }
.hx-bandeau--plein .hx-bouton--discret:hover:not(:disabled):not([aria-disabled="true"]) { background: var(--on-attention-voile); color: var(--on-attention); }
.hx-bandeau--plein .hx-bouton:focus-visible { outline-color: var(--on-attention); }
/* L'icone en tete d'un bandeau plein prend le texte de l'aplat (12/10/2026) : celle du socle melange le ton au texte du
   theme -- sur l'aplat du meme ton, l'icone de « Nouvelle version disponible » tombait a 1,4:1 (avant le lot L00 : un
   glyphe blanc). Double classe : le repli sans color-mix(), plus bas, repose la couleur du ton sur .hx-bandeau > .ni. */
.hx-bandeau.hx-bandeau--plein > .ni { color: var(--on-attention); }

/* Bandeau PLEIN au ton de l'accent (12/10/2026, Jerome : « le bandeau devrait etre bleu ! ») : « Nouvelle version disponible »
   etait un aplat de --accent, texte blanc, bouton « Recharger » blanc a texte d'accent, avant le lot L00. Meme modificateur que
   « Quelqu'un attend » ; seul le fond change, et le bouton primaire s'inverse (accent sur accent ne se verrait pas) ; selecteurs renforces : la regle du theme clair (:root[data-theme=light] .hx-bouton--primaire) gagnait. */
.hx-bandeau.hx-bandeau--plein.hx-bandeau--accent { background: var(--accent); border-bottom-color: var(--accent-2); }
.hx-bandeau.hx-bandeau--plein .hx-bouton.hx-bouton--primaire { background: var(--on-attention); border-color: var(--on-attention); color: var(--accent); }
.hx-bandeau.hx-bandeau--plein .hx-bouton.hx-bouton--primaire:hover:not(:disabled):not([aria-disabled="true"]) { background: var(--on-attention); color: var(--accent); filter: brightness(0.94); }
/* « Plus tard », sur l'aplat d'accent : sans voile au repos, comme avant le 10/10 (vérification du 12/10). Le voile blanc
   à 16 % éclaircissait le fond sous un texte blanc : 2,9:1 en sombre, 4,0:1 en clair -- 3,6:1 et 5,4:1 sans lui, comme le
   texte du bandeau. Le filet blanc reste ; le survol garde son voile. */
.hx-bandeau.hx-bandeau--plein.hx-bandeau--accent .hx-bouton:not(.hx-bouton--primaire):not(:hover) { background: transparent; }

/* Verrou du defilement de la page sous une visionneuse, une fenetre plein ecran : classe sur <body>. */
body.hx-sans-defilement { overflow: hidden; }

/* Ancre d'un element pose en absolu dans une carte (croix de fermeture ajoutee par le code). */
.hx-ancre { position: relative; }
.hx-fermer-coin { position: absolute; top: var(--space-2); right: var(--space-2); z-index: 2; }

/* Sprite des icones : present dans la page, jamais affiche (display:none casserait des degrades). */
.hx-sprite { position: absolute; width: 0; height: 0; overflow: hidden; }

/* Puce de filtre d'un ecran pas encore repris (.dev-chip, .tk-chip, .al-chip) : allumee ou eteinte par la
   classe, plus par trois styles en ligne. A retirer avec la reprise de ces ecrans (.hx-filtre). */
button.hx-puce-heritee { background: transparent; border-color: var(--border); color: var(--muted); }
button.hx-puce-heritee.is-allumee { background: color-mix(in srgb, var(--accent) 18%, transparent); border-color: var(--accent); color: var(--accent); }

/* Tableau a colonnes fixes (un <colgroup> donne les largeurs : une colonne coupe son texte au lieu de
   s'elargir). Sous 900 px, une largeur minimale : il defile dans son .hx-defile au lieu de s'ecraser. */
.hx-tableau--fixe { table-layout: fixed; }
@media (max-width: 900px) { .hx-tableau--fixe { min-width: 640px; } }

/* Bloc de code qui passe a la ligne (message, journal court) au lieu de defiler. */
.hx-bloc-code--retour { white-space: pre-wrap; overflow-wrap: anywhere; overflow-x: visible; }
/* Un bloc de code qui defile se prend au clavier (tabindex="0") : il montre son focus. */
.hx-bloc-code:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Champs couleur et fichier : exclus de l'anneau des champs texte, ils ont le leur. */
input.hx-saisie[type=color]:focus-visible, input.hx-saisie[type=file]:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Post-it du technicien (.fox-note) : une note flottante. Pastels FERMES, choisis par l'utilisateur : ils ne
   disent aucun etat, ce ne sont donc pas les jetons --color-* ; ils sont nommes ici une fois (jetons de
   pastel) et ne changent pas avec le theme (une note jaune reste jaune). Position, taille et rang au clic
   (--hx-rang) restent ecrits par le code. Les couleurs litterales ne sont admises au socle que dans ces
   declarations --pastel-* (le banc du socle le verifie). */
:root {
  --pastel-encre: #713f12; --pastel-texte: #1c1917;
  --pastel-jaune: #fef9c3;  --pastel-jaune-tete: #fde047;
  --pastel-vert: #dcfce7;   --pastel-vert-tete: #86efac;
  --pastel-rose: #fce7f3;   --pastel-rose-tete: #f9a8d4;
  --pastel-bleu: #dbeafe;   --pastel-bleu-tete: #93c5fd;
  --pastel-violet: #f3e8ff; --pastel-violet-tete: #d8b4fe;
  --pastel-gris: #f3f4f6;   --pastel-gris-tete: #d1d5db;
  --pastel-orange: #ffedd5; --pastel-orange-tete: #fdba74;
}
.fox-note {
  --fox-note-fond: var(--pastel-jaune); --fox-note-tete: var(--pastel-jaune-tete);
  position: fixed; z-index: var(--hx-rang, 9000);
  display: flex; flex-direction: column; min-width: 200px; min-height: 140px; overflow: hidden; resize: both;
  border-radius: var(--radius-sm); background: var(--fox-note-fond); color: var(--pastel-texte);
  box-shadow: 0 8px 32px var(--overlay-bg);
}
.fox-note--yellow { --fox-note-fond: var(--pastel-jaune);  --fox-note-tete: var(--pastel-jaune-tete); }
.fox-note--green  { --fox-note-fond: var(--pastel-vert);   --fox-note-tete: var(--pastel-vert-tete); }
.fox-note--pink   { --fox-note-fond: var(--pastel-rose);   --fox-note-tete: var(--pastel-rose-tete); }
.fox-note--blue   { --fox-note-fond: var(--pastel-bleu);   --fox-note-tete: var(--pastel-bleu-tete); }
.fox-note--purple { --fox-note-fond: var(--pastel-violet); --fox-note-tete: var(--pastel-violet-tete); }
.fox-note--gray   { --fox-note-fond: var(--pastel-gris);   --fox-note-tete: var(--pastel-gris-tete); }
.fox-note--orange { --fox-note-fond: var(--pastel-orange); --fox-note-tete: var(--pastel-orange-tete); }
.fox-note-head {
  display: flex; align-items: center; gap: var(--space-1); padding: var(--space-1) var(--space-2);
  background: var(--fox-note-tete); cursor: move; user-select: none;
}
.fox-note-head > button { padding: 0 var(--space-1); border: 0; background: none; color: var(--pastel-encre); line-height: 1; cursor: pointer; }
.fox-note-tools { display: flex; flex: 1; justify-content: center; gap: 4px; }
.fox-note-tools > button {
  width: 22px; height: 20px; padding: 0; border: 0; border-radius: 4px;
  background: color-mix(in srgb, var(--pastel-texte) 6%, transparent); color: var(--pastel-texte); font-size: var(--fs-xs); line-height: 1; cursor: pointer;
}
.fox-note-head > button:focus-visible, .fox-note-tools > button:focus-visible { outline: 2px solid var(--pastel-encre); outline-offset: 1px; }
.fox-note-body {
  flex: 1; padding: var(--space-2) var(--space-3); overflow: auto; outline: none;
  color: var(--pastel-texte); font-size: var(--fs-sm); line-height: 1.45;
}

/* Cadre isole : montre un element FIXE (bandeau de tete, panneau porte, post-it) a sa place dans le flux,
   pour une page de demonstration ou un apercu. */
.hx-cadre-isole { position: relative; isolation: isolate; overflow: hidden; }
.hx-cadre-isole > *, .hx-cadre-isole > .nav-dd-panel.is-porte { position: relative; top: auto; left: auto; right: auto; }

/* Theme clair : texte attenue assombri d'un cran (L17 : 4,47:1 sur le fond de page clair, sous les 4,5:1
   du petit texte ; #56657f : 5,5:1 sur --bg-0, 5,2:1 sur --bg-1). Le theme sombre ne bouge pas. */
:root[data-theme="light"] { --dim: #56657f; }
/* Liens du socle en theme clair : --accent (#14719E) tombe a 4,41:1 sur le fond degrade de la page ; --accent-2,
   le jeton des liens et des onglets, y tient 5,4:1 (verification du 09/10/2026). Le theme sombre ne bouge pas.
   :where() garde le poids de .hx-lien seul : une regle de lot plus loin (ex. .tk-d-retour) l'emporte encore. */
:where(:root[data-theme="light"]) .hx-lien { color: var(--accent-2); }
/* Même jeton pour le texte d'accent en thème clair : 4,41:1 sur le fond dégradé (lot L02, capacités LLDP d'un
   voisin dans la topologie, vérification du 10/10/2026). */
:where(:root[data-theme="light"]) .hx-texte-accent { color: var(--accent-2); }


/* ── Carte, encart ────────────────────────────────────────────────────────────────────────────────────────
   .hx-carte : un panneau (fond --bg-2, filet, 16 px). --serree 12 px, --large 24 px, --nue 0 (un tableau
   bord a bord). --cliquable : toute la carte est un lien. Liseré d'etat a gauche : --crit / --warn / --ok
   / --info / --accent. .hx-encart : un encadre neutre DANS une carte (fond en retrait). */
.hx-carte {
  min-width: 0; padding: var(--space-4);
  border: 1px solid var(--border); border-radius: var(--radius-lg);
  background: var(--bg-2); color: var(--text);
}
.hx-carte--serree { padding: var(--space-3); }
.hx-carte--large { padding: var(--space-6); }
.hx-carte--nue { padding: 0; overflow: hidden; }
.hx-carte--crit, .hx-carte--warn, .hx-carte--ok, .hx-carte--info, .hx-carte--accent, .hx-carte--neutre { border-left: 3px solid var(--hx-ton); }
.hx-carte--cliquable { color: inherit; text-decoration: none; cursor: pointer; transition: border-color .14s ease; }
/* Sur un <a> (en ligne par defaut) : un bloc, sauf si la carte est aussi une pile, une grappe ou une grille. */
.hx-carte--cliquable:not(.hx-pile):not(.hx-grappe):not(.hx-grille) { display: block; }
.hx-carte--cliquable:hover { border-color: var(--accent); }
.hx-carte--cliquable:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.hx-carte__pied {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: var(--space-2);
  margin-top: var(--space-4); padding-top: var(--space-3); border-top: 1px solid var(--border);
}
.hx-encart { min-width: 0; padding: var(--space-3) var(--space-4); border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--bg-1); }

/* ── Tableau ──────────────────────────────────────────────────────────────────────────────────────────────
   Toujours dans .hx-defile (alias .tbl-scroll) : un tableau trop large defile dans son cadre, la page
   jamais. Cellules 8 / 12 px (--dense : 4 / 8 px), en-tetes --fs-xs en capitales, filets --border,
   chiffres tabulaires. Alignements : .hx-num (nombre, a droite), .hx-centre, .hx-col-actions (boutons,
   a droite, sans retour ; --fixe : collee a droite pendant le defilement). Une ligne ne prend la main au
   survol que si elle s'ouvre : .hx-tableau--cliquable, ou tr.is-cliquable (la regle generale
   « tbody tr:hover » la donnait a TOUTE ligne). Tri : un <button class="hx-tri"> dans le <th>, avec
   aria-sort sur le <th> ; le tri automatique (tablesort.js) garde son indicateur, recolore ici. */
.hx-defile { max-width: 100%; }
/* Un cadre qui défile sans rien de focalisable dedans prend lui-même le focus au clavier (Chromium 130 et plus) :
   son anneau est celui de toute commande, 2 px d'accent, au lieu du contour de 1 px du navigateur. */
.hx-defile:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.hx-tableau { width: 100%; border-collapse: collapse; font-size: var(--fs-sm); font-variant-numeric: tabular-nums; }
.hx-tableau th, .hx-tableau td { padding: var(--space-2) var(--space-3); border-bottom: 1px solid var(--border); text-align: left; vertical-align: middle; }
.hx-tableau thead th {
  border-bottom-color: var(--border-strong); background: var(--bg-1); color: var(--dim);
  font-size: var(--fs-xs); font-weight: 600; letter-spacing: .04em; text-transform: uppercase; white-space: nowrap;
}
.hx-tableau tbody tr:hover { background: none; cursor: auto; }
.hx-tableau--cliquable tbody tr:hover, .hx-tableau tbody tr.is-cliquable:hover { background: color-mix(in srgb, var(--accent) 7%, transparent); cursor: pointer; }
.hx-tableau--dense th, .hx-tableau--dense td { padding: var(--space-1) var(--space-2); }
.hx-tableau .hx-num { text-align: right; white-space: nowrap; }
.hx-tableau .hx-centre { text-align: center; }
.hx-tableau .hx-col-actions { width: 1%; text-align: right; white-space: nowrap; }
.hx-tableau .hx-col-actions--fixe { position: sticky; right: 0; border-left: 1px solid var(--border-strong); background: var(--bg-2); }
.hx-tableau thead .hx-col-actions--fixe { background: var(--bg-1); }
.hx-tableau tr.is-attenue > td { color: var(--dim); }
.hx-tri {
  display: inline-flex; align-items: center; gap: var(--space-1); margin: 0; padding: 0;
  border: 0; background: none; color: inherit; cursor: pointer;
  font: inherit; letter-spacing: inherit; text-transform: inherit; white-space: nowrap;
}
.hx-tri:hover { color: var(--text); }
.hx-tri:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* L'indicateur garde sa place (rien ne saute au tri) ; i-chevron pointe a droite : -90° = croissant. */
.hx-tri .ni { width: 12px; height: 12px; margin: 0; opacity: 0; transform: rotate(-90deg); }
.hx-tri:hover .ni, .hx-tri:focus-visible .ni { opacity: .6; }
.hx-tableau th[aria-sort="ascending"] .hx-tri, .hx-tableau th[aria-sort="descending"] .hx-tri { color: var(--text); }
.hx-tableau th[aria-sort="ascending"] .hx-tri .ni, .hx-tableau th[aria-sort="descending"] .hx-tri .ni { opacity: 1; color: var(--accent-2); }
.hx-tableau th[aria-sort="descending"] .hx-tri .ni { transform: rotate(90deg); }
/* Colonne de nombres, alignee a droite : l'indicateur passe a gauche du libelle, qui reste au droit des chiffres. */
.hx-tableau th.hx-num .hx-tri { flex-direction: row-reverse; }
.hx-tableau th.fxsort-active { color: var(--text); }
.hx-tableau .fxsort-ind { color: var(--accent-2); opacity: 1; }

/* ── Formulaire ───────────────────────────────────────────────────────────────────────────────────────────
   .hx-champ : libelle, saisie, aide, erreur, l'un sous l'autre (4 px). La regle generale des champs reste
   la base (fond, filet, largeur 100 %) ; .hx-saisie les met a la hauteur des boutons (36 px, --petit
   28 px) et --auto rend sa largeur naturelle a un select de barre d'outils. Erreur : aria-invalid="true"
   sur la saisie, message dans .hx-erreur (relie par aria-describedby).
   ⚠ La regle generale des <input> pese (0,5,1) : une classe seule ne la bat pas, d'ou les :not() repris. */
.hx-champ { display: flex; flex-direction: column; gap: var(--space-1); min-width: 0; }
.hx-libelle {
  display: block; margin: 0; color: var(--text);
  font-size: var(--fs-sm); font-weight: 600; letter-spacing: normal; text-transform: none;
}
.hx-aide { margin: 0; color: var(--dim); font-size: var(--fs-xs); line-height: 1.5; }
.hx-erreur {
  display: flex; align-items: center; gap: var(--space-1); margin: 0;
  color: color-mix(in srgb, var(--color-crit) 70%, var(--text)); font-size: var(--fs-xs); font-weight: 600;
}
.hx-erreur .ni { width: 14px; height: 14px; margin: 0; opacity: 1; }
input.hx-saisie:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]):not([type=file]),
select.hx-saisie, textarea.hx-saisie {
  min-height: 36px; padding: var(--space-2) var(--space-3); border-radius: var(--radius-sm); font-size: var(--fs-sm);
}
input.hx-saisie--petit:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]):not([type=file]),
select.hx-saisie--petit, textarea.hx-saisie--petit { min-height: 28px; padding: var(--space-1) var(--space-2); }
input.hx-saisie--auto:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]):not([type=file]),
select.hx-saisie--auto { width: auto; max-width: 100%; }
input.hx-saisie[aria-invalid="true"]:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]):not([type=file]),
select.hx-saisie[aria-invalid="true"], textarea.hx-saisie[aria-invalid="true"] { border-color: var(--color-crit); }
/* Focus : la regle generale (input:focus { outline: none }) ne laisse qu'un halo a 15 % ; ici un anneau franc. */
input.hx-saisie:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]):not([type=file]):focus-visible,
select.hx-saisie:focus-visible, textarea.hx-saisie:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; border-color: var(--accent); }
.hx-saisie:disabled { opacity: .6; cursor: not-allowed; }
/* Case a cocher ou bouton radio et son libelle, sur une ligne. La regle generale des label qui
   contiennent une case (label:has(> input[type=checkbox]), plus haut) en fixe deja la taille. */
.hx-case { display: inline-flex; align-items: center; gap: var(--space-2); cursor: pointer; }
label.hx-case:has(> input) { margin-top: 0; }
.hx-case input { margin: 0; flex: none; accent-color: var(--accent); }
/* Groupe de champs (fieldset). */
.hx-groupe { min-width: 0; margin: 0; padding: var(--space-2) var(--space-4) var(--space-4); border: 1px solid var(--border); border-radius: var(--radius-sm); }
.hx-groupe > legend { padding: 0 var(--space-1); color: var(--text); font-size: var(--fs-sm); font-weight: 600; }

/* ── Liste de definitions (libelle → valeur) ─────────────────────────────────────────────────────────────
   Les libelles en une colonne (celle du plus long), les valeurs a cote ; l'un sous l'autre sur un telephone.
   Remplace les grilles dt / dd recopiees d'ecran en ecran (np-modele, syno-sante-liste, syno-exc-dl,
   tk-pg-liste, s1ag-faits…). <div> autour d'un couple dt / dd permis. */
.hx-def { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: var(--space-2) var(--space-4); margin: 0; font-size: var(--fs-sm); }
.hx-def > div { display: contents; }
.hx-def dt { color: var(--dim); }
.hx-def dd { min-width: 0; margin: 0; overflow-wrap: anywhere; font-variant-numeric: tabular-nums; }

/* ── Etats : vide, chargement, erreur ─────────────────────────────────────────────────────────────────────
   .hx-vide (alias .empty) : une ligne « rien a afficher » dans une carte. .hx-etat : l'etat d'une zone
   entiere — icone, titre, aide, action ; --chargement (avec .hx-spinner), --erreur (icone en rouge).
   Rendu par emptyState() quand l'aide sera reprise (phase 2). */
.hx-etat {
  display: flex; flex-direction: column; align-items: center; gap: var(--space-2);
  padding: var(--space-6) var(--space-4); color: var(--dim); font-size: var(--fs-sm); line-height: 1.5; text-align: center;
}
.hx-etat__icone { width: 32px; height: 32px; margin: 0; opacity: 1; color: var(--dim); }
.hx-etat--erreur .hx-etat__icone { color: color-mix(in srgb, var(--hx-ton) 70%, var(--text)); }
.hx-etat--chargement { cursor: progress; }
.hx-etat__titre { margin: 0; color: var(--text); font-size: var(--fs-base); font-weight: 600; }
.hx-etat__aide { max-width: 48ch; margin: 0; }
.hx-etat__actions { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--space-2); margin-top: var(--space-2); }
.hx-spinner--grand { width: 24px; height: 24px; border-width: 3px; }

/* ── Onglets ──────────────────────────────────────────────────────────────────────────────────────────────
   Le gabarit des onglets de Gestion NAS (valide le 05/10), en commun. Onglet actif : aria-selected="true"
   (role="tab") ou .is-actif (lien). Un compteur d'onglet est un .nav-dd-rollup, comme dans les menus. */
/* Le filet de la barre est une ombre interieure : le trait de l'onglet actif se peint par-dessus, sans
   marge negative de 1 px (hors echelle). */
.hx-onglets { display: flex; flex-wrap: wrap; gap: var(--space-1); margin: 0 0 var(--space-4); box-shadow: inset 0 -1px 0 var(--border); }
.hx-onglet {
  display: inline-flex; align-items: center; gap: var(--space-2); margin: 0; padding: var(--space-2) var(--space-3);
  border: 0; border-bottom: 2px solid transparent; background: none; color: var(--dim);
  font: inherit; font-size: var(--fs-sm); line-height: 1.25; text-decoration: none; white-space: nowrap; cursor: pointer;
}
.hx-onglet .ni { width: 14px; height: 14px; margin: 0; }
.hx-onglet:hover { color: var(--text); }
.hx-onglet:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.hx-onglet[aria-selected="true"], .hx-onglet.is-actif { border-bottom-color: var(--accent); color: var(--text); font-weight: 600; }

/* ── Fenetre (voile + boite) ──────────────────────────────────────────────────────────────────────────────
   Remplace les 86 voiles ecrits a la main (15 fonds, 13 z-index differents) : l'echelle --z-modal, le
   voile --overlay-bg des deux themes. Plein ecran sous 600 px (fin du bloc). La fermeture (Echap, croix)
   reste a showModal() : ces classes en sont l'habillage, pas le comportement. */
.hx-voile {
  position: fixed; inset: 0; z-index: var(--z-modal);
  display: flex; align-items: center; justify-content: center; padding: var(--space-6);
  background: var(--overlay-bg); backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px);
}
.hx-fenetre {
  position: relative; width: 100%; max-width: 36rem; max-height: 85vh; overflow: auto;
  padding: var(--space-6); border: 1px solid var(--border-strong); border-radius: var(--radius-lg);
  background: var(--bg-2); color: var(--text); box-shadow: 0 24px 48px var(--overlay-bg);
}
.hx-fenetre--large { max-width: 56rem; }
.hx-fenetre__entete { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); margin-bottom: var(--space-4); }
.hx-fenetre__titre { margin: 0; font-size: var(--fs-lg); font-weight: 700; }
.hx-fenetre__pied { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: var(--space-2); margin-top: var(--space-6); }

/* ── Jauge ────────────────────────────────────────────────────────────────────────────────────────────────
   Sur <progress value max> : la valeur est un attribut, plus un style="width:…%" ecrit dans le code. */
.hx-jauge {
  --hx-ton: var(--accent);
  display: block; width: 100%; height: 8px; overflow: hidden;
  border: 0; border-radius: var(--radius-sm); background: var(--border-strong); color: var(--hx-ton);
  -webkit-appearance: none; appearance: none;
}
.hx-jauge::-webkit-progress-bar { border-radius: var(--radius-sm); background: var(--border-strong); }
.hx-jauge::-webkit-progress-value { border-radius: var(--radius-sm); background: var(--hx-ton); }
.hx-jauge::-moz-progress-bar { border-radius: var(--radius-sm); background: var(--hx-ton); }

/* ── Numero d'etape ───────────────────────────────────────────────────────────────────────────────────── */
.hx-numero {
  display: inline-flex; align-items: center; justify-content: center; flex: none;
  width: 24px; height: 24px; border-radius: 50%;
  background: color-mix(in srgb, var(--accent) 18%, transparent); color: var(--text);
  font-size: var(--fs-xs); font-weight: 700; font-variant-numeric: tabular-nums;
}

/* ── Icones du sprite : tailles et rotations ──────────────────────────────────────────────────────────────
   .ni fait 16 px. Dans un composant du socle, sa marge est remise a zero : c'est le gap qui espace. */
.hx-grappe > .ni, .hx-entete .ni { margin: 0; }
.ni--12 { width: 12px; height: 12px; }
.ni--14 { width: 14px; height: 14px; }
.ni--20 { width: 20px; height: 20px; }
.ni--24 { width: 24px; height: 24px; }
.ni--32 { width: 32px; height: 32px; }
.ni--48 { width: 48px; height: 48px; }
.ni--tourne-90 { transform: rotate(90deg); }
.ni--tourne-180 { transform: rotate(180deg); }
.ni--tourne-270 { transform: rotate(270deg); }

/* ── Tons (APRES les composants : meme poids, c'est l'ordre qui tranche) ─────────────────────────────── */
.hx-badge--crit, .hx-point--crit, .hx-bandeau--crit, .hx-carte--crit, .hx-filtre--crit, .hx-jauge--crit, .hx-texte-crit, .hx-etat--erreur { --hx-ton: var(--color-crit); }
.hx-badge--warn, .hx-point--warn, .hx-bandeau--warn, .hx-carte--warn, .hx-filtre--warn, .hx-jauge--warn, .hx-texte-warn { --hx-ton: var(--color-warn); }
.hx-badge--ok, .hx-point--ok, .hx-bandeau--ok, .hx-carte--ok, .hx-filtre--ok, .hx-jauge--ok, .hx-texte-ok { --hx-ton: var(--color-ok); }
.hx-badge--info, .hx-point--info, .hx-bandeau--info, .hx-carte--info, .hx-filtre--info, .hx-jauge--info, .hx-texte-info { --hx-ton: var(--color-info); }
.hx-badge--neutre, .hx-point--neutre, .hx-bandeau--neutre, .hx-carte--neutre { --hx-ton: var(--dim); }
.hx-badge--accent, .hx-point--accent, .hx-bandeau--accent, .hx-carte--accent { --hx-ton: var(--accent); }

/* ── Espacements ponctuels (APRES les composants) ─────────────────────────────────────────────────────────
   Le gap d'une pile, d'une grappe ou d'une grille, et les marges isolees, pris dans l'echelle seulement :
   .hx-gap-N et .hx-mt-N / .hx-mb-N, N = 0, 4, 8, 12, 16, 24, 32, 48 (px). */
.hx-gap-0 { gap: 0; }
.hx-gap-4 { gap: var(--space-1); }
.hx-gap-8 { gap: var(--space-2); }
.hx-gap-12 { gap: var(--space-3); }
.hx-gap-16 { gap: var(--space-4); }
.hx-gap-24 { gap: var(--space-6); }
.hx-gap-32 { gap: 32px; }
.hx-gap-48 { gap: 48px; }
.hx-m-0 { margin: 0; }
.hx-mt-0 { margin-top: 0; }
.hx-mt-4 { margin-top: var(--space-1); }
.hx-mt-8 { margin-top: var(--space-2); }
.hx-mt-12 { margin-top: var(--space-3); }
.hx-mt-16 { margin-top: var(--space-4); }
.hx-mt-24 { margin-top: var(--space-6); }
.hx-mt-32 { margin-top: 32px; }
.hx-mt-48 { margin-top: 48px; }
.hx-mb-0 { margin-bottom: 0; }
.hx-mb-4 { margin-bottom: var(--space-1); }
.hx-mb-8 { margin-bottom: var(--space-2); }
.hx-mb-12 { margin-bottom: var(--space-3); }
.hx-mb-16 { margin-bottom: var(--space-4); }
.hx-mb-24 { margin-bottom: var(--space-6); }
.hx-mb-32 { margin-bottom: 32px; }
.hx-mb-48 { margin-bottom: 48px; }

/* Page autonome servie avec cette feuille mais sans la barre du haut (demonstration, aide) : la marge
   reservee a la barre fixe n'a pas lieu d'etre. */
.hx-page-autonome { padding-top: 0; }

/* ── Couche mobile du socle (parite web / PWA) ────────────────────────────────────────────────────────────
   Ce que la couche mobile faisait aux styles en dur PAR LEUR TEXTE, ces classes le font par elles-memes :
   grappe (display:flex) : enfants retrecissables ; .hx-pousse (margin-left:auto) : plus de poussee ;
   grilles a colonnes fixes : une colonne ; voile (position:fixed + inset:0) : fenetre plein ecran ;
   carte : 12 px. Les tableaux suivent la regle commune des tableaux (cellules en nowrap, defilement). */
@media (max-width: 800px) {
  .hx-grille--2col, .hx-grille--3col, .hx-grille--4col { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 900px) {
  /* iOS agrandit la page au focus d'un champ de moins de 16 px (la regle generale le sait deja). */
  input.hx-saisie:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]):not([type=file]),
  select.hx-saisie, textarea.hx-saisie { font-size: var(--fs-md); }
}
@media (max-width: 600px) {
  .hx-grappe > * { min-width: 0; }
  .hx-pousse { margin-left: 0; }
  .hx-carte { padding: var(--space-3); }
  .hx-carte--large { padding: var(--space-4); }
  .hx-carte--nue { padding: 0; }
  .hx-voile { align-items: stretch; padding: 0; }
  .hx-fenetre, .hx-fenetre--large { max-width: 100%; max-height: 100vh; border-radius: 0; }
  .hx-def { grid-template-columns: minmax(0, 1fr); gap: var(--space-1); }
  .hx-def dd { margin-bottom: var(--space-2); }
}

/* Navigateur sans color-mix() (anterieur a 2023) : les tons gardent leur couleur pure. */
@supports not (color: color-mix(in srgb, currentColor 50%, transparent)) {
  .hx-badge, .hx-texte-crit, .hx-texte-warn, .hx-texte-ok, .hx-texte-info, .hx-bandeau > .ni,
  .hx-filtre__n, .hx-etat--erreur .hx-etat__icone { color: var(--hx-ton); }
  .hx-bouton.hx-texte-crit, .hx-bouton.hx-texte-warn, .hx-bouton.hx-texte-ok, .hx-bouton.hx-texte-info { color: var(--hx-ton); }
  .hx-badge { background: transparent; border-color: currentColor; }
  .hx-bandeau { background: transparent; border-color: var(--border-strong); border-left-color: var(--hx-ton); }
  .hx-bouton--primaire { background: var(--accent); }
  .hx-bouton--danger { background: transparent; border-color: var(--color-crit); color: var(--color-crit); }
  .hx-bouton:hover:not(:disabled):not([aria-disabled="true"]), .hx-filtre[aria-pressed="true"], .hx-filtre.is-actif { background: var(--bg-1); }
  .hx-tableau--cliquable tbody tr:hover, .hx-tableau tbody tr.is-cliquable:hover { background: var(--bg-1); }
  .hx-numero { background: var(--bg-1); }
}
/* Anciennes classes d'etat posees sur une surface du socle (fenetre, carte : --bg-2). Leur couleur d'etat brute,
   ecrite sur son propre fond teinte, y tombe sous 4,5:1 en sombre (rouge 4,39 ; bleu 4,34) : meme melange que
   le texte d'un badge du socle. Le balisage de badge() et la classe .fc-erreur ne changent pas. */
:is(.hx-fenetre, .hx-carte) .badge--crit, :is(.hx-fenetre, .hx-carte) .fc-erreur { color: color-mix(in srgb, var(--state-crit) 70%, var(--text)); }
:is(.hx-fenetre, .hx-carte) .badge--info { color: color-mix(in srgb, var(--state-info) 70%, var(--text)); }
/* ═══ SOCLE VISUEL HeXaControl — fin ════════════════════════════════════════════════════════════════════ */

/* ═══ RÈGLES DES LOTS D'ÉCRAN — phase 2 de l'harmonisation visuelle (09/10/2026) ═══════════════════════════
   Une ancre par lot (~/foxrmm-work/harmonisation/plan/lots.json). Un lot n'écrit QU'entre son « début » et sa « fin »,
   en jetons seulement : deux lots ne touchent jamais les mêmes lignes et leurs branches fusionnent sans conflit.
   Une forme utile à plusieurs écrans n'a pas sa place ici : elle entre au socle (lot L19-feuille-pwa). */

/* [L01-tableau-de-bord] début */
/* Tableau de bord : formes propres au lot (harmonisation 09/10/2026). Le reste est au socle hx-*. */
/* Ton d'un widget, choisi par le code (accent + son triplet, que .widget lit pour ses halos). */
.dash-ton--info   { --w-accent: var(--state-info);    --w-accent-rgb: 59,130,246; }
.dash-ton--ok     { --w-accent: var(--state-ok);      --w-accent-rgb: 16,185,129; }
.dash-ton--warn   { --w-accent: var(--state-warn);    --w-accent-rgb: 245,158,11; }
.dash-ton--crit   { --w-accent: var(--state-crit);    --w-accent-rgb: 239,68,68; }
.dash-ton--neutre { --w-accent: var(--state-neutral); --w-accent-rgb: 124,138,163; }
.dash-lien-bloc { display: block; color: inherit; text-decoration: none; }
.dash-titre-suite { color: var(--muted); font-size: 0.6em; font-weight: 400; }
.dash-carte-tete { margin-bottom: var(--space-3); }
.dash-col-point { width: 1.5rem; }
.dash-etat-ok { --hx-ton: var(--color-ok); }
.dash-etat-ok .hx-etat__icone { color: var(--color-ok); }
.dash-sante { margin-bottom: var(--space-3); }
.dash-separateur { align-self: stretch; width: 1px; background: var(--border); margin: 0 var(--space-1); }
/* Mode kiosque : les onglets du socle se masquent comme les anciens .tabs. */
body.kiosk-mode #ov-tabs { display: none; }
/* Sélecteurs et bascules : l'état actif (accent). */
.dash-select-actif { border-color: var(--accent); color: var(--accent); }
.dash-bascule[aria-pressed="true"], .dash-bascule.is-actif, #btn-edit-dash[aria-pressed="true"] {
  border-color: var(--accent); background: rgba(var(--accent-rgb), 0.15); color: var(--accent-2);
}
.dash-choix {
  display: flex; align-items: center; gap: var(--space-2); width: 100%;
  padding: var(--space-1) var(--space-2); background: transparent; border: 1px solid transparent;
  border-radius: var(--radius-sm); color: var(--text); cursor: pointer; text-align: left; font: inherit;
}
.dash-choix:hover { background: var(--surface-2); }
.dash-choix[aria-pressed="true"] { border-color: var(--accent); background: rgba(var(--accent-rgb), 0.12); }
.dash-mini { padding: 0 var(--space-1); min-height: 0; }
.dash-ajout { width: 100%; border-style: dashed; color: var(--accent); }
.dash-champ { display: block; }
.dash-champ .hx-saisie { width: 100%; margin-top: var(--space-1); }
.dash-saisie-nombre { width: 5.5rem; }
.dash-regle { flex-wrap: wrap; align-items: center; }
/* Pastille d'une couleur de palette : la couleur est une donnée (--hx-couleur). */
.dash-nuance {
  width: 28px; height: 28px; padding: 0; border-radius: 50%; cursor: pointer;
  background: var(--hx-couleur, var(--accent)); border: 2px solid transparent; box-shadow: 0 0 0 1px var(--border);
}
.dash-nuance--petite { width: 22px; height: 22px; }
.dash-nuance[aria-pressed="true"] { border-color: var(--text); }
/* Cartes KPI personnalisées. */
.dash-kpi-dessus { position: relative; z-index: 1; }
.dash-kpi-pct { color: var(--stat-color, var(--accent)); font-weight: 700; }
.dash-kpi-point { width: 0.7em; height: 0.7em; }
.dash-kpi-erreur { color: var(--color-crit); }
.dash-jauge-kpi { --hx-ton: var(--stat-color, var(--accent)); width: 100%; }
.dash-couleur-regle .widget-metric { color: var(--dash-couleur); text-shadow: 0 0 24px var(--dash-couleur); }
.spark { margin-top: var(--space-2); position: relative; z-index: 1; }
#dash-widgets-area .spark:not(.dash-spark--courbe) { height: 28px; }
.dash-pied-liste {
  flex: 0 0 auto; padding: var(--space-2) var(--space-3); border-top: 1px solid var(--border);
  font-size: 0.78rem; color: var(--muted); background: var(--surface);
}
/* Le conteneur historique des KPI reste vide : toujours masqué. */
#dash-custom-kpis { display: none; }
/* Mode édition. */
.dash-en-edition { position: relative; outline: 2px dashed rgba(var(--accent-rgb), 0.4); outline-offset: 2px; }
.dash-glisse { opacity: 0.4; }
.dash-en-edition > .widget-edit-toolbar {
  position: absolute; top: -12px; left: 8px; right: 8px; z-index: 50; width: max-content; max-width: calc(100% - 16px);
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-1);
  border: 1px solid var(--accent); border-radius: var(--radius-sm); padding: 3px 5px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3); font-size: 0.75rem;
}
.dash-poignee { display: inline-flex; color: var(--muted); cursor: grab; }
.dash-poignee .ni + .ni { margin-left: -0.6em; }
/* Panneaux flottants (type de graphique, règles de couleur) : la position est écrite par le code. */
.dash-popover {
  position: fixed; z-index: 10001; width: 260px; padding: var(--space-2);
  background: var(--bg-1); border: 1px solid var(--border-strong); border-radius: var(--radius);
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.5);
}
.dash-popover--large { width: min(440px, calc(100vw - 16px)); max-height: 420px; overflow-y: auto; padding: var(--space-3) var(--space-4); }
/* Panneau « Ajouter / retirer un widget ». */
.dash-panneau {
  position: fixed; bottom: 96px; right: 24px; z-index: 9001; width: 92%; max-width: 380px; max-height: 70vh;
  overflow-y: auto; padding: var(--space-3); background: var(--bg-1); border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg); box-shadow: 0 16px 50px rgba(0, 0, 0, 0.6); backdrop-filter: blur(8px);
}
#aw-popover .aw-section { margin-bottom: var(--space-3); }
#aw-popover .aw-section-title { font-size: 0.72rem; color: var(--muted); text-transform: uppercase; letter-spacing: 1.2px; margin-bottom: var(--space-1); }
#aw-popover .aw-item {
  display: flex; align-items: center; gap: var(--space-2); width: 100%; padding: var(--space-2); margin-bottom: 4px;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); color: var(--text);
  cursor: pointer; font: inherit; font-size: 0.85rem; text-align: left; transition: background .15s ease, border-color .15s ease;
}
#aw-popover .aw-item:hover { background: var(--surface-2); border-color: rgba(var(--accent-rgb), 0.35); }
#aw-popover .aw-item.on { background: rgba(var(--accent-rgb), 0.08); border-color: var(--accent); }
#aw-popover .aw-item.on .aw-icon { color: var(--accent); }
#aw-popover .aw-item.off .aw-icon { color: var(--muted); }
#aw-popover .aw-icon { width: 18px; text-align: center; align-self: flex-start; margin-top: 1px; }
#aw-popover .aw-lbl { flex: 1; display: flex; flex-direction: column; gap: 2px; }
#aw-popover .aw-desc { color: var(--muted); font-size: 0.72rem; line-height: 1.25; }
/* Notification flottante (annuler, conflit d'enregistrement). */
.dash-toast {
  position: fixed; bottom: 24px; left: 50%; transform: translateX(-50%); z-index: 100050;
  display: flex; align-items: center; gap: var(--space-3); max-width: min(92vw, 520px);
  padding: var(--space-3) var(--space-4); background: var(--bg-1); border: 1px solid var(--border-strong);
  border-radius: var(--radius); box-shadow: 0 14px 40px rgba(0, 0, 0, 0.55); font-size: 0.86rem; color: var(--text);
}
.dash-toast--warn { border-color: var(--state-warn); z-index: 100060; }
/* Widgets injectés d'eux-mêmes (EDR, flux, santé, services, WAN) : une forme commune. */
.dash-auto { padding: var(--space-3); min-height: 160px; }
.dash-auto--bas { min-height: 140px; }
.dash-auto__titre { color: var(--muted); font-size: 0.75rem; text-transform: uppercase; letter-spacing: 1px; }
.dash-auto__corps { margin-top: var(--space-2); }
.dash-degrade { --hx-ton: color-mix(in srgb, var(--color-warn) 45%, var(--color-crit)); }
.dash-ton-texte { color: var(--hx-ton, var(--text)); }
.dash-gros-chiffre { font-size: 1.9rem; font-weight: 800; letter-spacing: -1px; font-variant-numeric: tabular-nums; }
.dash-gros-chiffre--moyen { font-size: 1.4rem; font-weight: 700; letter-spacing: 0; color: var(--accent); }
.dash-tete-lien { display: flex; align-items: baseline; gap: var(--space-2); margin-bottom: var(--space-1); padding: 4px; border-radius: var(--radius-sm); color: inherit; text-decoration: none; }
.dash-survol { display: block; padding: 6px 4px; border-radius: var(--radius); color: inherit; text-decoration: none; }
.dash-survol:hover, .dash-tete-lien:hover { background: rgba(var(--accent-rgb), 0.08); }
.dash-defile { max-height: 220px; overflow-y: auto; }
.dash-defile--court { max-height: 170px; margin-top: var(--space-1); }
.dash-ligne {
  display: flex; align-items: center; gap: var(--space-2); padding: 4px 0; border-bottom: 1px solid var(--border);
  font-size: 0.82rem; color: inherit; text-decoration: none;
}
.dash-ligne--haut { align-items: flex-start; }
.dash-ligne--haut > .hx-point { margin-top: 5px; }
.dash-ligne__texte { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dash-ligne__fin { flex: none; color: var(--muted); }
.dash-ligne__valeur { flex: none; font-weight: 700; font-variant-numeric: tabular-nums; }
.dash-score { min-width: 30px; font-weight: 700; color: var(--hx-couleur, var(--text)); }
/* Barre de répartition : la part de chaque bande est une donnée (--hx-part). */
.dash-repartition { display: flex; height: 14px; border-radius: 7px; overflow: hidden; margin: var(--space-2) 0; }
.dash-repartition > span { width: var(--hx-part, 0%); background: var(--hx-ton); }
.dash-legende { display: flex; flex-wrap: wrap; gap: var(--space-3); font-size: 0.76rem; color: var(--muted); }
.dash-legende .hx-point { margin-right: 4px; }
.dash-resume { cursor: pointer; color: var(--muted); font-size: 0.8rem; }
.dash-ligne--base { align-items: baseline; }
.dash-ligne__lien { flex: 1; min-width: 0; color: inherit; text-decoration: none; }
.dash-gravite { min-width: 64px; font-weight: 600; }
.dash-plus { padding: 4px 0; color: var(--muted); font-size: 0.78rem; }
.dash-mini-liste { margin-top: var(--space-1); font-size: 0.78rem; }
.dash-mini-liste > div { padding: 2px 0; }
.dash-tete-lien--large { gap: var(--space-3); padding: 6px 4px; }
.dash-tete-lien--centre { align-items: center; padding: var(--space-2) 6px; margin-bottom: 0; }
.dash-gros-chiffre--fort { font-size: 1.7rem; font-weight: 700; letter-spacing: 0; }
.dash-note-fin { color: var(--muted); font-size: 0.78rem; }
.dash-note-fin--apres { margin-left: var(--space-2); }
.dash-note-fin--seule { margin-left: auto; }
.dash-defile--moyen { max-height: 180px; margin-top: var(--space-2); }
.dash-defile--wan { max-height: 200px; margin-top: 4px; }
.dash-ton--accent { --w-accent: var(--accent); --w-accent-rgb: var(--accent-rgb); }
.dash-widget-erreur { color: var(--state-crit); }
.dash-dessus { position: relative; z-index: 1; }
.dash-mini-tableau { width: 100%; font-size: 0.85rem; position: relative; z-index: 1; }
.dash-mini-tableau th { text-align: left; padding: 4px 0; color: var(--muted); font-weight: 500; }
.dash-mini-tableau td { padding: 4px 0; }
.dash-mini-tableau .dash-num { text-align: right; }
.dash-puces { margin: 0; padding-left: 18px; font-size: 0.85rem; line-height: 1.8; position: relative; z-index: 1; }
/* Satisfaction client (fenêtre des réponses). */
.dash-csat-resume { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-4); margin-bottom: var(--space-4); }
.dash-csat-moyenne { text-align: center; }
.dash-csat-moyenne__chiffre { font-size: 2rem; font-weight: 700; color: var(--state-ok); }
.dash-csat-notes { flex: 1; display: flex; gap: var(--space-1); }
.dash-csat-note { flex: 1; text-align: center; padding: var(--space-2) 0; background: var(--bg-1); border-radius: var(--radius-sm); color: var(--hx-ton, var(--text)); }
.dash-csat-reponse { border: 1px solid var(--border); border-left: 4px solid var(--hx-ton, var(--border)); border-radius: var(--radius); padding: var(--space-2) var(--space-3); }
.dash-csat-reponse__note { color: var(--hx-ton, var(--text)); }
.dash-csat-commentaire { margin-top: var(--space-1); padding: var(--space-2) var(--space-3); background: var(--surface-2); border-radius: var(--radius-sm); font-size: 0.88rem; white-space: pre-wrap; }
/* Carte de chaleur : la teinte d'une case est une donnée (--hx-couleur). */
.dash-chaleur-svg { display: block; }
.dash-chaleur-defile { overflow-x: auto; padding-bottom: 4px; }
.dash-chaleur-case { display: inline-block; width: 12px; height: 12px; border-radius: 2px; background: var(--hx-couleur); }
.dash-chaleur-filtre[aria-pressed="true"] { background: color-mix(in srgb, var(--hx-couleur, var(--accent)) 14%, transparent); color: var(--hx-couleur, var(--accent)); border-color: var(--hx-couleur, var(--accent)); }
/* Santé de la flotte agent, Gestion NAS. */
.dash-collecteurs { align-items: center; }
.dash-collecteurs__titre { margin-right: var(--space-1); color: var(--muted); font-size: 0.7rem; text-transform: uppercase; }
.dash-chip-lien { cursor: pointer; text-decoration: none; }
.dash-chip-compte { font-weight: 400; }
.dash-tray-etat { width: 100%; margin-top: 4px; color: var(--muted); font-size: 0.68rem; }
.dash-col-tete { text-align: left; padding: 0 var(--space-2) var(--space-1) 0; color: var(--muted); font-size: 0.62rem; font-weight: 600; text-transform: uppercase; letter-spacing: 1.2px; }
.dash-cellule-haut { vertical-align: top; }
.dash-os { display: inline-flex; align-items: center; gap: var(--space-1); font-size: 0.8rem; font-weight: 600; }
.dash-section-tiret { margin-top: var(--space-3); padding-top: var(--space-2); border-top: 1px dashed var(--border); }
.dash-section-tiret--serre { margin-top: var(--space-2); }
.dash-droite { text-align: right; }
/* 10/10/2026 (vérification de l'intégration, banc visuel du lot, référence sur main 14ac33eb) :
   - halo du ton, thème sombre : au pire arrêt du dégradé (coin haut-droit à 22 %), un widget orange ou rouge
     faisait tomber sous 4,5:1 ses textes atténués, ses liens et ses chiffres d'état. Les widgets à ton du tableau
     de bord le ramènent à 8 % (4 % au coin opposé) ; liseré, bande du haut et orbe gardent la couleur du ton. Le
     thème clair a sa propre règle (!important, plus haut) et ne change pas ;
   - texte atténué d'un widget à ton : --muted, comme sur main (la reprise l'avait passé en --dim, plus sombre en
     sombre et plus clair en clair : contrastes neufs sous le seuil dans les deux thèmes) ; lien : --accent-2 ;
   - raisons du bandeau « Santé du parc » : le texte du filtre en --text (--dim tombait à 4,1:1 sur le bandeau
     critique en clair) ; le nombre garde son ton -- 12/10/2026 : une pastille du ton, plus bas ;
   - bascules pressées (taille S / M / L, « Terminer ») : texte en --accent-2 (3,9:1 en clair, 4,4:1 en sombre) ;
   - tableau des versions de la flotte : les marges de main (rien à gauche), sinon les pastilles sortaient de leur
     cellule à 360 et 600 px et le tableau débordait de son widget à 1 280 px. */
.widget[class*="dash-ton--"] {
  background:
    radial-gradient(ellipse at 100% 0%, rgba(var(--w-accent-rgb), 0.08) 0%, transparent 55%),
    radial-gradient(ellipse at 0% 100%, rgba(var(--w-accent-rgb), 0.04) 0%, transparent 50%),
    linear-gradient(180deg, rgba(20, 25, 50, 0.85), rgba(15, 18, 35, 0.70));
}
.widget[class*="dash-ton--"] .hx-attenue { color: var(--muted); }
:root:not([data-theme="light"]) .widget[class*="dash-ton--"] .hx-lien { color: var(--accent-2); }
/* 12/10/2026 (Jérôme : « on a perdu toutes les couleurs ») : le bandeau « Santé du parc » reprend la couleur de son
   état, comme avant le lot L01. L'icône en grand (24 px, la place du rond de couleur d'avant) et en couleur pure : un
   élément graphique, 3:1 suffit. Chaque raison, une pastille du ton : le texte mêlé à 70 % au texte du thème (la règle
   du socle pour un texte d'état) et le filet du ton, sur le voile du bandeau. Pas de fond à elle : un voile de plus
   faisait tomber le texte rouge à 4,4:1 en sombre (banc L01, pire arrêt du fond) ; sans lui, 4,8:1. Le --text du
   10/10 corrigeait un --dim trop pâle ; il éteignait aussi les raisons, dont seul le nombre restait coloré. */
.dash-sante > .ni { width: 24px; height: 24px; color: var(--hx-ton); }
.dash-sante .hx-filtre { color: color-mix(in srgb, var(--hx-ton) 70%, var(--text)); border-color: color-mix(in srgb, var(--hx-ton) 40%, transparent); }
.dash-sante .hx-filtre:hover { border-color: var(--hx-ton); background: color-mix(in srgb, var(--hx-ton) 10%, transparent); }
.dash-flotte th, .dash-flotte td { padding: var(--space-1) var(--space-2) var(--space-1) 0; }
.dash-flotte td:last-child { padding-right: 0; }
/* Sous 600 px, la couche mobile met chaque cellule en nowrap : l'état d'exécution du tray passe à la ligne ; l'icône
   d'une pastille (12 px, 4 px de marge, la place du « ✓ » de main) la garde dans sa colonne (pastille de 3 px et
   état de 32 px plus larges que leur colonne, à 360 et 600 px). */
.dash-flotte .fox-chip .ni { width: 12px; height: 12px; margin-right: 4px; }
@media (max-width: 600px) {
  .dash-flotte .dash-tray-etat { white-space: normal !important; }
  /* La rangée ne défile plus (son contenu tient) et une pastille garde sa largeur : la colonne, calculée au plus
     juste, la coupait de 3 px dans un cadre défilant ; elle mord sur la marge de sa cellule. */
  .dash-flotte .fox-chip-row { overflow: visible; max-height: none; }
  .dash-flotte .fox-chip { max-width: none; }
}
/* [L01-tableau-de-bord] fin */

/* ───────────────────────────────────────────────────────────── */

/* [L02-postes-liste] début */
/* Lire une étiquette (etiquette.js) : l'aperçu de la photo, une ligne « libellé · valeur · Copier ». */
.etiq-apercu-img { display: block; max-width: 100%; max-height: 190px; border: 1px solid var(--border); border-radius: var(--radius-sm); }
.etiq-lib { min-width: 8rem; }
.etiq-valeur { letter-spacing: 0.04em; }
.etiq-ambigu { margin: 0 var(--space-1) var(--space-1) 0; }
/* Le champ fichier (hx-saisie) mesurait 364 px : à 360 px, le tiroir défilait en largeur (vérification du 10/10/2026). */
#etiq-fichier { max-width: 100%; }
/* Filtres de postes (filters.js) : résultats en fenêtre (en-tête collé), une condition sur une ligne qui passe à la ligne. */
.f-resultats { max-height: 50vh; overflow-y: auto; }
.f-resultats .hx-tableau thead th { position: sticky; top: 0; z-index: 2; background: var(--bg-2); }
.fnlq-saisie { min-width: min(16rem, 100%); }
.rule-leaf .rl-col { flex: 1 1 11rem; min-width: min(9rem, 100%); }
.rule-leaf .rl-col--op { flex: 0 0 auto; min-width: 8rem; }
.rule-group--sous { margin-inline-start: var(--space-1); }
/* Liste des équipements (views-devices.js) et gestes d'un poste (views-device-actions.js), reprise du chantier S1. */
.dev-titre-client { font-size: var(--fs-md); font-weight: 400; }
.dev-th-tri { cursor: pointer; user-select: none; }
/* ⚠ !important : sous 600 px, la feuille pose « img, svg… { max-width: 100% !important } ». Une icône à largeur en
   pourcentage ne compte plus dans la largeur de sa colonne : l'indicateur de tri sortait de son en-tête (+8 px). */
.dev-th-tri .ni { margin: 0 0 0 var(--space-1); max-width: none !important; }
/* Le lien vers les archivés (ou les actifs), posé sur le fond de page : en clair, l'accent n'y tient que 4,41:1 au pire
   arrêt du dégradé ; --accent-2, la couleur des liens de la charte, 5,4:1. */
:root[data-theme="light"] .dev-lien-liste { color: var(--accent-2); }
/* Les boutons « danger » de la barre des actions groupées et des actions rapides de la fiche sont posés sur un fond
   teinté, en dégradé en sombre : le rouge du socle n'y tient que 4,22:1 au pire arrêt. Un cran de texte de plus : 4,9:1. */
.dev-barre-lot .hx-bouton--danger, .dev-actions-rapides .hx-bouton--danger { color: color-mix(in srgb, var(--color-crit) 60%, var(--text)); }
/* La note de santé, collée à ses trois jauges (l'espace est ici, pas dans le texte de la cellule). */
.dev-sante-note { margin-inline-end: var(--space-2); }
.dev-col-case { width: 1%; }
.dev-minijauge { display: inline-block; width: 32px; height: 8px; vertical-align: middle; }
.hx-tableau tbody tr.is-choisi > td { background: color-mix(in srgb, var(--color-info) 8%, transparent); }
.dev-tag-retirer { cursor: pointer; }
.dev-liste-cibles { max-height: 160px; overflow-y: auto; margin: 0; padding-left: var(--space-6); list-style: disc; }
/* Événements critiques d'un poste : le niveau en liseré de la ligne ; le détail technique défile dans son cadre. */
.hx-tableau tbody tr.ce-sev--crit > td:first-child { box-shadow: inset 3px 0 0 var(--color-crit); }
.hx-tableau tbody tr.ce-sev--warn > td:first-child { box-shadow: inset 3px 0 0 var(--color-warn); }
.hx-tableau tbody tr.ce-sev--info > td:first-child { box-shadow: inset 3px 0 0 var(--color-info); }
.hx-tableau tbody tr.ce-sev--neutre > td:first-child { box-shadow: inset 3px 0 0 var(--dim); }
.ce-details { max-height: 50vh; overflow: auto; white-space: pre-wrap; overflow-wrap: anywhere; }
.ce-details:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* « Voir tous les événements de <poste> » : un nom de poste de 30 caractères élargissait la fenêtre à 399 px pour 358
   (360 px). Le bouton tient dans la fenêtre ; son libellé s'abrège, entier en infobulle. */
.ce-aller { max-width: 100%; min-width: 0; }
/* [L02-postes-liste] fin */

/* ───────────────────────────────────────────────────────────── */

/* [L03-postes-fiche] début */
/* Fiche du poste et ses onglets (lot L03 de l'harmonisation, 09/10/2026) : ce qu'aucune classe du socle ne fait,
   en jetons seuls. Le reste de la fiche porte les classes du socle (hx-*). */
/* Une colonne de tableau réduite à son contenu : la place restante va aux colonnes sans cette classe. */
.hx-tableau col.fp-col-serree { width: 1%; }
/* Liste à cocher qui défile dans une fenêtre (inventaire et catalogue des services surveillés). */
.fp-liste { max-height: 50vh; overflow-y: auto; padding: var(--space-1); }
.fp-liste__ligne { padding: var(--space-2) var(--space-3); border-bottom: 1px solid var(--border); }
.fp-liste__ligne:last-child { border-bottom: 0; }
/* Script de remédiation d'un service : texte à chasse fixe. */
textarea.fp-script { font-family: 'Cascadia Code', 'Consolas', 'Courier New', monospace; resize: vertical; }
/* Un repli (details) dont le résumé se clique. */
.fp-repli > summary { cursor: pointer; }
/* Les 24 dernières heures d'un service : une barre par heure, son ton choisi par le code. */
.fp-spark { vertical-align: middle; margin-left: var(--space-1); }
.fp-spark rect { fill: var(--dim); }
.fp-spark .fp-spark--vide { fill: var(--border); }
.fp-spark .fp-spark--ok { fill: var(--color-ok); }
.fp-spark .fp-spark--warn { fill: var(--color-warn); }
.fp-spark .fp-spark--crit { fill: var(--color-crit); }
/* Carte des coupures sur 30 jours : la part du pire jour (--hx-part, une DONNÉE posée par le code) teinte la
   case, du fond en retrait au rouge. La légende pose ses parts par classe. */
.fp-chaleur {
  display: inline-block; flex: none; width: 16px; height: 16px;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--color-crit) var(--hx-part, 0%), var(--bg-1));
}
.fp-chaleur--legende { width: 12px; height: 12px; }
.fp-chaleur--25 { --hx-part: 25%; }
.fp-chaleur--50 { --hx-part: 50%; }
.fp-chaleur--75 { --hx-part: 75%; }
.fp-chaleur--100 { --hx-part: 100%; }
/* Un complément de titre (nombre de modules, de volumes, de VMs) : en graisse normale, à côté du titre gras. */
.fp-normal { font-weight: 400; }
/* Liste à puces d'une valeur (cartes graphiques d'un poste). */
.fp-puces { margin: 0; padding-left: var(--space-4); line-height: 1.6; }
/* Texte libre saisi (notes de l'inventaire) : ses retours à la ligne sont gardés. */
.fp-texte-libre { white-space: pre-wrap; overflow-wrap: anywhere; }
/* Réglages RMM de la carte « Système » : séparés des définitions par un filet. Les cases gardent la forme
   de .dev-reglage ; la ligne « automatique » (Windows Server) est atténuée. */
.fp-reglages { padding-top: var(--space-3); border-top: 1px solid var(--border); }
.dev-reglage.hx-attenue { color: var(--dim); }
.dev-reglage > input[type="checkbox"] { margin: 0; cursor: pointer; }
.dev-reglage > input[type="checkbox"]:disabled { cursor: not-allowed; }
/* Historique des métriques : le graphique SVG, sa zone de survol, son infobulle (sa place suit le pointeur,
   écrite par le code) et sa légende. La couleur de la série arrive par --hx-serie (une donnée). */
.fp-graphe { display: block; width: 100%; height: auto; }
.mh-svgwrap { position: relative; }
.mh-hit { cursor: crosshair; }
.fp-infobulle {
  position: absolute; top: var(--space-1); z-index: 5; pointer-events: none;
  padding: var(--space-1) var(--space-2); border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
  background: var(--bg-1); color: var(--text); box-shadow: 0 4px 12px var(--overlay-bg);
  font-size: var(--fs-xs); line-height: 1.35; white-space: nowrap;
}
.fp-legende { justify-content: center; }
.fp-serie__valeur { color: color-mix(in srgb, var(--hx-serie, var(--accent)) 70%, var(--text)); }
.fp-serie__trait { display: inline-block; width: 16px; height: 2px; vertical-align: middle; background: var(--hx-serie, var(--accent)); }
.fp-serie__trait--max { background: repeating-linear-gradient(90deg, var(--hx-serie, var(--accent)) 0 4px, transparent 4px 8px); }
/* Tons propres au lot : une donnée --fp-ton reprise par le liseré, la frise et la puce. */
.fp-ton--ok { --fp-ton: var(--color-ok); }
.fp-ton--warn { --fp-ton: var(--color-warn); }
.fp-ton--crit { --fp-ton: var(--color-crit); }
.fp-ton--info { --fp-ton: var(--color-info); }
.fp-ton--neutre { --fp-ton: var(--dim); }
/* Carte ou encart sans ton de gravité : un liseré neutre (ou celui de --fp-ton). */
.fp-liseret { border-left: 3px solid var(--fp-ton, var(--border-strong)); }
/* Séparation pointillée des actions SentinelOne sous la carte. */
.fp-filet { margin-top: var(--space-3); padding-top: var(--space-3); border-top: 1px dashed var(--border); }
.fp-centre { justify-content: center; margin-top: var(--space-3); }
/* Frise chronologique de l'onglet Historique : une ligne verticale, un événement par carte, sa puce ronde. */
.fp-frise { list-style: none; margin: 0; padding: 0 0 0 28px; border-left: 2px solid var(--border); }
.fp-frise__evt { position: relative; margin-bottom: var(--space-3); padding: var(--space-2) var(--space-3);
  background: var(--surface-2); border-radius: var(--radius-sm); border-left: 3px solid var(--fp-ton, var(--dim)); }
.fp-frise__puce { position: absolute; left: -41px; top: var(--space-2); width: 24px; height: 24px;
  display: flex; align-items: center; justify-content: center;
  background: var(--bg-1); border: 2px solid var(--fp-ton, var(--dim)); border-radius: 50%; color: var(--fp-ton, var(--dim)); }
/* Résultat d'une tâche dans la frise du poste : un bloc de texte qui défile, ses lignes repliées. (26/10/2026,
   revue M-c34-2 : sa variante haute ne servait qu'au visualiseur « Logs agent », retiré.) */
.fp-journal { overflow: auto; white-space: pre-wrap; word-break: break-word; line-height: 1.45; }
/* Badge de santé d'un sous-agent qui ouvre son détail. */
.fp-badge-ouvrable { cursor: pointer; }
.fp-carte-tete { padding: var(--space-4) var(--space-4) 0; }
.fp-carte-pied { padding: var(--space-3) var(--space-4); border-top: 1px solid var(--border); }
/* Score de santé du poste : un grand chiffre au-dessus de « / 100 ». */
.fp-score { min-width: 78px; text-align: center; }
.fp-score__valeur { font-size: 2rem; font-weight: 800; line-height: 1; }
.fp-ton--accent { --fp-ton: var(--accent); }
/* En-tête de la fiche : les contrats du poste (badge lien), le contrat par défaut cerclé. */
.fp-contrat { text-decoration: none; }
/* 10/10/2026 (vérification) : un contrat est un badge qui passe à la ligne (nom long, type, échéance de garantie) ;
   d'une ligne, il sortait de l'écran à 360 px. L'échéance reprend --muted, comme sur main (4,1:1 en --dim). */
.fp-contrat { flex-wrap: wrap; white-space: normal; border-radius: var(--radius-sm); }
.fp-contrat .hx-attenue { color: var(--muted); }
.fp-contrat--defaut { box-shadow: inset 0 0 0 1px var(--color-warn); }
.fp-bouton-ok { color: var(--color-ok); border-color: color-mix(in srgb, var(--color-ok) 45%, transparent); }
/* Menu « Gérer » : sorti vers <body> à l'ouverture (portail), sa place calculée par le code. */
.dev-manage-dd { position: relative; display: inline-block; }
.fp-menu { position: absolute; left: 0; top: calc(100% + 5px); z-index: 40; min-width: 215px; padding: var(--space-1);
  background: var(--menu-bg, var(--bg-2)); border: 1px solid var(--border-strong); border-radius: var(--radius-md, 10px);
  box-shadow: 0 16px 36px var(--overlay-bg); }
.fp-menu[data-manage-portal] { position: fixed; z-index: var(--z-modal); }
.fp-menu__filet { margin: var(--space-1) var(--space-1); }
#dev-manage-menu .dev-mn-item.hx-texte-crit { color: var(--color-crit); }
#dev-manage-menu .dev-mn-item.hx-texte-warn { color: var(--color-warn); }
#dev-manage-menu .dev-mn-item.hx-texte-ok { color: var(--color-ok); }
#dev-manage-menu .dev-mn-item:disabled { color: var(--dim); cursor: not-allowed; }
/* Groupes d'onglets (menu déroulant de chaque groupe). */
.tab-group { position: relative; }
.tab-group-btn { display: inline-flex; align-items: center; gap: var(--space-2); }
.tab-group-menu[hidden] { display: none; }
.tab-menu-item { display: flex; align-items: center; gap: var(--space-2); width: 100%; padding: var(--space-2) var(--space-3);
  border: 0; border-radius: var(--radius-sm); background: transparent; color: var(--text); text-align: left; cursor: pointer; }
.tab-menu-item:hover { background: var(--surface-2); }
.tab-menu-item.is-actif { background: color-mix(in srgb, var(--accent) 22%, transparent); color: var(--accent); font-weight: 600; }
/* Vue 360 : rail collant à gauche, sections à droite ; une colonne sur écran étroit. */
.fp-d360 { display: grid; grid-template-columns: 268px minmax(0, 1fr); gap: var(--space-4); align-items: start; }
.fp-d360__rail { position: sticky; top: calc(var(--topbar-h, 58px) + 10px); display: flex; flex-direction: column; gap: var(--space-3);
  max-height: calc(100vh - var(--topbar-h, 58px) - 20px); overflow-y: auto; }
@media (max-width: 980px) { .fp-d360__rail { position: static; max-height: none; } }
/* 10/10/2026 (vérification de l'intégration, banc visuel du lot) : sous 980 px, la colonne unique peut rétrécir
   (avec « 1fr », elle prenait la largeur minimale de son contenu : la pile des sections débordait de l'écran de
   24 px à 360 et 600 px) ; et le rail passe SOUS le contenu, comme le voulait la reprise (compte rendu du lot L03) :
   les sections, en tête, se chargent d'emblée (observateur à 300 px) comme sur main, au lieu d'attendre sous le rail
   (à 900 px, inventaire, logiciels et mises à jour restaient « Chargement… »). */
@media (max-width: 980px) { .fp-d360 { grid-template-columns: minmax(0, 1fr); } .fp-d360__rail { order: 2; } }
/* La pile des sections est un <main> : la règle générale « main { margin: 0 auto } » en faisait un élément de grille
   NON étiré, à la largeur de son contenu — les lignes coupées (imprimante, chemin) la poussaient à 100vw, 24 px de
   plus que sa colonne à 360 et 600 px. Sans marge automatique, elle prend la largeur de sa colonne. */
.fp-d360 > main { margin: 0; }
/* Sous 600 px, le détail d'une imprimante (port · pilote · emplacement) se replie au lieu d'être coupé (88 px de
   texte perdus à 360 px, l'icône d'emplacement hors du cadre) : la colonne n'a plus la largeur d'un écran large. */
@media (max-width: 600px) { .fp-ligne-liste .hx-tronque { white-space: normal; overflow-wrap: anywhere; } }
.fp-d360__nav { display: flex; flex-direction: column; gap: 2px; }
.fp-nav { display: flex; align-items: center; gap: var(--space-2); padding: var(--space-1) var(--space-3); border-radius: var(--radius-sm);
  color: var(--text); text-decoration: none; font-size: var(--fs-sm); }
.fp-nav:hover { background: var(--surface-2); }
.fp-nav.is-actif { background: color-mix(in srgb, var(--accent) 16%, transparent); }
.fp-section { scroll-margin-top: calc(var(--topbar-h, 58px) + 14px); }
.fp-section__detail { margin-top: var(--space-3); padding-top: var(--space-3); border-top: 1px solid var(--border); }
.fp-rail-bloc { margin-bottom: var(--space-2); padding-bottom: var(--space-2); border-bottom: 1px solid var(--border); }
.fp-ligne-rail { padding: 2px 0; }
.fp-ligne-etat { padding: var(--space-1) 0; }
.fp-ligne-liste { padding: var(--space-1) 0; border-bottom: 1px solid var(--border); }
.fp-ligne-liste:last-child { border-bottom: 0; }
.fp-col-icone { width: 22px; flex: none; text-align: center; }
/* Tuiles de la bande KPI : liseré du ton, grand chiffre. */
.fp-kpi { flex: 1; min-width: 0; text-align: left; padding: var(--space-2) var(--space-3); cursor: pointer; color: var(--text);
  background: var(--surface-2); border: 1px solid var(--border); border-left: 3px solid var(--fp-ton, var(--dim)); border-radius: var(--radius-md, 9px); }
.fp-kpi__lib { display: flex; align-items: center; gap: var(--space-1); text-transform: uppercase; letter-spacing: .05em; }
.fp-kpi__val { font-size: 1.45rem; font-weight: 800; line-height: 1.15; }
/* Barre de mesure (CPU, RAM, disque) : libellé, jauge du socle, valeur, complément. */
.fp-barre { flex-wrap: nowrap; }
.fp-barre__lib { min-width: 78px; }
.fp-barre__jauge { flex: 1; width: auto; }
.fp-barre__val { min-width: 42px; text-align: right; }
.fp-barre__extra { min-width: 90px; }
/* Mesures d'une machine hors ligne : dernières valeurs connues, en teinte effacée. */
.fp-perime { color: var(--dim); }
.fp-perime .hx-jauge { --hx-ton: var(--dim); }
.fp-perime [class*="hx-texte-"] { color: var(--dim); }
/* Listes à choisir (scripts, paquets) d'une fenêtre. */
.fp-liste--choix { max-height: 380px; padding: 0; }
.fp-liste__choix { cursor: pointer; }
.fp-liste__choix[hidden] { display: none; }
/* [L03-postes-fiche] fin */

/* ───────────────────────────────────────────────────────────── */

/* [L04-tickets] début */
/* Tickets, base de connaissances, modèles de réponse (harmonisation visuelle, lot L04, 09/10/2026).
   Formes propres à ces écrans ; tout le reste vient du socle hx-*. Jetons seuls. */

/* Liste : la portée du titre (client ou vue globale), une cellule qui ouvre le ticket, un sous-libellé de champ. */
.tk-portee { font-weight: 400; color: var(--dim); }
.tk-cliquable, .tk-d-cliquable { cursor: pointer; }
.tk-normal { font-weight: 400; text-transform: none; letter-spacing: 0; }

/* Étiquette en petites capitales (au-dessus d'une valeur). */
.tk-etiquette { display: inline-flex; align-items: center; gap: var(--space-1); font-size: var(--fs-xs); color: var(--dim); text-transform: uppercase; letter-spacing: 0.06em; }

/* Fiche d'un ticket : en-tête. */
.tk-d-titre { display: flex; align-items: center; gap: var(--space-2); margin: var(--space-2) 0 var(--space-1); }
.tk-d-meta { margin: 0 0 var(--space-2); }
/* Le retour à la liste reste du texte atténué, comme avant la reprise : en --accent (.hx-lien), il tombait à
   4,41:1 sur le fond dégradé du thème clair. */
.tk-d-retour { color: var(--muted); }
.tk-d-souligne { border-bottom: 1px dashed currentColor; }
.tk-d-contrat { text-decoration: none; }
.tk-d-poste { align-items: flex-end; }
.tk-d-champ { min-width: 10rem; }
.tk-d-champ--large { min-width: 13rem; }
.tk-d-pied-champs { align-self: flex-end; }
.tk-d-attente { min-height: 1em; }
.tk-d-tag-saisie.hx-saisie { width: 6rem; }
.tk-tag-chip { cursor: pointer; }
/* Un champ de la fiche changé et pas encore enregistré (refreshPendingMeta). L'id pèse sur la règle générale des champs. */
#tk-detail .hx-saisie.is-modifie { border-color: var(--color-warn); background: color-mix(in srgb, var(--color-warn) 14%, var(--bg-1)); }

/* Indicateurs SLA / temps : le pied colle en bas de la carte. */
.tk-d-kpi { display: flex; flex-direction: column; }
.tk-d-kpi__pied { margin-top: auto; padding-top: var(--space-2); }
.tk-d-facturation { cursor: pointer; border-style: dashed; }

/* Analyses IA, commentaires : texte long, retours à la ligne gardés. */
.tk-ia-texte { font-size: var(--fs-sm); line-height: 1.55; white-space: pre-wrap; }
.tk-d-resume { font-size: var(--fs-sm); line-height: 1.5; }

/* Le fil des messages : une bulle par message, rangée selon l'émetteur. */
.tk-d-fil { max-height: 50vh; overflow-y: auto; }
.tk-bulle-ligne { display: flex; margin-bottom: var(--space-3); }
.tk-bulle-ligne--user { justify-content: flex-start; }
.tk-bulle-ligne--tech { justify-content: flex-end; }
.tk-bulle-ligne--systeme { justify-content: center; }
.tk-bulle { --tk-bulle: var(--dim); max-width: 75%; min-width: 0; padding: var(--space-2) var(--space-3); border-left: 3px solid var(--tk-bulle); border-radius: var(--radius-sm); background: color-mix(in srgb, var(--tk-bulle) 10%, transparent); }
.tk-bulle--user { --tk-bulle: var(--accent); }
.tk-bulle--tech { --tk-bulle: var(--color-ok); }
.tk-bulle--interne { --tk-bulle: var(--color-warn); background: color-mix(in srgb, var(--tk-bulle) 15%, transparent); }
.tk-bulle--courriel { width: 100%; max-width: 860px; box-sizing: border-box; }
.tk-bulle__tete { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-1); margin-bottom: var(--space-1); font-size: var(--fs-xs); color: var(--dim); }
/* Vérification du 09/10/2026 : sur le fond plus teinté de la note interne, --dim tombait à 4,42:1 en sombre ;
   l'en-tête y prend le texte atténué le plus lisible des deux thèmes (--muted). */
.tk-bulle--interne .tk-bulle__tete { color: var(--muted); }
.tk-bulle__corps { font-size: var(--fs-sm); white-space: pre-wrap; overflow-wrap: anywhere; }
.tk-html-frame { display: block; width: 100%; max-width: 860px; min-height: 60px; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--code-bg); }
.tk-att-img { max-width: 240px; max-height: 140px; border: 1px solid var(--border); border-radius: var(--radius-sm); cursor: zoom-in; }
.tk-att-fichier { display: inline-flex; align-items: center; gap: var(--space-1); padding: var(--space-1) var(--space-2); border-radius: var(--radius-sm); background: var(--bg-2); font-size: var(--fs-xs); }
@media (max-width: 600px) { .tk-bulle { max-width: 100%; } }
/* 26/10/2026 : le rapport de l'assistant IA (corps du ticket et note interne) rangé en rubriques (rapportIaHtml de
   tickets.js). Le corps de bulle est en pre-wrap : le rapport repasse en normal, seuls les textes gardent leurs lignes. */
.tk-bulle--rapport-ia { width: 100%; max-width: 860px; box-sizing: border-box; }
.tk-rapport-ia { display: flex; flex-direction: column; gap: var(--space-3); white-space: normal; }
.tk-rapport-ia__etiquette { font-size: var(--fs-xs); font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; color: var(--muted); }
.tk-rapport-ia__meta { font-weight: 400; text-transform: none; letter-spacing: 0; }
.tk-rapport-ia__resume { display: flex; flex-direction: column; gap: var(--space-1); }
.tk-rapport-ia__lead { margin: 0; font-size: var(--fs-base); line-height: 1.5; white-space: pre-wrap; }
.tk-rapport-ia__intro { margin: 0; color: var(--muted); }
.tk-rapport-ia__grille { display: grid; grid-template-columns: fit-content(15rem) minmax(0, 1fr); align-items: baseline; gap: var(--space-2) var(--space-4); margin: 0; }
.tk-rapport-ia__grille > dt .badge { margin-left: var(--space-1); }
.tk-rapport-ia__grille .tk-rapport-ia__meta { display: block; }
.tk-rapport-ia__grille > dd { margin: 0; min-width: 0; line-height: 1.5; }
.tk-rapport-ia__grille > dd.tk-rapport-ia--alerte { padding-left: var(--space-2); border-left: 3px solid var(--color-warn); }
.tk-rapport-ia__texte { white-space: pre-wrap; }
.tk-rapport-ia__liste { display: flex; flex-direction: column; gap: var(--space-1); margin: 0; padding-left: var(--space-4); }
.tk-rapport-ia__liste > li::marker { color: var(--muted); }
.tk-rapport-ia--alerte .tk-rapport-ia__liste > li::marker { color: var(--color-warn); }
.tk-rapport-ia__replie > summary { cursor: pointer; color: var(--muted); }
.tk-rapport-ia__replie > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.tk-rapport-ia__replie[open] > summary { margin-bottom: var(--space-1); }
.tk-rapport-ia__pied { display: flex; flex-direction: column; gap: var(--space-1); padding-top: var(--space-2); border-top: 1px solid var(--border); font-size: var(--fs-xs); color: var(--muted); }
@media (max-width: 600px) {
  .tk-rapport-ia__grille { grid-template-columns: minmax(0, 1fr); gap: var(--space-1); }
  .tk-rapport-ia__grille > dd { margin-bottom: var(--space-2); }
}

/* Historique du ticket (fenêtre). */
.tk-histo-ligne { padding: var(--space-2) 0; border-bottom: 1px solid var(--border); font-size: var(--fs-sm); }

/* Statistiques : la couleur des indicateurs (widgets). */
.widget.tk-kpi--info { --w-accent: var(--color-info); }
.widget.tk-kpi--ok { --w-accent: var(--color-ok); }
.widget.tk-kpi--crit { --w-accent: var(--color-crit); }
.tk-csat-moyenne { min-width: 9rem; text-align: center; }
.tk-csat-note { margin: var(--space-2) 0; font-size: 2.6rem; font-weight: 700; line-height: 1; }
.tk-csat-repartition { min-width: 16rem; }
.tk-csat-libelle { min-width: 8rem; font-size: var(--fs-xs); }
.tk-csat-compte { min-width: 4rem; font-size: var(--fs-xs); }
.tk-csat-avis-liste { margin-top: var(--space-4); padding-top: var(--space-4); border-top: 1px solid var(--border); }
.tk-csat-avis { border-left: 3px solid var(--dim); }
.tk-csat-avis--ok { border-left-color: var(--color-ok); }
.tk-csat-avis--warn { border-left-color: var(--color-warn); }
.tk-csat-avis--crit { border-left-color: var(--color-crit); }

/* Base de connaissances : un filet entre deux articles, la mention de l'article rédigé par l'IA. */
.kb-row { padding: var(--space-2) 0; border-bottom: 1px solid var(--border); }
.kb-row:last-child { border-bottom: 0; }
.kb-provenance { margin: 0 0 var(--space-2); font-size: var(--fs-xs); color: var(--dim); }
/* [L04-tickets] fin */

/* ───────────────────────────────────────────────────────────── */

/* [L05-nas] début */
/* Gestion NAS › NAS (vérification de l'intégration, 09/10/2026) : avec les en-têtes du socle (capitales, lettres espacées),
   les 7 colonnes triables du parc débordaient de leur cadre à 1 280 px (« Alertes » hors champ, 15 px de défilement).
   Ce seul tableau resserre ses cellules à --space-2 de côté (au lieu de --space-3) : il tient dans son cadre, la ligne de
   client garde sa propre marge. */
.syno-parc-table th, .syno-parc-table td { padding-left: var(--space-2); padding-right: var(--space-2); }
/* [L05-nas] fin */

/* ───────────────────────────────────────────────────────────── */

/* [L06-supervision-alertes] début */
/* Portée « — client » à côté d un titre de page Réglages / Audit. */
.adm-portee { color: var(--muted); font-weight: normal; }
/* Rangée de champs alignés par le bas : le bouton tombe au niveau des saisies. */
.adm-grappe-bas { align-items: flex-end; }
/* Listes de texte (Réglages > concurrence, aide). */
.adm-liste { margin: 0 0 0 var(--space-4); line-height: 1.6; }
/* Tableau de comparaison concurrentielle : notre ligne marquée. */
.adm-concurrence { width: 100%; font-size: var(--fs-sm); }
.adm-concurrence tr.adm-nous { background: rgba(var(--accent-rgb), 0.05); }
/* Bloc repliable (données brutes, explication IA). */
.adm-repli > summary { cursor: pointer; color: var(--accent); font-size: var(--fs-sm); }
/* Dans une cellule de tableau, le libellé du repli passe à la ligne : sous 600 px, la couche mobile met les
   cellules en nowrap, et « Version pour le client » sortait de la sienne (vérification du 09/10/2026). */
td .adm-repli > summary { white-space: normal; }
.adm-texte-libre { white-space: pre-wrap; font-size: var(--fs-sm); line-height: 1.5; }
/* Éditeur de règle : champs alignés par le bas, cases empilées. */
.ar-champ-bas { margin-top: auto; }
.ar-cases { font-size: var(--fs-sm); }
/* Galerie de modèles : icône et description. */
.ar-galerie__icone { font-size: var(--fs-xl); line-height: 1; color: var(--accent); }
.ar-galerie__desc { flex: 1; line-height: 1.5; }
/* Flux d'activité : en-tête de jour, lignes cliquables. */
.af-recherche { flex: 1; min-width: 140px; }
.af-jour { padding: var(--space-2) var(--space-3); background: var(--surface-2); color: var(--muted); font-size: var(--fs-xs); font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px; }
.af-ligne { display: flex; align-items: flex-start; gap: var(--space-2); padding: var(--space-2) var(--space-3); border-top: 1px solid var(--border); }
.af-heure { min-width: 40px; padding-top: 2px; color: var(--muted); font-size: var(--fs-xs); font-variant-numeric: tabular-nums; }
.af-point { margin-top: 6px; }
.af-cat { flex: none; color: var(--muted); }
.af-texte { min-width: 0; }
.af-lien { display: block; color: inherit; text-decoration: none; }
.af-lien:hover { background: rgba(var(--accent-rgb), 0.06); }
/* Renouvellement : note à droite du bandeau de tête. */
.renouv-note { flex: 1; min-width: min(100%, 220px); }
/* Santé RMM : constats par domaine, listes des fenêtres de détail. */
.rh-calcul { flex-direction: column; align-items: flex-end; color: var(--muted); }
.rh-constat { display: flex; gap: var(--space-2); padding: var(--space-1) 0; border-top: 1px solid var(--border); }
.rh-sev { flex: none; width: 34px; font-weight: 700; }
.rh-drill { color: var(--text); text-decoration: underline dotted; cursor: pointer; }
.rh-clients { list-style: none; margin: 0; padding: 0; columns: 2; column-gap: var(--space-5); }
.rh-client { padding: var(--space-1) var(--space-2); border-top: 1px solid var(--border); }
.rh-defile { max-height: 60vh; overflow: auto; }
.rh-defile thead th { position: sticky; top: 0; z-index: 2; box-shadow: inset 0 -1px 0 var(--border); }
@media (max-width: 600px) { .rh-clients { columns: 1; } }
/* Console IA : sortie PowerShell repliée sous sa ligne. */
.ps-out-cellule { padding: 0 0 var(--space-2); }
.ps-out-box { margin: 0; max-height: 340px; overflow: auto; white-space: pre-wrap; }
/* Explorateur de fichiers. */
.fm-chemin { font-family: 'Cascadia Code', 'Consolas', 'Courier New', monospace; }
.fm-liste { max-height: 50vh; overflow: auto; }
/* Chat technicien <-> utilisateur. */
.ch-fenetre { display: flex; flex-direction: column; overflow: hidden; }
.ch-entete { padding-bottom: var(--space-2); border-bottom: 1px solid var(--border); margin-bottom: 0; }
.ch-fil { flex: 1; min-height: 300px; overflow: auto; padding: var(--space-2) 0; }
.ch-reponse { padding-top: var(--space-2); border-top: 1px solid var(--border); }
.ch-nouvelle { padding: var(--space-5) 0; }
.ch-msg { display: flex; margin: var(--space-2) 0; justify-content: center; }
.ch-msg--user { justify-content: flex-start; }
.ch-msg--tech { justify-content: flex-end; }
.ch-msg__bulle { max-width: 75%; padding: var(--space-2) var(--space-3); border-left: 3px solid var(--dim); border-radius: var(--radius-sm); background: color-mix(in srgb, var(--dim) 10%, transparent); }
.ch-msg--user .ch-msg__bulle { border-left-color: var(--accent); background: rgba(var(--accent-rgb), 0.10); }
.ch-msg--tech .ch-msg__bulle { border-left-color: var(--color-ok); background: color-mix(in srgb, var(--color-ok) 10%, transparent); }
.ch-msg__corps { font-size: var(--fs-sm); line-height: 1.5; }
/* Liste des conversations : ligne qui attend une réponse. */
.chat-non-lu { background: color-mix(in srgb, var(--color-crit) 8%, transparent); }
.chat-dernier { max-width: 340px; }
/* [L06-supervision-alertes] fin */

/* ───────────────────────────────────────────────────────────── */

/* [L07-supervision-reseau] début — réseau LAN, WAN, MDM, RustDesk (harmonisation, 09/10/2026)
   Ce que ces écrans ont de propre une fois leurs styles en dur repris par le socle (et les quatre blocs <style> que
   wan_monitoring.js injectait par le code). Jetons seuls ; placé après le socle : une classe d'ici COMPLÈTE le
   composant qu'elle accompagne. */

/* La suite d'un titre (« — Android + Apple », « — ce client ») : en graisse normale, à côté du titre. */
.l07-titre-suite { font-weight: 400; }

/* ⚠ La règle générale des <input> pèse (0,5,1) : d'où les :not() repris. Un ASN, un CIDR se saisissent en chasse
   fixe ; une priorité tient dans 5rem ; le choix du site d'un lien ne s'étire pas au-delà de 12rem. */
input.l07-saisie-code:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]):not([type=file]),
textarea.l07-saisie-code { font-family: 'Cascadia Code', 'Consolas', 'Courier New', monospace; resize: vertical; }
input.wan-saisie-priorite:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]):not([type=file]) { width: 5rem; text-align: center; }
select.wan-saisie-site { max-width: 12rem; }
input.wan-filtre:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]):not([type=file]) { min-width: 14rem; }

/* ── RustDesk ── */
/* Le QR se lit sur fond blanc dans les deux thèmes (un lecteur de QR veut le contraste du papier). */
.rd-qr { flex-shrink: 0; padding: var(--space-2); border-radius: var(--radius); background: #fff; }
.rd-qr--vide { display: flex; align-items: center; justify-content: center; width: 196px; height: 196px; background: var(--code-bg); }
.rd-etapes { min-width: 14rem; line-height: 1.6; }
/* Un identifiant ou un mot de passe à recopier : sélection d'un seul clic, lettres espacées. */
.rd-secret { user-select: all; letter-spacing: 0.08em; }
/* La fenêtre de prise en main passe au-dessus du tiroir DOC (--z-modal) quand les deux sont ouverts. */
.hx-voile.rdm-voile { z-index: calc(var(--z-modal) + 100); }

/* ── WAN ── */
/* Les en-têtes triables du tableau des sites. */
.wan-sortable { cursor: pointer; user-select: none; }
.wan-sortable:hover { background: var(--bg-1); }
.wan-sortable.wan-sorted { color: var(--color-info); }
.wan-sort-ind { margin-left: var(--space-1); }
/* La ligne d'en-tête d'un client dans le tableau groupé. */
.hx-tableau tr.wan-groupe td { background: color-mix(in srgb, var(--accent) 6%, transparent); border-top: 2px solid var(--accent); }
/* Un ASN vu sur l'anchor mais absent des liens déclarés : en italique, c'est une hypothèse. */
.wan-asn-unconfigured { font-style: italic; }
.wan-asn-unconfigured small { margin-left: var(--space-1); font-style: normal; }
/* La case OverTheBox : la case calée en haut du texte qui l'explique. */
label.hx-case.wan-case-otb { align-items: flex-start; }
/* L'en-tête d'un site dans le volet des anchors. */
.wan-anchors-panel .site-head { padding: var(--space-1); border-bottom: 2px solid var(--border-strong); }
/* Le tiroir de l'historique des bascules, collé à droite. */
.wan-tiroir {
  position: fixed; top: 0; right: 0; z-index: var(--z-modal);
  width: min(520px, 100%); height: 100vh; overflow-y: auto; padding: var(--space-6);
  background: var(--bg-1); border-left: 1px solid var(--border-strong); box-shadow: -12px 0 40px var(--overlay-bg);
  animation: wan-slide-in 0.18s ease-out;
}
@keyframes wan-slide-in { from { transform: translateX(100%); } to { transform: translateX(0); } }
@media (prefers-reduced-motion: reduce) { .wan-tiroir { animation: none; } }

/* ── Réseau LAN ── */
/* Le fond d'un schéma (SNMP ou déduit) et le dessin lui-même. */
.net-fond { background: var(--code-bg); border: 1px solid var(--border); border-radius: var(--radius); }
.hx-carte--nue > .net-fond { border: 0; border-radius: 0; }
.net-dessin { display: block; }
.net-glisse { cursor: grab; user-select: none; }
.net-glisse.is-saisi { cursor: grabbing; }
.net-dessin g.is-cliquable { cursor: pointer; }
.net-legende { border-top: 1px solid var(--border); padding-top: var(--space-3); }
/* Un trait de légende : la couleur du lien qu'il désigne sur le dessin. */
.net-trait { display: inline-block; width: 14px; height: 2px; background: var(--dim); }
.net-trait--ok { background: var(--color-ok); }
.net-trait--accent { height: 3px; background: var(--accent); }
.net-trait--info { background: var(--color-info); }
.net-trait--tirets { height: 0; background: none; border-top: 2px dashed var(--dim); }
/* Les jauges d'encre et de toner : la couleur du consommable lui-même (une donnée, pas un état) ; sous 15 %, le
   ton critique du socle l'emporte. */
.net-encre--noir { --hx-ton: var(--text); }
.net-encre--cyan { --hx-ton: #06b6d4; }
.net-encre--magenta { --hx-ton: #db2777; }
.net-encre--jaune { --hx-ton: #eab308; }
.net-encre--piece { --hx-ton: var(--accent); }
.net-encre--autre { --hx-ton: var(--state-neutral); }
.net-encre--bas { --hx-ton: var(--color-crit); }
.net-encre__nom { min-width: 9rem; max-width: 9rem; }
.net-encre__val { min-width: 4rem; }
/* Un hôte dans la fenêtre d'un groupe du schéma : une ligne, un filet dessous. */
.net-hote { padding: var(--space-2) var(--space-1); border-bottom: 1px solid var(--border); }
.net-bloc { display: block; }
/* La barre d'action de la sélection reste en vue quand la liste défile. */
.dd-barre { position: sticky; top: 0; z-index: var(--z-sticky); }
/* Les colonnes alignées d'un groupe de type à l'autre : case, IP, MAC, Hostname, Vendor, SNMP, Ports,
   Identification, Vu en dernier, Actions. */
table.dd-table { table-layout: fixed; min-width: 60rem; }
table.dd-table col:nth-child(1) { width: 2.25rem; }
table.dd-table col:nth-child(2) { width: 8rem; }
table.dd-table col:nth-child(3) { width: 9.5rem; }
table.dd-table col:nth-child(4), table.dd-table col:nth-child(5), table.dd-table col:nth-child(8) { width: 14%; }
table.dd-table col:nth-child(6) { width: 18%; }
table.dd-table col:nth-child(7) { width: 13%; }
table.dd-table col:nth-child(9) { width: 8.5rem; }
table.dd-table col:nth-child(10) { width: 12.5rem; }
/* Vérification du 10/10/2026. Le libellé OverTheBox est un texte d'accent sur le bandeau teinté : --accent-2, le
   jeton du texte d'accent (--accent y tombait à 4,41:1 en sombre). Dans la fenêtre des liens, le libellé caché
   « Actions » (.hx-lecteur-seul, en position absolue) échappait au cadre défilant et faisait défiler toute la
   fenêtre en largeur à 360 px : le cadre devient son bloc conteneur. */
.wan-case-otb .hx-texte-accent { color: var(--accent-2); }
#wan-modal-links-root .hx-defile { position: relative; }
/* [L07-supervision-reseau] fin */

/* ───────────────────────────────────────────────────────────── */

/* [L08-securite] début */
/* Sécurité Endpoint (dashboard-edr.js) : portée du titre, libellés des tuiles, score, jauges, activité, menaces. */
.es-titre-portee { font-weight: normal; }
.es-stat-lib { text-transform: uppercase; letter-spacing: 0.04em; }
.es-score { align-items: center; min-width: 7.5rem; }
.es-score-num { font-size: calc(var(--fs-xl) * 1.6); font-weight: 800; line-height: 1; }
.es-jauges { min-width: min(18rem, 100%); }
.es-jauge { flex: 1 1 9rem; min-width: min(9rem, 100%); }
.es-jauge--neutre { --hx-ton: var(--dim); }
.es-genere { margin-bottom: 0; text-align: end; }
.es-capitale { text-transform: capitalize; }
.es-inv-carte { width: 100%; text-align: start; font: inherit; color: inherit; }
.es-activites { max-height: 420px; overflow-y: auto; }
.es-activite { padding: var(--space-1) 0; border-bottom: 1px dashed var(--border); }
.es-activite-icone { flex: 0 0 24px; text-align: center; }
.es-col-case { width: 2rem; }
.es-chemin { max-width: 230px; }
.es-actions { max-width: 215px; margin-inline-start: auto; }
/* Vérification du 10/10/2026 : à 360 px, « Upgrader les 21 agents Windows en retard → 24.2… » sortait de l'écran
   (bouton sur une ligne) ; il passe à la ligne. Sous 700 px, le chemin d'une menace se replie au lieu d'être
   coupé (sur main, la colonne n'était coupée qu'en grand écran ; le chemin complet reste dans l'infobulle). */
.es-upgrade-btn { white-space: normal; text-align: start; max-width: 100%; }
@media (max-width: 700px) { .es-chemin { max-width: none; white-space: normal; overflow-wrap: anywhere; } }
/* Agents SentinelOne (s1-agents.js), reprise du chantier S1 : ce que les composants du socle posés à côté des accroches
   s1ag-* ne couvrent pas encore. La saisie de confirmation (s1ag-f-saisie, que ses bancs scellent à la lettre) prend
   l'anneau de focus du socle ; la portée du titre, posée sur le fond de page, n'est plus atténuée (4,47:1 en clair). */
.s1ag-f-saisie:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; border-color: var(--accent); }
.s1ag-titre-portee { color: var(--text); }
/* La ligne cochée : --accent-glow (45 % d'accent en sombre) ne laissait au badge « En ligne » que 3,2:1, et 2,66:1 au
   texte atténué de la ligne. Un voile d'accent comme celui des lignes cochées de la liste des équipements (8 %) :
   4,7:1 au texte atténué, au pire arrêt du dégradé (16 % n'en laissait que 4,22:1). */
.s1ag-table tr.is-choisie { background: color-mix(in srgb, var(--accent) 8%, transparent); }
/* [L08-securite] fin */

/* ───────────────────────────────────────────────────────────── */

/* [L09-facturation] début */
/* [L09-facturation] fin */

/* ───────────────────────────────────────────────────────────── */

/* [L10-contrats] début */
/* Tuiles du tableau de bord PSA (psa.js) : le ton de la tuile est une classe, plus un --w-accent en ligne.
   Le mecanisme .widget lit --w-accent et le triplet --w-accent-rgb. */
a.widget.psa-tuile { display: block; color: inherit; text-decoration: none; }
.psa-tuile { --w-accent: var(--accent); --w-accent-rgb: var(--accent-rgb); }
.psa-ton--crit { --w-accent: var(--color-crit); --w-accent-rgb: 220, 38, 38; }
.psa-ton--warn { --w-accent: var(--color-warn); --w-accent-rgb: 245, 158, 11; }
.psa-ton--info { --w-accent: var(--color-info); --w-accent-rgb: 59, 130, 246; }
.psa-ton--ok   { --w-accent: var(--color-ok);   --w-accent-rgb: 22, 163, 74; }
/* 23/10/2026 (Jerome : chiffres « beaucoup plus gros », et « uniquement sur la partie PSA ») : le chiffre des tuiles
   d'indicateurs, sur le SEUL tableau de bord PSA (conteneur data-tableau="psa", pose par psa.js). Le tableau de bord
   RMM n'a pas cet attribut : ses tuiles, PSA comprises, gardent leur taille. Specificite superieure a la regle des
   petites tuiles (#dash-widgets-area > [data-size="small"] .widget-metric). */
#dash-widgets-area[data-tableau="psa"] > [data-widget^="psa_kpi_"] .widget-metric {
  font-size: var(--fs-kpi); letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
}
.psa-tt-part { min-width: 6rem; }
.psa-tt-part .hx-jauge { width: 100%; }
/* « A classer » (psa-contract-review.js) */
.cr-barre-lot { position: sticky; top: 0; z-index: 10; }
.cr-groupe-client { align-items: center; }
.cr-logo { flex: none; width: 38px; height: 38px; display: flex; align-items: center; justify-content: center; }
.cr-case-poste { flex: none; margin-top: var(--space-2); }
.cr-liste-services { max-height: 16rem; overflow: auto; padding: var(--space-1); }
.cr-ligne-service { display: flex; width: 100%; padding: var(--space-2) var(--space-3); border-radius: var(--radius-sm); }
.cr-ligne-service.is-plein { background: var(--color-crit-soft); }
.cr-resume { cursor: pointer; }
.cr-liste-postes { max-height: 9rem; overflow: auto; margin-bottom: 0; padding-left: var(--space-6); list-style: disc; }
/* Vérification du 10/10/2026. Les textes atténués posés par la reprise (--dim) tombaient sous 4,5:1 sur les fonds
   teintés (tuiles, cartes de chiffres, en-tête de groupe client, ligne de service pleine, chiffres du contrat) ;
   avant la reprise, ils étaient en --muted : ils le redeviennent. */
#psa-recent-tickets .hx-attenue, .dash-kpi-card .hx-attenue, .cr-groupe-client .hx-attenue,
.cr-ligne-service .hx-attenue, .psa-contrat-chiffres .hx-attenue { color: var(--muted); }
/* Le retour « Contrats de <client> » porte le nom du client : il passe à la ligne au lieu de sortir de l'écran. */
.hx-statut.psa-retour { white-space: normal; }
/* [L10-contrats] fin */

/* ───────────────────────────────────────────────────────────── */

/* [L11-clients-m365] début */
/* Revue de conformite M365 (conformite.js) */
.conf-total { flex: 1 1 9rem; min-width: 9rem; }
.conf-redaction { white-space: pre-wrap; line-height: 1.55; }
/* Microsoft 365 du client (tenants-m365.js) */
.m365-chiffre { flex: 1 1 6.5rem; min-width: 6.5rem; }
.m365-risques { max-height: 18rem; overflow-y: auto; }
.m365-risque { gap: var(--space-3); padding: var(--space-1) 0; border-bottom: 1px solid var(--border); cursor: pointer; }
.m365-risque:hover { background: var(--bg-1); }
.m365-boite-defilante { max-height: 15rem; overflow-y: auto; }
.m365-ligne { padding: var(--space-1) 0; border-bottom: 1px solid var(--border); }
.m365-liste { margin: 0; padding-left: var(--space-5, 20px); }
.m365-liste--courte { max-height: 7.5rem; overflow-y: auto; }
.m365-cases { display: flex; flex-direction: column; gap: var(--space-1); max-height: 9rem; overflow-y: auto; }
.m365-resume { cursor: pointer; }
.m365-champ-adresse { min-width: 12rem; }
.m365-alloc { max-width: 11rem; }
.m365-secret { user-select: all; word-break: break-all; }
.m365-tendances { grid-template-columns: repeat(auto-fill, minmax(12.5rem, 1fr)); }
.m365-courbe { display: block; max-width: 100%; margin: 0 auto; }
.m365-jauge { width: 100%; }
.m365-ligne-gratuite > td { color: var(--dim); }
.m365-ligne-risque > td { background: var(--color-crit-soft); }
/* Vérification du 10/10/2026 : la liste défilante des risques prend le focus au clavier ; anneau de 2 px. */
.m365-risques:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Clients (tenants.js), reprise du chantier S1 : groupes, vue 360, accès Self-Service, sites, fiche et marque du client. */
/* La couleur d'un groupe, d'une marque : une DONNÉE, passée en variable (--hx-couleur) ; la règle reste ici. */
.tc-pastille { display: inline-block; flex: none; width: 14px; height: 14px; margin-right: var(--space-2); border-radius: var(--radius-sm); background: var(--hx-couleur, var(--dim)); vertical-align: middle; }
.tc-pastille--petite { width: 10px; height: 10px; }
.tc-choix { padding: var(--space-1) var(--space-2); border-radius: var(--radius-sm); }
.tc-choix:hover { background: var(--bg-2); }
.tc-choix .tc-pastille { margin-right: 0; }
.tc-maigre { font-weight: 400; }
textarea.tc-zone { min-height: 60px; resize: vertical; }
.tc-couleur { width: 52px; height: 32px; padding: 2px; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--bg-1); cursor: pointer; }
.tc-couleur:focus-visible, .tc-fichier:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.tc-defilant { max-height: 280px; overflow-y: auto; padding: var(--space-1); }
.tc-defilant--court { max-height: 160px; }
input.tc-mono:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]):not([type=file]), textarea.tc-mono {
  font-family: 'Cascadia Code', 'Consolas', 'Courier New', monospace; user-select: all;
}
.tc-volet { cursor: pointer; }
.tc-logo { max-height: 32px; max-width: 150px; object-fit: contain; }
/* Vue 360 : tuiles (widget) et leur ton, lignes de liste, actions d'un modèle de logiciels. */
.tc-kpi { display: block; color: inherit; text-decoration: none; }
.tc-ton--info { --w-accent: var(--color-info); --w-accent-rgb: 59, 130, 246; }
.tc-ton--crit { --w-accent: var(--color-crit); --w-accent-rgb: 220, 38, 38; }
.tc-ton--warn { --w-accent: var(--color-warn); --w-accent-rgb: 245, 158, 11; }
.tc-ton--accent { --w-accent: var(--accent); --w-accent-rgb: var(--accent-rgb); }
.tc-ton--neutre { --w-accent: var(--dim); --w-accent-rgb: 130, 144, 180; }
:root[data-theme="light"] .tc-ton--info { --w-accent-rgb: 29, 78, 216; }
:root[data-theme="light"] .tc-ton--crit { --w-accent-rgb: 185, 28, 28; }
:root[data-theme="light"] .tc-ton--warn { --w-accent-rgb: 169, 77, 8; }
:root[data-theme="light"] .tc-ton--neutre { --w-accent-rgb: 90, 106, 133; }
.tc-ligne { padding: var(--space-1) 0; border-bottom: 1px solid var(--border); }
.tc-ligne--pointillee { border-bottom-style: dashed; }
.tc-ligne--cliquable { cursor: pointer; }
.tc-ligne--cliquable:hover { background: var(--bg-1); }
.tc-tpl-actions { align-items: flex-end; min-width: 10rem; }
/* Aperçu de la marque du client : ses couleurs (variables) sur le gabarit du portail. Le texte du bandeau du
   portail est blanc : l'aperçu montre ce que le client verra. */
.tc-apercu-bandeau { padding: var(--space-2) var(--space-4); background: var(--hx-couleur, var(--accent)); color: #fff; font-size: var(--fs-sm); font-weight: 600; }
.tc-apercu-corps { padding: var(--space-3) var(--space-4); background: var(--bg-1); }
.tc-apercu-bouton { padding: var(--space-1) var(--space-4); border-radius: var(--radius-sm); background: var(--hx-couleur, var(--accent)); color: var(--hx-encre, #fff); font-size: var(--fs-sm); font-weight: 600; }
.tc-apercu-lien { color: var(--hx-couleur, var(--accent)); font-size: var(--fs-sm); font-weight: 600; }
/* Ligne d'un client archivé (opacité 0,85, réglée pour du texte --muted : voir .ligne-client--archive) : son texte
   atténué reste --muted, sinon « N archivés » et les tirets tombent à 4,42:1 en sombre et 4,21:1 en clair. */
.ligne-client--archive .hx-attenue { color: var(--muted); }
/* « Liste clients », posé sur le fond de page de la vue 360 : en clair, --accent-2 (5,4:1 au pire arrêt du dégradé). */
:root[data-theme="light"] .tc-lien-retour { color: var(--accent-2); }
/* [L11-clients-m365] fin */

/* ───────────────────────────────────────────────────────────── */

/* [L12-doc] début — Documentation des clients (DOC) : tableau de bord, coffre de mots de passe, documents,
   assets, checklists, schéma réseau (harmonisation, 09/10/2026).
   Ce que ces écrans gardent de propre une fois leurs styles en dur repris par le socle hx-*. Jetons et échelle
   d'espacement là où elle tombe juste ; placé après le socle : une classe d'ici COMPLÈTE le composant qu'elle
   accompagne. ⚠ La règle générale des <input> pèse (0,5,1) : les règles de saisie reprennent ses :not().
   Le commentaire au-dessus d'une règle générée dit où elle sert (fichier, ligne au moment de la reprise).
   Les blancs (#fff, #222…) de l'aperçu d'un document servent son CONTENU (papier blanc), pas l'interface. */

/* glue.js 16 324 451 661 1552 2806 2900 3148 */
.doc-span-1 { font-weight: normal; }
/* glue.js 26 */
.doc-a-1 { --w-accent: var(--color-ok); --w-accent-rgb: 34,197,94; text-decoration: none; color: inherit; display: block; }
/* glue.js 31 */
.doc-a-2 { --w-accent: var(--color-warn); --w-accent-rgb: 245,158,11; text-decoration: none; color: inherit; display: block; }
/* glue.js 36 */
.doc-a-3 { --w-accent: var(--color-crit); --w-accent-rgb: 220,38,38; text-decoration: none; color: inherit; display: block; }
/* glue.js 41 */
.doc-div-1 { --w-accent: #06b6d4; --w-accent-rgb: 6,182,212; display: block; }
/* glue.js 48 */
.doc-a-4 { --w-accent: var(--color-info); --w-accent-rgb: 59,130,246; text-decoration: none; color: inherit; display: block; }
/* glue.js 53 */
.doc-div-2 { --w-accent: #a855f7; --w-accent-rgb: 168,85,247; display: block; }
/* glue.js 58 */
.doc-div-3 { --w-accent: #facc15; --w-accent-rgb: 250,204,21; display: block; }
/* 26/10/2026 (revue DES5-02) : en thème clair, le chiffre d'« Avec TOTP » (cyan) et celui d'« Épinglés » (jaune) ne
   faisaient que 2,3:1 et 1,5:1 sur la tuile blanche (3:1 au moins pour un grand chiffre) : ils prennent la teinte
   foncée des jetons --ico-ciel et --ico-jaune (5,1:1 et 4,7:1). « Récents 7j » (violet, 3,8:1) reste tel quel. */
:root[data-theme="light"] .doc-div-1 { --w-accent: var(--ico-ciel); --w-accent-rgb: 14,116,144; }
:root[data-theme="light"] .doc-div-3 { --w-accent: var(--ico-jaune); --w-accent-rgb: 161,98,7; }
/* glue.js 63 */
.doc-div-4 { --w-accent: var(--state-neutral); --w-accent-rgb: 100,116,139; display: block; }
/* glue.js 229 2549 2610 */
.doc-ul-1 { list-style: none; padding: 0; }
/* glue.js 236 2618 */
.doc-a-5 { color: var(--text); text-decoration: none; }
/* glue.js 326 */
.doc-div-6 { border-left: 3px solid var(--accent); }
/* glue.js 328 */
input.doc-saisie-1:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]):not([type=file]), select.doc-saisie-1, textarea.doc-saisie-1 { min-width: 280px; }
@media (max-width: 600px) { input.doc-saisie-1:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]):not([type=file]), select.doc-saisie-1, textarea.doc-saisie-1 { min-width: 0; } }
/* glue.js 335 667 2415 */
input.doc-saisie-2:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]):not([type=file]), select.doc-saisie-2, textarea.doc-saisie-2 { min-width: 240px; }
@media (max-width: 600px) { input.doc-saisie-2:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]):not([type=file]), select.doc-saisie-2, textarea.doc-saisie-2 { min-width: 0; } }
/* glue.js 341 */
input.doc-saisie-3:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]):not([type=file]), select.doc-saisie-3, textarea.doc-saisie-3 { width: 200px; }
/* glue-network.js 105, glue.js 376 */
.doc-div-7 { padding: 0; overflow: hidden; }
/* glue.js 378 */
.doc-tr-1 { background: var(--surface-2); }
/* glue.js 379 */
.doc-th-1 { width: 50px; }
/* glue.js 383 875 */
.doc-pastille-3 { background: rgba(var(--accent-rgb),0.15); padding: 1px 7px; border-radius: 9px; margin-right: 3px; }
/* glue.js 384 */
.doc-tr-2 { cursor: pointer; }
/* glue.js 415 874 */
.doc-a-7 { text-decoration: none; }
/* glue.js 436 */
.doc-pastille-4 { display: inline-block; background: var(--border); padding: 2px 9px; border-radius: 9px; margin: 3px 5px 0 0; text-decoration: none; color: var(--text); }
/* glue.js 437 */
.doc-div-8 { line-height: 1.55; }
/* glue.js 438 */
.doc-div-9 { border-top: 1px solid var(--border); padding-top: var(--space-2); }
/* glue.js 470 */
.doc-pastille-5 { background: rgba(var(--accent-rgb),0.3); padding: 1px var(--space-2); border-radius: var(--radius); }
/* glue.js 506 */
.doc-pastille-6 { padding: 2px var(--space-2); background: rgba(var(--accent-rgb),0.2); border-radius: 8px; }
/* glue.js 558 */
.doc-fenetre-2 { max-width: 920px; width: 100%; max-height: 92vh; overflow: auto; }
/* glue.js 560 */
.doc-grille-1 { display: grid; grid-template-columns: 60px 60px 1fr; gap: 10px; }
@media (max-width: 600px) { .doc-grille-1 { grid-template-columns: minmax(0, 1fr); } }
/* glue.js 563 2376 */
input.doc-saisie-4:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]):not([type=file]), select.doc-saisie-4, textarea.doc-saisie-4 { text-align: center; }
/* glue.js 567 */
input.doc-saisie-5:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]):not([type=file]), select.doc-saisie-5, textarea.doc-saisie-5 { height: 36px; background: transparent; cursor: pointer; }
/* glue.js 578 3227 */
.doc-bouton-1 { margin-left: var(--space-2); }
/* glue.js 579 */
.doc-cadre-2 { border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 6px; background: var(--surface); }
/* glue.js 592 */
.doc-marge-1 { text-align: center; padding: 18px; }
/* glue.js 594 */
.doc-grille-2 { display: grid; grid-template-columns: auto 160px 1fr 1fr auto auto; gap: 6px; align-items: center; padding: 6px; border-bottom: 1px solid var(--border); }
@media (max-width: 600px) { .doc-grille-2 { grid-template-columns: minmax(0, 1fr); } }
/* glue.js 595 */
.doc-span-6 { cursor: grab; }
/* glue-network.js 677, glue.js 596 1180 1216 1332 1409 2437 */
input.doc-saisie-6:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]):not([type=file]), select.doc-saisie-6, textarea.doc-saisie-6 { font-family: monospace; }
/* glue.js 603 */
.doc-etendue-1 { grid-column: 1/7; }
/* glue.js 604 */
input.doc-saisie-7:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]):not([type=file]), select.doc-saisie-7, textarea.doc-saisie-7 { width: 160px; }
/* glue.js 605 */
input.doc-saisie-8:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]):not([type=file]), select.doc-saisie-8, textarea.doc-saisie-8 { width: calc(100% - 200px); margin-left: 6px; }
/* glue.js 859 */
.doc-tr-3 { text-align: left; border-bottom: 1px solid var(--border); }
/* glue.js 860 861 862 863 864 865 872 873 874 875 876 877 */
.doc-th-2 { padding: var(--space-2); }
/* glue.js 866 */
.doc-th-3 { padding: var(--space-2); text-align: right; }
/* glue.js 871 1600 */
.doc-tr-4 { border-bottom: 1px solid var(--border); }
/* glue.js 878 */
.doc-td-1 { padding: var(--space-2); text-align: right; white-space: nowrap; }
/* glue.js 881 */
.doc-media-1 { vertical-align: -2px; }
/* glue.js 976 */
.doc-fenetre-3 { max-width: 420px; width: 100%; }
/* glue.js 983 1222 */
.doc-div-10 { min-height: 18px; }
/* glue.js 1020 1030 */
input.doc-saisie-9:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]):not([type=file]), select.doc-saisie-9, textarea.doc-saisie-9 { font-family: monospace; background: var(--menu-bg); }
/* glue.js 1025 */
.doc-fenetre-4 { max-width: 520px; width: 90%; max-height: 80vh; overflow: auto; }
/* glue.js 1035 */
.doc-cadre-3 { padding: 14px; background: var(--menu-bg); border: 1px solid var(--border); border-radius: var(--radius-sm); }
/* glue.js 1037 */
.doc-div-12 { position: relative; width: 48px; height: 48px; flex-shrink: 0; }
/* glue.js 1038 */
.doc-media-2 { transform: rotate(-90deg); }
/* glue.js 1166 */
.doc-fenetre-5 { max-width: 680px; width: 100%; max-height: 90vh; overflow: auto; }
/* glue.js 1169 1210 1229 1233 */
.doc-etendue-2 { grid-column: 1/3; }
/* glue.js 1556 */
input.doc-saisie-10:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]):not([type=file]), select.doc-saisie-10, textarea.doc-saisie-10 { min-width: 260px; }
@media (max-width: 600px) { input.doc-saisie-10:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]):not([type=file]), select.doc-saisie-10, textarea.doc-saisie-10 { min-width: 0; } }
/* glue.js 1561 */
.doc-grille-3 { display: grid; grid-template-columns: 280px 1fr; gap: 18px; align-items: start; }
@media (max-width: 900px) { .doc-grille-3 { grid-template-columns: minmax(0, 1fr); } }
/* glue.js 1562 */
.doc-div-14 { min-height: 300px; padding: 10px var(--space-3); position: sticky; top: 14px; }
/* glue.js 1563 */
.doc-surtitre-2 { text-transform: uppercase; letter-spacing: 0.5px; }
/* glue.js 1566 */
.doc-div-15 { min-height: 500px; padding: var(--space-4); }
/* glue.js 1567 1856 */
.doc-marge-2 { text-align: center; padding: 40px 0; }
/* glue.js 1598 */
.doc-td-2 { padding: var(--space-6); text-align: center; }
/* glue.js 1601 */
.doc-td-3 { padding: var(--space-2) 6px; }
/* glue.js 1603 */
input.doc-saisie-11:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]):not([type=file]), select.doc-saisie-11, textarea.doc-saisie-11 { background: transparent; border: 1px solid transparent; }
/* glue.js 1608 */
.doc-td-4 { padding: var(--space-2) 6px; text-align: center; }
/* glue.js 1609 */
.doc-td-5 { padding: var(--space-2) 6px; text-align: right; white-space: nowrap; }
/* glue.js 1622 */
.doc-table-1 { width: 100%; border-collapse: collapse; }
/* glue.js 1623 */
.doc-tr-5 { border-bottom: 1px solid var(--border); text-transform: uppercase; letter-spacing: 0.5px; }
/* glue.js 1624 */
.doc-th-4 { padding: 6px; text-align: left; }
/* glue.js 1625 */
.doc-th-5 { padding: 6px; text-align: center; width: 90px; }
/* glue.js 1626 */
.doc-th-6 { padding: 6px; text-align: right; width: 200px; }
/* glue.js 1631 2590 */
.doc-div-16 { padding-top: 14px; border-top: 1px solid var(--border); }
/* glue.js 1639 */
.doc-cadre-4 { padding: 1px 5px; background: var(--surface-2); border: 1px solid var(--border); border-radius: 3px; }
/* glue.js 1696 1864 1869 */
.doc-label-1 { display: block; }
/* glue.js 1780 1782 2621 */
.doc-span-8 { flex-shrink: 0; }
/* glue.js 1781 2344 2604 2620 */
.doc-tronque-1 { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* glue.js 1849 2138 2413 */
.doc-div-17 { padding-bottom: 10px; border-bottom: 1px solid var(--border); }
/* glue.js 1939 */
.doc-cadre-5 { cursor: pointer; padding: var(--space-3); background: var(--surface-2); border: 1px solid var(--border); border-radius: 8px; min-height: 108px; transition: transform 0.08s, border-color 0.12s; position: relative; }
/* glue.js 1943 */
.doc-bouton-2 { position: absolute; top: 4px; right: 4px; background: none; border: 0; cursor: pointer; border-radius: 4px; line-height: 1; }
/* glue.js 1949 */
.doc-span-9 { width: 32px; height: 32px; flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center; line-height: 1; }
/* glue.js 1950 */
.doc-div-18 { min-width: 0; padding-right: var(--space-4); }
/* glue.js 1959 */
.doc-div-19 { margin-top: auto; }
/* glue.js 1960 */
.doc-pastille-8 { background: rgba(var(--accent-rgb),0.15); padding: 1px 6px; border-radius: 7px; }
/* glue.js 1967 */
.doc-grille-4 { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 10px; }
/* glue.js 2111 */
.doc-fenetre-7 { max-width: 100%; max-height: 600px; display: block; margin: 0 auto; border-radius: var(--radius-sm); cursor: zoom-in; }
/* glue.js 2116 */
.doc-cadre-6 { width: 100%; height: 720px; border: 1px solid var(--border); border-radius: var(--radius-sm); background: #fff; }
/* glue.js 2119 */
.doc-fenetre-8 { max-width: 100%; max-height: 600px; display: block; margin: 0 auto; border-radius: var(--radius-sm); }
/* glue.js 2122 */
.doc-media-4 { width: 100%; }
/* glue.js 2125 */
.doc-cadre-7 { width: 100%; height: 520px; border: 1px solid var(--border); border-radius: var(--radius-sm); background: #fff; }
/* glue.js 2128 */
.doc-cadre-8 { padding: 28px; background: var(--surface-2); border: 1px dashed var(--border); border-radius: var(--radius-sm); text-align: center; }
/* 25/10/2026 (revue R24-04) : .doc-pastille-9 retirée. Son seul emploi, le lien « Télécharger » de l'aperçu d'une
   pièce jointe Office (glue.js), porte hx-bouton hx-bouton--primaire ; placée après le socle, elle lui reprenait
   l'affichage (icône collée au libellé), le fond, le texte blanc (3,6:1 en sombre) et le rayon. */
/* glue.js 2141 */
.doc-tronque-2 { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* glue.js 2150 */
.doc-grille-5 { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: var(--space-2); padding: 10px var(--space-3); background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius-sm); }
/* glue.js 2154 */
.doc-pastille-10 { background: rgba(var(--accent-rgb),0.15); padding: 1px 6px; border-radius: 7px; margin-right: 3px; }
/* 26/10/2026 (revue M-c25-2) : .doc-cadre-9 et .doc-cadre-16 retirées. Placées après le socle, elles reprenaient aux
   deux liens « Télécharger » d'une pièce jointe (hx-bouton, glue.js) le rayon et le filet des autres boutons ; le bouton
   d'ajout de fichier qui portait la seconde est devenu « Joindre un fichier » (docBoutonFichier, sans elle). */
/* glue.js 2170 */
.doc-pastille-11 { cursor: pointer; padding: var(--space-1) 11px; background: rgba(var(--accent-rgb),0.15); border-radius: 5px; }
/* glue.js 2180 */
.doc-grille-6 { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: var(--space-2); }
/* glue.js 2182 */
.doc-cadre-10 { cursor: pointer; padding: 10px; background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius-sm); }
/* glue.js 2184 */
.doc-div-20 { min-width: 0; }
/* glue.js 2188 2605 */
.doc-bouton-3 { background: none; border: 0; color: var(--state-crit); cursor: pointer; }
/* glue.js 2196 */
.doc-cadre-11 { padding: 18px; background: var(--surface-2); border: 1px dashed var(--border); border-radius: var(--radius-sm); text-align: center; }
/* glue.js 2198 */
.doc-pastille-12 { cursor: pointer; display: inline-block; padding: 6px 14px; background: rgba(var(--accent-rgb),0.15); border-radius: 5px; }
/* glue.js 2208 */
.doc-cadre-12 { padding: 14px var(--space-4); background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius-sm); line-height: 1.55; }
/* glue.js 2337 */
.doc-media-5 { display: block; flex-shrink: 0; }
/* glue.js 2342 */
.doc-div-21 { padding-bottom: var(--space-2); border-bottom: 1px solid var(--border); }
/* glue.js 2348 */
.doc-cadre-13 { background: #fff; color: #222; padding: var(--space-6); min-height: 300px; max-height: 75vh; overflow: auto; border: 1px solid var(--border); border-radius: 5px; }
/* glue.js 2349 */
.doc-p-1 { text-align: center; color: #666; padding: 40px 0; }
/* glue.js 2375 */
.doc-p-2 { text-align: center; padding: var(--space-6); color: #a00; }
/* glue.js 2376 */
.doc-a-8 { color: #06c; }
/* glue.js 2423 */
.doc-cadre-14 { padding: 6px; background: var(--surface-2); border: 1px solid var(--border); border-radius: 6px 6px 0 0; }
/* glue.js 2428 2433 2438 2441 */
.doc-span-10 { border-left: 1px solid var(--border); margin: 0 var(--space-1); }
/* glue.js 2430 */
.doc-bouton-4 { font-style: italic; }
/* glue.js 2431 */
.doc-bouton-5 { text-decoration: underline; }
/* glue.js 2432 */
.doc-bouton-6 { text-decoration: line-through; }
/* glue.js 2445 */
.doc-cadre-15 { min-height: 300px; max-height: 500px; overflow-y: auto; padding: 14px var(--space-4); border: 1px solid var(--border); border-top: 0; background: var(--surface-2); color: var(--text); border-radius: 0 0 6px 6px; line-height: 1.6; }
/* glue.js 2448 */
.doc-grille-7 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 10px; }
@media (max-width: 600px) { .doc-grille-7 { grid-template-columns: minmax(0, 1fr); } }
/* glue.js 2467 */
.doc-etendue-3 { grid-column: 1/4; }
/* glue.js 2541 */
.doc-marge-3 { padding: 14px; }
/* glue.js 2550 */
.doc-li-2 { padding: 6px 0; border-top: 1px solid var(--border); }
/* glue.js 2593 */
.doc-pastille-13 { cursor: pointer; padding: 5px var(--space-3); background: rgba(var(--accent-rgb),0.15); border-radius: 5px; }
/* glue.js 2598 */
.doc-grille-8 { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: var(--space-2); }
/* glue.js 2599 */
.doc-cadre-17 { position: relative; background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius-sm); overflow: hidden; }
/* glue.js 2601 */
.doc-media-6 { display: block; width: 100%; height: 120px; object-fit: cover; cursor: zoom-in; }
/* glue.js 2603 */
.doc-marge-4 { padding: var(--space-1) 6px; }
/* glue.js 2617 */
.doc-cadre-18 { padding: 6px 10px; background: var(--surface-2); border-radius: 5px; }
/* glue.js 2766 3119 */
.doc-fenetre-9 { max-width: 680px; width: 100%; max-height: 80vh; overflow: auto; }
/* glue.js 2887 3165 */
.doc-marge-5 { text-align: center; padding: 30px; }
/* glue.js 2890 */
.doc-div-22 { justify-content: center; }
/* glue.js 2905 */
.doc-cadre-19 { padding: var(--space-2) 10px; background: var(--surface-2); border-radius: 5px; border: 1px solid var(--border); cursor: pointer; }
/* glue.js 2907 */
.doc-tronque-3 { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* glue.js 2970 */
.doc-label-2 { display: inline-flex; align-items: center; gap: 6px; padding: 6px 0; }
/* glue.js 3005 */
.doc-fenetre-10 { position: relative; max-width: 880px; width: 100%; max-height: 92vh; overflow: auto; }
/* glue.js 3006 */
.doc-bouton-7 { position: absolute; top: 10px; right: 10px; width: 32px; height: 32px; line-height: 1; background: transparent; border: 0; cursor: pointer; font-weight: 300; border-radius: var(--radius-sm); transition: background 0.12s, color 0.12s; z-index: 2; justify-content: center; }
/* glue.js 3013 */
.doc-div-23 { padding-right: 36px; }
/* glue.js 3015 */
.doc-span-11 { font-size: 1.7em; line-height: 1; }
/* glue.js 3027 */
.doc-cadre-20 { padding: 10px 14px; background: var(--surface); border-radius: var(--radius-sm); }
/* glue.js 3034 */
.doc-div-24 { align-items: flex-end; }
/* glue.js 3121 */
.doc-grille-9 { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px,1fr)); gap: 10px; }
/* glue.js 3177 */
.doc-marge-6 { text-align: right; }
/* glue.js 3201 */
.doc-fenetre-11 { max-width: 720px; width: 100%; max-height: 92vh; overflow: auto; }
/* glue.js 3209 */
.doc-grille-10 { display: grid; grid-template-columns: 2fr 1fr; gap: 10px; }
@media (max-width: 600px) { .doc-grille-10 { grid-template-columns: minmax(0, 1fr); } }
/* glue.js 3228 */
.doc-cadre-22 { background: var(--surface); border: 1px solid var(--border); border-radius: 5px; padding: var(--space-2); }
/* glue.js 3241 */
.doc-marge-7 { text-align: center; padding: 14px; }
/* glue.js 3243 */
.doc-grille-11 { display: grid; grid-template-columns: 30px 1fr 30px; gap: 6px; align-items: center; padding: 5px; border-bottom: 1px solid var(--border); }
/* glue.js 3248 */
.doc-etendue-4 { grid-column: 2/4; }
/* glue-network.js 119 */
.doc-bouton-8 { margin-right: 6px; }
/* glue-network.js 165 */
.doc-fenetre-12 { max-width: 480px; width: 100%; }
/* glue-network.js 255 */
input.doc-saisie-12:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]):not([type=file]), select.doc-saisie-12, textarea.doc-saisie-12 { background: transparent; border: 0; border-bottom: 1px dashed var(--border); }
/* glue-network.js 261 */
.doc-marge-8 { padding: 10px 14px; }
/* glue-network.js 276 */
.doc-grille-12 { display: grid; grid-template-columns: 1fr 330px; gap: var(--space-3); align-items: start; }
@media (max-width: 900px) { .doc-grille-12 { grid-template-columns: minmax(0, 1fr); } }
/* glue-network.js 277 */
.doc-div-25 { padding: 0; overflow: auto; max-height: 74vh; }
/* glue-network.js 280 */
.doc-div-26 { padding: 14px; max-height: 74vh; overflow: auto; }
/* glue-network.js 532 */
.doc-media-7 { cursor: move; }
/* glue-network.js 548 */
.doc-media-8 { display: block; min-width: 100%; }
/* glue-network.js 650 */
.doc-div-27 { padding: 9px; background: var(--surface); }
/* glue-network.js 652 */
input.doc-saisie-13:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]):not([type=file]), select.doc-saisie-13, textarea.doc-saisie-13 { width: 96px; }
/* glue-network.js 666 */
input.doc-saisie-14:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]):not([type=file]), select.doc-saisie-14, textarea.doc-saisie-14 { flex: 2; font-family: monospace; }

/* ── Formes reprises à la main (harmonisation L12) ── */

/* Couleur DONNÉE posée par le code (couleur d'un template, d'une catégorie, d'un type d'équipement réseau) :
   style="--hx-teinte:…" sur l'élément, la feuille l'applique. */
.doc-teinte { color: var(--hx-teinte, var(--accent)); }
.doc-liseret { border-left: 4px solid var(--hx-teinte, var(--accent)); }
.doc-liseret.is-inactif { border-left-style: dashed; background: var(--surface-2); }
button.doc-liseret { border-left-width: 3px; }
/* Choix d'un template d'asset : une fiche à gauche, pas un bouton centré. */
.hx-bouton.doc-choix-modele { display: block; height: auto; padding: var(--space-3); text-align: left; background: var(--surface-2); white-space: normal; }

/* Tableau de bord DOC : santé documentaire et ses points d'attention. */
.doc-sante-titre { min-width: 10rem; }
.doc-surtitre { text-transform: uppercase; letter-spacing: 0.04em; }
a.doc-badge-lien { text-decoration: none; }
a.doc-badge-lien:hover { text-decoration: underline; }
/* Liste « mots de passe expirant » : le widget prend le ton d'avertissement des widgets du tableau de bord. */
.doc-widget-warn { --w-accent: var(--color-warn); }
/* Activité récente : une ligne par entrée, filet entre deux. */
.doc-liste { list-style: none; padding: 0; }
.doc-liste__ligne { padding: var(--space-1) 0; border-bottom: 1px solid var(--border); }
.doc-a-droite { float: right; }
/* Nuage de tags : la taille (classe hx-t-*) suit la fréquence. */
a.doc-nuage { text-decoration: none; }
/* Documents sans équipement lié : liste qui défile dans la fenêtre. */
.doc-liste-defile { max-height: 55vh; overflow: auto; padding: 0; }
a.doc-ligne-lien { padding: var(--space-2) var(--space-3); border-bottom: 1px solid var(--border); color: var(--text); text-decoration: none; }
a.doc-ligne-lien:hover { background: var(--surface-2); }

/* Coffre : la ligne retrouvée par un lien direct, surlignée quelques secondes. */
tr.doc-surligne { background: var(--color-accent-soft-bg); transition: background 0.4s; }
/* Code TOTP : vert, rouge sous 5 secondes (is-urgent posé par le code). */
.doc-otp-code, .doc-otp-reste { color: var(--color-ok); }
.doc-otp-code { font-family: 'Cascadia Code', 'Consolas', 'Courier New', monospace; letter-spacing: 0.3em; text-align: center; }
.doc-otp-reste { position: absolute; inset: 0; justify-content: center; }
.doc-otp-anneau { stroke: var(--color-ok); transition: stroke-dashoffset 1s linear; }
.doc-otp-fond { stroke: var(--border); }
.doc-otp-code.is-urgent, .doc-otp-reste.is-urgent { color: var(--color-crit); }
.doc-otp-anneau.is-urgent { stroke: var(--color-crit); }
/* Un code, une commande d'éditeur « bloc code » : chasse fixe. */
.doc-chasse-fixe { font-family: 'Cascadia Code', 'Consolas', 'Courier New', monospace; }

/* Dossiers du coffre : le nom se modifie sur place, la saisie ne montre son cadre qu'au focus. */
input.doc-saisie-11:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]):not([type=file]):not(:focus) { background: transparent; border-color: transparent; }

/* Arborescence des documents : dossier actif sur fond d'accent, filet à la couleur de la catégorie. */
a.doc-dossier { padding: var(--space-2); margin-bottom: 2px; border-left: 3px solid transparent; border-radius: var(--radius-sm); color: var(--text); text-decoration: none; }
a.doc-dossier:hover { background: var(--surface-2); }
a.doc-dossier.is-actif { background: var(--color-accent-soft-bg); border-left-color: var(--hx-teinte, var(--accent)); }

/* Tuiles de documents : survol (is-survol posé par le code) ; bouton « fiche » discret jusqu'au survol. */
.gd-tile.is-survol { border-color: var(--accent); transform: translateY(-1px); }
.doc-bouton-2 { color: var(--muted); }
.doc-bouton-2:hover, .doc-bouton-2:focus-visible { background: var(--color-accent-soft-bg); color: var(--text); }
/* Fenêtre d'asset : la croix de fermeture passe au rouge au survol. */
.doc-bouton-7:hover, .doc-bouton-7:focus-visible { background: var(--surface-2); color: var(--color-crit); }

/* Checklists : un item fait est barré. */
input.doc-fait:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]):not([type=file]) { text-decoration: line-through; color: var(--muted); }

/* Schéma réseau : les cases DHCP d'un port ; cochée, la légende prend la couleur de son sens. */
.doc-case-port { cursor: pointer; color: var(--muted); }
.doc-case-port input { cursor: pointer; }
.doc-case-port--recoit.is-actif { color: var(--color-warn); }
.doc-case-port--sert.is-actif { color: var(--color-ok); }
/* Titre du schéma : saisie sur place, soulignée en pointillés. */
input.doc-saisie-titre:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]):not([type=file]) { background: transparent; border: 0; border-bottom: 1px dashed var(--border-strong); border-radius: 0; min-width: min(20rem, 100%); max-width: 52vw; }
@media (max-width: 600px) { input.doc-saisie-titre:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]):not([type=file]) { max-width: 100%; } }
/* Fenêtres du module : pleine largeur sur téléphone. Les classes nommées une à une (pas [class*="doc-fenetre-"]) :
   un sélecteur d'attribut sans classe vise n'importe quel élément pour l'outil de reprise (tools/visuel_migrer.py),
   qui refusait alors de passer un <table> à hx-tableau (« règle plus forte que la classe »). Mêmes éléments, même poids. */
@media (max-width: 600px) {
  .doc-fenetre-2, .doc-fenetre-3, .doc-fenetre-4, .doc-fenetre-5, .doc-fenetre-7, .doc-fenetre-8,
  .doc-fenetre-9, .doc-fenetre-10, .doc-fenetre-11, .doc-fenetre-12 { max-width: 100%; width: 100%; }

}
/* Vérification du 10/10/2026. Textes atténués des cartes (tuiles de documents, dossiers, configurations, fiche
   d'un document, pièces jointes) : --muted, comme avant la reprise ; en --dim, ils tombaient à 3,7-3,8:1 sur
   --surface-2 en sombre. Pastilles de tags et libellés d'accent sur fond teinté : --accent-2, le jeton du
   texte d'accent (en --accent : 2,8 à 3,4:1, contre 4 à 4,3:1 avant la reprise). */
.doc-cadre-5 .hx-attenue, .doc-cadre-11 .hx-attenue, .doc-cadre-19 .hx-attenue, .doc-grille-5 .hx-attenue,
a.doc-dossier .hx-attenue, #gd-att-zone .hx-attenue { color: var(--muted); }
.doc-pastille-3.hx-texte-accent, .doc-pastille-6.hx-texte-accent, .doc-pastille-8.hx-texte-accent,
.doc-pastille-10.hx-texte-accent, .doc-pastille-12.hx-texte-accent { color: var(--accent-2); }
/* L'éditeur de document (contenteditable) : anneau de focus de 2 px. */
#gd-editor:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
/* [L12-doc] fin */

/* ───────────────────────────────────────────────────────────── */

/* [L13-mises-a-jour] début — mises à jour Windows et Linux, logiciels tiers, policies, créneaux, exclusions (harmonisation, 09/10/2026)
   Ce que ces écrans ont de propre une fois leurs styles en dur repris par le socle. Jetons seuls, espacements de
   l'échelle ; placé après le socle : une classe d'ici COMPLÈTE le composant qu'elle accompagne. */

/* MAJ tiers : la liste des postes (deux tiers) et le classement des paquets (un tiers) ; l'un sous l'autre sous
   900 px. Forme « principale + à côté » : candidate au socle si un autre écran la demande. */
.maj-tiers-grille { display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); gap: var(--space-4); align-items: start; }
@media (max-width: 900px) { .maj-tiers-grille { grid-template-columns: minmax(0, 1fr); } }

/* Une case à cocher qui porte une fiche (catégorie de mise à jour, paquet obligatoire) : label.hx-case.hx-encart.
   ⚠ « label:has(> input[type=checkbox]) » pèse (0,1,2) : label.hx-case.maj-fiche (0,2,1) l'emporte pour caler la
   case en haut. Cochée, ou surchargée pour le client : le filet passe à l'accent. */
label.hx-case.maj-fiche { align-items: flex-start; }
.maj-fiche.is-actif { border-color: var(--accent); }
/* Une case désactivée (lecture seule du technicien) : la main ne promet plus le clic. */
label.hx-case.maj-choix:has(input:disabled) { cursor: not-allowed; }

/* Les jours d'un créneau d'installation manuelle : des cases serrées. */
label.hx-case.maj-jour { padding: var(--space-1) var(--space-2); }

/* Le nom d'une catégorie de logiciels tiers, écrit par l'API en minuscules (« pdf reader »). */
.maj-capitale { text-transform: capitalize; }

/* Fenêtre « Nouveau template » : la liste des paquets défile dans son cadre, bord à bord. */
.maj-liste-paquets { max-height: 21rem; overflow-y: auto; padding: 0; }

/* ⚠ La règle générale des <input> pèse (0,5,1) : d'où les :not() repris. Un motif de signature se saisit en chasse
   fixe ; un nombre d'heures tient dans 6rem. */
input.maj-saisie-code:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]):not([type=file]) {
  font-family: 'Cascadia Code', 'Consolas', 'Courier New', monospace;
}
input.maj-saisie-heures:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]):not([type=file]) { width: 6rem; }

/* Les boutons d'en-tête d'un onglet de mises à jour, l'un sous l'autre et calés à droite, avec leur légende ; à
   gauche sur un téléphone, où l'en-tête passe à la ligne. */
.maj-pile-fin { align-items: flex-end; text-align: right; }
@media (max-width: 600px) { .maj-pile-fin { align-items: flex-start; text-align: left; } }

/* Un lien posé sur le fond en dégradé de la page (sous-titre des Policies) ou dans la carte teintée des failles
   exploitées (onglet Mises à jour Windows) : --accent y descend sous 4,5:1 au pire du dégradé (4,41:1 en clair,
   4,14:1 en sombre) ; --accent-2, plus foncé en clair et plus clair en sombre, y tient. */
.pol-intro .hx-lien, .wu-kev .hx-lien { color: var(--accent-2); }

/* « + Nouvelle exclusion pour <client> » : avec un nom de client long, le libellé ne tient pas sur un téléphone. Il
   passe à la ligne, comme avant la reprise, au lieu de sortir de l'écran (le bouton du socle est en nowrap). */
.hx-bouton.pol-bouton-long { white-space: normal; text-align: left; }

/* Les paquets d'un template : des puces qui passent à la ligne. Le nom d'un paquet est une donnée, parfois longue ;
   un .hx-badge (nowrap, coupé à sa largeur) le rognait sur un téléphone. */
.maj-paquet {
  display: inline-flex; align-items: center; gap: var(--space-1); max-width: 100%; padding: 0 var(--space-2);
  border-radius: var(--radius-sm); background: color-mix(in srgb, var(--accent) 10%, transparent);
  font-size: var(--fs-xs); line-height: 1.6; overflow-wrap: anywhere;
}

/* Anneaux de déploiement, sous 900 px : la grille à cinq colonnes (près de 600 px de minimums) débordait de sa carte
   jusqu'à 820 px sur un téléphone, avant comme après la reprise. Le rang et le nom restent côte à côte ; réglages,
   effectif et boutons passent dessous, sur toute la largeur. */
@media (max-width: 900px) {
  .anneau { grid-template-columns: 2.5rem minmax(0, 1fr); }
  .anneau__reglages, .anneau__effectif, .anneau__actions { grid-column: 1 / -1; }
  .anneau__actions { flex-wrap: wrap; }
}
/* [L13-mises-a-jour] fin */

/* ───────────────────────────────────────────────────────────── */

/* [L14-deploiement] début */
/* Enrôler un agent (deploy.js) : formes propres à l'écran, en jetons. */
.dp-site-select { min-width: min(280px, 100%); }
.dp-grouplbl-sous { font-weight: 400; text-transform: none; letter-spacing: 0; }
.dp-colonne { min-width: min(240px, 100%); }
/* Un QR se lit sur fond blanc dans les deux thèmes : la case reste blanche. */
.dp-qr { flex: 0 0 190px; width: 190px; height: 190px; padding: var(--space-2); border-radius: var(--radius); background: #fff; color: #4b5563; font-size: var(--fs-sm); display: flex; align-items: center; justify-content: center; text-align: center; }
.dp-qr--petit { flex-basis: 160px; width: 160px; height: 160px; font-size: var(--fs-xs); }
.dp-qr--relance { cursor: pointer; }
.dp-qr svg { width: 100%; height: 100%; }
.dp-qr-ligne { padding-bottom: var(--space-6); border-bottom: 1px dashed var(--border); }
.dp-etapes, .dp-liste { margin: 0; padding-left: var(--space-5); line-height: 1.7; font-size: var(--fs-sm); }
.dp-mono { font-family: var(--font-mono, monospace); }
.dp-series { width: 100%; min-height: 78px; }
.dp-jeton-mdm { max-width: 360px; }
.dp-rd-hote { max-width: 240px; }
.dp-rd-cle { max-width: 430px; }
.dp-a-copier { user-select: all; white-space: pre-wrap; }
.dp-a-copier[data-taille="grand"] { font-size: var(--fs-base); }
.dp-separe { padding-top: var(--space-3); border-top: 1px dashed var(--border); }
.dp-parc { margin-top: var(--space-6); padding-top: var(--space-4); border-top: 1px solid var(--border-strong); }
.deploy-ss-grid { display: grid; grid-template-columns: 1fr 1fr 150px 170px auto; gap: var(--space-2); align-items: center; }
.dp-ss-ligne { padding: var(--space-1) 0; }
/* Vérification du 10/10/2026. Les boutons qui nomment le client ou le site (« Pousser la mise à jour au parc … »,
   « Régénérer le token de … », « Assigner à … ») passent à la ligne comme avant la reprise : sur une seule
   ligne, ils sortaient de l'écran de 7 à 391 px à 360 et 600 px. */
.hx-bouton.dp-bouton-client { white-space: normal; max-width: 100%; text-align: left; }
/* Assistance ponctuelle : le texte atténué de la carte, en --dim, tombait à 4,24:1 en clair ; --muted. */
.dp-hero .hx-note, .dp-hero .hx-attenue { color: var(--muted); }
/* Le choix d'un fichier de package prend l'anneau de focus de 2 px des commandes. */
.pk-choix-fichier:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* [L14-deploiement] fin */

/* ───────────────────────────────────────────────────────────── */

/* [L15-automatisations] début */
/* Automatisations : workflows.js (règles reprises du bloc <style> que le module écrivait dans <head>),
   automation.js (hub Crons / catalogue), automation_exclusions.js. En jetons. */
.wf-section-row td { background: var(--surface-2); border-top: 1px solid var(--border); border-bottom: 1px solid var(--border); padding: var(--space-2) var(--space-3); }
.wf-section-row:first-child td { border-top: none; }
.wf-section-title { font-weight: 700; font-size: var(--fs-sm); }
.wf-section-count { color: var(--muted); font-size: var(--fs-xs); margin-left: var(--space-3); font-weight: 500; }
.wf-canvas { position: relative; background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius); overflow: auto; height: 560px; }
/* Largeur et hauteur : NODE_W et NODE_H de workflows.js (210 x 92). */
.wf-node { position: absolute; width: 210px; min-height: 92px; box-sizing: border-box; background: var(--surface); border: 2px solid var(--border);
  /* Pas d'overflow:hidden : l'ancre de connexion déborde volontairement de 9 px sous la boîte. */
  border-radius: var(--radius); padding: var(--space-2) var(--space-3); cursor: grab; user-select: none; box-shadow: 0 2px 8px rgba(0,0,0,.18); }
.wf-node.wf-node--couleur { border-color: var(--hx-couleur, var(--border)); }
.wf-node.sel { border-color: var(--accent); box-shadow: 0 0 0 3px rgba(var(--accent-rgb),.25); }
.wf-node.wf-node--declencheur { left: 60px; top: 16px; border-color: var(--color-info); cursor: default; background: rgba(var(--accent-rgb),.12); }
.wf-node-titre { font-weight: 700; font-size: var(--fs-sm); }
.wf-node-ligne { font-size: var(--fs-xs); color: var(--muted); max-width: 215px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-top: 2px; }
.wf-node-ligne.is-accent { color: var(--text); }
.wf-anchor { position: absolute; width: 14px; height: 14px; border-radius: 50%; background: var(--accent); border: 2px solid var(--surface); cursor: crosshair; bottom: -9px; }
.wf-anchor--centre { left: 50%; margin-left: -7px; }
.wf-anchor--oui { left: 28%; background: var(--color-ok); }
.wf-anchor--non { left: 72%; background: var(--color-crit); }
.wf-svg-liens { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
.wf-lien { fill: none; stroke: var(--accent); stroke-width: 2.5; }
.wf-lien--entree { stroke: var(--color-info); }
.wf-lien--non { stroke: var(--color-crit); }
.wf-fleche { fill: var(--accent); }
.wf-pal { width: 180px; flex: none; }
.wf-pal button { display: block; width: 100%; text-align: left; margin-bottom: var(--space-1); }
.wf-props-panneau { width: 300px; flex: none; }
.wf-btn-action { border-left: 4px solid var(--hx-couleur, var(--border)); }
.wf-rte { border: 1px solid var(--border); border-radius: var(--radius-sm); overflow: hidden; background: var(--surface); }
.wf-rte-tb { display: flex; flex-wrap: wrap; gap: 3px; padding: var(--space-1); background: var(--surface-2); border-bottom: 1px solid var(--border); }
.wf-rte-tb button { padding: 3px var(--space-2); font-size: var(--fs-xs); background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-sm); cursor: pointer; color: var(--text); line-height: 1.3; }
.wf-rte-tb button:hover { border-color: var(--accent); }
.wf-rte-tb button.on { background: var(--accent); color: #fff; border-color: var(--accent); }
/* L'aperçu du corps est un COURRIEL : fond blanc et encre sombre dans les deux thèmes, comme chez le destinataire. */
.wf-rte-doc { min-height: 150px; max-height: 320px; overflow: auto; padding: var(--space-3) var(--space-4); background: #fff; color: #0f172a; font-size: var(--fs-sm); line-height: 1.55; }
.wf-rte-doc:focus { outline: none; }
.wf-rte-doc h2 { font-size: 17px; margin: 0 0 9px; color: #0f172a; font-weight: 700; }
.wf-rte-doc p { margin: 0 0 9px; }
.wf-rte-doc ul { margin: 0 0 9px; padding-left: 20px; }
.wf-rte-doc a { color: var(--accent); }
.wf-rte-doc table { border-collapse: collapse; margin: 8px 0; width: 100%; }
.wf-rte-doc td, .wf-rte-doc th { border: 1px solid #e2e8f0; padding: 5px 9px; text-align: left; }
.wf-rte-src { display: block; width: 100%; border: 0; border-radius: 0; resize: vertical; font-family: ui-monospace, Menlo, Consolas, monospace; font-size: var(--fs-xs); }
.wf-rte-doc[hidden], .wf-rte-src[hidden], .wf-masque[hidden] { display: none; }
.wf-vars { margin-top: var(--space-1); display: flex; flex-wrap: wrap; gap: var(--space-1); align-items: center; }
.wf-vars-h { width: 100%; font-size: var(--fs-xs); color: var(--muted); margin-bottom: 2px; }
.wf-vars button { padding: var(--space-1) var(--space-2); font-size: var(--fs-xs); cursor: pointer; border-radius: var(--radius-sm); display: inline-flex; flex-direction: column; align-items: flex-start; line-height: 1.2; background: rgba(var(--accent-rgb),.12); color: var(--accent-2); border: 1px solid rgba(var(--accent-rgb),.3); text-align: left; }
.wf-vars button:hover { background: rgba(var(--accent-rgb),.22); }
.wf-vars .wf-var-lbl { font-family: inherit; font-weight: 600; font-size: var(--fs-xs); }
.wf-vars .wf-var-slug { font-family: ui-monospace, Menlo, Consolas, monospace; font-size: var(--fs-xs); color: var(--muted); margin-top: 1px; }
.wf-andor { border: none; cursor: pointer; font-weight: 700; font-size: var(--fs-xs); letter-spacing: .05em; padding: 2px var(--space-3); border-radius: 999px; color: #fff; line-height: 1.7; box-shadow: 0 1px 3px rgba(0,0,0,.2); }
.wf-andor.is-and { background: var(--color-ok); }
.wf-andor.is-or { background: var(--color-warn); }
.wf-andor:hover { filter: brightness(1.08); }
.wf-andor-row { display: flex; align-items: center; gap: var(--space-2); margin: 1px 0 var(--space-1); }
.wf-andor-row::before, .wf-andor-row::after { content: ''; flex: 1; border-top: 1px dashed var(--border); }
.wf-andor-hint { font-size: var(--fs-xs); color: var(--muted); display: flex; align-items: center; gap: var(--space-2); margin-bottom: var(--space-1); }
.wf-sum-kw { color: #fff; font-weight: 700; font-size: var(--fs-xs); letter-spacing: .05em; padding: 2px var(--space-2); border-radius: var(--radius-sm); margin-right: var(--space-1); white-space: nowrap; background: var(--accent); }
.wf-sum-kw--quand { background: var(--color-info); }
.wf-sum-kw--si { background: var(--color-warn); }
.wf-sum-kw--alors { background: var(--color-ok); }
.wf-sum-list { list-style: none; margin: 0; padding: 0; }
.wf-sum-list li { display: flex; align-items: baseline; gap: var(--space-2); padding: var(--space-1) 0; }
.wf-sum-list li.is-repris { color: var(--muted); }
.wf-sum-dot { flex: 0 0 auto; width: 9px; height: 9px; border-radius: 50%; display: inline-block; align-self: center; background: var(--hx-couleur, var(--state-neutral)); }
.wf-sum-branch { font-size: var(--fs-xs); font-weight: 700; letter-spacing: .03em; padding: 1px var(--space-1); border-radius: 4px; color: #fff; }
.wf-sum-branch.is-yes { background: var(--color-ok); }
.wf-sum-branch.is-no { background: var(--color-crit); }
.wf-retrait { padding-left: calc(var(--hx-retrait, 0) * 18px); }
.wf-recipe-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(290px, 100%), 1fr)); gap: var(--space-3); }
.wf-recipe { border: 1px solid var(--border); border-radius: var(--radius); padding: var(--space-3); background: var(--surface); display: flex; flex-direction: column; }
.wf-recipe:hover { border-color: var(--accent); box-shadow: 0 2px 10px rgba(var(--accent-rgb),.15); }
.wf-recipe-flow { font-size: var(--fs-xs); background: var(--surface-2); border-radius: var(--radius-sm); padding: var(--space-2); line-height: 1.5; margin-top: auto; }
.wf-recipe-ico .ni { width: 26px; height: 26px; }
.wf-pleine-largeur { width: 100%; }
.wf-normal { font-weight: 400; }
.wf-id { margin-right: var(--space-1); }
.wf-nombre { width: 110px; }
.wf-commande { min-height: 260px; resize: vertical; font-family: Consolas, monospace; font-size: var(--fs-xs); white-space: pre; }
.wf-resume-corps { line-height: 1.6; }
.wf-encart--warn { border-color: var(--color-warn); }
.wf-encart--accent { border-color: var(--accent); }
.u-fpop.u-fpop--droite { left: auto; right: 0; }
.wf-op { flex: 0 0 150px; width: 150px; }
.wf-sous-groupe { border-style: dashed; border-color: var(--accent); background: rgba(var(--accent-rgb),.05); }
.wf-dry-etape { padding: var(--space-2) 0; border-top: 1px solid var(--border); }
.wf-dry-n { flex: 0 0 22px; text-align: right; }
.wf-pager { justify-content: center; }
.wf-col-contexte { max-width: 280px; }
.wf-col-erreur { max-width: 240px; }
.wf-col-sujet { max-width: 300px; }
.wf-liste { margin: 0; padding-left: var(--space-4); }
.wf-tpl-carte { max-width: 820px; }
.wf-resume { cursor: pointer; }
/* automation.js */
.auto-famille { padding: var(--space-2) var(--space-3) var(--space-1); background: var(--bg-2); font-weight: 700; font-size: var(--fs-sm); }
.auto-mono { font-family: monospace; }
.auto-runs-defile { max-height: 60vh; overflow: auto; }
.auto-sortie { margin: 0; max-height: 120px; font-size: var(--fs-xs); white-space: pre-wrap; word-break: break-all; }
.auto-ia { border-left: 2px solid var(--accent); }
.auto-ia-cause { color: var(--text); }
.auto-ia-etapes { margin: 0; padding-left: var(--space-4); }
.auto-col-22 { width: 22%; }
.auto-col-18 { width: 18%; }
/* automation_exclusions.js */
.axe-filtre { min-width: min(200px, 100%); }
.axe-filtre-source { min-width: min(170px, 100%); }
.axe-signature { font-family: monospace; max-width: 280px; }
.axe-titre { max-width: 300px; }
/* Vérification du 10/10/2026. Les boutons de variables passent en --accent-2 (ci-dessus) : en --accent, 4,45:1 sur
   leur fond teinté en clair. Les textes atténués des nœuds et des recettes reprennent --muted (en --dim : 3,7 à
   3,9:1 sur le fond des nœuds en sombre). Le canevas défilant prend l'anneau de focus de 2 px. Sous 600 px, le
   calque des liens garde la largeur que lui donne le code (sizeCanvas) : la règle mobile des svg le ramenait à
   la largeur visible du canevas, et les liens au-delà étaient coupés (déjà le cas avant la reprise). */
.wf-node .hx-attenue, .wf-recipe-flow .hx-attenue { color: var(--muted); }
.wf-canvas:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
@media (max-width: 600px) { svg.wf-svg-liens { max-width: none !important; } }
/* Le nœud sélectionné garde la bordure de son action (.wf-node.sel, posée après, la passait à --accent) : sur main,
   la couleur de l'action était écrite en ligne et l'emportait, la sélection ne se voyait qu'au halo. */
.wf-node.wf-node--couleur.sel { border-color: var(--hx-couleur, var(--accent)); }
/* [L15-automatisations] fin */

/* ───────────────────────────────────────────────────────────── */

/* [L16-parametres] début */
/* Réglages (users.js) : menu des groupes, sous-blocs des cartes, listes de cases, courbe HeXaIA. Jetons seuls. */
.cfg-menubar { position: relative; z-index: var(--z-dropdown); }
.cfg-menu-item { position: relative; }
.cfg-menu-trigger .ni--12 { opacity: .7; }
.cfg-menu-dropdown {
  position: absolute; top: 100%; left: 0; z-index: var(--z-dropdown);
  display: flex; flex-direction: column; gap: 2px;
  min-width: 16rem; margin-top: var(--space-1); padding: var(--space-1);
  border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
  background: var(--popover-bg); box-shadow: 0 10px 30px var(--overlay-bg);
}
.cfg-menu-dropdown[hidden] { display: none; }
.cfg-menu-dropdown .config-tab-btn {
  display: flex; align-items: center; gap: var(--space-2);
  padding: var(--space-2) var(--space-3); border: 0; border-radius: var(--radius-sm);
  background: none; color: var(--text); font: inherit; font-size: var(--fs-sm); text-align: left; cursor: pointer;
}
.cfg-menu-dropdown .config-tab-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.cfg-menu-dropdown .config-tab-btn .ni { flex: none; margin: 0; }
/* Sous-bloc séparé par un filet ; branche subordonnée (trait vertical) ; retrait sous une case à cocher. */
.l16-filet { padding-top: var(--space-3); border-top: 1px solid var(--border); }
.l16-branche { margin-left: var(--space-2); padding-left: var(--space-4); border-left: 2px solid var(--border); }
.l16-retrait { margin-left: var(--space-6); }
.l16-bas { align-items: flex-end; }
.l16-journal { max-height: 15rem; overflow: auto; }
.l16-liste-defile { max-height: 26rem; overflow: auto; }
/* Un client ou une fonction à cocher dans une liste. */
.l16-puce-client { padding: var(--space-1) var(--space-2); border-radius: var(--radius-sm); background: var(--surface-2); }
.l16-droit { display: flex; align-items: flex-start; padding: var(--space-2) 0; border-bottom: 1px solid var(--border); }
.l16-droit.hx-encart { padding: var(--space-2) var(--space-3); border: 1px solid var(--border); }
.l16-droit input { margin-top: var(--space-1); }
.l16-droit:has(input:disabled) { cursor: default; }
/* Fonction IA suspendue par son interrupteur parent : texte atténué, pas d'opacité. */
.ia-cascade.is-suspendu, .ia-cascade.is-suspendu strong { color: var(--dim); }
.is-dormant > label, .is-dormant { color: var(--dim); }
/* Formulaire d'ajout d'une console SentinelOne dédiée : quatre colonnes, une seule sous 600 px. */
.l16-s1-ov-grille { display: grid; grid-template-columns: 1fr 2fr 2fr auto; gap: var(--space-2); align-items: center; }
@media (max-width: 600px) { .l16-s1-ov-grille { grid-template-columns: 1fr; } }
/* Client d'une clé HeXaIA : un bouton qui se lit comme un lien souligné en pointillé. */
.iau-cli { padding: 0; border: 0; border-bottom: 1px dotted var(--border-strong); background: none; color: inherit; font: inherit; cursor: pointer; white-space: nowrap; }
.iau-cli:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Courbe d'utilisation : étiquettes de l'axe placées sur leur point (donnée : --hx-gauche). */
.l16-courbe-legendes { position: relative; height: 1.1em; }
.l16-courbe-etiquette { position: absolute; left: var(--hx-gauche, 0); transform: translateX(-50%); white-space: nowrap; }
.l16-courbe-etiquette--debut { left: 0; transform: none; }
.l16-courbe-etiquette--fin { right: 0; left: auto; transform: none; }
/* Jauge de capacité : la largeur est une donnée (--hx-largeur). */
.jauge-piste > .jauge-barre { width: var(--hx-largeur, 0); }
/* [L16-parametres] fin */

/* ───────────────────────────────────────────────────────────── */

/* [L17-compte] début */
/* Mon compte, 2FA, sessions IA, marque blanche, à propos, guide, extensions (harmonisation, 09/10/2026).
   Ce que ces écrans ont de propre une fois leurs styles en dur repris par le socle : rien de ce qu'une classe
   hx-* sait déjà faire. Jetons seuls, espacements de l'échelle 4 · 8 · 12 · 16 · 24 · 32 · 48. Placé APRÈS les
   classes du socle : à poids égal, une classe d'ici complète le composant qu'elle accompagne (hx-saisie +
   compte-code, hx-encart + marque-logo__cadre) ; aucune ne redit une propriété d'un utilitaire hx-mt-…, hx-t-… */

/* La précision d'un libellé de champ : « (c'est l'identifiant de connexion) ». */
.champ-precision { color: var(--dim); font-weight: 400; }

/* Fenêtre « Modifier l'utilisateur » : les clients cochables, dans un encart qui défile. */
.compte-retrait { margin-left: var(--space-6); }
.compte-clients { max-height: 38vh; overflow-y: auto; }
.compte-client { padding: var(--space-1) var(--space-2); border-radius: var(--radius-sm); background: var(--bg-2); }
/* Session IA : le résultat d'un outil se déplie. */
.compte-pli { cursor: pointer; }
/* Session IA d'un NAS (session 260) : les gestes proposés par la conclusion, des liens vers la fiche du NAS. */
.ai-nas-gestes-liste { display: grid; gap: var(--space-1); margin: 0; padding-left: var(--space-4); }
/* Mon compte : la signature se saisit en chasse fixe (ses variables {{…}} s'y lisent). */
.compte-code { font-family: 'Cascadia Code', 'Consolas', 'Courier New', monospace; }
/* 2FA : la clé et les codes se sélectionnent d'un clic ; le code à 6 chiffres se saisit en grand. Le QR est un
   SVG du serveur, qui porte son propre fond blanc et sa marge : aucune couleur ici. */
.compte-copiable { user-select: all; }
.compte-qr { flex: none; line-height: 0; }
/* ⚠ La règle générale des <input> pèse (0,5,1) : une classe seule ne la bat pas, d'où les :not() repris. */
input.compte-code-2fa:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]):not([type=file]) {
  font-family: 'Cascadia Code', 'Consolas', 'Courier New', monospace; font-size: var(--fs-lg);
  letter-spacing: .3em; text-align: center;
}
/* Une commande, des codes de récupération : le bloc de code passe à la ligne au lieu de défiler (rien à faire
   défiler sur un téléphone, et tout se lit d'un coup). */
.hx-bloc-code.compte-plie { white-space: pre-wrap; overflow-wrap: anywhere; }
/* Anneau de focus de 2 px là où la règle générale des champs l'éteint (champ couleur, champ fichier : hx-saisie
   les exclut) et sur un bloc qui défile, que le clavier atteint. */
.marque-couleur:focus-visible, .marque-fichier:focus-visible, .md-code:focus-visible, .bubble.tool pre:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px;
}

/* Marque blanche : le logo dans son cadre, les deux couleurs, l'icône du tray, l'aperçu. */
.marque-logo { flex: none; text-align: center; }
.marque-logo__cadre { display: flex; align-items: center; justify-content: center; min-width: 13rem; min-height: 6rem; }
.marque-logo__image { max-width: 13rem; max-height: 5rem; object-fit: contain; }
.marque-couleur { width: 4rem; height: 2rem; }
.marque-tray { width: 32px; height: 32px; object-fit: contain; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--bg-1); }
.marque-apercu { overflow: hidden; border: 1px solid var(--border); border-radius: var(--radius-sm); }
/* L'aperçu montre les couleurs CHOISIES par l'utilisateur : des données, que l'écran passe par --marque-chrome,
   --marque-accent et --marque-encre. Le bandeau est toujours écrit en blanc, comme dans les courriels envoyés
   (seule couleur littérale du bloc : celle du gabarit des courriels, que l'aperçu doit montrer telle quelle). */
.marque-apercu__bandeau { padding: var(--space-2) var(--space-4); background: var(--marque-chrome); color: #fff; font-size: var(--fs-sm); font-weight: 600; }
.marque-apercu__corps { padding: var(--space-3) var(--space-4); background: var(--bg-1); }
.marque-apercu__bouton { padding: var(--space-1) var(--space-4); border-radius: var(--radius-sm); background: var(--marque-accent); color: var(--marque-encre); font-size: var(--fs-sm); font-weight: 600; }
.marque-apercu__lien { color: var(--marque-accent); font-size: var(--fs-sm); font-weight: 600; }

/* Feuille de route : la part faite et la part en cours, empilées (deux parts : <progress> ne convient pas).
   --hx-part est une donnée (un pourcentage), posée par l'écran. */
.rm-barre { display: flex; height: var(--space-2); overflow: hidden; margin-bottom: var(--space-6); border-radius: var(--radius-sm); background: var(--border-strong); }
.rm-barre__fait, .rm-barre__cours { width: var(--hx-part, 0%); }
.rm-barre__fait { background: var(--color-ok); }
.rm-barre__cours { background: var(--color-warn); }

/* Rendu Markdown (journal des versions, feuille de route, guide) : suite de la famille md-* (.md-p, .md-quote,
   .md-code plus haut). .md-doc est la carte qui porte le texte. */
.md-doc { line-height: 1.65; }
.md-h1 { display: block; margin: var(--space-6) 0 var(--space-3); font-size: var(--fs-xl); }
.md-h2 { margin: var(--space-6) 0 var(--space-3); padding-bottom: var(--space-2); border-bottom: 1px solid var(--border); font-size: var(--fs-lg); }
.md-h3 { margin: var(--space-6) 0 var(--space-2); color: var(--accent); font-size: var(--fs-md); letter-spacing: .08em; text-transform: uppercase; }
.md-h4 { margin: var(--space-4) 0 var(--space-2); color: var(--text); }
.md-liste { margin: var(--space-2) 0 var(--space-4); padding-left: var(--space-6); }
.md-liste > li { margin: var(--space-1) 0; }
.md-taches { margin: var(--space-2) 0 var(--space-4); padding: 0; list-style: none; }
.md-tache { margin: var(--space-1) 0; }
.md-tache--faite, .md-tache--abandonnee { color: var(--dim); text-decoration: line-through; }
.md-brut { white-space: pre-wrap; color: var(--dim); font-size: var(--fs-sm); }
.hx-tableau.md-table td { vertical-align: top; white-space: normal; }

/* Guide : le sommaire à gauche (collant sous la barre du haut), le texte à droite ; l'un sous l'autre sous 800 px. */
.guide-page { display: grid; grid-template-columns: 17rem minmax(0, 1fr); gap: var(--space-6); align-items: start; }
.guide-nav { position: sticky; top: calc(var(--topbar-h) + var(--space-2)); display: flex; flex-direction: column; gap: var(--space-1); font-size: var(--fs-sm); }
.guide-lien { display: block; padding: var(--space-2) var(--space-3); border-radius: var(--radius-sm); color: var(--text); }
.guide-lien:hover, .guide-lien.is-actif { background: var(--bg-1); }
.guide-lien.is-actif { font-weight: 600; }
.guide-lien:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
@media (max-width: 800px) {
  .guide-page { grid-template-columns: minmax(0, 1fr); }
  .guide-nav { position: static; }
}

/* Extensions : le pied d'une fiche colle au bas de la carte (les fiches d'une rangée ont la même hauteur) ; une
   extension éteinte reste LISIBLE — c'est un catalogue, pas la liste de ce qu'on possède déjà : elle passe en
   retrait (fond --bg-1), sans opacité (à 72 %, ses petits textes tombaient à 3,5:1). */
.ext-fiche > .hx-carte__pied { margin-top: auto; }
.ext-fiche--eteinte { background: var(--bg-1); }

/* [L17-compte] fin */

/* ───────────────────────────────────────────────────────────── */

/* [L18-pages-autonomes] début */
/* Pages autonomes (harmonisation visuelle, lot L18, 09/10/2026) : portail client, enquête de satisfaction,
   prise en main à distance, fenêtre du coffre, retours de connexion (SSO, lien du portail, consentement
   Microsoft 365), enrôlement Android, écrans de connexion de la console. Elles chargent cette feuille avec
   .hx-page-autonome sur <body> : plus de bloc <style> ni de jetons à elles. Préfixe pa- (« page autonome »).
   Valeurs : jetons seuls. Le texte posé sur le chrome du client (--brand-navy) et la plaque glissée sous un
   logo que ce chrome ne porte pas sont MESURÉS par brand_encre() (includes/branding.php), qui ne rend que
   deux couleurs : la page pose la classe qui correspond, jamais une couleur en ligne. */

/* ── Communs ── */
.pa-sprite { display: none; }
/* Les deux seules encres que rend brand_encre() (blanc, marine du produit) : mêmes valeurs que la fonction. */
.pa-encre--blanche { --pa-encre: #FFFFFF; }
.pa-encre--marine { --pa-encre: #0B223A; }
.pa-plaque--blanche { --pa-plaque: #FFFFFF; }
.pa-plaque--marine { --pa-plaque: #0B223A; }
.pa-bloc { width: 100%; }
.pa-pied-lien { margin: var(--space-4) 0 0; text-align: center; }
.pa-preformate { white-space: pre-wrap; overflow-wrap: anywhere; }
.pa-ico-apres { margin: 0 0 0 var(--space-1); }
/* Un tableau trop large défile dans son cadre (.hx-defile). Sans rien de focalisable dedans (listes du portail),
   Chromium rend le cadre lui-même atteignable au clavier : il porte l'anneau de 2 px des autres éléments (la
   règle générale ne vise que [tabindex]), tracé en dedans pour ne pas être rogné par la carte. */
.hx-page-autonome .hx-defile:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
/* Séparateur « ou avec un compte local » : deux filets de part et d'autre du texte. */
.pa-ou { display: flex; align-items: center; gap: var(--space-3); margin-bottom: var(--space-2); }
.pa-ou::before, .pa-ou::after { content: ''; flex: 1; height: 1px; background: var(--border-strong); }
/* Écrans de connexion de la console (views.js) : la carte .login-box était translucide sur le fond de page en
   dégradé. Le texte atténué et les liens y tombaient sous 4,5:1 au pire arrêt du dégradé (4,48 et 4,26:1 en
   clair, 4,25:1 pour un lien en sombre) : la carte devient opaque, comme les cartes du socle. */
.login-box.pa-connexion-console { background: var(--bg-2); }
/* Le bouton du fournisseur SSO porte le libellé du réglage (« Se connecter avec Microsoft Entra ID » : 301 px de
   texte et d'icône). Sans retour à la ligne (.hx-bouton), à 360 px il poussait la carte hors de l'écran : il
   revient à la ligne quand il ne tient pas, et ses marges de 16 px le gardent sur une ligne dans la carte de
   420 px. Les autres boutons pleine largeur de ces écrans ont des libellés courts : rien n'y change. */
.pa-connexion-console .hx-bouton--grand.pa-bloc { padding-left: var(--space-4); padding-right: var(--space-4); white-space: normal; text-align: center; }
/* Code de vérification à deux facteurs : chiffres espacés, centrés (même poids que la règle des champs). */
input.pa-code-saisie.hx-saisie:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]):not([type=file]) {
  letter-spacing: .25em; text-align: center; font-size: var(--fs-lg);
}
/* Étapes numérotées (prise en main, enrôlement) : la liste ne porte plus de puces, le numéro est .hx-numero. */
.pa-etapes { list-style: none; margin: 0; padding: 0; }
.pa-etapes > li { line-height: 1.5; }

/* Sélecteur de langue des pages client (FR / EN), posé par leur mini-socle i18n. */
.pa-langues {
  position: fixed; right: var(--space-3); bottom: var(--space-3); z-index: var(--z-toast);
  display: flex; gap: var(--space-1); padding: var(--space-1);
  background: var(--bg-2); border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
}
.pa-langue {
  min-width: 36px; min-height: 28px; padding: var(--space-1) var(--space-2); border: 0; border-radius: var(--radius-sm);
  background: transparent; color: var(--dim); font: inherit; font-size: var(--fs-xs); font-weight: 700; cursor: pointer;
}
.pa-langue:hover { color: var(--text); }
.pa-langue:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.pa-langue.is-actif { background: var(--accent); color: var(--accent-on, var(--bg-2)); }

/* ── Portail client (public/portal/index.php) ── */
.pa-portail { min-height: 100vh; }
/* Le portail sur un fond UNI (celui qu'il avait) : le dégradé de la console, imposé au <body> du thème clair, ne
   laissait que 4,48:1 au texte atténué et 4,26:1 à un lien posés sur la page. */
.pa-portail > #root { min-height: 100vh; background: var(--bg-0); }
.pa-centre { display: flex; align-items: center; justify-content: center; min-height: 80vh; padding: var(--space-4); }
.pa-connexion { width: 100%; max-width: 420px; overflow: hidden; }
.pa-connexion__marque { padding: var(--space-6) var(--space-6); background: var(--brand-navy); text-align: center; }
.pa-connexion__logo { display: inline-block; height: 34px; width: auto; max-width: 100%; }
.pa-connexion__corps { padding: var(--space-6); }
.pa-entete {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3) var(--space-4); padding: var(--space-3) var(--space-6);
  background: var(--brand-navy); color: var(--pa-encre);
}
/* 22 px et non 32 : le mot-symbole a un rapport de 5:1 ; à hauteur égale il occuperait le double de largeur et
   écraserait le titre du bandeau. */
.pa-entete__logo { height: 22px; width: auto; }
.pa-entete__titre { flex: 1; min-width: 0; margin: 0; color: inherit; font-size: var(--fs-md); font-weight: 700; letter-spacing: 0; }
.pa-entete__compte { font-size: var(--fs-sm); }
.pa-entete__compte strong { color: inherit; }
.pa-entete .hx-bouton.pa-entete__bouton {
  background: transparent; color: inherit; border-color: color-mix(in srgb, var(--pa-encre) 45%, transparent);
}
.pa-entete .hx-bouton.pa-entete__bouton:hover { background: color-mix(in srgb, var(--pa-encre) 15%, transparent); border-color: var(--pa-encre); }
.pa-entete .hx-bouton.pa-entete__bouton:focus-visible { outline-color: var(--pa-encre); }
.pa-nav { margin: 0; padding: 0 var(--space-6); background: var(--bg-2); }
.pa-contenu { max-width: 1200px; margin: 0 auto; padding: var(--space-6); }
main.pa-contenu > * { animation: none; }
.pa-contrat__tete { padding: var(--space-3) var(--space-4); cursor: pointer; }
.pa-contrat__tete:hover { background: var(--surface-2); }
.pa-contrat__bascule { display: inline-flex; color: var(--dim); }
.pa-contrat__bascule .ni { margin: 0; transition: transform .14s ease; }
.pa-contrat__nom { min-width: 12rem; }
.pa-contrat__corps { padding: var(--space-3) var(--space-4); border-top: 1px solid var(--border); background: var(--surface-2); }
/* Fil d'un ticket : vos messages à droite, ceux du technicien à gauche, ceux du système centrés. */
.pa-msg { max-width: 78%; padding: var(--space-3); border-radius: var(--radius); }
/* 10 % et non 14 : l'en-tête atténué d'un message (.pa-msg__tete) y garde 4,8:1 (4,48:1 à 14 %). */
.pa-msg--moi { margin-left: auto; background: color-mix(in srgb, var(--accent) 10%, var(--bg-2)); }
.pa-msg--tech { margin-right: auto; background: var(--surface-2); border: 1px solid var(--border); }
.pa-msg--systeme { max-width: 100%; margin: 0 auto; color: var(--dim); font-style: italic; font-size: var(--fs-sm); text-align: center; }
.pa-msg--pleine { max-width: 100%; }
.pa-msg__tete { margin-bottom: var(--space-1); color: var(--dim); font-size: var(--fs-xs); }
.pa-msg__corps { white-space: pre-wrap; overflow-wrap: anywhere; }
/* L'assistant de premier niveau (nouvelle demande). */
.pa-conversation:empty { display: none; }
.pa-bulle { max-width: 85%; padding: var(--space-2) var(--space-3); border-radius: var(--radius); font-size: var(--fs-base); line-height: 1.5; overflow-wrap: anywhere; }
.pa-bulle--moi { align-self: flex-end; background: var(--accent); color: var(--accent-on, var(--bg-2)); }
.pa-bulle--assistant { align-self: flex-start; background: var(--surface-2); border: 1px solid var(--border); color: var(--text); }
.pa-bulle__sources { color: var(--dim); }
@media (max-width: 600px) {
  .pa-entete, .pa-nav { padding-left: var(--space-4); padding-right: var(--space-4); }
  .pa-contenu { padding: var(--space-4); }
  .pa-msg, .pa-bulle { max-width: 100%; }
}

/* ── Enquête de satisfaction (public/csat.php) ── */
.pa-csat { display: flex; align-items: center; justify-content: center; min-height: 100vh; padding: var(--space-6) var(--space-4); }
.pa-csat__carte {
  width: 100%; max-width: 640px; overflow: hidden;
  background: var(--bg-2); border: 1px solid var(--border); border-radius: var(--radius-lg);
}
.pa-csat__carte--message { max-width: 480px; }
/* Le bandeau porte la marque : le chrome du CLIENT et l'encre calculée sur ce chrome (--pa-encre). */
.pa-csat__bandeau { padding: var(--space-6); background: var(--brand-navy); color: var(--pa-encre); }
.pa-csat__bandeau .logo { display: block; width: auto; height: 32px; margin: 0 0 var(--space-3); }
.pa-csat__titre { margin: 0 0 var(--space-1); color: inherit; font-size: var(--fs-xl); font-weight: 700; letter-spacing: 0; }
.pa-csat__sous-titre { font-size: var(--fs-base); }
.pa-csat__marque { padding: var(--space-3) var(--space-6); background: var(--brand-navy); text-align: center; }
.pa-csat__marque .logo { display: inline-block; width: auto; height: 30px; vertical-align: middle; }
/* La plaque n'est posée QUE si le logo ne contraste pas avec le chrome (csat_logo_plaque). */
.pa-plaque { display: inline-block; margin: 0 0 var(--space-3); padding: var(--space-1) var(--space-2); border-radius: var(--radius); background: var(--pa-plaque); }
.pa-plaque .logo { margin: 0; }
.pa-csat__marque .pa-plaque { margin: 0; }
.pa-csat__corps { padding: var(--space-6); }
.pa-csat__invite { color: var(--dim); text-align: center; }
.pa-csat__commentaire { min-height: 100px; resize: vertical; }
.pa-csat__pied { padding: var(--space-3) var(--space-6); background: var(--bg-1); color: var(--dim); font-size: var(--fs-xs); text-align: center; }
.pa-csat__message .hx-etat__aide { color: var(--text); }
/* Les cinq notes : cibles tactiles d'au moins 44 px (page ouverte au téléphone depuis un courriel). */
.pa-notes { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--space-2); }
.pa-note {
  display: flex; flex-direction: column; align-items: center; gap: var(--space-1);
  min-width: 84px; min-height: 44px; padding: var(--space-3);
  background: var(--bg-1); border: 2px solid var(--border-strong); border-radius: var(--radius-lg);
  color: var(--text); font: inherit; cursor: pointer;
  transition: border-color .15s ease, transform .1s ease;
}
.pa-note:hover { transform: translateY(-2px); border-color: var(--pa-ton); }
.pa-note:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.pa-note__visage { width: 32px; height: 32px; margin: 0; opacity: 1; color: var(--pa-ton); }
.pa-note__libelle { color: var(--dim); font-size: var(--fs-xs); text-align: center; }
/* La couleur de chaque note : un ton du socle (classe), plus une couleur écrite dans la page. */
.pa-note--1 { --pa-ton: var(--color-crit); }
.pa-note--2 { --pa-ton: var(--color-warn); }
.pa-note--3 { --pa-ton: var(--color-warn); }
.pa-note--4 { --pa-ton: var(--color-ok); }
.pa-note--5 { --pa-ton: var(--color-ok); }
.pa-note.is-actif { border-color: var(--pa-ton); background: color-mix(in srgb, var(--pa-ton) 10%, var(--bg-2)); transform: translateY(-2px); }
.pa-note.is-actif .pa-note__libelle { color: color-mix(in srgb, var(--pa-ton) 70%, var(--text)); font-weight: 600; }
@media (max-width: 600px) {
  .pa-csat { padding: var(--space-4) var(--space-2); }
  .pa-csat__bandeau, .pa-csat__corps { padding: var(--space-4); }
  .pa-note { min-width: 0; flex: 1 1 30%; }
}
/* Au téléphone, deux notes par ligne : à trois, « Très insatisfait(e) » (94 px) ne tenait pas dans sa case (70 px
   utiles à 360 px) et passait sur deux lignes ; à deux, il en reste 123. Trois par ligne à partir de 481 px. */
@media (max-width: 480px) {
  .pa-note { flex-basis: 40%; }
}

/* ── Prise en main à distance (public/support/index.php) ── */
.pa-support { display: flex; align-items: center; justify-content: center; min-height: 100vh; padding: var(--space-6) var(--space-4); }
.pa-support__carte { width: 100%; max-width: 560px; overflow: hidden; }
.pa-support__marque { padding: var(--space-6); background: var(--brand-navy); text-align: center; }
/* Largeur ET hauteur plafonnées : un logo déposé n'a pas le rapport de celui du produit. Jamais sous 170 px de
   large : la signature du produit porte une ligne subordonnée qui cesse d'être lisible en dessous. */
.pa-support__logo { display: inline-block; max-width: 190px; max-height: 72px; width: auto; height: auto; }
.pa-support__corps { padding: 32px var(--space-6); }
.pa-support__titre { text-align: center; }
.pa-support__intro { color: var(--dim); text-align: center; }
.pa-support__meta, .pa-support__pied { text-align: center; }
@media (max-width: 600px) {
  .pa-support__corps { padding: var(--space-6) var(--space-4); }
}

/* ── Fenêtre du coffre (public/glue-passwords-popup.html) ── */
body.pa-coffre { display: flex; flex-direction: column; height: 100vh; overflow: hidden; }
.pa-coffre__entete { flex: none; padding: var(--space-2) var(--space-3); background: var(--bg-1); border-bottom: 1px solid var(--border-strong); }
.pa-coffre__entete > .ni { margin: 0; opacity: 1; }
.pa-coffre__titres { min-width: 0; }
/* La liste et le pied sur un fond UNI : sur le dégradé du <body>, le texte atténué et le lien « Actualiser »
   tombaient sous 4,5:1 en thème clair. */
main.pa-coffre__liste { flex: 1 1 auto; max-width: none; margin: 0; padding: var(--space-3); overflow-y: auto; background: var(--bg-0); }
main.pa-coffre__liste > * { animation: none; }
.pa-coffre__champ { min-width: 0; }
/* Au plus étroit, le socle laisse rétrécir tout enfant d'une .hx-grappe (min-width: 0) : « Afficher » et « Copier »
   perdaient leur dernière lettre à 360 px. Les boutons gardent leur taille. */
.pa-coffre__champ > .hx-bouton { flex: none; }
.pa-coffre__libelle { flex: 0 0 64px; color: var(--dim); font-size: var(--fs-xs); font-weight: 600; text-transform: uppercase; letter-spacing: .04em; }
/* La fenêtre s'ouvre à 420 px (synology.js). Plus étroite, le libellé passe au-dessus de sa ligne, où le champ et
   ses boutons tiennent ensemble : à côté de boutons qui ne rétrécissent plus, il ne resterait au champ du mot de
   passe qu'une quarantaine de pixels à 360 px. */
@media (max-width: 400px) {
  .pa-coffre__champ { flex-wrap: wrap; row-gap: var(--space-1); }
  .pa-coffre__libelle { flex-basis: 100%; }
  .pa-coffre__champ > input.pa-coffre__valeur { flex-basis: 0; }
}
input.pa-coffre__valeur.hx-saisie:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]):not([type=file]) {
  min-width: 0; font-family: 'Cascadia Code', 'Consolas', 'Courier New', monospace; font-size: var(--fs-xs);
}
input.pa-coffre__valeur--secret.hx-saisie:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]):not([type=file]) { color: color-mix(in srgb, var(--color-warn) 70%, var(--text)); }
input.pa-coffre__valeur--utilisateur.hx-saisie:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]):not([type=file]) { color: color-mix(in srgb, var(--color-info) 70%, var(--text)); }
.pa-coffre__decompte { text-align: right; }
.pa-coffre__decompte:empty { display: none; }
.pa-coffre__pied { flex: none; padding: var(--space-1) var(--space-3); border-top: 1px solid var(--border); background: var(--bg-1); }
.pa-coffre .toast { left: 50%; right: auto; bottom: var(--space-3); transform: translateX(-50%); }
.pa-coffre .toast.show { transform: translateX(-50%); }

/* ── Retours de connexion : lien du portail, SSO, consentement Microsoft 365 ── */
.pa-message { display: flex; align-items: center; justify-content: center; min-height: 100vh; padding: var(--space-6) var(--space-4); }
.pa-message__carte { width: 100%; max-width: 480px; overflow: hidden; }
.pa-message__marque { padding: var(--space-4) var(--space-6); background: var(--brand-navy); color: var(--pa-encre, inherit); font-weight: 600; text-align: center; }
.pa-message__logo { display: inline-block; max-height: 34px; max-width: 190px; width: auto; }
.pa-message__corps { padding: var(--space-6); }
.pa-message__etat { padding: 32px var(--space-6); }
.pa-message__texte { color: var(--text); }

/* ── Enrôlement Android (agent/android/enroll.php, amapi-callback.php) ── */
.pa-android { padding: var(--space-6) var(--space-4); }
.pa-android__colonne { max-width: 460px; margin: 0 auto; }
.pa-android__colonne--large { max-width: 560px; }
.pa-android__tete { align-items: center; text-align: center; }
.pa-android__logo { display: block; width: 54px; height: 54px; margin: 0 auto; }
/* Le logo HeXaControl d'enroll.php (19/10/2026 ; avant, le « F » de Foxchip) : les couleurs de
   public/assets/hexacontrol-icone.svg, les mêmes en thème clair et sombre. */
.pa-android__logo-fond { fill: var(--brand-navy); }
.pa-android__logo-montante { fill: var(--brand-x); }
.pa-android__logo-descendante { fill: #FFFFFF; }
.pa-android__secours > summary { cursor: pointer; }
.pa-android__jeton { display: inline-block; max-width: 100%; overflow-wrap: anywhere; word-break: break-all; }
input.pa-android__valeur.hx-saisie:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]):not([type=file]) {
  min-width: 0; font-family: 'Cascadia Code', 'Consolas', 'Courier New', monospace;
}
/* [L18-pages-autonomes] fin */

/* ───────────────────────────────────────────────────────────── */

/* [Atelier] début */
/* Atelier, bancs KVM (session 225, lot 2, 12/10/2026) : public/js/atelier.js -- la page d'un banc (écran du PC,
   barre de touches, frappe, session, alimentation, KVM, disque virtuel et images, journal) et la gestion des
   bancs. Le socle fait l'essentiel (hx-carte, hx-onglets, hx-bandeau, hx-def, hx-tableau, hx-jauge, hx-filtre) ;
   ces règles ne posent que ce qu'il n'a pas. Jetons seuls, espacements de l'échelle, clair et sombre par les
   jetons. Préfixe .atl-. */
.atl-page { display: flex; flex-direction: column; gap: var(--space-4); min-width: 0; }
/* Session 266 (14/10/2026) : la disposition de la page d'un banc — l'image et le volet de la mission, la bande et ses onglets, le
   journal — est dans public/css/atelier-banc.css ; la colonne de droite de la session 257 (sa grille, ses onglets collants, le
   rappel de la session) n'existe plus. */
.atl-ecran-carte { min-width: 0; }
/* L'écran du PC : le canvas suit la largeur, sa hauteur suit l'image (attributs width et height).
   Session 257 : sans dépasser la hauteur qui reste sous son bord haut (--atl-ecran-max, écrit par atlEcranAjuster, et les
   proportions de l'image --atl-ecran-ratio) : entier sans défiler, centré dans son cadre. */
.atl-ecran { position: relative; overflow: hidden; border-radius: var(--radius-sm); background: var(--bg-0); border: 1px solid var(--border); }
.atl-ecran__image { display: block; width: min(100%, calc(var(--atl-ecran-max, 100vh) * var(--atl-ecran-ratio, 1.7778))); height: auto; margin-inline: auto;
  cursor: crosshair; touch-action: manipulation; user-select: none; -webkit-user-select: none; }
.atl-ecran__image:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
/* Clavier direct : le cadre de l'écran le dit. */
.atl-ecran.is-clavier { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent); }
.atl-ecran__etat { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; gap: var(--space-2);
  padding: var(--space-4); text-align: center; background: var(--bg-1); color: var(--text); font-size: var(--fs-base); }
.atl-ecran__etat[hidden] { display: none; }
/* Le rond du clic : sa place est écrite par le code (géométrie). */
.atl-clic { position: absolute; width: 24px; height: 24px; margin: -12px 0 0 -12px; border: 2px solid var(--accent); border-radius: 50%; pointer-events: none; opacity: 0; }
.atl-clic.is-joue { animation: atl-clic 700ms ease-out; }
@keyframes atl-clic { from { transform: scale(0.5); opacity: 1; } to { transform: scale(1.6); opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .atl-clic.is-joue { animation: none; } }
/* Session 257 : le CLIC MAINTENU. Le repère du bouton tenu (sa place est écrite par le code), le cadre de l'écran à l'accent tant
   qu'un bouton est tenu ; avec « Maintenir », le doigt glisse le pointeur du PC au lieu de faire défiler la page. */
.atl-tenu { position: absolute; width: 32px; height: 32px; margin: -16px 0 0 -16px; border: 2px solid var(--accent); border-radius: 50%; pointer-events: none;
  background: color-mix(in srgb, var(--accent) 24%, transparent); box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 16%, transparent); }
.atl-tenu[hidden] { display: none; }
.atl-ecran.is-tenu { border-color: var(--accent); }
.atl-ecran.is-tenu .atl-ecran__image { cursor: grabbing; touch-action: none; }
/* 14/10/2026 : le VERROU DE CLIC. Armé (« Maintenir » cliqué, rien n'est encore pressé) : la main ouverte sur l'image, et le doigt
   y saisit au lieu de faire défiler la page. */
.atl-ecran.is-verrou .atl-ecran__image { cursor: grab; touch-action: none; }
/* La barre sous l'image : la souris (Maintenir clic gauche, Maintenir clic droit, Clic droit, Clavier direct) à gauche, les réglages
   de l'image (images par seconde, pleine taille, figer) à droite ; des pilules qui passent à la ligne, 44 px sous le doigt. */
.atl-barre { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-2) var(--space-4); }
.atl-souris, .atl-barre__image { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
@media (pointer: coarse) { .atl-barre .hx-filtre, .atl-barre .hx-bouton { min-height: 44px; } }
.atl-tenu-zone { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-2); }
.atl-tenu-zone[hidden] { display: none; }
/* 14/10/2026 : la ligne d'état du verrou et du bouton tenu — une consigne, donc le texte courant, pas l'atténué d'une note ; son
   compte à rebours en chiffres tabulaires (la ligne ne saute pas à chaque seconde). */
.atl-tenu-ligne { color: var(--text); }
.atl-tenu-rebours { font-variant-numeric: tabular-nums; }
@supports not (color: color-mix(in srgb, currentColor 50%, transparent)) { .atl-tenu { background: transparent; box-shadow: none; } }
/* La barre de touches : trois groupes qui passent à la ligne ; 44 px sous le doigt (tablette). */
.atl-touches { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-4); }
.atl-touches__groupe { display: flex; flex-wrap: wrap; gap: var(--space-1); }
.atl-touches .hx-bouton { min-width: 40px; justify-content: center; font-variant-numeric: tabular-nums; }
@media (pointer: coarse) { .atl-touches .hx-bouton { min-height: 44px; min-width: 44px; } }
.atl-frappe .hx-saisie { min-width: 12rem; font-family: 'Cascadia Code', 'Consolas', 'Courier New', monospace; }
/* Les envois d'images en cours. */
.atl-envois { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.atl-envois .hx-jauge { width: 100%; }
/* Les images du KVM : une liste (la carte est étroite), un filet entre deux. */
.atl-images { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.atl-images > li { display: flex; flex-direction: column; gap: var(--space-1); padding: var(--space-2) 0; border-top: 1px solid var(--border); min-width: 0; }
.atl-images > li:first-child { border-top: 0; padding-top: 0; }
.atl-images .hx-tronque { min-width: 0; }
/* Le journal : un pli, ouvert à la demande. */
.atl-journal > summary { cursor: pointer; }
/* La recherche d'une fiche, dans « Ouvrir une session ». */
.atl-choix__liste { list-style: none; margin: var(--space-2) 0 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-1); max-height: 14rem; overflow-y: auto; }
.atl-choix__item { width: 100%; justify-content: flex-start; text-align: left; }
.atl-choix__item .hx-tronque { min-width: 0; }
/* [Atelier] fin */

/* ───────────────────────────────────────────────────────────── */

/* ── Applications mobiles (session 223, 11/10/2026) : public/js/mdm-applications.js, public/mdm-play.html ──
   MDM Mobile, onglet « Applications » : catalogue, affectations, état par appareil, panneau d'un appareil,
   carte « Applications » de la fiche d'un téléphone, page Google Play géré. Le socle fait l'essentiel
   (hx-carte, hx-tableau, hx-badge, hx-bandeau, hx-etat) ; ces règles ne posent que ce qu'il n'a pas.
   Jetons seuls, espacements de l'échelle, clair et sombre par les jetons. */
.mapp-onglets { margin-top: var(--space-3); }
/* Bandeaux et suivi des travaux : une pile, absente quand elle est vide. */
.mapp-bandeaux, .mapp-suivi { display: flex; flex-direction: column; gap: var(--space-2); margin-bottom: var(--space-4); }
.mapp-bandeaux:empty, .mapp-suivi:empty { display: none; }
/* Une application : icône de 24 px, titre, nom de paquet. */
.mapp-appli { display: flex; align-items: center; gap: var(--space-2); min-width: 0; }
.mapp-appli__texte, .mapp-pile-serree { display: flex; flex-direction: column; gap: 0; min-width: 0; }
.mapp-icone { flex: none; width: 24px; height: 24px; margin: 0; border-radius: var(--radius-sm); object-fit: contain; }
.mapp-icone--vide { color: var(--dim); opacity: 1; }
/* Nom de paquet, identifiant de règle : chasse fixe, casse gardée, coupé plutôt que de déborder. */
.mapp-ident { color: var(--dim); font-family: 'Cascadia Code', 'Consolas', 'Courier New', monospace; font-size: var(--fs-xs); overflow-wrap: anywhere; }
/* « 3 installée(s) · 1 en attente · 0 échec(s) » : chiffres tabulaires, sur une ligne. */
.mapp-compteurs { font-variant-numeric: tabular-nums; white-space: nowrap; }
/* L'application désignée par ?app= dans le catalogue. */
.hx-tableau tbody tr.mapp-choisie > td { background: color-mix(in srgb, var(--accent) 10%, transparent); }
/* Plis (« Autres applications signalées », profil personnel, configuration gérée). */
.mapp-replie > summary { color: var(--text); font-size: var(--fs-sm); font-weight: 600; cursor: pointer; }
.mapp-json { font-family: 'Cascadia Code', 'Consolas', 'Courier New', monospace; font-size: var(--fs-xs); }
/* Carte « Applications » de la fiche : une ligne par application — nom et paquet, état, version. Sous
   600 px, la version passe sous le nom. */
.mapp-fiche__liste { display: grid; gap: var(--space-2); margin: 0; padding: 0; list-style: none; }
.mapp-fiche__ligne { display: grid; grid-template-columns: minmax(0, 1fr) auto auto; align-items: center; gap: var(--space-2) var(--space-3); padding-bottom: var(--space-2); border-bottom: 1px solid var(--border); }
.mapp-fiche__ligne:last-child { padding-bottom: 0; border-bottom: 0; }
.mapp-fiche__nom { display: flex; flex-direction: column; min-width: 0; }
@media (max-width: 600px) { .mapp-fiche__ligne { grid-template-columns: minmax(0, 1fr) auto; } }
/* Page Google Play géré (mdm-play.html) : le cadre de Google prend toute la largeur et 80 % de la hauteur,
   sans attribut style (la page n'admet aucun style ni script en ligne). */
.mapp-play__page { max-width: 1280px; }
/* Le champ et son bouton, alignés par le bas (le libellé du champ est au-dessus). */
.mapp-play__ligne { align-items: flex-end; }
.mapp-cadre { min-height: 160px; }
.mapp-cadre iframe { display: block; width: 100%; height: 80vh; border: 0; border-radius: var(--radius-sm); background: var(--bg-2); }
/* [Applications mobiles] fin */

/* ───────────────────────────────────────────────────────────── */

/* ── Antivirus particuliers et options de l'agent (session 224, lot D, 11/10/2026) ──────────────────────────
   public/js/antivirus.js : Sécurité › Antivirus particuliers, Paramètres › Antivirus, badge de la fiche d'un
   poste. public/js/agent_options.js : panneau « Options de l'agent » de la Vue 360° d'un client, note de la
   fiche d'un poste. Le socle fait l'essentiel (hx-carte, hx-tableau, hx-badge, hx-bandeau, hx-champ, hx-def,
   hx-jauge, np-compteurs, bascule .syno-inter) ; ces règles ne posent que ce qu'il n'a pas. Jetons seuls,
   espacements de l'échelle (4, 8, 12, 16, 24), clair et sombre par les jetons. Préfixes .avp- et .aopt-. */

/* La page : tête, pile des bandeaux et des blocs, entête de la liste. */
.avp-tete { margin-bottom: var(--space-2); }
.avp-titre { margin: 0; }
.avp-sous-titre { margin: var(--space-1) 0 0; }
.avp-zone { display: flex; flex-direction: column; gap: var(--space-3); min-width: 0; margin-top: var(--space-3); }
.avp-zone > * { margin-top: 0; margin-bottom: 0; }
/* Les modes, toujours lus en tête : « Retrait de SentinelOne [Simulation] · Rappels sur les PC [Simulation] ». */
.avp-modes .hx-bandeau__texte { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-4); }
.avp-mode { display: inline-flex; align-items: center; gap: var(--space-2); white-space: nowrap; }
.avp-mode-note { flex-basis: 100%; }
.avp-entete { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-2) var(--space-3); }
.avp-resume { margin: 0; color: var(--dim); font-size: var(--fs-sm); font-variant-numeric: tabular-nums; }
.avp-entete-actions { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.avp-entete-actions .hx-bouton { white-space: nowrap; }
.avp-lecture { margin: 0; }

/* Le quota du site SentinelOne : sièges pris sur le total, jauge, marge. */
.avp-quota { display: flex; flex-direction: column; gap: var(--space-2); max-width: 640px; }
.avp-quota-titre { margin: 0; }
.avp-quota-nom { color: var(--dim); font-weight: 400; }
.avp-quota-ligne { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); margin: 0; }
.avp-quota-chiffre { font-weight: 600; font-variant-numeric: tabular-nums; white-space: nowrap; }
/* Licences à la demande (session 228) : les licences annoncées par la console, information secondaire. */
.avp-quota-annonce { color: var(--dim); font-weight: 400; }
.avp-quota-jauge { width: 100%; }
.avp-quota-erreur { color: var(--text); }

/* Les tableaux : dans leur cadre, qui défile seul (jamais la page). Le cadre contient aussi le libellé
   caché de la colonne des gestes (.hx-lecteur-seul est en position absolue). */
.avp-cadre { position: relative; border: 1px solid var(--border); border-radius: var(--radius-sm); }
.avp-table { min-width: 760px; }
.avp-table-pc { min-width: 560px; }
.avp-table td { vertical-align: top; }
.avp-col-titulaire { min-width: 160px; }
.avp-col-pc { min-width: 140px; }
.avp-sous { display: block; margin-top: var(--space-1); color: var(--dim); font-size: var(--fs-xs); }
.avp-nom { font-weight: 600; overflow-wrap: anywhere; }
.avp-efface { color: var(--dim); font-style: italic; }
.avp-lien-pc { overflow-wrap: anywhere; }
.avp-num { font-variant-numeric: tabular-nums; white-space: nowrap; }
.avp-badges { display: flex; flex-wrap: wrap; gap: var(--space-1); margin-top: var(--space-1); }
.avp-rappel { display: block; font-size: var(--fs-xs); font-variant-numeric: tabular-nums; white-space: nowrap; }
/* Les gestes d'une ligne, empilés : le tableau tient dans son cadre à 1280 px. */
.avp-gestes { display: inline-flex; flex-direction: column; align-items: stretch; gap: var(--space-1); }
.avp-gestes .hx-bouton { white-space: nowrap; }
.avp-vide { padding: var(--space-3) 0; }
.avp-section { display: flex; flex-direction: column; gap: var(--space-2); }
.avp-section-titre { margin: var(--space-3) 0 0; font-size: var(--fs-lg); }

/* Les fenêtres : détail d'un abonnement, formulaires, confirmation. */
.avp-fenetre { display: flex; flex-direction: column; gap: var(--space-3); min-width: 0; }
.avp-f-titre { margin: 0; overflow-wrap: anywhere; }
.avp-f-etat { display: flex; flex-wrap: wrap; gap: var(--space-1); }
.avp-gestes-detail { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.avp-gestes-detail .hx-bouton { white-space: nowrap; }
.avp-f-sous-titre { margin: var(--space-2) 0 0; font-size: var(--fs-md); }
.avp-journal-detail { overflow-wrap: anywhere; }
.avp-form { display: flex; flex-direction: column; gap: var(--space-3); min-width: 0; }
.avp-annonce { display: grid; gap: var(--space-1); margin: 0; padding-left: var(--space-4); color: var(--text); font-size: var(--fs-sm); }

/* Le badge de la fiche d'un poste : un lien vers son abonnement. */
.avp-badge-poste { text-decoration: none; }
.avp-badge-poste:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 999px; }
/* 12/10/2026 (session 227) — « Installé le » et « Fin » de l'antivirus sur la fiche (avpDatesPosteHtml) : sous la ligne
   « Antivirus / EDR » de la vue 360, alignés sur son libellé (point de 8 px + écart de 8 px). */
.avp-dates { margin: var(--space-1) 0 var(--space-2); }
.fp-ligne-etat + .avp-dates { padding-left: var(--space-4); }

/* Paramètres › Antivirus : des sections séparées d'un filet, des champs en grille souple, l'aperçu des textes. */
.avp-r-intro { margin: 0 0 var(--space-3); }
.avp-r-section { display: flex; flex-direction: column; gap: var(--space-2); min-width: 0; padding: var(--space-4) 0; border-top: 1px solid var(--border); }
.avp-r-titre { margin: 0; font-size: var(--fs-md); }
.avp-r-ligne { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
.avp-r-ligne .hx-saisie { max-width: 100%; }
.avp-r-etat { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); margin: 0; font-size: var(--fs-sm); overflow-wrap: anywhere; }
.avp-r-grille { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr)); gap: var(--space-3) var(--space-4); }
.avp-r-texte { width: 100%; resize: vertical; }
.avp-r-bloc { display: flex; flex-direction: column; gap: var(--space-2); min-width: 0; }
.avp-r-bloc + .avp-r-bloc { margin-top: var(--space-3); }
.avp-r-apercu-titre { margin: 0; }
.avp-r-apercu { margin: 0; color: var(--text); font-size: var(--fs-sm); white-space: pre-wrap; overflow-wrap: anywhere; }
.avp-r-resume { margin: 0 auto 0 0; color: var(--dim); font-size: var(--fs-sm); }
.avp-r-pied .hx-bouton { white-space: nowrap; }

/* Options de l'agent (Vue 360° d'un client) : des groupes en grille souple, une ligne par option — la bascule
   (ou son état, en lecture seule), le libellé, l'explication, la dernière modification. Une option changée et
   pas encore enregistrée porte un liseré d'accent. */
.aopt { display: flex; flex-direction: column; gap: var(--space-3); }
.aopt-tete { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-2); }
.aopt-titre, .aopt-intro, .aopt-lecture { margin: 0; }
.aopt-groupes { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 320px), 1fr)); gap: var(--space-3) var(--space-4); }
.aopt-groupe { min-width: 0; margin: 0; padding: var(--space-3); border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--bg-1); }
.aopt-groupe-titre { padding: 0 var(--space-1); color: var(--dim); font-size: var(--fs-xs); font-weight: 600; letter-spacing: .04em; text-transform: uppercase; white-space: nowrap; }
.aopt-liste { display: flex; flex-direction: column; gap: var(--space-3); margin: 0; padding: 0; list-style: none; }
.aopt-option { display: flex; align-items: flex-start; gap: var(--space-3); min-width: 0; }
.aopt-option.is-change { padding-left: var(--space-2); box-shadow: inset 3px 0 0 var(--accent); }
.aopt-inter { flex: none; min-width: 104px; }
.aopt-etat { flex: none; min-width: 64px; justify-content: center; }
.aopt-texte { display: flex; flex-direction: column; gap: var(--space-1); min-width: 0; }
.aopt-libelle { color: var(--text); font-size: var(--fs-sm); font-weight: 600; }
.aopt-option.is-coupee .aopt-libelle { color: color-mix(in srgb, var(--color-warn) 70%, var(--text)); }
.aopt-aide { color: var(--dim); font-size: var(--fs-xs); line-height: 1.5; }
.aopt-modif { color: var(--dim); font-size: var(--fs-xs); font-variant-numeric: tabular-nums; }
.aopt-resume { margin: 0 auto 0 0; color: var(--dim); font-size: var(--fs-sm); }
.aopt-pied .hx-bouton { white-space: nowrap; }
/* La note de la fiche d'un poste : « Options de l'agent coupées pour ce client : … · Voir les options ». */
.aopt-note-fiche { margin: 0 0 var(--space-3); }
/* [Antivirus particuliers et options de l'agent] fin */
/* [Profils d'agent] début */
/* Session 234, lot E (12/10/2026) : les profils d'agent (Standard, Lite…) — public/js/agent_profils.js, et le profil
   du client dans le panneau « Options de l'agent » (agent_options.js). Le badge est la pilule du socle (.hx-badge) ;
   son ton est la TEINTE du profil (data-teinte → --teinte, jetons --ico-* clair et sombre). Lite : magenta, la
   sarcelle se confondait avec le vert « OK » du thème clair. Les teintes permises tiennent un écart CIEDE2000 d'au
   moins 15 contre ok, warn, crit, info, dim et accent dans les deux thèmes (tests/bench_agent_profils_rendu.js).
   Seulement ce que le socle ne sait pas faire ; jetons et échelle d'espacement (4, 8, 12, 16, 24) seuls. */
.aprof-badge[data-teinte], .aprof-point[data-teinte] { --hx-ton: var(--teinte, var(--dim)); }
.aprof-force { font-weight: 400; }
.aprof-fiche-badge { margin-left: var(--space-2); }
/* Les fenêtres : choix d'un profil (poste, sélection de la liste), simulation, postes écartés. ⚠ La règle générale
   des label qui contiennent un bouton radio (label:has(> input[type="radio"]), en tête de feuille) pèse (0,1,2) et
   pose display, taille et couleur en !important : ici (0,2,1), seulement l'alignement et la marge. */
.aprof-choix { display: flex; flex-direction: column; gap: var(--space-2); min-width: 0; margin: 0; padding: 0; border: 0; }
.aprof-choix > legend { margin: 0 0 var(--space-2); padding: 0; }
.aprof-choix label.aprof-choix-ligne {
  align-items: flex-start; gap: var(--space-3); margin: 0; padding: var(--space-2) var(--space-3);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
}
.aprof-choix label.aprof-choix-ligne:hover { border-color: var(--border-strong); }
.aprof-choix label.aprof-choix-ligne:has(input:checked) { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 8%, transparent); }
.aprof-choix-ligne input { flex: none; margin: var(--space-1) 0 0; accent-color: var(--accent); }
.aprof-choix-ligne input:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.aprof-choix-texte { display: flex; flex-direction: column; gap: var(--space-1); min-width: 0; }
.aprof-choix-nom { display: inline-flex; align-items: center; gap: var(--space-2); color: var(--text); font-weight: 600; white-space: nowrap; }
.aprof-choix-aide { color: var(--dim); font-size: var(--fs-xs); line-height: 1.5; }
.aprof-dlg-poste, .aprof-dlg-actuel { margin: 0; }
.aprof-dlg-actuel { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
.aprof-sim { display: flex; flex-direction: column; gap: var(--space-3); padding: var(--space-3); border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--bg-1); }
.aprof-sim:empty { display: none; }
.aprof-sim > .hx-note, .aprof-sim > .hx-bandeau { margin: 0; }
.aprof-sim-titre { display: flex; align-items: center; gap: var(--space-2); margin: 0 0 var(--space-1); color: var(--text); font-size: var(--fs-sm); font-weight: 600; }
.aprof-sim-titre .ni { margin: 0; }
.aprof-sim-liste, .aprof-refus { margin: 0; padding-left: var(--space-6); color: var(--text); font-size: var(--fs-sm); line-height: 1.6; }
/* Paramètres › Profils d'agent : la matrice options × profils, dans son cadre .hx-defile (elle défile, la page jamais). */
.aprof-reglages { display: flex; flex-direction: column; gap: var(--space-3); }
.aprof-tete { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-2); }
.aprof-tete .hx-titre, .aprof-lecture { margin: 0; }
.aprof-matrice { min-width: 30rem; }
.hx-tableau.aprof-matrice thead th.aprof-col-profil { vertical-align: bottom; text-align: center; text-transform: none; letter-spacing: normal; }
.hx-tableau.aprof-matrice tbody th { font-weight: 400; }
.aprof-matrice .aprof-col-option { min-width: 14rem; }
.aprof-opt-libelle { display: block; color: var(--text); font-size: var(--fs-sm); font-weight: 600; }
.aprof-opt-aide { display: block; color: var(--dim); font-size: var(--fs-xs); line-height: 1.5; }
.aprof-cellule { text-align: center; }
.aprof-entete { display: inline-flex; flex-direction: column; align-items: center; gap: var(--space-1); }
.aprof-entete-nom { color: var(--text); font-size: var(--fs-sm); font-weight: 600; }
.aprof-usage, .aprof-fige { color: var(--dim); font-size: var(--fs-xs); font-weight: 400; white-space: nowrap; }
.aprof-gestes { display: inline-flex; gap: var(--space-1); }
.hx-tableau.aprof-matrice tr.aprof-groupe > th { background: var(--bg-1); color: var(--dim); font-size: var(--fs-xs); font-weight: 600; letter-spacing: .04em; text-transform: uppercase; }
.aprof-inter { margin-left: 0; }
.aprof-inter.is-change .syno-inter-piste { box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 45%, transparent); }
.aprof-inter:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.aprof-socle, .aprof-os { display: flex; align-items: flex-start; gap: var(--space-2); }
.aprof-socle .ni, .aprof-os .ni { flex: none; margin: 0; }
.aprof-resume { margin: 0 auto 0 0; color: var(--dim); font-size: var(--fs-sm); }
.aprof-pied .hx-bouton { white-space: nowrap; }
.aprof-teintes { min-width: 0; margin: 0; padding: 0; border: 0; }
.aprof-teintes > legend { margin: 0 0 var(--space-2); padding: 0; }
.aprof-teinte { white-space: nowrap; }
/* Le panneau « Options de l'agent » d'un client : son profil, et l'écart d'une option à ce profil. */
.aopt-profil { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-3); }
.aopt-ecarts { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
/* [Profils d'agent] fin */
/* [Atelier — images] début */
/* Atelier, lot 5 (session 225, 12/10/2026) : le gestionnaire d'images (#atelier/images, public/js/atelier-images.js).
   Seulement ce que le socle ne sait pas faire ; tout le reste est en classes hx-*. */
.atim-bandeaux:empty { display: none; }
/* Le nom affiché au-dessus du nom de fichier, la catégorie au-dessus de la version. Un nom de fichier sans espace
   se coupe où il faut ; dans le tableau de la bibliothèque, le nom garde une largeur lisible (sur téléphone, le tableau
   défile dans son cadre .hx-defile). */
.atim-nom, .atim-pile { display: flex; flex-direction: column; gap: 0; min-width: 0; overflow-wrap: anywhere; }
.atim-biblio .atim-nom { min-width: 14rem; }
/* Noms de fichier et empreintes : chasse fixe, coupés plutôt que de déborder. */
.atim-fichier { color: var(--dim); font-family: 'Cascadia Code', 'Consolas', 'Courier New', monospace; font-size: var(--fs-xs); overflow-wrap: anywhere; }
.atim-sha { color: var(--text); font-family: 'Cascadia Code', 'Consolas', 'Courier New', monospace; font-size: var(--fs-xs); overflow-wrap: anywhere; }
/* La présence sur les bancs : un point et un nom par banc, qui passent à la ligne ; un libellé de banc long est coupé
   (le titre le donne en entier, avec l'état). */
.atim-bancs { display: flex; flex-wrap: wrap; gap: var(--space-1) var(--space-3); }
.atim-bancs .hx-statut { max-width: 12rem; min-width: 0; }
/* Les listes (file d'un banc, images sur sa carte, téléversements) : une ligne par élément, un filet entre deux. */
.atim-travaux { display: grid; gap: var(--space-3); margin: 0; padding: 0; list-style: none; }
.atim-travail { display: grid; gap: var(--space-2); padding-bottom: var(--space-3); border-bottom: 1px solid var(--border); }
.atim-travail:last-child { padding-bottom: 0; border-bottom: 0; }
/* Un choix d'image (envoi à un banc, image d'une session) : le bouton radio, le nom, les badges à droite — qui
   passent sous le nom quand la place manque (téléphone). */
.atim-choix { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-3); padding: var(--space-2); border: 1px solid var(--border); border-radius: var(--radius-sm); }
.atim-choix > .atim-nom { flex: 1 1 12rem; }
/* Le champ fichier, que hx-saisie exclut : sa taille et son anneau de focus. */
.atim-fichier-champ { max-width: 100%; color: var(--text); font-size: var(--fs-sm); }
.atim-fichier-champ:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Les notes d'une image : les sauts de ligne de la saisie sont gardés. */
.atim-notes { white-space: pre-line; overflow-wrap: anywhere; }
/* Le journal d'une image, replié. */
.atim-pli > summary { color: var(--text); font-size: var(--fs-sm); font-weight: 600; cursor: pointer; }
.atim-pli > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Point 9 (« sans VM », 15/10/2026) : la carte « Disque d'outils » — le menu (rang, alias au-dessus du nom, taille et risque) —
   et, dans le détail d'une image, l'ordre et l'alias. Le rang sur une colonne fixe, en chiffres tabulaires : les alias s'alignent. */
.atim-menu { display: grid; gap: var(--space-2); margin: 0; padding: 0; list-style: none; }
.atim-menu__ligne { display: grid; grid-template-columns: 2rem minmax(0, 1fr) auto; align-items: center; gap: var(--space-3);
  padding-bottom: var(--space-2); border-bottom: 1px solid var(--border); }
.atim-menu__ligne:last-child { padding-bottom: 0; border-bottom: 0; }
.atim-menu__rang { color: var(--dim); font-size: var(--fs-sm); text-align: right; }
.atim-disque-reglages { align-items: flex-end; }
.atim-disque-ordre { width: 6rem; }
@media (max-width: 600px) {
  .atim-menu__ligne { grid-template-columns: 2rem minmax(0, 1fr); }
  .atim-menu__ligne > :last-child { grid-column: 2; }
}
/* [Atelier — images] fin */
/* [Atelier — installation] début */
/* Atelier v1.1, lot install-serveur (session 226, 12/10/2026) : public/js/atelier-installation.js — la carte d'une session
   d'installation (étapes, avancement) et la fenêtre de l'accord d'effacement (le PC, ses disques, la confirmation) ; les
   champs d'installation de « Ouvrir une session » (public/js/atelier.js). Le socle fait l'essentiel (hx-carte, hx-bandeau,
   hx-def, hx-badge, hx-statut, hx-jauge, hx-case, hx-encart, hx-groupe) ; ces règles ne posent que ce qu'il n'a pas. Jetons
   seuls, espacements de l'échelle (4, 8, 12, 16), clair et sombre par les jetons. Préfixe .atli-. */
/* Le titre de la carte (Session, Installation, le sous-mode, le numéro) passe à la ligne sur un petit écran. */
.hx-titre.atli-titre { flex-wrap: wrap; row-gap: var(--space-1); }
/* Les étapes : une pastille par étape, qui passent à la ligne ; l'étape en cours est mise en avant. */
.atli-etapes { list-style: none; margin: var(--space-3) 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-3); }
.atli-etape { display: inline-flex; align-items: center; min-width: 0; color: var(--dim); font-size: var(--fs-sm); }
.atli-etape--fait { color: var(--text); }
.atli-etape--en_cours, .atli-etape--erreur { color: var(--text); font-weight: 600; }
.atli-etape .hx-statut { gap: var(--space-1); }
/* La fenêtre de l'accord : le PC, puis un disque par ligne — le choix, le nom, la série en chasse fixe, les marques. */
.atli-identite { display: flex; align-items: flex-start; gap: var(--space-3); }
.atli-identite > .ni { flex: none; margin-top: var(--space-1); }
.atli-disques { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.atli-disque { display: flex; flex-direction: column; gap: var(--space-1); padding: var(--space-2) var(--space-3); border: 1px solid var(--border); border-radius: var(--radius-sm); min-width: 0; }
.atli-disque:has(input:checked) { border-color: var(--accent); box-shadow: inset 3px 0 0 var(--accent); }
.atli-disque--bitlocker { border-color: color-mix(in srgb, var(--color-crit) 45%, var(--border)); }
.atli-disque--ecarte .atli-disque__nom > strong { color: var(--dim); }
.atli-disque--ecarte .atli-disque__choix { cursor: not-allowed; }
.atli-disque__choix { align-items: flex-start; min-width: 0; }
.atli-disque__nom { min-width: 0; overflow-wrap: anywhere; }
.atli-disque__serie { color: var(--dim); overflow-wrap: anywhere; }
.atli-disque__serie .hx-code { overflow-wrap: anywhere; }
/* Les 4 caractères : assez large pour les lire, en chasse fixe. */
.atli-confirmation { max-width: 8rem; font-family: 'Cascadia Code', 'Consolas', 'Courier New', monospace; letter-spacing: 0.2em; text-transform: uppercase; }
/* [Atelier — installation] fin */
/* [Atelier — mur] début */
/* Atelier v1.1, lot « mur » (session 226, 12/10/2026) : le mur des bancs (#atelier/mur, public/js/atelier-mur.js) — une
   carte par banc (vignette de l'écran du PC, étape, attention requise, gestes rapides) — et les avis d'attention, sur
   toutes les pages. Le socle fait l'essentiel (hx-carte, hx-bandeau, hx-badge, hx-statut, hx-def, hx-jauge, hx-filtre,
   hx-onglets) ; ces règles ne posent que la grille, la vignette, le liseré d'attention et la pile des avis. Jetons seuls,
   espacements de l'échelle, clair et sombre par les jetons. Préfixe .atl-mur. */
/* La grille : une colonne sous 600 px, deux jusqu'à 1 279 px, trois au-delà. */
.atl-mur { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-4); align-items: start; min-width: 0; }
@media (min-width: 600px) { .atl-mur { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1280px) { .atl-mur { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.atl-mur > .hx-etat, .atl-mur__vide { grid-column: 1 / -1; }
.atl-mur__vide[hidden] { display: none; }
/* Une carte : en-tête, vignette, corps ; le liseré de gauche dit la gravité de l'attention requise. */
.atl-mur__carte { display: flex; flex-direction: column; gap: var(--space-3); min-width: 0; }
.atl-mur__carte[hidden] { display: none; }
.atl-mur__carte--crit { --hx-ton: var(--color-crit); }
.atl-mur__carte--warn { --hx-ton: var(--color-warn); }
.atl-mur__carte--accent { --hx-ton: var(--accent); }
.atl-mur__carte--crit, .atl-mur__carte--warn, .atl-mur__carte--accent { border-left: 3px solid var(--hx-ton); }
.atl-mur__titre { flex: 1 1 auto; min-width: 0; }
.atl-mur__tete .hx-statut { flex: none; font-size: var(--fs-sm); }
/* La vignette : le canvas suit la largeur de la carte (480 × 270, 16/9) ; la couche dit ce qui manque. */
.atl-mur__ecran { position: relative; overflow: hidden; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--bg-0); }
.atl-mur__vignette { display: block; width: 100%; height: auto; aspect-ratio: 16 / 9; }
.atl-mur__couche { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; gap: var(--space-2);
  padding: var(--space-3); text-align: center; background: var(--bg-1); color: var(--text); font-size: var(--fs-sm); line-height: 1.4; }
.atl-mur__couche[hidden] { display: none; }
.atl-mur__couche .ni { flex: none; margin: 0; }
/* Un texte reçu (motif, message, libellé du PC) : coupé où il faut, jamais hors de la carte. */
.atl-mur__detail { overflow-wrap: anywhere; }
.atl-mur__attention .hx-bandeau__texte > span { overflow-wrap: anywhere; }
/* Les avis d'attention : en bas à gauche (les toasts sont à droite), au-dessus de la page, opaques. */
.atl-mur-avis { position: fixed; left: var(--space-6); bottom: var(--space-6); z-index: var(--z-toast); display: flex; flex-direction: column;
  gap: var(--space-2); width: min(380px, calc(100vw - 32px)); }
.atl-mur-avis:empty { display: none; }
.atl-mur-avis__item { align-items: center; background: color-mix(in srgb, var(--hx-ton) 16%, var(--bg-2)); box-shadow: 0 8px 24px var(--overlay-bg); }
@media (max-width: 600px) { .atl-mur-avis { left: var(--space-4); right: var(--space-4); bottom: var(--space-4); width: auto; } }
@supports not (color: color-mix(in srgb, currentColor 50%, transparent)) { .atl-mur-avis__item { background: var(--bg-2); } }
/* Revue des bancs (15/10/2026) : « À conclure et missions récentes », sous les cartes — une ligne par mission finie (heure, banc,
   première ligne du rapport de résolution, marques, gestes, « Lire le rapport ») ; la ligne du rapport tient sur une ligne, entière au
   survol. 19/10/2026 (« Actionnable » : « Conclure » et « Valider » sur la ligne) : les marques et les gestes prennent la moitié de la
   ligne au plus et passent à la ligne entre eux, jamais hors de la carte. Sous 900 px : l'heure et le banc, puis le rapport, puis les
   marques et les gestes. */
.atl-mur__missions { display: grid; gap: var(--space-2); margin: 0; padding: 0; list-style: none; }
.atl-mur__mission { display: grid; grid-template-columns: 5rem 8rem minmax(0, 1fr) fit-content(50%); align-items: center; gap: var(--space-3);
  padding-bottom: var(--space-2); border-bottom: 1px solid var(--border); }
.atl-mur__mission:last-child { padding-bottom: 0; border-bottom: 0; }
.atl-mur__mission-quand { color: var(--dim); }
.atl-mur__mission-ligne { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.atl-mur__mission-fin { justify-content: flex-end; }
@media (max-width: 900px) {
  .atl-mur__mission { grid-template-columns: auto minmax(0, 1fr); gap: var(--space-1) var(--space-3); }
  .atl-mur__mission-ligne, .atl-mur__mission-fin { grid-column: 1 / -1; }
  .atl-mur__mission-fin { justify-content: flex-start; }
}
/* [Atelier — mur] fin */
/* [Atelier — HeXaIA B] début */
/* Atelier v1.2, lot B (session 232, 14/10/2026) : les prompts types (#atelier/prompts, public/js/atelier-ia-prompts.js) — la
   liste, l'éditeur (variables, références, Windows, contrôles, lectures), l'aperçu, les versions et leur comparaison, la carte
   des réglages. Le socle fait l'essentiel (hx-carte, hx-champ, hx-groupe, hx-encart, hx-tableau, hx-bandeau, hx-badge) ; ces
   règles ne posent que ce qu'il n'a pas. Jetons seuls, espacements de l'échelle (4, 8, 12, 16, 24), clair et sombre par les
   jetons. Une comparaison n'emploie aucune couleur de sévérité. Préfixe .atip-. */
/* Un nom au-dessus de sa description, une portée au-dessus de ses clients : coupés où il faut, jamais hors de la cellule. */
.atip-pile { display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-1); min-width: 0; }
.atip-coupe { min-width: 0; overflow-wrap: anywhere; }
.atip-tableau td:first-child .atip-pile { min-width: 12rem; }
.atip-pile .ni { margin: 0; }
/* Noms de variables, règles, paramètres, documents collés : chasse fixe. */
.atip-mono { font-family: 'Cascadia Code', 'Consolas', 'Courier New', monospace; }
/* Les listes d'un bandeau (erreurs, avertissements). */
.atip-liste { margin: var(--space-1) 0 0; padding-left: var(--space-4); }
.atip-liste > li + li { margin-top: var(--space-1); }
/* Une référence par ligne (procédures, défauts des clients), un filet autour. */
.atip-liste-refs { display: grid; gap: var(--space-2); margin: 0; padding: 0; list-style: none; }
.atip-liste-refs > li { min-width: 0; padding: var(--space-2) var(--space-3); border: 1px solid var(--border); border-radius: var(--radius-sm); }
/* Un choix (catégorie, portée) : le bouton radio, le libellé et son aide ; le choix retenu est cerné de l'accent. */
.atip-choix { align-items: flex-start; padding: var(--space-2) var(--space-3); border: 1px solid var(--border); border-radius: var(--radius-sm); }
.atip-choix:has(input:checked) { border-color: var(--accent); }
.atip-choix .ni { margin: 0; }
/* Des cases à cocher en colonnes de 14rem, une seule sur un téléphone. */
.atip-cases { display: grid; gap: var(--space-2) var(--space-4); grid-template-columns: repeat(auto-fill, minmax(min(100%, 14rem), 1fr)); }
.atip-clients[hidden] { display: none; }
/* La consigne : assez haute pour la lire, agrandie à la main. */
.atip-consigne { min-height: 12rem; resize: vertical; line-height: 1.5; }
.atip-nombre { max-width: 10rem; }
/* Le formulaire de l'éditeur : un fieldset (lecture seule = désactivé), sans cadre à lui. */
.atip-formulaire { min-width: 0; margin: 0; padding: 0; border: 0; }
.atip-variable { display: flex; flex-direction: column; gap: var(--space-3); }
.atip-variable > legend { font-family: 'Cascadia Code', 'Consolas', 'Courier New', monospace; }
.atip-script { min-width: 0; }
/* La barre d'enregistrement (au-dessus et au-dessous du formulaire), son indicateur de modifications non enregistrées. */
.atip-barre { padding: var(--space-3) var(--space-4); border: 1px solid var(--border); border-radius: var(--radius-lg); background: var(--bg-2); }
/* La consigne rendue : le numéro de chaque phrase en marge, chiffres alignés. */
.atip-phrases { display: grid; gap: var(--space-1); margin: 0; padding: 0; list-style: none; font-size: var(--fs-sm); }
.atip-phrase { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: var(--space-2); }
.atip-phrase__n { color: var(--dim); }
/* La comparaison de deux versions : une ligne par réglage ; ajouts et retraits marqués d'un signe et d'un fond en retrait,
   jamais d'une couleur de sévérité (le retrait est barré). */
.atip-diff { display: grid; margin: 0; padding: var(--space-2) 0; list-style: none; max-height: 60vh; overflow-y: auto; border: 1px solid var(--border);
  border-radius: var(--radius-sm); background: var(--code-bg); font-family: 'Cascadia Code', 'Consolas', 'Courier New', monospace; font-size: var(--fs-xs); line-height: 1.55; }
.atip-diff__ligne { display: grid; grid-template-columns: 1.5em minmax(0, 1fr); gap: var(--space-1); padding: 0 var(--space-3); }
.atip-diff__ligne--ajout, .atip-diff__ligne--retrait { background: var(--bg-2); font-weight: 600; }
.atip-diff__ligne--retrait > .atip-coupe { color: var(--dim); text-decoration: line-through; }
.atip-diff__signe { color: var(--text); text-align: center; }
.atip-version { max-height: 60vh; overflow-y: auto; }
/* Le champ fichier de l'import, que hx-saisie exclut : sa taille et son anneau de focus. */
.atip-fichier-champ { max-width: 100%; color: var(--text); font-size: var(--fs-sm); }
.atip-fichier-champ:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.atip-reglage .hx-case { font-weight: 600; }
/* [Atelier — HeXaIA B] fin */
/* [Atelier — HeXaIA C] début */
/* Atelier v1.2, lot C « plans » (session 232, 14/10/2026) : public/js/atelier-ia-plan.js — la carte « Plan HeXaIA » d'un banc
   (#atl-plan), le détail et la liste de l'onglet « Plans », les fenêtres du plan. Le socle fait l'essentiel (hx-carte, hx-bandeau,
   hx-badge, hx-statut, hx-def, hx-jauge, hx-case, hx-encart, hx-bloc-code, hx-tableau, hx-defile) ; ces règles ne posent que la liste
   des étapes, les citations et les textes reçus. Jetons seuls, espacements de l'échelle (4, 8, 12, 16), clair et sombre par les
   jetons. Préfixe .atlp-. */
.hx-titre.atlp-titre { flex-wrap: wrap; row-gap: var(--space-1); }
/* Un texte reçu (de HeXaIA, d'un résultat, d'une consigne) : coupé où il faut, jamais hors de la carte. */
.atlp-texte { overflow-wrap: anywhere; min-width: 0; }
.atlp-rapport { white-space: pre-wrap; }
.atlp-liste { margin: var(--space-1) 0 0; padding-left: var(--space-4); }
/* Les étapes : une par ligne, l'étape courante marquée d'un liseré d'accent, une étape décochée atténuée. */
.atlp-etapes { list-style: none; margin: var(--space-3) 0 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-2); min-width: 0; }
.atlp-etape { display: flex; flex-direction: column; gap: var(--space-2); padding: var(--space-2) var(--space-3); border: 1px solid var(--border);
  border-radius: var(--radius-sm); background: var(--bg-1); min-width: 0; }
.atlp-etape--courante { border-color: var(--accent); box-shadow: inset 3px 0 0 var(--accent); }
.atlp-etape--echec { box-shadow: inset 3px 0 0 var(--color-crit); }
.atlp-etape--a_revalider { box-shadow: inset 3px 0 0 var(--color-warn); }
.atlp-etape--exclue .atlp-etape__nom, .atlp-etape--ignoree .atlp-etape__nom, .atlp-etape--retiree .atlp-etape__nom { color: var(--dim); font-weight: 500; }
.atlp-etape__tete { display: flex; align-items: center; gap: var(--space-2); min-width: 0; flex-wrap: wrap; }
.atlp-etape__tete > .ni { flex: none; margin: 0; }
.atlp-etape__nom { flex: 1 1 12rem; min-width: 0; overflow-wrap: anywhere; }
.atlp-etape__choix { flex: none; display: inline-flex; align-items: center; color: var(--dim); }
.atlp-etape__choix .ni { margin: 0; }
.atlp-etape__marques { min-width: 0; }
/* La phrase citée de la consigne : en retrait, atténuée. */
.atlp-citation { margin: 0; padding: 0 0 0 var(--space-3); border-left: 2px solid var(--border-strong); color: var(--dim); font-size: var(--fs-sm); overflow-wrap: anywhere; }
/* Le résultat d'une tâche : borné en hauteur, défilant, le texte coupé à la ligne. */
.hx-bloc-code.atlp-resultat { max-height: 12rem; overflow: auto; white-space: pre-wrap; overflow-wrap: anywhere; }
/* L'analyse de HeXaIA : l'icône, puis le texte. */
.atlp-suivi { display: flex; align-items: flex-start; gap: var(--space-3); min-width: 0; }
.atlp-suivi > .ni { flex: none; margin-top: var(--space-1); color: var(--accent); }
.atlp-consigne { margin-top: var(--space-3); }
.atlp-consigne > summary { cursor: pointer; color: var(--dim); font-size: var(--fs-sm); }
.atlp-phrases { margin: var(--space-2) 0 0; padding-left: var(--space-6); font-size: var(--fs-sm); }
.atlp-gestes { row-gap: var(--space-2); }
.atlp-candidat { align-items: flex-start; min-width: 0; overflow-wrap: anywhere; }
@media (max-width: 600px) { .atlp-etape { padding: var(--space-2); } .atlp-etape__nom { flex-basis: 100%; } }
/* [Atelier — HeXaIA C] fin */
/* [Atelier — HeXaIA D] début */
/* Atelier v1.2, lot D (session 232, 14/10/2026) : la carte « HeXaIA » d'un banc (public/js/atelier-ia-dialogue.js) — le fil de la
   conversation, les propositions et leur risque, l'état de la demande, la saisie. Le socle fait l'essentiel (hx-carte, hx-bandeau,
   hx-badge, hx-encart, hx-code, hx-saisie, hx-case, hx-bouton) ; ces règles posent le fil, les messages, les vignettes et les
   propositions. Jetons seuls, espacements de l'échelle, clair et sombre par les jetons. Préfixe .atia. */
/* Le fil : un message par ligne, le plus récent en tête. Session 266 (14/10/2026) : ni hauteur maximale ni barre à lui — la page
   est la seule à défiler ; la saisie, placée AVANT le fil, a ses règles dans public/css/atelier-banc.css. */
.atia-fil { list-style: none; margin: var(--space-3) 0 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-3); min-width: 0; }
.atia-vide { padding: var(--space-2) 0; }
.atia-msg { display: flex; flex-direction: column; gap: var(--space-1); padding: var(--space-2) var(--space-3); border: 1px solid var(--border);
  border-left: 3px solid var(--border-strong); border-radius: var(--radius-sm); background: var(--bg-1); min-width: 0; }
.atia-msg--hexaia { border-left-color: var(--accent); }
.atia-msg--systeme { border-left-color: var(--color-warn); }
.atia-msg__tete { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); color: var(--dim); font-size: var(--fs-sm); min-width: 0; }
.atia-msg__auteur { display: inline-flex; align-items: center; gap: var(--space-1); font-weight: 600; color: var(--text); min-width: 0; overflow-wrap: anywhere; }
.atia-msg__heure { flex: none; font-variant-numeric: tabular-nums; }
/* Le texte de HeXaIA et du technicien : échappé, ses sauts de ligne gardés, coupé où il faut. */
.atia-msg__texte { margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; line-height: 1.5; }
.atia-msg__sous-titre { margin: var(--space-2) 0 0; font-size: var(--fs-sm); font-weight: 600; }
.atia-liste { margin: var(--space-1) 0 0; padding-left: var(--space-4); display: flex; flex-direction: column; gap: var(--space-1); font-size: var(--fs-sm); }
.atia-liste li { overflow-wrap: anywhere; }
.atia-preuve { display: block; color: var(--dim); }
/* Les vignettes des écrans joints (960 × 540 réduits), en 16/9. */
.atia-vignettes { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-top: var(--space-2); }
.atia-vignette { display: block; width: 160px; height: auto; aspect-ratio: 16 / 9; object-fit: cover; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--bg-0); }
.atia-vignette-lien { display: inline-block; border-radius: var(--radius-sm); }
.atia-vignette-lien:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Les propositions : le liseré dit le risque décidé par le serveur (sensible : warn, refusée : crit ; jamais l'accent). */
.atia-props { list-style: none; margin: var(--space-2) 0 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.atia-props[hidden] { display: none; }
.atia-prop { display: flex; flex-direction: column; gap: var(--space-2); padding: var(--space-2) var(--space-3); border: 1px solid var(--border);
  border-radius: var(--radius-sm); background: var(--bg-2); min-width: 0; }
.atia-prop--sensible { border-color: color-mix(in srgb, var(--color-warn) 45%, var(--border)); }
.atia-prop--refusee { border-color: color-mix(in srgb, var(--color-crit) 45%, var(--border)); }
.atia-prop__tete { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: var(--space-2); min-width: 0; }
.atia-prop__code { display: inline-flex; align-items: center; gap: var(--space-1); font-weight: 600; min-width: 0; }
.atia-prop__texte { display: block; white-space: pre-wrap; overflow-wrap: anywhere; }
.atia-prop__motif { margin: 0; font-size: var(--fs-sm); overflow-wrap: anywhere; }
.atia-relancer { font-size: var(--fs-sm); }
/* L'état de la demande en vol, le compteur en chiffres tabulaires. */
.atia-etat { display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-2); margin-top: var(--space-3); color: var(--dim); font-size: var(--fs-sm); }
.atia-etat[hidden] { display: none; }
.atia-secondes { font-variant-numeric: tabular-nums; }
/* La saisie (sa disposition : public/css/atelier-banc.css). */
.atia-avertissement { display: flex; align-items: center; gap: var(--space-1); }
.atia-constat { margin-top: var(--space-3); overflow-wrap: anywhere; }
.atia-fiche { display: inline-flex; align-items: center; gap: var(--space-1); }
@media (max-width: 600px) { .atia-vignette { width: 120px; } }
@supports not (color: color-mix(in srgb, currentColor 50%, transparent)) {
  .atia-prop--sensible { border-color: var(--color-warn); }
  .atia-prop--refusee { border-color: var(--color-crit); }
}
/* Le badge du prompt type d'un message (atiaBadgeLong) : le nom d'un prompt type est une DONNÉE, parfois longue. Le badge reste
   borné à la largeur du message ; son texte se coupe en points de suspension et le libellé entier est en infobulle (title).
   Sans cela, à 360 px et dans la colonne du banc à 1 280 px, le texte sortait du badge et s'y perdait sans le dire (+119 px,
   jeu visuel atelier-ia-dialogue, contrôle 2). */
.atia-badge-long { max-width: 100%; }
.atia-badge-long__texte { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* [Atelier — HeXaIA D] fin */
/* [Atelier — Solutions B] début */
/* Atelier v1.3, lot B (cas, moteur, carte ; session 238, 16/10/2026) : public/js/atelier-kb-banc.js — la carte « Déjà vu et
   solutions » d'un banc (#atl-kb : déjà vu, solutions classées, essai en cours et ses étapes, préalables de la garde des données,
   escalade, pistes à valider, cas proches, symptômes), le formulaire « Conclure le cas » et le sous-onglet « Cas ». Le socle fait
   l'essentiel (hx-carte, hx-entete, hx-badge, hx-bandeau, hx-filtre, hx-case, hx-encart, hx-groupe, hx-def, hx-tableau, hx-code) ;
   ces règles ne posent que les listes, le rang, les pastilles et les filets. Jetons seuls, espacements de l'échelle (4, 8, 12, 16),
   clair et sombre par les jetons, tenue à 360 px. Le risque « sensible » est un badge warn, jamais l'accent. Préfixe .atkb-. */
.atkb-corps { display: flex; flex-direction: column; gap: var(--space-3); min-width: 0; }
.atkb-section { display: flex; flex-direction: column; gap: var(--space-2); min-width: 0; padding-top: var(--space-3); border-top: 1px solid var(--border); }
.atkb-corps > .atkb-section:first-child { padding-top: 0; border-top: 0; }
.atkb-sous-titre { display: flex; align-items: center; gap: var(--space-2); margin: 0; font-size: var(--fs-sm); font-weight: 600; color: var(--text); }
.atkb-sous-titre .ni { flex: none; }
.atkb-liste { margin: 0; padding-left: var(--space-4); display: flex; flex-direction: column; gap: var(--space-1); font-size: var(--fs-sm); overflow-wrap: anywhere; }
.atkb-dv__titre { display: flex; align-items: center; gap: var(--space-2); margin: 0; font-size: var(--fs-sm); }
.atkb-dv__titre .ni { flex: none; color: var(--dim); }
.atkb-decisive { color: var(--text); }
/* Les lignes classées : une par solution, un filet entre deux ; le rang en pastille ronde à chiffres tabulaires. */
.atkb-lignes { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.atkb-ligne { display: flex; flex-direction: column; gap: var(--space-1); padding: var(--space-2) 0; border-top: 1px solid var(--border); min-width: 0; }
.atkb-ligne:first-child { border-top: 0; padding-top: 0; }
.atkb-ligne--exploration { padding-left: var(--space-3); border-left: 2px dashed var(--border-strong); }
.atkb-ligne__tete { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-1) var(--space-2); min-width: 0; }
.atkb-ligne__titre { flex: 1 1 12rem; min-width: 0; font-weight: 600; overflow-wrap: anywhere; }
.atkb-ligne__resume { margin: 0; font-size: var(--fs-sm); color: var(--dim); overflow-wrap: anywhere; }
.atkb-rang { flex: none; display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px; border-radius: 50%;
  border: 1px solid var(--border-strong); font-size: var(--fs-xs); font-variant-numeric: tabular-nums; color: var(--text); }
/* Les préalables de la garde des données : ce qui manque, les cases à cocher. */
.atkb-prealables { display: flex; flex-direction: column; gap: var(--space-1); }
.atkb-prealable { display: flex; align-items: flex-start; gap: var(--space-2); margin: 0; font-size: var(--fs-sm); color: var(--text); overflow-wrap: anywhere; }
.atkb-prealable .ni { flex: none; margin-top: 2px; color: var(--color-warn); }
.atkb-case { font-size: var(--fs-sm); }
/* Les étapes : un pli par solution, une liste numérotée ; la commande exacte en chasse fixe, coupée où il faut. */
.atkb-etapes > summary { cursor: pointer; font-size: var(--fs-sm); color: var(--dim); }
.atkb-etapes > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.atkb-etapes__liste { margin: var(--space-2) 0 0; padding-left: var(--space-6); display: flex; flex-direction: column; gap: var(--space-2); }
.atkb-etape { min-width: 0; }
.atkb-etape__type { font-size: var(--fs-sm); font-weight: 600; }
.atkb-etape__texte { margin: var(--space-1) 0 0; font-size: var(--fs-sm); overflow-wrap: anywhere; }
.atkb-etape__commande { display: inline-block; max-width: 100%; margin-top: var(--space-1); overflow-wrap: anywhere; white-space: normal; }
/* L'essai en cours : encadré par l'accent (une information, pas une alerte). */
.atkb-essai { padding: var(--space-3); border: 1px solid var(--accent); border-radius: var(--radius-sm); }
.atkb-corps > .atkb-essai:first-child { padding-top: var(--space-3); border-top: 1px solid var(--accent); }
.atkb-resultat { display: flex; flex-direction: column; gap: var(--space-1); }
/* Les symptômes : des pastilles retirables, des suggestions à ajouter. */
.atkb-pastilles { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: var(--space-2); }
.atkb-pastille { display: inline-flex; align-items: center; gap: var(--space-1); padding: 0 0 0 var(--space-2); border: 1px solid var(--border);
  border-radius: var(--radius-sm); background: var(--bg-1); font-size: var(--fs-sm); }
.atkb-pastille .hx-bouton--icone { min-height: 24px; min-width: 24px; }
@media (pointer: coarse) { .atkb-pastille .hx-bouton--icone { min-height: 44px; min-width: 44px; } }
.atkb-suggestion { font-size: var(--fs-sm); }
/* « Conclure le cas » : les choix en colonnes qui passent à la ligne ; les pastilles de HeXaIA dans un encadré neutre. */
.atkb-choix { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-4); }
.atkb-ia .hx-filtre[aria-pressed="true"] { border-color: var(--accent); }
@media (max-width: 600px) {
  .atkb-etapes__liste { padding-left: var(--space-4); }
  .atkb-ligne__titre { flex-basis: 100%; }
}
/* Le dialogue v1.2 : la note d'une étape d'une solution de la base jouée depuis la carte est une information, pas une alerte. */
.atia-msg--systeme.atia-msg--kb { border-left-color: var(--color-info); }
/* [Atelier — Solutions B] fin */
/* [Atelier — Solutions C] début */
/* Atelier v1.3, lot C (session 238, 16/10/2026) : Atelier › Solutions (#atelier/solutions, public/js/atelier-kb-solutions.js) — la
   liste, la file « À valider », l'éditeur (symptômes, conditions en puces, étapes), les versions et leur comparaison, les réglages ;
   et la fiche d'une solution de l'Atelier en lecture seule dans « Connaissances » (public/js/kb.js). Le socle fait l'essentiel
   (hx-carte, hx-champ, hx-filtre, hx-tableau, hx-bandeau, hx-badge, hx-grille) ; ces règles ne posent que ce qu'il n'a pas. Jetons
   seuls, espacements de l'échelle (4, 8, 12, 16), clair et sombre par les jetons. Une comparaison de versions n'emploie aucune
   couleur de sévérité : l'ajout porte l'accent, le retrait est barré et atténué. Préfixe .atkbs-. */
.atkbs-page { min-width: 0; }
.atkbs-filtres > select { max-width: 100%; }
/* Le tableau des solutions : une largeur lisible, il défile dans son cadre (.hx-defile) sur un téléphone. */
.atkbs-tableau { min-width: 44rem; }
.atkbs-versions { min-width: 36rem; }
.atkbs-pile-cellule { display: flex; flex-direction: column; gap: var(--space-1); min-width: 0; overflow-wrap: anywhere; }
.atkbs-note { min-width: 12rem; color: var(--text); font-size: var(--fs-sm); font-variant-numeric: tabular-nums; }
/* Les files (« À valider », fiches à structurer, clients hors mutualisation) : une ligne par entrée, un filet entre deux. */
.atkbs-file { display: grid; gap: var(--space-3); margin: 0; padding: 0; list-style: none; }
.atkbs-file__ligne { display: flex; flex-wrap: wrap; align-items: flex-start; gap: var(--space-2) var(--space-3); padding-bottom: var(--space-3); border-bottom: 1px solid var(--border); }
.atkbs-file__ligne:last-child { padding-bottom: 0; border-bottom: 0; }
.atkbs-liste { display: grid; gap: var(--space-1); margin: 0; padding-left: var(--space-4); }
/* La barre des gestes de l'éditeur, sous l'en-tête ; l'indicateur « Modifications non enregistrées ». */
.atkbs-barre { padding-top: var(--space-2); border-top: 1px solid var(--border); }
.atkbs-sale[hidden] { display: none; }
/* Le formulaire : un seul ensemble de champs, sans cadre (les cartes sont ses sections) ; désactivé, il reste lisible. */
.atkbs-champs { min-width: 0; margin: 0; padding: 0; border: 0; }
.atkbs-champs:disabled .hx-saisie { color: var(--text); }
.atkbs-grille { gap: var(--space-3); }
/* Les symptômes et les causes à départager : des filtres qui passent à la ligne. */
.atkbs-symptomes, .atkbs-puces { display: flex; flex-wrap: wrap; gap: var(--space-2); min-width: 0; }
/* Un jeton d'une condition : son code en chasse fixe et son bouton de retrait. */
.atkbs-puce { display: inline-flex; align-items: center; gap: var(--space-1); max-width: 100%; min-width: 0; padding-left: var(--space-2);
  border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--bg-1); }
.atkbs-code { font-family: 'Cascadia Code', 'Consolas', 'Courier New', monospace; font-size: var(--fs-sm); overflow-wrap: anywhere; }
/* Les étapes : une carte par étape, ses badges calculés par le serveur en tête. */
.atkbs-etapes { display: grid; gap: var(--space-3); margin: 0; padding: 0; list-style: none; }
.atkbs-etape { display: flex; flex-direction: column; gap: var(--space-3); min-width: 0; padding: var(--space-3);
  border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--bg-1); }
.atkbs-etape__ref:empty { display: none; }
/* Vérification at13-1610 : les préalables d'une étape, en liste sous son en-tête ; un badge long sortait de lui-même à 360 px. */
.atkbs-prealables { display: grid; gap: var(--space-1); margin: 0; padding: 0; list-style: none; }
.atkbs-prealables li { display: flex; align-items: flex-start; gap: var(--space-1); min-width: 0; overflow-wrap: anywhere; }
.atkbs-prealables .ni { flex: none; width: 12px; height: 12px; margin-top: var(--space-1); }
/* La lecture et la comparaison d'une version : une ligne par réglage. */
.atkbs-diff { display: grid; gap: 0; max-height: 60vh; margin: 0; padding: 0; overflow: auto; list-style: none;
  font-family: 'Cascadia Code', 'Consolas', 'Courier New', monospace; font-size: var(--fs-sm); }
.atkbs-diff__ligne { display: flex; gap: var(--space-2); padding: 0 var(--space-2); border-left: 3px solid transparent; color: var(--text);
  white-space: pre-wrap; overflow-wrap: anywhere; }
.atkbs-diff__ligne--plus { border-left-color: var(--accent); background: color-mix(in srgb, var(--accent) 10%, transparent); }
.atkbs-diff__ligne--moins { border-left-color: var(--border-strong); color: var(--dim); text-decoration: line-through; }
.atkbs-diff__marque { flex: none; width: 1ch; color: var(--dim); }
@supports not (color: color-mix(in srgb, currentColor 50%, transparent)) { .atkbs-diff__ligne--plus { background: var(--bg-2); } }
/* « Connaissances » : la fiche d'une solution de l'Atelier, en lecture seule (le markdown rendu par le serveur, tel quel). */
.kb-atelier-corps { max-height: 60vh; margin: 0; padding: var(--space-3); overflow: auto; border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--bg-1); color: var(--text); font-size: var(--fs-sm); line-height: 1.5; white-space: pre-wrap; overflow-wrap: anywhere; }
/* [Atelier — Solutions C] fin */
/* [Atelier — Solutions E] début */
/* Atelier v1.3, lot E (configuration qui apprend, session 238, 16/10/2026) : la carte « Résultats de cette version » de l'éditeur
   d'un prompt type et la section « Configuration des PC » de l'écran Efficacité (public/js/atelier-kb-config.js). Jetons seulement,
   préfixe .akc-. Les badges de note des étapes de plan reprennent .hx-badge. */
.akc-liste { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-1); min-width: 0; }
.akc-liste > li { min-width: 0; overflow-wrap: anywhere; gap: var(--space-2); font-size: var(--fs-sm); }
.akc-liste > li .ni { margin-right: var(--space-1); }
.akc-citation { margin: 0; padding: 0 0 0 var(--space-3); border-left: 2px solid var(--border-strong); color: var(--text); font-size: var(--fs-sm); overflow-wrap: anywhere; }
.akc-retour:empty { display: none; }
/* [Atelier — Solutions E] fin */
/* [Correctifs tiers] début — la page Gestion › Correctifs tiers et l'onglet du poste (public/js/correctifs_tiers.js, lot D,
   19/10/2026). Le bandeau : six tuiles de chiffres, puis la tendance 12 semaines sur toute la largeur (un trait atténué,
   la semaine en cours en accent). Jetons seuls, espacements de l'échelle ; le reste est le socle (hx-*). */
.ctp-tuiles { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: var(--space-3); }
@media (max-width: 1200px) { .ctp-tuiles { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 600px) { .ctp-tuiles { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.ctp-tuile { display: flex; flex-direction: column; gap: var(--space-1); min-width: 0; padding: var(--space-3);
  border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--bg-1); }
.ctp-tuile__lib { color: var(--dim); font-size: var(--fs-sm); white-space: nowrap; }
.ctp-tuile__val { display: inline-flex; align-items: center; gap: var(--space-1); font-size: var(--fs-xl);
  font-weight: 700; line-height: 1.15; white-space: nowrap; }
.ctp-tuile__note { color: var(--dim); font-size: var(--fs-xs); }
.ctp-tuile--courbe { grid-column: 1 / -1; flex-direction: row; align-items: center; gap: var(--space-4); }
.ctp-courbe__legende { display: flex; flex: none; flex-direction: column; gap: var(--space-1); min-width: 10rem; }
.ctp-courbe { display: block; flex: 1; min-width: 0; width: 100%; height: 48px; overflow: visible; }
@media (max-width: 600px) { .ctp-tuile--courbe { flex-direction: column; align-items: stretch; } }
.ctp-courbe__aire { fill: color-mix(in srgb, var(--accent) 10%, transparent); stroke: none; }
.ctp-courbe__ligne { fill: none; stroke: var(--dim); stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; vector-effect: non-scaling-stroke; }
.ctp-courbe__point { stroke: var(--accent); stroke-width: 8; stroke-linecap: round; vector-effect: non-scaling-stroke; }
.ctp-courbe__cible { fill: transparent; }
.ctp-courbe__cible:hover { fill: color-mix(in srgb, var(--accent) 14%, transparent); }
@supports not (color: color-mix(in srgb, currentColor 50%, transparent)) { .ctp-courbe__aire { fill: var(--bg-2); } }
/* Une pile dans une cellule : ses badges gardent leur largeur (la pile du socle étire ses enfants). */
.ctp-pile { align-items: flex-start; }
.ctp-col-logiciel { min-width: 14rem; }
.ctp-col-client { min-width: 10rem; }
/* Une erreur d'agent dans une cellule : une ligne, le texte entier au survol (title). */
.ctp-erreur { display: inline-block; max-width: 16rem; vertical-align: bottom; }
.ctp-motif { min-width: 12rem; }
/* Les gestes d'une politique : deux par rangée, alignés à droite. */
.ctp-actions { display: grid; grid-template-columns: repeat(2, max-content); gap: var(--space-1); justify-content: end; }
/* Le bilan d'un déploiement, la liste des CVE d'un logiciel : une ligne par poste ou par faille. */
.ctp-bilan { display: flex; flex-direction: column; gap: var(--space-1); margin: 0; padding: 0; list-style: none; }
.ctp-bilan > li { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); min-width: 0; font-size: var(--fs-sm); overflow-wrap: anywhere; }
.ctp-cves > summary { cursor: pointer; white-space: nowrap; }
/* Saisie assistée des paquets d'une politique : pastilles choisies + liste de propositions. */
.ctp-choix { position: relative; display: flex; flex-direction: column; gap: var(--space-2); }
.ctp-choix__puces { display: flex; flex-wrap: wrap; gap: var(--space-1); }
.ctp-choix__puces[hidden] { display: none; }
.ctp-choix__puce { gap: var(--space-1); }
.ctp-choix__retirer { display: inline-flex; align-items: center; padding: 0; border: 0; background: transparent; color: inherit; cursor: pointer; }
.ctp-choix__retirer:hover, .ctp-choix__retirer:focus-visible { color: var(--accent); }
.ctp-choix__liste { position: absolute; top: 100%; left: 0; right: 0; z-index: var(--z-dropdown); max-height: 18rem; overflow-y: auto;
  margin: var(--space-1) 0 0; padding: var(--space-1); list-style: none;
  background: var(--bg-2); border: 1px solid var(--border); border-radius: var(--radius); box-shadow: 0 12px 28px var(--overlay-bg); }
.ctp-choix__liste[hidden] { display: none; }
.ctp-choix__option { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); padding: var(--space-2); border-radius: var(--radius-sm); cursor: pointer; }
.ctp-choix__option:hover, .ctp-choix__option.is-actif { background: color-mix(in srgb, var(--accent) 14%, transparent); }
.ctp-choix__nom { font-weight: 600; }
.ctp-choix__vide { padding: var(--space-2); color: var(--dim); font-size: var(--fs-sm); }
/* [Correctifs tiers] fin */
/* [Base des pannes] début — la page #kb/pannes, la fiche d'une panne (public/js/assistant_pannes.js) et la tâche d'une réparation
   ouverte depuis la conversation de l'assistant (public/js/assistant.js), lot Résolution du 21/10/2026. Jetons seuls, espacements de
   l'échelle, clair et sombre par les jetons ; le reste est le socle (hx-*). Préfixes .apn- et .as-. */
.apn-tuiles { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 11rem), 1fr)); gap: var(--space-3); }
.apn-tuile { display: flex; flex-direction: column; gap: var(--space-1); min-width: 0; margin: 0; padding: var(--space-3);
  border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--bg-1); color: var(--text);
  font: inherit; text-align: left; }
button.apn-tuile { cursor: pointer; transition: border-color .14s ease; }
button.apn-tuile:hover { border-color: var(--accent); }
button.apn-tuile:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.apn-tuile__lib { overflow: hidden; color: var(--dim); font-size: var(--fs-sm); white-space: nowrap; text-overflow: ellipsis; }
.apn-tuile__val { font-size: var(--fs-xl); font-weight: 700; line-height: 1.15; font-variant-numeric: tabular-nums; white-space: nowrap; }
.apn-tuile__note { color: var(--dim); font-size: var(--fs-xs); }
/* La fiche : la colonne principale (le contenu) et celle de droite (mesures, versions, cas, journal) ; une seule sous 900 px. */
.apn-fiche { display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); gap: var(--space-6); align-items: start; }
@media (max-width: 900px) { .apn-fiche { grid-template-columns: minmax(0, 1fr); } }
.apn-colonne { display: flex; flex-direction: column; gap: var(--space-4); min-width: 0; }
.apn-section { display: flex; flex-direction: column; gap: var(--space-2); min-width: 0; }
.apn-colonne > .apn-section + .apn-section { padding-top: var(--space-4); border-top: 1px solid var(--border); }
.apn-etapes { display: flex; flex-direction: column; gap: var(--space-3); margin: 0; padding-left: var(--space-6); font-size: var(--fs-sm); }
.apn-etapes > li { min-width: 0; overflow-wrap: anywhere; }
.apn-liste { display: flex; flex-direction: column; gap: var(--space-1); margin: 0; padding: 0; list-style: none; font-size: var(--fs-sm); }
.apn-liste > li { min-width: 0; overflow-wrap: anywhere; }
.apn-citation { margin: 0; padding-left: var(--space-3); border-left: 2px solid var(--border-strong); color: var(--text); overflow-wrap: anywhere; }
/* La fiche d'une tâche (tasks-queue.js) est un voile posé hors de la pile de showModal : ouverte depuis la conversation, elle passe
   au-dessus de sa fenêtre (la pile part de --z-portal). */
.hx-voile.as-voile-dessus { z-index: calc(var(--z-portal) + 100); }
/* Lot B, 21/10/2026 (public/js/assistant_pannes_edition.js) : l'éditeur guidé d'une fiche. Préfixe .ape-, jetons seuls. */
/* Les motifs des contrôles du serveur sous un champ : une ligne par motif (.hx-erreur). */
.ape-motifs { display: flex; flex-direction: column; gap: var(--space-1); min-width: 0; }
/* Les étapes, numérotées par .hx-numero : la liste n'a pas de puce ; celle qu'un motif vise prend le ton de l'erreur. */
.ape-etapes { display: flex; flex-direction: column; gap: var(--space-3); margin: 0; padding: 0; list-style: none; }
.ape-etape { min-width: 0; }
.ape-etape.ape-fautif { border-color: color-mix(in srgb, var(--color-crit) 55%, var(--border)); }
/* Les étiquettes retirables (mots, liens, cibles, signaux d'une étape) : un badge et sa croix. */
.ape-puces { display: flex; flex-wrap: wrap; gap: var(--space-1); min-width: 0; }
.ape-puce { gap: var(--space-1); }
.ape-puce__retirer { display: inline-flex; align-items: center; padding: 0; border: 0; border-radius: 50%; background: transparent; color: inherit; cursor: pointer; }
.ape-puce__retirer:hover { color: var(--accent); }
.ape-puce__retirer:focus-visible { color: var(--accent); outline: 2px solid var(--accent); outline-offset: 1px; }
.ape-puce__retirer:disabled { opacity: .5; cursor: not-allowed; }
/* Les cases d'une liste fermée (faits à lire, motifs d'échec d'une réparation) : en colonnes de 12rem, une sur un téléphone. */
.ape-cases { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 12rem), 1fr)); gap: var(--space-2) var(--space-4); }
/* Une liste éditable (un signal et son rôle, une réponse rapide) : un filet entre deux lignes. */
.ape-lignes { gap: 0; }
.ape-lignes > li { padding: var(--space-2) 0; border-bottom: 1px solid var(--border); }
.ape-lignes > li:last-child { border-bottom: 0; }
/* Un volet replié (textes anglais, aperçu, contenu proposé) : son titre se prend à la souris et au clavier. */
.ape-pli > summary { cursor: pointer; }
.ape-pli > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Le pied de l'éditeur (Annuler, Enregistrer, Valider) reste au bas de la fenêtre pendant le défilement ; il couvre la marge
   intérieure de la fenêtre (--space-6), au même fond qu'elle dans les deux thèmes. */
.ape-pied { position: sticky; bottom: calc(-1 * var(--space-6)); z-index: 1; margin: 0 calc(-1 * var(--space-6)) calc(-1 * var(--space-6));
  padding: var(--space-3) var(--space-6) var(--space-6); border-top: 1px solid var(--border); background: var(--bg-2); }
/* [Base des pannes] fin */

/* ── Sessions IA : les images jointes (21/10/2026, public/js/ai-images.js) ─────────────────────────────────────────────
   « Joindre une image » (dans la rangée des boutons d'une session, ou en rangée sous une demande), la zone du motif d'un
   moteur qui ne lit pas les images et des vignettes avant l'envoi (cachée tant qu'elle est vide : sans image, la saisie
   garde sa hauteur), la saisie pendant un glisser-déposer, et les vignettes du fil. Jetons seulement : lisible dans les
   deux thèmes. Dans une bulle (.bubble : white-space pre-wrap), le fil repasse en normal. */
.ia-img-barre { display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-2); margin: var(--space-2) 0; }
.ia-img-boutons { display: inline-flex; align-items: center; gap: var(--space-2); }
.ia-img-aide { color: var(--dim); font-size: var(--fs-xs); white-space: nowrap; }
.ia-img-boutons--refus .ia-img-aide { display: none; }
.ia-img-zone { display: flex; flex-direction: column; gap: var(--space-2); }
.ia-img-motif { color: var(--dim); font-size: var(--fs-sm); }
/* Sur un téléphone ou une fenêtre étroite : l'icône seule (le libellé reste lu par un lecteur d'écran), et pas d'aide
   « Ctrl+V » — la rangée des boutons d'une session ne passe pas à la ligne. */
@media (max-width: 600px), (pointer: coarse) {
  .ia-img-aide { display: none; }
}
@media (max-width: 600px) {
  .ia-img-joindre { padding-left: var(--space-2); padding-right: var(--space-2); }
  .ia-img-libelle { position: absolute; width: 1px; height: 1px; margin: 0; padding: 0; overflow: hidden;
    clip-path: inset(50%); white-space: nowrap; border: 0; }
}
.ia-img-apercus { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.ia-img-apercus:empty { display: none; }
.ia-img-apercu { display: flex; flex-direction: column; gap: var(--space-1); width: 112px; }
.ia-img-apercu__img { display: block; width: 112px; height: 84px; object-fit: cover;
  border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--bg-2); }
.ia-img-apercu .ia-img-retirer { width: 100%; }
.ia-img-apercu--attente { align-items: center; justify-content: center; height: 84px;
  border: 1px dashed var(--border); border-radius: var(--radius-sm); }
.ia-img-depot { outline: 2px dashed var(--accent); outline-offset: calc(-1 * var(--space-1)); }
.ia-img-fil { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-top: var(--space-2); white-space: normal; }
.ia-img-element { display: inline-flex; flex-direction: column; align-items: flex-start; gap: var(--space-1); }
.ia-img-vignette { display: block; border-radius: var(--radius-sm); line-height: 0; }
.ia-img-vignette img { display: block; max-width: 200px; max-height: 150px; width: auto; height: auto;
  border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--bg-2); cursor: zoom-in; }
.ia-img-vignette:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.ia-img-etat { font-size: var(--fs-xs); }
@media (max-width: 600px) {
  .ia-img-vignette img { max-width: 140px; max-height: 105px; }
}
/* [Sessions IA : images] fin */
