/* ============================================================== themes ===
 * Matched to PUK Booking (D:/Docker_Data/puk_booking): Inter, the zinc
 * neutral scale on black, orange-500 as the accent, and its rounded-xl
 * corners. A system monospace still carries anything a machine produced
 * (addresses, ports, keys). Status stays a dot plus a label.
 *
 * Brand orange is 2.9:1 on white, so it is split the way PUK Booking
 * splits it: --accent for fills and borders, --accent-text for the
 * deepened shade links use, near-black on orange for buttons.
 *
 * Three states: an explicit choice stamps data-theme on <html>; with no
 * choice the OS preference decides. Every colour is a token so both themes
 * stay in step - never hard-code a colour below this block.
 */
:root,
:root[data-theme="light"] {
  color-scheme: light;
  --bg: #f4f4f5;              /* zinc-100, their html.light body */
  --surface: #ffffff;
  --surface-2: #f1f1f3;
  --border: #e4e4e7;          /* zinc-800 as it maps in their light mode */
  --rule: #ececee;            /* hairline inside tables and lists */
  --text: #18181b;            /* zinc-900 */
  --muted: #52525b;           /* zinc-600 */
  --faint: #71717a;           /* zinc-500: labels, table heads, timestamps */
  --accent: #f97316;          /* orange-500, unchanged across themes */
  --accent-hover: #ea580c;    /* orange-600 */
  --accent-text: #c2410c;     /* orange-700: the readable shade for links */
  --on-accent: #18181b;       /* near-black on orange, as their buttons do */
  --danger: #dc2626;

  --ok-fg: #047857;           /* their light overrides, verbatim */
  --warn-fg: #b45309;
  --down-fg: #71717a;
  --danger-fg: #dc2626;
  --ok-bg: rgba(16, 185, 129, 0.12);
  --warn-bg: rgba(245, 158, 11, 0.14);
  --down-bg: rgba(113, 113, 122, 0.12);
  --danger-bg: rgba(239, 68, 68, 0.10);
  --accent-bg: rgba(249, 115, 22, 0.12);

  --code-text: #27272a;
  --code-bg: #f4f4f5;
  --backdrop: rgba(24, 24, 27, 0.45);
  --shadow: 0 16px 40px rgba(24, 24, 27, 0.16);

  /* Theme-independent, so defined once here. PUK Booking's dominant
     corner is rounded-xl; this app was on 8px. */
  --radius: 12px;
  --radius-sm: 10px;
  --radius-xs: 8px;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --bg: #000000;              /* bg-black, the page they author on */
    --surface: #18181b;         /* zinc-900 cards */
    --surface-2: #27272a;       /* zinc-800 */
    --border: #27272a;          /* zinc-800, their dominant border */
    --rule: #1f1f23;
    /* Three readable steps rather than PUK Booking's zinc-300/400/500. Its
       zinc-500 sits at 3.6:1 on a card, and --faint carries the small uppercase
       labels here (table heads, stat captions), so it has to clear 4.5:1. Same
       scale, shifted one step: white primary as their text-white, zinc-300 body
       as theirs, zinc-400 for the labels. */
    --text: #fafafa;            /* zinc-50, their text-white */
    --muted: #d4d4d8;           /* zinc-300, their body text */
    --faint: #a1a1aa;           /* zinc-400 */
    --accent: #f97316;          /* orange-500 */
    --accent-hover: #fb923c;    /* orange-400 lifts on hover against black */
    --accent-text: #fb923c;
    --on-accent: #18181b;
    --danger: #ef4444;          /* red-500 */

    --ok-fg: #34d399;           /* emerald-400 */
    --warn-fg: #f59e0b;         /* amber-500 */
    --down-fg: #a1a1aa;         /* zinc-400 */
    --danger-fg: #f87171;       /* red-400 */
    --ok-bg: rgba(16, 185, 129, 0.16);
    --warn-bg: rgba(245, 158, 11, 0.16);
    --down-bg: rgba(161, 161, 170, 0.16);
    --danger-bg: rgba(239, 68, 68, 0.16);
    --accent-bg: rgba(249, 115, 22, 0.16);

    --code-text: #d4d4d8;
    --code-bg: #09090b;         /* zinc-950 */
    --backdrop: rgba(0, 0, 0, 0.7);
    --shadow: 0 18px 50px rgba(0, 0, 0, 0.6);
  }
}

:root[data-theme="dark"] {
  color-scheme: dark;
  --bg: #000000;              /* bg-black, the page they author on */
  --surface: #18181b;         /* zinc-900 cards */
  --surface-2: #27272a;       /* zinc-800 */
  --border: #27272a;          /* zinc-800, their dominant border */
  --rule: #1f1f23;
  /* Three readable steps rather than PUK Booking's zinc-300/400/500. Its
     zinc-500 sits at 3.6:1 on a card, and --faint carries the small uppercase
     labels here (table heads, stat captions), so it has to clear 4.5:1. Same
     scale, shifted one step: white primary as their text-white, zinc-300 body
     as theirs, zinc-400 for the labels. */
  --text: #fafafa;            /* zinc-50, their text-white */
  --muted: #d4d4d8;           /* zinc-300, their body text */
  --faint: #a1a1aa;           /* zinc-400 */
  --accent: #f97316;          /* orange-500 */
  --accent-hover: #fb923c;    /* orange-400 lifts on hover against black */
  --accent-text: #fb923c;
  --on-accent: #18181b;
  --danger: #ef4444;          /* red-500 */

  --ok-fg: #34d399;           /* emerald-400 */
  --warn-fg: #f59e0b;         /* amber-500 */
  --down-fg: #a1a1aa;         /* zinc-400 */
  --danger-fg: #f87171;       /* red-400 */
  --ok-bg: rgba(16, 185, 129, 0.16);
  --warn-bg: rgba(245, 158, 11, 0.16);
  --down-bg: rgba(161, 161, 170, 0.16);
  --danger-bg: rgba(239, 68, 68, 0.16);
  --accent-bg: rgba(249, 115, 22, 0.16);

  --code-text: #d4d4d8;
  --code-bg: #09090b;         /* zinc-950 */
  --backdrop: rgba(0, 0, 0, 0.7);
  --shadow: 0 18px 50px rgba(0, 0, 0, 0.6);
}

