/* ============================================================================ Design tokens — the single source of colour, type, space, and motion. Spec: docs/02-design-system.md Never write a raw hex value or a magic number in a component. If a value is missing here, add it here. ========================================================================= */ :root { /* --- Colour ------------------------------------------------------------ Palette carried over unchanged from the previous build (AGENTS.md D7). Contrast ratios measured 2026-08-25 and recorded in docs/02. */ --cream: #faf7f2; /* page background */ --cream-2: #f3ede0; /* alternating section background */ --cream-3: #ebe3d1; /* cards and insets on cream */ --ink: #1a1614; /* body text; dark section backgrounds 16.81:1 */ --ink-soft: #3a322c; /* secondary text 11.75:1 */ --muted: #6e6359; /* metadata — ON CREAM ONLY (3.07:1 on ink) */ --maroon: #5a1a1c; /* primary action, dark panels 12.29:1 */ --maroon-d: #3d1112; /* hover on maroon */ --maroon-l: #7a2a2c; /* links on cream 8.95:1 */ /* GOLD IS NOT A TEXT COLOUR ON CREAM. --gold on cream measures 2.10:1 and fails AA for body AND large text. --gold-d measures 3.11:1 — large decorative text only, 24px+. Both are fine on --ink (8.00) and --maroon (5.84). See docs/02. */ --gold: #c9a876; /* rules, dividers, icon strokes, on-dark text */ --gold-d: #a88858; /* large decorative display text on cream only */ --gold-l: #e2c89a; /* text: 11.09:1 on ink, 8.11:1 on maroon */ --line: rgb(26 22 20 / 0.10); --line-2: rgb(26 22 20 / 0.06); --line-dark: rgb(250 247 242 / 0.14); /* Semantic aliases — prefer these in components over raw palette names. */ --bg: var(--cream); --bg-alt: var(--cream-2); --bg-raised: var(--cream-3); --bg-inverse: var(--ink); --text: var(--ink); --text-secondary: var(--ink-soft); --text-meta: var(--muted); --text-inverse: var(--cream); --text-inverse-2: var(--gold-l); --accent: var(--maroon); --accent-hover: var(--maroon-d); --link: var(--maroon-l); --rule: var(--gold); --border: var(--line); --focus-ring: var(--maroon); /* docs/02: "outline: 2px solid var(--maroon); outline-offset: 3px". The offset was written as a literal in global.css and then in two components, which is three places to forget. */ --focus-offset: 3px; /* --- Type -------------------------------------------------------------- */ --font-serif: 'Instrument Serif', 'Cormorant Garamond', Georgia, 'Times New Roman', serif; --font-sans: 'Geist', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; --font-mono: 'Geist Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; /* Fluid scale, 360px → 1600px viewport. Ratio widens toward the display end (1.25 → 1.333) so headlines scale harder than body copy. --text-2xs is the eyebrow floor docs/02 sets at 11px. Added 2026-08-27: SiteHeader wrote `0.6875rem` as a literal, step-1 review finding #7. */ --text-2xs: 0.6875rem; /* 11 — eyebrow */ --text-xs: 0.75rem; /* 12 — legal */ --text-sm: 0.875rem; /* 14 — meta */ --text-base: 1rem; /* 16 — body */ --text-lg: clamp(1.0625rem, 0.99rem + 0.32vw, 1.1875rem); /* 17→19 */ --text-xl: clamp(1.25rem, 1.14rem + 0.48vw, 1.5rem); /* 20→24 */ --text-2xl: clamp(1.5rem, 1.31rem + 0.81vw, 1.875rem); /* 24→30 */ --text-3xl: clamp(1.875rem, 1.55rem + 1.37vw, 2.5rem); /* 30→40 */ --text-4xl: clamp(2.25rem, 1.66rem + 2.50vw, 3.5rem); /* 36→56 */ --text-5xl: clamp(2.75rem, 1.75rem + 4.25vw, 4.75rem); /* 44→76 */ --text-6xl: clamp(3.25rem, 1.75rem + 6.35vw, 6rem); /* 52→96 */ --leading-display: 0.98; --leading-tight: 1.15; --leading-snug: 1.35; --leading-body: 1.6; --leading-relaxed: 1.75; --tracking-display: -0.02em; --tracking-tight: -0.01em; --tracking-normal: 0; --tracking-wide: 0.06em; --tracking-eyebrow: 0.18em; --weight-light: 300; --weight-normal: 400; --weight-medium: 500; --weight-semi: 600; /* --- Space — 8px base -------------------------------------------------- */ /* 2 — a hairline gap, below the 4px base step. It exists because the two-line brand block needs a gap smaller than --space-1 without the header growing; step-1 review finding #7 flagged the literal. Do not reach for it as a general step: the 8px base starts at --space-1. */ --space-05: 2px; --space-1: 0.25rem; /* 4 */ --space-2: 0.5rem; /* 8 */ --space-3: 0.75rem; /* 12 */ --space-4: 1rem; /* 16 */ --space-5: 1.5rem; /* 24 */ --space-6: 2rem; /* 32 */ --space-7: 3rem; /* 48 */ --space-8: 4rem; /* 64 */ --space-9: 6rem; /* 96 */ --space-10: 8rem; /* 128 */ --space-11: 10rem; /* 160 */ /* docs/02: "Section rhythm: --space-9 (96px) mobile, --space-11 (160px) desktop." The previous curve was `6vw + 2rem`, which reaches 160px only at a 2133px viewport — measured 128px at 1600px, 108.8px at 1280px. The upper bound was unreachable on any real screen, so the token read as if it delivered a rhythm it never delivered. `9vw + 1rem` hits 160px at 1600px and still clamps to 96px on a phone. [measured 2026-08-26] */ --section-y: clamp(var(--space-9), 9vw + 1rem, var(--space-11)); /* --- Layout ------------------------------------------------------------ */ /* Sticky-header height at >= 66rem, where the header IS sticky. global.css drives `scroll-padding-top` off this, so the skip link does not drop the reader behind the header. If SiteHeader's padding or nav sizing changes, re-measure and change this with it — one fact living in two files. [measured 2026-08-26 — headless Chrome at 1024/1100/1280/1440px, with six nav items and with a seventh injected. 81px at every one: 32 padding + 48 reserved brand block + the 1px bottom border, which is easy to forget and is why this is measured rather than added up. The brand reserves 48px so the height does not change when the tagline appears at 76rem] */ --header-h: 5.0625rem; /* 81 — measured, not chosen */ --width-content: 80rem; /* 1280 */ --width-wide: 90rem; /* 1440 */ --width-prose: 68ch; /* reading measure — never exceed for body copy */ --gutter: var(--space-5); --gutter-lg: var(--space-7); /* --- Radius, elevation, motion ----------------------------------------- */ --radius-sm: 4px; --radius-md: 8px; --radius-lg: 16px; --radius-full: 999px; --shadow-sm: 0 1px 2px rgb(26 22 20 / 0.04); --shadow-md: 0 4px 16px rgb(26 22 20 / 0.06); --shadow-lg: 0 12px 40px rgb(26 22 20 / 0.10); --ease: cubic-bezier(0.2, 0.7, 0.2, 1); --ease-out: cubic-bezier(0.16, 1, 0.3, 1); --dur-fast: 150ms; --dur-hover: 250ms; --dur-reveal: 600ms; --z-base: 0; --z-sticky: 100; --z-header: 200; --z-overlay: 300; --z-skip: 400; } @media (min-width: 48rem) { :root { --gutter: var(--gutter-lg); } } /* No dark mode by design (docs/02). A single committed light identity is the right call for a legal practice and halves the ways contrast can break. */