/* ============================================================
   Synet design system — SIGNAL EDITION
   Warm editorial canvas, Forest-ink dark, 1px rules over shadows,
   square corners, monospace labels. Both themes first-class.
   (Phase 1: tokens + component overrides; class names unchanged.)
   ============================================================ */

:root {
  /* === Signal palette (raw) === */
  --sg-canvas:   #f3f0e8;
  --sg-paper:    #fcfaf5;
  --sg-ink:      #18231f;
  --sg-muted:    #536059;
  --sg-line:     #b9c0b4;
  --sg-accent:   #284fe4;
  --sg-on:       #ffffff;
  --sg-soft:     #e1e7ff;
  --sg-code:     #172421;
  --sg-codeink:  #e0ecd8;
  --sg-deep:     #efeade;

  /* === Legacy zinc scale — remapped onto Signal (kept for compatibility) === */
  --z50:  #fcfaf5;
  --z100: #f7f4ec;
  --z200: #efeade;
  --z300: #d9dbcf;
  --z400: #b9c0b4;
  --z500: #8a938a;
  --z600: #536059;
  --z700: #3b4741;
  --z800: #26302b;
  --z900: #18231f;
  --z950: #0b0c0d;

  /* === Surfaces — warm light mode default === */
  --bg-0:      var(--sg-paper);
  --bg-1:      var(--sg-canvas);
  --bg-2:      var(--sg-deep);
  --surface:   var(--sg-paper);
  --surface-2: #f7f4ec;
  --surface-3: var(--sg-deep);

  /* === Lines === */
  --border:       var(--sg-line);
  --border-hi:    rgba(24, 35, 31, 0.34);
  --border-faint: rgba(185, 192, 180, 0.5);
  --rule:         1px solid var(--sg-line);

  /* === Text === */
  --text-0: var(--sg-ink);
  --text-1: #3b4741;
  --text-2: var(--sg-muted);
  --text-3: #7a857d;

  /* === Accent — signal blue in light === */
  --accent:      var(--sg-accent);
  --on-accent:   var(--sg-on);
  /* Requested by admin.js:33 sparkline() as `var(--accent-alpha, rgba(99,102,241,0.12))`.
     The token was defined NOWHERE in the product, so that Tailwind-indigo fallback was
     what actually painted every sparkline's area fill. Defining it here retires the
     foreign colour without touching admin.js. */
  --accent-alpha: color-mix(in srgb, var(--sg-accent) 12%, transparent);
  /* The design has NO glows. Keep these whisper-subtle so any legacy
     decoration that references them stays visually flat. */
  --accent-glow: rgba(40, 79, 228, 0.06);
  --accent-soft: rgba(40, 79, 228, 0.05);

  /* === Semantic === */
  --good:        #2f6b3f;
  --good-dim:    rgba(47, 107, 63, 0.08);
  --good-line:   rgba(47, 107, 63, 0.3);
  --warn:        #8a6a1f;
  --warn-dim:    rgba(138, 106, 31, 0.08);
  --warn-line:   rgba(138, 106, 31, 0.3);
  --bad:         #a4342a;
  --bad-dim:     rgba(164, 52, 42, 0.08);
  --bad-line:    rgba(164, 52, 42, 0.3);

  /* === Rules replace shadows (Signal) === */
  --shadow-sm: none;
  --shadow-md: none;
  --shadow-lg: none;

  /* === Square corners (Signal) === */
  --r-sm: 0;
  --r-md: 2px;
  --r-lg: 2px;
  --r-xl: 2px;
  --r-full: 999px;

  /* === Type — system only, no webfonts === */
  --font-sans: Arial, Helvetica, 'Helvetica Neue', sans-serif;
  --font-mono: 'Courier New', Courier, monospace;
  --label-caps: 11px/1.6 var(--font-mono);
  --track-h1: -0.065em;
  --track-h2: -0.05em;
  --track-h3: -0.025em;

  /* === Fluid type scale === */
  --text-xs:    11px;
  --text-sm:    13px;
  --text-base: 15px;
  --text-lg:    16px;
  --text-xl:    18px;
  --text-2xl:   22px;
  --text-3xl:   28px;
  --text-display: clamp(34px, 4.6vw, 64px);

  /* === Spacing scale (8px grid) === */
  --s-1:  4px;
  --s-2:  8px;
  --s-3:  12px;
  --s-4:  16px;
  --s-5:  20px;
  --s-6:  24px;
  --s-8:  32px;
  --s-12: 48px;

  /* === Sidebar === */
  --sidebar-w:           240px;
  --sidebar-w-collapsed: 56px;

  /* === Transitions === */
  --t-fast:  0.15s ease;
  --t-base:  0.2s ease;
  --t-slow:  0.3s cubic-bezier(0.4, 0, 0.2, 1);
}

/* === Dark theme — Forest ink / pale celery (Signal, not a black inversion) === */
[data-theme="dark"] {
  --sg-canvas:   #0b0c0d;
  --sg-paper:    #121314;
  --sg-ink:      #e8eaed;
  --sg-muted:    #adb2b8;
  --sg-line:     #3a3f45;
  --sg-accent:   #c5e986;
  --sg-on:       #0f1113;
  --sg-soft:     #1f2a1c;
  --sg-code:     #060708;
  --sg-codeink:  #dfecd8;
  --sg-deep:     #08090a;

  --bg-0:      var(--sg-paper);
  --bg-1:      var(--sg-canvas);
  --bg-2:      var(--sg-deep);
  --surface:   var(--sg-paper);
  --surface-2: #1a1c1e;
  --surface-3: #212426;

  --border:       var(--sg-line);
  --border-hi:    rgba(232, 234, 237, 0.34);
  --border-faint: rgba(58, 63, 69, 0.5);

  --text-0: var(--sg-ink);
  --text-1: #c3c7cc;
  --text-2: var(--sg-muted);
  --text-3: #83888f;

  --accent:      var(--sg-accent);
  --on-accent:   var(--sg-on);
  --accent-glow: rgba(197, 233, 134, 0.06);
  --accent-soft: rgba(197, 233, 134, 0.05);
  --accent-alpha: color-mix(in srgb, var(--sg-accent) 12%, transparent);

  --good:        #8fce7a;
  --good-dim:    rgba(143, 206, 122, 0.12);
  --good-line:   rgba(143, 206, 122, 0.3);
  --warn:        #e3c368;
  --warn-dim:    rgba(227, 195, 104, 0.12);
  --warn-line:   rgba(227, 195, 104, 0.3);
  --bad:         #f0836f;
  --bad-dim:     rgba(240, 131, 111, 0.12);
  --bad-line:    rgba(240, 131, 111, 0.3);

  --shadow-sm: none;
  --shadow-md: none;
  --shadow-lg: none;

  /* legacy scale in dark terms */
  --z50:  #e8eaed;
  --z100: #dfe1e5;
  --z200: #c3c7cc;
  --z300: #adb2b8;
  --z400: #8b9096;
  --z500: #6f7378;
  --z600: #5c6166;
  --z700: #3a3f45;
  --z800: #2a2e33;
  --z900: #121314;
  --z950: #0b0c0d;
}

/* === Reset === */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
body {
  background: var(--bg-0);
  font-family: var(--font-sans);
  color: var(--text-0);
  font-size: var(--text-base);
  line-height: 1.55;
  letter-spacing: -0.005em;
  font-feature-settings: 'cv02', 'cv03', 'cv04', 'cv11', 'ss01';
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
a { color: inherit; text-decoration: none; }
button, input, textarea, select { font: inherit; color: inherit; }
button { cursor: pointer; border: 0; background: none; }
code, pre, .mono {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}

/* === Scrollbar === */
::-webkit-scrollbar { width: 8px; height: 8px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--border); border-radius: 4px; }
::-webkit-scrollbar-thumb:hover { background: var(--border-hi); }

