/* SBL Monitoring — dashboard design system
   Replaces the previous ad-hoc palette with a disciplined dark NOC theme:
   one primary blue in the SB Link brand family, a refined gold secondary, a calm status set,
   tabular numerals for metrics, and consistent radii/spacing/typography. Flat surfaces, hairline
   borders and a single soft shadow step — no gradients on data surfaces, which is what made the
   old theme read as "consumer" rather than "network operations". */
:root {
  /* surfaces */
  --bg: #0F1622;
  --bg-soft: #131C2A;
  --panel: #17202E;
  --panel2: #1C2636;         /* inputs, raised rows */
  --panel3: #232F42;         /* hover */
  /* chart surfaces — port.html's traffic chart reads these, and the light block below overrides them.
     Before this, the plot was a hardcoded dark navy, so a light-themed page showed a black slab. */
  --chart-bg: #17202E;
  --chart-grid: #26334A;
  --chart-axis: #94A6C0;
  --chart-note: #C6D2E2;
  --chart-tip-bg: rgba(17, 24, 39, .94);
  --chart-tip-fg: #FFFFFF;
  --line: #2A3A55;           /* hairline */
  --line-soft: rgba(255, 255, 255, .06);

  /* text */
  --ink: #E0E8F4;
  --ink-dim: #C6D2E2;
  --muted: #94A6C0;

  /* brand */
  --accent: #4D8DFF;         /* primary blue */
  --accent2: #2456B4;        /* solid button base */
  --accent-soft: rgba(77, 141, 255, .14);
  --accent-ring: rgba(77, 141, 255, .20);
  --gold: #E0B24C;           /* brand gold — highlights only */

  /* status (kept distinguishable for the most common colour-blindness) */
  --up: #35C98A;
  --warn: #E0A84A;
  --down: #E8646E;
  --crit: #E8646E;
  --olt: #A78BFA;            /* OLT / PON */
  --bgp: #3FCFC0;            /* BGP */

  /* type */
  --font: 'Inter', 'Segoe UI Variable Text', 'Segoe UI', system-ui, -apple-system, Roboto, sans-serif;
  --mono: 'JetBrains Mono', 'Cascadia Mono', Consolas, 'SF Mono', monospace;
  --fs-xs: .72rem; --fs-sm: .82rem; --fs: .9rem; --fs-lg: 1.06rem; --fs-xl: 1.32rem;
  --radius: 14px; --radius-sm: 9px; --radius-xs: 7px;
  --shadow: 0 1px 2px rgba(0, 0, 0, .4), 0 8px 24px -12px rgba(0, 0, 0, .6);
}

* { box-sizing: border-box; margin: 0; padding: 0; }
html { -webkit-text-size-adjust: 100%; }
body {
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font);
  font-size: var(--fs);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}
