/* Minimalist pure-B/W stylesheet. System text, a serif home invitation, semantic
   HTML; inverts via prefers-color-scheme. No grays, no chromatic colors. */

:root {
    --fg: #000;
    --bg: #fff;
    --touch: 2.75rem;        /* 44px minimum tap target */
    --measure: 42rem;        /* comfortable reading measure for prose */
    accent-color: var(--fg);
    color-scheme: light;
}
@media (prefers-color-scheme: dark) {
    :root { --fg: #fff; --bg: #000; color-scheme: dark; }
}

/* ── Reset & document ─────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }

html {
    background: var(--bg);
    color: var(--fg);
    font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI",
                 Roboto, "Helvetica Neue", Arial, sans-serif;
    font-size: clamp(1rem, 0.9rem + 0.4vw, 1.25rem);
    line-height: 1.6;
    -webkit-text-size-adjust: 100%;
}
body { margin: 0; min-height: 100vh; display: flex; flex-direction: column; }
main { flex: 1; width: 100%; padding: 1rem clamp(1rem, 4vw, 3rem) 3rem; }

@media (prefers-reduced-motion: no-preference) {
    html { scroll-behavior: smooth; }
}

/* ── Typography ──────────────────────────────────────────────────── */
h1, h2, h3, h4, h5, h6 {
    font-weight: 600; line-height: 1.2;
    margin: 1.6em 0 0.5em; text-wrap: balance;
}
h1 { font-size: clamp(1.75rem, 1.2rem + 2vw, 3rem); letter-spacing: -0.02em; margin-top: 0.6em; }
h2 { font-size: clamp(1.35rem, 1.05rem + 1.1vw, 2.1rem); letter-spacing: -0.01em; }
h3 { font-size: clamp(1.1rem, 0.95rem + 0.6vw, 1.45rem); }
h4, h5, h6 { font-size: 1.05rem; }
h5, h6 { font-style: italic; }

p, li, dt, dd, blockquote { text-wrap: pretty; overflow-wrap: break-word; }
p { margin: 0 0 1em; }

/* Reading measure: running text caps at --measure for legibility, while
   headings (larger type, short lines) and structural elements (tables,
   figures, notation, rules, pagination) keep the full 60rem column.
   `.library-nav` is pagination, so it stays wide. */
main :is(p:not(.library-nav), ul, ol, dl, blockquote, aside, figcaption) {
    max-width: var(--measure);
}
ul, ol { padding-left: 1.5em; margin: 0 0 1em; }
li { margin: 0.2em 0; }
dl { margin: 1em 0; }
dt { font-style: italic; margin-top: 0.8em; }
dd { margin: 0 0 0.5em; padding-left: 1.2em; }

blockquote, aside {
    margin: 1.2em 0; padding-left: 1rem;
    border-left: 1px solid currentColor;
}
blockquote { border-left-width: 2px; font-style: italic; margin-block: 1.5em; }
aside { font-size: 0.9em; }
blockquote :first-child, aside :first-child { margin-top: 0; }
blockquote :last-child,  aside :last-child  { margin-bottom: 0; }

a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }
a:hover, a:focus-visible { text-decoration-thickness: 2px; }
strong, b { font-weight: 700; }
em, i, .text-muted { font-style: italic; }

code, pre, kbd, samp {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.95em;
}
pre { overflow-x: auto; padding: 0.75em 1em; border: 1px solid currentColor; }

table { border-collapse: collapse; width: 100%; margin: 1.5em 0; }
th, td { padding: 0.4em 0.7em; text-align: left; border-bottom: 1px solid currentColor; }
th { font-weight: 600; }

hr { border: 0; border-top: 1px solid currentColor; margin: 2em 0; }

figure { margin: 1.5em 0; }
figcaption { font-style: italic; font-size: 0.9em; margin-top: 0.4em; }
img, svg { max-width: 100%; height: auto; }

/* ── Universal accessibility ─────────────────────────────────────── */
:focus-visible { outline: 2px solid currentColor; outline-offset: 2px; }
::selection { background: currentColor; color: var(--bg); }

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        transition-duration: 0.01ms !important;
        animation-duration: 0.01ms !important;
    }
}

.skip-link {
    position: absolute; top: -100px; left: 1rem; z-index: 1000;
    padding: 0.5em 0.75em;
    background: var(--bg); color: var(--fg);
    border: 1px solid currentColor;
}
.skip-link:focus { top: 0.5rem; }

.visually-hidden {
    position: absolute; width: 1px; height: 1px;
    padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0);
    white-space: nowrap; border: 0;
}

/* ── Page chrome (header / breadcrumb / footer) ──────────────────── */
.site-header, .site-footer { padding: 0.9rem clamp(1rem, 4vw, 3rem) 1rem; }
.site-header { border-bottom: 1px solid currentColor; }
.site-footer { border-top: 1px solid currentColor; margin-top: 3rem; }

.site-header__inner, .site-footer__inner, .breadcrumb, main > * {
    max-width: 60rem; margin-inline: auto;
}

.site-header__inner {
    display: flex; flex-wrap: wrap; align-items: baseline; gap: 1.5rem;
}
.site-brand {
    font-style: italic; font-size: 1.15rem;
    text-decoration: none; white-space: nowrap;
}
.site-brand:hover { text-decoration: underline; }

