/* Langraph study screen — self-contained, no external assets. */

:root {
  --bg: #fbfaf7;
  --surface: #ffffff;
  --border: #d9d4c9;
  --text: #232019;
  --muted: #6d675c;
  --accent: #276245;
  --accent-soft: #e3efe8;
  --error: #a32b1c;
  --error-soft: #fbeae7;
  /* Derived forms in a paradigm are green, and grey already means
     'nothing here'. Shared with the admin palette, which has had
     this since feature 028. */
  --ok: #1f7a3d;
  --radius: 10px;
  /* One column for the header and the page under it. */
  --column: 800px;
  /* The desktop ruler (078 G2): the rail's width, paid exactly once by
     main, and the one content column that the view, the card and the
     wide dock all read. Every wide geometry derives from these two —
     the old layout paid the rail twice and left 460px of content under
     a 640px dock. */
  --rail-width: 200px;
  --content-column: 680px;
  /* What the stage leaves for a fixed dock (077 G11): the tallest dock
     is listening's — label, input, Kontrol, a possible Next — measured
     at ~168px with the safe-area inset on top. */
  --dock-clearance: 176px;

  /* The dark palette's values, stated once. The two blocks below map
     them; nothing else should name a dark colour. */
  --dark-bg: #16150f;
  --dark-surface: #1f1e17;
  --dark-border: #3a372c;
  --dark-text: #ece8dd;
  --dark-muted: #a49c8c;
  --dark-accent: #7fc29b;
  --dark-accent-soft: #1c3327;
  --dark-error: #f08a7a;
  --dark-error-soft: #3a1f1a;
  --dark-ok: #6dc98a;
}

/* ------------------------------------------------------- the dark palette
 *
 * Two ways in, one palette (feature 035). Until now these tokens answered ONLY
 * to `prefers-color-scheme`, so dark mode worked and could not be chosen; the
 * `data-theme` attribute is the choice, written by `shared/appearance.js` and
 * applied before first paint by the inline script in the document head.
 *
 * Three states, and the third is the absence of an attribute:
 *
 *   no attribute      follow the device   ← the media query below
 *   data-theme=light  stay light          ← excluded from the media query
 *   data-theme=dark   stay dark           ← the second block
 *
 * `system` writes no attribute rather than `data-theme="system"`, so the media
 * query needs one exclusion instead of every rule needing a condition.
 *
 * The values live on `:root` as `--dark-*` and the two blocks map them, so a
 * colour is stated once. `color-scheme` rides along: it is what makes the
 * scrollbars, form controls and caret follow the choice too, which a token
 * cannot do.
 */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --bg: var(--dark-bg);
    --surface: var(--dark-surface);
    --border: var(--dark-border);
    --text: var(--dark-text);
    --muted: var(--dark-muted);
    --accent: var(--dark-accent);
    --accent-soft: var(--dark-accent-soft);
    --error: var(--dark-error);
    --error-soft: var(--dark-error-soft);
  --ok: var(--dark-ok);
  }
}

:root[data-theme="dark"] {
  color-scheme: dark;
  --bg: var(--dark-bg);
  --surface: var(--dark-surface);
  --border: var(--dark-border);
  --text: var(--dark-text);
  --muted: var(--dark-muted);
  --accent: var(--dark-accent);
  --accent-soft: var(--dark-accent-soft);
  --error: var(--dark-error);
  --error-soft: var(--dark-error-soft);
  --ok: var(--dark-ok);
}

* { box-sizing: border-box; }

/* ============================================================ hidden means hidden
 *
 * `hidden` has no power of its own: its effect comes from a browser default rule
 * that ANY author rule setting a `display` outranks. Four elements were marked
 * hidden and rendering anyway — the sign-in bar, both editing badges, and the
 * grade buttons, which meant a learner could grade a card they had not read.
 *
 * `!important` is the right tool here and not a smell. `hidden` states whether an
 * element exists to the user; that outranks any statement about how it is laid
 * out. And it cannot be won on specificity — `#grades` is an id selector, which
 * beats any attribute-plus-class guard — so the guard has to claim a different
 * kind of authority.
 *
 * tests/integration/test_hidden_elements.py asserts the property rather than this
 * rule, so a future override fails a test instead of shipping.
 */
[hidden] { display: none !important; }


body {
  margin: 0;
  min-height: 100vh;
  background: var(--bg);
  color: var(--text);
  font: 16px/1.5 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  display: flex;
  flex-direction: column;
}

/* The rule spans the window; the contents do not (feature 035). */
.bar {
  border-bottom: 1px solid var(--border);
  background: var(--surface);
}
.bar-inner {
  display: flex;
  align-items: center;
  gap: 1rem;
  width: var(--column);
  max-width: 100%;
  margin: 0 auto;
  padding: .75rem 1.25rem;
}
/* Three flex items: the mark, the word, the section. `.wordmark` keeps the
   accented letter from becoming a fourth and opening a gap mid-word. */
.brand { font-weight: 600; display: flex; align-items: center; gap: .5rem; }
.wordmark {
  font-family: "Commissioner", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  letter-spacing: -.012em;
}
/* The stressed vowel takes the scheme's accent, not the brand file's. */
.tonos { color: var(--accent); }
.lang {
  font: inherit;
  font-size: .85rem;
  padding: .25rem .4rem;
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 6px;
}
.muted { color: var(--muted); }

/* ------------------------------------------ the progress strip (2026-09-02)
 *
 * Under the header, the full column wide: the deck on the left (Günlük, or the
 * theme names and the not-recorded mark), the counts on the right. The header
 * keeps the wordmark and the account button and nothing that can wrap them.
 * Same treatment as the sync line below it, which stacks under it when shown.
 */