/* ============================================================== base === */
* { box-sizing: border-box; }
/* An author rule that sets `display` outranks the browser's own
   [hidden] rule, so anything styled with display: flex/grid would show
   while marked hidden. Settle it once, globally. */
[hidden] { display: none !important; }
body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: "Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  font-size: 15px;
  line-height: 1.55;
}
a { color: var(--accent-text); text-decoration: none; }
a:hover { color: var(--accent-hover); text-decoration: underline; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

h2 { font-size: 21px; font-weight: 600; letter-spacing: -0.2px; margin: 18px 0 14px; }
h3 { font-size: 13.5px; font-weight: 600; margin: 0 0 12px; }
h4 {
  font-size: 10.5px; font-weight: 500; letter-spacing: 0.7px; text-transform: uppercase;
  color: var(--faint); margin: 16px 0 7px;
}
.muted { color: var(--muted); }
.small { font-size: 12.5px; }
/* Anything a machine produced - addresses, ports, keys - sets in mono. */
.mono, pre, code, input[name="network"], input[name="allowed_ips"] {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
  font-variant-numeric: tabular-nums;
}
.ellipsis { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
hr { border: none; border-top: 1px solid var(--border); margin: 18px 0; }
code { color: var(--accent-text); font-size: 0.92em; }

/* ============================================================ layout === */
.topbar {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  padding: 0 24px; min-height: 56px;
  background: var(--surface); border-bottom: 1px solid var(--border);
}
.brand {
  display: flex; align-items: center; gap: 9px;
  font-weight: 600; font-size: 15px; letter-spacing: 0.2px; white-space: nowrap;
}
/* The sign-in and unlock cards stack: logo above the name, both centred.
   The top bar keeps them side by side - a nav row has no vertical room. */
.brand.big {
  font-size: 22px; flex-direction: column; justify-content: center;
  gap: 10px; text-align: center;
}
.logo { color: var(--accent-text); display: inline-flex; }
.topbar nav { display: flex; align-items: center; flex-wrap: wrap; gap: 2px; }
.topbar nav a {
  display: flex; align-items: center; padding: 6px 11px;
  border-radius: var(--radius-sm); color: var(--muted); font-size: 13.5px;
}
.topbar nav a:hover { color: var(--text); background: var(--surface-2); text-decoration: none; }
.topbar nav a.active { background: var(--surface-2); color: var(--text); font-weight: 500; }
.topbar nav a.muted { color: var(--faint); font-size: 12.5px; margin-left: 6px; }
/* Which tenant's data is on screen. Kept next to the account so the two are
   read together - it changes the meaning of every page behind it. */
.tenant-chip {
  display: inline-flex; align-items: center; margin-left: 10px;
  padding: 4px 9px; border: 1px solid var(--border); border-radius: 999px;
  background: var(--surface-2); color: var(--text);
  font-size: 12px; font-weight: 500; letter-spacing: 0.01em;
  max-width: 180px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.container { max-width: 1180px; margin: 0 auto; padding: 24px 28px 48px; }
.foot { text-align: center; color: var(--faint); font-size: 12px; padding: 28px 0; }

.card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 16px 18px; margin-bottom: 16px;
}
.card.center { text-align: center; }
.card.narrow { max-width: 420px; }

.grid { display: grid; gap: 14px; }
.grid.two { grid-template-columns: 1fr 1fr; }
.grid.stats { grid-template-columns: repeat(4, minmax(0, 1fr)); margin-bottom: 18px; }
@media (max-width: 820px) {
  .grid.two { grid-template-columns: 1fr; }
  .grid.stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.card.stat { padding: 15px 16px; margin: 0; display: flex; flex-direction: column; gap: 6px; }
.stat .stat-label {
  color: var(--faint); font-size: 11px; text-transform: uppercase; letter-spacing: 0.7px;
  font-weight: 500;
}
.stat .stat-value {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-variant-numeric: tabular-nums;
  font-size: 26px; font-weight: 500; line-height: 1.1; margin-top: 1px;
}

.row { display: flex; align-items: center; }
.row.between { justify-content: space-between; }
.row.gap { gap: 8px; margin-top: 10px; }
.row.wrap { flex-wrap: wrap; gap: 20px; align-items: flex-start; }
.col { flex: 1; }

/* ============================================================= forms === */
label { display: block; color: var(--muted); font-size: 12.5px; margin: 12px 0 4px; }
input, textarea, select {
  width: 100%; padding: 7px 10px; background: var(--bg);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  color: var(--text); font-family: inherit; font-size: 13.5px;
}
select { width: auto; }
textarea { margin: 6px 0; resize: vertical; font-family: inherit; }
input::placeholder, textarea::placeholder { color: var(--faint); }
input:focus, textarea:focus, select:focus { outline: none; border-color: var(--accent); }

.btn {
  display: inline-block; padding: 6px 12px; border: 1px solid var(--border);
  border-radius: var(--radius-sm); background: var(--surface-2); color: var(--text);
  cursor: pointer; font-family: inherit; font-size: 13px;
}
.btn:hover { border-color: var(--accent); color: var(--accent-text); text-decoration: none; }
.btn.primary {
  background: var(--accent); border-color: var(--accent); color: var(--on-accent);
  font-weight: 500; margin-top: 12px;
}
.btn.primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); color: var(--on-accent); }
.btn.ghost { background: transparent; }
.btn.tiny { padding: 3px 9px; font-size: 12px; }
/* .btn.primary carries margin-top for the gap it needs when it follows a
   form field in normal block flow. In a container that lays buttons out
   horizontally that margin means nothing except "sit 6px lower than your
   siblings", which is how the primary button ended up out of line on the
   dashboard, the tenants page and the row actions in turn.

   So every such container cancels it, in one place. Listed after
   .btn.primary deliberately: the selectors have equal specificity, so
   source order is the whole of the decision - written above it, this rule
   loses silently and the buttons stay crooked. */
.row > .btn,
.head-actions > .btn,
.actions > .btn,
.menu-list .btn,
.peer-form .btn,
.group-edit .btn,
dialog.modal .btn,
.btn.tiny { margin-top: 0; }
.btn.danger { color: var(--danger-fg); border-color: transparent; background: transparent; }
.btn.danger:hover { border-color: var(--danger); color: var(--danger-fg); }
.btn.danger-solid { background: var(--danger); border-color: var(--danger); color: #fff; }
.btn.danger-solid:hover { filter: brightness(1.06); color: #fff; }
.inline { display: inline; }

.peer-form { display: flex; gap: 8px; flex-wrap: wrap; align-items: flex-end; }
.peer-form input { flex: 1; min-width: 150px; }
.peer-form .btn { white-space: nowrap; }

.route-form { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; margin-top: 10px; }
.route-form input[name="network"] { max-width: 165px; }
.route-form input[name="comment"] { max-width: 150px; }
.chk {
  display: inline-flex; align-items: center; gap: 5px; margin: 0;
  font-size: 12px; color: var(--muted);
}
.chk input { width: auto; }

/* ============================================================ tables === */
table.peers {
  width: 100%; border-collapse: collapse; background: var(--surface);
  border: 1px solid var(--border); border-radius: var(--radius);
}
table.peers th, table.peers td { text-align: left; padding: 11px 14px; border-bottom: 1px solid var(--rule); }
table.peers th {
  color: var(--faint); font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.7px;
  font-weight: 500; border-bottom: 1px solid var(--border);
}
table.peers td { font-size: 13.5px; }
table.peers tr:last-child td { border-bottom: none; }
table.peers tr.disabled { opacity: 0.5; }
table.peers.compact { background: transparent; border: none; }
table.peers.compact th, table.peers.compact td { padding: 7px 8px; font-size: 13px; }
.peer-name { font-weight: 500; color: var(--text); }
a.peer-name:hover { color: var(--accent-text); }
/* .actions is used on plain divs, where flex is right, and on table cells,
   where it is not: display:flex takes a <td> out of the table layout, so the
   cell stops filling the row and its buttons ride at the top of a tall row.
   Cells stay cells and centre their contents the way a cell does. */
.actions { display: flex; gap: 5px; justify-content: flex-end; align-items: center; }
td.actions {
  display: table-cell; vertical-align: middle;
  text-align: right; white-space: nowrap;
}
/* `table.peers th, table.peers td { text-align: left }` is (0,1,2) and beats
   the (0,1,1) rule above, so the row action menu was left-aligned in every
   table while its column header sat on the right. Re-stated at a specificity
   that wins; source order alone does not help against a more specific
   selector. Kept as its own rule so the one above stays a plain `td.actions`
   block, which is what test_an_actions_cell_stays_a_table_cell reads. */
table.peers td.actions { text-align: right; }
td.actions > * { vertical-align: middle; }
td.actions > * + * { margin-left: 5px; }

/* ------------------------------------------------------- row action menu */
.menu { position: relative; display: inline-block; }
.menu-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 26px; padding: 0; line-height: 1;
  border: 1px solid transparent; border-radius: var(--radius-sm);
  background: transparent; color: var(--muted); cursor: pointer;
  font-size: 17px; letter-spacing: 0;
}
.menu-btn:hover, .menu.open .menu-btn {
  background: var(--surface-2); border-color: var(--border); color: var(--text);
}
.menu-list {
  position: absolute; right: 0; top: calc(100% + 4px); z-index: 30;
  min-width: 170px; padding: 4px;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-sm); box-shadow: var(--shadow);
  display: flex; flex-direction: column; gap: 1px; text-align: left;
}
.menu-list[hidden] { display: none; }
.menu-list.drop-up { top: auto; bottom: calc(100% + 4px); }
.menu-list form { display: block; margin: 0; }
.menu-item {
  display: block; width: 100%; text-align: left;
  padding: 7px 10px; border: none; border-radius: var(--radius-xs);
  background: transparent; color: var(--text); cursor: pointer;
  font-family: inherit; font-size: 13px; white-space: nowrap;
}
.menu-item:hover { background: var(--surface-2); color: var(--text); text-decoration: none; }
.menu-item.danger { color: var(--danger-fg); }
.menu-item.danger:hover { background: var(--danger-bg); }
.menu-item.is-disabled { color: var(--faint); cursor: not-allowed; }
.menu-item.is-disabled:hover { background: transparent; }
.menu-sep { height: 1px; background: var(--border); margin: 4px 2px; }

table.kv { width: 100%; border-collapse: collapse; margin-bottom: 12px; }
table.kv td { padding: 5px 0; border-bottom: 1px solid var(--rule); vertical-align: top; font-size: 13px; }
table.kv td:first-child { width: 42%; color: var(--faint); font-size: 12.5px; }

/* ============================================== pills, alerts, feeds ===
 * Status is a coloured dot plus a plain label; type names and counts are
 * quiet chips. Nothing shouts.
 */
.pill {
  display: inline-block; padding: 2px 8px; border-radius: var(--radius-xs);
  font-size: 11.5px; font-weight: 500; background: var(--surface-2); color: var(--muted);
}
.pill.ok, .pill.idle, .pill.warn, .pill.down {
  display: inline-flex; align-items: center; gap: 5px; background: transparent; padding: 2px 0;
}
.pill.ok::before, .pill.idle::before, .pill.warn::before, .pill.down::before {
  content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex: none;
}
/* A tinted chip, not a dot: the dot grammar means "current status", and an
   audit action is a name for something that already happened. Keeping the
   chip shape lets one column be scanned down without the rows changing
   silhouette. */
.pill.danger { background: var(--danger-bg); color: var(--danger-fg); }
.pill.ok { color: var(--ok-fg); }
.pill.idle, .pill.warn { color: var(--warn-fg); }
.pill.down { color: var(--down-fg); }

.alert { padding: 10px 13px; border-radius: var(--radius-sm); margin: 12px 0; font-size: 13.5px; }
.alert.error { background: var(--danger-bg); color: var(--danger-fg); border: 1px solid var(--danger); }
.alert.ok { background: var(--ok-bg); color: var(--ok-fg); border: 1px solid var(--ok-fg); }
.alert.warn { background: var(--warn-bg); color: var(--warn-fg); border: 1px solid var(--warn-fg); }

ul.feed { list-style: none; margin: 0 0 8px; padding: 0; }
ul.feed li { padding: 9px 0; border-bottom: 1px solid var(--rule); font-size: 13px; }
ul.feed li:last-child { border-bottom: none; }
.feed-action {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12.5px;
  color: var(--text); margin-right: 6px;
}
.feed-time { display: block; color: var(--faint); font-size: 11.5px; margin-top: 2px; }

/* =========================================================== content === */
pre.config {
  background: var(--code-bg); border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 12px 14px; overflow-x: auto; font-size: 12px; line-height: 1.6;
  white-space: pre; color: var(--code-text);
}
pre.config.small { font-size: 11.5px; word-break: break-all; white-space: pre-wrap; }
.qr { width: 215px; height: 215px; background: #fff; padding: 8px; border-radius: var(--radius-sm); }
.empty { text-align: center; padding: 28px; color: var(--muted); }
.empty-inline { padding: 5px 0; margin: 0; }

details.bulk { margin-top: 12px; }
details.bulk summary { cursor: pointer; padding: 4px 0; font-size: 12.5px; }
details.bulk[open] summary { margin-bottom: 6px; }
details summary { cursor: pointer; }
details.inline-details { display: inline-block; position: relative; }
details.inline-details summary { list-style: none; display: inline-block; }
details.inline-details summary::-webkit-details-marker { display: none; }
.pw-form {
  position: absolute; right: 0; z-index: 10; background: var(--surface-2);
  border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 8px;
  display: flex; gap: 5px; width: 250px; margin-top: 4px;
}
.pw-form input { font-size: 12.5px; }

.group-edit { display: flex; gap: 6px; align-items: center; }
.group-edit[hidden] { display: none; }
.group-edit input { max-width: 220px; font-size: 13px; }
.btn.disabled { opacity: .45; cursor: not-allowed; }
.btn.disabled:hover { border-color: var(--border); color: var(--text); }

.group-card { border-left: 3px solid var(--border); }
.group-card h3 { display: flex; align-items: center; gap: 8px; }
/* --------------------------------------------- collapsible server rows */
/* Every server card wears the brand colour. A proxy used to be marked with
   --warn-fg, the palette's *warning* shade - so a healthy proxy looked like a
   problem, and once the accent became configurable it ignored the chosen
   colour entirely. The type is already named on a chip beside the title; the
   stripe does not need to carry it too.

   --accent-text rather than --accent: this is a mark on a surface, not a
   fill, so it has to stay visible. A brand colour near the surface's own
   lightness (white on a white card) would otherwise vanish. Same hue, always
   seen - the rule links already follow. */
.server-card { border-left: 3px solid var(--accent-text); padding: 0; }
.server-card h3 { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; font-size: 15px; }

/* The summary stays a block so the browser keeps treating it as the
   disclosure control; an inner row does the layout. */
.server-summary { display: block; list-style: none; cursor: pointer; padding: 12px 16px; }
.server-summary::-webkit-details-marker { display: none; }
.server-summary::marker { content: ""; }
.server-summary:hover { background: var(--surface-2); }
.server-card[open] > .server-summary { border-bottom: 1px solid var(--border); }

.summary-row { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; }
.summary-row .chev { display: inline-flex; color: var(--muted); flex: none; transition: transform .13s ease; }
.server-card[open] .summary-row .chev { transform: rotate(90deg); }
@media (prefers-reduced-motion: reduce) { .summary-row .chev { transition: none; } }
.summary-row .s-name { font-weight: 600; font-size: 15px; }
/* Facts take the slack so the buttons sit hard right. */
.summary-row .s-facts { flex: 1 1 auto; min-width: 0; text-align: right; }
.summary-row .summary-actions { flex: none; }

.server-body { padding: 16px; }

/* ------------------------------------------------------ add-server panel */
.add-panel { margin-bottom: 16px; }
.add-panel > summary.add-toggle { list-style: none; display: inline-block; cursor: pointer; margin-top: 0; }
.add-panel > summary::-webkit-details-marker { display: none; }
.add-panel > summary::marker { content: ""; }
.add-panel > .card { margin-top: 12px; }
.add-panel[open] > summary.add-toggle {
  background: var(--surface-2); color: var(--text); border-color: var(--border);
}

/* ============================================================== tabs === */
nav.tabs { display: flex; gap: 2px; border-bottom: 1px solid var(--border); margin-bottom: 18px; }
nav.tabs .tab {
  padding: 8px 14px; color: var(--muted); border-bottom: 2px solid transparent;
  margin-bottom: -1px; font-size: 13.5px; display: flex; align-items: center; gap: 7px;
}
nav.tabs .tab:hover { color: var(--text); text-decoration: none; }
nav.tabs .tab.active { color: var(--text); border-bottom-color: var(--accent); font-weight: 500; }
.tab-count {
  background: var(--surface-2); color: var(--muted); border-radius: 999px;
  padding: 1px 7px; font-size: 11px; font-variant-numeric: tabular-nums;
}
nav.tabs .tab.active .tab-count { background: var(--accent-bg); color: var(--accent-text); }

/* ====================================================== theme toggle === */
.theme-toggle {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; padding: 0; flex: none; margin-left: 6px;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--surface-2); color: var(--muted); cursor: pointer; line-height: 1;
}
.theme-toggle:hover { color: var(--accent-text); border-color: var(--accent); }
.theme-toggle svg { display: block; }
.theme-toggle.floating { position: fixed; top: 16px; right: 16px; margin: 0; z-index: 20; }

/* The icon shows the theme you would switch TO. Default (no explicit
   choice) follows the OS; the [data-theme] rules below override it. */
.theme-toggle .icon-dark { display: inline-flex; }
.theme-toggle .icon-light { display: none; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) .theme-toggle .icon-dark { display: none; }
  :root:not([data-theme]) .theme-toggle .icon-light { display: inline-flex; }
}
:root[data-theme="dark"] .theme-toggle .icon-dark { display: none; }
:root[data-theme="dark"] .theme-toggle .icon-light { display: inline-flex; }
:root[data-theme="light"] .theme-toggle .icon-dark { display: inline-flex; }
:root[data-theme="light"] .theme-toggle .icon-light { display: none; }