/* === Selection === */
::selection { background: var(--accent-glow); color: var(--text-0); }

/* === App shell — sidebar + main === */
.shell { display: flex; min-height: 100vh; }

/* === Sidebar — fixed left rail === */
aside.sidebar {
  width: var(--sidebar-w);
  flex-shrink: 0;
  border-right: 1px solid var(--border);
  background: var(--surface);
  display: flex;
  flex-direction: column;
  position: sticky;
  top: 0;
  height: 100vh;
  overflow-y: auto;
  transition: width var(--t-base);
  z-index: 200;
}
aside.sidebar.collapsed { width: var(--sidebar-w-collapsed); }
aside.sidebar.collapsed .label-text,
aside.sidebar.collapsed .sidebar-section-title { display: none; }
aside.sidebar.collapsed .sidebar-nav-link { justify-content: center; padding: 10px; }
aside.sidebar.collapsed .sidebar-foot .account-id { display: none; }
aside.sidebar.collapsed .sidebar-foot .signout { font-size: 0; padding: 8px; justify-content: center; }
aside.sidebar.collapsed .sidebar-foot .signout::before { content: "\2190"; font-size: 14px; }
aside.sidebar.collapsed .sidebar-tab { justify-content: center; padding: 10px; }
/* When collapsed, show expand hamburger beside the rail (not over the logo) */
.expand-hamburger {
  display: none;
  position: fixed;
  top: 12px;
  left: calc(var(--sidebar-w-collapsed) + 12px);
  z-index: 210;
  width: 32px;
  height: 32px;
  grid: place-items: center;
  border-radius: var(--r-sm);
  background: var(--surface);
  border: 1px solid var(--border);
  color: var(--text-1);
  cursor: pointer;
  transition: left var(--t-base);
}
.expand-hamburger:hover { background: var(--surface-2); }
.expand-hamburger svg { width: 16px; height: 16px; }
aside.sidebar.collapsed ~ .expand-hamburger { display: grid; }
/* Hide expand hamburger on mobile */
@media (max-width: 768px) {
  .expand-hamburger { display: none !important; }
}

/* Sidebar head */
.sidebar-head {
  padding: var(--s-4);
  display: flex;
  align-items: center;
  justify-content: space-between;
  border-bottom: 1px solid var(--border-faint);
  flex-shrink: 0;
}
.sidebar-head .brand {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  font-size: 15px;
  font-weight: 700;
  color: var(--text-0);
  letter-spacing: -0.02em;
}
/* Brand mark: synet-mark-bold.png is a 589x916 tall crop of the real mark, drawn
   white-on-transparent. Size by HEIGHT and let the width follow the aspect ratio
   (0.643) -- a square box would letterbox it and shrink the mark by ~36%. */
.sidebar-head .brand-mark,
.sidebar-head img.brand-mark {
  height: 26px;
  width: auto;
  flex-shrink: 0;
  object-fit: contain;
  display: block;
}
.sidebar-head .brand-mark img {
  height: 100%;
  width: auto;
  object-fit: contain;
  display: block;
}
/* Mark is white-on-transparent: invert to black in light mode */
:root:not([data-theme="dark"]) img.brand-mark,
:root:not([data-theme="dark"]) .brand-mark img { filter: invert(1); }
.sidebar-head .brand-text { white-space: nowrap; }
.sidebar-toggle {
  width: 28px;
  height: 28px;
  display: grid;
  place-items: center;
  border-radius: var(--r-sm);
  color: var(--text-2);
  transition: all var(--t-fast);
}
.sidebar-toggle:hover { background: var(--surface-2); color: var(--text-0); }

/* Sidebar nav */
.sidebar-nav {
  flex: 1;
  padding: var(--s-2);
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.sidebar-nav-link {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  padding: 8px var(--s-3);
  border-radius: var(--r-sm);
  font-size: 13px;
  font-weight: 500;
  color: var(--text-2);
  transition: all var(--t-fast);
  white-space: nowrap;
}
.sidebar-nav-link:hover { background: var(--surface-2); color: var(--text-0); }
.sidebar-nav-link.active { background: var(--surface-3); color: var(--text-0); font-weight: 600; }
.sidebar-nav-link .ico { width: 16px; height: 16px; flex-shrink: 0; opacity: 0.7; }
.sidebar-nav-link.active .ico { opacity: 1; }
.sidebar-nav-link[hidden] { display: none; }

.sidebar-section-title {
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-3);
  padding: var(--s-4) var(--s-3) var(--s-1);
  white-space: nowrap;
}

.sidebar-tab {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  padding: 8px var(--s-3);
  border-radius: var(--r-sm);
  font-size: 13px;
  font-weight: 500;
  color: var(--text-2);
  cursor: pointer;
  transition: all var(--t-fast);
  white-space: nowrap;
}
.sidebar-tab:hover { background: var(--surface-2); color: var(--text-0); }
.sidebar-tab.active { background: var(--surface-3); color: var(--text-0); }
.sidebar-tab .ico { width: 16px; height: 16px; flex-shrink: 0; opacity: 0.7; }
.sidebar-tab.active .ico { opacity: 1; }

.theme-toggle {
  width: 28px;
  height: 28px;
  display: grid;
  place-items: center;
  border-radius: var(--r-sm);
  color: var(--text-2);
  transition: all var(--t-fast);
}
.theme-toggle:hover { color: var(--text-0); background: var(--surface-2); }
[data-theme="dark"] .theme-toggle svg circle { fill: var(--text-1); stroke: none; }
[data-theme="dark"] .theme-toggle svg path { opacity: 0; }

.sidebar-foot {
  padding: var(--s-3);
  border-top: 1px solid var(--border-faint);
  flex-shrink: 0;
}
.sidebar-foot .account-id {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-3);
  margin-bottom: var(--s-2);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.sidebar-foot .signout {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  width: 100%;
  padding: 8px var(--s-3);
  border-radius: var(--r-sm);
  font-size: 13px;
  font-weight: 500;
  color: var(--text-2);
  transition: all var(--t-fast);
}
.sidebar-foot .signout:hover { background: var(--bad-dim); color: var(--bad); }
.sidebar-foot .signout:active { transform: scale(0.98); }
.sidebar-foot .signout[hidden] { display: none !important; }

/* Main content area */
main.content {
  flex: 1;
  min-width: 0;
  background: var(--bg-0);
  padding: clamp(16px, 3vw, 32px);
  padding-bottom: calc(clamp(16px, 3vw, 32px) + env(safe-area-inset-bottom, 0px));
}

.content-wrap {
  max-width: 1100px;
  margin: 0 auto;
}

/* === Buttons === */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  padding: 8px 16px;
  border-radius: var(--r-md);
  font-size: 13px;
  font-weight: 600;
  border: 1px solid transparent;
  transition: all var(--t-fast);
  white-space: nowrap;
}
/* NOTE: `.btn.btn-primary`, NOT `.btn-primary`. Two classes are load-bearing here.
   `.btn, button.btn, .btn-sm` (below) sets `background: transparent` and `button.btn`
   is (0,1,1) — one class + one element. A bare `.btn-primary` is (0,1,0), so it loses to
   the base rule no matter how much later in the file it appears, and the accent fill
   silently never happened. The giveaway: `.btn-primary:hover` compiled to (0,2,0) and so
   DID fill — the primary button was accent-filled on hover and unfilled at rest, which on
   every page made it identical to .btn-ghost. Verified by rule dump: `.btn-primary` (0,1,0)
   then `.btn, button.btn, .btn-sm { background: transparent }` (0,1,1) then `.btn-primary`
   (0,1,0) — the base rule wins both. */