.strip { border-bottom: 1px solid var(--border); background: var(--surface); }
.strip-inner {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: baseline;
  gap: .2rem 1rem;
  width: var(--column);
  max-width: 100%;
  margin: 0 auto;
  padding: .4rem 1.25rem;
  font-size: .85rem;
  color: var(--muted);
}
.strip-context .names { color: var(--accent); }
.strip-count { margin-left: auto; white-space: nowrap; font-variant-numeric: tabular-nums; }

main {
  flex: 1;
  width: var(--column);
  max-width: 100%;
  margin: 0 auto;
  padding: 1.5rem 1.25rem 3rem;
}

.sync {
  margin: 0;
  padding: .4rem 1.25rem;
  font-size: .8rem;
  color: var(--muted);
  border-bottom: 1px solid var(--border);
  background: var(--surface);
}
.sync.warn { color: var(--accent); background: var(--accent-soft); }

/* `.view[hidden]` used to live here; the guard above subsumes it. */
.center { text-align: center; }

/* A heading takes focus when its view opens (042 FR-031) so a screen reader is
   told where it is; a sighted reader does not need a ring around a title they
   did not click. The focus stays, the outline goes. */
h2[tabindex="-1"]:focus { outline: none; }

/* Read by assistive technology, drawn nowhere: the view headings that take
   focus, and the announcer (042 FR-031). */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%);
  white-space: nowrap;
}

/* One placement rule for the two cards (042 FR-035): the deck and the session
   end sit at the same height, centred in the column when the window is taller
   than they are, rather than one at the top and the other wherever. */
#view-study:not([hidden]) {
  display: flex;
  flex-direction: column;
  min-height: 100%;
}
#view-study .card { margin-block: auto; }
#view-study .card { margin-bottom: 0; }
#view-study .study-foot { margin-bottom: auto; }
#view-study .intro { margin-top: auto; margin-bottom: .75rem; }
#view-study .intro + .card { margin-top: 0; }

/* The first-run note (042 FR-034): quiet, dismissible, once. */
.intro {
  display: flex; flex-wrap: wrap; gap: .5rem 1rem; align-items: center;
  padding: .75rem 1rem;
  border: 1px solid var(--border);
  border-inline-start: 3px solid var(--accent);
  border-radius: var(--radius);
  background: var(--surface);
  font-size: .9rem; color: var(--muted);
}
.intro p { margin: 0; flex: 1 1 18rem; }

/* ----------------------------------------------------------------- card */
.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1.5rem;
  min-height: 18rem;
  display: flex;
  flex-direction: column;
}

.card-head { display: flex; gap: .6rem; align-items: center; margin-bottom: 1rem; }

.badge {
  font-size: .7rem;
  text-transform: uppercase;
  letter-spacing: .05em;
  padding: .15rem .45rem;
  border-radius: 999px;
  background: var(--accent-soft);
  color: var(--accent);
}

.prompt {
  font-size: clamp(1.5rem, 4vw, 2.1rem);
  line-height: 1.3;
  margin: auto 0 0;
  overflow-wrap: anywhere;
}

hr { border: 0; border-top: 1px solid var(--border); margin: 1.25rem 0; }

.answer {
  font-size: clamp(1.2rem, 3vw, 1.6rem);
  margin: 0;
  color: var(--accent);
  overflow-wrap: anywhere;
}

.actions { margin-top: auto; padding-top: 1.5rem; }
#grades { display: flex; flex-wrap: wrap; gap: .5rem; }

/* The forecast under each grade (042 FR-016): `3 gün`, `10 dk`. Its own line,
   quieter than the label; an empty slot takes no room, so a deck without
   forecasts (offline, themed) lays out exactly as before. */
#grades .preview {
  display: block;
  margin-top: .15rem;
  font-size: .75rem;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
#grades .preview:empty { display: none; }
.primary .preview { color: rgba(255,255,255,.85); }

/* Again is told apart by more than its colour (042 FR-017): a heavier border
   and a marker, which both survive greyscale and both colour schemes — the
   tokens flip, the border weight and the glyph do not. */
/* Keyed on the CLASS since 077 (G9): the row a learner presses after
   every exercise wears one uniform — deck, numbers, hours alike. */
.grades button[data-grade="again"] {
  border-width: 2px;
  border-color: var(--error);
}
.grades button[data-grade="again"] > span:first-child::before {
  content: "↺ ";
  color: var(--error);
}

