/*
  English Learning OS — design tokens.

  THIS IS THE ONLY FILE IN THE PROJECT THAT MAY CONTAIN A LITERAL COLOUR.

  Every other stylesheet reads var(--lx-*). _verify/checks/06 greps the other
  sheets for hex codes, rgb(), hsl() and named colours and fails the build if
  it finds one. That rule is what makes dark mode a re-declaration of the same
  token names rather than a parallel stylesheet, and it is the difference
  between a theme that works and a theme where one card stays white.

  A warning inherited from the reference sheet, and worth repeating because it
  cost a live site its layout: never write an asterisk immediately followed by
  a slash inside one of these comments. It terminates the comment early, the
  parser swallows the block that follows, and every var() in the file resolves
  to nothing — silently, with colours still working wherever a literal was used
  and failing wherever a token was.

  Structure:
    1. Primitives   — the raw ramps. Referenced only by section 2.
    2. Semantic     — what the UI actually uses.
    3. Dark values  — every dark colour, defined ONCE. See the note at part 4.
    4. Scales       — spacing, radius, shadow, type, motion. Theme-independent.


  THE PALETTE IS WARM ACADEMIC, AND EVERY VALUE WAS MEASURED.

  Parchment carries the surfaces, ink-navy carries the brand. The point is a
  screen that reads like a well-set book rather than a dashboard: a cream page
  instead of a blue-grey one, and a near-black navy for the brand instead of a
  bright accent that fights the text for attention.

  Contrast was computed, not judged, against all three light surfaces
  (--lx-surface, --lx-bg, --lx-surface-3). Three candidate values failed AA and
  were replaced rather than shipped:

    --lx-text-3        #7D7263 → #746A5B   (4.44 → 5.01)
    --lx-sk-listening  #1B7A8C → #176B7A   (4.36 → 5.36 on surface-3)
    --lx-sk-writing    #3F7D3F → #377037   (4.35 → 5.19 on surface-3)

  --lx-text-3 is the one that mattered: the comment below calls it the floor
  for anything a person must read, and it was under the floor.


  BREAKPOINTS — three tiers, px only, and this comment is the whole spec.

      sm    640px    phone / tablet. Brand text and headline chips drop.
      md    900px    desktop nav ↔ phone tab bar. Two-column page layouts.
      lg   1200px    wide layouts; the admin sidebar rail appears.

  They cannot be tokens: @media cannot read a custom property, and there is no
  workaround worth the indirection. So they are written literally at each site
  and this list is what keeps them agreeing.

  There used to be eight values across four sheets in two different units, and
  the one that mattered was practice.css responding at 40rem while the shell
  around it responded at 860px and 640px. At the default root size those are
  the same number, so it looked fine — and it came apart for anyone who had
  ever changed their browser's font size, which is exactly the reader most
  likely to have done so. Mixing rem and px breakpoints in one layout is the
  bug; the tier count is only tidiness.
*/

/* ── Typefaces ───────────────────────────────────────────────────
   Self-hosted, because they have to be: inc/bootstrap.php sets
   `font-src 'self'`, so a Google Fonts link is not slow — it is blocked. The
   stacks below used to name Heebo and Inter with nothing loading them, which
   meant the typeface was whatever the visitor happened to have installed.

   Three files, ~90 KB total, and all three are VARIABLE fonts — verified by
   reading the woff2 table directory for an `fvar` table, not assumed from the
   fact that Google served one URL for several weights. That is why each face
   is declared once with a `font-weight` RANGE instead of once per weight: the
   400/500/700 the UI uses are three instances of one file, so asking for a
   weight the design did not plan for still renders drawn glyphs rather than a
   synthetic smear.

   The unicode-range split is what keeps it honest. A screen with no Latin on
   it never downloads inter-latin.woff2 at all, and heebo-hebrew.woff2 is
   12 KB — the file that renders the entire interface.

   font-display: swap: text paints immediately in the fallback and reflows
   when the face arrives. The alternative is a blank screen on a slow
   connection, which is worse on a page whose whole content is words.

   _verify/checks/05 asserts these three files exist on disk, so a deploy that
   forgets assets/fonts/ fails locally instead of silently falling back. */

