@import url("mz-menu.css"); /* canonical top-menu (.mz-topbar/.mz-auth) */
@import url("cosmic-rail.css?v=r2"); /* canonical vertical rail (single source — do NOT redeclare .rail*) */
/* notes-skin.css — Mazmazika website skin around the complete Notes Editor.
   Loaded AFTER notes-editor.css so these rules win.

   Layout model: the WHOLE PAGE scrolls. The site rail + top-nav + hero form a
   centered intro band; below it the editor is a full-width workspace whose note
   sheet is the core — a wide central canvas with nothing flanking it. The two
   tool bands (toolbar + palette) pin to the top of the viewport while the score
   scrolls under them; the transport (and the piano when toggled) pin to the
   bottom. Parts + Inspector are on-demand slide-in overlay drawers, so they
   never sit beside the sheet. Everything reuses the editor's own mazmazika
   tokens (dark navy + cyan), so chrome and tool stay one coherent surface. */

/* ── Page scroll model ──────────────────────────────────────────────────── */
html, body { height: auto; min-height: 100%; }
/* Reserve the scrollbar gutter so 100vw agrees with the client width. The deck /
   transport / piano-dock are sized from --mz-content-viewport-w (= 100vw − rail);
   100vw counts the scrollbar gutter, so with a classic vertical scrollbar they used
   to poke a few px past the client edge → a phantom horizontal scrollbar at tablet
   widths. `scrollbar-gutter: stable` makes the gutter part of the layout (100vw now
   excludes it) so nothing overflows — and, unlike `overflow-x: clip`, it does NOT
   turn <html> into a clip/scroll context, so the sticky toolbar deck still clamps
   to the viewport. (A previous `overflow-x: clip` here fixed the phantom scrollbar
   but broke the sticky deck — it scrolled away with the page.) Note the vibration
   itself was the palette wrap loop, fixed elsewhere — not this. */
