/* ================================================================
   SPEED READER — RSVP + tone-colored TTS
   Styles for layouts/_default/reader.html
   Only loaded on /reader/ — scoped to .reader-page
   ================================================================ */

:root {
  --tone-1: #C88A00; /* gold / ochre  — 1st tone */
  --tone-2: #FF0000; /* red           — 2nd tone */
  --tone-3: #A020F0; /* purple/magenta— 3rd tone */
  --tone-4: #1C41FF; /* blue          — 4th tone */
  --tone-0: #595959; /* mid-dark gray — neutral tone (5th/tone sandhi) */

  --reader-accent: #1A56A0;
  --reader-bg:      #14161a;
  --reader-panel:   #f4f5f7;
  --reader-border:  rgba(0, 0, 0, 0.12);

  /* User-selectable stage background/font pair (.reader-swatch buttons) —
     scoped to the stage background and plain/untoned text only; pinyin
     tone colors and the ORP focal accent stay fixed regardless of theme.
     Matches the first (is-active-by-default) swatch, Peach / Warm Gray —
     not --reader-bg anymore, since that swatch is no longer the dark
     theme it originally named. */
  --rsvp-bg: #fad7b4;
  --rsvp-fg: #4a453f;

  /* RSVP word-display fonts, split by script ("Font I" alphabetic / "Font
     II" CJK) — defaults match the previously-hardcoded values exactly, so
     nothing changes visually until a setting is actually touched. Ported
     from the Chrome extension's reader.css, see RSVP_WEBSITE_PARITY_NOTES.md
     on the Kingston bridge drive (section 1). */
  --rsvp-font-alpha: "Lexend";
  --rsvp-font-alpha-variant: normal;
  --rsvp-font-cjk: serif;
  --rsvp-font-cjk-weight: 600;

  /* Selected-language button color — same pair as the 8th stage swatch
     (Electric Ultramarine / Electric Lime). */
  --lang-active-bg: #2B00FF;
  --lang-active-fg: #D4FF00;

  /* Idle-language button color — same pair as the 9th stage swatch
     (Yellow / Palatinate Blue). */
  --lang-idle-bg: #FFFF33;
  --lang-idle-fg: #3333FF;
}

/* ── Header padding: theme.scss's ".page-template .post-header" rule
   (theme.scss) gives every post-header 60px top+bottom padding — fine for
   a normal article, but on this compact topbar it reads as a big empty
   band above/below the row. vocab-page.css overrides this the same way
   for #vocab-page-header; this is reader's copy of that fix. ── */

#reader-page-header {
  padding: 1.1rem 0 0.8rem;
}

/* ── Back link + language menu share one row, same pattern as
   vocab-page.css's #vocab-page-topbar. Previously two stacked blocks
   (site-languages, then back-to-main-page below it) rendering as two
   header rows — merged into a single flex row instead. ── */

#reader-page-topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
}

/* ── Language menu — same pill-button look as the homepage's
   #site-languages (theme.scss), which is scoped to #site-head and so
   doesn't reach this standalone page on its own. Reader.html renders the
   identical markup; this just re-supplies the visual rules. ── */

#site-languages {
  position: relative;
  text-align: right;
}

#site-languages .btn-lang {
  text-decoration: none;
  background-color: var(--lang-idle-bg);
  color: var(--lang-idle-fg);
  border-radius: 5px;
  -webkit-border-radius: 5px;
  display: inline-block;
  font-size: 70%;
  line-height: 1;
  margin: 0 0 0 0.7rem;
  padding: 0.3rem 0.7rem;
  position: relative;
  text-transform: uppercase;
  font-family: "Oswald", sans-serif;
}

#site-languages .btn-lang.active {
  color: var(--lang-active-fg);
  background-color: var(--lang-active-bg);
}

#site-languages .btn-lang:hover {
  color: var(--lang-active-fg);
  background-color: var(--lang-active-bg);
}

/* ── Collapsed dropdown trigger — inert above the MOBILE breakpoint below,
   where #lang-dropdown instead renders flat as the plain pill row (the
   original look). Below it, this becomes the only visible piece and
   #lang-dropdown becomes an absolutely-positioned panel toggled by it. ── */

#lang-trigger {
  display: none;
}

#lang-dropdown {
  display: contents;
}

/* Widen the standalone page beyond the theme's 700px article column */
.reader-page .post {
  max-width: 1100px;
}

/* The desktop left-edge strips (#vocab-nav — Nouns/Verbs/Adjectives) are
   redundant here now that /vocab/ is the dedicated place for semantic
   categories. Same fix as vocab-page.css's copy of this rule: hide the
   strips and reclaim the margin custom.css normally reserves for them
   (body's own margin-left, >=769px). Scoped to reader-standalone-page
   (set by reader-engine.js) so no other page is affected. */
body.reader-standalone-page #vocab-nav {
  display: none;
}

@media only screen and (min-width: 769px) {
  body.reader-standalone-page {
    margin-left: 0;
  }
}

.reader-page .post-content {
  font-family: "Open Sans", sans-serif;
}

/* ── Page header (back link + "Speed Reader" title) ─────────
   Only loaded on /reader/, so these are unscoped selectors like the
   rest of this file — no risk of leaking onto other pages.

   OPTION 1 — colored glow: the homepage's white text-shadow only reads
   because .blog-title sits on a dark/photo cover background; on this
   page .post-title sits on the plain light page background, where a
   white halo has near-zero contrast. Keeping the light page as-is and
   swapping to an accent-colored halo instead, so it stays visible
   without needing a dark backdrop behind the title. */

