/* onjapanese — "Grammar Lab": neo-brutalist / sticker aesthetic.
   Thick black borders + hard offset shadows, cream ground with a dot-grid
   texture, orange as the primary/identity color, teal reserved for actions.
   Shared design-system layer: every page (selection + practice) pulls its
   palette/type/components from the tokens and classes below, so the
   practice screens inherit this look without being redesigned themselves. */

/* Single font family across the whole app — "M PLUS Rounded 1c" (Google
   Fonts) natively covers both Japanese and Latin, so it replaces both the
   old self-hosted Zen Kaku Gothic New (Japanese) and Space Grotesk (Latin
   display) in one family at different weights, rather than pairing two
   separate fonts. */

/* Single committed palette (no dark-mode variant) — a deliberate, confirmed
   "louder and more playful" personality choice, not an omission. */
:root {
  --bg: #f5f1e8;
  --card-header-bg: #f1ede8;
  --card-body-bg: #ffffff;
  --ink: #1a1a1a;

  --orange: #e0563a;
  --orange-text: #b0421e;
  --orange-tint: #fbe3d9;

  --teal: #0fb5a8;

  /* A LIVE MIC is not "an active button". Teal means action; recording gets
     its own accent so the state is unmistakable at arm's length (design
     principle 3). The status banner's orange "!" set the precedent for a
     second accent doing a different job. */
  --rec: #d9333f;
  --rec-tint: #fbdcdf;

  /* "Where you are NOW" — the ribbon's playhead tick and the lit mora.
     ⚠️ A separate token on purpose (Ed, 2026-08-23): these used --orange
     (#e0563a), which sits two steps from --rec (#d9333f) — and once your
     take is drawn in --rec over the band, a thin tick in near-red reads as
     one more red mark. Amber keeps the "now" family one colour while being
     unmistakable against both the recording red and the brand red-orange. */
  --now: #ff9500;

  --text-secondary: #6b6656;
  /* Darkened from the brief's literal #9A9484 / #8A8574 — both measured
     below WCAG AA contrast (~2.7-3.7:1) at the small sizes they're used at
     (11-13px labels/pills, 12px phrase count). Same muted hue, just deeper. */
  --text-muted: #6e6757;
  --text-muted-2: #79725f;

  --line: #e3ddcf;
  --radius: 10px;
  --radius-lg: 14px;

  --dot-color: #00000012;
}

* {
  box-sizing: border-box;
}

html {
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  min-height: 100vh;
  background-color: var(--bg);
  background-image: radial-gradient(circle, var(--dot-color) 1px, transparent 1px);
  background-size: 22px 22px;
  color: var(--ink);
  font-family: "M PLUS Rounded 1c", "Hiragino Sans", "Yu Gothic", sans-serif;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

.page {
  max-width: 960px;
  margin: 0 auto;
  padding: 24px 20px 64px;
}

/* --- header / wordmark --- */

.site-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 24px;
  gap: 12px;
  flex-wrap: wrap;
}

/* The menu, up from the footer (Ed, 2026-08-29). Same uppercase-tracked
   furniture style the footer used, a step larger for a header. The current
   page reads in ink and doesn't invite a pointless click. On a phone the
   header's flex-wrap drops the nav to its own line under the wordmark. */
.site-nav { display: flex; gap: 20px; }
.site-nav a {
  font-size: 0.74rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-secondary);
  text-decoration: none;
  padding: 4px 0;
}
.site-nav a:hover { color: var(--ink); }
.site-nav a[aria-current] { color: var(--ink); border-bottom: 2px solid var(--orange); }

.wordmark {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  text-decoration: none;
  color: var(--ink);
}

/* The mark. 40px because the step and the dot stop being legible below
   about 32 — verified against real renders at 24/32/40 (2026-08-24). The
   old 18px square lived here; it had no detail to lose. */
.wordmark-mark {
  width: 40px;
  height: 40px;
  flex: none;
  display: block;
}



/* One word, no tracking, set close to the mark — the pattern nearly every
   two-word logo uses (Bitbucket, Geckoboard, allplants): the capital does
   the separating, and colour stays in the mark rather than tinting a word.
   17px is where the cap height sits inside the 40px square so the mark
   leads; 18 competes with it, 16 reads as a caption. */
.wordmark-text {
  font-size: 17px;
  font-weight: 900;
  letter-spacing: -0.015em;
}

.site-tagline {
  color: var(--text-muted);
  font-size: 0.8rem;
  margin: 0;
  font-weight: 500;
}

.card {
  background: var(--card-body-bg);
  border: 3px solid var(--ink);
  border-radius: var(--radius-lg);
  box-shadow: 6px 6px 0 var(--ink);
  padding: 20px 22px;
}

.card + .card {
  margin-top: 22px;
}

/* --- search --- */

.search-box {
  width: 100%;
  padding: 13px 18px;
  border-radius: var(--radius);
  border: 2.5px solid var(--ink);
  background: var(--card-body-bg);
  color: var(--ink);
  font-size: 0.9rem;
  font-weight: 500;
  font-family: inherit;
  letter-spacing: 0.02em;
  margin-bottom: 20px;
}

.search-box::placeholder {
  color: var(--text-muted);
  text-transform: uppercase;
}

.search-box:focus {
  outline: none;
  box-shadow: 3px 3px 0 var(--ink);
}

/* --- pill filters --- */

.filter-label {
  display: block;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-weight: 700;
  color: var(--text-muted);
  margin-bottom: 8px;
}

.pill-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 18px;
}

.pill {
  appearance: none;
  border-radius: 999px;
  background: var(--card-body-bg);
  color: var(--text-muted);
  font-family: inherit;
  font-size: 0.78rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  padding: 8px 16px;
  cursor: pointer;
  white-space: nowrap;
}

/* level pills: bold/solid when active — a mutually-exclusive primary choice */
.pill-level {
  border: 2px solid var(--ink);
}
.pill-level.is-active {
  background: var(--orange);
  color: #fff;
}

/* category pills: soft tint/outline when active — secondary, multi-select */
.pill-category {
  border: 1.5px solid var(--line);
}
.pill-category.is-active {
  background: var(--orange-tint);
  color: var(--orange-text);
  border: 2px solid var(--orange);
}

/* --- grammar point card grid --- */

.point-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 32px 18px;
  margin-top: 28px;
}

.point-card {
  position: relative;
  border-radius: var(--radius-lg);
  text-decoration: none;
  color: inherit;
  transition: transform 0.08s ease, box-shadow 0.08s ease;
}

.point-card:active {
  transform: translate(3px, 3px);
}
.point-card:active .point-card-inner {
  box-shadow: 3px 3px 0 var(--ink);
}

/* The badge overlaps outside the card's own box (see .point-card-badge) —
   it must NOT be inside the overflow:hidden element that clips the header's
   rounded corners, or its overlapping half gets clipped off. This inner
   wrapper carries the border/shadow/clipping; .point-card itself stays a
   plain positioning context so the badge (a sibling of this wrapper) can
   escape the box freely. */
