# Brand + themes: the Found-line mark, and base × accent Status: SHIPPED to main 2026-09-25 (S0 `7c9e3bdf`, S1 `575ae1d4`, S2 `b9772e53`, S3 `85cf6e81` + its `plans:` records), not rolled out. Worked on `brand/found-line` (worktree `../brand-found-line`, pnpm wt block #1), with S1 and S2 as `brand/mark` and `brand/themes` off S0's tip. The release is [`release-10.md`](release-10.md); the operator's runbook is `~/reports/release-10/RUNBOOK.html`. S4 (Archilyzer Media, the YouTube channel) was built after the release-10 cut on `brand/media` (worktree `../brand-media`, block #3) and is not part of the site rollout; see "Slice S4, as shipped". Design canvas: https://claude.ai/artifact/UsUxwgRkP5a3m4jZXucAvG (boards *Family*, *In context*, *Themes · base × accent*, and the rejected directions A–C). Read it with Artifact `action:"read"`; republish by staging under the primary checkout's gitignored `node_modules/.brand-canvas/project/` (Artifact publish only accepts paths inside the repo). **Where this plan and the canvas differ, the plan wins** — notably the contrast-corrected on-light and on-sepia accent values below. ## What the operator asked for (2026-09-25) 1. One logo and brand for Archilyzer and every official child site. 2. A reader theme made of two independent choices: a **base** (Light / Sepia / Dark, plus System) and an **accent**. Every site defaults to its own accent; a reader can pick any other. ## Decisions (final) - **Mark D · Found line.** Four transcript lines on a rounded square; the second is lit and carries a play head. - **The wordmark splits on the subject's name,** not on "lyzer". - **The dark base is warm ink,** not graphite. - **Accent swaps:** Anilyzer takes Sakura and Jasolyzer takes Green, because Archilyzer keeps its teal ("Signal"). - **All five theme families retire:** base, archive, selenized, swiss and archilyzer. - **No new dependencies expected.** `rsvg-convert`, `magick` and `inkscape` are on the machine, and `next/og` bundles satori and resvg. sharp's native build failed in the worktree; nothing here needs it. ## The design (source of truth) ### Mark Everything is in a 512 viewBox. The shapes, back to front: | Part | Shape | Colour | |---|---|---| | ground | rect, `rx=112` | ground | | line 1 | rect x112 y128 w288 h44 rx22 | dim | | play head | polygon `112,210 172,234 112,258` | lit | | line 2 (the found line) | rect x188 y212 w212 h44 rx22 | lit | | line 3 | rect x112 y296 w232 h44 rx22 | dim | | line 4 | rect x112 y380 w152 h44 rx22 | dim | Icon palettes: - **Child site:** ground `#0c0a08`, dim `#6b5d47`, lit = the site accent's on-dark value. - **Archilyzer** (homepage, hub, editor): ground `#151b20`, dim `#586977`, lit `#e7edf1` (bone). The parent mark is achromatic. The dims were raised on 2026-09-27 (release 10, slice MC; `release-10.md`), from `#3b3327` (1.59:1 on ink) and `#3f4c56` (1.97:1 on slate), which the operator found too faint: on the dark base the ink tile merges into the page and the mark read as one lit line. Two rules now hold every icon palette (`brand.ts`, enforced in `brand.test.ts`): **a dim reaches 3:1 on its ground** (WCAG 1.4.11) and **every named lit value stands 2:1 above its dim**. The new dims keep the old hue and saturation and sit just over 3:1 (3.09, 3.06), because the two rules multiply to lit-on-ground and the darkest lit values leave little room: Vermilion is 2.28:1 over the child dim, and Signal on the Media slate 2.05:1. **On the dark base the in-page mark's tile has a 1px ring** (2026-09-28, release 10, slice MR; `release-10.md`): the operator asked for "a thin outline to the tile on dark", because the child's ink tile is the dark page itself and the slate is 1.14:1 on it. `BrandMark` draws it outside the tile, following its corner (`border-radius` 21.875% = rx 112 of 512), in the palette's own dim (`#6b5d47`, `#586977`: 3.09:1 and 3.48:1 on the ink page), as a box-shadow under `.dark` only, so no layout moves and light and sepia are unchanged. The icon files carry no ring. **Under forced colours the tile has a 1px `CanvasText` outline, on every base** (2026-09-28, release 11, slice O2; `release-11.md`): the reader's Canvas replaces the page there and may be as dark as the tile. Same place and corner as the ring (outline offset 0); outside forced colours the classes match nothing, and the marks render pixel-identical on light, sepia and dark. Icon variants: - `any`: `rx=112`. - `maskable`: full-bleed ground, the mark scaled 0.8 about (256,256). - `apple`: full-bleed ground at scale 1. ### Wordmark Archivo at `font-stretch:118%`. The lead is weight 720 in `--foreground`; the suffix is weight 380 in `--muted-foreground`. Two adjacent spans with no whitespace between them; the link's accessible name is the full title. | Site | Lead | Suffix | |---|---|---| | Archilyzer | Archi | lyzer | | Jeralyzer | Jer | alyzer | | Rekietalyzer | Rekieta | lyzer | | Hasanalyzer | Hasan | alyzer | | Anilyzer | Ani | lyzer | | Bonnellyzer | Bonnell | yzer | | Jasolyzer | Jaso | lyzer | ### Accents The on-light and on-sepia values are contrast-corrected: each must reach ≥4.5:1 against its ground and with white ink. A unit test enforces it. | id | Name | Site default | On dark | On light | On sepia | |---|---|---|---|---|---| | `signal` | Signal | Archilyzer (homepage, hub, editor) | `#5fa8a0` | `#2e7b73` | `#2b756e` | | `brass` | Brass | jeralyzer | `#e3b15c` | `#95661a` | `#8e6119` | | `vermilion` | Vermilion | rekietalyzer | `#ec7a52` | `#b3431f` | `#b3431f` | | `violet` | Violet | hasanalyzer | `#b49cf2` | `#6a4bc4` | `#6a4bc4` | | `sakura` | Sakura | anilyzer | `#ee8fb5` | `#a83a6a` | `#a83a6a` | | `blue` | Blue | bonnellyzer | `#74a9f2` | `#2d5fb8` | `#2d5fb8` | | `green` | Green | jasolyzer | `#7cc46a` | `#3f7a2c` | `#3d772b` | ### Bases Each base declares all 48 colour tokens listed in `homepage/e2e/theme.spec.ts:63-112`, plus `color-scheme`. (2026-09-28: 50 now — `REQUIRED_TOKENS` in `common/components/themeConfig.ts`, which the spec reads; `--chart-6` came in release 11, slice O2. Each base also declares the non-colour flags `--base-light|sepia|dark`.) | Base | Source | Values | |---|---|---| | Light | today's `archilyzer` light block | bg `#f3f6f7`, surface `#e8edef`, fg `#161c21`, muted-fg `#55646e`, border `#d2dade`, border-strong `#aeb9c0`, faint `#78868f` | | Sepia | new | bg `#f4ecd8`, card `#faf4e6`, fg `#33281a`, muted-fg `#6b5c43`, border `#dccdaa` | | Dark | today's `archive` dark (ink) | bg `#0c0a08`, surface `#141009`, fg `#efe7d8`, muted-fg `#a39a86`, border `rgba(233,220,197,.1)` | - **Sepia** derives its status, panel and chart values from the archive paper block, darkened for a light ground. - **Dark** adds the `--destructive-soft` and `--state-gone(-soft)` it lacks today. - **Charts do not follow the accent:** each base has its own fixed `--chart-1..5` (`--chart-6`, a rust, since release 11: light and sepia `#823c10`, dark `#a54a08`; `release-11.md`, slice O2). Re-validate chart-3 against `--state-gone` with the dataviz skill's validator (FACTS.md ~6442 records a real past collision), and keep chart-1 ≠ Signal. ### Type and radius Three faces everywhere: - Archivo as `--font-display`, with the `wdth` axis. `.font-display` gets `font-stretch:112%`. - IBM Plex Sans as `--font-sans`, weights 400–700, normal + italic. - IBM Plex Mono as `--font-mono`. `--radius: 0.375rem` on `:root`. ## Slices ``` S0 palette + data ──┬──> S1 brand (mark, icons, wordmark) ──┐ └──> S2 themes (tokens, runtime, picker) ┴──> S3 integrate + gates ──> operator rollout ``` Cadence: the planner/reviewer orchestrates; one Opus general-purpose implementer per slice writes the code, under [`tools/implementer-rules.md`](tools/implementer-rules.md). S1 and S2 run as two parallel implementers, each in its own worktree cut from S0's tip (`pnpm wt add brand/mark --from brand/found-line`, `pnpm wt add brand/themes --from brand/found-line`). Review fixes go back to the same agent. Sonnet or Haiku do exploration and extraction. **Conflict avoidance between S1 and S2:** - In `export/app/layout.tsx` and `homepage/app/layout.tsx`, S1 edits only the `icons` metadata; S2 edits only the theme constants, `generateViewport` and the `RootLayout` ``. - `Header.tsx` and `Footer.tsx` belong to S1; `ThemeMenu`, `MobileMenu` and `ThemeToggle` belong to S2. - S1 colours the header mark with `var(--brand-mark, var(--brand))`, so it works before S2 lands. - S1 puts new assertions in a new `export/e2e/brand.spec.ts`; S2 owns `site-branding.spec.ts:75-86`. - Whichever of S1/S2 merges second merges `main` and re-gates. - Release 9 slice C2 (`one-core/c2-federated-search`) is in flight on the hub search components; neither slice touches `SearchResults.tsx`, `FiltersPanel.tsx` or `ArchiveShelf.tsx` (they receive hexes and keep working unchanged). ### S0 — palette + data (`brand/found-line`, alone) **New `common/lib/brand.ts` (pure):** - `ACCENT_IDS` and an `ACCENTS` table, `{id, name, onDark, onLight, onSepia}` per accent. - `DEFAULT_ACCENT = "signal"`. - `BASE_GROUNDS = {light: "#f3f6f7", sepia: "#f4ecd8", dark: "#0c0a08"}`. - `ICON_PALETTES` for child sites and for Archilyzer. - The `MARK` shape list and `markSvg(palette, {variant})`. - `splitWordmark(title, lead?)`. `common/lib/project.ts` gains `PROJECT_WORDMARK_LEAD = "Archi"`. **`common/lib/accent.ts`:** - Keep `parseAccent`. - Add `parseAccentSetting(v)`: an accent id, a `"#rrggbb"`, or undefined. - Add `resolveAccent(v)`: `{id | "custom", light, sepia, dark}`. A custom hex is darkened or lightened per ground until it reaches 4.5:1. - Add `accentHex(v)`. The published value is always a hex: an id becomes its on-dark value. - Add `customAccentVars()`. - `siteAccentVars` is deleted in S2. **`common/lib/siteSchema.ts`:** - `accent` parses through `parseAccentSetting` (around :238, :302, :321), with new doc text (:110). - New optional `wordmarkLead`, after `headerTitle`. Stored only when it is a proper prefix of `headerTitle`; needs type, `SITE_FIELD_DOCS`, schema and `siteToDisk` entries. - Regenerate SITE.md: `pnpm --filter yt-dlp-transcript-common exec tsx bin/file-schemas-docs.ts`, checked with `--check`. **The published accent stays a hex.** Third-party hubs read other sites' `/site.json`, so these route through `accentHex`: - `common/lib/siteDescriptor.ts:~87` - `common/bin/compose-hub.ts:~94` - `common/lib/homepageSummary.ts:~431` **Editor form** (`editor/app/sites/components/SiteForm.tsx:229-234`, `editor/app/sites/actions.ts:~45-51,169-191`): - "Brand accent" becomes a native radio group of the 7 swatches plus "Custom", with a hex field. - A "Wordmark lead" field. The single writer is `writeSite` in `common/lib/site.ts`. - `Field` puts the hint inside the `