/* =====================================================================
   NGẤM — Czech flashcards for Vietnamese speakers
   ---------------------------------------------------------------------
   ALL the app's styling lives in this one stylesheet. (The app was split
   2026-07-26: markup in index.html, styles here, behaviour in js/ —
   config → data → decks → engine. Same code, new homes.)
   System fonts only — no web fonts, no libraries, no frameworks.
   Warm "neighbourhood shop" palette: cream background, warm brown text,
   amber accents.
   Green = correct; red = chosen wrong answer with punitive feedback enabled.
   ===================================================================== */

/* ---- SPACING SCALE + SAFE AREAS -------------------------------------
   One tunable scale for every padding/margin/gap below. Density is tuned
   here, in one place — not rule by rule. ----------------------------- */
:root {
  --sp-1: 4px;
  --sp-2: 8px;
  --sp-3: 12px;
  --sp-4: 16px;
  --sp-5: 24px;

  /* Safe-area insets: iPhone 13/14/16 have a notch or Dynamic Island at
     the top and a home indicator bar at the bottom. env() reads the real
     inset from iOS. The 0px fallback covers Android and desktop. */
  --safe-top: env(safe-area-inset-top, 0px);
  --safe-bottom: env(safe-area-inset-bottom, 0px);

  /* TABS-WEB-1 (2026-09-13): the bottom tab bar's height ABOVE the
     safe-area inset — the 52px tap floor plus its 2px top border.
     #picker / .tab-screen pad their bottom by this so content can always
     scroll clear of the bar (.tabbar, in the TAB BAR section). Declared
     HERE, with the scale and the insets, because this file tunes layout
     in one place — not beside the rule that happens to use it. */
  --tabbar-h: 54px;
}

/* Make width/height include padding+border so sizing is predictable. */
* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  /* System font stack: uses whatever native font the phone already has. */
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
               "Helvetica Neue", Arial, sans-serif;
  background: linear-gradient(180deg, #fffaf0 0%, #fff0d4 100%);
  color: #2a1f12;
  min-height: 100vh;    /* fallback for older browsers */
  min-height: 100dvh;   /* dvh tracks the real visible area on mobile, so the
                           body is never taller than the screen (no stray scroll) */
  -webkit-font-smoothing: antialiased;
  line-height: 1.4;   /* the floor, not just a default — Vietnamese diacritics
                         stack above and below the letter and collide tighter */
}

/* NSR-2 (2026-08-03): a downward swipe now navigates BACK a card. Without
   this, Android Chrome claims that same gesture for pull-to-refresh and
   reloads the page — destroying all in-memory progress (state, answers).
   `contain` keeps normal scrolling but stops the refresh gesture.
   html is in the selector ON PURPOSE: since Chrome 93 the viewport reads
   overscroll-behavior from the ROOT element only — on body alone the rule
   is silently ignored and pull-to-refresh stays live. */
html, body { overscroll-behavior-y: contain; }

/* THE SCREEN SHELL — exactly one screen tall, never scrolls. Everything
   inside (wordmark, card, hint) shares this fixed height via flex, so the
   layout fits Chrome-with-toolbar today AND grows into the taller viewport
   when this ships as a PWA later. */
main {
  height: 100vh;        /* fallback for older browsers */
  height: 100dvh;       /* dvh = dynamic viewport height; tracks the real
                           visible area on mobile Safari/Chrome */
  display: flex;
  flex-direction: column;
  overflow: hidden;     /* the shell itself never grows a scrollbar */
  max-width: 440px;     /* widest iPhone (16 Pro Max) — phone-shaped on
                           desktop, edge-to-edge on every real phone */
  margin-inline: auto;
  /* Base padding, plus extra at top/bottom to clear the notch and the
     home indicator. */
  padding: calc(var(--sp-3) + var(--safe-top))
           var(--sp-4)
           calc(var(--sp-3) + var(--safe-bottom))
           var(--sp-4);
}

/* Small, quiet wordmark. Not a menu — just identity. */
.wordmark {
  text-align: center;
  font-weight: 800;
  font-size: 20px;
  letter-spacing: 0.3px;
  color: #92400e;
  margin: var(--sp-1) 0 var(--sp-4);
}
/* In the deck view the wordmark costs ~50px of chrome on a screen that
   should be one decision point, so it's hidden there. "Deck view" needs no
   JS: the picker sits before the wordmark in the DOM, and once it's been
   dismissed (hidden) the deck is what's on screen. On the entry screen the
   picker is also hidden, but the opaque #entry overlay covers the wordmark
   there anyway, so this rule changes nothing visible outside the deck. */
#picker[hidden] ~ .wordmark { display: none; }

/* The deck area under the wordmark: takes all remaining screen height and
   hands it to the card. min-height:0 is required — without it a flex item
   refuses to shrink below its own content size and would blow out the shell. */
#app {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

/* =====================================================================
   CARD
   The renderer fills #app with one .card at a time.
   ===================================================================== */
.card {
  position: relative;
  /* Vertical flex column that exactly fills the space #app gives it and
     never overflows: the scene flexes, the options never shrink, and the
     reveal scrolls inside itself when the insight is long. */
  flex: 1 1 auto;
  min-height: 0;             /* lets the card shrink when the hint appears below it */
  display: flex;
  flex-direction: column;
  background: #ffffff;
  border-radius: 20px;
  box-shadow: 0 6px 20px rgba(60, 40, 10, 0.10);
  overflow: hidden;          /* keeps child content inside the rounded corners */
}

/* ---- SCENE PANEL ----------------------------------------------------
   For now this is a PLACEHOLDER BOX showing the caption text.
   >>> To use real media later, replace the .scene-caption line in the
       renderer's HTML with a <video> or <img> (see the comment in JS). <<<
   -------------------------------------------------------------------- */
.scene {
  position: relative;             /* anchors the ▶ play button */
  /* The flexible region: it absorbs all leftover height before an answer
     (no hardcoded height — it also grows into the taller PWA viewport),
     and the huge flex-shrink makes it the FIRST region to give height back
     when the reveal appears, before the reveal itself is squeezed.
     min-height:0 is required — without it a flex item refuses to shrink
     below its own content size and would blow out the card. */
  flex: 1 999 auto;
  min-height: 0;
  overflow-y: auto;               /* if ever squeezed below its own text, scroll — never clip diacritics */
  display: flex;
  flex-direction: column;         /* stack the header line over the description line */
  align-items: center;
  justify-content: center;
  justify-content: safe center;   /* "safe" keeps the text top reachable if it overflows */
  text-align: center;
  padding: var(--sp-4) var(--sp-3);
  background: #fef3c7;            /* warm amber tint = "media goes here" */
  border-bottom: 1px solid #f5e6c8;
}
.scene-caption {
  /* Fluid type: never below 13px, never above 15px, ~14px on a 390px
     screen. Now a small eyebrow label — the description below it is the
     bigger line (it carries the scene cue, per PRD §5.1). */
  font-size: clamp(13px, 3.6vw, 15px);
  font-weight: 700;
  color: #5a4220;
}
/* Second scene line: smaller + lighter than the header, so the header reads
   as a short label and this reads as the scene. Hidden when a card has none. */
.scene-desc {
  margin-top: var(--sp-2);
  font-size: clamp(15px, 4.35vw, 18px);   /* ~17px at 390px — the main scene line */
  font-weight: 500;
  line-height: 1.4;
  color: #8a7350;                /* lighter warm tone than the header */
}
.scene-desc[hidden] { display: none; }   /* no empty line when there's no description */

/* The ▶ button only appears if the browser BLOCKED the scene audio.
   Tapping it plays the sound and unlocks audio for the rest of the visit. */
.scene-play {
  position: absolute;
  width: 64px;
  height: 64px;
  border-radius: 50%;
  border: none;
  background: rgba(217, 119, 6, 0.92);  /* amber */
  color: #ffffff;
  font-size: 26px;
  line-height: 64px;
  cursor: pointer;
  box-shadow: 0 4px 14px rgba(217, 119, 6, 0.4);
  -webkit-tap-highlight-color: transparent;
}
.scene-play[hidden] { display: none; }   /* honour the hidden attribute */

/* Press-to-play speaker — sits between scene header and answers */
.replay-block {
  flex: 0 0 auto;   /* fixed region: never squeezed by the flexing scene/reveal */
  display: flex;
  flex-direction: column;
  align-items: center;
  margin: var(--sp-3) 0; /* breathing room above (scene) and below (answers) */
}
.replay-block[hidden] { display: none; }   /* honour the hidden attribute */
.replay-btn {
  width: 56px;    /* still well past the 44px tap-target floor */
  height: 56px;
  border-radius: 50%;
  border: none;
  background: #F5A623; /* warm amber */
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  box-shadow: 0 4px 14px rgba(245, 166, 35, 0.5); /* soft amber glow */
  transition: transform 90ms ease-out; /* fast spring-back on release — transform ONLY */
  touch-action: manipulation;             /* drop the ~300ms tap delay on mobile */
  -webkit-tap-highlight-color: transparent; /* no gray flash iOS draws on tap */
  -webkit-user-select: none;              /* iOS Safari needs the prefix for this to work */
  user-select: none;                      /* don't text-select the icon on a long press */
}
/* When pressed, the circle scales down so it visibly "presses in" under the finger,
   then springs back via the transform transition above. Touch has no :hover, so this
   :active scale is the whole press effect — do NOT add a :hover rule here. */
.replay-btn:active { transform: scale(0.92); }
.replay-label {
  margin-top: var(--sp-2);
  font-size: 14px;
  color: #8A8A8A; /* soft gray — non-punitive */
}

/* ---- ANSWER BUTTONS -------------------------------------------------
   Stacked, full-width, large tap targets — the ONLY tappable things.
   -------------------------------------------------------------------- */
