:root {
  color-scheme: dark;
  /* Oracle: a cool instrument palette. The board colors belong to the theme,
     so adding a light theme later means redefining these tokens and nothing
     below them. */
  --bg: #101820;
  --panel: #182430;
  --rule: #2a3947;
  --control-border: #687d8c;
  --ink: #dfe8ee;
  --muted: #8296a5;
  --light: #93a6ae;
  --dark: #4e626e;
  --accent: #f0a93b;
  --on-accent: #101820;
  --pass: #77c295;
  --fail: #e2664f;
  --pick: rgba(240, 169, 59, 0.5);
  --last: rgba(240, 169, 59, 0.34);
  --dot: rgba(16, 24, 32, 0.42);
  /* The high-contrast board setting: opaque marker inks chosen per square
     color, used only under `.high-contrast`. */
  --mark-on-light: #3d2900;
  --mark-on-dark: #ffdf91;
  --ink-on-light: #172530;
  --ink-on-dark: #f8fafc;
  --annotation-green: #238443;
  --annotation-blue: #2463bf;
  --annotation-red: #c53932;
  --annotation-yellow: #c58a08;
  --piece-light: #ffffff;
  --piece-light-edge: #12181d;
  --piece-dark: #151c22;
  --piece-dark-edge: #e6edf1;
}
* { box-sizing: border-box; }
body {
  margin: 0; background: var(--bg); color: var(--ink);
  font: 16px/1.5 system-ui, "Segoe UI", Roboto, sans-serif;
  -webkit-font-smoothing: antialiased;
}
a { color: var(--accent); }
input, select, textarea {
  font: inherit; color: var(--ink); background: var(--bg);
  border: 1px solid var(--control-border); border-radius: 4px; padding: 0.45rem 0.6rem;
  min-width: 0; max-width: 100%;
}
input:focus-visible, select:focus-visible, textarea:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 1px;
}
input[type="checkbox"], input[type="radio"] {
  width: 1.1rem; height: 1.1rem; padding: 0; margin: 0; accent-color: var(--accent);
}
header {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem; padding: 1rem 1.5rem; border-bottom: 1px solid var(--rule);
}
h1 { font-size: 1.25rem; margin: 0; letter-spacing: -0.01em; }
header h1 a { color: inherit; text-decoration: none; }
.due { color: var(--muted); font-variant-numeric: tabular-nums; }
main {
  display: grid; grid-template-columns: minmax(280px, 560px) minmax(240px, 1fr);
  gap: 2rem; padding: 1.5rem; max-width: 1100px; margin: 0 auto;
}
@media (max-width: 760px) { main { grid-template-columns: 1fr; } }
.goal {
  font-size: 1.1rem; font-weight: 600; margin: 0 0 0.75rem; line-height: inherit;
  min-height: 1.5rem; overflow-wrap: break-word; scroll-margin-top: 1rem;
}
/* View headings take programmatic focus so a
   screen reader announces them on navigation; the focus ring would be a
   visual distraction with no keyboard traversal behind it. */
main:focus, #progress:focus, #recommended-name:focus, #goal:focus, #lesson-name:focus, #verdict:focus { outline: none; }
/* The border sits on the wrapper, not the SVG. `inset: 0` resolves against the
   padding box, which excludes the border, while the SVG at `width: 100%` fills
   the content box, so with the border out here the two boxes coincide and a
   cell's center is its square's center. */
.board-wrap {
  position: relative; width: 100%; aspect-ratio: 1;
  border: 1px solid var(--rule); border-radius: 3px;
}
#board { width: 100%; height: 100%; display: block; user-select: none; }
#board.dragging-piece { touch-action: none; cursor: grabbing; }
#board .dragged-piece { pointer-events: none; }
/* Named squares pass pointers through to the SVG, except movable own-piece
   squares when touch dragging is enabled. */
.board-grid {
  position: absolute; inset: 0; display: grid; grid-template-rows: repeat(8, 1fr);
  pointer-events: none;
}
.board-grid [role="row"] { display: grid; grid-template-columns: repeat(8, 1fr); }
/* :focus, not :focus-visible: a cell takes focus only from the keyboard or a
   script, and the ring has to show either way unless the board hid it after a
   pointer choice (`focusCursor({ ring: false })`). */
.board-grid [role="gridcell"]:focus { outline: 2px solid var(--accent); outline-offset: -2px; }
/* The high-contrast ring's two edges keep it visible on either square color.
   Forced colors leave the SVG board's own colors in place but would drop the
   inner edge and recolor the outer one, so this ring opts out as well. */
.board-grid.high-contrast [role="gridcell"]:focus {
  outline-color: var(--ink-on-light);
  box-shadow: inset 0 0 0 4px var(--ink-on-dark);
  forced-color-adjust: none;
}
.board-grid.pointer-focus [role="gridcell"]:focus { outline: none; box-shadow: none; }
/* study-layout.js sets body.study only for the phone layout. Feedback's
   compact label and sent-message live region stay available to screen readers. */