button {
  font: inherit;
  /* 44px tall, which is the touch minimum and not a round number by accident:
     9px + 9px padding, the 24px line box `font: inherit` gives at 16px/1.5, and
     2px of border. It was 42 — two short, on the controls this app asks a thumb
     to hit more than any other, since every card ends in a grade.

     Padding rather than `min-height`: a min-height would leave the label sitting
     1px above centre, and reaching 44 by making the button a centred flex box
     would silently drop the whitespace between each label and its `kbd`. */
  padding: .5625rem .9rem;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text);
  cursor: pointer;
}
button:hover { border-color: var(--accent); }
button:focus-visible, a:focus-visible, input:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
button.primary { background: var(--accent); color: #fff; border-color: transparent; }
button.ghost { background: transparent; color: var(--muted); }
button[disabled] { opacity: .5; cursor: default; }

kbd {
  font: .75em ui-monospace, Consolas, monospace;
  padding: .05em .3em;
  border: 1px solid var(--border);
  border-radius: 4px;
  margin-left: .35rem;
  color: var(--muted);
}
.primary kbd { border-color: rgba(255,255,255,.4); color: rgba(255,255,255,.85); }

.note { margin: .75rem 0 0; font-size: .85rem; color: var(--muted); }
.note.error { color: var(--error); background: var(--error-soft); padding: .5rem .7rem; border-radius: 6px; }

.wander-open { margin-top: 1rem; }

/* ------------------------------------------- the session's end (042 US2) */
.done-detail p { margin: .35rem 0 0; font-size: .95rem; }
.progress {
  margin-top: 1.25rem;
  padding-top: 1rem;
  border-top: 1px solid var(--border);
  text-align: left;
}
.progress p { margin: .35rem 0 0; font-size: .9rem; color: var(--muted); }
.progress .progress-label { margin-top: .9rem; font-size: .8rem; }
/* Seven days, seven bars, oldest first. Quiet by design: a row a learner
   can read at a glance, not a chart to study. */
.week { display: flex; gap: .4rem; align-items: stretch; height: 4.75rem; margin-top: .3rem; }
.week .day {
  flex: 1; min-width: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: flex-end;
  font-size: .7rem; color: var(--muted); gap: .15rem;
}
.week .bar {
  width: 100%;
  background: var(--accent-soft);
  border-top: 2px solid var(--accent);
  border-radius: 2px 2px 0 0;
}
.week .count { font-variant-numeric: tabular-nums; }

/* --------------------------------------------------------------- sign in */
.signin {
  display: flex;
  flex-direction: column;
  gap: .35rem;
  max-width: 20rem;
  margin: 1.5rem auto 0;
  text-align: left;
}
.signin label { font-size: .8rem; color: var(--muted); }
.signin input {
  padding: .6rem;
  font: inherit;
  color: var(--text);
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 8px;
}
.signin button { margin-top: .75rem; padding: .7rem; }

/* --------------------------------------------------------------- wander */
.wander-bar { display: flex; gap: .75rem; align-items: center; flex-wrap: wrap; margin-bottom: 1rem; }
.crumbs { font-size: .85rem; color: var(--muted); overflow-wrap: anywhere; }
.crumbs span + span::before { content: " › "; }

/* The results overlay what is below rather than sitting in the flow. Listed in
   the flow they pushed the node's whole card down as you typed, so the thing you
   were reading moved while you read it — the same reason the admin page's search
   became an overlay. The wrapper is what the list is positioned against, which
   is why the input and the list share one. */
.wander-search { margin-bottom: 1rem; position: relative; }
.wander-search input {
  width: 100%;
  padding: .5rem .6rem;
  font: inherit;
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
}

.node {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1.25rem 1.5rem;
}
.node h2 { margin: 0 0 .25rem; font-size: 1.6rem; overflow-wrap: anywhere; }
.node .kind { font-size: .8rem; color: var(--muted); }

.section { margin-top: 1.5rem; }
/* One rule for both, so a section heading and the forms heading cannot drift.
   `h4` is the paradigm's, rendered by the shared module; `h3` is a section's.
   They were a small muted all-caps label and an unstyled browser default, which
   read as two different kinds of thing on one page. */
.section h3,
.section h4 {
  margin: 0 0 .5rem;
  font-size: 1rem;
  font-weight: 600;
  letter-spacing: 0;
  text-transform: none;
  color: var(--text);
}
.section .more { font-size: .75rem; color: var(--muted); margin-left: .4rem; text-transform: none; letter-spacing: 0; }

.links { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: .15rem; }
.links li { border-bottom: 1px solid var(--border); }
.links li:last-child { border-bottom: 0; }

.links button, .links .fact {
  display: block;
  width: 100%;
  text-align: left;
  border: 0;
  background: transparent;
  padding: .45rem .3rem;
  border-radius: 6px;
}
.links button:hover { background: var(--accent-soft); }
.links .label { overflow-wrap: anywhere; }
.links .detail { color: var(--muted); font-size: .85rem; }
.links .fact { display: flex; justify-content: space-between; gap: 1rem; padding: .35rem .3rem; }

/* One line for the sections that hold a fact or two rather than a list. No
   border, no row padding: a comma does the separating a rule used to. */
.inline-facts { margin: 0; padding: .1rem 0; line-height: 1.6; }
.inline-facts .detail { color: var(--muted); font-size: .85rem; }
.inline-facts .inline-link {
  border: 0;
  background: transparent;
  padding: 0;
  font: inherit;
  color: var(--accent);
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: .15em;
}
.inline-facts .inline-link:hover { text-decoration-thickness: 2px; }

.hits {
  list-style: none;
  padding: 0;
  margin: 0;
  max-height: 14rem;
  overflow-y: auto;
  position: absolute;
  top: calc(100% + .35rem);
  left: 0;
  right: 0;
  z-index: 30;
  /* Solid, and the panel's own colour: a dropdown floating over the node card
     with a see-through background is unreadable in exactly the moment it
     matters. Both themes define --surface. */
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: 0 .5rem 1.25rem rgba(0, 0, 0, .28);
}

/* An empty list is not a very short dropdown: it is no dropdown. Without this
   the border and shadow draw as a thin bar under the box before you type. */
.hits:empty { display: none; }
.hits li { border-bottom: 1px solid var(--border); }
.hits li:last-child { border-bottom: 0; }
.hits button { display: block; width: 100%; text-align: left; border: 0; background: transparent; padding: .4rem .3rem; }
/* The CURRENT option — the one Enter would open. Hover and the arrow keys write
   the same state (feature 030), so this is one rule rather than a hover rule
   beside a keyboard rule. The admin page carries the identical rule. */
.hits button[aria-selected="true"] {
  background: var(--accent-soft);
  box-shadow: inset 3px 0 0 var(--accent);
}

/* --------------------------------------------------------------- legend */
.legend {
  position: fixed;
  right: 1rem;
  bottom: 1rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: .75rem 1rem;
  font-size: .85rem;
  box-shadow: 0 6px 24px rgba(0,0,0,.12);
}
.legend h3 { margin: 0 0 .5rem; font-size: .8rem; text-transform: uppercase; color: var(--muted); }
.legend dl { display: grid; grid-template-columns: auto 1fr; gap: .2rem .8rem; margin: 0; }
.legend dt { white-space: nowrap; }
.legend dd { margin: 0; color: var(--muted); }

/* ----------------------------------------------------------------- phone */
@media (max-width: 480px) {
  main { padding: 1rem .75rem 2rem; }
  .bar { padding: .6rem .75rem; }
  .sync { padding: .4rem .75rem; }
  .strip-inner { padding: .4rem .75rem; }
  .card { padding: 1rem; min-height: 15rem; }

  /* Four across on ONE row (FR-034).
     This said "four across" in a comment while laying out two columns in two
     rows, which pushed the answer off a phone screen — the mismatch between the
     comment and the code is why it survived. The shortcut stays visible: there
     is no keyboard here, but the number is how the grades are referred to. */
  .grades {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: .35rem;
  }
  .grades button {
    padding: .8rem .25rem;
    font-size: .8rem;
    line-height: 1.2;
    min-width: 0;               /* let a long label shrink rather than overflow */
  }
  .grades button span { display: block; overflow-wrap: anywhere; }
  .grades kbd { font-size: .65rem; padding: 0 .25rem; opacity: .75; }
  #reveal { width: 100%; padding: .8rem; }
  .study-foot { flex-direction: column; }
  .wander-open { width: 100%; }
  .legend { left: .75rem; right: .75rem; bottom: .75rem; }
}


/* -------------------------------------------------------------- settings */
.setting { display: block; margin: 0 0 1.25rem; }
/* A toast that hovers at the top of the visible page, above everything, and
   fades out on its own (see showToast). Fixed to the viewport so a scrolled
   form does not carry it off; red for a refused value, plain for a saved one. */
.toast {
  position: fixed;
  top: max(1rem, env(safe-area-inset-top, 0px));
  left: 50%;
  transform: translateX(-50%);
  z-index: 1000;
  max-width: min(92vw, 30rem);
  padding: .6rem 1rem;
  border-radius: 8px;
  font-size: .9rem;
  font-weight: 600;
  text-align: center;
  background: var(--bg);
  color: var(--text);
  border: 1px solid var(--border);
  box-shadow: 0 6px 24px rgba(0, 0, 0, .22);
  opacity: 1;
  transition: opacity .5s ease;
}
.toast.fading { opacity: 0; }
.toast.error { color: var(--error); background: var(--error-soft); border-color: var(--error); }
@media (prefers-reduced-motion: reduce) { .toast { transition: none; } }
.setting-name { display: block; font-weight: 600; margin-bottom: .3rem; }
.setting input, .setting select {
  width: 100%;
  padding: .5rem;
  font: inherit;
  background: var(--bg);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 4px;
}
.setting-help {
  display: block;
  margin-top: .35rem;
  color: var(--muted);
  font-size: .85rem;
  line-height: 1.45;
}

/* The stop control sits beside "explore", not among the grades: it must not be
   reachable by the thumb that is aiming for a grade. */
.study-foot { display: flex; gap: .5rem; margin-top: .75rem; }
.study-foot button { flex: 1; }

/* ------------------------------------------------------- themed study (033) */

.theme-choices { list-style: none; margin: 0; padding: 0; }
.theme-choices li { margin: 0 0 .5rem; }
/* A tick beside each name (042 FR-028): the button still starts one theme alone;
   the ticks and Start make a deck of several. The 44px target is the
   `.pick-target` label around the box (079 G15) — the box itself
   stays readable at 20px. */
.theme-row { display: flex; align-items: center; gap: .5rem; }
.theme-row input[type="checkbox"] { width: 1.25rem; height: 1.25rem; margin: 0; flex: 0 0 auto; }
.theme-choices button {
  flex: 1; min-width: 0;
  width: 100%; text-align: left; display: flex; justify-content: space-between;
  gap: 1rem; align-items: baseline;
}
.theme-choices .count { font-size: .85em; opacity: .75; }
/* A theme with nothing studyable in it is offered as unavailable rather than
   hidden: an author needs to see that the theme exists and is empty, and a
   learner who tapped it once should not wonder where it went. */
.theme-choices button[disabled] { opacity: .5; cursor: not-allowed; }

/* FR-019's not-recorded mark moved into the progress strip (2026-09-02). */

/* A theme's Greek name beside the reader's own (034). Quieter than the label it
   follows: it is the same theme said twice, not a second theme. */
.theme-choices .theme-name-el { opacity: .7; font-style: italic; }

/* The way out to a dictionary (034). Quiet by default and obvious on hover: it
   is an escape hatch, not a call to action. */
.dictionary {
  text-decoration: none; opacity: .55; font-size: .9em;
  padding: 0 .2rem; align-self: center;
}
.dictionary:hover, .dictionary:focus-visible { opacity: 1; }

/* ------------------------------------------------ the account menu (035) */

.account { position: relative; margin-left: auto; }

.icon-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  padding: 0;
}
#account-menu[aria-expanded="true"] {
  border-color: var(--accent);
  background: var(--accent-soft);
  color: var(--accent);
}