html { scrollbar-gutter: stable; }
body.notes-editor-page {
  margin: 0;
  --site-header-h: 0px;
  overflow-x: hidden;
  background: var(--bg);
  /* The cosmic backdrop, defined ONCE so the full-viewport .skin-bg AND the thin
     scroll-clip mask (top) paint the IDENTICAL gradient (both read this var, so
     they can never seam). This recipe MIRRORS the site-wide cosmic background
     (css/vocal-cosmic.css .bg-base + .bg-nebula): a bg3 top-glow + bg2 bottom-glow
     base, plus glow/accent/accent2 nebula glows — so the Notes Editor paints the
     SAME background as every other page for a given theme (was a divergent
     accent-tinted recipe). --bg2/--bg3 are set by applyNeTheme (theme-bridge.js);
     the --bg-tint/--surface fallbacks cover the pre-React first paint. */
  --mz-cosmic-bg:
    radial-gradient(52% 58% at 24% 26%, color-mix(in srgb, var(--glow, #7c5cff) 24%, transparent), transparent 76%),
    radial-gradient(56% 60% at 80% 80%, color-mix(in srgb, var(--accent) 20%, transparent), transparent 78%),
    radial-gradient(48% 52% at 84% 20%, color-mix(in srgb, var(--accent2, #2a6fdb) 15%, transparent), transparent 78%),
    radial-gradient(120% 95% at 50% 12%, var(--bg3, var(--surface)), transparent 56%),
    radial-gradient(110% 80% at 50% 118%, var(--bg2, var(--bg-tint)), transparent 62%),
    var(--bg);
  /* Centered editor width variables */
  --mz-rail-w: 66px;               /* matches the canonical rail (cosmic-rail.css) */
  --mz-sheet-gap: 56px;
  --mz-content-viewport-w: calc(100vw - var(--mz-rail-w));
  --mz-sheet-w: min(1080px, calc(var(--mz-content-viewport-w) - var(--mz-sheet-gap)));
  --mz-sheet-left: calc(var(--mz-rail-w) + (var(--mz-content-viewport-w) - var(--mz-sheet-w)) / 2);
}

/* ── Cosmic background ──────────────────────────────────────────────────── */
.skin-bg { position: fixed; inset: 0; z-index: 0; pointer-events: none; overflow: hidden; }
/* Backdrop = the SAME two layers every other cosmic page paints (css/vocal-cosmic.css
   .bg-base + .bg-vignette on ::before, .bg-nebula on ::after) so the Notes Editor
   backdrop is identical to Vocal Remover et al. for a given theme. The nebula is
   blurred + .5 opacity (as on the other pages) — that's why the editor no longer
   looks brighter than the rest of the site. (Was: one un-blurred accent-tinted
   gradient stack painted at full strength + a grain overlay.) */
/* Layer order mirrors css/vocal-cosmic.css EXACTLY: base (::before) → blurred
   nebula (.skin-nebula) → stars (.skin-stars) → vignette (::after, topmost). The
   vignette sits ABOVE the nebula so it darkens the glow edges the same way it does
   on every other page — otherwise NE's glows read brighter. */
.skin-bg::before {                        /* .bg-base */
  content: ""; position: absolute; inset: 0;
  background:
    radial-gradient(120% 95% at 50% 12%, var(--bg3, var(--surface)), transparent 56%),
    radial-gradient(110% 80% at 50% 118%, var(--bg2, var(--bg-tint)), transparent 62%),
    var(--bg);
}
.skin-bg .skin-nebula {                    /* .bg-nebula */
  position: absolute; inset: -22%; pointer-events: none;
  background:
    radial-gradient(52% 58% at 24% 26%, color-mix(in srgb, var(--glow, #7c5cff) 30%, transparent), transparent 76%),
    radial-gradient(56% 60% at 80% 80%, color-mix(in srgb, var(--accent) 26%, transparent), transparent 78%),
    radial-gradient(48% 52% at 84% 20%, color-mix(in srgb, var(--accent2, #2a6fdb) 20%, transparent), transparent 78%);
  filter: blur(54px); opacity: .5;
}
.skin-bg::after {                          /* .bg-vignette (topmost) */
  content: ""; position: absolute; inset: 0;
  background: radial-gradient(115% 100% at 50% 42%, transparent 52%, color-mix(in srgb, var(--bg) 92%, #000) 100%);
}
.skin-stars { position: absolute; inset: 0; }
.skin-stars span {
  position: absolute; border-radius: 50%; background: #fff;
  box-shadow: 0 0 6px rgba(255,255,255,.6);
  animation: skin-twinkle linear infinite;
}
@keyframes skin-twinkle { 0%,100% { opacity: .2; } 50% { opacity: .9; } }

/* ── Keyboard-shortcuts modal ───────────────────────────────────────────────
   It's portaled to <body> (see app.jsx) so it escapes #app's stacking context;
   lift it above the fixed rail (z-6000) so it truly covers the page and its
   backdrop/✕/outside-tap all work. */
.shortcuts-modal { z-index: 9100 !important; }
.shortcuts-modal .kbd { white-space: normal; }   /* let long combos wrap, never clip */
@media (max-width: 1024px) {
  .shortcuts-modal-card { width: min(560px, 100%); }
  .shortcuts-modal-head { padding: 12px 14px; }
  .shortcuts-table th, .shortcuts-table td { padding: 9px 13px; }
  .shortcuts-table th { width: 40%; }            /* keys column — fits combos, no clip */
  .shortcuts-tip { padding: 11px 14px 14px; }
}

/* Vertical rail → css/cosmic-rail.css (single source, imported at top). */

/* ── Centered intro band: top nav + hero (scrolls with the page) ────────── */
.skin-shell { position: relative; z-index: 2; box-sizing: border-box; width: min(100%, 1080px); margin: 0 auto;
  padding: 22px 24px 4px 92px; }
.site-topbar { display: flex; align-items: center; justify-content: space-between; gap: 16px;
  padding: 6px 2px 0; margin: 0 0 30px; }
.site-brand { display: inline-flex; align-items: center; gap: 12px; text-decoration: none; color: var(--ink); }
.site-brand-mark { width: 34px; height: 34px; border-radius: 11px; display: grid; place-items: center;
  color: var(--accent-ink, #fff); background: linear-gradient(135deg, var(--accent2, var(--accent)), var(--accent));
  box-shadow: 0 16px 38px -14px color-mix(in srgb, var(--accent) 70%, transparent); }
.site-brand-mark svg { width: 19px; height: 19px; }
.site-brand-name { font-family: var(--font-sans); font-weight: 600; font-size: 16.5px; letter-spacing: -.02em; }
.site-brand-name em { font-family: var(--font-serif); font-style: italic; font-weight: 400; color: var(--accent); }
.site-nav { display: flex; align-items: center; gap: 6px; }
.site-nav a { color: var(--ink-2); text-decoration: none; font-family: var(--font-sans); font-size: 14px;
  font-weight: 500; padding: 9px 13px; border-radius: 10px; transition: color .18s ease, background .18s ease; }
.site-nav a:hover { color: var(--ink); background: color-mix(in srgb, var(--ink) 9%, transparent); }
.site-theme { appearance: none; border: 1px solid var(--line-strong);
  background: color-mix(in srgb, var(--surface) 70%, transparent); width: 40px; height: 40px;
  border-radius: 11px; cursor: pointer; color: var(--ink-2); display: grid; place-items: center;
  transition: .18s ease; }
.site-theme:hover { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 50%, transparent); }
.site-theme svg { width: 19px; height: 19px; }

/* ── Hero ───────────────────────────────────────────────────────────────── */
.hero { text-align: center; max-width: 760px; margin: 18px auto 36px;
  display: flex; flex-direction: column; align-items: center; }
.hero .eyebrow { display: inline-flex; align-items: center; gap: 8px;
  font-family: var(--font-sans); font-size: 12.5px; font-weight: 600; letter-spacing: .08em;
  text-transform: uppercase; color: var(--accent);
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 40%, transparent);
  padding: 6px 14px; border-radius: 999px; margin-bottom: 16px; }
.hero .eyebrow .dot { width: 7px; height: 7px; border-radius: 50%; flex-shrink: 0; background: var(--accent);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 24%, transparent); }
.hero h1 { margin: 0 0 14px; font-family: var(--font-sans); font-size: clamp(34px, 6vw, 56px);
  line-height: 1.04; letter-spacing: -.03em; font-weight: 600; color: var(--ink); text-wrap: balance; }
.hero h1 em { font-family: var(--font-serif); font-style: italic; font-weight: 400; color: var(--accent);
  letter-spacing: -.01em; }
.hero p { margin: 0 auto; max-width: 580px; color: var(--ink-2); font-family: var(--font-sans);
  font-size: 16px; line-height: 1.55; text-wrap: pretty; }

/* ═══ Editor: full-width, page-flow workspace ═══════════════════════════════ */
#app { position: relative; z-index: 1; display: block; padding-left: 68px; }
/* #app (z-index 1) and the site header .skin-shell (z-index 2) are siblings, so
   the header's avatar/nav normally paints ABOVE everything in #app — including
   the Inspector drawer (z-60) + scrim, which then "open behind the avatar".
   While any drawer is open, lift #app above the header so its scrim+drawer
   cover the nav/avatar (stays below the rail at z-6000). */
#app:has(.app.drawer-left-open),
#app:has(.app.drawer-right-open),
#app:has(.twk-panel) { z-index: 70; }
#app .app {
  display: block;
  height: auto;
  width: 100%;
  background: transparent;
  /* leave room at the end of the scroll for the fixed transport (+ piano) */
  padding-bottom: calc(80px + var(--piano-dock-h, 0px));
}

/* Sticky control deck: the document toolbar + the note palette pinned together
   so the tools stay reachable while the score scrolls beneath them. Kept
   deliberately shallow so the sheet leads. */
#app .editor-deck {
  /* top:0 (was 12px): sticking flush to the top leaves NO gap above the deck, so
     the score can't scroll into a strip above it — which retires .ne-viewport-mask-top
     (the 12px cover strip whose gradient never quite matched the backdrop and showed
     as a horizontal seam near the top, an artifact Vocal Remover never had). */
  position: sticky; top: 0; z-index: 22;
  width: calc(var(--mz-sheet-w) + 32px) !important;
  margin-left: calc((var(--mz-content-viewport-w) - var(--mz-sheet-w)) / 2 - 16px) !important;
  background: color-mix(in srgb, var(--surface) 96%, transparent);
  -webkit-backdrop-filter: blur(14px) saturate(150%); backdrop-filter: blur(14px) saturate(150%);
  box-shadow: 0 14px 34px -22px rgba(0,0,0,.6);
  border-radius: 12px;
  border: 1px solid var(--line);
}
#app .topbar {
  position: static; min-height: auto; border-radius: 0; background: transparent;
  display: flex; flex-direction: column; align-items: stretch;
  padding: 8px 10px; gap: 8px;
}
#app .topbar-row {
  display: flex; align-items: center; gap: 8px; min-height: 38px;
}
/* One unified line now (the 2nd toolbar row was merged in) — no inter-row
   divider. (The tiny score-name field was removed; rename from the sheet
   title or Score Details.) The deck is a fixed width locked to the sheet, so
   the single line is tightened (smaller gap/padding) to fit WITH headroom and
   the controls may shrink — Share is pinned last and never overflows the deck. */
#app .topbar-row-main { min-width: 0; }
#app .topbar .keyboard-toggle-pill,
#app .topbar .page-view-toggle-pill,
#app .topbar .score-details-pill,
#app .topbar .parts-toggle-pill { padding: 0 8px; gap: 5px; min-width: 0; }
/* If space ever gets truly tight, let the menus/tools/pills give before the
   right edge — Share stays a fixed size, last, inside the rounded deck. */
#app .topbar .menus,
#app .topbar .topbar-tools,
#app .topbar .keyboard-toggle-pill,
#app .topbar .page-view-toggle-pill,
#app .topbar .score-details-pill,
#app .topbar .parts-toggle-pill { flex-shrink: 1; }
#app .topbar .share-btn { flex: 0 0 auto; }

/* ── Mobile view switch (Sheet ⟷ Piano roll) ───────────────────────────────
   A segmented pill in the topbar. On phones the user picks ONE view at a time
   (note sheet OR the full-screen landscape piano roll) — following both at once
   is too distracting on a small screen. Hidden on desktop, where the on-screen
   piano is a dockable side-by-side convenience (the .keyboard-toggle-pill). The
   mobile media block flips this to inline-flex and hides that pill. */
#app .topbar .ne-view-toggle { display: none; }
.ne-view-toggle {
  align-items: center; gap: 2px;
  padding: 3px; flex: 0 0 auto;
  border: 1px solid var(--line); border-radius: 999px;
  background: var(--bg-tint);
}
.ne-view-toggle .ne-vt-btn {
  display: inline-flex; align-items: center; gap: 5px;
  height: 32px; padding: 0 13px;
  border: 0; border-radius: 999px;
  background: transparent; color: var(--ink-2);
  font: 600 12.5px/1 var(--font-sans); cursor: pointer; white-space: nowrap;
}
.ne-view-toggle .ne-vt-btn svg { width: 17px; height: 17px; }
.ne-view-toggle .ne-vt-btn.is-active {
  background: var(--accent); color: #fff;
  box-shadow: 0 2px 8px -3px color-mix(in srgb, var(--accent) 60%, transparent);
}

/* ── Zoom docked in the transport (mobile only) ────────────────────────────
   Compact [−] 100% [+] living INSIDE the fixed bottom transport on phones, so
   the zoom no longer free-floats over the sheet. Desktop keeps the floating
   .canvas-floats pill and hides this (the mobile media block flips it on and
   hides .canvas-floats there). */
.tr-zoom {
  display: none;                   /* mobile media block flips this to inline-flex */
  align-items: center; gap: 1px;
  padding: 2px; flex: 0 0 auto;
  border: 1px solid var(--line); border-radius: 999px;
  background: var(--bg-tint);
}
.tr-zoom .tr-zoom-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px;
  border: 0; border-radius: 50%; background: transparent;
  color: var(--ink-2); cursor: pointer;
}
.tr-zoom .tr-zoom-btn:active { background: var(--accent-soft); color: var(--ink); }
.tr-zoom .tr-zoom-btn svg { width: 18px; height: 18px; }
.tr-zoom .tr-zoom-val {
  min-width: 34px; text-align: center;
  font: 600 11px/1 var(--font-mono, var(--font-sans)); color: var(--ink-2);
}

/* ── Tweaks + Keyboard-hint buttons docked into the transport (mobile only) ──
   On phones the two corner FABs (settings gear + "how to type notes" keyboard)
   become bookends of the fixed control panel — see the mobile media block, which
   shows these and hides the free-floating .twk-fab / .kbd-hint-fab. Desktop keeps
   the floating FABs and hides these. */
#app .transport .tr-kbd,
#app .transport .tr-tweaks,
#app .transport .tr-undo,
#app .transport .tr-redo { display: none; }

/* ── Playing-note highlight → the live THEME accent ─────────────────────────
   The note sheet shipped a hardcoded green for the currently-sounding note,
   while the piano-roll keys use var(--accent) (the colour chosen in Tweaks).
   Re-tint the sheet highlight to the same accent so both views match. The #app
   prefix + !important outrank the base AND the data-aesthetic="studio" rules in
   notes-editor.css. */
#app .verovio-host .playing-note,
#app .verovio-host .playing-note path,
#app .verovio-host .playing-note ellipse,
#app .verovio-host .playing-note use {
  fill: var(--accent) !important;
  stroke: var(--accent) !important;
}
#app .verovio-host .playing-note {
  filter: drop-shadow(0 0 6px color-mix(in srgb, var(--accent) 55%, transparent)) !important;
}

#app .palette {
  position: static; background: transparent; border-bottom: 1px solid var(--line);
}
#app .app .topbar .brand { display: none; }   /* brand lives in the site nav above */
/* Let the colour swatches sit on a single row instead of a 2-row grid. */
#app .color-palette-group { max-width: none; row-gap: 2px; }

/* Measure palette: direct bar-count field sitting before the +/- buttons. */
#app .measure-count-input {
  width: 48px;
  height: 30px;
  margin-right: 2px;
  padding: 0 6px;
  text-align: center;
  font: 600 13px/1 var(--font-mono, var(--font-sans));
  color: var(--ink);
  background: color-mix(in srgb, var(--ink) 8%, transparent);
  border: 1px solid var(--line);
  border-radius: 8px;
  -moz-appearance: textfield;
  appearance: textfield;
}
#app .measure-count-input::-webkit-outer-spin-button,
#app .measure-count-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
#app .measure-count-input:hover { border-color: color-mix(in srgb, var(--accent) 45%, var(--line)); }
#app .measure-count-input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent);
}

/* New Score dialog — choose how many bars to start with. */
.ne-newscore-modal {
  position: fixed; inset: 0; z-index: 1000;
  display: grid; place-items: center; padding: 16px;
  background: color-mix(in srgb, #0a0a14 60%, transparent);
  -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px);
}
.ne-newscore-card {
  width: min(100%, 400px);
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: 16px;
  box-shadow: 0 24px 60px -20px rgba(0,0,0,.6);
  padding: 20px 22px;
  display: flex; flex-direction: column; gap: 16px;
}
.ne-newscore-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.ne-newscore-head h3 { margin: 0; font: 600 18px/1.2 var(--font-sans); color: var(--ink); }
.ne-newscore-head .icon-btn { color: var(--ink-2); }
.ne-newscore-note { margin: 0; font: 13px/1.5 var(--font-sans); color: var(--ink-2); }
.ne-newscore-field { display: flex; flex-direction: column; gap: 8px; }
.ne-newscore-field > span {
  font: 600 11px/1 var(--font-sans); letter-spacing: .07em; text-transform: uppercase; color: var(--ink-2);
}
.ne-newscore-stepper { display: inline-flex; align-items: center; gap: 8px; }
.ne-newscore-stepper button {
  width: 38px; height: 38px; flex: 0 0 auto;
  display: grid; place-items: center;
  font-size: 20px; line-height: 1; color: var(--ink);
  background: color-mix(in srgb, var(--ink) 8%, transparent);
  border: 1px solid var(--line); border-radius: 10px; cursor: pointer;
}
.ne-newscore-stepper button:hover { border-color: var(--accent); color: var(--accent); }
.ne-newscore-stepper input {
  width: 92px; height: 38px; padding: 0 8px; text-align: center;
  font: 600 16px/1 var(--font-mono, var(--font-sans)); color: var(--ink);
  background: color-mix(in srgb, var(--ink) 8%, transparent);
  border: 1px solid var(--line); border-radius: 10px;
  -moz-appearance: textfield; appearance: textfield;
}
.ne-newscore-stepper input::-webkit-outer-spin-button,
.ne-newscore-stepper input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.ne-newscore-stepper input:focus {
  outline: none; border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent);
}
.ne-newscore-foot { display: flex; justify-content: flex-end; gap: 10px; }
.ne-newscore-foot .btn {
  padding: 9px 18px; font: 600 14px/1 var(--font-sans);
  border-radius: 10px; cursor: pointer; border: 1px solid transparent;
}
.ne-newscore-foot .btn.ghost {
  background: transparent; color: var(--ink-2); border-color: var(--line);
}
.ne-newscore-foot .btn.ghost:hover { color: var(--ink); border-color: var(--ink-2); }
.ne-newscore-foot .btn.primary {
  color: var(--accent-ink, #fff);
  background: linear-gradient(135deg, var(--accent2, var(--accent)), var(--accent));
  border-color: color-mix(in srgb, var(--accent) 55%, var(--line));
}
.ne-newscore-foot .btn.primary:hover { filter: brightness(1.07); }

/* The score canvas: full width, transparent ground so the white sheet floats on
   the cosmic background. It grows with content — the page scrolls, not the box. */
#app .canvas-wrap { overflow: visible; height: auto; min-height: 0; background: transparent; }
#app .score-page {
  width: var(--mz-sheet-w) !important;
  margin: 16px auto 44px;
  padding: 22px 60px 64px;
}
/* Compact the default empty-score header so the staff sits high in view rather
   than below the fold. */
#app .score-titles { margin-bottom: 10px; }
#app .score-titles .title { font-size: 26px; }
#app .score-titles .subtitle { font-size: 14px; margin-bottom: 6px; }
#app .score-tempo { margin-bottom: 4px; }
#app .verovio-host svg { filter: none !important; }

/* Status info flows in normal document order BELOW the sheet (so it never
   covers the music), centered to the page. */
#app .canvas-status {
  position: static; transform: none; margin: 12px auto 4px; z-index: 1;
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 8px;
}
/* Zoom control: it zooms the note SHEET. Pinned to the right gutter and vertically
   CENTRED on the viewport (position:fixed), so it is symmetric in the gutter and never
   overlaps the toolbar / Share button at the top — at ANY width, including narrow
   screens where the palette toolbars collapse and the deck becomes short (the old
   absolute-in-deck `top:50%` then centred onto the TopBar and overlapped Share).
   Fixed also keeps it put while the sheet scrolls. Sized larger for easy tapping. */
