/* ==========================================================================
   KAIS site-wide theme layer — System / Light / Dark
   ==========================================================================

   Loaded from base.html IMMEDIATELY AFTER tailwind-main.css, blocking.
   Both facts are load-bearing and both are test-pinned
   (tests/test_static/test_theme_wiring.py):

     * AFTER, because the remap in section B overrides pre-built Tailwind
       utilities at EQUAL specificity, so it wins on source order alone.
       Linked first, this entire file is inert — and inert CSS is
       indistinguishable from CSS nobody wrote.

     * BLOCKING (never `media="print"` + swap), because the theme IS the
       first paint. Deferring it re-introduces the exact flash that
       theme-init.js exists to prevent.

   THEMING
   -------
   Three mechanisms, the same three already documented under the THEMING
   banner in css/curriculum-map.css, generalised site-wide:
   `color-scheme`, `prefers-color-scheme`, and `:root[data-theme]`.

   THE MEDIA QUERY IS SCOPED TO `:root[data-theme="auto"]` AND MUST STAY
   THAT WAY. base.html is hard-dark and most surfaces carry no theme
   opt-in, so an UNSCOPED `prefers-color-scheme: light` block paints a
   bright island inside dark chrome for every light-mode visitor — a
   regression, not a feature. theme-init.js stamps `auto` for anyone who
   has expressed no preference, so the hook is live by default; a page
   reached with JavaScript disabled carries no attribute at all and keeps
   painting dark, which is correct.

   No `light-dark()` and no `:has()` anywhere in this file (test-pinned).
   An unsupported `light-dark()` takes its whole declaration down
   silently, and `:has()` inherits the specificity of its most specific
   argument, which would make the section-B cascade unpredictable.

   TWO KINDS OF TOKEN LIVE HERE, AND THE DIFFERENCE IS DELIBERATE
   --------------------------------------------------------------
   1. ROLE tokens (`--kais-bg`, `--kais-text`, …) are declared in ALL
      THREE blocks — dark, explicit-light, and auto-light. Section B
      replaces a Tailwind utility's literal hex outright, so there is no
      literal left to fall back to; the dark value must be declared.

   2. CHROME tokens (`--kais-drawer-*`, `--kais-overlay-*`, …) are
      declared in the LIGHT BLOCKS ONLY. Their consumers in
      mobile-nav.css / spinner.css / skip-link.css are written as
      `var(--kais-token, <the exact literal that was there before>)`.
      An undefined custom property falls back, so DARK MODE IS BYTE-
      IDENTICAL TO THE PRE-THEME RENDERING BY CONSTRUCTION rather than
      by my having retyped a hex correctly. Do not "tidy" these into the
      bare `:root` block — that would discard the guarantee.

   The light values are spelled out twice, once per light block. They
   cannot be shared through an indirection: the contrast sweep in
   tests/test_static/test_theme_assets.py resolves the six role tokens to
   opaque hex, so a `var()` reference there would not be computable.
   KEEP THE TWO LIGHT BLOCKS IN SYNC.
   ========================================================================== */

/* ==========================================================================
   A. TOKENS
   ========================================================================== */

/* --------------------------------------------------------------------------
   A1. DARK — the default, and the palette the site already ships.
   Every value below is the literal Tailwind hex that base.html's utility
   classes resolve to today, so switching the chrome onto tokens changes
   nothing until a light palette is selected.
   -------------------------------------------------------------------------- */
:root {
  color-scheme: dark;

  /* Surfaces */
  --kais-bg: #1f2937;        /* gray-800 — page + navbar background       */
  --kais-bg-deep: #111827;   /* gray-900 — footer, deepest chrome         */
  --kais-surface: #374151;   /* gray-700 — raised chrome, pills, hovers   */
  --kais-elevated: #111827;  /* popovers and menus that float over chrome */

  /* Text */
  --kais-text: #ffffff;
  --kais-text-1: #e5e7eb;    /* gray-200 */
  --kais-text-2: #d1d5db;    /* gray-300 */
  --kais-muted: #9ca3af;     /* gray-400 */
  --kais-muted-2: #6b7280;   /* gray-500 */

  /* Lines */
  --kais-border: #374151;    /* gray-700 */
  --kais-border-2: #1f2937;  /* gray-800 */

  /* Interaction */
  --kais-hover: rgba(255, 255, 255, 0.06);
  --kais-shadow: rgba(0, 0, 0, 0.55);
  --kais-focus: #7dd3fc;

  /* Labels that sit on a saturated brand background. Section C pins
     these back to white after `.text-white` has been remapped. */
  --kais-on-accent: #ffffff;
}