/* ============================================================= login === */
.login-wrap { display: flex; justify-content: center; align-items: center; min-height: 72vh; }
.login-card { width: 340px; text-align: center; }
.login-card form { text-align: left; }
.login-card .btn.primary { width: 100%; }

/* ============================================================= modal === */
dialog.modal {
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface); color: var(--text);
  padding: 20px; max-width: 430px; width: calc(100% - 2rem);
  box-shadow: var(--shadow); font-family: inherit;
}
dialog.modal::backdrop { background: var(--backdrop); }
dialog.modal .modal-head { display: flex; align-items: flex-start; gap: 11px; }
dialog.modal .modal-icon {
  flex: none; width: 30px; height: 30px; border-radius: 999px;
  display: flex; align-items: center; justify-content: center;
  background: var(--danger-bg); color: var(--danger-fg); font-size: 15px; font-weight: 600;
}
dialog.modal h3 { margin: 4px 0 0; font-size: 15px; line-height: 1.4; font-weight: 600; }
dialog.modal p[data-confirm-detail] { margin: 8px 0 0 41px; color: var(--muted); font-size: 13px; }
dialog.modal .modal-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 20px; }

/* A dialog that holds a form rather than a question. Wider, left-aligned, and
   its fields stack instead of sitting on one row - a modal is narrow enough
   that .peer-form's row layout would squeeze every input to nothing. */