.btn.btn-primary {
  background: var(--accent);
  color: var(--bg-0);
  border-color: var(--accent);
}
.btn-primary:hover:not(:disabled) { opacity: 0.88; }
.btn-primary:disabled { opacity: 0.5; cursor: not-allowed; }
.btn-ghost {
  background: transparent;
  color: var(--text-1);
  border: 1px solid var(--border);
}
.btn-ghost:hover { background: var(--surface-2); color: var(--text-0); border-color: var(--border-hi); }
.btn-icon {
  width: 32px;
  height: 32px;
  padding: 0;
  display: grid;
  place-items: center;
  border-radius: var(--r-sm);
  color: var(--text-2);
  transition: all var(--t-fast);
}
.btn-icon:hover { background: var(--surface-2); color: var(--text-0); }

/* === Inputs === */
.input {
  width: 100%;
  padding: 9px 12px;
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  font-size: 14px;
  background: var(--surface);
  color: var(--text-0);
  transition: border-color var(--t-fast), box-shadow var(--t-fast);
}
.input::placeholder { color: var(--text-3); }
.input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-glow); outline: none; }
.input:disabled { opacity: 0.5; cursor: not-allowed; }
.input-lg { padding: 12px 14px; font-size: 15px; }

.label {
  display: block;
  font-size: 12px;
  font-weight: 600;
  color: var(--text-1);
  margin-bottom: 6px;
}

/* === Cards === */
.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  overflow: hidden;
  transition: border-color var(--t-fast);
}
.card:hover { border-color: var(--border-hi); }
.card-head {
  padding: 14px 20px;
  border-bottom: 1px solid var(--border);
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.card-head h3 { font-size: 13px; font-weight: 600; color: var(--text-1); }
.card-body { padding: 20px; }

/* === Status pills === */
.pill {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 8px;
  border-radius: var(--r-full);
  font-size: 11px;
  font-weight: 600;
  border: 1px solid var(--border);
  background: var(--surface-2);
  color: var(--text-1);
}
.pill-good { background: var(--good-dim); border-color: var(--good-line); color: var(--good); }
.pill-bad  { background: var(--bad-dim);  border-color: var(--bad-line);  color: var(--bad); }
.pill-warn { background: var(--warn-dim); border-color: var(--warn-line); color: var(--warn); }
.pill-info { background: var(--bg-2); border-color: var(--border); color: var(--text-1); }
.dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex-shrink: 0; }
.dot.pulse { animation: pulse 2s infinite; }
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.4; } }

/* === Code block === */
.code {
  position: relative;
  background: var(--bg-1);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  overflow: hidden;
}
.code pre {
  padding: 12px 14px;
  font-family: var(--font-mono);
  font-size: 12px;
  line-height: 1.6;
  color: var(--text-1);
  overflow-x: auto;
  white-space: pre;
}
.code .copy {
  position: absolute;
  top: 6px;
  right: 6px;
  padding: 4px 8px;
  border-radius: var(--r-sm);
  font-size: 11px;
  color: var(--text-2);
  background: var(--surface);
  border: 1px solid var(--border);
  cursor: pointer;
  transition: all var(--t-fast);
}
.code .copy:hover { background: var(--surface-2); }
.code .copy.copied { color: var(--good); border-color: var(--good-line); }

/* === Stat === */
.stat {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: 16px 20px;
  transition: border-color var(--t-fast);
}
.stat:hover { border-color: var(--border-hi); }
.stat .lbl { font-size: 11px; color: var(--text-2); margin-bottom: 6px; display: flex; justify-content: space-between; align-items: center; min-height: 33px; }
.stat .lbl .chip { font-size: 9px; padding: 1px 6px; border-radius: 3px; background: var(--bg-2); color: var(--text-2); border: 1px solid var(--border); text-transform: uppercase; letter-spacing: 0.04em; }
.stat .val { font-size: 24px; font-weight: 700; letter-spacing: -0.02em; margin-bottom: 2px; font-variant-numeric: tabular-nums; }
.stat .val.tbd { color: var(--text-3); font-size: 22px; font-weight: 500; }
.stat .val.mono { font-size: 14px; word-break: break-all; }
.stat .sub { font-size: 12px; color: var(--text-2); }
.stat .sub.good { color: var(--good); }
.stat .sub.bad { color: var(--bad); }

/* === K/V list === */
.kv { display: flex; flex-direction: column; gap: 10px; }
.kv .row { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; }
.kv .k { font-size: 13px; color: var(--text-2); }
.kv .v { font-size: 13px; font-weight: 500; font-family: var(--font-mono); text-align: right; word-break: break-all; }
.kv .v.good { color: var(--good); }
.kv .v.bad { color: var(--bad); }
.kv .v.muted { color: var(--text-3); }

/* === Toast === */
.toast {
  position: fixed;
  bottom: 24px;
  left: 50%;
  transform: translateX(-50%) translateY(20px);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: 10px 16px;
  font-size: 13px;
  font-weight: 500;
  color: var(--text-0);
  box-shadow: var(--shadow-lg);
  opacity: 0;
  transition: all var(--t-base);
  z-index: 9999;
  pointer-events: none;
}
.toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }

/* === Empty state === */
.empty {
  text-align: center;
  padding: 48px 20px;
}
.empty .empty-title { font-size: 15px; color: var(--text-1); margin-bottom: 4px; }

/* === Helpers === */
.muted { color: var(--text-2); }
.muted-2 { color: var(--text-3); }
.mono { font-family: var(--font-mono); }
.text-grad { color: var(--text-0); }
.h1 { font-size: clamp(28px, 4vw, 40px); font-weight: 700; letter-spacing: -0.03em; line-height: 1.1; }
.h2 { font-size: 22px; font-weight: 700; letter-spacing: -0.02em; }
.h3 { font-size: 16px; font-weight: 600; }
.row { display: flex; gap: 12px; align-items: center; }
.col { display: flex; flex-direction: column; gap: 8px; }
.gap-1 { gap: 4px; } .gap-2 { gap: 8px; } .gap-3 { gap: 12px; } .gap-4 { gap: 16px; } .gap-6 { gap: 24px; }
.mt-2 { margin-top: 8px; } .mt-3 { margin-top: 12px; } .mt-4 { margin-top: 16px; } .mt-6 { margin-top: 24px; } .mt-8 { margin-top: 32px; } .mt-12 { margin-top: 48px; }

/* === Page header === */
.page-head { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 16px; margin-bottom: 28px; }
.page-head h1 { font-size: 22px; font-weight: 700; letter-spacing: -0.02em; }
.page-head .sub { color: var(--text-2); font-size: 14px; margin-top: 4px; max-width: 600px; }

