/* ============================================================
   MOBILE LAYOUT for the Wavetable Synth.
   Rules are scoped under #synth-mobile and only take effect when the mobile
   template is injected (phone-width viewports). The one exception is the
   custom-LFO editor block (search "Custom-LFO editor"), which is intentionally
   global because that modal mounts on <body>, outside #synth-mobile. Desktop
   layout files are untouched. Knobs / dropdowns reuse their existing component
   CSS; this file only arranges the phone shell, tab bar and note grid.
   Tokens mirror docs/design/synth-mobile.html.
   ============================================================ */

#synth-mobile {
  --sm-bg: #060606;
  --sm-s0a: #0a0a0a;
  --sm-screen: #070708;
  --sm-well: #060608;
  --sm-line: rgba(255, 255, 255, .07);
  --sm-line-strong: rgba(255, 255, 255, .13);
  --sm-t1: rgba(255, 255, 255, .94);
  --sm-t2: rgba(255, 255, 255, .60);
  --sm-t3: rgba(255, 255, 255, .38);
  --sm-active: #DADADA;
  --sm-active-fg: #0c0c0c;
  --sm-glow: rgba(255, 255, 255, .32);
  --sm-val: #838383;
  --sm-r: 4px;
  --sm-r-lg: 8px;
  --sm-r-full: 9999px;
  --sm-mono: 'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, Consolas, monospace;
  /* Type scale — standardized across the mobile UI. Only 400/500/700 weights
     are loaded (Google Fonts import), so never use 600 (it rounds up to 700
     and reads as a heavier, "different" font). */
  --sm-fs-title: 12px;   /* per-panel titles (Oscillator, Filter, …) */
  --sm-fs-knob:   9px;   /* knob labels (VOL, OCT, …) — matches the KBD chip */
  --sm-fs-tab:   11px;   /* tab pills (OSC, FILTER, …) */
  --sm-fs-ctl:   11px;   /* toggles, field labels, segment buttons */
  --sm-fs-value: 12px;   /* numeric readouts (BPM, KBD, …) */
  --sm-fs-micro:  9px;   /* tiny captions (chip labels, pad octave) */
  --sm-fw-reg:   500;    /* regular body/label weight */
  --sm-fw-bold:  700;    /* emphasis (active states, values) */
  /* larger, touch-usable knobs (overrides the desktop 42px default). The
     knob rows scroll horizontally, so more knobs stay reachable via swipe. */
  --knob-md: 62px;

  position: fixed;
  inset: 0;
  display: flex;
  flex-direction: column;
  min-height: 0;
  height: 100vh;
  height: 100dvh;
  background: var(--sm-screen);
  color: var(--sm-t1);
  overflow: hidden;
  -webkit-user-select: none;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
}

#synth-mobile * { box-sizing: border-box; }

.sm-offscreen {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  opacity: 0;
  pointer-events: none;
}

/* ----------------------------------------------------------- panel area */
.sm-panelwrap {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-height: 0;
}
.sm-pscroll {
  flex: 1;
  overflow-y: auto;
  overflow-x: hidden;
  min-height: 0;
  -webkit-overflow-scrolling: touch;
  /* fade content out as it approaches the tab bar instead of hard-clipping */
  -webkit-mask-image: linear-gradient(#000 96%, transparent);
  mask-image: linear-gradient(#000 96%, transparent);
}
.sm-panel { display: none; }
.sm-panel.sm-panel-active {
  display: flex;
  flex-direction: column;
  min-height: 100%; /* fill the scroll area so short panels don't leave a void */
}
/* generic panel body */
.sm-pbody { padding: 14px 11px; display: flex; flex-direction: column; gap: 16px; }
.sm-row { display: flex; align-items: center; gap: 8px; }
.sm-sp { flex: 1; }
.sm-lbl {
  font-family: var(--sm-mono);
  font-size: 9px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--sm-t3);
}
/* segmented sub-nav (Env 1/2, LFO 1/2) */
.sm-seg {
  display: inline-flex;
  border: 1px solid var(--sm-line);
  border-radius: var(--sm-r);
  overflow: hidden;
  align-self: flex-start;
}
.sm-seg > button {
  font-family: var(--sm-mono);
  font-size: 9px;
  letter-spacing: .04em;
  padding: 7px 14px;
  color: var(--sm-t3);
  background: transparent;
  border: 0;
  cursor: pointer;
}
.sm-seg > button.active {
  background: var(--sm-active);
  color: var(--sm-active-fg);
  font-weight: 700;
}
/* Toggle buttons (Sync / Retrig / On / Out / fx toggles). Styled to match the
   desktop toggle chips (.mode-button / .aux-toggle): filled control-bg, 2px
   radius, and a light fill + glow when active — not an outlined pill. */
.sm-pill {
  font-family: var(--sm-mono);
  font-size: 9px;
  letter-spacing: .04em;
  font-weight: 500;
  color: var(--sm-t2);
  background: var(--control-bg);
  border: none;
  border-radius: 2px;
  padding: 8px 13px;
  flex: none;
  line-height: 1;
  cursor: pointer;
}
.sm-pill.active,
.sm-pill[data-enabled="true"].active {
  background: var(--sm-active);
  color: var(--sm-active-fg);
  font-weight: 700;
  box-shadow: 0 0 8px var(--text-secondary); /* MORPH-style glow */
}
/* dropdown wrapper (native select; custom-dropdown.js enhances it) */
.sm-ddwrap { flex: 1; min-width: 0; }
.sm-ddwrap select {
  width: 100%;
  font-family: var(--sm-mono);
  font-size: 11px;
}
/* LFO scope canvas */
.sm-scope {
  width: 100%;
  height: 76px;
  display: block;
  border: 1px solid var(--sm-line);
  border-radius: var(--sm-r);
  background: var(--sm-well);
}
/* LFO scopes use the full panel width and a taller draw area (an id rule in
   the desktop CSS pins them narrow, so re-assert width here at id strength) */
#synth-mobile #lfoScopeCanvas,
#synth-mobile #lfo2ScopeCanvas { width: 100%; max-width: 100%; height: 124px; }

/* Center knob rows inside panel bodies (Filter/Env/LFO/Noise/Arp) so 2-3 knob
   rows aren't left-justified against the edge. Osc uses its own 7-knob grid. */
#synth-mobile .sm-pbody .sm-knobrow {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 18px 10px;
}
/* keep cells narrow enough that a 4-knob row (Env/Arp) stays on one line, while
   2-3 knob rows (Filter/LFO/Noise) still center nicely. */
#synth-mobile .sm-pbody .sm-kcell { width: 70px; }
/* the ADSR graph is draggable to edit — taller + no scroll on drag */
.sm-envgraph { height: 110px; touch-action: none; cursor: ns-resize; }
/* Filter response curve — full width, tall, drawn from the live biquad coeffs */
#synth-mobile .sm-filtergraph { width: 100%; max-width: 100%; height: 150px; }
/* Bigger Filter knobs (Cut/Res) so the cutoff is easy to grab on mobile */
#synth-mobile .sm-panel[data-tab="filter"] { --knob-md: 78px; }
#synth-mobile .sm-filter-knobs { gap: 26px 28px; }
#synth-mobile .sm-filter-knobs .sm-kcell { width: 92px; }
/* sub waveform selector laid out for touch (visuals reuse desktop .wf-btn) */
.sm-wavesel { display: flex; gap: 6px; }
.sm-wavesel .wf-btn { flex: 1; height: 46px; }
/* Sub page: 4 waveform buttons + volume knob on one line */
.sm-subrow { gap: 14px; align-items: center; }
.sm-subrow .sm-wavesel { flex: 1; min-width: 0; }
#synth-mobile .sm-subrow .sm-kcell { width: auto; flex: 0 0 66px; }

/* FX accordion */
.sm-fx {
  border: 1px solid var(--sm-line);
  border-radius: var(--sm-r);
}
.sm-fx-head {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 11px;
  cursor: pointer;
}
.sm-fx-name { flex: 1; text-align: left; font-size: 12.5px; font-weight: 600; color: var(--sm-t2); }
.sm-fx-open > .sm-fx-head .sm-fx-name { color: var(--sm-t1); }
.sm-fx-body {
  display: none;
  padding: 2px 0 12px;
  border-top: 1px solid var(--sm-line);
}
.sm-fx-open > .sm-fx-body { display: block; }
/* FX body knob rows: centered + wrapping so 3-knob effects don't sit
   left-justified against the edge (looked unbalanced). */
.sm-fx-body .sm-knobrow {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  padding: 14px 9px 4px;
  gap: 18px 16px;
}
#synth-mobile .sm-fx-body .sm-kcell { width: 74px; }

/* Graphic EQ: bigger response graph + per-band shape selectors (#16).
   The shared renderer pins the canvas to a fixed inline width/height, so we
   override with !important to make it span the mobile panel width. */
