/* =====================================================================
   SKIN TOKENS (block 10). House rule: EVERY color, face, shadow,
   radius and font lives in this block; the rest of the sheet only uses var(--…)
   and color-mix() on them, and the canvases read them through js/ui/theme.js.
   Outside of here only rgba(0,0,0,·) and rgba(255,255,255,·) are allowed: they are
   shadow and highlight (the physics of relief), not color. The selftest («skin»)
   watches over it in style.css, js/ui and index.html: nothing may be hard-coded.
   A theme = another :root[data-theme="…"] block that redefines whatever it wants;
   it goes on :root (html) and not on body because the composite tokens (faces,
   thumb, keys) resolve their var(--accent) where they are declared.
   ===================================================================== */
:root{
  --theme-id:"cinescapes"; --theme-name:"Carbon";   /* the base block IS the default theme (theme.js lists it); the id is the usual one: the remembered choice isn't lost */
  /* surfaces, from the outside in: flat carbon, no halo */
  --bg:#15171c;          /* page */
  --bg-glow:var(--bg);   /* radial halo at the top of the background (none here: flat) */
  --panel:#22252d; --panel2:var(--panel);   /* module face (flat) */
  --well:#1a1d23;        /* well: inputs, selects, flat buttons, kbd, tooltip */
  --screen:#0f1114;      /* glass of the visualizers and bed of the keyboard */
  --edge:#2f333c;        /* borders */
  --edge-hi:#454a56;     /* lighter relief: scroll on hover, unlit ★, inert indicator */
  --line:#121418;        /* dark separators: knob border, between buttons, LED */
  /* ink */
  --ink:#dfe3ea; --dim:#8b919d;
  --ink-on-accent:#06212b; --ink-on-accent2:#1a0b33;   /* text on accent */
  /* accents: cyan = rack, violet = oscillators, red = alarm, amber = ★ */
  --accent:#3fc6f2; --accent2:#a78bfa; --hot:#ff6b7a; --star:#ffd166;
  /* knob (knobs.js only publishes --pos 0..1). The BOX always measures 44 px (it is
     the footprint on the grid and the drag area); inside, the physical disc
     shrinks by --knob-disc-inset so the value arc stays apart from it
     without leaving the box (in a row of 5 knobs there is no room outside). */
  --knob:#262a33; --knobhi:#474d5a;
  --knob-face:radial-gradient(circle at 50% 26%,var(--knobhi),var(--knob) 66%);   /* the light falls from above */
  --knob-disc-inset:4px;                   /* air between the box and the disc (0px = the disc is the box) */
  --knob-edge-w:2px;                       /* bevel thickness */
  --knob-bevel:linear-gradient(180deg,color-mix(in srgb,white 28%,var(--knob)),color-mix(in srgb,black 55%,var(--knob)));   /* bevel: highlight on top, shadow below */
  --knob-ring:color-mix(in srgb,white 5%,transparent); --knob-ring-r:11px;   /* notch: 1px ring at that radius */
  --knob-shadow:0 5px 10px rgba(0,0,0,.5);
  --knob-line:0px; --knob-line-top:5px;    /* tick: length (0px = no tick) and start from the box */
  --knob-dot:5px; --knob-dot-top:9px;      /* dot: diameter (0px = no dot) and start from the box */
  --knob-arc:2px; --knob-arc-gap:2px;      /* value arc: thickness (0px = no arc) and air to the disc (4 = 2 + 2: right on the edge of the box) */
  --knob-arc-track:rgba(0,0,0,.35);        /* arc track (the 270° still to travel) */
  /* slider (ADSR): flat track that fills up to the thumb (sliders.js publishes --pos) */
  --track-face:var(--well); --track-fill:var(--accent);
  /* keyboard: ivory, ebony and felt */
  --key-w:#e9ebef; --key-w-edge:#b4b9c2; --key-w-ink:#6b7280;
  --key-b:#1b1e24; --key-b-edge:#0b0d10; --key-b-ink:#8b919d;
  --felt:#27505e;
  /* composite faces (flat here; Classic makes them gradients) */
  --panel-face:var(--panel);
  --btn-face:var(--well);
  --btn-face-hi:color-mix(in srgb,var(--well) 84%,white);
  --seg-on:var(--accent); --seg-on2:var(--accent2);
  --thumb-face:radial-gradient(circle at 40% 35%,color-mix(in srgb,var(--accent) 55%,white),var(--accent) 65%);
  --key-w-face:var(--key-w);
  --key-w-on:color-mix(in srgb,var(--accent) 45%,var(--key-w));
  --key-b-face:var(--key-b);
  --key-b-on:color-mix(in srgb,var(--accent2) 55%,var(--key-b));
  --shadow-panel:none;
  --shadow-pop:0 10px 30px rgba(0,0,0,.6);
  /* shape and font: pills, soft corners, lowercase labels */
  --r-panel:14px; --r-ctl:8px; --r-btn:999px;
  --font:13px/1.4 "Segoe UI",system-ui,sans-serif;
  --lab-case:none; --lab-space:0;            /* control labels */
  --title-case:none; --title-space:.3px;     /* tabs, preset bar */
  --sec-case:uppercase; --sec-space:1.6px;   /* panel section title: screen printing */
  color-scheme:dark;   /* native widgets (popups, unstyled scrollbars) in dark */
}

/* «Classic» THEME: the original SubSDJ look (green and cyan, gradients,
   relief, spaced CAPITALS). It is also the proof that the tokens
   cover everything: if something can't be brought back from here, it is hard-coded. */
:root[data-theme="clasico"]{
  --theme-name:"Classic";
  --bg:#0d1014; --bg-glow:#1a2230; --panel:#161b22; --panel2:#1b222c; --well:#0f141a; --screen:#05080b;
  --edge:#2a3340; --edge-hi:#3d4a5c; --line:#0a0e12;
  --ink:#c9d4e0; --dim:#7d8a99; --ink-on-accent:#04140e; --ink-on-accent2:#04121a;
  --accent:#39d4a3; --accent2:#34c0ff; --hot:#ff5d73; --star:#ffd166;
  --knob:#222b36; --knobhi:#3a4a5c;
  --knob-face:radial-gradient(circle at 38% 32%,var(--knobhi),var(--knob) 70%);
  --knob-disc-inset:0px; --knob-edge-w:1px; --knob-bevel:linear-gradient(var(--line),var(--line));
  --knob-ring:var(--line); --knob-ring-r:15px;
  --knob-shadow:inset 0 2px 4px rgba(255,255,255,.06),0 3px 6px rgba(0,0,0,.5);
  --knob-line:14px; --knob-line-top:5px; --knob-dot:0px; --knob-dot-top:7px;
  --knob-arc:0px; --knob-arc-gap:2px; --knob-arc-track:transparent;
  --track-face:linear-gradient(180deg,var(--screen),var(--well)); --track-fill:transparent;
  --key-w:#fdfefe; --key-w-edge:#98a1ab; --key-w-ink:#6b7683;
  --key-b:#11151a; --key-b-edge:#05070a; --key-b-ink:#8f9aa7;
  --felt:#8e2740;
  --panel-face:linear-gradient(180deg,var(--panel),var(--panel2));
  --btn-face:linear-gradient(180deg,var(--panel2),var(--well) 85%);
  --btn-face-hi:linear-gradient(180deg,color-mix(in srgb,var(--panel2) 90%,white),color-mix(in srgb,var(--well) 92%,white) 85%);
  --seg-on:linear-gradient(180deg,color-mix(in srgb,var(--accent) 70%,black),var(--accent) 60%,color-mix(in srgb,var(--accent) 75%,white));
  --seg-on2:linear-gradient(180deg,color-mix(in srgb,var(--accent2) 70%,black),var(--accent2) 60%,color-mix(in srgb,var(--accent2) 75%,white));
  --thumb-face:radial-gradient(circle at 35% 28%,color-mix(in srgb,var(--accent) 25%,white),color-mix(in srgb,var(--accent) 70%,white) 40%,var(--accent) 62%,color-mix(in srgb,var(--accent) 55%,black) 100%);
  --key-w-face:linear-gradient(180deg,color-mix(in srgb,var(--key-w) 46%,var(--key-w-edge)) 0%,color-mix(in srgb,var(--key-w) 91%,var(--key-w-edge)) 12%,var(--key-w) 58%,color-mix(in srgb,var(--key-w) 80%,var(--key-w-edge)) 88%,color-mix(in srgb,var(--key-w) 49%,var(--key-w-edge)) 100%);
  --key-w-on:linear-gradient(180deg,color-mix(in srgb,var(--accent) 25%,var(--key-w-edge)) 0%,color-mix(in srgb,var(--accent) 45%,var(--key-w)) 28%,color-mix(in srgb,var(--accent) 75%,var(--key-w)) 72%,var(--accent) 100%);
  --key-b-face:linear-gradient(180deg,color-mix(in srgb,var(--key-b) 78%,white) 0%,color-mix(in srgb,var(--key-b) 93%,white) 10%,var(--key-b) 70%,color-mix(in srgb,var(--key-b) 86%,white) 78%,color-mix(in srgb,var(--key-b) 97%,white) 96%);
  --key-b-on:linear-gradient(180deg,color-mix(in srgb,var(--accent2) 20%,var(--key-b)) 0%,color-mix(in srgb,var(--accent2) 15%,var(--key-b)) 55%,color-mix(in srgb,var(--accent2) 75%,var(--key-b)) 76%,var(--accent2) 100%);
  --shadow-panel:0 6px 18px rgba(0,0,0,.35); --shadow-pop:0 10px 30px rgba(0,0,0,.6);
  --r-panel:10px; --r-ctl:6px; --r-btn:6px;
  --font:13px/1.4 "Segoe UI",system-ui,sans-serif;
  --lab-case:uppercase; --lab-space:.5px; --title-case:uppercase; --title-space:1.5px;
}

