A real report, written by 55 · Typography Audit, run against this repo — dogfood output, committed unedited. This is the artifact every brief ends in: findings that cite their evidence, ranked by severity, with a fix sketch each.
Read-only inventory of every way this product sets type, across the four style sources: template.html (landing), build.py→SITE_CSS (detail pages), studio.html, and vitals.html. Every number below is grepped from those files; each carries its selector and line.
Headline: two families, sanely chosen, with genuinely good measure — but the size scale has accreted to ~27 distinct sizes, six of them separated by a single indistinguishable half-pixel, and the font economics run exactly backwards: the build ships and precaches a mono weight no rule uses (plexmono-500, 14.9 KB) while eleven selectors request a mono weight that isn't shipped (700 → faux bold). The deliverable is a smaller system.
Backlog reconciliation (2026-07-09). This audit predates the font redesign (Archivo → Schibsted Grotesk for display, IBM Plex Sans added for UI), so its family names and per-size line numbers are stale. Dispositions: T1 (mono @700 faux-bold) — FIXED: the three surviving
--mono@700 badges retargeted to the shipped 600. T2 (unusedplexmono-500) — ALREADY DONE: the 500 face was dropped in the redesign; only 400/600 ship. T4 (same role typed 3 ways) — FIXED: body line-height unified to one--lh-bodytoken (was 1.6/1.62/1.5 across stylesheets). T3/T5 (font-size + line-height value sprawl) — FIXED (2026-07-09, round 3): every half-pixel size was folded to its nearest integer across all four style sources (69 declarations — 11.5/12.5→12, 13.5→13, 14.5→14, 15.5/16.5→16, 9.5→10, 10.5→11; each move ≤0.5px, imperceptible), so no size sits a half-pixel from its neighbor — the exact accretion T3 named. Near- duplicate line-heights were folded to their dominant neighbor (1.62/1.65→1.6, 1.55→1.5, 1.4→1.45, 1.03/1.04→1.05), 15→11 values; the survivors are deliberate per-role leadings (display .98/1.0, headings 1.05/1.15/1.22, sub 1.3/1.35, body 1.45/1.5/1.6, notes 1.7), not drift. The aggressive 5-step collapse (14/15/16→one 15) was declined: those full-pixel steps are perceptible and folding them would shift running body text — the defect the audit named was the indistinguishable half-steps, and those are gone. New minor issue found & FIXED:.drop-bigrequested--sans@700 where Plex Sans ships only 400/600 (a fresh faux-bold) — retargeted to--disp(Schibsted Grotesk, real 700). The Studio checkbox✓glyph at 700 is left as-is (an imperceptible synthesized weight on one symbol). SeeFIXLOG.md.
| Token | Stack | Loaded as |
|---|---|---|
--sans | 'Archivo', system-ui, -apple-system, 'Segoe UI', sans-serif | one variable woff2, font-weight:100 900 (build.py:316) |
--mono | 'IBM Plex Mono', ui-monospace, 'SF Mono', Menlo, Consolas, monospace | three static woff2: 400/500/600 (build.py:317–319) |
Defined identically in all four files (template.html:33, studio.html:32, vitals.html:30, build.py:324). Fallback stacks are metric-adjacent and sensible. font-display:swap on all 16 faces.
Fixed px (18 values), with usage counts:
12px ×33 13px ×23 12.5px ×17 11.5px ×16 14px ×13 16px ×10
13.5px ×10 11px ×10 15px ×9 10.5px ×8 15.5px ×7 14.5px ×5
20px ×3 19px ×3 17px ×3 17.5px ×1 10px ×1 9.5px ×1
Fluid clamp() (8 ranges): 44/9vw/72 (×2, studio.html:56,vitals.html:54), 38/6vw/62 (template.html:86), 30/5.6vw/50 (build.py:376), 24/4vw/38 (template.html:74), 22/3.4vw/30 (build.py:384), 20/3vw/26 (build.py:486), 16/2.4vw/20 (build.py:377), 16/2.2vw/19 (template.html:88). Plus .88em for code (×2).
The tell-tale of accretion — six half-pixel pairs, each visually identical: 11/11.5, 12/12.5, 13/13.5, 14/14.5, 15/15.5, 17/17.5. There is no ratio; the 11–17.5px band holds 15+ separate decisions.
700 ×27, 600 ×23, 800 ×10, 500 ×9, 900 ×5, 400 ×4. (The 100 ×4 hits are only the font-weight:100 900 bound in the four Archivo @font-face blocks — 100 is never applied to text.) All six render from the Archivo variable font. Mono weights requested: 400, 600, and 700 — see §4.
1.5 ×13, 1.45 ×6, 1.7 ×2, 1.62 ×2, 1.6 ×2, .95 ×2, then singletons 1.65, 1.55, 1.4, 1.35, 1.3, 1.25, 1.05, 1.03, 1.0. The body role alone carries three: 1.6 (template.html:46), 1.62 (build.py:330), 1.5 (studio.html:36) — the three stylesheets have drifted.
Headings negative (-.01, -.015, -.02, -.03em), eyebrows/kickers positive (.1, .12, .14, .16em). Discipline is directionally right (tight display, tracked-out uppercase) but four near-identical negatives could be two.
Keep the two families and the fluid-heading idea. Collapse the fixed-size sprawl to five UI steps and the eight clamps to four. ~27 sizes → 9.
Fixed UI scale (≈1.18 ratio, no half-pixels):
| Step | Size / LH | Role | Absorbs today |
|---|---|---|---|
t-micro | 11px / 1.4 | uppercase mono eyebrows, kickers, labels (tracking carries them) | 9.5, 10, 10.5, 11 |
t-meta | 12px / 1.45 | chips, meta rows, captions, code-in-UI | 11.5, 12, 12.5 |
t-sec | 13px / 1.5 | taglines, secondary body, helper text | 13, 13.5 |
t-body | 15px / 1.6 | UI base / running body | 14, 14.5, 15, 15.5, 16 |
t-sub | 18px / 1.3 | card titles, sub-heads (H3/H4) | 17, 17.5, 19, 20 |
Fluid headings (4 clamps):
| Step | Clamp | Role | Absorbs |
|---|---|---|---|
t-lede | clamp(16px,2.4vw,20px) / 1.5 | ledes/subs | 16/2.2vw/19 |
t-h2 | clamp(22px,3.4vw,30px) / 1.05 | section H2 | 20/3vw/26, 24/4vw/38 |
t-title | clamp(30px,5.6vw,50px) / 1.03 | page/detail H1 | — |
t-display | clamp(38px,6vw,62px) / 1.0 | landing/hero display | 44/9vw/72 (unify Studio & Vitals onto this) |
Weights: four is plenty — 400 body, 600 mono/UI emphasis, 700 sans strong, 900 display. Retire the sparse 500 (9 uses → fold into 600 or 400) and 800 (10 uses → fold into 700 or 900) unless a specific pairing needs them.
Deletions: ~13 fixed sizes, 4 clamps, ~10 line-heights, ~4 letter-spacing values, and (see §4) one whole font file. Nothing above changes what a reader perceives — the half-steps were never distinguishable.
IBM Plex Mono ships only 400/500/600 (build.py:317–319, fonts/), yet eleven selectors set var(--mono) at font-weight:700, forcing the browser to synthesize bold — smeared, uneven stems, worst at the 11–13px sizes where these live: .nav-cta (template.html:60,build.py:344), .stepno (:246), .badge (template.html:166), .art .sev (:104), .pnode .dot (:129), .phase .step (build.py:397), .lens .i (:405), .seqstep .node (:466), .topnav .cta (studio.html:48, vitals.html:46). Fix: retarget all to 600 (shipped) — or ship a 700 face. Retargeting is the smaller system.
plexmono-latin-500.woff2 (14,888 B) is declared (build.py:318), precached by the service worker (build.py:994 → sw.js), yet zero rules request mono @500 — every font-weight:500 in the codebase inherits sans (.eyebrow b, .quote .q, .hero .micro b, .empty b). So the build ships + downloads a face nobody renders, while omitting the 700 face eleven selectors ask for (T1). Drop the 500 face → reclaim ~14.9 KB (~18% of the ~80 KB font payload) with no visual change.
The 11–17.5px band holds 15+ sizes including six 0.5px-apart pairs (11/11.5, 12/12.5, 13/13.5, 14/14.5, 15/15.5, 17/17.5; counts in §1). No two adjacent steps are distinguishable, so the "hierarchy" they imply doesn't read. Collapse to the five-step UI scale in §2.
The three hand-authored stylesheets have drifted on shared roles:
1.6 (template.html:46) vs 1.62 (build.py:330)vs 1.5 (studio.html:36).
.badge weight: 700 (template.html:166) vs 600 (build.py:366).clamp(38,6vw,62) (landing) vs clamp(44,9vw,72)(Studio :56, Vitals :54) — same role, two sizes. One shared token block would end the drift.
Fold to five roles: display 1.0, heading 1.05, sub 1.3, body 1.6, mono/code 1.5. Removes ~10 improvised values.
ch-constrainedinside the 45–75 target: .hero p.sub 52ch (:88), .sub 58ch (studio.html:58), .lead 60ch (:75) / 64ch (build.py:385), .prose 68ch (:386), .turn 70ch (:122), .dhero .lede 60ch (:377). Keep.
.ctxbody labeltemplate.html:208), captions (.drop-small studio.html:73), code, and empty states are each deliberately styled, not inherited.
| File | Bytes | Status |
|---|---|---|
archivo-latin-var.woff2 | 34,928 | used — one variable file covers every sans weight (400/500/600/700/800/900). Efficient. |
plexmono-latin-400.woff2 | 14,708 | used — mono body/labels default weight. Preloaded (build.py:536). |
plexmono-latin-600.woff2 | 15,620 | used — mono emphasis (buttons, kickers, badges). |
plexmono-latin-500.woff2 | 14,888 | UNUSED — declared + precached, requested by no rule. Reclaim. |
| (mono 700) | — | MISSING — requested by 11 selectors (T1); currently faux-synthesized. |
Total shipped ≈ 80.1 KB. Immediately reclaimable ≈ 14.9 KB by deleting the 500 face (drop @font-face build.py:318 + the precache entry build.py:994 + the file). Net after also fixing T1 (retarget 700→600): –14.9 KB and zero faux bold, no faces added. Only preload the two above-the-fold faces (already done: archivo-var + plexmono-400).
Report only — measured values only, nothing edited. The two highest-leverage, lowest-risk changes are T1 (retarget the 11 mono-700 selectors to 600 — kills all faux bold) and T2 (drop the unused plexmono-500 — reclaims ~14.9 KB); they're one small commit together. Which fixes should I make?