.options {
  flex: 0 0 auto;                 /* answer buttons NEVER shrink — tap targets are sacred */
  /* Preserve comfortable spacing around the answer buttons. */
  padding: var(--sp-2) var(--sp-2) var(--sp-1);
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
}
.option {
  background: #ffffff;
  border: 2px solid #e8dac4;
  border-radius: 14px;
  padding: var(--sp-3) var(--sp-4);
  font-family: inherit;
  font-size: clamp(15px, 4.2vw, 17px);  /* fluid: ~16.4px on a 390px screen */
  font-weight: 600;
  color: #2a1f12;
  text-align: left;
  line-height: 1.4;                 /* floor for Vietnamese diacritics — never tighter */
  min-height: 52px;                 /* hard floor for wet/gloved thumbs mid-shift (never below 52px) */
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: border-color 0.15s, background-color 0.15s, transform 0.1s;
}
/* Gentle press feedback. No :hover-only behaviour (phones can't hover). */
.option:active:not(:disabled) { transform: scale(0.985); }
.option:disabled { cursor: default; }

/* CORRECT — friendly green for the right answer. */
.option.correct {
  background: #d1fae5;
  border-color: #15803d;
  color: #14532d;
}
/* QA-FEEDBACK-2 (2026-09-16): user correction restores red highlights
   and the error buzzer. Keep the silent fallback warm gray. */
.option.chosen {
  background: #f2efeb;
  border-color: #a8a29e;
  color: #57534e;
}
.option.is-wrong {
  background: #FDECEC;
  border-color: #E04B4B;
  color: #B02020;
}

/* ---- REVEAL ---------------------------------------------------------
   The Czech phrase (larger) with its Vietnamese meaning (smaller).
   -------------------------------------------------------------------- */
.reveal {
  /* Shares the card's leftover height with the scene (which collapses
     first) and scrolls INSIDE itself when the insight is long — so the
     card and the page never grow a scrollbar, and the insight is always
     reachable. */
  flex: 0 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: var(--sp-2) var(--sp-4) var(--sp-5);
  text-align: center;
}
.reveal[hidden] { display: none; }
.reveal-cz {
  font-size: 27px;
  font-weight: 800;
  color: #1c1410;
  letter-spacing: 0.2px;
}
.reveal-vi {
  margin-top: var(--sp-1);
  font-size: clamp(16px, 4.6vw, 20px);   /* fluid: ~18px on a 390px screen */
  font-weight: 600;
  color: #6b5640;
}

/* An isolated preview can invite a spoken attempt before exposing the
   existing two-turn reply. These elements remain hidden in ordinary cards. */
