:root {
  --bg: #0A0E14;
  --surface: #121820;
  --card: #161E28;
  --border: #1F2A38;
  --accent-cyan: #0AADCC;
  --accent-blue: #1059A4;
  --status-active: #29B573;
  --status-pending: #E8A020;
  --status-disconnected: #D94040;
  --status-neutral: #566774;
  --text-primary: #E6EDF3;
  --text-secondary: #8A97A6;
  --font-ui: 'Inter', system-ui, -apple-system, sans-serif;
  --font-mono: 'JetBrains Mono', 'Consolas', monospace;

  /* Echelle d'espacement (base 4px) - toute marge/padding/gap de ce fichier
     doit venir d'ici ; aucune valeur en dur ne doit rester dans les regles
     ci-dessous (0 excepte, qui n'est pas un espacement mais son absence). */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 24px;
  --space-6: 32px;
}

* { box-sizing: border-box; }

/* L'attribut hidden doit TOUJOURS masquer, quoi que declare par ailleurs
   une classe posee sur le meme element. Sans ce garde-fou, une regle
   d'auteur du type ".x { display: flex }" gagne contre le [hidden]{display:
   none} par defaut du navigateur des qu'elle existe (l'origine "auteur"
   l'emporte sur l'origine "user agent", independamment de la specificite) -
   c'est exactement ce qui a casse la bascule des onglets (.view{display:
   flex} empechait [hidden] de masquer les vues inactives, les 3 s'affichaient
   empilees). Regle globale plutot que corrective au cas par cas : au moins
   3 elements etaient deja touches (.view, .topology-breadcrumb,
   .topology-chart-empty) et rien n'empeche qu'un futur composant retombe
   dans le meme piege (voir aussi .operator-menu, ajoute plus tard). */
[hidden] {
  display: none !important;
}

html, body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--text-primary);
  font-family: var(--font-ui);
  font-size: 13px;
  line-height: 1.4;
}

/* Colonne pleine hauteur pour que .app-shell (et donc .sidebar) puisse
   reellement occuper tout l'espace vertical disponible sous le header
   sticky - necessaire pour que le bloc bas de la sidebar (operateur +
   version) s'ancre au bas reel de l'ecran (margin-top:auto) plutot qu'au
   bas de son propre contenu. N'affecte pas login.html : .login-page fixe
   son propre align-items/justify-content qui l'emportent pour le centrage
   de la carte, quel que soit flex-direction herite de cette regle. */
.app-page {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

/* ---------- Header ---------- */

.topbar {
  position: sticky;
  top: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--space-3) var(--space-5);
  background: var(--surface);
  border-bottom: 1px solid var(--border);
}

.brand {
  display: flex;
  align-items: center;
}

/* Logo : mono-couleur (--text-primary), plus de cyan ici - le cyan reste
   reserve aux elements actifs/interactifs. Poids fort + letter-spacing
   legerement negatif pour un rendu compact et delibere. */
.brand-wordmark {
  font-size: 19px;
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--text-primary);
}

.live-indicator {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--text-secondary);
}

.live-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--status-active);
  flex-shrink: 0;
}

.live-dot--error { background: var(--status-disconnected); }

.topbar-right {
  display: flex;
  align-items: center;
  gap: var(--space-4);
}

/* ---------- Selecteur de contexte client (header) ----------
   Pilote tous les onglets simultanement (voir app.js, globalClientContext)
   - remplace les selecteurs client locaux qui existaient auparavant sur
   Alertes et Correlation MITRE. */

.client-context {
  position: relative;
}

.client-context-trigger {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  appearance: none;
  background: transparent;
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: var(--space-1) var(--space-3);
  color: var(--text-primary);
  font-family: var(--font-ui);
  font-size: 13px;
  cursor: pointer;
}

.client-context-trigger:hover:not(:disabled) { border-color: var(--accent-cyan); }

/* Verrouillage programmatique (fondation du futur portail client, voir
   lockGlobalClientContext) : disabled natif rend le declencheur reellement
   non-interactif (aucun evenement click), le chevron disparait pour
   signaler visuellement qu'il n'y a rien a derouler. */
.client-context-trigger:disabled {
  cursor: default;
  opacity: 0.85;
}

.client-context-trigger.is-locked .client-context-chevron {
  display: none;
}

.client-context-icon,
.client-context-chevron {
  width: 14px;
  height: 14px;
  color: var(--text-secondary);
  flex-shrink: 0;
}

