/* ============================================================================
   v24-base.css — the elements.
   ----------------------------------------------------------------------------
   Loads after v24-tokens.css and before everything else. It styles ELEMENTS,
   never components: anything with a class belongs in a later layer. If a rule
   here needs a class to be correct, it is in the wrong file.

   It consumes tokens only. No colour, size, spacing or family literal appears
   below this line — a literal here is the beginning of a seventeenth palette.
   ============================================================================ */

*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

/* --- landing clear of the sticky header ------------------------------------
   A jump to #id must not put the heading under the header. Gate 2 did that
   with `scroll-padding-top` on `html`, which was wrong, and the way it was
   wrong is worth keeping:

   scroll-padding is a property of the SCROLLPORT, so it applies to every
   reason the browser ever scrolls something into view — including FOCUS. The
   header is `position: sticky` at the top, so its own buttons sit inside the
   padded strip. Tabbing onto the menu button while scrolled 400px down made
   the browser scroll the document to 166 to "clear" a button that was already
   fully visible, and the reader lost their place. Keyboard users got it on
   every migrated route; the drawer's scroll-restore got it too, because the
   page had already jumped before the drawer could record where it was.

   scroll-margin belongs to the TARGET instead, so it applies only when
   something is actually navigated to. Scoped to the content, which is the
   only place an in-page anchor on this site points.

   --sp-chrome-h is written onto the root element at runtime by
   assets/js/chrome-*.js, because a header that wraps at narrow widths is not
   the height a stylesheet guessed. The fallback is what applies before that
   script runs, and it is deliberately a little small: landing slightly low is
   a nudge, landing under the header is a lost heading. */
:is(#main, main) :is([id], :target),
#main { scroll-margin-top: calc(var(--sp-chrome-h, 64px) + var(--sp-4)); }

/* Smooth scrolling is a preference, not a default: it also makes programmatic
   scrolling animate, which breaks "jump to the current month" on a long page. */
@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}

body {
  margin: 0;
  background: var(--sp-ground);
  color: var(--sp-ink);
  font-family: var(--sp-body);
  font-size: var(--sp-t-body);
  line-height: var(--sp-lh-body);
  font-synthesis-weight: none;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
}

/* --- Indic scripts ---------------------------------------------------------
   NOT a nicety. A Latin fallback for Devanagari does not merely look wrong, it
   drops conjuncts, so the word stops being the word. assets/js/a11y.js marks
   each run with `lang` and these two rules route it. */
:lang(sa), :lang(hi), :lang(mr), [lang^="sa"], [lang^="hi"] {
  font-family: var(--sp-deva);
}
:lang(ta), [lang^="ta"] { font-family: var(--sp-tamil); }

/* --- headings --------------------------------------------------------------
   Sizes come from the ramp, not from a per-page decision. The estate carried
   NINE h1 sizes across 23 archetype pages; this is the one. */
h1, h2, h3, h4, h5, h6 {
  font-family: var(--sp-display);
  font-weight: 400;
  color: var(--sp-ink);
  margin: 0 0 var(--sp-4);
  line-height: var(--sp-lh-snug);
  text-wrap: balance;
}
h1 { font-size: var(--sp-t-h1); line-height: var(--sp-lh-tight); }
h2 { font-size: var(--sp-t-h2); margin-top: var(--sp-sec-tight); }
h3 { font-size: var(--sp-t-h3); margin-top: var(--sp-6); }
h4 { font-size: var(--sp-t-lg); margin-top: var(--sp-5); }
h5, h6 {
  font-family: var(--sp-sc);
  font-size: var(--sp-t-eyebrow);
  letter-spacing: var(--sp-track-eyebrow);
  text-transform: uppercase;
  color: var(--sp-ink-3);
  margin-top: var(--sp-5);
}
/* A heading that opens a container should not push it open. */
:is(h1, h2, h3, h4, h5, h6):first-child { margin-top: 0; }

p, ul, ol, dl, blockquote, figure, table, pre { margin: 0 0 var(--sp-4); }
:is(p, ul, ol, dl, blockquote, figure, table, pre):last-child { margin-bottom: 0; }

