/* GENERATED FILE — do not edit.
   Built from styles/*.css by scripts/build/css.js, concatenated in filename
   order. Edit the partial that owns the section, then re-run the build. */
/* ── Pretendard, self-hosted ────────────────────────────────────────────────
   A complete Hangul face is around 4 MB, because it carries all 11,172
   composed syllable blocks. Shipping that is not an option, and subsetting it
   statically does not work either — the site renders several thousand distinct
   syllables across its pages and any one page uses a different few hundred.

   The standard Korean web answer is the *dynamic subset*: the face is split
   into woff2 files, each declared with the `unicode-range` it covers, and the
   browser downloads only the ranges the page actually renders. A typical page
   pulls 40–90 KB.

   The files are not in the repo. Drop them into static/fonts/ from a
   Pretendard release. Everything degrades cleanly without them: the fallback
   stack in 00-tokens.css is Apple SD Gothic Neo / Malgun Gothic / Noto Sans
   KR, all real Korean faces, and layout.js only emits the preload when the
   file is actually on disk.

   `font-display: swap` throughout: a learner reading Korean must never be
   shown a blank page waiting for a face.
   ─────────────────────────────────────────────────────────────────────────── */

@font-face {
  font-family: Pretendard;
  font-weight: 400 700;
  font-display: swap;
  src: url("/fonts/pretendard-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+2000-206F,
                 U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215;
}

/* The Hangul ranges, split so a page that renders 가격 does not also download 힣. */
@font-face {
  font-family: Pretendard;
  font-weight: 400 700;
  font-display: swap;
  src: url("/fonts/pretendard-hangul-jamo.woff2") format("woff2");
  unicode-range: U+1100-11FF, U+3130-318F, U+A960-A97F, U+D7B0-D7FF;
}
@font-face {
  font-family: Pretendard;
  font-weight: 400 700;
  font-display: swap;
  src: url("/fonts/pretendard-hangul.woff2") format("woff2");
  unicode-range: U+AC00-D7A3;
}
/* Hanja. Only the /hanja/ pages and the hanja chip in a row need this, and
   both can wait for it — hence its own range rather than folding it into the
   Hangul file every page loads. */
@font-face {
  font-family: Pretendard;
  font-weight: 400 700;
  font-display: swap;
  src: url("/fonts/pretendard-hanja.woff2") format("woff2");
  unicode-range: U+3400-4DBF, U+4E00-9FFF, U+F900-FAFF;
}

/* ── Design tokens ───────────────────────────────────────────────────────────
   The target is the visual language of Korean product teams — Toss, Naver,
   Kakao, 클래스카드 — not a Western study site with Korean text pasted into it.
   Three things carry most of that: one saturated accent on near-white, corners
   at 16px, and a type scale with Korean leading rather than Latin leading.

   The six level colours are a real sweep, not six hand-picked values: an even
   hue walk from mint (165°) to indigo-navy (268°) at fixed chroma, with
   lightness descending in equal steps so the ladder reads as progression as
   well as as difference. Generated in OKLCH and checked for contrast — every
   one clears 4:1 on white and 5.8:1 on the dark ground, so any two are still
   tellable apart at 12px.
   ───────────────────────────────────────────────────────────────────────── */

:root {
  /* ── Type ──
     Pretendard is the de facto Korean UI face and the one that makes a page
     read as a Korean product. It is self-hosted as a dynamic subset (see
     00-fonts.css); the system fallbacks after it are all genuinely good Korean
     faces, so a build without the font files still looks right on macOS, iOS
     and Windows rather than dropping to a serif. */
  --font: Pretendard, -apple-system, BlinkMacSystemFont, "Apple SD Gothic Neo",
          "Malgun Gothic", "Noto Sans KR", "Segoe UI", Roboto, sans-serif;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;

  --t-xs: 0.75rem;     /* 12 — micro labels, the English under a Korean one */
  --t-sm: 0.875rem;    /* 14 — meta, captions, sentence glosses */
  --t-base: 1rem;      /* 16 — body */
  --t-md: 1.125rem;    /* 18 — lead paragraphs, Korean example sentences */
  --t-lg: 1.375rem;    /* 22 — h3, headwords in a row */
  --t-xl: 1.875rem;    /* 30 — h2 */
  --t-2xl: 2.5rem;     /* 40 — h1 */
  --t-3xl: 3.5rem;     /* 56 — home display */

  /* Korean needs more leading than Latin. A Hangul syllable block is square
     and full-height — there are no ascenders and descenders creating optical
     space between lines — so the 1.65 that reads as comfortable in German
     reads as cramped here. 1.75 is what Korean product typography uses. */
  --lh-tight: 1.15;
  --lh-snug: 1.35;
  --lh-body: 1.75;

  /* Pretendard is drawn to be tracked tight at display sizes, and every Korean
     product page does it. Never applied to body copy, where it hurts. */
  --tr-display: -0.03em;
  --tr-head: -0.02em;
  --tr-label: 0.04em;

  /* Space — 4px base, 8px rhythm */
  --s-1: 4px;  --s-2: 8px;  --s-3: 12px; --s-4: 16px; --s-5: 24px;
  --s-6: 32px; --s-7: 48px; --s-8: 64px; --s-9: 96px;

  /* Grid */
  --shell: 1180px;
  --shell-narrow: 760px;
  --gutter: 20px;
  --header-h: 58px;
  --tabbar-h: 56px;

  /* Corners. Korean products round hard — 16px on a card is the norm, not a
     flourish, and square edges read as a table rather than as a surface. */
  --r-sm: 8px;
  --r: 12px;
  --r-lg: 16px;
  --r-xl: 20px;
  --r-pill: 999px;

  /* Elevation — two steps. 1 is "resting on the page", 2 is "over it". */
  --shadow-1: 0 1px 2px rgba(16, 20, 32, 0.04), 0 2px 8px rgba(16, 20, 32, 0.05);
  --shadow-2: 0 2px 8px rgba(16, 20, 32, 0.07), 0 16px 40px rgba(16, 20, 32, 0.13);

  /* Neutrals — light. Near-white ground with a cooler sunken grey, which is
     the Korean convention: the page is white and the sections that group
     things sit *below* it rather than floating above it. */
  --bg: #ffffff;
  --bg-sunk: #f4f6f9;
  --bg-raise: #ffffff;
  --ink: #16181d;
  --ink-2: #454a55;
  --ink-3: #6b727e;
  --ink-4: #949aa6;
  --rule: #e6e9ee;
  --rule-strong: #16181d;
  --sel: #2d69de;
  --sel-ink: #ffffff;

  /* Brand. One saturated accent on near-white is the Korean convention — Toss
     blue, Naver green, Kakao yellow. Indigo-blue reads as "study product" in
     that market and collides with neither sibling site. */
  --accent: #2d69de;
  --accent-ink: #ffffff;
  --accent-wash: #eef3fd;
  --accent-line: #c3d6f8;

  /* 태극기 red and blue. Available to /topik/ and to nothing else — used
     site-wide they stop being a brand and become a flag. */
  --taeguk-red: #cd2e3a;
  --taeguk-blue: #0047a0;

  /* TOPIK levels, 1급 → 6급. Mint → indigo-navy. */
  --lvl-0: #6b727e;  /* ungraded — deliberately neutral, it is not a rung */
  --lvl-1: #008f66;
  --lvl-2: #00887b;
  --lvl-3: #007e8d;
  --lvl-4: #007199;
  --lvl-5: #09619f;
  --lvl-6: #39519c;

  /* State. Red is wrong answers only, green is right answers only. Neither is
     ever a level colour, which is why the ladder above stays out of both. */
  --ok: #0f7a4a;
  --ok-bg: #e7f6ee;
  --bad: #c02a30;
  --bad-bg: #fcecec;
  --warn: #9a6300;
  --warn-bg: #fdf3e2;
  --focus: #2d69de;
}

/* ── Dark ──────────────────────────────────────────────────────────────────
   A soft charcoal ground rather than near-black. #0d0d0f under #f2f2f0 text is
   about 18:1 — far past what anyone needs, and far enough past it that long
   sessions produce halation. This sits nearer 13:1 and gives raised surfaces
   somewhere to go: a card at #23262d is visibly lifted off #16181d.
   ───────────────────────────────────────────────────────────────────────── */

:root[data-theme="dark"] {
  --bg: #16181d;
  --bg-sunk: #1c1f25;
  --bg-raise: #23262d;
  --ink: #edeef1;
  --ink-2: #c3c7cf;
  --ink-3: #9aa0ab;
  --ink-4: #79808b;
  --rule: #333740;
  --rule-strong: #edeef1;
  --sel: #6ea1f0;
  --sel-ink: #16181d;

  --shadow-1: 0 1px 2px rgba(0, 0, 0, 0.32), 0 2px 8px rgba(0, 0, 0, 0.24);
  --shadow-2: 0 2px 8px rgba(0, 0, 0, 0.38), 0 16px 40px rgba(0, 0, 0, 0.46);

  --accent: #6ea1f0;
  --accent-ink: #16181d;
  --accent-wash: #1e2634;
  --accent-line: #2f4468;

  --taeguk-red: #e8636d;
  --taeguk-blue: #5b8ede;

  --lvl-0: #8b929d;
  --lvl-1: #60c89f;
  --lvl-2: #3dc3b5;
  --lvl-3: #2cbac8;
  --lvl-4: #3cafd6;
  --lvl-5: #5da0de;
  --lvl-6: #7792dc;

  --ok: #52c98a;
  --ok-bg: #17301f;
  --bad: #ec7d81;
  --bad-bg: #331f21;
  --warn: #e0a53f;
  --warn-bg: #302716;
  --focus: #6ea1f0;
}

/* ── Reset & base typography ───────────────────────────────────────────── */

*, *::before, *::after { box-sizing: border-box; }

/* The `hidden` attribute has to win.
   The browser hides it with a plain `display: none` from the UA sheet, which
   any author rule of equal specificity beats — so a component styled
   `display: grid` stays on screen with `hidden` set, and every control app.js
   toggles that way (the flashcard's finish panel, the grade row, an empty mark
   host) is visible when it should not be. Stated once, here, rather than
   patched per component. */
[hidden] { display: none !important; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--header-h) + 16px);
}
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font);
  font-size: var(--t-base);
  line-height: var(--lh-body);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  font-feature-settings: "kern" 1;
  /* Room for the mobile tab bar, which is fixed to the bottom. */
  padding-bottom: env(safe-area-inset-bottom);
}

/* ═══════════════════════════════════════════════════════════════════════════
   THE ONE NON-NEGOTIABLE RULE

   Browsers break Korean at any character by default, because CSS treats it
   like Chinese and Japanese, where that is correct. It is not correct for
   Korean. Korean is written with spaces and breaks at 어절 (word-phrase)
   boundaries, exactly as English breaks at word boundaries.

   Without this, every heading and every card splits mid-word — 한국어를 becomes
   "한국" / "어를" across a line break — and the page reads as machine output to
   anyone who can read Korean. It is the single clearest tell of a Korean site
   built by people who do not read Korean.

   `line-break: strict` additionally stops a line ending on a small kana-style
   character or an opening bracket. `overflow-wrap: break-word` is the escape
   hatch for a single 어절 genuinely longer than its container, which would
   otherwise overflow rather than break.

   A regression here is a build-breaking bug, not a style nit.
   ═══════════════════════════════════════════════════════════════════════════ */
:where(body, h1, h2, h3, h4, h5, h6, p, li, dt, dd, td, th, summary, figcaption, .ko, [lang="ko"]) {
  word-break: keep-all;
  line-break: strict;
  overflow-wrap: break-word;
}

h1, h2, h3, h4, h5 { margin: 0; font-weight: 600; line-height: var(--lh-snug); letter-spacing: var(--tr-head); }
h1 { font-size: var(--t-2xl); line-height: var(--lh-tight); letter-spacing: var(--tr-display); }
h2 { font-size: var(--t-xl); }
h3 { font-size: var(--t-lg); }
h4 { font-size: var(--t-base); }
p { margin: 0 0 var(--s-4); }
p:last-child { margin-bottom: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }

a { color: inherit; text-decoration: none; }
a:not([class]) {
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.18em;
  text-decoration-color: var(--ink-4);
}
a:not([class]):hover { text-decoration-color: currentColor; }

b, strong { font-weight: 600; }
small { font-size: var(--t-sm); }

::selection { background: var(--sel); color: var(--sel-ink); }

:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }

img, svg { max-width: 100%; }
svg { display: block; }

hr { border: 0; border-top: 1px solid var(--rule); margin: var(--s-6) 0; }

/* ── Korean text ──────────────────────────────────────────────────────────
   Two rules that apply everywhere Korean appears.

   NEVER synthesise bold or italic on Hangul. There is no true Hangul italic —
   the browser's oblique transform slants square syllable blocks and the result
   looks broken, not emphatic. Emphasis on Korean is weight and colour only,
   and Pretendard ships real weights so the browser never has to fake one.

   Tracking is tightened only at display sizes, where Pretendard expects it. */
.ko, [lang="ko"] {
  font-synthesis: none;
  font-style: normal;
}
.ko em, [lang="ko"] em, .ko i, [lang="ko"] i { font-style: normal; font-weight: 600; }

/* Hanja renders in the same face but wants a touch more space around it —
   the glyphs are denser than Hangul at the same size.

   Tagged ko-Hani, not zh-Hant: 學校 on this site is the Korean word 학교
   written in Han script, not Chinese text. The tag decides both the glyph
   source — Korean uses the traditional K forms, which differ from both the
   simplified and the Taiwanese shapes — and what a screen reader does with
   it, and zh-Hant makes it read 學 as Mandarin "xué" instead of 학. */
[lang="ko-Hani"] { font-synthesis: none; letter-spacing: 0.02em; }

mark {
  background: var(--accent-wash);
  color: inherit;
  border-radius: 3px;
  padding: 0 0.12em;
  box-shadow: inset 0 -0.32em 0 var(--accent-wash);
}

/* ── Grid ─────────────────────────────────────────────────────────────── */

.shell {
  width: 100%;
  max-width: var(--shell);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.shell-narrow { max-width: var(--shell-narrow); }
main.shell { padding-block: var(--s-6) var(--s-9); }

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  background: var(--ink);
  color: var(--bg);
  padding: var(--s-2) var(--s-4);
  border-radius: 0 0 var(--r) 0;
}
.skip-link:focus { left: 0; }

.empty {
  color: var(--ink-3);
  padding: var(--s-6);
  text-align: center;
  background: var(--bg-sunk);
  border-radius: var(--r-lg);
}

/* ── Site header ─────────────────────────────────────────────────────────
   Sticky, one row, and quiet: the header is not where the site competes. The
   nav panels below it carry the whole site map so crawl depth stays at two
   hops from any page.
   ───────────────────────────────────────────────────────────────────────── */

.site-header {
  position: sticky;
  top: 0;
  z-index: 40;
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  backdrop-filter: saturate(180%) blur(12px);
  border-bottom: 1px solid var(--rule);
}
.header-inner {
  height: var(--header-h);
  display: flex;
  align-items: center;
  gap: var(--s-5);
}

/* ── Wordmark ──
   The mark is 길 ("path") in a filled square. A Hangul syllable rather than a
   flag: 태극기 colours are reserved for /topik/. */
.brand { display: inline-flex; align-items: center; gap: var(--s-2); flex: none; }
.mark {
  display: grid;
  place-items: center;
  width: calc(var(--mark) * 1.45);
  height: calc(var(--mark) * 1.45);
  border-radius: calc(var(--mark) * 0.36);
  background: var(--accent);
  color: var(--accent-ink);
  font-size: calc(var(--mark) * 0.78);
  font-weight: 700;
  line-height: 1;
  letter-spacing: 0;
}
.brand-name {
  font-size: var(--t-base);
  font-weight: 500;
  letter-spacing: var(--tr-head);
  white-space: nowrap;
}
.brand-name b { font-weight: 700; }

/* ── Main nav ──
   Five plain links, no panels. Bilingual: the Korean sits under the English,
   both inside the same link. */
.main-nav { flex: 1; min-width: 0; display: flex; align-items: center; gap: var(--s-1); }
.nav-link {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1px;
  padding: var(--s-2) var(--s-3);
  border-radius: var(--r-sm);
  color: var(--ink-2);
  font-size: var(--t-sm);
  line-height: 1.15;
  white-space: nowrap;
}
.nav-link .nav-en { font-size: var(--t-sm); font-weight: 600; color: var(--ink); }
.nav-link .nav-ko { font-size: 10px; font-weight: 500; color: var(--ink-4); }
.nav-link:hover { background: var(--bg-sunk); }
.nav-link.is-active { background: var(--accent-wash); }
.nav-link.is-active .nav-en { color: var(--accent); }

/* ── Header buttons ── */
.header-actions { display: flex; align-items: center; gap: var(--s-2); flex: none; }
.ghost-btn {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  height: 34px;
  padding: 0 var(--s-3);
  border: 1px solid var(--rule);
  border-radius: var(--r-pill);
  background: var(--bg);
  color: var(--ink-2);
  font: inherit;
  font-size: var(--t-sm);
  cursor: pointer;
}
.ghost-btn:hover { background: var(--bg-sunk); color: var(--ink); }
.ghost-btn.icon-only { padding: 0; width: 34px; justify-content: center; }
.ghost-btn kbd {
  font: inherit;
  font-size: 11px;
  color: var(--ink-4);
  border: 1px solid var(--rule);
  border-radius: 4px;
  padding: 0 4px;
}
.roman-toggle[aria-pressed="true"] {
  background: var(--accent-wash);
  border-color: var(--accent-line);
  color: var(--accent);
}
.nav-toggle { display: none; }

/* ── Breadcrumbs ── */
.breadcrumbs {
  border-bottom: 1px solid var(--rule);
  background: var(--bg);
  font-size: var(--t-sm);
}
.breadcrumbs ol { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2); padding: var(--s-3) 0; }
.breadcrumbs li { color: var(--ink-3); display: flex; align-items: center; gap: var(--s-2); }
.breadcrumbs li + li::before { content: "›"; color: var(--ink-4); }
.breadcrumbs a:hover { color: var(--ink); text-decoration: underline; }
.breadcrumbs [aria-current] { color: var(--ink); }

/* ── Search overlay ── */
.search-overlay {
  position: fixed;
  inset: 0;
  z-index: 60;
  background: rgba(16, 20, 32, 0.42);
  display: grid;
  place-items: start center;
  padding: 12vh var(--gutter) var(--gutter);
}
.search-panel {
  width: min(640px, 100%);
  background: var(--bg-raise);
  border-radius: var(--r-xl);
  box-shadow: var(--shadow-2);
  overflow: hidden;
}
.search-row { display: flex; align-items: center; gap: var(--s-2); padding: var(--s-3) var(--s-4); border-bottom: 1px solid var(--rule); }
#search-input {
  flex: 1;
  border: 0;
  background: none;
  color: var(--ink);
  font: inherit;
  font-size: var(--t-md);
  padding: var(--s-2) 0;
}
#search-input:focus { outline: none; }
.search-hint { padding: var(--s-2) var(--s-4); font-size: var(--t-xs); color: var(--ink-4); border-bottom: 1px solid var(--rule); }
.search-status { padding: var(--s-2) var(--s-4); font-size: var(--t-sm); color: var(--ink-3); }
.search-status:empty { display: none; }
.search-results { max-height: 52vh; overflow-y: auto; padding: 0 var(--s-2) var(--s-2); }
.search-hit {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: baseline;
  gap: var(--s-3);
  padding: var(--s-3);
  border-radius: var(--r);
  cursor: pointer;
}
.search-hit:hover, .search-hit.is-active { background: var(--bg-sunk); }
.search-hit .hit-ko { font-size: var(--t-md); font-weight: 600; }
.search-hit .hit-roman { font-size: var(--t-xs); color: var(--ink-4); }
.search-hit .hit-en { color: var(--ink-2); font-size: var(--t-sm); }

/* ── The account control ──────────────────────────────────────────────────

   Sits last in .header-actions. It is chrome, so it belongs beside the header
   partial, and it has to come AFTER it in the cascade: .auth-avatar and
   .ghost-btn are the same specificity, so source order is what lets the avatar
   take its own radius and background. Hence 03-auth rather than 02-auth — the
   files are concatenated in filename order and "02-auth" would sort before
   "02-header".

   Both states ship hidden and static/firebase.js reveals exactly one, once
   Firebase has resolved the stored session. So the slot reserves the width of
   an icon-only button: the header must not reflow when it resolves, and a
   signed-in reader must never see "Sign in" flash first. A reader whose
   browser blocks the SDK sees no control at all, which is honest — it would
   not work. */

.auth {
  position: relative;
  display: flex;
  align-items: center;
  min-width: 29px;
}
.auth-signin[hidden],
.auth-account[hidden] { display: none; }
.auth-signin[aria-busy="true"] { opacity: 0.55; cursor: progress; }

/* Sign-in failed. Set by static/firebase.js for eight seconds, with the long
   form on the button's title — the alternative is a control that swallows the
   failure and looks like it was never clicked. */
.auth-signin.is-failed {
  color: var(--bad);
  background: var(--bad-bg);
  border-color: var(--bad);
}

/* The initial, in the same square as the icon-only buttons beside it. No
   remote avatar: it would mean a request to googleusercontent.com on every
   page load, and the chrome carries no other photographs. Round, because it is
   standing in for a face. */
.auth-avatar {
  justify-content: center;
  width: 34px;
  height: 34px;
  padding: 0;
  border-radius: var(--r-pill);
  background: var(--bg-sunk);
  font-size: var(--t-sm);
  font-weight: 600;
  line-height: 1;
  color: var(--ink);
  text-transform: uppercase;
}

/* Same panel as the nav dropdowns, but right-aligned rather than centred:
   this is the last control in the header and a centred panel would overflow
   the shell. */
.auth-panel {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  min-width: 224px;
  padding: var(--s-2);
  background: var(--bg-raise);
  border: 1px solid var(--rule);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-2);
  opacity: 0;
  visibility: hidden;
  transform: translateY(-4px);
  transition: opacity 0.13s ease, transform 0.13s ease, visibility 0.13s;
  z-index: 3;
}
.auth-account.is-open .auth-panel {
  opacity: 1;
  visibility: visible;
  transform: none;
}

/* A display name and a Google address are both arbitrary strings that can be
   longer than the panel: anywhere is the only break that will not push the
   panel wider than the viewport. */
.auth-who {
  display: grid;
  gap: 2px;
  padding: var(--s-2) var(--s-3) var(--s-3);
  border-bottom: 1px solid var(--rule);
  overflow-wrap: anywhere;
}
.auth-who b { font-size: var(--t-sm); color: var(--ink); }
.auth-who span { font-size: var(--t-xs); color: var(--ink-3); }

