:root {
  --paper: #f7f0e4;
  --card: #fffaf2;
  --ink: #191512;
  --muted: #76695d;
  --red: #b5312d;
  --gold: #c79542;
  --line: #d8c8b5;
  --seen: #b5312d;
  --unseen: #c4b7a4;
  --indigo: #33577b; /* the Japanese (JLPT) accent, vs the red Chinese (HSK) one */
  --space-section: 16px;
  --pad-control-y: 7px;
  --pad-control-x: 12px;
  --pad-card: 14px;
  --radius-control: 10px;
  --radius-card: 16px;
  --sans: ui-sans-serif, system-ui, "Segoe UI", Roboto, sans-serif;
  --han: "Noto Sans SC", "PingFang SC", "Microsoft YaHei", "Heiti SC", var(--sans);
  --kana: "Noto Sans JP", "Hiragino Kaku Gothic ProN", "Yu Gothic", Meiryo, var(--sans);
}

* { box-sizing: border-box; }

/* Japanese text is marked lang="ja" so unified Han codepoints render with
   Japanese glyph variants instead of the simplified-Chinese default. */
[lang="ja"] { font-family: var(--kana); }

/* Always reserve room for the vertical scrollbar so navigating between pages of
   different heights doesn't shift the layout left/right. Fallback: force the
   scrollbar to always show. Modern browsers instead reserve a stable gutter, so
   the scrollbar can still hide when content fits without any horizontal jump. */
html {
  scroll-behavior: smooth;
  overflow-y: scroll;
  /* iOS paints the standalone status-bar/safe-area surround from the page's
     explicit background-color. Do not leave the root canvas transparent: the
     body may not be composited there until the first scroll. */
  background-color: var(--paper);
}
@supports (scrollbar-gutter: stable) {
  html {
    overflow-y: auto;
    scrollbar-gutter: stable;
  }
}

/* Keep the page itself the only thing that scrolls on touch screens: no
   horizontal panning past the layout edge, and no browser overscroll actions
   (pull-to-refresh, back/forward swipe glow) chaining off the document edge.
   Vertical in-page scrolling is untouched. overflow-x: clip cuts overflow
   without making html/body horizontal scroll containers (hidden would); the
   plain hidden line is the fallback for engines without clip. */
html, body {
  overflow-x: hidden;
  overflow-x: clip;
  overscroll-behavior: none;
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.5;
}

/* The viewport extends under the iPhone notch / status bar / home indicator
   (viewport-fit=cover), so the reading column pads itself back into the safe
   area; on anything without insets the env() terms are 0 and this is the
   plain 28px 22px 120px it always was. --pad-x is shared with the sticky
   bars, which cancel it to go full-bleed. */
.wrap {
  --pad-x: max(22px, env(safe-area-inset-left, 0px), env(safe-area-inset-right, 0px));
  max-width: 1080px;
  margin: 0 auto;
  padding: calc(24px + env(safe-area-inset-top, 0px)) var(--pad-x) calc(96px + env(safe-area-inset-bottom, 0px));
}
/* Board view breaks out of the reading column to use the full viewport width,
   fitting as many characters on screen as possible. The site header/nav stays
   pinned to the normal reading column (see .wrap.full .nav) so switching to the
   board doesn't widen the chrome. */
.wrap.full {
  max-width: none;
}
.wrap.full .nav {
  /* the reading column: base max-width (1080) minus the wrap's side padding */
  max-width: 1036px;
  margin-left: auto;
  margin-right: auto;
}

.loading, .empty, .hint {
  color: var(--muted);
  padding: 40px 0;
}
/* First-visit dataset download meter (js/app.js): live byte progress in the
   content area while nothing else can render yet. */
.data-progress { color: var(--muted); padding: 40px 0; font-variant-numeric: tabular-nums; }
.empty, .hint { padding: 24px 0; }

a { color: var(--red); text-decoration: none; }
a:hover { text-decoration: underline; opacity: 0.8; }

/* Remove the mobile double-tap delay/zoom gesture from actual controls while
   retaining one-finger scrolling and normal two-finger pinch zoom. Keep this
   scoped to tappable elements: the page and any future drawing/gesture
   surfaces must retain the browser's full touch behavior. */
:where(
  a,
  button,
  input,
  select,
  textarea,
  .tab,
  .chip:not(.static),
  .result-word,
  .cell,
  .link,
  .setting-row,
  .pwa-pill.clickable
) {
  touch-action: manipulation;
}

