:root {
  color-scheme: light dark;
  --bg: #fff;
  --fg: #1a1a1a;
  --muted: #666;
  --border: #ccc;
  --table-border: #eee;
  --input-bg: #fff;
  --input-fg: #1a1a1a;
  --msg-ok-bg: #e6f4ea;
  --msg-ok-fg: #1e7e34;
  --msg-err-bg: #fdecea;
  --msg-err-fg: #b3261e;
  --danger: #b3261e;
  --keys-btn-fg: #333;
  --keys-btn-border: #999;
  --keys-btn-expanded-bg: #333;
  --keys-btn-expanded-fg: #fff;
  --panel-bg: #fafafa;
  /* Chart tokens (metrics.html) — categorical slots 1-5 of the dataviz
     skill's reference palette (references/palette.md), validated via
     scripts/validate_palette.js for this exact 5-slot subset in both
     modes. Identity (which container is which) stays consistent across
     charts by always assigning slots in CONTAINERS order (ssh/web/admin/
     app/db) — see metrics.js. */
  --chart-surface: #fcfcfb;
  --chart-gridline: #e1e0d9;
  --chart-baseline: #c3c2b7;
  --chart-muted: #898781;
  --series-1: #2a78d6;
  --series-2: #eb6834;
  --series-3: #1baf7a;
  --series-4: #eda100;
  --series-5: #e87ba4;
}
@media (prefers-color-scheme: dark) {
  :root {
    --bg: #16213e;
    --fg: #eee;
    --muted: #aaa;
    --border: #3a3a5a;
    --table-border: #2a2a4a;
    --input-bg: #1e2a4a;
    --input-fg: #eee;
    --msg-ok-bg: #1e3a2a;
    --msg-ok-fg: #7be0a0;
    --msg-err-bg: #3a1e1e;
    --msg-err-fg: #ff6b6b;
    --danger: #ff6b6b;
    --keys-btn-fg: #ccc;
    --keys-btn-border: #666;
    --keys-btn-expanded-bg: #2ec4b6;
    --keys-btn-expanded-fg: #16213e;
    --panel-bg: #1e2a4a;
    --chart-surface: #1a1a19;
    --chart-gridline: #2c2c2a;
    --chart-baseline: #383835;
    --chart-muted: #898781;
    --series-1: #3987e5;
    --series-2: #d95926;
    --series-3: #199e70;
    --series-4: #c98500;
    --series-5: #d55181;
  }
}
/* Explicit override from the in-page toggle (data-theme on <html>) —
   higher specificity than the plain :root above and the media query
   block, since it targets :root PLUS an attribute, so it wins either
   way regardless of the OS's actual preferences-color-scheme. */