.client-context-menu {
  position: absolute;
  top: calc(100% + var(--space-2));
  left: 0;
  width: 280px;
  max-height: 360px;
  display: flex;
  flex-direction: column;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 6px;
  z-index: 150;
  animation: dropdown-menu-in 150ms ease;
  overflow: hidden;
}

.client-context-search {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}

.client-context-search .icon {
  width: 14px;
  height: 14px;
  color: var(--text-secondary);
  flex-shrink: 0;
}

.client-context-search input {
  flex: 1;
  min-width: 0;
  background: transparent;
  border: none;
  outline: none;
  color: var(--text-primary);
  font-family: var(--font-ui);
  font-size: 13px;
}

.client-context-search input::placeholder { color: var(--text-secondary); }

.client-context-list {
  overflow-y: auto;
  padding: var(--space-2);
}

.client-context-item {
  display: block;
  width: 100%;
  text-align: left;
  appearance: none;
  background: transparent;
  border: none;
  border-radius: 4px;
  padding: var(--space-2) var(--space-3);
  color: var(--text-primary);
  font-family: var(--font-ui);
  font-size: 13px;
  cursor: pointer;
}

.client-context-item:hover { background: var(--surface); }

.client-context-item.is-active {
  color: var(--accent-cyan);
  font-weight: 600;
}

.client-context-empty {
  padding: var(--space-3);
  text-align: center;
  color: var(--text-secondary);
  font-size: 12.5px;
}

/* Selecteur de plage temporelle : reutilise integralement les classes
   .client-context-* ci-dessus (meme trigger, meme panneau, meme item de
   liste) - seule la largeur differe (pas de champ de recherche, 3 options
   fixes seulement). */
.time-range-menu {
  width: 160px;
}

/* ---------- Login page ---------- */

.login-page {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
}

.login-card {
  width: 100%;
  max-width: 340px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: var(--space-6) var(--space-5);
}

.login-brand {
  justify-content: center;
  margin-bottom: var(--space-2);
}