@font-face {
  font-family: 'Heebo';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('../fonts/heebo-hebrew.woff2') format('woff2');
  unicode-range: U+0307-0308, U+0590-05FF, U+200C-2010, U+20AA, U+25CC, U+FB1D-FB4F;
}

@font-face {
  font-family: 'Heebo';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('../fonts/heebo-latin.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: 'Inter';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('../fonts/inter-latin.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;
}


:root {

  /* ── 1. Primitives ─────────────────────────────────────────────
     Two neutral-to-brand families do the structural work. Parchment is the
     warm neutral behind every surface and every hairline; ink is the navy the
     brand is built from. The feedback hues below them are deliberately muted
     rather than pure — a saturated red next to cream reads as an alarm, and
     most of the time the message is "not quite", not "danger". */

  --lx-parch-0:   #FFFFFF;
  --lx-parch-50:  #FAF8F5;
  --lx-parch-100: #F3EFE9;
  --lx-parch-200: #E7E1D8;
  --lx-parch-300: #D5CCBF;
  --lx-parch-400: #A99E8D;
  --lx-parch-500: #746A5B;
  --lx-parch-600: #5C5347;
  --lx-parch-700: #433D34;
  --lx-parch-800: #2C2822;
  --lx-parch-900: #1E1B17;
  --lx-parch-950: #14110E;

  --lx-ink-50:  #EEF3F8;
  --lx-ink-100: #DCE6F0;
  --lx-ink-200: #B9CCDE;
  --lx-ink-300: #8AB4DC;
  --lx-ink-400: #5B80A6;
  --lx-ink-500: #345C87;
  --lx-ink-600: #1F4468;
  --lx-ink-700: #17324D;
  --lx-ink-800: #102539;
  --lx-ink-900: #0A1826;

  --lx-teal-100: #CCE9E5;
  --lx-teal-300: #6FC4BB;
  --lx-teal-600: #12857C;
  --lx-teal-700: #0F766E;
  --lx-teal-900: #0A4A45;

  --lx-garnet-100: #FBDDD9;
  --lx-garnet-300: #EE9A93;
  --lx-garnet-600: #C42A1E;
  --lx-garnet-700: #B42318;
  --lx-garnet-900: #6E1610;

  --lx-ochre-100: #FCEBD8;
  --lx-ochre-300: #F0BC7C;
  --lx-ochre-600: #C85309;
  --lx-ochre-700: #B54708;
  --lx-ochre-900: #6E2B05;

  --lx-lapis-100: #DBE7FB;
  --lx-lapis-300: #90B4F2;
  --lx-lapis-600: #1A66E0;
  --lx-lapis-700: #175CD3;
  --lx-lapis-900: #0E3780;

  --lx-amber-100: #FCEFCF;
  --lx-amber-300: #F0CE7A;
  --lx-amber-500: #D99E12;
  --lx-amber-600: #B57E0A;

  --lx-terra-300: #F0A57C;
  --lx-terra-500: #C2410C;


  /* ── 2. Semantic (light) ───────────────────────────────────── */

  /* Surfaces, from furthest back to nearest front. */
  --lx-bg:          var(--lx-parch-50);
  --lx-bg-subtle:   var(--lx-parch-100);
  --lx-surface:     var(--lx-parch-0);
  --lx-surface-2:   var(--lx-parch-50);
  --lx-surface-3:   var(--lx-parch-100);
  --lx-overlay:     rgba(30, 27, 23, .45);

  /* Text. --lx-text-3 is the floor for anything a person must read; anything
     lighter is decoration and must not carry meaning on its own. */
  --lx-text:        var(--lx-parch-900);
  --lx-text-2:      var(--lx-parch-600);
  --lx-text-3:      var(--lx-parch-500);
  --lx-text-invert: var(--lx-parch-0);

  --lx-border:      var(--lx-parch-200);
  --lx-border-2:    var(--lx-parch-300);

  /* Brand. --lx-on-primary exists so a button never has to guess whether its
     label should be white or dark once the primary changes. */
  --lx-primary:       var(--lx-ink-700);
  --lx-primary-hover: var(--lx-ink-800);
  --lx-primary-soft:  var(--lx-ink-50);
  --lx-primary-ring:  rgba(23, 50, 77, .26);
  --lx-on-primary:    #FFFFFF;

  /* Feedback. Every state gets a fill, a border, a text colour and a solid,
     because a correct-answer banner and a correct-answer chip need different
     contrast against different backgrounds. */
  --lx-ok:          var(--lx-teal-700);
  --lx-ok-soft:     var(--lx-teal-100);
  --lx-ok-border:   var(--lx-teal-300);
  --lx-ok-text:     var(--lx-teal-900);

  --lx-bad:         var(--lx-garnet-700);
  --lx-bad-soft:    var(--lx-garnet-100);
  --lx-bad-border:  var(--lx-garnet-300);
  --lx-bad-text:    var(--lx-garnet-900);

  --lx-warn:        var(--lx-ochre-700);
  --lx-warn-soft:   var(--lx-ochre-100);
  --lx-warn-border: var(--lx-ochre-300);
  --lx-warn-text:   var(--lx-ochre-900);

  --lx-info:        var(--lx-lapis-700);
  --lx-info-soft:   var(--lx-lapis-100);
  --lx-info-border: var(--lx-lapis-300);
  --lx-info-text:   var(--lx-lapis-900);

  /* Reward. Distinct from --lx-ok on purpose: "you earned something" and
     "you were right" are different messages and must not share a colour. */
  --lx-xp:          var(--lx-amber-500);
  --lx-streak:      var(--lx-terra-500);

  /* Text ON the brand gradient (the Continue card). It sits on ink in both
     themes, so unlike everything else here it does NOT get a dark-mode
     variant — the surface underneath it never changes. */
  --lx-on-brand:     #FFFFFF;
  --lx-on-brand-dim: rgba(255, 255, 255, .78);

  /* Text on a saturated accent chip — the CEFR pill, whose background is a
     --lx-lv-* colour in both themes. Fixed dark ink for the same reason
     --lx-on-brand is fixed white: what it sits on does not flip. */
  --lx-on-accent:   var(--lx-parch-900);

  /* The toast is a dark slab in both themes. It is an interruption, and an
     interruption that matched the page would not read as one. */
  --lx-toast-bg:    var(--lx-parch-800);
  --lx-toast-fg:    #F2EDE5;

  --lx-disabled-bg:    var(--lx-parch-100);
  --lx-text-disabled:  var(--lx-parch-400);

  /* Skill accents. Fixed per skill so a bar, a ring and a legend entry for
     Listening are the same colour on every screen without being passed one.
     Seven hues spread far enough apart to be told apart at chip size, all at
     one chroma so they read as a family rather than a paint box. */
  --lx-sk-vocabulary:    #2E5EAA;
  --lx-sk-grammar:       #6B4FA0;
  --lx-sk-reading:       #8A5A2B;
  --lx-sk-listening:     #176B7A;
  --lx-sk-speaking:      #C2410C;
  --lx-sk-writing:       #377037;
  --lx-sk-pronunciation: #A83246;

  /* CEFR band colours, cool to warm as the level climbs.
     These are pill BACKGROUNDS carrying --lx-on-accent text, so they are
     light by construction and every one clears 7:1 against that ink. */
  --lx-lv-a0: #B0A697;
  --lx-lv-a1: #7FC4E8;
  --lx-lv-a2: #6FCFC4;
  --lx-lv-b1: #83CC89;
  --lx-lv-b2: #BCD173;
  --lx-lv-c1: #EBBE6B;
  --lx-lv-c2: #EE9A93;

  /* Vocabulary states. Used as bar fills and border accents, never as text,
     so the bar to clear is 3:1 against the surface behind them. */
  --lx-vs-new:      #8F8474;
  --lx-vs-learning: #2E86AB;
  --lx-vs-weak:     var(--lx-garnet-700);
  --lx-vs-mastered: var(--lx-teal-700);

  /* Chart ink, for the inline SVG the dashboard draws. */
  --lx-chart-grid: var(--lx-parch-200);
  --lx-chart-axis: var(--lx-parch-400);
  --lx-chart-fill: rgba(23, 50, 77, .12);
  --lx-chart-line: var(--lx-ink-600);

  /* Heatmap ramp, five steps of activity. */
  --lx-heat-0: var(--lx-parch-100);
  --lx-heat-1: #C7D8E8;
  --lx-heat-2: #93B4D0;
  --lx-heat-3: #4F80AC;
  --lx-heat-4: var(--lx-ink-700);

  /* Warm shadow hue: a neutral-grey shadow over cream reads as dirt. */
  --lx-shadow-color: 30 25% 12%;
  --lx-shadow-strength: 1%;


  /* ── 3. Dark values, defined once ───────────────────────────────
     Every colour the dark theme uses lives here and NOWHERE else. The two
     blocks at the bottom of this file contain nothing but aliases pointing
     back at these names.

     The blocks have to stay two — the note above them explains why, and it is
     still correct. What used to be wrong is that they were two full copies of
     sixty values, so every dark tweak was two edits and nothing checked that
     the second one happened. Now they are two copies of an alias list that
     never changes, and changing a dark colour is one edit here.

     _verify/checks/14 asserts the two blocks stay identical.

     Two decisions worth stating, because both look like mistakes:

     - The surfaces are not pure black, and they are not blue-black either.
       They are the same warm hue as the light theme with the lightness
       inverted, so switching themes changes the light in the room rather than
       the room. A #000 page under a bright card is a strobe on every scroll.

     - --lx-primary gets LIGHTER in dark mode, not darker. Ink-700 on a
       near-black surface is close to unreadable; ink-300 holds its contrast
       ratio and stays recognisably the same hue. */

  --lx-dk-bg:          #14110E;
  --lx-dk-bg-subtle:   #1A1713;
  --lx-dk-surface:     #201C17;
  --lx-dk-surface-2:   #2A241E;
  --lx-dk-surface-3:   #362F27;
  --lx-dk-overlay:     rgba(10, 8, 6, .68);

  --lx-dk-text:        #F2EDE5;
  --lx-dk-text-2:      #C4B9AA;
  --lx-dk-text-3:      #9E9384;
  --lx-dk-text-invert: #1E1B17;

  --lx-dk-border:      #363029;
  --lx-dk-border-2:    #4A4238;

  --lx-dk-primary:       #8AB4DC;
  --lx-dk-primary-hover: #B9CCDE;
  --lx-dk-primary-soft:  rgba(138, 180, 220, .16);
  --lx-dk-primary-ring:  rgba(138, 180, 220, .38);
  --lx-dk-on-primary:    #14110E;

  --lx-dk-ok:          #6FC4BB;
  --lx-dk-ok-soft:     rgba(15, 118, 110, .20);
  --lx-dk-ok-border:   rgba(111, 196, 187, .42);
  --lx-dk-ok-text:     #8FD6CE;

  --lx-dk-bad:         #EE9A93;
  --lx-dk-bad-soft:    rgba(180, 35, 24, .22);
  --lx-dk-bad-border:  rgba(238, 154, 147, .42);
  --lx-dk-bad-text:    #F3B4AE;

  --lx-dk-warn:        #F0BC7C;
  --lx-dk-warn-soft:   rgba(181, 71, 8, .22);
  --lx-dk-warn-border: rgba(240, 188, 124, .42);
  --lx-dk-warn-text:   #F4CE9C;

  --lx-dk-info:        #90B4F2;
  --lx-dk-info-soft:   rgba(23, 92, 211, .22);
  --lx-dk-info-border: rgba(144, 180, 242, .42);
  --lx-dk-info-text:   #AFC9F6;

  --lx-dk-xp:          #F0CE7A;
  --lx-dk-streak:      #F0A57C;

  --lx-dk-disabled-bg:   #2A241E;
  --lx-dk-text-disabled: #6B6255;

  --lx-dk-sk-vocabulary:    #7FA8DC;
  --lx-dk-sk-grammar:       #A98FD4;
  --lx-dk-sk-reading:       #C99A62;
  --lx-dk-sk-listening:     #5FB3C4;
  --lx-dk-sk-speaking:      #EE9159;
  --lx-dk-sk-writing:       #79BC79;
  --lx-dk-sk-pronunciation: #DE8595;

  /* The CEFR pills and the vocabulary-state accents had NO dark values at all
     until now — eleven fixed saturated colours sitting on a dark card. The
     level pills are lifted so the fixed dark ink on them still clears AA; the
     state accents are lifted so they clear 3:1 against --lx-dk-surface. */
  --lx-dk-lv-a0: #C6BCAD;
  --lx-dk-lv-a1: #A3D8F0;
  --lx-dk-lv-a2: #96DED5;
  --lx-dk-lv-b1: #A5DCA9;
  --lx-dk-lv-b2: #D0DE97;
  --lx-dk-lv-c1: #F2D194;
  --lx-dk-lv-c2: #F4B7B1;

  --lx-dk-vs-new:      #8A8071;
  --lx-dk-vs-learning: #7FC4E8;
  --lx-dk-vs-weak:     #F0958E;
  --lx-dk-vs-mastered: #5FCFC2;

  --lx-dk-chart-grid: #363029;
  --lx-dk-chart-axis: #6B6255;
  --lx-dk-chart-fill: rgba(138, 180, 220, .18);
  --lx-dk-chart-line: #8AB4DC;

  --lx-dk-heat-0: #2A241E;
  --lx-dk-heat-1: #1F4468;
  --lx-dk-heat-2: #345C87;
  --lx-dk-heat-3: #5B80A6;
  --lx-dk-heat-4: #8AB4DC;

  --lx-dk-shadow-color: 30 40% 2%;
  --lx-dk-shadow-strength: 20%;


  /* ── 4. Scales — theme independent ─────────────────────────── */

  --lx-sp-1: .25rem;
  --lx-sp-2: .5rem;
  --lx-sp-3: .75rem;
  --lx-sp-4: 1rem;
  --lx-sp-5: 1.5rem;
  --lx-sp-6: 2rem;
  --lx-sp-7: 3rem;
  --lx-sp-8: 4rem;

  --lx-r-xs:   .25rem;
  --lx-r-sm:   .375rem;
  --lx-r-md:   .625rem;
  --lx-r-lg:   .875rem;
  --lx-r-xl:   1.25rem;
  --lx-r-2xl:  1.75rem;
  --lx-r-pill: 999px;

  /* Border widths and the focus ring, which were literal 1px / 2px / 4px in
     four sheets. The ring in particular has to be one value: a focus outline
     that changes thickness between a button and an input reads as a bug in
     the browser rather than a decision. */
  --lx-bw-hair:   1px;
  --lx-bw-thick:  2px;
  --lx-bw-accent: 3px;
  --lx-ring-w:      3px;
  --lx-ring-offset: 2px;

  --lx-ico-sm: 16px;
  --lx-ico-md: 20px;
  --lx-ico-lg: 24px;

  --lx-fw-normal: 400;
  --lx-fw-medium: 500;
  --lx-fw-semi:   600;
  --lx-fw-bold:   700;
  --lx-fw-black:  800;

  --lx-track-caps:  .06em;
  --lx-track-tight: -.02em;

  --lx-shadow-sm:
    0 1px 2px -1px hsl(var(--lx-shadow-color) / calc(var(--lx-shadow-strength) + 9%));
  --lx-shadow-md:
    0 3px 6px -3px hsl(var(--lx-shadow-color) / calc(var(--lx-shadow-strength) + 10%)),
    0 8px 16px -6px hsl(var(--lx-shadow-color) / calc(var(--lx-shadow-strength) + 8%));
  --lx-shadow-lg:
    0 6px 12px -6px hsl(var(--lx-shadow-color) / calc(var(--lx-shadow-strength) + 12%)),
    0 20px 40px -12px hsl(var(--lx-shadow-color) / calc(var(--lx-shadow-strength) + 12%));

  /* Heebo first because it draws Hebrew properly and covers Latin acceptably.
     assets/fonts/ self-hosts it — font-src is 'self', so a CDN is not an
     option and the stack behind it is only a fallback. */
  --lx-font: 'Heebo', system-ui, -apple-system, 'Segoe UI', 'Noto Sans Hebrew', Arial, sans-serif;

  /* A separate stack for English content. It is not decoration: mixing Hebrew
     and English inside one line looks noticeably better when the Latin run has
     its own metrics rather than inheriting a Hebrew-first face's fallback. */
  --lx-font-en: 'Inter', system-ui, -apple-system, 'Segoe UI', Arial, sans-serif;
  --lx-font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  /* The body size is 16px, not the 15px it used to be. Hebrew carries less
     of its letterform above the x-height than Latin does, so it loses
     legibility a step earlier — and this is a screen someone reads for twenty
     minutes at a time. The admin drops back to --lx-fs-sm as its base, which
     is a deliberate density choice for a screen full of rows. */
  --lx-fs-xs:  .8125rem;
  --lx-fs-sm:  .875rem;
  --lx-fs-md:  1rem;
  --lx-fs-lg:  1.125rem;
  --lx-fs-xl:  1.375rem;
  --lx-fs-2xl: 1.75rem;
  --lx-fs-3xl: 2.25rem;
  --lx-fs-4xl: 2.75rem;

  --lx-lh-tight:   1.25;
  --lx-lh-heading: 1.2;
  --lx-lh-body:    1.65;

  --lx-w-content: 1240px;
  --lx-w-admin:   1440px;
  --lx-w-narrow:  560px;
  --lx-w-reader:  700px;

  --lx-t-fast: 120ms;
  --lx-t-mid:  220ms;
  --lx-t-slow: 380ms;
  --lx-ease:   cubic-bezier(.4, 0, .2, 1);
  --lx-ease-out: cubic-bezier(.16, 1, .3, 1);

  /* Progress percentage, 0..100 and unitless.
     Declared here with a zero default so a bar whose inline value is missing
     renders empty rather than losing its width declaration entirely — and so
     the "every var() is defined" check has something to find. Every real value
     is written inline from PHP as style="--lx-pct:64". */
  --lx-pct: 0;

  --lx-z-sticky:  100;
  --lx-z-overlay: 900;
  --lx-z-modal:   1000;
  --lx-z-toast:   1100;

  color-scheme: light;
}


/* ── Dark ────────────────────────────────────────────────────────
   Aliases only. Every value is in part 3 above.

   THE BLOCK BELOW AND THE ONE INSIDE THE MEDIA QUERY MUST STAY IDENTICAL.
   _verify/checks/14 fails if they diverge. They are deliberately two rules
   rather than one selector list, because :root[data-theme="dark"] must keep
   winning over the media query when a user has explicitly chosen light on a
   dark OS — a selector list would put them at the same specificity and let
   source order decide. */

:root[data-theme="dark"] {
  --lx-bg:          var(--lx-dk-bg);
  --lx-bg-subtle:   var(--lx-dk-bg-subtle);
  --lx-surface:     var(--lx-dk-surface);
  --lx-surface-2:   var(--lx-dk-surface-2);
  --lx-surface-3:   var(--lx-dk-surface-3);
  --lx-overlay:     var(--lx-dk-overlay);

  --lx-text:        var(--lx-dk-text);
  --lx-text-2:      var(--lx-dk-text-2);
  --lx-text-3:      var(--lx-dk-text-3);
  --lx-text-invert: var(--lx-dk-text-invert);

  --lx-border:      var(--lx-dk-border);
  --lx-border-2:    var(--lx-dk-border-2);

  --lx-primary:       var(--lx-dk-primary);
  --lx-primary-hover: var(--lx-dk-primary-hover);
  --lx-primary-soft:  var(--lx-dk-primary-soft);
  --lx-primary-ring:  var(--lx-dk-primary-ring);
  --lx-on-primary:    var(--lx-dk-on-primary);

  --lx-ok:          var(--lx-dk-ok);
  --lx-ok-soft:     var(--lx-dk-ok-soft);
  --lx-ok-border:   var(--lx-dk-ok-border);
  --lx-ok-text:     var(--lx-dk-ok-text);

  --lx-bad:         var(--lx-dk-bad);
  --lx-bad-soft:    var(--lx-dk-bad-soft);
  --lx-bad-border:  var(--lx-dk-bad-border);
  --lx-bad-text:    var(--lx-dk-bad-text);

  --lx-warn:        var(--lx-dk-warn);
  --lx-warn-soft:   var(--lx-dk-warn-soft);
  --lx-warn-border: var(--lx-dk-warn-border);
  --lx-warn-text:   var(--lx-dk-warn-text);

  --lx-info:        var(--lx-dk-info);
  --lx-info-soft:   var(--lx-dk-info-soft);
  --lx-info-border: var(--lx-dk-info-border);
  --lx-info-text:   var(--lx-dk-info-text);

  --lx-xp:          var(--lx-dk-xp);
  --lx-streak:      var(--lx-dk-streak);

  --lx-disabled-bg:   var(--lx-dk-disabled-bg);
  --lx-text-disabled: var(--lx-dk-text-disabled);

  --lx-sk-vocabulary:    var(--lx-dk-sk-vocabulary);
  --lx-sk-grammar:       var(--lx-dk-sk-grammar);
  --lx-sk-reading:       var(--lx-dk-sk-reading);
  --lx-sk-listening:     var(--lx-dk-sk-listening);
  --lx-sk-speaking:      var(--lx-dk-sk-speaking);
  --lx-sk-writing:       var(--lx-dk-sk-writing);
  --lx-sk-pronunciation: var(--lx-dk-sk-pronunciation);

  --lx-lv-a0: var(--lx-dk-lv-a0);
  --lx-lv-a1: var(--lx-dk-lv-a1);
  --lx-lv-a2: var(--lx-dk-lv-a2);
  --lx-lv-b1: var(--lx-dk-lv-b1);
  --lx-lv-b2: var(--lx-dk-lv-b2);
  --lx-lv-c1: var(--lx-dk-lv-c1);
  --lx-lv-c2: var(--lx-dk-lv-c2);

  --lx-vs-new:      var(--lx-dk-vs-new);
  --lx-vs-learning: var(--lx-dk-vs-learning);
  --lx-vs-weak:     var(--lx-dk-vs-weak);
  --lx-vs-mastered: var(--lx-dk-vs-mastered);

  --lx-chart-grid: var(--lx-dk-chart-grid);
  --lx-chart-axis: var(--lx-dk-chart-axis);
  --lx-chart-fill: var(--lx-dk-chart-fill);
  --lx-chart-line: var(--lx-dk-chart-line);

  --lx-heat-0: var(--lx-dk-heat-0);
  --lx-heat-1: var(--lx-dk-heat-1);
  --lx-heat-2: var(--lx-dk-heat-2);
  --lx-heat-3: var(--lx-dk-heat-3);
  --lx-heat-4: var(--lx-dk-heat-4);

  --lx-shadow-color: var(--lx-dk-shadow-color);
  --lx-shadow-strength: var(--lx-dk-shadow-strength);

  color-scheme: dark;
}


/* The no-JS path.

   inc/header.php writes data-theme from the stored preference, and the inline
   boot script resolves 'system' before first paint. If scripting is off the
   attribute stays "system", and this block is what makes that render correctly
   anyway. Identical to the block above by requirement, not by accident. */
@media (prefers-color-scheme: dark) {
  :root[data-theme="system"] {
  --lx-bg:          var(--lx-dk-bg);
  --lx-bg-subtle:   var(--lx-dk-bg-subtle);
  --lx-surface:     var(--lx-dk-surface);
  --lx-surface-2:   var(--lx-dk-surface-2);
  --lx-surface-3:   var(--lx-dk-surface-3);
  --lx-overlay:     var(--lx-dk-overlay);

  --lx-text:        var(--lx-dk-text);
  --lx-text-2:      var(--lx-dk-text-2);
  --lx-text-3:      var(--lx-dk-text-3);
  --lx-text-invert: var(--lx-dk-text-invert);

  --lx-border:      var(--lx-dk-border);
  --lx-border-2:    var(--lx-dk-border-2);

  --lx-primary:       var(--lx-dk-primary);
  --lx-primary-hover: var(--lx-dk-primary-hover);
  --lx-primary-soft:  var(--lx-dk-primary-soft);
  --lx-primary-ring:  var(--lx-dk-primary-ring);
  --lx-on-primary:    var(--lx-dk-on-primary);

  --lx-ok:          var(--lx-dk-ok);
  --lx-ok-soft:     var(--lx-dk-ok-soft);
  --lx-ok-border:   var(--lx-dk-ok-border);
  --lx-ok-text:     var(--lx-dk-ok-text);

  --lx-bad:         var(--lx-dk-bad);
  --lx-bad-soft:    var(--lx-dk-bad-soft);
  --lx-bad-border:  var(--lx-dk-bad-border);
  --lx-bad-text:    var(--lx-dk-bad-text);

  --lx-warn:        var(--lx-dk-warn);
  --lx-warn-soft:   var(--lx-dk-warn-soft);
  --lx-warn-border: var(--lx-dk-warn-border);
  --lx-warn-text:   var(--lx-dk-warn-text);

  --lx-info:        var(--lx-dk-info);
  --lx-info-soft:   var(--lx-dk-info-soft);
  --lx-info-border: var(--lx-dk-info-border);
  --lx-info-text:   var(--lx-dk-info-text);

  --lx-xp:          var(--lx-dk-xp);
  --lx-streak:      var(--lx-dk-streak);

  --lx-disabled-bg:   var(--lx-dk-disabled-bg);
  --lx-text-disabled: var(--lx-dk-text-disabled);

  --lx-sk-vocabulary:    var(--lx-dk-sk-vocabulary);
  --lx-sk-grammar:       var(--lx-dk-sk-grammar);
  --lx-sk-reading:       var(--lx-dk-sk-reading);
  --lx-sk-listening:     var(--lx-dk-sk-listening);
  --lx-sk-speaking:      var(--lx-dk-sk-speaking);
  --lx-sk-writing:       var(--lx-dk-sk-writing);
  --lx-sk-pronunciation: var(--lx-dk-sk-pronunciation);

  --lx-lv-a0: var(--lx-dk-lv-a0);
  --lx-lv-a1: var(--lx-dk-lv-a1);
  --lx-lv-a2: var(--lx-dk-lv-a2);
  --lx-lv-b1: var(--lx-dk-lv-b1);
  --lx-lv-b2: var(--lx-dk-lv-b2);
  --lx-lv-c1: var(--lx-dk-lv-c1);
  --lx-lv-c2: var(--lx-dk-lv-c2);

  --lx-vs-new:      var(--lx-dk-vs-new);
  --lx-vs-learning: var(--lx-dk-vs-learning);
  --lx-vs-weak:     var(--lx-dk-vs-weak);
  --lx-vs-mastered: var(--lx-dk-vs-mastered);

  --lx-chart-grid: var(--lx-dk-chart-grid);
  --lx-chart-axis: var(--lx-dk-chart-axis);
  --lx-chart-fill: var(--lx-dk-chart-fill);
  --lx-chart-line: var(--lx-dk-chart-line);

  --lx-heat-0: var(--lx-dk-heat-0);
  --lx-heat-1: var(--lx-dk-heat-1);
  --lx-heat-2: var(--lx-dk-heat-2);
  --lx-heat-3: var(--lx-dk-heat-3);
  --lx-heat-4: var(--lx-dk-heat-4);

  --lx-shadow-color: var(--lx-dk-shadow-color);
  --lx-shadow-strength: var(--lx-dk-shadow-strength);

  color-scheme: dark;
  }
}
