:root {
  --bg: #0a0a0c;
  --bg-card: rgba(255, 255, 255, 0.045);
  --bg-card-hover: rgba(255, 255, 255, 0.07);
  --border: rgba(255, 255, 255, 0.09);
  --text: #fafafa;
  --text-soft: rgba(255, 255, 255, 0.62);
  --text-faint: rgba(255, 255, 255, 0.38);
  --accent: #34d399;
  --accent-2: #22d3ee;
  --warn: #f59e0b;
  --err: #ef4444;
  --radius: 14px;
}

html,
body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--text);
  font-family: 'Plus Jakarta Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  min-height: 100vh;
  min-height: 100dvh;
  overscroll-behavior: none;
}

* {
  box-sizing: border-box;
}

button,
input,
textarea {
  font-family: inherit;
  font-size: inherit;
  color: inherit;
}

.hidden {
  display: none !important;
}

/* ── PIN gate ────────────────────────────────────────────────────── */

.gate {
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background: radial-gradient(ellipse 60% 40% at 50% 30%, rgba(56, 189, 248, 0.06), transparent 70%);
}

.gate-card {
  width: 100%;
  max-width: 360px;
  padding: 32px 28px 28px;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 22px;
  text-align: center;
  backdrop-filter: blur(12px);
}

.gate-card .logo {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  background: linear-gradient(135deg, var(--accent), var(--accent-2));
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  margin-bottom: 16px;
}

.gate-card h1 {
  font-size: 26px;
  font-weight: 800;
  letter-spacing: -0.01em;
  margin: 0 0 8px;
}

.gate-card p {
  margin: 0 0 24px;
  color: var(--text-soft);
  font-size: 14px;
}

#pin-form {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

#pin-input {
  width: 100%;
  padding: 18px 20px;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid var(--border);
  border-radius: 14px;
  font-size: 22px;
  letter-spacing: 0.4em;
  text-align: center;
  outline: none;
  transition: border-color 160ms;
}

#pin-input:focus {
  border-color: rgba(52, 211, 153, 0.5);
  background: rgba(52, 211, 153, 0.06);
}

#pin-form button {
  padding: 14px 20px;
  background: linear-gradient(135deg, var(--accent), var(--accent-2));
  color: #0a0a0c;
  border: 0;
  border-radius: 12px;
  font-weight: 700;
  letter-spacing: 0.04em;
  cursor: pointer;
}

.error {
  margin-top: 12px;
  color: var(--err);
  font-size: 13px;
  min-height: 18px;
}

/* ── Panel + shell ───────────────────────────────────────────────── */

.panel {
  max-width: 760px;
  margin: 0 auto;
  padding: 0;
}

.shell {
  display: flex;
  flex-direction: column;
  /* Padding-bottom reserves space for the fixed bottom-nav on mobile. */
  min-height: 100vh;
  min-height: 100dvh;
}

.topbar {
  position: sticky;
  top: 0;
  z-index: 40;
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: calc(env(safe-area-inset-top, 0) + 10px) 14px 10px;
  background: linear-gradient(
    180deg,
    rgba(10, 10, 12, 0.94) 0%,
    rgba(10, 10, 12, 0.78) 80%,
    rgba(10, 10, 12, 0) 100%
  );
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
}

.brand {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.brand-mark {
  font-weight: 800;
  font-size: 15px;
  background: linear-gradient(135deg, var(--accent), var(--accent-2));
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  letter-spacing: -0.01em;
}

.brand-sub {
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--text-faint);
}

.topbar-actions {
  display: flex;
  gap: 6px;
}

.ghost {
  background: transparent;
  border: 1px solid var(--border);
  color: var(--text-soft);
  border-radius: 10px;
  padding: 8px 12px;
  cursor: pointer;
  font-size: 14px;
  transition: background 120ms, color 120ms;
}

.ghost:hover {
  background: var(--bg-card-hover);
  color: var(--text);
}

.ghost.danger:hover,
.ghost.danger:active {
  color: var(--err);
  border-color: rgba(239, 68, 68, 0.55);
  background: rgba(239, 68, 68, 0.08);
}

/* Even when the card is locked, the delete button must remain tappable —
   we never want a stuck "phantom" TV that you can't remove. The lock
   only gates scene/param writes; deletion is always allowed. */
.card.tv.locked .head-actions,
.card.tv.locked .head-actions * {
  pointer-events: auto;
  opacity: 1;
}

.tvs {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.card {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 16px;
}

.card.tv {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.tv-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.tv-id {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}

.dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.18);
  flex-shrink: 0;
  transition: background 200ms, box-shadow 200ms;
}

.dot.online {
  background: var(--accent);
  box-shadow: 0 0 10px rgba(52, 211, 153, 0.5);
}

.dot.stale {
  background: var(--warn);
  box-shadow: 0 0 10px rgba(245, 158, 11, 0.4);
}

.dot.offline {
  background: rgba(255, 255, 255, 0.18);
}

.tv-id .name {
  font-weight: 700;
  font-size: 16px;
  letter-spacing: -0.01em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.tv-id .scene {
  font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, monospace;
  font-size: 10px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--text-faint);
  padding: 3px 8px;
  border: 1px solid var(--border);
  border-radius: 6px;
}

.lock-badge {
  font-size: 12px;
  line-height: 1;
}

.head-actions {
  display: flex;
  gap: 6px;
}

.lock-btn:not(.util-btn) {
  font-size: 13px;
  padding: 8px 10px;
  min-width: 36px;
}

/* When the TV is locked, dim the scene/params controls so it's visually
   obvious that taps won't do anything until you unlock. */
.card.tv.locked .scene-buttons,
.card.tv.locked .replay-pin,
.card.tv.locked .rotate-config,
.card.tv.locked .quick-actions {
  opacity: 0.4;
  pointer-events: none;
  filter: saturate(0.5);
}

.card.tv.locked {
  border-color: rgba(245, 158, 11, 0.45);
  background: linear-gradient(
    180deg,
    rgba(245, 158, 11, 0.06),
    rgba(255, 255, 255, 0.04) 60%
  );
}

.card.tv.locked .lock-btn:not(.util-btn) {
  background: rgba(245, 158, 11, 0.18);
  border-color: rgba(245, 158, 11, 0.5);
  color: #fbbf24;
}

.quick-actions {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
}

.quick {
  background: rgba(245, 158, 11, 0.12);
  border: 1px solid rgba(245, 158, 11, 0.3);
  color: #fbbf24;
  border-radius: 999px;
  padding: 8px 14px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.04em;
  cursor: pointer;
  transition: background 120ms;
}

.quick:hover {
  background: rgba(245, 158, 11, 0.22);
}

.quick:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.scene-buttons {
  display: grid;
  /* auto-fit so the layout stays sane whether we have 5 scenes or 6+. */
  grid-template-columns: repeat(auto-fit, minmax(72px, 1fr));
  gap: 6px;
}

.scene-btn {
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--border);
  color: var(--text-soft);
  border-radius: 10px;
  padding: 12px 8px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  cursor: pointer;
  transition: background 120ms, color 120ms, border-color 120ms;
}

.scene-btn:hover {
  background: var(--bg-card-hover);
  color: var(--text);
}

.scene-btn.active {
  background: linear-gradient(135deg, rgba(52, 211, 153, 0.18), rgba(34, 211, 238, 0.18));
  border-color: rgba(52, 211, 153, 0.5);
  color: #fff;
  box-shadow: 0 0 16px rgba(52, 211, 153, 0.18) inset;
}

.scene-btn:disabled {
  opacity: 0.5;
  cursor: wait;
}

.replay-pin {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.replay-pin-head {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.replay-pin-title {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-faint);
}

.replay-pin-help {
  font-size: 12px;
  line-height: 1.45;
  color: var(--text-soft);
}

.replay-pin-help em {
  font-style: normal;
  color: var(--text);
  font-weight: 600;
}

.pin-row {
  display: flex;
  gap: 6px;
}

.pin-input {
  flex: 1;
  min-width: 0;
  padding: 10px 12px;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--border);
  border-radius: 10px;
  font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, monospace;
  font-size: 12px;
  outline: none;
}

.pin-input:focus {
  border-color: rgba(52, 211, 153, 0.4);
}

.pin-save {
  background: linear-gradient(135deg, var(--accent), var(--accent-2));
  color: #0a0a0c;
  border: 0;
  padding: 10px 14px;
  border-radius: 10px;
  font-weight: 700;
  cursor: pointer;
  font-size: 12px;
  letter-spacing: 0.04em;
}

.pin-current {
  font-size: 11px;
  line-height: 1.5;
  color: var(--text-faint);
  word-break: break-word;
}

.pin-current.pinned {
  color: var(--accent);
  font-weight: 600;
}

