/*
  base.css — reset, font loading, text styles, layout primitives.
  Depends on tokens.css. Contains no raw hex/px color or spacing values —
  everything here reads from custom properties.
*/

/* ============================================================
   FONT LOADING
   Self-hosted, latin subset, two variable files. font-display: swap
   plus metric-matched fallback faces so the swap produces ~0 CLS.
   Fallback metrics are close approximations (Arial/Georgia-based);
   revisit with a metrics tool (e.g. Capsize, Fontpie) once real
   traffic makes fine-tuning worthwhile — approximate today is far
   better than the unadjusted-fallback default of most sites.
   ============================================================ */

@font-face {
  font-family: "Fraunces";
  src: url("../fonts/fraunces-var.woff2") format("woff2-variations"),
       url("../fonts/fraunces-var.woff2") format("woff2");
  font-weight: 300 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215;
}

@font-face {
  font-family: "Inter";
  src: url("../fonts/inter-var.woff2") format("woff2-variations"),
       url("../fonts/inter-var.woff2") format("woff2");
  font-weight: 400 600;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215;
}

@font-face {
  font-family: "Fraunces Fallback";
  src: local("Georgia");
  ascent-override: 95%;
  descent-override: 28%;
  line-gap-override: 0%;
  size-adjust: 106%;
}

@font-face {
  font-family: "Inter Fallback";
  src: local("Arial");
  ascent-override: 90%;
  descent-override: 22%;
  line-gap-override: 0%;
  size-adjust: 107%;
}

/* ============================================================
   RESET — minimal, modern
   ============================================================ */

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

* {
  margin: 0;
}

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

body {
  min-height: 100svh;
  font-family: var(--font-text);
  font-weight: var(--fw-regular);
  font-size: var(--t-base);
  line-height: 1.6;
  color: var(--color-text);
  background: var(--color-bg);
  -webkit-font-smoothing: antialiased;
}

img, picture, svg, video, canvas {
  display: block;
  max-width: 100%;
}

img {
  height: auto;
}

input, button, textarea, select {
  font: inherit;
  color: inherit;
}

button {
  cursor: pointer;
  background: none;
  border: none;
  padding: 0;
  text-align: inherit;
}

a {
  color: inherit;
  text-decoration: none;
}

ul, ol {
  list-style: none;
  padding: 0;
}

table {
  border-collapse: collapse;
}

/* Widow/orphan control per §A2 */
h1, h2, h3, h4 {
  text-wrap: balance;
}

p, blockquote, figcaption {
  text-wrap: pretty;
}

::selection {
  background: var(--color-selection);
  color: var(--color-text);
}

/* ============================================================
   FOCUS — visible for keyboard, silent for pointer
   ============================================================ */

:focus {
  outline: none;
}

:focus-visible {
  outline: var(--focus-ring-width) solid var(--color-focus);
  outline-offset: var(--focus-ring-offset);
  border-radius: var(--radius-sm);
}

/* ============================================================
   TEXT STYLES — six family/weight combinations total.
   Fraunces 400/500/600, Inter 400/500/600.
   ============================================================ */

/* ============================================================
   FRAUNCES VARIABLE AXES (§A2)
   Fraunces was chosen over Playfair Display specifically for its
   opsz / SOFT / WONK axes — without these it renders as a generic
   serif and the typographic character the brand is built on is absent.

   Note: declaring font-variation-settings overrides
   font-optical-sizing: auto, so `opsz` must be set explicitly here.
   opsz is bound to the size tier (display sizes take a high optical
   size — tighter, higher-contrast letterforms; smaller headings take
   a lower one so they don't go spindly).
   ============================================================ */

.text-display-xl,
.text-display-l {
  font-variation-settings: "opsz" 144, "SOFT" 30, "WONK" 1;
}

h1, .text-h1,
h2, .text-h2 {
  font-variation-settings: "opsz" 72, "SOFT" 30, "WONK" 1;
}

h3, .text-h3,
.text-quote,
.text-stat {
  font-variation-settings: "opsz" 36, "SOFT" 30, "WONK" 1;
}

