:root {
  /* surfaces + structure (dark, default) */
  --canvas: #000000;
  --lift: #0A0A0A;
  --hair-faint: #1A1A1A;
  --hair: #2C2C2C;
  --hair-active: #3F3F3F;
  /* text */
  --fg: #FFFFFF;
  --fg-2: #C8C8C8;
  --fg-3: #9A9A9A;
  --muted: #8A8A8A;
  /* signals */
  --orange: #FF6B35;
  --green: #06D6A0;
  --yellow: #FFBE0B;
  /* Text painted on a signal fill. Black on the bright dark-theme signals,
     white on the darker light-theme ones (black on #BD3E0E is 3.86:1). */
  --on-signal: #000000;
  /* washes */
  --grid: rgba(255,255,255,0.022);
  --grid-strong: rgba(255,255,255,0.05);
  --surface: rgba(255,255,255,0.02);
  --surface-strong: rgba(255,255,255,0.04);
  --nav-bg: rgba(0,0,0,0.72);
  --nav-bg-scrolled: rgba(0,0,0,0.92);
  --frost-bg: rgba(10,10,12,0.55);
  /* type families */
  --font-display: 'Syne', ui-sans-serif, system-ui, sans-serif;
  --font-body: 'DM Sans', ui-sans-serif, system-ui, sans-serif;
  --font-mono: 'JetBrains Mono', ui-monospace, Menlo, monospace;
  /* type scale */
  /* Floor for labels. The root is 15px, so a 0.6rem label was 9px and the
     light-theme muted grey sat at 4.46:1. Nothing set in type goes below this. */
  --text-micro: 11px;
  --text-caption: 13px;
  --text-body: 15px;
  --text-subheading: 18px;
  /* Heading scale. Every h1/h2 on the site sits on one of these steps; the
     page style blocks used to carry 22.5, 30, 33, 40.5, 46 and 56 by hand. */
  --text-heading-xs: 24px;
  --text-heading-sm: 30px;
  --text-heading: 40px;
  --text-heading-lg: 48px;
  --text-heading-xl: 56px;
  /* The floor has to fit the longest single word on a 390px phone. At the old
     48px floor, "BYTEFRAME" measured 396px inside a 342px column and pushed
     the whole page sideways. 9.5vw overtakes the floor at ~358px wide, so
     every screen above that is larger than it was before, not smaller. */
  --text-display: clamp(34px, 9.5vw, 84px);
  /* Reading measure for body paragraphs. */
  --measure: 72ch;
  --leading-tight: 1.05;
  --leading-body: 1.6;
  --tracking-display: -0.03em;
  --tracking-heading: -0.02em;
  /* weights */
  --fw-light: 300; --fw-regular: 400; --fw-medium: 500;
  --fw-semibold: 600; --fw-bold: 700; --fw-display: 800;
  /* spacing */
  --space-4: 4px; --space-8: 8px; --space-12: 12px; --space-16: 16px;
  --space-20: 20px; --space-24: 24px; --space-32: 32px; --space-40: 40px;
  --space-48: 48px; --space-64: 64px; --space-80: 80px;
  /* radius */
  --radius-sm: 4px; --radius-md: 8px; --radius-lg: 12px; --radius-pill: 999px;
  /* elevation */
  --shadow-soft: 0 1px 1px rgba(0,0,0,0.4), 0 4px 14px rgba(0,0,0,0.4);
  --shadow-frost: 0 2px 30px rgba(0,0,0,0.5);
  /* motion. One signature ease, two durations, one stagger step. Every
     transition and animation on the site reads from these. */
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --dur-fast: 150ms;
  --dur-slow: 600ms;
  --stagger: 60ms;
  /* layout */
  --page-max: 1200px;
  --section-gap: var(--space-80);
}

html[data-theme="light"] {
  --canvas: #FBFBF8;
  --lift: #F1F1EC;
  --hair-faint: #ECECE4;
  --hair: #E3E3DC;
  --hair-active: #B4B4AA;
  --fg: #0C0F14;
  --fg-2: #2A2E33;
  --fg-3: #596066;
  /* Light signals and muted grey are each one step darker than the first
     pass: #6B7280 measured 4.46:1 on the cream canvas and 4.27 on the lifted
     surface, and orange #D54D1A 3.95 as 22px text. These hold 4.8 or better
     on the lifted surface, verified by the contrast sweep. */
  --muted: #5B626E;
  --orange: #BD3E0E;
  --green: #0E7357;
  /* Yellow is the hue that loses the most against a cream canvas. #C9940D was
     the last signal still used as text below 3:1 (2.4 on the lifted surface),
     so it is pulled down to sit with orange and green rather than a step
     lighter than either. */
  --yellow: #7F5A00;
  --on-signal: #FFFFFF;
  --grid: rgba(0,0,0,0.04);
  --grid-strong: rgba(0,0,0,0.07);
  --surface: rgba(0,0,0,0.02);
  --surface-strong: rgba(0,0,0,0.04);
  --nav-bg: rgba(251,251,248,0.8);
  --nav-bg-scrolled: rgba(251,251,248,0.94);
  --frost-bg: rgba(251,251,248,0.62);
}