/* ── Rotate config ───────────────────────────────────────────────── */

.rotate-config {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 12px;
  border: 1px dashed rgba(245, 158, 11, 0.35);
  border-radius: 12px;
  background: rgba(245, 158, 11, 0.04);
}

.rotate-row {
  display: flex;
  gap: 8px;
  align-items: flex-end;
}

.dwell-label {
  display: flex;
  flex-direction: column;
  gap: 6px;
  flex: 1;
  min-width: 0;
}

.dwell-label > span {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-faint);
}

.dwell-input {
  padding: 10px 12px;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--border);
  border-radius: 10px;
  font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, monospace;
  font-size: 13px;
  outline: none;
  width: 100%;
}

.dwell-input:focus {
  border-color: rgba(245, 158, 11, 0.5);
}

.dwell-save {
  background: linear-gradient(135deg, var(--accent), var(--accent-2));
  color: #0a0a0c;
  border: 0;
  padding: 10px 14px;
  border-radius: 10px;
  font-weight: 700;
  cursor: pointer;
  font-size: 12px;
  letter-spacing: 0.04em;
  white-space: nowrap;
}

.dwell-save:disabled {
  opacity: 0.5;
  cursor: wait;
}

.rotate-scenes {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(110px, 1fr));
  gap: 6px;
}

.rotate-scene {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 12px;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--border);
  border-radius: 10px;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.04em;
  cursor: pointer;
  transition: background 120ms, border-color 120ms;
}

.rotate-scene:has(input:checked) {
  background: rgba(245, 158, 11, 0.12);
  border-color: rgba(245, 158, 11, 0.45);
  color: #fbbf24;
}

.rotate-scene input[type='checkbox'] {
  accent-color: var(--warn);
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  cursor: pointer;
}

.rotate-current {
  font-size: 11px;
  color: var(--text-faint);
  letter-spacing: 0.04em;
}

.tv-foot {
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: 11px;
  color: var(--text-faint);
}

.tv-foot .seen {
  letter-spacing: 0.04em;
}

.tv-foot .open-link {
  padding: 4px 10px;
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}

/* ── Rotation + Custom-calls (collapsible cards) ─────────────────── */

.rotation,
.custom-calls {
  padding: 0;
  overflow: hidden;
}

.rotation summary,
.custom-calls summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 16px;
  font-weight: 700;
  font-size: 14px;
}

.rotation summary::-webkit-details-marker,
.custom-calls summary::-webkit-details-marker {
  display: none;
}

.rotation summary::after,
.custom-calls summary::after {
  content: '+';
  font-weight: 400;
  color: var(--text-faint);
  margin-left: auto;
  padding-left: 12px;
}

.rotation[open] summary::after,
.custom-calls[open] summary::after {
  content: '−';
}

.rotation .count,
.custom-calls .count {
  font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, monospace;
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-faint);
  margin-left: 12px;
  margin-right: auto;
}

.rotation-body {
  padding: 0 16px 16px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.rotation .hint {
  margin: 0;
  font-size: 12px;
  color: var(--text-soft);
  line-height: 1.5;
}

.rotation .hint-sub {
  font-size: 11px;
  color: var(--text-faint);
}

.rotation .hint code,
.replay-pin-help code {
  font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, monospace;
  font-size: 0.92em;
  padding: 1px 5px;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid var(--border);
  border-radius: 4px;
  color: var(--text);
}

#rotation-input {
  width: 100%;
  min-height: 140px;
  padding: 12px;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--border);
  border-radius: 12px;
  font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, monospace;
  font-size: 12px;
  line-height: 1.6;
  color: var(--text);
  resize: vertical;
  outline: none;
}

#rotation-input:focus {
  border-color: rgba(52, 211, 153, 0.4);
}

.rotation-actions {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
}

#rotation-save {
  background: linear-gradient(135deg, var(--accent), var(--accent-2));
  color: #0a0a0c;
  border: 0;
  padding: 10px 18px;
  border-radius: 10px;
  font-weight: 700;
  cursor: pointer;
}

.status-text {
  font-size: 11px;
  color: var(--text-faint);
}

.status-text.muted {
  color: var(--text-faint);
  margin-right: auto;
}

.status-text.ok {
  color: var(--accent);
}

.status-text.err {
  color: var(--err);
}

/* ── Custom calls card ───────────────────────────────────────────── */

.custom-body {
  padding: 0 16px 16px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.custom-calls .hint code {
  font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, monospace;
  font-size: 0.92em;
  padding: 1px 5px;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid var(--border);
  border-radius: 4px;
  color: var(--text);
}

.custom-upload {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 12px;
  border: 1px dashed rgba(34, 211, 238, 0.3);
  border-radius: 12px;
  background: rgba(34, 211, 238, 0.04);
}

.upload-field {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.upload-label {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-faint);
}

.upload-help {
  font-size: 10px;
  color: var(--text-faint);
  line-height: 1.4;
}

.upload-field input[type='text'] {
  padding: 10px 12px;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--border);
  border-radius: 10px;
  font-family: 'Plus Jakarta Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  font-size: 13px;
  color: var(--text);
  outline: none;
}

.upload-field input[type='text']:focus {
  border-color: rgba(34, 211, 238, 0.5);
}

.upload-field input[type='file'] {
  font-size: 12px;
  color: var(--text-soft);
  padding: 6px 0;
}

.upload-field input[type='file']::file-selector-button {
  background: rgba(255, 255, 255, 0.06);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 8px 12px;
  font-size: 12px;
  font-weight: 600;
  margin-right: 10px;
  cursor: pointer;
}

.upload-field input[type='file']::file-selector-button:hover {
  background: rgba(255, 255, 255, 0.1);
}

/* Call-type radio cards (two-speaker vs single-voice). The native radio
 * is hidden — clicking the card label flips its state, and we style the
 * .mode-card based on :checked + sibling state. */
.upload-mode {
  border: none;
  margin: 0;
  padding: 0;
}

.mode-options {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
  margin-top: 4px;
}

.mode-option {
  position: relative;
  cursor: pointer;
}

.mode-option input[type='radio'] {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

.mode-card {
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: rgba(255, 255, 255, 0.025);
  transition: all 0.15s ease;
  height: 100%;
}

.mode-option input[type='radio']:checked + .mode-card {
  border-color: rgba(34, 211, 238, 0.5);
  background: rgba(34, 211, 238, 0.08);
  box-shadow: 0 0 0 1px rgba(34, 211, 238, 0.2);
}

.mode-option input[type='radio']:focus-visible + .mode-card {
  outline: 2px solid rgba(34, 211, 238, 0.5);
  outline-offset: 2px;
}

.mode-card-title {
  font-size: 12px;
  font-weight: 700;
  color: var(--text);
}

.mode-option input[type='radio']:checked + .mode-card .mode-card-title {
  color: #67e8f9;
}

.mode-card-help {
  font-size: 10px;
  color: var(--text-faint);
  line-height: 1.35;
}

/* Hidden until single-voice is selected. */
.upload-field.single-voice-only.hidden {
  display: none;
}

.custom-upload-actions {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  margin-top: 4px;
}

#custom-upload-btn {
  background: linear-gradient(135deg, var(--accent), var(--accent-2));
  color: #0a0a0c;
  border: 0;
  padding: 10px 18px;
  border-radius: 10px;
  font-weight: 700;
  cursor: pointer;
  font-size: 13px;
}

#custom-upload-btn:disabled {
  opacity: 0.5;
  cursor: wait;
}

.upload-progress {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 11px;
  color: var(--text-soft);
}

.upload-progress-bar {
  flex: 1;
  height: 6px;
  background: rgba(255, 255, 255, 0.06);
  border-radius: 999px;
  overflow: hidden;
}

.upload-progress-bar .fill {
  height: 100%;
  width: 0;
  background: linear-gradient(90deg, var(--accent), var(--accent-2));
  transition: width 200ms ease-out;
}

.upload-progress-text {
  font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, monospace;
  min-width: 36px;
  text-align: right;
}

/* ── Library toolbar (search + bulk copy) ─ */

.custom-toolbar {
  display: flex;
  gap: 6px;
  align-items: center;
  flex-wrap: wrap;
  padding: 8px 0 4px;
  border-top: 1px dashed var(--border);
}

.custom-toolbar.hidden {
  display: none;
}

#custom-search {
  flex: 1 1 180px;
  min-width: 0;
  padding: 8px 12px;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--border);
  border-radius: 10px;
  font-family: 'Plus Jakarta Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  font-size: 12px;
  color: var(--text);
  outline: none;
}

#custom-search:focus {
  border-color: rgba(34, 211, 238, 0.5);
}

