/* Polyglitz design system. Loaded after every other sheet (only css/lang.css comes later), so it is the last word on
   colour, type, spacing and shared parts. The tokens and classes are listed in docs/design-system.md: new pages should
   use them instead of inventing their own.
   Rules that only change looks are wrapped in :where(html:not(.a11y-contrast)) so the high contrast mode (js/a11y.js)
   keeps its own strong borders and colours. html.pref-easy is the easy-learning mode for ADHD and autism: soft
   colours, no motion, nothing decorative. */

/* =====================================================================
   1. Tokens
   ===================================================================== */
:root {
  /* surfaces and text (light) */
  --bg: #f6f7fb; --surface: #ffffff; --surface-2: #f1f3f9; --surface-3: #e7eaf3;
  --card: var(--surface); /* older rooms write var(--card, #fff): without this, dark mode got white fields under light text */
  --ink: #0f1428; --ink-2: #363d55; --muted: #566079; --faint: #7d859b;
  --rule: #e2e6ef; --hairline: rgb(15 20 40 / .08); --hairline-strong: rgb(15 20 40 / .14);
  /* brand: indigo to violet, with Polly's gold sparkle */
  --accent: #3f49d8; --accent-2: #6d46d9; --accent-ink: #ffffff;
  --accent-soft: color-mix(in srgb, var(--accent) 9%, var(--surface));
  --accent-line: color-mix(in srgb, var(--accent) 28%, transparent);
  --grad: linear-gradient(135deg, #4250e0 0%, #5f47dd 55%, #7c4adf 100%);
  --gold: #f2b02a; --gold-ink: #8a5a00; --gold-soft: #fff4d8;
  /* states */
  /* deep enough to be read as text on their own tints (4.5:1 up to a 14% tint), as well as to carry white text */
  --ok: #0d7445; --ok-soft: #e3f5ec; --bad: #b8281e; --bad-soft: #fdebe8; --warn: #975a00; --warn-soft: #fff1da; --info: #1569a6;
  /* level colours (css/style.css) one step deeper, so the light text on level chips reads at 4.5:1 */
  --a1: #17804a; --a2: #11709a; --b1: #8c6000; --b2: #b8441f;
  /* type */
  --font-ui: 'Rubik', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif;
  --font-body: var(--font-ui);
  --font-display: var(--font-ui);
  --fs-display: clamp(2rem, 1.35rem + 2.2vw, 3rem);
  --fs-h1: clamp(1.65rem, 1.3rem + 1.3vw, 2.25rem);
  --fs-h2: clamp(1.25rem, 1.12rem + .5vw, 1.5rem);
  --fs-h3: 1.125rem;
  --fs-lg: 1.0625rem; --fs-sm: .875rem; --fs-xs: .78rem;
  /* space, shape, depth, motion */
  --sp-1: 4px; --sp-2: 8px; --sp-3: 12px; --sp-4: 16px; --sp-5: 20px; --sp-6: 24px; --sp-8: 32px; --sp-10: 40px; --sp-12: 48px; --sp-16: 64px;
  --r-xs: 8px; --r-sm: 12px; --r: 16px; --r-lg: 20px; --r-xl: 28px; --r-pill: 999px;
  --e1: 0 1px 2px rgb(15 20 40 / .06);
  --e2: 0 1px 2px rgb(15 20 40 / .05), 0 4px 14px -4px rgb(15 20 40 / .09);
  --e3: 0 2px 4px rgb(15 20 40 / .05), 0 14px 32px -10px rgb(15 20 40 / .16);
  --e4: 0 4px 10px rgb(15 20 40 / .06), 0 28px 60px -18px rgb(34 40 120 / .28);
  --shadow-xs: var(--e1); --shadow: var(--e2); --shadow-lg: var(--e4);
  --highlight: inset 0 1px 0 rgb(255 255 255 / .9);
  --ring: 0 0 0 3px color-mix(in srgb, var(--accent) 28%, transparent);
  --ease: cubic-bezier(.2, .7, .2, 1); --dur-1: .12s; --dur-2: .2s; --dur-3: .32s;
  /* the app shell */
  --side-w: 264px; --bar-h: 60px; --dock-h: 64px; --content-max: 1120px;
  /* bold "bento" shapes (docs/plan/2026-10-04-redesign-bento.md): a deep panel for the one dark card on a page, six soft
     tints made from colours we already use, one green for the single "learn for free" button */
  --panel: #16183a; --on-panel: #fff; --on-panel-2: rgb(255 255 255 / .8);
  --bx-grad: linear-gradient(135deg, #3c46d2 0%, #5843d4 55%, #7342d0 100%);
  --cta: #0d7a47; --on-cta: #fff;
  --tint-1: color-mix(in srgb, var(--accent) 12%, var(--surface)); --tint-ink-1: color-mix(in srgb, var(--accent) 85%, var(--ink));
  --tint-2: color-mix(in srgb, var(--accent-2) 12%, var(--surface)); --tint-ink-2: color-mix(in srgb, var(--accent-2) 85%, var(--ink));
  --tint-3: color-mix(in srgb, var(--gold) 18%, var(--surface)); --tint-ink-3: var(--gold-ink);
  --tint-4: color-mix(in srgb, var(--ok) 12%, var(--surface)); --tint-ink-4: var(--ok);
  --tint-5: color-mix(in srgb, var(--info) 12%, var(--surface)); --tint-ink-5: color-mix(in srgb, var(--info) 85%, var(--ink));
  --tint-6: color-mix(in srgb, #e8692b 13%, var(--surface)); --tint-ink-6: #a8440f;
  --r-2xl: 32px;
  --e-bento: 0 1px 2px rgb(16 18 40 / .06), 0 12px 32px -12px rgb(16 18 40 / .18);
  --fs-hero: clamp(2.1rem, 1.4rem + 3.2vw, 3.6rem);
  --fs-card: clamp(1.15rem, 1rem + .6vw, 1.45rem);
  --bento-gap: 12px;
}

/* Scripts Rubik does not cover (B22): Devanagari, Chinese and Japanese. Rubik keeps Latin and Cyrillic, then each
   system's own font for the script (no web font is downloaded: CJK files are megabytes). Arabic is in Rubik. */
html:lang(hi) { --font-ui: 'Rubik', 'Noto Sans Devanagari', 'Nirmala UI', 'Kohinoor Devanagari', Mangal, system-ui, sans-serif; }
html:lang(zh) { --font-ui: 'Rubik', 'PingFang SC', 'Microsoft YaHei', 'Noto Sans SC', 'Noto Sans CJK SC', 'Source Han Sans SC', system-ui, sans-serif; }
html:lang(ja) { --font-ui: 'Rubik', 'Hiragino Sans', 'Hiragino Kaku Gothic ProN', 'Yu Gothic UI', 'Yu Gothic', Meiryo, 'Noto Sans JP', 'Noto Sans CJK JP', system-ui, sans-serif; }
html:is(:lang(hi), :lang(zh), :lang(ja)) { --font-display: var(--font-ui); --font-target: 'Bricolage Grotesque', var(--font-ui); }
/* Arabic as the language being learned (docs/plan/2026-10-28-arabic-look.md): a naskh face carries the vowel marks best,
   and every system has one (Android Noto Naskh, Apple Geeza Pro, Windows Segoe UI); Rubik's Arabic is the bundled
   fallback. The marks sit above and below the letters, so an Arabic line needs far more height than a Latin one. */
:root { --font-ar: 'Noto Naskh Arabic', 'Geeza Pro', 'Segoe UI', 'Rubik', Tahoma, sans-serif; --ar-lh: 1.8; }
/* Aurora Glass (docs/plan/2026-10-29-aurora-glass.md): frosted glass lit from above, over a real aurora night sky. The
   night look (css/ds/cosmos.css) and the kids layer (css/kids-2050.css) both draw from these, in any theme. --ag-tint is
   the dark layer under the sheen that keeps white text readable where the aurora is brightest. */
:root {
  --ag-cy: #22d3ee; --ag-pk: #e879f9; --ag-lime: #a3e635;
  --ag-sheen: linear-gradient(150deg, rgb(255 255 255 / .2), rgb(255 255 255 / .06) 62%);
  --ag-tint: rgb(9 17 38 / .48); --ag-edge: rgb(255 255 255 / .26); --ag-edge-strong: rgb(255 255 255 / .4);
  --ag-spec: inset 0 1.5px 0 rgb(255 255 255 / .42);
  --ag-shadow: 0 22px 40px -20px rgb(0 0 0 / .8);
  --ag-blur: blur(16px) saturate(1.6);
  --ag-cta: linear-gradient(#e3ff8f, #a3e635); --ag-cta-ink: #0a1a02; --ag-cta-edge: #5f8a14;
  /* the dark wash over the aurora photo (media/site-bg/night-aurora.webp): light at the top so the aurora glows, deep
     at the bottom where the page's text gathers; the photo itself is set where it is used, so its address stays relative */
  --ag-scrim: linear-gradient(180deg, rgb(5 12 26 / .28), rgb(5 12 24 / .56) 45%, rgb(5 11 20 / .82));
}
/* weak phones (js/scene.js): every glass card that blurs through this token stops blurring; the cards keep their tint */
html.weak-phone { --ag-blur: none; }

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #0a0e1a; --surface: #121929; --surface-2: #192135; --surface-3: #212a41;
    --ink: #eef1f8; --ink-2: #c8cee0; --muted: #9aa3bc; --faint: #6f7893;
    --rule: #252e46; --hairline: rgb(255 255 255 / .07); --hairline-strong: rgb(255 255 255 / .13); --line: var(--hairline-strong);
    --accent: #8f9bff; --accent-2: #b394ff; --accent-ink: #0a0e1a;
    --accent-soft: color-mix(in srgb, var(--accent) 14%, var(--surface));
    --grad: linear-gradient(135deg, #8d9bff 0%, #9d8cff 55%, #b48dff 100%);
    --gold-ink: #f6c453; --gold-soft: #3a2f12;
    --ok: #46c486; --ok-soft: #12321f; --bad: #ff7b6c; --bad-soft: #3a1915; --warn: #f2b043; --warn-soft: #3a2a10; --info: #63b3f0;
    --e1: 0 1px 2px rgb(0 0 0 / .4);
    --e2: 0 1px 2px rgb(0 0 0 / .35), 0 6px 18px -6px rgb(0 0 0 / .55);
    --e3: 0 2px 4px rgb(0 0 0 / .4), 0 16px 36px -12px rgb(0 0 0 / .65);
    --e4: 0 4px 10px rgb(0 0 0 / .4), 0 30px 64px -20px rgb(0 0 0 / .8);
    --highlight: inset 0 1px 0 rgb(255 255 255 / .05);
    --panel: #1d2150; --bx-grad: linear-gradient(135deg, #2f38a8 0%, #4634a8 55%, #5a34a6 100%);
    --tint-1: color-mix(in srgb, var(--accent) 20%, var(--surface)); --tint-ink-1: var(--accent);
    --tint-2: color-mix(in srgb, var(--accent-2) 20%, var(--surface)); --tint-ink-2: var(--accent-2);
    --tint-3: color-mix(in srgb, var(--gold) 16%, var(--surface)); --tint-ink-3: var(--gold-ink);
    --tint-4: color-mix(in srgb, var(--ok) 18%, var(--surface)); --tint-ink-4: var(--ok);
    --tint-5: color-mix(in srgb, var(--info) 18%, var(--surface)); --tint-ink-5: var(--info);
    --tint-6: color-mix(in srgb, #ff8a4c 16%, var(--surface)); --tint-ink-6: #ff9a66;
    --e-bento: 0 1px 2px rgb(0 0 0 / .35), 0 14px 34px -14px rgb(0 0 0 / .7);
  }
}
html.pref-large { --side-w: 284px; }
html.pref-xl { --side-w: 304px; }
:root[data-theme="dark"] {
  --bg: #0a0e1a; --surface: #121929; --surface-2: #192135; --surface-3: #212a41;
  --ink: #eef1f8; --ink-2: #c8cee0; --muted: #9aa3bc; --faint: #6f7893;
  --rule: #252e46; --hairline: rgb(255 255 255 / .07); --hairline-strong: rgb(255 255 255 / .13); --line: var(--hairline-strong);
  --accent: #8f9bff; --accent-2: #b394ff; --accent-ink: #0a0e1a;
  --accent-soft: color-mix(in srgb, var(--accent) 14%, var(--surface));
  --grad: linear-gradient(135deg, #8d9bff 0%, #9d8cff 55%, #b48dff 100%);
  --gold-ink: #f6c453; --gold-soft: #3a2f12;
  --ok: #46c486; --ok-soft: #12321f; --bad: #ff7b6c; --bad-soft: #3a1915; --warn: #f2b043; --warn-soft: #3a2a10; --info: #63b3f0;
  --e1: 0 1px 2px rgb(0 0 0 / .4);
  --e2: 0 1px 2px rgb(0 0 0 / .35), 0 6px 18px -6px rgb(0 0 0 / .55);
  --e3: 0 2px 4px rgb(0 0 0 / .4), 0 16px 36px -12px rgb(0 0 0 / .65);
  --e4: 0 4px 10px rgb(0 0 0 / .4), 0 30px 64px -20px rgb(0 0 0 / .8);
  --highlight: inset 0 1px 0 rgb(255 255 255 / .05);
  --panel: #1d2150; --bx-grad: linear-gradient(135deg, #2f38a8 0%, #4634a8 55%, #5a34a6 100%);
  --tint-1: color-mix(in srgb, var(--accent) 20%, var(--surface)); --tint-ink-1: var(--accent);
  --tint-2: color-mix(in srgb, var(--accent-2) 20%, var(--surface)); --tint-ink-2: var(--accent-2);
  --tint-3: color-mix(in srgb, var(--gold) 16%, var(--surface)); --tint-ink-3: var(--gold-ink);
  --tint-4: color-mix(in srgb, var(--ok) 18%, var(--surface)); --tint-ink-4: var(--ok);
  --tint-5: color-mix(in srgb, var(--info) 18%, var(--surface)); --tint-ink-5: var(--info);
  --tint-6: color-mix(in srgb, #ff8a4c 16%, var(--surface)); --tint-ink-6: #ff9a66;
  --e-bento: 0 1px 2px rgb(0 0 0 / .35), 0 14px 34px -14px rgb(0 0 0 / .7);
}

/* =====================================================================
   2. Base: page, type scale, links, focus
   ===================================================================== */
:where(html:not(.a11y-contrast)) body {
  background:
    radial-gradient(900px 520px at 100% -8%, color-mix(in srgb, var(--accent-2) 9%, transparent), transparent 70%),
    radial-gradient(760px 520px at -6% 22%, color-mix(in srgb, #3fb7a4 7%, transparent), transparent 70%),
    var(--bg);
  background-attachment: fixed;
}
body { font-family: var(--font-ui); color: var(--ink); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-rendering: optimizeLegibility; }
body::before { opacity: .55; }
h1 { font-size: var(--fs-h1); line-height: 1.15; letter-spacing: -.02em; }
h2 { font-size: var(--fs-h2); line-height: 1.25; letter-spacing: -.012em; }
h3 { font-size: var(--fs-h3); line-height: 1.3; }
:where(:lang(he)) :is(h1, h2, h3) { font-weight: 600; letter-spacing: -.005em; }
:where(:lang(he)) h1 { font-weight: 700; }
:where(:lang(ru)) h3 { font-weight: 600; }
p { text-wrap: pretty; }
.lead { font-size: var(--fs-lg); color: var(--ink-2); line-height: 1.6; }
.muted { color: var(--muted); }
.small { font-size: var(--fs-sm); }
.eyebrow { font-size: var(--fs-xs); font-weight: 600; letter-spacing: .1em; text-transform: uppercase; color: var(--muted); }
:lang(he) .eyebrow { font-size: .82rem; letter-spacing: .01em; }
.num, .timer, .gem-count { font-variant-numeric: tabular-nums; }
:where(#view) a:not([class]) { color: var(--accent); text-underline-offset: 3px; text-decoration-thickness: 1px; }
:where(#view) a:not([class]):hover { text-decoration-thickness: 2px; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 6px; }
/* the "skip to content" link waits fully above the screen, even when its text takes two lines on a narrow phone */
.skip-link:not(:focus) { top: auto; bottom: 100%; }
::selection { background: color-mix(in srgb, var(--accent) 22%, transparent); }
:where(html:not(.a11y-contrast)) { scrollbar-color: color-mix(in srgb, var(--ink) 22%, transparent) transparent; }

/* =====================================================================
   3. Shared components
   ===================================================================== */
/* ---------- buttons: one shape, four kinds (primary, default, ghost, danger), two sizes ---------- */
:where(html:not(.a11y-contrast)) .btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px; text-align: center;
  min-height: 44px; padding: .55em 1.2em; border-radius: 14px; border: 1px solid var(--hairline-strong);
  font: 600 .95rem/1.25 var(--font-ui); letter-spacing: 0; color: var(--ink); text-decoration: none;
  background: var(--surface); box-shadow: var(--e1), inset 0 -2px 0 rgb(15 20 40 / .05);
  transform: none; filter: none;
  transition: background-color var(--dur-1), border-color var(--dur-1), box-shadow var(--dur-2) var(--ease), transform var(--dur-1), filter var(--dur-1);
}
:where(html:not(.a11y-contrast)) .btn:hover:not(:disabled) { transform: none; border-color: color-mix(in srgb, var(--ink) 22%, transparent); background: color-mix(in srgb, var(--surface-2) 55%, var(--surface)); box-shadow: var(--e2), inset 0 -2px 0 rgb(15 20 40 / .05); }
:where(html:not(.a11y-contrast)) .btn:active:not(:disabled) { transform: translateY(1px); box-shadow: var(--e1); }
:where(html:not(.a11y-contrast)) .btn:focus-visible { outline: none; box-shadow: var(--ring), var(--e1); }
:where(html:not(.a11y-contrast)) .btn:disabled { opacity: .5; filter: saturate(.4); cursor: not-allowed; }
:where(html:not(.a11y-contrast)) .btn.small { min-height: 36px; padding: .4em .95em; border-radius: 11px; font-size: .86rem; }
/* a finger needs more room than a mouse pointer */
@media (pointer: coarse) { .btn.small { min-height: 44px; } }
:where(html:not(.a11y-contrast)) .btn.ghost { background: transparent; border-color: var(--hairline-strong); box-shadow: none; }
:where(html:not(.a11y-contrast)) .btn.ghost:hover:not(:disabled) { background: color-mix(in srgb, var(--ink) 5%, transparent); box-shadow: none; }
:where(html:not(.a11y-contrast)) .btn.primary {
  color: #fff; border-color: transparent; text-shadow: 0 1px 1px rgb(20 10 80 / .2);
  background: var(--grad); background-size: 100% 100%;
  box-shadow: inset 0 1px 0 rgb(255 255 255 / .22), inset 0 -2px 0 rgb(10 10 60 / .18), 0 1px 2px rgb(30 30 100 / .22), 0 8px 18px -8px color-mix(in srgb, var(--accent) 75%, transparent);
}
:where(html:not(.a11y-contrast)) .btn.primary:hover:not(:disabled) { background: var(--grad); filter: brightness(1.07) saturate(1.04);
  box-shadow: inset 0 1px 0 rgb(255 255 255 / .22), inset 0 -2px 0 rgb(10 10 60 / .18), 0 1px 2px rgb(30 30 100 / .22), 0 12px 24px -10px color-mix(in srgb, var(--accent) 80%, transparent); }
:where(html:not(.a11y-contrast)) .btn.primary:active:not(:disabled) { box-shadow: inset 0 1px 0 rgb(255 255 255 / .18), 0 1px 2px rgb(30 30 100 / .25); }
:where(html:not(.a11y-contrast)) .btn.primary:focus-visible { box-shadow: var(--ring), inset 0 -2px 0 rgb(10 10 60 / .18), 0 8px 18px -8px color-mix(in srgb, var(--accent) 75%, transparent); }
:root[data-theme="dark"]:not(.a11y-contrast) .btn.primary { color: #0a0e1a; text-shadow: none; }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]):not(.a11y-contrast) .btn.primary { color: #0a0e1a; text-shadow: none; } }
:where(html:not(.a11y-contrast)) .btn.danger { color: #fff; border-color: transparent; background: var(--bad); box-shadow: inset 0 -2px 0 rgb(0 0 0 / .16), 0 6px 14px -8px color-mix(in srgb, var(--bad) 80%, transparent); }
:where(html:not(.a11y-contrast)) .btn.danger:hover:not(:disabled) { background: var(--bad); filter: brightness(1.06); }
/* ghost and danger set their own shadow above, which hid the keyboard focus ring; give it back (also while hovered) */
:where(html:not(.a11y-contrast)) .btn:is(.ghost, .danger):focus-visible:not(:disabled) { box-shadow: var(--ring); }
/* the old "shine" sweep is retired: buttons stay still */
.btn.primary:is(:hover, :focus-visible):not(:disabled) { background-position: 0 0; }
/* white keys on colourful cards (friends banner, featured plan) keep their dark text */
html:root:not(.a11y-contrast) :is(.so-hero, .plan.featured) .btn.primary { color: #1d2358; text-shadow: none; background: #fff;
  box-shadow: inset 0 -2px 0 rgb(20 20 80 / .12), 0 10px 24px -12px rgb(0 0 0 / .55); }
:where(html:not(.a11y-contrast)) .btn.share-wa { color: #06301a; background: #2fd06b; border-color: transparent; box-shadow: inset 0 -2px 0 rgb(0 60 20 / .2), var(--e1); }
:where(html:not(.a11y-contrast)) .btn.share-tg { color: #fff; background: #1673b0; border-color: transparent; box-shadow: inset 0 -2px 0 rgb(0 30 70 / .25), var(--e1); }
.link-btn { color: var(--accent); font-weight: 500; text-underline-offset: 3px; }
.icon-btn { display: inline-grid; place-items: center; width: 40px; height: 40px; padding: 0; border-radius: 12px; color: var(--muted); font-size: 1.6rem; transition: background-color var(--dur-1), color var(--dur-1); }
.icon-btn:hover { background: color-mix(in srgb, var(--ink) 6%, transparent); color: var(--ink); }

/* ---------- fields ---------- */
:where(html:not(.a11y-contrast)) select {
  min-height: 40px; border-radius: 12px; border: 1px solid var(--hairline-strong); background-color: var(--surface); color: var(--ink);
  font: 500 .92rem/1.2 var(--font-ui); box-shadow: var(--e1);
}
/* the open list of a select is drawn by the browser over white: --surface can be see-through (cosmos sky), so use the solid --bg or it is light text on white */
:where(html:not(.a11y-contrast)) :is(select option, select optgroup) { background-color: var(--bg); color: var(--ink); }
:where(html:not(.a11y-contrast)) :is(.auth input, .acct-form input, .copy-row input, .profile-form input, .admin-search, .cp-form input, .cp-form select, .type-form input, textarea) {
  min-height: 46px; border-radius: 12px; border: 1px solid var(--hairline-strong); background: var(--surface); color: var(--ink);
  box-shadow: inset 0 1px 2px rgb(15 20 40 / .04); transition: border-color var(--dur-1), box-shadow var(--dur-1);
}
:where(html:not(.a11y-contrast)) :is(input, select, textarea):focus-visible { outline: none; border-color: var(--accent); box-shadow: var(--ring); }
:where(html:not(.a11y-contrast)) :is(.auth, .acct-form, .profile-form) label { font-weight: 500; color: var(--ink-2); }

/* ---------- cards ---------- */
:where(html:not(.a11y-contrast)) :is(.panel, .auth-wrap, .game-card, .lb-me, .lb-cta, .game-end, .tip, .wd-panel, .test-result, .ds-card) {
  background: var(--surface); border: 1px solid var(--hairline); border-radius: var(--r-lg); box-shadow: var(--e2);
}
.ds-card { padding: var(--sp-6); display: grid; gap: var(--sp-3); }
.ds-card.tight { padding: var(--sp-4); }
.ds-card.flat { box-shadow: none; background: var(--surface-2); border-color: transparent; }
a.ds-card, button.ds-card { color: inherit; text-decoration: none; text-align: start; font: inherit; cursor: pointer; transition: border-color var(--dur-2), box-shadow var(--dur-2) var(--ease), transform var(--dur-2) var(--ease); }
a.ds-card:hover, button.ds-card:hover { border-color: var(--accent-line); box-shadow: var(--e3); }
.panel h2 { font-size: var(--fs-h3); font-weight: 600; }

/* section header: a title and an optional link on the other side */
.ds-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-3); margin: 0 0 var(--sp-4); }
.ds-head :is(h2, h3) { margin: 0; }
.ds-head a, .ds-head .link-btn { font-size: var(--fs-sm); font-weight: 600; color: var(--accent); text-decoration: none; white-space: nowrap; }
.ds-head a:hover { text-decoration: underline; }

/* grid of tiles that fills the row */
.ds-grid { display: grid; gap: var(--sp-4); grid-template-columns: repeat(auto-fill, minmax(min(220px, 100%), 1fr)); }
.ds-grid.wide { grid-template-columns: repeat(auto-fill, minmax(min(300px, 100%), 1fr)); }

/* a tile: picture or icon, title, one line (games, categories, ways to learn) */
.ds-tile { position: relative; display: grid; gap: var(--sp-2); align-content: start; padding: var(--sp-4); border-radius: var(--r-lg); background: var(--surface);
  border: 1px solid var(--hairline); box-shadow: var(--e1); color: inherit; text-decoration: none; text-align: start; font: inherit; cursor: pointer;
  transition: border-color var(--dur-2), box-shadow var(--dur-2) var(--ease), transform var(--dur-2) var(--ease); }
.ds-tile:hover { border-color: var(--accent-line); box-shadow: var(--e3); transform: translateY(-2px); }
.ds-tile img { width: 100%; aspect-ratio: 16 / 10; object-fit: cover; border-radius: var(--r-sm); background: var(--surface-2); }
.ds-tile strong { font-size: 1rem; font-weight: 600; }
.ds-tile small, .ds-tile .muted { font-size: var(--fs-sm); color: var(--muted); line-height: 1.45; }

/* icon in a soft rounded square; --t tints it (each menu section has its own colour) */
.ds-ico { --t: var(--accent); display: inline-grid; place-items: center; flex: none; width: 40px; height: 40px; border-radius: 12px;
  color: var(--t); background: color-mix(in srgb, var(--t) 12%, var(--surface)); }
.ds-ico svg { width: 22px; height: 22px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }

/* key number (streak, points, words) */
.ds-kpi { display: grid; gap: 2px; padding: var(--sp-3) var(--sp-4); border-radius: var(--r); background: var(--surface-2); }
.ds-kpi b, .ds-kpi strong { font: 700 1.5rem/1.15 var(--font-target); letter-spacing: -.01em; color: var(--ink); }
.ds-kpi span { font-size: var(--fs-xs); font-weight: 500; color: var(--muted); }

/* ---------- pills, badges, chips ---------- */
:where(html:not(.a11y-contrast)) .pill { display: inline-flex; align-items: center; gap: 4px; padding: .22em .7em; border-radius: var(--r-pill); font-size: var(--fs-xs); font-weight: 600; line-height: 1.4;
  background: var(--surface-2); color: var(--ink-2); border: 1px solid var(--hairline); }
:where(html:not(.a11y-contrast)) .pill.ok { background: var(--ok-soft); border-color: color-mix(in srgb, var(--ok) 22%, transparent); }
.ds-badge { display: inline-flex; align-items: center; gap: 4px; padding: .2em .6em; border-radius: var(--r-pill); font-size: .72rem; font-weight: 700; letter-spacing: .02em;
  color: var(--accent); background: var(--accent-soft); border: 1px solid var(--accent-line); }
.ds-badge.gold { color: var(--gold-ink); background: var(--gold-soft); border-color: color-mix(in srgb, var(--gold) 40%, transparent); }
.ds-badge.ok { color: var(--ok); background: var(--ok-soft); border-color: color-mix(in srgb, var(--ok) 28%, transparent); }
/* small bold text on a tinted pill needs more than the plain accent to clear AA 4.5:1 ("soon" badges measured 4.34) */
:where(html:not(.a11y-contrast)) .ds-badge:not(.gold, .ok) { color: color-mix(in srgb, var(--accent) 78%, var(--ink)); }
:where(html:not(.a11y-contrast)) .chip { min-height: 40px; border-radius: var(--r-pill); border: 1px solid var(--hairline-strong); box-shadow: var(--e1); font-weight: 500;
  transition: border-color var(--dur-1), background-color var(--dur-1), box-shadow var(--dur-1); }
:where(html:not(.a11y-contrast)) .chip:hover { border-color: var(--accent-line); }
:where(html:not(.a11y-contrast)) :is(.chip[aria-checked="true"], .chip[aria-pressed="true"]) { box-shadow: 0 4px 12px -6px color-mix(in srgb, var(--accent) 70%, transparent); }
:where(html:not(.a11y-contrast)) .chip.lv[aria-checked="true"] { box-shadow: 0 4px 12px -6px color-mix(in srgb, var(--lv) 70%, transparent); }
:where(html:not(.a11y-contrast)) .seg { padding: 3px; border-radius: 12px; background: var(--surface-2); border: 1px solid var(--hairline); box-shadow: none; }
:where(html:not(.a11y-contrast)) .seg button { border-radius: 9px; min-height: 34px; }
:where(html:not(.a11y-contrast)) .seg button[aria-pressed="true"] { background: var(--surface); color: var(--ink); box-shadow: var(--e1), 0 2px 6px -3px rgb(15 20 40 / .18); }
.badge { font-weight: 700; font-size: .7rem; min-width: 1.6em; height: 1.6em; }

/* ---------- progress ---------- */
.ds-progress { height: 8px; border-radius: var(--r-pill); background: color-mix(in srgb, var(--ink) 8%, transparent); overflow: hidden; }
.ds-progress > span { display: block; height: 100%; border-radius: inherit; background: var(--lv, var(--accent)); transition: width .5s var(--ease); }
.lc-bar, .lh-bar, .q-bar, .progress { box-shadow: none; }

/* ---------- toasts, empty states, dialogs ---------- */
:where(html:not(.a11y-contrast)) .toast { border-radius: 14px; padding: .8em 1.2em; font-weight: 500; box-shadow: var(--e4); }
:where(html:not(.a11y-contrast)) .empty { border-radius: var(--r-xl); border: 1px solid var(--hairline); background: color-mix(in srgb, var(--surface) 80%, transparent); box-shadow: var(--e2); }
dialog::backdrop { background: rgb(8 11 24 / .5); backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px); }
.mascot-img { box-shadow: 0 0 0 4px var(--surface), 0 0 0 5px var(--hairline-strong), var(--e3); }

/* ---------- Arabic text: .ds-ar, and target words a lesson prints with lang="ar" ----------
   Space between letters pulls the joined letters apart, and the lessons' tight target lines (1.1 to 1.3) cut the vowel
   marks, so Arabic resets both, also under the spacing and dyslexia preferences. The doubled :root outranks those
   rules without !important. Lessons print every target word dir="ltr"; direction here turns Arabic the right way. The
   attribute is matched on the word itself, not inherited: in the Arabic interface English target words stay as they are. */
:root:root :is(.ds-ar, .tgt[lang|="ar"]) {
  font-family: var(--font-ar); line-height: var(--ar-lh); letter-spacing: normal; word-spacing: normal;
  font-variant-ligatures: normal; text-transform: none; direction: rtl; unicode-bidi: isolate; }

/* =====================================================================
   4. App shell: header, menu, sidebar (desktop), tab strip (tablet), top bar + menu + tab bar (phone), footer
   ===================================================================== */
/* nav icons: one line icon per section, drawn with the text colour */
.tabs a::before, .dock-i::before, .nav-x::before {
  content: ""; display: inline-block; flex: none; width: 20px; height: 20px; background: currentColor; filter: none; margin: 0;
  -webkit-mask: var(--ico, var(--ico-def)) center / contain no-repeat; mask: var(--ico, var(--ico-def)) center / contain no-repeat;
}
.dock-more::before { --ico: var(--ico-more); }
.dock-set::before { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 7h9M17 7h3M4 17h3M11 17h9'/%3E%3Ccircle cx='15' cy='7' r='2.2'/%3E%3Ccircle cx='9' cy='17' r='2.2'/%3E%3C/svg%3E"); }
.nav-x::before { --ico: var(--ico-close); }
:root { --ico-def: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 3.5l1.9 4.6 4.6 1.9-4.6 1.9-1.9 4.6-1.9-4.6L5.5 10l4.6-1.9z'/%3E%3Cpath d='M18.5 15.5l.8 1.9 1.9.8-1.9.8-.8 1.9-.8-1.9-1.9-.8 1.9-.8z'/%3E%3C/svg%3E"); --ico-more: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='4' y='4' width='6.5' height='6.5' rx='1.8'/%3E%3Crect x='13.5' y='4' width='6.5' height='6.5' rx='1.8'/%3E%3Crect x='4' y='13.5' width='6.5' height='6.5' rx='1.8'/%3E%3Crect x='13.5' y='13.5' width='6.5' height='6.5' rx='1.8'/%3E%3C/svg%3E"); --ico-close: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 6l12 12M18 6 6 18'/%3E%3C/svg%3E"); }
[data-nav="map"] { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='6' cy='19' r='2'/%3E%3Ccircle cx='18' cy='5' r='2'/%3E%3Cpath d='M8 19h7.5a3.5 3.5 0 0 0 0-7h-7a3.5 3.5 0 0 1 0-7H16'/%3E%3C/svg%3E"); }
[data-nav="plan"] { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3.5' y='5' width='17' height='15.5' rx='2.5'/%3E%3Cpath d='M3.5 10h17M8 3v4M16 3v4M9 15l2 2 4-4'/%3E%3C/svg%3E"); }
[data-nav="review"] { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 11V9.5A3.5 3.5 0 0 1 7.5 6H19M16 3l3 3-3 3M20 13v1.5a3.5 3.5 0 0 1-3.5 3.5H5M8 21l-3-3 3-3'/%3E%3C/svg%3E"); }
[data-nav="videos"] { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='5' width='18' height='14' rx='3'/%3E%3Cpath d='M10 9.3v5.4l4.6-2.7z'/%3E%3C/svg%3E"); }
[data-nav="games"] { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M7 8h10a4 4 0 0 1 4 4v1.5a3.5 3.5 0 0 1-6.2 2.1L13.6 14h-3.2l-1.2 1.6A3.5 3.5 0 0 1 3 13.5V12a4 4 0 0 1 4-4z'/%3E%3Cpath d='M7.5 10.5v3M6 12h3M15.5 11.2h.01M17.5 12.8h.01'/%3E%3C/svg%3E"); }
[data-nav="coach"] { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 20h16'/%3E%3Crect x='5.5' y='12' width='3' height='5' rx='1'/%3E%3Crect x='10.5' y='8' width='3' height='9' rx='1'/%3E%3Crect x='15.5' y='4' width='3' height='13' rx='1'/%3E%3C/svg%3E"); }
[data-nav="league"] { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8 4h8v5a4 4 0 0 1-8 0z'/%3E%3Cpath d='M8 6H5v2a3 3 0 0 0 3 3M16 6h3v2a3 3 0 0 1-3 3M12 13v3M9 20h6M10 16h4l.6 4H9.4z'/%3E%3C/svg%3E"); }
[data-nav="cat"] { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 4.5l4 3.2h6l4-3.2V13a7 7 0 0 1-14 0z'/%3E%3Cpath d='M9.5 12.2v.6M14.5 12.2v.6M11 15.6l1 .7 1-.7M2.5 13.5l4 .3M2.8 16.6l3.8-1M21.5 13.5l-4 .3M21.2 16.6l-3.8-1'/%3E%3C/svg%3E"); }
[data-nav="friends"] { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='9' cy='8.5' r='3.5'/%3E%3Cpath d='M2.5 20a6.5 6.5 0 0 1 13 0M15.5 5.3a3.5 3.5 0 0 1 0 6.4M17.5 14.2a6.5 6.5 0 0 1 4 5.8'/%3E%3C/svg%3E"); }
[data-nav="stories"] { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 6.5C10.5 5.2 8.3 4.5 4 4.5v13c4.3 0 6.5.7 8 2 1.5-1.3 3.7-2 8-2v-13c-4.3 0-6.5.7-8 2zM12 6.5v13'/%3E%3C/svg%3E"); }
[data-nav="categories"] { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='4' y='4' width='6.5' height='6.5' rx='1.8'/%3E%3Ccircle cx='16.75' cy='7.25' r='3.25'/%3E%3Cpath d='M7.25 13.5l3.5 6.5h-7z'/%3E%3Crect x='13.5' y='13.5' width='6.5' height='6.5' rx='3.25'/%3E%3C/svg%3E"); }
[data-nav="paths"] { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 21v-9M12 12 6.5 7.5M12 12l5.5-4.5'/%3E%3Ccircle cx='12' cy='21' r='.5'/%3E%3Cpath d='M4 9.5V5h4.5M20 9.5V5h-4.5'/%3E%3C/svg%3E"); }
[data-nav="grammar"] { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 20 10 4h1l5 16M7 14h7'/%3E%3Cpath d='M17 9.5c.8-1 1.7-1.5 2.7-1.5'/%3E%3C/svg%3E"); }
[data-nav="talk"] { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 6.5A2.5 2.5 0 0 1 6.5 4h11A2.5 2.5 0 0 1 20 6.5v7a2.5 2.5 0 0 1-2.5 2.5H11l-4.5 4v-4A2.5 2.5 0 0 1 4 13.5z'/%3E%3Cpath d='M8.5 9h7M8.5 12h4.5'/%3E%3C/svg%3E"); }
[data-nav="speak"] { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='9' y='3' width='6' height='11' rx='3'/%3E%3Cpath d='M5.5 11a6.5 6.5 0 0 0 13 0M12 17.5V21M9 21h6'/%3E%3C/svg%3E"); }
[data-nav="dict"] { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='10.5' cy='10.5' r='6'/%3E%3Cpath d='M15 15l5.5 5.5'/%3E%3C/svg%3E"); }
[data-nav="me"] { --ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='8' r='4'/%3E%3Cpath d='M4.5 20.5a7.5 7.5 0 0 1 15 0'/%3E%3C/svg%3E"); }
/* each section keeps its own colour on its icon */
:where(.tabs a, .dock-i) { --t: var(--accent); }
[data-nav="map"] { --t: #1f9d5c; } [data-nav="plan"] { --t: #d9820b; } [data-nav="review"] { --t: #d9407a; }
[data-nav="videos"] { --t: #e0443d; } [data-nav="games"] { --t: #7b4ff0; } [data-nav="coach"] { --t: #0f93b8; }
[data-nav="league"] { --t: #c98f00; } [data-nav="cat"] { --t: #e8692b; } [data-nav="friends"] { --t: #2f6fe0; }
[data-nav="stories"] { --t: #a8612f; }
:root[data-theme="dark"] .tabs a::before { color: color-mix(in srgb, var(--t) 62%, #fff); }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .tabs a::before { color: color-mix(in srgb, var(--t) 62%, #fff); } }

/* ---------- header (all sizes) ---------- */
:where(html:not(.a11y-contrast)) .top { border-bottom: 1px solid var(--hairline); }
.brand { gap: 10px; border-radius: 12px; }
.brand-mark, .brand-anim { width: 40px; height: 40px; margin-block: 0; }
.brand-text { font-size: 1.28rem; letter-spacing: -.03em; }
:where(html:not(.a11y-contrast)) :is(.theme-btn, .lang-btn, .nav-tool) {
  width: 40px; height: 40px; border-radius: 12px; border: 1px solid var(--hairline-strong); background: var(--surface); color: var(--ink-2);
  box-shadow: var(--e1); transition: border-color var(--dur-1), color var(--dur-1), background-color var(--dur-1);
}
:where(html:not(.a11y-contrast)) .lang-btn { width: auto; min-width: 40px; padding: 0 10px; gap: 6px; font: 600 .82rem/1 var(--font-ui); }
:where(html:not(.a11y-contrast)) :is(.theme-btn, .lang-btn, .nav-tool):hover { transform: none; border-color: var(--accent-line); color: var(--accent); background: var(--surface); }
.theme-btn svg { width: 19px; height: 19px; }
/* The interface language (js/lang.js) reads as a globe and one code. A flag stands for a country, not a language,
   and Windows draws flag emoji as two letters, so the button said "HE IL" and "RU RU". */
.top .lang-btn > span[aria-hidden] { display: none; }
.top .lang-btn .lang-code { display: inline; }
.top .lang-btn::before { content: ''; flex: none; width: 18px; height: 18px; background: currentColor;
  -webkit-mask: var(--ico-globe) center / contain no-repeat; mask: var(--ico-globe) center / contain no-repeat; }
:root { --ico-globe: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M3 12h18M12 3c2.5 2.7 3.8 5.7 3.8 9s-1.3 6.3-3.8 9c-2.5-2.7-3.8-5.7-3.8-9S9.5 5.7 12 3z'/%3E%3C/svg%3E"); }
/* tool buttons for the accessibility menu and Polly's sea switch, in the tablet header and the phone menu (js/ui-shell.js) */
.nav-tool { display: none; place-items: center; width: 40px; height: 40px; padding: 0; appearance: none; cursor: pointer;
  border-radius: 12px; border: 1px solid var(--rule); background: var(--surface); color: var(--ink); }
.nav-tool svg { width: 20px; height: 20px; fill: currentColor; }
.nav-tool[data-tool="sea"] svg { fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
:where(html:not(.a11y-contrast)) .gem-count { min-height: 40px; padding: 0 12px; border-radius: 12px; border: 1px solid var(--hairline-strong); background: var(--surface); box-shadow: var(--e1); font-size: .92rem; gap: 6px; }
.gem-count svg { fill: #7b5cf0; }
:where(html:not(.a11y-contrast)) .account-btn { min-height: 40px; border-radius: 12px; font: 600 .92rem/1 var(--font-ui); padding: 0 16px; border: 1px solid transparent; color: #fff;
  background: var(--grad); box-shadow: inset 0 -2px 0 rgb(10 10 60 / .16), 0 6px 14px -8px color-mix(in srgb, var(--accent) 80%, transparent); }
:where(html:not(.a11y-contrast)) .account-btn:has(.avatar) { padding: 0; padding-inline: 4px 12px; color: var(--ink); background: var(--surface); border-color: var(--hairline-strong); box-shadow: var(--e1); }
.account-btn .avatar { width: 32px; height: 32px; font-size: .9rem; }
.acc-name { max-width: 12ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The plan badge (app.js planBadge): a die-cut "free" sticker that leads to the plans, or the paid member's emblem,
   gold on indigo (Plus) or on onyx with a crown (Premium Plus, full access). A card in the sidebar and the phone menu,
   an icon in the phone's top bar. */
.plan-badge { position: relative; display: inline-flex; align-items: center; gap: 10px; min-height: 40px; padding: 4px; padding-inline-end: 12px;
  font: 500 .8rem/1.15 var(--font-ui); text-decoration: none; white-space: nowrap; overflow: hidden; isolation: isolate; }
.plan-badge .pb-ico { display: grid; place-items: center; flex: none; width: 32px; height: 32px; border-radius: 10px; }
.plan-badge .pb-ico svg { width: 19px; height: 19px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.plan-badge .pb-text { display: grid; gap: 2px; min-width: 0; }
.plan-badge .pb-sub { font-size: .68rem; font-weight: 500; letter-spacing: .03em; }
.plan-badge .pb-name { overflow: hidden; text-overflow: ellipsis; font-size: .95rem; font-weight: 700; letter-spacing: .01em; }
.plan-badge .pb-up { flex: none; margin-inline-start: auto; padding: 4px 10px; border-radius: 999px; font-size: .74rem; font-weight: 700; }
html.in-lesson .plan-badge { display: none; }
/* free: a quiet chip, not a sales sticker (research remove-or-soften #4: a permanent "Upgrade" on every page annoyed
   people); it still leads to the plans */
:where(html:not(.a11y-contrast)) .plan-badge.pb-free { color: var(--ink-2); border: 1px solid var(--hairline-strong); border-radius: 12px; background: var(--surface);
  transition: border-color var(--dur-1), color var(--dur-1); }
:where(html:not(.a11y-contrast)) .plan-badge.pb-free:hover { color: var(--ink); border-color: var(--accent-line); }
:where(html:not(.a11y-contrast)) .pb-free .pb-ico { color: #138a55; background: color-mix(in srgb, #138a55 10%, transparent); }
:where(html:not(.a11y-contrast)) .pb-free .pb-sub { color: var(--muted); }
:where(html:not(.a11y-contrast)) .pb-free .pb-name { font-weight: 600; }
/* paid: a gold foil frame, a gold coin for the icon and gold lettering; a light passes over it on hover */
:where(html:not(.a11y-contrast)) .plan-badge:is(.pb-plus, .pb-pro, .pb-full) { color: #f3dc98; border: 1.5px solid transparent; border-radius: 14px;
  background: linear-gradient(135deg, #1c2152 0%, #2b2063 55%, #18163a 100%) padding-box,
    linear-gradient(135deg, #f9e9ac 0%, #c8952f 30%, #fff4c9 52%, #a7731b 78%, #ecc96c 100%) border-box;
  box-shadow: 0 10px 22px -12px rgb(30 22 80 / .8), inset 0 1px 0 rgb(255 255 255 / .1); }
:where(html:not(.a11y-contrast)) .plan-badge:is(.pb-pro, .pb-full) {
  background: radial-gradient(120% 140% at 0% 0%, #3a3346 0%, #17151d 45%, #0c0b10 100%) padding-box,
    linear-gradient(135deg, #fff1b8 0%, #d6a23a 28%, #fff6d2 50%, #b07a1c 74%, #f2d37c 100%) border-box;
  box-shadow: 0 12px 26px -12px rgb(0 0 0 / .75), inset 0 0 0 2px #121016, inset 0 0 0 3px rgb(242 211 124 / .38); } /* a second, fine gold line inside */
:where(html:not(.a11y-contrast)) .plan-badge:is(.pb-plus, .pb-pro, .pb-full) .pb-ico { color: #3a2600; border-radius: 50%;
  background: radial-gradient(circle at 35% 30%, #fff7d1 0%, #f0cf6e 35%, #c18a26 75%, #8f5f12 100%); box-shadow: inset 0 1px 0 rgb(255 255 255 / .7), inset 0 -2px 3px rgb(90 55 0 / .35), 0 2px 6px rgb(0 0 0 / .4); }
:where(html:not(.a11y-contrast)) .plan-badge:is(.pb-plus, .pb-pro, .pb-full) .pb-sub { color: #cfc8ec; }
:where(html:not(.a11y-contrast)) .plan-badge:is(.pb-pro, .pb-full) .pb-sub { color: #cdc3ad; }
:where(html:not(.a11y-contrast)) .plan-badge:is(.pb-plus, .pb-pro, .pb-full) .pb-name { color: #f3dc98;
  background: linear-gradient(180deg, #fff8dc 0%, #f1d27c 55%, #d2a548 100%); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; }
.plan-badge:is(.pb-pro, .pb-full) .pb-name { font-size: .84rem; letter-spacing: .07em; text-transform: uppercase; }
:where(html:not(.a11y-contrast)) .plan-badge:is(.pb-plus, .pb-pro, .pb-full)::after { content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background: linear-gradient(110deg, transparent 30%, rgb(255 240 200 / .2) 48%, transparent 62%); transform: translateX(-130%); transition: transform .9s var(--ease); }
:where(html:not(.a11y-contrast)) .plan-badge:is(.pb-plus, .pb-pro, .pb-full):hover::after { transform: translateX(130%); }
.plan-badge:focus-visible { outline: 3px solid var(--accent); outline-offset: 3px; }
html.a11y-contrast .plan-badge { border: 2px solid var(--ink); border-radius: 12px; background: var(--bg); color: var(--ink); }
html.a11y-contrast .plan-badge :is(.pb-ico, .pb-up) { border: 1px solid var(--ink); }
:where(html:not(.a11y-contrast)) .course-pick select { min-height: 40px; }

/* the menu items */
.tabs { scrollbar-width: none; }
.tabs::-webkit-scrollbar { display: none; }
.tabs a { display: flex; align-items: center; gap: 10px; text-decoration: none; white-space: nowrap; }
:where(html:not(.a11y-contrast)) .tabs a { color: var(--ink-2); font-weight: 500; border: 0; border-radius: 12px; margin: 0; transition: background-color var(--dur-1), color var(--dur-1); }
:where(html:not(.a11y-contrast)) .tabs a::before { color: var(--t); }
:where(html:not(.a11y-contrast)) .tabs a:hover { color: var(--ink); background: color-mix(in srgb, var(--ink) 5%, transparent); }
:where(html:not(.a11y-contrast)) .tabs a.on { color: var(--accent); font-weight: 600; text-shadow: none; background: var(--accent-soft); box-shadow: inset 0 0 0 1px var(--accent-line); }
:where(html:not(.a11y-contrast)) .tabs a.on::before { color: var(--accent); }
:where(html:not(.a11y-contrast)) .tabs a:active { transform: none; }
.tabs a .badge { margin-inline-start: auto; }
/* group labels in the menu (js/ui-shell.js): learn, play, read and watch, Polly and friends */
.nav-h { display: block; padding: 14px 12px 4px; font-size: .72rem; font-weight: 600; letter-spacing: .07em; text-transform: uppercase; color: var(--muted); white-space: nowrap; }
:lang(he) .nav-h { font-size: .8rem; letter-spacing: .01em; }
.nav-h:first-child { padding-top: 2px; }
/* "More" in the menu (js/ui-shell.js): tablets and desktops show the four main sections and fold the rest behind it;
   the page that is open stays visible. Phones have their own "More" in the tab bar, so it is hidden there. */
.nav-more { display: none; }
@media (min-width: 761px) {
  html:not(.in-lesson) .top .tabs .nav-more { display: flex; align-items: center; gap: 10px; margin: 0; border: 0; border-radius: 12px; background: none;
    color: var(--ink-2); font: inherit; font-weight: 500; white-space: nowrap; cursor: pointer; transition: background-color var(--dur-1), color var(--dur-1); }
  html:not(.in-lesson) .top .tabs .nav-more:hover { color: var(--ink); background: color-mix(in srgb, var(--ink) 5%, transparent); }
  html:not(.in-lesson) .top .tabs .nav-more::before { content: ""; flex: none; width: 20px; height: 20px; background: currentColor;
    -webkit-mask: var(--ico-more) center / contain no-repeat; mask: var(--ico-more) center / contain no-repeat; }
  html:not(.in-lesson) .top .tabs .nav-more::after { content: ""; flex: none; width: 8px; height: 8px; margin-inline-start: auto; border: solid currentColor;
    border-width: 0 2px 2px 0; transform: translateY(-2px) rotate(45deg); transition: transform var(--dur-1); }
  html:not(.in-lesson) .top .tabs .nav-more[aria-expanded="true"]::after { transform: translateY(2px) rotate(-135deg); }
  html:not(.in-lesson) .top .tabs:not(.more-open) .nav-extra:not(.on) { display: none; }
}
html.a11y-contrast .top .tabs .nav-more { color: var(--ink); text-decoration: underline; }

/* ---------- desktop: a fixed sidebar with the logo, the menu, and the account at the bottom ---------- */
@media (min-width: 1180px) {
  html:not(.in-lesson) #shell { max-width: none; margin: 0; padding-inline: calc(var(--side-w) + clamp(24px, 3vw, 48px)) clamp(24px, 3vw, 48px); }
  html:not(.in-lesson) :is(#view, .foot) { max-width: var(--content-max); margin-inline: auto; }
  html:not(.in-lesson) .top {
    position: fixed; z-index: 40; inset-block: 0; inset-inline-start: 0; width: var(--side-w); margin: 0; padding: 20px 14px 16px;
    display: grid; grid-template-columns: auto auto minmax(0, 1fr) auto; grid-template-rows: auto auto minmax(0, 1fr) auto auto auto;
    grid-template-areas: "brand brand brand brand" "course course course lang" "nav nav nav nav" "plan plan plan plan" "dict theme gems admin" "acct acct acct acct";
    align-content: start; gap: 12px 8px; overflow: hidden;
    border-bottom: 0; border-inline-end: 1px solid var(--hairline); box-shadow: none;
    background: color-mix(in srgb, var(--surface) 86%, transparent); backdrop-filter: saturate(1.3) blur(18px); -webkit-backdrop-filter: saturate(1.3) blur(18px);
  }
  html:not(.in-lesson) .top-controls { display: contents; }
  html:not(.in-lesson) .top .brand { grid-area: brand; padding: 2px 8px 10px; }
  html:not(.in-lesson) .top .course-pick { grid-area: course; display: flex; min-width: 0; }
  html:not(.in-lesson) .top .course-pick select { width: 100%; }
  html:not(.in-lesson) .top :is(.lang-btn, .ui-switch) { grid-area: lang; }
  html:not(.in-lesson) .top .theme-btn[data-nav="dict"] { grid-area: dict; }
  html:not(.in-lesson) .top .theme-btn[data-act="theme"] { grid-area: theme; }
  html:not(.in-lesson) .top .gem-count { grid-area: gems; justify-self: start; }
  html:not(.in-lesson) .top .admin-tag { grid-area: admin; align-self: center; }
  html:not(.in-lesson) .top .account-btn { grid-area: acct; justify-content: flex-start; width: 100%; max-width: none; min-height: 48px; }
  html:not(.in-lesson) .top .account-btn:not(:has(.avatar)) { justify-content: center; }
  html:not(.in-lesson) .top .plan-badge { grid-area: plan; display: flex; width: auto; min-height: 58px; margin: 2px 4px 0; padding: 8px; padding-inline-end: 12px; gap: 12px; }
  html:not(.in-lesson) .top .plan-badge .pb-ico { width: 40px; height: 40px; border-radius: 12px; }
  html:not(.in-lesson) .top .plan-badge:is(.pb-plus, .pb-pro, .pb-full) .pb-ico { border-radius: 50%; }
  html:not(.in-lesson) .top .plan-badge .pb-ico svg { width: 22px; height: 22px; }
  html:not(.in-lesson) .top .plan-badge.pb-free { min-height: 0; padding: 6px; padding-inline-end: 12px; gap: 10px; }
  html:not(.in-lesson) .top .plan-badge.pb-free .pb-ico { width: 30px; height: 30px; border-radius: 9px; }
  html:not(.in-lesson) .top .plan-badge.pb-free .pb-ico svg { width: 18px; height: 18px; }
  html:not(.in-lesson) .top .tabs {
    grid-area: nav; display: flex; flex-direction: column; flex-wrap: nowrap; align-items: stretch; justify-content: flex-start; gap: 2px;
    align-self: stretch; justify-self: stretch; width: auto; max-width: none; min-height: 0; margin: 4px -4px 0; padding: 10px 4px; overflow-y: auto; overscroll-behavior: contain;
    border: 0; border-top: 1px solid var(--hairline); border-radius: 0; background: none; box-shadow: none;
  }
  html:not(.in-lesson) .top .tabs :is(a, .nav-more) { padding: 10px 12px; font-size: .95rem; }
  html:not(.in-lesson) .top .tabs a::before { width: 22px; height: 22px; }
  /* the accessibility button takes the free end of the sidebar's tool row (search, theme, gems), so it never covers
     the page; for the admin, whose badge sits there, it waits just outside the sidebar */
  html:not(.in-lesson) .a11y-btn { inset-inline-start: calc(var(--side-w) - 55px); bottom: 76px; width: 40px; height: 40px; }
  html:not(.in-lesson) .a11y-btn svg { width: 24px; height: 24px; }
  html:not(.in-lesson) .a11y-menu { inset-inline-start: calc(var(--side-w) + 12px); bottom: 16px; }
  html:not(.in-lesson) body:has(.top .admin-tag) .a11y-btn { inset-inline-start: calc(var(--side-w) + 14px); bottom: 14px; width: 46px; height: 46px; }
  html:not(.in-lesson) body:has(.top .admin-tag) .a11y-menu { bottom: 70px; }
}

/* ---------- tablet: one header row and a single-line strip of sections ---------- */
@media (min-width: 761px) and (max-width: 1179.98px) {
  html:not(.in-lesson) .top {
    position: sticky; top: 0; z-index: 40; display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: center; gap: 10px 16px;
    margin-inline: -16px; padding: 12px 16px 0; padding-top: calc(12px + env(safe-area-inset-top, 0px)); border-bottom: 1px solid var(--hairline); box-shadow: none;
    background: color-mix(in srgb, var(--bg) 84%, transparent); backdrop-filter: saturate(1.3) blur(16px); -webkit-backdrop-filter: saturate(1.3) blur(16px);
  }
  html:not(.in-lesson) .top-controls { justify-content: flex-end; gap: 8px; }
  html:not(.in-lesson) .top .plan-badge { order: 1; }
  html:not(.in-lesson) .top .plan-badge :is(.pb-sub, .pb-up) { display: none; }
  html:not(.in-lesson) .top .account-btn { order: 2; }
  html:not(.in-lesson) .top .tabs {
    grid-column: 1 / -1; justify-self: stretch; min-width: 0; display: flex; flex-wrap: nowrap; justify-content: flex-start; gap: 4px; width: auto; max-width: none;
    margin: 0 -16px; padding: 4px 16px 10px; overflow-x: auto; overscroll-behavior-x: contain; scroll-snap-type: x proximity; scroll-padding-inline: 16px;
    border: 0; border-radius: 0; background: none; box-shadow: none;
    -webkit-mask-image: linear-gradient(90deg, transparent, #000 16px, #000 calc(100% - 16px), transparent); mask-image: linear-gradient(90deg, transparent, #000 16px, #000 calc(100% - 16px), transparent);
  }
  html:not(.in-lesson) .top .tabs :is(a, .nav-more) { flex: none; padding: 8px 12px; font-size: .92rem; scroll-snap-align: start; }
  html:not(.in-lesson) .top .tabs .nav-more::after { margin-inline-start: 2px; }
  html:not(.in-lesson) .top .nav-h { flex: none; align-self: center; width: 1px; height: 22px; margin-inline: 6px; padding: 0; overflow: hidden; color: transparent; background: var(--hairline-strong); }
  html:not(.in-lesson) .top .nav-h:first-child { display: none; }
  html:not(.in-lesson) .top .nav-tool[data-tool="a11y"], html:not(.in-lesson) body.sea-on .top .nav-tool[data-tool="sea"] { display: grid; }
  /* their menus drop down under the header, next to the tool buttons */
  html:not(.in-lesson) .a11y-menu { top: calc(60px + env(safe-area-inset-top, 0px)); bottom: auto; inset-inline: auto 16px; max-height: calc(100vh - 76px); }
  html:not(.in-lesson) .a11y-btn { display: none; }
  html:not(.in-lesson):not(.pw-on) .sea-ctl { top: calc(60px + env(safe-area-inset-top, 0px)); bottom: auto; }
  html:not(.in-lesson):not(.pw-on) .sea-ctl .sea-btn { display: none; }
}
/* Phones and tablets: the accessibility button and Polly's sea switch open from those tool buttons instead of covering
   page buttons, and show only while their own menu is open. In lessons and games the accessibility button stays. */
@media (max-width: 1179.98px) {
  html:not(.in-lesson) .a11y-btn:not([aria-expanded="true"]) { display: none; }
  html:not(.in-lesson):not(.pw-on) .sea-ctl:not(:has(.sea-menu:not([hidden]))) { display: none; }
}

/* ---------- phone: a slim top bar, a tab bar at the bottom, and a full menu behind "More" ---------- */
.dock, .nav-x { display: none; }
@media (max-width: 760px) {
  html:not(.in-lesson) .top {
    position: sticky; top: 0; z-index: 40; display: grid; grid-template-columns: minmax(0, 1fr) auto auto auto auto; grid-template-areas: "brand admin plan gems acct";
    align-items: center; gap: 8px; margin-inline: -16px; padding: 10px 16px; padding-top: calc(10px + env(safe-area-inset-top, 0px));
    border-bottom: 1px solid var(--hairline); box-shadow: none;
    background: color-mix(in srgb, var(--bg) 86%, transparent); backdrop-filter: saturate(1.3) blur(16px); -webkit-backdrop-filter: saturate(1.3) blur(16px);
  }
  html:not(.in-lesson) .top-controls { display: contents; }
  html:not(.in-lesson) .top .brand { grid-area: brand; justify-self: start; min-width: 0; padding-block: 5px; margin-block: -5px; } /* a 44 px tap target, same header height */
  html:not(.in-lesson) .top .brand-mark, html:not(.in-lesson) .top .brand-anim { width: 36px; height: 36px; }
  html:not(.in-lesson) .top .brand-text { font-size: 1.16rem; }
  html:not(.in-lesson) .top .gem-count { grid-area: gems; min-height: 36px; padding: 0 10px; font-size: .86rem; }
  html:not(.in-lesson) .top .admin-tag { grid-area: admin; }
  html:not(.in-lesson) .top .admin-tag span { display: none; }
  html:not(.in-lesson) .top .account-btn { grid-area: acct; min-height: 36px; max-width: 128px; padding-inline: 12px; font-size: .86rem; }
  html:not(.in-lesson) .top .account-btn:has(.avatar) { padding: 0; width: 38px; min-height: 38px; justify-content: center; border-radius: 50%; }
  /* the button stays 36-38 px to fit the header, but takes taps 4 px above and below it: a 44 px target, like the brand */
  html:not(.in-lesson) .top .account-btn { position: relative; }
  html:not(.in-lesson) .top .account-btn::after { content: ""; position: absolute; inset: -4px -2px; border-radius: inherit; }
  html:not(.in-lesson) .top .account-btn .acc-name { display: none; }
  html:not(.in-lesson) .top .plan-badge { grid-area: plan; width: 38px; min-height: 38px; padding: 2px; justify-content: center; }
  html:not(.in-lesson) .top .plan-badge :is(.pb-text, .pb-up) { display: none; }
  html:not(.in-lesson):not(.nav-open) .top .plan-badge.pb-free { display: none; }
  html:not(.in-lesson) .top .plan-badge .pb-ico { width: 30px; height: 30px; }
  html:not(.in-lesson) .top :is(.course-pick, .lang-btn, .ui-switch, .theme-btn, .tabs) { display: none; }
  /* the Spanish-variant button (js/courses/es-picker.js) has no slot of its own: on a phone it is the globe alone, so
     it never pushes over the logo; its name stays in the aria-label and comes back on wider screens */
  html:not(.in-lesson) .top .es-pk-chip { grid-row: 1; width: 38px; min-height: 38px; padding: 0; justify-content: center; border-radius: 50%; }
  html:not(.in-lesson) .top .es-pk-chip > span:last-child { display: none; }

  /* the tab bar */
  html:not(.in-lesson):not(.pw-on) .dock {
    position: fixed; z-index: 45; inset-inline: 0; bottom: 0; display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); /* as many columns as visible tabs */
    height: calc(var(--dock-h) + env(safe-area-inset-bottom)); padding: 6px 6px env(safe-area-inset-bottom);
    background: color-mix(in srgb, var(--surface) 90%, transparent); backdrop-filter: saturate(1.4) blur(18px); -webkit-backdrop-filter: saturate(1.4) blur(18px);
    border-top: 1px solid var(--hairline); box-shadow: 0 -6px 24px -12px rgb(15 20 40 / .18);
  }
  .dock-i {
    position: relative; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px; min-width: 0;
    appearance: none; border: 0; background: none; padding: 4px 2px; border-radius: 14px; cursor: pointer;
    color: var(--muted); font: 500 .7rem/1.1 var(--font-ui); text-decoration: none; -webkit-tap-highlight-color: transparent;
  }
  .dock-i::before { width: 24px; height: 24px; }
  .dock-i span { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .dock-i.on { color: var(--accent); font-weight: 600; }
  .dock-i.on::before { background: var(--accent); }
  .dock-i:active { background: color-mix(in srgb, var(--ink) 6%, transparent); }
  .dock-i .badge { position: absolute; top: 2px; inset-inline-start: calc(50% + 6px); }
  /* six tabs since the Settings tab: a touch smaller words, so "Повторение" and "Настройки" still fit at 390 px; a word
     still too long ("Einstellungen", "Налаштування") breaks onto a second line instead of losing its end */
  .dock:has(.dock-set) .dock-i { font-size: .66rem; letter-spacing: -.01em; }
  .dock:has(.dock-set) .dock-i { padding-block: 2px; }
  .dock:has(.dock-set) .dock-i span { overflow: visible; white-space: normal; overflow-wrap: anywhere; hyphens: auto; text-align: center; line-height: 1.15; }
  @media (max-width: 360px) { html:not(.in-lesson):not(.pw-on) .dock { padding-inline: 2px; } .dock-i { padding-inline: 0; font-size: .64rem; letter-spacing: -.01em; } }
  html:not(.in-lesson):not(.pw-on) body { padding-bottom: calc(var(--dock-h) + env(safe-area-inset-bottom)); }
  html:not(.in-lesson) #toasts { bottom: calc(var(--dock-h) + 12px + env(safe-area-inset-bottom)); }
  html:not(.in-lesson) .a11y-btn { bottom: calc(var(--dock-h) + 12px + env(safe-area-inset-bottom)); width: 42px; height: 42px; }
  html:not(.in-lesson) .a11y-menu { bottom: calc(var(--dock-h) + 64px + env(safe-area-inset-bottom)); }
  /* other things pinned to the bottom of the screen sit above the tab bar too (Polly's sea switch, the language notice) */
  html:not(.in-lesson):not(.pw-on) .sea-ctl { bottom: calc(var(--dock-h) + 12px + env(safe-area-inset-bottom)); }
  html:not(.in-lesson):not(.pw-on) .lang-soon { bottom: calc(var(--dock-h) + 10px + env(safe-area-inset-bottom)); }

  /* "More" opens the whole header as a menu sheet: course, language, theme, search and every section */
  html.nav-open:not(.in-lesson) { overflow: hidden; }
  html.nav-open:not(.in-lesson) .top {
    position: fixed; inset: 0 0 calc(var(--dock-h) + env(safe-area-inset-bottom)); z-index: 44; margin: 0; overflow-y: auto; overscroll-behavior: contain;
    grid-template-columns: auto auto auto auto minmax(0, 1fr) auto; grid-template-rows: auto;
    grid-template-areas: "brand brand brand brand brand close" "nav nav nav nav nav nav" "course course course course course lang" "dict theme a11y sea gems admin" "plan plan plan plan plan plan" "acct acct acct acct acct acct";
    align-content: start; gap: 12px 8px; padding: 14px 16px 24px; padding-top: calc(14px + env(safe-area-inset-top, 0px));
    background: var(--bg); backdrop-filter: none; -webkit-backdrop-filter: none; animation: ds-sheet-in var(--dur-3) var(--ease);
  }
  html.nav-open:not(.in-lesson) .top :is(.course-pick, .lang-btn, .ui-switch, .theme-btn) { display: flex; }
  html.nav-open:not(.in-lesson) .top .course-pick { grid-area: course; min-width: 0; }
  html.nav-open:not(.in-lesson) .top .course-pick select { width: 100%; }
  html.nav-open:not(.in-lesson) .top :is(.lang-btn, .ui-switch) { grid-area: lang; }
  html.nav-open:not(.in-lesson) .top .theme-btn { display: grid; }
  html.nav-open:not(.in-lesson) .top .theme-btn[data-nav="dict"] { grid-area: dict; }
  html.nav-open:not(.in-lesson) .top .theme-btn[data-act="theme"] { grid-area: theme; }
  html.nav-open:not(.in-lesson) .top .nav-tool[data-tool="a11y"], html.nav-open:not(.in-lesson) body.sea-on .top .nav-tool[data-tool="sea"] { display: grid; }
  html.nav-open:not(.in-lesson) .top .nav-tool[data-tool="a11y"] { grid-area: a11y; }
  html.nav-open:not(.in-lesson) .top .nav-tool[data-tool="sea"] { grid-area: sea; }
  html.nav-open:not(.in-lesson) .top .gem-count { justify-self: start; min-height: 40px; }
  html.nav-open:not(.in-lesson) .top .account-btn, html.nav-open:not(.in-lesson) .top .account-btn:has(.avatar) { width: 100%; max-width: none; min-height: 48px; border-radius: 14px; justify-content: flex-start; padding: 0 12px; }
  html.nav-open:not(.in-lesson) .top .account-btn:not(:has(.avatar)) { justify-content: center; }
  html.nav-open:not(.in-lesson) .top .account-btn .acc-name { display: inline; max-width: none; }
  html.nav-open:not(.in-lesson) .top .plan-badge { width: auto; min-height: 56px; margin-inline: 3px; padding: 8px; padding-inline-end: 12px; justify-content: flex-start; }
  html.nav-open:not(.in-lesson) .top .plan-badge .pb-text { display: grid; }
  html.nav-open:not(.in-lesson) .top .plan-badge .pb-up { display: inline-block; }
  html.nav-open:not(.in-lesson) .top .plan-badge .pb-ico { width: 38px; height: 38px; }
  html.nav-open:not(.in-lesson) .nav-x { grid-area: close; display: grid; place-items: center; width: 40px; height: 40px; border-radius: 12px; border: 1px solid var(--hairline-strong); background: var(--surface); color: var(--ink); cursor: pointer; }
  html.nav-open:not(.in-lesson) .top .tabs {
    grid-area: nav; align-self: start; justify-self: stretch; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; width: auto; max-width: none;
    margin: 4px 0 6px; padding: 0 0 18px; overflow: visible; border: 0; border-bottom: 1px solid var(--hairline); border-radius: 0; background: none; box-shadow: none;
  }
  html.nav-open:not(.in-lesson) .top .tabs a { min-height: 56px; padding: 10px 12px; font-size: .95rem; white-space: normal; line-height: 1.2;
    background: var(--surface); border: 1px solid var(--hairline); box-shadow: var(--e1); border-radius: 14px; }
  html.nav-open:not(.in-lesson) .top .tabs a::before { width: 24px; height: 24px; }
  html.nav-open:not(.in-lesson) .top .nav-h { grid-column: 1 / -1; padding: 8px 2px 0; }
  html.nav-open:not(.in-lesson) .top .nav-h:first-child { padding-top: 0; }
  html.nav-open:not(.in-lesson) .top .tabs a.on { background: var(--accent-soft); border-color: var(--accent-line); }
  html.nav-open:not(.in-lesson) .dock { background: var(--surface); }
  html.nav-open:not(.in-lesson) .dock-more { color: var(--accent); }
  html.nav-open:not(.in-lesson) .dock-more::before { --ico: var(--ico-close); background: var(--accent); }
  html.nav-open:not(.in-lesson) :is(.a11y-btn, #toasts) { visibility: hidden; }
  /* high contrast frames every button; the tab bar's "More" is a tab like the others */
  html.a11y-contrast .dock-i { border: 0 !important; }
  html.a11y-contrast .dock-i.on { box-shadow: inset 0 3px 0 var(--ink); border-radius: 0; }
}
@keyframes ds-sheet-in { from { opacity: 0; transform: translateY(10px); } }
/* Quick settings, the tab bar's "Settings" (js/ui-shell.js, docs/plan/2026-10-29-phone-settings-sheet.md): a sheet that
   rises from the tab bar over a shade, with the interface language, the course, text size and the comfort switches */
.qs-wrap { position: fixed; z-index: 44; inset: 0 0 calc(var(--dock-h) + env(safe-area-inset-bottom)); display: flex; flex-direction: column; justify-content: flex-end; }
.qs-wrap[hidden] { display: none; }
html.qs-open { overflow: hidden; }
.qs-shade { position: absolute; inset: 0; background: rgb(4 8 20 / .55); }
.qs {
  position: relative; display: grid; gap: var(--sp-4); max-height: calc(100% - 40px); overflow-y: auto; overscroll-behavior: contain;
  padding: var(--sp-4) var(--sp-4) var(--sp-5); border: 1px solid var(--hairline); border-bottom: 0; border-radius: 24px 24px 0 0;
  background: var(--bg); color: var(--ink); box-shadow: 0 -18px 40px -18px rgb(0 0 0 / .55);
}
.qs:focus { outline: none; }
@media (prefers-reduced-motion: no-preference) { html:not(.pref-calm) .qs { animation: ds-sheet-in var(--dur-3) var(--ease); } }
.qs-head { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); }
.qs-head h2 { margin: 0; font-size: 1.15rem; }
.qs-x { flex: none; display: grid; place-items: center; width: 44px; height: 44px; padding: 0; border-radius: 12px; border: 1px solid var(--hairline-strong); background: var(--surface); color: var(--ink); cursor: pointer; }
.qs-x::before { content: ""; width: 20px; height: 20px; background: currentColor; -webkit-mask: var(--ico-close) center / contain no-repeat; mask: var(--ico-close) center / contain no-repeat; }
.qs-sec h3 { margin: 0 0 var(--sp-2); font-size: .85rem; font-weight: 600; color: var(--muted); }
.qs-lang { display: flex; align-items: center; gap: 10px; width: 100%; min-height: 48px; padding: 0 14px; border-radius: 14px; border: 1px solid var(--hairline-strong);
  background: var(--surface); color: var(--ink); font: 600 1rem var(--font-ui); text-align: start; cursor: pointer; }
.qs-lang::after { content: "›"; margin-inline-start: auto; font-size: 1.3rem; line-height: 1; color: var(--muted); }
[dir="rtl"] .qs-lang::after { content: "‹"; }
.qs-chips { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.qs-chips .chip { min-height: 44px; }
.qs .qs-size { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); }
.qs .qs-size button { min-height: 44px; padding-inline: .4em; }
.qs-sw { display: grid; gap: var(--sp-3); }
.qs-sw .switch-row { min-height: 44px; }
/* the profile page's switch look (css/ds/plans.css): an off track that still shows on the dark sheet, a white knob */
:where(html:not(.a11y-contrast)) .qs .switch { background: color-mix(in srgb, var(--ink) 18%, var(--surface)); }
:where(html:not(.a11y-contrast)) .qs .switch-row input:checked + .switch { background: var(--accent); }
:where(html:not(.a11y-contrast)) .qs .switch::after { background: #fff; box-shadow: 0 1px 3px rgb(15 20 40 / .3); }
.qs-foot { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.qs-foot .btn { flex: 1 1 9rem; min-height: 44px; justify-content: center; }
html.qs-open .dock-set { color: var(--accent); }
html.qs-open .dock-set::before { background: var(--accent); }
html.qs-open :is(.a11y-btn, #toasts) { visibility: hidden; }
/* the top bar casts a soft shadow once the page scrolls under it (browsers without scroll timelines keep the line) */
@supports (animation-timeline: scroll()) {
  @media (max-width: 1179.98px) {
    html:not(.in-lesson):not(.nav-open) .top { animation: ds-bar-lift linear both; animation-timeline: scroll(root block); animation-range: 0 72px; }
  }
}
@keyframes ds-bar-lift { to { box-shadow: 0 10px 28px -18px rgb(15 20 40 / .45); } }

/* Polly's sea switch (js/pet/seascape.js) sits on the side away from the accessibility button and the sidebar */
html[dir="ltr"] .sea-ctl { left: auto; right: calc(14px + env(safe-area-inset-right, 0px)); }

/* ---------- footer ---------- */
:where(html:not(.a11y-contrast)) .foot { margin-top: var(--sp-12); padding-block: var(--sp-6) var(--sp-4); border-top: 1px solid var(--hairline); gap: var(--sp-4); font-size: var(--fs-sm); color: var(--muted); }
.foot-links { gap: 8px 22px; }
:where(html:not(.a11y-contrast)) .foot-links a { color: var(--muted); font-weight: 500; text-decoration: none; }
:where(html:not(.a11y-contrast)) .foot-links a:hover { color: var(--ink); text-decoration: underline; text-underline-offset: 3px; }
.foot summary { width: max-content; color: var(--muted); }
/* on touch screens each footer link and the Settings summary is a full 44 px finger target. The rows can't sit closer
   than 44 px (overlapping hit areas would leave each link only the row's height), so the air the targets add around the
   list is taken back with negative margins instead; the -3px end margin keeps the last row's area clear of Settings */
@media (pointer: coarse) {
  .foot-links { row-gap: 0; margin-block: -11px -3px; }
  .foot-links > :is(a, .link-btn) { display: inline-flex; align-items: center; min-height: 44px; }
  .foot summary { padding-block: 11px; margin-block-start: -11px; } /* stays a list item, so its open/closed triangle keeps showing */
}
.foot-note { color: var(--muted); font-size: var(--fs-xs); }

/* =====================================================================
   5. Lesson focus mode: lessons, tests and practice hide the menu; the lesson's own bar stays on top
   ===================================================================== */
html.in-lesson .top, html.in-lesson .dock { display: none; }
/* The footer shrinks to its withdrawal link: undoing a purchase stays one tap away on every page, lessons included
   (EU withdrawal button, docs/plan/2026-10-05-withdraw-everywhere.md). */
html.in-lesson .foot > :not(.foot-links), html.in-lesson .foot-links > :not([data-nav="withdraw"]) { display: none; }
html.in-lesson .foot { margin-top: var(--sp-6); padding-block: var(--sp-3); font-size: var(--fs-xs); }
/* its small text made a 19 px tap target; the padding gives a full-size one and the negative margin keeps the footer's height */
html.in-lesson .foot-links a[data-nav="withdraw"] { display: inline-block; padding-block: 12px; margin-block: -12px; }
html.in-lesson #shell { max-width: 820px; }
html.in-lesson #view { padding-top: 0; }
/* Polly (js/pet/buddy.js) sits just under the lesson bar on phones and tablets, not behind it */
@media (max-width: 1179.98px) { html.in-lesson .buddy-host > .buddy:not(.float):not(.inrow) { top: 74px; } }
:where(html:not(.a11y-contrast)).in-lesson .lesson-top {
  position: sticky; top: 0; z-index: 30; margin-inline: -16px; padding: 14px 16px 12px; padding-top: calc(14px + env(safe-area-inset-top, 0px));
  background: color-mix(in srgb, var(--bg) 88%, transparent); backdrop-filter: saturate(1.3) blur(14px); -webkit-backdrop-filter: saturate(1.3) blur(14px);
}

/* =====================================================================
   6. Preferences: easy learning, calm, reduced motion
   ===================================================================== */
html.pref-easy {
  --grad: linear-gradient(135deg, #5c7fd6, #6f86d8);
  --accent-2: #6f86d8;
}
html.pref-easy :is(.tabs a, .dock-i)::before { opacity: .85; }
html.pref-easy.scene-media :is(#view, .foot) { background: color-mix(in srgb, var(--bg) 94%, transparent) !important; }
html.pref-easy *, html.pref-easy *::before, html.pref-easy *::after { animation-duration: .001s !important; animation-iteration-count: 1 !important; transition-duration: .001s !important; }
html.pref-easy :is(.medal-pop, .toast) { animation: none !important; }
/* in a lesson: a clear step counter and nothing else moving */
html.pref-easy .lesson .ex-count { font-size: 1rem; font-weight: 700; padding: .2em .6em; border-radius: 999px; background: var(--surface-2); color: var(--ink); }
html.pref-easy .lesson .ex-head .q { line-height: 1.45; }
.q-say { appearance: none; border: 0; cursor: pointer; margin-inline-start: .4em; width: 34px; height: 34px; border-radius: 50%; vertical-align: middle;
  background: var(--surface-2); color: var(--ink); font-size: 1rem; box-shadow: inset 0 0 0 1px var(--hairline-strong); }
.q-say:active { transform: translateY(1px); }
/* the easy-learning switch in the personal area stands out */
.easy-card { border: 1px solid var(--accent-line); background: var(--accent-soft); border-radius: var(--r-lg); padding: 14px 16px; display: grid; gap: 6px; }
.easy-card .muted { margin: 0; }
html.pref-easy.in-lesson .buddy { display: none !important; }
/* easy learning: the answer card with "next" always sits in the same place, at the bottom of the screen */
html.pref-easy .lesson .feedback:not(:empty) { position: sticky; bottom: max(10px, env(safe-area-inset-bottom)); z-index: 6;
  box-shadow: 0 -6px 24px -10px rgb(16 21 42 / .35), 0 10px 24px -12px rgb(16 21 42 / .35); }
@media (max-width: 560px) { html.pref-easy .lesson .feedback:not(:empty) { bottom: 68px; } } /* above the accessibility button */

@media (prefers-reduced-motion: reduce) {
  :where(html:not(.a11y-contrast)) :is(.btn, .opt, .tok, .tabs a, .dock-i, .ds-tile, .ds-card) { transition: none; }
  .ds-tile:hover { transform: none; }
  html.nav-open .top { animation: none !important; }
}
html.pref-calm .ds-tile:hover { transform: none; }
/* Light mode (B43): calm mode plus no blur behind glass panels, the costliest effect on a weak phone's graphics chip. */
html.pref-lite *, html.pref-lite *::before, html.pref-lite *::after { -webkit-backdrop-filter: none !important; backdrop-filter: none !important; }
html.pref-calm.nav-open .top { animation: none !important; }

/* ---------- Loading placeholders (docs/plan/2026-10-28-skeletons.md) ----------
   Soft shapes stand where content is on its way, so a screen keeps its layout and reads as almost there, not empty.
   The waiting box carries aria-busy="true" and one hidden line (class "sr") saying what is loading; the shapes are
   aria-hidden. A faint light sweeps across in reading direction six times (about ten seconds), then they rest:
   a slow network does not keep a phone animating. Only `translate` moves, so the compositor does the work. */
/* solid, not see-through: the shapes sit on photo and starry page backgrounds too */
:is(.ds-skel, .lb-skel) { --skel: color-mix(in srgb, var(--ink) 9%, var(--surface)); --skel-hi: rgb(255 255 255 / .45); }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) :is(.ds-skel, .lb-skel) { --skel: color-mix(in srgb, var(--ink) 10%, var(--surface)); --skel-hi: rgb(255 255 255 / .06); } }
:root[data-theme="dark"] :is(.ds-skel, .lb-skel) { --skel: color-mix(in srgb, var(--ink) 10%, var(--surface)); --skel-hi: rgb(255 255 255 / .06); }
.ds-skel { position: relative; display: block; overflow: hidden; border-radius: var(--r-sm); background: var(--skel); }
.ds-skel-line { width: var(--w, 100%); height: .8em; border-radius: var(--r-pill); }
.ds-skel-title { width: var(--w, 70%); height: 1.35em; border-radius: var(--r-xs); }
.ds-skel-pill { width: var(--w, 9rem); height: 44px; border-radius: var(--r-pill); }
.ds-skel-round { flex: none; width: var(--s, 44px); height: var(--s, 44px); border-radius: 50%; }
.ds-skel-box { height: var(--h, 120px); border-radius: var(--r); }
.ds-skel-stack { display: grid; gap: var(--sp-2); min-width: 0; }
html.a11y-contrast .ds-skel { background: none; border: 1px dashed currentColor; opacity: .55; }
:where(html:not(.a11y-contrast)) :is(.ds-skel, .lb-skel)::after { content: ''; position: absolute; inset: 0; translate: -100% 0;
  background: linear-gradient(90deg, transparent, var(--skel-hi), transparent); pointer-events: none; }
@keyframes ds-sweep { to { translate: 100% 0; } }
@media (prefers-reduced-motion: no-preference) {
  html:not(.pref-calm) :is(.ds-skel, .lb-skel)::after { animation: ds-sweep 1.6s ease-in-out .2s 6 both; }
  /* right to left the light starts on the right, where reading starts */
  html[dir="rtl"]:not(.pref-calm) :is(.ds-skel, .lb-skel)::after { animation-direction: reverse; }
}

/* Card shown once after a payment (js/app.js showPaidCard): plan, price, next charge, how to cancel. */
.paid-card { position: fixed; inset-inline: var(--sp-4); bottom: var(--sp-4); max-width: 26rem; margin-inline: auto; z-index: 60; background: var(--card, #fff); box-shadow: var(--shadow); }
.paid-card h2 { margin: 0; }
.paid-card p { margin: 0; }

/* ---------- Step path: one clear order, done / now / later (docs/plan/2026-11-04-step-path.md) ----------
   <ol class="ds-steps"><li class="ds-step is-done|is-now|is-later|is-locked"><a|button class="ds-step-btn">
     <span class="ds-step-dot">✓ or a number</span><span class="ds-step-title">…</span><span class="ds-step-meta">…</span>
     optional <span class="ds-step-tag">Next</span></…></li></ol>
   Done: a filled check. Now: a filled number with a halo, the card in the colour (the one step to take next). Later: a dashed
   ring. The colour is --lv when the page sets a level, else the accent. On one column a rail joins the dots: solid through
   the done steps, dashed after. The course map's lesson list (css/ds/home.css, .station) wears the same marks. */
.ds-steps { --step-c: var(--lv, var(--accent)); container-type: inline-size; display: grid; gap: var(--sp-2); margin: 0; padding: 0; list-style: none; }
.ds-step { position: relative; }
.ds-step-btn { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; grid-template-areas: "dot title tag" "dot meta tag"; align-items: center; gap: 2px var(--sp-3);
  width: 100%; min-height: 64px; padding: var(--sp-3); border: 1px solid var(--hairline); border-radius: var(--r); background: var(--surface);
  color: var(--ink); font: inherit; text-align: start; text-decoration: none; cursor: pointer; transition: border-color var(--dur-1), background-color var(--dur-1); }
.ds-step-btn:hover { border-color: color-mix(in srgb, var(--step-c) 40%, var(--hairline)); }
.ds-step-btn:focus-visible { outline: none; box-shadow: 0 0 0 3px color-mix(in srgb, var(--step-c) 35%, transparent); }
.ds-step-dot { grid-area: dot; display: grid; place-items: center; box-sizing: border-box; width: 36px; height: 36px; border-radius: 50%;
  background: color-mix(in srgb, var(--step-c) 12%, var(--surface)); color: var(--ink-2); font: 700 .9rem/1 var(--font-target); }
.ds-step-dot svg { width: 17px; height: 17px; fill: none; stroke: currentColor; stroke-width: 2.6; stroke-linecap: round; stroke-linejoin: round; }
.ds-step-title { grid-area: title; min-width: 0; font-size: .95rem; font-weight: 500; line-height: 1.3; overflow-wrap: anywhere; }
.ds-step-meta { grid-area: meta; min-width: 0; color: var(--muted); font-size: var(--fs-xs); line-height: 1.3; }
.ds-step-tag { grid-area: tag; padding: .2em .7em; border-radius: var(--r-pill); background: color-mix(in srgb, var(--step-c) var(--chip-dim, 100%), #000); color: var(--bg); font-size: var(--fs-xs); font-weight: 700; }
.ds-step.is-done .ds-step-dot { background: color-mix(in srgb, var(--step-c) var(--chip-dim, 100%), #000); color: var(--bg); }
.ds-step.is-done .ds-step-btn { border-color: color-mix(in srgb, var(--step-c) 22%, var(--hairline)); background: color-mix(in srgb, var(--step-c) 5%, var(--surface)); }
.ds-step.is-done .ds-step-title { color: var(--ink-2); }
.ds-step.is-now .ds-step-btn { border-color: color-mix(in srgb, var(--step-c) 65%, transparent); background: color-mix(in srgb, var(--step-c) 7%, var(--surface));
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--step-c) 14%, transparent); }
.ds-step.is-now .ds-step-dot { background: color-mix(in srgb, var(--step-c) var(--chip-dim, 100%), #000); color: var(--bg);
  box-shadow: 0 0 0 4px var(--surface), 0 0 0 6px color-mix(in srgb, var(--step-c) 55%, transparent); }
.ds-step.is-now .ds-step-title { font-weight: 600; }
.ds-step.is-later .ds-step-dot { border: 2px dashed color-mix(in srgb, var(--step-c) 45%, var(--hairline)); background: transparent; }
.ds-step.is-locked .ds-step-dot { background: var(--surface-2); color: var(--muted); box-shadow: inset 0 0 0 1px var(--hairline); }
.ds-step.is-locked .ds-step-title { color: var(--muted); }
/* a locked step that is also the next one: the card and halo of "now", the dot keeps its lock */
.ds-step.is-now.is-locked .ds-step-dot { background: var(--surface-2); color: var(--ink-2); box-shadow: inset 0 0 0 1px var(--hairline), 0 0 0 4px var(--surface), 0 0 0 6px color-mix(in srgb, var(--step-c) 55%, transparent); }
.ds-step.is-now.is-locked .ds-step-title { color: var(--ink); }
@container (max-width: 567.98px) {
  .ds-step:not(:last-child)::after { content: ""; position: absolute; z-index: 1; inset-inline-start: calc(var(--sp-3) + 17px); top: calc(50% + 25px);
    width: 2px; height: calc(100% - 50px + var(--sp-2)); border-radius: 2px; pointer-events: none;
    background: repeating-linear-gradient(to bottom, color-mix(in srgb, var(--step-c) 40%, transparent) 0 4px, transparent 4px 8px); }
  .ds-step.is-done:not(:last-child)::after { background: color-mix(in srgb, var(--step-c) 70%, transparent); }
}
html.a11y-contrast .ds-step.is-now .ds-step-btn { outline: 3px solid currentColor; outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) { .ds-step-btn { transition: none; } }

/* =====================================================================
   7. Phones: every control is at least 44px under a finger (wave 5, T054)
   Small text buttons keep their look and get a larger invisible hit area; quiet text links in lessons grow to 44px.
   ===================================================================== */
@media (pointer: coarse) {
  .link-btn { position: relative; }
  .link-btn::after { content: ''; position: absolute; top: 50%; left: 50%; width: max(100%, 44px); height: max(100%, 44px); transform: translate(-50%, -50%); }
  .lesson .linkish { display: inline-flex; align-items: center; min-height: 44px; padding-block: 0; }
  .btn { min-width: 44px; }
  .chip { min-height: 44px; }
  /* the topic chips on the home page sit 8px apart: 3px more above and below reaches 44px without touching a neighbour */
  .chip.x-chip { position: relative; }
  .chip.x-chip::after { content: ''; position: absolute; inset: -3px 0; }
}
/* 7b. The header and menu keys are 44px on every screen, not only under a finger: the menu's icon keys (dictionary,
   light/dark, accessibility, close) and the course list were 40px (QA click audit, 4 Nov 2026, item 13). The phone
   header's "Sign in" keeps its 36px look so the bar stays 57px, and takes taps 44px high (its ::after above). */
.account-btn { min-height: 44px; }
:is(.theme-btn, .lang-btn, .nav-tool, .nav-x) { min-width: 44px; min-height: 44px; }
.top select { min-height: 44px; }
.fam-kids-btn { min-height: 44px; }

/* =====================================================================
   8. Contrast (wave 5, T055): small text keeps 4.5:1 in light and dark mode
   ===================================================================== */
/* Polly's streak count is small bold text: the warm orange stays, deep enough for white digits (5.2:1, was 2.6:1) */
:where(html:not(.a11y-contrast)) .buddy-streak { background: #c2410c; }
/* The warm closing line on finish screens (W07-N11): quiet, a person talking, not a result */
.warm-close { max-width: 36ch; margin: 6px auto 0; color: var(--muted); font-size: .98rem; line-height: 1.5; text-wrap: balance; }
.st-result .warm-close { margin-inline: 0; }

/* ---------- Weak phones (html.weak-phone, set by js/scene.js: 2 GB, or a phone with 4 GB or 4 cores or fewer) ----------
   Endless decorations keep the compositor busy for as long as the page is open (QA, 4x slower CPU: the idle home ran at
   ~27 fps). On these phones they stand still: shooting stars, the logo's orbit and spark, the stage's halo, orb, ring,
   ship and rainbow edge, and the "Start here" nudge. One-off entrances still play; !important because the loops come
   from theme rules with long selectors (cosmos.css, nova.css). */
html.weak-phone .scene-box::before, html.weak-phone .scene-box::after,
html.weak-phone :is(.nv-mark .o, .nv-mark .s, .nv-halo, .nv-halo2, .nv-orb, .nv-ring2, .nv-fly, .bx-tip),
html.weak-phone :is(.nv-next, .nv-tp.is-now .nv-hx)::before, html.weak-phone .nv-next::after { animation: none !important; }

/* =====================================================================
   9. Quiet motion (wave 8, docs/plan/2026-10-05-visual-polish.md)
   Cards rise into place as they scroll in, the main button and the streak greet once, and a new page fades in
   (js/ui-shell.js). Only opacity, translate and scale move, so the compositor does the work and no script runs per
   frame. Off with prefers-reduced-motion, the calm preference, during lessons, and in browsers without scroll timelines.
   ===================================================================== */
@keyframes ds-rise { from { opacity: 0; translate: 0 18px; scale: .985; } }
@keyframes ds-rise-in { from { opacity: 0; translate: 0 10px; } }
@keyframes ds-breathe { 50% { scale: 1.035; } }
@keyframes ds-nudge { 50% { translate: 4px 0; } }
@keyframes ds-nudge-rtl { 50% { translate: -4px 0; } }
@keyframes ds-flame { 30% { scale: 1.18; rotate: -6deg; } 60% { scale: .96; rotate: 4deg; } }
@keyframes ds-dot-pop { 40% { scale: 1.3; } }
@media (prefers-reduced-motion: no-preference) {
  /* `translate` and `scale` are their own properties here, so a card's own hover `transform` keeps working */
  @supports (animation-timeline: view()) {
    html:not(.pref-calm):not(.in-lesson) :is(.bx-card, .bx-mini, .bx-teacher, .bx-banner, .bx-cta, .home .level, .home .way,
      .game-card, .st-card, .dl-card, .gr-card, .vid-card, .lesson.finish > :is(section, .ds-card, .pet-cheer, .fw-finish)) {
      animation: ds-rise linear both; animation-timeline: view(); animation-range: entry 0px entry 160px;
    }
  }
  /* the finish card opens in a short cascade under Polly's pop */
  html:not(.pref-calm) .lesson.finish > :is(.lv-chip.big, h1, p.muted, .stat-grid, .ls-goal-end) { animation: ds-rise-in .5s var(--ease, ease-out) both; }
  html:not(.pref-calm) .lesson.finish > .lv-chip.big { animation-delay: .15s; }
  html:not(.pref-calm) .lesson.finish > h1 { animation-delay: .22s; }
  html:not(.pref-calm) .lesson.finish > p.muted { animation-delay: .3s; }
  html:not(.pref-calm) .lesson.finish > :is(.stat-grid, .ls-goal-end) { animation-delay: .38s; }
  /* the main button breathes twice after the page settles; the arrow on "Continue" nudges forward on hover */
  html:not(.pref-calm) .btn.bx-go { animation: ds-breathe 1.6s ease-in-out 1.2s 2; }
  html:not(.pref-calm) .home .next-card .btn.primary:hover::after { animation: ds-nudge .7s ease-in-out infinite; }
  html[dir="rtl"]:not(.pref-calm) .home .next-card .btn.primary:hover::after { animation-name: ds-nudge-rtl; }
  /* the streak flame flickers once, and today's dot pops once when today is done */
  html:not(.pref-calm) .home .tc-streak .tc-ico { animation: ds-flame .9s ease-in-out .5s 1; }
  html:not(.pref-calm) .home .ss-day.today.done .ss-dot { animation: ds-dot-pop .6s cubic-bezier(.2, 1.4, .4, 1) .8s 1; }
}

/* ---------- Arabic: the interface and Arabic words in any interface (docs/plan/2026-10-28-arabic-rtl.md) ----------
   Arabic letters join, so letter spacing of any kind (headings' negative tracking, small labels' wide tracking,
   pref-spacing) pulls a word apart into loose letters. It is off wherever the text is Arabic: the whole page in the
   Arabic interface, and words marked lang="ar" in the others. English words on an Arabic page carry their own lang
   and keep their tracking. !important because the tracking comes from dozens of component rules. */
:lang(ar) { letter-spacing: normal !important; }
/* one-line labels that clip their overflow cut Arabic tails at Latin line heights: the tab bar has room for a taller
   line; the plan badge widens only its clip box (padding paid back by margin), so the top bar keeps its height */
html:lang(ar) .dock-i span { line-height: 1.45; }
html:lang(ar) .plan-badge .pb-name { padding-block: .2em; margin-block: -.2em; }

/* ---------- One "listen" key (design backlog D21) ----------
   The round speaker button (.speak) looks the same on every page: 44 px, the accent-tinted face the lessons use,
   the same 20 px speaker icon, and a filled face while its clip plays (.is-playing, set by js/audio-base.js on the
   key that started the clip). Bigger keys in a lesson prompt or a flash card keep their size (css/ds/lesson.css). */
.speak { width: 44px; height: 44px; }
.speak svg { width: 20px; height: 20px; }
:where(html:not(.a11y-contrast)) .speak { color: var(--accent); background: var(--accent-soft); border: 1px solid var(--accent-line); box-shadow: none;
  transition: background-color var(--dur-1), border-color var(--dur-1), color var(--dur-1); }
:where(html:not(.a11y-contrast)) .speak:hover { transform: none; background: color-mix(in srgb, var(--accent) 16%, var(--surface)); border-color: color-mix(in srgb, var(--accent) 45%, transparent); }
:where(html:not(.a11y-contrast)) .speak:focus-visible { outline: none; box-shadow: var(--ring); }
:where(html:not(.a11y-contrast)) .speak.is-playing { color: var(--accent-ink); background: var(--accent); border-color: var(--accent); }
html.a11y-contrast .speak.is-playing { outline: 3px solid currentColor; outline-offset: 2px; }
/* the pulse is a ring that grows and fades (only scale and opacity move, so the compositor does the work) */
@media (prefers-reduced-motion: no-preference) {
  html:not(.pref-calm):not(.a11y-contrast) .speak.is-playing { position: relative; }
  html:not(.pref-calm):not(.a11y-contrast) .speak.is-playing::after { content: ''; position: absolute; inset: -1px; border-radius: 50%; border: 2px solid var(--accent); pointer-events: none; }
  html:not(.pref-calm):not(.a11y-contrast) .speak.is-playing::after { animation: ds-listen 1.2s ease-out infinite; }
}
@keyframes ds-listen { from { opacity: .7; scale: 1; } to { opacity: 0; scale: 1.4; } }
