/* fugue.fm playground — Sagmeister skin.
 *
 * Cream paper + warm anthracite + signal orange. Bricolage Grotesque italic
 * for the wordmark and panel index numbers; Space Grotesk for body chrome;
 * JetBrains Mono for code + meters; Caveat for the brand stamp + log line.
 *
 * Layout (rows, top → bottom):  chrome (56) · tabs (30) · main (1fr) · status (28).
 * Main is editor | panels-col (mixer + figure with tabs).
 *
 * The eval-flash + pattern-step CodeMirror hooks live at the bottom; the
 * editor view theme itself is set by EditorView.theme in main.js.
 */

/* ── Touch / hover policy ───────────────────────────────────────────────
 * iOS (and any `hover: none` pointer) APPLIES :hover on the first tap and
 * holds it until you tap elsewhere. A bare `:hover` therefore either (a) eats
 * the first tap on delegated-handler controls (the "needs two taps" bug) or
 * (b) leaves the last-tapped control stuck-highlighted (the "highlight that
 * follows my finger around / shows up on other buttons" bug). CSS has no
 * global off-switch for :hover, so the rule here is a convention: EVERY
 * :hover style lives behind `@media (hover: hover)`. Touch pointers never
 * match it and get the clean base style. Adding a hover rule? Gate it —
 * `grep -n ':hover' style.css` should show no bare (unguarded) match. */

:root {
    /* PAPER + INK — warm anthracite on cream. */
    --paper: #ece6d4;
    --surface: #f5f0de;
    --surface-2: #e0d9c2;
    --recess: #cec5a9;
    --hairline: #b8ad8e;
    --hairline-2: #d4cbac;
    --ink: #141414;
    --ink-2: #4a4640;
    --ink-3: #7a7361;
    --ink-4: #a59c82;

    /* SIGNAL — orange by default, swapped at runtime by the accent picker. */
    --signal: #ff551d;
    --signal-deep: #c93d10;
    --signal-soft: #ffd0bd;
    --signal-sel: rgba(255, 85, 29, 0.3);
    /* translucent accent for text selection; recomputed per-accent in JS */
    --signal-ink: #141414;

    /* REC — the record red. Independent of the accent so a take always reads
       the same colour whatever the signal is set to. */
    --rec: #e5342b;

    --display: 'Bricolage Grotesque', 'Space Grotesk', sans-serif;
    --sans: 'Space Grotesk', 'Helvetica Neue', Helvetica, Arial, sans-serif;
    --mono: 'JetBrains Mono', 'IBM Plex Mono', 'SF Mono', Menlo, Consolas, monospace;
    --hand: 'Caveat', 'Spectral', cursive;

    --chrome-h: 56px;
    --tabs-h: 30px;
    --status-h: 28px;

    /* iOS home-indicator inset — 0 on every non-notched device. Padded into
       the bottom-edge mobile chrome (nav + sheet) so nothing sits under the
       home bar. */
    --safe-b: env(safe-area-inset-bottom, 0px);
}

body.theme-dark {
    --paper: #161412;
    --surface: #221e1a;
    --surface-2: #2c2823;
    --recess: #0f0e0c;
    --hairline: #3a342a;
    --hairline-2: #2a261f;
    --ink: #f1e8d4;
    --ink-2: #b8ad92;
    --ink-3: #7a7363;
    --ink-4: #4a4538;
    --signal-soft: #4a2614;
}

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

html,
body {
    width: 100%;
    height: 100%;
    background: var(--paper);
    color: var(--ink);
    font-family: var(--sans);
    font-feature-settings: "kern" 1, "liga" 1, "tnum" 1, "ss01" 1;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    overflow: hidden;
    transition: background-color 220ms ease, color 220ms ease;
}

/* ── Page frame ─────────────────────────────────────────────────────── */
.page {
    width: 100%;
    height: 100vh;
    display: grid;
    grid-template-rows: var(--chrome-h) var(--tabs-h) minmax(0, 1fr) var(--status-h);
    position: relative;
}

body.zen .page {
    grid-template-rows: 36px 0 minmax(0, 1fr) 0;
}

body.zen .tabs,
body.zen .status {
    overflow: hidden;
    pointer-events: none;
}

/* ── Chrome ─────────────────────────────────────────────────────────── */
.chrome {
    display: flex;
    align-items: stretch;
    height: var(--chrome-h);
    background: var(--surface);
    border-bottom: 1.5px solid var(--ink);
    position: relative;
}

/* The spacer flexes the brand apart from everything right of it. Flex (not a
   counted grid track) so adding or removing a chrome button never reflows. */
.spacer {
    flex: 1;
}

body.zen .chrome {
    height: 36px;
    border-bottom: 1px solid var(--hairline-2);
    background: var(--paper);
}

body.zen .brand,
body.zen .cluster,
body.zen .theme-toggle,
body.zen .accents,
body.zen .docs {
    display: none;
}

body.zen .editor {
    grid-template-rows: minmax(0, 1fr);
}

body.zen .editor-head {
    display: none;
}

/* Brand cell — wordmark + version plate; both are "about" doors. */
.brand {
    padding: 0 22px;
    border-right: 1px solid var(--hairline);
    display: grid;
    grid-template-columns: auto;
    grid-template-rows: 1fr auto;
    align-items: end;
}

.wordmark {
    font-family: var(--display);
    font-weight: 700;
    font-style: italic;
    font-size: clamp(28px, 3.4vw, 38px);
    line-height: 1;
    letter-spacing: -0.035em;
    color: var(--ink);
    align-self: center;
    grid-row: 1 / 2;
    grid-column: 1 / 2;
    white-space: nowrap;
    display: inline-flex;
    align-items: baseline;
    padding-top: 4px;
    cursor: pointer;
}

.wordmark .dot {
    display: inline-block;
    width: 8px;
    height: 8px;
    background: var(--signal);
    border-radius: 50%;
    margin: 0 3px 4px 3px;
    flex: 0 0 auto;
}

.wordmark .tld {
    font-weight: 400;
    font-style: italic;
    color: var(--ink-2);
    font-size: 0.6em;
    letter-spacing: -0.02em;
    align-self: flex-end;
    margin-bottom: 4px;
    margin-left: 2px;
}

/* "about" cue — taught once on first run (JS adds .reveal for a few seconds),
   then a quiet hover affordance on the wordmark. The version pill is the
   other door. Handwriting kept at the house 16px (see the --hand pass). */
.about-cue {
    font-family: var(--hand);
    font-style: normal;
    font-weight: 500;
    font-size: 16px;
    line-height: 1;
    letter-spacing: 0;
    color: var(--signal);
    align-self: center;
    margin-left: 8px;
    white-space: nowrap;
    opacity: 0;
    transform: translateX(-5px);
    transition: opacity 0.22s ease, transform 0.22s ease;
    pointer-events: none;
}

.about-cue.reveal {
    opacity: 1;
    transform: none;
}

@media (hover: hover) {
    .wordmark:hover .about-cue {
        opacity: 1;
        transform: none;
    }
}

body.zen .about-cue {
    display: none;
}

body.zen .wordmark {
    font-size: 22px;
}

body.zen .wordmark .dot {
    width: 5px;
    height: 5px;
    margin-bottom: 2px;
}

.plate {
    grid-row: 2 / 3;
    grid-column: 1 / 2;
    display: flex;
    align-items: center;
    gap: 8px;
    padding-bottom: 6px;
    font-weight: 600;
    font-size: 8.5px;
    letter-spacing: 0.22em;
    text-transform: uppercase;
    color: var(--ink-3);
    white-space: nowrap;
}

/* The pill is an about door — clickable, with a hover colour shift as the
   affordance. No revealed label, though: it stays the plain version chip. */
.plate .pill {
    display: inline-flex;
    align-items: center;
    padding: 1px 6px;
    background: var(--ink);
    color: var(--surface);
    border: none;
    border-radius: 999px;
    font-family: var(--sans);
    font-size: 8px;
    letter-spacing: 0.2em;
    font-weight: 600;
    cursor: pointer;
    transition: background 0.14s ease, color 0.14s ease;
}

@media (hover: hover) {
    .plate .pill:hover {
        background: var(--signal);
        color: var(--signal-ink);
    }
}