dialog.modal.form-modal { max-width: 460px; }
dialog.modal.form-modal h3 { margin: 0; }
dialog.modal.form-modal .modal-head { align-items: center; justify-content: space-between; }
dialog.modal.form-modal .modal-body { margin-top: 14px; }
dialog.modal.form-modal label {
  display: block; font-size: 12.5px; color: var(--muted); margin: 12px 0 4px;
}
dialog.modal.form-modal input,
dialog.modal.form-modal select,
dialog.modal.form-modal textarea { width: 100%; }
dialog.modal.form-modal select[multiple] { min-height: 118px; padding: 4px; }
dialog.modal.form-modal select[multiple] option { padding: 5px 7px; border-radius: 6px; }
/* The close affordance in the corner, matching the row action buttons. */
.modal-close {
  flex: none; width: 28px; height: 28px; padding: 0; line-height: 1;
  font-size: 18px; color: var(--muted); background: none;
  border: 1px solid transparent; border-radius: var(--radius-sm); cursor: pointer;
}
.modal-close:hover { background: var(--surface-2); color: var(--text); }

@media (prefers-reduced-motion: no-preference) {
  dialog.modal[open] { animation: modal-in 0.13s ease-out; }
  @keyframes modal-in {
    from { opacity: 0; transform: translateY(-6px) scale(0.985); }
    to   { opacity: 1; transform: none; }
  }
}