/* --- links -----------------------------------------------------------------
   The estate carried four link colours. One, and it is the action colour.
   The underline is offset rather than removed: an underline that touches the
   descenders of a serif face is harder to read than no underline at all. */
a {
  color: var(--sp-accent);
  text-decoration: underline;
  text-decoration-thickness: from-font;
  text-underline-offset: 0.16em;
  transition: color var(--sp-dur-fast) var(--sp-ease);
}
a:hover { color: var(--sp-accent-deep); }

/* --- focus -----------------------------------------------------------------
   THIS IS THE RULE THE CHROME SHEET USED TO DESTROY. A var() that resolves to
   nothing invalidates the whole declaration, and the chrome's copy of this rule
   — written against legacy token names and loaded last — left thirteen pages
   with no focus ring at all, across 122 keyboard stops. The alias block in the
   token sheet is what keeps that from happening while the chrome is still
   unmigrated. */
:focus-visible {
  outline: var(--sp-ring) solid var(--sp-accent);
  outline-offset: 2px;
  border-radius: var(--sp-r-sm);
}
/* Never remove a focus ring without replacing it. */
:focus:not(:focus-visible) { outline: none; }

/* A TEXT CONTROL IS ALWAYS focus-visible PER SPEC, and :focus-within is needed
   as well — tabbing into <input type="date"> focuses a segment inside its
   SHADOW ROOT, so the host matches :focus-within but not :focus, and the ring
   never appears. Stated directly so no later rule can leave a control without
   one.

   This pair lived only in assets/css/shared-48a7ba2fff82.css. Gate 2 ported
   that sheet's prefers-contrast and forced-colors blocks and missed this one;
   it went unnoticed until Gate 3d retired the sheet and
   tools/verify_home_a11y.js reported one of eighty tab stops with no ring. */
input:focus, select:focus, textarea:focus,
input:focus-within, select:focus-within, textarea:focus-within {
  outline: var(--sp-ring) solid var(--sp-accent);
  outline-offset: 2px;
}

/* --- lists ----------------------------------------------------------------- */
ul, ol { padding-left: var(--sp-5); }
li { margin-bottom: var(--sp-2); }
li:last-child { margin-bottom: 0; }

/* --- quotes, code, rules --------------------------------------------------- */
blockquote {
  margin-inline: 0;
  padding-left: var(--sp-5);
  border-left: 2px solid var(--sp-bronze-soft);
  color: var(--sp-ink-2);
}
code, kbd, samp, pre {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.9em;
}
pre {
  overflow-x: auto;
  padding: var(--sp-4);
  background: var(--sp-quiet);
  border-radius: var(--sp-r-sm);
}
hr {
  border: 0;
  border-top: 1px solid var(--sp-line);
  margin: var(--sp-sec-tight) 0;
}

/* --- media ----------------------------------------------------------------- */
img, svg, video, canvas, iframe { max-width: 100%; }
img, video { height: auto; }

/* --- tables ----------------------------------------------------------------
   The striped row is the quiet band, which is exactly why every semantic colour
   in the token sheet is contrast-checked against it. */
table { width: 100%; border-collapse: collapse; font-size: var(--sp-t-sm); }
caption {
  text-align: left;
  font-family: var(--sp-sc);
  font-size: var(--sp-t-eyebrow);
  letter-spacing: var(--sp-track-eyebrow);
  text-transform: uppercase;
  color: var(--sp-ink-3);
  padding-bottom: var(--sp-2);
}
th, td {
  text-align: left;
  padding: var(--sp-2) var(--sp-3);
  border-bottom: 1px solid var(--sp-line);
  vertical-align: top;
}
th { font-family: var(--sp-sc); font-weight: 400; color: var(--sp-ink-2); }
tbody tr:nth-child(even) { background: var(--sp-quiet); }
/* Digits that sit in a column line up. */
td:has(+ td), td { font-variant-numeric: tabular-nums; }

/* --- forms -----------------------------------------------------------------
   Inherit the page's type rather than the platform's. 16px minimum on the
   font-size of a text input, or iOS zooms the viewport on focus. */