.customer-label {
  margin-bottom: var(--sp-1);
  font-size: 14px;
  font-weight: 700;
  color: #5a4220;
}
.speaking-step { margin-top: var(--sp-4); }
.speaking-step[hidden], .customer-label[hidden] { display: none; }
.speaking-prompt {
  margin: 0 0 var(--sp-3);
  font-size: 16px;
  line-height: 1.5;
  color: #1c1410;
}
.model-reply-btn {
  width: 100%;
  min-height: 48px;
  padding: 12px 16px;
  border: 0;
  border-radius: 12px;
  background: #1c1410;
  color: #fff;
  font: inherit;
  font-weight: 700;
  line-height: 1.4;
  cursor: pointer;
}
.model-reply-btn:focus-visible { outline: 3px solid #b98247; outline-offset: 3px; }

/* ---- REPLY BOX (two-turn cards only) --------------------------------
   The worker's own line back to the customer, shown inside the reveal as
   its own separate beat. Ported from restaurace.html MINUS its listen
   button: here the reply auto-plays as one more link in the reveal's audio
   chain, so there is nothing to tap (DNP-2, one decision point per screen —
   the learner already made theirs). Single-turn cards never un-hide this,
   so there is no empty box and no spacing gap.
   -------------------------------------------------------------------- */
.reply-box {
  margin-top: var(--sp-4);
  padding: var(--sp-3);
  background: #fdf6e3;            /* soft warm tint, same as the insight box */
  border: 1px solid #efe2c4;
  border-radius: 12px;
  text-align: center;
}
.reply-box[hidden] { display: none; }   /* honour the hidden attribute (no empty box) */
.reply-label {
  /* Small lead-in label: "Mình trả lời:" */
  font-size: clamp(13px, 3.6vw, 15px);
  font-weight: 700;
  color: #5a4220;
}
.reply-cz {
  /* The Czech reply — the larger line, like a small reveal-cz. */
  margin-top: var(--sp-2);
  font-size: 21px;
  font-weight: 800;
  color: #1c1410;
  letter-spacing: 0.2px;
}
.reply-vi {
  /* The Vietnamese meaning — the smaller line under it. */
  margin-top: var(--sp-1);
  font-size: clamp(14px, 4vw, 17px);
  font-weight: 600;
  color: #6b5640;
}

/* ---- VOCAB LIST -----------------------------------------------------
   One small left-aligned gloss line per option that has a vocab string
   ("objednala jsem se — đã đặt lịch rồi"). Appears together with the
   reveal text above; on cards with no vocab the container keeps its
   hidden attribute — no empty box, no spacing gap.
   -------------------------------------------------------------------- */
.vocab-list {
  margin-top: var(--sp-4);
  text-align: left;
  font-size: clamp(13px, 3.6vw, 15px);   /* fluid: ~14px on a 390px screen */
  font-weight: 600;
  line-height: 1.5;
  color: #6b5640;                /* muted warm brown — same as .reveal-vi and .insight */
}
.vocab-list[hidden] { display: none; }   /* honour the hidden attribute (no empty box) */
.vocab-line { padding: 2px 0; }

/* ---- CULTURAL INSIGHT PANEL -----------------------------------------
   Deliberately DIFFERENT from the translation above: a soft warm tint,
   a border, rounded corners and LEFT-aligned note text (the translation
   is centred). It carries a 💡 marker — a cultural "why", not a meaning.
   Starts invisible (opacity:0) and fades in via the .show class.
   -------------------------------------------------------------------- */
.insight {
  margin-top: var(--sp-4);
  padding: var(--sp-3);
  background: #fdf6e3;            /* soft warm tint, distinct from the white card */
  border: 1px solid #efe2c4;
  border-radius: 12px;
  text-align: left;              /* reads like a note, not a centred headline */
  font-size: clamp(13px, 3.6vw, 15px);   /* fluid: ~14px on a 390px screen */
  font-weight: 600;
  line-height: 1.45;
  color: #6b5640;
  opacity: 0;                    /* hidden until the fade-in runs */
}
.insight[hidden] { display: none; }     /* honour the hidden attribute (no empty box) */
.insight-adaptive strong { display: block; margin-bottom: var(--sp-2); }
.insight.show {
  opacity: 1;                    /* settle fully visible after the animation */
  animation: insightFadeIn 0.5s ease-out;
}
/* Gentle fade with a tiny upward rise — nothing jarring. */
@keyframes insightFadeIn {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* ---- SWIPE HINT -----------------------------------------------------
   Hidden until swiping unlocks (after SWIPE_LOCK_MS), then it appears
   once to tell the user they can move on.
   -------------------------------------------------------------------- */
.hint {
  flex: 0 0 auto;                /* sits under the card; the card shrinks to make room */
  text-align: center;
  font-size: clamp(13px, 3.6vw, 15px);   /* fluid: ~14px on a 390px screen */
  font-weight: 700;
  color: #92400e;
  margin: var(--sp-4) 0 0;
  /* A soft up-and-down nudge so the eye notices it. */
  animation: hintFloat 1.6s ease-in-out infinite;
}
.hint[hidden] { display: none; }
@keyframes hintFloat {
  0%, 100% { transform: translateY(0); opacity: 0.85; }
  50%      { transform: translateY(-5px); opacity: 1; }
}

/* ---- END MESSAGE (after the last card) ------------------------------ */
.end {
  text-align: center;
  padding: var(--sp-5) var(--sp-4);
  font-size: 26px;
  font-weight: 800;
  color: #5a4220;
}

/* DECK-EXIT-1 (2026-09-13): the way out of a finished deck. The pill
   itself is .flash-exit — the approved finish-pane button, reused so every
   surface ends with the same control instead of a parallel style. These
   two declarations only PLACE it: #app is a flex column with the default
   align-items: stretch, so without align-self the 200px pill would be
   pulled to the full width of the screen. .end above is untouched — the
   line of text sits exactly where it always did, with the button under
   it. */
.end-exit {
  align-self: center;
  margin-top: var(--sp-4);
}

/* RECAP-1 (2026-09-14): a quiet, readable summary inside the existing
   scrollable finish pane. No animations, no new answer-feedback effects. */
.session-finish .end { padding: var(--sp-3) 0; }
.session-recap {
  flex: 0 0 auto;
  width: 100%;
  max-width: 440px;
  padding: 20px;
  border: 1px solid #e8dac4;
  border-radius: 20px;
  background: #fff9eb;
  color: #5a4220;
  text-align: left;
  overflow-wrap: anywhere;
}
.recap-label { margin: 0 0 8px; color: #8a7350; font-size: 12px; font-weight: 800; letter-spacing: 1px; }
.recap-headline { margin: 0 0 12px; font-size: 23px; line-height: 1.35; }
.recap-observation, .recap-next { margin: 10px 0; font-size: 17px; line-height: 1.55; }
.recap-next { padding-top: 12px; border-top: 1px solid #e8dac4; }
.recap-disclosure, .recap-status { margin: 12px 0 0; font-size: 13px; line-height: 1.5; color: #766247; }
.recap-ask {
  min-height: 52px;
  width: 100%;
  margin-top: 12px;
  padding: 12px;
  border: 1px solid #b58d57;
  border-radius: 14px;
  background: #ffedc7;
  color: #5a4220;
  font: inherit;
  font-weight: 700;
  cursor: pointer;
}
.recap-ask:focus-visible { outline: 3px solid #97470f; outline-offset: 3px; }
.recap-ask[hidden], .recap-disclosure[hidden] { display: none; }
.mistake-coach-panel { margin-top: 18px; border-top: 1px solid #dfcbab; padding-top: 18px; }
.mistake-coach-panel[hidden] { display: none; }
.mistake-coach-observation, .mistake-coach-next { margin: 8px 0; font-size: 16px; line-height: 1.5; }
.mistake-coach-source { margin: 10px 0 0; color: #766247; font-size: 12px; line-height: 1.5; }

/* NEXT-1: a plan stays readable in the finish pane on a small phone. */
.next-session-panel { margin-top: 18px; border-top: 1px solid #dfcbab; padding-top: 18px; }
.next-session-counts { font-weight: 700; font-size: 14px; line-height: 1.6; }
.next-session-explanation { margin: 10px 0; font-size: 16px; line-height: 1.5; }
.next-session-source { color: #766247; font-size: 12px; line-height: 1.5; }
.next-session-start { min-height: 52px; width: 100%; padding: 12px; border: 0;
  border-radius: 14px; background: #805025; color: #fff9eb; font: inherit; font-weight: 700; cursor: pointer; }
.next-session-start:focus-visible { outline: 3px solid #97470f; outline-offset: 3px; }
.next-session-panel[hidden], .next-session-start[hidden], .recap-next[hidden] { display: none; }
.next-session-row { display: flex; flex-direction: column; gap: 6px; width: 100%;
  min-height: 72px; margin: 0 0 20px; padding: 16px; text-align: left; font: inherit;
  border: 1px solid #dfcbab; border-radius: 18px; color: #5a4220; background: #fff9eb; cursor: pointer; }
.next-session-row[hidden] { display: none; }
.next-session-row-title { font-weight: 700; }
.next-session-row-counts { font-size: 13px; line-height: 1.5; }
.next-session-row:focus-visible { outline: 3px solid #97470f; outline-offset: 3px; }

/* WEEK-1: an optional saved reflection beneath the progress worklist. */
.weekly-reflection { width: 100%; max-width: 520px; margin: 24px auto; padding: 20px;
  border: 1px solid #e8dac4; border-radius: 20px; background: #fff9eb; color: #5a4220; overflow-wrap: anywhere; }
.weekly-reflection[hidden], .weekly-report[hidden] { display: none; }
.weekly-toggle { display: flex; gap: 16px; align-items: center; justify-content: space-between;
  min-height: 52px; font-weight: 700; line-height: 1.5; }
.weekly-enable { flex: 0 0 auto; width: 28px; height: 28px; accent-color: #805025; }
.weekly-note { color: #766247; font-size: 13px; line-height: 1.5; }
.weekly-report { margin-top: 20px; padding-top: 12px; border-top: 1px solid #dfcbab; }
.weekly-dates { font-size: 13px; font-weight: 700; }
.weekly-headline { font-size: 23px; line-height: 1.4; }
.weekly-strength, .weekly-practice, .weekly-next_step { font-size: 16px; line-height: 1.6; }
.weekly-refresh { min-height: 52px; padding: 12px 18px; border: 1px solid #b58d57;
  border-radius: 14px; color: #5a4220; background: #ffedc7; font: inherit; cursor: pointer; }
.weekly-refresh:focus-visible, .weekly-enable:focus-visible { outline: 3px solid #97470f; outline-offset: 3px; }

/* Load sequence for the entry screen. Only opacity and transform are animated
   — both are GPU-composited, so this never triggers layout on a low-end phone. */
@keyframes entryRise {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* Opening screen — full-screen, poster layout, ONE tappable button only. */
#entry {
  position: fixed;
  inset: 0;
  z-index: 10;                 /* sits above the deck underneath */
  display: flex;
  flex-direction: column;
  align-items: center;
  /* Was `center`. The wordmark now sits in the upper third and the CTA is
     pushed to the bottom (see #entryBtn), so the screen reads as a poster
     rather than a dialog box — and the button lands where a thumb actually
     rests on a 6.7" phone. */
  justify-content: flex-start;
  text-align: center;
  /* Safe-area aware. #entry is position:fixed covering the whole viewport,
     including the notch and the home indicator, so unlike `main` it has to
     handle the insets itself. Without the bottom inset the CTA would sit
     under the home indicator bar once it moves down. */
  padding: calc(var(--safe-top) + 18vh) var(--sp-5)
           calc(var(--safe-bottom) + var(--sp-5));
  /* A radial wash instead of a vertical one: the screen now has a light source
     sitting behind the wordmark, rather than reading as flat off-white. Still
     opaque so the deck underneath never peeks through. */
  background: radial-gradient(ellipse 120% 80% at 50% 32%,
              #fffdf7 0%, #ffeed2 55%, #f6dfb9 100%);
}
#entry[hidden] { display: none; }

.entry-mark {
  font-weight: 800;
  /* Fluid, matching the clamp() pattern already used on the card text.
     Fixed 56px overflowed on a 320px iPhone SE. */
  font-size: clamp(48px, 15vw, 68px);
  /* NEGATIVE tracking. Large display type needs letters pulled together;
     the old +0.5px was pushing them apart, which is why it read as
     "system UI font at large size" rather than as a logotype. */
  letter-spacing: -1.8px;
  color: #92400e;              /* same brown as .wordmark */
  animation: entryRise 420ms ease-out backwards;
}

/* The ấ, in the amber accent. This is the one deliberately bold thing on the
   screen — everything else stays quiet so it carries. */
.mark-accent { color: #d97706; }
.entry-sub {
  margin-top: var(--sp-4);     /* was sp-3 — the mark is bigger now, needs air */
  font-size: clamp(14px, 3.9vw, 16px);
  font-weight: 500;            /* was 600 — steps back so the mark leads */
  /* POSITIVE tracking here, opposite of .entry-mark. Small text opens up,
     large text closes in. The contrast between the two is the point. */
  letter-spacing: 0.4px;
  color: #8a7350;
  animation: entryRise 420ms ease-out 90ms backwards;
}
#entryBtn {
  /* Was var(--sp-5). `auto` eats all the leftover space in the flex column,
     pushing the button to the bottom edge (above the safe-area padding). */
  margin-top: auto;
  min-width: 200px;
  min-height: 58px;            /* comfortable thumb target, matches answer buttons */
  padding: var(--sp-4) var(--sp-5);
  border: none;
  border-radius: 16px;
  /* Slight top-to-bottom gradient so the fill has a little dimension. */
  background: linear-gradient(180deg, #e8890b 0%, #d97706 100%);
  color: #ffffff;
  font-family: inherit;        /* system font — no new fonts */
  font-size: 19px;
  font-weight: 800;
  cursor: pointer;
  /* Two layers instead of one: a tight contact shadow that sits the button on
     the surface, plus a wide soft glow. One blurry shadow reads as a sticker. */
  box-shadow: 0 1px 2px rgba(120, 53, 15, 0.28),
              0 8px 24px rgba(217, 119, 6, 0.32);
  transition: transform 90ms ease-out;
  touch-action: manipulation;             /* drop the ~300ms mobile tap delay */
  -webkit-tap-highlight-color: transparent;
  /* backwards, NOT both: `both` would leave the animation's final transform
     applied forever, which permanently overrides #entryBtn:active's
     scale(0.96) and silently kills the press feedback. `backwards` only
     applies the `from` state during the delay, then hands control back. */
  animation: entryRise 420ms ease-out 180ms backwards;
}
#entryBtn:active { transform: scale(0.96); }   /* gentle press; no :hover (phones can't hover) */

@media (prefers-reduced-motion: reduce) {
  /* Some people get motion sickness or vestibular symptoms from
     moving UI. The screen still works, it just appears instead of
     rising. Kept here rather than merged into the block in the
     answer-buttons section so it sits next to the rules it governs. */
  .entry-mark, .entry-sub, #entryBtn { animation: none; }
}

/* =====================================================================
   JOB PICKER
   Second onboarding screen — shown after the entry screen, before any card.
   Same full-screen overlay pattern as #entry. Five live jobs; coworkers
   still locked. Locked is calm and muted — never an error state.
   TABS-WEB-1 (2026-09-13): #picker is now the "Học Dễ" tab of three.
   The other two (#progress, #account in index.html) share this exact
   overlay via .tab-screen — one rule, three screens, so they can never
   drift apart in wash, padding or scroll behaviour.
   ===================================================================== */
#picker, .tab-screen {
  position: fixed;
  inset: 0;
  z-index: 10;                 /* sits above the deck, like #entry */
  display: flex;
  flex-direction: column;
  align-items: center;
  /* FLASH-1 (2026-08-20): the flashcard section below the job list makes
     this screen TALLER than a small phone, so it must scroll. It was
     justify-content: center — but a center-justified flex scroller
     overflows past BOTH edges, and everything above the scroll origin
     can never be scrolled into view. (`safe center` fixes that only on
     engines that parse `safe`, which iOS Safari long did not — and this
     app lives on iPhones.) So: start-justified, with auto margins on
     the first and last children (.picker-head / .flash-section) doing
     the centering instead — they absorb the free space when the screen
     is tall (visually identical to center) and collapse to zero when it
     is not, leaving every row reachable in every engine. */
  justify-content: flex-start;
  overflow-y: auto;
  /* Same safe-area reasoning as #entry: position:fixed over the full viewport,
     and six job rows can reach the bottom of a small screen. */
  /* TABS-WEB-1: the bottom edge also clears the fixed tab bar — the bar
     covers the screen's last --tabbar-h pixels (the token is in :root at
     the top of this file), so without this the last flash row could
     scroll to a stop underneath it. The home-indicator inset is paid HERE
     for the content and AGAIN by the bar for itself; that is one inset
     each, not a double payment — they are two different boxes. */
  padding: calc(var(--safe-top) + var(--sp-5)) var(--sp-5)
           calc(var(--safe-bottom) + var(--tabbar-h) + var(--sp-5));
  /* PICKER-REF-1 (2026-09-13): the reference warms gradually from an
     ivory header to pale amber behind the review panel. */
  background: linear-gradient(180deg, #fffdf7 0%, #ffeed2 55%, #f6dfb9 100%);
}
#picker[hidden], .tab-screen[hidden] { display: none; }

/* Reference header: top-aligned on phones, centred as a whole on taller
   screens. The gear sits in the gap below the title and overlaps only
   the banner's top edge. No imitation iOS status bar: safe areas come
   from the device, so the title always stays below browser/system UI. */
#picker {
  padding-top: calc(var(--safe-top) + var(--sp-4));
  /* Keep the full phone column even on systems with a persistent
     scrollbar. The picker still scrolls with touch, wheel or keyboard. */
  scrollbar-width: none;
}
#picker::-webkit-scrollbar { display: none; }
.picker-head {
  position: relative;
  z-index: 1;
  flex-shrink: 0;
  width: 100%;
  max-width: 360px;
  margin-top: auto;
  margin-bottom: var(--sp-5);
}
/* The single question on this screen. */
.picker-title {
  margin: 0;
  text-align: center;
  /* Fluid, same clamp() pattern as the entry screen. Deliberately smaller and
     lighter than .entry-mark — this is a question being asked, not a brand mark,
     so it should not compete with the wordmark the user just saw. */
  font-size: clamp(20px, 5.65vw, 24px);
  font-weight: 800;
  letter-spacing: -0.3px;   /* mild negative — it is display type, but not large */
  color: #92400e;              /* same brown as .wordmark */
  animation: entryRise 340ms ease-out backwards;
}
.picker-settings {
  position: absolute;
  top: calc(100% - 3px);
  right: -6px;
  width: 52px;
  height: 52px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 4px solid rgba(255, 255, 255, 0.42);
  border-radius: 50%;
  background: #0098f5;
  color: #fff;
  box-shadow: 0 5px 14px rgba(45, 62, 70, 0.28);
  cursor: pointer;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
  transition: background-color 0.15s, transform 0.1s;
}
.picker-settings:active { background: #0088dd; transform: scale(0.96); }
/* Visible keyboard focus stays distinct from the amber borders. */
#picker button:focus-visible, .tabbar-tab:focus-visible {
  outline: 3px solid #92400e;
  outline-offset: 3px;
}
/* PICKER-WEB-1 (2026-09-13): a TWO-COLUMN GRID of tiles (after mobile's
   PICKER-P2), no longer stacked rows. minmax(0, 1fr), not bare 1fr: a bare
   1fr track refuses to shrink below its content, so one long name could
   push the other column off a 320px screen. Gap --sp-3 both ways, as on
   mobile. max-width 360 KEPT — it is what caps the grid at two ~174px
   tiles on anything wider than a phone. */
.job-list {
  flex-shrink: 0;
  width: 100%;
  max-width: 360px;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  align-items: start;
  gap: var(--sp-3);
}
/* One job TILE (PICKER-WEB-1 — a full-width row until then): the tinted
   icon square at the top, the name pushed to the bottom by its own auto
   margin (.job-name), and on the locked tile the "Sắp có" pill under the
   name. min-height and NEVER a fixed height: at half the column width
   "Cửa hàng tạp hóa" wraps to two lines and the tile must be free to grow.
   Still styled like an .option answer button so it feels familiar. */
.job-row {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: flex-start;
  min-height: 112px;
  width: 100%;
  background: #fffdf9;
  /* Warm border, not a neutral gray one. A gray border against a warm
     background is the single most common tell of un-designed CSS. 2px kept —
     same weight as .option (see the comment above this rule), so the rows
     still read as family. */
  border: 2px solid #ecdcc0;
  border-radius: 14px;
  /* PICKER-REF-1: flat ivory tiles with a warm outline, as supplied. */
  box-shadow: none;
  padding: var(--sp-3);
  font-family: inherit;        /* system font — no new fonts */
  font-size: clamp(14px, 4vw, 16px);
  font-weight: 700;
  color: #2a1f12;
  text-align: left;
  line-height: 1.4;            /* floor for Vietnamese diacritics — never tighter */
  /* (the old 58px thumb floor is met twice over by the 112px tile above) */
  cursor: pointer;
  transition: background-color 0.15s, transform 0.1s;
  -webkit-tap-highlight-color: transparent;
  /* Same trap as the entry CTA (pass B): `backwards`, never `both` or
     `forwards` — a filling animation would permanently override the press
     scale in .job-row.active:active below. */
  animation: entryRise 340ms ease-out backwards;
}

/* 40ms apart, so the six rows read as one wave rather than six separate
   events. Last row finishes around 600ms, matching the entry screen. */
.job-row:nth-child(1) { animation-delay:  60ms; }
.job-row:nth-child(2) { animation-delay: 100ms; }
.job-row:nth-child(3) { animation-delay: 140ms; }
.job-row:nth-child(4) { animation-delay: 180ms; }
.job-row:nth-child(5) { animation-delay: 220ms; }
.job-row:nth-child(6) { animation-delay: 260ms; }

/* Gentle press feedback on the live row (matches .option:active). No :hover. */
.job-row.active:active { background: #fdf6e3; transform: scale(0.985); }

/* Locked rows: muted and gray — calm "coming soon", NOT an error. Still
   tappable so we can log interest, with only a soft neutral press. */
.job-row.locked {
  /* Sits FLAT: no shadow, so it does not appear raised/available like a live
     row. That is the whole signal — a difference in elevation, not in colour
     temperature. No red, no strikethrough, no reduced opacity below 1: DNP-1
     applies here as much as it does to a wrong answer. */
  background: #fdf9f1;
  border: 2px solid #f0e8d8;   /* 2px, matching the live rows (amendment 4) */
  box-shadow: none;
  color: #a89678;
}
.job-row.locked:active { background: #f5ecdb; }   /* subtle neutral press only */

/* PICKER-WEB-1: the 44×44 tinted square behind the emoji. 14px radius (the
   control radius), not a circle — it echoes the tile's own corner instead
   of introducing a second shape language. line-height is set in px, not
   the 1.4 floor: emoji carry no stacked diacritics, and the floor would
   push the glyph off-centre in a 44px box. */
.job-icon {
  flex-shrink: 0;
  width: 44px;
  height: 44px;
  border-radius: 14px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 24px;
  line-height: 28px;
  background: #f2e6d0;         /* fallback: the neutral pill tint, for a
                                  vertical the map below does not know */
}
/* One flat wash per vertical (mobile theme.js jobTints), all low-chroma so
   the emoji stays the loudest thing in the square. The LOCKED tile keeps
   its full tint: draining the colour out of it would be exactly the
   punitive "this one is broken" reading DNP-1 exists to prevent. */
.job-row[data-vertical="grocery"]    .job-icon { background: #e4f0dc; }
.job-row[data-vertical="nail"]       .job-icon { background: #f7e0ea; }
.job-row[data-vertical="hair"]       .job-icon { background: #dce9f3; }
.job-row[data-vertical="restaurant"] .job-icon { background: #fae6ce; }
.job-row[data-vertical="coworkers"]  .job-icon { background: #e6e2dc; }
.job-row[data-vertical="homesick"]   .job-icon { background: #efe3f2; }
/* The name sits at the BOTTOM of the tile by its own auto margin, not by
   justify-content: space-between — a locked tile has a third child (the
   pill), and space-between would lift its name above its neighbour's. */
.job-name {
  margin-top: auto;
  padding-top: var(--sp-4);
}
/* The locked tile groups its name and pill directly below the icon,
   matching the reference, without making every tile taller. */
.job-row.locked .job-name { margin-top: 0; padding-top: 0; }

/* Small "Sắp có" (coming soon) tag beneath the locked tile's name. */
.job-lock-label {
  margin-top: 0;
  /* A quiet label, not a badge demanding attention. Muted warm tone, small,
     slightly loose tracking so it reads as a caption rather than a button. */
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.3px;
  color: #a89678;
  /* One step deeper than the locked row's press colour (#f5ecdb) so the pill
     stays distinct while the row is pressed — matching it made it vanish. */
  background: #f2e6d0;
  padding: 3px 9px;
  border-radius: 999px;
}

/* ---- FLASHCARD REVIEW SECTION (FLASH-1, 2026-08-20) -----------------
   A second block under the job grid: one row per live deck, always
   unlocked. (P4's queue row opened this list from 2026-09-11 until
   PICKER-WEB-1, 2026-09-13 — it is the .queue-row banner above the grid
   now, styled in its own section below.) Rows are a separate class from
   .job-row on purpose — that class is wired to the deck handler and
   deckIsOpen()'s scan in js/engine.js. PICKER-WEB-1: the rows sit in ONE
   bordered panel with hairline dividers (after mobile PICKER-P1) instead
   of five separate bordered cards. Compact (52px, the tap-target floor)
   so grid + panel + titles still fit a small phone; #picker scrolls if
   they don't.
   -------------------------------------------------------------------- */
.flash-section {
  flex-shrink: 0;
  width: 100%;
  max-width: 360px;               /* same column as .job-list */
  margin-top: 26px;
  margin-bottom: auto; /* FLASH-1: bottom half of the centering pair — see #picker */
  /* One rise for the WHOLE section, landing just after the six job rows'
     wave (last row starts at 260ms) — it reads as one arrival, not five
     more list events. Same trap as everywhere: backwards, never both. */
  animation: entryRise 340ms ease-out 300ms backwards;
}
.flash-section-title {
  /* A quiet eyebrow over the rows — same voice as .scene-caption. */
  font-size: 15px;
  font-weight: 700;
  letter-spacing: 0.3px;
  color: #8a7350;
  margin-bottom: var(--sp-2);
  padding-left: var(--sp-1);
  text-align: left;
}
/* The panel: one warm surface, one hairline border, the rows inside
   divided by hairlines. Horizontal padding lives on the PANEL so the
   dividers stop short of its edges, as on mobile. */
.flash-list {
  display: flex;
  flex-direction: column;
  gap: 0;
  background: #fffdf9;
  border: 1px solid #ecdcc0;      /* warm border, same family as .job-row */
  border-radius: 14px;
  padding: 0 var(--sp-4);
}
.flash-row {
  display: flex;
  align-items: center;
  width: 100%;
  background: transparent;         /* the panel is the surface */
  border: 0;
  border-bottom: 1px solid #ecdcc0;   /* the divider */
  border-radius: 0;
  gap: var(--sp-2);
  padding: var(--sp-3) 0;
  font-family: inherit;
  font-size: clamp(15px, 4.35vw, 17px);
  font-weight: 700;
  color: #2a1f12;
  text-align: left;
  line-height: 1.4;               /* floor for Vietnamese diacritics */
  min-height: 52px;               /* tap-target floor (compact, see above) */
  cursor: pointer;
  transition: background-color 0.15s, transform 0.1s;
  -webkit-tap-highlight-color: transparent;
}
.flash-row:last-child { border-bottom: 0; }   /* no divider under the last row */
.flash-row:active { background: #fdf6e3; transform: scale(0.985); }
.flash-icon { flex-shrink: 0; width: 20px; text-align: center; }
.flash-label { min-width: 0; }
/* The "X / Y" readout — the same right-edge pill as .job-lock-label. */
.flash-stat {
  flex-shrink: 0;
  margin-left: auto;
  white-space: nowrap;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.3px;
  color: #a89678;
  background: #f2e6d0;
  padding: 3px 9px;
  border-radius: 999px;
}

/* ---- QUEUE BANNER (P4, 2026-09-11 → PICKER-WEB-1, 2026-09-13) ---------
   "Thẻ đang chờ bạn" + the count in a badge, above the job grid. Amber-
   tinted rather than cream, so it reads as the one live thing on the
   screen — but still a calm row, not an alert: nothing here is wrong,
   there is just something worth doing (mobile QUEUE-1). Rises with the
   header, not with the tile wave: it is part of the screen's opening
   line, not a list item. -------------------------------------------- */
.queue-row {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  width: 100%;
  max-width: 360px;               /* same column as .job-list */
  min-height: 60px;
  margin-bottom: var(--sp-4);
  background: #fff4c4;
  border: 2px solid #efe2c4;      /* the soft-panel border */
  border-radius: 14px;
  box-shadow: none;
  padding: var(--sp-3) var(--sp-4);
  font-family: inherit;
  font-size: 17px;
  font-weight: 700;
  color: #5a4220;
  text-align: left;
  line-height: 1.4;               /* floor for Vietnamese diacritics */
  cursor: pointer;
  transition: background-color 0.15s, transform 0.1s;
  -webkit-tap-highlight-color: transparent;
  animation: entryRise 340ms ease-out backwards;
}
.queue-row:active { background: #fdf6e3; transform: scale(0.985); }
/* [hidden] needs its own rule — display:flex above would otherwise beat
   the browser's built-in [hidden] { display: none }, and a zero-count
   banner must vanish completely (P4 ruling 3), not sit there empty. */
.queue-row[hidden] { display: none; }
.queue-label { flex: 1 1 auto; min-width: 0; }   /* the label wraps; the badge never does */
/* The badge repeats the label's count visually, as in the reference.
   min-width keeps a one-digit count round instead of oval. */
.queue-count {
  flex-shrink: 0;
  margin-left: var(--sp-2);
  min-width: 28px;
  padding: 3px 9px;
  border-radius: 999px;
  background: #d97706;            /* the ấ accent */
  color: #ffffff;
  font-size: 13px;
  font-weight: 800;
  text-align: center;
  white-space: nowrap;
}

@media (prefers-reduced-motion: reduce) {
  /* Same reasoning as the entry screen block: the list still appears, it just
     does not rise. */
  .picker-title, .queue-row, .job-row, .flash-section { animation: none; }
}

/* =====================================================================
   TAB BAR (TABS-WEB-1, 2026-09-13) — see the decision log in index.html.
   Three fat text tabs on the row surface, fixed to the physical bottom
   edge, paying the home-indicator inset itself. Visible only on the
   picker screen (js/engine.js drives the hidden attribute). Its height
   token, --tabbar-h, lives in the :root block at the top of this file,
   with the spacing scale and the safe-area insets.
   ===================================================================== */
.tabbar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 11;                 /* above the .tab-screen overlays (10) */
  display: flex;
  background: #fffdf9;         /* the row surface */
  border-top: 2px solid #ecdcc0;   /* the row border — 2px like every row, NOT a gray hairline */
  padding-bottom: var(--safe-bottom);
}
.tabbar[hidden] { display: none; }   /* display:flex above would beat the built-in [hidden] */
.tabbar-tab {
  position: relative;          /* anchors the ::before strip */
  flex: 1 1 0;                 /* three equal segments */
  min-height: 52px;            /* tap floor — min, so the inset adds to it, never eats it */
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--sp-2) var(--sp-1);
  border: 0;
  background: transparent;
  font-family: inherit;
  font-size: 15px;
  font-weight: 800;
  letter-spacing: 0.3px;
  line-height: 1.4;            /* floor — "Tiến"/"Độ" stack diacritics */
  color: #8a7350;              /* inactive ink */
  cursor: pointer;
  transition: background-color 0.15s;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
}
/* The active indicator: a 3px strip across the top of the segment, tucked
   under the bar's border. ABSOLUTE, so it reserves nothing — the inactive
   segment's box is identical by construction and the label never moves.
   Painted on EVERY segment (transparent when inactive) so nothing is added
   or removed on a switch. Its signal is geometry, present or absent —
   what survives glare and colour-vision deficiency; the label colour is
   the second channel. No weight change (bold reflows the width and the
   bar would twitch), no scale — calm, like everything else here. */
.tabbar-tab::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 3px;
  background: transparent;
}
.tabbar-tab.active { color: #92400e; }             /* the heading ink */
.tabbar-tab.active::before { background: #92400e; }
.tabbar-tab:active { background: #fdf6e3; }        /* the row press tint; no scale on a bar segment */

/* =====================================================================
   TAB SCREENS (TABS-WEB-1, 2026-09-13) — the content of the "Tiến Độ"
   and "Tài Khoản" tabs. The screens themselves share #picker's overlay
   rule (.tab-screen, above).
   ===================================================================== */
/* "Tiến Độ": the worklist. Top-aligned like mobile's ProgressScreen (no
   centering pair — a list of work starts at the top); the same 360px
   column as the picker's lists. Rows are built by js/engine.js. */
.progress-list {
  width: 100%;
  max-width: 360px;
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
}
/* One deck: the .job-row skin (surface / warm 2px border / control radius),
   stacked as a column because a deck carries up to two lines of numbers.
   NOT a button: nothing here taps, yet. */
.progress-row {
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
  background: #fffdf9;
  border: 2px solid #ecdcc0;
  border-radius: 14px;
  box-shadow: 0 1px 2px rgba(120, 53, 15, 0.05),
              0 4px 12px rgba(146, 64, 14, 0.04);
  padding: var(--sp-4);
  text-align: left;
}
.progress-deck {
  font-size: 17px;
  font-weight: 600;
  color: #2a1f12;
  line-height: 1.4;
}
.progress-line {
  font-size: 14px;
  font-weight: 600;
  color: #6b5640;               /* the muted warm of .reveal-vi / .insight */
  line-height: 1.4;
}
/* ACCOUNT-1 (2026-09-14): warm, grouped settings. The account screen uses
   the existing safe-area / tab-bar scroller; no fixed-height cards. */
.account-content { width: 100%; max-width: 390px; text-align: left; }
.account-content [hidden], .account-dialog [hidden] { display: none !important; }
.account-heading { padding: 5px 0 20px; text-align: center; }
.account-heading h1 { margin: 4px 0; color: #92400e; font-size: 29px; line-height: 1.35; }
.account-heading > p { margin: 0; color: #786044; font-size: 14px; }
.account-heading .account-eyebrow { font-size: 10px; font-weight: 800; letter-spacing: 2px; color: #947347; }
.account-section { margin: 0 0 23px; }
.account-section > h2 { margin: 0 0 8px 4px; font-size: 14px; color: #786044; font-weight: 700; }
.account-card { border: 1px solid #e8d8bc; border-radius: 18px; background: #fffdf9; overflow: hidden; }
.pack-entry { margin: 22px 0; padding: 18px; border: 1px solid #e8d8bc; border-radius: 18px; background: #fff9eb; }
.pack-entry[hidden] { display: none; }
.pack-entry h2 { font-size: 19px; margin: 0 0 8px; }
.pack-entry p { font-size: 14px; line-height: 1.5; }
.pack-dialog { width: min(540px, calc(100vw - 24px)); max-height: calc(100dvh - 24px); padding: 0; border: 1px solid #e8d8bc; border-radius: 20px; background: #fffaf1; color: #3c281d; }
.pack-dialog::backdrop { background: rgb(45 30 15 / 45%); }
.pack-close { display: block; margin: 8px 12px 0 auto; min-height: 44px; padding: 8px 14px; border: 0; background: transparent; color: #92400e; font: inherit; cursor: pointer; }
#profession-pack-body { padding: 8px 20px 24px; overflow-wrap: anywhere; }
#profession-pack-body h2 { margin: 0 0 12px; font-size: 23px; line-height: 1.3; }
#profession-pack-body h3 { font-size: 16px; }
.pack-context, .pack-vi { line-height: 1.55; color: #786044; }
.pack-progress { font-size: 12px; color: #786044; }
.pack-offer { padding: 12px; border-radius: 12px; background: #fff0cb; line-height: 1.5; }
.pack-phrase { background: #fff; border: 1px solid #efe2ca; border-radius: 14px; padding: 16px; margin: 14px 0; }
.pack-cz { font-size: 22px; line-height: 1.45; font-weight: 700; margin: 0 0 8px; }
.pack-phrase .pack-vi { margin: 0; }
.pack-primary, .pack-secondary { width: 100%; min-height: 52px; padding: 12px 14px; margin: 6px 0; border: 1px solid #dec9a7; border-radius: 12px; font: inherit; text-align: left; cursor: pointer; line-height: 1.45; }
.pack-primary { background: #92400e; color: #fffdf9; text-align: center; font-weight: 700; }
.pack-secondary { background: #fffdf9; color: #533b25; }
.pack-primary:disabled, .pack-secondary:disabled { opacity: .55; cursor: default; }
.pack-feedback { margin-top: 12px; }
/* WEAK-1: calm feedback, independent of the free-deck feedback experiment. */
.pack-result { padding: 14px; border-radius: 12px; line-height: 1.5; }
.pack-result-help { background: #eee9e2; color: #574f46; }
.pack-result-correct { background: #e5f0e4; color: #28613b; }
.pack-review-offer { margin: 20px 0; padding: 16px; background: #f4eee3; border-radius: 16px; }
.pack-review-offer h3 { margin-top: 0; }
.pack-memory-state { display: inline-block; padding: 5px 10px; background: #eee9e2; color: #574f46; border-radius: 20px; font-size: 13px; }
.pack-prompt { font-weight: 700; line-height: 1.5; }
.pack-listen .pack-context:empty { display: none; }
.pack-toolkit-item { padding: 12px 0; border-bottom: 1px solid #e8d8bc; }
.toolkit-suggestions { display: flex; gap: 6px; flex-wrap: wrap; margin: 8px 0; }
.toolkit-suggestions[hidden] { display: none; }
.toolkit-suggestions .pack-secondary { width: auto; flex: 1 1 140px; font-size: 13px; margin: 0; }
.toolkit-message-preview { margin-top: 20px; }
#profession-pack-body .toolkit-check { display: flex; align-items: center; gap: 12px; min-height: 52px; line-height: 1.5; margin: 16px 0; }
#profession-pack-body .toolkit-check input { flex: 0 0 24px; width: 24px; height: 24px; min-height: 24px; margin: 0; accent-color: #92400e; }
.toolkit-message-preview textarea { box-sizing: border-box; width: 100%; min-height: 140px; padding: 12px; font: inherit; line-height: 1.5; border: 1px solid #dec9a7; border-radius: 12px; }
#profession-pack-body input { display: block; width: 100%; box-sizing: border-box; min-height: 52px; padding: 12px; margin: 8px 0; border: 1px solid #dec9a7; border-radius: 12px; font: inherit; }
#smart-nudges .account-card { padding: 16px; }
#smart-nudges .account-help:first-child { margin-top: 0; }
.account-profile { padding: 18px; }
.account-profile-main { display: flex; gap: 13px; align-items: center; }
.account-avatar { display: grid; place-items: center; flex: 0 0 54px; height: 54px; border-radius: 18px; background: #f3e5ce; color: #92400e; font-size: 23px; font-weight: 800; }
.account-profile-details { min-width: 0; }
.account-profile-details h3 { margin: 0 0 3px; font-size: 18px; overflow-wrap: anywhere; }
.account-profile-details p { margin: 2px 0; color: #786044; font-size: 13px; overflow-wrap: anywhere; }
.account-help { color: #786044; font-size: 13px; line-height: 1.6; margin: 12px 0; }
.account-button { display: block; width: 100%; min-height: 52px; border: 1px solid transparent; border-radius: 12px; background: #92400e; color: #fffdf9; padding: 12px 14px; font: inherit; font-size: 15px; font-weight: 700; cursor: pointer; touch-action: manipulation; }
.account-button:active { background: #78350f; }
.account-button:disabled, .account-row:disabled { opacity: .55; cursor: not-allowed; }
.account-secondary { background: #fffdf9; border-color: #dfc9a5; color: #784119; }
.account-secondary:active { background: #f9efdE; }
.account-text-button { background: transparent; color: #92400e; font-size: 13px; }
.account-text-button:active { background: #f9efde; }
.account-auth-actions { margin-top: 10px; }
.account-access { padding: 18px; background: linear-gradient(130deg, #fff5d4, #ffedbd); }
.account-access-top { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; gap: 8px; }
.account-plan-mark { color: #b56c12; font-size: 27px; line-height: 1; }
.account-badge { padding: 5px 9px; background: #f4e3bd; color: #705329; border-radius: 99px; font-size: 11px; font-weight: 700; }
.account-badge[data-active="true"] { color: #335c38; background: #e0edce; }
.account-access h3 { margin: 0; font-size: 21px; color: #533819; }
.account-access .account-help { margin: 5px 0 12px; }
.account-price { font-size: 28px; font-weight: 800; margin: 14px 0; color: #784119; }
.account-price span { font-size: 14px; font-weight: 500; color: #786044; }
.account-fine-print { font-size: 11px; line-height: 1.6; text-align: center; color: #786044; margin: 10px 0 0; }
.account-inline-button { display: block; min-height: 44px; margin: 0 auto; border: 0; background: transparent; color: #784119; font: inherit; font-size: 12px; text-decoration: underline; text-underline-offset: 3px; cursor: pointer; }
.account-row-list { padding: 0 16px; }
.account-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; width: 100%; min-height: 56px; border: 0; border-bottom: 1px solid #eee2ce; padding: 13px 0; background: transparent; color: #392b1a; font: inherit; font-size: 14px; text-align: left; cursor: pointer; }
.account-row:last-child { border-bottom: 0; }
.account-row > span:last-child { color: #947347; font-size: 23px; }
.account-row:active { background: #faf3e8; }
.account-row.account-danger { color: #994028; }
.account-toggle-row { display: flex; align-items: center; justify-content: space-between; gap: 14px; min-height: 74px; padding: 15px 16px; cursor: pointer; }
.account-row-title { display: block; font-size: 14px; font-weight: 650; }
.account-row-detail { display: block; margin-top: 4px; font-size: 12px; color: #786044; }
.account-toggle-row input { appearance: none; -webkit-appearance: none; flex: 0 0 46px; width: 46px; height: 28px; border-radius: 99px; border: 2px solid #b6a68b; background: #b6a68b; margin: 0; cursor: pointer; position: relative; transition: background .15s; }
.account-toggle-row input::before { content: ''; position: absolute; height: 20px; width: 20px; left: 2px; top: 2px; border-radius: 50%; background: #fffdf9; box-shadow: 0 1px 3px #0002; transition: transform .15s; }
.account-toggle-row input:checked { background: #92400e; border-color: #92400e; }
.account-toggle-row input:checked::before { transform: translateX(18px); }
.account-setting-note { padding: 0 16px; margin: 0 0 14px; font-size: 12px; }
.account-footer { text-align: center; color: #806544; font-size: 12px; padding: 5px 0 14px; }
.account-notice { border: 1px solid #deceaD; background: #fbf3de; padding: 12px; border-radius: 10px; color: #624619; font-size: 13px; line-height: 1.6; overflow-wrap: anywhere; }
.account-notice[data-error="true"] { border-color: #d7a68f; background: #fcf0e8; color: #803a25; }
.account-content :is(button,input,a):focus-visible, .account-dialog :is(button,input,textarea,a):focus-visible { outline: 3px solid #007baa; outline-offset: 3px; }
.account-dialog { max-width: 400px; width: calc(100% - 32px); max-height: calc(100dvh - 48px); overflow-y: auto; border: 1px solid #dfc9a5; border-radius: 20px; padding: 20px; background: #fffdf9; color: #2a1f12; box-shadow: 0 16px 70px #392b1a38; overscroll-behavior: contain; }
.account-dialog::backdrop { background: #30241380; }
.account-dialog-head { display: flex; align-items: flex-start; gap: 12px; justify-content: space-between; }
.account-dialog-head h2 { margin: 8px 0 16px; font-size: 21px; color: #784119; }
.account-dialog-close { flex: 0 0 44px; min-height: 44px; border: 0; background: #f5ead6; border-radius: 12px; color: #784119; font: inherit; font-size: 27px; cursor: pointer; }
.account-form { display: flex; flex-direction: column; gap: 14px; }
.account-field { display: flex; flex-direction: column; gap: 6px; font-size: 13px; font-weight: 650; }
.account-field input, .account-field textarea { min-width: 0; width: 100%; min-height: 52px; padding: 12px; border: 1px solid #d8c5a6; border-radius: 10px; background: #fff; color: #2a1f12; font: inherit; font-size: 16px; font-weight: 400; }
.account-field textarea { min-height: 130px; resize: vertical; }
.account-form .account-help { margin: 0; font-weight: 400; }
.account-check { display: flex; align-items: flex-start; gap: 10px; min-height: 44px; font-size: 13px; line-height: 1.6; }
.account-check input { margin-top: 4px; accent-color: #92400e; }
.account-dialog .account-danger-button { background: #994028; }
.account-qr { max-width: 260px; width: 100%; margin: 16px auto; padding: 8px; background: #fff; border-radius: 10px; }
.account-qr svg { display: block; width: 100%; height: auto; }
.account-payment-data { display: grid; grid-template-columns: auto minmax(0,1fr); gap: 10px; font-size: 13px; }
.account-payment-data dt { color: #786044; }
.account-payment-data dd { margin: 0; text-align: right; font-weight: 650; overflow-wrap: anywhere; }
.account-link { color: #92400e; text-underline-offset: 3px; overflow-wrap: anywhere; }
@media (prefers-reduced-motion: reduce) { .account-toggle-row input, .account-toggle-row input::before { transition: none; } }

/* PAY-FOR-ME-1 (2026-09-26): the "Gói học" card at the top of Settings and the
   "nhờ người thân thanh toán hộ" checkout screens (purchase -> method ->
   relative -> share, in js/account.js). Same warm palette as ACCOUNT-1. The
   relative's own page, pay.html, reuses the .account-* rules above and adds
   only css/pay.css for its page layout. */
.account-offer-eyebrow { margin: 0; font-size: 10px; font-weight: 800; letter-spacing: 2px; color: #947347; }
.account-offer-hint { margin: 0 0 14px; padding: 12px 14px; border-radius: 12px; background: #fffdf9cc; }
.account-offer-hint p { margin: 0; font-size: 14px; line-height: 1.55; color: #533819; }
.account-offer-question { font-weight: 700; }
.account-access .account-inline-button { margin-top: 6px; }
.account-purchase-price { margin: 0 0 6px; font-size: 22px; font-weight: 800; color: #784119; }
.account-purchase-question { margin: 14px 0 6px; font-weight: 700; font-size: 15px; color: #533819; }
.account-divider { border: 0; border-top: 1px solid #e8d8bc; margin: 18px 0; }
.account-radio { display: flex; align-items: center; gap: 12px; min-height: 56px; padding: 12px 14px; border: 1px solid #dfc9a5; border-radius: 12px; background: #fffdf9; font-size: 15px; line-height: 1.4; cursor: pointer; }
.account-radio input { flex: 0 0 auto; width: 22px; height: 22px; margin: 0; accent-color: #92400e; }
.account-radio:has(input:checked) { border-color: #92400e; background: #fff5d4; }
/* The link is selectable as one piece so a long press copies all of it. */
.account-share-link { margin: 6px 0 10px; padding: 12px; border: 1px dashed #dfc9a5; border-radius: 10px; background: #fff; font-size: 13px; line-height: 1.5; overflow-wrap: anywhere; user-select: all; -webkit-user-select: all; }

/* =====================================================================
   FLASH REVIEW SCREEN (FLASH-1, 2026-08-20)
   The flip-card review, rendered into #app like a card. Front: progress,
   the Czech line, the speaker, one "Xem nghĩa" button (one decision
   point). Back: the Vietnamese line + the known/unknown pair. No flip
   animation on purpose for the pilot — the reveal of the meaning IS the
   event; motion can be layered on later if Nam wants it.
   ===================================================================== */
.flash-card {
  /* Same shell as .card: fills #app, never overflows the screen; content
     is centered as a single column and scrolls inside if ever squeezed. */
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  /* Start-justified + flexible spacers (::before/::after below) instead
     of justify-content:center — same reasoning as #picker (FLASH-1): a
     center-justified flex scroller clips its top unreachably once the
     content overflows, and `safe` is not parsed by the iOS Safari
     versions this app's users carry. The spacers grow equally while the
     card fits (visually centered) and collapse to zero when it does not
     (everything reachable by scrolling). Pseudo-elements rather than
     child auto-margins because the first/last visible children change
     here (front vs flipped vs finish pane). */
  justify-content: flex-start;
  overflow-y: auto;
  gap: var(--sp-4);
  background: #ffffff;
  border-radius: 20px;
  box-shadow: 0 6px 20px rgba(60, 40, 10, 0.10);
  padding: var(--sp-5) var(--sp-4);
  text-align: center;
}
/* The centering spacers — see the justify-content note in .flash-card. */
.flash-card::before, .flash-card::after {
  content: '';
  flex: 1 1 0;          /* grow while there is room, vanish when there is not */
}
/* "3 / 27" — a quiet eyebrow, same size family as .scene-caption. */
.flash-progress {
  font-size: clamp(13px, 3.6vw, 15px);
  font-weight: 700;
  color: #8a7350;
}
/* The Czech line — the star of the front side, same voice as .reveal-cz. */
.flash-cz {
  font-size: 27px;
  font-weight: 800;
  color: #1c1410;
  letter-spacing: 0.2px;
}
/* Flip + exit share one look: a calm secondary button in the house
   border style — the amber circle (speaker) stays the only loud thing. */
.flash-flip, .flash-exit {
  min-width: 200px;
  min-height: 52px;               /* tap-target floor, same as .option */
  padding: var(--sp-3) var(--sp-5);
  border: 2px solid #e8dac4;
  border-radius: 14px;
  background: #fffdf9;
  font-family: inherit;
  font-size: 17px;
  font-weight: 700;
  color: #92400e;
  cursor: pointer;
  transition: background-color 0.15s, transform 0.1s;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
}
.flash-flip:active, .flash-exit:active { background: #fdf6e3; transform: scale(0.985); }
.flash-flip[hidden] { display: none; }   /* honour the hidden attribute */

/* The back side: meaning + the two marks. Hidden until the flip. */
.flash-back { width: 100%; }
.flash-back[hidden] { display: none; }
.flash-vi {
  font-size: clamp(16px, 4.6vw, 20px);   /* same fluid size as .reveal-vi */
  font-weight: 600;
  color: #6b5640;
}
.flash-actions {
  margin-top: var(--sp-4);
  display: flex;
  gap: var(--sp-2);
}
.flash-mark {
  flex: 1 1 0;                    /* two equal halves */
  min-height: 52px;               /* tap-target floor */
  border-radius: 14px;
  padding: var(--sp-3);
  font-family: inherit;
  font-size: clamp(15px, 4.2vw, 17px);
  font-weight: 700;
  line-height: 1.4;               /* floor for Vietnamese diacritics */
  cursor: pointer;
  transition: transform 0.1s;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
}
.flash-mark:active { transform: scale(0.985); }
/* "Đã thuộc" — the same friendly green as a correct answer. */
.flash-mark-known {
  background: #d1fae5;
  border: 2px solid #15803d;
  color: #14532d;
}
/* "Chưa thuộc" — calm and neutral, NEVER red: not knowing yet is the
   normal state of a learner, not a mistake. Non-punitive rule applies
   here exactly as it does to a wrong answer. */
.flash-mark-unknown {
  background: #fffdf9;
  border: 2px solid #e8dac4;
  color: #5a4220;
}

/* The finish pane — same voice as .end, plus the fresh count. P4
   (2026-09-11): the queue's finish and empty panes reuse these two and
   .flash-card / .flash-exit above, so both review surfaces end alike. */
.flash-done-title {
  font-size: 26px;
  font-weight: 800;
  color: #5a4220;
}
.flash-done-stats {
  font-size: clamp(16px, 4.6vw, 20px);
  font-weight: 600;
  color: #6b5640;
}

/* Bookmarks stay visually secondary to answering, flipping, and continuing. */
.phrase-save, .saved-phrases-link {
  display: inline-block;
  background: transparent;
  color: #57534e;
  border: 1px solid #d6d3d1;
  border-radius: 8px;
  padding: 10px 14px;
  margin: 12px 8px 0 0;
  font: inherit;
  font-size: 14px;
  text-decoration: none;
  cursor: pointer;
}
.phrase-save[aria-pressed="true"] { background: #f5f5f4; color: #292524; }
.phrase-save:focus-visible, .saved-phrases-link:focus-visible { outline: 2px solid #2563eb; outline-offset: 3px; }
.phrase-save:disabled { opacity: .5; cursor: default; }
.phrase-save-status { display: block; font-size: 13px; color: #57534e; }

/* =====================================================================
   SAVED PHRASES — the "Câu đã lưu" screen (SAVED-SCREEN-1, 2026-09-16).
   Rendered into #app by renderSavedPhrases() in js/saved-phrases.js.
   Same shell contract as .card / .flash-card: fills #app, never grows
   the page a scrollbar, scrolls INSIDE itself. main already pays the
   side gutters and the safe areas, so this pane adds none of its own.
   ===================================================================== */
.saved-phrases {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  width: 100%;
  padding: var(--sp-2) 0 var(--sp-5);
}
/* The quiet way out — a text button in the heading brown, above the
   title. Tap-target floor kept by min-height, not by padding. */
.saved-back {
  display: block;
  min-height: 44px;
  padding: var(--sp-2) 0;
  border: 0;
  background: transparent;
  color: #92400e;
  font: inherit;
  font-size: 15px;
  font-weight: 700;
  cursor: pointer;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
}
/* The title — same voice as .picker-title (a screen name, not a brand). */
.saved-title {
  margin: 0 0 var(--sp-3);
  font-size: clamp(22px, 6vw, 26px);
  font-weight: 800;
  letter-spacing: -0.3px;
  color: #92400e;
}
/* The search field. 17px on purpose: below 16px iOS Safari zooms the
   page on focus. House border, house radius, no native search chrome. */
.saved-search {
  display: block;
  width: 100%;
  min-height: 52px;
  padding: 12px 14px;
  border: 2px solid #e8dac4;
  border-radius: 14px;
  background: #fffdf9;
  color: #2a1f12;
  font: inherit;
  font-size: 17px;
  -webkit-appearance: none;
  appearance: none;
}
.saved-search[hidden] { display: none; }
.saved-search::placeholder { color: #8a7350; opacity: 1; }
.saved-search:focus-visible { outline: 2px solid #2563eb; outline-offset: 3px; }

/* SAVED-SCREEN-2 (2026-09-17): the filter chips under the search field —
   "Tất cả · Cần dùng" always, then one chip per profession that actually
   has a saved phrase (the app knows the context; the learner never tags).
   Pills in the house border style; the active one is the filled brown,
   so state is carried by fill AND hue, not hue alone. Wraps rather than
   scrolls: with data-driven chips there are rarely more than four. */
.saved-filters {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  margin-top: var(--sp-3);
}
.saved-chip {
  min-height: 44px;
  padding: var(--sp-2) var(--sp-4);
  border: 2px solid #e8dac4;
  border-radius: 999px;
  background: #fffdf9;
  color: #533b25;
  font: inherit;
  font-size: 15px;
  font-weight: 700;
  cursor: pointer;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
}
.saved-chip[aria-pressed="true"] { background: #92400e; border-color: #92400e; color: #fffdf9; }
.saved-chip:focus-visible { outline: 2px solid #2563eb; outline-offset: 3px; }
/* The "Cần dùng" flag on a row — the same quiet secondary look as the
   save toggle beside it; pressed = flagged. */
.saved-priority[aria-pressed="true"] { background: #fff0cb; border-color: #e0b95b; color: #533b25; }

.saved-list { margin-top: var(--sp-4); }
/* One saved phrase: a calm white tile, like .pack-phrase. */
.saved-phrase {
  margin: 0 0 var(--sp-3);
  padding: var(--sp-4);
  background: #ffffff;
  border: 1px solid #efe2ca;
  border-radius: 14px;
}
.saved-phrase[hidden] { display: none; }   /* the search filter hides rows */
/* Czech line and Vietnamese meaning — the reveal's own voices (.reveal-cz
   / .reveal-vi), one step smaller because there are many on screen. */
.saved-cz {
  margin: 0;
  font-size: 22px;
  line-height: 1.35;
  font-weight: 800;
  color: #1c1410;
  overflow-wrap: anywhere;
}
.saved-vi {
  margin: var(--sp-1) 0 0;
  font-size: clamp(16px, 4.6vw, 18px);
  line-height: 1.5;
  font-weight: 600;
  color: #6b5640;
}
/* Speaker + save toggle on one row; the toggle's status line (Đã bỏ lưu ·
   Hoàn tác) wraps to a full line beneath them. */
.saved-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2) var(--sp-3);
  margin-top: var(--sp-3);
}
.saved-actions .phrase-save { margin: 0; }
.saved-actions .phrase-save-status { flex: 1 0 100%; }
.saved-actions .phrase-save-status:empty { display: none; }   /* an empty status must not claim a row */
/* The speaker — the card's amber .replay-btn, one size down. */
.saved-listen {
  width: 48px;
  height: 48px;
  border-radius: 50%;
  border: none;
  background: #F5A623;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  box-shadow: 0 3px 10px rgba(245, 166, 35, 0.45);
  transition: transform 90ms ease-out;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
  user-select: none;
}
.saved-listen:active { transform: scale(0.92); }
.saved-listen:disabled { opacity: .5; cursor: default; box-shadow: none; }
.saved-listen:focus-visible { outline: 2px solid #2563eb; outline-offset: 3px; }
.saved-audio-status { margin: var(--sp-2) 0 0; font-size: 13px; color: #57534e; }
.saved-audio-status:empty { display: none; }

/* "Chưa có câu đã lưu phù hợp." — only while a query hides every row. */
.saved-none { margin: var(--sp-4) 0; line-height: 1.5; color: #6b5640; }
.saved-none[hidden] { display: none; }

/* EMPTY STATE — nothing saved yet: two lines and one primary action. */
.saved-empty { padding: var(--sp-5) 0 var(--sp-3); text-align: center; }
.saved-empty p { margin: 0 0 var(--sp-2); line-height: 1.5; color: #6b5640; }
.saved-empty-title { font-size: 19px; font-weight: 800; color: #1c1410; }
/* The primary action — the house filled button (.account-button's look). */
.saved-cta {
  display: block;
  width: 100%;
  min-height: 52px;
  margin-top: var(--sp-4);
  border: 1px solid transparent;
  border-radius: 12px;
  background: #92400e;
  color: #fffdf9;
  font: inherit;
  font-size: 17px;
  font-weight: 700;
  cursor: pointer;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
}
.saved-cta:active { background: #78350f; }
.saved-cta:focus-visible { outline: 2px solid #2563eb; outline-offset: 3px; }

/* The device-local footnote — present, never loud. */
.saved-note { margin: var(--sp-4) 0 0; font-size: 13px; line-height: 1.5; color: #8a7350; }

/* =====================================================================
   SAVED REVIEW — review sessions over saved phrases (SAVED-REVIEW-1,
   2026-09-17). Rendered by js/saved-review.js inside the same
   .saved-phrases scroller as the library. The answer buttons are the
   card's own (.options / .option, .correct / .is-wrong) and the two
   self-report pills are the flash review's (.flash-actions /
   .flash-mark) — one look per idea across the app; nothing here
   restyles them, it only places them.
   ===================================================================== */
/* The library's banner: one sentence, one primary button. */
.saved-review-banner {
  margin: 0 0 var(--sp-4);
  padding: var(--sp-4);
  border: 1px solid #e8d8bc;
  border-radius: 18px;
  background: #fff9eb;
}
.saved-review-banner-title { margin: 0; font-size: 18px; line-height: 1.4; font-weight: 800; color: #1c1410; }
.saved-review-banner .saved-cta { margin-top: var(--sp-3); }

/* SAVED-WORK-1 (2026-09-20): "Cần dùng" in the banner's slot and in the
   session. Placement only — every control keeps the look it already has
   elsewhere. The after-work question (.saved-work-ask) is a self-report
   with NO right answer: both answers are the flash review's calm neutral
   pill, so there is deliberately no green and no red anywhere in it. */
/* The second line when a session holds pinned phrases AND ordinary reviews. */
.saved-review-banner-sub { margin: var(--sp-1) 0 0; font-size: 15px; line-height: 1.5; font-weight: 600; color: #6b5640; }
/* "2 câu bạn muốn dùng ở chỗ làm hôm nay" above a pinned step — the scene
   eyebrow's voice (.saved-review-caption), outside the card. */
.saved-work-caption { margin: var(--sp-3) 0 0; font-size: clamp(13px, 3.6vw, 15px); line-height: 1.5; font-weight: 700; color: #8a7350; }
/* The phrase being asked about — the library row's Czech voice (.saved-cz). */
.saved-work-cz { margin: var(--sp-2) 0 0; font-size: 22px; line-height: 1.35; font-weight: 800; color: #1c1410; overflow-wrap: anywhere; }
.saved-work-ask .flash-actions { margin-top: var(--sp-3); }
.saved-work-ask .phrase-save { margin: var(--sp-3) 0 0; }
.saved-work-ask .phrase-save-status { margin-top: var(--sp-2); }
.saved-work-ask .phrase-save-status:empty { display: none; }

/* Session chrome: the way out on the left, "2 / 5" on the right. */
.saved-review-head { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); }
.saved-review-progress { margin: 0; font-size: clamp(13px, 3.6vw, 15px); font-weight: 700; color: #8a7350; }

/* One step = one white card, the same shell as .flash-card. */
.saved-review-card {
  margin-top: var(--sp-3);
  padding: var(--sp-5) var(--sp-4);
  background: #ffffff;
  border-radius: 20px;
  box-shadow: 0 6px 20px rgba(60, 40, 10, 0.10);
  text-align: center;
}
.saved-review-card > .saved-listen { width: 56px; height: 56px; margin: 0 auto; }   /* the card's replay size */
.saved-review-card .saved-listen { margin-left: auto; margin-right: auto; }
.saved-review-card .options { padding: var(--sp-4) 0 0; }
/* What the learner is shown first: scene eyebrow, scene, then the cue. */
.saved-review-caption { margin: 0; font-size: clamp(13px, 3.6vw, 15px); font-weight: 700; color: #8a7350; }
.saved-review-scene { margin: var(--sp-2) 0 0; font-size: 15px; line-height: 1.5; color: #6b5640; }
.saved-review-cue { margin: var(--sp-3) 0 0; font-size: clamp(19px, 5.4vw, 23px); line-height: 1.4; font-weight: 800; color: #1c1410; }
.saved-review-card > .saved-review-cue:first-child { margin-top: 0; }
.saved-review-ask { margin: var(--sp-3) 0 0; font-size: 16px; line-height: 1.5; font-weight: 600; color: #6b5640; }
/* The answer — the reveal's own voices (.reveal-cz / .reveal-vi). */
.saved-review-cz { margin: 0 0 var(--sp-2); font-size: 27px; line-height: 1.3; font-weight: 800; letter-spacing: 0.2px; color: #1c1410; overflow-wrap: anywhere; }
.saved-review-vi { margin: 0 0 var(--sp-3); font-size: clamp(16px, 4.6vw, 20px); line-height: 1.5; font-weight: 600; color: #6b5640; }
.saved-review-reveal { margin-top: var(--sp-4); }
.saved-review-reveal[hidden], .saved-cta[hidden] { display: none; }   /* display:block above would beat [hidden] */
.saved-review-actions { margin-top: var(--sp-4); }
.saved-review-actions .saved-cta { margin-top: 0; }
.saved-review-note { margin: var(--sp-3) 0 0; font-size: 15px; line-height: 1.6; color: #6b5640; }
.phrase-undo { border: 0; background: transparent; color: #44403c; font: inherit; text-decoration: underline; padding: 8px; min-height: 44px; cursor: pointer; }
.phrase-undo:focus-visible { outline: 2px solid #2563eb; outline-offset: 2px; }

/* OFFER-1: single paid-pack invitation after the free deck boundary. */
.end-offer { align-self: center; margin-top: var(--sp-3); max-width: 100%; }