@media (max-width: 760px) {
  .topbar { flex-direction: column; align-items: flex-start; padding: 10px 16px; }
  .container { padding: 18px 16px 40px; }
}

/* Tenant switcher (superadmin). Sits in the top bar next to the account, so
   it reads as "who you are, and whose data you are looking at". */
.tenant-switch { display: inline-flex; margin-left: 10px; }
.tenant-switch select {
  padding: 4px 8px; border: 1px solid var(--border); border-radius: 999px;
  background: var(--surface-2); color: var(--text);
  font-size: 12px; font-weight: 500; font-family: inherit;
  max-width: 190px; cursor: pointer;
}
.tenant-switch select:hover { border-color: var(--accent); }
.tenant-switch select:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* ========================================================== dashboard ===
 * The stat band reads left to right: what you run, who is on it, how much
 * moved, and how far it reaches. Each card carries one number at display
 * size, a meter or split beneath it, and nothing else competing.
 */
.page-head { align-items: flex-start; margin-bottom: 18px; }
.page-head h2 { margin: 18px 0 2px; }
.page-sub { margin: 0 0 0 1px; }
.head-actions { display: flex; gap: 8px; align-items: center; padding-top: 20px; }

.grid.stats { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.card.stat {
  position: relative; overflow: hidden;
  padding: 15px 16px 14px; gap: 0;
}
/* A hairline of colour along the top edge - enough to tell the cards apart
   at a glance without tinting the whole surface. */
.card.stat::before {
  content: ""; position: absolute; inset: 0 0 auto 0; height: 2px;
  background: var(--accent); opacity: 0.85;
}
.card.stat.accent-users::before { background: var(--ok-fg); }
.card.stat.accent-traffic::before { background: var(--warn-fg); }
.card.stat.accent-links::before { background: var(--muted); }

.stat-top { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.stat .stat-icon { color: var(--faint); display: inline-flex; }
.card.stat .stat-value { margin: 6px 0 0; }
.stat-of { color: var(--faint); font-size: 0.6em; font-weight: 400; margin-left: 1px; }
.stat-foot { margin-top: 9px; display: flex; flex-wrap: wrap; gap: 6px; align-items: center; min-height: 19px; }
.warn-text { color: var(--warn-fg); }

/* Thin proportion bar under a fraction. */
.meter {
  margin-top: 9px; height: 3px; border-radius: 2px;
  background: var(--surface-2); overflow: hidden;
}
.meter > span { display: block; height: 100%; background: var(--accent); border-radius: 2px; }
.meter > span.ok { background: var(--ok-fg); }

/* Backend chips: one neutral shape, told apart by a coloured dot - the same
   grammar the status pills use. */
.tchip {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 2px 8px; border: 1px solid var(--border); border-radius: 999px;
  background: var(--surface-2); color: var(--muted);
  font-size: 11px; font-weight: 500; white-space: nowrap;
}
.tchip > i { width: 6px; height: 6px; border-radius: 50%; background: var(--muted); }
.tchip.wireguard > i { background: var(--accent); }
.tchip.openvpn > i { background: var(--warn-fg); }
.tchip.socks5 > i { background: var(--down-fg); }

/* ---------------------------------------------------- dashboard layout */
.grid.dash { grid-template-columns: minmax(0, 2fr) minmax(260px, 1fr); align-items: start; }
.rail { display: grid; gap: 14px; }
.card.panel { padding: 0; margin-bottom: 0; overflow: hidden; }
.panel-head {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 13px 16px; border-bottom: 1px solid var(--rule);
}
.panel-head h3 { margin: 0; }

/* ------------------------------------------------------- server list */
.srv-list { list-style: none; margin: 0; padding: 0; }
.srv {
  display: flex; align-items: center; gap: 14px;
  padding: 13px 16px; border-bottom: 1px solid var(--rule);
}
.srv:last-child { border-bottom: none; }
.srv:hover { background: var(--surface-2); }
.srv-state { display: inline-flex; }
.dot { width: 8px; height: 8px; border-radius: 50%; background: var(--down-fg); }
.dot.ok { background: var(--ok-fg); box-shadow: 0 0 0 3px var(--ok-bg); }
.dot.down { background: var(--down-fg); box-shadow: 0 0 0 3px var(--down-bg); }
.srv-main { flex: 1; min-width: 0; display: flex; align-items: center; flex-wrap: wrap; gap: 7px; }
.srv-name { font-weight: 500; color: var(--text); }
.srv-name:hover { color: var(--accent-text); }
.srv-facts { flex-basis: 100%; }
.srv-metric { text-align: right; min-width: 74px; display: flex; flex-direction: column; gap: 1px; }
.srv-metric b { font-weight: 500; font-size: 14px; }
.faint-label {
  color: var(--faint); font-size: 10px; letter-spacing: 0.6px; text-transform: uppercase;
}

/* ----------------------------------------------------- quick actions */
.qa { list-style: none; margin: 0; padding: 0; }
.qa li { border-bottom: 1px solid var(--rule); }
.qa li:last-child { border-bottom: none; }
.qa a {
  display: flex; align-items: center; gap: 11px; padding: 11px 16px; color: var(--text);
}
.qa a:hover { background: var(--surface-2); text-decoration: none; }
.qa a > span:last-child { display: flex; flex-direction: column; line-height: 1.35; }
.qa b { font-weight: 500; font-size: 13.5px; }
.qa-icon {
  flex: none; width: 26px; height: 26px; border-radius: var(--radius-sm);
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--accent-bg); color: var(--accent); font-size: 14px; line-height: 1;
}
/* Log out is a POST, but it has to keep looking like the nav link it
   sits beside rather than sprouting a button. */
.logout-form { display: inline-flex; margin: 0; }
.linkish {
  background: none; border: 0; padding: 0; margin: 0; cursor: pointer;
  font: inherit; color: inherit;
}
.linkish:hover { color: var(--text); }

/* Audit log: the action reads as a phrase now, so it needs room to stay
   on one line while the Detail column keeps the rest of the width. */
.log-action { white-space: nowrap; width: 1%; }

/* ======================================================== app shell ===
 * A fixed left sidebar, following PUK Booking: 256px, card surface, its own
 * scroll, logo at the top and the account at the bottom. Below 1024px it goes
 * off-canvas behind a top bar, because 256px of a phone screen is most of it.
 */
.shell { display: flex; min-height: 100vh; }
.shell.no-nav { display: block; }

.sidebar {
  width: 256px; flex: none; position: sticky; top: 0; height: 100vh;
  display: flex; flex-direction: column;
  background: var(--surface); border-right: 1px solid var(--border);
}
.sidebar-head {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 8px; padding: 22px 16px; min-height: 100px;
  border-bottom: 1px solid var(--border);
}
.sidebar-name { font-weight: 600; font-size: 15px; letter-spacing: -0.01em; }
.sidebar-head .logo { color: var(--accent-text); display: inline-flex; }
/* height, not just max-height: .brand-logo sets an explicit 20px for the
   old top bar, and a ceiling does not override a height. The sidebar is
   256px wide, so there is room for a logo you can actually read. */
.sidebar-head .brand-logo {
  height: 44px; max-height: 44px; max-width: 190px; width: auto;
}

/* Its own scroll, so a long list never pushes the account out of reach. */
.sidebar-nav { flex: 1; min-height: 0; overflow-y: auto; padding: 14px; }
.nav-item {
  display: flex; align-items: center; gap: 12px;
  padding: 11px 14px; border-radius: var(--radius); margin-bottom: 4px;
  color: var(--muted); font-size: 14px; font-weight: 500;
  transition: background-color .15s ease, color .15s ease;
}
.nav-item:hover { background: var(--surface-2); color: var(--text); text-decoration: none; }
.nav-item.active { background: var(--accent-bg); color: var(--accent-text); }
.nav-icon { flex: none; }

.sidebar-foot {
  padding: 14px; border-top: 1px solid var(--border);
  display: flex; flex-direction: column; gap: 10px;
}
/* The account button: full width, and the same shape as a nav item so the
   footer reads as part of the same column. .menu-btn is otherwise a 28px
   square for the row actions, hence the overrides. */
.account-menu { display: block; }
.account-btn {
  display: flex; align-items: center; gap: 10px; width: 100%; height: auto;
  padding: 8px 10px; text-align: left; font-size: inherit;
  border-radius: var(--radius); border-color: transparent;
}
.account-chev { flex: none; color: var(--faint); transition: transform .15s ease; }
.menu.open .account-chev { transform: rotate(180deg); }
.account-menu .menu-list { left: 0; right: 0; min-width: 0; }
.account-menu .menu-item.active { background: var(--accent-bg); color: var(--accent-text); }
.avatar {
  flex: none; width: 34px; height: 34px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--surface-2); color: var(--text);
  font-size: 13px; font-weight: 600;
}
.account-who { flex: 1; min-width: 0; display: flex; flex-direction: column; line-height: 1.25; }
.account-who b { font-weight: 500; font-size: 13.5px; }
.account-who span { text-transform: capitalize; }
.sidebar-foot .logout-form { display: block; }
.sidebar-foot .logout-btn { width: 100%; }
.sidebar-foot .tenant-switch select { width: 100%; }