/* Thin matching scrollbars (window, panels and dropdowns) */
::-webkit-scrollbar{width:10px;height:10px;}
::-webkit-scrollbar-track{background:transparent;}
::-webkit-scrollbar-thumb{background:var(--edge);border:3px solid transparent;background-clip:padding-box;border-radius:8px;}
::-webkit-scrollbar-thumb:hover{background:var(--edge-hi);border-width:2px;background-clip:padding-box;}
::-webkit-scrollbar-corner{background:transparent;}

/* Dropdowns with a stylable picker (Chrome 135+): the popup becomes ordinary
   DOM — matching dark theme and the thin scrollbar above. In
   browsers without ::picker() the whole rule is dropped and the native one stays. */
select, select::picker(select){appearance:base-select;}
select::picker(select){background:var(--well);border:1px solid var(--edge);border-radius:8px;
  box-shadow:0 12px 28px rgba(0,0,0,.55);padding:4px;
  scrollbar-width:thin;scrollbar-color:var(--edge-hi) transparent;}   /* ::-webkit-scrollbar doesn't chain after ::picker() */
select option{padding:5px 8px;border-radius:5px;font-size:12px;}
select option:hover,select option:focus{background:var(--panel2);}
select option:checked{background:color-mix(in srgb,var(--accent) 14%,transparent);color:var(--accent);}
select optgroup{padding:2px;color:var(--dim);font-size:10px;text-transform:uppercase;letter-spacing:1px;}
select optgroup option{color:var(--ink);font-size:12px;text-transform:none;letter-spacing:0;}
*{box-sizing:border-box;}
/* THE INSTRUMENT (ui/frame.js): #inst ALWAYS measures 1920 × 1000 and is scaled
   as a whole with zoom to fit the window; nothing reflows. html.frame-scroll =
   the window is so small that the zoom hit the floor (MIN_ZOOM): then there is
   scrolling. margin:auto (not justify-content) centers without clipping on overflow. */
html{margin:0;height:100%;}
body{margin:0;height:100%;overflow:hidden;display:flex;}
html.frame-scroll body{overflow:auto;}
body{
  background:radial-gradient(1200px 600px at 50% -10%, var(--bg-glow), var(--bg));
  color:var(--ink); font:var(--font);
  -webkit-user-select:none; user-select:none;
}
#inst{width:1920px;height:1000px;flex:none;margin:auto;padding:10px;display:flex;flex-direction:column;gap:8px;}
/* No canvas contributes a size of its own: ONLY the CSS sets it. Without this, a canvas
   in a box sized to its content takes its backing store (width/height,
   which fitCanvas sets to size × dpr × zoom) as its natural width: every redraw
   enlarged it by another ×dpr×zoom (1.95 on a 4K at 150 %) until the canvas
   blew up. contain:size = it is laid out as if it were empty. */
#inst canvas{contain:size;}
/* Logo: accent→accent2 gradient clipped to the text (background-clip:text)
   that flows slowly (the background is twice as wide and shifts one lap, back
   and forth) and a halo via drop-shadow: a text-shadow would paint ON TOP
   of the clipped gradient, the filter acts on the result. Everything comes from
   the tokens: the logo changes with the theme. Deliberately WITHOUT
   prefers-reduced-motion: Windows with «Animation effects» turned off
   enables it and the browser would leave the logo still; it is a 10 s sway. */
h1{font-size:18px;margin:0;letter-spacing:2px;
  background:linear-gradient(100deg,var(--accent),var(--accent2),var(--accent)) 0 0 / 200% 100%;
  -webkit-background-clip:text;background-clip:text;color:transparent;
  filter:drop-shadow(0 0 5px color-mix(in srgb,var(--accent) 40%,transparent));
  animation:logoFlow 10s ease-in-out infinite;}
@keyframes logoFlow{50%{background-position:100% 0;filter:drop-shadow(0 0 9px color-mix(in srgb,var(--accent2) 55%,transparent));}}
/* Header: a single fixed row — title, tabs, presets (with «File ▾»), buttons, theme and status */
header{position:relative;z-index:6;flex:none;height:46px;display:flex;align-items:center;gap:10px;padding:0 12px;
  background:var(--panel-face);border:1px solid var(--edge);border-radius:var(--r-panel);box-shadow:var(--shadow-panel);}
header .sp{flex:1;}
.badge{font-size:11px;color:var(--dim);}
/* Status (audio · MIDI): two SVG icons with currentColor; the class sets the color (ui/status.js) */
#status{display:inline-flex;align-items:center;gap:10px;}
#status .st{display:inline-flex;color:var(--dim);cursor:help;transition:color .2s;}
#status .st svg{display:block;width:15px;height:15px;stroke:currentColor;fill:none;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round;}
#status .st svg .pin{fill:currentColor;stroke:none;}
#status .st.wait{color:var(--accent);animation:stWait 2.4s ease-in-out infinite;}   /* «your turn»: pulses slowly */
#status .st.on{color:var(--accent);filter:drop-shadow(0 0 4px color-mix(in srgb,var(--accent) 60%,transparent));}
#status .st.err{color:var(--hot);}
@keyframes stWait{50%{opacity:.3;}}
/* Buttons, selects and single-line inputs: same font (the theme's; the
   controls don't inherit it on their own) and a FIXED line-height, so they all measure
   the same (16 + 2·5 + 2 = 28 px). With line-height «normal» each one came out of
   its font: the button 26 px, the select 29 px. */
.btn{background:var(--well);border:1px solid var(--edge);color:var(--ink);border-radius:var(--r-btn);
  padding:5px 12px;font-family:inherit;font-size:12px;line-height:16px;cursor:pointer;letter-spacing:.5px;}
.btn:hover{border-color:var(--accent);color:var(--accent);}
.btn:active{transform:translateY(1px);}
.btn.rec{color:var(--hot);border-color:var(--hot);}
.btn.nav{padding:5px 10px;font-size:15px;font-weight:700;}
.btn.ico{padding:7px 11px;line-height:0;}   /* icon button (Wave Editor): the SVG measures what a 12 px line of text does */
.row.we-bar{flex-wrap:nowrap;}   /* the Wave Editor, narrow since the String module came in: the bar on one row… */
.we-bar .btn.ico{padding:7px 6px;min-width:0;flex:0 1 auto;}   /* …also at zoom < 1, where pixel rounding broke it: the icons give up 1 px */
.btn.ico svg{display:block;width:17px;height:12px;stroke:currentColor;fill:none;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round;}
/* Selects: a single well for all of them, at the height of a .btn (same padding and
   font size). Each select used to carry its own (3-4 px, 11 px) and came out
   shorter than the buttons beside it; and with appearance:base-select the
   browser adds no frame of its own: the well you see is EXACTLY this one.
   The matrix ones (three per row) are squeezed a little, further down. */
select{background:var(--well);border:1px solid var(--edge);color:var(--ink);border-radius:var(--r-ctl);
  padding:5px 8px;font-family:inherit;font-size:12px;line-height:16px;min-width:0;cursor:pointer;}
select:hover{border-color:var(--accent);color:var(--accent);}
#themeSel{border-radius:var(--r-btn);padding:5px 12px;letter-spacing:.5px;}   /* in the header it sits between .btn: same pill */

/* Parts: button + 1-4 selector in the header; Part Mixer; «room» tag */
#partsBar{display:flex;align-items:center;gap:8px;}
#partSel{display:inline-flex;}
#partSel[hidden]{display:none;}
#partSel button{padding:5px 11px;font-size:12px;}
.panel[hidden]{display:none;}
/* Parts modules with Parts off: in place, dimmed (they aren't
   hidden: the position doesn't change). The empty Part Mixer says what to do. */
.panel.parts-off > :not(h2){opacity:.32;pointer-events:none;filter:saturate(.4);transition:opacity .35s ease,filter .35s ease;}
.panel.parts-off h2{opacity:.6;transition:opacity .35s ease;}
#mixer > :not(h2),#harmony > :not(h2),#player > :not(h2),#arrange > :not(h2){transition:opacity .35s ease,filter .35s ease;}   /* and back on, just as softly */
#mixStrips:empty::before{content:"Turn on «Parts» in the header: four instruments over a shared room.";
  font-size:11px;color:var(--dim);}
