/* Design tokens — light mode is canonical. Dark mode overrides below.
 *
 * The legacy variables (--card, --text, --muted, --green, --yellow, --red)
 * are aliased onto the design tokens so existing rules retheme without
 * having to be rewritten. Subsequent B-steps replace these references
 * with the canonical tokens; the aliases get cleaned up in B7.
 */
:root {
  --bg:           #fafafa;
  --surface:      #ffffff;
  --surface-alt:  #f4f4f5;
  --ink:          #0a0a0a;
  --ink-2:        #525252;
  --ink-3:        #a3a3a3;
  --border:       #e5e5e5;
  --divider:      #eaeaea;
  --accent:       #16a34a;
  --accent-ink:   #ffffff;
  --warning:      #ca8a04;
  --alert:        #dc2626;
  --live-ring:    #dc2626;

  --font-sans: 'Space Grotesk', system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
  --font-mono: 'JetBrains Mono', var(--font-mono);

  /* Legacy aliases (B7 cleanup target). */
  --card:  var(--surface);
  --text:  var(--ink);
  --muted: var(--ink-2);
  --green: var(--accent);
  --yellow: var(--warning);
  --red:   var(--alert);
}

[data-theme="dark"] {
  --bg:           #08090b;
  --surface:      #1c1d22;
  --surface-alt:  #262830;
  --ink:          #ededee;
  --ink-2:        #9a9aa0;
  --ink-3:        #5a5b62;
  --border:       #34363d;
  --divider:      #2c2e35;
  --accent:       #22c55e;
  --accent-ink:   #06140b;
  --warning:      #eab308;
  --alert:        #ef4444;
  --live-ring:    #ef4444;
}

/* Auto-flip to dark when the user prefers it AND hasn't explicitly chosen.
 * The data-theme attribute (set by the toggle, persisted in localStorage)
 * always wins because it's a more specific selector. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) {
    --bg:           #08090b;
    --surface:      #1c1d22;
    --surface-alt:  #262830;
    --ink:          #ededee;
    --ink-2:        #9a9aa0;
    --ink-3:        #5a5b62;
    --border:       #34363d;
    --divider:      #2c2e35;
    --accent:       #22c55e;
    --accent-ink:   #06140b;
    --warning:      #eab308;
    --alert:        #ef4444;
    --live-ring:    #ef4444;
  }
}

* { box-sizing: border-box; }
body {
  margin: 0;
  font-family: var(--font-sans);
  background: var(--bg);
  color: var(--text);
  padding: 16px;
}
main { max-width: 720px; margin: 0 auto; }
header h1 { margin: 8px 0; font-size: 24px; }
header .tagline { color: var(--muted); margin: 0 0 16px; font-size: 14px; }

.card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 16px;
  margin: 12px 0;
}
.card h2 { margin: 0 0 8px; font-size: 16px; color: var(--muted); font-weight: 600; }
.label { color: var(--muted); font-size: 13px; }
.value { font-size: 18px; font-weight: 600; }

.status { text-align: center; padding: 24px 16px; }
.badge {
  display: inline-block;
  font-size: 32px;
  font-weight: 700;
  padding: 12px 28px;
  border-radius: 999px;
  background: var(--border);
  letter-spacing: 0.5px;
}
.badge[data-state="live"], .badge[data-state="scheduled_live"] { background: var(--green); color: var(--accent-ink); }
.badge[data-state="starting"], .badge[data-state="scheduled_soon"], .badge[data-state="stopping"] { background: var(--yellow); color: var(--ink); }
.badge[data-state="error"] { background: var(--red); color: #fff; }
.runtime { margin-top: 12px; color: var(--muted); font-size: 18px; }

.decoupled-indicators {
  display: flex;
  justify-content: center;
  gap: 18px;
  margin-top: 14px;
  flex-wrap: wrap;
}
.proc-indicator {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  color: var(--muted);
}
.proc-indicator .dot {
  width: 10px; height: 10px; border-radius: 50%;
  background: #555; flex: none;
}
.proc-indicator[data-running="1"] { color: var(--text); }
.proc-indicator[data-running="1"] .dot { background: var(--green); box-shadow: 0 0 6px var(--green); }

.actions { display: grid; gap: 8px; grid-template-columns: 1fr 1fr; }
.actions button {
  padding: 18px;
  font-size: 18px;
  font-weight: 600;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--surface-alt);
  color: var(--text);
  cursor: pointer;
}
.actions button:disabled { opacity: 0.45; cursor: not-allowed; }
.actions button.primary { background: var(--accent); color: var(--accent-ink); border-color: var(--accent); }
.actions button.danger { background: var(--red); color: #fff; border-color: var(--red); }
.actions button:active { transform: translateY(1px); }

.metrics { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 12px; }
.metric { display: flex; flex-direction: column; }
.metric .label { font-size: 12px; text-transform: uppercase; letter-spacing: 0.5px; }
.metric .value { font-size: 18px; }
.metric .value.metric-warn  { color: var(--yellow); }
.metric .value.metric-alarm { color: var(--red); }

/* Speedtest controls inside the metrics card span the full grid width
   so the button sits below the chips rather than competing with them
   for a column. */
.metrics-controls {
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  padding-top: 4px;
  border-top: 1px solid var(--border);
  margin-top: 4px;
}
.metrics-controls .link-btn { padding: 4px 0; }
.metrics-controls .hint { color: var(--muted); }

.preflight-detail ul { list-style: none; padding: 0; margin: 0; }
.preflight-detail li { display: flex; gap: 8px; padding: 6px 0; border-bottom: 1px solid var(--border); font-size: 14px; }
.preflight-detail li:last-child { border-bottom: none; }
.preflight-detail .ok { color: var(--green); }
.preflight-detail .fail { color: var(--red); }
.preflight-detail .name { flex: 0 0 160px; font-weight: 600; }
.preflight-detail .detail { color: var(--ink-2); flex: 1; }

.logs pre {
  background: var(--surface-alt);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 12px;
  font-size: 12px;
  max-height: 320px;
  overflow-y: auto;
  white-space: pre-wrap;
  word-break: break-word;
  margin: 0;
  color: var(--muted);
}

.help { font-size: 14px; color: var(--muted); }
.help p { margin: 6px 0; }
.help strong { color: var(--text); }

.error-banner { background: #3a1010; border-color: var(--red); color: #ffd6d6; }
.error-banner strong { color: var(--red); }
.error-banner p { margin: 4px 0 0; font-family: monospace; font-size: 13px; }

.config-section-controls {
  display: flex;
  gap: 8px;
  margin: 0 0 8px;
}
.config-section-controls button {
  background: transparent;
  border: 1px solid var(--border);
  color: var(--muted);
  padding: 4px 10px;
  border-radius: 6px;
  font-size: 12px;
  cursor: pointer;
}
.config-section-controls button:hover {
  color: var(--text);
  border-color: var(--text);
}

details.config-section {
  border: 1px solid var(--border);
  border-radius: 8px;
  margin: 12px 0;
  overflow: hidden;
}
details.config-section > summary {
  padding: 12px 16px;
  cursor: pointer;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.5px;
  font-size: 12px;
  font-weight: 600;
  user-select: none;
  background: rgba(255, 255, 255, 0.02);
  list-style: none;
  display: flex;
  align-items: center;
  gap: 8px;
}
/* Custom disclosure indicator — replaces native triangle for consistency. */
details.config-section > summary::-webkit-details-marker { display: none; }
details.config-section > summary::before {
  content: "▸";
  font-size: 10px;
  transition: transform 0.12s ease;
  display: inline-block;
  color: var(--muted);
}
details.config-section[open] > summary::before { transform: rotate(90deg); }
details.config-section > summary:hover { background: rgba(255, 255, 255, 0.04); }
details.config-section[open] > summary { border-bottom: 1px solid var(--border); }
.config-section-body { padding: 4px 16px; }

.secrets-subheading {
  margin: 20px 0 6px;
  padding-top: 16px;
  border-top: 1px solid var(--border);
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.5px;
  font-size: 12px;
  font-weight: 600;
}

/* --- Files browser --- */
.files-controls {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: 8px;
}
.files-controls input[type="search"] {
  background: var(--surface-alt);
  border: 1px solid var(--border);
  color: var(--text);
  padding: 6px 10px;
  border-radius: 6px;
  font-size: 13px;
  font-family: inherit;
}
.files-filters {
  display: flex;
  gap: 14px;
  font-size: 13px;
  color: var(--muted);
  flex-wrap: wrap;
}
.files-filters label { cursor: pointer; user-select: none; }
.files-count { color: var(--muted); font-size: 12px; min-height: 16px; }
.files-list {
  list-style: none;
  padding: 0;
  margin: 0 0 12px;
  max-height: 360px;
  overflow-y: auto;
}
.files-row {
  display: flex;
  flex-direction: column;
  padding: 8px 4px;
  border-bottom: 1px solid var(--border);
}
.files-row:last-child { border-bottom: none; }
.files-row a {
  color: var(--text);
  text-decoration: none;
  font-family: ui-monospace, monospace;
  font-size: 13px;
  word-break: break-all;
}
.files-row a:hover { text-decoration: underline; }
.files-meta { color: var(--muted); font-size: 11px; margin-top: 2px; }
.files-list .loading,
.files-list .empty { color: var(--muted); padding: 8px 4px; }
.files-list .err { color: var(--red); padding: 8px 4px; }

.config-row {
  display: grid;
  grid-template-columns: 1fr 220px;
  gap: 16px;
  align-items: center;
  padding: 10px 0;
  border-bottom: 1px solid var(--border);
}
.config-row:last-child { border-bottom: none; }
.config-label { display: flex; flex-direction: column; }
.config-label label { font-weight: 600; font-size: 14px; }
.config-label .hint { color: var(--muted); font-size: 12px; margin-top: 2px; }
.config-input { display: flex; flex-direction: column; gap: 4px; }
.config-input input[type="text"],
.config-input input[type="number"],
.config-input input[type="time"],
.config-input textarea,
.config-input select {
  background: var(--surface-alt);
  border: 1px solid var(--border);
  color: var(--text);
  padding: 8px 10px;
  border-radius: 6px;
  font-size: 14px;
  font-family: inherit;
  width: 100%;
}
.config-input textarea { resize: vertical; min-height: 60px; }

.preview img {
  display: block;
  width: 100%;
  max-width: 100%;
  border-radius: 8px;
  background: var(--surface-alt);
}
.preview .placeholder {
  background: var(--surface-alt);
  border: 1px dashed var(--border);
  border-radius: 8px;
  padding: 32px 16px;
  text-align: center;
  color: var(--muted);
  font-size: 14px;
}
.preview .hint { margin: 8px 0 0; font-size: 12px; color: var(--muted); }
.preview-controls { display: flex; gap: 8px; margin-top: 8px; }
.preview-controls button {
  flex: 1;
  padding: 12px;
  font-size: 15px;
  font-weight: 600;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--surface-alt);
  color: var(--text);
  cursor: pointer;
}
.preview-controls button:disabled { opacity: 0.45; cursor: not-allowed; }
.preview-controls button.danger { background: var(--red); color: #fff; border-color: var(--red); }

/* Wi-Fi section */
.wifi h3.wifi-add-heading {
  margin: 16px 0 4px;
  font-size: 14px;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.5px;
  font-weight: 600;
}
.wifi-platform-note {
  color: var(--yellow);
  font-style: italic;
}
.wifi-platform-note[hidden] { display: none; }
.wifi-current {
  padding: 8px 10px;
  background: var(--surface-alt);
  border-radius: 6px;
  font-size: 14px;
  margin: 4px 0 12px;
  display: flex;
  align-items: center;
  gap: 8px;
}
.wifi-current .muted { color: var(--muted); }
.wifi-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--green);
  box-shadow: 0 0 6px var(--green);
  display: inline-block;
}
.wifi-list { list-style: none; padding: 0; margin: 0 0 8px; }
.wifi-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  padding: 8px 10px;
  background: var(--surface-alt);
  border: 1px solid var(--border);
  border-radius: 6px;
  margin-bottom: 6px;
  font-size: 14px;
}
.wifi-ssid { flex: 1; min-width: 120px; }
.wifi-ssid { font-weight: 600; }
.wifi-badge {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--green);
  color: var(--accent-ink);
  font-weight: 600;
}
.wifi-badge.muted { background: var(--border); color: var(--muted); }
.wifi-forget {
  background: transparent;
  border: 1px solid var(--border);
  color: var(--muted);
  font-size: 12px;
  padding: 4px 10px;
  border-radius: 6px;
  cursor: pointer;
}
.wifi-forget:hover { color: var(--red); border-color: var(--red); }
.wifi-empty { color: var(--muted); padding: 8px 0; font-size: 14px; }
.config-input input[type="checkbox"] {
  width: 22px; height: 22px;
  accent-color: var(--accent);
}
.tag-restart {
  display: inline-block;
  font-size: 11px;
  color: var(--yellow);
  padding-top: 2px;
}
.config-actions { display: flex; gap: 8px; margin-top: 12px; }
.config-msg { margin-top: 8px; font-size: 14px; min-height: 20px; }
.config-msg.ok { color: var(--green); }
.config-msg.err { color: var(--red); }
/* Hide config-msg paragraphs when they have no text — without this,
 * the min-height + margin-top reserve ~28px per empty <p>. Big visible
 * dead space below the Session card came from two of these stacked. */