.menu {
  position: absolute;
  top: calc(100% + .4rem);
  right: 0;
  z-index: 20;
  min-width: 12rem;
  padding: .35rem;
  display: flex;
  flex-direction: column;
  gap: .1rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: 0 8px 28px rgb(0 0 0 / .14);
}
.menu button {
  border: 0;
  background: transparent;
  border-radius: 7px;
  text-align: left;
  width: 100%;
}
.menu button:hover { background: var(--accent-soft); border-color: transparent; }
.menu-rule { height: 1px; background: var(--border); margin: .25rem .4rem; }

/* On a narrow screen the panel spans the bar rather than hanging off its right
   edge, where a 12rem panel would sit half off a 320px phone. */
@media (max-width: 30rem) {
  .menu { right: -.5rem; left: auto; min-width: 13rem; }
}

/* ------------------------------------------------ appearance settings (035) */

.setting.segmented {
  border: 0;
  padding: 0;
  margin: 0 0 1rem;
  display: flex;
  flex-wrap: wrap;
  gap: .4rem;
}
.setting.segmented legend { padding: 0; margin-bottom: .3rem; width: 100%; }
.setting.segmented label {
  /* The radio inside is `position: absolute` (below). Without a positioned
     ancestor it laid out against the PAGE and landed 240px past its right
     edge — the whole settings view scrolled sideways (042 F6, research R11). */
  position: relative;
  flex: 1;
  min-width: 5.5rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .4rem;
  min-height: 44px;
  padding: 0 .6rem;
  border: 1px solid var(--border);
  border-radius: 8px;
  color: var(--muted);
  cursor: pointer;
}
/* The radio itself is the state; hiding it and styling the label keeps the
   keyboard behaviour a radio group already has. */
