/* Grilles Displayer — themes, layout, chord grid, chord symbols. */

/* Saira Extra Condensed (Google Fonts), bundled locally so file:// works
   offline. Trial replacement for Barlow Condensed — the barlow-condensed-*.woff2
   files are kept in fonts/ so switching back is just a family-name swap. The
   music glyphs ♯ ♭ Δ fall back to the system font. */
@font-face {
  font-family: "Saira Extra Condensed";
  font-style: normal;
  font-weight: 400;
  src: url("fonts/saira-extra-condensed-400.woff2") format("woff2");
}

@font-face {
  font-family: "Saira Extra Condensed";
  font-style: normal;
  font-weight: 500;
  src: url("fonts/saira-extra-condensed-500.woff2") format("woff2");
}

@font-face {
  font-family: "Saira Extra Condensed";
  font-style: normal;
  font-weight: 700;
  src: url("fonts/saira-extra-condensed-700.woff2") format("woff2");
}

:root[data-theme="dark"] {
  --bg: #0b0b0d;
  --panel: #16161a;
  --fg: #f4f4f6;
  --muted: #9a9aa2;
  --line: #e8e8ec;
  --border: #2a2a30;
  --accent: #3d6ba3;
  --ok: #4cae5a; /* digitized-asset icons */
  --icon-off: #5c5c64; /* scan-only asset icons */
  --accent-ink: #86aede; /* key chip, legible on dark panel */
  --mod-ink: #d6a24c; /* section-key (modulation) chip */
  --chip-bg: #1e1e24;
}

:root[data-theme="light"] {
  --bg: #faf9f5;
  --panel: #efede6;
  --fg: #141414;
  --muted: #6b6b66;
  --line: #141414;
  --border: #d8d5ca;
  --accent: #9db8d6;
  --ok: #2e8540;
  --icon-off: #b0ada2;
  --accent-ink: #3d6ba3; /* key chip, legible on light panel */
  --mod-ink: #9a6b12; /* section-key (modulation) chip */
  --chip-bg: #e7e4db;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
}

body {
  background: var(--bg);
  color: var(--fg);
  /* UI chrome uses Inter where available, falling back to the platform sans.
     Titles and chord symbols use the bundled condensed face (Saira Extra
     Condensed on trial, formerly Barlow Condensed). */
  font-family: "Inter", system-ui, "Segoe UI", "Helvetica Neue", Arial, sans-serif;
}

/* ------------------------------------------------------------ app shell */

/* Two panes side by side: a fixed list pane (collapsible) and a detail pane
   that fills the rest. Below the 860px seam the two become a single-screen
   switch (list OR detail), driven by body.show-detail. */
.shell {
  display: flex;
  flex-direction: row;
  height: 100%;
  position: relative; /* anchors the sheets + backdrop */
  overflow: hidden;
}

#listPane {
  flex: none;
  width: 300px;
  min-width: 0;
  display: flex;
  flex-direction: column;
  border-right: 1px solid var(--border);
  overflow: hidden;
  transition: width 0.18s ease;
}

body.list-collapsed #listPane { width: 0; border-right: none; }

/* The whole pane is the scroller: the header + tabs ride at the top of the flow
   and scroll away with the chords (the title row is never pinned), while only
   the footer control bar stays stuck to the bottom of the viewport. */
#detailPane {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  overflow-y: auto;
  overflow-x: hidden;
  position: relative;
}

/* --------------------------------------------------- icon / pill buttons */

/* 36×36 square icon button with a 10px radius — the mockup's chrome control. */
.icon-btn {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  position: relative;
  width: 2.25rem;
  height: 2.25rem;
  background: none;
  border: 1px solid var(--border);
  border-radius: 0.6rem;
  color: var(--fg);
  font-size: 1.1rem;
  line-height: 1;
  cursor: pointer;
}

.icon-btn:hover { border-color: var(--muted); }
.icon-btn svg { width: 1.05rem; height: 1.05rem; }
.icon-btn.on { border-color: var(--accent); color: var(--accent-ink); }

/* Small count badge (active filters) top-right of the filters button. */
.badge {
  position: absolute;
  top: -0.35rem;
  right: -0.35rem;
  min-width: 1rem;
  height: 1rem;
  padding: 0 0.25rem;
  border-radius: 999px;
  background: var(--accent);
  color: #fff;
  font-size: 0.62rem;
  font-weight: 700;
  line-height: 1rem;
  text-align: center;
}
.badge[hidden] { display: none; }

/* Pill toggle (a switch) used in the Filters / Settings sheets. */
.toggle-btn {
  flex: none;
  width: 2.5rem;
  height: 1.5rem;
  padding: 0;
  border-radius: 0.75rem;
  border: 1px solid var(--border);
  background: var(--panel);
  cursor: pointer;
  position: relative;
}
.toggle-btn .knob {
  position: absolute;
  top: 1px;
  left: 1px;
  width: 1.2rem;
  height: 1.2rem;
  border-radius: 50%;
  background: var(--muted);
  transition: left 0.15s ease, background 0.15s ease;
}
.toggle-btn.on { background: var(--accent); border-color: var(--accent); }
.toggle-btn.on .knob { left: calc(100% - 1.3rem); background: #fff; }

/* -------------------------------------------------------- list pane head */

.list-head {
  flex: none;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  padding: 0.8rem 0.8rem 0.55rem;
}

.list-head-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
}

.brand-wrap { display: flex; align-items: center; gap: 0.5rem; min-width: 0; }
.list-head-actions { display: flex; gap: 0.5rem; flex: none; }

.appname {
  font-family: "Saira Extra Condensed", "Arial Narrow", "Helvetica Neue", Arial, sans-serif;
  font-weight: 700;
  font-size: 1.6rem;
  letter-spacing: 0.02em;
}

#search {
  width: 100%;
  padding: 0.55rem 0.7rem;
  border: 1px solid var(--border);
  border-radius: 0.6rem;
  background: var(--panel);
  color: var(--fg);
  font-size: 0.95rem;
}

#search:focus { outline: 1px solid var(--muted); }

/* Active-playlist chip in the list header. */
.active-pl-chip {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}
.active-pl-chip[hidden] { display: none; }

#playlistBtn {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  text-align: left;
  background: var(--chip-bg);
  border: 1px solid var(--accent-ink);
  border-radius: 999px;
  color: var(--accent-ink);
  font-size: 0.8rem;
  padding: 0.3rem 0.7rem;
  cursor: pointer;
}
#playlistBtn::before { content: "▶ "; }

#playlistClear {
  flex: none;
  background: none;
  border: none;
  color: var(--muted);
  font-size: 0.78rem;
  cursor: pointer;
}
#playlistClear:hover { color: var(--fg); }

#tuneList {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 0 0.35rem 1.4rem;
}

/* ---------------------------------------------------- detail pane head */

.detail-head {
  flex: none;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.6rem 0.6rem 0.4rem;
}

/* Fixed-width gutters on both sides keep the title centered whether or not a
   back / sidebar button is present. */
.dh-side {
  flex: none;
  width: 2.25rem;
  display: flex;
  align-items: center;
}