.config-msg:empty { display: none; }

@media (max-width: 540px) {
  .config-row { grid-template-columns: 1fr; }
}

.sessions table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
}
.sessions th, .sessions td {
  text-align: left;
  padding: 8px 10px;
  border-bottom: 1px solid var(--border);
}
.sessions th {
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.5px;
  font-size: 11px;
  font-weight: 600;
}
.sessions tbody tr.session-row { cursor: pointer; }
.sessions tbody tr.session-row:hover { background: var(--surface-alt); }
.sessions td.reason.ok { color: var(--green); }
.sessions td.reason.err { color: var(--red); }
.sessions td.reason.warn { color: var(--yellow); }
.sessions td.ok { color: var(--green); }
.sessions td.fail { color: var(--red); }
.sessions td.empty { color: var(--muted); text-align: center; padding: 24px; }
.sessions #sessions-table-wrap { overflow-x: auto; margin-top: 8px; }

#session-detail {
  margin-top: 12px;
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 12px;
  background: var(--surface-alt);
}
#session-detail h3 { margin: 0 0 8px; font-size: 14px; color: var(--muted); }
#session-detail pre {
  background: transparent;
  margin: 0 0 10px;
  max-height: 400px;
  overflow-y: auto;
  white-space: pre-wrap;
  word-break: break-word;
  font-size: 12px;
  color: var(--text);
}
#session-detail button {
  background: var(--surface-alt);
  border: 1px solid var(--border);
  color: var(--text);
  padding: 6px 12px;
  border-radius: 6px;
  font-size: 13px;
  cursor: pointer;
}

/* WiFi priority controls */
.wifi-rank {
  display: inline-block;
  width: 22px;
  text-align: center;
  font-weight: 700;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.wifi-prio { font-size: 11px; }
.wifi-handle {
  color: var(--muted);
  font-size: 20px;
  line-height: 1;
  cursor: grab;
  user-select: none;
  padding: 0 4px;
}
.wifi-row[draggable="true"] { cursor: grab; }
.wifi-row.dragging { opacity: 0.4; cursor: grabbing; }
.wifi-row.dragging .wifi-handle { cursor: grabbing; }
.wifi-row.drag-over {
  border-color: var(--accent);
  box-shadow: 0 -2px 0 var(--accent) inset;
}

/* Inline link-style button (used inside config rows for "Clear" etc.) */
.link-btn {
  background: transparent;
  border: none;
  color: var(--muted);
  padding: 0 6px;
  font-size: 12px;
  cursor: pointer;
  text-decoration: underline;
}
.link-btn:hover { color: var(--red); }

/* Secrets card config-input lays out the password field + Clear link side by side. */
.card.secrets .config-input { flex-direction: row; align-items: center; gap: 6px; }
.card.secrets .config-input input { flex: 1; }

/* Monitor toggles */
.monitor-toggles {
  display: flex;
  flex-wrap: wrap;
  gap: 18px;
  margin: 10px 0 8px;
  align-items: center;
}
.monitor-toggles label {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 14px;
  cursor: pointer;
  color: var(--text);
  user-select: none;
}
.monitor-toggles input[type="checkbox"] {
  width: 18px;
  height: 18px;
  cursor: pointer;
  accent-color: var(--accent);
}

/* In-Room Levels card (SPL plot, constellation variant).
 *
 * Real-time in-room SPL (dBA) plotted against audio.spl_target_dba. The card
 * border pulses in the zone palette (green / yellow / red), faster +
 * crisper as the room gets hotter. All colour-state lives in JS
 * (palette resolves from <html data-theme>); CSS owns layout,
 * typography, and the pulse keyframe. */
.spl-card {
  position: relative;
  padding: 11px 12px 12px;
}
.spl-pulse {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  animation: splCardPulse 2s ease-in-out infinite;
  transition: box-shadow 240ms ease-out;
  will-change: opacity;
  z-index: 2;
}
@keyframes splCardPulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.42; }
}
.spl-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: 8px;
  gap: 8px;
  position: relative;
  z-index: 1;
}
.spl-title-group {
  display: flex;
  align-items: center;
  gap: 7px;
  min-width: 0;
}
.spl-title {
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: 14px;
  letter-spacing: -0.005em;
  white-space: nowrap;
  color: var(--ink);
}
/* Sensor health dot — freshness of the live MQTT feed. Driven by spl.js
   off the age_ms in /api/levels: green = live (<3 s), amber = dropping
   (3–10 s), red = offline (>10 s / LWT). Mirrors the .proc-indicator .dot
   glow. The label only appears when NOT live, so touch users (no hover)
   still see "dropping 5s" / "offline 4m" without a tooltip. */
.spl-health {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  flex: 0 0 auto;
  background: var(--muted);
  cursor: help;
  transition: background .3s ease, box-shadow .3s ease;
}
.spl-health[data-health="live"]  { background: var(--green);  box-shadow: 0 0 6px var(--green); }
.spl-health[data-health="stale"] { background: var(--yellow); box-shadow: 0 0 6px var(--yellow); }
.spl-health[data-health="off"]   { background: var(--red);    box-shadow: 0 0 5px var(--red);
  animation: spl-health-blink 1.5s ease-in-out infinite; }
@keyframes spl-health-blink { 50% { opacity: 0.35; } }
.spl-health-label {
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 600;
  white-space: nowrap;
  letter-spacing: 0.01em;
}
.spl-health-label[data-health="stale"] { color: var(--yellow); }
.spl-health-label[data-health="off"]   { color: var(--red); }
.spl-alerts-toggle {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 10px;
  font-size: 13px;
  color: var(--muted);
  cursor: pointer;
  user-select: none;
}
.spl-alerts-toggle input { cursor: pointer; flex: 0 0 auto; }
.spl-alerts-status { font-size: 12px; opacity: 0.8; margin-left: auto; }
.spl-readout-group {
  display: flex;
  align-items: baseline;
  gap: 6px;
}
.spl-readout {
  font-family: var(--font-mono);
  font-size: 18px;
  font-weight: 600;
  line-height: 1;
  letter-spacing: -0.01em;
  font-variant-numeric: tabular-nums;
  color: var(--ink); /* JS overrides with zone color */
}
.spl-readout-suffix {
  font-family: var(--font-mono);
  font-size: 9px;
  color: var(--ink-3);
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
/* Secondary readout: live median value, coloured to match the median line.
   JS sets the colour + toggles display. */
.spl-readout-median {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.02em;
}
.spl-plot-wrap {
  position: relative;
  z-index: 1;
  width: 100%;
  margin-bottom: 10px;
}
.spl-plot {
  display: block;
  width: 100%;
  height: 160px; /* 138 chart + 6 top + 16 bottom for time labels */
}
.spl-footer {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 10px;
  margin-top: 2px;
  position: relative;
  z-index: 1;
}
.spl-steppers {
  display: grid;
  grid-template-columns: max-content auto 1fr;
  align-items: center;
  gap: 6px 10px;
  flex: 1;
}
.spl-stepper-row {
  display: contents;
}
.spl-footer-label {
  font-family: var(--font-mono);
  font-size: 9px;
  color: var(--ink-3);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  white-space: nowrap;
}
.spl-target-status {
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--ink-3);
  min-width: 12px;
}
.spl-target-status.ok    { color: var(--accent); }
.spl-target-status.error { color: var(--alert); }
.spl-reset-btn {
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 600;
  padding: 5px 10px;
  background: var(--surface-alt);
  color: var(--ink);
  border: 1px solid var(--border);
  border-radius: 6px;
  cursor: pointer;
  letter-spacing: -0.005em;
}
.spl-reset-btn:hover { background: var(--surface); }
.spl-target-input.lufs-error { color: var(--alert); }
.spl-target-input.lufs-dirty { color: var(--ink-3); }