#app .canvas-floats {
  position: fixed; right: 22px; left: auto; top: 50%; bottom: auto;
  transform: translateY(-50%); margin: 0; z-index: 31;
  display: flex; flex-direction: column; align-items: center; gap: 4px; padding: 7px;
  border-radius: 16px; border: 1px solid var(--line);
  background: color-mix(in srgb, var(--surface) 92%, transparent);
  -webkit-backdrop-filter: blur(14px) saturate(150%); backdrop-filter: blur(14px) saturate(150%);
  box-shadow: 0 16px 36px -16px rgba(0,0,0,.6);
}
#app .canvas-floats .icon-btn { width: 40px; height: 40px; border-radius: 11px; }
#app .canvas-floats .icon-btn svg { width: 22px; height: 22px; }
#app .canvas-floats > div { font-size: 13px; font-weight: 600; padding: 2px 0; }

/* Parts (left) + Inspector / Practice (right): on-demand overlay drawers so the
   sheet always keeps the full width. Toggled from the topbar buttons. */
#app .leftpanel,
#app .inspector,
#app .practice-panel {
  position: fixed; top: 0; bottom: 0; z-index: 60;
  width: min(92vw, 344px);
  background: var(--surface);
  box-shadow: 0 24px 80px -18px rgba(0,0,0,.62);
  overflow-y: auto; -webkit-overflow-scrolling: touch;
  transition: none;
}
#app .leftpanel { left: 68px; border-right: 1px solid var(--line); transform: translateX(-110%); }
#app .inspector,
#app .practice-panel { right: 0; border-left: 1px solid var(--line); transform: translateX(105%); }
#app .app.drawer-left-open  .leftpanel { transform: none !important; }
#app .app.drawer-right-open .inspector,
#app .app.drawer-right-open .practice-panel { transform: none !important; }

/* Dimming scrim behind an open drawer. Click it to close (handler in app.jsx).
   On the dark cosmic bg a plain 50% black overlay is nearly invisible, so the
   drawer felt un-closable — darken + blur so the modal state is obvious. */
#app .mobile-scrim {
  display: block; position: fixed; inset: 0; z-index: 55;
  background: rgba(3,4,14,.6); -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px);
  opacity: 0; pointer-events: none; transition: none;
}
#app .app.drawer-left-open .mobile-scrim,
#app .app.drawer-right-open .mobile-scrim { opacity: 1; pointer-events: auto; }

/* Inspector / Practice drawer: an always-clickable close button (above the
   scrim) so the panel is closable without hunting for the dimmed overlay. */
#app .inspector-tabs { position: relative; }
#app .inspector-close {
  margin-left: auto; flex: 0 0 auto;
  width: 30px; height: 30px; border-radius: 9px;
  display: grid; place-items: center;
  background: transparent; border: 1px solid var(--line); color: var(--ink-2);
  font-size: 13px; cursor: pointer; transition: color .15s ease, background .15s ease, border-color .15s ease;
}
#app .inspector-close:hover { color: var(--ink); background: color-mix(in srgb, var(--ink) 10%, transparent); border-color: var(--ink-3); }

/* Native <select> popups: the open option list was unreadable — light option
   text (inherited from the select's color:inherit) on the OS's WHITE popup.
   `color-scheme:dark` alone did NOT darken the popup on Chrome/Windows, so we
   style the <option>s directly (Chrome honors option background/color). Vars so
   it tracks the light/dark theme. Applied to every editor select, not just the
   inspector. */