.point-card-inner {
  display: flex;
  flex-direction: column;
  background: var(--card-body-bg);
  border: 3px solid var(--ink);
  border-radius: var(--radius-lg);
  box-shadow: 6px 6px 0 var(--ink);
  overflow: hidden;
  height: 100%;
}

.point-card-badge {
  position: absolute;
  top: -16px;
  left: -16px;
  z-index: 2;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: var(--orange);
  border: 2.5px solid var(--ink);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  font-size: 0.72rem;
  box-shadow: 3px 3px 0 var(--ink);
}

.point-card-header {
  background-color: var(--card-header-bg);
  background-image: radial-gradient(circle, var(--dot-color) 1px, transparent 1px);
  background-size: 9px 9px;
  border-bottom: 3px solid var(--ink);
  min-height: 128px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 28px 16px 22px;
}

.point-card-pattern {
  font-weight: 900;
  color: var(--ink);
  text-align: center;
  line-height: 1.15;
  font-size: clamp(28px, 8vw, 44px);
  white-space: nowrap;
}

.point-card-pattern.is-long {
  font-size: clamp(19px, 5vw, 28px);
  white-space: normal;
}

.point-card-body {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 16px 18px 18px;
  flex: 1;
}

.point-card-category {
  text-transform: uppercase;
  font-size: 11px;
  letter-spacing: 0.06em;
  color: var(--text-muted);
  font-weight: 700;
}

.point-card-gloss {
  align-self: flex-start;
  background: var(--orange);
  color: #fff;
  font-weight: 700;
  font-size: 13px;
  padding: 5px 13px;
  border-radius: 999px;
}

.point-card-description {
  color: #444;
  font-size: 15px;
  font-weight: 400;
  line-height: 1.6;
}

.point-card-count {
  color: var(--text-muted-2);
  font-size: 12px;
  font-weight: 500;
  margin-top: auto;
  padding-top: 10px;
}

.point-card-cta {
  align-self: stretch;
  margin-top: 10px;
}

.empty-note {
  color: var(--text-muted);
  font-size: 0.9rem;
  text-align: center;
  padding: 24px 0;
}

/* --- buttons --- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  border-radius: var(--radius);
  padding: 12px 20px;
  font-family: inherit;
  font-size: 0.85rem;
  font-weight: 900;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  cursor: pointer;
  text-decoration: none;
  white-space: nowrap;
  transition: transform 0.08s ease, box-shadow 0.08s ease;
}

.btn:active {
  transform: translate(2px, 2px);
  box-shadow: 1px 1px 0 var(--ink) !important;
}

.btn-primary {
  background: var(--teal);
  color: #fff;
  border: 2.5px solid var(--ink);
  box-shadow: 3px 3px 0 var(--ink);
}

.btn-secondary {
  background: var(--card-body-bg);
  color: var(--ink);
  border: 2.5px solid var(--ink);
  box-shadow: 3px 3px 0 var(--ink);
}

.btn:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

/* --- the control cluster (design §5, mockup v5, Ed's sketch) ---
   [↺ Replay] [▶/⏸ Listen] [🎤 Record] [🗑 Reset] — symbols on the controls,
   words under them, straddling the stage's lower edge. Two heroes (74px
   discs, hard shadow) centred, one small helper (40px) each side. Colour
   carries meaning and nothing else: teal = playback (native), the peach
   tint with an orange mic = your voice, waiting; RED = live, and only live.
   ⛔ Nothing at rest may be red. */

.cluster {
  display: flex;
  align-items: flex-start;
  justify-content: center;
  gap: 14px;
}

.ctl {
  appearance: none;
  background: none;
  border: 0;
  padding: 0;
  margin: 0;
  font-family: inherit;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  cursor: pointer;
  color: var(--ink);
  -webkit-tap-highlight-color: transparent;
}

.ctl .lbl {
  font-size: 0.6rem;
  font-weight: 900;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--ink);
  white-space: nowrap;
}

.ctl.small .lbl {
  font-size: 0.55rem;
  color: var(--text-secondary);
}

.ctl .lbl.is-live { color: var(--rec); }

/* Quiet = present but not the thing to do now. It keeps its seat, so the
   eye learns where it lives (the "never saw Discard" lesson). */
.ctl.is-quiet { opacity: 0.35; }
.ctl:disabled { cursor: default; }

.disc-b {
  position: relative;
  width: 74px;
  height: 74px;
  border-radius: 50%;
  border: 3px solid var(--ink);
  box-shadow: 4px 4px 0 var(--ink);
  background: var(--card-body-bg);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: transform 0.08s ease, background 0.15s ease, color 0.15s ease, box-shadow 0.08s ease;
}

.disc-b svg { width: 33px; height: 33px; }

.ctl:not(:disabled):active .disc-b {
  transform: translate(2px, 2px);
  box-shadow: 2px 2px 0 var(--ink);
}

