/* THE GROUND IS THE APP'S: this unconditional html/body background is what the document is from first paint until
   a theme resolves, and index.html's theme-color meta and tests/pwa.test.mjs read it from HERE (MIR's base.css owns
   the tokens and the widgets, not the stage). */
html, body { width: 100%; height: 100%; margin: 0; overflow: hidden; background: #070a0f; }

/* ── shell ──────────────────────────────────────────────────────────────── */
/* THE STAGE IS THE WHOLE WINDOW; the rack floats over it as detached cards */
#lab { position: fixed; inset: 0; display: block; }
#stage { position: absolute; inset: 0; min-width: 0; min-height: 0; background: #070a0f; }
#field { position: absolute; inset: 0; width: 100%; height: 100%; display: block; touch-action: none; cursor: grab; }
#field.khover { cursor: pointer; } #field.kdrag { cursor: grabbing; }
#field.drag { cursor: grabbing; }
#rack, #rackL { position: absolute; top: 0; right: 0; bottom: auto; max-height: 100%; width: var(--rack-w); z-index: var(--z-chrome); min-height: 0; overflow: hidden auto; overscroll-behavior: contain; -webkit-overflow-scrolling: touch;
  padding: var(--sp-2); display: flex; flex-direction: column; gap: var(--sp-2); background: transparent; border: 0; scrollbar-width: thin; pointer-events: auto; touch-action: pan-y;
  transition: transform .28s ease, opacity .28s ease; }
#rack > *, #rackL > * { pointer-events: auto; }
#rackL { right: auto; left: 0; }
body.rack-hidden #rackL { transform: translateX(calc(-100% - 12px)); opacity: 0; }
body.rack-hidden.rack-peek #rackL { transform: none; opacity: 1; }
#rackPeekL { position: absolute; left: 0; top: 0; bottom: 0; width: 16px; z-index: var(--z-chrome); }
#rackL:empty { pointer-events: none; }
body.rack-hidden #rack { transform: translateX(calc(100% + 12px)); opacity: 0; }
body.rack-hidden.rack-peek #rack { transform: none; opacity: 1; }
#rackPeek { position: absolute; right: 0; top: 0; bottom: 0; width: 16px; z-index: var(--z-chrome); }
#rackToggle, #rackAdd, #rackFav { position: absolute; right: calc(var(--rack-w) + 14px); top: 8px; z-index: calc(var(--z-chrome) + 1); width: 30px; height: 30px; border-radius: 50%; color: var(--fg-soft); font-size: 14px; cursor: pointer; display: inline-flex; align-items: center; justify-content: center; padding: 0; }
body.rack-hidden #rackToggle { color: var(--acc); }
.dev-loading .mark { width: 28px; height: 28px; animation: lw-busy-breathe 1.1s linear infinite alternate; }
.dev.off .dev-stat::after { content: ' · OFF'; color: var(--dim); }
.dev.copied .dev-stat::after { content: ' · COPIED'; color: var(--acc); }
#rackAdd { top: 44px; font-size: 18px; line-height: 1; transition: opacity .28s ease; }
/* wave 54 (board #48): FAVOURITE LAYOUTS takes the next seat down the same column, on the same rules as its two
   neighbours — one geometry, one hide rule, one hover colour, and its list opens where the + list opens. */
#rackFav { top: 80px; font-size: 15px; line-height: 1; transition: opacity .28s ease; }
body.rack-hidden #rackAdd, body.rack-hidden #rackAddList, body.rack-hidden #rackFav, body.rack-hidden #rackFavList { opacity: 0; pointer-events: none; }
body.rack-hidden.rack-peek #rackAdd, body.rack-hidden.rack-peek #rackFav { opacity: 1; pointer-events: auto; }
#rackAdd:hover, #rackFav:hover { color: var(--acc); }
#rackAddList, #rackFavList { position: absolute; right: calc(var(--rack-w) + 14px); top: 116px; z-index: calc(var(--z-chrome) + 3); display: flex; flex-direction: column; min-width: 260px; padding: 4px; border-radius: 10px; }
.mb-item .fav-x { float: right; color: var(--dim); margin-left: 10px; }
.mb-item:hover .fav-x { color: var(--acc2); }
body.no-badges #badges { display: none !important; }
/* ── the NOTEBOOK: a free glass — real blur, no colour overlay ── */
#notebook { position: absolute; left: calc(50% - 320px); top: 12%; width: 640px; height: 460px; min-width: 320px; min-height: 240px; max-width: calc(100vw - 2 * var(--rack-w) - 40px); max-height: calc(100vh - 40px); resize: both; --nb-line: hsl(0 0% 100% / .45); --nb-btn: hsl(0 0% 100% / .18); --nb-btn-line: hsl(0 0% 100% / .35); z-index: calc(var(--z-chrome) + 4); display: flex; flex-direction: column; border-radius: 16px;
  border: 1px solid hsl(0 0% 100% / .45); background: transparent; -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(115%); backdrop-filter: blur(var(--glass-blur)) saturate(115%);
  box-shadow: inset 0 1px 0 hsl(0 0% 100% / .55), 0 8px 24px hsl(0 0% 0% / .18); color: var(--fg); overflow: hidden; }
#notebook[data-face="about"] { width: 470px; height: 670px; left: calc(50% - 235px); top: max(20px, calc(50% - 335px)); }
body[data-theme="dark"] #notebook { border-color: hsl(0 0% 100% / .18); box-shadow: inset 0 1px 0 hsl(0 0% 100% / .12), 0 8px 24px hsl(0 0% 0% / .45); --nb-line: hsl(0 0% 100% / .18); --nb-btn: hsl(0 0% 100% / .08); --nb-btn-line: hsl(0 0% 100% / .2); }
body[data-theme="dark"] #notebook::before { content: ''; position: absolute; inset: 0; z-index: -1; background: hsl(0 0% 0% / .22); pointer-events: none; }   /* a shade behind the blur on DARK, the glass itself stays clear */
.nb-head { display: flex; align-items: flex-start; justify-content: space-between; padding: 8px 8px 6px 14px; cursor: grab; user-select: none; touch-action: none; }
.nb-titles { display: flex; flex-direction: column; flex: 1 1 auto; min-width: 0; }
.nb-title { font-family: 'LW Title', var(--font-ui); font-size: 34px; letter-spacing: .04em; color: var(--fg); background: transparent; border: 0; outline: 0; padding: 0; width: 100%; min-width: 0; }
.nb-title:focus { color: var(--acc); }
.nb-subtitle { font-family: 'LW Title', var(--font-ui); font-size: 18px; letter-spacing: .04em; color: var(--fg-soft); background: transparent; border: 0; outline: 0; padding: 0; width: 100%; min-width: 0; margin-top: 2px; }
.nb-subtitle:focus { color: var(--acc); }
.nb-subtitle::placeholder { color: var(--dim); opacity: .6; }
#notebook[data-face="about"] .nb-titles, #notebook[data-face="projects"] .nb-titles,
#notebook[data-face="about"] .nb-title, #notebook[data-face="projects"] .nb-title,
#notebook[data-face="about"] .nb-subtitle, #notebook[data-face="projects"] .nb-subtitle { visibility: hidden; }
.nb-view { flex: 1 1 auto; min-height: 120px; overflow: auto; padding: 4px 2px; font: 14px/1.6 var(--font-ui); color: var(--fg); }
.nb-view h1, .nb-view h2, .nb-view h3 { margin: .6em 0 .3em; line-height: 1.2; font-weight: 700; color: var(--fg); }
.nb-view h1 { font-size: 22px; } .nb-view h2 { font-size: 18px; } .nb-view h3 { font-size: 15px; }
.nb-view p { margin: .35em 0; } .nb-view ul, .nb-view ol { margin: .3em 0 .3em 1.4em; } .nb-view li { margin: .1em 0; }
.nb-view code { font-family: var(--font-mono); font-size: 12px; padding: 0 4px; border-radius: 4px; background: hsl(0 0% 50% / .14); }
.nb-view pre { padding: 8px 10px; border-radius: 8px; background: hsl(0 0% 50% / .12); overflow: auto; } .nb-view pre code { padding: 0; background: transparent; }
.nb-view blockquote { margin: .4em 0; padding: 2px 10px; border-left: 2px solid var(--acc); color: var(--fg-soft); }
.nb-view table { border-collapse: collapse; margin: .4em 0; } .nb-view th, .nb-view td { padding: 3px 8px; border-bottom: 1px solid hsl(0 0% 50% / .25); }
.nb-view hr { border: 0; height: 1px; background: hsl(0 0% 50% / .3); margin: .8em 0; }
.nb-view a { color: var(--acc); }
.nb-view .katex { font-size: 1.08em; } .nb-view .katex-display { margin: .5em 0; overflow-x: auto; }
#notebook[data-mode="view"] .nb-text { display: none; } #notebook[data-mode="view"] .nb-view { display: block; }
#notebook[data-mode="edit"] .nb-view { display: none !important; }
.nb-mode, .nb-projects-btn { font-size: 12px; }
.nb-projectsface { gap: 8px; }
.pj-new { display: flex; gap: 6px; } .pj-path { flex: 1 1 auto; border: 1px solid hsl(0 0% 50% / .3); border-radius: 8px; background: var(--nb-btn); padding: 4px 8px; font: 13px var(--font-ui); color: var(--fg); }
.pj-save, .pj-export, .pj-import { height: 26px; padding: 0 12px; border-radius: 8px; border: 1px solid var(--nb-btn-line); background: var(--nb-btn); color: var(--fg-soft); font: 700 10px var(--font-ui); letter-spacing: var(--tr-wider); cursor: pointer; display: inline-flex; align-items: center; }
/* ══ WAVE 106 · THE HISTORY LIST ═══════════════════════════════════════════════════════════════
   Newest at the top, the way a stack reads.  Three states, and only ONE of them is a colour trick:
     · PAST     the ordinary ink — these happened and they hold.
     · CURRENT  where the instrument is standing.  Accent, plus a left rule, because "which row am I
                on" is the one thing you look for and colour alone is not enough to answer it.
     · FUTURE   the rows you stepped back from.  GREYED, NOT REMOVED — that is FL's behaviour and it
                is the honest one: they are still reachable until you do something new, and a list
                that deleted them would be lying about what the next click can still do.
   The engine keeps 60 states, but the card renders only the ten latest points. This is a glanceable
   recovery surface rather than another workspace; sequential Undo and Redo still reach the full ring. */
.hist-list { display: flex; flex-direction: column; gap: 1px; max-height: 260px; overflow: hidden auto;
  overscroll-behavior: contain; -webkit-overflow-scrolling: touch;
  scrollbar-width: thin; scrollbar-color: var(--acc) transparent; }
