/* ============================================================================
 * SuperAndy — onglet « Infrastructure » (télémétrie). Styles du module
 * assets/infra/* : états de nœud, graphe de séries temporelles et vue réseau
 * (cartes DOM + liens béziers SVG, pan/zoom).
 * Voir .claude/doc/infra-telemetry.md §8.
 *
 * Aucune couleur en dur hors des teintes de fond translucides déjà employées
 * par styles.css (.badge.*) : tout le reste passe par les tokens SuperAndy
 * (--panel / --panel2 / --border / --soft / --accent / --ok / --warn / --ko),
 * qui basculent seuls en thème clair via le :root de styles.css.
 * ========================================================================== */

/* ---- États d'un nœud — classes produites par SA.infra.statusClass() -------
 * Chaque classe ne fait que poser --inf-c : la pastille, la bordure gauche de
 * carte et le libellé s'y branchent, il n'y a donc qu'un seul endroit à
 * modifier pour changer la sémantique d'une couleur. */
.inf-st-ok{ --inf-c:var(--ok); }
.inf-st-stale{ --inf-c:var(--warn); }
.inf-st-down{ --inf-c:var(--ko); }
.inf-st-unknown{ --inf-c:var(--soft); }
.inf-st-declared{ --inf-c:var(--soft); }

.inf-dot{ flex:none; width:9px; height:9px; border-radius:50%; background:var(--inf-c,var(--soft)); }
.inf-state{ font-weight:700; color:var(--inf-c,var(--soft)); }
/* Une pastille d'état posée sur un .badge de styles.css : celui-ci ne porte ni
 * fond ni couleur tant qu'on ne lui donne pas de variante (.badge.ok…), d'où
 * cette teinte pilotée par --inf-c — un seul état, une seule couleur, partout. */
.inf-status{ background:var(--panel2); color:var(--inf-c,var(--soft));
  border:1px solid var(--inf-c,var(--border)); }

/* ---- Pastilles de KPI (cartes de nœud, entêtes de vues) ---- */
.inf-pill{ font-size:10.5px; font-weight:700; padding:2px 7px; border-radius:999px; white-space:nowrap;
  background:var(--panel2); color:var(--soft); border:1px solid var(--border); }
.inf-pill.is-ok{ color:var(--ok); border-color:rgba(34,197,94,.42); }
.inf-pill.is-warn{ color:var(--warn); border-color:rgba(245,158,11,.42); }
.inf-pill.is-ko{ color:var(--ko); border-color:rgba(239,68,68,.42); }
.inf-pill-k{ font-weight:600; opacity:.75; margin-right:4px; }

/* ============================ Graphe de séries ============================ */
/* La largeur du SVG est posée en pixels par chart.js (mesure du conteneur) :
 * pas de width:100% ici, sinon les coordonnées du survol ne correspondraient
 * plus à celles du tracé. */
.inf-chart{ position:relative; width:100%; min-width:0; overflow:hidden; }
.inf-chart-svg{ display:block; }
.inf-chart-empty{ color:var(--soft); text-align:center; padding:24px 8px; font-size:12.5px; margin:0; }
.inf-gridline{ stroke:var(--border); stroke-width:1; }
.inf-axis{ font-size:10.5px; fill:var(--soft); }
.inf-guide{ stroke:var(--soft); stroke-width:1; stroke-dasharray:3 3; opacity:.65; }
.inf-serie-line{ fill:none; stroke-linejoin:round; stroke-linecap:round; }
.inf-serie-area{ stroke:none; opacity:.14; }
.inf-serie-dot{ stroke:var(--panel); stroke-width:1.5; }

.inf-chart-tip{ position:absolute; z-index:3; top:8px; left:0; display:none; pointer-events:none;
  background:var(--panel); border:1px solid var(--border); border-radius:9px; box-shadow:var(--shadow);
  padding:8px 10px; font-size:12px; min-width:148px; max-width:280px; }
