/* ============================================================================
 * styles.css  —  "Urtext" theme
 * ----------------------------------------------------------------------------
 * Grounded in the object a pianist actually has on the stand: a scholarly
 * edition. A cloth-bound blue-black board, gilt stamping, ivory pages.
 *
 * The app has two natures and the design stops fighting that. Notation is a
 * PAPER medium; the falling notes and the keyboard are an INSTRUMENT. So the
 * score sits on ivory and the instrument is lacquered ink, joined by a shared
 * type system and one rule about colour:
 *
 *   COLOUR MEANS SOMETHING — it is never decoration.
 *     gold   the right hand
 *     lapis  the left hand
 *     jade   you (your own presses, and a correct hit)
 *     "now"  is always the OPPOSITE medium: ivory on the dark instrument,
 *            ink on the ivory page
 *   Chrome — buttons, panels, rules — stays neutral so that holds.
 *
 * TYPE, three roles:
 *   Newsreader      display serif; its italic does the job a score's expression
 *                   marks do (piece titles, section heads, the running status)
 *   IBM Plex Sans   interface: labels, buttons, controls
 *   IBM Plex Mono   every MEASUREMENT — times, BPM, milliseconds, bar numbers,
 *                   scores. Tabular by construction, so nothing jitters while
 *                   the playhead moves, and readouts read as instrument data
 *                   rather than as prose.
 * All three degrade to the system stack offline.
 * ========================================================================== */

:root {
  /* the mark's size; the app's name is set at the same size beside it */
  --mark: 40px;
  /* --- ground: cloth board --- */
  --ink:        #0e1420;
  --ink-raise:  #151d2c;
  --ink-sunk:   #0b1018;
  --well:       #080c14;   /* the instrument well — deepest surface */
  --rule:       #222d42;
  --rule-soft:  #1a2333;

  /* --- pages --- */
  --ivory:      #f1ece2;
  --ivory-warm: #e9e2d4;
  --page-ink:   #171d29;
  --page-rule:  #cdc3b0;

  /* --- text on ink --- */
  --fg:         #eae6dd;
  --fg-dim:     #9aa4b5;
  --fg-faint:   #828da1;   /* 5.0:1 on the panel — the small uppercase labels
                              are set in this, and #6b7688 measured only 3.7 */

  /* --- meaning --- */
  --gold:       #e2b15c;   /* right hand */
  --gold-hi:    #f4cf8d;
  --gold-deep:  #8d6a2c;
  --lapis:      #6c9be8;   /* left hand */
  --lapis-hi:   #a3c4f5;
  --lapis-deep: #2f5490;
  --jade:       #59c2a0;   /* you */
  --jade-hi:    #8fdcc2;

  /* --- state --- */
  --ok:         #59c2a0;
  --warn:       #d9a441;
  --err:        #d2604f;

  /* --- form --- */
  /* The instrument's two halves are sized from these, and its own minimum is
     computed from them. When they were set independently the sum exceeded the
     panel's min-height and `overflow:hidden` quietly CLIPPED THE BOTTOM OF THE
     KEYS — which is why the note names only appeared once another panel was
     switched off and the keyboard finally had room. */
  --kb-h: 138px;
  --roll-min: 150px;

  --r-lg: 16px;  --r-md: 11px;  --r-sm: 8px;  --r-pill: 999px;
  --lift-1: 0 1px 0 rgba(255,255,255,.04) inset, 0 1px 2px rgba(0,0,0,.4);
  --lift-2: 0 1px 0 rgba(255,255,255,.05) inset, 0 12px 34px -14px rgba(0,0,0,.8);
  --lift-3: 0 1px 0 rgba(255,255,255,.06) inset, 0 28px 60px -22px rgba(0,0,0,.9);
  --ease: cubic-bezier(.2,.7,.3,1);

  --display: "Newsreader", ui-serif, Georgia, "Times New Roman", serif;
  --ui: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
  --data: "IBM Plex Mono", ui-monospace, "SF Mono", Menlo, monospace;
}

* { box-sizing: border-box; }
:focus { outline: none; }
:focus-visible {
  outline: 2px solid var(--lapis-hi);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}
input[type="range"]:focus-visible { outline: none; }
input[type="range"]:focus-visible::-webkit-slider-thumb { box-shadow: 0 0 0 4px rgba(163,196,245,.35); }
input[type="range"]:focus-visible::-moz-range-thumb { box-shadow: 0 0 0 4px rgba(163,196,245,.35); }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { transition: none !important; animation: none !important; }
  .paper__scroll { scroll-behavior: auto; }
}