.auth-link,
.auth-signout {
  display: block;
  width: 100%;
  padding: 10px var(--s-3);
  background: none;
  border: 0;
  border-radius: var(--r-sm);
  font: inherit;
  font-size: var(--t-sm);
  text-align: left;
  color: var(--ink-2);
  cursor: pointer;
}
.auth-link { margin-top: var(--s-1); }
.auth-link:hover,
.auth-signout:hover { background: var(--bg-sunk); color: var(--ink); }

/* ── Shared blocks: section headers, cards, chips, stats, pagination ─────
   The Korean layout language. Each of these appears on most pages, and each
   is lifted from a pattern that is universal in Korean products rather than
   invented here — see PLAN.md §4.4 for the provenance of each.
   ───────────────────────────────────────────────────────────────────────── */

/* ── Section header with 전체보기 › on the right ──
   Naver and Coupang put this on every section of every page. The Korean label
   sits above the English one; the "see all" link is right-aligned and quiet. */
.section-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--s-4);
  margin-bottom: var(--s-4);
}
.section-head h2, .section-head h3 { display: flex; flex-direction: column; gap: 2px; }
.head-ko {
  order: -1;
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: var(--tr-label);
  color: var(--ink-4);
}
.head-en { font-size: var(--t-xl); font-weight: 700; letter-spacing: var(--tr-head); color: var(--ink); }
.section-head h3 .head-en { font-size: var(--t-lg); }
.section-sub { margin: var(--s-2) 0 0; color: var(--ink-3); font-size: var(--t-sm); max-width: 60ch; }
.head-more {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: var(--s-1);
  font-size: var(--t-sm);
  color: var(--ink-3);
  padding: var(--s-1) var(--s-2);
  border-radius: var(--r-sm);
}
.head-more:hover { color: var(--accent); background: var(--accent-wash); }

/* ── Cards ──
   The universal Korean surface: rounded, one soft shadow step, a coloured
   left edge when the card belongs to a level. */
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: var(--s-3);
}
.card-grid.cols-2 { grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); }
.card-grid.cols-3 { grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); }

.card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: var(--s-4);
  background: var(--bg-raise);
  border: 1px solid var(--rule);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-1);
  transition: transform 0.12s ease, box-shadow 0.12s ease, border-color 0.12s ease;
}
.card:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow-2);
  border-color: var(--accent-line);
}
.card-level { border-left: 3px solid var(--lvl, var(--accent)); }
.card-ko { font-size: var(--t-xs); font-weight: 600; letter-spacing: var(--tr-label); color: var(--lvl, var(--ink-4)); }
.card-title { font-size: var(--t-lg); font-weight: 700; letter-spacing: var(--tr-head); }
.card-sub { font-size: var(--t-sm); color: var(--ink-2); line-height: 1.55; }
.card-meta { margin-top: var(--s-2); font-size: var(--t-xs); color: var(--ink-4); }
.card-cta { position: absolute; top: var(--s-4); right: var(--s-4); color: var(--ink-4); font-size: var(--t-lg); line-height: 1; }

/* ── Chips ──
   Metadata rows: 3급 · 동사 · ㅂ불규칙 · 한자어. Universal in Korean products
   and the reason a row can carry six facts without looking like a table. */
.chips { display: flex; flex-wrap: wrap; gap: var(--s-1); align-items: center; }
.chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px var(--s-2);
  border-radius: var(--r-pill);
  background: var(--bg-sunk);
  border: 1px solid transparent;
  font-size: var(--t-xs);
  font-weight: 500;
  color: var(--ink-2);
  white-space: nowrap;
}
a.chip:hover { border-color: var(--rule); color: var(--ink); }
.chip-ko { color: var(--ink-4); font-weight: 400; }
.chip b { font-weight: 700; }
.chip-level {
  background: color-mix(in srgb, var(--lvl) 12%, transparent);
  color: var(--lvl);
  font-weight: 600;
}
.chip-hanja { background: var(--warn-bg); color: var(--warn); }
.chip-irr { background: var(--accent-wash); color: var(--accent); }

/* ── Stat row ──
   Toss's convention: a big number carrying the type weight with a small muted
   unit as a suffix, not as a second line. Reads as a figure, not a table cell. */
.stat-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  gap: var(--s-4);
  margin: 0;
  padding: var(--s-5);
  background: var(--bg-sunk);
  border-radius: var(--r-lg);
}
.stat dt {
  display: flex;
  flex-direction: column;
  font-size: var(--t-sm);
  font-weight: 500;
  color: var(--ink-2);
}
.stat dt [lang="ko"] { order: 1; font-size: var(--t-xs); font-weight: 400; letter-spacing: var(--tr-label); color: var(--ink-4); }
.stat dd { margin: var(--s-1) 0 0; display: flex; align-items: baseline; gap: 3px; }
.stat dd b { font-size: var(--t-xl); font-weight: 700; letter-spacing: var(--tr-head); line-height: 1.1; }
.stat-unit { font-size: var(--t-sm); color: var(--ink-4); }

/* ── Intro / lead ── */
.intro { max-width: 68ch; margin-bottom: var(--s-6); color: var(--ink-2); font-size: var(--t-md); }
.intro p { line-height: 1.7; }
.intro b { color: var(--ink); }

.page-head { margin-bottom: var(--s-5); }
.page-head h1 { display: flex; flex-direction: column; gap: var(--s-2); }
.page-head .h1-ko { font-size: var(--t-lg); font-weight: 600; color: var(--accent); letter-spacing: var(--tr-head); }

/* ── Pagination ── */
.list-nav {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  margin: var(--s-5) 0;
}
.list-count { margin: 0; font-size: var(--t-sm); color: var(--ink-3); }
.pagination { display: flex; flex-wrap: wrap; gap: var(--s-1); }
.pg {
  display: inline-grid;
  place-items: center;
  min-width: 34px;
  height: 34px;
  padding: 0 var(--s-2);
  border: 1px solid var(--rule);
  border-radius: var(--r-sm);
  font-size: var(--t-sm);
  color: var(--ink-2);
  background: var(--bg);
}
.pg:hover { border-color: var(--accent-line); color: var(--accent); }
.pg.is-current { background: var(--ink); border-color: var(--ink); color: var(--bg); font-weight: 600; }
.pg-gap { display: inline-grid; place-items: center; min-width: 20px; color: var(--ink-4); }

/* ── Cross-level strip ── */
.cross-level { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2); margin: var(--s-5) 0; font-size: var(--t-sm); }
.cross-label { color: var(--ink-3); }
.cross-level a {
  padding: var(--s-1) var(--s-3);
  border-radius: var(--r-pill);
  border: 1px solid color-mix(in srgb, var(--lvl) 34%, transparent);
  color: var(--lvl);
  font-weight: 600;
}
.cross-level a:hover { background: color-mix(in srgb, var(--lvl) 10%, transparent); }

/* ── FAQ ── */
.faq { margin-top: var(--s-8); }
.faq-list { display: grid; gap: var(--s-2); }
.faq-item {
  border: 1px solid var(--rule);
  border-radius: var(--r-lg);
  background: var(--bg-raise);
  overflow: hidden;
}
.faq-item > summary {
  padding: var(--s-4);
  cursor: pointer;
  font-weight: 600;
  list-style: none;
  display: flex;
  justify-content: space-between;
  gap: var(--s-3);
}
.faq-item > summary::-webkit-details-marker { display: none; }
.faq-item > summary::after { content: "+"; color: var(--ink-4); font-weight: 400; flex: none; }
.faq-item[open] > summary::after { content: "−"; }
.faq-a { padding: 0 var(--s-4) var(--s-4); color: var(--ink-2); }

.source-note { font-size: var(--t-sm); color: var(--ink-4); margin-top: var(--s-6); }

/* ── Two-column layout: a sticky aside beside the content ─────────────────
   Content first, aside second, so the single-column fallback stacks in the
   order the page reads. `.split-index` in 10-vocab-rows.css flips the columns
   for the word index, which is a rail the reader works down rather than a
   footnote beside it.
   ───────────────────────────────────────────────────────────────────────── */
.split {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 300px;
  gap: var(--s-7);
  align-items: start;
}
.split-aside { position: sticky; top: calc(var(--header-h) + var(--s-4)); display: grid; gap: var(--s-6); }
.split-main { min-width: 0; }

/* ── The level rail ──────────────────────────────────────────────────────
   1급 → 6급 as a segmented control, in the same place on every page. It is the
   site's spine: every page shows where the reader is on the ladder, and one
   tap moves along it.

   A segmented control rather than a <select>, because that is what Korean
   products use for a small fixed set — and because a select hides five of the
   six options behind a tap. The 급 leads; the English name sits under it.
   ───────────────────────────────────────────────────────────────────────── */

.level-rail {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  gap: 2px;
  padding: 3px;
  margin-bottom: var(--s-6);
  background: var(--bg-sunk);
  border-radius: var(--r-lg);
}
.rail-step {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1px;
  padding: var(--s-2) var(--s-1);
  border-radius: var(--r);
  text-align: center;
  transition: background 0.12s ease, color 0.12s ease;
}
.rail-step:hover { background: color-mix(in srgb, var(--lvl) 10%, transparent); }
.rail-code { font-size: var(--t-sm); font-weight: 700; color: var(--lvl); letter-spacing: var(--tr-head); white-space: nowrap; }
.rail-name { font-size: 10px; letter-spacing: var(--tr-label); text-transform: uppercase; color: var(--ink-4); white-space: nowrap; }
.rail-step.is-current {
  background: var(--bg-raise);
  box-shadow: var(--shadow-1);
}
.rail-step.is-current .rail-code { color: var(--lvl); }
.rail-step.is-current .rail-name { color: var(--ink-2); }
.rail-step.is-common .rail-code { font-size: var(--t-sm); }

/* ── The study layer: word marks, the effort grid, recall mode, the plan bar ─
   Everything in this file is shared by three surfaces — a vocabulary row, the
   daily plan's day table and the back of a flashcard — because all three write
   to one store. A mark set in one place has to look the same in the other two,
   or the reader has to work out whether they are looking at two piles.

   None of it ships in the HTML. app.js paints every control into a host that
   arrives empty, so a control that cannot work without JavaScript is never
   printed as though it could.
   ───────────────────────────────────────────────────────────────────────── */

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ── The two marks ──
   "Know it" is a toggle and "Remind me" is a gap. Green for known is the one
   place the state palette is used outside a right answer, and it is the same
   idea: this word is settled. */
.mark-done {
  display: inline-flex;
  align-items: center;
  gap: var(--s-1);
  padding: 5px var(--s-3) 5px var(--s-2);
  border: 1px solid var(--rule);
  border-radius: var(--r-pill);
  background: var(--bg);
  color: var(--ink-3);
  font: inherit;
  font-size: var(--t-xs);
  font-weight: 500;
  line-height: 1;
  cursor: pointer;
  transition: background 0.12s ease, color 0.12s ease, border-color 0.12s ease;
}
.mark-done:hover { border-color: var(--ok); color: var(--ok); }
.mark-done[aria-pressed="true"] { background: var(--ok-bg); border-color: var(--ok); color: var(--ok); }
.mark-done svg { flex: none; }

/* The compact variant is the round button in a row's control bar, where the
   bar sits over the summary and there is room for an icon and nothing else. */
.mark-done.is-compact {
  width: 30px;
  height: 30px;
  padding: 0;
  border-radius: 50%;
  justify-content: center;
}

.mark-remind {
  appearance: none;
  padding: 5px var(--s-2);
  border: 1px solid var(--rule);
  border-radius: var(--r-pill);
  background: var(--bg);
  color: var(--ink-3);
  font: inherit;
  font-size: var(--t-xs);
  line-height: 1;
  cursor: pointer;
  max-width: 140px;
}
.mark-remind:hover { border-color: var(--accent-line); color: var(--accent); }
.mark-remind[data-set] { border-color: var(--accent-line); background: var(--accent-wash); color: var(--accent); font-weight: 600; }
/* A due word is not a scheduled word: it is work waiting. */
.mark-remind[data-due] { border-color: var(--warn); background: var(--warn-bg); color: var(--warn); font-weight: 600; }

.row-mark { display: inline-flex; }

/* ── A word's state, on the row that carries it ──
   A quiet left edge rather than a filled row: forty of these on a page, and a
   coloured background on half of them would make the page unreadable.

   Drawn as a pseudo-element rather than as a border, because a row's border is
   1px and a 3px one would move every line of the row two pixels sideways the
   moment it was marked — forty rows on a page, each twitching as you work down
   it. The bar is inset from the 16px corners so it reads as a marker on the
   edge rather than as a second border trying to follow it. */
.row[data-word-state="done"],
.row[data-word-state="due"],
.row[data-word-state="scheduled"],
.row[data-word-state="learning"] { --state: var(--accent-line); }
.row[data-word-state="done"] { --state: var(--ok); }
.row[data-word-state="due"] { --state: var(--warn); }
.row[data-word-state]:not([data-word-state=""])::before {
  content: "";
  position: absolute;
  left: 0;
  top: var(--s-4);
  bottom: var(--s-4);
  width: 3px;
  border-radius: 0 3px 3px 0;
  background: var(--state, transparent);
}

/* ── The effort grid ──
   26 weeks, Monday at the top of each column, the current week last — so
   "now" is the bottom-right corner. Shared by /progress/ and the plan's day
   page, from one store and one renderer. */
.heat-grid {
  display: flex;
  gap: 3px;
  overflow-x: auto;
  padding-bottom: var(--s-2);
  -webkit-overflow-scrolling: touch;
}
.heat-col { display: grid; grid-template-rows: repeat(7, 1fr); gap: 3px; }
.heat-cell {
  width: 11px;
  height: 11px;
  border-radius: 3px;
  background: var(--bg-sunk);
  border: 1px solid transparent;
}
.heat-cell[data-lvl="1"] { background: color-mix(in srgb, var(--accent) 25%, var(--bg-sunk)); }
.heat-cell[data-lvl="2"] { background: color-mix(in srgb, var(--accent) 50%, var(--bg-sunk)); }
.heat-cell[data-lvl="3"] { background: color-mix(in srgb, var(--accent) 75%, var(--bg-sunk)); }
.heat-cell[data-lvl="4"] { background: var(--accent); }
.heat-cell.is-future { opacity: 0.35; }
.heat-stats { margin: var(--s-2) 0 0; font-size: var(--t-sm); color: var(--ink-3); }
.heat-stats b { color: var(--ink); font-weight: 700; }

/* ── Recall mode ──
   One switch blurs every meaning in a scope so a list becomes a drill. Each
   field opens on its own, so checking one word never spoils the next. */
.recall-toggle {
  display: inline-flex;
  align-items: center;
  gap: var(--s-1);
  padding: 5px var(--s-3);
  border: 1px solid var(--rule);
  border-radius: var(--r-pill);
  background: var(--bg);
  color: var(--ink-3);
  font: inherit;
  font-size: var(--t-xs);
  cursor: pointer;
}
.recall-toggle[aria-pressed="true"] { border-color: var(--accent); background: var(--accent-wash); color: var(--accent); }
.recall-label { font-weight: 600; }

.is-recall .recall-hide {
  filter: blur(5px);
  cursor: pointer;
  border-radius: var(--r-sm);
  transition: filter 0.12s ease;
  /* The blurred text must not be selectable, or a drag would read the answer
     out of it without revealing it. */
  user-select: none;
}
.is-recall .recall-hide:hover { filter: blur(3px); }
.is-recall .recall-hide.is-open { filter: none; user-select: auto; }
.is-recall .recall-hide:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }

@media (prefers-reduced-motion: reduce) {
  .is-recall .recall-hide { transition: none; }
}

/* ── The plan bar ──
   The join prompt other study pages carry. One slot: a join prompt before a
   plan exists and a resume link after, rather than two of which one is always
   wrong. */
.plan-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4);
  margin: var(--s-6) 0;
  padding: var(--s-4) var(--s-5);
  border: 1px solid var(--accent-line);
  border-radius: var(--r-lg);
  background: var(--accent-wash);
  color: var(--ink);
}
.plan-bar:hover { border-color: var(--accent); }
.plan-bar-text { display: flex; flex-direction: column; gap: 2px; font-size: var(--t-sm); }
.plan-bar-text b { font-size: var(--t-base); font-weight: 600; }
.plan-bar-text span { color: var(--ink-3); }
.plan-bar-go {
  flex: none;
  font-size: var(--t-sm);
  font-weight: 600;
  color: var(--accent);
  white-space: nowrap;
}
.plan-bar[data-plan-running] { border-color: var(--ok); background: var(--ok-bg); }
.plan-bar[data-plan-running] .plan-bar-go,
.plan-bar[data-plan-running] .plan-bar-text b { color: var(--ok); }

/* ── /progress/ — the reader's own dashboard ─────────────────────────────
   Every block here is painted by app.js from localStorage, so nothing in this
   file has a static counterpart. The four tiles at the top are the Toss
   convention — a big number with a small label under it — because the answer
   to "how am I doing" should be readable without reading.
   ───────────────────────────────────────────────────────────────────────── */

.pg-app { display: grid; gap: var(--s-6); }

.pg-block { }
.pg-block h2 {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--s-2);
  margin-bottom: var(--s-4);
  font-size: var(--t-lg);
  letter-spacing: var(--tr-head);
}
.pg-block h2 [lang="ko"] { color: var(--accent); }
.pg-count { margin-left: auto; font-size: var(--t-xs); font-weight: 500; color: var(--ink-4); }
.pg-note { font-size: var(--t-sm); color: var(--ink-3); max-width: 68ch; }
.pg-empty {
  color: var(--ink-3);
  font-size: var(--t-sm);
  padding: var(--s-5);
  background: var(--bg-sunk);
  border-radius: var(--r-lg);
}

/* ── The four numbers ── */
.pg-tiles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: var(--s-3);
}
.pg-tile {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: var(--s-4) var(--s-5);
  background: var(--bg-raise);
  border: 1px solid var(--rule);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-1);
}
.pg-tile b { font-size: var(--t-2xl); font-weight: 700; letter-spacing: var(--tr-display); line-height: 1.05; }
.pg-tile [lang="ko"] { font-size: var(--t-sm); font-weight: 600; color: var(--ink-2); }
.pg-tile span:last-child { font-size: var(--t-xs); color: var(--ink-4); }

/* ── Running plans ──
   The links here carry classes rather than being styled as `.pg-plan a`, because
   `01-base.css` underlines `a:not([class])`: on this site a class IS the
   declaration that a link is a component and not prose. */
.pg-plans { display: grid; gap: var(--s-2); }
.pg-plan-link {
  display: flex;
  align-items: baseline;
  gap: var(--s-3);
  padding: var(--s-3) var(--s-4);
  border: 1px solid var(--rule);
  border-radius: var(--r);
  background: var(--bg-raise);
  color: var(--ink);
}
.pg-plan-link:hover { border-color: var(--accent-line); background: var(--accent-wash); }
.pg-plan-lvl { font-weight: 700; color: var(--accent); }
.pg-plan-day { font-weight: 600; }
.pg-plan-meta { margin-left: auto; font-size: var(--t-xs); color: var(--ink-4); }

/* ── Coverage by level ── */
.pg-lvls { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: var(--s-2); }
.pg-lvl {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: var(--s-3) var(--s-4);
  border: 1px solid var(--rule);
  border-left: 3px solid var(--lvl, var(--accent));
  border-radius: var(--r);
  background: var(--bg-raise);
  color: var(--ink);
}
.pg-lvl:hover { border-color: var(--lvl, var(--accent)); }
.pg-lvl-code { font-size: var(--t-base); font-weight: 700; color: var(--lvl, var(--ink)); }
.pg-lvl-n { font-size: var(--t-xs); color: var(--ink-3); }
.pg-lvl-n b { color: var(--ink); font-weight: 600; }

/* ── History ── */
.pg-list { display: grid; gap: 2px; }
.pg-row-link {
  display: flex;
  align-items: baseline;
  gap: var(--s-3);
  padding: var(--s-2) var(--s-3);
  border-radius: var(--r-sm);
  color: var(--ink-2);
  font-size: var(--t-sm);
}
.pg-row-link:hover { background: var(--bg-sunk); color: var(--ink); }
.pg-row-main { display: flex; align-items: baseline; gap: var(--s-2); min-width: 0; }
.pg-row-title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pg-row-when { margin-left: auto; flex: none; font-size: var(--t-xs); color: var(--ink-4); }
.pg-chip {
  flex: none;
  padding: 1px var(--s-2);
  border-radius: var(--r-pill);
  background: var(--bg-sunk);
  color: var(--ink-4);
  font-size: var(--t-xs);
}
.pg-chip-lvl { background: color-mix(in srgb, var(--lvl) 12%, transparent); color: var(--lvl); font-weight: 600; }

/* ── Backup ── */
.pg-actions { display: flex; flex-wrap: wrap; gap: var(--s-2); margin-top: var(--s-4); }
.pg-file { cursor: pointer; }
.pg-danger { color: var(--bad); }
.pg-danger:hover { background: var(--bad-bg); }
.pg-where { padding-top: var(--s-4); border-top: 1px solid var(--rule); }

/* The sync line. Live sync reads as ordinary body copy — it is the state a
   reader should stop noticing — and a sync failure takes the accent, not
   --bad: the progress itself is safe in localStorage either way, so a red
   alarm would overstate what went wrong. */
.pg-ok { color: var(--ink-2); }
.pg-warn { color: var(--accent); }

/* ── The flashcard deck ──────────────────────────────────────────────────
   One card at a time, on a stage that does not resize between prompt and
   answer: a card whose height jumps when it flips tells the reader how much
   they are about to be shown before they have recalled anything.

   The front is Hangul at display size and nothing else. Everything that would
   be a hint — the hanja, the romanization, the 품사 — is on the back, because
   recognising the written word is the thing the deck is for.
   ───────────────────────────────────────────────────────────────────────── */

.fc-app {
  max-width: 640px;
  margin: 0 auto var(--s-7);
  display: grid;
  gap: var(--s-4);
}

.fc-top { display: flex; align-items: flex-end; justify-content: space-between; gap: var(--s-4); }
.fc-deck { display: flex; flex-direction: column; gap: 2px; }
.fc-deck-name { font-size: var(--t-lg); font-weight: 700; letter-spacing: var(--tr-head); color: var(--lvl, var(--ink)); }
.fc-deck-sub { font-size: var(--t-xs); color: var(--ink-4); }
.fc-tools { display: flex; align-items: center; gap: var(--s-2); }
.fc-tool,
.fc-limit {
  appearance: none;
  padding: 5px var(--s-2);
  border: 1px solid var(--rule);
  border-radius: var(--r-pill);
  background: var(--bg);
  color: var(--ink-3);
  font: inherit;
  font-size: var(--t-xs);
  cursor: pointer;
}
.fc-tool:hover, .fc-limit:hover { border-color: var(--accent-line); color: var(--accent); }
.fc-limit-label { display: inline-flex; align-items: center; gap: var(--s-1); font-size: var(--t-xs); color: var(--ink-4); }