/* --------------------------------------------------------------------------
   A2. LIGHT — explicit opt-in, stamped by theme-init.js / theme-toggle.js.
   A slate family rather than pure grays: warm-neutral enough to read as a
   designed surface instead of an unstyled one, and every text/background
   pairing below clears WCAG 2.1 AA for normal text.
   -------------------------------------------------------------------------- */
:root[data-theme="light"] {
  color-scheme: light;

  --kais-bg: #f1f5f9;
  --kais-bg-deep: #ffffff;
  --kais-surface: #e2e8f0;
  --kais-elevated: #ffffff;

  --kais-text: #0f172a;
  --kais-text-1: #1e293b;
  --kais-text-2: #334155;
  --kais-muted: #475569;
  --kais-muted-2: #4b5563;

  --kais-border: #cbd5e1;
  --kais-border-2: #e2e8f0;

  --kais-hover: rgba(15, 23, 42, 0.06);
  --kais-shadow: rgba(15, 23, 42, 0.18);
  --kais-focus: #0369a1;

  --kais-on-accent: #ffffff;

  /* -- chrome tokens: light-only, see the header note ------------------- */
  --kais-drawer-bg-1: #ffffff;
  --kais-drawer-bg-2: #f1f5f9;
  --kais-drawer-text: #0f172a;
  --kais-drawer-text-strong: #0f172a;
  --kais-drawer-text-2: #334155;
  --kais-drawer-muted: #475569;
  --kais-drawer-icon: #334155;
  --kais-drawer-accent: #15803d;
  --kais-drawer-border: rgba(203, 213, 225, 0.9);
  --kais-drawer-border-soft: rgba(203, 213, 225, 0.75);
  --kais-drawer-border-mid: rgba(203, 213, 225, 0.85);
  --kais-drawer-veil-1: rgba(15, 23, 42, 0.05);
  --kais-drawer-veil-2: rgba(15, 23, 42, 0.06);
  --kais-drawer-veil-3: rgba(15, 23, 42, 0.07);
  --kais-drawer-inset: rgba(15, 23, 42, 0.04);
  --kais-drawer-shadow: rgba(15, 23, 42, 0.16);
  --kais-backdrop: rgba(15, 23, 42, 0.35);

  --kais-overlay: rgba(226, 232, 240, 0.88);
  --kais-overlay-text: rgba(15, 23, 42, 0.9);
  --kais-overlay-veil: rgba(15, 23, 42, 0.06);
  --kais-overlay-veil-border: rgba(15, 23, 42, 0.14);
  --kais-overlay-inset: rgba(15, 23, 42, 0.06);

  --kais-skip-bg: #0f172a;
  --kais-skip-text: #ffffff;

  /* -- tailwind-main.css's OWN design tokens ----------------------------
     The vendored bundle declares these on a bare `:root` (line 11) and
     drives `body`, `.nav-auth a`, `.footer` and every card surface from
     them. Re-declaring them here is the single highest-leverage edit in
     this file: one block flips the body background, the default border
     colour and the default text colour for every page at once, and it
     cannot affect dark mode because `:root[data-theme="light"]` (0,2,0)
     only ever adds to `:root` (0,1,0). */
  accent-color: #2563eb;
  --bg-primary: #f1f5f9;
  --bg-alt: #e8eef6;
  --bg-card: #ffffff;
  --text-dark: #0f172a;
  --text-light: #334155;
  --text-muted: #5b6780;
  --border-gray: rgba(15, 23, 42, 0.12);
  --border-card: rgba(15, 23, 42, 0.10);
  --accent-blue: #2563eb;
  --hover-bg: #e2e8f0;
  --link-visited: #7c3aed;
  --gradient-hero:
    radial-gradient(1200px 600px at 80% -10%, rgba(155, 109, 255, 0.10), transparent),
    radial-gradient(900px 500px at -10% 10%, rgba(106, 168, 255, 0.12), transparent),
    var(--bg-primary);
  --shadow-card: 0 10px 30px rgba(15, 23, 42, 0.10);
  --shadow-card-hover: 0 25px 60px rgba(31, 45, 92, 0.18);

  /* -- the SHARED surface vocabulary -----------------------------------
     home.css, dashboard.css and note.css each declare their own bare
     `:root` block, and — measured, not assumed — they use the SAME token
     NAMES as tailwind-main.css for the same roles (--bg-page, --bg-card,
     --text-heading, --text-body, --border-card …), differing only
     slightly in value. Because `:root[data-theme="light"]` (0,2,0) beats
     every one of those `:root` blocks (0,1,0), declaring the vocabulary
     once here re-themes all four sheets at a stroke.

     This is not optional politeness. Flipping --bg-card without flipping
     --text-heading is precisely the half-flip that painted the homepage
     skip link white-on-white in the first pass of this change; the
     browser harness at tests/visual/theme_contrast_check.py caught it,
     and the pairs below are what it measures green.

     The white-alpha veils invert to black-alpha: a `rgba(255,255,255,.1)`
     hairline is invisible on a white card. */
  --bg-page: #f1f5f9;
  --surface: #ffffff;
  --text-heading: #0f172a;
  --text-body: #334155;
  --text-muted-strong: #47536b;
  --muted-text: rgba(15, 23, 42, 0.85);
  --border-light: rgba(15, 23, 42, 0.12);
  --stroke-soft: rgba(15, 23, 42, 0.09);
  --stroke-strong: rgba(15, 23, 42, 0.16);
  --inner-top-light: inset 0 1px 0 rgba(15, 23, 42, 0.04);
  --dot-grid: radial-gradient(circle at center, rgba(15, 23, 42, 0.07) 1px, transparent 1.2px);
  --focus-ring: 0 0 0 3px rgba(255, 255, 255, 0.9), 0 0 0 6px var(--brand);

  /* -- accent-as-TEXT -----------------------------------------------------
     The brand colours are tuned to be read on a dark page and to be used
     as BACKGROUNDS under white labels, so they must not flip: darkening
     --brand to fix accent-coloured text would repaint every brand button
     and badge, and `.pricing-badge` puts near-black on that same gradient.
     Same argument as section C.

     So accent-as-text sites opt in instead, reading
     `var(--accent-ink, var(--brand))` — the ink token is declared only in
     the light palettes, and the fallback keeps dark unchanged. Measured
     against every light surface here: worst pairing 5.25:1
     (--danger-ink on --kais-surface); all six clear AA on
     --kais-bg, --kais-surface and --bg-card.

     Declared centrally rather than per page stylesheet: six sheets each
     carrying their own copy is six chances to drift, and this repo has
     already had one sentence drift across two files. */
  --accent-ink: #1d4ed8;
  --accent-2-ink: #6d28d9;
  --ok-ink: #166534;
  --warn-ink: #9a3412;
  --danger-ink: #b91c1c;
  --gold-ink: #854d0e;
}