a { color: var(--accent); text-decoration: none; transition: color .12s ease; }
a:hover { color: #7FAAFF; }
::selection { background: var(--accent-soft); }
/* thin dark scrollbars — a bright OS scrollbar is the quickest way to look unfinished */
* { scrollbar-width: thin; scrollbar-color: #2A3A55 transparent; }
*::-webkit-scrollbar { width: 10px; height: 10px; }
*::-webkit-scrollbar-thumb { background: #2A3A55; border-radius: 99px; border: 3px solid transparent; background-clip: content-box; }
*::-webkit-scrollbar-thumb:hover { background: #3A4E70; background-clip: content-box; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }

/* ── top bar ─────────────────────────────────────────────────────────────── */
.topbar {
  background: linear-gradient(120deg, #1B3A6B 0%, #2456B4 100%);
  backdrop-filter: saturate(160%) blur(10px);
  -webkit-backdrop-filter: saturate(160%) blur(10px);
  border-bottom: 1px solid rgba(255, 255, 255, .14);
  position: sticky; top: 0; z-index: 100;
}
/* Two rows at every width. The nav alone is 666 px, the brand 168 px and the status chips plus the
   controls another ~670 px — about 1540 px of content in a 1280 px column. Forcing one row made the
   flex children shrink, which wrapped "Light mode" onto two lines and clipped the nav. Row 1 is the
   brand with the status/controls on the right; row 2 is the nav, which scrolls if it still overflows. */
.topbar-in {
  max-width: 1320px; margin: 0 auto; padding: 8px 20px 0;
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px;
}
.brand { order: 1; }
.topbar .right { order: 2; margin-left: auto; }
.topbar nav { order: 3; flex: 1 0 100%; overflow-x: auto; scrollbar-width: none; }
.topbar nav::-webkit-scrollbar { display: none; }
.brand { display: flex; align-items: center; gap: 10px; font-weight: 650; font-size: var(--fs-lg); letter-spacing: -.012em; color: #fff; }
.brand img { width: 32px; height: 32px; border-radius: 9px; display: block; }
.brand .mark {
  width: 30px; height: 30px; border-radius: 9px; display: grid; place-items: center;
  background: rgba(255, 255, 255, .16);
  border: 1px solid rgba(255, 255, 255, .35);
  font-size: .9rem; font-weight: 700; color: #fff;
}
.brand span { color: rgba(255, 255, 255, .9); font-weight: 550; }
.topbar nav { display: flex; gap: 2px; }
.topbar nav a {
  color: rgba(255, 255, 255, .82); padding: 7px 13px; border-radius: var(--radius-xs);
  font-weight: 500; font-size: var(--fs-sm); letter-spacing: -.005em;
  border: 1px solid transparent;
}
.topbar nav a:hover { color: #fff; background: rgba(255, 255, 255, .14); }
.topbar nav a.active { color: #fff; background: rgba(255, 255, 255, .18); border-color: rgba(255, 255, 255, .32); }
.topbar .right { margin-left: auto; display: flex; align-items: center; gap: 14px; color: rgba(255, 255, 255, .85); font-size: var(--fs-sm); }
.topbar .right .muted { color: rgba(255, 255, 255, .75); }
.topbar .btn.ghost { color: #fff !important; border-color: rgba(255, 255, 255, .4); }
.topbar .btn.ghost:hover { color: #fff !important; border-color: #fff; background: rgba(255, 255, 255, .16); }
.lic-badge { padding: 3px 11px; border-radius: 99px; font-size: var(--fs-xs); font-weight: 600; letter-spacing: .01em; white-space: nowrap; }
.lic-badge.ok { background: rgba(47, 191, 123, .12); color: var(--up); border: 1px solid rgba(47, 191, 123, .25); }
.lic-badge.bad { background: rgba(240, 86, 95, .12); color: var(--down); border: 1px solid rgba(240, 86, 95, .25); }
.lic-badge.warn { background: rgba(224, 163, 46, .14); color: #C08820; border: 1px solid rgba(224, 163, 46, .3); }
/* Each top-bar chip carries a full and a short rendering (see app.js); the phone-width stylesheet
   swaps them by CSS so a rotation needs no JS. */
.short { display: none; }

main { max-width: 1320px; margin: 0 auto; padding: 24px 20px 64px; }
h1 { font-size: var(--fs-xl); font-weight: 650; letter-spacing: -.02em; margin-bottom: 18px; }
h2 { font-size: 1.1rem; font-weight: 620; letter-spacing: -.015em; }
.muted { color: var(--muted); }
code { font-family: var(--mono); font-size: .86em; background: var(--panel2); padding: 1px 6px; border-radius: 5px; border: 1px solid var(--line-soft); }

/* ── stat cards ──────────────────────────────────────────────────────────── */
.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(168px, 1fr)); gap: 14px; margin-bottom: 22px; }
.stat { background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius); padding: 17px 18px; position: relative; overflow: hidden; }
.stat::after { content: ''; position: absolute; inset: 0 0 auto 0; height: 2px; background: linear-gradient(90deg, var(--accent), transparent 65%); opacity: .55; }
.stat b {
  font-size: 1.7rem; display: block; letter-spacing: -.028em; font-weight: 650; line-height: 1.15;
  font-variant-numeric: tabular-nums; font-feature-settings: 'tnum' 1;
}
.stat b.up { color: var(--up); } .stat b.down { color: var(--down); } .stat b.warn { color: var(--warn); }
.stat span { color: var(--muted); font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: .07em; font-weight: 600; }

/* ── panels & tables ─────────────────────────────────────────────────────── */
/* Every page shows and hides a panel with classList.add('hidden') / remove('hidden') — 23 call sites — but
   there was no generic `.hidden` rule anywhere: only `.modal.hidden` and `.connect-hint.hidden` were ever
   defined. So a `<div class="panel hidden">` stayed fully visible, and anything that "closed" it applied the
   class and changed nothing on screen. A Close button reported as broken was this, not the button.
   !important because other single-class rules set `display` too (.btn is display:inline-flex) and whichever
   comes later in this file would otherwise win — "hidden" must mean hidden, not "hidden unless styled". */
.hidden { display: none !important; }
.panel { background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius); padding: 20px; margin-bottom: 18px; overflow-x: auto; }
.panel h3 {
  font-size: var(--fs); font-weight: 620; letter-spacing: -.01em; margin-bottom: 14px;
  padding-bottom: 12px; border-bottom: 1px solid var(--line-soft);
  display: flex; align-items: center; gap: 8px;
}
table { width: 100%; border-collapse: collapse; }
th, td { text-align: left; padding: 10px 12px; border-bottom: 1px solid var(--line-soft); font-size: var(--fs-sm); }
th { white-space: nowrap; }
td { white-space: normal; overflow-wrap: break-word; }
th {
  font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: .08em; color: var(--muted);
  font-weight: 650; border-bottom-color: var(--line); padding-top: 4px;
}
tbody tr { transition: background .1s ease; }
tr:hover td { background: var(--line-soft); }
tr:last-child td { border-bottom: none; }
td .mono, .mono { font-family: var(--mono); font-size: .85em; font-variant-numeric: tabular-nums; letter-spacing: -.01em; }
.dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-right: 8px; vertical-align: middle; }
.dot.up { background: var(--up); box-shadow: 0 0 0 3px rgba(47, 191, 123, .16); }
.dot.down { background: var(--down); box-shadow: 0 0 0 3px rgba(240, 86, 95, .16); }
.dot.warn { background: var(--warn); box-shadow: 0 0 0 3px rgba(224, 163, 46, .16); }
.dot.unknown { background: var(--muted); }
.badge { padding: 2px 10px; border-radius: 99px; font-size: var(--fs-xs); font-weight: 650; letter-spacing: .01em; }
.badge.up { background: rgba(47, 191, 123, .12); color: var(--up); }
.badge.down { background: rgba(240, 86, 95, .12); color: var(--down); }
.badge.warn { background: rgba(224, 163, 46, .13); color: var(--warn); }
.badge.info { background: var(--accent-soft); color: var(--accent); }
/* paused: monitoring deliberately turned off, so the device must NOT look like a fault (red) and must not
   look healthy (green) either — it is a third state and reads as one. */
.dot.paused { background: #8a94a6; box-shadow: 0 0 0 3px rgba(138, 148, 166, .18); }
.badge.paused { background: rgba(138, 148, 166, .16); color: #aab4c4; }

/* ── buttons ─────────────────────────────────────────────────────────────── */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  padding: 9px 16px; min-height: 38px; border-radius: var(--radius-sm);
  border: 1px solid transparent; background: var(--accent2); color: #fff !important;
  cursor: pointer; font-size: var(--fs-sm); font-weight: 620; font-family: inherit;
  letter-spacing: -.005em; transition: background .12s ease, border-color .12s ease, transform .06s ease;
}
.btn:hover { background: #3B7CF0; }
.btn:active { transform: translateY(1px); }
.btn.ghost { background: transparent; color: var(--ink-dim) !important; border-color: var(--line); }
.btn.ghost:hover { border-color: var(--accent); color: var(--accent) !important; background: var(--accent-soft); }
.btn.danger { background: transparent; color: var(--down) !important; border-color: rgba(240, 86, 95, .45); }
.btn.danger:hover { background: rgba(240, 86, 95, .12); border-color: var(--down); }
.btn.small { padding: 5px 11px; min-height: 30px; font-size: var(--fs-xs); border-radius: var(--radius-xs); }
.btn:disabled { opacity: .45; cursor: not-allowed; }
.btn[download] { border-color: var(--line); background: var(--panel2); color: var(--ink) !important; }
.btn[download]:hover { border-color: var(--accent); color: var(--accent) !important; background: var(--accent-soft); }

/* ── forms ───────────────────────────────────────────────────────────────── */
input, select, textarea {
  padding: 9px 12px; min-height: 38px; border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--panel2); color: var(--ink); font-family: inherit; font-size: var(--fs-sm);
  max-width: 100%; outline: none; transition: border-color .12s ease, box-shadow .12s ease;
}
input::placeholder, textarea::placeholder { color: #63758F; }
input:focus, select:focus, textarea:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-ring); }
input[type=text], input[type=password], input[type=number], select { min-width: 170px; }
input[type=checkbox] { min-height: 0; width: 16px; height: 16px; padding: 0; margin: 0; border: none; background: none; box-shadow: none; accent-color: var(--accent2); vertical-align: middle; cursor: pointer; }
.field { margin-bottom: 14px; }
.field label { display: block; font-size: var(--fs-xs); color: var(--muted); margin-bottom: 6px; font-weight: 550; }
.row { display: flex; gap: 12px; flex-wrap: wrap; align-items: center; margin-bottom: 14px; }
.err { color: var(--down); font-size: var(--fs-sm); min-height: 1.2em; }
.ok { color: var(--up); font-size: var(--fs-sm); }

/* ── utilization bar ─────────────────────────────────────────────────────── */
.util { display: flex; align-items: center; gap: 9px; }
.util .bar { width: 88px; height: 6px; border-radius: 99px; background: var(--panel3); overflow: hidden; }
.util .bar i { display: block; height: 100%; border-radius: 99px; transition: width .3s ease; }
.util.bw-ok .bar i { background: var(--up); }
.util.bw-warn .bar i { background: var(--warn); }
.util.bw-crit .bar i { background: var(--down); }

/* ── charts ──────────────────────────────────────────────────────────────── */
canvas { width: 100%; height: 200px; display: block; }
.chart-wrap { position: relative; }
/* The data charts author themselves at their real pixel width (see the draw functions), so an
   explicit height keeps the plot area predictable at every screen size. */
svg.chart { width: 100%; display: block; height: 190px; }
@media (max-width: 640px) { svg.chart { height: 210px; } }

/* ── login ───────────────────────────────────────────────────────────────── */
.login-card {
  max-width: 400px; margin: 72px auto; background: var(--panel); border: 1px solid var(--line);
  border-radius: 18px; padding: 38px 34px 32px; text-align: center; position: relative;
  box-shadow: var(--shadow);
}
.login-card::before {
  content: ''; position: absolute; left: 12%; right: 12%; top: -1px; height: 1px;
  background: linear-gradient(90deg, transparent, var(--accent), transparent); opacity: .8;
}
.login-card img { width: 72px; height: 72px; border-radius: 18px; margin-bottom: 14px; }
.login-card h1 { font-size: 1.25rem; margin-bottom: 4px; }
.login-card .sub { color: var(--muted); font-size: var(--fs-sm); margin-bottom: 24px; }
.login-card .form { display: flex; flex-direction: column; gap: 14px; text-align: left; }
.login-card .form label { font-size: var(--fs-xs); color: var(--muted); font-weight: 550; margin-bottom: 6px; display: block; }
.login-card .form input { width: 100%; }
.login-card .btn { width: 100%; min-height: 42px; }
.license-box {
  background: rgba(224, 163, 46, .07); border: 1px solid rgba(224, 163, 46, .28);
  border-radius: var(--radius-sm); padding: 13px 15px; margin-bottom: 16px; color: var(--warn); font-size: var(--fs-sm);
}
.grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
.grid2 > * { min-width: 0; }
@media (max-width: 1100px) { .grid2 { grid-template-columns: 1fr; } }
/* ── Mobile ────────────────────────────────────────────────────────────────
   This app was desktop-only. The top bar's nav alone is 666 px wide, so on a 390 px phone
   everything past "Devices" — Alerts, Report, Settings, Keys, OLT — was simply unreachable, and
   wide data tables stretched the page sideways. Now: the bar wraps into two rows with a
   horizontally scrollable nav, tables scroll inside their own card, grids collapse to one column,
   and tap targets meet the 40 px minimum. */
@media (max-width: 1180px) {
  /* The bar needs two rows well before phone width: the nav alone is 666 px, and squeezing it
     against the brand and the status chips wrapped "Light mode" onto two lines. */
  .topbar-in { height: auto; flex-wrap: wrap; padding: 8px 12px; gap: 6px 10px; align-items: center; }
  .brand { order: 1; flex: 0 0 auto; font-size: 1rem; }
  .brand img { width: 28px; height: 28px; border-radius: 7px; }
  /* controls sit beside the brand; the nav takes the row below */
  .topbar .right { order: 2; flex: 0 1 auto; min-width: 0; margin-left: auto; gap: 8px 10px; white-space: nowrap; }
  .topbar nav {
    order: 3; flex: 1 0 100%;
    display: flex; gap: 4px;
    overflow-x: auto; -webkit-overflow-scrolling: touch;
    scrollbar-width: none; padding-bottom: 2px;
  }
  .topbar nav::-webkit-scrollbar { display: none; }
  .topbar nav a { white-space: nowrap; padding: 8px 12px; font-size: .8rem; }
}

@media (max-width: 900px) {
  main { padding: 18px 12px 56px; }
  h1 { font-size: 1.32rem; margin-bottom: 14px; }
  .panel { padding: 15px; }
}

@media (max-width: 640px) {
  /* Compact top-bar chips. The bar was 180 px tall (four rows) on a phone; this brings it to two.
     Each chip carries a full and a short rendering (see app.js) swapped by CSS, so a rotation
     needs no JS. */
  .full { display: none; }
  .short { display: inline; }
  .hide-sm { display: none !important; }
  /* Keep the mark + "SBL" only: the full brand plus the controls cannot share a 366 px row. */
  .brand span { display: none; }
  /* The licence chip is the least critical thing up here (it is on Settings too, with a tooltip
     everywhere else). Hiding just this one buys a whole row — but a "no license" WARNING is never
     hidden, and the IP chip Abdul asked for stays visible. */
  .lic-badge.ok { display: none; }
  .topbar .right { flex-wrap: nowrap; gap: 6px; }
  .topbar .right > button { padding: 0 10px; }
  #ipBadge { font-size: .68rem !important; }
  /* a wide data table must scroll inside its card, not stretch the whole page */
  table { display: block; width: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch; }
  th, td { white-space: nowrap; }
  .panel { padding: 12px; }
  /* any 12-column span becomes full width */
  .z-grid > * { grid-column: 1 / -1 !important; }
  .grid2 { grid-template-columns: 1fr; gap: 12px; }
  .cards { grid-template-columns: repeat(auto-fit, minmax(136px, 1fr)); gap: 10px; margin-bottom: 16px; }
  .stat { padding: 13px 14px; }
  .stat b { font-size: 1.42rem; }
  /* comfortable tap targets (Apple/Google both recommend >= 40 px) */
  .btn, button, a.btn { min-height: 40px; }
  .btn.small { min-height: 34px; }
  input[type=text], input[type=password], input[type=number], select, textarea { min-width: 0 !important; max-width: 100%; }
  .field { min-width: 0; }
  .row { gap: 8px; }
  h2 { font-size: 1rem; }
  #ipBadge { font-size: .7rem; }
  .lic-badge { font-size: .68rem; }
  /* charts: keep the plot area usable rather than letterboxed */
  svg.chart, #chartWrap, .chart-wrap { min-width: 0; }
}

/* ── Light (bright) theme — toggled via <html data-theme="light"> ──
   Same layout and accent; only surfaces and text invert. Status colours are deepened slightly so
   they still hold contrast on a white ground. Toggled from the top-bar button (app.js). */
html[data-theme="light"] {
  --chart-bg: #FFFFFF;
  --chart-grid: #E2E8F1;
  --chart-axis: #5C6D82;
  --chart-note: #34455A;
  --chart-tip-bg: rgba(255, 255, 255, .97);
  --chart-tip-fg: #16212E;
  --bg: #F4F6FB;
  --bg-soft: #EAEEF5;
  --panel: #FFFFFF;
  --panel2: #F1F4F9;
  --panel3: #E7ECF3;
  --line: #D7DFE9;
  --line-soft: rgba(16, 26, 44, .08);

  --ink: #16212E;
  --ink-dim: #34455A;
  --muted: #5C6D82;

  --accent: #2456B4;
  --accent2: #2456B4;
  --accent-soft: rgba(47, 111, 228, .10);
  --accent-ring: rgba(47, 111, 228, .18);
  --gold: #B8860B;

  --up: #1E9E63;
  --warn: #C07D0A;
  --down: #DC3D47;
  --crit: #DC3D47;
  --olt: #7C5CE0;
  --bgp: #0E9C8C;

  --shadow: 0 1px 2px rgba(15, 23, 42, .05), 0 8px 24px -14px rgba(15, 23, 42, .16);
}

/* --------------------------------------------------------------------------
   A box that shows the first few rows and scrolls for the rest.
   The height is set in JS from the real rendered row height, not guessed here,
   so it is genuinely five rows whatever the font size or zoom level.
   -------------------------------------------------------------------------- */
.scrollbox { overflow-y: auto; overscroll-behavior: contain; }
.scrollbox::-webkit-scrollbar { width: 9px; }
.scrollbox::-webkit-scrollbar-track { background: rgba(255,255,255,.04); border-radius: 5px; }
.scrollbox::-webkit-scrollbar-thumb { background: rgba(255,255,255,.18); border-radius: 5px; }
.scrollbox::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,.3); }
.scrollbox table tr:first-child th { position: sticky; top: 0; background: var(--card); z-index: 1; }
.rowcount { font-size: var(--fs-xs); color: var(--muted-foreground); margin-top: 6px; }

/* --------------------------------------------------------------------------
   Device console (AI page). The terminal is a real pane on the page, not a
   popup — a 20-line floating window is the wrong shape for reading a config.
   -------------------------------------------------------------------------- */
.cliGrid { display: grid; grid-template-columns: minmax(0, 1fr) 320px; gap: 16px; align-items: start; }
@media (max-width: 1050px) { .cliGrid { grid-template-columns: 1fr; } }
.cliAside { position: sticky; top: 10px; display: flex; flex-direction: column; gap: 10px; }
.aside-h { font-size: var(--fs-sm); font-weight: 600; margin-bottom: 6px; }
.term { height: 400px; overflow: auto; background: #0a111e; border: 1px solid var(--border); border-radius: 10px;
        padding: 10px 12px; font-family: 'JetBrains Mono', monospace; font-size: .75rem; line-height: 1.55;
        white-space: pre-wrap; word-break: break-word; overscroll-behavior: contain; }
.term span { display: block; }
.t-cmd { color: #8FD6FF; }
.t-out { color: #d9e3ef; }
.t-err { color: var(--down); }
.t-ok { color: var(--up); }
.t-warn { color: #C08820; }
.t-dim { color: #74849b; }
.t-block { color: var(--down); font-weight: 700; }
.advice { border-left: 3px solid var(--accent); background: rgba(47,111,228,.09); padding: 10px 12px;
          border-radius: 0 9px 9px 0; font-size: var(--fs-sm); line-height: 1.55; }
.advice-alt { margin-top: 8px; padding-top: 7px; border-top: 1px solid var(--border); font-size: var(--fs-xs); color: var(--muted-foreground); }
.risk { display: inline-block; margin-top: 8px; font-size: 10px; text-transform: uppercase; letter-spacing: .05em;
        border: 1px solid var(--border); border-radius: 5px; padding: 2px 7px; }
.risk-low { color: var(--up); border-color: rgba(47,191,123,.5); }
.risk-medium { color: #C08820; border-color: rgba(224,163,46,.5); }
.risk-high { color: var(--down); border-color: rgba(240,86,95,.5); }
.planlist { display: flex; flex-direction: column; gap: 6px; }
.planstep { border: 1px solid var(--border); border-radius: 9px; padding: 7px 9px; }
.planstep.warn { border-color: rgba(224,163,46,.45); background: rgba(224,163,46,.07); }
.planstep.bad { border-color: rgba(240,86,95,.5); background: rgba(240,86,95,.09); }
.plan-n { display: inline-block; width: 18px; color: var(--muted-foreground); font-size: .7rem; }
.plan-cmd { font-size: .78rem; }
.plan-why { font-size: .72rem; color: var(--muted-foreground); margin: 3px 0 0 18px; }

/* --------------------------------------------------------------------------
   Flow evidence on the DDoS page: what the addresses are, and what to do first.
   -------------------------------------------------------------------------- */
.v-atk { background: rgba(224,163,46,.18); border: 1px solid rgba(224,163,46,.5); color: #C08820; }
.flowblock { margin-top: 10px; padding: 10px 12px; border-left: 3px solid var(--accent);
             background: rgba(47,111,228,.07); border-radius: 0 9px 9px 0; }
.attackline { font-weight: 600; font-size: var(--fs-sm); }
.firstaction { margin-top: 8px; font-size: var(--fs-sm); padding: 7px 10px; border-radius: 8px;
               background: rgba(47,191,123,.09); border-left: 3px solid var(--up); }
.who { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 5px; }
.whochip { display: inline-flex; align-items: center; gap: 6px; border: 1px solid var(--border);
           border-radius: 8px; padding: 3px 6px 3px 9px; font-size: .74rem; }
table.tight td, table.tight th { padding: 4px 8px; font-size: var(--fs-xs); }
table.tight { width: 100%; }

/* --------------------------------------------------------------------------
   Flow coverage: every switch, and whether it feeds the collector.
   The bar answers "how blind am I overall"; the rows answer "what do I do about
   this one switch". Status is carried by a dot AND a word, because colour alone
   is not readable for everyone and "can export" versus "cannot export" is the
   whole point of the table.
   -------------------------------------------------------------------------- */
.covbar { height: 8px; border-radius: 5px; background: rgba(255,255,255,0.08); overflow: hidden; margin: 2px 0 4px; }
.covbar i { display: block; height: 100%; background: #0E8A6F; border-radius: 5px; transition: width .3s ease; }
.covrow { border-top: 1px solid rgba(255,255,255,0.07); }
.covhead { display: flex; align-items: center; gap: 10px; padding: 7px 2px; cursor: pointer; flex-wrap: wrap; font-size: var(--fs-sm); }
.covhead:hover { background: rgba(255,255,255,0.03); }
.covdot { width: 8px; height: 8px; border-radius: 50%; flex: none; background: #6b7280; }
.covdot.ok   { background: #0E8A6F; box-shadow: 0 0 6px rgba(18,169,138,.6); }
.covdot.warn { background: #C08820; }
.covdot.dim  { background: #55607a; }
.covname { font-weight: 600; min-width: 148px; }
.covstat { font-size: var(--fs-xs); padding: 2px 8px; border-radius: 10px; border: 1px solid transparent; }
.covstat.ok   { color: #0E8A6F; border-color: rgba(18,169,138,.45); background: rgba(18,169,138,.08); }
.covstat.warn { color: #C08820; border-color: rgba(224,163,46,.45); background: rgba(224,163,46,.08); }
.covstat.dim  { color: #93a0b8; border-color: rgba(147,160,184,.3); }
.covdetail { padding: 4px 2px 12px 18px; }
.covdetail .raw { white-space: pre; overflow-x: auto; font-size: 11.5px; line-height: 1.55; }
.fallbacknote { margin-top: 8px; padding: 8px 10px; border-left: 3px solid #C08820; background: rgba(224,163,46,.07); border-radius: 0 5px 5px 0; font-size: var(--fs-xs); }

/* Why a device is producing nothing. A host whose SNMP agent is not running otherwise shows as a blank row —
   ping replies, no interfaces, no history — which reads as "never added" rather than "not answering", and the
   operator goes looking in the wrong place. Truncated so a long diagnosis cannot stretch the row. */
/* A literal colour, not a variable. On the dashboard `--warn` resolves to a muted grey, so the reason the
   device is silent came out looking like background text — the one line on the row that matters most was the
   one line you could not see. */
/* Specificity matters here: the dashboard's own <style> block sets `.dev-card .sub { color: var(--muted) }`,
   which is the same weight as `.dev-card .snmp-err` and comes later in the document — so the reason rendered as
   muted background text. Three classes outrank two. */
.snmp-err, .dev-card .sub.snmp-err {
  display: block;
  margin-top: 3px;
  font-size: .72rem;
  line-height: 1.3;
  color: #C08820;
  max-width: 46ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Why a device page has no numbers. Without this the page shows CPU/RAM/uptime as bare dashes and an empty
   table, which reads as a broken page rather than as a device whose agent is not answering. */
.dev-note {
  display: flex;
  flex-direction: column;
  gap: 3px;
  margin: 12px 0 4px;
  padding: 10px 13px;
  border: 1px solid rgba(224, 163, 46, .3);
  border-left: 3px solid #C08820;
  border-radius: var(--radius-xs, 6px);
  background: rgba(224, 163, 46, .07);
  font-size: .82rem;
  line-height: 1.45;
  color: var(--ink, #e8eef7);
}
.dev-note strong { color: #C08820; }
.dev-note .muted { color: var(--muted, #94a6c0); }