.login-subtitle {
  margin: 0 0 var(--space-5);
  font-size: 11px;
  color: var(--text-secondary);
  text-align: center;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.login-form {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.field {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.field label {
  font-size: 11px;
  color: var(--text-secondary);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.field input {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 4px;
  color: var(--text-primary);
  font-family: var(--font-ui);
  font-size: 13px;
  padding: var(--space-2) var(--space-3);
  outline: none;
}

.field input:focus { border-color: var(--accent-cyan); }

.btn-primary {
  margin-top: var(--space-1);
  background: var(--accent-cyan);
  color: #06131A;
  border: none;
  border-radius: 4px;
  padding: var(--space-2) var(--space-3);
  font-family: var(--font-ui);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
}

.btn-primary:hover { background: #3BC2DC; }
.btn-primary:disabled { opacity: 0.6; cursor: default; }

.form-error {
  margin: var(--space-1) 0 0;
  font-size: 12px;
  color: var(--status-disconnected);
}

/* ---------- Layout ---------- */

/* Pas de max-width/margin:auto ici : la sidebar + le contenu doivent
   occuper 100% de la largeur du viewport (align-items:stretch, la valeur
   par defaut du flex-column parent .app-page, s'en charge des que rien ne
   plafonne la largeur). Un max-width couple a un margin:auto etait la
   cause racine du grand espace vide a droite constate en production. */
.app-shell {
  display: flex;
  flex: 1;
  width: 100%;
  transition: opacity 180ms ease;
}

.app-shell.content-hidden {
  opacity: 0;
}

.loading-indicator {
  display: none;
  position: fixed;
  inset: 0;
  align-items: center;
  justify-content: center;
  font-family: var(--font-ui);
  font-size: 12px;
  color: var(--text-secondary);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  pointer-events: none;
}

.app-shell.content-hidden ~ .loading-indicator {
  display: flex;
}

/* ---------- Sidebar ---------- */

/* Largeur reduite (64px, meme valeur que le pattern deja etabli sur
   AISAKA NOC) repliquee ici avec sa propre etat/persistance - voir
   SIDEBAR_COLLAPSED_KEY dans app.js, jamais partage avec le NOC.
   overflow:hidden evite tout debordement horizontal pendant la
   transition (les libelles disparaissent instantanement via .nav-label,
   seule la largeur s'anime). */
.sidebar {
  width: 200px;
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  padding: var(--space-4) 0;
  background: var(--surface);
  border-right: 1px solid var(--border);
  overflow: hidden;
  transition: width 150ms ease;
}

.sidebar.is-collapsed {
  width: 64px;
}

/* Meme padding horizontal ET meme alignement (flex-start par defaut) que
   .nav-item : l'icone doit tomber exactement sur le meme axe vertical que
   les icones de navigation en dessous. Le border-left transparent de 2px
   n'est pas decoratif ici : .nav-item en a un identique (indicateur d'etat
   actif) qui repousse son contenu de 2px vers l'interieur au-dela du
   padding - sans le reproduire (meme transparent), l'icone du toggle
   tombe 2px trop a gauche par rapport aux icones de nav malgre un padding
   nominal identique. Pas de margin-bottom isole non plus - le
   gap: var(--space-1) de .sidebar (deja applique entre tous les freres)
   suffit, un margin-bottom en plus aurait double cet espacement et fait
   paraitre le bouton detache du reste de la liste. */
.sidebar-toggle {
  appearance: none;
  background: transparent;
  border: none;
  border-left: 2px solid transparent;
  display: flex;
  align-items: center;
  justify-content: flex-start;
  color: var(--text-secondary);
  padding: var(--space-3) var(--space-4);
  cursor: pointer;
}

/* Replie : meme traitement que .nav-item, l'icone (seul contenu restant)
   se recentre au lieu de rester calee a gauche - coherence entre le
   toggle et les items de nav dans les deux etats. */
.sidebar.is-collapsed .sidebar-toggle {
  justify-content: center;
}

.sidebar-toggle:hover {
  color: var(--text-primary);
}

/* Une seule des deux icones a la fois - celle qui decrit l'ACTION
   disponible (replier/deplier), pas l'etat courant. */
.icon-expand { display: none; }
.sidebar.is-collapsed .icon-collapse { display: none; }
.sidebar.is-collapsed .icon-expand { display: block; }

.nav-item {
  appearance: none;
  background: transparent;
  border: none;
  border-left: 2px solid transparent;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  color: var(--text-secondary);
  font-family: var(--font-ui);
  font-size: 13px;
  text-align: left;
  padding: var(--space-3) var(--space-4);
  cursor: pointer;
}

/* Replie : le libelle disparait, l'icone (seule enfant visible restant)
   se recentre dans le meme padding symetrique plutot que de rester
   collee a gauche. */
.sidebar.is-collapsed .nav-item {
  justify-content: center;
}

.sidebar.is-collapsed .nav-label {
  display: none;
}

/* L'icone suit toujours la couleur du texte du bouton (currentColor dans
   le SVG) - jamais de couleur autonome sur l'icone elle-meme. */
.nav-icon {
  width: 17px;
  height: 17px;
  flex-shrink: 0;
}

.nav-item:hover {
  color: var(--text-primary);
  background: var(--card);
}

.nav-item.is-active {
  color: var(--text-primary);
  background: var(--card);
  border-left-color: var(--accent-cyan);
}

/* position:relative sert d'ancre logique/lisible dans le DOM, mais le
   menu contextuel lui-meme est position:fixed (voir .operator-menu) -
   ce conteneur n'a donc pas besoin d'etre le point de reference reel du
   positionnement, seulement de rester le parent visuel du declencheur. */
.sidebar-footer {
  margin-top: auto;
  border-top: 1px solid var(--border);
  position: relative;
}

/* Meme padding horizontal que .nav-item/.sidebar-toggle dans les DEUX
   etats (pas de reduction isolee a var(--space-2) ici) - seul
   justify-content change au repli, exactement le meme traitement que le
   reste de la sidebar pour rester coherent. */
.operator-menu-trigger {
  appearance: none;
  background: transparent;
  border: none;
  width: 100%;
  padding: var(--space-4);
  display: flex;
  align-items: center;
  gap: var(--space-2);
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.operator-menu-trigger:hover {
  background: var(--card);
}

.sidebar.is-collapsed .operator-menu-trigger {
  justify-content: center;
}

/* Menu contextuel (Deconnexion) declenche par clic sur l'avatar/le nom.
   position:fixed - calcule en JS via openOperatorMenu() - plutot que
   position:absolute : .sidebar a overflow:hidden (necessaire pour la
   transition de largeur repli/deplie), un menu en absolute serait rogne
   des qu'il depasserait les bornes de la sidebar, en particulier a
   l'etat replie (64px). z-index au-dessus du header (100) pour rester
   visible en toute circonstance, sous les toasts (1000). */
.operator-menu {
  position: fixed;
  min-width: 180px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: var(--space-2);
  z-index: 150;
  animation: dropdown-menu-in 150ms ease;
}

.operator-menu-item {
  display: block;
  width: 100%;
  appearance: none;
  background: transparent;
  border: none;
  border-radius: 4px;
  padding: var(--space-2) var(--space-3);
  color: var(--text-secondary);
  font-family: var(--font-ui);
  font-size: 13px;
  text-align: left;
  cursor: pointer;
}

.operator-menu-item:hover {
  color: var(--text-primary);
  background: var(--surface);
}

/* Partagee entre .operator-menu (bas de sidebar) et .client-context-menu
   (header) - deux dropdowns, une seule animation d'ouverture. */
@keyframes dropdown-menu-in {
  from { opacity: 0; transform: translateY(4px); }
  to { opacity: 1; transform: translateY(0); }
}

/* Avatar d'identite operateur (initiales) - toujours visible, dans les
   deux etats, meme taille dans les deux : seul le nom complet a cote
   apparait/disparait au repli (voir .operator-name ci-dessous). Fond
   ambre reutilise --status-pending (deja dans la palette, aucune
   nouvelle couleur) ; texte en --bg pour le meilleur contraste possible
   sur un fond aussi clair. */
.operator-avatar {
  display: flex;
  flex-shrink: 0;
  width: 26px;
  height: 26px;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--status-pending);
  color: var(--bg);
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
}

.sidebar-footer .operator-name {
  font-size: 12px;
  color: var(--text-primary);
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Disparait au meme instant que .nav-label (voir plus haut) - la seule
   transition animee de la sidebar reste sa largeur (150ms), coherent avec
   le traitement deja etabli pour les libelles de navigation. */
.sidebar.is-collapsed .sidebar-footer .operator-name {
  display: none;
}

@media (max-width: 768px) {
  .app-shell {
    flex-direction: column;
  }

  .sidebar {
    width: 100%;
    min-height: 0;
    flex-direction: row;
    overflow-x: auto;
    padding: var(--space-2);
    border-right: none;
    border-bottom: 1px solid var(--border);
  }

  .nav-item {
    border-left: none;
    border-bottom: 2px solid transparent;
    padding: var(--space-2) var(--space-4);
    white-space: nowrap;
  }

  .nav-item.is-active {
    border-left-color: transparent;
    border-bottom-color: var(--accent-cyan);
  }

  /* L'ancrage bas n'a pas de sens dans la barre horizontale mobile. */
  .sidebar-footer {
    display: none;
  }

  /* Le repli est un concept desktop (colonne etroite -> tres etroite).
     Sur la barre horizontale mobile il n'a pas de sens - masque le bouton,
     et neutralise l'etat "replie" s'il a ete persiste depuis une session
     desktop precedente (sinon .sidebar.is-collapsed{width:64px}, plus
     specifique que la regle .sidebar ci-dessus, l'emporterait et casserait
     la barre mobile). */
  .sidebar-toggle {
    display: none;
  }

  .sidebar.is-collapsed {
    width: 100%;
  }

  .sidebar.is-collapsed .nav-label {
    display: inline;
  }

  .sidebar.is-collapsed .nav-item {
    justify-content: flex-start;
  }
}

.main {
  flex: 1;
  min-width: 0;
  padding: var(--space-4) var(--space-5) calc(var(--space-6) + var(--space-2));
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

/* ---------- Views (tabs) ---------- */

/* Meme grille verticale que .main pour l'onglet actif : gabarit de page
   (en-tete) + panels s'espacent tous de la meme facon, systematiquement. */
.view {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.view--enter {
  animation: view-fade-in 140ms ease;
}

@keyframes view-fade-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

/* ---------- Gabarit de page commun ---------- */

.page-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-4);
  padding-bottom: var(--space-4);
  border-bottom: 1px solid var(--border);
}

.page-header-text h1 {
  margin: 0;
  font-size: 18px;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--text-primary);
}

.page-header-text p {
  margin: var(--space-1) 0 0;
  font-size: 12px;
  color: var(--text-secondary);
}

.page-header-actions {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-shrink: 0;
}

/* Etat de chargement discret par section - une ligne de texte sobre,
   jamais un spinner. */
.loading-tag {
  font-size: 11px;
  font-style: italic;
  color: var(--text-secondary);
}

.panel {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: var(--space-4);
}

.panel-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-bottom: var(--space-3);
}

.panel-header h2 {
  margin: 0;
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--accent-cyan);
}

.panel-row {
  display: flex;
  gap: var(--space-4);
  flex-wrap: wrap;
}

.chart-panel {
  flex: 1 1 320px;
  min-width: 0;
}

.chart {
  height: 260px;
  width: 100%;
}

/* ---------- Health banner ---------- */

.health-body {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.health-counters {
  display: flex;
  gap: var(--space-6);
  flex-wrap: wrap;
}

.health-counter {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.health-counter-value {
  font-size: 22px;
  font-weight: 600;
  color: var(--text-primary);
}

.health-counter-label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-secondary);
}

/* ---------- Table controls ---------- */

.table-controls {
  display: flex;
  gap: var(--space-3);
  align-items: center;
  flex-wrap: wrap;
}

.search-box {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  width: 260px;
  padding: var(--space-1) var(--space-2);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 4px;
}

.search-box .icon {
  width: 14px;
  height: 14px;
  color: var(--text-secondary);
  flex-shrink: 0;
}

.search-box input {
  flex: 1;
  background: transparent;
  border: none;
  outline: none;
  color: var(--text-primary);
  font-family: var(--font-ui);
  font-size: 13px;
}

.search-box input::placeholder { color: var(--text-secondary); }

.search-box:focus-within { border-color: var(--accent-cyan); }

/* Case a cocher de filtre (ex: "Masquer le trafic sans client" - Vue
   Correlation MITRE) - meme famille visuelle que .table-controls/.filter-select,
   checkbox native plutot qu'un composant toggle personnalise. */
.filter-checkbox {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: 13px;
  color: var(--text-secondary);
  cursor: pointer;
  user-select: none;
}

.filter-checkbox input[type="checkbox"] {
  accent-color: var(--accent-cyan);
  width: 14px;
  height: 14px;
  cursor: pointer;
}

.filter-select {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 4px;
  color: var(--text-primary);
  font-family: var(--font-ui);
  font-size: 13px;
  padding: var(--space-2);
}

button:focus-visible,
.filter-select:focus-visible,
.search-box input:focus-visible {
  outline: 1px solid var(--accent-cyan);
  outline-offset: 1px;
}

.search-box input:focus-visible {
  outline: none;
}

.btn-secondary {
  background: transparent;
  border: 1px solid var(--border);
  border-radius: 4px;
  color: var(--text-secondary);
  font-family: var(--font-ui);
  font-size: 12px;
  padding: var(--space-2) var(--space-3);
  cursor: pointer;
}

.btn-secondary:hover:not(:disabled) {
  color: var(--text-primary);
  border-color: var(--accent-cyan);
}

.btn-secondary:disabled {
  opacity: 0.5;
  cursor: default;
}

.table-footer-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-top: var(--space-2);
}

.table-footer-row .table-footer { margin-top: 0; }

/* ---------- Table ---------- */

.table-wrap {
  overflow-x: auto;
  margin-top: var(--space-1);
  position: relative;
}

table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
}

thead th {
  text-align: left;
  padding: var(--space-2) var(--space-3);
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-secondary);
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}