/* ---- nav ---- */
.nav {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 12px 22px;
  border-bottom: 1px solid var(--line);
  padding-bottom: 14px;
  margin-bottom: 20px;
}
.brand { display: flex; align-items: baseline; gap: 12px; margin-right: auto; }
.logo {
  display: inline-flex;
  align-items: center;
  gap: 0.35em;
  color: var(--ink);
  font-size: 24px;
  font-weight: 700;
}
.logo-mark {
  flex: none;
  color: var(--red);
  font-family: var(--han);
  font-size: 1.15em;
  line-height: 1;
  user-select: none;
}
.logo:hover { text-decoration: none; color: var(--red); }
.tabs { display: flex; gap: 6px; }
.tab {
  display: inline-flex;
  align-items: center;
  min-height: 36px;
  cursor: pointer;
  padding: 7px 14px;
  border-radius: 6px;
  color: var(--muted);
  font-weight: 600;
  user-select: none;
}
.tab:hover { color: var(--ink); background: rgba(181, 49, 45, 0.08); }
.tab.active { color: #fff; background: var(--red); }
.tab:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
.tab-count {
  display: inline-flex;
  min-width: 18px;
  height: 18px;
  margin-left: 5px;
  align-items: center;
  justify-content: center;
  border-radius: 999px;
  background: var(--red);
  color: #fff;
  font-size: 11px;
  font-variant-numeric: tabular-nums;
}
.tab.active .tab-count {
  background: #fff;
  color: var(--red);
}

/* ---- shared ---- */
.view { animation: fade 0.15s ease; }
@keyframes fade { from { opacity: 0; } to { opacity: 1; } }

.subsection { margin: var(--space-section) 0; }
.subsection label, .notes-panel label {
  display: block;
  font-size: 12px;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--muted);
  font-weight: 700;
  margin-bottom: 8px;
}

.btn {
  display: inline-block;
  padding: var(--pad-control-y) var(--pad-control-x);
  margin: 0 8px 8px 0;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--card);
  font-weight: 600;
}
.btn:hover { border-color: var(--red); text-decoration: none; opacity: 1; }

.chips { display: flex; flex-wrap: wrap; gap: 8px; }
.chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: var(--pad-control-y) var(--pad-control-x);
  border: 1px solid var(--line);
  border-radius: var(--radius-control);
  background: var(--card);
  cursor: pointer;
  font-family: var(--han);
  font-size: 18px;
}
.chip:hover { border-color: var(--red); }
.chip.static { cursor: default; color: var(--muted); }
.chip.static:hover { border-color: var(--line); }

.link { color: var(--red); cursor: pointer; }
.link:hover { text-decoration: underline; }
.muted { color: var(--muted); }

input, textarea, button { font-family: inherit; font-size: 16px; color: var(--ink); }
input[type="search"], input[type="text"] {
  width: 100%;
  padding: 10px var(--pad-card);
  border: 1px solid var(--line);
  border-radius: var(--radius-control);
  background: var(--card);
}
input:focus, textarea:focus { outline: 2px solid var(--gold); outline-offset: 1px; }

button {
  cursor: pointer;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--card);
  padding: var(--pad-control-y) var(--pad-control-x);
  font-weight: 700;
}
button:hover { border-color: var(--red); }
button.danger { color: var(--red); }

/* ---- search ---- */
.search-box { margin-bottom: 8px; }
/* The blank-input hint inherits .hint's 24px empty-state padding on top of
   the paragraph's own margin, which opened a ~60px hole right under the
   search input (worst on phones, where the input is the whole viewport
   width). It is a caption, not an empty state - keep it tight. The first
   results subsection gets the same trim so typed results also start near
   the input. */
.results .search-hint { padding: 2px 0; margin: 8px 0; }
/* The sentence-fallback caption ("words found in your text") sits between the
   query chrome and the extracted results - same tight trim as the blank-input
   hint, it is a caption too, not an empty state. */
.results .extract-hint { padding: 2px 0; margin: 8px 0 0; font-size: 13px; }
.results .subsection:first-child { margin-top: 12px; }
/* On its own each row is an independent grid, so its auto / max-content tracks
   (the hanzi cell, and the freq / level cells) size to that row's own content -
   a one-hanzi word and a four-hanzi word get different first columns, and the
   rest drift with them. To keep columns aligned regardless of content shape the
   column tracks must be shared across every row, so .result-words owns the grid
   and each row adopts its tracks via subgrid: now every auto / max-content track
   sizes to the widest cell across ALL rows and the columns line up. The optional
   property columns (frequency, level) are toggled in Settings; the container's
   has-freq / has-level classes append exactly the trailing tracks the rows
   render. The pre-subgrid fallback keeps the per-row grid (functional, just not
   cross-row aligned). */
.result-words {
  display: grid;
  grid-template-columns: minmax(72px, auto) minmax(96px, 1fr) minmax(0, 3fr);
  column-gap: 16px;
}
.result-words.has-freq,
.result-words.has-level {
  grid-template-columns: minmax(72px, auto) minmax(96px, 1fr) minmax(0, 3fr) max-content;
}
.result-words.has-freq.has-level {
  grid-template-columns: minmax(72px, auto) minmax(96px, 1fr) minmax(0, 3fr) max-content max-content;
}
.result-word {
  display: grid;
  grid-column: 1 / -1;
  grid-template-columns: subgrid;
  column-gap: 16px;
  align-items: baseline;
  padding: 9px 6px;
  border-bottom: 1px solid var(--line);
  cursor: pointer;
}
/* Fallback for engines without subgrid: each row is its own grid (columns may
   drift between rows, but nothing overflows). */