.disc-b.teal { background: var(--teal); color: #fff; }

/* At rest: alive, waiting, and visibly not the same kind of thing as
   play. Tried and rejected: ink on white (dead), teal tint (same family
   as play), rose (already red), full orange (fights play). */
.disc-b.mic { background: var(--orange-tint); color: var(--orange); }

.disc-b.mic.is-live {
  background: var(--rec);
  color: #fff;
  box-shadow: 4px 4px 0 var(--ink), 0 0 0 10px rgba(217, 51, 63, 0.16);
}

/* The ring exists only while live, and it moves — recording is
   unmistakable on the control itself, at arm's length. */
.ring {
  position: absolute;
  inset: 7px;
  border-radius: 50%;
  border: 2.5px dotted #fff;
  opacity: 0.9;
  animation: ring-spin 2.4s linear infinite;
  pointer-events: none;
}

@keyframes ring-spin { to { transform: rotate(360deg); } }

.disc-s {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  border: 2px solid var(--ink);
  box-shadow: 2px 2px 0 var(--ink);
  background: var(--card-body-bg);
  color: var(--ink);
  display: flex;
  align-items: center;
  justify-content: center;
  margin-top: 14px;   /* helpers sit lower, so the discs' centres line up */
  transition: transform 0.08s ease, box-shadow 0.08s ease;
}

.disc-s svg { width: 18px; height: 18px; }

.ctl:not(:disabled):active .disc-s {
  transform: translate(1px, 1px);
  box-shadow: 1px 1px 0 var(--ink);
}

/* The comparison pill: worded, two lines, under the cluster. */
.cmp-btn {
  appearance: none;
  font-family: inherit;
  display: inline-flex;
  align-items: center;
  gap: 9px;
  color: var(--ink);
  background: var(--card-body-bg);
  border: 2px solid var(--ink);
  border-radius: 999px;
  padding: 7px 16px 7px 13px;
  box-shadow: 2px 2px 0 var(--ink);
  cursor: pointer;
  text-align: left;
}

.cmp-btn:active {
  transform: translate(1px, 1px);
  box-shadow: 1px 1px 0 var(--ink);
}

.cmp-btn svg { width: 16px; height: 16px; flex: none; }

.cmp-txt {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  line-height: 1.15;
}

.cmp-main {
  font-size: 0.68rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.cmp-sub {
  font-size: 0.6rem;
  font-weight: 700;
  color: var(--text-secondary);
}

/* --- practice screen --- */

.explanation {
  color: var(--text-secondary);
  font-size: 0.95rem;
}

/* The explanation is light markdown rendered to real elements (see
   app/templating.py). Before 2026-08-20 it was one escaped string, so every
   paragraph ran together and the ## and ** showed on screen. */
.explanation p,
.explanation ul,
.explanation ol {
  margin: 0 0 0.75em;
}
.explanation > *:last-child { margin-bottom: 0; }
.explanation strong { color: var(--text-primary); font-weight: 700; }
.explanation em { font-style: italic; }
.explanation h3 {
  font-size: 0.95rem;
  font-weight: 700;
  color: var(--text-primary);
  margin: 1.1em 0 0.45em;
}
.explanation h3:first-child { margin-top: 0; }
.explanation ul,
.explanation ol { padding-left: 1.35em; }
.explanation li { margin-bottom: 0.3em; }

.jp-text {
  font-size: 1.7rem;
  line-height: 2.6;
  font-weight: 500;
  /* Japanese has no spaces to guide wrapping, so a browser will happily
     leave a lone た。 on its own line (Ed, 2026-08-17, on the phone).
     `balance` picks break points that even the lines out, which removes
     the one-character orphan in almost every case; `strict` applies the
     full kinsoku rules. `word-break: auto-phrase` is the proper fix — the
     browser breaks at 文節 (phrase) boundaries, BudouX under the hood —
     Chromium 119+ only; elsewhere it is ignored and balance/strict still
     apply. No library to vendor. */
  text-wrap: balance;
  line-break: strict;
  word-break: auto-phrase;
}

.jp-text ruby rt {
  font-size: 0.5em;
  font-weight: 400;
  color: var(--text-secondary);
  ruby-align: center;
}

.translation {
  margin-top: 14px;
  color: var(--text-secondary);
  font-size: 1rem;
}

.controls-row {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  flex-wrap: wrap;
  margin-top: 18px;
}

.mic-warning {
  background: var(--orange-tint);
  color: var(--orange-text);
  border: 2px solid var(--orange);
  border-radius: var(--radius);
  padding: 12px 16px;
  font-size: 0.9rem;
  margin-top: 14px;
}

/* =====================================================================
   Three levels: JLPT Grammar → Pattern → Practice
   (practice-screen-DESIGN.md §5b; execution plan chunk 1)
   ===================================================================== */

/* --- page headings --- */

/* 52px is the mock's desktop size, but the Japanese titles set the floor:
   「このサイトについて」 is ~8.77px of ink per font px, and Japanese breaks
   between ANY two characters, so there is no safe wrap. Breakpoints can't
   fix this cleanly: the constraint is continuous, so every fixed step
   leaves a width where it fails. clamp() tracks it — but the FLOOR is the
   part that can still break: one line needs font ≤ (vw−40)/8.77, so a
   30px floor failed below a 303px viewport, and 280px is a real width
   (the Galaxy Fold's cover screen — caught live on prod, 2026-08-29,
   after a check that only went down to 320). 1.625rem holds to 268px. */
.page-title {
  font-size: clamp(1.625rem, 7.5vw, 3.25rem);   /* 26px … 52px */
  font-weight: 900;
  letter-spacing: -0.025em;
  line-height: 1.02;
  margin: 0 0 22px;
}
/* On the Japanese page the same rule reads oversized — 「このサイトについて」
   is nine characters where "About" is five letters, so at 52px it fills the
   column instead of heading it. Ed's native reader called it too big
   (2026-08-29), confirming what the width alone suggested: the 52px ceiling
   was designed on English titles. Same floor and slope, lower ceiling; the
   selector keys on the page's lang so any future Japanese page inherits it. */
.about[lang="ja"] .page-title {
  font-size: clamp(1.625rem, 7.5vw, 2.25rem);   /* 26px … 36px */
}

.page-subtitle {
  font-size: 0.8rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted);
  margin: 0 0 20px;
}

/* --- the bar every room and page wears: one exit, and your position ---
   Navigation lives here, deliberately away from the action controls; an
   exit names the LEVEL it returns to, never the item (a pattern called
   〜としたら／〜にしたら would break a fixed control). */

.room-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 16px;
}

.exit {
  display: inline-flex;
  align-items: center;
  white-space: nowrap;
  font-size: 0.7rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  text-decoration: none;
  color: var(--ink);
  background: var(--card-body-bg);
  border: 2px solid var(--ink);
  border-radius: 999px;
  padding: 6px 14px;
  box-shadow: 2px 2px 0 var(--ink);
}

.exit:active {
  transform: translate(2px, 2px);
  box-shadow: none;
}

.room-tag {
  font-size: 0.65rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted);
  border: 1.5px solid var(--line);
  border-radius: 999px;
  padding: 5px 11px;
  white-space: nowrap;
}

.posnav {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  flex: none;
}

.chev {
  appearance: none;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  border: 2px solid var(--ink);
  background: var(--card-body-bg);
  color: var(--ink);
  font-family: inherit;
  font-size: 1.1rem;
  font-weight: 900;
  line-height: 1;
  cursor: pointer;
  box-shadow: 2px 2px 0 var(--ink);
}

.chev:active {
  transform: translate(2px, 2px);
  box-shadow: none;
}

/* Dimmed at the ends of the set — still tappable, simply does nothing.
   (Principle 2: a visible control is never a trap.) */
.chev.is-dim {
  opacity: 0.35;
  box-shadow: none;
}

.pos {
  font-size: 0.78rem;
  font-weight: 800;
  color: var(--text-secondary);
  min-width: 48px;
  text-align: center;
  white-space: nowrap;
}

/* --- Pattern page --- */

.pattern-name {
  font-size: 1.9rem;
  font-weight: 900;
  margin: 0 0 2px;
  line-height: 1.25;
}

.pattern-reading {
  font-size: 0.85rem;
  color: var(--text-muted);
  font-weight: 700;
  margin: 0 0 12px;
}

.pattern-gloss {
  display: inline-block;
  background: var(--orange);
  color: #fff;
  font-weight: 700;
  font-size: 13px;
  padding: 5px 13px;
  border-radius: 999px;
  margin-bottom: 12px;
}

.pattern-actions {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  margin: 22px 0 26px;
}

.btn-wide {
  width: 100%;
  max-width: 420px;
}

.linkish {
  font-size: 0.8rem;
  font-weight: 700;
  color: var(--text-muted);
  text-decoration: underline;
  text-underline-offset: 3px;
}

.list-heading {
  font-size: 0.7rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-muted);
  margin: 0 0 8px;
}

/* --- the phrase list (Pattern page) --- */

.phrase-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

/* Rows must LOOK tappable — the first real run asked "are these
   clickable? can I skip to any one?" (Ed, 2026-08-17). They are: a
   bordered row on the card background, with a › to say so. */
.phrase-list li {
  margin-bottom: 8px;
}

.phrase-item {
  appearance: none;
  width: 100%;
  display: flex;
  align-items: center;
  gap: 12px;
  text-align: left;
  background: var(--card-body-bg);
  border: 2px solid var(--line);
  border-radius: var(--radius);
  padding: 11px 12px;
  font-family: inherit;
  font-size: 0.95rem;
  color: var(--text-secondary);
  text-decoration: none;
  cursor: pointer;
}