#app select { color-scheme: dark; }
#app select option,
#app select optgroup {
  background-color: var(--surface, #0c1640);
  color: var(--ink, #f3efff);
}

/* (The Inspector right drawer was wiped from the UI entirely. The Parts drawer
   is toggled by the topbar Parts pill / View menu / mobile Menu sheet — it is
   the only UI that can add or remove instruments.) */

/* Transport: fixed bottom band, full width (after the rail). The editor's
   fixed 240/1fr/320 columns squeeze the right group (count-in + speed + tempo +
   volume + dB) into overlap; let each region size to its content instead, with
   the scrub in the flexible middle. */
#app .transport {
  position: fixed;
  /* EXACTLY the sheet width — every fixed component (deck, dock, transport)
     shares this alignment; never let one of them grow past it. Breathing
     room for the play cluster comes from COMPACTING the bar's contents. */
  left: calc(var(--mz-sheet-left) - 16px) !important;
  width: calc(var(--mz-sheet-w) + 32px) !important;
  bottom: 12px; z-index: 30; height: 64px;
  grid-template-columns: auto minmax(310px, 1fr) auto;
  gap: clamp(8px, 1.5vw, 20px);
  padding: 0 clamp(8px, 2vw, 20px);
  border: 1px solid var(--line);
  border-radius: 12px;
  background: color-mix(in srgb, var(--surface) 96%, transparent);
  -webkit-backdrop-filter: blur(14px) saturate(150%); backdrop-filter: blur(14px) saturate(150%);
  box-shadow: 0 8px 30px rgba(0,0,0,.3);
  container-type: inline-size;
}
#app .tr-scrub {
  position: absolute;
  top: 4px;
  left: 16px;
  right: 16px;
  height: 5px;
  min-width: 0;
  margin: 0;
  background: var(--bg-tint);
  border-radius: 999px;
  z-index: 10;
  transition: height 0.15s ease, top 0.15s ease;
}
/* Generous invisible hit band (~22px) — the painted 5px hairline was nearly
   impossible to click; clicks anywhere in the band land on the bar. Biased
   upward so it never swallows the transport buttons below. */
#app .tr-scrub::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: -12px;
  bottom: -5px;
}
#app .tr-scrub:hover,
#app .tr-scrub.is-dragging {
  height: 7px;
  top: 3px;
}
#app .tr-scrub .fill {
  background: var(--accent) !important;
}
/* Always-visible playhead knob — the hover-only reveal hid the only grab
   affordance, making the seek bar look non-interactive. */
#app .tr-scrub .head {
  border-color: var(--accent) !important;
  opacity: 0.9;
  width: 10px;
  height: 10px;
  transition: opacity 0.15s ease, width 0.15s ease, height 0.15s ease;
}
#app .tr-scrub:hover .head,
#app .tr-scrub.is-dragging .head {
  opacity: 1;
  width: 14px;
  height: 14px;
}
#app .tr-left, #app .tr-center, #app .tr-right { gap: clamp(6px, 1vw, 10px); min-width: 0; }
#app .tr-right { flex-wrap: nowrap; white-space: nowrap; }

/* On-screen piano: fixed dock just above the transport when toggled on. In the
   page-flow layout the dock's inline height collapses, so pin an explicit tall
   height when open (0 when closed) and let the keys fill it. Key sculpting +
   the recessed keybed/felt frame live in notes-editor.css (.piano-dock scope) —
   don't re-declare paddings/radii here or the felt geometry drifts. */
#app .piano-dock {
  position: fixed;
  left: calc(var(--mz-sheet-left) - 16px) !important;
  width: calc(var(--mz-sheet-w) + 32px) !important;
  bottom: 76px; z-index: 29;
  box-shadow: 0 -12px 32px rgba(0,0,0,.3);
  display: grid; grid-template-columns: 46px 1fr 46px; align-items: stretch;
  border-left: 1px solid var(--line);
  border-right: 1px solid var(--line);
  border-top: 1px solid var(--line);
  border-radius: 12px 12px 0 0;
}
#app .piano-dock[data-open="true"] { height: 150px !important; min-height: 150px !important; }
#app .piano-dock[data-open="false"] { height: 0 !important; min-height: 0 !important; }
#app .piano-keys { height: 100%; box-sizing: border-box; }
#app .piano-white-row { height: 100%; }
#app .piano-key-white .piano-key-label { opacity: .6; font-size: 10px; }
#app .piano-pan { font-size: 20px; }

/* ── Editor topbar: hide the hide-md icon duplicates (page-view / score-details
   / keyboard already appear as labeled pills; mixer/search are non-wired
   placeholders). Every real action stays visible — exports show as icons here
   AND in the File menu. (The dead Menu + Parts buttons were removed.) ───────── */
#app .topbar .topbar-tools .icon-btn.hide-md { display: none; }

/* ── Palette: compact + DENSE single flow on desktop. ─────────────────────── */
#app .palette { padding: 7px 16px 8px; gap: 5px; }
#app .palette-group { gap: 1px; }
/* Label + controls ride in one inline-flex cluster, so a group always wraps as
   a whole unit (title never stranded above its controls). */
#app .palette-cluster { display: inline-flex; align-items: center; gap: 8px; }
/* The old separate divider elements are gone — the | is now a left-border on
   each cluster (see below). Hide any stray ones. */
#app .palette-divider { display: none; }

/* Desktop-only flat flow. MUST stay paired with the editor's mobile breakpoint
   (notes-editor.css @media max-width:1024px): below it the mobile tab layout
   owns the palette, so this desktop flattening starts one pixel above at 1025px.
   If you change one breakpoint, change the other — a gap between them lets both
   layouts fight (the #app rules here would flatten the rows the tab layout hides). */
@media (min-width: 1025px) {
  /* Flatten the fixed rows into ONE wrapping flow so groups pack tight and no
     group ever owns a whole row to itself (Time / Staff used to). The rows +
     sections stay in the DOM — the mobile tab layout needs them — but lay out
     transparently here so their clusters become one continuous flow. */
  /* Inter-group spacing is a flex COLUMN-GAP (not per-cluster margin/padding).
     Flex omits the gap before the first cluster of every wrapped line, so line
     starts are already flush and no line opens with a dangling separator —
     WITHOUT any width that depends on wrap position. */
  #app .palette { flex-direction: row; flex-wrap: wrap; align-items: center; row-gap: 7px; column-gap: 25px; }
  #app .palette > .palette-row,
  #app .palette .palette-section { display: contents; }
  /* Divider = a | between groups, drawn as an ABSOLUTELY-POSITIONED 1px line
     centred in the 25px gap to the cluster's left. It is OUT OF FLOW, so showing
     or hiding it changes NO layout width. The first cluster of each visual line
     gets .at-line-start (added by JS on wrap/resize/show-hide); we hide only its
     divider's COLOUR.
     ⚠ Do NOT go back to a left border + margin/padding here. The previous version
     zeroed margin-left+padding-left on .at-line-start — a 24px WIDTH change — so a
     cluster sitting exactly on the flex-wrap boundary would shed 24px → un-wrap →
     regain 24px → re-wrap, once per frame. The palette's ResizeObserver re-ran the
     tagger each frame, so the toolbar grew/shrank a whole row and the ENTIRE page
     vibrated (seen on iPad-Pro widths in Chrome DevTools device mode). Keeping the
     class layout-neutral makes wrapping a pure function of viewport width. */
  #app .palette-cluster { position: relative; }
  #app .palette-cluster::before {
    content: ""; position: absolute; left: -13px; top: 50%; transform: translateY(-50%);
    width: 1px; height: 18px; background: var(--line-strong); pointer-events: none;
  }
  #app .palette-cluster.at-line-start::before { background: transparent; }
}

