/* =============================================================================
   styles.css — the composer's own sheet.

   TOKENS ONLY. Every colour here is a var() that assets/css/site.css defines
   and assets/css/dark.css redefines, so this one file serves both editions and
   there is nothing to keep in step. A literal colour in this file is a bug in
   the dark edition that the paper edition cannot show you.

   THE ONE EXCEPTION IS THE PRINT BLOCK, at the bottom, which is literal black
   on literal white ON PURPOSE and in BOTH editions. See the note there.
   ========================================================================== */

/* -----------------------------------------------------------------------------
   THE ENGRAVING

   VexFlow paints with PRESENTATION ATTRIBUTES — it writes fill="black" and
   stroke="black" onto the root <svg> with setAttributeNS and lets almost
   everything inherit, adding stroke="#999999" on the stave groups and
   stroke="#444" on ledger lines. There is no stylesheet inside the vendor
   bundle to fight with.

   A presentation attribute sits BELOW an author stylesheet rule in the
   cascade, so these four rules repaint the whole engraving from tokens without
   patching the renderer or post-processing the SVG. That is the entire
   two-edition story for the music itself.
   -------------------------------------------------------------------------- */
:root {
    --score-ink:    var(--ink);
    --score-rule:   rgba(30, 38, 43, .48);
    --score-ledger: rgba(30, 38, 43, .62);
    --score-mark:   #B91C1C;
    --sheet-ground: #ffffff;
    --sheet-edge:   rgba(30, 38, 43, .14);
}
html[data-dark] {
    --score-ink:    #EAEEF2;
    --score-rule:   rgba(234, 238, 242, .46);
    --score-ledger: rgba(234, 238, 242, .62);
    --score-mark:   #F87171;
    --sheet-ground: #141B22;
    --sheet-edge:   rgba(255, 255, 255, .12);
}

#score svg               { fill: var(--score-ink); stroke: var(--score-ink); }
#score svg .vf-stave     { fill: var(--score-rule); stroke: var(--score-rule); }
#score svg [stroke="#999999"] { stroke: var(--score-rule); }
#score svg [fill="#999999"]   { fill: var(--score-rule); }
#score svg [stroke="#444"]    { stroke: var(--score-ledger); }
#score svg #play-cursor  { stroke: var(--accent); }

/* ------------------------------------------------------------------ the page */
.sheet {
    background: var(--sheet-ground);
    border: 1px solid var(--sheet-edge);
    border-radius: 12px;
    padding: 1rem .85rem 1.2rem;
    box-shadow: 0 1px 3px rgba(0, 0, 0, .06);
}
.sheet__head {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    text-align: center;
    gap: .15rem;
    margin-bottom: .75rem;
    color: var(--score-ink);
}
.sheet__title { font: 600 clamp(1.3rem, 2.6vw, 1.9rem)/1.2 Palatino, Georgia, serif; margin: 0; }
.sheet__sub   { font: italic .88rem/1.4 Palatino, Georgia, serif; opacity: .68; margin: 0; }
.sheet__by {
    display: flex; justify-content: space-between; gap: 1rem;
    font: .8rem/1.4 Palatino, Georgia, serif; opacity: .78;
    margin-top: .45rem; padding-top: .4rem; border-top: 1px solid var(--sheet-edge);
}

/* The engraving is 864 units wide and does not usefully compress below about
   680, so it scrolls INSIDE this box. One unwrappable row would otherwise
   widen the document and carry every fixed element off the screen with it. */
.sheet__wrap { overflow-x: auto; overflow-y: hidden; -webkit-overflow-scrolling: touch; }
#score svg { min-width: 680px; display: block; }

/* The staff the reader has taken is dimmed on the page, so it is obvious which
   line is theirs — it is still engraved, and the playhead still runs. */
.page--along-rh .sheet__wrap { --along-note: "Upper staff is yours"; }
.page--along-lh .sheet__wrap { --along-note: "Lower staff is yours"; }
.page--along-rh .sheet__wrap::before,
.page--along-lh .sheet__wrap::before {
    content: var(--along-note);
    display: block; margin-bottom: .5rem;
    font-size: .625rem; font-weight: 700; letter-spacing: .18em; text-transform: uppercase;
    color: var(--accent);
}

/* --------------------------------------------------------------- transport */
.tport {
    display: flex; flex-wrap: wrap; align-items: center; gap: .5rem;
    padding: .75rem .85rem;
    border: 1px solid var(--line); border-radius: 11px; background: var(--tint);
}.tport__read {
    margin-left: auto; display: flex; gap: .9rem; align-items: baseline;
    font-size: .8rem; color: var(--ink-55); font-variant-numeric: tabular-nums;
}
.tport__read b { color: var(--ink); font-size: .92rem; }

/* THE RAIL IS 8px; THE TARGET IS NOT. #prog is role="slider" and the manual
   sells clicking it to seek, and it measured 274 x 8 at 360px where every
   other transport control clears 43. The rail keeps its look and the
   pseudo-element carries the touch area — asymmetric, because the room below
   it is a paragraph of hint text and the room above it is the transport's own
   buttons. */
.prog {
    position: relative;
    width: 100%; height: 8px; margin-top: .1rem;
    border-radius: 5px; background: var(--line); cursor: pointer;
}
.prog::after {
    content: ''; position: absolute; left: 0; right: 0;
    top: -.55rem; bottom: -1.7rem;
}
.prog i { display: block; height: 100%; width: 0; background: var(--accent); border-radius: 5px; }

/* -------------------------------------------------------------- the rack */
.rack { display: grid; gap: 1rem; }
.rack__row {
    display: grid;
    grid-template-columns: 8.5rem minmax(0, 1fr) 8rem;
    grid-template-areas: "head seg lvl" "sig sig sig";
    gap: .5rem .8rem;
    align-items: center;
    padding: .8rem .9rem;
    border: 1px solid var(--line); border-radius: 11px; background: var(--paper);
}
.rack__head { grid-area: head; display: flex; flex-direction: column; min-width: 0; }
.rack__head b { font-size: .95rem; }
.rack__head span { font-size: .7rem; opacity: .55; text-transform: uppercase; letter-spacing: .1em; }
.rack__seg { grid-area: seg; }
.rack__lvl { grid-area: lvl; display: flex; flex-direction: column; gap: .2rem; }
.rack__lvl input { width: 100%; accent-color: var(--accent); }
.rack__sig {
    grid-area: sig; margin: 0; font-size: .76rem; line-height: 1.5; color: var(--ink-55);
    border-top: 1px solid var(--line-soft); padding-top: .5rem;
}
@media (max-width: 46rem) {
    .rack__row { grid-template-columns: minmax(0, 1fr); grid-template-areas: "head" "seg" "lvl" "sig"; }
    .rack__lvl { flex-direction: row; align-items: center; gap: .6rem; }
}

/* --------------------------------------------------------- the tool strip */
.tools { display: flex; flex-wrap: wrap; gap: .9rem; align-items: flex-start; }
.tools__grp { display: flex; flex-direction: column; gap: .3rem; }
.dur .seg__b { font-family: Palatino, Georgia, serif; font-size: .95rem; min-width: 2.6rem; }

/* The exports were a grid of ten separate outlines; they are a rail now, and
   the rail is what segpill.js upgrades. Below 720px it goes back to wrapping
   chips — a ten-segment rail on a phone is a scroll bar with buttons in it,
   and there the thumb is switched off rather than left pointing at a segment
   on another row. */