/* === Collapsible sidebar section === */
.sidebar-section { margin: 0; }
.sidebar-section-toggle {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  padding: 8px var(--s-3);
  border-radius: var(--r-sm);
  font-size: 13px;
  font-weight: 500;
  color: var(--text-2);
  cursor: pointer;
  transition: all var(--t-fast);
  white-space: nowrap;
  width: 100%;
}
.sidebar-section-toggle:hover { background: var(--surface-2); color: var(--text-0); }
.sidebar-section-toggle .ico { width: 16px; height: 16px; flex-shrink: 0; opacity: 0.7; }
.sidebar-section-toggle .chevron {
  margin-left: auto;
  width: 14px;
  height: 14px;
  opacity: 0.5;
  transition: transform var(--t-base);
}
.sidebar-section.expanded .chevron { transform: rotate(90deg); }
.sidebar-children {
  max-height: 0;
  overflow: hidden;
  transition: max-height var(--t-base);
}
.sidebar-section.expanded .sidebar-children {
  max-height: 500px;
}
.sidebar-children a {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  padding: 7px var(--s-3) 7px 28px;
  font-size: 13px;
  font-weight: 500;
  color: var(--text-2);
  border-radius: var(--r-sm);
  transition: all var(--t-fast);
  white-space: nowrap;
}
.sidebar-children a:hover { background: var(--surface-2); color: var(--text-0); }
.sidebar-children a.active { background: var(--surface-3); color: var(--text-0); }
aside.sidebar.collapsed .sidebar-section-toggle { justify-content: center; position: relative; }
aside.sidebar.collapsed .sidebar-section-toggle .label-text,
aside.sidebar.collapsed .sidebar-section-toggle .chevron { display: none; }
/* When collapsed, hovering a section toggle shows a flyout with children */
aside.sidebar.collapsed .sidebar-section:hover .sidebar-children {
  display: block !important;
  position: absolute;
  left: 100%;
  top: 0;
  min-width: 180px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: 8px;
  box-shadow: var(--shadow-lg);
  z-index: 100;
  max-height: none;
}
aside.sidebar.collapsed .sidebar-section:hover .sidebar-children a {
  padding: 7px 12px;
}

/* === Admin table sort/filter === */
th.sort-asc::after { content: ' \25B2'; font-size: 9px; color: var(--accent); }
th.sort-desc::after { content: ' \25BC'; font-size: 9px; color: var(--accent); }
th[data-sort-key] { position: relative; }
th[data-sort-key]:hover { color: var(--text-0); }

.table-filter {
  width: 100%;
  padding: 8px 12px 8px 32px;
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  font-size: 13px;
  background: var(--surface) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%2371717a' stroke-width='2'%3E%3Ccircle cx='11' cy='11' r='8'/%3E%3Cpath d='m21 21-4.35-4.35'/%3E%3C/svg%3E") 10px center no-repeat;
  color: var(--text-0);
  transition: border-color var(--t-fast);
}
.table-filter:focus { border-color: var(--accent); outline: none; box-shadow: 0 0 0 3px var(--accent-glow); }
.table-filter::placeholder { color: var(--text-3); }

.refresh-indicator { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--text-3); font-family: var(--font-mono); }
.refresh-indicator .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--good); animation: pulse 2s infinite; }

.chart-container { margin: 16px 0; padding: 16px; background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-lg); }
.chart-container .chart-title { font-size: 13px; font-weight: 600; color: var(--text-1); margin-bottom: 8px; }

/* === Mobile: sidebar becomes overlay drawer === */
@media (max-width: 768px) {
  /* Shell is NOT flex on mobile - sidebar is pure overlay */
  .shell { display: block; }

  aside.sidebar {
    position: fixed;
    top: 0;
    left: 0;
    bottom: 0;
    width: var(--sidebar-w);
    transform: translateX(-100%);
    transition: transform var(--t-slow);
    z-index: 200;
    box-shadow: var(--shadow-lg);
  }
  aside.sidebar.mobile-open { transform: translateX(0); }
  aside.sidebar.collapsed { width: var(--sidebar-w); }
  aside.sidebar.collapsed .label-text,
  aside.sidebar.collapsed .sidebar-section-title { display: block; }
  aside.sidebar.collapsed .sidebar-nav-link { justify-content: flex-start; padding: 8px var(--s-3); }


  .mobile-top-bar {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 12px 16px;
    border-bottom: 1px solid var(--border);
    position: sticky;
    top: 0;
    background: var(--bg-0);
    z-index: 100;
  }
  .mobile-top-bar .hamburger {
    width: 32px;
    height: 32px;
    display: grid;
    place-items: center;
    border-radius: var(--r-sm);
    color: var(--text-1);
  }
  .mobile-top-bar .hamburger:hover { background: var(--surface-2); }
  .mobile-top-bar .brand-mini { font-size: 15px; font-weight: 700; letter-spacing: -0.02em; }
  .mobile-top-bar .spacer { flex: 1; }
  .mobile-top-bar .theme-btn-mobile {
    width: 32px; height: 32px; border-radius: var(--r-sm);
    border: 1px solid var(--border); background: var(--surface);
    display: grid; place-items: center; cursor: pointer; color: var(--text-1);
  }
  .mobile-top-bar .theme-btn-mobile svg { width: 14px; height: 14px; }

  main.content { padding: 16px; padding-bottom: calc(16px + env(safe-area-inset-bottom, 0px)); }

  .data-table thead { display: none; }
  .data-table tr { display: block; border: 1px solid var(--border); border-radius: var(--r-lg); margin-bottom: 12px; padding: 8px 0; }
  .data-table td { display: flex; justify-content: space-between; gap: 12px; padding: 8px 16px; border-bottom: 1px solid var(--border-faint); }
  .data-table td:last-child { border-bottom: 0; }
  .data-table td::before { content: attr(data-label); font-size: 11px; color: var(--text-3); text-transform: uppercase; letter-spacing: 0.05em; font-weight: 600; }
}

/* Desktop: hide mobile top bar */
.mobile-top-bar { display: none; }
@media (max-width: 768px) {
  .mobile-top-bar { display: flex; }
}
/* Overlay: hidden by default, shown when .show is added (works both desktop and mobile) */
.sidebar-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.4);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--t-base);
  z-index: 150;
}
.sidebar-overlay.show { opacity: 1; pointer-events: auto; }

/* === Animation utility === */
.stagger-card { opacity: 1; }
@media (prefers-reduced-motion: no-preference) {
  .stagger-enter {
    opacity: 0;
    transform: translateY(12px);
    animation: stagger-in 0.4s cubic-bezier(0.16, 1, 0.3, 1) forwards;
  }
  @keyframes stagger-in {
    to { opacity: 1; transform: translateY(0); }
  }
}

/* === Command palette === */
.cmdk-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.4);
  z-index: 300;
  display: none;
  align-items: flex-start;
  justify-content: center;
  padding-top: 15vh;
}
.cmdk-overlay.show { display: flex; }
.cmdk-box {
  width: 90%;
  max-width: 520px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-lg);
  overflow: hidden;
}
.cmdk-input {
  width: 100%;
  padding: 16px 20px;
  border: 0;
  border-bottom: 1px solid var(--border);
  font-size: 15px;
  background: transparent;
}
.cmdk-input:focus { outline: none; }
.cmdk-results { max-height: 320px; overflow-y: auto; padding: 8px; }
.cmdk-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 12px;
  border-radius: var(--r-sm);
  font-size: 13px;
  color: var(--text-1);
  cursor: pointer;
}
.cmdk-item:hover, .cmdk-item.selected { background: var(--surface-2); color: var(--text-0); }
.cmdk-item .ico { width: 16px; height: 16px; opacity: 0.6; }
/* ============================================================
   SIGNAL EDITION OVERRIDES (Phase 1)
   Appended: remaps existing components onto the Signal vocabulary
   without changing any class name or removing any rule above.
   Rules below win for equal specificity (later in cascade).
   ============================================================ */