.visually-hidden,
.text-only:not(:focus),
.skip-link:not(:focus),
.lesson-list .skill-kind,
body.study #report.feedback-compact .button-label,
body.study .study-toolbar.paging-compact #example-controls .button-label,
body.study .study-switch .button-label,
body.study #report-status {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip: rect(0, 0, 0, 0); clip-path: inset(50%); white-space: nowrap; border: 0;
}
#board .light { fill: var(--light); }
#board .dark { fill: var(--dark); }
#board .last { fill: var(--last); }
#board .your-move { fill: var(--last); opacity: 0.4; }
#board.high-contrast .your-move { fill: none; stroke-width: 0.045; stroke-dasharray: 0.1 0.06; opacity: 1; }
#board.high-contrast .your-move.on-light { stroke: var(--mark-on-light); }
#board.high-contrast .your-move.on-dark { stroke: var(--mark-on-dark); }
#board .selected { fill: var(--pick); }
#board .target { fill: var(--dot); }
#board.high-contrast { --light: #bacad1; --dark: #3d5360; }
#board.high-contrast .last, #board.high-contrast .selected { fill: none; stroke-width: 0.075; }
#board.high-contrast .selected { stroke-dasharray: 0.15 0.06; }
#board.high-contrast .last.on-light, #board.high-contrast .selected.on-light { stroke: var(--mark-on-light); }
#board.high-contrast .last.on-dark, #board.high-contrast .selected.on-dark { stroke: var(--mark-on-dark); }
#board.high-contrast .target.on-light { fill: var(--ink-on-light); }
#board.high-contrast .target.on-dark { fill: var(--ink-on-dark); }
#board.high-contrast .target.occupied { fill: none; stroke-width: 0.07; }
#board.high-contrast .target.occupied.on-light { stroke: var(--ink-on-light); }
#board.high-contrast .target.occupied.on-dark { stroke: var(--ink-on-dark); }
.chess-board .piece { pointer-events: none; user-select: none; }
/* Browsers decide panning at gesture start. Only movable pieces opt out when
   touch dragging is enabled; the rest of the board keeps native scrolling. */
.board-grid .touch-draggable { pointer-events: auto; touch-action: none; }
/* A coordinate takes the color of the opposite square, so the labels stay tied
   to the board tokens and follow the theme. */
#board .coord {
  font-size: 0.2px; font-weight: 600; text-anchor: start;
  font-family: system-ui, sans-serif; pointer-events: none; user-select: none;
}
#board .coord.on-light { fill: var(--dark); }
#board .coord.on-dark { fill: var(--light); }
/* The halo in the square's own color keeps a label readable where a
   high-contrast marker border crosses it. */
#board.high-contrast .coord { paint-order: stroke; stroke-width: 0.06px; stroke-linejoin: round; }
#board.high-contrast .coord.on-light { stroke: var(--light); }
#board.high-contrast .coord.on-dark { stroke: var(--dark); }
.note { color: var(--muted); font-style: italic; }
.controls { display: flex; flex-wrap: wrap; align-items: center; gap: 0.75rem; margin-top: 0.75rem; }
.promotion:not([hidden]) {
  display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; margin-top: 0.75rem;
}
button {
  font: inherit; padding: 0.45rem 1rem; border-radius: 3px; cursor: pointer;
  border: 1px solid var(--control-border); background: transparent; color: var(--ink);
}
button:hover { border-color: var(--accent); color: var(--accent); }
#next {
  background: var(--accent); border-color: var(--accent);
  color: var(--on-accent); font-weight: 600;
}
#next:hover { filter: brightness(1.08); color: var(--on-accent); }
button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
a:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.result {
  background: var(--panel); border: 1px solid var(--rule); border-radius: 4px;
  padding: 1rem 1.25rem; align-self: start;
}
.result h2 { margin-top: 0; }
.result.pass h2 { color: var(--pass); }
.result.fail h2 { color: var(--fail); }
.result h3 { margin-bottom: 0.25rem; }
.result p { margin: 0.25rem 0 0.75rem; color: var(--muted); }
.verdict-line { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0 0.5rem; margin-bottom: 0.75rem; }
.verdict-line #verdict, .verdict-line #reason { margin: 0; }
[hidden] { display: none !important; }
/* The text-only page link and the skip link to main both stay off screen
   until keyboard focus reaches them. */
