/**
 * scale-collection-page.css — dedicated scale Collection browser.
 * Class prefix `.scp-*`.  Full-viewport layout matching Scale Explorer.
 */

/* Anti-flicker on grid-pane teardown → rebuild.
   Per user 2026-05-26: opacity-fade approach (was
   `.is-rerendering { opacity: 0 }` toggled over 120 ms) read AS the
   flicker because the transition itself was perceived.  Replaced by
   `visibility: hidden` set in JS at the start of the rebuild and
   released on the 2nd rAF — instant hide / instant show, no
   transition, layout box stays intact via the min-height pin set
   alongside.  See shared/collection-grid-pane.js _renderGrid.  Same
   pattern lives in shared/music-page/area-card.js destroyBoards for
   the browse-area fretboard. */
.scp-grid-host {
  /* Constrain to parent.  Without `min-width: 0` the flex child
     defaults to `min-width: auto` and grows past the viewport when
     children (inline chart-v2 mount) have large natural width.
     Per user 2026-05-17: chart should shrink proportionally on
     small browser. */
  min-width: 0;
}
/* Same min-width: 0 on the grid-pane's wrapper inside the chord/
   scale page so the entire chain from `.scp-shell-content` down to
   the chart-v2 mount can shrink to viewport width. */
.scp-shell-area { min-width: 0; }

/* ── Fretboard layout modes ─────────────────────────────────────
   Single CSS source of truth for `data-fretboard-layout` on
   `.scp-root` (set by both chord and scale pages from
   `state.fretboardLayout`).  Shared rules so both pages render
   identically — per user 2026-05-10 "do it at top page level for
   inherited behaviour".

     fit       (default): renderer fits container width — no
                          overrides needed.
     natural             : renderer uses fixed per-fret px (44),
                          card overflows; wrap allows horizontal
                          scroll inside the fretboard area.
     vertical            : rotate the rendered SVG 90° so the neck
                          reads top→bottom.  Hosting cards adapt
                          their dimensions accordingly. */

/* NATURAL mode — card area scrolls horizontally; the SVG renders
   at its natural (un-clipped) width because the renderer was
   passed `targetDx: 44`.  Scrollbar hidden by default; revealed
   on hover so the user has a clear affordance that horizontal
   scrolling is available within the card.  `touch-action: pan-x`
   tells the browser/OS the user's horizontal pan gestures belong
   to this element. */
/* The `html[data-fretly-zoom="in"] …[data-fretboard-layout="fit"]`
   variants below give the SAME scroll affordance when the user has
   browser-zoomed IN while in fit mode: zoom-aware boards then render
   wider than the card (zoom-compensated width — see
   shared/music-page/fretboard-board.js + page-zoom.js) and the card
   must scroll instead of clipping.  Per user 2026-06-11. */