.phrase-item:active {
  transform: translate(1px, 1px);
  background: var(--card-header-bg);
}

.phrase-go {
  margin-left: auto;
  flex: none;
  font-weight: 900;
  font-size: 1.05rem;
  line-height: 1;
  color: var(--text-muted);
}

.list-hint {
  font-size: 0.8rem;
  color: var(--text-muted);
  margin: 0 0 12px;
}

.phrase-num {
  flex: none;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  border: 1.5px solid var(--line);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.65rem;
  font-weight: 900;
  color: var(--text-muted);
}

.phrase-item.is-done .phrase-num {
  background: var(--teal);
  border-color: var(--ink);
  color: #fff;
}

.phrase-item.is-current {
  background: var(--orange-tint);
  color: var(--ink);
  font-weight: 700;
}

.phrase-item.is-current .phrase-num {
  background: var(--orange);
  border-color: var(--ink);
  color: #fff;
}

.phrase-text {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* --- the voice chip and its sheet (voice-DESIGN §7, placement A) ---
   Room furniture, not a practice control: it sits in the room's top bar
   with the exit and the position, never in the cluster. Quiet when closed
   — the room's job is the phrase, not the settings. */

.voice-chip {
  appearance: none;
  font-family: inherit;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 0.68rem;
  font-weight: 900;
  letter-spacing: 0.04em;
  color: var(--ink);
  background: var(--card-body-bg);
  border: 2px solid var(--ink);
  border-radius: 999px;
  padding: 3px 10px 3px 3px;
  box-shadow: 2px 2px 0 var(--ink);
  cursor: pointer;
}

.voice-chip:active { transform: translate(1px, 1px); box-shadow: 1px 1px 0 var(--ink); }
.voice-chip img { border-radius: 50%; border: 1.5px solid var(--ink); display: block; }
.voice-chip .caret { color: var(--text-muted); font-size: 0.6rem; }

/* The Voice row sits under Speed with the same label treatment; the chip
   carries the ink border and shadow because it OPENS something, which the
   speed pills don't. (An earlier top-bar placement had to drop the name on
   narrow phones; here there is room for it everywhere.) */
.voice-row { margin-top: 8px; }

.voice-sheet {
  border: 2.5px solid var(--ink);
  border-radius: var(--radius);
  background: var(--card-body-bg);
  box-shadow: 4px 4px 0 var(--ink);
  padding: 12px;
  margin: 12px auto 4px;
  max-width: 340px;
  text-align: left;
}

.voice-sheet h4 {
  margin: 0 0 6px;
  font-size: 0.8rem;
  letter-spacing: 0.03em;
}

.voice-sheet > p {
  margin: 0 0 8px;
  font-size: 0.7rem;
  color: var(--text-secondary);
}

.voice-row-item {
  appearance: none;
  font-family: inherit;
  width: 100%;
  display: flex;
  align-items: center;
  gap: 10px;
  text-align: left;
  background: none;
  border: 0;
  border-radius: 10px;
  padding: 8px;
  cursor: pointer;
  color: var(--ink);
}

.voice-row-item:active { background: var(--card-header-bg); }
.voice-row-item img { border-radius: 50%; border: 2px solid var(--ink); display: block; flex: none; }

.voice-name {
  display: flex;
  flex-direction: column;
  line-height: 1.2;
  font-size: 0.9rem;
  font-weight: 800;
}

.voice-name small {
  font-size: 0.68rem;
  font-weight: 600;
  color: var(--text-muted);
}

.voice-row-item.is-on { background: var(--orange-tint); }
.voice-tick { margin-left: auto; color: var(--orange-text); font-weight: 900; }

[x-cloak] { display: none !important; }

/* --- the strip: CONTEXT, not a truncated list (mockup v5, strip A) ---
   "Nearby" with rows tagged Previous · Current · Next — no numbers, no
   chevrons; the room's own ‹ › move you and the Pattern page keeps the full
   numbered list one tap away. Must look different from that list. */

.strip {
  margin-top: 20px;
  border-top: 2px solid var(--line);
  padding-top: 10px;
}

.strip-h {
  display: block;
  font-size: 0.6rem;
  font-weight: 900;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--text-muted);
  margin: 0 0 6px 2px;
}

.strip-rows {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.strip-row {
  appearance: none;
  width: 100%;
  display: flex;
  align-items: center;
  gap: 10px;
  text-align: left;
  background: none;
  border: 0;
  border-radius: 8px;
  padding: 8px 10px;
  font-family: inherit;
  font-size: 0.85rem;
  color: var(--text-secondary);
  cursor: pointer;
}

.strip-row:not(:disabled):active { background: var(--card-header-bg); }

.strip-row .tag {
  flex: none;
  width: 60px;
  font-size: 0.55rem;
  font-weight: 900;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-muted);
}

.strip-row .txt {
  flex: 1;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.strip-row .tick {
  flex: none;
  font-size: 0.7rem;
  font-weight: 900;
  color: var(--teal);
}

.strip-row.is-now {
  background: var(--orange-tint);
  color: var(--ink);
  font-weight: 700;
  cursor: default;
}

.strip-row.is-now .tag { color: var(--orange-text); }

/* --- the practice room (chunk 2) --- */

.room-card {
  text-align: center;
}

.jp-hidden {
  color: var(--line);
  letter-spacing: 0.3em;
  user-select: none;
}

/* Toggles: the label names the thing, the eye shows its STATE (open =
   shown), and the fill agrees. ⛔ Never a flipping verb ("Hide translation"
   reads both as "it is hidden" and "press to hide"). */
.eye-row {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 6px;
  margin: 14px 0 18px;
}

.eye-chip {
  appearance: none;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  white-space: nowrap;
  font-family: inherit;
  font-size: 0.68rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 7px 12px;
  border-radius: 999px;
  border: 1.5px solid var(--line);
  background: var(--card-body-bg);
  color: var(--text-muted);
  cursor: pointer;
}

.eye-chip.is-on {
  background: var(--orange-tint);
  color: var(--orange-text);
  border: 2px solid var(--orange);
}

.eye-chip svg {
  width: 14px;
  height: 14px;
  display: block;
}

/* Speed sits with Listen: you change it BECAUSE you're about to listen
   again — repeat-listen is the core loop, not a step (C1). */
.speed-row {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: 6px;
  margin: 14px 0 4px;
}

.chip-speed {
  appearance: none;
  font-family: inherit;
  font-size: 0.7rem;
  font-weight: 800;
  padding: 5px 12px;
  border-radius: 999px;
  border: 1.5px solid var(--line);
  background: var(--card-body-bg);
  color: var(--text-muted);
  cursor: pointer;
}

.chip-speed.is-on {
  background: var(--orange-tint);
  color: var(--orange-text);
  border: 2px solid var(--orange);
}

.row-label {
  font-size: 0.62rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-muted);
  align-self: center;
  margin-right: 2px;
}

.status-line {
  text-align: center;
  font-size: 0.82rem;
  font-weight: 700;
  color: var(--text-secondary);
  min-height: 22px;
  margin: 12px 0 0;
}

