/* Famzo Learn web — layout and components.
 *
 * Every colour, radius and spacing step comes from tokens.css, which is
 * GENERATED from the React Native app's own theme. There are no hex literals in
 * this file on purpose: the palette drifted once already when it was copied by
 * hand, and a literal here is how that happens again.
 *
 * THE LAYOUT, AND WHY IT IS NOT THE PHONE'S
 *
 * The first version of this page was the phone screen centred in a 620px column
 * with a bottom tab bar, which on a laptop reads as an app someone forgot to
 * design for the web. Two shapes, one breakpoint:
 *
 *   < 900px   one column, bottom tab bar — a phone, and correct there
 *   >= 900px  a persistent left rail and a wide content area
 *
 * Nothing is hidden at either size; the navigation simply changes form. Prose
 * is still held to a readable measure inside the wide area, because a story
 * chapter set 1200px wide is worse than one set 600px wide, not better.
 */

*, *::before, *::after { box-sizing: border-box; }
html, body { height: 100%; }

body {
  margin: 0;
  background: var(--page);
  color: var(--ink-muted);
  font: 16px/1.6 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  overscroll-behavior-y: none;
}

h1, h2, h3 { color: var(--ink-strong); line-height: 1.25; margin: 0; }
button { font-family: inherit; }
img { max-width: 100%; display: block; }
a { color: var(--teal-ink); }

/* ═══════════════════════════════════════════════════════════════════════════
   Shell
   ═════════════════════════════════════════════════════════════════════════ */

#root { min-height: 100%; }

.shell {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  min-height: 100vh;
}

/* ── The left rail (desktop only) ─────────────────────────────────────────── */

.rail { display: none; }

@media (min-width: 900px) {
  .shell { grid-template-columns: 264px minmax(0, 1fr); }

  .rail {
    display: flex;
    flex-direction: column;
    gap: var(--space-xs);
    position: sticky;
    top: 0;
    height: 100vh;
    overflow-y: auto;
    padding: var(--space-xxl) var(--space-lg) var(--space-xl);
    background: var(--paper);
    border-right: 1px solid var(--line-hairline);
  }
}

.rail-brand {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  padding: 0 var(--space-md) var(--space-xl);
}
/* The mark is an <img> now, with a <span> emoji as its only fallback, so every
   rule here has to work for both. Height is set and width follows from the
   intrinsic size in the markup - no crop, no squash, no reflow when it lands. */
.rail-brand .mark { font-size: 30px; line-height: 1; }
img.mark { display: block; width: auto; object-fit: contain; }
.rail-brand img.mark { height: 40px; }
.topbar img.mark { height: 30px; }
.emptymark { display: block; width: auto; height: 84px; margin: 0 auto var(--space-md); font-size: 48px; }
.onb .wordmark { display: block; width: min(230px, 62%); height: auto; margin: 0 auto var(--space-lg); }
.rail-brand .name {
  font-weight: 800; color: var(--ink-strong); font-size: 17px; letter-spacing: -0.01em;
}

.rail-nav { display: flex; flex-direction: column; gap: 2px; }

.rail-item {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  width: 100%;
  /* Seven items now rather than five, so the rail trims its vertical padding
     to keep the progress block and the links below the fold-free zone. */
  padding: var(--space-sm) var(--space-md);
  border: none;
  background: none;
  border-radius: var(--radius-md);
  cursor: pointer;
  text-align: left;
  font-size: 15.5px;
  font-weight: 700;
  color: var(--ink-muted);
  transition: background 0.15s, color 0.15s;
}
.rail-item .ic {
  width: 34px; height: 34px; flex: none;
  display: grid; place-items: center;
  border-radius: var(--radius-sm);
  font-size: 18px;
  background: var(--line-hairline);
  transition: background 0.15s;
}
.rail-item:hover { background: var(--page); color: var(--ink-strong); }
.rail-item[aria-current="page"] { background: var(--page-warm); color: var(--ink-strong); }
.rail-item[aria-current="page"] .ic { background: var(--paper); }
/* Each rail item wears its own feature's chip colour, from FEATURE_COLOURS. */
.rail-item[data-feature="today"]    .ic { background: var(--butter-panel); }
.rail-item[data-feature="gk"]       .ic { background: var(--gk-panel); }
.rail-item[data-feature="language"] .ic { background: var(--language-panel); }
.rail-item[data-feature="story"]    .ic { background: var(--stories-panel); }
.rail-item[data-feature="riddles"]  .ic { background: var(--riddles-panel); }
.rail-item[data-feature="safety"]   .ic { background: var(--safety-panel); }
.rail-item[data-feature="parent"]   .ic { background: var(--parent-panel); }

.rail-foot { margin-top: auto; padding-top: var(--space-xl); }

/* Today's progress, in the rail. The Today screen has the same information in
   its `.hero` band for phones; exactly one of the two is ever visible. */
.rail-progress {
  display: flex; align-items: center; gap: var(--space-lg);
  background: var(--page-warm); border-radius: var(--radius-lg);
  padding: var(--space-lg); margin-bottom: var(--space-md);
}
.rail-progress .ring { flex: none; }
.rail-progress .val b { font-size: 19px; }
.rail-progress .val span { font-size: 9px; }
.rail-progress-meta { min-width: 0; }
.rail-progress-meta b { display: block; color: var(--ink-strong); font-size: 15px; }
.rail-progress-meta span { display: block; color: var(--ink-muted); font-size: 12.5px; margin-top: 2px; }
.rail-streak {
  display: flex; align-items: center; gap: var(--space-sm);
  background: var(--butter-surface);
  color: var(--hue-butter);
  border-radius: var(--radius-md);
  padding: var(--space-md) var(--space-lg);
  font-weight: 800; font-size: 14.5px;
  margin-bottom: var(--space-md);
}
.rail-links { display: flex; flex-wrap: wrap; gap: var(--space-sm) var(--space-md); padding: 0 var(--space-md); }
.rail-links a { color: var(--ink-faint); font-size: 13px; text-decoration: none; }
.rail-links a:hover { color: var(--ink-muted); text-decoration: underline; }

/* ── The content column ───────────────────────────────────────────────────── */

.content { min-width: 0; padding-bottom: 96px; }
@media (min-width: 900px) { .content { padding-bottom: var(--space-gap); } }

/* Sticky header. On a phone it carries the branding and the back button; on a
   desktop the rail already does, so it thins down to a title. */
.topbar {
  position: sticky; top: 0; z-index: 20;
  background: color-mix(in srgb, var(--page) 92%, transparent);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--line-hairline);
}
.topbar .inner {
  display: flex; align-items: center; gap: var(--space-md);
  padding: var(--space-md) var(--space-xl);
  max-width: 1180px;
}
.topbar .mark { font-size: 24px; line-height: 1; }
@media (min-width: 900px) { .topbar { display: none; } }
.topbar .name { font-weight: 800; color: var(--ink-strong); font-size: 16px; }
.topbar .spacer { flex: 1; }

.backbtn {
  background: none; border: none; color: var(--purple);
  font-weight: 700; font-size: 15px; cursor: pointer;
  padding: var(--space-sm); margin-left: calc(var(--space-sm) * -1);
  border-radius: var(--radius-sm);
}
.backbtn:hover { background: var(--gk-surface); }

.streakchip {
  display: inline-flex; align-items: center; gap: var(--space-sm);
  background: var(--butter-surface); color: var(--hue-butter);
  font-weight: 800; font-size: 14px;
  border-radius: var(--radius-pill); padding: var(--space-sm) var(--space-lg);
  white-space: nowrap;
}

/* Centred, not left-hugging. The measure is capped so a line of prose never
   runs the width of a 27-inch monitor, but with the cap and no auto margin the
   leftover width all piled up on the right and read as a layout that had come
   loose rather than one that had stopped on purpose. */
.page { padding: var(--space-xl); max-width: 1180px; margin-inline: auto; }
@media (min-width: 900px) { .page { padding: var(--space-huge) var(--space-gap); } }
/* Past a point the symmetric margins get wider than the content deserves, so
   the cap opens up rather than the gutters growing forever. */
@media (min-width: 1600px) { .page { max-width: 1380px; } }

/* Prose and single-item screens hold a readable measure even in a wide column. */
.measure { max-width: 760px; }

/* ── Bottom tabs (phone only) ─────────────────────────────────────────────── */

.tabbar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 30;
  background: var(--paper);
  border-top: 1px solid var(--line-hairline);
  box-shadow: 0 -6px 24px rgb(36 12 84 / 7%);
  padding-bottom: env(safe-area-inset-bottom);
}
@media (min-width: 900px) { .tabbar { display: none; } }
.tabbar .inner { display: flex; max-width: 620px; margin: 0 auto; }
.tab {
  flex: 1; background: none; border: none; cursor: pointer;
  padding: var(--space-md) var(--space-xs) var(--space-md);
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  color: var(--ink-faint); font-size: 11px; font-weight: 700;
}
.tab .ic { font-size: 21px; line-height: 1.1; }
.tab[aria-current="page"] { color: var(--purple); }

/* ═══════════════════════════════════════════════════════════════════════════
   Shared pieces
   ═════════════════════════════════════════════════════════════════════════ */

.eyebrow {
  font-size: 12px; font-weight: 800; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--purple); margin: 0 0 var(--space-sm);
}
.muted { color: var(--ink-muted); }
.small { font-size: 14px; }
.center { text-align: center; }

.card {
  background: var(--paper);
  border: 1px solid var(--line-hairline);
  border-radius: var(--radius-lg);
  padding: var(--space-xl);
  box-shadow: 0 10px 30px rgb(36 12 84 / 6%);
}
.card + .card { margin-top: var(--space-lg); }

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--space-sm);
  background: var(--teal-ink); color: var(--paper);
  border: none; border-radius: var(--radius-pill);
  padding: var(--space-lg) var(--space-xxl);
  font-size: 16px; font-weight: 800; cursor: pointer;
  text-decoration: none; width: 100%;
  transition: transform 0.12s cubic-bezier(0.2, 0.8, 0.3, 1), filter 0.15s, box-shadow 0.15s;
}
.btn:hover { filter: brightness(1.07); box-shadow: 0 6px 18px rgb(18 131 123 / 25%); }
.btn:active { transform: scale(0.975); }
.btn.violet { background: var(--purple); }
.btn.violet:hover { box-shadow: 0 6px 18px rgb(108 60 180 / 28%); }
.btn.ghost {
  background: var(--paper); color: var(--purple);
  border: 1.5px solid var(--gk-edge);
}
.btn.ghost:hover { background: var(--gk-surface); box-shadow: none; }
.btn.quiet {
  background: none; color: var(--ink-muted); font-weight: 700;
  font-size: 15px; padding: var(--space-md);
}
.btn.quiet:hover { filter: none; background: var(--page-warm); box-shadow: none; }
.btn.danger { background: var(--status-danger); }
.btn[disabled] { opacity: 0.45; cursor: not-allowed; transform: none; filter: none; box-shadow: none; }
.btn.auto { width: auto; }
.btnrow { display: flex; gap: var(--space-md); margin-top: var(--space-lg); }
.btnrow .btn { flex: 1; }