/* Annotation bar — note input + send button + recent-notes list. */
.spl-note-bar {
  display: flex;
  gap: 8px;
  margin-top: 12px;
  position: relative;
  z-index: 1;
}
.spl-note-input {
  flex: 1;
  min-width: 0;
  font-family: var(--font-sans);
  font-size: 13px;
  padding: 8px 10px;
  background: var(--surface-alt);
  color: var(--ink);
  border: 1px solid var(--border);
  border-radius: 6px;
}
.spl-note-input::placeholder { color: var(--ink-3); }
.spl-note-input:focus { outline: none; border-color: var(--accent); }
.spl-note-input.spl-note-error { border-color: var(--alert); }
.spl-note-btn {
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 600;
  padding: 6px 16px;
  background: var(--accent);
  color: var(--accent-ink);
  border: none;
  border-radius: 6px;
  cursor: pointer;
}
.spl-note-btn:hover { filter: brightness(1.05); }
.spl-note-btn:disabled { opacity: 0.5; cursor: default; }
.spl-note-recent {
  margin-top: 8px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  position: relative;
  z-index: 1;
}
.spl-note-recent:empty { display: none; }
.spl-note-item {
  display: flex;
  gap: 8px;
  font-size: 11px;
  align-items: baseline;
}
.spl-note-time {
  font-family: var(--font-mono);
  color: var(--ink-3);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.spl-note-text {
  color: var(--ink-2);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Metric row — per-window live values, dot-separated; tap to pick the
 * metric that drives the readout + plotted trail. */
.spl-metric-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 6px 10px;
  margin: 6px 0 2px;
  position: relative;
  z-index: 1;
  font-family: var(--font-mono);
  font-size: 11px;
}
.spl-metric-windows {
  display: inline-flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 2px 4px;
}
.spl-metric-pick {
  display: inline-flex;
  align-items: baseline;
  gap: 4px;
  background: none;
  border: none;
  padding: 2px 5px;
  border-radius: 4px;
  cursor: pointer;
  color: var(--ink-3);
  font: inherit;
}
.spl-metric-pick:hover { color: var(--ink-2); background: var(--surface-alt); }
.spl-metric-pick.selected { color: var(--accent); }
.spl-metric-name { letter-spacing: 0.04em; }
.spl-metric-pick.selected .spl-metric-name { font-weight: 700; }
.spl-metric-num { font-variant-numeric: tabular-nums; }
.spl-metric-sep { color: var(--ink-3); opacity: 0.45; user-select: none; }

/* Stepped/Smooth segmented toggle. */
.spl-mode-toggle {
  display: inline-flex;
  border: 1px solid var(--border);
  border-radius: 6px;
  overflow: hidden;
}
.spl-mode-btn {
  font-family: var(--font-sans);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.02em;
  padding: 4px 10px;
  background: transparent;
  color: var(--ink-3);
  border: none;
  cursor: pointer;
}
.spl-mode-btn + .spl-mode-btn { border-left: 1px solid var(--border); }
.spl-mode-btn:hover { color: var(--ink-2); }
.spl-mode-btn.selected {
  background: var(--accent);
  color: var(--accent-ink);
}

/* Trail controls cluster (mode toggle + median toggle), right of the windows. */
.spl-trail-controls {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
/* Median overlay toggle — selected state uses the median line's blue so the
   control and the line read as the same thing. */
.spl-median-toggle {
  font-family: var(--font-sans);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.02em;
  padding: 4px 10px;
  background: transparent;
  color: var(--ink-3);
  border: 1px solid var(--border);
  border-radius: 6px;
  cursor: pointer;
}
.spl-median-toggle:hover { color: var(--ink-2); }
.spl-median-toggle.selected {
  background: #2563eb;
  color: #fff;
  border-color: #2563eb;
}
[data-theme="dark"] .spl-median-toggle.selected {
  background: #60a5fa;
  color: #0b1220;
  border-color: #60a5fa;
}

/* (legacy `.stepper-readout` block stays below for miniDSP card.) */
.stepper-readout {
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--ink-2);
  font-variant-numeric: tabular-nums;
  min-width: 36px;
}
.stepper-readout.dirty { color: var(--ink-3); }
.stepper-readout.error { color: var(--alert); }

/* Shared stepper widget. The +/- buttons dispatch input+change events
 * on the wrapped <input> so the per-input handlers (SPL target, miniDSP
 * gain, etc.) react identically to direct keyboard typing. */
.stepper {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px;
  background: var(--surface-alt);
  border: 1px solid var(--border);
  border-radius: 6px;
}
.stepper-down, .stepper-up {
  width: 22px; height: 22px;
  border: none;
  background: transparent;
  color: var(--ink-2);
  font-family: var(--font-mono);
  font-size: 14px;
  cursor: pointer;
  border-radius: 4px;
  padding: 0;
  line-height: 1;
}
.stepper-down:hover, .stepper-up:hover { color: var(--ink); background: var(--surface); }
.stepper-input {
  flex: 1;
  min-width: 0;
  width: 48px;
  text-align: center;
  background: transparent;
  border: none;
  outline: none;
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
  -moz-appearance: textfield;
  appearance: textfield;
}
.stepper-input::-webkit-outer-spin-button,
.stepper-input::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

/* Login view */
.card.login form { display: flex; flex-direction: column; gap: 14px; }
.card.login label { display: flex; flex-direction: column; gap: 4px; font-size: 13px; }
.card.login label span { color: var(--muted); text-transform: uppercase; letter-spacing: 0.5px; }
.card.login input {
  padding: 12px 14px; background: var(--surface-alt);
  border: 1px solid var(--border); border-radius: 8px;
  color: var(--text); font-size: 16px;
}
.card.login input:focus { outline: none; border-color: var(--accent); }
.card.login button { margin-top: 4px; }
.card.login .error {
  background: #3a1010; border: 1px solid var(--red);
  color: #ffd6d6; padding: 10px 12px; border-radius: 6px; margin: 0 0 8px;
}

/* Sign-out link + theme toggle in page header */
header { position: relative; }
header .header-tools {
  position: absolute;
  top: 8px; right: 0;
  display: flex;
  align-items: center;
  gap: 8px;
}
header .signout { display: inline-flex; }
header .signout button {
  background: transparent; border: 1px solid var(--border);
  color: var(--ink-2); padding: 6px 12px; border-radius: 6px;
  font-size: 12px; cursor: pointer;
}
header .signout button:hover { color: var(--ink); border-color: var(--accent); }

.theme-toggle {
  width: 28px; height: 28px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--ink-2);
  cursor: pointer;
  padding: 0;
  font-size: 14px;
  line-height: 1;
}
.theme-toggle:hover { color: var(--ink); border-color: var(--accent); }
.theme-toggle .theme-icon { display: block; }

/* ── Volunteer (AFW) view ─────────────────────────────────────────────── */

.session-metadata .meta-field,
.session-metadata .meta-toggle,
.session-metadata .meta-actions {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 10px 0;
  border-bottom: 1px solid var(--border);
}
.session-metadata > *:last-child { border-bottom: none; }

.session-metadata label { font-weight: 600; font-size: 14px; }
.session-metadata input[type="text"],
.session-metadata textarea {
  background: var(--surface-alt);
  border: 1px solid var(--border);
  color: var(--text);
  padding: 10px 12px;
  border-radius: 6px;
  font-size: 15px;
  font-family: inherit;
  width: 100%;
}
.session-metadata textarea { resize: vertical; min-height: 64px; }

.meta-saved {
  color: var(--green);
  font-size: 12px;
  transition: opacity 240ms ease-in;
}

.switch-row {
  display: flex; justify-content: space-between; align-items: center;
  gap: 12px; padding: 8px 0;
  font-weight: 600; font-size: 14px;
}
.switch-row.inline { display: inline-flex; gap: 8px; padding: 0; font-weight: 500; }
.switch-row.disabled-row { opacity: 0.5; }
.switch-row input[type="checkbox"] {
  width: 18px; height: 18px; flex: none; accent-color: var(--accent);
}

.meta-actions { flex-direction: row; align-items: center; gap: 12px; flex-wrap: wrap; }
.meta-actions button { flex: 0 0 auto; }

.slot-chip {
  display: inline-flex; align-items: center; gap: 8px;
  background: var(--surface-alt); border: 1px solid var(--border);
  padding: 8px 12px; border-radius: 999px;
  font-size: 13px; color: var(--text);
}
.slot-chip .dot {
  width: 10px; height: 10px; border-radius: 50%;
  background: #555; flex: none;
}
.slot-chip .dot.ok { background: var(--green); box-shadow: 0 0 6px var(--green); }

/* Streaming + Recording cards */
.streaming .actions,
.recording .actions { margin-top: 12px; }

.schedule-block,
.recording-shape {
  padding: 12px 0;
  border-bottom: 1px solid var(--border);
}
.schedule-block:last-child,
.recording-shape:last-child { border-bottom: none; }

.schedule-header {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
}
.schedule-header h3,
.recording-shape h3 {
  font-size: 14px; font-weight: 700; margin: 0 8px 0 0;
  text-transform: uppercase; letter-spacing: 0.5px; color: var(--muted);
}

.schedule-fields {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 8px 12px;
  align-items: center;
  margin-top: 10px;
}
.schedule-fields label { font-size: 13px; color: var(--muted); }
.schedule-fields input,
.schedule-fields select {
  background: var(--surface-alt);
  border: 1px solid var(--border);
  color: var(--text);
  padding: 8px 10px;
  border-radius: 6px;
  font-size: 14px;
  font-family: inherit;
}

.recording-shape .audio-sources {
  margin-top: 0;
  padding-top: 0;
}
.audio-sources-label {
  font-size: 13px; color: var(--muted);
  text-transform: uppercase; letter-spacing: 0.5px;
  margin-bottom: 8px;
}
.source-row {
  display: flex; align-items: center; gap: 10px;
  padding: 6px 0; font-size: 14px;
}
.source-row input[type="radio"] {
  width: 18px; height: 18px; accent-color: var(--accent);
}

/* ── Schedule slot widget (shared volunteer + admin) ───────────────── */

.slot-list { display: flex; flex-direction: column; gap: 8px; }

.slot-empty {
  color: var(--muted);
  font-size: 13px;
  font-style: italic;
  padding: 6px 2px;
}