.setting.segmented input { position: absolute; opacity: 0; pointer-events: none; }
.setting.segmented label:has(input:checked) {
  border-color: var(--accent);
  background: var(--accent-soft);
  color: var(--accent);
}
.setting.segmented label:has(input:focus-visible) {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.setting.switch-row {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: .1rem .6rem;
  align-items: center;
  cursor: pointer;
}
.setting.switch-row input { grid-row: span 3; width: 1.15rem; height: 1.15rem; }
.setting.switch-row .setting-help,
.setting.switch-row .setting-when { grid-column: 2; }

/* Whether a setting applies now or to the next session (042 FR-008). Quieter
   than the help above it: it is a fact about timing, not about the setting. */
.setting-when {
  display: block;
  margin-top: .2rem;
  color: var(--muted);
  font-size: .8rem;
  font-style: italic;
}
.setting.segmented .setting-when { flex-basis: 100%; margin-top: 0; }

/* The heading over the type groups (feature 044, US2). A separator with a name,
   not a second list: the groups are chosen the same way the themes are, and
   splitting them into two controls would say they are two kinds of choice. */
.theme-group-heading {
  list-style: none;
  margin: 1.1rem 0 .3rem;
  font-size: .72rem;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--muted);
}

/* --------------------------------------------------- hearing the card (050)
 *
 * The control sits after the text rather than beside it in a fixed column: the
 * taught-language side wraps to two or three lines on a phone, and a column
 * would leave the button stranded beside the first line.
 *
 * `.play[hidden]` is spelled out because `display: flex` on the row would
 * otherwise beat the attribute and show a control the page has decided it
 * cannot honour -- which is the whole of FR-011 undone by one line of CSS.
 */
.spoken {
  display: flex;
  align-items: flex-end;
  gap: 0.6rem;
  margin: auto 0 0;
}

.spoken > .prompt,
.spoken > .answer { margin: 0; }

.play {
  flex: 0 0 auto;
  width: 2.25rem;
  height: 2.25rem;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 50%;
  background: var(--card, transparent);
  color: var(--accent);
  cursor: pointer;
  line-height: 1;
  align-self: center;
}

/* A triangle drawn in CSS: no icon font, no SVG file, nothing to precache. */
.play::before {
  content: "";
  display: block;
  margin: 0 auto 0 0.75rem;
  width: 0;
  height: 0;
  border-style: solid;
  border-width: 0.4rem 0 0.4rem 0.62rem;
  border-color: transparent transparent transparent currentColor;
}

.play:hover, .play:focus-visible { border-color: var(--accent); }

/* Not colour alone: the focus ring is a shape as well as a hue. */
.play:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.play[hidden] { display: none; }

/* The offered voice's name, where a select would be if there were a choice
   (050). Sits in the select's row so the settings screen keeps one rhythm. */
.setting-value {
  font-weight: 600;
  align-self: start;
}

/* Explore's own play controls (050 follow-up): beside the word being explored
   and beside each thing listed around it. Smaller than a card's, because a list
   row is a line of text rather than the thing the screen is for. */
h2 > .play { width: 1.9rem; height: 1.9rem; vertical-align: middle; }

.links li { display: flex; align-items: center; gap: 0.4rem; }
.links li > button:first-child { flex: 1 1 auto; text-align: left; }
.links li > .play { width: 1.9rem; height: 1.9rem; }

/* A card back holding several words (feature 056): one native word is one card,
   and every taught word that means it is an answer. Lines, not a wrapped comma
   list — six is the collection's worst case (`harika`) and an eye cannot
   separate six comma-joined words at a glance. `ch` so the block stays narrow
   next to a long note, and the notes align in a second column. */
.answer-list {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-direction: column; gap: .25rem;
}
.answer-list li {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: .4rem;
  justify-content: center;
}
.answer-word { font-weight: 600; }
/* A control per word (056 R6, built 2026-09-09). Smaller than the card's own
   control and scaled with it: a back holds up to five words here, and five
   36px circles stacked read as a toolbar rather than as a list of words. The
   triangle is drawn from border widths, so it has to be scaled too or it sits
   off-centre in the smaller circle. */
