/* ===========================================================================
 * shared-chrome.css — the APP-WIDE rules the vocab sheet used to smuggle
 * ===========================================================================
 * Split out of subjects/vocab/styles.css (usability finding C6). Everything
 * here is selected by markup OUTSIDE the vocab app — the static shell in
 * index.html (`header.topbar`, the `.icon-btn` topbar buttons), the shared
 * <ModeHeader> in shared-app-shell.jsx (`.mode-header*`, `.home-link-*`,
 * `.progress-bar-*`, `.mode-progress-chip`), every `.mode-screen` sub-surface
 * (editing lab, guided math, practice home, admin queue, why-lessons, year
 * tests), and the document-level element rules. It used to load LAST (after
 * every component sheet) purely because it rode along with the vocab styles;
 * it now loads right after app-surface.css, BEFORE every subject/component
 * sheet.
 *
 * CASCADE NOTE — the move is order-preserving. These rules previously beat
 * index.html's inline <style> (`.icon-btn`, `header.topbar`'s padding/gap,
 * `:root` tokens) and they still do: the inline block is in <head> above the
 * app-surface.css link, so it is still earlier in document order. No selector
 * in this file collides at equal specificity with anything in the sheets that
 * load between here and the vocab sheet (reading, math, tool-btn, hint,
 * mc-grid, mc-item, analogy-item, passage-item, passage-edit-item,
 * anchor-paper, focus-rings, editing) — verified sheet by sheet.
 *
 * The :root block is the app's TOKEN source, not vocab decoration:
 * --radius / --radius-sm / --radius-lg / --shadow* / --font-mono have no
 * other definition anywhere, and mc-item.css, analogy-item.css,
 * passage-item.css, passage-edit-item.css, mc-grid.css, hint.css and
 * tool-btn.css all consume them. foundation.js's activateTheme() re-binds most
 * of the colour tokens as inline styles on <html> at runtime; these values are
 * the pre-paint fallback plus the sole source for the ones it doesn't set.
 *
 * Anything vocab-only lives on in subjects/vocab/styles.css, scoped under
 * :where(.vocab-app) (the class on VocabApp's root in subjects/vocab/app.jsx).
 * :where() is used deliberately: it adds ZERO specificity, so scoping changed
 * only what those rules MATCH, never which rule wins a tie.
 * ========================================================================= */