/* ── The stage ──
   min-height rather than a fixed height: a long gloss or a long example must
   not be clipped, but the common case must not bounce either. */
.fc-stage {
  position: relative;
  min-height: 260px;
  display: grid;
}

.fc-card {
  position: relative;
  display: grid;
  place-items: center;
  padding: var(--s-6) var(--s-5);
  background: var(--bg-raise);
  border: 1px solid var(--rule);
  border-top: 3px solid var(--lvl, var(--accent));
  border-radius: var(--r-xl);
  box-shadow: var(--shadow-2);
  cursor: pointer;
  text-align: center;
}
.fc-card:focus-visible { outline: 2px solid var(--focus); outline-offset: 3px; }

/* No 3D flip. A rotated Hangul block renders through the browser's transform
   rasteriser and comes back visibly softer than the type around it, which on a
   face this large is the first thing you notice. The face swap is the same
   information in the same place, and it stays sharp. */
.fc-face { display: grid; gap: var(--s-3); place-items: center; width: 100%; }
.fc-front { }
.fc-back { display: none; }
.fc-card.is-flipped .fc-front { display: none; }
.fc-card.is-flipped .fc-back { display: grid; }

.fc-chip {
  padding: 2px var(--s-3);
  border-radius: var(--r-pill);
  background: var(--bg-sunk);
  color: var(--ink-4);
  font-size: var(--t-xs);
  font-weight: 500;
}
.fc-chip[data-q="new"] { background: var(--accent-wash); color: var(--accent); }
.fc-chip[data-q="learn"] { background: var(--warn-bg); color: var(--warn); }
.fc-chip[data-q="review"] { background: var(--ok-bg); color: var(--ok); }

.fc-word {
  font-size: var(--t-3xl);
  font-weight: 700;
  letter-spacing: var(--tr-display);
  line-height: 1.2;
}

.fc-back-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: var(--s-2); }
.fc-back-word { font-size: var(--t-xl); font-weight: 700; letter-spacing: var(--tr-head); }
.fc-back-hanja { font-size: var(--t-lg); color: var(--warn); }
.fc-audio {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  border: 1px solid var(--rule);
  border-radius: 50%;
  background: var(--bg);
  color: var(--ink-3);
  cursor: pointer;
}
.fc-audio:hover { border-color: var(--accent-line); color: var(--accent); }
.fc-audio.is-playing { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }

.fc-back-meta { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--s-2); font-size: var(--t-xs); color: var(--ink-4); }
/* Same gate as every other romanization on the site — see 10-vocab-rows.css. */
.fc-back-roman { display: none; }
:root[data-roman="on"] .fc-back-roman { display: inline; }

.fc-back-mean { font-size: var(--t-md); color: var(--ink); max-width: 42ch; }

.fc-ex {
  width: 100%;
  padding-top: var(--s-3);
  border-top: 1px solid var(--rule);
  display: grid;
  gap: var(--s-1);
}
.fc-ex-ko { font-size: var(--t-md); line-height: var(--lh-body); }
.fc-ex-en { font-size: var(--t-sm); color: var(--ink-3); }

/* Four grades and a link out, and nothing else: in a deck the grade IS the
   decision, so the shared word marks deliberately do not appear here. */
.fc-back-foot { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: var(--s-3); }
.fc-link { font-size: var(--t-sm); color: var(--accent); }
.fc-link [lang="ko"] { font-weight: 600; }

/* ── The finish card ── */
.fc-finish {
  display: grid;
  place-items: center;
  gap: var(--s-3);
  padding: var(--s-7) var(--s-5);
  background: var(--bg-sunk);
  border-radius: var(--r-xl);
  text-align: center;
}
.fc-finish-title { display: grid; gap: 2px; font-size: var(--t-lg); font-weight: 700; letter-spacing: var(--tr-head); }
.fc-finish-title [lang="ko"] { font-size: var(--t-sm); font-weight: 600; color: var(--accent); }
.fc-finish-note { font-size: var(--t-sm); color: var(--ink-3); max-width: 46ch; }
.fc-finish-stats { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--s-3); font-size: var(--t-xs); color: var(--ink-4); }
.fc-more {
  padding: var(--s-2) var(--s-5);
  border: 1px solid var(--accent);
  border-radius: var(--r-pill);
  background: var(--accent);
  color: var(--accent-ink);
  font: inherit;
  font-size: var(--t-sm);
  font-weight: 600;
  cursor: pointer;
}
.fc-more:hover { filter: brightness(1.08); }

/* ── Answering ── */
.fc-answer { display: grid; gap: var(--s-3); justify-items: center; }
.fc-counts { display: flex; align-items: center; gap: var(--s-2); font-size: var(--t-xs); color: var(--ink-4); }
.fc-count b { font-weight: 700; }
.fc-count-new b { color: var(--accent); }
.fc-count-learn b { color: var(--warn); }
.fc-count-due b { color: var(--ok); }
/* Which queue the card in play came from, underlined in the counts row. */
.fc-counts[data-fc-active="new"] .fc-count-new,
.fc-counts[data-fc-active="learn"] .fc-count-learn,
.fc-counts[data-fc-active="review"] .fc-count-due { color: var(--ink); font-weight: 600; }

.fc-show {
  width: 100%;
  max-width: 360px;
  padding: var(--s-3) var(--s-5);
  border: 0;
  border-radius: var(--r-pill);
  background: var(--ink);
  color: var(--bg);
  font: inherit;
  font-size: var(--t-base);
  font-weight: 600;
  cursor: pointer;
}
.fc-show:hover { background: var(--accent); }

.fc-grades { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--s-2); width: 100%; }
.fc-grade {
  display: grid;
  gap: 2px;
  padding: var(--s-2) var(--s-1);
  border: 1px solid var(--rule);
  border-radius: var(--r);
  background: var(--bg-raise);
  color: var(--ink-2);
  font: inherit;
  cursor: pointer;
  text-align: center;
}
.fc-grade:hover { border-color: currentColor; }
.fc-grade-ivl { font-size: var(--t-xs); color: var(--ink-4); font-variant-numeric: tabular-nums; }
.fc-grade-name { font-size: var(--t-sm); font-weight: 600; }
.fc-grade-key { font-size: var(--t-xs); color: var(--ink-4); font-family: var(--mono); }
.fc-grade-again { color: var(--bad); }
.fc-grade-hard { color: var(--warn); }
.fc-grade-good { color: var(--ok); }
.fc-grade-easy { color: var(--accent); }

.fc-plannote { font-size: var(--t-xs); color: var(--ink-4); text-align: center; }

/* ── The daily plan ──────────────────────────────────────────────────────
   Two halves in one stylesheet, because they are two states of one page.
   `.sp-table` and `.sp-steps` are static: they render for everyone and for a
   crawler. Everything with an `sp-day`, `sp-pace` or `sp-vtable` in its name is
   painted into #plan-app by app.js and exists only for the reader it belongs
   to.

   The day card is deliberately the densest thing on the site. It is the one
   surface a reader opens every day, and the Korean convention on a screen like
   that (Toss, 클래스카드) is more information per screen rather than more
   whitespace.
   ───────────────────────────────────────────────────────────────────────── */

.sp-app { display: grid; gap: var(--s-4); margin-bottom: var(--s-7); }

.sp-card {
  padding: var(--s-5);
  background: var(--bg-raise);
  border: 1px solid var(--rule);
  border-top: 3px solid var(--lvl, var(--accent));
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-1);
}
.sp-title { display: grid; gap: 2px; margin-bottom: var(--s-4); font-size: var(--t-xl); letter-spacing: var(--tr-head); }
.sp-title-sub { font-size: var(--t-sm); font-weight: 600; color: var(--lvl, var(--accent)); letter-spacing: 0; }
.sp-note { font-size: var(--t-sm); color: var(--ink-3); }

/* ── Joining: three finish times ── */
.sp-paces { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: var(--s-3); }
.sp-pace {
  position: relative;
  display: grid;
  gap: 2px;
  padding: var(--s-4);
  border: 1px solid var(--rule);
  border-radius: var(--r);
  background: var(--bg);
  color: var(--ink);
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.sp-pace:hover { border-color: var(--accent); }
.sp-pace.is-rec { border-color: var(--accent); background: var(--accent-wash); }
.sp-pace-tag {
  position: absolute;
  top: calc(var(--s-3) * -1);
  left: var(--s-3);
  padding: 2px var(--s-2);
  border-radius: var(--r-pill);
  background: var(--accent);
  color: var(--accent-ink);
  font-size: var(--t-xs);
  font-weight: 600;
}
.sp-pace-name { font-size: var(--t-sm); font-weight: 600; color: var(--ink-2); }
.sp-pace-days { font-size: var(--t-sm); color: var(--ink-3); }
.sp-pace-days b { font-size: var(--t-xl); font-weight: 700; letter-spacing: var(--tr-head); color: var(--ink); margin-right: 3px; }
.sp-pace-goal { font-size: var(--t-xs); color: var(--ink-4); }
.sp-pace-cta { margin-top: var(--s-2); font-size: var(--t-sm); font-weight: 600; color: var(--accent); }
.sp-join-fine { margin-top: var(--s-4); }

/* ── The level summary card ── */
.sp-tiles { display: flex; flex-wrap: wrap; gap: var(--s-3); margin-bottom: var(--s-4); }
.sp-tile {
  display: grid;
  gap: 0;
  padding: var(--s-3) var(--s-4);
  border-radius: var(--r);
  background: var(--bg-sunk);
  font-size: var(--t-xs);
  color: var(--ink-4);
  min-width: 92px;
}
.sp-tile b { font-size: var(--t-xl); font-weight: 700; letter-spacing: var(--tr-head); color: var(--ink); line-height: 1.15; }
.sp-tile span { font-size: var(--t-xs); font-weight: 600; color: var(--ink-2); }
.sp-go-today {
  display: inline-block;
  padding: var(--s-3) var(--s-5);
  border-radius: var(--r-pill);
  background: var(--ink);
  color: var(--bg);
  font-size: var(--t-sm);
  font-weight: 600;
}
.sp-go-today:hover { background: var(--accent); }
.sp-otherplans { margin-top: var(--s-4); font-size: var(--t-xs); color: var(--ink-4); display: flex; flex-wrap: wrap; gap: var(--s-2); }
.sp-otherplans a { color: var(--accent); }

/* ── The day strip ──
   Nothing in here is conditional. A control that appeared on only some days
   would change the header's height as a reader steps through them. */
.sp-daynav { display: flex; align-items: center; justify-content: space-between; gap: var(--s-2); }
.sp-nav-btn {
  display: inline-flex;
  align-items: center;
  gap: var(--s-1);
  padding: var(--s-2) var(--s-3);
  border: 1px solid var(--rule);
  border-radius: var(--r-pill);
  background: var(--bg);
  color: var(--ink-3);
  font: inherit;
  font-size: var(--t-xs);
  cursor: pointer;
}
.sp-nav-btn:hover:not([disabled]) { border-color: var(--accent-line); color: var(--accent); }
.sp-nav-btn[disabled] { opacity: 0.35; cursor: default; }
.sp-day-mid { display: grid; justify-items: center; gap: 2px; }
.sp-day-pick { position: relative; display: inline-flex; align-items: center; gap: var(--s-1); }
.sp-day-jump {
  appearance: none;
  padding: 2px var(--s-5) 2px var(--s-2);
  border: 0;
  background: transparent;
  color: var(--ink);
  font: inherit;
  font-size: var(--t-lg);
  font-weight: 700;
  letter-spacing: var(--tr-head);
  cursor: pointer;
}
.sp-day-pick svg { position: absolute; right: var(--s-1); pointer-events: none; color: var(--ink-4); }
.sp-day-date { font-size: var(--t-xs); color: var(--ink-4); }

.sp-day-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-2);
  margin: var(--s-4) 0;
  padding-bottom: var(--s-3);
  border-bottom: 1px solid var(--rule);
}
.sp-day-prog { font-size: var(--t-sm); color: var(--ink-3); }
.sp-day-prog b { color: var(--ink); font-weight: 700; }
.sp-day-tools { display: flex; align-items: center; gap: var(--s-2); }
.sp-back-today {
  padding: 5px var(--s-3);
  border: 1px solid var(--rule);
  border-radius: var(--r-pill);
  background: var(--bg);
  color: var(--ink-3);
  font: inherit;
  font-size: var(--t-xs);
  cursor: pointer;
}

/* ── Blocks inside the day card ── */
.sp-block { margin-bottom: var(--s-5); }
.sp-block:last-child { margin-bottom: 0; }
.sp-block-head { margin-bottom: var(--s-3); }
.sp-block h3 { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--s-2); font-size: var(--t-base); }
.sp-block h3 { font-weight: 700; }
.sp-sect-meta { margin-left: auto; font-size: var(--t-xs); font-weight: 400; color: var(--ink-4); }

/* ── The day's word table ── */
.sp-vtable { width: 100%; border-collapse: collapse; font-size: var(--t-sm); }
.sp-vtable th, .sp-vtable td { padding: var(--s-2) var(--s-2); text-align: left; vertical-align: middle; }
.sp-vtable thead th {
  font-size: var(--t-xs);
  font-weight: 500;
  letter-spacing: var(--tr-label);
  text-transform: uppercase;
  color: var(--ink-4);
  border-bottom: 1px solid var(--rule);
}
.sp-vtable tbody tr { border-bottom: 1px solid var(--rule); }
.sp-vtable tbody tr:last-child { border-bottom: 0; }
.sp-vtable tr[data-word-state="done"] .sp-vt-ko { color: var(--ink-4); text-decoration: line-through; text-decoration-thickness: 1px; }
.sp-vtable tr[data-word-state="due"] { background: color-mix(in srgb, var(--warn) 6%, transparent); }

.sp-vt-word { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--s-2); }
.sp-vt-ko { font-size: var(--t-md); font-weight: 600; color: var(--ink); }
.sp-vt-ko:hover { color: var(--accent); }
.sp-vt-hanja { font-size: var(--t-xs); color: var(--warn); }
.sp-vt-roman { display: none; font-size: var(--t-xs); color: var(--ink-4); }
:root[data-roman="on"] .sp-vt-roman { display: inline; }
.sp-vt-pos { font-size: var(--t-xs); color: var(--ink-4); }
.sp-vt-audio {
  display: inline-grid;
  place-items: center;
  width: 24px;
  height: 24px;
  border: 1px solid var(--rule);
  border-radius: 50%;
  background: var(--bg);
  color: var(--ink-4);
  cursor: pointer;
}
.sp-vt-audio:hover { border-color: var(--accent-line); color: var(--accent); }
.sp-vt-audio.is-playing { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
.sp-vt-mean { color: var(--ink-2); }
.sp-ctl-col { width: 1%; white-space: nowrap; }
.sp-vt-act { display: flex; gap: var(--s-1); align-items: center; }
.sp-vt-go { display: inline-grid; place-items: center; width: 28px; height: 28px; border-radius: 50%; color: var(--ink-4); }
.sp-vt-go:hover { background: var(--bg-sunk); color: var(--accent); }

/* ── Finishing the day ── */
.sp-day-foot { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-3); padding-top: var(--s-4); border-top: 1px solid var(--rule); }
.sp-finish {
  padding: var(--s-3) var(--s-5);
  border: 1px solid var(--ink);
  border-radius: var(--r-pill);
  background: var(--bg);
  color: var(--ink);
  font: inherit;
  font-size: var(--t-sm);
  font-weight: 600;
  cursor: pointer;
}
.sp-finish:hover { background: var(--ink); color: var(--bg); }
/* Everything marked: the button stops being a way out and becomes the answer. */
.sp-finish.is-ready { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
.sp-finish.is-ready:hover { filter: brightness(1.08); }

/* ── What is coming back ── */
.sp-up-list { display: grid; gap: 2px; }
.sp-up-row { display: flex; align-items: baseline; gap: var(--s-3); font-size: var(--t-sm); color: var(--ink-3); }
.sp-up-when { min-width: 8ch; color: var(--ink-2); }
.sp-up-date { font-size: var(--t-xs); color: var(--ink-4); }
.sp-up-n { margin-left: auto; font-variant-numeric: tabular-nums; }

.sp-heat { padding-top: var(--s-4); }
.sp-block.sp-heat h3, .sp-upcoming h3 { margin-bottom: var(--s-3); }

/* ── Plan settings ── */
.sp-editor {
  padding: var(--s-4);
  border: 1px solid var(--rule);
  border-radius: var(--r-lg);
  background: var(--bg-sunk);
  font-size: var(--t-sm);
}
.sp-editor summary { cursor: pointer; font-weight: 600; }
.sp-editor-sum { margin: var(--s-2) 0 var(--s-3); }
.sp-goal-field { display: inline-grid; gap: var(--s-1); font-size: var(--t-xs); color: var(--ink-4); }
.sp-goal-field input {
  width: 110px;
  padding: var(--s-2);
  border: 1px solid var(--rule);
  border-radius: var(--r-sm);
  background: var(--bg);
  color: var(--ink);
  font: inherit;
}
.sp-editor-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2); margin-top: var(--s-3); }
.sp-preset, .sp-save {
  padding: 5px var(--s-3);
  border: 1px solid var(--rule);
  border-radius: var(--r-pill);
  background: var(--bg);
  color: var(--ink-2);
  font: inherit;
  font-size: var(--t-xs);
  cursor: pointer;
}
.sp-save { border-color: var(--accent); background: var(--accent); color: var(--accent-ink); font-weight: 600; }
.sp-finish-note { font-size: var(--t-xs); color: var(--ink-4); }
.sp-danger { margin-top: var(--s-4); padding-top: var(--s-3); border-top: 1px solid var(--rule); display: grid; gap: var(--s-2); justify-items: start; }
.sp-reset-btn {
  padding: 5px var(--s-3);
  border: 1px solid var(--bad);
  border-radius: var(--r-pill);
  background: var(--bg);
  color: var(--bad);
  font: inherit;
  font-size: var(--t-xs);
  cursor: pointer;
}
.sp-reset-btn:hover { background: var(--bad-bg); }
.sp-leave { margin-top: var(--s-3); }
.sp-leave button { background: none; border: 0; padding: 0; color: var(--ink-4); font: inherit; font-size: var(--t-xs); text-decoration: underline; cursor: pointer; }

/* ── The static half ── */
.sp-steps {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: var(--s-3);
  margin: var(--s-6) 0;
  counter-reset: sp-step;
}
.sp-step {
  display: grid;
  gap: var(--s-1);
  padding: var(--s-4);
  border: 1px solid var(--rule);
  border-radius: var(--r-lg);
  background: var(--bg-raise);
}
.sp-step-icon { color: var(--accent); }
.sp-step-ko { font-size: var(--t-sm); font-weight: 600; color: var(--ink-2); }
.sp-step strong { font-size: var(--t-xs); font-weight: 500; color: var(--ink-4); }

.sp-table { width: 100%; border-collapse: collapse; font-size: var(--t-sm); }
.sp-table th, .sp-table td { padding: var(--s-2) var(--s-3); text-align: left; border-bottom: 1px solid var(--rule); }
.sp-table thead th {
  font-size: var(--t-xs);
  font-weight: 500;
  letter-spacing: var(--tr-label);
  text-transform: uppercase;
  color: var(--ink-4);
}
.sp-table tbody th { font-weight: 600; white-space: nowrap; }
.sp-table kbd {
  padding: 1px 5px;
  border: 1px solid var(--rule);
  border-radius: var(--r-sm);
  background: var(--bg-sunk);
  font-family: var(--mono);
  font-size: var(--t-xs);
}

/* A one-line prose pointer between sections — "would rather do this instead?" */
.onward { margin: var(--s-4) 0; font-size: var(--t-sm); color: var(--ink-3); max-width: 68ch; }
.onward a { color: var(--accent); }

/* ── The expandable word row ─────────────────────────────────────────────
   This component replaces the per-word page the German sibling has, so it is
   the single most important surface on the site. Everything it needs to do:

   - Collapsed, it must read as a complete dictionary entry — headword, hanja,
     level, gloss, one example — because that is the state a crawler weighs and
     the state a skimming reader spends most of their time in.
   - Expanded, it must not reflow the rows above it. The <details> grows
     downward and the anchor scroll position stays valid.
   - The controls (audio, save) sit OUTSIDE the <summary> and are overlaid on
     it here. A <button> inside a <summary> inherits the summary's activation
     behaviour, so a click on "play" would also toggle the row.

   The learner's eye must land on the Hangul: the example sentence is 18px and
   full-ink, its English gloss is 14px and muted. That ratio is the whole
   typographic argument of the component.
   ───────────────────────────────────────────────────────────────────────── */

.rows {
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
}

.row {
  position: relative;
  background: var(--bg-raise);
  border: 1px solid var(--rule);
  border-radius: var(--r-lg);
  transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
.row:hover { border-color: var(--accent-line); }
.row:has(.row-d[open]) { box-shadow: var(--shadow-1); border-color: var(--accent-line); }
/* The row a fragment link just jumped to. Set by app.js so a search result
   lands somewhere visibly, rather than at an anchor that looks like any other. */
.row.is-target { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-wash); }

.row-head {
  display: block;
  padding: var(--s-4) 88px var(--s-4) var(--s-4);
  cursor: pointer;
  list-style: none;
}
.row-head::-webkit-details-marker { display: none; }

/* The disclosure caret, drawn on the summary rather than shipped as an icon in
   ~15,000 rows. */
.row-head::after {
  content: "";
  position: absolute;
  left: calc(100% - 30px);
  top: 26px;
  width: 7px;
  height: 7px;
  border-right: 1.6px solid var(--ink-4);
  border-bottom: 1.6px solid var(--ink-4);
  transform: rotate(45deg);
  transition: transform 0.15s ease;
}
.row-d[open] .row-head::after { transform: rotate(-135deg); }

.row-line {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--s-2);
  margin-bottom: var(--s-1);
}
.row-word {
  font-size: var(--t-lg);
  font-weight: 700;
  letter-spacing: var(--tr-head);
  line-height: 1.3;
}
.row-hanja { font-size: var(--t-base); color: var(--ink-4); font-weight: 400; }

/* ── Romanization ──
   Hidden by default and revealed by the toggle in the header, which sets
   data-roman="on" on <html> before first paint. Off by default on purpose:
   Hangul is learnable in an afternoon, and every hour spent reading "gagyeok"
   is an hour not spent reading 가격. */