.answer-list .play {
  width: 1.6rem;
  height: 1.6rem;
  align-self: center;
}
.answer-list .play::before {
  margin: 0 auto 0 0.52rem;
  border-width: 0.28rem 0 0.28rem 0.44rem;
}
/* A thumb needs a bigger target than an eye needs a glyph, and this control
   sits between words rather than alone under a card. */
@media (pointer: coarse) {
  .answer-list .play { width: 2.25rem; height: 2.25rem; }
  .answer-list .play::before { margin: 0 auto 0 0.75rem; border-width: 0.4rem 0 0.4rem 0.62rem; }
}
/* The author's note for THIS word, quieter than the word and beside it, so a
   merged back keeps the distinction the author typed rather than flattening it. */
.answer-note { font-size: .82em; opacity: .72; }

/* ------------------------------------------------- the Bugün home (071) */
.home-head { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; }
.home-head .muted { font-size: .85rem; }
.home-counts { display: flex; gap: 1.4rem; margin: .35rem 0 .9rem; }
.home-count { color: var(--muted); font-size: .85rem; }
.home-count b { color: var(--text); font-size: 1.45rem; font-weight: 600; margin-right: .3rem; }
#home-cta { width: 100%; margin-bottom: .4rem; }

/* ------------------------------------------------ the practice shelf (071) */
.shelf-label { font-size: .78rem; text-transform: uppercase; letter-spacing: .05em; margin: 1.1rem 0 .55rem; }
.shelf-grid { display: grid; grid-template-columns: 1fr 1fr; gap: .65rem; }
.tile { display: flex; flex-direction: column; align-items: flex-start; gap: .3rem;
        padding: .8rem .85rem; text-align: left; font: inherit; color: var(--text);
        background: var(--surface); border: 1px solid var(--border); border-radius: 12px; cursor: pointer; }
.tile-title { font-weight: 600; }
.tile-line { font-size: .78rem; }
.tile-chip { font-size: .72rem; padding: .1rem .55rem; border-radius: 999px;
             background: var(--accent-soft, #f6ece2); color: var(--accent); }
.tile-track { width: 100%; height: 5px; border-radius: 3px; background: var(--accent-soft, #f6ece2); }
.tile-fill { height: 5px; border-radius: 3px; background: var(--accent); }
.tile-unavailable { opacity: .65; }

/* ------------------------------------------------------ the tab bar (071) */
#tab-bar { position: fixed; left: 0; right: 0; bottom: 0; z-index: 30;
           display: flex; gap: .3rem; padding: .35rem .5rem calc(.35rem + env(safe-area-inset-bottom, 0px));
           background: var(--surface); border-top: 1px solid var(--border); }
#tab-bar .tab { flex: 1; display: flex; flex-direction: column; align-items: center; gap: .15rem;
                padding: .35rem 0; border: 0; border-radius: 10px; background: transparent;
                color: var(--muted); font: inherit; font-size: .68rem; cursor: pointer; }
#tab-bar .tab svg { width: 22px; height: 22px; }
#tab-bar .tab[aria-current] { color: var(--accent); background: var(--accent-soft, #f6ece2); }
body.in-sitting #tab-bar { display: none; }
/* Content never hides under the bar: clearance whenever it can be shown. */
body:not(.in-sitting) main, body:not(.in-sitting) .view { padding-bottom: 76px; }

/* --------------------------------------------- full-bleed sittings (072) */
/* The sit-head: a quiet exit and one line of where-am-I. Beside the a11y
   h2 machinery, never instead of it (research R1). */
.sit-head { display: flex; align-items: center; gap: .6rem; margin: .1rem 0 .6rem; }
.sit-back { width: 38px; height: 38px; flex: none; border-radius: 10px;
            border: 1px solid var(--border); background: var(--surface);
            color: var(--muted); font: inherit; font-size: 1.15rem; cursor: pointer; }
.sit-line { font-size: .85rem; color: var(--muted); }

/* The dock: the same controls, kept beside their content by default —
   a compact card footer — and pinned to the bottom edge ONLY where
   thumbs exist (075: the field screenshots showed a desktop sitting
   split by the viewport's whole leftover height). A CLASS on existing
   elements; ids and handlers do not move. */
.dock { display: flex; flex-direction: column; gap: .5rem;
        margin-top: 1.1rem; padding-top: .9rem;
        border-top: 1px solid var(--border); }
.dock .grades { display: flex; gap: .5rem; }
.dock .grades button { flex: 1; min-height: 50px; }
.dock > button.primary { width: 100%; min-height: 48px; }

@media (pointer: coarse) {
  /* The 072 thumb zone, verbatim: sittings hide the tab bar, so the
     edge is free. The keyboard lift (077 G4) rides a custom property
     the JS sets while an input inside the dock has focus. */
  .dock { position: fixed; left: 0; right: 0; bottom: 0; z-index: 25;
          background: var(--surface); margin: 0;
          transform: translateY(calc(-1 * var(--kbd-lift, 0px)));
          padding: .6rem .8rem calc(.6rem + env(safe-area-inset-bottom, 0px)); }
  /* The stage clears the dock so nothing hides under it — one property
     (077 G11), sized to the tallest dock (listening: label, input,
     Kontrol, Next) instead of the old guessed constant. */
  body.in-sitting .view:not([hidden]), .view.has-dock:not([hidden]) {
    padding-bottom: var(--dock-clearance);
  }
}

/* --------------------------------------- inputs join the design system (075) */
input:not([type="checkbox"]):not([type="radio"]), select {
  font: inherit; color: var(--text); background: var(--surface);
  border: 1px solid var(--border); border-radius: 10px;
  padding: .5rem .7rem;
}
input:not([type="checkbox"]):not([type="radio"]):focus-visible,
select:focus-visible, button:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px;
}

/* Listening center stage: the play button IS the disc it always was. */
.disc { width: 96px; height: 96px; border-radius: 50%;
        border: 1px solid var(--accent); background: var(--accent-soft, #f6ece2);
        color: var(--accent); font-size: 2rem; cursor: pointer; display: block;
        margin: 0 auto; }
.listen-stage { display: flex; flex-direction: column; align-items: center;
                gap: .9rem; padding: 2.2rem 0 1rem; text-align: center; }
.listen-stage .note { font-size: 1.25rem; }
.listen-stage .answer { font-size: 1.15rem; }

/* --------------------------------------------------- the desktop rail (073) */
/* Below the breakpoint: the phone is FROZEN — the rail-only members simply
   do not exist, and nothing else here applies. `#tab-bar #rail-account`
   rather than the bare id: the bar's own `.tab` rule is (1,1,0) and was
   outranking a (1,0,0) hide — the phone bar grew a fourth item, caught
   by 074's walk one feature later. */
.rail-brand, #tab-bar #rail-account, #side-column { display: none; }

@media (min-width: 1160px) {
  /* The bar becomes the rail: same element, same tabs, same handlers. */
  #tab-bar { top: 0; bottom: 0; right: auto; width: 200px;
             flex-direction: column; gap: .25rem; padding: 14px 10px;
             border-top: 0; border-right: 1px solid var(--border); }
  #tab-bar .tab { flex: 0 0 auto; flex-direction: row; justify-content: flex-start;
                  gap: .6rem; padding: .55rem .65rem; font-size: .9rem; }
  .rail-brand { display: flex; align-items: center; padding: 0 .4rem .8rem; }
  #tab-bar #rail-account { display: flex; margin-top: auto; }
  /* The header's own account BUTTON hides: the control moved, it is
     not doubled. The BUTTON, never the .account container (086,
     field-found): the panel lives inside that container, and fixed
     positioning cannot escape a display:none ancestor — the rail's
     Hesap toggled an unrenderable panel for three features. */
  #account-menu { display: none; }
  /* The one menu re-anchors above the rail's foot when opened from it.
     z-index above the rail's 30 (086 round two): the base .menu's 20
     put the panel BEHIND the pane it opens beside. */
  #account-panel { position: fixed; left: 12px; right: auto; bottom: 64px; top: auto;
                   z-index: 35; }
  /* Wide sittings KEEP the rail: focus mode is a phone concern. */
  body.in-sitting #tab-bar { display: flex; }
  /* The rail paid once (078 G2), by the container: main stretches into
     the band right of the rail and the view centers inside it at the
     ruler's width. The old doubled margin (paid by container and view
     both) and the view's second 220px payment are gone, not
     overridden. */
  main { margin-left: var(--rail-width); margin-right: 0;
         width: auto; max-width: none; }
  .view:not([hidden]) { max-width: var(--content-column);
                        margin-left: auto; margin-right: auto; }
}