/* ── Responsive ─────────────────────────────────────────────────────────── */
@media (max-width: 1200px) {
  /* top:22px matches Vocal Remover / the rest of the site (was 0 — the topbar hugged
     the screen edge with no breathing room at tablet widths). */
  .skin-shell { width: auto; margin: 0; padding: 22px 20px 4px 88px; }
}
@media (max-width: 1024px) {
  body.notes-editor-page {
    --mz-rail-w: 56px;             /* matches the canonical collapsed rail (cosmic-rail.css) */
    --mz-sheet-gap: 24px;          /* 12px of breathing on each side of the sheet */
  }
  /* rail mobile behaviour lives in cosmic-rail.css */
  /* Header + hero breathe a consistent ~12px clear of the rail and the right edge.
     top:16px matches Vocal Remover / the rest of the site on mobile (was 6px — the
     topbar sat noticeably tighter to the screen top than every other page). */
  .skin-shell { padding: 16px 12px 4px calc(var(--mz-rail-w) + 12px); }
  .site-nav a { display: none; }
  .site-topbar { padding-top: 0; }
  .hero { margin: 14px auto 16px; }
  .hero h1 { font-size: clamp(24px, 7vw, 34px); }
  .hero p { font-size: 14.5px; }
  #app { padding-left: 58px; }
  #app .transport, #app .piano-dock { left: calc(var(--mz-sheet-left) - 16px) !important; }
  /* Parts drawer: the desktop geometry (left:68px + 344px width) overflows a
     phone viewport — hug the collapsed rail and cap the width to what fits. */
  #app .leftpanel {
    left: var(--mz-rail-w, 56px);
    width: min(calc(100vw - var(--mz-rail-w, 56px) - 10px), 344px);
  }
  /* The sticky control deck (toolbar + palette) breathes WITH — and aligns to —
     the score sheet, instead of running full-bleed (it used to overflow the right
     edge and tuck under the rail). Width = sheet, centred in the content area. */
  #app .editor-deck {
    width: var(--mz-sheet-w) !important;
    margin-left: calc((var(--mz-content-viewport-w) - var(--mz-sheet-w)) / 2) !important;
  }
  #app .score-page { width: var(--mz-sheet-w); padding: 22px 16px 44px; margin: 16px auto 28px; }
  #app .topbar-row { display: contents !important; }

  /* ── Top panel: ONE CENTRED row of four EQUAL tabs — Sheet · Piano · Menu · Help.
     The Sheet/Piano segmented toggle is un-grouped on phones (its wrapper goes
     transparent with gap = the row gap) so all four read as standalone pills with
     EVEN spacing, centred. (Undo/Redo moved to the transport; export/share/MIDI/
     collab chips hidden; this collapses the old four stacked rows into one.) */
  #app .topbar { flex-direction: row !important; flex-wrap: wrap; align-items: center; justify-content: center; gap: 6px; padding: 6px; }
  #app .topbar .spacer { display: none !important; }
  #app .topbar .topbar-tools { display: none !important; }   /* undo/redo now live in the transport */
  #app .topbar .ne-midi-button-wrap, #app .topbar .ne-midi-button,
  #app .topbar .peer-list, #app .topbar .collab-status,
  #app .topbar .keyboard-toggle-pill { display: none !important; }

  /* Un-group the toggle: invisible wrapper, gap == row gap, so Sheet↔Piano
     spacing equals Piano↔Menu equals Menu↔Help — four evenly-spaced tabs. */
  #app .topbar .ne-view-toggle {
    display: inline-flex !important; flex: 0 0 auto; min-width: 0;
    padding: 0; border: 0; background: none; gap: 6px;
  }
  /* All four share ONE pill style (icon + text); the active VIEW tab (Sheet or
     Piano) is filled with the accent, the others are outlined. */
  #app .topbar .ne-view-toggle .ne-vt-btn,
  #app .topbar .ne-topbar-tab {
    display: inline-flex !important; align-items: center; justify-content: center; gap: 4px;
    width: auto; min-width: 0; height: 32px; padding: 0 9px; flex: 0 0 auto;
    border: 1px solid var(--line); border-radius: 999px;
    background: var(--bg-tint); color: var(--ink-2);
    font: 600 11px/1 var(--font-sans); white-space: nowrap;
  }
  #app .topbar .ne-view-toggle .ne-vt-btn svg,
  #app .topbar .ne-topbar-tab svg { width: 16px; height: 16px; flex: 0 0 auto; }
  #app .topbar .ne-topbar-tab span { display: inline; }
  #app .topbar .ne-view-toggle .ne-vt-btn.is-active {
    background: var(--accent); color: #fff; border-color: var(--accent);
  }
  #app .topbar .ne-topbar-tab:active,
  #app .topbar .ne-topbar-tab[aria-expanded="true"] {
    color: var(--ink); border-color: color-mix(in srgb, var(--accent) 45%, var(--line));
  }

  /* The editor (⋯) menu is portaled to <body>, so it doesn't inherit #app's
     rail-clearing left padding — at left:0 it slid UNDER the fixed nav rail and
     the rail clipped the first letter of every item ("New"→"lew"). Float it as a
     card clear of the rail, matching the Tweaks sheet. */
  .mobile-editor-menu {
    left: calc(var(--mz-rail-w, 58px) + 8px) !important;
    right: 8px !important;
    bottom: 8px !important;
    border-radius: 14px !important;
  }
}
@media (prefers-reduced-motion: reduce) {
  .skin-stars span { animation: none !important; }
}

/* Float the live-tempo badge absolutely above the speed control (its new
   anchor since BPM moved to the palette) to prevent layout shifts */
#app .tr-speed {
  position: relative;
}
#app .tr-tempo-live {
  position: absolute;
  bottom: 38px;
  left: 50%;
  transform: translateX(-50%);
  margin: 0;
  z-index: 15;
  white-space: nowrap;
  box-shadow: 0 4px 12px rgba(0,0,0,0.35);
  animation: pulse-live 1.5s infinite alternate;
}

@keyframes pulse-live {
  0% { transform: translateX(-50%) scale(1); }
  100% { transform: translateX(-50%) scale(1.03); }
}

/* Responsive transport adjustments using container queries to prevent overlapping */
/* The dB read-out is informational only — it is the first thing to yield so
   the play cluster keeps its air inside the sheet-aligned bar. */
@container (max-width: 1220px) {
  #app .transport .tr-db-badge { display: none !important; }
}
@container (max-width: 950px) {
  #app .transport .tr-vol { display: none !important; }
  #app .transport .tr-db-badge { display: none !important; }
}
@container (max-width: 820px) {
  #app .transport .tr-total-bars { display: none !important; } /* Hide "/ total bars" */
  #app .transport .tr-speed input[type="range"] { display: none !important; } /* Hide range slider, keep value label */
}
@container (max-width: 680px) {
  #app .transport .tr-jump { display: none !important; } /* jump control needs elbow room */
  #app .tr-scrub { display: none !important; } /* Hide progress bar entirely on tiny viewports */
}

/* ════════════════════════════════════════════════════════════════════════
   Mobile transport — clean two-row layout (≤1024px)
   ------------------------------------------------------------------------
   On phones the desktop 3-column grid (auto · minmax(310px,1fr) · auto) cannot
   fit the play cluster + read-outs in a ~314–344px bar: the centre track ate
   the whole width (computes to `0px 310px 0px`) so the time read-out painted ON
   TOP of rewind/stop/play and the A/B chip overlapped count-in + speed — the
   "corrupted transport" bug. Here we flatten the three groups with
   display:contents and lay every control out as a centred flex in TWO tidy,
   predictable rows:
     • Row 1 — core transport: rewind · stop · PLAY · forward
     • Row 2 — read-outs + practice: time · loop · metronome · A/B · count-in
   The MIDI record dot (no Web-MIDI on phones) and the non-adjustable speed
   read-out (its slider is already hidden on mobile) are dropped here so Row 2
   fits one clean line down to 320px — keeping the bar a stable two rows. That
   stable height (--ne-tp-h) lets us reserve matching score-scroll room and lift
   EVERY transport-anchored floating control (piano dock, practice sheet, the
   keyboard-hint + Tweaks FABs, the page-nav arrows) above it, so nothing is
   ever hidden behind, or painted on top of, the bar.
   ════════════════════════════════════════════════════════════════════════ */