#back-to-main-page {
  font-family: "Lexend", sans-serif;
  white-space: nowrap;
  text-decoration: none;
}

.post-title {
  text-align: center;
  text-shadow: 0 0 10px rgba(26, 86, 160, 0.55),
               0 0 18px rgba(26, 86, 160, 0.35);
}

.reader-intro {
  max-width: 640px;
  margin: 0 auto 2.5rem;
  text-align: center;
  color: #555;
  font-size: 1.05rem;
  line-height: 1.6;
}

/* Lexend only for alphabetic (Latin-script) languages — ja/zh-tw keep
   their normal CJK-appropriate font. */
:lang(en) .reader-intro,
:lang(es) .reader-intro {
  font-family: "Lexend", sans-serif;
}

/* ── Stage color theme swatches ──────────────────────────── */

.reader-color-swatches {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.5rem;
  margin: 0 auto 1rem;
  max-width: 900px;
}

.reader-swatch {
  width: 28px;
  height: 28px;
  border-radius: 6px;
  border: 2px solid var(--reader-border);
  padding: 0;
  cursor: pointer;
  transition: border-color 0.2s, transform 0.15s;
}

.reader-swatch:hover {
  transform: scale(1.1);
}

/* Swap-colors icon button — same footprint as a color swatch so it sits
   flush in the row, but plain (no bg/fg gradient of its own) since it acts
   on whichever pair is currently applied rather than holding one itself. */
.reader-swatch-swap {
  width: 28px;
  height: 28px;
  border-radius: 6px;
  border: 2px solid var(--reader-border);
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: none;
  cursor: pointer;
  transition: border-color 0.2s, transform 0.15s;
}

.reader-swatch-swap:hover {
  transform: scale(1.1);
  border-color: var(--reader-accent);
}

.reader-swatch-swap img {
  width: 25px;
  height: 25px;
  pointer-events: none;
}

.reader-swatch.is-active {
  border-color: var(--reader-accent);
  box-shadow: 0 0 0 2px var(--reader-accent);
}

/* ── Custom two-color swatch picker ──────────────────────────
   One small square holding two native <input type="color"> pickers,
   each clipped to a diagonal half via clip-path — top-left triangle
   opens the OS picker for the background color, bottom-right for the
   font color. Ported from the Chrome extension (see
   RSVP_WEBSITE_PARITY_NOTES.md addendum 1) alongside its explicit
   "no matching preset" fallback in reader-engine.js. */
.reader-swatch-custom {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  margin-left: 0.3rem;
}

.reader-swatch-custom-picker {
  position: relative;
  display: inline-block;
  width: 28px;
  height: 28px;
  border-radius: 6px;
  border: 2px solid var(--reader-border);
  overflow: hidden;
  transition: border-color 0.2s, transform 0.15s;
}

.reader-swatch-custom-picker:hover {
  transform: scale(1.1);
}

.reader-swatch-custom-picker.is-active {
  border-color: var(--reader-accent);
  box-shadow: 0 0 0 2px var(--reader-accent);
}

.reader-swatch-custom-picker input[type="color"] {
  position: absolute;
  top: -6px;
  left: -6px;
  width: 40px;
  height: 40px;
  padding: 0;
  border: none;
  cursor: pointer;
  background: none;
}

.reader-swatch-custom-picker input[type="color"]::-webkit-color-swatch-wrapper { padding: 0; }
.reader-swatch-custom-picker input[type="color"]::-webkit-color-swatch { border: none; }
.reader-swatch-custom-picker input[type="color"]::-moz-color-swatch { border: none; }

#reader-swatch-custom-bg { clip-path: polygon(0 0, 100% 0, 0 100%); }
#reader-swatch-custom-fg { clip-path: polygon(100% 0, 100% 100%, 0 100%); }

.reader-swatch-custom-checkbox {
  display: flex;
  align-items: center;
  gap: 0.3rem;
  font-family: "Lexend", sans-serif;
  font-size: 0.85rem;
  color: #555;
  cursor: pointer;
  user-select: none;
}

.reader-swatch-custom-checkbox input {
  cursor: pointer;
}

/* ================================================================
   STAGE — the RSVP display
   ================================================================ */

.rsvp-stage-wrap {
  position: relative;
  margin: 0 auto 2rem;
  max-width: 900px;
}

/* Row wrapping the stage + its forced-sync step arrows — arrows are plain
   flex siblings (not absolutely positioned) so they never get clipped by
   .rsvp-stage's own overflow:hidden and always sit vertically centered on
   the stage regardless of whether the translation panel above is visible. */
.rsvp-stage-row {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}

.rsvp-stage {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 1;
  min-width: 0;
  height: 220px;
  background: var(--rsvp-bg);
  border-radius: 14px;
  overflow: hidden;
  box-shadow: 0 8px 28px rgba(0, 0, 0, 0.18);
}