/* cold — version not yet resolved: hollow pill so the em-dash placeholder
   reads as "loading", not a filled value. paintVersionPill() drops .cold. */
.plate .pill.cold {
    background: transparent;
    color: var(--ink-3);
    box-shadow: inset 0 0 0 1px var(--ink-3);
}

body.zen .plate {
    display: none;
}

/* Transport cluster — play · stop · record · share, icon-only.
   (The master meter that used to sit here was retired: the mixer panel
   already shows master + dBFS, so the chrome copy was a duplicate.) */
.cluster {
    display: flex;
    align-items: stretch;
    height: 100%;
    border-left: 1px solid var(--hairline);
    margin-left: 12px;
}

body.zen .cluster {
    display: none;
}

.tbtn {
    height: 100%;
    background: none;
    border: none;
    border-right: 1px solid var(--hairline);
    padding: 0 15px;
    color: var(--ink);
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: background 120ms ease, color 120ms ease;
}

@media (hover: hover) {
    .tbtn:hover {
        background: rgba(20, 20, 20, 0.04);
    }
}

.tbtn .glyph {
    width: 15px;
    height: 15px;
    display: grid;
    place-items: center;
    color: currentColor;
}

.tbtn.play.on {
    background: var(--signal);
    color: var(--signal-ink);
    border-right-color: var(--signal);
}

@media (hover: hover) {
    .tbtn.play.on:hover {
        background: var(--signal);
        filter: brightness(0.96);
    }
}

/* Idle pulse on the play button until the first start. */
@keyframes playIdlePulse {
    0% {
        box-shadow: inset 0 -2px 0 0 var(--signal);
    }

    50% {
        box-shadow: inset 0 -3px 0 0 var(--signal-deep);
    }

    100% {
        box-shadow: inset 0 -2px 0 0 var(--signal);
    }
}

.tbtn.play.idle {
    animation: playIdlePulse 1.6s ease-in-out infinite;
}

/* Record — always armable (never greyed). Pressing it starts playback and
   arms the take; pressing again finishes (playback keeps running). The red
   dot is the whole affordance. */
.tbtn.rec {
    color: var(--rec);
}

@media (hover: hover) {
    .tbtn.rec:hover {
        background: rgba(229, 52, 43, 0.08);
    }
}

/* Recording — the red dot breathes at a fixed ~1s. No bar, no fill flip:
   just the dot, so the state reads as "a take is rolling". */
.tbtn.rec.on {
    background: rgba(229, 52, 43, 0.10);
}

.tbtn.rec.on .glyph {
    animation: recBreath 1s ease-in-out infinite;
}

@keyframes recBreath {

    0%,
    100% {
        opacity: 1;
    }

    50% {
        opacity: 0.4;
    }
}

/* Bounce-length menu — a small popover under the record button. The button
   is wrapped so the menu can anchor to it without disturbing the icon row. */
.rec-wrap {
    position: relative;
    display: flex;
    height: 100%;
}

.rec-menu {
    position: absolute;
    top: 100%;
    right: 0;
    z-index: 60;
    margin-top: 6px;
    display: flex;
    flex-direction: column;
    min-width: 96px;
    padding: 5px;
    background: var(--surface);
    border: 1px solid var(--hairline);
    border-radius: 7px;
    box-shadow: 0 6px 22px rgba(0, 0, 0, 0.18);
}

.rec-menu[hidden] {
    display: none;
}

.rec-menu-cap {
    padding: 3px 7px 5px;
    font-size: 9px;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--ink-3);
}

.rec-menu button {
    padding: 6px 8px;
    background: none;
    border: none;
    border-radius: 5px;
    text-align: left;
    font: inherit;
    font-size: 12px;
    color: var(--ink);
    cursor: pointer;
}

/* Hover-capable only (see the touch / hover policy note at the top). */
@media (hover: hover) {
    .rec-menu button:hover {
        background: rgba(229, 52, 43, 0.10);
    }
}

.rec-menu button.on {
    color: var(--rec);
    font-weight: 600;
}

/* Theme toggle. */
.theme-toggle {
    display: flex;
    height: 100%;
}

.theme-toggle button {
    background: none;
    border: none;
    border-right: 1px solid var(--hairline);
    width: 34px;
    cursor: pointer;
    color: var(--ink-3);
    display: grid;
    place-items: center;
    transition: color 120ms ease, background 120ms ease;
}

.theme-toggle button.on {
    color: var(--ink);
    background: var(--paper);
}

@media (hover: hover) {
    .theme-toggle button:not(.on):hover {
        color: var(--ink-2);
        background: rgba(20, 20, 20, 0.03);
    }
}

.accents {
    display: flex;
    height: 100%;
    align-items: center;
    gap: 9px;
    padding: 0 16px;
    border-right: 1px solid var(--hairline);
}

.accent-dot {
    width: 14px;
    height: 14px;
    border-radius: 50%;
    border: 1.5px solid var(--ink);
    cursor: pointer;
    padding: 0;
    transition: transform 120ms ease;
}

@media (hover: hover) {
    .accent-dot:hover {
        transform: scale(1.15);
    }
}

.accent-dot.on {
    box-shadow: 0 0 0 2px var(--paper), 0 0 0 3.5px var(--ink);
}

.zen-btn {
    height: 100%;
    background: none;
    border: none;
    padding: 0 18px;
    font-family: var(--sans);
    font-weight: 700;
    font-size: 10px;
    letter-spacing: 0.22em;
    text-transform: uppercase;
    color: var(--ink);
    cursor: pointer;
    display: flex;
    align-items: center;
    gap: 8px;
    white-space: nowrap;
    transition: background 120ms ease, color 120ms ease;
}

@media (hover: hover) {
    .zen-btn:hover {
        background: rgba(20, 20, 20, 0.04);
        color: var(--signal);
    }
}

.zen-btn .glyph {
    width: 12px;
    height: 12px;
    display: grid;
    place-items: center;
}

body.zen .zen-btn {
    padding: 0 14px;
    font-size: 9.5px;
}

/* Docs pair — Learn + Ref share one bordered cell, split by an inner rule. */
.docs {
    display: flex;
    align-items: stretch;
    height: 100%;
    border-right: 1px solid var(--hairline);
}

.docs .cheat-btn {
    border-right: 1px solid var(--hairline);
}

.docs .cheat-btn:last-child {
    border-right: none;
}

/* ── Cheatsheet: header button + dismissible drawer ─────────────────────
 * The "Ref" button mirrors .zen-btn (right-side chrome). The drawer overlays
 * the panels column (absolute inset:0 inside .panels-col) so the editor never
 * reflows; it's non-modal — clicking back into the editor keeps it open. */
.cheat-btn {
    height: 100%;
    background: none;
    border: none;
    padding: 0 18px;
    font-family: var(--sans);
    font-weight: 700;
    font-size: 10px;
    letter-spacing: 0.22em;
    text-transform: uppercase;
    color: var(--ink);
    cursor: pointer;
    display: flex;
    align-items: center;
    gap: 8px;
    white-space: nowrap;
    transition: background 120ms ease, color 120ms ease;
}

@media (hover: hover) {
    .cheat-btn:hover {
        background: rgba(20, 20, 20, 0.04);
        color: var(--signal);
    }
}

.cheat-btn .glyph {
    width: 12px;
    height: 12px;
    display: grid;
    place-items: center;
}

body.zen .cheat-btn {
    display: none;
}

.cheat-drawer {
    position: absolute;
    inset: 0;
    z-index: 30;
    display: flex;
    flex-direction: column;
    background: var(--surface);
    border-left: 1.5px solid var(--signal);
    overflow: hidden;
}

.cheat-drawer[hidden] {
    display: none;
}

.cheat-head {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    height: 36px;
    padding: 0 14px;
    background: var(--surface-2);
    border-bottom: 1px solid var(--hairline);
}

.cheat-title {
    font-family: var(--sans);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.22em;
    text-transform: uppercase;
    color: var(--signal);
}

.cheat-actions {
    display: flex;
    align-items: center;
    gap: 6px;
}

/* Copy-all icon button — serialises the whole sheet to Markdown for an LLM.
 * Borderless ghost icon, paired with the ✕ close. On success the copy icon
 * swaps to a check; failure tints it deep-signal. */