.custom-toolbar .ghost {
  font-size: 11px;
  padding: 7px 12px;
  white-space: nowrap;
}

#custom-copy-all.copied,
#custom-add-all.added {
  color: var(--accent);
  border-color: var(--accent);
}

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

.custom-list-no-match {
  padding: 12px;
  text-align: center;
  font-size: 11px;
  color: var(--text-faint);
  font-style: italic;
}

.custom-row {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 12px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: rgba(255, 255, 255, 0.025);
}

.custom-row-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}

.custom-row-meta {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 1;
  min-width: 0;
}

.custom-row-meta .title {
  font-weight: 600;
  font-size: 13px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.status-badge {
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  padding: 3px 7px;
  border-radius: 999px;
  border: 1px solid var(--border);
  color: var(--text-soft);
  background: rgba(255, 255, 255, 0.04);
  white-space: nowrap;
}

.status-badge.ready {
  color: var(--accent);
  border-color: rgba(52, 211, 153, 0.4);
  background: rgba(52, 211, 153, 0.1);
}

.status-badge.transcribing {
  color: var(--accent-2);
  border-color: rgba(34, 211, 238, 0.4);
  background: rgba(34, 211, 238, 0.1);
}

.status-badge.failed {
  color: var(--err);
  border-color: rgba(239, 68, 68, 0.4);
  background: rgba(239, 68, 68, 0.1);
}

.del-custom {
  font-size: 14px;
  padding: 6px 8px;
  border-radius: 8px;
}

.del-custom:hover {
  color: var(--err);
  border-color: var(--err);
  background: rgba(239, 68, 68, 0.08);
}

/* Big tap-to-copy ID chip — the entire chip is one click target,
 * so it's friendly on mobile where the previous tiny "Copy ID" button
 * was too small. The chip is full-width with a clear copy affordance
 * on the right.
 */
.id-chip {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  width: 100%;
  padding: 10px 12px;
  background: rgba(34, 211, 238, 0.06);
  border: 1px solid rgba(34, 211, 238, 0.25);
  border-radius: 10px;
  cursor: pointer;
  transition: all 0.15s ease;
  text-align: left;
  font: inherit;
  color: inherit;
  -webkit-appearance: none;
  appearance: none;
}

.id-chip:hover,
.id-chip:focus-visible {
  background: rgba(34, 211, 238, 0.12);
  border-color: rgba(34, 211, 238, 0.5);
  outline: none;
}

.id-chip:active {
  transform: translateY(1px);
}

.id-chip .id {
  font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, monospace;
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
  word-break: break-all;
  flex: 1;
  min-width: 0;
}

.id-chip-action {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent-2);
  white-space: nowrap;
  flex-shrink: 0;
}

.id-chip-icon {
  font-size: 14px;
  line-height: 1;
}

.id-chip.copied {
  background: rgba(52, 211, 153, 0.12);
  border-color: rgba(52, 211, 153, 0.5);
}

.id-chip.copied .id-chip-action {
  color: var(--accent);
}

/* Pill that flags single-voice (ad/demo) calls vs default two-speaker. */
.mode-badge {
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding: 3px 7px;
  border-radius: 999px;
  white-space: nowrap;
  border: 1px solid rgba(34, 211, 238, 0.4);
  background: rgba(34, 211, 238, 0.1);
  color: #67e8f9;
}

.mode-badge.hidden {
  display: none;
}

/* Status pill that lights up when the id is in the active rotation. */
.rotation-badge {
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding: 3px 7px;
  border-radius: 999px;
  border: 1px solid rgba(167, 139, 250, 0.4);
  background: rgba(167, 139, 250, 0.1);
  color: #c4b5fd;
  white-space: nowrap;
}

.rotation-badge.hidden {
  display: none;
}

/* Hidden by .custom-row.filtered when the search query doesn't match. */
.custom-row.filtered {
  display: none;
}

.custom-row-detail {
  font-size: 11px;
  color: var(--text-faint);
  line-height: 1.4;
}

.custom-row-detail.err {
  color: var(--err);
}

.custom-row-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.custom-row-actions .ghost {
  font-size: 11px;
  padding: 6px 10px;
}

.custom-row-actions .add-to-rotation {
  color: var(--accent);
  border-color: rgba(52, 211, 153, 0.35);
}

.custom-row-actions .add-to-rotation:hover {
  background: rgba(52, 211, 153, 0.1);
}

.custom-row-actions .add-to-rotation.in-rotation {
  color: var(--text-faint);
  border-color: var(--border);
  cursor: default;
}

.custom-row-actions .add-to-rotation.in-rotation:hover {
  background: transparent;
}

.custom-list-empty {
  padding: 14px;
  text-align: center;
  font-size: 12px;
  color: var(--text-faint);
  border: 1px dashed var(--border);
  border-radius: 12px;
}

/* ── Mute button (per TV head) ───────────────────────────────────── */

.mute-btn:not(.util-btn) {
  font-size: 14px;
  width: 32px;
  padding: 0;
  text-align: center;
}

.mute-btn:not(.util-btn).muted {
  color: var(--warn);
  border-color: rgba(245, 158, 11, 0.45);
  background: rgba(245, 158, 11, 0.08);
}

/* ── Group chip on a TV card ─────────────────────────────────────── */

.group-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  align-self: flex-start;
  margin: -6px 0 8px;
  padding: 6px 10px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.05em;
  color: var(--accent-2);
  background: rgba(34, 211, 238, 0.07);
  border: 1px solid rgba(34, 211, 238, 0.32);
  border-radius: 999px;
  cursor: pointer;
  transition: background 120ms ease, border-color 120ms ease;
}
.group-chip:hover {
  background: rgba(34, 211, 238, 0.13);
  border-color: rgba(34, 211, 238, 0.55);
}
.group-chip-icon {
  font-size: 12px;
}
.group-chip-status {
  color: var(--text-faint);
  font-weight: 500;
  letter-spacing: 0.04em;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
}

/* ── Display groups card ─────────────────────────────────────────── */

.groups summary {
  cursor: pointer;
  list-style: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 18px;
  font-weight: 700;
  font-size: 14px;
}
.groups summary::-webkit-details-marker { display: none; }
.groups[open] summary {
  border-bottom: 1px solid var(--border);
}
.groups .count {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 11px;
  font-weight: 600;
  color: var(--text-soft);
  background: rgba(255, 255, 255, 0.06);
  padding: 2px 8px;
  border-radius: 999px;
}
.groups-body {
  padding: 16px 18px 18px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.group-create {
  display: grid;
  grid-template-columns: 1.4fr 1fr auto;
  gap: 8px;
  align-items: center;
}
.group-create input {
  background: rgba(0, 0, 0, 0.32);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 10px 12px;
  font-size: 13px;
  outline: none;
}
.group-create input:focus {
  border-color: var(--accent);
}
.group-create button {
  background: linear-gradient(135deg, var(--accent), var(--accent-2));
  color: #06231a;
  border: none;
  border-radius: 10px;
  padding: 10px 14px;
  font-weight: 700;
  font-size: 13px;
  cursor: pointer;
}
.group-create button:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
#group-create-status {
  grid-column: 1 / -1;
  font-size: 11px;
}

.groups-list {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.group-card {
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 14px 14px 12px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.group-card.synced {
  border-color: rgba(34, 211, 238, 0.32);
  background: linear-gradient(160deg, rgba(34, 211, 238, 0.05), rgba(255, 255, 255, 0.03));
}
.group-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
}
.group-id {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.group-name {
  font-weight: 700;
  font-size: 14px;
  letter-spacing: -0.01em;
}
.group-meta {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 10px;
  color: var(--text-faint);
  letter-spacing: 0.06em;
}
.group-head-actions {
  display: flex;
  gap: 6px;
  flex-shrink: 0;
}
.group-head-actions .resync {
  color: var(--accent-2);
  border-color: rgba(34, 211, 238, 0.4);
}
.group-head-actions .resync:hover {
  background: rgba(34, 211, 238, 0.12);
}

.group-config {
  display: grid;
  grid-template-columns: 1fr 1.2fr auto;
  gap: 10px;
  align-items: end;
}
.group-field {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
}
.group-field-label {
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-faint);
}
.group-field select,
.group-field input {
  background: rgba(0, 0, 0, 0.32);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 10px 12px;
  font-size: 13px;
  font-family: inherit;
  color: inherit;
  line-height: 1.2;
  outline: none;
  width: 100%;
  box-sizing: border-box;
}
/* Normalize the native <select> so it matches the sibling <input>:
   - macOS otherwise sits the text on a different baseline and crops
     the descenders ("p", "y") inside the chrome-rendered box.
   - The native chevron also overlaps the value text at our padding.
   We strip the native appearance and paint our own caret as a tiny SVG
   on the right, then leave room for it with extra right padding. */
.group-field select {
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  padding-right: 32px;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'><path d='M1 1l4 4 4-4' fill='none' stroke='%23ffffff' stroke-opacity='0.6' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/></svg>");
  background-repeat: no-repeat;
  background-position: right 12px center;
  background-size: 10px 6px;
  cursor: pointer;
}
.group-field select option {
  background: #1a1d24;
  color: #fff;
}
.group-field select:focus,
.group-field input:focus {
  border-color: var(--accent);
}
.group-save {
  background: rgba(255, 255, 255, 0.07);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 10px 14px;
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
}
.group-save:hover {
  background: rgba(255, 255, 255, 0.12);
}
.group-save-status {
  grid-column: 1 / -1;
  font-size: 11px;
  margin-top: -4px;
}

.group-members-label {
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-faint);
  margin-bottom: 6px;
}
.group-members-list {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.group-member-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 10px;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid var(--border);
  border-radius: 999px;
  font-size: 11px;
  cursor: pointer;
  transition: border-color 120ms ease, background 120ms ease;
}
.group-member-chip input {
  margin: 0;
  accent-color: var(--accent);
}
.group-member-chip.member {
  border-color: rgba(52, 211, 153, 0.45);
  background: rgba(52, 211, 153, 0.08);
}
.group-member-chip.offline {
  opacity: 0.55;
}