/* --- Typography + page canvas: editorial hierarchy, system fonts ---
   Body sits on the Signal CANVAS (--bg-1), not paper. The design's screenshots
   are ~67% canvas by pixel area; paper is reserved for raised surfaces. */
body { letter-spacing: -0.005em; background: var(--bg-1); }
h1, .h1 {
  font-size: var(--text-display);
  line-height: 0.98;
  letter-spacing: var(--track-h1);
  font-weight: 500;
}
h2, .h2 { font-size: clamp(26px, 3vw, 40px); line-height: 1.03; letter-spacing: var(--track-h2); font-weight: 500; }
h3, .h3 { font-size: 21px; line-height: 1.15; letter-spacing: var(--track-h3); font-weight: 600; }
.page-head h1, .page-head .h1 { font-size: clamp(28px, 3.2vw, 44px); }
.muted, .muted-2 { color: var(--text-2); }
.mono, code, pre, kbd { font-family: var(--font-mono); }
.label, .stat .lbl, .sidebar-section-title, .section-title, .section-head, .kv .k {
  font-family: var(--font-mono);
  text-transform: uppercase;
  letter-spacing: 1.3px;
  font-size: var(--text-xs);
  color: var(--text-2);
}
.text-grad { color: var(--accent); background: none; -webkit-text-fill-color: currentColor; }

/* --- Rules over shadows --- */
.card, .stat, .setting-card, .stat-card, .cmdk, .modal {
  box-shadow: none;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--bg-0);
}
.card-head { border-bottom: 1px solid var(--border); padding-bottom: 12px; }
.card:hover, .stat:hover { border-color: var(--border-hi); box-shadow: none; }
.card-body { padding-top: 4px; }
hr, .sg-rule { border: 0; border-top: 1px solid var(--border); }

/* --- Buttons: square, accent-filled primary, ruled secondary --- */
.btn, button.btn, .btn-sm {
  border-radius: var(--r-sm);
  border: 1px solid var(--border);
  background: transparent;
  color: var(--text-0);
  box-shadow: none;
  padding: 9px 16px;
  min-height: 40px;
  font-size: var(--text-sm);
  letter-spacing: -0.01em;
}
.btn:hover { border-color: var(--text-0); }
.btn.btn-primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-accent);
}
.btn-primary:hover { background: var(--accent); border-color: var(--text-0); color: var(--on-accent); }
.btn-ghost { border-color: transparent; color: var(--text-1); }
.btn-ghost:hover { border-color: var(--border); color: var(--text-0); }
.btn-icon { border: 1px solid var(--border); border-radius: var(--r-sm); background: transparent; }
.btn-icon:hover { border-color: var(--text-0); }

/* --- Inputs: ruled, square, accent focus --- */
.input, .input-lg, input[type="text"], input[type="email"], input[type="password"],
input[type="number"], input[type="search"], select, textarea {
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  background: var(--bg-0);
  color: var(--text-0);
  box-shadow: none;
}
.input:focus, .input-lg:focus, input:focus, select:focus, textarea:focus,
.input:focus-visible, select:focus-visible, textarea:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
  border-color: var(--border-hi);
}
button:focus-visible, a:focus-visible, summary:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 3px;
}
::placeholder { color: var(--text-3); opacity: 1; }

/* --- Stats: ruled editorial blocks with tabular numerals --- */
.stat { padding: 16px; }
.stat .val, .stat .val.mono { font-variant-numeric: tabular-nums; letter-spacing: -0.03em; font-size: 30px; font-weight: 600; line-height: 1.05; }
.stat .val.tbd { color: var(--text-3); }
.stat .sub { color: var(--text-2); font-size: var(--text-sm); }
.stat .chip, .chip {
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  background: transparent;
  font-family: var(--font-mono);
  text-transform: uppercase;
  letter-spacing: 1px;
  font-size: 10px;
}

/* --- Pills and dots --- */
.pill {
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  background: transparent;
  font-family: var(--font-mono);
  text-transform: uppercase;
  letter-spacing: 1px;
  font-size: 10px;
  padding: 3px 8px;
}
.pill-good { color: var(--good); border-color: var(--good-line); background: var(--good-dim); }
.pill-bad  { color: var(--bad);  border-color: var(--bad-line);  background: var(--bad-dim); }
.pill-warn { color: var(--warn); border-color: var(--warn-line); background: var(--warn-dim); }
.pill-info { color: var(--accent); border-color: var(--accent); background: var(--accent-soft); }
.dot { box-shadow: none; }

/* --- KV lists: ruled rows --- */
.kv .row { border-bottom: 1px solid var(--border-faint); padding: 10px 0; }
.kv .row:last-child { border-bottom: 0; }
.kv .v.good { color: var(--good); }
.kv .v.bad { color: var(--bad); }
.kv .v.muted { color: var(--text-2); }

/* --- Code blocks: forest/ink panels --- */
.code, pre.code {
  background: var(--sg-code);
  color: var(--sg-codeink);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
}
.code .copy, .code button {
  border: 1px solid var(--sg-codeink);
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--sg-codeink);
  font-family: var(--font-mono);
  text-transform: uppercase;
  letter-spacing: 1px;
  font-size: 10px;
}
.code .copy.copied { background: var(--sg-codeink); color: var(--sg-code); }

/* --- Sidebar: paper panel, rule-separated, accent edge for active --- */
aside.sidebar {
  background: var(--bg-0);
  border-right: 1px solid var(--border);
  box-shadow: none;
}
.sidebar-head { border-bottom: 1px solid var(--border); }
.brand-text { letter-spacing: -0.04em; font-weight: 700; }
.sidebar-nav-link {
  border-radius: 0;
  border-left: 2px solid transparent;
  letter-spacing: -0.01em;
}
.sidebar-nav-link:hover { background: var(--surface-2); }
.sidebar-nav-link.active {
  background: transparent;
  border-left: 2px solid var(--accent);
  color: var(--text-0);
  font-weight: 600;
}
.sidebar-nav-link .ico { opacity: 0.75; }
.sidebar-section-toggle, .sidebar-tab { font-family: var(--font-mono); text-transform: uppercase; letter-spacing: 1.1px; font-size: 10px; }
.sidebar-children { border-left: 1px solid var(--border); }
.sidebar-foot { border-top: 1px solid var(--border); }
.account-id, .signout { font-family: var(--font-mono); text-transform: uppercase; letter-spacing: 1px; font-size: 10px; }
.theme-toggle { border: 1px solid var(--border); border-radius: var(--r-sm); }
.theme-toggle:hover { border-color: var(--text-0); }

/* --- Tables: rules only, caps headers, numeric alignment --- */
.tbl-wrap { border: 1px solid var(--border); border-radius: var(--r-md); background: var(--bg-0);
  /* A wide admin table must scroll inside its own frame. Without this the table
     (573px wide on admin_contracts) pushed the whole document past the viewport
     at 390px, giving the page a horizontal scrollbar. `overflow-x:auto` was
     never set here, and the border-radius already implies a clipping frame. */
  overflow-x: auto; -webkit-overflow-scrolling: touch; max-width: 100%; }
.tbl-wrap table { border-collapse: collapse; }
.tbl-wrap th {
  font-family: var(--font-mono);
  text-transform: uppercase;
  letter-spacing: 1.1px;
  font-size: 10px;
  color: var(--text-2);
  border-bottom: 1px solid var(--border);
  text-align: left;
}
.tbl-wrap td { border-bottom: 1px solid var(--border-faint); }
.tbl-wrap tbody tr:last-child td { border-bottom: 0; }
.tbl-wrap tbody tr:hover { background: var(--surface-2); }
.tbl-wrap td.mono, .tbl-wrap td .mono { font-variant-numeric: tabular-nums; }