.site-nav {
    list-style: none; padding: 0; margin: 0; flex: 1;
    display: flex; flex-wrap: wrap; gap: 1rem;
}
.site-nav li { margin: 0; }
.site-nav a { text-decoration: none; }
.site-nav a:hover { text-decoration: underline; }
.site-nav a[aria-current="page"] { font-style: italic; }
.site-nav details { position: relative; }
.site-nav summary { cursor: pointer; list-style: none; }
.site-nav summary:hover { text-decoration: underline; }
.site-nav summary::-webkit-details-marker { display: none; }
.site-nav summary::after { content: " \25BE"; }
.site-nav .submenu {
    position: absolute; top: 100%; left: 0; z-index: 100;
    min-width: 13rem; margin: 0.5rem 0 0; padding: 0.75rem 1rem;
    list-style: none;
    background: var(--bg); border: 1px solid currentColor;
    display: flex; flex-direction: column; gap: 0.4rem;
}
.submenu-divider { border: 0; border-top: 1px solid currentColor; margin: 0.3rem 0; }

.nav-toggle {
    display: none;
    background: transparent; color: inherit;
    border: 1px solid currentColor; padding: 0.4em 0.7em;
    font: inherit; cursor: pointer;
}

.breadcrumb {
    padding: 0.6rem clamp(1rem, 4vw, 3rem);
    font-size: 0.9em; font-style: italic;
    border-bottom: 1px dotted currentColor;
}
/* The home page ships an empty breadcrumb slot; don't draw a stray rule. */
.breadcrumb:not(:has(ol)) { display: none; }
.breadcrumb ol {
    list-style: none; padding: 0; margin: 0;
    display: flex; flex-wrap: wrap; gap: 0.4rem;
}
.breadcrumb a { text-decoration: none; }
.breadcrumb a:hover { text-decoration: underline; }
.breadcrumb li + li::before { content: "/ "; }

.site-footer__inner {
    display: flex; flex-direction: column; gap: 1rem;
    font-style: italic; font-size: 0.9em;
}
.site-footer p { margin: 0; }
.site-footer__sitemap {
    display: grid; gap: 1.5rem; margin-bottom: 1rem;
    grid-template-columns: repeat(auto-fit, minmax(min(15rem, 100%), 1fr));
}
.site-footer__sitemap h3 {
    font-size: 0.95em; margin: 0 0 0.5rem;
    text-transform: uppercase; letter-spacing: 0.05em; font-style: italic;
}
.site-footer__sitemap ul {
    list-style: none; padding: 0; margin: 0;
    display: flex; flex-direction: column; gap: 0.3rem; font-size: 0.92em;
}
.site-footer__sitemap a { text-decoration: none; }
.site-footer__sitemap a:hover { text-decoration: underline; }
.site-footer__credits {
    padding-top: 1rem; border-top: 1px solid currentColor;
    font-size: 0.9em;
}

/* A small musical invitation, using the same ink-and-paper palette as
   the lessons. The score is the home page's illustration and can be heard. */
.home-intro {
    display: grid; grid-template-columns: 1fr 1fr;
    gap: clamp(2rem, 5vw, 4rem); align-items: center;
    padding-block: clamp(1.5rem, 4vw, 3rem) 3rem;
}
.home-welcome h1 {
    font-family: Georgia, 'Times New Roman', serif;
    font-weight: 400; font-size: clamp(2.6rem, 4.4vw, 4rem);
    line-height: 1.08; letter-spacing: -0.035em; margin: 0 0 1.2rem;
}
.home-lead { font-size: 1.05rem; max-width: 29rem; }
.start-link {
    display: inline-flex; align-items: center; min-height: var(--touch);
    padding: 0.65rem 1rem; margin: 0.25rem 0 0.8rem;
    background: var(--fg); color: var(--bg); border: 2px solid var(--fg);
    text-decoration: none; font-weight: 600; line-height: 1.4;
    border-radius: 0.25rem;
}
.start-link:hover { background: var(--bg); color: var(--fg); }
.start-link:focus-visible { outline-color: var(--fg); outline-offset: 3px; }
.home-preparation { font-size: 0.9rem; }
.home-promise { font-size: 0.8rem; margin: 1.4rem 0 0; }
.home-tune { min-width: 0; border: 1px solid currentColor; padding: clamp(1rem, 2vw, 1.5rem); }
.home-tune h2 { font-size: 1.35rem; margin: 0 0 0.65rem; }
.home-tune p { font-size: 0.9rem; }
.home-tune figure { margin: 0.75rem 0 0; }
.home-tune figcaption { font-size: 0.8rem; margin-top: 1rem; }
.home-tune transpose-controls { margin-block: 0.75rem; }
.home-session { border-top: 1px solid currentColor; padding-top: 0.3rem; }
.first-steps {
    display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 2rem; padding-left: 1.25rem; margin-block: 1.75rem 3rem;
}
main .first-steps { max-width: none; }
.first-steps li { padding-left: 0.35rem; }
.first-steps li::marker { font-family: Georgia, 'Times New Roman', serif; font-size: 1.3rem; }
.first-steps a { font-weight: 600; }
.first-steps p { font-size: 0.9rem; margin: 0.6rem 0 0; }
.home-paths {
    display: grid; grid-template-columns: 1fr 1.35fr; gap: 3rem;
    border-top: 1px solid currentColor; padding-block: 1.25rem 2rem;
}
.home-paths h2 { margin-top: 0.7rem; }
.home-paths > div > p { font-size: 0.95rem; }
ol.learning-paths { list-style: none; padding: 0; margin: 0; }
.learning-paths li { margin: 0; border-bottom: 1px solid currentColor; }
.learning-paths a { display: block; padding: 1rem 0.3rem; text-decoration: none; }
.learning-paths a:hover strong { text-decoration: underline; text-underline-offset: 3px; }
.learning-paths strong { font-weight: 600; }
.learning-paths span { display: block; font-size: 0.85rem; margin-top: 0.25rem; }
.home-repertoire { border-top: 1px solid currentColor; padding-block: 0.3rem 1rem; }

