/* chrome tokens: generated by scripts/chrome-tokens.mjs from Studio's theme.json. Do not edit by hand. */
:root {
  color-scheme: dark;
  --frame: #0b0d11;
  --page: #181a1d;
  --card: #242832;
  --raised: #333946;
  --stroke-subtle: #ffffff30;
  --stroke-strong: #ffffff4a;
  --accent: #8bb8ee;
  --caution: #feb454;
  --failure: #ef7177;
  --failure-wash: #48161b;
  --icon: #a6acb4;
  --strip: #232733;
  --ground: #17191c;
  --ink: #dce0e6;
  --muted: #a6acb4;
  --code-ink: #d5d9df;
  --green: #aad84c;
  --hover: #303642;
  --stage: var(--frame);
  --code: var(--page);
  --panel: var(--card);
  --line: var(--stroke-subtle);
  --line-strong: var(--stroke-strong);
  --blue: var(--accent);
  --red: var(--failure);
}
:root[data-theme="light"] {
  color-scheme: light;
  --frame: #dfe5ed;
  --page: #fafbfd;
  --card: #ffffff;
  --raised: #e4e9f1;
  --stroke-subtle: #ccd4df;
  --stroke-strong: #aab6c6;
  --accent: #285ea2;
  --caution: #8a5810;
  --failure: #b42c3d;
  --failure-wash: #f7dcdf;
  --icon: #59677c;
  --strip: #eceff5;
  --ground: #f2f4f8;
  --ink: #253249;
  --muted: #59677c;
  --code-ink: #253249;
  --green: #487222;
  --hover: #2532490b;
}
/* end chrome tokens */
:root {
  /* The paper is the score's, not the app's: printed music keeps its own
     white and ink in both themes. */
  --paper: #ffffff;
  --paper-ink: #111;
  --paper-muted: #68717d;
  --paper-cursor: #3978bb;
  --paper-wrong: #bb2233;
  --sans: system-ui, -apple-system, "Segoe UI", sans-serif;
  --mono: "JetBrains Mono", "SFMono-Regular", "Cascadia Code", monospace;
}
* { box-sizing: border-box; }
html { min-width: 320px; background: var(--ground); }
body { margin: 0; color: var(--ink); font: 14px/1.5 var(--sans); }
button, input, textarea, select { font: inherit; }
button, summary, a { -webkit-tap-highlight-color: transparent; }
button, summary { cursor: pointer; }
button:disabled { cursor: default; opacity: .45; }
button { color: inherit; }
a { color: inherit; text-underline-offset: 3px; }
[hidden] { display: none !important; }
:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }
button, select, summary { border-radius: 4px; }
.skip-link { position: fixed; top: 8px; left: 12px; z-index: 100; padding: 8px 12px; background: var(--raised); transform: translateY(-150%); }
.skip-link:focus { transform: none; }
.topbar { height: 72px; padding: 0 28px; display: flex; align-items: center; gap: 48px; border-bottom: 1px solid var(--line); background: var(--ground); }
.wordmark { text-decoration: none; font-family: Georgia, serif; font-weight: 600; font-size: 25px; letter-spacing: -.6px; white-space: nowrap; }
.wordmark small { display: none; }
.primary-nav { display: flex; gap: 6px; height: 100%; align-items: center; }
.primary-nav a { padding: 6px 10px; border-radius: 4px; text-decoration: none; font-size: 13px; color: var(--muted); }
.primary-nav a:hover { background: var(--hover); color: var(--ink); }
.primary-nav a.active { color: var(--ink); box-shadow: 0 2px var(--blue); border-radius: 0; }
.topbar-actions { margin-left: auto; display: flex; align-items: center; gap: 14px; }
.secondary-nav { display: flex; gap: 6px; align-items: center; padding-right: 8px; border-right: 1px solid var(--line); }
.secondary-nav a { padding: 6px 10px; border-radius: 4px; text-decoration: none; font-size: 13px; color: var(--muted); }
.secondary-nav a:hover { background: var(--hover); color: var(--ink); }
.secondary-nav a.active { color: var(--ink); box-shadow: 0 2px var(--blue); border-radius: 0; }
.theme-toggle { display: inline-grid; place-items: center; width: 32px; height: 32px; border: 1px solid transparent; border-radius: 4px; background: none; color: var(--muted); cursor: pointer; }
.theme-toggle:hover { color: var(--ink); border-color: var(--line); }
.theme-toggle svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
:root[data-theme="dark"] .icon-moon { display: none; }
:root:not([data-theme="dark"]) .icon-sun { display: none; }
.utility-link, .app-link { border: 0; padding: 6px 0; background: none; font-size: 13px; color: var(--muted); text-decoration: none; }
.utility-link:hover, .app-link:hover { color: var(--ink); }
.app-link span { margin-left: 3px; }
.explore-main { height: calc(100dvh - 72px); min-height: 440px; }
.playhouse { height: 100%; display: grid; grid-template-rows: minmax(0,1fr) 30px; }
.example-label, .document-dot { display: none; }
.quick-example, .example-picker summary { min-height: 32px; padding: 6px 10px; border: 0; background: none; color: var(--muted); font-size: 13px; white-space: nowrap; }
.quick-example:hover, .example-picker summary:hover { background: var(--hover); color: var(--ink); }
.quick-example.active { color: var(--blue); background: color-mix(in srgb, var(--accent) 10%, transparent); }
.example-picker { position: relative; }
.example-picker summary { display: flex; gap: 10px; align-items: center; list-style: none; }
.example-picker summary::-webkit-details-marker { display: none; }
.example-picker[open] > summary { color: var(--ink); background: var(--raised); }
.picker-popover > label { display: block; margin-bottom: 8px; font-size: 12px; color: var(--muted); }
#example-search, #catalog-search { width: 100%; height: 38px; padding: 0 10px; border: 1px solid var(--line-strong); border-radius: 4px; background: var(--code); color: var(--ink); }
input::placeholder { color: #969da7; }
.picker-results { margin-top: 8px; max-height: min(420px,60vh); overflow-y: auto; }
.picker-results button { display: flex; width: 100%; align-items: center; justify-content: space-between; gap: 12px; border: 0; padding: 10px 8px; background: none; text-align: left; font-size: 13px; }
.picker-results button:hover, .picker-results button.active { background: var(--hover); }
.picker-results button.active { color: var(--blue); }
.picker-results small { color: var(--muted); font-size: 11px; text-align: right; }
.picker-empty { margin: 12px 8px 4px; color: var(--muted); font-size: 13px; }
#engine-state { font-size: 12px; color: var(--muted); }
.workbench { min-height: 0; display: grid; grid-template-columns: minmax(0, var(--source-width,44%)) 6px minmax(0,1fr); }
/* One row: the pane's header, gutter, find bar and diagnostics strip are
   all inside the canvas, drawn by the shared editor crate. */
.source-pane { min-width: 0; min-height: 0; display: grid; grid-template-rows: minmax(0,1fr); background: var(--code); border-right: 1px solid var(--line); }
.editor-host { position: relative; min-width: 0; min-height: 0; overflow: hidden; }
.editor-host #source { display: block; width: 100%; height: 100%; padding: 0; border: 0; outline-offset: -2px; }
/* eframe writes `outline: none` INLINE on the canvas when it starts, which beats
   the global :focus-visible rule; a keyboard visitor must still see where focus
   is, so the ring is restated with the weight to win. A pointer press on the
   pane is not :focus-visible and draws nothing. */
.editor-host #source:focus-visible { outline: 2px solid var(--blue) !important; }
.runtime-state { display: none; }
.format-dialog { width: min(620px,calc(100vw - 24px)); max-height: 85dvh; padding: 24px; border: 1px solid var(--line-strong); border-radius: 6px; background: var(--panel); color: var(--ink); }
.format-dialog::backdrop { background: #0009; }
.format-dialog header, .format-dialog footer { display: flex; align-items: center; justify-content: space-between; gap: 16px; }
.format-dialog h2 { margin: 0; font-size: 19px; font-weight: 550; }
.format-dialog p { color: var(--muted); font-size: 13px; }
.format-dialog button { padding: 6px 10px; background: var(--raised); border: 1px solid var(--line); }
.format-presets { display: flex; gap: 8px; padding: 8px 0 16px; }
#format-options > label { display: flex; align-items: center; justify-content: space-between; gap: 20px; padding: 12px 0; border-top: 1px solid var(--line); }
#format-options strong, #format-options small { display: block; }
#format-options strong { font: 13px var(--mono); font-weight: 500; }
#format-options small { color: var(--muted); font-size: 12px; margin-top: 3px; }
#format-options select { min-width: 100px; color: var(--ink); background: var(--code); padding: 6px; border: 1px solid var(--line-strong); }
#format-options input { accent-color: var(--blue); }
.format-dialog footer { margin-top: 20px; font-size: 12px; }
.output-pane { min-width: 0; min-height: 0; display: grid; grid-template-rows: 44px minmax(0,1fr) auto; background: var(--stage); }
.output-pane.paged { grid-template-rows: 44px auto minmax(0,1fr) auto; }
.output-pane:has(.layout-notes:not([hidden])) { grid-template-rows: 44px auto minmax(0,1fr) auto auto; }
.pane-bar { min-width: 0; display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 0 16px; background: var(--panel); border-bottom: 1px solid var(--line); }
.pane-bar > div:first-child { display: flex; align-items: center; gap: 10px; min-width: 0; }
.pane-index { display: none; }
.pane-bar h1, .pane-bar h2 { margin: 0; font: 500 13px var(--sans); color: var(--muted); }
.pane-bar small { color: var(--muted); font-size: 12px; }
.pane-actions { display: flex; align-items: center; gap: 6px; color: var(--muted); font-size: 12px; }
.pane-actions button, .view-switch button { min-height: 30px; padding: 5px 8px; border: 0; background: none; color: var(--muted); font-size: 12px; }
.pane-actions button:hover:not(:disabled) { color: var(--ink); background: var(--hover); }
.view-switch { display: flex; gap: 2px; padding: 2px; background: var(--ground); border-radius: 5px; }
.view-switch button { min-height: 26px; padding: 3px 9px; }
.view-switch button.active { color: var(--ink); background: var(--raised); }
/* The zoom rail wears the view switch's chrome — same ground, same pill, same
   button metrics — because it is the same kind of control beside it. It sits
   in the pane's header and never over the paper: a control that covers the
   music is a control that has to be moved before the music can be read. */
.zoom-rail { display: flex; align-items: center; gap: 2px; padding: 2px; background: var(--ground); border-radius: 5px; }
.zoom-rail button { min-height: 26px; padding: 3px 8px; border: 0; background: none; color: var(--muted); font-size: 13px; line-height: 1; }
.zoom-rail button:hover:not(:disabled) { color: var(--ink); background: var(--hover); }
.zoom-rail button:disabled { opacity: .45; }
#zoom-level { min-width: 40px; text-align: center; font-size: 11px; font-variant-numeric: tabular-nums; }
#source-size { margin-right: 6px; font-size: 11px; font-variant-numeric: tabular-nums; }
.paper-stage { position: relative; min-height: 0; padding: 32px; overflow: auto; background: var(--stage); }
.paper { position: relative; min-height: 260px; padding: 40px 32px; display: grid; align-content: start; background: var(--paper); color: var(--paper-ink); }
.paper svg { width: 100%; height: auto; display: block; }
.paper.pages { display: block; padding: 0; background: none; }
.paper.pages .sheet { margin: 0 auto 24px; background: var(--paper); color: var(--paper-ink); max-width: none; }
.sheet figcaption { padding: 4px; background: var(--stage); color: var(--muted); text-align: center; font-size: 11px; }
.loading-message { margin: 40px auto; color: var(--paper-muted); font-size: 13px; }
.ss-cursor { position: absolute; z-index: 8; left: 0; width: 2px; opacity: 0; pointer-events: none; background: var(--paper-cursor); }
.ss-cursor.on { opacity: .85; }
.transport { min-height: 44px; display: grid; grid-template-columns: auto auto minmax(60px,1fr) auto auto auto; align-items: center; gap: 10px; padding: 6px 16px; border-top: 1px solid var(--line); background: var(--panel); }
.transport button { min-height: 30px; padding: 4px 8px; border: 0; background: none; color: var(--ink); font-size: 13px; }
.transport button:hover:not(:disabled) { background: var(--hover); }
.transport input { width: 100%; accent-color: var(--blue); }
#time-readout, #bar-readout, #sound-readout { white-space: nowrap; color: var(--muted); font-size: 11px; font-variant-numeric: tabular-nums; }
/* Which sound the last Play used, in the player's own words. It says nothing
   until there is something to say, so it claims no room when it is empty. */
#sound-readout { font-variant-numeric: normal; }
/* The bar readout holds a dash until the first performance is known. Reserving
   the room it will need keeps the transport from re-columning underneath it. */
#bar-readout { min-width: 3.6em; }
/* Both rows of the workstation must be allowed to be narrower than their
   contents. A grid item's automatic minimum size is its MIN-CONTENT, and the
   footer's readouts are `white-space: nowrap`, so without this the whole
   playhouse column is as wide as the credit line: MEASURED at a 390 px
   viewport, the column settled at 754 px on /dictation/ and 859 px on Explore
   and the workbench walked off the right of the phone. The credit truncates
   instead. */
.playhouse > * { min-width: 0; }
.workbench-footer { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 0 16px; border-top: 1px solid var(--line); color: var(--muted); background: var(--ground); font-size: 11px; overflow: hidden; }
.workbench-footer > span { min-width: 0; }
.workbench-footer > span:nth-child(2) { display: none; }
/* The tally is a dash until the first engraving lands. Everything to its right
   is packed against it, so give the count its slot up front instead of shoving
   the Embed button sideways the moment the engine answers. */
#score-count { flex: 0 1 11em; font-variant-numeric: tabular-nums; }
.workbench-footer a { text-decoration: none; }
.workbench-footer a:hover { color: var(--blue); }
.embed-dialog { width: min(640px,calc(100vw - 32px)); max-height: 85vh; padding: 0; border: 1px solid var(--line-strong); border-radius: 6px; background: var(--panel); color: var(--ink); }
.embed-dialog::backdrop { background: #07090bb3; }
.dialog-card { padding: 20px; }
.dialog-card > header { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin-bottom: 16px; }
.eyebrow { display: none; }
.dialog-card h2 { margin: 0; font-size: 19px; font-weight: 600; }
.dialog-close { width: 32px; height: 32px; border: 0; background: none; font-size: 24px; color: var(--muted); }
.dialog-close:hover { background: var(--hover); }
.embed-options article { display: grid; grid-template-columns: 1fr auto; gap: 20px; align-items: center; padding: 18px 0; border-top: 1px solid var(--line); }
.embed-options article > span { display: none; }
.embed-options h3 { margin: 0 0 4px; font-size: 14px; font-weight: 550; }
.embed-options p { margin: 0; color: var(--muted); font-size: 13px; }
.embed-options code { font-family: var(--mono); }
.embed-options button, .embed-options a { display: inline-block; padding: 7px 10px; border: 1px solid var(--line-strong); border-radius: 4px; background: var(--raised); color: var(--ink); font-size: 12px; text-decoration: none; white-space: nowrap; }
.copy-status { min-height: 1.5em; margin: 12px 0 0; color: var(--muted); font-size: 12px; }
.catalog-shell { width: min(100% - 48px,1080px); margin: 0 auto; padding: 48px 0 80px; }
.catalog-lead { display: grid; grid-template-columns: 1fr 400px; gap: 40px; align-items: end; padding-bottom: 28px; border-bottom: 1px solid var(--line); }
.catalog-lead h1 { margin: 0; font-size: 28px; line-height: 1.2; font-weight: 600; letter-spacing: -.7px; }
.catalog-intro p { margin: 0 0 16px; font-size: 14px; color: var(--muted); }
.catalog-intro label { display: block; margin-bottom: 8px; font-size: 12px; color: var(--muted); }
.catalog-list article { display: grid; grid-template-columns: minmax(0,1fr) 140px 130px; gap: 24px; align-items: center; padding: 24px 0; border-bottom: 1px solid var(--line); }
.catalog-number { display: none; }
.catalog-list h2 { margin: 0 0 6px; font-size: 17px; font-weight: 550; }
.catalog-list p { max-width: 600px; margin: 0 0 10px; color: var(--muted); font-size: 13px; }
.catalog-list code { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--code-ink); font: 12px var(--mono); }
.catalog-list small { color: var(--muted); font-size: 12px; }
.catalog-list a { justify-self: end; color: var(--blue); font-size: 12px; text-decoration: none; }
.catalog-list a:hover { text-decoration: underline; }
.catalog-empty { padding: 24px 0; color: var(--muted); }
@media(max-width: 1000px) {
  .topbar { gap: 20px; padding-inline: 16px; }
  .paper-stage { padding: 20px; }
  .paper { padding: 28px 20px; }
  #engine-state { font-size: 11px; }
  #source-size, #render-meta { display: none; }
}
@media(max-width: 760px) {
  .topbar { height: auto; min-height: 54px; flex-wrap: wrap; gap: 8px; padding: 10px 12px; }
  .wordmark { font-size: 17px; }
  .topbar-actions { gap: 12px; }
  .primary-nav { order: 3; width: 100%; gap: 6px; }
  .primary-nav a { padding: 5px 10px; }
  .explore-main { height: auto; min-height: 0; }
  .playhouse { height: auto; grid-template-rows: auto 34px; }
  .workbench { grid-template-columns: 1fr; }
  .source-pane { height: 390px; border-right: 0; border-bottom: 1px solid var(--line); }
  .output-pane { height: min(650px,85dvh); min-height: 360px; }
  .pane-bar { padding-inline: 12px; }
  .paper-stage { padding: 16px; }
  .paper { padding: 28px 18px; min-height: 200px; }
  .picker-results { max-height: 45vh; }
  .transport { padding-inline: 12px; gap: 6px; grid-template-columns: auto auto 1fr auto; }
  #bar-readout, #sound-readout { display: none; }
  .catalog-shell { width: calc(100% - 32px); padding-top: 28px; }
  .catalog-lead { grid-template-columns: 1fr; gap: 20px; }
  .catalog-list article { grid-template-columns: 1fr auto; gap: 12px; }
  .catalog-list article > div { grid-column: 1/-1; }
  .embed-options article { grid-template-columns: 1fr; gap: 12px; }
  .embed-options article > :last-child { justify-self: start; }
}
/* Stillness is NONE, not "very fast". A .01ms transition still parks every
   property change behind the next rendered frame — and a page that is idle
   renders none, so under reduced motion the OLD value held forever: measured
   2026-09-15, the etude rail would not give up its width, an inline
   `grid-template-columns` was ignored, and a `min-height` read back 72px
   after being set to 90. A student who asks for stillness gets the new state
   immediately, with no animation at all. */
@media(prefers-reduced-motion: reduce) {
  *, *::before, *::after { scroll-behavior: auto !important; animation: none !important; transition: none !important; }
}

.source-workspace { min-width: 0; min-height: 0; display: grid; grid-template-columns: minmax(0,1fr); }
.source-workspace.with-wiki { grid-template-columns: minmax(0,var(--editor-width,50%)) 6px minmax(0,1fr); }
.source-pane { border-right: 0; }
.splitter { background: var(--ground); border-inline: 1px solid var(--line); cursor: col-resize; touch-action: none; position: relative; z-index: 2; }
.splitter:hover, .splitter:focus-visible { background: var(--blue); }
.resizing { cursor: col-resize; user-select: none; }
.resizing iframe, .resizing canvas { pointer-events: none; }
.wiki-pane { min-width: 0; min-height: 0; display: grid; grid-template-rows: 44px minmax(0,1fr); background: var(--panel); }
.wiki-pane h2 { margin: 0; font-size: 13px; font-weight: 500; }
.wiki-pane iframe { width: 100%; height: 100%; border: 0; }
.wiki-pane a { font-size: 11px; white-space: nowrap; }
.page-controls { display: flex; flex-wrap: wrap; gap: 8px 12px; padding: 8px 14px; border-bottom: 1px solid var(--line); background: var(--panel); color: var(--muted); font-size: 11px; align-items: center; }
.page-controls label { display: flex; align-items: center; gap: 5px; }
.page-controls select { padding: 3px; max-width: 95px; color: var(--ink); background: var(--ground); border: 1px solid var(--line); }
.layout-notes { margin: 0; padding: 7px 14px; font-size: 12px; color: var(--muted); background: var(--panel); max-height: 72px; overflow: auto; }
/* The composition inspector under the paper: the diagnostics strip's chrome
   (a rule above, the panel ground, small type, its own scroll) holding the
   kernel's monospace text in the code pane's ink. No tokens of its own. */
.inspector { min-height: 0; max-height: min(42dvh, 480px); padding: 7px 14px; overflow: auto; border-top: 1px solid var(--line); background: var(--panel); }
.inspector pre { margin: 0 0 12px; color: var(--code-ink); font: 12px/1.6 var(--mono); }
/* theme.js stamps the opening view on the root before the first paint. Holding
   the page bar's own row open from that first layout — the real bar, merely not
   yet drawn, so its height is exact — keeps the stage where it is when the first
   paginated render arrives. The !important answers the global [hidden] rule. */
:root[data-score-view="pages"] .workbench-page:not(.dictation-page) .output-pane { grid-template-rows: 44px auto minmax(0,1fr) auto; }
:root[data-score-view="pages"] .page-controls[hidden] { display: flex !important; visibility: hidden; }
@media(max-width: 760px) {
  .topbar { height: auto; min-height: 72px; }
  .workbench, .workbench.with-wiki { grid-template-columns: minmax(0,1fr); }
  #score-splitter { display: none; }
  .source-workspace.with-wiki { grid-template-columns: minmax(0,1fr); }
  #wiki-splitter { display: none; }
  .wiki-pane { height: 440px; }
  .output-pane { height: 650px; }
  .workbench-footer { font-size: 10px; }
  .example-picker summary { padding-inline: 4px; }
}


/* Document status sits in the workbench footer beside the other session
   readouts: the source pane's own header is the app's, inside the canvas. */
.document-state { display: flex; align-items: baseline; gap: 8px; min-width: 0; }
/* Save is the PAGE's control, in the page's own chrome — the code pane carries
   no Save row, exactly as Studio's does not (Studio's File menu has it). The
   command, its words and its chord are the pane registry's (`keymap::PANE`). */
#save-button { flex: none; padding: 2px 8px; border: 1px solid var(--line); border-radius: 4px; background: var(--raised); color: var(--ink); font-size: 11px; }
#save-button:hover { background: var(--hover); color: var(--ink); }
#document-name, #engine-state { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#document-name { margin: 0; font: inherit; color: var(--ink); font-weight: 550; }
.output-bar { gap: 8px; }
.output-bar > div:first-child { flex-shrink: 0; }
.output-bar .example-picker { margin-left: auto; }
.picker-popover { position: absolute; z-index: 30; right: 0; top: calc(100% + 8px); width: min(420px,calc(100vw - 24px)); padding: 12px; border: 1px solid var(--line-strong); border-radius: 6px; background: var(--panel); }
@media(max-width: 760px) {
  .output-bar { padding-inline: 8px; gap: 4px; }
  /* The zoom rail keeps its three controls on a phone and gives up the
     percentage: the readout is a comfort, the controls are the feature. The
     bar measured 401 px of content in a 390 px pane with the rail in it
     (2026-09-19); dropping the readout and tightening the rail's own padding
     and the actions' gap brings it back inside, and `paper-zoom.test.mjs`
     measures the bar at 390 px so it can never silently go back out. */
  .output-bar #zoom-level { display: none; }
  .output-bar .zoom-rail { gap: 0; padding: 1px; }
  .output-bar .zoom-rail button { padding: 3px 5px; }
  .output-bar .pane-actions { gap: 4px; }
  .output-bar .example-picker { position: static; }
  .output-bar { position: relative; }
  .output-bar .picker-popover { right: 8px; width: calc(100% - 16px); }
}
/* Narrower than about 400 px the score pane's bar cannot hold the example
   picker, the zoom rail, the view switch and the download beside one another:
   measured 2026-09-19, 373 px of content in a 360 px pane. It WRAPS rather
   than clips — every control stays reachable and the music simply starts a
   row lower — so the pane's header row stops being a fixed 44 px and sizes to
   what is in it. The four row definitions below are the four above with that
   one number opened up; nothing else about the pane changes. Since the phone's
   44 px touch rule (below) the same controls need about 455 px, so the bar
   may wrap anywhere a phone rule applies: measured 2026-09-23, 455 px of
   content in a 420 px pane before this reached past 400. */
@media(max-width: 760px) {
  .output-bar { flex-wrap: wrap; row-gap: 2px; padding-block: 4px; }
  .output-pane { grid-template-rows: auto minmax(0,1fr) auto; }
  .output-pane.paged { grid-template-rows: auto auto minmax(0,1fr) auto; }
  .output-pane:has(.layout-notes:not([hidden])) { grid-template-rows: auto auto minmax(0,1fr) auto auto; }
  :root[data-score-view="pages"] .workbench-page:not(.dictation-page) .output-pane { grid-template-rows: auto auto minmax(0,1fr) auto; }
}

.site-credit { margin-left: auto; color: var(--muted); font-size: 12px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.site-credit a { color: inherit; }


/* ── Shell continuity between Explore and About ────────────────────────────
   Both pages ship the same top bar markup and metrics. Where the browser
   supports cross-document view transitions the bar and the wordmark are the
   same box either side of the navigation, so moving between the two reads as
   one place rather than two documents. Everywhere else it is an ordinary
   navigation and nothing is lost.                                          */
@view-transition { navigation: auto; }
.topbar { view-transition-name: topbar; }
.wordmark { view-transition-name: wordmark; }
@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*) { animation: none !important; }
}

/* ── About ─────────────────────────────────────────────────────────────────
   One page, scrolled. Full-bleed bands alternate ground so each section reads
   as its own place, and the composition inside a band follows what its copy
   actually is: prose, two pillars, route cards, a roadmap, or prose beside a
   real engraving. Georgia carries the writing; the interface sans carries
   everything that is not his voice.                                        */

.about-page {
  --wrap: 1360px;
  --pad: clamp(20px, 4vw, 56px);
  --measure: 64ch;
  --serif: Georgia, "Iowan Old Style", "Palatino Linotype", serif;
  --sheet: var(--paper);
}
.about-page main { display: block; }
.band-inner { max-width: var(--wrap); margin: 0 auto; padding-inline: var(--pad); }

/* Jump bar */
/* About tabs — the idea of a member-page strip (glyph beside each label, one
   panel at a time), in this site's own quiet chip: the primary nav's weight,
   a hairline on hover, the blue rule under the one that is open. Without
   JavaScript every panel stays visible and the links still jump. */
.page-tabs { position: sticky; top: 0; z-index: 20; border-bottom: 1px solid var(--line); background: var(--ground); }
.page-tabs-links { display: flex; gap: 2px; max-width: var(--wrap); min-width: 0; margin: 0 auto; padding: 6px var(--pad); overflow-x: auto; overscroll-behavior-inline: contain; scrollbar-width: none; }
.page-tabs-links::-webkit-scrollbar { display: none; }
.page-tabs a { display: inline-flex; flex: 0 0 auto; align-items: center; gap: 8px; min-height: 34px; padding: 6px 11px; border-radius: 4px; color: var(--muted); font: 500 13px/1 var(--sans); text-decoration: none; white-space: nowrap; transition: color 120ms ease, background 120ms ease; }
.page-tabs a:hover { color: var(--ink); background: var(--hover); }
.page-tabs a.active { color: var(--ink); background: none; box-shadow: 0 2px var(--blue); border-radius: 0; }
.page-tabs a:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }
.page-tabs a svg { width: 15px; height: 15px; flex: 0 0 auto; fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; opacity: .85; }
.page-tabs a.active svg { opacity: 1; }
@media (max-width: 640px) {
  .page-tabs-links { gap: 0; padding: 4px 8px; }
  .page-tabs a { min-height: 36px; padding: 6px 9px; font-size: 12px; }
  /* The open tab carries its label on phones; the rest stay glyphs. */
  .page-tabs a:not(.active) span { display: none; }
}
.about-page .page-title { margin: 0; color: var(--ink); font: 400 clamp(34px, 4.4vw, 56px)/1.05 var(--serif); letter-spacing: -.02em; }
.about-page .tab-panel:not(.is-active) { display: none; }
html[data-about-tab] .about-page .tab-panel { display: none; }
html[data-about-tab] .about-page .tab-panel:target { display: block; }
/* A hashed link opens at the top of the page, not with the header scrolled off. */
.about-page .tab-panel { scroll-margin-top: 260px; }
@media (max-width: 640px) {
  .hub-tabs { gap: 7px; padding: 6px 10px; }
  .hub-tabs a { min-height: 36px; padding: 6px 10px; font-size: 11px; }
  /* The active destination carries its label on phones; the rest stay glyphs. */
  .hub-tabs a:not(.active) span { display: none; }
}