/* --- Modals: paper panel, ruled, no shadow --- */
.modal-overlay { background: rgba(16, 25, 24, 0.55); }
.modal {
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--bg-0);
  box-shadow: none;
}
.modal h3, .modal .h3 { letter-spacing: var(--track-h3); }

/* --- Empty states: centered, unboxed --- */
.empty, .empty-state { border: 0; background: transparent; box-shadow: none; color: var(--text-2); }
.empty-title { letter-spacing: var(--track-h3); color: var(--text-1); }

/* --- Command palette --- */
.cmdk { border: 1px solid var(--border); border-radius: var(--r-md); box-shadow: none; background: var(--bg-0); }
.cmdk-item.selected { background: var(--accent-soft); }

/* --- Misc surfaces --- */
.refresh-bar, .auto-toggle { border: 1px solid var(--border); border-radius: var(--r-sm); background: transparent; }
.section-head { border-bottom: 1px solid var(--border); }
/* `.section-head` used to be that one line and nothing else: a bottom border with no font, no
   size, no caps and no rhythm, because it was never added to the `.label` group above. It is
   the class the flagship admin pages actually use — admin_revenue, admin_contracts,
   admin_metrics, admin_security, admin_cache, models, pricing, auth, landing — while the
   identical role on stats.html and admin_yield.html is `.section-title`, which WAS in that
   group. So "Revenue by Model" rendered in body Arial at body size with a rule under it, and
   "Recent Harvests" rendered in mono 11px caps: two treatments for one role on adjacent pages
   of the same console. It now shares the label typography and differs only in carrying the
   rule beneath it. `:first-child` keeps a leading section from opening with dead space. */
.section-head {
  margin: var(--s-8) 0 var(--s-4);
  padding-bottom: var(--s-2);
}
.section-head:first-child { margin-top: 0; }
/* `.section-title` IS THE SAME ROLE AS `.section-head` AND HAD THE SAME PROBLEM, IN REVERSE.
   Both are in the label group at line 903 (mono, caps, 1.3px, 11px), but five pages redefine
   `.section-title` in their own <style>, and a page <style> comes AFTER the <link> -- so at equal
   (0,1,0) specificity the PAGE wins on source order and the group rule is what gets overridden.
   Measured on the running stack:

     stats           11px  caps  ls 0.66px  w600  margin 32/12   (page rule wins)
     node            11px  caps  ls 0.66px  w600  margin 28/12   (page rule wins)
     regions         11px  caps  ls 1.3px   w400  margin  0/0    (NO page rule -> group rule)
     admin_yield     16px  caps  ls 1.3px   w600  margin 24/12   (page rule wins)
     marketplace_billing  16px  caps        (page rule wins)

   So the SAME semantic heading rendered at 11px on one page and 16px on the next, and on
   `regions` it had zero margin -- sections butted directly against the block above with no
   rhythm at all. That last one was the actual visual bug: four section headings stacked with no
   space, on the page a reviewer opens right after stats.
   These four lines give the role one definition. Specificity is deliberately (0,1,0) to match,
   because raising it above the page rules would break the two pages that intentionally need a
   different treatment for a DIFFERENT class -- nothing overrides this on purpose. */
.section-title {
  margin: var(--s-8) 0 var(--s-4);
  font-weight: 600;
  letter-spacing: 1.3px;
}
.section-title:first-child, .section-title:first-of-type { margin-top: 0; }
.health-grid > *, .stat-grid > *, .settings-grid > *, .yield-stats > * { border-radius: var(--r-md); }
.banner, .notice {
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--surface-2);
  box-shadow: none;
}


/* --- Scrollbar --- */
::-webkit-scrollbar-thumb { background: var(--border); border-radius: 0; }
::-webkit-scrollbar-thumb:hover { background: var(--border-hi); }

/* ============================================================
   Signal structural helpers (new; sg- prefixed, nothing collides)
   ============================================================ */
.sg-rule { height: 1px; background: var(--border); border: 0; margin: 0; }
.sg-label {
  font-family: var(--font-mono);
  text-transform: uppercase;
  letter-spacing: 1.3px;
  font-size: var(--text-xs);
  color: var(--text-2);
}
.sg-band {
  background: var(--accent);
  color: var(--on-accent);
  border: 0;
  padding: 28px 32px;
}
.sg-band h2, .sg-band h3, .sg-band p { color: var(--on-accent); }
.sg-hero { display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, 0.85fr); gap: 40px; align-items: center; }
.sg-hero-title { font-size: var(--text-display); line-height: 0.96; letter-spacing: var(--track-h1); font-weight: 500; }
.sg-hero-title em { font-style: italic; color: var(--accent); }
.sg-engraving { color: var(--accent); max-width: 420px; }
.sg-engraving svg { width: 100%; height: auto; display: block; }
.sg-tabs { display: inline-flex; border: 1px solid var(--border); }
.sg-tabs button { border: 0; border-right: 1px solid var(--border); padding: 8px 14px; font-family: var(--font-mono); text-transform: uppercase; letter-spacing: 1px; font-size: 10px; }
.sg-tabs button:last-child { border-right: 0; }
.sg-tabs button[aria-pressed="true"] { background: var(--text-0); color: var(--bg-1); }
.sg-strip { display: flex; gap: 0; border-top: 1px solid var(--border); border-bottom: 1px solid var(--border); }
.sg-strip > * { flex: 1; padding: 14px 0; border-right: 1px solid var(--border); text-align: center; font-family: var(--font-mono); text-transform: uppercase; letter-spacing: 1.2px; font-size: 11px; }
.sg-strip > *:last-child { border-right: 0; }
.sg-model-row { display: grid; grid-template-columns: minmax(0, 1fr) repeat(3, minmax(0, 140px)); gap: 16px; border-bottom: 1px solid var(--border); padding: 18px 0; }

@media (max-width: 900px) {
  .sg-hero { grid-template-columns: 1fr; gap: 24px; }
  .sg-engraving { max-width: 300px; }
  .sg-model-row { grid-template-columns: 1fr 1fr; }
}

/* --- Toast: inverted ink --- */
.toast {
  background: var(--text-0);
  color: var(--bg-1);
  border: 1px solid var(--text-0);
  border-radius: var(--r-sm);
  box-shadow: none;
}

/* The page head sits closer to its first section than line 648's 28px allows. A strip already
   carries 40px of its own air — `padding-top: 24px` on the cell — so 28px here stacked up to a
   ~93px gap between the page's subtitle and the first metric, which reads as a missing block
   rather than as breathing room. The redesign pairs them at 20px. Declared after line 648 so the
   later rule wins on source order at equal specificity, rather than by being made heavier. */
.page-head { margin-bottom: var(--s-5); }