.text-only, .skip-link {
  position: absolute; left: 0.5rem; top: 0.5rem; z-index: 10; padding: 0.5rem 0.75rem;
  background: var(--panel); color: var(--ink); border: 1px solid var(--accent); border-radius: 4px;
}
.header-summary {
  display: flex; flex: 1 1 auto; align-items: baseline; justify-content: space-between;
  gap: 1rem; min-width: 0;
}
.header-actions { display: flex; align-items: center; gap: 1rem; min-width: 0; }
#sound .icon {
  display: none; width: 1rem; height: 1rem; fill: none; stroke: currentColor;
  stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round;
}
#sound[aria-pressed="true"] .sound-muted, #sound[aria-pressed="false"] .sound-waves { display: none; }
@media (max-width: 760px) {
  #sound .icon { display: block; }
  #sound .sound-label { display: none; }
}
.dashboard { grid-column: 1 / -1; }
.dashboard-top { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1.25rem; }
.dashboard > .progress-strip { margin: 1rem 0; }
.dashboard > .progress-strip:first-child { margin-top: 0; }
.dashboard-help { display: flex; flex-wrap: wrap; align-items: start; gap: 0.5rem 1.5rem; }
.dashboard-help > details { flex: 1 1 16rem; }
.eyebrow { color: var(--accent) !important; font-size: 0.75rem; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; }
.small { font-size: 0.85rem; }
.muted { color: var(--muted); }
.curriculum-heading { margin-bottom: 0; }
.curriculum-stage { border-top: 1px solid var(--rule); }
.curriculum-stage > summary { padding: 0.75rem 0; }
.stage-name { font-weight: 600; }
.stage-progress { margin-left: 0.75rem; color: var(--muted); font-size: 0.82rem; white-space: nowrap; }
.curriculum-stage[open] > summary { margin-bottom: 0.25rem; }
.curriculum-stage > .lesson-list { margin-bottom: 0.75rem; }
.lesson-list { list-style: none; padding: 0; margin: 0; display: grid; gap: 0.5rem; }
.curriculum-skills-header,
.lesson-list button {
  display: grid; grid-template-columns: minmax(8rem, 1.2fr) minmax(10rem, 1fr) minmax(10rem, 1fr);
  column-gap: 1rem; align-items: start;
}
.curriculum-skills-header {
  padding: 0 1rem 0.35rem; color: var(--muted); font-size: 0.72rem; font-weight: 600;
  letter-spacing: 0.08em; text-transform: uppercase;
}
.lesson-list button { text-align: left; width: 100%; padding: 0.8rem 1rem; background: var(--panel); }
.lesson-name { display: grid; gap: 0.12rem; min-width: 0; }
.lesson-enrollment { color: var(--muted); font-size: 0.82rem; }
.lesson-list button.lesson-not-started { display: block; }
.lesson-not-started .lesson-name { display: flex; justify-content: space-between; align-items: baseline; gap: 0.75rem; }
.lesson-not-started .lesson-name > :first-child { min-width: 0; }
.lesson-not-started-marker { color: var(--muted); font-size: 0.82rem; white-space: nowrap; }
.skill-column { display: grid; gap: 0.1rem; min-width: 0; color: var(--muted); font-size: 0.82rem; }
.skill-kind { font-size: 0.72rem; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; }
.lesson-proficiency { color: var(--ink); font-size: 0.85rem; font-weight: 700; }
.lesson-proficiency[data-proficiency="difficult"] { color: var(--fail); }
.lesson-proficiency[data-proficiency="learned"] { color: var(--pass); }
.lesson-proficiency[data-proficiency="mastered"] { color: var(--accent); }
.lesson-stats { font-variant-numeric: tabular-nums; }
.concept-performance {
  display: grid; grid-template-columns: 1fr 1fr; gap: 1rem;
  margin: -0.5rem 0 1rem; color: var(--muted); font-size: 0.85rem;
}
#lesson-actions:not(:empty) { margin-bottom: 1.5rem; }
button.primary { background: var(--accent); border-color: var(--accent); color: var(--on-accent); font-weight: 600; }
button.primary:hover { filter: brightness(1.08); color: var(--on-accent); }
button:disabled { opacity: 0.5; cursor: default; }
button:disabled:hover { filter: none; }
#evaluation button:disabled:hover { border-color: var(--rule); color: var(--ink); }
#coaching-lines button[aria-pressed="true"] {
  opacity: 1; background: var(--panel); border-color: var(--accent); color: var(--accent);
  box-shadow: inset 0 0 0 1px var(--accent); font-weight: 600;
}
#coaching-lines button[aria-pressed="true"]::before { content: "✓ "; content: "✓ " / ""; }
#result { scroll-margin-top: 1rem; }
.error { max-width: 1052px; margin: 1rem auto 0; padding: 0.75rem 1rem; color: var(--ink); border: 1px solid var(--fail); background: var(--panel); }
#example-text { color: var(--muted); }
.example-annotation { border-left: 3px solid var(--accent); padding-left: 0.8rem; margin: 1rem 0; }
.example-annotation #example-text { color: var(--ink); }
.example-annotation h3 { margin: 0 0 0.4rem; }
.example-annotation p { margin: 0.4rem 0; }
.worked-example { align-self: start; position: sticky; top: 1rem; scroll-margin-top: 1rem; }
.worked-example .board-wrap { width: min(100%, 52vh); margin: 0 auto; }
@media (min-width: 761px) {
  .finished-result .board-wrap { width: min(100%, 52vh); margin: 0 auto; }
}
/* The move tree reads like a book line: moves flow inline, and each variation
   is an indented block beneath the move it replaces. */
.move-tree, .move-tree .variation {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.1rem 0.35rem;
  font-variant-numeric: tabular-nums; line-height: 1.6;
}
.move-tree { margin: 0.5rem 0 1rem; }
.move-tree button {
  padding: 0 0.3rem; border: 0; border-radius: 3px; background: transparent;
  color: var(--ink); font-weight: 500; white-space: nowrap;
}
.move-tree button:hover { color: var(--accent); background: color-mix(in srgb, var(--accent) 14%, transparent); }
.move-tree .variation {
  flex-basis: 100%; margin: 0.2rem 0 0.2rem 0.5rem; padding-left: 0.6rem;
  border-left: 2px solid var(--rule); font-size: 0.9rem;
}
.move-tree .variation button { color: var(--muted); font-weight: 400; }
.move-tree .variation-title { flex-basis: 100%; text-align: left; font-style: italic; white-space: normal; }
/* Score trees introduce a branch the way a printed game does it: a
   short lead-in, then the notation that opens the line, on one wrapping run.
   The parentheses hang off the lead-in and the last move rather than off the
   block, so the comment seated among the moves cannot separate a bracket from
   the notation; :last-of-type passes over the comment, which is not a button. */