/* Optional lesson navigation and self-assessment stay separate from the
   music. A checked goal reflects the learner's judgement, never a score. */
.lesson-guide { max-width: var(--measure); margin-block: 1.5rem 2rem; border-block: 1px solid currentColor; }
.lesson-guide summary { cursor: pointer; min-height: var(--touch); padding-block: 0.5rem; font-weight: 600; }
.lesson-guide ul { list-style: none; padding: 0; margin: 0 0 0.75rem; columns: 2; column-gap: 2rem; }
.lesson-guide li { break-inside: avoid; margin: 0; }
.lesson-guide a { display: inline-block; min-height: var(--touch); padding-block: 0.35rem; font-size: 0.9rem; }
music-lesson h2[id] { scroll-margin-top: 1rem; }
.lesson-check { margin-top: 3rem; padding: 0.5rem 0 0; border-top: 2px solid currentColor; max-width: var(--measure); }
.lesson-check h2 { margin-top: 1rem; }
.lesson-check fieldset { min-width: 0; padding: 0; margin: 1.25rem 0; border: 0; }
.lesson-check legend { font-weight: 600; font-size: 0.85rem; margin-bottom: 0.5rem; }
.lesson-check label { display: flex; align-items: flex-start; gap: 0.75rem; padding-block: 0.6rem; min-height: var(--touch); cursor: pointer; }
.lesson-check input { flex: none; width: 1.15rem; height: 1.15rem; margin: 0.2rem 0 0; accent-color: var(--fg); }
.lesson-check-status { font-weight: 600; font-size: 0.95rem; }
.lesson-check-note { font-size: 0.8rem; }

@media (max-width: 850px) {
    .home-intro { grid-template-columns: 1fr; gap: 2rem; padding-bottom: 2rem; }
    .home-welcome h1 { font-size: clamp(2.6rem, 7vw, 4rem); }
    .home-lead { max-width: 34rem; }
    .home-tune { max-width: 34rem; width: 100%; }
    .home-paths { grid-template-columns: 1fr; gap: 0.5rem; }
}
@media (max-width: 540px) {
    .first-steps { grid-template-columns: 1fr; gap: 1.35rem; }
    .lesson-guide ul { columns: 1; }
}

/* Mobile nav: collapse into a tap-to-open panel; expand all touch targets. */
@media (max-width: 720px) {
    .nav-toggle { display: inline-block; }
    .site-nav {
        flex-basis: 100%; flex-direction: column; gap: 0; display: none;
    }
    body.nav-open .site-nav { display: flex; }
    .site-nav a, .site-nav summary { display: block; padding-block: 0.6rem; }
    .site-nav summary { cursor: default; pointer-events: none; font-style: italic; }
    .site-nav summary::after { content: ""; }
    .site-nav .submenu {
        position: static; border: 0; gap: 0;
        margin: 0; padding: 0 0 0 1rem;
    }
    .submenu-divider { display: none; }

    .nav-toggle,
    notation-display .chip,
    notation-display .play-btn,
    notation-display .stop-btn,
    notation-display .game-btn {
        min-height: var(--touch); padding-block: 0.6rem;
    }
    notation-display .playback-controls { gap: 1rem; row-gap: 0.6rem; }
}

/* ── Custom-element block layout ─────────────────────────────────── */
notation-display, exercise-container, music-lesson,
lesson-sidebar, transpose-controls, game-launcher {
    display: block; margin: 1.5em 0;
}
fingering-chart { display: inline-block; vertical-align: top; }

/* ── Notation (Verovio SVG, ten pre-rendered variants per snippet) ──
 * Each <notation-display> hosts ten SVGs: (soprano, alto) × (xs, sm, md,
 * lg, xl). The host is a size container; `@container` queries flip a
 * per-SVG `--on` flag so exactly one width variant is eligible, then a
 * single instrument gate displays the eligible SVG of the active
 * instrument (no body attribute — e.g. JS disabled — means soprano).
 * Thresholds must match NOTATION_VARIANTS in notation.py — generate.py
 * asserts this at build time. The queries target the descendant SVGs
 * because a container query cannot style its own container.
 * `overflow-x: auto` stays as a safety net for unusually long bars. */