.exports { display: flex; align-items: center; }
.exports .seg__b { text-align: center; }
.busy { display: flex; align-items: center; gap: .55rem; font-size: .84rem; color: var(--ink-55); }
.busy__dot { width: 9px; height: 9px; border-radius: 50%; background: var(--accent); animation: cpulse 1s var(--ease) infinite; }
@keyframes cpulse { 0%, 100% { opacity: .25; } 50% { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .busy__dot { animation: none; } }

.press {
    margin: 0; padding: .7rem .8rem; max-height: 9rem; overflow: auto;
    border: 1px solid var(--line); border-radius: 9px; background: var(--tint);
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: .72rem; line-height: 1.6; color: var(--ink-55); white-space: pre-wrap;
}

.notes { margin: 1rem 0 0; padding: 0; list-style: none; display: grid; gap: .7rem; }
.notes li {
    font-size: .88rem; line-height: 1.6; color: var(--ink-77);
    padding-left: .9rem; border-left: 2px solid var(--line);
}
.notes b { color: var(--ink); }
.notes i { font-size: .78rem; opacity: .7; }

/* An input under 16px makes iOS Safari zoom on focus and carry the layout off
   the screen. Matched on the POINTER, not on the width — a small window on a
   desktop is not a phone. */
@media (pointer: coarse) {
    input, select, textarea, .field { font-size: 16px; }
    /* Undo and Redo are this property's own editing controls and they sit in a
       house console head, where .tbtn measures 27.9px tall. On a finger that is
       not a button. */
    .console__head .tbtn { min-height: 44px; }
}

/* =============================================================================
   PRINT — BLACK ON WHITE, IN BOTH EDITIONS.

   A dark-edition sheet that prints white-on-black costs a cartridge and is a
   real defect, so this block is literal rather than tokenised and it carries
   !important: it has to beat dark.css, which is linked after this file on the
   dark page. The rules are surgical rather than a blanket `svg * { fill:#000 }`
   — VexFlow leaves fill="none" on stroked paths, and forcing a fill onto those
   turns beams and ties into blots.
   ========================================================================== */
@page { size: letter portrait; margin: 12mm; }

@media print {
    html, body {
        background: #ffffff !important;
        color: #000000 !important;
    }
    /* THE WHOLE OF THE CONSOLE FURNITURE, and #desk and the editor's console
       head are on this list because they were not. #desk was hidden only by way
       of its inner #controls, so the printout opened with the section's band,
       its eyebrow and its heading — a 73px strip, and a solid black one in the
       moon edition. The "Write on it" console has no id of its own, so
       site.css's print rule took its BODY away and left its head printed under
       the score: a black bar reading WRITE ON IT · Undo · Redo. Between them
       they pushed the flow past a page break and every printout ended on a
       blank fourth sheet. */
    /* EVERYTHING THAT IS NOT THE MUSIC. #desk and #rackband are gone from the
       document — the compose panel folded into the sheet band and the
       instruments into the panel under it — so they are dropped from this
       list rather than left naming sections that no longer exist. What is on
       it now is every console, wherever it sits, plus the page's own chrome.

       .sheet itself goes too: while printing, the engraving is served from the
       paginated clones below, and leaving the original in the flow printed the
       whole score a second time, unpaginated, after the numbered pages. */
    .nav, .menu, .foot, .skip, .hero, #controls, #transport,
    #exportband, #manual, .console, .press, .busy, .shelf,
    #toast-host { display: none !important; }
    html[data-printing] .sheet,
    html[data-printing] .band { display: none !important; }
    main { padding: 0 !important; }
    .band { padding-block: 0 !important; }
    .wrap { max-width: none !important; padding-inline: 0 !important; }
    .sheet {
        background: #ffffff !important;
        border: 0 !important;
        box-shadow: none !important;
        padding: 0 !important;
    }
    /* EVERY CHILD, not just the box. site.css gives h1..h4 a colour of their
       own, so .sheet__title — an <h2> — did not inherit this one: in the moon
       edition it printed white on the sheet's white and the page came out with
       no title at all, subtitle and all else intact. An inherited value cannot
       beat a rule that names the element. */
    .sheet__head, .sheet__head * { color: #000000 !important; }
    .sheet__by { border-top-color: #999999 !important; }
    .sheet__wrap { overflow: visible !important; }
    .sheet__wrap::before { display: none !important; }
    #score svg { width: 100% !important; min-width: 0 !important; height: auto !important; }
    /* Everything inherits from the root svg; the three exceptions VexFlow
       writes its own attribute onto are named after it. */
    #score svg               { fill: #000000 !important; stroke: #000000 !important; }
    #score svg .vf-stave     { fill: #000000 !important; stroke: #000000 !important; }
    #score svg [stroke="#999999"] { stroke: #000000 !important; }
    #score svg [fill="#999999"]   { fill: #000000 !important; }
    #score svg [stroke="#444"]    { stroke: #000000 !important; }
    #score svg #play-cursor  { display: none !important; }
}


/* The placard toggle. It sits under the ten format buttons as a footnote to
   them, not as an eleventh: a checkbox and one line, at the weight of the band's
   own caption. 16px on the input so iOS does not zoom the page to reach it. */
.pl-opt { display: flex; align-items: flex-start; gap: .6rem; margin-top: 1rem;
          font-size: .82rem; line-height: 1.5; color: var(--ink-77); cursor: pointer; }
.pl-opt b { font-weight: 600; color: var(--ink); }

/* ==========================================================================
   SEGMENTED PILLS
   --------------------------------------------------------------------------
   One track, one thumb, and the segments sitting in it. The thumb is placed by
   js/segpill.js because the segments have different widths — "Woodwinds" is
   not "Bass" — so no CSS-only nth-child trick can put it in the right place.
   What CSS owns here is the well, the shape and the two colours; the script
   owns only transform and width.
   ========================================================================== */
.segp {
    position: relative;
    display: flex; align-items: center; flex-wrap: nowrap;
    gap: 0; padding: 2px; border-radius: 999px;
    border: 1px solid var(--line);
    background: linear-gradient(180deg, rgba(30, 38, 43, .045), rgba(30, 38, 43, .025));
    box-shadow: inset 0 1px 2px rgba(30, 38, 43, .055);
    max-width: 100%;
    /* Nine instruments do not fit a phone. The track scrolls sideways rather
       than wrapping: a wrapped pill has two rows and one thumb, and the thumb
       would have to lie about which row it is on. */
    overflow-x: auto; overflow-y: hidden;
    scrollbar-width: none; -ms-overflow-style: none;
    overscroll-behavior-x: contain;
}
.segp::-webkit-scrollbar { display: none; }

.segp__thumb {
    position: absolute; z-index: 0; top: 2px; bottom: 2px; left: 0;
    width: 0; opacity: 0;
    border-radius: 999px; background: var(--ink);
    box-shadow: 0 1px 2px rgba(30, 38, 43, .26), 0 6px 18px rgba(30, 38, 43, .14);
    transition: transform .34s var(--ease), width .34s var(--ease),
                opacity .18s var(--ease);
    pointer-events: none;
}

/* The segments give up their own outline — the track is the outline now. */
.segp .seg__b {
    position: relative; z-index: 1; flex: 0 0 auto;
    border: 1px solid transparent; background: transparent;
    border-radius: 999px; white-space: nowrap;
    transition: color .2s var(--ease);
}
.segp .seg__b:hover { background: transparent; border-color: transparent; color: var(--ink); }
.segp .seg__b[aria-pressed="true"] { background: transparent; border-color: transparent; color: var(--paper); }
.segp .seg__b:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* An action rail chooses nothing, so its thumb is a cursor: it follows the
   pointer and the keyboard, and the label under it goes light while it is
   there. Without this rule the ten exports would be dark text on a dark thumb
   for as long as the pointer sat on one. */
.segp--action .seg__b { transition: color .14s var(--ease); }
.segp--action .seg__b:hover,
.segp--action .seg__b:focus-visible { color: var(--paper); }

/* The rack's pill carries nine choices inside a grid cell that must be allowed
   to be narrower than its content, or the track never scrolls and the row
   grows instead — the 1fr-grows-to-min-content trap. */
.rack__seg { min-width: 0; }

@media (prefers-reduced-motion: reduce) {
    .segp__thumb { transition: opacity .18s linear; }
}

/* The night edition. The well is ink-on-paper at single-digit alpha, which on
   a dark ground is invisible — it has to be redrawn in light. The thumb needs
   nothing: it is var(--ink), which is already white here. */
/* ------------------------------------------------------- the companion sheet */
/* Two rows: what this sheet is, and how to open another one. */
.companion {
    margin-top: .8rem; padding-top: .7rem;
    border-top: 1px solid var(--line);
}
.companion__r {
    display: flex; align-items: center; gap: .4rem; flex-wrap: wrap;
    margin-bottom: .35rem;
}
.companion__r .lbl { flex: 0 0 auto; min-width: 5.4rem; }
.companion__s {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: .95rem; font-weight: 700; letter-spacing: .04em;
    padding: .3rem .6rem; border-radius: 8px;
    background: var(--tint); border: 1px solid var(--line); color: var(--ink);
    user-select: all;
}
.companion__in {
    flex: 1 1 12rem; min-width: 0;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    /* 16px or iOS Safari zooms the page the moment this is focused. */
    font-size: 16px; letter-spacing: .04em; text-transform: uppercase;
    padding: .34rem .6rem; border-radius: 8px;
    border: 1px solid var(--line); background: var(--paper); color: var(--ink);
}
.companion__in:focus { outline: none; border-color: var(--ink); }
.companion__in::placeholder { text-transform: none; letter-spacing: 0; }
.companion__n {
    font-size: .95rem; font-weight: 600; color: var(--ink); min-width: 0;
}
.companion.is-drop { outline: 2px dashed var(--accent); outline-offset: 6px; }
#companionNote.is-warn { color: var(--ink); }
#companionNote.is-warn strong { color: var(--accent); }


/* ------------------------------------------------------------- fit in screen */
/* Three ways to look at one engraving.

   NORMAL is what the page has always done: natural size, scrolling sideways
   inside its own box on a narrow window.

   FIT IN SCREEN scales the engraving to the width of the box and then shows
   ONE SCREENFUL OF IT AT A TIME, paged. Scaling the whole thing to fit instead
   would be honest arithmetic and useless music: a twenty-four bar sheet is six
   systems tall, and six systems squeezed into one screen is a picture of a
   score rather than something anybody can read.

   TWO UP is the same paging with the next page beside this one, for a desktop
   window wide enough to hold both. It is a second live pane rather than a
   picture of one — the playhead runs in whichever page it is on, because
   PlateRender.setCursor is given both hosts.

   All of the arithmetic is in app.js, because neither dimension is knowable
   from CSS: the engraving's width is fixed by the renderer but its height
   depends on how many systems this particular sheet needed. */
.sheet__pages { display: flex; gap: 1rem; align-items: flex-start; }
.sheet__pane  { flex: 1 1 0; min-width: 0; overflow: hidden; }

.page--fit .sheet__wrap { overflow: hidden; }
.page--fit .sheet__pane > div { transform-origin: top left; }
.page--fit #score svg, .page--fit #score2 svg { min-width: 0; }

/* A second pane exists only when it is asked for. */
.sheet__pane[hidden] { display: none; }

.sheet__nav {
    display: flex; align-items: center; justify-content: center; gap: .55rem;
    margin-top: .65rem;
}
.sheet__count {
    font-size: .8rem; font-weight: 600; letter-spacing: .02em;
    color: var(--ink-55); min-width: 8rem; text-align: center;
}
.sheet__nav .seg__b { min-width: 2.6rem; text-align: center; font-size: 1rem; }
.sheet__nav .seg__b[disabled] { opacity: .38; cursor: default; }

.tport__view { margin-left: .35rem; }
@media (max-width: 720px) {
    .tport__view { order: 5; }
}
/* Two up needs two readable pages side by side. Below this there is not room
   for one of them, let alone both, so the control goes rather than being
   offered and then disappointing. */
@media (max-width: 1000px) {
    #btnTwoUp { display: none; }
}
/* --------------------------------------------------------------- compact */
/* The page is a reading surface with a lot of controls above it. These trim
   the chrome rather than the sheet: the engraving keeps every pixel it had,
   and the panels around it give some back. */
.tport { gap: .4rem; }
.console__body { padding-top: .7rem; padding-bottom: .7rem; }
.field-row { gap: .5rem; }
.rack__row { row-gap: .5rem; }
.rack__sig { font-size: .74rem; line-height: 1.4; }
/* ------------------------------------------------- one control height ----
   The transport is a row of five different components — plain buttons, two
   segmented pills, a checkbox label and a readout — and each had been
   sized by its own padding. Measured, they came out 39, 41, 37 and 24 pixels
   tall, so the row had a nine-pixel stagger in it that reads as carelessness
   long before anybody works out what is uneven.
   
   One variable sets the height and every control in the row is told to fill
   it and centre its contents. The pills subtract their own 2px well and 1px
   border so the thumb lands flush inside the same outline as the buttons
   beside it. */
.tport {
    --tport-h: 2.4rem;
    display: flex; align-items: center; flex-wrap: wrap;
}
/* HEIGHT, NOT MIN-HEIGHT. min-height only sets a floor, so the pills — whose
   segments carry their own padding — still came out two pixels taller than
   the buttons beside them. The centres lined up and the outlines did not,
   which is the version of this that is harder to see and more annoying once
   seen. Fixed height plus centred contents makes every outline in the row the
   same rectangle. */
.tport__b,
.tport > label,
.tport .segp {
    height: var(--tport-h);
    box-sizing: border-box;
}
.tport__b,
.tport > label {
    display: inline-flex; align-items: center; justify-content: center;
}
.tport .segp .seg__b {
    height: calc(var(--tport-h) - 6px);
    padding-top: 0; padding-bottom: 0;
    display: inline-flex; align-items: center;
}
.tport .segp__thumb { top: 2px; bottom: 2px; }
.tport__read {
    display: inline-flex; align-items: center;
    height: var(--tport-h);
    margin-left: auto;
}
/* ------------------------------------------------------------- the desk ---
   Compose and Transport are one instrument, so they read as one: the second
   console butts against the first with a single hairline between them rather
   than a gap, and only the outer corners are rounded. Two panels with air
   between them said they were two unrelated things. */
#controls { margin-top: 1.2rem; border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
.console--joined {
    margin-top: 0 !important;
    border-top: 0;
    border-top-left-radius: 0; border-top-right-radius: 0;
}
.console--joined .console__head { border-top: 1px solid var(--line); }

/* The compose panel's own rows, tightened. The hint under it repeats what the
   seed field already says, so it gets the smaller of the two type sizes. */
#controls .console__body > .hint { font-size: .76rem; margin-top: .5rem; }
#controls .run { gap: .45rem; margin-top: .6rem; }
/* --------------------------------------------------------------- the shelf
   Forty sheets in the height of about six. Each row is one line: the name,
   the serial in the monospaced face it is always set in, and two buttons. The
   list scrolls inside itself rather than growing the panel, because a desk
   that gets taller every time you press Compose is a desk that walks off the
   screen. */
.shelf { margin-top: .8rem; padding-top: .7rem; border-top: 1px solid var(--line); }
.shelf__head { display: flex; align-items: center; gap: .5rem; margin-bottom: .45rem; }
.shelf__n { font-size: .74rem; color: var(--ink-55); margin-left: auto; }
.shelf__list {
    list-style: none; margin: 0; padding: 0;
    max-height: 11.5rem; overflow-y: auto;
    border: 1px solid var(--line); border-radius: 10px;
}
.shelf__row {
    display: grid; align-items: center; gap: .5rem;
    /* minmax(0,…) on the name, or one long title sets the column width for
       every row and pushes the buttons off the end. */
    grid-template-columns: minmax(0, 1fr) auto auto auto;
    padding: .3rem .5rem;
    border-bottom: 1px solid var(--line);
    font-size: .8rem;
}
.shelf__row:last-child { border-bottom: 0; }
.shelf__row.is-on { background: var(--tint); }
.shelf__nm { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.shelf__sr {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: .7rem; color: var(--ink-55);
}
.shelf__b {
    font: inherit; font-size: .64rem; font-weight: 800; letter-spacing: .05em;
    padding: .2rem .48rem; border-radius: 999px;
    border: 1px solid var(--line); background: transparent; color: var(--ink-55);
    cursor: pointer;
}
.shelf__b:hover { border-color: var(--ink); color: var(--ink); }

/* ------------------------------------------------------------ compose row
   THE CLASS WAS ALREADY TAKEN. These three labelled fields were marked up as
   `.ib`, which in the house stylesheet is the 32x32 ICON BUTTON — a fixed
   square with centred contents and a border of its own. A <label> wrapping a
   select inherited all of it, which is why the row read as jagged: three boxes
   sized by a rule written for something else, with the captions squeezed
   beside them at whatever width was left.

   They are `.fld` now and laid out on purpose: the caption above its field in
   the small caps the rest of the desk uses, every field the same height, and
   widths chosen for what each one holds — a key name, three digits, ten. */
.field-row { align-items: end; }
.fld {
    display: grid; gap: .18rem; min-width: 0;
    border: 0; background: none; padding: 0; height: auto; width: auto;
    cursor: default;
}
.fld > .lbl {
    font-size: .58rem; font-weight: 800; letter-spacing: .12em;
    text-transform: uppercase; color: var(--ink-55);
}
.fld .field {
    width: 100%; height: 2.4rem; box-sizing: border-box;
    padding: 0 .6rem;
}
.fld--key   { flex: 0 1 11rem; max-width: 11rem; }
.fld--tempo { flex: 0 0 5.2rem; }
.fld--seed  { flex: 0 1 11rem; max-width: 11rem; }
@media (max-width: 560px) {
    .fld--key, .fld--seed { flex: 1 1 100%; max-width: none; }
}

/* ------------------------------------------------------------- close the gap
   The band under the desk carried a full band's padding above a heading that
   was already spaced — an empty screen between the sheet and the instruments.
   Trimmed at the seam only; the bands elsewhere keep their air. */
#rackband { padding-top: 1.6rem; }
#sheet + .band { padding-top: 1.4rem; }
/* A joined console nested inside another one is still a seam, not a new
   panel: no top border, square top corners, and the head it carries is a
   divider rather than a lid. */
.console--joined .console--joined { margin-top: 0; border-top: 0; border-radius: 0; }
.console--joined > .console__body > .console--joined:last-child {
    border-bottom-left-radius: 11px; border-bottom-right-radius: 11px;
}
#rack { margin-top: 0; }
#rack + .console--joined { margin-top: .9rem; }
/* The one nav item that is a place rather than a section: the sheet's own
   serial and every way of taking it away. The key is drawn rather than a
   glyph — a font's key sits wherever its metrics put it and this one has to
   sit on the baseline of the words beside it. */
.nav__sheet { display: inline-flex; align-items: center; gap: .34rem; }
.nav__key { opacity: .72; flex: 0 0 auto; }
.nav__sheet:hover .nav__key { opacity: 1; }
.companion__quick { display: inline-flex; gap: .3rem; margin-left: .2rem; }
.companion__quick .seg__b { font-size: .72rem; padding: .3rem .6rem; }
@media (max-width: 640px) { .companion__quick { width: 100%; margin-left: 0; } }
/* ------------------------------------------------------- the master panel
   Compose, Transport, the sheet, Write on it, the instruments and Play along
   are one column now. What makes them read as one desk rather than six boxes
   stacked is that only the OUTER corners are round and only the outer edge is
   drawn — everything between is a hairline, which is the difference between
   a rack and a pile.

   The rows inside are tightened rather than the type shrunk: a panel gets
   compact by losing the air between its parts, not by becoming harder to
   read. */
.console--joined + .console--joined { margin-top: 0; }
.console--joined .console__head {
    padding-top: .6rem; padding-bottom: .6rem;
    background: color-mix(in srgb, var(--ink) 2.5%, transparent);
}
.console--joined .console__body { padding-top: .8rem; padding-bottom: .8rem; }
.console--joined .console__body > .hint:last-child { margin-bottom: 0; }

/* The rack's two rows and the play-along under them. */
#rack .rack__row + .rack__row { margin-top: .55rem; }
#rack .rack__row { padding: .6rem .7rem; }
.rack__sig { margin-top: .35rem; }

/* Write on it: three chip groups and a log. The groups were on four rows with
   a caption above each; two rows is enough. */
#log { margin-top: .6rem; max-height: 6.5rem; }

/* ------------------------------------------------------- the printed pages
   Built by printPaginate() in app.js on beforeprint and thrown away after —
   see the note there for why the page number cannot be done in CSS.

   Nothing here is visible on screen: the container is display:none outside a
   print context, so the document a reader sees never carries a second copy of
   the engraving. */
#printPages { display: none; }

@page {
    size: 8.5in 11in;
    margin: 0.5in;
}

@media print {
    #printPages { display: block !important; }

    .printpage {
        /* One page each, and the last one does not force a blank sheet after
           it — `page-break-after: always` on every page is the classic way to
           print one more page than there is music. */
        break-after: page;
        page-break-after: always;
        display: flex; flex-direction: column;
        height: 10in;                       /* 11in less the two 0.5in margins */
        overflow: hidden;
    }
    .printpage:last-child { break-after: auto; page-break-after: auto; }

    .printpage__head {
        display: flex; align-items: flex-start; gap: .55rem;
        padding-bottom: .45rem; margin-bottom: .5rem;
        border-bottom: 1px solid #000;
    }
    .printpage__logo { width: 74px; height: auto; flex: 0 0 auto; }
    .printpage__ttl { min-width: 0; }
    .printpage__name {
        margin: 0; font: 600 17pt/1.15 Palatino, Georgia, serif; color: #000;
    }
    .printpage__sub {
        margin: .12rem 0 0; font: italic 9pt/1.3 Palatino, Georgia, serif; color: #000;
    }

    .printpage__win { flex: 1 1 auto; overflow: hidden; }
    .printpage__ink { transform-origin: top left; }
    .printpage__ink svg { min-width: 0; }
    /* The engraving prints black on white in both editions — the dark
       edition is a way of reading the page, not a property of the music, and
       a score printed white-on-black costs a cartridge. */
    .printpage__ink svg,
    .printpage__ink svg * { fill: #000 !important; stroke: #000 !important; }
    .printpage__ink svg #play-cursor { display: none !important; }

    .printpage__foot {
        display: flex; justify-content: space-between; align-items: baseline;
        border-top: 1px solid #000; margin-top: .4rem; padding-top: .3rem;
        font: 7.5pt/1 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
        color: #000;
    }
}

/* --------------------------------------------------------- heard on the grand
   Two rows, each one line: what it is, what format, and the two things you can
   do with it. minmax(0, …) on the name so a long title shortens itself rather
   than setting the width of the row and pushing the buttons off the end. */
.heard { margin-top: .8rem; padding-top: .7rem; border-top: 1px solid var(--line); }
.heard__list { list-style: none; margin: .4rem 0 0; padding: 0; display: grid; gap: .3rem; }
.heard__row {
    display: grid; align-items: center; gap: .5rem;
    grid-template-columns: minmax(0, 1fr) auto auto auto;
}
.heard__n { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
            font-size: .85rem; font-weight: 600; }
.heard__k { font-size: .68rem; font-weight: 700; letter-spacing: .08em;
            text-transform: uppercase; color: var(--ink-55); }
.heard__row .seg__b { font-size: .7rem; padding: .28rem .62rem; }
@media (max-width: 560px) {
    .heard__row { grid-template-columns: minmax(0, 1fr) auto auto; }
    .heard__k { grid-column: 1 / -1; }
}

/* ------------------------------------------------ the notes being played
   The note lights rather than a line crossing it. Two things do the work:
   the ink goes to the accent, and a glow is laid under it — an SVG
   drop-shadow with no offset, which is a halo rather than a shadow.

   IT HAS TO BE A FILTER, NOT A text-shadow. These are <path> glyphs inside an
   <svg>; text-shadow does nothing to them. drop-shadow() in a CSS filter does,
   and it follows the actual outline of the notehead, stem and beam rather
   than boxing them.

   THE GLOW IS THE SAME COLOUR ON BOTH EDITIONS AND THE GROUND IS NOT, which
   is the whole reason it is written in var(--accent) and not a literal: on
   paper the accent is a burnt orange over white, on the moon edition it is a
   brighter orange over near-black, and each was chosen against its own
   ground. A single hex here would be right on one of them.

   The transition is on the filter and the fill together, so a note does not
   snap on: at a hundred and twenty beats a minute a hard cut reads as a
   flicker, and 90ms is long enough to be a swell and short enough to still
   land on the beat. */
#score g.vf-stavenote.is-playing,
#score2 g.vf-stavenote.is-playing,
#score g.vf-stavenote.is-playing *,
#score2 g.vf-stavenote.is-playing * {
    fill: var(--accent) !important;
    stroke: var(--accent) !important;
}
/* THE HALO IS GLOW'S ALONE. Colour paints the note and stops there — it is
   the default because it says which note without asking the compositor for a
   filter on every frame, which on a long sheet at double speed is the
   difference between following the music and watching a page struggle. */
html[data-follow="glow"] #score g.vf-stavenote.is-playing,
html[data-follow="glow"] #score2 g.vf-stavenote.is-playing {
    filter: drop-shadow(0 0 3px color-mix(in srgb, var(--accent) 62%, transparent))
            drop-shadow(0 0 9px color-mix(in srgb, var(--accent) 34%, transparent));
}
#score g.vf-stavenote,
#score2 g.vf-stavenote {
    transition: filter .09s linear;
}
@media (prefers-reduced-motion: reduce) {
    #score g.vf-stavenote, #score2 g.vf-stavenote { transition: none; }
}
/* The moon edition's ground is darker, so the halo can carry further before it
   becomes a smear. */
html[data-dark][data-follow="glow"] #score g.vf-stavenote.is-playing,
html[data-dark][data-follow="glow"] #score2 g.vf-stavenote.is-playing {
    filter: drop-shadow(0 0 4px color-mix(in srgb, var(--accent) 72%, transparent))
            drop-shadow(0 0 12px color-mix(in srgb, var(--accent) 40%, transparent));
}
/* A lit note must never reach paper or an exported file — both are the
   music, not a picture of somebody listening to it. */
@media print {
    g.vf-stavenote.is-playing, g.vf-stavenote.is-playing * {
        fill: #000 !important; stroke: #000 !important;
    }
    g.vf-stavenote.is-playing { filter: none !important; }
}

/* ------------------------------------------------------- take it away, here
   The compose panel's two action rows share one grid so the buttons under
   COMPOSE A SHEET line up with the ten above them rather than sitting on
   their own ragged line. */
.run--take { display: flex; align-items: center; gap: .5rem; margin-top: .6rem; flex-wrap: wrap; }
.run--take > .lbl { flex: 0 0 auto; }
.run__exports { flex: 1 1 20rem; min-width: 0; }
.run__exports .seg__b { font-size: .72rem; padding: .34rem .62rem; }
/* ------------------------------------------------------ the sheet's marks */
.sheet__logo { display: block; width: 46px; height: auto; opacity: .82; }
.sheet__by { align-items: center; }
.sheet__foot {
    display: flex; align-items: center; justify-content: space-between;
    gap: 1rem; margin: .7rem 0 0; padding-top: .5rem;
    border-top: 1px solid var(--sheet-edge);
}
.sheet__foot .sheet__logo { width: 40px; opacity: .62; }
/* The line sits between the mark and the page number and takes what is left,
   centred — the footer is a three-part rule, not a mark with a gap after it. */
.sheet__cr {
    flex: 1 1 auto; text-align: center; min-width: 0;
    font: .68rem/1 Palatino, Georgia, serif; letter-spacing: .02em;
    color: var(--score-ink); opacity: .55;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
@media (max-width: 480px) {
    /* On a phone the whole line will not sit between the two, so it goes under
       them rather than pushing the page number off the sheet. */
    .sheet__foot { flex-wrap: wrap; row-gap: .25rem; }
    .sheet__cr { order: 3; flex-basis: 100%; text-align: center; }
}
.sheet__pg {
    font: .74rem/1 Palatino, Georgia, serif; letter-spacing: .04em;
    color: var(--score-ink); opacity: .7; font-variant-numeric: tabular-nums;
}
.tport__follow { margin-left: .35rem; }
.tport__follow .seg__b { padding-left: .62rem; padding-right: .62rem; }

/* THE LINE, WHEN IT IS ASKED FOR. It is drawn in the accent and thin: it marks
   an instant, and an instant is a hairline. On the moon edition the same
   accent is the brighter cut, so this needs nothing of its own. */
#score #play-cursor, #score2 #play-cursor {
    stroke: var(--accent);
    stroke-width: 1.5;
}
/* --------------------------------------------------------- the key control */
.fld--key > .lbl { display: flex; align-items: baseline; gap: .4rem; }
.fld__pos {
    font-size: .62rem; font-weight: 700; letter-spacing: .04em;
    text-transform: none; color: var(--accent);
}
.fld--key { position: relative; }
.keystep {
    position: absolute; right: .3rem; bottom: .3rem;
    display: inline-flex; gap: 2px;
}
.keystep__b {
    width: 1.35rem; height: 1.35rem; padding: 0; line-height: 1;
    display: inline-flex; align-items: center; justify-content: center;
    font: inherit; font-size: .82rem;
    border: 1px solid var(--line); border-radius: 6px;
    background: var(--paper); color: var(--ink-55); cursor: pointer;
    transition: border-color .16s var(--ease), color .16s var(--ease);
}
.keystep__b:hover { border-color: var(--accent); color: var(--accent); }
.keystep__b:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Room for the two buttons so a long key name never runs under them. */
.fld--key .field { padding-right: 3.4rem; }
/* --------------------------------------------------------- the hero, tightened
   The band between the figures and the desk was a screen of nothing on a
   laptop: a full band's padding under the hero, then the desk band's own
   padding above its eyebrow, then the heading's margin. Three spacings doing
   one job. */
.hero { padding-bottom: 1.4rem; }
.hero + .band, #sheet { padding-top: 1.2rem; }
#sheet > .wrap > .eyebrow { margin-bottom: .15rem; }
#sheet > .wrap > .d2 { margin-bottom: .8rem; }
.band { padding-block: 2rem; }
@media (max-width: 720px) { .band { padding-block: 1.4rem; } }

/* ======================================================== THE POPPED SHEET
   Nothing but the engraving, filling the window and refitting as it changes
   size — the scale is applyView()'s, which already runs on resize.

   The page bar survives when there is more than one page, because a sheet you
   cannot turn is half a sheet. It is small and it sits over the paper rather
   than under it, so it costs the music no room. */
html[data-sheet-only] .nav,
html[data-sheet-only] .menu,
html[data-sheet-only] .foot,
html[data-sheet-only] .hero,
html[data-sheet-only] .skip,
html[data-sheet-only] .console,
html[data-sheet-only] #exportband,
html[data-sheet-only] #manual,
html[data-sheet-only] .shelf,
html[data-sheet-only] #sheet > .wrap > .eyebrow,
html[data-sheet-only] #sheet > .wrap > .d2,
html[data-sheet-only] .band:not(#sheet) { display: none !important; }

html[data-sheet-only] body { padding: 0; }
html[data-sheet-only] .band,
html[data-sheet-only] #sheet { padding: 0 !important; }
html[data-sheet-only] .wrap { max-width: none !important; padding-inline: .5rem !important; }
html[data-sheet-only] .sheet {
    border: 0; box-shadow: none; border-radius: 0;
    padding: .5rem .4rem 0; margin: 0 !important;
}
html[data-sheet-only] .sheet__head { margin-bottom: .4rem; }
html[data-sheet-only] .sheet__title { font-size: clamp(1rem, 2.2vw, 1.4rem); }

/* The page bar, small and out of the way. */
html[data-sheet-only] .sheet__nav {
    position: fixed; left: 50%; transform: translateX(-50%);
    bottom: .6rem; z-index: 30;
    margin: 0; padding: .18rem .3rem;
    border-radius: 999px; border: 1px solid var(--line);
    background: color-mix(in srgb, var(--paper) 88%, transparent);
    backdrop-filter: blur(8px);
    opacity: .38; transition: opacity .2s ease;
}
html[data-sheet-only]:hover .sheet__nav { opacity: 1; }
html[data-sheet-only] .sheet__nav .seg__b {
    min-width: 1.7rem; font-size: .78rem; padding: .12rem .34rem;
}
html[data-sheet-only] .sheet__count { min-width: 5.4rem; font-size: .68rem; }
/* THE POPPED SHEET KEEPS ITS FOOT. It is a sheet, and a sheet carries its
   mark, its line and its number — hiding it here meant the one view people
   actually read music in was the one view with nothing at the bottom of it.
   It is set small and quiet so it costs the music almost nothing. */
html[data-sheet-only] .sheet__foot {
    margin-top: .3rem; padding-top: .3rem; font-size: .62rem;
}
html[data-sheet-only] .sheet__cr { font-size: .6rem; }

/* ==================================================== THE DESK, TIGHTENED
   Three complaints, one cause: rows that stretch to the width of the panel
   whether or not they have anything to put there.

   THE EXPORT RAIL WAS THE WORST OF IT. Ten buttons in a flex row with room to
   spare gave every button a sixth of a very wide panel, so a rail of ten short
   words ran the whole width of the screen with a finger of air between each —
   a control the width of a desk that holds four hundred pixels of text. It
   hugs its own content now and sits where the eye already is. */
.exports, .run__exports { justify-content: flex-start; }
.exports .seg__b, .run__exports .seg__b { flex: 0 0 auto; }
.run--take > .run__exports { flex: 0 1 auto; }
/* The console body is a GRID, and in a grid it is justify-self that governs
   the inline axis — align-self, which is what was tried first, moved the rail
   up and down and left it exactly as wide as it had been. width:max-content is
   what actually makes the rail the size of the ten words in it. */
#exportband .console__body > .exports {
    justify-self: start;
    width: max-content; max-width: 100%;
}

/* The keyboard line is a caption, not a control: its own quiet row under the
   buttons rather than a label shoved to the far right of them by margin-left
   auto, which is what put it half a screen from the keys it describes. */
.run__kbd {
    margin: .5rem 0 0; display: flex; flex-wrap: wrap; gap: .3rem;
    align-items: center; font-size: .72rem; color: var(--ink-55);
}
.run__kbd span { opacity: .45; }
.run__kbd kbd {
    font: 600 .68rem/1 ui-monospace, SFMono-Regular, Menlo, monospace;
    padding: .2rem .38rem; border: 1px solid var(--line); border-radius: 5px;
    background: var(--paper-2, transparent); color: var(--ink);
}

/* ------------------------------------------------- less air around the music
   In fit mode the window IS the page, and every pixel the chrome takes is a
   pixel of music — measured, the title block, the page bar and the footer line
   were costing 178 of them, which on a laptop is most of a system. The type
   stays the size it is; it is the space around it that goes. */
.page--fit .sheet__head { margin-bottom: .4rem; gap: .05rem; }
.page--fit .sheet__by { margin-top: .28rem; padding-top: .28rem; }
.page--fit .sheet__nav { margin-top: .35rem; }
.page--fit .sheet__foot { margin-top: .3rem; }
.page--fit.sheet { padding-bottom: .55rem; }

/* ================================================== THE DESK ON A PHONE
   A GRID COLUMN IS max-content UNTIL YOU SAY OTHERWISE, and .console__body is
   a grid. One unwrappable row inside it — the five-composer rail — therefore
   set the width of every row in the panel: measured on a 375px phone, a 335px
   console laying out 498px children, with overflow:hidden quietly cutting the
   last composer, the right half of the key field and the end of COMPOSE A
   SHEET. Naming the track minmax(0, 1fr) is the whole fix; it is the same
   trap the house grids were fixed for. */

/* And then the rail itself has to be able to wrap, or it would still be 498px
   wide inside a track that now refuses to grow for it — clipped just the same.
   Below 620 it becomes chips, the way the export rail already does, and the
   sliding thumb is switched off rather than left pointing at a segment that
   has moved to another row. */
@media (max-width: 620px) {
    /* EVERY RAIL IN THE DESK, not one at a time. Chasing them individually
       found the composer rail, then the editor's tool rails at 783px, then the
       instrument rack's — they are all the same object, a row of buttons that
       does not wrap, and each one cut the panel the same way. The page bar is
       the exception: three small controls that already fit, and wrapping them
       would put the arrows on separate lines from the count. */
    #main .seg:not(.sheet__nav) { flex-wrap: wrap; overflow: visible; }
    .tport { flex-wrap: wrap; }
    .tools { gap: .6rem; }
    .tools__grp, .rack__row, .rack__seg { min-width: 0; max-width: 100%; }
    .run__kbd { font-size: .68rem; }
}


/* =============================================================================
   THE CONTROL LAYER — ONE SPECIFICATION
   -----------------------------------------------------------------------------
   APPENDED AT THE END OF THIS FILE ON PURPOSE. Several rules below tie on
   specificity with the ones they replace, and a tie is broken by source order.
   Anything added after this block has to clear (1,x,x) to reach these controls.

   WHAT WAS WRONG, COUNTED RATHER THAN GUESSED. Measured in a browser on the
   shipped page at 1280px: the fourteen segmented rails stood at FOUR heights
   (41px x9, 38.4 x3, 37.5, 45.4), their segments at four more (35 x60, 32.4
   x12, 31.5 x10, 39.4 x6), and the words in them at three sizes (12.48px x72,
   11.52 x10, 15.2 x6). The free controls added six heights of their own
   (38.4 x11, 25 x8, 46.3 x3, 27.9 x2, 21.6 x2, 56.2). Nobody reads those
   numbers and everybody sees them: it is the same staggered-row carelessness
   the transport was fixed for at line 464, spread across six panels.

   WHY THE WHOLE BLOCK IS SCOPED TO #main. Every control this touches is inside
   #main — counted, not assumed: 102 of 102 .seg__b, 14 of 14 .segp, 6 of 6
   .tport__b, 3 of 3 fields, 2 of 2 .sw, 8 of 8 .shelf__b. The scope says these
   are the DESK'S controls and not the shared chrome, and it is also the only
   thing that reliably beats assets/css/dark.css, which indexdark.html links
   AFTER this sheet and whose overrides top out at (0,3,1). An id column of 1
   beats any number of classes, so one rule serves both editions and there is
   no twin to keep in step. Where dark.css is already RIGHT, this block stays
   out of its way and says so at the rule.
   ========================================================================== */
#main {
    /* THE STROKE IS HALF THE INK, MIXED WITH THE KEYWORD `transparent`.
       --line is rgba(30,38,43,.12) and composites to 1.26:1 on paper — on the
       page, on the --tint transport ground and on the console's 2.4% wash, the
       same 1.26 on all three — and 1.61:1 on the dark ground. That is a rumour
       rather than a boundary, and it is under the 3:1 WCAG 1.4.11 asks of a
       control's edge in BOTH editions. Every control drawn in it bought its
       presence back with bulk instead, which is what the 8px corners around
       .6rem of padding on the transport keys actually were.

       MIXED WITH `transparent`, NEVER WITH var(--paper): a .band--deep section
       repoints --ink to white and leaves --paper white, so a two-token mix
       resolves white-on-white exactly where the outline is needed most.
       Composer ships no .band--deep today (grepped, zero) — this is written
       for the day somebody adds one, and it costs nothing now.

       AND THAT IS WHY NONE OF THESE FIVE NEEDS A DARK TWIN: they are mixes of
       var(--ink), which dark.css re-points to #FFFFFF, so they flip on their
       own. Measured: 50% ink resolves to rgb(142,146,149) on paper and
       rgb(133,136,138) on the dark transport ground. */
    --ctl-line:  color-mix(in srgb, var(--ink) 50%, transparent);
    /* A divider INSIDE a bounded object is not the object's own boundary, so
       it is not asked to carry 3:1 on its own — the frame around it already
       does. This draws the four seams inside the transport switch. */
    --ctl-div:   color-mix(in srgb, var(--ink) 26%, transparent);
    /* Disabled is the one state allowed under the floor: WCAG exempts inactive
       controls, and a dead control that measures like a live one is lying
       about whether it can be pressed. */
    --ctl-off:   color-mix(in srgb, var(--ink) 38%, transparent);
    --ctl-wash:  color-mix(in srgb, var(--ink)  7%, transparent);
    --ctl-press: color-mix(in srgb, var(--ink) 14%, transparent);

    /* ONE RUNG, AND IT IS THE ONE THE TRANSPORT ALREADY CHOSE. 2.4rem is what
       --tport-h has been since the nine-pixel-stagger pass at line 464; the
       rest of the desk simply joins it rather than a fifth number being
       invented for everybody to be uneven against. */
    --ctl-h:    2.4rem;
    /* AN ACTION IS NOT A LOZENGE, AND A CHOICE IS. 999px says "one of a set,
       exactly one of which is true" — right for the 102 pills, wrong for a
       key, a field or a switch. 4px on a 38.4px box is a ratio of .10; a
       corner radius is a chamfer, a physical constant, not a proportion, so it
       does not scale with the box. */
    --ctl-r:    4px;
    --pill-r:   999px;
    /* The micro-label. It is a token because .lbl and .fld > .lbl were two
       different sizes of the same caption and the only way that stays fixed is
       if there is one place to read the number from. */
    --ctl-cap:  .625rem;

    /* THE ONE FILLED KEY, AS TOKENS RATHER THAN AS TWO RULES PLUS A BUG.
       Play was `.tport__b--go` (ink) plus `html[data-dark] .tport__b--go`
       (accent) at line 112, and its :hover was a literal `#000 / #fff` at line
       111. Putting the edition difference in the VALUE instead of in a second
       rule leaves one selector, which is what makes both bugs below
       impossible rather than merely fixed. */
    --go-fill:  var(--ink);
    --go-on:    var(--paper);
    --go-lit:   color-mix(in srgb, var(--ink) 74%, var(--accent));
    --go-press: color-mix(in srgb, var(--ink) 62%, var(--accent));
}
/* THE ONLY TOKEN TWIN IN THE BLOCK, AND IT IS UNAVOIDABLE. --go-fill is the
   one value that is not derived from the ink: paper fills Play with ink and
   the moon edition fills it with the accent, which is a real editorial
   difference and not an artefact. Everything else here flips by itself. */