.hist-list::-webkit-scrollbar { width: 2px; }
.hist-list::-webkit-scrollbar-thumb { background: var(--acc); border-radius: 2px; }
.hist-row { display: flex; align-items: baseline; gap: 8px; width: 100%; text-align: left;
  border: 0; border-left: 2px solid transparent; background: transparent; cursor: pointer;
  padding: 3px 6px; border-radius: var(--r-sm); color: var(--fg); font: 12px/1.4 var(--font-ui); }
.hist-row:hover { background: hsl(0 0% 50% / .12); }
.hist-i { flex: 0 0 auto; min-width: 1.6em; text-align: right; color: var(--dim);
  font: var(--fs-micro)/1.4 var(--font-mono); font-variant-numeric: tabular-nums; }
.hist-lbl { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hist-current { border-left-color: var(--acc); color: var(--acc); }
.hist-current .hist-i { color: var(--acc); }
/* the stepped-back future: still there, still clickable, and visibly spent */
.hist-future { opacity: .42; }
.hist-future .hist-lbl { text-decoration: line-through; text-decoration-thickness: 1px; }
.hist-actions { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); width: 100%; }
.hist-actions > .trig { width: 100%; min-width: 0; padding-inline: 5px; }

/* WAVE 106 · THE PROJECT LIST SCROLLS, AND NOW IT SAYS SO.  It always had `overflow: auto`, but no
   scrollbar of its own while #rack two rules below wears an explicit 2-px accent one — so a list
   longer than the box gave a reader nothing to see and nothing to grab, on a face that already looked
   empty for a different reason (see rack.js's `show('projects')`).  It takes the RACK'S OWN
   scrollbar, not a new one, plus the two behaviours every other scroller in this lab has: the scroll
   stops at its own ends instead of dragging the notebook behind it, and iOS gets momentum. */
.pj-list { display: flex; flex-direction: column; gap: 2px; max-height: 44vh; overflow: hidden auto;
  overscroll-behavior: contain; -webkit-overflow-scrolling: touch;
  scrollbar-width: thin; scrollbar-color: var(--acc) transparent; }
.pj-list::-webkit-scrollbar { width: 2px; }
.pj-list::-webkit-scrollbar-thumb { background: var(--acc); border-radius: 2px; }
.pj-list::-webkit-scrollbar-track { background: transparent; }
/* WAVE 106 · THE ROOTS, shown above the list so SAVE AS can aim at one.  They wear the .pj-folder
   heading's own type — same micro size, same tracking — because a chip here NAMES the heading it
   scrolls you to; it is the same word, made pressable.  A 44-px target would tower over a 13-px
   field, so these take the notebook's own dense scale and rely on being a wide, well-spaced row. */
.pj-roots { display: flex; flex-wrap: wrap; gap: 4px; margin: 6px 0 2px; }
.pj-root { border: 1px solid hsl(0 0% 50% / .3); background: var(--nb-btn); color: var(--dim);
  border-radius: var(--r-pill); padding: 3px 9px; cursor: pointer;
  font: var(--fs-micro)/1.5 var(--font-ui); letter-spacing: var(--tr-wider); }
.pj-root:hover { color: var(--fg); border-color: color-mix(in srgb, var(--acc) 55%, transparent); }
.pj-root:active { background: var(--glass-press); }
.pj-folder { margin-top: 6px; font-size: var(--fs-micro); letter-spacing: var(--tr-wider); color: var(--dim); }
.pj-item { display: flex; align-items: center; gap: 8px; padding: 4px 6px; border-radius: 6px; } .pj-item:hover { background: hsl(0 0% 50% / .12); }
.pj-item .pj-name { flex: 1 1 auto; cursor: pointer; font-size: 13px; color: var(--fg); } .pj-item .pj-when { font-size: var(--fs-micro); color: var(--dim); }
.pj-item button { border: 0; background: transparent; color: var(--dim); cursor: pointer; font-size: 12px; } .pj-item button:hover { color: var(--acc); }
.pj-foot { display: flex; align-items: center; gap: 8px; font-size: var(--fs-micro); color: var(--dim); }
.pj-none { font-size: 12px; color: var(--dim); padding: 6px; }
.nb-tools { display: flex; gap: 4px; }
.nb-tools button { width: 24px; height: 24px; border-radius: 50%; border: 1px solid var(--nb-btn-line); background: var(--nb-btn); color: var(--fg-soft); cursor: pointer; font-size: 13px; line-height: 1; }
.nb-about { font: italic 700 12px Georgia, 'Times New Roman', serif; }
.nb-tools button:hover { color: var(--acc); }
.nb-face { display: flex; flex-direction: column; padding: 0 14px 12px; flex: 1 1 auto; min-height: 0; overflow: auto; }   /* the faces fill the glass: the resize handle sizes the notes and the preview */
.nb-text { flex: 1 1 auto; min-height: 120px; resize: none; border: 0; outline: 0; background: transparent; color: var(--fg); font: 13px/1.5 var(--font-ui); padding: 4px 0; }
.nb-text::placeholder { color: var(--dim); }
.nb-foot { display: flex; align-items: center; justify-content: space-between; margin-top: 6px; font-size: var(--fs-micro); letter-spacing: var(--tr-wide); color: var(--dim); }
.nb-foot button, .nb-dump, .ab-home { border-radius: 8px; border: 1px solid var(--nb-btn-line); background: var(--nb-btn); color: var(--fg-soft); font: 700 10px var(--font-ui); letter-spacing: var(--tr-wider); cursor: pointer; box-sizing: border-box; }
.nb-foot button { height: 26px; padding: 0 12px; }
.nb-dump, .ab-home { height: 30px; padding: 0 14px; line-height: 28px; }
.nb-aboutface { align-items: center; text-align: center; gap: 4px; padding-top: 4px; }
.nb-logo { display: flex; align-items: baseline; gap: 0; font-size: 35px; margin: 6px 0 4px; }
.nb-logo .lam { font-family: 'STIX Two Math', 'Latin Modern Math', 'Noto Serif', 'DejaVu Serif', serif; font-style: italic; font-weight: 700; font-size: 1.3em; line-height: 1; margin-right: -.04em; }
.nb-logo .word { font-family: 'LW Title', var(--font-ui); font-weight: 700; letter-spacing: .06em; color: var(--fg); }
.nb-logo .mark { align-self: center; height: 1.05em; width: 1.05em; margin-left: .05em; }
.ab-eyebrow { font-size: var(--fs-micro); letter-spacing: var(--tr-wider); color: var(--dim); margin-top: 6px; }
.ab-version { font-size: 12px; letter-spacing: .06em; color: var(--acc); }
.ab-tag { text-transform: uppercase; }
.ab-tagline { margin: 4px 0; font-size: 13px; line-height: 1.45; color: var(--fg); max-width: 34ch; }
.ab-credit { margin: 2px 0; font-size: 12px; line-height: 1.4; color: var(--fg-soft); }
.ab-team { color: var(--fg); }
.ab-fine { margin: 1px 0; font-size: 10.5px; color: var(--dim); }
.ab-fine code { font-family: var(--font-mono); }
.nb-aboutface a { color: var(--acc); }
.ab-rule { width: 60%; height: 1px; background: hsl(0 0% 50% / .25); margin: 8px 0; }
.ab-actions { display: flex; align-items: center; justify-content: center; gap: 24px; width: 100%; margin-top: auto; padding-top: 12px; flex: 0 0 auto; }
.nb-aboutface .ab-home { color: var(--fg-soft); text-decoration: none; }
.nb-dump:hover, .ab-home:hover { color: var(--fg); border-color: color-mix(in srgb, var(--acc) 45%, var(--nb-btn-line)); }
.nb-dump:active, .ab-home:active { transform: translateY(1px); background: var(--glass-press); }

.picker, .pk-n { background: transparent !important; box-shadow: none !important; }
/* the playhead slides away with the rack and fades back when the pointer nears its place */


#transport.mini { transition: transform .24s cubic-bezier(.23, 1, .32, 1), opacity .24s cubic-bezier(.23, 1, .32, 1); }
body.rack-hidden #transport.mini { transform: translate(-50%, 140px); opacity: 0; pointer-events: none; }
body.rack-hidden.transport-peek #transport.mini { transform: translateX(-50%); opacity: 1; pointer-events: auto; }


.sp-knobs { display: flex; flex-direction: column; align-items: center; gap: 1px; }
.sp-row .sp-knobs .k { width: 30px; gap: 0; }
.sp-row .sp-knobs .k-dial { width: 17px; height: 17px; }
.sp-row .sp-knobs .k-needle { height: 6px; width: 1.2px; }
.sp-row .sp-knobs .k-val { font-size: 8px; line-height: 1; padding: 0 3px; }
/* PHASE — the live one: it turns by itself, so it takes the accent the rest of the lab spends on live.
   ⚠ AS A RING OF SHADOW, NOT A BORDER, and that is not a detail: skin.css sets `border-color:
   transparent` on EVERY `.k-dial` and draws the puck with `--neu-raise` instead — "the puck stands
   proud; the groove ring is the only texture".  A border here would win on specificity and look
   wrong-footed against every other dial in the lab.  The accent ring is spelled exactly as skin.css
   already spells a lit dial (`.k.drag`, `.k.active`): the raise, plus one accent pixel outside it. */
.sp-row .sp-knobs .k.live .k-dial { box-shadow: var(--neu-raise), 0 0 0 1px color-mix(in srgb, var(--acc) 60%, transparent); }
/* RATE — a setting, not a reading: two pixels smaller, and the plain puck.  The 45-px finger band
   skin.css gives these dials on a phone sits on `.k-dial::before` and is absolute, so it does not
   shrink with the ink. */
.sp-row .sp-knobs .k.rate .k-dial { width: 15px; height: 15px; }
.sp-row .sp-knobs .k.rate .k-needle { height: 5px; }
/* 2 px accent scrollbars: the left rack scrolls on its right edge, the right rack on its left edge (direction: rtl on the
   container only — every card is set back to ltr, so the controls read and drag exactly as before) */
#rack, #rackL { scrollbar-width: thin; scrollbar-color: var(--acc) transparent; }
#rack::-webkit-scrollbar, #rackL::-webkit-scrollbar { width: 2px; }
#rack::-webkit-scrollbar-thumb, #rackL::-webkit-scrollbar-thumb { background: var(--acc); border-radius: 2px; }
#rack::-webkit-scrollbar-track, #rackL::-webkit-scrollbar-track { background: transparent; }
#rack { direction: rtl; }
#rack > * { direction: ltr; }
.k-val, .fd-val, .ro-val, .ro-sub, .dev-stat, .sp-e, .sp-nm, .keys-chip, .tbtn, #transport .time { font-variant-numeric: tabular-nums; }

