/* chrome.css — Extras, playlists, the responsive rules and the player's menus.

   Cut from the single style.css on 2026-08-02. The <link> order in
   index.html IS the cascade order these were split at; reordering the
   tags changes which rule wins. */

/* --------------------------------------------------------------- extras */

.extras {
  max-width: 640px;
  margin: 2.2rem auto 0;
  font-size: 0.85rem;
  color: var(--muted);
}

.extras details {
  border-top: 1px solid var(--border);
  padding: 0.5rem 0;
}

.extras summary {
  cursor: pointer;
  font-weight: 600;
  color: var(--fg);
}

.extras ul { margin: 0.5rem 0 0.25rem; padding-left: 1.4rem; }
.extras p { margin: 0.4rem 0; }

/* Collapsed "Tags" section: the harmonic chip rows, left-aligned here (the
   retired summary block centred them). */
.extras-harmony { display: flex; flex-direction: column; gap: 0.4rem; margin-top: 0.5rem; }
.extras-harmony .harm-row { justify-content: flex-start; }

/* Collapsed "Details" section: a compact label / value list (style, form,
   year, page). */
.detail-list {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.25rem 0.9rem;
  margin: 0.5rem 0 0.25rem;
}
.detail-list dt {
  color: var(--muted);
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  align-self: baseline;
}
.detail-list dd { margin: 0; color: var(--fg); }
.detail-list dd a { color: var(--accent-ink); text-decoration: none; }
.detail-list dd a:hover { text-decoration: underline; }

/* "Links" block: the tune's reference pages, sitting above the collapsibles
   and never folded — one click to Wikipedia / jazzstandards / the lyrics. */
.links-block { padding: 0.1rem 0 0.7rem; }
.links-head {
  color: var(--muted);
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.links-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem 0.9rem;
  margin-top: 0.35rem;
}
.links-row a { color: var(--accent-ink); text-decoration: none; }
.links-row a:hover { text-decoration: underline; }
/* "played as …" under a livestream block whose title match was approximate. */
.links-as { color: var(--muted); font-size: 0.78rem; font-style: italic; margin-top: 0.2rem; }
.links-summary { margin: 0.45rem 0 0; font-size: 0.95em; line-height: 1.45; }

/* "Recordings": one expanded artist per line, then the verbatim margin text
   folded away underneath. */
.rec-list { margin: 0.5rem 0 0.25rem; padding-left: 1.1rem; list-style: none; }
.rec-list li { margin: 0.1rem 0; }
/* The expanded names are buttons that search the library for that
   performer; they must read as plain text until hovered. */
.rec-artist {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  color: var(--fg);
  cursor: pointer;
}
.rec-artist:hover, .rec-artist:focus-visible { text-decoration: underline; }
.rec-raw { font-style: italic; }
.rec-years { color: var(--muted); }
.rec-mark { color: var(--muted); font-size: 0.85em; }
.rec-raw-block { border: 0; padding: 0.4rem 0 0; }
.rec-raw-block summary { font-weight: 400; color: var(--muted); font-size: 0.9em; }

/* "Listen" block: YouTube links for the notable recordings (enrichment).
   Open like the Links block, so it needs the same top spacing. */
.listen-list { margin: 0.35rem 0 0; padding-left: 1.1rem; list-style: none; }
.listen-list li { margin: 0.15rem 0; }
.listen-list a { color: var(--accent-ink); text-decoration: none; }
.listen-list a:hover { text-decoration: underline; }
.listen-title { color: var(--muted); font-size: 0.85em; }
.listen-wiki { font-size: 0.8em; opacity: 0.75; }

/* Variants block: shown below the chord grid, always visible (not collapsed). */
/* Full-width container so each .variant-grid centres within the panel exactly
   like the main grid; syncVariantGrids pins their widths to match, so the bars
   line up. The title/caption are centred to the same width (via JS max-width). */
.variants {
  margin: 1.6rem 0 0;
}

.variants-title {
  font-family: var(--font-cond);
  font-weight: 700;
  font-size: 1.15rem;
  letter-spacing: 0.04em;
  color: var(--fg);
  border-top: 1px solid var(--border);
  padding-top: 0.7rem;
  margin: 0 auto 0.5rem;
}

.variant { margin: 0.75rem 0 1rem; }

/* A clickable variant swaps its bars into the grid; its box lifts on hover and
   shows an "applied" state when active. Padding keeps the outline off the bars. */
