Basefox · Decisions locked · 26 Aug 2026
Both calls applied and shipping. The line-art approach is wired end to end and verified against a real build; brand cards stay logo-led.
Archivo for display, Source Sans 3 for body, Spline Sans Mono unchanged. The other three
candidates are deleted, along with Space Grotesk, Barlow, and Fraunces — five bundled faces
down to three. TypeSystem.swift and the -type= flag are gone, and
the winner is inlined in Fonts.swift.
Caps at 22pt and above, letterspaced 0.3, narrowing to wdth 89 — the width
axis was already switching at that threshold, so caps and condensing arrive together. Below
22pt display stays sentence case at full width, which keeps row titles and labels readable.
The accent rule from your mockup sits between the headline and the descriptor, in the card's
own colour.
This settles the spec contradiction. basefox-spec-v03.md no longer says
“no letterspaced all-caps”; it now states the caps threshold, the letterspacing,
the width axis, and the accent rule.
Tier 3 became two rungs. A category names a drawing and an SF Symbol, so the tier always resolves and the app is still complete with an empty asset directory.
| Tier | What it is | Source | Needs a file |
|---|---|---|---|
| 1 · Campaign | Line art composed for one sale | ExchangeEvent.hero | yes |
| 2 · Brand | Merchant lockup | Brand.logo | yes |
| 3 · Category | Library object, else SF Symbol | Category.illustration → Category.glyph | no |
| 4 · Generic | Abstract pattern, drawn in code | — | no |
Brand.accent, which already exists.larchpipe assets sync writes the imageset with
preserves-vector-representation set, so one file scales to every size.I drew two throwaway placeholders to the spec, ran the sync, and rendered them. Both were deleted afterwards, so nothing fake ships — the screenshot is the evidence, not the artwork.
Placeholder tent resolving through tier 3, with the accent disc and topo texture drawn in code behind it. The thin stroke is the placeholder's fault, and it is what produced the calibration fix below.
The AAFES card carries [footwear, outdoor]. The resolver took the
first-named illustration, so it asked for illus-footwear, found no file,
and dropped to the SF Symbol — even though outdoor's drawing existed. It now tries every
named candidate in card order and takes the first that resolves. Without this, partial
library coverage would have looked like no coverage.
My placeholders used 4pt strokes on a 512pt artboard. On a standard card that renders at
roughly 0.8pt and nearly disappears. registry/assets/README.md now specifies
10pt and says so explicitly: if the drawing looks heavy on the artboard, it
is probably right in the app.
Related: while coverage is partial, the browse tiles mix line art and SF Symbols, and the two do not match in weight. Commissioning all seven category objects together avoids a transitional period where the row looks inconsistent.
Line art cannot distinguish Hoka from Brooks — both would show a generic running shoe — so tier 2 remains the treatment for a merchant, and the ten lockups are still needed. That is now written into the spec rather than left implicit.
| Count | What | Format | Cost of absence |
|---|---|---|---|
| 10 | Brand lockups | PNG, 900 × 240 @3x | Card drops to the category mark |
| 7 | Category objects | PDF vector, 512pt artboard | Falls back to the SF Symbol |
| 3 | Campaign compositions | PDF vector | Falls back to the category object |
larchpipe assets sync --dry-run lists all twenty by name and states what each
one's absence costs. Drawing specs are in registry/assets/README.md.
ChannelKind case across four contract mirrors, registry entries, and a source to
sweep — ingestion work, and adding one exchange invites CGX. Left alone pending your call.Route.endsOn, so the Ending
Soon card still fires only from exchange events.