notation-display {
    position: relative; overflow-x: auto;
    container-type: inline-size;
}
notation-display .notation-svg { display: none; max-width: 100%; height: auto; --on: none; }

notation-display .notation-svg[data-variant="xl"] { --on: block; }
@container (max-width: 1019px) {
    notation-display .notation-svg[data-variant="xl"] { --on: none; }
    notation-display .notation-svg[data-variant="lg"] { --on: block; }
}
@container (max-width: 779px) {
    notation-display .notation-svg[data-variant="lg"] { --on: none; }
    notation-display .notation-svg[data-variant="md"] { --on: block; }
}
@container (max-width: 599px) {
    notation-display .notation-svg[data-variant="md"] { --on: none; }
    notation-display .notation-svg[data-variant="sm"] { --on: block; }
}
@container (max-width: 459px) {
    notation-display .notation-svg[data-variant="sm"] { --on: none; }
    notation-display .notation-svg[data-variant="xs"] { --on: block; }
}

/* Instrument gate, once for all variants. */
notation-display .notation-svg[data-instrument="soprano"],
body[data-instrument="alto"] notation-display .notation-svg[data-instrument="alto"] {
    display: var(--on, none);
}
body[data-instrument="alto"] notation-display .notation-svg[data-instrument="soprano"] {
    display: none;
}

/* Per-instrument prose blocks: `data-instrument="…"` toggled by body attr. */
body[data-instrument="soprano"] [data-instrument="alto"]    { display: none; }
body[data-instrument="alto"]    [data-instrument="soprano"] { display: none; }

/* Verovio renders glyphs via <use> with no explicit fill, so default "black"
   would be invisible on dark backgrounds. Force every shape to current colour. */
notation-display .notation-svg,
notation-display .notation-svg .definition-scale { color: var(--fg); }
notation-display .notation-svg :where(path, use, rect, ellipse, polygon, polyline, text, tspan) {
    fill: currentColor; stroke: currentColor;
}
notation-display .notation-svg .note.current,
notation-display .notation-svg .note.current * {
    stroke-width: 1.75; paint-order: stroke;
}

/* JS positions the cursor on all three axes (left, top, height) so it
   spans only the system currently playing. top/height get no transition
   on purpose: line changes should snap, not sweep diagonally. */
notation-display .play-cursor {
    position: absolute; top: 0; height: 4rem;
    left: 0; width: 0.6rem;
    background: var(--fg); mix-blend-mode: difference;
    opacity: 0; pointer-events: none;
    transition: left 80ms linear, opacity 120ms ease;
}
@media (prefers-reduced-motion: reduce) {
    notation-display .play-cursor { transition: opacity 120ms ease; }
}

notation-display .playback-controls {
    display: flex; flex-wrap: wrap; align-items: baseline;
    gap: 0.75rem; margin-top: 0.4rem;
    font-size: 0.85rem; font-style: italic;
}
notation-display .play-btn,
notation-display .stop-btn,
notation-display .game-btn {
    appearance: none; background: transparent; border: 0;
    color: inherit; font: inherit; cursor: pointer;
    padding: 0.2rem 0.1rem;
    text-decoration: underline; text-underline-offset: 2px;
}
notation-display .play-btn:hover,  notation-display .stop-btn:hover,
notation-display .game-btn:hover,
notation-display .play-btn:focus-visible, notation-display .stop-btn:focus-visible,
notation-display .game-btn:focus-visible {
    text-decoration-thickness: 2px;
}
notation-display .playback-controls--simple { align-items: center; font-style: normal; }
notation-display .playback-controls--simple :is(.play-btn, .stop-btn) {
    min-height: var(--touch); padding: 0.5rem 1rem;
    background: var(--fg); color: var(--bg); border: 1px solid var(--fg);
    border-radius: 0.25rem; text-decoration: none;
}
notation-display .playback-controls--simple .now-playing { font-size: 0.8rem; }
notation-display .playback-controls--simple :is(.play-btn, .stop-btn):focus-visible {
    outline-color: var(--fg); outline-offset: 3px;
}
notation-display .play-btn[disabled],
notation-display .stop-btn[disabled] {
    text-decoration: line-through; cursor: not-allowed;
}

notation-display .tempo-label {
    display: inline-flex; align-items: center; flex-wrap: wrap;
    max-width: 100%; gap: 0.4rem; font-style: italic;
}
notation-display .tempo-value {
    display: inline-flex; align-items: baseline; gap: 0.25em; white-space: nowrap;
}
notation-display .tempo-equivalent {
    flex-basis: 100%; text-align: right; font-size: 0.8em; font-style: normal;
}
notation-display .tempo-equivalent:empty { display: none; }
:is(notation-display .tempo-slider, .game-dialog input[type="range"]) {
    appearance: none; -webkit-appearance: none;
    width: 8rem; height: 2px;
    /* The track is currentColor, and Chrome's UA sheet gives range inputs
       a warm grey colour of their own. */
    color: var(--fg);
    background: currentColor; cursor: pointer; vertical-align: middle;
}
/* Thumb rules must stay separate: one unknown pseudo-element in a selector
   list invalidates the whole rule, leaving the default (chromatic) thumb. */