.cheat-copyall {
    width: 22px;
    height: 22px;
    display: grid;
    place-items: center;
    background: none;
    border: none;
    border-radius: 2px;
    color: var(--ink-3);
    cursor: pointer;
    padding: 0;
    transition: color 120ms ease, background 120ms ease;
}

@media (hover: hover) {
    .cheat-copyall:hover {
        color: var(--signal);
    }
}

.cheat-copyall .ico-done {
    display: none;
}

.cheat-copyall.copied {
    color: var(--signal);
}

.cheat-copyall.copied .ico-copy {
    display: none;
}

.cheat-copyall.copied .ico-done {
    display: block;
}

.cheat-copyall.failed {
    color: var(--signal-deep);
}

.cheat-close {
    width: 20px;
    height: 20px;
    display: grid;
    place-items: center;
    background: none;
    border: none;
    color: var(--ink-3);
    font-family: var(--sans);
    font-size: 17px;
    line-height: 1;
    cursor: pointer;
    padding: 0;
    transition: color 120ms ease;
}

@media (hover: hover) {
    .cheat-close:hover {
        color: var(--ink);
    }
}

.cheat-body {
    flex: 1 1 auto;
    overflow-y: auto;
    padding: 12px 14px 18px;
}

.cheat-loading {
    font-family: var(--mono);
    font-size: 11px;
    color: var(--ink-3);
    padding: 8px 2px;
}

/* ── Cheatsheet content (the injected ./cheatsheet.html fragment) ──────── */
.cheat-body .cs-sec {
    margin-bottom: 16px;
}

/* The "Shape" skeleton — a fenced code block of a real, compiling patch. */
.cheat-body .cs-code {
    margin: 0 0 6px;
    padding: 9px 10px;
    background: var(--recess);
    border-left: 2px solid var(--signal);
    border-radius: 2px;
    font-family: var(--mono);
    font-size: 10px;
    line-height: 1.5;
    color: var(--ink);
    white-space: pre;
    overflow-x: auto;
}

.cheat-body h3 {
    margin: 0 0 7px;
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 8px;
    font-family: var(--sans);
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--ink);
}

.cheat-body h3.cs-warn {
    color: var(--signal);
}

.cheat-body h3.cs-warn::before {
    content: "!";
    font-family: var(--display);
    font-style: italic;
    font-weight: 700;
    font-size: 14px;
    line-height: 1;
}

.cheat-body .cs-sub {
    font-weight: 500;
    font-size: 9.5px;
    letter-spacing: 0.04em;
    text-transform: none;
    color: var(--ink-3);
}

.cheat-body .cs-table {
    width: 100%;
    border-collapse: collapse;
    margin: 0;
}

.cheat-body .cs-table td,
.cheat-body .cs-table th {
    text-align: left;
    vertical-align: top;
    padding: 4px 8px 4px 0;
    border-bottom: 1px solid var(--hairline-2);
    font-family: var(--sans);
    font-size: 11px;
    line-height: 1.5;
    color: var(--ink-2);
}

.cheat-body .cs-table th {
    width: 1%;
    white-space: nowrap;
    padding-right: 12px;
    font-size: 9.5px;
    font-weight: 700;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--ink-3);
}

.cheat-body .cs-guess {
    color: var(--ink-3);
}

.cheat-body .cs-dim {
    color: var(--ink-3);
}

.cheat-body .cs-note {
    margin: 6px 0 0;
    font-family: var(--sans);
    font-size: 10.5px;
    line-height: 1.6;
    color: var(--ink-2);
}

/* Concise bullet notes — same voice as .cs-note, one point per line.
   Typography lives on the <li> (the text-bearing element) so it never depends
   on inheritance from the <ul>. */
.cheat-body .cs-list {
    margin: 6px 0 0;
    padding-left: 15px;
    list-style: disc;
}

.cheat-body .cs-list li {
    margin: 2px 0;
    font-family: var(--sans);
    font-size: 10.5px;
    line-height: 1.5;
    color: var(--ink-2);
}

.cheat-body code {
    font-family: var(--mono);
    font-size: 10.5px;
    color: var(--ink);
    background: var(--recess);
    border-radius: 2px;
    padding: 1px 4px;
    white-space: nowrap;
}

/* Stage/transform/gotcha cells hold many code chips — let them breathe. */
.cheat-body .cs-table code,
.cheat-body .cs-note code,
.cheat-body .cs-list code {
    display: inline-block;
    margin: 2px 2px 2px 0;
}

.cheat-body code.cs-copy {
    cursor: pointer;
    transition: background 120ms ease, color 120ms ease;
}

/* Hover-capable only (see the touch / hover policy note at the top). */
@media (hover: hover) {
    .cheat-body code.cs-copy:hover {
        background: var(--signal);
        color: var(--signal-ink);
    }
}

.cheat-body code.cs-copy.copied {
    background: var(--signal-soft);
    color: var(--ink);
}

/* ── Tabs row ───────────────────────────────────────────────────────── */
.tabs {
    display: flex;
    align-items: stretch;
    background: var(--paper);
    border-bottom: 1px solid var(--hairline-2);
    overflow-x: auto;
}

.file-tab {
    background: none;
    border: none;
    border-right: 1px solid var(--hairline-2);
    padding: 0 16px;
    font-family: var(--mono);
    font-size: 11px;
    color: var(--ink-3);
    cursor: pointer;
    display: flex;
    align-items: center;
    gap: 10px;
    white-space: nowrap;
    position: relative;
    line-height: 1;
}

.file-tab .idx {
    font-family: var(--sans);
    font-weight: 700;
    font-size: 8.5px;
    letter-spacing: 0.1em;
    background: var(--surface);
    border: 1px solid var(--hairline-2);
    padding: 1px 4px;
    border-radius: 2px;
    color: var(--ink-4);
}

@media (hover: hover) {
    .file-tab:hover {
        color: var(--ink-2);
    }
}

.file-tab.active {
    color: var(--ink);
    background: var(--surface);
}

.file-tab.active .idx {
    background: var(--signal);
    border-color: var(--signal);
    color: var(--ink);
}

.file-tab.active::after {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    bottom: -1px;
    height: 3px;
    background: var(--signal);
}

.file-tab .dot {
    width: 5px;
    height: 5px;
    border-radius: 50%;
    background: var(--signal);
    opacity: 0;
}

.file-tab .dot.dirty {
    opacity: 1;
}

.file-tab .close {
    font-family: var(--sans);
    font-size: 13px;
    line-height: 1;
    color: var(--ink-4);
    opacity: 0;
    /* revealed on hover / active — layout stays stable */
    cursor: pointer;
    padding: 0 1px;
    border-radius: 2px;
    transition: opacity .12s, color .12s, background .12s;
}

.file-tab.active .close {
    opacity: 1;
}

@media (hover: hover) {
    .file-tab:hover .close {
        opacity: 1;
    }

    .file-tab .close:hover {
        color: var(--signal);
        background: var(--surface-2);
    }
}

.tab-add {
    background: none;
    border: none;
    border-right: 1px solid var(--hairline-2);
    padding: 0 12px;
    font-family: var(--sans);
    font-size: 15px;
    color: var(--ink-4);
    cursor: pointer;
    line-height: 1;
}

@media (hover: hover) {
    .tab-add:hover {
        color: var(--ink-2);
    }
}

/* ── Main grid ──────────────────────────────────────────────────────── */
.main {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(360px, 38vw);
    min-height: 0;
    background: var(--paper);
    overflow: hidden;
}

body.zen .main {
    grid-template-columns: 1fr;
}

body.zen .panels-col {
    display: none;
}

/* ── Editor ─────────────────────────────────────────────────────────── */
.editor {
    position: relative;
    overflow: hidden;
    background: var(--paper);
    border-right: 1px solid var(--hairline-2);
    display: grid;
    /* Head row matches the panels' `.panel-head` (36px) so the editor header and
     * the mixer/figure headers line up as one strip across the divider. */
    grid-template-rows: 36px minmax(0, 1fr);
}

.editor-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0 26px;
    border-bottom: 1px solid var(--hairline-2);
    background: var(--paper);
}

.editor-head .label {
    display: flex;
    align-items: baseline;
    gap: 12px;
}