.slot-row {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 12px 14px;
  background: var(--surface-alt);
  border: 1px solid var(--border);
  border-radius: 8px;
}

.slot-active {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-weight: 600;
  font-size: 14px;
  width: max-content;
}
.slot-active input[type="checkbox"] {
  width: 18px; height: 18px;
  accent-color: var(--accent);
}

.slot-days {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}
.day-pill {
  min-width: 34px;
  padding: 6px 8px;
  background: var(--surface-alt);
  border: 1px solid var(--border);
  color: var(--muted);
  border-radius: 6px;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  font-family: inherit;
  user-select: none;
  text-align: center;
}
.day-pill:hover { color: var(--text); }
.day-pill.on {
  background: var(--accent);
  color: var(--accent-ink);
  border-color: var(--accent);
}
.day-pill:active { transform: translateY(1px); }

.slot-times {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.slot-times input[type="time"] {
  background: var(--surface-alt);
  border: 1px solid var(--border);
  color: var(--text);
  padding: 6px 10px;
  border-radius: 6px;
  font-size: 14px;
  font-family: inherit;
  min-width: 7em;
}
.slot-times-sep {
  color: var(--muted);
  font-size: 18px;
}

/* Times + action toggles share one row when the screen has room and
   wrap below on narrow phones. Inside .slot-row (column flex). */
.slot-window {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px 18px;
}
.slot-window .slot-times { flex: 1 1 auto; min-width: 0; }
.slot-window .slot-times input[type="time"] { min-width: 6.5em; }

.slot-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  padding-top: 2px;
}
.slot-action {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
  font-weight: 600;
  color: var(--text);
  cursor: pointer;
  user-select: none;
}
.slot-action input[type="checkbox"] {
  width: 18px; height: 18px;
  accent-color: var(--accent);
}

.slot-remove {
  position: absolute;
  top: 4px; right: 6px;
  background: transparent;
  border: none;
  color: var(--muted);
  font-size: 22px;
  line-height: 1;
  padding: 4px 10px;
  border-radius: 6px;
  cursor: pointer;
  font-family: inherit;
}
.slot-remove:hover {
  color: var(--red);
  background: rgba(255, 90, 90, 0.08);
}

.add-slot-btn {
  background: transparent;
  border: 1px dashed var(--border);
  color: var(--accent);
  padding: 8px 14px;
  border-radius: 8px;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  margin-top: 8px;
  font-family: inherit;
  align-self: flex-start;
}
.add-slot-btn:hover {
  border-color: var(--accent);
  background: rgba(78, 168, 255, 0.08);
}

.schedule-saved.err { color: var(--red); }
.meta-saved.err { color: var(--red); }

/* Main-page Schedule card. <details> sized like any other card; the
   summary renders the next-trigger line and toggles the slot editor. */
.schedule-card { padding: 0; }
.schedule-card > summary {
  list-style: none;
  cursor: pointer;
  padding: 14px 16px;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 6px 10px;
  user-select: none;
}
.schedule-card > summary::-webkit-details-marker { display: none; }
.schedule-card > summary::after {
  content: "▸";
  margin-left: auto;
  color: var(--muted);
  font-size: 14px;
  transition: transform 120ms ease;
}
.schedule-card[open] > summary::after { transform: rotate(90deg); }
.schedule-summary-label {
  font-family: var(--font-mono, monospace);
  font-size: 11px;
  color: var(--ink-3, var(--muted));
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.schedule-summary-when {
  font-size: 15px;
  font-weight: 600;
  color: var(--text);
}
.schedule-body {
  padding: 4px 16px 16px;
  border-top: 1px solid var(--border);
}
.schedule-body .slot-list { margin-top: 12px; }

/* Admin config form: slot_list rows span the full grid width (label on top,
   widget below) instead of the default two-column layout. */
.config-row.config-row-block {
  grid-template-columns: 1fr;
  gap: 8px;
  align-items: stretch;
}

/* ── Camera tweak overlay ────────────────────────────────────────────── */
/* Fullscreen-ish modal the volunteer opens to position the camera.
   Server boosts the idle preview's framerate while the modal is open,
   sliders auto-save to /api/config so the camera responds live. */
.tweak-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.75);
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 12px;
}
.tweak-overlay[hidden] { display: none; }
body.tweak-open { overflow: hidden; }

.tweak-panel {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 12px;
  width: 100%;
  /* Wide enough that a 1280px-native preview JPEG doesn't get
     CSS-downscaled (which would defeat the point of running the preview
     at camera-native resolution during tweak mode). Caps at viewport
     width minus margins so it stays usable on phones too. */
  max-width: min(1280px, calc(100vw - 24px));
  max-height: calc(100vh - 24px);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.tweak-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 16px;
  border-bottom: 1px solid var(--border);
}
.tweak-header h2 { margin: 0; font-size: 16px; color: var(--text); }
.tweak-close {
  background: transparent;
  border: none;
  color: var(--muted);
  font-size: 28px;
  line-height: 1;
  cursor: pointer;
  padding: 0 8px;
}
.tweak-close:hover { color: var(--text); }

.tweak-body {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 12px 16px 16px;
  overflow-y: auto;
}

.tweak-preview-wrap {
  position: relative;
  background: #000;
  border: 1px solid var(--border);
  border-radius: 8px;
  aspect-ratio: 16 / 9;
  overflow: hidden;
}
.tweak-preview-wrap img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
}
.tweak-preview-wrap .placeholder {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--muted);
  font-size: 14px;
}
.tweak-preview-wrap .placeholder[hidden] { display: none; }

.tweak-controls { display: flex; flex-direction: column; gap: 10px; }
.tweak-row {
  display: grid;
  grid-template-columns: 90px 1fr 56px;
  align-items: center;
  gap: 10px;
}
.tweak-row label { color: var(--muted); font-size: 14px; }
.tweak-row input[type="range"] {
  width: 100%;
  accent-color: var(--accent);
}
.tweak-row output {
  font-variant-numeric: tabular-nums;
  font-size: 13px;
  color: var(--text);
  text-align: right;
}

/* Toggle row (Continuous AF checkbox): flex instead of the 3-col grid;
   indent the checkbox to roughly align with where the sliders sit. */
.tweak-row.tweak-row-toggle {
  display: flex;
  align-items: center;
  gap: 12px;
  padding-left: 100px;
}
.tweak-row-toggle label {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: var(--text);
  font-size: 14px;
  cursor: pointer;
}
.tweak-row-toggle input[type="checkbox"] { accent-color: var(--accent); }
.tweak-row-toggle .hint { color: var(--muted); font-size: 12px; }
@media (max-width: 480px) {
  .tweak-row.tweak-row-toggle { padding-left: 0; flex-wrap: wrap; }
}

.tweak-actions {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: 4px;
}
.tweak-actions .big {
  padding: 10px 14px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: transparent;
  color: var(--text);
  font-size: 14px;
  cursor: pointer;
}
.tweak-actions .big:hover { border-color: var(--accent); color: var(--accent); }
.tweak-actions .big:disabled { opacity: 0.45; cursor: not-allowed; }

@media (max-width: 480px) {
  .tweak-row { grid-template-columns: 72px 1fr 48px; gap: 8px; }
  .tweak-header h2 { font-size: 15px; }
}

/* ═══ Unified dashboard layout (B2) ════════════════════════════════════
 * Header + camera hero + Levels + Session + REC/STREAM twins + Metrics
 * strip + Admin drawer. Replaces the previous flat card stack on / .
 */

main { max-width: 520px; padding-bottom: 24px; }

/* — Header — */
header.rig-header {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px 4px 10px;
  margin-bottom: 6px;
}
header.rig-header h1 {
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: 17px;
  letter-spacing: -0.005em;
  margin: 0;
  white-space: nowrap;
}
.rig-runtime {
  flex: 1;
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--ink-2);
  font-variant-numeric: tabular-nums;
  margin-left: 4px;
}
header.rig-header .header-tools {
  position: static;
  margin-left: auto;
}

/* — Camera hero — */
.card.camera-hero { padding: 0; overflow: hidden; }
.camera-hero-frame {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  background: #0a0a0a;
  overflow: hidden;
}
.camera-hero-frame img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.camera-hero-frame .placeholder {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #1a1a1a;
  color: rgba(255, 255, 255, 0.45);
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.08em;
}
/* The `display: flex` above wins over the [hidden] attribute's default
 * `display: none`, so the placeholder would sit on top of the working
 * preview JPEG. Re-honor hidden explicitly. */
.camera-hero-frame .placeholder[hidden] { display: none; }
.hero-state {
  position: absolute;
  top: 8px; left: 8px;
}
.hero-state .badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 9px;
  border-radius: 999px;
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.04em;
  background: var(--surface-alt);
  color: var(--ink-2);
  border: 1px solid var(--border);
}
.hero-state .badge[data-state="live"],
.hero-state .badge[data-state="scheduled_live"] {
  background: var(--alert); color: #fff; border-color: transparent;
}
.hero-state .badge[data-state="starting"],
.hero-state .badge[data-state="scheduled_soon"],
.hero-state .badge[data-state="stopping"] {
  background: var(--ink-2); color: var(--surface); border-color: transparent;
}
.hero-state .badge[data-state="error"] {
  background: var(--alert); color: #fff; border-color: transparent;
}
.hero-slot {
  position: absolute;
  top: 8px; right: 8px;
}
.slot-ready-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px 8px;
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.5);
  color: #fff;
  font-family: var(--font-mono);
  font-size: 10px;
  white-space: nowrap;
  backdrop-filter: blur(4px);
}
.slot-ready-chip .dot {
  width: 5px; height: 5px;
  border-radius: 50%;
  background: var(--accent);
  display: inline-block;
}
.slot-ready-chip[hidden] { display: none; }
.hero-footer {
  position: absolute;
  bottom: 6px; right: 8px;
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.04em;
  color: rgba(255, 255, 255, 0.4);
}
.hero-controls {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 8px 12px;
  flex-wrap: wrap;
  font-size: 13px;
  color: var(--ink-2);
}
.hero-controls .monitor-toggle {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  cursor: pointer;
  user-select: none;
}
.hero-controls .monitor-toggle input[type="checkbox"] {
  width: 16px; height: 16px;
  accent-color: var(--accent);
}
.hero-controls .link-btn { padding: 4px 0; }