:is(notation-display .tempo-slider, .game-dialog input[type="range"])::-webkit-slider-thumb {
    -webkit-appearance: none; appearance: none;
    width: 0.9rem; height: 0.9rem; border: 0; border-radius: 0;
    background: var(--fg); cursor: pointer;
}
:is(notation-display .tempo-slider, .game-dialog input[type="range"])::-moz-range-thumb {
    appearance: none;
    width: 0.9rem; height: 0.9rem; border: 0; border-radius: 0;
    background: var(--fg); cursor: pointer;
}
/* Small or touch screens: a full-height transparent hit area, with the
   visible track painted as a 4px line, so the slider stays thin but meets
   the 44px touch target. After the rule above, which it overrides. */
@media (max-width: 720px), (pointer: coarse) {
    :is(notation-display .tempo-slider, .game-dialog input[type="range"]) {
        height: var(--touch);
        background: linear-gradient(currentColor, currentColor)
                    50% 50% / 100% 4px no-repeat;
    }
}
/* A Game button while the game downloads (game-launcher.js openGame):
   aria-busy alone changes nothing a touch player can see. */
notation-display .game-btn[aria-busy="true"] { cursor: progress; text-decoration-style: dotted; }
notation-display .game-btn[aria-busy="true"]::after { content: " — loading…"; }

/* The Game button's "could not load — reload" note (game-launcher.js). */
notation-display .game-load-error { font-style: italic; }
notation-display .game-load-error button {
    appearance: none; background: transparent; border: 0;
    color: inherit; font: inherit; font-style: normal; cursor: pointer;
    min-height: var(--touch); padding: 0.2rem 0.1rem;
    text-decoration: underline; text-underline-offset: 2px;
}
notation-display .tempo-out {
    min-width: 2.2em; text-align: right;
    font-variant-numeric: tabular-nums; font-style: normal;
}
notation-display .tempo-unit { font-size: 0.8em; font-style: italic; }

notation-display .chip {
    appearance: none; background: transparent; color: inherit;
    border: 1px solid currentColor; border-radius: 999px;
    padding: 0.1rem 0.55rem;
    font: inherit; font-style: italic; font-size: 0.82rem;
    line-height: 1.3; cursor: pointer;
}
notation-display .chip:hover,
notation-display .chip:focus-visible {
    box-shadow: inset 0 0 0 1px currentColor;
}
notation-display .chip[aria-pressed="true"] {
    background: var(--fg); color: var(--bg);
    border-color: var(--fg); font-style: normal;
}

notation-display .now-playing {
    margin-left: auto; min-height: 1.2em;
    font-style: italic; font-variant-numeric: tabular-nums;
}

/* ── Sight-reading game (js/game/overlay.js, cards.js) ──────────────
 * A full-screen <dialog>: top bar, the Phaser stage, and HTML cards
 * over it. Same palette as the page — fill vs outline, never grey. The
 * dialog and form controls get explicit colours because the browser
 * defaults are grey. */
html.game-open { overflow: hidden; }

.game-dialog {
    position: fixed; inset: 0;
    width: 100vw; height: 100vh; height: 100dvh;
    max-width: none; max-height: none;
    margin: 0; padding: 0; border: 0;
    background: var(--bg); color: var(--fg);
    overscroll-behavior: contain;
}
.game-dialog::backdrop { background: var(--bg); }
.game-shell {
    display: flex; flex-direction: column; height: 100%;
    /* Fullscreen puts the shell on its own (black) backdrop. */
    background: var(--bg); color: var(--fg);
}

.game-dialog :is(button, select) {
    appearance: none; background: var(--bg); color: var(--fg);
    border: 1px solid currentColor; border-radius: 0;
    font: inherit; min-height: var(--touch);
}
.game-dialog button { padding: 0.3rem 0.8rem; cursor: pointer; line-height: 1.2; }
.game-dialog button:hover { box-shadow: inset 0 0 0 1px currentColor; }
.game-dialog .primary { background: var(--fg); color: var(--bg); border-color: var(--fg); }
.game-dialog button:disabled { cursor: not-allowed; text-decoration: line-through; }
.game-dialog select {
    max-width: 100%; padding: 0.3rem 2rem 0.3rem 0.6rem;
    /* A drawn chevron: appearance:none removes the (grey) native one. */
    background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                      linear-gradient(135deg, currentColor 50%, transparent 50%);
    background-position: calc(100% - 1.1rem) 55%, calc(100% - 0.75rem) 55%;
    background-size: 0.35rem 0.35rem, 0.35rem 0.35rem;
    background-repeat: no-repeat;
}
.game-dialog input[type="range"] { width: min(16rem, 100%); }
/* Drawn radios and checkboxes: the native ones fill grey in dark mode. */
.game-dialog :is(input[type="radio"], input[type="checkbox"]) {
    appearance: none; -webkit-appearance: none;
    display: inline-grid; place-content: center; flex: none;
    width: 1.1rem; height: 1.1rem; margin: 0;
    border: 1px solid currentColor; border-radius: 0;
    background: var(--bg); color: var(--fg); cursor: pointer;
}
.game-dialog input[type="radio"] { border-radius: 50%; }
.game-dialog :is(input[type="radio"], input[type="checkbox"])::before {
    content: ""; width: 0.6rem; height: 0.6rem;
    background: currentColor; transform: scale(0);
}
.game-dialog input[type="radio"]::before { border-radius: 50%; }
.game-dialog input[type="checkbox"]::before {
    clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%);
}
.game-dialog :is(input[type="radio"], input[type="checkbox"]):checked::before { transform: scale(1); }
.game-dialog input:disabled { cursor: not-allowed; border-style: dashed; }
.game-dialog input:disabled + span { text-decoration: line-through; }
.game-dialog kbd {
    font-size: 0.8em; padding: 0 0.3em;
    border: 1px solid currentColor; font-style: normal;
}