.editor-head .label .num {
    font-family: var(--display);
    font-weight: 700;
    font-size: 18px;
    color: var(--signal);
    letter-spacing: -0.04em;
    line-height: 1;
}

.editor-head .label .name {
    font-family: var(--sans);
    font-weight: 700;
    font-size: 11px;
    letter-spacing: 0.22em;
    text-transform: uppercase;
    color: var(--ink);
}

.editor-head .label .meta {
    font-family: var(--hand);
    font-size: 16px;
    color: var(--ink-2);
    font-weight: 500;
    transform: translateY(1px) rotate(-1deg);
}

.editor-head .pos {
    font-family: var(--mono);
    font-size: 10px;
    color: var(--ink-3);
}

.editor-body {
    overflow: hidden;
    position: relative;
    min-height: 0;
}

.editor-body #editor-mount {
    height: 100%;
}

.editor-body .cm-editor {
    height: 100%;
}

.editor-body .cm-editor.cm-focused {
    outline: none;
}

/* Eval flash — orange wash on the evaluated lines. */
@keyframes evalFlash {
    from {
        background-color: rgba(255, 85, 29, 0.18);
    }

    to {
        background-color: rgba(255, 85, 29, 0);
    }
}

.cm-eval-flash {
    animation: evalFlash 0.5s ease-out forwards;
}

/* Pattern-step highlight — solid accent fill over the sounding atom.
 *
 * The catch: mini-notation is a string literal, and the `tags.string` rule in
 * main.js already paints its own `backgroundColor: var(--signal-soft)` on an
 * INNER span. That inner span sits on top of this mark, so a background set on
 * `.cm-pattern-active` alone is covered and never shows (only text-decoration,
 * which paints on the glyphs above all backgrounds, got through). So we style
 * the inner span directly — the descendant selector outranks the generated
 * string-tag class on specificity, no !important needed.
 *
 * Paint-only (background + color), so the line never reflows as steps mount and
 * unmount — NO padding/border/margin/font-weight (the original chip's jitter). */
.cm-pattern-active,
.cm-pattern-active span {
    background-color: var(--signal);
    color: var(--signal-ink);
    border-radius: 2px;
}

/* Compile/boot error — an overlay pinned to the bottom edge of the editor, so
 * it covers only the last lines rather than the whole buffer. Because it can
 * still hide an end-of-file line, the pane carries a ✕ to collapse it (and the
 * status bar grows a "show" button to re-surface it). A failed hot-swap keeps
 * the old engine playing, so this is a non-blocking notice, not a blackout.
 * `display: none` when hidden. */
.boot-error {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 40;
}

.boot-error[hidden] {
    display: none;
}

.boot-error-body {
    /* An orange rule + a tonal step up from the editor's --paper ground mark it
     * as a floating panel — no blurry drop shadow (foreign to this flat UI).
     * Caps at 40vh and scrolls vertically; horizontal scroll is confined to the
     * message (below) so a wide diagnostic never drags the header — and its ✕ —
     * off the visible edge. */
    border-top: 1.5px solid var(--signal);
    background: var(--surface);
    padding: 10px 14px;
    max-height: 40vh;
    overflow-y: auto;
    overflow-x: hidden;
}

/* Header row: the "! COMPILE ERROR" label on the left, a collapse button on
 * the right. Collapsing hides the overlay so a line beneath it is reachable;
 * the status-bar "show" button brings it back. */
.boot-error-headrow {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 8px;
}

/* Compact ✕ close button. */
.boot-error-collapse {
    flex: 0 0 auto;
    width: 20px;
    height: 20px;
    display: grid;
    place-items: center;
    background: none;
    border: none;
    color: var(--ink-3);
    font-family: var(--sans);
    font-size: 17px;
    line-height: 1;
    cursor: pointer;
    padding: 0;
    transition: color 120ms ease;
}

@media (hover: hover) {
    .boot-error-collapse:hover {
        color: var(--ink);
    }
}

.boot-error-head {
    display: flex;
    align-items: baseline;
    gap: 8px;
    font-family: var(--sans);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.22em;
    text-transform: uppercase;
    color: var(--signal);
}

/* Orange index marker — the same role the editor head's "A" or a panel's "01"
 * plays, recolored as an error glyph so the pane reads as a first-class head.
 * A ::before so JS setting the head's textContent (the label) never wipes it. */
.boot-error-head::before {
    content: "!";
    font-family: var(--display);
    font-style: italic;
    font-weight: 700;
    font-size: 15px;
    line-height: 1;
    letter-spacing: -0.04em;
}

.boot-error-msg {
    font-family: var(--mono);
    font-size: 11.5px;
    color: var(--ink);
    line-height: 1.4;
    /* `pre` (not pre-wrap) keeps the caret column-aligned under its token; the
     * message itself scrolls horizontally for long lines (so the header above
     * stays put). */
    white-space: pre;
    overflow-x: auto;
}

/* ── Inline @param slider — pinned to the right of its code line ─────
 * The CodeMirror widget mounts at the value token but is absolutely
 * positioned so the line's text (range annotation, modifiers) flows
 * naturally to its left while every slider lines up on the same right
 * edge. The line gets `position: relative` and a min-width that fills
 * the scroller, so the slider docks to the editor's right edge even
 * for short lines (and to the longest-line edge once horizontal scroll
 * is active). */
.cm-param-knob {
    display: inline-grid;
    place-items: center;
    position: relative;
    width: 16px;
    height: 16px;
    margin: 0 1px 0 8px;
    vertical-align: 3px;
    cursor: ns-resize;
    user-select: none;
}

.cm-param-knob .ring {
    position: absolute;
    inset: 0;
    border: 1.2px solid var(--hairline);
    border-radius: 50%;
    transition: border-color 120ms ease;
}

.cm-param-knob.dragging .ring {
    border-color: var(--ink-3);
}

@media (hover: hover) {
    .cm-param-knob:hover .ring {
        border-color: var(--ink-3);
    }
}

.cm-param-knob .ind {
    position: absolute;
    width: 1.6px;
    height: 5px;
    top: 2px;
    left: 50%;
    background: var(--ink);
    transform-origin: 50% 6px;
    border-radius: 1px;
}

.cm-param-knob.dragging .ind {
    background: var(--signal);
}

.cm-param-knob .dot {
    width: 2px;
    height: 2px;
    background: var(--ink);
    border-radius: 50%;
}

/* ── Panels column ──────────────────────────────────────────────────── */
.panels-col {
    position: relative;
    /* anchor for the absolutely-positioned cheatsheet drawer */
    background: var(--surface);
    border-left: 1px solid var(--hairline);
    overflow: hidden;
    display: grid;
    /* mixer · figure (flexes to fill). */
    grid-template-rows: minmax(140px, auto) minmax(0, 1fr);
    min-height: 0;
}

.panel {
    display: grid;
    grid-template-rows: 36px minmax(0, 1fr);
    border-bottom: 1px solid var(--hairline);
    background: var(--surface);
    min-height: 0;
}

.panel:last-child {
    border-bottom: none;
}

.panel-head {
    display: flex;
    align-items: center;
    padding: 0 14px 0 16px;
    border-bottom: 1px solid var(--hairline-2);
    background: var(--surface);
    gap: 10px;
}

.panel-head .num {
    font-family: var(--display);
    font-weight: 700;
    font-size: 14px;
    color: var(--signal);
    letter-spacing: -0.04em;
    line-height: 1;
    min-width: 18px;
}

.panel-head .name {
    font-family: var(--sans);
    font-weight: 700;
    font-size: 10.5px;
    letter-spacing: 0.22em;
    text-transform: uppercase;
    color: var(--ink);
}

.panel-head .meta {
    font-family: var(--hand);
    font-size: 16px;
    color: var(--ink-2);
    flex: 1;
    margin-left: 4px;
    line-height: 1;
    transform: translateY(1px) rotate(-1deg);
}

.panel-body {
    overflow: hidden;
    min-height: 0;
    position: relative;
    display: flex;
    flex-direction: column;
}

/* ── Mixer ──────────────────────────────────────────────────────────── */
/* Scrollable channel list. `display: block` deliberately overrides the shared
 * `.panel-body { display: flex }` — a block scroll-container is the right model
 * for a vertical list. `max-height` caps it (the mixer's grid track is
 * `minmax(140px, auto)`, so the height has to be bounded here). No bottom
 * padding: a scroll container counts its bottom padding as scrollable area, so
 * any would show as a gap below the last strip at the scroll end. */