.mix{display:flex;gap:10px;flex-wrap:nowrap;align-items:stretch;}
.strip{flex:1 1 200px;min-width:0;background:rgba(0,0,0,.18);border:1px solid var(--edge);border-radius:8px;
  padding:6px 8px;display:flex;flex-direction:column;gap:4px;}   /* tight: with the four parts, the fixed panel has no room for more */
.strip.cur{border-color:var(--accent);box-shadow:inset 0 0 0 1px var(--accent);}
.strip .row{transition:opacity .3s ease;}
.strip.muted .row{opacity:.45;}
.strip .pname{background:var(--well);border:1px solid var(--edge);color:var(--ink);border-radius:var(--r-btn);padding:5px 8px;
  font-size:11px;cursor:pointer;text-align:left;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.strip .pname:hover{border-color:var(--accent);color:var(--accent);}
.strip.cur .pname{color:var(--accent);}
.strip .pname.static{cursor:default;color:var(--accent);text-transform:var(--title-case);letter-spacing:var(--title-space);text-align:center;}
.strip.room{flex:0 1 200px;}
.strip .shead{display:flex;gap:6px;align-items:center;}   /* name + M on one row */
.strip .shead .pname{flex:1;min-width:0;}
.strip .mute{flex:none;padding:2px 9px;font-size:10px;font-weight:700;background:var(--well);
  border:1px solid var(--edge);color:var(--dim);border-radius:5px;cursor:pointer;}
.strip .mute.on{color:var(--hot);border-color:var(--hot);}
.chk{font-size:11px;color:var(--dim);display:flex;align-items:center;gap:6px;margin-top:auto;cursor:pointer;}
.tag-room{font-size:9px;letter-spacing:1px;text-transform:uppercase;color:var(--ink-on-accent);background:var(--accent);
  border-radius:4px;padding:1px 6px;margin-left:8px;font-weight:600;}
/* Harmony / Player */
#harmony input{background:var(--well);border:1px solid var(--edge);color:var(--ink);border-radius:var(--r-ctl);
  padding:5px 8px;font-family:inherit;font-size:12px;line-height:16px;min-width:0;}
#harmony select{flex:1;}
#harmProg{width:110px;}
#harmSeed{width:76px;}
.harmlay{gap:18px;align-items:flex-start;}
.harmlay .vcol{gap:7px;min-width:200px;}
#harmRoot{width:70px;flex:none;}
#playBtn.on{color:var(--hot);border-color:var(--hot);}
/* The part's arrangement (in the Arrangement panel): compact row like the footers of the Matrix and the Delay */
.arr-part{gap:10px;margin-top:6px;align-items:center;}
.arr-part .fieldlab{flex:none;color:var(--accent);}   /* whose row it is */
#playerArr{gap:10px;align-items:center;}
#playerArr .ks .th{width:auto;}
#playerArr .ks .val{width:30px;}
#playerArr select{font-size:10px;padding:2px 4px;max-width:96px;}
/* Presets in the header: ‹ picker › name Save «File ▾» */
#presetBar{display:flex;align-items:center;gap:8px;}
#presetBar input{background:var(--well);border:1px solid var(--edge);color:var(--ink);width:150px;
  border-radius:var(--r-ctl);padding:5px 8px;font-family:inherit;font-size:12px;line-height:16px;}
/* Quick preset picker (ui/presetPicker.js): button with the current one (same well as a select)
   and, when open, search box + categories on the left + list on the right */
#presetPick{width:230px;display:flex;align-items:center;gap:6px;background:var(--well);border:1px solid var(--edge);color:var(--ink);
  border-radius:var(--r-ctl);padding:5px 8px;font-family:inherit;font-size:12px;line-height:16px;cursor:pointer;text-align:left;}
#presetPickName{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
#presetPick::after{content:"▾";flex:none;color:var(--dim);font-size:10px;}
#presetPick:hover,#presetPick.on{border-color:var(--accent);color:var(--accent);}
.ppop{position:absolute;top:calc(100% + 6px);left:0;z-index:30;width:560px;display:flex;flex-direction:column;
  background:var(--well);border:1px solid var(--edge);border-radius:var(--r-ctl);box-shadow:var(--shadow-pop);}
.ppop[hidden]{display:none;}
.pp-top{display:flex;align-items:center;gap:10px;padding:8px;border-bottom:1px solid var(--edge);}
#presetBar #ppSearch{flex:1;width:auto;}
.pp-count{flex:none;min-width:70px;text-align:right;color:var(--dim);font-size:11px;font-variant-numeric:tabular-nums;}
.pp-main{display:flex;height:476px;}   /* the ~19 categories fit without scrolling */
.pp-cats,.pp-list{overflow-y:auto;padding:4px;scrollbar-width:thin;scrollbar-color:var(--edge-hi) transparent;}
.pp-cats{width:172px;flex:none;border-right:1px solid var(--edge);}
.pp-list{flex:1;min-width:0;}
.pp-cat,.pp-item{display:flex;align-items:center;gap:6px;padding:4px 8px;border-radius:5px;font-size:12px;line-height:16px;cursor:pointer;}
.pp-cat:hover,.pp-item:hover{background:var(--panel2);}
.pp-cat.on,.pp-item.cur{background:color-mix(in srgb,var(--accent) 14%,transparent);color:var(--accent);}
.pp-cat.empty{opacity:.4;}
.pp-cname,.pp-name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.pp-cn,.pp-tag{flex:none;color:var(--dim);font-size:10px;font-variant-numeric:tabular-nums;}
.pp-item.hi{box-shadow:inset 0 0 0 1px var(--edge-hi);}              /* the ↑ ↓ cursor */
.pp-sec{padding:9px 8px 3px;color:var(--dim);font-size:10px;text-transform:var(--lab-case);letter-spacing:var(--lab-space);}
.pp-sec:first-child{padding-top:3px;}
.pp-star{flex:none;color:var(--edge-hi);font-size:11px;}
.pp-star:hover{color:var(--star);}
.pp-star.on{color:var(--star);}
.pp-none{padding:16px 8px;color:var(--dim);font-size:12px;}
.pp-foot{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:6px 8px;border-top:1px solid var(--edge);}
.pp-keys{color:var(--dim);font-size:10px;}
/* ● modified (ui/history.js): what is sounding is no longer the saved preset. Off, it is barely visible */
#modDot{font-size:11px;line-height:1;color:var(--edge);margin:0 -4px;transition:color .15s;cursor:default;}
#modDot.on{color:var(--accent2);}
/* undo · redo · compare */
#editBar{display:flex;align-items:center;gap:4px;margin-left:4px;}
.btn:disabled{opacity:.35;cursor:default;pointer-events:none;}
.menuWrap{position:relative;}
.menu{position:absolute;top:calc(100% + 6px);left:0;z-index:20;display:flex;flex-direction:column;gap:4px;padding:6px;min-width:190px;
  background:var(--well);border:1px solid var(--edge);border-radius:var(--r-ctl);box-shadow:var(--shadow-pop);}
.menu[hidden]{display:none;}
.menu .btn{text-align:left;border-radius:var(--r-ctl);}
/* Bank notices (saved, imported…): they float under the header and fade out by themselves (ui/display.js) */
#presetMsg{position:absolute;left:50%;top:calc(100% + 6px);transform:translateX(-50%);z-index:20;pointer-events:none;
  font-size:11px;color:var(--ink);background:var(--well);border:1px solid var(--edge);border-radius:var(--r-btn);
  padding:4px 12px;white-space:nowrap;box-shadow:var(--shadow-pop);opacity:0;transition:opacity .3s;}
#presetMsg.show{opacity:1;}

/* SCREENS AND BANDS. Each tab is a .screen with its fixed panel: fixed-height
   bands (.band) and sections (.panel) separated by a line, like the
   screen printing of a front panel. Sections measure what their content does (flex
   auto) and .grow shares out the leftover: so there are no stretched empty cells.
   The shared modules (data-slot) hang from a .slot with display:
   contents: for the band they are one more section. */
#screens{flex:none;height:744px;display:flex;}   /* the body measures the same on every screen; the keyboard keeps the rest */
.screen{flex:1;min-width:0;display:flex;flex-direction:column;gap:8px;}
.screen[hidden]{display:none;}
.band{flex:none;display:flex;min-width:0;background:var(--panel-face);border:1px solid var(--edge);
  border-radius:var(--r-panel);box-shadow:var(--shadow-panel);overflow:hidden;}
#bandSource{height:238px;} #bandVoice{height:236px;} #bandModFx{height:254px;}
#bandSeq{height:262px;} #bandRoom{height:236px;} #bandScene{height:230px;}
.band-full{flex:1;min-height:0;}
.slot{display:contents;}
.panel{flex:0 0 auto;min-width:0;display:flex;flex-direction:column;padding:8px 10px;border-left:1px solid var(--edge);}
.band > .panel:first-child,.band > .slot:first-child > .panel{border-left:none;}
.panel.grow{flex:1 1 auto;}
.panel h2{font-size:10px;font-weight:600;text-transform:var(--sec-case);letter-spacing:var(--sec-space);color:var(--accent);
  margin:0 0 6px;white-space:nowrap;}