/* Forced-sync step arrows — self-contained iridescent circular buttons
   (iridescent-arrow-controls.md's original blue/cyan/teal/lavender sweep,
   same palette as .reader-vocab-nav above), NOT the shared .flashcard-arrow
   in assets/css/custom.css: that class's gradient falls back to
   --vocab-yellow-1..4 (gold) unless a vocab-category ancestor sets
   --arrow-tone-*, which nothing here does — reusing it verbatim rendered
   solid gold instead of iridescent. Own @keyframes name so reader.css stays
   self-contained per its file-header comment (same rationale as
   .reader-vocab-nav's own reader-vocab-nav-drift, just above).
   Hidden unless Forced sync is on (reader-engine.js toggles .is-visible) —
   natural pacing has no per-word pause for these to step through. */
.rsvp-arrow {
  display: none;
  flex: 0 0 auto;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  border: 1px solid #799df1;
  background: linear-gradient(
    60deg,
    #799df1 5%, #60c1ec 15%, #7bd9de 25%, #cfb0d2 35%,
    #799df1 45%, #60c1ec 55%, #7bd9de 65%, #cfb0d2 75%,
    #799df1 85%, #60c1ec 95%, #7bd9de
  );
  background-size: 200% 200%;
  background-position: -100% -100%;
  animation: rsvp-arrow-drift 8s infinite;
  box-shadow: 1px 1px 10px rgba(0, 0, 0, 0.25);
  transition: box-shadow 0.3s ease, filter 0.5s ease;
  cursor: pointer;
  align-items: center;
  justify-content: center;
  padding: 0;
  outline: none;
}

.rsvp-arrow.is-visible { display: flex; }

.rsvp-arrow::after {
  font-size: 15px;
  font-weight: bold;
  color: #fff;
  line-height: 1;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.35);
}

.rsvp-arrow--prev::after { content: "\2039"; }
.rsvp-arrow--next::after { content: "\203A"; }

.rsvp-arrow:active { transform: scale(0.88); }