html[data-dark] #main {
    --go-fill:  var(--accent);
    --go-on:    var(--on-accent);
    --go-lit:   color-mix(in srgb, var(--accent) 74%, var(--ink));
    --go-press: color-mix(in srgb, var(--accent) 62%, var(--ink));
}

/* ONE RULE, SIX CLASSES, EVERY KEY ON THE DESK.
   Weight comes DOWN to 600 and tracking goes UP to .12em at 10px, because
   what makes a small capital legible is the space between the letters, not
   the mass inside them: at 700/.02em an 11px O closes its counter and the
   label stops being a word and becomes a bar of grey. The six recipes this
   replaces were .86rem/700 on the transport keys, .78rem/600 on the rail
   segments, .78rem/600 on the switches, .64rem/800 on the shelf keys,
   .58rem/700 on the .tbtn pair and .82rem/400 on the key steppers. */
#main .seg__b,
#main .tport__b,
#main .shelf__b,
#main .keystep__b,
#main .tbtn,
#main .sw {
    position: relative;
    box-sizing: border-box;
    height: var(--ctl-h); min-height: 0;
    padding: 0 .75rem;
    display: inline-flex; align-items: center; justify-content: center; gap: .5rem;
    font: inherit;
    font-size: var(--ctl-cap); font-weight: 600; letter-spacing: .12em;
    text-transform: uppercase; line-height: 1; white-space: nowrap;
    border: 1px solid var(--ctl-line); border-radius: var(--ctl-r);
    background: transparent; color: var(--ink-77);
    cursor: pointer; -webkit-tap-highlight-color: transparent;
    transition: border-color .18s var(--ease), color .18s var(--ease),
                background-color .18s var(--ease);
}
/* The pills keep the lozenge. Three of the four shapes on this desk are
   saying something and this is the one that says the most: a 999px track with
   one thumb in it reads as "these are alternatives" before a word of it has
   been read. Squaring them would buy a tidier screenshot and throw away the
   only part of the design still saying what a control does. */