/* ═══ WAVE 69 · .fx — A CLOSED FORM WITH LIVE SLOTS (kit.js `formula`) ════════════════════════════
 * Two faces on one line, on purpose: the EXPRESSION is the math face and a SLOT is `--font-num` with
 * tabular figures, so the mathematics reads as mathematics and the numbers substituted into it can
 * never dance.  The slot is also the brightest ink on the line, which is how the eye finds the values
 * that are moving WITHOUT anything having to move decoratively (MOTION-LAW gate 3). */
.fx { margin: 3px 0 0; display: flex; flex-direction: column; gap: 1px; }
.fx-l { display: block; font-size: var(--fs-small); line-height: 1.55; color: var(--ink-key); letter-spacing: 0; }
.fx-v { font-style: normal; font-family: var(--font-num); font-size: .92em; font-variant-numeric: tabular-nums;
        color: var(--fg); letter-spacing: var(--tr-tight); }
.fx.dim .fx-l { color: var(--ink-faint); }
/* SPECTRUM's live law never reflows the window: a number that grows (t, arg c) cannot wrap the line or
   change the card's height. Each slot holds its widest value; the line clips instead of wrapping. */
.sp-fx .fx-l { white-space: nowrap; overflow: hidden; text-overflow: clip; }
.sp-fx .fx-v { display: inline-block; text-align: right; }
.sp-fx .fx-v[data-s="lab"] { text-align: left; }
.sp-fx .fx-v[data-s="t"] { min-width: 7ch; }      /* t grows to four figures; E, |c| are fixed-format, arg c is at most 5 */
.sp-fx .fx-v[data-s="arg"] { min-width: 6ch; }
.sp-fx .fx-l + .fx-l { font-size: calc(var(--fs-small) * .93); }   /* the numbers line fits the card at its narrowest without clipping |c| */
/* THE PILL IS A 32-px STRIP AND A DERIVATION HAS NO PLACE IN IT.  The transport's REPEATS formula
   lives on the DOCKED card; undocked, the same DOM is restyled as a 560 x 32 pill with twelve seats
   in one flex row, and a two-line expression there would either be clipped or would break the row.
   It is `display: none`, so the pill still has exactly twelve seats and B74's claim stays true. */
#transport.mini .fx { display: none; }
.mb-group { position: relative; }
.mb-btn { height: 26px; padding: 0 10px; border: 0; border-radius: 7px; background: transparent; color: var(--fg-soft); font: var(--w-bold) var(--fs-tiny) var(--font-ui); letter-spacing: var(--tr-wider); cursor: pointer; }
.mb-btn:hover { background: transparent; color: var(--acc); }
.mb-list { position: absolute; left: 0; top: 30px; min-width: 240px; max-height: 60vh; overflow: auto; padding: 4px; border-radius: 10px; background: hsl(var(--glass-tint) / .97); border: 1px solid var(--glass-hairline); box-shadow: var(--glass-shadow); display: flex; flex-direction: column; }
.mb-item { display: flex; justify-content: space-between; align-items: baseline; gap: 18px; text-align: left; border: 0; background: transparent; color: var(--fg-soft); padding: 6px 10px; border-radius: 6px; font: var(--fs-small) var(--font-ui); cursor: pointer; white-space: nowrap; }
.mb-item:hover { background: var(--glass-raise); color: var(--fg); }
.mb-key { color: var(--dim); font-size: var(--fs-micro); letter-spacing: var(--tr-wide); }
.mb-sep { height: 1px; margin: 4px 8px; background: var(--glass-hairline); pointer-events: none; }


.mb-item > .mb-num { margin-left: auto; flex: 0 0 auto; display: inline-flex; align-items: center;
  justify-content: center; width: 16px; height: 16px; border-radius: 50%;
  background: var(--acc); color: var(--acc-ink);
  font: var(--w-bold) var(--fs-micro)/1 var(--font-num); font-variant-numeric: tabular-nums; }
.mb-item.queued { color: var(--fg); background: var(--acc-soft); }
/* WAVE 104 · the favourite mark.  It sits BEFORE any queue ordinal in the DOM, so the sibling rule
   below hands the auto margin to whichever of the two comes first and the pair never fights over it. */
.mb-item > .mb-fav { margin-left: auto; flex: 0 0 auto; color: var(--acc2); font-size: var(--fs-small); line-height: 1; }
.mb-item > .mb-fav ~ .mb-num { margin-left: 6px; }
.mb-item:disabled { color: var(--dim); opacity: .45; cursor: default; }                    /* nothing to UNDO / REDO: the item is there and says so */
.mb-item:disabled:hover { background: transparent; color: var(--dim); }
.sp-head .note { display: none; }
.sp-head .trig { flex: 1 1 0; min-width: 0; padding: 0 6px; }
/* THE NARROW SCREEN: the rack drops along the bottom.  This is the IPAD's rule (portrait 744–834) and a
   narrow desktop window's, and wave 51 left it exactly as it was.  A PHONE does not land here: skin.css's
   last block is a (hover: none) + size pair that comes later and overrides every property below. */
@media (max-width: 860px) {
  #rack { top: auto; bottom: 0; left: 0; right: 0; width: auto; height: 46vh; }
}
#title { position: absolute; left: calc(var(--rack-w) + 14px); top: 6px; z-index: var(--z-chrome); display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-2) var(--sp-4); border-radius: var(--r-pill); pointer-events: none; }
#title .lam { color: var(--acc); font-size: var(--fs-lead); font-weight: var(--w-bold); letter-spacing: var(--tr-wide); }
#title .word { color: var(--fg); font-size: var(--fs-lead); font-weight: var(--w-bold); letter-spacing: .14em; }
#title .mark { height: 1.05em; width: 1.05em; display: inline-block; vertical-align: -0.12em; }
#title .ms { display: none; }   /* WAVE 59: the `!important` here beat skin.css:151's `#title:hover .ms { display: inline }`
                                  regardless of specificity, so QWAVE-0 · HYDROGEN SHADOW LAB — the ONLY on-screen string
                                  containing the word "hydrogen" — could never render, on hover or otherwise.  The rule
                                  stays (the subtitle is a hover-level fact); only the sledgehammer goes. */
#badges { position: absolute; left: calc(var(--rack-w) + 340px); right: calc(var(--rack-w) + 56px); top: 9px; z-index: var(--z-chrome); display: flex; flex-wrap: wrap; justify-content: center; gap: var(--sp-2); pointer-events: none; }
#badges > * { pointer-events: auto; }
#sheet { position: absolute; right: var(--sp-4); top: 52px; z-index: var(--z-veil); width: min(440px, 80%); padding: var(--sp-4) var(--sp-5); border-radius: var(--r-lg);
  font-family: var(--font-sans); font-size: var(--fs-base); line-height: 1.5; color: var(--fg-soft); }
#sheet h3 { margin: 0 0 6px; font: var(--w-bold) var(--fs-tiny) var(--font-ui); letter-spacing: var(--tr-wider); color: var(--acc); }
#sheet p { margin: 0 0 8px; }
#sheet b { color: var(--fg); }
#banner { position: absolute; left: 50%; top: 40%; transform: translate(-50%, -50%); z-index: var(--z-banner); max-width: 520px; padding: var(--sp-5); border-radius: var(--r-lg);
  font-family: var(--font-sans); background: hsl(350 40% 18% / .95); border: 1px solid hsl(350 60% 50% / .6); }
/* WAVE 59 · THE BANNER IS THE ONE PANE THAT DOES NOT FOLLOW THE THEME, so its ink is written here and not
   inherited.  It paints its own dark maroon ground unconditionally (above), while `body { color: var(--fg) }`
   resolves to NEAR-BLACK on the shipped LIGHT theme — measured in the page at 1.19 : 1, and worse than a
   plain miss because showBanner() runs three thousand lines BEFORE applySettings(): the sentence appeared
   white-on-maroon and went DARK a second later.  It is the only thing a visitor without WebGPU ever sees,
   so it is written unconditionally — one string of ink on BOTH themes, 13.69 : 1 on the pane's own ground,
   with the heading at 6.64 : 1.  Nothing here is an accent (STYLE-LOCK's ruling stands). */
#banner, #banner p { color: hsl(0 0% 96%); }
#banner p { margin: 0; }
#banner h3 { margin: 0 0 6px; padding-right: 30px; font-family: var(--font-ui); letter-spacing: var(--tr-wide); color: var(--bad); }
/* …and a way out.  It sat at z-index 60 over the stage for the whole session with no dismiss and no
   pointer-events rule, so the message a broken browser could not read was also one it could not put down. */
#banner .banner-x { position: absolute; top: 8px; right: 8px; width: 26px; height: 26px; display: flex; align-items: center; justify-content: center;
  background: none; border: 0; border-radius: var(--r-sm); color: hsl(0 0% 96%); opacity: .75; font-size: var(--fs-lead); line-height: 1; cursor: pointer; }
#banner .banner-x::after { content: ''; position: absolute; inset: -9px; }    /* 26 px of ink, 44 px of finger — wave 51's rule, not a 44-px mark */
#banner .banner-x:hover, #banner .banner-x:focus-visible { opacity: 1; background: hsl(0 0% 100% / .12); }
/* ── transport strip (the master logical transport, §25) ───────────────── */
#transport { position: absolute; left: var(--sp-4); right: var(--sp-4); bottom: var(--sp-4); z-index: var(--z-chrome); display: flex; align-items: center; gap: var(--sp-2);
  height: 56px; padding: 0 var(--sp-3); border-radius: var(--r-lg); }
/* the subtitle-sized transport: one slim bar at the bottom centre */
#transport.mini { left: 50%; right: auto; transform: translateX(-50%); width: min(560px, calc(100% - 2 * var(--rack-w) - 40px)); height: 46px; bottom: 60px; padding: 2px 7px 3px; gap: 2px; border-radius: 16px; }   /* wave 92: the modulation work bar's box */
#transport.mini .tbtn { width: 26px; height: 26px; font-size: 11px; border-radius: 50%; }
#transport.mini .tbtn.play { width: 34px; }
#transport.mini .fd { flex: 1 1 auto; min-width: 120px; }   /* the play bar keeps its length: the readouts beside it are fixed-width */
#transport.mini .k { width: 36px; }
/* WAVE 65 · THE PILL'S WIDTH BUDGET, MEASURED RATHER THAN ASSUMED.  Adding the MOD seat is what
   made this worth checking, and the check found the row was ALREADY over: at 520 px the eleven
   fixed items and their gaps wanted 542, so `play` was rendering at 20.3 px of its 26 and the three
   step buttons at 18.9 of their 24 — every one of them silently flex-shrunk, on the shipped default.
   Four numbers close it, and each is MEASURED against the longest string its readout can hold rather
   than the one that happens to be on screen: the pill takes lab.css's own 560 in skin.css (where a
   520 had been contradicting it), MOD is 26 in the pill rather than 40, the `t` readout gives up 8
   (its worst, "12345.67  (300)", measures 68.2 in a box that is now 74) and REPEATS TAKES 12 —
   because `fmtPeriod`'s worst is "≈ 123456 a.u. · 2.987 ps" at 106.5 px, which the old 132 cleared
   and a naive trim would have clipped.  The row wants 556 in 560 and the SCRUB — the one
   `flex: 1 1 auto` in it — takes the rest.  Move a seat in this row and re-run the measurement. */
