/* ==========================================================================
   XOTUTOR — shared theme tokens
   Stage 1: token foundation. Single source of truth for the colour layer
   across every dark-themed page.

   Tokens are named by ROLE, not by appearance, so Stage 2 can add a light
   value set without renaming a single reference.

   Dark is the base palette and lives on :root, so every page renders
   correctly with or without a theme class on <html>. `.dark` on <html> is
   the explicit dark selector and matches
   `tailwind.config = { darkMode: 'class' }` in index.html. `.light` is the
   Stage 2 hook and is deliberately empty for now — Stage 1 changes no pixels.

   Contrast ratios below are WCAG 2.1 against --surface (#1c2a48) unless
   stated otherwise. AA thresholds: 4.5:1 normal text, 3.0:1 large text.
   ========================================================================== */

:root {

  /* ── Surfaces ────────────────────────────────────────────────────────── */
  --surface:          #1c2a48;   /* page background (app pages)            */
  --surface-raised:   #243349;   /* cards standing above the page          */
  --surface-sunken:   #1e293b;   /* lesson sections, inset panels          */
  --surface-control:  #334155;   /* form inputs, selects, textareas        */
  --surface-control-focus: #3f4b5f; /* same, focused                       */

  /* ── Text ────────────────────────────────────────────────────────────── */
  --text-primary:     #f1f5f9;   /* 13.00:1  PASS                          */
  --text-secondary:   #cbd5e1;   /*  9.59:1  PASS                          */
  --text-muted:       #94a3b8;   /*  5.56:1  PASS (4.98:1 on raised)       */
  --text-on-accent:   #ffffff;   /* text sitting on an accent fill         */

  /* --text-muted is the only muted grey that clears AA on every surface in
     this set. It replaces #64748b (2.99:1) and #475569 (1.88:1), both of
     which failed normal AND large text on every dark surface. */

  /* ── Accent (purple), pale → deepest ─────────────────────────────────── */
  --accent-pale:      #c4b5fd;   /*  7.72:1  PASS                          */
  --accent:           #a78bfa;   /*  5.23:1  PASS — accent text and links  */
  --accent-strong:    #8b5cf6;   /*  3.36:1  large text / non-text only    */
  --accent-deep:      #7c3aed;   /*  2.50:1  fills only, never text        */
  --accent-deepest:   #6b21a8;   /*  fills only; also PWA manifest colour  */

  /* ── Borders ─────────────────────────────────────────────────────────── */
  --border:           #334155;   /* card and divider borders               */
  --border-strong:    #475569;   /* form inputs, ghost-button outlines     */
  --border-accent:        rgba(139, 92, 246, 0.25);
  --border-accent-strong: rgba(139, 92, 246, 0.35);
  --border-gold:          rgba(251, 191, 36, 0.30);

  /* ── Status ──────────────────────────────────────────────────────────── */
  --gold:             #fbbf24;   /*  8.53:1  PASS                          */
  --gold-deep:        #f59e0b;   /*  6.63:1  PASS                          */
  --success:          #10b981;   /*  5.62:1  PASS — success FILLS          */
  --success-text:     #86efac;   /* 10.62:1  PASS — success COPY. Same
                                    split as --danger-text below: the fill
                                    value and the copy value are the same
                                    hue family but not the same lightness,
                                    and only the copy one has to survive on
                                    a near-white card in light mode.       */
  --danger:           #ef4444;   /*  3.79:1  large text / non-text only    */
  --danger-text:      #fca5a5;   /*  8.05:1  PASS — error COPY, not fills  */
  --info:             #06b6d4;   /*  6.03:1  PASS — the cyan used by the
                                     lesson "Diagram" section label         */

  /* --danger-text is the one token Task 26 adds to Stage 1's 23. --danger is
     a fill/border value that only clears large-text AA, but the estate also
     renders error *sentences* (join.html .error-msg, and the same pattern in
     the app). Those used a literal #fca5a5, which is right on a dark card and
     1.90:1 on a white one — it had to become a token to have a light value. */
}


/* ==========================================================================
   Surface families

   Three page groups ship a different surface trio than the app does. That
   divergence is real in production today, so Stage 1 preserves it exactly —
   expressed in the SAME vocabulary rather than in a competing one.

   Collapsing these onto --surface is a design decision, not a foundation
   one: it would change how those pages look. Stage 2/3 can delete any block
   below in a single move once that call is made.
   ========================================================================== */