#main .seg__b { border-radius: var(--pill-r); }
#main .keystep__b { width: 1.6rem; height: 1.6rem; padding: 0; }
#main .sw { justify-content: flex-start; }
/* THE SWITCHES GIVE UP THEIR 999px AND KEEP EVERYTHING ELSE. A .sw sits in the
   transport row between squared keys and two segmented rails, and a lozenge
   there reads as a segment that failed to join a rail. It is not a choice out
   of a set; it is a switch, and it holds a checkbox that says so. */

/* Hover is the same object differently lit: ink to full strength, and nothing
   else. Written here rather than left to dark.css because #main out-specifies
   it either way, and both values are ink-derived so one declaration is right
   in both editions. Scoped to (hover: hover) — a touch browser keeps the last
   tapped element in :hover, which leaves a phone with one permanently lit key
   and no way to unlight it. */
@media (hover: hover) {
    #main .seg__b:not(:disabled):hover,
    #main .tport__b:not(:disabled):hover,
    #main .shelf__b:not(:disabled):hover,
    #main .keystep__b:not(:disabled):hover,
    #main .tbtn:not(:disabled):hover,
    #main .sw:hover { border-color: var(--ink); color: var(--ink); }
    #main .keystep__b:not(:disabled):hover { border-color: var(--accent); color: var(--accent); }
}
#main .seg__b:disabled, #main .tport__b:disabled,
#main .shelf__b:disabled, #main .keystep__b:disabled, #main .tbtn:disabled {
    border-color: var(--ctl-off); color: var(--ctl-off); cursor: not-allowed;
}
/* A checked switch keeps the full-ink border site.css gives it — that is the
   state signal, and the box inside it is about to say the same thing again. */