/* Live ring on camera hero when state is live/error. */
.camera-hero[data-state="live"] .camera-hero-frame,
.camera-hero[data-state="scheduled_live"] .camera-hero-frame {
  box-shadow: 0 0 0 3px var(--live-ring), 0 0 16px rgba(220, 38, 38, 0.33);
  transition: box-shadow 0.25s ease;
}
.camera-hero[data-state="error"] .camera-hero-frame {
  box-shadow: 0 0 0 3px var(--alert);
}
.camera-hero[data-state="starting"] .camera-hero-frame,
.camera-hero[data-state="scheduled_soon"] .camera-hero-frame,
.camera-hero[data-state="stopping"] .camera-hero-frame {
  box-shadow: 0 0 0 2px var(--warning);
}

/* — Card heads shared by Levels card (and others) — */
.card-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-bottom: 8px;
}
.card-head h2 {
  margin: 0;
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: 13px;
  color: var(--ink);
  text-transform: none;
  letter-spacing: 0;
}
.card-caption {
  font-family: var(--font-mono);
  font-size: 9px;
  color: var(--ink-3);
  letter-spacing: 0.04em;
}

/* — Session card — */
.session-card .card-eyebrow {
  font-family: var(--font-mono);
  font-size: 9px;
  color: var(--ink-3);
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.session-title {
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: 17px;
  letter-spacing: -0.01em;
  line-height: 1.15;
  margin: 4px 0 3px;
}
.session-subtitle {
  font-size: 12px;
  color: var(--ink-2);
  margin: 0;
}
.session-actions {
  display: flex;
  gap: 6px;
  margin-top: 10px;
}
.ghost-btn, .primary-btn {
  flex: 1;
  padding: 8px 10px;
  min-height: 32px;
  border-radius: 8px;
  font-family: var(--font-sans);
  font-size: 12px;
  cursor: pointer;
}
.ghost-btn {
  background: transparent;
  color: var(--ink);
  border: 1px solid var(--divider);
  font-weight: 500;
}
.ghost-btn:hover { border-color: var(--ink-2); }
.primary-btn {
  background: var(--surface-alt);
  color: var(--ink);
  border: 1px solid var(--border);
  font-weight: 600;
}
.primary-btn:hover { border-color: var(--accent); color: var(--accent); }
.primary-btn:disabled { opacity: 0.45; cursor: not-allowed; }

.session-edit {
  margin-top: 10px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.session-edit[hidden] { display: none; }
.session-edit label {
  display: flex;
  flex-direction: column;
  gap: 3px;
  font-size: 12px;
  color: var(--ink-2);
}
.session-edit input[type="text"],
.session-edit textarea {
  background: var(--surface-alt);
  color: var(--ink);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 8px 10px;
  font-family: inherit;
  font-size: 14px;
}
.session-edit textarea { resize: vertical; min-height: 50px; }
.session-public {
  flex-direction: row !important;
  align-items: center;
  gap: 8px !important;
}
.session-public input[type="checkbox"] {
  width: 18px; height: 18px;
  accent-color: var(--accent);
}
.session-edit-actions {
  display: flex;
  gap: 6px;
  margin-top: 2px;
}

/* — REC / STREAM twin cards — */
.twin-cards {
  display: flex;
  gap: 9px;
  margin: 12px 0;
  align-items: stretch;
}
.card.twin-card {
  flex: 1;
  padding: 10px 11px;
  margin: 0;
  display: flex;
  flex-direction: column;
}
.card.twin-card .card-head { margin-bottom: 6px; }
.card.twin-card h2 { font-size: 12px; }
.status-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px 7px;
  border-radius: 999px;
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 500;
  background: transparent;
  color: var(--ink-3);
  border: 1px solid var(--divider);
}
.status-chip .dot {
  width: 5px; height: 5px;
  border-radius: 50%;
  background: var(--ink-3);
  display: inline-block;
}
.twin-card[data-running="1"] .status-chip {
  background: var(--surface-alt);
  color: var(--ink);
}
.twin-card[data-running="1"] .status-chip .dot {
  background: var(--accent);
  box-shadow: 0 0 4px var(--accent);
}
.twin-card .big {
  width: 100%;
  padding: 10px 12px;
  min-height: 42px;
  border-radius: 8px;
  font-family: var(--font-sans);
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  border: 1px solid var(--border);
  background: var(--surface-alt);
  color: var(--ink);
}
.twin-card .big.primary {
  background: var(--accent);
  color: var(--accent-ink);
  border-color: var(--accent);
}
.twin-card .big.danger {
  background: var(--alert);
  color: #fff;
  border-color: var(--alert);
}
.twin-card .big:disabled { opacity: 0.45; cursor: not-allowed; }
.twin-card .twin-sublabel {
  margin-top: 6px;
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--ink-3);
}
.card.twin-card[data-running="1"] {
  box-shadow: 0 0 0 2px var(--live-ring), 0 0 14px rgba(220, 38, 38, 0.25);
  transition: box-shadow 0.25s ease;
}

/* — Sonos outdoor speakers card — */
/* Visually similar to a twin-card but full-width. Playing state lights a
   subtle accent-color ring (no urgency vs the REC/STREAM live ring). */
.card.sonos-card { padding: 12px 14px; }
.card.sonos-card .card-head { margin-bottom: 6px; }
.card.sonos-card h2 { font-size: 12px; }
.card.sonos-card .twin-sublabel {
  margin-top: 6px;
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--ink-3);
}
.card.sonos-card .big { width: 100%; }

/* Two-column layout: play/stop on the left half, − [level] + on the right
   half. Both halves get equal flex space; the right half pushes its
   buttons to the edges with the readout centered between them, so the
   gap inside the half is generous finger-press padding. */
.sonos-controls {
  display: flex;
  align-items: stretch;
  gap: 10px;
}
.sonos-control-action {
  flex: 1 1 50%;
  min-width: 0;
  display: flex;
  align-items: stretch;
}
.sonos-control-action .big { width: 100%; }
/* Outer half is a transparent flex container — it reserves the 50% slot
   so the layout stays symmetric, but doesn't paint any background. The
   inner panel carries the grey border so it visually wraps just the
   buttons + readout, with 22px of transparent space on each side acting
   as the finger-tap padding. */
.sonos-control-volume {
  flex: 1 1 50%;
  display: flex;
  align-items: stretch;
  padding: 0 22px;  /* the 22px tap-padding lives here */
}
.sonos-volume-panel {
  flex: 1 1 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  background: var(--surface-alt);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 0 6px;
  min-height: 42px;  /* match .big's min-height for visual parity */
}
.sonos-vol-btn {
  width: 44px; height: 44px;       /* Apple HIG touch target minimum */
  border: none;
  background: var(--surface);       /* subtle default so it reads as a button */
  color: var(--ink);
  font-family: var(--font-mono);
  font-size: 22px;
  font-weight: 600;
  cursor: pointer;
  border-radius: 8px;
  padding: 0;
  line-height: 1;
  flex-shrink: 0;
}
.sonos-vol-btn:hover { background: var(--card); }
.sonos-vol-btn:active { transform: translateY(1px); }
.sonos-vol-btn:disabled {
  opacity: 0.4;
  cursor: not-allowed;
  background: var(--surface);
}
.sonos-vol-readout {
  font-family: var(--font-mono);
  font-size: 18px;
  font-weight: 600;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
  min-width: 2.2em;
  text-align: center;
}
.card.sonos-card[data-running="1"] .status-chip {
  background: var(--accent);
  color: var(--accent-ink);
}
.card.sonos-card[data-running="1"] .status-chip .dot {
  background: var(--accent-ink);
  box-shadow: 0 0 6px var(--accent-ink);
}
.card.sonos-card[data-running="1"] {
  box-shadow: 0 0 0 1px var(--accent), 0 0 10px rgba(78, 168, 255, 0.18);
  transition: box-shadow 0.25s ease;
}

/* — miniDSP volume card (admin drawer section) — */
/* Two channels (L/R) side-by-side, each with a stepper, dB readout, and
   mute checkbox. A Link toggle below propagates changes across both. The
   card has three visual states: ready (live), disabled, and unreachable —
   set via [data-state] on the container. */
.minidsp-card {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 10px 12px;
  background: var(--surface-alt);
  border: 1px solid var(--border);
  border-radius: 8px;
}
.minidsp-header {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 12px;
  color: var(--ink-2);
}
.minidsp-device {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--ink-2);
}
.minidsp-card[data-state="ready"] .status-chip {
  background: var(--accent);
  color: var(--accent-ink);
}
.minidsp-card[data-state="ready"] .status-chip .dot {
  background: var(--accent-ink);
  box-shadow: 0 0 6px var(--accent-ink);
}
.minidsp-card[data-state="unreachable"] .status-chip {
  background: var(--warning);
  color: var(--accent-ink);
}
.minidsp-card[data-state="disabled"] .status-chip {
  background: var(--surface);
  color: var(--ink-3);
  border: 1px solid var(--border);
}
.minidsp-card[data-state="disabled"] .minidsp-row,
.minidsp-card[data-state="unreachable"] .minidsp-row,
.minidsp-card[data-state="disabled"] .minidsp-channel,
.minidsp-card[data-state="unreachable"] .minidsp-channel,
.minidsp-card[data-state="disabled"] .minidsp-link,
.minidsp-card[data-state="unreachable"] .minidsp-link {
  opacity: 0.5;
  pointer-events: none;
}
/* Preset section in disabled / unreachable states: dim + disable the
   switch buttons (no preset is really loaded when the daemon isn't
   talking to the device), suppress the active highlight on the matching
   note row, but keep the note textareas at full opacity so the operator
   can write notes in advance. */
.minidsp-card[data-state="disabled"] .minidsp-preset-buttons,
.minidsp-card[data-state="unreachable"] .minidsp-preset-buttons {
  opacity: 0.5;
  pointer-events: none;
}
.minidsp-card[data-state="disabled"] .minidsp-preset-btn.active,
.minidsp-card[data-state="unreachable"] .minidsp-preset-btn.active {
  background: var(--surface);
  border-color: var(--border);
  color: var(--ink-2);
}
.minidsp-card[data-state="disabled"] .minidsp-preset-note-row.active .minidsp-preset-note-num,
.minidsp-card[data-state="unreachable"] .minidsp-preset-note-row.active .minidsp-preset-note-num {
  color: var(--ink-3);
  font-weight: normal;
}
.minidsp-card[data-state="disabled"] .minidsp-preset-note-row.active .minidsp-preset-note-textarea,
.minidsp-card[data-state="unreachable"] .minidsp-preset-note-row.active .minidsp-preset-note-textarea {
  border-left-color: var(--border);
}