/* Bands */
.band { padding-block: clamp(3.25rem, 6vw, 5.5rem); scroll-margin-top: 52px; }
.band-alt { background: var(--panel); }
.band-open { padding-top: clamp(2.5rem, 5vw, 4rem); border-top: 0; }
.band-head { display: flex; align-items: baseline; gap: clamp(14px, 1.6vw, 22px); margin-top: 0; margin-bottom: clamp(1.8rem, 3vw, 2.6rem); padding-bottom: 18px; border-bottom: 1px solid var(--line); }
.band-head .mark { flex: 0 0 auto; display: grid; place-items: center; width: 38px; height: 34px; border: 1px solid var(--line); color: var(--muted); font: 500 15px/1 var(--sans); font-style: normal; }
.about-page h2 { margin: 0; color: var(--ink); font: 500 clamp(26px, 3vw, 38px)/1.1 var(--serif); letter-spacing: -.015em; }

/* Opening band */
/* The card comes first in the page so the text can wrap around it on a wide
   screen and run the full width beneath it; on a narrow one it follows the text. */
.open-grid { display: flex; flex-direction: column; gap: clamp(2rem, 4vw, 3.5rem); }
.open-grid > .idcard { order: 2; }
.about-page h1 { text-wrap: balance; margin: 0; max-width: 15ch; color: var(--ink); font: 400 clamp(38px, 5.6vw, 74px)/1.03 var(--serif); letter-spacing: -.022em; }
.about-page .lead { text-wrap: pretty; max-width: 52ch; margin: clamp(18px, 2vw, 26px) 0 0; color: var(--ink); font: 400 clamp(18px, 1.5vw, 21px)/1.6 var(--serif); }