#main .sw:has(input:checked) { border-color: var(--ink); color: var(--ink); }

/* ============================================================== THE RAILS
   THE WELL AND THE DROP SHADOW GO, AND MEASUREMENT IS WHY. The track was a
   gradient of ink at 4.3% falling to 2.4% under a 5.5% inset shadow;
   composited on paper those stops are #F5F6F6 and #FAFAFA, which measure
   1.09:1 and 1.05:1. That is not a groove, it is a smudge nobody can see, and
   the only reason the control read at all was the thumb's two drop shadows at
   26% and 14% — bulk bought to make up for a boundary that was never drawn.
   Draw the boundary instead: one hairline at half the ink.

   It also deletes both of this file's html[data-dark] rail blocks. They
   existed only because ink at single-digit alpha is invisible on a dark ground
   and had to be restated in light; a token that flips needs no such thing. */
#main .segp {
    height: var(--ctl-h); box-sizing: border-box;
    padding: 2px; border: 1px solid var(--ctl-line); border-radius: var(--pill-r);
    background: none; box-shadow: none;
}
#main .segp__thumb { box-shadow: none; border-radius: var(--pill-r); }
/* The segment is the rail less its own 2px well and 1px border on each side —
   that 6 is the same 6 the transport has been computing since line 495, and it
   is why the well must stay 2px and the frame 1px. segpill.js reads
   el.offsetWidth and el.offsetLeft and writes them onto the thumb, and
   offsetWidth is a border box that excludes margin, so the segments must also
   stay edge to edge: gap 0, no margins inside a rail. Verified live after the
   change, on every rail and after driving a click: thumb.style.width === the
   chosen segment's offsetWidth (92 === 92) and translateX === offsetLeft
   (127 === 127), 9 of 9 rails registered, rect delta 0.2px. */
#main .segp .seg__b {
    height: calc(var(--ctl-h) - 6px);
    padding: 0 .7rem;
    background: transparent;
    /* NO BORDER AT ALL, AND IT IS A FIX RATHER THAN A TIDY-UP. The segments
       carried `border: 1px solid transparent`, and dark.css:244 ships
       `html[data-dark] .seg__b:hover { border-color: var(--ink) }` at (0,3,1)
       AFTER this sheet — so in the moon edition every segment you pointed at
       drew a full WHITE box around itself inside the track, in all fourteen
       rails, defeating "the segments give up their own outline — the track is
       the outline now" everywhere on the page. Verified before and after:
       rgb(255,255,255), then border-width 0px. A colour cannot paint a border
       that has no width, so this settles it structurally and the phone
       fallback re-declares the whole shorthand because down there the segments
       really are chips and need one. */
    border: 0;
}
#main .segp .seg__b:hover { color: var(--ink); background: transparent; }
#main .segp .seg__b[aria-pressed="true"] { color: var(--paper); background: transparent; }
/* THE SECOND DARK BUG, AND IT WAS NOT IN THE BRIEF EITHER. dark.css:244 also
   sets `color: var(--ink)`, which at night is white — so hovering the segment
   you had ALREADY CHOSEN painted a white label onto the white thumb and the
   choice vanished under your own pointer. Measured: rgb(255,255,255) on
   rgb(255,255,255), which is 1.00:1. This selector is (1,4,0); even without
   the id it would be (0,4,0) and the class column is compared before the
   element column, so 4 beats dark.css's (0,3,1) on its own. No twin is needed
   in either direction: --paper flips by itself. After: 18.96:1. */
#main .segp .seg__b[aria-pressed="true"]:hover { color: var(--paper); }
#main .segp .seg__b:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
/* The ring on a CHOSEN segment is drawn over the thumb, and the inset offset
   the track's overflow makes necessary would put --accent on it: measured
   2.80:1 against the moon edition's white thumb. In the paper colour it is
   15.36:1 on the ink thumb and 18.96:1 on the white one. */
#main .segp .seg__b[aria-pressed="true"]:focus-visible { outline-color: var(--paper); }

/* AN ACTION RAIL'S THUMB IS A CURSOR, NOT A STATE. The ten exports never
   choose anything, and a filled plate means "this is the one chosen"
   everywhere else on this page — the old rail said the wrong thing twice, and
   then had to write var(--paper) on top of var(--ink) to keep the label
   readable. Drawn as the frame's own hairline at full ink with nothing inside
   it, the pointer is HERE and nothing has been decided, and the label stays
   on the paper where it started. box-sizing is border-box because the script
   sets the width from offsetWidth and a border added outside that width would
   stand the plate two pixels wider than its station. */
#main .segp--action .segp__thumb { background: transparent; border: 1px solid var(--ink); box-sizing: border-box; }
#main .segp--action .seg__b:hover,
#main .segp--action .seg__b:focus-visible { color: var(--ink); }

/* THE DURATIONS KEEP THEIR FACE, and it is the one type exemption here.
   whole / half / quarter / 8th are the score's vocabulary, not the desk's, and
   they are set in the sheet's own serif for that reason. They take the rail's
   geometry and nothing else. */
#main .segp.dur .seg__b, #main .dur .seg__b {
    font-family: Palatino, Georgia, serif; font-size: .95rem;
    letter-spacing: normal; text-transform: none; min-width: 2.6rem;
}
/* Tracking a SINGLE glyph is just a trailing space that pushes it off its own
   centre, so the popped sheet's page arrows opt out of everything but the
   stroke — and out of the rung as well, because that bar is deliberately tiny. */
#main .sheet__nav .seg__b {
    letter-spacing: normal; text-transform: none;
    font-size: 1rem; height: auto; padding: .25rem .6rem; border-radius: var(--ctl-r);
}

