/* ═══════════════════════════════════════════════════════════════════════════
   KORVA APP SURFACES — Agent H2, Session 6.

   The logged-in application layer: kid home, parent dashboard, the four practice
   sections, the Weekly Challenge, the Weekly Mock, and the admin screens.

   WHAT THIS FILE IS FOR
   ---------------------
   H1 owns static/style.css and static/theme.css (the base system and the palette
   tokens). This file owns the APP surfaces on top of them. It therefore:

     1. carries a FALLBACK :root block for H1's tokens, so these pages render
        standalone in a worktree where theme.css does not exist yet;
     2. maps surfaces.css's `--ks-*` namespace onto those tokens, so one palette
        change moves every surface instead of two;
     3. restyles the app components themselves.

   ⚑ LOAD ORDER. This sheet must load AFTER style.css and AFTER surfaces.css /
   challenge.css / mock.css, because remapping `--ks-*` depends on winning the
   cascade against surfaces.css's own :root block (same specificity, later wins).
   base.html links it last for exactly that reason.

   ⚑ MEASURED CONTRAST — these are computed, not estimated. scratch_agent_h2/
   contrast.py re-derives them.

       #4f91ee on white   3.17   SURFACE ONLY — must never carry text
       #2f5fa8 on white   6.32   text OK
       #1e4478 on white   9.75   text OK
       #7e3df4 on white   5.44   text OK, and white-on-it is 5.44 at any size
       #d93df4 on white   3.54   LARGE text only (>=24px, or >=18.66px bold)
       #ee4f91 on white   3.41   LARGE text only
       #E63946 on white   4.17   LARGE text only  (a "4.8 WCAG-safe" claim is wrong)
       #1A1128 on white  18.19   text OK
       #6B6580 on white   5.53   text OK

   ⚑ WHITE ON --k-blue IS 3.17:1 AND FAILS AA. A button filled --k-blue with a
   normal-size white label is a contrast bug. Fill with --k-blue-text (6.32 against
   white) instead. Every fill-with-white-label in this file uses --k-blue-text,
   --k-blue-deep, --k-purple or --k-ink. --k-blue is used only for borders, meter
   fills, and marks that carry no text.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── Palette tokens — FALLBACK ONLY. ────────────────────────────────────────
   H1 authors these in static/theme.css. The names and values here are IDENTICAL
   to H1's, so once theme.css lands the merge is a no-op and whichever sheet
   loads later wins with the same result. Do not rename, and do not "improve" a
   value here — change it in theme.css.
   The one token H1 derives rather than fixes is --k-blue-hover; the value below
   is a plain darkening of --k-blue and should be re-checked after the merge. */
:root {
  --k-blue:          #4f91ee;   /* SURFACE ONLY — never text, never white-on-it */
  --k-blue-hover:    #3d7fdc;   /* H1 derives; surface only, same rule as above  */
  --k-blue-text:     #2f5fa8;
  --k-blue-deep:     #1e4478;
  --k-purple:        #7e3df4;
  --k-purple-bright: #d93df4;   /* LARGE text only */
  --k-magenta:       #ee4f91;   /* LARGE text only */
  --k-coral:         #E63946;   /* LARGE text only */
  --k-bg:            #FCFBFA;
  --k-card:          #FFFFFF;
  --k-tint:          #F8F7FC;
  --k-ink:           #1A1128;
  --k-muted:         #6B6580;
}

/* Derived values this sheet needs that are not part of H1's token contract.
   Kept separate so the block above stays a byte-for-byte match with theme.css. */