.score-tree .variation-title { flex-basis: auto; }
.score-tree .variation-title::before { content: "("; }
.score-tree .variation-title::after { content: ":"; }
.score-tree .variation > button:last-of-type::after { content: ")"; }
/* Both ends of a pair read as punctuation rather than taking the color of the
   button they hang off, except inside the selected move, where muted ink on
   the accent measures 1.5:1 and the pill's own ink measures 8.9:1. */
.score-tree .variation-title::before,
.score-tree .variation-title::after,
.score-tree .variation > button:last-of-type::after { color: var(--muted); }
.score-tree .variation button[aria-current]::before,
.score-tree .variation button[aria-current]::after { color: inherit; }
.move-tree .variation[data-kind="mistake"] { border-left-color: var(--fail); }
.move-tree .variation[data-kind="mistake"] .variation-title { color: var(--fail); }
.move-tree button[aria-current] { background: var(--accent); color: var(--on-accent); }
/* The selected move sets its own ink, which the branch color above outranks by
   specificity: a mistake lead-in would print at 1.7:1 on the accent. */
.move-tree .variation[data-kind="mistake"] button[aria-current] { color: var(--on-accent); }
.move-tree button[aria-current]:hover { color: var(--on-accent); }
/* The comment takes its own row beside the move it explains and keeps full
   prose size inside the smaller variation type. */
.move-tree .example-annotation {
  flex-basis: 100%; margin: 0.4rem 0; font-size: 1rem; font-style: normal;
  border-left-width: 2px;
}
.move-tree .example-annotation h3 { font-size: 1rem; }
#execution-analysis-moves .variation-title { overflow-wrap: anywhere; }
#execution-analysis-moves .variation { min-width: 0; }
#execution-analysis-moves button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
#execution-analysis-detail p { overflow-wrap: anywhere; }
.move-tree .example-annotation.empty-comment { flex-basis: 0; margin: 0; padding: 0; border: 0; }
#result.linked-analysis .verdict-line { margin-bottom: 0.35rem; }
#result.linked-analysis #verdict { font-size: 1.25rem; }

/* The glyph is drawn with an empty alternative text, so a screen reader and a
   copied selection get the letter beside it instead. The letter keeps its place
   in the line at zero size; an absolutely positioned hidden letter would split
   innerText and screen-reader lines at every figurine. */
.figurine::before {
  font-family: "DejaVu Sans", "Noto Sans Symbols 2", "Segoe UI Symbol", sans-serif;
  font-weight: normal;
}
.figurine[data-piece="K"]::before { content: "\265A"; content: "\265A" / ""; }
.figurine[data-piece="Q"]::before { content: "\265B"; content: "\265B" / ""; }
.figurine[data-piece="R"]::before { content: "\265C"; content: "\265C" / ""; }
.figurine[data-piece="B"]::before { content: "\265D"; content: "\265D" / ""; }
.figurine[data-piece="N"]::before { content: "\265E"; content: "\265E" / ""; }
.figurine-letter { font-size: 0; }
@media (max-width: 760px) {
  .worked-example { position: static; }
  /* The score, and the comment seated inside it, would otherwise sit below
     other text in the lesson or result panel; move it up next to the board. */
  #lesson, #result { display: flex; flex-direction: column; }
  #example-detail { order: -1; }
  header { align-items: stretch; flex-direction: column; gap: 0.5rem; padding: 0.75rem 1rem; }
  .header-summary { flex: none; align-items: center; gap: 0.75rem; }
  .header-summary h1 { flex: 0 0 auto; white-space: nowrap; font-size: clamp(1rem, 4.2vw, 1.125rem); }
  .due { min-width: 0; font-size: 0.75rem; line-height: 1.25; text-align: right; white-space: normal; }
  .header-actions { justify-content: space-between; gap: 0.75rem; width: 100%; }
  .header-actions > button { flex: 0 0 auto; }
  header button, header .account a, header .account [aria-current] { min-height: 2.75rem; }
  header .account {
    min-width: 0; flex: 1 1 auto; flex-wrap: nowrap; justify-content: flex-end;
    align-items: center; gap: 0.625rem;
  }
  header .account #who { min-width: 0; max-width: none; text-align: right; }
  header .account a, header .account [aria-current] { display: inline-flex; align-items: center; flex: 0 0 auto; }
  header .account button { flex: 0 0 auto; }
  main { padding: 1rem; gap: 1rem; }
  .dashboard-top { grid-template-columns: 1fr; }
  .stage-progress { display: block; margin-left: 1.2rem; }
  .curriculum-skills-header { display: none; }
  .lesson-list button {
    grid-template-columns: 1fr 1fr;
    grid-template-areas: "name name" "eval exec";
    row-gap: 0.4rem;
  }
  .lesson-list .lesson-name { grid-area: name; }
  .lesson-list .skill-column[data-kind="evaluation"] { grid-area: eval; }
  .lesson-list .skill-column[data-kind="execution"] { grid-area: exec; }
  .lesson-list .skill-kind {
    position: static; width: auto; height: auto; margin: 0; overflow: visible;
    clip: unset; clip-path: none; white-space: normal;
  }
  .promotion:not([hidden]) {
    display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 0.5rem;
  }
  .promotion span { grid-column: 1 / -1; line-height: 1.25; }
  .promotion button { min-width: 0; min-height: 2.75rem; padding: 0.45rem 0.25rem; }
  #promotion-back { grid-column: 1 / -1; }
  .error { margin: 1rem; }
}