.row-roman { display: none; font-size: var(--t-sm); color: var(--ink-4); }
:root[data-roman="on"] .row-roman { display: inline; }

.row-gloss {
  margin: 0 0 var(--s-2);
  font-size: var(--t-base);
  font-weight: 500;
  color: var(--ink);
}

/* ── Example sentences ──
   Two sizes of text, always. The Korean is 18px and full-ink; the English is
   14px and muted. */
.ex { margin-top: var(--s-2); }
.ex-ko {
  margin: 0;
  font-size: var(--t-md);
  line-height: 1.7;
  color: var(--ink);
}
.ex-en {
  margin: 2px 0 0;
  font-size: var(--t-sm);
  line-height: 1.5;
  color: var(--ink-3);
}

/* ── Controls, overlaid on the summary ── */
.row-controls {
  position: absolute;
  top: var(--s-3);
  right: 42px;
  display: flex;
  gap: var(--s-1);
}
.row-btn {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  border: 1px solid var(--rule);
  border-radius: 50%;
  background: var(--bg);
  color: var(--ink-3);
  font: inherit;
  font-size: var(--t-base);
  line-height: 1;
  cursor: pointer;
  transition: background 0.12s ease, color 0.12s ease, border-color 0.12s ease;
}
.row-btn:hover { background: var(--accent-wash); border-color: var(--accent-line); color: var(--accent); }
.audio-btn.is-playing { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
/* The mark that sits beside it is the shared "알아요" toggle, painted by
   app.js from the same store the plan and the deck write to — see
   05-study.css. It is not styled here, because it is not a row control: it is
   the same control the day table and the card back show. */

/* ── The expanded body ── */
.row-body {
  padding: 0 var(--s-4) var(--s-4);
  display: grid;
  gap: var(--s-4);
  border-top: 1px solid var(--rule);
  margin-top: var(--s-1);
  padding-top: var(--s-4);
}
.row-body h4 {
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: var(--tr-label);
  text-transform: uppercase;
  color: var(--ink-4);
  margin-bottom: var(--s-2);
}
.row-senses ol { display: flex; flex-wrap: wrap; gap: var(--s-2); }
.row-senses li {
  padding: 2px var(--s-3);
  background: var(--bg-sunk);
  border-radius: var(--r-pill);
  font-size: var(--t-sm);
  color: var(--ink-2);
}
.row-pron p { margin: 0; display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--s-2); }
.pron { font-size: var(--t-md); font-weight: 600; color: var(--accent); }
.pron-note { font-size: var(--t-sm); color: var(--ink-4); }

.rels { display: grid; gap: var(--s-2); margin: 0; }
.rel { display: grid; grid-template-columns: 116px 1fr; gap: var(--s-3); align-items: baseline; }
.rel dt { display: flex; flex-direction: column; font-size: var(--t-sm); font-weight: 500; }
.rel-en { font-size: var(--t-xs); color: var(--ink-4); }
.rel dd { margin: 0; display: flex; flex-wrap: wrap; gap: var(--s-2); }
.rel dd a, .rel dd span {
  padding: 2px var(--s-2);
  border-radius: var(--r-sm);
  background: var(--bg-sunk);
  font-size: var(--t-sm);
}
.rel dd a:hover { background: var(--accent-wash); color: var(--accent); }

.idiom-links { display: grid; gap: var(--s-1); }
.idiom-links a { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--s-2); font-size: var(--t-sm); }
.idiom-links a:hover .ko { color: var(--accent); }
.idiom-links .ko { font-weight: 600; }
.idiom-gloss { color: var(--ink-3); font-size: var(--t-xs); }

.row-links { display: flex; flex-wrap: wrap; gap: var(--s-2); }
.row-link {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-2) var(--s-3);
  border: 1px solid var(--rule);
  border-radius: var(--r-pill);
  font-size: var(--t-sm);
  color: var(--ink-2);
}
.row-link:hover { border-color: var(--accent-line); background: var(--accent-wash); color: var(--accent); }
.row-link [lang] { font-weight: 600; color: var(--ink); }
.row-link:hover [lang] { color: var(--accent); }

/* ── Filter bar above a list ── */
.filter-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-3);
  margin-bottom: var(--s-4);
  background: var(--bg-sunk);
  border-radius: var(--r-lg);
}
.filter-bar .chip { background: var(--bg); border-color: var(--rule); }
.expand-all {
  margin-left: auto;
  border: 1px solid var(--rule);
  border-radius: var(--r-pill);
  background: var(--bg);
  color: var(--ink-2);
  font: inherit;
  font-size: var(--t-sm);
  padding: var(--s-1) var(--s-3);
  cursor: pointer;
}
.expand-all:hover { border-color: var(--accent-line); color: var(--accent); }

/* ── The word index ──────────────────────────────────────────────────────
   The forty words in front of you, as a rail beside the rows. Sticky and
   scrolled on its own, so it stays put while the rows move past it; the row
   being read is marked by app.js. Below 1080px the split collapses and this
   folds into a dense strip of chips above the list — see 31-responsive.css.

   Index left, rows right: it is the list the reader is working down, not a
   note in the margin, so it reads first.
   ───────────────────────────────────────────────────────────────────────── */
.split-index { grid-template-columns: 264px minmax(0, 1fr); gap: var(--s-6); }

.word-index {
  position: sticky;
  top: calc(var(--header-h) + var(--s-4));
  display: flex;
  flex-direction: column;
  max-height: calc(100vh - var(--header-h) - var(--s-6));
}
.wi-head { flex: none; padding-bottom: var(--s-2); border-bottom: 1px solid var(--rule); }
.wi-head h2 {
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: var(--tr-label);
  text-transform: uppercase;
  color: var(--ink-3);
}
/* How far into the whole list this page reaches. */
.wi-bar {
  display: block;
  height: 4px;
  margin-top: var(--s-2);
  background: var(--bg-sunk);
  border-radius: var(--r-pill);
  overflow: hidden;
}
.wi-bar i { display: block; height: 100%; width: var(--pos, 0%); background: var(--lvl); border-radius: inherit; }

.wi-list {
  flex: 1 1 auto;
  margin-top: var(--s-1);
  padding-right: var(--s-2);
  overflow-y: auto;
  overscroll-behavior: contain;
  /* The platform scrollbar is a bright slab against this palette. Firefox
     takes the standard property, WebKit the pseudo-elements. */
  scrollbar-width: thin;
  scrollbar-color: var(--rule) transparent;
}
.wi-list::-webkit-scrollbar { width: 4px; }
.wi-list::-webkit-scrollbar-track { background: transparent; }
.wi-list::-webkit-scrollbar-thumb { background: var(--rule); border-radius: var(--r-pill); }
.wi-list:hover::-webkit-scrollbar-thumb { background: var(--ink-4); }

.wi-link {
  display: flex;
  align-items: baseline;
  gap: var(--s-2);
  padding: 6px var(--s-2);
  font-size: var(--t-sm);
  color: var(--ink-2);
  border-left: 2px solid transparent;
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
}
.wi-link:hover { background: var(--bg-sunk); color: var(--ink); }
.wi-n {
  flex: none;
  min-width: 2.6ch;
  font-family: var(--mono);
  font-size: var(--t-xs);
  font-variant-numeric: tabular-nums;
  color: var(--ink-4);
}
.wi-w { flex: none; max-width: 60%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The gloss takes what the word leaves and is clipped, never wrapped: it is
   here to tell two 있다 apart, not to be read. */
.wi-g {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  text-align: right;
  font-size: var(--t-xs);
  color: var(--ink-4);
}
.wi-link:hover .wi-g, .wi-link.is-current .wi-g { color: var(--ink-3); }
/* Where the reader is, mirrored from the row list. */
.wi-link.is-current {
  background: var(--bg-sunk);
  border-left-color: var(--lvl);
  color: var(--ink);
  font-weight: 600;
}
.wi-link.is-current .wi-n { color: var(--lvl); }

/* ── Idioms, proverbs and set phrases ────────────────────────────────────
   The same expandable row as the vocabulary lists, because these entries get
   no pages of their own for the same reason words do not. What differs is what
   the collapsed state has to show: an idiom is only interesting in the gap
   between what it says and what it means, so both are visible before opening.
   ───────────────────────────────────────────────────────────────────────── */

.idiom-row .idiom-form { font-size: var(--t-lg); font-weight: 600; letter-spacing: var(--tr-head); }

/* The literal reading. Set quieter than the meaning and tagged, so it can never
   be mistaken for the translation — half the point is that it is not one. */
.idiom-literal {
  margin: var(--s-2) 0 0;
  font-size: var(--t-sm);
  color: var(--ink-3);
  font-style: normal;
}
.idiom-tag {
  display: inline-block;
  margin-right: var(--s-2);
  padding: 1px var(--s-2);
  border-radius: var(--r-pill);
  background: var(--bg-sunk);
  font-size: var(--t-xs);
  text-transform: uppercase;
  letter-spacing: var(--tr-label);
  color: var(--ink-4);
}
.chip-kind { background: var(--accent-wash); color: var(--accent); border-color: transparent; }

.idiom-def p, .idiom-pattern p, .idiom-note p { margin: 0; color: var(--ink-2); font-size: var(--t-sm); }
.idiom-pattern p { font-family: var(--mono); }

/* Examples are Korean-only — the dictionary ships no translation for any of
   them. The label says so rather than letting the reader assume one was
   withheld, and the block is set apart so it never reads as a gloss. */
.idiom-examples .ex-ko { margin: 0 0 var(--s-2); font-size: var(--t-md); line-height: var(--lh-body); }
.idiom-untranslated {
  display: block;
  margin-top: 2px;
  font-size: var(--t-xs);
  font-weight: 400;
  text-transform: none;
  letter-spacing: normal;
  color: var(--ink-4);
}

.idiom-word-links { list-style: none; display: flex; flex-wrap: wrap; gap: var(--s-2); padding: 0; margin: 0; }
.idiom-word-links a {
  display: inline-flex;
  align-items: baseline;
  gap: var(--s-2);
  padding: var(--s-1) var(--s-3);
  background: var(--bg-sunk);
  border-radius: var(--r-pill);
  font-size: var(--t-sm);
}
.idiom-word-links a:hover { background: var(--accent-wash); color: var(--accent); }
.idiom-word-links .idiom-gloss { font-size: var(--t-xs); color: var(--ink-4); }

/* ── /hangul/ — the alphabet section ──────────────────────────────────────
   Eight authored pages that share one shell. The dominant element is the
   letter itself, at a size you could trace, with its stroke diagram beside it
   — so the type scale here is larger than anywhere else on the site and the
   surfaces are quieter, because on these pages the letters are the design.
   ───────────────────────────────────────────────────────────────────────── */

/* The section's own sub-nav, under the h1 on every page. A scrolling strip on
   mobile rather than a wrapping block: eight items wrapping to three lines
   pushes the content of a landing page below the fold. */
.jnav {
  display: flex;
  gap: var(--s-1);
  margin-bottom: var(--s-6);
  padding-bottom: var(--s-2);
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.jnav::-webkit-scrollbar { display: none; }
.jnav-item {
  flex: none;
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: var(--s-2) var(--s-4);
  border: 1px solid var(--rule);
  border-radius: var(--r-pill);
  background: var(--bg);
  white-space: nowrap;
}
.jnav-item:hover { border-color: var(--accent-line); background: var(--accent-wash); }
/* English leads: this strip is navigation, and a first-time reader has to be
   able to pick a destination from it. The Korean name sits under it. */
.jnav-en { font-size: var(--t-sm); font-weight: 600; letter-spacing: var(--tr-head); }
.jnav-ko { order: 1; font-size: var(--t-xs); color: var(--ink-3); }
.jnav-item.is-current { background: var(--ink); border-color: var(--ink); }
.jnav-item.is-current .jnav-en { color: var(--bg); }
.jnav-item.is-current .jnav-ko { color: var(--bg); opacity: 0.72; }

.jsection { margin-top: var(--s-8); }
.jsub {
  margin: var(--s-6) 0 var(--s-3);
  font-size: var(--t-base);
  font-weight: 600;
  color: var(--ink-3);
  display: flex;
  align-items: baseline;
  gap: var(--s-2);
}
.jsub [lang="ko"] { color: var(--ink); font-weight: 700; }
.jmuted { color: var(--ink-4); font-weight: 400; }
.jstrong { font-weight: 700; color: var(--accent); }
.jnote {
  margin: var(--s-4) 0 0;
  padding: var(--s-4);
  background: var(--bg-sunk);
  border-radius: var(--r);
  color: var(--ink-2);
  font-size: var(--t-sm);
  line-height: 1.7;
  max-width: 78ch;
}
.jnote b { color: var(--ink); }

.jgrid-2, .jgrid-3 { display: grid; gap: var(--s-3); margin-top: var(--s-4); }
.jgrid-2 { grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); }
.jgrid-3 { grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }
.jcard {
  padding: var(--s-5);
  border: 1px solid var(--rule);
  border-radius: var(--r-lg);
  background: var(--bg-raise);
}
.jcard-wide { grid-column: 1 / -1; }
.jcard h3 { font-size: var(--t-base); margin: 0 0 var(--s-2); }
.jcard h3 .ko { font-size: var(--t-lg); color: var(--accent); margin-right: var(--s-2); }
.jcard p { margin: 0 0 var(--s-2); color: var(--ink-2); font-size: var(--t-sm); line-height: 1.7; }
.jcard p:last-child { margin-bottom: 0; }
.jcard a { color: var(--accent); font-weight: 500; }
.jladder {
  font-size: var(--t-lg);
  letter-spacing: var(--tr-head);
  color: var(--ink);
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-3);
}
.jladder .ko { font-weight: 600; }

/* ── The alphabet grid on the hub ───────────────────────────────────────
   The hub is the page a complete beginner lands on, so the letter is the
   largest thing in the cell by a wide margin and the tile is big enough to be
   a comfortable tap target for a child or for someone whose hands are not
   steady — 96px minimum, which is well past the 44px floor. Each tile is a
   link into that letter's own card, so the grid doubles as the section's
   contents. */

.jtiles {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
  gap: var(--s-2);
  margin-top: var(--s-4);
}
.jtile {
  display: grid;
  place-items: center;
  gap: 2px;
  padding: var(--s-4) var(--s-2);
  border: 1px solid var(--rule);
  border-radius: var(--r);
  background: var(--bg-raise);
  text-align: center;
}
.jtile:hover { border-color: var(--accent-line); background: var(--accent-wash); }
.jtile-ch { font-size: 2.25rem; font-weight: 600; line-height: 1.05; color: var(--ink); }
.jtile-rr { font-size: var(--t-base); font-weight: 700; color: var(--ink); }
.jtile-hint { font-size: var(--t-xs); color: var(--ink-3); line-height: 1.4; }
.jtiles-v .jtile-ch, .jtiles-v .jtile-rr { color: var(--accent); }
/* Fixed columns once there is room for them, because the count is the point:
   14 consonants fall into two rows of seven and the ten vowels into one row of
   ten. Auto-fill gives 11 + 3, which reads as "some letters" rather than as a
   set you could finish. */
@media (min-width: 900px) {
  .jtiles { grid-template-columns: repeat(7, 1fr); }
  .jtiles-v { grid-template-columns: repeat(10, 1fr); }
}

/* The step list on the hub carries its links on a second line, so a step reads
   as one instruction rather than as a sentence with holes in it. */
.jsteps-big li { font-size: var(--t-md); padding-left: 46px; }
.jsteps-big li::before { width: 32px; height: 32px; font-size: var(--t-base); }
.jstep-links { display: flex; flex-wrap: wrap; gap: var(--s-4); margin-top: var(--s-1); font-size: var(--t-sm); }

/* The one thing on the hub that is a button rather than a link. */
.jcta {
  display: flex;
  align-items: center;
  gap: var(--s-4);
  padding: var(--s-5);
  border: 1px solid var(--accent-line);
  border-radius: var(--r-lg);
  background: var(--accent-wash);
}
.jcta:hover { border-color: var(--accent); }
.jcta-face {
  flex: none;
  display: grid;
  place-items: center;
  width: 60px;
  height: 60px;
  border-radius: var(--r);
  background: var(--accent);
  color: var(--accent-ink);
  font-size: 2rem;
  font-weight: 700;
  line-height: 1;
}
.jcta-text { display: grid; gap: 2px; min-width: 0; }
.jcta-text b { font-size: var(--t-lg); letter-spacing: var(--tr-head); color: var(--ink); }
.jcta-text span { font-size: var(--t-sm); color: var(--ink-2); }
.jcta-go { flex: none; margin-left: auto; font-size: var(--t-xl); color: var(--accent); }

.jsteps { counter-reset: jstep; list-style: none; padding: 0; margin: var(--s-4) 0 0; display: grid; gap: var(--s-3); }
.jsteps li {
  position: relative;
  padding-left: 42px;
  color: var(--ink-2);
  line-height: 1.7;
}
.jsteps li::before {
  counter-increment: jstep;
  content: counter(jstep);
  position: absolute;
  left: 0;
  top: 0;
  width: 28px;
  height: 28px;
  display: grid;
  place-items: center;
  border-radius: var(--r-pill);
  background: var(--accent-wash);
  color: var(--accent);
  font-size: var(--t-sm);
  font-weight: 700;
}
.jsteps b { color: var(--ink); }
.jsteps a { color: var(--accent); }

/* ── The letter card ──────────────────────────────────────────────────── */

.jletters { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: var(--s-3); margin-top: var(--s-4); }
.jletter {
  display: grid;
  grid-template-columns: 108px 1fr;
  gap: var(--s-4);
  padding: var(--s-4);
  border: 1px solid var(--rule);
  border-radius: var(--r-lg);
  background: var(--bg-raise);
}
.jletter-face { display: grid; align-content: start; justify-items: center; gap: var(--s-2); }
.jletter-id { display: grid; justify-items: center; gap: 1px; }
.jletter-ch { font-size: var(--t-xl); font-weight: 700; line-height: 1; }
.jletter-name { font-size: var(--t-sm); font-weight: 600; color: var(--ink-2); }
.jletter-rr { font-size: var(--t-xs); color: var(--ink-4); }
.jletter-body { min-width: 0; }
.jletter-sounds { display: flex; gap: var(--s-2); margin: 0 0 var(--s-3); flex-wrap: wrap; }
.jslot {
  display: grid;
  gap: 1px;
  padding: var(--s-2) var(--s-3);
  border-radius: var(--r-sm);
  background: var(--bg-sunk);
  text-align: center;
}
.jslot b { font-size: var(--t-base); font-weight: 700; }
.jslot span { font-size: var(--t-xs); color: var(--ink-4); }
.jslot-ipa b { font-family: var(--mono); font-size: var(--t-sm); }
.jletter-desc { margin: 0 0 var(--s-2); color: var(--ink-2); line-height: 1.7; font-size: var(--t-sm); }
.jletter-note { margin: 0 0 var(--s-2); color: var(--ink-3); font-size: var(--t-sm); line-height: 1.65; }
.jletter-v .jletter-ch { color: var(--accent); }

.chip-harmony-bright { background: var(--warn-bg); color: var(--warn); }
.chip-harmony-dark { background: var(--accent-wash); color: var(--accent); }
.chip-harmony-neutral { background: var(--bg-sunk); color: var(--ink-3); }

/* Example words, linking into the vocabulary rows they live on. */
.jex-label { margin: var(--s-3) 0 var(--s-1); font-size: var(--t-xs); color: var(--ink-4); }
.jex { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: var(--s-1); }
.jex a {
  display: inline-flex;
  align-items: baseline;
  gap: var(--s-2);
  padding: var(--s-1) var(--s-3);
  border: 1px solid var(--rule);
  border-radius: var(--r-pill);
  font-size: var(--t-sm);
}
.jex a:hover { border-color: var(--accent-line); background: var(--accent-wash); }
.jex-ko { font-weight: 600; }
.jex-roman { font-size: var(--t-xs); color: var(--ink-4); }
.jex-en { font-size: var(--t-xs); color: var(--ink-3); }

/* ── Stroke-order diagrams ────────────────────────────────────────────── */