:root {
  /* Bookish — warm paper, deep ink, warm brown accent (from reference projects) */
  --cream: #F4EFE6;
  --cream-2: #EDE5D6;
  --cream-3: #E4D9C3;
  --surface: #FBF8F2;
  --surface-2: #EDE5D6;
  --ink: #2A2620;
  --ink-soft: #6B6355;
  --ink-faint: #A39A88;
  --ink-70: rgba(42, 38, 32, 0.72);
  --ink-50: rgba(42, 38, 32, 0.5);
  --ink-30: rgba(42, 38, 32, 0.3);
  --ink-10: rgba(42, 38, 32, 0.1);
  --ink-5: rgba(42, 38, 32, 0.05);

  --accent-coral: #C96F4A;
  --accent-coral-tint: #F1E0D3;
  --accent-coral-ink: #8A3F21;

  --accent-lavender: #7A5A3A;
  --accent-lavender-tint: #E9DEC9;
  --accent-lavender-ink: #5A3E1F;

  --accent-sage: #5B7A4A;
  --accent-sage-tint: #DDE6CF;
  --accent-sage-ink: #3D5630;

  --accent-marigold: #C89C4A;
  --accent-marigold-tint: #EFE1C1;
  --accent-marigold-ink: #7A5A26;

  --bg: var(--cream);

  --radius-inset: 4px;   /* read-only panes — near-square, see CLAUDE.md */
  --radius-sm: 10px;
  --radius: 16px;
  --radius-lg: 20px;
  /* The reserved height of a pinned drill action bar (VI-20): the TALLEST beat
     any of them holds — the workout/quiz advance (a 52px lg button plus the
     bar's 12px padding pair) — so the button sits at one height across beats
     AND across the four drills, instead of the root drill's shorter bar moving
     the target every time a learner crossed between them. Excludes the safe
     area, which each bar adds to its own bottom padding. */
  --action-bar-min-h: 76px;
  --shadow-sm: 0 1px 2px rgba(42,38,32,0.04), 0 2px 6px rgba(42,38,32,0.04);
  --shadow: 0 1px 2px rgba(42,38,32,0.03), 0 8px 24px rgba(42,38,32,0.05);
  --shadow-lg: 0 2px 4px rgba(42,38,32,0.04), 0 16px 40px rgba(42,38,32,0.08);

  --font-display: "Lexend", -apple-system, system-ui, sans-serif;
  --font-serif-body: "Iowan Old Style", "Charter", Georgia, serif;
  --font-ui: "Geist", -apple-system, system-ui, sans-serif;
  --font-mono: "Geist Mono", ui-monospace, "SF Mono", Menlo, monospace;

  /* Keyboard focus ring — ONE definition for the whole app. The browser
     default is `1px auto rgb(0,95,204)`, a system blue that is the only
     unbranded pixel on a cream/warm palette (usability finding P2.8). 2px in
     the live theme accent, offset so the ring reads as a halo around the
     control rather than a border on it. components/items/focus-rings.css
     aliases its --item-focus-* onto these so the choice rows stay identical.
     Fallback hex matches that sheet's. */
  --focus-color: var(--accent, #B86E3E);
  --focus-width: 2px;
  --focus-offset: 2px;
}


* { box-sizing: border-box; }

html, body { margin: 0; padding: 0; height: 100%; }

body {
  font-family: var(--font-ui); color: var(--ink);
  background: var(--cream-2); /* page frame color behind the mobile shell */
  font-feature-settings: "ss01", "cv11";
  -webkit-font-smoothing: antialiased;
  transition: background var(--dur-settle) var(--ease-settle), color var(--dur-settle) var(--ease-settle);
}


::-webkit-scrollbar { width: 0; height: 0; }


/* ---------- SHARED: THE SCRIM UNDER A SHEET ----------
   ONE backdrop for every modal in the app (2026-09-19 study S-55). The shell's
   own overlays (the sign-in gate, the onboarding scrim) dim with a cream-
   preserving mix plus a blur — "cream backdrop everywhere, never dark" — while
   sheets written later each hand-rolled an `rgba(28,22,18,0.42)` wash, so the
   reminder sheet went flat grey-brown over the same home screen the sign-in
   sheet had blurred one set earlier. A sheet spreads THIS class and adds only
   its own layout (`align-items`, `z-index`, padding).
   The blur is what carries "same page, one layer up"; the mix keeps the paper's
   temperature so the sheet's own cream still wins on contrast. */
.app-scrim {
  position: fixed; inset: 0;
  background: color-mix(in srgb, var(--bg-deep) 45%, transparent);
  backdrop-filter: blur(14px) saturate(1.1);
  -webkit-backdrop-filter: blur(14px) saturate(1.1);
}

/* ---------- SHARED: TOPBAR + CARDS ---------- */
.topbar {
  display: flex; align-items: center; gap: 12px;
  padding: 16px 20px 12px;
}


.icon-btn {
  background: var(--surface); border: 1px solid var(--ink-10); border-radius: 999px;
  width: 36px; height: 36px; display: inline-flex; align-items: center; justify-content: center;
  cursor: pointer; color: var(--ink);
  transition: background var(--dur-quick) var(--ease-settle);
  /* A button does not inherit the page font: the "Aa" glyph rendered in the
     UA default (Arial) — the only non-Lexend UI string in the shell (D20). */
  font-family: var(--font-ui); font-size: inherit;
}

.icon-btn:hover { background: var(--cream-2); }

.icon-btn.small { width: 28px; height: 28px; }
/* NO `.small` HIT-BOX OVERRIDE (2026-09-19 study S-38). There used to be an
   `inset: -8px` rule here, written when `.icon-btn::after` was a plain -4px
   inset. That overlay is now SIZED — centred on the control at a 44px floor
   whatever the glyph measures (app-surface.css, "Minimum touch boxes") — so the
   override was both redundant and wrong: `inset` wins on specificity for
   left/top while the base rule's `transform: translate(-50%, -50%)` still
   applies, which shifted the 44px box 22px up and left of the 28px button it
   belongs to. Deleting it leaves .small at a centred 44, like every other
   .icon-btn. */


/* ================= MODE SCREENS (shared) ================= */
/* No `min-height: 100vh` here: .mode-screen lives inside main.stage, which
   carries the text-size `zoom`, so a vh resolved in layout px and was THEN
   painted ×1.1 — every mode screen in the app grew a phantom tenth of a page.
   It painted nothing either way; the cream fill is `.shell`'s (and
   `.shell-body`'s at desktop), both outside the zoom. */
.mode-screen { padding: 0 0 40px; }


/* A ROW THAT CANNOT FIT WRAPS — IT NEVER CLIPS (2026-09-19 study S-07/S-41).
   At the Largest Aa rung the runner's rail measured 306px of tools inside a
   375px viewport: the scratchpad button's right edge landed at 403 (half of it
   off screen) and at 320 it was gone entirely, with the Flag clipped after it.
   Nothing scrolled — `.mode-header` and `.mode-header-right` are both
   overflow: visible and the document width never grew — so the only way out of
   a live question and the pad a Quantitative set needs were simply not there.
   `flex-wrap` costs NOTHING while the row fits (a flex line only breaks when
   the items' own content widths exceed it), so every screen below the largest
   rungs renders the identical single row it did before. The scratchpad's own
   control row has followed this rule since NEW-2; this is the same rule for the
   rail the shell owns. */
.mode-header {
  display: flex; align-items: center; gap: 12px;
  flex-wrap: wrap; row-gap: 6px;
  padding: 10px 20px 10px;
}

/* PINNED DRILL CHROME (VI-20, PracticeHeader `sticky`).
   On a question surface the header is not decoration: it carries the only exit,
   the round name and the position, and it used to scroll away exactly when a
   long reveal made all three useful. Opt-in per screen — read-only pages (a
   plan, a summary, a list) still let it scroll, because pinning chrome over
   content nobody is answering only spends the fold.
   The wrapper pins and PAINTS (content must not show through a chrome strip);
   its three children keep their own classes, so app-surface.css's per-element
   column and gutter rules still align them with the body below.
   The topbar is `position: relative` and scrolls away, and at desktop it does
   not exist at all, so there is no chrome above this to offset for — but
   `top` IS THE SAFE-AREA INSET, NOT 0 (S-23, measured on a native iPhone).
   index.html paints a FIXED strip over `env(safe-area-inset-top)`
   (`body::before`, z-index 52) so content passes under a legible status bar. A
   wrapper pinned at `top: 0` parks itself UNDER that strip: the bar, the Aa,
   the step chip and the progress track were all drawn behind it, and the only
   row left visible was the eyebrow hanging below the inset — so a header that
   was pinning correctly read as one that does not pin at all, on exactly the
   screens the pin/scroll rule says must pin. The runner's `.runner-rail` has
   pinned at the inset since SI-12; this is the same correction for every OTHER
   pinned header (the Rules Lab stepper, the vocab and root drills, the session
   browse). `::before` repaints the sliver the fixed strip used to own, in this
   wrapper's ground and at its layer — `bottom: 100%` of a sticky box is always
   the band immediately above it. env() is 0 in a browser, so off-device this is
   the old `top: 0` rule with a zero-height painter. */
.mode-headwrap--sticky {
  position: sticky;
  top: env(safe-area-inset-top, 0px);
  z-index: 5;
  background: var(--bg);
  /* The hairline appears only once something has scrolled under it — but a
     conditional border needs a scroll listener, and the strip reads as part of
     the page without one. A permanent, very quiet edge is the cheaper honest
     answer: it says "this is chrome" at rest too. */
  border-bottom: 1px solid var(--line);
  margin-bottom: 14px;
}
.mode-headwrap--sticky::before {
  content: '';
  position: absolute; left: 0; right: 0; bottom: 100%;
  height: env(safe-area-inset-top, 0px);
  background: var(--bg);
  pointer-events: none;
}
/* The eyebrow's own bottom margin was the gap under the header; inside the
   pinned strip that margin would be painted chrome. The wrapper carries it. */
.mode-headwrap--sticky .mode-header-eyebrow { margin-bottom: 8px; }

.home-link-btn {
  display: inline-flex; align-items: center; gap: 4px;
  background: transparent; border: none; padding: 0; cursor: pointer;
  color: var(--accent-coral); font-size: 15px; font-weight: 600;
  font-family: var(--font-ui, inherit);
  flex-shrink: 0;
}

.home-link-btn:hover { opacity: 0.8; }

/* Destination label next to the ‹ chevron (e.g. "Home", "Rules"). */
/* NEW-B8: line-height 1 is the glyph box, and at the big Aa rungs the render
   clipped the top of "Pause". 1.25 leaves the ascender room; the button is
   inline-flex/centre, so nothing moves. */
.home-link-label { font-size: 15px; font-weight: 600; line-height: 1.25; }

/* Legacy inline title — kept for any un-migrated caller, but the canonical
   placement is now the below-bar eyebrow (.mode-header-eyebrow). */
.mode-header-title {
  flex: 1; font-family: var(--font-display);
  font-size: 18px; font-weight: 600; line-height: 1.1;
  text-align: left; padding-left: 4px;
}

/* Activity title — below-bar eyebrow (small-caps), generalized from the
   exam screen's .exam-qtype so every surface labels its activity the same
   way without crowding the top bar. */
.mode-header-eyebrow {
  font-size: 11px; text-transform: uppercase; letter-spacing: 0.1em;
  color: var(--ink-soft); font-weight: 700;
  font-family: var(--font-ui, inherit);
  padding: 0 20px; margin-bottom: 14px;
}

/* In-bar variant (PracticeHeader inlineTitle): occupies the bar's flex space
   instead of its own row below. */
.mode-header-eyebrow--inline {
  flex: 1; min-width: 0; padding: 0; margin: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* min-width: 0 is load-bearing, not tidiness (study P0.1): the runner's rail
   rides in here, and its counter carries text-overflow: ellipsis as the row's
   declared safety valve. A flex item defaults to min-width: auto, so without
   this the block refuses to shrink below its content and grows past the
   header instead — which pushed the karalama button off the right edge of a
   402pt screen during a deneme. The buttons themselves keep flex-shrink: 0
   (.icon-btn), so the counter is the one thing that gives.
   …and the block itself WRAPS for the same reason (S-07): once the rail holds
   a clock, a flag, the pad and the Aa rung, 320pt at the largest text size
   cannot hold them on one line even with the label gone. It keeps its
   right-alignment on every line, so the tools stay under each other rather
   than drifting across the bar. */
.mode-header-right {
  display: flex; align-items: center; gap: 8px; min-width: 0;
  flex-wrap: wrap; row-gap: 6px; justify-content: flex-end;
}

.mode-header-meta { display: flex; gap: 6px; }

/* Runner rail overrides (study P0.4). Scoped to `.runner-rail` because they
   change shared chrome (.mode-header, .home-link-btn) for THAT bar only — the
   runner is the one surface where the row is genuinely full:
     · the back button may shrink and its label truncate. Shared chrome pins
       it at flex-shrink 0, which is what made the icons lose;
     · the tools never shrink and never wrap — they are the reason the bar
       exists;
     · the ‹ chevron keeps a 44pt hit box even with the label gone (study
       P1.15: it measured 22×22 in a deneme, and it is the only way out).
       That pad is no longer restated here: `.home-link-btn::after`
       (app-surface.css) took the rail's own -13 as the shared value and states
       its height as a 44px FLOOR rather than an inset, which is what the rail
       needed. An override that matched it exactly would only be one more place
       to forget. */
.runner-rail .mode-header { min-width: 0; }
/* NO `overflow: hidden` HERE, EVER (2026-09-19 study L1-16). It was added for
   the label truncation, but an overflow other than `visible` establishes a clip
   on the whole box and takes the ::after hit pad below with it — the control
   measured 17×17, the only way out of a live question. The truncation it was
   for is already the LABEL's job one line down (min-width:0 + ellipsis), so the
   button never needs to clip: at min-width the label collapses to zero and only
   the 18px chevron is left. */
.runner-rail .home-link-btn { flex-shrink: 1; min-width: 18px; }
.runner-rail .home-link-btn > svg { flex: 0 0 auto; }
.runner-rail .home-link-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The tools, the clock and the score never give ground; the counter is the
   rail's designated safety valve and carries flex: 0 1 auto inline. */
.runner-rail .mode-header-right > span > * { flex: 0 0 auto; }

.mode-progress-chip {
  background: var(--ink); color: var(--cream); font-variant-numeric: tabular-nums;
  padding: 5px 11px; border-radius: 999px; font-size: 12px; font-weight: 600;
}


/* When an eyebrow follows the bar, keep them tight (10px); the eyebrow adds
   its own 14px below. When the bar is the last header child (no eyebrow),
   restore the original 18px breathing room. */
.progress-bar-track {
  height: 5px; background: var(--ink-5); border-radius: 99px; overflow: hidden;
  margin: 0 20px 10px;
}

.progress-bar-track:last-child { margin-bottom: 18px; }

.progress-bar-fill {
  /* Flat, not a gradient: both stops resolve to theme.accent, so the gradient
     was a no-op between one colour and itself (usability D21). */
  height: 100%; background: var(--accent-coral);
  transition: width var(--dur-settle) var(--ease-settle);
}


.mode-body { padding: 0 20px; }

/* 16px is the floor, not a preference: iOS Safari zooms the whole page when a
   focused field is under 16px, and nothing zooms it back (usability A11/B13). */
input, select, textarea {
  font-family: inherit; font-size: 16px; padding: 8px 12px; border: 1px solid var(--ink-10);
  border-radius: 10px; background: var(--bg); color: var(--ink); width: 100%;
}

textarea { font-family: var(--font-mono); font-size: 16px; resize: vertical; }

input:focus, select:focus, textarea:focus { outline: 2px solid var(--accent-coral); outline-offset: -1px; border-color: transparent; }

/* Placeholders: a project token, not Chrome's grey. The UA default resolves to
   rgb(117,117,117) = 4.35:1 on the input's cream fill — just under AA (P2.11).
   --ink-soft is 5.6:1 on --surface and still 4.7:1 on the deepest cream the
   app paints an input on. `opacity: 1` because Firefox dims placeholders on
   top of the colour, which would undo the ratio. */
input::placeholder,
textarea::placeholder { color: var(--ink-soft); opacity: 1; }

/* ---------- Keyboard focus ring (app-wide) ----------
 * Everything focusable that does not paint its own ring gets the branded one.
 * :focus-visible only, so a mouse click never draws it; inputs keep their own
 * inset rule above (an input's ring belongs on its border, not around it).
 * Low specificity by design — a component that needs a different shape sets
 * its own outline (see components/items/focus-rings.css for the choice rows). */
:focus-visible {
  outline: var(--focus-width) solid var(--focus-color);
  outline-offset: var(--focus-offset);
}

/* High-contrast: the system colours must win, same as focus-rings.css. */
@media (forced-colors: active) {
  :focus-visible { outline: 2px solid CanvasText; outline-offset: 2px; }
}

/* An animation name is global and cannot be scoped, so any name the shell
   animates with has to live here. index.html's mobile bottom sheet
   (`animation: fadeIn 0.3s`) and `.confirm-scrim.open` both reference this and
   nothing else defines it — the vocab sheet was their only source. The
   vocab-only names (slideUp / pop) stayed behind: mc-item.css and
   analogy-item.css have their own `mc-shake`/`an-shake` and index.html its
   own `focus-ask-pop`, so none of them collide. */
@keyframes fadeIn { from { opacity: 0; } }

/* --- NEW-B4: a long lab title wraps between WORDS, never inside one --------
 * `zoom` scales the BOX with the type, so at the two big Aa rungs the pill's
 * text column is narrower in layout px than at Standard while the word is the
 * same length in them. The word does not give way — the chrome does: a
 * narrower icon chip and tighter gutters hand the column back the ~20px
 * "comprehension" needs. Keyed off the root's own rung stamp (applyA11y in
 * index.html), so it follows the ladder with no JS. */
.skillpill-title { overflow-wrap: normal; word-break: normal; hyphens: none; }
.skillpill-chip { display: flex; }
:root[data-a11y-scale="1.35"] .skillpill-row,
:root[data-a11y-scale="1.6"] .skillpill-row { gap: 8px; }
:root[data-a11y-scale="1.35"] .skillpill-chip > span,
:root[data-a11y-scale="1.6"] .skillpill-chip > span {
  width: 28px; height: 28px; border-radius: 8px;
}
/* …and at Largest the chip GOES (ship smoke B). Shrinking it still left
   "comprehension" touching the chevron on a 390pt phone: 28px + the gutter is
   about the width the word is short by, and the chip is decoration — the
   eyebrow above the title already says which lab this is. The chevron and the
   word both keep their space; only the ornament pays. */
:root[data-a11y-scale="1.6"] .skillpill-chip { display: none; }