.variant.clickable {
  cursor: pointer;
  border: 1px solid transparent;
  border-radius: 0.5rem;
  padding: 0.3rem 0.5rem;
  transition: background 0.12s, border-color 0.12s;
}
.variant.clickable:hover { background: var(--panel); border-color: var(--border); }
.variant.clickable:focus-visible { outline: 2px solid var(--muted); outline-offset: 2px; }
.variant.clickable.active { border-color: var(--accent); background: var(--chip-bg); }
/* A small "applied" tag on the active variant's caption. */
.variant.clickable.active .variant-caption::after {
  content: " ✓ applied";
  color: var(--accent-ink);
  font-weight: 600;
}

/* `.bar.variant-swap` marks main-grid bars an applied variant has swapped in.
   It carries no visual styling (the swapped chords speak for themselves); the
   class is kept purely as a hook for tests/inspection. */

.variant-caption {
  color: var(--fg);
  font-size: 0.85rem;
  margin: 0 auto 0.4rem;
}

/* The variant grid is a plain .grid; syncVariantGrids gives it the main grid's
   fitted font-size and pixel width so their columns align. */
.variant-grid { margin-top: 0.2rem; }

/* ------------------------------------------------------------- playlists */

/* Tune-head controls (§5.3): add button on every tune; Prev/Next only while
   a playlist is active. */
.head-actions {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 0.6rem;
  margin-top: 0.55rem;
}

.step-btn {
  background: none;
  border: 1px solid var(--border);
  border-radius: 0.4rem;
  color: var(--muted);
  font-size: 0.78rem;
  padding: 0.22rem 0.6rem;
  cursor: pointer;
  white-space: nowrap;
}

.step-btn:hover:not(:disabled) { color: var(--fg); border-color: var(--muted); }
.step-btn:disabled { opacity: 0.4; cursor: default; }

/* Playlists menu (top bar) and add-to-playlist popover: dropdowns on desktop,
   bottom sheets on phones (.sheet, §11.6). */
#playlistMenu,
#plPopover {
  position: fixed;
  z-index: 60;
  min-width: 15rem;
  max-width: 20rem;
  max-height: 70vh;
  overflow-y: auto;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 0.5rem;
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.35);
  padding: 0.4rem;
  font-size: 0.9rem;
}

#playlistMenu[hidden],
#plPopover[hidden] { display: none; }

#playlistMenu.sheet,
#plPopover.sheet {
  left: 0;
  right: 0;
  bottom: 0;
  top: auto;
  max-width: none;
  border-radius: 0.8rem 0.8rem 0 0;
  border-bottom: none;
}

.pl-menu-title {
  font-weight: 600;
  padding: 0.3rem 0.5rem 0.4rem;
}

.pl-menu-empty {
  color: var(--muted);
  font-size: 0.82rem;
  padding: 0.3rem 0.5rem 0.4rem;
}

/* "Show all tunes" — deactivates the current playlist. */
.pl-all {
  display: block;
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  border-radius: 0.3rem;
  color: var(--fg);
  font-size: 0.88rem;
  padding: 0.4rem 0.5rem;
  cursor: pointer;
}

.pl-all:hover { background: var(--panel); }

.pl-menu-row {
  display: flex;
  align-items: center;
  gap: 0.2rem;
  border-radius: 0.3rem;
}

.pl-menu-row:hover { background: var(--panel); }
.pl-menu-row.active .pl-name { color: var(--accent); font-weight: 600; }

.pl-name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  text-align: left;
  background: none;
  border: none;
  color: var(--fg);
  font-size: 0.88rem;
  padding: 0.4rem 0.5rem;
  cursor: pointer;
}

.pl-count { color: var(--muted); font-size: 0.78rem; }

.pl-tool {
  flex: none;
  background: none;
  border: none;
  border-radius: 0.3rem;
  color: var(--muted);
  padding: 0.3rem 0.35rem;
  cursor: pointer;
}

.pl-tool:hover { color: var(--fg); background: var(--bg); }

.pl-menu-footer {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  border-top: 1px solid var(--border);
  margin-top: 0.4rem;
  padding-top: 0.5rem;
}

.pl-menu-footer button {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 0.35rem;
  color: var(--fg);
  font-size: 0.82rem;
  padding: 0.3rem 0.6rem;
  cursor: pointer;
}

.pl-menu-footer button:hover { border-color: var(--muted); }

.pl-status {
  color: var(--muted);
  font-size: 0.78rem;
  padding: 0.35rem 0.5rem 0.1rem;
}