@media (max-width: 1024px) {
  body.notes-editor-page { --ne-tp-h: 116px; }   /* stable 2-row height — anchor for everything below */

  #app .transport {
    display: flex !important;
    grid-template-columns: none !important;        /* defuse the desktop/legacy grid */
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    height: auto !important;
    min-height: var(--ne-tp-h);
    /* Align to the same ~12px breathing as the sheet/deck (left = sheet-left,
       right = matching gap) so the whole stack shares one consistent margin. */
    left: calc(var(--mz-rail-w) + 12px) !important;
    right: 12px !important;
    width: auto !important;
    bottom: calc(8px + env(safe-area-inset-bottom, 0px));
    padding: 10px 7px 8px;
    row-gap: 6px;
    column-gap: 4px;
    overflow: visible;                             /* we wrap, never scroll sideways */
  }

  /* Flatten the three groups so every control becomes a direct flex item we can
     order freely (children keep their own display, so the existing display:none
     read-out hides still apply). */
  #app .transport .tr-left,
  #app .transport .tr-center,
  #app .transport .tr-right { display: contents !important; }

  /* Drop the two phone-irrelevant controls so Row 2 stays one tidy line:
       • record  — Web-MIDI recording is unavailable on phones;
       • speed   — its slider is already hidden on mobile, leaving only a
                   non-interactive "100%" read-out (pure clutter here). */
  #app .transport .tr-record,
  #app .transport .tr-jump,
  #app .transport .tr-speed { display: none !important; }

  /* Controls never shrink — they keep touch-friendly sizes. */
  #app .transport .tr-btn,
  #app .transport .tr-play,
  #app .transport .tr-loop-ab,
  #app .transport .tr-countin,
  #app .transport .tr-time { flex: 0 0 auto; }

  /* ── Two-row control panel — undo/redo + the two utility FABs at the 4 CORNERS.
        Row 1:  [undo] · rewind · stop · PLAY · forward · [redo]
        Row 2:  [kbd] · zoom · loop · metronome · A/B · count-in · [tweaks]
        The time read-out floats over the left of the seek bar (below). Floating
        .kbd-hint-fab / .twk-fab AND the topbar's undo/redo are hidden — they all
        live in the bar now. */
  #app .kbd-hint-fab,
  body.notes-editor-page .twk-fab { display: none !important; }
  #app .transport .tr-kbd,
  #app .transport .tr-tweaks,
  #app .transport .tr-undo,
  #app .transport .tr-redo {
    display: inline-flex !important; align-items: center; justify-content: center;
  }

  /* Row 1 (order 1·2·3): undo → core four → redo. */
  #app .transport .tr-undo { order: 1; }
  #app .transport .tr-center > :nth-child(-n+4) { order: 2; }
  #app .transport .tr-redo { order: 3; }
  /* Forced flex line break between the two rows. */
  #app .transport::after {
    content: ""; order: 4; flex: 0 0 100%; width: 100%; height: 0; margin: 0;
  }
  /* Row 2 (order 5–9): kbd → zoom → loop·metronome·A/B → count-in → tweaks. */
  #app .transport .tr-kbd { order: 5; }
  #app .transport .tr-zoom { display: inline-flex !important; order: 6; }
  #app .transport .tr-center > :nth-child(n+5) { order: 7; }   /* loop · metronome · A/B */
  #app .transport .tr-countin { order: 8; }
  #app .transport .tr-tweaks { order: 9; }

  /* Sizes. Core four (play a touch larger) + the four corner utilities (small,
     secondary). Tuned so both rows fit one line on a typical ~390px phone. */
  #app .transport .tr-center > .tr-btn:nth-child(-n+4) { width: 38px; height: 38px; }
  #app .transport .tr-play { width: 44px; height: 44px; }
  #app .transport .tr-undo,
  #app .transport .tr-redo,
  #app .transport .tr-kbd,
  #app .transport .tr-tweaks { width: 30px; height: 30px; }
  #app .transport .tr-undo svg,
  #app .transport .tr-redo svg,
  #app .transport .tr-kbd svg,
  #app .transport .tr-tweaks svg { width: 17px; height: 17px; }

  /* Zoom (free-floating .canvas-floats dropped on phones) — compact [−] % [+]. */
  #app .canvas-floats { display: none !important; }
  #app .transport .tr-zoom .tr-zoom-btn { width: 22px; height: 22px; }
  #app .transport .tr-zoom .tr-zoom-val { min-width: 18px; font-size: 9.5px; }

  /* Row-2 practice chips — compact so the seven-item row fits. */
  #app .transport .tr-center > .tr-btn:nth-child(n+5) { width: 32px; height: 32px; }
  #app .transport .tr-loop-ab { width: 30px; height: 32px; font-size: 10.5px; }
  #app .transport .tr-countin { height: 32px; min-width: 26px; padding: 0 5px; font-size: 10px; }

  /* Time read-out → floats at the LEFT of the seek bar (elapsed-time pattern),
     freeing a Row-2 slot so the panel stays two tidy rows. The scrub is inset on
     the left to clear it but still reaches the right edge. */
  #app .transport .tr-time {
    position: absolute; left: 12px; top: 2px; z-index: 2;
    min-width: 0; padding: 0; font: 600 9.5px/12px var(--font-mono, var(--font-sans));
    color: var(--ink-3); pointer-events: none;
  }
  #app .transport .tr-time b { color: var(--ink-2); }

  /* Progress scrub → a proper touch-friendly DRAG-to-seek control (panels.jsx
     handles the pointer drag + seek-on-release; this gives it grabbable size).
     The slim painted track sits near the top; a transparent ::before grows the
     hit area to ~18px so a finger can catch it, biased only slightly downward so
     it doesn't swallow taps meant for the Play cluster ~14px below. touch-action:
     none lets the drag run without scrolling the page — scoped to .tr-scrub ONLY,
     never the score scroll container. */
  #app .transport .tr-scrub {
    display: block !important;
    top: 6px; left: 64px; right: 12px; height: 5px;   /* left inset clears the floated time read-out */
    border-radius: 999px;
    cursor: pointer;
    touch-action: none;
  }
  #app .transport .tr-scrub::before {
    content: ""; position: absolute; left: 0; right: 0;
    top: -6px; bottom: -7px;            /* ~18px hit pad; lower edge stays in the top gutter */
  }
  #app .transport .tr-scrub.is-dragging { height: 6px; }
  /* Always-visible thumb on touch (the desktop hover-reveal head is useless here). */
  #app .transport .tr-scrub .head {
    opacity: 1 !important;
    width: 16px; height: 16px;
    top: 50%; transform: translate(-50%, -50%);
    background: var(--surface);
    border: 2px solid var(--accent);
    box-shadow: 0 1px 5px rgba(0,0,0,.45);
  }
  #app .transport .tr-scrub.is-dragging .head { width: 20px; height: 20px; }

  /* Reserve room at the end of the score scroll for the taller bar. */
  #app .app {
    padding-bottom: calc(var(--ne-tp-h) + 30px + env(safe-area-inset-bottom, 0px) + var(--piano-dock-h, 0px)) !important;
  }

  /* ── Lift every transport-anchored floating control above the taller bar so it
        is never covered by, nor paints on top of, the transport. ─────────────
        (All use !important so they win regardless of source order vs. the base
        rules they override — incl. the JS-injected .twk-fab/.twk-panel style.) */
  /* Practice sheet docks flush just above the bar, aligned to it. (The on-screen
     piano dock no longer renders on phones — the mobile piano is the full-screen
     landscape Piano-Roll view chosen via the topbar Sheet⟷Piano toggle.) */
  #app .practice-panel {
    bottom: calc(var(--ne-tp-h) + 14px + env(safe-area-inset-bottom, 0px)) !important;
  }

  /* Bottom-corner FABs / page-nav arrows clear the bar with a small gap. NOTE: the
     Tweaks PANEL is deliberately excluded — on mobile it's a full-height modal
     bottom sheet (see tweaks-panel.jsx @media, bottom:8 + scrim); only the closed
     gear FAB needs to ride above the transport. */
  #app .kbd-hint,
  #app .kbd-hint-fab,
  #app .score-nav-arrow,
  body.notes-editor-page .twk-fab {
    bottom: calc(var(--ne-tp-h) + 22px + env(safe-area-inset-bottom, 0px)) !important;
  }
}

/* ════════════════════════════════════════════════════════════════════════
   Mobile landscape piano — full-screen CONTAINED unit (MODE C)
   ------------------------------------------------------------------------
   A <body> portal (escapes #app's stacking context; covers rail + menus). It is
   a framed panel: a transport strip (play/pause · time · progress scrub · octave
   scroll · exit) above a bordered piano FRAME holding BIG keys that scroll
   horizontally (Piano-page-style, ~52px wide). The stage is CSS-rotated 90° to
   simulate landscape on a phone held PORTRAIT, and fills normally when the device
   is physically rotated — the @media (orientation) rules switch automatically.
   ════════════════════════════════════════════════════════════════════════ */