/* Outside the study layout a phone still caps a scored board so the score
   below it starts on the first screen. */
@media (max-width: 760px) {
  #play.scored #board { width: min(100%, 52vh); margin: 0 auto; }
}
/* The height of the viewport with any mobile browser toolbar showing. */
.small-viewport {
  position: fixed; top: 0; left: 0; width: 0; height: 100vh; height: 100svh;
  visibility: hidden; pointer-events: none;
}
@media (max-width: 380px) {
  .header-summary { align-items: flex-start; flex-direction: column; gap: 0; }
  .due { text-align: left; }
  header .account:has(#sound) { flex-wrap: wrap; }
}

.account { display: flex; flex-wrap: wrap; gap: 0.75rem; align-items: baseline; color: var(--muted); }
/* Capping a long display name, which then ends in an ellipsis, bounds the
   account bar so a wide screen keeps the whole header on one row. */
.account #who { max-width: 12em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.account a { color: var(--ink); }
.account [aria-current] { color: var(--ink); }
.account button { padding: 0.25rem 0.6rem; }
.auth-main { display: block; max-width: 440px; margin: 2rem auto; padding: 0 1.5rem; }
.auth-intro { margin: 0 0 1rem; color: var(--muted); }
.auth-form {
  display: flex; flex-direction: column; gap: 0.9rem;
  background: var(--panel); border: 1px solid var(--rule); border-radius: 6px; padding: 1.25rem 1.5rem;
}
.auth-form h2 { margin: 0; }
.auth-form > p { margin: 0; overflow-wrap: anywhere; }
.auth-form form { display: flex; flex-direction: column; gap: 0.9rem; }
.auth-form label { display: flex; flex-direction: column; gap: 0.3rem; font-weight: 600; }
.auth-form small { font-weight: 400; color: var(--muted); }
.auth-form .checkbox-field { display: flex; flex-direction: column; gap: 0.3rem; }
.auth-form .checkbox-field label { flex-direction: row; align-items: center; gap: 0.5rem; }
.password-field { display: grid; grid-template-columns: 1fr auto; gap: 0.3rem 0.5rem; }
.password-field label { grid-column: 1 / -1; font-weight: 600; }
.password-field input { min-width: 0; }
.password-field small { grid-column: 1 / -1; }
.message { margin: 0; padding: 0.6rem 0.8rem; border-radius: 4px; background: var(--rule); color: var(--ink); }
.message.error { background: var(--fail); color: var(--on-accent); }
.auth-form .message { max-width: none; border: 0; }
.auth-form a { color: var(--accent); }
.account-main { max-width: 1000px; }
.account-settings { background: none; border: 0; padding: 0; }
.account-sections { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1.25rem; align-items: start; }
.account-preferences { display: grid; gap: 1.25rem; min-width: 0; }
.account-section {
  display: flex; flex-direction: column; gap: 1rem; min-width: 0;
  padding: 1.25rem; background: var(--panel); border: 1px solid var(--rule); border-radius: 6px;
}
.account-section h3, .account-section h4, .account-section p { margin: 0; }
.account-section p { overflow-wrap: anywhere; }
.account-section h3 { font-size: 1.125rem; }
.account-section h4 { font-size: 1rem; margin-bottom: 0.75rem; }
.account-section form + form, .account-section #google-link { border-top: 1px solid var(--rule); padding-top: 1rem; }
.account-section input, .account-section select { min-width: 0; max-width: 100%; }
.account-section button { align-self: flex-start; max-width: 100%; }
#board-setting-status { color: var(--ink); }
#account-security > #set { display: flex; flex-direction: column; gap: 0.9rem; }
#account-security > #second-factor { border-color: var(--rule); }
#account-deletion.account-section {
  grid-column: 1 / -1; border: 1px solid var(--fail); padding: 1.25rem;
}
.account-settings > .links { margin-top: 0.35rem; }
@media (max-width: 760px) {
  .account-main { margin: 1.25rem auto; padding: 0 1rem; }
  .account-sections { grid-template-columns: 1fr; gap: 1rem; }
  .account-preferences { gap: 1rem; }
  .account-section { padding: 1rem; }
}
#account-deletion { border-top: 1px solid var(--muted); padding-top: 1rem; }
#account-deletion h3 { margin-top: 0; }
#second-factor { border-top: 1px solid var(--muted); padding-top: 1rem; }
#second-factor h4 { margin-top: 0; }
.second-factor-status { display: grid; gap: 0.35rem; margin: 0.75rem 0; padding: 0; }
.second-factor-status > div { display: flex; justify-content: space-between; gap: 1rem; margin: 0; }
.second-factor-status dt { margin: 0; color: var(--muted); }
.second-factor-status dd { margin: 0; font-weight: 600; }
#totp-secret, #totp-uri { overflow-wrap: anywhere; }
/* `.auth-form a` colors every anchor, so without this the URI reads as a
   link on a pointer that never gets an href. */
#totp-uri:not([href]) { color: inherit; }
#totp-qr { display: flex; justify-content: center; }
#totp-qr svg { width: 100%; max-width: 15rem; height: auto; border-radius: 4px; }
#recovery-codes-list { display: grid; gap: 0.3rem; margin: 0.5rem 0; padding-left: 1.25rem; font-variant-numeric: tabular-nums; }
button.danger { color: var(--fail); border-color: currentColor; }
button.primary.danger { background: var(--fail); border-color: var(--fail); color: var(--on-accent); }
.links { margin: 0; color: var(--muted); }
.links a { color: var(--ink); }
.google {
  text-align: center; padding: 0.45rem 1rem; border: 1px solid var(--control-border); border-radius: 4px;
  color: var(--ink); text-decoration: none;
}
.google:hover { border-color: var(--accent); }