/* Back (mobile) and sidebar-open (desktop, collapsed) are shown by JS via
   body classes; hidden by default. */
#backBtn, #sidebarOpenBtn { display: none; font-size: 1.3rem; }
/* Sidebar-reopen button: wide screens only, when the list is collapsed. The
   back button is narrow-only (added in the responsive block). */
body.list-collapsed #sidebarOpenBtn { display: inline-flex; }

.detail-title {
  flex: 1;
  min-width: 0;
  text-align: center;
  display: flex;
  flex-direction: column;
  gap: 0.05rem;
}
.detail-title .dt-title {
  font-family: "Saira Extra Condensed", "Arial Narrow", "Helvetica Neue", Arial, sans-serif;
  font-weight: 700;
  font-size: 1.3rem;
  letter-spacing: 0.01em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.detail-title .dt-composer {
  font-size: 0.78rem;
  color: var(--muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Chords / Melody underline tabs (shown by JS only when both assets exist). */
.detail-tabs {
  flex: none;
  display: flex;
  gap: 0;
  padding: 0 0.8rem 0.15rem;
}
.detail-tabs[hidden] { display: none; }
.detail-tabs .tab {
  flex: 1;
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  color: var(--muted);
  font: inherit;
  font-size: 0.85rem;
  font-weight: 500;
  padding: 0.35rem 0;
  cursor: pointer;
}
.detail-tabs .tab.active {
  color: var(--fg);
  font-weight: 600;
  border-bottom-color: var(--accent);
}

#tuneView {
  /* Grow to fill the pane so a short tune still pushes the footer to the bottom,
     but let a tall tune overflow and drive the pane's scroll (not a nested one). */
  flex: 1 0 auto;
  min-height: 0;
  overflow-y: visible;
  padding: 0.6rem 1.4rem 2rem;
  position: relative;
}

#tunePane {
  min-width: 0;
  container-type: inline-size;
}

/* ------------------------------------------------------- detail footer */

/* Persistent control/summary bar at the foot of the detail pane: harmony
   chips + transpose + practice transport + playlist step buttons. */
#detailFooter {
  flex: none;
  position: sticky;
  bottom: 0;
  z-index: 20;
  background: var(--bg);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem 0.9rem;
  padding: 0.5rem 1rem 0.6rem;
  border-top: 1px solid var(--border);
}
#detailFooter[hidden] { display: none; }

/* The footer summary chips (key/tempo/family/starts-on) are retired: that info
   now lives in the collapsible Details/Tags sections and the Key is the centre
   dropdown below. The bottom bar is a clean three-zone control strip. */
#footerChips { display: none; }
.footer-chips .fchip {
  font-size: 0.75rem;
  color: var(--muted);
  background: var(--chip-bg);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 0.2rem 0.55rem;
  white-space: nowrap;
}
.footer-chips .fchip.key { color: var(--accent-ink); border-color: var(--accent-ink); }

/* Three zones: zoom (− +) pinned bottom-left, the Key transpose dropdown
   centred, the practice transport (play + tempo + playlist step) bottom-right. */
.footer-controls {
  flex: 1 1 100%;
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 0.5rem;
}
.fc-left { justify-self: start; display: flex; align-items: center; gap: 0.5rem; }
.fc-center { justify-self: center; display: flex; align-items: center; min-width: 0; }
.fc-right { justify-self: end; display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; justify-content: flex-end; }

#brushPlay {
  flex: none;
  width: 2.1rem;
  height: 2.1rem;
  background: none;
  border: 1px solid var(--border);
  border-radius: 0.5rem;
  color: var(--fg);
  cursor: pointer;
}
#brushPlay.on { border-color: var(--accent); color: var(--accent); }
#brushBtn {
  flex: none;
  background: none;
  border: 1px solid var(--border);
  border-radius: 0.5rem;
  color: var(--fg);
  font-size: 0.82rem;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  padding: 0.3rem 0.55rem;
  cursor: pointer;
}

.footer-nav { display: inline-flex; gap: 0.3rem; }
.footer-nav[hidden] { display: none; }

/* --------------------------------------------------- sheets & backdrop */

#sheetBackdrop {
  position: absolute;
  inset: 0;
  z-index: 45;
  background: rgba(0, 0, 0, 0.45);
}
#sheetBackdrop[hidden] { display: none; }

.sheet-panel {
  position: absolute;
  z-index: 50;
  background: var(--bg);
  border: 1px solid var(--border);
  overflow-y: auto;
  padding: 0.9rem 1.1rem 1.2rem;
  /* Desktop: floating card, top-right of the shell. */
  top: 0.9rem;
  right: 0.9rem;
  width: 23rem;
  max-height: 86%;
  border-radius: 1rem;
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.35);
}
.sheet-panel[hidden] { display: none; }

.sheet-grip { display: none; }

.sheet-title {
  font-family: "Saira Extra Condensed", "Arial Narrow", "Helvetica Neue", Arial, sans-serif;
  font-weight: 700;
  font-size: 1.2rem;
  margin-bottom: 0.8rem;
}

.sheet-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.8rem;
  padding: 0.5rem 0.1rem;
}
.sheet-row-label { font-size: 0.92rem; }

/* Divider between the current-tune options and the global settings. */
.sheet-divider {
  height: 1px;
  background: var(--border);
  margin: 0.9rem 0 0.2rem;
}

/* The current tune's title, heading its options block in the Settings sheet. */
.sheet-section-head {
  font-size: 1rem;
  font-weight: 700;
  text-transform: none;
  letter-spacing: 0;
  color: var(--fg);
  margin-bottom: 0.6rem;
}

.sheet-field { margin-top: 0.7rem; }
.sheet-field-label {
  display: block;
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--muted);
  margin-bottom: 0.35rem;
}
.sheet-field select {
  width: 100%;
  font: inherit;
  font-size: 0.9rem;
  color: var(--fg);
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 0.5rem;
  padding: 0.4rem 0.5rem;
}

.sheet-action {
  width: 100%;
  text-align: left;
  background: none;
  border: 1px dashed var(--border);
  border-radius: 0.6rem;
  color: var(--fg);
  font-size: 0.88rem;
  padding: 0.55rem 0.6rem;
  cursor: pointer;
}
.sheet-action:hover { border-color: var(--muted); }

/* Segmented control (theme picker, view picker). */
.seg-group { display: flex; gap: 0.4rem; }
.seg-btn {
  flex: 1;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 0.5rem;
  color: var(--fg);
  font: inherit;
  font-size: 0.85rem;
  padding: 0.45rem 0;
  cursor: pointer;
}
.seg-btn.active { background: var(--accent); border-color: var(--accent); color: #fff; }

/* Pill option buttons in the tune sheet (view / similar rows reuse existing). */
.opt-pill {
  background: none;
  border: 1px solid var(--border);
  border-radius: 999px;
  color: var(--fg);
  font: inherit;
  font-size: 0.82rem;
  padding: 0.35rem 0.75rem;
  cursor: pointer;
}
.opt-pill.active { border-color: var(--accent); color: var(--accent-ink); background: var(--chip-bg); }


/* --------------------------------------- content panels (chords/melody) */

/* Toolbar with the Chords / Melody switches, under the tune head (§5.4). */
.panel-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 0.6rem 1.4rem;
  margin: 0.2rem auto 1rem;
}