#synth-mobile #geqGraphCanvas { width: 100% !important; max-width: 100% !important; height: 140px !important; }
#synth-mobile .sm-geq-types {
  display: flex;
  gap: 12px;
  padding: 12px 9px 2px;
  justify-content: center;
}
#synth-mobile .sm-geq-col {
  display: flex;
  align-items: center;
  gap: 6px;
  flex: 1;
  justify-content: center;
}
#synth-mobile .sm-geq-bandlbl {
  font-family: var(--sm-mono);
  font-size: 9px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--sm-t3);
  margin-right: 2px;
}
#synth-mobile .sm-geq-types .geq-filter-type-btn {
  width: 42px;
  height: 34px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--sm-line);
  border-radius: var(--sm-r);
  background: var(--sm-well);
  color: var(--sm-t2);
  cursor: pointer;
  padding: 0;
}
#synth-mobile .sm-geq-types .geq-filter-type-btn.active {
  color: var(--sm-active-fg);
  background: var(--sm-active);
  border-color: transparent;
}

/* Compressor: MB/Single switch, GR meter scope, crossover knobs (#17).
   The 3-band meter keeps its native horizontal-bar layout (16-effects.css);
   we only control which meter shows per mode + give it breathing room. */
#synth-mobile .sm-mbtoggle { min-width: 60px; text-align: center; }
#synth-mobile .sm-comp-meter { padding: 12px 11px 2px; }
#synth-mobile .comp-multiband-meter,
#synth-mobile .comp-single-meter { display: none; }
#synth-mobile .sm-fx[data-fx="comp"].sm-comp-mb .comp-multiband-meter { display: flex; }
#synth-mobile .sm-fx[data-fx="comp"].sm-comp-single .comp-single-meter {
  display: block;
  height: 16px;
  border: 1px solid var(--sm-line);
  border-radius: var(--sm-r-full);
  background: var(--sm-well);
  overflow: hidden;
}
#synth-mobile .comp-single-meter-fill { height: 100%; width: 0; background: var(--sm-val); }
/* Comp bottom row = Time + crossover knobs; crossovers show in multiband only,
   so multiband shows 3-up (Time/Low·X/Hi·X), single shows just Time centered. */
#synth-mobile .sm-comp-bottom { gap: 18px 10px; }
#synth-mobile .sm-comp-xknob { display: none; }
#synth-mobile .sm-fx[data-fx="comp"].sm-comp-mb .sm-comp-xknob { display: flex; }

/* wavetable hero — modest fixed height; spare vertical space is taken up by
   docking the transport strip to the bottom (see [data-tab="osc"] .sm-ustrip).
   Scoped under #synth-mobile to beat the existing #visualizer-container /
   #visualizer ID rules (ID specificity would otherwise win). */
/* Hero row: wavetable visualizer + a vertical Position rail (MORPH under it)
   to its right. Explicit widths neutralize the desktop 18-responsive.css
   #visualizer-container rule. */
.sm-herorow {
  display: flex;
  align-items: stretch;
  gap: 12px;
  margin: 8px auto 0;
  width: 90%;
  height: clamp(160px, 25vh, 240px);
  flex: 0 0 auto;
}
#synth-mobile .sm-wt-hero {
  position: relative;
  margin: 0;
  border: 0;
  border-radius: 0;
  background: #050507;
  overflow: hidden;
  flex: 1 1 auto;
  min-width: 0;
  width: auto;
  height: 100%;
  order: 0;
}
#synth-mobile .sm-wt-hero canvas {
  display: block;
  width: 100%;
  height: 100%;
  padding: 0; /* kill the desktop #visualizer padding-top:55px (empty top band) */
}
/* Position-rail + MORPH column beside the visualizer */
.sm-poscol {
  flex: 0 0 50px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  min-height: 0;
}
.sm-morphbtn {
  flex: 0 0 auto;
  width: 100%;
  font-family: var(--sm-mono);
  font-size: 8px;
  letter-spacing: .06em;
  padding: 7px 0;
  text-align: center;
  border-radius: var(--sm-r);
  border: 1px solid var(--sm-line);
  background: transparent;
  color: var(--sm-t2);
  cursor: pointer;
}
.sm-morphbtn.active {
  background: var(--sm-active);
  color: var(--sm-active-fg);
  border-color: transparent;
  font-weight: 700;
}
/* collapsed visualizer: thin strip to give the panels more room */
#synth-mobile.sm-hero-collapsed .sm-wt-hero { height: 30px; }
#synth-mobile.sm-hero-collapsed .sm-wt-hero canvas { opacity: .5; }
#synth-mobile.sm-hero-collapsed .sm-posv { display: none; }
.sm-herotoggle { font-size: 11px; line-height: 1; padding: 4px 6px; }
.sm-wt-modes {
  position: absolute;
  top: 8px;
  left: 8px;
  right: auto;
  display: flex;
  gap: 4px;
  z-index: 2;
}
.sm-tg {
  font-family: var(--sm-mono);
  font-size: 8px;
  letter-spacing: .08em;
  color: var(--sm-t2);
  background: rgba(0, 0, 0, .5);
  border: 1px solid var(--sm-line);
  border-radius: 2px;
  padding: 4px 7px;
  cursor: pointer;
}
.sm-tg.active {
  color: var(--sm-active-fg);
  background: var(--sm-active);
  border-color: transparent;
  font-weight: 700;
}

/* .wav pill row */
.sm-wavrow {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 2px 9px 0;
}
.sm-wavpill {
  flex: 1;
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
  background: transparent;
  border: 0;
  padding: 2px 4px;
}
.sm-nav {
  color: var(--sm-t3);
  background: transparent;
  border: 0;
  font-size: 19px;
  line-height: 1;
  padding: 8px 11px;
  cursor: pointer;
}
.sm-fn {
  flex: 1;
  /* A <button> reset elsewhere makes this display:flex, which turns
     text-align:center into a no-op — the text needs justify-content
     to actually center inside a flex box. Kept alongside text-align
     as a fallback for a plain block/inline-block rendering. */
  display: flex;
  justify-content: center;
  background: transparent;
  border: 0;
  font-family: var(--sm-mono);
  font-size: 10px;
  color: var(--sm-t1);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  text-align: center;
  cursor: pointer;
}

/* Vertical Position fader beside the visualizer — bespoke hardware-fader look
   (recessed slot with travel ticks + a chunky cap), not a stock round slider. */
.sm-posv {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  width: 100%;
  min-height: 0;
}
.sm-posv-val { font-family: var(--sm-mono); font-size: 10px; font-weight: 600; color: var(--sm-val); }
.sm-posv-lbl {
  font-family: var(--sm-mono);
  font-size: 9px;
  letter-spacing: .14em;
  color: var(--sm-t3);
}
.sm-posv-track {
  flex: 1;
  width: 22px;
  min-height: 0;
  position: relative;
  border-radius: 5px;
  background:
    repeating-linear-gradient(to top, transparent 0 8px, rgba(255, 255, 255, .05) 8px 9px),
    linear-gradient(#111114, #050506);
  border: 1px solid var(--sm-line-strong);
  box-shadow: inset 0 2px 5px rgba(0, 0, 0, .85), inset 0 -1px 0 rgba(255, 255, 255, .04);
  touch-action: none;
  overflow: hidden;
}
.sm-posv-fill {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 50%;
  background: linear-gradient(to top, rgba(131, 131, 131, .85), rgba(131, 131, 131, .22));
}
.sm-posv-head {
  position: absolute;
  left: 50%;
  bottom: 50%;
  width: 18px;
  height: 13px;
  transform: translate(-50%, 50%);
  border-radius: 3px;
  background: linear-gradient(#f4f4f6, #c2c2c8);
  border: 1px solid rgba(0, 0, 0, .55);
  box-shadow: 0 1px 3px rgba(0, 0, 0, .65), inset 0 1px 0 rgba(255, 255, 255, .85);
}
.sm-posv-head::after {
  content: '';
  position: absolute;
  left: 3px;
  right: 3px;
  top: 50%;
  height: 1px;
  margin-top: -0.5px;
  background: rgba(0, 0, 0, .4);
}

/* knob rows — 4 per row. minmax(0,1fr) forces 4 equal columns regardless of
   each knob's min-content (1fr alone = minmax(auto,1fr), which let a wide knob
   steal space and drop the 4th knob off the row). */
.sm-knobrow {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 20px 10px;
  padding: 16px 10px;
  flex: none;
  justify-items: center;
  width: 100%;
  max-width: 100%;
  min-width: 0;
}
/* Hard-clamp the panel to the viewport so a wide knob's content can't push
   the grid (and the 4th knob) off-screen. min-width:0 lets the flex chain
   shrink instead of growing to max-content. */
#synth-mobile .sm-panel-active {
  width: 100%;
  max-width: 100%;
  overflow-x: hidden;
}
#synth-mobile .sm-pbody,
#synth-mobile .env-content-1,
#synth-mobile .env-content-2,
#synth-mobile .lfo-content-1,
#synth-mobile .lfo-content-2 { min-width: 0; max-width: 100%; overflow-x: hidden; }
/* The knob cells ARE the [data-knob] elements, and knobs/style.css (loaded
   later) pins [data-knob] to 42px — which clips the 58px knob + label. Scope
   under #synth-mobile to win specificity and let the cell grow to fit. */
#synth-mobile .sm-kcell {
  width: 100%;
  height: auto;
  min-width: 0;
  min-height: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
}
#synth-mobile .sm-kcell .knob-container { margin: 0 auto; }