.content { flex: 1; min-width: 0; display: flex; flex-direction: column; }
/* width:100% matters here. .content is a column flex container, so the
   container is a flex item, and `margin: 0 auto` on a flex item's cross axis
   makes it shrink to its content and centre instead of stretching. Every page
   was therefore as wide as its widest row - the dashboard nearly full, the
   servers page barely half - despite all of them sharing one max-width. */
.content > .container { flex: 1; width: 100%; }

/* The top bar and backdrop exist only on narrow screens. */
.mobile-bar, .sidebar-backdrop, .hamburger { display: none; }

@media (max-width: 1023px) {
  .mobile-bar {
    display: flex; align-items: center; justify-content: space-between;
    position: fixed; inset: 0 0 auto 0; height: 60px; padding: 0 14px; z-index: 50;
    background: var(--surface); border-bottom: 1px solid var(--border);
  }
  .hamburger {
    display: inline-flex; align-items: center; justify-content: center;
    width: 38px; height: 38px; padding: 0; cursor: pointer;
    background: none; border: 1px solid var(--border);
    border-radius: var(--radius-sm); color: var(--muted);
  }
  .hamburger:hover { color: var(--text); }
  .sidebar {
    position: fixed; inset: 0 auto 0 0; z-index: 60;
    transform: translateX(-100%); transition: transform .2s ease;
  }
  .sidebar.open { transform: translateX(0); }
  .sidebar-backdrop[hidden] { display: none; }
  .sidebar-backdrop {
    display: block; position: fixed; inset: 0; z-index: 55;
    background: var(--backdrop); backdrop-filter: blur(2px);
  }
  .content { padding-top: 60px; }
}