tbody td {
  padding: var(--space-2) var(--space-3);
  border-bottom: 1px solid var(--border);
  color: var(--text-primary);
  white-space: nowrap;
}

tbody tr:hover { background: var(--surface); }

.mono { font-family: var(--font-mono); font-size: 12.5px; }

.text-muted { color: var(--text-secondary); }

.status-cell {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}

.status-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex-shrink: 0;
}

.status-active { background: var(--status-active); }
.status-pending { background: var(--status-pending); }
.status-disconnected { background: var(--status-disconnected); }
.status-never_connected,
.status-unknown { background: var(--status-neutral); }

/* Severites d'alertes : jamais vert, une alerte n'est jamais "positive". */
.severity-critical { background: var(--status-disconnected); }
.severity-high { background: var(--status-pending); }
.severity-low { background: var(--status-neutral); }

/* Etat vide explicite : message centre expliquant pourquoi, pas une table
   muette. Le texte exact est fourni par app.js selon le contexte (aucune
   donnee du tout vs. rien ne correspond aux filtres). */
.table-empty {
  padding: var(--space-6) var(--space-3);
  text-align: center;
  color: var(--text-secondary);
}

.table-footer {
  margin-top: var(--space-2);
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--text-secondary);
}

/* ---------- Topologie (Vue reseau) ---------- */