/* osc utility strip */
.sm-ustrip {
  display: flex;
  align-items: center;
  gap: 7px;
  margin: 0 9px 10px;
  overflow-x: auto;
  scrollbar-width: none;
}
/* Distribute the Osc blocks (wavrow / knobs / warp / transport) to fill the
   panel height evenly instead of leaving a void above the tab bar. */
/* Osc: top-align so the warp-mode row sits right under the knobs (not far
   away); the transport strip docks to the bottom. */
#synth-mobile .sm-panel[data-tab="osc"] { justify-content: flex-start; padding-bottom: 4px; }
#synth-mobile [data-tab="osc"] .sm-warprow { margin-top: 2px; }
#synth-mobile [data-tab="osc"] .sm-ustrip { margin-top: 8px; }

/* warp-mode row sits directly in the Osc panel (no .sm-pbody wrapper) */
.sm-warprow { margin: 4px 9px 0; }
.sm-warprow .sm-lbl { flex: none; }
/* Warp dropdown opens UPWARD on mobile (it sits low in the Osc panel, so a
   downward menu would spill into the transport/tab bar). */
#synth-mobile .sm-warprow .custom-dropdown-scroll-container {
  top: auto;
  bottom: 100%;
  margin-top: 0;
  margin-bottom: 2px;
}
.sm-ustrip::-webkit-scrollbar { display: none; }
.sm-step {
  display: flex;
  align-items: center;
  gap: 6px;
  background: var(--sm-well);
  border: 1px solid var(--sm-line);
  border-radius: var(--sm-r);
  padding: 7px 10px;
  flex: none;
}
.sm-sl {
  font-family: var(--sm-mono);
  font-size: 8px;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--sm-t3);
}
.sm-sv {
  font-family: var(--sm-mono);
  font-size: 12px;
  color: var(--sm-val);
  background: transparent;
  border: 0;
  width: 40px;
  text-align: center;
}
.sm-pm {
  color: var(--sm-t3);
  background: transparent;
  border: 0;
  font-family: var(--sm-mono);
  font-size: 17px;
  cursor: pointer;
  padding: 4px 9px;
}

/* mod matrix container — let it use the full mobile width instead of the
   desktop 320px cap with fixed 45px columns. */
.sm-modwrap { padding: 9px; }
#synth-mobile .mod-matrix { max-width: none; width: 100%; align-items: stretch; }
#synth-mobile .mod-matrix-scroll-container { width: 100%; }
#synth-mobile .mod-matrix-grid {
  grid-template-columns: auto repeat(4, 1fr);
  width: 100%;
}
/* Mod matrix fits the panel — show all source rows with no nested vertical
   scroll (a scroll-within-scroll made cells confusing to drag). */
#synth-mobile .mod-matrix-grid-wrapper { height: auto; max-height: none; overflow: visible; }
/* Page-group nav (‹ OSC CORE ›): bigger, clearer tap targets. */
/* Page-nav arrows styled like the filename picker: bare ‹ › glyphs, no border
   or background. */
#synth-mobile .mod-matrix-page-btn {
  min-width: 0;
  width: auto;
  height: auto;
  font-size: 19px;
  line-height: 1;
  border: 0;
  border-radius: 0;
  background: transparent;
  color: var(--sm-t2);
  padding: 8px 10px;
}
#synth-mobile .mod-matrix-page-indicator {
  font-size: 12px;
  min-width: 92px;
  color: var(--sm-t1);
  padding-top: 0;
}
#synth-mobile .mod-matrix-pagination { gap: 8px; }
/* let a finger-drag on a cell change its value instead of scrolling the page */
#synth-mobile .mod-matrix-cell,
#synth-mobile .fx-matrix-cell,
#synth-mobile .mod-matrix-cell-value { touch-action: none; }
/* taller cells = fat-finger drag targets */
#synth-mobile .mod-matrix-cell,
#synth-mobile .fx-matrix-cell { min-height: 44px; }

/* Custom-LFO editor draw canvas: let a finger draw the curve instead of
   scrolling. The editor modal is appended to <body> (outside #synth-mobile),
   so this rule is global; touch-action:none is a no-op for mouse on desktop. */
#custom-lfo-canvas { touch-action: none; }

/* Custom-LFO editor: full-screen on phones with a big draw canvas and the
   side controls compacted to a wrapping row under it. */
@media (max-width: 640px) {
  /* All rules are #custom-lfo-modal-scoped: custom-lfo-editor.css loads AFTER
     this file, so plain-class rules here lose on source order. The id wins. */

  /* Fit the panel to its content (centered); no border radius; background matches
     the web synth (var(--sm-screen)); no header underline. */
  #custom-lfo-modal .lfo-modal-panel {
    width: min(94vw, 440px);
    height: auto;
    max-width: none;
    max-height: 92dvh;
    border-radius: 0;
    overflow-y: auto;
    background: #070708;
  }
  /* Title: same mono font as the app, small, tight header, no divider. */
  #custom-lfo-modal .lfo-modal-header {
    padding: 6px 14px;
    min-height: 0;
    background: #070708;
    border-bottom: 0;
  }
  #custom-lfo-modal .lfo-modal-title {
    font-family: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    font-weight: 700;
    font-size: 11px;
    letter-spacing: .04em;
  }
  /* Close button: no background chip. */
  #custom-lfo-modal .lfo-modal-close { background: transparent; border: 0; box-shadow: none; }
  #custom-lfo-modal .lfo-modal-body {
    display: flex;
    flex-direction: column;
    padding: 14px;
    gap: 14px;
    overflow-y: auto;
    background: #070708;
  }
  #custom-lfo-modal .lfo-canvas-wrap { width: 100%; }
  /* keep the canvas' native 780x240 ratio so the curve isn't distorted */
  #custom-lfo-modal #custom-lfo-canvas { width: 100%; height: auto; aspect-ratio: 780 / 240; }
  /* Two columns of control GROUPS (SEGMENT/SNAP, GRID/GRID Y, Reset/GRID Y
     SCALE) — each a label stacked over its control, filling the modal width. */
  #custom-lfo-modal .lfo-side-controls {
    display: grid;
    grid-template-columns: 1fr 1fr;
    align-items: start;
    gap: 14px 16px;
    width: 100%;
    max-width: none;
    min-width: 0;
    box-sizing: border-box;
    padding: 0;
    background: transparent;
    border: 0;
    border-radius: 0;
  }
  #custom-lfo-modal .lfo-ctl { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
  #custom-lfo-modal .lfo-side-label,
  #custom-lfo-modal .lfo-side-label--spaced { margin: 0; text-align: left; }
  #custom-lfo-modal .lfo-ctl select,
  #custom-lfo-modal .lfo-ctl .custom-dropdown { width: 100%; min-width: 0; }
  #custom-lfo-modal .lfo-ctl .lfo-slider { width: 100%; }
  #custom-lfo-modal .lfo-ctl-reset { justify-content: flex-end; }
  #custom-lfo-modal .lfo-ctl-reset .lfo-actions-row { width: 100%; }
  #custom-lfo-modal .lfo-reset-btn { width: 100%; margin-top: 0; }

  /* Flat controls matching the rest of the site — drop the cyan→purple
     gradients on the SNAP toggles and the Grid-Y-Scale slider. */
  #custom-lfo-modal .lfo-checkbox input:checked + .lfo-checkbox-box {
    background: #dadada;
    color: #16161a;
    box-shadow: none;
  }
  #custom-lfo-modal .lfo-checkbox input:checked + .lfo-checkbox-box:hover {
    background: #ececec;
    box-shadow: none;
    transform: none;
  }
  #custom-lfo-modal .lfo-slider::before { background: rgba(216, 216, 218, .32); }
  #custom-lfo-modal .lfo-slider::-webkit-slider-thumb { background: #d8d8d8 !important; }
  #custom-lfo-modal .lfo-slider::-moz-range-thumb { background: #d8d8d8 !important; }
}