.group-foot {
  border-top: 1px dashed var(--border);
  padding-top: 8px;
  font-size: 11px;
}
.group-sync-status {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  letter-spacing: 0.04em;
  color: var(--text-faint);
}
.group-sync-status.live {
  color: var(--accent);
}

.group-field-help {
  display: block;
  margin-top: 6px;
  font-size: 11px;
  line-height: 1.4;
  color: var(--text-faint);
}

/* Rotation status block — only visible when group is auto-rotating
   through the rotation library (no operator pin set). */
.group-rotation {
  margin-top: 6px;
  padding: 10px 12px;
  border: 1px solid rgba(52, 211, 153, 0.25);
  background: rgba(52, 211, 153, 0.05);
  border-radius: 10px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.group-rotation.hidden {
  display: none;
}
.group-rotation-row {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}
.group-rotation-label {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--accent);
  font-weight: 600;
}
.group-rotation-now {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 12px;
  background: rgba(0, 0, 0, 0.35);
  padding: 4px 8px;
  border-radius: 6px;
  cursor: pointer;
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  user-select: all;
  border: 1px solid var(--border);
}
.group-rotation-now:hover {
  border-color: rgba(52, 211, 153, 0.45);
}
.group-rotation-skip {
  flex: 0 0 auto;
  font-size: 11px;
  padding: 4px 10px;
}
.group-rotation-meta {
  font-size: 11px;
  color: var(--text-faint);
}

.hint-link {
  color: var(--accent);
  text-decoration: underline;
  text-decoration-style: dotted;
  text-underline-offset: 2px;
  cursor: pointer;
}
.hint-link:hover {
  text-decoration-style: solid;
}

@media (max-width: 600px) {
  .group-create {
    grid-template-columns: 1fr;
  }
  .group-config {
    grid-template-columns: 1fr;
  }
  .group-save {
    width: 100%;
  }
}

/* ── Announcement card ───────────────────────────────────────────────
 *
 * Mirrors the rotation/groups card chrome (collapsible details with a
 * status pill in the summary), and adds a side-by-side editor + live
 * preview. The preview node is a scaled-down sibling of the /announce/
 * scene so operators see exactly what the TV will look like.
 * ──────────────────────────────────────────────────────────────────── */

.announcement-card {
  padding: 0;
  overflow: hidden;
}

.announcement-card summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 16px;
  font-weight: 700;
  font-size: 14px;
}

.announcement-card summary::-webkit-details-marker {
  display: none;
}

.announcement-card summary::after {
  content: '+';
  font-weight: 400;
  color: var(--text-faint);
  margin-left: auto;
  padding-left: 12px;
}

.announcement-card[open] summary::after {
  content: '−';
}

/* Status pill — colored dot + label ("on · 4", "saved", "off"). */
.announcement-card .count {
  font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, monospace;
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-faint);
  margin-left: 12px;
  margin-right: auto;
  padding: 3px 9px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: rgba(255, 255, 255, 0.04);
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.announcement-card .count::before {
  content: '';
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--text-faint);
}

.announcement-card .count.off::before {
  background: var(--text-faint);
}

.announcement-card .count.saved::before {
  background: var(--accent-2);
  box-shadow: 0 0 8px rgba(34, 211, 238, 0.5);
}

.announcement-card .count.on {
  color: #0a0a0c;
  background: linear-gradient(135deg, var(--accent), var(--accent-2));
  border-color: transparent;
}

.announcement-card .count.on::before {
  background: #0a0a0c;
  box-shadow: none;
  animation: ann-on-pulse 1.6s ease-in-out infinite;
}

@keyframes ann-on-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50%      { opacity: 0.5; transform: scale(1.4); }
}

.announcement-body {
  padding: 0 16px 16px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.announcement-card .hint {
  margin: 0;
  font-size: 12px;
  color: var(--text-soft);
  line-height: 1.5;
}

.announcement-card .hint code,
.announcement-card .hint em {
  font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, monospace;
  font-size: 0.92em;
  padding: 1px 5px;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid var(--border);
  border-radius: 4px;
  color: var(--text);
  font-style: normal;
}

/* Two-column layout: form on the left, preview on the right. Stacks on
   narrow phones via the @media block below. */
.announce-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 16px;
  align-items: start;
}

.announce-form {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 14px;
  border: 1px dashed rgba(34, 211, 238, 0.25);
  border-radius: 12px;
  background: rgba(34, 211, 238, 0.035);
}

.ann-field {
  display: flex;
  flex-direction: column;
  gap: 4px;
  position: relative;
  margin: 0;
  padding: 0;
  border: 0;
}

.ann-label {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-soft);
}

.ann-help {
  font-weight: 500;
  letter-spacing: 0.04em;
  text-transform: none;
  color: var(--text-faint);
  margin-left: 6px;
}

.ann-field input[type='text'],
.ann-field textarea {
  width: 100%;
  padding: 10px 12px;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--border);
  border-radius: 10px;
  color: var(--text);
  font-size: 14px;
  outline: none;
  resize: vertical;
  transition: border-color 160ms;
}

.ann-field textarea {
  font-family: inherit;
  line-height: 1.4;
  min-height: 56px;
}

.ann-field input[type='text']:focus,
.ann-field textarea:focus {
  border-color: rgba(52, 211, 153, 0.5);
  background: rgba(52, 211, 153, 0.05);
}

.ann-counter {
  position: absolute;
  right: 8px;
  bottom: 6px;
  font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, monospace;
  font-size: 10px;
  color: var(--text-faint);
  pointer-events: none;
  background: rgba(10, 10, 12, 0.7);
  padding: 1px 5px;
  border-radius: 4px;
}

/* Accent picker: 4 swatches in a row. The selected one glows. */
.ann-accents {
  border: 0;
}

.accent-options {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 8px;
  margin-top: 4px;
}

.accent-option {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 10px 6px;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--border);
  border-radius: 10px;
  cursor: pointer;
  transition: border-color 160ms, background 160ms, transform 120ms;
}