body.ne-landscape-open { overflow: hidden !important; }
.ne-landscape { position: fixed; inset: 0; z-index: 9001; background: var(--bg, #0f0f23); }
.ne-landscape-stage {
  position: fixed; top: 0; left: 0; transform-origin: top left;
  display: flex; align-items: center; justify-content: center; box-sizing: border-box;
  padding: calc(8px + env(safe-area-inset-top, 0px)) calc(8px + env(safe-area-inset-right, 0px))
           calc(8px + env(safe-area-inset-bottom, 0px)) calc(8px + env(safe-area-inset-left, 0px));
}
/* Held portrait → rotate to fill the screen sideways (turn the phone clockwise). */
@media (orientation: portrait) {
  .ne-landscape-stage { width: 100dvh; height: 100dvw; transform: rotate(90deg) translateY(-100%); }
}
/* Device actually in landscape → fill normally, no CSS rotation. */
@media (orientation: landscape) {
  .ne-landscape-stage { width: 100dvw; height: 100dvh; transform: none; }
}
.ne-landscape-panel { flex: 0 1 auto; width: 100%; min-width: 0; display: flex; flex-direction: column; gap: 8px; }
.ne-landscape-bar {
  flex: 0 0 auto; display: flex; align-items: center; gap: 10px; padding: 7px 10px;
  border-radius: 12px; border: 1px solid var(--line);
  background: color-mix(in srgb, var(--surface) 94%, transparent);
  box-shadow: 0 6px 18px -10px rgba(0,0,0,.5);
}
.ne-ls-btn {
  flex: 0 0 auto; width: 40px; height: 40px; display: grid; place-items: center;
  border: 1px solid var(--line); border-radius: 10px; cursor: pointer; line-height: 1;
  background: color-mix(in srgb, var(--surface) 70%, transparent); color: var(--ink); font-size: 15px;
}
.ne-ls-btn:hover { color: var(--accent); border-color: var(--accent); }
.ne-ls-accent { background: var(--accent); color: #0f0f23; border-color: transparent; }
.ne-ls-accent:hover { color: #0f0f23; filter: brightness(1.06); }
.ne-ls-time {
  flex: 0 0 auto; font-family: var(--font-mono); font-size: 12px; color: var(--ink-2);
  min-width: 62px; text-align: center;
}
.ne-ls-scrub { flex: 1 1 auto; min-width: 60px; height: 22px; accent-color: var(--accent); cursor: pointer; }
.ne-landscape-exit { font-size: 16px; }
/* The contained piano FRAME — dark recessed body, like the Piano page's unit. It
   wraps the keyboard snugly (height = the keys) and the whole panel is centred in
   the screen. */
.ne-landscape-frame {
  flex: 0 0 auto; overflow: hidden; padding: 8px;
  border: 1px solid var(--line-strong, var(--line)); border-radius: 12px;
  background: linear-gradient(180deg, #07060a, #100d14);
  box-shadow: inset 0 2px 8px rgba(0,0,0,.7);
}
.ne-landscape-keys {
  overflow-x: auto; overflow-y: hidden;
  touch-action: pan-x; -webkit-overflow-scrolling: touch;
  container-type: inline-size;   /* so the keys can be sized as a fraction of the visible width */
}
.ne-landscape-keys::-webkit-scrollbar { height: 6px; }
.ne-landscape-keys::-webkit-scrollbar-thumb { background: var(--line-strong, var(--line)); border-radius: 3px; }
.ne-landscape-keys .piano-keys { height: auto; width: max-content; box-sizing: border-box; padding: 0; position: relative; }
/* Size each white key to 1/29 of the visible width so C2–C6 (29 white keys) all
   show at once; the rendered range runs to C7, so the extra keys overflow and are
   a minimal scroll / follow away. Fixed-px fallback where cqi is unsupported. */
.ne-landscape-keys .piano-white-row {
  height: auto; width: max-content;
  grid-template-columns: repeat(var(--white-count), 30px);
  grid-template-columns: repeat(var(--white-count), calc(100cqi / 29));
}
/* Proportional height (~1:5) — like a real piano, not stretched bars. */
.ne-landscape-keys .piano-key-white { height: auto; aspect-ratio: 1 / 5; min-height: 0; }
.ne-landscape-keys .piano-key-black { top: 0; height: 62%; }
.ne-landscape-keys .piano-key-white .piano-key-label { font-size: 9px; opacity: .55; }
/* The played-note feedback keeps its accent glow but NOT the pianoFlash scaleY —
   on big tall keys that squish reads as the keys "warping". */
.ne-landscape-keys .piano-key.is-playing { animation: none !important; }
/* Steadier GPU compositing of the rotated stage + scrolling keyboard. */
.ne-landscape-stage { backface-visibility: hidden; -webkit-backface-visibility: hidden; }
.ne-landscape-keys { will-change: scroll-position; }

/* ── Seamless cross-page navigation ─────────────────────────────────────────
   Opt every cosmic page into the browser's cross-document View Transitions, so
   moving between tools cross-fades instead of flashing a full white reload. The
   rail, hero and cosmic background get stable transition names so the browser
   treats them as the SAME element across pages — they hold their place while
   only the tool body swaps, for an SPA-like feel with no rebuild flicker.
   (Scripts still load per tool, but the shared chrome is cached + never
   visibly reloads.) */
/* Cross-document view-transitions are intentionally OFF for the editor: a
   reload mid-transition rejects the transition promise ("Transition was
   skipped"), and this heavy page is better navigated instantly anyway. */
/* @view-transition { navigation: auto; } */

.skin-bg { view-transition-name: mz-bg; }
/* .rail view-transition-name is set in cosmic-rail.css */
.hero    { view-transition-name: mz-hero; }

/* Rail + background are persistent chrome — snap instantly (no fade) so they
   look perfectly static while the page changes. */
::view-transition-group(mz-bg),
::view-transition-group(mz-rail) { animation-duration: 0s; }
::view-transition-old(mz-bg), ::view-transition-new(mz-bg),
::view-transition-old(mz-rail), ::view-transition-new(mz-rail) { animation: none; }

/* Tool body + hero cross-fade smoothly. */
::view-transition-old(root), ::view-transition-new(root) {
  animation-duration: 0.34s;
  animation-timing-function: cubic-bezier(.4, 0, .2, 1);
}
::view-transition-group(mz-hero) { animation-duration: 0.34s; }

@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*),
  ::view-transition-old(*), ::view-transition-new(*) { animation-duration: 0.01s !important; }
}

/* ── Keyboard note-entry cheat sheet ──────────────────────────────────────
   Dismissible card pinned in the left gutter (clear of the right-side zoom and
   the bottom-right Tweaks gear); collapses to a small keyboard chip. */
#app .kbd-hint {
  position: fixed; left: 78px; bottom: 80px; z-index: 30; width: 248px;
  border-radius: 16px; border: 1px solid var(--line);
  background: color-mix(in srgb, var(--surface) 96%, transparent);
  -webkit-backdrop-filter: blur(18px) saturate(150%); backdrop-filter: blur(18px) saturate(150%);
  box-shadow: 0 18px 44px -18px rgba(0,0,0,.62);
  color: var(--ink); overflow: hidden;
}
#app .kbd-hint-head { display: flex; align-items: center; justify-content: space-between;
  padding: 10px 10px 9px 13px; border-bottom: 1px solid var(--line); }
#app .kbd-hint-title { display: inline-flex; align-items: center; gap: 8px;
  font-family: var(--font-serif); font-style: italic; font-size: 15px; color: var(--ink); }
#app .kbd-hint-title svg { color: var(--accent); }
#app .kbd-hint-x { appearance: none; border: 0; background: transparent; color: var(--ink-2);
  width: 24px; height: 24px; border-radius: 7px; cursor: pointer; font-size: 12px; }
#app .kbd-hint-x:hover { background: color-mix(in srgb, var(--ink) 9%, transparent); color: var(--ink); }
#app .kbd-hint-list { list-style: none; margin: 0; padding: 9px 13px 12px; display: flex; flex-direction: column; gap: 7px; }
#app .kbd-hint-list li { display: flex; align-items: center; gap: 10px; }
#app .kbd-hint-keys { display: inline-flex; align-items: center; gap: 3px; flex-shrink: 0; min-width: 96px; }
#app .kbd-hint-key {
  font-family: var(--font-mono); font-size: 10.5px; font-weight: 600; line-height: 1;
  min-width: 17px; padding: 4px 5px; text-align: center; color: var(--ink);
  background: color-mix(in srgb, var(--ink) 8%, transparent);
  border: 1px solid var(--line-strong, var(--line)); border-bottom-width: 2px; border-radius: 5px;
}
#app .kbd-hint-plus, #app .kbd-hint-dash { color: var(--ink-3); font-size: 11px; padding: 0 1px; }
#app .kbd-hint-desc { font-family: var(--font-sans); font-size: 12px; color: var(--ink-2); }
#app .kbd-hint-desc em { font-style: normal; color: var(--ink-3); font-size: 11px; }

#app .kbd-hint-fab {
  position: fixed; left: 78px; bottom: 80px; z-index: 30;
  width: 44px; height: 44px; border-radius: 13px; cursor: pointer; display: grid; place-items: center;
  border: 1px solid var(--line); color: var(--ink-2);
  background: color-mix(in srgb, var(--surface) 92%, transparent);
  -webkit-backdrop-filter: blur(14px) saturate(150%); backdrop-filter: blur(14px) saturate(150%);
  box-shadow: 0 14px 32px -16px rgba(0,0,0,.6); transition: color .15s ease, transform .15s ease;
}
#app .kbd-hint-fab:hover { color: var(--accent); transform: translateY(-1px); }

@media (max-width: 1024px) {
  #app .kbd-hint, #app .kbd-hint-fab { left: 64px; bottom: 74px; }
  #app .kbd-hint { width: min(248px, calc(100vw - 84px)); }
}

/* ── My Projects drawer (local library, mirrors Beat Maker) ───────────────── */
#app + .np-scrim, .np-scrim {
  position: fixed; inset: 0; z-index: 9000; display: flex; justify-content: flex-end;
  background: rgba(6,4,16,.5); -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px);
}
.np-drawer {
  width: min(440px, 96vw); height: 100%; display: flex; flex-direction: column;
  background: color-mix(in srgb, var(--surface) 96%, #000); color: var(--ink);
  border-left: 1px solid var(--line-strong, var(--line));
  box-shadow: -30px 0 80px -30px rgba(0,0,0,.7);
  font-family: var(--font-sans); animation: npIn .26s cubic-bezier(.2,.7,.3,1);
}
@keyframes npIn { from { transform: translateX(24px); opacity: .4; } }
.np-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px;
  padding: 22px 22px 16px; border-bottom: 1px solid var(--line); }
.np-eyebrow { font-size: 11px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase; color: var(--accent); }
.np-title { margin: 4px 0 4px; font-family: var(--font-serif); font-style: italic; font-weight: 400; font-size: 28px; color: var(--ink); }
.np-sub { font-size: 12px; color: var(--ink-2); }
.np-x { appearance: none; border: 0; background: transparent; color: var(--ink-2); cursor: pointer;
  width: 30px; height: 30px; border-radius: 9px; font-size: 14px; flex-shrink: 0; }
.np-x:hover { background: color-mix(in srgb, var(--ink) 9%, transparent); color: var(--ink); }
.np-save { display: flex; gap: 8px; padding: 16px 22px; border-bottom: 1px solid var(--line); }
.np-save-input { flex: 1; min-width: 0; height: 40px; padding: 0 13px; border-radius: 11px;
  border: 1px solid var(--line-strong, var(--line)); background: color-mix(in srgb, var(--bg) 45%, transparent);
  color: var(--ink); font: inherit; font-size: 13.5px; outline: none; }
