/* ── Reset ── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

/* ── Fantasy display font ── */
.deck-label,
.fade-btn,
.balance-center-btn,
.combine-center-btn,
.fade-effect-btn,
.setting-label,
.volume-label,
.modal-title {
  font-family: var(--font-display);
}

body {
  font-family: var(--font-body);
  background: var(--bg);
  color: var(--text);
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  transition: background 0.2s, color 0.2s;
}

/* ── Header ── */
header {
  position: relative;
  text-align: center;
  padding: 0.6rem 1rem 0.5rem;
  border-bottom: 1px solid var(--border);
  background: var(--bg-header);
  transition: background 0.2s, border-color 0.2s;
}
header h1 {
  font-size: 1.6rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  background: linear-gradient(135deg, #c9a227, #e8a020);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
}
header p {
  color: var(--text-muted);
  font-size: 0.8rem;
  margin-top: 0.2rem;
  letter-spacing: 0.05em;
}
.header-logo {
  height: 62px;
  width: auto;
  display: block;
  margin: 0 auto;
}
.header-subtitle {
  text-align: center;
  font-size: 0.65rem;
  color: var(--text-muted);
  letter-spacing: 0.04em;
  margin: 2px 0 0;
}

/* ── Theme toggle ── */
#theme-toggle {
  background: var(--bg-btn);
  border: 1px solid var(--border);
  border-radius: 8px;
  color: var(--toggle-text);
  padding: 0.4rem 0.6rem;
  cursor: pointer;
  font-size: 1rem;
  line-height: 1;
  transition: all 0.15s;
  display: flex;
  align-items: center;
  gap: 0.3rem;
}
#theme-toggle:hover { border-color: var(--border-focus); color: var(--text); }
#theme-toggle .toggle-label {
  font-size: 0.65rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

/* ── Layout ── */
main {
  flex: 1;
  display: flex;
  flex-direction: column;
}
/* Desktop: three equal-gutter columns (Deck A | Playlist | Deck B) on the first
   flex line, with .center-controls wrapping onto a full-width row beneath them.
   Nothing requires scrolling the page. --app-chrome is the vertical space taken
   by the header, footer, controls row and padding; it caps the playlist column
   so a long playlist scrolls internally instead of growing the page. */
:root {
  --app-chrome: 300px;
  /* Typeface tokens. Themes may override these; see the circuit block at the
     bottom of this file. Fonts are deliberately NOT theme-manager vars, so the
     custom-theme editor (which copies a preset's whole vars map) can't drag a
     typeface along with a palette. */
  --font-body:    'Segoe UI', system-ui, sans-serif;
  --font-display: 'Cinzel', serif;
  --font-mono:    ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
.decks-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.875rem;
  padding: 1.25rem 1.25rem 1rem;
  align-items: flex-start;
}

/* ── Deck panels ── */
.panel {
  flex: 1;
  background: var(--bg-panel-solid);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 1.125rem;
  display: flex;
  flex-direction: column;
  gap: 0.875rem;
  min-width: 0;
  transition: background 0.2s, border-color 0.2s;
}
.panel-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.deck-label {
  font-size: 0.65rem; font-weight: 700; letter-spacing: 0.18em; text-transform: uppercase; color: var(--text-muted);
}

/* ── Status badges ── */
.status-badge {
  font-size: 0.6rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  padding: 0.2rem 0.55rem;
  border-radius: 999px;
  text-transform: uppercase;
  transition: all 0.2s;
}
.status-badge.playing { background: var(--bg-badge-playing); color: var(--badge-playing-text); border: 1px solid var(--badge-playing-bord); }
.status-badge.paused  { background: var(--bg-badge-paused);  color: var(--warning-text);  border: 1px solid var(--badge-paused-bord); }
.status-badge.ready   { background: var(--bg-badge-ready);   color: var(--badge-ready-text);   border: 1px solid var(--badge-ready-bord); }
.status-badge.idle    {
  background: var(--bg-badge-idle);
  color: var(--badge-idle-text);
  border: 1px solid var(--badge-idle-bord);
}

/* ── URL input row ── */
.url-row { display: flex; gap: 0.5rem; }
.url-input {
  flex: 1;
  background: var(--bg-input);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 0.45rem 0.75rem;
  color: var(--text);
  font-size: 0.8rem;
  outline: none;
  transition: border-color 0.15s, background 0.2s;
  min-width: 0;
}
.url-input:focus { border-color: var(--border-focus); }
.url-input::placeholder { color: var(--placeholder); }