.accent-option input[type='radio'] {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

.accent-option .accent-swatch {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: linear-gradient(135deg, #34d399, #22d3ee, #818cf8);
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.08) inset;
}

.accent-option[data-accent='amber'] .accent-swatch {
  background: linear-gradient(135deg, #fbbf24, #fb923c, #f87171);
}
.accent-option[data-accent='rose'] .accent-swatch {
  background: linear-gradient(135deg, #fb7185, #f472b6, #c084fc);
}
.accent-option[data-accent='violet'] .accent-swatch {
  background: linear-gradient(135deg, #a78bfa, #818cf8, #38bdf8);
}

.accent-option .accent-name {
  font-size: 11px;
  font-weight: 600;
  color: var(--text-soft);
  letter-spacing: 0.06em;
}

.accent-option:hover {
  border-color: rgba(255, 255, 255, 0.2);
}

.accent-option.selected {
  border-color: rgba(52, 211, 153, 0.55);
  background: rgba(52, 211, 153, 0.06);
}

.accent-option.selected .accent-swatch {
  box-shadow:
    0 0 0 2px rgba(255, 255, 255, 0.6) inset,
    0 0 14px currentColor;
  color: rgba(52, 211, 153, 0.6);
  transform: scale(1.05);
}

.accent-option[data-accent='amber'].selected .accent-swatch {
  color: rgba(251, 191, 36, 0.55);
}
.accent-option[data-accent='rose'].selected .accent-swatch {
  color: rgba(244, 114, 182, 0.55);
}
.accent-option[data-accent='violet'].selected .accent-swatch {
  color: rgba(167, 139, 250, 0.55);
}

.ann-urgent-row {
  flex-direction: row;
  align-items: flex-start;
  gap: 10px;
}

.ann-urgent-row input[type='checkbox'] {
  margin-top: 2px;
  width: 18px;
  height: 18px;
  accent-color: var(--accent);
}

.ann-urgent-row span {
  font-size: 12.5px;
  color: var(--text-soft);
  line-height: 1.4;
}

.ann-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-top: 4px;
}

.ann-actions .primary {
  background: linear-gradient(135deg, var(--accent), var(--accent-2));
  color: #0a0a0c;
  border: 0;
  padding: 10px 14px;
  border-radius: 10px;
  font-weight: 700;
  cursor: pointer;
  font-size: 13px;
  letter-spacing: 0.02em;
  transition: filter 160ms, transform 80ms;
}

.ann-actions .primary:hover {
  filter: brightness(1.08);
}

.ann-actions .primary:active {
  transform: translateY(1px);
}

.ann-actions .primary:disabled {
  opacity: 0.55;
  cursor: not-allowed;
  filter: none;
}

#ann-show {
  background: linear-gradient(135deg, #fbbf24, #fb923c);
  color: #0a0a0c;
}

.ann-actions .ghost {
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid var(--border);
  color: var(--text);
  padding: 10px 12px;
  border-radius: 10px;
  font-weight: 600;
  font-size: 13px;
  cursor: pointer;
  transition: background 160ms, border-color 160ms;
}

.ann-actions .ghost:hover {
  background: rgba(255, 255, 255, 0.09);
  border-color: rgba(255, 255, 255, 0.18);
}

.ann-actions .ghost.danger {
  color: #fda4af;
}

.ann-actions .ghost.danger:hover {
  background: rgba(239, 68, 68, 0.08);
  border-color: rgba(239, 68, 68, 0.4);
}

.ann-actions .status-text {
  flex: 1;
  text-align: right;
  min-width: 0;
}

/* ── Live preview ───────────────────────────────────────────────────
 *
 * Self-contained mini-replica of the /announce/ scene. Uses the same
 * accent-driven CSS variables so a theme switch in the form recolors
 * the preview instantly without re-rendering anything.
 * ──────────────────────────────────────────────────────────────────── */

.announce-preview-wrap {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.announce-preview-label {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-faint);
}

.announce-preview {
  position: relative;
  aspect-ratio: 16 / 10;
  overflow: hidden;
  border-radius: 14px;
  background: #0a0a0c;
  border: 1px solid var(--border);
  isolation: isolate;
  --ap-1: #34d399;
  --ap-2: #22d3ee;
  --ap-3: #818cf8;
  --ap-glow: rgba(52, 211, 153, 0.4);
}

.announce-preview[data-accent='amber'] {
  --ap-1: #fbbf24;
  --ap-2: #fb923c;
  --ap-3: #f87171;
  --ap-glow: rgba(251, 191, 36, 0.42);
}
.announce-preview[data-accent='rose'] {
  --ap-1: #fb7185;
  --ap-2: #f472b6;
  --ap-3: #c084fc;
  --ap-glow: rgba(244, 114, 182, 0.42);
}
.announce-preview[data-accent='violet'] {
  --ap-1: #a78bfa;
  --ap-2: #818cf8;
  --ap-3: #38bdf8;
  --ap-glow: rgba(167, 139, 250, 0.42);
}

.announce-preview .ap-bg {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(60% 80% at 20% 20%, var(--ap-1), transparent 70%),
    radial-gradient(70% 80% at 80% 80%, var(--ap-2), transparent 70%),
    radial-gradient(40% 60% at 50% 50%, var(--ap-3), transparent 70%);
  filter: blur(28px);
  opacity: 0.55;
  z-index: 0;
}

.announce-preview .ap-content {
  position: relative;
  z-index: 1;
  height: 100%;
  width: 100%;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 16px 18px;
  gap: 10px;
}

.announce-preview .ap-kicker {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 4px 10px;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 999px;
  align-self: flex-start;
  backdrop-filter: blur(4px);
}

.announce-preview .ap-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--ap-1);
  box-shadow: 0 0 8px var(--ap-glow);
}

.announce-preview .ap-kicker-text {
  font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, monospace;
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.28em;
  color: rgba(255, 255, 255, 0.85);
}

.announce-preview .ap-headline {
  font-size: clamp(18px, 3vw, 32px);
  font-weight: 900;
  letter-spacing: -0.02em;
  line-height: 1;
  background: linear-gradient(
    135deg,
    #ffffff 0%,
    var(--ap-1) 35%,
    var(--ap-2) 65%,
    var(--ap-3) 100%
  );
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  padding-bottom: 0.05em;
  word-break: break-word;
}

.announce-preview .ap-headline.placeholder {
  background: none;
  -webkit-text-fill-color: rgba(255, 255, 255, 0.35);
  font-weight: 700;
  font-style: italic;
}

.announce-preview .ap-body {
  font-size: 12px;
  line-height: 1.4;
  color: rgba(255, 255, 255, 0.75);
  max-width: 90%;
}

.announce-preview .ap-foot {
  margin-top: auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.announce-preview .ap-urgent {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 8px;
  background: linear-gradient(135deg, rgba(255, 255, 255, 0.05), var(--ap-glow));
  border: 1px solid var(--ap-glow);
  border-radius: 999px;
  font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, monospace;
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.24em;
  color: #fff;
}

.announce-preview .ap-urgent-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--ap-1);
  box-shadow: 0 0 6px var(--ap-glow);
  animation: ap-urgent-pulse 1.4s ease-out infinite;
}

@keyframes ap-urgent-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50%      { opacity: 0.6; transform: scale(1.4); }
}

.announce-preview .ap-brand {
  font-size: 11px;
  font-weight: 800;
  letter-spacing: -0.005em;
  background: linear-gradient(135deg, var(--accent), var(--accent-2));
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  margin-left: auto;
}

.announce-broadcast-meta {
  font-size: 11px;
  color: var(--text-faint);
  text-align: center;
}

.announce-broadcast-meta.live {
  color: var(--accent);
  font-weight: 600;
}

@media (max-width: 720px) {
  .announce-grid {
    grid-template-columns: 1fr;
  }
  .accent-options {
    grid-template-columns: repeat(4, 1fr);
  }
}

/* ── Footer ──────────────────────────────────────────────────────── */

.foot {
  text-align: center;
  font-size: 10px;
  color: var(--text-faint);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  padding: 8px 0 0;
}

/* Smaller phones */
@media (max-width: 480px) {
  .scene-buttons {
    grid-template-columns: repeat(3, 1fr);
  }
}

/* ──────────────────────────────────────────────────────────────────
 * Shell v2 — tabbed mobile-first IA
 *
 * Adds the tabbed shell, sticky bottom navigation, status hero, primary
 * CTA card, compact TV rows, per-TV bottom-sheet, and tab-section
 * containers. Built additively over the existing card styles so the
 * card internals (.scene-buttons, .replay-pin, etc.) keep rendering as
 * before — they just live inside #sheet-body when the operator opens a
 * specific TV instead of being stacked vertically all the time.
 * ────────────────────────────────────────────────────────────────── */

/* ─── Topbar (icon-only buttons + brand) ─────────────────────────── */

.icon-only {
  width: 38px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 14px;
}

.topbar-actions .icon-only {
  height: 36px;
}

/* ─── Tab views ─────────────────────────────────────────────────── */

.tab-views {
  flex: 1 1 auto;
  position: relative;
  /* Padding-bottom = bottom-nav height + safe area + breathing room. */
  padding: 12px 14px calc(96px + env(safe-area-inset-bottom, 0));
  display: block;
}

.tab-view {
  display: flex;
  flex-direction: column;
  gap: 14px;
  animation: tab-fade 180ms ease-out;
}

@keyframes tab-fade {
  from { opacity: 0; transform: translateY(4px); }
  to { opacity: 1; transform: none; }
}

.tab-view[hidden] { display: none !important; }

.tab-header {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 4px 4px 0;
}

.tab-title {
  margin: 0;
  font-size: 22px;
  font-weight: 800;
  letter-spacing: -0.015em;
}

/* ─── Status hero (TVs tab top) ──────────────────────────────────── */

.status-hero {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 8px;
  padding: 14px 12px;
  background: linear-gradient(
    180deg,
    rgba(52, 211, 153, 0.05),
    rgba(34, 211, 238, 0.03) 60%,
    rgba(255, 255, 255, 0.02)
  );
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

.status-stat {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  text-align: center;
}

.status-stat .status-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.18);
  margin-bottom: 4px;
}