/* --------------------------------------------------------------------------
   A3. AUTO — follow the operating system.
   Identical payload to A2. SCOPED TO `:root[data-theme="auto"]`; see the
   header. A bare `:root` here, or a `:root:not(...)` wearing a disguise,
   repaints every hard-dark surface on the site for every light-mode
   visitor whether or not they asked for it.
   -------------------------------------------------------------------------- */
@media (prefers-color-scheme: light) {
  :root[data-theme="auto"] {
    color-scheme: light;

    --kais-bg: #f1f5f9;
    --kais-bg-deep: #ffffff;
    --kais-surface: #e2e8f0;
    --kais-elevated: #ffffff;

    --kais-text: #0f172a;
    --kais-text-1: #1e293b;
    --kais-text-2: #334155;
    --kais-muted: #475569;
    --kais-muted-2: #4b5563;

    --kais-border: #cbd5e1;
    --kais-border-2: #e2e8f0;

    --kais-hover: rgba(15, 23, 42, 0.06);
    --kais-shadow: rgba(15, 23, 42, 0.18);
    --kais-focus: #0369a1;

    --kais-on-accent: #ffffff;

    --kais-drawer-bg-1: #ffffff;
    --kais-drawer-bg-2: #f1f5f9;
    --kais-drawer-text: #0f172a;
    --kais-drawer-text-strong: #0f172a;
    --kais-drawer-text-2: #334155;
    --kais-drawer-muted: #475569;
    --kais-drawer-icon: #334155;
    --kais-drawer-accent: #15803d;
    --kais-drawer-border: rgba(203, 213, 225, 0.9);
    --kais-drawer-border-soft: rgba(203, 213, 225, 0.75);
    --kais-drawer-border-mid: rgba(203, 213, 225, 0.85);
    --kais-drawer-veil-1: rgba(15, 23, 42, 0.05);
    --kais-drawer-veil-2: rgba(15, 23, 42, 0.06);
    --kais-drawer-veil-3: rgba(15, 23, 42, 0.07);
    --kais-drawer-inset: rgba(15, 23, 42, 0.04);
    --kais-drawer-shadow: rgba(15, 23, 42, 0.16);
    --kais-backdrop: rgba(15, 23, 42, 0.35);

    --kais-overlay: rgba(226, 232, 240, 0.88);
    --kais-overlay-text: rgba(15, 23, 42, 0.9);
    --kais-overlay-veil: rgba(15, 23, 42, 0.06);
    --kais-overlay-veil-border: rgba(15, 23, 42, 0.14);
    --kais-overlay-inset: rgba(15, 23, 42, 0.06);

    --kais-skip-bg: #0f172a;
    --kais-skip-text: #ffffff;

    accent-color: #2563eb;
    --bg-primary: #f1f5f9;
    --bg-alt: #e8eef6;
    --bg-card: #ffffff;
    --text-dark: #0f172a;
    --text-light: #334155;
    --text-muted: #5b6780;
    --border-gray: rgba(15, 23, 42, 0.12);
    --border-card: rgba(15, 23, 42, 0.10);
    --accent-blue: #2563eb;
    --hover-bg: #e2e8f0;
    --link-visited: #7c3aed;
    --gradient-hero:
      radial-gradient(1200px 600px at 80% -10%, rgba(155, 109, 255, 0.10), transparent),
      radial-gradient(900px 500px at -10% 10%, rgba(106, 168, 255, 0.12), transparent),
      var(--bg-primary);
    --shadow-card: 0 10px 30px rgba(15, 23, 42, 0.10);
    --shadow-card-hover: 0 25px 60px rgba(31, 45, 92, 0.18);

    --bg-page: #f1f5f9;
    --surface: #ffffff;
    --text-heading: #0f172a;
    --text-body: #334155;
    --text-muted-strong: #47536b;
    --muted-text: rgba(15, 23, 42, 0.85);
    --border-light: rgba(15, 23, 42, 0.12);
    --stroke-soft: rgba(15, 23, 42, 0.09);
    --stroke-strong: rgba(15, 23, 42, 0.16);
    --inner-top-light: inset 0 1px 0 rgba(15, 23, 42, 0.04);
    --dot-grid: radial-gradient(circle at center, rgba(15, 23, 42, 0.07) 1px, transparent 1.2px);
    --focus-ring: 0 0 0 3px rgba(255, 255, 255, 0.9), 0 0 0 6px var(--brand);

    --accent-ink: #1d4ed8;
    --accent-2-ink: #6d28d9;
    --ok-ink: #166534;
    --warn-ink: #9a3412;
    --danger-ink: #b91c1c;
    --gold-ink: #854d0e;
  }
}