.hs { display: block; overflow: visible; }
.hs-grid rect { fill: var(--bg-sunk); stroke: var(--rule); stroke-width: 1; }
.hs-grid path { stroke: var(--rule); stroke-width: 1; stroke-dasharray: 3 5; fill: none; }
.hs-strokes path {
  fill: none;
  stroke: var(--ink);
  stroke-width: 7;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.hs-num circle { fill: var(--accent); }
.hs-num text {
  fill: var(--accent-ink);
  font-family: var(--font);
  font-size: 10px;
  font-weight: 700;
  text-anchor: middle;
}

/* The letter draws itself once, stroke by stroke, in writing order. Pure CSS
   on stroke-dashoffset — each path carries its own measured length as --len,
   so a long stroke takes longer than a short one instead of every stroke
   snapping at the same speed. */
@keyframes hs-draw { to { stroke-dashoffset: 0; } }
@keyframes hs-pop { from { opacity: 0; transform: scale(0.6); } to { opacity: 1; transform: scale(1); } }
.hs-anim .hs-strokes path {
  stroke-dasharray: var(--len);
  stroke-dashoffset: var(--len);
  animation: hs-draw calc(var(--len) * 5ms) ease-out forwards;
  animation-delay: calc(var(--i) * 620ms);
}
.hs-anim .hs-num {
  opacity: 0;
  transform-box: fill-box;
  transform-origin: center;
  animation: hs-pop 200ms ease-out forwards;
  animation-delay: calc(var(--i) * 620ms);
}

@media (prefers-reduced-motion: reduce) {
  .hs-anim .hs-strokes path { stroke-dasharray: none; stroke-dashoffset: 0; animation: none; }
  .hs-anim .hs-num { opacity: 1; animation: none; }
}

/* The stroke-order wall: every letter at tracing size. */
.jwall { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: var(--s-4); margin-top: var(--s-4); }
.jwall-cell { margin: 0; display: grid; justify-items: center; gap: var(--s-2); }
.jwall-cell figcaption { display: grid; justify-items: center; gap: 1px; text-align: center; }
.jwall-cell figcaption .ko { font-size: var(--t-lg); font-weight: 700; line-height: 1.1; }
.jwall-name { font-size: var(--t-xs); color: var(--ink-2); }
.jwall-n { font-size: var(--t-xs); color: var(--ink-4); }

/* ── Tables shared across the section ─────────────────────────────────── */

.jtable { width: 100%; border-collapse: collapse; font-size: var(--t-sm); }
.jtable th, .jtable td { text-align: left; padding: var(--s-2) var(--s-3); border-bottom: 1px solid var(--rule); vertical-align: middle; }
.jtable th { font-weight: 600; color: var(--ink-3); font-size: var(--t-xs); text-transform: uppercase; letter-spacing: var(--tr-label); }
.jtable td { color: var(--ink-2); }
.jtable tbody tr:hover { background: var(--bg-sunk); }
.jcell { font-size: var(--t-lg); font-weight: 700; color: var(--ink); }
.jtable-ladder .jcell { text-align: center; }
.jpair .ko { font-weight: 600; color: var(--ink); }

/* ── Syllable blocks ──────────────────────────────────────────────────── */

.jshapes { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: var(--s-3); margin-top: var(--s-4); }
.jshape {
  display: flex;
  align-items: center;
  gap: var(--s-4);
  padding: var(--s-4);
  border: 1px solid var(--rule);
  border-radius: var(--r-lg);
  background: var(--bg-raise);
}
.jshape-text { min-width: 0; }
.jshape-text h3 { margin: 0; font-size: var(--t-sm); font-weight: 600; }
.jshape-en { margin: 0 0 var(--s-1); font-size: var(--t-xs); color: var(--ink-4); }
.jblockrow { display: flex; align-items: center; gap: var(--s-2); flex: none; }
.jblock {
  display: grid;
  width: 76px;
  height: 76px;
  border: 1px dashed var(--accent-line);
  border-radius: var(--r-sm);
  background: var(--bg-sunk);
}
.jb { display: grid; place-items: center; font-weight: 600; color: var(--accent); font-size: var(--t-base); }
.jb-i { grid-area: i; }
.jb-m { grid-area: m; }
.jb-f { grid-area: f; }
.jblock-side { grid-template: "i m" 1fr / 1fr 1fr; }
.jblock-stack { grid-template: "i" 1fr "m" 1fr / 1fr; }
.jblock-wrap { grid-template: "i m" 1fr / 0.8fr 1.2fr; }
.jblock-side-b { grid-template: "i m" 1.4fr "f f" 1fr / 1fr 1fr; }
.jblock-stack-b { grid-template: "i" 1fr "m" 1fr "f" 1fr / 1fr; }
.jblock-wrap-b { grid-template: "i m" 1.4fr "f f" 1fr / 0.8fr 1.2fr; }
.jblock-eq { color: var(--ink-4); }
.jblock-out { font-size: 2.6rem; font-weight: 700; line-height: 1; letter-spacing: var(--tr-display); }

/* The commonest syllable blocks, counted across the whole dictionary. */
.jtop { display: grid; grid-template-columns: repeat(auto-fit, minmax(88px, 1fr)); gap: var(--s-2); margin-top: var(--s-4); }
.jtop-cell {
  display: grid;
  place-items: center;
  gap: 2px;
  padding: var(--s-3);
  border: 1px solid var(--rule);
  border-radius: var(--r);
  background: var(--bg-raise);
}
.jtop-ch { font-size: var(--t-xl); font-weight: 700; line-height: 1; }
.jtop-rr { font-size: var(--t-xs); color: var(--ink-4); }
.jtop-n { font-size: var(--t-xs); color: var(--accent); font-weight: 600; }

/* ── Batchim ──────────────────────────────────────────────────────────── */

.jbats { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: var(--s-3); margin-top: var(--s-4); }
.jbat { padding: var(--s-4); border: 1px solid var(--rule); border-radius: var(--r-lg); background: var(--bg-raise); }
.jbat-head { display: flex; align-items: center; gap: var(--s-3); margin-bottom: var(--s-3); }
.jbat-ch {
  display: grid;
  place-items: center;
  width: 52px;
  height: 52px;
  flex: none;
  border-radius: var(--r);
  background: var(--accent-wash);
  color: var(--accent);
  font-size: var(--t-xl);
  font-weight: 700;
}
.jbat-rr, .jbat-ex { display: block; }
.jbat-rr { font-size: var(--t-base); font-weight: 700; }
.jbat-ex { font-size: var(--t-sm); color: var(--ink-2); }
.jbat-spelled { margin: 0 0 var(--s-2); font-size: var(--t-xs); color: var(--ink-4); display: flex; flex-wrap: wrap; gap: var(--s-1); align-items: center; }
.jbat-tag {
  display: inline-grid;
  place-items: center;
  min-width: 26px;
  padding: 2px 4px;
  border: 1px solid var(--rule);
  border-radius: var(--r-sm);
  color: var(--ink);
  font-size: var(--t-sm);
  font-weight: 600;
}

/* 연음 and the romanization rules: written form → what is actually said. */
.jflow { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: var(--s-2); margin-top: var(--s-4); }
.jflow-item {
  display: flex;
  align-items: baseline;
  gap: var(--s-2);
  padding: var(--s-3) var(--s-4);
  border: 1px solid var(--rule);
  border-radius: var(--r);
  background: var(--bg-raise);
  font-size: var(--t-md);
  font-weight: 600;
}
.jflow-arrow { color: var(--ink-4); font-weight: 400; }
.jflow-pron { color: var(--accent); }
.jflow-rr { font-size: var(--t-xs); color: var(--ink-4); font-weight: 400; margin-left: auto; }

.jrules { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: var(--s-3); margin-top: var(--s-4); }
.jrule { padding: var(--s-4); border: 1px solid var(--rule); border-radius: var(--r-lg); background: var(--bg-raise); }
.jrule-head { display: flex; align-items: baseline; gap: var(--s-2); margin-bottom: var(--s-2); }
.jrule-ko { font-size: var(--t-base); font-weight: 700; }
.jrule-en { font-size: var(--t-xs); color: var(--ink-4); text-transform: uppercase; letter-spacing: var(--tr-label); }
.jrule-ex { margin: 0 0 var(--s-2); display: flex; align-items: baseline; gap: var(--s-2); font-size: var(--t-md); }
.jrule-ex .ko { font-weight: 600; }
.jrule-ex b { color: var(--accent); }

/* ── Romanization tables ──────────────────────────────────────────────── */

.jromtable { margin-bottom: var(--s-5); }
.jromgrid { display: grid; grid-template-columns: repeat(var(--cols), minmax(0, 1fr)); gap: var(--s-2); }
.jrom {
  display: grid;
  place-items: center;
  gap: 2px;
  padding: var(--s-3) var(--s-2);
  border: 1px solid var(--rule);
  border-radius: var(--r);
  background: var(--bg-raise);
}
.jrom-ch { font-size: var(--t-lg); font-weight: 700; line-height: 1; }
.jrom-rr { font-size: var(--t-sm); color: var(--accent); font-weight: 600; }

/* ── The keyboard ─────────────────────────────────────────────────────── */

.jkbd { display: grid; gap: var(--s-2); margin-top: var(--s-4); }
.jkbd-row { display: flex; gap: var(--s-2); }
.jkbd-r2 { padding-left: 3%; }
.jkbd-r3 { padding-left: 9%; }
.jkey {
  position: relative;
  flex: 1 1 0;
  min-width: 0;
  aspect-ratio: 1 / 1;
  max-width: 74px;
  display: grid;
  place-items: center;
  border: 1px solid var(--rule);
  border-radius: var(--r);
  background: var(--bg-raise);
  box-shadow: var(--shadow-1);
}
.jkey-lat { position: absolute; top: 4px; left: 6px; font-size: var(--t-xs); color: var(--ink-4); }
.jkey-shift { position: absolute; top: 3px; right: 6px; font-size: var(--t-xs); color: var(--accent); font-weight: 600; }
.jkey-ko { font-size: var(--t-lg); font-weight: 700; line-height: 1; }
.jkey-c { background: var(--bg-raise); }
.jkey-c .jkey-ko { color: var(--ink); }
.jkey-v { background: var(--accent-wash); border-color: var(--accent-line); }
.jkey-v .jkey-ko { color: var(--accent); }
.jkbd-legend { display: flex; flex-wrap: wrap; gap: var(--s-4); margin-top: var(--s-3); font-size: var(--t-xs); color: var(--ink-3); align-items: center; }
.jswatch { width: 12px; height: 12px; border-radius: 3px; display: inline-block; margin-right: var(--s-1); vertical-align: -1px; border: 1px solid var(--rule); }
.jswatch-c { background: var(--bg-raise); }
.jswatch-v { background: var(--accent-wash); border-color: var(--accent-line); }
.jswatch-s { background: var(--accent); border-color: var(--accent); }

.jtype { display: grid; gap: var(--s-2); margin-top: var(--s-4); }
.jtype-step {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-3) var(--s-4);
  border: 1px solid var(--rule);
  border-radius: var(--r);
  background: var(--bg-raise);
}
.jtype-step .ko { font-size: var(--t-lg); font-weight: 600; letter-spacing: 0.12em; }
.jtype-step kbd, .jcard kbd, .jnote kbd {
  font-family: var(--mono);
  font-size: var(--t-xs);
  padding: 2px 7px;
  border: 1px solid var(--rule);
  border-bottom-width: 2px;
  border-radius: 5px;
  background: var(--bg-sunk);
  color: var(--ink-2);
}

/* ── The practice drill (/hangul/practice/) ─────────────────────────────
   One question fills the screen and nothing else competes with it. The stage
   has a floor rather than a fixed height so a letter and a whole syllable sit
   in the same place, and the answer buttons never move between questions —
   a drill answered by muscle memory cannot have its targets shifting.

   Everything here is sized for a reader who may be eight or eighty: the face
   is 6rem, the answer buttons are 64px tall, and right/wrong is carried by a
   word and an icon-free colour block rather than by colour alone.
   ───────────────────────────────────────────────────────────────────────── */

.hp {
  max-width: 620px;
  margin: 0 auto var(--s-7);
}
.hq-h { font-size: var(--t-lg); font-weight: 700; letter-spacing: var(--tr-head); margin: 0 0 var(--s-4); }

/* ── Setup ── */

.hq-groups { display: grid; gap: var(--s-2); }
.hq-group {
  display: flex;
  align-items: center;
  gap: var(--s-4);
  padding: var(--s-4);
  border: 1px solid var(--rule);
  border-radius: var(--r-lg);
  background: var(--bg-raise);
  cursor: pointer;
}
.hq-group:hover { border-color: var(--accent-line); }
.hq-group input {
  flex: none;
  width: 22px;
  height: 22px;
  accent-color: var(--accent);
  cursor: pointer;
}
.hq-group:has(input:checked) { border-color: var(--accent); background: var(--accent-wash); }
.hq-group input:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.hq-group-body { display: grid; gap: 1px; min-width: 0; }
.hq-group-sample { font-size: var(--t-lg); font-weight: 700; line-height: 1.2; color: var(--ink); letter-spacing: 0.06em; }
.hq-group-name { font-size: var(--t-base); font-weight: 600; color: var(--ink); }
.hq-group-note { font-size: var(--t-xs); color: var(--ink-3); }

.hq-warn { margin: var(--s-3) 0 0; color: var(--bad); font-size: var(--t-sm); font-weight: 600; }

.hq-opts { display: grid; gap: var(--s-4); margin-top: var(--s-5); }
.hq-opt { border: 0; padding: 0; margin: 0; min-width: 0; }
.hq-opt legend { padding: 0; font-size: var(--t-xs); font-weight: 600; color: var(--ink-3); letter-spacing: var(--tr-label); text-transform: uppercase; margin-bottom: var(--s-2); }
.hq-seg { display: flex; flex-wrap: wrap; gap: var(--s-2); }
.hq-seg label { cursor: pointer; }
.hq-seg input { position: absolute; opacity: 0; width: 0; height: 0; }
.hq-seg span {
  display: inline-block;
  padding: var(--s-2) var(--s-4);
  border: 1px solid var(--rule);
  border-radius: var(--r-pill);
  background: var(--bg);
  color: var(--ink-2);
  font-size: var(--t-sm);
}
.hq-seg input:checked + span { border-color: var(--accent); background: var(--accent); color: var(--accent-ink); font-weight: 600; }
.hq-seg input:focus-visible + span { outline: 2px solid var(--focus); outline-offset: 2px; }

.hq-start { width: 100%; margin-top: var(--s-5); justify-content: center; }

/* ── The round ── */

.hq-top { display: flex; align-items: center; gap: var(--s-3); }
.hq-progress { margin: 0; font-size: var(--t-sm); color: var(--ink-3); }
.hq-progress span { color: var(--ink); font-weight: 700; }
.hq-score { margin: 0 0 0 auto; font-size: var(--t-sm); color: var(--ink-3); }
.hq-score b { color: var(--ok); font-size: var(--t-base); }
.hq-stop {
  border: 1px solid var(--rule);
  border-radius: var(--r-pill);
  background: var(--bg);
  color: var(--ink-3);
  font: inherit;
  font-size: var(--t-xs);
  padding: 4px var(--s-3);
  cursor: pointer;
}
.hq-stop:hover { border-color: var(--accent-line); color: var(--accent); }

.hq-bar { height: 6px; margin-top: var(--s-3); border-radius: var(--r-pill); background: var(--bg-sunk); overflow: hidden; }
.hq-bar-fill { display: block; height: 100%; width: 0; border-radius: var(--r-pill); background: var(--accent); transition: width 220ms ease-out; }

.hq-stage {
  display: grid;
  place-items: center;
  gap: var(--s-3);
  min-height: 200px;
  margin: var(--s-5) 0;
  padding: var(--s-5);
  border: 1px solid var(--rule);
  border-radius: var(--r-xl);
  background: var(--bg-raise);
  box-shadow: var(--shadow-1);
  text-align: center;
}
.hq-ask { margin: 0; font-size: var(--t-sm); color: var(--ink-3); }
.hq-face { margin: 0; font-size: 6rem; font-weight: 600; line-height: 1; letter-spacing: var(--tr-display); color: var(--ink); }
/* A romanization prompt is Latin text and reads as shouty at 6rem; it also has
   to be told apart at a glance from the Hangul it is asking about. */
.hq-face-sound { font-size: 3.5rem; font-weight: 700; color: var(--accent); }

.hq-choices { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-2); }
.hq-choice {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  min-height: 64px;
  padding: var(--s-3) var(--s-4);
  border: 1px solid var(--rule);
  border-radius: var(--r-lg);
  background: var(--bg-raise);
  color: var(--ink);
  font: inherit;
  cursor: pointer;
  text-align: left;
}
.hq-choice:hover:not(:disabled) { border-color: var(--accent); background: var(--accent-wash); }
.hq-choice:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.hq-choice:disabled { cursor: default; }
.hq-choice-key {
  flex: none;
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  border-radius: var(--r-sm);
  background: var(--bg-sunk);
  color: var(--ink-4);
  font-size: var(--t-xs);
  font-weight: 700;
}
.hq-choice-label { font-size: var(--t-lg); font-weight: 600; letter-spacing: var(--tr-head); }
.hq-choice-ko .hq-choice-label { font-size: var(--t-xl); }
/* Right and wrong are a fill, a border and a word in the verdict line — never
   colour on its own. */
.hq-choice.is-right { border-color: var(--ok); background: var(--ok-bg); color: var(--ok); }
.hq-choice.is-right .hq-choice-key { background: var(--ok); color: var(--bg); }
.hq-choice.is-wrong { border-color: var(--bad); background: var(--bad-bg); color: var(--bad); }
.hq-choice.is-wrong .hq-choice-key { background: var(--bad); color: var(--bg); }

/* The verdict and the Next button occupy the same space whether or not they
   are showing. A right answer advances on its own and shows neither, and
   without the reservation the page under the drill jumps up and down between
   every question. */
.hq-after { margin-top: var(--s-4); display: grid; gap: var(--s-3); justify-items: center; align-content: start; min-height: 112px; }
.hq-after[hidden] { display: grid; visibility: hidden; }
.hq-verdict { margin: 0; font-size: var(--t-md); font-weight: 600; text-align: center; }
.hq-verdict.is-right { color: var(--ok); }
.hq-verdict.is-wrong { color: var(--bad); }
.hq-next { min-width: 200px; justify-content: center; }

/* ── The finish ── */

.hq-done { text-align: center; }
.hq-done-score { margin: 0; font-size: var(--t-md); color: var(--ink-3); }
.hq-done-score b { display: block; font-size: var(--t-3xl); line-height: 1.1; color: var(--ink); letter-spacing: var(--tr-display); }
.hq-done-note { margin: var(--s-2) auto var(--s-5); max-width: 44ch; color: var(--ink-2); }
.hq-missed { text-align: left; margin-bottom: var(--s-5); }
.hq-missed-list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s-2); }
.hq-missed-list li {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-3) var(--s-4);
  border: 1px solid var(--rule);
  border-radius: var(--r);
  background: var(--bg-raise);
}
.hq-missed-ch { font-size: var(--t-xl); font-weight: 700; line-height: 1; }
.hq-missed-ans { font-size: var(--t-base); font-weight: 700; color: var(--accent); }
.hq-missed-hint { font-size: var(--t-sm); color: var(--ink-3); margin-left: auto; text-align: right; }
.hq-done-btns { display: flex; flex-wrap: wrap; gap: var(--s-2); justify-content: center; }

.jtable-chart .jcell { text-align: center; width: 4em; }

@media (max-width: 720px) {
  .jletter { grid-template-columns: 84px 1fr; gap: var(--s-3); padding: var(--s-3); }
  .jshape { flex-direction: column; align-items: flex-start; }
  .jkey { border-radius: var(--r-sm); }
  .jkey-shift { display: none; }
  .jblock-out { font-size: 2rem; }
  .jtiles { grid-template-columns: repeat(auto-fill, minmax(84px, 1fr)); }
  .jtile-ch { font-size: 1.875rem; }
  .jcta { gap: var(--s-3); padding: var(--s-4); }
  .jcta-face { width: 48px; height: 48px; font-size: 1.5rem; }
  .hq-face { font-size: 4.5rem; }
  .hq-face-sound { font-size: 2.75rem; }
  .hq-stage { min-height: 168px; margin: var(--s-4) 0; }
  .hq-missed-hint { flex-basis: 100%; margin-left: 0; text-align: left; }
}

/* ── Home ────────────────────────────────────────────────────────────────
   A funnel, not a page of content: hero, what you were last doing, the
   sections as cards that carry their own size, the ladder, and your own
   progress. Denser above the fold than a Western study site, which is the
   Naver habit — more sections, each of them shorter.
   ───────────────────────────────────────────────────────────────────────── */

.hero {
  padding: var(--s-8) 0 var(--s-7);
  border-bottom: 1px solid var(--rule);
  margin-bottom: var(--s-7);
}
.hero h1 {
  font-size: clamp(2rem, 5vw, var(--t-3xl));
  letter-spacing: var(--tr-display);
  line-height: 1.1;
  max-width: 18ch;
}
.hero .hero-ko {
  display: block;
  font-size: var(--t-md);
  font-weight: 600;
  color: var(--accent);
  letter-spacing: var(--tr-head);
  margin-bottom: var(--s-3);
}
.hero-lead {
  margin-top: var(--s-4);
  max-width: 58ch;
  font-size: var(--t-md);
  color: var(--ink-2);
  line-height: 1.7;
}
.hero-cta { display: flex; flex-wrap: wrap; gap: var(--s-3); margin-top: var(--s-5); }
.btn {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-3) var(--s-5);
  border-radius: var(--r-pill);
  font-weight: 600;
  font-size: var(--t-sm);
  border: 1px solid transparent;
}
.btn-primary { background: var(--accent); color: var(--accent-ink); }
.btn-primary:hover { filter: brightness(1.08); }
.btn-ghost { border-color: var(--rule); color: var(--ink-2); background: var(--bg); }
.btn-ghost:hover { border-color: var(--accent-line); color: var(--accent); }

.home-section { margin-bottom: var(--s-8); }

/* The level ladder as six full cards rather than the compact rail — on the
   home page the ladder IS the content, not the navigation around it. */
.ladder { display: grid; grid-template-columns: repeat(auto-fit, minmax(168px, 1fr)); gap: var(--s-3); }
.ladder-step {
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
  padding: var(--s-4);
  background: var(--bg-raise);
  border: 1px solid var(--rule);
  border-top: 3px solid var(--lvl);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-1);
  transition: transform 0.12s ease, box-shadow 0.12s ease;
}
.ladder-step:hover { transform: translateY(-3px); box-shadow: var(--shadow-2); }
.ladder-ko { font-size: var(--t-xl); font-weight: 700; color: var(--lvl); letter-spacing: var(--tr-head); line-height: 1; }
.ladder-name { font-size: var(--t-sm); font-weight: 600; }
.ladder-blurb { font-size: var(--t-xs); color: var(--ink-3); line-height: 1.55; flex: 1; }
.ladder-count { font-size: var(--t-xs); color: var(--ink-4); }
.ladder-count b { color: var(--ink-2); font-size: var(--t-sm); }

/* The differentiator strip: the sections that exist because this dataset has
   fields the German and Japanese ones do not. */
.feature-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: var(--s-3); }
.feature {
  padding: var(--s-5);
  background: var(--bg-sunk);
  border-radius: var(--r-lg);
}
.feature h3 { display: flex; flex-direction: column; gap: 2px; margin-bottom: var(--s-2); }
.feature h3 .ko { font-size: var(--t-lg); font-weight: 700; letter-spacing: var(--tr-head); }
.feature h3 .en { font-size: var(--t-xs); text-transform: uppercase; letter-spacing: var(--tr-label); color: var(--ink-4); }
.feature p { font-size: var(--t-sm); color: var(--ink-2); line-height: 1.65; }
.feature a { color: var(--accent); font-weight: 600; font-size: var(--t-sm); }

/* The line under the CTAs. The search opener is a button because it opens the
   overlay rather than navigating, and it has to read as the link it behaves
   like — 01-base.css only underlines a bare <a>, so the rule is spelled out. */
.hero-fine { margin-top: var(--s-4); font-size: var(--t-sm); color: var(--ink-3); }
.link-btn {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  font-weight: 600;
  color: var(--accent);
  cursor: pointer;
  text-underline-offset: 3px;
}
.link-btn:hover { text-decoration: underline; }

/* ── Your progress, on the home page ──
   One bar per level, painted by app.js and hidden until a level has something
   marked. `done` is the level's own colour so the six bars read as the ladder;
   scheduled is the same hue held back, because a word waiting for review is
   the same word part-way there rather than a separate category. */