.mixer-body {
    display: block;
    overflow-y: auto;
    max-height: 35vh;
}

/* Six columns: idx · name · slider+vu · m/s · dB. */
.channel-strip {
    display: grid;
    grid-template-columns: 18px minmax(74px, 0.9fr) minmax(0, 2fr) 56px 50px;
    align-items: center;
    column-gap: 10px;
    height: 34px;
    padding: 0 16px;
    border-bottom: 1px dashed var(--hairline-2);
    position: relative;
}

.channel-strip:last-child {
    border-bottom: none;
}

@media (hover: hover) {
    .channel-strip:hover {
        background: rgba(255, 85, 29, 0.04);
    }
}

.channel-strip .idx {
    font-family: var(--mono);
    font-size: 9.5px;
    font-weight: 700;
    color: var(--ink-4);
}

.channel-strip .label {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.channel-strip .label .key {
    font-family: var(--sans);
    font-weight: 700;
    font-size: 12px;
    color: var(--ink);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.channel-strip.muted .label .key {
    color: var(--ink-3);
}

/* Slider + integrated VU — the slider's track is the meter. */
.gain-slider {
    position: relative;
    width: 100%;
    height: 18px;
    cursor: ew-resize;
    user-select: none;
    display: flex;
    align-items: center;
}

.gain-slider .track {
    position: relative;
    width: 100%;
    height: 6px;
    background: var(--recess);
    border: 1px solid var(--hairline);
    overflow: visible;
    transition: border-color 120ms ease;
}

.gain-slider.dragging .track {
    border-color: var(--ink-3);
}

@media (hover: hover) {
    .gain-slider:hover .track {
        border-color: var(--ink-3);
    }
}

.gain-slider .rms {
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    background: var(--ink);
    opacity: 0.85;
}

.channel-strip.muted .gain-slider .rms {
    background: var(--ink-4);
}

.gain-slider .peak {
    position: absolute;
    top: -2px;
    bottom: -2px;
    width: 1.5px;
    background: var(--signal);
}

.gain-slider .handle {
    position: absolute;
    top: 50%;
    width: 4px;
    height: 18px;
    background: var(--ink);
    transform: translate(-50%, -50%);
    border-radius: 1px;
    box-shadow: 0 0 0 1px var(--paper);
}

.gain-slider.dragging .handle {
    background: var(--signal);
}

.gain-slider.clip .track {
    box-shadow: inset 0 0 0 1px var(--signal);
}

.ms-buttons {
    display: flex;
    gap: 3px;
    justify-content: center;
}

.ms-buttons button {
    width: 22px;
    height: 19px;
    border: 1.2px solid var(--ink);
    background: var(--paper);
    font-family: var(--sans);
    font-weight: 700;
    font-size: 9px;
    color: var(--ink);
    cursor: pointer;
    line-height: 1;
    border-radius: 0;
}

@media (hover: hover) {
    .ms-buttons button:hover {
        background: var(--surface-2);
    }
}

.ms-buttons button.on.mute {
    background: var(--ink);
    color: var(--paper);
}

.ms-buttons button.on.solo {
    background: var(--signal);
    color: var(--ink);
    border-color: var(--signal);
}

.ms-buttons button:disabled {
    opacity: 0.35;
    cursor: not-allowed;
}

/* Live-MIDI arm (docs/midi.md): the MIDI-connector glyph lights when armed and
   the strip glows — the keyboard now plays this voice, overriding its pattern. */
.ms-buttons button.arm {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
}

.ms-buttons button.arm svg {
    width: 14px;
    height: 14px;
    display: block;
}

/* The two armed modes read as how *full* the button is (docs/midi.md):
   override  = a diagonal HALF fill — live plays *over* the pattern (it's still
               there underneath);
   MIDI-only = a FULL fill — the pattern is muted, only the keyboard sounds.
   The note keeps the ink colour (not overridden) so it reads on the unfilled
   half; the accent border marks it armed in either mode. */
.ms-buttons button.on.arm {
    background: linear-gradient(135deg, var(--signal) 0 50%, var(--paper) 50% 100%);
    border-color: var(--signal);
}

.ms-buttons button.on.only.arm {
    background: var(--signal);
}

.db-readout {
    font-family: var(--mono);
    font-size: 10.5px;
    text-align: right;
    color: var(--ink-3);
    font-feature-settings: "tnum" 1;
}

.db-readout .neg {
    color: inherit;
}

.channel-strip:not(.muted) .db-readout {
    color: var(--ink);
}

/* Pinned to the bottom of the scroll viewport: voice strips scroll above it,
 * master stays put. Sticky (not a separate container) keeps the DOM flat — the
 * opaque `--surface-2` background + z-index stop scrolling strips bleeding
 * through, and the shadow lifts it when there's content scrolling underneath. */
.channel-strip.master-strip {
    position: sticky;
    bottom: 0;
    z-index: 2;
    border-top: 1.5px solid var(--ink);
    background: var(--surface-2);
    box-shadow: 0 -6px 12px -6px rgba(0, 0, 0, 0.18);
    border-bottom: none;
}

.channel-strip.master-strip .label .key {
    font-family: var(--display);
    font-weight: 700;
    font-style: italic;
    font-size: 16px;
    letter-spacing: -0.02em;
}

/* ── Figure pane ────────────────────────────────────────────────────── */
.figure-panel {
    grid-template-rows: 36px minmax(0, 1fr) 28px;
}

.figure-panel .panel-head {
    gap: 6px;
    padding-right: 8px;
}

.fig-tabs {
    display: flex;
    align-items: stretch;
    flex: 1;
    height: 100%;
    margin-left: 4px;
}

.fig-tab {
    background: none;
    border: none;
    padding: 0 10px;
    cursor: pointer;
    font-family: var(--sans);
    font-weight: 700;
    font-size: 10px;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    color: var(--ink-3);
    border-right: 1px solid var(--hairline-2);
    line-height: 1;
    transition: color 120ms ease, background 120ms ease;
    position: relative;
}

/* Hover-capable only (see the touch / hover policy note at the top). */
@media (hover: hover) {
    .fig-tab:hover {
        color: var(--ink-2);
    }
}

.fig-tab.active {
    color: var(--ink);
    background: var(--paper);
}

.fig-tab.active::after {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    bottom: -1px;
    height: 2px;
    background: var(--signal);
}

.right-meta {
    display: flex;
    align-items: center;
    gap: 12px;
}

.spec-ctl {
    display: flex;
    align-items: center;
    gap: 12px;
}

.spec-ctl.hidden {
    display: none;
}

.ctl {
    display: flex;
    flex-direction: column;
    gap: 1px;
    cursor: ns-resize;
    user-select: none;
}

.ctl .lbl {
    font-family: var(--sans);
    font-weight: 700;
    font-size: 8.5px;
    letter-spacing: 0.22em;
    text-transform: uppercase;
    color: var(--ink-3);
}

.ctl .cv {
    font-family: var(--mono);
    font-size: 11px;
    line-height: 1;
    color: var(--ink);
    font-feature-settings: "tnum" 1;
}

.ctl .cv i {
    font-style: italic;
    font-family: var(--mono);
    font-size: 9px;
    color: var(--ink-3);
    margin-left: 1px;
}

@media (hover: hover) {
    .ctl:hover .cv {
        color: var(--signal);
    }
}

.hold-btn {
    background: none;
    border: none;
    font-family: var(--sans);
    font-weight: 700;
    font-size: 9.5px;
    letter-spacing: 0.22em;
    text-transform: uppercase;
    color: var(--ink-3);
    padding: 0 10px;
    cursor: pointer;
    display: flex;
    align-items: center;
    gap: 6px;
    line-height: 1;
}

.hold-btn .led {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--recess);
    border: 1px solid var(--ink);
}

@media (hover: hover) {
    .hold-btn:hover {
        color: var(--ink-2);
    }
}

.hold-btn.on {
    color: var(--ink);
}

.hold-btn.on .led {
    background: var(--signal);
    border-color: var(--signal);
}

.hold-btn.hidden {
    display: none;
}

.figure-body {
    overflow: hidden;
    position: relative;
    padding: 14px 16px 8px;
    background: var(--paper);
}

.fig-panel {
    position: absolute;
    inset: 0;
    padding: 14px 16px 8px;
    display: none;
}

.fig-panel.active {
    display: block;
}

.figure-cap {
    border-top: 1px solid var(--hairline-2);
    padding: 8px 16px;
    font-family: var(--hand);
    font-size: 16px;
    color: var(--ink-3);
    font-weight: 500;
    line-height: 1.1;
    display: flex;
    align-items: center;
    background: var(--surface);
}

/* SVG figure styling — class names match the existing main.js drawers. */
.fig-svg {
    width: 100%;
    height: 100%;
    display: block;
    overflow: visible;
}

.fig-svg .ax-soft {
    stroke: var(--hairline-2);
    stroke-width: 1;
    fill: none;
}

.fig-svg .ax {
    stroke: var(--hairline-2);
    stroke-width: 1;
    fill: none;
}

.fig-svg .ax-strong {
    stroke: var(--hairline);
    stroke-width: 1;
    fill: none;
}

.fig-svg .ink-line {
    stroke: var(--ink);
    stroke-width: 1.4;
    fill: none;
}

.fig-svg .ink-fill {
    fill: var(--ink);
}

.fig-svg .accent {
    stroke: var(--signal);
    stroke-width: 1.2;
    fill: none;
}

.fig-svg text {
    font-family: var(--mono);
    font-size: 9.5px;
    fill: var(--ink-2);
}

.fig-svg text.tag {
    font-family: var(--sans);
    font-weight: 700;
    font-size: 8.5px;
    letter-spacing: 0.22em;
    text-transform: uppercase;
    fill: var(--ink-3);
}

.fig-svg .dash {
    stroke-dasharray: 3 4;
}

/* ── Status bar — dark with orange accents ──────────────────────────── */
.status {
    /* A dark slab in light mode (inverted from the cream chrome) and a raised
     * dark panel in dark mode — NOT the bright bar the --ink/--paper swap would
     * otherwise produce. The foreground stays constant (cream text, dark ink on
     * the orange chip) in both themes, so it's pinned to local vars that don't
     * flip; only the ground tone changes per theme (body.theme-dark .status). */
    --status-fg: #ece6d4;
    /* cream text — stable across themes */
    --status-kbd-ink: #141414;
    /* dark ink on the orange kbd chip — stable */
    display: flex;
    align-items: center;
    background: var(--ink);
    color: var(--status-fg);
    border-top: 1.5px solid var(--ink);
    padding: 0 22px;
    height: var(--status-h);
    font-family: var(--mono);
    font-size: 10.5px;
    gap: 22px;
    overflow: hidden;
}

body.theme-dark .status {
    /* Dark mode: a raised dark panel with a subtle hairline, not the cream bar
     * the --ink/--paper inversion would produce. */
    background: var(--surface-2);
    border-top-color: var(--hairline);
}

.status .chunk {
    display: flex;
    align-items: baseline;
    gap: 7px;
    flex: 0 0 auto;
    white-space: nowrap;
}

.status .chunk .lbl {
    font-family: var(--sans);
    font-weight: 700;
    font-size: 8.5px;
    letter-spacing: 0.24em;
    text-transform: uppercase;
    color: var(--signal);
}

.status .chunk .val {
    color: var(--status-fg);
}

.status .chunk .val .dot {
    display: inline-block;
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--signal);
    margin-right: 5px;
    vertical-align: middle;
}