.switch {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  cursor: pointer;
  font-size: 0.9rem;
  user-select: none;
}

.switch input { position: absolute; opacity: 0; pointer-events: none; }

.switch .knob {
  width: 2.1rem;
  height: 1.15rem;
  border-radius: 0.6rem;
  background: var(--panel);
  border: 1px solid var(--border);
  position: relative;
  transition: background 0.15s ease;
  flex: none;
}

.switch .knob::after {
  content: "";
  position: absolute;
  top: 1px;
  left: 2px;
  width: 0.95rem;
  height: 0.95rem;
  border-radius: 50%;
  background: var(--muted);
  transition: transform 0.15s ease, background 0.15s ease;
}

.switch input:checked + .knob { background: var(--accent); border-color: var(--accent); }
.switch input:checked + .knob::after { transform: translateX(0.9rem); background: #fff; }
.switch input:focus-visible + .knob { outline: 2px solid var(--muted); }

/* Transpose key picker: a compact native <select> so it stays small and gets
   the OS picker on phones. Sits in the panel bar beside the Chords/Melody
   switches. */
.transpose {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  font-size: 0.9rem;
  user-select: none;
}

.key-select {
  font: inherit;
  font-size: 0.85rem;
  color: var(--fg);
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 0.5rem;
  padding: 0.15rem 0.4rem;
  cursor: pointer;
  max-width: 9.5rem;
}

.key-select:focus-visible { outline: 2px solid var(--muted); }

/* Panels stack vertically (mobile portrait); on wide screens two visible
   panels sit side by side — chords left, melody right (§5.4). */
.panels {
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
}

.panel {
  min-width: 0;
  position: relative;
  container-type: inline-size;
}

@media (min-width: 860px) {
  .panels.dual { flex-direction: row; align-items: flex-start; gap: 2rem; }
  .panels.dual .panel { flex: 1 1 50%; }

  /* A lone chord panel (no melody sheet) still occupies about half the
     container — the same footprint it has beside the melody — so the grid
     fills a readable half-width column instead of stranding the chords in a
     wide, sparse one. On phones/portrait (< 900px) panels stack full width.
     The book-layout view is the exception: its 8-boxes-per-row lattice gets
     the full pane (the lattice itself caps at 58em and centres). */
  .panels:not(.dual) .panel.chords { width: 50%; align-self: center; }
  .panels:not(.dual) .panel.chords.show-boxes:not(.show-scan) { width: 100%; }
  /* Melody hidden + the user zoomed in: let the lone chords grid spread to the
     full pane width instead of the readable half-column (set in JS by fitAll). */
  .panels:not(.dual) .panel.chords.zoom-wide { width: 100%; }
}

.panel img.scan {
  display: block;
  max-width: 100%;
  margin: 0 auto;
  border-radius: 2px;
  cursor: zoom-in;
}

/* Toggle on a panel with a rendered form (chord grid or abcjs lead sheet):
   rendered ⇄ original scan. Both live in the DOM; .show-scan flips which one
   is visible. The button sits in its own tools row above the content — never
   overlapping the scan — and its icon shows what it switches TO (photo ⇄
   grid/notes). */
.panel-tools {
  display: flex;
  justify-content: flex-end;
  margin-bottom: 0.35rem;
}

/* Empty stand-in matching a real tools row's height, so a toolless melody panel
   lines its top up with the chords panel's grid (see renderTune). */
.panel-tools.tools-spacer { min-height: 1.7rem; }

.panel .scan-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.1rem;
  height: 1.7rem;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 0.4rem;
  color: var(--fg);
  cursor: pointer;
  opacity: 0.9;
}

.panel .scan-toggle svg {
  width: 1rem;
  height: 1rem;
  fill: currentColor;
}

.panel .scan-toggle:hover { opacity: 1; }
.panel .scan-toggle.on { border-color: var(--accent); color: var(--accent); }

/* Debug aid: the crop's filename, in small print under the scan. */
.panel .scan-name {
  font-family: ui-monospace, Consolas, monospace;
  font-size: 0.62rem;
  color: var(--muted);
  text-align: center;
  margin-top: 0.2rem;
  word-break: break-all;
}

/* Only when the panel holds a rendered form does the scan start hidden. */
.panel.has-render img.scan,
.panel.has-render .scan-name { display: none; }
.panel.has-render.show-scan img.scan { display: block; }
.panel.has-render.show-scan .scan-name { display: block; }
.panel.show-scan .grid,
.panel.show-scan .variants,
.panel.show-scan .extras,
.panel.show-scan .abc-sheet { display: none; }

/* Book-layout view (.show-boxes): the 8-boxes-per-row lattice replaces the
   4-bar grid and its variants; the extras stay. Scan view wins over it. */
.panel .boxgrid { display: none; }
.panel.show-boxes:not(.show-scan) .boxgrid { display: block; }
.panel.show-boxes .grid,
.panel.show-boxes .variants { display: none; }

/* Segmented view switch (grid / book layout / scan) in the panel tools row. */
.view-seg { display: inline-flex; gap: 0.25rem; }

/* Harmonic-analysis controls (spelling switch + overlay toggle) sit on the LEFT
   of the tools row, aligned with the grid/book-layout content — margin-right:auto
   pushes them left while the view switch stays right. The spelling pills (C · C⁄ii
   · ii) are a one-hot group, kept close together; a gap sets the independent
   overlay (_¬) toggle apart. Hidden in scan view — they don't apply to the photo. */
.analysis-tools {
  display: inline-flex;
  align-items: center;
  margin-right: auto;
}
.analysis-tools .analysis-toggle { margin-left: 0.6rem; }
.panel.show-scan .analysis-tools { display: none; }

/* Desktop melody show/hide toggle (renderTune): a small pill in a panel's tools
   row. Hidden on phones — the Chords/Melody tabs handle switching there. The
   "hide" button rides on the melody panel (top-right, over the sheet/png); the
   "show" button rides on the chords tools row and only appears once the melody
   has been hidden (.panels.melody-off), when the lone chords panel centres. */
.melody-toggle { display: none; }
.melody-toggle svg { width: 0.95rem; height: 0.95rem; fill: currentColor; }
@media (min-width: 860px) {
  .melody-toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    background: var(--panel);
    border: 1px solid var(--border);
    border-radius: 0.4rem;
    color: var(--muted);
    font-size: 0.72rem;
    line-height: 1.2;
    padding: 0.2rem 0.5rem;
    cursor: pointer;
    white-space: nowrap;
  }
  .melody-toggle:hover { color: var(--fg); border-color: var(--accent); }
  .melody-toggle.show { display: none; }
  .panels.melody-off .melody-toggle.show { display: inline-flex; }
}

/* ------------------------------------------ melody lead sheet (abcjs) */

/* abcjs draws with fill/stroke: currentColor, so the sheet follows the
   theme through the CSS color property. */