.btn {
  background: var(--bg-btn);
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--text);
  padding: 0.45rem 0.875rem;
  cursor: pointer;
  font-size: 0.8rem;
  font-weight: 600;
  white-space: nowrap;
  transition: all 0.15s;
  flex-shrink: 0;
}
.btn:hover:not(:disabled) { border-color: var(--border-focus); }
.btn:active:not(:disabled) { transform: translateY(1px); }
.btn:disabled { opacity: 0.4; cursor: not-allowed; }
.btn-load { background: var(--bg-btn-load); border-color: var(--border-focus); color: var(--fade-btn-color); }
.btn-load:hover:not(:disabled) { border-color: #d4a828; }

/* ── YouTube player ── */
.player-wrapper {
  position: relative;
  width: 100%;
  padding-bottom: 56.25%;
  background: var(--player-empty-bg);
  border-radius: 8px;
  overflow: hidden;
  border: 1px solid var(--border);
  transition: background 0.3s, border-color 0.2s;
}
.player-wrapper > div,
.player-wrapper iframe {
  position: absolute;
  top: 0; left: 0;
  width: 100%; height: 100%;
  border: none;
}
.drag-shield {
  display: none;
  z-index: 10;
}
.player-placeholder {
  position: absolute;
  top: 0; left: 0; right: 0; bottom: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  gap: 0.875rem;
  z-index: 1;
}
.placeholder-waves { width: 72px; height: auto; color: var(--placeholder-icon); opacity: 0.7; }
.placeholder-title { font-size: 0.82rem; font-weight: 600; letter-spacing: 0.08em; color: var(--placeholder-icon); opacity: 0.9; }
.placeholder-hint  { font-size: 0.68rem; letter-spacing: 0.04em; color: var(--placeholder-icon); opacity: 0.55; }

/* ── Volume control ── */
.volume-row { display: flex; align-items: center; gap: 0.625rem; }
/* Volume belongs to the loaded track, so an empty deck hides the row outright - the same
   reasoning the progress row already follows. Toggled by Deck._setDeckEmpty() in player.js. */
.deck-panel.deck-empty .volume-row { display: none; }
.volume-label {
  font-size: 0.65rem; font-weight: 700; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--text-dim); width: 2.5rem; flex-shrink: 0;
}
.volume-slider {
  flex: 1; -webkit-appearance: none; appearance: none;
  height: 3px; background: linear-gradient(to right, #c9a227 0%, #c9a227 var(--fill-pct, 100%), var(--vol-track) var(--fill-pct, 100%), var(--vol-track) 100%);
  border-radius: 2px;
  outline: none; cursor: pointer; min-width: 0; transition: background 0.2s;
}
.volume-slider::-webkit-slider-thumb {
  -webkit-appearance: none; width: 13px; height: 13px;
  background: #c9a227; border-radius: 50%; cursor: pointer;
  transition: transform 0.1s, background 0.1s;
}
.volume-slider::-webkit-slider-thumb:hover { transform: scale(1.25); background: #d4a828; }
.volume-slider::-moz-range-thumb {
  width: 13px; height: 13px; background: #c9a227;
  border-radius: 50%; cursor: pointer; border: none;
}
.volume-value {
  font-size: 0.72rem; color: var(--text-muted);
  width: 2.5rem; text-align: right; font-variant-numeric: tabular-nums; flex-shrink: 0;
}

/* Wrapper that holds a volume slider + the balance recommendation marker */
.vol-slider-wrap {
  position: relative; flex: 1; display: flex; align-items: center;
}
/* Deck slider inside wrapper: inherit wrapper's width, don't double-flex */
.vol-slider-wrap .volume-slider { flex: none; width: 100%; }
/* Playlist slider inside wrapper */
.playlist-item-vol-row .vol-slider-wrap { flex: none; width: 80px; }
.vol-slider-wrap .playlist-item-vol-slider { width: 100%; }
.playlist-item-vol-row .vol-recommend-marker { transform: translate(-50%, calc(-100% - 1px)); }

/* ▾ marker showing the auto-balanced recommended position */
.vol-recommend-marker {
  position: absolute;
  top: 50%;
  transform: translate(-50%, calc(-100% - 5px));
  font-size: 1rem;
  line-height: 1;
  color: #f59e0b;
  display: none;
  user-select: none;
  cursor: default;
  z-index: 10;
}
.vol-recommend-marker::after {
  content: 'Recommended volume level';
  position: absolute;
  bottom: 180%;
  left: 50%;
  transform: translateX(-50%);
  white-space: nowrap;
  padding: 0.2rem 0.5rem;
  background: rgba(0, 0, 0, 0.85);
  color: #fff;
  font-size: 0.6rem;
  border-radius: 4px;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.12s;
  transition-delay: 0s;
  z-index: 200;
}
.vol-recommend-marker:hover::after {
  opacity: 1;
  transition-delay: 1s;
}

/* ── Deck button row (Play + Mute + Loop) ── */
.deck-btn-row { display: flex; gap: 0.4rem; width: 100%; }
.deck-btn-row .play-btn { flex: 1; }

.deck-aux-btn {
  flex-shrink: 0;
  padding: 0.55rem 0.65rem;
  background: var(--bg-play-btn); border: 1px solid var(--border);
  border-radius: 8px; color: var(--text-muted);
  font-size: 0.7rem; font-weight: 600; cursor: pointer;
  transition: all 0.15s; letter-spacing: 0.04em; white-space: nowrap;
}
.deck-aux-btn:hover:not(:disabled) { border-color: var(--border-focus); color: var(--text); }
.deck-aux-btn:disabled { opacity: 0.35; cursor: not-allowed; }
.deck-aux-btn.loop-active { border-color: var(--border-focus); color: var(--fade-btn-color); background: var(--bg-badge-ready); }

/* ── Play/Pause button ── */
.play-btn {
  width: 100%; padding: 0.55rem;
  background: var(--bg-play-btn); border: 1px solid var(--border);
  border-radius: 8px; color: var(--text-muted);
  font-size: 0.82rem; font-weight: 600; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  gap: 0.45rem; transition: all 0.15s; letter-spacing: 0.05em;
}
.play-btn:hover:not(:disabled) { border-color: var(--border-focus); color: var(--text); }
.play-btn:disabled { opacity: 0.35; cursor: not-allowed; }
.play-btn.playing { border-color: var(--btn-playing-border); color: var(--btn-playing-text); background: var(--btn-playing-bg); }
.play-btn.paused  { border-color: var(--btn-paused-border);  color: var(--btn-paused-text);  background: var(--btn-paused-bg); }

/* ── Track title box (draggable, shown under each deck) ── */
.track-title-box {
  width: 100%; padding: 0.5rem 0.75rem;
  background: var(--bg-play-btn); border: 1px solid var(--border);
  border-radius: 8px; color: var(--text-faint);
  font-size: 0.75rem; display: flex; align-items: center;
  gap: 0.5rem; transition: all 0.15s; min-height: 2.1rem;
  overflow: hidden; user-select: none;
}
.track-title-box.has-track { cursor: grab; color: var(--text-muted); }
.track-title-box.has-track:hover { border-color: var(--text-dim); color: var(--text); }
.track-title-box.has-track:active { cursor: grabbing; }
.track-title-box.drop-active { border-color: var(--border-focus) !important; background: var(--bg-btn-load); color: var(--text); }
.track-title-box.hotkey-selected { border-color: #d97706 !important; background: var(--hotkey-selected-bg); box-shadow: 0 0 0 1px rgba(217,119,6,0.25); color: var(--text); }
.track-title-handle { font-size: 0.8rem; color: var(--text-faint); flex-shrink: 0; line-height: 1; }
.track-title-content { flex: 1; min-width: 0; display: flex; align-items: center; overflow: hidden; }
.track-title-text { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; flex-shrink: 1; min-width: 0; font-style: italic; }
.track-title-box.has-track .track-title-text { font-style: normal; }
.track-title-orig { opacity: 0.5; font-size: 0.7rem; white-space: nowrap; flex-shrink: 0; }
.track-title-input {
  flex: 1; min-width: 0; background: var(--bg-input);
  border: 1px solid var(--border-focus); border-radius: 4px;
  padding: 0.1rem 0.4rem; color: var(--text); font-size: 0.75rem; outline: none;
}
.track-chip-btn {
  background: none; border: none; color: var(--text-faint);
  cursor: pointer; padding: 0.1rem 0.2rem; border-radius: 3px; flex-shrink: 0;
  display: flex; align-items: center; line-height: 1; transition: color 0.12s;
}
.track-chip-btn.edit-btn:hover   { color: #c9a227; }
.track-chip-btn.del-btn:hover    { color: #f87171; }
.track-chip-btn.manage-btn:hover { color: #2dd4bf; }

/* ── Transport controls ──
   Base is the narrow stacked column that mobile and touch mode both build on.
   The desktop layout turns it into a full-width horizontal row - see the
   "Desktop three-column layout" block below. */
.center-controls {
  display: flex; flex-direction: column;
  align-items: center; justify-content: flex-start;
  gap: 1rem; padding: 1.125rem 0.875rem;
}

/* ── FADE button ── */
.fade-btn {
  width: 100%; padding: 0.9rem 0.5rem;
  background: var(--bg-fade); border: 1px solid var(--border-focus);
  border-radius: 12px; color: var(--fade-btn-color);
  font-size: 0.85rem; font-weight: 700; letter-spacing: 0.2em;
  text-transform: uppercase; cursor: pointer; transition: all 0.2s;
  position: relative; overflow: hidden;
}
.fade-btn::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(160deg, rgba(201,162,39,0.2), rgba(139,26,26,0.2));
  opacity: 0; transition: opacity 0.2s;
}
.fade-btn:hover:not(:disabled)::after { opacity: 1; }
.fade-btn:hover:not(:disabled) { border-color: #d4a828; box-shadow: 0 0 18px rgba(201,162,39,0.35); }
.fade-btn:disabled { cursor: not-allowed; background: var(--fade-btn-dis-bg); border-color: var(--fade-btn-dis-bd); color: var(--fade-btn-dis-tx); }
.fade-btn.fading { border-color: #c9a227; color: #e8c86a; animation: pulse-glow 1.2s ease-in-out infinite; cursor: not-allowed; }
.silence-deck-indicator { font-size: 0.75rem; color: var(--text-faint); text-align: center; margin-top: -0.25rem; letter-spacing: 0; }

@keyframes pulse-glow {
  0%, 100% { box-shadow: 0 0 8px rgba(201,162,39,0.2); }
  50%       { box-shadow: 0 0 22px rgba(201,162,39,0.5); }
}

/* ── Fade progress ── */
/* The FADE progress bar and the Fade Duration control occupy the same grid cell and
   trade places when a fade starts, so the controls row never changes width. The
   hidden one keeps its box - visibility, not display - or the cell would collapse to
   the other's size and the buttons would shift anyway. The cell is sized by the
   duration control, the wider of the two, so the bar inherits its width. */
.fade-slot { display: grid; align-items: center; width: 100%; }
.fade-slot > * { grid-area: 1 / 1; }
.fade-slot > .fade-progress-wrap { visibility: hidden; }
.fade-slot.fading > .fade-progress-wrap { visibility: visible; }
.fade-slot.fading > .setting-group { visibility: hidden; }
.fade-progress-wrap { width: 100%; }
.fade-progress-bar { width: 100%; height: 3px; background: var(--progress-track); border-radius: 2px; overflow: hidden; }
.fade-progress-fill { height: 100%; background: linear-gradient(90deg, #c9a227, #e8a020); border-radius: 2px; width: 0%; transition: width 0.08s linear; }
.fade-progress-label { font-size: 0.6rem; color: var(--text-dim); text-align: center; margin-top: 0.3rem; letter-spacing: 0.08em; font-variant-numeric: tabular-nums; }

/* ── Balance button (center controls, Pro) ── */
.balance-center-btn {
  width: 100%; padding: 0.6rem 0.5rem; min-height: 44px;
  background: var(--balance-btn-bg);
  border: 1px solid var(--balance-btn-border);
  border-radius: 10px; color: var(--balance-btn-text);
  font-size: 0.72rem; font-weight: 700; letter-spacing: 0.16em;
  text-transform: uppercase; cursor: pointer; transition: all 0.2s;
}
.balance-center-btn:focus-visible { outline: 2px solid var(--balance-btn-text); outline-offset: 2px; }
.balance-center-btn:hover:not(:disabled) { border-color: var(--balance-btn-text); box-shadow: 0 0 14px rgba(245,158,11,0.25); }
.balance-center-btn:disabled { cursor: not-allowed; opacity: 0.6; }

/* ── Combine button (center controls, Pro) ── */
.combine-center-btn {
  width: 100%; padding: 0.6rem 0.5rem; min-height: 44px;
  background: var(--combine-btn-bg);
  border: 1px solid var(--combine-btn-border);
  border-radius: 10px; color: var(--combine-btn-text);
  font-size: 0.72rem; font-weight: 700; letter-spacing: 0.16em;
  text-transform: uppercase; cursor: pointer; transition: all 0.2s;
}
.combine-center-btn:focus-visible { outline: 2px solid var(--combine-btn-text); outline-offset: 2px; }
.combine-center-btn:hover:not(:disabled) { border-color: var(--combine-btn-text); box-shadow: 0 0 14px rgba(13,148,136,0.25); }
.combine-center-btn:disabled { cursor: not-allowed; opacity: 0.4; }

/* ── Fade Effect button (center controls, Pro) ── */
.fade-effect-btn {
  width: 100%; padding: 0.6rem 0.5rem; min-height: 44px;
  background: var(--fade-effect-btn-bg);
  border: 1px solid var(--fade-effect-btn-border);
  border-radius: 10px; color: var(--fade-effect-btn-text);
  font-size: 0.72rem; font-weight: 700; letter-spacing: 0.16em;
  text-transform: uppercase; cursor: pointer; transition: all 0.2s;
}
.fade-effect-btn:focus-visible { outline: 2px solid var(--fade-effect-btn-hover-border); outline-offset: 2px; }
.fade-effect-btn:hover:not(:disabled) { border-color: var(--fade-effect-btn-hover-border); box-shadow: 0 0 14px rgba(139,92,246,0.25); }
.fade-effect-btn.effect-active { border-color: var(--fade-effect-btn-hover-border); background: rgba(139,92,246,0.12); }

/* ── Fade Effect Picker modal content ── */
.fe-list { display: flex; flex-direction: column; gap: 0.5rem; }
.fe-list-item {
  display: flex; align-items: center; gap: 0.75rem;
  padding: 0.6rem 0.75rem; min-height: 44px;
  background: var(--bg-input); border: 1px solid var(--border);
  border-radius: 8px; cursor: pointer; text-align: left; width: 100%;
  color: var(--text); font-size: 0.88rem; transition: border-color 0.15s;
}
.fe-list-item:hover { border-color: var(--fade-effect-btn-hover-border); }
.fe-list-item:focus-visible { outline: 2px solid var(--fade-effect-btn-hover-border); outline-offset: 2px; }
.fe-list-num { font-size: 0.75rem; color: var(--text-muted); min-width: 1.25rem; }
.fe-list-name { flex: 1; font-weight: 600; }
.fe-list-count { font-size: 0.75rem; color: var(--text-muted); }
.fe-board-title {
  font-size: 0.78rem; font-weight: 700; color: var(--text-muted);
  letter-spacing: 0.1em; text-transform: uppercase; margin: 0.5rem 0 0.75rem;
}
.fe-back-btn { margin-bottom: 0.75rem; }
.fe-sound-grid { display: flex; flex-direction: column; gap: 0.4rem; }
.fe-sound-item {
  display: flex; align-items: center; gap: 0.75rem;
  padding: 0.5rem 0.75rem; min-height: 44px;
  background: var(--bg-input); border: 1px solid var(--border);
  border-radius: 8px; cursor: pointer; text-align: left; width: 100%;
  color: var(--text); transition: border-color 0.15s, background 0.15s;
}
.fe-sound-item:focus-visible { outline: 2px solid var(--fade-effect-btn-hover-border); outline-offset: 2px; }
.fe-sound-item:hover, .fe-sound-item.fe-active {
  border-color: var(--fade-effect-btn-hover-border);
  background: rgba(139,92,246,0.1);
}
.fe-sound-key {
  font-family: var(--font-mono); font-size: 0.75rem; font-weight: 700;
  padding: 0.15rem 0.4rem; background: var(--bg-btn); border-radius: 4px;
  min-width: 1.75rem; text-align: center; color: var(--fade-effect-btn-text);
}
.fe-sound-name { flex: 1; font-size: 0.88rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fe-current-label {
  font-size: 0.78rem; color: var(--fade-effect-btn-text);
  margin-bottom: 0.75rem; padding: 0.4rem 0.6rem;
  border: 1px solid var(--fade-effect-btn-border);
  border-radius: 6px; background: rgba(139,92,246,0.08);
}
.fe-hint { font-size: 0.78rem; color: var(--text-muted); text-align: center; margin-top: 0.75rem; }

/* ── Combine manage modal rows ── */
.combine-manage-row {
  display: flex; flex-direction: column; gap: 0.4rem;
  padding: 0.625rem 0.75rem;
  background: var(--bg-input); border: 1px solid var(--border); border-radius: 8px;
}
.combine-manage-name {
  font-size: 0.82rem; color: var(--text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.combine-manage-vol-row {
  display: flex; align-items: center; gap: 0.5rem;
}
.combine-manage-vol-label {
  font-size: 0.6rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.08em; color: var(--text-dim); flex-shrink: 0; width: 1.75rem;
}
.combine-manage-actions { display: flex; justify-content: flex-end; gap: 0.4rem; margin-top: 0.1rem; }
.combine-manage-row--focused { background: var(--bg-panel); border-color: var(--border-focus); }
.layer-mute-btn {
  font-size: 0.65rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em;
  padding: 0.2rem 0.45rem; flex-shrink: 0;
  background: var(--bg-input); border: 1px solid var(--border); border-radius: 4px;
  color: var(--text-dim); cursor: pointer;
  transition: background 0.12s, color 0.12s, border-color 0.12s;
}
.layer-mute-btn:hover { background: var(--bg-panel); color: var(--text); }
.layer-mute-btn--active {
  background: rgba(239, 68, 68, 0.15);
  border-color: #ef4444; color: #ef4444;
}
@media (pointer: coarse) { .layer-mute-btn { min-height: 44px; padding: 0.6rem 0.45rem; } }
.layer-fade-row { display: flex; gap: 0.4rem; }
.layer-fade-btn {
  flex: 1; font-size: 0.7rem; padding: 0.22rem 0.4rem;
  background: var(--bg-input); border: 1px solid var(--border); border-radius: 4px;
  color: var(--text-dim); cursor: pointer; text-align: center;
  transition: background 0.12s, color 0.12s, border-color 0.12s;
}
.layer-fade-btn:hover { background: var(--bg-panel); color: var(--text); }
.layer-fade-btn--active {
  background: rgba(20, 184, 166, 0.12);
  border-color: #14b8a6; color: #14b8a6;
}
.layer-fade-btn:disabled { opacity: 0.45; cursor: not-allowed; }
.layer-fade-btn:disabled:hover { background: var(--bg-input); color: var(--text-dim); }
@media (pointer: coarse) {
  .layer-fade-btn { min-height: 44px; padding: 0.6rem 0.4rem; }
}

/* ── Balance progress (under Balance button) ── */
/* display:contents keeps this wrapper out of the layout entirely, so at mobile widths
   and in touch mode the button and the bar stay direct flex children of
   .center-controls and lay out exactly as they did before the wrapper existed. The
   desktop block turns it into a real positioned box.
   Hidden outright until the tier check confirms Pro, rather than left empty: an empty
   flex item still claims one of .center-controls' 1rem gaps. */
.balance-slot { display: none; }
.balance-slot.visible { display: contents; }
.balance-progress-wrap { width: 100%; }
.balance-progress-bar { width: 100%; height: 3px; background: var(--progress-track); border-radius: 2px; overflow: hidden; }
.balance-progress-fill { height: 100%; background: linear-gradient(90deg, #d97706, #f59e0b); border-radius: 2px; width: 0%; transition: width 0.15s linear; }
/* Squeezed to the button's width on desktop, so the track name is clipped rather than
   wrapped onto a second line. The track is audibly playing while it is measured, so the
   name is a nicety - the done/total count in front of it is the part that matters. */
.balance-progress-label { font-size: 0.58rem; color: #d97706; text-align: center; margin-top: 0.3rem; letter-spacing: 0.06em; font-variant-numeric: tabular-nums; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* ── Fade duration ── */
.setting-group { width: 100%; display: flex; flex-direction: column; gap: 0.4rem; }
.setting-label { font-size: 0.6rem; font-weight: 700; letter-spacing: 0.12em; text-transform: uppercase; color: var(--text-dim); text-align: center; }
.setting-row { display: flex; align-items: center; justify-content: center; gap: 0.4rem; }
.duration-input {
  width: 52px; background: var(--bg-input); border: 1px solid var(--border);
  border-radius: 6px; padding: 0.35rem 0.5rem; color: var(--text);
  font-size: 0.82rem; text-align: center; outline: none; transition: border-color 0.15s, background 0.2s;
}
.duration-input:focus { border-color: var(--border-focus); }
.setting-unit { font-size: 0.7rem; color: var(--text-dim); }

/* ── Local Library (file list lives inside the Local Library modal) ── */
.local-library-conn {
  display: flex; flex-direction: column; gap: 0.75rem; flex-shrink: 0;
}
.local-library-files {
  display: flex; flex-direction: column; gap: 0.625rem;
  min-height: 0; flex: 1;
  padding-top: 0.75rem; border-top: 1px solid var(--border);
}
.local-library-action-row { display: flex; gap: 0.5rem; }
#local-file-list {
  display: flex; flex-direction: column; gap: 0.25rem;
  min-height: 2.5rem; max-height: 48vh; overflow-y: auto;
  padding: 2px 3px; margin: -2px -3px;
}
#local-file-list:focus-visible { outline: 2px solid var(--border-focus); outline-offset: 2px; border-radius: 6px; }
.local-file-empty {
  padding: 1.25rem; text-align: center; color: var(--text-faint);
  font-size: 0.75rem; border: 1px dashed var(--border); border-radius: 8px;
}
.local-file-row {
  display: flex; align-items: center; gap: 0.5rem; padding: 0.45rem 0.625rem;
  background: var(--bg-input); border: 1px solid var(--border);
  border-radius: 6px; cursor: pointer; transition: border-color 0.1s;
  user-select: none;
}
.local-file-row:hover { border-color: var(--text-dim); }
.local-file-row.is-checked { border-color: var(--border-focus); background: var(--hotkey-selected-bg); }
/* The row is a <label>, so the checkbox is what takes focus - keep the ring on it. */
.local-file-checkbox {
  flex-shrink: 0; width: 16px; height: 16px; margin: 0;
  accent-color: #c9a227; cursor: pointer;
}
.local-file-checkbox:focus-visible { outline: 2px solid var(--border-focus); outline-offset: 2px; }
.local-file-icon { color: var(--text-faint); font-size: 0.75rem; flex-shrink: 0; }
.local-file-name {
  flex: 1; font-size: 0.78rem; color: var(--text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.local-file-check { font-size: 0.75rem; color: #c9a227; flex-shrink: 0; }
.local-player-container { justify-content: center; align-items: center; }
.local-player-container.combined-clickable { cursor: pointer; }
.local-player-container.combined-clickable:hover { background: rgba(45,212,191,0.04); }

/* ── Soundboard source toggle ── */
.sb-source-toggle { display: flex; gap: 0.375rem; margin-bottom: 0.25rem; }
.sb-source-btn {
  flex: 1; padding: 0.35rem 0.75rem; font-size: 0.78rem; font-weight: 600;
  border-radius: 7px; border: 1px solid var(--border);
  background: var(--bg-input); color: var(--text-muted); cursor: pointer;
  transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.sb-source-btn.active {
  background: #c9a227; border-color: #c9a227; color: #1a1000;
}

/* ── Playlist section ── */
.playlist-section {
  margin: 0 1.25rem 1.25rem;
  background: var(--bg-panel-solid);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 1rem 1.125rem;
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  transition: background 0.2s, border-color 0.2s;
}
.playlist-top {
  display: flex; align-items: center; gap: 1rem; flex-wrap: wrap;
}
.playlist-header-label {
  font-family: var(--font-display);
  font-size: 0.65rem; font-weight: 700; letter-spacing: 0.18em;
  text-transform: uppercase; color: var(--text-muted); flex-shrink: 0;
}
.playlist-add-row { flex: 1; display: flex; gap: 0.5rem; min-width: 120px; }
.playlist-items {
  display: flex; flex-direction: column; gap: 0.25rem; min-height: 2.5rem;
}
.playlist-items:focus-visible { outline: 2px solid var(--border-focus); outline-offset: 3px; border-radius: 6px; }

/* ── Desktop three-column layout ──────────────────────────────────────────
   Deck A | Playlist | Deck B on the first flex line, transport controls
   wrapping onto a full-width row beneath them. Scoped to wide non-touch
   viewports so the mobile and touch-mode layouts are left completely alone. */
@media (min-width: 861px) {
  body:not(.touch-mode) .decks-row > .playlist-section {
    margin: 0;
    align-self: stretch;
    flex: 0 1 clamp(230px, 26%, 380px);
    min-width: 0;
    max-height: calc(100dvh - var(--app-chrome));
    overflow: hidden;
  }
  body:not(.touch-mode) .decks-row > .playlist-section .playlist-top { flex-shrink: 0; }
  body:not(.touch-mode) .decks-row > .playlist-section .playlist-items {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    padding: 2px 3px;
    margin: -2px -3px;
  }

  /* Settings > Preferences > Playlist position. "Centre" needs no rule - the DOM
     order is already Deck A, Playlist, Deck B, controls. */
  body:not(.touch-mode).playlist-left  .decks-row > .playlist-section { order: -1; }
  body:not(.touch-mode).playlist-right .decks-row > .playlist-section { order: 1; }

  body:not(.touch-mode) .center-controls {
    order: 10;
    flex: 0 0 100%;
    width: 100%;
    flex-direction: row; flex-wrap: wrap;
    align-items: center; justify-content: center;
    gap: 0.6rem 1rem;
    padding: 0.25rem 0 0;
  }
  body:not(.touch-mode) .center-controls .fade-btn { width: auto; min-width: 190px; padding: 0.8rem 1.75rem; }
  body:not(.touch-mode) .center-controls .setting-group { width: auto; flex-direction: row; align-items: center; gap: 0.6rem; }
  body:not(.touch-mode) .center-controls .balance-center-btn,
  body:not(.touch-mode) .center-controls .combine-center-btn,
  body:not(.touch-mode) .center-controls .fade-effect-btn { width: auto; padding: 0.6rem 1.25rem; }
  /* The button relabels itself Balance -> Cancel for the length of a run, and Cancel is
     the narrower word, so its natural width alone made the row twitch every time a run
     started or ended. 10em clears the widest label in the widest theme (Balance in
     Cinzel measures 9.65em), so the box is the same either way. */
  body:not(.touch-mode) .center-controls .balance-center-btn { min-width: 10em; }
  body:not(.touch-mode) .center-controls .skel-pro-btn { width: 112px; }
  body:not(.touch-mode) .center-controls .skel-pro-btn.wide { width: 140px; }
  body:not(.touch-mode) .center-controls .fade-slot { width: auto; }
  /* The bar hangs just under the Balance/Cancel button and is out of flow, so a run
     starting can't reflow the row - it used to be a 420px flex item wedged in beside
     the button, which shoved COMBINE and ADD EFFECT sideways for the whole run. */
  body:not(.touch-mode) .center-controls .balance-slot.visible { display: block; position: relative; }
  body:not(.touch-mode) .center-controls .balance-progress-wrap {
    position: absolute; top: calc(100% + 5px); left: 0; right: 0; width: auto;
  }
  body:not(.touch-mode) .center-controls .silence-deck-indicator { margin-top: 0; }
}
.playlist-empty {
  padding: 1.25rem; text-align: center; color: var(--text-faint);
  font-size: 0.75rem; letter-spacing: 0.05em; border: 1px dashed var(--border);
  border-radius: 8px;
}
.playlist-empty.drop-active { border-color: var(--border-focus); color: var(--text-dim); }

/* ── Playlist item ── */
.playlist-item {
  display: flex; align-items: center; gap: 0.5rem; padding: 0.45rem 0.625rem;
  background: var(--bg-input); border: 1px solid var(--border);
  border-radius: 6px; cursor: grab; transition: border-color 0.1s;
  user-select: none; position: relative;
}
.playlist-item:hover { border-color: var(--text-dim); }
.playlist-item.dragging { opacity: 0.35; }
.playlist-item.drop-before { border-top: 2px solid var(--border-focus); }
.playlist-item.drop-after  { border-bottom: 2px solid var(--border-focus); }
.playlist-item.hotkey-selected { border-color: #d97706; background: var(--hotkey-selected-bg); box-shadow: 0 0 0 1px rgba(217,119,6,0.25); }
.playlist-item.local-unavailable { opacity: 0.4; cursor: default; }
.playlist-item.local-unavailable .playlist-item-del { pointer-events: all; cursor: pointer; opacity: 1; }

.playlist-item-handle { color: var(--text-faint); font-size: 1rem; cursor: grab; flex-shrink: 0; line-height: 1; }
.playlist-item-info { flex: 1; min-width: 0; }
.playlist-item-title-row { display: flex; align-items: center; gap: 0.3rem; min-width: 0; }
.playlist-item-edit {
  background: none; border: none; color: var(--text-faint);
  cursor: pointer; padding: 0.1rem; border-radius: 3px; flex-shrink: 0;
  display: flex; align-items: center; transition: color 0.12s; line-height: 1;
}
.playlist-item-edit:hover { color: #c9a227; }
.playlist-item-title {
  font-size: 0.78rem; color: var(--text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; flex-shrink: 0; max-width: 100%; min-width: 0;
}
.playlist-item-title-input {
  flex: 1; min-width: 0; background: var(--bg-input); border: 1px solid var(--border-focus);
  border-radius: 4px; padding: 0.1rem 0.4rem; color: var(--text); font-size: 0.78rem; outline: none;
}
.playlist-item-orig-title { opacity: 0.5; font-size: 0.72rem; flex-shrink: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.playlist-item-author { font-size: 0.65rem; color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-top: 0.1rem; }

.playlist-item-vol-row { display: flex; align-items: center; gap: 0.4rem; margin-top: 0.25rem; }
.playlist-item-vol-icon { font-size: 0.55rem; font-weight: 700; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.05em; }
.playlist-item-vol-slider {
  width: 80px; height: 3px; -webkit-appearance: none; appearance: none;
  background: linear-gradient(to right, #c9a227 0%, #c9a227 var(--fill-pct, 100%), var(--progress-track) var(--fill-pct, 100%), var(--progress-track) 100%);
  border-radius: 2px; outline: none; cursor: pointer;
}
.playlist-item-vol-slider::-webkit-slider-thumb { -webkit-appearance: none; width: 8px; height: 8px; background: #c9a227; border-radius: 50%; }
.playlist-item-vol-display { font-size: 0.65rem; color: var(--text-faint); min-width: 24px; }
.playlist-item-del {
  background: none; border: none; color: var(--text-faint);
  cursor: pointer; padding: 0.25rem 0.3rem; border-radius: 4px;
  display: flex; align-items: center; flex-shrink: 0; transition: color 0.12s;
}
.playlist-item-del:hover { color: #f87171; }

.playlist-item-fade {
  background: none; border: none; color: var(--text-faint);
  cursor: pointer; padding: 0.25rem 0.3rem; border-radius: 4px;
  display: flex; align-items: center; flex-shrink: 0; transition: color 0.12s;
}
.playlist-item-fade:hover { color: #e8c86a; }

.playlist-item-manage {
  background: none; border: none; color: var(--text-faint);
  cursor: pointer; padding: 0.25rem 0.3rem; border-radius: 4px;
  display: flex; align-items: center; flex-shrink: 0; transition: color 0.12s;
}
.playlist-item-manage:hover { color: #2dd4bf; }

/* ── Touch Action Menu ── */
.touch-action-menu {
  position: fixed; z-index: 8500;
  background: var(--bg-panel-solid); border: 1px solid var(--border);
  border-radius: 10px; box-shadow: 0 8px 28px rgba(0,0,0,0.55);
  display: flex; flex-direction: column; overflow: hidden; min-width: 160px;
}
.touch-action-btn {
  background: none; border: none; border-bottom: 1px solid var(--border);
  color: var(--text); font-size: 0.88rem; padding: 0.9rem 1.25rem;
  text-align: left; cursor: pointer; white-space: nowrap;
  touch-action: manipulation; -webkit-touch-callout: none; user-select: none;
}
.touch-action-btn:last-child { border-bottom: none; }
.touch-action-btn:active { background: var(--bg-btn-load); }
body.touch-mode .touch-action-btn { min-height: 0; }

/* ── Deck Options Button (hidden unless in touch mode) ── */
.deck-options-btn { display: none; }

/* ── Deck Options Modal actions ── */
.deck-options-action-btn {
  width: 100%; text-align: center;
  background: var(--bg-btn-load); border: 1px solid var(--border);
  border-radius: 8px; color: var(--text); padding: 0.75rem;
  font-size: 0.85rem; cursor: pointer; transition: all 0.15s; font-weight: 500;
}
.deck-options-action-btn:hover { border-color: var(--border-focus); }
.deck-options-action-btn.is-active { color: #c9a227; border-color: #c9a227; }
.deck-options-action-btn.is-danger { color: #f87171; border-color: #7f2020; background: #1c0a0a; }
.deck-options-action-btn.is-danger:hover { border-color: #f87171; }

/* ── Control Scheme Settings ── */
.scheme-btn-group { display: flex; }
.scheme-btn {
  background: var(--bg-btn);
  border: 1px solid var(--border);
  color: var(--text-muted);
  padding: 0.3rem 0.75rem;
  font-size: 0.8rem;
  font-family: inherit;
  cursor: pointer;
  transition: all 0.15s;
  margin-left: -1px;
}
.scheme-btn:first-child { border-radius: 7px 0 0 7px; margin-left: 0; }
.scheme-btn:last-child  { border-radius: 0 7px 7px 0; }
.scheme-btn.active {
  background: var(--bg-play-btn);
  border-color: var(--border-focus);
  color: var(--text);
  position: relative;
  z-index: 1;
}
.scheme-btn:hover:not(.active) { border-color: var(--border); color: var(--text); }
.scheme-hint { font-size: 0.75rem; color: var(--text-muted); font-style: italic; }

/* ── Touch Mode Overrides ── */

/* Header: flat single row, logo/subtitle hidden */
body.touch-mode header {
  display: flex; align-items: center; justify-content: space-between;
  padding: 0.4rem 0.6rem; text-align: left;
}
body.touch-mode .header-logo,
body.touch-mode .header-subtitle { display: none; }
body.touch-mode .header-left { display: contents; }
body.touch-mode .header-extra-links { display: contents; }
body.touch-mode .header-btn .btn-label { display: none; }
body.touch-mode #spotify-connect-btn { display: none !important; }
body.touch-mode #user-name { display: none; }
body.touch-mode #perpetual-badge { display: none !important; }

/* Deck row: side-by-side, with center-controls pushed below as a full-width row */
body.touch-mode .decks-row {
  flex-direction: row !important;
  flex-wrap: wrap;
  gap: 0.4rem;
  padding: 0.5rem 0.5rem 0;
  align-items: stretch;
}
body.touch-mode .decks-row .panel { flex: 1 1 0; min-width: 0; gap: 0.5rem; }
body.touch-mode .decks-row .center-controls { order: 10; flex: 0 0 100%; width: 100%; }
/* Playlist drops to its own full-width row below the controls, as it always has.
   Deck A and Deck B keep order 0 so they share the first line. */
body.touch-mode .decks-row > .playlist-section {
  order: 20;
  /* 0.75rem margin + the row's 0.5rem side padding = the 1.25rem inset the
     playlist had when it sat outside .decks-row. The basis has to subtract the
     margins or the line overflows. */
  flex: 0 0 calc(100% - 1.5rem);
  margin: 0 0.75rem 0.5rem;
}

/* Compact deck panels: hide player iframe, URL input, and progress bar */
body.touch-mode .player-wrapper { display: none; }
body.touch-mode .url-row { display: none; }
body.touch-mode .progress-row { display: none; }
body.touch-mode .panel { padding: 0.65rem 0.75rem; }

/* Deck button row: Play dominant, mute/loop hidden, Options shown */
body.touch-mode .deck-btn-row { gap: 0.3rem; }
body.touch-mode .deck-btn-row .play-btn { flex: 3; }
body.touch-mode .deck-btn-row .deck-aux-btn { display: none; }
body.touch-mode .deck-options-btn { display: flex !important; flex: 1; align-items: center; justify-content: center; }
body.touch-mode .track-chip-btn.edit-btn,
body.touch-mode .track-chip-btn.del-btn { display: none !important; }

/* Center controls: full-width horizontal layout with two-column pairing */
body.touch-mode .center-controls {
  flex-direction: row; flex-wrap: wrap; align-items: stretch;
  gap: 0.4rem; padding: 0.5rem 0.25rem 0.75rem;
  position: static; width: 100%;
}
body.touch-mode #fade-btn { flex: 0 0 100%; padding: 0.75rem 0.5rem; }
body.touch-mode #silence-btn { flex: 1 1 calc(50% - 0.25rem); padding: 0.6rem 0.25rem; font-size: 0.72rem; }
body.touch-mode .center-controls .fade-slot { flex: 0 0 calc(50% - 0.25rem); width: auto; }
body.touch-mode .center-controls .setting-group {
  width: auto; align-items: center; justify-content: center;
}
body.touch-mode .center-controls .setting-group .setting-label { display: none; }
body.touch-mode #silence-deck-indicator { display: none; }
body.touch-mode #balance-btn { flex: 1 1 calc(50% - 0.25rem); }
body.touch-mode #balance-progress-wrap { flex: 0 0 100%; }
body.touch-mode #combine-btn { flex: 1 1 calc(50% - 0.25rem); }
body.touch-mode #fade-effect-btn { flex: 1 1 100%; }

/* Larger slider thumbs */
body.touch-mode .volume-slider::-webkit-slider-thumb,
body.touch-mode .playlist-item-vol-slider::-webkit-slider-thumb { width: 24px; height: 24px; }
body.touch-mode .volume-slider::-moz-range-thumb,
body.touch-mode .playlist-item-vol-slider::-moz-range-thumb { width: 24px; height: 24px; border: none; }

/* Touch-friendly buttons */
body.touch-mode button { min-height: 44px; touch-action: manipulation; }

/* Range inputs */
body.touch-mode input[type="range"] { touch-action: pan-y; }

/* Playlist items */
body.touch-mode .playlist-item {
  padding-top: 0.7rem; padding-bottom: 0.7rem;
  user-select: none; -webkit-touch-callout: none;
}
/* Whole row is the tap target, but grow the box itself too */
body.touch-mode .local-file-checkbox { width: 24px; height: 24px; }

/* Deck chip */
body.touch-mode .track-title-box { -webkit-touch-callout: none; user-select: none; touch-action: none; }

/* Hide small icon buttons in playlist - action menu handles these */
body.touch-mode .playlist-item-edit,
body.touch-mode .playlist-item-del,
body.touch-mode .playlist-item-fade,
body.touch-mode .playlist-item-handle { display: none; }

.thumb-tooltip {
  position: fixed; z-index: 9999; border-radius: 8px; overflow: hidden;
  border: 1px solid var(--border); box-shadow: 0 8px 32px rgba(0,0,0,0.5);
  pointer-events: none; display: none; background: var(--tooltip-bg);
}
.thumb-tooltip img { display: block; width: 240px; height: auto; }

footer {
  text-align: center; padding: 0.75rem; border-top: 1px solid var(--border);
  color: var(--footer-text); font-size: 0.7rem; letter-spacing: 0.06em;
}
footer a { color: #c9a227; text-decoration: none; }
footer a:hover { text-decoration: underline; }

.header-left {
  position: absolute; left: 1rem; top: 50%; transform: translateY(-50%);
  display: flex; gap: 0.4rem; align-items: center;
}
.header-btn {
  background: var(--bg-btn); border: 1px solid var(--border);
  border-radius: 8px; color: var(--toggle-text);
  padding: 0.4rem 0.6rem; cursor: pointer; font-size: 0.65rem; font-weight: 600;
  letter-spacing: 0.06em; text-transform: uppercase; white-space: nowrap;
  transition: all 0.15s; display: flex; align-items: center; gap: 0.3rem; line-height: 1;
}
.header-btn:hover { border-color: var(--border-focus); color: var(--text); }
.header-btn.danger:hover { border-color: #f87171; color: #f87171; }

.modal-overlay {
  position: fixed; inset: 0; background: rgba(0,0,0,0.65);
  z-index: 2000; display: none; align-items: center; justify-content: center; padding: 1rem;
}
.modal-overlay.visible { display: flex; }
#confirm-modal { z-index: 2500; }
/* The Loop Range editor can be opened on top of the MANAGE modal (per-layer loop ranges). */
#loop-range-modal { z-index: 2100; }
.modal-panel {
  background: var(--bg-panel-solid); border: 1px solid var(--border);
  border-radius: 14px; padding: 1.375rem; width: 100%; max-width: 500px;
  max-height: 85vh; display: flex; flex-direction: column; gap: 0.875rem;
  box-shadow: 0 24px 64px rgba(0,0,0,0.55);
}
.modal-header { display: flex; align-items: center; justify-content: space-between; flex-shrink: 0; }
.modal-title { font-size: 0.78rem; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase; color: var(--text-muted); }
.modal-close {
  background: none; border: none; color: var(--text-faint); cursor: pointer;
  font-size: 1rem; line-height: 1; padding: 0.2rem 0.3rem; border-radius: 4px;
}
.modal-close:hover { color: var(--text); }

.modal-save-row { display: flex; gap: 0.5rem; flex-shrink: 0; }
.modal-name-input {
  flex: 1; background: var(--bg-input); border: 1px solid var(--border);
  border-radius: 6px; padding: 0.45rem 0.75rem; color: var(--text);
  font-size: 0.8rem; outline: none; transition: border-color 0.15s;
}
.modal-name-input:focus { border-color: var(--border-focus); }

.session-list { overflow-y: auto; display: flex; flex-direction: column; gap: 0.3rem; flex: 1; min-height: 3rem; }
.session-list-empty {
  text-align: center; color: var(--text-faint); font-size: 0.78rem;
  padding: 1.5rem; border: 1px dashed var(--border); border-radius: 8px;
}
.session-item {
  display: flex; align-items: center; gap: 0.4rem; padding: 0.5rem 0.625rem;
  background: var(--bg-input); border: 1px solid var(--border); border-radius: 8px;
}
.session-item-name { flex: 1; font-size: 0.82rem; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.session-item-btn {
  background: none; border: 1px solid var(--border); border-radius: 5px;
  color: var(--text-muted); cursor: pointer; font-size: 0.7rem; font-weight: 600;
  padding: 0.22rem 0.5rem; transition: all 0.12s;
}
.session-item-btn:hover { border-color: var(--border-focus); color: var(--text); }
.session-item-btn.del:hover { border-color: #f87171; color: #f87171; }

.modal-body { flex: 1; overflow-y: auto; display: flex; flex-direction: column; gap: 0.875rem; min-height: 0; }
.modal-footer { display: flex; gap: 0.5rem; padding-top: 0.25rem; border-top: 1px solid var(--border); }
.modal-footer .btn { flex: 1; font-size: 0.75rem; text-align: center; }

/* ── Hotkey Config Modal ── */
.hotkey-panel { max-width: 360px; }
.hotkey-note { font-size: 0.78rem; color: var(--text-muted); line-height: 1.5; margin: 0; }
.hotkey-section-label { font-size: 0.7rem; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; color: var(--text-faint); }
.hotkey-list { display: flex; flex-direction: column; gap: 0.5rem; }
.hotkey-row { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
.hotkey-row.auto-load-row { cursor: pointer; min-height: 1.75rem; }
body.touch-mode .hotkey-row.auto-load-row { min-height: 44px; }
.hotkey-row.auto-load-row input[type="checkbox"] { width: 18px; height: 18px; accent-color: var(--border-focus); cursor: pointer; flex-shrink: 0; }
.hotkey-action { font-size: 0.82rem; color: var(--text); }
.hotkey-modifier { font-size: 0.72rem; color: var(--text-faint); font-weight: 400; }
.hotkey-capture-btn {
  min-width: 96px; padding: 0.35rem 0.75rem; text-align: center;
  background: var(--bg-input); border: 1px solid var(--border);
  border-radius: 6px; color: var(--text); font-size: 0.8rem; font-weight: 600;
  cursor: pointer; transition: all 0.15s; font-family: inherit;
}
.hotkey-capture-btn:hover { border-color: var(--border-focus); }
.hotkey-capture-btn.listening {
  border-color: var(--balance-btn-border); color: var(--listening-color);
  animation: pulse-amber 1s ease-in-out infinite;
}
@keyframes pulse-amber {
  0%, 100% { box-shadow: 0 0 0 0 rgba(217,119,6,0); }
  50%       { box-shadow: 0 0 0 3px rgba(217,119,6,0.3); }
}

.confirm-panel { max-width: 380px; }
.confirm-msg { font-size: 0.85rem; color: var(--text-muted); line-height: 1.6; }
.confirm-btns { display: flex; gap: 0.5rem; justify-content: flex-end; }
.btn-danger { background: var(--btn-danger-bg); border-color: var(--btn-danger-border); color: var(--btn-danger-text); }
.btn-danger:hover:not(:disabled) { border-color: #f87171; color: var(--btn-danger-text); }

@media (max-width: 860px) {
  .decks-row { flex-direction: column; flex-wrap: nowrap; gap: 0; }
  .center-controls {
    width: 100%; flex-direction: row; flex-wrap: wrap; justify-content: center; position: static; padding: 0.5rem 1rem;
  }
  .fade-btn { width: auto; padding: 0.75rem 2rem; }
  .header-btn span.btn-label { display: none; }
  /* Not enough room for three columns - everything stacks and the page scrolls, in
     the same visual order as before this layout existed: Deck A, transport
     controls, Deck B, playlist. DOM order is tuned for the desktop columns, so
     these orders put the stack back the way it was. */
  .decks-row > .deck-panel-a    { order: 0; }
  .decks-row > .center-controls { order: 1; }
  .decks-row > .deck-panel-b    { order: 2; }
  .decks-row > .playlist-section {
    order: 3;
    /* 0.25rem bottom + the row's 1rem padding-bottom reproduces the 1.25rem gap
       the playlist had when it was a direct block child of <main>. */
    margin: 1rem 0 0.25rem;
    /* .decks-row is align-items:flex-start, so without this the playlist would
       shrink to its content width instead of spanning the column like it did
       when it was a direct block child of <main>. */
    align-self: stretch;
  }
  .playlist-add-row { min-width: 200px; }
  #local-file-list { max-height: none; }
}

/* ── Content Overlays ── */
.header-extra-links { position: absolute; right: 1rem; top: 50%; transform: translateY(-50%); display: flex; gap: 8px; }
.header-btn-icon {
  background: var(--bg-btn); border: 1px solid var(--border);
  color: var(--text-muted); cursor: pointer; padding: 8px; display: flex;
  align-items: center; justify-content: center; transition: all 0.2s; border-radius: 8px;
  min-width: 44px; min-height: 44px;
}
.header-btn-icon:hover { color: var(--text); border-color: var(--border-focus); }
.content-overlay {
  position: fixed; inset: 0; background: rgba(0,0,0,0.85); backdrop-filter: blur(8px);
  z-index: 3000; display: flex; align-items: flex-start; justify-content: center; padding: 40px 20px; overflow-y: auto;
}
.overlay-inner { background: var(--bg-panel-solid); width: 100%; max-width: 800px; padding: 50px; border-radius: 16px; border: 1px solid var(--border); }
.overlay-close {
  position: sticky; top: 0; float: right; background: var(--bg-btn-load); color: var(--text);
  border: 1px solid var(--border-focus); padding: 10px 20px; border-radius: 8px; cursor: pointer;
  font-weight: 600;
}
.prose-content { color: var(--text-muted); line-height: 1.8; }
.prose-content h1 { font-size: 2.2em; margin-bottom: 30px; color: var(--text); }
.prose-content h2 { font-size: 1.4em; margin-top: 40px; margin-bottom: 15px; color: var(--text); border-bottom: 1px solid var(--border); padding-bottom: 10px; }
.prose-content h3 { margin-top: 3rem; margin-bottom: 0.25rem; color: var(--text); }
.prose-content h3:first-of-type { margin-top: 1rem; }
.prose-content p { margin-bottom: 1.25em; }
.prose-content ol, .prose-content ul { margin: 0 0 1.25em 1.5em; padding: 0; }
.prose-content li { margin-bottom: 0.4em; }
/* ── Progress bar ── */
.progress-row { display: flex; align-items: center; gap: 0.625rem; margin-top: -0.25rem; }
.time-label {
  font-size: 0.65rem; font-weight: 600; color: var(--text-faint);
  width: 2.5rem; text-align: center; font-variant-numeric: tabular-nums;
}
.progress-slider {
  flex: 1; -webkit-appearance: none; appearance: none;
  height: 3px; background: linear-gradient(to right, #c9a227 0%, #c9a227 var(--progress-pct, 0%), var(--vol-track) var(--progress-pct, 0%), var(--vol-track) 100%);
  border-radius: 2px; outline: none; cursor: pointer; min-width: 0;
}
.progress-slider::-webkit-slider-thumb {
  -webkit-appearance: none; width: 10px; height: 10px;
  background: #c9a227; border-radius: 50%; cursor: pointer;
  transition: transform 0.1s;
}
.progress-slider::-webkit-slider-thumb:hover { transform: scale(1.3); }
.progress-slider::-moz-range-thumb {
  width: 10px; height: 10px; background: #c9a227;
  border-radius: 50%; cursor: pointer; border: none;
}

/* ── Phase 4: Spotify Integration ── */
.header-btn.spotify.connected { border-color: var(--spotify-green); color: var(--spotify-green); }
.warning-banner {
  background: var(--warning-bg); color: var(--warning-text); padding: 0.75rem; margin: 1rem 1.25rem 0;
  border-radius: 8px; border: 1px solid var(--warning-border); font-size: 0.85rem; font-weight: 600;
  display: flex; align-items: center; justify-content: center; gap: 10px;
}
.spotify-logo-placeholder { background: var(--spotify-logo-bg); border-radius: 8px; color: var(--spotify-green); }
#pro-features { border-right: 1px solid var(--border); padding-right: 10px; }


/* ── Toast Notifications ── */
#toast-container {
  position: fixed; bottom: 1.5rem; left: 50%; transform: translateX(-50%);
  z-index: 9000; display: flex; flex-direction: column; gap: 0.5rem; align-items: center;
  pointer-events: none;
}
.toast {
  background: var(--bg-panel-solid); color: var(--text); border: 1px solid var(--border);
  border-radius: 8px; padding: 0.65rem 1rem; font-size: 0.8rem; max-width: 420px;
  box-shadow: 0 4px 16px rgba(0,0,0,0.4); pointer-events: auto;
  animation: toast-in 0.2s ease;
}
.toast.toast-out { animation: toast-out 0.3s ease forwards; }
@keyframes toast-in  { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }
@keyframes toast-out { from { opacity: 1; transform: translateY(0); } to { opacity: 0; transform: translateY(8px); } }

/* ── Soundboard ─────────────────────────────────────────────────────────────── */

.sb-panel { max-width: 780px; width: 95vw; display: flex; flex-direction: column; }

/* List view */
.sb-list { flex: 1; overflow-y: auto; padding: 0 0 8px; }
.sb-empty { padding: 24px 0; text-align: center; color: var(--text-dim); font-size: 0.85rem; }
.sb-list-item {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 12px; border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--bg-input);
  margin-bottom: 6px;
}
.sb-list-num {
  width: 18px; text-align: center; font-size: 0.75rem;
  color: var(--text-dim); font-weight: 700; flex-shrink: 0;
}
.sb-list-name { flex: 1; font-weight: 600; font-size: 0.9rem; }
.sb-list-count { font-size: 0.78rem; color: var(--text-muted); white-space: nowrap; }

/* Board view */
#sb-board-view { display: flex; flex-direction: column; flex: 1; min-height: 0; }
.sb-back-btn {
  background: none; border: 1px solid var(--border); border-radius: 5px;
  color: var(--text-muted); padding: 4px 10px; font-size: 0.78rem;
  cursor: pointer; white-space: nowrap;
}
.sb-back-btn:hover { border-color: var(--border-focus); color: var(--text); }
.sb-board-content {
  display: flex; gap: 16px; flex: 1; min-height: 0;
  padding: 14px 0 4px;
}

/* Keyboard panel */
.sb-keyboard-panel { display: flex; flex-direction: column; gap: 8px; flex-shrink: 0; }
.sb-keyboard-hint { font-size: 0.72rem; color: var(--text-faint); }
.sb-keyboard { display: flex; flex-direction: column; gap: 4px; }
.sb-key-row { display: flex; gap: 4px; }
.sb-key {
  width: 38px; height: 38px; border-radius: 5px;
  border: 1px solid var(--border); background: var(--bg-btn);
  color: var(--text-muted); font-size: 0.75rem; font-weight: 600;
  cursor: pointer; flex-shrink: 0; transition: background 0.1s, border-color 0.1s, color 0.1s;
}
.sb-key:hover { border-color: var(--border-focus); color: var(--text); }
.sb-key.mapped {
  border-color: var(--border-focus);
  background: var(--bg-btn-load);
  color: var(--text);
}
.sb-key.playing {
  border-color: #4ade80;
  background: rgba(74,222,128,0.12);
  color: #4ade80;
}
.sb-key-space { width: 120px; }

/* Mappings panel */
.sb-mappings-panel {
  flex: 1; min-width: 0; display: flex; flex-direction: column;
  border-left: 1px solid var(--border); padding-left: 16px; min-height: 0;
}
.sb-mappings-header {
  font-size: 0.72rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.05em; color: var(--text-dim); margin-bottom: 8px; flex-shrink: 0;
}
.sb-mappings { flex: 1; overflow-y: auto; display: flex; flex-direction: column; gap: 6px; }
.sb-mapping-item {
  display: flex; align-items: center; gap: 8px;
  padding: 8px 10px; border-radius: 6px;
  border: 1px solid var(--border); background: var(--bg-input);
}
.sb-mapping-key {
  width: 28px; height: 28px; border-radius: 4px; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  background: var(--bg-btn-load); border: 1px solid var(--border-focus);
  color: var(--text); font-size: 0.72rem; font-weight: 700;
}
.sb-mapping-info { flex: 1; min-width: 0; }
.sb-mapping-title { font-size: 0.82rem; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sb-mapping-meta { font-size: 0.72rem; color: var(--text-muted); margin-top: 2px; }
.sb-del-btn { padding: 4px 8px !important; font-size: 0.75rem !important; }

/* Editor sub-modal, sits above soundboard modal */
.sb-editor-overlay { z-index: 2100; }
.sb-editor-panel { max-width: 420px; }
.sb-editor-form { display: flex; flex-direction: column; gap: 6px; padding: 4px 0 8px; }
.sb-editor-label { font-size: 0.75rem; color: var(--text-muted); margin-top: 6px; }
.sb-editor-time-row { display: flex; gap: 12px; }
.sb-editor-time-group { flex: 1; display: flex; flex-direction: column; gap: 4px; }
.sb-time-input { width: 100% !important; box-sizing: border-box; }

/* ── Boot skeletons ───────────────────────────────────────────────────────
   Placeholder shapes painted on the first frame so a restored session fades in
   instead of snapping in piece by piece. Which ones appear is decided by the
   inline boot-hint script in app.html; the app removes the html.boot-* classes
   as each piece of real content lands (clearBootSkeletons).

   The sweep is drawn in `currentColor` off --placeholder-icon, which is a
   readable mid-tone in every theme, so skeletons need no theme tokens of their
   own and work under custom themes too. */
.skel {
  position: relative;
  overflow: hidden;
  background: var(--bg-btn);
  border-radius: 6px;
  color: var(--placeholder-icon);
}
.skel::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, transparent 0%, currentColor 50%, transparent 100%);
  opacity: 0.24;
  animation: skel-sweep 1.5s ease-in-out infinite;
}
@keyframes skel-sweep {
  from { transform: translateX(-100%); }
  to   { transform: translateX(100%); }
}
@media (prefers-reduced-motion: reduce) {
  .skel::after { animation: none; background: currentColor; opacity: 0.12; }
}

/* Header: signed-in user chip. Takes the Login button's slot until auth resolves,
   so a returning user never sees "Login" flash first. */
.skel-user { display: none; align-items: center; gap: 10px; }
html.boot-user .skel-user { display: flex; }
html.boot-user #login-btn { display: none; }
.skel-avatar { width: 30px; height: 30px; border-radius: 50%; }
.skel-user-name { width: 84px; height: 11px; }

/* Header: the two Pro icon buttons (Soundboards, Spotify) */
.skel-pro-header { display: none; gap: 8px; }
html.boot-pro .skel-pro-header { display: flex; }
.skel-pro-icon { width: 44px; height: 44px; border-radius: 8px; }

/* Center controls: Balance / Combine / Add Effect. Direct children of
   .center-controls so they inherit the same column (mobile) or row (desktop). */
.skel-pro-btn { display: none; width: 100%; min-height: 44px; border-radius: 10px; }
html.boot-pro .skel-pro-btn { display: block; }

/* Decks: player area, progress row and title chip */
.skel-player {
  position: absolute;
  inset: 0;
  z-index: 2;
  border-radius: 0;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.25s ease;
}
html.boot-deck-a .deck-panel-a .skel-player,
html.boot-deck-b .deck-panel-b .skel-player { opacity: 1; }
html.boot-deck-a .deck-panel-a .player-placeholder,
html.boot-deck-b .deck-panel-b .player-placeholder { display: none; }

/* A deck the boot hint says is about to be filled keeps its volume row, so the row doesn't
   shift the rest of the panel down when the track actually lands. */
html.boot-deck-a .deck-panel-a.deck-empty .volume-row,
html.boot-deck-b .deck-panel-b.deck-empty .volume-row { display: flex; }

.skel-progress { display: none; height: 1rem; margin-top: -0.25rem; border-radius: 4px; }
html.boot-deck-a .deck-panel-a .skel-progress,
html.boot-deck-b .deck-panel-b .skel-progress { display: block; }
/* Touch mode hides the real progress row, so its placeholder goes too. */
body.touch-mode .skel-progress { display: none !important; }

.skel-chip-line { display: none; flex: 1; height: 11px; }
html.boot-deck-a .deck-panel-a .skel-chip-line,
html.boot-deck-b .deck-panel-b .skel-chip-line { display: block; }
html.boot-deck-a .deck-panel-a .track-title-content,
html.boot-deck-b .deck-panel-b .track-title-content { display: none; }

/* Playlist rows. The wrapper keeps the rows out of #playlist-empty's and
   .playlist-item's way, so nth-child counts only skeleton rows. */
.pl-skel-wrap { display: none; flex-direction: column; gap: 0.25rem; }
html[data-boot-rows] .pl-skel-wrap { display: flex; }
html[data-boot-rows] #playlist-empty { display: none; }
html[data-boot-rows="1"] .pl-skel:nth-child(n+2),
html[data-boot-rows="2"] .pl-skel:nth-child(n+3),
html[data-boot-rows="3"] .pl-skel:nth-child(n+4),
html[data-boot-rows="4"] .pl-skel:nth-child(n+5),
html[data-boot-rows="5"] .pl-skel:nth-child(n+6) { display: none; }
.pl-skel {
  display: flex; align-items: center; gap: 0.5rem; padding: 0.45rem 0.625rem;
  background: var(--bg-input); border: 1px solid var(--border); border-radius: 6px;
}
.pl-skel-info { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 0.3rem; }
.skel-pl-handle { width: 8px; height: 14px; flex-shrink: 0; border-radius: 2px; }
.skel-pl-title { height: 10px; width: 78%; }
.skel-pl-vol { height: 6px; width: 46%; }
/* Vary the bar widths so the stack reads as content, not as a striped block. */
.pl-skel:nth-child(2) .skel-pl-title { width: 60%; }
.pl-skel:nth-child(3) .skel-pl-title { width: 71%; }
.pl-skel:nth-child(4) .skel-pl-title { width: 52%; }
.pl-skel:nth-child(5) .skel-pl-title { width: 66%; }
.pl-skel:nth-child(6) .skel-pl-title { width: 74%; }

/* ── App Footer ── */
.app-footer {
  text-align: center;
  padding: 14px;
  border-top: 1px solid var(--border);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 8px 16px;
}
.app-footer a {
  color: var(--footer-text);
  text-decoration: none;
  font-size: 0.72rem;
  transition: color 0.15s;
}
.app-footer a:hover { color: var(--text-muted); }
.app-footer .footer-dot { color: var(--footer-text); font-size: 0.7rem; }

/* ── Static Info Pages (About / Privacy / Terms) ── */
.static-page {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  background: var(--bg);
  color: var(--text);
  font-family: inherit;
}
.static-nav {
  background: var(--bg-header);
  border-bottom: 1px solid var(--border);
  padding: 12px 24px;
  display: flex;
  align-items: center;
  gap: 16px;
}
.static-nav-back {
  color: var(--text-muted);
  text-decoration: none;
  font-size: 0.85rem;
  display: flex;
  align-items: center;
  gap: 6px;
  transition: color 0.15s;
}
.static-nav-back:hover { color: var(--text); }
.static-nav-title {
  color: var(--text-faint);
  font-size: 0.8rem;
}
.static-nav-divider { color: var(--border); }
.static-nav-theme-btn {
  margin-left: auto;
  background: var(--bg-btn); border: 1px solid var(--border);
  color: var(--toggle-text); cursor: pointer; padding: 6px 10px;
  border-radius: 8px; font-size: 0.78rem; display: flex; align-items: center;
  gap: 5px; transition: all 0.2s; font-family: inherit;
}
.static-nav-theme-btn:hover { border-color: var(--border-focus); color: var(--text); }
.video-embed-wrapper {
  width: 100%;
  aspect-ratio: 16 / 9;
  border-radius: 8px;
  overflow: hidden;
  margin: 1.5rem 0 2rem;
}
.video-embed-wrapper iframe {
  width: 100%;
  height: 100%;
  border: 0;
  display: block;
}

.static-content-area {
  display: flex;
  flex: 1;
  align-items: flex-start;
  justify-content: center;
}
.static-body {
  flex: 1;
  max-width: 800px;
  margin: 0;
  padding: 48px 24px;
  min-width: 0;
  box-sizing: border-box;
}
.static-footer {
  text-align: center;
  padding: 24px;
  border-top: 1px solid var(--border);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 16px;
  flex-wrap: wrap;
}
.static-footer a {
  color: var(--footer-text);
  text-decoration: none;
  font-size: 0.78rem;
  transition: color 0.15s;
}
.static-footer a:hover { color: var(--text-muted); }
.static-footer .footer-dot { color: var(--footer-text); }

/* ── Built by Bee & Gold badge ── */
.app-footer, .static-footer { position: relative; padding-left: 52px; padding-right: 52px; }
.bee-badge {
  position: absolute; right: 20px; top: 50%; transform: translateY(-50%);
  display: flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; color: var(--footer-text); text-decoration: none;
}
.bee-badge-logo {
  width: 20px; height: 20px;
  transition: transform 0.3s cubic-bezier(0.34,1.56,0.64,1), opacity 0.3s ease;
}
.bee-badge-tip {
  position: absolute; right: 0; top: 50%; transform: translate(4px, -50%);
  white-space: nowrap; font-size: 0.72rem; color: var(--footer-text);
  /* "Built by Bee & Gold" is a wordmark, not app chrome - it keeps Playfair in every
     theme. Deliberately a literal stack and deliberately !important: themes are free to
     override the --font-* tokens (the circuit theme swaps the lot for SF Mono), and a
     theme block's selector outranks this one, so nothing token-based or unflagged could
     protect it. The children inherit from here, so the name and sparks follow.
     Do not tokenize this and do not override it from a theme. */
  font-family: "Playfair Display", serif !important;
  opacity: 0; pointer-events: none;
  transition: transform 0.3s ease 0.1s, opacity 0.3s ease 0.1s;
}
/* The wordmark is someone else's brand, so its colour is a literal, the same way its
   typeface is - see the note on .bee-badge-tip above. It used to read --border-focus,
   which only looked right by coincidence: that token means "focus ring", and it merely
   happened to be gold in most themes. The water theme's focus ring is blue, so the
   wordmark came out blue with it.
   --bee-wordmark exists purely so the two high-contrast themes can keep their own
   value; nothing else sets it, so every other theme lands on the gold fallback. A
   logotype is exempt from WCAG 1.4.3 contrast and this tip is aria-hidden (the anchor
   carries the accessible name), but an HC theme is the one place where shipping
   deliberately low-contrast text would still be the wrong call. */
.bee-badge-tip-name { position: relative; color: var(--bee-wordmark, #c9a227); font-weight: 600; margin-left: 3px; }
.bee-badge-spark {
  position: absolute; font-size: 0.55rem; line-height: 1; color: var(--bee-wordmark, #c9a227); opacity: 0;
}
.bee-badge-spark-1 { top: -9px; left: -10px; animation-delay: 0s; }
.bee-badge-spark-2 { top: -11px; right: 6px; animation-delay: 0.45s; }
.bee-badge-spark-3 { bottom: -9px; right: -8px; animation-delay: 0.9s; }
.bee-badge-spark-4 { bottom: -7px; left: 10px; animation-delay: 0.25s; }
@keyframes bee-badge-twinkle {
  0%, 100% { opacity: 0; transform: scale(0.3) rotate(0deg); }
  50% { opacity: 1; transform: scale(1) rotate(90deg); }
}
@media (hover: hover) {
  .bee-badge:hover .bee-badge-logo,
  .bee-badge:focus-visible .bee-badge-logo { transform: scale(0); opacity: 0; }
  .bee-badge:hover .bee-badge-tip,
  .bee-badge:focus-visible .bee-badge-tip { transform: translate(0, -50%); opacity: 1; }
  .bee-badge:hover .bee-badge-spark,
  .bee-badge:focus-visible .bee-badge-spark { animation: bee-badge-twinkle 1.4s ease-in-out infinite; }
}
@media (prefers-reduced-motion: reduce) {
  .bee-badge-logo, .bee-badge-tip { transition: none; }
  .bee-badge-spark { animation: none !important; }
}

/* ── Landing Page ── */

/* Nav */
.lp-nav {
  background: var(--bg-header);
  border-bottom: 1px solid var(--border);
  padding: 0.75rem 1.5rem;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
}
.lp-nav-brand {
  font-size: 1.05rem;
  font-weight: 700;
  font-family: var(--font-display);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  background: linear-gradient(135deg, #c9a227, #e8a020);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
  text-decoration: none;
}
.lp-nav-actions { display: flex; align-items: center; gap: 0.75rem; }
.lp-nav-theme-btn {
  background: var(--bg-btn); border: 1px solid var(--border);
  color: var(--toggle-text); cursor: pointer; padding: 6px 10px;
  border-radius: 8px; font-size: 0.78rem; display: flex; align-items: center;
  gap: 5px; transition: all 0.2s; font-family: inherit; align-self: stretch;
}
.lp-nav-theme-btn:hover { border-color: var(--border-focus); color: var(--text); }

/* Primary button */
.btn-primary {
  background: #c9a227;
  border: 1px solid #d4a828;
  border-radius: 8px;
  color: #1a1000;
  padding: 0.6rem 1.4rem;
  font-size: 0.88rem;
  font-weight: 700;
  letter-spacing: 0.03em;
  cursor: pointer;
  transition: all 0.2s;
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-family: inherit;
}
.btn-primary:hover {
  background: #b8911e;
  box-shadow: 0 0 20px rgba(201,162,39,0.45);
}
.btn-primary.large {
  padding: 0.85rem 2rem;
  font-size: 1rem;
}

/* Hero */
.lp-hero {
  padding: 5rem 1.5rem 4rem;
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.75rem;
  position: relative;
  isolation: isolate;
  overflow: hidden;
}
.lp-hero::before,
.lp-hero::after {
  content: '';
  position: absolute;
  inset: -1000px;
  z-index: -1;
  pointer-events: none;
  background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.65' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: 7000px 7000px;
  background-repeat: no-repeat;
  background-position: center;
  filter: var(--lp-hero-filter, none);
  -webkit-mask-image: radial-gradient(ellipse 42% 55% at center, transparent 35%, black 85%);
  mask-image: radial-gradient(ellipse 42% 55% at center, transparent 35%, black 85%);
}
.lp-hero::before {
  opacity: var(--lp-hero-before-opacity);
  animation: heroWaveA 12s ease-in-out infinite alternate;
}
.lp-hero::after {
  opacity: var(--lp-hero-after-opacity);
  background-position: calc(50% + 1500px) calc(50% + 1000px);
  animation: heroWaveB 17s ease-in-out infinite alternate;
}
@keyframes heroWaveA {
  0%   { transform: translate(0, 0); }
  100% { transform: translate(-350px, -200px); }
}
@keyframes heroWaveB {
  0%   { transform: translate(0, 0); }
  100% { transform: translate(280px, -320px); }
}
.lp-hero-eyebrow {
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.25em;
  text-transform: uppercase;
  color: var(--badge-ready-text);
}
.lp-hero-title {
  font-weight: 800;
  font-family: var(--font-display);
  letter-spacing: -0.02em;
  line-height: 1.15;
  color: var(--text);
  max-width: 760px;
}
.lp-hero-title-top {
  display: block;
  font-size: clamp(1.3rem, 3vw, 2rem);
  margin-bottom: 0.1em;
}
.lp-hero-title em {
  display: block;
  font-size: clamp(2.4rem, 6.5vw, 4rem);
  font-style: normal;
  background: linear-gradient(135deg, #c9a227, #e8a020);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
}
.lp-hero-subtitle {
  font-size: clamp(0.95rem, 2.5vw, 1.15rem);
  color: var(--text-muted);
  max-width: 540px;
  line-height: 1.65;
}
.lp-hero-cta-group {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  flex-wrap: wrap;
  justify-content: center;
}

/* Sections */
.lp-section {
  padding: 4rem 1.5rem;
  max-width: 1100px;
  margin: 0 auto;
  width: 100%;
  box-sizing: border-box;
}
.lp-section.narrow { max-width: 800px; }
.lp-divider { border: none; border-top: 1px solid var(--border); margin: 0; }
.lp-section-label {
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.25em;
  text-transform: uppercase;
  color: var(--badge-ready-text);
  margin-bottom: 0.6rem;
}
/* At 0.68rem this is "normal" text for WCAG, so it needs 4.5:1 - the light
   theme's --badge-ready-text (#92700a) only reaches 4.07:1 on the page
   background. Overridden here rather than on the token itself, which is also
   used by .status-badge.ready and the pro checkmarks against different
   backgrounds where the current value is fine. */
html[data-theme="light"] .lp-section-label { color: #7d5f06; }
.lp-section-title {
  font-size: clamp(1.5rem, 4vw, 2.2rem);
  font-weight: 700;
  font-family: var(--font-display);
  color: var(--text);
  line-height: 1.2;
  margin-bottom: 0.75rem;
}
.lp-section-subtitle {
  font-size: 0.95rem;
  color: var(--text-muted);
  line-height: 1.65;
  max-width: 600px;
  margin-bottom: 2.5rem;
}

/* Feature cards */
.lp-feature-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 1.25rem;
}
.lp-feature-card {
  position: relative;
  background: var(--bg-panel-solid);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 1.6rem 1.5rem 1.5rem;
  display: flex;
  flex-direction: column;
  gap: 0.7rem;
  overflow: hidden;
  transition: border-color 0.2s, transform 0.2s, filter 0.2s;
}
.lp-feature-card::before {
  content: '';
  position: absolute;
  top: 0;
  right: 0;
  width: 26px;
  height: 26px;
  background: var(--border);
  clip-path: polygon(100% 0, 100% 100%, 0 0);
  box-shadow: -2px 2px 5px rgba(0, 0, 0, 0.3);
  transition: background 0.2s;
}
.lp-feature-card:hover {
  border-color: var(--border-focus);
  transform: translateY(-3px);
  filter: drop-shadow(0 12px 20px rgba(0, 0, 0, 0.35));
}
.lp-feature-card:hover::before { background: var(--border-focus); }

.lp-feature-card.pro { border-color: rgba(201, 162, 39, 0.4); }
.lp-feature-card.pro::before { background: #c9a227; }
.lp-feature-card.pro:hover::before { background: #e8b923; }

.lp-feature-card-icon {
  position: relative;
  width: 42px;
  height: 42px;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--border);
  clip-path: polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%);
  transition: background 0.2s;
}
.lp-feature-card-icon::before {
  content: '';
  position: absolute;
  top: 1.5px;
  left: 1.5px;
  right: 1.5px;
  bottom: 1.5px;
  background: var(--bg-input);
  clip-path: polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%);
}
.lp-feature-card-icon svg { position: relative; z-index: 1; }
.lp-feature-card:hover .lp-feature-card-icon { background: #c9a227; }

.lp-feature-card-title {
  font-family: var(--font-display);
  font-size: 0.95rem;
  font-weight: 700;
  letter-spacing: 0.01em;
  color: var(--text);
  position: relative;
  padding-bottom: 0.5rem;
}
.lp-feature-card-title::after {
  content: '';
  position: absolute;
  left: 0;
  bottom: 0;
  width: 24px;
  height: 2px;
  background: linear-gradient(to right, #c9a227, #e8a020);
  transition: width 0.25s ease;
}
.lp-feature-card:hover .lp-feature-card-title::after { width: 44px; }

.lp-feature-card-body {
  font-size: 0.82rem;
  color: var(--text-muted);
  line-height: 1.6;
}

/* Session Timeline */
.lp-timeline { display: flex; flex-direction: column; gap: 2rem; }

.lp-timeline-track {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: 0.25rem;
  overflow-x: auto;
  overflow-y: hidden;
  padding: 26px 0.5rem 0.5rem;
}

/* The rail and the gold fill are positioned from measured dot centres
   (see the timeline script) so the fill always stops exactly on the active
   node. The var() fallbacks keep the rail sensible if the script never runs. */
.lp-timeline-rail,
.lp-timeline-track-fill {
  position: absolute;
  top: var(--tl-rail-top, 26px);
  left: var(--tl-rail-start, 0.5rem);
  height: 2px;
  border-radius: 1px;
}
/* Unlit rail and gems use --text-dim, not --border: in the light theme
   --border is a warm tan (#c9a880) close enough to the gold fill that the
   progress reads as already complete. --text-dim stays neutral in all four
   themes. */
.lp-timeline-rail {
  width: var(--tl-rail-width, calc(100% - 1rem));
  background: linear-gradient(to right, transparent, var(--text-dim) 5%, var(--text-dim) 95%, transparent);
  opacity: 0.6;
}
/* Ornamental diamond end-caps, so the rail reads as an inlaid bar
   rather than a plain progress track. */
.lp-timeline-rail::before,
.lp-timeline-rail::after {
  content: '';
  position: absolute;
  top: 50%;
  width: 7px;
  height: 7px;
  background: var(--text-dim);
  transform: translateY(-50%) rotate(45deg);
}
.lp-timeline-rail::before { left: -4px; }
.lp-timeline-rail::after { right: -4px; }

.lp-timeline-track-fill {
  width: var(--tl-fill-width, 0px);
  background: linear-gradient(to right, #c9a227, #e8b923);
  box-shadow: 0 0 9px rgba(232, 193, 74, 0.45);
  transition: width 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}

/* flex-basis must be 0, not auto: with auto each node starts at its own
   label's width and only the leftover space is shared equally, so a long
   label like "Setting the Scene" pushes its gem out of step with the rest.
   A zero basis makes every node identical, so the gems are equidistant.
   --tl-node-min is measured from the widest label by the timeline script,
   so labels (which never wrap) cannot overflow into their neighbours when
   the track is too narrow to grow. */
.lp-timeline-node {
  position: relative;
  z-index: 1;
  flex: 1 1 0;
  min-width: var(--tl-node-min, 96px);
  min-height: 44px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.55rem;
  background: none;
  border: none;
  padding: 0.4rem 0.5rem;
  cursor: pointer;
  color: var(--text-muted);
  font-family: inherit;
}
/* Cut gem rather than a plain dot: a rotated square picks up the same
   angular motif as the feature-card hexagons and corner folds. */
.lp-timeline-node-dot {
  width: 13px;
  height: 13px;
  background: var(--bg-panel-solid);
  border: 2px solid var(--text-dim);
  box-sizing: border-box;
  transform: rotate(45deg);
  transition: background 0.25s, border-color 0.25s, box-shadow 0.25s, transform 0.25s;
}
.lp-timeline-node-label {
  font-family: var(--font-display);
  font-size: 0.74rem;
  font-weight: 700;
  letter-spacing: 0.03em;
  text-align: center;
  white-space: nowrap;
  transition: color 0.2s;
}
.lp-timeline-node:hover .lp-timeline-node-dot { border-color: var(--border-focus); }
.lp-timeline-node:hover .lp-timeline-node-label { color: var(--text); }
.lp-timeline-node:focus-visible { outline: 2px solid var(--border-focus); outline-offset: 2px; border-radius: 8px; }

/* Moments already passed stay lit, so the rail reads as progress. */
.lp-timeline-node.is-past .lp-timeline-node-dot {
  background: linear-gradient(135deg, #d8b23c, #c9a227);
  border-color: #c9a227;
}
.lp-timeline-node[aria-selected="true"] .lp-timeline-node-dot {
  background: linear-gradient(135deg, #f0d066, #c9a227);
  border-color: #f0d066;
  transform: rotate(45deg) scale(1.25);
  animation: lp-tl-ember 2.6s ease-in-out infinite;
}
.lp-timeline-node[aria-selected="true"] .lp-timeline-node-label { color: var(--text); }

@keyframes lp-tl-ember {
  0%, 100% { box-shadow: 0 0 0 4px rgba(201, 162, 39, 0.13), 0 0 10px rgba(240, 208, 102, 0.45); }
  50%      { box-shadow: 0 0 0 5px rgba(201, 162, 39, 0.20), 0 0 17px rgba(240, 208, 102, 0.75); }
}

/* All panels share one grid cell, so the box is always as tall as the
   longest moment and switching never reflows the page below it. Inactive
   panels keep their space but are hidden from view and from screen
   readers via visibility. */
.lp-timeline-panels { display: grid; }
.lp-timeline-panel {
  grid-area: 1 / 1;
  display: flex;
  gap: 1.25rem;
  align-items: flex-start;
  background: var(--bg-panel-solid);
  border: 1px solid var(--border);
  border-radius: 14px;
  padding: 1.75rem;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.32s ease, transform 0.32s cubic-bezier(0.33, 0, 0.2, 1), visibility 0.32s;
}
/* The horizontal offsets are applied inline by the timeline script, which
   knows the travel direction: the outgoing card exits against the direction
   of travel while the incoming one arrives from the side you came from. The
   20px shift is deliberately under the section's 24px side padding so a
   sliding card can never push the page into horizontal scroll. */
.lp-timeline-panel.is-active {
  opacity: 1;
  visibility: visible;
  transform: none;
}
.lp-timeline-panel:focus-visible { outline: 2px solid var(--border-focus); outline-offset: 2px; }
.lp-timeline-panel-icon {
  width: 46px;
  height: 46px;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bg-input);
  border: 1px solid var(--border);
  border-radius: 10px;
}
.lp-timeline-panel-tag {
  display: inline-block;
  font-size: 0.65rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--badge-ready-text);
  margin-bottom: 0.4rem;
}
/* The tag deliberately does NOT recolour for Pro. A hardcoded #c9a227 here
   measured 2.32:1 on the light theme's panel background, and the "· Pro"
   wording already carries the distinction without relying on colour.
   The light theme's --badge-ready-text (#92700a) is itself only 4.44:1 at
   this size, so light gets a deeper gold to clear AA. */
html[data-theme="light"] .lp-timeline-panel-tag { color: #7d5f06; }
.lp-timeline-panel-title {
  font-family: var(--font-display);
  font-size: 1.1rem;
  font-weight: 700;
  color: var(--text);
  margin: 0 0 0.5rem;
  line-height: 1.3;
}
.lp-timeline-panel-desc { font-size: 0.86rem; color: var(--text-muted); line-height: 1.65; margin: 0; }

@media (max-width: 640px) {
  .lp-timeline-panel { flex-direction: column; }
}
@media (prefers-reduced-motion: reduce) {
  .lp-timeline-track-fill { transition: none; }
  .lp-timeline-node-dot { transition: none; }
  .lp-timeline-node[aria-selected="true"] .lp-timeline-node-dot {
    animation: none;
    box-shadow: 0 0 0 4px rgba(201, 162, 39, 0.18);
  }
  .lp-timeline-panel { transition: none; transform: none; }
}

/* Pricing */
.lp-pricing-grid {
  display: grid;
  grid-template-columns: 1fr minmax(150px, 180px) 1fr;
  align-items: stretch;
  gap: 1.5rem;
}
@media (max-width: 640px) {
  .lp-pricing-grid { grid-template-columns: 1fr; }
}
.lp-pricing-card {
  position: relative;
  overflow: hidden;
  background: var(--bg-panel-solid);
  border: 1px solid var(--border);
  border-radius: 14px;
  padding: 2rem;
  display: flex;
  flex-direction: column;
}
.lp-pricing-card::before {
  content: '';
  position: absolute;
  top: 0;
  right: 0;
  width: 26px;
  height: 26px;
  background: var(--border);
  clip-path: polygon(100% 0, 100% 100%, 0 0);
  box-shadow: -2px 2px 5px rgba(0, 0, 0, 0.3);
}
.lp-pricing-card.pro {
  background: var(--lp-pricing-pro-bg);
  border-color: var(--lp-pricing-pro-border);
  animation: lp-pricing-pro-glow 6s ease-in-out infinite;
}
.lp-pricing-card.pro::before { background: #c9a227; }

@keyframes lp-pricing-pro-glow {
  0%   { box-shadow: var(--lp-pricing-pro-shadow), 0 0 45px 8px rgba(201, 162, 39, 0.18); }
  25%  { box-shadow: var(--lp-pricing-pro-shadow), 10px 0 50px 10px rgba(201, 162, 39, 0.22); }
  50%  { box-shadow: var(--lp-pricing-pro-shadow), 0 10px 55px 12px rgba(201, 162, 39, 0.25); }
  75%  { box-shadow: var(--lp-pricing-pro-shadow), -10px 0 50px 10px rgba(201, 162, 39, 0.22); }
  100% { box-shadow: var(--lp-pricing-pro-shadow), 0 0 45px 8px rgba(201, 162, 39, 0.18); }
}
@media (prefers-reduced-motion: reduce) {
  .lp-pricing-card.pro { animation: none; box-shadow: var(--lp-pricing-pro-shadow); }
}

/* Fader rail: a decorative divider connecting the two tiers, echoing the
   app's own crossfader/slider visual language. */
.lp-pricing-rail {
  position: relative;
}
.lp-pricing-rail::before {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  left: 50%;
  width: 2px;
  transform: translateX(-50%);
  background: linear-gradient(180deg, transparent, #c9a227, transparent);
}
@media (max-width: 640px) {
  .lp-pricing-rail { padding: 1.5rem 0; }
  .lp-pricing-rail::before {
    top: 50%;
    left: 0;
    right: 0;
    bottom: auto;
    width: auto;
    height: 2px;
    transform: translateY(-50%);
    background: linear-gradient(90deg, transparent, #c9a227, transparent);
  }
}

.lp-pricing-tier-row {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin-bottom: 0.5rem;
}
.lp-pricing-tier-icon {
  position: relative;
  width: 30px;
  height: 30px;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--border);
  clip-path: polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%);
}
.lp-pricing-tier-icon::before {
  content: '';
  position: absolute;
  top: 1.5px;
  left: 1.5px;
  right: 1.5px;
  bottom: 1.5px;
  background: var(--bg-input);
  clip-path: polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%);
}
.lp-pricing-tier-icon svg { position: relative; z-index: 1; color: var(--text); }
.lp-pricing-card.pro .lp-pricing-tier-icon { background: #c9a227; }
.lp-pricing-card.pro .lp-pricing-tier-icon svg { color: #c9a227; }

.lp-pricing-tier {
  font-size: 0.65rem;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.lp-pricing-tier.pro { color: var(--badge-ready-text); }
.lp-pricing-price {
  font-size: 2.2rem;
  font-weight: 800;
  color: var(--text);
  line-height: 1;
  margin-bottom: 0.35rem;
}
.lp-pricing-price-note {
  font-size: 0.78rem;
  color: var(--text-muted);
  margin-bottom: 1.5rem;
}
.lp-pricing-group { margin-top: 1rem; }
.lp-pricing-group:first-of-type { margin-top: 0; }
.lp-pricing-group-label {
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--text-muted);
  opacity: 0.75;
  margin-bottom: 0.6rem;
}
.lp-pricing-feature-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 0.65rem;
}
.lp-pricing-feature-list li {
  font-size: 0.82rem;
  color: var(--text-muted);
  display: flex;
  gap: 0.6rem;
  align-items: flex-start;
  line-height: 1.4;
}
.lp-pricing-feature-list li::before {
  content: '✓';
  color: #4ade80;
  font-weight: 700;
  flex-shrink: 0;
  margin-top: 0.05em;
}
.lp-pricing-card.pro .lp-pricing-feature-list li::before { color: var(--badge-ready-text); }
.lp-pricing-cta { margin-top: 2rem; }

/* CTA banner */
.lp-cta-banner {
  background: var(--bg-fade);
  border-top: 1px solid var(--border-focus);
  border-bottom: 1px solid var(--border-focus);
  padding: 5rem 1.5rem;
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.25rem;
  position: relative;
  isolation: isolate;
}
.lp-cta-banner::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  opacity: var(--lp-cta-before-opacity);
  background-image: url("data:image/svg+xml,%3Csvg width='32' height='32' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M16 3 L29 16 L16 29 L3 16 Z' fill='none' stroke='%23c9a227' stroke-width='0.75'/%3E%3C/svg%3E");
  background-size: 32px 32px;
  background-repeat: repeat;
}
.lp-cta-banner-title {
  font-size: clamp(1.4rem, 4vw, 2rem);
  font-weight: 700;
  font-family: var(--font-display);
  color: var(--text);
  max-width: 640px;
  line-height: 1.3;
}
.lp-cta-banner-sub {
  font-size: 0.95rem;
  color: var(--text-muted);
  max-width: 480px;
  line-height: 1.6;
}

/* CSS App Mockup */
.lp-mockup {
  display: flex;
  gap: 0.6rem;
  align-items: stretch;
  max-width: 640px;
  width: 100%;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 16px;
  padding: 0.875rem;
  box-shadow: 0 24px 64px rgba(0,0,0,0.45), 0 0 0 1px rgba(255,255,255,0.04);
  pointer-events: none;
  user-select: none;
}
.lp-mockup-deck {
  flex: 1;
  background: var(--bg-panel-solid);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 0.7rem;
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
}
.lp-mockup-deck-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.lp-mockup-deck-label {
  font-size: 0.6rem;
  font-weight: 700;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--text-faint);
}
.lp-mockup-waveform {
  height: 48px;
  background: var(--player-empty-bg);
  border-radius: 6px;
  border: 1px solid var(--border);
  display: flex;
  align-items: flex-end;
  justify-content: center;
  gap: 3px;
  padding: 6px 8px;
  overflow: hidden;
}
.lp-mockup-bar {
  width: 3px;
  background: linear-gradient(to top, #c9a227, #e8a020);
  border-radius: 2px;
  transform-origin: bottom;
  animation: lp-eq-bounce var(--dur, 0.8s) ease-in-out infinite alternate;
}
@keyframes lp-eq-bounce {
  0%   { transform: scaleY(0.25); opacity: 0.6; }
  100% { transform: scaleY(1);    opacity: 1; }
}
.lp-mockup-deck.idle .lp-mockup-bar {
  background: var(--vol-track);
  animation: none;
  transform: scaleY(0.3);
}
.lp-mockup-vol-row {
  display: flex;
  align-items: center;
  gap: 0.4rem;
}
.lp-mockup-vol-label {
  font-size: 0.55rem;
  color: var(--text-faint);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.lp-mockup-vol-track {
  flex: 1;
  height: 3px;
  border-radius: 2px;
  background: var(--vol-track);
  position: relative;
  overflow: hidden;
}
.lp-mockup-vol-fill {
  position: absolute;
  top: 0;
  left: 0;
  bottom: 0;
  border-radius: 2px;
  background: linear-gradient(to right, #c9a227, #e8a020);
  transition: width 1.3s ease;
}
.lp-mockup-play-row {
  display: flex;
  gap: 0.3rem;
}
.lp-mockup-play-btn {
  flex: 1;
  background: var(--btn-playing-bg);
  border: 1px solid var(--btn-playing-border);
  border-radius: 6px;
  color: var(--btn-playing-text);
  font-size: 0.6rem;
  font-weight: 700;
  padding: 0.3rem 0.2rem;
  text-align: center;
}
.lp-mockup-deck.idle .lp-mockup-play-btn {
  background: var(--bg-play-btn);
  border-color: var(--border);
  color: var(--text-muted);
}
.lp-mockup-aux-btn {
  background: var(--bg-btn);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 0.3rem 0.35rem;
  font-size: 0.55rem;
  color: var(--text-faint);
  text-align: center;
}
.lp-mockup-center {
  width: 52px;
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
}
.lp-mockup-fade-btn {
  appearance: none;
  -webkit-appearance: none;
  pointer-events: auto;
  width: 100%;
  min-height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0.55rem 0.1rem;
  background: var(--bg-fade);
  border: 1px solid var(--border-focus);
  border-radius: 8px;
  color: var(--fade-btn-color);
  font-family: inherit;
  font-size: 0.55rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-align: center;
  cursor: pointer;
  transition: filter 0.15s;
}
.lp-mockup-fade-btn:hover { filter: brightness(1.15); }
.lp-mockup-fade-btn:active { filter: brightness(0.95); }
.lp-mockup-fade-btn.pulsing { animation: lp-mock-fade-pulse 1.3s ease-in-out; }
@keyframes lp-mock-fade-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(201, 162, 39, 0); }
  50%      { box-shadow: 0 0 0 6px rgba(201, 162, 39, 0.25); }
}
.lp-mockup-track-chip {
  width: 100%;
  background: var(--bg-input);
  border: 1px solid var(--border);
  border-radius: 5px;
  padding: 0.3rem 0.4rem;
  font-size: 0.52rem;
  color: var(--text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

@media (max-width: 520px) {
  .lp-mockup { display: none; }
  .lp-hero { padding: 3rem 1.25rem 2.5rem; }
}

@media (prefers-reduced-motion: reduce) {
  .lp-mockup-bar { animation: none !important; }
  .lp-mockup-vol-fill { transition: none !important; }
  .lp-mockup-fade-btn.pulsing { animation: none !important; }
}

/* ── Blog ── */
.blog-post-date {
  font-size: 0.8rem;
  color: var(--text-dim);
  margin-bottom: 2rem;
  margin-top: -0.5rem;
}
.blog-index { display: flex; flex-direction: column; gap: 2rem; margin-top: 1.5rem; }
.blog-index-item { display: flex; flex-direction: column; gap: 0.25rem; }
.blog-index-title {
  font-size: 1.1rem;
  font-weight: 600;
  color: var(--text);
  text-decoration: none;
}
.blog-index-title:hover { color: var(--border-focus); }
.blog-index-date { font-size: 0.78rem; color: var(--text-dim); }
.blog-index-desc { font-size: 0.88rem; color: var(--text-muted); margin: 0; }

/* Blog two-column layout */
.blog-body { max-width: 960px; }
.blog-layout {
  display: flex;
  align-items: flex-start;
  gap: 0;
  margin-top: 1.5rem;
}
.blog-sidebar {
  width: 185px;
  flex-shrink: 0;
  padding-right: 1.5rem;
  border-right: 1px solid var(--border);
  position: sticky;
  top: 24px;
  align-self: flex-start;
}
.blog-nav-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.blog-nav-link {
  display: block;
  padding: 0.45rem 0.6rem;
  font-size: 0.85rem;
  color: var(--text-muted);
  text-decoration: none;
  border-radius: 5px;
  line-height: 1.4;
  transition: color 0.15s, background 0.15s;
}
.blog-nav-link:hover { color: var(--text); background: var(--bg-panel); }
.blog-nav-link:focus-visible { outline: 2px solid var(--border-focus); outline-offset: 2px; }
.blog-nav-link[aria-current="page"] {
  color: var(--text);
  background: var(--bg-panel);
  font-weight: 600;
}
.blog-main {
  flex: 1;
  min-width: 0;
  padding-left: 2rem;
}
.blog-main > h1:first-child { margin-top: 0; }
.blog-main > h2.blog-post-heading {
  font-size: 2rem;
  border-bottom: none;
  padding-bottom: 0;
  margin-top: 0;
  margin-bottom: 0.25rem;
  color: var(--text);
}
@media (max-width: 680px) {
  .blog-layout { flex-direction: column; }
  .blog-sidebar {
    width: 100%;
    padding-right: 0;
    padding-bottom: 1rem;
    border-right: none;
    border-bottom: 1px solid var(--border);
    margin-bottom: 1.5rem;
    position: static;
  }
  .blog-nav-list { flex-direction: row; flex-wrap: wrap; gap: 0.4rem; }
  .blog-main { padding-left: 0; }
}

/* ── Accessibility ── */

/* Skip-to-main link (hidden until focused) */
.skip-link {
  position: absolute;
  top: -100%;
  left: 0.5rem;
  z-index: 99999;
  padding: 0.4rem 0.75rem;
  background: var(--bg-panel);
  color: var(--text);
  border: 2px solid var(--border-focus);
  border-radius: 4px;
  font-size: 0.85rem;
  font-weight: 600;
  text-decoration: none;
  transition: top 0.1s;
}
.skip-link:focus { top: 0.5rem; }

/* Available to screen readers, never painted. Clip rather than display:none
   or visibility:hidden, both of which also remove it from the a11y tree. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Global keyboard focus indicator - overrides inline outline:none */
:focus-visible {
  outline: 2px solid var(--border-focus) !important;
  outline-offset: 2px;
}

/* ── Custom Theme Editor ─────────────────────────────────────────────────── */
.ct-gear-btn {
  background: var(--bg-btn);
  border: 1px solid var(--border);
  border-radius: 8px;
  color: var(--toggle-text);
  padding: 0.4rem;
  cursor: pointer;
  line-height: 0;
  transition: all 0.15s;
  flex-shrink: 0;
}
.ct-gear-btn:hover { border-color: var(--border-focus); color: var(--text); }

.ct-panel { max-width: 420px; }
.ct-body { gap: 0; padding-top: 0.25rem; }

.ct-presets {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  flex-wrap: wrap;
  padding-bottom: 0.75rem;
  border-bottom: 1px solid var(--border);
  margin-bottom: 0.25rem;
}
.ct-presets-label { font-size: 0.72rem; color: var(--text-faint); margin-right: 0.1rem; }
.ct-preset-btn {
  background: var(--bg-btn);
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--text-muted);
  font-size: 0.72rem;
  font-weight: 600;
  padding: 0.25rem 0.6rem;
  cursor: pointer;
  transition: all 0.12s;
  font-family: inherit;
}
.ct-preset-btn:hover { border-color: var(--border-focus); color: var(--text); }

.ct-section { display: flex; flex-direction: column; gap: 0; }
.ct-section + .ct-section { margin-top: 0.125rem; }
.ct-section-title {
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-faint);
  padding: 0.5rem 0 0.25rem;
  border-top: 1px solid var(--border);
  margin-top: 0.25rem;
}
.ct-section:first-of-type .ct-section-title { border-top: none; margin-top: 0; }

.ct-row {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.2rem 0;
}
.ct-row label {
  flex: 1;
  font-size: 0.78rem;
  color: var(--text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ct-row input[type="color"] {
  width: 32px;
  height: 26px;
  padding: 1px 2px;
  border: 1px solid var(--border);
  border-radius: 5px;
  background: var(--bg-input);
  cursor: pointer;
  flex-shrink: 0;
}
.ct-row input[type="color"]:hover { border-color: var(--border-focus); }
.ct-hex {
  width: 72px;
  padding: 0.2rem 0.4rem;
  background: var(--bg-input);
  border: 1px solid var(--border);
  border-radius: 5px;
  color: var(--text);
  font-size: 0.75rem;
  font-family: var(--font-mono);
  text-transform: uppercase;
  outline: none;
  flex-shrink: 0;
}
.ct-hex:focus { border-color: var(--border-focus); }

.ct-share-section { margin-top: 0.5rem; }
.ct-share-hint {
  font-size: 0.75rem;
  color: var(--text-faint);
  margin: 0 0 0.4rem;
  line-height: 1.4;
}
#custom-theme-share {
  width: 100%;
  background: var(--bg-input);
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--text);
  font-size: 0.7rem;
  font-family: var(--font-mono);
  padding: 0.4rem 0.5rem;
  resize: vertical;
  outline: none;
  box-sizing: border-box;
  line-height: 1.5;
}
#custom-theme-share:focus { border-color: var(--border-focus); }
.ct-share-actions {
  display: flex;
  gap: 0.5rem;
  margin-top: 0.4rem;
}
.ct-share-actions button {
  flex: 1;
  padding: 0.35rem 0.75rem;
  background: var(--bg-btn);
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--text-muted);
  font-size: 0.75rem;
  font-weight: 600;
  cursor: pointer;
  transition: all 0.12s;
  font-family: inherit;
}

/* ── Auth / Login modal ── */
.auth-provider-btn {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  width: 100%;
  padding: 0.6rem 1rem;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg-btn);
  color: var(--text);
  font-size: 0.85rem;
  font-weight: 600;
  cursor: pointer;
  transition: border-color 0.15s, background 0.15s;
  font-family: inherit;
}
.auth-provider-btn:hover { border-color: var(--border-focus); }
.auth-facebook-btn { border-color: #1877f2; color: #1877f2; }
.auth-facebook-btn:hover { background: rgba(24,119,242,0.08); border-color: #1877f2; }
.auth-divider {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  color: var(--text-muted);
  font-size: 0.75rem;
}
.auth-divider::before, .auth-divider::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--border);
}
.auth-form { display: flex; flex-direction: column; gap: 0.5rem; }
.auth-form-actions { display: flex; gap: 0.5rem; margin-top: 0.25rem; }
.auth-form-actions .btn { flex: 1; }
.auth-input-wrap { position: relative; }
.auth-input-wrap .modal-name-input { width: 100%; }
.auth-msg {
  font-size: 0.78rem;
  padding: 0.4rem 0.6rem;
  border-radius: 6px;
}
.auth-msg-error {
  color: var(--btn-danger-text);
  border: 1px solid var(--btn-danger-border);
  background: var(--btn-danger-bg);
}
.auth-msg-success {
  color: var(--badge-playing-text);
  border: 1px solid var(--badge-playing-bord);
  background: var(--bg-badge-playing);
}
.auth-msg-info {
  color: var(--auth-info-text);
  border: 1px solid var(--auth-info-border);
  background: var(--auth-info-bg);
}
.auth-forgot-btn {
  background: none;
  border: none;
  color: var(--text-muted);
  font-size: 0.75rem;
  cursor: pointer;
  padding: 0;
  text-align: left;
  font-family: inherit;
  transition: color 0.15s;
}
.auth-forgot-btn:hover { color: var(--text); }
.ct-share-actions button:hover { border-color: var(--border-focus); color: var(--text); }

/* ── Circuit theme (Pro) ─────────────────────────────────────────────────────
   Green FR-4 substrate with gold traces and monospaced type. The palette tokens
   live in the `circuit` entry in theme-manager.js; this block carries the two
   things a color token can't express - the typeface swap and the animated trace
   layer behind the app.

   The trace layer is a fixed pseudo-element on <body>, so it stays put while a
   long playlist scrolls and never re-tiles mid-session. Only the app page gets
   it: a tiled board behind long prose hurts readability, so the static pages
   keep the flat substrate. circuit-board.svg carries its own reduced-motion
   media query, which applies even though it is loaded as a background image. */
html[data-theme="circuit"] {
  --font-body:    'SF Mono', SFMono-Regular, ui-monospace, Menlo, Monaco, Consolas, 'Liberation Mono', monospace;
  --font-display: var(--font-body);
  --font-mono:    var(--font-body);
}
html[data-theme="circuit"] body.app-page::before {
  content: '';
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  /* Traces over a substrate glow, both on the one fixed layer - a
     background-attachment: fixed on <body> misbehaves on iOS Safari. The flat
     board color underneath is --bg, from the body rule. */
  /* Three layers: travelling pulses over the static trace board over a substrate
     glow. Only circuit-flow.svg animates, so it is the only one the browser has to
     re-rasterize per frame - see tools/gen-circuit-board.js. Both SVGs share one
     viewBox and are drawn `cover`, so the pulses sit exactly on their traces at any
     viewport size. cover rather than repeat: the board is generated to fill its own
     canvas edge to edge, so there is no tile seam to line up. */
  background-image:
    url('/circuit-flow.svg'),
    url('/circuit-board.svg'),
    radial-gradient(120% 85% at 50% 0%, rgba(63,156,90,0.22) 0%, transparent 62%);
  background-size: cover, cover, cover;
  background-position: center center, center center, center top;
  background-repeat: no-repeat, no-repeat, no-repeat;
  opacity: 0.5;
  /* Keep the layer on its own compositor layer. The page scrolls on mobile, and a
     fixed layer that repaints on every scroll frame is the usual source of jank. */
  will-change: transform;
}
/* `cover` on a phone squeezes the whole 2400-unit board into ~390px, which packs the
   traces to ~8px apart - unreadable mush that fights the UI. Show a crop at close to
   1:1 instead: fewer, cleaner traces, and no blank spots either way because the board
   fills its own canvas edge to edge. The two SVG layers must keep identical size and
   position or the pulses drift off their traces. */
@media (max-width: 860px) {
  html[data-theme="circuit"] body.app-page::before {
    /* max(..., 100%) rather than a flat 1800x1200: percentages here resolve against
       this layer, which is the viewport, so a tall narrow screen (an 800x1280 tablet)
       still gets full coverage instead of a blank strip below 1200px. */
    background-size: max(1800px, 100%) max(1200px, 100%),
                     max(1800px, 100%) max(1200px, 100%),
                     cover;
  }
  /* Panels cover nearly the whole screen at this width, so the 0.90 alpha that reads
     as texture on desktop - where wide gutters carry the board - instead puts traces
     behind every control. Disabled controls sit at opacity 0.35, so those traces read
     straight through them. Keep the panel interiors calm here and let the board show
     in the header, footer, controls area and page margins. */
  html[data-theme="circuit"] body.app-page .panel,
  html[data-theme="circuit"] body.app-page .playlist-section {
    background: rgba(11,25,17,0.97);
  }
}
/* Let the board read through the app chrome. Kept off the shared --bg-header /
   --bg-panel-solid tokens, which also back modals, toasts and the landing-page
   nav - those must stay opaque. */
html[data-theme="circuit"] body.app-page header {
  background: rgba(10,23,16,0.82);
}
html[data-theme="circuit"] body.app-page .app-footer {
  background: rgba(10,23,16,0.72);
}
html[data-theme="circuit"] body.app-page .panel,
html[data-theme="circuit"] body.app-page .playlist-section {
  background: rgba(11,25,17,0.90);
}
/* The controls row has no panel behind it, so its loose labels sit straight on the
   traces. A halo in the substrate color keeps them legible without covering the
   board with a plate. */
html[data-theme="circuit"] body.app-page .center-controls {
  text-shadow: 0 0 4px var(--bg), 0 0 9px var(--bg);
}

/* ── Water theme (Pro) ───────────────────────────────────────────────────────
   A bright aquatic theme: pale blue substrate, deep-navy ink, and an animated
   Vanta.FOG shader drifting behind the app. The palette tokens live in the `water`
   entry in theme-manager.js; this block carries the three things a color token
   can't express - the Rockwell slab type stack, the backdrop layer, and the
   translucency that lets the fog read through the app chrome. */

/* Rockwell's own vertical metrics are unusable for centred UI text. Its hhea box is
   ascender 0.679 / descender 0.321 with a 0.200em line gap - and its cap height,
   0.736, is *taller than its own declared ascender*. The result is a tall,
   bottom-heavy line box with the caps riding near the top of it: cap centre lands
   0.189em above the box centre, so every button label sat visibly high. (For
   comparison: SF Pro +0.026, Georgia +0.002, Rokkitt -0.043.)

   The fix is metric overrides rather than per-selector nudges, because the offset is
   a property of the font, not of any one control. For explicit line-height L the
   half-leading is (L - (ascent + descent)) / 2, so cap centre sits at
   (ascent - descent - capHeight) / 2 away from the box centre - L cancels out.
   Setting **ascent-override - descent-override = capHeight** therefore centres the
   caps at *every* size and line-height in the app, in one place. The sum is set to
   1.244 for both faces so they share a line box and `normal` line-height matches
   what the theme replaced.

     Rockwell cap 0.736 -> 99.0% / 25.4%   (difference 0.736)
     Rokkitt  cap 0.583 -> 91.3% / 33.1%   (difference 0.583)

   Descenders overhang the line box by ~0.04em under these numbers, which is ordinary
   CSS behaviour and what every other font here does too. If you change either cap
   height, keep the difference equal to it or the labels drift again.

   Rockwell itself is Monotype-licensed and cannot be shipped. It IS installed by
   default on macOS and on any Windows box with Office, so local() picks it up and
   most users get the real face. Rokkitt (Google Fonts, OFL) is the fallback - the
   free typeface drawn in Rockwell's mould - self-hosted in public/fonts/ so it can
   carry the same metric overrides, which a Google Fonts <link> cannot. Note Rokkitt
   has a much smaller cap height, so the fallback renders visibly smaller; that is
   the face's nature, not a bug.

   Only the woff2 files are fetched, and only when text actually renders in these
   families - i.e. only under this theme. --font-mono is deliberately left alone: it
   backs hex fields and the share textarea, where a slab serif would break digit
   alignment. */
@font-face {
  font-family: 'Rockwell Fader';
  src: local('Rockwell'), local('Rockwell-Regular'), local('Rockwell Nova');
  font-weight: 400;
  font-style: normal;
  ascent-override: 99.0%;
  descent-override: 25.4%;
  line-gap-override: 0%;
}
@font-face {
  font-family: 'Rockwell Fader';
  src: local('Rockwell Bold'), local('Rockwell-Bold'), local('Rockwell Nova Bold');
  font-weight: 700;
  font-style: normal;
  ascent-override: 99.0%;
  descent-override: 25.4%;
  line-gap-override: 0%;
}
@font-face {
  font-family: 'Rokkitt Fader';
  src: url('/fonts/rokkitt-latin.woff2') format('woff2');
  /* Variable wght, so one file covers every weight the app asks for. */
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                 U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
                 U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
  ascent-override: 91.3%;
  descent-override: 33.1%;
  line-gap-override: 0%;
}
@font-face {
  font-family: 'Rokkitt Fader';
  src: url('/fonts/rokkitt-latin-ext.woff2') format('woff2');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
                 U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
                 U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
  ascent-override: 91.3%;
  descent-override: 33.1%;
  line-gap-override: 0%;
}
html[data-theme="water"] {
  --font-body:    'Rockwell Fader', 'Rokkitt Fader', Rockwell, Rokkitt, Georgia, serif;
  --font-display: var(--font-body);
}

/* The backdrop layer. Created and torn down by water-bg.js, which also owns the
   WebGL lifecycle; it only ever exists while this theme is on and only on the app
   page. z-index -1 on a child of <body> paints under the page content but over the
   root background - which works because <html> carries no background here and
   body's is propagated to the canvas. Never give html[data-theme="water"] a
   background of its own or this layer vanishes behind it. */
#water-bg { display: none; }
html[data-theme="water"] body.app-page #water-bg {
  display: block;
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  overflow: hidden;
  /* Tuned against the shader's *measured* output, not its theoretical range. Half a
     million background pixels sampled off a real frame put the darkest raw fog at
     rgb(41,104,157) - a mid steel blue. The near-black rgb(11,11,74) that
     `lowlightColor` implies never actually appears: base white dominates the mix, so
     `f` never gets high enough. An earlier 0.62 / 0.32 pair was tuned for that
     phantom worst case and washed the fog out to almost nothing.
     At 0.95 / 0.10 the darkest cloud lands near rgb(71,125,171) and the median near
     rgb(188,210,236). Every plated surface still clears 4.5:1 on the ink it carries
     (checked against an rgb(20,70,130) safety floor, darker than anything observed);
     the loose labels in .center-controls do not, which is what the halo below is
     for. Re-measure before moving these. */
  opacity: 0.95;
  /* A slight white scrim over the canvas rather than a lower layer opacity. Opacity
     alone fades the fog toward the flat --bg and flattens the colour out of it; the
     scrim keeps the hue and only lifts the floor. z-index 1 to clear the canvas,
     which vanta pins at 0. */
  /* Shown while three.js is in flight and left in place when WebGL is unavailable
     or the visitor prefers reduced motion, so the theme still reads as water with
     no shader at all. The opaque canvas covers it whenever the shader is running,
     so these alphas only ever affect the fallback. */
  background-color: #ffffff;
  background-image:
    radial-gradient(120% 80% at 18% 10%,  rgba(26,190,240,0.95) 0%, transparent 58%),
    radial-gradient(95% 70% at 84% 26%,   rgba(30,30,230,0.62)  0%, transparent 60%),
    radial-gradient(130% 95% at 50% 100%, rgba(11,11,74,0.55)   0%, transparent 62%);
  /* Own compositor layer: the page scrolls on mobile, and a fixed layer that
     repaints every scroll frame is the usual source of jank. */
  will-change: transform;
}
html[data-theme="water"] body.app-page #water-bg::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 1;
  background: rgba(255,255,255,0.10);
}

/* Let the fog read through the app chrome. Kept off the shared --bg-header /
   --bg-panel-solid tokens, which also back modals, toasts and the landing-page nav
   and must stay opaque. Every alpha here is set so the weakest ink the surface
   carries still clears 4.5:1 over the darkest cloud. The footer is 0.84 rather than
   the 0.74 the circuit block uses: --footer-text only reaches 4.1:1 at 0.74 against
   this backdrop. */
html[data-theme="water"] body.app-page header {
  background: rgba(244,251,254,0.84);
}
html[data-theme="water"] body.app-page .app-footer {
  background: rgba(244,251,254,0.84);
}
/* 0.75, not the 0.90 the circuit block uses, and the difference is not a whim.
   Circuit is a dark panel over thin BRIGHT gold traces, so a 10% opening reads
   easily - the eye is good at high-frequency light-on-dark. Water is a near-white
   panel over a pale fog whose median sits within ~50 units of the panel colour, so
   at 0.90 the show-through measured only 4 dL* across the whole panel: technically
   translucent, visually flat. 0.75 puts it at ~10 dL*, which reads as a wash.
   This is what forced --text-muted / --text-dim / --text-faint darker in the water
   palette: at their old values --text-faint hit 4.66:1 at 0.90 and had no room to
   give. If you reopen the panels further, those three have to follow. */
html[data-theme="water"] body.app-page .panel,
html[data-theme="water"] body.app-page .playlist-section {
  background: rgba(244,251,254,0.75);
}

/* The controls row has no panel behind it, so a handful of labels sit straight on
   the fog, where --text manages only 2.4:1. Plating the row would cover the widest
   opening the backdrop shows through, and the loose text is only a few short
   strings, so each one gets its own scrim instead: stacked white text-shadows, the
   inner two doubled so they saturate to near-opaque within a pixel of the glyph and
   then feather out. That puts the effective surround at ~rgb(239,242,246) and the
   ink at better than 13:1, without drawing a box. Scoped to the loose text only -
   on a button label, over a background of its own, the glow would just look smeared. */
html[data-theme="water"] body.app-page .center-controls .setting-label,
html[data-theme="water"] body.app-page .center-controls .setting-unit,
html[data-theme="water"] body.app-page .center-controls .fade-progress-label,
html[data-theme="water"] body.app-page .center-controls .silence-deck-indicator,
html[data-theme="water"] body.app-page .center-controls .balance-progress-label {
  color: var(--text);
  text-shadow:
    0 0 2px #fff, 0 0 2px #fff,
    0 0 3px #fff, 0 0 3px #fff,
    0 0 6px #fff, 0 0 10px #fff;
}

/* The FADE button's glow, tint and fading-state label are hardcoded gold in the
   base sheet - pale gold on a pale blue button is unreadable, so this is the one
   place the brand accent has to give. Everything else gold (sliders, chips, footer
   links) stays: brass on water reads as nautical, and it keeps the accent
   consistent with the other five themes. */
html[data-theme="water"] .fade-btn::after {
  background: linear-gradient(160deg, rgba(26,190,240,0.22), rgba(30,30,230,0.18));
}
html[data-theme="water"] .fade-btn:hover:not(:disabled) {
  border-color: #0e7fb4;
  box-shadow: 0 0 18px rgba(26,143,199,0.35);
}
html[data-theme="water"] .fade-btn.fading {
  border-color: #0e7fb4;
  color: var(--fade-btn-color);
  animation-name: pulse-glow-water;
}
html[data-theme="water"] .fade-progress-fill {
  background: linear-gradient(90deg, #0e7fb4, #1e5ee6);
}
@keyframes pulse-glow-water {
  0%, 100% { box-shadow: 0 0 8px  rgba(26,143,199,0.25); }
  50%      { box-shadow: 0 0 22px rgba(26,143,199,0.55); }
}

/* Panels cover nearly the whole screen below this width, so the 0.90 that reads as
   atmosphere on desktop - where wide gutters carry the fog - instead puts clouds
   behind every control, and disabled controls sit at opacity 0.35 so they read
   straight through. Keep the panel interiors calm here and let the fog show in the
   header, footer, controls area and page margins. Same tradeoff, same numbers as
   the circuit block. */
@media (max-width: 860px) {
  html[data-theme="water"] body.app-page .panel,
  html[data-theme="water"] body.app-page .playlist-section {
    background: rgba(244,251,254,0.88);
  }
}