/* Generic row layout used for the Preset and Master rows above the
   per-channel grid. Label sits on the left, control fills the rest. */
.minidsp-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 4px 0;
}
.minidsp-row-label {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  color: var(--ink-3);
  flex: 0 0 56px;     /* fixed gutter so all rows align */
}
/* Presets section: 4 horizontal switch buttons across the top (active
   one highlighted), then 4 full-width auto-growing note textareas
   below — one per preset, all visible at the same time. Sits at the
   bottom of the card with a divider above it. */
.minidsp-presets-section {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding-top: 8px;
  border-top: 1px solid var(--divider);
}

/* Button row spans the full card width — no left gutter label, since
   the buttons are self-labeling ("Preset 1" etc.) and the section is
   already bounded above by the divider. */
.minidsp-preset-buttons {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 6px;
}
.minidsp-preset-btn {
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 500;
  color: var(--ink-2);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 8px 4px;
  cursor: pointer;
  min-height: 32px;
  text-align: center;
}
.minidsp-preset-btn:hover {
  background: var(--surface-alt);
  color: var(--ink);
}
.minidsp-preset-btn.active {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
}

/* Notes list — one row per preset, tiny number badge on the left for
   visual anchoring back to the button row, and a full-width textarea
   that auto-grows with content so multi-line notes are never clipped. */
.minidsp-preset-notes {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.minidsp-preset-note-row {
  display: flex;
  align-items: flex-start;
  gap: 6px;
  padding: 2px 0;
}
.minidsp-preset-note-num {
  flex: 0 0 16px;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--ink-3);
  padding-top: 7px;            /* visually centers w/ 1st text line */
  text-align: right;
}
.minidsp-preset-note-row.active .minidsp-preset-note-num {
  color: var(--accent);
  font-weight: 600;
}
.minidsp-preset-note-textarea {
  flex: 1 1 auto;
  min-width: 0;
  width: 100%;
  font-family: var(--font-sans);
  font-size: 13px;
  line-height: 1.4;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 5px 8px;
  outline: none;
  resize: none;                /* height controlled by JS auto-grow */
  overflow: hidden;
  /* Browser default for textareas (vertical) breaks line-up with the
     surrounding flex layout. */
  vertical-align: top;
  /* Subtle left accent strip when this row matches the active preset,
     so the textarea visually pairs with the highlighted button above. */
  border-left-width: 3px;
}
.minidsp-preset-note-textarea:focus {
  border-color: var(--accent);
  background: var(--surface);
}
.minidsp-preset-note-textarea::placeholder {
  color: var(--ink-3);
}
.minidsp-preset-note-row.active .minidsp-preset-note-textarea {
  border-left-color: var(--accent);
}

.minidsp-preset-note-status {
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--ink-3);
  min-height: 12px;
}

.minidsp-channels {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px;
}
.minidsp-channel {
  display: flex;
  flex-direction: column;
  gap: 8px;
  align-items: stretch;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 10px;
}
.minidsp-channel-label {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  color: var(--ink-3);
}
.minidsp-channel .stepper {
  width: 100%;
}
.minidsp-channel .stepper-input {
  flex: 1 1 auto;
}
.minidsp-channel .stepper-readout {
  font-family: var(--font-mono);
  font-size: 13px;
  text-align: center;
  font-variant-numeric: tabular-nums;
}
.minidsp-mute {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  color: var(--ink);
  cursor: pointer;
}
.minidsp-link {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  color: var(--ink);
  cursor: pointer;
  padding: 6px 0 0;
  border-top: 1px solid var(--divider);
}

/* — Right-speaker volunteer card (main page) — */
/* Single-feature subset of the admin miniDSP card so the volunteer at
   the café can soften the right speaker (behind the cash register) when
   a worker complains. Visual weight matches the Sonos card. */
.card.right-speaker-card { padding: 12px 14px; }
.card.right-speaker-card .card-head { margin-bottom: 4px; }
.card.right-speaker-card h2 { font-size: 12px; }
.right-speaker-sublabel {
  margin: 0 0 10px;
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--ink-3);
}
.right-speaker-controls {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
}
.rs-vol-btn {
  width: 56px;
  height: 56px;             /* big touch target — primary volunteer action */
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--ink);
  font-family: var(--font-mono);
  font-size: 26px;
  font-weight: 600;
  cursor: pointer;
  border-radius: 10px;
  padding: 0;
  line-height: 1;
}
.rs-vol-btn:hover { background: var(--surface-alt); }
.rs-vol-btn:active { transform: translateY(1px); }
.rs-vol-btn:disabled {
  opacity: 0.4;
  cursor: not-allowed;
  background: var(--surface);
}
.rs-readout {
  font-family: var(--font-mono);
  font-size: 20px;
  font-weight: 600;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
  min-width: 4em;
  text-align: center;
}
.rs-reset-btn {
  margin-left: 6px;
  padding: 8px 14px;
  font-family: var(--font-sans);
  font-size: 13px;
  color: var(--ink-2);
  background: var(--surface-alt);
  border: 1px solid var(--border);
  border-radius: 8px;
  cursor: pointer;
}
.rs-reset-btn:hover { color: var(--ink); background: var(--surface); }
.rs-reset-btn:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}
/* Chip color reflects "is this speaker currently attenuated?" — neutral
   when at 0 (normal), accent-colored when softened so the volunteer
   notices on a quick glance. */
.card.right-speaker-card[data-state="normal"] .status-chip {
  background: var(--surface);
  border: 1px solid var(--border);
  color: var(--ink-2);
}
.card.right-speaker-card[data-state="normal"] .status-chip .dot {
  background: var(--ink-3);
}
.card.right-speaker-card[data-state="softened"] .status-chip {
  background: var(--warning);
  color: var(--accent-ink);
  border-color: var(--warning);
}
.card.right-speaker-card[data-state="softened"] .status-chip .dot {
  background: var(--accent-ink);
  box-shadow: 0 0 6px var(--accent-ink);
}
.card.right-speaker-card[data-state="softened"] {
  box-shadow: 0 0 0 1px var(--warning), 0 0 10px rgba(202, 138, 4, 0.2);
}

/* — Master volume card (main-page, volunteer-facing) — */
.card.master-volume-card { padding: 12px 14px; }
.card.master-volume-card .card-head { margin-bottom: 6px; }
.card.master-volume-card h2 { font-size: 12px; }
.master-volume-controls {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
}
.mv-vol-btn {
  width: 56px;
  height: 56px;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--ink);
  font-family: var(--font-mono);
  font-size: 26px;
  font-weight: 600;
  cursor: pointer;
  border-radius: 10px;
  padding: 0;
  line-height: 1;
}
.mv-vol-btn:hover { background: var(--surface-alt); }
.mv-vol-btn:active { transform: translateY(1px); }
.mv-vol-btn:disabled {
  opacity: 0.4;
  cursor: not-allowed;
  background: var(--surface);
}
.mv-readouts {
  display: flex;
  flex-direction: column;
  align-items: center;
  min-width: 6em;
}
.mv-readout {
  font-family: var(--font-mono);
  font-size: 22px;
  font-weight: 700;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
}
.mv-target {
  margin-top: 2px;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--ink-3);
  font-variant-numeric: tabular-nums;
}
.mv-reset-btn {
  margin-left: 6px;
  padding: 8px 14px;
  font-family: var(--font-sans);
  font-size: 13px;
  color: var(--ink-2);
  background: var(--surface-alt);
  border: 1px solid var(--border);
  border-radius: 8px;
  cursor: pointer;
}
.mv-reset-btn:hover { color: var(--ink); background: var(--surface); }

/* Status chip: neutral when on-target, alert-red when off. */
.card.master-volume-card[data-off-default="0"] .status-chip {
  background: var(--surface);
  border: 1px solid var(--border);
  color: var(--ink-2);
}
.card.master-volume-card[data-off-default="0"] .status-chip .dot {
  background: var(--ink-3);
}
.card.master-volume-card[data-off-default="1"] .status-chip {
  background: var(--alert);
  color: #fff;
  border-color: var(--alert);
}
.card.master-volume-card[data-off-default="1"] .status-chip .dot {
  background: #fff;
  box-shadow: 0 0 6px #fff;
}
/* Off-default: readout turns alert-red and the whole card pulses so a
   volunteer notices at a glance from across the cafe. Pulse stops the
   moment current == default. */
.card.master-volume-card[data-off-default="1"] .mv-readout {
  color: var(--alert);
}
.card.master-volume-card[data-off-default="1"] {
  animation: mvPulse 1.4s ease-in-out infinite;
}
@keyframes mvPulse {
  0%, 100% {
    box-shadow: 0 0 0 1px var(--alert),
                0 0 0 rgba(220, 38, 38, 0.0);
  }
  50% {
    box-shadow: 0 0 0 1px var(--alert),
                0 0 14px rgba(220, 38, 38, 0.55);
  }
}
@media (prefers-reduced-motion: reduce) {
  .card.master-volume-card[data-off-default="1"] {
    animation: none;
    box-shadow: 0 0 0 1px var(--alert),
                0 0 12px rgba(220, 38, 38, 0.45);
  }
}

/* — Process indicators row (slim debug row) — */
.proc-row {
  display: flex;
  justify-content: center;
  gap: 14px;
  margin: 6px 0 8px;
  flex-wrap: wrap;
  font-size: 11px;
}