.field { margin-bottom: var(--space-lg); }
.field label {
  display: block; font-size: 13px; font-weight: 800; letter-spacing: 0.04em;
  text-transform: uppercase; color: var(--ink-muted); margin-bottom: var(--space-sm);
}
.field input {
  width: 100%; background: var(--paper);
  border: 1.5px solid var(--line-hairline); border-radius: var(--radius-md);
  padding: var(--space-lg); font-size: 16px; color: var(--ink-strong);
  font-family: inherit; transition: border-color 0.15s;
}
.field input:focus { outline: none; border-color: var(--purple); }
.field .hint { font-size: 13px; color: var(--ink-muted); margin-top: var(--space-sm); }

.notice {
  border-radius: var(--radius-md); padding: var(--space-md) var(--space-lg);
  font-size: 14.5px; margin-bottom: var(--space-lg);
}
.notice.err { background: var(--status-wrong-surface); color: var(--status-danger-ink); border: 1px solid var(--status-wrong-deep); }
.notice.ok { background: var(--status-done-surface); color: var(--status-done-deep); border: 1px solid var(--status-done-deep); }
.notice.info { background: var(--butter-surface); color: var(--hue-butter); border: 1px solid var(--butter-edge); }

/* Anything focused by keyboard gets the same ring, once, here. */
:focus-visible { outline: 3px solid var(--purple); outline-offset: 2px; border-radius: var(--radius-sm); }

/* ═══════════════════════════════════════════════════════════════════════════
   Today — a dashboard, not a list
   ═════════════════════════════════════════════════════════════════════════ */

.hello { margin-bottom: var(--space-xxl); }
.hello h1 { font-size: 28px; letter-spacing: -0.02em; }
.hello p { margin: var(--space-sm) 0 0; color: var(--ink-muted); font-size: 16.5px; }
@media (min-width: 900px) { .hello h1 { font-size: 34px; } }

/* The hero band: ring, streak copy and the two counters. One row on a desktop,
   stacked on a phone. */
.hero {
  display: grid; gap: var(--space-xl);
  grid-template-columns: minmax(0, 1fr);
  align-items: center;
  background: var(--paper);
  border: 1px solid var(--line-hairline);
  border-radius: var(--radius-xl);
  padding: var(--space-xxl);
  box-shadow: 0 10px 30px rgb(36 12 84 / 6%);
  margin-bottom: var(--space-xxl);
}
/* On a phone: ring beside the copy, counters across the bottom. Stacking all
   three put the tiles 500px down the page, which on the one screen meant to
   say "here is today" is most of the screen saying nothing. */
.hero { grid-template-columns: auto minmax(0, 1fr); }
.hero-stats { grid-column: 1 / -1; }
@media (min-width: 760px) {
  .hero { grid-template-columns: auto minmax(0, 1fr) auto; gap: var(--space-huge); }
  .hero-stats { grid-column: auto; }
}
/* The phone's copy of the rail's progress block. Placed AFTER the rules above
   on purpose: same specificity, so the last one wins — declaring it earlier
   silently lost to `.hero { display: grid }` and showed both at once. */
@media (min-width: 900px) { .hero { display: none; } }
.hero-ring { display: flex; justify-content: center; align-items: center; }
.hero-copy h2 { font-size: 21px; margin-bottom: var(--space-xs); }
.hero-copy p { margin: 0; color: var(--ink-muted); font-size: 15.5px; }
.hero-stats { display: flex; gap: var(--space-xl); }
@media (max-width: 759px) { .hero-stats { justify-content: space-around; } }
.hero-stat { text-align: center; }
.hero-stat b { display: block; font-size: 26px; color: var(--ink-strong); line-height: 1.1; }
.hero-stat span {
  display: block; font-size: 11px; font-weight: 800; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--ink-faint); margin-top: var(--space-xs);
}

.ring { width: 116px; height: 116px; position: relative; }
.ring svg { transform: rotate(-90deg); }
.ring .track { stroke: color-mix(in srgb, var(--ink-strong) 11%, transparent); }
.ring .fill { stroke: var(--teal); transition: stroke-dashoffset 0.7s cubic-bezier(0.2, 0.8, 0.3, 1); }
.ring .val {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
}
.ring .val b { font-size: 26px; color: var(--ink-strong); line-height: 1; }
.ring .val span {
  font-size: 10.5px; color: var(--ink-faint); font-weight: 800;
  letter-spacing: 0.08em; text-transform: uppercase; margin-top: 3px;
}

/* The three daily things, side by side. */
.dash {
  display: grid; gap: var(--space-lg);
  grid-template-columns: minmax(0, 1fr);
}
/* One big card for what is next, two compact ones beside it. Three equal boxes
   made the page a list of options; this makes it an answer. */
@media (min-width: 860px) {
  .dash { grid-template-columns: minmax(0, 1.45fr) minmax(0, 1fr); align-items: start; }
  /* The two compact cards keep their own height and the riddle takes the rest,
     so the column ends level with the featured card instead of stopping short
     and leaving a card's worth of empty page beside it.

     `height: 100%` is doing the work: `.dash` aligns its items to the start, so
     without it this column is only as tall as its contents and the `1fr` row
     has no leftover space to claim. The percentage resolves against the grid
     area, which the featured card has already sized. */
  .dash-rest { height: 100%; grid-template-rows: auto auto minmax(0, 1fr); }
}
.dash-rest { display: grid; gap: var(--space-lg); align-content: start; }
.dash-rest .teaser { height: 100%; }

.tile {
  display: flex; flex-direction: column; text-align: left;
  background: var(--paper);
  border: 1px solid var(--line-hairline);
  border-radius: var(--radius-lg);
  padding: 0; overflow: hidden; cursor: pointer;
  box-shadow: 0 10px 30px rgb(36 12 84 / 6%);
  transition: transform 0.16s cubic-bezier(0.2, 0.8, 0.3, 1), box-shadow 0.16s;
}
.tile:hover { transform: translateY(-3px); box-shadow: 0 16px 36px rgb(36 12 84 / 12%); }
.tile:active { transform: translateY(-1px) scale(0.995); }

/* The picture. Every chapter and every fact card in the library has one, so the
   emoji underneath is a fallback for a missing file, not the design.

   3:2 is chosen, not arbitrary: the chapter art is exactly 3:2 and the fact
   cards are 1.62:1, so almost nothing is cropped. The detail pages below crop
   nothing at all — a card is a card, but a page should show the whole picture. */
.tile-art { position: relative; display: block; overflow: hidden; }
.tile-art img { width: 100%; height: 100%; object-fit: cover; }
.tile-emoji { display: grid; place-items: center; width: 100%; height: 100%; font-size: 48px; }
.tile[data-feature="stories"]  .tile-art { background: var(--stories-surface); }
.tile[data-feature="language"] .tile-art { background: var(--language-surface); }
.tile[data-feature="gk"]       .tile-art { background: var(--gk-surface); }

/* 16:9 rather than the source's 3:2. A card is a card — it crops a little so
   that the title and the button stay above the fold, which matters more here
   than the last centimetre of sky. The detail page below crops nothing. */
.tile.featured .tile-art { aspect-ratio: 16 / 9; max-height: 300px; }
.tile.compact  .tile-art { width: 132px; flex: none; align-self: stretch; }
.tile.compact  .tile-emoji { font-size: 34px; }

.tile-body { padding: var(--space-lg) var(--space-xl) var(--space-xl); flex: 1; display: flex; flex-direction: column; min-width: 0; }
.tile .kicker {
  font-size: 11px; font-weight: 800; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--purple); margin-bottom: var(--space-xs);
}
.tile h3 { font-size: 17px; margin-bottom: var(--space-xs); }
.tile p { margin: 0; font-size: 14px; color: var(--ink-muted); }

/* The featured card gets room to breathe and its blurb gets to be prose. */
.tile.featured h3 { font-size: 23px; letter-spacing: -0.01em; margin-bottom: var(--space-sm); }
.tile.featured p { font-size: 15.5px; line-height: 1.55; }
.tile.featured .tile-body { padding: var(--space-xl); }
@media (min-width: 860px) { .tile.featured .tile-body { padding: var(--space-xxl); } }