.abc-sheet {
  color: var(--fg);
  max-width: 52em;
  margin: 0 auto;
}

.abc-sheet svg { max-width: 100%; height: auto; }

.abc-error {
  color: #c96e6e;
  font-size: 0.85rem;
  margin: 0.5rem 0;
}

/* ------------------------------------------- fullscreen scan (overlay) */

/* Click on the image toggles .magnified; click on the backdrop / ✕ closes. */
#scanOverlay {
  display: flex;
  align-items: center;
  justify-content: center;
  position: fixed;
  inset: 0;
  z-index: 50;
  background: rgba(0, 0, 0, 0.82);
  padding: 1rem;
  cursor: zoom-out;
}

#scanOverlay[hidden] { display: none; }

#scanOverlay img {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  cursor: zoom-in;
  background: #fff;
}

#overlayClose {
  position: fixed;
  top: 0.6rem;
  right: 0.9rem;
  z-index: 51;
  width: 2.2rem;
  height: 2.2rem;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.5);
  border: 1px solid rgba(255, 255, 255, 0.5);
  color: #fff;
  font-size: 1rem;
  line-height: 1;
  cursor: pointer;
}

/* Magnified: image at up to natural size (capped at 2.5× the viewport on
   small screens), panned by scrolling. */
#scanOverlay.magnified {
  overflow: auto;
  align-items: flex-start;
  justify-content: flex-start;
  padding: 0;
}

#scanOverlay.magnified img {
  max-width: 250vw;
  max-height: none;
  object-fit: initial;
  cursor: zoom-out;
}

/* --------------------------------------------------- grid zoom controls */

/* Grid zoom lives in the detail footer's control row. */
#gridZoom {
  display: inline-flex;
  gap: 0.35rem;
}

#gridZoom button {
  width: 1.9rem;
  height: 1.9rem;
  border-radius: 50%;
  background: var(--panel);
  color: var(--fg);
  border: 1px solid var(--border);
  font-size: 1.1rem;
  line-height: 1;
  cursor: pointer;
  opacity: 0.85;
}

#gridZoom button:hover { opacity: 1; }

/* ----------------------------------------------------------- tune list */

.tune-item {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.5rem 0.55rem;
  margin: 0.1rem 0;
  border-radius: 0.7rem;
  cursor: pointer;
  border: 1px solid transparent;
}

.tune-item:hover { background: var(--panel); }
.tune-item.active { background: var(--panel); }
.tune-item.current { background: var(--chip-bg); border-color: var(--accent); }

/* Key chip on the right of each row. */
.tune-item .row-key {
  flex: none;
  font-size: 0.72rem;
  color: var(--accent-ink);
  background: var(--chip-bg);
  border-radius: 0.5rem;
  padding: 0.15rem 0.45rem;
  white-space: nowrap;
}

/* Availability icons (§5.2): two fixed slots — chord grille | melody — so
   the columns stay aligned; gray = scan only, green = digitized. */
.tune-item .icons {
  display: inline-flex;
  gap: 0.25rem;
  flex: none;
}

.tune-item .icon {
  width: 0.8rem;
  height: 0.8rem;
  display: inline-block;
}

.tune-item .icon svg {
  width: 100%;
  height: 100%;
  display: block;
  fill: var(--icon-off);
}

.tune-item .icon.ok svg { fill: var(--ok); }

.tune-item .txt { min-width: 0; }
.tune-item .t { display: block; font-size: 0.92rem; }
.tune-item .c { display: block; font-size: 0.78rem; color: var(--muted); }

.list-empty {
  padding: 1rem;
  color: var(--muted);
  font-size: 0.9rem;
}

/* --------------------------------------------------- harmonic chips */

/* Chip rows for the collapsible "Tags" section (opening degree + section-key
   modulations, fingerprint family + tags). The old always-visible summary
   block that centred these below the grid has been retired. */
.harm-row {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.4rem;
}

.harm-chip {
  display: inline-flex;
  align-items: baseline;
  gap: 0.35rem;
  padding: 0.16rem 0.55rem;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--chip-bg);
  font-size: 0.78rem;
  line-height: 1.3;
  white-space: nowrap;
}

.harm-chip .harm-label {
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}

.harm-chip .harm-val { color: var(--fg); }

/* Main key: the headline chip — accent border + accent label. */
.harm-chip.key {
  border-color: var(--accent-ink);
}

.harm-chip.key .harm-label { color: var(--accent-ink); }
.harm-chip.key .harm-val { font-weight: 600; }

/* Section key = a per-section modulation; amber to set it apart. */
.harm-chip.section {
  border-color: var(--mod-ink);
}

.harm-chip.section .harm-label { color: var(--mod-ink); }

/* Fingerprint family + tags: quieter, second row. */
.harm-tags .harm-chip {
  font-size: 0.72rem;
  padding: 0.13rem 0.5rem;
  background: transparent;
  color: var(--muted);
}

.harm-chip.family {
  border-style: dashed;
  font-weight: 600;
}

.harm-chip.family .harm-val { color: var(--fg); }
.harm-chip.tag .harm-val { color: var(--muted); }

/* Per-section prose analysis inside the collapsible extras. */
.harm-sections {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.3rem 0.7rem;
  margin: 0.5rem 0 0.25rem;
}

.harm-sections dt {
  font-weight: 700;
  color: var(--fg);
  font-family: "Saira Extra Condensed", "Arial Narrow", "Helvetica Neue", Arial, sans-serif;
}

.harm-sections dd { margin: 0; }

.harm-modnote { color: var(--fg); }

/* ---------------------------------------------------------- chord grid */

/* All grid dimensions are em-based so the JS fit pass can scale the whole grid
   to the viewport height by adjusting this one font-size. The font grows with
   the panel width (2.6cqw) up to a 15px ceiling, so the chords stay a readable
   lead-sheet size instead of ballooning on a wide screen — a sparse tune (one
   chord per bar, e.g. Annie Laurie) needs no more than a busy one (Ask Me Now)
   to read, and a smaller grid also fits the page better. The grid fills its
   panel until the ceiling is hit, then stops widening: 38.5em is the width at
   which 2.6cqw reaches the ceiling (1 / 0.026 ≈ 38.5em, independent of the px
   value), so below the ceiling max-width never binds (the grid fills the panel)
   and at/above it the grid caps there (~15px × 38.5em ≈ 577px), centered.
   Chords never shrink to fit their slots, so fitGridWidth shrinks the font only
   if a busy bar would still collide at the available width. */
.grid {
  max-width: 38.5em;
  margin: 0 auto;
  padding-left: 1.4em;
  /* Container-relative (the .panel is an inline-size container): the grid
     shrinks when the chord panel shares the row with the melody panel. */
  font-size: clamp(10px, 2.6cqw, 15px);
}

/* Form badge on the first section's label row — section letter left, form
   badge right — mirroring the book layout's .bx-form. Sits inside .grid so it
   scales with the fitted grid font. */
.sec-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
}
/* Equal bottom margins so the two badges' borders sit on the same line and the
   row keeps the section label's usual 0.3em gap to the grid below. */