.chess-board .annotation-highlights, .chess-board .annotation-arrows { pointer-events: none; }
.chess-board .annotation-square { fill: currentColor; fill-opacity: 0.35; }
.chess-board .annotation-box { fill: currentColor; fill-opacity: 0.16; stroke: currentColor; stroke-width: 0.07; }
.chess-board .annotation-arrow { fill: currentColor; fill-opacity: 0.85; }
.chess-board.high-contrast .annotation-square { stroke: currentColor; stroke-width: 0.07; }
.chess-board.high-contrast .annotation-square-outline { fill: none; stroke-width: 0.14; }
.chess-board.high-contrast .annotation-square-outline.on-light { stroke: var(--ink-on-light); }
.chess-board.high-contrast .annotation-square-outline.on-dark { stroke: var(--ink-on-dark); }
.chess-board.high-contrast .annotation-box { stroke: var(--ink-on-dark); stroke-width: 0.06; }
.chess-board.high-contrast .annotation-box-outline { fill: none; stroke: var(--ink-on-light); stroke-width: 0.13; }
.chess-board.high-contrast .annotation-arrow-outline { fill: var(--ink-on-light); stroke: var(--ink-on-light); stroke-width: 0.12; stroke-linejoin: round; }
.chess-board.high-contrast .annotation-arrow { fill-opacity: 1; stroke: var(--ink-on-dark); stroke-width: 0.06; stroke-linejoin: round; paint-order: stroke; }

.progress-strip {
  grid-column: 1 / -1; background: var(--panel); border: 1px solid var(--rule);
  border-radius: 8px; padding: 1rem 1.25rem;
}
.progress-strip:not([hidden]) { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.5rem 1rem; }
.stat { display: flex; flex-direction: column; font-variant-numeric: tabular-nums; }
.stat-label { color: var(--muted); font-size: 0.8rem; }
.stat strong { font-size: 1.5rem; color: var(--accent); }
.daily-status { grid-column: 1 / -1; margin: 0.35rem 0 0; color: var(--muted); font-size: 0.875rem; }
.daily-status.complete { color: var(--accent); }
.attempt-progress { margin: -0.15rem 0 0.75rem; font-size: 0.875rem; font-variant-numeric: tabular-nums; }
.attempt-progress:not([hidden]) { display: flex; gap: 0.4rem 0.75rem; align-items: center; flex-wrap: nowrap; }
.attempt-progress > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#attempt-points { color: var(--accent); font-weight: 600; }
.badge-notice {
  position: fixed; z-index: 15; right: 1rem; bottom: 1rem; width: min(340px, calc(100% - 2rem));
  max-height: min(300px, 50dvh); overflow-y: auto; display: flex; align-items: start; gap: 0.75rem;
  padding: 0.85rem; border: 1px solid var(--accent); border-radius: 8px; background: var(--panel);
  box-shadow: 0 4px 20px #0006; font-size: 0.875rem;
}
.badge-notice > div { flex: 1; min-width: 0; }
.badge-notice-mark { color: var(--accent); font-size: 1.5rem; }
.badge-notice-heading { color: var(--accent); font-weight: 600; margin: 0 0 0.3rem; }
#badge-notice-list { list-style: none; padding: 0; margin: 0; }
#badge-notice-list li + li { margin-top: 0.5rem; }
#badge-notice-list p { margin: 0.2rem 0 0; color: var(--muted); }
.badge-notice-actions { display: flex; align-items: center; justify-content: space-between; gap: 1rem; margin-top: 0.5rem; }
.badge-notice-actions button { padding: 0.3rem 0.6rem; }
@media (max-width: 760px) {
  body.study .badge-notice { bottom: calc(4.5rem + env(safe-area-inset-bottom)); }
}
.scoring-help { color: var(--muted); font-size: 0.875rem; }
summary { cursor: pointer; }
summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.due-forecast { border-top: 1px solid var(--rule); margin-top: 1rem; padding-top: 1rem; }
.due-forecast h2 { font-size: 1rem; margin: 0 0 0.75rem; }
.forecast-stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 0.5rem 1rem; }
.badge-collection h2 { font-size: 1.125rem; margin: 0 0 0.5rem; }
.badge-collection .links { margin-top: 1rem; }
#badge-count { color: var(--muted); font-size: 0.875rem; font-weight: 400; margin-left: 0.4rem; }
.progress-links { grid-column: 1 / -1; margin: 0; }
.progress-links a { color: var(--accent); }
.progress-links > * + * { margin-left: 0.75rem; }
main > .progress-links { margin-bottom: 1rem; }
/* The header has room for the badges and history items only above the mobile
   layout, which is also where the progress strip would otherwise repeat the
   links. The selector matches the mobile account-item rules so it is not
   overridden, and the query is the exact complement of that block, so every
   width including a fractional one shows one set of links and only one. */