/* ==================================================== THE TRANSPORT SWITCH
   BACK, REWIND, PLAY, FORWARD AND END ARE ONE CONTROL, SO THEY ARE DRAWN AS
   ONE. Five separate boxes put twenty hairline edges on the row for five keys
   that are never apart, never reordered and never used for anything but moving
   through the same sheet. Joined: four outer edges and four dividers — eight
   strokes for identical function.

   But the argument that matters on THIS page is not economy. Composer's
   transport is the one control a person works while their eyes are on the
   stave, and five loose boxes on a wrapping row have no findable geometry. A
   bar with two ends has a middle a hand can reach without looking, and the
   middle is Play.

   The group is built by js/app.js (see the TransportKeys block that replaces
   setPlayLabel) rather than written into the markup, because indexdark.html is
   GENERATED from index.html by tools/mkdark.py and one wrapper written in
   script cannot fall out of step between the two editions the way two
   hand-edited files can. Without the script the five keys simply keep their
   own 4px boxes: it degrades, it does not break.

   Measured after: the group is 274 x 38.4 on a mouse and 274 x 50 on a finger,
   at every width from 320 up, and it never wraps — it is one flex ITEM in the
   row with flex-wrap: nowrap inside it, so the row can only break AROUND it. */
/* THE TRANSPORT'S OWN KNOB IS POINTED AT THE RUNG RATHER THAN LEFT AT 2.4rem.
   Every rule that reads --tport-h is out-specified by this block already, so on
   a mouse this line changes nothing — both values are 2.4rem. It matters on a
   coarse pointer, where --ctl-h goes to 3.125rem: without it the row's readout
   and anything else still sizing itself from --tport-h would stay at 38.4 while
   everything around it stood at 50, and the nine-pixel stagger the note at line
   464 was written about would come back on the one device that cannot afford
   it. Measured at 375 on a finger: every control in the row at 50, the five
   keys inside the switch at 48 because the group's border sits outside them. */
#main .tport { --tport-h: var(--ctl-h); }

#main .tport__keys {
    display: inline-flex; align-items: stretch; flex: 0 0 auto; flex-wrap: nowrap;
    box-sizing: border-box; height: var(--ctl-h);
    border: 1px solid var(--ctl-line); border-radius: var(--ctl-r);
    background: transparent;
}
/* Inside the group a key has no box of its own — the group is the box. The
   inner corners are 3px against the group's 4px, because a corner concentric
   with the one outside it is that radius minus the stroke between them. */
#main .tport__keys .tport__b {
    height: auto; border: 0; border-radius: 0; padding: 0; min-width: 2.75rem;
}
#main .tport__keys .tport__b + .tport__b { border-left: 1px solid var(--ctl-div); }
#main .tport__keys .tport__b:first-child { border-radius: 3px 0 0 3px; }
#main .tport__keys .tport__b:last-child  { border-radius: 0 3px 3px 0; }
/* EVERY DIVIDER STAYS DRAWN, at rest and under the pointer, and this rule is
   load-bearing rather than tidy: `border-color: transparent` at (1,3,0) beats
   the `+` rule at (1,2,0) and would blank the hovered key's left divider — the
   one edge that keeps the fill from being an unbounded block — so the same
   declaration puts it straight back. Verified: 26% ink at rest and 26% ink
   hovered, in both editions. */
#main .tport__keys .tport__b:not(:disabled):hover { border-color: transparent; border-left-color: var(--ctl-div); }
/* The ring is this sheet's own — 2px of accent at 2px of offset, the same one
   .keystep__b already draws — and it is declared rather than inherited so that
   nothing upstream can quietly change it. IT IS NEVER INSET, and on the moon
   edition that is not a preference: dark.css sets --accent and --accent-fill to
   the same #F97316, so an accent ring drawn inside the Play key would measure
   1.00:1 against its own fill and simply not exist. A focused key inside the
   group is raised so its ring draws OVER its neighbour instead of being cut in
   half by it. Measured: accent is 4.96:1 on the paper transport ground and
   6.55:1 on the dark one. */
#main .tport__b:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
#main .tport__keys .tport__b:focus-visible { outline-offset: 1px; z-index: 1; }

/* THE GLYPHS ARE DRAWN, AND THE FONT CLAIM WAS TESTED RATHER THAN REPEATED.
   Measured on a canvas at the key's own font, U+25B6 is 11.0886px wide in
   "DM Sans" and 11.0886px wide in a family that does not exist — identical,
   which is how you show a glyph is coming out of the platform's fallback chain
   and not the face this page loaded. The control discriminates: "A" is 9.7536
   against 9.9464 across the same pair. A fallback chain is a different chain on
   every platform, and on several of them the face that answers for a bare
   triangle is the colour emoji one — a cartoon in a greyscale console.

   AND THE WORD MOVED UNDER THE FINGER. The key itself never resized: its
   min-width is 6.2rem and both labels are narrower, so the symptom reported
   elsewhere on the estate is not this property's symptom and is not claimed
   here. What did happen is that "▶ Play" measures 42.60px against "■ Stop" at
   47.45px and the contents are centred, so every press slid the word 2.4px
   sideways. A fixed 12px box removes the difference at the source instead of
   padding around it. */
#main .tport__g { display: block; width: 12px; height: 12px; flex: 0 0 auto; fill: currentColor; }

/* THE PRIMARY, IN ONE RULE, WHICH IS THE WHOLE FIX.
   styles.css:111 was `.tport__b--go:hover { background:#000; color:#fff }` —
   two literals outside a dark block — and :112 was the dark twin that restyled
   the key but not its hover. Measured in both editions with every sheet served
   in its real order and `:hover` rewritten to an attribute selector of
   identical weight, transitions off:

     PAPER, hovering Play: background rgb(0,0,0). The literal IS live.
     DARK,  hovering Play: rgb(249,115,22) at rest AND hovered — no change at
            all. `html[data-dark] .tport__b--go` is (0,2,1) and
            `.tport__b--go:hover` is (0,2,0), so the base rule out-specifies
            the state rule and the accent survives.

   So the bug as reported — black fill on hover in the dark edition — does not
   happen, and the real dark defect is the quieter one: the primary action of
   the whole page is the ONE control in the transport that does not answer the
   pointer. Both are fixed by there being one rule and one token. After:
   paper rgb(30,38,43) -> rgb(73,45,35), a step of 1.23:1 carried mostly by the
   hue rather than the luminance, with the white label at 12.50:1; dark
   rgb(249,115,22) -> rgb(251,151,83), 1.29:1, label 8.72:1 (UP from 6.76). */
#main .tport__b--go {
    background: var(--go-fill); color: var(--go-on); border-color: var(--go-fill);
    min-width: 6.2rem;
}
#main .tport__keys .tport__b--go { min-width: 6rem; }
@media (hover: hover) {
    #main .tport__b--go:not(:disabled):hover {
        background: var(--go-lit); border-color: var(--go-lit); color: var(--go-on);
    }
    #main .tport__keys .tport__b--go:not(:disabled):hover { border-color: transparent; border-left-color: var(--ctl-div); }
}
#main .tport__b--go:not(:disabled):active { background: var(--go-press); border-color: var(--go-press); }
#main .tport__b--go:disabled { background: transparent; color: var(--ctl-off); border-color: var(--ctl-off); }
/* No lift on a transport key. .btn translates -1px because it is a call to
   action with room to move; a key on an instrument that jumps is a key that
   misses the finger already on it. */
#main .tport__b:not(:disabled):active { background: var(--ctl-press); transform: none; }

/* THE PROGRESS RAIL IS A CONTROL — the note at line 116 says the manual sells
   clicking it to seek — and its ground was var(--line) at 1.26:1, the same
   rumour the keys had. The boundary is what has to measure, not the fill: a
   1px inset hairline around a 7% ground, with the accent still spent on the
   one part of it that is a value. The ::after that carries its touch area is
   untouched, and it is the ONE control on this desk drawn under 44 — 8px tall
   with an extension of -.55rem and -1.7rem, which measures 8 + 8.8 + 27.2 =
   44.0 exactly. That is a rule I can point at, which is the standard. */
#main .prog { background: var(--ctl-wash); box-shadow: inset 0 0 0 1px var(--ctl-line); }

/* ================================================================ THE FIELDS
   Three of them, all inside a .console: #key (a select), #tempo (a number) and
   #seed (text). The 9px radius comes onto the control corner, and the border
   stops being a literal — site.css already drew it at rgba(30,38,43,.5), which
   is exactly half the ink, so naming it --ctl-line changes no pixel on paper
   and makes the field, the key and the switch demonstrably one stroke rather
   than three that happen to agree.

   AND THE FOCUS HALO GOES. site.css puts a 3px rgba cloud under a focused
   field and dark.css answers it with a white one. A soft shadow spreading out
   of a control on click is the single most un-Braun thing a field can do, and
   it is redundant: the border going to full ink is a state change, and
   :focus-visible still draws a real ring. Verified in both editions —
   box-shadow computes to `none`, the ring to 2px accent at 2px offset. */
#main .field { border-radius: var(--ctl-r); border-color: var(--ctl-line); }
#main .fld .field { height: var(--ctl-h); border-radius: var(--ctl-r); }
#main .field:focus { outline: none; border-color: var(--ink); box-shadow: none; }
#main .field:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* dark.css:217-227 pins html[data-dark] .field's border to white at 34% and
   gives the focus state its own halo, both at (0,2,1) in a sheet linked after
   this one. The #main scope is what reaches them, in one declaration for both
   editions rather than two numbers to keep in step. This does raise the moon
   edition's field outline from 34% to 50% white — 3.09:1 to 5.29:1 — a
   deliberate departure from a value dark.css chose, taken so that one
   expression draws every outline on the desk. It should be signed off rather
   than assumed. */
#main .field:disabled, #main .field[disabled] {
    border-color: var(--ctl-off); color: var(--ctl-off); background: transparent;
    cursor: not-allowed;
}

/* ============================================================= THE SWITCHES
   Three native checkboxes: countIn and autoNext in .sw chips in the transport,
   and opt-placard in the .pl-opt line under the export rail. They were the
   platform's own boxes — a rounded blue-grey lozenge on macOS, a flat square
   on Windows, a different animal on each — sitting inside a house pill, which
   is the most obviously un-Braun thing left on the page once the transport is
   done.

   THE NATIVE INPUT IS KEPT AND STYLED, NOT HIDDEN BEHIND A DRAWN SIBLING.
   appearance:none keeps :checked, :indeterminate, :disabled, :focus-visible,
   the label's for= association and the space key, with no script and no markup
   change — and the markup could not change anyway, since indexdark.html is
   generated. Decisively: .pl-opt is an <input> followed by the sentence that
   describes it, so there is no spare element to draw into.

   THE FILL IS THE STATE AND THE TICK IS THE CONFIRMATION, in that order — which
   is also the fallback. If an engine ever declines to paint ::before on a form
   control, what is left is a solid square against an empty one, still a
   complete and unambiguous switch. Verified live that ::before DOES render on
   an appearance:none input in this engine: a 4x8 box at opacity 1, white on
   paper and rgb(12,17,22) at night — the control inverts by itself.

   AND IT IS INK, NOT THE ACCENT. On this property the accent is spoken for: it
   is the playhead, the lit note and the progress fill, and it means the music
   is HERE, NOW. A count-in preference is not an instant. site.css already gives
   .sw input accent-color: var(--ink); styles.css:288 made .pl-opt's the odd one
   out at var(--accent), and it comes back to the ink with the other two. */
#main .sw input[type="checkbox"],
#main .pl-opt input[type="checkbox"] {
    appearance: none; -webkit-appearance: none;
    box-sizing: border-box;
    width: 16px; height: 16px; flex: 0 0 auto; margin: 0;
    display: inline-grid; place-content: center;
    border: 1px solid var(--ctl-line); border-radius: 2px;
    background: transparent; color: var(--paper);
    cursor: pointer;
    transition: background-color .16s var(--ease), border-color .16s var(--ease);
}
/* The tick: two borders of a small box, rotated. DRAWN rather than typed, for
   the same reason the transport's glyphs are — U+2713 lands at a different
   weight and a different baseline in every face the estate falls back through,
   and on several platforms it comes back as a colour emoji. The nudge is the
   correction for rotating about the centre of the unrotated 4x8 box. */
