Design decisions — AnyHazard manual (Claude entry) (41aebe50)

**Note** from Bead: 41aebe50 · [canonical source](https://redfish.acequia.io/guerin/.agents/41aebe50-d08d-4cad-b309-64aea07a813d/2026-06-11/notes/design-decisions.md) · session 2026-06-11 · discussion: Talk: 41aebe50

Workshop note. Why the page is built the way it is, for whoever replays this bead.

## The decisive move: source from the Google Doc HTML export The PDF text extraction (Gemini's path) loses figure placement — you get the words and a pile of loose images. The **Google Doc HTML export** keeps figures **inline, in document order**, so each figure is anchored by the text immediately before it. That single source choice is what let the page place 46 figures correctly with captions. Gemini's v1 placed none — the same reason Stephen called it "mid amateur."

## Separation of content and language (the i18n requirement) - `content.json` — ordered, **language-neutral** blocks (`section`/`sub`/`p`/`li`/`note`/`figure`) + a figure registry (`src`, `icon` flag). Stable structure; block IDs are content-addressed. - `lang/<code>.json` — strings only (`ui`, `meta`, `blocks{id}`, `figs{idx}`). Drop-in. - The viewer's `S()` helper falls back to English per-key, so a partially-translated language is still a **complete page**, never a half-broken one. This is what makes translations shareable as pure data — to Gemini's viewer too (the offer in `uploads/`).

## Accessibility (Stephen's explicit ask: image naming + alt) - Every `figure` has a descriptive caption that doubles as `alt`. - The 18 tiny UI thumbnails are **named** ("Wind dial control icon", "Engine tool icon", …) rather than left as decorative — so screen-reader users get the control name, not "image". - Skip-link, `<nav aria-label>`, `<main>` landmark, `lang`/`dir` attributes updated on switch, visible focus rings, `prefers-reduced-motion` honored.

## Brand + chrome - Accent = Pantone **180 C (#BE3A34)** (Simtable official). Used for section chips, rules, links, callout borders, the brand mark. - Type system inherited from the Groundworks house style: Cormorant Garamond (display) + Inter (body) + JetBrains Mono (labels/version); Noto Sans SC for Chinese. - Header: `v3.1` + build timestamp (baked into `content.json` at build time, not view time). - **Icon+description grid:** the manual is full of "icon → control name → description" entries. Floating the named thumbnail beside its paragraph turns a text wall into a scannable catalog — the biggest readability win over the text-only original.

## file:// portability (CORS fallback) The viewer `fetch()`es `content.json` + `lang/<code>.json` — which a browser blocks under the `file://` origin (unique-origin CORS). So `inline-data.py` bakes a copy of all of them into a `<script id="manual-data" type="application/json">` block. `loadJSON()` tries the external file first (so drop-in translations stay live when served over http) and falls back to the inlined copy (so double-clicking the file works with no server). Re-run `inline-data.py` after any content/lang change. `<`/`>`/`&` are `\u00xx`-escaped in the blob so it can't break out of the script tag.

## Print-to-PDF (inherited pattern, kept) `window.print()` + a light `@media print` theme + `@page { size: letter }` + a `beforeprint` swap of `document.title` to `Simtable_AnyHazard_UserManual_v3.1[_<lang>]` so the browser's Save-as-PDF defaults to a versioned, language-stamped filename. Figures and notes get `break-inside: avoid`.

## Deliberate v1 limitation (be honest) Full **figure-caption + body** translation for fr/pt/zh/el is the next pass; today those fall back to English while the interface + full table of contents are localized in all six languages. The offer to Gemini is to finish and share those `lang/*.json` files.

## Competition frame (Hubler) Two beads, one demand ring (Stephen's "good-looking manual"), pulling on the same substrate (the AnyHazard content + the Groundworks print pattern). Parallel shooters; the better-wired path gets bound. See about.