.pl-status:empty { display: none; }
.pl-status.error { color: #c96e6e; }

.pl-name-input {
  flex: 1;
  min-width: 0;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 0.3rem;
  color: var(--fg);
  font-size: 0.88rem;
  padding: 0.3rem 0.45rem;
}

/* Popover rows: checkbox = membership of the current tune (§11.2). */
.pl-check {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.35rem 0.5rem;
  border-radius: 0.3rem;
  cursor: pointer;
}

.pl-check:hover { background: var(--panel); }

.pl-check-name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Sidebar rows in playlist mode: reorder/remove tools; entries whose tune id
   left the corpus are greyed and not clickable (§11.1, §11.3). */
.tune-item .txt { flex: 1; }

.pl-row-tools {
  display: inline-flex;
  gap: 0.1rem;
  flex: none;
  margin-left: auto;
}

.pl-act {
  background: none;
  border: none;
  border-radius: 0.25rem;
  color: var(--muted);
  font-size: 0.8rem;
  padding: 0.1rem 0.25rem;
  cursor: pointer;
}

.pl-act:hover { color: var(--fg); background: var(--bg); }

.tune-item.missing { cursor: default; }
.tune-item.missing .t { color: var(--muted); font-size: 0.82rem; word-break: break-all; }
.tune-item.missing .c { font-style: italic; }

/* ------------------------------------------------------------ responsive */

/* Below the 860px seam the two panes become a single-screen switch: the list
   pane fills the screen; opening a tune slides the detail pane in over it via
   body.show-detail. Sheets anchor to the bottom of the screen instead of the
   top-right. Keep the 860 breakpoint in sync with narrowMq in app.js. */
@media (max-width: 859px) {
  /* The rail becomes a bottom tab bar: the shell stacks instead of splitting. */
  .app-shell { flex-direction: column; }
  .rail { display: none; }
  .bottombar { display: flex; }
  /* …except over an open chart. Browse's list, Find, Playlists and Settings are
     screens you navigate FROM, so they keep the tab bar; a chart is a screen you
     read, and its 60px are better spent on chords. The ‹ in the title row is the
     way back to the list, and from there the tab bar is there again. */
  body.show-detail.view-browse .bottombar { display: none; }

  #listPane {
    width: 100%;
    border-right: none;
  }
  body.list-collapsed #listPane { width: 100%; } /* desktop collapse is inert here */

  /* Detail pane rides over the list; body.show-detail reveals it. */
  #detailPane {
    position: absolute;
    inset: 0;
    z-index: 30;
    background: var(--bg);
    transform: translateX(100%);
    transition: transform 0.18s ease;
  }
  body.show-detail #detailPane { transform: translateX(0); }

  #sidebarOpenBtn { display: none !important; } /* no desktop collapse on phones */
  #listToggle { display: none; } /* list is full-screen; nothing to collapse */
  body.show-detail #backBtn { display: inline-flex; } /* back to the list */

  /* The pane-is-the-scroller model (header scrolls away, footer pinned) now lives
     in the base rules and applies at every width; phones only tighten padding. */

  /* One panel shows at a time on phones, so the desktop alignment spacer would
     just leave an empty bar above the melody — drop it. */
  .panel-tools.tools-spacer { display: none; }

  #tuneView { padding: 0.55rem 0.4rem 1.4rem; }
  /* Tight left gutter so the grid runs nearly edge-to-edge; just wide enough to
     hold the first row's (narrow condensed) time signature. The section badge and
     timesig ride in this gutter, their offsets trimmed to match. */
  .grid { padding-left: 0.5em; }
  .sec-label { margin-left: -0.5em; }
  .timesig { left: -0.42em; }

  /* Sheets become bottom sheets. */
  .sheet-panel {
    top: auto;
    left: 0;
    right: 0;
    bottom: 0;
    width: auto;
    max-height: 80%;
    border-radius: 1.1rem 1.1rem 0 0;
    border-bottom: none;
    box-shadow: 0 -12px 40px rgba(0, 0, 0, 0.35);
  }
  .sheet-grip {
    display: block;
    width: 2.2rem;
    height: 0.25rem;
    border-radius: 0.15rem;
    background: var(--border);
    margin: 0 auto 0.8rem;
  }

  /* Chords bar: same three groups, thumb-sized. Every control on it is 44px
     tall — the bar is the one strip you hit while holding the instrument. */
  .chords-bar { padding: 0.3rem 0.5rem; }
  #gridZoom button,
  .cb-btn,
  .footer-nav .step-btn { width: 2.75rem; height: 2.75rem; }
  .footer-nav .step-btn { width: 2rem; }
  .chords-bar .key-select { height: 2.75rem; font-size: 0.88rem; }
  /* (The ♪/⋯ menus' own phone rules live with the menu CSS further down — they
     have to come after the desktop card they override.) */
}