:root {
  --ka-line:        #E6E2EF;   /* hairline on --k-card / --k-bg */
  --ka-line-soft:   #F0EDF6;
  --ka-blue-50:     #EEF4FD;
  --ka-blue-100:    #D9E7FB;
  --ka-purple-50:   #F3ECFE;
  --ka-purple-100:  #E4D6FC;
  --ka-green:       #1e6b3a;   /* 6.52 on white */
  --ka-green-50:    #E6F6EC;
  --ka-amber:       #8a6100;   /* 5.54 on white */
  --ka-amber-50:    #FFF5E8;
  --ka-coral-ink:   #a02630;   /* 7.46 on white — the TEXT weight of --k-coral */
  --ka-coral-50:    #FDEAEC;

  --ka-r:           16px;
  --ka-r-sm:        11px;
  --ka-r-lg:        20px;
  --ka-shadow:      0 1px 2px rgba(26, 17, 40, 0.04), 0 8px 24px rgba(26, 17, 40, 0.06);
  --ka-shadow-lift: 0 10px 28px rgba(47, 95, 168, 0.14);
}

/* ── surfaces.css `--ks-*` remap ────────────────────────────────────────────
   surfaces.css was authored against a hand-written copy of the locked blue. Most
   of its values already equal the tokens; the PURPLE family did not (it used
   #5b3ba8, a much darker violet). Pointing the whole namespace at the tokens
   makes surfaces.css, challenge.css and this file one palette rather than three
   copies that drift. Values, not new names — surfaces.css is untouched.
   Specificity note: this is :root, same as surfaces.css's own block, so it wins
   only by load order. See the LOAD ORDER note at the top. */
:root {
  --ks-blue:        var(--k-blue);
  --ks-blue-deep:   var(--k-blue-text);
  --ks-blue-ink:    var(--k-blue-deep);
  --ks-blue-50:     var(--ka-blue-50);
  --ks-blue-100:    var(--ka-blue-100);

  --ks-purple:      var(--k-purple);
  --ks-purple-deep: #5b28c4;               /* darker --k-purple, for text on tint */
  --ks-purple-50:   var(--ka-purple-50);

  --ks-green:       var(--ka-green);
  --ks-green-50:    var(--ka-green-50);
  --ks-amber:       var(--ka-amber);
  --ks-amber-50:    var(--ka-amber-50);

  --ks-radius:      var(--ka-r);
  --ks-radius-sm:   var(--ka-r-sm);
}

/* ── App-surface neutrals ───────────────────────────────────────────────────
   ⚑ THIS IS THE ONE BASE-LEVEL OVERRIDE IN THIS FILE, and it is deliberately
   scoped, not global. `has-knav-bottom` is set by base.html only when
   session.parent_id or session.child_id exists, so it selects exactly the
   logged-in application and never a marketing page. Admin screens carry no
   session of that kind and are scoped by .ops-page instead.

   Each override is expressed AS a --k-* token rather than as a literal, so if H1
   retunes the palette these follow instead of fighting it. */
