/* 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;
  --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;     /* notifications 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);
  --border: rgba(148, 163, 220, 0.10);
  --border-strong: rgba(148, 163, 220, 0.22);
  --text: #f1f5fc;
  --muted: #97a3c4;
  --dim: #5a6688;

  /* Accents : violet electric → cyan, plus moderne que le bleu basique */
  --accent: #7c5cff;       /* violet primaire Foxchip */
  --accent-2: #b794ff;     /* violet hover */
  --accent-cyan: #22d3ee;  /* cyan accent secondaire (data/tech) */
  --accent-glow: rgba(124, 92, 255, 0.45);
  --gradient-primary: linear-gradient(135deg, #7c5cff 0%, #5b6fff 50%, #22d3ee 100%);
  --gradient-card: linear-gradient(135deg, rgba(124,92,255,0.06), rgba(34,211,238,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);
  --state-neutral:      #64748b;
  --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 */

  /* 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(124,92,255,0.18);

  --scheme: dark;
}

/* Light theme — meme identite violet/cyan mais sur fond clair */
:root[data-theme="light"] {
  --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(124, 92, 255, 0.12);
  --border-strong: rgba(124, 92, 255, 0.22);
  --text: #0f172a;
  --muted: #475569;
  --dim: #94a3b8;

  --accent: #6d4dff;
  --accent-2: #5b6fff;
  --accent-cyan: #0ea5e9;
  --accent-glow: rgba(109, 77, 255, 0.18);
  --gradient-primary: linear-gradient(135deg, #6d4dff 0%, #5b6fff 50%, #0ea5e9 100%);
  --gradient-card: linear-gradient(135deg, rgba(109,77,255,0.04), rgba(14,165,233,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: #b45309;
  --state-crit: #b91c1c;
  --state-info: #1d4ed8;

  --scheme: light;
}

:root[data-theme="light"] body {
  background:
    linear-gradient(rgba(109,77,255,0.04) 1px, transparent 1px) 0 0 / 60px 60px,
    linear-gradient(90deg, rgba(109,77,255,0.04) 1px, transparent 1px) 0 0 / 60px 60px,
    radial-gradient(circle at 15% -5%, rgba(109,77,255,0.10), transparent 45%),
    radial-gradient(circle at 85% 100%, rgba(14,165,233,0.08), transparent 45%),
    var(--bg-0) !important;
}
:root[data-theme="light"] select option { background-color: #ffffff; color: var(--text); }
:root[data-theme="light"] ::-webkit-scrollbar-thumb { background: rgba(109,77,255,0.20); background-clip: padding-box; }
:root[data-theme="light"] ::selection { background: rgba(109,77,255,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. */
:root[data-theme="light"] .nav a.active,
:root[data-theme="light"] .nav-dd-trigger.active,
:root[data-theme="light"] .nav-side a.active {
  color: #4c1d95 !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(124,92,255,0.025) 1px, transparent 1px) 0 0 / 60px 60px,
    linear-gradient(90deg, rgba(124,92,255,0.025) 1px, transparent 1px) 0 0 / 60px 60px,
    /* Gradient orbs */
    radial-gradient(circle at 15% -5%, rgba(124,92,255,0.18), transparent 45%),
    radial-gradient(circle at 85% 100%, rgba(34,211,238,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: rgba(2,6,23,0.55);
  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: rgba(124,92,255,0.18);
  border-radius: 10px;
  border: 2px solid transparent;
  background-clip: padding-box;
}
::-webkit-scrollbar-thumb:hover { background: rgba(124,92,255,0.35); background-clip: padding-box; border: 2px solid transparent; }
::selection { background: rgba(124,92,255,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: #0b1224; color: var(--text); }
select option:checked, select option:hover { background-color: #1e293b; color: #93c5fd; }

/* 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. */
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(28%) sepia(96%) saturate(5000%) hue-rotate(353deg) brightness(95%) contrast(95%);
}

/* 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));
  border-bottom: 1px solid transparent;
  /* 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; margin-right: 40px; }
.brand-logo-wrap { position: relative; display: inline-block; padding-right: 8px; padding-bottom: 4px; }
.brand-logo {
  height: 56px; 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. Violet = RMM, Orange = PSA, Vert = GLUE (v0.21.5). */
.topbar { border-bottom: 3px solid transparent; transition: border-color .3s ease, box-shadow .3s ease; }
body[data-module="rmm"]  .topbar { border-bottom-color: #7c5cff; box-shadow: 0 2px 8px rgba(124,92,255,0.18); }
body[data-module="psa"]  .topbar { border-bottom-color: #f59e0b; box-shadow: 0 2px 8px rgba(245,158,11,0.18); }
body[data-module="glue"] .topbar { border-bottom-color: #16a34a; box-shadow: 0 2px 8px rgba(22,163,74,0.20); }
/* 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/GLUE cote a cote sous le logo.
   Chaque bouton garde sa couleur (violet/orange/vert) en permanence.
   L'actif est distingue par un trait colore sous le bouton. */
.brand-mod-switcher {
  display: flex;
  gap: 4px;
  align-items: stretch;
  width: 100%;
  padding: 0;
}
.brand-mod-btn {
  flex: 1;
  border: 0;
  color: #fff;
  padding: 5px 0 6px;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.8px;
  cursor: pointer;
  border-radius: 4px;
  position: relative;
  opacity: 0.55;
  transition: opacity .15s ease, transform .15s ease, box-shadow .15s ease;
}
/* Couleurs fixes par module (toujours visibles) */
.brand-mod-btn[data-set-mod="rmm"]  { background: linear-gradient(135deg, #7c5cff, #5b3df0); }
.brand-mod-btn[data-set-mod="psa"]  { background: linear-gradient(135deg, #f59e0b, #ea580c); }
.brand-mod-btn[data-set-mod="glue"] { background: linear-gradient(135deg, #22c55e, #15803d); }
.brand-mod-btn:hover { opacity: 0.85; transform: translateY(-1px); }
.brand-mod-btn.active { opacity: 1; }
.brand-mod-btn[data-set-mod="rmm"].active  { box-shadow: 0 0 10px rgba(124,92,255,0.55); }
.brand-mod-btn[data-set-mod="psa"].active  { box-shadow: 0 0 10px rgba(245,158,11,0.55); }
.brand-mod-btn[data-set-mod="glue"].active { box-shadow: 0 0 10px rgba(34,197,94,0.55); }
/* 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;
}
.brand-mod-btn[data-set-mod="rmm"].active::after  { background: linear-gradient(90deg, #7c5cff, #5b3df0); box-shadow: 0 0 8px rgba(124,92,255,0.7); }
.brand-mod-btn[data-set-mod="psa"].active::after  { background: linear-gradient(90deg, #f59e0b, #ea580c); box-shadow: 0 0 8px rgba(245,158,11,0.7); }
.brand-mod-btn[data-set-mod="glue"].active::after { background: linear-gradient(90deg, #22c55e, #15803d); box-shadow: 0 0 8px rgba(34,197,94,0.7); }
/* v0.21.5 Phase 2 — éditeur de documents Glue */
#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(124,92,255,0.05); }
#gd-editor pre { background: #0f172a; padding: 10px 14px; border-radius: 5px; overflow-x: auto; font-family: monospace; font-size: 0.85rem; }
#gd-editor code { background: rgba(124,92,255,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(124,92,255,0.15); border-color: var(--accent); }
/* Legacy fallback (au cas où certaines pages utilisent encore logo-dot) */
.logo-dot {
  width: 12px; height: 12px; border-radius: 50%;
  background: var(--accent); box-shadow: 0 0 12px var(--accent);
}
.brand-name { font-weight: 800; letter-spacing: -0.3px; font-size: 1.1rem; }
.brand-sub { color: var(--muted); font-size: 0.78rem; text-transform: uppercase; letter-spacing: 1.2px; }

/* (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;
  overflow-x: hidden;
  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(124,92,255,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; }
}
/* 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 { 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(124,92,255,0.025),
    rgba(124,92,255,0.025) 10px,
    transparent 10px,
    transparent 20px
  );
  padding: 8px;
  border-radius: 12px;
  border: 2px dashed rgba(124,92,255,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(59,130,246,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(124,92,255,0.45);
  background: linear-gradient(135deg, rgba(124,92,255,0.10), rgba(34,211,238,0.04));
}
.picker-btn:active { transform: scale(0.985); }
.picker-btn:focus-visible {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(124,92,255,0.20);
}
.picker-btn.is-open {
  border-color: var(--accent);
  background: linear-gradient(135deg, rgba(124,92,255,0.15), rgba(34,211,238,0.06));
  box-shadow: 0 0 0 3px rgba(124,92,255,0.18);
}
.picker-btn.has-value {
  background: linear-gradient(135deg, rgba(59,130,246,0.16), rgba(124,92,255,0.06));
  border-color: rgba(59,130,246,0.55);
  color: #cfe1ff;
  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: rgba(13, 19, 38, 0.97);
  border: 1px solid rgba(124,92,255,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); }
}
.picker-search {
  padding: 8px; border-bottom: 1px solid rgba(255,255,255,0.06);
  background: rgba(0,0,0,0.18);
}
.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(124,92,255,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: rgba(255,255,255,0.08); border-radius: 4px;
}
.picker-list::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,0.16); }
.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: rgba(255,255,255,0.05); padding: 2px 7px; border-radius: 10px;
  flex-shrink: 0;
}
.picker-item-check {
  width: 16px; height: 16px; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  color: #3b82f6; font-weight: 700; font-size: 0.95rem;
  opacity: 0;
}
.picker-item:hover,
.picker-item.is-keyboard {
  background: rgba(124,92,255,0.16); color: #fff;
}
.picker-item.is-active {
  background: linear-gradient(90deg, rgba(59,130,246,0.20), rgba(124,92,255,0.10));
  color: #cfe1ff; 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(124,92,255,0.18), rgba(34,211,238,0.10));
  color: #fff;
  box-shadow: 0 0 0 1px rgba(124,92,255,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(124,92,255,0.18), rgba(34,211,238,0.10));
  color: #fff;
  box-shadow: 0 0 0 1px rgba(124,92,255,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: #dc2626; color: #fff; padding: 1px 6px; border-radius: 10px;
  font-size: 0.72rem; font-weight: 600; margin-left: 2px;
}
.nav-dd-rollup--crit { background: #dc2626; color: #fff; }  /* bloquants (failed/offline + RustDesk) */
.nav-dd-rollup--warn { background: #ca8a04; color: #fff; }  /* retards (late) */
.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); color: var(--accent); }

.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 rgba(255,255,255,0.05);
  position: static;
  overflow: visible;
}
.sidebar-tenant {
  padding: 10px 14px; margin-bottom: 14px;
  background: linear-gradient(135deg, rgba(124,92,255,0.18), rgba(34,211,238,0.10));
  border: 1px solid rgba(124,92,255,0.32);
  border-radius: var(--radius);
  font-size: 0.72rem; color: var(--text);
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.05), 0 4px 14px -8px var(--accent-glow);
}
.sidebar-tenant .lbl { color: var(--muted); font-size: 0.62rem; text-transform: uppercase; letter-spacing: 1.4px; font-weight: 600; }
.sidebar-tenant .name {
  font-weight: 700; font-size: 0.92rem; margin-top: 3px;
  background: var(--gradient-primary);
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent;
}
.nav-side { display: flex; flex-direction: column; gap: 3px; }
.nav-side a {
  padding: 10px 14px; border-radius: var(--radius); color: var(--muted); font-size: 0.88rem; font-weight: 500;
  display: flex; align-items: center; 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(124,92,255,0.18), rgba(124,92,255,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. */
.tab-group-menu {
  background-color: #1e293b !important;
  background-image: none !important;
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
  position: fixed !important;
  z-index: 999999 !important;
  border: 1px solid #7c5cff !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;
}
/* v0.13.65 — restaure le rendu couleur natif des emoji en debut de h1.
   Sans ca, -webkit-text-fill-color: transparent (cf. gradient ci-dessus)
   transforme l emoji en glyphe transparent et il sort en carre blanc
   illisible sur theme sombre. ::first-letter cible le 1er caractere
   (emoji single-codepoint) et reinitialise TOUTES les proprietes qui
   cassent le rendu couleur natif (gradient clip + text-fill).
   IMPORTANT : NE PAS poser color: ici (forcerait un rendu monochrome
   sur certains navigateurs Chromium). On laisse l emoji se rendre
   avec sa palette interne (Segoe UI Emoji sur Windows, etc.). */
/* h1::first-letter RETIRE (15/06) : le degrade sur h1 a ete supprime (titre en
   couleur unie var(--text)), donc plus besoin de reinitialiser le 1er caractere.
   Cette regle forcait au contraire la 1re LETTRE des h1 SANS emoji (ex hostname
   "ACCEUIL1") dans la police emoji + color:initial -> 1re lettre d'une couleur/
   police differente du reste. Les emojis de tete restent geres par .h1-emoji-fix. */
/* Fallback robuste : common.js fixH1Emojis() wrap l emoji de tete dans
   un <span class="h1-emoji-fix">. Cette classe reset toutes les
   proprietes qui cassent le rendu couleur natif. ::first-letter peut
   echouer sur certains emojis multi-codepoint (ZWJ sequences) — le
   wrap JS est plus robuste. */
.h1-emoji-fix {
  -webkit-text-fill-color: initial;
  background-image: none;
  background-color: transparent;
  -webkit-background-clip: initial;
  background-clip: initial;
  color: initial;
  font-family: 'Apple Color Emoji', 'Segoe UI Emoji', 'Noto Color Emoji', emoji, sans-serif;
  /* Alignement vertical avec le texte du h1 :
     - pas de display:inline-block (cassait la baseline + descendait l icone)
     - margin-right via espace insere par fixH1Emojis (cf common.js)
     - Segoe UI Emoji rend lemoji plus bas que les capitales du texte ;
       on remonte de 0.12em pour aligner le centre optique. */
  vertical-align: baseline;
  position: relative;
  top: -0.12em;
}
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 (helper JS pageHeader()).
   Wrapper flex : titre h1 + sous-titre .subtitle à gauche, actions .page-actions à droite. */
.page-head { display: flex; justify-content: space-between; align-items: flex-start; gap: var(--space-3); flex-wrap: wrap; margin-bottom: var(--space-4); }
.page-head > .page-head-titles { min-width: 220px; flex: 1; }
.page-head h1 { margin: 0; }
.page-head .subtitle { margin: 4px 0 0; }
.page-actions { display: flex; gap: var(--space-2); align-items: center; flex-wrap: wrap; }

/* 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(124,92,255,0.40), rgba(34,211,238,0.30), transparent);
  opacity: .6;
  pointer-events: none;
}
/* v0.15 (harmonisation 02/06) — Modificateurs de .card : remplacent les overrides
   inline de padding et les border-left de statut en hex arbitraires. */
.card--tight { padding: var(--space-3); }
.card--flush { padding: 0; }
.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. */
.widget {
  --w-accent: #7c5cff;
  --w-accent-rgb: 124, 92, 255;
  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: #fbbf24; text-shadow: 0 0 24px rgba(251, 191, 36, 0.40); }
.widget-metric.is-crit { color: #f87171; text-shadow: 0 0 24px rgba(248, 113, 113, 0.40); }
.widget-metric.is-ok   { color: #4ade80; text-shadow: 0 0 24px rgba(74, 222, 128, 0.40); }
.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;
}
.widget-divider {
  height: 1px; margin: 12px 0;
  background: linear-gradient(90deg, transparent, rgba(var(--w-accent-rgb), 0.40), transparent);
  border: none;
}
.widget-foot {
  margin-top: 12px; padding-top: 10px;
  border-top: 1px solid rgba(var(--w-accent-rgb), 0.20);
  font-size: 0.74rem; color: var(--muted);
  display: flex; justify-content: space-between; align-items: center; gap: 8px;
  position: relative; z-index: 1;
}

/* 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;
}

/* Backwards compat : .card-clickable garde l'ancien hover (pour cards non migrées) */
.card-clickable {
  position: relative;
  transition: transform .15s ease, border-color .2s ease, box-shadow .2s ease;
}
.card-clickable:hover {
  transform: translateY(-2px);
  border-color: rgba(124,92,255,0.40);
  box-shadow: 0 8px 24px -10px rgba(124,92,255,0.30);
}

.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;
}
/* 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(124,92,255,0.05), rgba(34,211,238,0.025));
  border: 1px solid rgba(124,92,255,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; }
.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 */
input[type="datetime-local"], input[type="date"], input[type="time"] {
  color-scheme: dark; /* 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 {
  filter: invert(1) brightness(1.2);
  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(124, 92, 255, 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"]),
label.checkbox-label, label.inline-label {
  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: rgba(10,15,36,0.6);
  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: rgba(10,15,36,0.85);
  box-shadow: 0 0 0 3px rgba(124,92,255,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 */
button.primary {
  background: var(--gradient-primary);
  background-size: 200% 200%;
  background-position: 0% 50%;
  color: white; 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: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:active { transform: translateY(0); }
button.primary:disabled { opacity: .4; cursor: not-allowed; transform: none; box-shadow: none; }

button.ghost {
  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:hover {
  background: var(--surface-2); color: var(--text); border-color: var(--border-strong);
}
button.danger {
  background: rgba(239,68,68,0.12); color: #fca5a5;
  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:hover {
  background: rgba(239,68,68,0.22); color: #fca5a5;
  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, .btn-sm { padding: 4px 10px; font-size: var(--fs-sm); border-radius: var(--radius-sm); }
button.btn-lg, .btn-lg { padding: 12px 22px; font-size: var(--fs-md); }
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, .toolbar-btn {
  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, .toolbar-btn:hover {
  background: var(--surface-2); border-color: var(--border-strong); color: var(--text);
}
button.toolbar.active, .toolbar-btn.active,
button.toolbar[data-on="1"], .toolbar-btn[data-on="1"] {
  border-color: var(--accent); background: var(--color-accent-soft-bg); color: var(--accent);
}
button.toolbar[disabled], .toolbar-btn[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 13px; 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(124,92,255,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(124,92,255,0.10), var(--surface) 70%); border-radius:10px; padding:15px 17px; margin-bottom:14px; }
.dp-reco { font-size:0.7rem; font-weight:700; padding:3px 9px; border-radius:7px; background:rgba(124,92,255,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:10px 13px; 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 14px 13px; 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(124,92,255,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(124,92,255,0.06), rgba(34,211,238,0.03));
  cursor: pointer;
}
tbody tr:last-child td { border-bottom: none; }

/* Badges — v0.13.2 : pulse subtil sur "online" pour effet "live" */
.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;
}
.badge.online   {
  background: rgba(34,197,94,0.12); color: #86efac;
  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: rgba(148,163,184,0.12); color: #94a3b8;
  border-color: rgba(148,163,184,0.20);
}
.badge.disabled {
  background: rgba(239,68,68,0.12); color: #fca5a5;
  border-color: rgba(239,68,68,0.25);
}
.badge.windows  {
  background: rgba(59,130,246,0.12); color: #93c5fd;
  border-color: rgba(59,130,246,0.22);
}
.badge.android  {
  background: rgba(34,197,94,0.12); color: #86efac;
  border-color: rgba(34,197,94,0.22);
}
.badge.linux    {
  background: rgba(245,158,11,0.12); color: #fcd34d;
  border-color: rgba(245,158,11,0.22);
}
.badge.ios      {
  background: rgba(167,139,250,0.12); color: #c4b5fd;
  border-color: rgba(167,139,250,0.22);
}
.badge.macos    {
  background: rgba(167,139,250,0.12); color: #c4b5fd;
  border-color: rgba(167,139,250,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); }

/* 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: #020618; 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: #86efac; }
.console .err { color: #fca5a5; }
.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(59,130,246,0.12); border-color: rgba(59,130,246,0.25); }
.bubble.assistant { align-self: flex-start; }
.bubble.tool {
  background: #020618; border: 1px solid var(--border-strong);
  font-family: 'Cascadia Code', monospace; font-size: 0.78rem; color: #cbd5e1;
  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; }
.tool-call {
  background: rgba(59,130,246,0.08); border: 1px dashed rgba(59,130,246,0.3);
  border-radius: 8px; padding: 8px 12px; font-family: monospace; font-size: 0.78rem;
}

/* Toast — v0.13.2 : glassmorphism + glow par type */
.toast {
  position: fixed; bottom: 24px; right: 24px;
  padding: 14px 20px; border-radius: var(--radius);
  background: linear-gradient(135deg, rgba(15,21,48,0.92), rgba(10,15,36,0.92));
  border: 1px solid var(--border-strong); color: var(--text);
  opacity: 0; transform: translateY(20px); transition: all .25s ease;
  font-size: 0.88rem; z-index: 100; 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); }
.toast.error {
  border-color: rgba(239,68,68,0.45); color: #fca5a5;
  box-shadow: 0 12px 40px -10px var(--red-glow);
}
.toast.ok {
  border-color: rgba(34,197,94,0.45); color: #86efac;
  box-shadow: 0 12px 40px -10px var(--green-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: #fff; border-bottom-color: var(--accent);
  text-shadow: 0 0 16px var(--accent-glow);
}

/* Spinner amélioré v0.13.2 */
.spinner {
  display: inline-block; width: 14px; height: 14px;
  border: 2px solid rgba(124,92,255,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(124,92,255,0.10);
  border: 1px solid rgba(124,92,255,0.18);
  padding: 1px 6px;
  border-radius: 4px;
  font-family: 'Cascadia Code', 'JetBrains Mono', 'Consolas', monospace;
  font-size: 0.84em;
  color: #d8b4fe;
}
pre code { background: transparent; border: none; padding: 0; color: inherit; }

/* Hover lift utility */
.hover-lift { transition: transform .2s ease, box-shadow .2s ease; }
.hover-lift:hover { transform: translateY(-2px); box-shadow: 0 8px 24px -8px var(--accent-glow); }

/* Reduce motion preference */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* 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; }
.integration-disabled-banner {
  padding: 14px 18px;
  border-radius: 10px;
  background: rgba(245, 158, 11, 0.10);
  border: 1px solid rgba(245, 158, 11, 0.35);
  color: var(--text);
  font-size: 0.88rem;
  margin: 0 0 14px;
}
.integration-disabled-banner strong { color: #d97706; }

/* V1 Cockpit fiche equipement : 2 colonnes -> 1 sur ecran etroit */
@media (max-width: 980px){ .dev360-grid{ grid-template-columns:1fr !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;
}

/* ============================================================
   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.
   ============================================================ */
.nav-burger {
  display: none; align-items: center; justify-content: center;
  width: 42px; height: 42px; padding: 0; flex-shrink: 0;
  background: transparent; border: 1px solid var(--border); border-radius: 8px;
  color: var(--text); font-size: 1.3rem; cursor: pointer;
}
/* 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 { margin-right: 4px; }
  .brand-logo { height: 26px; }
  .brand-mod-btn { padding: 1px 7px; font-size: 0.66rem; }
  #btn-theme { margin-left: auto; }
  .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) */
  .nav-dd { width: 100%; flex-direction: column; align-items: stretch; }
  .nav-dd-trigger { width: 100%; justify-content: flex-start; min-height: 44px; font-size: 1rem; }
  .nav-dd-panel { position: static; min-width: 0; border: none; box-shadow: none; background: transparent; padding-left: 18px; }
  .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) {
  /* Glue 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; }
#nav-alerts-top-badge {
  position: absolute; top: 0; right: 4px;
  /* !important : le badge a des styles INLINE (font-size/padding) a neutraliser
     pour qu'il ait EXACTEMENT la meme taille que les rollups Supervision. */
  margin-left: 0 !important; padding: 0 4px !important; font-size: 0.6rem !important;
  line-height: 1.5 !important; 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, .dev360-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 */
  img, svg, 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 */
  }
  /* 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 */
  .tbl-telco      { min-width: 760px !important; }
  .tbl-discovery  { min-width: 880px !important; table-layout: auto !important; } /* 9 col + colgroup px */

  /* ===== 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; }

  /* ===== 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;
  }

  /* ===== 14. Masquage colonnes secondaires (option, voir §4) ======= */
  .col-sec { display: none !important; }
}

/* ====================================================================== *
 *  PALIER ETROIT 420px — topbar : action Deconnexion hors-ecran
 * ====================================================================== */
@media (max-width: 420px) {
  /* Remplace le libelle "Deconnexion" par une icone power -> ~60px gagnes */
  #btn-logout { font-size: 0 !important; padding: 6px 10px !important; }
  #btn-logout::before { content: "\23FB"; font-size: 1rem; }
  .user-zone .name { max-width: 70px !important; }
  .brand-mod-btn { padding: 2px 5px !important; }
}
/* ==================== FIN PATCH RESPONSIVE v2 ========================= */