.hp-bars { display: grid; gap: var(--s-2); }
.hp-row {
  display: grid;
  grid-template-columns: 4.5rem 1fr auto auto;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-3) var(--s-4);
  background: var(--bg-raise);
  border: 1px solid var(--rule);
  border-radius: var(--r);
  color: var(--ink);
}
.hp-row:hover { border-color: var(--lvl, var(--accent)); }
.hp-lvl { font-size: var(--t-sm); font-weight: 700; color: var(--lvl, var(--ink)); }
.hp-bar {
  display: flex;
  height: 8px;
  min-width: 0;
  border-radius: var(--r-pill);
  background: var(--bg-sunk);
  overflow: hidden;
}
.hp-seg { display: block; height: 100%; }
.hp-done { background: var(--lvl, var(--accent)); }
.hp-sched { background: color-mix(in srgb, var(--lvl, var(--accent)) 38%, transparent); }
.hp-count { font-size: var(--t-xs); color: var(--ink-4); white-space: nowrap; }
.hp-count b { color: var(--ink); font-weight: 600; font-size: var(--t-sm); }
.hp-due:not(:empty) {
  padding: 2px var(--s-2);
  border-radius: var(--r-pill);
  background: var(--warn-bg);
  color: var(--warn);
  font-size: var(--t-xs);
  font-weight: 600;
  white-space: nowrap;
}

/* ── Authored prose: guides, attribution, privacy, 404 ─────────────────── */

.prose { max-width: 68ch; }
.prose h2 { margin: var(--s-7) 0 var(--s-3); }
.prose h3 { margin: var(--s-6) 0 var(--s-2); }
.prose p, .prose li { color: var(--ink-2); line-height: 1.75; }
.prose ul, .prose ol { margin: 0 0 var(--s-4); padding-left: 1.2em; }
.prose ul { list-style: disc; }
.prose ol { list-style: decimal; }
.prose li { margin-bottom: var(--s-2); }
.prose a { color: var(--accent); text-decoration: underline; text-underline-offset: 0.18em; }
.prose code {
  font-family: var(--mono);
  font-size: 0.9em;
  background: var(--bg-sunk);
  padding: 1px 5px;
  border-radius: 4px;
}
.prose table { width: 100%; border-collapse: collapse; margin-bottom: var(--s-5); font-size: var(--t-sm); }
.prose th, .prose td { text-align: left; padding: var(--s-2) var(--s-3); border-bottom: 1px solid var(--rule); }
.prose th { font-weight: 600; color: var(--ink); }
.prose blockquote {
  margin: 0 0 var(--s-4);
  padding-left: var(--s-4);
  border-left: 3px solid var(--accent-line);
  color: var(--ink-3);
}

/* A table that is wider than its container scrolls inside its own box rather
   than making the page scroll sideways. */
.table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }

.notfound { text-align: center; padding: var(--s-9) 0; }
.notfound h1 { font-size: var(--t-3xl); }
.notfound .ko { display: block; font-size: var(--t-lg); color: var(--accent); margin-bottom: var(--s-3); }

/* ── Grammar ─────────────────────────────────────────────────────────────
   Three surfaces: the compact list row, the grammar point page, and the
   comparison pages. The point page is the site's first real "article" layout
   — one heading, a stack of senses, examples underneath each — so it sets the
   measure that the idiom and hanja pages will follow.
   ───────────────────────────────────────────────────────────────────────── */

/* ── The list row ──
   A link, not a <details>: a grammar point has a page behind it. The form
   leads at display size because scanning a grammar list is scanning shapes —
   -고, -어서, -으니까 — and the English is what confirms the guess. */
.grows { display: flex; flex-direction: column; gap: var(--s-2); }

.grow {
  display: grid;
  grid-template-columns: minmax(88px, auto) 1fr auto;
  align-items: baseline;
  gap: var(--s-2) var(--s-4);
  padding: var(--s-3) var(--s-4);
  background: var(--bg-raise);
  border: 1px solid var(--rule);
  border-radius: var(--r);
  transition: border-color 0.12s ease, background 0.12s ease;
}
.grow:hover { border-color: var(--accent-line); background: var(--accent-wash); }
.grow-form {
  grid-row: span 2;
  font-size: var(--t-lg);
  font-weight: 600;
  letter-spacing: var(--tr-head);
  color: var(--ink);
}
.grow-meta { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--s-2); font-size: var(--t-xs); color: var(--ink-4); }
.grow-roman { font-variant: small-caps; letter-spacing: var(--tr-label); }
.grow-variants { color: var(--ink-3); }
.grow-type { color: var(--ink-4); }
.grow-gloss {
  grid-column: 2;
  font-size: var(--t-sm);
  color: var(--ink-2);
  line-height: 1.6;
}
.grow .chip-level { grid-row: span 2; align-self: center; }

/* Romanization rides the same toggle as the vocabulary rows. */
.grow-roman { display: none; }
:root[data-roman="on"] .grow-roman { display: inline; }

/* ── The grammar point page ── */
.gp-head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--s-4);
  padding-bottom: var(--s-5);
  margin-bottom: var(--s-5);
  border-bottom: 1px solid var(--rule);
}
.gp-title h1 { margin: 0; }
.gp-form {
  display: block;
  font-size: var(--t-2xl);
  font-weight: 700;
  letter-spacing: var(--tr-display);
  line-height: var(--lh-tight);
}
.gp-h1-en {
  display: block;
  margin-top: var(--s-2);
  font-size: var(--t-xs);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: var(--tr-label);
  color: var(--ink-4);
}
.gp-roman {
  margin: var(--s-1) 0 0;
  font-size: var(--t-sm);
  font-variant: small-caps;
  letter-spacing: var(--tr-label);
  color: var(--ink-4);
}
.gp-lead { font-size: var(--t-md); color: var(--ink-2); }

/* The variants block: 은 / 는, 이 / 가 — the pair as one object. */
.gp-variants {
  padding: var(--s-4) var(--s-5);
  background: var(--bg-sunk);
  border-radius: var(--r-lg);
  margin-bottom: var(--s-6);
}
.gp-variants h3 { display: flex; flex-direction: column; gap: 2px; font-size: var(--t-xs); text-transform: uppercase; letter-spacing: var(--tr-label); color: var(--ink-4); margin: 0 0 var(--s-3); }
.gp-variants h3 span { font-size: var(--t-base); text-transform: none; letter-spacing: normal; color: var(--ink); font-weight: 600; }
.gp-forms { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2); }
.gp-form-chip {
  font-size: var(--t-lg);
  font-weight: 600;
  padding: var(--s-1) var(--s-3);
  border-radius: var(--r-sm);
  background: var(--bg-raise);
  border: 1px solid var(--rule);
}
.gp-form-chip.is-head { border-color: var(--accent-line); color: var(--accent); }
.gp-slash { color: var(--ink-4); }
.gp-forms-note { margin: var(--s-3) 0 0; font-size: var(--t-sm); color: var(--ink-3); }

/* One sense. Numbered only when there is more than one — a lone "1" implies a
   second sense that does not exist. */
.gp-sense { padding-top: var(--s-6); border-top: 1px solid var(--rule); margin-top: var(--s-6); }
.gp-sense:first-child { padding-top: 0; border-top: 0; margin-top: 0; }
.gp-sense h3 { display: flex; align-items: baseline; gap: var(--s-3); font-size: var(--t-lg); line-height: var(--lh-snug); max-width: 62ch; }
.gp-sense-n {
  flex: none;
  display: inline-grid;
  place-items: center;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--accent-wash);
  color: var(--accent);
  font-size: var(--t-sm);
  font-weight: 700;
}
.gp-def-ko { margin: var(--s-2) 0 0; color: var(--ink-3); font-size: var(--t-sm); max-width: 62ch; }

/* The usage note — the 받침 rule, in both languages. English leads because
   the reader is an English speaker; the Korean stays because it is the
   dictionary's own wording and is what a Korean textbook will repeat. */
.usage-note {
  margin: var(--s-4) 0;
  padding: var(--s-3) var(--s-4);
  border-left: 3px solid var(--accent-line);
  background: var(--accent-wash);
  border-radius: 0 var(--r) var(--r) 0;
}
.usage-note h4 {
  display: flex;
  gap: var(--s-2);
  align-items: baseline;
  margin: 0 0 var(--s-2);
  font-size: var(--t-xs);
  text-transform: uppercase;
  letter-spacing: var(--tr-label);
  color: var(--ink-4);
}
.usage-note h4 span { text-transform: none; letter-spacing: normal; color: var(--accent); font-weight: 600; }
.note-en { margin: 0; color: var(--ink); font-size: var(--t-sm); }
.note-ko { margin: var(--s-1) 0 0; color: var(--ink-3); font-size: var(--t-sm); }

.gp-examples { margin-top: var(--s-4); }
.gp-examples h4, .dialogue h4 {
  display: flex;
  gap: var(--s-2);
  align-items: baseline;
  margin: 0 0 var(--s-3);
  font-size: var(--t-xs);
  text-transform: uppercase;
  letter-spacing: var(--tr-label);
  color: var(--ink-4);
}
.gp-examples h4 span, .dialogue h4 span { text-transform: none; letter-spacing: normal; color: var(--ink-2); font-weight: 600; }
.gp-examples .ex { padding: var(--s-2) 0; border-bottom: 1px solid var(--rule); }
.gp-examples .ex:last-child { border-bottom: 0; }

/* ── Dialogue ──
   Two turns, alternating alignment, with the speaker as a small disc. Shared
   with the idiom and conversation pages. */
.dialogue { margin-top: var(--s-5); padding: var(--s-4); background: var(--bg-sunk); border-radius: var(--r-lg); }
.turn { display: flex; gap: var(--s-3); padding: var(--s-2) 0; }
.turn-who {
  flex: none;
  display: inline-grid;
  place-items: center;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  font-size: var(--t-xs);
  font-weight: 700;
  background: var(--bg-raise);
  border: 1px solid var(--rule);
  color: var(--ink-3);
}
.turn-b .turn-who { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
.turn-ko { margin: 0; font-size: var(--t-md); line-height: var(--lh-body); }
.turn-en { margin: var(--s-1) 0 0; font-size: var(--t-sm); color: var(--ink-3); }

/* ── Comparison pages ── */
.cmp-hook { font-size: var(--t-md); color: var(--ink-2); }

.cmp-forms {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: var(--s-3);
  margin: var(--s-6) 0;
}
.cmp-form {
  padding: var(--s-4);
  background: var(--bg-raise);
  border: 1px solid var(--rule);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-1);
}
.cmp-form-ko { display: block; font-size: var(--t-xl); font-weight: 700; letter-spacing: var(--tr-head); }
.cmp-form-label {
  display: block;
  margin-top: var(--s-1);
  font-size: var(--t-xs);
  text-transform: uppercase;
  letter-spacing: var(--tr-label);
  color: var(--ink-4);
}
.cmp-form p { margin: var(--s-3) 0 0; font-size: var(--t-sm); color: var(--ink-2); }
.cmp-form-link { display: inline-block; margin-top: var(--s-3); font-size: var(--t-sm); color: var(--accent); }

.cmp-table { width: 100%; border-collapse: collapse; font-size: var(--t-sm); }
.cmp-table th, .cmp-table td { text-align: left; padding: var(--s-3); border-bottom: 1px solid var(--rule); vertical-align: top; }
.cmp-table th { font-size: var(--t-xs); text-transform: uppercase; letter-spacing: var(--tr-label); color: var(--ink-4); }
.cmp-table td { color: var(--ink-2); }
.cmp-pick { font-weight: 600; color: var(--accent) !important; white-space: nowrap; }

/* The minimal pairs. Both sides are correct, so neither is styled as the
   winner — they sit side by side at equal weight, which is the whole
   argument the page is making. */
.cmp-pairs { display: flex; flex-direction: column; gap: var(--s-3); }
.cmp-pair {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--s-3);
  padding: var(--s-4);
  background: var(--bg-sunk);
  border-radius: var(--r-lg);
}
.cmp-side { padding: var(--s-3); background: var(--bg-raise); border-radius: var(--r); border: 1px solid var(--rule); }
.cmp-ko { margin: 0; font-size: var(--t-md); font-weight: 500; line-height: var(--lh-body); }
.cmp-en { margin: var(--s-1) 0 var(--s-2); font-size: var(--t-sm); color: var(--ink-2); }
.cmp-note { margin: 0; padding-top: var(--s-2); border-top: 1px solid var(--rule); font-size: var(--t-xs); color: var(--ink-3); line-height: 1.6; }

/* Mistakes. Red is for wrong and green for right — the only place on the site
   either colour appears outside answer feedback, and for the same reason. */
.cmp-mistakes { display: flex; flex-direction: column; gap: var(--s-3); }
.cmp-mistake { padding: var(--s-4); border: 1px solid var(--rule); border-radius: var(--r-lg); }
.cmp-bad, .cmp-good { display: flex; gap: var(--s-2); align-items: baseline; margin: 0 0 var(--s-2); font-size: var(--t-md); }
.cmp-bad { color: var(--bad); text-decoration: line-through; text-decoration-color: color-mix(in oklab, var(--bad) 45%, transparent); }
.cmp-good { color: var(--ok); }
.cmp-mark { flex: none; font-weight: 700; text-decoration: none; display: inline-block; }
.cmp-why { margin: var(--s-2) 0 0; padding-top: var(--s-2); border-top: 1px solid var(--rule); font-size: var(--t-sm); color: var(--ink-3); }

.cmp-reals { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: var(--s-4); }
.cmp-real { padding: var(--s-4); background: var(--bg-sunk); border-radius: var(--r-lg); }
.cmp-real h3 { display: flex; align-items: baseline; justify-content: space-between; gap: var(--s-3); margin: 0 0 var(--s-3); font-size: var(--t-lg); }
.cmp-real h3 a { font-size: var(--t-xs); font-weight: 500; color: var(--accent); }
.cmp-real .ex { padding: var(--s-2) 0; border-bottom: 1px solid var(--rule); }
.cmp-real .ex:last-child { border-bottom: 0; }

/* ── Conjugation ─────────────────────────────────────────────────────────
   The table page is the densest surface on the site: sixty-odd forms, four
   speech levels, on a phone. Two decisions carry it — the speech levels are
   columns rather than rows, so a reader compares them by scanning across; and
   each group of tenses is its own small table rather than one long one, so the
   header stays within sight of the row being read.
   ───────────────────────────────────────────────────────────────────────── */

/* ── The verb list row ── */
.vrows { display: flex; flex-direction: column; gap: var(--s-2); }
.vrow {
  display: grid;
  grid-template-columns: minmax(96px, auto) 1fr auto auto auto;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-3) var(--s-4);
  background: var(--bg-raise);
  border: 1px solid var(--rule);
  border-radius: var(--r);
  transition: border-color 0.12s ease, background 0.12s ease;
}
.vrow:hover { border-color: var(--accent-line); background: var(--accent-wash); }
.vrow-word { font-size: var(--t-lg); font-weight: 600; letter-spacing: var(--tr-head); }
.vrow-gloss { font-size: var(--t-sm); color: var(--ink-2); }
.vrow-form { font-size: var(--t-sm); color: var(--accent); font-weight: 500; }
.chip-irr { background: var(--warn-bg); color: var(--warn); border-color: transparent; }

/* ── The verb page header ── */
.cj-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-3) var(--s-4);
  padding-bottom: var(--s-5);
  margin-bottom: var(--s-5);
  border-bottom: 1px solid var(--rule);
}
.cj-title { flex: 1 1 260px; }
.cj-title h1 { margin: 0; }
.cj-word { display: block; font-size: var(--t-2xl); font-weight: 700; letter-spacing: var(--tr-display); line-height: var(--lh-tight); }
.cj-h1-en {
  display: block;
  margin-top: var(--s-2);
  font-size: var(--t-xs);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: var(--tr-label);
  color: var(--ink-4);
}
.cj-gloss { margin: var(--s-2) 0 0; font-size: var(--t-md); color: var(--ink-2); }
.cj-audio { width: 40px; height: 40px; border-radius: 50%; flex: none; }

/* The principal parts — the four the dictionary itself prints. Given their own
   surface because everything below is derived from them. */
.cj-principal { margin-bottom: var(--s-7); }
.cj-parts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: var(--s-2);
  margin: 0;
}
.cj-parts > div {
  padding: var(--s-3) var(--s-4);
  background: var(--bg-sunk);
  border-radius: var(--r);
}
.cj-parts dt {
  display: flex;
  flex-direction: column;
  gap: 1px;
  font-size: var(--t-xs);
  text-transform: uppercase;
  letter-spacing: var(--tr-label);
  color: var(--ink-4);
}
.cj-parts dt span { font-size: var(--t-sm); text-transform: none; letter-spacing: normal; color: var(--ink-3); }
.cj-parts dd { margin: var(--s-2) 0 0; font-size: var(--t-lg); font-weight: 600; }

/* ── The tables ── */
.cj-group { margin-bottom: var(--s-7); }
.cj-table { width: 100%; border-collapse: collapse; font-size: var(--t-base); }
.cj-table th, .cj-table td {
  text-align: left;
  padding: var(--s-3);
  border-bottom: 1px solid var(--rule);
  white-space: nowrap;
}
.cj-table thead th {
  display: table-cell;
  border-bottom: 2px solid var(--rule-strong);
  vertical-align: bottom;
}
.cj-table thead th span { display: block; }
.cj-table thead th .ko { font-size: var(--t-sm); font-weight: 600; color: var(--ink); }
.cj-th-en { font-size: var(--t-xs); font-weight: 500; text-transform: uppercase; letter-spacing: var(--tr-label); color: var(--ink-4); }
.cj-table tbody th { font-weight: 500; color: var(--ink-2); }
.cj-table tbody th .ko { display: block; font-size: var(--t-sm); color: var(--ink); }
.cj-en { display: block; font-size: var(--t-xs); color: var(--ink-4); }
.cj-table tbody tr:hover { background: var(--bg-sunk); }

/* The first data column is 해요체, the form a learner needs most, so it is the
   one carrying the accent. Every other column is equal weight. */
.cj-table tbody td:first-of-type { color: var(--accent); font-weight: 500; }
.cj-archaic td, .cj-archaic th { color: var(--ink-4); font-size: var(--t-sm); border-bottom-style: dashed; }

/* Attributives and connectives are single forms, not a level × tense grid. */
.cj-single { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: var(--s-2); margin: 0; }
.cj-single-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-3);
  padding: var(--s-3) var(--s-4);
  background: var(--bg-sunk);
  border-radius: var(--r);
}
.cj-single-row dt { display: flex; flex-direction: column; gap: 1px; }
.cj-single-row dt .ko { font-size: var(--t-sm); color: var(--ink-3); }
.cj-single-row dd { margin: 0; font-size: var(--t-md); font-weight: 600; }

.cj-patterns { list-style: none; display: flex; flex-wrap: wrap; gap: var(--s-2); padding: 0; margin: 0; }
.cj-patterns li {
  padding: var(--s-2) var(--s-3);
  background: var(--bg-sunk);
  border-radius: var(--r-sm);
  font-size: var(--t-sm);
  font-family: var(--mono);
}
.cj-examples .ex { padding: var(--s-3) 0; border-bottom: 1px solid var(--rule); }
.cj-examples .ex:last-child { border-bottom: 0; }

/* The legend on /conjugation/ — the group notes, explained once here instead of
   repeated on 3,698 verb pages. */
.cj-legend { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: var(--s-3); margin: 0; }
.cj-legend > div { padding: var(--s-4); background: var(--bg-sunk); border-radius: var(--r-lg); }
.cj-legend dt { display: flex; flex-direction: column; gap: 1px; margin-bottom: var(--s-2); }
.cj-legend dt .ko { font-size: var(--t-md); font-weight: 600; }
.cj-legend dd { margin: 0; font-size: var(--t-sm); color: var(--ink-2); line-height: 1.65; }

/* ── The irregular class tables ── */
.irr-table, .irr-worked { width: 100%; border-collapse: collapse; font-size: var(--t-sm); margin-bottom: var(--s-5); }
.irr-table th, .irr-table td, .irr-worked th, .irr-worked td {
  text-align: left;
  padding: var(--s-3);
  border-bottom: 1px solid var(--rule);
  vertical-align: top;
}
.irr-table thead th, .irr-worked thead th {
  font-size: var(--t-xs);
  text-transform: uppercase;
  letter-spacing: var(--tr-label);
  color: var(--ink-4);
  border-bottom: 2px solid var(--rule-strong);
}
.irr-worked thead th span { text-transform: none; letter-spacing: normal; color: var(--ink-4); font-weight: 400; }
.irr-table td .ko, .irr-worked td .ko { font-size: var(--t-md); }
.irr-gloss { display: block; font-size: var(--t-xs); color: var(--ink-4); }
/* The two columns where the stem actually changes — the whole point of the
   table, so they are the only coloured thing in it. */
.irr-worked .is-change { color: var(--accent); font-weight: 600; }

/* ── /hanja/ — the character layer ───────────────────────────────────────
   Two surfaces. The chart is a dense grid of glyphs, because that is what a
   한자 chart has looked like for a very long time and a learner scanning for
   a character they half remember scans a grid, not a list. The character page
   is the opposite: one enormous glyph, then the words it builds, each with the
   character marked inside its spelling — the mark is the content, since the
   pattern is invisible in the Hangul.
   ───────────────────────────────────────────────────────────────────────── */

/* ── The chart ── */
.htiles {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(78px, 1fr));
  gap: var(--s-2);
  margin: var(--s-5) 0;
}
.htile {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: var(--s-3) var(--s-2);
  background: var(--bg-raise);
  border: 1px solid var(--rule);
  border-radius: var(--r);
  text-align: center;
  transition: border-color 0.12s ease, background 0.12s ease;
}
a.htile:hover { border-color: var(--accent-line); background: var(--accent-wash); }
.htile-char { font-size: var(--t-xl); line-height: 1.1; font-weight: 500; }
.htile-reading { font-size: var(--t-sm); font-weight: 600; color: var(--ink-2); }
.htile-n { font-size: var(--t-xs); color: var(--ink-4); font-variant-numeric: tabular-nums; }

/* Under the gate: in the chart, but with no page behind it. Muted rather than
   hidden — the chart would otherwise misreport what the dataset holds. */
.htile.is-rare { background: transparent; border-style: dashed; cursor: default; }
.htile.is-rare .htile-char { color: var(--ink-4); font-weight: 400; }
.htile.is-rare .htile-reading { color: var(--ink-4); font-weight: 400; }

.hnote {
  margin: var(--s-4) 0 0;
  font-size: var(--t-sm);
  color: var(--ink-3);
  max-width: 78ch;
}

/* ── The character page header ── */
.hj-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-4) var(--s-6);
  padding-bottom: var(--s-5);
  margin-bottom: var(--s-5);
  border-bottom: 1px solid var(--rule);
}

/* The glyph at a size you can actually see the strokes of. Sunken rather than
   raised: on this page the character is the ground the rest sits on. */