/* --------------------------------------------------------------- tab bar */
.sm-sectabs {
  display: flex;
  gap: 7px;
  padding: 11px 11px;
  flex: none;
  background: var(--sm-screen); /* opaque so scrolling panel content can't show through */
  border-top: 1px solid var(--sm-line);
  overflow-x: auto;
  scrollbar-width: none;
  -webkit-mask-image: linear-gradient(90deg, #000 90%, transparent);
  mask-image: linear-gradient(90deg, #000 90%, transparent);
}
.sm-sectabs::-webkit-scrollbar { display: none; }
.sm-stab {
  font-family: var(--sm-mono);
  font-size: 9px;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--sm-t3);
  background: transparent;
  border: 1px solid var(--sm-line);
  border-radius: var(--sm-r-full);
  padding: 10px 16px;
  flex: none;
  cursor: pointer;
}
.sm-stab-on {
  color: var(--sm-active-fg);
  background: var(--sm-active);
  border-color: transparent;
  font-weight: 700;
}

/* ----------------------------------------------------------- keyboard dock */
.sm-kbd-dock {
  flex: none;
  background: var(--sm-s0a);
  border-top: 1px solid var(--sm-line);
  padding: 7px 0 9px;
}
.sm-kbd-tools {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0 10px 8px;
  overflow-x: auto;
  scrollbar-width: none;
}
.sm-kbd-tools::-webkit-scrollbar { display: none; }
.sm-tbtn {
  font-family: var(--sm-mono);
  font-size: 9px;
  letter-spacing: .04em;
  color: var(--sm-t2);
  background: transparent;
  border: 1px solid var(--sm-line);
  border-radius: var(--sm-r);
  padding: 9px 14px;
  flex: none;
  cursor: pointer;
}
.sm-tbtn.active {
  color: var(--sm-active-fg);
  background: var(--sm-active);
  border-color: transparent;
  font-weight: 700;
}
.sm-scalesel,
.sm-octctl {
  display: flex;
  align-items: center;
  gap: 8px;
  border: 1px solid var(--sm-line);
  border-radius: var(--sm-r);
  padding: 9px 11px;
  flex: none;
  font-family: var(--sm-mono);
  font-size: 9px;
  color: var(--sm-t1);
  background: transparent;
  cursor: pointer;
}
.sm-spacer { flex: 0 0 8px; }
/* Expression row: own line under the kbd tools, full-width slider. */
.sm-expr { display: flex; align-items: center; gap: 11px; margin: 0 10px 9px; }
/* Mod/Bend segmented switch — fixed-width segments so selecting a mode never
   reflows the row, and it reads unambiguously as a 2-state toggle. */
.sm-exprseg {
  display: inline-flex;
  flex: none;
  border: 1px solid var(--sm-line-strong);
  border-radius: var(--sm-r);
  overflow: hidden;
}
.sm-expropt {
  font-family: var(--sm-mono);
  font-size: 9px;
  letter-spacing: .06em;
  width: 42px;
  padding: 7px 0;
  text-align: center;
  color: var(--sm-t2);
  background: transparent;
  border: 0;
  cursor: pointer;
}
.sm-expropt + .sm-expropt { border-left: 1px solid var(--sm-line); }
.sm-expropt.active {
  color: var(--sm-active-fg);
  background: var(--sm-active);
  font-weight: 700;
}

.sm-mt {
  flex: 1;
  min-width: 100px;
  height: 12px;
  border-radius: var(--sm-r-full);
  background: var(--sm-well);
  border: 1px solid var(--sm-line);
  position: relative;
  touch-action: none;
}
.sm-mh {
  position: absolute;
  top: 50%;
  left: 0;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: #d8d8d8;
  transform: translate(-50%, -50%);
}

/* swipeable note grid */
.sm-gridwrap {
  position: relative;
  -webkit-mask-image: linear-gradient(90deg, #000 94%, transparent);
  mask-image: linear-gradient(90deg, #000 94%, transparent);
}
.sm-grid-keys {
  display: grid;
  grid-template-rows: 1fr 1fr;
  grid-auto-flow: column;
  grid-auto-columns: 54px;
  gap: 5px;
  padding: 0 10px;
  height: 104px;
  overflow-x: auto;
  scrollbar-width: none;
  touch-action: pan-x;
}
.sm-grid-keys::-webkit-scrollbar { display: none; }
.sm-pad {
  border-radius: 7px;
  background: #161618;
  border: 1px solid var(--sm-line);
  display: flex;
  align-items: flex-end;
  gap: 2px;
  padding: 6px 7px;
  font-family: var(--sm-mono);
  font-size: 10px;
  color: var(--sm-t3);
  cursor: pointer;
}
.sm-pad-root {
  background: rgba(255, 255, 255, .05);
  border-color: rgba(255, 255, 255, .16);
  color: var(--sm-t1);
}
.sm-pad-act {
  background: var(--sm-active);
  border-color: transparent;
  color: var(--sm-active-fg);
  box-shadow: 0 0 12px var(--sm-glow);
}
.sm-pad .sm-oc { opacity: .55; font-size: 7px; }

/* --------------------------------------------------------- panel titles */
/* Arpeggiator panel header was identical in size/style to the "Pattern"
   field label below it. Promote the header to a real title so the hierarchy
   reads clearly. */
#synth-mobile .sm-panel[data-tab="arp"] .sm-pbody > .sm-row:first-child .sm-lbl {
  font-size: 14px;
  font-weight: 700;
  letter-spacing: .03em;
  color: var(--sm-t1);
  text-transform: none;
}

/* =============================================================
   Round 2: nav at top, single-line dock, fit-not-scroll panels
   ============================================================= */

/* Section nav moved to the very top of the page (divider below, not above). */
.sm-sectabs-top {
  border-top: 0;
  border-bottom: 1px solid var(--sm-line);
}

/* Single-line dock: Hold / Scale / Octave / MOD|BEND + a compact slider all on
   one row. Octave box sized to match the Hold/Scale chips beside it. */
#synth-mobile .sm-kbd-tools { flex-wrap: nowrap; gap: 6px; }
#synth-mobile .sm-kbd-tools .sm-tbtn { padding: 8px 11px; }
#synth-mobile .sm-kbd-tools .sm-scalesel { padding: 8px 9px; gap: 5px; }
#synth-mobile .sm-octctl { padding: 8px 8px; gap: 4px; }
#synth-mobile .sm-octctl .sm-pm { font-size: 14px; padding: 2px 5px; }
#synth-mobile #smGridOctLabel { font-size: 9px; white-space: nowrap; }
#synth-mobile .sm-expr { flex: 1; min-width: 88px; gap: 6px; margin: 0; }
#synth-mobile .sm-expropt { width: 33px; padding: 6px 0; }
#synth-mobile .sm-mt { flex: 1; min-width: 38px; height: 10px; }
#synth-mobile .sm-mh { width: 13px; height: 13px; }

/* Panels fit the viewport — no scroll — except the long FX rack, which scrolls
   internally. (Mask removed since there's nothing to fade.) */
#synth-mobile .sm-pscroll {
  overflow: hidden;
  -webkit-mask-image: none;
  mask-image: none;
}
#synth-mobile .sm-panel[data-tab="fx"].sm-panel-active {
  height: 100%;
  overflow-y: auto;
}
/* Mod matrix pinned + fully visible (no nested scroll, fits the panel). */
#synth-mobile .sm-panel[data-tab="mod"] .sm-modwrap { padding: 10px 9px; }
#synth-mobile .mod-matrix-grid-wrapper { max-height: none; overflow: visible; }

/* =============================================================
   Round 3: reordered stack (file selector → wavetable/POS →
   tab nav → tab content → dock tools → keyboard), compacter
   heights so panels fit the short mobile viewport, uniform dock
   control height, POS % readout removed, env graph handles.
   ============================================================= */

/* File selector now sits at the very top as global chrome. */
.sm-wavrow-top { margin: 6px 9px 2px; }

/* Tab nav sits between the wavetable and the tab content — a divider band
   (this is the requested border below the wavetable row). */
.sm-sectabs-mid {
  border-top: 1px solid var(--sm-line-strong);
  border-bottom: 1px solid var(--sm-line);
}

/* Drop the POS % readout to save vertical space in the rail. */
.sm-posv-val { display: none; }

/* Tighter hero + graphs so the reordered stack and each panel's knobs fit a
   short Safari viewport (toolbars eat a big chunk) without clipping. */
.sm-herorow { height: clamp(106px, 16vh, 142px); margin: 4px auto 10px; }
#synth-mobile .sm-filtergraph { height: 72px; }
#synth-mobile #lfoScopeCanvas, #synth-mobile #lfo2ScopeCanvas { height: 60px; }
#synth-mobile .sm-envgraph { height: 90px; }
/* Shorter keyboard + tighter mod-matrix cells reclaim vertical room so the
   taller panels (Filter/LFO/Mod) fit the short viewport without scrolling. */
#synth-mobile .sm-grid-keys { height: 86px; }
#synth-mobile .mod-matrix-cell, #synth-mobile .fx-matrix-cell { min-height: 34px; }
#synth-mobile .sm-panel[data-tab="mod"] .sm-modwrap { padding: 6px 9px; }
/* Filter knobs slightly smaller on the short mobile viewport so the curve +
   Cut/Res both fit without clipping. */