/* Marketing / SEO landing pages: ai-language-tutor, ai-tutor-app,
   ai-tutor-gcse, for-schools. (Task 56 deleted testimonials and
   xoai-cloud-with-sports, which also used this class.) */
:root.surface-deep {
  --surface:          #0f172a;
  --surface-raised:   #1e293b;
  --surface-sunken:   #1e293b;
}

/* Internal marketing dashboard: xo-marketing-dashboard. */
:root.surface-dashboard {
  --surface:          #080c14;
  --surface-raised:   #141d2e;
  --surface-sunken:   #0f1623;
  --border:           #1e2d47;
}

/* Feedback page: brand purple page background. */
:root.surface-brand {
  --surface:          #6b21a8;
  --surface-raised:   #6b21a8;
  --surface-sunken:   #6b21a8;
}


/* ==========================================================================
   Light theme (Task 26).

   Dark stays the base on :root, so a page with no theme class still renders
   correctly. `.light` on <html> overrides the same token names — swapped by
   the pre-paint boot script in theme-toggle.js.

   Ratios are WCAG 2.1, verified against all four light surfaces below.
   AA: 4.5:1 normal text, 3.0:1 large text and non-text.

   THE ACCENT RAMP DOES NOT SIMPLY INVERT. The five accent steps split by how
   they are actually used in the estate, not by lightness:
     - pale / accent      are text on a dark surface  → must go DARK in light
     - strong             is both text and fill       → one mid value serves both
     - deep / deepest     are already dark-purple text on the white modal
                          islands (which are white in BOTH themes) → stay dark
   So the text end inverts and the fill end holds. Verified against every
   var(--accent-*) call site.
   ========================================================================== */

:root.light {

  /* ── Surfaces ────────────────────────────────────────────────────────── */
  --surface:          #f8fafc;   /* page background                        */
  --surface-raised:   #ffffff;   /* cards standing above the page          */
  --surface-sunken:   #f1f5f9;   /* lesson sections, inset panels          */
  --surface-control:  #ffffff;   /* form inputs, selects, textareas        */
  --surface-control-focus: #f8fafc; /* same, focused                       */

  /* ── Text ────────────────────────────────────────────────────────────── */
  --text-primary:     #0f172a;   /* 17.06:1 on --surface  PASS             */
  --text-secondary:   #334155;   /*  9.90:1  PASS                          */
  --text-muted:       #5f6b7a;   /*  5.19:1  PASS (4.95:1 on sunken)       */
  --text-on-accent:   #ffffff;   /* unchanged — still sits on accent fills */

  /* --text-muted is NOT slate-500 #64748b. That is the obvious light
     equivalent, but it lands at 4.34:1 on --surface-sunken — a fail. This
     value is two steps darker and clears AA on all four light surfaces,
     mirroring what --text-muted does on the dark side. */

  /* ── Accent (purple) — see the ramp note above ───────────────────────── */
  --accent-pale:      #5b21b6;   /*  8.59:1  PASS — the high-contrast step */
  --accent:           #6d28d9;   /*  6.79:1  PASS — accent text and links  */
  --accent-strong:    #7c3aed;   /*  5.45:1  PASS as text; 5.70:1 white on
                                     it as a fill — clears AA both ways    */
  --accent-deep:      #7c3aed;   /*  fills, and dark-purple text on white  */
  --accent-deepest:   #5b21b6;   /*  deepest fill; PWA manifest colour     */

  /* ── Borders ─────────────────────────────────────────────────────────── */
  --border:           #e2e8f0;   /* card and divider borders               */
  --border-strong:    #64748b;   /* 4.76:1 on white — clears 1.4.11 for
                                    input and ghost-button outlines        */
  --border-accent:        rgba(124, 58, 237, 0.28);
  --border-accent-strong: rgba(124, 58, 237, 0.40);
  --border-gold:          rgba(180, 83, 9, 0.35);

  /* ── Status ──────────────────────────────────────────────────────────── */
  --gold:             #b45309;   /*  4.80:1  PASS. #fbbf24 is 1.75:1 on
                                     white — unusable as text in light.    */
  --gold-deep:        #92400e;   /*  6.78:1  PASS                          */
  --success:          #047857;   /*  5.24:1  PASS (#10b981 is 2.28:1)      */
  --success-text:     #047857;   /*  5.24:1  PASS. The dark side's #86efac
                                    is 1.44:1 on white — unusable. In light
                                    the fill and copy values converge, the
                                    same way they nearly do for --danger.  */
  --danger:           #c81e1e;   /*  5.48:1  PASS (#ef4444 is 3.76:1)      */
  --danger-text:      #b91c1c;   /*  6.44:1  PASS on white; the dark side's
                                     #fca5a5 is 1.90:1 there — unusable    */
  --info:             #0e7490;   /*  4.89:1  PASS (#06b6d4 is 2.35:1 here) */
}