.status-stat .status-dot.online {
  background: var(--accent);
  box-shadow: 0 0 8px rgba(52, 211, 153, 0.55);
}
.status-stat .status-dot.announce {
  background: #c084fc;
  box-shadow: 0 0 8px rgba(192, 132, 252, 0.55);
}
.status-stat .status-dot.muted {
  background: var(--warn);
  box-shadow: 0 0 8px rgba(245, 158, 11, 0.5);
}
.status-stat .status-dot.offline {
  background: rgba(255, 255, 255, 0.18);
}

.status-stat-num {
  font-size: 20px;
  font-weight: 800;
  letter-spacing: -0.02em;
  font-feature-settings: 'tnum' 1;
}

.status-stat-label {
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-faint);
}

/* ─── Primary CTA card (context-aware) ───────────────────────────── */

.primary-cta-card {
  display: flex;
  align-items: stretch;
  gap: 8px;
  padding: 10px;
  background: linear-gradient(
    135deg,
    rgba(192, 132, 252, 0.08),
    rgba(168, 85, 247, 0.04) 60%,
    rgba(255, 255, 255, 0.02)
  );
  border: 1px solid rgba(192, 132, 252, 0.25);
  border-radius: var(--radius);
}

.primary-cta-card[data-mode='live'] {
  background: linear-gradient(
    135deg,
    rgba(245, 158, 11, 0.16),
    rgba(245, 158, 11, 0.06) 60%,
    rgba(255, 255, 255, 0.02)
  );
  border-color: rgba(245, 158, 11, 0.45);
}

.primary-cta-card[data-mode='ready'] {
  background: linear-gradient(
    135deg,
    rgba(52, 211, 153, 0.12),
    rgba(34, 211, 238, 0.06) 60%,
    rgba(255, 255, 255, 0.02)
  );
  border-color: rgba(52, 211, 153, 0.4);
}

.primary-cta-btn {
  flex: 1 1 auto;
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 14px 16px;
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid var(--border);
  border-radius: 12px;
  cursor: pointer;
  text-align: left;
  color: var(--text);
  transition: background 150ms, border-color 150ms, transform 100ms;
}

.primary-cta-btn:hover {
  background: rgba(255, 255, 255, 0.06);
  border-color: rgba(255, 255, 255, 0.18);
}

.primary-cta-btn:active { transform: scale(0.99); }

.primary-cta-emoji {
  font-size: 24px;
  flex-shrink: 0;
  width: 40px;
  height: 40px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: rgba(192, 132, 252, 0.16);
  border-radius: 10px;
}

.primary-cta-card[data-mode='live'] .primary-cta-emoji {
  background: rgba(245, 158, 11, 0.22);
  animation: cta-pulse 2.4s ease-in-out infinite;
}

@keyframes cta-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(245, 158, 11, 0.45); }
  50% { box-shadow: 0 0 0 10px rgba(245, 158, 11, 0); }
}

.primary-cta-card[data-mode='ready'] .primary-cta-emoji {
  background: rgba(52, 211, 153, 0.2);
}

.primary-cta-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.primary-cta-title {
  font-weight: 700;
  font-size: 15px;
  letter-spacing: -0.01em;
}

.primary-cta-sub {
  font-size: 12px;
  color: var(--text-soft);
}

.primary-cta-edit {
  flex-shrink: 0;
  align-self: stretch;
  padding: 8px 12px;
  font-size: 12px;
  display: inline-flex;
  align-items: center;
}

/* ─── Compact TV rows ────────────────────────────────────────────── */

.tv-rows {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.tv-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 14px;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 12px;
  cursor: pointer;
  text-align: left;
  color: var(--text);
  width: 100%;
  transition: background 120ms, border-color 120ms, transform 80ms;
}

.tv-row:hover {
  background: var(--bg-card-hover);
  border-color: rgba(255, 255, 255, 0.16);
}

.tv-row:active {
  transform: scale(0.997);
}

.tv-row.locked {
  border-color: rgba(245, 158, 11, 0.4);
  background: linear-gradient(
    180deg,
    rgba(245, 158, 11, 0.05),
    rgba(255, 255, 255, 0.04) 60%
  );
}

.tv-row.announcing {
  border-color: rgba(192, 132, 252, 0.5);
  background: linear-gradient(
    180deg,
    rgba(192, 132, 252, 0.07),
    rgba(255, 255, 255, 0.04) 60%
  );
}

.tv-row-dot {
  flex-shrink: 0;
}

.tv-row-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  flex: 1 1 auto;
}

.tv-row-name {
  font-weight: 700;
  font-size: 15px;
  letter-spacing: -0.01em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.tv-row-meta {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 11px;
  color: var(--text-soft);
}

.tv-row-meta .scene {
  font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, monospace;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  font-size: 10px;
  color: var(--text-faint);
  border: 1px solid var(--border);
  border-radius: 5px;
  padding: 2px 6px;
}

.tv-row-pin { font-size: 11px; }

.tv-row-pills {
  display: flex;
  align-items: center;
  gap: 4px;
  flex-shrink: 0;
}

.tv-row-pill {
  width: 22px;
  height: 22px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.07);
  border: 1px solid var(--border);
}

.tv-row-pill.mute {
  color: var(--warn);
  border-color: rgba(245, 158, 11, 0.45);
  background: rgba(245, 158, 11, 0.08);
}

.tv-row-pill.lock {
  color: var(--warn);
  border-color: rgba(245, 158, 11, 0.45);
  background: rgba(245, 158, 11, 0.08);
}

.tv-row-pill.announce {
  color: #c4b5fd;
  border-color: rgba(192, 132, 252, 0.5);
  background: rgba(192, 132, 252, 0.1);
}

.tv-row-pill.group {
  color: var(--accent-2);
  border-color: rgba(34, 211, 238, 0.4);
  background: rgba(34, 211, 238, 0.08);
}

.tv-row-chevron {
  font-size: 22px;
  color: var(--text-faint);
  flex-shrink: 0;
  line-height: 1;
}

/* ─── Empty state ────────────────────────────────────────────────── */

.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 36px 20px;
  text-align: center;
  background: var(--bg-card);
  border: 1px dashed var(--border);
  border-radius: var(--radius);
}

.empty-state-icon { font-size: 36px; opacity: 0.45; }
.empty-state-title { font-weight: 700; font-size: 15px; }
.empty-state-sub { font-size: 13px; color: var(--text-soft); max-width: 360px; }
.empty-state-sub code {
  background: rgba(255, 255, 255, 0.05);
  padding: 2px 6px;
  border-radius: 6px;
  color: var(--accent);
}

/* ─── Bottom navigation (sticky tabs) ────────────────────────────── */

.bottom-nav {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 50;
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 0;
  padding: 6px 6px calc(6px + env(safe-area-inset-bottom, 0));
  background: linear-gradient(
    180deg,
    rgba(10, 10, 12, 0.78),
    rgba(10, 10, 12, 0.96) 80%
  );
  border-top: 1px solid var(--border);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
}

.nav-tab {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: 8px 4px;
  background: transparent;
  border: none;
  border-radius: 10px;
  color: var(--text-faint);
  cursor: pointer;
  transition: color 150ms, background 150ms;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.05em;
}

.nav-tab .nav-icon {
  font-size: 18px;
  line-height: 1;
}

.nav-tab .nav-label {
  font-size: 10px;
  letter-spacing: 0.06em;
}

.nav-tab[aria-selected='true'] {
  color: var(--text);
  background: rgba(52, 211, 153, 0.08);
}

.nav-tab[aria-selected='true'] .nav-icon {
  filter: drop-shadow(0 0 6px rgba(52, 211, 153, 0.4));
}

.nav-tab:active {
  transform: scale(0.97);
}

.nav-badge {
  position: absolute;
  top: 4px;
  right: 18px;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  font-size: 10px;
  font-weight: 700;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: rgba(255, 255, 255, 0.1);
  border-radius: 999px;
  color: var(--text);
}

.nav-badge.live {
  background: var(--warn);
  color: #1a1300;
  animation: cta-pulse 2.4s ease-in-out infinite;
}

.nav-badge.empty,
.nav-badge:empty {
  display: none;
}

/* ─── Per-TV bottom sheet ────────────────────────────────────────── */

.sheet {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: flex-end;
  justify-content: center;
}

.sheet[aria-hidden='true'],
.sheet.hidden {
  display: none !important;
}

.sheet-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(2, 4, 8, 0.62);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  animation: backdrop-in 180ms ease-out;
}

@keyframes backdrop-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