.status-line.is-live {
  color: var(--rec);
}

.under-row {
  display: flex;
  justify-content: center;
  margin-top: 12px;
}

.audio-warning {
  margin: 14px 0 0;
  padding: 10px 12px;
  border-radius: var(--radius);
  background: var(--rec-tint);
  border: 2px solid var(--rec);
  color: var(--ink);
  font-size: 0.8rem;
  font-weight: 600;
}

/* --- the stage: one band, three moments (design §6.4b) --- */

.stage {
  position: relative;
  border: 2.5px solid var(--ink);
  border-radius: var(--radius);
  background: var(--card-body-bg);
  box-shadow: 3px 3px 0 var(--ink);
  padding: 8px 10px 26px;
  margin: 6px 0 0;
}

.stage-canvas {
  display: block;
  width: 100%;
  height: 84px;
}

.stage-label {
  position: absolute;
  top: 7px;
  font-size: 0.55rem;
  font-weight: 900;
  text-transform: uppercase;
  letter-spacing: 0.1em;
}

.stage-label-native { left: 11px; color: var(--teal); }
.stage-label-you    { left: 62px; color: var(--rec); }
/* Pushed to the far end of the flex row — the labels sit static inside
   .ribbon-meta, so `right:` would resolve against the wrong box and land it
   beside the voice name (it did). */
.stage-label-help {
  margin-left: auto;
  color: var(--text-muted);
  text-decoration: underline dotted;
  text-underline-offset: 3px;
  letter-spacing: 0.06em;
  text-transform: none;
  font-weight: 700;
}
.stage-label-help:hover { color: var(--orange-text); }

/* The controls straddle the band's lower edge — they belong to it. */
.controls-on-stage {
  margin-top: -30px;
  position: relative;
  z-index: 2;
}

@media (max-width: 480px) {
  .stage-canvas { height: 72px; }
}

/* --- progress on the door (JLPT Grammar cards) --- */

.card-progress {
  display: flex;
  flex-direction: column;
  gap: 7px;
  margin-top: auto;
  padding-top: 12px;
  font-size: 0.72rem;
  font-weight: 800;
  color: var(--text-secondary);
}

.card-progress-label {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}

.card-progress-arrow {
  color: var(--orange-text);
  font-size: 0.7rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  white-space: nowrap;
}

.card-progress-bar {
  height: 9px;
  border: 2px solid var(--ink);
  border-radius: 999px;
  background: var(--card-body-bg);
  overflow: hidden;
}

.card-progress-bar i {
  display: block;
  height: 100%;
  background: var(--teal);
}

@media (max-width: 480px) {
  .jp-text { font-size: 1.4rem; line-height: 2.4; }
  .disc-b { width: 68px; height: 68px; }
  .disc-b svg { width: 30px; height: 30px; }
  .card { box-shadow: 4px 4px 0 var(--ink); }
  .point-card-inner { box-shadow: 4px 4px 0 var(--ink); }
  .pattern-name { font-size: 1.6rem; }
  .phrase-item { font-size: 0.9rem; }
}


/* --- About page and the footer link ---------------------------------- */

.site-footer {
  margin-top: 40px;
  padding-top: 14px;
  border-top: 2px solid var(--line);
  text-align: center;
}

/* Demoted from the top of every page, but it still has to be SEEN (Ed):
   muted grey type in a footer is functionally invisible. So it keeps the
   sticker treatment the old banner had — ink border, hard shadow, the "!"
   mark — just smaller, and down here rather than ahead of the headline. */
.site-note {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  margin: 16px auto 0;
  padding: 9px 15px;
  background: var(--orange-tint);
  border: 2.5px solid var(--ink);
  border-radius: var(--radius);
  box-shadow: 3px 3px 0 var(--ink);
  font-size: 0.8rem;
  font-weight: 500;
  color: var(--ink);
  text-align: left;
}
.site-note b { font-weight: 800; }
.site-note-mark {
  flex: none;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--orange);
  border: 2px solid var(--ink);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.85rem;
  font-weight: 900;
  line-height: 1;
}
/* The footer's link row moved into the header as .site-nav (2026-08-29);
   the footer keeps only the note sticker. */

/* Same width as every other page's content — the header and banner span
   the page column, so a narrower About column hung left of them on
   desktop (Ed, 2026-08-18). Prose measure is kept readable inside cards. */
/* ==========================================================================
   Inner-page typography — About, and the system the Pitch and Guide pages
   will follow. Built from the design mock (design_handoff_inner_pages),
   measured rather than transcribed: the handoff's summary table and its own
   mock disagreed on the two headline sizes, and the mock is the artefact
   that was actually reviewed.

   ⭐ THE RULE, and it is the whole system:
   **Prose flows on the page ground. Ink borders are reserved for elements
   that are playable, illustrative, or alerting.**
   That is what makes a border informative again. Before this, prose lived in
   bordered cards on About while the Pitch page needed those same cards for
   playable examples — so the border meant nothing. On this page exactly two
   things keep ink now: the footer's alert sticker, and the back button.

   ⚠️ What was actually wrong, and it was not a matter of taste: `.about-h2`
   was 0.78rem, uppercase, in --text-muted, while the body it introduced was
   0.86rem in --text-secondary. Section headings were the SMALLEST and PALEST
   text on the page. Survivable across four sections; on the ten-section Pitch
   page there was nothing to skim by at all.
   ========================================================================== */

/* 660px, centred. ⚠️ Load-bearing, not cosmetic: the type scale below is
   sized for this measure (~69ch at 17px). `max-width: none` was here because
   a narrower column hung left of the header — centring is the fix, not
   widening the text. */
.about {
  max-width: 660px;
  margin-inline: auto;
}

.about-lede {
  font-size: 1.28rem;      /* 20.5px */
  line-height: 1.6;
  color: var(--ink);
  margin: 0 0 18px;
}

/* The site's name, inline in prose: the wordmark's own weight and tracking
   at the surrounding text size. In the Japanese lede this replaced 「」 —
   the brackets were doing the "this is a name" work the font now does
   (Ed's reader, 2026-08-29; CamelCase alone reads as words to a Japanese
   eye, and bracketed Latin reads as apparatus). */
.brand {
  font-weight: 900;
  letter-spacing: -0.015em;
}
.about-note {
  font-size: 1.0625rem;    /* 17px */
  line-height: 1.68;
  color: var(--ink);
  margin: 0 0 18px;
}
/* Small print — licence terms, caveats. The only prose that stays muted. */
.about-fine {
  font-size: 0.906rem;     /* 14.5px */
  line-height: 1.6;
  color: var(--text-muted);
  margin: 0 0 18px;
}

/* Sections announce themselves. The hairline is a real <hr class="rule">
   rather than a border-top on the heading — the handoff left this open, and
   a separate element is what the mock does: a border fights margin-top, and
   collapsing margins make the gap above unpredictable. */
.rule {
  border: 0;
  height: 1px;
  background: var(--line);
  margin: 52px 0 0;
}
.about-h2 {
  font-size: 1.69rem;      /* 27px */
  font-weight: 900;
  letter-spacing: -0.015em;
  line-height: 1.2;
  color: var(--ink);
  text-transform: none;
  margin: 14px 0 16px;
}