.panel.osc h2{color:var(--accent2);}
.g{display:grid;grid-template-columns:repeat(var(--c,3),auto);gap:6px 2px;justify-content:start;align-items:end;}
.hrow{display:flex;gap:2px;align-items:flex-end;}
.vcol{display:flex;flex-direction:column;gap:6px;align-items:center;}
.row{display:flex;flex-wrap:wrap;gap:12px;align-items:flex-start;}
.row.tight{gap:2px;}   /* generated knob rows (Part Mixer): the .ctl share the width */
.row.tight .ctl{flex:1 1 0;width:auto;min-width:46px;}
.ctl{display:flex;flex-direction:column;align-items:center;gap:4px;width:58px;}
.hrow.narrow .ctl{width:52px;}   /* dense row (Filter: morph, keytrack and the drive group) */
.ctl .lab{font-size:10px;color:var(--dim);text-align:center;text-transform:var(--lab-case);letter-spacing:var(--lab-space);}
.ctl .val{font-size:10px;color:var(--ink);min-height:12px;}

/* Knob: the JS only publishes the state (--pos 0..1, knobs.js) and the CSS decides what
   to draw with it. .ind is a transparent carrier that rotates; its ::before is the
   tick and its ::after the dot (the halo, a drop-shadow of the carrier: what
   isn't drawn has no halo). The knob's ::after is the value arc: a
   conic-gradient clipped to a ring with mask (in a mask the color doesn't matter,
   only the alpha counts). The tokens --knob-line / --knob-dot / --knob-arc at
   0px switch each piece off: a theme picks tick, dot, arc or a mix without JS. */
.knob{width:44px;height:44px;border-radius:50%;cursor:ns-resize;position:relative;--knob-ind:var(--accent);--knob-glow:var(--knob-ind);}
/* The physical disc: shrunk by --knob-disc-inset inside the box. Three background
   layers: the notch (1px radial ring at --knob-ring-r), the face (--knob-face,
   which must be an IMAGE: a flat color is written linear-gradient(c,c)) and the
   bevel, which is the border-box layer peeking out under a transparent border (the
   gradient-border trick: light on top, shadow below, no extra nodes). */
.knob::before{content:"";position:absolute;inset:var(--knob-disc-inset);border-radius:50%;border:var(--knob-edge-w) solid transparent;
  background:radial-gradient(circle,transparent calc(var(--knob-ring-r) - 1.5px),var(--knob-ring) calc(var(--knob-ring-r) - 1px) var(--knob-ring-r),transparent calc(var(--knob-ring-r) + .5px)) padding-box,   /* half a pixel of transition: a smooth ring, not jagged */
    var(--knob-face) padding-box,var(--knob-bevel) border-box;
  box-shadow:var(--knob-shadow);}
.knob .ind{position:absolute;inset:0;transform:rotate(calc(-135deg + var(--pos,0) * 270deg));pointer-events:none;
  filter:drop-shadow(0 0 2px var(--knob-glow));}
.knob .ind::before{content:"";position:absolute;left:50%;top:var(--knob-line-top);width:2px;height:var(--knob-line);margin-left:-1px;
  background:var(--knob-ind);border-radius:1px;}
.knob .ind::after{content:"";position:absolute;left:50%;top:var(--knob-dot-top);width:var(--knob-dot);height:var(--knob-dot);
  margin-left:calc(var(--knob-dot) / -2);border-radius:50%;background:var(--knob-ind);}
.knob::after{content:"";position:absolute;inset:calc(var(--knob-disc-inset) - var(--knob-arc-gap) - var(--knob-arc));border-radius:50%;
  background:conic-gradient(from 225deg,var(--knob-ind) calc(var(--pos,0) * 270deg),var(--knob-arc-track) 0 270deg,transparent 0);
  mask:radial-gradient(farthest-side,transparent calc(100% - var(--knob-arc)),black calc(100% - var(--knob-arc) + 1px));}
.panel.osc .knob{--knob-ind:var(--accent2);}
/* Large knob (the main control of its section: Cutoff) and small (banks:
   LFOs, matrix, FM4 operators). Same knob, another box: the disc's
   pieces (notch, dot) are rescaled so they don't stick out. */
.knob.big{width:56px;height:56px;--knob-ring-r:15px;--knob-dot:6px;--knob-dot-top:11px;--knob-line-top:6px;}
.ks{flex-direction:row;width:auto;gap:4px;}
.ks .knob{width:24px;height:24px;flex:none;--knob-disc-inset:2px;--knob-edge-w:1px;--knob-ring-r:5px;--knob-dot:3px;--knob-dot-top:4px;
  --knob-line:6px;--knob-line-top:2px;--knob-arc:2px;--knob-arc-gap:1px;--knob-shadow:0 2px 4px rgba(0,0,0,.5);}
.ks .val{font-size:9px;min-height:0;width:46px;white-space:nowrap;overflow:hidden;}
.ks .th{width:34px;}

.slider{display:flex;flex-direction:column;gap:4px;width:100%;}
.slider .head{display:flex;justify-content:space-between;font-size:10px;}
.slider .head .lab{color:var(--dim);text-transform:var(--lab-case);letter-spacing:var(--lab-space);}
.slider .head .val{color:var(--ink);}
/* Slider track: filled up to the thumb (sliders.js publishes --pos; the thumb measures 16px) over the track face */
input[type=range]{-webkit-appearance:none;appearance:none;height:6px;border-radius:3px;
  background:linear-gradient(var(--track-fill),var(--track-fill)) 0 0 / calc(var(--pos,0) * (100% - 16px) + 8px) 100% no-repeat,var(--track-face);outline:none;width:100%;
  box-shadow:inset 0 1px 2px rgba(0,0,0,.7), 0 1px 0 rgba(255,255,255,.04);}
input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;width:16px;height:16px;border-radius:50%;
  background:var(--thumb-face);
  border:1px solid color-mix(in srgb,var(--accent) 30%,black);cursor:pointer;
  box-shadow:0 2px 5px rgba(0,0,0,.55), 0 0 9px color-mix(in srgb,var(--accent) 40%,transparent), inset 0 1px 1px rgba(255,255,255,.45);}
input[type=range]:active::-webkit-slider-thumb{
  filter:brightness(1.12);
  box-shadow:0 1px 3px rgba(0,0,0,.6), 0 0 14px color-mix(in srgb,var(--accent) 70%,transparent), inset 0 1px 1px rgba(255,255,255,.5);}
input[type=range]::-moz-range-thumb{width:16px;height:16px;border:1px solid color-mix(in srgb,var(--accent) 30%,black);border-radius:50%;
  background:var(--thumb-face);cursor:pointer;
  box-shadow:0 2px 5px rgba(0,0,0,.55), 0 0 9px color-mix(in srgb,var(--accent) 40%,transparent), inset 0 1px 1px rgba(255,255,255,.45);}
/* Vertical fader (envelopes, as on the Juno/Jupiter): the same <input range>
   from sliders.js stood upright with writing-mode (Chrome 124+: bottom = minimum
   with direction:rtl). Track filled from the bottom up to the cap (--pos) and a
   rectangular fader cap with its index line. */
.slider.fader{width:34px;align-items:center;gap:3px;}
.slider.fader .lab,.slider.fader .val{font-size:10px;text-align:center;white-space:nowrap;}
.slider.fader .lab{color:var(--dim);text-transform:var(--lab-case);letter-spacing:var(--lab-space);}
.slider.fader .val{color:var(--ink);min-height:12px;}
.slider.fader input[type=range]{writing-mode:vertical-lr;direction:rtl;width:6px;height:96px;margin:0 0 4px;
  background:linear-gradient(var(--track-fill),var(--track-fill)) 0 100% / 100% calc(var(--pos,0) * (100% - 12px) + 6px) no-repeat,var(--track-face);}
.slider.fader input[type=range]::-webkit-slider-thumb{width:24px;height:12px;border-radius:3px;
  background:linear-gradient(transparent 5px,var(--accent) 5px 6px,transparent 6px),var(--btn-face-hi);
  border:1px solid var(--edge-hi);box-shadow:0 2px 3px rgba(0,0,0,.55);}
.slider.fader input[type=range]:active::-webkit-slider-thumb{filter:brightness(1.15);box-shadow:0 1px 2px rgba(0,0,0,.6),0 0 8px color-mix(in srgb,var(--accent) 50%,transparent);}

.seg{display:flex;border:1px solid var(--edge);border-radius:var(--r-ctl);overflow:hidden;   /* control radius, not a pill: its buttons are narrow (flex:1, no air) and a pill would eat the text */
  background:var(--well);box-shadow:0 2px 4px rgba(0,0,0,.35);}
.seg button{flex:1;background:var(--btn-face);border:none;color:var(--dim);
  padding:5px 0;font-size:11px;cursor:pointer;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.07), inset 0 -1px 0 rgba(0,0,0,.4);
  transition:background .1s,color .1s,box-shadow .1s;}
