
  /* ─── Theme variables ─────────────────────────────────── */
  :root, body[data-theme="dark"] {
    /* page */
    --page-bg-1: #161a24;
    --page-bg-2: #08090f;
    /* slab */
    --slab-fill-1: #3a3d44;
    --slab-fill-2: #2e3138;
    --slab-fill-3: #26282e;
    --slab-edge-top: rgba(255,255,255,0.12);
    --slab-edge-bottom: rgba(0,0,0,0.4);
    --slab-brand: rgba(255,255,255,0.55);
    --slab-divider: rgba(255,255,255,0.1);
    --slab-affordance: rgba(255,255,255,0.5);
    /* key bed (recessed area behind keys) */
    --keybed-fill: #15171a;
    /* white keys */
    --whitekey-fill-1: #494c54;
    --whitekey-fill-2: #525560;
    --whitekey-fill-3: #5a5e69;
    --whitekey-shadow: rgba(0,0,0,0.55);
    --whitekey-pocket: rgba(0,0,0,0.5);
    --whitekey-dimple: rgba(0,0,0,0.4);
    /* black keys */
    --blackkey-fill-1: #1a1c20;
    --blackkey-fill-2: #101216;
    --blackkey-fill-3: #08090c;
    --blackkey-top: rgba(255,255,255,0.15);
    --blackkey-pocket: rgba(0,0,0,0.7);
    /* held key brightness (filter values) */
    --whitekey-held-brightness: 0.7;
    --blackkey-held-brightness: 0.7;
    /* green wash opacity when held — 0 in dark mode, subtle in light */
    --whitekey-wash-opacity: 0;
    /* knob */
    --knob-ring: #1a1c20;
    --knob-face: #2e3138;
    --knob-ring-stroke: rgba(255,255,255,0.15);
    --knob-indicator: rgba(255,255,255,0.7);
    /* labels */
    --patch-active: rgba(255,255,255,0.92);
    --patch-inactive: rgba(255,255,255,0.55);
    /* dock */
    --dock-bg: rgba(28, 30, 36, 0.65);
    --dock-border: rgba(255,255,255,0.1);
    --dock-shadow: rgba(0,0,0,0.28);
    --dock-edge-top: rgba(255,255,255,0.08);
    --dock-btn-bg: rgba(255,255,255,0.05);
    --dock-btn-hover: rgba(255,255,255,0.12);
    --dock-btn-active-bg: rgba(255,255,255,0.18);
    --dock-btn-active-border: rgba(255,255,255,0.4);
    --dock-divider: rgba(255,255,255,0.1);
    --dock-icon: rgba(255,255,255,0.85);
    --dock-icon-active: #ffffff;
    --active-dot: rgba(255,255,255,0.7);
    /* menu */
    --menu-bg: rgba(20, 22, 28, 0.92);
    --menu-border: rgba(255,255,255,0.1);
    --menu-label: rgba(255,255,255,0.85);
    --menu-status: rgba(255,255,255,0.4);
    --menu-divider: rgba(255,255,255,0.08);
    --menu-row-hover: rgba(255,255,255,0.06);
    --stepper-bg: rgba(255,255,255,0.06);
    --stepper-bg-hover: rgba(255,255,255,0.14);
    --toggle-track: rgba(255,255,255,0.1);
    --toggle-thumb: rgba(255,255,255,0.7);
    --toggle-on-track: rgba(120,200,170,0.4);
    --toggle-on-thumb: rgba(168,216,196,1);
    /* tooltip */
    --tooltip-bg: rgba(20,22,28,0.95);
    --tooltip-text: rgba(255,255,255,0.85);
    /* hint */
    --hint: rgba(255,255,255,0.25);
  }

  body[data-theme="light"] {
    /* page stays dark — light objects on a dark space */
    --page-bg-1: #161a24;
    --page-bg-2: #08090f;
    /* slab — pale gray with subtle depth */
    --slab-fill-1: #f0f0f2;
    --slab-fill-2: #e6e6e9;
    --slab-fill-3: #dcdce0;
    --slab-edge-top: rgba(255,255,255,0.4);
    --slab-edge-bottom: rgba(0,0,0,0.1);
    --slab-brand: rgba(0,0,0,0.78);
    --slab-divider: rgba(0,0,0,0.18);
    --slab-affordance: rgba(0,0,0,0.7);
    /* key bed — deeper gray tray that the keys sit in */
    --keybed-fill: #b8b8be;
    /* white keys — crisp white that lifts out of the gray slab.
       Bottom of the gradient pulled cooler so the curve of the key
       reads as a real bend, not a flat panel. */
    --whitekey-fill-1: #ffffff;
    --whitekey-fill-2: #f5f5f8;
    --whitekey-fill-3: #e6e6eb;
    /* Cast shadow under each key — pushes it forward off the bed */
    --whitekey-shadow: rgba(0,0,0,0.18);
    /* The well/pocket each key sits in — main depth cue */
    --whitekey-pocket: rgba(0,0,0,0.32);
    /* Embossed dimple at the base of each key */
    --whitekey-dimple: rgba(0,0,0,0.32);
    /* black keys — matte charcoal, not pure black */
    --blackkey-fill-1: #2e3138;
    --blackkey-fill-2: #22252b;
    --blackkey-fill-3: #16181c;
    --blackkey-top: rgba(255,255,255,0.18);
    --blackkey-pocket: rgba(0,0,0,0.4);
    /* held key brightness — whites less darkened than dark mode (wash carries the cue) */
    --whitekey-held-brightness: 0.96;
    --blackkey-held-brightness: 0.7;
    /* green wash opacity when held — subtle in light mode */
    --whitekey-wash-opacity: 0.28;
    /* knob — darker face so it reads as a control, not slab continuation */
    --knob-ring: #2a2c30;
    --knob-face: #3a3d44;
    --knob-ring-stroke: rgba(0,0,0,0.35);
    --knob-indicator: rgba(255,255,255,0.85);
    /* labels */
    --patch-active: rgba(0,0,0,0.92);
    --patch-inactive: rgba(0,0,0,0.62);
    /* dock — light frosted to match keyboard */
    --dock-bg: rgba(240, 240, 242, 0.7);
    --dock-border: rgba(0,0,0,0.12);
    --dock-shadow: rgba(0,0,0,0.16);
    --dock-edge-top: rgba(255,255,255,0.7);
    --dock-btn-bg: rgba(255,255,255,0.4);
    --dock-btn-hover: rgba(255,255,255,0.85);
    --dock-btn-active-bg: rgba(0,0,0,0.1);
    --dock-btn-active-border: rgba(0,0,0,0.45);
    --dock-divider: rgba(0,0,0,0.18);
    --dock-icon: rgba(0,0,0,0.78);
    --dock-icon-active: #1a1c20;
    --active-dot: rgba(0,0,0,0.7);
    /* menu */
    --menu-bg: rgba(240, 240, 242, 0.95);
    --menu-border: rgba(0,0,0,0.12);
    --menu-label: rgba(0,0,0,0.88);
    --menu-status: rgba(0,0,0,0.55);
    --menu-divider: rgba(0,0,0,0.12);
    --menu-row-hover: rgba(0,0,0,0.06);
    --stepper-bg: rgba(0,0,0,0.08);
    --stepper-bg-hover: rgba(0,0,0,0.16);
    --toggle-track: rgba(0,0,0,0.22);
    --toggle-thumb: #ffffff;
    --toggle-on-track: rgba(80, 180, 145, 0.7);
    --toggle-on-thumb: #2c8a6a;
    /* tooltip */
    --tooltip-bg: rgba(40,42,48,0.95);
    --tooltip-text: rgba(255,255,255,0.92);
    /* hint */
    --hint: rgba(255,255,255,0.25);
  }

  * { margin: 0; padding: 0; box-sizing: border-box; }

  body.theme-transitioning,
  body.theme-transitioning .emerald,
  body.theme-transitioning .dock,
  body.theme-transitioning .more-menu,
  body.theme-transitioning .vox-panel,
  body.theme-transitioning .sampler-panel,
  body.theme-transitioning .scope {
    transition:
      background-color 420ms ease,
      border-color 420ms ease,
      box-shadow 420ms ease,
      color 420ms ease,
      opacity 420ms ease;
  }

  body.theme-transitioning .emerald rect,
  body.theme-transitioning .emerald path,
  body.theme-transitioning .emerald line,
  body.theme-transitioning .emerald circle,
  body.theme-transitioning .emerald text,
  body.theme-transitioning .emerald stop,
  body.theme-transitioning .scope rect,
  body.theme-transitioning .scope path,
  body.theme-transitioning .scope line,
  body.theme-transitioning .scope circle,
  body.theme-transitioning .scope text,
  body.theme-transitioning .scope stop {
    transition:
      fill 420ms ease,
      stroke 420ms ease,
      stop-color 420ms ease,
      color 420ms ease,
      opacity 420ms ease,
      filter 420ms ease;
  }

  body.theme-transitioning .dock,
  body.theme-transitioning .emerald,
  body.theme-transitioning .scope,
  body.theme-transitioning .vox-panel,
  body.theme-transitioning .sampler-panel,
  body.theme-transitioning .more-menu {
    will-change: opacity, background-color, border-color, box-shadow, color;
  }
  .stage-label {
    position: fixed;
    top: 1.25rem;
    left: 1.5rem;
    font-size: 9px;
    font-weight: 400;
    letter-spacing: 0.06em;
    color: var(--hint);
    text-transform: lowercase;
  }

  /* ─── EMERALD slab ─────────────────────────────────────── */
  .emerald {
    width: 100%;
    /* Keyboard sits ~25% wider than the dock's intrinsic width.
       The dock is content-sized (~626px with 11 buttons + 2 dividers).
       Keyboard at 780px keeps white keys at ~47px CSS — still comfortable
       for click-and-drag — while leaving viewport room for sampler
       records, scope, and other panels that share the screen. */
    max-width: 780px;
    will-change: transform, opacity;
  }
  .emerald.is-hidden {
    transform: translateY(120%);
    opacity: 0;
    pointer-events: none;
  }
  /* Descent: linear fall back into the bay */
  .emerald.is-descending {
    animation: keyboard-descent 333ms linear forwards;
    pointer-events: none;
  }
  @keyframes keyboard-descent {
    0%   { transform: translateY(0);     opacity: 1; }
    100% { transform: translateY(120%);  opacity: 0; }
  }
  /* Liftoff: linear rise — constant velocity, controlled */
  .emerald.is-ascending {
    animation: keyboard-liftoff 333ms linear forwards;
  }
  @keyframes keyboard-liftoff {
    0%   { transform: translateY(120%);  opacity: 0; }
    100% { transform: translateY(0);     opacity: 1; }
  }
  .emerald svg {
    display: block;
    width: 100%;
    border-radius: 22px;
    overflow: hidden;
    filter:
      drop-shadow(0 18px 42px var(--dock-shadow))
      drop-shadow(0 6px 16px rgba(0, 0, 0, 0.12));
  }

  /* ============================================================
     VOX panel — floating, draggable voice processor
     All theme variables scoped to .vox-panel so they don't pollute
     the host page. Theme follows body[data-theme].
     ============================================================ */
  .vox-panel {
    /* dark theme defaults */
    --vp-fill-1: #1c1e22;
    --vp-fill-2: #15171b;
    --vp-fill-3: #0f1115;
    --vp-edge-top: rgba(255,255,255,0.10);
    --vp-edge-bottom: rgba(0,0,0,0.5);
    --vp-shadow: rgba(0,0,0,0.34);

    --vp-hairline: rgba(255,255,255,0.07);
    --vp-hairline-strong: rgba(255,255,255,0.16);
    --vp-divider: rgba(255,255,255,0.05);

    --vp-text: rgba(255,255,255,0.95);
    --vp-text-dim: rgba(255,255,255,0.55);
    --vp-text-faint: rgba(255,255,255,0.30);
    --vp-text-hair: rgba(255,255,255,0.10);

    /* fx category accent — pink */
    --vp-accent: #ff7cae;
    --vp-accent-bright: #ff95bf;
    --vp-accent-deep: #d6629a;
    --vp-accent-soft: rgba(255, 124, 174, 0.18);
    --vp-accent-line: rgba(255, 124, 174, 0.55);

    --vp-tab-bg: rgba(255,255,255,0.03);
    --vp-tab-bg-hover: rgba(255,255,255,0.09);
    --vp-tab-bg-active: rgba(255,255,255,0.16);

    --vp-btn-bg: rgba(255,255,255,0.05);
    --vp-btn-bg-hover: rgba(255,255,255,0.12);
    --vp-btn-bg-active: rgba(255,255,255,0.20);
    --vp-btn-border: rgba(255,255,255,0.10);
    --vp-btn-border-hover: rgba(255,255,255,0.25);

    --vp-waveform-bg: rgba(0,0,0,0.40);
    --vp-cell-hover-bg: rgba(255,255,255,0.05);
    --vp-cell-active-bg: rgba(255,255,255,0.10);

    --vp-slider-track: rgba(255,255,255,0.10);
    --vp-slider-fill: rgba(255,255,255,0.85);

    /* layout */
    position: fixed;
    width: 1120px;
    height: 680px;
    /* A1: keep the 1120x680 design box at ALL viewport sizes — do NOT clamp
       it with max-width/max-height. Clamping shrank the box below 1120 on
       narrower windows (e.g. a Retina-halved ~1000px CSS viewport), which
       reflowed and smushed the param grid. Instead the panel is shrunk to fit
       by a uniform transform whose factor (--vox-scale, capped at 0.88 = ~12%
       smaller) is computed in JS from the viewport, so it never reflows,
       always fits the width + the area above the dock, and stays centered.
       transform-origin 0 0 keeps the scaled visual's top-left == the box's
       left/top, so the JS positioning math is exact. */
    transform: scale(var(--vox-scale, 0.88));
    transform-origin: 0 0;
    background:
      linear-gradient(180deg, var(--vp-fill-1) 0%, var(--vp-fill-2) 50%, var(--vp-fill-3) 100%);
    border: 1px solid var(--vp-hairline);
    border-radius: 16px;
    box-shadow:
      0 10px 24px var(--vp-shadow),
      inset 0 1px 0 var(--vp-edge-top),
      inset 0 -1px 0 var(--vp-edge-bottom);
    overflow: hidden;
    flex-direction: column;
    color: var(--vp-text);
    font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
    z-index: 50;
    will-change: transform, opacity;
    display: none;
  }
  .vox-panel.is-visible { display: flex; }
  .vox-panel.is-ascending {
    animation: vox-ascend 333ms linear forwards;
  }
  .vox-panel.is-descending {
    animation: vox-descend 333ms linear forwards;
    pointer-events: none;
  }
  /* Keyframes use the JS-computed --vox-scale so the panel animates to its
     fitted resting size instead of snapping back to full size on end. */
  @keyframes vox-ascend {
    0%   { transform: translateY(20px) scale(var(--vox-scale, 0.88)); opacity: 0; }
    100% { transform: translateY(0) scale(var(--vox-scale, 0.88));     opacity: 1; }
  }
  @keyframes vox-descend {
    0%   { transform: translateY(0) scale(var(--vox-scale, 0.88));     opacity: 1; }
    100% { transform: translateY(20px) scale(var(--vox-scale, 0.88)); opacity: 0; }
  }
  .vox-panel.is-dragging .vox-drag-handle,
  .vox-panel.is-dragging .vox-footer-drag-handle { cursor: grabbing; }

  /* Tier 3a-C: shared dissolve for panels that previously vanished instantly
     on close — AURA, NOVA, WORMHOLE, Oscilloscope, and the NEXUS/EMERALD/
     QUARTZ/OPAL billboards. Opacity-only so it never fights the per-frame JS
     transform/position these panels use (a translate/scale dissolve would
     "snap" against the tracking). 333ms matches the existing VOX/keyboard/
     scope dissolve duration. The element is removed on animationend. */
  @keyframes panel-dissolve { from { opacity: 1; } to { opacity: 0; } }
  .panel-dissolving { animation: panel-dissolve 180ms ease-in forwards; pointer-events: none; }

  /* light theme overrides — driven by host body[data-theme] */
  body[data-theme="light"] .vox-panel {
    --vp-fill-1: #faf9f8;
    --vp-fill-2: #f2f0ef;
    --vp-fill-3: #ece9e6;
    --vp-edge-top: rgba(255,255,255,0.9);
    --vp-edge-bottom: rgba(0,0,0,0.06);
    --vp-shadow: rgba(0,0,0,0.10);

    --vp-hairline: rgba(0,0,0,0.08);
    --vp-hairline-strong: rgba(0,0,0,0.18);
    --vp-divider: rgba(0,0,0,0.06);

    --vp-text: #1a1a1a;
    --vp-text-dim: rgba(0,0,0,0.65);
    --vp-text-faint: rgba(0,0,0,0.40);
    --vp-text-hair: rgba(0,0,0,0.15);

    --vp-accent: #ff7cae;
    --vp-accent-bright: #e85a92;
    --vp-accent-deep: #c84376;
    --vp-accent-soft: rgba(255, 124, 174, 0.15);
    --vp-accent-line: rgba(255, 124, 174, 0.5);

    --vp-tab-bg: rgba(0,0,0,0.02);
    --vp-tab-bg-hover: rgba(0,0,0,0.05);
    --vp-tab-bg-active: rgba(0,0,0,0.10);

    --vp-btn-bg: rgba(0,0,0,0.03);
    --vp-btn-bg-hover: rgba(0,0,0,0.07);
    --vp-btn-bg-active: rgba(0,0,0,0.12);
    --vp-btn-border: rgba(0,0,0,0.10);
    --vp-btn-border-hover: rgba(0,0,0,0.22);

    --vp-waveform-bg: rgba(255,255,255,0.55);
    --vp-cell-hover-bg: rgba(0,0,0,0.03);
    --vp-cell-active-bg: rgba(0,0,0,0.06);

    --vp-slider-track: rgba(0,0,0,0.10);
    --vp-slider-fill: rgba(0,0,0,0.75);
  }

  /* ── Header ── */
  .vox-header {
    padding: 14px 22px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    position: relative;
    min-height: 50px;
  }
  /* Drag handle: sits behind controls. pointer-events on text/dividers below
     are 'none' so the handle catches mousedown but interactive elements work. */
  .vox-drag-handle {
    position: absolute;
    inset: 0;
    cursor: grab;
    z-index: 0;
  }
  .vox-header > *:not(.vox-drag-handle) { position: relative; z-index: 1; }

  .vox-header-right { display: flex; align-items: center; gap: 10px; pointer-events: none; }
  .vox-header-right > * { pointer-events: auto; }

  /* Wordmark: SCOPE-style flush left brand text */
  .vox-wordmark {
    display: flex;
    align-items: center;
    gap: 12px;
    pointer-events: none;
  }
  .vox-brand {
    font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
    /* A2: match NEXUS's header wordmark family (.tb-title in nexus-v7.html:
       28px / weight 200 / letter-spacing -0.04em / uppercase). VOX's was a
       small 11px/300/0.3em label that read much lighter than NEXUS's; bring
       it up to NEXUS's thin, large, tight-tracked wordmark scale. Slightly
       under NEXUS's 28px since VOX's header also carries the "voice
       processor" sub-label + divider in the same lockup. */
    font-size: 24px;
    font-weight: 200;
    letter-spacing: -0.02em;
    color: var(--vp-text);
    line-height: 1;
    text-transform: uppercase;
  }
  .vox-brand-divider {
    width: 1px;
    height: 12px;
    background: var(--vp-hairline-strong);
  }
  .vox-brand-sub {
    font-size: 9px;
    font-weight: 400;
    letter-spacing: 0.06em;
    color: var(--vp-text-dim);
    text-transform: lowercase;
    line-height: 1;
  }
  .vox-label-tiny {
    font-size: 9px;
    font-weight: 400;
    letter-spacing: 0.06em;
    color: var(--vp-text-dim);
    text-transform: lowercase;
  }
  .vox-source-info {
    font-size: 11px;
    font-weight: 300;
    letter-spacing: 0.04em;
    line-height: 1.2;
    color: var(--vp-text-dim);
    text-align: right;
    text-transform: lowercase;
    pointer-events: none;
  }
  .vox-source-info .source-emphasis {
    color: var(--vp-text);
    font-weight: 400;
  }

  .vox-mode-switch {
    display: flex;
    background: var(--vp-btn-bg);
    border: 1px solid var(--vp-btn-border);
    border-radius: 6px;
    height: 24px;
    overflow: hidden;
  }
  .vox-mode-btn {
    padding: 0 12px;
    font-size: 9px;
    font-weight: 400;
    letter-spacing: 0.06em;
    text-transform: lowercase;
    background: transparent;
    border: none;
    color: var(--vp-text-dim);
    cursor: pointer;
    font-family: inherit;
    transition: color 0.15s, background 0.15s;
  }
  .vox-mode-btn:hover { color: var(--vp-text); }
  .vox-mode-btn.active {
    background: var(--vp-accent);
    color: #fff;
  }

  .vox-close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    background: var(--vp-btn-bg);
    border: 1px solid var(--vp-btn-border);
    border-radius: 50%;
    color: var(--vp-text-dim);
    cursor: pointer;
    font-family: inherit;
    font-size: 14px;
    line-height: 1;
    padding: 0 0 1px 0;
    transition: color 0.15s, background 0.15s;
  }
  .vox-close:hover { color: var(--vp-text); background: var(--vp-btn-bg-hover); }

  /* ── Source tabs ── */
  .vox-source-tabs {
    display: flex;
    gap: 4px;
    padding: 0 22px 12px 22px;
    flex-shrink: 0;
  }
  .vox-source-tab {
    flex: 1;
    padding: 9px 8px;
    font-size: 9px;
    font-weight: 400;
    letter-spacing: 0.06em;
    background: var(--vp-tab-bg);
    border: 1px solid var(--vp-hairline);
    border-radius: 6px;
    color: var(--vp-text-dim);
    cursor: pointer;
    font-family: inherit;
    text-transform: lowercase;
    transition: color 0.12s, background 0.12s, border-color 0.12s;
  }
  .vox-source-tab:hover {
    color: var(--vp-text);
    background: var(--vp-tab-bg-hover);
    border-color: var(--vp-hairline-strong);
  }
  .vox-source-tab:active { background: var(--vp-tab-bg-active); }
  .vox-source-tab.active {
    color: #fff;
    background: var(--vp-accent);
    border-color: var(--vp-accent);
  }
  .vox-source-tab.recording {
    color: #fff;
    background: var(--vp-accent);
    border-color: var(--vp-accent);
  }
  .vox-source-tab.recording::before {
    content: '●';
    margin-right: 4px;
    color: #fff;
    animation: vox-pulse 1.2s infinite;
  }
  @keyframes vox-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.3; } }

  /* ── Waveform display ── */
  .vox-waveform {
    height: 130px;
    margin: 0 22px;
    border: 1px solid var(--vp-hairline);
    border-radius: 10px;
    position: relative;
    background: var(--vp-waveform-bg);
    flex-shrink: 0;
    overflow: hidden;
    box-shadow: inset 0 1px 0 var(--vp-edge-top);
  }
  .vox-wave-canvas { width: 100%; height: 100%; display: block; }
  .vox-wave-overlay {
    position: absolute;
    inset: 0;
    display: flex;
    justify-content: space-between;
    pointer-events: none;
    padding: 0 14px;
  }
  .vox-grid-v { width: 1px; height: 100%; background: var(--vp-text-hair); opacity: 0.5; }

  .vox-pitch-readout {
    position: absolute;
    top: 10px;
    right: 14px;
    font-size: 10px;
    font-weight: 400;
    letter-spacing: 0.04em;
    color: var(--vp-text-dim);
    text-transform: lowercase;
    pointer-events: none;
  }
  .vox-pitch-readout .note { color: var(--vp-text); font-weight: 500; }
  .vox-pitch-readout .arrow { color: var(--vp-accent); margin: 0 6px; }

  .vox-status-line {
    position: absolute;
    top: 10px;
    left: 14px;
    font-size: 9px;
    font-weight: 400;
    letter-spacing: 0.06em;
    color: var(--vp-text-dim);
    text-transform: lowercase;
    display: flex;
    align-items: center;
    gap: 8px;
    pointer-events: none;
  }
  .vox-status-dot {
    width: 6px;
    height: 6px;
    background: var(--vp-accent);
    border-radius: 50%;
    animation: vox-pulse 2s infinite;
  }
  .vox-status-dot.idle {
    background: var(--vp-text-faint);
    animation: none;
  }

  /* ── Param grid ── */
  .vox-param-grid {
    display: grid;
    grid-template-columns: repeat(6, 1fr);
    grid-template-rows: repeat(2, 1fr);
    flex: 1;
    margin: 14px 22px 0 22px;
    border: 1px solid var(--vp-hairline);
    border-radius: 10px;
    overflow: hidden;
    min-height: 0;
    background: rgba(0,0,0,0.05);
  }
  body[data-theme="light"] .vox-panel .vox-param-grid { background: rgba(0,0,0,0.015); }
  .vox-cell {
    background: transparent;
    border-right: 1px solid var(--vp-divider);
    border-bottom: 1px solid var(--vp-divider);
    display: flex;
    flex-direction: column;
    padding: 11px 13px;
    position: relative;
    cursor: ns-resize;
    transition: background 0.15s;
    touch-action: none;
    min-height: 0;
    user-select: none;
  }
  .vox-cell:nth-child(6n) { border-right: none; }
  .vox-cell:nth-child(n+7) { border-bottom: none; }
  .vox-cell.discrete { cursor: pointer; }
  .vox-cell:hover { background: var(--vp-cell-hover-bg); }
  .vox-cell.dragging {
    background: var(--vp-cell-active-bg);
    box-shadow: inset 0 0 0 1px var(--vp-accent-line);
  }
  .vox-cell-label {
    font-size: 9px;
    font-weight: 400;
    letter-spacing: 0.06em;
    color: var(--vp-text-dim);
    text-transform: lowercase;
  }
  .vox-cell-vis {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 0;
  }
  .vox-cell-vis canvas {
    width: min(100%, 150px);
    aspect-ratio: 1 / 1;
    height: auto;
    max-width: 150px;
    max-height: 150px;
    margin: auto;
    display: block;
    background: transparent;
  }
  .vox-cell-value {
    font-size: 10px;
    font-weight: 400;
    letter-spacing: 0.04em;
    line-height: 1.1;
    min-height: 1.2em;
    color: var(--vp-text);
    text-align: left;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    text-transform: lowercase;
  }

  /* ── Footer ── */
  .vox-footer {
    height: 76px;
    box-sizing: border-box;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 12px 22px;
    flex-shrink: 0;
    position: relative;
  }
  .vox-footer-drag-handle {
    position: absolute;
    inset: 0;
    cursor: grab;
    z-index: 0;
  }
  .vox-footer > *:not(.vox-footer-drag-handle) { position: relative; z-index: 1; }

  .vox-output-section {
    display: flex;
    flex-direction: column;
    gap: 6px;
    width: 220px;
    pointer-events: none;
  }
  .vox-meter-bg {
    height: 3px;
    background: var(--vp-slider-track);
    width: 100%;
    position: relative;
    border-radius: 2px;
    overflow: hidden;
  }
  .vox-meter-fill {
    position: absolute;
    left: 0;
    top: 0;
    height: 100%;
    background: var(--vp-accent);
    width: 0%;
    transition: width 0.05s linear;
  }
  .vox-meter-readout {
    font-family: inherit;
    font-size: 9px;
    font-weight: 400;
    letter-spacing: 0.04em;
    color: var(--vp-text-dim);
    text-transform: lowercase;
  }

  /* A3: preset name homed in the footer, just right of the master-output
     (master volume) section. Absolutely positioned (out of flow) so it does
     NOT push the flex layout — the dry/wet + volume mix section stays centered
     on the panel. Static — never floats or follows the cursor; fades in on the
     first preset cycle and stays put. The compound selector overrides the
     `.vox-footer > *` position:relative rule above. left:256 sits just past
     the 220px master-output section (22px footer pad + 220 + ~14 gap). */
  .vox-footer .vox-preset-home {
    position: absolute;
    left: 256px;
    top: 50%;
    transform: translateY(-50%);
    z-index: 1;
    font-family: inherit;
    font-size: 10px;
    font-weight: 400;
    letter-spacing: 0.04em;
    color: var(--vp-text-dim);
    text-transform: lowercase;
    white-space: nowrap;
    pointer-events: none;
    opacity: 0;
    transition: opacity 0.25s;
  }
  .vox-footer .vox-preset-home.show { opacity: 1; }

  .vox-mix-section {
    display: flex;
    align-items: center;
    gap: 18px;
    flex: 1;
    justify-content: center;
    pointer-events: none;
  }
  /* Center the dry/wet + volume pair on the PANEL center (which lines up with
     the param-grid's drive|color column divider), not on the footer's flex
     space — the 220px master-output section vs the wider action group skewed
     the flex centre ~8px left. Absolutely centering it (out of flow) pins it
     to the true centre. Compound selector overrides the footer's `> *`
     position:relative rule. */
  .vox-footer .vox-mix-section {
    position: absolute;
    left: 50%;
    transform: translateX(-50%);
    flex: 0 0 auto;
    z-index: 1;
  }
  .vox-mix-control {
    display: flex;
    flex-direction: column;
    gap: 5px;
    width: 110px;
    cursor: ew-resize;
    user-select: none;
    touch-action: none;
    pointer-events: auto;
  }
  .vox-mix-label-row {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
  }
  .vox-mix-value {
    font-size: 10px;
    font-weight: 400;
    letter-spacing: 0.04em;
    color: var(--vp-text);
    text-transform: lowercase;
  }
  .vox-mix-bar {
    height: 4px;
    background: var(--vp-slider-track);
    width: 100%;
    position: relative;
    border-radius: 2px;
    overflow: hidden;
  }
  .vox-mix-fill {
    position: absolute;
    left: 0;
    top: 0;
    height: 100%;
    background: var(--vp-slider-fill);
    width: 0%;
  }

  .vox-action-group { display: flex; gap: 8px; pointer-events: none; }
  .vox-action-group > * { pointer-events: auto; }

  /* Vocal-filter on/off popup — anchored directly above the vocal filter
     button (the wrapper is position:relative), appears + vanishes there
     instead of the floating bottom-centre toast. */
  .vox-vf-wrap { position: relative; display: inline-flex; }
  .vox-vf-popup {
    position: absolute;
    bottom: calc(100% + 8px);
    left: 50%;
    transform: translateX(-50%) translateY(4px);
    white-space: nowrap;
    font-family: inherit;
    font-size: 10px;
    font-weight: 400;
    letter-spacing: 0.04em;
    text-transform: lowercase;
    color: var(--vp-text);
    background: var(--vp-fill-2, #15171b);
    border: 1px solid var(--vp-hairline);
    border-radius: 6px;
    padding: 5px 10px;
    box-shadow: 0 8px 22px var(--vp-shadow, rgba(0,0,0,0.34));
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.16s ease, transform 0.16s ease;
    z-index: 6;
  }
  .vox-vf-popup.show { opacity: 0.98; transform: translateX(-50%) translateY(0); }
  .vox-btn-swiss {
    padding: 10px 18px;
    font-size: 10px;
    font-weight: 400;
    letter-spacing: 0.06em;
    text-transform: lowercase;
    border: 1px solid var(--vp-btn-border);
    border-radius: 6px;
    background: var(--vp-btn-bg);
    color: var(--vp-text);
    cursor: pointer;
    transition: background 0.12s, border-color 0.12s, transform 0.08s;
    font-family: inherit;
  }
  .vox-btn-swiss:hover {
    background: var(--vp-btn-bg-hover);
    border-color: var(--vp-btn-border-hover);
  }
  .vox-btn-swiss:active {
    background: var(--vp-btn-bg-active);
    transform: translateY(1px);
  }
  .vox-btn-swiss.primary {
    background: var(--vp-accent);
    border-color: var(--vp-accent);
    color: #fff;
  }
  .vox-btn-swiss.primary:hover {
    background: var(--vp-accent-bright);
    border-color: var(--vp-accent-bright);
  }
  .vox-btn-swiss.primary:active {
    background: var(--vp-accent-deep);
    border-color: var(--vp-accent-deep);
    transform: translateY(1px);
  }
  .vox-btn-swiss.engaged {
    background: var(--vp-text);
    border-color: var(--vp-text);
    color: var(--vp-fill-2);
  }
  .vox-btn-swiss.engaged:hover {
    background: var(--vp-text);
    opacity: 0.85;
  }
  .vox-btn-swiss.vox-btn-small {
    padding: 9px 12px;
    font-size: 9px;
  }
  .vox-btn-swiss.vox-btn-small.engaged {
    background: var(--vp-accent);
    border-color: var(--vp-accent);
    color: #fff;
  }

  /* ── Toast ── */
  .vox-toast {
    position: fixed;
    bottom: 24px;
    left: 50%;
    transform: translateX(-50%);
    background: var(--vp-fill-2, #15171b);
    color: var(--vp-text, rgba(255,255,255,0.95));
    padding: 10px 18px;
    border-radius: 8px;
    font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
    font-size: 10px;
    font-weight: 400;
    letter-spacing: 0.04em;
    text-transform: lowercase;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.2s;
    z-index: 200;
    border: 1px solid rgba(255,255,255,0.07);
    backdrop-filter: blur(20px) saturate(1.4);
    -webkit-backdrop-filter: blur(20px) saturate(1.4);
    box-shadow: 0 8px 24px rgba(0,0,0,0.4);
  }
  .vox-toast.show { opacity: 0.98; }

  /* ── Mic error overlay ── */
  .vox-mic-error-overlay {
    position: absolute;
    inset: 0;
    background:
      linear-gradient(180deg, var(--vp-fill-1) 0%, var(--vp-fill-2) 100%);
    display: none;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 12px;
    padding: 24px;
    z-index: 5;
    border-radius: inherit;
  }
  .vox-mic-error-overlay.show { display: flex; }
  .vox-mic-error-title {
    font-size: 13px;
    font-weight: 400;
    letter-spacing: 0.06em;
    text-transform: lowercase;
    color: var(--vp-accent);
  }
  .vox-mic-error-text {
    font-size: 11px;
    font-weight: 300;
    color: var(--vp-text-dim);
    max-width: 480px;
    text-align: center;
    line-height: 1.5;
    text-transform: lowercase;
  }
  .vox-mic-error-actions {
    display: flex;
    gap: 8px;
    margin-top: 4px;
  }

  /* ============================================================
     SAMPLER panel — floating, draggable vinyl-disc sample player
     Dark default to match the rest of the dock (VOX, EMERALD, SCOPE).
     Light variant via body[data-theme="light"] preserves the v6 cream
     identity. All vars scoped to .sampler-panel so they don't pollute
     the host page.

     Two color systems live here on purpose:
       --sp-src      — fixed chrome accent (load btn, input focus, scrub).
                       Stays put across loaded samples.
       --sp-pressing — per-record identity color (marble shader, waveform,
                       sticker). Re-rolls per loaded sample so each
                       Sampler instance in the sequencer is visually
                       differentiated, like color-coded folders.
     ============================================================ */
  .sampler-panel {
    /* --- dark theme defaults --- */
    --sp-panel-bg:     #15171b;
    --sp-panel-fill-1: #1c1e22;
    --sp-panel-fill-2: #15171b;
    --sp-panel-fill-3: #0f1115;
    --sp-panel-edge-top: rgba(255,255,255,0.10);
    --sp-panel-edge-bottom: rgba(0,0,0,0.5);
    --sp-panel-hair:   rgba(255,255,255,0.07);

    --sp-ink:          rgba(255,255,255,0.95);
    --sp-ink-dim:      rgba(255,255,255,0.65);
    --sp-ink-faint:    rgba(255,255,255,0.4);
    --sp-ink-hair:     rgba(255,255,255,0.08);

    --sp-header-bg:    rgba(255,255,255,0.04);
    --sp-input-bg:     rgba(255,255,255,0.05);
    --sp-input-border: rgba(255,255,255,0.10);
    --sp-drop-bg:      rgba(255,255,255,0.03);
    --sp-drop-border:  rgba(255,255,255,0.18);
    --sp-stage-bg:     #0a0a0d;
    --sp-stage-border: rgba(255,255,255,0.06);
    --sp-transport-bg: rgba(255,255,255,0.03);
    --sp-transport-border: rgba(255,255,255,0.06);

    --sp-btn-ghost-bg:     transparent;
    --sp-btn-ghost-border: rgba(255,255,255,0.18);
    --sp-btn-ghost-hover-bg: rgba(255,255,255,0.06);
    --sp-btn-ghost-hover-border: rgba(255,255,255,0.32);
    --sp-btn-dark-bg:    rgba(255,255,255,0.92);
    --sp-btn-dark-fg:    #1a1a1a;
    --sp-btn-dark-hover: #fff;

    --sp-close-bg:     rgba(255,255,255,0.06);
    --sp-close-hover:  rgba(255,255,255,0.14);
    --sp-arrow-hover:  rgba(255,255,255,0.10);
    --sp-track-bg:     rgba(255,255,255,0.10);

    /* fixed chrome accent — the dock's source-green */
    --sp-src:          #03ff74;
    --sp-src-ink:      #0a0a0a;
    --sp-src-deep:     #02b454;
    --sp-src-rgb:      3, 255, 116;

    /* per-pressing identity — JS rewrites these on each new pressing */
    --sp-pressing:     #03ff74;
    --sp-pressing-rgb: 3, 255, 116;
    --sp-pressing-deep: rgb(2, 178, 81);

    position: fixed;
    left: auto;
    top: auto;
    width: 640px;
    max-width: calc(100vw - 24px);
    max-height: calc(100vh - 24px);
    padding: 0;
    gap: 0;
    border: 1px solid var(--sp-panel-hair);
    border-radius: 10px;
    overflow: hidden;
    background:
      linear-gradient(180deg, var(--sp-panel-fill-1) 0%, var(--sp-panel-fill-2) 50%, var(--sp-panel-fill-3) 100%);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    box-shadow:
      0 6px 14px rgba(0,0,0,0.16),
      inset 0 1px 0 var(--sp-panel-edge-top),
      inset 0 -1px 0 var(--sp-panel-edge-bottom);
    color: var(--sp-ink);
    font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
    /* Z-index assigned by panelStack.register() (see panel-physics.js).
       Fallback to the above-keyboard tier if registration hasn't happened. */
    z-index: var(--tier-above-keyboard);
    display: none;
    opacity: 0;
    flex-direction: initial;
    transform: translate3d(0, 0, 0);
    transform-origin: top left;
    scale: 1;
    animation: none;
    will-change: transform, opacity;
    backface-visibility: hidden;
    contain: layout paint style;
  }

  /* --- light theme overrides — follows VOX light chrome. --- */
  body[data-theme="light"] .sampler-panel {
    --sp-panel-bg:     #f2f0ef;
    --sp-panel-fill-1: #faf9f8;
    --sp-panel-fill-2: #f2f0ef;
    --sp-panel-fill-3: #ece9e6;
    --sp-panel-edge-top: rgba(255,255,255,0.9);
    --sp-panel-edge-bottom: rgba(0,0,0,0.06);
    --sp-panel-hair:   rgba(0,0,0,0.08);

    --sp-ink:          #1a1a1a;
    --sp-ink-dim:      rgba(0,0,0,0.7);
    --sp-ink-faint:    rgba(0,0,0,0.5);
    --sp-ink-hair:     rgba(0,0,0,0.08);

    --sp-header-bg:    rgba(255,255,255,0.5);
    --sp-input-bg:     rgba(255,255,255,0.92);
    --sp-input-border: rgba(0,0,0,0.1);
    --sp-drop-bg:      rgba(255,255,255,0.45);
    --sp-drop-border:  rgba(0,0,0,0.3);
    --sp-stage-bg:     #1a1a1a;
    --sp-stage-border: rgba(0,0,0,0.0);
    --sp-transport-bg: rgba(0,0,0,0.04);
    --sp-transport-border: rgba(0,0,0,0.06);

    --sp-btn-ghost-bg:     transparent;
    --sp-btn-ghost-border: rgba(0,0,0,0.15);
    --sp-btn-ghost-hover-bg: rgba(0,0,0,0.03);
    --sp-btn-ghost-hover-border: rgba(0,0,0,0.3);
    --sp-btn-dark-bg:    var(--sp-ink);
    --sp-btn-dark-fg:    #fff;
    --sp-btn-dark-hover: #333;

    --sp-close-bg:     rgba(0,0,0,0.05);
    --sp-close-hover:  rgba(0,0,0,0.12);
    --sp-arrow-hover:  rgba(0,0,0,0.08);
    --sp-track-bg:     rgba(0,0,0,0.10);

    --sp-src-ink:      #1a1a1a;
  }
  .sampler-panel.is-preparing {
    display: block;
    opacity: 0;
    transform: translate3d(0, 0, 0);
    pointer-events: none;
  }
  .sampler-panel.is-visible {
    display: block;
    opacity: 1;
    transform: translate3d(0, 0, 0);
  }
  .sampler-panel.is-ascending { animation: sampler-ascend 220ms ease-out forwards; }
  .sampler-panel.is-descending {
    animation: sampler-descend 180ms ease-in forwards;
    pointer-events: none;
  }
  @keyframes sampler-ascend {
    0%   { transform: translate3d(0, 0, 0); opacity: 0; }
    100% { transform: translate3d(0, 0, 0); opacity: 1; }
  }
  @keyframes sampler-descend {
    0%   { transform: translate3d(0, 0, 0); opacity: 1; }
    100% { transform: translate3d(0, 0, 0); opacity: 0; }
  }
  .sampler-panel.is-dragging .sp-drag-handle { cursor: grabbing; }
  .sampler-panel * { box-sizing: border-box; }

  /* Header — also doubles as the drag region */
  .sampler-panel .sp-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 14px 20px;
    border-bottom: 1px solid var(--sp-ink-hair);
    background: var(--sp-header-bg);
    position: relative;
  }
  .sampler-panel .sp-drag-handle {
    position: absolute;
    inset: 0;
    cursor: grab;
    z-index: 0;
  }
  .sampler-panel .sp-header > *:not(.sp-drag-handle) { position: relative; z-index: 1; }
  .sampler-panel .sp-name {
    font-size: 28px;
    font-weight: 200;
    letter-spacing: -0.04em;
    color: var(--sp-ink);
    line-height: 1;
    text-transform: uppercase;
    pointer-events: none;
  }
  .sampler-panel .sp-close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    background: var(--sp-close-bg);
    border: 1px solid var(--sp-ink-hair);
    border-radius: 50%;
    color: var(--sp-ink-dim);
    cursor: pointer;
    font-family: inherit;
    font-size: 14px;
    line-height: 1;
    padding: 0 0 1px 0;
    transition: background 0.15s, color 0.15s, border-color 0.15s;
  }
  .sampler-panel .sp-close:hover {
    background: var(--sp-close-hover);
    color: var(--sp-ink);
  }

  /* Load row */
  .sampler-panel .sp-load {
    display: flex;
    gap: 6px;
    padding: 14px 20px 10px;
  }
  .sampler-panel .sp-btn {
    font-family: inherit;
    font-size: 11px;
    font-weight: 400;
    letter-spacing: 0;
    text-transform: lowercase;
    padding: 0 14px;
    height: 32px;
    border-radius: 4px;
    border: 1px solid transparent;
    cursor: pointer;
    transition: all 0.15s;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    white-space: nowrap;
  }
  .sampler-panel .sp-btn-src {
    background: var(--sp-src);
    color: var(--sp-src-ink);
    font-weight: 500;
  }
  .sampler-panel .sp-btn-src:hover {
    filter: brightness(1.05);
    box-shadow: 0 0 16px rgba(var(--sp-src-rgb), 0.4);
  }
  .sampler-panel .sp-btn-ghost {
    background: var(--sp-btn-ghost-bg);
    color: var(--sp-ink-dim);
    border-color: var(--sp-btn-ghost-border);
  }
  .sampler-panel .sp-btn-ghost:hover {
    color: var(--sp-ink);
    border-color: var(--sp-btn-ghost-hover-border);
    background: var(--sp-btn-ghost-hover-bg);
  }
  .sampler-panel .sp-btn-dark {
    background: var(--sp-btn-dark-bg);
    color: var(--sp-btn-dark-fg);
    font-weight: 500;
  }
  .sampler-panel .sp-btn-dark:hover { background: var(--sp-btn-dark-hover); }

  .sampler-panel .sp-input {
    flex: 1;
    height: 32px;
    background: var(--sp-input-bg);
    border: 1px solid var(--sp-input-border);
    border-radius: 4px;
    padding: 0 12px;
    font-family: inherit;
    font-size: 12px;
    font-weight: 400;
    color: var(--sp-ink);
    outline: none;
    transition: border-color 0.15s, box-shadow 0.15s;
  }
  .sampler-panel .sp-input::placeholder {
    color: var(--sp-ink-faint);
    text-transform: lowercase;
  }
  .sampler-panel .sp-input:focus {
    border-color: var(--sp-src);
    box-shadow: 0 0 0 3px rgba(var(--sp-src-rgb), 0.15);
  }

  .sampler-panel .sp-hint {
    padding: 0 20px;
    font-size: 10px;
    font-weight: 400;
    color: var(--sp-ink-faint);
    text-transform: lowercase;
    display: flex;
    align-items: center;
    gap: 8px;
  }
  .sampler-panel .sp-hint .dot {
    width: 3px; height: 3px;
    border-radius: 50%;
    background: var(--sp-ink-faint);
  }

  .sampler-panel .sp-drop {
    margin: 10px 20px 0;
    border: 1px dashed var(--sp-drop-border);
    border-radius: 4px;
    padding: 14px;
    text-align: center;
    background: var(--sp-drop-bg);
    cursor: pointer;
    transition: all 0.2s;
  }
  .sampler-panel .sp-drop:hover {
    border-color: var(--sp-src);
    background: rgba(var(--sp-src-rgb), 0.06);
  }
  .sampler-panel .sp-drop.drag-over {
    border-color: var(--sp-src);
    background: rgba(var(--sp-src-rgb), 0.16);
  }
  .sampler-panel .sp-drop-label {
    font-size: 11px;
    font-weight: 400;
    letter-spacing: 0.04em;
    color: var(--sp-ink-dim);
    text-transform: lowercase;
    display: inline-flex;
    align-items: center;
    gap: 8px;
  }
  .sampler-panel .sp-drop:hover .sp-drop-label { color: var(--sp-ink); }
  .sampler-panel .sp-drop-label svg { opacity: 0.6; }

  /* Stage — black container with record + waveform side by side */
  .sampler-panel .sp-stage {
    margin: 14px 20px 12px;
    background: var(--sp-stage-bg);
    border: 1px solid var(--sp-stage-border);
    border-radius: 6px;
    padding: 14px;
    display: grid;
    grid-template-columns: 220px 1fr;
    gap: 14px;
    align-items: center;
    box-shadow: 0 2px 12px rgba(0,0,0,0.25), inset 0 1px 0 rgba(255,255,255,0.04);
  }

  /* Record disc */
  .sampler-panel .sp-record-wrap {
    position: relative;
    width: 220px;
    height: 220px;
    border-radius: 50%;
    overflow: hidden;
    background: #000;
    box-shadow:
      0 0 0 1px rgba(0,0,0,0.2),
      0 12px 28px rgba(0,0,0,0.35),
      inset 0 0 40px rgba(0,0,0,0.25);
    cursor: grab;
    user-select: none;
    -webkit-user-select: none;
    touch-action: none;
  }
  .sampler-panel .sp-record-wrap.scratching { cursor: grabbing; }
  .sampler-panel .sp-record-spin {
    position: absolute;
    inset: 0;
    border-radius: 50%;
    will-change: transform;
  }
  /* WebGL marble shader canvas — fills the spinning record */
  .sampler-panel .sp-record-canvas {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    border-radius: 50%;
    display: block;
  }
  .sampler-panel .sp-grooves {
    position: absolute;
    inset: 0;
    border-radius: 50%;
    pointer-events: none;
    background:
      repeating-radial-gradient(
        circle at center,
        rgba(0,0,0,0) 0px,
        rgba(0,0,0,0) 2.5px,
        rgba(0,0,0,0.18) 2.5px,
        rgba(0,0,0,0.18) 3px
      );
    mix-blend-mode: multiply;
    opacity: 0.55;
    z-index: 2;
  }
  .sampler-panel .sp-vignette {
    position: absolute;
    inset: 0;
    border-radius: 50%;
    background: radial-gradient(circle at center, transparent 55%, rgba(0,0,0,0.5) 100%);
    pointer-events: none;
    z-index: 5;
  }

  /* Sticker — center label with starburst.
     Sticker bg flips light/dark depending on pressing brightness;
     JS sets --sp-sticker-* vars when the pressing changes. */
  .sampler-panel .sp-sticker {
    --sp-sticker-c1: #faf9f8;
    --sp-sticker-c2: #ece9e6;
    --sp-sticker-c3: #e0ddd9;
    --sp-sticker-edge: rgba(0,0,0,0.08);
    --sp-sticker-shine: rgba(255,255,255,0.8);

    position: absolute;
    top: 50%;
    left: 50%;
    width: 88px;
    height: 88px;
    transform: translate(-50%, -50%);
    border-radius: 50%;
    background: radial-gradient(circle at 35% 30%, var(--sp-sticker-c1) 0%, var(--sp-sticker-c2) 70%, var(--sp-sticker-c3) 100%);
    box-shadow:
      0 0 0 1px var(--sp-sticker-edge),
      0 2px 6px rgba(0,0,0,0.4),
      inset 0 1px 1px var(--sp-sticker-shine);
    display: grid;
    place-items: center;
    z-index: 4;
    color: var(--sp-pressing);
    transition: color 0.4s ease, background 0.4s ease;
  }
  .sampler-panel .sp-sticker-logo {
    width: 87%;
    height: 87%;
    display: block;
    transform: translateY(1px);
  }

  .sampler-panel .sp-record-wrap::after {
    content: "";
    position: absolute;
    inset: 4px;
    border-radius: 50%;
    border: 1px solid rgba(var(--sp-pressing-rgb), 0);
    pointer-events: none;
    transition: border-color 0.2s;
    z-index: 6;
  }
  .sampler-panel .sp-record-wrap.scratching::after {
    border-color: rgba(var(--sp-pressing-rgb), 0.5);
    box-shadow: 0 0 20px rgba(var(--sp-pressing-rgb), 0.3);
  }

  /* Waveform area */
  .sampler-panel .sp-wave-box {
    position: relative;
    background: transparent;
    overflow: hidden;
    height: 220px;
    min-width: 0;
  }
  .sampler-panel .sp-wave-box canvas {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    display: block;
  }
  .sampler-panel .sp-wave-time-l,
  .sampler-panel .sp-wave-time-r {
    position: absolute;
    bottom: 8px;
    font-family: 'Space Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 9px;
    letter-spacing: 0.04em;
    color: rgba(255,255,255,0.55);
    z-index: 3;
    text-shadow: 0 1px 2px rgba(0,0,0,0.6);
  }
  .sampler-panel .sp-wave-time-l { left: 10px; }
  .sampler-panel .sp-wave-time-r { right: 10px; }
  .sampler-panel .sp-wave-trim {
    position: absolute;
    top: 0;
    bottom: 0;
    width: 12px;
    background: linear-gradient(
      90deg,
      transparent 0,
      transparent calc(50% - 1px),
      var(--sp-src) calc(50% - 1px),
      var(--sp-src) calc(50% + 1px),
      transparent calc(50% + 1px),
      transparent 100%
    );
    filter: drop-shadow(0 0 6px var(--sp-src));
    margin-left: -6px;
    cursor: ew-resize;
    z-index: 2;
    touch-action: none;
  }
  .sampler-panel .sp-wave-trim::before,
  .sampler-panel .sp-wave-trim::after {
    content: "";
    position: absolute;
    left: 50%;
    transform: translateX(-50%);
    width: 8px; height: 8px;
    background: var(--sp-src);
    border-radius: 2px;
    box-shadow: 0 0 6px var(--sp-src);
  }
  .sampler-panel .sp-wave-trim::before { top: 0; }
  .sampler-panel .sp-wave-trim::after { bottom: 0; }
  .sampler-panel .sp-wave-sel {
    position: absolute;
    top: 0;
    bottom: 0;
    background: rgba(var(--sp-src-rgb), 0.08);
    pointer-events: none;
    z-index: 1;
  }
  .sampler-panel .sp-wave-head {
    position: absolute;
    top: 0;
    bottom: 0;
    width: 1px;
    background: #fff;
    box-shadow: 0 0 4px rgba(255,255,255,0.7);
    z-index: 3;
    cursor: ew-resize;
    touch-action: none;
  }
  /* Wider invisible hit zone so the 1px line is grabbable */
  .sampler-panel .sp-wave-head::before {
    content: "";
    position: absolute;
    top: 0;
    bottom: 0;
    left: -6px;
    right: -6px;
  }

  /* Transport row */
  .sampler-panel .sp-transport-row {
    margin: 0 20px 14px;
    padding: 8px 12px;
    background: var(--sp-transport-bg);
    border: 1px solid var(--sp-transport-border);
    border-radius: 4px;
    display: flex;
    align-items: center;
    gap: 6px;
  }
  .sampler-panel .sp-mode {
    display: inline-flex;
    background: var(--sp-input-bg);
    border: 1px solid var(--sp-ink-hair);
    border-radius: 4px;
    padding: 2px;
    gap: 2px;
    margin-right: 4px;
    flex-shrink: 0;
  }
  .sampler-panel .sp-mode-btn {
    font-family: inherit;
    font-size: 10px;
    font-weight: 400;
    letter-spacing: 0.04em;
    text-transform: lowercase;
    background: transparent;
    border: none;
    padding: 4px 10px;
    border-radius: 3px;
    color: var(--sp-ink-dim);
    cursor: pointer;
    transition: all 0.15s;
  }
  .sampler-panel .sp-mode-btn:hover { color: var(--sp-ink); }
  .sampler-panel .sp-mode-btn.on {
    background: var(--sp-btn-dark-bg);
    color: var(--sp-btn-dark-fg);
  }
  .sampler-panel .sp-tbtn {
    width: 30px;
    height: 28px;
    display: grid;
    place-items: center;
    background: var(--sp-input-bg);
    border: 1px solid var(--sp-input-border);
    border-radius: 4px;
    color: var(--sp-ink);
    cursor: pointer;
    transition: all 0.15s;
  }
  .sampler-panel .sp-tbtn:hover {
    background: var(--sp-btn-ghost-hover-bg);
    border-color: var(--sp-btn-ghost-hover-border);
  }
  .sampler-panel .sp-tbtn.play {
    width: 40px;
    background: var(--sp-btn-dark-bg);
    color: var(--sp-btn-dark-fg);
    border-color: var(--sp-btn-dark-bg);
  }
  .sampler-panel .sp-tbtn.play:hover { background: var(--sp-btn-dark-hover); }
  .sampler-panel .sp-tbtn.play.playing {
    background: var(--sp-src);
    color: var(--sp-src-ink);
    border-color: var(--sp-src);
    box-shadow: 0 0 16px rgba(var(--sp-src-rgb), 0.4);
  }
  .sampler-panel .sp-tbtn.loop {
    background: var(--sp-btn-dark-bg);
    border-color: var(--sp-btn-dark-bg);
    color: var(--sp-btn-dark-fg);
    opacity: 0.55;
  }
  .sampler-panel .sp-tbtn.loop:hover {
    background: var(--sp-btn-dark-hover);
    opacity: 0.85;
  }
  .sampler-panel .sp-tbtn.loop.on {
    opacity: 1;
    color: var(--sp-src);
    box-shadow: 0 0 12px rgba(var(--sp-src-rgb), 0.5);
  }
  .sampler-panel .sp-scrub-wrap {
    flex: 1;
    display: flex;
    align-items: center;
    gap: 10px;
    margin-left: 8px;
  }
  .sampler-panel .sp-ts {
    font-family: 'Space Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 10px;
    letter-spacing: 0.04em;
    color: var(--sp-ink-dim);
    min-width: 30px;
    text-align: center;
    font-variant-numeric: tabular-nums;
  }
  .sampler-panel .sp-scrub {
    flex: 1;
    position: relative;
    height: 14px;
    cursor: pointer;
    display: flex;
    align-items: center;
  }
  .sampler-panel .sp-scrub-track {
    width: 100%;
    height: 3px;
    background: var(--sp-track-bg);
    border-radius: 2px;
    position: relative;
    overflow: visible;
  }
  .sampler-panel .sp-scrub-fill {
    position: absolute;
    left: 0; top: 0; bottom: 0;
    width: 0%;
    background: var(--sp-src);
    border-radius: 2px;
    box-shadow: 0 0 6px rgba(var(--sp-src-rgb), 0.45);
    pointer-events: none;
  }
  .sampler-panel .sp-scrub-knob {
    position: absolute;
    top: 50%;
    width: 12px; height: 12px;
    background: var(--sp-input-bg);
    border: 1.5px solid var(--sp-src);
    border-radius: 50%;
    transform: translate(-50%, -50%);
    box-shadow: 0 1px 3px rgba(0,0,0,0.2), 0 0 8px rgba(var(--sp-src-rgb), 0.3);
    pointer-events: none;
    transition: transform 0.1s;
  }
  .sampler-panel .sp-scrub:hover .sp-scrub-knob {
    transform: translate(-50%, -50%) scale(1.15);
  }

  /* Param cells */
  .sampler-panel .sp-card-params {
    display: grid;
    grid-template-columns: 1fr 1fr 1fr;
    gap: 1px;
    background: var(--sp-ink-hair);
    margin: 0 20px;
    border-radius: 4px;
    overflow: hidden;
  }
  .sampler-panel .sp-param-cell {
    background: var(--sp-input-bg);
    padding: 8px 12px;
    cursor: ns-resize;
    transition: background 0.15s;
    display: flex;
    flex-direction: column;
    gap: 4px;
    height: 74px;
    overflow: hidden;
    user-select: none;
    touch-action: none;
  }
  .sampler-panel .sp-param-cell:hover { background: var(--sp-btn-ghost-hover-bg); }
  .sampler-panel .sp-param-cell-label {
    font-size: 10px;
    font-weight: 400;
    color: var(--sp-ink-dim);
    text-transform: lowercase;
  }
  .sampler-panel .sp-param-cell-vis {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 22px;
    position: relative;
  }
  .sampler-panel .sp-param-cell-vis canvas {
    width: 100%;
    height: 100%;
    display: block;
  }
  .sampler-panel .sp-param-cell-value {
    font-size: 13px;
    font-weight: 400;
    letter-spacing: -0.01em;
    color: var(--sp-ink);
    text-transform: lowercase;
    white-space: nowrap;
  }

  /* Footer */
  .sampler-panel .sp-footer {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 14px 20px 16px;
    margin-top: 14px;
    border-top: 1px solid var(--sp-ink-hair);
    background: var(--sp-header-bg);
    position: relative;
  }
  .sampler-panel .sp-footer-drag-handle {
    position: absolute;
    inset: 0;
    cursor: grab;
    z-index: 0;
  }
  .sampler-panel .sp-footer > *:not(.sp-footer-drag-handle) {
    position: relative;
    z-index: 1;
  }
  .sampler-panel.is-dragging .sp-footer-drag-handle { cursor: grabbing; }
  .sampler-panel .sp-footer-actions {
    display: flex;
    align-items: center;
    gap: 6px;
    pointer-events: none;
  }
  .sampler-panel .sp-footer-actions > * { pointer-events: auto; }

  /* Scratch flavor toggle — per-sample default for what scratching does.
     Sibling to send-wrap; styled to feel like the play|scratch toggle in
     the transport row but keyed to the pressing accent. */
  .sampler-panel .sp-flavor-wrap {
    display: flex;
    align-items: center;
    pointer-events: none;
  }
  .sampler-panel .sp-flavor-wrap > * { pointer-events: auto; }
  .sampler-panel .sp-flavor-label {
    font-size: 9px;
    font-weight: 400;
    letter-spacing: 0.08em;
    color: var(--sp-ink-faint);
    text-transform: lowercase;
    margin-right: 8px;
    pointer-events: none;
  }
  .sampler-panel .sp-flavor {
    display: inline-flex;
    background: var(--sp-input-bg);
    border: 1px solid var(--sp-ink-hair);
    border-radius: 4px;
    padding: 2px;
    gap: 2px;
  }
  .sampler-panel .sp-flavor-btn {
    font-family: inherit;
    font-size: 10px;
    font-weight: 400;
    letter-spacing: 0.04em;
    text-transform: lowercase;
    background: transparent;
    border: none;
    padding: 4px 10px;
    border-radius: 3px;
    color: var(--sp-ink-dim);
    cursor: pointer;
    transition: background 0.15s, color 0.15s;
  }
  .sampler-panel .sp-flavor-btn:hover { color: var(--sp-ink); }
  .sampler-panel .sp-flavor-btn.on {
    background: var(--sp-pressing);
    color: var(--sp-src-ink);
  }

  .sampler-panel .sp-send-wrap {
    display: flex;
    align-items: center;
    pointer-events: none;
  }
  .sampler-panel .sp-send-wrap > * { pointer-events: auto; }
  .sampler-panel .sp-send-label {
    font-size: 9px;
    font-weight: 400;
    letter-spacing: 0.08em;
    color: var(--sp-ink-faint);
    text-transform: lowercase;
    margin-right: 8px;
    pointer-events: none;
  }
  .sampler-panel .sp-send {
    display: inline-flex;
    align-items: center;
    background: var(--sp-src);
    color: var(--sp-src-ink);
    border-radius: 4px;
    height: 32px;
    overflow: hidden;
    user-select: none;
    transition: background 0.15s, color 0.15s;
  }
  .sampler-panel .sp-send:hover {
    filter: brightness(1.05);
    box-shadow: 0 0 16px rgba(var(--sp-src-rgb), 0.4);
  }
  .sampler-panel .sp-send-arrow {
    width: 26px;
    height: 100%;
    display: grid;
    place-items: center;
    background: transparent;
    border: none;
    color: inherit;
    opacity: 0.6;
    cursor: pointer;
    transition: opacity 0.12s, background 0.12s;
  }
  .sampler-panel .sp-send-arrow:hover {
    opacity: 1;
    background: var(--sp-arrow-hover);
  }
  .sampler-panel .sp-send-arrow:disabled {
    opacity: 0.25;
    cursor: default;
    background: transparent;
  }
  .sampler-panel .sp-send-target {
    min-width: 80px;
    text-align: center;
    padding: 0 4px;
    font-size: 11px;
    font-weight: 500;
    letter-spacing: 0.06em;
    text-transform: lowercase;
    color: inherit;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
  }

  /* SOLAR's legacy shared panel stylesheet still owns older sampler fallbacks
     with !important rules. The live v31 panel has this stable id, so keep its
     prototype chrome isolated without disturbing legacy fallback nodes. */
  #sampler-panel.sampler-panel {
    width: 640px !important;
    max-width: calc(100vw - 24px) !important;
    max-height: calc(100vh - 24px) !important;
    /* A4: shrink the sampler ~7% via a uniform transform (factor computed in
       JS to fit the viewport + the area above the dock, capped at 0.93).
       !important so it beats the open/close animation's transform in the
       cascade — the fade still animates, the scale stays put (no snap), and
       no keyframe edits are needed. transform-origin 0 0 makes the scaled
       visual's top-left equal the box's left/top for exact JS positioning. */
    transform: scale(var(--sp-scale, 0.93)) !important;
    transform-origin: 0 0 !important;
    padding: 0 !important;
    gap: 0 !important;
    border: 1px solid var(--sp-panel-hair) !important;
    border-radius: 10px !important;
    overflow: hidden !important;
    background-color: var(--sp-panel-bg) !important;
    background-image:
      linear-gradient(180deg, var(--sp-panel-fill-1) 0%, var(--sp-panel-fill-2) 50%, var(--sp-panel-fill-3) 100%) !important;
    backdrop-filter: none !important;
    -webkit-backdrop-filter: none !important;
    box-shadow:
      0 6px 14px rgba(0,0,0,0.16),
      inset 0 1px 0 var(--sp-panel-edge-top),
      inset 0 -1px 0 var(--sp-panel-edge-bottom) !important;
    color: var(--sp-ink) !important;
    font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif !important;
    /* Was z-index: 50 !important — competed with the panel-physics tier
       system and broke click-to-raise on Sampler. panelStack.register()
       now owns this. */
    scale: 1 !important;
    animation: none !important;
    transform-origin: top left !important;
  }

  #sampler-panel.sampler-panel.is-ascending {
    animation: sampler-ascend 220ms ease-out forwards !important;
  }

  #sampler-panel.sampler-panel.is-descending {
    animation: sampler-descend 180ms ease-in forwards !important;
  }

  #sampler-panel.sampler-panel * {
    font-family: inherit;
  }

  /* ─── Floating workspace record (demo stand-in) ────────
     Spawned by Sampler 'apply'. Lives outside the panel.
     Each instance has its own pressing snapshot + WebGL canvas.
     ─────────────────────────────────────────────────────── */
  .ws-record {
    --ws-pressing: #03ff74;
    --ws-pressing-rgb: 3, 255, 116;
    --ws-pressing-ink: #0a0a0a;
    position: fixed;
    width: 144px;
    z-index: 40;
    user-select: none;
    -webkit-user-select: none;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
    will-change: transform;
    animation: ws-pop-in 333ms linear;
  }
  @keyframes ws-pop-in {
    0%   { transform: translateY(8px) scale(0.92); opacity: 0; }
    100% { transform: translateY(0) scale(1); opacity: 1; }
  }
  .ws-record.is-fading {
    animation: ws-pop-out 200ms linear forwards;
  }
  @keyframes ws-pop-out {
    0%   { transform: translateY(0) scale(1); opacity: 1; }
    100% { transform: translateY(-8px) scale(0.92); opacity: 0; }
  }
  .ws-flavor {
    display: inline-flex;
    background: rgba(0,0,0,0.5);
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    border: 1px solid rgba(255,255,255,0.08);
    border-radius: 4px;
    padding: 2px;
    gap: 1px;
    cursor: grab;
  }
  .ws-record.dragging .ws-flavor,
  .ws-record.is-dragging .ws-flavor,
  .ws-record.dragging .ws-filename,
  .ws-record.is-dragging .ws-filename { cursor: grabbing; }
  .ws-flavor-btn {
    font-family: inherit;
    font-size: 9px;
    font-weight: 400;
    letter-spacing: 0.04em;
    text-transform: lowercase;
    background: transparent;
    border: none;
    padding: 3px 7px;
    border-radius: 3px;
    color: rgba(255,255,255,0.55);
    cursor: pointer;
    transition: background 0.15s, color 0.15s;
  }
  .ws-flavor-btn:hover { color: rgba(255,255,255,0.9); }
  .ws-flavor-btn.on {
    background: var(--ws-pressing);
    color: var(--ws-pressing-ink);
  }
  .ws-disc-wrap {
    position: relative;
    width: 144px;
    height: 144px;
    border-radius: 50%;
    overflow: hidden;
    background: #000;
    box-shadow:
      0 0 0 1px rgba(0,0,0,0.2),
      0 4px 10px rgba(0,0,0,0.26),
      inset 0 0 18px rgba(0,0,0,0.24);
    cursor: grab;
    touch-action: none;
  }
  /* Floating record widgets (.ws-record = the OPAL step records AND the EMERALD
     workspace record; both append to document.body over the bright scene). The
     in-panel sampler record uses .sp-* classes and is unaffected. Lighten the
     heavy outer drop shadow (0 4px 10px /0.26 read as a slab over the scene);
     keep the 1px rim + inset record depth. */
  .ws-record .ws-disc-wrap {
    box-shadow:
      0 0 0 1px rgba(0,0,0,0.2),
      0 2px 6px rgba(0,0,0,0.14),
      inset 0 0 10px rgba(0,0,0,0.10);
  }
  /* Brighten the floating disc — over the bright scene the rim vignette
     (rgba 0.5), groove darkening (0.5) and inset read muted/dark. Pulled all
     the way back + a modest brightness/saturation lift on the pigment itself,
     scoped to .ws-record floaters (the in-panel .sp-* record is untouched). */
  .ws-record .ws-disc-vignette {
    background: radial-gradient(circle at center, transparent 80%, rgba(0,0,0,0.12) 100%);
  }
  .ws-record .ws-disc-grooves { opacity: 0.16; }
  .ws-record .ws-disc-canvas { filter: brightness(1.12) saturate(1.18); }
  .ws-record.is-dragging .ws-disc-wrap,
  .ws-record.dragging .ws-disc-wrap,
  .ws-record.grabbing .ws-disc-wrap { cursor: grabbing; }
  .emerald-sample-record.is-playing .ws-disc-spin,
  .opal-sample-record.spinning .ws-disc-spin {
    animation: none;
  }
  @keyframes ws-disc-spin {
    to { transform: rotate(360deg); }
  }
  .ws-disc-spin {
    position: absolute;
    inset: 0;
    border-radius: 50%;
    will-change: transform;
  }
  .ws-disc-canvas {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    border-radius: 50%;
    display: block;
  }
  .ws-disc-grooves {
    position: absolute;
    inset: 0;
    border-radius: 50%;
    pointer-events: none;
    background: repeating-radial-gradient(
      circle at center,
      rgba(0,0,0,0) 0px,
      rgba(0,0,0,0) 2px,
      rgba(0,0,0,0.18) 2px,
      rgba(0,0,0,0.18) 2.5px
    );
    mix-blend-mode: multiply;
    opacity: 0.5;
    z-index: 2;
  }
  .ws-disc-vignette {
    position: absolute;
    inset: 0;
    border-radius: 50%;
    background: radial-gradient(circle at center, transparent 55%, rgba(0,0,0,0.5) 100%);
    pointer-events: none;
    z-index: 5;
  }
  .ws-disc-sticker {
    --ws-sticker-c1: #faf9f8;
    --ws-sticker-c2: #ece9e6;
    --ws-sticker-c3: #e0ddd9;
    position: absolute;
    top: 50%; left: 50%;
    width: 60px; height: 60px;
    transform: translate(-50%, -50%);
    border-radius: 50%;
    background: radial-gradient(circle at 35% 30%, var(--ws-sticker-c1) 0%, var(--ws-sticker-c2) 70%, var(--ws-sticker-c3) 100%);
    box-shadow: 0 0 0 1px rgba(0,0,0,0.08), 0 1px 3px rgba(0,0,0,0.4);
    display: grid;
    place-items: center;
    z-index: 4;
    color: var(--ws-pressing);
    pointer-events: none;
  }
  .ws-disc-sticker svg {
    width: 85%;
    height: 85%;
    transform: translateY(0.5px);
  }
  .ws-filename {
    font-family: 'Space Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 10px;
    color: rgba(255,255,255,0.7);
    text-align: center;
    max-width: 144px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    text-shadow: 0 1px 2px rgba(0,0,0,0.6);
    cursor: grab;
    outline: none;
    border-radius: 4px;
  }
  /* Floating record widget label (.ws-record = OPAL step + EMERALD workspace
     records): flat black in light, white in eclipse, NO text-shadow — they
     float over the scene, so the old fixed white+shadow was wrong. */
  .ws-record .ws-filename { color: #000; text-shadow: none; }
  body[data-theme="dark"] .ws-record .ws-filename { color: #fff; }
  .ws-filename.is-renaming {
    color: rgba(255,255,255,0.95);
    background: rgba(255,255,255,0.12);
    box-shadow: 0 0 0 1px rgba(255,255,255,0.25);
    padding: 1px 4px;
    text-overflow: clip;
    cursor: text;
  }


  .scope {
    position: fixed;
    width: 400px;
    max-width: 90vw;
    z-index: 50;
    will-change: transform, opacity;
    /* default hidden — JS positions on first show */
    display: none;
  }
  .scope.is-visible {
    display: block;
  }
  .scope.is-ascending {
    animation: scope-ascend 333ms linear forwards;
  }
  .scope.is-descending {
    animation: scope-descend 333ms linear forwards;
    pointer-events: none;
  }
  @keyframes scope-ascend {
    0%   { transform: translateY(20px) scale(0.96); opacity: 0; }
    100% { transform: translateY(0) scale(1);       opacity: 1; }
  }
  @keyframes scope-descend {
    0%   { transform: translateY(0) scale(1);       opacity: 1; }
    100% { transform: translateY(20px) scale(0.96); opacity: 0; }
  }
  .scope svg { display: block; width: 100%; height: auto; }
  .scope.is-dragging #scope-drag-handle,
  .scope.is-dragging #scope-drag-handle-footer { cursor: grabbing; }

  /* Canvas overlay — absolutely positioned over the SVG canvas region.
     SVG viewBox is 400x225. Canvas region is at (0, 28, 400, 175). */
  #scope-canvas {
    position: absolute;
    left: 0;
    top: 12.444%;       /* 28/225 */
    width: 100%;
    height: 77.778%;    /* 175/225 */
    display: block;
    pointer-events: none;
  }
  /* Sand mode needs the canvas to receive pointer events for drawing.
     Wave/fft/spec stay click-through so the SVG drag handles work. */
  #scope-panel.scope-sand #scope-canvas {
    pointer-events: auto;
    /* Gamma-Cross cursor (Fix 6). --gamma-cursor is themed on <body> in
       index.html (this panel lives in the host doc), so it tracks eclipse. */
    cursor: var(--gamma-cursor), crosshair;
  }

  .key-white, .key-black {
    cursor: pointer;
    transition: filter 0.06s;
  }
  .key-white:active, .key-white.held {
    filter: brightness(var(--whitekey-held-brightness));
  }
  .key-black:active, .key-black.held {
    filter: brightness(var(--blackkey-held-brightness));
  }

  /* Green wash overlay — fades in only in light mode when key is held */
  .key-wash {
    opacity: 0;
    transition: opacity 0.08s ease-out;
  }
  .key-wash.is-lit {
    opacity: var(--whitekey-wash-opacity, 0);
  }

  /* Per-key LED — inactive by default, lit when key is held */
  .key-led {
    opacity: 0;
    transition: opacity 0.08s ease-out;
  }
  .key-led.is-lit {
    opacity: 1;
    filter: drop-shadow(0 0 3px rgba(93, 187, 107, 0.7))
            drop-shadow(0 0 6px rgba(93, 187, 107, 0.4));
  }

  .patch-dot {
    cursor: pointer;
    transition: filter 0.18s ease-out, fill 0.18s ease-out;
  }
  .patch-dot.is-lit {
    filter: drop-shadow(0 0 2px rgba(93, 187, 107, 0.7))
            drop-shadow(0 0 5px rgba(93, 187, 107, 0.35));
  }
  .patch-label {
    cursor: pointer;
    transition: fill 0.15s;
  }
  /* Hover microinteractions on each preset cell. The hover trigger is
     the cell group (.patch-cell) because the invisible hit rect would
     otherwise swallow :hover on the dot and label themselves. Active
     dot/label are excluded so the green glow + active text fill stay
     put when the cursor sweeps across.
     Light-mode polarity is flipped: white glow doesn't read on a pale
     slab, so the dot fill goes darker and the label goes black. */
  .patch-cell:hover .patch-dot:not(.is-lit) {
    fill: #d8d8dc;
    filter: drop-shadow(0 0 2px rgba(255, 255, 255, 0.7))
            drop-shadow(0 0 5px rgba(255, 255, 255, 0.35));
  }
  .patch-cell:hover .patch-label:not(.is-active) {
    fill: #ffffff;
  }
  body[data-theme="light"] .patch-cell:hover .patch-dot:not(.is-lit) {
    fill: #5B7FFF;
    filter: drop-shadow(0 0 2px rgba(91, 127, 255, 0.85))
            drop-shadow(0 0 5px rgba(91, 127, 255, 0.5));
  }
  body[data-theme="light"] .patch-cell:hover .patch-label:not(.is-active) {
    fill: #000000;
  }

  /* ─── Dock ─────────────────────────────────────────────── */
  .dock {
    background: var(--dock-bg);
    backdrop-filter: blur(32px) saturate(1.6);
    -webkit-backdrop-filter: blur(32px) saturate(1.6);
    border: 1px solid var(--dock-border);
    border-radius: 22px;
    padding: 8px 10px;
    box-shadow:
      0 18px 42px var(--dock-shadow),
      0 6px 16px rgba(0,0,0,0.12),
      inset 0 1px 0 var(--dock-edge-top);
    display: flex;
    align-items: center;
    gap: 4px;
  }
  .dock button {
    width: 50px; height: 50px;
    border-radius: 12px;
    border: none;
    background: var(--dock-btn-bg);
    color: var(--dock-icon);
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transition: background 0.15s, transform 0.08s, color 0.15s;
  }
  .dock button:hover { background: var(--dock-btn-hover); }
  .dock button:focus-visible {
    outline: 1px solid var(--dock-btn-active-border);
    outline-offset: 2px;
  }
  .dock button:active { transform: scale(0.94); }
  .dock button.is-active {
    border: 1px solid var(--dock-btn-active-border);
    background: var(--dock-btn-active-bg);
    color: var(--dock-icon-active);
  }
  .dock button .eclipse-icon {
    transition: transform 700ms cubic-bezier(0.2, 0.8, 0.2, 1);
  }
  .dock button.active .eclipse-icon,
  .dock button[aria-pressed="true"] .eclipse-icon {
    transform: rotate(180deg);
  }
  .dock-divider {
    width: 1px; height: 32px;
    background: var(--dock-divider);
    margin: 0 6px;
  }
  .dock-tool-wrap {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
  }
  .active-dot {
    position: absolute;
    bottom: -6px;
    left: 50%;
    transform: translateX(-50%);
    width: 4px; height: 4px;
    border-radius: 50%;
    background: var(--active-dot);
  }

  /* ─── Tooltip ─────────────────────────────────────────── */
  .tooltip {
    position: absolute;
    bottom: calc(100% + 8px);
    left: 50%;
    transform: translateX(-50%);
    background: var(--tooltip-bg);
    color: var(--tooltip-text);
    font-size: 10px;
    font-weight: 300;
    letter-spacing: 0.04em;
    padding: 4px 8px;
    border-radius: 4px;
    white-space: nowrap;
    pointer-events: none;
    opacity: 0;
    transition: opacity 0.12s;
  }
  .dock-tool-wrap:hover .tooltip { opacity: 1; }

  /* ─── More menu popover ────────────────────────────────── */
  .more-menu {
    position: absolute;
    background: var(--menu-bg);
    backdrop-filter: blur(28px) saturate(1.6);
    -webkit-backdrop-filter: blur(28px) saturate(1.6);
    border: 1px solid var(--menu-border);
    border-radius: 10px;
    box-shadow: 0 8px 20px var(--dock-shadow), inset 0 1px 0 var(--dock-edge-top);
    padding: 6px;
    min-width: 200px;
    /* Cap to viewport so a long menu (e.g. with MIDI rows visible) can scroll
       instead of running off-screen. The 32px breathing room keeps it from
       butting against the viewport edge. */
    max-height: calc(100vh - 32px);
    overflow-y: auto;
    overscroll-behavior: contain;
    z-index: 100;
  }
  .more-menu::-webkit-scrollbar { width: 6px; }
  .more-menu::-webkit-scrollbar-track { background: transparent; }
  .more-menu::-webkit-scrollbar-thumb {
    background: var(--menu-border);
    border-radius: 3px;
  }
  .more-menu::-webkit-scrollbar-thumb:hover {
    background: var(--menu-label);
    opacity: 0.5;
  }
  .more-menu-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 7px 10px;
    border-radius: 6px;
    transition: background 0.12s;
  }
  .more-menu-row.clickable {
    cursor: pointer;
  }
  .more-menu-row.clickable:hover {
    background: var(--menu-row-hover);
  }
  .more-menu-label {
    font-size: 12px;
    font-weight: 300;
    color: var(--menu-label);
    letter-spacing: 0.02em;
  }
  .more-menu-status {
    font-size: 11px;
    font-weight: 300;
    color: var(--menu-status);
    letter-spacing: 0.02em;
  }
  .more-menu-shortcut {
    font-size: 10px;
    font-weight: 300;
    color: var(--menu-status);
    letter-spacing: 0.02em;
  }
  .more-menu-divider {
    height: 1px;
    background: var(--menu-divider);
    margin: 3px 6px;
  }
  .more-menu-stepper {
    display: flex;
    align-items: center;
    gap: 4px;
  }
  .stepper-btn {
    width: 22px; height: 22px;
    background: var(--stepper-bg);
    border: none;
    border-radius: 4px;
    color: var(--menu-label);
    font-size: 13px;
    font-weight: 400;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    line-height: 1;
    padding: 0;
    transition: background 0.12s;
  }
  .stepper-btn:hover { background: var(--stepper-bg-hover); }
  .stepper-btn:active { background: var(--stepper-bg-hover); filter: brightness(1.2); }
  .stepper-value {
    min-width: 24px;
    text-align: center;
    font-size: 12px;
    font-weight: 400;
    color: var(--menu-label);
    font-variant-numeric: tabular-nums;
  }
  /* MIDI BPM control — typed input that looks like stepper-value */
  input.midi-bpm-input {
    background: transparent;
    border: none;
    outline: none;
    padding: 0 2px;
    font-family: inherit;
    min-width: 32px;
    width: 32px;
  }
  .keyboard-bpm-stepper {
    gap: 4px;
  }
  input.keyboard-bpm-input {
    min-width: 24px;
    width: 24px;
    padding: 0;
  }
  input.midi-bpm-input:focus {
    background: var(--stepper-bg-hover);
    border-radius: 3px;
  }
  /* Reset button — same shape as stepper-btn but visually subordinate */
  .stepper-btn.midi-bpm-reset {
    margin-left: 4px;
    font-size: 11px;
    opacity: 0.65;
  }
  .stepper-btn.midi-bpm-reset:hover { opacity: 1; }
  /* Play glyph in the filename row */
  .midi-play-glyph {
    font-size: 11px;
    color: var(--menu-label);
    opacity: 0.8;
    font-variant-numeric: tabular-nums;
    min-width: 14px;
    text-align: right;
  }
  #midi-filename {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 180px;
  }
  .more-menu-toggle {
    background: transparent;
    border: none;
    padding: 0;
    cursor: pointer;
  }
  .toggle-track {
    display: block;
    width: 28px;
    height: 16px;
    background: var(--toggle-track);
    border-radius: 8px;
    position: relative;
    transition: background 0.15s;
  }
  .toggle-thumb {
    position: absolute;
    top: 2px;
    left: 2px;
    width: 12px;
    height: 12px;
    background: var(--toggle-thumb);
    border-radius: 50%;
    transition: transform 0.15s, background 0.15s;
  }
  .more-menu-toggle[data-state="on"] .toggle-track { background: var(--toggle-on-track); }
  .more-menu-toggle[data-state="on"] .toggle-thumb { transform: translateX(12px); background: var(--toggle-on-thumb); }