@supports not (grid-template-columns: subgrid) {
  .result-words { display: flex; flex-direction: column; }
  .result-word {
    grid-template-columns: minmax(72px, auto) minmax(96px, 1fr) minmax(0, 3fr);
    gap: 16px;
  }
  .result-words.has-freq .result-word,
  .result-words.has-level .result-word {
    grid-template-columns: minmax(72px, auto) minmax(96px, 1fr) minmax(0, 3fr) max-content;
  }
  .result-words.has-freq.has-level .result-word {
    grid-template-columns: minmax(72px, auto) minmax(96px, 1fr) minmax(0, 3fr) max-content max-content;
  }
}
.result-word:hover { background: rgba(181, 49, 45, 0.05); }
.rw-hanzi { font-family: var(--han); font-size: 22px; }
.rw-trad { font-size: 15px; color: var(--muted); margin-left: 8px; }
.rw-py { color: var(--gold); font-weight: 600; }
.rw-m { color: var(--ink); min-width: 0; }
/* Frequency rank bucket; a dash (muted) when the word carries no corpus rank. */
.rw-freq {
  white-space: nowrap;
  font-size: 12px;
  font-weight: 600;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.rw-freq.blank { color: var(--unseen); }
/* Layer badge: HSK band (red) vs extended CC-CEDICT (gold), so the two result
   groups are never mistaken for one another. */
.rw-badge {
  justify-self: end;
  align-self: center;
  white-space: nowrap;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.04em;
  padding: 2px 9px;
  border-radius: 999px;
  border: 1px solid var(--red);
  color: var(--red);
}
.rw-badge.ext { border-color: var(--gold); color: var(--gold); background: rgba(199, 149, 66, 0.1); }
/* JLPT level badge: indigo, so Japanese results are never mistaken for HSK. */
.rw-badge.jlpt { border-color: var(--indigo); color: var(--indigo); }
/* Ungraded (non-HSK) word: the level cell is a quiet placeholder, not a badge,
   so the HSK band reads as an occasional tag rather than a category marker. */
.rw-badge.none { border-color: transparent; color: var(--unseen); background: none; font-weight: 400; }
.attribution { color: var(--muted); font-size: 12px; margin: 10px 0 0; }

/* Chinese/Japanese result tabs over combined-language results (js/search.js):
   true tabs, only the selected language's list is rendered. The bar rides
   just below the pinned nav (--nav-h is 0 where the nav scrolls away, i.e.
   desktop) so the other language is always one tap away from anywhere in a
   long list. Flat solid paper - no translucency, blur, or gradient
   show-through - and both tabs pin in the standard red. */
.lang-tabs {
  position: sticky;
  top: var(--nav-h, 0px);
  z-index: 9;
  padding: 8px 0;
  background: var(--paper);
}
.lang-count {
  margin-left: 7px;
  font-weight: 400;
  font-size: 13px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.seg-btn.active .lang-count { color: rgba(255, 255, 255, 0.85); }

/* word chip (detail examples) */
.word-chip {
  font-family: var(--sans);
  font-size: 14px;
  align-items: baseline;
  max-width: min(100%, 42rem);
}
.wc-hanzi { font-family: var(--han); font-size: 18px; flex: 0 0 auto; }
.wc-py { color: var(--gold); font-weight: 600; flex: 0 0 auto; }
.wc-m {
  color: var(--muted);
  display: inline-block;
  max-width: clamp(12rem, 34vw, 24rem);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  vertical-align: bottom;
}
/* extended CC-CEDICT badge on import chips */
.wc-badge.ext {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.03em;
  padding: 1px 6px;
  border-radius: 999px;
  border: 1px solid var(--gold);
  color: var(--gold);
  background: rgba(199, 149, 66, 0.1);
}
.word-chip.ext { border-color: var(--gold); }

/* ---- board ---- */
.board-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px 16px;
  margin-bottom: 10px;
}
.board-filter { flex: 1 1 240px; }
.board-controls { display: inline-flex; align-items: center; gap: 12px 16px; flex-wrap: wrap; }
/* Segmented hanzi/kanji switch, shown when both languages are enabled. */
.seg { display: inline-flex; border: 1px solid var(--line); border-radius: 999px; overflow: hidden; background: var(--card); }
.seg-btn {
  border: none;
  border-radius: 0;
  background: none;
  padding: 8px 14px;
  font-weight: 600;
  color: var(--muted);
}
.seg-btn:hover { color: var(--ink); }
.seg-btn.active { background: var(--red); color: #fff; }
.board-order-wrap {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  color: var(--muted);
  font-size: 12px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  font-weight: 700;
}
.board-order {
  padding: 9px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius-control);
  background: var(--card);
  font-weight: 600;
  color: var(--ink);
  text-transform: none;
  letter-spacing: 0;
}
.board-order:focus { outline: 2px solid var(--gold); outline-offset: 1px; }
.legend { display: inline-flex; align-items: center; gap: 8px; color: var(--muted); font-size: 13px; }
.legend .sample { cursor: default; font-size: 20px; }

.band { margin-bottom: 10px; }
.band-title {
  display: flex;
  align-items: baseline;
  gap: 10px;
  font-size: 15px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--red);
  border-bottom: 1px solid var(--line);
  padding-bottom: 4px;
  margin: 0 0 6px;
}
.band-count { color: var(--muted); font-size: 12px; font-weight: 400; }
.band-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(27px, 1fr));
  gap: 1px;
}
.cell {
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 1;
  font-family: var(--han);
  font-size: 18px;
  color: var(--unseen);
  cursor: pointer;
  border-radius: 4px;
  user-select: none;
  transition: color 0.1s, background 0.1s;
}
.cell:hover { background: rgba(181, 49, 45, 0.1); color: var(--red); }
.cell.seen { color: var(--seen); font-weight: 500; }
.cell.sample { display: inline-flex; width: 1.4em; height: 1.4em; aspect-ratio: auto; }