/* Fil d'Ariane du drill-down, dans la zone d'actions du gabarit de page -
   le lien racine reutilise .btn-secondary (memes etats hover/focus que le
   reste de l'appli), rien de nouveau a styler pour lui. */
.topology-breadcrumb {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: 12px;
  color: var(--text-secondary);
}

.breadcrumb-current {
  color: var(--text-primary);
  font-weight: 500;
}

.topology-body {
  display: flex;
  gap: var(--space-4);
}

.topology-chart {
  flex: 1;
  min-width: 0;
  /* Hauteur fixe connue en CSS avant meme que Cytoscape ne s'initialise -
     jamais calculee en JS. position:relative + overflow:hidden contiennent
     les canvas internes de Cytoscape (positionnes en absolute) : sans ca,
     ils se dimensionnent par rapport au premier ancetre positionne trouve
     dans l'arbre (potentiellement bien plus grand que ce conteneur) et
     debordent visuellement sur le panneau "Nœuds" en dessous. */
  height: 420px;
  position: relative;
  overflow: hidden;
}

/* Cytoscape se monte dans ce conteneur interne (plein 100% du wrapper
   positionne ci-dessus), separe du message d'etat vide qui partage le
   meme espace en absolute. */
.topology-chart-mount {
  width: 100%;
  height: 100%;
}