header .account .header-badges, header .account .header-history { display: none; }
@media not all and (max-width: 760px) {
  header .account .header-badges, header .account .header-history { display: inline; }
  .progress-links { display: none; }
}
.badge-main { display: block; max-width: 900px; margin: 0 auto; padding: 1.5rem; }
.badges { list-style: none; padding: 0; margin: 0.75rem 0 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(145px, 1fr)); gap: 0.6rem; }
.badge { border: 1px solid var(--rule); border-radius: 6px; padding: 0.75rem; font-size: 0.8rem; }
.badge.earned { background: var(--panel); border-color: var(--accent); }
.badge h3 { font-size: 0.875rem; margin: 0 0 0.3rem; }
.badge p { color: var(--muted); margin: 0.25rem 0; }
.badge .badge-state { color: var(--accent); font-weight: 600; }
.badge progress { display: block; width: 100%; height: 0.4rem; margin-top: 0.5rem; accent-color: var(--accent); }
.history h2 { font-size: 1.125rem; margin: 0 0 0.5rem; }
.history .links { margin-top: 1rem; }
.history-filters { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-bottom: 16px; }
.history-unscored { display: flex; align-items: center; gap: 0.3rem; }
.history-list { list-style: none; padding: 0; margin: 0; }
.history-row { border-top: 1px solid var(--rule); padding: 12px 0; }
.history-row h3 { margin: 0 0 4px; }
.history-row p { margin: 0 0 4px; }
@media (max-width: 520px) {
  .forecast-stats { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 400px) {
  .stat strong { font-size: 1.2rem; }
  .progress-strip { padding: 0.75rem; }
  .attempt-progress { gap: 0.25rem 0.5rem; font-size: clamp(0.75rem, 3.4vw, 0.875rem); }
}

#report-dialog { color: var(--ink); background: var(--panel); border: 1px solid var(--rule); border-radius: 8px; width: min(32rem, calc(100% - 2rem)); max-height: calc(100% - 2rem); padding: 1.25rem; }
#report-dialog::backdrop { background: rgb(0 0 0 / 55%); }
#report-dialog h2 { margin-top: 0; }
#report-form label { display: block; margin: 1rem 0 0.4rem; }
#report-form select, #report-form textarea { width: 100%; }
#report-form textarea { resize: vertical; }
#report-form .controls { margin-top: 1rem; }
#report-form .error { margin: 0.75rem 0; }
#report-status:empty { margin: 0; }

/* Outside the phone study layout the grid places the panels as if their
   pane wrapper were not there. */
.study-pane { display: contents; }
.exercise-panel { align-self: start; min-width: 0; }
.exercise-panel #evaluation .controls { flex-direction: column; }
.exercise-panel #evaluation button { min-height: 2.75rem; }
.played-moves-scroll {
  max-height: min(55vh, 30rem); overflow-y: auto; overscroll-behavior: contain;
  scroll-padding: 0.5rem; border-radius: 3px;
}
.played-moves-scroll:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.played-moves { list-style: none; margin: 0; padding: 0; font-variant-numeric: tabular-nums; }
.played-moves li { padding: 0.2rem 0.5rem; }
.played-moves li:nth-child(even) { background: var(--bg); }
#execution-panel #fifty-move-count { margin: 1rem 0 0; }
.pane-actions:empty { display: none; }
.icon {
  flex: none; width: 1.25rem; height: 1.25rem;
  fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round;
}
/* Outside the phone toolbar an icon sits before its button's visible label,
   and paging is one joined group centered under the board. */
body:not(.study) :is(#play, #study-pane) button:has(> .icon) { display: inline-flex; align-items: center; gap: 0.4rem; }
body:not(.study) #example-controls { gap: 0; justify-content: center; }
body:not(.study) #example-controls button { border-radius: 0; }
body:not(.study) #example-controls button + button { margin-left: -1px; }
body:not(.study) #example-controls button:first-child { border-radius: 3px 0 0 3px; }
body:not(.study) #example-controls button:last-child { border-radius: 0 3px 3px 0; }
body:not(.study) #example-controls button:is(:hover, :focus-visible) { position: relative; z-index: 1; }
/* study-layout.js seats a finished result's actions after its verdict line
   outside the phone layout. An empty row takes no space, so paging sits as
   close under the board on a lesson, whose actions stay in its card, as on a
   result. */
#result > #primary-controls { margin: 0 0 0.5rem; }
body:not(.study) #primary-controls:not(:has(> :not([hidden]))) { display: none; }
/* Feedback ends the open card, apart from the actions that move on. */
.card-feedback {
  display: flex; flex-wrap: wrap; align-items: center; gap: 0.25rem 0.75rem;
  margin-top: 1rem; padding-top: 0.75rem; border-top: 1px solid var(--rule);
}
.card-feedback:not(:has(> :not([hidden], :empty))) { display: none; }
.card-feedback p { margin: 0; }
.card-feedback #report { padding: 0.3rem 0.75rem; font-size: 0.875rem; }
/* In the study layout Feedback's icon shows a check badge while its status
   says it was sent; elsewhere the status text itself is visible. */
.sent-badge { display: none; }
.sent-badge circle { fill: var(--pass); stroke: var(--bg); stroke-width: 2; }
.sent-badge path { fill: none; stroke: var(--bg); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }

/* Phone study layout: the page stops scrolling, the board keeps one size and
   place, the explanation pane scrolls on its own and the toolbar sits at the
   bottom. study-layout.js sets the class and --study-board. */
@media (max-width: 760px) {
  body.study { height: 100vh; height: 100dvh; display: flex; flex-direction: column; overflow: hidden; }
  body.study > header { display: none; }
  body.study .error { margin: 0.5rem 0.75rem 0; }
  body.study main {
    flex: 1 1 auto; min-height: 0; width: 100%;
    display: flex; flex-direction: column; gap: 0.5rem; padding: 0.5rem 0.75rem 0;
  }
  body.study #play { display: contents; }
  body.study .play-status { order: 1; display: flex; flex-wrap: nowrap; align-items: baseline; gap: 0 0.5rem; }
  /* Sound and Home share the goal's line at its right end, no taller than that line.
     A review's points strip sits between them while the goal, strip and both buttons
     fit the row at their full widths; study-layout.js measures them in the
     strip-measure state and sets strip-wrap when they do not, which moves the
     strip to a line of its own at full width. */
  body.study .play-status > #home, body.study .play-status > #sound {
    order: 1; flex: none; align-self: center; margin-left: auto; height: 1.5rem;
    padding: 0 0.6rem; font-size: 0.875rem; line-height: 1;
  }
  body.study .play-status > #sound { margin-left: 0; width: 1.5rem; padding: 0; }
  body.study .play-status > #sound .icon { margin: 0 auto; }
  body.study .play-status > .attempt-progress { flex: none; }
  body.study .play-status.strip-measure > * { flex: none; }
  body.study .play-status.strip-wrap { flex-wrap: wrap; }
  body.study .play-status.strip-wrap > .goal { flex-basis: 0; }
  body.study .play-status.strip-wrap > .attempt-progress { order: 2; flex-basis: 100%; }
  /* The goal stays one line: a long goal truncates rather than widening the
     page, and a lesson goal truncates its name before the side to move. */
  body.study .goal {
    flex: 1 1 auto; min-width: 0; margin: 0; min-height: 0; font-size: 1rem;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  body.study .goal:has(.goal-name) { display: flex; }
  body.study .goal-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
  body.study .goal-turn { flex: none; white-space: pre; }
  /* The status row holds the header's Home button, which also leaves history. */
  body.study #back-home { display: none; }
  body.study .attempt-progress { margin: 0; }
  body.study .board-wrap { order: 2; flex: none; align-self: center; width: var(--study-board, min(100%, 50svh)); }
  body.study #play #board { width: 100%; margin: 0; }
  body.study #evaluation, body.study #promotion { display: none; }
  /* Relative, so visually hidden text inside the pane scrolls with it rather
     than extending the page. */
  body.study .study-pane {
    order: 3; display: block; position: relative; flex: 1 1 0; min-height: 0;
    overflow-y: auto; overscroll-behavior: contain; scroll-padding: 0.5rem 0;
  }
  /* The ring sits outside the scroll box, in the gap and padding around it,
     so the scrolled cards cannot paint over it. A pane focused by a press
     shows none, even where the browser treats that focus as visible. */
  body.study .study-pane:focus { outline: none; }
  body.study .study-pane:focus-visible:not([data-pointer]) { outline: 2px solid var(--accent); outline-offset: 1px; }
  body.study .study-toolbar {
    order: 4; display: flex; align-items: center; gap: 0.25rem;
    margin: 0 -0.75rem; padding: 0.25rem 0.5rem calc(0.25rem + env(safe-area-inset-bottom));
    border-top: 1px solid var(--rule); background: var(--bg);
  }
  body.study #example-controls, body.study #primary-controls { margin: 0; gap: 0.25rem; flex-wrap: nowrap; }
  body.study #primary-controls { margin-left: auto; min-width: 0; }
  body.study #example-controls button, body.study #primary-controls #report {
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    min-width: 3.25rem; min-height: 2.75rem; padding: 0.2rem;
    font-size: 0.75rem; line-height: 1.1; border-color: transparent;
  }
  body.study #primary-controls #report { flex: none; white-space: nowrap; position: relative; }
  body.study #report.sent .sent-badge {
    display: block; position: absolute; top: 0; left: calc(50% + 0.2rem); width: 14px; height: 14px;
  }
  /* Compact icon targets keep their physical width when text is enlarged. */
  body.study #primary-controls #report.feedback-compact { min-width: 44px; }
  body.study .study-toolbar.paging-compact #example-controls button { min-width: 44px; }
  body.study #primary-controls .study-switch { flex: none; min-width: 44px; padding: 0.2rem; }
  body.study .study-toolbar .icon { display: block; }
  /* Feedback's button joins the toolbar; its row keeps only the hidden status. */
  body.study .card-feedback { display: contents; }
  /* app.js seats the comment in the pane once the layout applies; until then
     it must not add its height to the toolbar being measured. */
  body.study .study-toolbar > #example-annotation { display: none; }
  /* Smaller text lets a long primary label such as "Next evaluation check"
     keep two lines beside Feedback on a 360 px phone. */
  body.study #primary-controls button {
    min-width: 2.75rem; min-height: 2.75rem; padding: 0.3rem; overflow-wrap: anywhere;
    font-size: 0.875rem; line-height: 1.2;
  }
  body.study #primary-controls #next { min-width: 3.5rem; }
  body.study .verdict-line #verdict { font-size: 1.25rem; }
  #execution-analysis-moves button { min-width: 2.75rem; min-height: 2.75rem; }
  body.study .study-toolbar.empty { display: none; }
  body.study .pane-actions { margin: 0.75rem 0; }
}