.status .chunk .val.idle .dot {
    background: var(--ink-3);
}

/* DSP-load over ~80% of the render budget — flag it in the brand signal so a
 * heavy patch reads at a glance without adding a second alarm colour. */
.status .chunk .val.warn {
    color: var(--signal);
    font-weight: 600;
}

.status .chunk .val.err {
    color: var(--signal);
}

.status .chunk .val.err .dot {
    background: var(--signal);
}

.status .log {
    flex: 0 1 auto;
    min-width: 0;
    font-family: var(--hand);
    font-size: 16px;
    color: var(--status-fg);
    line-height: 1;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    font-weight: 500;
    /* Caveat is a script face whose final glyph ink overhangs its advance
     * width; pad the right so `overflow: hidden` (the box now shrink-wraps the
     * text) doesn't clip the last letter — e.g. the d in "stopped" /
     * "hot-swapped". */
    padding-left: 4px;
    padding-right: 8px;
}

.status .log .ts {
    font-family: var(--mono);
    color: var(--ink-3);
    font-size: 10px;
    margin-right: 6px;
    font-weight: 400;
}

/* Dynamic open icon next to the log — appears only while a compile error is
 * collapsed; re-surfaces the error pane. */
.status .log-action {
    flex: 0 0 auto;
    margin-left: 4px;
    width: 18px;
    height: 18px;
    display: grid;
    place-items: center;
    background: none;
    border: none;
    color: var(--signal);
    cursor: pointer;
    padding: 0;
    transition: color 120ms ease;
}

@media (hover: hover) {
    .status .log-action:hover {
        color: var(--status-fg);
    }
}

.status .log-action svg {
    width: 12px;
    height: 12px;
    display: block;
}

.status .log-action[hidden] {
    display: none;
}

.status .kbd {
    flex: 0 0 auto;
    margin-left: auto;
    display: flex;
    align-items: center;
    gap: 6px;
    font-family: var(--sans);
    font-weight: 700;
    font-size: 9px;
    letter-spacing: 0.24em;
    text-transform: uppercase;
    color: var(--status-fg);
}

.status .kbd kbd {
    font-family: var(--mono);
    font-size: 9.5px;
    background: var(--signal);
    color: var(--status-kbd-ink);
    padding: 1px 5px;
    font-weight: 700;
    letter-spacing: 0;
    border-radius: 2px;
}

/* ── MIDI — an I/O status chunk, same family as SR / Buf / Eng ───────── */
/* The outer .chunk.midi groups the clickable toggle and the (hidden-until-
   playable) target select into one ledger entry. The toggle carries .lbl /
   .val / .dot, so it inherits the shared chunk glyph styling. */
.status .midi {
    white-space: nowrap;
}

.status .midi-toggle {
    appearance: none;
    -webkit-appearance: none;
    background: none;
    border: 0;
    margin: 0;
    padding: 0;
    /* Buttons don't inherit font — without this the .val falls back to the UA
       default face instead of the bar's --mono, breaking the SR/Buf/Eng match. */
    font: inherit;
    display: flex;
    align-items: baseline;
    gap: 7px;
    cursor: pointer;
}

/* Hover brightens the value to full cream — a theme-stable "this is
   clickable" cue. (Can't lean on --signal-soft here: like --ink-4 it flips
   to a near-black brown in dark mode, but the status bar is dark in *both*
   themes, so the bar sticks to --status-fg / --ink-3 / --signal only.) */
@media (hover: hover) {
    .status .midi-toggle:hover:not(:disabled) .val {
        color: var(--status-fg);
    }
}

/* off — not yet enabled: dim value + grey dot, like Eng when idle */
.status .midi-toggle.idle .val {
    color: var(--ink-3);
}

.status .midi-toggle.idle .dot {
    background: var(--ink-3);
}

/* on — device connected: full value + signal dot */
.status .midi-toggle.on .dot {
    background: var(--signal);
}

/* unsupported (Safari/iOS): muted, no dot, non-interactive */
.status .midi-toggle.na {
    cursor: default;
}

.status .midi-toggle.na .val {
    color: var(--ink-3);
}

.status .midi-toggle.na .dot {
    display: none;
}

.status .midi-toggle:disabled {
    cursor: default;
}

/* target select — appears only once the patch exposes a note/trig port */
.status .midi-target {
    position: relative;
    /* anchor for the accent caret overlay */
    display: inline-flex;
    align-items: center;
    gap: 5px;
    margin-left: 9px;
    padding-left: 9px;
    border-left: 1px solid var(--ink-3);
}

.status .midi-target[hidden] {
    display: none;
}

.status .midi-target .pre {
    font-family: var(--sans);
    font-weight: 700;
    font-size: 8px;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--ink-3);
}