.voice-credits {
  list-style: none;
  margin: 0 0 18px;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: 28px;
  row-gap: 4px;
}

/* A credit is a fact, not a component — a hairline is enough to separate
   them. The bordered-card version cost four full-width boxes for four short
   lines, and spent the ink border on something that does nothing. */
.voice-credit {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 0;
  border-bottom: 1px solid var(--line);
}

.voice-credit img { border-radius: 50%; border: 2px solid var(--ink); flex: none; }
.voice-credit-text { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.voice-credit-text b { font-size: 0.98rem; }

.voice-credit-src {
  font-size: 0.8rem;
  color: var(--text-secondary);
}

.voice-credit-text code {
  font-size: 0.72rem;
  color: var(--orange-text);
  background: var(--orange-tint);
  border-radius: 5px;
  padding: 1px 6px;
  align-self: flex-start;
  margin-top: 3px;
}

/* One of only two places ink survives on this page (the other is the
   footer's alert sticker) — it is an action, so it keeps its border. */
.about-back { margin: 40px 0 0; }
.about-back a {
  display: inline-flex;
  align-items: center;
  font-size: 0.94rem;      /* 15px */
  font-weight: 700;
  color: var(--ink);
  text-decoration: none;
  padding: 10px 18px;
  border: 2px solid var(--ink);
  border-radius: 6px;
}
.about-back a:hover { background: var(--card-body-bg); }

/* About: the one page offered in two languages — a quiet switch above the title */
.about-langs {
  display: flex;
  gap: 10px;
  font-size: 0.72rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted);
  margin: -6px 0 6px;
}
.about-langs a { color: var(--orange-text); text-decoration: none; }
.about-langs [aria-current] { color: var(--ink); }
.about-langs a:hover { text-decoration: underline; }

/* --- the inner-page scale on a phone -------------------------------------
   The mock is a fixed 660px desktop column, so every value above is a
   desktop value. Measured at 375px (2026-08-29), two of them break:

   1. The credits grid. Two columns leave each row 154px; after the 44px
      avatar the chip has ~98px and needs 98px, so ALL FOUR credits wrapped
      onto two lines — splitting `VOICEVOX:雀松朱司` mid-string. That string
      is the licence's verbatim credit, so breaking it is the one thing this
      list must not do. One column gives it 279px.

   2. The Japanese title — handled at .page-title itself with clamp(), not
      here; see the note there. A breakpoint could not cover it safely.

   The lede comes down too: at 20.5px on a 335px column it ran ~16 characters
   a line, well under a readable measure. Body stays 17px — it was already
   sized for a phone and doesn't want shrinking. */
@media (max-width: 540px) {
  .about-lede { font-size: 1.125rem; line-height: 1.62; }
  .about-h2   { font-size: 1.4rem; }
  .voice-credits { grid-template-columns: 1fr; }
  .rule { margin-top: 40px; }
}


/* --- the register line (content-quality-DESIGN §2) --------------------
   What kind of Japanese this set is. A chip and a native details toggle —
   quiet until asked, one line when asked. */

.register-line {
  margin: 14px 0 0;
  font-size: 0.72rem;
  color: var(--text-secondary);
}

.register-line summary {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  list-style: none;
}

.register-line summary::-webkit-details-marker { display: none; }

.register-chip {
  font-size: 0.62rem;
  font-weight: 900;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  border: 1.5px solid var(--line);
  border-radius: 999px;
  padding: 2px 9px;
  color: var(--text-muted);
  background: var(--card-body-bg);
}

.register-what {
  font-size: 0.66rem;
  font-weight: 700;
  color: var(--text-muted);
  text-decoration: underline dotted;
}

.register-line[open] .register-what { display: none; }

.register-line p {
  margin: 6px 0 0;
  max-width: 52ch;
}

.register-line-room { text-align: center; }
.register-line-room p { margin-left: auto; margin-right: auto; }

/* ── THE RIBBON (pitch-DESIGN.md §6) ──────────────────────────────────────
   Kana inside the band at their true time positions, the measured pitch over
   them, the taught accent stepping along the top. Three colours, three
   meanings and they must stay distinct: TEAL measures this recording, INK
   classifies the word as high/low morae, ORANGE is where you are now. The
   gap between teal and ink is not error — see §3. */
.ribbon-wrap { margin-top: 0.7rem; }

.ribbon-window {
  overflow-x: auto;
  overflow-y: hidden;
  border: 2.5px solid var(--ink);
  border-radius: 12px;
  background: #fff;
  box-shadow: 3px 3px 0 var(--ink);
  padding: 3px 6px;
  cursor: grab;
  scrollbar-width: thin;
  scrollbar-color: var(--line) transparent;
  -webkit-overflow-scrolling: touch;
}
/* ⭐ Locked while playing (Ed). Not a restriction — it removes the question
   "is this scroll mine or the user's", which is what the first build got
   wrong badly enough to kill playback on its first frame. */
.ribbon-window.is-playing { overflow-x: hidden; cursor: default; }
.ribbon-window::-webkit-scrollbar { height: 5px; }
.ribbon-window::-webkit-scrollbar-thumb { background: var(--line); border-radius: 3px; }

.rb-svg { display: block; }
.rb-curve {
  fill: none;
  stroke: var(--teal);
  stroke-width: 2.3;
  stroke-linejoin: round;
  stroke-linecap: round;
}
.rb-step {
  fill: none;
  stroke: #0a6f68;
  stroke-width: 2.2;
  stroke-linejoin: miter;
  stroke-linecap: butt;
  opacity: 1;
}
.rb-kana {
  font-size: 25px;
  font-weight: 700;
  fill: var(--ink);
  opacity: 0.28;
  transition: opacity 90ms, fill 90ms;
}
.rb-kana.is-devoiced { opacity: 0.10; }
.rb-kana.is-now { opacity: 1; fill: var(--now); }
.rb-head {
  stroke: var(--now);
  /* 3px, not 2 (Ed, 2026-08-23: "maybe it is hard for me to pick out the
     color because it is so thin"). Hue discrimination collapses on thin
     marks — at 2px ANY saturated warm colour reads as red, which defeats
     the whole point of --now being distinct from --rec. 3px is the
     narrowest width at which the amber actually registers as amber. */
  stroke-width: 3;
}


/* The loudness envelope, stacked behind the ribbon and deliberately muted
   (Ed, 2026-08-21) — it is context for the pitch, not a second subject.
   Same data the stage draws above; decoded once, used twice. */
.rb-amp { fill: var(--teal); opacity: 0.22; }

/* YOUR take, drawn into the ribbon over hers. Warm against her teal, and
   onset-aligned at real time — never stretched to her length, so drift is
   visible rather than smoothed away (pitch-DESIGN §6c). */
.rb-you { fill: var(--rec); opacity: 0.42; }
.rb-you.is-live { opacity: 0.55; }

/* One caption row above the band: who each wave is, and the drag hint. The
   labels are fixed captions — they never change with the content — so they
   sit above rather than being overlaid and pinned (Ed, 2026-08-21). */