/* ==========================================================================
   B. TAILWIND UTILITY REMAP
   ==========================================================================

   tailwind-main.css is VENDORED and PRE-BUILT — there is no config, no
   JIT, no build step. Its utilities are literal hexes
   (`.bg-gray-800{background-color:#1f2937}`), so a theme cannot be
   expressed in tokens alone: the dark-role utilities have to be routed
   through `var(--kais-*)` by hand. These rules win at equal specificity
   purely because this file is linked after the bundle.

   WHAT IS *NOT* HERE MATTERS AS MUCH AS WHAT IS.

   (a) Light-card utilities are deliberately untouched: `.bg-white`,
       `.bg-gray-50`, `.bg-gray-200`, `.bg-gray-100`, `.bg-gray-300`,
       `.text-gray-600` … `.text-gray-900`, `.border-gray-200`. Those
       paint intentionally-light islands — a white card with dark ink —
       inside otherwise dark pages. They already read correctly in both
       themes; remapping one inverts the card into an unreadable slab in
       dark mode. Their absence is asserted, not assumed
       (test_light_card_utility_is_not_remapped).

   (b) Utilities that the vendored build never compiled are NOT invented
       here. `bg-white/5`, `border-white/10`, `placeholder-gray-500`,
       `from-gray-800` and friends appear in templates but paint NOTHING
       today — the bundle has no rule for them. Defining them would add
       veils and hairlines to DARK mode that have never been there, which
       is a visible regression dressed up as a theme fix. The one
       exception is `.bg-gray-700`, which the contract requires and which
       is documented on its own rule below.
   -------------------------------------------------------------------- */