/* ---- detail ---- */
.hero {
  display: flex;
  gap: 22px;
  align-items: center;
  padding: 8px 0 4px;
}
.hero-char {
  font-family: var(--han);
  font-size: clamp(54px, 13vw, 104px);
  line-height: 1;
  color: var(--red);
}
.hero-py { font-size: 22px; color: var(--gold); font-weight: 700; }
.hero-m { font-size: 18px; margin: 0; max-width: 46ch; }
.hero-tags { display: inline-flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.tag {
  display: inline-block;
  padding: 3px 10px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: var(--card);
  font-size: 13px;
  color: var(--muted);
}
.band-tag { border-color: var(--red); color: var(--red); font-weight: 700; }
.band-tag.jlpt { border-color: var(--indigo); color: var(--indigo); }
/* The two language blocks of a shared character hero. The small-caps
   Hanzi/Kanji (or Chinese word/Japanese word) label appears whenever the
   combined mode is active, or when both blocks are shown. */
.hero-lang {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 8px 14px;
  margin-bottom: 14px;
}
.hero-lang:last-child { margin-bottom: 0; }
.lang-note {
  flex-basis: 100%;
  font-size: 11px;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--muted);
  font-weight: 700;
}
/* kun/on suffix on a kanji reading line. */
.reading-kind {
  font-size: 12px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
  font-weight: 700;
  margin-left: 10px;
}
.hero-py.on { color: var(--indigo); }
.decomp {
  font-family: var(--han);
  font-size: 22px;
  color: var(--muted);
  margin-bottom: 10px;
}
.etymology { max-width: 60ch; }

.panel-title { font-size: 20px; margin: 6px 0; }

/* ---- recent searches (blank search input) ---- */
.recent-searches { margin-top: var(--space-section); }
.recent-query { font-size: 16px; gap: 4px; }
/* Per-chip remove: visually compact, but padding + negative margins keep a
   finger-sized hit target without inflating the chip. */
.chip-remove {
  border: 0;
  background: none;
  font: inherit;
  font-size: 15px;
  line-height: 1;
  color: var(--muted);
  cursor: pointer;
  padding: 10px 8px;
  margin: -10px -10px -10px -4px;
}
.chip-remove:hover { color: var(--red); }
.recent-clear { margin: 12px 0 0; }
.recent-clear .link { color: var(--muted); font-size: 13px; }
.recent-clear .link:hover { color: var(--red); }

/* ---- import ---- */
.import-lang { margin: 6px 0 10px; }
.import-input {
  width: 100%;
  min-height: 180px;
  padding: var(--pad-card);
  border: 1px solid var(--line);
  border-radius: var(--radius-control);
  background: var(--card);
  resize: vertical;
  line-height: 1.7;
  font-family: var(--han);
  font-size: 18px;
}
.import-actions { display: flex; gap: 10px; margin: 12px 0 4px; }
.import-summary { margin-top: 8px; }
.import-stats { line-height: 1.7; }
.import-stats strong { color: var(--red); }
.import-chip { cursor: pointer; }
.import-chip-count {
  color: var(--muted);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
}
.import-more { margin: 10px 0 0; }
.import-section { margin-top: 4px; }

/* ---- settings ---- */
.settings-list { display: flex; flex-direction: column; gap: 2px; }
/* Scoped above `.subsection label` so these rows keep normal (not the uppercase
   heading) typography. */