.ribbon-meta {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  margin: 0 0.15rem 0.3rem;
}
.ribbon-meta .stage-label { position: static; top: auto; left: auto; }

/* The register chip is metadata ABOUT the phrase, not another control. Sitting
   14px under Speed and Voice with nothing between them, it read as a third row
   of the same group (Ed, 2026-08-21). A hairline closes the controls so the eye
   stops there, and the extra room says the chip belongs to a different thought.
   Scoped to the room: the pattern page shows the same chip in a context where
   there are no controls to be confused with. */
.register-line-room {
  margin-top: 20px;
  padding-top: 15px;
  border-top: 1.5px solid var(--line);
}

/* ==========================================================================
   The pitch-accent page (/pitch). Borrows the About page's prose rhythm and
   adds the small step diagrams, drawn in the ribbon's own deep teal so the
   reader learns the shape here and recognises it in the room.
   ========================================================================== */

.pa-row, .pa-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 14px 26px;
  margin: 6px 0 12px;
}

.pa-fig {
  margin: 0;
  padding: 10px 14px 8px;
  background: var(--card-body-bg);
  border: 2px solid var(--ink);
  border-radius: var(--radius);
  box-shadow: 3px 3px 0 var(--ink);
}
.pa-fig figcaption {
  margin-top: 4px;
  font-size: 0.82rem;
  color: var(--text-secondary);
  line-height: 1.45;
}
.pa-fig-wide { min-width: 230px; flex: 1 1 40%; max-width: 360px; }
/* Two columns, deliberately: the prose asks the reader to compare 桜 (heiban)
   with 男 (odaka), which only works if both are on screen at once. A 3+1
   wrap strands 男 on its own row. */
