# 02 — Design system Authority: `AGENTS.md` §3 D7 — *keep the palette and the infinity mark; modernize the execution.* The look is not up for redesign. What follows is the system that preserves it while fixing what the old build got wrong. --- ## What carries over unchanged - The **palette**: cream, ink, maroon, gold. - The **infinity mark** — SML Company Ltd.'s actual logo, and the metaphor holds: a dispute is a loop, and the work is redrawing the loop into a line. **It is a shaded ribbon, not a stroked curve**: a band of variable width that twists in three dimensions, maroon flowing into champagne, passing over itself at the crossing. Ink bounding box **2668 × 1704 = 1.5657:1** `[verified 2026-08-26 — measured against the master]`. Source of truth: `src/assets/brand/sml-infinity-mark.png`; provenance in `docs/reference/brand-assets.md`. - The **type pairing**: Instrument Serif for display, Geist for text, Geist Mono for eyebrows and labels. - The **editorial register** — generous whitespace, restrained colour, serif display type at large sizes. ## What changes | Was | Is | Why | |---|---|---| | Google Fonts at runtime | Self-hosted, subset, preloaded | Removes a render-blocking third-party round trip from a page that collects legal inquiries | | Fixed px type sizes | Fluid `clamp()` scale | One scale from 360 px to 1600 px with no breakpoint jumps | | Ad hoc spacing values | 8 px base scale | Consistent vertical rhythm; no magic numbers | | Gold used as a text colour on cream | Gold restricted to decorative and on-dark | **It fails WCAG AA at 2.10:1.** Measured, not assumed | | Scroll-reveal on every element, always on | Reveal on major sections only, gated behind `prefers-reduced-motion` | Motion that reads as confident rather than decorative; accessible by default | | 2.2 MB single file, ~1 MB of base64 logos *(both figures under review — `AGENTS.md` Q34; and "470 KB PNG", which this row used to assert, has no source anywhere in the repo and has been removed)* | Optimized SVG mark, AVIF/WebP photography | The mark is geometry, so it should be vector. That holds whatever the old file weighed | | React 18 dev build + Babel Standalone in the browser | Static HTML, near-zero JS | The reason the site is invisible to crawlers | --- ## Colour Tokens live in `src/styles/tokens.css`. Never write a raw hex value in a component. | Token | Value | Use | |---|---|---| | `--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 | | `--ink-soft` | `#3a322c` | Secondary text | | `--muted` | `#6e6359` | Metadata, captions — **on cream only** | | `--maroon` | `#5a1a1c` | Primary action, accents, dark panels | | `--maroon-d` | `#3d1112` | Hover on maroon | | `--maroon-l` | `#7a2a2c` | Links on cream | | `--gold` | `#c9a876` | Rules, dividers, on-dark accent — **never text on cream** | | `--gold-d` | `#a88858` | Large decorative text on cream only | | `--gold-l` | `#e2c89a` | Text on ink or maroon | ### Contrast — measured, 2026-08-25 Against `--cream` `#faf7f2`: | Colour | Ratio | AA body (4.5) | AA large (3.0) | |---|---|---|---| | `--ink` `#1a1614` | **16.81** | pass | pass | | `--ink-soft` `#3a322c` | **11.75** | pass | pass | | `--maroon` `#5a1a1c` | **12.29** | pass | pass | | `--maroon-l` `#7a2a2c` | **8.95** | pass | pass | | `--muted` `#6e6359` | **5.47** | pass | pass | | `--gold-d` `#a88858` | **3.11** | **FAIL** | pass | | `--gold` `#c9a876` | **2.10** | **FAIL** | **FAIL** | Against `--maroon` `#5a1a1c`: cream 12.29 · gold-l 8.11 · gold 5.84 — all pass. Against `--ink` `#1a1614`: cream 16.81 · gold-l 11.09 · gold 8.00 — all pass. `--muted` on ink measures **3.07 and fails**; use `--gold-l` or cream at reduced opacity for secondary text on dark. **Hard rules.** 1. `--gold` is never a text colour on cream. Rules, borders, dividers, icon strokes, and on-dark text only. 2. `--gold-d` on cream only at 24 px+ / 19 px bold, and only for decorative display text — never for anything a reader must parse. 3. Body text on cream is `--ink` or `--ink-soft`. Metadata may use `--muted`. 4. Secondary text on dark is `--gold-l` or `--cream` at ≥ 70% opacity. 5. **No dark mode.** A single committed light identity is the right call for a legal practice, and halving the surface area halves the ways contrast breaks. --- ## Typography **Faces.** Instrument Serif (display) · Geist 300/400/500/600 (text) · Geist Mono 400/500 (eyebrows, labels, data). Self-host all three. Subset to Latin + Latin Extended-A. `font-display: swap`. Preload only the two faces used above the fold — Instrument Serif regular and Geist 400. **Scale.** Fluid, `clamp()`, `1.25` ratio at the small end widening to `1.333` at the display end. Tokens `--text-2xs` through `--text-6xl` in `tokens.css`, plus `--text-eyebrow` at 14 px — the same value as `--text-sm` and deliberately not an alias of it; see the eyebrow rule below. **Rules.** - Display type (`--font-serif`) at `--text-4xl` and above only. It has almost no hinting at small sizes and looks weak below 32 px. - Display line-height `0.95`–`1.05`; letter-spacing `-0.02em`. - Body line-height `1.6`. Measure capped at `68ch` — the old site ran full-bleed paragraphs at 1400 px, which is unreadable. - Eyebrows: mono, **14 px** (`--text-eyebrow`), `0.18em` tracking, uppercase, weight 500, always paired with a real heading. Pouya raised it **12 → 13 → 14 px on 2026-08-31**, in two passes, because uppercase mono at this tracking reads smaller than it measures and 13 px was still getting lost against the display type. There is **one definition**, `.eyebrow` in `global.css`. An eyebrow is not a heading and never carries the `` — but a real heading may carry the class, and **fourteen do, in two groups**: the footer's four column headings (`SiteFooter.astro`) and `/bio/`'s ten block headings. ⚠️ **"ONE DEFINITION" HAS ONE CARVE-OUT AND IT IS NAMED RATHER THAN IMPLIED:** `/contact/`'s `