/* ───────────────────────── Similar tunes & comparison (spec §8.2/§8.3) ── */

/* Harmonic filter dropdowns in the top bar (§8.2a/§8.2b). */
#startsOnFilter,
#keyFilter,
#formFilter,
#styleFilter,
#yearFilter {
  flex: none;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 0.4rem;
  color: var(--fg);
  font-size: 0.85rem;
  padding: 0.3rem 0.4rem;
  max-width: 10rem;
}
#startsOnFilter.on,
#keyFilter.on,
#formFilter.on,
#styleFilter.on,
#yearFilter.on { border-color: var(--accent); color: var(--accent-ink); }
#formFilter { max-width: 12rem; }

/* Tag filter (§8.2b): button opens a checkbox dropdown; checks AND together. */
#tagFilterBtn {
  flex: none;
  background: none;
  border: 1px solid var(--border);
  border-radius: 0.4rem;
  color: var(--fg);
  font-size: 0.85rem;
  padding: 0.3rem 0.55rem;
  cursor: pointer;
  white-space: nowrap;
}
#tagFilterBtn.on { border-color: var(--accent); color: var(--accent-ink); }

/* "Clear all filters": a full-width reset at the bottom of the Filters sheet,
   disabled (and dimmed) while no filter is engaged. */
.clear-filters-btn {
  width: 100%;
  background: none;
  border: 1px solid var(--border);
  border-radius: 0.4rem;
  color: var(--fg);
  font-size: 0.9rem;
  padding: 0.45rem;
  cursor: pointer;
}
.clear-filters-btn:hover:not(:disabled) { border-color: var(--accent); color: var(--accent-ink); }
.clear-filters-btn:disabled { opacity: 0.45; cursor: default; }

#tagFilterMenu {
  position: fixed;
  z-index: 60;
  min-width: 16rem;
  max-height: min(70vh, 28rem);
  overflow-y: auto;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 0.5rem;
  box-shadow: 0 8px 28px rgba(0, 0, 0, 0.35);
  padding: 0.4rem;
  font-size: 0.9rem;
}
#tagFilterMenu[hidden] { display: none; }
#tagFilterMenu.sheet {
  left: 0;
  right: 0;
  bottom: 0;
  top: auto;
  border-radius: 0.8rem 0.8rem 0 0;
}

.tag-hint {
  color: var(--muted);
  font-size: 0.78rem;
  padding: 0.2rem 0.4rem 0.4rem;
}

.tag-row {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.22rem 0.4rem;
  border-radius: 0.35rem;
  cursor: pointer;
}
.tag-row:hover { background: var(--chip-bg); }
.tag-row input { accent-color: var(--accent); }
.tag-name { flex: 1; }
.tag-count { color: var(--muted); font-size: 0.8rem; }

/* ------------------------------------------- the ♪ and ⋯ menus (player.js)

   Both open from the chords bar as popovers anchored above it, over a scrim:
   a card at the right end on desktop, a full-width sheet on phones. The bar
   itself is the containing block (it is sticky, so positioned), which is what
   keeps a menu attached to it while the chart scrolls underneath. */

#playScrim {
  position: fixed;
  inset: 0;
  z-index: 44;
  background: rgba(0, 0, 0, 0.2);
}
#playScrim[hidden] { display: none; }

.bar-menu {
  position: absolute;
  z-index: 46;
  bottom: 100%;
  right: 0.9rem;
  width: 23rem;
  margin-bottom: 0.4rem;
  /* Capped and scrolling, so a menu can never grow past a short viewport. */
  max-height: min(78vh, 34rem);
  overflow-y: auto;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 0.9rem;
  padding: 0.8rem 0.9rem 1rem;
  box-shadow: 0 18px 46px rgba(0, 0, 0, 0.6);
  animation: pm-rise 0.14s ease;
}
.bar-menu[hidden] { display: none; }

@keyframes pm-rise {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .bar-menu { animation: none; }
}

/* Title row: name, live position (♪ only), ✕. */
.pm-top {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin-bottom: 0.55rem;
}
.pm-title {
  font-family: var(--font-cond);
  font-weight: 700;
  font-size: 1.15rem;
  line-height: 1;
}
.pm-close {
  flex: none;
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.8rem;
  height: 1.8rem;
  padding: 0;
  background: none;
  border: 1px solid var(--border);
  border-radius: 0.45rem;
  color: var(--muted);
  font-size: 0.85rem;
  line-height: 1;
  cursor: pointer;
}