html, body {
  margin: 0;
  min-height: 100%;
  background:
    radial-gradient(1100px 620px at 82% -12%, #1a2540 0%, rgba(0,0,0,0) 62%),
    radial-gradient(900px 540px at 6% 108%, #101a2b 0%, rgba(0,0,0,0) 58%),
    var(--ink);
  color: var(--fg);
  font-family: var(--ui);
  font-size: 15px;
  font-feature-settings: "ss01";
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
/* the radial above uses a literal so it degrades gracefully if unsupported */
html, body { background-color: var(--ink); }

.app {
  max-width: 1360px;
  margin: 0 auto;
  padding: 20px clamp(14px, 3.2vw, 34px) 30px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}

/* THE WORKSPACE fills the window and the three views SHARE what's left after
   the chrome, instead of each having a fixed height and pushing the keyboard
   below the fold. The whole premise is following the music three ways at once,
   so on any normal screen you never scroll during practice: the score and the
   instrument shrink together and keep their proportions. The reference panels
   (practice log, device & sound) sit deliberately BELOW this fold — they are
   things you read between sessions, not while playing. */
.workspace {
  min-height: calc(100vh - 50px);
  min-height: calc(100svh - 50px);
  display: flex;
  flex-direction: column;
  gap: 14px;
}

/* ============================================================ masthead ==== */
/* One row: brand, the piece and its status, actions. The piece title used to
   have a row of its own under the brand; folding it in returns ~35px to the
   panels, which is where the eye should be. */
.masthead {
  display: flex; align-items: center; justify-content: space-between;
  gap: 18px 20px; flex-wrap: wrap; flex: none;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--rule-soft);
}
.brand { display: flex; align-items: center; gap: 15px; min-width: 0; }

/* the mark: the app's icon (icon.svg) — Sisyphus pushing a note up a stair of keys. The grind. While
   the music plays, the note breathes. */
.brand__mark {
  width: var(--mark); height: var(--mark); flex: none; display: block; border-radius: 9px;
  box-shadow: 0 6px 18px -8px rgba(0,0,0,.9);
}
.is-playing .brand__note { animation: breathe 2.6s var(--ease) infinite; }
@keyframes breathe { 0%,100% { opacity: 1; } 50% { opacity: .55; } }

/* the name as large as the mark: its font size is the mark's height (the letters, which have no
   descenders, come to about 70% of it) */
.brand h1 {
  font-family: var(--display);
  font-weight: 500; font-size: var(--mark); letter-spacing: -.014em;
  margin: 0; line-height: 1; color: var(--fg); white-space: nowrap;
}
/* the tagline is not shown: the name alone is as tall as the mark, and the top bar keeps its height */
.brand__sub {
  display: none;
  margin: 3px 0 0;
  font-family: var(--display); font-style: italic; font-weight: 400;
  color: var(--fg-faint); font-size: 13.5px; letter-spacing: .005em;
}

.loaders { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
/* the two lists show only their names ("Samples…", "Saved pieces…") closed — sized to those, not to the
   longest piece in them (170 px), so the worded Exercise button fits the one row */
.loaders #sampleList { width: 118px; }
.loaders #pieceList { width: 136px; }

/* ============================================================ controls ==== */
.btn {
  font-family: var(--ui); font-weight: 500; font-size: 13px; color: var(--fg);
  background: linear-gradient(180deg, #1a2333, #131b28);
  border: 1px solid var(--rule); padding: 9px 14px; border-radius: var(--r-md);
  cursor: pointer; white-space: nowrap;
  transition: border-color .16s var(--ease), background .16s var(--ease),
              transform .06s var(--ease), color .16s var(--ease);
}
.btn:hover:not(:disabled) { border-color: #33405c; background: linear-gradient(180deg,#1f2937,#161f2e); }
.btn:active:not(:disabled) { transform: translateY(1px); }
.btn:disabled { opacity: .38; cursor: default; }
.btn--file {
  background: linear-gradient(180deg, var(--ivory), var(--ivory-warm));
  color: #131a26; border-color: transparent; font-weight: 600;
  box-shadow: 0 1px 0 rgba(255,255,255,.5) inset, 0 8px 20px -12px rgba(241,236,226,.7);
}
.btn--file:hover { background: linear-gradient(180deg, #fff, var(--ivory)); }
.btn--ghost { background: transparent; }
.btn--ghost:hover:not(:disabled) { background: rgba(255,255,255,.035); }
.btn--sm { padding: 6px 10px; font-size: 12px; border-radius: var(--r-sm); }
.btn.is-on {
  background: linear-gradient(180deg, #24344f, #1a2740);
  border-color: var(--lapis-deep); color: var(--lapis-hi);
}

/* A <select>'s open list is drawn by the OS, not by the page, and it inherits
   only the colour — so ours came up as light grey text on the system's white
   popup. `color-scheme: dark` asks the browser for its dark popup, and the
   <option> rules cover engines that honour those instead. */
.select, select {
  color-scheme: dark;
  font-family: var(--ui); font-size: 12.5px; color: var(--fg);
  background: linear-gradient(180deg,#1a2333,#131b28);
  border: 1px solid var(--rule); border-radius: var(--r-md);
  padding: 8px 9px; max-width: 220px;
  transition: border-color .16s var(--ease);
}
select option, select optgroup {
  background-color: var(--ink-raise); color: var(--fg);
  font-family: var(--ui);
}
select option:checked, select option:hover { background-color: #26334b; color: var(--fg); }
select option:disabled { color: var(--fg-faint); }
.select:hover { border-color: #33405c; }
.select--inline { max-width: 106px; padding: 5px 7px; font-size: 12px; border-radius: var(--r-sm); }

/* --- segmented control: one job, one strip. Replaces a wall of identical
       chips, so Mode and Hand read as single decisions. --- */
.seg {
  display: inline-flex; padding: 3px; gap: 2px;
  background: var(--ink-sunk); border: 1px solid var(--rule-soft);
  border-radius: var(--r-md);
}
.seg__opt { position: relative; }
.seg__opt input { position: absolute; opacity: 0; width: 1px; height: 1px; margin: 0; pointer-events: none; }
.seg__opt span {
  display: block; padding: 5px 11px; border-radius: var(--r-sm);
  font-size: 12.5px; font-weight: 500; color: var(--fg-dim);
  cursor: pointer; user-select: none; white-space: nowrap;
  transition: background .18s var(--ease), color .18s var(--ease);
}
.seg__opt:hover span { color: var(--fg); }
.seg__opt input:checked + span {
  background: linear-gradient(180deg, #26334b, #1c2739);
  color: var(--fg);
  box-shadow: 0 1px 0 rgba(255,255,255,.07) inset, 0 2px 8px -3px rgba(0,0,0,.7);
}
.seg__opt input:focus-visible + span { outline: 2px solid var(--lapis-hi); outline-offset: 2px; }

/* --- toggle pill (checkboxes) --- */
.chip {
  position: relative; display: inline-flex; align-items: center; gap: 6px;
  font-size: 12.5px; color: var(--fg-dim);
  background: transparent; border: 1px solid var(--rule-soft);
  border-radius: var(--r-pill); padding: 5px 10px; cursor: pointer;
  user-select: none; white-space: nowrap;
  transition: background .16s var(--ease), border-color .16s var(--ease), color .16s var(--ease);
}
.chip::before {
  content: ""; width: 6px; height: 6px; border-radius: 50%;
  background: var(--rule); flex: none;
  transition: background .16s var(--ease), box-shadow .16s var(--ease);
}
.chip:hover { color: var(--fg); border-color: #2c3850; }
.chip input { position: absolute; opacity: 0; width: 1px; height: 1px; margin: 0; pointer-events: none; }
.chip:has(input:checked) { color: var(--fg); border-color: #34435f; background: rgba(108,155,232,.07); }
.chip:has(input:checked)::before { background: var(--jade); box-shadow: 0 0 8px rgba(89,194,160,.7); }
.chip:has(input:focus-visible) { outline: 2px solid var(--lapis-hi); outline-offset: 2px; }

.key {
  font-family: var(--data); font-size: 10px; font-weight: 500;
  color: var(--fg-faint); border: 1px solid var(--rule);
  border-radius: 4px; padding: 1px 5px; margin-left: 3px;
  background: var(--ink-sunk);
}

/* ============================================================ now playing = */
/* the piece: it gives way first when the row is full (its name ends in "…"), so the top bar stays one
   row with the app's name at the mark's size */
.nowplaying {
  display: flex; flex-direction: column; gap: 3px; min-width: 0;
  flex: 1 1 140px; padding-left: 18px; border-left: 1px solid var(--rule-soft);
}
.nowplaying__title { display: flex; align-items: baseline; gap: 12px; min-width: 0; flex-wrap: nowrap; }
.nowplaying__tempo { flex: none; }
.nowplaying__name {
  font-family: var(--display); font-weight: 500; font-size: 22px;
  letter-spacing: -.016em; line-height: 1.1; color: var(--fg);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%;
  min-width: 0; flex: 0 1 auto;
}
.nowplaying__tempo {
  font-family: var(--data); font-size: 12px; font-weight: 400;
  color: var(--fg-faint); letter-spacing: .04em;
  padding: 3px 9px; border: 1px solid var(--rule-soft); border-radius: var(--r-pill);
}
.status {
  margin: 0; color: var(--fg-dim); font-size: 12.5px;
  font-family: var(--display); font-style: italic;
  min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.status:not(:empty)::before {
  content: ""; display: inline-block; width: 5px; height: 5px; border-radius: 50%;
  background: currentColor; opacity: .7; margin-right: 8px; vertical-align: 2px;
}
.status--ok { color: var(--ok); } .status--warn { color: var(--warn); } .status--err { color: var(--err); }

/* ============================================================ toolbar ===== */
.rail {
  display: flex; flex-wrap: wrap; align-items: flex-start;
  gap: 8px 12px; flex: none;
  border: 1px solid var(--rule-soft); background: linear-gradient(180deg, var(--ink-raise), #121926);
  border-radius: var(--r-lg); padding: 8px 12px;
  box-shadow: var(--lift-1);
}
/* The group label sits INLINE with its controls rather than stacked above
   them. Same information, roughly 40px less height per row — which goes
   straight to the three panels below. */
.rail__group { display: flex; flex-direction: row; align-items: center; gap: 10px; }
.rail__group + .rail__group { border-left: 1px solid var(--rule-soft); padding-left: 12px; }
.rail__controls { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.rail__label {
  font-family: var(--ui); font-size: 9.5px; font-weight: 600;
  letter-spacing: .14em; text-transform: uppercase; color: var(--fg-faint);
  white-space: nowrap;
}
.rail__readout {
  font-family: var(--data); font-size: 12px; color: var(--fg-dim);
  letter-spacing: .01em; padding-left: 2px; white-space: nowrap;   /* (squeezed, it broke into four lines) */
}

/* ============================================================ stage ======= */
.stage {
  display: flex; flex-direction: column; gap: 14px;
  flex: 1 1 auto; min-height: 0;
}

/* --- the page --- */
/* One grand-staff system measures 159-174px in these samples; with the panel's
   own padding the score needs ~215px before the LEFT-HAND staff is visible at
   all. It used to bottom out at 150px, which showed the treble stave and cut
   the bass one off entirely. It now takes the larger share of the stage. */
.paper {
  position: relative;
  flex: 5 1 0; min-height: 250px;           /* 5 : 6 — the falling notes get the larger share */
  padding: 0 clamp(12px, 2.6vw, 30px);
  background:
    linear-gradient(180deg, #f6f2ea 0%, var(--ivory) 30%, var(--ivory-warm) 100%);
  border-radius: var(--r-lg); overflow: hidden;
  border: 1px solid #0a0f18;
  box-shadow: var(--lift-3), 0 0 0 1px rgba(241,236,226,.06);
}
/* Fingers on the score: the score and the instrument share the height 1:1 (and the page's margins
   shrink, sheet-view.js) — a system with digits above and below is a quarter taller */
body.score-fingers .paper { flex-grow: 6; }
/* a system cut off at the bottom of the panel fades out instead of ending in
   a hard slice of stems and beams */
.paper::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 22px; z-index: 2;
  background: linear-gradient(180deg, rgba(233,226,212,0), var(--ivory-warm));
  pointer-events: none;
}
/* a gilt hairline along the top edge — the stamped rule of an edition page */
.paper::before {
  content: ""; position: absolute; inset: 0 0 auto; height: 2px; z-index: 2;
  background: linear-gradient(90deg, transparent, var(--gold-deep) 12%, var(--gold) 50%, var(--gold-deep) 88%, transparent);
  opacity: .55;
}
/* CURSOR CALIBRATION — do not put horizontal padding on this element, and do
   not let CSS scale the SVG.
   OSMD lays the score out to the container's clientWidth, which INCLUDES
   padding, so 36px of side padding made it engrave 1290px of music into a
   1218px box. `max-width:100%` then shrank the SVG by 0.944 — but the cursor
   is an absolutely positioned <img> OUTSIDE the SVG, so it was not shrunk with
   it. The result was a cursor that drifted right by +11px at the first note and
   +70px by the end of a system.
   The side inset now lives on .paper instead, and the score scrolls
   horizontally rather than being scaled, so cursor pixels and engraving pixels
   are always the same pixels. */
.paper__scroll {
  height: 100%; overflow-y: auto; overflow-x: auto; scroll-behavior: smooth;
  padding: 14px 0 12px; color: var(--page-ink);
  /* No scrollbar furniture on the page. The cursor scrolls the score for you
     during playback and the wheel still works, so the bars were permanent
     clutter on a surface that is meant to read like paper. */
  scrollbar-width: none; -ms-overflow-style: none;
}
.paper__scroll::-webkit-scrollbar { width: 0; height: 0; display: none; }
.paper__scroll svg { display: block; margin: 0 auto; max-width: none; }

/* --- the instrument: roll + keyboard as ONE object, no seam between them.
       Notes fall past the hit line and into the keys they belong to. --- */
.instrument {
  border: 1px solid var(--rule); border-radius: var(--r-lg); overflow: hidden;
  background: var(--well); box-shadow: var(--lift-3);
  display: flex; flex-direction: column;
  /* +2px for the keyboard's top hairline and sub-pixel rounding: if this ever
     comes out smaller than its two children, overflow:hidden eats the bottom
     of the keys again. */
  flex: 6 1 0; min-height: calc(var(--kb-h) + var(--roll-min) + 2px);
}
.rollwrap { position: relative; background: var(--well); flex: 1 1 auto; min-height: var(--roll-min); }
.roll { display: block; width: 100%; height: 100%; }
.kbwrap {
  position: relative; background: var(--well); flex: none;
  border-top: 1px solid rgba(241,236,226,.16);
  box-shadow: 0 -14px 26px -18px rgba(0,0,0,.95) inset;
}
.keyboard { display: block; width: 100%; height: var(--kb-h); }
/* the felt strip: what the notes pass through on their way into the keys */
.kbwrap::before {
  content: ""; position: absolute; inset: 0 0 auto; height: 5px; z-index: 3;
  background: linear-gradient(180deg, rgba(8,12,20,.85), rgba(8,12,20,0));
  pointer-events: none;
}

.is-hidden { display: none !important; }
/* out of sight but keeping its place: for controls that take turns in one slot (see "slot" below) */
.is-away { visibility: hidden !important; }

/* --- empty / no-notation states --- */
.nosheet {
  display: grid; place-items: center; border-radius: var(--r-lg);
  border: 1px dashed var(--rule); min-height: 140px; padding: 26px;
  background: repeating-linear-gradient(135deg, var(--ink-raise) 0 16px, #121a27 16px 32px);
}
.nosheet__inner { text-align: center; max-width: 560px; }
.nosheet__inner h2 {
  font-family: var(--display); font-weight: 500; font-size: 21px;
  margin: 0 0 9px; color: var(--fg); letter-spacing: -.01em;
}
.nosheet__inner p { margin: 0; color: var(--fg-dim); line-height: 1.6; font-size: 13.5px; }
.nosheet__actions { display: flex; gap: 10px; align-items: center; justify-content: center; flex-wrap: wrap; margin-top: 16px; }

/* THE EMPTY STATE IS THE PAGE. It used to be a dashed card stacked ABOVE an
   empty ivory panel, so the first screen showed a blank page under a card.
   It now sits on the page itself, where the score is about to appear, set
   like an edition's title page: ink on ivory. */
.firstrun {
  position: absolute; inset: 0; z-index: 3;
  display: grid; place-items: center; padding: 22px;
}
.firstrun__inner { text-align: center; max-width: 560px; color: var(--page-ink); }
.firstrun__kicker {
  margin: 0 0 8px !important; font: 600 10px var(--ui) !important; letter-spacing: .22em;
  text-transform: uppercase; color: #8d6a2c !important;
}
.firstrun__inner h2 {
  font-family: var(--display); font-weight: 500; font-size: 30px;
  margin: 0 0 10px; color: var(--page-ink); letter-spacing: -.018em;
}
.firstrun__inner p { margin: 0; color: #4a5160; line-height: 1.6; font-size: 14px; }
.firstrun__hint { margin-top: 14px !important; font-family: var(--display); font-style: italic; font-size: 13px !important; color: #6b6557 !important; }
.btn--paper {
  background: transparent; color: var(--page-ink); border-color: #b9ad97;
}
.btn--paper:hover:not(:disabled) { background: rgba(23,29,41,.05); border-color: #8f846f; }
.btn--ink {
  background: linear-gradient(180deg, #1b2436, #111827); color: var(--ivory); border-color: #0b1018;
  font-weight: 600; box-shadow: 0 10px 22px -14px rgba(10,14,22,.9);
}
.btn--ink:hover:not(:disabled) { background: linear-gradient(180deg, #243049, #151e30); }
.btn--paper i, .btn--ink i { font-family: var(--display); }
/* a note for the falling notes while there is nothing to fall */
body.is-empty .rollwrap::after {
  content: "Notes fall here and land on the keys they belong to";
  position: absolute; inset: 0; display: grid; place-items: center; pointer-events: none;
  font-family: var(--display); font-style: italic; font-size: 15px; color: var(--fg-faint);
}

/* ============================================================ keys (SVG) == */
.pk { cursor: pointer; transition: fill .09s linear; }
.pk--white { fill: #efe9dd; stroke: #b3a996; stroke-width: .6; }
.pk--black { fill: #10141d; stroke: #05070b; stroke-width: .5; }
/* the hand test's passage: its keys tinted and named (before every other state: a key played, pressed or asked for shows that instead) */
.pk--white.is-passage { fill: #f1dfb6; }
.pk--black.is-passage { fill: #5b4521; }
.pk-name { font: 600 10px var(--data); fill: #4a3a1c; text-anchor: middle; pointer-events: none; }
.pk-name--black { font-size: 8.5px; fill: #f4e6c4; }
.pk--white.is-playR { fill: var(--gold-hi); }
.pk--black.is-playR { fill: var(--gold); }
.pk--white.is-playL { fill: var(--lapis-hi); }
.pk--black.is-playL { fill: var(--lapis); }
.pk--white.is-user  { fill: var(--jade-hi); }
.pk--black.is-user  { fill: var(--jade); }
.pk.is-expected { stroke: var(--ivory); stroke-width: 2.4; stroke-dasharray: 3 2.5; }
.pk.is-correct { fill: var(--jade) !important; }
.pk.is-wrong   { fill: var(--err) !important; }
.pk-label { font: 500 9.5px var(--data); fill: #7d7566; text-anchor: middle; pointer-events: none; }
.pk-label--c { fill: #5d5340; font-weight: 600; }
.pk-typing { fill: var(--jade); opacity: .8; pointer-events: none; }
.pk-badge { fill: var(--ivory); stroke: #0b1018; stroke-width: 1.2; pointer-events: none; }
.pk-badge__text { font: 600 11px var(--data); fill: #10141d; text-anchor: middle; pointer-events: none; }

/* ============================================================ readout ===== */
/* ONE BAR FOR EVERY MODE. The score used to be a panel of its own over the play bar, shown in Follow and
   Wait, and Wait hid the play bar: each change of mode moved the score and the falling notes by 30-40 px,
   then the score was drawn again at its new size a quarter of a second later. Now the readout is one line
   in the play bar, over the timeline (as Tempo's label sits over its slider), in every mode: the bar you
   are in always, the score in Follow and Wait. The line is as tall in every mode and the bar no taller
   than its tallest control, so a change of mode changes no size anywhere. Where the line is too short for
   all of it, the cells that matter least go first: best, points, streak. */
.readout {
  display: flex; align-items: baseline; gap: 0 15px; height: 14px; line-height: 14px;
  padding-left: 55px;                         /* over the track: past the time (42 px) and its gap */
  overflow: hidden; white-space: nowrap;
}
.readout__score { display: contents; }
.readout.is-listen .readout__score { display: none; }
body.is-wait .readout__cell--timing { display: none; }          /* (Wait has no beat to be on) */
.readout__cell { display: inline-flex; align-items: baseline; gap: 6px; flex: none; }
.readout__cell > span {
  font: 600 9.5px/14px var(--ui); letter-spacing: .14em; text-transform: uppercase; color: var(--fg-faint);
}
.readout__cell > b {
  font: 500 12px/14px var(--data); color: var(--fg-dim); font-variant-numeric: tabular-nums; letter-spacing: -.01em;
}
.readout__cell--bar > b { color: var(--fg); }
.readout__cell--bar + .readout__score > .readout__cell:first-child { margin-left: 3px; padding-left: 15px; border-left: 1px solid var(--rule); }
.readout__cell.is-cut { display: none; }                         /* (no room: see fitReadout in app.js) */

/* ============================================================ transport === */
.transport {
  position: sticky; bottom: 12px; z-index: 30;
  display: grid; grid-template-columns: auto 1fr auto;
  align-items: center; gap: clamp(14px, 2.6vw, 30px);
  padding: 10px clamp(12px, 2vw, 20px);
  background: rgba(17, 24, 36, .82);
  backdrop-filter: blur(16px) saturate(1.3);
  -webkit-backdrop-filter: blur(16px) saturate(1.3);
  border: 1px solid var(--rule);
  border-radius: var(--r-lg);
  box-shadow: var(--lift-2), 0 -10px 34px -20px rgba(0,0,0,.9);
}
.transport__buttons { display: flex; gap: 8px; align-items: center; }
.tbtn {
  width: 38px; height: 38px; display: grid; place-items: center; border-radius: 50%;
  border: 1px solid var(--rule); background: linear-gradient(180deg,#1c2536,#141c29);
  cursor: pointer;
  transition: border-color .16s var(--ease), background .16s var(--ease), transform .06s var(--ease);
}
.tbtn:hover:not(:disabled) { border-color: #36435f; background: linear-gradient(180deg,#222c40,#18202f); }
.tbtn:active:not(:disabled) { transform: translateY(1px); }
.tbtn:disabled { opacity: .35; cursor: default; }
.tbtn--play {
  width: 46px; height: 46px; border-color: transparent;
  background: linear-gradient(180deg, var(--ivory), var(--ivory-warm));
  box-shadow: 0 1px 0 rgba(255,255,255,.6) inset, 0 10px 26px -14px rgba(241,236,226,.85);
}
.tbtn--play:hover:not(:disabled) { background: linear-gradient(180deg,#fff,var(--ivory)); }
.ico { display: block; }
.ico--play { width: 0; height: 0; border-left: 14px solid #121926; border-top: 9px solid transparent; border-bottom: 9px solid transparent; margin-left: 4px; }
.ico--pause { width: 13px; height: 15px; border-left: 4.5px solid var(--fg); border-right: 4.5px solid var(--fg); }
.ico--stop { width: 12px; height: 12px; background: var(--fg); border-radius: 2px; }

.transport__scrub { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.transport__track { display: flex; align-items: center; gap: 13px; }
.time {
  font-family: var(--data); font-variant-numeric: tabular-nums;
  font-size: 12.5px; color: var(--fg-dim); min-width: 42px; letter-spacing: .01em;
}
.time--total { text-align: right; color: var(--fg-faint); }
.transport__knobs { display: flex; gap: 15px; flex-wrap: wrap; align-items: center; }
.chip--net { font-weight: 500; }
/* (label 14 px, 2 px, slider 24 px: the readout's line and the timeline beside it, so the two labels share a line) */
.knob { display: flex; flex-direction: column; gap: 2px; }
.knob__label {
  font-size: 9.5px; font-weight: 600; letter-spacing: .13em; line-height: 14px;
  text-transform: uppercase; color: var(--fg-faint);
}
.knob__label em { font-family: var(--data); color: var(--fg-dim); font-style: normal; letter-spacing: 0; }
/* in the play bar, Tempo's label is set as a cell of the readout beside it: the same line, the same type */
.transport__knobs .knob__label { display: flex; align-items: baseline; gap: 6px; height: 14px; }
.transport__knobs .knob__label em { font: 500 12px/14px var(--data); letter-spacing: -.01em; }
.knob input[type="range"] { width: 92px; }

/* SLIDERS. The browser hit-tests a range input by its BOX, not by the thumb
   you see — and the box used to be the 4-8px track, so a finger had to land
   within a few pixels to catch it (below WCAG's 24px floor even with a mouse).
   The box is now a tall, transparent hit area; the thin track and the thumb
   are drawn inside it and look exactly as before. */
input[type="range"] {
  -webkit-appearance: none; appearance: none; background: transparent;
  height: 24px; margin: 0; padding: 0; outline: none; cursor: pointer;
}
input[type="range"]::-webkit-slider-runnable-track { height: 4px; border-radius: var(--r-pill); background: var(--rule); }
input[type="range"]::-moz-range-track { height: 4px; border-radius: var(--r-pill); background: var(--rule); }
/* input[type=range].scrubber, not just .scrubber: a bare class loses to
   input[type="range"] on specificity (0,1,0 < 0,1,1), which is why the played
   portion of the position bar never actually filled in. */
input[type="range"].scrubber { flex: 1; }
input[type="range"].scrubber::-webkit-slider-runnable-track {
  height: 5px;
  background: linear-gradient(90deg, var(--ivory) calc(var(--p,0)*1%), var(--rule) calc(var(--p,0)*1%));
}
input[type="range"].scrubber::-moz-range-track { height: 5px; }
input[type="range"].scrubber::-moz-range-progress { height: 5px; border-radius: var(--r-pill); background: var(--ivory); }
input[type="range"]:disabled { opacity: .4; cursor: default; }
input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none; width: 14px; height: 14px; margin-top: -5px;
  border-radius: 50%; background: var(--ivory); border: none;
  box-shadow: 0 2px 6px rgba(0,0,0,.6);
  transition: transform .14s var(--ease);
}
input[type="range"].scrubber::-webkit-slider-thumb { margin-top: -4.5px; }
input[type="range"]:hover::-webkit-slider-thumb { transform: scale(1.18); }
input[type="range"]::-moz-range-thumb { width: 14px; height: 14px; border-radius: 50%; background: var(--ivory); border: none; }

/* ============================================================ panels ====== */
.settings {
  border: 1px solid var(--rule-soft); background: var(--ink-raise);
  border-radius: var(--r-lg); padding: 13px 18px;
}
.settings__summary {
  font-size: 9.5px; font-weight: 600; letter-spacing: .14em; text-transform: uppercase;
  color: var(--fg-faint); cursor: pointer; user-select: none;
  transition: color .16s var(--ease);
}
.settings__summary::marker { color: var(--lapis-deep); }
.settings__summary:hover { color: var(--fg-dim); }
.settings__body { display: flex; flex-wrap: wrap; gap: 12px 26px; align-items: center; margin-top: 15px; }
.settings__body--stack { flex-direction: column; align-items: stretch; gap: 16px; }
.settings__group { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; }
.settings__group + .settings__group { border-left: 1px solid var(--rule-soft); padding-left: 22px; }
.settings .btn.is-on { background: linear-gradient(180deg,#1e3b34,#16302a); border-color: #2c6455; color: var(--jade-hi); }

/* ============================================================ coach ======= */
/* where each section stands: the state colours (err -> warn -> ok), and a cool
   blue for "come back to it" */
.is-new      { --st: #2c3852; }
.is-learning { --st: var(--err); }
.is-notes    { --st: var(--warn); }
.is-net      { --st: #c9a94f; }
.is-slow     { --st: #b9b75c; }
.is-tempo    { --st: var(--jade-hi); }
.is-mastered { --st: var(--jade); }
.is-review   { --st: var(--lapis-hi); }
.coach__grid { display: flex; flex-wrap: wrap; gap: 6px; }
.csec {
  display: inline-flex; flex-direction: column; align-items: flex-start; justify-content: center; gap: 1px;
  min-width: 58px; min-height: 40px; padding: 4px 9px 5px; cursor: pointer;
  border: 1px solid var(--rule-soft); border-top: 3px solid var(--st); border-radius: var(--r-sm);
  background: var(--ink-sunk);
  background: linear-gradient(180deg, color-mix(in srgb, var(--st) 14%, var(--ink-sunk)), var(--ink-sunk));
  color: var(--fg); font: inherit; text-align: left;
  transition: border-color .15s var(--ease), transform .15s var(--ease);
}
.csec:hover { border-color: #33405c; border-top-color: var(--st); }
.csec:focus-visible { outline: 2px solid var(--jade); outline-offset: 2px; }
.csec__bars { font: 500 12px var(--data); font-variant-numeric: tabular-nums; }
.csec__state { font: 10.5px var(--data); color: var(--fg-dim); }
.csec.is-mastered .csec__state { color: var(--jade-hi); }
.csec.is-review .csec__state { color: var(--lapis-hi); }
.coach__legend { display: flex; flex-wrap: wrap; gap: 4px 14px; font-size: 11px; color: var(--fg-faint); }
.coach__key::before { content: ""; display: inline-block; width: 9px; height: 9px; border-radius: 2px; margin-right: 5px; vertical-align: -1px; background: var(--st); }
.coach__note { margin: 0; font-size: 13px; line-height: 1.5; color: var(--fg-dim); }
.coach__due { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.coach__due.is-hidden, .firstrun__due.is-hidden { display: none; }
.coach__duehead { margin: 0; font-size: 12px; color: var(--fg-faint); }
.firstrun__due { display: flex; flex-wrap: wrap; gap: 6px; justify-content: center; align-items: center; margin-top: 16px; }
.firstrun__due .coach__duehead { color: #6b6557; width: 100%; }
.firstrun__due .btn { color: var(--page-ink); border-color: var(--page-rule); }
.btn--next { border-color: rgba(89,194,160,.55); color: var(--jade-hi); }
.btn--next:hover:not(:disabled) { background: rgba(89,194,160,.08); }
.coachset .num { width: 52px; }

/* ============================================================ log ========= */
.summary__hint {
  font-family: var(--data); color: var(--jade); font-size: 10px;
  letter-spacing: .06em; margin-left: 10px; text-transform: none;
}
.log__stats { display: flex; flex-wrap: wrap; gap: 12px 26px; align-items: flex-start; }
.log__stat { display: flex; flex-direction: column; gap: 3px; min-width: 96px; }
.log__stat span { font-size: 9.5px; font-weight: 600; letter-spacing: .14em; text-transform: uppercase; color: var(--fg-faint); }
.log__stat b { font-family: var(--data); font-size: 17px; font-weight: 500; color: var(--fg); font-variant-numeric: tabular-nums; }
.log__stat--note {
  flex: 1 1 260px; min-width: 210px; color: var(--fg-dim);
  font-family: var(--display); font-style: italic; font-size: 13px; line-height: 1.55; align-self: center;
}
.log__chart {
  display: flex; align-items: flex-end; gap: 5px; height: 76px;
  padding: 9px 11px; border: 1px solid var(--rule-soft); border-radius: var(--r-md);
  background: var(--ink-sunk);
}
.log__bar {
  flex: 1 1 0; min-width: 6px; height: 100%; border-radius: 3px 3px 1px 1px;
  background: linear-gradient(180deg, var(--jade-hi), #2c7a63);
  transform-origin: bottom; transform: scaleY(var(--h,0));
  transition: transform .45s var(--ease);
}
.log__bar--empty { background: var(--rule-soft); transform: scaleY(.04); }
.log__trend { display: flex; align-items: flex-end; gap: 4px; min-height: 26px; flex-wrap: wrap; }
.log__trend i { display: block; width: 12px; height: 24px; border-radius: 2px; background: var(--rule-soft); position: relative; }
.log__trend i::after { content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: var(--v,0%); border-radius: 2px; background: var(--lapis); }
.log__trendlabel {
  font-size: 9.5px; font-weight: 600; letter-spacing: .14em; text-transform: uppercase;
  color: var(--fg-faint); margin: 0 8px; align-self: center;
}

/* ============================================================ dialog ====== */
.sheetdlg {
  border: 1px solid var(--rule); border-radius: var(--r-lg);
  background: linear-gradient(180deg, var(--ink-raise), #111825);
  color: var(--fg); padding: 24px 26px; max-width: 480px; width: calc(100vw - 32px);
  box-shadow: var(--lift-3); font-family: var(--ui);
}
.sheetdlg::backdrop { background: rgba(5,8,14,.7); backdrop-filter: blur(4px); }
.sheetdlg__head { display: flex; align-items: baseline; justify-content: space-between; gap: 18px; margin-bottom: 18px; }
.sheetdlg__head h2 { font-family: var(--display); font-weight: 500; font-size: 21px; margin: 0; letter-spacing: -.015em; }
.keys { display: grid; grid-template-columns: auto 1fr; gap: 10px 18px; margin: 0; align-items: baseline; }
.keys dt { text-align: right; white-space: nowrap; }
.keys dd { margin: 0; color: var(--fg-dim); font-size: 13px; }
.keys__note {
  margin: 18px 0 0; color: var(--fg-faint); font-size: 12.5px; line-height: 1.9;
  padding-top: 16px; border-top: 1px solid var(--rule-soft);
}
/* the version, at the foot of the ? window */
.about { margin: 18px 0 0; padding-top: 12px; border-top: 1px solid var(--rule-soft); font: 11.5px var(--data); color: var(--fg-faint); text-align: right; }
/* the language, at the head of the ? window: each one named in itself, so it
   can be found whatever language the page is in */
.langpick {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px;
  margin: -4px 0 18px; padding: 10px 12px;
  border: 1px solid var(--rule-soft); border-radius: var(--r-md); background: rgba(255,255,255,.02);
}
.langpick__label { display: inline-flex; align-items: center; gap: 7px; font-size: 13px; font-weight: 500; color: var(--fg); }
.langpick__globe { flex: none; fill: none; stroke: var(--fg-dim); stroke-width: 1.5; }
.langpick .select { max-width: none; min-width: 128px; font-size: 13px; }
.langpick__note { flex-basis: 100%; color: var(--fg-faint); font-size: 12px; line-height: 1.5; }

/* JAPANESE. Newsreader and Plex have no kana or kanji: the characters they lack
   are drawn by the system's Japanese faces, named here so the choice is a good
   one on each system (Mincho beside the serif, Gothic beside the sans), with
   the Japanese forms of the shared Han characters (lang="ja" on the page).
   Japanese is not set in italics: a slanted kanji is the browser's fake, so the
   slant is kept for the Latin letters only (font-synthesis: weight). */
html:lang(ja) {
  --display: "Newsreader", "Hiragino Mincho ProN", "Yu Mincho", YuMincho, "Noto Serif JP", "Noto Serif CJK JP", ui-serif, Georgia, serif;
  --ui: "IBM Plex Sans", "Hiragino Sans", "Hiragino Kaku Gothic ProN", "Yu Gothic UI", "Yu Gothic", YuGothic, Meiryo, "Noto Sans JP", "Noto Sans CJK JP", system-ui, sans-serif;
  --data: "IBM Plex Mono", ui-monospace, "SF Mono", Menlo, "Hiragino Sans", "Yu Gothic UI", Meiryo, "Noto Sans CJK JP", monospace;
  font-synthesis: weight;
}
html:lang(ja) body { line-break: strict; }
/* a title breaks between its words (at the spaces around "→"), not inside one:
   "コード記号 →" over "和音", never "和" over "音" */
html:lang(ja) .dcard__title, html:lang(ja) .drill__title { word-break: keep-all; overflow-wrap: anywhere; }

/* ============================================================ responsive == */
/* SHORT WINDOWS (most laptops at 768-900px tall). The score is the panel that
   stops being usable first — half a grand staff is worth nothing — so the
   instrument gives up height instead, and the chrome tightens. */
@media (max-height: 900px) {
  :root { --kb-h: 118px; --roll-min: 124px; }
  .app { padding-top: 14px; padding-bottom: 18px; gap: 11px; }
  .workspace { min-height: calc(100vh - 32px); min-height: calc(100svh - 32px); gap: 11px; }
  .stage { gap: 11px; }
  .masthead { padding-bottom: 8px; }
  :root { --mark: 30px; }
  .nowplaying__name { font-size: 20px; }
  .paper { min-height: 226px; }
  .transport { padding: 8px clamp(10px, 2vw, 18px); }
  .tbtn { width: 34px; height: 34px; }
  .tbtn--play { width: 40px; height: 40px; }
  .knob input[type="range"] { width: 88px; }
}
@media (max-height: 780px) {
  :root { --kb-h: 100px; --roll-min: 100px; }
  .paper { min-height: 200px; }
}

/* Below this width the six control groups wrap to three rows and cost the
   music ~120px of height. One swipeable row instead — everything stays
   reachable, nothing is hidden, and the fade on the right edge says so. */
@media (max-width: 1320px) {
  .rail {
    flex-wrap: nowrap; overflow-x: auto; overflow-y: hidden;
    gap: 0; padding: 12px 0;
    scroll-snap-type: x proximity;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    -webkit-mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 30px), transparent 100%);
            mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 30px), transparent 100%);
  }
  .rail::-webkit-scrollbar { display: none; }
  .rail__group { flex: none; padding: 0 16px; scroll-snap-align: start; }
  .rail__group + .rail__group { border-left: 1px solid var(--rule-soft); padding-left: 16px; }
  .rail__controls { flex-wrap: nowrap; }
}

/* with the app's name at the mark's size, the piece's name and its meter/tempo pill don't fit side by
   side. Where the row is 40 px tall (a window over 900 px high) the two stack, the meter under the name,
   and the name has the whole width; in a shorter window, narrower than 1321 px, the pill goes (its text
   is the piece name's tooltip) */
@media (min-height: 901px) and (min-width: 901px) {
  .nowplaying__title { flex-direction: column; align-items: flex-start; gap: 1px; }
  .nowplaying__name { max-width: 100%; }
  .nowplaying__tempo { padding: 0; border: none; border-radius: 0; font-size: 11.5px; line-height: 14px; }
}
@media (max-height: 900px) and (max-width: 1320px) and (min-width: 901px) {
  .nowplaying__tempo { display: none; }
}
@media (max-width: 900px) {
  .nowplaying { flex-basis: 100%; padding-left: 0; border-left: none; }
  .status { white-space: normal; }
}
@media (max-width: 760px) {
  :root { --kb-h: 96px; --roll-min: 104px; }
  .app { padding: 14px 12px 40px; gap: 11px; }

  /* the transport's knobs get the same swipe treatment */
  .transport__knobs {
    flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none;
    padding-bottom: 2px; justify-content: flex-start;
  }
  .transport__knobs::-webkit-scrollbar { display: none; }
  .knob { flex: none; }
  .nowplaying__name { font-size: 19px; }
  .transport { grid-template-columns: 1fr; gap: 12px; }
  .transport__buttons { justify-content: center; }
  .knob input[type="range"] { width: 86px; }
  .paper { min-height: 186px; }
  .log__stat--note { flex-basis: 100%; }
  .settings__group + .settings__group { border-left: none; padding-left: 0; }
}

/* ---- score overlay: noteheads coloured by how you played them ---- */
.paper__scroll svg g.vf-stavenote.is-correct .vf-notehead path { filter: drop-shadow(0 0 1.5px rgba(89,194,160,.55)); }
.paper__scroll svg g.vf-stavenote.is-wrong .vf-notehead path { filter: drop-shadow(0 0 1.5px rgba(210,96,79,.55)); }
.paper__scroll svg g.vf-stavenote.is-held .vf-notehead path { filter: drop-shadow(0 0 1.5px rgba(217,164,65,.6)); }

/* ---- typing-keyboard map in the shortcuts dialog ---- */
.typemap { margin-top: 20px; padding-top: 18px; border-top: 1px solid var(--rule-soft); }
.typemap__title {
  margin: 0 0 12px; font-size: 9.5px; font-weight: 600; letter-spacing: .14em;
  text-transform: uppercase; color: var(--fg-faint);
}
.typemap__rows { display: flex; flex-direction: column; gap: 4px; }
.typemap__row { display: grid; grid-template-columns: repeat(11, 1fr); gap: 4px; }
.typemap__row b, .typemap__row i, .typemap__row span {
  display: grid; place-items: center; height: 26px; border-radius: 5px;
  font: 500 11px var(--data); font-style: normal;
}
.typemap__row--black b { background: #10141d; color: var(--ivory); border: 1px solid var(--rule); }
.typemap__row--black i { background: transparent; color: var(--fg-faint); border: 1px dashed var(--rule-soft); opacity: .55; }
.typemap__row--black { transform: translateX(4.5%); }
.typemap__row--white b { background: var(--ivory); color: #171d29; font-weight: 600; }
.typemap__row--note span { height: 16px; color: var(--fg-faint); font-size: 10px; }
.typemap__note { margin-top: 14px; padding-top: 0; border-top: none; }

/* ============================================================ settings ==== */
/* SETTINGS IN SECTIONS. Eight groups in one 640px column made a dialog you
   scrolled through to find anything (the device settings were three screens
   down). Now a list of sections on the left and one section at a time on
   the right; the dialog keeps a fixed height, so it doesn't jump. */
.sheetdlg--wide {
  max-width: 880px; height: min(700px, calc(100vh - 40px)); max-height: calc(100vh - 40px);
  display: flex; flex-direction: column; padding: 20px 22px 18px; overflow: hidden;
}
.sheetdlg--wide:not([open]) { display: none; }
.sheetdlg--wide .sheetdlg__head { margin-bottom: 14px; }
.setlayout { flex: 1 1 auto; min-height: 0; display: grid; grid-template-columns: 188px 1fr; gap: 20px; }
.setnav { display: flex; flex-direction: column; gap: 2px; border-right: 1px solid var(--rule-soft); padding-right: 14px; }
.setnav__tab {
  text-align: left; font: 500 13px var(--ui); color: var(--fg-dim);
  background: transparent; border: 1px solid transparent; border-radius: var(--r-sm);
  padding: 8px 11px; cursor: pointer; transition: background .15s var(--ease), color .15s var(--ease);
}
.setnav__tab:hover { color: var(--fg); background: rgba(255,255,255,.03); }
.setnav__tab[aria-selected="true"] {
  color: var(--fg); background: linear-gradient(180deg, #223049, #1a253a);
  border-color: var(--rule); box-shadow: inset 2px 0 0 var(--gold);
}
.setpanes { min-height: 0; overflow-y: auto; padding-right: 6px; scrollbar-width: thin; scrollbar-color: var(--rule) transparent; }
.setpanes .setgroup[hidden] { display: none; }
.setgroup { padding: 16px 0 4px; border-top: 1px solid var(--rule-soft); }
.setgroup:first-of-type, .setgroup.is-first { border-top: none; padding-top: 2px; }
@media (max-width: 760px) {
  .sheetdlg--wide { padding: 16px 14px; height: calc(100vh - 24px); }
  .setlayout { grid-template-columns: 1fr; grid-template-rows: auto 1fr; gap: 12px; }
  .setnav {
    flex-direction: row; overflow-x: auto; border-right: none; padding: 0 0 8px;
    border-bottom: 1px solid var(--rule-soft); scrollbar-width: none;
  }
  .setnav::-webkit-scrollbar { display: none; }
  .setnav__tab { flex: none; white-space: nowrap; }
  .setnav__tab[aria-selected="true"] { box-shadow: inset 0 -2px 0 var(--gold); }
}

.pieceinfo { margin: 2px 0 12px; font-size: 12.5px; line-height: 1.55; color: var(--fg-dim); }
.pieceinfo b { color: var(--fg); font-weight: 600; }
/* fingering styles: one card per variant, with what it costs on this piece */
.unit { display: inline-flex; align-items: center; gap: 6px; }
.unit__label { font: 500 11px var(--data); color: var(--fg-faint); }
.num--wide { width: 64px; }
#handReach { max-width: 290px; }
/* what the last change of hand did to the fingering */
.handdiff {
  margin: 0 0 10px; padding: 8px 11px; border-left: 2px solid var(--jade-hi); border-radius: 0 var(--r-md) var(--r-md) 0;
  background: rgba(89,194,160,.07); font-size: 12.5px; line-height: 1.5; color: var(--fg);
}
.handdiff[hidden] { display: none; }
.styles { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 8px; margin: 6px 0 10px; }
.styles__empty { margin: 4px 0; color: var(--fg-faint); font-family: var(--display); font-style: italic; font-size: 13px; }
.style {
  position: relative; display: flex; gap: 10px; align-items: flex-start; cursor: pointer;
  padding: 11px 12px; border: 1px solid var(--rule-soft); border-radius: var(--r-md);
  background: var(--ink-sunk); transition: border-color .15s var(--ease), background .15s var(--ease);
}
.style:hover { border-color: #33405c; }
.style input { margin: 3px 0 0; accent-color: var(--gold); }
.style.is-on { border-color: rgba(226,177,92,.55); background: linear-gradient(180deg, rgba(226,177,92,.08), rgba(226,177,92,.02)); }
.style__body { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.style__name { font-weight: 600; font-size: 13.5px; color: var(--fg); display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px; }
.style__badge {
  font: 600 9.5px var(--ui); letter-spacing: .08em; text-transform: uppercase; font-style: normal;
  color: var(--jade-hi); border: 1px solid rgba(89,194,160,.45); border-radius: var(--r-pill); padding: 1px 7px;
}
.style__desc { font-size: 12px; line-height: 1.45; color: var(--fg-dim); }
.style__stats { font: 11.5px var(--data); color: var(--fg-faint); line-height: 1.5; }
/* whether this piece follows your style or keeps one of its own (and the way back) */
.stylefollow { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; margin: -2px 0 10px; font-size: 12px; line-height: 1.5; color: var(--fg-dim); }
.stylefollow[hidden] { display: none; }
.stylefollow b { color: var(--fg); font-weight: 600; }
.setgroup__title {
  margin: 0 0 3px; font-family: var(--ui); font-size: 9.5px; font-weight: 600;
  letter-spacing: .14em; text-transform: uppercase; color: var(--fg-faint);
}
.setgroup__hint {
  margin: 0 0 10px; font-family: var(--display); font-style: italic;
  font-size: 12.5px; color: var(--fg-dim);
}
.setgroup__row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; margin-bottom: 10px; }
.setgroup__note {
  margin: 4px 0 6px; font-size: 12px; line-height: 1.55; color: var(--fg-dim);
}
.setgroup__note b { color: var(--fg); font-weight: 600; }
.field { display: inline-flex; align-items: center; gap: 8px; }
.field__label {
  font-size: 9.5px; font-weight: 600; letter-spacing: .14em; text-transform: uppercase;
  color: var(--fg-faint); white-space: nowrap;
}
.knob--inline { flex-direction: row; align-items: center; gap: 10px; }
.knob--inline input[type="range"] { width: 150px; }

/* Settings' gear: the first icon drawn for the new name, in white — a gear with a keyboard at its hub */
.gear {
  display: inline-block; width: 15px; height: 15px; vertical-align: -3px; margin: -2px 3px -2px 0;
  color: var(--fg);   /* (the negative margins: a 15 px gear must not make Settings 1 px taller than its row) */
}
.btn:hover .gear { color: #fff; }

/* ============================================================ repeat ====== */
.rail__group--repeat .rail__labeltext { cursor: help; text-decoration: underline dotted var(--rule); text-underline-offset: 3px; }
/* the pass count, hung under "Repeat" (it used to follow Off, and grew the row by up to 56 px as passes were
   counted); out of the flow, so neither the name nor anything else in the row moves when it appears */
.rail__label--repeat { position: relative; }
.rail__pass {
  position: absolute; left: 0; top: calc(100% + 2px);
  font: 500 10px/12px var(--data); letter-spacing: 0; text-transform: none; color: var(--jade);
  white-space: nowrap; pointer-events: none;
}
.range { display: inline-flex; align-items: center; gap: 5px; }
.range__word, .range__dash { font-size: 12px; color: var(--fg-dim); }
.num {
  width: 46px; font-family: var(--data); font-size: 12.5px; color: var(--fg);
  background: var(--ink-sunk); border: 1px solid var(--rule); border-radius: var(--r-sm);
  padding: 5px 6px; text-align: center; -moz-appearance: textfield; appearance: textfield;
  color-scheme: dark;
}
.num::-webkit-outer-spin-button, .num::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.num:disabled { opacity: .4; }
.num:focus-visible { outline: 2px solid var(--lapis-hi); outline-offset: 1px; }
.rail__group--tools { margin-left: auto; }
/* SLOTS: controls that take turns in one place — the Metronome switch and Skip (while a count-in clicks), Off
   and Drill (offered after a run). The slot is as wide as the widest of them whichever is showing, so nothing in
   the row moves when they swap. On a touch screen every control is 44 px and the row has ~30 px to spare: a
   control that appeared beside the others pushed the last group onto a second row, and the score, the falling
   notes and the keys dropped 52 px — at every Play with a count-in, and after a run with mistakes. */
.slot { display: inline-grid; align-items: center; }
.rail__controls.slot { display: inline-grid; }
.slot > * { grid-area: 1 / 1; }
.slot > .btn { justify-self: stretch; }                       /* the narrower one fills the slot: a bigger target */
.rail__group--metro .slot > .btn { align-self: stretch; }     /* Skip: the switch's whole box */
/* Drill: the passage it offers, written faintly in the bar boxes beside it */
.btn--drill { color: var(--jade-hi); border-color: #2c6455; background: linear-gradient(180deg, #1e3b34, #16302a); }
.btn--drill:hover { border-color: var(--jade); color: #c4f0e1; }
.rail__group--repeat.is-drill .num::placeholder { color: var(--jade); opacity: .85; }
/* EDITING FINGERS: the toolbar becomes the editing tools, in one row. They used
   to be added beside the practice groups and wrapped to a second row (638px of
   tools in the ~150px left at the app's full width, 1292px). The practice
   groups wait while fingers are edited — Mode, Hand, Repeat and the coach's
   Drill are not used to edit — and come back with Done (or Esc). Done sits
   right after the row's name, in the order it is reached with Tab, so a narrow
   window, where the toolbar scrolls sideways, shows it from the start. */
.rail__label--edit { display: none; }
.rail.is-fingering > .rail__group:not(.rail__group--tools) { display: none; }
.rail.is-fingering .rail__group--tools { flex: 1 1 auto; margin-left: 0; border-left: none; padding-left: 0; }
.rail.is-fingering .rail__group--tools .rail__controls { flex: 1 1 auto; flex-wrap: nowrap; }
.rail.is-fingering .rail__label--edit { display: inline; color: var(--lapis-hi); }
.rail.is-fingering #btnFingering { margin-right: 6px; }
/* Exit: the same as Done, at the end of the row beside Settings */
#btnFingerExit { display: none; }
.rail.is-fingering #btnFingerExit { display: inline-block; margin-left: auto; }
/* the metronome is for Listen and Follow: in Wait the music waits for you, so there is no beat to keep */
body.mode-wait .rail__group--metro { display: none; }

/* SIMPLIFY SCORE: the button (lit while a simplified version is open) and its menu of the four versions.
   The menu is moved onto the page when it opens and placed under the button (fixed): inside the toolbar,
   which scrolls sideways in a narrow window, it would be clipped. Editing fingers hides the button. */
.simplify { display: inline-flex; }
.rail.is-fingering .simplify { display: none; }
/* The button's faces — "Simplify", and "Simplified" over a level's name — stacked in one place, only the open
   version's visible: the button is always as wide as its widest face, and the two-line face is no wider than
   "Simplify" (it was "Simplified · Medium" on one line, 65-70 px wider, which pushed the tools onto a second row
   on a touch screen). Its height stays the row's: 29 px with a mouse, 44 px with a finger. */
#btnSimplify { padding-top: 2px; padding-bottom: 2px; }
.simplify__faces { display: inline-grid; vertical-align: middle; }
.simplify__face {
  grid-area: 1 / 1; display: flex; flex-direction: column; align-items: center; justify-content: center;
  line-height: 15px;
}
.simplify__k { font-size: 10px; line-height: 11px; font-weight: 500; opacity: .78; }
.simplify__v { font-size: 12px; line-height: 12px; }
.menu {
  position: fixed; z-index: 58; width: 304px; max-width: calc(100vw - 16px);
  display: flex; flex-direction: column; gap: 2px; padding: 6px;
  background: linear-gradient(180deg, #172032, #10161f);
  border: 1px solid var(--rule); border-radius: var(--r-md);
  box-shadow: 0 1px 0 rgba(255,255,255,.04) inset, 0 20px 44px -16px rgba(0,0,0,.95);
}
.menu__item {
  position: relative; display: grid; grid-template-columns: 1fr auto; gap: 2px 12px; align-items: baseline;
  width: 100%; text-align: left; padding: 8px 10px 9px 27px; cursor: pointer;
  background: transparent; border: 1px solid transparent; border-radius: var(--r-sm);
  color: var(--fg); font: 500 13px var(--ui);
}
.menu__item:hover:not(:disabled), .menu__item:focus-visible { background: rgba(255,255,255,.045); border-color: var(--rule); outline: none; }
.menu__head {
  padding: 5px 10px 4px; font: 600 9.5px var(--ui); letter-spacing: .14em; text-transform: uppercase; color: var(--fg-faint);
}
.menu__item:disabled { cursor: default; color: var(--fg-faint); }
.menu__item:disabled .menu__what { color: var(--fg-faint); }
.menu__item b { font-weight: 600; }
.menu__share { font: 11.5px var(--data); color: var(--fg-dim); white-space: nowrap; }
.menu__what { grid-column: 1 / -1; font-size: 12px; line-height: 1.4; color: var(--fg-dim); }
.menu__item[aria-checked="true"] b { color: var(--jade-hi); }
.menu__item[aria-checked="true"]::before {
  content: ""; position: absolute; left: 11px; top: 14px; width: 7px; height: 7px; border-radius: 50%;
  background: var(--jade); box-shadow: 0 0 8px rgba(89,194,160,.7);
}

/* the piece's sections under the scrubber track, coloured by where each stands */
.scrub__sections { position: absolute; left: 0; right: 0; top: 50%; height: 3px; transform: translateY(8px); pointer-events: none; z-index: 0; }
.scrub__sections i { position: absolute; top: 0; bottom: 0; border-radius: 2px; background: var(--st, var(--rule)); box-shadow: inset -1px 0 0 var(--ink-raise); }

/* the repeated stretch, drawn on the scrubber track */
.scrub { position: relative; flex: 1; display: flex; align-items: center; }
.scrub .scrubber { position: relative; z-index: 1; width: 100%; }
.scrub__loop {
  position: absolute; top: 50%; height: 11px; transform: translateY(-50%);
  left: calc(var(--a, 0) * 1%); width: calc((var(--b, 0) - var(--a, 0)) * 1%);
  background: rgba(89,194,160,.22); border: 1px solid rgba(89,194,160,.7);
  border-radius: 4px; pointer-events: none; z-index: 0;
}

@media (max-width: 1320px) {
  .rail__group--tools { margin-left: 0; }
  .rail.is-fingering .rail__group--tools { padding-left: 16px; }
  /* scrolled to its end, the last control clears the fade that marks the edge */
  .rail > .rail__group:last-child { padding-right: 34px; }
}

/* ============================================================ touch ======= */
/* Without these the browser keeps a finger for itself: sliding across the
   on-screen keys scrolled the page instead of playing, and a held key could be
   cancelled when the browser decided the touch was a pan. */
.keyboard { touch-action: none; -webkit-user-select: none; user-select: none; }
.roll { touch-action: none; }
button, .chip, .seg__opt, select, .tbtn, input[type="range"] { touch-action: manipulation; }

/* Coarse pointers (a finger): every control reaches 44px. That clears
   Microsoft's own touch guidance for Windows (40x40 effective px, about 7.5 mm)
   and WCAG 2.5.5's AAA level (44x44); the AA floor, 2.5.8, is 24x24. Apple asks
   for 44pt and Material for 48dp. Mouse users keep the compact sizes. */
@media (pointer: coarse) {
  .btn, .btn--sm, .select, .num { min-height: 44px; }
  .btn--sm { padding: 8px 14px; font-size: 13px; }
  .chip { min-height: 44px; padding: 8px 14px; }
  .seg__opt span { min-height: 44px; display: flex; align-items: center; padding: 6px 15px; }
  /* in the toolbar, 12 px a side: every control there is still at least 44 x 44, and its one row keeps ~80 px
     to spare at the app's full width instead of ~20 — a little more text (another font, a longer word) no
     longer drops its last group onto a second row */
  .rail .btn--sm, .rail .chip { padding-left: 12px; padding-right: 12px; min-width: 44px; }
  .rail .seg__opt span { padding-left: 12px; padding-right: 12px; min-width: 44px; justify-content: center; }
  .seg { padding: 0; }
  .tbtn { width: 46px; height: 46px; }
  .tbtn--play { width: 54px; height: 54px; }
  input[type="range"] { height: 44px; }
  /* One vendor per rule: a selector list containing a pseudo-element the
     browser doesn't know (Chrome and ::-moz-*, Firefox and ::-webkit-*) makes
     it drop the WHOLE rule — which is what silently undid these on touch. */
  input[type="range"]::-webkit-slider-runnable-track { height: 8px; }
  input[type="range"]::-moz-range-track { height: 8px; }
  input[type="range"].scrubber::-webkit-slider-runnable-track { height: 8px; }
  input[type="range"].scrubber::-moz-range-track { height: 8px; }
  input[type="range"].scrubber::-moz-range-progress { height: 8px; }
  input[type="range"]::-webkit-slider-thumb { width: 26px; height: 26px; margin-top: -9px; }
  input[type="range"].scrubber::-webkit-slider-thumb { width: 26px; height: 26px; margin-top: -9px; }
  input[type="range"]::-moz-range-thumb { width: 26px; height: 26px; }
  .btn--round { min-width: 44px; }
  .knob input[type="range"] { width: 110px; }
  /* the Simplify button's two-line face at a finger's size (still no wider than "Simplify" at 13 px) */
  .simplify__k { font-size: 11px; line-height: 12px; }
  .simplify__v { font-size: 13px; line-height: 14px; }
}

/* LONGER WORDS. Romanian and Spanish say the toolbar's words in more letters
   than English ("Urmărește", "Izquierda", "Este compás"): the same row, drawn a
   little tighter — less space inside each control and between the groups —
   stays one line at the app's full width, with a mouse and with a finger
   (where every control is still at least 44 x 44). English and Japanese keep
   the spacing above: their words fit it. */
html:lang(ro) .rail .seg__opt span, html:lang(es) .rail .seg__opt span,
html:lang(ro) .rail .btn--sm, html:lang(es) .rail .btn--sm,
html:lang(ro) .rail .chip, html:lang(es) .rail .chip { padding-left: 8px; padding-right: 8px; }
html:lang(ro) .rail__label, html:lang(es) .rail__label { letter-spacing: .1em; }
@media (pointer: coarse) {
  html:lang(ro) .rail .seg__opt span, html:lang(es) .rail .seg__opt span,
  html:lang(ro) .rail .btn--sm, html:lang(es) .rail .btn--sm,
  html:lang(ro) .rail .chip, html:lang(es) .rail .chip { padding-left: 8px; padding-right: 8px; }
}
@media (min-width: 1321px) {
  html:lang(ro) .rail, html:lang(es) .rail { column-gap: 8px; }
  html:lang(ro) .rail__group, html:lang(es) .rail__group { gap: 7px; }
  html:lang(ro) .rail__group + .rail__group, html:lang(es) .rail__group + .rail__group { padding-left: 9px; }
}

/* ============================================================ heads-up ==== */
/* Practice feedback sits over the falling notes, where the eyes are while
   playing — the status line at the top of the page is a glance away from the
   keys, which during practice means it is effectively invisible. */
.hud {
  position: absolute; top: 10px; left: 50%; transform: translateX(-50%);
  z-index: 5; pointer-events: none; max-width: calc(100% - 24px);
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  padding: 8px 16px 9px; border-radius: 12px;
  background: rgba(10, 14, 22, .82); border: 1px solid var(--rule);
  box-shadow: 0 10px 28px -12px rgba(0,0,0,.9);
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  transition: opacity .18s var(--ease);
}
.hud__text {
  font-family: var(--display); font-style: italic; font-size: 17px; color: var(--fg);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%;
}
.hud--ok   { border-color: rgba(89,194,160,.55); }
.hud--ok .hud__text { color: var(--jade-hi); }
.hud--warn { border-color: rgba(217,164,65,.55); }
.hud--warn .hud__text { color: var(--gold-hi); }
.hud--err  { border-color: rgba(210,96,79,.6); }
.hud--err .hud__text { color: #f0a497; }
.hud__bar { width: 180px; height: 4px; border-radius: 2px; background: var(--rule); overflow: hidden; }
.hud__bar i { display: block; height: 100%; width: 0; background: var(--jade); border-radius: 2px; }
.hud__bar i.is-running { animation: hudfill var(--hold-ms, 700ms) linear forwards; }
@keyframes hudfill { from { width: 0; } to { width: 100%; } }

/* the held chord fills with green on the keys themselves */
.pk.is-holding { animation: keyhold var(--hold-ms, 700ms) linear forwards; }
@keyframes keyhold { from { fill: #efe9dd; } to { fill: var(--jade); } }
.pk--black.is-holding { animation-name: keyholdb; }
@keyframes keyholdb { from { fill: #10141d; } to { fill: var(--jade); } }

/* ============================================================ finger pad == */
.fingerpad { display: inline-flex; gap: 3px; }
.fingerpad .btn { min-width: 30px; padding-left: 0; padding-right: 0; font-family: var(--data); }
/* the words get their room back (the squares are for the digits only) */
.fingerpad .btn[data-f="0"], .fingerpad #btnStyles { padding-left: 10px; padding-right: 10px; }
.fingerpad #btnStyles { font-family: var(--ui); margin-left: 6px; }
/* a finger: the digits are 44px squares like every other control (the 30px above, for a mouse,
   came after the touch sizes and undid them) */
@media (pointer: coarse) {
  .fingerpad .btn { min-width: 44px; }
}

/* ============================================================ first run === */
.firstrun__actions { display: flex; gap: 10px; flex-wrap: wrap; justify-content: center; margin-top: 16px; }
.firstrun__actions i { font-family: var(--display); }

/* ============================================================ calibration = */
.calib { display: flex; align-items: center; gap: 14px; margin: 4px 0 10px; }
.calib__text { margin: 0; flex: 1; font-size: 12.5px; color: var(--fg-dim); }
.calib__pad {
  width: 96px; height: 64px; border-radius: 12px; font: 600 14px var(--ui);
  color: var(--ink); background: linear-gradient(180deg, var(--ivory), var(--ivory-warm));
  border: none; cursor: pointer; touch-action: manipulation;
}
.calib__pad:active, .calib__pad.is-hit { background: var(--jade-hi); }

/* ============================================================ drop zone === */
.dropzone {
  position: fixed; inset: 12px; z-index: 50; display: grid; place-items: center;
  border: 2px dashed var(--jade); border-radius: 20px; background: rgba(8,12,20,.82);
  pointer-events: none;
}
.dropzone span { font-family: var(--display); font-style: italic; font-size: 28px; color: var(--jade-hi); }

/* ============================================================ keyboard chip */
.midichip { position: relative; padding-left: 26px; }
.midichip::before {
  content: ""; position: absolute; left: 11px; top: 50%; width: 8px; height: 8px;
  margin-top: -4px; border-radius: 50%; background: var(--fg-faint);
}
.midichip.is-on { border-color: rgba(89,194,160,.55); color: var(--jade-hi); }
.midichip.is-on::before { background: var(--jade); box-shadow: 0 0 8px rgba(89,194,160,.8); }
.midichip.is-warn { border-color: rgba(217,164,65,.55); color: var(--gold-hi); }
.midichip.is-warn::before { background: var(--warn); }

/* ============================================================ masthead ==== */
.btn--round { min-width: 32px; padding-left: 0; padding-right: 0; font-family: var(--data); font-weight: 600; }
#sampleList { max-width: 150px; }
#pieceList { max-width: 170px; }

/* ============================================================ wait mode ==== */
/* Wait mode needs no Play: the first right note starts it, the keys to press light up by themselves, and
   the next first note after the end starts again. The play bar stays (the timeline, the tempo, Next, the
   score — and the same size as in the other modes); in Play's place, a mark that says the music waits,
   pulsing while it waits for you. (Play and Pause would only flicker there: the playhead stops at every
   note.) */
.waitmark {
  display: none; place-items: center; flex: none; width: 46px; height: 46px; border-radius: 50%;
  border: 1px solid rgba(89,194,160,.42); background: radial-gradient(circle at 50% 38%, #1b2a30, #111923 70%);
}
.waitmark__dot { width: 12px; height: 12px; border-radius: 50%; background: var(--jade); box-shadow: 0 0 10px rgba(89,194,160,.75); }
body.is-wait .waitmark { display: grid; }
body.is-wait #btnPlay, body.is-wait #btnPause { display: none !important; }
body.is-wait:not(.is-playing) .waitmark__dot { animation: waitpulse 1.6s var(--ease) infinite; }
@keyframes waitpulse { 0%, 100% { transform: scale(1); opacity: 1; } 50% { transform: scale(.68); opacity: .55; } }
@media (prefers-reduced-motion: reduce) { body.is-wait:not(.is-playing) .waitmark__dot { animation: none; } }
@media (max-height: 900px) { .waitmark { width: 40px; height: 40px; } }      /* Play's size there */
.chip--metro { font-weight: 500; }

/* ============================================================ MIDI parts === */
.parts { display: grid; gap: 6px; width: 100%; margin: 4px 0 6px; }
.part { display: flex; align-items: center; gap: 10px; padding: 6px 10px; border: 1px solid var(--rule-soft); border-radius: var(--r-sm); background: var(--ink-sunk); }
.part__name { font-weight: 500; color: var(--fg); min-width: 110px; }
.part__meta { flex: 1; color: var(--fg-dim); font-size: 12px; font-family: var(--data); }
.part.is-off .part__name, .part.is-off .part__meta { opacity: .5; }

/* ============================================================ messages ==== */
/* The status line is announced to screen readers but takes no room on the
   page; its text lives in the panel over the falling notes, which shows only
   while the pointer is over them. An error (.is-alert) shows for a moment
   anyway, because it needs you to do something. */
.status {
  position: absolute !important; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0;
}
.rollwrap .hud { opacity: 0; }
.rollwrap:hover .hud:not(.is-empty), .rollwrap .hud.is-alert:not(.is-empty) { opacity: 1; }
.hud.is-empty { display: none; }
.nowplaying { justify-content: center; }

/* ============================================================ toasts ====== */
/* WHAT JUST HAPPENED. The status line is kept for screen readers only, and
   its text shows over the falling notes while the pointer is there — which
   left the result of opening a file (what was repaired, which part you are
   practising, or that it could only be opened as notes) invisible unless you
   happened to hover. A toast shows it for a few seconds under the header, in
   the corner the eye does not need while playing; practice feedback stays in
   the heads-up display over the notes. */
.toasts {
  position: fixed; top: var(--toast-top, 132px); z-index: 60;
  /* aligned with the right edge of the workspace, not the window */
  right: max(clamp(12px, 3vw, 34px), calc((100vw - 1360px) / 2 + clamp(14px, 3.2vw, 34px)));
  display: flex; flex-direction: column; gap: 8px; align-items: flex-end;
  pointer-events: none; max-width: min(460px, calc(100vw - 24px));
}
.toast {
  pointer-events: auto; display: flex; gap: 10px; align-items: flex-start;
  padding: 10px 12px 10px 14px; border-radius: 12px;
  background: rgba(17, 24, 36, .94); border: 1px solid var(--rule);
  box-shadow: 0 18px 40px -18px rgba(0,0,0,.95);
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
  font-size: 13px; line-height: 1.5; color: var(--fg);
  animation: toastin .22s var(--ease);
}
.toast::before { content: ""; flex: none; width: 8px; height: 8px; margin-top: 6px; border-radius: 50%; background: var(--fg-faint); }
.toast--ok::before { background: var(--ok); box-shadow: 0 0 8px rgba(89,194,160,.7); }
.toast--warn { border-color: rgba(217,164,65,.45); }
.toast--warn::before { background: var(--warn); }
.toast--err { border-color: rgba(210,96,79,.6); }
.toast--err::before { background: var(--err); }
.toast__text { flex: 1; min-width: 0; display: -webkit-box; -webkit-line-clamp: 5; -webkit-box-orient: vertical; overflow: hidden; }
.toast__text b { font-weight: 600; }
.toast__close {
  flex: none; border: none; background: transparent; color: var(--fg-faint); cursor: pointer;
  font: 500 16px/1 var(--ui); padding: 2px 2px 0; margin-top: 1px;
}
.toast__close:hover { color: var(--fg); }
.toast.is-leaving { opacity: 0; transform: translateY(-6px); transition: opacity .2s var(--ease), transform .2s var(--ease); }
@keyframes toastin { from { opacity: 0; transform: translateY(-8px); } to { opacity: 1; transform: none; } }

/* ============================================================ phone ======= */
/* The header took three rows on a phone (brand, then two rows of buttons):
   190px of a 844px screen before any music. Two rows now: the piece, then
   one row of actions, with the keyboard chip reduced to its status dot. */
@media (max-width: 760px) {
  .masthead { gap: 10px; padding-bottom: 10px; }
  .brand__text { display: none; }
  .brand__mark { width: 30px; height: 30px; }
  .nowplaying { flex: 1 1 0; order: 0; }
  .loaders { flex-basis: 100%; width: 100%; min-width: 0; flex-wrap: nowrap; gap: 6px; }
  .loaders .select, .loaders #sampleList, .loaders #pieceList { flex: 1 1 0; min-width: 0; max-width: none; }
  .loaders .btn--file { flex: none; padding: 9px 12px; }
  /* the keyboard chip keeps its status dot and a short name */
  .midichip { flex: none; padding: 9px 10px 9px 24px; font-size: 0; }
  .midichip::after { content: "MIDI"; font-size: 12px; }
  .midichip::before { left: 10px; }
  .toasts { top: auto; bottom: 12px; right: 12px; left: 12px; max-width: none; align-items: stretch; }
  .firstrun__inner h2 { font-size: 24px; }
  .firstrun__inner p { font-size: 13px; }
  .firstrun__hint { display: none; }
  /* the start page defines the page's height on a phone, instead of being
     clipped by a panel sized for a score */
  body.is-empty .paper { min-height: 0; flex: none; }
  body.is-empty .firstrun { position: relative; inset: auto; padding: 22px 14px 26px; }
  .firstrun__inner { width: 100%; }
  .firstrun__actions .btn { white-space: normal; flex: 1 1 100%; }
  /* settings fields stack: label over control */
  .setgroup .field { flex-wrap: wrap; row-gap: 5px; }
  .setgroup .field__label { white-space: normal; }
  #handReach { max-width: 100%; }
}

/* ============================================================ marks help == */
/* What the marks under the falling notes mean, in the ? window. They used to be
   a legend over the falling notes, top right, where it covered the notes it
   explained; the marks of the open piece are tagged "in this piece". */
.marks { margin-top: 20px; padding-top: 18px; border-top: 1px solid var(--rule-soft); }
.marks__list { display: grid; grid-template-columns: 34px 1fr; gap: 10px 12px; margin: 0; align-items: baseline; }
.marks__list dt { display: flex; justify-content: center; align-self: start; padding-top: 0; }
/* each mark on a small falling note (gold, the right hand's), drawn as the falling notes draw it */
.marks__list dt svg { overflow: visible; }
.marks__list .mk-note { fill: var(--gold); stroke: var(--gold-hi); stroke-width: 1; }
.marks__list .mk-disc { fill: rgba(10,14,22,.86); }
.marks__list .mk-ring { stroke: var(--ivory); stroke-width: 1.3; }
.marks__list .mk-digit { fill: var(--ivory); font: 700 8.5px var(--ui); text-anchor: middle; }
.marks__list .mk-halo { fill: none; stroke: rgba(10,14,22,.9); stroke-width: 4; stroke-linecap: round; stroke-linejoin: round; }
.marks__list .mk-line { fill: none; stroke: var(--ivory); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.marks__list .mk-line.mk-thin { stroke-width: 1.5; }
.marks__list .mk-outline { fill: none; stroke: var(--ivory); stroke-width: 2; }
.marks__list .mk-faint { fill: var(--ivory); opacity: .22; }
.marks__list .mk-miss { fill: none; stroke: var(--warn); stroke-width: 2; stroke-linecap: round; }
.marks__list .mk-wrong { fill: none; stroke: var(--err); stroke-width: 2; stroke-linecap: round; }
.marks__list dd { margin: 0; color: var(--fg-dim); font-size: 13px; line-height: 1.5; }
.marks__list dd b { color: var(--fg); font-weight: 600; }
.marks__here { display: none; margin-left: 6px; padding: 1px 7px; border-radius: var(--r-pill); font: 500 10.5px var(--ui); font-style: normal;
  color: var(--jade-hi); border: 1px solid rgba(89,194,160,.45); white-space: nowrap; }
.marks__list dd.is-here .marks__here { display: inline-block; }
.marks__note { margin-top: 12px; padding-top: 0; border-top: none; }

/* ============================================================ exercises === */
/* The exercises take the stage: the score and the falling notes give way, the
   keys stay — they are half of every exercise (drill-ui.js). The stage is a
   cool grey-blue page: the app's blue-black lightened, not the score's warm
   ivory, so the exercises read as a page of their own while the notation is
   still ink on a light ground, as it is printed. Ink for text and notation,
   a deep gold for "this one", green for right, red for wrong. Every colour of
   the exercises and the hand test is one of the --st-* tokens below, so the
   stage is re-coloured in one place. Measured (AUDIT.md): every text token at
   least 4.5:1 at the stage's darkest point, every mark at least 3:1. */
body.is-drill .rail, body.is-drill #sheetPanel, body.is-drill #noSheet, body.is-drill #rollPanel,
body.is-drill #scorePanel, body.is-drill .transport { display: none !important; }
body.is-drill #keyboardPanel { display: block !important; }
body.is-drill .instrument { flex: none; min-height: calc(var(--kb-h) + 2px); }
.drill {
  --st-bg-top: #eef1f6;  --st-bg-mid: #e8ecf3;  --st-bg-bottom: #dfe4ec;
  --st-ink: #171d29;              /* text, notes, clefs — the note asked keeps it, right or wrong */
  --st-body: #353d4c;             /* running text */
  --st-dim: #4a5262;              /* secondary text */
  --st-faint: #586172;            /* small labels: 4.9:1 at the darkest */
  --st-line: #434b5a;             /* staff lines: a step behind the notes */
  --st-rule: #d0d6e0;             /* separators, empty meters (a filled one is 3.4:1 against it) */
  --st-card: rgba(255,255,255,.6);
  --st-card-line: #c4ccd9;
  --st-btn: rgba(255,255,255,.55);          /* a quiet button, a choice */
  --st-btn-hover: rgba(255,255,255,.92);
  --st-btn-line: #aeb7c6;
  --st-btn-line-hi: #8792a5;
  --st-field: rgba(255,255,255,.8);         /* a select */
  --st-strong: #171d29;           /* a pressed ♯/♭: ivory on ink, like the action to take (Start) */
  --st-strong-text: var(--ivory);
  --st-focus: var(--lapis-deep);  /* the app's light-blue ring would be 1.4-1.6:1 here; this is 5.9:1 or more */
  --st-gold: #8a672a;             /* "this one": an answer shown on the staff, the next note to play */
  --st-gold-text: #77571f;
  --st-gold-line: rgba(138,103,42,.55);
  --st-gold-wash: rgba(226,177,92,.16);
  --st-gold-sel: rgba(226,177,92,.2);       /* the finger being typed */
  --st-ok: #1b7f60;
  --st-ok-text: #196b51;
  --st-ok-fill: #237a5e;          /* a right choice, white text on it */
  --st-bad: #b9473a;
  --st-bad-text: #a33b2f;
  --st-bad-fill: #b2463a;
  --st-done: #596274;             /* played already */
  --st-empty: #5d6677;            /* a finger still to give (?) */
  --st-due: var(--lapis-deep);
  --st-len: #9a6512;              /* Note lengths: the right note, the wrong length (3.9:1 at the darkest) */
  --st-play: #3d5f9e;             /* Note lengths: your keys as you play them, and the playhead (4.9:1) */
  position: relative; flex: 1 1 0; min-height: 330px;
  display: flex; flex-direction: column;
  padding: 16px clamp(14px, 2.6vw, 30px) 20px;
  background: linear-gradient(180deg, var(--st-bg-top) 0%, var(--st-bg-mid) 38%, var(--st-bg-bottom) 100%);
  color: var(--st-ink);
  border-radius: var(--r-lg); border: 1px solid var(--rule);
  box-shadow: var(--lift-3);
  overflow: auto;
  color-scheme: light;
}
.drill :focus-visible { outline-color: var(--st-focus); }
.drill::before {
  content: ""; position: absolute; inset: 0 0 auto; height: 2px;
  background: linear-gradient(90deg, transparent, var(--gold-deep) 12%, var(--gold) 50%, var(--gold-deep) 88%, transparent);
  opacity: .55; pointer-events: none;
}
.drill__home, .drill__run { display: flex; flex-direction: column; gap: 12px; flex: 1 1 auto; min-height: 0; }
/* the start page keeps its own height: shrunk to the stage, its rows ran out past the stage's bottom
   padding, and the last one (note names, speed) sat on the edge when scrolled to the end */
.drill__home { flex: 0 0 auto; }
.drill__home.is-hidden, .drill__run.is-hidden { display: none; }
.drill__head { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px 20px; flex-wrap: wrap; }
.drill__heading { min-width: 0; }
.drill__kicker { margin: 0 0 3px; font: 600 10px var(--ui); letter-spacing: .2em; text-transform: uppercase; color: var(--st-gold-text); }
.drill__due { letter-spacing: .06em; text-transform: none; color: var(--st-due); margin-left: 8px; }
.drill__title { margin: 0; font-family: var(--display); font-weight: 500; font-size: 26px; letter-spacing: -.015em; line-height: 1.1; color: var(--st-ink); }
.drill__lede { margin: 0; max-width: 92ch; font-size: 13.5px; line-height: 1.55; color: var(--st-dim); }
/* on the stage the paper buttons are quiet white ones on cool lines, and the "ink" button (styled with
   the score's page) is the action to take: ivory on ink, as on the page */
.drill .btn--paper { color: var(--st-ink); background: var(--st-btn); border-color: var(--st-btn-line); }
.drill .btn--paper:hover:not(:disabled) { background: var(--st-btn-hover); border-color: var(--st-btn-line-hi); }
.drill .field__label { color: var(--st-faint); }
.drill .select--paper, .drill .dcard__sel { background-color: var(--st-field); color: var(--st-ink); border-color: var(--st-btn-line); }
.drill .select--paper:hover, .drill .dcard__sel:hover { border-color: var(--st-btn-line-hi); }
/* (2.4: the lists' own colours are the app's — design.css. These two rules painted every list in the exercises
   white, under the stage's ink, which in the dark schemes is light: light text on a white list) */

.drill__cards { display: flex; flex-direction: column; gap: 14px; }
.dgroup__title { margin: 0 0 7px; font: 600 10px var(--ui); letter-spacing: .18em; text-transform: uppercase; color: var(--st-faint); }
.dgroup__cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(232px, 1fr)); gap: 10px; }
.dcard {
  display: flex; flex-direction: column; gap: 6px; padding: 12px 13px 11px;
  border: 1px solid var(--st-card-line); border-radius: var(--r-md); background: var(--st-card);
}
.dcard__top { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; }
.dcard__title { font-family: var(--display); font-weight: 500; font-size: 18px; letter-spacing: -.01em; }
.dcard__lvl { font: 11px var(--data); color: var(--st-faint); white-space: nowrap; }
.dcard__what { margin: 0; font-size: 12.5px; color: var(--st-dim); line-height: 1.45; }
.dcard__level { margin: 0; font-size: 12.5px; font-weight: 600; color: var(--st-ink); line-height: 1.35; }
.dcard__meter { height: 4px; border-radius: 3px; background: var(--st-rule); overflow: hidden; }
.dcard__meter i { display: block; height: 100%; background: var(--st-ok); }
.dcard__stats { margin: 0; font: 11px/1.45 var(--data); color: var(--st-faint); }
.dcard__acts { display: flex; align-items: center; gap: 6px 8px; flex-wrap: wrap; margin-top: auto; padding-top: 4px; }
.dcard__acts .btn { flex: none; }
.dcard__sel { flex: 1 1 0; min-width: 0; max-width: none; }
.dcard__free { flex-basis: 100%; display: inline-flex; align-items: center; gap: 5px; font-size: 11.5px; color: var(--st-dim); cursor: pointer; }
.dcard__free input { accent-color: var(--st-gold); }
.drill__big.is-listen b { cursor: pointer; color: var(--st-gold-text); }
.drill__opts { display: flex; gap: 12px 24px; flex-wrap: wrap; align-items: center; padding: 12px 0 4px; border-top: 1px solid var(--st-rule); }
/* a lesson, prepared by the app (drill-ui.js prepareLesson): first on the page, in the stage's gold — "this one" */
.dgroup--lesson .dgroup__title { color: var(--st-gold-text); }
.dlesson { display: flex; align-items: center; gap: 10px 18px; flex-wrap: wrap; padding: 12px 14px;
  border: 1px solid var(--st-gold-line); border-radius: var(--r-md); background: var(--st-gold-wash); }
.dlesson__text { flex: 1 1 340px; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.dlesson__title { font-family: var(--display); font-weight: 500; font-size: 19px; letter-spacing: -.01em; color: var(--st-ink); }
.dlesson__what { margin: 0; font-size: 12.5px; line-height: 1.45; color: var(--st-dim); max-width: 80ch; }
.dlesson__acts { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.dlesson__min { max-width: none; }
.dlesson__plan { margin: 2px auto 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 7px; text-align: left; max-width: 76ch;
  font-size: 14px; line-height: 1.45; color: var(--st-body); }
.dlesson__plan b { font-family: var(--display); font-weight: 500; font-size: 16.5px; color: var(--st-ink); }
.dlesson__grp { color: var(--st-faint); font-size: 12.5px; }
.drill__lessonlede { margin: 0; font-size: 14px; line-height: 1.5; color: var(--st-body); max-width: 76ch; }
.drill__run.is-plan .drill__progress { visibility: hidden; }

.drill__progress { display: flex; align-items: center; gap: 10px; font: 12px var(--data); color: var(--st-dim); margin-left: auto; }
.drill__progress > span:not(.drill__bar) { white-space: nowrap; }
.drill__bar { width: 150px; height: 5px; border-radius: 3px; background: var(--st-rule); overflow: hidden; }
.drill__bar i { display: block; height: 100%; width: 0; background: var(--st-ok); transition: width .3s var(--ease); }
.drill__score { min-width: 0; white-space: nowrap; }
.drill__actions { display: flex; gap: 6px; flex-wrap: wrap; }
/* the set can no longer open the next level: Reset is the thing to press */
.drill .btn--paper.is-suggest { border-color: var(--st-gold-line); color: var(--st-gold-text); background: var(--st-gold-wash); }
.drill .btn--paper.is-suggest:hover:not(:disabled) { border-color: var(--st-gold); background: var(--st-gold-sel); }
/* never shrunk below what it holds (the staff is sized to the room left, drill-ui.js _fitStaff): on a
   short phone the words below move down and the stage scrolls, instead of the staff being drawn over the
   buttons above it. "safe": what does not fit starts at the top, never pushed up out of reach. */
.drill__prompt {
  flex: 1 0 auto; min-height: 150px;
  display: flex; flex-direction: column; align-items: center; justify-content: center; justify-content: safe center; gap: 12px;
}
.drill__prompt.is-hidden { display: none; }
.drill__staffrow { display: flex; align-items: center; justify-content: center; gap: 14px; max-width: 100%; }
.drill__staff { display: block; flex: none; touch-action: none; user-select: none; -webkit-user-select: none; }
.drill__staff.is-picking { cursor: crosshair; }
.drill__acc { display: flex; flex-direction: column; gap: 6px; }
.drill__acc.is-hidden { display: none; }
.drill__acc .btn { width: 48px; height: 46px; padding: 0; font: 400 24px var(--display); line-height: 1; }
/* pressed stays pressed under the pointer: the quiet button's hover (0,4,0) used to win over it, and a
   chosen ♯ looked unchosen while the mouse was on it — or, on a touch screen, after it was tapped */
.drill .drill__acc .btn[aria-pressed="true"],
.drill .drill__acc .btn[aria-pressed="true"]:hover:not(:disabled) { background: var(--st-strong); color: var(--st-strong-text); border-color: var(--st-strong); }
.drill__big { display: flex; flex-direction: column; align-items: center; gap: 2px; text-align: center; }
.drill__big.is-hidden { display: none; }
.drill__big b { font-family: var(--display); font-weight: 500; font-size: clamp(60px, 13vh, 124px); line-height: 1.02; letter-spacing: -.02em; color: var(--st-ink); }
.drill__big span { font-family: var(--display); font-style: italic; font-size: clamp(17px, 3vh, 24px); color: var(--st-dim); min-height: 1em; }
.drill__choices { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; max-width: 820px; }
.drill__choices.is-hidden { display: none; }
.dchoice {
  min-width: 104px; min-height: 48px; display: inline-flex; align-items: center; justify-content: center; gap: 9px;
  font: 500 19px var(--display); color: var(--st-ink);
  background: var(--st-btn); border-color: var(--st-btn-line);
}
.dchoice:hover:not(:disabled) { background: var(--st-btn-hover); border-color: var(--st-btn-line-hi); }
.dchoice__n { font: 600 10px var(--data); color: var(--st-faint); }
/* an answer's colour wins over the hover, which a touch screen leaves on the button just tapped */
.dchoice.is-ok, .dchoice.is-ok:hover:not(:disabled) { background: var(--st-ok-fill); border-color: var(--st-ok-fill); color: #fff; }
.dchoice.is-bad, .dchoice.is-bad:hover:not(:disabled) { background: var(--st-bad-fill); border-color: var(--st-bad-fill); color: #fff; }
.dchoice.is-ok .dchoice__n, .dchoice.is-bad .dchoice__n { color: rgba(255,255,255,.9); }
/* (their min-heights hold a line while empty; without flex-shrink 0 they also let a two-line question be
   squeezed to one, over the feedback, when a short phone has no room — the stage scrolls instead) */
.drill__head, .drill__ask, .drill__fb { flex-shrink: 0; }
.drill__ask { margin: 0; text-align: center; font-family: var(--display); font-size: 18px; color: var(--st-ink); min-height: 1.35em; }
.drill__fb { margin: 0 auto; text-align: center; font-size: 14px; line-height: 1.5; min-height: 1.5em; color: var(--st-body); max-width: 84ch; }
.drill__fb.is-ok { color: var(--st-ok-text); }
.drill__fb.is-bad { color: var(--st-bad-text); }
.drill__done { display: flex; flex-direction: column; align-items: center; justify-content: center; justify-content: safe center; gap: 10px; text-align: center; padding: 18px 0; flex: 1 0 auto; }
.drill__done.is-hidden { display: none; }
.drill__verdict { margin: 0; font-family: var(--display); font-size: 25px; letter-spacing: -.01em; }
.drill__up { margin: 0; font-size: 14.5px; line-height: 1.5; color: var(--st-body); max-width: 72ch; }
.drill__up.is-up { color: var(--st-ok-text); font-weight: 600; }
.drill__missed { margin: 0; font-size: 13.5px; color: var(--st-bad-text); max-width: 72ch; }
.drill__doneacts { display: flex; gap: 10px; flex-wrap: wrap; justify-content: center; margin-top: 4px; }

/* the staff (staff-view.js): ink on the stage; green is only ever "right", red "wrong", and an answer
   shown on the staff (key -> staff) is gold, the colour of the key asked. The note asked keeps its ink
   when the answer is shown — the key played wrong is the red one. */
.st-line, .st-ledger { stroke: var(--st-line); }
.st-glyph, .st-note { fill: var(--st-ink); }
.st-brace { fill: var(--st-ink); stroke: var(--st-ink); }
.st-note.is-ok { fill: var(--st-ok); }
.st-note.is-hint { fill: var(--st-gold); }
.st-note.is-bad { fill: var(--st-bad); }
.st-note.is-ghost, .st-ledger.is-ghost { opacity: .36; }

/* Note lengths (rhythm-view.js): the bar in ink. Under it the count, and the time line: what is written
   (outlined; a rest a dotted silence), and your keys in blue as they are held — then green right, amber
   the length, red wrong. Played to you, the time line is gold: an answer shown, as on the staff. */
.drill__rhythm { display: block; flex: none; max-width: 100%; user-select: none; -webkit-user-select: none; }
.rh-line { stroke: var(--st-line); }
.rh-glyph, .rh-note { fill: var(--st-ink); }
.rh-stem { stroke: var(--st-ink); }
.rh-beam { fill: var(--st-ink); }
.rh-note.is-ok, .rh-beam.is-ok { fill: var(--st-ok); }
.rh-stem.is-ok { stroke: var(--st-ok); }
.rh-note.is-len, .rh-beam.is-len { fill: var(--st-len); }
.rh-stem.is-len { stroke: var(--st-len); }
.rh-note.is-bad, .rh-beam.is-bad { fill: var(--st-bad); }
.rh-stem.is-bad { stroke: var(--st-bad); }
.rh-count { fill: var(--st-faint); font-family: var(--data); font-weight: 600; }
.rh-count.is-now { fill: var(--st-ink); }
.rh-count.is-cin { fill: var(--st-gold-text); }
.rh-w { fill: rgba(88,97,114,.16); stroke: var(--st-faint); stroke-width: 1px; }
.rh-w.is-miss { fill: rgba(185,71,58,.13); stroke: var(--st-bad); stroke-dasharray: 4 3; }
.rh-rest { stroke: var(--st-faint); stroke-dasharray: 1.5 4.5; stroke-linecap: round; }
.rh-p { fill: var(--st-play); }
.rh-p.is-demo { fill: var(--st-gold); }
.rh-p.is-ok { fill: var(--st-ok); }
.rh-p.is-len { fill: var(--st-len); }
.rh-p.is-bad { fill: var(--st-bad); }
.rh-head { stroke: var(--st-play); stroke-linecap: round; }

/* find your hand by playing (hand-ui.js) */
/* its middle scrolls when it does not fit (a phone): centred while it fits, from the top when it
   does not — auto margins never go below zero, where centring the overflow would push its top
   out of reach, over the heading */
#handPrompt { justify-content: flex-start; overflow-y: auto; overscroll-behavior: contain; }
#handPrompt > * { margin-block: auto; }
/* a long passage on a narrow phone: the staff shrinks to the width (its viewBox keeps the drawing whole)
   rather than overflowing on both sides; its notes are tapped, never dragged, so the page may scroll over it */
#handStaff { max-width: 100%; height: auto; touch-action: manipulation; }
.hand__big { font-family: var(--display); font-weight: 500; font-size: clamp(40px, 9vh, 84px); line-height: 1.05; letter-spacing: -.02em; color: var(--st-ink); text-align: center; }
.hand__big.is-hidden { display: none; }
.hand__answers { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; min-height: 0; }
.hand__answers:empty { display: none; }
.hand__choice { min-width: 130px; min-height: 46px; font: 500 17px var(--display); }
.hand__digit { min-width: 46px; min-height: 46px; padding: 0 !important; font: 600 18px var(--data); }
.hand__out:empty { display: none; }
#handTitle:focus { outline: none; }        /* focused for a keyboard user at each new step, so it is read out — not a control */
.hand__steps { list-style: none; counter-reset: hstep; margin: 0 auto; padding: 0; max-width: 640px; display: flex; flex-direction: column; gap: 16px; text-align: left; }
.hand__steps li { counter-increment: hstep; display: grid; grid-template-columns: 32px 1fr; column-gap: 14px; row-gap: 2px; }
.hand__steps li::before {
  content: counter(hstep); grid-row: span 2; align-self: start; width: 30px; height: 30px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  font: 600 14px var(--data); color: var(--st-gold-text); border: 1.5px solid var(--st-gold-line); background: var(--st-gold-wash);
}
.hand__steps b { font-family: var(--display); font-weight: 500; font-size: 21px; line-height: 1.3; color: var(--st-ink); }
.hand__steps span { font-size: 14.5px; line-height: 1.5; color: var(--st-dim); }
.hand__result { max-width: 760px; margin: 0 auto; text-align: left; }
.hand__result p { margin: 0 0 8px; font-size: 14px; line-height: 1.55; color: var(--st-dim); }
.hand__result .hand__rec { margin: 0 0 14px; font-family: var(--display); font-size: 19px; line-height: 1.4; color: var(--st-ink); }
.st-finger { fill: var(--st-ink); font-family: var(--data); font-weight: 600; }
.st-finger.is-next { fill: var(--st-gold-text); }
.st-finger.is-done { fill: var(--st-done); }
.st-finger.is-empty { fill: var(--st-empty); }
.st-fsel { fill: var(--st-gold-sel); stroke: var(--st-gold); stroke-width: 1; }
.st-note.is-next { fill: var(--st-gold); }
.st-note.is-done { fill: var(--st-done); }
.dhand { display: flex; align-items: center; gap: 10px 16px; flex-wrap: wrap; padding: 10px 13px; border: 1px dashed var(--st-card-line); border-radius: var(--r-md); }
.dhand__text { margin: 0; flex: 1 1 320px; font-size: 12.5px; line-height: 1.45; color: var(--st-dim); }
.handtest { align-items: center; }

/* the key asked about, or the answer shown on the keys */
.pk--white.is-ask { fill: var(--gold-hi); }
.pk--black.is-ask { fill: var(--gold); }

@media (max-height: 900px) {
  .drill__lede { display: none; }
  .drill { padding-top: 12px; }
  .drill__home, .drill__run { gap: 10px; }
}

/* the coach panel's way in */
.coach__drills { display: flex; align-items: center; gap: 10px 16px; flex-wrap: wrap; padding-top: 12px; border-top: 1px solid var(--rule-soft); }
.coach__drills .coach__note { flex: 1 1 360px; }
#coachDrillsDue { color: var(--lapis-hi); }

@media (pointer: coarse) {
  .dcard__free { min-height: 44px; }
}
@media (max-width: 760px) {
  .hand__steps { gap: 10px; }
  .hand__steps b { font-size: 18px; }
  .hand__steps span { font-size: 13.5px; }
  #handAsk { font-size: 16px; }
  #handFeedback { font-size: 13px; }
  .hand__result .hand__rec { font-size: 17px; }
  .drill { padding: 14px 12px 12px; min-height: 300px; }
  .drill__title { font-size: 22px; }
  .drill__progress { margin-left: 0; width: 100%; }
  .drill__bar { flex: 1 1 auto; }
  .drill__acc .btn { width: 42px; height: 40px; }
  .dchoice { min-width: 84px; font-size: 17px; }
}