/* The side column fits or hides (078 G2+G13): anchored BESIDE the
   column by the same ruler, never to the window's right edge. At width
   W its right edge sits at rail + (W - rail)/2 + column/2 + 24px gap
   + 260px card; that meets the window's 18px inset at W = 1484 — so
   below 1490 the column does not exist, where before it collided with
   the content everywhere from 1160 to ~1350. The global [hidden]
   guard still outranks the display here. */
@media (min-width: 1490px) {
  #side-column { display: flex; flex-direction: column; gap: 12px;
                 position: fixed; top: 84px; width: 260px; z-index: 5;
                 left: calc(var(--rail-width) + (100vw - var(--rail-width)) / 2
                            + var(--content-column) / 2 + 24px); }
  .side-card { background: var(--surface); border: 1px solid var(--border);
               border-radius: 12px; padding: 12px 14px; font-size: .85rem; }
  .side-counts { display: flex; gap: 1.1rem; margin: .4rem 0 .5rem; color: var(--muted); }
  .side-counts b { color: var(--text); font-size: 1.15rem; margin-right: .25rem; }
  .side-line { margin: .3rem 0 0; color: var(--muted); white-space: nowrap;
               overflow: hidden; text-overflow: ellipsis; }
}

/* ---------------------------------------------- settings sections (074) */
.settings-section { font-size: .78rem; text-transform: uppercase; letter-spacing: .05em;
                    color: var(--muted); margin: 1.4rem 0 .2rem; font-weight: 600; }
.settings-section:first-of-type { margin-top: .6rem; }

/* A FIXED dock at rail widths (coarse wide tablets only, since 075)
   re-anchors under the content column instead of spanning the window. */
@media (pointer: coarse) and (min-width: 1160px) {
  /* The same ruler as the view column (078 G2): the dock's center is
     the band's center and its width is the column's, so it sits exactly
     under the card. The centering composes with the keyboard lift
     (077 G4). */
  .dock { left: calc(var(--rail-width) + (100vw - var(--rail-width)) / 2); right: auto;
          transform: translateX(-50%)
                     translateY(calc(-1 * var(--kbd-lift, 0px)));
          width: var(--content-column); border: 1px solid var(--border);
          border-radius: 14px 14px 0 0; }
}

/* The brand as the way home (075): a button wearing the brand''s own
   clothes - no chrome of its own. */