/* ---- the player menu.

   Everything in here lines up in vertical columns, and that is the point of
   the layout rather than a nicety: the menu is read at a glance between
   choruses, so a hand must be able to travel straight down a column and hit
   the same control in every row. One row grammar does it — a fixed name
   column, a fixed pair of stepper buttons around a stretchy middle, and a
   fixed switch at the end — so nothing is ever hand-tuned per row, which is
   how the columns stay honest when a label changes. */
.bar-menu {
  --pm-name: 3.6rem; /* every source name, and every caption in the block */
  --pm-step: 1.65rem; /* − and +, and ◂ and ▸, which sit under them */
  --pm-sw: 2.5rem; /* the switch — the one right edge everything ends on */
  --pm-gap: 0.45rem;
}

/* ---- the transport, at the top of the sheet on a phone (on a desktop it is
   on the chords bar itself). */

.pm-transport {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 0.7rem;
}
.pm-transport-tail {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
}
.pm-mini {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.25rem;
  height: 2.25rem;
  padding: 0;
  background: none;
  border: 1px solid var(--border);
  border-radius: 0.5rem;
  color: var(--fg);
  cursor: pointer;
}
/* ▶ is the button this menu exists for, so it is the one thing in it drawn
   filled and at thumb size — everything else is an outline. */
.pm-play {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 3.1rem;
  height: 3.1rem;
  padding: 0;
  background: var(--accent);
  border: 1px solid var(--accent);
  border-radius: 50%;
  color: #fff;
  font-size: 1.25rem;
  line-height: 1;
  cursor: pointer;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.22);
}
/* The glyph is a triangle with air on its left; nudge it back onto the centre. */
.pm-play:not(.playing) { padding-left: 0.16rem; }
.pm-play.playing { font-size: 1.05rem; }
.pm-play:hover { filter: brightness(1.08); }
.pm-play:disabled { opacity: 0.5; cursor: progress; box-shadow: none; }

/* ---- one question per line: caption on the left, answer to the right. */

.pm-line {
  display: grid;
  grid-template-columns: 4.4rem 1fr;
  gap: 0.6rem;
  align-items: center;
  padding: 0.4rem 0;
}
.pm-line-lab {
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
  line-height: 1.15;
}
.pm-line > :last-child { min-width: 0; }
/* The mixer and the sound block are their own blocks under the questions: a
   solid rule opens each, a dotted one divides it. */
.pm-line.rule { border-top: 1px solid var(--border); margin-top: 0.35rem; }
.pm-line.rule-dot { border-top: 1px dotted var(--border); }
.pm-line.tall { align-items: start; }
.pm-line.tall > .pm-line-lab { padding-top: 0.7rem; }

/* Tempo: the stepper first, so it shares its column with Choruses' — the two
   numbers you nudge most often, one above the other. */
.pm-row {
  display: flex;
  align-items: center;
  gap: var(--pm-gap);
}
.pm-slider {
  flex: 1;
  min-width: 0;
  accent-color: var(--accent-ink);
}
.pm-plain {
  flex: none;
  background: none;
  border: 1px solid var(--border);
  border-radius: 0.4rem;
  color: var(--fg);
  font: inherit;
  font-size: 0.82rem;
  padding: 0.28rem 0.7rem;
  cursor: pointer;
  white-space: nowrap;
}

/* − value + in one box: Tempo's and Choruses' are the same box, so they share
   both edges. */
.pm-num {
  flex: none;
  /* Choruses puts one straight into the answer column, where a grid child
     stretches by default — and a Choruses stepper three times the width of
     the Tempo one directly above it is the alignment this menu is for. */
  justify-self: start;
  display: inline-grid;
  grid-template-columns: 1.6rem 2.9rem 1.6rem;
  align-items: stretch;
  border: 1px solid var(--border);
  border-radius: 0.4rem;
  overflow: hidden;
}
.pm-num-btn {
  padding: 0;
  height: 1.8rem;
  background: none;
  border: none;
  color: var(--fg);
  font: inherit;
  font-size: 1rem;
  line-height: 1;
  cursor: pointer;
}
.pm-num-btn:first-child { border-right: 1px solid var(--border); }
.pm-num-btn:last-child { border-left: 1px solid var(--border); }
.pm-num-val {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 0.88rem;
  font-variant-numeric: tabular-nums;
}

/* ---- the loop chips */

.pm-chips {
  display: flex;
  gap: 0.35rem;
  flex-wrap: wrap;
}
.pm-chip {
  background: none;
  border: 1px solid var(--border);
  border-radius: 999px;
  color: var(--fg);
  font: inherit;
  font-size: 0.78rem;
  padding: 0.22rem 0.6rem;
  cursor: pointer;
  white-space: nowrap;
}
.pm-chip.on {
  background: var(--chip-bg);
  border-color: var(--accent);
  color: var(--accent-ink);
}