#transport.mini .ro { flex: 0 0 76px; min-width: 76px; width: 76px; }
#transport.mini .ro.period { flex: 0 0 120px; min-width: 120px; width: 120px; }
#transport.mini .tbtn.jump { font-size: 13px; }
#transport.mini .fd-label, #transport.mini .ro-label, #transport.mini .k-label { display: none; }
#transport.mini .k-val, #transport.mini .ro-val { font-size: 9px; }
/* docked into the rack as a card */
#transport.docked { position: static; transform: none; width: auto; height: auto; flex-wrap: wrap; padding: 6px; gap: 6px; border: 0; box-shadow: none; background: transparent; }


.dock-btn, .mod-exp { width: 34px; height: 34px; border-radius: 50%; flex: 0 0 auto; cursor: pointer;
  border: var(--glass-border) solid var(--glass-border-color); background: transparent;
  box-shadow: var(--glass-shadow); color: var(--ink-key); font-size: 11px;
  display: inline-flex; align-items: center; justify-content: center; }
.dock-btn > svg, .mod-exp > svg { filter: drop-shadow(0 1px 0 var(--glass-raise)); }
.dock-btn:hover, .mod-exp:hover { color: var(--fg); }
.dock-btn:active, .mod-exp:active { background: var(--glass-press); }
#transport .trig { min-width: var(--touch); }
#transport .fd { flex: 1 1 auto; min-width: 80px; }
#transport .k { width: 58px; }
#transport .ro { min-width: 118px; }
.tbtn { width: var(--touch); height: var(--touch); border: var(--glass-border) solid var(--glass-hairline); border-radius: var(--r-md); background: var(--glass-raise); color: var(--fg); font-size: 16px; display: inline-flex; align-items: center; justify-content: center; }
.tbtn:active { background: var(--glass-press); }
.tbtn.play { width: 56px; color: var(--acc-ink); background: var(--acc); border-color: transparent; box-shadow: var(--acc-glow); font-weight: var(--w-bold); }
.tbtn.play.on { background: hsl(var(--hue-acc) var(--sat-acc) 56%); }
/* WAVE 65 · THE MOD ARM — a small mark on a full seat.  The `.tbtn` 44-px box is kept exactly (the
   density law: grow the seat, never the ink) and only the WIDTH and the TYPE are narrowed to hold a
   word instead of a dingbat.  The glow is ACCENT B because modulation is a RELATIONSHIP, and that is
   the colour it already wears everywhere else in this instrument: the ⤢ lamp two seats away, and
   every dial out in the rack that a modulator is holding. */
.tbtn.modb { font-size: var(--fs-tiny); font-weight: var(--w-bold); letter-spacing: var(--tr-wide); color: var(--ink-key); }   /* the 44-px `.tbtn` box IS the seat — B70 walks it at the phone breakpoint and a 40 would fail the law */
.tbtn.modb.on { color: var(--acc2); border-color: color-mix(in srgb, var(--acc2) 55%, transparent); background: var(--acc2-soft); }
#transport.mini .tbtn.modb { width: 26px; border-radius: var(--r-pill); font-size: var(--fs-micro); }
.dev-stat { min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; text-align: right; font-size: var(--fs-tiny); letter-spacing: var(--tr-tight); color: var(--dim); font-variant-numeric: tabular-nums;
  padding: 0 var(--sp-2); }   /* the header's spacing lives in the STATUS's own padding, so it collapses with the status instead of stealing the eyebrow's width (wave 44) */
.dev-stat.live { color: var(--acc); }
.dev-stat.warn { color: var(--warn); }

/* ── §55 · THE FLOATING WINDOW ─────────────────────────────────────────────────────────────────────
 * ONE LAYER over the stage, pointer-transparent so the canvas gestures underneath are untouched, above
 * both racks and BELOW the notebook (+4) and the menus (+3), so a menu can always be opened over a
 * window.  Each floating card takes its own pointer events back and carries its own z-index, written by
 * the press that raised it. */
#floats { position: absolute; inset: 0; z-index: calc(var(--z-chrome) + 2); pointer-events: none; }


#floats > .dev { position: absolute; pointer-events: auto; width: var(--float-w, var(--rack-w));
  max-height: calc(100dvh - 16px); }
/* a window taller than the screen scrolls its BODY, which moves no control relative to any other */
#floats > .dev > .dev-body { min-height: 0; overflow: hidden auto; overscroll-behavior: contain; }
/* the rack a dragged window would land in says so while the finger is over it */
#rack.rack-drop, #rackL.rack-drop { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--acc) 55%, transparent); border-radius: var(--r-lg); }

/* COMPACT: a MODE, not a resize.  The body stands down and the header re-flows to a vertical rail that
   keeps the three things the reference keeps — the window's NAME, its POWER switch, its CLOSE — plus the
   way back.  Because the body is hidden rather than re-laid-out, FULL restores the interior exactly. */
/* the width rule above is `#floats > .dev`, an ID selector, so COMPACT has to answer at the same
   specificity or it loses silently — which it did, on the first run of the gate. */
#floats > .dev.compact { width: var(--rail-w); }
.dev.floating.compact .dev-stat { display: none; }
#rackAdd > svg { width: 16px; height: 16px; }
.dock-btn > svg, .mod-exp > svg { width: 13px; height: 13px; }
.sp-cap, .sturm-note { font-family: var(--font-sans); font-size: var(--fs-small); color: var(--dim); line-height: 1.35; padding: 0 2px 2px; }   /* the note's look, outside the ⓘ sweep */
.sp-cap b, .sturm-note b { color: var(--fg-soft); font-weight: var(--w-med); }

/* ── spectrum rail ──────────────────────────────────────────────────────── */
.ladder { position: relative; height: 92px; border-radius: var(--r-md); border: var(--glass-border) solid var(--glass-hairline); background: var(--glass-well); overflow: hidden; }
.ladder canvas { width: 100%; height: 100%; display: block; }
.sp-rows { display: flex; flex-direction: column; gap: 3px; }
.sp-row { display: grid; grid-template-columns: 4px 50px minmax(0, 1fr) 40px 28px 28px 22px; align-items: center; gap: 4px; height: 56px; padding: 0 4px 0 0;
  border-radius: 7px; border: var(--glass-border) solid var(--glass-hairline); background: hsl(0 0% 100% / .035); }
.sp-row.sel { border-color: color-mix(in srgb, var(--acc) 55%, transparent); background: var(--acc-soft); }
.sp-row.muted { opacity: .55; }
.sp-row.off { opacity: .35; }
.sp-band { width: 4px; height: 100%; border-radius: 7px 0 0 7px; background: var(--nc); }
.sp-id { display: flex; flex-direction: column; justify-content: center; line-height: 1.1; padding-left: 4px; cursor: pointer; min-height: var(--touch); }
.sp-name { font-size: var(--fs-lead); color: var(--fg); }
.sp-sub { font-size: var(--fs-micro); color: var(--ink-faint); letter-spacing: var(--tr-tight); font-variant-numeric: tabular-nums; }
.sp-row .fd { height: 40px; }
.sp-row .k { width: 46px; }
.sp-row .k .k-lbl { display: none; }
.sp-row .k .k-val { font-size: var(--fs-micro); }
.sp-row .k-dial { width: 28px; height: 28px; }
.sp-row .k-needle { height: 11px; }
.sp-b { width: 34px; height: 40px; border-radius: 6px; border: var(--glass-border) solid var(--glass-hairline); background: hsl(0 0% 100% / .05); color: var(--dim); font-size: var(--fs-tiny); font-weight: var(--w-bold); }
.sp-b.on { background: hsl(var(--hue-acc) var(--sat-acc) 62% / .32); color: #fff; }
.sp-b.solo.on { background: hsl(288 70% 62% / .35); }
.sp-b:active { background: var(--glass-press); }
.sp-x { width: 26px; height: 40px; border: 0; background: transparent; color: var(--ink-faint); font-size: 13px; border-radius: 6px; }
.sp-x:active { color: var(--bad); background: var(--glass-press); }
.pk-n { display: grid; grid-template-columns: 28px minmax(0, 1fr); align-items: start; gap: 4px; }
.pk-nl { font-size: var(--fs-small); color: var(--nc); padding-top: 9px; text-align: center; }
.pk-chips { display: flex; flex-wrap: wrap; gap: 3px; }
.pk-c { min-width: 38px; height: 34px; padding: 0 4px; border-radius: 5px; border: var(--glass-border) solid var(--glass-hairline); background: hsl(0 0% 100% / .04); color: var(--dim); font-size: var(--fs-tiny); }
.pk-c.on { background: color-mix(in oklab, var(--nc) 28%, transparent); color: var(--fg); border-color: color-mix(in oklab, var(--nc) 60%, transparent); }
.pk-c:active { background: var(--glass-press); }

/* ── frontier: the vortex overlay, the orbit spheres, the ladder plots ──── */
#fieldlines, #vortex, #particles, #kepler { position: absolute; inset: 0; width: 100%; height: 100%; display: block; pointer-events: none; z-index: 5; }
#fieldlines { z-index: 4; }   /* the classical field sits UNDER the quantum overlays */
#particles { z-index: 6; }
#kepler { z-index: 7; }
/* H: the interface hides, the stage takes the whole window */


body.ui-hidden #rackFav, body.ui-hidden #rackFavList,
body.ui-hidden #rack, body.ui-hidden #rackL, body.ui-hidden #rackToggle, body.ui-hidden #rackAdd, body.ui-hidden #rackAddList, body.ui-hidden #menubar, body.ui-hidden #badges, body.ui-hidden #title, body.ui-hidden #transport, body.ui-hidden #sheet { display: none !important; }
body.ui-hidden #lab { grid-template-columns: minmax(0, 1fr) 0; }
.keys-list { display: flex; flex-direction: column; gap: 2px; max-height: 260px; overflow: auto; }
.keys-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; font-size: var(--fs-tiny); }
.keys-label { color: var(--dim); }
.keys-chip { font: inherit; color: var(--fg); background: hsl(0 0% 100% / .06); border: 1px solid var(--glass-hairline); border-radius: 4px; padding: 1px 8px; cursor: pointer; min-width: 64px; }
.keys-chip.hot { border-color: var(--acc); color: var(--acc); }
/* wave 68: the one refusal this panel can make (TAB), said where the eye and the ear both get it */
.keys-say { font-size: var(--fs-tiny); color: var(--acc); margin-top: 6px; line-height: 1.4; }
.dyn-c { position: relative; height: 84px; border-radius: var(--r-md); border: var(--glass-border) solid var(--glass-hairline); background: var(--glass-well); overflow: hidden; }   /* the well, not a hard black: the light theme has its own (wave 46) */
.dyn-c canvas { width: 100%; height: 100%; display: block; }
.slice-c { width: 100%; aspect-ratio: 1 / 1; display: block; border-radius: var(--r-md); border: var(--glass-border) solid var(--glass-hairline);
  background: var(--glass-well); touch-action: none; cursor: grab; image-rendering: auto; }