.pa-grid { max-width: 760px; }
.pa-romaji { color: var(--text-muted); font-style: italic; }
.pa-num { font-weight: 800; color: #0a6f68; margin-left: 4px; }

/* The step, exactly as the ribbon draws it: deep teal, never joined across a
   phrase boundary, one kana per mora beneath. */
.pa-step { display: block; overflow: visible; }
.pa-step-line { stroke: #0a6f68; stroke-width: 2.6; stroke-linecap: round; }
.pa-step-kana {
  font-family: inherit;
  font-size: 15px;
  font-weight: 700;
  fill: var(--ink);
}
.pa-step-particle { fill: var(--text-muted); font-weight: 500; }

.pa-ribbon {
  margin: 6px 0 14px;
  max-width: 560px;
}
.pa-ribbon img {
  display: block;
  width: 100%;
  height: auto;
  border: 2px solid var(--ink);
  border-radius: var(--radius-lg);
  box-shadow: 4px 4px 0 var(--ink);
}

.pa-legend { margin: 0 0 6px; max-width: 72ch; }
.pa-legend-row {
  display: grid;
  grid-template-columns: 9.5em 1fr;
  gap: 6px 14px;
  padding: 10px 0;
  border-top: 1px solid var(--line);
}
.pa-legend-row:last-child { border-bottom: 1px solid var(--line); }
.pa-legend dt {
  display: flex;
  align-items: center;
  gap: 8px;
  font-weight: 800;
  font-size: 0.88rem;
}
.pa-legend dd { margin: 0; font-size: 0.86rem; line-height: 1.55; color: var(--text-secondary); }
@media (max-width: 540px) {
  .pa-legend-row { grid-template-columns: 1fr; gap: 4px; }
}

.pa-swatch {
  flex: 0 0 auto;
  width: 26px;
  height: 12px;
  border-radius: 3px;
}
.pa-swatch-step  { background: #0a6f68; height: 3px; }
.pa-swatch-curve { background: var(--teal); height: 3px; border-radius: 99px; }
.pa-swatch-kana  { background: transparent; border: 2px solid var(--ink); width: 12px; height: 14px; }
.pa-swatch-faint { background: transparent; border: 2px solid var(--line); width: 12px; height: 14px; }
.pa-swatch-head  { background: var(--orange); width: 3px; height: 16px; }

.pa-back { margin-top: 26px; }

/* The teaching itself is PRIMARY text (Ed's notes: "the teaching in primary
   text, not gray" — the old page had the one full-ink paragraph be the
   intro, and everything that actually taught in the muted secondary
   colour: inverted). .about-note remains for genuinely secondary lines. */
.pa-p {
  margin: 0 0 18px;
  font-size: 1.0625rem;    /* 17px — the same body as About */
  line-height: 1.68;
  color: var(--ink);
}
.pa-p a { font-weight: 700; }

.pa-play {
  display: inline-grid;
  place-items: center;
  width: 30px;
  height: 30px;
  margin-right: 8px;
  vertical-align: middle;
  border: 2px solid var(--ink);
  border-radius: 50%;
  background: var(--teal);
  box-shadow: 2px 2px 0 var(--ink);
  color: #fff;
  font-size: 0.72rem;
  cursor: pointer;
  transition: transform .1s ease, box-shadow .1s ease;
}
.pa-play:active { transform: translate(2px, 2px); box-shadow: 0 0 0 var(--ink); }
.pa-play.is-playing { background: var(--orange); }

/* The sentence diagram is wider than a phone. It scrolled from day one —
   but the WHOLE figure scrolled, caption and all, and nothing said so: the
   step line ran straight into the border and stopped, which reads as
   "cut off", not "drag me" (Ed, on his phone, 2026-08-29). Now only this
   strip scrolls, dressed like the ribbon-window (grab cursor, thin visible
   scrollbar, momentum touch scroll), and the edge fades below are the tell.

   The fades are the background-attachment trick: the white covers scroll
   with the content (local), the grey shadows do not (scroll) — so a shadow
   is visible only while content remains on that side, and dies exactly at
   the end of the scroll. No JS, and browsers that ignore `local` just show
   no fade — which is today's behaviour, not a break. */
.pa-fig-sent { max-width: 100%; }
.pa-scroll {
  overflow-x: auto;
  overflow-y: hidden;
  cursor: grab;
  scrollbar-width: thin;
  scrollbar-color: var(--line) transparent;
  -webkit-overflow-scrolling: touch;
  background:
    linear-gradient(90deg,  var(--card-body-bg) 40%, transparent) left  / 36px 100%,
    linear-gradient(270deg, var(--card-body-bg) 40%, transparent) right / 36px 100%,
    radial-gradient(farthest-side at 0 50%,    #00000030, transparent) left  / 14px 100%,
    radial-gradient(farthest-side at 100% 50%, #00000030, transparent) right / 14px 100%;
  background-repeat: no-repeat;
  background-attachment: local, local, scroll, scroll;
}
.pa-scroll::-webkit-scrollbar { height: 5px; }
.pa-scroll::-webkit-scrollbar-thumb { background: var(--line); border-radius: 3px; }
.pa-scroll svg { display: block; }
/* The hint is a claim, and on a desktop the diagram visibly does not
   scroll — so the sentence only exists where it is true. The strip fits
   from a ~682px viewport up; 700 is that with a margin. */
.pa-scroll-hint { display: none; }
@media (max-width: 700px) { .pa-scroll-hint { display: inline; } }
.pa-out { color: var(--orange-text); font-weight: 700; }

/* The stress-vs-pitch comparison cards ("It is not stress"). The same
   vocabulary as the room and the guide's band cards — teal bars loudness,
   teal line the voice — so a reader who reaches the room recognises both. */
.pa-str { display: block; overflow: visible; }
.pa-str-bar   { fill: var(--teal); opacity: 0.55; }
.pa-str-curve { stroke: var(--teal); stroke-width: 2.4;
                stroke-linejoin: round; stroke-linecap: round; }
.pa-str-syl   { font-family: inherit; font-size: 15px; font-weight: 700;
                fill: var(--ink); }
.pa-str-syl.is-weak { opacity: 0.45; font-weight: 500; }

/* The table of contents. Structure is the mock's; the sizes are this site's
   real scale rather than the mock's — that Pitch artboard is a 460px preview
   (34px title, 15.5px body), so its absolute values are a miniature, not the
   design intent. The About artboard was full size and is what the rest of
   the system was built from.

   The uppercase-tracked mono label is the style the handoff moved OFF
   section headings and onto page furniture. This is page furniture. */
.pa-toc {
  border-top: 2px solid var(--line);
  border-bottom: 2px solid var(--line);
  padding: 14px 0 16px;
  margin: 0 0 44px;
}
.pa-toc-label {
  display: block;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.75rem;      /* 12px */
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: 10px;
}
/* Column-major: 5 explicit rows + column flow puts 1–5 left, 6–10 right.
   With row-major the eye would zig-zag across to follow the page order. */
.pa-toc ol {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-rows: repeat(5, auto);
  grid-auto-flow: column;
  gap: 6px 22px;
}
.pa-toc a {
  font-size: 0.94rem;      /* 15px */
  line-height: 1.4;
  color: var(--ink);
  text-decoration: none;
  border-bottom: 1px solid var(--line);
}
.pa-toc a:hover { border-bottom-color: var(--ink); }

/* Anchor targets must not land flush against the viewport top. Applies to
   /guide too, which has had ids since it was written and never had this. */
.about-h2 { scroll-margin-top: 24px; }

/* ⚠️ This block must stay AFTER the .pa-toc rules above. It lived in the
   About phone block first, ~300 lines earlier, and silently did nothing:
   same specificity, and the base rules came later in the file, so they won.
   A media query adds no specificity — only position breaks the tie. */
@media (max-width: 540px) {
  /* Two columns of ten titles leaves ~75px of text per column on a phone. */
  .pa-toc ol {
    grid-template-columns: 1fr;
    grid-template-rows: auto;
    grid-auto-flow: row;
    gap: 8px 0;
  }
  .pa-toc { margin-bottom: 32px; }
}

/* ==========================================================================
   The guide (/guide). Prose in the About voice; one picture per section;
   the five ribbon cards drawn in the ribbon's own colours, one element each.
   ========================================================================== */

/* The chip TOC that used to live here moved to the shared .pa-toc system
   (2026-08-29) — /guide and /pitch are sister reference pages and now carry
   the same "On this page" map. Guide overrides the row count: */
.gd .pa-toc ol { grid-template-rows: repeat(4, auto); }

/* A caption must sit visibly closer to ITS image than to the next one —
   10px above it, 14px below it read as ambiguous ownership (Ed, 2026-08-29).
   Within-figure stays 10px; between figures is now 3.6× that. */
.gd-fig { margin: 8px 0 36px; max-width: 560px; }
.gd-fig-narrow { max-width: 420px; }
.gd-fig img {
  display: block;
  width: 100%;
  height: auto;
  border: 2px solid var(--ink);
  border-radius: var(--radius-lg);
  box-shadow: 4px 4px 0 var(--ink);
  background: var(--card-body-bg);
}
.gd-fig figcaption {
  margin-top: 10px;
  font-size: 0.86rem;
  line-height: 1.55;
  color: var(--text-muted);
  max-width: 72ch;
}

.gd-crossref { font-weight: 700; }
.gd-crossref a { color: var(--orange-text); }

.gd-cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(270px, 1fr));
  gap: 14px;
  margin: 8px 0 16px;
  max-width: 900px;
}
.gd-card {
  margin: 0;
  padding: 12px 14px 10px;
  background: var(--card-body-bg);
  border: 2px solid var(--ink);
  border-radius: var(--radius);
  box-shadow: 3px 3px 0 var(--ink);
}
.gd-card figcaption {
  margin-top: 6px;
  font-size: 0.82rem;
  line-height: 1.5;
  color: var(--text-secondary);
}

/* The ribbon's own marks, at card scale. Colours match the room exactly so
   the reader recognises them, not merely understands them. */
.gd-rb { display: block; overflow: visible; }
.gd-rb-step  { stroke: #0a6f68; stroke-width: 2.6; stroke-linecap: round; }
.gd-rb-ghost { opacity: 0.28; }
.gd-rb-curve { stroke: var(--teal); stroke-width: 2.4; stroke-linejoin: round; stroke-linecap: round; }
.gd-rb-kana  { font-family: inherit; font-size: 17px; font-weight: 700; fill: var(--ink); }
.gd-rb-kana.is-now { fill: var(--now); }
.gd-rb-kana.is-quiet { opacity: 0.45; font-weight: 500; }
.gd-rb-kana.is-devoiced { fill: var(--ink); opacity: 0.28; }
.gd-rb-head  { stroke: var(--now); stroke-width: 4; }
.gd-band-bar   { fill: var(--teal); opacity: 0.55; }
.gd-band-ghost { opacity: 0.18; }
.gd-band-you   { fill: var(--rec); opacity: 0.65; }
.gd-cmp-you    { stroke: var(--rec); stroke-width: 2.4; stroke-linejoin: round; stroke-linecap: round; }
.gd-soon {
  display: inline-block;
  margin: 0 0 8px;
  padding: 4px 12px;
  border: 2px solid var(--ink);
  border-radius: 999px;
  background: var(--orange-tint);
  font-size: 0.68rem;
  font-weight: 900;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--orange-text);
}
.gd-dlg { fill: var(--card-body-bg); stroke: var(--ink); stroke-width: 2; }
.gd-dlg-mic { fill: var(--orange-tint); stroke: var(--ink); stroke-width: 1.5; }
.gd-dlg-mic-glyph { stroke: var(--orange-text); stroke-width: 1.6; stroke-linecap: round; }
.gd-dlg-text { font-size: 11px; font-weight: 700; fill: var(--ink); }
.gd-dlg-btn { fill: var(--card-header-bg); stroke: var(--line); stroke-width: 1.5; }
.gd-dlg-allow { fill: var(--teal); stroke: var(--ink); }
.gd-dlg-btn-text { font-size: 11px; font-weight: 800; fill: var(--text-secondary); }
.gd-dlg-allow-text { fill: #fff; }
.gd-fig-card {
  padding: 12px 14px 10px;
  background: var(--card-body-bg);
  border: 2px solid var(--ink);
  border-radius: var(--radius);
  box-shadow: 3px 3px 0 var(--ink);
  max-width: 340px;
}