.settings-list .setting-row {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  margin: 0;
  padding: 10px 6px;
  border-bottom: 1px solid var(--line);
  cursor: pointer;
  text-transform: none;
  letter-spacing: 0;
  font-weight: 400;
  font-size: 16px;
  color: var(--ink);
}
.setting-check {
  width: 18px;
  height: 18px;
  margin-top: 2px;
  flex: none;
  cursor: pointer;
  accent-color: var(--red);
}
.setting-text { display: flex; flex-direction: column; gap: 2px; }
.setting-label { font-weight: 600; }
.setting-hint { color: var(--muted); font-size: 13px; }
.settings-hint { font-size: 13px; margin: 0 0 10px; }
.sync-controls { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.sync-key { flex: 1 1 280px; min-width: 0; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
.sync-status { margin: 10px 0 4px; }
.sync-secret-hint { margin: 0; font-size: 11px; color: var(--muted); }

/* ---- word detail ---- */
/* The hero slot holds a whole word, so it is smaller than a single-character
   hero and allowed to wrap. */
.hero-char.word { font-size: clamp(32px, 8vw, 64px); line-height: 1.05; word-break: break-word; }
.hero-trad { font-family: var(--han); color: var(--muted); font-size: 16px; margin-bottom: 4px; }
.ext-tag { border-color: var(--gold); color: var(--gold); font-weight: 700; }

/* ---- SRS enrollment ---- */
/* Language-first hierarchy: the aggregate All strip heads a frame around the
   non-interactive Chinese/Japanese groups, and inside each group the Read
   button sits inset within the Recall frame (selecting recall implies read,
   so the wider capability visibly contains the one it includes). Each frame
   takes its state classes and colors from the button that heads it (All,
   Recall), so a frame's fill is that button's truthful rendering - but only
   the buttons themselves are interactive: no button nests inside another,
   and the strip and inset hit targets never overlap. */
.learn-panel { margin: var(--space-section) 0; }
.learn-options {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.learn-hierarchy {
  display: grid;
  width: min(100%, 620px);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  background: var(--card);
}
.learn-hierarchy.enrolled { border-color: #8b641f; background: #edc66f; }
.learn-hierarchy.suspended { border-style: dashed; border-color: var(--muted); }
.learn-hierarchy > .learn-all-option { border: none; border-radius: 15px 15px 0 0; }
.learn-languages {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px;
  margin: 2px 8px 8px;
}
.learn-language-group {
  position: relative;
  display: grid;
  align-content: start;
  padding: 24px 8px 8px;
  border: 1px solid var(--line);
  border-radius: 14px;
  background: var(--paper);
}
.learn-language-label {
  position: absolute;
  top: 6px;
  left: 13px;
  color: var(--muted);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.06em;
  line-height: 1.2;
  text-transform: uppercase;
}
.learn-capability-set {
  display: grid;
  align-content: start;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--card);
}
.learn-capability-set.enrolled { border-color: #8b641f; background: #edc66f; }
.learn-capability-set.suspended { border-style: dashed; border-color: var(--muted); }
.learn-options > .learn-capability-set {
  flex: 1 1 170px;
  min-width: min(170px, 100%);
  max-width: 280px;
}
.learn-capability-set > .learn-option[data-profile="recall"] {
  border: none;
  border-radius: 11px 11px 0 0;
}
.learn-capability-set > .learn-option[data-profile="read"] { margin: 2px 8px 8px; }
/* The borderless strip buttons lose the app's border hover cue; restore it
   on the frame they fill. */
.learn-hierarchy:has(> .learn-all-option:hover),
.learn-capability-set:has(> [data-profile="recall"]:hover) { border-color: var(--red); }
.learn-option {
  display: flex;
  min-height: 44px;
  padding: 5px 12px;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  line-height: 1.15;
  text-align: left;
}
.learn-option.enrolled { border-color: #8b641f; background: #edc66f; color: var(--ink); }
.learn-option.suspended { border-style: dashed; border-color: var(--muted); color: #594f46; }
.learn-all-option { align-items: center; text-align: center; }
.learn-option-name { font-size: 14px; }
.hint.compact { padding: 6px 0; }

/* ---- SRS review and management ---- */
.review-head { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin-bottom: var(--space-section); }
.review-counts { margin: 0; font-variant-numeric: tabular-nums; }
.review-head-controls { display: flex; flex: none; flex-wrap: wrap; justify-content: flex-end; gap: 8px; }
.review-switcher, .review-lang { flex: none; }
.review-card {
  max-width: 720px;
  margin: 0 auto;
  padding: clamp(18px, 4vw, 32px);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  background: rgba(255, 250, 242, 0.82);
  box-shadow: 0 8px 32px rgba(25, 21, 18, 0.06);
  text-align: center;
}
.review-request { margin: 0; color: var(--muted); }
.review-prompt { margin: 8px 0; font-family: var(--han); font-size: clamp(38px, 10vw, 72px); line-height: 1.15; overflow-wrap: anywhere; }
.review-card[data-qtype^="definition-"] .review-prompt,
.review-card[data-qtype^="cloze-"] .review-prompt { font-family: var(--sans); font-size: clamp(25px, 6vw, 40px); }
.review-card[data-qtype^="pronunciation-"] .review-prompt { font-family: var(--sans); color: var(--gold); font-size: clamp(26px, 7vw, 46px); }
/* Assisted recall: a sourced usage example with the target blanked out,
   under the definition prompt. The example reads at glyph size (the blanks
   must be as legible as the characters around them); its gloss stays small. */
.review-cloze { margin: 2px 0 6px; }
.review-cloze-example { font-family: var(--han); font-size: clamp(26px, 7vw, 40px); line-height: 1.2; overflow-wrap: anywhere; }
.review-cloze-example[lang="ja"] { font-family: var(--kana); }
.review-cloze-gloss { font-size: 14px; }
.review-cloze-provenance { font-style: italic; white-space: nowrap; }
.review-answer-example { font-size: 13px; margin-top: 6px; }
.review-answer-example [lang] { font-family: var(--han); font-size: 15px; }
.review-answer-example [lang="ja"] { font-family: var(--kana); }
.review-example-search { color: inherit; text-decoration: underline; text-decoration-color: color-mix(in srgb, currentColor 45%, transparent); text-underline-offset: 0.14em; }
.review-example-search:hover { opacity: 1; text-decoration-color: currentColor; }
.review-example-search:focus-visible { outline: 2px solid var(--red); outline-offset: 3px; border-radius: 3px; }
.review-options { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-top: var(--space-section); }
.review-options button { min-height: 58px; border-radius: var(--radius-control); overflow-wrap: anywhere; }
/* Chinese/Japanese answer choices (written forms, kana readings) read at
   glyph size in the Han/kana fonts; pinyin and English options stay at the
   ordinary button size. */
.review-options.cjk button { font-family: var(--han); font-size: clamp(24px, 6vw, 32px); font-weight: 500; }
.review-options.cjk[lang="ja"] button { font-family: var(--kana); }
.review-reveal, .review-next { min-width: 160px; margin-top: 10px; }
/* The reviewed surface links to its entry page. It keeps its glyph styling -
   the link is navigation, not a highlighted control - and announces itself
   only on hover/keyboard focus, so the card stays a quiz, not a hyperlink. */
.review-entry-link { color: inherit; }
.review-entry-link:hover { opacity: 1; text-decoration-thickness: 2px; text-underline-offset: 0.14em; }
.review-entry-link:focus-visible { outline: 2px solid var(--red); outline-offset: 4px; border-radius: 4px; }
.review-choice-link { color: inherit; }
.review-answer { margin: var(--space-section) 0 12px; padding: var(--pad-card); border-radius: var(--radius-control); background: rgba(199, 149, 66, 0.1); }
.review-answer strong { display: block; color: var(--red); }
/* The full-item reveal: the written form leads at glyph size in the Han
   font, the pronunciation and English meaning follow at reading sizes. */
.review-answer-written { font-family: var(--han); font-size: clamp(34px, 9vw, 54px); line-height: 1.2; overflow-wrap: anywhere; }
.review-answer-written[lang="ja"] { font-family: var(--kana); }
.review-answer-pron { color: var(--gold); font-weight: 600; font-size: clamp(17px, 4.5vw, 22px); }
.review-answer-def { margin-top: 2px; }
/* A reviewed word's individual Hanzi/Kanji, each linking to its character
   page. Glyphs render a step larger than the label so they stay legible. */
.review-answer-chars { font-size: 13px; margin-top: 6px; }
.review-char-link { margin-left: 6px; font-family: var(--han); font-size: 17px; line-height: 1; }
.review-char-link[lang="ja"] { font-family: var(--kana); }
.review-char-link:focus-visible { outline: 2px solid var(--red); outline-offset: 2px; border-radius: 3px; }
/* Pre-answer recency ("Last asked: never / 3.0d ago") inside the reveal. */
.review-last-asked { font-size: 13px; margin-top: 6px; font-variant-numeric: tabular-nums; }
/* Exact persisted schedule reported after an immediately committed wrong MCQ. */
.review-next-shown { margin: -4px 0 2px; text-align: center; font-size: 14px; font-variant-numeric: tabular-nums; }
/* Compact post-answer actions: burn (mark known forever) and, on recall
   items, drop recall practice. Quiet by default so they never compete with
   the grade buttons. */
.review-burn, .review-drop-recall { display: block; margin: 10px auto 0; padding: 4px 14px; font-size: 13px; color: var(--muted); }
.review-burn:hover { color: var(--red); }
.review-drop-recall:hover { color: var(--ink); }
.review-drop-note { display: block; margin-top: 10px; text-align: center; font-size: 13px; }
.review-grades { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
.review-grades button { border-radius: var(--radius-control); }
.review-failed-aspect { margin: 12px 0 0; padding: 0; border: 0; min-width: 0; }
.review-failed-aspect legend { margin-bottom: 8px; font-weight: 600; }
.review-failed-aspect-options { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
.review-failed-aspect-options button { min-height: 44px; padding-inline: 8px; border-radius: var(--radius-control); }
/* The projected next interval on Good/Easy. Quieter and tabular so the grade
   word stays the button; nowrap keeps "· 12d" as one token, and the button
   may wrap between word and preview on narrow phones instead of overflowing. */
.grade-preview { font-size: 12px; color: var(--muted); white-space: nowrap; font-variant-numeric: tabular-nums; }
.grade-again { color: var(--red); }
/* Tucked-away link from the revealed answer to the item's drilldown page. */
.review-details { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px; font-size: 13px; margin-top: 4px; }
.review-details-link { color: var(--muted); }
.review-details-link:hover { color: var(--ink); }
.review-context-toggle { min-height: 0; padding: 0; border: 0; background: none; color: var(--muted); font: inherit; text-decoration: underline; text-decoration-style: dotted; text-underline-offset: 0.18em; }
.review-context-toggle:hover, .review-context-toggle.active { color: var(--ink); }
.review-context-toggle.active::after { content: " · on"; text-decoration: none; }
.review-empty { padding: 32px 18px; border: 1px dashed var(--line); border-radius: var(--radius-card); text-align: center; }
.review-empty h2 { margin: 0; }

/* Manage: one dense semantic table row per item. The written form is the
   only visible label; schedule state rides beside it as a compact token and
   the full detail (profile, learning step, lifetime counters) lives in the
   row title. Recency columns are tabular numerals so they scan vertically.
   The wrapper only becomes a horizontal scroller on narrow screens (below):
   an overflow container would also capture the header's sticky top, so on
   desktop the table sits in normal flow and the header pins to the page. */
.srs-table { width: 100%; border-collapse: collapse; }
.srs-table th {
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--paper);
  text-align: left;
  padding: 6px 8px;
  border-bottom: 1px solid var(--line);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
  white-space: nowrap;
}
.srs-table td { padding: 3px 8px; border-bottom: 1px solid var(--line); white-space: nowrap; }
.srs-word { font-size: 20px; }
.srs-item-link { font-family: var(--han); }
.srs-row.suspended .srs-item-link { color: var(--muted); }
/* Compact schedule-state token beside the written form. */
.srs-status { margin-left: 8px; font-size: 10px; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase; color: var(--muted); }
.srs-status.srs-due { color: var(--red); }
.srs-when { font-size: 13px; font-variant-numeric: tabular-nums; }
.srs-when.never, .srs-when.na { color: var(--unseen); }
/* Item-level burn state token. */
.srs-burned { color: var(--gold); font-weight: 600; }
.srs-actions { text-align: right; }
.srs-actions button { padding: 4px 10px; margin-left: 4px; font-size: 12px; min-height: 32px; }

/* Below the manage table's natural width it scrolls sideways inside its
   wrapper with the Word column pinned; a scroll container disables the
   header's sticky top, so the pinned column takes over as the row anchor. */
@media (max-width: 760px) {
  .srs-table-wrap { overflow-x: auto; }
  .srs-table th:first-child, .srs-table .srs-word { position: sticky; left: 0; z-index: 1; background: var(--paper); }
  .srs-table th:first-child { z-index: 2; }
}
.srs-db-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.srs-db-actions input[type="file"] { max-width: 250px; }
.srs-db-actions select { max-width: 280px; padding: var(--pad-control-y) 10px; border: 1px solid var(--line); border-radius: var(--radius-control); background: var(--card); }
.srs-message { color: var(--red); }

/* Per-item drilldown (#/review/item/<key>): the item's identity up top, then
   labelled subsections for profile, schedule facts, per-direction evidence,
   and the persisted answer ledger. Facts are label/value pairs that wrap into
   columns where the width allows and stack on phones. */
.srs-item-detail { max-width: 720px; margin: 0 auto; }
.srs-detail-word { font-family: var(--han); font-size: clamp(34px, 9vw, 54px); line-height: 1.2; overflow-wrap: anywhere; }
.srs-detail-word[lang="ja"] { font-family: var(--kana); }
.srs-detail-word .srs-status { font-size: 12px; vertical-align: middle; }
.srs-detail-gloss, .srs-detail-kind { margin: 2px 0 0; font-size: 14px; }
.srs-facts { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 4px 20px; margin: 0; }
.srs-fact { display: flex; gap: 8px; align-items: baseline; }
.srs-fact dt { flex: none; min-width: 108px; font-size: 12px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; color: var(--muted); }
.srs-fact dd { margin: 0; font-variant-numeric: tabular-nums; }
.srs-detail-actions { margin-top: 10px; }
.srs-detail-actions button { padding: 4px 12px; font-size: 13px; min-height: 32px; }
.srs-context-toggle { padding: 6px 12px; font-size: 13px; min-height: 34px; }
.srs-context-toggle.active { border-color: var(--gold); background: rgba(199, 149, 66, 0.14); }
/* The ledger's question prose may wrap; the recency column stays one token. */
.srs-hist-table td { white-space: normal; }
.srs-hist-table .srs-when { white-space: nowrap; }
.srs-hist-grade { font-variant-numeric: tabular-nums; }
.srs-hist-grade[data-grade="again"] { color: var(--red); }
.srs-hist-grade[data-grade="easy"], .srs-hist-grade[data-grade="elapsed"] { color: var(--gold); }
.srs-hist-failed { min-width: 116px; }
.srs-hist-note { margin: 0 0 6px; font-size: 13px; }

@media (max-width: 560px) {
  .hero { flex-direction: column; align-items: flex-start; gap: 8px; text-align: left; }
  /* On narrow screens the result row becomes a wrapping flex line: hanzi,
     pinyin and the property columns sit together and wrap as needed (so nothing
     overflows), while the meaning drops to its own full-width line. This works
     regardless of how many property columns are enabled. */
  .result-word {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 4px 12px;
  }
  .rw-m { flex-basis: 100%; order: 5; color: var(--muted); }
  /* Sticky primary nav. The header box dissolves (display: contents) so the
     brand is ordinary page content that scrolls away, while the tabs bar pins
     to the top of the viewport on its own. Pinning the tabs rather than the
     whole header keeps the sticky bar compact without any height jump when it
     engages. The bar goes full-bleed by cancelling the wrap's side padding,
     and owns the safe-area top inset: the extra padding keeps the tab pills
     below the iPhone status bar while stuck, and the matching negative margin
     cancels it again in normal flow so the resting layout is unchanged. */
  .nav { display: contents; }
  .brand { width: 100%; margin: 0 0 6px; }
  .tabs {
    position: sticky;
    z-index: 10;
    top: 0;
    margin: 0 calc(-1 * var(--pad-x)) 20px;
    margin-top: calc(-1 * env(safe-area-inset-top, 0px));
    padding: 8px var(--pad-x);
    padding-top: calc(8px + env(safe-area-inset-top, 0px));
    border-bottom: 1px solid var(--line);
    gap: 1px;
    justify-content: space-between;
  }
  .tab { min-height: 34px; padding: 6px 8px; font-size: 14px; }
  .tab-count { display: none; }
  /* Chrome that only makes sense while pinned over scrolling content; the
     .stuck class is toggled by an IntersectionObserver in js/navbar.js.
     Flat solid paper, like the search tabs - no blur or gradient
     show-through. */
  .tabs.stuck {
    background: var(--paper);
    box-shadow: 0 1px 10px rgba(25, 21, 18, 0.1);
  }
  /* The language switcher goes full-bleed under the pinned nav, like the nav
     itself, so results scroll cleanly under it edge to edge. */
  .lang-tabs {
    margin: 0 calc(-1 * var(--pad-x));
    padding: 8px var(--pad-x);
  }
  /* The title plus two segmented controls (language, review/manage) do not
     fit one phone row; wrap the controls onto their own full-width line. */
  .review-head { flex-wrap: wrap; align-items: flex-start; row-gap: 10px; }
  .review-head-controls { width: 100%; justify-content: flex-start; }
  .review-head-controls .seg-btn { padding: 7px 12px; font-size: 14px; }
  .review-card { margin: 0 calc(-1 * min(8px, var(--pad-x))); padding: 18px 14px; }
  .review-options { grid-template-columns: 1fr; }
  /* Phone-width manage table: tighter cells; the horizontal scroller and
     pinned Word column come from the 760px rule above. */
  .srs-table th, .srs-table td { padding-left: 6px; padding-right: 6px; }
  .srs-word { font-size: 18px; }
}

/* Offline/update status pill (js/pwa.js): a small corner notice for the
   initial offline download and for a new version being ready. */
.pwa-pill {
  position: fixed;
  right: max(14px, env(safe-area-inset-right, 0px));
  bottom: calc(14px + env(safe-area-inset-bottom, 0px));
  z-index: 20;
  padding: 6px 14px;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 999px;
  box-shadow: 0 2px 10px rgba(25, 21, 18, 0.14);
  color: var(--muted);
  font-size: 13px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.3s;
}
.pwa-pill.show { opacity: 1; pointer-events: auto; }
.pwa-pill.clickable { cursor: pointer; color: var(--ink); }