.rsvp-arrow:hover,
.rsvp-arrow:focus-visible {
  filter: drop-shadow(0 0 0.5rem #f7f7f6);
  box-shadow: inset 0 0 0.5rem rgba(247, 247, 246, 0.5);
}

@keyframes rsvp-arrow-drift {
  20% { background-position: -130% -120%; }
  40% { background-position: -140% -100%; }
  70% { background-position: -110% -130%; }
}

/* Focal guide marks — fixed to the horizontal center of the stage,
   independent of chunk length (top/bottom tick marks) */
.rsvp-stage::before,
.rsvp-stage::after {
  content: "";
  position: absolute;
  left: 50%;
  width: 2px;
  height: 14px;
  background: var(--rsvp-fg);
  opacity: 0.85;
  transform: translateX(-50%);
}

.rsvp-stage::before { top: 10px; }
.rsvp-stage::after   { bottom: 10px; }

/* ── Word rendering ──────────────────────────────────────── */

.rsvp-word {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: baseline;
  width: 100%;
  padding: 0 2rem;
  font-family: "Open Sans", sans-serif;
  font-size: 3rem;
  font-weight: 600;
  line-height: 1.4;
  white-space: nowrap;
}

/* Lexend for alphabetic (non-CJK) stage text only — tone-mark pinyin
   (rt.rsvp-pinyin below) deliberately stays off Lexend: it renders
   precomposed macron vowels like ā/ē/ī/ō/ū as a detached bar next to a
   bare "a" instead of a combined glyph (reproducible across every
   weight), so 1st-tone syllables render broken. Plain English/Spanish
   text has no such characters, so it's unaffected. */
.rsvp-word:not(.is-cjk) {
  font-family: var(--rsvp-font-alpha), sans-serif;
  font-variant: var(--rsvp-font-alpha-variant);
  font-weight: 300;
  font-size: 3.3rem;
}

/* Idle "Ready — press Play" text — follows the theme's font color too,
   dimmed to still read as a placeholder rather than active content.
   Lexend falls back to Open Sans automatically for CJK placeholder text
   (準備就緒… / 準備完了…), since Lexend has no CJK glyph coverage. */
.rsvp-word.rsvp-placeholder {
  color: var(--rsvp-fg);
  opacity: 0.5;
  font-family: "Lexend", "Open Sans", sans-serif;
}

.rsvp-before {
  text-align: right;
  color: var(--rsvp-fg);
  overflow-x: hidden;
  text-overflow: clip;
}

.rsvp-after {
  text-align: left;
  color: var(--rsvp-fg);
  overflow-x: hidden;
  text-overflow: clip;
}

.rsvp-focal {
  color: var(--rsvp-fg);
  text-align: center;
  padding: 0 0.05em;
}

/* CJK chunks (Chinese or Japanese): centered as a whole block, no ORP
   split, larger + serif to match the site's CJK type conventions */
.rsvp-word.is-cjk {
  grid-template-columns: 1fr;
  font-family: var(--rsvp-font-cjk, serif);
  font-weight: var(--rsvp-font-cjk-weight, 600);
  font-size: 4rem;
  letter-spacing: 0.15em;
}

.rsvp-word.is-cjk .rsvp-focal {
  text-align: center;
  color: inherit;
}

/* Per-character tone coloring — Chinese (zh-Hant/zh-Hans) only.
   Japanese chunks use .tone-plain instead since pinyin tones don't apply. */
.rsvp-word.is-cjk .tone-1 { color: var(--tone-1); }
.rsvp-word.is-cjk .tone-2 { color: var(--tone-2); }
.rsvp-word.is-cjk .tone-3 { color: var(--tone-3); }
.rsvp-word.is-cjk .tone-4 { color: var(--tone-4); }
.rsvp-word.is-cjk .tone-0 { color: var(--tone-0); }
.rsvp-word.is-cjk .tone-plain { color: var(--rsvp-fg); }

.rsvp-word.is-cjk:not(.tone-color-on) .tone-1,
.rsvp-word.is-cjk:not(.tone-color-on) .tone-2,
.rsvp-word.is-cjk:not(.tone-color-on) .tone-3,
.rsvp-word.is-cjk:not(.tone-color-on) .tone-4,
.rsvp-word.is-cjk:not(.tone-color-on) .tone-0 {
  color: var(--rsvp-fg);
}

/* Pinyin ruby annotation — always the theme's plain font color, deliberately
   independent of the tone-color toggle (it's a reading aid, not itself a
   tone indicator, and shouldn't visually compete with the tone-N hue on the
   character underneath it). Reset the CJK block's letter-spacing/serif
   choices, which would otherwise spread and mis-render the small syllable
   text. */
.rsvp-word.is-cjk rt.rsvp-pinyin {
  color: var(--rsvp-fg);
  /* NOT Lexend — see the .rsvp-word:not(.is-cjk) comment above: Lexend
     renders 1st-tone macron vowels (ā/ē/ī/ō/ū) broken, and pinyin is
     exactly where those show up. */
  font-family: "Open Sans", sans-serif;
  font-size: 0.44em;
  font-weight: 200; /* ExtraLight — Thin (100) at this size risked being too faint to read */
  letter-spacing: normal;
  transform: translateY(-0.2em);
}

/* Bopomofo (Phonetic annotation II ZH) — Open Sans has no Zhuyin glyphs,
   so use a CJK system font; regular weight, since the thin fallback
   strokes of ExtraLight read poorly for these shapes. Romaji (II JP) is
   plain Latin text and keeps the pinyin styling above as-is. */
.rsvp-word.is-cjk rt.rsvp-bopomofo {
  font-family: "PingFang TC", "Heiti TC", "Microsoft JhengHei", "Noto Sans TC", sans-serif;
  font-weight: 400;
  /* A Zhuyin reading (ㄓㄨㄥ) is wider than its character — slightly smaller
     than pinyin plus side padding, or neighbouring readings run together. */
  font-size: 0.36em;
  padding: 0 0.25em;
}

/* ── Progress bar ────────────────────────────────────────── */

.rsvp-progress-track {
  height: 4px;
  background: rgba(0, 0, 0, 0.08);
  border-radius: 2px;
  margin-top: 0.6rem;
  overflow: hidden;
}

.rsvp-progress-bar {
  height: 100%;
  width: 0%;
  background: var(--rsvp-fg);
  transition: width 0.15s linear;
}

.rsvp-status {
  text-align: center;
  margin-top: 0.5rem;
  font-family: "Oswald", sans-serif;
  letter-spacing: 1px;
  font-size: 0.85rem;
  color: #888;
  text-transform: uppercase;
}

/* ── Translation badge + panel ("Translate to") — ported from the Chrome
   extension's reader-engine.js, see RSVP_WEBSITE_PARITY_NOTES.md on the
   Kingston bridge drive (third addendum). Reuses --rsvp-bg/--rsvp-fg and
   --rsvp-font-alpha/--rsvp-font-cjk* verbatim so the panel auto-matches
   whatever stage theme (including Custom) is active, zero extra wiring. */
.rsvp-lang-btn {
  display: none;
  position: absolute;
  top: 4px;
  left: 4px;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: var(--rsvp-bg, #14161a);
  color: var(--rsvp-fg, #fff);
  align-items: center;
  justify-content: center;
  z-index: 2;
  pointer-events: none; /* inert badge — clicking it does nothing, per spec */
}

.rsvp-lang-btn.is-visible { display: flex; }

.rsvp-lang-btn svg {
  width: 18px;
  height: 18px;
}

.rsvp-translation-panel {
  display: none;
  position: relative; /* positioning context for .rsvp-lang-btn, moved in here */
  box-sizing: border-box; /* keeps this exactly as wide as .rsvp-stage despite its own padding */
  width: 100%;
  height: calc(220px / 3);
  margin: 0 auto 0.75rem;
  border-radius: 14px;
  background: var(--rsvp-bg);
  color: var(--rsvp-fg);
  align-items: center;
  justify-content: center;
  padding: 0 1.5rem;
  cursor: default;
}

.rsvp-translation-panel.is-visible { display: flex; }

/* Same Font I/II mechanism as .rsvp-word, keyed on the TARGET language
   (see setTranslateTo() in reader-engine.js), not the source — the panel
   shows the translation, which may be a different script than the word.
   Declared on the panel (inherited by .rsvp-translation-text) since the
   badge SVG shares this element and uses currentColor, not font-family. */
.rsvp-translation-panel:not(.is-cjk) {
  font-family: var(--rsvp-font-alpha), sans-serif;
  font-variant: var(--rsvp-font-alpha-variant);
  font-weight: 200;
}

.rsvp-translation-panel.is-cjk {
  font-family: var(--rsvp-font-cjk, serif);
  font-weight: var(--rsvp-font-cjk-weight, 600);
}

.rsvp-translation-text {
  max-width: 100%;
  text-align: center;
  font-size: 1.85rem;
  line-height: 1.3;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* CJK translations can wrap without looking broken. */
.rsvp-translation-panel.is-cjk .rsvp-translation-text { white-space: normal; }

/* One-time "tap to enable" / "downloading" gesture-prompt state — see
   ensureTranslator()'s NotAllowedError handling in reader-engine.js. */
.rsvp-translation-panel.is-prompt {
  cursor: pointer;
  opacity: 0.85;
}

.rsvp-translation-panel.is-prompt .rsvp-translation-text {
  font-size: 1.05rem;
  white-space: normal;
}

.reader-translate-lang { display: none; margin-top: 0.4rem; }
.reader-translate-lang.is-visible { display: block; }

/* ================================================================
   CONTROLS
   ================================================================ */

.reader-panel {
  max-width: 900px;
  margin: 0 auto;
  background: var(--reader-panel);
  border: 1px solid var(--reader-border);
  border-radius: 14px;
  padding: 1.8rem 2rem;
}

.reader-panel h2,
.reader-panel h3 {
  font-family: "Lexend", sans-serif;
  letter-spacing: 1px;
  text-transform: uppercase;
  font-size: 1.3rem;
  color: #666;
  margin: 0 0 0.8rem;
}

.reader-textarea {
  width: 100%;
  min-height: 120px;
  padding: 0.9rem 1rem;
  font-size: 1.15rem;
  font-family: serif;
  border: 1px solid var(--reader-border);
  border-radius: 8px;
  resize: vertical;
  box-sizing: border-box;
  background: #fff;
}

/* Placeholder only ("Paste text here…") — the textarea's own typed/pasted
   content stays serif for comfortable reading, CJK included. */
.reader-textarea::placeholder {
  font-family: "Lexend", sans-serif;
}

.reader-textarea:focus {
  outline: none;
  border-color: var(--reader-accent);
}

/* ── Vocab quick-load (per-category subcategory dropdowns) ──
   --vocab-* vars live in assets/css/custom.css (not loaded here) — same
   fallback pattern the old pre-removal buttons used. One class per
   category (matching that category's ribbon color elsewhere on the site,
   see vocab-16-categories-complete memory) since Hugo's html/template
   auto-escaper won't allow a dynamic --vocab-* custom-property name inside
   a style="" attribute. */

.reader-vocab-picker-wrap {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin-bottom: 3rem; /* was 1.6rem + the Play/Reset row, moved up under the stage 2026-10-10 */
}

/* Clipped viewport, not a wrapping row — once more <select> pickers exist
   than fit, the overflow ones are reached by rotating through them with
   the nav pills (reader-engine.js), not by wrapping to a second line or
   scrolling. min-width: 0 overrides the flex-item default (auto = content
   size), which would otherwise stop this from ever shrinking below its
   children's combined width and defeat the clipping entirely. */
.reader-vocab-picker {
  display: flex;
  flex-wrap: nowrap;
  overflow: hidden;
  gap: 0.7rem;
  flex: 1 1 auto;
  min-width: 0;
}

.reader-vocab-select {
  flex: 0 0 auto;
  font-family: "Oswald", sans-serif;
  font-size: 0.85rem;
  letter-spacing: 1px;
  text-transform: uppercase;
  padding: 0.45rem 2rem 0.45rem 1rem;
  border-radius: 18px;
  border: 2px solid transparent;
  cursor: pointer;
  color: #fff;
  appearance: none;
  -webkit-appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath fill='%23fff' d='M1 1l5 5 5-5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0.9rem center;
  background-size: 0.7rem;
  transition: filter 0.2s;
}

.reader-vocab-select:hover { filter: brightness(1.1); }

.reader-vocab-select.vocab-nouns   { background-color: var(--vocab-blue, #1A56A0); }
.reader-vocab-select.vocab-verbs   { background-color: var(--vocab-red, #C0392B); }
.reader-vocab-select.vocab-adverbs { background-color: var(--vocab-orange, #F27F0D); }
/* Same violet as the /vocab/ Dictionary's Health category — no
   --vocab-violet-dark exists site-wide (custom.css defaults it to white
   text like blue/red/orange), so it stays in the default white-text group. */
.reader-vocab-select.vocab-health  { background-color: var(--vocab-violet, #6230DD); }
/* Same magenta as /vocab/'s Conjunctions category — no --vocab-magenta-dark
   exists site-wide either, so white text again. */
.reader-vocab-select.vocab-conjunctions { background-color: var(--vocab-magenta, #B62084); }
/* Same burgundy as /vocab/'s Time category (recolored from Gold on
   2026-08-12, see vocab-category-reorder-2026-08-12 memory) — no
   --vocab-burgundy-dark exists, white text like the others above. */
.reader-vocab-select.vocab-time { background-color: var(--vocab-burgundy, #C02B50); }
/* Same black as /vocab/'s Demonstratives category — no --vocab-black-dark
   exists (white wins overwhelmingly, 17.40:1 vs 1.21:1 for black text),
   white text like the others above. */
.reader-vocab-select.vocab-demonstratives { background-color: var(--vocab-black, #1A1A1A); }

/* Yellow and cream are light enough that white text/caret (the default
   above) fails contrast — same crossover the site already accounts for
   elsewhere (see feedback-vocab-color-text-contrast memory) — so they get
   their own dark text + a re-tinted caret SVG (fill swapped from #fff to
   #2a2a2a). Green isn't light, but the site's own vocab ribbon (custom.css
   .vocab-mobile-section.vocab-green .vocab-mobile-summary) already pairs it
   with dark text, so it gets the same treatment here for consistency with
   that established choice rather than a fresh contrast call. */
.reader-vocab-select.vocab-adjectives {
  background-color: var(--vocab-yellow, #D4A017);
  color: var(--vocab-yellow-dark, #2a2a2a);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath fill='%232a2a2a' d='M1 1l5 5 5-5'/%3E%3C/svg%3E");
}

.reader-vocab-select.vocab-measures {
  background-color: var(--vocab-cream, #E6D6A8);
  color: var(--vocab-cream-dark, #2a2a2a);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath fill='%232a2a2a' d='M1 1l5 5 5-5'/%3E%3C/svg%3E");
}

.reader-vocab-select.vocab-objects {
  background-color: var(--vocab-green, #19B34D);
  color: var(--vocab-green-dark, #2a2a2a);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath fill='%232a2a2a' d='M1 1l5 5 5-5'/%3E%3C/svg%3E");
}

.reader-vocab-select.vocab-food {
  background-color: var(--vocab-aqua, #7CC3DE);
  color: var(--vocab-aqua-dark, #2a2a2a);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath fill='%232a2a2a' d='M1 1l5 5 5-5'/%3E%3C/svg%3E");
}

/* --vocab-brown (Body's ribbon color, #B57330) is a genuine split-scale
   crossover on /vocab/ itself — see the --vocab-brown comment in
   custom.css — and its own precedent there pairs THIS specific base tone
   with white text, but only because that call was made for the larger
   ribbon/summary font size (WCAG's "large text" 3:1 floor). At this
   picker's much smaller pill font-size the normal-text 4.5:1 floor
   applies, and black clearly wins here (contrast-vs-black 5.46:1 vs
   vs-white 3.85:1, computed independently rather than copying the
   ribbon's large-text-calibrated choice) — same dark-text treatment as
   Adjectives/Measures/Objects/Food above. */
.reader-vocab-select.vocab-body {
  background-color: var(--vocab-brown, #B57330);
  color: var(--vocab-brown-dark, #2a2a2a);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath fill='%232a2a2a' d='M1 1l5 5 5-5'/%3E%3C/svg%3E");
}

.reader-vocab-select.vocab-prepositions {
  background-color: var(--vocab-pink, #E8C8C5);
  color: var(--vocab-pink-dark, #2a2a2a);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath fill='%232a2a2a' d='M1 1l5 5 5-5'/%3E%3C/svg%3E");
}

.reader-vocab-select.vocab-pronouns {
  background-color: var(--vocab-white, #E6E6E6);
  color: var(--vocab-white-dark, #2a2a2a);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath fill='%232a2a2a' d='M1 1l5 5 5-5'/%3E%3C/svg%3E");
}

.reader-vocab-select.vocab-questions {
  background-color: var(--vocab-silver, #ACB5BE);
  color: var(--vocab-silver-dark, #2a2a2a);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath fill='%232a2a2a' d='M1 1l5 5 5-5'/%3E%3C/svg%3E");
}

/* --vocab-gray (#808080) is a genuine near-crossover: vs-white only
   3.95:1 (fails the 4.5:1 normal-text floor), vs-black 5.32:1 (clears
   it) — computed independently the same way as Body/brown above, black
   wins clearly enough here despite gray sitting near the midpoint. */
.reader-vocab-select.vocab-not {
  background-color: var(--vocab-gray, #808080);
  color: var(--vocab-gray-dark, #2a2a2a);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath fill='%232a2a2a' d='M1 1l5 5 5-5'/%3E%3C/svg%3E");
}

.reader-vocab-select option {
  color: #000;
  background: #fff;
}

/* Nav pills — chip/pill variant of iridescent-arrow-controls.md (same
   gradient-drift technique already live as .flashcard-arrow in
   assets/css/custom.css, but that one's circular and tinted per vocab
   category; this one is a fixed blue/cyan/teal/lavender pill, generic
   across whichever categories currently sit in .reader-vocab-picker).
   Own @keyframes name (not reusing flashcard-arrow-drift) so reader.css
   stays self-contained per its file-header comment. */
.reader-vocab-nav {
  flex: 0 0 auto;
  height: 30px;
  padding: 0 0.85rem;
  border-radius: 999px;
  border: 1px solid #799df1;
  background: linear-gradient(
    60deg,
    #799df1 5%, #60c1ec 15%, #7bd9de 25%, #cfb0d2 35%,
    #799df1 45%, #60c1ec 55%, #7bd9de 65%, #cfb0d2 75%,
    #799df1 85%, #60c1ec 95%, #7bd9de
  );
  background-size: 200% 200%;
  background-position: -100% -100%;
  animation: reader-vocab-nav-drift 8s infinite;
  box-shadow: 1px 1px 10px rgba(0, 0, 0, 0.25);
  transition: box-shadow 0.3s ease, filter 0.5s ease;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  outline: none;
}

.reader-vocab-nav::after {
  font-size: 14px;
  font-weight: bold;
  color: #fff;
  line-height: 1;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.35);
}

.reader-vocab-nav--prev::after { content: "\2039"; }
.reader-vocab-nav--next::after { content: "\203A"; }

.reader-vocab-nav:active { transform: scale(0.88); }

.reader-vocab-nav:hover,
.reader-vocab-nav:focus-visible {
  filter: drop-shadow(0 0 0.5rem #f7f7f6);
  box-shadow: inset 0 0 0.5rem rgba(247, 247, 246, 0.5);
}

@keyframes reader-vocab-nav-drift {
  20% { background-position: -130% -120%; }
  40% { background-position: -140% -100%; }
  70% { background-position: -110% -130%; }
}

/* ── Transport buttons ───────────────────────────────────── */

.reader-transport {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.7rem;
  margin: 1rem 0 0.4rem;
}

/* Word-language toggle for the vocab pickers (EN/ES/繁/简/日) — sits
   centered above the color swatches. Same pill look as /vocab/'s
   dictionary language filter (.vocab-search-filter-btn in custom.css,
   using its light-background --vs-* values), redrawn here because
   custom.css isn't loaded on this page. Single-select, no ALL. */
.reader-vocab-lang-toggle {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.4rem;
  margin: 0 auto 1.2rem;
}

.reader-vocab-lang-btn {
  padding: 0.15rem 0.75rem;
  border-radius: 20px;
  background: transparent;
  cursor: pointer;
  font-size: 0.95rem;
  font-family: "Oswald", sans-serif;
  letter-spacing: 1px;
  border: 2px solid rgba(0, 0, 0, 0.12);
  color: rgba(0, 0, 0, 0.4);
  transition: border-color 0.2s, color 0.2s, background 0.2s;
}

.reader-vocab-lang-btn:hover {
  border-color: #222;
  color: #222;
}

.reader-vocab-lang-btn.is-active {
  background: rgba(0, 0, 0, 0.07);
  border-color: #222;
  color: #222;
}

/* Pill row between the RSVP stage and the TEXT panel — 20/50/100 word
   count justified left, 3s/5s/8s seconds-per-word justified right, both
   edges lined up with the stage/panel above and below (max-width 900px).
   Reuses .reader-vocab-lang-btn's pill look. */
.reader-pill-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  max-width: 900px;
  margin: 1.4rem auto;
}

.reader-pill-group {
  display: flex;
  gap: 6rem;
}

.reader-pill-row-btn {
  min-width: 3.6rem;
}

@media (max-width: 480px) {
  .reader-pill-group { gap: 1.2rem; }
}

.reader-btn {
  font-family: "Oswald", sans-serif;
  letter-spacing: 1px;
  text-transform: uppercase;
  font-size: 0.95rem;
  padding: 0.65rem 1.6rem;
  border-radius: 24px;
  border: 2px solid var(--reader-accent);
  background: var(--reader-accent);
  color: #fff;
  cursor: pointer;
  transition: background 0.2s, color 0.2s, border-color 0.2s, opacity 0.2s;
}

.reader-btn:hover { background: #124075; border-color: #124075; }

.reader-btn.reader-btn-secondary {
  background: transparent;
  color: var(--reader-accent);
}

.reader-btn.reader-btn-secondary:hover {
  background: rgba(26, 86, 160, 0.08);
}

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

/* ── Settings grid ───────────────────────────────────────── */

.reader-settings {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 1.4rem 2rem;
  padding-top: 1.4rem;
  border-top: 1px solid var(--reader-border);
}

.reader-field {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 0.4rem;
}

.reader-field label {
  font-family: "Lexend", sans-serif;
  font-size: 1.3rem;
  letter-spacing: 0.5px;
  color: #555;
  display: flex;
}

/* Pushes the WPM readout to the right without a parent justify-content —
   that leaked into .reader-toggle labels below (also plain <label>s) and
   shoved each toggle's switch and text apart to opposite ends of the row. */
.reader-field .reader-field-value {
  margin-left: auto;
  color: var(--reader-accent);
  font-weight: 600;
}

.reader-speed-stages {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.reader-speed-btn {
  font-family: "Oswald", sans-serif;
  font-size: 1.05rem;
  letter-spacing: 0.5px;
  padding: 0.35rem 0.9rem;
  border-radius: 16px;
  /* Unselected = same look as the Reset button (.reader-btn-secondary):
     accent-blue outline and text on a transparent fill. */
  border: 2px solid var(--reader-accent);
  background: transparent;
  color: var(--reader-accent);
  cursor: pointer;
  transition: background 0.2s, color 0.2s, border-color 0.2s;
}

.reader-speed-btn:hover {
  background: rgba(26, 86, 160, 0.08);
}

.reader-speed-btn.is-active {
  background: var(--reader-accent);
  border-color: var(--reader-accent);
  color: #fff;
}

.reader-field select {
  padding: 0.5rem 0.6rem;
  border-radius: 6px;
  border: 1px solid var(--reader-border);
  background: #fff;
  font-family: "Lexend", sans-serif;
  font-size: 1.4rem;
}

.reader-field select:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/* ── Toggle switches (pacing mode / tone color) ─────────── */

.reader-toggle {
  display: flex;
  align-items: center;
  gap: 0.7rem;
}

.reader-toggle-switch {
  position: relative;
  width: 44px;
  height: 24px;
  flex-shrink: 0;
}

.reader-toggle-switch input {
  opacity: 0;
  width: 0;
  height: 0;
}

.reader-toggle-slider {
  position: absolute;
  inset: 0;
  background: #ccc;
  border-radius: 24px;
  cursor: pointer;
  transition: background 0.2s;
}

.reader-toggle-slider::before {
  content: "";
  position: absolute;
  height: 18px;
  width: 18px;
  left: 3px;
  top: 3px;
  background: #fff;
  border-radius: 50%;
  transition: transform 0.2s;
}

.reader-toggle-switch input:checked + .reader-toggle-slider {
  background: var(--reader-accent);
}

.reader-toggle-switch input:checked + .reader-toggle-slider::before {
  transform: translateX(20px);
}

.reader-toggle-switch input:disabled + .reader-toggle-slider {
  opacity: 0.45;
  cursor: not-allowed;
}

.reader-toggle-label {
  font-family: "Lexend", sans-serif;
  font-size: 1.3rem;
  color: #444;
  /* flex column + gap replaces line-height-driven spacing (which read as
     the title sitting "way up" and the description "way down") with a
     small, precise gap between the two. */
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
}

.reader-toggle-label small {
  display: block;
  color: #888;
  font-family: "Lexend", sans-serif;
  font-size: 1.15rem;
  letter-spacing: 0;
  text-transform: none;
  margin: 0;
}

/* ── Tone Colors: Custom 5-color palette ─────────────────────
   Shown (via .is-visible, toggled in reader-engine.js) only while the
   "Tone colors – Custom" checkbox is checked. Ported from the Chrome
   extension's Default/Custom split, see RSVP_WEBSITE_PARITY_NOTES.md
   section 2. */
.reader-tone-custom-colors {
  display: none;
  flex-wrap: wrap;
  gap: 0.6rem;
  margin-top: 0.2rem;
}

.reader-tone-custom-colors.is-visible {
  display: flex;
}

.reader-tone-custom-colors label {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.15rem;
  font-family: "Oswald", sans-serif;
  font-size: 0.75rem;
  color: #888;
  cursor: pointer;
}

.reader-tone-custom-colors input[type="color"] {
  width: 26px;
  height: 26px;
  padding: 0;
  border: 1px solid var(--reader-border);
  border-radius: 5px;
  cursor: pointer;
  background: none;
}

/* ================================================================
   MOBILE
   ================================================================ */

@media (max-width: 640px) {
  .rsvp-stage { height: 160px; }
  .rsvp-stage-row { gap: 0.4rem; }
  .rsvp-arrow { width: 32px; height: 32px; }
  .rsvp-translation-panel { height: calc(160px / 3); }
  .rsvp-translation-text { font-size: 1.4rem; }
  .rsvp-word { font-size: 2rem; padding: 0 1rem; }
  .rsvp-word.is-cjk { font-size: 2.6rem; }
  .reader-panel { padding: 1.4rem 1.2rem; }
  .reader-transport { flex-wrap: wrap; }
  .reader-btn { flex: 1; text-align: center; }

  /* ── Language switcher collapses into a globe-icon trigger + dropdown
     panel here instead of the flat pill row wrapping across lines. Toggle
     wiring is in reader-engine.js (#lang-trigger click handler). ── */

  #lang-trigger {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    background-color: var(--lang-idle-bg);
    color: var(--lang-idle-fg);
    border: none;
    border-radius: 5px;
    /* content-box (not the UA default border-box for <button>) so that the
       explicit height below adds to the padding the same way .btn-lang's
       auto height does, instead of the padding eating into it. */
    box-sizing: content-box;
    /* height (not just padding) pinned to a single line's em-box so this
       matches .btn-lang's own (padding + line-height:1) height exactly —
       fixed rather than auto so the enlarged icon below can't stretch it. */
    height: 1em;
    line-height: 1;
    padding: 0.3rem 0.7rem;
    font-size: 70%;
    font-family: "Oswald", sans-serif;
    text-transform: uppercase;
    cursor: pointer;
  }

  #lang-trigger .lang-trigger-icon-svg {
    /* Larger than the button's own 1em content height on purpose — it
       overflows the fixed-height box slightly (centered by align-items)
       rather than growing it. */
    width: 1.5em;
    height: 1.5em;
    flex-shrink: 0;
  }

  #lang-trigger .lang-trigger-caret {
    font-size: 0.8em;
    transition: transform 0.2s ease;
  }

  #site-languages.open #lang-trigger {
    color: var(--lang-active-fg);
    background-color: var(--lang-active-bg);
  }

  #site-languages.open #lang-trigger .lang-trigger-caret {
    transform: rotate(180deg);
  }

  #lang-dropdown {
    display: none;
    flex-direction: column;
    align-items: flex-end;
    gap: 0.5rem;
    position: absolute;
    top: 100%;
    right: 0;
    margin-top: 0.5rem;
    padding: 0.75rem;
    background: var(--reader-panel);
    border-radius: 0.75rem;
    box-shadow: 0 12px 28px rgba(0, 0, 0, 0.25);
    z-index: 1000;
  }

  #site-languages.open #lang-dropdown {
    display: flex;
  }

  #lang-dropdown .btn-lang {
    margin: 0;
  }
}

/* Phonetic annotation ZH/JP I/II toggles, stacked in one .reader-field
   cell. A language pair dims while the word-language pill doesn't match
   it (see updatePhoneticTogglesApplicable in reader-engine.js). */
.reader-toggle--stacked + .reader-toggle--stacked {
  margin-top: 0.8rem;
}
.reader-toggle.is-inapplicable {
  opacity: 0.4;
  cursor: not-allowed;
}

/* Voice dropdown's label, sharing a cell with the Voice-over toggle. */
.reader-field .reader-voice-label {
  margin-top: 0.8rem;
}
.reader-field .reader-toggle--spaced {
  margin-top: 1.2rem;
}