/* -- surfaces ------------------------------------------------------------ */
.bg-gray-800 {
  background-color: var(--kais-bg, #1f2937);
}

.bg-gray-900 {
  background-color: var(--kais-bg-deep, #111827);
}

/* NOT in the vendored bundle: the navbar coin pill and a handful of
   panels ask for `bg-gray-700` and currently get nothing, so today they
   are transparent. Defining it is required by the contract and honours
   what the markup asks for; the visible consequence in dark mode is that
   the coin pill gains the raised gray-700 background it was always
   written to have. This is the ONE intentional dark-mode pixel change in
   this change set. */
.bg-gray-700,
.hover\:bg-gray-700:hover {
  background-color: var(--kais-surface, #374151);
}

/* -- text ---------------------------------------------------------------- */
.text-white,
.hover\:text-white:hover {
  color: var(--kais-text, #ffffff);
}

.text-gray-200 {
  color: var(--kais-text-1, #e5e7eb);
}

.text-gray-300 {
  color: var(--kais-text-2, #d1d5db);
}

.text-gray-400 {
  color: var(--kais-muted, #9ca3af);
}

/* gray-500 on gray-800 is 3.5:1 — it has always been below AA in dark
   mode. The dark value is preserved byte-for-byte here (fixing it is a
   separate, visible decision); the light value is chosen to clear AA so
   the defect is not carried into the new palette. */
.text-gray-500 {
  color: var(--kais-muted-2, #6b7280);
}

/* -- lines --------------------------------------------------------------- */
.border-gray-700 {
  border-color: var(--kais-border, #374151);
}

.border-gray-800 {
  border-color: var(--kais-border-2, #1f2937);
}

/* ==========================================================================
   C. ON-ACCENT EXCEPTIONS
   ==========================================================================

   Section B remaps `.text-white`, which is correct — it is a dark-role
   utility. But `text-white` also sits on SATURATED BUTTONS
   (`bg-green-600 text-white`), where the label must stay white in both
   themes or it repaints near-black on green.

   The resting-background list below is GENERATED FROM THE TEMPLATES by
   test_every_colored_button_keeps_white_text, so a colored button added
   next week fails that test instead of shipping an unreadable control.
   Each compound is (0,2,0) and beats the (0,1,0) remap regardless of
   order; they are placed after it anyway so the intent reads top-down.

   The `hover:` block below goes BEYOND what the test generates. A
   `text-white` element whose only colored background arrives on hover
   has a resting background that is now light, so section B correctly
   darkens its label — but the moment the pointer lands on it the
   background goes dark and the darkened label disappears. Only
   backgrounds dark enough to need white text are listed; `orange-300`
   and `green-300` are deliberately excluded, because white-on-pale was
   already unreadable there and section B's darker label is an
   improvement rather than a regression.
   -------------------------------------------------------------------- */
.bg-blue-500.text-white,
.bg-blue-600.text-white,
.bg-green-500.text-white,
.bg-green-600.text-white,
.bg-green-900.text-white,
.bg-orange-900.text-white,
.bg-red-500.text-white,
.bg-red-900.text-white {
  color: var(--kais-on-accent, #ffffff);
}

.hover\:bg-blue-600:hover.text-white,
.hover\:bg-blue-700:hover.text-white,
.hover\:bg-blue-900:hover.text-white,
.hover\:bg-green-500:hover.text-white,
.hover\:bg-green-600:hover.text-white,
.hover\:bg-red-500:hover.text-white,
.hover\:bg-red-600:hover.text-white,
.hover\:bg-red-900:hover.text-white {
  color: var(--kais-on-accent, #ffffff);
}

/* ==========================================================================
   D. CHROME THAT PAINTS ITS OWN LITERALS
   ==========================================================================

   A handful of base.html's chrome is styled by component rules inside
   tailwind-main.css rather than by utilities, so section B cannot reach
   it. `.navbar` is the important one: it declares
   `background: rgba(11,16,32,.85)` at line 1510, AFTER `.bg-gray-800` at
   line 979 and at equal specificity — so the navbar has never actually
   been gray-800 at all. Left alone, its background would stay dark navy
   while section B flipped `.text-white` to near-black ink, which is the
   exact half-flip this whole change set exists to prevent: a navbar with
   an invisible label.

   These overrides carry `:root[data-theme=…]` and are therefore (0,3,0)
   against the bundle's (0,1,0). They win on specificity, not on source
   order, which is what makes them safe to add without touching the
   vendored file.
   -------------------------------------------------------------------- */
:root[data-theme="light"] .navbar {
  background: rgba(255, 255, 255, 0.85);
  box-shadow: 0 4px 20px rgba(15, 23, 42, 0.08);
}

:root[data-theme="light"] .nav-auth a:hover {
  color: var(--kais-text, #0f172a);
}

:root[data-theme="light"] .footer {
  background: rgba(255, 255, 255, 0.9);
}

@media (prefers-color-scheme: light) {
  :root[data-theme="auto"] .navbar {
    background: rgba(255, 255, 255, 0.85);
    box-shadow: 0 4px 20px rgba(15, 23, 42, 0.08);
  }

  :root[data-theme="auto"] .nav-auth a:hover {
    color: var(--kais-text, #0f172a);
  }

  :root[data-theme="auto"] .footer {
    background: rgba(255, 255, 255, 0.9);
  }
}

/* ==========================================================================
   D2. DARK ISLANDS — NONE
   ==========================================================================

   This list is EMPTY, and keeping it empty is the point.

   An island is a page whose sheet is hard-coded dark, held open here until
   that sheet gains a light palette. Every entry has now been migrated:
   `.orgs` (organizations.css) took its light palette in section 20 of that
   sheet, and `.aids` (resources-aidigest.css, the AI Digest) — for a while
   documented here as PERMANENT, dark by design — now owns its own light and
   auto halves too.

   THE INVERTED BUG THIS GUARDS AGAINST. A stale entry is worse than a
   missing one. Once a sheet owns a light palette, an island rule here goes
   on forcing the DARK tokens onto it under `[data-theme="light"]`, and the
   page the author just made light silently stays dark — with nothing
   failing, because both halves are individually valid CSS. So: when a sheet
   gains a light palette, its entry comes OUT of this section in the SAME
   change. `resources/tests/test_aidigest_light_palette.py` pins that for
   the digest by refusing any line here that names `.aids` alongside
   `data-theme`.

   Adding an island back is therefore a deliberate act with an exit
   condition attached, never a default.
   -------------------------------------------------------------------- */

/* ==========================================================================
   E. THE APPEARANCE TOGGLE WIDGET
   ==========================================================================

   Markup: templates/_theme_toggle.html. Behaviour:
   staticfiles/js/theme-toggle.js. Everything below is built from the
   role tokens in section A, so the widget follows the theme it sets
   without a single duplicated colour.

   The widget renders TWICE per page — once in the desktop navbar
   (`.nav-auth`, which is `hidden md:flex`, so pointer-only at >=768px)
   and once inside `#mobile-nav-drawer`. The drawer is `overflow-y: auto`,
   which would CLIP an absolutely-positioned popover, so the compact
   variant expands in flow instead of floating. Same markup, same script,
   different containment.
   -------------------------------------------------------------------- */
/* The widget is hidden until <html> carries data-theme, which only
   happens once theme-init.js has run. With JavaScript disabled the theme
   cannot be changed OR stored, so a visible control would be a button
   that does nothing; this removes it from the page and the accessibility
   tree instead. There is no flash and no layout shift, because
   theme-init.js is synchronous and executes before this stylesheet is
   applied. theme-toggle.js re-stamps the attribute on init, so a failed
   theme-init.js fetch degrades to a late widget rather than no widget. */
.kais-theme {
  display: none;
}

:root[data-theme] .kais-theme {
  position: relative;
  display: inline-flex;
  flex-shrink: 0;
}

/* -- trigger ------------------------------------------------------------- */
.kais-theme__trigger {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  min-height: 40px;
  padding: 0.4rem 0.75rem;
  border: 1px solid var(--kais-border, #374151);
  border-radius: 999px;
  background: transparent;
  color: var(--kais-text, #ffffff);
  font: inherit;
  font-size: 0.875rem;
  font-weight: 600;
  line-height: 1.2;
  cursor: pointer;
  transition: background-color 150ms ease, border-color 150ms ease;
}

.kais-theme__trigger:hover {
  background-color: var(--kais-hover, rgba(255, 255, 255, 0.06));
}

/* A visible ring, never colour alone — the trigger sits on a translucent
   navbar whose backdrop varies with whatever is scrolled underneath it. */
.kais-theme__trigger:focus-visible,
.kais-theme__option:focus-visible {
  outline: 3px solid var(--kais-focus, #7dd3fc);
  outline-offset: 2px;
}

.kais-theme__icon {
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  display: block;
}

/* -- popover ------------------------------------------------------------- */
.kais-theme__popover {
  position: absolute;
  inset-block-start: calc(100% + 8px);
  inset-inline-end: 0;
  z-index: 60;
  min-width: 11.5rem;
  padding: 0.375rem;
  display: flex;
  flex-direction: column;
  gap: 2px;
  background-color: var(--kais-elevated, #111827);
  border: 1px solid var(--kais-border, #374151);
  border-radius: 12px;
  box-shadow: 0 20px 40px -10px var(--kais-shadow, rgba(0, 0, 0, 0.55));
}

.kais-theme__popover[hidden] {
  display: none;
}

/* -- options ------------------------------------------------------------- */
.kais-theme__option {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  width: 100%;
  min-height: 44px;
  padding: 0.5rem 0.625rem;
  border: 1px solid transparent;
  border-radius: 8px;
  background: transparent;
  color: var(--kais-text-2, #d1d5db);
  font: inherit;
  font-size: 0.875rem;
  font-weight: 600;
  line-height: 1.2;
  text-align: start;
  cursor: pointer;
  transition: background-color 150ms ease, color 150ms ease;
}

.kais-theme__option:hover {
  background-color: var(--kais-hover, rgba(255, 255, 255, 0.06));
  color: var(--kais-text, #ffffff);
}

/* Selection is carried by aria-checked, so the visual state and the
   accessible state cannot drift apart. It is marked with a filled
   surface AND a border AND a check glyph — never by colour alone. */
.kais-theme__option[aria-checked="true"] {
  background-color: var(--kais-surface, #374151);
  border-color: var(--kais-border, #374151);
  color: var(--kais-text, #ffffff);
}

.kais-theme__option-icon {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  display: block;
}

.kais-theme__option-label {
  flex: 1 1 auto;
}

.kais-theme__check {
  width: 14px;
  height: 14px;
  flex-shrink: 0;
  display: block;
  opacity: 0;
}

.kais-theme__option[aria-checked="true"] .kais-theme__check {
  opacity: 1;
}

/* -- compact variant: inside #mobile-nav-drawer -------------------------- */
/* Qualified with :root[data-theme] to match the specificity of the
   display gate above — a bare `.kais-theme--compact` (0,1,0) would lose
   to `:root[data-theme] .kais-theme` (0,2,0) and the drawer instance
   would silently render as an inline row. */
:root[data-theme] .kais-theme--compact {
  display: flex;
  flex-direction: column;
  width: 100%;
}

.kais-theme--compact .kais-theme__trigger {
  width: 100%;
  min-height: 44px;
  justify-content: flex-start;
  border-radius: 8px;
  color: var(--kais-drawer-text, #f3f4f6);
  border-color: var(--kais-drawer-border-mid, rgba(55, 65, 81, 0.6));
}

/* Static, not absolute: the drawer clips overflow, so a floating panel
   would be cut off rather than shown. */
.kais-theme--compact .kais-theme__popover {
  position: static;
  min-width: 0;
  margin-block-start: 6px;
  box-shadow: none;
  background-color: var(--kais-drawer-veil-1, rgba(255, 255, 255, 0.04));
  border-color: var(--kais-drawer-border-soft, rgba(55, 65, 81, 0.5));
}

.kais-theme--compact .kais-theme__option {
  color: var(--kais-drawer-text, #f3f4f6);
}

.kais-theme--compact .kais-theme__option[aria-checked="true"] {
  background-color: var(--kais-drawer-veil-3, rgba(255, 255, 255, 0.06));
  border-color: var(--kais-drawer-border-mid, rgba(55, 65, 81, 0.6));
}

/* -- motion -------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .kais-theme__trigger,
  .kais-theme__option {
    transition: none;
  }
}