.hj-glyph {
  flex: none;
  display: grid;
  place-items: center;
  width: 132px;
  height: 132px;
  border-radius: var(--r-lg);
  background: var(--bg-sunk);
  font-size: 82px;
  line-height: 1;
  font-weight: 500;
  color: var(--ink);
}
.hj-title { flex: 1 1 280px; }
.hj-title h1 { margin: 0; }
.hj-reading {
  display: block;
  font-size: var(--t-2xl);
  font-weight: 700;
  letter-spacing: var(--tr-display);
  line-height: var(--lh-tight);
}
.hj-h1-en {
  display: block;
  margin-top: var(--s-2);
  font-size: var(--t-xs);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: var(--tr-label);
  color: var(--ink-4);
}
.hj-meaning { margin: var(--s-2) 0 0; font-size: var(--t-md); color: var(--ink-2); }

/* ── The word list ── */
/* Each row is its own grid, so its four columns would be sized by its own
   contents and the list would stagger — 학교 學校 and 학교생활 學校生活 put
   their glosses at different x. Subgrid gives the whole list one set of tracks
   so the marked characters line up down the page, which is the entire point of
   marking them. The min widths under it are the fallback, and they are close
   enough that a browser without subgrid loses alignment on long words only. */
.hwords { display: flex; flex-direction: column; gap: var(--s-2); }
@supports (grid-template-columns: subgrid) {
  .hwords { display: grid; grid-template-columns: max-content max-content 1fr max-content; }
  .hword { grid-column: 1 / -1; grid-template-columns: subgrid; }
}
.hword {
  display: grid;
  grid-template-columns: minmax(104px, auto) minmax(72px, auto) 1fr auto;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-3) var(--s-4);
  background: var(--bg-raise);
  border: 1px solid var(--rule);
  border-radius: var(--r);
  transition: border-color 0.12s ease, background 0.12s ease;
}
.hword:hover { border-color: var(--accent-line); background: var(--accent-wash); }
.hword-ko { font-size: var(--t-lg); font-weight: 600; letter-spacing: var(--tr-head); }
.hword-hanja { font-size: var(--t-base); color: var(--ink-3); }
/* The mark carries the page's meaning, so it is the strong colour rather than
   the wash the rest of the site uses for a highlight. */
.hword-hanja mark { background: transparent; color: var(--accent); font-weight: 600; box-shadow: none; padding: 0; }
.hword-gloss { font-size: var(--t-sm); color: var(--ink-2); }

/* ── Characters it combines with ── */
.hpairs {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(132px, 1fr));
  gap: var(--s-2);
}
.hpair {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: var(--s-3) var(--s-4);
  background: var(--bg-raise);
  border: 1px solid var(--rule);
  border-radius: var(--r);
}
.hpair:hover { border-color: var(--accent-line); background: var(--accent-wash); }
/* The partner character and its reading, then a word the two really build.
   Never the two glyphs stuck together: 學 and 大 share 대학, not 學大, and a
   tile that printed the pair would be inventing a word for a third of them. */
.hpair-head { display: flex; align-items: baseline; gap: var(--s-2); }
.hpair-char { font-size: var(--t-lg); font-weight: 600; }
.hpair-reading { font-size: var(--t-sm); color: var(--ink-2); font-weight: 600; }
.hpair-example { font-size: var(--t-sm); color: var(--ink-2); }
.hpair-example [lang="ko-Hani"] { color: var(--ink-4); }
.hpair-example mark { background: transparent; color: var(--accent); font-weight: 600; box-shadow: none; padding: 0; }
.hpair-n { font-size: var(--t-xs); color: var(--ink-4); font-variant-numeric: tabular-nums; }

@media (max-width: 560px) {
  .hj-glyph { width: 96px; height: 96px; font-size: 58px; }
  /* Back to a per-row grid: three columns of shared tracks at 360px would give
     the gloss whatever the widest headword left over, which is nothing. */
  .hwords { display: flex; }
  .hword { grid-column: auto; grid-template-columns: minmax(76px, auto) 1fr auto; }
  /* The spelling drops under the headword rather than off the page: it is the
     one column that cannot be cut, since the marked character is the point. */
  .hword-hanja { grid-column: 1; grid-row: 2; }
  .hword-gloss { grid-column: 2 / -1; grid-row: 2; }
}

/* ── Conversation ────────────────────────────────────────────────────────
   One page per topic per level, and each page is a stack of blocks: a word,
   then every dialogue in the dictionary that uses it.

   The block, not the exchange, is the unit here — a word's dialogues arrive
   together and are read together, and it is the block an anchor addresses, so
   the "In conversation" link on a word's row lands on the word rather than at
   the top of a list of forty. That is why the highlight below is on :target
   rather than driven from app.js the way the word rows are: nothing has to be
   opened first, so nothing needs JavaScript.

   The turn styling itself is not here. It is .dialogue / .turn in
   15-grammar.css, shared with the grammar points and the idiom rows, because
   a dialogue should not look like two different things on two pages.
   ───────────────────────────────────────────────────────────────────────── */

.xsets { display: flex; flex-direction: column; gap: var(--s-3); margin: var(--s-4) 0 var(--s-6); }

.xset {
  padding: var(--s-4);
  background: var(--bg-raise);
  border: 1px solid var(--rule);
  border-radius: var(--r-lg);
  /* The header is sticky, so an anchor jump has to stop short of it. */
  scroll-margin-top: calc(var(--header-h) + var(--s-4));
}
.xset:hover { border-color: var(--accent-line); }
.xset:target { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-wash); }

/* ── The word this set illustrates ──
   Reads as a heading and behaves as a link back to the word's row. The gloss
   sits on the same line at body size: the reader is here to read Korean, and
   the English is a reminder of a word already met, not the content. */
.xset-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  padding-bottom: var(--s-3);
  border-bottom: 1px solid var(--rule);
}
.xset-word {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--s-2) var(--s-3);
  min-width: 0;
  color: inherit;
}
.xset-word:hover .xset-ko { color: var(--accent); }
.xset-ko { font-size: var(--t-lg); font-weight: 600; letter-spacing: var(--tr-head); }
.xset-gloss { font-size: var(--t-sm); color: var(--ink-3); }

.xset-tools { display: flex; align-items: center; gap: var(--s-2); flex: none; }
.chip-pos { background: var(--bg-sunk); color: var(--ink-3); }

/* A word with two or three dialogues gets one panel per exchange. They are
   siblings under the same heading, so the first loses the top margin .dialogue
   carries for the grammar pages. */
.xset .dialogue { margin-top: var(--s-3); }

/* The count beside a level on the "same topic at other levels" strip. */
.cross-level .cross-n {
  margin-left: var(--s-2);
  font-size: var(--t-xs);
  font-weight: 400;
  color: var(--ink-4);
}

@media (max-width: 640px) {
  .xset { padding: var(--s-3); }
  .xset-head { align-items: flex-start; }
  .xset .dialogue { padding: var(--s-3); }
}

/* ── /pronunciation/ — the sound-change rules ─────────────────────────────
   Twelve pages that share a shell with /hangul/ — .jnav, .jsection, .jcard,
   .jnote and the two card grids are all defined in 12-hangul.css and used
   here unchanged. The two sections are the same kind of thing (authored
   teaching pages with derived examples in them) and a reader moving between
   them should not be able to tell where one stylesheet stops.

   What is new here is one component: the derivation. A rule page's whole
   argument is that a pronunciation is *produced* rather than looked up, so the
   arrows and the rule name on each arrow are the content, not decoration. It
   is a real <ol> — the steps are ordered and the order is the point — laid out
   as a horizontal flow that wraps on a narrow screen rather than scrolling.
   ───────────────────────────────────────────────────────────────────────── */

/* ── The derivation chain ── */

.pderiv {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-2) var(--s-1);
}
.pstep {
  display: flex;
  align-items: center;
  gap: var(--s-2);
}
.pstep-text {
  font-size: var(--t-lg);
  font-weight: 600;
  letter-spacing: var(--tr-head);
  color: var(--ink-3);
  white-space: nowrap;
}
/* The first stage is the spelling and the last is what the dictionary says.
   Both are the reader's anchors; the intermediate stages are working. */
.pstep:first-child .pstep-text { color: var(--ink); }
.pstep.is-final .pstep-text { color: var(--accent); }

.pstep-arrow {
  color: var(--ink-4);
  font-size: var(--t-sm);
}
/* The rule name rides above its own arrow, small, so the chain still reads as
   a line of Korean with labels on it rather than as a table. */
.pstep-rule {
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: var(--tr-head);
  white-space: nowrap;
}
.pstep-rule a { color: var(--ink-3); border-bottom: 1px dotted var(--rule); }
.pstep-rule a:hover { color: var(--accent); border-bottom-color: var(--accent-line); }
.pstep-rule span { color: var(--ink-4); }

.pderiv-compact .pstep-text { font-size: var(--t-base); }

/* ── A worked example: the chain plus what the word is ── */

.pworked-grid {
  display: grid;
  gap: var(--s-3);
  margin-top: var(--s-4);
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
}
.pworked {
  margin: 0;
  padding: var(--s-5);
  background: var(--bg-raise);
  border: 1px solid var(--rule);
  border-radius: var(--r-lg);
}
.pworked figcaption {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-2);
  margin-top: var(--s-4);
  padding-top: var(--s-3);
  border-top: 1px solid var(--rule);
  font-size: var(--t-sm);
}
.pworked figcaption b { font-size: var(--t-base); font-weight: 700; }
.pworked figcaption a { color: var(--ink); }
.pworked figcaption a:hover { color: var(--accent); }
.pworked-gloss { color: var(--ink-3); }

/* The hub's single showpiece derivation, at reading size rather than card
   size — it is the one thing on that page a visitor should stop at. */
.pshowcase {
  padding: var(--s-6);
  background: var(--bg-sunk);
  border-radius: var(--r-lg);
  margin-top: var(--s-4);
}
.pshowcase .pworked { border: 0; padding: 0; background: none; }
.pshowcase .pstep-text { font-size: var(--t-xl); }
.pshowcase .jnote { background: var(--bg); }

/* ── The rule, as a table ── */

.ptable-wrap { overflow-x: auto; margin-top: var(--s-4); }
.ptable {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--t-sm);
}
.ptable th,
.ptable td {
  padding: var(--s-3) var(--s-4);
  text-align: left;
  border-bottom: 1px solid var(--rule);
  vertical-align: baseline;
}
.ptable thead th {
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: var(--tr-label);
  text-transform: uppercase;
  color: var(--ink-4);
  border-bottom-color: var(--rule-strong);
}
.ptable tbody th {
  font-size: var(--t-base);
  font-weight: 600;
  letter-spacing: var(--tr-head);
  white-space: nowrap;
}
.ptable td:last-child { font-weight: 500; color: var(--ink-2); white-space: nowrap; }
.ptable tbody tr:last-child th,
.ptable tbody tr:last-child td { border-bottom: 0; }

/* ── The trap, and the method note ── */

.pnote {
  margin-top: var(--s-4);
  padding-left: var(--s-5);
  border-left: 3px solid var(--accent-line);
  max-width: 78ch;
}
.pnote p { margin: 0 0 var(--s-3); color: var(--ink-2); line-height: var(--lh-body); }
.pnote p:last-child { margin-bottom: 0; }
.pnote em { font-style: normal; font-weight: 600; color: var(--ink); }

.pmethod {
  margin: var(--s-4) 0 0;
  max-width: 78ch;
  color: var(--ink-2);
  line-height: var(--lh-body);
}
.pmethod b { color: var(--ink); }

/* ── The example list ── */

.plist-head { margin: var(--s-4) 0 var(--s-2); }

.plist {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--s-1);
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
}
/* One line, always. The gloss is the only elastic part, so it truncates and
   every row in the grid keeps the same height — a list of 150 with ragged rows
   reads as a mess rather than as a list. */
.prow {
  display: flex;
  align-items: baseline;
  gap: var(--s-2);
  padding: var(--s-2) var(--s-3);
  border-radius: var(--r-sm);
  border: 1px solid transparent;
}
.prow:hover { background: var(--bg-sunk); border-color: var(--rule); }

.prow-main {
  display: flex;
  align-items: baseline;
  gap: var(--s-2);
  color: var(--ink);
}
.prow-ko { font-size: var(--t-base); font-weight: 600; letter-spacing: var(--tr-head); }
.prow-arrow { color: var(--ink-4); font-size: var(--t-xs); }
/* The pronunciation is the answer the row exists to give, so it carries the
   accent — the spelling next to it is the question. */
.prow-pron { font-size: var(--t-base); font-weight: 600; color: var(--accent); letter-spacing: var(--tr-head); }
.prow-gloss {
  flex: 1 1 auto;
  min-width: 0;
  font-size: var(--t-sm);
  color: var(--ink-3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.prow-meta { display: flex; align-items: center; gap: var(--s-1); flex: none; }

/* The other rules that fired on the same word. Deliberately quieter than a
   .chip: it is a cross-reference, not a fact about the word. */
.pchip {
  font-size: var(--t-xs);
  color: var(--ink-4);
  padding: 1px var(--s-2);
  border: 1px solid var(--rule);
  border-radius: var(--r-pill);
  white-space: nowrap;
}
a.pchip:hover { color: var(--accent); border-color: var(--accent-line); background: var(--accent-wash); }

/* ── The two vowel conventions on the hub ── */

.pminor-clause { font-size: var(--t-xs); font-weight: 500; color: var(--ink-4); }
.pminor-ex { display: flex; flex-wrap: wrap; gap: var(--s-2) var(--s-4); }
.pminor-ex a { color: var(--ink-2); font-weight: 500; }
.pminor-ex a:hover { color: var(--accent); }
.pminor-arrow { color: var(--ink-4); }

@media (max-width: 640px) {
  .pshowcase { padding: var(--s-4); }
  .pshowcase .pstep-text { font-size: var(--t-lg); }
  .plist { grid-template-columns: 1fr; }
  /* One column is wide enough to give the gloss its own line rather than
     truncating it to nothing. */
  .prow { flex-wrap: wrap; }
  .prow-gloss { flex-basis: 100%; white-space: normal; }
}

/* ── The link back from a word row ──
   The expandable row prints "[걷] spelled 것" and now names the rule that did
   it. Styled here rather than in 10-vocab-rows.css because it only exists when
   this section does — the loader leaves `soundRules` unset without the dataset
   and the row renders exactly as it did before. */
.pron-rules {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--s-2);
  margin-top: var(--s-2);
}
.pron-rule {
  font-size: var(--t-xs);
  font-weight: 600;
  color: var(--ink-2);
  padding: 2px var(--s-2);
  border: 1px solid var(--rule);
  border-radius: var(--r-pill);
}
.pron-rule:hover { color: var(--accent); border-color: var(--accent-line); background: var(--accent-wash); }

/* ── /honorifics/ — 높임말 ─────────────────────────────────────────────────
   Three authored pages that share the teaching shell defined in
   12-hangul.css — .jnav, .jsection, .jcard, .jgrid-*, .jnote — for the same
   reason /pronunciation/ does: a reader moving between the authored sections
   should not be able to tell where one stylesheet stops.

   Two components are this section's own, and both exist because the material
   is comparative rather than expository.

   The ladder (.hon-ladder) is a six-column table of one verb, and the four
   columns worth producing are marked in the table itself rather than in a
   caption under it. Everything a learner needs from that table is "which of
   these do I say", so the answer is in the cell.

   The pair tables (.hon-pairs) put the honorific in the second column and give
   it the accent, because the left column is a word the reader already knows
   and the right column is the one being taught. The .hon-dlist below them is the
   dictionary's full list at half the weight — reference, not syllabus, and
   styled to say so.
   ───────────────────────────────────────────────────────────────────────── */

/* ── Inline links: a form, a word ── */

.hon-form, .hon-word {
  display: inline-flex;
  align-items: baseline;
  gap: var(--s-2);
}
a.hon-form, a.hon-word { color: inherit; border-bottom: 1px dotted var(--rule); }
a.hon-form:hover, a.hon-word:hover { color: var(--accent); border-bottom-color: var(--accent-line); }
.hon-word-gloss { font-size: var(--t-xs); color: var(--ink-3); }
.hon-sep { color: var(--ink-4); margin: 0 var(--s-1); }

.hon-page .jcard h3 [lang="ko"] { color: var(--accent); font-weight: 700; }
.hon-card-link { margin-top: var(--s-3) !important; font-size: var(--t-sm); }

/* ── The speech-level ladder ── */

.hon-ladder { width: 100%; border-collapse: collapse; font-size: var(--t-sm); }
.hon-ladder caption {
  caption-side: top;
  text-align: left;
  padding-bottom: var(--s-3);
  font-size: var(--t-xs);
  letter-spacing: var(--tr-label);
  text-transform: uppercase;
  color: var(--ink-4);
}
.hon-ladder th, .hon-ladder td {
  padding: var(--s-3);
  border-bottom: 1px solid var(--rule);
  text-align: left;
  white-space: nowrap;
}
.hon-ladder thead th { border-bottom: 2px solid var(--rule-strong); vertical-align: bottom; }
.hon-ladder thead th a { color: inherit; display: block; }
.hon-ladder thead th a:hover .hon-l-ko { color: var(--accent); }
.hon-l-ko { display: block; font-weight: 700; letter-spacing: var(--tr-head); }
.hon-l-en { display: block; margin-top: 2px; font-size: var(--t-xs); font-weight: 400; color: var(--ink-4); }
.hon-ladder tbody th { color: var(--ink-3); }
.hon-ladder tbody th .hon-l-ko { font-weight: 600; font-size: var(--t-xs); }
.hon-ladder td { font-size: var(--t-md); color: var(--ink-4); }
/* The four rungs in daily use carry the page's answer, so they carry the ink.
   The two archaic columns stay legible and stay quiet. */
.hon-ladder td.is-alive { color: var(--ink); font-weight: 500; background: var(--bg-sunk); }
.hon-ladder thead th.is-alive .hon-l-ko { color: var(--accent); }

/* ── One rung ── */

.hon-rungs { display: flex; flex-direction: column; gap: var(--s-5); margin-top: var(--s-4); }
.hon-rung {
  padding: var(--s-5);
  border: 1px solid var(--rule);
  border-radius: var(--r-lg);
  background: var(--bg-raise);
  scroll-margin-top: calc(var(--header-h) + var(--s-4));
}
.hon-rung.is-archaic { background: var(--bg-sunk); border-style: dashed; }
.hon-rung-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-3);
  flex-wrap: wrap;
}
.hon-rung-head h3 { margin: 0; display: flex; align-items: baseline; gap: var(--s-3); flex-wrap: wrap; }
.hon-rung-ko { font-size: var(--t-xl); letter-spacing: var(--tr-head); }
.hon-rung-label { font-size: var(--t-sm); font-weight: 500; color: var(--ink-3); }
.hon-rung-badge {
  flex: none;
  padding: 3px var(--s-3);
  border-radius: var(--r-pill);
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: var(--tr-label);
  background: var(--accent-wash);
  color: var(--accent);
}
.hon-rung.is-archaic .hon-rung-badge { background: transparent; color: var(--ink-4); border: 1px solid var(--rule); }
.hon-rung-also { margin: var(--s-1) 0 0; font-size: var(--t-sm); color: var(--ink-4); }
.hon-rung-who { margin: var(--s-3) 0 0; font-size: var(--t-sm); color: var(--ink-2); }
.hon-rung-who b { color: var(--ink); }

.hon-rung-forms { display: flex; flex-wrap: wrap; gap: var(--s-2); margin: var(--s-4) 0; }
.hon-rung-form {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: var(--s-2) var(--s-4);
  border-radius: var(--r);
  background: var(--bg-sunk);
  min-width: 96px;
}
.hon-rung.is-archaic .hon-rung-form { background: var(--bg-raise); }
.hon-rf-ko { font-size: var(--t-lg); font-weight: 600; letter-spacing: var(--tr-head); }
.hon-rf-tag { font-size: var(--t-xs); color: var(--ink-4); }

.hon-ends { list-style: none; margin: var(--s-4) 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: var(--s-2); }
.hon-ends li {
  display: flex;
  align-items: baseline;
  gap: var(--s-2);
  padding: var(--s-2) var(--s-3);
  border: 1px solid var(--rule);
  border-radius: var(--r-pill);
  font-size: var(--t-sm);
}
.hon-ends .hon-form { font-weight: 600; }
.hon-end-note { font-size: var(--t-xs); color: var(--ink-3); }

.hon-note { margin-top: var(--s-4); padding-top: var(--s-4); border-top: 1px solid var(--rule); }
.hon-note h4 {
  margin: 0 0 var(--s-2);
  font-size: var(--t-xs);
  text-transform: uppercase;
  letter-spacing: var(--tr-label);
  color: var(--ink-4);
}
.hon-note p { margin: 0 0 var(--s-2); font-size: var(--t-sm); color: var(--ink-2); line-height: 1.75; }
.hon-note p:last-child { margin-bottom: 0; }

/* ── The 요 switch ── */

.hon-switch {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: var(--s-2);
  margin-top: var(--s-4);
}
.hon-sw {
  display: flex;
  align-items: baseline;
  gap: var(--s-2);
  padding: var(--s-3) var(--s-4);
  border-radius: var(--r);
  background: var(--bg-sunk);
}
.hon-sw-casual { font-size: var(--t-md); color: var(--ink-3); }
.hon-sw-arrow { font-size: var(--t-xs); font-weight: 700; color: var(--accent); }
.hon-sw-polite { font-size: var(--t-md); font-weight: 600; color: var(--ink); }
.hon-sw-en { margin-left: auto; font-size: var(--t-xs); color: var(--ink-4); }

/* ── Grammar points behind the endings ── */

.hon-points { display: flex; flex-wrap: wrap; gap: var(--s-2); margin-top: var(--s-4); }
.hon-point {
  display: flex;
  align-items: baseline;
  gap: var(--s-2);
  padding: var(--s-2) var(--s-3);
  border: 1px solid var(--rule);
  border-radius: var(--r);
  background: var(--bg-raise);
  color: inherit;
}
.hon-point:hover { border-color: var(--accent-line); background: var(--accent-wash); }
.hon-point-form { font-size: var(--t-base); font-weight: 600; letter-spacing: var(--tr-head); }
.hon-point-roman { font-size: var(--t-xs); color: var(--ink-4); }

/* ── The -(으)시- table ──
   Generated forms, so the Korean carries the weight and the note beside it
   stays out of the way. The verb column is the anchor and is the only one
   that links out. */

