/* ===========================================================================
   TaniLearn — base.css: what EVERY page needs before first paint.
   ===========================================================================
   Moved out of style.css (Asana: "Cold first visit — trim/split render-blocking
   style.css") so the public pages can stop blocking on the whole 300KB game
   stylesheet. This file is the part they genuinely cannot paint without:

     · the eight @font-face declarations and the box-sizing reset;
     · the Soft Clay token block (:root) that site.css and style.css both
       resolve every colour against;
     · the html/body ground and focus rings;
     · the .brand wordmark, which every header draws above the fold.

   LOAD ORDER IS THE CONTRACT: base.css first, then style.css (app pages) or
   site.css (site pages) — the data-bg overrides in style.css re-declare tokens
   from here and must stay later in the cascade. index.html and the generated
   learn catalogue load style.css DEFERRED (media="print" + js/defer-css.js),
   which is the whole point of the split; every other page keeps it blocking.
   Nothing in here may reference anything outside this file. */

/* ---------------------------------------------------------------------------
   Self-hosted webfonts.
   ---------------------------------------------------------------------------
   Every page used to open with a render-blocking stylesheet from
   fonts.googleapis.com, which then pulled the font files from a second origin,
   fonts.gstatic.com. That is two third-party DNS+TLS handshakes in front of
   first paint on every page — and, on a product used by children, two requests
   carrying the visitor's IP and referrer to somebody else. Baloo 2 and Nunito
   are both SIL OFL licensed, so they are simply served from here instead.

   These are VARIABLE fonts, so ONE file covers every weight the site uses
   rather than one file per weight (`font-weight` below is a RANGE).

   Nunito's italic is a genuinely separate font file. The upright variable font
   has no italic axis, so without nunito-italic.woff2 the browser would fake
   italics by slanting the upright — which is what <em> would have lost had we
   self-hosted only the file already in the repo.

   Two tiers per face, split on unicode-range exactly the way Google subsets, so
   an ordinary page downloads only the small Latin file and the maths/science
   pages pull the extended one on demand. The core tier carries U+20B9 ₹ because
   the pricing table is on the landing page. Core is declared FIRST so it wins
   for codepoints both tiers cover.

   Regenerate with: python scripts/build-fonts.py   (rarely — fonts don't change)
   --------------------------------------------------------------------------- */