body.has-knav-bottom,
.ops-page {
  --bg:          var(--k-bg);
  --bg-soft:     var(--k-tint);
  --surface:     var(--k-card);
  --surface-2:   var(--k-tint);
  --surface-elev: var(--k-card);
  --ink:         var(--k-ink);
  --ink-soft:    var(--k-muted);
  --ink-mute:    #8B8598;
  --line:        var(--ka-line);
  --line-soft:   var(--ka-line-soft);

  /* ⚑ THE ACCENT FAMILY. style.css's --accent is warm orange (#E85D2F); the
     locked palette contains no orange at all, so leaving it would keep every
     button, ring, streak chip and progress bar on the app surfaces off-palette
     while the new cards around them are blue and purple.

     --accent is a FILL that white labels sit on, so it must be a colour that is
     safe with white: --k-blue-text is 6.32:1, --k-blue is 3.17 and would fail.
     --accent-hover deepens rather than lightens for the same reason.

     Every value is a --k-* token, not a literal, so if H1 retunes the palette
     these follow. If H1's theme.css sets --accent globally, note that this block
     is scoped to the logged-in body and therefore WINS here — reconcile by
     deleting this block, not by fighting it. */
  --accent:        var(--k-blue-text);
  --accent-hover:  var(--k-blue-deep);
  --accent-soft:   var(--ka-blue-50);
  --accent-border: var(--ka-blue-100);
  --blue-600:      var(--k-blue-text);
  --blue-500:      var(--k-blue);
  --blue-700:      var(--k-blue-deep);
  --blue-900:      var(--k-blue-deep);
  --blue-100:      var(--ka-blue-100);
  --blue-50:       var(--ka-blue-50);
  --primary:       var(--k-blue-text);
  --primary-dark:  var(--k-blue-deep);
  --primary-50:    var(--ka-blue-50);
  --primary-100:   var(--ka-blue-100);
  --terracotta:      var(--k-purple);
  --terracotta-soft: var(--ka-purple-50);
  --lilac:         var(--k-purple);
  --lilac-50:      var(--ka-purple-50);
  --mint:          var(--ka-green);
  --mint-50:       var(--ka-green-50);
  --coral:         var(--ka-coral-ink);   /* text weight — see --k-coral note */
  --coral-50:      var(--ka-coral-50);
  --gold:          var(--ka-amber);
  --gold-50:       var(--ka-amber-50);
  --shadow-glow:   0 6px 24px rgba(79, 145, 238, 0.22);

  /* ⚑ THE FOUR SECTION COLOURS. style.css paints these terracotta / forest /
     mauve / ochre, and they are the strongest remaining warm signal on the
     practice pages — the header spine, the section label, the option outline and
     the writing prompt's rail all read from them.

     They must stay four clearly distinct hues AND they are used as TEXT
     (.q-header__section sets `color`), so every one has to pass AA on white, not
     just look right. Measured: blue-text 6.32, purple 5.44, coral-ink 7.46,
     green 6.52. --k-coral itself (4.17) and --k-magenta (3.41) are large-text
     only and are deliberately NOT used here. */
  --sec-reading:  var(--k-blue-text);
  --sec-quant:    var(--ka-green);
  --sec-abstract: var(--k-purple);
  --sec-writing:  var(--ka-coral-ink);
  --sec-reading-soft:  var(--ka-blue-50);
  --sec-quant-soft:    var(--ka-green-50);
  --sec-abstract-soft: var(--ka-purple-50);
  --sec-writing-soft:  var(--ka-coral-50);
}
body.has-knav-bottom { background: var(--k-bg); }

/* ═══════════════════════════════════════════════════════════════════════════
   1. CONTRAST REPAIRS on sheets that predate the measurement
   ═══════════════════════════════════════════════════════════════════════════ */

/* challenge.css:34 painted .ch-eyebrow #4f91ee — 12px uppercase text at 3.17:1.
   It is the only place either sheet put --k-blue behind glyphs. */
.ch-eyebrow { color: var(--k-blue-text); }

/* admin_challenges.html filled .adm-tag--now with #4f91ee under a 10px WHITE
   label: 3.17:1, and 10px is nowhere near "large text". */
.adm-tag--now { background: var(--k-blue-text); color: #fff; }

/* Non-text uses of --k-blue are legitimate; tokenise them so they move with the
   palette rather than staying literals. */
.ch-status__ring     { border-color: var(--k-blue); background: var(--ka-blue-50); }
.ch-section__bar > span,
.mock-bar__fill      { background: var(--k-blue); }
.mock-note           { border-left-color: var(--k-blue); }

/* ═══════════════════════════════════════════════════════════════════════════
   2. NAVIGATION
   ═══════════════════════════════════════════════════════════════════════════ */

/* Renamed from `knav-group--soon`, which was a misnomer: it never expressed a
   "coming soon" state, only the separator rule that divides the weekly links
   from the section links inside the mobile burger panel. The old name outlived
   the shipped Weekly Challenge and read as a bug to anyone grepping for it.
   The old rule still sits in style.css (H1's file) matching nothing. */
@media (max-width: 900px) {
  .knav-group--weekly {
    padding-top: 6px;
    margin-top: 4px;
    border-top: 1px solid var(--line-soft);
  }
}

body.has-knav-bottom .knav-link--cta {
  background: var(--k-blue-text);
  border-color: var(--k-blue-text);
  color: #fff;
}
body.has-knav-bottom .knav-link--cta:hover,
body.has-knav-bottom .knav-link--cta:focus-visible {
  background: var(--k-blue-deep);
  border-color: var(--k-blue-deep);
}
body.has-knav-bottom .knav-link.is-active { color: var(--k-blue-text); }

/* ═══════════════════════════════════════════════════════════════════════════
   3. SHARED APP COMPONENTS  (`ka-` namespace)
   ═══════════════════════════════════════════════════════════════════════════ */

.ka-card {
  background: var(--k-card);
  border: 1px solid var(--ka-line);
  border-radius: var(--ka-r);
  padding: 18px 20px;
}

/* Section label above a block. Blue-text weight, so it passes at 11.5px. */
.ka-eyebrow {
  font-family: 'Lexend', system-ui, sans-serif;
  font-size: 11.5px; font-weight: 700; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--k-blue-text);
}