:root[data-theme="dark"] {
  --bg: #16213e;
  --fg: #eee;
  --muted: #aaa;
  --border: #3a3a5a;
  --table-border: #2a2a4a;
  --input-bg: #1e2a4a;
  --input-fg: #eee;
  --msg-ok-bg: #1e3a2a;
  --msg-ok-fg: #7be0a0;
  --msg-err-bg: #3a1e1e;
  --msg-err-fg: #ff6b6b;
  --danger: #ff6b6b;
  --keys-btn-fg: #ccc;
  --keys-btn-border: #666;
  --keys-btn-expanded-bg: #2ec4b6;
  --keys-btn-expanded-fg: #16213e;
  --panel-bg: #1e2a4a;
  --chart-surface: #1a1a19;
  --chart-gridline: #2c2c2a;
  --chart-baseline: #383835;
  --chart-muted: #898781;
  --series-1: #3987e5;
  --series-2: #d95926;
  --series-3: #199e70;
  --series-4: #c98500;
  --series-5: #d55181;
}
:root[data-theme="light"] {
  --bg: #fff;
  --fg: #1a1a1a;
  --muted: #666;
  --border: #ccc;
  --table-border: #eee;
  --input-bg: #fff;
  --input-fg: #1a1a1a;
  --msg-ok-bg: #e6f4ea;
  --msg-ok-fg: #1e7e34;
  --msg-err-bg: #fdecea;
  --msg-err-fg: #b3261e;
  --danger: #b3261e;
  --keys-btn-fg: #333;
  --keys-btn-border: #999;
  --keys-btn-expanded-bg: #333;
  --keys-btn-expanded-fg: #fff;
  --panel-bg: #fafafa;
  --chart-surface: #fcfcfb;
  --chart-gridline: #e1e0d9;
  --chart-baseline: #c3c2b7;
  --chart-muted: #898781;
  --series-1: #2a78d6;
  --series-2: #eb6834;
  --series-3: #1baf7a;
  --series-4: #eda100;
  --series-5: #e87ba4;
}
#themeToggle { margin: 0; }
body { font-family: system-ui, sans-serif; max-width: 640px; margin: 2rem auto; padding: 0 1rem; background: var(--bg); color: var(--fg); }
body.metrics-page { max-width: 920px; }
.page-nav { font-size: 0.85rem; margin-bottom: 0.5rem; }
.page-nav a { color: var(--muted); }
h1 { font-size: 1.3rem; }
fieldset { margin-bottom: 1.5rem; border: 1px solid var(--border); border-radius: 6px; }
#studentsSection { position: relative; }
#studentsSection #refreshBtn { position: absolute; top: 0.6rem; right: 0.8rem; margin: 0; }
.status-dot { display: inline-block; width: 0.55rem; height: 0.55rem; border-radius: 50%; margin-right: 0.4rem; }
.status-dot.running { background: #2ec4b6; }
.status-dot.other { background: var(--danger); }
td.version { font-family: monospace; }
label { display: block; margin-top: 0.6rem; font-size: 0.9rem; }
input, textarea { width: 100%; box-sizing: border-box; padding: 0.4rem; margin-top: 0.2rem; font-family: inherit; background: var(--input-bg); color: var(--input-fg); border: 1px solid var(--border); border-radius: 4px; }
textarea { height: 4rem; font-family: monospace; font-size: 0.85rem; }
button { margin-top: 0.8rem; padding: 0.4rem 1rem; cursor: pointer; }
table { width: 100%; border-collapse: collapse; margin-top: 0.5rem; }
td, th { text-align: left; padding: 0.4rem; border-bottom: 1px solid var(--table-border); vertical-align: top; }
/* Fixed column widths so refreshing values (CPU%, status, version) don't
   reflow the table every 5s auto-refresh — see loadContainers() in
   containers.js. */
#containersTable { table-layout: fixed; }
#containersTable td, #containersTable th { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#msg { margin: 1rem 0; padding: 0.6rem; border-radius: 4px; display: none; }
#msg.ok { display: block; background: var(--msg-ok-bg); color: var(--msg-ok-fg); }
#msg.err { display: block; background: var(--msg-err-bg); color: var(--msg-err-fg); }
.remove-btn, .keys-btn { color: var(--danger); background: none; border: 1px solid var(--danger); border-radius: 4px; margin: 0 0 0 0.3rem; }
.keys-btn { color: var(--keys-btn-fg); border-color: var(--keys-btn-border); }
.keys-btn.expanded { background: var(--keys-btn-expanded-bg); color: var(--keys-btn-expanded-fg); }
.keys-btn .chevron { display: inline-block; transition: transform 0.15s ease; }
.keys-btn.expanded .chevron { transform: rotate(90deg); }
.keys-panel { background: var(--panel-bg); }
.keys-panel ul { list-style: none; padding: 0; margin: 0 0 0.6rem 0; font-family: monospace; font-size: 0.8rem; }
.keys-panel li { display: flex; justify-content: space-between; align-items: center; gap: 0.5rem; padding: 0.2rem 0; }
.keys-panel li span { overflow-wrap: anywhere; }
.keys-panel textarea { height: 2.5rem; }
#progressOverlay {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.45);
  z-index: 1000;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  gap: 1rem;
}
#progressOverlay.show { display: flex; }
.spinner {
  width: 2.5rem;
  height: 2.5rem;
  border: 4px solid #ddd;
  border-top-color: #2ec4b6;
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
#progressText { color: white; font-size: 1rem; }
#progressLog {
  list-style: none;
  padding: 0;
  margin: 0;
  max-height: 12rem;
  min-width: 280px;
  overflow-y: auto;
  font-family: monospace;
  font-size: 0.8rem;
  color: #ccc;
}
#progressLog li { padding: 0.15rem 0; }
#progressLog li:last-child { color: #2ec4b6; }