.topology-chart-empty {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: var(--space-4);
  color: var(--text-secondary);
  font-size: 13px;
  pointer-events: none;
}

.topology-details {
  width: 240px;
  flex-shrink: 0;
  padding: var(--space-3);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 6px;
}

.details-list {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--space-2) var(--space-3);
  margin: 0;
}

.details-list dt {
  color: var(--text-secondary);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.details-list dd {
  margin: 0;
  color: var(--text-primary);
  font-size: 13px;
}

.topology-alert-summary {
  margin-top: var(--space-4);
  padding-top: var(--space-3);
  border-top: 1px solid var(--border);
}

.topology-alert-summary h3 {
  margin: 0 0 var(--space-2);
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-secondary);
}

.topology-alert-summary p {
  margin: 0 0 var(--space-3);
  font-size: 13px;
  color: var(--text-primary);
}

.topology-add-form {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--space-3);
  margin-top: var(--space-4);
  padding-top: var(--space-4);
  border-top: 1px solid var(--border);
}

.topology-add-form .field {
  min-width: 150px;
}

@media (max-width: 768px) {
  .topology-body {
    flex-direction: column;
  }

  .topology-details {
    width: 100%;
  }
}

/* ---------- Evolution des alertes (onglet Alertes) ---------- */

.alerts-timeseries-chart {
  /* Meme convention que .mitre-chart/.topology-chart : hauteur fixe
     connue en CSS avant l'init ECharts, position:relative +
     overflow:hidden pour contenir le canvas et permettre a l'etat vide de
     partager le meme espace en absolute. */
  height: 260px;
  position: relative;
  overflow: hidden;
}

.alerts-timeseries-chart-mount {
  width: 100%;
  height: 100%;
}

.alerts-timeseries-chart-empty {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: var(--space-4);
  color: var(--text-secondary);
  font-size: 13px;
  pointer-events: none;
}

/* ---------- MITRE (Correlation MITRE ATT&CK) ---------- */

.mitre-chart {
  /* Meme convention que .topology-chart : hauteur fixe connue en CSS avant
     l'init ECharts, position:relative + overflow:hidden pour contenir le
     canvas interne et permettre a l'etat vide de partager le meme espace
     en absolute. */
  height: 420px;
  position: relative;
  overflow: hidden;
}

.mitre-chart-mount {
  width: 100%;
  height: 100%;
}

.mitre-chart-empty {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: var(--space-4);
  color: var(--text-secondary);
  font-size: 13px;
  pointer-events: none;
}

/* ---------- Conformite ---------- */

/* Largeurs de colonnes CONTRAINTES (voir <colgroup> dans index.html)
   plutot que laissees a la repartition automatique du navigateur : sans
   ca, LIBELLE (texte le plus long) absorbe tout l'espace disponible et
   cree un grand vide avant SEVERITE MAX, pendant que REPARTITION/TOTAL se
   retrouvent tassees au bord droit - moins dense que la reference Wazuh/
   Datadog visee. table-layout:fixed applique STRICTEMENT les largeurs du
   colgroup ; width:auto (au lieu du width:100% global a `table`, voir plus
   haut dans ce fichier) evite que le tableau ne s'etire jusqu'au bord du
   panneau si son contenu ne le justifie pas - l'espace excedentaire reste
   simplement vide a droite plutot que d'etre absorbe par une colonne.
   .table-wrap garde overflow-x:auto (deja global) comme filet de securite
   si un ecran est plus etroit que la somme des colonnes. */
