/* ============================================================
   LearnWords AI — Design Tokens & Theme Engine (v3 · "Ink & Paper")
   ------------------------------------------------------------
   Single source of truth for color/typography across every host
   (Telegram Mini App, Reading.Web, future MAUI WebView).

   v3 moves OFF the generic "vibecoder" look (Inter + sapphire +
   floating rounded cards + soft shadows) toward an editorial /
   literary identity: Schibsted Grotesk (UI) + Newsreader / Literata
   (display + reading serif), a single TERRACOTTA accent, crisp small
   radii, flat hairline-ruled surfaces, and the "orbit" memory motif.

   FOUR explicit palettes + auto, selected via the `data-theme`
   attribute on <html> (set by theme.js / ThemeService). The internal
   keys are kept from v2 for backward-compatible persistence; only the
   palettes + display labels changed:

     (no attribute)        → "auto": follows Telegram --tg-theme-*
                             inside Telegram, else OS prefers-color-scheme
                             (falls back to NIGHT off-Telegram)
     data-theme="dark"     → NIGHT  (observatory cosmos, lamplight gold)
     data-theme="cool"     → COOL   (clean editorial light, terracotta)
     data-theme="sepia"    → PAPER  (warm book stock, spice-rust accent)
     data-theme="amoled"   → AMOLED (true-black OLED — NIGHT ink on pure #000)

   KEY PRIMITIVE — `--on-surface-rgb`
     The RGB triplet of the foreground "ink" for the current theme.
     Inline page styles compose neutral fills/borders as
        rgba(var(--on-surface-rgb), <alpha>)
     so one token flips every translucent overlay per theme. Same idea
     for --accent-rgb.

   Type stacks: --font (UI sans), --font-display (brand/heading serif),
   --font-reading (book body serif, user-overridable), --font-mono.
   ============================================================ */

/* ---------- Self-hosted type (was: one @import from fonts.googleapis.com) ----------
   The seven families below are vendored as woff2 under ../fonts/<family>/ instead of
   being pulled from Google at runtime (#1209). All seven are OFL; each folder carries
   its own OFL.txt, which the licence requires to travel with the binaries.

   WHY — explicitly NOT performance. Measured on the Android boot trace (#1208) the
   Google sheet cost ~2 ms to parse and the splash window was 1534 ms with wifi off vs
   1544 ms with wifi on. The reasons are:
     - Offline. The reader ships downloadable packs and an explicit offline mode. On a
       cold start with no network the faces never resolved, so downloaded books rendered
       in whatever the platform substituted — and reading width, line height and the
       reader's own font-size control are all tuned against these faces.
     - Privacy. Every launch of the native app announced itself to a third party with IP
       + User-Agent, which the store listings and the privacy policy do not disclose.
     - Determinism. A remote stylesheet can change under us, and a face that silently
       stops resolving reads as a layout regression nobody would attribute to Google.

   SUBSETS — latin, latin-ext, cyrillic, cyrillic-ext. That covers every ship locale:
   latin for en, latin-ext for the de/es/fr/it/pt diacritics, cyrillic(+ext) for ru.
   zh needs a CJK face none of these carry and stays on the system font, exactly as it
   did behind the @import. Google's greek / vietnamese / hebrew / devanagari cuts are
   dropped. The unicode-range of each kept cut is preserved verbatim, so a Latin-only
   reader on the web hosts still downloads only the Latin files.

   WEIGHT RANGES (font-weight: 400 700) rather than one rule per weight: these are
   variable fonts and Google served the same file for every weight anyway.

   PATHS are RELATIVE to this file (served at _content/LearnWordsAI.SharedUI/css/tokens.css)
   so they resolve under the RCL static-asset base on every host — an absolute /fonts/
   would 404 outside the RCL. Same rule as OpenDyslexic further down.

   TO REFRESH from upstream: python tools/vendor_google_fonts.py, then paste its stdout
   over the block below and re-check the reader side-by-side. */