#synth-mobile .sm-panel[data-tab="filter"] { --knob-md: 70px; }
#synth-mobile .sm-filter-knobs .sm-kcell { width: 80px; }

/* Dock: force all controls to one uniform height so the OCT stepper is no
   taller than Hold / Scale / MOD|BEND beside it. */
#synth-mobile .sm-kbd-tools .sm-tbtn,
#synth-mobile .sm-kbd-tools .sm-scalesel,
#synth-mobile .sm-kbd-tools .sm-octctl,
#synth-mobile .sm-kbd-tools .sm-exprseg {
  height: 34px;
  display: flex;
  align-items: center;
  justify-content: center;
}
#synth-mobile .sm-octctl .sm-pm { font-size: 15px; padding: 0 6px; line-height: 1; }

/* =============================================================
   Round 4: file-box arrows inside the box + MORPH on that row,
   taller hero/fader, warp under knobs, transport chips styled
   like the OCT stepper, shorter dock chips.
   ============================================================= */

/* File-selector: ‹ name › inside a single box, MORPH beside it. */
#synth-mobile .sm-wavrow-top { display: flex; align-items: center; gap: 8px; margin: 6px 9px 2px; }
#synth-mobile .sm-wavrow-top .sm-wavpill {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 2px;
  padding: 0 3px;
  background: var(--sm-well);
  border: 1px solid var(--sm-line-strong);
  border-radius: var(--sm-r);
}
#synth-mobile .sm-wavrow-top .sm-fn {
  border: 0 !important;
  background: transparent !important;
  box-shadow: none !important;
  /* This button also carries the desktop .visualizer-file-box class, whose
     own justify-content:space-between (15-visualizer.css) is equal
     specificity to the base .sm-fn rule and was winning on source order —
     the text sat flush left instead of centered. !important here beats it
     outright rather than depending on load order. */
  justify-content: center !important;
  padding: 9px 4px;
  text-align: center;
}
/* The ‹ › wavetable-nav arrows were the same size as the file-name text
   beside them but had barely any padding — too small a target for how often
   they get tapped. Bigger glyph + more padding. */
#synth-mobile .sm-wavrow-top .sm-nav { padding: 12px 14px; font-size: 20px; color: var(--sm-t2); }
#synth-mobile .sm-wavrow-top .sm-morphbtn {
  flex: 0 0 auto;
  width: auto;
  padding: 9px 14px;
  font-size: 9px;
}

/* KBD / BPM / PB transport chips look like the OCT stepper (transparent box). */
#synth-mobile .sm-step {
  background: transparent;
  border: 1px solid var(--sm-line);
  border-radius: var(--sm-r);
  padding: 7px 9px;
}

/* Consistent per-tab header (styled like the old Arpeggiator title) for every
   panel — rendered as the panel's first flex item via ::before. */
#synth-mobile .sm-panel::before {
  font-family: var(--sm-mono);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: .05em;
  color: var(--sm-t1);
  padding: 6px 12px 4px;
  flex: 0 0 auto;
}
#synth-mobile .sm-panel[data-tab="osc"]::before { content: "Oscillator"; }
#synth-mobile .sm-panel[data-tab="filter"]::before { content: "Filter"; }
#synth-mobile .sm-panel[data-tab="env"]::before { content: "Envelope"; }
#synth-mobile .sm-panel[data-tab="lfo"]::before { content: "LFO"; }
#synth-mobile .sm-panel[data-tab="sub"]::before { content: "Sub Oscillator"; }
#synth-mobile .sm-panel[data-tab="noise"]::before { content: "Noise"; }
#synth-mobile .sm-panel[data-tab="mod"]::before { content: "Modulation"; }
#synth-mobile .sm-panel[data-tab="fx"]::before { content: "Effects"; }
#synth-mobile .sm-panel[data-tab="arp"]::before { content: "Arpeggiator"; }
/* reclaim the header's vertical cost from the keyboard so panels still fit */
#synth-mobile .sm-grid-keys { height: 68px; }

/* =============================================================
   Round 5: file-box right arrow visible, hero breathing room,
   left-aligned tab titles, edge-to-edge MOD|BEND, Sub/Noise
   toggles on the title row, draggable filter curve, transport
   chips matched to the OCT stepper, warp dropdown docked under
   the warp knob.
   ============================================================= */

/* 1) The right ‹›: let the filename shrink so the › stays inside the box. */
#synth-mobile .sm-wavrow-top .sm-wavpill { overflow: hidden; }
#synth-mobile .sm-wavrow-top .sm-fn { min-width: 0; }
#synth-mobile .sm-wavrow-top .sm-nav { flex: 0 0 auto; }

/* 3) Tab titles left-aligned (the ::before header was rendering centered). */
#synth-mobile .sm-panel::before { align-self: stretch; width: 100%; text-align: left; }

/* 4) MOD | BEND: the active segment fills the full control height edge-to-edge
   (the uniform-height dock rule set align-items:center, which shrank the fill
   into an inner box). Stretch the segments so the colour spans the whole box. */
#synth-mobile .sm-kbd-tools .sm-exprseg { align-items: stretch; padding: 0; }
#synth-mobile .sm-kbd-tools .sm-expropt {
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* 5) Sub / Noise: On/Out toggles share the title row, so suppress the ::before
   header for those two panels and use a real header element. */
#synth-mobile .sm-panel[data-tab="sub"]::before,
#synth-mobile .sm-panel[data-tab="noise"]::before { content: none; }
#synth-mobile .sm-phead { display: flex; align-items: center; gap: 8px; }
#synth-mobile .sm-ptitle {
  font-family: var(--sm-mono);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: .05em;
  color: var(--sm-t1);
}

/* 6) Filter curve is draggable (x = cutoff, y = resonance). */
#synth-mobile .sm-filtergraph { touch-action: none; cursor: crosshair; }

/* 7) KBD / BPM / PB transport chips match the dock OCT stepper (same box,
   arrow glyphs, colours and font). */
#synth-mobile .sm-ustrip .sm-step {
  gap: 5px;
  padding: 0 9px;
  height: 34px;
  background: transparent;
  border: 1px solid var(--sm-line);
  border-radius: var(--sm-r);
}
#synth-mobile .sm-ustrip .sm-step .sm-pm { font-size: 15px; padding: 0 6px; line-height: 1; color: var(--sm-t3); }
#synth-mobile .sm-ustrip .sm-step .sm-sl { font-size: 9px; color: var(--sm-t2); }
#synth-mobile .sm-ustrip .sm-step .sm-sv { font-size: 12px; width: auto; min-width: 30px; }

/* 8) Warp knob + dropdown as one centered unit — the dropdown sits directly
   under the knob (opens upward). The remaining six Osc knobs (Vol Oct Semi
   Vox / Spr Glide) are a 4-column grid so the first four — the core
   oscillator-identity knobs — stay together on row one, rather than
   splitting Vox onto its own row with Spr and Glide. */
#synth-mobile .sm-knobrow-osc { grid-template-columns: repeat(4, minmax(0, 1fr)); }
/* Warp = a normal-size Osc knob (with its own "Warp" label, like the others)
   on the left, and its ‹ dropdown › to the right of the knob. */
#synth-mobile .sm-warpunit {
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: center;
  gap: 14px;
  margin: 10px auto 0;
  width: 100%;
}
#synth-mobile .sm-warpunit .sm-kcell { width: auto; flex: 0 0 auto; }
/* The warp knob's label is globally hidden (07-dropdowns.css) for the desktop
   layout; restore it on mobile so it matches every other knob. */
#synth-mobile .sm-warpunit [data-knob-id="warp"] .knob-label { display: block; }
#synth-mobile .sm-warpunit .sm-warprow {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 0;
  width: auto;
}
/* keep the ‹ dropdown › as a tight group so the › arrow hugs the box */
#synth-mobile .sm-warpunit .sm-ddwrap { flex: 0 0 auto; width: 132px; min-width: 0; }

/* =============================================================
   Round 6: typography standardization. One type scale (tokens at
   the top of this file) drives every text size, so the panel
   title, tab pills, knob labels and in-panel controls read as one
   consistent system. The dock (Hold/Scale/Oct/MOD·BEND), piano
   pads and knob dials are intentionally left as-is.
   ============================================================= */

/* Panel title: smaller + medium weight (was 13px/700, which rounded to a
   heavier face because 600/„bold" is not a loaded weight — it read as a
   different font). Now matches the rest of the mono UI. */
#synth-mobile .sm-panel::before,
#synth-mobile .sm-ptitle {
  font-size: var(--sm-fs-title);
  font-weight: var(--sm-fw-reg);
  letter-spacing: .02em;
}

/* Tab pills: bigger, so OSC/FILTER/… are legible (was 9px). */
#synth-mobile .sm-stab { font-size: var(--sm-fs-tab); }