#main .sw input[type="checkbox"]::before,
#main .pl-opt input[type="checkbox"]::before {
    content: ''; display: block; width: 4px; height: 8px;
    border: solid currentColor; border-width: 0 2px 2px 0;
    transform: translate(0, -1px) rotate(45deg);
    opacity: 0; transition: opacity .12s var(--ease);
}
#main .sw input[type="checkbox"]:checked,
#main .pl-opt input[type="checkbox"]:checked { background: var(--ink); border-color: var(--ink); }
#main .sw input[type="checkbox"]:checked::before,
#main .pl-opt input[type="checkbox"]:checked::before { opacity: 1; }
/* A styled box has to answer :indeterminate itself: the native control drew a
   dash, appearance:none draws nothing, and a box script had set part-way would
   render as plainly UNCHECKED — a wrong answer rather than no answer. Nothing
   sets it today; it costs four lines to never be a bug. */
#main .sw input[type="checkbox"]:indeterminate,
#main .pl-opt input[type="checkbox"]:indeterminate { background: var(--ink); border-color: var(--ink); }
#main .sw input[type="checkbox"]:indeterminate::before,
#main .pl-opt input[type="checkbox"]:indeterminate::before {
    opacity: 1; width: 8px; height: 0; border-width: 0 0 2px 0; transform: none;
}
/* ONE RING, ON THE THING THAT CHANGES. site.css rings the whole .sw chip
   through :has(input:focus-visible), which drew a 999px halo around what is now
   a squared switch, and a 2px square ring on the identical control downstairs —
   two answers to one key press. The chip's ring is turned off by repeating
   site.css's exact selector: same specificity, and this sheet is linked after
   it, so no escalation is needed. Verified: the chip's outline-style computes
   to `none` and the box's to 2px solid accent at 2px offset. */
.sw:has(input:focus-visible) { outline: none; }
#main .sw input[type="checkbox"]:focus-visible,
#main .pl-opt input[type="checkbox"]:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
#main .sw input[type="checkbox"]:disabled,
#main .pl-opt input[type="checkbox"]:disabled {
    cursor: not-allowed; background: transparent;
    border-color: var(--ctl-off); color: var(--ctl-off);
}
/* The placard line is a footnote to the ten export formats rather than an
   eleventh of them, so it stays a sentence and takes only the box. */
#main .pl-opt input[type="checkbox"] { margin-top: .16rem; }

/* ================================================== ONE MICRO-LABEL RECIPE
   Six recipes did this job and two of them sat in the same panel: .lbl at
   10px/700/.18em, .console__title at 10px/700/.2em, .fld > .lbl at
   9.28px/800/.12em, .shelf__b at 10.24px/800/.05em, .tbtn at 9.28px/700/.14em,
   .heard__k at 10.88px/700/.08em. So "Composer" and "Key", stacked in one
   panel, were different sizes, weights and trackings of what is meant to be
   one voice.

   Weight comes DOWN from both 700 and 800. Tracking meets in the middle
   rather than by accident: .18em at this size is 1.8px, wide enough that the
   word comes apart into letters, and .12em is 1.2px, which at weight 600
   begins to close the counters. It is deliberately NOT the .eyebrow
   (11px/700/.19em) — an eyebrow names a section, a .lbl names a control.
   Measured after: all 23 of them at 10px / 600 / 1.4px. */
#main .console__title, #main .lbl, #main .fld > .lbl,
#main .heard__k, #main .rack__head span, #main .sheet__count {
    font-size: var(--ctl-cap); font-weight: 600; letter-spacing: .14em;
    text-transform: uppercase; color: var(--ink-55);
}
#main .fld { gap: .3rem; }
#main .fld > .lbl { margin-bottom: 0; }
/* The key cell's caption carries the scale-degree readout beside it, which is
   a VALUE and not a caption, so it opts out of the recipe. */
#main .fld__pos { font-size: .62rem; font-weight: 600; letter-spacing: .04em; text-transform: none; color: var(--accent); }
/* A hint is prose, and prose is set at one size. There were three — .78, .76
   and .74rem — three sentences pretending to be three ranks of information
   when they are all the same rank: the line that explains the control above.
   Measured after: all 14 at 12.48px. --ink-55 is 4.64:1 on paper and 6.72:1 at
   night, over the 4.5:1 floor that body copy has and the captions above do
   not. */
#main .hint, #controls .console__body > .hint { font-size: .78rem; line-height: 1.55; color: var(--ink-55); }

/* THE STATUS PIP GOES. Six .console__dot spans, one per panel head, all fixed
   at var(--accent) and none of them bound to anything — grep finds no
   reference in any js file. A pip that is always lit reports that the page has
   loaded, which the page is already reporting by being on the screen. This
   property has an EARNED one: .busy__dot, which is attached to real work and
   stops when the work does. The six spans should come out of index.html and
   indexdark.html by hand — mkdark.py is drifted and must not be re-run
   wholesale — and this rule can be deleted once they have. */
#main .console__dot { display: none; }

/* THE SEAM WASH, AND AN EDITION BUG NOBODY REPORTED.
   `.console--joined .console__head` is (0,2,0) and dark.css:213
   `html[data-dark] .console__head { background: var(--paper) }` is (0,2,1) in
   a sheet linked after this one — so the wash that makes six stacked panels
   read as one rack has been rendering on paper and silently NOT rendering in
   the moon edition. Measured: paper color(srgb .1176 .1490 .1686 / .025), dark
   flat rgb(12,17,22). One rule at (1,2,0) fixes it in both, reading one token;
   after, dark computes color(srgb 1 1 1 / 0.03) and the un-joined heads stay
   flat, which is correct. */
#main .console--joined .console__head { background: color-mix(in srgb, var(--ink) 2.5%, transparent); }

/* THE SAME PERMISSION, GIVEN TO ALL SIX PANELS INSTEAD OF TWO. The
   1fr-grows-to-min-content trap is already named and fixed further up this
   file — for #controls and #exportband only, and there are six console bodies.
   This is a GENERALISATION AND NOT A BUG FIX, and it is worth being exact
   about which: measured at 900px on the shipped file, the instrument panel's
   track was already 754.6px inside a 793px body with overflow visible, and the
   rail inside it was scrolling rather than being cut. So nothing on the page
   moves today. What it buys is that the next rail added to one of the four
   panels that were left off the list cannot re-open the trap. The two
   id-scoped copies become redundant and are deleted. The flex items need the
   same permission for the same reason — a flex item's automatic minimum is its
   content too. */
#main .console__body { grid-template-columns: minmax(0, 1fr); }
#main .tools, #main .tools__grp { min-width: 0; max-width: 100%; }

/* ============================================================= THE SLIDERS
   THERE ARE TWO, AND THE BRIEF SAYS ZERO. js/app.js builds one
   input[type=range] per staff for the levels, which is why a grep of
   index.html finds none; they measured 128 x 22 with nothing on them but
   accent-color, in a page whose rule is 44.

   THE VENDOR PSEUDO-ELEMENTS CANNOT SHARE A SELECTOR LIST. A rule naming
   ::-webkit-slider-thumb and ::-moz-range-thumb together is dropped WHOLE by
   both engines, because each treats the other's pseudo-element as a parse
   error and a selector list is all-or-nothing. They are written out twice on
   purpose; this is not a copy-paste that got away. The box is the full control
   height so the whole band is draggable rather than just the 3px track.
   Measured after: 128 x 38.4 on a mouse and 128 x 50 on a finger. */
#main .rack__lvl input[type="range"] {
    -webkit-appearance: none; appearance: none;
    width: 100%; height: var(--ctl-h); background: transparent; cursor: pointer;
}
#main .rack__lvl input[type="range"]::-webkit-slider-runnable-track {
    height: 3px; border-radius: 2px; background: var(--ctl-line);
}
#main .rack__lvl input[type="range"]::-webkit-slider-thumb {
    -webkit-appearance: none; appearance: none; border: 0;
    width: 14px; height: 14px; margin-top: -5.5px;   /* (14 - 3) / 2, centred */
    border-radius: 50%; background: var(--ink);
}
#main .rack__lvl input[type="range"]::-moz-range-track {
    height: 3px; border-radius: 2px; background: var(--ctl-line);
}
#main .rack__lvl input[type="range"]::-moz-range-thumb {
    width: 14px; height: 14px; border: 0; border-radius: 50%; background: var(--ink);
}
/* appearance:none takes the native focus ring with it, so it is given back. */
#main .rack__lvl input[type="range"]:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ONE SPECIFICATION FOR THE FOUR BUTTONS, WITHOUT TOUCHING .btn ITSELF.
   .btn is the house call-to-action on sixty-odd pages and composer's being the
   only flat one would be a bigger inconsistency than anything it would fix —
   its box, its fill and even its translateY(-1px) lift are left exactly where
   they are, on purpose, and someone may reasonably disagree: it is one rule to
   change and it should be changed everywhere or nowhere. What IS fixed is
   composer's own internal split: site.css:771 pushes `.run .btn--go` to
   .92rem/.06em while the three ghosts beside it sit at 13.12px/.02em, so four
   buttons in one row carried two type sizes and two trackings. Measured after:
   all four at 13.12px / 0.2624px, and the primary keeps its width. */
#main .run .btn--go { font-size: .82rem; letter-spacing: .02em; }

/* ============================================================== ON A FINGER
   44px IS THE TARGET A THUMB NEEDS, AND IT IS NOT THE SAME RULE AS THE 16px
   CARET FLOOR. 16px is about a caret: under it iOS Safari zooms on focus and
   carries the fixed header off the screen, and it belongs to fields. 44 belongs
   to anything a finger lands on. Both are matched on the POINTER and never on
   the width, because a narrow window somebody dragged on a desktop is neither.

   AND THE SEGMENTS ARE SIZED TO 44 RATHER THAN FAKED UP TO IT. The obvious
   move is an invisible ::after on each segment — and it does not work here,
   which I checked instead of assuming: `.segp` is `overflow-x: auto;
   overflow-y: hidden`, so a vertical extension on a child is CLIPPED by the
   track. Probed with elementFromPoint on the live page, a -6px extension hit
   the button at 3px above the segment and returned the page container at 5px
   above and at 3px BELOW. The pseudo-element measures 44 in a rect and cannot
   be pressed there. So the rung itself goes to 3.125rem: the rail becomes 50,
   the segment inside it becomes 50 - 6 = 44 for real, and every free control
   in the row stands at 50 with no stagger and no invisible geometry anywhere.

   Measured at 375 with a coarse pointer, both editions: 130 controls at exactly
   50.0, the five keys inside the transport switch at 48 (the group's border
   sits outside them), the two key steppers at 44.0, .pl-opt at 55.4, the three
   fields at 16px type. Controls under 44 with no rule expanding them: none. */
@media (pointer: coarse) {
    #main { --ctl-h: 3.125rem; }
    #main .field, #main input, #main select, #main textarea { font-size: 16px; }
    /* THE KEY STEPPERS AND THE PAGE ARROWS ARE THE TWO CONTROLS THAT CANNOT
       SIMPLY TAKE THE RUNG, and both were measured rather than waved through.
       The steppers live INSIDE the key field, so growing them to 44 means
       growing the room the field reserves for them in the same breath — the
       3.4rem reserve was cut for two 21.6px buttons and would run the key's
       own name underneath them. 2 x 44 + the 2px gap is 5.6rem, so the reserve
       is 6rem and the two are pinned 3px off the corner rather than .3rem, or
       44 + 4.8 would stand taller than the 50px field holding them. Verified at
       375: reserve 96px, field 304px wide, the buttons' bottom and right edges
       both inside the field's.
       The page arrows opt out of the rung on a mouse because the popped sheet's
       bar is deliberately tiny; on a finger they take the floor like everything
       else, as a MINIMUM so that bar can still be short where it has to be. */
    #main .keystep__b { width: 2.75rem; height: 2.75rem; font-size: .82rem; }
    #main .keystep { right: 3px; bottom: 3px; }
    #main .fld--key .field { padding-right: 6rem; }
    #main .sheet__nav .seg__b { min-height: var(--ctl-h); }
    /* The placard is a sentence with a box in it and had no height of its own;
       it measured 21.9px, half a target. Padding alone does not finish the job
       because the sentence beside the box does not grow with the caret floor,
       so the floor is stated as a floor. */
    #main .pl-opt { min-height: 2.75rem; padding-block: .5rem; }
}