/* Schibsted Grotesk */
@font-face {
    font-family: 'Schibsted Grotesk';
    font-style: normal;
    font-weight: 400 700;
    font-display: swap;
    src: url('../fonts/schibsted-grotesk/schibsted-grotesk-latin-ext-400-700.woff2') format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
    font-family: 'Schibsted Grotesk';
    font-style: normal;
    font-weight: 400 700;
    font-display: swap;
    src: url('../fonts/schibsted-grotesk/schibsted-grotesk-latin-400-700.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Newsreader  (dropped: vietnamese) */
@font-face {
    font-family: 'Newsreader';
    font-style: italic;
    font-weight: 400 500;
    font-display: swap;
    src: url('../fonts/newsreader/newsreader-latin-ext-italic-400-500.woff2') format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
    font-family: 'Newsreader';
    font-style: italic;
    font-weight: 400 500;
    font-display: swap;
    src: url('../fonts/newsreader/newsreader-latin-italic-400-500.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
    font-family: 'Newsreader';
    font-style: normal;
    font-weight: 400 600;
    font-display: swap;
    src: url('../fonts/newsreader/newsreader-latin-ext-400-600.woff2') format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
    font-family: 'Newsreader';
    font-style: normal;
    font-weight: 400 600;
    font-display: swap;
    src: url('../fonts/newsreader/newsreader-latin-400-600.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Literata  (dropped: greek, greek-ext, vietnamese) */
@font-face {
    font-family: 'Literata';
    font-style: normal;
    font-weight: 400 600;
    font-display: swap;
    src: url('../fonts/literata/literata-cyrillic-ext-400-600.woff2') format('woff2');
    unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
@font-face {
    font-family: 'Literata';
    font-style: normal;
    font-weight: 400 600;
    font-display: swap;
    src: url('../fonts/literata/literata-cyrillic-400-600.woff2') format('woff2');
    unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
    font-family: 'Literata';
    font-style: normal;
    font-weight: 400 600;
    font-display: swap;
    src: url('../fonts/literata/literata-latin-ext-400-600.woff2') format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
    font-family: 'Literata';
    font-style: normal;
    font-weight: 400 600;
    font-display: swap;
    src: url('../fonts/literata/literata-latin-400-600.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* JetBrains Mono  (dropped: greek, vietnamese) */
@font-face {
    font-family: 'JetBrains Mono';
    font-style: normal;
    font-weight: 400 500;
    font-display: swap;
    src: url('../fonts/jetbrains-mono/jetbrains-mono-cyrillic-ext-400-500.woff2') format('woff2');
    unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
@font-face {
    font-family: 'JetBrains Mono';
    font-style: normal;
    font-weight: 400 500;
    font-display: swap;
    src: url('../fonts/jetbrains-mono/jetbrains-mono-cyrillic-400-500.woff2') format('woff2');
    unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
    font-family: 'JetBrains Mono';
    font-style: normal;
    font-weight: 400 500;
    font-display: swap;
    src: url('../fonts/jetbrains-mono/jetbrains-mono-latin-ext-400-500.woff2') format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
    font-family: 'JetBrains Mono';
    font-style: normal;
    font-weight: 400 500;
    font-display: swap;
    src: url('../fonts/jetbrains-mono/jetbrains-mono-latin-400-500.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Merriweather  (dropped: vietnamese) */
@font-face {
    font-family: 'Merriweather';
    font-style: normal;
    font-weight: 400 700;
    font-display: swap;
    src: url('../fonts/merriweather/merriweather-cyrillic-ext-400-700.woff2') format('woff2');
    unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
@font-face {
    font-family: 'Merriweather';
    font-style: normal;
    font-weight: 400 700;
    font-display: swap;
    src: url('../fonts/merriweather/merriweather-cyrillic-400-700.woff2') format('woff2');
    unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
    font-family: 'Merriweather';
    font-style: normal;
    font-weight: 400 700;
    font-display: swap;
    src: url('../fonts/merriweather/merriweather-latin-ext-400-700.woff2') format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
    font-family: 'Merriweather';
    font-style: normal;
    font-weight: 400 700;
    font-display: swap;
    src: url('../fonts/merriweather/merriweather-latin-400-700.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Atkinson Hyperlegible */
@font-face {
    font-family: 'Atkinson Hyperlegible';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url('../fonts/atkinson-hyperlegible/atkinson-hyperlegible-latin-ext-400.woff2') format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
    font-family: 'Atkinson Hyperlegible';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url('../fonts/atkinson-hyperlegible/atkinson-hyperlegible-latin-400.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
    font-family: 'Atkinson Hyperlegible';
    font-style: normal;
    font-weight: 700;
    font-display: swap;
    src: url('../fonts/atkinson-hyperlegible/atkinson-hyperlegible-latin-ext-700.woff2') format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
    font-family: 'Atkinson Hyperlegible';
    font-style: normal;
    font-weight: 700;
    font-display: swap;
    src: url('../fonts/atkinson-hyperlegible/atkinson-hyperlegible-latin-700.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Fredoka  (dropped: hebrew) */
@font-face {
    font-family: 'Fredoka';
    font-style: normal;
    font-weight: 400 700;
    font-display: swap;
    src: url('../fonts/fredoka/fredoka-latin-ext-400-700.woff2') format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
    font-family: 'Fredoka';
    font-style: normal;
    font-weight: 400 700;
    font-display: swap;
    src: url('../fonts/fredoka/fredoka-latin-400-700.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* OpenDyslexic — self-hosted (OFL, see ../fonts/opendyslexic/OFL.txt). Not on Google Fonts;
   a dyslexia-friendly reader font option. Paths are RELATIVE to this file
   (served at _content/LearnWordsAI.SharedUI/css/tokens.css), so they resolve under the RCL
   static-asset base on every host — an absolute /fonts/ would 404 outside the RCL. */
@font-face {
    font-family: 'OpenDyslexic';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url('../fonts/opendyslexic/OpenDyslexic-Regular.woff2') format('woff2');
}
@font-face {
    font-family: 'OpenDyslexic';
    font-style: normal;
    font-weight: 700;
    font-display: swap;
    src: url('../fonts/opendyslexic/OpenDyslexic-Bold.woff2') format('woff2');
}

:root {
    /* ---------- Interface text-size multiplier (the "Interface size" regulator) ----------
       Global UI scale knob. 1 = normal; the S/M/L control steps it to 1.15 / 1.30.
       It multiplies BOTH the root font-size (the `html { font-size }` rule just below
       this block, so rem-based chrome scales) AND the UI `--type-*-size/-line` tokens
       (so token-based chrome — the whole `.lw-type-*` system — scales). The book prose
       is deliberately NOT affected: it sizes off the inline `--reader-font-size`, and
       `--type-reading-*` is left un-multiplied. Written to <html> at runtime by
       window.lwUi (js/theme.js) and, before first paint, by the per-host boot snippet
       (index.html / App.razor). Driven from C# by SharedUI UiScaleService. */
    --ui-scale: 1;

    /* ---------- Geometry / motion / type stacks (theme-independent) ----------
       Small, crisp editorial radii. Pill (999) is kept for true circles
       (avatars, toggles) — NOT for cards/buttons, which read as flat plates. */
    --radius-xs: 3px;
    --radius-sm: 4px;
    --radius: 6px;
    --radius-lg: 12px;
    --radius-pill: 999px;

    /* ---------- Content width tiers (theme-independent layout) ----------
       Width follows CONTENT TYPE, not the page — one source of truth for both
       hosts (main app + Reading.Web) so the two can't drift. All are max-widths:
       phones (viewport < cap) stay full-width; margin-inline:auto only centers
       once the screen is wider than the cap.
         readable — text, forms, lists, settings, reading prose (~70–75 chars)
         wide     — card-grid catalogs / book shelves
         narrow   — auth screens & modal dialogs
       Apply via the .lw-container-readable / .lw-container-wide utilities below,
       or `max-width: var(--w-…)` on an existing page wrapper. */
    --w-readable: 700px;
    --w-wide: 1180px;
    --w-narrow: 440px;

    --transition: 180ms cubic-bezier(0.4, 0, 0.2, 1);
    --transition-fast: 110ms cubic-bezier(0.4, 0, 0.2, 1);

    --font: 'Schibsted Grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, system-ui, sans-serif;
    /* Brand / heading face — the editorial serif. Big numbers, titles, the word
       on a flashcard, chapter names. Applied by the .lw-type-display/title-* utils.
       'Literata' for the same reason as in --font-reading below: Newsreader carries no
       Cyrillic, so a Russian heading fell through to Georgia and read as a different
       (larger, heavier) face than the Latin headings beside it. */
    --font-display: 'Newsreader', 'Literata', 'Iowan Old Style', Georgia, 'Times New Roman', serif;
    --font-mono: 'JetBrains Mono', 'SF Mono', 'Fira Code', monospace;
    /* Reading surfaces (story body, parallel text) read this. Serif by default on
       EVERY theme now (the product is editorial); the reader's font-family setting
       (ReadingPreferences) overrides it per user.
       'Literata' sits right after 'Newsreader' on purpose: Newsreader's Google Fonts webfont
       ships NO Cyrillic subset (latin / latin-ext / vietnamese only), so a parallel book's
       Russian translation layer fell through to the platform serif (Palatino Linotype / Georgia),
       which renders visibly larger + heavier than the Latin Newsreader next to it — the EN/RU
       size+weight mismatch in #818. Literata (self-hosted alongside it above) DOES carry
       Cyrillic and is a close editorial-serif match, so Cyrillic now lands on a consistent webfont
       instead of a platform fallback. Latin glyphs still resolve to Newsreader (first in the list),
       so the English side is unchanged. */
    --font-reading: 'Newsreader', 'Literata', 'Iowan Old Style', 'Palatino Linotype', Palatino, Georgia, serif;
    /* Playful display face for AI-story shelf covers (#767 Ч.1) — a rounded, friendly heading
       font so a generated story's title reads as a fun "cover", distinct from the editorial
       --font-display serif. Falls back to the brand serif if Fredoka fails to load. */
    --font-cover: 'Fredoka', var(--font-display);

    /* Optional decorative page background (per-theme: night starfield, paper
       sun-wash + ruled stock; cool leaves it none). Applied on <body> in reset.css. */
    --paper-texture: none;

    /* ---------- Spacing scale (4px base) ---------- */
    --space-1: 4px;
    --space-2: 8px;
    --space-3: 12px;
    --space-4: 16px;
    --space-5: 20px;
    --space-6: 24px;
    --space-8: 32px;
    --space-10: 40px;
    --space-12: 48px;
    --space-16: 64px;

    /* ---------- Type scale (theme-independent) ----------
       Use these EVERYWHERE instead of inline font-size. Pair with the
       .lw-type-* utility classes at the bottom of this file. The display +
       title tiers render in --font-display (serif); body/caption stay sans.

       Every UI tier's -size AND -line multiply by --ui-scale (the "Interface size"
       regulator) so the whole .lw-type-* system grows/shrinks together — size and
       line scale in lockstep so leading never overlaps. At the default step
       (--ui-scale: 1) `calc(Npx * 1)` resolves to exactly Npx, so the unscaled
       look is byte-identical (no desktop regression). --type-reading-* is the BOOK
       prose tier and is intentionally NOT multiplied — book size is the reader's
       own --reader-font-size, independent of the UI regulator. */
    --type-display-size: calc(32px * var(--ui-scale, 1));  --type-display-line: calc(38px * var(--ui-scale, 1));  --type-display-weight: 600; --type-display-track: -0.02em;
    --type-title-l-size: calc(22px * var(--ui-scale, 1));  --type-title-l-line: calc(28px * var(--ui-scale, 1));  --type-title-l-weight: 600; --type-title-l-track: -0.015em;
    --type-title-m-size: calc(18px * var(--ui-scale, 1));  --type-title-m-line: calc(24px * var(--ui-scale, 1));  --type-title-m-weight: 600; --type-title-m-track: -0.01em;
    --type-title-s-size: calc(15px * var(--ui-scale, 1));  --type-title-s-line: calc(21px * var(--ui-scale, 1));  --type-title-s-weight: 600; --type-title-s-track: -0.005em;
    --type-body-l-size:  calc(16px * var(--ui-scale, 1));  --type-body-l-line:  calc(26px * var(--ui-scale, 1));  --type-body-l-weight: 400; --type-body-l-track: 0;
    --type-body-size:    calc(14px * var(--ui-scale, 1));  --type-body-line:    calc(22px * var(--ui-scale, 1));  --type-body-weight:   400; --type-body-track:   0;
    --type-body-s-size:  calc(12px * var(--ui-scale, 1));  --type-body-s-line:  calc(18px * var(--ui-scale, 1));  --type-body-s-weight: 400; --type-body-s-track: 0.002em;
    --type-caption-size: calc(11px * var(--ui-scale, 1));  --type-caption-line: calc(16px * var(--ui-scale, 1));  --type-caption-weight: 500; --type-caption-track: 0.14em;
    --type-reading-size: 18px;  --type-reading-line: 30px;  --type-reading-weight: 400; --type-reading-track: 0.002em;

    /* Числовые дисплеи. Всегда tabular-nums. */
    --type-num-l-size: calc(30px * var(--ui-scale, 1)); --type-num-l-line: calc(34px * var(--ui-scale, 1));
    --type-num-m-size: calc(22px * var(--ui-scale, 1)); --type-num-m-line: calc(26px * var(--ui-scale, 1));
    --type-num-s-size: calc(16px * var(--ui-scale, 1)); --type-num-s-line: calc(20px * var(--ui-scale, 1));

    /* ---------- App shell (adaptive navigation frame) — theme-independent geometry ---------- */
    --shell-topbar-h: 52px;     /* content height of the top bar (excl. safe-area) */
    --shell-tabbar-h: 58px;     /* mobile bottom tab bar (excl. safe-area)          */
    --shell-rail-w: 92px;       /* desktop side rail width (wide enough most labels fit one line) */
    --shell-z-chrome: 50;       /* top bar / tab bar / rail stacking                 */

    /* Height of the mobile exercise action dock + its docked buttons (≤640px lesson
       layout). Theme-independent geometry; the verdict-color tokens live per-theme
       (searched as --verdict-*). See exercises.css @media(max-width:640px).

       Multiplied by --ui-scale for the same reason the type tokens are: the dock's
       label already grows with the regulator, so a fixed 56px box left the text
       climbing the walls of a plate that never moved — at step 2 the lesson read as
       "the setting does nothing" even though every glyph in it was 30% larger. The
       button is the target the thumb aims at, so it has to grow with what it says. */
    --ex-dock-h: calc(56px * var(--ui-scale, 1));

    /* Approximate height of an in-page .page-header (h1 row + padding). Used by
       sticky controls to anchor "just under the header" without each page having
       to know the exact number. */
    --page-header-h: 60px;

    /* Footprint reserved on the RIGHT edge of every sticky page header for the global
       FloatingActionDock (position: fixed, top-right). Sized for the ACTION PILL — a
       88px-wide capsule (two 44px segments) at a 12px inset, plus ~8px of breathing room —
       not the 38px circle that used to live there. Desktop widens to 46px segments at an
       18px inset. Right-aligned header actions must clear this. */
    --fab-inset-right: calc(var(--safe-right) + 108px);

    /* Physical device insets — the screen's own geometry (notch, status bar, home indicator)
       exactly as the platform reports it. Always the raw env() values, on every host, including
       the MAUI WebView (measured on a Redmi Note 12 Pro: 35px of top inset there). Read these
       ONLY when the thing being positioned has to line up with something drawn OUTSIDE the
       WebView — today that is just the boot splash matching the native splash frame. */
    --screen-inset-top: env(safe-area-inset-top, 0px);
    --screen-inset-bottom: env(safe-area-inset-bottom, 0px);
    --screen-inset-left: env(safe-area-inset-left, 0px);
    --screen-inset-right: env(safe-area-inset-right, 0px);

    /* What the PAGE still has to reserve — the layout tokens, and what every rule below should
       read. Identical to the physical insets wherever the page owns the whole screen (browser,
       installed PWA, iOS), but a host whose NATIVE layer has already inset the WebView overrides
       them to 0: see LearnWordsAI.Mobile/wwwroot/index.html, where MainActivity pads the activity
       out of the system bars and the page would otherwise reserve the same strip a second time. */
    --safe-top: var(--screen-inset-top);
    --safe-bottom: var(--screen-inset-bottom);
    --safe-left: var(--screen-inset-left);
    --safe-right: var(--screen-inset-right);

    /* ============================================================
       AUTO mode (default — no data-theme attribute)
       Bridges Telegram theme vars when inside Mini App; otherwise
       falls back to the NIGHT palette below for off-Telegram clients.
       ============================================================ */
    --bg:           var(--tg-theme-bg-color, #0b0e15);
    --bg-secondary: var(--tg-theme-secondary-bg-color, #10131d);
    --bg-elevated:  #1a1f30;
    --card-bg:      var(--tg-theme-section-bg-color, #141826);
    --card-border:  rgba(230, 232, 240, 0.12);
    --card-hover:   rgba(230, 232, 240, 0.06);

    --text:           var(--tg-theme-text-color, #e6e8f0);
    --text-secondary: var(--tg-theme-subtitle-text-color, #9298a8);
    --hint:           var(--tg-theme-hint-color, #5c6373);
    --on-surface-rgb: 230, 232, 240;

    --accent:        var(--tg-theme-accent-text-color, var(--tg-theme-link-color, #e07a4e));
    --accent-rgb:    224, 122, 78;
    --accent-strong: #ec9069;
    --accent-soft:   rgba(224, 122, 78, 0.16);
    --accent-light:  rgba(224, 122, 78, 0.16); /* v1 alias */

    /* ---------- Loader cosmos (boot splash + LoadingOverlay) ----------
       The orbital loader is a self-contained DARK starfield with a per-theme HUE — even
       on the light themes it stays a dark sky (cool = navy/blue, paper = warm umber/gold)
       so the starlight actually reads. The loader CSS remaps the page tokens it uses
       (--bg / --text / --accent*) to these, so one block re-skins the whole loader:
         --loader-bg           dark sky base
         --loader-text(-dim)   light ink for the wordmark + status line
         --loader-accent-rgb   the hue (orbit rings, word-chips, comet streak)
         --star-* / --star-peak  starlight + twinkle depth
         --meteor-tip-rgb      the comet's bright leading head */
    --loader-bg:         #0b0e15;
    --loader-text:       #e6e8f0;
    --loader-text-dim:   #9298a8;
    --loader-accent-rgb: 224, 122, 78;
    --star-rgb:          255, 255, 255;
    --star-alt-rgb:      130, 185, 250;
    --star-glow:         rgba(180, 210, 255, 0.8);
    --star-peak:         1;
    --meteor-tip-rgb:    255, 255, 255;

    --button-bg:   var(--tg-theme-button-color, #e07a4e);
    --button-text: var(--tg-theme-button-text-color, #190f08);

    --success:     #9cc77f; --success-rgb:     156, 199, 127;
    --warning:     #e0b25a; --warning-rgb:     224, 178, 90;
    --destructive: var(--tg-theme-destructive-text-color, #e08a7f); --destructive-rgb: 224, 138, 127;
    --info:        #7fb6cf; --info-rgb:        127, 182, 207;
    /* SRS progress ring — purple, deliberately distinct from --accent and --info. */
    --srs:         #b39bff; --srs-rgb:         179, 155, 255;
    --grammar:         #b39bff; --grammar-rgb:         179, 155, 255;

    /* Categorical highlight palettes (grammar pattern slots + vocab-status underlines).
       Slot hues stay distinguishable per theme: saturated on dark, deepened on light. */
    --grammar-cat-0-rgb: 168, 85, 247;
    --grammar-cat-1-rgb: 251, 146, 60;
    --grammar-cat-2-rgb: 20, 184, 166;
    --grammar-cat-3-rgb: 234, 179, 8;
    --grammar-cat-4-rgb: 236, 72, 153;
    --hl-green-rgb: 34, 197, 94;
    --hl-blue-rgb: 59, 130, 246;
    /* Stabilized-through-FSRS (auto-mastered) — teal, distinct from accent. */
    --mastered-stable: #1ec8b8; --mastered-stable-rgb: 30, 200, 184;

    /* SRS rating hues (flashcard Again/Hard/Good/Easy) — warm, restrained. */
    --again: #e08a7f; --hard: #e0b25a; --good: #7fb6cf; --easy: #9cc77f;

    --word-new-bg:        rgba(224, 122, 78, 0.16);
    --word-new-line:      var(--accent);
    --word-new-text:      var(--text);
    --word-seen-bg:       rgba(224, 122, 78, 0.09);
    --word-seen-line:     transparent;
    --word-seen-text:     var(--text);
    --word-learning-bg:   transparent;
    --word-learning-line: var(--accent);
    --word-learning-text: var(--text);
    --word-familiar-bg:   transparent;
    --word-familiar-line: rgba(224, 122, 78, 0.4);
    --word-familiar-text: var(--text);
    --word-mastered-bg:   transparent;
    --word-mastered-line: transparent;
    --word-mastered-text: var(--text);

    /* v1 compat — reader still references these. */
    --hl-ai-bg:     var(--word-new-bg);
    --hl-ai-border: var(--word-new-line);
    --hl-user-bg:   var(--word-learning-bg);
    --hl-user-border: var(--word-learning-line);
    --hl-added:     var(--success);

    /* Editorial = flat surfaces. shadow-1/2 are off; shadow-3 stays for genuinely
       floating overlays (modals, bottom sheets, dropdowns, the quick-add FAB). */
    --shadow-1: none;
    --shadow-2: none;
    --shadow-3: 0 16px 40px rgba(0, 0, 0, 0.55);
    --shadow:   var(--shadow-2);

    /* Verdict sheet (mobile lesson ≤640) — fill + ink for the color-coded "Next" pill
       and the bottom sheet. Night ink on the success/destructive hues; the sheet/option
       tints compose off --success-rgb / --destructive-rgb. See exercises.css .ex-verdict. */
    --verdict-ok-bg:  #9cc77f;  --verdict-ok-ink:  #14210e;
    --verdict-bad-bg: #e08a7f;  --verdict-bad-ink: #260f0d;

    /* Night cosmos: scattered star dust + a warm Mars glow + a faint cool nebula. */
    --paper-texture:
        radial-gradient(1.5px 1.5px at 14% 16%, rgba(255,255,255,.75), transparent 60%),
        radial-gradient(1px 1px   at 32% 9%,  rgba(255,255,255,.5),  transparent 60%),
        radial-gradient(1.5px 1.5px at 57% 21%, rgba(255,255,255,.6), transparent 60%),
        radial-gradient(1px 1px   at 78% 12%, rgba(255,255,255,.45), transparent 60%),
        radial-gradient(1px 1px   at 88% 29%, rgba(255,255,255,.55), transparent 60%),
        radial-gradient(1.5px 1.5px at 8% 43%, rgba(255,255,255,.5),  transparent 60%),
        radial-gradient(1px 1px   at 45% 51%, rgba(255,255,255,.4),  transparent 60%),
        radial-gradient(1px 1px   at 70% 61%, rgba(255,255,255,.45), transparent 60%),
        radial-gradient(1.5px 1.5px at 22% 72%, rgba(255,255,255,.5), transparent 60%),
        radial-gradient(1px 1px   at 90% 69%, rgba(255,255,255,.4),  transparent 60%),
        radial-gradient(1px 1px   at 37% 85%, rgba(255,255,255,.45), transparent 60%),
        radial-gradient(1px 1px   at 64% 92%, rgba(255,255,255,.35), transparent 60%),
        radial-gradient(170px 170px at 93% 6%, rgba(224,122,78,.11), transparent 70%),
        radial-gradient(240px 220px at 6% 98%, rgba(120,150,220,.06), transparent 72%);
}

/* Мобильный диапазон: мелкие тиры поднимаются (телефон), крупные — поджимаются.
   Всё по-прежнему множится на --ui-scale, регулятор «Размер интерфейса» работает поверх.
   Было → стало при --ui-scale: 1 — caption 11→12, body-s 12→14, body 14→15, body-l 16→17,
   title-s 15→17, title-m 18→20, title-l 22→21, display 32→28. Мелкие тиры растут ≈115 %,
   крупные почти не двигаются, так что экран не раздувается. body-l остаётся ≥ 16px —
   инпуты не ловят iOS auto-zoom. */
@media (max-width: 640px) {
    :root {
        --type-display-size: calc(28px * var(--ui-scale, 1));  --type-display-line: calc(34px * var(--ui-scale, 1));
        --type-title-l-size: calc(21px * var(--ui-scale, 1));  --type-title-l-line: calc(27px * var(--ui-scale, 1));
        --type-title-m-size: calc(20px * var(--ui-scale, 1));  --type-title-m-line: calc(26px * var(--ui-scale, 1));
        --type-title-s-size: calc(17px * var(--ui-scale, 1));  --type-title-s-line: calc(23px * var(--ui-scale, 1));
        --type-body-l-size:  calc(17px * var(--ui-scale, 1));  --type-body-l-line:  calc(26px * var(--ui-scale, 1));
        --type-body-size:    calc(15px * var(--ui-scale, 1));  --type-body-line:    calc(23px * var(--ui-scale, 1));
        --type-body-s-size:  calc(14px * var(--ui-scale, 1));  --type-body-s-line:  calc(20px * var(--ui-scale, 1));
        --type-caption-size: calc(12px * var(--ui-scale, 1));  --type-caption-line: calc(17px * var(--ui-scale, 1));
    }
}

/* The action pill grows to 46px segments and moves to an 18px inset at the desktop
   breakpoint, so the header reserve has to grow with it (see --fab-inset-right above). */
@media (min-width: 768px) {
    :root {
        --fab-inset-right: calc(var(--safe-right) + 118px);
    }
}

/* ---------- Root UI-text base (drives every `rem`) ----------
   NO global `html { font-size }` existed before — root was the device/browser default
   (usually 16px). Pinning it here lets the interface respect the user's device/browser
   "font size" setting AND our own regulator, the two things plain `px` can't honour:
     • clamp(15px … 17px) — a small responsive bump so tiny rem-based chrome is a touch
       larger on phones (the 0.25vw term) without ballooning on desktop;
     • the inner `0.9rem` keeps the OS/browser font-size preference in the loop (rem on
       the root resolves against that preference, so a user who sets a larger default
       still scales up);
     • × var(--ui-scale) — the explicit "Interface size" regulator (also reliable inside
       iOS Safari / the Telegram WebView, where the system text-size setting is ignored).
   Book prose is untouched: .reader-content sizes off the inline --reader-font-size (px),
   never rem. Keep this in `px`-with-clamp — a bare `px` root would break zoom. */
html {
    font-size: calc(clamp(15px, 0.9rem + 0.25vw, 17px) * var(--ui-scale, 1));
}

/* AUTO mode under a light OS preference (only when not forced to a theme).
   Off-Telegram light users land on the COOL editorial palette. */
@media (prefers-color-scheme: light) {
    :root:not([data-theme]) {
        --bg:           var(--tg-theme-bg-color, #eef1f5);
        --bg-secondary: var(--tg-theme-secondary-bg-color, #e3e8ee);
        --bg-elevated:  #ffffff;
        --card-bg:      var(--tg-theme-section-bg-color, #fbfcfe);
        --card-border:  rgba(27, 32, 39, 0.12);
        --card-hover:   #f3f6fa;

        /* Verdict sheet (mobile lesson ≤640) — AUTO light: success/destructive fill, white ink. */
        --verdict-ok-bg:  var(--success);      --verdict-ok-ink:  #ffffff;
        --verdict-bad-bg: var(--destructive);  --verdict-bad-ink: #ffffff;

        --text:           var(--tg-theme-text-color, #1b2027);
        --text-secondary: var(--tg-theme-subtitle-text-color, #586271);
        --hint:           var(--tg-theme-hint-color, #909aa6);
        --on-surface-rgb: 27, 32, 39;

        --accent:        var(--tg-theme-accent-text-color, var(--tg-theme-link-color, #b8431f));
        --accent-rgb:    184, 67, 31;
        --accent-strong: #97361a;
        --accent-soft:   rgba(184, 67, 31, 0.10);
        --accent-light:  rgba(184, 67, 31, 0.10);

        /* Loader cosmos — dark navy sky + icy-blue starlight for off-Telegram light users (matches COOL). */
        --loader-bg:         #0c1a35;
        --loader-text:       #e2ecfa;
        --loader-text-dim:   #97abc8;
        --loader-accent-rgb: 96, 150, 228;
        --star-rgb:          208, 226, 255;
        --star-alt-rgb:      130, 180, 255;
        --star-glow:         rgba(150, 195, 255, 0.85);
        --star-peak:         1;
        --meteor-tip-rgb:    236, 244, 255;

        --button-bg:   var(--tg-theme-button-color, #b8431f);
        --button-text: var(--tg-theme-button-text-color, #ffffff);

        --success: #3f7d3a; --success-rgb: 63, 125, 58;
        --warning: #b07415; --warning-rgb: 176, 116, 21;
        --destructive: #b23b46; --destructive-rgb: 178, 59, 70;
        --info:    #2c6c87;     --info-rgb:    44, 108, 135;
        --srs:     #7c3aed;     --srs-rgb:     124, 58, 237;
        --grammar:     #7c3aed;     --grammar-rgb:     124, 58, 237;

        /* Categorical highlight palettes (grammar pattern slots + vocab-status underlines).
           Slot hues stay distinguishable per theme: saturated on dark, deepened on light. */
        --grammar-cat-0-rgb: 124, 58, 237;
        --grammar-cat-1-rgb: 194, 88, 14;
        --grammar-cat-2-rgb: 13, 131, 118;
        --grammar-cat-3-rgb: 176, 116, 21;
        --grammar-cat-4-rgb: 190, 24, 93;
        --hl-green-rgb: 22, 140, 62;
        --hl-blue-rgb: 29, 88, 191;
        --mastered-stable: #0e8d7f; --mastered-stable-rgb: 14, 141, 127;

        --again: #b23b46; --hard: #b07415; --good: #2c6c87; --easy: #3f7d3a;

        --word-new-bg:        rgba(184, 67, 31, 0.12);
        --word-new-line:      var(--accent);
        --word-seen-bg:       rgba(184, 67, 31, 0.06);
        --word-learning-line: var(--accent);
        --word-familiar-line: rgba(184, 67, 31, 0.4);

        --shadow-1: none;
        --shadow-2: none;
        --shadow-3: 0 12px 32px rgba(21, 40, 70, 0.14);
        --shadow:   var(--shadow-2);

        --paper-texture: none;
    }
}

/* ============================================================
   NIGHT (data-theme="dark") — observatory cosmos, lamplight gold.
   Deep blue-black sky, starlight off-white ink, terracotta/Mars accent.
   ============================================================ */
:root[data-theme="dark"] {
    --bg:           #0b0e15;
    --bg-secondary: #10131d;
    --bg-elevated:  #1a1f30;
    --card-bg:      #141826;
    --card-border:  rgba(230, 232, 240, 0.12);
    --card-hover:   rgba(230, 232, 240, 0.06);

    --text:           #e6e8f0;
    --text-secondary: #9298a8;
    --hint:           #5c6373;
    --on-surface-rgb: 230, 232, 240;

    --accent:        #e07a4e;
    --accent-rgb:    224, 122, 78;
    --accent-strong: #ec9069;
    --accent-soft:   rgba(224, 122, 78, 0.16);
    --accent-light:  rgba(224, 122, 78, 0.16);

    /* Loader cosmos — NIGHT: deep blue-black sky, white starlight, warm terracotta comet. */
    --loader-bg:         #0b0e15;
    --loader-text:       #e6e8f0;
    --loader-text-dim:   #9298a8;
    --loader-accent-rgb: 224, 122, 78;
    --star-rgb:          255, 255, 255;
    --star-alt-rgb:      130, 185, 250;
    --star-glow:         rgba(180, 210, 255, 0.8);
    --star-peak:         1;
    --meteor-tip-rgb:    255, 255, 255;

    --button-bg:   var(--accent);
    --button-text: #190f08;

    --success: #9cc77f; --success-rgb: 156, 199, 127;
    --warning: #e0b25a; --warning-rgb: 224, 178, 90;
    --destructive: #e08a7f; --destructive-rgb: 224, 138, 127;
    --info:    #7fb6cf;     --info-rgb:    127, 182, 207;
    --srs:     #b39bff;     --srs-rgb:     179, 155, 255;
    --grammar:     #b39bff;     --grammar-rgb:     179, 155, 255;

    /* Categorical highlight palettes (grammar pattern slots + vocab-status underlines).
       Slot hues stay distinguishable per theme: saturated on dark, deepened on light. */
    --grammar-cat-0-rgb: 168, 85, 247;
    --grammar-cat-1-rgb: 251, 146, 60;
    --grammar-cat-2-rgb: 20, 184, 166;
    --grammar-cat-3-rgb: 234, 179, 8;
    --grammar-cat-4-rgb: 236, 72, 153;
    --hl-green-rgb: 34, 197, 94;
    --hl-blue-rgb: 59, 130, 246;
    --mastered-stable: #1ec8b8; --mastered-stable-rgb: 30, 200, 184;

    --again: #e08a7f; --hard: #e0b25a; --good: #7fb6cf; --easy: #9cc77f;

    --word-new-bg:        rgba(224, 122, 78, 0.16);
    --word-new-line:      var(--accent);
    --word-new-text:      var(--text);
    --word-seen-bg:       rgba(224, 122, 78, 0.09);
    --word-seen-line:     transparent;
    --word-seen-text:     var(--text);
    --word-learning-bg:   transparent;
    --word-learning-line: var(--accent);
    --word-learning-text: var(--text);
    --word-familiar-bg:   transparent;
    --word-familiar-line: rgba(224, 122, 78, 0.4);
    --word-familiar-text: var(--text);
    --word-mastered-bg:   transparent;
    --word-mastered-line: transparent;
    --word-mastered-text: var(--text);

    --hl-ai-bg:     var(--word-new-bg);
    --hl-ai-border: var(--word-new-line);
    --hl-user-bg:   var(--word-learning-bg);
    --hl-user-border: var(--word-learning-line);
    --hl-added:     var(--success);

    --shadow-1: none;
    --shadow-2: none;
    --shadow-3: 0 16px 40px rgba(0, 0, 0, 0.6);
    --shadow:   var(--shadow-2);

    /* Verdict sheet (mobile lesson ≤640) — night ink on success/destructive. */
    --verdict-ok-bg:  #9cc77f;  --verdict-ok-ink:  #14210e;
    --verdict-bad-bg: #e08a7f;  --verdict-bad-ink: #260f0d;

    --paper-texture:
        radial-gradient(1.5px 1.5px at 14% 16%, rgba(255,255,255,.75), transparent 60%),
        radial-gradient(1px 1px   at 32% 9%,  rgba(255,255,255,.5),  transparent 60%),
        radial-gradient(1.5px 1.5px at 57% 21%, rgba(255,255,255,.6), transparent 60%),
        radial-gradient(1px 1px   at 78% 12%, rgba(255,255,255,.45), transparent 60%),
        radial-gradient(1px 1px   at 88% 29%, rgba(255,255,255,.55), transparent 60%),
        radial-gradient(1.5px 1.5px at 8% 43%, rgba(255,255,255,.5),  transparent 60%),
        radial-gradient(1px 1px   at 45% 51%, rgba(255,255,255,.4),  transparent 60%),
        radial-gradient(1px 1px   at 70% 61%, rgba(255,255,255,.45), transparent 60%),
        radial-gradient(1.5px 1.5px at 22% 72%, rgba(255,255,255,.5), transparent 60%),
        radial-gradient(1px 1px   at 90% 69%, rgba(255,255,255,.4),  transparent 60%),
        radial-gradient(1px 1px   at 37% 85%, rgba(255,255,255,.45), transparent 60%),
        radial-gradient(1px 1px   at 64% 92%, rgba(255,255,255,.35), transparent 60%),
        radial-gradient(170px 170px at 93% 6%, rgba(224,122,78,.11), transparent 70%),
        radial-gradient(240px 220px at 6% 98%, rgba(120,150,220,.06), transparent 72%);
}

/* ============================================================
   AMOLED (data-theme="amoled") — true-black OLED variant of NIGHT.
   Identical to dark except surface tokens are #000 pure black so
   OLED panels can turn off pixels. All other tokens copied verbatim
   from the dark block so nothing is undefined.
   ============================================================ */
:root[data-theme="amoled"] {
    --bg:           #000000;
    --bg-secondary: #050608;
    --bg-elevated:  #0d0f16;
    --card-bg:      #0a0c12;
    --card-border:  rgba(230, 232, 240, 0.14);
    --card-hover:   rgba(230, 232, 240, 0.06);

    --text:           #e6e8f0;
    --text-secondary: #9298a8;
    --hint:           #5c6373;
    --on-surface-rgb: 230, 232, 240;

    --accent:        #e07a4e;
    --accent-rgb:    224, 122, 78;
    --accent-strong: #ec9069;
    --accent-soft:   rgba(224, 122, 78, 0.16);
    --accent-light:  rgba(224, 122, 78, 0.16);

    /* Loader cosmos — AMOLED: pure black sky, white starlight, warm terracotta comet. */
    --loader-bg:         #000000;
    --loader-text:       #e6e8f0;
    --loader-text-dim:   #9298a8;
    --loader-accent-rgb: 224, 122, 78;
    --star-rgb:          255, 255, 255;
    --star-alt-rgb:      130, 185, 250;
    --star-glow:         rgba(180, 210, 255, 0.8);
    --star-peak:         1;
    --meteor-tip-rgb:    255, 255, 255;

    --button-bg:   var(--accent);
    --button-text: #190f08;

    --success: #9cc77f; --success-rgb: 156, 199, 127;
    --warning: #e0b25a; --warning-rgb: 224, 178, 90;
    --destructive: #e08a7f; --destructive-rgb: 224, 138, 127;
    --info:    #7fb6cf;     --info-rgb:    127, 182, 207;
    --srs:     #b39bff;     --srs-rgb:     179, 155, 255;
    --grammar:     #b39bff;     --grammar-rgb:     179, 155, 255;

    /* Categorical highlight palettes (grammar pattern slots + vocab-status underlines).
       Slot hues stay distinguishable per theme: saturated on dark, deepened on light. */
    --grammar-cat-0-rgb: 168, 85, 247;
    --grammar-cat-1-rgb: 251, 146, 60;
    --grammar-cat-2-rgb: 20, 184, 166;
    --grammar-cat-3-rgb: 234, 179, 8;
    --grammar-cat-4-rgb: 236, 72, 153;
    --hl-green-rgb: 34, 197, 94;
    --hl-blue-rgb: 59, 130, 246;
    --mastered-stable: #1ec8b8; --mastered-stable-rgb: 30, 200, 184;

    --again: #e08a7f; --hard: #e0b25a; --good: #7fb6cf; --easy: #9cc77f;

    --word-new-bg:        rgba(224, 122, 78, 0.16);
    --word-new-line:      var(--accent);
    --word-new-text:      var(--text);
    --word-seen-bg:       rgba(224, 122, 78, 0.09);
    --word-seen-line:     transparent;
    --word-seen-text:     var(--text);
    --word-learning-bg:   transparent;
    --word-learning-line: var(--accent);
    --word-learning-text: var(--text);
    --word-familiar-bg:   transparent;
    --word-familiar-line: rgba(224, 122, 78, 0.4);
    --word-familiar-text: var(--text);
    --word-mastered-bg:   transparent;
    --word-mastered-line: transparent;
    --word-mastered-text: var(--text);

    --hl-ai-bg:     var(--word-new-bg);
    --hl-ai-border: var(--word-new-line);
    --hl-user-bg:   var(--word-learning-bg);
    --hl-user-border: var(--word-learning-line);
    --hl-added:     var(--success);

    --shadow-1: none;
    --shadow-2: none;
    --shadow-3: 0 16px 40px rgba(0, 0, 0, 0.8);
    --shadow:   var(--shadow-2);

    /* Verdict sheet (mobile lesson ≤640) — night ink on success/destructive. */
    --verdict-ok-bg:  #9cc77f;  --verdict-ok-ink:  #14210e;
    --verdict-bad-bg: #e08a7f;  --verdict-bad-ink: #260f0d;

    --paper-texture:
        radial-gradient(1.5px 1.5px at 14% 16%, rgba(255,255,255,.75), transparent 60%),
        radial-gradient(1px 1px   at 32% 9%,  rgba(255,255,255,.5),  transparent 60%),
        radial-gradient(1.5px 1.5px at 57% 21%, rgba(255,255,255,.6), transparent 60%),
        radial-gradient(1px 1px   at 78% 12%, rgba(255,255,255,.45), transparent 60%),
        radial-gradient(1px 1px   at 88% 29%, rgba(255,255,255,.55), transparent 60%),
        radial-gradient(1.5px 1.5px at 8% 43%, rgba(255,255,255,.5),  transparent 60%),
        radial-gradient(1px 1px   at 45% 51%, rgba(255,255,255,.4),  transparent 60%),
        radial-gradient(1px 1px   at 70% 61%, rgba(255,255,255,.45), transparent 60%),
        radial-gradient(1.5px 1.5px at 22% 72%, rgba(255,255,255,.5), transparent 60%),
        radial-gradient(1px 1px   at 90% 69%, rgba(255,255,255,.4),  transparent 60%),
        radial-gradient(1px 1px   at 37% 85%, rgba(255,255,255,.45), transparent 60%),
        radial-gradient(1px 1px   at 64% 92%, rgba(255,255,255,.35), transparent 60%),
        radial-gradient(170px 170px at 93% 6%, rgba(224,122,78,.11), transparent 70%),
        radial-gradient(240px 220px at 6% 98%, rgba(120,150,220,.06), transparent 72%);
}

/* ============================================================
   COOL (data-theme="cool") — clean editorial light.
   Cold paper-white, slate ink, terracotta as the warm counterpoint
   (NO sapphire). Contrast: ink/bg ≈ 13:1 · accent/bg ≈ 5.9:1
   ============================================================ */
:root[data-theme="cool"] {
    --bg:           #eef1f5;
    --bg-secondary: #e3e8ee;
    --bg-elevated:  #ffffff;
    --card-bg:      #fbfcfe;
    --card-border:  rgba(27, 32, 39, 0.12);
    --card-hover:   #f3f6fa;

    /* Verdict sheet (mobile lesson ≤640) — light theme: success/destructive fill, white ink. */
    --verdict-ok-bg:  var(--success);      --verdict-ok-ink:  #ffffff;
    --verdict-bad-bg: var(--destructive);  --verdict-bad-ink: #ffffff;

    --text:           #1b2027;
    --text-secondary: #586271;
    --hint:           #909aa6;
    --on-surface-rgb: 27, 32, 39;

    --accent:        #b8431f;
    --accent-rgb:    184, 67, 31;
    --accent-strong: #97361a;
    --accent-soft:   rgba(184, 67, 31, 0.10);
    --accent-light:  rgba(184, 67, 31, 0.10);

    /* Loader cosmos — COOL: dark navy sky, icy-blue starlight + blue comet
       (the loader stays a dark sky even on this light editorial theme). */
    --loader-bg:         #0c1a35;
    --loader-text:       #e2ecfa;
    --loader-text-dim:   #97abc8;
    --loader-accent-rgb: 96, 150, 228;
    --star-rgb:          208, 226, 255;
    --star-alt-rgb:      130, 180, 255;
    --star-glow:         rgba(150, 195, 255, 0.85);
    --star-peak:         1;
    --meteor-tip-rgb:    236, 244, 255;

    --button-bg:   var(--accent);
    --button-text: #ffffff;

    --success: #3f7d3a; --success-rgb: 63, 125, 58;
    --warning: #b07415; --warning-rgb: 176, 116, 21;
    --destructive: #b23b46; --destructive-rgb: 178, 59, 70;
    --info:    #2c6c87;     --info-rgb:    44, 108, 135;
    --srs:     #7c3aed;     --srs-rgb:     124, 58, 237;
    --grammar:     #7c3aed;     --grammar-rgb:     124, 58, 237;

    /* Categorical highlight palettes (grammar pattern slots + vocab-status underlines).
       Slot hues stay distinguishable per theme: saturated on dark, deepened on light. */
    --grammar-cat-0-rgb: 124, 58, 237;
    --grammar-cat-1-rgb: 194, 88, 14;
    --grammar-cat-2-rgb: 13, 131, 118;
    --grammar-cat-3-rgb: 176, 116, 21;
    --grammar-cat-4-rgb: 190, 24, 93;
    --hl-green-rgb: 22, 140, 62;
    --hl-blue-rgb: 29, 88, 191;
    --mastered-stable: #0e8d7f; --mastered-stable-rgb: 14, 141, 127;

    --again: #b23b46; --hard: #b07415; --good: #2c6c87; --easy: #3f7d3a;

    --word-new-bg:        rgba(184, 67, 31, 0.12);
    --word-new-line:      var(--accent);
    --word-new-text:      var(--text);
    --word-seen-bg:       rgba(184, 67, 31, 0.06);
    --word-seen-line:     transparent;
    --word-seen-text:     var(--text);
    --word-learning-bg:   transparent;
    --word-learning-line: var(--accent);
    --word-learning-text: var(--text);
    --word-familiar-bg:   transparent;
    --word-familiar-line: rgba(184, 67, 31, 0.4);
    --word-familiar-text: var(--text);
    --word-mastered-bg:   transparent;
    --word-mastered-line: transparent;
    --word-mastered-text: var(--text);

    --hl-ai-bg:     var(--word-new-bg);
    --hl-ai-border: var(--word-new-line);
    --hl-user-bg:   var(--word-learning-bg);
    --hl-user-border: var(--word-learning-line);
    --hl-added:     var(--success);

    --shadow-1: none;
    --shadow-2: none;
    --shadow-3: 0 12px 32px rgba(21, 40, 70, 0.14);
    --shadow:   var(--shadow-2);

    --paper-texture: none;
}

/* ============================================================
   PAPER (data-theme="sepia") — warm book stock.
   Dune-sand surface, deep-umber ink, spice-rust accent, sun-wash +
   faint ruled-manuscript texture. Serif reading body.
   Contrast: ink/bg ≈ 10:1 · accent/bg ≈ 5.2:1
   ============================================================ */
:root[data-theme="sepia"] {
    --bg:           #e7d9bd;
    --bg-secondary: #ddccaa;
    --bg-elevated:  #f2e8d1;
    --card-bg:      #f2e8d1;
    --card-border:  rgba(42, 32, 18, 0.17);
    --card-hover:   #ede0c4;

    --text:           #2a2012;
    --text-secondary: #6d5b41;
    --hint:           #9a896a;
    --on-surface-rgb: 42, 32, 18;

    --accent:        #b3401a;
    --accent-rgb:    179, 64, 26;
    --accent-strong: #8f3315;
    --accent-soft:   rgba(179, 64, 26, 0.10);
    --accent-light:  rgba(179, 64, 26, 0.10);

    /* Loader cosmos — PAPER: warm dusk sky (deep umber), cream/gold starlight + amber comet. */
    --loader-bg:         #3a2c18;
    --loader-text:       #f6ecd6;
    --loader-text-dim:   #cbb38d;
    --loader-accent-rgb: 224, 168, 86;
    --star-rgb:          247, 233, 198;
    --star-alt-rgb:      236, 200, 130;
    --star-glow:         rgba(247, 216, 156, 0.8);
    --star-peak:         1;
    --meteor-tip-rgb:    255, 242, 212;

    --button-bg:   var(--accent);
    --button-text: #fbf3e1;

    --success: #5f7d2a; --success-rgb: 95, 125, 42;
    --warning: #b07415; --warning-rgb: 176, 116, 21;
    --destructive: #a8392a; --destructive-rgb: 168, 57, 42;
    --info:    #2c6c87;     --info-rgb:    44, 108, 135;
    --srs:     #6b3aa6;     --srs-rgb:     107, 58, 166;
    --grammar:     #6b3aa6;     --grammar-rgb:     107, 58, 166;

    /* Categorical highlight palettes (grammar pattern slots + vocab-status underlines).
       Slot hues stay distinguishable per theme: saturated on dark, deepened on light. */
    --grammar-cat-0-rgb: 124, 58, 237;
    --grammar-cat-1-rgb: 194, 88, 14;
    --grammar-cat-2-rgb: 13, 131, 118;
    --grammar-cat-3-rgb: 176, 116, 21;
    --grammar-cat-4-rgb: 190, 24, 93;
    --hl-green-rgb: 22, 140, 62;
    --hl-blue-rgb: 29, 88, 191;
    --mastered-stable: #2c8576; --mastered-stable-rgb: 44, 133, 118;

    --again: #a8392a; --hard: #b07415; --good: #2c6c87; --easy: #5f7d2a;

    --word-new-bg:        rgba(179, 64, 26, 0.14);
    --word-new-line:      var(--accent);
    --word-new-text:      var(--text);
    --word-seen-bg:       rgba(179, 64, 26, 0.07);
    --word-seen-line:     transparent;
    --word-seen-text:     var(--text);
    --word-learning-bg:   transparent;
    --word-learning-line: var(--accent);
    --word-learning-text: var(--text);
    --word-familiar-bg:   transparent;
    --word-familiar-line: rgba(179, 64, 26, 0.4);
    --word-familiar-text: var(--text);
    --word-mastered-bg:   transparent;
    --word-mastered-line: transparent;
    --word-mastered-text: var(--text);

    --hl-ai-bg:     var(--word-new-bg);
    --hl-ai-border: var(--word-new-line);
    --hl-user-bg:   var(--word-learning-bg);
    --hl-user-border: var(--word-learning-line);
    --hl-added:     var(--success);

    --shadow-1: none;
    --shadow-2: none;
    --shadow-3: 0 12px 32px rgba(90, 60, 25, 0.18);
    --shadow:   var(--shadow-2);

    /* Verdict sheet (mobile lesson ≤640) — light theme: success/destructive fill, white ink. */
    --verdict-ok-bg:  var(--success);      --verdict-ok-ink:  #ffffff;
    --verdict-bad-bg: var(--destructive);  --verdict-bad-ink: #ffffff;

    /* Warm sun-wash over the dune + faint ruled manuscript stock. */
    --paper-texture:
        radial-gradient(135% 80% at 50% -12%, rgba(214, 150, 66, 0.13), transparent 62%),
        repeating-linear-gradient(0deg, transparent, transparent 33px, rgba(42, 32, 18, 0.028) 33px, rgba(42, 32, 18, 0.028) 34px);
}

/* ============================================================
   Type utility classes — apply directly to elements.
   Display + title tiers render in the editorial serif (--font-display);
   body / caption / reading set their own family. Caption is tracked +
   uppercase (eyebrow feel) but stays in the UI sans.
   ============================================================ */
.lw-type-display { font-family: var(--font-display); font-size: var(--type-display-size); line-height: var(--type-display-line); font-weight: var(--type-display-weight); letter-spacing: var(--type-display-track); }
.lw-type-title-l { font-family: var(--font-display); font-size: var(--type-title-l-size); line-height: var(--type-title-l-line); font-weight: var(--type-title-l-weight); letter-spacing: var(--type-title-l-track); }
.lw-type-title-m { font-family: var(--font-display); font-size: var(--type-title-m-size); line-height: var(--type-title-m-line); font-weight: var(--type-title-m-weight); letter-spacing: var(--type-title-m-track); }
.lw-type-title-s { font-family: var(--font-display); font-size: var(--type-title-s-size); line-height: var(--type-title-s-line); font-weight: var(--type-title-s-weight); letter-spacing: var(--type-title-s-track); }
.lw-type-body-l  { font-size: var(--type-body-l-size);  line-height: var(--type-body-l-line);  font-weight: var(--type-body-l-weight);  letter-spacing: var(--type-body-l-track); }
.lw-type-body    { font-size: var(--type-body-size);    line-height: var(--type-body-line);    font-weight: var(--type-body-weight);    letter-spacing: var(--type-body-track); }
.lw-type-body-s  { font-size: var(--type-body-s-size);  line-height: var(--type-body-s-line);  font-weight: var(--type-body-s-weight);  letter-spacing: var(--type-body-s-track); }
.lw-type-caption { font-size: var(--type-caption-size); line-height: var(--type-caption-line); font-weight: var(--type-caption-weight); letter-spacing: var(--type-caption-track); text-transform: uppercase; }
.lw-type-reading { font-size: var(--type-reading-size); line-height: var(--type-reading-line); font-weight: var(--type-reading-weight); letter-spacing: var(--type-reading-track); font-family: var(--font-reading); }

/* Числовые дисплеи (цифры в кольцах, счётчики, статистика). */
.lw-type-num-l { font-size: var(--type-num-l-size); line-height: var(--type-num-l-line); }
.lw-type-num-m { font-size: var(--type-num-m-size); line-height: var(--type-num-m-line); }
.lw-type-num-s { font-size: var(--type-num-s-size); line-height: var(--type-num-s-line); }
.lw-type-num-l, .lw-type-num-m, .lw-type-num-s {
    font-weight: 750; letter-spacing: -0.02em; font-variant-numeric: tabular-nums;
}

/* ============================================================
   Content-width containers — center + cap by content tier.
   Add to a page's top-level wrapper. Because these are max-widths,
   phones (viewport < cap) render full-width; the column only centers
   once the screen is wider than the cap. See the --w-* tokens above. */
.lw-container-readable { width: 100%; max-width: var(--w-readable); margin-inline: auto; }
.lw-container-wide     { width: 100%; max-width: var(--w-wide);     margin-inline: auto; }

/* ============================================================
   TYPOGRAPHY DOCTRINE (краткий свод — миграция 2026-07)
   ------------------------------------------------------------
   • Разрешённые размеры UI-текста — ТОЛЬКО тиры шкалы --type-*:
     display 32/38 · title-l 22/28 · title-m 18/24 · title-s 15/21 ·
     body-l 16/26 · body 14/22 · body-s 12/18 · caption 11/16
     (num-l 30/34 · num-m 22/26 · num-s 16/20 для цифр, tabular-nums).
   • Минимум — caption (11px). Ничего текстового меньше; «не влезает» —
     задача компоновки, не кегля.
   • Голый px в font-size допустим только с пометкой «skip(c): …»
     (глиф ×/›/эмодзи в круге или блоке фиксированного размера) — или,
     в плотной геометрии (GrammarMap, WordLookupCard, exercises),
     обёрнутый в calc(Npx * var(--ui-scale, 1)).
   • Инпуты на мобиле ≥ 16px (--type-body-l-size) — иначе iOS auto-zoom.
   • var(--type-*-size) меняет ТОЛЬКО размер; font-family назначают
     классы .lw-type-*, не переменные.
   • Мобильный диапазон крупных тиров (display/title-l −12% при ≤640px)
     живёт здесь, в tokens.css — страницы НЕ заводят своих @media для
     заголовков.
   • Кнопки: НЕ pill (999 — только истинные круги/аватары); радиусы
     кнопок/карточек — --radius / --radius-sm / --radius-lg.
   ============================================================ */