.seg button + button{border-left:1px solid var(--line);}
.seg button:hover{color:var(--ink);background:var(--btn-face-hi);}
.seg button.active{background:var(--seg-on2);color:var(--ink-on-accent2);font-weight:600;
  box-shadow:inset 0 2px 4px rgba(0,0,0,.35), inset 0 -1px 1px rgba(255,255,255,.3);
  text-shadow:0 1px 1px rgba(255,255,255,.25);}
.seg.waves button{font-size:9px;padding:5px 1px;}
.panel:not(.osc) .seg button.active{background:var(--seg-on);color:var(--ink-on-accent);
  box-shadow:inset 0 2px 4px rgba(0,0,0,.35), inset 0 -1px 1px rgba(255,255,255,.3);}
/* Rotary selector (ui/rotary.js): the .seg loses its box and its buttons
   become the positions screen-printed around a central knob (the
   JS sets each button's left/top and creates the knob). No value arc: a
   selector points, it doesn't «fill»; it carries a tick in every theme. */
.seg.rot{display:block;position:relative;width:104px;height:80px;border:none;border-radius:0;background:none;box-shadow:none;overflow:visible;}
.seg.rot button,.seg.rot button + button,.panel:not(.osc) .seg.rot button{position:absolute;transform:translate(-50%,-50%);flex:none;
  padding:1px 2px;border:none;background:none;box-shadow:none;font-size:9px;line-height:1;color:var(--dim);white-space:nowrap;}
.seg.rot button:hover{color:var(--ink);background:none;}
.seg.rot button.active,.panel:not(.osc) .seg.rot button.active{background:none;box-shadow:none;text-shadow:none;font-weight:700;color:var(--accent);}
.panel.osc .seg.rot button.active{color:var(--accent2);}
.seg.rot button svg{display:block;width:13px;height:10px;stroke:currentColor;fill:none;stroke-width:1.4;stroke-linecap:round;stroke-linejoin:round;}
.seg.rot .knob{position:absolute;left:30px;top:20px;--knob-arc:0px;--knob-line:9px;--knob-line-top:6px;--knob-dot:0px;}
.ctl.rotc{width:auto;gap:0;}
.ctl.rotc .rcap{font-size:9px;color:var(--dim);min-height:11px;margin-top:2px;}
/* FM4 algorithm graph (ui/fm4Graph.js): carriers in the module's accent, modulators dimmed */
.fm4g{margin-top:4px;line-height:0;}
.fm4g svg{display:block;overflow:visible;}
.fm4g rect{fill:var(--well);stroke:var(--dim);stroke-width:1;}
.fm4g .car rect{stroke:var(--accent2);}
.fm4g text{fill:var(--ink);font-size:8px;text-anchor:middle;dominant-baseline:central;}
.fm4g path{fill:none;stroke:var(--dim);stroke-width:1;}
.fm4g path.o{stroke:var(--accent2);}
.fm4g path.fb{stroke:var(--edge-hi);stroke-dasharray:2 2;}
.fm4g path.fb.on{stroke:var(--accent2);stroke-dasharray:none;}
/* Switch with LED: the 2-3 option .seg as a list of LEDs with their
   label (the hardware's Poly/Mono). Click on a label = pick it. */
.seg.tog{display:flex;flex-direction:column;gap:4px;border:none;border-radius:0;background:none;box-shadow:none;overflow:visible;}
.seg.tog button,.seg.tog button + button,.panel:not(.osc) .seg.tog button{flex:none;display:flex;align-items:center;gap:6px;padding:1px 0;
  border:none;background:none;box-shadow:none;font-size:10px;color:var(--dim);text-align:left;white-space:nowrap;}
.seg.tog button::before{content:"";width:6px;height:6px;flex:none;border-radius:50%;background:var(--knob);border:1px solid var(--line);
  box-shadow:inset 0 1px 1px rgba(0,0,0,.6);}
.seg.tog button:hover{color:var(--ink);background:none;}
.seg.tog button.active,.panel:not(.osc) .seg.tog button.active{background:none;box-shadow:none;text-shadow:none;font-weight:400;color:var(--ink);}
.seg.tog button.active::before{background:var(--accent);box-shadow:0 0 5px var(--accent);}
.panel.osc .seg.tog button.active::before{background:var(--accent2);box-shadow:0 0 5px var(--accent2);}
.togc .fieldlab{margin-bottom:3px;}
.stepper{display:flex;align-items:center;gap:6px;}
.stepper button{width:22px;height:22px;border-radius:5px;border:1px solid var(--edge);color:var(--ink);cursor:pointer;font-size:14px;line-height:1;
  background:var(--btn-face);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.07), 0 1px 2px rgba(0,0,0,.4);}
.stepper button:active{transform:translateY(1px);box-shadow:inset 0 1px 3px rgba(0,0,0,.5);}
.stepper .num{min-width:14px;text-align:center;font-size:13px;}
.fieldlab{font-size:10px;color:var(--dim);text-transform:var(--lab-case);letter-spacing:var(--lab-space);margin-bottom:4px;}
/* Table banks (LFOs, FM4 operators): the label goes ONCE per column
   (.th) and once per row (.rn), like a mixer. Each row of the LFO bank
   is a .lfo-row with display:contents (its cells belong to the grid, but the
   row still exists for the LED and the dimming). */
.tbl{display:grid;gap:3px 8px;align-items:center;}
.th{font-size:9px;color:var(--dim);text-transform:var(--lab-case);letter-spacing:var(--lab-space);}
.rn{font-size:10px;color:var(--ink);display:flex;align-items:center;white-space:nowrap;}
.lfo-bank{grid-template-columns:auto 40px auto auto auto;}
.lfo-bank select,.lfo-foot select{font-size:9px;line-height:1.2;padding:2px 3px;letter-spacing:.3px;}   /* at the scale of the shape buttons */
.fm4ops{grid-template-columns:28px repeat(4,auto);gap:3px 4px;margin-left:4px;}
.fm4ops .ks .val{width:30px;}   /* «12.00», «600 ms»: with 4 columns 46 px per value don't fit */
.lfo-row{display:contents;}
/* Steps (Roadmap 7.0 · 2.3): one row per lane; canvas of FIXED width (32 steps) */
.ps-grid{grid-template-columns:12px 288px auto auto auto auto auto;gap:2px 8px;}   /* 288: the Player needed the room (9 px per step with 32) */
.ps-row{display:contents;}
.ps-canvas{width:288px;height:32px;display:block;cursor:crosshair;touch-action:none;}
.ps-grid select{font-size:9px;line-height:1.2;padding:2px 3px;}
.ps-grid .stepper .num{min-width:16px;}
.ps-head{gap:8px;align-items:center;margin-bottom:6px;}
.ps-head select{font-size:10px;padding:2px 4px;}
.ps-head .note{margin:0;}
.lfoShape,.noshape{background:var(--well);border:1px solid var(--edge);border-radius:4px;color:var(--accent2);font-size:9px;
  padding:2px 0;text-align:center;letter-spacing:.5px;line-height:1.2;font-family:inherit;}
.lfoShape{cursor:pointer;}
.lfoShape:hover{border-color:var(--accent2);}
.noshape{color:var(--dim);border-style:dashed;}
.lfo-foot,.mx-foot,.dly-foot,.arp-foot{gap:14px;margin-top:8px;align-items:center;}
.dly-foot{margin-top:2px;}   /* the Delay also lives in Sequencing, whose band is lower: it just fits */
.lfo-foot .seg.tog{gap:2px;}
.note{font-size:9px;color:var(--dim);margin:2px 0 10px;}

.panel h2{display:flex;align-items:center;}   /* LED · title · «?» · «room» tag */

#adsrCanvas{display:block;width:250px;height:52px;background:var(--screen);border:1px solid var(--edge);border-radius:var(--r-ctl);margin-bottom:6px;}
#seqCanvas,#wsCanvas{display:block;width:100%;flex:1;min-height:0;background:var(--screen);border:1px solid var(--edge);border-radius:var(--r-ctl);
  touch-action:none;cursor:crosshair;}
.seqlay{align-items:stretch;gap:14px;flex:1;min-height:0;}
.seqcanvas{flex:none;width:340px;align-items:stretch;}
.seq-sel{gap:6px;flex:none;}
#seqPatSel{flex:1;min-width:0;width:auto;}
#seqNotesSel{flex:none;width:116px;}   /* notes by degree (5.2) */
#mxRows{display:flex;flex-direction:column;gap:4px;}

/* Output: the instrument's display (ui/display.js) + the master's oscilloscope and spectrum (ui/scope.js) */
#salida{min-width:420px;}
/* Two lines, like a synth's LCD: on top the parameter and, to its right, what the option means
   (or why it does nothing: .off); below, large, the value. What doesn't fit shrinks the font and, if
   it still doesn't, scrolls back and forth (.scroll; ui/display.js computes --dx and --t). */