.hon-si { width: 100%; border-collapse: collapse; font-size: var(--t-sm); }
.hon-si th, .hon-si td { padding: var(--s-3); border-bottom: 1px solid var(--rule); text-align: left; }
.hon-si thead th {
  font-size: var(--t-xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: var(--tr-label);
  color: var(--ink-4);
  white-space: nowrap;
}
.hon-si thead th [lang="ko"] { text-transform: none; letter-spacing: var(--tr-head); }
.hon-si tbody th { font-size: var(--t-md); font-weight: 500; white-space: nowrap; }
.hon-si tbody th a { color: var(--ink); border-bottom: 1px dotted var(--rule); }
.hon-si tbody th a:hover { color: var(--accent); border-bottom-color: var(--accent-line); }
.hon-si td { font-size: var(--t-md); color: var(--ink); white-space: nowrap; }
.hon-si td.hon-si-note { font-size: var(--t-sm); color: var(--ink-3); white-space: normal; min-width: 20ch; }

/* ── The authored pair tables ── */

.hon-pairs { width: 100%; border-collapse: collapse; font-size: var(--t-sm); }
.hon-pairs th, .hon-pairs td { padding: var(--s-3); border-bottom: 1px solid var(--rule); text-align: left; vertical-align: top; }
.hon-pairs thead th {
  font-size: var(--t-xs);
  text-transform: uppercase;
  letter-spacing: var(--tr-label);
  color: var(--ink-4);
  font-weight: 600;
}
.hon-pairs thead th [lang="ko"] { text-transform: none; letter-spacing: var(--tr-head); color: var(--ink-3); margin-right: var(--s-1); }
.hon-pairs tbody th { font-size: var(--t-md); font-weight: 500; color: var(--ink-3); white-space: nowrap; }
/* The right-hand column is the one being taught. */
.hon-pair-hon { font-size: var(--t-md); font-weight: 600; color: var(--accent); white-space: nowrap; }
.hon-pair-hon a.hon-word { color: var(--accent); border-bottom-color: var(--accent-line); }
.hon-pair-when { color: var(--ink-2); line-height: 1.7; min-width: 18ch; }

/* ── Worked examples and mistakes ── */

.hon-examples { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: var(--s-3); margin-top: var(--s-4); }
.hon-ex { padding: var(--s-4); border: 1px solid var(--rule); border-radius: var(--r-lg); background: var(--bg-raise); }
.hon-ex-ko { margin: 0; font-size: var(--t-md); font-weight: 600; line-height: var(--lh-snug); }
.hon-ex-en { margin: var(--s-2) 0; font-size: var(--t-sm); color: var(--ink-2); }
.hon-ex-note { margin: 0; padding-top: var(--s-3); border-top: 1px solid var(--rule); font-size: var(--t-xs); color: var(--ink-3); line-height: 1.7; }
.hon-ex-note b { color: var(--ink); }

.hon-obj-examples { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: var(--s-3); margin-top: var(--s-4); }
.hon-obj { padding: var(--s-4); border-radius: var(--r-lg); background: var(--bg-sunk); }
.hon-obj-ko { margin: 0 0 var(--s-2); font-size: var(--t-md); font-weight: 500; }
.hon-obj-en { margin: 0; font-size: var(--t-sm); color: var(--ink-3); }

/* Red for wrong, green for right — the same convention as the comparison
   pages in 15-grammar.css, and used for the same reason. */
.hon-mistakes { display: flex; flex-direction: column; gap: var(--s-3); margin-top: var(--s-4); }
.hon-mistake { padding: var(--s-4); border: 1px solid var(--rule); border-radius: var(--r-lg); }
.hon-bad, .hon-good { display: flex; gap: var(--s-2); align-items: baseline; margin: 0 0 var(--s-2); font-size: var(--t-md); }
.hon-bad { color: var(--bad); }
.hon-good { color: var(--ok); }
.hon-mark { flex: none; font-weight: 700; }
.hon-plain { color: var(--ink-4); font-size: var(--t-xs); font-weight: 400; }
.hon-why { margin: var(--s-2) 0 0; padding-top: var(--s-2); border-top: 1px solid var(--rule); font-size: var(--t-sm); color: var(--ink-3); line-height: 1.7; }
.hon-why b { color: var(--ink); }

/* ── The dictionary's own list ── */

.hon-dgroups { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: var(--s-5); margin-top: var(--s-4); }
.hon-dgroup { padding: var(--s-4); border: 1px solid var(--rule); border-radius: var(--r-lg); }
.hon-dgroup h3 { display: flex; align-items: baseline; gap: var(--s-2); margin: 0; font-size: var(--t-base); }
.hon-dgroup h3 [lang="ko"] { font-size: var(--t-lg); color: var(--accent); }
.hon-dcount { margin-left: auto; font-size: var(--t-xs); font-weight: 500; color: var(--ink-4); }
.hon-dblurb { margin: var(--s-2) 0 var(--s-3); font-size: var(--t-xs); color: var(--ink-3); line-height: 1.6; }
.hon-dlist { list-style: none; margin: 0; padding: 0; }
.hon-drow {
  display: flex;
  align-items: baseline;
  gap: var(--s-2);
  padding: var(--s-2) 0;
  border-top: 1px solid var(--rule);
  font-size: var(--t-sm);
}
.hon-drow-pair { display: flex; align-items: baseline; gap: var(--s-2); flex: none; }
.hon-drow-pair a { color: inherit; border-bottom: 1px dotted var(--rule); }
.hon-drow-pair a:hover { color: var(--accent); border-bottom-color: var(--accent-line); }
.hon-drow-pair a:last-child, .hon-drow-pair span:last-child { font-weight: 600; }
.hon-drow-arrow { color: var(--ink-4); font-size: var(--t-xs); }
.hon-drow-gloss {
  color: var(--ink-3);
  font-size: var(--t-xs);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.hon-drow .chip-level { margin-left: auto; flex: none; }

@media (max-width: 640px) {
  .hon-rung-ko { font-size: var(--t-lg); }
  .hon-pairs tbody th, .hon-pair-hon { white-space: normal; }
  .hon-pair-when { min-width: 0; }
  .hon-sw-en { display: none; }
}

/* ── /topik/ — the exam ───────────────────────────────────────────────────
   Seven pages on the same teaching shell as /hangul/, /pronunciation/ and
   /honorifics/ — .jnav, .jsection, .jcard, .jnote — with three components of
   their own, all of them tables of numbers because that is what this section
   is.

   The one piece that is not a table is `.tverify`, and it is deliberately the
   loudest thing on every page. This is the only section of the site whose
   source cannot be diffed against anything, so the reader is told that where
   they will see it rather than in a footnote.

   The two bar components (.tbud, .tgap) are proportion made visible: how the
   writing marks are distributed across four items, and how far apart the grade
   cut-offs are. Both are decoration over a number that is also printed, so
   losing the bar loses nothing.
   ───────────────────────────────────────────────────────────────────────── */

/* ── The line this section cannot ship without ── */

.tverify {
  margin: var(--s-8) 0 0;
  padding: var(--s-4) var(--s-5);
  border: 1px solid var(--warn-bg);
  border-left: 3px solid var(--warn);
  border-radius: var(--r);
  background: var(--warn-bg);
  color: var(--warn);
  font-size: var(--t-sm);
  line-height: 1.75;
  max-width: 78ch;
}
.tverify b { color: inherit; }
.tverify a { color: inherit; text-decoration: underline; text-underline-offset: 3px; }

.tsub {
  margin: var(--s-6) 0 var(--s-3);
  font-size: var(--t-base);
  font-weight: 600;
  color: var(--ink-2);
}
.tsub [lang="ko"] { color: var(--ink); font-weight: 700; }

/* ── Shared table furniture ── */

.tshape, .tgrades, .tlevels-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--t-sm);
}
.tshape th, .tshape td,
.tgrades th, .tgrades td,
.tlevels-table th, .tlevels-table td {
  padding: var(--s-3);
  border-bottom: 1px solid var(--rule);
  text-align: left;
  vertical-align: top;
}
.tshape thead th, .tgrades thead th, .tlevels-table thead th {
  font-size: var(--t-xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: var(--tr-label);
  color: var(--ink-4);
  white-space: nowrap;
}
.tshape tbody th, .tgrades tbody th, .tlevels-table tbody th { white-space: nowrap; }
.tshape td, .tgrades td { color: var(--ink-2); }

.tshape tbody th [lang="ko"] { font-size: var(--t-md); font-weight: 600; margin-right: var(--s-2); }
.tshape-en { font-size: var(--t-xs); color: var(--ink-4); text-transform: uppercase; letter-spacing: var(--tr-label); }
.tshape-per { color: var(--ink-4); white-space: nowrap; }
.tshape tfoot th, .tshape tfoot td {
  border-top: 2px solid var(--rule-strong);
  border-bottom: 0;
  font-weight: 700;
  color: var(--ink);
}

.tgrade-min { font-size: var(--t-md); color: var(--ink); white-space: nowrap; }
.tgrade-pct { color: var(--ink-4); white-space: nowrap; }

.tlv-name { white-space: nowrap; }
.tlv-test [lang="ko"] { white-space: nowrap; }
.tlv-score, .tlv-count { white-space: nowrap; }
.tlv-score b { color: var(--ink); }
.tlv-can { color: var(--ink-2); line-height: 1.7; min-width: 24ch; }

/* ── The 쓰기 and 말하기 items ── */

.ttasks { list-style: none; margin: var(--s-4) 0 0; padding: 0; display: flex; flex-direction: column; gap: var(--s-3); }
.ttask { padding: var(--s-4); border: 1px solid var(--rule); border-radius: var(--r-lg); background: var(--bg-raise); }
.ttask-head { display: flex; align-items: baseline; gap: var(--s-3); flex-wrap: wrap; }
.ttask-no {
  flex: none;
  min-width: 30px;
  padding: 2px var(--s-2);
  border-radius: var(--r-sm);
  background: var(--ink);
  color: var(--bg);
  font-size: var(--t-xs);
  font-weight: 700;
  text-align: center;
}
.ttask-ko { font-size: var(--t-lg); font-weight: 600; letter-spacing: var(--tr-head); }
.ttask-points { margin-left: auto; font-size: var(--t-xs); font-weight: 600; color: var(--accent); white-space: nowrap; }
.ttask-en { margin: var(--s-2) 0 0; font-size: var(--t-sm); color: var(--ink-2); }
.ttask-length { margin: var(--s-2) 0 0; font-size: var(--t-xs); color: var(--ink-3); }
.ttask-length [lang="ko"] { color: var(--ink-4); margin-right: var(--s-1); }

/* ── Level strips and level steps ── */

.tlevels { margin-top: var(--s-4); }
.tlevels-label { margin: 0 0 var(--s-2); font-size: var(--t-xs); text-transform: uppercase; letter-spacing: var(--tr-label); color: var(--ink-4); }
.tlevels-label:empty { display: none; }
.tlevel-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: var(--s-2); }
.tlevel {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: var(--s-3) var(--s-4);
  border: 1px solid var(--rule);
  border-left: 3px solid var(--lvl);
  border-radius: var(--r);
  background: var(--bg-raise);
  color: inherit;
}
.tlevel:hover { border-color: var(--accent-line); border-left-color: var(--lvl); background: var(--accent-wash); }
.tlevel-ko { font-size: var(--t-lg); font-weight: 700; letter-spacing: var(--tr-head); color: var(--lvl); }
.tlevel-name { font-size: var(--t-sm); font-weight: 500; }
.tlevel-counts { font-size: var(--t-xs); color: var(--ink-3); }

.tsteps { display: flex; flex-direction: column; gap: var(--s-3); margin-top: var(--s-4); }
.tstep {
  padding: var(--s-5);
  border: 1px solid var(--rule);
  border-left: 3px solid var(--lvl);
  border-radius: var(--r-lg);
  background: var(--bg-raise);
}
.tstep-head { display: flex; align-items: baseline; gap: var(--s-3); flex-wrap: wrap; }
.tstep-ko { font-size: var(--t-xl); font-weight: 700; letter-spacing: var(--tr-head); color: var(--lvl); }
.tstep-name { font-size: var(--t-base); font-weight: 600; }
.tstep-score { margin-left: auto; font-size: var(--t-xs); color: var(--ink-3); white-space: nowrap; }
.tstep-blurb { margin: var(--s-3) 0 0; font-size: var(--t-sm); color: var(--ink-2); line-height: 1.75; }
.tstep-can { margin: var(--s-2) 0 0; font-size: var(--t-sm); color: var(--ink-3); line-height: 1.75; }
.tstep-can b { color: var(--ink); }
.tstep-links { display: flex; flex-wrap: wrap; gap: var(--s-2) var(--s-4); margin: var(--s-3) 0 0; font-size: var(--t-sm); }
.tstep-links a { color: var(--accent); }

/* ── Strategy cards ── */

.tstrategy { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: var(--s-3); margin-top: var(--s-4); }
.tstrat { padding: var(--s-4); border-radius: var(--r-lg); background: var(--bg-sunk); }
.tstrat h3 { margin: 0 0 var(--s-2); font-size: var(--t-base); }
.tstrat p { margin: 0; font-size: var(--t-sm); color: var(--ink-2); line-height: 1.75; }

/* ── Two proportion bars ── */

.tbudget, .tgaps { display: flex; flex-direction: column; gap: var(--s-2); margin-top: var(--s-4); }
.tbud, .tgap { display: flex; align-items: center; gap: var(--s-3); font-size: var(--t-sm); }
.tbud-no {
  flex: none;
  min-width: 30px;
  padding: 2px var(--s-2);
  border-radius: var(--r-sm);
  background: var(--bg-sunk);
  font-size: var(--t-xs);
  font-weight: 700;
  text-align: center;
}
.tbud-bar, .tgap-bar { flex: 1 1 auto; height: 10px; border-radius: var(--r-pill); background: var(--bg-sunk); overflow: hidden; }
.tbud-fill { display: block; height: 100%; width: var(--share); background: var(--accent); border-radius: var(--r-pill); }
.tgap-fill { display: block; height: 100%; width: var(--pct); background: var(--lvl, var(--accent)); border-radius: var(--r-pill); }
.tbud-points { flex: none; min-width: 12ch; text-align: right; color: var(--ink-3); font-size: var(--t-xs); }
.tgap-level { flex: none; }
.tgap-num { flex: none; min-width: 10ch; text-align: right; font-weight: 600; }
.tgap-of { color: var(--ink-4); font-weight: 400; }
.tgap-step { flex: none; min-width: 18ch; color: var(--ink-4); font-size: var(--t-xs); }

/* ── Register, right and wrong ── */

.tregister { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: var(--s-3); margin-top: var(--s-4); }
.treg { position: relative; padding: var(--s-4) var(--s-4) var(--s-4) var(--s-7); border: 1px solid var(--rule); border-radius: var(--r-lg); }
.treg-mark { position: absolute; left: var(--s-4); top: var(--s-4); font-weight: 700; }
.treg-bad { border-color: color-mix(in oklab, var(--bad) 30%, var(--rule)); }
.treg-bad .treg-mark { color: var(--bad); }
.treg-good { border-color: color-mix(in oklab, var(--ok) 30%, var(--rule)); }
.treg-good .treg-mark { color: var(--ok); }
.treg p { margin: 0; font-size: var(--t-md); line-height: var(--lh-snug); }
.treg-note { margin-top: var(--s-3) !important; padding-top: var(--s-3); border-top: 1px solid var(--rule); font-size: var(--t-xs) !important; color: var(--ink-3); }

@media (max-width: 640px) {
  .tgap-step { display: none; }
  .tlv-can { min-width: 0; }
  .tstep-ko { font-size: var(--t-lg); }
}

/* ── Footer ──────────────────────────────────────────────────────────────
   One row: the mark, the sections the top nav does not carry, and the two
   display preferences. The level × section matrix that used to live here is
   gone — the home ladder and the level hubs already put every section two
   hops from the root, so the matrix was 68 links a reader never used.
   ───────────────────────────────────────────────────────────────────────── */

.site-footer {
  margin-top: var(--s-9);
  border-top: 1px solid var(--rule);
  background: var(--bg-sunk);
  padding-top: var(--s-5);
}
.footer-inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-4);
  padding-bottom: var(--s-5);
}
.footer-links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2) var(--s-4);
  flex: 1;
  min-width: 0;
}
/* A nav row, not prose: no underline, unlike a bare link in body text. */
.footer-links a { font-size: var(--t-sm); color: var(--ink-2); text-decoration: none; }
.footer-links a:hover { color: var(--accent); }
.footer-prefs { display: flex; align-items: center; gap: var(--s-2); flex: none; }

.footer-note {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-4);
  padding: var(--s-4) var(--gutter);
  border-top: 1px solid var(--rule);
  font-size: var(--t-xs);
  color: var(--ink-4);
}
.footer-note a { color: var(--ink-3); text-decoration: underline; text-underline-offset: 0.16em; }
.footer-note a:hover { color: var(--accent); }

/* ── The mobile tab bar ──
   Three items, not five: the three surfaces a reader returns to. Hidden on
   desktop, where the top bar takes over. */
.tab-bar {
  display: none;
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 50;
  height: calc(var(--tabbar-h) + env(safe-area-inset-bottom));
  padding-bottom: env(safe-area-inset-bottom);
  background: color-mix(in srgb, var(--bg) 92%, transparent);
  backdrop-filter: saturate(180%) blur(12px);
  border-top: 1px solid var(--rule);
}
.tab {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1px;
  color: var(--ink-4);
  padding-top: var(--s-1);
}
.tab svg { width: 20px; height: 20px; }
.tab-en { font-size: 10px; font-weight: 600; letter-spacing: var(--tr-label); }
.tab.is-current { color: var(--accent); }

/* ── Responsive ──────────────────────────────────────────────────────────
   Last in the cascade, so everything here wins by source order.

   The mobile breakpoint does one structural thing rather than many small
   ones: the top nav collapses into a drawer and the bottom tab bar takes over
   as the primary navigation. That is how every Korean app is laid out, and it
   is what makes the site read as one on a phone.
   ───────────────────────────────────────────────────────────────────────── */

/* No room for a rail beside the rows: the split collapses to one column and
   the word index folds into a dense strip of chips above the list. It is
   first in the source, so the single column stacks it there without moving
   anything. */
@media (max-width: 1080px) {
  .split, .split-index { grid-template-columns: minmax(0, 1fr); gap: var(--s-5); }
  .split-aside { position: static; }

  .word-index { position: static; max-height: none; }
  .wi-head { display: none; }
  .wi-list {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s-1);
    margin-top: 0;
    padding: 0 0 var(--s-4);
    overflow: visible;
  }
  /* A chip is a tap target here, not a row in a list, so it gets a target's
     height rather than a line's. */
  .wi-link {
    align-items: center;
    min-height: 32px;
    padding: 4px var(--s-3);
    border: 1px solid var(--rule);
    border-left-width: 1px;
    border-radius: var(--r-pill);
  }
  .wi-link.is-current { border-color: var(--lvl); background: none; }
  /* As a chip the entry is the word alone — a strip of forty chips is scanned,
     and a gloss on each turns it back into the list it sits above. */
  .wi-n, .wi-g { display: none; }
  .wi-w { max-width: none; }
}

@media (max-width: 900px) {
  :root { --gutter: 16px; }

  /* The top nav becomes a drawer, toggled by the hamburger. */
  .nav-toggle { display: inline-grid; }
  .main-nav {
    position: fixed;
    inset: var(--header-h) 0 0;
    z-index: 45;
    background: var(--bg);
    padding: var(--s-4) var(--gutter) calc(var(--tabbar-h) + var(--s-6));
    overflow-y: auto;
    display: none;
  }
  .main-nav.is-open { display: flex; flex-direction: column; align-items: stretch; gap: 0; }
  .nav-link {
    flex-direction: row;
    align-items: baseline;
    justify-content: flex-start;
    gap: var(--s-2);
    padding: var(--s-4) 0;
    border-bottom: 1px solid var(--rule);
    border-radius: 0;
  }
  .nav-link .nav-en { font-size: var(--t-md); }

  .tab-bar { display: flex; }
  body { padding-bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom)); }
  .site-footer { margin-bottom: var(--tabbar-h); }

  /* The search button loses its label and its shortcut hint — neither means
     anything on a device with no keyboard. Sign in loses its label the same
     way and keeps only the Google mark. It gets the label back when it has
     something to say: a bare red circle would report a failure nobody could
     read. */
  #search-trigger .btn-label, #search-trigger kbd, .auth-signin .btn-label { display: none; }
  #search-trigger { width: 34px; padding: 0; justify-content: center; }
  .auth-signin { width: 34px; padding: 0; justify-content: center; }
  .auth-signin.is-failed { width: auto; padding: 0 var(--s-3); }
  .auth-signin.is-failed .btn-label { display: inline; }

}

@media (max-width: 640px) {
  :root { --t-2xl: 2rem; --t-xl: 1.5rem; }

  main.shell { padding-block: var(--s-5) var(--s-8); }
  .hero { padding: var(--s-6) 0 var(--s-5); margin-bottom: var(--s-6); }

  /* The rail scrolls horizontally rather than squeezing seven steps into
     360px. Snap points keep it landing on whole steps. */
  .level-rail {
    grid-auto-flow: column;
    grid-auto-columns: minmax(88px, 1fr);
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .level-rail::-webkit-scrollbar { display: none; }
  .rail-step { scroll-snap-align: start; }

  .section-head { flex-direction: row; align-items: baseline; }
  .card-grid, .ladder, .feature-grid { grid-template-columns: 1fr; }

  /* The row loses its horizontal padding for the controls and puts them on
     their own line, because at 360px an 88px right gutter eats the gloss. */
  .row-head { padding: var(--s-4) var(--s-8) var(--s-4) var(--s-3); }
  .row-controls { top: auto; bottom: var(--s-3); right: var(--s-3); }
  .row-head::after { top: 20px; left: calc(100% - 24px); }
  .row-body { padding-inline: var(--s-3); }
  .rel { grid-template-columns: 1fr; gap: var(--s-1); }

  /* The progress row drops to two lines: label and tallies over the bar. At
     360px a four-column grid squeezes the bar down to nothing, which is the
     one part of the row that has to be readable. */
  .hp-row { grid-template-columns: auto 1fr auto; row-gap: var(--s-2); }
  .hp-bar { grid-column: 1 / -1; order: 2; }

  .stat-row { grid-template-columns: repeat(2, 1fr); padding: var(--s-4); }
  .footer-note { flex-direction: column; gap: var(--s-2); }

  .search-overlay { padding-top: 4vh; }
}

/* Print: strip the chrome, open every row, drop the colour. A learner who
   prints a word list wants the words. */
@media print {
  .site-header, .tab-bar, .site-footer, .search-overlay, .row-controls,
  .pagination, .level-rail, .breadcrumbs, .skip-link, .word-index { display: none !important; }
  .split, .split-index { display: block; }
  .row-body { display: block !important; }
  .row { break-inside: avoid; border-color: #ccc; box-shadow: none; }
  body { background: #fff; color: #000; }
}