/* The other two run horizontally: thumbnail, then words. */
.tile.compact { flex-direction: row; align-items: stretch; min-height: 148px; }
.tile.compact .tile-body { padding: var(--space-lg) var(--space-xl); justify-content: center; }
.tile.compact h3 {
  font-size: 16px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tile.compact p {
  font-size: 13.5px;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
.tile.compact .go { margin-top: var(--space-sm); font-size: 13.5px; }
.tile .go {
  margin-top: var(--space-lg); font-weight: 800; font-size: 14.5px; color: var(--teal-ink);
  display: flex; align-items: center; gap: var(--space-xs);
}
.tile:hover .go .arrow { transform: translateX(3px); }
.tile .go .arrow { transition: transform 0.16s; }

/* A finished tile steps back rather than shouting. */
.tile.done .tile-art { opacity: 0.5; }
.tile.done h3 { color: var(--ink-muted); }
.tile.done .go { color: var(--status-done-deep); }
.tile-check {
  position: absolute; top: var(--space-md); right: var(--space-md);
  width: 30px; height: 30px; border-radius: var(--radius-pill);
  background: var(--status-done); color: var(--paper);
  display: grid; place-items: center; font-size: 16px; font-weight: 800;
  box-shadow: 0 3px 10px rgb(31 199 118 / 40%);
}

.alldone {
  background: var(--status-done-surface);
  border: 1px solid var(--status-done-deep);
  border-radius: var(--radius-lg);
  padding: var(--space-xxl); text-align: center; margin-top: var(--space-xxl);
}
.alldone h3 { color: var(--status-done-deep); font-size: 19px; margin-bottom: var(--space-sm); }
.alldone p { margin: 0; font-size: 15.5px; color: var(--status-done-deep); }

/* ═══════════════════════════════════════════════════════════════════════════
   Detail screens
   ═════════════════════════════════════════════════════════════════════════ */

/* `.detail` itself is defined further down, with the aside it belongs to. */

/* The whole picture, never a crop of it.
 *
 * This was a fixed 240/320px with object-fit: cover, which cut the top and
 * bottom off every illustration — and a browser has no reason to do that. The
 * art is 3:2 (chapters) and 1.62:1 (fact cards), so at the widths below it
 * lands somewhere comfortable on its own. `max-height` with `vh` stops a
 * hypothetical portrait image filling the screen before a word of text. */
.hero-art {
  border-radius: var(--radius-lg); overflow: hidden;
  box-shadow: 0 10px 30px rgb(36 12 84 / 8%);
  margin-bottom: var(--space-xl); max-width: 700px;
}
/* Width is what controls the height here, not a max-height: the art is 3:2 and
   1.62:1, so 700px lands around 440px and the first line of text stays on the
   same screen. `max-height` is only a guard against a portrait image nobody has
   uploaded yet — in the normal case it never fires, so nothing is letterboxed. */
.hero-art img { width: 100%; height: auto; max-height: 68vh; object-fit: contain; }
.hero-art .fallback {
  aspect-ratio: 3 / 2; display: grid; place-items: center; font-size: 66px;
  background: var(--gk-surface);
}

.crumb {
  font-size: 12px; font-weight: 800; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--teal-ink); margin-bottom: var(--space-sm);
}
.title { font-size: 30px; letter-spacing: -0.02em; margin-bottom: var(--space-md); }
@media (min-width: 900px) { .title { font-size: 38px; } }
.desc { font-size: 17.5px; margin: 0 0 var(--space-xl); max-width: 62ch; }

.levels { display: flex; gap: var(--space-sm); margin-bottom: var(--space-xl); flex-wrap: wrap; }
.lv {
  border: 1.5px solid var(--gk-edge); background: var(--paper); color: var(--purple);
  font-weight: 800; font-size: 13px;
  padding: var(--space-sm) var(--space-lg); border-radius: var(--radius-pill);
  cursor: pointer; transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.lv:hover { background: var(--gk-surface); }
.lv[aria-pressed="true"] { background: var(--purple); border-color: var(--purple); color: var(--paper); }

.facts { list-style: none; padding: 0; margin: 0 0 var(--space-xl); display: grid; gap: var(--space-md); }
/* One fact per line. They used to stretch to 1110px for a single sentence;
   inside the main column they land near 700px, which is a comfortable measure
   and does not need splitting. */
.facts li {
  background: var(--paper); border: 1px solid var(--line-hairline);
  border-radius: var(--radius-md);
  padding: var(--space-lg) var(--space-lg) var(--space-lg) var(--space-gap);
  position: relative; font-size: 16px;
}
.facts li::before {
  content: "✦"; position: absolute; left: var(--space-lg); top: var(--space-lg);
  color: var(--sunny); font-size: 17px;
}

.quiz {
  background: var(--gk-surface); border-radius: var(--radius-lg);
  padding: var(--space-xl); margin-bottom: var(--space-lg);
}
.quiz h3 { font-size: 17px; margin-bottom: var(--space-lg); color: var(--hue-lavender); }
.quiz .opts { display: grid; gap: var(--space-md); }
.opt {
  background: var(--paper); border: 2px solid transparent;
  border-radius: var(--radius-md); padding: var(--space-lg);
  font-size: 16px; font-weight: 600; color: var(--ink-strong);
  cursor: pointer; text-align: left; width: 100%;
  transition: border-color 0.15s, background 0.15s, transform 0.12s;
}
.opt:hover:not([disabled]) { border-color: var(--gk-deep); transform: translateX(2px); }
.opt.right { border-color: var(--status-done); background: var(--status-done-surface); color: var(--status-done-deep); }
.opt.wrong { border-color: var(--status-wrong); background: var(--status-wrong-surface); color: var(--status-danger-ink); }
.opt[disabled] { cursor: default; }
.quizmsg { margin: var(--space-lg) 0 0; font-weight: 700; font-size: 15px; }

/* ── Words ────────────────────────────────────────────────────────────────── */

/* All thirteen languages are visible. The first cut put them in a horizontal
   scroller and twelve of the thirteen were off-screen with no affordance —
   including Hindi, Kannada and Marathi, which sit last in the app's order and
   are the three an Indian parent is most likely to want. */
.langrow {
  display: flex; flex-wrap: wrap; gap: var(--space-sm);
  margin-bottom: var(--space-xl);
}
.langpill {
  display: inline-flex; align-items: center; gap: var(--space-sm);
  border: 1.5px solid var(--line-hairline); background: var(--paper);
  border-radius: var(--radius-pill);
  padding: var(--space-sm) var(--space-lg);
  font-size: 14.5px; font-weight: 700; color: var(--ink-muted);
  cursor: pointer; white-space: nowrap;
  transition: background 0.15s, color 0.15s, border-color 0.15s, transform 0.12s;
}
.langpill:hover { background: var(--language-surface); border-color: var(--language-edge); }
.langpill[aria-pressed="true"] {
  background: var(--purple); border-color: var(--purple); color: var(--paper);
}

.wordcard {
  background: var(--paper); border: 1px solid var(--line-hairline);
  border-radius: var(--radius-xl); padding: var(--space-huge) var(--space-xxl);
  box-shadow: 0 10px 30px rgb(36 12 84 / 6%); text-align: center;
  margin-bottom: var(--space-xl);
}
.wordcard .emoji { font-size: 60px; line-height: 1; margin-bottom: var(--space-md); }
.wordart { margin-bottom: var(--space-lg); max-width: 460px; margin-inline: auto; }
.wordart img {
  width: 100%; height: auto; max-height: 46vh; object-fit: contain;
  border-radius: var(--radius-md);
}
.wordart .fallback {
  aspect-ratio: 3 / 2; border-radius: var(--radius-md); background: var(--language-surface);
  display: grid; place-items: center; font-size: 64px;
}
.wordcard .en {
  font-size: 14px; font-weight: 800; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--ink-faint); margin-bottom: var(--space-md);
}
.wordcard .target {
  font-size: 42px; font-weight: 800; color: var(--ink-strong);
  line-height: 1.2; margin-bottom: var(--space-sm); word-break: break-word;
}
.wordcard .pron {
  font-size: 20px; color: var(--teal-ink); font-weight: 700;
  letter-spacing: 0.02em; margin-bottom: var(--space-xl);
}

.speak {
  background: var(--teal-ink); color: var(--paper); border: none;
  border-radius: var(--radius-pill);
  padding: var(--space-md) var(--space-xxl);
  font-size: 15.5px; font-weight: 800; cursor: pointer;
  transition: background 0.15s, transform 0.12s;
}
.speak:hover:not([disabled]) { filter: brightness(1.07); }
.speak:active { transform: scale(0.97); }
.speak[disabled] { opacity: 0.45; cursor: not-allowed; }
.speak.on { background: var(--coral); }

.usage {
  background: var(--butter-surface); color: var(--hue-butter);
  border-radius: var(--radius-md); padding: var(--space-lg);
  font-size: 15px; margin-bottom: var(--space-lg);
}
.example {
  background: var(--language-surface); border-radius: var(--radius-md);
  padding: var(--space-lg); margin-bottom: var(--space-lg);
}
.example .t { font-size: 20px; font-weight: 700; color: var(--ink-strong); margin-bottom: var(--space-xs); }
.example .p { font-size: 15px; color: var(--teal-ink); font-weight: 600; margin-bottom: var(--space-sm); }
.example .e { font-size: 14.5px; color: var(--ink-muted); font-style: italic; }

/* ── Stories ──────────────────────────────────────────────────────────────── */

/* One continuous column, the way anything else on the web is read. The phone
   app pages through a chapter and is right to; a browser is not a bedside
   tap-target. */
.reader {
  background: var(--paper); border-radius: var(--radius-lg);
  padding: var(--space-huge) var(--space-xxl);
  box-shadow: 0 10px 30px rgb(36 12 84 / 6%);
  margin-bottom: var(--space-xxl);
  max-width: 720px;
}
.reader p {
  font-size: 19px; line-height: 1.85; color: var(--ink-strong);
  margin: 0 0 1.15em;
}
.reader p:last-child { margin-bottom: 0; }
@media (min-width: 900px) {
  .reader { padding: var(--space-gap); max-width: 760px; }
  .reader p { font-size: 20px; }
}

/* Chapter meta and the listen button, on one line above the text. */
.readerbar {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-lg); flex-wrap: wrap; margin-bottom: var(--space-lg);
  max-width: 720px;
}

/* Reading progress. Fixed to the very top of the viewport rather than sitting
   in the flow: it has to clear the phone's sticky header, and this is the
   pattern a reader already recognises from every long article. */
.progressbar.reading {
  position: fixed; top: 0; left: 0; right: 0; z-index: 40;
  height: 4px; background: transparent; border-radius: 0; margin: 0;
  pointer-events: none;
}
.progressbar {
  height: 6px; background: var(--line-hairline); border-radius: var(--radius-pill);
  overflow: hidden; margin-bottom: var(--space-xl);
}
.progressbar i {
  display: block; height: 100%; background: var(--stories-deep);
  border-radius: var(--radius-pill);
  transition: width 0.15s linear;
}
.progressbar.reading i { border-radius: 0 var(--radius-pill) var(--radius-pill) 0; }

.chapter-end { margin-top: var(--space-gap); }
.extra-h { font-size: 16px; margin-bottom: var(--space-md); }
.extra-p { margin: 0; font-size: 15.5px; }

.moral {
  background: var(--status-done-surface); border-radius: var(--radius-md);
  padding: var(--space-lg) var(--space-xl); margin-bottom: var(--space-lg);
}
.moral b {
  color: var(--status-done-deep); display: block; font-size: 12.5px;
  letter-spacing: 0.08em; text-transform: uppercase; margin-bottom: var(--space-xs);
}
.moral p { margin: 0; font-size: 16px; color: var(--status-done-deep); }

/* End-of-chapter extras: two columns when there is room. */
.extras { display: grid; gap: var(--space-lg); grid-template-columns: minmax(0, 1fr); }
@media (min-width: 900px) { .extras { grid-template-columns: 1fr 1fr; } }
.extras .card { margin-top: 0; }
.extras .full { grid-column: 1 / -1; }

.glossary { list-style: none; padding: 0; margin: 0; }
.glossary li { padding: var(--space-md) 0; border-bottom: 1px solid var(--line-hairline); font-size: 15px; }
.glossary li:last-child { border-bottom: 0; }
.glossary b { color: var(--ink-strong); }
.qlist { padding-left: var(--space-xl); margin: 0; }
.qlist li { margin-bottom: var(--space-md); font-size: 15.5px; }
.valuerow { display: flex; gap: var(--space-sm); flex-wrap: wrap; margin-top: var(--space-md); }
.valuechip {
  background: var(--gk-surface); color: var(--hue-lavender);
  border-radius: var(--radius-pill); padding: var(--space-sm) var(--space-lg);
  font-size: 14px; font-weight: 700;
}

/* ═══════════════════════════════════════════════════════════════════════════
   Onboarding, Parent Corner, and the odds and ends
   ═════════════════════════════════════════════════════════════════════════ */