.sheet-panel {
  position: relative;
  width: 100%;
  max-width: 760px;
  max-height: 92vh;
  max-height: 92dvh;
  display: flex;
  flex-direction: column;
  background: #0e0f12;
  border-top: 1px solid var(--border);
  border-radius: 20px 20px 0 0;
  box-shadow: 0 -20px 60px rgba(0, 0, 0, 0.5);
  animation: sheet-up 240ms cubic-bezier(0.2, 0.85, 0.3, 1);
}

@keyframes sheet-up {
  from { transform: translateY(28px); opacity: 0.4; }
  to { transform: none; opacity: 1; }
}

.sheet-head {
  position: sticky;
  top: 0;
  z-index: 1;
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 8px;
  padding: 12px 14px;
  background: linear-gradient(
    180deg,
    rgba(14, 15, 18, 0.98),
    rgba(14, 15, 18, 0.92)
  );
  border-bottom: 1px solid var(--border);
  border-radius: 20px 20px 0 0;
}

.sheet-title-wrap {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.sheet-title {
  font-weight: 700;
  font-size: 16px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.sheet-subtitle {
  font-size: 11px;
  color: var(--text-faint);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.sheet-spacer { width: 38px; }

.sheet-body {
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: 14px 14px calc(20px + env(safe-area-inset-bottom, 0));
}

/* The TV article inside the sheet has its own padding/border from .card,
   so kill the redundant outer padding when it lives in the sheet. */
.sheet-body .card.tv {
  background: transparent;
  border: none;
  padding: 0;
  border-radius: 0;
}

/* Drag-handle hint at the very top of the sheet (visual cue only). */
.sheet-panel::before {
  content: '';
  position: absolute;
  top: 6px;
  left: 50%;
  transform: translateX(-50%);
  width: 38px;
  height: 4px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.18);
  pointer-events: none;
}

/* ─── TV utility grid (replaces the old icon-only header buttons) ── */

.tv-utility-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 6px;
}

.util-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 10px 6px;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--border);
  border-radius: 10px;
  color: var(--text-soft);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  cursor: pointer;
  transition: background 120ms, color 120ms, border-color 120ms;
}

.util-btn:hover {
  background: var(--bg-card-hover);
  color: var(--text);
}

.util-btn:active { transform: scale(0.98); }

.util-btn .util-icon { font-size: 17px; line-height: 1; }
.util-btn .util-label { font-size: 11px; line-height: 1; }

.util-btn.danger:hover {
  color: var(--err);
  border-color: rgba(239, 68, 68, 0.55);
  background: rgba(239, 68, 68, 0.08);
}

/* Mute "active" tint reuses the warn palette like before. */
.util-btn.mute-btn.muted {
  color: var(--warn);
  border-color: rgba(245, 158, 11, 0.45);
  background: rgba(245, 158, 11, 0.08);
}

/* When the TV is locked, the lock util button glows amber. */
.card.tv.locked .util-btn.lock-btn {
  color: #fbbf24;
  border-color: rgba(245, 158, 11, 0.5);
  background: rgba(245, 158, 11, 0.18);
}

/* ─── TV section labels (inside the sheet) ───────────────────────── */

.tv-section {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.tv-section-label {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-faint);
}

/* ─── Library + Setup section containers ─────────────────────────── */

.lib-section,
.setup-section {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.lib-section-head {
  display: flex;
  align-items: center;
  gap: 8px;
}

.lib-section-title {
  margin: 0;
  font-size: 16px;
  font-weight: 700;
  letter-spacing: -0.01em;
  flex: 1 1 auto;
}

.lib-section-action {
  font-size: 12px;
  padding: 6px 10px;
}

.rotation-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}

.rotation-actions button {
  background: linear-gradient(135deg, var(--accent), var(--accent-2));
  color: #04120a;
  border: none;
  padding: 10px 16px;
  border-radius: 10px;
  font-weight: 700;
  cursor: pointer;
}

#rotation-input {
  width: 100%;
  min-height: 110px;
  padding: 12px;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--border);
  border-radius: 10px;
  font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, monospace;
  font-size: 13px;
  color: var(--text);
  resize: vertical;
  outline: none;
  transition: border-color 120ms;
}

#rotation-input:focus {
  border-color: rgba(52, 211, 153, 0.45);
}

/* ─── Maintenance grid (Setup tab) ───────────────────────────────── */

.maintenance-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 8px;
}

.setup-action {
  display: grid;
  grid-template-columns: 36px 1fr;
  align-items: center;
  gap: 4px 12px;
  padding: 12px 14px;
  text-align: left;
}

.setup-action .setup-action-icon {
  grid-row: span 2;
  font-size: 22px;
  align-self: center;
}

.setup-action .setup-action-label {
  font-weight: 700;
  font-size: 14px;
  color: var(--text);
}

.setup-action .setup-action-help {
  font-size: 12px;
  color: var(--text-soft);
}

/* ─── Sticky bottom action bar (Announce tab) ────────────────────── */

.sticky-actions {
  position: sticky;
  bottom: calc(86px + env(safe-area-inset-bottom, 0));
  z-index: 5;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  padding: 12px 14px;
  margin: 0 -14px -12px;
  background: linear-gradient(
    180deg,
    rgba(10, 10, 12, 0.6),
    rgba(10, 10, 12, 0.95) 60%
  );
  border-top: 1px solid var(--border);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
}

.sticky-actions .primary,
.sticky-actions button.primary {
  background: linear-gradient(135deg, var(--accent), var(--accent-2));
  color: #04120a;
  border: none;
  padding: 12px 16px;
  border-radius: 10px;
  font-weight: 700;
  cursor: pointer;
  font-size: 14px;
}

.sticky-actions .primary-strong {
  background: linear-gradient(135deg, #c084fc, #8b5cf6);
  color: #fff;
}

.sticky-actions .ghost {
  padding: 11px 14px;
}

.sticky-actions .status-text {
  margin-left: auto;
}

/* ─── Desktop overrides ──────────────────────────────────────────── */

@media (min-width: 880px) {
  .tab-views {
    /* Slightly more breathing room on desktop. */
    padding: 18px 22px calc(110px + env(safe-area-inset-bottom, 0));
  }

  .topbar {
    padding-left: 22px;
    padding-right: 22px;
  }

  /* Desktop bottom nav becomes a centered floating pill. */
  .bottom-nav {
    left: 50%;
    right: auto;
    bottom: 16px;
    transform: translateX(-50%);
    width: min(640px, calc(100vw - 48px));
    grid-template-columns: repeat(5, 1fr);
    border: 1px solid var(--border);
    border-radius: 16px;
    padding: 6px;
    box-shadow: 0 12px 40px rgba(0, 0, 0, 0.35);
  }

  /* Desktop sheet floats centered, smaller than full width. */
  .sheet {
    align-items: center;
  }

  .sheet-panel {
    max-width: 640px;
    border-radius: 20px;
    border: 1px solid var(--border);
    max-height: 88vh;
  }

  .sheet-panel::before { display: none; }

  .sheet-head {
    border-radius: 20px 20px 0 0;
  }

  /* Compensate sticky-actions for the floating bottom nav. */
  .sticky-actions {
    bottom: calc(96px + env(safe-area-inset-bottom, 0));
  }
}

/* ─────────────────────────────────────────────────────────────────────
 * Monitor tab — security control-room grid.
 *
 * Goals:
 *   • Render up to 30 TVs at once without melting the device.
 *   • Each tile is the actual TV host shell embedded as an iframe with
 *     ?monitor=1 (mute, no heartbeat, no chrome). Lazy-mounted via
 *     IntersectionObserver — see control.js renderMonitor().
 *   • Density toggle (lg/md/sm/xs) re-flows the grid via a CSS var.
 *   • The TV host renders in landscape 16:9 and we scale the tile to
 *     match — a fixed aspect-ratio frame keeps the grid tidy at any
 *     density regardless of how each TV scene actually paints.
 *   • Every tile is a button so the operator can tap any preview to
 *     open its full per-TV sheet (existing behavior).
 * ──────────────────────────────────────────────────────────────────── */

.monitor-toolbar {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  padding: 4px 4px;
}

.monitor-density {
  display: inline-flex;
  align-items: center;
  gap: 0;
  padding: 3px;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--border);
  border-radius: 10px;
}

.monitor-density-btn {
  background: transparent;
  border: none;
  color: var(--text-faint);
  padding: 6px 12px;
  font-size: 16px;
  line-height: 1;
  border-radius: 7px;
  cursor: pointer;
  transition: background 120ms, color 120ms;
}

.monitor-density-btn:hover {
  color: var(--text);
  background: rgba(255, 255, 255, 0.05);
}