/* A number big enough to legitimately carry a large-text-only colour. 34px is
   comfortably past the 24px threshold, so --k-purple-bright/--k-magenta/--k-coral
   are allowed here and ONLY here. */
.ka-figure {
  font-family: 'Lexend', system-ui, sans-serif;
  font-weight: 700; font-size: 34px; line-height: 1; letter-spacing: -0.02em;
  color: var(--k-blue-deep);
}
.ka-figure--purple { color: var(--k-purple); }
.ka-figure--coral  { color: var(--k-coral); }   /* 4.17 — valid at 34px */

.ka-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }

/* ═══════════════════════════════════════════════════════════════════════════
   3b. KID HOME
   ═══════════════════════════════════════════════════════════════════════════ */

.kh-greet {
  display: flex; align-items: center; gap: 18px;
  flex-wrap: wrap; margin-bottom: 22px;
}
.kh-greet__main { flex: 1 1 220px; min-width: 0; }
.kh-greet__name {
  font-size: clamp(24px, 6.4vw, 31px); line-height: 1.1; margin: 0;
  letter-spacing: -0.02em;
}
.kh-greet__sub {
  margin-top: 3px; font-size: 14.5px; line-height: 1.45; color: var(--k-muted);
}
.kh-greet__count { flex: 0 0 auto; }

/* ═══════════════════════════════════════════════════════════════════════════
   3c. PRACTICE — ABSTRACT
   ⚑ The 640px figure cap is NOT set here. It lives in practice_abstract.html's
   own <style> block (`.abstract-stage { width: min(640px, 100%) }`) and the
   figures must never be scaled or cropped — abstract items are 100% visual.
   Nothing in this section touches the stage's width, height or transform.
   ═══════════════════════════════════════════════════════════════════════════ */

@media (min-width: 900px) {
  /* style.css lays this out as
         "stim prompt"
         "stim opts"
     with the stimulus spanning both rows. The stage is ~650px tall while the
     prompt is one line and the options ~330px, so the leftover ~280px was split
     between the two rows — which opened a large dead gap between the question
     stem and the answers, and pushed the options into the middle of the panel.
     A third, empty row soaks up the remainder BELOW the options instead. */
  .abstract-layout {
    grid-template-areas:
      "stim prompt"
      "stim opts"
      "stim ....";
    grid-template-rows: auto auto 1fr;
  }
}

/* The option cards hovered/selected to #B8860B (gold) — a leftover from the warm
   palette and the one place a child gets colour feedback before marking. Blue for
   the pending choice; correct/incorrect keep their own semantics below. */
.abstract-option:hover:not(.disabled) {
  border-color: var(--k-blue);
  box-shadow: 0 6px 14px rgba(79, 145, 238, 0.18);
}
.abstract-option.selected {
  border-color: var(--k-blue-text);
  box-shadow: inset 0 0 0 2px var(--k-blue-text);
}
.abstract-option.correct   { border-color: var(--ka-green);     background: var(--ka-green-50); }
.abstract-option.incorrect { border-color: var(--ka-coral-ink); background: var(--ka-coral-50); }
.abstract-option.disabled:hover { border-color: var(--ka-line); }
.abstract-option.disabled.correct:hover { border-color: var(--ka-green); }
.abstract-option-letter { color: var(--k-muted); }