.onb { max-width: 480px; margin: 0 auto; padding: var(--space-gap) var(--space-xl) 64px; }
.onb .mark { font-size: 64px; text-align: center; margin-bottom: var(--space-md); }
.onb h1 { font-size: 27px; text-align: center; margin-bottom: var(--space-sm); letter-spacing: -0.02em; }
.onb .lede { text-align: center; color: var(--ink-muted); margin: 0 0 var(--space-huge); font-size: 16px; }
.bandrow { display: flex; gap: var(--space-md); margin-bottom: var(--space-sm); }
.band {
  flex: 1; border: 2px solid var(--line-hairline); background: var(--paper);
  border-radius: var(--radius-lg); padding: var(--space-xl) var(--space-md);
  cursor: pointer; text-align: center; transition: border-color 0.15s, background 0.15s;
}
.band:hover { border-color: var(--gk-edge); }
.band[aria-pressed="true"] { border-color: var(--purple); background: var(--gk-surface); }
.band .n { font-size: 22px; font-weight: 800; color: var(--ink-strong); display: block; }
.band .s { font-size: 13px; color: var(--ink-muted); margin-top: 2px; display: block; }
.steps { display: flex; gap: var(--space-sm); justify-content: center; margin-bottom: var(--space-xxl); }
.steps i { width: 28px; height: 5px; border-radius: var(--radius-pill); background: var(--line-hairline); transition: background 0.3s; }
.steps i.on { background: var(--purple); }

/* Parent Corner: settings groups, two columns on a wide screen. */
.parent-grid { display: grid; gap: var(--space-xxl); grid-template-columns: minmax(0, 1fr); }
@media (min-width: 1000px) { .parent-grid { grid-template-columns: 1fr 1fr; align-items: start; } }
.parent-grid .full { grid-column: 1 / -1; }

.stats { display: flex; gap: var(--space-md); margin-bottom: var(--space-xl); }
.stat {
  flex: 1; background: var(--paper); border: 1px solid var(--line-hairline);
  border-radius: var(--radius-lg); padding: var(--space-lg) var(--space-md);
  text-align: center; box-shadow: 0 10px 30px rgb(36 12 84 / 6%);
}
.stat b { display: block; font-size: 28px; color: var(--ink-strong); line-height: 1.1; }
.stat span {
  display: block; font-size: 11px; font-weight: 800; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--ink-faint); margin-top: var(--space-xs);
}

.rowlist {
  background: var(--paper); border: 1px solid var(--line-hairline);
  border-radius: var(--radius-lg); box-shadow: 0 10px 30px rgb(36 12 84 / 6%);
  overflow: hidden;
}
.row {
  display: flex; align-items: center; gap: var(--space-md);
  padding: var(--space-lg) var(--space-xl); width: 100%;
  background: none; border: none; border-bottom: 1px solid var(--line-hairline);
  cursor: pointer; text-align: left; text-decoration: none;
  color: inherit; font-size: 16px; font-family: inherit;
  transition: background 0.14s;
}
.row:last-child { border-bottom: 0; }
.row:hover { background: var(--page-warm); }
.row .l { font-weight: 700; color: var(--ink-strong); flex: none; }
.row .v {
  flex: 1; text-align: right; color: var(--ink-muted); font-size: 14.5px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.row .c { color: var(--ink-faint); font-size: 20px; flex: none; transition: transform 0.14s; }
.row:hover .c { transform: translateX(2px); }

.sectitle {
  font-size: 12px; font-weight: 800; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--ink-faint); margin: 0 0 var(--space-md);
}

.spinner {
  width: 34px; height: 34px;
  border: 3px solid var(--line-hairline); border-top-color: var(--purple);
  border-radius: 50%; animation: spin 0.8s linear infinite;
  margin: 64px auto var(--space-lg);
}
@keyframes spin { to { transform: rotate(360deg); } }
.loadwrap { text-align: center; padding: var(--space-gap) var(--space-xl); color: var(--ink-muted); }

.appfoot { text-align: center; font-size: 13px; color: var(--ink-faint); padding: var(--space-huge) var(--space-xl) var(--space-xl); }
.appfoot a { color: var(--ink-faint); margin: 0 var(--space-sm); }
@media (min-width: 900px) { .appfoot { display: none; } }

/* ═══════════════════════════════════════════════════════════════════════════
   Motion
   ═════════════════════════════════════════════════════════════════════════ */

/* Everything is inside this query rather than being switched off by a separate
   reduced-motion block at the bottom. Opting IN means a missed animation can
   only ever mean "no movement", never "movement that ignored the setting". */
@media (prefers-reduced-motion: no-preference) {

  /* Screens arrive rather than snap. 220ms and 8px — enough to read as a
     transition, not enough to make a child wait for the page.

     Scoped to `.is-entering`, which mount() adds only when the SCREEN changed.
     Every interaction re-renders the whole screen, so without that gate this
     fired on each level button, each quiz answer, each book swap — and a page
     that fades in every time you touch it reads as a flicker, not as motion. */
  .is-entering .page, .onb {
    animation: enter 0.22s cubic-bezier(0.2, 0.8, 0.3, 1) both;
  }
  @keyframes enter {
    from { opacity: 0; transform: translateY(8px); }
    to   { opacity: 1; transform: none; }
  }

  /* The three tiles land in sequence. --i is set per tile in app.js. */
  .is-entering .tile {
    animation: rise 0.34s cubic-bezier(0.2, 0.8, 0.3, 1) both;
    animation-delay: calc(var(--i, 0) * 70ms + 60ms);
  }
  @keyframes rise {
    from { opacity: 0; transform: translateY(14px) scale(0.985); }
    to   { opacity: 1; transform: none; }
  }

  /* A fact card at a time. NOT gated on `.is-entering`: a level change is
     exactly when this should play, because the point of it is to show that more
     facts just arrived. */
  .facts li {
    animation: rise 0.28s cubic-bezier(0.2, 0.8, 0.3, 1) both;
    animation-delay: calc(var(--i, 0) * 45ms);
  }

  /* The ring counts up on arrival — see setRingFill() in app.js, which sets the
     final offset on the next frame so the transition has something to run. */

  /* The answered option settles rather than jumping colour. */
  .opt.right { animation: pop 0.34s cubic-bezier(0.2, 0.8, 0.3, 1); }
  @keyframes pop {
    0%   { transform: scale(1); }
    45%  { transform: scale(1.025); }
    100% { transform: scale(1); }
  }

  /* Finishing the day. One bounce, no confetti — the app's whole argument is
     that a finished day should feel like an ending, not a slot machine. */
  .alldone { animation: land 0.5s cubic-bezier(0.2, 0.9, 0.3, 1) both; }
  @keyframes land {
    0%   { opacity: 0; transform: scale(0.94) translateY(10px); }
    60%  { opacity: 1; transform: scale(1.015) translateY(0); }
    100% { transform: scale(1); }
  }

  .tile-check { animation: land 0.4s cubic-bezier(0.2, 0.9, 0.3, 1) both; }

  /* The stop button breathes while speech is playing, so a child who cannot
     read still knows something is happening and what to press to stop it. */
  .speak.on { animation: breathe 1.6s ease-in-out infinite; }
  @keyframes breathe {
    0%, 100% { transform: scale(1); }
    50%      { transform: scale(1.035); }
  }
}

/* Belt and braces: if anything above ever moves outside the query, this still
   stops it for someone who asked for stillness. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

@media (max-width: 420px) {
  .title { font-size: 26px; }
  .wordcard .target { font-size: 33px; }
  .page { padding: var(--space-lg); }
}

/* ═══════════════════════════════════════════════════════════════════════════
   The second column

   Detail screens were one column of content stretched across the whole width —
   a fact card 1110px wide holding one short sentence, a word card 1110px wide
   holding a centred word. That is not "space around the content", it is space
   INSIDE it, and it reads worse than a gutter.

   Now the main column takes a sane measure and the rest carries context the
   main column raises but cannot answer: how much of this topic is left, what
   else is in this unit, where am I in this book.
   ═════════════════════════════════════════════════════════════════════════ */

.detail { display: grid; gap: var(--space-xxl); grid-template-columns: minmax(0, 1fr); }
.detail-main { min-width: 0; }

@media (min-width: 1080px) {
  .detail { grid-template-columns: minmax(0, 1fr) 340px; gap: var(--space-gap); }
  .detail-aside {
    position: sticky; top: var(--space-xl);
    max-height: calc(100vh - var(--space-gap));
    overflow-y: auto;
    /* The scrollbar only appears when the aside is genuinely taller than the
       screen — a 59-chapter table of contents, not a two-line meter. */
    scrollbar-width: thin;
  }
}
@media (min-width: 1320px) { .detail { grid-template-columns: minmax(0, 1fr) 380px; } }

.detail-aside { display: flex; flex-direction: column; gap: var(--space-lg); min-width: 0; }

.aside-card {
  background: var(--paper); border: 1px solid var(--line-hairline);
  border-radius: var(--radius-lg); padding: var(--space-xl);
}
.aside-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--space-md); margin-bottom: var(--space-md);
}
.aside-title {
  font-size: 12px; font-weight: 800; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--ink-faint);
}
.aside-note { margin: var(--space-md) 0 0; font-size: 14px; color: var(--ink-muted); }

.meter { display: flex; flex-direction: column; gap: var(--space-sm); }
.meter-track {
  height: 8px; background: var(--line-hairline);
  border-radius: var(--radius-pill); overflow: hidden;
}
.meter-track i {
  display: block; height: 100%; background: var(--teal);
  border-radius: var(--radius-pill);
  transition: width 0.5s cubic-bezier(0.2, 0.8, 0.3, 1);
}
.meter-label { font-size: 14px; font-weight: 700; color: var(--ink-strong); }

/* ── Lists inside an aside ────────────────────────────────────────────────── */

.minilist { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-sm); }
.minirow {
  display: flex; align-items: center; gap: var(--space-md);
  width: 100%; padding: var(--space-sm); border: none; background: none;
  border-radius: var(--radius-md); cursor: pointer; text-align: left;
  font-family: inherit; transition: background 0.14s;
}
.minirow:hover { background: var(--page-warm); }
.minithumb {
  width: 52px; height: 52px; flex: none; border-radius: var(--radius-sm);
  overflow: hidden; background: var(--gk-surface);
  display: grid; place-items: center; font-size: 22px;
}
.minithumb img { width: 100%; height: 100%; object-fit: cover; }
.minitext { min-width: 0; }
.minitext b { display: block; font-size: 14.5px; color: var(--ink-strong); }
.minitext span {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; font-size: 12.5px; color: var(--ink-muted); margin-top: 2px;
}

.wordrow {
  display: flex; align-items: center; gap: var(--space-sm);
  padding: var(--space-sm) var(--space-md); border-radius: var(--radius-sm);
}
.wordrow.now { background: var(--language-surface); }
.wordrow-en { display: flex; align-items: center; gap: 6px; min-width: 0; flex: 1; }
.wordrow-en b { font-size: 14.5px; color: var(--ink-strong); font-weight: 700; }
.wordrow-target {
  font-size: 14px; color: var(--ink-muted); max-width: 45%;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.pill {
  font-size: 10.5px; font-weight: 800; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--ink-faint); background: var(--line-hairline);
  border-radius: var(--radius-pill); padding: 3px 8px; flex: none;
}
.pill.now { background: var(--purple); color: var(--paper); }

   aside any more — choosing the language is the first thing you do on this
   screen, so it belongs above the word, not beside it. */