/* Top bar */
.game-bar {
    display: flex; flex-wrap: wrap; align-items: center; gap: 0.25rem 1rem;
    padding: 0.35rem clamp(0.5rem, 2vw, 1rem);
    border-bottom: 1px solid currentColor;
}
.game-title { margin: 0; font-size: 1rem; letter-spacing: 0; }
.game-exercise {
    flex: 1 1 4rem; min-width: 0; margin: 0;
    font-style: italic; font-size: 0.9rem;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.game-bar-actions { display: flex; flex-wrap: wrap; gap: 0.4rem; margin-left: auto; }
.game-rotate { display: none; order: 1; flex-basis: 100%; margin: 0; font-style: italic; font-size: 0.85rem; }
@media (orientation: portrait) {
    .game-dialog[data-state="play"] .game-rotate { display: block; }
}
/* Small screens: one row of controls. The title still labels the dialog. */
@media (max-width: 600px), (max-height: 500px) {
    .game-title {
        position: absolute; width: 1px; height: 1px; overflow: hidden;
        clip: rect(0, 0, 0, 0); white-space: nowrap;
    }
    .game-bar { gap: 0.2rem 0.5rem; padding-block: 0.25rem; }
    .game-bar-actions { gap: 0.3rem; }
    .game-bar-actions button { padding-inline: 0.55rem; }
}

/* Stage: its size never depends on the canvas (an inline canvas's
   baseline gap would grow it, and the ResizeObserver would chase it). */
.game-main { position: relative; flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
.game-stage {
    position: relative; flex: 1 1 auto; min-width: 0; min-height: 0;
    overflow: hidden; contain: strict;
    touch-action: none; user-select: none;
    -webkit-user-select: none; -webkit-touch-callout: none;
}
.game-stage:focus { outline: none; }
.game-stage > canvas { display: block; position: absolute; left: 0; top: 0; }

/* Cards. The layer is opaque: the last frame of a run (its HUD, the
   recorder, stubs of the staff) must not show around a card, and the
   Game's loop sleeps behind it. */
.game-cards {
    position: absolute; inset: 0;
    display: flex; align-items: center; justify-content: center;
    padding: clamp(0.5rem, 3vw, 1.5rem);
    background: var(--bg);
}
.game-cards[hidden] { display: none; }
.game-card {
    --pad: clamp(0.9rem, 3vw, 1.5rem);
    width: 100%; max-width: 34rem; max-height: 100%; overflow: auto;
    padding: 1rem var(--pad) 0;
    background: var(--bg); color: var(--fg); border: 1px solid currentColor;
    overscroll-behavior: contain;
}
.game-card-title { margin: 0 0 0.75rem; font-size: 1.35rem; }
.game-card-title:focus { outline: none; }
.game-card h3 { margin: 1.25rem 0 0.4rem; font-size: 1rem; }
.game-card p { margin: 0 0 0.75rem; }
.game-card fieldset { min-width: 0; margin: 1rem 0 0; padding: 0; border: 0; }
.game-card legend { padding: 0; margin-bottom: 0.25rem; font-weight: 600; }
.game-note { font-style: italic; font-size: 0.9rem; }
.game-card .game-note:empty { display: none; }
.game-dialog [hidden] { display: none !important; }
.game-choice { display: flex; align-items: flex-start; gap: 0.5rem; padding-block: 0.3rem; line-height: 1.4; }
.game-choice > input { margin-top: 0.15em; }
.game-choice-note { margin-left: 0.3rem; font-size: 0.85rem; }
.game-field, .game-row {
    display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem 0.75rem;
    margin: 0.75rem 0;
}
.game-field > label:first-child { min-width: 5rem; font-weight: 600; }
.game-bars label { display: inline-flex; align-items: center; gap: 0.4rem; }
.game-field output { font-style: italic; font-variant-numeric: tabular-nums; }
.game-prompts { margin: 0.5rem 0 0.75rem; padding-left: 1.2em; }
.game-goal { font-style: italic; font-size: 0.9rem; }
.game-goal > span { font-style: normal; }
/* Wide screens: setup and results in two columns (what to play | how to
   play it; the numbers | the music). A card may still scroll at laptop
   sizes, but what matters first sits above the action bar: the chosen
   mode's controls, and the Practice button above a long bar strip. */
@media (min-width: 60rem) and (min-height: 32rem) {
    .game-card:has(> .game-cols) { max-width: 62rem; }
    .game-cols {
        display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
        gap: 0 2.5rem; align-items: start;
    }
    .game-col > :first-child { margin-top: 0; }
}
.game-advanced { margin-top: 1rem; }
.game-advanced > summary { cursor: pointer; font-weight: 600; }
/* "Another keyboard layout?" (browsers that cannot read the layout). */
.game-keypos { margin: 0.5rem 0 0.75rem; }
.game-keypos > summary { cursor: pointer; }
.game-keypos > p { margin: 0.4rem 0 0; }
.game-actions {
    position: sticky; bottom: 0;
    display: flex; flex-wrap: wrap; gap: 0.5rem;
    margin: 1rem calc(-1 * var(--pad)) 0; padding: 0.75rem var(--pad);
    background: var(--bg); border-top: 1px solid currentColor;
}

/* Landscape phones: cards use the width, and the actions are one row
   (scrolling sideways if they must), so the bar does not take half the
   card. It stays sticky: Start and Again stay in reach. */
@media (max-height: 500px) {
    .game-card { max-width: none; }
    /* One system at a time in a box of its own, inside the card's own
       scroll, hides the marks: let the card be the only scroller. */
    .game-results .game-score { max-height: none; overflow: visible; }
    .game-actions {
        flex-wrap: nowrap; overflow-x: auto;
        margin-top: 0.5rem; padding-block: 0.4rem;
    }
    .game-actions > button { flex: none; padding-inline: 0.65rem; }
}

/* The page's own engraving, copied into a card. */
.game-score {
    max-height: 40vh; overflow: auto; margin: 0 0 0.75rem; padding-block: 0.5rem;
    border-block: 1px solid currentColor;
}
.game-score svg { display: block; width: 100%; max-width: 100%; height: auto; color: var(--fg); }
.game-score svg :where(path, use, rect, ellipse, polygon, polyline, text, tspan) {
    fill: currentColor; stroke: currentColor;
}
.game-score svg .game-mark { fill: none; stroke: currentColor; }

/* Microphone level: filled bar, with the room's noise floor as a tick
   standing out above and below it. */
.game-meter {
    position: relative; flex: 1 1 8rem; height: 0.75rem; min-width: 6rem;
    border: 1px solid currentColor;
}
.game-meter-level {
    position: absolute; left: 0; top: 0; bottom: 0;
    width: calc(var(--level, 0) * 100%); background: currentColor;
}
.game-meter-floor {
    position: absolute; top: -0.3rem; bottom: -0.3rem; width: 2px;
    left: calc(var(--floor, 0) * 100%); background: currentColor;
}

/* "Check your keyboard" */
.game-keys-row { display: flex; flex-wrap: wrap; gap: 0.3rem; margin: 0.5rem 0; }
.game-key {
    min-width: 2rem; padding: 0.15rem 0.4rem; text-align: center;
    border: 1px solid currentColor;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 0.85rem;
}
.game-key.is-down { background: var(--fg); color: var(--bg); }

/* Results */
.game-stars { margin: 0 0 0.5rem; font-size: 2rem; line-height: 1; letter-spacing: 0.1em; }
.game-badge {
    display: inline-block; margin-left: 0.5rem; padding: 0.05rem 0.5rem; vertical-align: middle;
    border: 1px solid currentColor; font-size: 0.8rem; font-style: italic; font-weight: 400;
}
.game-stats {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
    gap: 0.5rem 1rem; margin: 0 0 1rem;
}
/* The counts and the timing sentence get a whole row each. */
.game-stats > div:nth-child(n+4) { grid-column: 1 / -1; }
.game-stats dt { margin: 0; font-style: italic; font-size: 0.8rem; }
.game-stats dd { margin: 0; padding: 0; font-variant-numeric: tabular-nums; }
.game-playlist { margin: 0.5rem 0 1rem; font-size: 0.9rem; }
.game-strip { display: flex; flex-wrap: wrap; gap: 1rem 0.5rem; margin: 0.5rem 0 1.25rem; }
.game-strip-key { margin-bottom: 0.25rem; }
.game-practice:empty { display: none; }
.game-dialog .game-cell {
    position: relative;
    min-width: var(--touch); padding: 0;
    font-size: 0.8rem; font-variant-numeric: tabular-nums;
}
/* A long piece's strip: smaller cells, except on touch screens. */
@media (pointer: fine) {
    .game-dialog .game-strip.is-long { gap: 1rem 0.4rem; }
    .game-dialog .game-strip.is-long .game-cell { min-width: 2rem; min-height: 2rem; }
}
.game-dialog .game-cell.is-clean { background: var(--fg); color: var(--bg); }
.game-dialog .game-cell.is-crossed {
    background-image:
        linear-gradient(to top right, transparent calc(50% - 1px), currentColor calc(50% - 1px), currentColor calc(50% + 1px), transparent calc(50% + 1px)),
        linear-gradient(to top left, transparent calc(50% - 1px), currentColor calc(50% - 1px), currentColor calc(50% + 1px), transparent calc(50% + 1px));
}
.game-dialog .game-cell.is-crossed > span { padding: 0 0.15em; background: var(--bg); }
.game-dialog .game-cell.is-empty { border-style: dashed; }
/* A bar that only holds a tied note on: nothing to play, but not a rest. */
.game-dialog .game-cell.is-held { border-style: dotted; border-width: 2px; }
.game-dialog .game-cell[aria-pressed="true"] { outline: 3px solid var(--fg); outline-offset: 2px; }
/* Chosen for practice: underlined as well as outlined, so it never reads
   as just another outlined ("some misses") cell. */
.game-dialog .game-cell[aria-pressed="true"]::after {
    content: ""; position: absolute; left: -5px; right: -5px; bottom: -0.65rem;
    height: 0.2rem; background: var(--fg);
}
/* Focus rings. The global ring is currentColor, which on a filled button
   or cell is the background colour: invisible. A chosen cell's outline
   is already the focus ring's width, so focus doubles it. */
.game-dialog :is(.primary, .game-cell):focus-visible { outline-color: var(--fg); }
.game-dialog .game-cell[aria-pressed="true"]:focus-visible { outline-style: double; outline-width: 6px; }
.game-diagnoses, .game-suggestions { margin: 0.5rem 0 1rem; }
.game-diagnoses { list-style: none; padding: 0; }
.game-diagnoses li { display: flex; align-items: flex-start; gap: 0.75rem; margin: 0 0 0.75rem; }
.game-diagnoses p { margin: 0; }
.game-suggestions { padding-left: 1.2em; font-style: italic; font-size: 0.9rem; }

/* ── Fingering grid ──────────────────────────────────────────────── */
.fingering-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(9rem, 100%), 1fr));
    gap: 1rem; justify-items: center;
    margin: 1.5em 0; padding: 1rem 0;
    border-top: 1px solid currentColor;
    border-bottom: 1px solid currentColor;
}
.fingering-cell { text-align: center; }