/* ═══════════════════════════════════════════════════════════════════════════
   3d. PRACTICE — READING / WRITING
   ⚑ Both keep their SPLIT-PANEL layout. `.q-split`, `.q-split--reading`,
   `.q-page--split` and `.wr-split` are defined in style.css and are NOT
   redefined here — no grid-template-columns, no display change. This section
   only adds stickiness and colour.
   ═══════════════════════════════════════════════════════════════════════════ */

/* The passage is the thing every question refers back to, but it scrolled away
   after part B and the child had to scroll up and lose their place. Sticking it
   is purely additive — the two-column grid is untouched. */
@media (min-width: 1000px) {
  .q-split--reading > .q-passage-shell {
    position: sticky;
    top: 12px;
    max-height: calc(100vh - 32px);
    overflow-y: auto;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   3e. FO CHAT LAUNCHER
   `#fo-chat-trigger` is styled INLINE in _fo_chat.html, so a plain rule here
   cannot reach it — only `!important` can, which is why style.css already uses
   one for the mobile offset. The inline shadow is a hardcoded warm orange
   (rgba(224,138,28,...)) left over from the old accent; the button's fill is
   var(--primary) and already follows the palette.
   ═══════════════════════════════════════════════════════════════════════════ */
#fo-chat-trigger {
  box-shadow: 0 6px 20px rgba(47, 95, 168, 0.34) !important;
}

/* The launcher is position:fixed at bottom-right, so when the page is scrolled to
   the end it sits ON TOP of the footer's contact email — a real link, covered.
   Reserve the corner in the footer, but only on pages that actually have the
   launcher. :has() keeps this from padding every other page's footer. */
@media (min-width: 901px) {
  body:has(#fo-chat-trigger) .korva-footer-inner { padding-right: 210px; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   4. ADMIN
   ═══════════════════════════════════════════════════════════════════════════ */

/* The challenges table measured 548px of intrinsic width and overflowed a 375px
   viewport by 197px, which pushed the Review link — the only action on the
   screen — off the right edge with no way to reach it. The table keeps its shape
   and scrolls inside its own box; the PAGE never scrolls sideways. */
.adm-scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  border: 1px solid var(--ka-line);
  border-radius: var(--ka-r-sm);
  background: var(--k-card);
}
.adm-scroll > .adm-table { border: 0; border-radius: 0; min-width: 640px; }
.adm-scroll > .adm-table tr:last-child td { border-bottom: 0; }

/* ═══════════════════════════════════════════════════════════════════════════
   5. FOCUS — one visible ring everywhere, on the app surfaces.
   ═══════════════════════════════════════════════════════════════════════════ */
body.has-knav-bottom a:focus-visible,
body.has-knav-bottom button:focus-visible,
body.has-knav-bottom summary:focus-visible,
body.has-knav-bottom [tabindex]:focus-visible,
.ops-page a:focus-visible,
.ops-page button:focus-visible {
  outline: 3px solid var(--k-blue);
  outline-offset: 2px;
  border-radius: 6px;
}

/* ═══════════════════════════════════════════════════════════════════════════
   6. RESPONSIVE — every media block is LAST, for the reason surfaces.css states:
   media queries add no specificity, so a later plain rule restating the same
   property beats them at every width.
   ═══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 620px) {
  .ka-figure { font-size: 30px; }
}

/* Below ~560px the greeting's three flex children each get about a third of the
   width, which breaks the child's own name across two lines and turns the trial
   sentence into a five-line column. Give the countdown its own row instead. */
@media (max-width: 560px) {
  .kh-greet       { gap: 14px; }
  /* auto, not 100%: the name must stay BESIDE Fo on row one. Only the countdown
     wraps to row two. */
  .kh-greet__main  { flex: 1 1 auto; }
  .kh-greet__count { flex: 1 1 100%; justify-content: flex-start; margin-top: 2px; }
}