/* Brand colour picker: the native swatch, sized to the form grid. */
.colour-row { display: flex; align-items: center; gap: 10px; margin-bottom: 4px; }
.colour-row input[type="color"] {
  width: 48px; height: 34px; padding: 2px; cursor: pointer;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--surface-2);
}

/* An empty panel still has to line up with its own heading: .card.panel is
   padding:0, so a bare <p> would sit flush against the border. */
.panel-empty { margin: 0; padding: 16px; color: var(--muted); font-size: 13.5px; }

/* ------------------------------------------------------------- health
 * Rows in a padding:0 panel, so each supplies its own 16px gutter - the same
 * one .panel-head, .srv and .qa use, or the heading and the rows would not
 * line up. This was a table with class="kv health"; table.kv td is (0,1,2)
 * and .health td is (0,1,1), so every padding rule here lost silently and
 * the rows sat flush against both borders with the values touching the edge.
 * A flex row cannot be defeated that way, and a description list is what a
 * column of label/value pairs actually is.
 */
.health { margin: 0; padding: 0; }
.health > div {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 11px 16px; border-bottom: 1px solid var(--rule);
}
.health > div:last-child { border-bottom: none; }
.health dt { color: var(--muted); font-size: 12.5px; }
/* The value is the part that can grow, so it is the part that wraps. */
.health dd { margin: 0; text-align: right; min-width: 0; font-size: 13px; }
.health-plain { color: var(--text); font-size: 13px; }

@media (max-width: 980px) {
  .grid.dash { grid-template-columns: 1fr; }
  .grid.stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 560px) {
  .grid.stats { grid-template-columns: 1fr; }
  .srv { flex-wrap: wrap; }
}

/* Which tenant a server is bound to. */
.pill.tenant { color: var(--accent); background: var(--accent-bg); }
.bind-bar {
  display: flex; align-items: center; flex-wrap: wrap; gap: 10px;
  padding: 10px 12px; margin-bottom: 14px;
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
.bind-label {
  font-size: 10.5px; font-weight: 500; letter-spacing: 0.7px; text-transform: uppercase;
  color: var(--faint);
}
.bind-bar select { width: auto; min-width: 150px; }
.bind-bar form { display: flex; align-items: center; gap: 8px; }

/* ============================================ tenant service assignment ===
 * Which VPN services a tenant may use. Grouped by backend so "give them
 * WireGuard and the proxy" is one pass, and each row says who holds it now -
 * ticking a service takes it from that tenant rather than copying it.
 */
.svc-pick { display: grid; gap: 12px; margin: 4px 0 14px; }
.svc-group { border: none; margin: 0; padding: 0; }
.svc-group legend { padding: 0 0 6px; }
.svc-group > label { margin: 0; }

.svc {
  display: flex; align-items: flex-start; gap: 9px;
  padding: 9px 11px; margin-bottom: 6px;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--surface); cursor: pointer;
}
.svc:hover { border-color: var(--accent); }
.svc:last-child { margin-bottom: 0; }
.svc input[type="checkbox"] { width: auto; margin: 3px 0 0; flex: none; cursor: pointer; }
.svc-body { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.svc-body b { font-weight: 500; font-size: 13.5px; }
.svc-owner {
  font-size: 11px; color: var(--warn-fg); margin-top: 2px;
}
.svc-owner.free { color: var(--muted); }
/* Held by someone else: legible, but clearly not yours yet. */
.svc.is-taken { background: var(--surface-2); }

.svc-row > td { padding: 4px 16px 16px; background: var(--surface-2); }
.svc-row h4 { margin-top: 12px; }
.svc-cell { max-width: 260px; }
.svc-cell .tchip { margin: 1px 3px 1px 0; }

@media (min-width: 720px) {
  .svc-pick { grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); align-items: start; }
}

/* The nav gained Tenants and the tenant switcher, so it runs out of room
   sooner. Tighten it before it wraps onto a second line - a wrapped top bar
   reads as broken rather than responsive. */