.readout{display:grid;grid-template-columns:auto minmax(0,1fr);align-items:center;column-gap:12px;row-gap:3px;padding:5px 12px;min-height:46px;background:var(--screen);border:1px solid var(--edge);border-radius:var(--r-ctl);}
.readout .who{max-width:220px;font-size:10px;color:var(--dim);letter-spacing:1.5px;text-transform:uppercase;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.readout .side{min-width:0;font-size:10px;color:var(--dim);text-align:right;white-space:nowrap;overflow:hidden;}
.readout .side.off{color:var(--accent2);}
.readout .v{grid-column:1 / -1;min-width:0;overflow:hidden;text-align:right;font:600 24px/1 Consolas,"Cascadia Mono",ui-monospace,monospace;color:var(--accent);white-space:nowrap;
  text-shadow:0 0 10px color-mix(in srgb,var(--accent) 40%,transparent);}
.readout .v > span,.readout .side > span{display:inline-block;}
.readout .scroll{text-align:left;}
.readout .scroll > span{animation:lcdScroll var(--t,6s) ease-in-out infinite alternate;}
@keyframes lcdScroll{0%,12%{transform:translateX(0);}88%,100%{transform:translateX(var(--dx,0));}}
.vizrow{display:flex;gap:8px;margin-top:8px;flex:1;min-height:0;}
.vizrow canvas{flex:1;min-width:0;height:100%;display:block;background:var(--screen);border:1px solid var(--edge);border-radius:var(--r-ctl);}
/* Output meter (ui/meter.js): two peak bars (−48..0 dBFS; the zones, fixed in the background:
   −12 dBFS at 75 %, −1 at 97.9 %) that a semi-transparent cover hides from the top, the
   hold line and the limiter's reduction (violet, from the top). Transform only: no layout */
.meter{flex:none;width:60px;display:flex;flex-direction:column;gap:4px;}
.mt-bars{flex:1;min-height:0;display:flex;gap:3px;padding:4px;background:var(--screen);border:1px solid var(--edge);border-radius:var(--r-ctl);}
.mt-ch,.mt-gr{position:relative;overflow:hidden;border-radius:2px;}
.mt-ch{flex:1;background:linear-gradient(to top,var(--accent) 0 75%,var(--star) 75% 97.9%,var(--hot) 97.9%);}
.mt-ch i{position:absolute;inset:0;background:color-mix(in srgb,var(--screen) 86%,transparent);transform-origin:top;transform:scaleY(1);}
.mt-ch b{position:absolute;inset:0;border-top:2px solid var(--ink);transform:translateY(100%);}
.mt-gr{flex:none;width:6px;background:color-mix(in srgb,var(--accent2) 14%,transparent);}
.mt-gr i{position:absolute;inset:0;background:var(--accent2);transform-origin:top;transform:scaleY(0);}
.mt-foot{display:flex;align-items:center;justify-content:space-between;gap:2px;font:600 10px/1 Consolas,"Cascadia Mono",ui-monospace,monospace;
  color:var(--dim);font-variant-numeric:tabular-nums;}
#mtLed{padding:2px 4px;border:1px solid var(--edge);border-radius:4px;background:none;color:var(--dim);font:inherit;cursor:pointer;}
#mtLed.on{color:var(--hot);border-color:var(--hot);box-shadow:0 0 6px color-mix(in srgb,var(--hot) 50%,transparent);}

/* Keyboard: part of the instrument, scales with it */
#kbWrap{flex:1;min-height:0;display:flex;flex-direction:column;gap:6px;}
#keyboard{flex:1;min-height:0;background:linear-gradient(180deg,var(--line),var(--screen));border:1px solid var(--edge);
  border-radius:8px;padding:8px;box-shadow:inset 0 2px 10px rgba(0,0,0,.6);}
#kbKeys{position:relative;height:100%;display:flex;touch-action:none;}   /* a finger on the keys plays, it doesn't scroll or zoom */
#kbKeys::before{ /* red felt behind the keys */
  content:'';position:absolute;left:0;right:0;top:0;height:4px;z-index:4;pointer-events:none;
  background:linear-gradient(180deg,var(--felt),color-mix(in srgb,var(--felt) 60%,black));}
.wkey{position:relative;flex:1;margin:0 1px;cursor:pointer;
  display:flex;flex-direction:column;align-items:center;justify-content:flex-end;gap:3px;padding-bottom:5px;
  border:1px solid var(--key-w-edge);border-top:none;border-radius:0 0 4px 4px;
  background:var(--key-w-face);
  box-shadow:inset -2px 0 3px rgba(0,0,0,.10), inset 0 -3px 4px rgba(0,0,0,.14);
  transition:transform .05s, filter .05s;}
.wkey:hover{filter:brightness(.95);}
.wkey.on{transform:translateY(2px);
  background:var(--key-w-on);
  box-shadow:inset 0 4px 8px rgba(0,0,0,.28), 0 0 14px color-mix(in srgb,var(--accent) calc(var(--vel,1) * 35%),transparent);}
.bkey{position:absolute;top:0;height:62%;z-index:3;cursor:pointer;
  display:flex;align-items:flex-end;justify-content:center;padding-bottom:5px;
  border:1px solid var(--key-b-edge);border-top:none;border-radius:0 0 4px 4px;
  background:var(--key-b-face);
  box-shadow:inset 2px 0 3px rgba(255,255,255,.07), inset -2px 0 4px rgba(0,0,0,.65), 2px 3px 6px rgba(0,0,0,.55);
  transition:transform .05s, filter .05s;}
.bkey:hover{filter:brightness(1.25);}
.bkey.on{transform:translateY(2px);
  background:var(--key-b-on);
  box-shadow:inset 0 3px 6px rgba(0,0,0,.5), 0 0 14px color-mix(in srgb,var(--accent2) calc(var(--vel,1) * 40%),transparent);}
.klab{pointer-events:none;min-width:14px;padding:2px 3px;border-radius:3px;text-align:center;
  font-size:9px;font-weight:600;line-height:1;}
.wkey .klab{color:var(--key-w-ink);background:rgba(255,255,255,.55);border:1px solid color-mix(in srgb,var(--key-w-edge) 69%,white);}
.bkey .klab{color:var(--key-b-ink);background:rgba(0,0,0,.35);border:1px solid color-mix(in srgb,var(--key-b) 83%,white);}
.wkey.on .klab{color:var(--ink-on-accent);background:rgba(255,255,255,.4);border-color:color-mix(in srgb,var(--ink-on-accent) 35%,transparent);}
.bkey.on .klab{color:var(--ink-on-accent2);background:rgba(255,255,255,.3);border-color:color-mix(in srgb,var(--ink-on-accent2) 35%,transparent);}
.kname{pointer-events:none;font-size:9px;font-weight:700;letter-spacing:.5px;color:var(--dim);}
.wkey.on .kname{color:var(--ink-on-accent);}
/* Under the keyboard: the octave by mouse (− / + and the visible range); the rest lives in the tooltip */
#kbBar{flex:none;display:flex;align-items:center;gap:8px;}
#kbBar .lab{font-size:10px;color:var(--dim);text-transform:var(--lab-case);letter-spacing:var(--lab-space);}
#kbBar .num{min-width:52px;}

/* MIDI learn */
#midiBar[hidden]{display:none;}  /* the display:flex below would win over the hidden attribute */
#midiBar{position:fixed;left:50%;bottom:14px;transform:translateX(-50%);z-index:60;max-width:96vw;
  display:flex;align-items:center;gap:10px;flex-wrap:wrap;justify-content:center;font-size:11px;
  background:var(--panel-face);border:1px solid var(--accent2);
  border-radius:var(--r-panel);padding:9px 14px;box-shadow:var(--shadow-pop);}
#midiBar .mm-hint{color:var(--ink);}
#midiBar .mm-hint b{color:var(--accent2);letter-spacing:1px;}
#midiBar label{display:flex;align-items:center;gap:4px;color:var(--dim);cursor:pointer;}
#midiBar kbd{background:var(--well);border:1px solid var(--edge);border-radius:3px;padding:1px 5px;font-size:10px;color:var(--ink);}
#mmMsg{color:var(--accent);}
.btn.on{border-color:var(--accent2);color:var(--accent2);box-shadow:0 0 8px color-mix(in srgb,var(--accent2) 35%,transparent);}
body.midi-learn .mappable{position:relative;outline:1px dashed color-mix(in srgb,var(--accent2) 45%,transparent);outline-offset:2px;
  border-radius:8px;cursor:copy;}
body.midi-learn .mappable:hover{outline-color:var(--accent2);outline-style:solid;}
body.midi-learn input[type=range]{pointer-events:none;}
body.midi-learn [data-cc]::after{content:"CC" attr(data-cc);position:absolute;top:-7px;right:-4px;z-index:5;
  background:var(--accent2);color:var(--ink-on-accent2);font-size:8px;font-weight:700;padding:1px 4px;border-radius:6px;
  pointer-events:none;}
.midi-wait{outline:2px solid var(--hot) !important;animation:mmPulse .8s infinite;}
@keyframes mmPulse{50%{outline-color:transparent;}}
.midi-hit{box-shadow:0 0 12px color-mix(in srgb,var(--accent2) 55%,transparent);border-radius:8px;}