.idcard { display: grid; grid-template-columns: 120px minmax(0, 1fr); gap: 4px 20px; align-content: start; }
.about-page .portrait { position: relative; grid-column: 1; grid-row: 1 / span 4; display: grid; place-items: center; aspect-ratio: 1; border: 1px solid var(--line); background: var(--ground); overflow: hidden; }
.band-alt .portrait { background: var(--panel); }
.portrait-staff { position: absolute; left: 0; right: 0; top: 50%; height: 41px; transform: translateY(-50%); color: var(--line); background: repeating-linear-gradient(to bottom, currentColor 0 1px, transparent 1px 10px); }
.portrait-monogram { position: relative; padding: 0 .18em; background: var(--ground); color: var(--ink); font: 400 34px/1 var(--serif); letter-spacing: .04em; }
.band-alt .portrait-monogram { background: var(--panel); }
/* Drop a portrait at src/about/brandon.jpg and it covers the plate. Absent, the
   background simply does not paint and the monogram stands in. */
.portrait-photo { position: absolute; inset: 0; background: url("/about/brandon.jpg") center/cover no-repeat; }
.rail-name, .rail-role, .rail-site, .rail-status { grid-column: 2; margin: 0; }
.rail-name { color: var(--ink); font: 600 17px/1.3 var(--sans); }
.rail-role { margin-top: 4px; color: var(--muted); font-size: 13.5px; }
.rail-site { margin-top: 10px; font-size: 13.5px; }
.rail-site a { color: var(--blue); text-decoration: none; border-bottom: 1px solid transparent; }
.rail-site a:hover { border-bottom-color: currentColor; }
.rail-status { display: inline-flex; align-items: center; gap: 8px; margin-top: 14px; padding: 5px 11px 5px 9px; border: 1px solid var(--line); border-radius: 999px; color: var(--muted); font-size: 12px; justify-self: start; }
.rail-status span { width: 6px; height: 6px; border-radius: 50%; background: var(--caution); }
.rail-credits { max-width: var(--measure); margin: 2.4rem 0 0; padding-top: 16px; border-top: 1px solid var(--line); color: var(--muted); font: 400 11.5px/1.65 var(--sans); }

/* Prose */
.prose { max-width: var(--measure); }
.prose > * + * { margin-top: 1.15em; }
.prose > h3 + * { margin-top: .75em; }
.prose > * + h3 { margin-top: 2em; }
.about-page .prose p { text-wrap: pretty; margin: 0; color: var(--ink); font: 400 clamp(17px, 1.15vw, 18.5px)/1.75 var(--serif); }
.about-page .prose h3, .pillar h3 { margin: 0; color: var(--ink); font: 600 clamp(17px, 1.4vw, 19px)/1.35 var(--serif); }
/* Space between the blocks of a tab's text. The rule above zeroes a paragraph's
   own margins, so the gaps are set here at the same weight, after it. */
.about-page .prose > * + p, .about-page .prose > * + ul, .about-page .prose > * + ol,
.about-page .prose > * + pre, .about-page .prose > * + table { margin-top: 1.1em; }
.about-page .prose > * + h3 { margin-top: 2em; }
/* A list belongs to the sentence that introduces it: close to it, and a wider
   gap before whatever comes next. */
.about-page .prose > p + ul, .about-page .prose > p + ol { margin-top: .5em; }
.about-page .prose > ul + p, .about-page .prose > ol + p { margin-top: 1.25em; }
.about-page .prose > h3 + * { margin-top: .55em; }
.about-page .list { margin: 0; padding-left: 1.15em; display: grid; gap: .6em; }
.about-page .list li { padding-left: 0; color: var(--ink); font: 400 clamp(16.5px, 1.1vw, 18px)/1.65 var(--serif); }
.about-page .list li::marker { color: var(--muted); font-size: .95em; font-variant-numeric: tabular-nums; }
.about-page ul.list li::marker { font-size: 1.05em; }
.about-page ol.list { padding-left: 1.3em; }
.about-page .hero-more { max-width: 38rem; margin-top: clamp(14px, 1.6vw, 20px); }
.about-page .hero-more ol.list { padding-left: 1.3em; gap: .35em; }
.about-page .hero-more li, .about-page .hero-more p { font: 400 clamp(17px, 1.3vw, 19.5px)/1.6 var(--serif); }
.about-page .hero-more strong { font-weight: 600; }
/* Georgia's bold is a wide face: close the letters slightly so a bold phrase
   reads as emphasis instead of a heavier block, and never synthesise a weight. */
.about-page strong { font-synthesis-weight: none; letter-spacing: -.013em; }
/* One more step in the scale: a quiet label above a group, in the UI face, so
   there is a heading size between the lead and the sentences. */
.about-page .band-open .hero-more h3 { margin: 0; color: var(--muted); font: 600 11.5px/1.3 var(--sans); letter-spacing: .09em; text-transform: uppercase; }
/* Links inside the words: the site's blue, an underline clear of the
   descenders, and a solid one on hover — the shape the route links use. */