/* ==========================================================================
   Surface families — light values.

   Same three divergent groups as the dark side. In light they converge much
   harder than they do in dark: #1c2a48 / #0f172a / #080c14 are three clearly
   different darks, but their light counterparts are all near-white, so the
   families differ only by which of the three neutrals is the page vs card.
   ========================================================================== */

/* Marketing / SEO landing pages. Sits one step brighter than the app so the
   page reads as paper and the cards lift off it. */
:root.light.surface-deep {
  --surface:          #ffffff;
  --surface-raised:   #f8fafc;
  --surface-sunken:   #f1f5f9;
}

/* Internal marketing dashboard. */
:root.light.surface-dashboard {
  --surface:          #ffffff;
  --surface-raised:   #f8fafc;
  --surface-sunken:   #f1f5f9;
  --border:           #e2e8f0;
}

/* Feedback page. The dark side makes the whole page brand purple #6b21a8.
   A light theme cannot keep a saturated purple page and still take dark text
   on it, so this becomes the palest purple wash instead — brand tint kept,
   text tokens usable unchanged. --text-primary on it is 15.94:1. */
:root.light.surface-brand {
  --surface:          #faf5ff;
  --surface-raised:   #ffffff;
  --surface-sunken:   #f5eeff;
}


/* ==========================================================================
   color-scheme

   Makes the UA theme its own furniture — scrollbars, form control chrome,
   spinners, and the iOS text-selection handles. Without this, light mode
   still renders dark scrollbars and dark native select popups.
   ========================================================================== */

:root       { color-scheme: dark;  }
:root.light { color-scheme: light; }


/* ==========================================================================
   Inline-axis sign (Task 44)

   Layout mirrors for free once <html dir="rtl"> is set, and spacing is
   expressed in logical properties. A `transform: translateX()` cannot be:
   there is no logical transform, and translateX is always physical. Rather
   than duplicate every such rule under a [dir="rtl"] selector, multiply the
   distance by this: 1 in left-to-right, -1 in right-to-left.

       transform: translateX(calc(20px * var(--xo-flip)));

   Inert on the marketing/SEO pages, which are English-only and never get
   dir="rtl".
   ========================================================================== */

:root          { --xo-flip:  1; }
[dir="rtl"]    { --xo-flip: -1; }


/* ==========================================================================
   Gradient-clipped headings

   A heading painted with `background-clip: text` has no `color` to tokenise,
   so it cannot follow the theme by inheritance — the gradient itself has to
   change. The dark gradient runs brand violet -> white, which is exactly the
   wrong shape on a light card: the fade end lands on near-white paper and the
   second half of the word disappears.

   Light keeps the same idea (brand violet -> maximum contrast) with the
   direction flipped, so the heading still reads as a gradient rather than as
   flat text. Contrast is measured at the WORST stop, against --surface-raised
   (#ffffff in light), because that stop is what a reader actually struggles
   with:

     light  #7c3aed (violet-600) -> #4c1d95 (violet-900)
            worst stop 5.70:1 — clears normal-text AA (4.5), not just the 3:1
            large-text threshold this 24px/700 heading would be allowed
     dark   #a78bfa -> #ffffff   unchanged; worst stop 5.23:1 on --surface-raised

   Only islands that follow the theme need this. #landingTaglineEl uses the
   same white-ended gradient and is deliberately NOT converted: it lives inside
   #upgradeCTA, a fixed-dark brand island that stays deep purple in both
   themes, so its white end is correct there.
   ========================================================================== */