/* Knob labels (VOL/OCT/…): bigger (was 11px). */
#synth-mobile .knob-label { font-size: var(--sm-fs-knob); }

/* In-panel controls unified: field labels (TYPE/Pattern/ADSR), segment
   buttons (Env 1/2, LFO 1/2) and panel pills (On/Out/Sync/Retrig). The dock
   controls use their own selectors and are deliberately not caught here. */
#synth-mobile .sm-lbl,
#synth-mobile .sm-seg > button,
#synth-mobile .sm-pbody .sm-pill,
#synth-mobile .sm-fx-head .sm-pill { font-size: var(--sm-fs-ctl); }

/* Effect row names (Spread/Saturate/…): part of the same scale, medium weight. */
#synth-mobile .sm-fx-name { font-size: var(--sm-fs-value); font-weight: var(--sm-fw-reg); }

/* =============================================================
   Round 8: transport chips (KBD/BPM/PB) restyled to match the
   dock OCT stepper exactly — white text, integer at the same
   size and colour as OCT's "3", no extra padding. OCT's own
   outer padding tightened. Knob labels shrunk to the KBD size
   (via --sm-fs-knob above).
   ============================================================= */

/* OCT stepper: less outer padding (space left of − and right of +). */
#synth-mobile .sm-octctl { padding: 8px 2px; gap: 4px; }
#synth-mobile .sm-octctl .sm-pm { padding: 0 5px; }

/* KBD / BPM / PB: identical to OCT 3 — label + integer both white and at the
   OCT "3" size, with the padding around the integer removed. */
#synth-mobile .sm-ustrip .sm-step { gap: 4px; padding: 0 3px; }
#synth-mobile .sm-ustrip .sm-step .sm-pm { padding: 0 5px; }
#synth-mobile .sm-ustrip .sm-step .sm-sl { color: var(--sm-t1); font-size: var(--sm-fs-micro); }
#synth-mobile .sm-ustrip .sm-step .sm-sv {
  color: var(--sm-t1);
  font-size: var(--sm-fs-micro);
  width: 3ch;
  min-width: 0;
  padding: 0;
}

/* =============================================================
   Round 9: FX rows get an expand chevron (they didn't read as
   clickable), knob labels pulled up closer to the dial.
   ============================================================= */

/* FX accordion: a disclosure chevron on the left of each row header, rotating
   down when the row is open — makes the row obviously tappable. */
#synth-mobile .sm-fx-head::before {
  content: '▸';
  flex: 0 0 auto;
  font-size: 10px;
  line-height: 1;
  color: var(--sm-t3);
  transition: transform .15s ease;
}
#synth-mobile .sm-fx-open > .sm-fx-head::before {
  transform: rotate(90deg);
  color: var(--sm-t1);
}

/* Knob labels sit ~10px too low under the dial (the knob element reserves a
   full square, leaving a gap under the arc). Pull them up toward the dial. */
#synth-mobile .sm-kcell .knob-label { margin-top: -10px; }

/* =============================================================
   Round 10: compact Osc panel (4 knobs/row, smaller dials, no
   redundant panel title) and folder-style tabs whose active tab
   connects to the content below (no dividing border under it).
   ============================================================= */

/* Panel titles removed — the tab bar already names the active section. The
   [data-tab] attribute is needed to out-specify the per-tab content:"…" rules. */
#synth-mobile .sm-panel[data-tab]::before { content: none; display: none; }
#synth-mobile .sm-ptitle { display: none; }

/* Osc: 4 smaller dials per row + tighter spacing, so knobs + warp + the
   KBD/BPM/PB transport row all fit without the transport clipping. */
#synth-mobile .sm-panel[data-tab="osc"] { --knob-md: 50px; }
#synth-mobile .sm-knobrow-osc {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 12px 8px;
  padding: 10px 8px 4px;
}
#synth-mobile .sm-knobrow-osc .sm-kcell { flex: 0 0 76px; width: 76px; }
#synth-mobile .sm-warpunit { margin-top: 6px; gap: 12px; }
#synth-mobile [data-tab="osc"] .sm-ustrip { margin-top: 6px; }

/* Folder-style tabs: they sit on a baseline; the active tab is framed on three
   sides and its bottom breaks the baseline so it reads as connected to the
   panel below it (no border separating the active tab from its content). */
#synth-mobile .sm-sectabs-mid {
  border-top: 0;
  border-bottom: 1px solid var(--sm-line-strong);
  align-items: flex-end;
  gap: 4px;
  padding: 9px 11px 0;
}
#synth-mobile .sm-sectabs-mid .sm-stab {
  background: transparent;
  color: var(--sm-t3);
  border: 1px solid transparent;
  border-bottom: 0;
  border-radius: var(--sm-r) var(--sm-r) 0 0;
  margin-bottom: -1px;
  padding: 9px 15px;
}
#synth-mobile .sm-sectabs-mid .sm-stab-on {
  background: var(--sm-screen);
  color: var(--sm-t1);
  border-color: var(--sm-line-strong);
  font-weight: 700;
}

/* Piano pads were shrunk earlier to make room for the (now removed) panel
   title — restore a comfortable, tappable pad height. */
#synth-mobile .sm-grid-keys { height: 96px; }

/* =============================================================
   Round 11: remove the ModMatrix's own heading, drop the active
   tab's underline so it connects to content, give MOD|BEND the
   MORPH button's glow, and size the Sub shape buttons to MORPH.
   (Wavetable/POS hero height +10px handled in the Round-3 rule.)
   ============================================================= */

/* ModMatrix: hide the component's own "MOD MATRIX" heading (the tab names the
   section already); keep the page nav, centered. */
#synth-mobile .mod-matrix-header-row { justify-content: center; }
#synth-mobile .mod-matrix-header-row h3 { display: none; }

/* Section tabs: no baseline under the active tab — its 3-sided frame connects
   straight into the content below. */
#synth-mobile .sm-sectabs-mid { border-bottom: 0; }
#synth-mobile .sm-sectabs-mid .sm-stab { margin-bottom: 0; }

/* MOD | BEND: the active segment reads like the MORPH button — same light fill
   and dark text (already), plus MORPH's glow. Let the glow escape the segment
   frame and the dock row so it isn't clipped. */
#synth-mobile .sm-kbd-tools { padding-top: 5px; padding-bottom: 5px; overflow: visible; }
#synth-mobile .sm-kbd-tools .sm-exprseg { overflow: visible; }
#synth-mobile .sm-expropt.active { box-shadow: 0 0 8px var(--text-secondary); }
#synth-mobile .sm-expropt[data-expr="mod"].active { border-radius: 3px 0 0 3px; }
#synth-mobile .sm-expropt[data-expr="bend"].active { border-radius: 0 3px 3px 0; }

/* Sub oscillator: shape buttons were oversized — size them to the MORPH button
   (same height, similar width) instead of stretching to fill the row. */
#synth-mobile .sm-subrow { justify-content: center; }
#synth-mobile .sm-wavesel { flex: 0 1 auto; gap: 6px; }
#synth-mobile .sm-wavesel .wf-btn { flex: 0 0 56px; height: 30px; }

/* =============================================================
   Round 12: baseline under the inactive section tabs only (not
   the active one), and the Osc "Spread / Glide / Warp + warp
   dropdown" controls consolidated onto a single row.
   ============================================================= */

/* Section tabs: bottom border under every inactive tab; the active tab has no
   bottom border so its 3-sided frame connects straight into the content. */
#synth-mobile .sm-sectabs-mid .sm-stab { border-bottom: 1px solid var(--sm-line-strong); }
#synth-mobile .sm-sectabs-mid .sm-stab-on { border-bottom-color: transparent; }

/* Osc row 2: Spread, Glide, Warp knobs + the warp dropdown, all on one line. */
#synth-mobile .sm-osc-row2 {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 4px 8px;
}
#synth-mobile .sm-osc-row2 .sm-kcell { flex: 0 0 62px; width: 62px; }
/* the warp knob's own label is globally hidden — restore it here like the others */
#synth-mobile .sm-osc-row2 [data-knob-id="warp"] .knob-label { display: block; }
#synth-mobile .sm-osc-row2 .sm-warprow {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 3px;            /* arrows hug the dropdown box */
  margin: 0;
  min-width: 0;
}
/* narrow dropdown so the ‹ › arrows sit right beside the box (not the wide edges) */
#synth-mobile .sm-osc-row2 .sm-ddwrap { flex: 0 0 100px; width: 100px; min-width: 0; }
#synth-mobile .sm-osc-row2 .sm-warprow .sm-pm { padding: 2px 2px; font-size: 15px; }

/* =============================================================
   Round 13: continuous tab baseline, Sub shapes in a 2x2 grid
   clearly separated from the Vol knob, more headroom above the
   KBD/BPM/PB row, and MORPH-style toggle chips for the Env/LFO
   page switches and the Arp Sync/On buttons.
   ============================================================= */