.about-page .prose a { color: var(--blue); text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: .16em; text-decoration-color: color-mix(in srgb, var(--blue) 40%, transparent); transition: text-decoration-color 120ms ease; }
.about-page .prose a:hover, .about-page .prose a:focus-visible { text-decoration-color: var(--blue); }
/* The first tab is one screen: what ScoreScript is, then the example, without
   scrolling on a laptop. Tighter type and spacing than the tabs below it. */
.about-page .band-open { padding-top: clamp(1.6rem, 3vw, 2.4rem); }
.about-page .band-open h1 { font-size: clamp(34px, 4.2vw, 60px); }
.about-page .band-open .lead { margin-top: clamp(12px, 1.4vw, 18px); font-size: clamp(17.5px, 1.3vw, 20px); line-height: 1.55; }
.about-page .band-open .hero-more { margin-top: 12px; }
.about-page .band-open .hero-more > * + p, .about-page .band-open .hero-more > * + ul, .about-page .band-open .hero-more > * + ol { margin-top: 1.15em; }
.about-page .band-open .hero-more li, .about-page .band-open .hero-more p { font-size: clamp(16.5px, 1.2vw, 18.5px); line-height: 1.5; }
.about-page .band-open .hero-more .list { gap: 0; max-width: 72rem; }
.about-page .band-open .hero-more > p + ul, .about-page .band-open .hero-more > p + ol { margin-top: .4em; }
.about-page .band-open .hero-more > ul + p, .about-page .band-open .hero-more > ol + p { margin-top: 1.75em; }
/* The label and its list are one group: a wide gap above the label, almost
   none below it. These follow the block rules above, which they override. */
.about-page .band-open .hero-more > * + h3 { margin-top: 3.4em; }
.about-page .band-open .hero-more > h3 + p, .about-page .band-open .hero-more > h3 + ul, .about-page .band-open .hero-more > h3 + ol { margin-top: .25em; }
.about-page .band-open .example-wide { margin-top: clamp(1.5rem, 2.2vw, 2.1rem); }
.about-page .band-open .example score-script .score-script-paper { padding: clamp(10px, 1.1vw, 16px); }
.about-page .band-open .example figcaption { margin-top: 10px; max-width: none; }
.about-page .band-open .example figcaption, .about-page .band-open .rail-role { letter-spacing: .005em; }
@media (min-width: 1000px) { .about-page .band-open .open-grid > .idcard { margin-bottom: 6px; } }
.about-page code { padding: .12em .4em; border: 1px solid var(--line); border-radius: 4px; background: var(--code); color: var(--code-ink); font: 450 .84em/1.2 var(--mono); letter-spacing: -.01em; }
.about-page .aside { padding-left: 20px; border-left: 2px solid var(--line); color: var(--muted); }
.about-page .warn { display: flex; gap: .7em; color: var(--caution); }
.about-page .warn::before { content: ""; flex: 0 0 auto; width: 7px; height: 7px; margin-top: .62em; border-radius: 50%; background: currentColor; }
.prose-after { margin-top: clamp(1.8rem, 3vw, 2.4rem); }
/* Code and comparison tables written into a tab's Markdown. */
.about-page .prose pre { margin: 0; padding: 16px 18px; overflow-x: auto; border: 1px solid var(--line); background: var(--code); }
.about-page .prose pre code { padding: 0; border: 0; background: none; color: var(--code-ink); font: 400 12.5px/1.7 var(--mono); white-space: pre; }
.about-page .prose table { display: block; max-width: 100%; overflow-x: auto; border-collapse: collapse; font: 400 15px/1.5 var(--sans); }
.about-page .prose th, .about-page .prose td { padding: 10px 14px 10px 0; border-bottom: 1px solid var(--line); color: var(--ink); text-align: left; vertical-align: top; }
.about-page .prose th { color: var(--muted); font-weight: 600; }
.about-page .prose td:first-child { color: var(--muted); white-space: nowrap; }

/* Two pillars */
/* A walk-through: one step per row, its text beside the music it makes. The
   number comes from a counter, so the file stays plain prose. */
/* The walk-through carries its own rules: one above the first step and one
   between each pair, so the tab's heading needs none of its own. */
.steps { counter-reset: step; list-style: none; margin: 0; padding: 0; display: grid; gap: 1px; background: var(--line); border-top: 1px solid var(--line); }
.band:has(.steps-body) .band-head { margin-bottom: clamp(1.1rem, 1.8vw, 1.6rem); padding-bottom: 0; border-bottom: 0; }
.step { display: grid; gap: 0; align-items: stretch; padding: 0; background: var(--ground); }
.step-text { padding: clamp(11px, 1vw, 15px) clamp(18px, 1.6vw, 26px) clamp(11px, 1vw, 15px) 0; align-self: start; }
.step-wide .step-text { padding-right: clamp(18px, 1.6vw, 26px); }
.about-page .step-text h3 { margin: 0 0 .4em; color: var(--ink); font: 600 clamp(15px, 1.05vw, 16.5px)/1.3 var(--serif); }
.about-page .step-text h3::before { counter-increment: step; content: counter(step, decimal-leading-zero) "  "; color: var(--muted); font: 500 11px/1 var(--sans); letter-spacing: .12em; }
.about-page .step-text p { margin: 0; color: var(--muted); font: 400 clamp(14px, .95vw, 15.5px)/1.6 var(--sans); }
.about-page .step-text .fine { margin-top: .7em; font-size: 12.5px; letter-spacing: .01em; opacity: .85; }
.about-page .step-text .fine code { padding: .1em .32em; font-size: .92em; }
.about-page .step-text > * + p { margin-top: .8em; }
.about-page .step-text table { font-size: 14px; }
/* The step row is the only container: the element's own frame goes flush, and the
   engraving keeps its drawn size instead of stretching to the column. */
.step-example { margin: 0; --score-script-font-size: 13.5px; --score-script-line-height: 1.65; --score-script-code-background: transparent; --score-script-code-color: var(--ink); }
.step-example .score-script-source { padding: 9px 0 clamp(11px, 1vw, 15px); border: 0; }
.step-example .score-script-input { min-height: 0; }
.step-example { display: grid; grid-template-rows: minmax(0, 1fr) auto; }
.step-example, .step-example score-script, .step-example .score-script-frame { height: 100%; }
/* The engraving takes the room the row has; its source stays on the floor of the
   same container, so the text under one example lines up with the next. */
.step-example .score-script-frame { grid-template-rows: minmax(0, 1fr) auto; }
.step-example .score-script-paper { display: grid; align-content: center; padding: 0; border: 0; }
.step-example .score-script-paper img, .step-example .score-script-paper svg { display: block; width: 100%; height: auto; }
.step-split .step-example { grid-column: 1 / -1; }
.step-split .score-script-frame { grid-template-rows: auto; }
.step-example figcaption { margin-top: .6em; color: var(--muted); font: 400 12.5px/1.6 var(--sans); }
.lead-wide { max-width: 78ch; }
.about-page .steps-body .lead-wide p { font-size: clamp(17.5px, 1.25vw, 20px); }
@media (min-width: 900px) {
  .step { grid-template-columns: minmax(0, 1fr) 700px; }
  .step-split { grid-template-columns: minmax(0, 1fr) 700px; }
  .step-split .score-script-frame { grid-template-columns: minmax(0, 1fr) 700px; }
  .step-wide { grid-template-columns: minmax(0, 1fr); }
}

/* Below an example, paragraphs keep their measure but a list or table may use the width. */
.about-page .band .prose-after { max-width: none; }
.about-page .band .prose-after > p, .about-page .band .prose-after > h3 { max-width: var(--measure); }
.pillars { display: grid; gap: clamp(1.6rem, 3vw, 3rem); }
.pillar p { margin: 0; max-width: 44ch; color: var(--ink); font: 400 clamp(17px, 1.1vw, 18px)/1.72 var(--serif); }
.pillar strong { display: block; margin-bottom: .5em; color: var(--ink); font-weight: 600; }

/* Prose beside a real engraving */
.two-up { display: grid; gap: clamp(2rem, 4vw, 3.5rem); align-items: start; }
.two-up .prose { max-width: 38rem; }

.example { margin: 0; }
/* The examples are the `scorescript` package's <score-script> element; the
   site only themes it, through the element's own custom properties. */
.example score-script {
  --score-script-font: var(--mono);
  --score-script-code-background: var(--code);
  --score-script-code-color: var(--code-ink);
  --score-script-paper: var(--sheet);
  --score-script-border: var(--line);
  --score-script-accent: var(--blue);
  --score-script-problem: var(--red);
}
.example figcaption { margin-top: 12px; max-width: 58ch; color: var(--muted); font: 400 12.5px/1.6 var(--sans); }
.example-wide { margin-top: clamp(2.5rem, 4vw, 3.5rem); }

/* Get started */
.routes { display: grid; gap: 1px; margin: 0; padding: 0; list-style: none; background: var(--line); border: 1px solid var(--line); }
.route { display: flex; flex-direction: column; gap: 10px; padding: clamp(18px, 1.8vw, 24px); background: var(--ground); }
.band-alt .route { background: var(--panel); }
.route-status { display: inline-flex; align-items: center; gap: 7px; margin: 0; color: var(--muted); font: 500 11.5px/1 var(--sans); }
.route-status i { width: 7px; height: 7px; border-radius: 50%; background: currentColor; }
.route-live .route-status { color: var(--green); }
.route-prealpha .route-status { color: var(--caution); }
.route-planned .route-status { color: var(--muted); }
.route-planned { opacity: .82; }
.route-title { margin: 0; color: var(--ink); font: 600 clamp(16px, 1.2vw, 18px)/1.3 var(--serif); }
.route-line { margin: 0; flex: 1; color: var(--muted); font: 400 14px/1.6 var(--sans); }
.route-line a, .route-cta { color: var(--blue); text-decoration: none; border-bottom: 1px solid transparent; }
.route-cta { align-self: start; font: 500 13px/1 var(--sans); padding-bottom: 3px; border-bottom-color: color-mix(in srgb, var(--blue) 45%, transparent); }
.route-cta:hover, .route-line a:hover { border-bottom-color: currentColor; }

/* Roadmap */
.roadmap { display: grid; gap: clamp(1.8rem, 3vw, 2.6rem); margin-top: clamp(2rem, 3vw, 2.8rem); }
.rm-col h3 { display: flex; align-items: center; gap: 9px; margin: 0 0 14px; padding-bottom: 12px; border-bottom: 1px solid var(--line); color: var(--ink); font: 600 13px/1 var(--sans); letter-spacing: .01em; }
.rm-col h3 span { margin-left: auto; color: var(--muted); font-weight: 400; font-variant-numeric: tabular-nums; }
.rm-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--muted); }
.rm-landed .rm-dot { background: var(--green); }
.rm-building .rm-dot { background: var(--caution); }
.rm-planned .rm-dot { background: var(--muted); }
.rm-col ul { margin: 0; padding: 0; list-style: none; display: grid; gap: 2px; }
.rm-col li { padding: 11px 0; border-bottom: 1px solid var(--line); }
.rm-col li:last-child { border-bottom: 0; }
.rm-item { margin: 0; color: var(--ink); font: 500 14.5px/1.4 var(--sans); }
.rm-line { margin: 4px 0 0; color: var(--muted); font: 400 13px/1.55 var(--sans); }
.rm-source { display: inline-block; margin-top: 6px; color: var(--muted); font: 400 11.5px/1.5 var(--mono); overflow-wrap: anywhere; }
a.rm-source { color: var(--blue); text-decoration: none; border-bottom: 1px solid transparent; }
a.rm-source:hover { border-bottom-color: currentColor; }
.rm-planned li { opacity: .85; }