/* Self-test (?selftest=1) */
#selftestPanel{position:fixed;top:12px;right:12px;width:360px;max-height:84vh;z-index:50;
  display:flex;flex-direction:column;font-size:11px;
  background:var(--panel-face);border:1px solid var(--edge);
  border-radius:var(--r-panel);box-shadow:var(--shadow-pop);}
#selftestPanel .st-head{display:flex;align-items:center;gap:8px;padding:8px 10px;
  border-bottom:1px solid var(--edge);color:var(--accent);}
#selftestPanel .st-head b{letter-spacing:1px;text-transform:uppercase;font-size:11px;}
#selftestPanel .st-head button{margin-left:auto;background:none;border:none;color:var(--dim);cursor:pointer;font-size:13px;}
#stSummary{color:var(--dim);}
#stBody{overflow-y:auto;padding:6px 10px 10px;}
.st-cat{margin:6px 0 2px;color:var(--accent2);text-transform:uppercase;letter-spacing:1px;font-size:9px;}
.st-row{display:flex;gap:6px;padding:2px 0;border-bottom:1px dashed color-mix(in srgb,var(--edge) 50%,transparent);}
.st-row .st-name{flex:1;color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.st-row .st-peak{color:var(--dim);min-width:64px;text-align:right;}
.st-row.ok .st-mark{color:var(--accent);}
.st-row.fail .st-mark,.st-row.fail .st-name{color:var(--hot);}
.st-err{color:var(--hot);font-size:10px;padding:1px 0 3px 14px;}

/* Wave Editor («user» table) */
#weCanvas{width:100%;flex:1;min-height:0;min-width:150px;background:var(--screen);border:1px solid var(--edge);
  border-radius:8px;margin-top:8px;cursor:crosshair;touch-action:none;display:block;}

/* Per-module help: the text lives hidden (.help) and shows in the «?» tooltip */
.panel .help{display:none;}
/* Switch in a module's header (Osc A Sync): before the «?», which sticks to it */
.h2tog{margin-left:auto;padding:1px 6px;border:1px solid var(--edge);border-radius:4px;background:none;color:var(--dim);
  font-family:inherit;font-size:9px;font-weight:600;line-height:1.3;letter-spacing:.5px;text-transform:var(--lab-case);cursor:pointer;}
.h2tog:hover{color:var(--ink);border-color:var(--edge-hi);}
.h2tog.active{color:var(--accent);border-color:var(--accent);background:color-mix(in srgb,var(--accent) 14%,transparent);}
.h2tog + .qmark{margin-left:6px;}
.qmark{margin-left:auto;width:14px;height:14px;flex:none;border-radius:50%;
  border:1px solid var(--edge);color:var(--dim);font-size:9px;font-weight:600;
  display:inline-flex;align-items:center;justify-content:center;cursor:help;text-transform:none;}
.qmark:hover{color:var(--accent);border-color:var(--accent);}
#helpTip{position:fixed;z-index:60;max-width:280px;background:var(--well);border:1px solid var(--edge);
  border-radius:8px;padding:8px 10px;font-size:10px;line-height:1.5;color:var(--dim);
  box-shadow:0 8px 24px rgba(0,0,0,.55);pointer-events:none;}
#helpTip b{color:var(--ink);}

/* Waveform icons (inline SVG with currentColor: immune to the theme) */
.seg.waves button svg{display:block;margin:0 auto;width:17px;height:12px;
  stroke:currentColor;fill:none;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round;}

/* Off (ui/deps.js): what does NOTHING with the current settings —
   a whole section or a single control. Dimmed IN PLACE but touchable
   (to prepare a setting before switching on what activates it, as on
   hardware); the reason goes in data-off (tooltip and Output display). A switched-off
   section dims everything but its title; what is inside something already off
   isn't dimmed twice. */
/* On ↔ off, softly (Sergio, 2026-10-27): a third of a second instead of a cut. Going off is a
   transition declared in the off state (CSS reads the transition from the state it goes TO);
   coming back is a one-shot animation on the transient .dep-on that deps.js adds for 400 ms,
   so the controls' own hover transitions are never overridden while they are on. */
.dep-off:not(.panel){opacity:.35;filter:saturate(.35);transition:opacity .35s ease,filter .35s ease;}
.panel.dep-off > :not(h2){opacity:.35;filter:saturate(.35);transition:opacity .35s ease,filter .35s ease;}
.panel.dep-off h2{opacity:.6;transition:opacity .35s ease;}
@keyframes depOn{from{opacity:.35;filter:saturate(.35);}to{opacity:1;filter:none;}}
@keyframes depOnH2{from{opacity:.6;}to{opacity:1;}}
.dep-on:not(.panel){animation:depOn .35s ease both;}
.panel.dep-on > :not(h2){animation:depOn .35s ease both;}
.panel.dep-on h2{animation:depOnH2 .35s ease both;}
.dep-off .dep-off,.panel.dep-off .dep-off{opacity:1;filter:none;}

/* Activity LEDs (ui/activity.js): the section is DOING something.
   In each section's h2; mini version in the rows of the LFO bank. */
.led{width:7px;height:7px;border-radius:50%;background:var(--knob);border:1px solid var(--line);
  box-shadow:inset 0 1px 1px rgba(0,0,0,.6);display:inline-block;margin-right:7px;flex:none;
  transition:background .25s,box-shadow .25s;}
.led.on{background:var(--accent);box-shadow:0 0 6px var(--accent),inset 0 1px 1px rgba(255,255,255,.25);}
.panel.osc .led.on{background:var(--accent2);box-shadow:0 0 6px var(--accent2),inset 0 1px 1px rgba(255,255,255,.25);}
.lfo-row .rn .led{width:5px;height:5px;margin-right:5px;}
/* "Amount" knobs (act in PARAMS) at their inert value: indicator off, no halo */
/* a knob at 0 («off») dims its tick, dot, arc and glow: the two colors are registered so they can FADE */
@property --knob-ind{syntax:'<color>';inherits:true;initial-value:transparent;}   /* (every knob sets it: the initial value never shows) */
@property --knob-glow{syntax:'<color>';inherits:true;initial-value:transparent;}
.knob{transition:--knob-ind .3s ease,--knob-glow .3s ease;}
.knob.off,.panel.osc .knob.off{--knob-ind:var(--edge-hi);--knob-glow:transparent;}

/* Modulation matrix: rows [source ▾] → [destination ▾] × amount knob */
.mx-row{display:grid;grid-template-columns:1fr 12px 1fr 76px;align-items:center;gap:6px;}
.mx-row select{min-width:0;padding:4px 6px;font-size:11px;}
.mx-arrow{color:var(--dim);font-size:11px;text-align:center;}

/* Screen tabs (ui/screens.js): each one shows ITS fixed panel */
#screenTabs{margin-left:4px;border-radius:var(--r-btn);}   /* the tabs are pills: they have air */
#screenTabs button{flex:none;padding:6px 14px;font-size:11px;letter-spacing:var(--title-space);text-transform:var(--title-case);}

/* Presets screen: the Library by sections, in columns (ui/libraryScreen.js) */
#library{overflow-y:auto;}   /* the Library is the only thing that scrolls (vertically): the columns balance in height */
#library .lib-bar{position:sticky;top:-8px;z-index:2;background:var(--panel-face);padding:6px 0;margin:-6px 0 4px;}
#presetList{columns:220px;column-gap:16px;flex:none;}
.pl-sec{break-inside:avoid;margin-bottom:12px;}
.pl-sec.big{break-inside:auto;}   /* a 70-item section must not be a mile-long column */
.pl-head{font-size:10px;color:var(--accent);text-transform:var(--title-case);letter-spacing:1px;margin:0 0 4px;
  border-bottom:1px solid var(--edge);padding-bottom:3px;}
.pl-item{display:flex;align-items:center;gap:6px;width:100%;background:none;border:1px solid transparent;color:var(--ink);
  border-radius:5px;padding:2px 7px;font-size:12px;cursor:pointer;line-height:1.4;}