/* Continuous underline: zero gap between tabs so the inactive tabs' bottom
   borders join into one line, broken only under the (borderless) active tab. */
#synth-mobile .sm-sectabs-mid { gap: 0; }

/* Sub oscillator: shapes in a 2x2 grid, clearly separated (left) from the Vol
   knob (right) so they no longer overlap. */
#synth-mobile .sm-subrow { justify-content: center; gap: 26px; align-items: center; }
#synth-mobile .sm-wavesel {
  display: grid;
  grid-template-columns: repeat(2, 56px);
  gap: 6px;
  flex: 0 0 auto;
}
#synth-mobile .sm-wavesel .wf-btn { width: 56px; height: 32px; }
#synth-mobile .sm-subrow .sm-kcell { flex: 0 0 auto; }

/* More headroom above the KBD / BPM / PB transport row. */
#synth-mobile [data-tab="osc"] .sm-ustrip { margin-top: 20px; }

/* Env 1/2 and LFO 1/2 page switches: MORPH-style chips (light fill + glow when
   active) instead of a bordered segmented box. */
#synth-mobile .sm-seg { border: 0; gap: 4px; overflow: visible; background: transparent; }
#synth-mobile .sm-seg > button {
  background: var(--control-bg);
  border: none;
  border-radius: 2px;
  padding: 8px 13px;
  color: var(--sm-t2);
}
#synth-mobile .sm-seg > button.active {
  background: var(--sm-active);
  color: var(--sm-active-fg);
  font-weight: 700;
  box-shadow: 0 0 8px var(--text-secondary);
}

/* Arp: the On button (#arpToggle) had ID-level desktop styling that made it
   smaller than the Sync pill beside it. Match it to the Sync pill so both read
   as the same MORPH-style chip. */
#synth-mobile #arpToggle {
  font-size: var(--sm-fs-ctl);
  font-weight: 500;
  padding: 8px 13px;
  line-height: 1;
  border-radius: 2px;
}
#synth-mobile #arpToggle.active { box-shadow: 0 0 8px var(--text-secondary); }

/* =============================================================
   Round 14: taller hero, tighter file arrow, truly continuous
   tab baseline, filter Type-under-curve, MORPH-height seg
   buttons, per-effect colours, arp pattern under knobs, etc.
   ============================================================= */

/* Wavetable + POS. */
#synth-mobile .sm-herorow { height: clamp(120px, 16vh, 148px); }

/* The › arrow's padding used to be trimmed on its right to hug the file
   box's edge — but that made it a noticeably smaller tap target than ‹,
   which is what made it feel small and inconsistent. Matching padding on
   both now; the box has room. */

/* Section tabs: one CONTINUOUS baseline drawn as an inset shadow on the strip
   (no per-tab borders, so no dips at the tab joins). The active tab's own
   screen-coloured background covers the baseline under it, breaking the line
   only there. */
/* Nine tabs in a single scrolling row hid Sub/Noise/Mod/FX/Arp behind a
   scroll a visitor had no reason to try — a 5-column grid instead wraps
   them into two full rows (5 + 4) with everything visible up front. */
#synth-mobile .sm-sectabs-mid {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 4px;
  overflow-x: visible;
  -webkit-mask-image: none;
  mask-image: none;
  box-shadow: inset 0 -1px 0 0 var(--sm-line-strong);
}
#synth-mobile .sm-sectabs-mid .sm-stab {
  border-bottom: 0;
  min-width: 0;
  text-align: center;
}
#synth-mobile .sm-sectabs-mid .sm-stab-on { border-bottom: 0; background: var(--sm-screen); }

/* Osc warp: arrows hug the dropdown even tighter; keep it vertically centred. */
#synth-mobile .sm-osc-row2 { align-items: center; }
#synth-mobile .sm-osc-row2 .sm-warprow { gap: 2px; }
#synth-mobile .sm-osc-row2 .sm-ddwrap { flex: 0 0 94px; width: 94px; }

/* Filter: Type dropdown now sits under the curve; centre it. Curve +10px taller
   and a touch narrower. Field label matches the oscillator knob labels. */
#synth-mobile .sm-filtergraph { height: 82px; width: 88%; margin: 0 auto; }
#synth-mobile .sm-filter-type { justify-content: center; gap: 8px; }
#synth-mobile .sm-filter-type .sm-ddwrap { flex: 0 0 150px; }
#synth-mobile .sm-filter-type .sm-lbl {
  font-size: var(--sm-fs-knob);
  letter-spacing: 1px;
  color: rgba(255, 255, 255, .8);
}

/* Env 1/2 and LFO 1/2 page switches: same height and font size as the MOD|BEND
   segments. */
#synth-mobile .sm-seg > button {
  height: 34px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--sm-fs-micro);
}

/* Sub: +6px vertical gap between the 2x2 shape buttons. */
#synth-mobile .sm-wavesel { gap: 12px 6px; }

/* Arp: Pattern dropdown sits under the knobs, centred (was a spread-out row). */
#synth-mobile .sm-arppattern { justify-content: center; gap: 8px; margin-top: 10px; }
#synth-mobile .sm-arppattern .sm-lbl { flex: 0 0 auto; }
#synth-mobile .sm-arppattern .sm-ddwrap { flex: 0 0 160px; }

/* Dock: less horizontal padding around the OCT stepper group as a whole,
   but the −/+ buttons themselves need a real touch target — matched to the
   Hold button and scale selector beside them (padding: 8px ~10px) rather
   than the near-zero padding this had, which made them hard to tap
   accurately. The row already scrolls horizontally (.sm-kbd-tools,
   overflow-x: auto) so there's no fixed budget forcing these tiny. */
#synth-mobile .sm-octctl { padding: 8px 0; }
#synth-mobile .sm-octctl .sm-pm { padding: 8px 10px; }

/* Effects: per-effect colour accent — a left border in the effect's signature
   colour (from the web synth) and the knob arcs tinted to match. */
#synth-mobile .sm-fx[data-fx="str"]     { border-left: 3px solid var(--fx-str-color); }
#synth-mobile .sm-fx[data-fx="sat"]     { border-left: 3px solid var(--fx-sat-color); }
#synth-mobile .sm-fx[data-fx="geq"]     { border-left: 3px solid var(--fx-eq-color); }
#synth-mobile .sm-fx[data-fx="eq"]      { border-left: 3px solid var(--fx-eq-color); }
#synth-mobile .sm-fx[data-fx="comp"]    { border-left: 3px solid var(--fx-cmp-color); }
#synth-mobile .sm-fx[data-fx="chorus"]  { border-left: 3px solid var(--fx-cho-color); }
#synth-mobile .sm-fx[data-fx="flanger"] { border-left: 3px solid var(--fx-flg-color); }
#synth-mobile .sm-fx[data-fx="delay"]   { border-left: 3px solid var(--fx-dly-color); }
#synth-mobile .sm-fx[data-fx="reverb"]  { border-left: 3px solid var(--fx-rev-color); }
#synth-mobile .sm-fx[data-fx="fxfilter"]{ border-left: 3px solid var(--fx-fil-color); }
#synth-mobile .sm-fx[data-fx="str"]     .knob-arc-foreground { stroke: var(--fx-str-color) !important; }
#synth-mobile .sm-fx[data-fx="sat"]     .knob-arc-foreground { stroke: var(--fx-sat-color) !important; }
#synth-mobile .sm-fx[data-fx="geq"]     .knob-arc-foreground { stroke: var(--fx-eq-color) !important; }
#synth-mobile .sm-fx[data-fx="eq"]      .knob-arc-foreground { stroke: var(--fx-eq-color) !important; }
#synth-mobile .sm-fx[data-fx="comp"]    .knob-arc-foreground { stroke: var(--fx-cmp-color) !important; }
#synth-mobile .sm-fx[data-fx="chorus"]  .knob-arc-foreground { stroke: var(--fx-cho-color) !important; }
#synth-mobile .sm-fx[data-fx="flanger"] .knob-arc-foreground { stroke: var(--fx-flg-color) !important; }
#synth-mobile .sm-fx[data-fx="delay"]   .knob-arc-foreground { stroke: var(--fx-dly-color) !important; }
#synth-mobile .sm-fx[data-fx="reverb"]  .knob-arc-foreground { stroke: var(--fx-rev-color) !important; }
#synth-mobile .sm-fx[data-fx="fxfilter"].knob-arc-foreground { stroke: var(--fx-fil-color) !important; }

/* =============================================================
   Round 15: bigger Osc knobs + dropdowns, filter Type/Cut/Res on
   one row, LFO reflow, power-icon toggles, centred On/Out, per-
   panel graph proportions, mono FX titles.
   ============================================================= */