/* Subtitles are italic display text at body scale — WONK off here, the
   swashed forms read as noise at this size. */
.text-subtitle {
  font-variation-settings: "opsz" 24, "SOFT" 30, "WONK" 0;
}

.text-display-xl {
  font-family: var(--font-display);
  font-size: var(--t-4xl);
  font-weight: var(--fw-medium);
  line-height: 1.02;
  letter-spacing: -0.03em;
  max-width: 20ch;
}

.text-display-l {
  font-family: var(--font-display);
  font-size: var(--t-3xl);
  font-weight: var(--fw-medium);
  line-height: 1.05;
  letter-spacing: -0.025em;
  max-width: 20ch;
}

h1, .text-h1 {
  font-family: var(--font-display);
  font-size: var(--t-2xl);
  font-weight: var(--fw-medium);
  line-height: 1.10;
  letter-spacing: -0.02em;
}

h2, .text-h2 {
  font-family: var(--font-display);
  font-size: var(--t-xl);
  font-weight: var(--fw-medium);
  line-height: 1.18;
  letter-spacing: -0.015em;
}

h3, .text-h3 {
  font-family: var(--font-display);
  font-size: var(--t-lg);
  font-weight: var(--fw-semibold);
  line-height: 1.25;
  letter-spacing: -0.01em;
}

h4, .text-h4 {
  font-family: var(--font-text);
  font-size: var(--t-md);
  font-weight: var(--fw-semibold);
  line-height: 1.35;
}

.text-subtitle {
  font-family: var(--font-display);
  font-size: var(--t-md);
  font-weight: var(--fw-regular);
  font-style: italic;
  line-height: 1.45;
  color: var(--color-text-secondary);
}

.text-body-l {
  font-family: var(--font-text);
  font-size: var(--t-md);
  font-weight: var(--fw-regular);
  line-height: 1.6;
}

.text-body-s, small {
  font-family: var(--font-text);
  font-size: var(--t-sm);
  font-weight: var(--fw-regular);
  line-height: 1.5;
}

.text-label {
  font-family: var(--font-text);
  font-size: var(--t-sm);
  font-weight: var(--fw-medium);
  line-height: 1.4;
}

.text-eyebrow {
  font-family: var(--font-text);
  font-size: var(--t-xs);
  font-weight: var(--fw-semibold);
  line-height: 1.4;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--color-text-tertiary);
}

.text-quote {
  font-family: var(--font-display);
  font-size: var(--t-lg);
  font-weight: var(--fw-regular);
  font-style: italic;
  line-height: 1.45;
  letter-spacing: -0.01em;
}

.text-stat {
  font-family: var(--font-display);
  font-size: var(--t-2xl);
  font-weight: var(--fw-semibold);
  line-height: 1;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}

/* Body copy measure cap, per §A2 */
p {
  max-width: var(--container-prose);
}

/* ============================================================
   LAYOUT PRIMITIVES — container, section, grid (§C1 asymmetric grid)
   ============================================================ */

.container {
  width: 100%;
  max-width: var(--container-content);
  margin-inline: auto;
  padding-inline: 20px;
}

.container--wide {
  max-width: var(--container-wide);
}

.container--full {
  max-width: none;
}

/* Narrow variants for single-column reading and form pages. */
.container--form {
  max-width: 720px;
}
.container--narrow {
  max-width: 640px;
}
.container--prose {
  max-width: var(--container-prose);
}

@media (min-width: 640px) {
  .container { padding-inline: 32px; }
}

@media (min-width: 1024px) {
  .container { padding-inline: 48px; }
}

@media (min-width: 1280px) {
  .container { padding-inline: 72px; }
}

/* Section rhythm: padding is owned by the section, never by child margins.
   This is what lets a section (reviews, stories) disappear entirely without
   leaving a void — see §C2 and Part III §9 Rule 4. */
.section {
  padding-block: var(--space-8);
  background: var(--color-bg);
}

@media (min-width: 768px) {
  .section { padding-block: var(--space-9); }
}

@media (min-width: 1024px) {
  .section { padding-block: var(--space-10); }
}