/* ---- the mixer: four identical rows, name · − · level · + · switch. */

.pm-mix {
  display: grid;
  grid-template-columns: var(--pm-name) var(--pm-step) 1fr var(--pm-step) var(--pm-sw);
  gap: var(--pm-gap);
  align-items: center;
  padding: 0.4rem 0;
}
.pm-mixer > .pm-mix + .pm-mix { border-top: 1px dotted var(--border); }
.pm-mix-lab {
  display: flex;
  flex-direction: column;
  min-width: 0;
  font-size: 0.86rem;
  line-height: 1.15;
}
.pm-note { font-size: 0.66rem; color: var(--muted); }
/* A source switched off dims what it would have done, and keeps its row. */
.pm-mix.off > .pm-mix-lab,
.pm-mix.off > .pm-level { opacity: 0.45; }
.pm-step {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--pm-step);
  height: var(--pm-step);
  padding: 0;
  background: none;
  border: 1px solid var(--border);
  border-radius: 0.4rem;
  color: var(--fg);
  font: inherit;
  font-size: 0.9rem;
  line-height: 1;
  cursor: pointer;
}
.pm-level {
  width: 100%;
  min-width: 0;
  accent-color: var(--accent-ink);
}

/* The last lit switch, tapped: it holds, and shows that it is holding. */
@keyframes pm-nudge {
  0%, 100% { transform: none; }
  25% { transform: translateX(3px); }
  75% { transform: translateX(-3px); }
}
.pm-mix.nudge { animation: pm-nudge 0.18s ease; }
@media (prefers-reduced-motion: reduce) {
  .pm-mix.nudge { animation: none; }
}

/* ---- the sound block: the feel, then what the two sources are doing. */

.pm-feel { padding-bottom: 0.5rem; }
.pm-feel-lab {
  grid-column: 1 / 5;
  font-size: 0.86rem;
  white-space: nowrap;
}
/* Shaded, because these are details of the two switches above rather than
   sources of their own. It runs to the same right edge as the switches, and
   its right padding is exactly a switch wide — so ◂ and ▸ land under − and +. */