.sec-head .grid-form { margin-bottom: 0.3em; }
.grid-form {
  border: 0.1em solid var(--line);
  font-family: "Saira Extra Condensed", "Arial Narrow", "Helvetica Neue", Arial, sans-serif;
  font-size: 1.1em;
  line-height: 1;
  letter-spacing: 0.1em;
  padding: 0.12em 0.5em;
  margin-bottom: 0.4em;
  text-transform: uppercase;
}

.section { margin-bottom: 0.0em; }

.sec-label {
  display: inline-block;
  background: var(--fg);
  color: var(--bg);
  font-family: "Saira Extra Condensed", "Arial Narrow", "Helvetica Neue", Arial, sans-serif;
  font-weight: 400;
  font-size: 1.0em;
  line-height: 1;
  padding: 0.1em 0.2em;
  margin-bottom: 0.3em;
  margin-left: -1.0em; /* nudge the badge left, closer to the barline gutter */
}

/* Grid label row: strain name ("Impro") and section letter ("A") as two
   separate texts — the strain reads as lighter context, the letter as the
   filled badge. Plain chorus letters render the badge alone (no .sec-labels). */
.sec-labels {
  display: flex;
  align-items: baseline;
  gap: 0.35em;
  margin-left: -1.0em; /* match the lone badge's gutter nudge */
}
.sec-labels .sec-label { margin-left: 0; }
.sec-strain {
  font-family: "Saira Extra Condensed", "Arial Narrow", "Helvetica Neue", Arial, sans-serif;
  font-weight: 700;
  font-size: 1.0em;
  line-height: 1;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  opacity: 0.7;
}

.row {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  position: relative;
  margin-bottom: 0.7em;
}

.timesig {
  position: absolute;
  left: -0.6em;
  top: 50%;
  transform: translateY(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  font-family: "Saira Extra Condensed", "Arial Narrow", sans-serif;
  font-weight: 500;
  font-size: 1.4em;
  line-height: 1;
  gap: 0.08em;
}

.bar {
  min-width: 0;
  position: relative; /* anchors the barline pseudo-elements */
  /* Just tall enough to enclose a chord box (2.1em) with a little air above and
     below; the barlines hug the row instead of towering over it. */
  min-height: 2.5em;
  /* Fixed grid of `beats` equal columns (grid-template-columns set per bar in
     fillBar). Each chord is anchored to its beat's column line, so beats sit at
     the same fraction of every bar and align vertically bar-to-bar. */
  display: grid;
  align-items: center;
  /* Uniform insets — the left inset just clears a section double-bar. Crucially
     the padding (hence the content box and its beat grid) is identical on every
     bar, which is what keeps beats aligned: the barlines are drawn as
     pseudo-elements below, NOT as borders, so a thick section bar never changes
     a bar's width. */
  padding: 0.15em 0.15em 0.15em 0.27em;
}

/* Barlines as em-scaled pseudo-elements (position:absolute → no layout effect).
   Left line on every bar; right line on the last bar of a row; section
   boundaries thicken to a double bar. */
.bar::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: 0.09em;
  background: var(--line);
}
.bar.rowlast::after,
.bar.sec-end::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  right: 0;
  width: 0.09em;
  background: var(--line);
}
/* Double bar (two thin lines with a small gap) at a section start/end. */
.bar.sec-start::before,
.bar.sec-end::after {
  width: 0.24em;
  background: linear-gradient(90deg,
    var(--line) 0 0.09em, transparent 0.09em 0.15em, var(--line) 0.15em 0.24em);
}
.bar.empty::before,
.bar.empty::after { content: none; }

/* Coda jump-off sign (spec §13 coda_jump): a small, subtle glyph at the start
   of the anchored bar, tucked just outside it in the inter-row gap — below the
   bar (.coda-below) when it sits in its section's last row, above (.coda-above)
   otherwise, as space allows. Shared by the grid (.bar) and book (.bx) layouts;
   both cells are already position:relative. `--muted` carries the subtle tone
   across light and dark themes. Display-only, no interaction. */
.coda-sign {
  position: absolute;
  font-size: 1em; /* the em basis the drawn sign scales with */
  color: var(--muted);
  opacity: 0.75;
  pointer-events: none;
}
/* Jump-off mark: at the start of the bar, just outside it vertically. */
.coda-sign.coda-below,
.coda-sign.coda-above { left: 0.1em; }
.coda-sign.coda-below { top: calc(100% + 0.05em); }
.coda-sign.coda-above { bottom: calc(100% + 0.05em); }
/* Landing mark: just before (left of) the first coda bar, vertically centred. */
.coda-sign.coda-before {
  right: calc(100% + 0.1em);
  top: 50%;
  transform: translateY(-50%);
}
/* Drawn coda sign: circle + two crossing lines, colour via currentColor. */
.coda-sign .coda-glyph {
  display: block;
  width: 1.5em;
  height: 1.5em;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
}

.slot {
  min-width: 0;
  overflow: visible;
  /* Left-aligned at its beat column line; content width, never compressed — a
     crowded chord overflows to the right (fitGridWidth keeps it from colliding
     with the next beat's chord). */
  justify-self: start;
  align-self: center;
}

/* Crowded bars (3+ chords, tagged .tight in fillBar) render their chords in a
   much narrower face and pack the columns tighter, so a single busy bar can't
   force the whole grid's font down — the rest of the grid stays large while the
   dense bar simply gets condensed (see fitGridWidth). The same treatment
   squeezes multi-chord boxes of the book-layout view (.bx.tight). */
.bar.tight .chord,
.bx.tight .chord {
  font-family: "Saira Extra Condensed", "Arial Narrow", sans-serif;
  letter-spacing: -0.03em;
}
/* Narrow the middle/right columns (never the root, so the ground line and the
   root's height stay consistent with the roomy bars) and pull the columns in
   tight, so a dense bar's chords are horizontally compact but still full-height. */
.bar.tight .chord .qual,
.bx.tight .chord .qual { font-size: 1.3em; margin-left: 0.01em; }
.bar.tight .chord .acc,
.bx.tight .chord .acc { margin-left: 0.01em; }
.bar.tight .chord .acc.flat,
.bx.tight .chord .acc.flat { font-size: 1.55em; }
.bar.tight .chord .acc.sharp,
.bx.tight .chord .acc.sharp { font-size: 1.35em; }
.bar.tight .chord .alt-up,
.bar.tight .chord .alt-down,
.bx.tight .chord .alt-up,
.bx.tight .chord .alt-down { font-size: 1.2em; margin-left: 0em; letter-spacing: -0.04em; }

/* ------------------------------------------- book layout ("boxes") view */

/* Re-creation of the printed grille: one contiguous lattice, (up to) 8 boxes
   per row, one section per row, the section letter in the left margin. All
   dimensions are em-based so fitBoxes can zoom the whole lattice through this
   one font-size; individual chords get a per-chord squeeze when they'd
   overflow their box (or their half/slot of it). */
.boxgrid {
  max-width: 58em;
  margin: 0 auto;
  padding-left: 1.6em; /* gutter for the section letters */
  font-size: clamp(9px, 2.6cqw, 16px);
}