/* metrics.html — chart cards, legend, tooltip, stat tiles. See
   metrics.js for the SVG line-chart renderer these support. */
.chart-row { display: flex; gap: 1rem; flex-wrap: wrap; }
.chart-card { flex: 1 1 320px; min-width: 0; }
.chart-card h3 { font-size: 0.95rem; margin: 0 0 0.3rem 0; font-weight: 600; }
.chart-svg-wrap { position: relative; background: var(--chart-surface); border-radius: 6px; }
.chart-svg-wrap svg { display: block; width: 100%; height: auto; }
.chart-gridline { stroke: var(--chart-gridline); stroke-width: 1; }
.chart-baseline { stroke: var(--chart-baseline); stroke-width: 1; }
.chart-axis-label { fill: var(--chart-muted); font-size: 10px; font-family: system-ui, sans-serif; }
.chart-line { fill: none; stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
.chart-crosshair { stroke: var(--chart-baseline); stroke-width: 1; pointer-events: none; display: none; }
.chart-hit-area { fill: transparent; }
.chart-legend { display: flex; flex-wrap: wrap; gap: 0.7rem; margin-top: 0.4rem; font-size: 0.78rem; color: var(--muted); }
.chart-legend-item { display: flex; align-items: center; gap: 0.35rem; }
.chart-legend-swatch { display: inline-block; width: 0.7rem; height: 2px; border-radius: 1px; }
.chart-tooltip {
  position: absolute;
  pointer-events: none;
  display: none;
  background: var(--panel-bg);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 0.35rem 0.5rem;
  font-size: 0.75rem;
  white-space: nowrap;
  z-index: 10;
}
.chart-tooltip .tt-time { color: var(--muted); margin-bottom: 0.2rem; }
.chart-tooltip .tt-row { display: flex; align-items: center; gap: 0.35rem; }
.chart-tooltip .tt-key { display: inline-block; width: 0.6rem; height: 2px; border-radius: 1px; }
.chart-tooltip .tt-value { font-weight: 600; }
.chart-tooltip .tt-name { color: var(--muted); }
.stat-tiles { display: flex; gap: 0.8rem; flex-wrap: wrap; margin-top: 0.5rem; }
.stat-tile { flex: 1 1 100px; background: var(--panel-bg); border-radius: 6px; padding: 0.5rem 0.7rem; }
.stat-tile .stat-label { font-size: 0.72rem; color: var(--muted); }
.stat-tile .stat-value { font-size: 1.3rem; font-weight: 600; }
#metricsDatabasesTable { table-layout: fixed; }
#metricsDatabasesTable td, #metricsDatabasesTable th { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#metricsDatabasesTable td.num, #metricsDatabasesTable th.num { text-align: right; font-variant-numeric: tabular-nums; }
#metricsStudentsTable { table-layout: fixed; }
#metricsStudentsTable td, #metricsStudentsTable th { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#metricsStudentsTable td.num, #metricsStudentsTable th.num { text-align: right; font-variant-numeric: tabular-nums; }
/* A nonzero error count is a status signal, not a series identity — reuses
   --danger (already the app's one reserved error/status color, never used
   for chart series — see --series-* above) so it's visible scanning the
   column, not just on hover. */
.count-error { color: var(--danger); font-weight: 600; }