.slice-c:active { cursor: grabbing; }
/* WAVE 101 · the palette's two step chips: a fixed seat that does not take a share of the row, and
   the drawn glyph at the size every other header chip in this lab uses (§55b). */
.row > .trig.pal-nav { flex: 0 0 auto; min-width: 34px; width: 34px; padding: 0; position: relative; }


.row > .trig.pal-nav::before {
  content: ""; position: absolute; top: 0; bottom: 0; left: -6px; right: -6px; }
.pal-nav > svg { width: 13px; height: 13px; display: block; margin: auto; }
.pal-wrap { display: grid; grid-template-columns: minmax(0, 1fr) 62px; gap: var(--sp-2); align-items: stretch; }
.pal-strip { width: 100%; height: 54px; display: block; border-radius: var(--r-sm); border: var(--glass-border) solid var(--glass-hairline); touch-action: none; cursor: crosshair; }
.pal-ring { width: 62px; height: 54px; display: block; border-radius: var(--r-sm); border: var(--glass-border) solid var(--glass-hairline); background: hsl(0 0% 0% / .25); }
.pal-color { width: 46px; height: var(--touch); padding: 2px; border-radius: var(--r-md); border: var(--glass-border) solid var(--glass-hairline); background: var(--glass-raise); }
.dyn-rows { display: flex; flex-direction: column; gap: 2px; }
.dyn-row { display: grid; grid-template-columns: 54px minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1fr); align-items: center; gap: 4px; padding: 3px 5px;
  border-radius: 5px; background: hsl(0 0% 100% / .04); }
.ladder-c { position: relative; height: 96px; border-radius: var(--r-md); border: var(--glass-border) solid var(--glass-hairline); background: var(--glass-well); overflow: hidden; }
.ladder-c canvas { width: 100%; height: 100%; display: block; }
.orbit-c { width: 100%; height: 156px; display: block; border-radius: var(--r-md); border: var(--glass-border) solid var(--glass-hairline); background: var(--glass-well); touch-action: none; }
.orbit-c.drive { border-color: hsl(38 70% 70% / .55); cursor: grab; }
.orbit-c.drive:active { cursor: grabbing; }
.orbit-rows { display: flex; flex-direction: column; gap: 3px; }
.orbit-row { display: grid; grid-template-columns: 4px 54px minmax(0, 1fr) minmax(0, 1fr); align-items: center; gap: 4px; min-height: 48px; padding: 0 4px 0 0;
  border-radius: 7px; border: var(--glass-border) solid var(--glass-hairline); background: hsl(0 0% 100% / .035); }
.orbit-band { width: 4px; height: 100%; border-radius: 7px 0 0 7px; background: var(--nc); }
.orbit-id { display: flex; flex-direction: column; justify-content: center; line-height: 1.1; padding-left: 4px; }
.orbit-row .ro { min-width: 0; }

/* ── shadow view ────────────────────────────────────────────────────────── */
.shadow-c { width: 100%; height: 236px; display: block; border-radius: var(--r-md); border: var(--glass-border) solid var(--glass-hairline); background: var(--glass-well); touch-action: none; }
.mol-c { width: 100%; height: 150px; display: block; border-radius: var(--r-md); border: var(--glass-border) solid var(--glass-hairline); background: var(--glass-well); }
.qcd-c { width: 100%; height: 150px; display: block; border-radius: var(--r-md); border: var(--glass-border) solid var(--glass-hairline); background: var(--glass-well); }
/* ATOMS: the shell ladder and the radials of the atom in force (wave 37) */
.atm-c { width: 100%; height: 152px; display: block; border-radius: var(--r-md); border: var(--glass-border) solid var(--glass-hairline); background: var(--glass-well); }
.atm-r { width: 100%; height: 120px; display: block; margin-top: 6px; border-radius: var(--r-md); border: var(--glass-border) solid var(--glass-hairline); background: var(--glass-well); }

/* ═══ wave 46 — THE MINIMALIST GRAPH ═══════════════════════════════════════════════════════════ */


.dev[data-id="observer"] .seg:has(> .seg-b:nth-child(6)) { display: grid; grid-template-columns: repeat(3, 1fr); grid-auto-rows: 1fr; }
.dev[data-id="observer"] .seg:has(> .seg-b:nth-child(6)) > .seg-b { min-width: 0; padding: 0 4px; }


/* ═══ wave 48 — HIDDEN IS THE FASTEST STATE · THE GRIP · THE BUSY MARK · THE WARNING ═══════════ */


body.ui-hidden #notebook, body.ui-hidden #rackPeek, body.ui-hidden #rackPeekL, body.ui-hidden #graphTip, body.ui-hidden #floats { display: none !important; }   /* wave 55: a floating window is interface, so H takes it out of paint with the rest */

/* (b) A RACK THAT HAS SLID AWAY IS NOT PAINTED EITHER.  opacity: 0 keeps an element in the compositor with its
   backdrop-filter live; visibility: hidden takes it out of paint AND KEEPS ITS scrollTop, which display: none
   would throw away (the wave-24 law: a rack comes back where you left it).  The delay lets the slide finish
   first, so hiding is still animated; peeking cancels the delay so it comes back instantly. */
body.rack-hidden #rack, body.rack-hidden #rackL { visibility: hidden; transition: transform .28s ease, opacity .28s ease, visibility 0s linear .28s; }
body.rack-hidden.rack-peek #rack, body.rack-hidden.rack-peek #rackL { visibility: visible; transition: transform .28s ease, opacity .28s ease, visibility 0s linear 0s; }
body.rack-hidden #rackAdd, body.rack-hidden #rackAddList { visibility: hidden; }
body.rack-hidden.rack-peek #rackAdd { visibility: visible; }
body.rack-hidden #transport.mini { visibility: hidden; transition: transform .28s ease, opacity .28s ease, visibility 0s linear .28s; }
body.rack-hidden.transport-peek #transport.mini { visibility: visible; transition: transform .28s ease, opacity .28s ease, visibility 0s linear 0s; }


.nb-grip { position: absolute; right: 0; bottom: 0; width: 22px; height: 22px; z-index: 4; cursor: nwse-resize; touch-action: none; }
.nb-grip::before { content: ''; position: absolute; right: 5px; bottom: 5px; width: 10px; height: 10px; opacity: .85;
  border-right: 2px solid var(--nb-line); border-bottom: 2px solid var(--nb-line); border-bottom-right-radius: 3px; }
.nb-grip:hover::before { opacity: 1; }


#busyMark { position: fixed; left: 0; top: 0; width: 22px; height: 22px; z-index: calc(var(--z-banner) + 20);
  pointer-events: none; box-shadow: none; text-shadow: none; will-change: transform, opacity;
  transform: translate3d(calc(var(--cx, -200px) + 15px), calc(var(--cy, -200px) + 15px), 0); }
#busyMark { animation: lw-busy-breathe 1.1s linear infinite alternate; }
#busyMark .mark { display: block; width: 22px; height: 22px; box-shadow: none; filter: none; }


.mod-logo .mark { display: block; width: 16px; height: 16px; margin: auto; box-shadow: none; filter: none; }
.mod-logo { display: flex; align-items: center; justify-content: center; }


#title .mark, .nb-logo .mark { transform-origin: 50% 50%; }
#title .mark.turn rect { animation-duration: var(--logo-turn, 1.2s); animation-timing-function: linear; animation-iteration-count: 1; }
#title .mark.busy rect, #busyMark .mark rect, .dev-loading .mark rect { animation-duration: var(--logo-turn, 1.2s); animation-timing-function: linear; animation-iteration-count: infinite; }


#title { transform-origin: left center; }
#title.menu-open { transform: none; }

/* (e3) §53c — THE '?' SHEET: every binding, built live from the table.  Same glass, same ink and the same seat as
   #sheet (which is the badges' EXACT/NUMERICAL panel) — a second panel, not a second design. */
/* It sits CLEAR OF THE RIGHT RACK rather than over it (#sheet is four paragraphs and can afford the overlap; this
   is thirty-eight rows and cannot), and the clamp keeps it inside a phone viewport where the rack is the whole width:
   its right edge is the rack's inner edge, but never further in than a 440-px pane can be and stay on screen. */


#warnPane { position: fixed; inset: 0; z-index: calc(var(--z-banner) + 40); display: flex; align-items: center; justify-content: center;
  padding: 1.5rem; background: hsl(0 0% 100% / .34); color: #000;
  /* 2026-09-11 (Josh): less blur, still a strong veil — the blur drops to just over half the glass blur and a faint
     ground takes over the legibility the blur used to carry, so the field reads through as shapes, not as haze */
  -webkit-backdrop-filter: blur(calc(var(--glass-blur) * .55)) saturate(.6); backdrop-filter: blur(calc(var(--glass-blur) * .55)) saturate(.6);
  font-family: var(--font-ui); opacity: 1; animation: lw-warn-in 350ms ease-out forwards;
  transition: opacity 350ms cubic-bezier(.16, .84, .44, 1); }
body[data-theme="dark"] #warnPane { color: #fff; background: hsl(0 0% 4% / .42); }


@supports not ((backdrop-filter: blur(2px)) or (-webkit-backdrop-filter: blur(2px))) {
  #warnPane { background: hsl(0 0% 100% / .88); }
  body[data-theme="dark"] #warnPane { background: hsl(0 0% 4% / .88); }
}
#warnPane.warn-out { opacity: 0; pointer-events: none; }
.warn-card { display: flex; flex-direction: column; align-items: center; text-align: center; max-width: 34em; width: 100%; margin: auto; }
.warn-sign { display: block; width: clamp(200px, 44vmin, 420px); height: auto; margin: 0 auto 2rem; user-select: none; pointer-events: none; }
.warn-title { margin: 0 0 1rem; font-size: clamp(1.1rem, 2.8vmin, 1.45rem); font-weight: 700; letter-spacing: .18em;
  line-height: 1.3; text-transform: uppercase; max-width: 14em; }