.np-save-input:focus { border-color: color-mix(in srgb, var(--accent) 60%, var(--line)); }
.np-save-btn { flex-shrink: 0; height: 40px; padding: 0 16px; border: 0; border-radius: 11px; cursor: pointer;
  font: inherit; font-weight: 600; font-size: 13.5px; color: #0f0f23; background: var(--accent);
  box-shadow: 0 10px 26px -12px color-mix(in srgb, var(--accent) 70%, transparent); }
.np-save-btn:hover { filter: brightness(1.07); }
.np-list { flex: 1; overflow-y: auto; padding: 14px 18px 22px; display: flex; flex-direction: column; gap: 9px; }
.np-empty { text-align: center; color: var(--ink-2); font-size: 13px; line-height: 1.6; padding: 40px 16px; }
.np-card { display: flex; align-items: stretch; gap: 6px; border-radius: 14px; border: 1px solid var(--line);
  background: color-mix(in srgb, var(--ink) 4%, transparent); overflow: hidden; transition: border-color .15s ease, transform .15s ease; }
.np-card:hover { border-color: color-mix(in srgb, var(--accent) 45%, var(--line)); }
.np-card-main { flex: 1; min-width: 0; display: flex; align-items: center; gap: 12px; padding: 12px 8px 12px 14px;
  background: transparent; border: 0; cursor: pointer; text-align: left; color: inherit; font: inherit; }
.np-card-ico { width: 38px; height: 38px; flex-shrink: 0; border-radius: 10px; display: grid; place-items: center;
  color: var(--accent); background: color-mix(in srgb, var(--accent) 14%, transparent); }
.np-card-text { min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.np-card-name { font-size: 14.5px; font-weight: 600; color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.np-card-main:hover .np-card-name { color: var(--accent); }
.np-card-meta { font-size: 11.5px; color: var(--ink-2); }
.np-rename { width: 100%; font: inherit; font-size: 14px; font-weight: 600; color: var(--ink);
  background: var(--bg); border: 1px solid var(--accent); border-radius: 7px; padding: 3px 7px; outline: none; }
.np-card-actions { display: flex; align-items: center; gap: 2px; padding: 0 8px 0 0; flex-shrink: 0; }
.np-card-actions button { width: 30px; height: 30px; border: 0; background: transparent; color: var(--ink-2);
  cursor: pointer; border-radius: 8px; display: grid; place-items: center; transition: .15s ease; }
.np-card-actions button:hover { background: color-mix(in srgb, var(--ink) 10%, transparent); color: var(--ink); }
.np-card-actions .np-del:hover { background: color-mix(in srgb, #ef4444 22%, transparent); color: #ff8a8a; }
.np-toast { position: absolute; left: 50%; bottom: 18px; transform: translateX(-50%); z-index: 5;
  padding: 10px 18px; border-radius: 11px; background: color-mix(in srgb, var(--bg2, var(--bg)) 94%, transparent);
  border: 1px solid var(--accent-line, var(--line)); color: var(--ink); font-size: 13px; font-weight: 600;
  box-shadow: 0 16px 36px -16px rgba(0,0,0,.6); -webkit-backdrop-filter: blur(16px); backdrop-filter: blur(16px); }

/* ── Page view: FIXED-size A4 box (zoom changes engraving density inside, not
   the box). The old `calc(840px * var(--zoom-level)/100)` magnified a frozen
   render → bars-per-line never changed. Now Verovio reflows the A4 content
   (pageWidth/Height ∝ 1/zoom) and the SVG (viewBox, width:100%) fills this box.
   `min(840px,100%)` keeps it responsive so mobile never gets horizontal scroll. */
#app .score-paper-page {
  width: min(840px, 100%) !important;
  min-height: 1088px !important;
  padding: 40px 52px 30px !important;
}
#app .verovio-host--paged[data-paper-size="letter"] .score-paper-page {
  min-height: 1060px !important;
}
/* The reflowed A4 SVG fills the fixed page box. */
#app .score-paper-page svg { display: block; width: 100%; height: auto; }
@media (max-width: 1024px) {
  /* On phones the page box is content-tall (no fixed A4 min-height) so a short
     score isn't a tall empty sheet, and width:100% (via min()) blocks h-scroll. */
  #app .score-paper-page { min-height: auto !important; padding: 20px 14px 26px !important; }
}

/* Position the pager pill relatively below the editor deck so it never overlaps toolbars */
#app .score-pager {
  position: relative !important;
  top: auto !important;
  margin: 14px auto !important;
  z-index: 10 !important;
  display: inline-flex !important;
}

/* MS-377 — page-view navigation arrows flanking the sheet. The A4 page is
   taller than the viewport, so the arrows are viewport-FIXED and stay reachable
   while the user scrolls a tall page. Horizontally they hug the sheet's
   left/right edges via --ne-page-left / --ne-page-right (set in JS from the live
   page rect, so they track zoom + resize). Vertically they are centred in the
   CANVAS region *below the sticky toolbar deck* (--ne-deck-bottom) — never at a
   raw 50% of the viewport, because the toolbar deck is WIDER than the sheet and
   a short window would otherwise drop the arrows on top of the palette. They
   borrow the Tweaks-panel accent so they read as part of the active theme. The
   canvas is NOT inside the backdrop-filter deck, so fixed resolves to the
   viewport here. */
#app .verovio-host--paged .score-paged-stage {
  display: flex;
  align-items: flex-start;
  justify-content: center;
  width: 100%;
}
#app .score-nav-arrow {
  position: fixed;
  /* Centre of the band between the toolbar deck and the floating transport bar
     (transport is bottom:12 + height:64 → reserve ~84px) so the arrows never
     touch the palette above nor the transport below. */
  top: calc(var(--ne-deck-bottom, 150px) + (100dvh - var(--ne-deck-bottom, 150px) - 84px) / 2);
  transform: translateY(-50%);
  width: 54px;
  height: 54px;
  display: grid;
  place-items: center;
  border-radius: 999px;
  cursor: pointer;
  color: var(--accent-ink, #fff);
  background: linear-gradient(135deg, var(--accent2, var(--accent)), var(--accent));
  border: 1px solid color-mix(in srgb, var(--accent) 55%, var(--line));
  box-shadow: 0 14px 30px -10px color-mix(in srgb, var(--accent) 70%, transparent);
  transition: box-shadow .15s ease, opacity .15s ease, filter .15s ease;
  z-index: 30;
}
#app .score-nav-arrow.prev {
  right: calc(100vw - var(--ne-page-left, 80px) + 14px);
  left: auto;
}
#app .score-nav-arrow.next {
  left: calc(var(--ne-page-right, 92vw) + 14px);
  right: auto;
}
#app .score-nav-arrow:hover {
  box-shadow: 0 18px 38px -10px color-mix(in srgb, var(--accent) 85%, transparent);
  filter: brightness(1.07);
}
#app .score-nav-arrow:active { filter: brightness(0.95); }
#app .score-nav-arrow:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}
#app .score-nav-arrow[disabled] {
  opacity: .32;
  cursor: default;
  pointer-events: none;
  box-shadow: none;
  filter: grayscale(.4);
}
#app .score-nav-arrow svg { width: 25px; height: 25px; }

/* No room for side arrows — on phones / narrow widths (no side gutter) OR short
   windows (the tall toolbar deck + transport leave no vertical band beside the
   sheet) — overlay the arrows as fixed bottom-corner pills above the transport,
   clear of the sheet, the side zoom widget, and each other. */
@media (max-width: 980px), (max-height: 760px) {
  #app .score-nav-arrow {
    top: auto;
    bottom: 96px;
    transform: none;
    width: 48px;
    height: 48px;
    z-index: 44;
  }
  #app .score-nav-arrow.prev { left: 14px; right: auto; }
  #app .score-nav-arrow.next { right: 14px; left: auto; }
  #app .score-nav-arrow svg { width: 22px; height: 22px; }
}

/* Contain the notes sheet and prevent any overflow outside the gray area */
#app .score-page {
  overflow-x: auto !important;
  max-width: 100% !important;
}
#app .verovio-host {
  max-width: 100% !important;
  overflow-x: visible !important;
}

/* Top scroll-clip mask — RETIRED. The deck now sticks at top:0 (no gap above it),
   so the sheet never scrolls into a strip above the deck and there's nothing to
   cover. This strip's gradient never quite matched the textured backdrop behind it
   (it painted pure gradient while .skin-bg adds the blurred nebula), so it read as
   a faint horizontal seam near the top — an artifact the other pages never had. */
.ne-viewport-mask-top {
  display: none !important;
}
/* The BOTTOM mask is retired: the fixed transport already covers the bottom, the
   page reserves padding for it, and a bottom-anchored slice can't align to the
   top-anchored backdrop — so it would only risk a seam with nothing to clip. */
.ne-viewport-mask-bottom { display: none !important; }