.xo-gradient-heading {
  background: linear-gradient(135deg, #a78bfa 0%, #ffffff 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
}

:root.light .xo-gradient-heading {
  background: linear-gradient(135deg, #7c3aed 0%, #4c1d95 100%);
  -webkit-background-clip: text;
  background-clip: text;
}


/* ==========================================================================
   The theme toggle control

   ONE control, TWO presentations. The label is in the DOM in both cases and
   is hidden by the media query below 768px — which is Tailwind's `md`, the
   breakpoint the header already uses for every other responsive decision in
   it. No new breakpoint was invented for this control.

     >= 768px : [ DARK  (o———) ]   labelled pill
     <  768px : [ (o———) ]         icon-only sun/moon slider

   The control is deliberately NOT token-driven in its --on-brand form: it
   sits on the purple header gradient, which is purple in BOTH themes, so
   token colours would make it disappear in one of them.
   ========================================================================== */

.xo-theme-toggle {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 3px 4px 3px 9px;
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  background: transparent;
  color: var(--text-secondary);
  cursor: pointer;
  font: inherit;
  line-height: 1;
  -webkit-tap-highlight-color: transparent;
  transition: background-color .2s ease, border-color .2s ease;
}

.xo-theme-toggle:hover { background: rgba(127, 127, 127, .12); }

.xo-theme-toggle:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.xo-theme-toggle__label {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  white-space: nowrap;
}

.xo-theme-toggle__track {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: space-between;
  width: 46px;
  height: 26px;
  padding: 0 5px;
  border-radius: 999px;
  background: rgba(127, 127, 127, .28);
  flex: none;
}

/* The thumb sits UNDER the icons (z-index 0 vs 1) so the active icon reads
   as sitting on the thumb rather than being covered by it. */
.xo-theme-toggle__thumb {
  position: absolute;
  top: 3px;
  inset-inline-start: 3px;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: #ffffff;
  box-shadow: 0 1px 3px rgba(0, 0, 0, .35);
  transition: transform .22s cubic-bezier(.3, .7, .4, 1);
  z-index: 0;
}

.xo-theme-toggle__icon {
  position: relative;
  z-index: 1;
  width: 14px;
  height: 14px;
  flex: none;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  transition: color .22s ease, opacity .22s ease;
}

.xo-theme-toggle__icon--sun    { fill: currentColor; stroke: none; }
.xo-theme-toggle__icon--sun  g { stroke: currentColor; }
.xo-theme-toggle__icon--moon   { fill: currentColor; stroke: none; }

/* Dark is the resting state: thumb at the inline end, moon lit, sun dimmed.
   --xo-flip mirrors the travel in RTL, where the track order flips too. */
.xo-theme-toggle__thumb            { transform: translateX(calc(20px * var(--xo-flip))); }
.xo-theme-toggle .xo-theme-toggle__icon--sun  { color: currentColor; opacity: .45; }
.xo-theme-toggle .xo-theme-toggle__icon--moon { color: #1c2a48;      opacity: 1;   }

/* Light: thumb left, sun lit. Driven off aria-checked so the visual state
   cannot drift from the state assistive tech is told about. */
.xo-theme-toggle[aria-checked="true"] .xo-theme-toggle__thumb { transform: translateX(0); }
.xo-theme-toggle[aria-checked="true"] .xo-theme-toggle__icon--sun  { color: #b45309; opacity: 1; }
.xo-theme-toggle[aria-checked="true"] .xo-theme-toggle__icon--moon { color: currentColor; opacity: .45; }

/* On the purple brand header, in both themes. */
.xo-theme-toggle--on-brand {
  border-color: rgba(255, 255, 255, .30);
  color: #ffffff;
}
.xo-theme-toggle--on-brand:hover       { background: rgba(255, 255, 255, .12); }
.xo-theme-toggle--on-brand .xo-theme-toggle__track { background: rgba(15, 23, 42, .30); }
.xo-theme-toggle--on-brand:focus-visible {
  outline: 2px solid #ffffff;
  outline-offset: 2px;
}

/* Mobile: icon-only. Below the header's own md breakpoint. */
@media (max-width: 767px) {
  .xo-theme-toggle        { padding: 3px 4px; gap: 0; }
  .xo-theme-toggle__label { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .xo-theme-toggle,
  .xo-theme-toggle__thumb,
  .xo-theme-toggle__icon { transition: none; }
}