/* Strain head above each lattice: caption ("Impro") on the left, that
   strain's form label ("16 A B") right — one per strain. */
.bx-blockhead {
  display: flex;
  align-items: baseline;
  margin-top: 1.2em;
}
.boxgrid > .bx-blockhead:first-child { margin-top: 0; }
.bx-blockhead .bx-caption { margin-top: 0; }
.bx-blockhead .bx-form { margin-left: auto; }

.bx-form {
  border: 0.1em solid var(--line);
  font-family: "Saira Extra Condensed", "Arial Narrow", "Helvetica Neue", Arial, sans-serif;
  font-size: 1.1em;
  line-height: 1;
  letter-spacing: 0.1em;
  padding: 0.12em 0.5em;
  margin-bottom: 0.4em;
  text-transform: uppercase;
}

/* Auxiliary blocks (verse/intro/coda) print as their own grille. */
.bx-block + .bx-block,
.bx-caption { margin-top: 1.2em; }

/* Book-layout variants: captioned box rows below the lattice, clickable to
   swap into it — same states/behavior as the grid view's .variant blocks. */
.bx-variants-title { margin-top: 1.4em; }
.bx-variant {
  margin: 0.5em 0 0.9em;
  border: 1px solid transparent;
  border-radius: 0.5em;
  padding: 0.4em 0.5em;
}
.bx-variant .variant-caption { margin: 0 0 0.35em; }
.bx-variant .bx-block { margin-top: 0; }
.bx-variant.clickable { cursor: pointer; transition: background 0.12s, border-color 0.12s; }
.bx-variant.clickable:hover { background: var(--panel); border-color: var(--border); }
.bx-variant.clickable:focus-visible { outline: 2px solid var(--muted); outline-offset: 2px; }
.bx-variant.clickable.active { border-color: var(--accent); background: var(--chip-bg); }
.bx-variant.clickable.active .variant-caption::after {
  content: " ✓ applied";
  color: var(--accent-ink);
  font-weight: 600;
}

.bx-caption {
  /* Match the chord grid's .sec-strain so the strain name reads the same in
     both views (size, spacing and lighter weight). */
  font-family: "Saira Extra Condensed", "Arial Narrow", "Helvetica Neue", Arial, sans-serif;
  font-weight: 700;
  font-size: 1.0em;
  line-height: 1;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  opacity: 0.7;
  margin-bottom: 0.0em;
}

/* Two four-bar phrases per row: bars within a group of 4 touch (shared single
   borders via .merge-left), the narrow middle track parts the groups. */
.bx-row {
  display: grid;
  grid-template-columns: repeat(4, 1fr) 0.18em repeat(4, 1fr);
  position: relative; /* anchors the section letter in the gutter */
}

/* Air between the rows. */
.bx-row + .bx-row { margin-top: 0.55em; }

/* Section letter in the left margin, centred on its section's first row. */
.bx-seclabel {
  position: absolute;
  left: -1.6em;
  top: 50%;
  transform: translateY(-50%);
  background: var(--fg);
  color: var(--bg);
  font-family: "Saira Extra Condensed", "Arial Narrow", "Helvetica Neue", Arial, sans-serif;
  font-size: 1.0em;
  line-height: 1;
  padding: 0.12em 0.22em;
}

/* Section shading, shared by BOTH chord views: boxes (book layout) and bars
   (4-bar grid) mix their section's hue (--bxhue, set by the renderer) into
   the theme background — a light wash in light mode, a stronger mix in dark
   mode where 12% wouldn't show. The topbar droplet toggles body.tint-off. */
.bx,
.section .bar:not(.empty) {
  --bxtint: color-mix(in srgb, var(--bxhue, transparent) 12%, var(--bg));
  background-color: var(--bxtint);
}

:root[data-theme="dark"] .bx,
:root[data-theme="dark"] .section .bar:not(.empty) {
  --bxtint: color-mix(in srgb, var(--bxhue, transparent) 26%, var(--bg));
}

body.tint-off .bx,
body.tint-off .section .bar:not(.empty) { --bxtint: var(--bg); }

.bx {
  position: relative;
  min-height: 5em;
  min-width: 0;
  border: 0.2em solid var(--line);
}

.bx.merge-left { border-left: none; }

/* Lone chord: big, centred — like the printed box. */
.bx-solo {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  padding: 0.1em 0.3em;
}

/* Solo chords fill their room; the main chord of an inset pair stays a bit
   smaller so its ink clears the inset quadrant. fitBoxes squeezes overflows. */
.bx-solo .chord { font-size: 1.15em; }
.bx-main .chord { font-size: 1.05em; }

/* Two chords: the book's diagonal split — the line runs from the top-right
   corner to the bottom-left one, making the top-left triangle the home of the
   first chord and the bottom-right triangle that of the second. (The gradient
   axis points to the bottom-right corner; the drawn stripe lies perpendicular
   to it, i.e. along the top-right ↔ bottom-left diagonal.) */
.bx.duo {
  background-image: linear-gradient(to bottom right,
    transparent calc(50% - 0.03em),
    var(--line) calc(50% - 0.03em),
    var(--line) calc(50% + 0.03em),
    transparent calc(50% + 0.03em));
}

/* Each chord sits by the centre of gravity of its triangle — the centroid is a
   third in from the right-angle corner (33%) — backed off a little further
   from the diagonal (to 27% resp. 73%) so the symbols keep clear air to the
   line. */
.bx-a,
.bx-b {
  position: absolute;
  font-size: 1.05em;
  white-space: nowrap;
  transform: translate(-50%, -50%);
}

/* The first chord sits a touch lower than its mirror: a root accidental (♭/♯)
   adds ink above the chord, which touched the box's top border at 27%. */
.bx-a { top: 31%; left: 27%; }
.bx-b { top: 73%; left: 73%; }

/* Uneven two-chord bars (1+4, 1+2, …): the long chord big, the short one in
   a small framed box in the corner matching its position in the bar. The main
   chord centres in the half-strip opposite the inset — the free region's
   dominant rectangle, which the opaque quadrant can never cover (a wide chord
   at the L-shape's true centroid would run underneath the inset). */
.bx-main {
  position: absolute;
  white-space: nowrap;
  transform: translate(-50%, -50%);
}

/* 24%/76% (not 25/75) plus the smaller main font keep a root accidental's ink
   clear of the inset's edge. */
.bx-main.clear-bottom { top: 24%; left: 50%; }
.bx-main.clear-top { top: 76%; left: 50%; }

/* The inset is one quadrant of the box (half width × half height); its frame
   is an inside line, much thinner than the frame (0.075em at the 0.8em font =
   0.06em at the box scale). */
.bx-inset {
  position: absolute;
  width: 50%;
  height: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 0.075em solid var(--line);
  /* The box's own opaque tint (masks the main chord underneath); follows the
     .tint-off override through --bxtint. */
  background-color: var(--bxtint, var(--bg));
  font-size: 0.8em;
  line-height: 1;
  white-space: nowrap;
}

.bx-inset .bx-beat { align-self: flex-start; padding-top: 0.2em; }