input, select, textarea, button {
  font: inherit;
  color: inherit;
}
input, select, textarea {
  font-size: max(1rem, var(--sp-t-sm));
  background: var(--sp-paper);
  border: 1px solid var(--sp-line-strong);
  border-radius: var(--sp-r-sm);
  padding: var(--sp-2) var(--sp-3);
}
button { cursor: pointer; }
fieldset { border: 0; padding: 0; margin: 0 0 var(--sp-4); }
legend {
  padding: 0;
  font-family: var(--sp-sc);
  font-size: var(--sp-t-eyebrow);
  letter-spacing: var(--sp-track-eyebrow);
  text-transform: uppercase;
  color: var(--sp-ink-3);
}

/* --- small print, emphasis ------------------------------------------------- */
small { font-size: var(--sp-t-sm); color: var(--sp-ink-3); }
strong, b { font-weight: 600; }
abbr[title] { text-decoration-style: dotted; }

/* --- the skip link ---------------------------------------------------------
   `.sp-skip` is the system's name, for markup the system writes. The skip link
   that exists TODAY is `class="skip-link"`, written into all 219 routes by
   tools/apply_chrome.py, and it is styled by chrome-*.css, which loads on
   every route migrated or not.

   Gate 2 aliased `.skip-link` onto this rule, on the reasoning that a migrated
   page no longer loads the legacy sheet that styled it. That was wrong about
   which sheet styled it, and the alias made two sheets own one object — which
   verify_component_governance.py rule 1 caught the moment it was extended to
   judge the chrome. The chrome owns it until the chrome migrates. */
.sp-skip {
  position: absolute;
  left: var(--sp-3);
  top: calc(-1 * var(--sp-9));
  z-index: calc(var(--sp-z-header) + 1);
  padding: var(--sp-2) var(--sp-4);
  background: var(--sp-deep);
  color: var(--sp-ground);
  border-radius: var(--sp-r-sm);
  text-decoration: none;
  transition: top var(--sp-dur-fast) var(--sp-ease);
}
.sp-skip:focus { top: var(--sp-3); color: var(--sp-ground); }

/* --- high contrast ---------------------------------------------------------
   PORTED HERE ON 2026-10-03 (v25 Gate 2), NOT WRITTEN FRESH. Both blocks come
   from assets/css/shared-48a7ba2fff82.css, which a migrated page stops loading
   because it is bound to the legacy token names. Had they simply been dropped,
   every migrated route would have silently lost high-contrast and
   forced-colours support — the two settings whose users are least able to
   report that something looks wrong.

   The TOKEN half of prefers-contrast lives in v24-tokens.css, because deciding
   what a token resolves to is that file's entire job and a media query does
   not change whose job it is. What is left here is the element rules. */
@media (prefers-contrast: more) {
  ::placeholder { color: var(--sp-ink); opacity: 1; }
}

/* forced-colors: the user's palette replaces ours, so a control whose shape
   came only from a background fill vanishes into the page. Each one is given an
   explicit boundary. `outline` rather than `border` so no box changes size, and
   forced-color-adjust stays auto: the user's colours win, we only make sure the
   edges are still there. */
@media (forced-colors: active) {
  .sp-cta, .sp-btn, .sp-tag, .sp-card,
  button, [type="button"], [type="submit"], [role="button"],
  input, select, textarea {
    forced-color-adjust: auto;
    outline: 1px solid ButtonText;
    outline-offset: -1px;
  }
  /* These carry their meaning in a fill alone. */
  .sp-prov::before { outline: 1px solid CanvasText; }
  /* Once every colour collapses to LinkText, the underline is the only thing
     still distinguishing a link from the sentence around it. */
  a:not([class*="btn"]):not([class*="cta"]):not(.sp-brand) { text-decoration: underline; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* Print: the chrome is furniture and does not belong on paper. */
@media print {
  body { background: #fff; color: #000; }
  a { text-decoration: underline; }
  header, footer, nav, .sp-skip { display: none !important; }
}