/* ── Lesson list & pagination ────────────────────────────────────── */
.lesson-pagination {
    display: flex; justify-content: space-between; flex-wrap: wrap; gap: 1.5rem;
    margin-top: 2.5rem; padding-top: 1.25rem;
    border-top: 1px solid currentColor;
    font-size: 0.95rem; font-style: italic;
}
.lesson-prev::before { content: "\2190 "; font-style: normal; }
.lesson-next::after  { content: " \2192"; font-style: normal; }

/* Book-to-book links on song-library pages share the pagination design. */
.library-nav {
    margin-top: 2.5rem; padding-top: 1.25rem;
    border-top: 1px solid currentColor;
    font-size: 0.95rem; font-style: italic;
}

ol.lesson-list { list-style: none; padding: 0; margin: 1.5em 0; }
ol.lesson-list .lesson-item {
    margin: 0; padding: 0.6rem 0; line-height: 1.5;
    border-bottom: 1px solid currentColor;
}
.lesson-num {
    display: inline-block; min-width: 2.2rem;
    font-style: italic; font-variant-numeric: tabular-nums;
}
.lesson-desc { display: block; padding-left: 2.2rem; font-style: italic; font-size: 0.9em; }
.lesson-count { font-style: italic; font-size: 0.9em; margin-left: 0.4rem; }