#complianceTable {
  table-layout: fixed;
  width: auto;
}

/* LIBELLE : seule colonne "souple" du tableau - largeur plafonnee par le
   colgroup (pas d'etirement illimite), mais peut passer a la ligne plutot
   que de tronquer (aucun libelle FR fourni n'est illisible une fois
   reparti sur 2-3 lignes, meme sur un ecran etroit ~1280px). */
#complianceTable th:nth-child(2),
#complianceTable td:nth-child(2) {
  white-space: normal;
  word-break: break-word;
}

/* TOTAL aligne a droite (nombre, lecture plus naturelle alignee sur les
   unites) - seule colonne concernee, les autres restent alignees a
   gauche comme partout ailleurs dans l'appli. */
#complianceTable th:nth-child(5),
#complianceTable td:nth-child(5) {
  text-align: right;
}

.compliance-row {
  cursor: pointer;
}

/* Meme affordance "selectionne" que .client-context-item.is-active
   (accent-cyan) - coherence entre les deux patterns de selection. Fond
   --card (plus marque que le --surface du survol deja global sur
   tbody tr:hover) pour rester distinguable d'un simple survol. */
.compliance-row.is-selected {
  background: var(--card);
}

.compliance-row.is-selected td:first-child {
  color: var(--accent-cyan);
}

/* Micro-barre COMPACTE (largeur fixe, jamais pleine largeur ni
   proportionnelle au volume entre controles) : les segments sont
   normalises a 100% de LA BARRE ELLE-MEME (voir buildComplianceMicroBar),
   donc elle montre la COMPOSITION interne d'un controle, pas son volume -
   les volumes reels vont de 4 a plus de 17000 alertes selon le controle
   (ratio ~3000:1), qu'aucune representation proportionnelle au volume ne
   rendrait lisible sans ecraser les petits controles. */
.compliance-micro-bar {
  display: flex;
  width: 100px;
  height: 8px;
  border-radius: 4px;
  overflow: hidden;
  background: var(--border);
}

.compliance-micro-bar-segment {
  height: 100%;
}

/* ---------- Vulnerabilites ---------- */

/* Meme technique que #complianceTable (voir plus haut) : largeurs
   contraintes via <colgroup> + table-layout:fixed, le tableau ne s'etire
   pas au-dela de ce que son contenu justifie. */
#vulnerabilitiesAgentTable,
#vulnerabilitiesTable {
  table-layout: fixed;
  width: auto;
}

/* Deuxieme ligne (version du paquet) dans la cellule "Paquet" du tableau
   detaille - plus discrete que le nom du paquet lui-meme. */
.vulnerability-package-version {
  color: var(--text-secondary);
  font-size: 11px;
  margin-top: 2px;
}

/* Lien CVE -> vulnerability.reference (nouvel onglet) - reutilise
   accent-cyan, deja la couleur "interactif" standard de l'appli (titres de
   panneau, etats actifs/selectionnes, focus). */
.vulnerability-cve-link {
  color: var(--accent-cyan);
  text-decoration: none;
}

.vulnerability-cve-link:hover {
  text-decoration: underline;
}

/* ---------- Configuration (SCA) ---------- */

/* Meme technique que #complianceTable/#vulnerabilitiesTable : largeurs
   contraintes via <colgroup> + table-layout:fixed. */
#configurationAgentTable {
  table-layout: fixed;
  width: auto;
}

/* POLITIQUE (nom lisible, peut etre long, ex: "CIS Debian Linux 13
   Benchmark") : passe a la ligne plutot que de tronquer, meme traitement
   que LIBELLE dans #complianceTable. */
#configurationAgentTable th:nth-child(3),
#configurationAgentTable td:nth-child(3) {
  white-space: normal;
  word-break: break-word;
}

/* Reussis/Echoues/Non applicables : nombres, alignes a droite. */
#configurationAgentTable th:nth-child(5),
#configurationAgentTable td:nth-child(5),
#configurationAgentTable th:nth-child(6),
#configurationAgentTable td:nth-child(6),
#configurationAgentTable th:nth-child(7),
#configurationAgentTable td:nth-child(7) {
  text-align: right;
}