.warn-body { margin: 0 0 2.25rem; font-size: clamp(.95rem, 2vmin, 1.05rem); font-weight: 400; line-height: 1.65; max-width: 26em; }
.warn-btn { display: inline-block; background: transparent; color: inherit; font: inherit; font-size: .95rem; font-weight: 600;
  letter-spacing: .14em; text-transform: uppercase; padding: .8rem 2.5rem; border: 1px solid currentColor; border-radius: 4px;
  cursor: pointer; min-height: 44px; min-width: 44px; opacity: .82;
  transition: background-color 150ms ease, color 150ms ease, opacity 150ms ease; }
.warn-btn:hover, .warn-btn:focus-visible { opacity: 1; background: currentColor; outline: none; }
#warnPane .warn-btn:hover, #warnPane .warn-btn:focus-visible { color: #fff; background: #000; }
body[data-theme="dark"] #warnPane .warn-btn:hover, body[data-theme="dark"] #warnPane .warn-btn:focus-visible { color: #000; background: #fff; }
/* ═══ WAVE 81 · THE PLAYHEAD'S OTHER SEAT, AND THE TUNNEL BETWEEN THEM ════════════════════════
 * The pill has always lived at the bottom.  When the modulation plugin is dragged over that seat,
 * rack.js (`modDodge`) sends it to the top instead — and the move is a TUNNEL: it leaves through the
 * edge it is on and arrives through the opposite one, so the two halves read as one continuous
 * travel rather than a jump.  Both halves are `forwards`, so the pill holds the frame it lands on
 * until the script clears the animation.
 *   The X half of every transform is `-50%` because that is what centres the pill (§12); a keyframe
 * that wrote only Y would throw it to the left edge mid-flight. */
#transport.mini.at-top { top: calc(env(safe-area-inset-top) + 52px); bottom: auto; }

@media (prefers-reduced-motion: reduce) {
  /* "fewer and gentler, never zero": the palette turn and the busy breath are COLOUR and OPACITY and they stay
     (MOTION-LAW); what goes is the one transform-based motion the logo has, the menu's enlarge. */
  #warnPane { animation: none !important; transition: none !important; }
  #title { transition: none !important; }
  #title.menu-open { transform: none !important; }
  #warnPane { opacity: 1 !important; }
  #warnPane.warn-out { opacity: 0 !important; }
  /* ── WAVE 69 · THE HALF THE DOC SAID WAS MISSING, TAKEN BEFORE ANOTHER ANIMATION WAS ADDED ───────
   * MOTION-LAW's accessibility section names this list and says it will keep growing until somebody
   * closes it: the rack's 310-px slide losing on SPECIFICITY, the two fold chevrons, the card's
   * drag transform, and `.dev.dragging`.  It is a short list of selectors and one specificity fix.
   *   THE SPECIFICITY FIX IS THE WHOLE POINT of the first two rules.  `body.rack-hidden #rack` is
   * (1,1,1); skin.css's media block names `#rack` at (1,0,0) and therefore never reached the rule
   * that carries the transform.  Repeating the FULL selector inside the query is the fix, and it is
   * why a `transition: none` on `#rack` alone looked right and did nothing for four waves.
   *   WHAT STAYS: every opacity fade in the lab — the two rack chips, the rack's own fade, the value
   * tooltips, `.note` — because reduced motion is FEWER AND GENTLER, NEVER ZERO, and an opacity
   * change is exactly what the preference asks to keep.  Only transforms go. */
  body.rack-hidden #rack, body.rack-hidden #rackL,
  body.rack-hidden.rack-peek #rack, body.rack-hidden.rack-peek #rackL,
  body.rack-hidden #transport.mini, body.rack-hidden.transport-peek #transport.mini {
    transition-property: opacity, visibility; }
  body.rack-hidden #rack, body.rack-hidden #rackL, body.rack-hidden #transport.mini { transform: none; }
  .dev-fold > svg, .mod-fold > svg { transition: none; }
  /* the PORTED plugin declares its own motion tokens and this is what they are for — one line
     reaches every `var(--m2-motion-*)` transition in modwindow.css without touching its sheet */
  .mir-modwindow, .kwin-chiprail { --m2-motion-structural: 0s; --m2-motion-micro: 0s; }
}

/* ═══ wave 52 — W-MODWINDOW: the modulation window's own furniture ════════════════════════════════
 * The window is built from the kit (knob · sw · seg · trig · fader) and these classes are its
 * FURNITURE, not new widgets: the lists the kit's controls sit in, the value strip that answers the
 * contract's "a modulated parameter must be readable while it moves", and the chips that name a
 * macro and a source.  Every colour is a token; ACCENT B carries the relationship (macros, routes,
 * the sweep band, the lit EXPAND) and ACCENT A carries the live signal (the meter, the NOW dot),
 * which is the material law in STYLE-LOCK read literally.  The gap inside a block is 3 px and the
 * gap between blocks is 7 px, so space carries the hierarchy before a word is read. */
.mod-srcs, .mod-macros, .mod-targets { display: flex; flex-direction: column; gap: var(--sp-3); }
.mod-empty { font-size: var(--fs-tiny); color: var(--ink-faint); padding: 2px 1px; }
.mod-dead { opacity: .38; cursor: not-allowed; }
.mod-beats { flex: 0 0 auto; min-width: 92px; padding: 0 6px; font-size: var(--fs-small); font-variant-numeric: tabular-nums;
  color: var(--ink-key); align-self: center; white-space: nowrap; }
.mod-beats.live { color: var(--acc); }

/* a source: a head line (power · id · live meter · remove) over its own parameter row */
.mod-src, .mod-macro { display: flex; flex-direction: column; gap: var(--sp-1); }
.mod-srch, .mod-macroh { display: flex; align-items: center; gap: var(--sp-2); }
.mod-src.mod-off { opacity: .5; }                                    /* bypassed: the material says so, not a colour */
.mod-tag { font-size: var(--fs-micro); letter-spacing: var(--tr-wide); color: var(--ink-faint); flex: 0 0 auto; }
.mod-meter { position: relative; flex: 1 1 auto; min-width: 24px; height: 4px; border-radius: 2px; background: var(--glass-well); overflow: hidden; }
.mod-meter > i { position: absolute; left: 0; top: 0; bottom: 0; width: 0; background: var(--acc); }
.mod-x { flex: 0 0 auto; width: 22px; height: 22px; border-radius: 50%; border: var(--glass-border) solid var(--glass-hairline);
  background: var(--glass-raise); color: var(--dim); font-size: 12px; line-height: 1; }
.mod-x:hover { color: var(--fg); background: var(--glass-press); }
.mod-x { position: relative; }                                       /* the 44-px finger is the phone block's, skin.css §5 */
.mod-wave { flex: 0 1 110px; min-width: 96px; }                     /* the shape menu takes its own width, it does not eat the row */

/* a macro: the name, the ONE drive control, its reach, and a value that is a fader or a meter — never both */
.mod-name { flex: 1 1 60px; min-width: 40px; height: 30px; padding: 0 6px; border-radius: var(--r-sm);
  border: var(--glass-border) solid var(--glass-hairline); background: var(--glass-well); color: var(--fg); font-size: var(--fs-small); }
.mod-drive { flex: 1 1 90px; min-width: 78px; }        /* select.sel already carries the 44-px seat */
.mod-routes { flex: 0 0 auto; min-width: 18px; padding: 1px 5px; border-radius: var(--r-pill); font-size: var(--fs-micro);
  text-align: center; color: var(--acc2); background: var(--acc2-soft); font-variant-numeric: tabular-nums; }
.mod-val { flex: 1 1 auto; }
/* the DRIVEN row is a METER, not a fader with its pointers off: it does not look like something to grab */
.mod-mtr { position: relative; flex: 1 1 auto; min-width: 60px; height: 26px; border-radius: 6px; overflow: hidden;
  background: var(--glass-well); box-shadow: inset 0 0 0 1px var(--glass-hairline); }
.mod-mtr > i { position: absolute; left: 0; top: 0; bottom: 0; width: 0; background: color-mix(in srgb, var(--acc2) 30%, transparent);
  border-right: 1px solid var(--acc2); }
.mod-mtr > b { position: absolute; left: 7px; top: 50%; transform: translateY(-50%); font-size: var(--fs-micro);
  font-weight: var(--w-med); letter-spacing: var(--tr-wider); color: var(--acc2); }
.mod-mtr > em { position: absolute; right: 7px; top: 50%; transform: translateY(-50%); font-style: normal;
  font-size: var(--fs-tiny); color: var(--fg-soft); font-variant-numeric: tabular-nums; }

/* a routed target: the strip is the contract — base · sweep · now, all three at once */
.mod-target { display: flex; flex-direction: column; gap: var(--sp-1); }
.mod-th { display: flex; align-items: baseline; gap: var(--sp-2); }
.mod-tlabel { font-size: var(--fs-small); letter-spacing: var(--tr-wide); color: var(--fg-soft); }
.mod-target.mod-live .mod-tlabel { color: var(--acc); }
.mod-tgrp { font-size: var(--fs-micro); letter-spacing: var(--tr-wider); color: var(--ink-faint); }
.mod-strip { position: relative; height: 12px; border-radius: 3px; background: var(--glass-well);
  box-shadow: inset 0 0 0 1px var(--glass-hairline); overflow: hidden; }
.mod-span { position: absolute; top: 0; bottom: 0; background: color-mix(in srgb, var(--acc2) 40%, transparent);
  border-left: 1px solid var(--acc2); border-right: 1px solid var(--acc2); }
.mod-base { position: absolute; top: 0; bottom: 0; width: 2px; margin-left: -1px; background: var(--fg-soft); }
.mod-now { position: absolute; top: 50%; width: 7px; height: 7px; margin: -3.5px 0 0 -3.5px; border-radius: 50%; background: var(--acc); }
.mod-nums { display: flex; gap: var(--sp-2); }
.mod-n { display: flex; flex-direction: column; gap: 0; min-width: 0; flex: 1 1 auto; }
.mod-n > b { font-size: var(--fs-micro); font-weight: var(--w-med); letter-spacing: var(--tr-wider); color: var(--ink-faint); }
.mod-n > em { font-style: normal; font-size: var(--fs-small); color: var(--fg-soft); font-variant-numeric: tabular-nums; white-space: nowrap; }
.mod-n.now > em { color: var(--acc); }
.mod-n.span { flex: 1 1 110px; }
.mod-n.span > em { color: var(--acc2); }
.mod-rt { display: flex; align-items: center; gap: var(--sp-1); }
.mod-rt.mod-dormant { opacity: .45; }
.mod-mchip { flex: 0 0 auto; max-width: 74px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  padding: 2px 6px; border-radius: var(--r-pill); font-size: var(--fs-micro); color: var(--acc2); background: var(--acc2-soft); }
.mod-mm { flex: 1 1 60px; min-width: 52px; }