@media (max-width: 1250px) {
  .topbar { padding: 0 16px; gap: 10px; }
  .topbar nav a { padding: 6px 8px; font-size: 13px; }
  .topbar nav a.muted { margin-left: 2px; }
  .role-suffix { display: none; }
  .tenant-switch { margin-left: 6px; }
  .tenant-switch select { max-width: 130px; }
}
@media (max-width: 1080px) {
  .topbar nav a { padding: 5px 6px; font-size: 12.5px; }
}

/* ============================================ tenant technology grants ===
 * A tenant is entitled to backends, not to particular servers. Ticking one
 * lets the tenant run any number of servers of that kind.
 */
.tech-pick { display: grid; gap: 8px; margin: 4px 0 14px; }
@media (min-width: 620px) {
  .tech-pick { grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); }
}
.tech {
  display: flex; align-items: center; gap: 10px; margin: 0;
  padding: 11px 13px; cursor: pointer;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--surface);
}
.tech:hover { border-color: var(--accent); }
.tech.is-on { border-color: var(--accent); background: var(--accent-bg); }
.tech input[type="checkbox"] { width: auto; margin: 0; flex: none; cursor: pointer; }
.tech-body { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.tech-note { font-size: 11px; color: var(--warn-fg); }
.tech-note.muted { color: var(--faint); }

/* Inline editor revealed from a row's action menu. Two small forms side by
   side - identity and credentials - rather than a modal, so the row it
   belongs to stays visible above it. */
.admin-edit-row > td { padding: 12px 16px 14px; background: var(--surface-2); }
.edit-pair { display: grid; gap: 18px; }
@media (min-width: 780px) { .edit-pair { grid-template-columns: 3fr 2fr; } }
.edit-block h4 { margin-top: 0; }
.edit-block .peer-form input { min-width: 130px; }
.add-row { margin: 0 0 14px; }

/* Branding: the uploaded logo in the header, sign-in card and Settings. */
.brand-logo { height: 20px; width: auto; max-width: 90px; display: block; object-fit: contain; }
.brand.big .brand-logo {
  /* Bounded on both axes so a tall, wide or square logo all sit well. */
  height: auto; max-height: 64px; max-width: 220px; margin: 0 auto;
}
/* The fallback glyph needs the same presence as an uploaded logo. */
.brand.big .logo { font-size: 34px; line-height: 1; }
.logo-row { display: flex; align-items: center; gap: 12px; margin-top: 4px; }
.logo-preview {
  flex: none; width: 56px; height: 56px; display: inline-flex;
  align-items: center; justify-content: center;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--surface-2); overflow: hidden;
}
.logo-preview img { max-width: 100%; max-height: 100%; object-fit: contain; }
.logo-row input[type="file"] { font-size: 12.5px; }

/* ============================================== utilization chart ======
 * Inline SVG so the page needs no external requests. Two series sharing one
 * axis: download filled under the line, upload drawn over it.
 */
.usage-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 10px; margin-bottom: 12px;
}
.usage-head h3 { margin: 0; }
.usage-controls {
  display: flex; align-items: center; flex-wrap: wrap; gap: 10px; margin-bottom: 14px;
}
.seg {
  display: inline-flex; border: 1px solid var(--border);
  border-radius: var(--radius-sm); overflow: hidden;
}
.seg-btn {
  padding: 6px 12px; border: 0; background: var(--surface);
  color: var(--muted); font: inherit; font-size: 12.5px; cursor: pointer;
  border-right: 1px solid var(--border);
}
.seg-btn:last-child { border-right: 0; }
.seg-btn:hover { background: var(--surface-2); color: var(--text); }
.seg-btn.is-on { background: var(--accent); color: var(--on-accent); font-weight: 500; }
.usage-custom { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.usage-custom label { margin: 0; font-size: 12px; }
.usage-custom input[type="datetime-local"],
.usage-custom input[type="date"] { width: auto; padding: 5px 8px; font-size: 12.5px; }

.usage-chart-wrap { position: relative; }
.usage-chart { width: 100%; height: 260px; display: block; }
.usage-empty {
  position: absolute; inset: 0; display: flex; align-items: center;
  justify-content: center; margin: 0; pointer-events: none;
}
.usage-chart .grid { stroke: var(--rule); stroke-width: 1; }
.usage-chart .axis { fill: var(--faint); font-size: 11px; font-family: inherit; }
.usage-chart .line { fill: none; stroke-width: 2; stroke-linejoin: round; }
.usage-chart .line.rx { stroke: var(--accent); }
.usage-chart .line.tx { stroke: var(--ok-fg); }
.usage-chart .fill { stroke: none; }
.usage-chart .fill.rx { fill: var(--accent); opacity: 0.14; }
.usage-chart .fill.tx { fill: var(--ok-fg); opacity: 0.12; }

.usage-legend {
  display: flex; align-items: center; flex-wrap: wrap; gap: 18px;
  margin-top: 10px; font-size: 12.5px; color: var(--muted);
}
.usage-legend b { color: var(--text); font-weight: 500; margin-left: 4px; }
.swatch { width: 10px; height: 10px; border-radius: 2px; display: inline-block; margin-right: 5px; }
.swatch.dl { background: var(--accent); }
.swatch.ul { background: var(--ok-fg); }

/* Realtime readout: only visible while the live view is running. */
.usage-live {
  display: flex; align-items: center; flex-wrap: wrap; gap: 18px;
  margin-top: 10px; padding: 10px 12px;
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
.usage-live .live-rate { display: flex; align-items: baseline; gap: 6px; }
.usage-live .live-rate b {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-variant-numeric: tabular-nums; font-size: 16px; font-weight: 500;
}
.live-dot {
  width: 8px; height: 8px; border-radius: 50%; background: var(--ok-fg);
  box-shadow: 0 0 0 3px var(--ok-bg);
  animation: live-pulse 1.6s ease-in-out infinite;
}
@keyframes live-pulse { 50% { opacity: 0.35; } }
@media (prefers-reduced-motion: reduce) { .live-dot { animation: none; } }
.seg-btn.seg-live.is-on { background: var(--ok-fg); }
