/*
 * jam.css
 *
 * Visual language for ToneForge Jam.
 *
 * The page lives in three states (intake → band room → performance) and
 * the layout is intentionally calm — large type, generous spacing, no
 * gradients. The product promise is "be inside the song", so chrome stays
 * out of the way.
 */

:root {
  --bg: #0b0d10;
  --panel: #14181d;
  --panel-2: #1c2128;
  --text: #f3f4f6;
  --text-dim: #8b95a5;
  --accent: #f97316;       /* warm orange */
  --accent-2: #fdba74;
  --good: #4ade80;
  --warn: #fbbf24;
  --bad: #f87171;
  --border: #232a33;
}

* { box-sizing: border-box; }
html, body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--text);
  font-family: -apple-system, BlinkMacSystemFont, "Inter", "Helvetica Neue",
               Arial, sans-serif;
  font-size: 16px;
  height: 100%;
}

/* ---------------------------------------------------------------- header */
.jam-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 18px 28px;
  border-bottom: 1px solid var(--border);
}
.brand {
  font-weight: 700;
  font-size: 18px;
  letter-spacing: 0.5px;
  color: var(--text);
  text-decoration: none;
}
.jam-header nav { display: flex; gap: 18px; }
.jam-header nav a {
  color: var(--text-dim);
  text-decoration: none;
  font-size: 14px;
  padding: 6px 10px;
  border-radius: 6px;
}
.jam-header nav a.active {
  color: var(--text);
  background: var(--panel);
}

/* ---------------------------------------------------------------- views */
#jam-app { padding: 32px 28px 80px; max-width: 1100px; margin: 0 auto; }
.view { display: none; }
.view.active { display: block; }

/* ---------------------------------------------------------- intake view */
.hero {
  font-size: 56px;
  font-weight: 700;
  margin: 56px 0 12px;
  line-height: 1.05;
}
.sub { color: var(--text-dim); font-size: 20px; margin: 0 0 24px; }

/* engine banner --------------------------------------------------------- */
.engine-banner {
  display: flex;
  align-items: center;
  gap: 14px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 14px 18px;
  margin: 0 0 18px;
  max-width: 880px;
}
.engine-banner-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--text-dim);
  flex-shrink: 0;
}
.engine-banner-text {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.engine-banner-text strong { font-size: 14px; font-weight: 600; }
.engine-banner-text span { font-size: 12px; color: var(--text-dim); }
.engine-banner--unknown .engine-banner-dot { background: var(--text-dim); }
.engine-banner--off .engine-banner-dot { background: var(--bad); }
.engine-banner--starting .engine-banner-dot {
  background: var(--warn);
  animation: pulse 1.2s ease-in-out infinite;
}
.engine-banner--on { border-color: rgba(74, 222, 128, 0.35); }
.engine-banner--on .engine-banner-dot { background: var(--good); }
@keyframes pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.35; }
}
.intake {
  display: grid;
  grid-template-columns: 1fr 220px auto;
  gap: 12px;
  align-items: stretch;
  max-width: 880px;
}
.intake input, .intake select {
  background: var(--panel);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 14px 16px;
  font-size: 16px;
  height: 52px;
  font-family: inherit;
}
.intake input:focus, .intake select:focus {
  outline: none;
  border-color: var(--accent);
}
.intake button {
  background: var(--accent);
  color: #1a0f06;
  border: none;
  border-radius: 10px;
  padding: 0 22px;
  font-size: 16px;
  font-weight: 600;
  cursor: pointer;
  height: 52px;
}
.intake button:hover { background: var(--accent-2); }
.hint { color: var(--text-dim); font-size: 14px; margin-top: 16px; }
.linklike {
  background: none;
  border: none;
  color: var(--accent);
  cursor: pointer;
  padding: 0;
  font: inherit;
  text-decoration: underline;
}

/* ------------------------------------------------------- band-room view */
.bandroom-header { margin-bottom: 28px; }
.song-meta h2 { margin: 0 0 6px; font-size: 28px; }
.status { color: var(--text-dim); font-size: 15px; }
.progress {
  margin-top: 14px;
  height: 4px;
  background: var(--panel);
  border-radius: 2px;
  overflow: hidden;
}
.progress-bar {
  height: 100%;
  width: 0%;
  background: var(--accent);
  transition: width 0.4s ease;
}

.bandroom-stage {
  position: relative;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: repeat(3, 1fr);
  gap: 18px;
  aspect-ratio: 16 / 10;
  max-width: 900px;
  margin: 0 auto;
}
.slot {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 16px;
  padding: 16px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  transition: background 0.3s, border-color 0.3s, transform 0.3s;
  opacity: 0.55;
}
.slot.ready { opacity: 1; border-color: var(--good); }
.slot.user-slot {
  background: var(--panel-2);
  border-color: var(--accent);
  opacity: 1;
}
/* Pin the user slot to dead center of the 3x3 grid */
.slot.center { grid-column: 2; grid-row: 2; }
/* Place the remaining four around the user */
.slot[data-stem="vocals"] { grid-column: 2; grid-row: 1; }
.slot[data-stem="keys"]   { grid-column: 1; grid-row: 1; }
.slot[data-stem="bass"]   { grid-column: 1; grid-row: 3; }
.slot[data-stem="drums"]  { grid-column: 3; grid-row: 3; }

.slot-icon { font-size: 36px; margin-bottom: 6px; }
.slot-name { font-size: 16px; font-weight: 600; }
.slot-state { font-size: 12px; color: var(--text-dim); margin-top: 4px; }
.slot.ready .slot-state { color: var(--good); }

#bandroom-cancel { margin: 28px auto 0; display: block; }