.bx-inset.inset-late { right: 0; bottom: 0; border-right: none; border-bottom: none; }
.bx-inset.inset-early { left: 0; top: 0; border-left: none; border-top: none; }

/* Three or more chords: the box halves horizontally (top = the bar's first
   half, bottom = the second); a half with several chords splits into
   side-by-side cells — four chords make the 2×2 quadrants. */
.bx-halves {
  display: flex;
  flex-direction: column;
  height: 100%;
}

.bx-half {
  flex: 1;
  min-height: 0;
  display: flex;
  font-size: 0.8em;
}

/* Inside lines, much thinner than the box frame: 0.075em at the halves'
   0.8em font = 0.06em at the box scale. */
.bx-half + .bx-half { border-top: 0.075em solid var(--line); }

.bx-cell {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 0.15em;
  white-space: nowrap;
}

.bx-cell + .bx-cell { border-left: 0.075em solid var(--line); }

/* Small beat digit before an off-default chord (the book's superscripts). */
.bx-beat {
  font-family: "Saira Extra Condensed", "Arial Narrow", "Helvetica Neue", Arial, sans-serif;
  font-size: 0.9em;
  line-height: 1;
  margin-right: 0.1em;
  vertical-align: 1.1em;
}

/* In the flex containers the sup is a flex item: lift it to the top edge. */
.bx-solo .bx-beat { align-self: flex-start; padding-top: 0.35em; }

/* -------------------------------------------------------- chord symbols */

/* The chord is a flex row: an optional "(", the box grid, an optional bass note,
   an optional ")". */
.chord {
  display: inline-flex;
  align-items: center;
  font-family: "Saira Extra Condensed", "Arial Narrow", "Helvetica Neue", Arial, sans-serif;
  font-weight: 400;
  line-height: 1;
  white-space: nowrap;
}

/* Fixed box grid (Figure D.3): a full-height root letter on the left, then a
   middle column (accidental over core quality) and a right column (alt-up over
   alt-down). The two rows are a fixed height in em, so the root always spans the
   same box and every chord shares one ground line — B7♯11 and B♭7♯11 line up
   whether or not there's an accidental. Anything overspilling a box (an enlarged
   flat, a tall alteration) overflows visibly without nudging the layout. */
.chord .box {
  display: inline-grid;
  grid-template-columns: auto auto auto; /* root | middle | alts */
  grid-template-rows: 1.05em 1.05em;
  align-items: center;
}

.chord .root {
  grid-column: 1;
  grid-row: 1 / 3;
  align-self: center;
  font-size: 2.1em;
  line-height: 0.72;
  font-weight: 400;
}

/* Middle column, top box: root accidental as a superscript. */
.chord .acc {
  grid-column: 2;
  grid-row: 1;
  align-self: start;
  justify-self: start;
  margin-left: 0.06em;
  font-size: 1.3em;
  line-height: .5;
}

/* The flat glyph renders small in the fallback font — enlarge it so ♭ reads at
   the same weight as ♯ (both as a root accidental and inside qualities). */
.chord .acc.flat { font-size: 1.8em; line-height: 0.4; margin-left: -0.05em}
.chord .fl { font-size: 1.28em; line-height: 0.7; }

/* Middle column, bottom box: the core quality (7, -7, Δ7, ø7 …). */
.chord .qual {
  grid-column: 2;
  grid-row: 2;
  align-self: center;
  justify-self: start;
  margin-left: 0.06em;
  font-size: 1.2em;
  line-height: 0.85;
}

/* Right column: up to two alterations. A lone one sits in the upper box
   (superscript); a pair straddles the core quality (Figure D.3). Extended
   chords like B♭7♯9♯5 or A♭7(13) stay about a letter-width wide. */
.chord .alt-up,
.chord .alt-down {
  grid-column: 3;
  justify-self: start;
  margin-left: 0.04em;
  font-size: 1.1em;
  line-height: 0.95;
}

.chord .alt-up { grid-row: 1; align-self: end; }
.chord .alt-down { grid-row: 2; align-self: start; }

/* Bass note sits below-right of the box, after a short slash (Aø7/E♭). */
.chord .bass {
  align-self: flex-end;
  transform: translateY(0.3em);
  margin-left: 0.08em;
  font-size: 1.2em;
  line-height: 1;
}

/* Enlarge the slash separator only — the bass note/degree keeps the .bass size. */
.chord .bass .slash {
  font-size: 1.5em;
}

.chord .paren {
  font-size: 1.6em;
  font-weight: 400;
  opacity: 0.8;
}

.chord.optional { opacity: 0.85; }
.chord.optional .root { font-size: 1.7em; }

.chord .nc {
  font-size: 1.1em;
  font-variant: small-caps;
  color: var(--muted);
}

.chord-raw {
  font-family: Consolas, monospace;
  font-size: 1em;
  color: #c96e6e;
}

/* --------------------------------------------------------------- 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); }

/* 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: "Saira Extra Condensed", "Arial Narrow", "Helvetica Neue", Arial, sans-serif;
  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) {
  #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;
  }
}

/* ───────────────────────── 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; }

/* Brush transport popover (brushes.js): tempo slider + BPM, tap tempo with
   beat dots, pattern segment, volume. Same shell as the tag-filter menu. */
#brushMenu {
  position: fixed;
  z-index: 60;
  min-width: 17rem;
  max-width: 22rem;
  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.5rem;
}
#brushMenu[hidden] { display: none; }
#brushMenu.sheet {
  left: 0;
  right: 0;
  bottom: 0;
  top: auto;
  max-width: none;
  border-radius: 0.8rem 0.8rem 0 0;
}

.brush-row {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.28rem 0.3rem;
}
.brush-lab {
  color: var(--muted);
  font-size: 0.8rem;
  width: 3.4rem;
  flex: none;
}
.brush-row input[type="range"] {
  flex: 1;
  min-width: 0;
  accent-color: var(--accent);
}
#brushBpmNum {
  width: 3.8rem;
  flex: none;
  background: none;
  border: 1px solid var(--border);
  border-radius: 0.35rem;
  color: var(--fg);
  padding: 0.15rem 0.3rem;
  font-size: 0.9rem;
  font-variant-numeric: tabular-nums;
}
#brushTap,
.brush-toggle,
.brush-seg button {
  background: none;
  border: 1px solid var(--border);
  border-radius: 0.4rem;
  color: var(--fg);
  font-size: 0.85rem;
  padding: 0.25rem 0.7rem;
  cursor: pointer;
}
.brush-toggle { margin-left: auto; min-width: 3rem; }
.brush-seg button.on,
.brush-toggle.on { border-color: var(--accent); color: var(--accent); }

/* Beat dots: live 1-2-3-4 pulse; amber during the count-in bar. */
.brush-dots {
  display: flex;
  gap: 0.4rem;
  margin-left: auto;
  padding-right: 0.2rem;
}
.brush-dot {
  width: 0.6rem;
  height: 0.6rem;
  border-radius: 50%;
  background: var(--chip-bg);
  border: 1px solid var(--border);
}
.brush-dot.on { background: var(--accent); border-color: var(--accent); }
.brush-dot.count { background: #d99a2b; border-color: #d99a2b; }

/* Playhead: the sounding bar is outlined in both chord layouts, with the
   cursor underlining the current beat's column span. */
.bar.brush-now,
.bx.brush-now {
  position: relative;
  box-shadow: inset 0 0 0 2px var(--accent);
  border-radius: 2px;
}
.brush-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;
}