.pl-item.drag{opacity:.4;}
.pl-label{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.pl-star{color:var(--edge-hi);font-size:12px;flex:none;cursor:pointer;}
.pl-star:hover{color:var(--star);}
.pl-star.on{color:var(--star);text-shadow:0 0 5px color-mix(in srgb,var(--star) 60%,transparent);}
.pl-tag{font-size:9px;color:var(--accent2);border:1px solid var(--edge);border-radius:4px;padding:0 4px;flex:none;letter-spacing:.5px;}
.pl-play{color:var(--accent2);cursor:pointer;flex:none;width:16px;text-align:center;font-size:11px;}
.pl-play:hover{filter:brightness(1.35);}
.pl-play.on{color:var(--hot);}
.pl-tools{display:inline-flex;gap:2px;margin-left:auto;opacity:0;flex:none;}
.pl-item:hover .pl-tools,.pl-head:hover .pl-tools{opacity:1;}
.pl-btn{color:var(--dim);cursor:pointer;padding:0 4px;border-radius:4px;font-size:11px;line-height:1.3;}
.pl-btn:hover{color:var(--accent);background:color-mix(in srgb,var(--accent) 12%,transparent);}
.pl-btn.del:hover{color:var(--hot);background:color-mix(in srgb,var(--hot) 12%,transparent);}
.pl-head{display:flex;align-items:center;gap:6px;}
.pl-head .pl-name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.pl-head .pl-n{color:var(--dim);flex:none;}
.pl-head .pl-tools{text-transform:none;letter-spacing:0;}
.pl-sec.over{outline:1px dashed var(--accent);outline-offset:3px;border-radius:6px;}
.pl-edit{width:100%;min-width:0;background:var(--well);border:1px solid var(--accent);color:var(--ink);border-radius:4px;padding:1px 5px;font-size:12px;}
.lib-bar{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:10px;}
.lib-bar .chk{margin-top:0;}
#libSearch{background:var(--well);border:1px solid var(--edge);color:var(--ink);border-radius:var(--r-ctl);padding:5px 8px;font-size:12px;width:200px;}
#libMsg{margin:0 0 0 auto;font-size:11px;}
.pl-item:hover{border-color:var(--accent);color:var(--accent);}
.pl-item.cur{background:color-mix(in srgb,var(--accent) 14%,transparent);border-color:var(--accent);color:var(--accent);}
/* what an item is: ● = factory preset modified in place (the header's dot; a click brings the factory values back),
   old = copy of a previous bank, mine / imported out of their sections */
.pl-dot{color:var(--accent2);cursor:pointer;flex:none;font-size:11px;line-height:1;text-shadow:0 0 5px color-mix(in srgb,var(--accent2) 60%,transparent);}
.pl-dot:hover{filter:brightness(1.35);}
.pl-tag.old{color:var(--dim);}
/* drag and drop: the insertion line where the item will land; a section lit = it lands at its end */
.pl-item.drop-before{box-shadow:0 -2px 0 0 var(--accent);}
.pl-item.drop-after{box-shadow:0 2px 0 0 var(--accent);}
.pl-sec.over .pl-head{background:color-mix(in srgb,var(--accent) 18%,transparent);border-radius:4px;}
/* «Undo» after a deletion (no confirm dialogs) */
.undo{color:var(--accent);cursor:pointer;text-decoration:underline;margin-left:6px;pointer-events:auto;}

/* mod wheel under the keyboard (Roadmap 5.0 · block 4): the matrix's «Wheel (CC1)» source */
#kbBar .wheel{display:flex;align-items:center;gap:8px;margin-left:18px;}
#kbBar .wheel input[type=range]{width:140px;}
#kbBar .kb-grp{display:flex;align-items:center;gap:8px;margin-left:18px;}
#kbBar .kb-grp select{padding:2px 6px;font-size:11px;}
#kbTouch button{padding:3px 8px;font-size:10px;}

/* WELCOME WINDOW (ui/splash.js): a modal window centered over the
   instrument, which shows behind it dimmed (darkened and desaturated). On top, the
   small effect screen at double pixels (pixelated); below, ENTER and the footer.
   It scales with the instrument (inline zoom from splash.js). Brand colors,
   not theme colors: --sp-* tokens just below (and the canvas palettes). */
:root{
  --sp-dim:rgba(4,7,9,.6); --sp-card-top:#0f1b1d; --sp-card-bot:#091113; --sp-edge:rgba(63,214,190,.3); --sp-edge2:rgba(255,255,255,.06);
  --sp-shadow:rgba(0,0,0,.7); --sp-halo:rgba(63,198,242,.12); --sp-ink:#cdeee6; --sp-soft:#7aa39a; --sp-meta:#8fd9c8; --sp-white:#fff;
  --sp-green:#39d4a3; --sp-cyan:#3fc6f2; --sp-violet:#a78bfa; --sp-glow:rgba(63,198,242,.4);
  --sp-btn-top:rgba(6,24,26,.96); --sp-btn-bot:rgba(2,10,12,.98); --sp-btn-top-on:rgba(12,52,56,.96); --sp-btn-bot-on:rgba(4,22,26,.98);
  --sp-btn-ink:#d6fff6; --sp-btn-glow:rgba(63,214,242,.75); --sp-sweep:rgba(255,255,255,.2);
  /* canvas palettes (splash.js interpolates them to 256 colors): cyclic plasma and the name's copper */
  --sp-plasma:#02090a,#041d1c,#08383a,#0d5752,#127a6c,#18a08a,#127a6c,#0d4f57,#132a4a,#08202a;
  --sp-copper:#0e3b52,#3fc6f2,#e8fbff,#3fc6f2,#2a6f9e,#7d63d8,#d9ccff,#a78bfa,#3b2a78,#0e3b52;}
#splash{position:fixed;inset:0;z-index:1000;display:grid;place-items:center;background:var(--sp-dim);
  backdrop-filter:saturate(.45) brightness(.82);transition:opacity .4s ease;}
#splash.out{opacity:0;pointer-events:none;}
#splash[hidden]{display:none;}   /* closed: the display:grid above would override the browser's [hidden] */
#aboutBtn,#verBadge{cursor:pointer;}   /* the header title opens «About» */
.sp-card{width:336px;overflow:hidden;border:1px solid var(--sp-edge);border-radius:10px;
  background:linear-gradient(180deg,var(--sp-card-top),var(--sp-card-bot));
  box-shadow:inset 0 0 0 1px var(--sp-edge2),0 24px 60px var(--sp-shadow),0 0 48px var(--sp-halo);
  animation:spIn .45s cubic-bezier(.2,.8,.2,1) both;transition:transform .4s ease;}
#splash.out .sp-card{transform:scale(.96);}
#splashFx{display:block;width:336px;height:132px;image-rendering:pixelated;border-bottom:1px solid var(--sp-edge);}
.sp-body{display:flex;flex-direction:column;align-items:center;gap:13px;padding:14px 24px 16px;}
.sp-sub{font:600 10px/1 ui-monospace,"Cascadia Mono",Consolas,monospace;letter-spacing:.32em;text-transform:uppercase;color:var(--sp-meta);}
.sp-btn{filter:drop-shadow(0 0 10px var(--sp-glow));}
#spEnter{--cut:11px;position:relative;width:196px;height:44px;padding:0;border:0;cursor:pointer;
  background:linear-gradient(90deg,var(--sp-green),var(--sp-cyan),var(--sp-violet),var(--sp-cyan),var(--sp-green));background-size:300% 100%;
  animation:spFlow 5s linear infinite;
  clip-path:polygon(var(--cut) 0,100% 0,100% calc(100% - var(--cut)),calc(100% - var(--cut)) 100%,0 100%,0 var(--cut));
  transition:transform .12s;}
#spEnter span{position:absolute;inset:1.5px;display:flex;align-items:center;justify-content:center;overflow:hidden;
  background:linear-gradient(180deg,var(--sp-btn-top),var(--sp-btn-bot));
  clip-path:polygon(calc(var(--cut) - 1px) 0,100% 0,100% calc(100% - var(--cut) + 1px),calc(100% - var(--cut) + 1px) 100%,0 100%,0 calc(var(--cut) - 1px));
  font:700 13px/1 system-ui,sans-serif;letter-spacing:.55em;text-indent:.55em;color:var(--sp-btn-ink);
  text-shadow:0 0 8px var(--sp-btn-glow);transition:background .2s,color .2s;}
#spEnter span::after{content:"";position:absolute;top:0;bottom:0;width:40%;left:-60%;
  background:linear-gradient(100deg,transparent,var(--sp-sweep),transparent);animation:spSweep 2.8s ease-in-out infinite;}
#spEnter:hover span,#spEnter:focus-visible span{background:linear-gradient(180deg,var(--sp-btn-top-on),var(--sp-btn-bot-on));color:var(--sp-white);}
#spEnter:active{transform:scale(.97);}
#spEnter:focus{outline:none;}
.sp-foot{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:9px 14px;border-top:1px solid var(--sp-edge2);
  font:500 10px/1 ui-monospace,"Cascadia Mono",Consolas,monospace;letter-spacing:.04em;color:var(--sp-soft);}
.sp-foot b{color:var(--sp-ink);font-weight:600;}
.sp-foot a{display:inline-flex;align-items:center;gap:5px;color:var(--sp-meta);text-decoration:none;}
.sp-gh{display:block;width:12px;height:12px;flex:none;margin-top:-1px;}   /* 12 px next to 11 px text: half a pixel up to center it */
.sp-links{display:flex;align-items:center;gap:6px;}   /* centered by boxes: the icon doesn't push the text line */
.sp-foot a:hover{color:var(--sp-white);text-decoration:underline;}
@keyframes spIn{from{opacity:0;transform:translateY(8px) scale(.97);}to{opacity:1;transform:none;}}
@keyframes spFlow{to{background-position:300% 0;}}
@keyframes spSweep{0%{left:-60%;}60%,100%{left:120%;}}