/* ── Table of contents ────────────────────────────────────────────────────── */

.toc { display: grid; gap: var(--space-sm); }
.toc-section {
  background: var(--paper); border: 1px solid var(--line-hairline);
  border-radius: var(--radius-lg); padding: var(--space-md) var(--space-lg);
}
.toc-section summary {
  cursor: pointer; list-style: none; display: flex;
  align-items: center; justify-content: space-between; gap: var(--space-md);
  padding: var(--space-sm) 0;
}
.toc-section summary::-webkit-details-marker { display: none; }
.toc-name { font-size: 14.5px; font-weight: 800; color: var(--ink-strong); }
.toc-count {
  font-size: 12px; font-weight: 800; color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
}
.toc-list { list-style: none; margin: var(--space-sm) 0 var(--space-sm); padding: 0; }
.toc-item {
  display: flex; align-items: center; gap: var(--space-md);
  width: 100%; padding: var(--space-sm) var(--space-sm);
  border: none; background: none; border-radius: var(--radius-sm);
  cursor: pointer; text-align: left; font-family: inherit;
  font-size: 14px; color: var(--ink-muted); transition: background 0.14s;
}
.toc-item:hover { background: var(--page-warm); color: var(--ink-strong); }
.toc-mark {
  width: 24px; height: 24px; flex: none; border-radius: var(--radius-pill);
  display: grid; place-items: center;
  font-size: 11px; font-weight: 800;
  background: var(--line-hairline); color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
}
.toc-item.done .toc-mark { background: var(--status-done); color: var(--paper); }
.toc-item.now { background: var(--stories-surface); color: var(--ink-strong); font-weight: 700; }
.toc-item.now .toc-mark { background: var(--stories-deep); color: var(--paper); }
.toc-title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ── Browsing a card that is not today's ──────────────────────────────────── */

.browsebar {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-lg); flex-wrap: wrap;
  background: var(--butter-surface); border: 1px solid var(--butter-edge);
  border-radius: var(--radius-md); padding: var(--space-md) var(--space-lg);
  margin-bottom: var(--space-lg); font-size: 14.5px; color: var(--hue-butter);
}
.browsebar .btn { padding: var(--space-sm) var(--space-lg); font-size: 14px; }

/* ── This week ────────────────────────────────────────────────────────────── */

.week {
  background: var(--paper); border: 1px solid var(--line-hairline);
  border-radius: var(--radius-lg); padding: var(--space-xl);
  box-shadow: 0 10px 30px rgb(36 12 84 / 6%);
}
.week-count {
  font-size: 13px; font-weight: 800; color: var(--ink-strong);
  font-variant-numeric: tabular-nums;
}
.week-days { display: grid; grid-template-columns: repeat(7, 1fr); gap: var(--space-xs); }
.week-day {
  display: flex; flex-direction: column; align-items: center; gap: var(--space-sm);
}
.week-letter { font-size: 11px; font-weight: 800; color: var(--ink-faint); }
.week-dot {
  width: 30px; height: 30px; border-radius: var(--radius-pill);
  display: grid; place-items: center;
  background: var(--line-hairline); color: var(--paper);
  font-size: 13px; font-weight: 800;
}
.week-day.done   .week-dot { background: var(--status-done); }
.week-day.rest   .week-dot { background: var(--butter-chip); color: var(--hue-butter); }
.week-day.partial .week-dot { background: var(--teal); opacity: 0.4; }
.week-day.today  .week-dot { box-shadow: 0 0 0 2.5px var(--purple); }
.week-day.today  .week-letter { color: var(--purple); }
.week-note { margin: var(--space-lg) 0 0; font-size: 13.5px; color: var(--ink-muted); }

/* ═══════════════════════════════════════════════════════════════════════════
   The shelf

   Six books ship with 600x800 cover paintings that nothing was using, and the
   Today screen had a screen's worth of empty page below the dashboard.
   ═════════════════════════════════════════════════════════════════════════ */

.shelf { margin-top: var(--space-gap); }
.shelf-head {
  display: flex; align-items: baseline; gap: var(--space-md);
  flex-wrap: wrap; margin-bottom: var(--space-lg);
}
.shelf-title { font-size: 19px; letter-spacing: -0.01em; }
.shelf-note { font-size: 14px; color: var(--ink-muted); }

.shelf-row {
  display: grid; gap: var(--space-lg);
  grid-template-columns: repeat(auto-fill, minmax(132px, 1fr));
}

.book {
  display: flex; flex-direction: column; text-align: left;
  background: none; border: none; padding: 0; cursor: pointer;
  position: relative; font-family: inherit;
  transition: transform 0.16s cubic-bezier(0.2, 0.8, 0.3, 1);
}
.book:hover { transform: translateY(-4px); }
.book:active { transform: translateY(-1px); }

.book-cover {
  display: block; aspect-ratio: 3 / 4; border-radius: var(--radius-md);
  overflow: hidden; background: var(--stories-surface);
  box-shadow: 0 8px 22px rgb(36 12 84 / 14%);
  margin-bottom: var(--space-md);
}
.book-cover img { width: 100%; height: 100%; object-fit: cover; }
.book-emoji { display: grid; place-items: center; width: 100%; height: 100%; font-size: 44px; }
.book:hover .book-cover { box-shadow: 0 14px 30px rgb(36 12 84 / 20%); }

.book-title {
  font-size: 14.5px; font-weight: 800; color: var(--ink-strong); line-height: 1.3;
}
.book-meta { font-size: 12.5px; color: var(--ink-muted); margin-top: 2px; }

/* The one currently feeding the daily chapter. */
.book.active .book-cover { outline: 3px solid var(--stories-deep); outline-offset: 2px; }
.book-badge {
  position: absolute; top: var(--space-sm); left: var(--space-sm);
  background: var(--stories-deep); color: var(--paper);
  font-size: 10.5px; font-weight: 800; letter-spacing: 0.06em; text-transform: uppercase;
  border-radius: var(--radius-pill); padding: 4px 10px;
}