/* 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;}

/* ================= harmonic analysis: numerals + overlay lanes (spec §4) */

/* Spelling switch (C / C⁄ii / ii) in the chords panel tools row: text pills
   sharing the scan-toggle chrome, sized by their label. */
.panel .scan-toggle.spell-btn {
  width: auto;
  min-width: 2.1rem;
  padding: 0 0.45rem;
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.02em;
}

/* Hybrid spelling: the numeral in a small line under the printed chord,
   left-aligned with it so the beat anchoring still reads. */
.chstack {
  display: inline-flex;
  flex-direction: column;
  align-items: flex-start;
  vertical-align: top;
}
.deg-sub {
  font-size: 0.8em;
  line-height: 1.15;
  color: var(--accent-ink);
  white-space: nowrap;
  margin-top: 0.1em;
}
.deg-sub .deg-line { display: block; }
/* Roman-only spelling: the numeral replaces the chord, near the comparison
   view's degree size but scaled down for stacked pivot readings. */
.chord.numeral { font-weight: 400; letter-spacing: 0.02em; font-size: 1.7em; }
.chord.numeral .deg-line { display: block; line-height: 1.1; }
/* Pivot chords stack the outgoing key's reading (muted) over the new one. */
.pivot-old { color: var(--muted); font-size: 0.82em; }

/* Overlay lanes: one thin band per feature class under each row of bars,
   aligned to the bars via a beat-resolution column grid. */
.lane {
  display: grid;
  grid-column: 1 / -1;
  min-width: 0;
  font-size: 1.0em;
  line-height: 1.1;
  margin-top: 0.12em;
  pointer-events: none;
}

/* Key-region band: a dashed extent line with the "F:" prefix at its start
   (lowercase for minor keys, like the book's colon notation). The dashes are
   a gradient, not a dashed border, so the dash/gap sizes stay legible. */
.lane.lane-regions { margin-top: 0.05em; }
.lane-region {
  padding-top: 0.2em;
  min-width: 0;
  background: repeating-linear-gradient(to right,
      var(--muted) 0 0.5em, transparent 0.5em 0.95em)
    left top / 100% 0.14em no-repeat;
}
.lane-region.rg-section {
  background-image: linear-gradient(var(--muted), var(--muted));
}
.lane-region .lane-label {
  color: var(--accent-ink);
  font-weight: 700;
  padding-left: 0.15em;
  white-space: nowrap;
}

/* ii–V brackets: the book's under-bracket |____| ; dotted for ii–subV /
   subii–V pairs. Row-crossing pieces drop the open side's tick. */
.lane-bracket {
  height: 0.55em;
  margin: 0 0.3em;
  border: 0.13em solid var(--fg);
  border-top: none;
  opacity: 0.75;
}
.lane-bracket.dotted {
  /* wide-gap dashes on the horizontal (gradient — border dashes are too
     fine to tell from solid at lane size); the short ticks stay solid */
  border-bottom: none;
  background: repeating-linear-gradient(to right,
      var(--fg) 0 0.5em, transparent 0.5em 0.95em)
    left bottom / 100% 0.13em no-repeat;
}
.lane-bracket.cont-right { border-right: none; }
.lane-bracket.cont-left { border-left: none; }

/* Resolution arrows: a line with a right-pointing head — solid for the
   down-a-fifth resolution, dashed for the half-step (subV) one, headless
   for the to-minor move. Drawn just below the bracket band. */
.lane-arrow {
  position: relative;
  height: 0.5em; /* also the stacking pitch when arrows overlap in a row */
  min-width: 0;
}
.lane-arrow::before {
  content: "";
  position: absolute;
  left: 0.35em;
  right: 0.5em;
  top: 45%;
  border-top: 0.14em solid var(--fg);
  opacity: 0.75;
}
.lane-arrow.half::before {
  border-top: none;
  height: 0.14em;
  background: repeating-linear-gradient(to right,
      var(--fg) 0 0.5em, transparent 0.5em 0.95em);
}
.lane-arrow::after {
  content: "";
  position: absolute;
  right: 0.15em;
  top: 45%;
  transform: translateY(-50%);
  border-left: 0.45em solid var(--fg);
  border-top: 0.28em solid transparent;
  border-bottom: 0.28em solid transparent;
  opacity: 0.8;
}
.lane-arrow.to_minor::after,
.lane-arrow.cont-right::after { border-left-color: transparent; }

/* Building blocks: a labelled tinted span. Bottom margin keeps the pill
   clear of the next row of bars. */
.lane.lane-blocks { margin-bottom: 0.3em; }
.lane-block {
  margin-top: 0.1em;
  min-width: 0;
  background: color-mix(in srgb, var(--accent) 26%, transparent);
  border-radius: 0.5em;
  padding: 0.08em 0.4em;
  color: var(--fg);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  text-align: center;
}
.lane-block.cont-right { border-top-right-radius: 0; border-bottom-right-radius: 0; }
.lane-block.cont-left { border-top-left-radius: 0; border-bottom-left-radius: 0; }
/* Blocks are tappable (the lane itself is click-through) to reveal the full,
   possibly-truncated, name in the popover below. */
.lane-block[data-full] { pointer-events: auto; cursor: pointer; }

/* Tap-to-reveal popover for a truncated building-block label. Viewport-fixed
   and positioned in JS; dismissed on the next tap, scroll or resize. */
.block-popover {
  position: fixed;
  z-index: 70;
  max-width: min(80vw, 22rem);
  background: var(--panel);
  color: var(--fg);
  border: 1px solid var(--border);
  border-radius: 0.4rem;
  padding: 0.32rem 0.55rem;
  font-size: 0.85rem;
  line-height: 1.25;
  text-align: center;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.25);
  pointer-events: none;
}
.block-popover[hidden] { display: none; }

/* Legend for the overlay symbols, at the bottom of the chords panel while
   the overlay is on. Sample glyphs reuse the lane classes above. */
.lane-legend {
  margin-top: 1.4rem;
  padding-top: 0.6rem;
  border-top: 1px solid var(--border);
  display: flex;
  flex-wrap: wrap;
  gap: 0.65rem 1.6rem;
  font-size: 1.0rem;
  color: var(--muted);
}
.lane-legend .lg-item { display: flex; align-items: center; gap: 0.55em; min-width: 0; }
/* The ii–V bracket entries each occupy a full row of their own. */
.lane-legend .lg-item.lg-break { flex-basis: 100%; }
.lane-legend .lg-sample { width: 3em; flex: none; font-size: 1.2em; }
.lane-legend .lg-sample > div { margin: 0; }
.lane-legend .lane-region { padding-top: 0.05em; }
.panel.chords.show-scan .lane-legend { display: none; }