.section--alt {
  background: var(--color-bg-alt);
}

/* THIRD BAND + PAPER GRAIN.

   The band: Sand and Shell sit one step apart on the same neutral ramp, so
   alternating them alone produced very little felt rhythm — the warmth
   review measured 1,784px of near-identical background running unbroken
   through three consecutive sections. This band carries actual brand chroma
   (see --c-warm-100) and is used sparingly: one or two per page, placed
   where a long text run would otherwise go flat.

   The grain: one inline SVG feTurbulence as a data: URI, so it stays within
   the CSP (img-src 'self' data:) and costs zero requests. Enough that a
   large flat area reads as paper rather than as a painted rectangle, not
   enough to be perceptible as texture. Deliberately NOT a gradient —
   gradients are banned by the anti-brief (§4) and read trendy; grain is a
   surface property and reads like stock.

   Two implementation notes, both learned the hard way:

   1. It is a layered background-image, NOT a pseudo-element. Both ::before
      and ::after are already spoken for on these sections by .section--ruled
      (the horizon line and its sun), and an element gets only one of each —
      using them here silently overwrote the Horizon Rule.

   2. The colour and the grain MUST be set in one rule, and the colour must
      use background-color, not the `background` shorthand. A separate later
      `.section--warm { background: ... }` rule reset background-image to
      none and removed the grain with no error anywhere. */
.section--warm {
  background-color: var(--color-bg-warm);
  background-image: var(--grain);
  background-repeat: repeat;
}
/* Dark bands only. .hero is deliberately excluded: its background is fully
   covered by a photograph, so grain there would never be visible, and .hero
   sets its colour with the `background` shorthand anyway (see note 2 above),
   which would drop the image regardless. */
[data-section="dark"].section {
  background-image: var(--grain);
  background-repeat: repeat;
}

/* Consecutive same-background sections merge padding rather than doubling it. */
.section + .section[data-bg="same"] {
  padding-top: 0;
}

/* Asymmetric editorial grid: header cols 1-5, content cols 6-12 (§C1) */
.grid-asymmetric {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 24px;
}

/* Mobile-default column span, BEFORE the desktop override below. These two
   rules previously sat AFTER the @media block with identical specificity
   (single class each) — source order made this unscoped version win the
   cascade at every width, so grid-column: 1/-1 always beat the desktop
   grid-column: 1/span 5 / 6/span 7 split. The header stayed full-width
   while still position:sticky (which the media query alone DID apply
   correctly), so a full-width sticky bar rode down over the content
   scrolling beneath it in the same row — the "overlapping/blocking text"
   bug reported live on Ways to Go, How It Works and FAQ, which all share
   this one component. Mobile-first order (base rule first, override
   after) is the fix; this is also why every other section in this file
   defines its base rule before its @media override. */
.grid-asymmetric__header {
  grid-column: 1 / -1;
}

.grid-asymmetric__content {
  grid-column: 1 / -1;
}

@media (min-width: 1024px) {
  .grid-asymmetric {
    grid-template-columns: repeat(12, 1fr);
    gap: 24px;
  }
  .grid-asymmetric__header {
    grid-column: 1 / span 5;
    position: sticky;
    top: calc(var(--header-height) + var(--space-8));
    align-self: start;
  }
  .grid-asymmetric__content {
    grid-column: 6 / span 7;
  }
}

/* ============================================================
   REDUCED MOTION — global guard (CSS half; JS half in lib/motion.js)
   Reduced motion means arrived, not invisible. See §D7 / VI checklist.
   ============================================================ */

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

/* The injected icon symbol library — never rendered directly. This is a class
   rather than style="display:none" on the <svg> itself because the CSP sets
   style-src 'self', which blocks inline style attributes; the sprite would
   otherwise render as a visible empty box in production. */
.icon-sprite {
  display: none;
}

/* Utility: visually hidden but available to assistive tech (skip-link pattern,
   honeypot fields). display:none is deliberately not used for honeypots —
   some bots skip those; this stays in the accessibility tree as absent-but-focusable. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