.status .midi-target .sel {
    appearance: none;
    -webkit-appearance: none;
    font-family: var(--mono);
    font-size: 10.5px;
    color: var(--status-fg);
    /* faint chip fill — --status-fg (#ece6d4) at 8% */
    background: rgba(236, 230, 212, 0.08);
    border: 1px solid var(--ink-3);
    border-radius: 3px;
    padding: 1px 18px 1px 6px;
    cursor: pointer;
}

@media (hover: hover) {
    .status .midi-target .sel:hover {
        border-color: var(--signal);
    }
}

/* ▾ caret painted in the live accent via a mask, so it tracks both the theme
   and the runtime accent picker. A baked-in SVG `fill` would freeze it orange
   while the dot and hover-border beside it follow var(--signal). */
.status .midi-target::after {
    content: "";
    position: absolute;
    right: 6px;
    top: 50%;
    width: 8px;
    height: 6px;
    transform: translateY(-50%);
    background: var(--signal);
    -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='6'%3E%3Cpath d='M0 0l4 6 4-6z'/%3E%3C/svg%3E") center / contain no-repeat;
    mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='6'%3E%3Cpath d='M0 0l4 6 4-6z'/%3E%3C/svg%3E") center / contain no-repeat;
    pointer-events: none;
}

/* ─────────────────────────── LEARN MODAL ───────────────────────────
   The 8-lesson primer, floated over the live playground as an iframe.
   The scrim dims the session; the frame is the modal card. The iframe
   carries its own (paper) skin + isolated engine — see learn/index.html. */
.learn-overlay {
    position: fixed;
    inset: 0;
    z-index: 1000;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(20, 18, 12, 0.46);
    -webkit-backdrop-filter: blur(2px);
    backdrop-filter: blur(2px);
}

.learn-overlay[hidden] {
    display: none;
}

.learn-frame {
    width: min(940px, 94vw);
    height: 91vh;
    border: 1.5px solid var(--ink);
    background: var(--paper);
    box-shadow: 0 40px 90px -30px rgba(20, 18, 12, 0.6);
}

/* ─────────────────────────── ABOUT MODAL ───────────────────────────
   A small card floated over the session from the wordmark / version pill
   (the two "about" doors). Same scrim as Learn; placeholder content. */
.about-overlay {
    position: fixed;
    inset: 0;
    z-index: 1000;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(20, 18, 12, 0.46);
    -webkit-backdrop-filter: blur(2px);
    backdrop-filter: blur(2px);
}

.about-overlay[hidden] {
    display: none;
}

.about-card {
    position: relative;
    width: min(420px, 92vw);
    padding: 38px 40px 30px;
    background: var(--paper);
    border: 1.5px solid var(--ink);
    box-shadow: 0 40px 90px -30px rgba(20, 18, 12, 0.6);
    text-align: left;
}

.about-close {
    position: absolute;
    top: 12px;
    right: 14px;
    width: 26px;
    height: 26px;
    background: none;
    border: none;
    font-family: var(--sans);
    font-size: 20px;
    line-height: 1;
    color: var(--ink-3);
    cursor: pointer;
    transition: color 120ms ease;
}

@media (hover: hover) {
    .about-close:hover {
        color: var(--signal);
    }
}

.about-mark {
    font-family: var(--display);
    font-weight: 700;
    font-style: italic;
    font-size: 34px;
    line-height: 1;
    letter-spacing: -0.035em;
    color: var(--ink);
    display: inline-flex;
    align-items: baseline;
}

.about-mark .dot {
    display: inline-block;
    width: 7px;
    height: 7px;
    background: var(--signal);
    border-radius: 50%;
    margin: 0 3px 4px;
}

.about-mark .tld {
    font-weight: 400;
    color: var(--ink-2);
    font-size: 0.6em;
    margin-left: 2px;
    align-self: flex-end;
    margin-bottom: 3px;
}

.about-ver {
    font-family: var(--mono);
    font-size: 11px;
    letter-spacing: 0.08em;
    color: var(--ink-3);
    margin: 8px 0 16px;
}

.about-blurb {
    font-size: 13.5px;
    line-height: 1.6;
    color: var(--ink-2);
    margin-bottom: 18px;
}

.about-links {
    display: flex;
    flex-direction: column;
    gap: 7px;
    margin-bottom: 20px;
}

.about-link {
    background: none;
    border: none;
    padding: 0;
    font-family: var(--sans);
    font-weight: 600;
    font-size: 12.5px;
    color: var(--signal);
    cursor: pointer;
    text-align: left;
    transition: color 120ms ease;
}

@media (hover: hover) {
    .about-link:hover {
        color: var(--signal-deep);
    }
}

.about-ack {
    font-size: 11.5px;
    line-height: 1.6;
    color: var(--ink-3);
    margin-bottom: 18px;
}

.about-credit {
    border-top: 1px solid var(--hairline-2);
    padding-top: 12px;
    font-family: var(--sans);
    font-size: 12px;
    line-height: 1.5;
    color: var(--ink-2);
    margin-bottom: 7px;
}

.about-credit a {
    color: var(--signal);
    text-decoration: none;
    transition: color 120ms ease;
}

@media (hover: hover) {
    .about-credit a:hover {
        color: var(--signal-deep);
        text-decoration: underline;
    }
}

.about-foot {
    font-family: var(--hand);
    font-size: 16px;
    color: var(--ink-3);
}

/* ════════════════════════════ MOBILE LAYER ════════════════════════════
 *
 * The desktop layout is a fixed two-column grid (editor | panels). On phones
 * and portrait tablets it collapses to a single switched pane (Code / Panels)
 * with a bottom nav and an overflow sheet for the secondary chrome.
 *
 * The mobile-only elements (#menu-btn, .hotswap-m, .mobile-nav, .sheet,
 * .sheet-scrim) carry their full styling here but stay display:none until the
 * @media breakpoint flips them on — so desktop is byte-for-byte unchanged.
 * ══════════════════════════════════════════════════════════════════════ */

/* ── Mobile-only elements: defined, but hidden on desktop ─────────────── */
.menu-btn,
.hotswap-m,
.mobile-nav,
.sheet,
.sheet-scrim {
    display: none;
}