/* ============================================================
   STAT STRIPS — the ruled table

   A strip is not a grid of filled cards. It is a TABLE: one top and one bottom hairline for the
   block, a 1px vertical rule BETWEEN cells, and no left rule on the first cell of each row — so a
   label sits on the page's content edge, in line with the h1, instead of being pushed off it by a
   gutter. The design's own numbers:

     .stat-grid   gap 0; border-block 1px; margin 0 0 32px; auto-fit minmax(min(300px,100%),1fr)
     .stat        fill none; border 0; border-LEFT 1px; radius 0; padding 24/20/20; min-height 132
     .stat .lbl   mono 11px caps 1.3px --text-2; margin 0 0 12px; min-height 33px (two lines reserved)
     .stat .val   sans 30px/600 -.03em --text-0; tabular-nums
     .stat .sub   mono 13px/1.5 --text-2; margin-top auto; padding-top 12px

   `.stat` had already been HALF-ported here (line ~970 sets padding 16px and the 30px/600 value),
   which is worse than not ported at all: the figure was the redesign's and everything around it was
   the old card. What was still live, and what these rules replace:

     - a `surface` fill, a 1px border on all four sides and a radius on every cell, so a six-cell
       strip read as six boxes rather than as one ruled band;
     - `padding: 16px` with no gap to the rule beside it once the fill went;
     - `.lbl` at margin-bottom 6px, pressing each label against its own value;
     - NO `margin-top: auto` on `.sub`. That one property is what pins a caption to the bottom of
       its cell; without it a two-line caption pulls up instead of filling down and captions in one
       row stop sharing a baseline.

   ORDERED LAST ON PURPOSE. `.stat` is also matched by the line-590 and line-970 groups above, and
   `.lbl`/`.val`/`.sub` are plain class selectors that pages redeclare in their own <style> — which
   wins over this <link> at equal specificity. Declaring the treatment once, at the end, means a
   page's later delta reads as a delta instead of as a second copy of this block. */
.stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr));
  gap: 0;
  border-block: 1px solid var(--border);
  margin: 0 0 var(--s-8);
}
/* Fixed counts get an explicit count. `auto-fit` at a 300px minimum resolves to three tracks at
   desktop width — which is what balances 3, 4 and 6 cells — so 3 restates it and 4/5, which do not
   divide by three, are stated rather than left to the browser. */
.stat-grid.cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.stat-grid.cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.stat-grid.cols-5 { grid-template-columns: repeat(5, minmax(0, 1fr)); }
.stat-grid.cols-6 { grid-template-columns: repeat(6, minmax(0, 1fr)); }
/* The flow row is the one strip that is not a uniform grid: arrow cells sit BETWEEN the three
   stats. It is excluded from the wrap rules below, because collapsing its 5-track template to two
   columns would strand the arrows as lines of their own. */
.stat-grid-flow { grid-template-columns: 1fr auto 1fr auto 1fr; align-items: stretch; }
.flow-arrow {
  display: flex; align-items: center; justify-content: center;
  color: var(--text-3); font-size: 24px; padding: 0 var(--s-2);
}
/* The cell. `border-left` is the only rule a cell draws — the strip's own top and bottom hairlines
   close the block, so a four-sided border here would double every edge ("2,4,2 and one lighter"
   borders is the first thing that reads as wrong on a strip). `margin-top: auto` on `.sub` below
   only works because the cell is a flex column. */
.stat, .stat-card, .dash-card {
  background: none;
  border: 0;
  border-left: 1px solid var(--border);
  border-radius: 0;
  padding: var(--s-6) var(--s-5) var(--s-5);
  min-height: 132px;
  display: flex;
  flex-direction: column;
}
/* The first cell of each ROW drops the hairline and the padding it was there to balance, so its
   label lands on the content edge. `:first-child` alone stopped being enough the moment a strip
   wrapped: cell 4 of a 3x2 strip kept a rule floating out at the page edge. */
.stat:first-child, .stat-card:first-child, .dash-card:first-child { border-left: 0; padding-left: 0; }
.stat-grid:not(.cols-4):not(.cols-5):not(.cols-6):not(.stat-grid-flow) > .stat:nth-child(n+4),
.stat-grid.cols-4 > .stat:nth-child(n+5),
.stat-grid.cols-5 > .stat:nth-child(n+6),
.stat-grid.cols-6 > .stat:nth-child(n+7) { border-top: 1px solid var(--border); }
.stat-grid:not(.cols-4):not(.cols-5):not(.cols-6):not(.stat-grid-flow) > .stat:nth-child(3n+1),
.stat-grid.cols-4 > .stat:nth-child(4n+1),
.stat-grid.cols-5 > .stat:nth-child(5n+1),
.stat-grid.cols-6 > .stat:nth-child(6n+1) { border-left: 0; padding-left: 0; }
/* TWO LINES OF LABEL ARE RESERVED — AND `.label` IS A FOURTH NAME FOR THIS SAME COMPONENT.
   Measured: a one-line label is 17px tall, a two-line label 33px. Without the reservation, the
   single label in a row that wraps pushes that one value 16px down its own column, and a four-up
   strip reads as two rows with three value baselines.
   `.label` matters here because it is ALSO the form-field label (line 498: `display:block; 12px;
   --text-1; margin-bottom:6px`), so it could not simply be folded into this group — the stat
   selector has to be qualified. `admin_revenue.html` is the page it broke: its cells are
   `<div class="label">` + `<div class="label sub">`, so all six missed the reservation, and because
   row 1's captions wrap to two lines while row 2's two cells have no caption at all, the strip
   measured 143/143/143 over 132/132/132 with `min-height: auto` on every label. Two different
   causes, one visible defect: a strip that looks like two different rows. */
.stat .lbl, .stat-card .lbl, .dash-card .lbl, .stat .label, .stat-card .label, .dash-card .label,
.stat .stat-label, .stat-card .stat-label, .dash-card .stat-label {
  font-family: var(--font-mono);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 1.3px;
  font-weight: 400;
  color: var(--text-2);
  margin: 0 0 var(--s-3);
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-2);
  min-height: 33px;
}
.stat .val, .stat-card .val, .dash-card .val,
.stat .value, .stat-card .value, .dash-card .value,
.stat .stat-value, .stat-card .stat-value, .dash-card .stat-value {
  font-family: var(--font-sans);
  font-size: 30px; font-weight: 600; letter-spacing: -0.03em; line-height: 1.05;
  color: var(--text-0); margin: 0;
  font-variant-numeric: tabular-nums;
  white-space: nowrap; overflow-wrap: normal;
}
/* FOUR/FIVE NAMES FOR THIS ONE PART, and the design drop names them all: `.val` and `.value` are
   folded together in its own rule (components.css:946 `.val, .dash-card .val, .value`), while
   `theme.css` carried neither `.value` nor `.stat-value` nor `.unit` at all. Five live pages use
   `.value` (`rent`, `marketplace`, `marketplace_billing`, `marketplace_usage`, `admin_yield`) and
   two wrap their units in `<span class="unit">` (`rent`, `marketplace_models`) — so those figures
   rendered with no strip treatment whatsoever: the value at body size, the unit at full ink. */
/* A unit appended to a value wraps mid-number ("0.001 / ETH/interval") and pulls that one baseline
   below its neighbours. The unit belongs in the caption, not concatenated onto the figure.
   `.unit` is the inline-element spelling of the same idea as `.val small`; neither had a rule in
   theme.css, so `rent`'s "2848<span class="unit">GB</span>" rendered the unit at full value ink. */
.stat .val small, .stat-card .val small, .dash-card .val small,
.stat .unit, .stat-card .unit, .dash-card .unit {
  font-family: var(--font-sans); font-size: 12px; font-weight: 400;
  letter-spacing: 0; color: var(--text-2);
}
/* A metric nobody is measuring yet says so. It is NOT a zero: 0 is a measurement, "—" is the
   absence of one, and the figure keeps the full 30px/600 weight so the cell stays in the grid
   rather than shrinking into a footnote. */