/* THE MINIMISED MODE.  The playhead's geometry is untouched — the EXPAND button takes a seat in the
   same flex row the dock button already sits in, so the pill's box does not move.  It lights ACCENT B
   while the modulation is running, which is how a user with the window shut can see it is still going. */
/* WAVE 92 · THIS RULE IS WHY THE CHIP TREATMENT DID NOT TAKE: it is 500 lines further down the
   sheet than §55's `.dock-btn, .mod-exp` block and won on source order at equal specificity —
   measured, `.mod-exp` read 22 px against the 34 the chip rule asked for.  It IS the chip now, so
   the two verbs are one declaration in two places rather than two answers. */
.mod-exp { width: 34px; height: 34px; border-radius: 50%; flex: 0 0 auto; cursor: pointer;
  border: var(--glass-border) solid var(--glass-border-color); background: transparent;
  box-shadow: var(--glass-shadow); color: var(--ink-key); font-size: 11px;
  display: inline-flex; align-items: center; justify-content: center; }
.mod-exp:hover { color: var(--fg); background: var(--glass-press); }
.mod-exp.live { color: var(--acc2); border-color: color-mix(in srgb, var(--acc2) 55%, transparent); background: var(--acc2-soft); }

/* A knob whose parameter a modulator is holding wears ACCENT A wherever it lives in the rack.
   Selecting the route's macro or source promotes it to ACCENT B in the host layer. */
.k.mod-held .k-dial { box-shadow: var(--neu-raise), 0 0 0 1px color-mix(in srgb, var(--acc) 65%, transparent); }
.k.mod-held .k-needle { background: var(--acc); }


.mod-srcs { gap: var(--sp-4); }                                      /* a gap INSIDE a card is at most half the gap between cards */
.mod-card { border: var(--glass-border) solid var(--glass-hairline); border-radius: var(--r-md);
  padding: var(--sp-2) var(--sp-1); gap: var(--sp-2); background: var(--glass-raise); }
.mod-cardb { display: flex; flex-direction: column; gap: var(--sp-2); }
/* THE HEADER WRAPS.  `.mod-srch` has never wrapped, and it did not need to while it carried three
   things; it now carries six, and on the PHONE the four round chips take their 44-px seats — 290 px
   of header in a 250-px card, which overflowed the rack rather than folding, and put two × buttons
   where elementFromPoint answers `#rack` (found by the 44-px walk, not by looking). */
.mod-card .mod-srch { flex-wrap: wrap; }
.mod-card.mod-min .mod-cardb { display: none; }                      /* FOLD is a HEIGHT act; the state is in the MODEL and round-trips */
.mod-x { display: inline-flex; align-items: center; justify-content: center; }
.mod-x > svg { width: 12px; height: 12px; display: block; }
.mod-fold > svg { transition: transform var(--t-fast) ease; }
/* ONE drawing for the caret, turned a quarter turn when the card is folded — device()'s own idiom */
.mod-card.mod-min .mod-fold > svg { transform: rotate(-90deg); }
.mod-x:disabled { opacity: .3; cursor: default; }

/* the picture: a well, and inside it the ONE renderer both kinds share */
.mod-graph { position: relative; border-radius: var(--r-sm); background: var(--glass-well);
  box-shadow: inset 0 0 0 1px var(--glass-hairline); overflow: hidden; }
svg.mod-cv { display: block; width: 100%; height: 108px; touch-action: none; cursor: crosshair; }
/* with the ladder on, the SHAPE stays under the stairs rather than being replaced by them */
.mod-cv.stepped .cv-line { stroke: var(--ink-faint); stroke-width: 1; }
.mod-cv.stepped .cv-fill { fill: color-mix(in srgb, var(--acc) 6%, transparent); }

/* the caption says what the picture IS; the capsule says what the source is DOING, in numbers, on
   the card — not in a hover tip (ANTI-PATTERN 4) */
.mod-capr { display: flex; align-items: baseline; gap: var(--sp-2); }
.mod-cap { flex: 1 1 auto; min-width: 0; font-size: var(--fs-micro); color: var(--ink-faint); line-height: 1.35; }
.mod-stat { flex: 0 0 auto; font-size: var(--fs-micro); color: var(--acc); white-space: nowrap; font-variant-numeric: tabular-nums; }
.mod-src.mod-off .mod-stat { color: var(--ink-faint); }
.mod-say { font-size: var(--fs-micro); color: var(--fg-soft); }
.mod-say:empty { display: none; }                                    /* a line that has nothing to say takes no room */

/* THE GLYPH ON A SHAPE BUTTON IS THE PRESET ITSELF, SAMPLED — a button can never draw a shape the
   engine would not produce, and it redraws MIRRORED when the mirror is what is loaded */
/* SIX BUTTONS, THREE TO A ROW.  The map's budget assumed a 286-px body; MEASURED, this window is
   274 px wide floating (--float-w is the rack's content box, less its scrollbar) and the card's own
   content box is 226 px — six 44-px seats in one row need 279 and would have come out at 36 px each.
   Three to a row is 74 px a seat at any width this window can take, and it never has to be checked. */
.mod-shapes { display: grid; grid-template-columns: repeat(3, 1fr); gap: 2px; }
.mod-sh { height: var(--touch); padding: 0; border: var(--glass-border) solid var(--glass-hairline);
  border-radius: var(--r-sm); background: var(--glass-raise); display: block; }
.mod-sh:hover { background: var(--glass-press); }
.mod-sh:active { background: var(--glass-press); }
.mod-sh.on { background: var(--acc-soft); border-color: color-mix(in srgb, var(--acc) 50%, transparent); }
.mod-sh.on .sh-p { stroke: var(--acc); }

/* the compact seats.  A 44-px SEAT with micro ink is the wave-51 rule read literally: grow the seat,
   never the mark — four chips fit a 268-px card row and none of their hit areas touch. */
.mod-chip { min-width: 44px; padding: 0 4px; gap: 3px; }
.mod-chip .sw-led { width: 6px; height: 6px; }
.mod-chip .sw-lbl { font-size: var(--fs-micro); letter-spacing: var(--tr-wide); }
.mod-mini { min-width: 44px; padding: 0 5px; }
.mod-mini .trig-l { font-size: var(--fs-micro); letter-spacing: var(--tr-wide); }
/* `.row > .sw` and `.row > .trig` are TWO classes and beat a one-class rule: a chip that stretched to
   the whole row is what that specificity looks like from the outside (measured: DOTTED at 226 px). */
.row > .mod-chip, .row > .mod-mini { flex: 0 0 auto; }
.mod-seg, .mod-bank { flex: 0 0 auto; }
/* a segment inside a shrink-to-fit `.segw` does not stretch, so its own min-width IS its finger:
   44 px here, never the kit's 40, because 40 + the 2-px gap is 42 and B70 measures the real region */
.mod-seg .seg-b, .mod-bank .seg-b { min-width: 44px; padding: 0 5px; font-size: var(--fs-micro); }
.mod-bankr { align-items: center; }
.mod-waver > .mod-wave { flex: 1 1 auto; min-width: 90px; }          /* the shape menu takes the rest of the row */


/* THE GRIP.  A dedicated handle carrying the macro's ordinal, because the row beside it holds a text
   field and a select and a draggable row would steal the caret and the menu. 22 px of ink; the phone
   block gives it the 44-px seat on the same law as `.mod-x` — grow the seat, never the mark. */
.mod-grip { flex: 0 0 auto; width: 24px; height: 24px; border-radius: 7px; padding: 0; cursor: grab;
  border: var(--glass-border) solid color-mix(in srgb, var(--acc2) 40%, var(--glass-hairline));
  background: var(--glass-raise); color: var(--acc2); font: inherit; font-size: var(--fs-micro);
  font-weight: var(--w-bold); font-variant-numeric: tabular-nums; touch-action: none;
  display: inline-flex; align-items: center; justify-content: center; }
.mod-grip:hover { background: var(--acc2-soft); }
/* LATCHED reads as latched (material semantics: press/latched = darker, recessed) — and the ordinal
   stays READABLE, which "accent ink on an accent ground" would not be on either theme. */
.mod-grip.armed { background: var(--acc2-soft); border-color: var(--acc2); cursor: grabbing;
  box-shadow: var(--neu-inset), 0 0 0 2px color-mix(in srgb, var(--acc2) 45%, transparent); }
.mod-macro.sel > .mod-macroh > .mod-grip { box-shadow: 0 0 0 1px var(--acc2); }

/* WHILE THE GESTURE IS LIVE.  There is no hover on a touch screen, so validity cannot be reported at
   the pointer (Serum's "+" cursor): it is reported ON THE SURFACE, for the whole gesture, and the
   invalid half RECEDES.  This is Massive's permanently-drawn slots without Massive's permanent
   noise — the sockets exist only while you are holding something. */
body.mod-arming .k[data-param] { position: relative; }
body.mod-arming .k[data-param].mod-drop > .k-dial {
  outline: 2px dashed color-mix(in srgb, var(--acc2) 55%, transparent); outline-offset: 3px; }
body.mod-arming .k[data-param].mod-drop > .k-lbl { color: var(--acc2); }
/* A CONTROL THIS MACRO ALREADY REACHES: solid, not dashed, and no highlight on press — a drop here
   SELECTS the existing route rather than adding a second (addRoute answers `already`). */
body.mod-arming .k[data-param].mod-drop.is-dup > .k-dial { outline-style: solid; outline-color: color-mix(in srgb, var(--acc2) 75%, transparent); }
/* the `+` cursor, drawn at the destination because a touch screen has no pointer to draw it at */
body.mod-arming .k[data-param].mod-drop.is-over > .k-dial {
  outline: 3px solid var(--acc2); outline-offset: 3px; box-shadow: var(--neu-raise), 0 0 0 7px var(--acc2-soft); }
body.mod-arming .sw, body.mod-arming .seg, body.mod-arming .trig, body.mod-arming .fd,
body.mod-arming select.sel, body.mod-arming .k:not([data-param]) { opacity: .45; }

/* THE POPOVER — press and hold 450 ms, or the right button.  One menu, two doors: everything Serum
   puts behind a right-click needs another road on a touch screen.  And it carries the half Serum is
   missing — with more than one macro on a control, Serum sends you to another tile or to the Mod
   Matrix; here the choice of which route the outer arc edits is ON the control. */
.mod-pop { position: fixed; z-index: calc(var(--z-banner) + 4); min-width: 210px; max-width: 280px;
  display: flex; flex-direction: column; gap: var(--sp-2); padding: var(--sp-3); border-radius: var(--r-md);
  border: var(--glass-border) solid var(--glass-border-color); background: hsl(var(--glass-tint) / .98);
  box-shadow: var(--glass-shadow), 0 10px 30px hsl(0 0% 0% / .28);
  -webkit-backdrop-filter: blur(var(--glass-blur)); backdrop-filter: blur(var(--glass-blur)); }