/* Osc knobs back to the standard size used by the other sections. */
#synth-mobile .sm-panel[data-tab="osc"] { --knob-md: 62px; }
#synth-mobile .sm-knobrow-osc .sm-kcell { flex: 0 1 84px; width: auto; max-width: 84px; min-width: 0; }
#synth-mobile .sm-osc-row2 .sm-kcell { flex: 0 0 72px; width: 72px; }
/* Vol/Oct/Semi/Vox must stay one row — at 84px + the row's gap they fit a
   390px-wide phone with room to spare, but nowrap makes that explicit
   instead of leaving it to width math that a narrower device could tip
   into wrapping Vox onto its own row. */
#synth-mobile .sm-knobrow-osc { flex-wrap: nowrap; }

/* Dropdowns were too small — larger text + taller trigger everywhere. */
#synth-mobile .sm-ddwrap select { font-size: 13px; }
#synth-mobile .sm-ddwrap .custom-dropdown-button { min-height: 34px; padding: 6px 10px; }
#synth-mobile .sm-ddwrap .custom-dropdown-button-text { font-size: 13px; }

/* Filter: Type dropdown + Cut + Res on one row under the curve; curve taller
   and narrower. */
#synth-mobile .sm-filtergraph { height: 100px; width: 74%; }
#synth-mobile .sm-filter-row {
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  justify-content: center;
  gap: 16px;
  padding: 6px 8px;
}
#synth-mobile .sm-filter-row > * { flex: 0 0 auto; align-self: center; }
#synth-mobile .sm-filter-row .sm-filter-type {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 5px;
}
#synth-mobile .sm-filter-row .sm-filter-type .sm-ddwrap { width: 112px; flex: 0 0 auto; }
#synth-mobile .sm-filter-row .sm-kcell { width: auto; margin: 0; }

/* LFO: narrower + taller scope; the shape (Type) dropdown sits by the LFO 1/2
   selector; Rate/Atk/Ofs knobs with Sync/Retrig stacked to their right. */
#synth-mobile #lfoScopeCanvas,
#synth-mobile #lfo2ScopeCanvas { height: 100px; width: 78%; max-width: 78%; margin: 0 auto; }
#synth-mobile .sm-lfo-head { align-items: center; }
#synth-mobile .sm-lfo-head .sm-lfo-shape { flex: 0 0 132px; min-width: 0; }
#synth-mobile .sm-lfo-ctlrow { display: flex; align-items: center; justify-content: center; gap: 12px; }
/* the knobrow is a full-width grid by default — shrink it to its 3 knobs so the
   Sync/Retrig column fits beside it instead of being pushed off-screen. */
#synth-mobile .sm-lfo-ctlrow .sm-knobrow {
  width: auto;
  flex: 0 1 auto;
  grid-template-columns: repeat(3, 66px);
  padding: 8px 0;
}
#synth-mobile .sm-lfo-toggles { display: flex; flex-direction: column; gap: 6px; flex: 0 0 auto; }
#synth-mobile .sm-lfo-toggles .sm-pill { width: 100%; text-align: center; }

/* Sub / Noise: centre the Out + On buttons. */
#synth-mobile .sm-phead { justify-content: center; gap: 8px; }
#synth-mobile .sm-phead .sm-sp { display: none; }

/* Arp: centre the Sync + On buttons (first row of the panel body). */
#synth-mobile .sm-panel[data-tab="arp"] .sm-pbody > .sm-row:first-child { justify-content: center; gap: 8px; }
#synth-mobile .sm-panel[data-tab="arp"] .sm-pbody > .sm-row:first-child .sm-sp { display: none; }

/* Mod matrix: drop the border/line under the page nav. */
#synth-mobile .mod-matrix-scroll-container { border-top: 0; }
#synth-mobile .mod-matrix-header-row { border-bottom: 0; }

/* Graphic EQ graph was squished — narrower (and a touch taller) to restore
   sensible proportions. */
#synth-mobile #geqGraphCanvas { width: 80% !important; max-width: 80% !important; height: 150px !important; margin: 0 auto; display: block; }

/* FX row titles in the app's mono font. */
#synth-mobile .sm-fx-name { font-family: var(--sm-mono); }

/* Power-icon toggles: the On button on FX rows and Sub/Noise/Arp becomes a
   power glyph (its colour follows the on/off state). */
#synth-mobile .sm-fx-head .fx-toggle,
#synth-mobile #subToggle,
#synth-mobile #noiseToggle,
#synth-mobile #arpToggle {
  font-size: 0;
  padding: 8px 12px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
#synth-mobile .sm-fx-head .fx-toggle::before,
#synth-mobile #subToggle::before,
#synth-mobile #noiseToggle::before,
#synth-mobile #arpToggle::before {
  content: "";
  display: inline-block;
  width: 15px;
  height: 15px;
  background: currentColor;
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round'><path d='M12 3.4v8'/><path d='M7.4 6.6a6.6 6.6 0 1 0 9.2 0'/></svg>") no-repeat center / contain;
  mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round'><path d='M12 3.4v8'/><path d='M7.4 6.6a6.6 6.6 0 1 0 9.2 0'/></svg>") no-repeat center / contain;
}

/* =============================================================
   Round 16: audio-not-running "Loading" state for the note grid.
   Until the AudioContext resumes (first tap), the keys look
   deactivated and a "Loading" overlay sits over them.
   ============================================================= */
#synth-mobile.sm-audio-loading .sm-grid-keys {
  opacity: .35;
  filter: grayscale(1);
}
#synth-mobile.sm-audio-loading .sm-gridwrap::after {
  content: "Tap to start audio";
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  font-family: var(--sm-mono);
  font-size: 12px;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--sm-t1);
  background: rgba(6, 6, 8, .45);
  pointer-events: none;
  z-index: 3;
}

/* The note-grid overlay above sits below the fold on first open (visualizer +
   knobs render above it, looking fully functional), so a visitor who doesn't
   scroll down and tap a key never sees any cue that a tap is required — this
   is what reads as the synth being permanently stuck "loading". Repeat the
   same call-to-action as a banner pinned to the top of the viewport so it's
   visible immediately, with no scrolling. #synth-mobile is a column flex
   container, so this stays in normal flow (not position: absolute) and pushes
   the wavetable row down rather than overlapping it. `pointer-events: none`
   keeps every control beneath it tappable, since any tap anywhere in
   #synth-mobile resumes the AudioContext (see resumeAudio() in
   mobile-shell.js). */
#synth-mobile.sm-audio-loading::before {
  content: "Tap to start audio";
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 8px 0;
  font-family: var(--sm-mono);
  font-size: 11px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--sm-active-fg);
  background: var(--sm-active);
  pointer-events: none;
}

/* =============================================================
   Round 17: native OS dropdowns (custom widget skipped on mobile
   in custom-dropdown.js) — style the native <select> boxes with a
   slightly smaller closed-state font. Env graph sized like the LFO
   scope; Graphic EQ narrowed further.
   ============================================================= */
#synth-mobile .sm-ddwrap select {
  width: 100%;
  font-family: var(--sm-mono);
  font-size: 12px;
  color: var(--sm-t1);
  background: var(--control-bg);
  border: 1px solid var(--sm-line-strong);
  border-radius: 2px;
  padding: 8px 10px;
}

/* Env graph: same height/width as the LFO scope. */
#synth-mobile .sm-envgraph { height: 100px; width: 78%; max-width: 78%; margin: 0 auto; }

/* Graphic EQ graph: 15px narrower still. */
#synth-mobile #geqGraphCanvas { width: calc(80% - 15px) !important; }

/* =============================================================
   Round 18: tablet / large screens — the wavetable grows with the
   viewport, and the panel area shows a contiguous group of panels
   (2 or 3) side by side instead of a single one, driven by the
   .sm-cols-2 / .sm-cols-3 class set in MobileShell.
   ============================================================= */

/* Wavetable + POS grow with the viewport on larger screens (kept shorter). */
@media (min-width: 720px) {
  #synth-mobile .sm-herorow { height: clamp(132px, 20vh, 224px); }
}
@media (min-width: 1080px) {
  #synth-mobile .sm-herorow { height: clamp(156px, 23vh, 288px); }
}

/* Multi-column panel area. */
#synth-mobile.sm-cols-2 .sm-pscroll,
#synth-mobile.sm-cols-3 .sm-pscroll {
  display: flex;
  flex-direction: row;
  overflow: hidden;
}
#synth-mobile.sm-cols-2 .sm-panel.sm-panel-active,
#synth-mobile.sm-cols-3 .sm-panel.sm-panel-active {
  flex: 1 1 0;
  min-width: 0;
  min-height: 0;
  height: 100%;
  overflow-y: auto;
  border-left: 1px solid var(--sm-line);
}
/* no divider before the leftmost visible column */
#synth-mobile.sm-cols-2 .sm-panel-active.sm-panel-lead,
#synth-mobile.sm-cols-3 .sm-panel-active.sm-panel-lead { border-left: 0; }