.inf-chart-tip.is-on{ display:block; }
.inf-tip-t{ font-size:11.5px; font-weight:700; color:var(--soft); margin-bottom:5px; white-space:nowrap; }
.inf-tip-r{ display:flex; align-items:center; gap:6px; margin-top:2px; }
.inf-tip-c{ flex:none; width:9px; height:9px; border-radius:2px; }
.inf-tip-l{ flex:1; min-width:0; color:var(--soft); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.inf-tip-v{ flex:none; font-weight:700; font-variant-numeric:tabular-nums; }

.inf-chart-legend{ display:flex; gap:12px; flex-wrap:wrap; margin-top:8px; }
.inf-leg-i{ display:flex; align-items:center; gap:5px; font-size:12px; color:var(--soft); }
.inf-leg-c{ width:10px; height:10px; border-radius:2px; display:inline-block; flex:none; }

/* ============================== Vue réseau =============================== */
/* Reprend le viewport zoomable de assets/ai/ai.css (.ai-gviewport) : overflow
 * caché + monde transformé, donc jamais de débordement horizontal de la page,
 * même en fenêtre étroite. */
.inf-topo{ display:flex; flex-direction:column; gap:10px; }
.inf-topo-vp{ position:relative; overflow:hidden; height:480px; border:1px solid var(--border);
  border-radius:var(--radius); background:var(--panel2); touch-action:none; cursor:grab;
  user-select:none; -webkit-user-select:none; }
.inf-topo-vp:active{ cursor:grabbing; }
.inf-topo-world{ position:absolute; left:0; top:0; transform-origin:0 0; }
/* Le SVG des liens est dessiné SOUS les cartes ; seuls les tracés reçoivent le
 * survol, pour que leur <title> s'affiche sans bloquer le pan. */
.inf-topo-links{ position:absolute; left:0; top:0; overflow:visible; pointer-events:none; }
.inf-topo-links path{ pointer-events:stroke; fill:none; }
/* Traits du mur. Sans ces règles, les chemins étaient tracés MAIS invisibles :
 * un <path> sans `stroke` ne peint rien, et rien ne le signale. */
.inf-topo-link{ stroke:var(--accent); stroke-opacity:.5; }
.inf-topo-link.is-idle{ stroke:var(--soft); stroke-opacity:.45; stroke-dasharray:5 5; }
.inf-topo-link.is-warn{ stroke:var(--warn); stroke-opacity:.75; }
.inf-topo-link.is-ko{ stroke:var(--ko); stroke-opacity:.8; }
.inf-topo-colh{ position:absolute; top:0; text-align:center; font-size:11px; font-weight:700;
  color:var(--soft); text-transform:uppercase; letter-spacing:.6px; }
.inf-topo-empty{ position:absolute; inset:0; display:grid; place-items:center; color:var(--soft); font-size:13px; }

.inf-node{ position:absolute; background:var(--panel); border:1px solid var(--border);
  border-left:3px solid var(--inf-c,var(--soft)); border-radius:9px; padding:8px 10px;
  overflow:hidden; box-shadow:var(--shadow); }
.inf-node:hover{ border-color:var(--accent); border-left-color:var(--inf-c,var(--soft)); }
.inf-node.is-declared{ border-style:dashed; border-left-style:solid; opacity:.72; }
.inf-node.click{ cursor:pointer; }
.inf-node-h{ display:flex; align-items:center; gap:6px; }
.inf-node-t{ flex:1; min-width:0; font-weight:700; font-size:12.5px;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.inf-node-k{ font-size:10.5px; color:var(--soft); margin-top:2px; letter-spacing:.3px;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.inf-node-kpis{ display:flex; flex-wrap:wrap; gap:4px; margin-top:6px; }
/* Redis local = pastille DANS la carte du nœud, jamais un nœud du graphe (§8). */
.inf-redis{ flex:none; font-size:9.5px; font-weight:800; letter-spacing:.4px; padding:2px 5px;
  border-radius:5px; background:var(--panel2); color:var(--soft); border:1px solid var(--border); }

.inf-topo-ctrls{ position:absolute; top:10px; right:10px; display:flex; gap:6px; z-index:2; }
.inf-topo-btn{ width:30px; height:30px; border-radius:8px; border:1px solid var(--border);
  background:var(--panel); color:var(--text); font-size:16px; line-height:1; padding:0; }
.inf-topo-btn:hover{ border-color:var(--accent); }

/* Padding porté par la légende : la vue réseau est parfois posée dans une
 * carte sans padding (pour que le viewport aille bord à bord). */
.inf-topo-legend{ display:flex; gap:14px; flex-wrap:wrap; font-size:12px; color:var(--soft);
  padding:0 12px 12px; }
.inf-topo-legend .inf-leg-i{ font-size:12px; }
.inf-leg-line{ display:inline-block; width:22px; border-top:2px solid var(--accent); vertical-align:middle; }
.inf-leg-line.is-idle{ border-top-color:var(--soft); border-top-style:dashed; }
.inf-leg-line.is-warn{ border-top-color:var(--warn); border-top-width:3px; }
.inf-leg-line.is-ko{ border-top-color:var(--ko); border-top-width:3px; }

@media (max-width:820px){
  .inf-topo-vp{ height:380px; }
}

/* ---- Cartes KPI (SA.infra.metric) — même gabarit que .rt-metric ---- */
.inf-metrics{ display:grid; grid-template-columns:repeat(auto-fit,minmax(150px,1fr)); gap:10px; }
.inf-metric{ background:var(--panel2); border:1px solid var(--border); border-radius:var(--radius);
  padding:12px 10px; text-align:center; }
.inf-metric-v{ font-size:19px; font-weight:800; line-height:1.15; }
.inf-metric-s{ font-size:11.5px; color:var(--soft); margin-top:3px; }
.inf-metric-l{ font-size:11px; color:var(--soft); margin-top:5px; text-transform:uppercase; letter-spacing:.4px; }
.inf-metric.ok .inf-metric-v{ color:var(--ok); }
.inf-metric.warn .inf-metric-v{ color:var(--warn); }
.inf-metric.ko .inf-metric-v{ color:var(--ko); }

/* ======================= Tableaux de détail (HAProxy, SQL, motifs) ========
 * Un tableau de stats a des colonnes incompressibles : il défile DANS son
 * conteneur, jamais la page. C'est la seule façon de garder « octets entrants »
 * et « temps de réponse » lisibles sur un écran étroit sans tout écraser. */
.inf-tableWrap{ overflow-x:auto; -webkit-overflow-scrolling:touch; max-width:100%;
  border:1px solid var(--border); border-radius:9px; margin-top:8px; }
.inf-table{ border-collapse:collapse; width:100%; min-width:640px; font-size:12.5px; }
.inf-table th{ text-align:left; white-space:nowrap; padding:7px 10px; font-size:11px; font-weight:700;
  color:var(--soft); text-transform:uppercase; letter-spacing:.4px;
  background:var(--panel2); border-bottom:1px solid var(--border); }
.inf-table td{ padding:7px 10px; white-space:nowrap; border-top:1px solid var(--border);
  font-variant-numeric:tabular-nums; }
.inf-table th.is-num, .inf-table td.is-num{ text-align:right; }
.inf-table tbody tr:hover{ background:var(--panel2); }
/* Ligne d'agrégat (le BACKEND d'HAProxy, le total SQL) : c'est un résumé de
 * celles qui suivent, pas une ligne de plus. */
.inf-table tr.is-sum td{ font-weight:700; background:var(--panel2); }
.inf-table tr.is-srv td:first-child{ color:var(--soft); }

/* ---- Pastilles et méta d'une ligne de liste (cartes de nœud) ----
 * `.row .meta` de styles.css est en nowrap + ellipsis : un nœud porte trop
 * d'informations pour tenir sur une ligne tronquée. */
.inf-rowmeta{ color:var(--soft); font-size:12.5px; margin-top:2px; }
.inf-rowpills{ display:flex; flex-wrap:wrap; gap:4px; margin-top:6px; }

/* ═══════════════════════ VUE À PLAT DES GRAPPES (wall.js) ════════════════
 * Une colonne par grappe, sa carte de tête puis ses serveurs. Les cartes sont
 * positionnées en absolu par le module : toute marge posée ici décalerait le
 * tracé des liens, qui, lui, est calculé en coordonnées du monde.
*/
.inf-topo-vp.is-wall{ height:760px; }
/* Sur le mur, les commandes de zoom passent EN BAS : en haut à droite elles
 * recouvraient le titre de la dernière colonne. */
.inf-topo-vp.is-wall .inf-topo-ctrls{ top:auto; bottom:12px; }
@media (max-width:860px){ .inf-topo-vp.is-wall{ height:480px; } }

.inf-wcard{ position:absolute; box-sizing:border-box; background:var(--panel);
  border:1px solid var(--border); border-left:3px solid var(--inf-c,var(--soft));
  border-radius:12px; padding:9px 11px 11px; }
.inf-wcard.click{ cursor:pointer; }
.inf-wcard:hover{ border-color:var(--accent); border-left-color:var(--inf-c,var(--soft)); }
/* La carte de TÊTE porte les chiffres de toute la grappe : elle se distingue
 * au premier coup d'œil de celles des serveurs qui la composent. */
.inf-wcard.is-cluster{ background:var(--panel2); border-width:1px 1px 1px 3px; }
.inf-wcard.is-cluster .inf-wcard-t{ font-size:14px; }
.inf-wcard.is-node{ opacity:.97; }
/* Acteur qui ne rapporte rien et n'a rien à rapporter (navigateurs). Traits
 * pointillés = « hors périmètre mesuré », JAMAIS « nœud muet ». */
.inf-wcard.is-external{ border-style:dashed; border-left-style:dashed; background:transparent; }

.inf-wcard-h{ display:flex; align-items:center; gap:7px; }
.inf-wcard-t{ flex:1; min-width:0; font-weight:800; font-size:12.5px;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.inf-wcard-r{ flex:none; font-size:10.5px; font-weight:800; color:var(--soft);
  font-variant-numeric:tabular-nums; }
.inf-wcard-r.is-late{ color:var(--warn); }
.inf-wcard-r.is-ahead{ color:var(--accent); }
.inf-wcard-r.is-ok{ color:var(--ok); }
.inf-wcard-s{ font-size:11px; color:var(--soft); margin-top:2px;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.inf-wcard-p{ display:flex; flex-wrap:wrap; gap:4px; margin-top:7px; }
.inf-wcard-p:empty{ display:none; }
.inf-wcard-chip{ font-size:9.5px; font-weight:800; letter-spacing:.4px; padding:2px 5px;
  border-radius:5px; border:1px solid var(--border); color:var(--soft); background:var(--panel2); }

.inf-wcard-g{ display:grid; grid-template-columns:1fr 1fr; gap:6px 8px; margin-top:9px; }
.inf-wcard-g:empty{ display:none; }
.inf-wchart{ min-width:0; }
.inf-wchart-t{ font-size:10px; font-weight:700; color:var(--soft); letter-spacing:.2px;
  margin-bottom:1px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
/* Dans une carte, le graphe doit pouvoir rétrécir sous la largeur mini du
 * module : sans cette surcharge il déborderait de la colonne. */
.inf-wchart .inf-chart{ min-width:0; }
.inf-wchart .inf-chart-empty{ padding:22px 4px; font-size:10.5px; }

/* ═══════════════════════════ INFOBULLE D'AIDE (?) ════════════════════════ */
.inf-help{ position:relative; display:inline-flex; align-items:center; justify-content:center;
  width:15px; height:15px; margin-left:5px; border-radius:50%; cursor:help; flex:none;
  border:1px solid var(--border); background:var(--panel2); color:var(--soft);
  font-size:10px; font-weight:800; line-height:1; vertical-align:middle; }
.inf-help:hover{ border-color:var(--accent); color:var(--accent); }
.inf-help-b{ position:absolute; z-index:40; top:20px; left:50%; transform:translateX(-50%);
  display:none; width:min(340px,80vw); text-align:left; cursor:auto;
  background:var(--panel); border:1px solid var(--border); border-radius:10px;
  padding:10px 12px; box-shadow:0 10px 30px rgba(0,0,0,.28); }
.inf-help.is-on .inf-help-b{ display:block; }
/* Une infobulle près du bord droit sortirait de l'écran : la variante `is-end`
 * l'ancre sur son bord droit au lieu de la centrer. */
.inf-help.is-end .inf-help-b{ left:auto; right:0; transform:none; }
.inf-help-t{ font-size:12px; font-weight:800; color:var(--fg); margin-bottom:5px; }
.inf-help-p{ font-size:11.5px; color:var(--soft); margin:0 0 6px; line-height:1.5; }
.inf-help-p:last-child{ margin-bottom:0; }
.inf-help-x{ font-family:var(--mono,ui-monospace,monospace); font-size:10.5px;
  color:var(--fg); background:var(--panel2); border:1px solid var(--border);
  border-radius:6px; padding:5px 7px; margin:4px 0 0; display:block; line-height:1.45; }

/* ═════════════════════════ PAGE DISPONIBILITÉ ════════════════════════════ */
.inf-upRow{ display:flex; align-items:center; gap:12px; padding:9px 2px; border-bottom:1px solid var(--border); }
.inf-upRow:last-child{ border-bottom:0; }
.inf-upRow.is-child{ padding-left:18px; }
.inf-upHead{ flex:none; width:min(230px,38%); min-width:0; }
.inf-upName{ font-weight:700; font-size:12.5px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.inf-upSub{ font-size:11px; color:var(--soft); }
.inf-upBar{ flex:1; min-width:0; display:flex; gap:1px; height:30px; align-items:stretch; }
.inf-upSeg{ flex:1 1 0; min-width:1px; border-radius:2px; background:var(--soft); opacity:.22; }
.inf-upSeg.is-up{ background:var(--ok); opacity:1; }
.inf-upSeg.is-down{ background:var(--ko); opacity:1; }
.inf-upSeg.is-part{ background:var(--warn); opacity:1; }
.inf-upPct{ flex:none; width:74px; text-align:right; font-weight:800;
  font-variant-numeric:tabular-nums; font-size:13px; }
.inf-upToggle{ flex:none; width:20px; text-align:center; color:var(--soft);
  cursor:pointer; user-select:none; font-size:11px; }
.inf-upScale{ display:flex; justify-content:space-between; font-size:10.5px;
  color:var(--soft); margin-top:4px; }
@media (max-width:720px){
  .inf-upHead{ width:44%; }
  .inf-upPct{ width:58px; font-size:12px; }
}