.pm-picks {
  background: var(--chip-bg);
  border-radius: 0.55rem;
  padding: 0.35rem 0 0.35rem 0.55rem;
  padding-right: calc(var(--pm-sw) + var(--pm-gap));
}
.pm-pick {
  display: grid;
  grid-template-columns:
    calc(var(--pm-name) - 0.55rem) var(--pm-step) 1fr var(--pm-step);
  gap: var(--pm-gap);
  align-items: center;
  padding: 0.3rem 0;
}
.pm-pick + .pm-pick { border-top: 1px dotted var(--border); }
.pm-pick-lab {
  font-size: 0.72rem;
  color: var(--muted);
  white-space: nowrap;
}
.pm-arrow {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--pm-step);
  height: var(--pm-step);
  padding: 0;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 0.4rem;
  color: var(--fg);
  font: inherit;
  font-size: 0.72rem;
  line-height: 1;
  cursor: pointer;
}
.pm-pick-val {
  min-width: 0;
  text-align: center;
  /* The longest name ("On the changes") has to fit between ◂ and ▸, and that
     gap is fixed by the columns above — so it is the type that gives.
     test_player.py measures the text against the box it is in. */
  font-size: 0.68rem;
  color: var(--accent-ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ---- ⋯ rows: the verse switch and add-to-playlist */

.pm-switch {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.5rem 0 0.45rem;
}
.pm-switch-label { display: flex; flex-direction: column; font-size: 0.9rem; }
.pm-hint { font-size: 0.72rem; color: var(--muted); }

.pm-dashed {
  width: 100%;
  text-align: left;
  background: none;
  border: 1px dashed var(--border);
  border-radius: 0.6rem;
  color: var(--fg);
  font: inherit;
  font-size: 0.86rem;
  padding: 0.5rem 0.6rem;
  margin-top: 0.4rem;
  cursor: pointer;
}
.pm-dashed:hover { border-color: var(--muted); }

/* Phones: the menus become full-width sheets over a heavier scrim — while one
   is open it IS the screen. (Below the same 860px seam as the rest of the app;
   these have to sit after the desktop card above to override it.) */
@media (max-width: 859px) {
  #playScrim { background: rgba(0, 0, 0, 0.45); }
  .bar-menu {
    left: 0.4rem;
    right: 0.4rem;
    width: auto;
    /* Tall enough for the whole player on a phone-sized screen, and still
       bounded by the viewport on a short one. */
    max-height: min(76vh, 34rem);
    --pm-name: 3.9rem;
    --pm-step: 1.9rem;
  }
  /* The caption goes on its own line above its controls: a 4.4rem label
     column is a fifth of a 390px screen, and the answers are what you came
     for. The insets stay equal on both sides — nothing is inset "a bit more"
     because a slider happened to cap out. */
  .pm-line {
    display: block;
    padding: 0.35rem 0;
  }
  .pm-line-lab { display: block; margin-bottom: 0.3rem; }
  .pm-line.tall > .pm-line-lab { padding-top: 0; }
  /* Slider · Tap · stepper: the hand is on the slider, and the stepper is
     the fine adjustment you reach for after it. */
  .pm-row > .pm-num { order: 3; }
  .pm-row > .pm-slider { order: 1; }
  .pm-row > .pm-plain { order: 2; }
  /* Room for a thumb on everything you touch mid-take. */
  .pm-chip { padding: 0.4rem 0.7rem; }
  .pm-mix { padding: 0.3rem 0; }
  .pm-num-btn { height: 2rem; }
}

/* Playhead: the sounding bar is outlined in both chord layouts, with the
   cursor underlining the current beat's column span. */
.bar.play-now,
.bx.play-now {
  position: relative;
  box-shadow: inset 0 0 0 2px var(--accent);
  border-radius: 2px;
}
.play-cursor {
  position: absolute;
  left: 0;
  bottom: 1px;
  height: 3px;
  background: var(--accent);
  border-radius: 2px;
  opacity: 0.85;
  pointer-events: none;
  transition: left 80ms linear, width 80ms linear;
}

/* The loop, bracketed across the bars it covers in whichever chord layout is
   up — the same span the amber band marks on the ♪ menu's form strip. Shown
   here only: the chart is for reading, and the loop is set on that strip. */
.bar.loop-in,
.bx.loop-in {
  position: relative;
  background-image: linear-gradient(rgba(216, 161, 63, 0.14), rgba(216, 161, 63, 0.14));
  box-shadow: inset 0 2px 0 var(--loop), inset 0 -2px 0 var(--loop);
}
.bar.loop-first,
.bx.loop-first { box-shadow: inset 0 2px 0 var(--loop), inset 0 -2px 0 var(--loop), inset 2px 0 0 var(--loop); }
.bar.loop-last,
.bx.loop-last { box-shadow: inset 0 2px 0 var(--loop), inset 0 -2px 0 var(--loop), inset -2px 0 0 var(--loop); }
.bar.loop-first.loop-last,
.bx.loop-first.loop-last { box-shadow: inset 0 0 0 2px var(--loop); }
/* The sounding bar still wins: the loop is where you are working, the playhead
   is where the music is. */
.bar.play-now.loop-in,
.bx.play-now.loop-in {
  box-shadow: inset 0 0 0 2px var(--accent), inset 0 3px 0 var(--loop), inset 0 -3px 0 var(--loop);
}

/* Narrow screens (keep in sync with narrowMq): shorter form dropdown so the
   wrapped filter row packs tighter. */
@media (max-width: 700px), (max-height: 500px), (max-width: 899px) and (orientation: portrait) {
  #formFilter { max-width: 8rem; }
}

/* Opening-degree chip next to the key chip. */
.harm-chip.opening { background: var(--chip-bg); }
.harm-chip.opening .harm-label { color: var(--muted); }

/* Suggestion buttons in the panel bar, tinted by the best match's quality
   band (same palette as the score meters). */
.suggest-btn {
  background: none;
  border: 1px solid var(--border);
  border-radius: 0.4rem;
  color: var(--fg);
  font-size: 0.85rem;
  padding: 0.25rem 0.6rem;
  cursor: pointer;
}
.suggest-btn.q-high { border-color: #2eb872; background: color-mix(in srgb, #2eb872 18%, transparent); }
.suggest-btn.q-good { border-color: #8fc63f; background: color-mix(in srgb, #8fc63f 18%, transparent); }
.suggest-btn.q-fair { border-color: #e8a33d; background: color-mix(in srgb, #e8a33d 18%, transparent); }
.suggest-btn.q-low  { border-color: #9aa0a6; background: color-mix(in srgb, #9aa0a6 15%, transparent); }
.suggest-btn.on { box-shadow: 0 0 0 2px var(--accent); }

/* Suggestions panel. */
.suggest-panel {
  margin: 0.6rem auto 0.8rem;
  max-width: 44rem;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 0.5rem;
  padding: 0.6rem 0.8rem;
}
.suggest-group {
  border: 1px solid var(--border);
  border-radius: 0.5rem;
  background: var(--chip-bg);
  padding: 0.5rem 0.7rem 0.6rem;
}
.suggest-group + .suggest-group { margin-top: 0.7rem; }
.suggest-group-head {
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
  flex-wrap: wrap;
  margin: 0 0 0.45rem;
  padding-bottom: 0.35rem;
  border-bottom: 1px solid var(--border);
}
.sg-title {
  font-size: 0.75rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--fg);
}
.sg-hint { font-size: 0.75rem; color: var(--muted); }
/* ~5 rows visible; longer lists scroll. */
.suggest-list {
  max-height: 10.4rem;
  overflow-y: auto;
}
.suggest-row {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  border-bottom: 1px solid var(--border);
  color: var(--fg);
  font: inherit;
  font-size: 0.95rem;
  padding: 0.3rem 0.1rem;
  cursor: pointer;
}
.suggest-row:hover .sim-title { color: var(--accent-ink); }
.suggest-row:last-of-type { border-bottom: none; }

/* Score as a quality meter: colored fill by band, number overlaid. */
.sim-score {
  flex: none;
  position: relative;
  display: inline-block;
  width: 3rem;
  height: 1.05rem;
  border-radius: 0.55rem;
  border: 1px solid var(--border);
  background: var(--panel);
  overflow: hidden;
  vertical-align: -0.2rem;
}
.sim-score i {
  position: absolute;
  inset: 0 auto 0 0;
  border-radius: inherit;
}
.sim-score b {
  position: relative;
  display: block;
  text-align: center;
  font-size: 0.7rem;
  font-weight: 700;
  line-height: 1.05rem;
  font-variant-numeric: tabular-nums;
  color: var(--fg);
}
.sim-score.q-high i { background: color-mix(in srgb, #2eb872 45%, transparent); }
.sim-score.q-good i { background: color-mix(in srgb, #8fc63f 45%, transparent); }
.sim-score.q-fair i { background: color-mix(in srgb, #e8a33d 45%, transparent); }
.sim-score.q-low i  { background: color-mix(in srgb, #9aa0a6 40%, transparent); }

.sim-title { flex: 1; min-width: 0; }
.sim-family {
  color: var(--muted);
  font-size: 0.75rem;
  max-width: 11rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Comparison view. */
.compare { margin-top: 0.6rem; }
.cmp-bar {
  display: flex;
  align-items: center;
  gap: 0.8rem;
  flex-wrap: wrap;
  margin-bottom: 0.8rem;
}
.cmp-back, .cmp-mode {
  background: none;
  border: 1px solid var(--border);
  border-radius: 0.4rem;
  color: var(--fg);
  font: inherit;
  font-size: 0.85rem;
  padding: 0.25rem 0.7rem;
  cursor: pointer;
}
.cmp-mode.active { border-color: var(--accent); color: var(--accent-ink); }
.cmp-mode:disabled { opacity: 0.4; cursor: default; }
.cmp-modes { display: flex; gap: 0.3rem; margin-left: auto; }
.cmp-label { font-size: 0.9rem; color: var(--muted); }
.cmp-panels { display: flex; gap: 1.2rem; align-items: flex-start; flex-wrap: wrap; }
.cmp-side { flex: 1 1 24rem; min-width: 20rem; }
.cmp-side-head {
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
  margin-bottom: 0.4rem;
}
.cmp-side-title { font-weight: 700; }
.cmp-side-key { color: var(--muted); font-size: 0.8rem; }
.cmp-side-note { color: var(--muted); font-size: 0.75rem; font-style: italic; }
/* The comparison grids size themselves (no fit pass): a fixed readable font. */
.grid.cmp-grid { font-size: 12px; max-width: none; }

/* Aligned-bar highlighting from the engine's bar mapping. No section shading in
   comparisons — the accent-tinted matched bars carry the meaning — so these must
   outweigh the `.section .bar:not(.empty)` background-color rule (incl. its dark
   override), hence the deliberately specific selectors. */
.cmp-grid .section .bar.sim-hl:not(.empty) {
  background-color: color-mix(in srgb, var(--accent) 22%, transparent);
}
.cmp-grid .section .bar.sim-hl-active:not(.empty) {
  background-color: color-mix(in srgb, var(--accent) 55%, transparent);
}

/* Roman-degree cells in the comparison's Degrees mode. */
.chord.degree { font-weight: 400; letter-spacing: 0.02em; font-size: 2.1em;}