.stat .val.tbd, .stat .val.build { color: var(--text-3); font-size: 30px; font-weight: 600; }
.stat .sub, .stat-card .sub, .dash-card .sub {
  font-family: var(--font-mono);
  font-size: 13px; line-height: 1.5;
  color: var(--text-2);
  margin-top: auto; padding-top: var(--s-3);
}
.stat .sub.good { color: var(--good); }
.stat .sub.bad { color: var(--bad); }
/* A cell with NO caption must still line up with a cell that has one. `margin-top: auto` pins a
   caption to the bottom, but a cell whose markup has no `.sub` at all (admin_revenue's last two:
   "Active Subscriptions" and "Yield Distributed") simply stops 11px shorter — measured 143px beside
   132px in one row, which reads as a broken cell rather than as a shorter card. Exactly one
   caption's height of padding, applied only to cells that LACK a caption, so nothing moves on a cell
   that already has one. The design drop ships those same two cells without a caption, so this is a
   defect inherited from the mockup rather than one the port introduced.
   Declared AFTER the cell block above, because `padding` there is a shorthand that sets
   `padding-bottom` and would otherwise erase this. */
.stat:not(:has(.sub)), .stat-card:not(:has(.sub)), .dash-card:not(:has(.sub)) {
  padding-bottom: calc(var(--s-5) + 11px);
}

/* ============================================================
   STAT-CARD VOCABULARY — the console scale

   The same three parts are named three ways across the product: `.stat-label`/`.stat-value`
   (admin_yield), `.label`/`.value`/`.unit` (rent, marketplace_usage) and `.lbl`/`.val`
   (admin_users, admin_cache). Before these rules only the last spelling got any treatment, so a
   page using another one rendered its label in body Arial. Measured on admin_yield: `.stat-label`
   computed `Arial` at `min-height: 38px` against caps mono everywhere else.

   NOT scoped to a `.console-page` class. The design drop uses one, but NO live page declares it —
   grep finds it only in the mockup shell — so scoping there would have been 40 lines of dead CSS.
   The card SHAPE is a per-page decision these pages already make locally (each of the four
   declares its own `background`/`border`/`radius`), so what the shared layer owes them is the
   TYPE, and the type applies wherever the names appear.

   The label reservation is 24px here rather than the strip's 33px: a console card's label is
   11px/13px, so two lines is 26px, and 24px holds the same two-line guarantee the 33px holds at
   the strip's metrics. This is the value the design drop settled on for the same reason. */
/* SCOPED, for the same reason as the value rule below: `.stat .lbl` here would otherwise reach a
   strip cell declared by the ruled-table rules above, replacing its 33px/12px reservation with the
   console's 24px/8px. A cell's label inside a `.stat-grid` keeps the strip's metrics.
   `.stat-label` needs no combinator — no strip cell uses that name, so it cannot collide. */
:not(.stat-grid) > .stat > .lbl, :not(.stat-grid) > .stat > .label,
:not(.stat-grid) > .stat-card > .lbl, :not(.stat-grid) > .stat-card > .label,
.stat-label {
  min-height: 24px; line-height: 13px; margin-bottom: var(--s-2);
}
/* The console value scale is 24px, not the strip's 30px: a console card is a third of a strip
   cell's width, and a 30px figure crowds it.

   IT MUST NOT REACH A STRIP CELL. This block is declared after the ruled-table rules above and
   matches the same `.stat .val`, so unscoped it would override the strip's 30px and shrink every
   strip in the product to 24px. The scope has to be a child combinator from a non-`.stat-grid`
   parent, because a cell carries no marker distinguishing the two roles: `.stat` inside a
   `.stat-grid` is a strip cell, `.stat` anywhere else is a standalone card. */
:not(.stat-grid) > .stat > .val, :not(.stat-grid) > .stat > .val.mono,
:not(.stat-grid) > .stat-card > .val,
:not(.stat-grid) > .stat > .value, :not(.stat-grid) > .stat-card > .value,
:not(.stat-grid) > .stat > .stat-value, :not(.stat-grid) > .stat-card > .stat-value {
  font-size: 24px; font-weight: 600; letter-spacing: -.02em; line-height: 1.15;
  margin-bottom: var(--s-1);
}

/* The "not live yet" banner on a page that has a control that cannot complete. Ported from the
   DEPLOYED earnings.html, which had it as a page-local class; it belongs in the layer because it is
   the honest-state treatment for any page waiting on a backend (earnings' withdrawal is the first).
   `--warn`, not `--bad`: nothing has failed, the feature simply is not deployed yet, and colouring
   it red would tell a user their money is in trouble when it is only not yet movable. */
.withdraw-note {
  background: var(--warn-dim);
  border: 1px solid var(--warn-line);
  border-radius: var(--r-md);
  padding: 14px 18px;
  margin-bottom: var(--s-6);
  font-size: 13px;
  color: var(--text-1);
}
.withdraw-note strong { color: var(--warn); }

/* The "Live" pill. One page uses this class and only one, and its version was a rounded capsule
   (`--r-full` on a wide inline-flex is a 999px radius) filled with `--good-dim` at 11px/500 sans.
   Capsules are NOT wrong in this design — the drop uses `--r-full` on `.tag`, `.mc-badge`,
   `.badge` and `.filter-pill` deliberately — so the reason to change this one is narrower: the
   design's own `stats` markup (`mockup stats.html:8`, transcribed from `:118-124`) draws this pill
   as a flat ruled label. It is a status word, not a badge.
   `--good` on this warm canvas is a dark forest green, not a neon status colour, so the tint is
   dropped with the capsule: it added weight without meaning. */
.live-pill {
  display: inline-flex; align-items: center; gap: var(--s-2);
  font-family: var(--font-mono); font-size: 11px; text-transform: uppercase;
  letter-spacing: 1.3px; color: var(--good);
  border: 1px solid var(--good-line); border-radius: 0;
  padding: 4px 10px;
  flex: none;
}
.dot.pulse { animation: synet-pulse 2s ease-in-out infinite; }
@keyframes synet-pulse { 0%, 100% { opacity: 1 } 50% { opacity: .3 } }

@media (max-width: 1024px) {
  /* Two balanced columns, with the flow row excluded so its arrow template survives. A 2-up strip
     needs its hairline MOVED, not deleted: the interior rule belongs to the right-hand cell of each
     pair, and every row after the first needs a top rule. Setting only `grid-template-columns`
     leaves the third cell — now the start of row 2 — carrying a border-left at the page edge. */
  .stat-grid:not(.stat-grid-flow) { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .stat-grid:not(.stat-grid-flow) > .stat { border-left: 0; padding-left: 0; }
  .stat-grid:not(.stat-grid-flow) > .stat:nth-child(even) { border-left: 1px solid var(--border); padding-left: var(--s-5); }
  .stat-grid:not(.stat-grid-flow) > .stat:nth-child(n+3) { border-top: 1px solid var(--border); }
}
@media (max-width: 720px) {
  .stat-grid:not(.stat-grid-flow) { grid-template-columns: minmax(0, 1fr); }
  .stat, .stat-card, .dash-card { border-left: 0; border-top: 1px solid var(--border); padding-left: 0; }
  .stat:first-child, .stat-card:first-child, .dash-card:first-child { border-top: 0; }
  /* the arrow cells are decoration BETWEEN columns; once the grid stacks they would each be a line
     of their own, so they go. */
  .stat-grid-flow { grid-template-columns: minmax(0, 1fr); }
  .flow-arrow { display: none; }
}