/* — Metrics footer strip (always visible) — */
.card.metrics-strip {
  padding: 10px 12px;
  margin: 12px 0;
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 8px;
  align-items: center;
}
.card.metrics-strip .metric {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
}
.card.metrics-strip .metric .label {
  font-family: var(--font-mono);
  font-size: 9px;
  color: var(--ink-3);
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
.card.metrics-strip .metric .value {
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.card.metrics-strip .metrics-controls {
  grid-column: 1 / -1;
  border-top: 1px solid var(--divider);
  margin-top: 4px;
  padding-top: 6px;
}
@media (max-width: 480px) {
  .card.metrics-strip { grid-template-columns: repeat(3, 1fr); }
}

/* — Admin drawer — */
.admin-drawer {
  margin-top: 14px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--surface-alt);
  overflow: hidden;
}
.admin-drawer > summary {
  padding: 10px 14px;
  cursor: pointer;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--ink-2);
  list-style: none;
  display: flex;
  align-items: center;
  gap: 8px;
  user-select: none;
  letter-spacing: 0.04em;
}
.admin-drawer > summary::-webkit-details-marker { display: none; }
.admin-drawer > summary .drawer-icon {
  transition: transform 0.12s ease;
  display: inline-block;
}
.admin-drawer[open] > summary .drawer-icon { transform: rotate(90deg); }
.admin-drawer > summary:hover { color: var(--ink); }
.admin-drawer[open] > summary {
  border-bottom: 1px solid var(--border);
  color: var(--ink-3);
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
.admin-drawer-body {
  padding: 6px 10px 10px;
  background: var(--surface);
}

/* Each section inside the drawer is its own collapsible. */
.drawer-section {
  border-bottom: 1px solid var(--divider);
  padding: 2px 0;
}
.drawer-section:last-child { border-bottom: none; }
.drawer-section > summary {
  padding: 8px 4px;
  cursor: pointer;
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 500;
  list-style: none;
  display: flex;
  align-items: center;
  gap: 8px;
}
.drawer-section > summary::-webkit-details-marker { display: none; }
.drawer-section > summary::before {
  content: "→";
  color: var(--ink-3);
  font-size: 12px;
  margin-left: auto;
  order: 99;
  transition: transform 0.12s ease;
}
.drawer-section[open] > summary::before { transform: rotate(90deg); }
.drawer-section-body {
  padding: 4px 4px 12px;
}

/* Composite-controls grid inside drawer */
.composite-actions { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }

/* Hide the legacy .preview rules that targeted the old .card.preview img.
 * The new .camera-hero rules handle this; flagged for B7 cleanup. */

/* ─────────────────────────────────────────────────────────────────────
 * DJ-mode theme-pick overlay
 *
 * Full-screen on page load, dismisses when the user taps a tile. Dual
 * purpose: pick light/dark and supply NoSleep.js the user gesture it
 * needs to start the keep-awake video. Visual chrome is intentionally
 * dark — the page underneath defaults to dark anyway so a light flash
 * would be jarring.
 * ───────────────────────────────────────────────────────────────────── */
.dj-theme-prompt {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(8, 9, 11, 0.92);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  padding: 24px;
  box-sizing: border-box;
  transition: opacity 220ms ease-out;
}
.dj-theme-prompt-hidden {
  opacity: 0;
  pointer-events: none;
}
.dj-theme-prompt-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 18px;
  max-width: 640px;
  width: 100%;
  color: #ededee;
  text-align: center;
}
.dj-theme-prompt-title {
  font-family: var(--font-sans);
  font-size: 28px;
  font-weight: 600;
  letter-spacing: -0.01em;
  margin: 0;
  color: #ededee;
}
.dj-theme-prompt-hint {
  font-family: var(--font-sans);
  font-size: 14px;
  color: #9a9aa0;
  margin: 0 0 8px;
}
.dj-theme-prompt-tiles {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px;
  width: 100%;
}
.dj-theme-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 32px 18px;
  border-radius: 16px;
  border: 1px solid #34363d;
  background: #1c1d22;
  color: #ededee;
  font-family: var(--font-sans);
  font-size: 18px;
  font-weight: 600;
  cursor: pointer;
  transition: transform 120ms ease-out, border-color 120ms ease-out, background 120ms ease-out;
  -webkit-tap-highlight-color: transparent;
}
.dj-theme-tile:hover { border-color: #22c55e; }
.dj-theme-tile:active { transform: scale(0.97); }
.dj-theme-tile-light {
  background: #fafafa;
  color: #0a0a0a;
  border-color: #e5e5e5;
}
.dj-theme-tile-light:hover { border-color: #16a34a; }
.dj-theme-tile-icon {
  font-size: 56px;
  line-height: 1;
}
.dj-theme-tile-label {
  font-size: 18px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

/* ─────────────────────────────────────────────────────────────────────
 * DJ-mode view (full-bleed SPL plot for a 9" iPad in the booth)
 *
 * Strips the dashboard down to a single card filling the viewport.
 * Title top-left, theme toggle + sign-out top-right, plot fills the
 * rest. Targeted at landscape iPad (~1180×820) but degrades to
 * portrait + smaller screens via flexbox. spl.js reads
 * .spl-plot-wrap.clientHeight at render time + scales the chart's
 * cosmetic elements (danger-band thickness, marker, line widths,
 * time-label font) sublinearly so the same plot reads clearly across
 * everything from phone (≈300 px) to landscape iPad (~700 px).
 * ───────────────────────────────────────────────────────────────────── */
body[data-view="dj"] {
  padding: 0;
  margin: 0;
  height: 100vh;
  height: 100dvh; /* iOS Safari address bar safe */
  overflow: hidden;
  background: var(--bg);
}
body[data-view="dj"] main {
  max-width: none;
  margin: 0;
  height: 100%;
  padding: 0;
}
.dj-shell {
  height: 100%;
  display: flex;
  /* Safe-area insets keep our chrome clear of the iOS status bar / home
   * indicator when the page is launched in standalone mode via Add to
   * Home Screen. In a normal Safari tab the insets are 0 so this just
   * reads as the baseline 12 px padding. */
  padding:
    max(12px, env(safe-area-inset-top))
    max(12px, env(safe-area-inset-right))
    max(12px, env(safe-area-inset-bottom))
    max(12px, env(safe-area-inset-left));
  box-sizing: border-box;
}
.dj-spl-card {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  margin: 0;
  padding: 14px 18px 12px;
  border-radius: 12px;
  min-height: 0; /* allow children with min-height to shrink */
}
.dj-spl-header {
  /* Override the base .spl-header bottom margin so the plot can swell. */
  margin-bottom: 6px;
}
.dj-spl-header .spl-title {
  font-size: clamp(15px, 1.6vw, 22px);
}
.dj-header-tools {
  display: flex;
  align-items: center;
  gap: 8px;
}
.dj-header-tools .signout { display: inline-flex; }
.dj-header-tools .signout button.dj-close {
  width: 32px;
  height: 32px;
  background: var(--surface);
  border: 1px solid var(--border);
  color: var(--ink-2);
  border-radius: 6px;
  font-size: 22px;
  line-height: 1;
  cursor: pointer;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.dj-header-tools .signout button.dj-close:hover {
  color: var(--ink);
  border-color: var(--accent);
}
.dj-header-tools .theme-toggle {
  width: 32px;
  height: 32px;
  font-size: 16px;
}
/* Reload + Reset trail icon buttons — same chrome as the close button
 * so the header reads as a single icon cluster. */
.dj-header-tools .dj-tool-btn {
  width: 32px;
  height: 32px;
  background: var(--surface);
  border: 1px solid var(--border);
  color: var(--ink-2);
  border-radius: 6px;
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.dj-header-tools .dj-tool-btn:hover {
  color: var(--ink);
  border-color: var(--accent);
}
.dj-header-tools .dj-tool-btn:active {
  transform: scale(0.96);
}

/* ─────────────────────────────────────────────────────────────────────
 * DJ-mode dim wall clock (behind the plot)
 *
 * Big hh:mm readout positioned absolutely at the top of the plot wrap
 * with low opacity. SPL plot's SVG is on top (z-index 1 of the wrap);
 * spl.js skips its opaque background fill in DJ mode so the clock
 * shows through wherever the trail isn't.
 * ───────────────────────────────────────────────────────────────────── */
.dj-clock {
  position: absolute;
  /* top + left + font-size + height are set by spl.js each render
   * frame: top + height bind to the warnHi band, left binds to the
   * steady-state midpoint of the Current zone. The fallback values
   * below are only used for the first paint, before the render loop
   * fires; values get overwritten ~16 ms later. */
  top: 18%;
  left: 75%;
  transform: translateX(-50%);
  z-index: 0;
  font-family: var(--font-mono);
  font-size: clamp(60px, 10vh, 140px);
  font-weight: 600;
  line-height: 1;
  letter-spacing: -0.04em;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  opacity: 0.16;
  pointer-events: none;
  white-space: nowrap;
  user-select: none;
}

/* ─────────────────────────────────────────────────────────────────────
 * DJ-mode bottom tool row
 *
 * Three-column grid: [reload | theme] in the left cluster, title in
 * the centre, [reset | sign-out] in the right cluster. Buttons sized
 * for cross-desk tapping (~64 px target, ~72 px on a roomy iPad).
 * ───────────────────────────────────────────────────────────────────── */
.dj-bottom-bar {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 16px;
  /* More breathing room above the screen edge — buttons sit further
   * up so a thumb reach from below has clearance. */
  margin-top: 24px;
  padding: 8px 8px 24px;
  position: relative;
  z-index: 1;
}
.dj-bottom-cluster {
  display: flex;
  align-items: center;
  gap: 14px;
}
.dj-bottom-left  { justify-self: start; }
.dj-bottom-right { justify-self: end; }
.dj-bottom-center {
  justify-self: center;
  text-align: center;
}
.dj-bottom-title {
  font-family: var(--font-sans);
  font-weight: 500;
  /* Bigger than the old 14 px header title — readable across a desk —
   * but dimmer (ink-3) per the redesign brief: "increase text size,
   * but reduce contrast". */
  font-size: clamp(22px, 2.6vw, 36px);
  color: var(--ink-3);
  letter-spacing: 0.01em;
  white-space: nowrap;
}
.dj-bottom-btn {
  /* Bottom buttons are 50% bigger than the previous round (which were
   * already 2× the original header icons) — clamp(84..114) up from
   * clamp(56..76). Easy to hit from across a desk; not so big they
   * dominate the bar. */
  min-width: clamp(84px, 9vw, 114px);
  height: clamp(84px, 9vw, 114px);
  padding: 0 22px;
  background: var(--surface);
  border: 1px solid var(--border);
  color: var(--ink-2);
  border-radius: 18px;
  font-family: var(--font-sans);
  /* Icon font-size doubled — ⟳/☾/× now read as solid icons rather than
   * tiny glyphs in a big tile. Text-labelled buttons (Restart) use
   * .dj-bottom-reset's smaller, weight-matched size below. */
  font-size: clamp(44px, 4vw, 56px);
  line-height: 1;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  -webkit-tap-highlight-color: transparent;
  transition: transform 100ms ease-out, border-color 120ms ease-out, color 120ms ease-out;
}
.dj-bottom-btn:hover { color: var(--ink); border-color: var(--accent); }
.dj-bottom-btn:active { transform: scale(0.96); }
/* Restart is text-labelled — scale-up of the prior 22–28 size to fit
 * the bigger button, but smaller than the doubled icon glyphs above
 * so the word reads as text rather than a logo. */
.dj-bottom-reset {
  font-size: clamp(30px, 2.8vw, 40px);
  font-weight: 600;
  letter-spacing: 0.02em;
}
.dj-bottom-signout { display: inline-flex; }
/* Close (×) glyph specifically — was clamp(28..36); doubled to
 * clamp(56..72) to match the rest of the icon cluster. */
.dj-bottom-close { font-size: clamp(56px, 4.8vw, 72px); }
/* Reload (⟳) glyph is visually lighter than ☾ / × so it carries the
 * old +25 % bump on top of the 2× — final size clamp(56..70). */
.dj-bottom-reload { font-size: clamp(56px, 5vw, 70px); }
/* The plot's wrap and SVG must both fill remaining vertical space —
 * spl.js reads wrap.clientHeight at render time to compute chartH. */
.dj-spl-plot-wrap {
  flex: 1 1 auto;
  min-height: 0;
  width: 100%;
  margin-bottom: 0;
}
.dj-spl-plot {
  width: 100%;
  height: 100%;
  display: block;
  /* The SVG is later in document order than .dj-clock, but without an
   * explicit z-index the absolute-positioned clock would paint on top
   * (positioned > static at the same layer). Promote the SVG to its
   * own positioned layer so it sits above the clock. */
  position: relative;
  z-index: 1;
}
/* Pulse border looks anemic at the DJ scale — widen it. spl.js still
 * owns the inset shadow (animation lives on the .spl-pulse overlay).
 * This is a visual-only nudge, no behaviour change. */
body[data-view="dj"] .spl-pulse { border-radius: inherit; }
@media (max-aspect-ratio: 3/4) {
  /* Portrait iPhone/iPad: drop the side padding so we get every pixel.
   * Still honor safe-area insets so the status bar / home indicator
   * never overlaps content in standalone mode. */
  .dj-shell {
    padding:
      max(6px, env(safe-area-inset-top))
      max(6px, env(safe-area-inset-right))
      max(6px, env(safe-area-inset-bottom))
      max(6px, env(safe-area-inset-left));
  }
  .dj-spl-card { padding: 10px 12px 8px; }
}

/* Footer gear glyph — lighter than the ⟳/× icons, so a touch smaller
 * than the doubled-icon size to read as a matched-weight icon. */
.dj-bottom-settings { font-size: clamp(46px, 4.4vw, 60px); }

/* ─────────────────────────────────────────────────────────────────────
 * DJ-mode settings popup (opened by the footer gear)
 *
 * Centered modal over a blurred backdrop. Theme-aware (unlike the
 * load-time theme-pick overlay, which is always dark) since the DJ has
 * already committed a theme by the time they open this. Controls inside
 * are wired in spl.js; show/hide in dj.js.
 * ───────────────────────────────────────────────────────────────────── */
.dj-settings-overlay {
  position: fixed;
  inset: 0;
  z-index: 1100;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  box-sizing: border-box;
  background: rgba(8, 9, 11, 0.72);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  transition: opacity 180ms ease-out;
}
.dj-settings-overlay[hidden] { display: none; }
.dj-settings-hidden {
  opacity: 0;
  pointer-events: none;
}
.dj-settings-card {
  width: 100%;
  max-width: 460px;
  max-height: 86vh;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 16px;
  padding: 16px 20px 22px;
  box-sizing: border-box;
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.45);
}
.dj-settings-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 6px;
}
.dj-settings-title {
  margin: 0;
  font-family: var(--font-sans);
  font-size: 20px;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--ink);
}
.dj-settings-close {
  flex: 0 0 auto;
  width: 40px;
  height: 40px;
  background: transparent;
  border: 1px solid var(--border);
  border-radius: 10px;
  color: var(--ink-2);
  font-size: 26px;
  line-height: 1;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  -webkit-tap-highlight-color: transparent;
  transition: color 120ms ease-out, border-color 120ms ease-out;
}
.dj-settings-close:hover { color: var(--ink); border-color: var(--accent); }
.dj-settings-close:active { transform: scale(0.96); }
.dj-settings-section {
  padding: 14px 0;
  border-top: 1px solid var(--border);
}
.dj-settings-section:first-child {
  border-top: none;
  padding-top: 6px;
}
.dj-settings-label {
  margin: 0 0 2px;
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-2);
}
.dj-settings-hint {
  margin: 0 0 10px;
  font-family: var(--font-sans);
  font-size: 13px;
  color: var(--ink-3);
}
.dj-settings-streams {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.dj-stream-check {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 11px 14px;
  border-radius: 10px;
  border: 1px solid var(--border);
  background: var(--bg);
  cursor: pointer;
  font-family: var(--font-sans);
  -webkit-tap-highlight-color: transparent;
  transition: border-color 120ms ease-out;
}
.dj-stream-check:hover { border-color: var(--accent); }
.dj-stream-check input[type="checkbox"] {
  flex: 0 0 auto;
  width: 22px;
  height: 22px;
  margin: 0;
  accent-color: var(--accent);
  cursor: pointer;
}
.dj-stream-name {
  flex: 1 1 auto;
  font-size: 16px;
  color: var(--ink);
}
.dj-stream-sub {
  font-size: 12px;
  color: var(--ink-3);
  letter-spacing: 0.02em;
}
/* lcpeak is a different kind of overlay (peak markers, not a line) — set it
   off from the LAeq window rows with a little extra top gap + a divider. */
.dj-stream-check-peak {
  margin-top: 8px;
  position: relative;
}
.dj-stream-check-peak::before {
  content: "";
  position: absolute;
  left: 12px;
  right: 12px;
  top: -5px;
  border-top: 1px dashed var(--border);
}
.dj-stream-val {
  flex: 0 0 auto;
  min-width: 3.5ch;
  text-align: right;
  font-family: var(--font-mono);
  font-size: 14px;
  font-variant-numeric: tabular-nums;
  color: var(--ink-3);
}
.dj-settings-select {
  width: 100%;
  padding: 12px 14px;
  border-radius: 10px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: 16px;
  -webkit-appearance: none;
  appearance: none;
  cursor: pointer;
}
.dj-settings-select:focus { outline: none; border-color: var(--accent); }
.dj-settings-note {
  margin: 8px 0 0;
  font-family: var(--font-sans);
  font-size: 12px;
  color: var(--ink-3);
}
.dj-settings-theme {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 12px 16px;
  border-radius: 10px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: 15px;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: border-color 120ms ease-out;
}
.dj-settings-theme:hover { border-color: var(--accent); }
.dj-settings-theme:active { transform: scale(0.98); }
.dj-settings-theme .theme-icon { font-size: 20px; line-height: 1; }

/* — Streaming Offset Gain card — */
/* Post-gain loudness plot (10 s + 60 s rolling) vs the fixed -14 LUFS stream
   target, plus the offset-gain stepper. Renderer: stream-level.js. Reuses the
   shared .stepper / .spl-target-status styling for the gain control. */
.card.sog-card { padding: 12px 14px; }
.card.sog-card .card-head { margin-bottom: 6px; }
.card.sog-card h2 { font-size: 12px; }

.sog-legend {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 5px 14px;
  margin-bottom: 8px;
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--ink-3);
}
.sog-key { display: inline-flex; align-items: center; gap: 5px; }
.sog-key i {
  display: inline-block;
  width: 12px; height: 3px;
  border-radius: 2px;
}
.sog-key-10 i { background: #f59e0b; }
.sog-key-60 i { background: #38bdf8; }
.sog-key-target i {
  height: 0;
  border-radius: 0;
  border-top: 2px dashed var(--accent);
}
.sog-key b {
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.sog-key-10 b { color: #f59e0b; }
.sog-key-60 b { color: #38bdf8; }

.sog-plot-wrap {
  position: relative;
  width: 100%;
  height: 150px;
  margin-bottom: 10px;
}
.sog-canvas { display: block; width: 100%; height: 100%; }

.sog-footer {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}
.sog-footer-label {
  font-family: var(--font-mono);
  font-size: 9px;
  color: var(--ink-3);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  white-space: nowrap;
}
.sog-unit {
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--ink-3);
}
.sog-sublabel {
  margin-top: 8px;
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--ink-3);
  line-height: 1.4;
}

/* live / idle chip (data-live set by stream-level.js) */
.card.sog-card[data-live="1"] .status-chip {
  background: var(--surface-alt);
  color: var(--ink);
}
.card.sog-card[data-live="1"] .status-chip .dot {
  background: var(--accent);
  box-shadow: 0 0 4px var(--accent);
}
.card.sog-card[data-live="0"] .sog-plot-wrap { opacity: 0.9; }

/* — Rec Out L/R level lines (under the camera feed) — */
/* Two thin horizontal bars showing the live mixer-feed peak (post-gain dBFS).
   Track carries a dim always-visible zone gradient (the "printed scale");
   the fill reveals the lit left portion. Painted by recout-meter.js. */
.recout-meter {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 8px 2px 0;
  /* Zone gradient: green to -6 dBFS, amber -6..0, red 0..+3.
     Stops match recout-meter.js's -60..+3 scale (85.71% = -6, 95.24% = 0). */
  --recout-gradient: linear-gradient(to right,
    var(--accent) 0%, var(--accent) 85.71%,
    var(--warning) 85.71%, var(--warning) 95.24%,
    var(--alert) 95.24%, var(--alert) 100%);
}
.recout-label {
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-3);
  white-space: nowrap;
}
.recout-bars { flex: 1; display: flex; flex-direction: column; gap: 3px; }
.recout-row { display: flex; align-items: center; gap: 6px; }
.recout-ch {
  font-family: var(--font-mono);
  font-size: 9px;
  color: var(--ink-3);
  width: 8px;
  text-align: center;
}
.recout-track {
  position: relative;
  flex: 1;
  height: 5px;
  border-radius: 3px;
  overflow: hidden;
  background: var(--surface-alt);
}
.recout-track::before {        /* dim always-visible zone scale */
  content: "";
  position: absolute;
  inset: 0;
  opacity: 0.22;
  background: var(--recout-gradient);
}
.recout-fill {
  position: absolute;
  inset: 0;
  background: var(--recout-gradient);
  clip-path: inset(0 100% 0 0); /* JS sets the right inset to the level */
  transition: clip-path 90ms linear;
}
.recout-db {
  font-family: var(--font-mono);
  font-size: 9px;
  color: var(--ink-3);
  width: 22px;
  text-align: right;
  font-variant-numeric: tabular-nums;
}