/* Barre de score - SEUL endroit du produit ou le vert est autorise (voir
   CLAUDE.md/consignes produit) : un score de conformite eleve est
   reellement une bonne nouvelle, contrairement a une alerte ou une
   vulnerabilite, jamais "positives" par nature (voir plus haut,
   .severity-critical/.severity-high/.severity-low n'ont jamais de variante
   verte). */
.sca-score-cell {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.sca-score-track {
  width: 80px;
  height: 8px;
  border-radius: 4px;
  overflow: hidden;
  background: var(--border);
  flex-shrink: 0;
}

.sca-score-fill {
  height: 100%;
}

.sca-score-fill.sca-score-low { background: var(--status-disconnected); }   /* < 50% */
.sca-score-fill.sca-score-mid { background: var(--status-pending); }        /* 50-79% */
.sca-score-fill.sca-score-high { background: var(--status-active); }        /* >= 80% */

/* Liste des controles - pas un <table> (chaque entree se deplie en un
   bloc de contenu heterogene : rationale, remediation, commande,
   etiquettes de conformite - un colgroup fixe n'aurait aucun sens ici),
   meme conteneur .table-wrap-like que le reste de l'appli pour la
   coherence visuelle (bordure, radius, fond carte). */
.sca-checks-wrap {
  border: 1px solid var(--border);
  border-radius: 6px;
  overflow: hidden;
  margin-top: var(--space-1);
}

.sca-check-item {
  border-bottom: 1px solid var(--border);
}

.sca-check-item:last-child {
  border-bottom: none;
}

.sca-check-item-header {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  cursor: pointer;
}

.sca-check-item-header:hover {
  background: var(--surface);
}

.sca-check-title {
  flex: 1;
  font-size: 13px;
  color: var(--text-primary);
}

/* Resultat "reussi" en accent-cyan (couleur de marque, pas de severite) -
   jamais vert : le vert est reserve exclusivement a la barre de score
   ci-dessus dans cette vue. "echoue" reutilise severity-critical (rouge,
   coherent avec le reste de l'appli), "non applicable" reutilise
   severity-low (gris neutre). */
.sca-result-passed { background: var(--accent-cyan); }

.sca-check-chevron {
  color: var(--text-secondary);
  transition: transform 120ms ease;
  flex-shrink: 0;
}

.sca-check-item.is-expanded .sca-check-chevron {
  transform: rotate(90deg);
}

.sca-check-detail {
  padding: 0 var(--space-4) var(--space-4) calc(var(--space-4) + 8px + var(--space-3));
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.sca-check-detail-label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-secondary);
  margin-bottom: var(--space-1);
}

.sca-check-detail-text {
  font-size: 13px;
  color: var(--text-primary);
  line-height: 1.5;
}

/* Bloc remediation/commande - JetBrains Mono, fond distinct du panneau
   pour lire ce qui EST une commande a executer, separement du texte
   explicatif (rationale). */
.sca-check-code-block {
  font-family: var(--font-mono);
  font-size: 12.5px;
  color: var(--text-primary);
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: var(--space-2) var(--space-3);
  white-space: pre-wrap;
  word-break: break-word;
  overflow-x: auto;
}

.sca-check-compliance-tags {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

/* Etiquette de correspondance conformite (ex: "CIS 1.1.2.1.1") - seul
   badge en pastille du produit, introduit pour ce besoin precis (aucun
   .badge/.tag generique n'existait avant). */
.control-tag {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-secondary);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 2px 6px;
}

/* ---------- Toasts ---------- */

.toast-container {
  position: fixed;
  bottom: var(--space-5);
  right: var(--space-5);
  z-index: 1000;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  pointer-events: none;
}

.toast {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  max-width: 320px;
  background: var(--card);
  border: 1px solid var(--border);
  border-left: 3px solid var(--accent-cyan);
  border-radius: 6px;
  color: var(--text-primary);
  font-size: 12.5px;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.35);
  animation: toast-in 160ms ease;
  pointer-events: auto;
}

.toast--error {
  border-left-color: var(--status-disconnected);
}

.toast--leaving {
  animation: toast-out 160ms ease forwards;
}

@keyframes toast-in {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: translateY(0); }
}

@keyframes toast-out {
  from { opacity: 1; transform: translateY(0); }
  to { opacity: 0; transform: translateY(6px); }
}