@media (prefers-reduced-motion: no-preference) {
  .is-entering .book {
    animation: rise 0.34s cubic-bezier(0.2, 0.8, 0.3, 1) both;
    animation-delay: calc(var(--i, 0) * 55ms + 200ms);
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   Home — a front door, not a dashboard of everything
   ═════════════════════════════════════════════════════════════════════════ */

/* One column, full width, top to bottom. The two-column layout this replaces
   put five sections in a 380px rail: fine for one deck, wrong for four - the
   rail ran longer than the page beside it, and photographs, the widest thing
   on the screen, were living in its narrowest space. */

/* A titled block: Today, Explore. */
.block { margin-top: var(--space-gap); }
.blockhead { margin-bottom: var(--space-lg); }
.blockhead-top { display: flex; align-items: center; gap: var(--space-sm); flex-wrap: wrap; }
.blockhead-top h2 { font-size: 22px; letter-spacing: -0.01em; }
.blockhead p { margin: var(--space-xs) 0 0; font-size: 14.5px; color: var(--ink-muted); }
.blockflag {
  font-size: 10px; font-weight: 800; letter-spacing: 0.06em; text-transform: uppercase;
  border-radius: var(--radius-pill); padding: 3px 8px; flex: none;
  background: var(--line-hairline); color: var(--ink-faint);
}
.blockflag.done { background: var(--status-done-surface); color: var(--status-done-deep); }
.blockflag.today { background: var(--gk-surface); color: var(--hue-lavender); }

/* The ring and the week strip, for the widths where the rail is not on screen
   to carry them. */
.phoneprogress { margin-top: var(--space-gap); }
.phoneprogress .hero { margin-bottom: var(--space-lg); }
@media (min-width: 900px) { .phoneprogress { display: none; } }

/* ── The explore mosaic ───────────────────────────────────────────────────── */

/* Five tiles. Stories is the tall one because its art is 3:4 and everything
   else is landscape, so it takes a column of its own and the others stack
   beside it. Riddles is a banner: it is one thing a day, not a deck. */
.explore {
  display: grid; gap: var(--space-lg);
  grid-template-columns: minmax(0, 1fr);
}

/* One column: each tile keeps its own content's shape. */
.explore .slides { aspect-ratio: 3 / 2; }
.explore .slides.stories { aspect-ratio: 3 / 4; }

@media (min-width: 700px) {
  /* Explicit row heights rather than aspect ratios: a tile spanning two rows
     has to end level with the two beside it, and only fixed rows can promise
     that. The resulting shapes are close to each content's own - Stories works
     out near 3:4, the pair beside it near 3:2. */
  .explore {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    grid-template-rows: 230px 230px 230px;
    grid-template-areas:
      "stories  discover"
      "stories  language"
      "safety   safety";
  }
  .explore .slides, .explore .slides.stories { aspect-ratio: auto; }
  .explore .slides.stories  { grid-area: stories; }
  .explore .slides.discover { grid-area: discover; }
  .explore .slides.language { grid-area: language; }
  .explore .slides.safety   { grid-area: safety; }
}

@media (min-width: 1080px) {
  .explore {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    grid-template-rows: 250px 250px;
    grid-template-areas:
      "stories  discover  language"
      "stories  safety    safety";
  }
}

/* ── A deck ───────────────────────────────────────────────────────────────── */

/* The tile IS the picture. The label, the dots and the copy all ride on it, so
   nothing outside the frame sets the tile's height and the mosaic's rows stay
   the mosaic's business. */
/* The tile is the picture, and the picture keeps its own colours.
 *
 * These tiles briefly wore a scrim tinted with each section's hue. It read well
 * on the flat panels and badly on the photographs, which is most of them: the
 * app's ink is a deep violet, so any amount of it over the bottom of a picture
 * casts purple across whatever was actually there. A photograph a family is
 * being shown to decide whether they want the app should be the colour it was
 * taken, so the scrim is now neutral and identical on every tile — its only job
 * is to make white text legible.
 *
 * The BACKGROUNDS stay per-section: they show behind an emoji when a word or a
 * scenario has no photograph of its own, and there is no picture there for them
 * to discolour. */
.slides {
  position: relative; display: block; overflow: hidden;
  border-radius: var(--radius-xl);
  background: var(--stories-surface);
  box-shadow: 0 14px 34px rgb(36 12 84 / 15%);
}
.slides.discover { background: var(--gk-surface); }
.slides.language { background: var(--language-surface); }
.slides.safety   { background: var(--safety-surface); }
.slides-stage { position: absolute; inset: 0; }

.slides-tag {
  position: absolute; top: var(--space-lg); left: var(--space-lg); z-index: 2;
  display: inline-flex; align-items: baseline; gap: var(--space-sm);
  border-radius: var(--radius-pill); padding: 6px 14px;
  /* Neutral, like the scrim: a tinted chip on a photograph has the same problem
     as a tinted scrim, in a smaller rectangle. */
  background: rgb(0 0 0 / 58%);
  backdrop-filter: blur(6px);
  pointer-events: none;
}
.slides-tag b { font-size: 14px; font-weight: 800; letter-spacing: -0.01em; color: var(--paper); }

/* All six are always in the DOM, stacked; only the opacity changes. Crossfading
   beats swapping the src, which would show a blank frame on a cold cache.

   `visibility` is delayed by exactly the length of the fade so a slide on its
   way out stays paintable until it has finished going, then stops being a
   target for a click that lands late. */
.slide {
  position: absolute; inset: 0; display: block; width: 100%; height: 100%;
  padding: 0; border: 0; background: none; cursor: pointer; font-family: inherit;
  opacity: 0; visibility: hidden;
  transition: opacity 0.6s ease, visibility 0s linear 0.6s;
}
.slide.on { opacity: 1; visibility: visible; transition: opacity 0.6s ease, visibility 0s; }

.slide-art { position: absolute; inset: 0; display: block; }
.slide-art img { width: 100%; height: 100%; object-fit: cover; }
/* Two thirds of the daily words have no photograph of their own and show their
   emoji instead. At 72px in a 3:2 box it looked mislaid rather than chosen, so
   the shorter decks give it more room. */
.slide-emoji { display: grid; place-items: center; width: 100%; height: 100%; font-size: 72px; }
.slides.language .slide-emoji, .slides.discover .slide-emoji { font-size: 96px; }

/* Neutral black, not the app's ink. Ink is a deep violet, and at any useful
   strength it tints a photograph — which is exactly the complaint this replaced.
   Black darkens without colouring, so the picture above the fade is the colour
   it was taken. Kept short and low so it covers the copy and little else. */
.slide-shade {
  position: absolute; inset: 0; display: block;
  background: linear-gradient(
    to top,
    rgb(0 0 0 / 74%) 0%,
    rgb(0 0 0 / 38%) 24%,
    rgb(0 0 0 / 0%) 54%);
}

.slide-copy {
  position: absolute; left: 0; right: 0; bottom: 0;
  display: flex; flex-direction: column; align-items: flex-start; gap: 3px;
  padding: var(--space-xl); text-align: left;
}
/* The scrim handles a dark cover; the shadow handles the ones that are nearly
   white behind the title - several GK photographs are a subject on a pale
   studio background, and white-on-white is not something a gradient can fix. */
.slide-copy b, .slide-copy > span:last-child {
  text-shadow: 0 1px 8px rgb(0 0 0 / 60%);
}
.slide-copy b { font-size: 20px; line-height: 1.25; color: var(--paper); }
.slide-copy > span:last-child { font-size: 13.5px; color: var(--paper); opacity: 0.86; }
/* The flag is the one thing on a Words slide that says which language this is
   before you have read anything, so it goes above the word rather than beside
   the language name underneath it. */
.slide-flag {
  font-size: 30px; line-height: 1; margin-bottom: var(--space-xs);
  filter: drop-shadow(0 2px 6px rgb(0 0 0 / 50%));
}
.slide-badge {
  background: var(--stories-fill); color: var(--stories-on-fill);
  font-size: 10.5px; font-weight: 800; letter-spacing: 0.06em; text-transform: uppercase;
  border-radius: var(--radius-pill); padding: 4px 10px; margin-bottom: var(--space-sm);
}
.slide:hover .slide-copy b { text-decoration: underline; text-underline-offset: 3px; }

/* A dot per slide, on the picture at top right with the label at top left, so
   neither is in the copy's way at the bottom. The current one stretches as well
   as lightening - at 6px a colour change alone is easy to miss - and every dot
   carries a shadow, because a pale cover would otherwise swallow them. */
.slides-dots {
  position: absolute; top: var(--space-lg); right: var(--space-lg); z-index: 2;
  display: flex; gap: 5px;
}
.dot {
  width: 6px; height: 6px; padding: 0; border: 0; cursor: pointer;
  border-radius: var(--radius-pill);
  background: color-mix(in srgb, var(--paper) 45%, transparent);
  box-shadow: 0 1px 3px color-mix(in srgb, var(--ink-strong) 45%, transparent);
  transition: background 0.25s, width 0.25s;
}
.dot:hover { background: color-mix(in srgb, var(--paper) 75%, transparent); }
.dot.on { background: var(--paper); width: 18px; }

/* Chevrons: the affordance that replaced the motion.
   Hidden until the tile is hovered or something inside it takes focus, so they
   never become another thing moving on their own. Touch has no hover, so there
   they simply stay visible. */
.slides-nav {
  position: absolute; top: 50%; z-index: 3; transform: translateY(-50%);
  width: 34px; height: 34px; padding: 0; border: 0; cursor: pointer;
  border-radius: var(--radius-pill);
  background: color-mix(in srgb, var(--ink-strong) 52%, transparent);
  backdrop-filter: blur(4px);
  color: var(--paper); font-family: inherit; font-size: 22px; line-height: 1;
  display: grid; place-items: center;
  opacity: 0; transition: opacity 0.18s, background 0.18s;
}
.slides-nav.prev { left: var(--space-md); }
.slides-nav.next { right: var(--space-md); }
.slides-nav:hover { background: color-mix(in srgb, var(--ink-strong) 78%, transparent); }
.slides:hover .slides-nav, .slides:focus-within .slides-nav { opacity: 1; }
@media (hover: none) { .slides-nav { opacity: 1; } }

.apphero {
  background: linear-gradient(160deg, var(--butter-surface), var(--page));
  border: 1px solid var(--butter-edge);
  border-radius: var(--radius-xl);
  padding: var(--space-xxl);
  margin-bottom: var(--space-xxl);
}
@media (min-width: 900px) { .apphero { padding: var(--space-huge); } }
.apphero-eyebrow {
  font-size: 13px; font-weight: 800; color: var(--hue-butter);
  margin: 0 0 var(--space-md);
}
.apphero-title { font-size: 26px; letter-spacing: -0.02em; margin-bottom: var(--space-md); }
@media (min-width: 900px) { .apphero-title { font-size: 31px; } }

/* The showcase, inside the hero.
   A banner rather than a card: it sits under a paragraph and above a line of
   closing copy, and a tall tile there would push the day's actual work off the
   screen. Fixed height so the hero's height does not depend on which section
   happens to be showing. */
/* Not a card. As a white box inside the hero it was mostly empty panel — a
   small title adrift in a field of paper with a picture at the far edge. So
   the box is gone: the words sit straight on the hero's own butter, and the
   picture stands beside them in its own frame. The only white on the slide is
   now the picture's matting, and it is exactly as big as the picture. */
.slides.showcase {
  margin: var(--space-xl) 0;
  background: none; box-shadow: none; border-radius: 0; overflow: visible;
}
.slides.showcase .slides-stage { position: absolute; inset: 0; }
@media (min-width: 700px) { .slides.showcase { height: 480px; } }
@media (max-width: 699px) { .slides.showcase { aspect-ratio: 4 / 3; } }

/* The showcase shows its pictures WHOLE.
 *
 * A banner-shaped window cannot hold a 3:4 book cover — at any height it shows
 * a horizontal band of it, and two crops of that band were rejected before this
 * layout. So the showcase slide is split instead: the words live on their own
 * panel at the left, in ink on paper, and the picture stands beside them
 * letterboxed — object-fit: contain — so a cover is the shape a cover is. The
 * scrim goes entirely: it existed to keep white text readable on a photograph,
 * and there is no longer any text on the photograph.
 *
 * Only the showcase works this way. The mosaic tiles keep cover-crop because
 * their windows are close to their pictures' own shapes and a full-bleed
 * photograph is the point of them. */
/* row-reverse because the copy is the LAST child in the slide's markup (it has
   to paint above the art in the stacked decks) but belongs on the left here,
   under the "Inside Famzo Learn" label. */
.slides.showcase .slide {
  /* stretch, not center: the picture's height: 100% needs a full-height flex
     item to resolve against, or it falls back to the file's natural size and
     bursts out of the tile. The copy centers itself — it is a column flex with
     justify-content: center. */
  display: flex; flex-direction: row-reverse; align-items: stretch;
  gap: var(--space-xxl);
}
/* The chevrons were placed for tiles whose picture reaches both edges; here
   the left one sat on top of the title. This deck autoplays and has dots, so
   they go rather than move. */
.slides.showcase .slides-nav { display: none; }
.slides.showcase .slide-shade { display: none; }
/* The words take whatever the picture leaves. The picture, not the panel,
   decides the split — see .slide-art below. */
.slides.showcase .slide-copy {
  position: static; flex: 1; min-width: 0;
  justify-content: center; padding: 0;
}
.slides.showcase .slide-copy b,
.slides.showcase .slide-copy > span:last-child { text-shadow: none; }
/* The left panel carries the FEATURE — a claim and its supporting sentence —
   so it reads at prose sizes, not poster sizes. The section's name is on the
   picture (see .slide-over). */
.slides.showcase .slide-copy b {
  font-size: 26px; line-height: 1.25; letter-spacing: -0.01em; color: var(--ink-strong);
  max-width: 17ch;
}
.slides.showcase .slide-copy > span:last-child {
  font-size: 16px; line-height: 1.55; color: var(--ink-muted); opacity: 1;
  margin-top: var(--space-md); max-width: 38ch;
}
/* The picture owns the right 56% of the tile, drawn to the size the design
   settled on rather than whatever shape today's file happens to be. `cover`
   is back — with the name ON the picture there is no all-white cover matting
   for it to sit against, and the window is now big enough that the crop takes
   edges, not subjects. */
.slides.showcase .slide-art {
  position: relative; flex: 0 0 56%;
  display: block;
  background: none;
  border-radius: var(--radius-lg); overflow: hidden;
  box-shadow: 0 14px 34px rgb(36 12 84 / 16%);
}
.slides.showcase .slide-art img {
  width: 100%; height: 100%; object-fit: cover;
  /* Book covers keep faces in the upper half; photographs keep subjects near
     the middle. 35% serves both better than dead centre. */
  object-position: center 35%;
}

/* The section's name, printed on the picture's lower left over its own short
   scrim — the same grammar as the Explore tiles below, so "the name is on the
   picture" is one rule everywhere. */
.slide-over {
  position: absolute; left: 0; right: 0; bottom: 0;
  padding: 44px var(--space-xl) var(--space-lg);
  font-size: 22px; font-weight: 800; letter-spacing: -0.01em; color: var(--paper);
  text-shadow: 0 1px 8px rgb(0 0 0 / 60%);
  background: linear-gradient(to top, rgb(0 0 0 / 58%), rgb(0 0 0 / 0%));
  pointer-events: none;
}
.slides.showcase .slide-emoji {
  font-size: 84px;
  display: grid; place-items: center; width: 100%; height: 100%;
  background: var(--page-warm);
}
/* On butter, the chip becomes a plain eyebrow — same voice as the "Daily 10
   minutes" line above it, because it now lives in the same panel. Still
   absolute (it is a sibling of the stage, not part of the copy flow), but
   pinned to the tile's own top-left corner so it lines up with the title. */
.slides.showcase .slides-tag {
  position: absolute; top: 0; left: 0;
  background: none; backdrop-filter: none; padding: 0;
}
.slides.showcase .slides-tag b {
  color: var(--hue-butter); font-size: 12px; letter-spacing: 0.08em;
  text-transform: uppercase;
}
@media (max-width: 699px) {
  /* Stacked below 700px: picture on top, words underneath. column, not
     column-reverse: with the copy LAST in the markup, column-reverse would put
     it back on top of the picture. */
  .slides.showcase .slide { flex-direction: column; gap: var(--space-md); }
  .slides.showcase .slide-copy { flex: 0 0 auto; padding: 0; }
  .slides.showcase .slide-copy b { font-size: 20px; }
  /* In a column the art's height is no longer set by stretching, so it takes
     `flex: 1` of the tile and the image sizes against that — the same
     natural-size blowout guard as the row layout, turned 90 degrees. */
  .slides.showcase .slide-art {
    flex: 1; min-height: 0; justify-content: center;
  }
  .slides.showcase .slide-art img { max-width: 100%; }
  /* Hidden, not restyled: at this width it would sit on the picture, and the
     hero's own eyebrow two lines up already names the app. */
  .slides.showcase .slides-tag { display: none; }
}
.apphero-lede {
  font-size: 17px; color: var(--ink-muted); margin: 0 0 var(--space-xxl); max-width: 58ch;
}
.apphero-after {
  margin: var(--space-xl) 0 0; font-size: 14.5px; color: var(--ink-muted); max-width: 56ch;
}

/* The owl came off this panel when the showcase pictures grew to fill its
   corner — a mascot and a photograph in the same right-hand column made two
   things compete for one glance. The owl still owns the rail, the header and
   onboarding. */

/* ── The riddle teaser ────────────────────────────────────────────────────── */

.teaser {
  display: flex; align-items: center; gap: var(--space-xl); width: 100%;
  background: var(--riddles-surface); border: 2px solid transparent;
  border-radius: var(--radius-xl); padding: var(--space-xl);
  cursor: pointer; font-family: inherit; text-align: left;
  transition: transform 0.14s, border-color 0.14s;
}
.teaser:hover { transform: translateY(-2px); border-color: var(--riddles-edge); }
.teaser-art {
  width: 64px; height: 64px; flex: none; border-radius: var(--radius-pill);
  background: var(--paper); display: grid; place-items: center; font-size: 32px;
}
.teaser-body { min-width: 0; flex: 1; }
.teaser-body b { display: block; font-size: 16.5px; color: var(--ink-strong); }
.teaser-body > span {
  display: block; margin-top: 2px; font-size: 15px; color: var(--ink-muted);
}
.teaser-go { flex: none; font-size: 20px; color: var(--ink-faint); }
.teaser.is-locked { opacity: 0.75; }

/* ── The word menu ────────────────────────────────────────────────────────── */

.wordbrowse { margin-top: var(--space-gap); }

/* The signpost to the browser below the fold. Sits on the crumb line so it
   costs no height of its own. */
.crumbrow {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--space-md); margin-bottom: var(--space-sm);
}
.crumbrow .crumb { margin-bottom: 0; }
.crumbrow.words { justify-content: flex-end; margin: var(--space-md) 0; }
.browsejump {
  display: inline-flex; align-items: center; gap: 6px; flex: none;
  background: none; border: 0; cursor: pointer; font-family: inherit;
  font-size: 13.5px; font-weight: 800; color: var(--purple);
  padding: var(--space-xs) var(--space-md); border-radius: var(--radius-pill);
}
.browsejump:hover { background: var(--gk-surface); }
.browsejump .arrow { transition: transform 0.16s; }
.browsejump:hover .arrow { transform: translateY(2px); }
.unitrow { display: flex; flex-wrap: wrap; gap: var(--space-sm); margin-bottom: var(--space-xl); }
.unitchip {
  display: inline-flex; align-items: center; gap: 6px;
  background: var(--paper); border: 2px solid var(--line-hairline);
  border-radius: var(--radius-pill); padding: var(--space-sm) var(--space-lg);
  font-size: 14px; font-weight: 700; color: var(--ink-strong);
  cursor: pointer; font-family: inherit;
  transition: background 0.14s, border-color 0.14s;
}
.unitchip:hover { background: var(--language-surface); border-color: var(--language-edge); }
.unitchip[aria-pressed="true"] {
  background: var(--language-fill); border-color: var(--language-fill);
  color: var(--language-on-fill);
}
/* The same chip on the Discover screen wears Discover's ramp, not Words'. */
.unitchip.gk:hover { background: var(--gk-surface); border-color: var(--gk-edge); }
.unitchip.gk[aria-pressed="true"] {
  background: var(--gk-fill); border-color: var(--gk-fill);
  color: var(--gk-on-fill);
}

/* The Discover browser's theme cards: like wordcells, but landscape, because
   the GK photographs are 3:2 and a theme is named after what its pictures show. */
.themegrid {
  display: grid; gap: var(--space-md);
  grid-template-columns: repeat(auto-fill, minmax(170px, 1fr));
}
.themecell {
  display: flex; flex-direction: column; gap: 2px; text-align: left;
  background: var(--paper); border: 2px solid var(--line-hairline);
  border-radius: var(--radius-lg); padding: var(--space-md);
  cursor: pointer; font-family: inherit; min-width: 0;
  transition: transform 0.14s, border-color 0.14s, box-shadow 0.14s;
}
.themecell:hover {
  transform: translateY(-3px); border-color: var(--gk-edge);
  box-shadow: 0 12px 26px rgb(36 12 84 / 12%);
}
.themecell.now { border-color: var(--gk-deep); background: var(--gk-surface); }
.themecell-art {
  display: block; aspect-ratio: 3 / 2; width: 100%; overflow: hidden;
  border-radius: var(--radius-md); background: var(--gk-surface);
  margin-bottom: var(--space-sm);
}
.themecell-art img { width: 100%; height: 100%; object-fit: cover; }
.themecell-art .fallback {
  display: grid; place-items: center; width: 100%; height: 100%; font-size: 34px;
}
.themecell-name { font-size: 14px; font-weight: 800; color: var(--ink-strong); }
.themecell-meta { font-size: 12.5px; color: var(--ink-muted); }

/* Only about a third of the words have a photograph; the rest show their emoji.
   A square tile made those two thirds a large flat panel with a small glyph
   marooned in it, and nineteen of them is a lot of page. 5:4 and a narrower
   cell keeps the photographs worth looking at without giving the emoji ones
   more room than they can fill. */
.wordgrid {
  display: grid; gap: var(--space-md);
  grid-template-columns: repeat(auto-fill, minmax(112px, 1fr));
}
.wordcell {
  display: flex; flex-direction: column; gap: 2px; text-align: center;
  background: var(--paper); border: 2px solid var(--line-hairline);
  border-radius: var(--radius-lg); padding: var(--space-md);
  cursor: pointer; font-family: inherit; min-width: 0;
  transition: transform 0.14s, border-color 0.14s, box-shadow 0.14s;
}
.wordcell:hover {
  transform: translateY(-3px); border-color: var(--language-edge);
  box-shadow: 0 12px 26px rgb(36 12 84 / 12%);
}
.wordcell.now { border-color: var(--language-deep); background: var(--language-surface); }
.wordcell-art {
  display: block; aspect-ratio: 5 / 4; width: 100%; overflow: hidden;
  border-radius: var(--radius-md); background: var(--language-surface);
  margin-bottom: var(--space-sm);
}
.wordcell-art img { width: 100%; height: 100%; object-fit: cover; }
.wordcell-emoji { display: grid; place-items: center; width: 100%; height: 100%; font-size: 30px; }
.wordcell-en { font-size: 13.5px; font-weight: 800; color: var(--ink-strong); }
.wordcell-target {
  font-size: 14px; font-weight: 700; color: var(--language-deep);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

@media (prefers-reduced-motion: no-preference) {
  /* The mosaic assembles tile by tile in reading order. Gated on
     `.is-entering`, so nothing replays when a tap re-renders the screen. */
  .is-entering .explore > * {
    animation: rise 0.4s cubic-bezier(0.2, 0.8, 0.3, 1) both;
    animation-delay: calc(var(--t, 0) * 90ms + 140ms);
  }
  .is-entering .explore > *:nth-child(2) { --t: 1; }
  .is-entering .explore > *:nth-child(3) { --t: 2; }
  .is-entering .explore > *:nth-child(4) { --t: 3; }
  .is-entering .explore > *:nth-child(5) { --t: 4; }

  .is-entering .wordcell, .is-entering .themecell {
    animation: rise 0.3s cubic-bezier(0.2, 0.8, 0.3, 1) both;
    animation-delay: calc(var(--i, 0) * 28ms + 120ms);
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   Subscription
   ═════════════════════════════════════════════════════════════════════════ */

/* The strip on Home. Butter while the trial is running out, because it is a
   reminder; the attention colour once it has gone, because it is now the reason
   three of the five sections will not open. */
.trialbar {
  display: flex; align-items: center; gap: var(--space-lg); flex-wrap: wrap;
  background: var(--butter-surface); border: 1px solid var(--butter-edge);
  border-radius: var(--radius-lg); padding: var(--space-lg) var(--space-xl);
  margin-bottom: var(--space-xl);
}
.trialbar.ended { background: var(--status-wrong-surface); border-color: var(--status-wrong-deep); }
.trialbar-copy { flex: 1; min-width: 200px; }
.trialbar-copy b { display: block; font-size: 16px; color: var(--ink-strong); }
.trialbar-copy span { display: block; margin-top: 2px; font-size: 14px; color: var(--ink-muted); }
.trialbar .btn { flex: none; }

.paywall { text-align: center; max-width: 560px; margin: var(--space-gap) auto; }
.paywall-art {
  font-size: 56px; width: 116px; height: 116px; margin: 0 auto var(--space-xl);
  display: grid; place-items: center;
  background: var(--butter-surface); border-radius: var(--radius-pill);
}
.paywall .desc { margin-left: auto; margin-right: auto; }
.paywall .notice { text-align: left; }

/* Two plans, side by side where there is room. No prices here on purpose - see
   planButtons() for why a second copy of a number is a liability. */
.planrow {
  display: grid; gap: var(--space-lg); margin-bottom: var(--space-xl);
  grid-template-columns: minmax(0, 1fr);
}
@media (min-width: 560px) { .planrow { grid-template-columns: 1fr 1fr; } }
.plancard {
  display: flex; flex-direction: column; gap: 2px; text-align: left;
  background: var(--paper); border: 2px solid var(--line-hairline);
  border-radius: var(--radius-lg); padding: var(--space-xl);
  text-decoration: none; transition: transform 0.14s, border-color 0.14s, box-shadow 0.14s;
}
.plancard:hover {
  transform: translateY(-2px); border-color: var(--purple);
  box-shadow: 0 12px 28px rgb(36 12 84 / 12%);
}
.plancard b { font-size: 18px; color: var(--ink-strong); }
.plancard > span { font-size: 14px; color: var(--ink-muted); }
.plancard-go { margin-top: var(--space-md); font-weight: 800; color: var(--teal-deep) !important; }

/* The state card, shared by Account and the Plans screen. */
.subcard {
  background: var(--status-done-surface); border-radius: var(--radius-lg);
  padding: var(--space-xl); margin-bottom: var(--space-lg);
}
.subcard.off { background: var(--page-warm); }
.subcard-top { display: flex; align-items: center; gap: var(--space-md); flex-wrap: wrap; }
.subcard-top b { font-size: 18px; color: var(--ink-strong); }
.subcard p { margin: var(--space-sm) 0 0; font-size: 15px; color: var(--ink-muted); }
.subcard-manage {
  display: inline-block; margin-top: var(--space-md);
  font-size: 14.5px; font-weight: 700; color: var(--purple);
}

/* Store badges, and the line that stands in for them until there is something
   to link to. */
.storerow { display: flex; gap: var(--space-md); flex-wrap: wrap; }
.storebadge {
  display: inline-flex; align-items: center; gap: var(--space-md);
  background: var(--ink-strong); color: var(--paper); text-decoration: none;
  border-radius: var(--radius-md); padding: var(--space-md) var(--space-lg);
}
.storebadge-mark { font-size: 24px; line-height: 1; }
.storebadge small { display: block; font-size: 10.5px; opacity: 0.75; }
.storebadge b { display: block; font-size: 15px; }
.storerow-soon {
  display: block; text-decoration: none;
  background: var(--page-warm); border: 1px dashed var(--line-hairline);
  border-radius: var(--radius-lg); padding: var(--space-lg) var(--space-xl);
}
.storerow-soon b { display: block; font-size: 15px; color: var(--ink-strong); }
.storerow-soon span { display: block; margin-top: 2px; font-size: 13.5px; color: var(--ink-muted); }

/* Onboarding's last screen says what the trial is before the button that starts it. */
.onb-trial {
  background: var(--status-done-surface); border-radius: var(--radius-lg);
  padding: var(--space-lg) var(--space-xl); margin-top: var(--space-lg); text-align: left;
}
.onb-trial b { display: block; font-size: 15px; color: var(--status-done-deep); }
.onb-trial span { display: block; margin-top: 2px; font-size: 13.5px; color: var(--ink-muted); }

/* ═══════════════════════════════════════════════════════════════════════════
   Riddles
   ═════════════════════════════════════════════════════════════════════════ */

.locked { text-align: center; max-width: 560px; margin: var(--space-gap) auto; }
.locked-art {
  font-size: 72px; width: 128px; height: 128px; margin: 0 auto var(--space-xl);
  display: grid; place-items: center;
  background: var(--riddles-surface); border-radius: var(--radius-pill);
}
.locked .desc { margin-left: auto; margin-right: auto; }
.lockedrow {
  display: flex; gap: var(--space-md); justify-content: center;
  flex-wrap: wrap; margin-top: var(--space-xl);
}

.clues { list-style: none; counter-reset: clue; padding: 0; margin: 0 0 var(--space-xl); }
.clues li {
  counter-increment: clue; position: relative;
  background: var(--riddles-surface);
  border-radius: var(--radius-md);
  padding: var(--space-lg) var(--space-lg) var(--space-lg) 56px;
  margin-bottom: var(--space-md); font-size: 17px; color: var(--ink-strong);
}
.clues li::before {
  content: counter(clue);
  position: absolute; left: var(--space-lg); top: var(--space-lg);
  width: 26px; height: 26px; border-radius: var(--radius-pill);
  background: var(--riddles-fill); color: var(--riddles-on-fill);
  display: grid; place-items: center; font-size: 13px; font-weight: 800;
}
@media (prefers-reduced-motion: no-preference) {
  /* On arrival at the screen, the clues stagger in. After that only the clue a
     hint just revealed moves — see the note in renderRiddles. */
  .is-entering .clues li {
    animation: rise 0.3s cubic-bezier(0.2, 0.8, 0.3, 1) both;
    animation-delay: calc(var(--i, 0) * 90ms);
  }
  .clues li.fresh { animation: rise 0.32s cubic-bezier(0.2, 0.8, 0.3, 1) both; }
}

.ropts { display: grid; gap: var(--space-md); margin-bottom: var(--space-xl); }
.ropts.pics { grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); }
.ropt {
  background: var(--paper); border: 2px solid var(--line-hairline);
  border-radius: var(--radius-lg); padding: var(--space-md);
  cursor: pointer; font-family: inherit; overflow: hidden;
  display: flex; flex-direction: column; gap: var(--space-md);
  transition: border-color 0.15s, transform 0.12s, background 0.15s;
}
.ropts:not(.pics) .ropt { flex-direction: row; align-items: center; padding: var(--space-lg); }
.ropt:hover:not([disabled]) { border-color: var(--riddles-edge); transform: translateY(-2px); }
.ropt-art {
  display: block; aspect-ratio: 1 / 1; border-radius: var(--radius-md);
  overflow: hidden; background: var(--riddles-surface);
}
.ropt-art img { width: 100%; height: 100%; object-fit: cover; }
.ropt-emoji { display: grid; place-items: center; width: 100%; height: 100%; font-size: 40px; }
.ropt-label { font-size: 16px; font-weight: 700; color: var(--ink-strong); text-align: center; }
.ropts:not(.pics) .ropt-label { text-align: left; }
.ropt.right { border-color: var(--status-done); background: var(--status-done-surface); }
.ropt.wrong { border-color: var(--status-wrong); background: var(--status-wrong-surface); }
.ropt[disabled] { cursor: default; }

.hintrow { display: flex; align-items: center; gap: var(--space-lg); flex-wrap: wrap; }

.rresult { border-radius: var(--radius-lg); padding: var(--space-xl); }
.rresult.good { background: var(--status-done-surface); }
.rresult.kind { background: var(--butter-surface); }
.rresult h3 { font-size: 19px; margin-bottom: var(--space-sm); }
.rresult.good h3 { color: var(--status-done-deep); }
.rresult.kind h3 { color: var(--hue-butter); }
.rresult p { margin: 0 0 var(--space-sm); font-size: 15.5px; }
.rresult p:last-child { margin-bottom: 0; color: var(--ink-muted); }

/* ═══════════════════════════════════════════════════════════════════════════
   Smart & Safe
   ═════════════════════════════════════════════════════════════════════════ */

.sq {
  background: var(--safety-surface); border-radius: var(--radius-lg);
  padding: var(--space-xl); margin-bottom: var(--space-xl);
}
.sq-q { font-size: 18px; margin-bottom: var(--space-lg); color: var(--ink-strong); }
.sq-opts { display: grid; gap: var(--space-md); }
.sq-opt {
  background: var(--paper); border: 2px solid transparent;
  border-radius: var(--radius-md); padding: var(--space-lg);
  font-size: 16px; font-weight: 600; color: var(--ink-strong);
  cursor: pointer; text-align: left; width: 100%; font-family: inherit;
  transition: border-color 0.15s, background 0.15s, transform 0.12s;
}
.sq-opt:hover:not([disabled]) { border-color: var(--safety-deep); transform: translateX(2px); }
.sq-opt.right { border-color: var(--status-done); background: var(--status-done-surface); }
.sq-opt.wrong { border-color: var(--status-wrong); background: var(--status-wrong-surface); }
.sq-opt[disabled] { cursor: default; }

/* The owl speaks AFTER an answer. It is a voice, not decoration — putting it on
   every card as a mascot made it mean nothing. */
.owl {
  display: flex; gap: var(--space-lg); align-items: flex-start;
  border-radius: var(--radius-lg); padding: var(--space-xl);
  margin-bottom: var(--space-xl);
}
.owl.good { background: var(--status-done-surface); }
.owl.redo { background: var(--butter-surface); }
.owl-face { font-size: 38px; line-height: 1; flex: none; }
.owl-say { min-width: 0; }
.owl-line { margin: 0 0 var(--space-sm); font-size: 17px; font-weight: 700; color: var(--ink-strong); }
.owl-why { margin: 0; font-size: 15px; color: var(--ink-muted); }

/* The chant is the part that survives. It gets the loudest treatment on the
   screen for exactly that reason. */
.chant {
  background: var(--safety-fill); color: var(--safety-on-fill);
  border-radius: var(--radius-lg); padding: var(--space-xl);
  margin-bottom: var(--space-xl); text-align: center;
}
.chant-label {
  font-size: 11.5px; font-weight: 800; letter-spacing: 0.12em;
  text-transform: uppercase; opacity: 0.8;
}
.chant-line {
  font-size: 24px; font-weight: 800; line-height: 1.35;
  margin: var(--space-md) 0 var(--space-lg);
}
.chant .speak { background: var(--paper); color: var(--safety-fill); }

.parentnote {
  background: var(--paper); border: 1px solid var(--line-hairline);
  border-left: 4px solid var(--parent-deep);
  border-radius: var(--radius-md); padding: var(--space-lg);
  margin-top: var(--space-xl);
}
.parentnote b {
  display: block; font-size: 11.5px; font-weight: 800; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--parent-deep); margin-bottom: var(--space-sm);
}
.parentnote p { margin: 0; font-size: 15px; }

.badges { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-md); }
.badge { text-align: center; }
.badge img { width: 56px; height: 56px; margin: 0 auto var(--space-sm); object-fit: contain; }
.badge-emoji { display: block; font-size: 40px; margin-bottom: var(--space-sm); }
.badge-name { font-size: 12.5px; font-weight: 700; color: var(--ink-strong); }

.drill {
  display: flex; gap: var(--space-md); align-items: flex-start; width: 100%;
  background: none; border: none; padding: var(--space-md);
  border-radius: var(--radius-md); cursor: pointer; text-align: left;
  font-family: inherit; transition: background 0.14s;
}
.drill:hover { background: var(--page-warm); }
.drill-check {
  width: 24px; height: 24px; flex: none; border-radius: var(--radius-sm);
  border: 2px solid var(--line-hairline); display: grid; place-items: center;
  font-size: 13px; font-weight: 800; color: var(--paper);
}
.drill.done .drill-check { background: var(--status-done); border-color: var(--status-done); }
.drill-text { min-width: 0; }
.drill-text b { display: block; font-size: 14.5px; color: var(--ink-strong); }
.drill-text > span { display: block; font-size: 13px; color: var(--ink-muted); margin-top: 2px; }
.drill-mins { font-size: 12px !important; color: var(--ink-faint) !important; margin-top: var(--space-sm) !important; }