@font-face {
  font-family: 'Baloo 2';
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url('../fonts/baloo2.woff2?v=f579e4aca9') 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+2074,U+20AC,U+20B9,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face {
  font-family: 'Baloo 2';
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url('../fonts/baloo2-ext.woff2?v=d2dfe6707c') format('woff2');
  unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0370-03FF,U+0400-04FF,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20B8,U+20BA-20C0,U+2070-209F,U+2113,U+2150-215F,U+2C60-2C7F,U+A720-A7FF;
}
@font-face {
  font-family: 'Nunito';
  font-style: normal;
  font-weight: 200 1000;
  font-display: swap;
  src: url('../fonts/nunito.woff2?v=e27c9a66c1') 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+2074,U+20AC,U+20B9,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face {
  font-family: 'Nunito';
  font-style: normal;
  font-weight: 200 1000;
  font-display: swap;
  src: url('../fonts/nunito-ext.woff2?v=b8160b2de8') format('woff2');
  unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0370-03FF,U+0400-04FF,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20B8,U+20BA-20C0,U+2070-209F,U+2113,U+2150-215F,U+2C60-2C7F,U+A720-A7FF;
}
@font-face {
  font-family: 'Nunito';
  font-style: italic;
  font-weight: 200 1000;
  font-display: swap;
  src: url('../fonts/nunito-italic.woff2?v=695b424850') 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+2074,U+20AC,U+20B9,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face {
  font-family: 'Nunito';
  font-style: italic;
  font-weight: 200 1000;
  font-display: swap;
  src: url('../fonts/nunito-italic-ext.woff2?v=01234bbd3a') format('woff2');
  unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0370-03FF,U+0400-04FF,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20B8,U+20BA-20C0,U+2070-209F,U+2113,U+2150-215F,U+2C60-2C7F,U+A720-A7FF;
}

/* ---------------------------------------------------------------------------
   COUNTRY FLAGS — a third tier, for the codepoints Windows will not draw.
   ---------------------------------------------------------------------------
   🇮🇳 🇺🇸 🇬🇧 are REGIONAL-INDICATOR PAIRS (U+1F1E6-1F1FF), not ordinary emoji, and
   Segoe UI Emoji contains no glyph for a single one of them: Chromium on Windows
   falls back to drawing the two LETTERS. A child on a Windows laptop opened the
   GK flag lesson and read "US Our flag" and "…we call it the Union Jack. GB"
   where the flag was meant to be. Chromium has declined to fix it until Windows
   does (crbug 1209677), so the only thing that works everywhere is to ship the
   glyphs ourselves. Asana: "Flag is not shown for GK lear 5 to 6 years".

   NAMED 'Nunito' AND 'Baloo 2' ON PURPOSE — this is the trick that makes it a
   two-block change instead of a 250-rule one. @font-face EXTENDS a family over a
   unicode-range rather than replacing it, which is already how the core/ext tiers
   above work. Declaring the flag file under the two names the site already asks
   for means every one of the ~250 `font-family: "Nunito"` rules gains flags, and
   not one of them has to be edited. Nothing else about those families changes:
   the range below and the Latin ranges above do not overlap at any codepoint.

   COSTS NOTHING ON A PAGE WITH NO FLAGS. A browser only fetches a @font-face
   whose unicode-range matches a character it is actually laying out, so the 78KB
   is paid on the GK lessons that show flags and nowhere else, then cached for a
   year by the path rule in public/fonts/.htaccess.

   THE TRADE-OFF, stated rather than hidden: this wins on EVERY platform, not only
   the broken one, so an iPhone shows Twemoji flags instead of Apple's. That is
   deliberate — the alternative is a script that feature-tests and injects the
   font afterwards, which means the child watches "GB" repaint into a flag, and on
   a lesson whose subject IS the flag that is worse than a consistent house style.

   THE FONT is Twemoji Country Flags, a pyftsubset of Mozilla's twemoji-colr build
   of Twemoji (github.com/talkjs/country-flag-emoji-polyfill). Twemoji artwork is
   CC-BY 4.0, © Twitter/X — attribution is in README.md beside the OFL note for
   the two families above. Vendored, not generated: scripts/build-fonts.py does
   not produce this one.
   --------------------------------------------------------------------------- */
@font-face {
  font-family: 'Nunito';
  font-style: normal;
  /* The whole axis the site asks for, so the flag face is selected whatever
     weight the surrounding text is set in. Twemoji has one weight; a range here
     is a match rule, not a promise of eight cuts. */
  font-weight: 100 1000;
  font-display: swap;
  src: url('../fonts/twemoji-flags.woff2?v=9f04f14429') format('woff2');
  unicode-range: U+1F1E6-1F1FF,U+1F3F4,U+E0062-E0063,U+E0065,U+E0067,U+E006C,U+E006E,U+E0073-E0074,U+E0077,U+E007F;
}
@font-face {
  font-family: 'Baloo 2';
  font-style: normal;
  font-weight: 100 1000;
  font-display: swap;
  src: url('../fonts/twemoji-flags.woff2?v=9f04f14429') format('woff2');
  unicode-range: U+1F1E6-1F1FF,U+1F3F4,U+E0062-E0063,U+E0065,U+E0067,U+E006C,U+E006E,U+E0073-E0074,U+E0077,U+E007F;
}

* { box-sizing: border-box; margin: 0; }

/* ===========================================================================
   SOFT CLAY — the design system, v2.
   ===========================================================================
   Every colour, radius and shadow in the app resolves here, so a screen never
   invents one of its own. See tanilearn-docs/design/V2/DESIGN-SPEC.md §1; the
   table there is the contract and these are its variables.

   WHY THE OLD NAMES SURVIVED. The V1 rebrand put `--tl-*` in front of roughly
   two thousand declarations across style.css and site.css, and the aliases
   `--ink` / `--accent` / `--card-shadow` in front of the thousand before that.
   Renaming them to `--clay-*` would have been a rename with no design in it: the
   same rule, the same place, a different spelling. So the names stay and the
   VALUES move onto Soft Clay, which is what makes this a re-skin the whole app
   picks up rather than a file-by-file rewrite.

   The one rule to keep: a new colour goes HERE first. `#FAF3E4` typed into a
   component is how a design system dies.
   ---------------------------------------------------------------------------
   THE THREE SHADOW RECIPES (spec §1, radii & elevation) are `--clay-raised`,
   `--clay-inset` and the extruded button, which is a ledge colour plus a drop —
   written per component because the ledge is the component's own colour. */
:root {
  /* ---- surfaces --------------------------------------------------------- */
  /* LIGHTENED (Asana: "…Make background colour much lighter than the current
     one"), together with the character wash going from five of the cast to all
     ten. The two are one change: ten translucent characters on the old cream read
     as a busy surface, and the answer to that is a paler ground rather than a
     fainter cast — the point of the wash is that a child can see who is standing
     behind their screen.
     Every stop is the old colour taken 55% of the way to white, so the RAMP is
     untouched: surface is still a shade above bg, surface-b still a shade under
     bg-2, and the well is still the darkest of them. Nothing that reads as
     elevation had to be re-tuned.
     --tl-canvas DOES NOT MOVE. It is the desk, it shows only as a margin above
     1100px, and it is the colour ~90 pages and the manifest carry as
     `theme-color` for the browser chrome — moving it means moving all of those,
     which is a rename of the phone's status bar, not a lighter page. */
  --tl-canvas: #E7DAC2;      /* the desk the screens sit on */
  --tl-bg: #FCFAF4;          /* screen surface, top stop — the "cream" */
  --tl-bg-2: #F9F3EA;        /* screen surface, bottom stop */
  --tl-surface: #FEFCF7;     /* panel top stop (cards, modals, menus) */
  --tl-surface-b: #F8F2E7;   /* panel bottom stop */
  --tl-surface-2: #F6EFE2;   /* tinted chips, counters, quiet badges */
  --tl-well: #F1E9D8;        /* inset well (segmented controls, tracks) */
  --tl-screen: linear-gradient(180deg, var(--tl-bg), var(--tl-bg-2));
  --tl-panel: linear-gradient(180deg, var(--tl-surface), var(--tl-surface-b));

  /* ---- ink -------------------------------------------------------------- */
  --tl-ink: #4E4636;         /* primary text */
  --tl-ink-soft: #6B5F48;    /* secondary text */
  /* THE CAPTION PAIR, DARKENED APP-WIDE TO CLEAR WCAG AA.
     style.css already said this was coming: the four mesh grounds shifted this
     pair to #5E5340/#6B5F49 and the note there ends "the other ten grounds are
     untouched — widening it is a design decision, not a bug fix, and belongs in
     its own ticket." This is that ticket. The old pair measured 3.75:1 on the
     default cream and 2.64:1 on the daily-card tint — under AA for body text on
     the app's own home screen. The mesh-ground overrides are now the same value
     as the default and are left in place; they document the reasoning. */
  --tl-muted: #5E5340;       /* captions, hints */
  --tl-muted-2: #6B5F49;     /* the quieter half of the caption pair */
  --tl-head: #544B38;        /* display headings on body screens */
  --tl-head-warm: #8A6B3E;   /* display headings on celebration/trophy screens */
  --tl-navy: #3E4A66;        /* the dark chip: caret keycap, Play mode, Math CTA */
  --tl-navy-edge: #252E42;

  /* ---- accents ---------------------------------------------------------- */
  --tl-orange: #E8862E;      /* brand "Tani" */
  /* THE CTA GRADIENT, DARKENED TO WCAG AA.
     These carry white button labels and both stops failed: the top stop measured
     2.01:1 on "Start free — no card needed", which was the lowest figure left
     anywhere in the product once the subject cards were fixed, and it was on the
     primary conversion button. site.css line ~927 had listed these as a
     deliberate exception on the grounds that the big CTAs are "large and
     decorative"; at 17px they are neither (WCAG's large-text threshold is
     18.66px bold) and they are the main call to action.
     Same treatment as the seven subject palettes: hue and saturation untouched,
     ONE lightness factor (x0.58) across the pair so the top-lighter/bottom-darker
     direction and the spread survive. The ledge follows the pair so the button
     keeps its depth.
       --tl-orange-lt  2.01:1 -> 4.61:1      --tl-orange-dk  2.89:1 -> 7.14:1  */
  --tl-orange-dk: #84490C;   /* CTA gradient, bottom stop */
  --tl-orange-lt: #AB6408;   /* CTA gradient, top stop */
  --tl-orange-shadow: #613109; /* CTA ledge */
  /* THE BRIGHT ORANGE, KEPT — for chips that carry DARK ink on it.
     The pair above went dark because white sits on it. These are the opposite
     arrangement (#2A1A06 on orange: 8.4:1 at the top stop, 5.8:1 at the bottom),
     so darkening would have broken them rather than fixed them — the parent-zone
     tag, the selected Parent Zone tab, the learn-page tag and the shop's "get"
     chip were all reading --tl-orange-dk for their bottom stop and would have
     fallen to 2.35:1. They have their own token now so the two cannot be
     confused again: bright is for dark ink, the pair above is for white. */
  --tl-orange-chip-lt: #F6A63E;
  --tl-orange-chip-dk: #E37E14;
  --tl-orange-tint: #FFF3CF;
  --tl-orange-text: #8C5918;  /* 4.6:1 on the daily-card cream, 5.9:1 on white */
  --tl-teal: #3BA278;        /* brand "Learn" — secondary accent */
  --tl-teal-tint: #E4F2EA;
  --tl-teal-text: #2B724F;   /* success / correct — 4.6:1 on the palest daily-card tint */
  --tl-danger: #B7493C;      /* wrong answers, log out — 4.7:1 on the menu ground */
  --tl-danger-tint: #F5DFD2;
  --tl-timer: #3D6EB4;       /* countdown numerals */
  --tl-gold: #E8A93A;        /* stars, plaques, highlights */
  --tl-gold-lt: #F6E1A6;
  --tl-purple: #7357D2;      /* LEVEL UP / Game CTA */
  --tl-purple-lt: #9D8BE8;
  --tl-purple-edge: #4A3595;
  --tl-brown: #7A4F22;       /* Star Shop CTA */
  --tl-brown-lt: #9C6F3E;
  --tl-brown-edge: #4E3010;

  /* ---- clay ------------------------------------------------------------- */
  --tl-ledge: #D5C29D;       /* the cream ledge every pale control stands on */
  --tl-ledge-dk: #CBB48D;
  --tl-hairline: #E2D3B4;
  --clay-raised: inset 0 2px 0 #fff, inset 0 -3px 5px rgba(166, 124, 62, .14),
    0 4px 9px rgba(120, 85, 40, .18);
  --clay-inset: inset 0 3px 6px rgba(120, 85, 40, .22), inset 0 -1px 0 rgba(255, 255, 255, .7);
  --clay-lift: 0 26px 50px rgba(110, 75, 30, .18), 0 4px 10px rgba(110, 75, 30, .1);
  --clay-drop: 0 8px 12px rgba(120, 85, 40, .2);

  /* THE GAP THE SCREEN SLAB LEAVES AROUND ITSELF. Zero until there is room for
     it (see the 1100px block below `.screen-page`), and on :root rather than on
     the slab because `body` has to read it too — anything that pins a screen to
     exactly one viewport has to account for the margin, and a variable scoped to
     the slab is invisible to its own parent. */
  --screen-inset: 0px;

  /* ---- radii ------------------------------------------------------------ */
  --r-screen: 30px;
  --r-panel: 22px;
  --r-card: 24px;
  --r-key: 14px;
  --r-chip: 9px;

  /* legacy aliases remapped onto the new palette */
  --ink: #4E4636;
  --ink-soft: #6B5F48;
  --faint: #5E5340;          /* = --tl-muted; darkened with it, see the note above */
  --card-shadow: rgba(120, 85, 40, .18);
  --accent: #E8862E;
  --accent-dk: #E37E14;
  --accent-shadow: #A85410;
}

html, body { height: 100%; }
body {
  background: var(--tl-canvas);
  color: var(--ink);
  font-family: "Nunito", system-ui, sans-serif;
  min-height: 100vh;
}
button { font-family: inherit; color: inherit; border: none; background: none; cursor: pointer; }
button:focus-visible, input:focus-visible { outline: 3px solid var(--tl-gold); outline-offset: 2px; }
a:focus-visible { outline: 3px solid var(--tl-gold); outline-offset: 2px; }
/* Cards that behave as buttons (T.a11yBtn in js/util.js) are <div>s, so they
   need the same focus ring — a tab stop nobody can see is not much use. */
[role="button"]:focus-visible { outline: 3px solid var(--tl-gold); outline-offset: 2px; }

/* ---------- TaniLearn logo (Tani in flight + wordmark) ----------
   SIZED BY HEIGHT, width auto. The mark used to be a square inline SVG of a tiger
   HEAD, so every rule set both dimensions to the same number. It is now the
   delivered artwork — Tani full-body, mid-flight — and hard-coding a width per
   breakpoint would mean recomputing seven numbers against the aspect ratio, and
   getting them wrong again the day the art is recropped. Each rule below
   therefore sets only the height.

   Which is the whole reason the second delivery cost nothing: the art went from
   1.37:1 landscape to 0.88:1 PORTRAIT (Tani gained a crown), and not one of these
   rules had to change. Only the <img> attributes below did.

   The <img> carries width/height attributes (384x437, its real pixels) so the
   browser knows the ratio before the file arrives. Without them `width: auto`
   measures zero until it loads and all 87 page headers jump sideways when it
   does. Built by scripts/build-logo.py. */
.brand { display: inline-flex; align-items: center; gap: 10px; }
.brand-mark {
  width: auto; height: 40px; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
}
.brand-mark img { height: 100%; width: auto; display: block; }
.brand-word {
  font-family: "Baloo 2", system-ui, sans-serif;
  font-weight: 800; font-size: 22px; letter-spacing: .01em; line-height: 1;
  color: var(--tl-orange);
}
/* "Tani" is a rainbow gradient (clipped to the text); "Learn" stays solid. */
.brand-word span {
  background: linear-gradient(95deg, #FF5A5F 0%, #F0862E 24%, #FFC53D 42%, var(--tl-teal-text) 60%, #2FA3A0 76%, var(--tl-purple) 100%);
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent; color: transparent;
}
.brand-word b { color: var(--ink); font-weight: 800; }
/* On navy backgrounds "Learn" flips to cream, "Tani" stays orange. */
.brand.on-dark .brand-word b,
.brand-word.on-dark b { color: var(--tl-bg); }
.brand.big { flex-direction: column; gap: 16px; }
.brand.big .brand-mark { width: auto; height: 92px; }
.brand.big .brand-word { font-size: clamp(36px, 7vw, 54px); }