.scp-root[data-fretboard-layout="natural"] .spg-area-card,
.scp-root[data-board-zoom="in"] .spg-area-card,
html[data-fretly-zoom="in"] .scp-root[data-fretboard-layout="fit"] .spg-area-card {
  /* `!important` here beats `scale-page.css { .spg-area-card {
     overflow: hidden !important } }` which would otherwise clip
     horizontally and prevent the swipe scroll inside the card. */
  overflow-x: auto !important;
  overflow-y: hidden !important;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-x: contain;
  touch-action: pan-x;
  /* min-width: 0 lets the card shrink below its intrinsic
     content width so overflow-x: auto can do its job. */
  min-width: 0;
  max-width: 100%;
  /* Hide the scrollbar UNTIL the user hovers over the card —
     then show a slim accent-tinted bar so the scroll affordance
     is discoverable.  Content-as-affordance becomes hover-as-
     affordance, which a desktop user can find via cursor. */
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.scp-root[data-fretboard-layout="natural"] .spg-area-card::-webkit-scrollbar,
.scp-root[data-board-zoom="in"] .spg-area-card::-webkit-scrollbar,
html[data-fretly-zoom="in"] .scp-root[data-fretboard-layout="fit"] .spg-area-card::-webkit-scrollbar {
  height: 0;
}
/* Toolbar at the top of the card is also a child of the scroll
   container — without intervention it would scroll horizontally
   along with the SVG, hiding the Prev/Next/position label as the
   user scrolls right.  Pin it to the LEFT edge of the visible
   card area via `position: sticky; left: 0` so it stays put as
   the SVG slides under it.  Per user 2026-05-10. */
.scp-root[data-fretboard-layout="natural"] .spg-area-card .spg-area-card-toolbar,
.scp-root[data-fretboard-layout="natural"] .spg-area-card .cpg-area-card-toolbar,
.scp-root[data-board-zoom="in"] .spg-area-card .spg-area-card-toolbar,
.scp-root[data-board-zoom="in"] .spg-area-card .cpg-area-card-toolbar,
html[data-fretly-zoom="in"] .scp-root[data-fretboard-layout="fit"] .spg-area-card .spg-area-card-toolbar,
html[data-fretly-zoom="in"] .scp-root[data-fretboard-layout="fit"] .spg-area-card .cpg-area-card-toolbar {
  position: sticky;
  left: 0;
  z-index: 2;
  background: var(--surface-2, #f7f6f3);
}
.scp-root[data-fretboard-layout="natural"] .spg-area-card:hover,
.scp-root[data-board-zoom="in"] .spg-area-card:hover,
html[data-fretly-zoom="in"] .scp-root[data-fretboard-layout="fit"] .spg-area-card:hover {
  scrollbar-width: thin;
  scrollbar-color: var(--accent, #e8853c) transparent;
}
.scp-root[data-fretboard-layout="natural"] .spg-area-card:hover::-webkit-scrollbar,
.scp-root[data-board-zoom="in"] .spg-area-card:hover::-webkit-scrollbar,
html[data-fretly-zoom="in"] .scp-root[data-fretboard-layout="fit"] .spg-area-card:hover::-webkit-scrollbar {
  height: 8px;
}
.scp-root[data-fretboard-layout="natural"] .spg-area-card:hover::-webkit-scrollbar-thumb,
.scp-root[data-board-zoom="in"] .spg-area-card:hover::-webkit-scrollbar-thumb,
html[data-fretly-zoom="in"] .scp-root[data-fretboard-layout="fit"] .spg-area-card:hover::-webkit-scrollbar-thumb {
  background: var(--accent, #e8853c);
  border-radius: 4px;
}
.scp-root[data-fretboard-layout="natural"] .spg-area-card:hover::-webkit-scrollbar-track,
.scp-root[data-board-zoom="in"] .spg-area-card:hover::-webkit-scrollbar-track,
html[data-fretly-zoom="in"] .scp-root[data-fretboard-layout="fit"] .spg-area-card:hover::-webkit-scrollbar-track {
  background: transparent;
}
/* min-width: 0 on EVERY ancestor in the chain so the SVG's
   1000+px intrinsic width can't propagate up and grow the page.
   Flex / grid items default to `min-width: auto` (= content
   width); without overriding, ancestors expand to match the
   widest descendant — pushing the page horizontally instead of
   clipping inside the card. */
.scp-root[data-fretboard-layout="natural"] .cpg-area-boards,
.scp-root[data-fretboard-layout="natural"] .spg-shell-area,
.scp-root[data-fretboard-layout="natural"] .scp-shell-content,
.scp-root[data-fretboard-layout="natural"] .scp-main,
.scp-root[data-fretboard-layout="natural"] .spg-view-fretboard,
.scp-root[data-fretboard-layout="natural"] .spg-view-diagram,
.scp-root[data-board-zoom="in"] .cpg-area-boards,
.scp-root[data-board-zoom="in"] .spg-shell-area,
.scp-root[data-board-zoom="in"] .scp-shell-content,
.scp-root[data-board-zoom="in"] .scp-main,
.scp-root[data-board-zoom="in"] .spg-view-fretboard,
.scp-root[data-board-zoom="in"] .spg-view-diagram,
html[data-fretly-zoom="in"] .scp-root[data-fretboard-layout="fit"] .cpg-area-boards,
html[data-fretly-zoom="in"] .scp-root[data-fretboard-layout="fit"] .spg-shell-area,
html[data-fretly-zoom="in"] .scp-root[data-fretboard-layout="fit"] .scp-shell-content,
html[data-fretly-zoom="in"] .scp-root[data-fretboard-layout="fit"] .scp-main,
html[data-fretly-zoom="in"] .scp-root[data-fretboard-layout="fit"] .spg-view-fretboard,
html[data-fretly-zoom="in"] .scp-root[data-fretboard-layout="fit"] .spg-view-diagram {
  min-width: 0;
  max-width: 100%;
}
/* Belt-and-braces — clip any horizontal overflow at the page
   root in natural mode so the body NEVER scrolls horizontally.
   Inside the card, overflow-x: auto provides the swipe surface;
   outside the card, nothing should escape. */
.scp-root[data-fretboard-layout="natural"],
.scp-root[data-board-zoom="in"],
html[data-fretly-zoom="in"] .scp-root[data-fretboard-layout="fit"] {
  overflow-x: clip;
  max-width: 100vw;
}

/* VERTICAL mode — SVG-level rotation handled by the global
   `Fretboard.drawVertical()` (in assets/js/fretboard.js).  The
   SVG's `width`/`height`/`viewBox` are swapped natively and all
   text elements are counter-rotated at render time so fret
   numbers and dot labels read upright.  No CSS rotation hack —
   the previous transform-based approach was removed 2026-05-11
   in favour of the global drawing routine.
   `flex-direction: column` keeps the toolbar above the SVG (not
   side-by-side); `align-items: center` horizontally centres the
   tall+narrow SVG within the card. */
.scp-root[data-fretboard-layout="vertical"] .cpg-area-boards .spg-area-card,
.scp-root[data-fretboard-layout="vertical"] .spg-shell-area .spg-area-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  /* Per user 2026-05-11: card kept narrow in vertical mode —
     a tall fretboard doesn't need to span the full page width.
     Max-width tuned so the toolbar (Prev / Next / position label
     / actions) fits comfortably AND the rotated SVG (≈336px wide
     after 1.4× scale) sits centred with breathing room.  Centred
     via `margin: auto`. */
  max-width: 580px;
  margin-left: auto;
  margin-right: auto;
  /* Per user 2026-05-11: "don't cut the fretboard off, show the
     whole thing".  Card grows vertically to fit the full ~1600px
     rotated SVG.  No internal scroll — the PAGE scrolls when the
     content exceeds the viewport.  Auto-scroll (in voicing-board
     / fingering-board JS) targets `window.scrollTo` so the active
     dots are kept on-screen via page scroll, not card scroll. */
}

/* ── Right-pane mode swap (Diagram ↔ Collection) on scale page ─── */

/* Fretboard is the default visible sub-view; Collection hidden until
   the user flips the View toggle to 'Collection'.  Renamed
   2026-05-10 from `.spg-view-diagram` → `.spg-view-fretboard` per
   user — "diagram" is reserved for the chord-diagram cards
   (DiagramsArea); the fretboard view is "fretboard". */
.spg-view-fretboard {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.spg-view-collection {
  display: none;
  flex-direction: column;
  gap: 12px;
  /* min-width: 0 so the inline chart-v2 mount (suite drill-in) can
     shrink to viewport width.  Without this, flex-child default
     `min-width: auto` lets the natural-width chart push past the
     parent.  Per user 2026-05-17. */
  min-width: 0;
}

.spg-content--collection .spg-view-fretboard  { display: none; }
.spg-content--collection .spg-view-collection { display: flex; }

/* ── Advanced mode — DOM-moved Scale Explorer host ───────────────
   PIVOT 2026-07-03 (owner): Advanced mode is the real Scale Explorer
   surface, DOM-moved into `.spg-view-advanced` by
   assets/js/scale-page/advanced-explorer-host.js. View-swap mirrors
   the `.spg-content--lab` pattern in lab.css exactly — same idea,
   different host class. `.spg-view-advanced` has no static rules of
   its own beyond display; Explorer supplies 100% of its own layout
   via scale-explorer.css (loaded app-wide, see app.php). */
.spg-view-advanced {
  display: none;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
  flex: 1;
}
.spg-content--advanced .spg-view-fretboard  { display: none; }
.spg-content--advanced .spg-view-collection { display: none; }
.spg-content--advanced .spg-shell-panels    { display: none; }
.spg-content--advanced .spg-view-advanced   { display: flex; }

/* Breadcrumb strip ("+ Folder" / "+ Fingering" actions live in its
   actionsEl — see page.js Breadcrumb/CollectionGridPane wiring) is a
   SIBLING of `.spg-view-fretboard` / `.spg-view-collection` inside
   `.scp-shell-content` (appended once, above both view containers — see
   page.js `crumbHost`), so the three rules above never reached it: it
   stayed visible floating above the DOM-moved Explorer surface. Advanced
   mode is the Explorer's OWN chrome edge-to-edge; hide the breadcrumb
   (and its Folder/Fingering add actions) the same way the other
   normal-mode-only views are hidden. Found + fixed 2026-07-03 (Phase A
   Item 1 completeness pass). */
.spg-content--advanced .scp-crumb-host { display: none; }

/* Explorer is full-width, edge-to-edge chrome with its own responsive
   list pane — the scale-page's own Scales/Collection drawer toggles
   (outer edge cells of the control shell) and side drawers must not
   show or open underneath it. `.spg-root--advanced` is set on the page
   root (page.js `_applyAdvancedExplorerHost`) since the edge toggles +
   drawers live outside `.scp-shell-content` (siblings of it, not
   descendants — `.spg-content--advanced` alone can't reach them).
   BUG FIX 2026-07-11: `.scp-shell-list` is ALSO the exact class
   advanced-sidepanes-graft.js reuses for Explorer's OWN Scales/
   Collection side panes (`this._leftPane`/`this._rightPane`, built
   INSIDE `#route-scale-explorer`, itself a descendant of the SAME
   `.spg-root--advanced` root) — see that module's docblock ("SAME scp
   pane machinery ... shared/scp-drawer.js ... shared/scp-pane-resize.js").
   The un-scoped selector below was force-hiding those panes too
   (`display:none !important` beats the `.scp-main--list-open
   .scp-shell-list` open-state rule further down, which carries no
   `!important`), so the "Scales" sidebar toggle never showed anything —
   latent since Advanced mode was admin-only (nobody exercised the
   graft's panes against this rule until members got access). Exclude
   any `.scp-shell-list` that lives under `#route-scale-explorer` so only
   the NORMAL page's own docked panes are hidden here. */
.spg-root--advanced .scp-ctrl-cell--edge,
.spg-root--advanced .scp-shell-list:not(#route-scale-explorer .scp-shell-list) {
  display: none !important;
}

/* Owner 2026-07-03 round 3 follow-up ("Keep the 'open below' one, not the
   one on top" — fingerings bar): the scales-page InlineCollectionStrip
   band (`.spg-strip`, the "FINGERINGS Full/2NPS/…" system-tabs strip that
   normal mode renders under its ctrl bar) was still showing ABOVE the
   Explorer surface at desktop width in Advanced mode — the graft's own
   below-the-bar submenu strip is the only fingerings bar Advanced shows. */
.spg-root--advanced .spg-strip {
  display: none !important;
}

/* Owner bug 2026-07-08 ("the orange bar between title and control is
   illegal"): the NORMAL-mode fingerings dropdown (fingerings-popover.js
   `_openNow`, kept open across loads via localStorage
   `fretly-spg-fpop-open`) mounts `.spg-fpop-panel--integrated` as a
   DIRECT CHILD of the page root, sibling after `.scp-ctrl-shell`. In
   Advanced mode the rule above hides its inner `.spg-strip` but the
   panel itself (accent-wash bg + padding + border-bottom) stayed — an
   empty orange band between the app header and the Explorer surface
   (and above the Lab toolbar when Lab is entered from Advanced). The
   CHILD combinator is required: the advanced fingerings graft builds
   its own submenu with the SAME `.spg-fpop-panel--integrated` class
   INSIDE `#route-scale-explorer` (advanced-fingerings-graft.js) — that
   one must stay visible. */
.spg-root--advanced > .spg-fpop-panel--integrated {
  display: none !important;
}

/* "Don't use 2 control menus" (owner directive, 2026-07-03): the
   scales-page docked ctrl grid (`.scp-ctrl-shell` — Key & Display /
   Fingerings / View / Ghost Notes / Lab / Advanced cells, built by
   page.js's `_buildKeyDisplayCell` etc.) was still rendering ABOVE the
   Explorer surface at desktop width (>1100px) — only the ≤1100px
   hamburger-popover path (`.scp-app-ham`) had ever been considered, and
   even that trigger itself was never hidden. Both are direct children of
   `this.container` (same node `.spg-root--advanced` toggles — see
   page.js `_applyAdvancedExplorerHost`), siblings of `.scp-shell-content`,
   so neither was reachable by the `.spg-content--advanced` view-swap
   rules above. Fixed here, at EVERY width: the needed controls (Advanced
   exit, Lab, Ghost Notes) are now native `.se-ctrl-cell`s injected into
   Explorer's OWN ctrl-grid instead (advanced-chrome-cells-graft.js) — see
   ADDENDUM 2026-07-03 (4th) in Documents/SCALES-ADVANCED-PHASE-A-SPEC-
   2026-07-03.md. Pure CSS class toggle, same `.spg-root--advanced`
   class already added/removed symmetrically on mode entry/exit — no DOM
   removal, fully reversible on mode exit like every other rule in this
   block. */
/* `:not(.spg-root--lab)` exception (owner bug 2026-07-08, Lab-from-
   Advanced): while Lab is active the Explorer surface — and with it the
   Explorer ctrl bar carrying the injected LAB cell — is hidden
   (lab.css `.spg-content--advanced.spg-content--lab .spg-view-advanced`),
   so hiding the docked ctrl-shell too would leave the user with NO
   control bar and no way to exit Lab. The normal ctrl-shell (its Lab +
   Advanced switches stay state-synced by `_applyPageMode` /
   `_applyScalePageMode`) resurfaces for exactly the Lab stay; flipping
   Lab off removes `.spg-root--lab` (page.js `_applyPageMode`) and this
   hide kicks back in as the Explorer surface returns. */
.spg-root--advanced:not(.spg-root--lab) .scp-ctrl-shell {
  display: none !important;
}
/* ROUND-4 ADDENDUM 2026-07-04: `.scp-app-ham` is now the ≤1100px entry
   point into Explorer's OWN ctrl-grid controls while Advanced mode is
   active — the grid's 10-11 cells don't fit the viewport at narrow
   widths (same reason page.js's own hamburger exists for Normal mode).
   Un-hidden here (was `display:none !important` unconditionally,
   leaving Advanced mode with NO narrow-width path to Key&Display/
   Fingerings/Filters/etc at all); still governed by the SAME
   `@media (max-width:1100px)` threshold as Normal mode's hamburger
   (scale-collection-page.css "Hamburger threshold" rule below — that
   rule's `.scp-app-ham { display:inline-flex }` applies here too, this
   selector only removes the unconditional override). Click routes to
   `advanced-explorer-host.js`'s own `toggleControlsPopover()` (DOM-move,
   not rebuild) — see page.js's hamburger click handler. */

/* ROUND-4 completeness (2026-07-04, Fable): parity with Normal mode also
   requires the Explorer ctrl-grid itself to HIDE at ≤1100px in Advanced
   mode — the hamburger popover is then the SINGLE entry point, exactly
   like Normal mode's `.scp-ctrl-shell` hide at the same breakpoint
   ("Hamburger threshold" rule below). Scoped to `.spg-root--advanced` so
   the standalone route's narrow layout is untouched. */
@media (max-width: 1100px) {
  /* Carries #route-scale-explorer to out-rank the base
     `#route-scale-explorer .se-ctrl-grid { display:grid }` rule in
     scale-explorer.css (same specificity trap as #se-app-header). */
  .spg-root--advanced #route-scale-explorer .se-ctrl-grid { display: none; }
}

/* Bug fix 2026-07-03: the Scales page's OWN app-header subtitle FRAGMENT
   span (the "· system · view" text, `.scp-app-subtitle` with no `--btn`
   modifier, refreshed by `_refreshAppHeader()`) must hide while Advanced
   mode is docked — it describes NORMAL-mode state (active fingering
   system) that's meaningless once Explorer's own chrome takes over the
   page, and was showing stale fragments (e.g. "· 3NPS") underneath
   Explorer. The header ELEMENT itself, title, gear/help/hamburger icons
   stay visible — `.scp-app-header` lives outside `.scp-shell-content` (a
   sibling, not a descendant), so it was never touched by the
   `.spg-content--advanced` view-swap rules above; this rule only targets
   the trailing subtitle fragment inside it. Restored automatically on
   mode exit since this is a pure CSS toggle (no DOM removal) driven by
   the SAME `.spg-root--advanced` class `_applyAdvancedExplorerHost()`
   already adds/removes on every mode flip — symmetric with
   mount()/unmount(), no new JS mechanism.
   Round-3 BUGFIX (2026-07-03, owner desktop review — item 8, "verify the
   tuning picker keeps working"): the ORIGINAL selector here was the bare
   `.scp-app-subtitle` class, which the tuning quick-switch BUTTON also
   carries (`tuningBtn.className = 'scp-app-subtitle scp-app-subtitle--btn'`
   — page.js `_render()`) — so this rule was silently hiding the tuning
   picker ENTIRELY in Advanced mode, not just the inert text fragment it
   was meant to target. `:not(.scp-app-subtitle--btn)` excludes the
   button, restoring the "tuning picker keeps working" contract while
   still hiding the plain fragment span (which has no `--btn` class). */
/* SUPERSEDED 2026-07-03 (owner "unify title bar"): the fragment span is
   no longer hidden in Advanced mode — page.js `_applyAdvancedTitleOverride`
   writes "All possible scales in 12-TET" into it and `_refreshAppHeader`'s
   `_advancedTitleSaved` guard prevents normal-mode text from clobbering it,
   so the docked title bar alone now carries the Explorer heading (the
   in-surface `#se-app-header` duplicate is hidden below). Rule kept as a
   comment for archaeology:
   .spg-root--advanced .scp-app-subtitle:not(.scp-app-subtitle--btn) { display:none !important; } */

/* Round-3 (2026-07-03, owner desktop review — item 8): the Scales page's
   own app-header now carries the "Scale Explorer" / "All possible scales
   in 12-TET" text while Advanced mode is active (page.js
   `_applyAdvancedTitleOverride()`), so Explorer's OWN in-surface heading
   card (`#se-app-header` — `<h1>Scale Explorer</h1>` +
   `.subtitle All possible scales in 12-TET`, route-scale-explorer.php)
   would otherwise render as a visible DUPLICATE directly below the docked
   page header. Hidden via `.spg-root--advanced` scope — an ANCESTOR of
   `#route-scale-explorer` once DOM-moved into `.spg-view-advanced` (see
   the `.spg-root--advanced .scp-ctrl-shell` rule above for the same
   ancestor relationship) — so this NEVER applies on the standalone
   `?route=scale-explorer` route, which keeps its own heading card fully
   visible and unchanged (that node is never a `.spg-root--advanced`
   descendant there). Pure CSS toggle, symmetric with mode exit like every
   other rule in this block — no DOM removal, Explorer's own header stays
   intact underneath, simply not rendered while docked. */
/* Selector carries the #route-scale-explorer id on purpose: Explorer's own
   base rule `#route-scale-explorer #se-app-header { display:flex }`
   (scale-explorer.css:21) counts TWO ids and would out-rank a plain
   `.spg-root--advanced #se-app-header` (one id) — the duplicate heading
   stayed visible (owner report 2026-07-03: "We don't need double title
   bar"). class + 2 ids wins; still never matches standalone (no
   `.spg-root--advanced` ancestor there). */
.spg-root--advanced #route-scale-explorer #se-app-header {
  display: none;
}

/* Owner 2026-07-03: "In admin, scale explorer is true full width. I want
   this in the scales > advanced mode also." Standalone renders
   #route-scale-explorer full-bleed directly in .page-wrap; embedded it
   sat inside .scp-shell-content's horizontal padding. Strip the gutters
   while advanced is active (everything else in that shell is hidden in
   advanced, so this affects only the Explorer surface). */
.spg-root--advanced .scp-shell-content {
  padding-left: 0;
  padding-right: 0;
  /* Owner 2026-07-04: "no grey background below title bar (above control
     bar)" + "control bar position exactly the same in both modes". The
     shell's default 12px top padding let the page background show as a
     grey band between the docked title bar and Explorer's ctrl grid —
     Normal mode's ctrl shell is a SIBLING of this container (flush under
     the header), so only Advanced was affected. Zero top AND bottom for
     the same full-bleed posture as the standalone route. */
  padding-top: 0;
  padding-bottom: 0;
}

/* Bug fix 2026-07-03: this block used to pin `.scp-root`/`.scp-main`/
   `.scp-shell-content` to `height: 100vh` / `height: 100%` while Advanced
   mode is docked, on the theory that Explorer needs a hard viewport cap
   here the way it has one standalone. That was wrong and self-defeating:
   `#route-scale-explorer`'s OWN CSS (scale-explorer.css) already supplies
   100% of the sizing/overflow behaviour Explorer needs, INCLUDING the
   `.page-wrap:has(#route-scale-explorer.active)` / `html:has(...) body`
   rules that fire automatically once the host adds `.active` in
   advanced-explorer-host.js `mount()` — those alone reproduce the
   standalone route's constraints exactly, since `#route-scale-explorer`
   is a `.page-wrap` descendant in both the standalone and docked cases.
   Layering a SECOND, independent `height:100vh`/`height:100%` cap on the
   scp ancestors on top of that just starves Explorer's own internal flex
   chain (`#se-body` → `#se-main` → `#se-detail-pane`, all `flex:1` +
   `overflow:hidden/auto`) of the height it computes for itself, crushing
   `#se-detail-pane` down to a sliver (measured: 169px, scrollHeight 3142)
   while the rest of the docked page's content sits clipped beneath it.
   Fix: don't impose any competing height here — `.scp-root`/`.scp-main`/
   `.scp-shell-content` fall back to their normal-mode sizing (natural /
   content-driven, `min-height` not `height`), so `#route-scale-explorer`'s
   own CSS is the ONLY thing deciding how Advanced mode sizes itself,
   identically to the standalone `?route=scale-explorer` route. */
.spg-root--advanced .scp-shell-content {
  display: flex;
  flex-direction: column;
  min-height: 0;
}

/* Bug fix 2026-07-03 (owner desktop review, >1300px): the comment block
   directly above assumed scale-explorer.css's
   `.page-wrap:has(#route-scale-explorer.active)` / `html:has(...) body`
   rules "reproduce the standalone route's constraints exactly" once the
   host adds `.active` — true in isolation, but wrong for the DOCKED case:
   those rules pin `.page-wrap`/`html`/`body` to `height:100vh` +
   `overflow:hidden`, which is correct standalone (Explorer alone owns the
   viewport and its OWN internal flex chain — `#se-body` → `#se-main` →
   `#se-list-pane`/`#se-detail-pane`, all `overflow:hidden/auto` — relies on
   that bounded ancestor height to know where to start scrolling
   internally). Docked inside `.spg-view-advanced`, Explorer is no longer
   alone on the page: the embed was deliberately built (2026-07-03, verified
   at 1055px) to let Explorer grow to its natural content height and have
   the DOCUMENT scroll instead. A hard `height:100vh; overflow:hidden` on
   `.page-wrap` fights that at ANY width — it was masked at 1055px (viewport
   height alone happened to roughly fit) and only became visibly broken at
   desktop widths (>1300px) where Explorer's list+detail panes are wide
   enough to reveal it needs more vertical room than one screen.
   Fix: neutralize the standalone viewport-clamp for the docked case only.
   Selector specificity, not source order, must win here — scale-explorer.css
   loads AFTER this file (app.php), so this override's selector is built
   strictly MORE specific than the base rule's `.page-wrap:has(#route-
   scale-explorer.active)` (1 id + 2 classes) by keying :has() on
   `.spg-view-advanced #route-scale-explorer.active` (1 id + 2 classes
   INSIDE :has, plus `.page-wrap` outside = 1 id + 3 classes total) so it
   wins regardless of load order. Both `!important` (matches the base
   rule's own `!important`, required to out-rank it) and the `html:has()`
   body companion rule get the identical treatment. Once these clamps are
   lifted, `#route-scale-explorer`/`#se-body`/`#se-main`'s own
   `flex:1; min-height:0; overflow:hidden` become no-ops (no bounded
   ancestor height left to clip against) and every pane — list pane
   included — flows to natural content height, letting the document scroll
   at every width. Standalone `?route=scale-explorer` never matches this
   selector (the node there is never inside `.spg-view-advanced`), so it
   stays pixel-identical. */
.page-wrap:has(.spg-view-advanced #route-scale-explorer.active) {
  overflow: visible !important;
  height: auto !important;
}
html:has(.spg-view-advanced #route-scale-explorer.active) body,
html:has(.spg-view-advanced #route-scale-explorer.active) body.has-admin-sidebar {
  overflow: visible !important;
  height: auto !important;
}

/* ── Full-viewport override (matches Scale Explorer pattern) ────── */

/* Applied to EVERY page that uses the SCP shell — scale, scale-
   collection, chord — so they all render edge-to-edge with no
   page-wrap padding.  Doctrine: the music pages are full-bleed by
   contract; only one place declares this. */
.page-wrap:has(#route-scale-collection.active),
.page-wrap:has(#route-scale.active),
.page-wrap:has(#route-chord.active) {
  padding: 0 !important;
  max-width: none !important;
  margin: 0 !important;
  width: 100% !important;
}

#route-scale-collection,
#route-scale,
#route-chord {
  background: var(--bg, #f5f3ee);
  padding: 0 !important;
  margin: 0 !important;
  width: 100%;
}

#route-scale-collection.active,
#route-scale.active,
#route-chord.active { display: flex; flex-direction: column; }

.scp-root {
  display: flex;
  flex-direction: column;
  gap: 0;
  padding: 0;
  font-family: var(--font-ui, var(--font), system-ui, sans-serif);
  color: var(--text, #1a1a18);
  background: var(--bg, #f5f3ee);
  /* Page scrolls naturally — when content (diagrams panel, panels
     row) grows beyond the viewport, the page accommodates.  The
     sidebars use `position: sticky` against the chrome above so
     they stay viewport-locked while content scrolls.  Doctrine
     2026-05-10: page may grow; sidebars never scroll-with-page.
     `--scp-chrome-h` is the combined sticky-chrome height (app-
     header + controls bar) — sidebars subtract it from `100vh` to
     compute their viewport-fit height. */
  --scp-chrome-h: 140px;
  /* Visual breathing room between the global navbar (sticky at
     top:0, z=100) and the pinned SCP chrome (z=4/5, sits behind
     the navbar).  Without this offset the control bar's column
     titles ("CHORDS", "KEY & DISPLAY", …) pin flush against the
     navbar's bottom edge — too tight visually.  Added to the
     ctrl-shell sticky `top` AND to the side-pane sticky `top`
     so the whole stack moves together. */
  --scp-chrome-top-gap: 3px;
  min-height: 100vh;
  /* Full-width — matches Scale Explorer.  No max-width / centering. */
  max-width: none;
  margin: 0;
  width: 100%;
}

/* Sticky chrome — header + controls bar stay pinned at the top of
   the viewport while the page scrolls, so the FIXED side panes
   (which start at `top: var(--scp-chrome-h)`) line up perfectly
   below them.  No gap above the pane top edge as the user
   scrolls. */
.scp-app-header {
  position: sticky;
  top: 0;
  /* Above the side panes (z-index 10) so the sticky header always
     paints OVER a pane when they overlap during scroll — the panes
     used to slide up over the control bar.  Per user 2026-06-11. */
  z-index: 12;
  background: var(--surface, #fff);
}
.scp-ctrl-shell {
  position: sticky;
  /* Stack just below the app-header — `--scp-app-header-h` is
     written by `installScpChromeMeasure` (assets/js/shared/
     scp-chrome-measure.js) from the live header height.  Default
     52px covers a typical small header until the JS measure runs.
     `--scp-chrome-top-gap` adds a small visual gap below the
     global navbar so the column titles aren't flush against it
     when the chrome pins (per user 2026-05-10). */
  top: calc(var(--scp-app-header-h, 52px) + var(--scp-chrome-top-gap, 8px));
  /* Above the side panes (z-index 10) so the control bar always paints
     OVER a pane when they overlap during scroll.  Per user 2026-06-11. */
  z-index: 11;
  background: var(--bg, #f5f3ee);
}

/* ── App header band — Scale-Explorer pattern ───────────────────── */

/* Tokens applied per CLAUDE.md "TOP-PRIORITY DOCTRINE - Design
   tokens + UI scale".  Every font-size / padding / gap / radius /
   control size references a `var(--*)` token so the user's
   Interface size + Density preferences propagate. */
.scp-app-header {
  background: var(--surface, #fff);
  border-bottom: var(--border-w) solid var(--border, #e0ddd5);
  /* Horizontal gutter matches the navbar (--space-xl) so the page header's
     left title aligns under the logo and its right buttons (gear/help/menu)
     align under the navbar's right icons. */
  padding: var(--space-sm) var(--space-xl);
  display: flex;
  /* Baseline-align all text so title / subtitle / breadcrumb sit
     on the same typographic line.  Non-text controls (home button,
     divider) self-center to stay visually centred. */
  align-items: baseline;
  gap: var(--space-md);
  flex-wrap: wrap;
  flex-shrink: 0;
}
.scp-app-header > .scp-app-divider,
.scp-app-header > .scp-app-home-btn { align-self: center; }

.scp-app-back {
  font-size: var(--font-sm);
  color: var(--text-3, #888);
  text-decoration: none;
  margin-right: var(--space-sm);
  transition: color var(--t-fast);
  white-space: nowrap;
}

.scp-app-back:hover { color: var(--accent, #e8853c); }

.scp-app-back .scp-back-arrow {
  display: inline-block;
  margin-right: var(--space-2xs);
}

.scp-app-title {
  margin: 0;
  font-size: var(--font-lg);
  font-weight: var(--weight-bold);
  white-space: nowrap;
  letter-spacing: -0.005em;
  color: var(--text, #1a1a18);
}

/* Clickable variant — title doubles as a "scale home" affordance.
   Subtle hover cue (accent colour) without making it look like a
   web-link (no underline, no permanent accent). */
.scp-app-title--link {
  cursor: pointer;
  user-select: none;
  transition: color var(--t-fast);
}
.scp-app-title--link:hover,
.scp-app-title--link:focus-visible {
  color: var(--accent, #e8853c);
  outline: none;
}

.scp-app-subtitle {
  font-size: var(--font-sm);
  color: var(--text-2, #555);
  white-space: nowrap;
}

/* Subtitle-as-button — the tuning QUICK-SWITCH affordance (2026-06-12).
   Reads as the same quiet text until hover; ▾ signals the popover. */
.scp-app-subtitle--btn {
  background: none;
  border: none;
  padding: 0;
  font-family: inherit;
  cursor: pointer;
}
.scp-app-subtitle--btn::after {
  content: ' ▾';
  font-size: 0.8em;
  color: var(--text-3, #999);
}
.scp-app-subtitle--btn:hover {
  color: var(--accent, #f2a93b);
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* ── Tuning picker — centered card-grid overlay (2026-06-12, owner's
   bookkeeping-app reference: search + instrument categories +
   horizontally spaced cards). ─────────────────────────────────────── */
.fly-tuning-overlay {
  position: fixed; inset: 0;
  z-index: 10500;
  background: rgba(20, 18, 14, 0.45);
  display: flex; align-items: flex-start; justify-content: center;
  padding: 6vh 16px;
}
.fly-tuning-panel {
  background: var(--bg-1, #fff);
  border-radius: var(--radius-lg, 14px);
  box-shadow: 0 18px 60px rgba(0, 0, 0, 0.3);
  width: min(860px, 100%);
  max-height: 84vh;
  display: flex; flex-direction: column;
  padding: var(--space-lg, 20px);
  gap: var(--space-sm, 12px);
}
.fly-tuning-head {
  display: flex; align-items: center; justify-content: space-between;
}
.fly-tuning-head h3 {
  margin: 0;
  font-size: var(--font-lg, 17px);
  font-weight: 700;
}
.fly-tuning-close {
  background: none; border: none; cursor: pointer;
  font-size: 22px; line-height: 1;
  color: var(--text-3, #999);
  padding: 2px 6px;
}
.fly-tuning-close:hover { color: var(--text, #222); }
.fly-tuning-search {
  width: 100%;
  font-size: var(--font-sm, 13px);
  padding: var(--space-sm, 10px) var(--space-md, 14px);
  border: 1.5px solid var(--border, #ddd);
  border-radius: var(--radius-md, 10px);
  outline: none;
}
.fly-tuning-search:focus { border-color: var(--accent, #f2a93b); }
.fly-tuning-body {
  overflow-y: auto;
  min-height: 0;
  flex: 1;
}
.fly-tuning-cat {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--text-3, #999);
  margin: var(--space-md, 14px) 0 var(--space-xs, 6px);
}
.fly-tuning-cat:first-child { margin-top: 0; }
.fly-tuning-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
  gap: var(--space-sm, 10px);
}
.fly-tuning-card {
  display: flex; flex-direction: column; align-items: flex-start;
  gap: 2px;
  text-align: left;
  font-family: inherit;
  background: var(--surface, #fff);
  border: 1.5px solid var(--border, #ddd);
  border-radius: var(--radius-md, 10px);
  padding: var(--space-sm, 10px) var(--space-md, 12px);
  cursor: pointer;
  transition: border-color .12s, box-shadow .12s;
}
.fly-tuning-card:hover {
  border-color: var(--accent, #f2a93b);
  box-shadow: 0 0 0 2px rgba(242, 169, 59, 0.18);
}
.fly-tuning-card.is-current {
  border-color: var(--accent, #f2a93b);
  background: rgba(242, 169, 59, 0.07);
}
.fly-tuning-card-name {
  font-size: var(--font-sm, 13px);
  font-weight: 700;
  color: var(--text, #222);
}
.fly-tuning-card-sub {
  font-size: var(--font-xs, 11px);
  color: var(--text-3, #999);
  letter-spacing: 0.04em;
}

/* Custom-tuning card + inline editor (set your own tuning). */
.fly-tuning-card--custom {
  border-style: dashed;
}
.fly-tuning-custom {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm, 10px);
  padding: var(--space-xs, 6px) 0;
}
.fly-tuning-custom-top {
  display: flex;
  align-items: center;
  gap: var(--space-sm, 10px);
}
.fly-tuning-custom-lbl {
  font-size: 11px; font-weight: 700; letter-spacing: 0.07em;
  text-transform: uppercase; color: var(--text-3, #999);
  margin-right: auto;
}
.fly-tuning-custom-count {
  min-width: 1.5em; text-align: center;
  font-size: var(--font-sm, 13px); font-weight: 600; color: var(--text, #222);
}
.fly-tuning-step {
  width: 30px; height: 30px; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--surface, #fff); color: var(--text-2, #555);
  border: 1px solid var(--border-strong, rgba(0,0,0,.18));
  border-radius: var(--radius-sm, 7px); cursor: pointer;
  font-size: 13px; line-height: 1;
}
.fly-tuning-step:hover:not(:disabled) { border-color: var(--accent, #e8874a); color: var(--accent, #e8874a); }
.fly-tuning-step:disabled { opacity: .4; cursor: default; }
.fly-tuning-custom-list { display: flex; flex-direction: column; gap: 4px; }
.fly-tuning-cs-row { display: flex; align-items: center; gap: 8px; }
.fly-tuning-cs-lbl { flex: 1; min-width: 0; font-size: var(--font-sm, 13px); color: var(--text-2, #555); }
.fly-tuning-cs-note {
  min-width: 2.4em; text-align: center;
  font-size: var(--font-md, 15px); font-weight: 700; color: var(--text, #222);
}
.fly-tuning-custom-preview {
  font-size: var(--font-sm, 13px); color: var(--text-2, #555);
  padding-top: var(--space-xs, 6px); border-top: 1px solid var(--border, #e5e1d8);
}
.fly-tuning-custom-actions { display: flex; justify-content: flex-end; gap: var(--space-sm, 10px); }
.fly-tuning-custom-cancel, .fly-tuning-custom-use {
  height: 34px; padding: 0 16px; border-radius: var(--radius-sm, 7px);
  font-size: var(--font-sm, 13px); font-weight: 600; cursor: pointer; font-family: inherit;
}
.fly-tuning-custom-cancel { background: var(--surface, #fff); color: var(--text-2, #555); border: 1px solid var(--border-strong, rgba(0,0,0,.18)); }
.fly-tuning-custom-use { background: var(--accent, #e8874a); color: #fff; border: 1px solid var(--accent, #e8874a); }
.fly-tuning-custom-use:hover { background: var(--accent-hover, #d4713a); }

/* Hint row at the bottom of the tuning picker. */
.fly-tuning-menu-hint {
  font-size: 10px;
  color: var(--text-3, #999);
  padding-top: var(--space-xs, 6px);
  border-top: 1px solid var(--border, #e5e1d8);
  white-space: nowrap;
}

/* Vertical separator between subtitle and the home / breadcrumb
   navigation cluster on the right. */
.scp-app-divider {
  display: inline-block;
  width: 1px;
  height: var(--space-xl);
  margin: 0 var(--space-xs);
  background: var(--border, #e0ddd5);
}

/* Scale home button — icon only.  Squared button mirrors the
   typographic weight of the title bar without competing visually. */
.scp-app-home-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--ctl-h-md);
  height: var(--ctl-h-md);
  padding: 0;
  border: var(--border-w) solid transparent;
  background: transparent;
  border-radius: var(--radius-md);
  cursor: pointer;
  color: var(--text-2, #555);
  transition: background var(--t-fast), color var(--t-fast), border-color var(--t-fast);
}
.scp-app-home-btn svg { width: 1.2857rem; height: 1.2857rem; display: block; }   /* 18 px @ 14px rem */
.scp-app-home-btn:hover,
.scp-app-home-btn:focus-visible {
  background: var(--surface-2, #f7f6f3);
  color: var(--accent, #e8853c);
  border-color: var(--border, #e0ddd5);
  outline: none;
}

/* ── Controls grid — Scale-Explorer .se-ctrl-grid clone ─────────── */

/* Outer shell — Scales pinned outer-LEFT, Collection pinned outer-
   RIGHT, inner action grid in between (flex: 1) wraps internally.
   Single bottom border belongs to the shell so the horizontal
   separator under the controls is one continuous line full-width. */
.scp-ctrl-shell {
  /* Edge columns are auto-sized to their content by default, but
     the page locks them to measured pixel widths via the CSS
     variables `--scp-ctrl-edge-l` / `--scp-ctrl-edge-r` once the
     bar has rendered.  Locking the COLUMN widths (not just cell
     widths) is required because edge cells live inside a CSS
     Grid — flex/width on the cell itself doesn't override the
     column track.  When the toggle text flips between
     "Show…" and "Hide…", or when the right/left list pane opens,
     the column tracks no longer reflow. */
  display: grid;
  grid-template-columns: var(--scp-ctrl-edge-l, auto) 1fr var(--scp-ctrl-edge-r, auto);
  grid-template-rows: auto auto;
  align-items: start;
  background: var(--surface, #fff);
  border-bottom: 1px solid var(--border, #e0ddd5);
  flex-shrink: 0;
  padding-bottom: 8px;
  margin-bottom: 0;
}
/* Edges and inner-grid explicit placement.  Edges use
   `align-self: start` so they don't stretch when the inner-grid
   wraps to multiple rows. */
.scp-ctrl-shell > .scp-ctrl-cell--edge:first-child { grid-column: 1; grid-row: 1; align-self: start; }
.scp-ctrl-shell > .scp-ctrl-cell--edge:last-child  { grid-column: 3; grid-row: 1; align-self: start; }
.scp-ctrl-shell > .scp-ctrl-grid                   { grid-column: 2; grid-row: 1; }

/* Inner-grid invariant — single row, no wrap.  When the grid runs
   out of horizontal room the inline cells hide via the hamburger
   media query below, NOT wrap to row 2. */
.scp-ctrl-shell > .scp-ctrl-grid {
  flex-wrap: nowrap;
  overflow: hidden;
  min-width: 0;
}

/* App-header hamburger button — visible only at narrow viewports.
   Click opens `.scp-overflow-popover` which contains every control
   (chord/scale list toggle + action cells + collection toggle +
   browse-full link).  Hidden at wide widths (≥ threshold) where
   the inline ctrlShell shows everything natively. */
.scp-app-ham {
  display: none;
  background: transparent;
  border: 1px solid var(--border, #e0ddd5);
  border-radius: 4px;
  width: 32px;
  height: 32px;
  font-size: 18px;
  line-height: 1;
  color: var(--text-2, #555);
  cursor: pointer;
  margin-right: 8px;
}
.scp-app-ham:hover {
  border-color: var(--accent, #e8853c);
  color: var(--accent, #e8853c);
}

/* Hamburger threshold — at ≤1100 CSS-px the entire ctrlShell hides
   and the app-header hamburger is the single entry-point to ALL
   controls.  Nothing lives outside the consolidated overflow menu. */
@media (max-width: 1100px) {
  .scp-ctrl-shell { display: none; }
  .scp-app-ham    { display: inline-flex; align-items: center; justify-content: center; }
}

/* Overflow popover — anchored below the hamburger.  Default layout:
   single column at 280px (narrow phones).  When the viewport has
   horizontal headroom we spread the action cells into two columns
   via the auto-fit grid below — wider, shorter, easier to scan. */
.scp-overflow-popover {
  background: var(--surface, #fff);
  border: 1px solid var(--border, #e0ddd5);
  border-radius: 6px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
  display: flex;
  flex-direction: column;
  padding: 0;
  width: 280px;
  max-height: 80vh;
  overflow-y: auto;
}
/* Two-column action grid — kicks in when the viewport ≥ 720 CSS-px.
   The action cells (every `.scp-ctrl-cell` after the nav band) flow
   into a CSS Grid with auto-fit minmax so they fill 2 columns when
   there's room.
   ROUND-4 ADDENDUM 2026-07-04: `.se-ctrl-cell` added to every selector
   below (comma-widened, not duplicated) — Advanced mode's popover
   (advanced-explorer-host.js `toggleControlsPopover()`) DOM-MOVES the
   live Explorer `.se-ctrl-cell`s in here (never rebuilds them — they
   carry directly-bound listeners), so they need the SAME popover-cell
   chrome `.scp-ctrl-cell` already gets. No new class, no copy. */
.scp-overflow-popover .scp-ctrl-cell,
.scp-overflow-popover .se-ctrl-cell {
  border-bottom: 1px solid var(--border, #ece8df);
  padding: 8px 14px;
}
.scp-overflow-popover .scp-ctrl-cell:last-child,
.scp-overflow-popover .se-ctrl-cell:last-child { border-bottom: none; }
@media (min-width: 720px) {
  .scp-overflow-popover {
    width: 540px;          /* fits 2 columns of ~250px */
    display: grid;
    grid-template-columns: 1fr 1fr;
    align-items: start;
  }
  /* Nav band keeps full width across both columns at the top. */
  .scp-overflow-popover .scp-overflow-nav {
    grid-column: 1 / -1;
  }
  /* Each action cell occupies ONE column; auto-place lets the grid
     pack them into two columns row-by-row.  Cell borders are
     vertical between columns instead of horizontal-stacking. */
  .scp-overflow-popover .scp-ctrl-cell,
  .scp-overflow-popover .se-ctrl-cell {
    border-bottom: 1px solid var(--border, #ece8df);
    border-right: 1px solid var(--border, #ece8df);
  }
  /* Right-column cells lose their right border (they're at the edge). */
  .scp-overflow-popover .scp-ctrl-cell:nth-child(2n + 1),
  .scp-overflow-popover .se-ctrl-cell:nth-child(2n + 1) {
    border-right: none;
  }
}
/* Top nav band — holds the Show-list toggles + Browse-full icon
   button.  Two stacked rows: chord-list toggle on its own row,
   then Collection toggle + Browse-full icon side-by-side on the
   next row.  All grouped at the TOP of the popover for navigation
   affordances first; controls follow underneath. */
.scp-overflow-nav {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 10px 14px;
  border-bottom: 1px solid var(--border, #ece8df);
  background: var(--surface-2, #f7f6f3);
}
.scp-overflow-nav-toggle {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
/* Browse icon sits inline AFTER the Collection toggle on the same
   row.  Use flex on the parent rows when these two share a line. */
.scp-overflow-nav .scp-ctrl-toggle:nth-of-type(2) {
  /* Collection toggle — wrapped with the browse-full icon so they
     read as one affordance group.  Adjacent siblings using flex
     via the wrapper below. */
}
.scp-overflow-nav-browse {
  flex: 0 0 auto;
}
/* Wrap Collection toggle + Browse-full icon in a flex row so they
   sit side-by-side.  Done via CSS Grid template instead of an
   extra DOM wrapper to keep the JS minimal. */
.scp-overflow-nav {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  column-gap: 8px;
  row-gap: 8px;
}
.scp-overflow-nav .scp-ctrl-toggle:first-child {
  grid-column: 1 / -1;   /* chord/scale list spans full width */
}
.scp-overflow-nav .scp-ctrl-toggle:nth-child(2) {
  grid-column: 1;         /* Collection toggle in left col */
}
.scp-overflow-nav .scp-overflow-nav-browse {
  grid-column: 2;         /* Browse-full icon in right col */
}
.scp-ctrl-shell > .scp-ctrl-grid--secondary {
  grid-column: 1 / -1;
  grid-row: 2;
  border-top: 1px solid var(--border, #e0ddd5);
}
.scp-ctrl-shell > .scp-ctrl-grid--secondary:empty { display: none; }

/* Edge cells (Scales, Collection) — fixed widths sized to their
   content.  Scales has only a single toggle (~160px); Collection
   has toggle + browse-full icon (~220px), so they get different
   widths instead of a uniform 240px. */
.scp-ctrl-cell--edge {
  background: var(--surface, #fff);
}
/* Edges stay locked to row 1 left/right by explicit flex `order`.
   Inner cells default to order:1 and flow between them; if they
   overflow they wrap to row 2 below.  Collection's high order
   keeps it pinned to the right edge of row 1, never wrapping. */
.scp-ctrl-cell--edge:first-child { flex: 0 0 150px; order: 0; }   /* Chords/Scales */
.scp-ctrl-cell--edge:last-child  { flex: 0 0 185px; order: 9999; } /* Collection */
.scp-ctrl-shell > .scp-ctrl-grid > .scp-ctrl-cell { order: 1; }
.scp-ctrl-cell--edge:first-child { border-right: 1px solid var(--border, #e0ddd5); }
.scp-ctrl-cell--edge:last-child  { border-left:  1px solid var(--border, #e0ddd5); }

.scp-ctrl-shell > .scp-ctrl-grid {
  flex: 1;
  min-width: 0;
  border-bottom: none;     /* shell owns the bottom border */
}

.scp-ctrl-grid {
  /* Inner-grid lives in column 2 of the shell.  Cells flex-wrap at
     content width (no flex-grow), packing tightly on row 1 and
     overflowing to row 2 with the wider cells wrapping together. */
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  align-content: flex-start;
  row-gap: 1px;
  min-width: 0;
}
.scp-ctrl-grid > .scp-ctrl-cell {
  flex: 0 0 auto;
  min-width: max-content;
  border-right: 1px solid var(--border, #e0ddd5);
}
.scp-ctrl-grid > .scp-ctrl-cell:last-child {
  border-right: none;
}
@media (max-width: 480px) {
  .scp-ctrl-shell { flex-direction: column; }
  .scp-ctrl-cell--edge:first-child { border-right: none; border-bottom: 1px solid var(--border, #e0ddd5); }
  .scp-ctrl-cell--edge:last-child  { border-left:  none; border-top:    1px solid var(--border, #e0ddd5); }
  .scp-ctrl-grid { grid-template-columns: 1fr; }
}

/* ── Side-pane DRAWER mode at narrow viewports (≤ 1300 px) ─────────
   2026-07-02 (owner): REPLACES the old "stack mode" (2026-05-18),
   which pushed the main content below the panes — content below the
   fold is an anti-pattern; the fretboard must be visible at every
   width.  Below the breakpoint the side panes become fixed
   OFF-CANVAS DRAWERS sliding OVER the content (left pane from the
   left edge, Collection pane from the right), under the sticky
   chrome, with a scrim behind.  Content keeps full width always.

   Open/close is still driven by the SAME `.scp-main--list-open` /
   `.scp-main--coll-list-open` classes, toggles, persistence and the
   `fretly:close-side-pane` event.  Behaviours CSS can't express
   (scrim click, default-closed on entering drawer mode, one drawer
   at a time, close-on-leaf-pick) live in
   assets/js/shared/scp-drawer.js — keep breakpoints in sync.

   Breakpoint = --bp-scp-drawer (fretly-tokens.css).  Media queries
   can't read var(); the token documents the canonical value, this
   literal mirrors it.  A @container twin near the end of this file
   fires on --bp-scp-drawer-container against .scp-main's own width
   (admin sidebar, docked dev-tools). */
@media (max-width: 1300px) {
  /* Content claims the full row — the panes are fixed (out of
     flow), so nothing reserves space and nothing gets pushed. */
  .scp-shell-content,
  .scp-pane-promoted .scp-main--list-open      .scp-shell-content,
  .scp-pane-promoted .scp-main--coll-list-open .scp-shell-content {
    margin-left:  0 !important;
    margin-right: 0 !important;
    width: 100%;
    min-width: 0;
  }
  /* Both panes: fixed drawers under the sticky chrome.  Same
     geometry math as the full-window sticky panes; the pane keeps
     its own internal scroll (.scp-list-rows) exactly like
     full-window mode — no stacked-mode height hacks. */
  .scp-shell-list,
  .scp-shell-list--coll,
  .scp-pane-promoted .scp-shell-list,
  .scp-pane-promoted .scp-shell-list--coll {
    position: fixed !important;
    top: calc(var(--scp-chrome-h, 140px) + var(--scp-chrome-top-gap, 3px)) !important;
    height: calc(100vh - var(--scp-chrome-h, 140px) - var(--scp-chrome-top-gap, 3px)) !important;
    max-height: none !important;
    width: min(320px, 85vw) !important;
    max-width: 85vw !important;
    flex: 0 0 auto !important;
    margin: 0 !important;
    z-index: 60 !important;
    opacity: 1 !important;
    background: var(--surface, #fff);
    transition: transform 0.22s ease, box-shadow 0.22s ease;
    pointer-events: none !important;
  }
  .scp-shell-list:not(.scp-shell-list--coll),
  .scp-pane-promoted .scp-shell-list:not(.scp-shell-list--coll) {
    left: 0 !important;
    right: auto !important;
    transform: translateX(-105%) !important;
    border-right: 1px solid var(--border, #e0ddd5);
  }
  .scp-shell-list--coll,
  .scp-pane-promoted .scp-shell-list--coll {
    right: 0 !important;
    left: auto !important;
    transform: translateX(105%) !important;
    border-left: 1px solid var(--border, #e0ddd5);
  }
  .scp-main--list-open .scp-shell-list:not(.scp-shell-list--coll),
  .scp-pane-promoted .scp-main--list-open .scp-shell-list:not(.scp-shell-list--coll),
  .scp-main--coll-list-open .scp-shell-list--coll,
  .scp-pane-promoted .scp-main--coll-list-open .scp-shell-list--coll {
    transform: none !important;
    pointer-events: auto !important;
    box-shadow: 0 8px 32px rgba(0, 0, 0, 0.25);
  }
  /* Scrim — element created by scp-drawer.js; shown while a drawer
     is open.  Sits above content (z 55) and below the drawer (60).
     Starts below the chrome so the controls stay reachable. */
  .scp-drawer-scrim { display: none; }
  .scp-main--list-open > .scp-drawer-scrim,
  .scp-main--coll-list-open > .scp-drawer-scrim {
    display: block;
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    top: calc(var(--scp-chrome-h, 140px) + var(--scp-chrome-top-gap, 3px));
    background: rgba(20, 16, 8, 0.35);
    z-index: 55;
  }
  /* Footer padding reserved for promoted fixed panes is meaningless
     under drawers (drawers overlay; nothing is displaced). */
  body:has(.scp-pane-promoted .scp-main--list-open)      .site-footer { padding-left: 0; }
  body:has(.scp-pane-promoted .scp-main--coll-list-open) .site-footer { padding-right: 0; }
}

.scp-ctrl-cell {
  /* Action cells stay tight — the visible width of each is governed
     by the inner grid's 1fr columns, this padding just controls
     internal breathing room. */
  padding: var(--space-xs) var(--space-sm);
  background: var(--surface, #fff);
  overflow: hidden;
  min-width: 0;
}

/* Edge cells get a touch more breathing room since their content
   (label + toggle) is larger than an action cell's btn-group. */
.scp-ctrl-cell--edge {
  padding: var(--space-xs) var(--space-md);
}

/* Inactive control — current view doesn't use this setting.  Faded +
   click-blocked but still readable so the user knows what each cell
   would do once the prereqs (e.g. switch to Diagram mode, pick a
   non-Arpeggio voicing source) are met. */
.scp-ctrl-cell--disabled {
  opacity: 0.45;
  pointer-events: none;
  filter: saturate(0.4);
}
.scp-ctrl-cell--disabled .scp-ctrl-label {
  /* Keep the label tone — only the inputs read as inactive. */
  color: var(--text-3, #888);
}

.scp-ctrl-label {
  font-size: var(--font-xs);
  font-weight: var(--weight-bold);
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--text-3, #888);
  margin-bottom: var(--space-sm);
}
/* Admin-controlled label case (FretlyDisplayConfig.controlLabelCase).
   Default = upper (rule above). 'sentence' = quiet sentence-case. */
html[data-label-case="sentence"] .scp-ctrl-label {
  font-size: var(--font-sm);
  font-weight: var(--weight-medium);
  letter-spacing: 0;
  text-transform: none;
  color: var(--text-2, #555);
}

.scp-ctrl-toggle {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  padding: 0;
  background: none;
  border: none;
  cursor: pointer;
  font-size: var(--font-sm);
  font-weight: var(--weight-semibold);
  color: var(--text-2, #555);
  font-family: inherit;
  transition: color var(--t-fast);
  text-align: left;
  white-space: nowrap;
}

/* Panel-icon variant of the list toggles (replaces the "Show/Hide … list"
   text — owner 2026-06-19, reduce control-bar text). Accent when open. */
.scp-ctrl-toggle .scp-panel-ico { width: 24px; height: 24px; display: block; }
.scp-ctrl-toggle.is-open { color: var(--accent); }
/* Edge-cell panel-icon toggles: match the other control-bar controls' row
   height (28px) so the icon's vertical centre lines up with the selects /
   switches beside it (measured centre 41px from the bar top — owner
   2026-06-19, "really LOOK and make it make sense visually").  The right
   (Collection) toggle fills the cell and pushes its icon to the right. */
.scp-ctrl-cell--edge .scp-ctrl-toggle { display: flex; align-items: center; height: 28px; min-height: 0; }
.scp-ctrl-toggle--right { width: 100%; justify-content: flex-end; }

.scp-ctrl-toggle-label {
  display: inline-block;
  /* No min-width — let the label size to its content.  Earlier a
     135px min-width was set to prevent reflow on Show↔Hide swap,
     but both labels are the same character count ("Show Collection
     list" / "Hide Collection list") so they have the same natural
     width and no reflow happens.  Smaller cell = more room for
     col-2 inner cells. */
}

.scp-ctrl-toggle:hover { color: var(--accent, #e8853c); }

.scp-ctrl-toggle .chevron {
  display: inline-block;
  transition: transform 0.2s;
  font-size: 10px;
}

.scp-ctrl-toggle .chevron.open { transform: rotate(90deg); }

.scp-ctrl-text {
  font-size: 12px;
  font-weight: 600;
  color: var(--text-2, #555);
}

/* Panels cell — icon-button group laid out as a horizontal row.
   `.toolbar-panel-icon` itself is styled globally in site.css so the
   icons render the same as the legacy `?route=scales` toolbar. */
.spg-panel-icons {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

/* Collection cell — toggle on the LEFT, browse-full icon on the
   RIGHT.  Total right inset must equal `.scp-list-browse-row`'s
   17px so the / arrow centre lines up with the home button centre
   directly below it.  Parent `.scp-ctrl-cell--edge` already adds
   10px padding-right, so this row only needs +7px to land at the
   same X.  Verified by matching button-centre-to-pane-edge. */
.scp-ctrl-coll-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding-right: 7px;
}

.scp-ctrl-browse-btn {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* 26×26 — same square size as the home button in the side pane
     below, so the browse-full arrow centre aligns with the home
     button centre on the same X. */
  width: 26px;
  height: 26px;
  border: 1px solid var(--border, #e0ddd5);
  background: var(--surface, #fff);
  border-radius: 5px;
  cursor: pointer;
  font-family: inherit;
  font-size: 13px;
  color: var(--accent, #e8853c);
  transition: background 120ms, color 120ms, border-color 120ms;
}
.scp-ctrl-browse-btn:hover {
  background: var(--accent, #e8853c);
  color: #fff;
  border-color: var(--accent, #e8853c);
}

/* Active state — visible when the page is in Collection mode.  Reads
   as a clearly-filled toggle so the user knows clicking returns them
   to Diagram view.  Same visual as `:hover` but persistent. */
.scp-ctrl-browse-btn--active,
.scp-ctrl-browse-btn--active:hover {
  background: var(--accent, #e8853c);
  color: #fff;
  border-color: var(--accent, #e8853c);
}

/* Key & Display cell — inline row with key select + display cycle. */
.scp-kd-row {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.scp-kd-select {
  font-family: inherit;
  font-size: 12px;
  font-weight: 600;
  color: var(--text, #1a1a18);
  padding: 3px 22px 3px 8px;
  border: 1px solid var(--border, #e0ddd5);
  border-radius: 4px;
  background: var(--surface, #fff);
  appearance: none;
  -webkit-appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23999' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='4,6 8,10 12,6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 4px center;
  background-size: 10px;
  cursor: pointer;
  transition: border-color 120ms;
  /* Stable width — accommodates two-char keys (C#, Bb) without
     the cell reflowing when the key changes. */
  min-width: 56px;
}
.scp-kd-select:hover  { border-color: var(--text-3, #999); }
.scp-kd-select:focus  { border-color: var(--accent, #e8853c); outline: none; box-shadow: 0 0 0 2px rgba(232, 135, 74, 0.15); }

/* Segmented btn-group (List | Browse). */
.scp-btn-group {
  display: flex;
  gap: 0;
}

.scp-btn-group-btn {
  font-size: var(--font-sm);
  padding: var(--space-xs) var(--space-md);
  border: var(--border-w) solid var(--border, #e0ddd5);
  background: var(--surface, #fff);
  color: var(--text-2, #555);
  cursor: pointer;
  white-space: nowrap;
  font-family: inherit;
  transition: background var(--t-fast), color var(--t-fast), border-color var(--t-fast);
}

.scp-btn-group-btn:first-child { border-radius: var(--radius-sm) 0 0 var(--radius-sm); }
.scp-btn-group-btn:last-child  { border-radius: 0 var(--radius-sm) var(--radius-sm) 0; }
.scp-btn-group-btn:not(:first-child) { border-left: none; }

.scp-btn-group-btn:hover { background: var(--surface-2, #f7f6f3); }

.scp-btn-group-btn.active {
  background: var(--accent, #e8853c);
  color: #fff;
  border-color: var(--accent, #e8853c);
}

.scp-btn-group-btn.active + .scp-btn-group-btn {
  border-left-color: var(--accent, #e8853c);
}

/* ── UNIFIED CONTROL SIZING ───────────────────────────────────────
   Every interactive control inside a `.scp-ctrl-cell` (select, input,
   btn-group btn, popover trigger) shares the same height + padding
   + border style so the cell reads as one cohesive row.  Single
   source of truth — change here, every cell follows.  Lock heights
   so cells don't reflow on hover / focus / active. */
:where(.scp-ctrl-cell, .cv-toolbar) :is(select, input[type="text"], input[type="search"]),
:where(.scp-ctrl-cell, .cv-toolbar) .scp-btn-group-btn,
:where(.scp-ctrl-cell, .cv-toolbar) .scp-kd-select,
:where(.scp-ctrl-cell, .cv-toolbar) .scp-ctrl-select,
:where(.scp-ctrl-cell, .cv-toolbar) .fly-select-trigger,
:where(.scp-ctrl-cell, .cv-toolbar) .spg-fpop-trigger {
  box-sizing: border-box;
  height: 28px;
  font-size: 12px;
  font-family: inherit;
  font-weight: 500;
  border: 1px solid var(--border, #e0ddd5);
  background: var(--surface, #fff);
  color: var(--text-2, #555);
  border-radius: 4px;
  padding: 0 10px;
  cursor: pointer;
  white-space: nowrap;
  line-height: 1;
  transition: background 120ms, color 120ms, border-color 120ms;
}

/* Native selects — refined chrome:
   - Custom caret (SVG chevron) right-aligned.
   - Subtle inner shadow for depth.
   - Smooth hover/focus transitions.
   - Slight scale + accent ring on focus. */
:where(.scp-ctrl-cell, .cv-toolbar) :is(select, .scp-kd-select, .scp-ctrl-select, .fly-select-trigger) {
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  padding: 0 26px 0 12px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23888' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='4,6 8,10 12,6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 8px center;
  background-size: 11px;
  background-color: var(--surface, #fff);
  border: 1px solid var(--border, #e0ddd5);
  border-radius: 5px;
  box-shadow: 0 1px 0 rgba(0,0,0,0.02), inset 0 1px 0 rgba(255,255,255,0.6);
  transition: border-color 120ms ease, box-shadow 120ms ease, background-color 120ms ease;
  cursor: pointer;
  font: inherit;
  color: var(--text, #1a1a18);
}
:where(.scp-ctrl-cell, .cv-toolbar) :is(select, .scp-kd-select, .scp-ctrl-select, .fly-select-trigger):hover {
  border-color: var(--text-3, #999);
  background-color: var(--surface, #fff);
  box-shadow: 0 1px 3px rgba(0,0,0,0.05);
}
:where(.scp-ctrl-cell, .cv-toolbar) :is(select, .scp-kd-select, .scp-ctrl-select, .fly-select-trigger):focus {
  outline: none;
  border-color: var(--accent, #e8853c);
  box-shadow: 0 0 0 3px rgba(232, 135, 74, 0.18);
}
:where(.scp-ctrl-cell, .cv-toolbar) :is(select, .scp-kd-select, .scp-ctrl-select, .fly-select-trigger):disabled {
  opacity: 0.55;
  cursor: not-allowed;
  box-shadow: none;
}

/* Popover triggers are <button>s — centre-aligned by default, unlike
   selects.  Left-align + a sensible min-width so they read exactly
   like the selects they replace. */
:where(.scp-ctrl-cell, .cv-toolbar) .fly-select-trigger {
  text-align: left;
  min-width: 64px;
}

/* btn-group buttons keep adjacent-borderless segments so the visual
   groups read as one rounded pill. */
:where(.scp-ctrl-cell) .scp-btn-group-btn:first-child  { border-radius: 4px 0 0 4px; }
:where(.scp-ctrl-cell) .scp-btn-group-btn:last-child   { border-radius: 0 4px 4px 0; }
:where(.scp-ctrl-cell) .scp-btn-group-btn:not(:first-child) { border-left: none; }

/* Active-state styling shared by btn-group + popover trigger so the
   "Computed" trigger reads the same as the "Browse" / "Notes"
   active button. */
:where(.scp-ctrl-cell) .scp-btn-group-btn.active,
:where(.scp-ctrl-cell) .spg-fpop-trigger--open {
  background: var(--accent, #e8853c) !important;
  color: #fff !important;
  border-color: var(--accent, #e8853c) !important;
}

/* Hover — uniform across every control. */
:where(.scp-ctrl-cell) :is(select, input, .scp-btn-group-btn, .spg-fpop-trigger):hover {
  border-color: var(--text-3, #999);
}
:where(.scp-ctrl-cell) :is(select, input):focus,
:where(.scp-ctrl-cell) .spg-fpop-trigger:focus {
  border-color: var(--accent, #e8853c);
  outline: none;
  box-shadow: 0 0 0 2px rgba(232, 135, 74, 0.15);
}

/* Cell-internal row — same gap everywhere so cells read as a
   coherent grid.  Stable widths on selects so cells don't reflow
   when the active value changes. */
.scp-ctrl-cell .scp-kd-row,
.scp-ctrl-cell .spg-fingerings-row,
.scp-ctrl-cell .spg-fpop-host {
  gap: 6px;
  align-items: center;
  /* When the cell is too narrow for all controls in one line, stack
     them inside the cell instead of clipping.  Each control keeps
     its 28px height so the cell just grows vertically. */
  display: flex;
  flex-wrap: wrap;
  row-gap: 4px;
}
.scp-ctrl-cell select.scp-kd-select { min-width: 56px; }
.scp-ctrl-cell select.scp-ctrl-select { min-width: 80px; }

/* Standalone sort-toggle button (Lab's ⇥ next to the SET dropdown).
   Same EXACT box as the dropdown next to it (28×28 square, same
   border, same border-radius) so it can't influence row height OR
   horizontal flow.  Glyph centred via flex.  flex:0 0 auto so it
   never grows; `vertical-align: middle` keeps the button on the
   selects' baseline even if a sibling has a slightly taller line-
   box (avoids the 1-2px nudge the user reported). */
.scp-ctrl-cell .scp-ctrl-sortbtn {
  box-sizing: border-box;
  flex: 0 0 auto;
  width: 28px;
  height: 28px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  vertical-align: middle;
  padding: 0;
  font-size: 13px;
  line-height: 1;
  font-family: inherit;
  font-weight: 500;
  color: var(--text-2, #555);
  background: var(--surface, #fff);
  border: 1px solid var(--border, #e0ddd5);
  border-radius: 4px;
  cursor: pointer;
  white-space: nowrap;
  transition: background 120ms, color 120ms, border-color 120ms;
}
.scp-ctrl-cell .scp-ctrl-sortbtn:hover {
  border-color: var(--accent, #e8853c);
  color: var(--accent, #e8853c);
}
.scp-ctrl-sortbtn-glyph {
  font-size: 13px;
  line-height: 1;
  opacity: 0.7;
}
.scp-ctrl-sortbtn-label {
  font-variant-numeric: tabular-nums;
}
/* Prevent btn-group buttons from being squeezed out — once any cell
   forces wrap, every control inside keeps its natural width. */
.scp-ctrl-cell .scp-btn-group { flex-shrink: 0; }
.scp-ctrl-cell .scp-btn-group-btn { flex: 0 0 auto; }
.scp-ctrl-cell input[type="text"] { flex: 0 1 auto; min-width: 60px; }

/* ── Toggle switch (.scp-switch) ──────────────────────────────────
   Compact two-state switch used by KEY & DISPLAY (Notes/Degrees),
   CONNECTIONS, GHOST NOTES, VIEW.  Sliding-thumb track + adjacent
   text label that reflects the current state.  Replaces side-by-
   side btn-group pairs where binary semantics are sufficient. */
.scp-switch {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  user-select: none;
  font-size: 12px;
  font-weight: 600;
  color: var(--text-2, #555);
  white-space: nowrap;
  /* Match the 28px height of selects + buttons in the controls grid
     so the switch's vertical centre lines up with every other
     control's centre.  Without this, the 18px-tall track sits
     higher than the 28px-tall selects/buttons under the same
     label and the row looks jagged. */
  height: 28px;
}
.scp-switch input { display: none; }
.scp-switch-track {
  position: relative;
  width: 32px;
  height: 18px;
  background: var(--surface-2, #d8d4cc);
  border: 1px solid var(--border, #c8c4ba);
  border-radius: 10px;
  transition: background 0.15s, border-color 0.15s;
  flex: 0 0 auto;
}
.scp-switch-thumb {
  position: absolute;
  top: 1px;
  left: 1px;
  width: 14px;
  height: 14px;
  background: #fff;
  border-radius: 50%;
  transition: left 0.15s;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.18);
}
.scp-switch input:checked + .scp-switch-track {
  background: var(--accent, #e8853c);
  border-color: var(--accent, #e8853c);
}
.scp-switch input:checked + .scp-switch-track .scp-switch-thumb {
  left: 16px;
}
.scp-switch:hover .scp-switch-track {
  filter: brightness(1.04);
}
.scp-switch-state {
  font-size: 12px;
  color: var(--text-1, #222);
  /* Single uniform width across every two-state switch in the
     controls grid — fits the longest label any cell uses
     ("String set"), keeps every cell visually balanced.  Inline
     min-width pins per-cell are no longer needed. */
  min-width: 64px;
}
/* Disabled state for a whole switch cell (owner 2026-07-11) --
   e.g. Connections/Ghost notes/View on the chord page's Full
   Fretboard view, where the control has no effect.  Same visual
   language as .cpg-pick-disabled (dim + non-interactive) so every
   disabled control in the bar reads consistently. */
.scp-switch--disabled {
  opacity: 0.5;
  pointer-events: none;
}
.scp-switch--disabled .scp-switch-track {
  cursor: default;
}


/* `auto-fit minmax(170px, 1fr)` already handles narrow viewports —
   cells wrap to additional rows as needed, no media query required. */

/* ── Main body — Scale-Explorer-style side-collapse ──────────────── */

/* Flex row: list pane on left, content on right.  List pane is
   ALWAYS in the DOM but collapses to 0 width via class on parent
   so the slide transition works smoothly.  Content takes whatever
   remains. */
.scp-main {
  /* Use flex-start alignment — sidebars are `position: sticky` and
     their height is computed against the viewport; we don't want
     `align-items: stretch` to force them to the parent row's
     height (which would be the whole page once content grows). */
  display: flex;
  align-items: flex-start;
  gap: 0;
  min-width: 0;
  padding: 0;
  flex: 1;
  /* Container-query anchor — see @container rule near the end of
     this file.  Lets the stack/un-stack decision react to the
     actual main-pane width rather than just the viewport. */
  container-type: inline-size;
  container-name: scp-main;
  /* Stretch main to at least viewport so empty pages still fill
     the screen.  When content grows, main grows with it; the
     sticky sidebars stay at viewport via their own `top` +
     `height` math. */
  min-height: 100vh;
}

.scp-shell-list {
  flex: 0 0 0;
  width: 0;
  /* STICKY pane — locked to the chrome's bottom edge (per user
     2026-05-18: "Search bar and Filter bar should stick
     underneath the Control bar at full window.  Now, there is
     a big grey gap.").  Pane stays glued to chrome-bottom as
     the user scrolls; its inner content (title + search + rows)
     scrolls inside the pane's own height. */
  overflow-x: hidden;
  overflow-y: auto;
  overscroll-behavior: contain;
  opacity: 0;
  border-right: 1px solid transparent;
  background: var(--surface, #fff);
  transition:
    opacity 0.15s ease,
    border-color 0.15s ease;
  position: sticky;
  top: calc(var(--scp-chrome-h, 140px) + var(--scp-chrome-top-gap, 3px));
  height: calc(100vh - var(--scp-chrome-h, 140px) - var(--scp-chrome-top-gap, 3px));
  align-self: flex-start;
  z-index: 10;
  pointer-events: none;     /* re-enabled when open */
}
/* Sticky panes participate in flex flow — flex-row order in
   `.scp-main` (left pane → content → right pane) is sufficient;
   no explicit left/right anchoring needed. */

/* ── Drag-to-resize handle (docked ≥1300px layout ONLY) ──────────────
   Installed by `shared/scp-pane-resize.js` on both `.scp-shell-list`
   (item pane, handle on its RIGHT edge) and `.scp-shell-list--coll`
   (Collection pane, handle on its LEFT edge — same visual edge that
   touches `.scp-shell-content` in both cases).  `.scp-shell-list` is
   `position: sticky`, which establishes an offset container for this
   absolutely-positioned child same as `position: relative` would.
   Hit area is wider than the visible line (per CLAUDE.md instructions:
   "6-8px hit area, visible on hover") so it's easy to grab without
   stealing space from the pane's own border. The module never installs
   this in drawer mode (see scp-pane-resize.js's drawerMode() gate), so
   these rules are simply unused there — no `!important` fights needed
   with the drawer's fixed-width blocks. */
.scp-pane-resize-handle {
  position: absolute;
  top: 0;
  bottom: 0;
  width: var(--space-sm, 8px);
  cursor: col-resize;
  z-index: 11;
  background: transparent;
  touch-action: none;
}
.scp-shell-list:not(.scp-shell-list--coll) .scp-pane-resize-handle {
  right: calc(var(--space-sm, 8px) / -2);
}
.scp-shell-list--coll .scp-pane-resize-handle {
  left: calc(var(--space-sm, 8px) / -2);
}
.scp-pane-resize-handle::after {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  left: 50%;
  width: var(--border-w, 1px);
  transform: translateX(-50%);
  background: transparent;
  transition: background-color var(--t-fast, 120ms) ease;
}
.scp-pane-resize-handle:hover::after,
.scp-pane-resizing .scp-pane-resize-handle::after {
  background: var(--accent, #e8853c);
  width: var(--border-w-thick, 2px);
}
/* While dragging, prevent text selection app-wide (the pointer stays
   captured on `window`, not the handle, so the drag survives leaving
   the handle's small hit area). */
body.scp-pane-resizing {
  cursor: col-resize;
  user-select: none;
}
body.scp-pane-resizing .scp-pane-resize-handle {
  cursor: col-resize;
}

.scp-shell-list .scp-list-rows {
  /* Inner rows still benefit from containment — keeps the chain
     fully sealed if `.scp-list-rows` itself ever becomes a scroll
     container in a future layout change. */
  overscroll-behavior: contain;
}

/* ── Promoted panes (scroll past chrome's natural y) ─────────────
   Added by `installScpPanePin` (assets/js/shared/scp-pane-pin.js)
   once `window.scrollY` exceeds the chrome's natural top.  Switches
   panes from `position: sticky` to `position: fixed` so they stay
   viewport-locked when scrolling past `.scp-main`'s bottom (e.g.
   into a global footer below `.scp-root`).
   Single-line reversibility: don't call `installScpPanePin` and
   the class is never added; rules below stay inert. */
.scp-pane-promoted .scp-shell-list {
  position: fixed;
}
.scp-pane-promoted .scp-shell-list:not(.scp-shell-list--coll) { left: 0; }
.scp-pane-promoted .scp-shell-list--coll                       { right: 0; }
/* Reserve column space when promoted to fixed (pane is out of
   flex flow, so the content area would otherwise expand under it).
   Use MARGIN — not padding — so the existing `padding: 12px 24px
   24px` on `.scp-shell-content` keeps its 24px inner breathing
   room.  Padding here would override padding-left/right and the
   inner content would shift 24px when promoted (sticky mode adds
   280px pane + 24px content padding = 304px from main edge;
   promoted mode with `padding-left: 280px` puts content at 280px
   — visible misalignment per user 2026-05-10). */
.scp-pane-promoted .scp-main--list-open      .scp-shell-content { margin-left:  var(--scp-pane-w, 280px); }
.scp-pane-promoted .scp-main--coll-list-open .scp-shell-content { margin-right: var(--scp-pane-w-coll, 280px); }

/* Global footer (`<footer class="site-footer">` in app.php, lives
   OUTSIDE `.scp-root` as a sibling of `<main class="page-wrap">`)
   slides under the fixed panes when scrolled into view, since the
   panes are viewport-anchored at the document edges.  Pad the
   footer's inner content so it fits in the space BETWEEN the
   panes — same amounts that the content area uses.  `:has()` is
   the cleanest reach: rule fires only when both the promoted state
   AND the corresponding open-pane state are active.  Reversible:
   if the JS module isn't installed, `.scp-pane-promoted` never
   appears and these rules are inert. */
body:has(.scp-pane-promoted .scp-main--list-open)      .site-footer { padding-left:  var(--scp-pane-w, 280px); }
body:has(.scp-pane-promoted .scp-main--coll-list-open) .site-footer { padding-right: var(--scp-pane-w-coll, 280px); }

/* Show-scale-list toggle expands ONLY the scales pane (first
   .scp-shell-list, NOT the Collection-list variant).
   Width is a CSS custom property — `--scp-pane-w` — so the drag-resize
   module (shared/scp-pane-resize.js) can drive it live without CSS ever
   losing single ownership of the layout math.  Falls back to the
   historical 280px default when the module hasn't set it (e.g. narrow
   viewports where drawer mode owns width instead — see the drawer
   blocks below, which `!important`-override this with the drawer's own
   width and are therefore unaffected). */
.scp-main--list-open .scp-shell-list:not(.scp-shell-list--coll) {
  flex: 0 0 var(--scp-pane-w, 280px);
  width: var(--scp-pane-w, 280px);
  opacity: 1;
  border-right-color: var(--border, #e0ddd5);
  pointer-events: auto;
}
/* No padding reservation on .scp-shell-content — sticky panes
   are flex items and naturally claim their width from the row when
   open.  Adding padding-left would DOUBLE-RESERVE the space and
   create an empty grey strip between the pane and content. */

/* Collection-list pane — OVERLAY treatment.  Sits absolutely on top
   of the right edge of `.scp-main` instead of stealing flex space, so
   the content area's width never changes when the pane toggles.  This
   prevents the right-side info panels (Modes / Scale Circle) from
   colliding when the pane opens — they see a constant container
   width regardless of pane state.
   Closed: zero footprint (pointer-events disabled so clicks pass to
   content beneath the would-be pane region).  Open: 320 px wide,
   subtle left-edge shadow so it reads as floating on top. */
/* Collection-list pane — INLINE flex pane, behaves identically to
   the scale-list pane on the left.  Same snap-open transition (no
   width animation), same sticky-top behaviour, same overscroll
   containment so scrolling the pane never bubbles up to the page. */
.scp-shell-list--coll {
  border-right: none;
  border-left: 1px solid transparent;
  transition:
    opacity 0.15s ease,
    border-left-color 0.15s ease;
  overscroll-behavior: contain;
  /* App-shell — match scp-shell-list's viewport-anchored sizing.
     `position: sticky` is removed; pane now fills its flex slot in
     `.scp-main` (which is viewport-height-minus-chrome). */
}
.scp-shell-list--coll .scp-list-rows {
  overscroll-behavior: contain;
}

/* Close (×) button — only meant for an overlay variant of the pane.
   Hidden in the default inline mode since the toggle button in the
   controls bar is reachable. */
.scp-shell-list-close-btn { display: none; }

/* Close (×) button on side panes — visible ONLY at narrow viewports
   (≤ 1100 px) where the controls bar collapses into a hamburger and
   the toggle that would otherwise close the pane is hidden behind a
   menu.  Pinned at the TOP-LEFT of the pane (NOT top-right — the
   search input's own clear-× lives there).  Sticky inside the pane
   so it stays in view while the user scrolls.  Pairs with ESC +
   LIFO stack on the page. */
/* Title bar — always visible at the top of every side pane.  Hosts:
     · pane title + item count on the left
     · bulk Expand/Collapse text-link in the middle/right
     · "Close" word button on the right (visible only at narrow
       viewports where the controls-bar toggle is hidden)
   Sticky inside the pane's scroll container so it stays in view
   while the user scrolls inside.  Replaces the prior split
   close-row + header-row stack. */
.scp-list-titlebar {
  position: sticky;
  top: 0;
  z-index: 4;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 8px 12px 8px 12px;
  background: var(--surface-2, #f7f6f3);
  border-bottom: 1px solid var(--border, #e0ddd5);
  font-size: 12px;
  font-weight: 600;
  color: var(--text-2, #555);
}
.scp-list-titlebar-title--btn {
  /* Strip every native <button> visual — appearance, border,
     background, padding, font.  The element reads exactly like the
     plain heading it replaces; only `cursor: pointer` and the
     hover tint hint at its clickability. */
  appearance: none;
  -webkit-appearance: none;
  background: transparent;
  border: 0;
  padding: 0;
  margin: 0;
  text-align: left;
  font: inherit;
  color: inherit;
  cursor: pointer;
}
.scp-list-titlebar-title--btn:hover { color: var(--accent, #e8853c); }
.scp-list-titlebar-title--btn:focus { outline: none; }
.scp-list-titlebar-title--btn:focus-visible { outline: 2px solid var(--accent, #e8853c); outline-offset: 2px; border-radius: 2px; }
.scp-list-titlebar-title {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  letter-spacing: 0.02em;
}
.scp-list-titlebar-actions {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.scp-list-close-btn {
  flex: 0 0 auto;
  display: none;
  border: 0.5px solid var(--border, #e0ddd5);
  background: var(--surface, #fff);
  color: var(--accent, #e8853c);
  font-size: 11px;
  font-weight: 600;
  font-family: inherit;
  padding: 4px 10px;
  border-radius: 4px;
  cursor: pointer;
  transition: background 100ms, color 100ms, border-color 100ms;
}
.scp-list-close-btn:hover {
  background: var(--accent, #e8853c);
  color: #fff;
  border-color: var(--accent, #e8853c);
}
@media (max-width: 1100px) {
  .scp-list-close-btn { display: inline-flex; align-items: center; }
}

/* Show-Collection-list toggle expands ONLY the Collection pane.
   `--scp-pane-w-coll` mirrors `--scp-pane-w` on the item pane above —
   see that rule's comment for the drag-resize + fallback rationale. */
.scp-main--coll-list-open .scp-shell-list--coll {
  flex: 0 0 var(--scp-pane-w-coll, 280px);
  width: var(--scp-pane-w-coll, 280px);
  opacity: 1;
  border-left-color: var(--border, #e0ddd5);
  pointer-events: auto;
}
/* No padding reservation here — sticky Collection pane is a
   flex item and claims its width from the row.  Padding-right
   would double-reserve and create an empty grey strip. */

.scp-shell-content {
  flex: 1 1 auto;
  min-width: 0;
  /* Content area flows naturally — when the diagrams panel grows
     beyond viewport, the PAGE scrolls (not a nested scroll
     container).  Per user 2026-05-10: diagrams panel must NEVER
     have a scrollbar; growth pushes the page.  The side panes are
     `position: fixed` (viewport-anchored), so they stay put while
     the page scrolls underneath them — every row remains
     reachable via the pane's INTERNAL scroll. */
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: 12px;
  /* Tighter top padding so the breadcrumb sits closer to the
     control bar above; sides + bottom keep the regular 24px
     breathing room.  The gap below the breadcrumb (12px from
     `.spg-view-collection`) matches this top padding so the
     breadcrumb row reads as visually centred between the bar
     and the cards beneath. */
  padding: 12px 24px 24px;
}

/* ── Drawer-overlay panes for narrow desktop widths ──────────────
   Per user 2026-05-10 (Option A — overlay drawer pattern):
   between 900 and 1100 px the desktop sticky panes claim too much
   horizontal real-estate (280px each + content = squeezed layout).
   Promote the panes to fixed-position drawers that overlay the
   content area, with a backdrop dimming the rest of the page.
   Standard mobile/Slack pattern.
     - Closed: pane translateX(±100%), pointer-events: none.
     - Open: pane translateX(0), backdrop visible.
   Reversibility: remove this @media block and behaviour reverts
   to current desktop sticky panes at all widths >900px. */
@media (max-width: 1100px) and (min-width: 901px) {
  /* Drawer base — viewport-fixed, slid off-screen by default. */
  .scp-shell-list {
    position: fixed;
    top: var(--scp-chrome-h, 140px);
    bottom: 0;
    height: auto;
    flex: 0 0 0;
    width: 320px;
    z-index: 100;
    transform: translateX(-100%);
    transition: transform 0.18s ease, opacity 0.15s ease;
    box-shadow: 0 0 24px rgba(0, 0, 0, 0.15);
    /* Reset desktop sticky-only properties */
    align-self: auto;
    pointer-events: none;
  }
  .scp-shell-list:not(.scp-shell-list--coll) { left: 0; right: auto; }
  .scp-shell-list--coll                       { right: 0; left: auto;
                                                transform: translateX(100%); }
  /* Open state — slide into view. */
  .scp-main--list-open .scp-shell-list:not(.scp-shell-list--coll),
  .scp-main--coll-list-open .scp-shell-list--coll {
    transform: translateX(0);
    opacity: 1;
    pointer-events: auto;
    flex: 0 0 0;            /* never claim flex space — overlay only */
    width: 320px;
  }
  /* Content area takes 100% — no padding reservation, no margin
     (drawer overlays content rather than displacing it). */
  .scp-main--list-open      .scp-shell-content,
  .scp-main--coll-list-open .scp-shell-content {
    padding-left: 0;
    padding-right: 0;
    margin-left: 0;
    margin-right: 0;
  }
  /* Backdrop — element is mounted by JS in `.scp-main`; toggled
     visible whenever either open class is present.  Click → JS
     dispatches `fretly:close-side-pane` for both kinds. */
  .scp-pane-backdrop {
    display: none;
    position: fixed;
    top: var(--scp-chrome-h, 140px);
    left: 0;
    right: 0;
    bottom: 0;
    background: rgba(0, 0, 0, 0.35);
    z-index: 99;
    cursor: pointer;
    animation: scpBackdropFadeIn 0.18s ease;
  }
  .scp-main--list-open      .scp-pane-backdrop,
  .scp-main--coll-list-open .scp-pane-backdrop {
    display: block;
  }
  @keyframes scpBackdropFadeIn {
    from { opacity: 0; }
    to   { opacity: 1; }
  }
  /* Close (×) button on the drawer header is hidden on desktop;
     show it inside the drawer at this breakpoint so users always
     have a discoverable dismiss affordance besides the backdrop. */
  .scp-list-close-btn { display: inline-flex; align-items: center; }
}

/* Narrow viewport — stack BOTH panes at the TOP of the page (above
   the content).  Critically: panes flow with the document scroll
   instead of having their own internal scroll container.  Nested
   scroll containers on mobile create dead-zones where the page
   can't scroll because the inner pane "eats" the wheel/touch
   event.  Standard mobile sheet pattern is content flows inline,
   document scrolls normally. */
/* Legacy 900-px narrow block REMOVED 2026-05-18 — consolidated
   into the single @media (max-width: 1300px) stack rule at the
   top of this file.  Duplicate breakpoints created cascade
   conflicts (width:auto !important vs width:100% etc.).  All
   pages using the SCP shell now inherit the same stack-mode
   layout from one source of truth. */

/* ── Left list pane — exact Scale-Explorer parity ───────────────── */

/* No outer card.  Pane is the surface itself; the parent
   .scp-shell-list provides the right-border separator. */
.scp-list-pane {
  display: flex;
  flex-direction: column;
  height: 100%;
  background: var(--surface, #fff);
  overflow: hidden;
}

/* Top action row — Browse-full link on the left, scale-home icon
   on the right.  Same surface-2 band as the search bar below.
   Right padding (12px) matches `.scp-ctrl-coll-row` so the home
   button here lines up vertically with the browse-full icon in
   the controls bar above. */
.scp-list-browse-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 8px 17px 8px 10px;
  border-bottom: 1px solid var(--border, #e0ddd5);
  background: var(--surface-2, #f7f6f3);
  /* Not sticky — scrolls up out of view as the user scrolls the
     pane's contents.  Only the .scp-list-search below stays
     pinned.  Per user 2026-05-18: "let sidebar titles and tabs
     scroll up too, and only stick from the search bars tops." */
}

.scp-list-browse-link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex: 1;
  min-width: 0;
  font-size: 12px;
  font-weight: 600;
  color: var(--accent, #e8853c);
  text-decoration: none;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.scp-list-browse-link:hover { text-decoration: underline; }

.scp-list-browse-icon {
  flex: 0 0 auto;
  font-size: 13px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 14px;
}

/* Home icon — square button matching the title-bar home button. */
.scp-list-home-btn {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  padding: 0;
  border: 1px solid var(--border, #e0ddd5);
  background: var(--surface, #fff);
  border-radius: 5px;
  cursor: pointer;
  font-size: 14px;
  color: var(--text-2, #555);
  transition: background 120ms, color 120ms, border-color 120ms;
}
.scp-list-home-btn svg { width: 14px; height: 14px; display: block; }
.scp-list-home-btn:hover,
.scp-list-home-btn:focus-visible {
  background: var(--accent, #e8853c);
  color: #fff;
  border-color: var(--accent, #e8853c);
  outline: none;
}

/* Chord page sidebar tab strip — sits between titlebar and search.
   One tab per data type (Voicings / Arpeggios / Suites / Scales /
   Articles).  Active tab gets the accent underline.  Per user
   2026-05-13: replaces the SOURCE bar's conflated nav pills with
   a clear data-type switcher inside the sidebar. */
.cpg-sidebar-tabs {
  display: flex;
  flex-wrap: wrap;     /* stack onto multiple rows when narrow */
  gap: 2px;
  padding: 4px 8px 0;
  background: var(--surface, #fff);
  border-bottom: 1px solid var(--border, #e0ddd5);
  /* Tabs are in flow (not sticky) — wrapping rows can make them
     tall enough that a sticky offset would fight the sticky search
     bar below.  Titlebar above stays sticky; tabs scroll out with
     the rows on long lists.  Per user 2026-05-13. */
}
.cpg-sidebar-tab {
  flex: 0 0 auto;
  background: transparent;
  border: none;
  border-bottom: 2px solid transparent;
  padding: 6px 10px 7px;
  margin-bottom: -1px;   /* overlap the strip's bottom border for the active underline */
  font: inherit;
  font-size: 12px;
  font-weight: 500;
  color: var(--text-2, #555);
  cursor: pointer;
  border-radius: 4px 4px 0 0;
  transition: background 0.12s, color 0.12s, border-color 0.12s;
}
.cpg-sidebar-tab:hover {
  background: var(--surface-2, #f7f6f3);
  color: var(--text-1, #222);
}
.cpg-sidebar-tab--active {
  color: var(--accent, #e8853c);
  border-bottom-color: var(--accent, #e8853c);
  background: var(--surface, #fff);
}

/* Sticky search bar — surface-2 band above the list.  Right inset
   17px = 14 (app-header padding) + 3 (compensate for the 26px×
   button vs 32px hamburger) so the `×` centre aligns with the
   hamburger centre on the same X. */
.scp-list-search {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 6px 17px 6px 8px;
  background: var(--surface-2, #f7f6f3);
  border-bottom: 1px solid var(--border, #e0ddd5);
  /* Sticky to the PANE's inner scroll, ANCHORED at the very top
     (top: 0).  Title row + tabs scroll up out of view as the
     user scrolls the list rows; only this search bar stays
     pinned at the pane's top edge (which is glued to the chrome
     bottom via the outer sticky on .scp-shell-list).  Per user
     2026-05-18: "let sidebar titles and tabs scroll up too, and
     only stick from the search bars tops." */
  position: sticky;
  top: 0;
  z-index: 3;
}

.scp-list-search-input {
  flex: 1;
  padding: var(--space-xs) var(--space-sm);
  font-size: var(--font-sm);
  border: var(--border-w) solid var(--border, #ddd);
  border-radius: var(--radius-sm);
  background: var(--bg, #fff);
  color: var(--text, #1a1a18);
  outline: none;
  font-family: inherit;
  transition: border-color var(--t-fast), box-shadow var(--t-fast);
}

.scp-list-search-input::placeholder { color: var(--text-3, #999); }

.scp-list-search-input:focus {
  border-color: var(--accent, #e8853c);
  box-shadow: 0 0 0 0.1429rem rgba(232, 135, 74, 0.15);
}

.scp-list-search-clear {
  /* Squared button - same width/height so the `×` centre lines up
     vertically with the home icon's centre across panels. */
  width: var(--ctl-h-md);
  height: var(--ctl-h-md);
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: none;
  color: var(--text-3, #888);
  font-size: var(--font-lg);
  cursor: pointer;
  padding: 0;
  line-height: 1;
}

.scp-list-search-clear:hover { color: var(--text, #1a1a18); }

/* Sticky count header — sentence case, surface-2 band. */
.scp-list-header {
  padding: var(--space-sm) var(--space-md);
  font-size: var(--font-sm);
  font-weight: var(--weight-semibold);
  color: var(--text-2, #555);
  background: var(--surface-2, #f7f6f3);
  border-bottom: var(--border-w) solid var(--border, #e0ddd5);
  /* Not sticky — scrolls up with the pane content.  Only the
     search bar stays pinned.  Per user 2026-05-18. */
}

/* Header with bulk toggle on the right (Expand all / Collapse all). */
.scp-list-header--row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm);
}

.scp-list-header-text {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}

.scp-list-bulk-toggle {
  flex: 0 0 auto;
  font-family: inherit;
  font-size: 11px;
  font-weight: 600;
  color: var(--accent, #e8853c);
  background: transparent;
  border: none;
  padding: 2px 4px;
  cursor: pointer;
  letter-spacing: 0.02em;
  border-radius: 3px;
  transition: background 100ms;
}

.scp-list-bulk-toggle:hover {
  background: rgba(232, 135, 74, 0.1);
}

/* Header row container — count text + bulk action icons.  Replaces
   the chunky default-button bulk row with two compact icon buttons
   inline on the right side of the header.  Right inset 17px = 14
   (app-header padding) + 3 (compensate for the 26px bulk icons vs
   32px hamburger) so the rightmost bulk icon (`⊟`) centre aligns
   with the gear/hamburger above on the same X. */
.scp-list-header-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 8px 17px 8px 12px;
  font-size: 12px;
  font-weight: 600;
  color: var(--text-2, #555);
  background: var(--surface-2, #f7f6f3);
  border-bottom: 1px solid var(--border, #e0ddd5);
  position: sticky;
  top: 39px;
  z-index: 2;
}
.scp-list-header-row .scp-list-header {
  /* Inside the header row, the header element loses its standalone
     padding/background — the row container owns those. */
  padding: 0;
  border: none;
  background: transparent;
  position: static;
  flex: 1;
  min-width: 0;
}
.scp-list-bulk-actions {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  flex: 0 0 auto;
}
.scp-list-bulk-icon {
  /* 26px square — matches the home + filter-clear buttons so the
     RIGHTMOST bulk icon (`⊟`) lines up vertically with them and
     the gear/hamburger above. */
  width: 26px;
  height: 26px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 3px;
  color: var(--text-3, #888);
  cursor: pointer;
  font-size: 14px;
  line-height: 1;
  transition: background 100ms, color 100ms, border-color 100ms;
}
.scp-list-bulk-icon:hover {
  background: var(--surface, #fff);
  color: var(--accent, #e8853c);
  border-color: var(--border, #e0ddd5);
}

/* Per-section subtree button — appears at the right edge of every
   expandable parent row.  Same icon as the header's bulk button so
   the affordance reads consistently.  Subtle by default, accent on
   hover. */
.scp-list-subtree-btn {
  margin-left: 4px;
  width: 20px;
  height: 20px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 3px;
  color: var(--text-3, #aaa);
  cursor: pointer;
  font-size: 12px;
  line-height: 1;
  opacity: 0.55;
  transition: opacity 100ms, background 100ms, color 100ms, border-color 100ms;
}
.scp-list-row:hover .scp-list-subtree-btn { opacity: 1; }
.scp-list-subtree-btn:hover {
  background: var(--surface, #fff);
  color: var(--accent, #e8853c);
  border-color: var(--border, #e0ddd5);
  opacity: 1;
}

.scp-list-rows {
  flex: 1;
  overflow-y: auto;
  /* Generous bottom padding so the LAST rows in the list can be
     scrolled up into the centre of the viewport (per user
     2026-05-10).  Without it, the last items hit the bottom edge
     immediately and can't be scrolled higher even if the active-
     row centering logic asks for it.  ~50% of viewport height
     gives plenty of headroom for centering on long lists. */
  padding: 4px 0 50vh;
}

.scp-list-row {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  width: 100%;
  padding: var(--space-xs) var(--space-md);
  font-size: var(--font-sm);
  background: transparent;
  color: var(--text, #1a1a18);
  cursor: pointer;
  text-align: left;
  font-family: inherit;
  white-space: nowrap;
  border: none;
  border-left: 3px solid transparent;
  border-bottom: var(--border-w) solid var(--surface-2, #f0ede5);
  /* No background transition — when the user clicks an expand/
     collapse chevron the list rebuilds and any hovered row gets a
     fresh DOM element under the cursor.  Setting the state
     instantly removes the flicker. */
}

.scp-list-row:hover {
  background: rgba(232, 135, 74, 0.08);
}

/* SINGLE SELECTED CHROME — only the row driven by `state` (the
   `--active` row) gets the orange bar + tinted bg + bold "selected"
   look.  Per user 2026-05-10: only one row may LOOK selected at any
   time.  Keyboard `:focus` is a separate concern (see below) and
   uses a subtler ring so it never doubles up with `--active` —
   otherwise focus left over from the previous row + new --active
   on a different row would visibly highlight TWO rows at once. */
.scp-list-row--active {
  outline: none;
  background: rgba(232, 135, 74, 0.15);
  border-left-color: var(--accent, #e8853c);
  color: var(--text, #1a1a18);
  font-weight: 600;
}

/* Keyboard focus — INVISIBLE.  Per user 2026-05-10: only ONE row
   may LOOK selected at any time, and that's the `--active` row.
   Browse-as-select keeps focus and --active in sync so the user
   never needs a separate "I'm focused here" indicator — the
   --active chrome IS the visible pointer.  Removing every visible
   focus affordance prevents the "two highlighted rows" bug where
   focus lingering on a parent folder + active class on the leaf
   read as both rows being selected.
   Accessibility note: rows remain `tabIndex=0` and reachable; the
   visual indicator is simply unified with --active. */
.scp-list-row:focus {
  outline: none;
  box-shadow: none;
}
.scp-list-row:focus-visible {
  outline: none;
  box-shadow: none;
}

.scp-list-chev {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 14px;
  height: 14px;
  flex-shrink: 0;
  font-size: 9px;
  color: var(--text-3, #999);
  user-select: none;
}

.scp-list-chev--toggle {
  cursor: pointer;
  border-radius: 3px;
}

.scp-list-chev--toggle:hover {
  background: var(--surface-2, #ece9e0);
  color: var(--text, #1a1a18);
}

.scp-list-chev--leaf { visibility: hidden; }

/* `.scp-list-icon` rules removed — the leading `▥` / `·` glyphs are
   no longer rendered by the list panes (chevron + indent are
   sufficient hierarchy markers).  See `ItemListPaneBase` and the
   collection-list base. */

.scp-list-name {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
}

.scp-list-row--section .scp-list-name,
.scp-list-row--folder .scp-list-name {
  font-weight: 500;
}

.scp-list-row--section .scp-list-name {
  text-transform: uppercase;
  font-size: 11px;
  letter-spacing: 0.05em;
}

.scp-list-count {
  font-size: 11px;
  font-variant-numeric: tabular-nums;
  background: var(--surface-2, #f7f6f3);
  border-radius: 999px;
  padding: 1px 7px;
  color: var(--text-3, #888);
}

.scp-list-empty {
  margin: 0;
  padding: 24px 16px;
  text-align: center;
  font-size: 12px;
  color: var(--text-3, #999);
  font-style: italic;
}

/* ── Breadcrumb ──────────────────────────────────────────────────── */

.scp-crumb-host {
  padding: 0;
}

/* Row wrapper — segments LEFT, actions slot (e.g. + Folder /
   + Voicing) RIGHT.  Single-row layout so the user sees the path
   and the create-affordances on one line. */
.scp-crumb-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}

/* Right-aligned actions slot.  Empty when no host has populated it
   (toolbar opt-in); flex container otherwise so consecutive buttons
   space evenly. */
.scp-crumb-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-left: auto;
}

/* Compact breadcrumb — quiet text line, no pill background. */
.scp-crumb {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 0;
  background: transparent;
  font-size: 12px;
  font-weight: 500;
  color: var(--text-2, #555);
}

.scp-crumb-seg a {
  color: var(--text-2, #555);
  text-decoration: none;
  transition: color 120ms;
}

.scp-crumb-seg a:hover {
  color: var(--accent, #e8853c);
}

.scp-crumb-seg--current {
  color: var(--text, #1a1a18);
  font-weight: 600;
}

.scp-crumb-sep {
  color: var(--text-3, #bbb);
  font-weight: 400;
}

/* Home segment — distinct from the Collection-internal trail so the
   user reads it as a separate navigation affordance.  Accent colour
   + caret prefix.  Hover deepens to underline. */
.scp-crumb-seg--home a {
  color: var(--accent, #e8853c);
  font-weight: 600;
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.scp-crumb-seg--home a:hover {
  color: var(--accent, #e8853c);
  text-decoration: underline;
}

/* Inline trailing link — sits AFTER the last breadcrumb segment as
   a subtle contextual affordance (e.g. "Open suite ↗" on a suite
   drill-in).  Smaller + lighter than path segments so it reads as
   a side-action, not part of the path itself. */
.scp-crumb-tail {
  margin-left: var(--space-md, 12px);
  padding: 2px 8px;
  color: var(--accent, #e8853c);
  font-size: var(--font-xs, 0.78rem);
  font-weight: 500;
  letter-spacing: 0.02em;
  border-radius: var(--radius-sm, 4px);
  text-decoration: none;
  transition: background 0.12s ease;
}
.scp-crumb-tail:hover {
  background: var(--surface-hover, rgba(232, 133, 60, 0.08));
  text-decoration: none;
}

/* ── Toolbar ─────────────────────────────────────────────────────── */

.scp-grid-toolbar {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 10px;
  padding: 4px 0 8px;
}

/* Space the folder/card grid off the sort toolbar above it (owner 2026-06-19). */
.scp-grid--folders, .scp-folder-list { margin-top: var(--space-md, 16px); }

/* ── Shared CollectionViewToolbar (cv-toolbar-*) ──────────────────── */
/* Tokens applied per CLAUDE.md design-tokens doctrine. */
.cv-toolbar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-md) var(--space-lg);
  padding: var(--space-sm) var(--space-md);
  margin-bottom: 0;
  /* Reserve stable height — pager segment (34px) appears only in
     Map+Browse mode; without a min-height, toggling List ↔ Browse
     shifts everything below the toolbar by ~4px.  Per user
     2026-05-15. */
  min-height: 50px;
  /* Visual separation from page background — chip/card chrome so
     the toolbar reads as its own surface rather than floating text.
     Per user 2026-05-15: "more beautiful and more separated
     collection menu bar". */
  background: var(--surface, #fff);
  border: var(--border-w) solid var(--border, #e0ddd5);
  border-radius: var(--radius-md);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
}
.cv-toolbar-group { display: inline-flex; align-items: center; gap: var(--space-sm); }
.cv-toolbar-adds   { flex: 0 0 auto; }
.cv-toolbar-view   { flex: 0 0 auto; margin-left: var(--space-sm); }
.cv-toolbar-pager  { flex: 0 0 auto; margin-left: var(--space-md); }
.cv-toolbar-sort   { flex: 0 0 auto; margin-left: auto; }
.cv-toolbar-filter { flex: 0 1 15.7143rem; }   /* 220 px @ 14px rem */
/* Visible separator + breathing room before the Cols section so the
   View pills (Cards/List) and the Cols stepper read as distinct
   groups.  Per user 2026-05-15: "more separation between the
   buttons and the cols section". */
.cv-toolbar-cols-label {
  margin-left: var(--space-md);
  padding-left: var(--space-md);
  border-left: var(--border-w) solid var(--border, #e0ddd5);
}
/* Pager — Prev/Next sit adjacent inside a single rounded container
   with a hairline divider between them.  Visually weightier than the
   view pills (border + shadow-of-sorts) so they read as primary
   actions.  Per user 2026-05-15. */
.cv-toolbar-pager-seg {
  gap: 0;
  padding: 0;
  background: var(--surface, #fff);
  border: 1px solid var(--border, #e0ddd5);
  border-radius: 8px;
  overflow: hidden;
  height: 34px;
}
.cv-toolbar-pager-btn {
  height: 100%;
  min-width: 5.2rem;
  padding: 0 14px;
  border: 0;
  border-radius: 0;
  background: transparent;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--text-1, #1a1a18);
  font-variant-numeric: tabular-nums;
  cursor: pointer;
  transition: background 120ms ease, color 120ms ease;
}
.cv-toolbar-pager-btn + .cv-toolbar-pager-btn {
  border-left: 1px solid #ece9e2;
}
.cv-toolbar-pager-btn:hover:not(:disabled) {
  background: var(--accent-bg, #fff8f1);
  color: var(--accent, #e8853c);
}
.cv-toolbar-pager-btn:active:not(:disabled) {
  background: var(--accent, #e8853c);
  color: #fff;
}
.cv-toolbar-pager-btn:disabled {
  color: var(--text-3, #aaa);
  cursor: default;
}
.cv-toolbar-pager-btn:disabled:hover {
  background: transparent;
  color: var(--text-3, #aaa);
}
.cv-toolbar-pager-info {
  padding: 0 var(--space-sm);
  font-size: var(--font-sm);
  font-variant-numeric: tabular-nums;
  text-transform: none;
  letter-spacing: 0;
  color: var(--text-2, #555);
  font-weight: var(--weight-medium);
  white-space: nowrap;
}
.cv-toolbar-label {
  font-size: var(--font-xs); font-weight: var(--weight-semibold); letter-spacing: 0.05em;
  text-transform: uppercase; color: var(--text-3, #888);
}
.cv-toolbar-btn {
  padding: var(--space-xs) var(--space-md); font-size: var(--font-sm); font-weight: var(--weight-medium);
  border: var(--border-w) solid var(--border, #e0ddd5); border-radius: var(--radius-sm);
  background: var(--surface, #fff); color: var(--text-2, #555); cursor: pointer;
}
.cv-toolbar-btn:hover { background: var(--surface-2, #f7f6f3); color: var(--text-1, #222); }
.cv-toolbar-seg {
  display: inline-flex; gap: 2px; padding: 2px;
  background: var(--surface-2, #f7f6f3); border-radius: var(--radius-sm);
}
/* When every button in a segmented control is hidden (e.g. Cards/List
   suppressed in Browse mode), the empty 4px chip rendered as a stray
   dot above the fretboard.  Collapse empty segs.  Per user 2026-06-13. */
.cv-toolbar-seg:empty { display: none; }
.cv-toolbar-seg-btn {
  padding: var(--space-xs) var(--space-md); font-size: var(--font-sm); font-weight: var(--weight-medium);
  border: none; background: transparent; color: var(--text-2, #555);
  border-radius: var(--radius-xs); cursor: pointer; min-width: 2rem;
}
.cv-toolbar-seg-btn:hover { background: var(--surface, #fff); color: var(--text, #1a1a18); }
.cv-toolbar-seg-btn--active,
.cv-toolbar-seg-btn--active:hover { background: var(--accent, #e8853c); color: #fff; }
.cv-toolbar-cols-btn {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.cv-toolbar-cols-btn svg { display: block; opacity: 0.8; }
.cv-toolbar-cols-btn--active svg,
.cv-toolbar-seg-btn--active.cv-toolbar-cols-btn svg { opacity: 1; }
/* Overlay color toggle — fixed footprint so toggling
   "Single color" ↔ "Multi color" doesn't shift adjacent
   Prev/Next buttons.  Per user 2026-05-26.  Width must fit the
   wider of the two labels ("Multi color" / "Single color").
   `text-align: center` keeps the label visually centered inside
   the fixed footprint. */
.cv-toolbar-overlay-color-btn {
  min-width: 6.5rem;
  text-align: center;
  /* Sits OUTSIDE the .cv-toolbar-seg chip, so it doesn't inherit the
     group's grey backdrop — give the button the same neutral grey
     itself (owner 2026-07-08: "shouldn't be white, but same grey as
     the other ones; orange when Multi").  The --active class supplies
     the orange. */
  background: var(--surface-2, #f7f6f3);
}
.cv-toolbar-overlay-color-btn:hover:not(.cv-toolbar-seg-btn--active) {
  background: var(--surface-2, #f7f6f3);
  color: var(--text, #1a1a18);
}
/* Selected (Multi color) state must stay ORANGE + readable (owner 2026-07-13).
   The neutral-grey `.cv-toolbar-overlay-color-btn` background above sits later
   in the cascade with equal specificity, so it was overriding the `--active`
   orange and leaving white text on grey. This 2-class rule wins. */
.cv-toolbar-overlay-color-btn.cv-toolbar-seg-btn--active,
.cv-toolbar-overlay-color-btn.cv-toolbar-seg-btn--active:hover {
  background: var(--accent, #e8853c);
  color: #fff;
}
/* Toolbar-level layout sizing — width / flex behaviour only.  All
   visual chrome (height, padding, border, caret, hover, focus) is
   inherited from the existing app-standard control rules above
   via the canonical class names (`.scp-ctrl-select`, `.scp-btn-group-btn`,
   etc.) applied to the toolbar's elements.  No re-implementation —
   single source of truth for control chrome. */
.cv-toolbar .cv-toolbar-filter-input { flex: 1 1 auto; width: 100%; }

/* Folder list view — single-line rows used when the toolbar's
   Folders view is set to "List".  Compact alternative to the
   standard card layout for navigating dense folder trees.
   `data-cols` (1–4) splits the rows across N columns so a long
   list stays scannable without endless vertical scroll. */
.scp-folder-list {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1px;
  background: var(--border, #e0ddd5);
  border: 1px solid var(--border, #e0ddd5);
  border-radius: 6px;
  overflow: hidden;
  margin-bottom: 14px;
}
.scp-folder-list[data-cols="2"] { grid-template-columns: repeat(2, 1fr); }
.scp-folder-list[data-cols="3"] { grid-template-columns: repeat(3, 1fr); }
.scp-folder-list[data-cols="4"] { grid-template-columns: repeat(4, 1fr); }
@media (max-width: 900px) {
  .scp-folder-list[data-cols="3"],
  .scp-folder-list[data-cols="4"] { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 600px) {
  .scp-folder-list[data-cols="2"],
  .scp-folder-list[data-cols="3"],
  .scp-folder-list[data-cols="4"] { grid-template-columns: 1fr; }
}
.scp-folder-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 14px;
  background: var(--surface, #fff);
  cursor: pointer;
  font-size: 13px;
  color: var(--text-1, #222);
}
.scp-folder-row:hover { background: var(--surface-2, #f7f6f3); }
.scp-folder-row-icon { color: var(--accent, #e8853c); display: inline-flex; }
.scp-folder-row-name { flex: 1 1 auto; font-weight: 500; }
.scp-folder-row-count {
  font-size: 11px; color: var(--text-3, #888);
  letter-spacing: 0.04em;
}
.scp-folder-row--section .scp-folder-row-name { font-weight: 600; }

.scp-grid-toolbar-label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--text-3, #888);
}

.scp-grid-toolbar-tabs {
  display: inline-flex;
  gap: 2px;
  padding: 3px;
  background: var(--surface-2, #f7f6f3);
  border-radius: 6px;
}

.scp-grid-toolbar-tab {
  padding: 4px 14px;
  font-size: 12px;
  font-weight: 500;
  border: none;
  background: transparent;
  color: var(--text-2, #555);
  cursor: pointer;
  border-radius: 4px;
  text-transform: capitalize;
  transition: background 120ms, color 120ms;
}

.scp-grid-toolbar-tab:hover {
  background: var(--surface, #fff);
  color: var(--text, #1a1a18);
}

.scp-grid-toolbar-tab--active {
  background: var(--accent, #e8853c);
  color: #fff;
}

.scp-grid-toolbar-tab--active:hover {
  background: var(--accent, #e8853c);
  color: #fff;
}

/* ── Folder grid (compact nav cards) ─────────────────────────────── */

.scp-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: 12px;
  /* Top-align cards so each sizes to its own content instead of
     stretching to the tallest card in its row.  Per user 2026-06-13:
     "top align everything". */
  align-items: start;
  /* Limit reflow scope so opening/closing the side panel doesn't
     thrash the rest of the page. */
  contain: layout style;
}

.scp-grid--folders { margin-bottom: 8px; }
/* (Removed 2026-05-17) — `.scp-grid-header-actions` / `.scp-grid-header-action`
   styled a standalone action bar above the grid.  Replaced by inline
   `.scp-crumb-tail` links rendered AFTER the breadcrumb segments so
   the action lives in the chrome instead of as a banner.
   See Breadcrumb.setTail + CollectionGridPane onHeaderActions. */

/* Inline chart-collection drill-in — chord-page suite drill-in mounts
   chart-v2's read-only chart AND the aux sections (Chart Diagrams /
   Arpeggios / Scales) into the grid body.  The grid pane's
   `renderInline` callback creates `.scp-grid-inline` as the outer
   host; data source nests `.scp-grid-inline-chart` (chart-v2 mount)
   and `.scp-grid-inline-aux` (aux sections) inside.  Subtle spacing
   between the chart and the aux sections so they read as related
   but distinct. */
.scp-grid-inline {
  display: flex;
  flex-direction: column;
  gap: var(--space-lg, 24px);
  /* Constrain to parent — without `min-width: 0` the flex child
     defaults to `min-width: auto` and grows past the viewport when
     chart-v2's natural width (~1440 px at bpl=4) exceeds available
     space.  Combined with `min-width: 0` on the chart host below,
     chart-v2's ResizeObserver sees the actual viewport width and
     `_fitChartToWidth` applies a uniform `transform: scale()` so
     the chart shrinks PROPORTIONALLY.  Per user 2026-05-17: "on
     small browser, shrink chart proportionally.  Perfect
     proportions, no interpretations." */
  min-width: 0;
}
.scp-grid-inline-chart {
  /* Bare container — no width caps, no overflow:hidden, no
     transform.  Chart-v2 handles its own layout.  Earlier
     experiments with max-width + overflow broke the internal
     bar/chord layout because they conflicted with chart-v2's
     engraver + _fitChartToWidth cascade.  Per user 2026-05-17:
     reverted to native chart-v2 behaviour.  Width-shrink-at-
     narrow-viewport must be handled by extending chart-v2
     itself (e.g. an inline-mount flag that always triggers
     _fitChartToWidth), not by outer CSS. */
  width: 100%;
  min-width: 0;
}
.scp-grid-inline-aux { display: flex; flex-direction: column; gap: var(--space-md, 16px); min-width: 0; }

/* Active-chord accent — chord-display field.  Per user 2026-05-18:
   "Use an accent color FIELD instead of a border.  Put it around
   where the chord name is, full height.  Not around the entire
   bar."  Filled rectangle behind the chord text, extending
   vertically to span the chord row (negative inset top/bottom)
   and a small horizontal halo around the text.  Uses the page's
   --scp-chord-accent-rgb triplet (orange by default; swaps with
   overlay palette) so the colour stays consistent with the rest
   of the chord-page accent system. */
/* Optional text-accent on the chord-display (the field div appended
   to the .suite-bar via JS does the actual rectangle).  Left here as
   a no-op styling hook in case we want bold/coloured text on the
   matching chord in a later pass. */
.scp-chord-match-chord { /* hook only */ }
/* Card-level outline (Diagram / Arpeggio / Scale aux sections) —
   `_paintActiveChordAccent` walks UP from each matching label to
   the enclosing card and tags it.  Same red treatment so the
   chord-page accent reads consistently across the inline view. */
.scp-chord-match-card {
  outline: 2px solid #d04030;
  outline-offset: 4px;
  border-radius: var(--radius-md, 6px);
}
/* Folder grid in Cards view honors the COLS stepper.  Per user
   2026-05-15: cols control was visible but had no effect on folder
   cards (only on leaf cards). */
.scp-grid--folders[data-cols="1"] { grid-template-columns: 1fr; }
.scp-grid--folders[data-cols="2"] { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.scp-grid--folders[data-cols="3"] { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.scp-grid--folders[data-cols="4"] { grid-template-columns: repeat(4, minmax(0, 1fr)); }

.scp-card {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
  padding: 16px 18px;
  background: var(--surface, #fff);
  border: 1px solid var(--border, #e0ddd5);
  border-radius: 10px;
  cursor: pointer;
  text-align: left;
  font-family: inherit;
  color: inherit;
  transition: border-color 120ms, transform 120ms;
}

.scp-card:hover {
  border-color: var(--accent, #e8853c);
  transform: translateY(-1px);
}

.scp-card--folder {
  background: var(--surface-2, #f7f6f3);
}

.scp-card--section {
  border-left: 3px solid var(--accent, #e8853c);
  border-radius: 8px;
}

.scp-card-thumb {
  font-size: 18px;
  color: var(--accent, #e8853c);
  line-height: 1;
}

/* Folder card with a board preview (miniature of the first content
   item) instead of the folder icon.  Full-width band above the name;
   non-interactive so the whole card stays clickable.  Per user
   2026-06-13. */
.scp-card-thumb--board {
  width: 100%;
  margin-bottom: 8px;
  border: 1px solid var(--border, #e0ddd5);
  border-radius: 6px;
  background: var(--surface, #fff);
  overflow: hidden;
  max-height: 110px;
  pointer-events: none;
  text-align: center;
}
/* Constrain by HEIGHT (like the motif thumbs) so the preview always
   FITS the 110px band.  The old width:100% + height:auto let the svg
   grow past the band at wide cards (COLS 1) and overflow:hidden cut
   the low strings + fret numbers off (owner 2026-07-08). viewBox
   scaling keeps the aspect; max-width covers narrow columns. */
.scp-card-thumb--board svg {
  display: inline-block;
  height: 108px;
  width: auto;
  max-width: 100%;
  vertical-align: middle;
}

/* Curated category motif thumbnails (Full Fretboard / Arpeggios /
   Voicings / Scales / Suites / Articles).  Same band as --board; the
   SVG ships structure only, colours live here so they stay theme-aware.
   Per user 2026-06-16. */
.scp-card-thumb--motif {
  width: 100%;
  margin-bottom: 8px;
  border: 1px solid var(--border, #e0ddd5);
  border-radius: 6px;
  background: var(--surface, #fff);
  overflow: hidden;
  pointer-events: none;
  text-align: center;
  padding: 6px 0;
  box-sizing: border-box;
}
/* Constrain by HEIGHT (not card width) so the small 172×76 viewBox
   doesn't blow the dots up to full-card scale — a fixed ~96px-tall,
   aspect-correct, centred thumbnail at every COLS setting. */
.scp-card-thumb--motif svg.cat-thumb {
  display: inline-block;
  height: 96px;
  width: auto;
  max-width: 100%;
  vertical-align: middle;
}
.cat-thumb__str  { stroke: var(--border, #cdcabf); stroke-width: 0.6; opacity: 0.95; }
.cat-thumb__fret { stroke: var(--border, #d7d4c9); stroke-width: 0.6; opacity: 0.55; }
.cat-thumb__nut  { stroke: var(--text-3, #8a877d); stroke-width: 2; }
.cat-thumb__bar  { stroke: var(--border, #cdcabf); stroke-width: 1; }
.cat-thumb__dot  { fill: #f2a93b; }
.cat-thumb__root { fill: #d4861e; }
.cat-thumb__fnum { fill: var(--text-3, #b8b5aa); font-family: Arial, Helvetica, sans-serif; font-size: 7px; }
.cat-thumb__mute { fill: var(--text-3, #aaa); font-family: Arial, Helvetica, sans-serif; font-size: 8px; }
.cat-thumb__chord { fill: var(--accent, #d4861e); font-family: Arial, Helvetica, sans-serif; font-weight: 600; font-size: 11px; }
.cat-thumb__slash { fill: #e8924a; }
.cat-thumb__stem  { stroke: #c9962f; stroke-width: 1.2; }
.cat-thumb__beam  { stroke: #c9962f; stroke-width: 2.6; }
.cat-thumb__page  { fill: var(--surface, #fff); stroke: var(--border, #d7d4c9); stroke-width: 0.8; }
.cat-thumb__head  { fill: #e8a24a; }
.cat-thumb__line  { fill: var(--border, #cdcabf); }

.scp-card-name {
  font-size: 14px;
  font-weight: 600;
  color: var(--text, #1a1a18);
  word-break: break-word;
}

.scp-card-count {
  font-size: 11px;
  color: var(--text-3, #888);
  letter-spacing: 0.02em;
}

.scp-grid-empty {
  margin: 0;
  padding: 64px 16px;
  text-align: center;
  font-size: 14px;
  color: var(--text-3, #888);
  font-style: italic;
}

.scp-grid-actions {
  display: flex;
  gap: 8px;
  padding-top: 8px;
}

.scp-add-btn {
  padding: 8px 16px;
  font-size: 13px;
  font-weight: 500;
  border: 1px dashed var(--border, #ccc);
  border-radius: 6px;
  background: transparent;
  color: var(--text-2, #555);
  cursor: pointer;
  transition: border-color 120ms, color 120ms;
}

.scp-add-btn:hover {
  border-color: var(--accent, #e8853c);
  color: var(--accent, #e8853c);
}

/* ── Leaf cards (real fretboard renders) ─────────────────────────── */

.scp-leaf-stack {
  display: flex;
  flex-direction: column;
  gap: 24px;
}

.scp-leaf-card {
  background: var(--surface, #fff);
  border: var(--border-w) solid var(--border, #e0ddd5);
  border-radius: var(--radius-lg);
  overflow: hidden;
  /* Perf: skip layout/paint for off-screen leaf cards.  A Scales
     bucket can produce ~30+ cards; only a few are on-screen at any
     time.  Per CLAUDE.md "HIGH-PRIORITY TODO - UI navigation
     performance audit".  Placeholder size reserves scroll space. */
  content-visibility: auto;
  contain-intrinsic-size: 22.857rem;   /* ~320 px @ 14px rem */
}

/* Top toolbar — legacy scale-page pattern: title left, action btns
   right, separator below.  Holds the Open button (and future
   actions: duplicate, export, etc.). */
.scp-leaf-card-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 16px;
  border-bottom: 1px solid var(--border, #e0ddd5);
  background: var(--surface, #fff);
}

.scp-leaf-card-title {
  flex: 1;
  min-width: 0;
  font-size: 13px;
  font-weight: 600;
  /* Render the leaf's name as authored — no `text-transform:
     uppercase` so multi-word names ("Full Fretboard", drop-class
     leaves, etc.) read naturally and match the sidebar / data
     source. */
  color: var(--text-2, #555);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.scp-leaf-card-spacer { flex: 1; }

.scp-leaf-card-actions {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
}

/* Pill-shaped card button — icon + label.  Mirrors legacy
   "Add" / "Top" / export buttons at the top of the legacy fretboard
   card.  White surface, thin border, hover lifts to accent. */
.scp-card-btn {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 26px;
  padding: 0 10px;
  font-family: inherit;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--text-2, #555);
  background: var(--surface, #fff);
  border: 1px solid var(--border, #e0ddd5);
  border-radius: 5px;
  cursor: pointer;
  transition: background 120ms, color 120ms, border-color 120ms, box-shadow 120ms;
}

/* Compact at narrower viewports (half-window territory). */
@media (max-width: 1100px) {
  .scp-card-btn {
    height: 24px;
    padding: 0 8px;
    font-size: 11px;
    gap: 4px;
  }
  .scp-card-btn-icon { font-size: 12px; }
}

.scp-card-btn:hover {
  background: var(--surface-2, #f7f6f3);
  color: var(--accent, #e8853c);
  border-color: var(--accent, #e8853c);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
}

.scp-card-btn-icon {
  font-size: 13px;
  line-height: 1;
  display: inline-flex;
  align-items: center;
}

.scp-card-btn-label {
  line-height: 1;
}

/* When a card is too narrow for full button labels (e.g. COLS 4), drop
   to ICON-ONLY so Add / Open / Top never clip.  Width-driven via a
   container query, so it adapts to the real card size regardless of
   viewport or column count.  Tooltips (title=) cover the hidden labels.
   Owner 2026-06-19: "buttons don't fit on these cards." */
.scp-leaf-card { container-type: inline-size; }
@container (max-width: 300px) {
  .scp-leaf-card-actions .scp-card-btn-label,
  .scp-leaf-card-actions .atc-btn > span { display: none; }
  .scp-leaf-card-actions .scp-card-btn,
  .scp-leaf-card-actions .atc-btn {
    gap: 0;
    padding-left: 7px;
    padding-right: 7px;
  }
}

/* Open button — accent-tinted variant for primary action. */
.scp-card-btn--open {
  color: var(--accent, #e8853c);
  border-color: rgba(232, 135, 74, 0.35);
  background: rgba(232, 135, 74, 0.06);
}

.scp-card-btn--open:hover {
  background: var(--accent, #e8853c);
  color: #fff;
  border-color: var(--accent, #e8853c);
  box-shadow: 0 2px 6px rgba(232, 135, 74, 0.25);
}

/* Nav buttons (Prev / Next) — quieter than Open; chevron-led. */
.scp-card-btn--nav { color: var(--text-2, #555); }
.scp-card-btn--nav:disabled {
  opacity: 0.35;
  cursor: not-allowed;
}
.scp-card-btn--nav:disabled:hover {
  background: var(--surface, #fff);
  color: var(--text-2, #555);
  border-color: var(--border, #e0ddd5);
  box-shadow: none;
}

/* In-toolbar browse cluster: [Prev] counter [Next]. */
.scp-leaf-card-nav {
  display: flex;
  align-items: center;
  gap: 10px;
  flex: 1;
  min-width: 0;
}

.scp-leaf-card-counter {
  font-size: 12px;
  font-weight: 600;
  /* No `text-transform: uppercase` — render names as authored. */
  color: var(--text-3, #888);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.scp-leaf-board {
  position: relative;
  padding: 18px 24px 16px;
}

/* Cards view (Gallery-style) - leaves stack in N columns (driven
   by the toolbar's Cols control), default 1 column = full-width
   per leaf.  Per user 2026-05-11: every Cards-view list in the
   app should look like Gallery, including columns functionality.
   Overrides the default `.scp-grid` `auto-fill, minmax(180px,1fr)`
   so leaf-card sizing follows the explicit user choice. */
.scp-grid--leaves {
  display: grid;
  grid-template-columns: 1fr;
  gap: 16px;
}
.scp-grid--leaves[data-cols="2"] { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.scp-grid--leaves[data-cols="3"] { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.scp-grid--leaves[data-cols="4"] { grid-template-columns: repeat(4, minmax(0, 1fr)); }


.scp-leaf-card--cards {
  width: 100%;
  scroll-margin-top: 80px;
}

/* Gallery-card title - larger, bolder than the compact list mode.
   Matches gallery.js card title styling (15px / 600 / -0.01em). */
.scp-leaf-card--cards .scp-leaf-card-title {
  font-size: 15px;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--text, #1a1a18);
}

/* Open / Top action buttons - matches Gallery's pill-style links. */
.scp-card-btn--open {
  text-decoration: none;
  color: var(--accent, #e8853c);
}
.scp-card-btn--open:hover { background: var(--accent-bg, #fef0e0); }
.scp-card-btn--top {
  text-decoration: none;
  color: var(--text-3, #888);
  border-color: transparent;
  background: transparent;
}
.scp-card-btn--top:hover {
  color: var(--accent, #e8853c);
  background: var(--surface-2, #f7f6f3);
}

/* Browse mode — top bar with prev / counter / next; diagram card
   then takes the full content width below.  Buttons are NOT on
   the sides.  Mirrors Scale Explorer's full-width detail pane. */

.scp-leaf-browse {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.scp-leaf-card--browse {
  width: 100%;
}

.scp-leaf-browse-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 8px 4px;
}

.scp-leaf-browse-counter {
  flex: 1;
  text-align: center;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-3, #888);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.scp-leaf-nav {
  flex: 0 0 auto;
  height: 32px;
  padding: 0 14px;
  border-radius: 6px;
  border: 1px solid var(--border, #ccc);
  background: var(--surface, #fff);
  font-size: 13px;
  font-weight: 500;
  font-family: inherit;
  line-height: 1;
  cursor: pointer;
  color: var(--text-2, #555);
  transition: background 120ms, color 120ms, border-color 120ms;
}

.scp-leaf-nav:hover:not(:disabled) {
  background: var(--surface-2, #f7f6f3);
  color: var(--accent, #e8853c);
  border-color: var(--accent, #e8853c);
}

.scp-leaf-nav:disabled {
  opacity: 0.3;
  cursor: not-allowed;
}

/* ── Quick-jump (search-anywhere navigation) ─────────────────────── */
/* (Old `.scp-crumb-row` wrapper rules removed — class is now owned
   by the breadcrumb component itself, not the quick-jump pattern.) */

.scp-jump-trigger {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 8px 12px;
  font-size: 12px;
  font-weight: 500;
  border: 1px solid var(--border, #e0ddd5);
  border-radius: 8px;
  background: var(--surface, #fff);
  color: var(--text-2, #555);
  cursor: pointer;
  transition: border-color 120ms, color 120ms;
  white-space: nowrap;
}

.scp-jump-trigger:hover,
.scp-jump-trigger--open {
  border-color: var(--accent, #e8853c);
  color: var(--accent, #e8853c);
}

.scp-jump-trigger-icon {
  font-size: 14px;
  line-height: 1;
}

.scp-jump-scrim {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.08);
  z-index: 95;
}

.scp-jump-popover {
  position: absolute;
  z-index: 100;
  width: 380px;
  max-width: 90vw;
  max-height: 60vh;
  background: var(--surface, #fff);
  border: 1px solid var(--border, #ddd);
  border-radius: 10px;
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.12);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.scp-jump-head {
  padding: 12px;
  border-bottom: 1px solid var(--border, #eee);
}

.scp-jump-input {
  width: 100%;
  padding: 8px 12px;
  font-size: 13px;
  border: 1px solid var(--accent, #e8853c);
  border-radius: 6px;
  background: var(--surface, #fff);
  color: var(--text, #1a1a18);
  outline: none;
  box-sizing: border-box;
}

.scp-jump-input:focus {
  box-shadow: 0 0 0 2px var(--accent-soft, rgba(232, 133, 60, 0.25));
}

.scp-jump-list {
  flex: 1;
  overflow-y: auto;
  padding: 4px 0 8px;
}

.scp-jump-row {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 6px 12px;
  font-size: 13px;
  border: none;
  background: transparent;
  color: var(--text, #1a1a18);
  cursor: pointer;
  text-align: left;
  font-family: inherit;
}

.scp-jump-row:hover {
  background: var(--surface-2, #f7f6f3);
}

.scp-jump-icon {
  width: 14px;
  font-size: 12px;
  color: var(--text-3, #888);
  flex-shrink: 0;
}

.scp-jump-row--folder .scp-jump-icon,
.scp-jump-row--section .scp-jump-icon {
  color: var(--accent, #e8853c);
}

.scp-jump-name {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.scp-jump-row--folder .scp-jump-name,
.scp-jump-row--section .scp-jump-name {
  font-weight: 500;
}

.scp-jump-crumb {
  font-size: 11px;
  color: var(--text-3, #999);
  flex-shrink: 0;
}

.scp-jump-empty {
  margin: 0;
  padding: 24px 16px;
  text-align: center;
  font-size: 12px;
  color: var(--text-3, #999);
  font-style: italic;
}

/* ── Container-query DRAWER twin — robust to ANY viewport / chrome
   combination.  Fires when .scp-main ITSELF is narrow even on a wide
   viewport (admin sidebar, dev-tools docked-right).  Mirrors the
   @media drawer block above — same doctrine, same rules; breakpoint
   documents as --bp-scp-drawer-container in fretly-tokens.css.
   Modern browsers only (Chrome 105+/Safari 16+/Firefox 110+);
   older engines degrade to the @media rule.
   NOTE: no body:has() footer rule here — body sits outside the
   scp-main container, so such a rule can never match inside a
   @container block (the @media twin covers the common case). */
@container scp-main (max-width: 1200px) {
  .scp-shell-content,
  .scp-pane-promoted .scp-main--list-open      .scp-shell-content,
  .scp-pane-promoted .scp-main--coll-list-open .scp-shell-content {
    margin-left:  0 !important;
    margin-right: 0 !important;
    width: 100%;
    min-width: 0;
  }
  .scp-shell-list,
  .scp-shell-list--coll,
  .scp-pane-promoted .scp-shell-list,
  .scp-pane-promoted .scp-shell-list--coll {
    position: fixed !important;
    top: calc(var(--scp-chrome-h, 140px) + var(--scp-chrome-top-gap, 3px)) !important;
    height: calc(100vh - var(--scp-chrome-h, 140px) - var(--scp-chrome-top-gap, 3px)) !important;
    max-height: none !important;
    width: min(320px, 85vw) !important;
    max-width: 85vw !important;
    flex: 0 0 auto !important;
    margin: 0 !important;
    z-index: 60 !important;
    opacity: 1 !important;
    background: var(--surface, #fff);
    transition: transform 0.22s ease, box-shadow 0.22s ease;
    pointer-events: none !important;
  }
  .scp-shell-list:not(.scp-shell-list--coll),
  .scp-pane-promoted .scp-shell-list:not(.scp-shell-list--coll) {
    left: 0 !important;
    right: auto !important;
    transform: translateX(-105%) !important;
    border-right: 1px solid var(--border, #e0ddd5);
  }
  .scp-shell-list--coll,
  .scp-pane-promoted .scp-shell-list--coll {
    right: 0 !important;
    left: auto !important;
    transform: translateX(105%) !important;
    border-left: 1px solid var(--border, #e0ddd5);
  }
  .scp-main--list-open .scp-shell-list:not(.scp-shell-list--coll),
  .scp-pane-promoted .scp-main--list-open .scp-shell-list:not(.scp-shell-list--coll),
  .scp-main--coll-list-open .scp-shell-list--coll,
  .scp-pane-promoted .scp-main--coll-list-open .scp-shell-list--coll {
    transform: none !important;
    pointer-events: auto !important;
    box-shadow: 0 8px 32px rgba(0, 0, 0, 0.25);
  }
  .scp-drawer-scrim { display: none; }
  .scp-main--list-open > .scp-drawer-scrim,
  .scp-main--coll-list-open > .scp-drawer-scrim {
    display: block;
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    top: calc(var(--scp-chrome-h, 140px) + var(--scp-chrome-top-gap, 3px));
    background: rgba(20, 16, 8, 0.35);
    z-index: 55;
  }
}