/* A RAIL TOO WIDE FOR THE SCREEN STOPS BEING A RAIL, AND A WELL WITH NO THUMB
   IN IT IS A GROOVE DRAWN FOR NOTHING. Below 620px every rail wraps into chips
   and the sliding thumb is switched off — a thumb left pointing at a station
   that has moved to another row is worse than no thumb — so the track is
   holding an outline around a set of things it no longer relates. It goes, the
   chips take the outline instead, and they are chips: a lozenge is still the
   right shape for one of a set of choices whether or not they are in a rail.

   ONE BREAKPOINT, NOT TWO. The exports wrapped at 720 and every other rail at
   620, the same object behaving two ways for no reason anybody could state.
   They meet at 620 and the .exports-only 720px block is deleted.

   Measured at 600px, second read (the first read after a resize comes back
   stale in this harness — see the install notes): rails transparent and
   wrapping, thumbs display:none, chips 56 x 50 at 999px with a 50%-ink
   hairline, the chosen chip ink-filled with a white label, document
   scrollWidth 600 against a 600px window. */
@media (max-width: 620px) {
    #main .segp:not(.sheet__nav) {
        height: auto; flex-wrap: wrap; overflow: visible;
        background: none; border-color: transparent; box-shadow: none; padding: 0;
    }
    #main .segp:not(.sheet__nav) .segp__thumb { display: none; }
    #main .segp:not(.sheet__nav) .seg__b {
        height: var(--ctl-h); margin: 2px; flex: 0 0 auto;
        border: 1px solid var(--ctl-line); border-radius: var(--pill-r);
    }
    #main .segp:not(.sheet__nav) .seg__b[aria-pressed="true"] {
        background: var(--ink); color: var(--paper); border-color: var(--ink);
    }
    #main .segp:not(.sheet__nav) .seg__b:hover { border-color: var(--ink); color: var(--ink); }
    #main .segp:not(.sheet__nav) .seg__b[aria-pressed="true"]:hover { color: var(--paper); }
}

/* ==================================================== WHEN THE READER ASKS
   A design carried by one-pixel strokes fails hardest here, so it says what it
   wants rather than leaving it to the compositor. None of these three
   questions was answered anywhere on this property before — not in this file,
   not in site.css, not in dark.css.

   NO DARK TWIN IS NEEDED FOR THE CONTRAST BLOCK: --ctl-line is redefined as
   var(--ink), and a var() resolves against the element it lands on, so the
   moon edition gets white and the paper edition near-black from one line. */
@media (prefers-reduced-motion: reduce) {
    #main .seg__b, #main .tport__b, #main .shelf__b, #main .keystep__b,
    #main .tbtn, #main .sw, #main .field,
    #main .sw input[type="checkbox"], #main .sw input[type="checkbox"]::before,
    #main .pl-opt input[type="checkbox"], #main .pl-opt input[type="checkbox"]::before { transition: none; }
}
@media (prefers-contrast: more) {
    #main { --ctl-line: var(--ink); --ctl-div: var(--ink);
            --ctl-off: color-mix(in srgb, var(--ink) 62%, transparent); }
}
/* Forced colours strips author backgrounds, and on this desk the two things
   that carry state ARE backgrounds — the sliding thumb and the checkbox fill —
   so without this the rails would offer nine choices and indicate none, and the
   three switches would come back blank in both directions. The answer is to
   opt IN and re-say the state in system colours, not to set
   forced-color-adjust:none and hand a high-contrast reader back the palette
   they turned off. The engraving is deliberately not touched: it is the music,
   and whether a stave should become a system colour is not this rule's call. */
@media (forced-colors: active) {
    #main .seg__b, #main .tport__b, #main .shelf__b, #main .keystep__b,
    #main .tbtn, #main .sw, #main .segp, #main .tport__keys, #main .field {
        border: 1px solid ButtonBorder; background: ButtonFace; color: ButtonText;
    }
    #main .tport__keys .tport__b { border: 0; }
    #main .tport__keys .tport__b + .tport__b { border-left: 1px solid ButtonBorder; }
    #main .segp__thumb { display: none; }
    #main .seg__b[aria-pressed="true"] { background: Highlight; color: HighlightText; forced-color-adjust: none; }
    #main .tport__b--go { background: Highlight; color: HighlightText; }
    #main .tport__g { fill: currentColor; }
    #main .sw input[type="checkbox"], #main .pl-opt input[type="checkbox"] { border-color: ButtonBorder; background: Canvas; }
    #main .sw input[type="checkbox"]:checked, #main .sw input[type="checkbox"]:indeterminate,
    #main .pl-opt input[type="checkbox"]:checked, #main .pl-opt input[type="checkbox"]:indeterminate {
        background: Highlight; border-color: Highlight; color: HighlightText;
    }
    #main .seg__b:disabled, #main .tport__b:disabled, #main .field:disabled { border-color: GrayText; color: GrayText; }
}


/* ------------------------------------------------- A RAIL IS AS LONG AS ITS KEYS
   Measured on the running page: the COMPOSER rail was 935px wide holding 353px
   of pills — 582 pixels of empty track, a switch four times longer than the
   thing it switches. Every other rail on the page came out with exactly 6px to
   spare, and the difference was nothing to do with the rails: those sit in flex
   parents, where `flex: 0 1 auto` shrink-wraps them, and this one sits in a
   BLOCK parent, where a display:flex box is still a block-level box and fills
   the line like a paragraph.

   So the width is stated rather than inherited. max-content is the sum of the
   segments, which is what a segmented switch is; the 100% cap keeps the two
   rails that genuinely overflow — the rack's, at 736px of keys in 621px of room
   — scrolling inside the track instead of widening the console. One rule, and
   the rail can no longer be a different length depending on what it happens to
   be sitting in. */
#main .segp,
#main .seg:not(.sheet__nav):not(.seg--wide) {
    width: max-content;
    max-width: 100%;
}


/* ------------------------------------------- THE COMPOSE KEY LIGHTS WHEN IT FIRES
   Composing is the one press on this page with no other acknowledgement: the
   sheet is engraved in a few milliseconds, so there is no spinner to show and
   nothing moves except the music itself — and if the dice happen to deal a
   piece in the same key and style as the last one, a reader who pressed it can
   be genuinely unsure whether anything happened at all. So the key says so.

   GREEN, AND IT IS THE HOUSE GREEN. --free already carries an edition twin
   (#15803D on paper, #4ADE80 on the moon) because dark.css walks it UP its own
   scale — #15803D measures 2.4:1 on the night ground and would be a bruise
   rather than a light.

   THE LABEL RIDES --paper, WHICH IS THE WHOLE TRICK. It is #ffffff on paper and
   #0C1116 at night, so one declaration lands white type on a deep green by day
   and near-black type on a bright green after dark. Measured: 5.02:1 and
   10.88:1. A literal white would have been 1.74:1 in the dark edition — lit,
   and unreadable, which is the worst of both.

   :active covers the pointer for as long as it is held; .is-lit is added by
   generateNow() so the keyboard route (G) and the dice light it too, and so a
   press shorter than the eye lights it for long enough to be seen. */
#main .run .btn--go:active,
#main .run .btn--go.is-lit {
    background: var(--free);
    border-color: var(--free);
    color: var(--paper);
}
#main .run .btn--go.is-lit { transition: background-color .12s var(--ease), border-color .12s var(--ease); }
@media (prefers-reduced-motion: reduce) {
    #main .run .btn--go.is-lit { transition: none; }
}


/* --------------------------------------------- THE TRANSPORT ON ONE LINE
   Twelve controls in one row, and it wrapped onto two at every desktop width:
   measured, it wanted 1201px against 1084 available at 1440 and 906 at 1100.
   Shorter labels were the first move — COUNT-IN became IN, AUTO NEXT AUTO,
   FIT IN SCREEN SCREEN, COLOUR RED, POP OUT POP, with the full words kept in a
   title and an aria-label so a pointer and a screen reader still get the whole
   thing — but words alone could not close a 117px gap, let alone a 295px one.

   So the geometry gives up what it can spare, and only that. The segments were
   .7rem of air each side; at .45rem the twelve of them hand back 96px and the
   rail still reads as a rail rather than as crowded type. The row's own gap
   comes down a hair, and the Play key gives up a quarter of a min-width it was
   never filling — "Play" and "Stop" are four letters in a box sized for six.

   WHAT IS NOT TOUCHED: the 44px touch floor at pointer:coarse, the segment
   height, and the shared edges of the transport switch. Those were measured
   into place and a row that fits by making itself unhittable has not been fixed.

   AND IT STILL WRAPS BELOW ABOUT 1360px, WHICH IS HONEST. Twelve controls do
   not fit a laptop's inner column, and the fix for that is deciding which of
   them the page can do without — a content decision, not a padding one. */
#main .tport { column-gap: .28rem; }
#main .segp .seg__b { padding: 0 .45rem; }
#main .tport__keys .tport__b--go { min-width: 4.6rem; }


/* ------------------------------------------------------------- THE GO KEY
   GREEN WHILE IT IS RUNNING. aria-pressed is already kept in step with the
   transport, so the state is read off the attribute rather than off a second
   class that could disagree with it. --free carries its own edition twin
   (#15803D on paper, #4ADE80 at night, because dark.css walks it up its own
   scale), and the glyph rides --paper, which is white by day and near-black
   after dark: measured 5.02:1 and 10.88:1. A literal white glyph would be
   1.74:1 on the moon edition — lit, and invisible.

   The key is now the glyph alone, so it no longer has to be six characters
   wide; 3.2rem is a square-ish switch that still clears the touch floor. */
#main .tport__keys .tport__b--go { min-width: 3.2rem; }
#main .tport__keys .tport__b--go[aria-pressed="true"] {
    background: var(--free);
    border-color: var(--free);
    color: var(--paper);
}

/* THE PULSE ON ARRIVAL. Nothing else on the page says the engraving can be
   heard, so the key asks once — three breaths and done, and app.js takes the
   class away the instant anything is pressed anywhere.

   prefers-reduced-motion gets NO animation at all rather than a slower one.
   This is the single most motion-y thing on the property and that setting is
   precisely a request not to see it; the key simply sits there, which is what
   it did before. */
@keyframes tport-beckon {
    0%, 100% { background: var(--go-fill); border-color: var(--go-fill); }
    50%      { background: var(--free);    border-color: var(--free); }
}
#main .tport__keys .tport__b--go.is-beckoning:not([aria-pressed="true"]) {
    animation: tport-beckon 1.15s ease-in-out 3;
}
@media (prefers-reduced-motion: reduce) {
    #main .tport__keys .tport__b--go.is-beckoning { animation: none; }
}

/* AND THE CLOCK STOPS BREAKING THE ROW. .tport__read carried margin-left:auto
   to push the time to the right end. Measured, that auto margin resolved to
   1008px — it claims ALL the free space on the line, so the clock could never
   share a row with the controls and wrapped alone onto a second one, while
   every actual control fitted. The row was two lines tall because of a
   seventy-five-pixel readout. It sits after the last key now, at the row's own
   gap, which is where a transport's clock belongs anyway. */
#main .tport__read { margin-left: .5rem; }