/* ── Glossary ────────────────────────────────────────────────────── */
.glossary-term {
    background: transparent; border: 0; padding: 0; margin: 0;
    color: inherit; font: inherit; cursor: help;
    text-decoration: underline dotted; text-underline-offset: 3px;
}
.glossary-popover {
    position: absolute; z-index: 150;
    max-width: min(22rem, 90vw); padding: 0.6rem 0.8rem;
    background: var(--bg); color: var(--fg);
    border: 1px solid currentColor;
    font-size: 0.9em; line-height: 1.5;
}
.glossary-popover strong {
    display: block; margin-bottom: 0.3em;
    font-style: italic; font-weight: 600;
}

/* ── Print ───────────────────────────────────────────────────────── */
@page { margin: 18mm 16mm; }

@media print {
    /* Paper is always black-on-white, even for dark-mode visitors. The
       custom properties must be reset too: notation SVGs and the shadow-DOM
       fingering charts draw with var(--fg)/var(--bg), and would otherwise
       print white-on-white. */
    :root { --fg: #000; --bg: #fff; color-scheme: light; }

    .skip-link, .site-header, .site-footer, .breadcrumb,
    .lesson-pagination, .library-nav, .lesson-guide, lesson-sidebar, transpose-controls,
    game-launcher, .game-dialog,
    .nav-toggle, .glossary-popover,
    notation-display .playback-controls,
    notation-display .play-cursor { display: none !important; }

    html { font-size: 11pt; line-height: 1.45; }
    html.game-open { overflow: visible; }
    body { color: #000; background: #fff; }
    main { padding: 0; }
    a    { text-decoration: none; color: #000; }
    .glossary-term { text-decoration: none; }
    notation-display { overflow: visible; }

    figure, table, notation-display, exercise-container,
    fingering-chart, .fingering-grid, blockquote, aside, pre {
        break-inside: avoid; page-break-inside: avoid;
    }
    h1, h2, h3 { break-after: avoid; page-break-after: avoid; }
    p, li { orphans: 2; widows: 2; }

    /* Modest provenance line in place of the hidden site footer. */
    main::after {
        content: "learntoplayrecorder.com \00b7\0020 free, no ads, no tracking";
        display: block; max-width: 60rem; margin: 2.5em auto 0;
        padding-top: 0.75em; border-top: 1px solid #000;
        font-size: 0.85em; font-style: italic;
    }
}