.monitor-density-btn[aria-pressed='true'] {
  color: var(--text);
  background: rgba(52, 211, 153, 0.16);
  box-shadow: 0 0 0 1px rgba(52, 211, 153, 0.3);
}

.monitor-refresh {
  margin-left: auto;
}

.monitor-show-offline {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  color: var(--text-faint);
  cursor: pointer;
  user-select: none;
}

.monitor-show-offline input {
  margin: 0;
  accent-color: var(--accent, #34d399);
}

/*
 * Grid sizing is driven by --monitor-min: the minimum tile width the
 * operator wants. Browsers use auto-fill to pack as many columns as
 * fit. data-density on .monitor-grid swaps the var so the toolbar
 * buttons feel chunky.
 */
.monitor-grid {
  --monitor-min: 240px;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(var(--monitor-min), 1fr));
  gap: 10px;
  padding: 0 2px 4px;
}

.monitor-grid[data-density='lg'] { --monitor-min: 380px; }
.monitor-grid[data-density='md'] { --monitor-min: 260px; }
.monitor-grid[data-density='sm'] { --monitor-min: 180px; }
.monitor-grid[data-density='xs'] { --monitor-min: 130px; }

/*
 * The tile is now an <article> wrapping a clickable body button + a
 * separate "Preview" toggle button. We keep the card visuals on the
 * <article> so hover/active states still reach across both children.
 */
.monitor-tile {
  position: relative;
  display: flex;
  flex-direction: column;
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid var(--border);
  border-radius: 12px;
  overflow: hidden;
  padding: 0;
  text-align: left;
  font: inherit;
  color: inherit;
  transition: border-color 150ms, box-shadow 150ms, transform 100ms;
}

.monitor-tile-body {
  display: flex;
  flex-direction: column;
  width: 100%;
  background: transparent;
  border: 0;
  padding: 0;
  margin: 0;
  cursor: pointer;
  text-align: left;
  font: inherit;
  color: inherit;
  -webkit-appearance: none;
  appearance: none;
}

.monitor-tile:hover {
  border-color: rgba(52, 211, 153, 0.35);
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.35);
}

.monitor-tile-body:active {
  transform: scale(0.98);
}

.monitor-tile.is-offline {
  opacity: 0.78;
}

.monitor-tile.is-offline:hover {
  border-color: rgba(255, 255, 255, 0.2);
}

/*
 * Mount-state visuals.
 *   data-mount-state="status" — iframe hidden, status placeholder shown
 *   data-mount-state="live"   — iframe visible, status placeholder hidden
 *
 * Hiding via display:none on the iframe keeps it parked on about:blank
 * so it consumes (essentially) zero memory until live mode mounts a src.
 */
.monitor-tile[data-mount-state='status'] .monitor-iframe {
  display: none;
}

.monitor-tile[data-mount-state='live'] .monitor-status {
  display: none;
}

.monitor-status {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 10px;
  text-align: center;
  background: linear-gradient(
    160deg,
    rgba(255, 255, 255, 0.04) 0%,
    rgba(0, 0, 0, 0.18) 100%
  );
  color: var(--text);
}

.monitor-status-icon {
  font-size: 28px;
  line-height: 1;
  opacity: 0.85;
}

.monitor-status-scene {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text);
}

.monitor-status-seen {
  font-size: 11px;
  color: var(--text-faint);
}

/* Preview toggle pill — anchored top-right of the frame so it doesn't
 * fight the meta strip. */
.monitor-preview-toggle {
  position: absolute;
  top: 6px;
  right: 6px;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 4px 8px;
  font-size: 11px;
  font-weight: 600;
  line-height: 1;
  color: var(--text);
  background: rgba(8, 10, 14, 0.78);
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 999px;
  cursor: pointer;
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}

.monitor-preview-toggle:hover {
  border-color: rgba(52, 211, 153, 0.55);
}

.monitor-preview-toggle[data-state='on'] {
  background: rgba(52, 211, 153, 0.2);
  border-color: rgba(52, 211, 153, 0.55);
  color: #34d399;
}

.monitor-preview-toggle:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/* On the smallest density the toggle would crowd the picture; hide it
 * (operator can re-densify if they want per-tile control). */
.monitor-grid[data-density='xs'] .monitor-preview-toggle {
  display: none;
}

/*
 * The frame is the iframe's "viewport". We force a 16:9 box because
 * every TV in the booth runs landscape — and because aspect-ratio keeps
 * tiles aligned even before iframes finish loading.
 *
 * The iframe inside is rendered at TV_NATIVE_WIDTH and CSS-scaled to
 * fit. This avoids the responsive layout of each scene reflowing for a
 * 200px-wide tile and looking broken — the scene paints at its real
 * size and we shrink the picture.
 */
.monitor-frame {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  background: #0a0a0c;
  overflow: hidden;
  /* Used to compute the iframe scale in JS. Override per-density if needed. */
  --tv-native-width: 1920px;
}

.monitor-iframe {
  /* Render the scene at native TV resolution then scale down to fit. */
  position: absolute;
  top: 0;
  left: 0;
  width: var(--tv-native-width);
  height: calc(var(--tv-native-width) * (9 / 16));
  border: 0;
  background: #0a0a0c;
  /*
   * scale = tile width / native width; computed in JS at boot and on
   * every density / resize change. The var() fallback (0.16) is what
   * paints between first frame and the JS recompute — without the
   * fallback the iframe would briefly render at native 1920px and look
   * 6× too big. NOTE: declaring `--tv-scale: …` directly on this
   * selector is a trap — that wins over any value cascaded from .grid,
   * so the JS recompute would silently no-op. Always use the
   * inheritance + fallback pattern instead.
   */
  transform: scale(var(--tv-scale, 0.16));
  transform-origin: top left;
  /* Pointer-events stay enabled so motion doesn't look frozen, but the
   * tile button overlays them and intercepts clicks. */
  pointer-events: none;
}

/* A subtle vignette helps UI text overlays read on busy scenes. */
.monitor-frame-shade {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(
    180deg,
    rgba(0, 0, 0, 0) 60%,
    rgba(0, 0, 0, 0.55) 100%
  );
}

.monitor-offline-mask {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  background: rgba(10, 10, 12, 0.78);
  -webkit-backdrop-filter: blur(2px);
  backdrop-filter: blur(2px);
  color: var(--text-faint);
}

.monitor-offline-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--text-faint);
  opacity: 0.6;
}

.monitor-offline-text {
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.monitor-meta {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 8px 10px;
  font-size: 12px;
  line-height: 1.2;
  background: rgba(0, 0, 0, 0.35);
  border-top: 1px solid var(--border);
  min-height: 34px;
}

.monitor-tile .monitor-dot {
  flex: 0 0 auto;
}

.monitor-name {
  font-weight: 700;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 50%;
}

.monitor-scene {
  flex: 1 1 auto;
  color: var(--text-faint);
  font-size: 11px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.monitor-pills {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  flex: 0 0 auto;
  font-size: 11px;
}

.monitor-pill {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  font-size: 10px;
  line-height: 1;
}

/* Tighter chrome on the smallest tiles so the preview actually breathes. */
.monitor-grid[data-density='xs'] .monitor-meta {
  padding: 5px 7px;
  font-size: 11px;
  min-height: 28px;
}

.monitor-grid[data-density='xs'] .monitor-scene,
.monitor-grid[data-density='xs'] .monitor-pills {
  display: none;
}

/* Hint is mobile-only; default to hidden, the @media block reveals it. */
.monitor-mobile-hint {
  display: none;
}

/*
 * Mobile defaults.
 *
 * iOS Safari can't keep ~3+ nested iframes resident — the inner /tv/
 * shell is itself a host for another iframe (the actual scene), so 3
 * preview tiles = 6+ live frames, each running a Vite/React app. The
 * OS evicts under memory pressure and Safari "reloads" the iframe
 * over and over until it gives up and shows a blank.
 *
 * So on phones we:
 *   • Force every tile into status-only mode (no iframe by default)
 *   • Hide the density toolbar (one density makes sense at this width)
 *   • Make the Preview button bigger / always visible
 *   • Cap concurrent live previews to 1 (enforced in JS)
 *
 * Operators can still tap Preview on a single tile to inspect it
 * live; tapping another tile's Preview unmounts the previous one.
 */
@media (max-width: 720px) {
  .monitor-toolbar .monitor-density {
    display: none;
  }
  .monitor-grid {
    --monitor-min: 100%;
  }
  .monitor-preview-toggle {
    top: 8px;
    right: 8px;
    padding: 6px 12px;
    font-size: 12px;
  }
  /* Mobile hint copy — visibility flipped from the JS so we never even
   * paint it on desktop. */
  .monitor-mobile-hint {
    display: block;
  }
}