.mod-poph { display: flex; align-items: baseline; gap: var(--sp-2); }
.mod-poph > b { font-size: var(--fs-small); letter-spacing: var(--tr-wide); color: var(--fg); font-weight: var(--w-med); }
.mod-poph > span { font-size: var(--fs-micro); letter-spacing: var(--tr-wider); color: var(--acc2); }
.mod-poprts { display: flex; flex-wrap: wrap; gap: var(--sp-1); }
.mod-poprts > .grp-lbl { flex: 1 0 100%; }
.mod-poprts > .mod-mchip { border: 0; cursor: pointer; font: inherit; font-size: var(--fs-micro); }
.mod-poprts > .mod-mchip.on { box-shadow: inset 0 0 0 1px var(--acc2); font-weight: var(--w-bold); }
.km-panel { position: absolute; inset: 0; display: flex; flex-direction: column;
  background: hsl(var(--glass-tint) / .97); backdrop-filter: blur(var(--glass-blur, 22px)); }
.km-header { display: flex; justify-content: space-between; align-items: center; padding: 20px 24px;
  border-bottom: 1px solid var(--glass-hairline); flex: 0 0 auto; }
.km-header-titles { display: flex; flex-direction: column; gap: 4px; }
.km-eyebrow { font: var(--w-bold) var(--fs-micro) / 1 var(--font-ui); letter-spacing: .12em; text-transform: uppercase; color: var(--dim); }
.km-title { font: var(--w-bold) 22px / 1.1 var(--font-ui); letter-spacing: .03em; color: var(--fg); }
.km-close { width: 44px; height: 44px; border-radius: 50%; border: 1px solid var(--glass-border-color);
  background: var(--glass-raise); color: var(--fg); font-size: 20px; line-height: 1; cursor: pointer; }
.km-close:hover, .km-close:focus-visible { background: var(--glass-press); box-shadow: 0 0 0 2px var(--acc-soft); }

.km-columns { flex: 1; display: flex; gap: 24px; padding: 20px 24px; min-height: 0; }
.km-col { display: flex; flex-direction: column; min-height: 0; padding: 18px;
  background: var(--glass-well); border: 1px solid var(--glass-hairline); border-radius: 12px; }
.km-col-left { flex: 1.4; }
.km-col-right { flex: 1; min-width: 360px; }
.km-col-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 14px; flex: 0 0 auto; }
.km-col-title { font: var(--w-bold) var(--fs-micro) / 1 var(--font-ui); letter-spacing: .1em; text-transform: uppercase; color: var(--dim); }

.km-platform-switch { display: flex; gap: 2px; padding: 3px; border-radius: 24px; background: var(--glass-well); border: 1px solid var(--glass-hairline); }
.km-platform-btn { min-height: 44px; padding: 6px 14px; border-radius: 20px; border: 0; background: transparent;
  font: var(--w-bold) var(--fs-micro) / 1 var(--font-ui); color: var(--dim); cursor: pointer; }
.km-platform-active { background: var(--glass-press); color: var(--fg); box-shadow: var(--neu-raise); }

.km-legend { display: flex; align-items: center; gap: 12px; margin: 10px 0 14px; font: var(--fs-micro) / 1 var(--font-ui); color: var(--dim); flex-wrap: wrap; }
.km-legend-dot { width: 7px; height: 7px; border-radius: 50%; display: inline-block; margin-right: 5px; vertical-align: -1px; }
.km-legend-active { background: var(--acc); box-shadow: 0 0 6px var(--acc-soft); }
.km-legend-unbound { background: var(--ink-faint); }
.km-legend-shift-sample { padding: 1px 5px; border-radius: 3px; font-size: 10px; font-weight: 700; background: var(--acc2); color: var(--glass-hue-ink, #fff); }
.km-legend-text { color: var(--dim); }


.km-keyboard { display: flex; flex-direction: column; gap: 6px; }
.km-kb-row { display: flex; gap: 6px; }
.km-key { position: relative; flex: 1 0 0; min-height: 44px; padding: 4px 5px; border-radius: 6px;
  display: flex; flex-direction: column; justify-content: space-between; cursor: pointer;
  font: var(--font-ui); text-align: left; overflow: hidden; }
/* the 44-px finger over smaller ink, the way every other control in this lab does it */
.km-key::before { content: ''; position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: max(100%, 44px); height: max(100%, 44px); }
.km-key-w-1 { flex-grow: 1; } .km-key-w-esc { flex-grow: 1.25; } .km-key-w-bksp { flex-grow: 1.6; }
.km-key-w-tab { flex-grow: 1.5; } .km-key-w-backslash { flex-grow: 1.35; } .km-key-w-caps { flex-grow: 1.8; }
.km-key-w-enter { flex-grow: 2.05; } .km-key-w-lshift { flex-grow: 2.2; } .km-key-w-rshift { flex-grow: 1.8; }
.km-key-w-ctrl { flex-grow: 1.4; } .km-key-w-win { flex-grow: 1.3; } .km-key-w-alt { flex-grow: 1.3; }
.km-key-w-space { flex-grow: 5.2; } .km-key-w-arrow { flex-grow: 1; }
.km-key-bound { background: var(--glass-raise); border: 1px solid var(--glass-border-color); color: var(--fg); }
.km-key-unbound { background: var(--glass-well); border: 1px solid var(--glass-hairline); color: var(--ink-faint); }
.km-key-selected { outline: 2px solid var(--acc); outline-offset: -1px; box-shadow: 0 0 14px var(--acc-soft); }
.km-key-hovered, .km-key:hover, .km-key:focus-visible { background: var(--glass-press); border-color: var(--acc-soft); }
.km-key-recording { border: 2px solid var(--bad); animation: km-pulse 1s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce) { .km-key-recording { animation: none; }
}
.km-key-top { display: flex; justify-content: space-between; align-items: flex-start; gap: 3px; }
.km-key-shift-char { font-size: 10px; opacity: .5; }
.km-key-cap { font-size: 13px; font-weight: 700; text-align: center; color: inherit; }
.km-key-sub { font-size: 9px; color: var(--dim); letter-spacing: .02em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.km-badge { font-size: 9px; font-weight: 700; padding: 1px 4px; border-radius: 3px; color: #fff; }
.km-badge-ctrl { background: var(--acc); }
.km-badge-shift { background: var(--acc2); }
.km-badge-alt { background: color-mix(in oklch, var(--acc) 50%, var(--acc2)); }
.km-badge-combo { background: linear-gradient(90deg, var(--acc), var(--acc2)); }

.km-count { font: var(--w-bold) var(--fs-micro) / 1 var(--font-ui); color: var(--dim); }
.km-list { flex: 1; overflow-y: auto; display: flex; flex-direction: column; gap: 4px; scrollbar-width: thin; }
.km-action-row { width: 100%; min-height: 44px; padding: 8px 12px; border-radius: 6px; display: flex; align-items: center;
  border: 1px solid transparent; background: transparent; color: var(--fg); cursor: pointer; text-align: left; }
.km-action-row-selected { background: var(--acc-soft); border-color: var(--acc); }
.km-action-row-hovered, .km-action-row:hover, .km-action-row:focus-visible { background: var(--glass-press); }
.km-action-row-recording { background: var(--glass-press); box-shadow: inset 0 0 0 1px var(--bad); }
.km-chips { display: flex; gap: 4px; min-width: 90px; flex: 0 0 auto; }
.km-chip { padding: 3px 7px; font: 600 11px / 1 var(--font-mono); border-radius: 4px; }
.km-chip-mod { background: var(--glass-raise); border: 1px solid var(--glass-border-color); color: var(--fg-soft); }
.km-chip-key { background: var(--glass-press); border: 1px solid var(--glass-border-color); color: var(--fg); box-shadow: var(--neu-flat); }
.km-chip-unbound { color: var(--ink-faint); border: 1px dashed var(--glass-hairline); }
.km-chip-recording { color: var(--bad); border: 1px solid var(--bad); }
.km-action-text { flex: 1; font-size: 12px; text-align: left; margin-left: 10px; }
.km-action-label { font-weight: 500; color: var(--fg); }
.km-action-sep { color: var(--ink-faint); }
.km-action-desc { color: var(--dim); font-weight: 400; }

.km-footer { display: flex; justify-content: space-between; align-items: center; gap: 16px; padding: 18px 24px;
  border-top: 1px solid var(--glass-hairline); flex: 0 0 auto; }
.km-footer-actions { display: flex; gap: 12px; }
.km-btn { min-height: 44px; padding: 8px 18px; border-radius: 8px; display: flex; flex-direction: column; gap: 2px;
  background: var(--glass-raise); border: 1px solid var(--glass-border-color); cursor: pointer; }
.km-btn:hover, .km-btn:focus-visible { background: var(--glass-press); }
.km-btn-main { font: var(--w-bold) 12px / 1 var(--font-ui); letter-spacing: .05em; text-transform: uppercase; color: var(--fg); }
.km-btn-sub { font-size: 10px; opacity: .65; color: var(--fg-soft); }
.km-btn-record { border-color: var(--acc); }
.km-btn-recording { border-color: var(--bad); }
.km-footer-info { display: flex; flex-direction: column; align-items: flex-end; gap: 3px; text-align: right; }
.km-status { font: 600 12px / 1.3 var(--font-ui); color: var(--acc); }
.km-status-conflict { color: var(--bad); }
.km-hint { font: 11px / 1.3 var(--font-ui); letter-spacing: .02em; color: var(--dim); }

/* the phone gets one column: the drawn board needs ~700 px to stay legible, and below that the LIST is
   the manual — every binding, still rebindable, which is the part that has to work on a small screen */
@media (max-width: 860px) {
  .km-columns { flex-direction: column; padding: 12px; gap: 12px; }
  .km-col-left { display: none; }
  .km-col-right { min-width: 0; }
  .km-footer { flex-direction: column; align-items: stretch; }
  .km-footer-info { align-items: flex-start; text-align: left; }
}
#transport .mod-logo:active { transform:scale(.91); box-shadow:inset 0 2px 5px #0006; }

/* One retained custom colour; FOLLOW THEME bypasses it without erasing it. */
.stage-seat { display: flex; flex-direction: column; align-items: center; justify-content: flex-end; gap: 3px; align-self: flex-end; }
.stage-colour { width: 26px; height: 34px; padding: 2px; }
.stage-colour:focus-visible { box-shadow: 0 0 0 1.5px var(--acc); }
.stage-follow { height: 18px; min-height: 18px; padding: 0 6px; font-size: var(--fs-micro); letter-spacing: var(--tr-wide); }

/* the bundled demos row in the PROJECTS face */
.pj-demos { display: flex; align-items: center; gap: 8px; margin: 8px 0 4px; }
.pj-demos .ab-eyebrow { margin: 0; }