/* SOLAR SYSTEM host integration overrides */
/* No-highlight (Fix 1): emerald-v31.css is the LAST stylesheet loaded, so this
   body rule wins the cascade over the inline <style>. It previously forced
   user-select: auto, silently re-enabling highlighting across the whole host
   and defeating Fix 1. Set to none; the inline allow-list still re-enables
   real inputs (it targets input/textarea/etc., which this body rule doesn't). */
body { width: 100vw; height: 100vh; overflow: hidden; padding: 0; display: block; align-items: initial; justify-content: initial; gap: 0; background: #ffffff; color: #111; user-select: none; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; }
.stage-label { pointer-events: none; z-index: 21; }
#info { display: none !important; }
.emerald {
  position: fixed;
  left: 50%;
  bottom: 106px;
  width: min(780px, calc(100vw - 32px));
  min-width: min(680px, calc(100vw - 32px));
  max-width: calc(100vw - 32px);
  transform: translateX(-50%);
  z-index: 22;
}
.emerald.is-hidden { transform: translate(-50%, calc(100% + 126px)); }
.emerald.is-descending { animation-name: keyboard-descent-host; }
.emerald.is-ascending { animation-name: keyboard-liftoff-host; }
@keyframes keyboard-descent-host { from { transform: translateX(-50%); opacity: 1; } to { transform: translate(-50%, calc(100% + 126px)); opacity: 0; } }
@keyframes keyboard-liftoff-host { from { transform: translate(-50%, calc(100% + 126px)); opacity: 0; } to { transform: translateX(-50%); opacity: 1; } }
.dock {
  position: fixed;
  left: 50%;
  bottom: 16px;
  transform: translateX(-50%);
  z-index: 24;
  max-width: calc(100vw - 32px);
  overflow: visible;
}
/* Static fallback tier for panels not registered with panel-physics.js
   (.more-menu and .vox-toast aren't floating panels in scope). Registered
   panels override this via inline style.zIndex from panelStack.register(). */
.more-menu, .scope, .vox-panel, .sampler-panel, .vox-toast { z-index: var(--tier-above-keyboard); }
.dock button.active, .dock button.playing, .dock button.recording, .dock button.saved { background: var(--dock-btn-active-bg); border-color: var(--dock-btn-active-border); color: var(--dock-icon-active); }
.dock button.saved { box-shadow: 0 0 0 1px rgba(3,255,116,0.28), 0 0 16px rgba(3,255,116,0.2); }
body.eclipse-mode .dock,
body.eclipse-mode .more-menu,
body.eclipse-mode .aura-theremin-panel,
body.eclipse-mode .nova-panel,
body.eclipse-mode .vox-panel,
body.eclipse-mode .sampler-panel,
body.eclipse-mode .emerald-panel-3d,
body.eclipse-mode .quartz-panel,
body.eclipse-mode .knob-panel,
body.eclipse-mode .scope-panel,
body.eclipse-mode .nexus-global-panel,
body.eclipse-mode .opal-global-panel,
body.eclipse-mode .emerald-global-panel,
body.eclipse-mode .quartz-global-panel,
body.eclipse-mode .nexus-billboard-panel,
body.eclipse-mode .opal-billboard-panel,
body.eclipse-mode .emerald-billboard-panel,
body.eclipse-mode .quartz-billboard-panel {
  box-shadow:
    0 0 0 1px rgba(254, 254, 248, 0.22),
    0 0 8px rgba(254, 254, 248, 0.38),
    0 0 18px rgba(254, 254, 248, 0.22),
    0 0 34px rgba(254, 254, 248, 0.10),
    inset 0 1px 0 var(--dock-edge-top) !important;
}
/* QUARTZ host-level eclipse glow MUST stay neutralized — the host
   .quartz-billboard-panel is a rectangle but the visible QUARTZ shape
   is a chamfered octagon inside the iframe. Letting the bulk
   eclipse-glow box-shadow above apply draws a giant rectangular halo
   well beyond the chamfered silhouette. The actual QUARTZ eclipse
   glow lives inside the iframe on .quartz-flip via filter:drop-shadow,
   which hugs the chamfered shape correctly. */
body.eclipse-mode .quartz-billboard-panel,
body.eclipse-mode .quartz-billboard-frame {
  box-shadow: none !important;
  filter: none !important;
  outline: none !important;
  background: transparent !important;
}
body.eclipse-mode #sampler-panel.sampler-panel {
  box-shadow:
    0 0 5px rgba(254, 254, 248, 0.30),
    0 0 12px rgba(254, 254, 248, 0.16),
    0 0 22px rgba(254, 254, 248, 0.07),
    inset 0 1px 0 var(--sp-panel-edge-top),
    inset 0 -1px 0 var(--sp-panel-edge-bottom) !important;
}
body.eclipse-mode .wormhole-panel {
  box-shadow:
    0 0 0 1px rgba(254, 254, 248, 0.26),
    0 0 7px rgba(254, 254, 248, 0.30),
    0 0 16px rgba(254, 254, 248, 0.16),
    0 0 26px rgba(254, 254, 248, 0.07) !important;
}
body.eclipse-mode .emerald {
  box-shadow: none !important;
  filter: none !important;
}
body.eclipse-mode .emerald svg,
body.eclipse-mode .scope {
  box-shadow: none !important;
  filter:
    drop-shadow(0 0 4px rgba(254, 254, 248, 0.28))
    drop-shadow(0 0 10px rgba(254, 254, 248, 0.14))
    drop-shadow(0 0 18px rgba(254, 254, 248, 0.06));
}
body.eclipse-mode .dock button.active,
body.eclipse-mode .dock button.playing,
body.eclipse-mode .dock button.recording,
body.eclipse-mode .dock button.saved {
  box-shadow:
    0 0 5px rgba(254, 254, 248, 0.28),
    0 0 10px rgba(254, 254, 248, 0.12);
}
#control-dock, #piano-keyboard-container { display: none !important; }

/* Self-patch confirmation toast — de-emphasized, text-only status note in the
   dock/keyboard notification strip. It's a quiet confirmation, not an alert, so
   no pill / background / shadow (other notifications keep their own styles —
   this class is used only by audio-graph.js showToast(), which is self-patch
   only). Single-slot + auto-dismiss live in that JS. Eclipse: dim white, same
   invert convention as track labels; it's a DOM overlay so never thermal-shaded. */
.self-patch-toast {
  position: fixed;
  /* Sit just above the dock with a little breathing room — the dock top is
     ~84px from the viewport bottom, so 88px clears it (~4px gap) without
     floating up toward the keyboard. */
  bottom: 88px;
  left: 50%;
  transform: translateX(-50%);
  font-size: 11.5px;
  font-weight: 500;
  letter-spacing: 0.02em;
  color: rgba(0, 0, 0, 0.65);
  z-index: 9999;
  pointer-events: none;
  white-space: nowrap;
  opacity: 0;
  transition: opacity 180ms ease;
}
body[data-theme="dark"] .self-patch-toast { color: rgba(255, 255, 255, 0.7); }