/* Footer */
.about-footer { padding-block: clamp(2.4rem, 4vw, 3.4rem) clamp(3rem, 5vw, 4.5rem); border-top: 1px solid var(--line); }
.about-footer .band-inner { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 18px 40px; }
.about-page .about-links { display: flex; flex-wrap: wrap; gap: 14px 30px; margin: 0; font-size: 14px; }
.about-page .about-links a { padding-bottom: 2px; border-bottom: 1px solid transparent; color: var(--blue); text-decoration: none; }
.about-page .about-links a:hover { border-bottom-color: currentColor; }
.about-footer .about-note { margin-left: auto; text-align: right; }
.about-footer .about-note p + p { margin-top: .55em; }
.about-footer p { margin: 0; color: var(--muted); font: 400 13.5px/1.7 var(--sans); }
.about-footer a { color: inherit; }

@media (min-width: 720px) {
  .routes { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .pillars { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .roadmap { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .idcard { grid-template-columns: 132px minmax(0, 1fr); }
  .portrait-monogram { font-size: 38px; }
}

@media (min-width: 1000px) {
  /* Three sentences read as a row, not a stack. `.list` is a grid, and a grid
     ignores columns, so display is set back to block here. */
  .about-page .band .prose-after > ul, .about-page .step-wide .step-text > ul { display: block; columns: 3; column-gap: clamp(1.8rem, 3vw, 3rem); }
  .about-page .band .prose-after > ul > li, .about-page .step-wide .step-text > ul > li { break-inside: avoid; margin: 0 0 .6em; }
  .open-grid { display: flow-root; }
  .open-grid > .idcard { float: right; width: 340px; margin: 0 0 clamp(1.5rem, 3vw, 2.5rem) clamp(2rem, 4vw, 3.5rem); }
  .about-page .open-grid .lead, .about-page .open-grid .hero-more { max-width: none; }
  .two-up { grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr); }
  .routes { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

@media (min-width: 1200px) {
  .routes { grid-template-columns: repeat(5, minmax(0, 1fr)); }
  .band-head { align-items: center; }
}

@media (prefers-reduced-motion: reduce) {
  .about-page * { transition: none !important; }
  html { scroll-behavior: auto; }
}


/* ── Melodic dictation ────────────────────────────────────────────────────────
   The page IS the workbench: the same topbar, the same two panes, the same
   splitter, the same paper stage, the same transport row and the same footer
   as Explore, from the same tokens. Everything below is the marking — the only
   thing this page has that Explore does not — plus the two lines that seat the
   four dictation controls in the shared transport. */

/* The marking drawer is the inspector drawer: same ground, same border, same
   scroll. The teaching sentence stands at its top; Check writes under it. */
.how { margin: 0 0 12px; color: var(--muted); font-size: 13px; line-height: 1.7; }
.how code, .bar-row code, .revealed code { padding: 1px 5px; border-radius: 3px; background: var(--code); color: var(--code-ink); font: 13px var(--mono); }

.verdict { margin: 0 0 12px; padding: 12px 14px; border: 1px solid var(--line); border-radius: 4px; font-size: 15px; }
.verdict.ok { border-color: color-mix(in srgb, var(--green) 55%, var(--line)); background: color-mix(in srgb, var(--green) 10%, transparent); color: var(--green); }
.verdict.miss { border-color: color-mix(in srgb, var(--red) 55%, var(--line)); background: color-mix(in srgb, var(--red) 10%, transparent); color: var(--red); }
.bar-rows { margin: 0; padding: 0; list-style: none; display: grid; gap: 10px; }
.bar-row { padding: 12px 14px; border: 1px solid var(--line); border-radius: 4px; background: var(--panel); font-size: 14.5px; }
.bar-row[data-state="ok"] { color: var(--muted); }
.bar-row .tick { color: var(--green); }
.miss-line { margin: 0 0 10px; }
.hint { margin: 0 0 10px; display: grid; gap: 4px; }
.hint div { display: flex; flex-wrap: wrap; gap: 8px; align-items: baseline; }
.hint dt { min-width: 74px; color: var(--muted); font-size: 12px; }
.hint dd { margin: 0; }
.show-me { min-height: 44px; padding: 8px 14px; border: 1px solid var(--line); border-radius: 4px; background: var(--raised); color: var(--ink); font-size: 14px; }
.revealed { margin: 10px 0 0; font-size: 14.5px; }

/* Explore's transport spends its wide column on the timeline; this one has four
   controls and no scrubbing, so the readouts are pushed to the far end instead. */
/* Start over stands at the far end with a REAL GAP, beside the bar readout:
   flex with `margin-left: auto` on the button, because a grid's 1fr track
   collapses the moment the readout in it is hidden and the gap silently
   vanishes with it (measured 2026-09-15). */
.transport.dictation-transport { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }
.transport.dictation-transport #reset { order: 5; margin-left: auto; }
.transport.dictation-transport #bar-readout { order: 6; }
.transport.dictation-transport #sound-readout { overflow: hidden; text-overflow: ellipsis; max-width: 34ch; }

/* Touch targets are a requirement on the SHARED transport, not a reason for a
   second set of buttons: at phone width every transport button is 44px, here
   and on Explore. */
@media (max-width: 760px) {
  .transport button { min-height: 44px; }
  .transport.dictation-transport { grid-template-columns: repeat(4, minmax(0, auto)); }
}
/* **44 px is the phone rule for EVERY control, not just the transport** (UI-W
   audit W-14, measured 2026-09-23 at 390 px: nav links 30 px tall, the zoom
   "−" 17 × 26, "Open in Explore" 18 px, the theme button 32). Each control
   keeps its own look; only its box grows, and the text sits centred in it. The
   top bar's own rows are what the taller links fill, so the bar's padding and
   row gap give back the room they add and its height barely moves — how the
   header is laid out on a phone is a separate question (audit Q2). Inline
   prose links are not controls and are left alone. */
@media (max-width: 760px) {
  .topbar { padding-block: 2px; row-gap: 0; }
  .wordmark, .primary-nav a, .secondary-nav a, .app-link, .skip-link,
  .catalog-list a, .about-page .about-links a,
  .site-credit a, #save-button, .example-picker summary {
    display: inline-flex; align-items: center; min-height: 44px;
  }
  .theme-toggle { width: 44px; height: 44px; }
  .page-tabs a { min-width: 44px; min-height: 44px; justify-content: center; }
  .zoom-rail button, .view-switch button, .pane-actions button, #restart-button { min-width: 44px; min-height: 44px; }
  #example-search, #catalog-search, #etude-search { height: 44px; }
  .source-line #bars-line { min-height: 44px; }
  #timeline { height: 44px; }
  .site-credit a, #save-button { min-width: 44px; justify-content: center; vertical-align: middle; }
}

/* **390 px is a requirement, not an aspiration.** The workbench footer is a
   nowrap flex row and the credit sentence at its end set the whole grid's
   min-content width, so the pane measured 929 px inside a 390 px viewport and
   Find and Actions sat off the right edge (parity audit C16). The footer wraps
   at phone width and its row is sized by its content; nothing else in the
   workbench claims a minimum wider than the screen. These live AFTER
   `.site-credit`'s own rule, which is the one they override. */
@media(max-width: 760px) {
  .playhouse { grid-template-rows: auto auto; }
  .workbench-footer { flex-wrap: wrap; row-gap: 2px; padding-block: 4px; }
  .site-credit { flex: 1 1 100%; margin-left: 0; white-space: normal; }
  #score-count { flex: 0 1 auto; }
}


/* ── Melodic dictation v0.2 ───────────────────────────────────────────────────
   The answer is the page (ruling 1), the etudes are a list you climb (ruling 2)
   and the scale degrees are a second way in (ruling 5). Every rule below is
   built from this file's own tokens — no framework, no palette of its own —
   and each new family names, in a comment, the existing thing it was cut from.

   The workstation is still Explore's: `.workbench` is still the three-column
   grid with `#score-splitter` between the columns and `--source-width` on it.
   What changed on this page is WHICH pane is on the left. The rail is clamped
   rather than free: `--source-width` is shared with Explore, where 44 % is a
   source pane, and 44 % of 1280 px is not a list of eight etudes. Dragging
   still works, inside the band a rail is worth having. */
.dictation-page .workbench { grid-template-columns: minmax(0, clamp(240px, var(--source-width, 26%), 420px)) 6px minmax(0, 1fr); }
/* THE VERDICT ALWAYS HAS A FLOOR. `.inspector` scrolls, so its automatic
   minimum is zero and the row collapsed under the paper's `1fr`: measured
   2026-09-14 at 1280x900 with the source panel open, "3 of 4 bars match" was
   cut in half by the panel below it. 120 px always holds the verdict line and
   its first bar; past that the drawer scrolls on its own, and the marks sit
   ABOVE the primer so what scrolls away is the sentence a student stops
   needing. Rows: bar, paper, degree pad, transport, result, source.
   The result row is a plain `auto`, and that is only correct because the
   drawer below no longer scrolls: a scrolling box has an automatic minimum of
   ZERO, so the row collapsed under the paper's `1fr` and the marking was cut
   (measured 2026-09-14 at 1280x900 — "3 of 4 bars match" cut in half). A
   floor of its own would not have fixed it either: once the rows overflow,
   free space is negative and an `auto` MAXIMUM never grows past its base, so
   the marking would paint over the source panel instead. The paper's floor
   must also stay equal to `.paper-stage`'s own `min-height`, or the stage
   paints over the degree pad the same way. */
/* The sheet's row is pinned to its CONTENT. A fluid-width SVG has a tiny
   min-content height, so the moment the marking overflowed the column the
   grid squeezed the paper to a 50px band with the staff cut off (measured
   2026-09-15, after every finished check). `max-content` as the row's floor
   means the paper never gives an inch — the column scrolls instead, which is
   what `overflow-y` is for. */
/* Each control row hugs its content. Explore's saved page view must never
   substitute its four-row layout here: that stretched the transport and
   source hundreds of pixels apart on BDLaptop (2026-10-01). */
.dictation-page .output-pane { grid-template-rows: max-content max-content max-content max-content minmax(auto, 1fr); overflow-y: auto; }
/* THE PAPER HUGS THE MUSIC. Explore's stage holds a whole score and gives it a
   260 px sheet to live on; four bars of dictation on a 1080 px screen made
   that a blank white slab with the answer stranded at the top of it and the
   keyboard pushed 700 px down the page (Brandon, 2026-09-14: "I can't even
   fathom why this is stuck the way it is at the very top"). Here the sheet is
   exactly as tall as the bars on it, and the buttons sit under the music. */
.dictation-page .paper-stage { min-height: 0; padding: 14px 18px; display: grid; align-content: start; }
.dictation-page .paper { min-height: 0; width: min(1080px, 100%); justify-self: center; padding: 22px 28px; }

.dictation-page .source-pane { border-right: 0; }

/* The rail. Cut from `.output-pane` — same `.pane-bar` header, same ground as
   `.wiki-pane`, same scroll. */
.etude-rail { min-width: 0; min-height: 0; display: grid; grid-template-rows: var(--bar-h) minmax(0,1fr); background: var(--panel); border-right: 1px solid var(--line); }
.etude-rail .pane-bar { background: var(--panel); }
/* THE CHAPTER MENU. The header's own words are the button — `.example-picker`'s
   summary, opening the `.picker-popover` the site already has, anchored to the
   left because the rail is on the left. */
.etude-rail .pane-bar > div:first-child { flex: 1 1 auto; }
.etude-rail { container-type: inline-size; }
.etude-rail #ladder-meta { flex: none; white-space: nowrap; }
.chapter-picker { min-width: 0; flex: 0 1 auto; }
/* The button's words come first: a narrow rail drops the count, which every
   row's badge already says, before it cuts the chapter's name. */
@container (max-width: 400px) { .etude-rail #ladder-meta { display: none; } }
/* A BUTTON YOU CAN SEE IS ONE (Brandon, 2026-09-16: "make the top-left area
   look more like an engageable button, probably with an icon"): the transport's
   own dress — bordered, raised, rounded — with a book for chapters and a
   chevron that turns while the menu is open. */
.chapter-picker summary { gap: 8px; min-height: 38px; padding: 5px 10px 5px 9px; border: 1px solid color-mix(in srgb, var(--blue) 40%, var(--line)); border-radius: 8px; background: var(--raised); color: var(--ink); cursor: pointer; }
.chapter-picker summary:hover { border-color: var(--blue); background: color-mix(in srgb, var(--blue) 10%, var(--raised)); color: var(--ink); }
.chapter-picker[open] > summary { border-color: var(--blue); background: color-mix(in srgb, var(--blue) 16%, var(--raised)); }
.chapter-picker summary:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }
.chapter-picker summary h2 { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--ink); font-size: 13.5px; font-weight: 600; }
.chapter-icon, .chapter-chevron { flex: none; width: 17px; height: 17px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.chapter-icon { color: var(--blue); }
.chapter-chevron { width: 15px; height: 15px; color: var(--muted); transition: transform 120ms ease; }
.chapter-picker[open] .chapter-chevron { transform: rotate(180deg); }
.chapter-picker[data-menu="false"] summary { pointer-events: none; border-color: transparent; background: none; }
.chapter-picker[data-menu="false"] .chapter-chevron { display: none; }
.chapter-picker .picker-popover { left: 0; right: auto; width: min(400px, calc(100vw - 24px)); padding: 6px; }
.chapter-picker .picker-results { margin-top: 0; max-height: min(640px, 78vh); }
.chapter-option { border-radius: 5px; }
.chapter-option-body { min-width: 0; display: grid; gap: 2px; }
.chapter-option-body strong { font-weight: 550; }
.chapter-option-body span { color: var(--muted); font-size: 11.5px; }
.chapter-option[data-status="passed"] small { color: var(--green); }
.chapter-option small { white-space: nowrap; line-height: 1.5; }
.picker-group-head { margin: 12px 4px 6px; color: var(--muted); font: 500 11.5px/1.2 var(--sans); text-transform: uppercase; letter-spacing: .05em; }
[data-picker-group]:first-child .picker-group-head { margin-top: 2px; }
.lesson-rows { min-height: 0; margin: 0; padding: 8px; list-style: none; overflow-y: auto; display: grid; gap: 6px; align-content: start; }
#forget-progress { display: block; margin-top: 8px; min-height: 30px; padding: 4px 8px; border: 1px solid var(--line); border-radius: 4px; background: none; color: var(--muted); font-size: 11px; }
#forget-progress:hover { color: var(--ink); background: var(--hover); }

/* ONE ROW OF THE LIST. The anatomy is the Music Trainer's LessonListingRow —
   round badge, title, subtitle, progress bar, stat stamp, status pill,
   chevron — dressed in `.picker-results button`'s ground and hover, which is
   the row this replaces. The two-selector specificity is deliberate: the same
   markup is mounted inside `.picker-results` on a phone, where that rule would
   otherwise win. */
.lesson-item { display: grid; }
/* A TAB YOU PRESS. The accent down the left is what says "this one is open in
   the answer beside you"; the row is a card so eight of them read as a stack
   to choose from rather than a list to read. */
.lesson-rows .lesson-row { position: relative; display: grid; grid-template-columns: 26px minmax(0,1fr) auto auto 14px; align-items: center; gap: 10px; width: 100%; min-height: 56px; padding: 9px 11px 9px 13px; border: 1px solid var(--line); border-radius: 7px; background: var(--raised); color: var(--ink); text-align: left; font-size: 13px; }
.lesson-rows .lesson-row::before { content: ""; position: absolute; left: 0; top: 9px; bottom: 9px; width: 3px; border-radius: 0 3px 3px 0; background: transparent; }
.lesson-rows .lesson-row:hover { background: var(--hover); border-color: color-mix(in srgb, var(--blue) 35%, var(--line)); }
.lesson-rows .lesson-row[aria-current="true"] { border-color: color-mix(in srgb, var(--blue) 60%, var(--line)); background: color-mix(in srgb, var(--blue) 14%, var(--raised)); }
.lesson-rows .lesson-row[aria-current="true"]::before { background: var(--blue); }
.lesson-rows .lesson-row[data-status="passed"][aria-current="true"]::before { background: var(--green); }
.lesson-rows .lesson-row[data-status="locked"] { color: var(--muted); background: none; }
.lesson-rows .lesson-row[data-status="locked"]:hover { background: var(--hover); color: var(--ink); }
/* The etude's fingerprint, drawn by the engine: clef, key signature, meter. */
.lesson-row-preview { display: grid; place-items: center; padding: 4px 8px; border-radius: 4px; background: var(--paper); color: var(--paper-ink); }
.lesson-row-preview .glyph { width: auto; display: block; }
.lesson-row[data-status="locked"] .lesson-row-preview { opacity: .5; }
.lesson-row-number { display: grid; place-items: center; width: 26px; height: 26px; border: 1px solid var(--line); border-radius: 50%; color: var(--muted); font: 500 12px/1 var(--sans); font-variant-numeric: tabular-nums; }
.lesson-row-number svg { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.lesson-row[data-status="passed"] .lesson-row-number { border-color: color-mix(in srgb, var(--green) 60%, var(--line)); color: var(--green); }
.lesson-row[data-status="in progress"] .lesson-row-number { border-color: color-mix(in srgb, var(--blue) 60%, var(--line)); color: var(--blue); }
.lesson-row-body { min-width: 0; display: grid; gap: 3px; }
.lesson-row-body strong { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 550; }
.lesson-row-meta { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--muted); font-size: 11.5px; }
.lesson-row-progress { height: 3px; margin-top: 2px; border-radius: 2px; background: var(--raised); overflow: hidden; }
.lesson-row-progress > div { height: 100%; background: var(--blue); }
.lesson-row[data-status="passed"] .lesson-row-progress > div { background: var(--green); }
.lesson-row-stamp { display: grid; justify-items: end; gap: 1px; color: var(--muted); }
.lesson-row-stamp-value { font: 550 12px/1 var(--sans); font-variant-numeric: tabular-nums; color: var(--ink); }
.lesson-row-stamp-label { font-size: 9.5px; text-transform: uppercase; letter-spacing: .06em; }
.lesson-row-chevron { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; color: var(--muted); }
/* Locking is a ladder and never a trap: the door out of it is always drawn. */

/* A SECOND WAY IN. Cut from `.transport button` — same ground, same hover,
   same 44 px rule — because the site has no button pad and this is the nearest
   control it does have. Every press spends an engine action; nothing here
   writes text. */
/* THE KEYBOARD: letter names and the engine's own note shapes, nothing to read.
   The letters are the notes of THIS key (F major reads F G A B♭ C D E), the
   lengths are `render_svg` on one note apiece, and the two arrows move between
   them. Every key is 44 px, here and on a phone. */
.degree-pad { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 20px; padding: 12px 16px; border-top: 1px solid var(--line); background: var(--panel); }
.pad-group { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.pad-marks, .pad-lengths, .pad-moves { padding-left: 16px; border-left: 1px solid var(--line); }
.pad-key { position: relative; display: grid; place-items: center; min-width: 58px; min-height: 58px; padding: 6px 10px; border: 1px solid var(--line); border-radius: 7px; background: var(--raised); color: var(--ink); }
.pad-key:hover { background: var(--hover); border-color: var(--blue); }
.pad-key:active { background: color-mix(in srgb, var(--blue) 22%, transparent); transform: translateY(1px); }
.pad-letter { font: 600 23px/1 var(--serif); }
.pad-shortcut { position: absolute; top: 4px; left: 6px; color: var(--muted); font: 500 10.5px/1 var(--sans); font-variant-numeric: tabular-nums; }
.pad-rest { border-style: solid; }
.pad-rest .pad-letter, .pad-move .pad-letter { font: 500 14px/1 var(--sans); color: var(--muted); }
.pad-move .pad-letter { font-size: 26px; }
.pad-rung { min-width: 62px; }
.pad-accidental .pad-letter { font-size: 26px; }
.pad-dot .pad-letter { font-size: 26px; }
.pad-rung .glyph { width: auto; display: block; margin-top: 3px; }
.pad-rung .pad-letter { font-family: var(--mono); }
.pad-rest { border-style: dashed; }
/* The pad's reserved room before the engine answers (`dictation/index.html`):
   the wrapper takes no box of its own, so its groups wrap exactly as the real
   ones will, and nothing in it can be seen, pressed or read. */
.pad-reserve { display: contents; }
.pad-reserve > * { visibility: hidden; pointer-events: none; }

/* THE SOURCE, BELOW THE ANSWER AND COLLAPSED. Cut from `.inspector` (the
   drawer under the paper) for its ground and border, and from `.pane-bar` for
   its header row. The reveal is CLIPPED rather than removed: the pane's canvas
   keeps a real box whether it is on screen or not, because the degree buttons
   go on spending entry actions into it while it is shut. */
.answer-source { border-top: 1px solid var(--line); background: var(--panel); }
.source-summary { display: flex; align-items: center; gap: 12px; min-height: 40px; padding: 4px 14px; }
#source-toggle { display: flex; align-items: center; gap: 7px; min-height: 32px; padding: 4px 6px; border: 0; border-radius: 4px; background: none; color: var(--muted); font-size: 12px; white-space: nowrap; }
#source-toggle:hover { color: var(--ink); background: var(--hover); }
#source-toggle .chev { display: inline-block; transition: transform 120ms ease; }
.answer-source[data-open="true"] #source-toggle { color: var(--ink); }
.answer-source[data-open="true"] #source-toggle .chev { transform: rotate(90deg); }
.source-bars { min-width: 0; display: flex; flex-wrap: nowrap; gap: 10px; overflow-x: auto; scrollbar-width: none; }
.source-bars::-webkit-scrollbar { display: none; }
.bar-chip { display: flex; align-items: baseline; gap: 5px; white-space: nowrap; color: var(--muted); font-size: 11px; }
.bar-chip b { font-weight: 550; }
.bar-chip code { padding: 1px 5px; border-radius: 3px; background: var(--code); color: var(--code-ink); font: 12px var(--mono); }
.bar-chip.empty { color: var(--muted); }
.whole-source { display: flex; align-items: center; gap: 6px; margin-left: auto; color: var(--muted); font-size: 11px; white-space: nowrap; }
.whole-source input { accent-color: var(--blue); }
.source-reveal { height: 0; overflow: hidden; }
.answer-source[data-open="true"] .source-reveal { height: auto; }
.dictation-page .source-workspace { height: clamp(200px, 30dvh, 300px); border-top: 1px solid var(--line); }
.answer-source[data-whole="true"] .source-workspace { height: clamp(260px, 46dvh, 460px); }
.answer-source:not([data-open="true"]) .source-reveal { visibility: hidden; }

/* THE MARK. The verdict, the bar rows and the `.hint` pairs are the ones that
   shipped; the score line, the readings and the coaching are new, in the same
   card shape as `.bar-row`. */
.grade-line { display: flex; align-items: baseline; gap: 12px; margin: 0 0 14px; padding: 10px 14px; border: 1px solid var(--line); border-radius: 4px; background: var(--ground); }
.grade-number { color: var(--ink); font: 550 24px/1 var(--sans); font-variant-numeric: tabular-nums; }
.grade-why { color: var(--muted); font-size: 11.5px; line-height: 1.6; }
.marks-head { margin: 16px 0 8px; color: var(--muted); font: 500 12px/1 var(--sans); text-transform: uppercase; letter-spacing: .06em; }
.dimensions, .coaching { margin: 0; padding: 0; list-style: none; display: grid; gap: 8px; }
.dimension { padding: 9px 12px; border: 1px solid var(--line); border-left-width: 3px; border-radius: 4px; background: var(--panel); }
.dimension[data-verdict="correct"] { border-left-color: var(--green); }
.dimension[data-verdict="near"] { border-left-color: var(--caution); }
.dimension[data-verdict="missed"] { border-left-color: var(--red); }
.dimension-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 10px; margin: 0 0 4px; font-size: 13.5px; }
.dimension-verdict { padding: 2px 7px; border-radius: 999px; background: var(--raised); color: var(--muted); font-size: 10.5px; text-transform: uppercase; letter-spacing: .05em; }
.dimension[data-verdict="correct"] .dimension-verdict { color: var(--green); }
.dimension[data-verdict="near"] .dimension-verdict { color: var(--caution); }
.dimension[data-verdict="missed"] .dimension-verdict { color: var(--red); }
.dimension-score { margin-left: auto; color: var(--ink); font-variant-numeric: tabular-nums; font-size: 13px; }
.dimension-weight { color: var(--muted); font-size: 11px; font-variant-numeric: tabular-nums; }
.dimension-why { margin: 0; color: var(--muted); font-size: 12.5px; }
.coach { padding: 11px 13px; border: 1px solid var(--line); border-radius: 4px; background: var(--panel); }
.coach-q { margin: 0 0 6px; font-size: 14.5px; }
.coach-label { display: inline-block; margin-right: 8px; padding: 2px 7px; border-radius: 999px; background: var(--raised); color: var(--muted); font-size: 10.5px; text-transform: uppercase; letter-spacing: .05em; }
.coach-listen { margin: 0 0 10px; color: var(--muted); font-size: 12.5px; line-height: 1.6; }

/* THE PHONE. The rail becomes today's Etudes menu — the same rows, in the
   `.picker-popover` the site already had — and the workstation stops being a
   fixed-height viewport so the answer, the buttons, the mark and the source
   can be scrolled to instead of squeezed. Every control stays 44 px. */
#etude-picker { display: none; }
@media (max-width: 900px) {
  .dictation-page .etude-rail { display: none; }
  #etude-picker { display: block; }
  .dictation-page .workbench { grid-template-columns: minmax(0,1fr); }
  .dictation-page #score-splitter { display: none; }
  .dictation-page .output-pane { height: auto; min-height: 0; overflow: visible; grid-template-rows: max-content max-content max-content max-content auto; }
  .dictation-page .explore-main { height: auto; min-height: 0; }
  .dictation-page .playhouse { height: auto; grid-template-rows: auto auto; }
  .dictation-page .picker-popover { width: min(520px, calc(100vw - 24px)); }
  .dictation-page .picker-results { max-height: min(60vh, 460px); }
  .dictation-page .lesson-rows { padding: 0; }
}
@media (max-width: 760px) {
  .dictation-page .source-pane { height: auto; border-bottom: 0; }
  .dictation-page .source-workspace { height: clamp(300px, 46dvh, 420px); }
  .answer-source[data-whole="true"] .source-workspace { height: clamp(340px, 60dvh, 520px); }
  .source-summary { flex-wrap: wrap; row-gap: 2px; padding-inline: 12px; }
  .source-bars { order: 3; flex: 1 1 100%; }
  .degree-pad { padding-inline: 12px; gap: 6px 10px; }
  .grade-line { flex-wrap: wrap; row-gap: 6px; }
  #replay-readout { display: none; }
  .dictation-page .transport.dictation-transport { grid-template-columns: repeat(4, minmax(0, auto)); }
}
/* The answer column scrolls as a whole when the mark and the source panel are
   both open; its own header must not scroll away with them — and neither may
   the transport: the marking it scrolls to says "listen again to m3", so Hear
   it, Hear mine and Check have to be under the reader's thumb while they read
   it, not a scroll away above. The mark parks itself below both. */
.dictation-page .score-dock { position: sticky; top: 0; z-index: 5; background: var(--stage); border-bottom: 1px solid var(--line); }
.dictation-page .score-dock .paper-stage { max-height: calc(45dvh - var(--score-heading-height, var(--bar-h))); overflow: auto; }
.dictation-page .output-bar { position: static; min-height: var(--bar-h); height: auto; flex-wrap: wrap; }
/* Keep the native notation visible while reading feedback. The transport's
   offset follows the measured dock, including a wrapped two-system score. */
.dictation-page .transport.dictation-transport { position: sticky; top: var(--score-dock-height, 0px); z-index: 4; min-height: 62px; }
.dictation-tabs { display: flex; gap: 4px; flex: 0 0 100%; }
.dictation-tabs button { min-height: 44px; padding: 8px 15px; border: 1px solid var(--line); border-radius: 6px; color: var(--muted); background: var(--panel); font-weight: 600; }
.dictation-tabs button[aria-selected="true"] { color: var(--ink); border-color: var(--blue); background: var(--raised); box-shadow: inset 0 -3px var(--blue); }
.dictation-tabs button:focus-visible, .voice-picker button:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }
.dictation-tabs span { margin-left: 6px; font-size: 12px; color: var(--muted); }
.dictation-page .output-bar { flex-wrap: wrap; height: auto; }
.voice-picker { grid-column: 1 / -1; display: flex; gap: 6px; }
.voice-picker[hidden] { display: none; }
.voice-picker button { min-height: 44px; padding: 6px 12px; border: 1px solid var(--line); border-radius: 5px; background: var(--panel); color: var(--muted); }
.voice-picker button[aria-pressed="true"] { color: var(--ink); border-color: var(--blue); background: var(--raised); }
.voice-picker small { margin-left: 5px; font-weight: normal; }
.score-views { display: flex; gap: 4px; }
.score-views[hidden] { display: none; }
.score-views button { min-height: 44px; padding: 6px 10px; border: 1px solid var(--line); border-radius: 5px; background: var(--panel); color: var(--muted); }
.score-views button[aria-pressed="true"] { color: var(--ink); border-color: var(--blue); background: var(--raised); }
.dictation-page #score[data-reading="etude"] { pointer-events: none; }
@media (max-width: 900px) {
  .dictation-page .transport.dictation-transport { min-height: 0; }
  .dictation-page .source-line, .dictation-page .degree-pad { position: static; min-height: 0; }
}
/* Buttons a hand finds without looking: bordered, raised, 46 px, the icon a
   real glyph — not four words floating in a row. */
.transport.dictation-transport button { display: inline-flex; align-items: center; justify-content: center; gap: 8px; min-height: 46px; padding: 8px 18px; border: 1px solid var(--line); border-radius: 8px; background: var(--raised); font-size: 14px; font-weight: 550; }
.transport.dictation-transport button:hover:not(:disabled) { border-color: var(--blue); background: var(--hover); }
.transport.dictation-transport button:disabled { opacity: .45; }
.transport.dictation-transport #hear-it { border-color: color-mix(in srgb, var(--blue) 55%, var(--line)); background: color-mix(in srgb, var(--blue) 14%, var(--raised)); }
.transport.dictation-transport #submit { border-color: color-mix(in srgb, var(--green) 50%, var(--line)); }
.transport.dictation-transport .button-note .glyph { height: 20px; width: auto; display: block; }
.transport.dictation-transport .button-note { display: inline-flex; }
.dictation-page #result { scroll-margin-top: calc(var(--score-dock-height, 0px) + 70px); }
/* The marking is longer than Explore's inspector (a score, the bar rows, five
   readings and the coaching), and the paper must not be squeezed to nothing to
   hold it: the answer column scrolls instead — ONE scroller. Explore's drawer
   caps itself and scrolls inside; here that made two nested scrollers and the
   reading card was cut at a hard inner edge, which is the same thing a student
   sees as broken. The drawer is as tall as it needs to be and the column moves. */
.dictation-page .inspector { max-height: none; min-height: auto; overflow: visible; }
@media (max-width: 760px) {
  /* Every control a student presses is 44 px, the rail's doors included: on a
     phone the rows live in the Etudes popover, so Unlock anyway is there too. */
  #source-toggle { min-height: 44px; }
}

/* ── the dictation page, 2026-09-15 ────────────────────────────────────────
   Brandon: "the top portion is a little too thin, which makes it feel like
   we're squinting. This goes for both the app and everything." The bars that
   frame every pane are one token now, and it is bigger; the type on them is
   bigger with it. */
:root { --bar-h: 56px; }
.pane-bar { min-height: var(--bar-h); }
.dictation-page .pane-bar h2 { font-size: 14.5px; }
.dictation-page .pane-bar small { font-size: 12.5px; }
/* "This goes for both the app and everything": the site's own top bar grows
   with the pane bars, not just this page's. Studio's is its own repo's. */
.topbar { min-height: 66px; }
.dictation-page .pane-bar h2 { font-size: 14.5px; }

/* THE RAIL IS A TAB YOU CAN SHUT. Shut, it leaves the chevron that opens it
   again and gives the whole width to the answer. */
.rail-toggle { display: grid; place-items: center; width: 30px; height: 30px; border: 1px solid transparent; border-radius: 5px; background: none; color: var(--muted); font-size: 17px; line-height: 1; }
.rail-toggle:hover { background: var(--hover); border-color: var(--line); color: var(--ink); }
body[data-rail="shut"] .etude-rail .lesson-rows,
body[data-rail="shut"] .etude-rail .pane-bar div { display: none; }
body[data-rail="shut"] .etude-rail { grid-template-rows: var(--bar-h); }
body[data-rail="shut"] .workbench { grid-template-columns: 44px 6px minmax(0, 1fr); }
body[data-rail="shut"] .rail-toggle span { transform: rotate(180deg); }
body[data-rail="shut"] #score-splitter { pointer-events: none; opacity: .4; }

/* THE SOURCE IS ONE LINE, AND IT IS THE BARS. A terminal line: the mark, the
   text, and what the kernel says about it. There is no head in it to break. */
.source-line { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; gap: 10px; padding: 9px 16px; border-top: 1px solid var(--line); background: var(--panel); }
.source-line-mark { color: var(--blue); font: 600 15px/1 var(--mono); }
.bar-input { position: relative; min-width: 0; font: 15px/1.5 var(--mono); }
#bars-line { width: 100%; min-width: 0; min-height: 58px; padding: 26px 12px 8px; border: 1px solid var(--line); border-radius: 6px; background: var(--code); color: var(--code-ink); font: inherit; }
.bar-ruler { position: absolute; inset: 1px; overflow: hidden; pointer-events: none; }
.bar-ruler-track { position: absolute; top: 26px; left: 12px; white-space: pre; font: inherit; visibility: hidden; }
.bar-tick, .bar-first { position: relative; }
.bar-number { position: absolute; bottom: calc(100% + 2px); left: 50%; transform: translateX(-50%); visibility: visible; color: var(--muted); font: 12px/1 var(--mono); }
.bar-first .bar-number { left: 0; transform: none; }
#bars-line:focus { outline: none; border-color: var(--blue); box-shadow: 0 0 0 3px color-mix(in srgb, var(--blue) 22%, transparent); }
.source-line-note { color: var(--muted); font-size: 12px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: min(42ch, 34vw); }
.source-line-note:empty { display: none; }
/* On a phone the note goes UNDER the line: the line keeps the whole width. */
@media (max-width: 760px) {
  .about-footer .about-note { margin-left: 0; text-align: left; }
  .source-line { grid-template-columns: auto minmax(0, 1fr); }
  .source-line-note { grid-column: 1 / -1; max-width: none; white-space: normal; }
}
.source-line-note[data-kind="error"] { color: var(--red); }


/* ── THE FINISH (2026-09-15) ───────────────────────────────────────────────
   Brandon: "when we actually complete it, this is not what we want… a nice
   animation… fun while still staying professional." Ported as DESIGN from the
   Music Trainer's LessonCompleteCelebration: notes pop onto a little staff one
   after another, rising; a few glyphs drift up and fade; the score counts up;
   the door to the next etude stands ready. All of it CSS, all of it stilled
   under prefers-reduced-motion. */
.finale { position: relative; overflow: hidden; display: grid; justify-items: center; gap: 8px; margin: 2px 0 10px; padding: 12px 16px; border: 1px solid color-mix(in srgb, var(--green) 40%, var(--line)); border-radius: 10px; background: linear-gradient(180deg, color-mix(in srgb, var(--green) 9%, var(--panel)), var(--panel)); }
.finale-staff { width: min(260px, 60%); height: auto; color: var(--ink); }
.finale-staff line { stroke: color-mix(in srgb, currentColor 38%, transparent); stroke-width: 1; }
.finale-note { fill: currentColor; opacity: 0; transform-origin: center; animation: finale-pop .45s cubic-bezier(.2, 1.4, .4, 1) forwards; animation-delay: calc(160ms + var(--i) * 150ms); }
@keyframes finale-pop { from { opacity: 0; translate: 0 10px; scale: .6; } to { opacity: 1; translate: 0 0; scale: 1; } }
.finale-drift { position: absolute; bottom: -6px; color: color-mix(in srgb, var(--green) 55%, var(--muted)); font-size: 15px; opacity: 0; pointer-events: none; animation: finale-drift 2.4s ease-out forwards; }
@keyframes finale-drift { 8% { opacity: .9; } 100% { opacity: 0; transform: translate(var(--d), -130px) rotate(12deg); } }
.finale-best { padding: 3px 10px; border: 1px solid color-mix(in srgb, var(--green) 55%, var(--line)); border-radius: 999px; color: var(--green); font-size: 11.5px; letter-spacing: .04em; text-transform: uppercase; animation: finale-pop .4s .9s cubic-bezier(.2, 1.4, .4, 1) backwards; }
.next-etude { min-height: 44px; padding: 10px 18px; border: 1px solid color-mix(in srgb, var(--green) 55%, var(--line)); border-radius: 7px; background: color-mix(in srgb, var(--green) 16%, var(--raised)); color: var(--ink); font-size: 14.5px; font-weight: 550; animation: finale-pop .4s 1.05s cubic-bezier(.2, 1.4, .4, 1) backwards; }
.next-etude:hover { background: color-mix(in srgb, var(--green) 26%, var(--raised)); }
/* A finished answer needs no table: the bars fold into one row of chips and
   the five readings into slim rows, still every one of them on the page. */
/* A bar that MATCHES is one chip, on a pass and on a miss alike: three rows
   of "matches." told nobody anything (Brandon, 2026-09-15: condensed, not
   line by line). What missed keeps the whole width and the teaching. */
.dictation-page .bar-rows { display: flex; flex-wrap: wrap; gap: 6px; align-items: flex-start; }
.dictation-page .bar-row[data-state="ok"] { border: 1px solid color-mix(in srgb, var(--green) 45%, var(--line)); border-radius: 999px; padding: 4px 12px; color: var(--green); background: color-mix(in srgb, var(--green) 8%, transparent); }
.dictation-page .bar-row[data-state="miss"] { flex: 1 1 100%; }
.dimensions { display: grid; grid-template-columns: repeat(auto-fit, minmax(228px, 1fr)); gap: 6px; }
.dimension { border: 1px solid var(--line); border-radius: 6px; padding: 7px 10px; }
.dimension[data-verdict="correct"] .dimension-why { display: none; }
.dimension[data-verdict="not asked"] .dimension-why { display: none; }
.grade-line { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 12px; padding: 8px 12px; }
.grade-line .grade-why { font-size: 11.5px; line-height: 1.5; max-width: 72ch; }
.dictation-page .marks-head { margin: 12px 0 6px; }
.dictation-page .inspector { padding: 10px 14px 14px; }
@media (prefers-reduced-motion: reduce) {
  .finale-note, .finale-best, .next-etude { animation: none; opacity: 1; }
  .finale-drift { display: none; }
  .chapter-chevron { transition: none; }
}

/* Mark the student’s own top notation; the reference remains unmarked. */
.dictation-page #score svg .ss-event[data-wrong] use, .dictation-page #score svg .ss-event[data-wrong] path,
.dictation-page #score svg .ss-event[data-wrong] rect:not(.ss-selection) { fill: var(--paper-wrong); }
.dictation-page #score svg .ss-event[data-close] use, .dictation-page #score svg .ss-event[data-close] path { fill: #a06a00; }
/* A CLOSE bar keeps its amber teaching note, with no points deducted
   (Brandon, 2026-10-01). */
.dictation-page .bar-row[data-state="close"] { flex: 1 1 100%; border-left: 3px solid var(--caution); padding-left: 10px; color: var(--ink); }
.dictation-page .bar-row[data-state="close"] .tick { color: var(--caution); }

/* ── the dictation class gate (website/gate/dictation-gate.js, 2026-09-23) ──
   One field and one button in front of /dictation/ while it is a class test
   run. Served by a Pages Function; these rules touch nothing but .gate-page. */
.gate-page { min-height: 100dvh; background: var(--ground); }
.gate-main { padding: clamp(40px, 12vh, 120px) 16px 48px; }
.gate-card { max-width: 420px; margin: 0 auto; padding: 28px 28px 22px; border: 1px solid var(--line); border-radius: 8px; background: var(--panel); }
.gate-card h1 { margin: 0 0 6px; font: 600 24px/1.2 Georgia, serif; letter-spacing: -.4px; }
.gate-lede { margin: 0 0 20px; color: var(--muted); }
.gate-card label { display: block; margin-bottom: 6px; font-size: 12px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; color: var(--muted); }
.gate-row { display: flex; gap: 8px; }
.gate-row input { flex: 1; min-width: 0; min-height: 40px; padding: 8px 12px; border: 1px solid var(--line); border-radius: 4px; background: var(--ground); color: var(--ink); font-size: 16px; }
.gate-row input[aria-invalid="true"] { border-color: var(--red); }
.gate-row button { min-height: 40px; padding: 8px 18px; border: 0; background: var(--blue); color: var(--ground); font-weight: 600; }
.gate-row button:hover { filter: brightness(1.08); }
.gate-wrong { margin: 10px 0 0; color: var(--red); font-size: 13px; }
.gate-note { margin: 18px 0 0; padding-top: 14px; border-top: 1px solid var(--line); color: var(--muted); font-size: 12.5px; }
@media (max-width: 760px) {
  .gate-card { padding: 22px 18px 18px; }
}

/* Marking leads with the music; calculations are available on request. */
.dictation-page .score-details { margin-top: 16px; border-top: 1px solid var(--line); padding-top: 10px; }
.dictation-page .score-details > summary,
.dictation-page .bar-details > summary { cursor: pointer; color: var(--muted); font-size: 13px; padding: 8px 0; }
.dictation-page .score-details[open] > summary { margin-bottom: 8px; }
.dictation-page .bar-row[data-state="caution"] { border-color: var(--accent); }

.workspace-views { display: none; }
@media (any-pointer: coarse) {
  button, summary, select { touch-action: manipulation; min-width: 44px; min-height: 44px; }
  input[type="search"], .format-dialog select { font-size: 16px; min-height: 44px; }
}
@media (max-width: 760px) {
  .workbench-page:not(.dictation-page) { height: 100dvh; display: flex; flex-direction: column; overflow: hidden; }
  .workbench-page:not(.dictation-page) .topbar { flex: none; min-height: 92px; padding: 0 12px; gap: 0 8px; }
  .workbench-page:not(.dictation-page) .topbar-actions { margin-left: auto; gap: 4px; }
  .workbench-page:not(.dictation-page) .secondary-nav { gap: 8px; border: 0; padding: 0; }
  .workbench-page:not(.dictation-page) .secondary-nav a { padding-inline: 4px; }
  .workbench-page:not(.dictation-page) .app-link { display: none; }
  .workbench-page:not(.dictation-page) .primary-nav { height: 44px; flex-wrap: nowrap; overflow-x: auto; }
  .workbench-page:not(.dictation-page) .explore-main { flex: 1; height: auto; min-height: 0; }
  .workbench-page:not(.dictation-page) .playhouse { height: 100%; grid-template-rows: auto minmax(0,1fr) auto; }
  .workspace-views { display: flex; gap: 4px; padding: 4px 12px; background: var(--panel); border-bottom: 1px solid var(--line); }
  .workspace-views button { flex: 1; min-height: 44px; border: 0; border-radius: 4px; background: none; color: var(--muted); font-size: 14px; }
  .workspace-views button[aria-pressed="true"] { background: var(--raised); color: var(--blue); box-shadow: inset 0 -2px var(--blue); }
  .workbench-page:not(.dictation-page) .workbench { grid-template-rows: minmax(0,1fr); }
  .workbench-page:not(.dictation-page) .source-pane,
  .workbench-page:not(.dictation-page) .wiki-pane,
  .workbench-page:not(.dictation-page) .output-pane { height: 100%; min-height: 0; }
  :root[data-workspace-view="score"] .workbench-page:not(.dictation-page) .source-workspace,
  :root[data-workspace-view="source"] .workbench-page:not(.dictation-page) .output-pane,
  :root[data-workspace-view="source"] .workbench-page:not(.dictation-page) .wiki-pane,
  :root[data-workspace-view="wiki"] .workbench-page:not(.dictation-page) .source-pane,
  :root[data-workspace-view="wiki"] .workbench-page:not(.dictation-page) .output-pane { display: none; }
  .workbench-page:not(.dictation-page) .output-bar { padding: 4px 12px; gap: 4px; }
  .workbench-page:not(.dictation-page) .output-bar .pane-actions { width: 100%; justify-content: space-between; }
  .workbench-page:not(.dictation-page) .paper-stage { padding: 12px; }
  .workbench-page:not(.dictation-page) .paper:not(.pages) { padding: 24px 12px; }
  .workbench-page:not(.dictation-page) .workbench-footer { padding: 4px 12px; min-height: 52px; gap: 8px; }
  .workbench-page:not(.dictation-page) .document-state { flex: 1; align-items: center; }
  .workbench-page:not(.dictation-page) .site-credit { display: none; }
  .page-controls select { min-height: 44px; font-size: 16px; }
  .picker-results button { min-height: 44px; }
  .format-dialog { padding: 16px; }
  .format-dialog header, .format-dialog footer { flex-wrap: wrap; gap: 8px; }
  .format-dialog button, .format-dialog select { min-height: 44px; }
  #format-options > label { flex-wrap: wrap; gap: 8px; }
}
@media (any-pointer: coarse) and (min-width: 761px) {
  .primary-nav a, .secondary-nav a { display: inline-flex; align-items: center; min-height: 44px; }
  .playhouse { grid-template-rows: minmax(0,1fr) auto; }
  .workbench-footer { min-height: 52px; padding-block: 4px; }
  .output-pane { grid-template-rows: auto minmax(0,1fr) auto; }
  .output-pane.paged, :root[data-score-view="pages"] .workbench-page:not(.dictation-page) .output-pane { grid-template-rows: auto auto minmax(0,1fr) auto; }
  .output-pane:has(.layout-notes:not([hidden])) { grid-template-rows: auto auto minmax(0,1fr) auto auto; }
  .output-bar { flex-wrap: wrap; padding-block: 4px; }
}
@media (max-width: 760px) {
  .dictation-page .degree-pad { display: grid; grid-template-columns: minmax(0,1fr) auto; gap: 8px; padding: 12px; }
  .dictation-page .pad-group { min-width: 0; gap: 4px; flex-wrap: nowrap; padding: 0; border: 0; }
  .dictation-page .pad-notes, .dictation-page .pad-lengths { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(44px,1fr); grid-column: 1/-1; overflow-x: auto; padding-bottom: 2px; }
  .dictation-page .pad-notes { order: 1; }
  .dictation-page .pad-lengths { order: 2; }
  .dictation-page .pad-marks { order: 3; grid-column: 1; }
  .dictation-page .pad-moves { order: 4; grid-column: 2; }
  .dictation-page .pad-key { min-width: 44px; min-height: 48px; padding: 4px; }
  .dictation-page .pad-marks .pad-key { flex: 1; }
  .dictation-page .pad-shortcut { left: 4px; top: 3px; font-size: 9px; }
  .dictation-page #bars-line { font-size: 16px; }
  .dictation-page .topbar .app-link { display: none; }
  .dictation-page .score-dock .paper-stage { max-height: min(32dvh,280px); }
}