/* ══ Phones + portrait tablets ═════════════════════════════════════════ */
@media (max-width: 900px) {

    /* The page grows a 5th row for the bottom nav. dvh (not vh) tracks the
       *visible* viewport, so the nav isn't pushed behind Safari's toolbars /
       below the fold when they're shown — the iOS "100vh is too tall" trap,
       worst in landscape. */
    .page {
        height: 100dvh;
        grid-template-rows: var(--chrome-h) var(--tabs-h) minmax(0, 1fr) var(--status-h) auto;
    }

    /* Touch can't hover to reveal the per-tab ✕ (that hover rule is now gated —
       see the hover policy note), so show it on every tab and closing any tab
       stays reachable. The active tab already shows it via `.file-tab.active`. */
    .file-tab .close {
        opacity: 1;
    }

    /* ── Chrome: brand + transport + hamburger only ─────────────────── */
    .brand {
        padding: 0 14px;
    }

    .wordmark {
        font-size: 26px;
    }

    .about-cue {
        display: none;
        /* hover-only affordance — useless on touch */
    }

    /* Keep the version pill (an About door) but drop the long tagline. */
    .brand .plate>span {
        display: none;
    }

    /* Desktop-only chrome controls fold into the overflow sheet. */
    .theme-toggle,
    .accents,
    .docs,
    .zen-btn,
    #share {
        display: none;
    }

    .menu-btn {
        display: flex;
        align-items: center;
        justify-content: center;
        height: 100%;
        width: 48px;
        background: none;
        border: none;
        border-left: 1px solid var(--hairline);
        color: var(--ink);
        cursor: pointer;
    }

    @media (hover: hover) {
        .menu-btn:hover {
            background: rgba(20, 20, 20, 0.04);
        }
    }

    /* ── Main: single switched pane ─────────────────────────────────── */
    .main {
        display: block;
        position: relative;
        grid-template-columns: none;
    }

    .editor,
    .panels-col {
        position: absolute;
        inset: 0;
    }

    .editor {
        border-right: none;
    }

    /* data-mview on <body> selects which pane is live. */
    body[data-mview="code"] .panels-col {
        display: none;
    }

    body[data-mview="panels"] .editor {
        display: none;
    }

    /* Panels view → mixer + figure stacked and scrollable. */
    body[data-mview="panels"] .panels-col {
        display: flex;
        flex-direction: column;
        overflow-y: auto;
        -webkit-overflow-scrolling: touch;
    }

    body[data-mview="panels"] .mixer-panel,
    body[data-mview="panels"] .figure-panel {
        flex: 0 0 auto;
    }

    /* The mixer sizes to its content; the whole column scrolls. The master
       strip's sticky-bottom degrades to a static last row (it just trails the
       voice list) — fine on a phone. */
    body[data-mview="panels"] .mixer-body {
        max-height: none;
        overflow-y: visible;
    }

    /* The figure needs an explicit height — its SVG is drawn from the host's
       clientHeight, so a flex-auto (content-less) box would collapse to 0. */
    body[data-mview="panels"] .figure-panel {
        height: 320px;
    }

    /* ── Editor head: drop the desktop meta + cursor readout, show run ─ */
    .editor-head {
        padding: 0 16px;
    }

    #editor-meta,
    #editor-pos {
        display: none;
    }

    .hotswap-m {
        display: inline-flex;
        align-items: center;
        gap: 6px;
        padding: 6px 13px;
        background: var(--signal);
        color: var(--signal-ink);
        border: none;
        border-radius: 4px;
        font-family: var(--sans);
        font-weight: 700;
        font-size: 10px;
        letter-spacing: 0.16em;
        text-transform: uppercase;
        cursor: pointer;
        line-height: 1;
    }

    /* A touch larger code for thumbs. */
    .editor-body .cm-content {
        font-size: 13.5px;
    }

    /* ── Mixer strip reflow: fader gets its own full-width row ───────── */
    .channel-strip {
        grid-template-columns: auto minmax(0, 1fr) auto;
        grid-template-areas:
            "idx  name db"
            "fade fade ms";
        height: auto;
        row-gap: 9px;
        column-gap: 10px;
        padding: 11px 16px;
        align-items: center;
    }

    .channel-strip .idx {
        grid-area: idx;
    }

    .channel-strip .label {
        grid-area: name;
    }

    .channel-strip .gain-slider {
        grid-area: fade;
        height: 26px;
    }

    .channel-strip .ms-buttons {
        grid-area: ms;
    }

    .channel-strip .db-readout {
        grid-area: db;
    }

    /* Bigger M/S touch targets. */
    .ms-buttons button {
        width: 30px;
        height: 26px;
        font-size: 11px;
    }

    /* Per-voice MIDI arm is desktop-only for now (docs/midi.md). Match the base
       `.ms-buttons button.arm { display: flex }` specificity (0,2,1) so this
       wins by source order — `.ms-buttons .arm` (0,2,0) would lose to it. */
    .ms-buttons button.arm {
        display: none;
    }

    /* ── Ref drawer → full-screen overlay ───────────────────────────── */
    /* The drawer normally lives inside .panels-col (absolute inset:0). On a
       phone it covers the whole screen. */
    .cheat-drawer:not([hidden]) {
        position: fixed;
        inset: 0;
        width: 100%;
        z-index: 1200;
    }

    /* Ref can be opened from the sheet while the Code pane is live — and a
       display:none ancestor would suppress the (fixed) drawer. Reveal the
       column whenever it holds an open drawer; the opaque full-screen drawer
       hides the mixer/figure behind it. (:has specificity outranks the
       data-mview rule above, so no !important needed.) */
    .panels-col:has(.cheat-drawer:not([hidden])) {
        display: block;
    }

    /* ── Status bar: keep SR · Eng · log; drop the rest ─────────────── */
    .status {
        padding: 0 14px;
        gap: 14px;
    }

    /* Keep SR · Load · Eng · log on mobile. Buf is always 128, MIDI is a
       desktop-only feature here, and the ⌘↵ hint is irrelevant on touch — but
       DSP load is *more* useful on a phone (tighter CPU headroom), so it stays. */
    .status .chunk.buf,
    .status .chunk.midi,
    .status .kbd {
        display: none;
    }

    /* ── Bottom nav ─────────────────────────────────────────────────── */
    .mobile-nav {
        display: grid;
        grid-template-columns: 1fr 1fr;
        background: var(--surface);
        border-top: 1.5px solid var(--ink);
        padding-bottom: var(--safe-b);
    }

    .mnav-btn {
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        gap: 4px;
        height: 54px;
        background: none;
        border: none;
        border-right: 1px solid var(--hairline);
        font-family: var(--sans);
        font-weight: 700;
        font-size: 9px;
        letter-spacing: 0.18em;
        text-transform: uppercase;
        color: var(--ink-3);
        cursor: pointer;
        transition: color 120ms ease;
    }

    .mnav-btn:last-child {
        border-right: none;
    }

    .mnav-btn .glyph {
        width: 18px;
        height: 18px;
        display: grid;
        place-items: center;
    }

    .mnav-btn.on {
        color: var(--signal);
    }

    /* ── Overflow sheet + scrim ─────────────────────────────────────── */
    .sheet-scrim {
        display: block;
        position: fixed;
        inset: 0;
        z-index: 1300;
        background: rgba(20, 18, 12, 0.46);
        -webkit-backdrop-filter: blur(2px);
        backdrop-filter: blur(2px);
        opacity: 0;
        pointer-events: none;
        transition: opacity 0.2s ease;
    }

    .sheet-scrim.open {
        opacity: 1;
        pointer-events: auto;
    }

    .sheet {
        display: block;
        position: fixed;
        left: 0;
        right: 0;
        bottom: 0;
        z-index: 1301;
        background: var(--surface);
        border-top: 1.5px solid var(--ink);
        padding: 14px 18px calc(18px + var(--safe-b));
        max-height: 82dvh;
        overflow-y: auto;
        transform: translateY(101%);
        transition: transform 0.24s cubic-bezier(0.2, 0.8, 0.2, 1);
    }

    .sheet.open {
        transform: translateY(0);
    }

    .sheet-grip {
        width: 38px;
        height: 4px;
        border-radius: 2px;
        background: var(--hairline);
        margin: 0 auto 14px;
    }

    .sheet-row {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 12px;
        padding: 12px 0;
        border-bottom: 1px dashed var(--hairline-2);
    }

    .sheet-row:last-child {
        border-bottom: none;
    }

    .sheet-lbl {
        font-family: var(--sans);
        font-weight: 700;
        font-size: 10px;
        letter-spacing: 0.22em;
        text-transform: uppercase;
        color: var(--ink-3);
    }

    .sheet-seg {
        display: flex;
        border: 1.2px solid var(--ink);
        border-radius: 4px;
        overflow: hidden;
    }

    .sheet-seg button {
        padding: 8px 18px;
        background: var(--paper);
        border: none;
        border-right: 1px solid var(--ink);
        font-family: var(--sans);
        font-weight: 700;
        font-size: 11px;
        color: var(--ink);
        cursor: pointer;
    }

    .sheet-seg button:last-child {
        border-right: none;
    }

    .sheet-seg button.on {
        background: var(--signal);
        color: var(--signal-ink);
    }

    .sheet-accents {
        display: flex;
        gap: 16px;
    }

    /* Bigger accent dots — comfortable thumb targets. */
    .sheet-accents .accent-dot {
        width: 22px;
        height: 22px;
    }

    .sheet-acts {
        gap: 10px;
    }

    .sheet-act {
        flex: 1;
        padding: 13px 0;
        background: var(--paper);
        border: 1.2px solid var(--ink);
        border-radius: 4px;
        font-family: var(--sans);
        font-weight: 700;
        font-size: 11px;
        letter-spacing: 0.14em;
        text-transform: uppercase;
        color: var(--ink);
        cursor: pointer;
    }

    .sheet-act:active {
        background: var(--signal);
        color: var(--signal-ink);
        border-color: var(--signal);
    }
}

/* ══ Narrow phones ═════════════════════════════════════════════════════ */
@media (max-width: 380px) {
    .wordmark {
        font-size: 22px;
    }

    .brand {
        padding: 0 10px;
    }

    /* Tighten the transport so play · stop · rec · hamburger always fit. */
    .tbtn {
        padding: 0 11px;
    }

    .menu-btn {
        width: 42px;
    }
}