button.brand { background: none; border: 0; padding: 0; cursor: pointer;
               font: inherit; color: inherit; }

/* ---------------------------------- the worn layout classes (077 G11) */
/* Grep found these on the choosers and docks and no rule anywhere -
   the ranges and the label+input+Kontrol assemblies laid out by
   browser defaults. */
.row { display: flex; gap: .5rem; flex-wrap: wrap; align-items: center; }
.field-pair { display: flex; gap: .5rem; align-items: flex-end; }
.field-pair label { display: flex; flex-direction: column; gap: .3rem; flex: 1; min-width: 0; }
.narrow { flex: 0 1 auto; }
/* Five digits, exactly (080 FR-002): the widest value the range can
   name, plus the input's own padding and border — the box, not the
   rule. The old 6.5ch counted the padding against the digits and
   showed four. */
.narrow input { width: calc(5ch + 1.4rem + 2px); }
/* The listening answer is five digits or a clock time (080 FR-004):
   the input takes its content's width — with one spare character —
   and Check stays beside it, instead of a column-wide box pushing
   the button to the far edge. */
.dock .field-pair label { flex: 0 0 auto; }
.dock .field-pair input { width: calc(6ch + 1.4rem + 2px); }

/* The practice lobby leaves the sitting (077 G10): the big titles keep
   their place in the tree - focus and announcement intact - but stop
   repeating what the sit-line already says. */
body.in-sitting #view-numbers h2, body.in-sitting #view-hours h2 {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  border: 0; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%);
  white-space: nowrap;
}
/* The mark may wrap alone on a narrow phone, never mid-count. */
.sit-line { overflow-wrap: break-word; }

/* ------------------------------------- feedback and first paint (079) */
/* The shelf paints its shape before its data (G12): four of these
   stand where the tiles will — same box, no text, no cursor. */
.tile-ghost { min-height: 96px; border: 1px solid var(--border);
              border-radius: 12px; background: var(--surface); opacity: .55; }
/* The home action area exists before counts and button arrive (G12),
   so their arrival lands in reserved space instead of pushing the
   summary down. Sized to the counts row plus the CTA. */
.home-action { min-height: 108px; }
/* The tick earns the 44px the old comment only claimed (G15): the
   label around the 20px box is the target. */
.pick-target { display: inline-flex; align-items: center; justify-content: center;
               width: 44px; height: 44px; flex: 0 0 auto; cursor: pointer;
               margin-left: -.35rem; }
/* Playing is visible even without motion: the ring thickens (G17). */
.disc.playing { border-width: 3px; }
@media (prefers-reduced-motion: no-preference) {
  .disc.playing { animation: disc-pulse 1.6s ease-in-out infinite; }
  @keyframes disc-pulse {
    50% { box-shadow: 0 0 0 9px var(--accent-soft, #f6ece2); }
  }
  /* One transition (G18): the deck card and the listening stage
     ARRIVE on content swap - the state feedback the bare advance
     timeouts were reaching for. */
  .swap-in { animation: swap-in .15s ease-out; }
  @keyframes swap-in {
    from { opacity: 0; transform: translateY(4px); }
  }
}

/* ------------------------------------------------- Sorun bildir (099)
 * The flag is quiet on purpose: a small ghost control at the far end of a head
 * row, never in the dock. The sheet is a native dialog, so it needs a look and
 * nothing else -- the browser owns its layer, its focus and its backdrop. */
.report-flag {
  margin-left: auto; display: inline-flex; align-items: center; gap: .3rem;
  padding: .15rem .5rem; font-size: .8rem; line-height: 1.4;
  background: transparent; color: var(--muted);
  border: 1px solid var(--border); border-radius: 999px; cursor: pointer;
}
.report-flag svg { width: 14px; height: 14px; }
.report-flag:hover { color: var(--text); border-color: var(--accent); }
.report-flag.is-sent { color: var(--ok); border-color: transparent; cursor: default; }
.card-head { flex-wrap: wrap; }
.card-head .report-status, .report-row .report-status, .node .kind .report-status {
  flex: 0 0 100%; margin: .2rem 0 0; font-size: .85rem;
}
.report-row { display: flex; flex-wrap: wrap; justify-content: flex-end; margin: .2rem 0 .4rem; }
.node .kind { display: flex; align-items: center; flex-wrap: wrap; gap: .5rem; }

.report-sheet {
  width: min(28rem, calc(100vw - 2rem)); padding: 1rem 1.1rem 1.1rem;
  color: var(--text); background: var(--surface);
  border: 1px solid var(--border); border-radius: var(--radius);
}
.report-sheet::backdrop { background: rgb(0 0 0 / .45); }
.report-sheet h2 { margin: 0 0 .3rem; font-size: 1.05rem; }
.report-item { margin: 0 0 .7rem; color: var(--muted); font-size: .95rem; }
.report-reasons { border: 0; padding: 0; margin: 0 0 .7rem; display: grid; gap: .35rem; }
.report-reasons legend { padding: 0; margin-bottom: .35rem; font-size: .85rem; color: var(--muted); }
.report-choice { display: flex; align-items: center; gap: .5rem; font-size: .95rem; cursor: pointer; }
.report-explain { display: grid; gap: .3rem; font-size: .85rem; color: var(--muted); }
.report-explain textarea {
  width: 100%; box-sizing: border-box; resize: vertical; font: inherit; color: var(--text);
  background: var(--bg); border: 1px solid var(--border); border-radius: 8px; padding: .45rem .55rem;
}
.report-actions { display: flex; gap: .6rem; justify-content: flex-end; margin-top: .8rem; }