/* ------------------------------------------------------- performance view */
.perform-top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  margin-bottom: 24px;
}
.np-title { font-size: 22px; font-weight: 700; }
.np-meta { color: var(--text-dim); font-size: 14px; margin-top: 4px; }
a.np-source {
  display: inline-block;
  margin-top: 8px;
  padding: 3px 10px;
  border: 1px solid var(--border);
  border-radius: 999px;
  color: var(--text-dim);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.3px;
  text-decoration: none;
  transition: border-color 120ms, color 120ms, background 120ms;
}
a.np-source:hover,
a.np-source:focus-visible {
  border-color: var(--accent);
  color: var(--text);
  background: rgba(249, 115, 22, 0.06);
}
/* Attribution credit line (D-024) — shown only for licensed sessions. */
a.np-credit {
  display: block;
  margin-top: 6px;
  color: var(--text-dim);
  font-size: 11px;
  text-decoration: none;
  max-width: 420px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
a.np-credit[href]:hover,
a.np-credit[href]:focus-visible {
  color: var(--text);
  text-decoration: underline;
}

.perform-stage {
  display: grid;
  grid-template-columns: 320px 1fr;
  gap: 24px;
  margin-bottom: 28px;
}
.user-card {
  background: var(--panel-2);
  border: 1px solid var(--accent);
  border-radius: 16px;
  padding: 20px;
}
.user-card-label {
  color: var(--text-dim);
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}
.user-card-tone { font-size: 22px; font-weight: 700; margin: 6px 0; }
.user-card-meta { color: var(--text-dim); font-size: 13px; margin-bottom: 16px; }
.user-card-controls { display: flex; flex-direction: column; gap: 8px; }
#connect-btn.connected {
  background: var(--accent, #4ade80);
  color: #0a0a0a;
  border-color: var(--accent, #4ade80);
}
/* Brief visual pulse so a click on the Connect button always has an
   on-element response, even when the resulting state change is purely
   textual (e.g. "already paired — no preset to push"). Without this,
   the only feedback was a status flash that auto-cleared, which made
   the button feel dead. */
#connect-btn.clicked-pulse {
  animation: connect-btn-pulse 480ms ease-out;
}
@keyframes connect-btn-pulse {
  0%   { box-shadow: 0 0 0 0 rgba(255, 122, 0, 0.55); }
  60%  { box-shadow: 0 0 0 10px rgba(255, 122, 0, 0); }
  100% { box-shadow: 0 0 0 0 rgba(255, 122, 0, 0); }
}
.connect-status {
  font-size: 12px;
  color: var(--text-dim);
  line-height: 1.4;
  padding: 2px 2px;
}
.connect-status.ok { color: var(--accent, #4ade80); }
.connect-launcher-link {
  display: inline-block;
  margin-top: 4px;
  color: var(--accent, #ff7a00);
  text-decoration: underline;
  font-weight: 500;
}
.connect-launcher-link:hover { text-decoration: none; }
.connect-restart-btn {
  display: inline-block;
  margin-top: 6px;
  padding: 4px 10px;
  font-size: 12px;
  font-weight: 500;
  color: var(--text-dim, #aaa);
  background: transparent;
  border: 1px solid var(--text-dim, #aaa);
  border-radius: 4px;
  cursor: pointer;
  font-family: inherit;
}
.connect-restart-btn:hover {
  color: var(--accent, #ff7a00);
  border-color: var(--accent, #ff7a00);
}
.connect-restart-btn:disabled {
  opacity: 0.5;
  cursor: default;
}
/* Phase 6: install / launch CTA inside the Connect status card.
   `connect-launch-btn` is the primary action when Connect.app is on
   disk but not paired; styled more prominently than the restart
   button (filled accent rather than outlined neutral) so it reads
   as the recommended next step. `connect-install-link` is the
   download CTA when Connect.app isn't installed yet. */
.connect-launch-btn {
  display: inline-block;
  margin-top: 6px;
  padding: 6px 14px;
  font-size: 13px;
  font-weight: 600;
  color: #fff;
  background: var(--accent, #ff7a00);
  border: 1px solid var(--accent, #ff7a00);
  border-radius: 4px;
  cursor: pointer;
  font-family: inherit;
}
.connect-launch-btn:hover {
  filter: brightness(1.1);
}
.connect-launch-btn:disabled {
  opacity: 0.5;
  cursor: default;
}
.connect-launch-version {
  display: inline-block;
  margin-left: 6px;
  font-size: 11px;
  color: var(--text-dim, #aaa);
  letter-spacing: 0.3px;
}
.connect-install-link {
  display: inline-block;
  margin-top: 4px;
  color: var(--accent, #ff7a00);
  text-decoration: underline;
  font-weight: 500;
}
.connect-install-link:hover { text-decoration: none; }
.connect-gain {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 8px;
  margin-top: 4px;
}
.connect-gain label {
  font-size: 12px;
  color: var(--text-dim);
  text-transform: uppercase;
  letter-spacing: 0.5px;
}
.connect-gain input[type="range"] {
  width: 100%;
}
.connect-gain-readout {
  font-variant-numeric: tabular-nums;
  font-size: 12px;
  color: var(--text-dim);
  min-width: 48px;
  text-align: right;
}
.connect-gain-hint {
  grid-column: 1 / -1;
  font-size: 11px;
  color: var(--text-dim);
  line-height: 1.4;
}

.stem-rack {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 16px;
  padding: 14px 18px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.stem-row {
  display: grid;
  grid-template-columns: 100px 1fr 60px 60px;
  align-items: center;
  gap: 14px;
  padding: 10px 4px;
  border-bottom: 1px solid var(--border);
}
.stem-row:last-child { border-bottom: none; }
.stem-row .name { font-weight: 600; font-size: 15px; display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.stem-row .name.original-instrument { color: var(--accent); }
.stem-row .stem-match {
  font-weight: 400;
  font-size: 11px;
  color: var(--text-dim);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.stem-row .gain { -webkit-appearance: none; appearance: none; height: 4px; border-radius: 2px; background: var(--border); }
.stem-row .gain::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  height: 16px;
  width: 16px;
  border-radius: 50%;
  background: var(--accent);
  cursor: pointer;
}

/* Mixer master channels (Song / Guitar input). Same row template as
 * stem rows so the CSS grid stays uniform; differentiation is purely
 * visual -- bolder name, brighter accent for Guitar input, and an
 * accent-coloured divider after the Guitar Input row that separates
 * master channels from per-stem channels. */
.stem-row.mixer-channel-row .name.mixer-channel-name {
  font-weight: 700;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  font-size: 13px;
}
.stem-row.mixer-channel-song .name { color: var(--text); }
.stem-row.mixer-channel-guitar .name { color: var(--accent); }
.stem-row.mixer-channel-divider {
  border-bottom: 2px solid var(--accent);
  margin-bottom: 6px;
  padding-bottom: 12px;
}

/* ---------------------------------------------------------- transport */
.transport {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 16px;
  padding: 18px 20px;
}
.transport-row {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-bottom: 14px;
}
.time {
  margin-left: auto;
  font-variant-numeric: tabular-nums;
  color: var(--text-dim);
  font-size: 14px;
}
.section-bar {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
}
.section-pill {
  border: 1px solid var(--border);
  background: var(--panel-2);
  color: var(--text);
  border-radius: 999px;
  padding: 6px 12px;
  font-size: 13px;
  cursor: pointer;
  transition: background 0.2s, border-color 0.2s, color 0.2s;
}
.section-pill:hover { border-color: var(--accent); }
.section-pill.active { background: var(--accent); color: #1a0f06; border-color: var(--accent); }

/* Phase-5 structural-role badge on section pills.
 *
 * Renders the ANCHOR/DEVELOPMENT/UNIQUE classification from
 * backend/structural_role_classifier_design.md as a single-letter
 * chip appended to the pill text. The pill TEXT still names the
 * detected section type (Verse 1, Chorus, etc.); the badge surfaces
 * how recurrent the chord trigrams in this section are versus the
 * rest of the song.
 *
 * Color semantics:
 *   ANCHOR      — warm accent (this is "the part you hear all song")
 *   DEVELOPMENT — neutral panel (variation on the anchor material)
 *   UNIQUE      — desaturated dim (one-off material: intro, bridge)
 *
 * Glyph stays 1 char to keep the bar legible on songs with many
 * sections.
 */
.section-role-badge {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 5px;
  border-radius: 6px;
  font-size: 10px;
  font-weight: 700;
  line-height: 1.3;
  letter-spacing: 0.5px;
  vertical-align: middle;
  border: 1px solid var(--border);
}
.section-role-badge.role-anchor {
  background: var(--accent);
  color: #1a0f06;
  border-color: var(--accent);
}
.section-role-badge.role-development {
  background: var(--panel);
  color: var(--text);
  border-color: var(--border);
}
.section-role-badge.role-unique {
  background: transparent;
  color: var(--text-dim);
  border-color: var(--border);
}
/* When the pill itself is the active loop region, keep the badge
 * legible against the orange background by inverting only the
 * anchor variant (which would otherwise blend in). */
.section-pill.active .section-role-badge.role-anchor {
  background: #1a0f06;
  color: var(--accent);
  border-color: #1a0f06;
}
.section-pill.active .section-role-badge.role-development,
.section-pill.active .section-role-badge.role-unique {
  border-color: #1a0f06;
}

/* Fix B: suspicious-section indicator.
 *
 * When the duration-guard post-pass
 * (analysis/section_naming.py:flag_suspicious_durations) decides a
 * section's duration is structurally implausible for its label
 * (e.g. a 70s CHORUS that clearly ate several boundaries the
 * RMS-novelty detector missed), the pill gets an amber dashed
 * border plus a ⚠ glyph. The label itself is unchanged — this is
 * an advisory hint that the boundary detector under-segmented,
 * not a re-classification.
 *
 * Amber (not red) so it doesn't compete visually with the active
 * loop pill (which uses --accent = orange). Dashed border makes
 * "something's provisional here" legible at a glance.
 */
.section-pill-suspicious {
  border-style: dashed;
  border-color: #d4a017;
}
.section-pill-suspicious:hover { border-color: #f0b820; }
.section-pill-suspicious.active {
  /* Keep the dashed provisional look even when this pill is the
   * active loop region — swapping to a solid orange border would
   * hide the fact that this section boundary is untrusted. */
  border-style: dashed;
  border-color: #d4a017;
}
.section-duration-warn {
  display: inline-block;
  margin-left: 6px;
  font-size: 11px;
  line-height: 1;
  color: #d4a017;
  vertical-align: middle;
  cursor: help;
}
.section-pill.active .section-duration-warn { color: #1a0f06; }

.loop-status { color: var(--text-dim); font-size: 13px; margin-top: 10px; }

/* ----------------------------------------------------- chord ribbon
 *
 * JAM Alpha visual reference. A horizontal strip of chord pills,
 * scrolled by `transform: translateX(...)` from updateChordPlayhead()
 * such that the currently-sounding chord sits under the fixed
 * playhead marker. `transition: none` on the transform is mandatory:
 * loop snap-back must jump, not animate. The pill currently active
 * gets `.chord-pill.active` (highlight only; no layout shift).
 */
.chord-ribbon {
  position: relative;
  margin: 16px 0;
  height: 64px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--panel-2);
  overflow: hidden;
}
.chord-ribbon-playhead {
  position: absolute;
  left: 50%;
  top: 0;
  bottom: 0;
  width: 2px;
  background: var(--accent);
  z-index: 2;
  pointer-events: none;
}
.chord-ribbon-strip {
  position: absolute;
  left: 50%;
  top: 0;
  bottom: 0;
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 0 8px;
  transform: translateX(0);
  /* No transition: loop snap-back must be instant. */
  transition: none;
  will-change: transform;
}
.chord-pill {
  flex: 0 0 auto;
  min-width: 56px;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--panel);
  color: var(--text);
  font-size: 16px;
  font-weight: 500;
  padding: 0 10px;
  white-space: nowrap;
}
.chord-pill.active {
  background: var(--accent);
  color: #1a0f06;
  border-color: var(--accent);
  font-weight: 700;
}

/* Guidance-mode visual gating intentionally still parked.
 *
 * The per-section classifier (analysis/guidance_mode.py) emits a
 * mode + confidence per section and JS reflects that on the chord
 * ribbon / lane via data-guidance-mode. The synthetic-fixture-tuned
 * thresholds over-classify real songs as riff/lead — flipping the
 * visuals on sections that are still musically chord-shaped — so
 * mode-based muting stays parked here as a no-op until the
 * classifier is calibrated on a real-song corpus.
 *
 * What IS shipped is the observation-based data-section-chordy
 * gate (no classifier dependency). JS computes chord count + rate
 * inside the active section in _applyGuidanceModeForActiveSection
 * and emits data-section-chordy="true|false". When chordy=false
 * the chord ribbon would be showing the rapid-flip garbage labels
 * that the chord detector produces on picked notes (Birds-of-Tokyo
 * failure mode), and the dual chord-bar / tab-lane display is hard
 * to follow. We hide the ribbon entirely so the tab lane stands
 * alone as the primary guidance. The chord-guidance NOW PLAYING
 * card stays visible because it carries shape/finger info that's
 * still useful when valid chord data exists (this is a no-op when
 * the card already shows nothing because there are no chords).
 */
#chord-ribbon[data-section-chordy="false"] { display: none; }

/* ---------------------------------------------------------- buttons */
button {
  font-family: inherit;
  cursor: pointer;
}
button.primary {
  background: var(--accent);
  color: #1a0f06;
  border: none;
  border-radius: 10px;
  padding: 10px 18px;
  font-weight: 600;
}
button.primary:hover { background: var(--accent-2); }
button.ghost {
  background: transparent;
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 10px 18px;
}
button.ghost:hover { border-color: var(--accent); }
button.big { font-size: 16px; padding: 12px 22px; }
#perform-settings {
  font-size: 20px;
  line-height: 1;
  padding: 6px 12px;
}
button[disabled] { opacity: 0.45; cursor: not-allowed; }

.intake-error {
  margin-top: 14px;
  padding: 10px 14px;
  background: rgba(211, 106, 69, 0.12);
  border: 1px solid #d36a45;
  border-radius: 10px;
  color: #f0a585;
  font-size: 14px;
}

/* ----------------------------------------------- Onboarding modal */
/* Device-discovery first-run prompt (§8). The modal sits as a sibling of
   <main> so the backdrop covers the whole viewport without inheriting the
   jam-app padding. Visible only when jam.js detects no persisted device
   preferences (GET /api/device/preferences → null). */
.onboarding-modal[hidden] { display: none !important; }
.onboarding-modal {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
}
.onboarding-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.72);
  backdrop-filter: blur(2px);
}
.onboarding-panel {
  position: relative;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 16px;
  padding: 28px 28px 22px;
  width: min(480px, calc(100vw - 32px));
  max-height: calc(100vh - 32px);
  overflow-y: auto;
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.6);
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.onboarding-title {
  font-size: 22px;
  font-weight: 700;
  margin: 0;
  line-height: 1.2;
}
.onboarding-sub {
  margin: 0;
  font-size: 14px;
  color: var(--text-dim);
}
.onboarding-options {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: 4px;
}
.onboarding-option {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: 10px;
  cursor: pointer;
  background: var(--panel-2);
  font-size: 15px;
  transition: border-color 120ms, background 120ms;
}
.onboarding-option:hover { border-color: var(--accent); }
.onboarding-option input[type="radio"] {
  accent-color: var(--accent);
  margin: 0;
}
.onboarding-option input[type="radio"]:checked + span { color: var(--text); font-weight: 600; }
.onboarding-detected-badge {
  margin-left: auto;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  background: rgba(80, 200, 120, 0.15);
  color: var(--good, #5c8);
  border: 1px solid rgba(80, 200, 120, 0.4);
}
.onboarding-error {
  color: var(--bad);
  font-size: 13px;
  margin-top: 4px;
}
.onboarding-input-row[hidden],
.onboarding-input-picker[hidden] { display: none !important; }
.onboarding-input-row {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  color: var(--text-dim, #aaa);
  margin-top: 4px;
}
.onboarding-input-name { color: var(--text); font-weight: 600; }
.onboarding-input-change-link {
  background: none;
  border: none;
  color: var(--accent);
  cursor: pointer;
  padding: 0;
  font-size: 13px;
  text-decoration: underline;
}
.onboarding-input-picker {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin-top: 4px;
}
.onboarding-input-picker-label { font-size: 13px; color: var(--text-dim, #aaa); }
.onboarding-input-picker select {
  padding: 8px;
  background: var(--panel, #1a1a1a);
  color: var(--text);
  border: 1px solid var(--border, #333);
  border-radius: 4px;
}
#onboarding-submit { margin-top: 8px; align-self: flex-end; }

/* ----------------------------------------------- Settings popover */
.settings-popover {
  position: absolute;
  right: 20px;
  top: 70px;
  z-index: 20;
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 14px 18px;
  min-width: 260px;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
}
.settings-row + .settings-row { margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--border); }
.settings-row label { display: block; font-size: 14px; padding: 3px 0; cursor: pointer; }
.settings-label { font-weight: 600; font-size: 13px; margin-bottom: 6px; color: var(--text-dim); }
.settings-hint { font-size: 11px; color: var(--text-dim); margin-top: 2px; padding-left: 22px; }
.settings-hint--inline { padding-left: 0; margin: 0 0 6px; }
#setting-device-reset { font-size: 12px; padding: 4px 10px; }
.launchpad-status--idle { color: var(--text-dim); }
.launchpad-status--ok { color: #6ce07a; }
.launchpad-status--error { color: #e07a6c; }
.launchpad-openjam { margin-top: 6px; padding-left: 22px; }
.launchpad-openjam label { display: block; font-size: 12px; padding: 3px 0; }
.launchpad-openjam select { font-size: 12px; margin-left: 4px; }

/* ----------------------------------------------- Intonation panel */
.intonation-panel {
  margin: 12px 0;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 14px 18px;
  display: grid;
  grid-template-columns: 1fr auto auto;
  gap: 18px;
  align-items: center;
}
.intonation-main { display: grid; grid-template-columns: auto 1fr; gap: 18px; align-items: center; }
.intonation-note { text-align: center; min-width: 70px; }
.intonation-note-name { font-size: 28px; font-weight: 700; color: var(--text); line-height: 1; }
.intonation-cents { font-size: 12px; color: var(--text-dim); margin-top: 4px; }
.intonation-meter {
  position: relative;
  height: 18px;
  min-width: 220px;
  display: flex;
  align-items: center;
}
.intonation-meter-track {
  position: absolute;
  inset: 8px 0;
  background: var(--border);
  border-radius: 2px;
}
.intonation-meter-center {
  position: absolute;
  left: 50%;
  top: 0;
  bottom: 0;
  width: 2px;
  background: var(--text-dim);
  transform: translateX(-50%);
  opacity: 0.5;
}
.intonation-meter-needle {
  position: absolute;
  left: 50%;
  top: 2px;
  bottom: 2px;
  width: 4px;
  background: var(--accent);
  border-radius: 2px;
  transform: translateX(-50%);
  transition: left 80ms ease-out, background 200ms;
}
.intonation-meter-needle.flat { background: #d36a45; }
.intonation-meter-needle.sharp { background: #d36a45; }
.intonation-meter-needle.in-tune { background: #58c47a; }
.intonation-status { font-size: 11px; color: var(--text-dim); grid-column: 1 / -1; margin-top: 4px; }
.intonation-rolling { text-align: center; padding-left: 14px; border-left: 1px solid var(--border); }
.intonation-rolling-label { font-size: 11px; color: var(--text-dim); }
.intonation-rolling-pct { font-size: 20px; font-weight: 700; margin-top: 2px; }
.intonation-full { padding-left: 14px; border-left: 1px solid var(--border); font-size: 12px; min-width: 180px; }
.intonation-full-row { display: flex; justify-content: space-between; gap: 14px; padding: 2px 0; }
.intonation-full-label { color: var(--text-dim); }
.intonation-full-val { font-weight: 600; font-variant-numeric: tabular-nums; }
.intonation-full-divider { height: 1px; background: var(--border); margin: 6px 0; opacity: 0.6; }

/* ============================== tone card ============================== */
/* Monitor-chain ToneRecommendation surface inside .user-card. Tier
   badge colour communicates the confidence band at a glance:
     --tone-high   = green, ready to apply
     --tone-medium = amber, suggested
     --tone-low    = muted, low confidence
     --tone-unknown= grey, default chain
*/
.tone-card {
  margin: 12px 0 16px;
  padding: 12px 14px;
  border-radius: 12px;
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid var(--border, #2a2a2a);
}
.tone-card-header {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 6px;
}
.tone-tier-badge {
  display: inline-block;
  font-size: 10px;
  letter-spacing: 0.5px;
  text-transform: uppercase;
  padding: 2px 8px;
  border-radius: 999px;
  font-weight: 700;
  background: #2a2a2a;
  color: var(--text, #eee);
}
.tone-tier-badge--high    { background: #14532d; color: #bbf7d0; }
.tone-tier-badge--medium  { background: #78350f; color: #fde68a; }
.tone-tier-badge--low     { background: #3a3a3a; color: #d4d4d4; }
.tone-tier-badge--unknown { background: #2a2a2a; color: #9ca3af; }
.tone-card-title {
  font-weight: 600;
  font-size: 14px;
}
.tone-card-rationale {
  color: var(--text-dim, #aaa);
  font-size: 12px;
  line-height: 1.4;
  margin: 4px 0 10px;
}
.tone-card-actions {
  display: flex;
  gap: 8px;
}
.tone-card-alternates {
  margin-top: 10px;
  padding-top: 8px;
  border-top: 1px solid var(--border, #2a2a2a);
}
.tone-card-alternates-label {
  font-size: 10px;
  color: var(--text-dim, #aaa);
  text-transform: uppercase;
  letter-spacing: 0.5px;
  margin-bottom: 4px;
}
.tone-card-alternates-list {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.tone-alternate-chip {
  border: 1px solid var(--border, #2a2a2a);
  background: rgba(255, 255, 255, 0.02);
  color: var(--text, #eee);
  font-size: 11px;
  padding: 3px 10px;
  border-radius: 999px;
  cursor: pointer;
}
.tone-alternate-chip:hover {
  background: rgba(255, 255, 255, 0.08);
}

/* ============================================================
 * perform v2 — musician-first three-column layout
 * ============================================================
 *
 * Activated only when #view-perform is the visible view, via the
 * body.perform-active class toggled in jam.js showView(). The
 * intake and band-room views keep their existing 1100px max-width.
 */
body.perform-active #jam-app {
  max-width: 1440px;
  padding-bottom: 140px; /* room for the fixed playback bar */
}

/* hide the legacy perform-top header; the new header lives below */
body.perform-active #view-perform .perform-top { display: none; }
body.perform-active #view-perform .perform-stage { display: none; }
body.perform-active #view-perform .transport { display: none; }
body.perform-active #view-perform > .chord-ribbon { display: none; }

.perform-header {
  display: grid;
  grid-template-columns: 1fr auto auto auto;
  align-items: center;
  gap: 16px;
  margin-bottom: 20px;
  padding-bottom: 16px;
  border-bottom: 1px solid var(--border);
}
.perform-header .ph-song { min-width: 0; }
.perform-header .ph-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  position: relative;
}
.connect-pill {
  background: transparent;
  border: 1px solid var(--border);
  color: var(--text-dim);
  border-radius: 999px;
  padding: 6px 14px;
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
  letter-spacing: 0.3px;
  transition: border-color 120ms, color 120ms, background 120ms;
}
.connect-pill:hover { border-color: var(--accent); }
.connect-pill.connected {
  border-color: var(--accent);
  color: var(--accent);
  background: rgba(249, 115, 22, 0.08);
}
.connect-pill.connecting {
  border-color: var(--warn);
  color: var(--warn);
}
.connect-pill.offline {
  border-color: var(--border);
  color: var(--text-dim);
}
/* Phase 1: distinct error styling. Reuses --err (red accent token)
   to separate "auto-reconnect exhausted" from the routine
   transient-offline state above. */
.connect-pill.errored {
  border-color: var(--err, #ff4d4d);
  color: var(--err, #ff4d4d);
}

.connect-monitor-pop {
  position: absolute;
  right: 0;
  top: calc(100% + 8px);
  z-index: 30;
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 12px 14px;
  min-width: 240px;
  box-shadow: 0 12px 30px rgba(0, 0, 0, 0.5);
}
.connect-monitor-pop[hidden] { display: none; }

/* grid */
.perform-grid {
  display: grid;
  /* 2-column layout: "Your slot" sidebar + everything else (chord
     ribbon, NOW PLAYING card, LEAD PART, and the relocated Stems
     mixer underneath). The prior 3-column layout had a separate
     right-hand stems column; that was moved into the centre column
     so the chord ribbon + chord card span the full available width. */
  grid-template-columns: 320px 1fr;
  gap: 24px;
  margin-bottom: 24px;
  align-items: start;
}
/* Launchpad tab: sidebar is meaningless (pads are the instrument),
   so collapse the first column and let the center reclaim the width.
   NB: `.stage-col { display: flex }` below has higher specificity than
   the UA's built-in `[hidden] { display: none }` rule, so we have to
   re-assert the hidden semantics explicitly. */
main.lp-tab-active .perform-grid {
  grid-template-columns: 1fr;
}
.stage-col[hidden] { display: none !important; }
.stage-col {
  background: var(--panel);
  border-radius: 16px;
  padding: 18px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.stage-col.stage-center {
  background: transparent;
  padding: 0;
}
.stage-col-label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.7px;
  color: var(--text-dim);
  font-weight: 600;
}

/* #slot-right is a <details> so the whole stem rack can collapse to
   the label row. Disclosure marker is a rotating caret before the
   label text (native ▶/▼ marker is hidden for a cleaner look). */
#slot-right > summary {
  cursor: pointer;
  list-style: none;
  user-select: none;
  display: flex;
  align-items: center;
  gap: 8px;
}
#slot-right > summary::-webkit-details-marker { display: none; }
#slot-right > summary::before {
  content: '▾';
  display: inline-block;
  color: var(--text-dim);
  transition: transform 120ms ease;
  font-size: 10px;
}
#slot-right:not([open]) > summary::before { transform: rotate(-90deg); }
#slot-right:not([open]) { gap: 0; }
/* Belt-and-braces: `.stage-col { display: flex }` on <details> means
   the browser's native "hide non-summary children when not [open]"
   still works, but some layout combinations were rendering the rack
   even while collapsed. Explicitly hide the rack when closed. */
#slot-right:not([open]) > .stem-rack { display: none; }

/* left col: instrument selector */
.perform-instrument-selector {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.perform-instrument-selector select {
  background: var(--panel-2);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 10px 12px;
  font-size: 14px;
  font-family: inherit;
}

/* left col: tuner-compact */
.tuner-compact {
  background: var(--panel-2);
  border-radius: 12px;
  padding: 14px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.tuner-compact[hidden] { display: none; }
.tuner-row {
  display: flex;
  align-items: baseline;
  gap: 10px;
  justify-content: center;
}
.tuner-row #into-note {
  font-size: 32px;
  font-weight: 700;
  color: var(--text);
  line-height: 1;
}
.tuner-row #into-cents {
  font-size: 14px;
  font-variant-numeric: tabular-nums;
  color: var(--text-dim);
}
.tuner-bar {
  position: relative;
  height: 14px;
  background: var(--border);
  border-radius: 7px;
}
.tuner-bar .tuner-bar-center {
  position: absolute;
  left: 50%;
  top: 0;
  bottom: 0;
  width: 2px;
  background: var(--text-dim);
  opacity: 0.5;
  transform: translateX(-50%);
}
.tuner-bar #into-needle {
  position: absolute;
  left: 50%;
  top: 1px;
  bottom: 1px;
  width: 4px;
  border-radius: 2px;
  background: var(--accent);
  transform: translateX(-50%);
  transition: left 80ms ease-out, background 200ms;
}
.tuner-bar #into-needle.in-tune { background: var(--good); }
.tuner-bar #into-needle.flat,
.tuner-bar #into-needle.sharp { background: var(--bad); }
.tuner-compact #into-status {
  font-size: 11px;
  color: var(--text-dim);
  text-align: center;
}
.tuner-stats-toggle {
  background: transparent;
  border: 1px solid var(--border);
  color: var(--text-dim);
  border-radius: 8px;
  padding: 6px 10px;
  font-size: 12px;
  cursor: pointer;
}
.tuner-stats-toggle:hover { border-color: var(--accent); color: var(--text); }
.tuner-stats-body {
  border-top: 1px solid var(--border);
  padding-top: 10px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.tuner-stats-body[hidden] { display: none; }
/* Re-skin the embedded rolling / full panels for the compact layout */
.tuner-stats-body #into-rolling {
  padding-left: 0;
  border-left: none;
  text-align: left;
}
.tuner-stats-body #into-full {
  padding-left: 0;
  border-left: none;
  min-width: 0;
}

/* left col: tone-match */
.tone-match-card {
  background: var(--panel-2);
  border: 1px solid var(--accent);
  border-radius: 12px;
  padding: 14px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.tone-match-card .user-card-label {
  color: var(--text-dim);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.6px;
}
.tone-match-card #user-tone-name {
  font-size: 18px;
  font-weight: 700;
}
.tone-match-card #user-tone-meta {
  color: var(--text-dim);
  font-size: 12px;
}
.tone-match-card .tone-card { margin: 8px 0 0; }
.tone-match-card #tone-send-connect {
  background: var(--accent);
  color: #1a0f06;
  border: none;
  border-radius: 10px;
  padding: 9px 14px;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  margin-top: 6px;
}
.tone-match-card #tone-send-connect:hover { background: var(--accent-2); }
.tone-match-card #user-mute-original {
  background: transparent;
  border: 1px solid var(--border);
  color: var(--text);
  border-radius: 10px;
  padding: 8px 12px;
  font-size: 12px;
  cursor: pointer;
  margin-top: 4px;
}
.tone-match-card #user-mute-original:hover { border-color: var(--accent); }

/* left col: audio-in row */
.audio-in-row {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  color: var(--text-dim);
  padding-top: 8px;
  border-top: 1px solid var(--border);
}
.audio-in-row .audio-in-label { color: var(--text-dim); }
/* `flex: 1 1 auto` lets the device name take leftover space but
 * shrink first when the indicators + Change button + latency need
 * room. `max-width: 20ch` fits "Line 6 HX Stomp" (15 chars) and
 * similar interface names in full while still capping pathological
 * labels so they can't monopolise the row.
 */
.audio-in-row #audio-in-name {
  color: var(--text);
  font-weight: 500;
  flex: 1 1 auto;
  min-width: 0;
  max-width: 20ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Spacer pushes the indicators + Change button to the right of the
 * (truncated) device name without forcing the name to grow. */
.audio-in-row .audio-in-spacer { flex: 1 1 auto; min-width: 4px; }

/* Phase 3 — lightweight in-row monitor-status indicators.
 *
 * These read out of state.monitor; the full monitor panel below the
 * row owns the controls. Sized small so the row stays a single line
 * and the chord strip remains the dominant element on screen.
 */
.audio-in-status-dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex: 0 0 auto;
  transition: background 0.18s ease, box-shadow 0.18s ease;
}
.audio-in-status-dot.audio-in-status-off { background: var(--border); }
.audio-in-status-dot.audio-in-status-on {
  background: #4caf50;
  box-shadow: 0 0 6px rgba(76, 175, 80, 0.55);
}
.audio-in-status-dot.audio-in-status-err {
  background: #e54b4b;
  box-shadow: 0 0 6px rgba(229, 75, 75, 0.55);
}
.audio-in-mini-meter {
  display: inline-block;
  position: relative;
  width: 42px;
  height: 6px;
  border-radius: 3px;
  background: rgba(255, 255, 255, 0.08);
  flex: 0 0 auto;
  overflow: hidden;
}
.audio-in-mini-meter-fill {
  display: block;
  height: 100%;
  width: 0%;
  background: linear-gradient(90deg, #4caf50 0%, #cdd54a 60%, #e54b4b 100%);
  transition: width 60ms linear;
}
/* CLIP indicator is a thin red bracket at the right edge of the
 * meter, not a wash across the whole bar. The underlying level
 * fill stays visible so the player can still see how much they're
 * clipping by. */
.audio-in-mini-meter-clip {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: 4px;
  background: var(--bad);
  box-shadow: 0 0 4px var(--bad);
}
.audio-in-latency {
  font-size: 11px;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
  flex: 0 0 auto;
}

/* --------------------------------------------- live guitar monitor */
/* Compact card under the audio-in row. Owns the play-along input
   path: device picker, enable toggle, gain, level meter, clipping
   indicator. Deliberately lightweight so the chord strip stays the
   primary surface.
*/
.monitor-panel {
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 10px 12px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 10px;
  font-size: 13px;
}
.monitor-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
  /* Allow long status copy ("Off — Connect is using your input",
     "Connect is using your input — quit it to monitor here") to
     wrap to its own line instead of being ellipsed at 60% width. */
  flex-wrap: wrap;
}
.monitor-title {
  font-weight: 600;
  font-size: 13px;
  color: var(--text);
  text-transform: uppercase;
  letter-spacing: 0.5px;
  white-space: nowrap;
  flex: 0 0 auto;
}
.monitor-status {
  font-size: 11px;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
  min-width: 0;
  text-align: right;
  /* No truncation — see .monitor-header flex-wrap. The full status
     copy is always readable; if it doesn't fit next to the title
     it wraps to the next line within the header row. */
}
.monitor-status-ok { color: var(--good); }
.monitor-status-err { color: var(--bad); }

/* Audio-Ownership Pivot, Phase 5 — ownership-boundary disclosure.
 * Wraps the legacy #monitor-panel in <details id="audio-input-card">
 * so the browser-monitor controls collapse-by-default when Connect
 * is installed. The summary carries a status badge that mirrors
 * the four-state vocabulary. */
.audio-input-card {
  /* Reset native <details> chrome so the disclosure blends with the
     surrounding card style — we want the same visual weight as
     the previous bare div, plus a chevron. */
  margin: 0;
}
.audio-input-card-summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  cursor: pointer;
  padding: 8px 10px;
  border-radius: 8px;
  list-style: none;
}
/* Hide the default disclosure marker; we'll rely on the <details>
   open/closed state class. */
.audio-input-card-summary::-webkit-details-marker { display: none; }
.audio-input-card-summary::marker { content: ''; }
.audio-input-card-summary:hover {
  background: rgba(255, 255, 255, 0.03);
}
.audio-input-card-title {
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.4px;
  text-transform: uppercase;
  color: var(--text-dim, #aaa);
}
.audio-input-card-badge {
  font-size: 11px;
  font-weight: 600;
  padding: 2px 8px;
  border-radius: 999px;
  border: 1px solid var(--border, #2a2a2a);
  color: var(--text-dim, #aaa);
  letter-spacing: 0.3px;
}
.audio-input-card-badge.is-paired {
  border-color: var(--good, #4caf50);
  color: var(--good, #4caf50);
}
.audio-input-card-badge.is-installed {
  border-color: var(--warn, #ff9800);
  color: var(--warn, #ff9800);
}
.audio-input-card-badge.is-fallback {
  border-color: var(--accent, #ff7a00);
  color: var(--accent, #ff7a00);
}
.audio-input-card-badge.is-missing {
  border-color: var(--border, #2a2a2a);
  color: var(--text-dim, #aaa);
}

/* Audio-Ownership Pivot, Phase 2 — fallback affordance.
 * Renders beneath the disabled "Launch Connect for low-latency
 * monitoring" toggle when Connect.app is installed but not paired.
 * Styled muted-secondary so it reads as a fallback, not the
 * recommended action. _syncMonitorVsConnect() owns visibility. */
.monitor-row-fallback {
  padding: 4px 0 0 0;
}
.monitor-fallback-link {
  color: var(--text-dim, #aaa);
  font-size: 12px;
  text-decoration: underline;
  text-decoration-color: var(--text-dim, #aaa);
  cursor: pointer;
}
.monitor-fallback-link:hover {
  color: var(--accent, #ff7a00);
  text-decoration-color: var(--accent, #ff7a00);
}

/* Audio-Ownership Pivot, Phase 3 — dual-latency card.
 * Sits inside #monitor-panel; will move into #audio-input-card
 * when Phase 5 lands the rename. Styled to feel like a nested
 * subsection of the monitor panel: lighter background, no border,
 * so it reads as "data" rather than "another card". */
.audio-latency-card {
  background: rgba(255, 255, 255, 0.03);
  border-radius: 8px;
  padding: 8px 10px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  font-size: 12px;
}
.audio-latency-card-title {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--text-dim);
}
.audio-latency-row {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.audio-latency-row-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 8px;
}
.audio-latency-row-name {
  font-weight: 600;
  color: var(--text);
}
.audio-latency-recommended {
  font-size: 10px;
  color: var(--good);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.3px;
}
.audio-latency-row-value {
  display: flex;
  gap: 6px;
  align-items: baseline;
  font-variant-numeric: tabular-nums;
  color: var(--text-dim);
}
.audio-latency-row-label {
  color: var(--text-dim);
}
.audio-latency-row-num {
  color: var(--text);
}
/* Greyed-out variant when the data source is unavailable
 * (Connect not paired, or AudioContext not constructed yet). */
.audio-latency-row.is-unavailable .audio-latency-row-num,
.audio-latency-row.is-unavailable .audio-latency-row-name {
  color: var(--text-dim);
  opacity: 0.7;
}

/* Audio-Ownership Pivot follow-up: LatencyProbe row.
 * Sits under the Connect Monitor row inside the same card. Shows
 * the ground-truth measured round-trip alongside the estimated
 * floor, plus a "Measure" button that triggers an impulse probe
 * on Connect. */
.audio-latency-measured {
  display: flex;
  gap: 8px;
  align-items: baseline;
  margin-top: 4px;
  font-variant-numeric: tabular-nums;
  flex-wrap: wrap;
}
.audio-latency-measured-num {
  color: var(--text);
}
.audio-latency-measured-badge {
  font-size: 10px;
  font-weight: 600;
  padding: 1px 6px;
  border-radius: 6px;
  border: 1px solid var(--border);
  color: var(--text-dim);
  white-space: nowrap;
}
.audio-latency-measured-badge.is-high {
  color: var(--good);
  border-color: var(--good);
}
.audio-latency-measured-badge.is-low {
  color: var(--warn, #c08a2c);
  border-color: var(--warn, #c08a2c);
  text-transform: uppercase;
  letter-spacing: 0.3px;
}
.audio-latency-measured-badge.is-nosignal {
  color: var(--err, #c0392b);
  border-color: var(--err, #c0392b);
  text-transform: uppercase;
  letter-spacing: 0.3px;
}
.audio-latency-measure-btn {
  margin-left: auto;
  font-size: 11px;
  padding: 3px 10px;
  border-radius: 6px;
  background: var(--bg);
  color: var(--text);
  border: 1px solid var(--border);
  cursor: pointer;
  transition: background 0.12s, border-color 0.12s;
}
.audio-latency-measure-btn:hover:not(:disabled) {
  background: var(--accent);
  color: var(--bg);
  border-color: var(--accent);
}
.audio-latency-measure-btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.monitor-row {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}
.monitor-row > * { min-width: 0; }
.monitor-row-device .monitor-device-label {
  color: var(--text-dim);
  font-size: 11px;
}
.monitor-row-device .monitor-device-select {
  flex: 1;
  min-width: 0;
  background: var(--bg);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 4px 6px;
  font-size: 12px;
}
.monitor-row-controls #monitor-toggle {
  /* Compact primary; keeps the panel from outgrowing slot-left. */
  padding: 6px 12px;
  font-size: 13px;
}
.monitor-row-controls #monitor-toggle.active {
  background: var(--good);
  border-color: var(--good);
  color: #0b0d10;
}
.monitor-row-controls #monitor-mute { font-size: 12px; padding: 6px 10px; }
.monitor-row-controls #monitor-mute:disabled { opacity: 0.45; cursor: not-allowed; }
.monitor-gain-wrap {
  display: flex;
  align-items: center;
  gap: 6px;
  flex: 1 1 0;
  min-width: 0;
  overflow: hidden;
}
.monitor-gain-label { color: var(--text-dim); font-size: 11px; flex: 0 0 auto; }
.monitor-gain-wrap .gain {
  flex: 1 1 0;
  min-width: 0;
  width: 100%;
  -webkit-appearance: none;
  appearance: none;
  height: 4px;
  border-radius: 2px;
  background: var(--border);
}
.monitor-gain-wrap .gain::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  height: 14px;
  width: 14px;
  border-radius: 50%;
  background: var(--accent);
  cursor: pointer;
}
.monitor-gain-readout {
  font-variant-numeric: tabular-nums;
  color: var(--text-dim);
  font-size: 11px;
  min-width: 32px;
  text-align: right;
}
.monitor-row-meter {
  align-items: center;
  gap: 10px;
}
.monitor-meter {
  position: relative;
  flex: 1;
  height: 10px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 4px;
  overflow: hidden;
}
.monitor-meter-fill {
  position: absolute;
  inset: 0 auto 0 0;
  /* Green at low, yellow mid, red near top — gradient gives the
     "track to 0" mental model without needing per-frame colour
     calculations. */
  background: linear-gradient(
    90deg,
    var(--good) 0%,
    var(--good) 55%,
    var(--warn) 78%,
    var(--bad)  100%
  );
  width: 0%;
  transition: width 60ms linear;
}
.monitor-meter-clip {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: 6px;
  background: var(--bad);
  box-shadow: 0 0 6px var(--bad);
  border-top-right-radius: inherit;
  border-bottom-right-radius: inherit;
}
.monitor-meter-clip::after {
  content: "!";
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  color: #0b0d10;
  font-size: 9px;
  font-weight: 700;
  line-height: 1;
}
.monitor-latency {
  font-size: 10px;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
  min-width: 70px;
  text-align: right;
}

/* center column: section bar + chord ribbon */
#slot-center .section-bar {
  margin-bottom: 10px;
  min-height: 28px;
}
#slot-center .chord-ribbon {
  height: 80px;
  margin: 0;
}
#slot-center .chord-pill {
  height: 56px;
  font-size: 18px;
  cursor: pointer;
  transition: transform 100ms;
}
#slot-center .chord-pill:hover { transform: translateY(-1px); }
#slot-center .chord-pill:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
#slot-center .loop-status {
  margin-top: 10px;
  color: var(--text-dim);
  font-size: 13px;
}
.chord-snap-row {
  margin-top: 8px;
  font-size: 13px;
  color: var(--text-dim);
  display: inline-flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
}
.chord-snap-label {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  cursor: pointer;
  user-select: none;
}
.chord-snap-label input[type="checkbox"] {
  cursor: pointer;
  margin: 0;
}
/* Chord-lane stem selector: lives on the same row as the snap toggle
   and mirrors .chord-snap-label so the two controls read as a family.
   The <select> uses the same dim-on-dark palette as surrounding UI.
   Hidden via the [hidden] attribute when only one stem-lane exists
   (legacy bundles, single-source mixes). */
.chord-lane-stem-label {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  cursor: pointer;
  user-select: none;
}
.chord-lane-stem-label select {
  background: var(--bg-elev, #1c1c1c);
  color: var(--text, #e8e8e8);
  border: 1px solid var(--border, #333);
  border-radius: 4px;
  padding: 2px 6px;
  font-size: 13px;
  cursor: pointer;
}
.chord-lane-stem-label select:focus {
  outline: 1px solid var(--accent, #6aa6ff);
  outline-offset: 1px;
}

/* Queued chord diagrams row (Phase C of chord-diagram feature).
   4 slots showing current + next 3 chords. Slots flex-grow to fill
   the center column so the row reads as a "queue lane" rather than
   four floating thumbnails. Slot 0 (the active chord) gets a brighter
   --accent border + soft shadow so it pops from the ribbon's static
   colour. SVGs inside the slots use the same theme tokens as the
   surrounding UI (white nut/strings, --text for fret marks, --accent
   for the highlighted active slot). */
/* Chord guidance: three-card row — large NOW PLAYING focal card on
   the left + two smaller "UP NEXT" / "THEN" preview cards on the
   right. The previews show diagrams for activeIdx+1 and activeIdx+2
   so the player can see what's coming, while NOW PLAYING dominates
   visual weight to keep the eye anchored to "what do I play now". */
.chord-guidance {
  display: flex;
  gap: 16px;
  margin-top: 12px;
  padding: 0 8px;
  align-items: stretch;
}
.chord-now-block {
  flex: 1 1 50%;
  min-width: 0;
  border: 2px solid var(--accent);
  border-radius: 10px;
  background: var(--panel);
  box-shadow: 0 0 0 1px var(--accent), 0 4px 12px rgba(249, 115, 22, 0.20);
  padding: 14px 16px 16px;
  display: flex;
  flex-direction: column;
  align-items: center;
  box-sizing: border-box;
}
.chord-now-label {
  font-size: 11px;
  letter-spacing: 0.12em;
  font-weight: 700;
  color: var(--accent);
  text-transform: uppercase;
  margin-bottom: 4px;
  align-self: flex-start;
}
.chord-now-symbol {
  font-size: 56px;
  font-weight: 800;
  color: var(--text);
  letter-spacing: 0.02em;
  margin-bottom: 8px;
  line-height: 1;
}
.chord-now-diagram {
  flex: 0 0 auto;
  width: 100%;
  max-width: 520px;
  height: 380px;
  margin: 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
}
.chord-now-diagram svg {
  width: 100%;
  height: 100%;
  max-height: 380px;
  display: block;
}
/* Footer row inside NOW PLAYING — countdown on the left, transition
   hint on the right. On narrow viewports they stack via flex-wrap. */
.chord-now-footer {
  width: 100%;
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--border);
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px 16px;
}
.chord-countdown {
  font-size: 14px;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
  min-height: 1.2em;
}
.chord-transition {
  font-size: 14px;
  color: var(--accent);
  font-weight: 600;
}
/* Phase 7 (JAM Learning V1) — per-section "Report wrong" affordance.
   Compact ghost button living in the chord-now-footer. The button is
   shown only when the current section has a defined section_id; the
   click handler captures the current chord symbol and POSTs to
   /api/learning/correct as evidence. */
.chord-now-report-wrong {
  font-size: 12px;
  color: var(--text-dim);
  background: transparent;
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 4px 8px;
  cursor: pointer;
  transition: color 120ms ease, border-color 120ms ease;
}
.chord-now-report-wrong:hover,
.chord-now-report-wrong:focus-visible {
  color: var(--text);
  border-color: var(--accent);
  outline: none;
}
.chord-now-report-wrong.is-busy {
  opacity: 0.5;
  pointer-events: none;
}
/* Up-next preview cards (activeIdx+1, +2). Smaller than NOW PLAYING
   with a dim border so the eye isn't tempted away from the focal
   card. The whole card is a <button> for click-to-seek; keyboard
   focus picks up the accent ring. */
.chord-up-next {
  flex: 1 1 25%;
  min-width: 0;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--panel);
  padding: 12px;
  display: flex;
  flex-direction: column;
  align-items: center;
  box-sizing: border-box;
  font: inherit;
  color: inherit;
  cursor: pointer;
  text-align: left;
  transition: border-color 120ms ease, box-shadow 120ms ease;
}
.chord-up-next:hover,
.chord-up-next:focus-visible {
  border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent);
  outline: none;
}
.chord-up-next-label {
  font-size: 10px;
  letter-spacing: 0.12em;
  font-weight: 700;
  color: var(--text-dim);
  text-transform: uppercase;
  margin-bottom: 4px;
  align-self: flex-start;
}
.chord-up-next-symbol {
  font-size: 28px;
  font-weight: 800;
  color: var(--text);
  letter-spacing: 0.02em;
  margin-bottom: 6px;
  line-height: 1;
}
.chord-up-next-diagram {
  flex: 1 1 auto;
  width: 100%;
  min-height: 200px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.chord-up-next-diagram svg {
  width: 100%;
  height: 100%;
  max-height: 260px;
  display: block;
}
/* NOW PLAYING diagram is a click target for the voicing picker
   (§4). Hover affordance via subtle accent ring; cursor:pointer to
   communicate interactivity. */
.chord-now-diagram.is-clickable {
  cursor: pointer;
  border-radius: 8px;
  transition: box-shadow 120ms ease;
}
.chord-now-diagram.is-clickable:hover {
  box-shadow: 0 0 0 2px var(--accent);
}
/* Voicing picker popover — anchored under chord-now-block, opens on
   click of the NOW PLAYING diagram. */
.chord-now-block {
  position: relative;
}
.chord-voicing-popover {
  position: absolute;
  top: 100%;
  left: 0;
  margin-top: 8px;
  z-index: 50;
  background: var(--panel);
  border: 1px solid var(--accent);
  border-radius: 8px;
  padding: 8px;
  min-width: 180px;
  box-shadow: 0 4px 18px rgba(0, 0, 0, 0.4);
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.chord-voicing-header {
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-dim);
  padding: 2px 6px 6px;
  border-bottom: 1px solid var(--border);
}
.chord-voicing-option {
  background: transparent;
  border: 1px solid transparent;
  border-radius: 6px;
  padding: 6px 10px;
  margin: 0;
  font: inherit;
  font-size: 13px;
  color: var(--text);
  text-align: left;
  cursor: pointer;
  transition: background 120ms ease, border-color 120ms ease;
}
.chord-voicing-option:hover,
.chord-voicing-option:focus-visible {
  background: rgba(249, 115, 22, 0.08);
  border-color: var(--accent);
  outline: none;
}
.chord-voicing-option.is-selected {
  background: rgba(249, 115, 22, 0.16);
  border-color: var(--accent);
  color: var(--accent);
  font-weight: 600;
}
/* Overlay labels under NOW PLAYING (§8). Nashville + Roman sit
   inline above the diagram; the scale suggestion sits below. */
.chord-now-overlays {
  display: flex;
  gap: 10px;
  align-items: baseline;
  min-height: 20px;
  margin-bottom: 4px;
}
.chord-overlay-nashville {
  font-size: 15px;
  font-weight: 700;
  color: var(--accent);
  letter-spacing: 0.04em;
}
.chord-overlay-roman {
  font-size: 15px;
  font-weight: 700;
  color: var(--text-dim);
  font-style: italic;
}
.chord-overlay-scales {
  margin-top: 6px;
  font-size: 12px;
  color: var(--text-dim);
  text-align: center;
}
/* Advanced overlays toggle row — collapsed by default via <details>. */
.chord-overlays-row {
  margin-top: 8px;
  font-size: 12px;
  color: var(--text-dim);
}
.chord-overlays-details {
  background: transparent;
}
.chord-overlays-summary {
  cursor: pointer;
  user-select: none;
  padding: 4px 8px;
  border-radius: 6px;
  display: inline-block;
}
.chord-overlays-summary:hover {
  color: var(--accent);
}
.chord-overlay-option {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-left: 12px;
  cursor: pointer;
}
.chord-diagram-empty {
  font-size: 12px;
  color: var(--text-dim);
  text-align: center;
  padding: 6px;
}

/* On narrow viewports stack the three guidance cards vertically so
   the focal card isn't squeezed and the up-next previews stay
   legible. */
@media (max-width: 900px) {
  .chord-guidance {
    flex-direction: column;
  }
  .chord-now-block,
  .chord-up-next {
    flex: 1 1 auto;
  }
}
@media (max-width: 600px) {
  .chord-now-block {
    padding: 10px 12px 12px;
  }
  .chord-now-symbol {
    font-size: 44px;
  }
  .chord-now-diagram {
    height: 260px;
  }
  .chord-now-diagram svg {
    max-height: 260px;
  }
}

/* SVG element styling — the renderer in chord_diagrams.js applies
   these class names to <line>/<circle>/<text>/<rect>. Default SVG
   paint is black, which is invisible on the dark panel; pin every
   stroke/fill to a theme token here so the diagrams read like the
   rest of the UI. */
.chord-diagram-title {
  fill: var(--text);
  font-size: 12px;
  font-weight: 600;
  font-family: inherit;
}
.chord-diagram-slot.is-active .chord-diagram-title,
.chord-diagram-svg.is-active .chord-diagram-title {
  fill: var(--accent);
}
.chord-diagram-string {
  stroke: var(--text-dim);
  stroke-width: 1.2;
}
.chord-diagram-fret {
  stroke: var(--text-dim);
  stroke-width: 1;
  opacity: 0.7;
}
.chord-diagram-nut {
  stroke: var(--text);
  stroke-width: 2.5;
}
.chord-diagram-dot {
  fill: var(--accent);
  stroke: none;
}
.chord-diagram-open {
  fill: none;
  stroke: var(--text);
  stroke-width: 1.2;
}
.chord-diagram-mute {
  fill: var(--text-dim);
  font-size: 9px;
  font-weight: 700;
  font-family: inherit;
}
.chord-diagram-finger {
  fill: #0b0d10;
  font-size: 7px;
  font-weight: 700;
  font-family: inherit;
}
.chord-diagram-barre {
  fill: var(--accent);
  opacity: 0.85;
}
.chord-diagram-fret-label {
  fill: var(--text-dim);
  font-size: 8px;
  font-family: inherit;
}
.chord-diagram-placeholder {
  fill: var(--text-dim);
  font-size: 14px;
  font-family: inherit;
}

/* Lead-tab lane: single full-width fretboard SVG showing the
   monophonic lead-line notes for the active chord window. */
.chord-tab-queue {
  margin-top: 12px;
  padding: 10px 12px 12px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--panel);
  min-height: 110px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.chord-tab-label {
  font-size: 11px;
  letter-spacing: 0.12em;
  font-weight: 700;
  color: var(--text-dim);
  text-transform: uppercase;
}
.chord-tab-body {
  flex: 1 1 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 90px;
}
.chord-tab-body svg {
  width: 100%;
  height: auto;
  display: block;
  max-height: 120px;
}
.chord-tab-body .chord-tab-empty {
  font-size: 13px;
  color: var(--text-dim);
  text-align: center;
}

/* Tab-SVG elements — same theming approach as the chord diagram
   classes above. Default SVG fill (black) is invisible on the
   dark panel; pin to theme tokens. */
.chord-tab-string {
  stroke: var(--text-dim);
  stroke-width: 1;
  opacity: 0.7;
}
.chord-tab-clef {
  fill: var(--text-dim);
  font-size: 9px;
  font-weight: 700;
  font-family: inherit;
}
.chord-tab-fret {
  fill: var(--accent);
  font-size: 11px;
  font-weight: 700;
  font-family: inherit;
}
.chord-tab-placeholder {
  fill: var(--text-dim);
  font-size: 12px;
  font-family: inherit;
}

/* ─── Picking-tab-lane header + controls ──────────────────────────
 * The LEAD PART panel grew a header row that pairs the label with
 * Difficulty / Note-display selectors. The lane itself is rendered
 * by picking-tab-lane.js::createTabLane into #chord-tab-body and
 * scrolls notes past a fixed playhead. CSS only handles theming;
 * geometry lives in the JS module. */
.chord-tab-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}
.chord-tab-controls {
  display: inline-flex;
  gap: 8px;
}
.tab-control-group {
  display: inline-flex;
  border: 1px solid var(--border);
  border-radius: 6px;
  overflow: hidden;
  background: var(--panel);
}
.tab-control-btn {
  appearance: none;
  border: 0;
  background: transparent;
  color: var(--text-dim);
  padding: 3px 8px;
  font-size: 11px;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
  min-width: 28px;
  border-right: 1px solid var(--border);
}
.tab-control-group .tab-control-btn:last-child {
  border-right: 0;
}
.tab-control-btn:hover {
  color: var(--text);
  background: rgba(255, 255, 255, 0.04);
}
.tab-control-btn.is-active {
  color: var(--bg);
  background: var(--accent);
}

/* The scrolling tab SVG. Sits inside #chord-tab-body, alongside or
   in place of the legacy renderLeadTabSVG output. */
.picking-tab-lane {
  width: 100%;
  height: auto;
  /* Explicit aspect-ratio: older Safari resolves height:auto on an SVG
     with only viewBox (no width/height attrs) to 0, collapsing the lane
     vertically even though child glyphs are correctly positioned.
     viewBox is 640×110 → aspect 64:11. */
  aspect-ratio: 64 / 11;
  min-height: 90px;
  display: block;
  max-height: 140px;
  /* SVG root clips to viewBox by default. We rely on that for hiding
     off-screen scrolling notes and disable the per-group clip-path
     below — see .tab-notes rule for rationale. */
  overflow: hidden;
}
/* Disable the SVG-attribute clip-path that picking-tab-lane.js sets on
   the notes group. The clipPath rect is authored in viewBox space
   (x=padLeft, width=usable area), but Safari applies the group's own
   translate(-tx 0) transform to the clip rect coordinates as well —
   so at runtime the clip rect moves thousands of viewBox units
   off-screen with the content, and every glyph gets clipped out.
   The SVG root's intrinsic overflow:hidden handles bleed past the
   string-label gutter; the gutter sits at viewBox x ∈ [0,36], so
   labels can be briefly overdrawn by a scrolling glyph near the
   edge — a minor cosmetic cost for the notes being visible at all. */
.picking-tab-lane .tab-notes {
  clip-path: none !important;
}
.picking-tab-lane .tab-string {
  stroke: var(--text-dim);
  stroke-width: 1;
  opacity: 0.55;
}
.picking-tab-lane .tab-string-label {
  fill: var(--text-dim);
  font-size: 9px;
  font-weight: 700;
  font-family: inherit;
  letter-spacing: 0.05em;
}
.picking-tab-lane .tab-playhead {
  stroke: var(--accent);
  stroke-width: 1.5;
  opacity: 0.85;
}
.picking-tab-lane .tab-note-dot {
  fill: var(--accent);
  opacity: 0.95;
}
.picking-tab-lane .tab-note-fret {
  fill: var(--accent);
  font-size: 11px;
  font-weight: 700;
  font-family: inherit;
}
.picking-tab-lane .tab-note-name {
  fill: var(--accent);
  font-size: 10px;
  font-weight: 700;
  font-family: inherit;
}

/* View-mode toggle row: Diagrams / Tab / Both. Mirrors the
   chord-snap-row typography so the two toggles look like a family. */
.chord-view-mode-row {
  margin-top: 10px;
  font-size: 13px;
  color: var(--text-dim);
  display: inline-flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
}
.chord-view-mode-label {
  color: var(--text-dim);
}
.chord-view-mode-option {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  cursor: pointer;
  user-select: none;
}
.chord-view-mode-option input[type="radio"] {
  cursor: pointer;
  margin: 0;
}

/* Click-track toggle: when ON, accent border + faint accent fill so the
   user can tell the metronome scheduler is actually running. */
#t-click.click-on {
  border-color: var(--accent);
  color: var(--accent);
}

/* right column: stem mixer (tighter rows) */
.stage-col.stage-right .stem-rack {
  background: transparent;
  border: none;
  border-radius: 0;
  padding: 0;
  gap: 4px;
}
.stage-col.stage-right .stem-row {
  grid-template-columns: 1fr 1fr 28px 28px;
  gap: 10px;
  padding: 8px 2px;
}
.stage-col.stage-right .stem-row .name { font-size: 13px; }
.stage-col.stage-right .stem-row .mute-btn,
.stage-col.stage-right .stem-row .solo-btn {
  padding: 4px 0;
  font-size: 10px;
  height: 28px;
  border-radius: 6px;
}

/* playback bar (fixed) */
.playback-bar {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 25;
  background: var(--panel);
  border-top: 1px solid var(--border);
  padding: 12px 20px;
  display: grid;
  grid-template-columns: auto 1fr auto;
  gap: 18px;
  align-items: center;
}
.playback-bar .pb-transport {
  display: flex;
  gap: 8px;
  align-items: center;
}
.playback-bar .pb-transport button {
  padding: 8px 14px;
  font-size: 13px;
}
.playback-bar #waveform-canvas {
  display: block;
  width: 100%;
  height: 64px;
  background: var(--panel-2);
  border-radius: 8px;
  cursor: pointer;
}
.playback-bar #t-time {
  margin-left: 0;
  font-variant-numeric: tabular-nums;
  color: var(--text-dim);
  font-size: 13px;
}


/* ─── NOW PLAYING view toggle ──────────────────────────────────────
   Chord-diagram vs scrolling-tab-lane switcher inside the chord-now-
   block. The Tab view shows landmark notes from picking-tab-lane.js;
   the glyph subgroup lets the user A/B different rendering styles
   (dot, fret number, note name) for effectiveness evaluation. */
.chord-now-view-toggle {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 6px 0 8px;
  font-size: 12px;
}
.cv-toggle-btn,
.cv-style-btn {
  background: transparent;
  border: 1px solid var(--border, #2c2c2c);
  color: var(--text-dim, #888);
  padding: 3px 10px;
  border-radius: 4px;
  cursor: pointer;
  font: inherit;
  line-height: 1.4;
}
.cv-toggle-btn.is-active,
.cv-style-btn.is-active {
  background: var(--accent, #e8743b);
  color: #0b0b0b;
  border-color: var(--accent, #e8743b);
}
.cv-toggle-btn:hover:not(.is-active),
.cv-style-btn:hover:not(.is-active) {
  color: var(--text, #ddd);
  border-color: var(--text-dim, #888);
}
.cv-style-group {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding-left: 8px;
  border-left: 1px solid var(--border, #2c2c2c);
}

/* The tab lane SVG fills the same horizontal envelope as the chord
   diagram. Only one of {diagram, lane} is visible at a time; the
   hidden attribute on either container drives display. */
.chord-now-tab-lane {
  width: 100%;
  max-width: 360px;
  margin: 0 auto;
}
.chord-now-tab-lane svg.picking-tab-lane {
  width: 100%;
  height: auto;
  display: block;
}
.chord-now-tab-lane .tab-string {
  stroke: var(--border, #2c2c2c);
  stroke-width: 1;
}
.chord-now-tab-lane .tab-playhead {
  stroke: var(--accent, #e8743b);
  stroke-width: 1.5;
}
.chord-now-tab-lane .tab-string-label {
  fill: var(--text-dim, #888);
  font-size: 10px;
  font-family: ui-monospace, SFMono-Regular, monospace;
}
.chord-now-tab-lane .tab-note-dot {
  fill: var(--accent, #e8743b);
}
.chord-now-tab-lane .tab-note-fret,
.chord-now-tab-lane .tab-note-name {
  fill: var(--accent, #e8743b);
  font-size: 10px;
  font-family: ui-monospace, SFMono-Regular, monospace;
}

/* ------------------------------------------------------- rehearsal view
 * Section-by-section onboarding practice. The layout is a two-column
 * grid (section list + main pane) with a fixed transport strip below.
 * Colours reuse the existing palette (--accent for progress, --good
 * for pass state, --bad for fail state) so the surface feels native
 * next to the perform view. */
#view-rehearsal {
  max-width: 1100px;
  margin: 0 auto;
}
.rehearsal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin: 24px 0 20px;
}
.rehearsal-title {
  font-size: 28px;
  font-weight: 700;
  color: var(--text);
}
.rehearsal-meta {
  color: var(--text-dim);
  font-size: 14px;
  margin-top: 4px;
}
.rehearsal-header-actions {
  display: flex;
  gap: 10px;
}
.rehearsal-grid {
  display: grid;
  grid-template-columns: 260px 1fr;
  gap: 18px;
  margin-bottom: 24px;
}
.rehearsal-sections {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 14px;
}
.rehearsal-sections-label {
  color: var(--text-dim);
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 1px;
  margin-bottom: 10px;
}
.rehearsal-section-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.rehearsal-section-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 12px;
  background: var(--panel-2);
  border: 1px solid transparent;
  border-radius: 8px;
  color: var(--text);
  font-size: 14px;
  cursor: pointer;
  text-align: left;
  width: 100%;
}
.rehearsal-section-item:hover {
  border-color: var(--border);
}
.rehearsal-section-item.is-active {
  border-color: var(--accent);
  background: rgba(249, 115, 22, 0.08);
}
.rehearsal-section-item .rehearsal-check {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  border: 1.5px solid var(--border);
  color: var(--good);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  font-weight: 700;
  flex-shrink: 0;
}
.rehearsal-section-item.is-complete .rehearsal-check {
  border-color: var(--good);
  background: rgba(74, 222, 128, 0.16);
}
.rehearsal-section-item .rehearsal-item-label {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.rehearsal-section-item .rehearsal-item-role {
  color: var(--text-dim);
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}
.rehearsal-section-variant {
  padding-left: 24px;
  font-size: 13px;
  color: var(--text-dim);
}

.rehearsal-main {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 20px;
  min-height: 460px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.rehearsal-section-heading {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 10px;
}
.rehearsal-section-heading-name {
  font-size: 22px;
  font-weight: 700;
}
/* Pattern-ROI subtitle: "Appears 4× · unlocks 41% of the song".
   Sits on its own line under the heading name to frame the section
   as a mastery investment. Colour intentionally dim so it reads
   as context, not competing with the name. */
.rehearsal-section-heading-roi {
  flex-basis: 100%;
  font-size: 13px;
  color: var(--text-dim);
  letter-spacing: 0.01em;
  margin-top: 2px;
}
.rehearsal-section-heading-roi[hidden] {
  display: none;
}
.rehearsal-section-heading-badge {
  font-size: 11px;
  padding: 3px 8px;
  border-radius: 10px;
  background: rgba(249, 115, 22, 0.12);
  color: var(--accent-2);
  text-transform: uppercase;
  letter-spacing: 0.5px;
}
.rehearsal-mic-chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 6px 10px;
  border-radius: 12px;
  background: var(--panel-2);
  border: 1px solid var(--border);
  font-size: 12px;
  align-self: flex-start;
}
.rehearsal-mic-chip .rehearsal-mic-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--text-dim);
}
.rehearsal-mic-chip.rehearsal-mic-on .rehearsal-mic-dot {
  background: var(--good);
  box-shadow: 0 0 6px rgba(74, 222, 128, 0.6);
}
.rehearsal-mic-chip.rehearsal-mic-off .rehearsal-mic-label { color: var(--text-dim); }
.rehearsal-mic-pitch {
  color: var(--text-dim);
  font-family: ui-monospace, SFMono-Regular, monospace;
  font-size: 11px;
}
.rehearsal-items {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-content: flex-start;
  flex: 1;
  min-height: 200px;
  padding: 8px;
  background: var(--panel-2);
  border-radius: 8px;
}
.rehearsal-tile {
  min-width: 96px;
  padding: 12px 14px;
  border-radius: 8px;
  background: var(--panel);
  border: 1.5px solid var(--border);
  text-align: center;
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-family: ui-monospace, SFMono-Regular, monospace;
  transition: transform 0.12s, border-color 0.12s, background 0.12s;
}
.rehearsal-tile-label {
  font-size: 18px;
  font-weight: 700;
  color: var(--text);
}
.rehearsal-tile-sub {
  font-size: 11px;
  color: var(--text-dim);
}
/* Card meta strip under the diagram: musician-language duration
   ("2 bars") + inline fretting-hand tag chip. The chip reuses the
   same severity/hand mixins as the heading strip so a barre tile
   glows warm without needing bespoke styling. */
.rehearsal-tile-meta {
  display: flex;
  justify-content: center;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 2px;
  font-size: 11px;
}
.rehearsal-tile-bars {
  color: var(--text-dim);
  letter-spacing: 0.02em;
}
.rehearsal-tile-tag {
  padding: 1px 6px;
  border-radius: 8px;
  background: rgba(148, 163, 184, 0.14);
  color: var(--text-dim);
  font-weight: 600;
  font-size: 10px;
  letter-spacing: 0.02em;
}
.rehearsal-tile-tag.tag-severity-3 {
  background: rgba(249, 115, 22, 0.16);
  color: var(--accent-2);
}
/* Next-chord preview under the meta strip. Muted colour keeps it
   subordinate to the current chord label; the arrow suggests
   forward motion. */
.rehearsal-tile-next {
  margin-top: 6px;
  padding-top: 6px;
  border-top: 1px dashed rgba(148, 163, 184, 0.28);
  font-size: 11px;
  color: var(--text-dim);
  letter-spacing: 0.02em;
}
.rehearsal-tile.is-active {
  border-color: var(--accent);
  background: rgba(249, 115, 22, 0.10);
  transform: translateY(-1px);
}
.rehearsal-tile.is-pass {
  border-color: var(--good);
  background: rgba(74, 222, 128, 0.12);
}
.rehearsal-tile.is-fail {
  border-color: var(--bad);
  background: rgba(248, 113, 113, 0.10);
}
.rehearsal-empty {
  color: var(--text-dim);
  font-style: italic;
  padding: 40px 0;
  text-align: center;
}
.rehearsal-score {
  display: flex;
  align-items: center;
  gap: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--border);
  font-size: 13px;
}
.rehearsal-score-label {
  color: var(--text-dim);
  text-transform: uppercase;
  letter-spacing: 0.5px;
  font-size: 11px;
}
.rehearsal-score-value {
  font-family: ui-monospace, SFMono-Regular, monospace;
  font-weight: 700;
  color: var(--text);
}
.rehearsal-score-hint { color: var(--text-dim); margin-left: auto; }

.rehearsal-transport {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 14px 18px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 12px;
  margin-bottom: 40px;
}
.rehearsal-transport button.is-active {
  border-color: var(--accent);
  color: var(--accent-2);
}
.rehearsal-speed {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-left: 8px;
}
.rehearsal-speed-label {
  color: var(--text-dim);
  font-size: 12px;
  margin-right: 4px;
}
.rehearsal-speed-btn {
  background: var(--panel-2);
  border: 1px solid var(--border);
  color: var(--text-dim);
  padding: 4px 10px;
  border-radius: 6px;
  cursor: pointer;
  font-size: 12px;
  font-family: inherit;
}
.rehearsal-speed-btn.is-active {
  background: var(--panel);
  color: var(--text);
  border-color: var(--accent);
}
.rehearsal-speed-btn:hover:not(.is-active) { color: var(--text); }
#rehearsal-next { margin-left: auto; }

/* ─── v2 rehearsal redesign ──────────────────────────────────────
   Musician-language IA: three group buckets in the left list
   (Parts to Learn / Moments / Extras), warm status line replacing
   the "Passes 0/0" scoreboard, quieter Skip-to-jam text link, and
   soft-label italics for low-confidence sections. Row layout has
   a reserved beads slot so the Phase-2 mastery rollout drops in
   without a second grid-shift.
   ───────────────────────────────────────────────────────────── */
.rehearsal-header-meta,
#rehearsal-meta {
  color: var(--text-dim);
  font-size: 13px;
  margin-top: 4px;
  letter-spacing: 0.1px;
}
.text-link {
  background: transparent;
  border: none;
  color: var(--text-dim);
  padding: 6px 8px;
  font-size: 13px;
  font-family: inherit;
  cursor: pointer;
  border-radius: 6px;
  transition: color 0.15s, background 0.15s;
}
.text-link:hover {
  color: var(--text);
  background: rgba(255, 255, 255, 0.04);
  text-decoration: underline;
}

/* Left list — group headings (Parts to learn / Moments / Extras). */
.rehearsal-group-heading {
  color: var(--text-dim);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 1.1px;
  font-weight: 600;
  margin: 14px 0 8px;
  padding: 0 4px;
}
.rehearsal-group-heading:first-child { margin-top: 0; }
.rehearsal-group-heading--extras {
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 6px;
  user-select: none;
}
.rehearsal-group-heading--extras::before {
  content: '▸';
  font-size: 10px;
  transition: transform 0.15s;
  display: inline-block;
}
.rehearsal-group-heading--extras.is-open::before {
  transform: rotate(90deg);
}
.rehearsal-extras-tray {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: 4px;
}
.rehearsal-extras-tray.is-collapsed {
  display: none;
}

/* Section row. Grid columns: name, mastery beads, state chip, check.
   Keeps baseline consistent across variants and hover states. */
.rehearsal-section-row {
  display: grid;
  grid-template-columns: 1fr auto auto auto;
  align-items: center;
  gap: 8px;
  padding: 9px 12px;
  background: var(--panel-2);
  border: 1px solid transparent;
  border-radius: 8px;
  color: var(--text);
  font-size: 14px;
  cursor: pointer;
  text-align: left;
  width: 100%;
  font-family: inherit;
  transition: border-color 0.12s, background 0.12s;
}
.rehearsal-section-row + .rehearsal-section-row { margin-top: 6px; }
.rehearsal-section-row:hover:not(.is-active) {
  border-color: var(--border);
}
.rehearsal-section-row.is-active {
  border-color: var(--accent);
  background: rgba(249, 115, 22, 0.10);
}
.rehearsal-section-row.is-variant {
  padding-left: 28px;
  font-size: 12.5px;
  color: var(--text-dim);
  background: transparent;
}
.rehearsal-section-row.is-variant.is-active {
  color: var(--text);
  background: rgba(249, 115, 22, 0.08);
}
/* Cluster wraps one anchor + its variation drawer. Kept transparent
   so it's invisible when a group has no variants; the drawer
   toggle only appears when there's something to reveal. */
.rehearsal-cluster {
  display: flex;
  flex-direction: column;
}
.rehearsal-variation-toggle {
  align-self: flex-start;
  margin: 2px 0 2px 28px;
  padding: 2px 8px;
  border: none;
  background: transparent;
  color: var(--text-dim);
  font-size: 11px;
  letter-spacing: 0.02em;
  cursor: pointer;
  text-align: left;
}
.rehearsal-variation-toggle::before {
  content: '▸';
  display: inline-block;
  margin-right: 4px;
  transition: transform 0.12s;
}
.rehearsal-variation-toggle.is-open::before {
  transform: rotate(90deg);
}
.rehearsal-variation-toggle:hover {
  color: var(--text);
}
.rehearsal-variation-drawer {
  display: flex;
  flex-direction: column;
  gap: 2px;
  overflow: hidden;
  max-height: 400px;
  transition: max-height 0.16s ease-out;
}
.rehearsal-variation-drawer.is-collapsed {
  max-height: 0;
}
.rehearsal-section-row.is-soft .rehearsal-section-row-name {
  font-style: italic;
  color: var(--text-dim);
}
.rehearsal-section-row-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.rehearsal-section-row-beads {
  display: inline-flex;
  gap: 3px;
  align-items: center;
  min-width: 0;
}
.rehearsal-section-row .rehearsal-check {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  border: 1.5px solid var(--border);
  color: var(--good);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  font-weight: 700;
  flex-shrink: 0;
}
.rehearsal-section-row.is-complete .rehearsal-check {
  border-color: var(--good);
  background: rgba(74, 222, 128, 0.16);
}

/* Section heading — "Maybe …" italics for low-confidence labels. */
.rehearsal-section-heading-name.is-soft {
  font-style: italic;
  color: var(--text-dim);
}

/* Phase 3: recurrence subtitle under an anchor row ("repeats 4×").
   Rendered inline inside .rehearsal-section-row-name so it wraps
   below the label without stealing width from the beads column. */
.rehearsal-section-row-subtitle {
  display: block;
  font-size: 11px;
  color: var(--text-dim);
  font-weight: 400;
  margin-top: 1px;
  letter-spacing: 0.01em;
}

/* Phase 3: locked-row treatment. The row is still visible so
   musicians see what's coming, but it's desaturated + non-
   interactive-looking. The 🔒 sits in the check slot. Long-press
   hint provided by title attr, no explicit affordance in the row
   itself (matches the design memo — "let the musician discover it,
   don't clutter the list"). */
.rehearsal-section-row.is-locked {
  opacity: 0.55;
  cursor: not-allowed;
}
.rehearsal-section-row.is-locked:hover {
  background: transparent;
}
.rehearsal-section-row .rehearsal-check.is-lock {
  border-color: transparent;
  background: transparent;
  color: var(--text-dim);
  font-size: 12px;
}

/* Phase 3: difficulty hints under the section heading. Chips flow
   inline so multiple hints ("has a barre chord", "quick change")
   read as a comma-less short list. Colour is intentionally subdued
   — this is context, not a warning. */
.rehearsal-section-heading-hints {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 4px;
}
.rehearsal-section-heading-hints[hidden] {
  display: none;
}
.difficulty-hint {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 10px;
  background: rgba(148, 163, 184, 0.12);
  color: var(--text-dim);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.01em;
}
/* Severity styling: severity 3 (roadblock) gets the warm accent so
   barre chords stand out in the heading strip; severity 2 keeps the
   neutral default. */
.difficulty-hint.tag-severity-3 {
  background: rgba(249, 115, 22, 0.14);
  color: var(--accent-2);
}
/* Hand hint: a small leading marker tells the musician at a glance
   which hand or timing dimension the tag targets. Purely
   decorative — the chip label carries the copy. */
.difficulty-hint::before {
  font-size: 9px;
  letter-spacing: 0.06em;
  margin-right: 4px;
  opacity: 0.6;
  text-transform: uppercase;
}
.difficulty-hint.tag-hand-fretting::before { content: 'FRET'; }
.difficulty-hint.tag-hand-picking::before { content: 'PICK'; }
.difficulty-hint.tag-hand-timing::before { content: 'TIME'; }
.difficulty-hint.tag-hand-both::before { content: 'BOTH'; }

/* Dev-only tag-id badges on rehearsal rows (?debug=1). Renders a
   compact monospace pill per detected tag id so the machinery is
   visible without opening /debug. The wrapper only participates
   in layout when the body carries `.debug-tags-visible`; on normal
   loads it collapses to zero-width so section rows stay identical
   to the shipped design. */
.rehearsal-row-debug-tags {
  display: none;
  gap: 4px;
  margin: 0 8px 0 4px;
  align-items: center;
  flex-wrap: wrap;
}
body.debug-tags-visible .rehearsal-row-debug-tags {
  display: inline-flex;
}
.rehearsal-row-debug-tag {
  font-family: ui-monospace, 'SF Mono', Menlo, monospace;
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 1px 5px;
  border-radius: 3px;
  background: rgba(148, 163, 184, 0.18);
  color: var(--text-dim);
  line-height: 1.4;
}
.rehearsal-row-debug-tag[data-tag-id="barre"] {
  background: rgba(249, 115, 22, 0.24);
  color: var(--accent-2);
}
.rehearsal-row-debug-tag[data-tag-id="colour"] {
  background: rgba(56, 189, 248, 0.20);
  color: #38bdf8;
}
.rehearsal-row-debug-tag[data-tag-id="jumps"] {
  background: rgba(168, 85, 247, 0.20);
  color: #c084fc;
}
.rehearsal-row-debug-tag[data-tag-id="quick"] {
  background: rgba(34, 197, 94, 0.20);
  color: #4ade80;
}

/* Phase A: session-end summary card. Overlays #view-rehearsal
   without unmounting it so the practice state is still behind the
   card if the user picks "Keep practicing". The card carries the
   locked-in tally, best/needs-work rows, and a practice
   recommendation; sits on a translucent scrim so the rehearsal
   view visibly recedes but doesn't disappear. */
.session-summary-overlay {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(10, 14, 20, 0.72);
  z-index: 20;
  padding: 24px;
  opacity: 0;
  transition: opacity 0.15s ease-out;
  pointer-events: none;
}
.session-summary-overlay.is-visible {
  opacity: 1;
  pointer-events: auto;
}
.session-summary-overlay[hidden] {
  display: none;
}
.session-summary-card {
  background: var(--surface, #131922);
  border: 1px solid var(--border);
  border-radius: 16px;
  padding: 28px 32px;
  max-width: 440px;
  width: 100%;
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.45);
}
.session-summary-heading {
  font-size: 22px;
  font-weight: 600;
  margin-bottom: 20px;
  color: var(--text, #e9edf2);
}
/* Follow-up: today's-goal badge on the summary card. A small inline
   stripe that surfaces when the elapsed session met the 15 min soft
   target. Muted good-green so it feels like a nod, not a trophy. */
.session-summary-goal {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  margin: -12px 0 20px 0;
  border-radius: 999px;
  background: rgba(74, 222, 128, 0.10);
  color: var(--good, #4ade80);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.02em;
}
.session-summary-goal[hidden] { display: none; }
.session-summary-goal-icon {
  font-size: 11px;
  line-height: 1;
}
.session-summary-lines {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-bottom: 20px;
}
.session-summary-line {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
  font-size: 14px;
}
.session-summary-line[hidden] {
  display: none;
}
.session-summary-line-label {
  color: var(--text-dim);
  font-weight: 500;
}
.session-summary-line-value {
  color: var(--text, #e9edf2);
  font-weight: 600;
  text-align: right;
}
.session-summary-recommendation {
  color: var(--text-dim);
  font-size: 13px;
  line-height: 1.5;
  margin-bottom: 20px;
  padding: 10px 12px;
  border-left: 2px solid var(--accent, #6ea8ff);
  background: rgba(110, 168, 255, 0.06);
  border-radius: 4px;
}
.session-summary-recommendation:empty {
  display: none;
}
.session-summary-cta-row {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
}
.session-summary-footer {
  margin-top: 12px;
  display: flex;
  justify-content: center;
  border-top: 1px solid var(--border);
  padding-top: 12px;
}
/* view-rehearsal needs to be a positioned container so the overlay
   can inset:0 to it rather than the whole viewport. */
#view-rehearsal {
  position: relative;
}

/* Phase B: cross-song skill map. Modal overlay pinned to the
   viewport so it works from any view (bandroom or rehearsal
   summary card). Two-column mastery breakdown + Recent Songs
   strip. Read-only — dismissed via Close button or backdrop
   click. */
.skill-map-overlay {
  position: fixed;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(10, 14, 20, 0.72);
  z-index: 50;
  padding: 24px;
}
.skill-map-overlay[hidden] {
  display: none;
}
.skill-map-card {
  background: var(--surface, #131922);
  border: 1px solid var(--border);
  border-radius: 16px;
  padding: 28px 32px;
  max-width: 640px;
  width: 100%;
  max-height: 80vh;
  overflow-y: auto;
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.45);
}
.skill-map-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: 20px;
}
.skill-map-heading {
  font-size: 22px;
  font-weight: 600;
  color: var(--text, #e9edf2);
}
.skill-map-empty {
  color: var(--text-dim);
  font-size: 14px;
  text-align: center;
  padding: 32px 0;
}
.skill-map-columns {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 24px;
  margin-bottom: 24px;
}
.skill-map-column-heading {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-dim);
  margin-bottom: 8px;
}
.skill-map-column-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.skill-map-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  padding: 6px 8px;
  border-radius: 6px;
  font-size: 13px;
  background: rgba(148, 163, 184, 0.06);
}
.skill-map-row-label {
  color: var(--text, #e9edf2);
  font-weight: 500;
}
.skill-map-row-dots {
  color: var(--accent, #6ea8ff);
  letter-spacing: 2px;
  font-size: 10px;
}
.skill-map-column-empty {
  color: var(--text-dim);
  font-size: 12px;
  padding: 8px;
  font-style: italic;
}
.skill-map-recent {
  border-top: 1px solid var(--border);
  padding-top: 20px;
}
.skill-map-recent-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.skill-map-recent-song {
  display: grid;
  grid-template-columns: 1fr auto auto;
  gap: 12px;
  padding: 8px 10px;
  border-radius: 6px;
  background: rgba(148, 163, 184, 0.06);
  font-size: 13px;
  align-items: baseline;
}
.skill-map-recent-song-title {
  color: var(--text, #e9edf2);
  font-weight: 500;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.skill-map-recent-song-parts {
  color: var(--text-dim);
  font-size: 12px;
}
.skill-map-recent-song-when {
  color: var(--text-dim);
  font-size: 11px;
  font-style: italic;
}

/* ─── Phase C warm-up mode ─── */

/* Bandroom suggestion chip. Appears under the CTA row when the
   session includes a barre section and no warm-up in the last 24h. */
.warmup-suggestion-chip {
  align-self: stretch;
  padding: 8px 12px;
  border-radius: 999px;
  border: 1px dashed var(--border);
  background: rgba(148, 163, 184, 0.05);
  color: var(--text-dim);
  font-size: 12px;
  cursor: pointer;
  font-style: italic;
  transition: background 0.15s, color 0.15s;
}
.warmup-suggestion-chip:hover {
  background: rgba(148, 163, 184, 0.12);
  color: var(--text);
}

/* Tertiary CTA style for the bandroom "Warm up" button — quieter
   than .ghost so it doesn't compete visually with Just jam. */
.bandroom-cta .ghost.tertiary {
  opacity: 0.78;
  font-size: 13px;
}

/* Modal overlay wrapper — mirrors .skill-map-overlay so the two
   feel like the same class of surface. */
.warmup-overlay {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background: rgba(10, 14, 20, 0.72);
}
.warmup-overlay[hidden] {
  display: none;
}
.warmup-card {
  width: min(560px, 100%);
  max-height: 80vh;
  overflow-y: auto;
  padding: 24px;
  background: var(--card, #1c2028);
  border: 1px solid var(--border);
  border-radius: 12px;
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.warmup-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
.warmup-heading {
  font-size: 18px;
  font-weight: 600;
  color: var(--text);
}
.warmup-hint {
  color: var(--text-dim);
  font-size: 13px;
}
.warmup-tile-strip {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 10px;
}
.warmup-tile {
  padding: 20px 8px;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: rgba(148, 163, 184, 0.05);
  color: var(--text);
  font-size: 20px;
  font-weight: 600;
  text-align: center;
  transition: border-color 0.15s, background 0.15s, box-shadow 0.15s;
}
.warmup-tile.is-active {
  border-color: var(--accent, #6ea8ff);
  box-shadow: 0 0 0 2px rgba(110, 168, 255, 0.25);
  background: rgba(110, 168, 255, 0.08);
}
.warmup-tile.is-passed {
  border-color: var(--good, #66c67d);
  background: rgba(102, 198, 125, 0.12);
  color: var(--good, #66c67d);
}
.warmup-progress {
  color: var(--text-dim);
  font-size: 13px;
  text-align: center;
}
.warmup-done-copy {
  padding: 12px;
  border-radius: 8px;
  background: rgba(102, 198, 125, 0.10);
  color: var(--good, #66c67d);
  text-align: center;
  font-weight: 500;
}
.warmup-done-copy[hidden] {
  display: none;
}

/* ─── Phase D best-rep playback ─── */

/* Small sibling button rendered after each rehearsal-section-row.
   Uses text-link styling so it doesn't compete with the row itself
   but is discoverable. */
.best-rep-button {
  align-self: flex-end;
  margin: -4px 12px 8px auto;
  padding: 2px 8px;
  border: none;
  background: transparent;
  color: var(--accent, #6ea8ff);
  font-size: 11px;
  cursor: pointer;
  border-radius: 4px;
  transition: background 0.15s;
}
.best-rep-button:hover {
  background: rgba(110, 168, 255, 0.10);
}

/* Sibling "⬇ WAV" download control. Uses the same text-link visual
   language as the play button but with a slightly dimmer accent so
   Play remains the primary affordance. Sits directly after the play
   button in the row (no extra margin) so the two read as a pair. */
.best-rep-download-button {
  align-self: flex-end;
  margin: -4px 12px 8px 0;
  padding: 2px 8px;
  border: none;
  background: transparent;
  color: var(--text-dim, #94a3b8);
  font-size: 11px;
  cursor: pointer;
  border-radius: 4px;
  transition: background 0.15s;
}
.best-rep-download-button:hover {
  background: rgba(148, 163, 184, 0.10);
  color: var(--accent, #6ea8ff);
}

/* Session-summary "Hear your best rep" link. Inline next to the
   Best mastery value; text-link sizing keeps the line clean. */
#session-summary-best-rep {
  margin-left: 8px;
  padding: 0;
  border: none;
  background: transparent;
  color: var(--accent, #6ea8ff);
  font-size: 12px;
  cursor: pointer;
}
#session-summary-best-rep[hidden] {
  display: none;
}

/* Opt-in toast — bottom-centre fixed banner. Auto-inserted by
   _promptBestRepOptIn on the first qualifying perfect run. */
.bestrep-optin-toast {
  position: fixed;
  left: 50%;
  bottom: 24px;
  transform: translateX(-50%);
  z-index: 70;
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 14px 18px;
  background: var(--card, #1c2028);
  border: 1px solid var(--border);
  border-radius: 10px;
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.4);
  color: var(--text);
  font-size: 13px;
}
.bestrep-optin-copy {
  font-weight: 500;
}
.bestrep-optin-cta {
  display: flex;
  gap: 8px;
  justify-content: flex-end;
}
.bestrep-optin-cta button {
  padding: 6px 12px;
  font-size: 12px;
}

/* Status line replacing the "Passes 0/0" scoreboard. Warm sentence
   copy from _rehearsalStatusCopy(); container reuses the
   #rehearsal-score div so DOM ids stay stable. */
.rehearsal-status {
  display: flex;
  align-items: center;
  gap: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--border);
  font-size: 13px;
  min-height: 34px;
}
.rehearsal-status-line {
  color: var(--text);
  font-family: inherit;
  font-weight: 500;
  line-height: 1.4;
}
.rehearsal-status-line.is-complete {
  color: var(--good);
  font-weight: 600;
}
.rehearsal-status-hint {
  color: var(--text-dim);
  margin-left: auto;
}

/* ─── Phase 2 mastery beads, state chip, tile fine-grained states ─── */

/* Bead: 5-dot row rendered inside .rehearsal-section-row-beads. Empty
   beads are a hollow outline; graded beads pick up quality colour. */
.bead {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  border: 1.5px solid var(--border);
  background: transparent;
  display: inline-block;
  transition: background 0.15s, border-color 0.15s;
}
.bead.is-empty {
  /* explicit for readability; same as default */
  background: transparent;
}
.bead.is-rough {
  background: #f4c063;
  border-color: #f4c063;
}
.bead.is-clean {
  background: #86efac;
  border-color: #86efac;
}
.bead.is-perfect {
  background: var(--good);
  border-color: var(--good);
  box-shadow: 0 0 4px rgba(74, 222, 128, 0.5);
}

/* State chip: small pill shown next to the beads. Hidden when
   rec.state === 'not_tried'. */
.mastery-chip {
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  padding: 2px 7px;
  border-radius: 999px;
  border: 1px solid var(--border);
  color: var(--text-dim);
  background: transparent;
  white-space: nowrap;
}
.mastery-chip[hidden] { display: none; }
.mastery-chip.is-learning {
  color: #f4c063;
  border-color: rgba(244, 192, 99, 0.35);
  background: rgba(244, 192, 99, 0.08);
}
.mastery-chip.is-practicing {
  color: #86efac;
  border-color: rgba(134, 239, 172, 0.35);
  background: rgba(134, 239, 172, 0.08);
}
.mastery-chip.is-locked-in {
  color: var(--good);
  border-color: rgba(74, 222, 128, 0.45);
  background: rgba(74, 222, 128, 0.14);
}
.mastery-chip.is-mastered {
  color: #ffffff;
  border-color: var(--good);
  background: rgba(74, 222, 128, 0.35);
}
/* Fading: previously locked-in, but stale. Muted amber signals
   "refresher needed" without the alarm of a fail state. */
.mastery-chip.is-fading {
  color: #f4c063;
  border-color: rgba(244, 192, 99, 0.4);
  background: rgba(244, 192, 99, 0.10);
}

/* Today's-goal strip: a quiet, single-row session-budget indicator
   above the section list. Not a hard cap — just a nudge to help a
   practicer notice they've been at it long enough to hit their
   Duolingo-style daily goal. Progress bar fills in real time. */
.rehearsal-session-goal {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 10px;
  margin: 0 0 8px 0;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: rgba(255, 255, 255, 0.02);
  font-size: 12px;
  color: var(--dim);
}
.rehearsal-session-goal[hidden] { display: none; }
.rehearsal-session-goal-label {
  font-weight: 600;
  color: var(--text);
  letter-spacing: 0.02em;
}
.rehearsal-session-goal-value {
  color: var(--dim);
  white-space: nowrap;
}
.rehearsal-session-goal-bar {
  flex: 1 1 auto;
  height: 4px;
  min-width: 40px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.06);
  overflow: hidden;
}
.rehearsal-session-goal-fill {
  display: block;
  height: 100%;
  width: 0;
  background: var(--accent);
  transition: width 0.4s ease-out;
}
.rehearsal-session-goal.is-complete .rehearsal-session-goal-fill {
  background: var(--good);
}
.rehearsal-session-goal.is-complete .rehearsal-session-goal-label {
  color: var(--good);
}

/* Tile fine-grained failure states. is-fail keeps the existing red
   treatment (defined elsewhere); is-close/early/late nudge the
   colour warmer so a musician sees "almost there" vs "wrong". */
.rehearsal-tile.is-close {
  border-color: #86efac;
  background: rgba(134, 239, 172, 0.10);
}
.rehearsal-tile.is-early {
  border-color: #93c5fd;
  background: rgba(147, 197, 253, 0.10);
}
.rehearsal-tile.is-late {
  border-color: #f4c063;
  background: rgba(244, 192, 99, 0.10);
}

/* Per-tile why-hint. One line max, breaks below the tile's main
   content. Written by _setWhyHint on fail; cleared on next active. */
.tile-why-hint {
  margin-top: 4px;
  font-size: 11px;
  line-height: 1.3;
  color: var(--text-dim);
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.rehearsal-tile.is-close .tile-why-hint { color: #86efac; }
.rehearsal-tile.is-early .tile-why-hint { color: #93c5fd; }
.rehearsal-tile.is-late .tile-why-hint { color: #f4c063; }

/* Bandroom post-analysis CTA pair. Hidden until onAnalysisComplete
   flips the bandroom into "Ready". Skip-to-jam is styled ghost so the
   primary rehearsal CTA reads first. */
.bandroom-cta {
  display: flex;
  gap: 12px;
  margin-top: 18px;
  justify-content: center;
}

/* ==========================================================
   Launchpad center-column view (tab strip + dedicated panel)
   ==========================================================
   The tab strip swaps #chord-stack (chord ribbon + guidance +
   riff + stems) for #launchpad-panel (status + mode + mirror
   grid + legend + voice tweaks). Persisted via
   state.settings.launchpadCenterTab.
*/
.center-tabs {
  display: flex;
  gap: 6px;
  margin-bottom: 10px;
  border-bottom: 1px solid var(--border, #2b2f38);
}
.center-tab {
  background: transparent;
  border: 0;
  color: var(--text-dim, #8a8f99);
  font: inherit;
  padding: 8px 14px;
  cursor: pointer;
  border-bottom: 2px solid transparent;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.center-tab:hover { color: var(--text, #e6e8ec); }
.center-tab.is-active {
  color: var(--text, #e6e8ec);
  border-bottom-color: var(--accent, #6ce07a);
}
.center-tab[disabled] { opacity: 0.5; cursor: not-allowed; }
.center-tab-warn-dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #e07a6c;
  box-shadow: 0 0 4px #e07a6c;
}
.chord-stack[hidden], .launchpad-panel[hidden] { display: none !important; }

/* Panel body */
.launchpad-panel {
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 4px 0;
}
.lp-section-label {
  display: block;
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-dim, #8a8f99);
  margin-bottom: 4px;
}
.lp-status-row {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}
.lp-status-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 12px;
  border-radius: 999px;
  background: #1c1f26;
  font-size: 13px;
  font-weight: 500;
}
.lp-status-device { color: var(--text-dim, #8a8f99); font-size: 12px; }
.lp-status-enable { display: inline-flex; align-items: center; gap: 6px; margin-left: auto; }
.lp-status-enable[hidden] { display: none !important; }

/* Preset ("Play style") selector: five opinionated radios stacked
   vertically inside the settings column, with a short caption below
   that swaps in on selection. */
.lp-preset-row {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 6px;
  padding: 10px 12px;
  background: #16181e;
  border-radius: 8px;
}
/* Chunky button-style radios. The native radio is hidden and the
   entire label becomes the clickable button — round accent dot on
   the left, label text on the right, glowing border/background when
   selected. The per-preset accent color is set via CSS custom
   properties on each label (--preset-color / --preset-color-soft) so
   the shared button rules stay DRY. Matches the Launchpad top-row
   preset LEDs painted from jam.js. */
.lp-preset-row label {
  --preset-color: #8a8f99;
  --preset-color-soft: rgba(138, 143, 153, 0.14);
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 12px;
  min-height: 34px;
  background: #1c1f27;
  border: 1px solid #2b2f38;
  border-radius: 999px;
  color: #d8dbe1;
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
  transition: background 120ms ease, border-color 120ms ease, transform 60ms ease;
}
.lp-preset-row label:hover {
  background: #20242d;
  border-color: #3a3f4a;
}
.lp-preset-row label:active {
  transform: translateY(1px);
}
/* Hide the native radio; the label itself is the clickable target. */
.lp-preset-row label input[type="radio"] {
  position: absolute;
  opacity: 0;
  pointer-events: none;
  width: 0;
  height: 0;
}
/* Accent dot rendered via ::before so we don't need to touch the
   markup. Empty circle by default; filled bright when selected. */
.lp-preset-row label::before {
  content: '';
  flex: 0 0 auto;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  border: 2px solid var(--preset-color);
  background: transparent;
  box-shadow: none;
  transition: background 120ms ease, box-shadow 120ms ease;
}
/* Selected: full accent color, subtle glow, filled dot, soft tint
   background so the button visibly stands out from its siblings. */
.lp-preset-row label:has(input:checked) {
  background: var(--preset-color-soft);
  border-color: var(--preset-color);
  color: #ffffff;
}
.lp-preset-row label:has(input:checked)::before {
  background: var(--preset-color);
  box-shadow: 0 0 0 3px var(--preset-color-soft),
              0 0 10px 0 var(--preset-color);
}
/* Per-preset accent colors — matches the Launchpad hardware LEDs
   painted from _launchpadPaintPresetTopRow (jam.js). */
.lp-preset-row label:has(input[value="learn-song"])       { --preset-color: #64b5f6; --preset-color-soft: rgba(100, 181, 246, 0.14); }
.lp-preset-row label:has(input[value="perform-song"])     { --preset-color: #4fc3f7; --preset-color-soft: rgba( 79, 195, 247, 0.14); }
.lp-preset-row label:has(input[value="learn-harmony"])    { --preset-color: #ba68c8; --preset-color-soft: rgba(186, 104, 200, 0.14); }
.lp-preset-row label:has(input[value="jam-in-key"])       { --preset-color: #ff8a65; --preset-color-soft: rgba(255, 138, 101, 0.14); }
.lp-preset-row label:has(input[value="melody-practice"])  { --preset-color: #ffd54f; --preset-color-soft: rgba(255, 213,  79, 0.14); }
.lp-preset-row label:has(input[value="play-bassline"])    { --preset-color: #a1887f; --preset-color-soft: rgba(161, 136, 127, 0.14); }
.lp-preset-row label:has(input[value="contribute-vocal"])    { --preset-color: #66bb6a; --preset-color-soft: rgba(102, 187, 106, 0.16); }
.lp-preset-row label:has(input[value="contribute-drums"])    { --preset-color: #ef5350; --preset-color-soft: rgba(239,  83,  80, 0.16); }
.lp-preset-row label:has(input[value="contribute-bass"])     { --preset-color: #ab47bc; --preset-color-soft: rgba(171,  71, 188, 0.16); }
.lp-preset-row label:has(input[value="contribute-harmonic"]) { --preset-color: #26c6da; --preset-color-soft: rgba( 38, 198, 218, 0.16); }
.lp-preset-row label:has(input[value="contribute-sections"]) { --preset-color: #ffb74d; --preset-color-soft: rgba(255, 183,  77, 0.16); }
/* Sub-header inside the preset row splitting the play-along presets
   from the Contribute family. Visually a thin caption above a divider
   line to signal "different mental model below". */
.lp-preset-group-label {
  margin-top: 6px;
  padding-top: 8px;
  width: 100%;
  border-top: 1px solid #23262e;
  color: var(--text-dim, #8a8f99);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.lp-preset-caption {
  margin: 4px 0 0;
  padding-top: 6px;
  border-top: 1px solid #23262e;
  width: 100%;
  color: var(--text-dim, #8a8f99);
  font-size: 12px;
  line-height: 1.4;
}
/* Suggestion shown when the classifier thinks a different preset
   would fit the loaded song better than the currently-selected one. */
.lp-preset-hint {
  margin: 6px 0 0;
  padding: 6px 8px;
  width: 100%;
  background: rgba(255, 183, 77, 0.08);
  border-left: 2px solid #ffb74d;
  border-radius: 4px;
  color: #ffcc80;
  font-size: 11px;
  line-height: 1.4;
}

/* Advanced-controls disclosure wraps the underlying mode/layout/assist/
   instrument/anticipation/openjam controls so they're hidden by default
   but still reachable for power users. */
.lp-advanced {
  border-top: 1px solid var(--border, #2b2f38);
  padding-top: 8px;
}
.lp-advanced-summary {
  cursor: pointer;
  padding: 4px 0;
  font-size: 12px;
  color: var(--text-dim, #8a8f99);
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.lp-advanced-body {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 8px 0;
}

.lp-mode-row {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: center;
}
.lp-mode-row label { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; }
.lp-openjam-options {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  padding: 8px 12px;
  background: #16181e;
  border-radius: 8px;
}
.lp-openjam-options label { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; }

/* Three-column split: legend (auto-width) | mirror (fills the free
   space) | settings (fixed narrow on the right). Legend on the left
   lets it flow vertically without stealing the grid's headroom, and
   the mirror stays the visual anchor in the middle. */
.lp-split {
  display: grid;
  grid-template-columns: 180px minmax(0, 1fr) 260px;
  gap: 20px;
  align-items: start;
}
@media (max-width: 900px) {
  .lp-split { grid-template-columns: 1fr; }
}

/* Right-side settings stack — mode / layout / assist / instrument /
   anticipation / open-jam options / legend, top-to-bottom. */
.lp-settings-column {
  display: flex;
  flex-direction: column;
  gap: 12px;
  min-width: 0;
}
.lp-settings-column .lp-mode-row,
.lp-settings-column .lp-openjam-options {
  padding: 6px 10px;
  background: #16181e;
  border-radius: 8px;
}
.lp-settings-column .lp-mode-row {
  flex-direction: column;
  align-items: flex-start;
  gap: 6px;
}
.lp-settings-column .lp-mode-row .lp-section-label {
  margin-bottom: 0;
}
.lp-settings-column .lp-legend-wrap {
  padding: 6px 10px;
  background: #16181e;
  border-radius: 8px;
}

/* On-screen 8×8 mirror. Rendered so bottom-left = pad 11 to
   match the physical device orientation. The grid fills the free
   column of .lp-split (settings sit at a fixed 260px on the right)
   so the mirror scales up as the viewport grows. */
.lp-grid-wrap {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
  /* Cap by both dimensions so the grid never overflows the viewport
     vertically. `min()` picks whichever is smaller: full column width,
     or the available vertical space (viewport minus header + tabs +
     status/legend rows above + transport controls below). The cap
     lives on the wrapper (not .lp-grid) so the "PADS" label and
     countdown bar share the same centered width as the grid itself. */
  width: min(100%, calc(100vh - 190px));
  margin: 0 auto;
}
.lp-grid {
  display: grid;
  grid-template-columns: repeat(8, 1fr);
  grid-auto-rows: 1fr;
  gap: 4px;
  aspect-ratio: 1 / 1;
  padding: 6px;
  background: #0e1015;
  border-radius: 10px;
  width: 100%;
}
.lp-pad {
  border: 0;
  border-radius: 4px;
  background: #1b1e25;
  cursor: pointer;
  padding: 0;
  transition: transform 60ms ease-out, box-shadow 60ms ease-out;
  position: relative;
}
.lp-pad:hover { transform: scale(1.05); }
.lp-pad:active { transform: scale(0.95); }
.lp-pad:focus-visible { outline: 2px solid var(--accent, #6ce07a); outline-offset: 2px; }
.lp-pad--pulse {
  /* Upcoming-chord pads pulse and stay dimmer than active pads so
     the currently-sounding tones remain the visual anchor. */
  animation: lp-pulse 0.85s ease-in-out infinite;
}
/* Static (all playable pads that aren't currently sounding) get
   knocked back so the active pad reads as "you are here" at a glance.
   Only applied in modes with a single sounding pad (song-verify /
   song-display); jam/instrument modes leave every pad bright because
   the user is free-picking, not tracking a "now" position. */
.lp-grid--has-active .lp-pad--static { filter: brightness(0.28); }
/* Disabled / empty pad: knocked way back visually so it clearly
   reads as "unusable" next to bright, tap-able chop pads. Uses
   both a low opacity and a dark overlay so the residual tint
   from the recorded RGB doesn't compete with active neighbours. */
.lp-pad--disabled {
  filter: brightness(0.35) saturate(0.4);
  opacity: 0.45;
}
/* Active pad: brightened + white ring + strong glow so the user's
   current position pops against the dimmed backdrop. */
.lp-pad--active {
  filter: brightness(1.5) saturate(1.15);
  box-shadow:
    0 0 0 2px rgba(255, 255, 255, 0.9),
    0 0 18px 4px currentColor;
  z-index: 1;
}
@keyframes lp-pulse {
  0%, 100% { filter: brightness(0.55); }
  50%     { filter: brightness(0.15); }
}

/* Legend */
.lp-legend { display: flex; flex-direction: column; gap: 8px; font-size: 12px; }
.lp-legend-item { display: flex; align-items: center; gap: 8px; }
.lp-legend-swatch {
  width: 14px; height: 14px; border-radius: 3px; flex: 0 0 14px;
  border: 1px solid rgba(255,255,255,0.08);
}
.lp-legend-caption {
  margin-top: 6px;
  color: var(--text-dim, #8a8f99);
  font-size: 11px;
  line-height: 1.4;
}

/* Horizontal variant: chips flow left-to-right in a wrap row above
   the pad grid; caption drops to its own line below. */
.lp-legend-wrap--horizontal {
  padding: 6px 10px;
  background: #16181e;
  border-radius: 8px;
}
.lp-legend--horizontal {
  flex-direction: row;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 14px;
}
.lp-legend--horizontal .lp-legend-caption {
  flex-basis: 100%;
  margin-top: 2px;
}

/* Countdown bar: sits above the pad grid, communicates time until the
   next distinct chord change. Fill shrinks smoothly from 100% to 0. */
.lp-countdown {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 6px 10px;
  background: #16181e;
  border-radius: 8px;
  margin-bottom: 6px;
}
.lp-countdown-track {
  position: relative;
  height: 8px;
  background: #0d0f13;
  border-radius: 4px;
  overflow: hidden;
}
.lp-countdown-fill {
  position: absolute;
  top: 0;
  left: 0;
  bottom: 0;
  width: 0%;
  background: linear-gradient(90deg, #4dd0e1 0%, #ffb74d 65%, #ff5252 100%);
  transition: width 80ms linear;
  border-radius: 4px;
}
.lp-countdown-label {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  font-size: 12px;
  color: var(--text-dim, #8a8f99);
}
.lp-countdown-next {
  color: var(--text, #e6e8ec);
  font-weight: 600;
}
.lp-countdown-time {
  font-variant-numeric: tabular-nums;
}
.lp-countdown.lp-countdown--imminent .lp-countdown-fill {
  animation: lp-countdown-pulse 0.4s ease-in-out infinite alternate;
}
@keyframes lp-countdown-pulse {
  0% { filter: brightness(0.9); }
  100% { filter: brightness(1.35); }
}

/* Voice tweaks (collapsible) */
.lp-voice { border-top: 1px solid var(--border, #2b2f38); padding-top: 8px; }
.lp-voice-summary { cursor: pointer; padding: 4px 0; font-size: 13px; }
.lp-voice-body { display: flex; flex-direction: column; gap: 6px; padding: 8px 0; }
.lp-tweak-row {
  display: grid;
  grid-template-columns: 130px 1fr 60px;
  align-items: center;
  gap: 10px;
  font-size: 12px;
}
.lp-tweak-label { color: var(--text-dim, #8a8f99); }
.lp-tweak-readout { text-align: right; font-variant-numeric: tabular-nums; color: var(--text, #e6e8ec); }
.lp-tweak-row input[type="range"] { width: 100%; }
.lp-voice-actions { display: flex; justify-content: flex-end; padding-top: 6px; }

/* ============================================================ landing reskin
   JamN marketing landing for #view-intake. Purple/indigo theme, scoped so the
   band-room and perform views keep the calm orange language. Every functional
   id is preserved (#upload-form, #upload-file, #upload-submit, #engine-banner*,
   #intake-form/#intake-instrument/#intake-submit, #upload-attest,
   #intake-change-device) — this block is visual only.
   ============================================================================ */
:root {
  --brand-purple: #8b5cf6;
  --brand-purple-lite: #a78bfa;
  --brand-grad: linear-gradient(120deg, #a855f7 0%, #7c6cf5 50%, #6366f1 100%);
}

/* Deep purple-black backdrop (harmonises every view, fixed while scrolling). */
body {
  background:
    radial-gradient(1200px 620px at 50% -12%, rgba(96, 63, 176, 0.34), transparent 60%),
    radial-gradient(900px 520px at 10% 6%, rgba(124, 58, 237, 0.12), transparent 55%),
    radial-gradient(900px 520px at 90% 6%, rgba(99, 102, 241, 0.12), transparent 55%),
    #07060d;
  background-attachment: fixed;
}

/* header */
.jam-header {
  border-bottom-color: rgba(255, 255, 255, 0.06);
  padding: 20px 32px;
  justify-content: flex-start;
  gap: clamp(32px, 7vw, 120px);
  position: relative;
}
.brand { display: inline-flex; align-items: center; gap: 11px; font-size: 20px; flex: none; }
.brand-mark, .brand-mark svg { display: inline-flex; }
.brand-text { display: inline-flex; flex-direction: column; line-height: 1; gap: 3px; }
.brand-name { font-weight: 800; letter-spacing: -0.01em; }
.brand-domain {
  font-size: 11px; font-weight: 500; letter-spacing: 0.02em;
  color: var(--text-dim); text-transform: lowercase;
}
.jam-header #account-chip { margin-left: auto; }
.nav-burger {
  display: none;                       /* desktop: inline nav, no burger */
  align-items: center; justify-content: center;
  width: 40px; height: 40px; margin-left: 14px;
  border-radius: 10px; cursor: pointer;
  color: var(--text); background: rgba(124, 58, 237, 0.16);
  box-shadow: inset 0 0 0 1px rgba(167, 139, 250, 0.28);
}
.nav-burger svg { width: 22px; height: 22px; }
.jam-header nav a {
  font-size: 15px; padding: 8px 14px; border-radius: 9px;
  transition: color .15s, background .15s;
}
.jam-header nav a:hover { color: var(--text); }
.jam-header nav a.active {
  color: #fff;
  background: rgba(124, 58, 237, 0.28);
  box-shadow: inset 0 0 0 1px rgba(167, 139, 250, 0.35);
}

/* decorative waveform wisps flanking the intake column */
#view-intake { position: relative; }
#view-intake::before,
#view-intake::after {
  content: "";
  position: absolute; top: 40px;
  width: 42%; height: 320px;
  pointer-events: none; z-index: 0; opacity: 0.7;
  background: radial-gradient(60% 80% at 50% 50%, rgba(139, 92, 246, 0.22), transparent 70%);
  filter: blur(2px);
  -webkit-mask-image: repeating-linear-gradient(90deg, #000 0 2px, transparent 2px 6px);
          mask-image: repeating-linear-gradient(90deg, #000 0 2px, transparent 2px 6px);
}
#view-intake::before { left: -6%; transform: rotate(-6deg); }
#view-intake::after  { right: -6%; transform: rotate(6deg); }

.landing {
  position: relative; z-index: 1;
  max-width: 680px; margin: 0 auto;
  text-align: center;
}

/* hero */
.landing .hero {
  font-size: 72px; line-height: 0.98; font-weight: 800;
  letter-spacing: -0.02em; margin: 40px 0 18px;
}
.hero-accent {
  background: var(--brand-grad);
  -webkit-background-clip: text; background-clip: text;
  color: transparent;
}
.landing .sub { color: var(--text-dim); font-size: 19px; line-height: 1.5; margin: 0 0 40px; }

/* upload card — the single dominant focal point of the page */
.upload-card {
  position: relative;
  width: 100%; max-width: 600px; margin: 0 auto;
  background: linear-gradient(180deg, rgba(32, 24, 54, 0.72), rgba(18, 14, 30, 0.72));
  border: 1px solid rgba(139, 92, 246, 0.22);
  border-radius: 26px;
  padding: 44px 48px 40px;
  text-align: center;
  backdrop-filter: blur(8px);
  box-shadow: 0 24px 60px rgba(6, 4, 16, 0.55);
}
/* soft purple radial glow that lifts the card off the background */
.upload-card__glow {
  position: absolute; inset: -16% -12% -22%;
  z-index: -1; pointer-events: none;
  background: radial-gradient(closest-side,
    rgba(139, 92, 246, 0.40),
    rgba(124, 58, 237, 0.14) 58%,
    transparent 80%);
  filter: blur(48px);
}
.upload-card__badge {
  width: 60px; height: 60px; margin: 0 auto 16px;
  display: grid; place-items: center; border-radius: 50%;
  color: var(--brand-purple-lite);
  background: rgba(139, 92, 246, 0.14);
  box-shadow: inset 0 0 0 1px rgba(139, 92, 246, 0.25);
}
.upload-card__badge svg { width: 28px; height: 28px; }
.upload-card__title { font-size: 27px; font-weight: 700; letter-spacing: -0.01em; margin: 0 0 7px; }
.upload-card__meta { color: var(--text-dim); font-size: 15px; letter-spacing: 0.04em; margin: 0 0 26px; }

.upload-form { display: block; }
.dropzone {
  display: flex; flex-direction: column; align-items: center; gap: 9px;
  border: 1.5px dashed rgba(139, 92, 246, 0.42);
  border-radius: 18px; padding: 48px 22px; cursor: pointer;
  background: rgba(139, 92, 246, 0.03);
  transition: border-color .15s, background .15s;
}
.dropzone:hover { border-color: var(--brand-purple-lite); background: rgba(139, 92, 246, 0.08); }
.dropzone__icon { color: var(--brand-purple-lite); line-height: 0; }
.dropzone__icon svg { width: 44px; height: 44px; display: block; }
.dropzone__cta { color: var(--brand-purple-lite); font-size: 20px; font-weight: 700; }
.dropzone__hint { color: var(--text-dim); font-size: 15px; }

.analyze-btn {
  margin-top: 20px; width: 100%;
  border: none; border-radius: 13px;
  padding: 17px 20px; font-size: 17px; font-weight: 700;
  font-family: inherit; color: #fff;
  background: var(--brand-grad); cursor: pointer;
  box-shadow: 0 10px 26px rgba(124, 58, 237, 0.35);
  transition: filter .15s, opacity .15s, transform .1s;
}
.analyze-btn:hover:not(:disabled) { filter: brightness(1.08); }
.analyze-btn:active:not(:disabled) { transform: translateY(1px); }
.analyze-btn:disabled { opacity: 0.4; cursor: not-allowed; box-shadow: none; }

/* engine status compacted into a small pill inside the card */
.landing .engine-banner {
  max-width: none; width: 100%; margin: 14px 0 0;
  display: flex; align-items: center; gap: 10px; text-align: left;
  padding: 11px 14px; border-radius: 12px;
  background: rgba(16, 12, 26, 0.6);
  border: 1px solid rgba(255, 255, 255, 0.06);
}
.landing .engine-banner-text { display: flex; flex-direction: column; gap: 1px; }
.landing .engine-banner-text strong { font-size: 13.5px; font-weight: 600; }
.landing .engine-banner-text span { font-size: 12.5px; color: var(--text-dim); }
.landing .engine-banner-dot { flex: none; }

/* rights attestation — sits quietly at the base of the card */
.attest {
  display: flex; gap: 10px; align-items: flex-start; justify-content: center;
  max-width: 520px; margin: 20px auto 0;
  color: var(--text-dim); font-size: 13px; line-height: 1.4; cursor: pointer;
  text-align: left;
}
.attest input { margin-top: 2px; flex: none; accent-color: var(--brand-purple); }
.landing .intake-error { margin: 16px auto 0; max-width: 560px; }

/* decorative feature cards */
.feature-cards {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; margin: 40px 0 0;
}
.feature-card {
  background: rgba(20, 16, 32, 0.5);
  border: 1px solid rgba(255, 255, 255, 0.05);
  border-radius: 16px; padding: 22px 16px; text-align: center;
}
.feature-card__icon { display: inline-flex; color: var(--brand-purple-lite); margin-bottom: 12px; }
.feature-card__icon svg { width: 30px; height: 30px; }
.feature-card h3 { font-size: 18px; font-weight: 700; margin: 0 0 6px; }
.feature-card p { font-size: 13.5px; color: var(--text-dim); margin: 0; line-height: 1.4; }

/* footer chips */
.landing-footer {
  display: flex; justify-content: center; gap: 40px; flex-wrap: wrap;
  margin: 44px 0 0; padding-top: 22px;
  border-top: 1px solid rgba(255, 255, 255, 0.05);
  color: var(--text-dim); font-size: 14px;
}
.landing-footer__item { display: inline-flex; align-items: center; gap: 8px; }
.landing-footer__item svg { width: 17px; height: 17px; color: var(--brand-purple-lite); }

@media (max-width: 620px) {
  .landing .hero { font-size: 46px; }
  .upload-card { padding: 28px 22px 30px; }
  .feature-cards { grid-template-columns: repeat(2, 1fr); }
  .landing-footer { gap: 20px; }
}

/* ============================================================ one-screen fit
   The landing must fit a single viewport with NO scroll on any device. When
   #view-intake is the active view we turn <body> into a full-height flex
   column (header auto, #jam-app fills the rest) and center the landing inside.
   Vertical rhythm collapses to a single flex `gap`, every size uses vh-aware
   clamp()s, and decorative rows are shed on short viewports so the upload card
   always stays above the fold. Scoped via :has() so band-room / perform views
   keep their normal scrolling behaviour.
   ============================================================================ */
body:has(#view-intake.active) {
  height: 100dvh;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
body:has(#view-intake.active) > .jam-header { flex: 0 0 auto; }
body:has(#view-intake.active) #jam-app {
  flex: 1 1 auto;
  min-height: 0;
  max-width: 900px;
  padding: clamp(6px, 1.5vh, 20px) 24px;
  display: flex;
  align-items: center;
  justify-content: center;
}
body:has(#view-intake.active) #view-intake.active { width: 100%; }

/* Collapse the landing's stacked margins into one flex gap */
#view-intake .landing {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: clamp(6px, 1.4vh, 20px);
  max-width: 680px;
  max-height: 100%;
  overflow: hidden;
}
#view-intake .landing .hero {
  margin: 0;
  font-size: clamp(24px, 4.4vh, 58px);
  line-height: 1.02;
}
#view-intake .landing .sub {
  margin: 0;
  font-size: clamp(13px, 1.9vh, 19px);
}
#view-intake .upload-card {
  width: 100%;
  max-width: 600px;
  box-sizing: border-box;
  padding: clamp(20px, 3vh, 44px) clamp(22px, 4vw, 48px) clamp(18px, 2.8vh, 40px);
}
#view-intake .upload-card__badge {
  width: clamp(40px, 6vh, 60px);
  height: clamp(40px, 6vh, 60px);
  margin: 0 auto clamp(6px, 1.4vh, 16px);
}
#view-intake .upload-card__badge svg { width: clamp(20px, 3vh, 28px); height: clamp(20px, 3vh, 28px); }
#view-intake .upload-card__title { font-size: clamp(19px, 2.7vh, 27px); }
#view-intake .upload-card__meta { margin: 0 0 clamp(12px, 2.2vh, 26px); }
#view-intake .dropzone { padding: clamp(16px, 3.2vh, 48px) 22px; gap: clamp(5px, 1.1vh, 9px); }
#view-intake .dropzone__icon svg { width: clamp(30px, 4.6vh, 44px); height: clamp(30px, 4.6vh, 44px); }
#view-intake .analyze-btn {
  margin-top: clamp(12px, 1.9vh, 20px);
  padding: clamp(12px, 1.8vh, 17px) 20px;
}
#view-intake .landing .engine-banner { margin: clamp(8px, 1.3vh, 14px) 0 0; }
#view-intake .attest { margin: clamp(12px, 1.8vh, 20px) auto 0; }

/* Progressive shedding so the essential upload stack never clips on short
   viewports. Trust pill, then hero secondary text, then badge, in that order.
   The card title, dropzone, button and attest always survive. */
@media (max-height: 700px) {
  #view-intake .landing .sub { display: none; }
}
@media (max-height: 600px) {
  #view-intake .landing .engine-banner { display: none; }
}
@media (max-height: 520px) {
  #view-intake .landing .hero { display: none; }
  #view-intake .upload-card__badge { display: none; }
}

/* Narrow phones: collapse the inline nav into a hamburger dropdown. Sign-in
   (#account-chip) stays a separate always-visible element to the left of the
   burger. Pure CSS toggle via the hidden #nav-toggle checkbox — no JS needed. */
@media (max-width: 620px) {
  .jam-header { padding: 12px 16px; }
  .brand { font-size: 17px; }
  .nav-burger { display: inline-flex; }

  /* nav becomes an absolutely-positioned dropdown panel, hidden until toggled */
  .jam-header nav {
    display: none;
    position: absolute; top: calc(100% + 6px); right: 12px;
    flex-direction: column; align-items: stretch; gap: 4px;
    margin-left: 0; padding: 8px;
    min-width: 168px; z-index: 60;
    background: #150f24;
    border: 1px solid rgba(167, 139, 250, 0.28);
    border-radius: 14px;
    box-shadow: 0 16px 40px rgba(0, 0, 0, 0.55);
  }
  .nav-toggle:checked ~ nav { display: flex; }
  .jam-header nav a { font-size: 15px; padding: 11px 14px; border-radius: 10px; }

  /* Free up header space: hide the email, keep only a compact Sign out button
     that never wraps. Sign-in stays separate from the burger. */
  .jam-header #account-chip { gap: 6px; }
  .jam-header .account-email { display: none; }
  .jam-header .account-btn {
    padding: 7px 12px; font-size: 13px; white-space: nowrap; line-height: 1;
  }
}
