diff --git a/.superpowers/brainstorm/.last-port b/.superpowers/brainstorm/.last-port
new file mode 100644
index 0000000..496f6db
--- /dev/null
+++ b/.superpowers/brainstorm/.last-port
@@ -0,0 +1 @@
+58245
\ No newline at end of file
diff --git a/.superpowers/brainstorm/.last-token b/.superpowers/brainstorm/.last-token
new file mode 100644
index 0000000..85a9459
--- /dev/null
+++ b/.superpowers/brainstorm/.last-token
@@ -0,0 +1 @@
+e13012f97798f2f41fa46ae58204a4bcd50a142b56e7cca42efec09e7633540f
\ No newline at end of file
diff --git a/.superpowers/brainstorm/1683332-1792128126/content/a-game-setup.html b/.superpowers/brainstorm/1683332-1792128126/content/a-game-setup.html
new file mode 100644
index 0000000..6c4949b
--- /dev/null
+++ b/.superpowers/brainstorm/1683332-1792128126/content/a-game-setup.html
@@ -0,0 +1,700 @@
+
+
+
+
+
+A · Every game has its own setup
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.superpowers/brainstorm/1683332-1792128126/content/b-routines-home.html b/.superpowers/brainstorm/1683332-1792128126/content/b-routines-home.html
new file mode 100644
index 0000000..08f41de
--- /dev/null
+++ b/.superpowers/brainstorm/1683332-1792128126/content/b-routines-home.html
@@ -0,0 +1,616 @@
+
+
+
+
+
+B · Routines get their own home
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.superpowers/brainstorm/1683332-1792128126/content/c-one-due-list.html b/.superpowers/brainstorm/1683332-1792128126/content/c-one-due-list.html
new file mode 100644
index 0000000..68b2144
--- /dev/null
+++ b/.superpowers/brainstorm/1683332-1792128126/content/c-one-due-list.html
@@ -0,0 +1,703 @@
+
+
+
+
+
+C · One due list
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.superpowers/brainstorm/1683332-1792128126/state/server-stopped b/.superpowers/brainstorm/1683332-1792128126/state/server-stopped
new file mode 100644
index 0000000..9a061a4
--- /dev/null
+++ b/.superpowers/brainstorm/1683332-1792128126/state/server-stopped
@@ -0,0 +1 @@
+{"reason":"stop-server.sh","timestamp":1792128694}
diff --git a/AGENTS.md b/AGENTS.md
index 8a50c01..4f5a972 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -56,7 +56,7 @@ falling back to checked-in fixtures.
## Three constraints that shape everything
1. **No accounts, no logins, no user records.** Completion state lives in the browser's
- `localStorage`, keyed by event ID. There is no user table and no session. Any request implying
+ `localStorage`, in a v2 envelope keyed by local game/server account and unchanged event ID. There is no user table and no session. Any request implying
"sync across devices" is solved with export/import JSON, not a server-side user.
2. **No LLM in the pipeline.** Event data is extracted by deterministic code-based parsers only.
There is no Anthropic dependency, no API key, and no per-run inference cost. A source that
@@ -133,6 +133,15 @@ cycle:
run `bun run build:feed`, which builds from snapshots and fixtures) before serving. A worktree
also needs its own `bun install`.
+**Browser verification must include an ordinary HTTP network hostname, not only localhost.**
+Localhost is a secure context even over HTTP, so it conceals API restrictions that affect LAN and
+WireGuard addresses. On 2026-10-16 the redesign called `crypto.randomUUID()` during first-visit
+migration: it was undefined on HTTP `10.20.20.2`, and startup mislabeled that TypeError as a saved-data
+failure. Browser-local IDs now come from `state/reader/ids.ts` using 128-bit `crypto.getRandomValues`,
+which works on HTTP too. Use that helper for accounts, operations, clock revisions and backups;
+never remint existing identities. A Chrome test hostname mapped to loopback reproduces the HTTP
+security boundary without contacting another machine. Test startup and subsequent writes there.
+
**`build:js` passes `--production`, and that flag is load-bearing.** Without it Bun bundles React's
*development* build: 566 KB rather than 344 KB (164 KB vs 103 KB gzipped), every element creation
validated at runtime, and `StrictMode` double-invoking effects — which in this app means
@@ -167,8 +176,9 @@ src/ingest/ html.ts, dates.ts (sixteen formats), merge.ts, sanitize.ts, ro
parsers/ game8.ts, wikigg.ts, akwiki.ts, fandom.ts, bawiki.ts, holodori.ts, iopwiki.ts,
stellasora.ts, arustats.ts, karendar.ts — keyed by SITE, not game
adapters/ index.ts — SOURCES registry binding url+game+parser, and the sanitize seam
-src/client/ React app, service worker, manifest
- state/ progress, daily log, ignores, prefs, sort — all localStorage
+src/client/ ReaderApp (shared interfaces), components/redesign/, service worker, manifest
+ state/reader/ active v2 envelope, accounts/clocks, migration, archive, backup/restore
+ state/ legacy v1 helpers, progress validators, prefs normalization, sort
gameOrder.ts — the reader's game order, and the A–Z fallback; pure
useCustom.ts — the reader's own games and events (PRD F13)
lens.ts — who sees which rows (focus, outstanding, next-to-expire); pure
@@ -1228,48 +1238,19 @@ to an open page). Four things hold it up:
keeps a retired source's slot warm rather than pruning it. `games` itself is left in feed order on
purpose: `adoptNewLanes` diffs it and `knownGames` is seeded from it, so ordering it at source
would let a display preference reach the code that hides a reader's games.
-- **Reordering is a settings-only affordance.** The focus bar and the dailies strip are the fastest
- tap targets in the app, and a drag target on top of a tick target costs somebody a streak the first
- time it misfires — so the live surfaces are never draggable. Both a handle and ↑↓ buttons ship,
- because touch fires no drag events at all: the arrows are the mechanism and the drag is the pointer
- fast path, and being ordinary buttons is what makes the whole thing reachable by keyboard and
- screen reader without a second implementation of the same interaction.
-- **The settings panel is groups that state their own answer, and they ship closed.** Six
- `` groups — games, server region, appearance, what you see, your own, your progress — each
- with its current state on the summary line (`Europe`, `17 of 18 on · A–Z`, `plus finished, not
- started`). It was one open block in two columns, which is readable at four games and not at
- eighteen, where the game list is eighteen rows of four controls sitting above the checkbox somebody
- came to tick. The state line is the half that makes collapsing honest: without it every question
- about how the app is set up costs a click, so the closed panel has to be a report and not a menu.
- Three rules follow. **Do not default a group open** to make something inside it findable — the empty
- states name the group as well as the switch, which is what makes that unnecessary. The summaries are
- **derived from `prefs` at render**, never stored, so they cannot drift from the controls they
- describe. And **one group answers one question**, which is what the sixth group is: the region and
- the theme were a group called `Reading`, grouped on "both are how do I read this?" and summarising
- as `Europe · Dark` — two unrelated answers joined by a dot, under a name for neither of them, in a
- panel whose whole premise is that a closed line answers *its* group. The region is not a reading
- preference: region-scoped ends and every daily reset are cut on that server's clock (§ Domain
- rules), so it is the one control here that can make a countdown wrong, and it is now a line of its
- own rather than filed behind a word for the theme.
- Native `` for the reason the reorder arrows are ordinary buttons: keyboard and screen
- reader reach it without a second implementation. `summary` is not an `a`, `button` or `[tabindex]`,
- so it needs its own `:focus-visible` rule in `styles.css` — the shared one does not reach it.
-- **The panel is one column of groups, and it uses the page's width.** Those are two claims and only
- the first survived a desktop read: the rows were held to `max-w-3xl` inside a shell a third wider,
- so every rule inside the panel stopped short of the panel's own, and it was the one block on a wide
- page not using the width — between a two-column checklist and a three-column footer that both do.
- A name at one end of a row and its state at the other is what "running now" already does with a
- title and its countdown, so full width is the house pattern rather than a stretch. Prose inside a
- group keeps its own measure, and the game list — the one thing tall enough to need it — takes **two
- columns of its own past `lg` once there are nine or more games**, flowing down then across so the
- numbers still read in a straight line and an arrow still swaps a row with its neighbour. Eighteen
- rows in one column was a 1,000px ribbon with two thirds of the width empty beside it.
-- **A game's dailies stay together, and grouping never re-sorts inside a group.** `dailyGroups`
- (`Dailies.tsx`) emits a game's standing chore followed by that game's repeating events, in the
- order those arrived; it replaced `[...chores, ...repeating]`, which put a game's chore and its own
- login event at opposite ends of the strip. Collapsed, the grouping is **adjacency only** — no
- per-game headings, because that strip is the part of the page answerable in ten seconds and a
- heading per game makes it the tallest block on it, pushing "next to expire" down the page.
+- **Reordering is a settings-only affordance.** Your games provides ordinary ↑↓ buttons for
+ keyboard, screen reader and touch. Live focus/daily tick targets are never draggable.
+- **Settings uses named pages.** General, Your games, Appearance, Lists, Region & time,
+ Data & sources, Backup, About Event Clock and Changelog. Keep the complete colophon in About;
+ Changelog has its own page and no footer link. The footer always states data age and links About.
+ Reset overrides live per game/server; display timezone is a separate preference.
+- **Both interfaces share the reader.** Structured (default, Round 6 B rail) and Relaxed (Round 7)
+ are CSS/layout choices over one feature tree. Theme, palette and row density are independent.
+ Do not mount the old v1 persistence hooks alongside ReaderApp's v2 store. See DATA-MODEL for
+ account IDs, clock revisions, migration recovery, backups and export validation.
+- **A game's routines stay together.** The separate daily panel keeps direct ticks; grouped
+ routines use per-game/server summaries. Collapse policy is configurable and focus can expand a
+ group without changing that saved policy. Timeline reset strips sit next to their game's events.
- **A day already gone is still tickable, and the window bounds display and never data.** People play
at midnight and tick at breakfast, so `catchUpDays` (`src/shared/daily.ts`) offers the last
`CATCH_UP_DAYS` game-days for a standing chore and for an event whose end was never announced —
diff --git a/TODO.md b/TODO.md
index 0083df6..d5804be 100644
--- a/TODO.md
+++ b/TODO.md
@@ -10,9 +10,12 @@ note only says where to start.
([spec](docs/superpowers/specs/2026-10-14-built-in-routines-design.md)), every-N-days cycles for
tasks and built-ins ([spec](docs/superpowers/specs/2026-10-16-cycle-routines-design.md)), and
battle pass windows as feed events ([spec](docs/superpowers/specs/2026-10-16-pass-windows-design.md),
- see its § Outcome). Plans are beside them in `docs/superpowers/plans/`. Nothing is in flight: no
- open branches or worktrees.
-- **No next task has been chosen.** The open items below are the candidates; the owner decides.
+ see its § Outcome). Plans are beside them in `docs/superpowers/plans/`. The redesign attempt is uncommitted and was rejected for substantial markup divergence.
+ **Start with [the replacement-agent handoff](docs/handoffs/2026-10-16-redesign-fidelity-handoff.md).**
+ The existing plan/ledger contain superseded completion claims; no extra branch/worktree.
+- **Redesign rejected for markup divergence; presentation correction pending.** See docs/design-reviews/2026-10-16-redesign-implementation.md.
+ The assigned follow-up is to faithfully port the approved markup and finish the redesign.
+ Unrelated open items below remain separate.
The ones that need the owner in the game first are marked as checks.
- **How the owner works** (their standing instructions, also in their personal Claude settings):
once they approve a design, carry it through spec → plan → execution with no extra review stops,
@@ -30,6 +33,17 @@ note only says where to start.
## Open items
+- [ ] **Redesign Settings → Your own events and games** (owner, 2026-10-17: “the split-up from
+ the mockup is nice, but I guess it needs a bigger design”). Deferred on purpose: it works as well
+ as it does today, which is enough for now. Direction from the owner: a heading per game you
+ created, with that game's events listed under it, taking inspiration from the Routines tab —
+ possibly reusing the routine editor (`components/redesign/RoutineDrawer.tsx`, the right-docked
+ create/edit shelf) as the base for an event editor. Not in the Round 6/7 mockups, so it needs its
+ own short design (brainstorm → spec → plan; owner approves once, then carry it through). Keep the
+ current behaviour (`components/YourOwn.tsx`) working until the replacement is accepted.
+ - Owner, same day, an alternative to weigh in that design: drop the separate settings page and
+ put an **Add** button on the Settings sidebar's “Your games” list, opening an event editor
+ there (a new game, or a new event under a game).
- [ ] Make it clear which game each *This week & month* chip belongs to (owner, 2026-10-16: "it can
become a bit unclear"). With built-ins for seven games, that row now holds many chips whose
titles do not name their game — "Weekly Routine", "Weekly Challenge", "City Stamina",
@@ -235,3 +249,20 @@ note only says where to start.
`EventForm`, `src/client/components/TaskForm.tsx`). Review them as a reader would — field
order, defaults, copy, validation messages, how a cadence and a measure are chosen, phone
width — against `docs/DESIGN.md` and `docs/PRODUCT.md`.
+- [ ] Follow up the 2026-10-18 pre-ship review of the v1→v2 reader migration. A schema-guardian review
+ found nothing blocking: event IDs, day keys and the feed contract are unchanged, and v1 keys are
+ never removed, so a rollback finds them byte-identical. These gaps remain, all low severity:
+ - Orphaned v1 entries (marks or amounts for a deleted `myevent:`/`mytask:`, or any entry whose game
+ can't be resolved) are not carried into v2 account data (`state/reader/migration.ts`,
+ `gameOf`). They survive in `legacyRaw`, `gacha-tracker:v2:migration-recovery` and the untouched v1
+ keys. Consider a catch-all bucket so a restored custom item finds them.
+ - Importing a v1 file whose region differs from the reader's account lands its marks in a new
+ `account:import-N`, not the visible account (`state/reader/import.ts`, `export.ts`). Kept, but
+ out of sight.
+ - `initializeReader` reconciles and writes back outside `withReaderLock`, with no compare-and-set
+ (`state/reader/store.ts`). A second tab's write in that window can be overwritten.
+ - Storage pressure: every save carries `legacyRaw`, and every automatic backup embeds the state,
+ `legacyRaw` and the v1 keys again, with no size cap (only manual backups are capped). Heavy
+ readers could reach the ~5 MB quota. Failures are reported, not silent.
+ - After a rollback, anything done in the new build lives only in v2 and isn't shown by the old app.
+ It's stranded, not lost.
diff --git a/design/mockups/round4/converged.html b/design/mockups/round4/converged.html
new file mode 100644
index 0000000..bad5560
--- /dev/null
+++ b/design/mockups/round4/converged.html
@@ -0,0 +1,1636 @@
+
+
+
+
+
+Round 4 · Round 4 · Combined review
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/design/mockups/round5/converged.html b/design/mockups/round5/converged.html
new file mode 100644
index 0000000..46dcd56
--- /dev/null
+++ b/design/mockups/round5/converged.html
@@ -0,0 +1,1813 @@
+
+
+
+
+
+Round 5 · Event Clock redesign
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/design/mockups/round5/refined.html b/design/mockups/round5/refined.html
new file mode 100644
index 0000000..d323bd1
--- /dev/null
+++ b/design/mockups/round5/refined.html
@@ -0,0 +1,1843 @@
+
+
+
+
+
+Round 5 · Quieter layout
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/design/mockups/round6/a-inline.html b/design/mockups/round6/a-inline.html
new file mode 100644
index 0000000..9964eaa
--- /dev/null
+++ b/design/mockups/round6/a-inline.html
@@ -0,0 +1,1968 @@
+
+
+
+
+
+Event Clock · Design A
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/design/mockups/round6/b-rail.html b/design/mockups/round6/b-rail.html
new file mode 100644
index 0000000..a912fbd
--- /dev/null
+++ b/design/mockups/round6/b-rail.html
@@ -0,0 +1,1968 @@
+
+
+
+
+
+Event Clock · Design B
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/design/mockups/round6/c-open.html b/design/mockups/round6/c-open.html
new file mode 100644
index 0000000..4de3a7a
--- /dev/null
+++ b/design/mockups/round6/c-open.html
@@ -0,0 +1,1968 @@
+
+
+
+
+
+Event Clock · Design C
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/design/mockups/round6/index.html b/design/mockups/round6/index.html
new file mode 100644
index 0000000..f4de58e
--- /dev/null
+++ b/design/mockups/round6/index.html
@@ -0,0 +1,8 @@
+
+Event Clock · Three layouts
Give the events room.
Three complete layouts for Event Clock. Each keeps the agreed routines, Archive, settings and onboarding, while moving focus and freshness controls out of the way of the event list.
Compare the same busy calendar in each. The top review bar can simulate older data, open onboarding and reset the sample state.
+A
Inline desk
Game focus beside the list heading. A small freshness line with Details. Your one-click dailies and pinned routines stay in the familiar left rail.
The smallest change to the layout you already liked; retains two columns.
Routines → Checklist display now controls the daily panel and group expansion. Numeric fields keep their native one-step arrows.
Settings includes Changelog as its own page, custom interface palettes, per-game source links and thanks, reset-time overrides with Automatic, and clearly labelled backup previews. Upcoming events is part of What do you want to track.
Returning user opens a guided tour in your preferred view. Take it or keep browsing; your progress stays intact.
This is a review prototype. Codes, source health and historical data are illustrative. The production app and earlier mockups are unchanged.
diff --git a/design/mockups/round7/converged.html b/design/mockups/round7/converged.html
new file mode 100644
index 0000000..624a035
--- /dev/null
+++ b/design/mockups/round7/converged.html
@@ -0,0 +1,2103 @@
+
+
+
+
+
+Event Clock · Airier unified design
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md
index 7a40874..db8c473 100644
--- a/docs/ARCHITECTURE.md
+++ b/docs/ARCHITECTURE.md
@@ -364,3 +364,56 @@ other, so `test/update.test.tsx` pins both ends of the `skip-waiting` handshake
cleanly because state is in SQLite.
- **No server-side rendering.** The feed is small and cacheable; a static SPA is enough.
- **No websockets.** Events change on a scale of hours; a 5-minute cache is more than adequate.
+
+## Reader application (2026-10-16 redesign)
+
+`App.tsx` exports `ReaderApp`; only the v2 reader store writes production progress. Legacy hooks
+remain as compatibility helpers, validators and tests, not mounted writers. ReaderContent coordinates
+shared Checklist/Timeline/Routines/Archive/Settings, account-aware detail adapters and timed archive
+transitions. `components/redesign/` contains the feature pages. `redesign.css` scopes Relaxed and
+Structured via data-interface, with one application tree and independent theme/density.
+
+`state/reader/` owns migration, schema, reducer, store, clocks, accounts, archive, backups, restore,
+imports, routines and event selectors. Operations re-read the persisted envelope under Web Locks;
+an IndexedDB readwrite transaction serializes browsers without Web Locks. Failed locking/storage
+never publishes an optimistic success. Storage events update other tabs. Field timestamps protect
+newer edits and tombstones; account writes rebase changed routine choices. Legacy deltas reconcile
+on startup into the original migrated accounts; raw keys remain preserved.
+
+Clock revisions freeze automatic metadata and overrides. Imported historical clocks are excluded
+from active clock resolution; history resolves each explicit revision. Display timezone lives in
+DisplayTimeContext and never changes the feed.
+
+Events and redeem codes use separate validated static feeds. `build-codes.ts` reads pinned snapshots
+or fixtures offline and preserves firstObservedAt in snapshots/codes-first-seen.json. The refresh
+runner combines event adapters and count-only code sources while sharing robots, floors, per-host
+spacing and snapshot gates. No fake GachaEvents represent codes. Both feeds are network-first in
+the worker; manual refresh bypasses the browser cache. Shell update waiting/activation is unchanged.
+
+Backups are outside the reader envelope. Restore validates first, writes a recovery export, prepares
+a pending transaction, then replaces live state. Startup finishes an interrupted transaction before
+opening the reader. Browser UI restore uses the same cross-tab lock as ordinary edits.
+
+
+### Ordinary HTTP / WireGuard hosts
+
+Reader identity minting uses `state/reader/ids.ts`: 128-bit `crypto.getRandomValues` tokens for
+accounts, operations, reset revisions and backups. The redesign's initial `randomUUID` calls were a
+startup bug on ordinary HTTP network hosts; unlike localhost, these are not browser secure contexts,
+even when reached through a WireGuard tunnel. Existing IDs are unchanged and valid. Initialization
+preserves migration recovery before minting, so the failed first visit did not discard legacy bytes.
+
+Regression verification uses a Chrome test hostname mapped to loopback with isSecureContext false,
+randomUUID unavailable and actual getRandomValues/IndexedDB available. Existing-save migration,
+new setup, editing, additional servers, reset override/revert, backup and reload all pass there.
+
+
+### Presentation correction (2026-10-17)
+
+`ChecklistEvent`/`NextDeadline` render the approved list hierarchy; `TimelinePage` owns the shared
+axis for event spans and routine periods. `RoutinesPage` groups by reset or account, with native
+`RoutineEditor` dialogs. `ArchiveDetail` reads saved archive snapshots and never routes historical
+progress through the live event editor. Settings keeps one shared tree for both interfaces, with
+separate default-view/custom-content pages and direct game profiles. Structured is the absent-value
+default at prepaint, preference normalization, setup and appearance application. No state keys,
+event identities or stored clock revisions changed for this presentation correction.
diff --git a/docs/DATA-MODEL.md b/docs/DATA-MODEL.md
index 6160317..9aab3ab 100644
--- a/docs/DATA-MODEL.md
+++ b/docs/DATA-MODEL.md
@@ -244,7 +244,10 @@ CREATE TABLE snapshots (
`region_ends` and `payload` hold JSON as TEXT; parse them through the Zod schema on read so a
malformed row surfaces at the boundary rather than deep in the UI.
-## Client-side storage
+## Legacy client-side storage (v1 compatibility)
+
+Production uses the [active v2 reader envelope](#active-reader-state-v2-2026-10-16). The keys and
+merge rules in this section describe migration input and retained compatibility helpers.
Namespaced, versioned, and small. Nothing here ever goes to the server.
@@ -772,3 +775,48 @@ Two timestamp fields distinguish crawler activity from site updates: `lastConfir
the crawler last successfully contacted and verified the source (HTTP 200 or 304 Not Modified), and
`contentChangedAt` records when the served bytes last changed. Both default to `null` so feeds built
before their introduction continue to validate.
+
+## Active reader state (v2, 2026-10-16)
+
+The localStorage/export descriptions above document legacy v1 compatibility. Production mounts
+only `ReaderApp` and persists `gacha-tracker:v2:reader`, validated by state/reader/schema.ts.
+One stable `account:` represents one tracked game/server; it is browser-local, not a login.
+Original event/task/daily IDs remain unchanged inside account data. Account data contains progress,
+ignored, and daily/amount maps nested by clock revision. A revision freezes effectiveAt, region,
+reset override and automaticBase (offset/hour/minute). Changes append revisions; imported clocks
+can be historicalOnly and never replace the local active reset.
+
+The envelope includes accounts, data, archive, seenEvents, prefs, customGames/customEvents/tasks,
+codeSeen (per game), codeRedeemed (per account), and eventSeen (per account). Archive/seen keys are
+JSON.stringify([accountId,eventId]); archive snapshots retain event, outcome/time, resolved end,
+clockRevision, progress, days and amounts. Account/revision cross-references are validated before
+restore. Field timestamps and operation IDs rebase writes and preserve deletion tombstones. Unknown
+fields pass through exports; progress imports retain opaque root/account extensions in
+importedExtensions as well as mergeable known fields. Existing same-server settings remain local.
+
+Migration preserves raw v1 keys in place and in migration recovery, creates one account per game at
+the saved region and maps old follows/pins/logs. Newly offered games remain off. Startup compares
+legacyRaw with surviving v1 keys to carry older-tab deltas into the original account without
+resurrecting newer v2 deletions. Parser aliases carry progress, ticks, amounts and archive/seen IDs.
+
+Keys outside the envelope:
+
+- gacha-tracker:v2:migration-recovery — raw pre-migration bytes.
+- gacha-tracker:v2:backups — at most 7 daily + 4 weekly + 6 monthly UTC bucket snapshots.
+- gacha-tracker:v2:restore-recovery — the complete pre-restore export.
+- gacha-tracker:v2:restore-pending — prepared replacement, resumed before mounting on startup.
+
+Exports use format gacha-tracker-export, version 2, exportedAt, reader and legacy. Progress exports
+mark scope progress; full imports preview replacement, progress imports preview a merge. Backups
+are never recursively included. Original v1 export envelopes remain convertible.
+
+Prefs add interface (structured/default or relaxed), density (standard/default or compact), scheme
+(slate/warm/plum/custom), separate light/dark customPalette, per-game gameColors, timezone
+(null/Automatic or IANA), checklist daily placement/expansion and redesignTourSeen. Main view remains
+soon or timeline; utility navigation is transient. Future/unreadable records remain exportable even
+when this build cannot offer editing controls.
+
+
+The fidelity correction adds `prefs.routineGroup` (`reset`, the absent-value default, or `game`)
+for the Routines page's grouping. It changes no routine IDs or placement keys. Structured is the
+interface default as of the owner's 2026-10-17 instruction; explicit saved Relaxed is preserved.
diff --git a/docs/DESIGN.md b/docs/DESIGN.md
index 707611c..7421917 100644
--- a/docs/DESIGN.md
+++ b/docs/DESIGN.md
@@ -2,84 +2,84 @@
name: Event Clock
description: Live and upcoming gacha events, sorted by what expires next.
colors:
- ground: "#12141c"
- surface: "#1b1e2a"
- raised: "#232739"
- hairline: "#2c3145"
- ink: "#e9ebf3"
+ ground: "#1a1c24"
+ surface: "#22252f"
+ raised: "#30333c"
+ hairline: "#43454c"
+ ink: "#eeeae3"
ink-strong: "#ffffff"
- muted: "#888fa6"
- faint: "#5a6178"
+ muted: "#b1afad"
+ faint: "#9c9b9b"
scrim: "rgb(18 20 28 / 0.8)"
glow: "#1a1e2c"
calm: "#55607f"
- near: "#5b8def"
- soon: "#e8b33c"
- critical: "#ff4d6a"
- ground-light: "#edf0f7"
- surface-light: "#ffffff"
- raised-light: "#dfe4f0"
- hairline-light: "#c9d1e2"
- ink-light: "#151824"
+ near: "#8eb3f4"
+ soon: "#e9b95b"
+ critical: "#ff8598"
+ ground-light: "#f3f1ec"
+ surface-light: "#fffdf8"
+ raised-light: "#f0eeea"
+ hairline-light: "#dddbd8"
+ ink-light: "#28272d"
ink-strong-light: "#000000"
- muted-light: "#535b73"
- faint-light: "#767e94"
+ muted-light: "#69676a"
+ faint-light: "#6e6d6e"
scrim-light: "rgb(26 31 48 / 0.42)"
glow-light: "#ffffff"
calm-light: "#8a92a8"
- near-light: "#2b62c6"
- soon-light: "#8a6008"
- critical-light: "#c81e3c"
+ near-light: "#315fa5"
+ soon-light: "#805a12"
+ critical-light: "#b12947"
typography:
numeral:
- fontFamily: "Chakra Petch, ui-sans-serif, system-ui, sans-serif"
+ fontFamily: "Bricolage Grotesque, ui-sans-serif, system-ui, sans-serif"
fontSize: "2.75rem"
fontWeight: 700
lineHeight: 1
letterSpacing: "-0.025em"
fontFeature: "\"tnum\" 1"
display:
- fontFamily: "Chakra Petch, ui-sans-serif, system-ui, sans-serif"
+ fontFamily: "Bricolage Grotesque, ui-sans-serif, system-ui, sans-serif"
fontSize: "1.75rem"
fontWeight: 600
lineHeight: 1.15
letterSpacing: "-0.025em"
wordmark:
- fontFamily: "Chakra Petch, ui-sans-serif, system-ui, sans-serif"
+ fontFamily: "Bricolage Grotesque, ui-sans-serif, system-ui, sans-serif"
fontSize: "0.9375rem"
fontWeight: 700
letterSpacing: "0.02em"
reading:
- fontFamily: "Chakra Petch, ui-sans-serif, system-ui, sans-serif"
+ fontFamily: "Bricolage Grotesque, ui-sans-serif, system-ui, sans-serif"
fontSize: "0.875rem"
fontWeight: 600
fontFeature: "\"tnum\" 1"
title:
- fontFamily: "Public Sans, ui-sans-serif, system-ui, sans-serif"
+ fontFamily: "Figtree, ui-sans-serif, system-ui, sans-serif"
fontSize: "0.9375rem"
fontWeight: 500
lineHeight: 1.375
body:
- fontFamily: "Public Sans, ui-sans-serif, system-ui, sans-serif"
+ fontFamily: "Figtree, ui-sans-serif, system-ui, sans-serif"
fontSize: "0.875rem"
fontWeight: 400
lineHeight: 1.625
summary:
- fontFamily: "Public Sans, ui-sans-serif, system-ui, sans-serif"
+ fontFamily: "Figtree, ui-sans-serif, system-ui, sans-serif"
fontSize: "0.8125rem"
fontWeight: 400
lineHeight: 1.375
control:
- fontFamily: "Public Sans, ui-sans-serif, system-ui, sans-serif"
+ fontFamily: "Figtree, ui-sans-serif, system-ui, sans-serif"
fontSize: "0.75rem"
fontWeight: 500
label:
- fontFamily: "Chakra Petch, ui-sans-serif, system-ui, sans-serif"
+ fontFamily: "Bricolage Grotesque, ui-sans-serif, system-ui, sans-serif"
fontSize: "0.6875rem"
fontWeight: 600
letterSpacing: "0.14em"
tag:
- fontFamily: "Public Sans, ui-sans-serif, system-ui, sans-serif"
+ fontFamily: "Figtree, ui-sans-serif, system-ui, sans-serif"
fontSize: "0.625rem"
fontWeight: 500
rounded:
@@ -170,7 +170,11 @@ components:
# Design System: Event Clock
-## Overview
+The active Relaxed/Structured layout and palette contract is recorded in
+[Active redesign system](#active-redesign-system-2026-10-16). The component rationale below describes
+the preceding instrument-panel system; use the active section and live token resolver for new work.
+
+## Original component rationale
**Creative North Star: "The Lit Instrument Panel"**
@@ -528,3 +532,105 @@ rather than being toned down.
chooses.
- **Don't** put a second control inside a row, or a drag target on the focus bar or the dailies
strip.
+
+## Shipped redesign system (2026-10-18)
+
+The owner accepted this build as the initial redesign on 2026-10-18, after screen-by-screen review
+against the approved mockups (`design/mockups/round6/b-rail.html` for Structured,
+`design/mockups/round7/converged.html` for Relaxed) and the corrections recorded round by round in
+`docs/handoffs/2026-10-17-redesign-second-rejection-handoff.md`. This section supersedes the original
+shell/type/palette/layout descriptions above. Those older sections remain the rationale for the detail,
+timeline and uncertainty components that carried over. Overrides live in `src/client/redesign.css`
+and `src/client/state/appearance.ts`.
+
+### Shell and layout
+
+- **Interfaces.** Settings → Appearance offers Structured (the Round 6 B rail; the default for new
+ installs and missing preferences) and Relaxed (Round 7). A saved Relaxed choice stays Relaxed.
+ Structured uses Barlow Condensed/Barlow and adjoining ruled sections. Relaxed uses Bricolage
+ Grotesque/Figtree with separated panels. Fonts keep system fallbacks offline.
+- **Navigation.** The tabs are Checklist, Timeline, Routines and Archive, with Settings on the right.
+ The EVENTCLOCK logo returns to the default view. Each page is in the address bar (`#checklist`,
+ `#settings/games/genshin`, …), and changing tab starts at the top of the page.
+- **Width.** The page fills up to 1920px (Relaxed 1840px) and centres beyond that.
+- **Two insets on `.reader-ui`.** `--edge` (16px) between content and the page's outer edges.
+ `--gutter` (24px) where a side column (Checklist rail, Settings nav, Archive sidebar) meets the main
+ content. At ≤700px, where everything stacks, both are the edge.
+- **Checklist rail.** 360px wide (Relaxed 380px; 280/300px at medium widths). Top to bottom: the
+ focus pills, Next to expire (headline, meter and the next three), Today's dailies, then pinned
+ routines grouped by game.
+
+### Urgency and colour
+
+- **Two axes, never crossed.** Game hue answers whose event: the 3px strip on the left of each
+ Standard row, the Compact game cell, the pills. The urgency ramp answers how long: countdowns and
+ meter ticks.
+- **The urgency ramp is live's.** Grey for a week or more, blue under a week, yellow under 3 days, red
+ under a day: `--color-urgency-{calm,near,soon,critical}`, set by `urgencyTokens`. It is separate
+ from the accent, so a chosen accent never changes urgency.
+- **Reader-editable.** Settings → Appearance → Urgency colours sets each step per theme
+ (`prefs.urgencyPalette`), with any scheme. Values are nudged only as far as 3:1 contrast, the
+ large-text bar.
+- **Fallback for the live UI.** `URGENCY_COLOR` falls back to the old `--color-*` tokens, so the
+ live UI is unchanged.
+- **Schemes.** Slate, Warm, Plum and Custom shift surfaces. Custom stores ground/surface/text/accent
+ per theme. Text and accent derive at least 4.5:1 contrast on ground and surface. Game colours are
+ set per game in Your games.
+
+Default Slate tokens:
+
+| Role | Structured dark | Structured light | Relaxed dark | Relaxed light |
+| --- | --- | --- | --- | --- |
+| Ground | #0e1117 | #eef0f5 | #1a1c24 | #f3f1ec |
+| Surface | #141923 | #ffffff | #22252f | #fffdf8 |
+| Primary text | #eeeae3 | #28272d | #eeeae3 | #28272d |
+| Accent | #8eb3f4 | #315fa5 | #8eb3f4 | #315fa5 |
+
+### Checklist
+
+- **Heading.** "Running now" sits with the sort and density buttons on one row. Under them is one
+ line: the count on the left and live's legend on the right ("bars = time left · colour = ends in a
+ week+ / under a week / under 3 days / under a day").
+- **Standard rows.** Game, title, optional description, then the countdown, date and meter, with the
+ chevron 10px from the date. Every row has a meter, with or without a description; the mockup hid
+ it on rows without one, and the owner chose consistency.
+- **Compact rows.** A ledger with Game / Event / Time left / Ends in columns.
+- **"Not started yet."** It opens a second section: a 48px gap, a 2px accent rule and a raised band
+ with a "Coming up" eyebrow. In Compact its columns read Duration / Starts in.
+
+### Focus pills
+
+They have live's chip feel: 30px tall, 12px text, a 30% hue border unpressed, the full hue with a 14%
+wash when pressed. State changes ease over 130ms. Hover adds a hue ring and a 1px lift, a press
+scales to 0.96, and games with nothing outstanding dim to 50%. The "Focus" label is a 12px body-face
+legend on the same baseline as "Next: …".
+
+### Ticking things off
+
+- **Today's dailies.** The whole row is the target. A tick washes the row in its game's hue, pops the
+ box and draws the check. A pip bar and "N of M done" sit under the heading. A check-in goal (for
+ example "5/5 wk") counts as done once met, with a dotted check, and shows its goal count instead of
+ a streak.
+- **Pinned routines** use the same card language. A box ticks a check-in routine, or completes an
+ amount routine in one click. "+1" steps an amount, and the count opens an editor. Typing a value at
+ or over the target completes it after a 450ms pause.
+- **Completion animation.** The bar fills, the row warms, then the box pops. Unticking undoes it:
+ the amount from before goes back, or the editor opens when that value isn't known.
+- **Celebration.** A single completion fires live's small `Fireworks` in the row. Finishing a whole
+ section (every daily, or every routine in a game's group) fires `Fireworks grand`: seven bursts in
+ two waves with the game's hue mixed in, a light sweep, and the "All done" line popping.
+- **When it fires.** Only an action taken now that completes the item or section, never arriving
+ complete. Reduced motion removes all of it.
+
+### Other pages
+
+- **Routines.** Bands by reset (Dailies, Weeklies, Monthlies, Cycles) or by game. The table has tick
+ and click-to-edit amount cells. The library and create/edit drawers are shelves.
+- **Timeline.** Two looks (new and classic) chosen in Appearance. Dailies are not on the timeline.
+- **Settings.** About, Changelog, Data & sources (list options first), Region & time, Appearance
+ (interface, theme, scheme, urgency colours, default view, game order, timeline design, focus bar,
+ checklist group), Your own events and games, Backup (tiers plus a manual "Your backups" list), and
+ per-game Setup / Codes / About.
+- **Returning-user tour.** Routines → Checklist → Timeline → Redeem codes → Archive → Settings.
+ Continue stays in a fixed place, Back sits beside it, and Skip is apart. "Replay the introduction"
+ starts it at step 1.
diff --git a/docs/INGESTION.md b/docs/INGESTION.md
index 1bb85c6..cae93a6 100644
--- a/docs/INGESTION.md
+++ b/docs/INGESTION.md
@@ -640,3 +640,22 @@ fixture is the regression test proving the parser still handles the previous for
guessing, impossible calendar dates are rejected, ranges crossing New Year roll the start year back,
and abbreviated months parse. `test/merge.test.ts` covers cross-source agreement, disagreement, and
rerun disambiguation. These are the last line of defense before a wrong date reaches a user.
+
+
+## Redeem codes source (2026-10-16)
+
+Genshin codes use https://game8.co/games/Genshin-Impact/archives/304759, separately from events.
+One descriptive-agent robots request and one page request returned HTTP 200 during capture; no
+access workaround or repeated source fetch was used. The raw page and capture metadata are pinned
+as fixtures/genshin/game8-codes-2026-10-16.*, with ten independently counted current codes. The
+parser reads actual code inputs/current reward rows and excludes expired programme sections; a
+future-dated programme fixture variant verifies that still-current programmes are admitted.
+Unannounced expiry remains null. A printed calendar date is expiresOn, never a fabricated exact
+expiresAt instant. Attribution is retained on every code.
+
+src/ingest/codes/registry.ts registers a count-only refresh source. The existing runner shares robots,
+six-hour floors, conditional requests, per-host spacing and snapshot gates across event/code sources.
+Event adapter schemas are unchanged. scripts/build-codes.ts builds data/codes.v1.json offline from
+snapshots or fixtures and preserves observation timestamps in snapshots/codes-first-seen.json.
+Code newness is per game; redemption is per local game/server account and never clears newness by
+itself. No code row is synthesized into the event feed.
diff --git a/docs/PRD.md b/docs/PRD.md
index 4f049c7..2e846fb 100644
--- a/docs/PRD.md
+++ b/docs/PRD.md
@@ -111,14 +111,9 @@ few pixels apart the board is being asked the one question it exists to answer a
reader to squint. It also gives a one-day event a bar to read and to tap rather than the 34px
minimum every short bar collapses to.
-**What has not started yet is shown to a new reader, and either can switch it** — `prefs.showUpcoming`,
-which governs **both views**: the board plots them, and the checklist keeps its "Not started yet"
-section (F2). One question, asked once, because they are the same events. It was off by default,
-because this app answers *what expires next* and a reader with fourteen lanes has a next patch queued
-behind every one of them. Since 2026-10-13 (repository owner) first-run setup turns it **on**: a new
-reader should see what is coming before deciding they do not want it. The stored default is still
-`false`, so an existing install whose saved prefs predate the field keeps today's answer rather than
-having it switched on underneath its reader.
+**Upcoming events are optional and off initially.** New setup asks “Show events that haven't
+started” under “What do you want to track?”. The same saved answer governs Checklist and Timeline;
+Checklist separates them under “Upcoming events”. Existing saved choices survive migration.
On the board that is structural as well as editorial: the window is drawn from what is plotted, so
showing the future stretches the right edge weeks past today and squeezes the running bars the
@@ -129,10 +124,8 @@ quiet fortnight, and this app does not leave a reader to infer what it is not sh
page header states `N live · N upcoming` whatever the setting says, and either view left with
nothing to show says so in words and names the setting rather than rendering an empty column.
-The switch is in settings (F4), with the two other answers to *what am I allowed to look at* —
-`showCompleted` and `showIgnored` — and **not** in the board's own header beside the stacking and
-scale controls. That is the line those two draw: they reshape what is already on the board, which is
-why they are reached for while reading it, and this one decides what is on it at all.
+The switch lives in Settings → Lists, because it decides which events enter both main views.
+Done and ignored events live in Archive; Lists has no redundant Archive link or reveal switches.
**Switched on, the board says in words where they begin.** A bar drawn to the right of the "now"
rule is only *implicitly* in the future, and implicitly is not a standard this product holds itself
@@ -198,42 +191,17 @@ an explicit "show all N". This truncates the *view* only: the order is untouched
the cut are still counted in the header, still on the timeline, and one tap away.
**F3 — Mark completed.**
-A toggle on every event, in both views. State is written to `localStorage` immediately and
-optimistically — there is no server round trip and no failure case. Completed events stay visible
-but de-emphasized; a filter toggles them out entirely.
+Done writes durable progress and a metadata snapshot immediately. A confirmation remains for about
+1.5 seconds before the row fades/collapses into Archive. Undo is available for eight seconds;
+Archive Restore remains available afterwards. A failed write is disclosed and never presented as
+saved. Reduced motion removes the fade.
-**F4 — Filters.**
-Filter by game (multi-select, persisted) and by event type. Hiding a game hides it from both views.
-Preferences persist in `localStorage`.
-
-The panel holding them is **six collapsed groups, each stating its own answer on the summary line**
-— games, server region, appearance, what you see, your own games and events, your progress. It was
-one open block of everything, laid out in two columns on a wide screen, which read fine at four games
-and stopped working at eighteen: the game list alone is eighteen rows of four controls, and it sat
-above the checkbox a reader had come down here to tick. Collapsing on its own would only trade that
-friction for another, though — a group that shows nothing but its name turns *what is my region set
-to?* into a click — so each summary answers its group's question, and the closed panel is a six-line
-report of how the app is configured. Opening one is for changing an answer, not for reading it.
-
-Three things follow. **The groups ship closed**, including the one holding the filters that other
-copy points at: the empty states name both the switch and the group it is in, which is more findable
-than a checkbox in a wall was. **The summary states are derived, never stored** — they are a reading
-of `prefs`, so they cannot disagree with the controls inside. And **a group answers one question**.
-The region and the theme began as one group called *Reading*, on the argument that both are "how do I
-read this?" — which summarised as `Europe · Dark`, two unrelated answers joined by a dot, under a
-name for neither. The region is not a reading preference: it is which server the reader's account is
-on, and region-scoped ends, undated deadlines and every daily streak are read off that server's
-clock, so it is the one control in this panel that can make a countdown wrong. It gets its own line,
-and says as much when opened.
-
-**The panel uses the page's width**, like every other block on it. The rows were held to a narrower
-measure on the argument that a name and its state stop reading as one line across a wide screen, but
-the panel's own rule spans the shell, so the rules inside it stopped a third of the column short and
-the panel became the one thing on a desktop page not using the width — sitting between a two-column
-checklist and a three-column footer that both do. A title at one end of a full-width row and its
-countdown at the other is what "running now" does directly above it. Prose inside a group keeps a
-readable measure, and the game list takes two columns of its own on a wide screen once there are nine
-or more games, because eighteen rows in one column is a thousand pixels of ribbon down the left.
+**F4 — Settings and visibility.**
+Settings has named pages: General, Your games, Appearance, Lists, Region & time, Data & sources,
+Backup, About Event Clock, and Changelog. Your games controls visibility/order, tracking choices,
+servers, reset overrides and colours; Lists controls upcoming events and timeline grouping.
+Routines owns follows and independent Checklist/Timeline pins. Utility navigation does not replace
+the saved default main view. Empty states name where to change the relevant choice.
**F4a — Focus one game at a time.**
Switching games on and off says *which games the reader plays*, and is set once. It is the wrong
@@ -253,15 +221,26 @@ has left the feed is ignored rather than obeyed, so it can never strand the read
whose cause is elsewhere. Each chip carries that game's outstanding count, so a game with nothing
waiting says so before it is visited.
-**F5 — Region selection.**
-A user picks Asia / America / Europe once. For events where `regionScoped` is true, all displayed
-end times resolve to that region's server reset. This is stored in `localStorage` and defaults to a
-guess from the browser timezone, shown as a dismissible "showing America server times — change".
+**F5 — Servers and display timezone.**
+Default server is chosen in setup and Region & time. Each locally tracked game/server has its own
+stable account identity and progress. “Add another server” is for simultaneously playing multiple
+servers: it starts fresh tracking choices and never clones marks. Server/reset changes append clock
+revisions so history stays on its original clock. Overrides can revert to Automatic. Display timezone
+is Automatic by default or a searchable IANA zone; it changes printed dates and manual date entry,
+never a source's exact timestamps or game-day keys.
-**F6 — Export / import.**
-Because there are no accounts, moving between devices is manual: download a JSON file of completed
-IDs and preferences, upload it elsewhere. Import merges rather than replaces, and never removes a
-completion the user already has.
+**F6 — Export / import and backup.**
+No login is involved. Export progress / Import progress merges per game/server, retaining local
+preferences and active reset configuration. Day logs union; newer records/amounts win conflicts.
+Imported clock revisions remain accessible as historical clocks. Unknown extensions are retained in
+the envelope. Export all / Import all restores the complete validated envelope through a preview,
+with a recovery copy before replacement. Undo last restore reverses it. Version-1 exports remain
+convertible; original v1 browser keys are retained after migration.
+
+Local backup history keeps seven daily, four weekly and six monthly snapshots while the app is
+used. UTC buckets keep their latest snapshot; backups never contain other backups. View backup
+opens the preview; Restore this backup is the separate committing action. Storage failures are
+visible. Browser deletion removes local history; export is the way to keep an external copy.
**F12 — Record your own progress and effort.**
Three states, not two: untouched, doing it, done. Plus an optional effort estimate — quick, short,
@@ -418,15 +397,16 @@ the one after it — because they answer "what ends soonest". The timeline draws
every occurrence in view, because it answers "what is the rhythm".
**F8 — First-run setup.**
-Before any events are shown, the reader picks which games they play, and how they want to read
-them. A calendar full of games they
-don't play is worse than an empty one — it buries the thing they came for. Nothing is preselected
-and the button stays disabled until something is chosen; guessing on their behalf and hoping they
-notice is worse than asking. **The games are listed alphabetically by name**, not in feed order:
-everywhere else a lane's position carries meaning, but here the reader is scanning for the two or
-three names they already know, and a list ordered by which game happened to hold the first event row
-gives them nothing to scan by. The choice is stored as *hidden* games, the inverse — which is a storage
-shape, not a policy: what happens to a game added later is decided separately, below.
+Before events are shown, a four-step wizard asks for games, default server and preferred appearance,
+tracking kinds, and the default main view. Games are alphabetical, nothing selected. Events are the
+only initially selected tracking kind. Dailies, weekly/monthly and cycles have short descriptions;
+unavailable kinds remain selectable and say nothing is currently detected. Upcoming visibility
+belongs to that tracking step and starts off. Server and appearance can be changed later.
+
+Returning readers get a one-time introduction and optional four-step tour. Skipping or finishing
+preserves progress and the saved main view; General can replay it.
+
+
**A game added later arrives switched off.** Adding a source is our decision, not the reader's, and
someone who plays two games did not ask for the other twelve; a calendar that fills itself up is the
@@ -447,10 +427,13 @@ The view question (F2) sits under it, with each option drawn rather than only de
it arrives already answered, and the screen says where to change it afterwards: the tabs are small
text in a corner, which is the one control a first-time reader will not find on their own.
-**F9 — Ignore an event.**
-Distinct from completing one. "Done" keeps an event visible and counted; "not interested" removes it
-from both views entirely. Ignored events stay recoverable: a count and a reveal toggle appear in
-settings once there is something to reveal.
+**F9 — Ignore and Archive.**
+Ignore saves immediately and follows the same confirmation/Undo transition as Done. Both move to
+Archive with event metadata, notes, goals, logs, resolved end and clock revision. Previously seen
+followed events that expire unfinished also enter Archive; unknown-end events never expire by a
+guessed date. Per-game history is default, with an alternate chronological history. Story statistics
+include completion, check-ins and shortfalls where known; ignored events are excluded from success
+rates. Restore recovers saved progress without pretending an expired event is live.
**F10 — Works offline.**
The reader's question is answered entirely by data already on the device, and countdowns run off the
@@ -459,9 +442,9 @@ the last feed it downloaded. Offline is disclosed in the header and above the fo
data must never be presented as current.
**F11 — Credit and disclaimer.**
-The sources that compile these calendars, and the studios that make the games, are named on the same
-screen as the data rather than one navigation step away. The page states plainly that it is
-unofficial and unaffiliated, and that the source page is the authority when the two disagree.
+Settings → About Event Clock preserves the full colophon, source credits, unofficial/unaffiliated
+disclaimer and feedback links. The footer links About. Each game's About lists the real data sources,
+links and thanks; event detail links its authoritative source.
**F14 — Say when a new version of the app is ready.**
F10 caches the shell so the app survives losing signal, and the same cache is why a reader who never
@@ -483,42 +466,29 @@ arrive without a reload, and calling that a new version would train readers to d
unread.
**F7 — Freshness disclosure.**
-The footer shows when the feed was last updated, per game. If a game's data is more than 48 hours
-stale, its lane carries a warning badge. Never present stale data as current — the whole value
-proposition is trust in the dates.
+The footer always states data age, never build age. Only tracked games with stale data produce a
+compact “Event data may be out of date” row in the shared updates area. New events and new codes
+remain distinct notices. Data & sources and event detail expose the last successful check, last
+content change, source links and recorded reasons. A successful 304 confirms a check without
+claiming the wiki changed.
-**The reader can ask for the newest data.** A reload is not enough — the browser may keep the feed
-for minutes — so *Check for new data* beside the age asks the server past that copy and swaps a newer
-feed in without a reload. It says what happened: updated, nothing newer published yet (sources are
-read about twice a day, and the button cannot make that sooner), or the server could not be reached.
-"Newer" is judged on events and source health, never on the build stamp. Disabled offline; nothing
-polls on its own.
+Check for new data revalidates event and code feeds, compares their content rather than generatedAt,
+and reports updated, unchanged or failure. It does not scrape source sites from the browser.
-**The staleness notice names only the games the reader has switched on, and says that it does.**
-The headline age is about the feed and stays whole; the notice under it is an *instruction*, and the
-only remedy it offers is "go and check that game's source page" — which a reader cannot act on for a
-game they turned off and cannot see a single row of. With nineteen lanes and two switched on, naming
-eleven they do not play buries the one they do, and a warning about games nobody reads is a warning
-nobody reads (F8: a game we add is a game they never asked for, and arrives switched off, so an
-untouched install would be warned about the whole catalogue).
+**F15 — Appearance.**
+Relaxed (Round 7) and Structured (Round 6 B rail) share one component/state tree. Structured is the default (owner decision, 2026-10-17); an explicitly saved Relaxed preference is preserved.
+Interface choice, Standard/Compact density, Dark/Light/System theme and Slate/Warm/Plum/Custom
+scheme are independent. Custom stores ground/surface/text/accent separately for light and dark;
+rendered colours derive readable contrast without overwriting the saved choices. Per-game colours
+have a picker, hex field, preview and reset. Checklist dailies can occupy a separate direct-tick panel
+or grouped routines, with collapsed/expanded/always-expanded policies; these controls also live in
+Routines. Numeric routines use native step-one inputs.
-Both halves of that are load-bearing. Scoping it silently would be the worse half: "nothing has
-refreshed" is a claim about the whole calendar, so a reader with fourteen of eighteen lanes off would
-read a sentence about four as one about all of them. The notice therefore states whose games it
-counted, in the same words the empty list uses — *the games you have switched on*. Credit in the
-column beside it is owed to every source we read and is not scoped; nor is the "last refreshed" line,
-which reports the feed and feeds the bug form.
-
-**The staleness notice differentiates crawler checks from site updates.** If our crawler successfully
-checked the wiki recently (e.g. confirming HTTP 304) but the site's content has not changed in over
-two days, the notice explicitly distinguishes the two: "data pulled X ago, site updated Y ago", with
-an expandable breakdown detailing each source's check and content update times.
-
-**F15 — Light mode, with dark still the default.**
+The earlier theme rationale below still applies to both interfaces.
The app is a lit instrument panel and that is what it should be on first sight, but it is also read
on a train in daylight and by people who find a dark UI harder rather than moodier. A public fork
worked a theme toggle out first and is credited for the idea in the colophon. So the ground is
-the reader's choice: **Dark**, **Light**, or **System** to follow the device, in settings next to the
+the reader's choice: **Dark**, **Light**, or **System** to follow the device, in Settings → Appearance, separately from the
region — both are "how do I read this?" and neither changes what the page knows.
Dark is what it ships as, and deliberately not `System`. A reader whose laptop is in light mode has
@@ -571,7 +541,15 @@ placed. It also arrives switched off (F8), so settings is where they meet it, wh
would move it anyway. **A game's dailies stay together** — a repeating event the reader marked sits
with the standing chore of the game it came from, because the reader thinks in games and not in kinds.
-**F17 — Weekly and monthly tasks.**
+**F17 — Routines.**
+Routines is the library and management page for daily, weekly, monthly and cycle tasks. Follow each
+per server; pin independently to Checklist and Timeline. Built-ins offer Make it mine, copying only
+that account's logs; custom routines remain editable. Catch up & recorded history exposes logs on
+their original reset revisions. A separate Timeline reset strip sits directly after its game's events;
+with strips off, Ending soonest remains a single cross-game deadline queue.
+
+The cadence and goal semantics below remain the domain contract; the redesign moves their controls
+from the former Your own section to Routines.
Some chores roll weekly or monthly rather than daily — earning ZZZ's weekly activity points,
finishing Endfield's weekly routine — and no source publishes them, so they were nowhere in the app.
A reader can now set them up per game in *Your own*: a title, every week (from a weekday they pick),
@@ -624,7 +602,7 @@ instant on every server rolls on that server's reset for every reader.
The app now says what changed, in two tiers, both tied to git. **Short entries** — one line each,
tagged New, Fixed, or Games & sources — appear once as *What's new*, above the lists, after a new
version loads; *Got it* dismisses them. **Release notes** — a few paragraphs per feature or
-milestone, summarised from the history and the design specs — are in the footer's *Changelog*, each
+milestone, summarised from the history and the design specs — are in Settings → *Changelog*, each
linked to the commits it covers and the specs it draws on. Data refreshes are not changes in this
sense; the footer already states the data's age.
diff --git a/docs/PRODUCT.md b/docs/PRODUCT.md
index f5e8569..f3dbef9 100644
--- a/docs/PRODUCT.md
+++ b/docs/PRODUCT.md
@@ -71,7 +71,7 @@ away for more coverage (see Product Principles).
## Capabilities and Constraints
-**Capabilities.** The full feature set is specified as F1–F16 in `docs/PRD.md`:
+**Capabilities.** The full feature set is specified as F1–F18 in `docs/PRD.md`:
- A timeline: one lane per game, or a single queue sorted by soonest end.
- A checklist of everything running, sorted by soonest end.
@@ -79,16 +79,17 @@ away for more coverage (see Product Principles).
- Daily checklists, with past days that can still be filled in.
- Ignoring an event, and filters by game or type.
- Focusing on one game at a time.
-- Region selection.
-- JSON export and import.
+- Separate game/server tracking, reset corrections and Automatic or named display timezone.
+- JSON export/import, recoverable restore and 7 daily / 4 weekly / 6 monthly local backups.
- The reader's own games and events, one-off or repeating.
-- First-run setup.
+- Events-first new setup and a one-time returning-reader introduction.
- Offline use.
- A notice when a new version of the app is ready.
- Dark or light mode, plus a "follow the system" option.
-- The reader's own game order.
+- The reader's own game order, per-game colours, Relaxed/Structured interfaces and custom schemes.
+- A Routines library with independent view pins, an Archive with story statistics, and sourced redeem codes.
-The app currently covers twenty games from twenty-two sources.
+The app covers twenty games; source registries are the authoritative current count.
**Hard constraints** (`AGENTS.md` § Three constraints):
@@ -112,7 +113,7 @@ The app currently covers twenty games from twenty-two sources.
**Terminology:**
- The user is a **reader**.
-- The two views are **Checklist** (stored id `"soon"`) and **Timeline**.
+- The main views are **Checklist** (stored id `"soon"`) and **Timeline**; Routines, Archive and Settings are utility pages.
- An event's states are **untouched**, **doing it** and **done**.
- **Ignore** is not the same as done.
- A **lane** is one game's row or source.
diff --git a/docs/SOURCES.md b/docs/SOURCES.md
index 8d5e3a0..808c5ed 100644
--- a/docs/SOURCES.md
+++ b/docs/SOURCES.md
@@ -976,3 +976,22 @@ Facts stated above that a future reader may want to re-check, with how they were
| `marisaimpact.com` states no year on any calendar | stripped tags from `/calendar89` and `/calendar90` and grepped the text for `20\d\d` — zero matches on either |
| Its calendar URL is per-version, with no stable alias | `/calendar90` is `200` and covers `Aug 20 - Oct 22`; `/valk/calendar90` — the scheme its own `og:url` uses — is `404`; the nav links only `/calendar88` and `/calendar89` |
| Its schedule is not behind an API | `GET /calendar90/__data.json` returns 144 bytes: an `ogImage` URL and nothing else |
+
+
+## Redeem codes source (2026-10-16)
+
+Genshin codes use https://game8.co/games/Genshin-Impact/archives/304759, separately from events.
+One descriptive-agent robots request and one page request returned HTTP 200 during capture; no
+access workaround or repeated source fetch was used. The raw page and capture metadata are pinned
+as fixtures/genshin/game8-codes-2026-10-16.*, with ten independently counted current codes. The
+parser reads actual code inputs/current reward rows and excludes expired programme sections; a
+future-dated programme fixture variant verifies that still-current programmes are admitted.
+Unannounced expiry remains null. A printed calendar date is expiresOn, never a fabricated exact
+expiresAt instant. Attribution is retained on every code.
+
+src/ingest/codes/registry.ts registers a count-only refresh source. The existing runner shares robots,
+six-hour floors, conditional requests, per-host spacing and snapshot gates across event/code sources.
+Event adapter schemas are unchanged. scripts/build-codes.ts builds data/codes.v1.json offline from
+snapshots or fixtures and preserves observation timestamps in snapshots/codes-first-seen.json.
+Code newness is per game; redemption is per local game/server account and never clears newness by
+itself. No code row is synthesized into the event feed.
diff --git a/docs/design-reviews/2026-10-16-redesign-implementation.md b/docs/design-reviews/2026-10-16-redesign-implementation.md
new file mode 100644
index 0000000..5455d3d
--- /dev/null
+++ b/docs/design-reviews/2026-10-16-redesign-implementation.md
@@ -0,0 +1,125 @@
+# Approved redesign — implementation review, 2026-10-16
+
+**Status: rejected by the owner for substantial divergence from the approved Round 7 markup.**
+The completion claims below describe the earlier implementation report and are withdrawn as
+evidence of design acceptance. Functional tests and overflow checks did not establish visual or
+interaction fidelity. A faithful markup-to-component correction is still required.
+
+The approved [spec](../superpowers/specs/2026-10-16-ui-redesign-design.md) and
+[16-task plan](../superpowers/plans/2026-10-16-ui-redesign.md) are implemented in the working tree.
+Execution was Native. Nothing has been committed, pushed or published.
+
+## Delivered
+
+Relaxed (Round 7, default) and Structured (Round 6 B rail) share one reader/component tree; Appearance
+switches between them independently of theme, palette and Standard/Compact density. The redesign
+includes Routines and view pins, direct daily ticks, configurable grouping, multi-server progress,
+reset override/revert with accessible clock history, game colours/source credits, custom palettes,
+Automatic/IANA timezone, new and returning onboarding, compact source/new-event/code notices,
+Archive/story statistics, timed Done/Ignore/Undo, local rolling backups and recoverable restore.
+Settings About preserves the colophon; Changelog has its own page and no footer link.
+
+The codes feed contains ten real Genshin codes from the pinned Game8 capture. Source fetching remains
+deterministic and shares the existing conduct gates; no additional community requests were needed
+while implementing or reviewing. Unstated expiry stays unknown. Codes newness and per-server
+redemption are separate. Manual refresh checks both feeds and discloses partial code-feed failure.
+
+## Verification
+
+- **1,587 tests pass**, zero failures, across 74 files. Strict TypeScript and whitespace checks pass.
+- The suite also runs without `public/` or `docs/` in the Docker COPY layout: 1,586 pass, one
+ intentional README-only skip, zero failures. No tests depend on a preceding build.
+- Full offline build passes: 209 events across 20 games, ten codes, production JS/CSS/static shell.
+ Final shell build `f5b25675010f` was loaded on a fresh origin (port 41880).
+- Chrome checks cover both interfaces, both themes, 1440/390 widths, Standard/Compact, reduced motion
+ and an effective 200% CSS viewport. No horizontal page overflow or page errors in that matrix.
+- Browser journeys pass for migration, saved preferred Timeline, events-only setup, selectable empty
+ tracking kinds, upcoming placement, isolation from unselected games, extra server with fresh state,
+ direct ticks, reset override/revert/history, per-server redemption, Done/Ignore/Undo, unknown-end
+ custom creation, real export download/upload/restore, backup preview/restore/Undo, custom palette,
+ timezone selection/search, offline reload and the real service-worker update/reload handshake.
+- Two real browser tabs save concurrently with Web Locks explicitly disabled: IndexedDB serialization
+ preserves both edits. Unit regressions cover stale progress fields and independent routine pins.
+- A fresh independent reviewer completed whole-change review and focused re-reviews. All important
+ findings were fixed with reproductions; final review reports no remaining important findings.
+
+## Review fixes and durable decisions
+
+Deleted custom definitions no longer reappear from seen snapshots. Progress imports preserve local
+active reset configuration, retain imported clocks as history, and reuse those revisions on repeated
+imports. Opaque extensions survive subsequent transfers without duplicate accumulation. Timeline
+focus applies to routines as well as events; Ending soonest uses one global queue when reset strips
+are off. The browser fallback uses an IndexedDB transaction rather than unlocked whole-envelope
+writes. Account rebase preserves independent routine/pin edits and explicit retirement removal.
+
+Original v1 bytes remain available after migration. Restore validates account/clock references and
+saves recovery before replacement. Setup completes before seen-history capture. Source diagnostics
+state what the feed records; absent failure reasons are described as unavailable, never guessed.
+
+## Practical limits
+
+Backups are browser-local and created while the app is used; they cannot recover storage a reader
+clears. Older completion IDs whose metadata was never retained remain recoverable raw data, but
+cannot produce invented archive descriptions or historical statistics. Codes currently have one
+registered game/source. External fonts have system fallbacks offline. Calendar-date code expiry is
+shown as a date without an invented instant. No accounts, remote sync or push notifications added.
+
+## Captures inspected
+
+[Relaxed, light, desktop](assets/2026-10-16-redesign/relaxed-light-1440.png) ·
+[Structured, dark, desktop](assets/2026-10-16-redesign/structured-dark-1440.png) ·
+[Structured, dark, phone](assets/2026-10-16-redesign/structured-dark-390.png)
+
+Additional transient browser logs and captures are in `/tmp/redesign-browser-*` and
+`/tmp/redesign-final-*`; the facts above and representative images are recorded here for handoff.
+
+
+## Follow-up: first-visit HTTP/WireGuard startup failure
+
+The owner reported the recovery screen on first visit through WireGuard `10.20.20.2`. The precise
+failure reproduced in Chrome at an ordinary HTTP hostname: `crypto.randomUUID` was undefined,
+initial account minting threw, and initialization returned the generic unavailable/saved-data error.
+Localhost-only verification had missed this because localhost is a browser secure context.
+
+All reader ID consumers now use 128-bit `crypto.getRandomValues` through `state/reader/ids.ts`.
+Three new regressions failed before the fix and pass afterwards. Browser verification on an actual
+insecure HTTP origin passes both existing-save migration and fresh setup, ordinary edits, adding a
+server, reset override/revert, backups and reload; the original v1 bytes remain unchanged. Focused
+independent review found no important issues. The full suite now passes **1,590 tests** across 75
+files. Typecheck, production rebuild and whitespace checks pass. Rebuilt shell: `9752bd6bf1d2`.
+
+The existing preview serves the rebuilt files; hard-refreshing bypasses the browser's cached JS.
+No storage deletion or identity remapping was performed.
+
+
+## Owner rejection: markup fidelity
+
+Direct comparison confirms substantial divergence despite matching outer gutters/panels/fonts:
+
+- Round 7's event-entry structure separates event content, a large deadline with supporting text,
+ and chevron. Production reused EventRow's prior internal structure and decorated it with CSS.
+- Round 7's Routines uses game bands and aligned columns for progress, cycle/reset, pins and menus.
+ Production renders a flattened library with checkbox options and no equivalent table structure.
+- Round 7's settings sidebar includes individual game entries with descriptions, leading into their
+ profiles. Production substitutes a generic Your games page and game selector.
+- Round 7's Timeline places routine periods/progress/reset markers on the shared time axis.
+ Production appends ordinary routine controls below event timelines and uses textual reset strips.
+- Round 7's Archive story includes interactive per-game outcome breakdowns. Production substitutes
+ per-game text totals.
+- The approved header has navigation counts and a dedicated Settings placement. Production uses
+ the simpler generic navigation and a different header summary.
+
+References: design/mockups/round7/converged.html (final CSS from line 1003 onward and final renderer
+overrides); production ReaderApp.tsx, EventRow.tsx, TimelinePage.tsx, RoutinesPage.tsx,
+SettingsLayout.tsx, GameSettings.tsx, ArchiveStory.tsx. Earlier render functions/CSS in the mockup
+are superseded by later overrides and must not be used in isolation.
+
+Root delivery error: the implementation preserved/reused existing presentation components and
+added selected reference styling instead of faithfully translating the approved rendered markup.
+Verification checked functionality and overflow without comparing equivalent reference/production
+screens and interactions. The final review concentrated on data preservation and did not close that
+design-fidelity gap. The owner should not have had to identify it after a completion claim.
+
+No implementation edits or data resets were made during this rejection audit. Existing storage and
+HTTP fixes remain in place. Required correction: use the approved rendered markup, hierarchy and
+interactions as the presentation contract, then connect real reader data and compare matching states.
diff --git a/docs/design-reviews/2026-10-16-redesign-round4.md b/docs/design-reviews/2026-10-16-redesign-round4.md
new file mode 100644
index 0000000..17db7fc
--- /dev/null
+++ b/docs/design-reviews/2026-10-16-redesign-round4.md
@@ -0,0 +1,167 @@
+# Routine redesign: Round 4 review
+
+This records the owner's decisions from three mockup rounds and the subsequent review on
+2026-10-16. These are proposed product changes, **not shipped behaviour**. The implementation spec
+and migration plan come after review of this combined prototype.
+
+## Review artifact
+
+Open [the standalone Round 4 prototype](../../design/mockups/round4/converged.html) in a browser.
+The review copy is also placed in `~/gacha-mockups/round4/converged.html`, linked from the existing
+Samba index. Earlier rounds remain intact there.
+
+The prototype extends the Round 3 archive variant, keeping its ruled timetable visual language and
+routine library. It is deliberately separate from the production app. Its storage namespace is
+`event-clock:mockup:round4:v1`, never a production `gacha-tracker` key. Reset demo restores sample
+state; reloading otherwise keeps density, choices and progress where browser storage is available.
+
+## Decisions to preserve
+
+- Checklist and Timeline retain their names. Routines and Archive have their own tabs, as does
+ Settings. Settings uses a sidebar with General first.
+- Routines owns follows, individual editing and independent Checklist/Timeline pins. Built-ins do
+ not appear under Your own events and games. The library puts Create your own routine first.
+- Checklist keeps compact per-game routine summaries in its rail. Focusing a game expands its
+ routines; All collapses them. Remove the unrelated Your own events callout from the rail.
+- The main event list has two densities: Standard by default, with wrapping titles, breathing room
+ and supporting copy, and Compact using the dense table. Density is remembered and independent of
+ routine pins, filters, sort order and Timeline. Both densities open the same event detail.
+- Game setup offers composable Events / Dailies / Weeklies & monthlies / Cycles shortcuts; individual
+ follows remain editable in Routines. Events is a visibility choice, rather than a routine.
+- Each game can have multiple servers, with separate routine and daily progress. An additional
+ server starts with events only and fresh routine choices; it does not copy the first server's
+ follows, pins or ticks.
+- Onboarding begins with events only. Explain each type in plain language, especially cycles:
+ routines following the game's own cadence, such as a mode resetting every fourteen days. Ask
+ whether Checklist or Timeline should open first. Defer pin placement until readers have seen the
+ views.
+- Returning readers receive an introduction to the new layout in either main view, with existing
+ choices and progress preserved. Changelog remains accessible afterward.
+- Codes and About live beside Setup in the game's settings area. In-app notices appear in both
+ main views. New means discovered since the reader last viewed that game's codes, not unredeemed.
+ Viewing clears the new indicator without marking redemption. Redemption is separate per server.
+- Archive defaults to per-game groups, with the ledger's story panel and small statistics above
+ them. History timeline is an additional view. Records remain in the save, with description,
+ source and progress available after the live source changes.
+- Done and Ignore immediately save an archive record, then retain the active row for about 1.5
+ seconds with Completed/Ignored · Moved to Archive before fading and closing the gap. Undo remains
+ available afterward; Restore also lives in Archive. Reduced motion keeps the confirmation delay
+ and removes the fade. Unknown end dates stay unknown in archived records.
+- Expired unfinished events belong in Archive as unfinished, rather than silently disappearing.
+- Timeline uses full rows for routines. It offers Ending soonest and By game. Separate reset strips
+ are optional and sit directly below their own game's events on the same axis. In separate-strip
+ mode, Ending soonest orders game groups by their next deadline to preserve this adjacency.
+
+## What to review
+
+1. Compare the same 21 events in Standard and Compact; focus Genshin and inspect its routine rail.
+2. Complete or ignore an event, observe confirmation/fade, Undo, then open its saved Archive detail
+ and Restore it. Try an event with no announced end.
+3. Follow/unfollow a routine from the library and pin it independently to each view.
+4. Add a server in game settings and confirm the other server's choices and ticks stay separate.
+5. Open Codes, mark one redeemed on a single server, and simulate a new arrival from the demo bar.
+6. Compare the per-game Archive with History timeline, and check both timeline reset layouts.
+7. Walk through New user, then Returning user with Timeline as the default.
+
+## Owner's feedback after Round 4
+
+The combined prototype was received positively. Further requirements:
+
+- Restore the richer statistics from Round 3 H's Ledger with a story. Round 4 reduced that panel
+ to completed/ignored/unfinished counts, which lost what the owner liked. Keep the per-game
+ archive as the default body, but restore the completion percentage for the current month with
+ an overall comparison, current/best completion run, logged event check-in days, and unfinished
+ events with near-miss detail. Retain the compact per-game outcome breakdown from that panel.
+ Ignored events must not be counted as failures in the completion percentage. Definitions and
+ scope labels must be explicit: recorded archive history is not all play history, event check-in
+ totals are not unique calendar days, and incomplete/unknown progress cannot support a fabricated
+ near-miss claim. These statistics should use saved progress and dates, not invented telemetry.
+
+- Keep the colophon, potentially in Settings, and add an app-level About section distinct from a
+ game's About tab. Preserve source/studio credits, author and contributor acknowledgements and
+ useful project links. Moving credits must not hide the feed-age and stale-source disclosures
+ that readers need while looking at deadlines.
+- The grouped routine summaries are easier to scan but add friction to ticking daily chores.
+ Explore a separate daily panel with directly available one-click ticks, selectable as an
+ alternative to grouping dailies with the other routines. Keep server progress separate and
+ avoid showing the same tick in both panels. Placement and default are not yet settled.
+- Appearance should support colour schemes in addition to dark/light/system. The choice between
+ curated palettes and reader-edited colours remains open. Any scheme must preserve game identity,
+ urgency meanings, and readable contrast in both modes.
+- Each game's Settings → Setup includes a custom game-colour control, a preview and Reset to
+ default. Remember the override and apply it consistently wherever that game appears, including
+ Checklist, Timeline, Routines and Archive. It belongs to the game, shared across its servers,
+ separately from the global interface colour scheme. Use the existing readable-hue approach to
+ preserve legibility in dark and light appearances without changing the reader's saved base
+ colour. Game colour expresses identity; urgency colours retain their meaning. Export/import
+ must retain these overrides in the eventual implementation.
+- Clarify the additional-server action as simultaneous account tracking. Use “Track another
+ server” with helper text: “Play on more than one server? Add your other server to track both
+ accounts separately.” Keep changing the existing server as a distinct control. The added-server
+ setup still starts with fresh routine choices and separate progress, as previously agreed.
+- Standard event rows without descriptions currently feel too empty. Size rows to their actual
+ content: reserve no description line when absent, reduce vertical padding for title-only rows,
+ and keep the countdown/meter stack from imposing a description-sized minimum height. Preserve
+ wrapping titles and comfortable tap targets; Standard remains roomier than Compact without
+ forcing every event to the same tall height. Do not invent fallback description text to fill
+ the space.
+- Region & time must let the reader select their display timezone. Automatic, using the browser's
+ timezone, is the default; a manual selection is remembered and can be returned to Automatic.
+ Display named IANA zones with their current UTC offsets, so daylight-saving changes follow the
+ selected zone rather than a fixed offset. This affects displayed dates/times, not the server
+ clocks that determine deadlines, reset boundaries and stored game-day keys. Reader-authored
+ event entry must explicitly identify the timezone used to interpret its input.
+- In onboarding's What do you want to track step, all type choices stay selectable even when a
+ game has no current matching routines or windows. Explain the empty state beside the choice;
+ distinguish no current active window from no supported built-in routine. An empty result is not
+ a disabled control. Retain the reader's selected tracking intent rather than deriving it solely
+ from which routines happen to exist today. The implementation spec must reconcile this intent
+ with the earlier shortcut-only composition model and define how newly supported routines are
+ offered without silently expanding what existing readers track.
+- Initial setup also asks for default game server, preferred appearance and whether to show
+ events that have not started. The selected server initializes game setup and individual games
+ can override it. Appearance includes Dark / Light / System and the agreed colour-scheme choices
+ once those are settled. Upcoming events remain off initially, consistent with the existing
+ default, until the reader selects otherwise. The previously agreed Checklist/Timeline default
+ question remains. Present these as a short preferences step rather than crowding the tracking
+ matrix with unrelated controls.
+
+## Prototype boundaries and implementation work still ahead
+
+- “Now” and reset clocks are fixed for review. The final app must use the existing per-game reset
+ rules and actual region-specific end times.
+- Most event content was carried over from the earlier prototypes. Codes, historical archive
+ records, version groups and some descriptions are illustrative. No new source fetch runs here.
+ Live-list source links are matched by title against the existing local feed; unmatched sample
+ events receive no invented source URL.
+ Game8's supplied Genshin code page is a source-shape reference, not an implemented adapter.
+- Only in-app code notices are proposed. Push, email and background notifications remain outside
+ the product scope.
+- Some inherited controls remain placeholders: backup and portions of own-event/routine editing.
+- The archive snapshot works within this prototype's save. Production needs an explicit snapshot
+ retention/export format and a policy for capturing events before they drop from the feed.
+- Production needs a reviewed migration for existing progress into per-server storage; no existing
+ event ID, daily ID or day key may be changed casually. The archive and code state also need a
+ documented export/import contract. This artifact is not a storage implementation to copy.
+- `docs/PRD.md`, `docs/DATA-MODEL.md`, `docs/ARCHITECTURE.md`, `docs/DESIGN.md` and the matching
+ AGENTS guidance must be updated with the eventual implementation. They describe the shipped app
+ today, so this review record does not rewrite them as though the redesign had landed.
+
+## Verification
+
+Browser verification and screenshots are performed with scratch-installed puppeteer-core and
+Chrome, at 1440px and 390px. The prototype needs no Bun build or production feed changes. The test
+journeys cover density parity, navigation, completion/ignore delay and Undo, archived detail and
+Restore, separate code visibility/redemption, server setup, reset-strip adjacency, onboarding,
+returning users, persistence, reduced motion and unknown end dates.
+
+Both width passes completed with no JavaScript errors and no page-level horizontal overflow in
+the checked screens. The initial mobile pass caught a confirmation notice intercepting Archive
+row taps; informational notice surfaces now let pointer events through while Undo remains a real
+button. An early verification-script failure also tried to open a Star Rail event after onboarding
+had selected only Genshin; the scenario now resets sample state before testing an unknown end.
+
+Impeccable's mechanical detector reports palette, typography and size differences against the
+**shipped** DESIGN.md, plus inherited stripe treatments. The new Barlow timetable appearance is
+intentional and selected in earlier rounds; this prototype does not rewrite the shipped design
+reference or add detector suppressions. Its visual language remains subject to this review.
diff --git a/docs/design-reviews/2026-10-16-redesign-round5.md b/docs/design-reviews/2026-10-16-redesign-round5.md
new file mode 100644
index 0000000..43bc139
--- /dev/null
+++ b/docs/design-reviews/2026-10-16-redesign-round5.md
@@ -0,0 +1,112 @@
+# Routine redesign: Round 5 review
+
+This is a review prototype, not a production release or an approved implementation spec. It
+combines the owner's feedback after Round 4, recorded in
+[the preceding review](2026-10-16-redesign-round4.md), and the subsequent freshness, backup and
+focus-bar requests.
+
+Open [Round 5](../../design/mockups/round5/converged.html), or its copy at
+`~/gacha-mockups/round5/converged.html` through the shared index. Earlier rounds remain unchanged.
+
+## What changed
+
+- **One-click dailies:** an optional panel exposes daily ticks directly, separately per server.
+ The alternative groups dailies with the routine summaries. A daily appears in only one place;
+ pins still decide which dailies appear. The separate panel is the prototype's starting choice,
+ to test the owner's request for immediate access; this default remains reviewable.
+- **Focus alternatives:** a labelled game toolbar is the starting choice. The review bar's Focus
+ selector also offers a compact game switcher and a picker at the top of the Checklist rail.
+ Each changes the same focus state and counts, and preserves auto-expanding focused routines.
+ Timeline retains a visible toolbar/switcher rather than acquiring a Checklist rail.
+- **Content-sized Standard rows:** events lacking descriptions use less vertical padding and
+ omit the segmented meter. They retain a wrapping title, countdown and tap target. No filler
+ descriptions are added.
+- **About and colophon:** Settings → About Event Clock retains the author, source communities,
+ studio credits, acknowledgements, reset-clock reference, disclaimer and project links. The
+ footer links there. Source credits come from the existing local feed, not the selected games.
+- **Freshness:** Checklist and Timeline visibly state data age and warn about older schedules for
+ the games switched on. Source breakdown distinguishes last successful check from last content
+ change and explains unchanged pages, overdue pulls and never-fetched data. Unknown causes are
+ described as unreported, not invented. Settings → Data & sources exposes the same information.
+ The review bar offers Current / Some stale / All stale / Never fetched / Offline. These are
+ labelled illustrative states; no real fetch runs. Check for new data demonstrates a no-newer-feed
+ result and is disabled offline. The headline data timestamp covers the demo feed as a whole;
+ warnings and named source rows cover selected games. No build timestamp is used.
+- **Appearance:** Dark / Light / System plus three curated scheme previews, Slate / Warm / Plum.
+ This answers the palette request for review, not the unresolved possibility of a fully editable
+ interface palette. Semantic deadline colours remain separate from game identity.
+- **Game colour:** Setup adds a colour picker, hex input, preview and Reset to default. An override
+ applies to all the game's servers and all its views. The stored base colour remains unchanged
+ while its rendered colour is adjusted for contrast in the chosen appearance.
+- **Timezone:** Automatic is the default; named zones can be selected and searched in Region &
+ time. Dates use the selected zone, including daylight saving, without changing server reset
+ boundaries. The prototype's per-game reset clocks remain simplified.
+- **Additional servers:** “Track another server” explains simultaneous separate account tracking.
+ Added servers still start with fresh routine choices and progress.
+- **Onboarding:** initial preferences now ask default server, mode/scheme, timezone and whether
+ upcoming events should appear. Game selection, tracking types and Checklist/Timeline default
+ follow. All tracking types remain selectable even if no matching built-in exists. Availability
+ is explained, and selected intent is saved separately from actual follows. The no-matching-types
+ message does not incorrectly call that selection events only. New supported built-ins are
+ described as offers for review rather than automatic extra work for existing readers.
+- **Archive story:** restores monthly/overall completion percentages, current/best completion
+ run, logged event check-ins and unfinished/near-miss counts, plus per-game outcome bars, above
+ the default per-game archive. Ignored events are excluded from completion-rate denominators.
+ Progress and statistics derive from illustrative saved archive records, and scope is labelled.
+ History timeline remains an alternate view.
+- **Automatic backup history:** Backup shows seven daily, four weekly and six monthly snapshots,
+ with latest-per-period compression. The retained historical examples are labelled sample; today's
+ snapshot updates from the demo's own state. Restore previews game/routine/archive/progress counts
+ and preserves current data as a recovery copy first. Undo last restore is available. Manual
+ Back up now and demo JSON export/import live alongside the automatic history.
+
+## Backup semantics used for this review
+
+The most recent seven days retain one latest snapshot per day. Older snapshots fold into four
+consecutive seven-day windows. Older records then fold into six calendar-month buckets, keeping
+the latest snapshot in each. This is up to seventeen retained snapshots, plus a recovery copy from
+the last restore. No snapshot is invented for a day the reader never opened the app.
+
+The demo clock is fixed and its bucket calculations use UTC. Production needs to settle and
+document the stable calendar basis independently of display-timezone changes, handle storage
+capacity/failures, validate restore files, and use the real export contract. Backups are browser
+local and are created while the app is used; they cannot be promised for days when it stays closed.
+Export remains the way to keep a copy outside that browser or move to another device.
+
+## How to inspect
+
+1. Compare focus options in the review bar. Focus Genshin; tick EU and Asia dailies separately.
+2. Compare Standard/Compact, especially rows without descriptions, and switch the daily layout.
+3. Use the Data selector to inspect every freshness state, then expand Why the data is older.
+4. Open About, Data & sources, Region & time and Appearance. Change timezone, scheme and game colour.
+5. Open Archive to inspect richer stats and History timeline.
+6. Open Backups, review a monthly sample, restore it, then Undo last restore. Demo export files are
+ separate from the production app's format and storage.
+7. Open New user. Choose preferences and Genshin, then select Cycles despite no matching built-in.
+8. Set Timeline as the default and use Returning user to check the introduction appears there.
+
+## Verification and boundaries
+
+Chrome journeys at 1440px and 390px exercised direct daily ticks, layout choices, focus alternatives,
+freshness variants, credits, timezone persistence, light/dark/system and schemes, game-colour reset,
+fresh server setup, Archive stats, backup restore/recovery, onboarding with an empty type,
+returning-reader introduction and completion/Undo. Both passes had no JavaScript errors and no
+page-level horizontal overflow in the checked screens. A follow-up checked latest-per-period
+retention across 400 simulated days, all-games empty-type toggles, and desktop sticky toolbar/rail
+separation.
+
+The mechanical design detector reports 30 findings, chiefly differences from the shipped palette,
+Barlow typography and size/radius ramp, plus inherited archive stripe treatments. These are review
+artifacts in the owner-selected replacement visual direction; no production design reference or
+detector suppression was changed to make the prototype appear to match the shipped app.
+
+An inherited Round 4 Reset handler initially discarded the new state fields. Round 5 now owns
+reset initialization and its browser check passes. The active prototype data namespace is
+`event-clock:mockup:round5:v1`; production localStorage keys and progress are untouched.
+
+The existing demo library/create form, independent pins, completion confirmation/fade/Undo,
+Archive restore and code-newness/redemption distinctions remain. Some inherited own-event editors
+are still placeholders. Codes, old Archive history, backup history and source health are sample
+content. Real parsers, source refreshes, app storage migrations and production backup machinery are
+not implemented by this artifact. The shipped specification and design documents remain about the
+shipped app until the redesign's spec and implementation are reviewed.
diff --git a/docs/design-reviews/2026-10-16-redesign-round6.md b/docs/design-reviews/2026-10-16-redesign-round6.md
new file mode 100644
index 0000000..08b41b8
--- /dev/null
+++ b/docs/design-reviews/2026-10-16-redesign-round6.md
@@ -0,0 +1,105 @@
+# Routine redesign: three quieter layouts
+
+Review artifacts, not a production release. This round responds to the owner's concern that
+Round 5's focus toolbar and stale-data notice occupied too much space, and incorporates the
+additional feedback received while building the alternatives.
+
+Open [the comparison index](../../design/mockups/round6/index.html). The shared copy lives at
+`~/gacha-mockups/round6/index.html`; earlier rounds stay available. Each design is a complete
+standalone HTML prototype with the same sample event data and the shared features below.
+
+## The alternatives
+
+- **A — Inline desk:** retains the left routine/daily rail. Game focus is a small selector beside
+ the event heading, and freshness is a single understated line. Source reasons are one click away.
+- **B — Game desk:** puts game focus, daily ticks and a short source-status report in the rail.
+ There is no full-width freshness or focus band above the event list. It reserves more attention
+ for the list; its tradeoff is a more populated rail that stacks before events on mobile.
+- **C — Open ledger:** replaces the rail with a full-width event list, a brief next-deadline row,
+ direct daily ticks in a horizontal strip, and a disclosure for other pinned routines. Game focus
+ and a short freshness report sit with the event heading. It gives titles the most width; the
+ routine controls scroll with the list.
+
+The chosen timetable appearance is preserved. These compare structure and interaction rather than
+asking the owner to pick a new visual identity again.
+
+## Shared decisions implemented for review
+
+- **Changelog is its own Settings page.** The owner explicitly chose this over a main app tab,
+ and explicitly declined a footer link. Remove the awkward Changelog button from About, remove
+ it from the footer, and render release notes in the page rather than the old overlay. Existing
+ returning-reader news links lead to that Settings page.
+- **Freshness remains honest and available.** Keep the full data timestamp and age in the footer,
+ scope warnings to selected games, and expose successful-check/content-change times and reasons
+ in Data & sources. Older source data is also explained in the event's detail. Unknown causes
+ remain unreported, not invented. The review bar's Data selector tests the illustrative scenarios.
+- **Dailies on Checklist belongs in Routines as well.** Routines → Checklist display controls the
+ same preference as the daily panel shortcut and Appearance. Choose Separate panel or Grouped
+ with routines. Independent pins still govern whether a routine appears.
+- **Checklist routine groups have three behaviours:** Collapsed by default, Expanded by default
+ (still collapsible), and Always expanded (no collapse buttons). Focusing a game expands its
+ routines. The wide layout also honours this for its enclosing routine section.
+- **Numeric fields show the native stepper.** Browser increase/decrease controls remain visible
+ and use step one, as requested. They are not replaced by a separate custom spinner.
+- **Appearance includes Custom.** Edit background, surface, text and interface accent separately
+ for light and dark. Stored choices are retained while preview text/surfaces are adjusted for
+ contrast. Game identities and deadline urgency remain separate from the custom interface accent.
+ Individual game colours retain their own control and reset.
+- **Reset timing can be overridden per game/server.** Game Setup and Region & time expose Automatic
+ versus a custom server-local hour/minute, with Revert to Automatic. The prototype applies the
+ override consistently to daily/routine periods and reset summaries. Reader-authored routines
+ with their own explicit reset retain it. The automatic source clocks remain simplified here;
+ this is not a production reset migration.
+- **A game's About names its sources.** Links come from that game's entries in the existing local
+ feed, including separate event/banner/pass sources. Use readable schedule labels and thank the
+ editors. This differs from the global About's full, unfiltered colophon.
+- **Remove Done and ignored events from Lists.** That row only linked to Archive and was not a
+ setting. Archive keeps its own main navigation tab.
+- **Backup rows say View backup.** They open a preview headed Backup from [date], showing the
+ snapshot's contents. Restore this backup is the explicit action there; the current state is
+ preserved as a recovery copy. The seven-day/four-week/six-month history is retained.
+- **Returning readers get a guided introduction.** The review bar's Returning user control opens
+ a short welcome in whichever view is preferred, with Take the quick tour and Keep browsing.
+ Four steps visit Routines, Checklist, Archive and Settings. Back and Skip are available; finishing
+ returns to the preferred view. Games, follows and progress remain intact. Production will bind
+ this to a saved release marker, so it is offered once rather than every load.
+- **Upcoming events moves to What do you want to track?** It is no longer in the initial preference
+ step. The initial preferences retain default server, appearance and timezone; all type choices
+ remain selectable and explained even when no matching built-in exists.
+
+Standard/Compact density, richer Archive statistics and the history timeline, separate server
+progress, code-newness versus redemption, completion confirmation/fade/Undo, timezone and the
+existing routine library/create form remain available in every alternative.
+
+## Boundaries
+
+Each alternative uses its own `event-clock:mockup:round6::v1` browser namespace. No production
+keys, parsers or feed data are changed. Source-health states, codes, archived history and historical
+backups are illustrative. Clock overrides affect the demo's period calculations, not any real stored
+game-day keys. Production must review migration and the preservation of historical ticks before
+introducing reader overrides. These are disposable prototypes, not code to transplant into the app.
+
+## Verification record
+
+The browser pass checks all three designs at 1440px and 390px: focus and daily controls, native
+numeric stepping, all disclosure modes, source reasons in event detail, the Changelog page and its
+absence from About/footer, custom-palette contrast, reset override/revert, per-game source credits,
+Lists cleanup, backup labels, the moved upcoming option, and the returning-user tour preserving
+progress. Screenshots cover each main layout plus the new shared pages.
+
+The first mobile pass found a backup-preview smooth scroll continuing after navigation, allowing a
+subsequent automated tap to hit the fixed review bar. The preview now scrolls instantly, avoiding a
+pending animation against a different screen.
+
+The confirming pass completed all six combinations of design and viewport with no JavaScript
+errors or page-level horizontal overflow in the checked screens. Custom-palette checks included
+equal chosen background/text colours and confirmed the rendered text was adjusted to at least
+4.5:1. Per-server override checks exercised 05:30, verified the 90-minute change from 04:00, then
+returned to Automatic. The tour returned a Timeline-default reader to Timeline with saved games,
+follows and progress unchanged.
+
+Design-hook triage: retain the owner-selected Barlow typography. Narrow value exceptions for that
+font are scoped to `design/mockups/round6/index.html` only, with evidence in `.impeccable/config.json`.
+No whole-file or whole-rule exclusions are added. The shipped DESIGN.md remains the production
+reference; prototype differences in palette/size and inherited archive treatments remain review
+findings rather than being suppressed broadly.
diff --git a/docs/design-reviews/2026-10-16-redesign-round7.md b/docs/design-reviews/2026-10-16-redesign-round7.md
new file mode 100644
index 0000000..c04d9c2
--- /dev/null
+++ b/docs/design-reviews/2026-10-16-redesign-round7.md
@@ -0,0 +1,80 @@
+# Routine redesign: one airier design
+
+**Later owner decision:** retain two interfaces after all: Round 7 and Round 6 **B**, selected
+in Settings. The earlier single-interface direction below records the mockup review, not the
+final implementation decision. Replace “Older schedules” with “Event data may be out of date”.
+See the [production redesign specification](../superpowers/specs/2026-10-16-ui-redesign-design.md).
+
+Review prototype, not shipped behaviour. Open
+[the unified mockup](../../design/mockups/round7/converged.html), also copied to
+`~/gacha-mockups/round7/converged.html` through the shared index.
+
+## The owner's correction
+
+Round 6's three alternatives were rejected. The owner asked for:
+
+- Older-data notices in the same structure as New since you last looked, appearing only when
+ games have older data.
+- The current production game's focus-pill structure, placed at the top of the left rail as in B.
+- A more airy, easy-to-read design informed by the original
+ `~/gacha-mockups/c-one-due-list.html`.
+
+The initial idea of a Simple mode or a spacing toggle was then explicitly reconsidered: the owner
+does not want two distinct UIs. Carry the spirit of C into one unified interface instead.
+
+## What this mockup does
+
+**A shared updates area.** New code notices and Older schedules use two compact rows in one
+component above the Checklist's event list and above Timeline. The older row lists affected games
+and links to Details & reasons; no full-width health banner or rail status panel remains. It is
+absent when the selected games have current sources. If there are no new codes either, the whole
+updates area disappears. Source reasons remain available in Data & sources and event detail; the
+full feed timestamp stays in the footer on every load. Scope is the games the reader has switched
+on, rather than only the temporary focus. Repetitive older-data labels on individual list rows are
+removed because the updates row already names those games.
+
+**The existing focus behaviour.** `src/client/components/GameFocus.tsx` is the reference: All and
+game pills, game hues, outstanding counts, and Next progressing through games and then back to All.
+Tapping the focused pill returns to All. Pills wrap in their own panel at the top of the Checklist
+rail; they are absent for a single-game setup. Timeline uses the same pills above its lanes. Focus
+never changes which games are followed.
+
+**One visual world.** The original C informs separated panels, quieter rules, softened surfaces,
+more room around controls and a warmer reading rhythm. Bricolage Grotesque and Figtree replace the
+compressed timetable typography consistently across the prototype, including settings and forms.
+The rail and event list have a real gutter; focus, deadline, dailies and routines have separated
+surfaces. Light mode uses warm paper and dark mode a softer charcoal. No Simple/Detailed mode or
+global spacing toggle is added. Standard/Compact remain two row densities of this same appearance.
+Rows with no description remain shorter; comfortable layout is not a fixed tall event height.
+
+Direct daily ticks, routine disclosure choices, independent pins, custom palettes, per-game colours
+and reset overrides, source links and thanks, Changelog in Settings, Archive statistics and history,
+backup history/recovery, timezone and both onboarding flows remain available. The daily-panel layout
+shortcut is removed from the Checklist rail to reduce controls there; its Routines and Appearance
+settings continue to edit the same preference.
+
+## Validation
+
+Chrome journeys at 1440px and 390px passed with no JavaScript errors and no page-level horizontal
+overflow in the checked views. They cover conditional/scope-aware notices, empty updates, focus
+toggle/advance, spacing, routine expansion, reset override/revert, source credit links, custom
+scheme controls, light-theme game-colour contrast on ground and panel, Changelog location, backup
+history, Archive stats, Timeline, completion/fade/Undo and onboarding. Contrast adjustment now
+considers both panel and page grounds, while retaining saved base game colours.
+
+Screenshots were inspected for Checklist in both appearances, Routines, Data & sources, Archive
+and Timeline. The first verification script used a nonexistent review-bar Settings shortcut; it
+was corrected to use the actual Settings tab. No product change was needed for that script error.
+
+The mechanical design check flags differences from the **shipped** DESIGN.md in palette, type
+size, radius and inherited archive treatments. Those remain review findings, not broad suppressions.
+The intentional Bricolage font exception is scoped to this one prototype file, with rationale in
+`.impeccable/config.json`. The production visual reference is not rewritten before implementation.
+
+## Boundaries
+
+The namespace is `event-clock:mockup:round7:v1`. Earlier mockups and production progress remain
+unchanged. Historical records, codes and source health are illustrative; clocks are fixed and
+automatic reset defaults simplified. No source requests or production parser/storage changes run
+here. This is a disposable visual review artifact; the redesign still needs a reviewed production
+specification and migration plan.
diff --git a/docs/design-reviews/2026-10-17-redesign-fidelity.md b/docs/design-reviews/2026-10-17-redesign-fidelity.md
new file mode 100644
index 0000000..098145f
--- /dev/null
+++ b/docs/design-reviews/2026-10-17-redesign-fidelity.md
@@ -0,0 +1,101 @@
+# Redesign fidelity correction — 2026-10-17
+
+> **Owner rejected this implementation after browsing it:** “This is still not even close to the
+> mockup.” The completion claim is withdrawn. Screenshots and review verdicts below are historical
+> evidence of the rejected attempt, not acceptance. Continue from
+> [the second-rejection handoff](../handoffs/2026-10-17-redesign-second-rejection-handoff.md):
+> launch the current server, then ask the owner for the biggest differences before further UI edits.
+
+The owner asked to repair the rejected implementation against Round 7 Relaxed and Round 6 B
+Structured, then explicitly made **Structured the default**. Existing saved Relaxed choices remain
+valid. This correction is uncommitted in the existing checkout; no branch, publication or data-source
+refresh was performed.
+
+## What changed
+
+- Checklist uses dedicated reference-shaped event rows and next-deadline panel, with separate copy,
+ countdown, supporting date, segmented meter and detail chevron. Header counts and Settings placement
+ follow the reference. Both interfaces retain the owner-approved focus pills.
+- Routines has By reset/By game bands, aligned game/routine/progress/view-pin/menu columns, live totals,
+ direct daily status, amount bars/inputs, library and a native custom-routine drawer. Bulk pins combine
+ writes per account. Multi-server row identity includes both account and routine.
+- Timeline plots event spans and routine periods on one axis with frozen labels, progress fill and
+ reset markers. In-progress bar labels remain visible after scrolling. Deadline ordering includes
+ routine resets, and separate reset strips remain available.
+- Settings restores separate Default view and Your own events and games pages, game profiles, tracking
+ summaries and Other games. Profiles follow tracking → servers → colour → reset timing. A server
+ selector above tracking keeps choices independent without duplicating tall account panels.
+- Archive restores the story, interactive outcome bars, game sidebar, saved rows and history timeline.
+ Its detail dialog reads saved metadata, original clock region, notes, days and amounts. It does not
+ open the live progress editor or replace historical state with the current clock.
+- Structured defaults agree across prepaint, normalization, setup and applied appearance. No IDs,
+ localStorage key spaces, migrations or ingestion rules were changed by this correction.
+
+## Reference comparisons
+
+Images are in [the evidence directory](assets/2026-10-17-fidelity/). Representative matched views:
+
+| View | App | Approved reference |
+| --- | --- | --- |
+| Structured Checklist | [Desktop](assets/2026-10-17-fidelity/app-structured-dark-1440.png) | [Desktop](assets/2026-10-17-fidelity/reference-structured-dark-1440.png) |
+| Relaxed Checklist | [Desktop](assets/2026-10-17-fidelity/app-relaxed-dark-1440.png) | [Desktop](assets/2026-10-17-fidelity/reference-relaxed-dark-1440.png) |
+| Relaxed mobile | [390px](assets/2026-10-17-fidelity/app-relaxed-dark-390.png) | [390px](assets/2026-10-17-fidelity/reference-relaxed-dark-390.png) |
+| Routines | [Structured](assets/2026-10-17-fidelity/app-structured-routines.png) | [Structured](assets/2026-10-17-fidelity/reference-structured-routines.png) |
+| Timeline | [Structured](assets/2026-10-17-fidelity/app-structured-timeline.png) | [Structured](assets/2026-10-17-fidelity/reference-structured-timeline.png) |
+| Game Setup | [Relaxed](assets/2026-10-17-fidelity/app-relaxed-settings.png) | [Relaxed](assets/2026-10-17-fidelity/reference-relaxed-settings.png) |
+| Archive | [Structured](assets/2026-10-17-fidelity/app-structured-archive.png) | [Structured](assets/2026-10-17-fidelity/reference-structured-archive.png) |
+
+Additional captures cover both themes at 1440px/390px, populated mobile utility pages, compact rows,
+event/archive details, custom-routine drawers, archive history and the Settings pages.
+
+The comparison harness freezes time at 2026-10-16 12:00 UTC and intercepts the event-feed request
+with the prototype's **synthetic demonstration events**. It seeds disposable browser storage with
+matching routine choices and 38 archive snapshots. These fixtures are review evidence only: they
+are not imported by production, copied into public, or written into source snapshots. Actual setup,
+HTTP migration and offline checks use the built app and its real feed separately.
+
+Expected semantic differences: real account rows include Asia events, so totals differ from the
+prototype's game-only event list. One prototype custom task was not seeded. Production has no
+version metadata for archive events, so it groups saved records without inventing version names.
+Real reset clocks and available routines govern progress. Code/release notices reflect actual data;
+Checklist comparisons hide these notices to align the first viewport. The Structured prototype's
+filter dropdown is intentionally superseded by the owner's focus-pill decision.
+
+Reproduce with [fidelity-capture.mjs](fidelity-capture.mjs), from the repository root, setting
+`PUPPETEER_MODULE` to the scratch installation's ESM entry and `APP_URL` / `MOCKUP_URL` to fresh
+running origins. Chrome is `/usr/bin/google-chrome-stable`; the harness maps event-clock.test to
+loopback. Rebuild CSS/JS/static without fetching the feed, and use a new app port after each rebuild.
+
+## Independent review
+
+The first reviewer invocation hit a session usage limit; a retry succeeded. Its initial material
+findings were invisible scrolled timeline labels, substituted Settings navigation, routine column/
+status hierarchy, multi-account row-key collisions, and insufficient populated Archive evidence.
+One follow-up also required correcting the game-profile hierarchy. Each finding was fixed and the
+same screenshot paths recaptured. Final independent verdict: **“ship the reviewed corrections.”**
+All reported material findings were resolved. This verdict is scoped to the compared screens and
+reported fixes; it is not owner acceptance or blanket proof of every interaction.
+
+The Impeccable detector ran once and returned `[]` (no findings).
+
+## Verification
+
+Final browser build: `a28021bbe1a8`, production React/JSX transform, 117 bundled modules.
+
+- TypeScript passes. Full offline suite: **1,594 passed, 0 failed**, 5,410 assertions.
+- Docker-equivalent copied file layout, without docs/design/public: 1,593 passed, one intentional
+ docs-dependent skip, no failures. This validates the COPY-layout assumption; no Docker image was built.
+- Eight interface/theme/width combinations: no document overflow or page exceptions. Both densities,
+ reduced motion/effective 200% viewport, custom palette and timezone selection checked.
+- Ordinary insecure HTTP hostname: new setup, existing v1 migration, daily writes, second server,
+ reset override/revert, backup and reload pass; original legacy bytes retained. Structured is the
+ missing-choice default and saved Relaxed survives reload.
+- Real two-tab simultaneous writes with Web Locks disabled pass using the existing IndexedDB lock.
+ Offline reload passes on localhost's service-worker-capable origin.
+- Final-build interaction checks: multi-server bulk pins preserve the other view's placement; numeric
+ edits and custom-routine creation persist; completion, Undo, archive detail, Restore and Escape pass.
+ Final profile's server/reset controls were rechecked on insecure HTTP after layout changes.
+- `git diff --check` passes. No new runtime dependency, source fetch, commit or publication.
+
+The earlier rejection is preserved in the 2026-10-16 report. Passing tests did not establish its
+visual fidelity; the side-by-side evidence and scoped review above are the correction's design record.
diff --git a/docs/design-reviews/assets/2026-10-16-redesign/relaxed-light-1440.png b/docs/design-reviews/assets/2026-10-16-redesign/relaxed-light-1440.png
new file mode 100644
index 0000000..9a14723
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-16-redesign/relaxed-light-1440.png differ
diff --git a/docs/design-reviews/assets/2026-10-16-redesign/structured-dark-1440.png b/docs/design-reviews/assets/2026-10-16-redesign/structured-dark-1440.png
new file mode 100644
index 0000000..e6c119e
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-16-redesign/structured-dark-1440.png differ
diff --git a/docs/design-reviews/assets/2026-10-16-redesign/structured-dark-390.png b/docs/design-reviews/assets/2026-10-16-redesign/structured-dark-390.png
new file mode 100644
index 0000000..851fc0f
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-16-redesign/structured-dark-390.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-archive-detail-1440.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-archive-detail-1440.png
new file mode 100644
index 0000000..7c4b7ad
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-archive-detail-1440.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-archive-detail-390.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-archive-detail-390.png
new file mode 100644
index 0000000..b23003d
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-archive-detail-390.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-archive-history-1440.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-archive-history-1440.png
new file mode 100644
index 0000000..88614bc
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-archive-history-1440.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-archive-history-390.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-archive-history-390.png
new file mode 100644
index 0000000..e698ba1
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-archive-history-390.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-archive-mobile.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-archive-mobile.png
new file mode 100644
index 0000000..bf1aa69
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-archive-mobile.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-archive.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-archive.png
new file mode 100644
index 0000000..f5f3374
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-archive.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-compact-1440.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-compact-1440.png
new file mode 100644
index 0000000..b36fd13
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-compact-1440.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-compact-390.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-compact-390.png
new file mode 100644
index 0000000..c8d23df
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-compact-390.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-dark-1440.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-dark-1440.png
new file mode 100644
index 0000000..79ea5f8
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-dark-1440.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-dark-390.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-dark-390.png
new file mode 100644
index 0000000..0a270d7
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-dark-390.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-event-detail-1440.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-event-detail-1440.png
new file mode 100644
index 0000000..26d9771
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-event-detail-1440.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-event-detail-390.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-event-detail-390.png
new file mode 100644
index 0000000..ace6977
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-event-detail-390.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-light-1440.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-light-1440.png
new file mode 100644
index 0000000..b93d5c4
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-light-1440.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-light-390.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-light-390.png
new file mode 100644
index 0000000..5443e66
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-light-390.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-routine-editor-1440.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-routine-editor-1440.png
new file mode 100644
index 0000000..7a8aa09
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-routine-editor-1440.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-routine-editor-390.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-routine-editor-390.png
new file mode 100644
index 0000000..c7a0d1d
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-routine-editor-390.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-routines-mobile.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-routines-mobile.png
new file mode 100644
index 0000000..cbccdae
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-routines-mobile.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-routines.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-routines.png
new file mode 100644
index 0000000..d838cad
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-routines.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-settings-About-Event-Clock-1440.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-settings-About-Event-Clock-1440.png
new file mode 100644
index 0000000..0962800
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-settings-About-Event-Clock-1440.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-settings-About-Event-Clock-390.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-settings-About-Event-Clock-390.png
new file mode 100644
index 0000000..896b6a1
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-settings-About-Event-Clock-390.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-settings-Appearance-1440.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-settings-Appearance-1440.png
new file mode 100644
index 0000000..5b84fbc
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-settings-Appearance-1440.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-settings-Appearance-390.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-settings-Appearance-390.png
new file mode 100644
index 0000000..dc46df6
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-settings-Appearance-390.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-settings-Backup-1440.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-settings-Backup-1440.png
new file mode 100644
index 0000000..75d91f1
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-settings-Backup-1440.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-settings-Backup-390.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-settings-Backup-390.png
new file mode 100644
index 0000000..a0b8495
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-settings-Backup-390.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-settings-Changelog-1440.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-settings-Changelog-1440.png
new file mode 100644
index 0000000..fb7de33
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-settings-Changelog-1440.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-settings-Changelog-390.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-settings-Changelog-390.png
new file mode 100644
index 0000000..910c52f
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-settings-Changelog-390.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-settings-Default-view-1440.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-settings-Default-view-1440.png
new file mode 100644
index 0000000..59a7aee
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-settings-Default-view-1440.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-settings-Default-view-390.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-settings-Default-view-390.png
new file mode 100644
index 0000000..cdbf3aa
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-settings-Default-view-390.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-settings-Region-&-time-1440.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-settings-Region-&-time-1440.png
new file mode 100644
index 0000000..d13ee9e
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-settings-Region-&-time-1440.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-settings-Region-&-time-390.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-settings-Region-&-time-390.png
new file mode 100644
index 0000000..612fbb2
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-settings-Region-&-time-390.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-settings-Your-own-events-and-games-1440.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-settings-Your-own-events-and-games-1440.png
new file mode 100644
index 0000000..fd312ab
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-settings-Your-own-events-and-games-1440.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-settings-Your-own-events-and-games-390.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-settings-Your-own-events-and-games-390.png
new file mode 100644
index 0000000..924fe3d
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-settings-Your-own-events-and-games-390.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-settings-mobile.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-settings-mobile.png
new file mode 100644
index 0000000..024370b
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-settings-mobile.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-settings.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-settings.png
new file mode 100644
index 0000000..cbf824f
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-settings.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-timeline-mobile.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-timeline-mobile.png
new file mode 100644
index 0000000..3ecba2e
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-timeline-mobile.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-timeline.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-timeline.png
new file mode 100644
index 0000000..83f5a49
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-relaxed-timeline.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-archive-detail-1440.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-archive-detail-1440.png
new file mode 100644
index 0000000..dcc5894
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-archive-detail-1440.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-archive-detail-390.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-archive-detail-390.png
new file mode 100644
index 0000000..a7cdd35
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-archive-detail-390.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-archive-history-1440.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-archive-history-1440.png
new file mode 100644
index 0000000..794d73e
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-archive-history-1440.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-archive-history-390.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-archive-history-390.png
new file mode 100644
index 0000000..edf6655
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-archive-history-390.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-archive-mobile.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-archive-mobile.png
new file mode 100644
index 0000000..e37b401
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-archive-mobile.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-archive.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-archive.png
new file mode 100644
index 0000000..174b1fa
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-archive.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-compact-1440.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-compact-1440.png
new file mode 100644
index 0000000..94678af
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-compact-1440.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-compact-390.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-compact-390.png
new file mode 100644
index 0000000..8ba9d6c
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-compact-390.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-dark-1440.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-dark-1440.png
new file mode 100644
index 0000000..2674f8d
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-dark-1440.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-dark-390.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-dark-390.png
new file mode 100644
index 0000000..e176da5
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-dark-390.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-event-detail-1440.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-event-detail-1440.png
new file mode 100644
index 0000000..c888168
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-event-detail-1440.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-event-detail-390.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-event-detail-390.png
new file mode 100644
index 0000000..c6d14ab
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-event-detail-390.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-light-1440.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-light-1440.png
new file mode 100644
index 0000000..e2b389b
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-light-1440.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-light-390.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-light-390.png
new file mode 100644
index 0000000..7fec5b1
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-light-390.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-routine-editor-1440.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-routine-editor-1440.png
new file mode 100644
index 0000000..5e6a4df
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-routine-editor-1440.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-routine-editor-390.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-routine-editor-390.png
new file mode 100644
index 0000000..a9b4ab4
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-routine-editor-390.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-routines-mobile.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-routines-mobile.png
new file mode 100644
index 0000000..28446f8
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-routines-mobile.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-routines.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-routines.png
new file mode 100644
index 0000000..1b0c7a1
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-routines.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-settings-About-Event-Clock-1440.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-settings-About-Event-Clock-1440.png
new file mode 100644
index 0000000..ec16347
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-settings-About-Event-Clock-1440.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-settings-About-Event-Clock-390.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-settings-About-Event-Clock-390.png
new file mode 100644
index 0000000..b64c6bf
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-settings-About-Event-Clock-390.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-settings-Appearance-1440.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-settings-Appearance-1440.png
new file mode 100644
index 0000000..4dc91fd
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-settings-Appearance-1440.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-settings-Appearance-390.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-settings-Appearance-390.png
new file mode 100644
index 0000000..ea51b52
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-settings-Appearance-390.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-settings-Backup-1440.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-settings-Backup-1440.png
new file mode 100644
index 0000000..68cbf9b
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-settings-Backup-1440.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-settings-Backup-390.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-settings-Backup-390.png
new file mode 100644
index 0000000..afc3193
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-settings-Backup-390.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-settings-Changelog-1440.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-settings-Changelog-1440.png
new file mode 100644
index 0000000..9d108cd
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-settings-Changelog-1440.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-settings-Changelog-390.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-settings-Changelog-390.png
new file mode 100644
index 0000000..4322477
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-settings-Changelog-390.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-settings-Default-view-1440.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-settings-Default-view-1440.png
new file mode 100644
index 0000000..357d882
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-settings-Default-view-1440.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-settings-Default-view-390.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-settings-Default-view-390.png
new file mode 100644
index 0000000..97f059e
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-settings-Default-view-390.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-settings-Region-&-time-1440.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-settings-Region-&-time-1440.png
new file mode 100644
index 0000000..b572331
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-settings-Region-&-time-1440.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-settings-Region-&-time-390.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-settings-Region-&-time-390.png
new file mode 100644
index 0000000..b71b55e
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-settings-Region-&-time-390.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-settings-Your-own-events-and-games-1440.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-settings-Your-own-events-and-games-1440.png
new file mode 100644
index 0000000..6d0d23f
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-settings-Your-own-events-and-games-1440.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-settings-Your-own-events-and-games-390.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-settings-Your-own-events-and-games-390.png
new file mode 100644
index 0000000..7c2f439
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-settings-Your-own-events-and-games-390.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-settings-mobile.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-settings-mobile.png
new file mode 100644
index 0000000..cc5ade6
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-settings-mobile.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-settings.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-settings.png
new file mode 100644
index 0000000..6ba61c1
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-settings.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-timeline-mobile.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-timeline-mobile.png
new file mode 100644
index 0000000..1abbc6c
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-timeline-mobile.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-timeline.png b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-timeline.png
new file mode 100644
index 0000000..65667c0
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/app-structured-timeline.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/reference-events.json b/docs/design-reviews/assets/2026-10-17-fidelity/reference-events.json
new file mode 100644
index 0000000..6c45876
--- /dev/null
+++ b/docs/design-reviews/assets/2026-10-17-fidelity/reference-events.json
@@ -0,0 +1 @@
+[{"id":"e0","game":"genshin","title":"Anniversary Memories Album","desc":"Look back through the year's moments in an album and claim Primogems for each page you fill.","start":1790564400000,"end":1792292400000,"src":"https://genshin-impact.fandom.com/wiki/Anniversary_Memories_Album"},{"id":"e1","game":"zzz","title":"Clink, Clank, Pinball Knight!","desc":"A pinball minigame with daily stages; clear them for Polychromes and upgrade materials.","start":1789009200000,"end":1792378800000,"src":"https://game8.co/games/Zenless-Zone-Zero/archives/457176"},{"id":"e2","game":"zzz","title":"Diary of an Orbie Parent","desc":"Raise an Orbie through daily check-ins and small tasks.","start":1790737200000,"end":1792378800000,"src":"https://game8.co/games/Zenless-Zone-Zero/archives/457176"},{"id":"e3","game":"nte","title":"Stamina Recharge","desc":"Spend City Stamina twice as fast in select Hethereau Hobby activities.","start":1791169200000,"end":1792378800000,"src":"https://game8.co/games/Neverness-to-Everness/archives/592073"},{"id":"e4","game":"zzz","title":"Data Bounty - Combat Simulation","desc":"Short combat simulations with bonus rewards for the first clear of each.","start":1791946800000,"end":1792378800000,"src":"https://game8.co/games/Zenless-Zone-Zero/archives/457176"},{"id":"e5","game":"zzz","title":"All-New Program","desc":"","start":1788922800000,"end":1792465200000,"src":"https://game8.co/games/Zenless-Zone-Zero/archives/457176"},{"id":"e6","game":"genshin","title":"Predictive Victory Dynamics","desc":"A combat event in stages; each unlocks a day after the last.","start":1791774000000,"end":1792465200000,"src":"https://game8.co/games/Genshin-Impact/archives/301601"},{"id":"e7","game":"wuwa","title":"Bountiful Crescendo","desc":"","start":1792033200000,"end":1792638000000,"src":"https://game8.co/games/Wuthering-Waves/archives/453473"},{"id":"e8","game":"hsr","title":"4.6 Planar Fissure","desc":"Double rewards from Planar Ornament Extraction, a few times a day.","start":1791774000000,"end":1792897200000,"src":"https://game8.co/games/Honkai-Star-Rail/archives/408749"},{"id":"e9","game":"wuwa","title":"Artisan's Search","desc":"","start":1791428400000,"end":1792983600000,"src":"https://game8.co/games/Wuthering-Waves/archives/453473"},{"id":"e10","game":"wuwa","title":"Waking Moon Fishing","desc":"","start":1792033200000,"end":1793502000000,"src":"https://game8.co/games/Wuthering-Waves/archives/453473"},{"id":"e11","game":"genshin","title":"Battle Pass - Moontrace","desc":"","start":1790132400000,"end":1793588400000,"src":"https://game8.co/games/Genshin-Impact/archives/301601"},{"id":"e12","game":"genshin","title":"To Temper Thyself and Journey Far","desc":"","start":1786330800000,"end":1793588400000,"src":"https://game8.co/games/Genshin-Impact/archives/301601"},{"id":"e13","game":"hsr","title":"Love, Ghosts & Robots","desc":"","start":1790564400000,"end":1794366000000,"src":"https://game8.co/games/Honkai-Star-Rail/archives/408749"},{"id":"e14","game":"hsr","title":"Astral Imagea","desc":"","start":1790564400000,"end":1794366000000,"src":"https://game8.co/games/Honkai-Star-Rail/archives/408749"},{"id":"e15","game":"nte","title":"Pukaland Travelogue","desc":"","start":1790737200000,"end":1794366000000,"src":"https://game8.co/games/Neverness-to-Everness/archives/592073"},{"id":"e16","game":"wuwa","title":"3.7 Moonlit Path","desc":"","start":1790737200000,"end":1794366000000,"src":"https://game8.co/games/Wuthering-Waves/archives/453473"},{"id":"e17","game":"nte","title":"Coal Lumps Treasure","desc":"","start":1791428400000,"end":1794366000000,"src":"https://game8.co/games/Neverness-to-Everness/archives/592073"},{"id":"e18","game":"endfield","title":"Originium Supply Run","desc":"Mockup event so Endfield has a row.","start":1791514800000,"end":1792724400000,"src":null},{"id":"e19","game":"hsr","title":"Free Gilgamesh or Archer","desc":"","start":1784862000000,"end":null,"src":"https://game8.co/games/Honkai-Star-Rail/archives/408749"}]
\ No newline at end of file
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/reference-relaxed-archive-mobile.png b/docs/design-reviews/assets/2026-10-17-fidelity/reference-relaxed-archive-mobile.png
new file mode 100644
index 0000000..3980b8e
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/reference-relaxed-archive-mobile.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/reference-relaxed-archive.png b/docs/design-reviews/assets/2026-10-17-fidelity/reference-relaxed-archive.png
new file mode 100644
index 0000000..e638422
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/reference-relaxed-archive.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/reference-relaxed-dark-1440.png b/docs/design-reviews/assets/2026-10-17-fidelity/reference-relaxed-dark-1440.png
new file mode 100644
index 0000000..eb6c659
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/reference-relaxed-dark-1440.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/reference-relaxed-dark-390.png b/docs/design-reviews/assets/2026-10-17-fidelity/reference-relaxed-dark-390.png
new file mode 100644
index 0000000..6ea3648
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/reference-relaxed-dark-390.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/reference-relaxed-light-1440.png b/docs/design-reviews/assets/2026-10-17-fidelity/reference-relaxed-light-1440.png
new file mode 100644
index 0000000..7ee5976
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/reference-relaxed-light-1440.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/reference-relaxed-light-390.png b/docs/design-reviews/assets/2026-10-17-fidelity/reference-relaxed-light-390.png
new file mode 100644
index 0000000..18dff2f
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/reference-relaxed-light-390.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/reference-relaxed-routines-mobile.png b/docs/design-reviews/assets/2026-10-17-fidelity/reference-relaxed-routines-mobile.png
new file mode 100644
index 0000000..066e769
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/reference-relaxed-routines-mobile.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/reference-relaxed-routines.png b/docs/design-reviews/assets/2026-10-17-fidelity/reference-relaxed-routines.png
new file mode 100644
index 0000000..dd642cd
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/reference-relaxed-routines.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/reference-relaxed-settings-mobile.png b/docs/design-reviews/assets/2026-10-17-fidelity/reference-relaxed-settings-mobile.png
new file mode 100644
index 0000000..6812bfb
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/reference-relaxed-settings-mobile.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/reference-relaxed-settings.png b/docs/design-reviews/assets/2026-10-17-fidelity/reference-relaxed-settings.png
new file mode 100644
index 0000000..808a15d
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/reference-relaxed-settings.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/reference-relaxed-timeline-mobile.png b/docs/design-reviews/assets/2026-10-17-fidelity/reference-relaxed-timeline-mobile.png
new file mode 100644
index 0000000..6d4420a
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/reference-relaxed-timeline-mobile.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/reference-relaxed-timeline.png b/docs/design-reviews/assets/2026-10-17-fidelity/reference-relaxed-timeline.png
new file mode 100644
index 0000000..88854d4
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/reference-relaxed-timeline.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/reference-state.json b/docs/design-reviews/assets/2026-10-17-fidelity/reference-state.json
new file mode 100644
index 0000000..b2475d5
--- /dev/null
+++ b/docs/design-reviews/assets/2026-10-17-fidelity/reference-state.json
@@ -0,0 +1 @@
+{"archive":[{"id":"g1","game":"genshin","ver":"6.0","title":"Thunder Gauntlet Trials","status":"done","start":"2026-08-27","end":"2026-09-08","at":"2026-09-07","desc":"A combat challenge in six stages, one unlocking each day. Each first clear pays Primogems.","moved":false,"prog":{"t":"stages","got":6,"need":6,"unit":"stages","days":[]},"src":"https://game8.co/games/Genshin-Impact/archives/297500"},{"id":"g4","game":"genshin","ver":"6.0","title":"Fontinalia Film Festival","status":"done","start":"2026-09-03","end":"2026-09-17","at":"2026-09-16","desc":"Shoot short scenes around Fontaine and trade Film Points in the event shop.","moved":false,"prog":{"t":"amount","got":1200,"need":1200,"unit":"Film Points","days":[]},"src":"https://game8.co/games/Genshin-Impact/archives/297500"},{"id":"g5","game":"genshin","ver":"6.0","title":"Mystic Melody Concert","status":"ignored","start":"2026-09-10","end":"2026-09-24","at":"2026-09-11","desc":"A rhythm minigame with daily songs.","moved":false,"prog":{"t":"days","got":0,"need":5,"unit":"days","days":[]},"src":"https://game8.co/games/Genshin-Impact/archives/297500"},{"id":"g7","game":"genshin","ver":"6.0","title":"Gilded Winds Exploration","status":"expired","start":"2026-09-12","end":"2026-10-06","at":"2026-10-06","desc":"Explore five wind-swept ruins; each area opens a few days after the last.","moved":false,"prog":{"t":"stages","got":4,"need":5,"unit":"stages","days":[]},"src":"https://game8.co/games/Genshin-Impact/archives/297500"},{"id":"g3","game":"genshin","ver":"6.0","title":"Ley Line Overflow","status":"expired","start":"2026-09-15","end":"2026-09-22","at":"2026-09-22","desc":"Double Ley Line rewards, three times a day.","moved":false,"prog":{"t":"days","got":1,"need":7,"unit":"days","days":[6]},"src":"https://game8.co/games/Genshin-Impact/archives/297500"},{"id":"g6","game":"genshin","ver":"6.0","title":"Primordial Echoes Web Event","status":"done","start":"2026-09-20","end":"2026-09-27","at":"2026-09-21","desc":"A browser event: check in on three days for Primogems.","moved":true,"prog":{"t":"days","got":3,"need":3,"unit":"days","days":[0,4,6]},"src":"https://game8.co/games/Genshin-Impact/archives/297500"},{"id":"g2","game":"genshin","ver":"6.0","title":"Overflowing Mastery","status":"done","start":"2026-10-02","end":"2026-10-09","at":"2026-10-08","desc":"Double Talent Domain drops, three times a day.","moved":false,"prog":{"t":"days","got":6,"need":7,"unit":"days","days":[0,1,2,3,4,5]},"src":"https://game8.co/games/Genshin-Impact/archives/297500"},{"id":"g8","game":"genshin","ver":"6.1","title":"Moonlit Feast Login","status":"done","start":"2026-10-08","end":"2026-10-15","at":"2026-10-14","desc":"Log in on seven days for Intertwined Fates.","moved":false,"prog":{"t":"days","got":7,"need":7,"unit":"days","days":[0,1,2,3,4,5,6]},"src":"https://game8.co/games/Genshin-Impact/archives/297500"},{"id":"h2","game":"hsr","ver":"4.5","title":"Starlight Rhapsody","status":"done","start":"2026-08-13","end":"2026-09-02","at":"2026-09-01","desc":"A story event in eight chapters.","moved":false,"prog":{"t":"stages","got":8,"need":8,"unit":"stages","days":[]},"src":null},{"id":"h3","game":"hsr","ver":"4.5","title":"Gift of Odyssey","status":"done","start":"2026-08-13","end":"2026-09-24","at":"2026-08-20","desc":"Log in on seven days for Star Rail Special Passes.","moved":false,"prog":{"t":"days","got":7,"need":7,"unit":"days","days":[11,12,15,21,25,32,35]},"src":null},{"id":"h4","game":"hsr","ver":"4.5","title":"Aetherium Wars: Final Bout","status":"ignored","start":"2026-08-20","end":"2026-09-23","at":"2026-08-21","desc":"A creature-battling event with a separate shop.","moved":true,"prog":{"t":"days","got":0,"need":10,"unit":"days","days":[]},"src":null},{"id":"h5","game":"hsr","ver":"4.5","title":"Realm of the Strange","status":"done","start":"2026-09-01","end":"2026-09-08","at":"2026-09-07","desc":"Triple relic drops from Caverns of Corrosion, a few times a day.","moved":false,"prog":{"t":"days","got":3,"need":3,"unit":"days","days":[0,2,5]},"src":null},{"id":"h6","game":"hsr","ver":"4.5","title":"Fate's Forge Trial","status":"done","start":"2026-09-05","end":"2026-09-19","at":"2026-09-18","desc":"Trial characters in staged fights; points unlock rewards.","moved":false,"prog":{"t":"amount","got":3000,"need":3000,"unit":"points","days":[]},"src":null},{"id":"h7","game":"hsr","ver":"4.5","title":"Boulder Town Market","status":"expired","start":"2026-09-09","end":"2026-09-23","at":"2026-09-23","desc":"Run a market stall; coins buy materials in the event shop.","moved":false,"prog":{"t":"amount","got":1800,"need":2400,"unit":"coins","days":[]},"src":null},{"id":"h8","game":"hsr","ver":"4.6","title":"Planar Fissure","status":"done","start":"2026-09-26","end":"2026-10-03","at":"2026-10-02","desc":"Double rewards from Planar Ornament Extraction.","moved":false,"prog":{"t":"days","got":5,"need":5,"unit":"days","days":[0,1,2,3,4]},"src":null},{"id":"h1","game":"hsr","ver":"4.6","title":"Garden of Plenty","status":"expired","start":"2026-09-29","end":"2026-10-06","at":"2026-10-06","desc":"Double rewards from Calyx, until the daily limit.","moved":false,"prog":{"t":"days","got":3,"need":7,"unit":"days","days":[1,2,5]},"src":null},{"id":"z3","game":"zzz","ver":"2.2","title":"Sweet Sweet Sweets","status":"done","start":"2026-08-20","end":"2026-09-03","at":"2026-09-02","desc":"Bake with Bangboos on seven days for Polychromes.","moved":false,"prog":{"t":"days","got":7,"need":7,"unit":"days","days":[0,1,2,3,4,5,6]},"src":null},{"id":"z6","game":"zzz","ver":"2.2","title":"Ether Tide Login","status":"done","start":"2026-08-20","end":"2026-09-02","at":"2026-08-27","desc":"Log in on seven days for Encrypted Master Tapes.","moved":false,"prog":{"t":"days","got":7,"need":7,"unit":"days","days":[0,1,2,3,4,5,6]},"src":null},{"id":"z4","game":"zzz","ver":"2.2","title":"Rally Racer Remix","status":"ignored","start":"2026-08-27","end":"2026-09-10","at":"2026-08-27","desc":"A racing minigame in six tracks.","moved":false,"prog":{"t":"stages","got":0,"need":6,"unit":"stages","days":[]},"src":null},{"id":"z1","game":"zzz","ver":"2.2","title":"Hollow Zero: Lost Void – Season 2","status":"done","start":"2026-08-29","end":"2026-09-28","at":"2026-09-27","desc":"A roguelite season with weekly score tiers; each tier pays out once.","moved":false,"prog":{"t":"stages","got":9,"need":9,"unit":"tiers","days":[]},"src":null},{"id":"z5","game":"zzz","ver":"2.2","title":"Bangboo Brawl","status":"expired","start":"2026-09-03","end":"2026-09-17","at":"2026-09-17","desc":"Team Bangboos up in auto-battles for event points.","moved":false,"prog":{"t":"amount","got":1450,"need":1500,"unit":"points","days":[]},"src":null},{"id":"z2","game":"zzz","ver":"2.2","title":"Hollow Zero: Lost Void – Season 3","status":"done","start":"2026-09-12","end":"2026-10-12","at":"2026-10-11","desc":"A roguelite season with weekly score tiers; each tier pays out once.","moved":false,"prog":{"t":"stages","got":9,"need":9,"unit":"tiers","days":[]},"src":null},{"id":"z7","game":"zzz","ver":"2.3","title":"Phaethon's Errands","status":"done","start":"2026-10-01","end":"2026-10-13","at":"2026-10-12","desc":"Run five errands across New Eridu, one a day.","moved":false,"prog":{"t":"stages","got":5,"need":5,"unit":"stages","days":[]},"src":null},{"id":"w2","game":"wuwa","ver":"3.6","title":"Tidebound Regatta","status":"done","start":"2026-08-21","end":"2026-09-04","at":"2026-09-03","desc":"Boat races along the coast; first clears pay Astrite.","moved":false,"prog":{"t":"stages","got":7,"need":7,"unit":"races","days":[]},"src":null},{"id":"w3","game":"wuwa","ver":"3.6","title":"Echoing Marsh Patrol","status":"expired","start":"2026-08-28","end":"2026-09-11","at":"2026-09-11","desc":"Daily patrol missions through the marsh.","moved":false,"prog":{"t":"days","got":4,"need":7,"unit":"days","days":[2,4,11,12]},"src":null},{"id":"w4","game":"wuwa","ver":"3.6","title":"Lollo Logistics","status":"done","start":"2026-09-04","end":"2026-09-18","at":"2026-09-15","desc":"Deliver parcels around Lahai-Roi for Credits.","moved":false,"prog":{"t":"amount","got":2000,"need":2000,"unit":"Credits","days":[]},"src":null},{"id":"w6","game":"wuwa","ver":"3.6","title":"Waveplate Bonus Week","status":"ignored","start":"2026-09-15","end":"2026-09-22","at":"2026-09-15","desc":"Double drops from Simulation Challenges.","moved":false,"prog":{"t":"days","got":0,"need":7,"unit":"days","days":[]},"src":null},{"id":"w5","game":"wuwa","ver":"3.6","title":"Thousand Gateways","status":"done","start":"2026-09-11","end":"2026-09-25","at":"2026-09-24","desc":"A ten-floor challenge tower with set teams.","moved":false,"prog":{"t":"stages","got":10,"need":10,"unit":"floors","days":[]},"src":null},{"id":"w1","game":"wuwa","ver":"3.7","title":"Ascendant Aces","status":"ignored","start":"2026-10-01","end":"2026-10-29","at":"2026-10-03","desc":"A card battle event with a separate shop.","moved":false,"prog":{"t":"stages","got":0,"need":8,"unit":"rounds","days":[]},"src":null},{"id":"e2","game":"endfield","ver":"1.2","title":"Talos-II Survey Grant","status":"done","start":"2026-08-14","end":"2026-08-28","at":"2026-08-27","desc":"Log in on seven days for Originium.","moved":false,"prog":{"t":"days","got":7,"need":7,"unit":"days","days":[0,1,2,3,4,5,6]},"src":null},{"id":"e3","game":"endfield","ver":"1.2","title":"Wuling Night Market","status":"expired","start":"2026-08-21","end":"2026-09-11","at":"2026-09-11","desc":"Trade at the night market stalls for event points.","moved":true,"prog":{"t":"amount","got":4200,"need":6000,"unit":"points","days":[]},"src":null},{"id":"e1","game":"endfield","ver":"1.2","title":"Frontier Logistics Drill","status":"done","start":"2026-09-04","end":"2026-09-18","at":"2026-09-16","desc":"A logistics event in daily stages.","moved":false,"prog":{"t":"stages","got":6,"need":6,"unit":"stages","days":[]},"src":null},{"id":"e4","game":"endfield","ver":"1.3","title":"Protocol Overload Exercise","status":"ignored","start":"2026-09-25","end":"2026-10-09","at":"2026-09-26","desc":"Combat drills with rotating modifiers.","moved":false,"prog":{"t":"stages","got":0,"need":5,"unit":"stages","days":[]},"src":null},{"id":"n2","game":"nte","ver":"1.1","title":"Hethereau Cup Kart","status":"done","start":"2026-08-28","end":"2026-09-11","at":"2026-09-10","desc":"Kart races through the city's districts.","moved":false,"prog":{"t":"stages","got":5,"need":5,"unit":"races","days":[]},"src":null},{"id":"n3","game":"nte","ver":"1.1","title":"City Pulse Sign-in","status":"done","start":"2026-08-28","end":"2026-09-10","at":"2026-09-05","desc":"Sign in on seven days for Annulith.","moved":false,"prog":{"t":"days","got":7,"need":7,"unit":"days","days":[0,1,2,3,4,5,6]},"src":null},{"id":"n4","game":"nte","ver":"1.1","title":"Anomaly Night Shift","status":"expired","start":"2026-09-17","end":"2026-10-01","at":"2026-10-01","desc":"Night-shift anomaly hunts, one a day.","moved":false,"prog":{"t":"days","got":6,"need":7,"unit":"days","days":[0,6,9,10,11,12]},"src":null},{"id":"n1","game":"nte","ver":"1.1","title":"Neon Nights Photo Hunt","status":"ignored","start":"2026-09-20","end":"2026-10-04","at":"2026-09-22","desc":"Photograph neon signs around the city for event currency.","moved":false,"prog":{"t":"days","got":1,"need":5,"unit":"days","days":[1]},"src":null},{"id":"n5","game":"nte","ver":"1.2","title":"Pukaland Pre-show","status":"done","start":"2026-10-08","end":"2026-10-14","at":"2026-10-13","desc":"A three-day warm-up for Pukaland.","moved":false,"prog":{"t":"days","got":3,"need":3,"unit":"days","days":[0,1,2]},"src":null}],"followed":["routine:genshin:daily","routine:genshin:daily@asia","routine:hsr:daily","routine:zzz:daily","routine:wuwa:daily","routine:genshin:weekly-bosses","routine:genshin:weekly-bosses@asia","routine:genshin:battle-pass-exp","routine:genshin:spiral-abyss","routine:hsr:echo-of-war","routine:hsr:points","routine:hsr:pure-fiction","routine:hsr:memory-of-chaos","routine:zzz:ridu-weekly","routine:zzz:notorious-hunt","routine:zzz:shiyu-defense","routine:wuwa:weekly-activities","routine:wuwa:tower-of-adversity","routine:endfield:weekly-routine","routine:nte:beyond-the-rails","mytask:a1"],"pins":{"routine:genshin:daily":{"c":1},"routine:genshin:daily@asia":{"c":1},"routine:hsr:daily":{"c":1},"routine:zzz:daily":{"c":1},"routine:wuwa:daily":{"c":1},"routine:genshin:weekly-bosses":{"c":1,"t":1},"routine:genshin:weekly-bosses@asia":{"c":1},"routine:genshin:spiral-abyss":{"t":1},"routine:hsr:echo-of-war":{"c":1,"t":1},"routine:hsr:points":{"c":1},"routine:hsr:pure-fiction":{"c":1,"t":1},"routine:hsr:memory-of-chaos":{"t":1},"routine:zzz:ridu-weekly":{"c":1,"t":1},"routine:zzz:shiyu-defense":{"c":1,"t":1},"routine:wuwa:tower-of-adversity":{"t":1},"routine:endfield:weekly-routine":{"c":1},"routine:nte:beyond-the-rails":{"t":1},"mytask:a1":{"c":1}},"servers":{"genshin":["europe","asia"],"hsr":["europe"],"zzz":["europe"],"wuwa":["europe"],"arknights":["europe"],"endfield":["europe"],"nte":["europe"],"nikki":["europe"],"p5x":["europe"],"r1999":["europe"],"ba":["europe"],"fgo":["europe"],"gfl2":["europe"],"stellasora":["europe"],"czn":["europe"],"nikke":["europe"],"uma":["europe"],"hi3":["europe"],"pgr":["europe"]}}
\ No newline at end of file
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/reference-structured-archive-mobile.png b/docs/design-reviews/assets/2026-10-17-fidelity/reference-structured-archive-mobile.png
new file mode 100644
index 0000000..39fcfe9
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/reference-structured-archive-mobile.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/reference-structured-archive.png b/docs/design-reviews/assets/2026-10-17-fidelity/reference-structured-archive.png
new file mode 100644
index 0000000..71f9e79
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/reference-structured-archive.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/reference-structured-dark-1440.png b/docs/design-reviews/assets/2026-10-17-fidelity/reference-structured-dark-1440.png
new file mode 100644
index 0000000..17e69bf
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/reference-structured-dark-1440.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/reference-structured-dark-390.png b/docs/design-reviews/assets/2026-10-17-fidelity/reference-structured-dark-390.png
new file mode 100644
index 0000000..3e33459
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/reference-structured-dark-390.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/reference-structured-light-1440.png b/docs/design-reviews/assets/2026-10-17-fidelity/reference-structured-light-1440.png
new file mode 100644
index 0000000..2b2dc82
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/reference-structured-light-1440.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/reference-structured-light-390.png b/docs/design-reviews/assets/2026-10-17-fidelity/reference-structured-light-390.png
new file mode 100644
index 0000000..f99406a
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/reference-structured-light-390.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/reference-structured-routines-mobile.png b/docs/design-reviews/assets/2026-10-17-fidelity/reference-structured-routines-mobile.png
new file mode 100644
index 0000000..6b2d2ca
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/reference-structured-routines-mobile.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/reference-structured-routines.png b/docs/design-reviews/assets/2026-10-17-fidelity/reference-structured-routines.png
new file mode 100644
index 0000000..c4fda44
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/reference-structured-routines.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/reference-structured-settings-mobile.png b/docs/design-reviews/assets/2026-10-17-fidelity/reference-structured-settings-mobile.png
new file mode 100644
index 0000000..d05e71e
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/reference-structured-settings-mobile.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/reference-structured-settings.png b/docs/design-reviews/assets/2026-10-17-fidelity/reference-structured-settings.png
new file mode 100644
index 0000000..6d64d1a
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/reference-structured-settings.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/reference-structured-timeline-mobile.png b/docs/design-reviews/assets/2026-10-17-fidelity/reference-structured-timeline-mobile.png
new file mode 100644
index 0000000..14e1d74
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/reference-structured-timeline-mobile.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-fidelity/reference-structured-timeline.png b/docs/design-reviews/assets/2026-10-17-fidelity/reference-structured-timeline.png
new file mode 100644
index 0000000..1834b25
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-fidelity/reference-structured-timeline.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-second-rejection/current-structured-checklist.png b/docs/design-reviews/assets/2026-10-17-second-rejection/current-structured-checklist.png
new file mode 100644
index 0000000..1ab5d33
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-second-rejection/current-structured-checklist.png differ
diff --git a/docs/design-reviews/assets/2026-10-17-second-rejection/current-structured-setup.png b/docs/design-reviews/assets/2026-10-17-second-rejection/current-structured-setup.png
new file mode 100644
index 0000000..a661830
Binary files /dev/null and b/docs/design-reviews/assets/2026-10-17-second-rejection/current-structured-setup.png differ
diff --git a/docs/design-reviews/fidelity-capture.mjs b/docs/design-reviews/fidelity-capture.mjs
new file mode 100644
index 0000000..750cc99
--- /dev/null
+++ b/docs/design-reviews/fidelity-capture.mjs
@@ -0,0 +1,349 @@
+// Run from the repository root. Puppeteer stays in a scratch installation.
+const { default: puppeteer } = await import(process.env.PUPPETEER_MODULE);
+const APP_URL = process.env.APP_URL || "http://event-clock.test:41907";
+const MOCKUP_URL = process.env.MOCKUP_URL || "http://localhost:41901";
+import fs from "node:fs/promises";
+const out = "docs/design-reviews/assets/2026-10-17-fidelity";
+await fs.mkdir(out, { recursive: true });
+const base = JSON.parse(
+ await fs.readFile("public/data/events.v1.json", "utf8"),
+ ),
+ events = JSON.parse(
+ await fs.readFile(`${out}/reference-events.json`, "utf8"),
+ );
+const at = "2026-10-16T12:00:00.000Z";
+const fixture = {
+ ...base,
+ events: events.map((e) => ({
+ ...base.events[0],
+ id: `${e.game}:visual-${e.id}:2026-10-01`,
+ game: e.game,
+ title: e.title,
+ summary: e.desc || null,
+ startsAt: new Date(e.start).toISOString(),
+ endsAt: e.end ? new Date(e.end).toISOString() : null,
+ startPrecision: "exact",
+ endPrecision: e.end ? "exact" : "unknown",
+ regionScoped: false,
+ regionEnds: null,
+ sourceUrl: e.src || base.events[0].sourceUrl,
+ })),
+ sources: base.sources.map((s) => ({
+ ...s,
+ lastSuccessAt: at,
+ lastConfirmedAt: at,
+ })),
+};
+const sampleState = JSON.parse(
+ await fs.readFile(`${out}/reference-state.json`, "utf8"),
+);
+const b = await puppeteer.launch({
+ executablePath: "/usr/bin/google-chrome-stable",
+ args: [
+ "--no-sandbox",
+ "--no-proxy-server",
+ "--host-resolver-rules=MAP event-clock.test 127.0.0.1",
+ ],
+});
+let errors = [];
+for (const variant of ["structured", "relaxed"])
+ for (const width of [1440, 390])
+ for (const theme of ["dark", "light"]) {
+ for (const mode of ["reference", "app"]) {
+ const ctx = await b.createBrowserContext(),
+ p = await ctx.newPage();
+ p.on("pageerror", (e) => errors.push(e.message));
+ await p.setViewport({ width, height: 1000 });
+ if (mode === "app") {
+ await p.setRequestInterception(true);
+ p.on("request", (r) =>
+ r.url().endsWith("events.v1.json")
+ ? r.respond({
+ status: 200,
+ contentType: "application/json",
+ body: JSON.stringify(fixture),
+ })
+ : r.continue(),
+ );
+ await p.evaluateOnNewDocument((at) => {
+ const D = Date;
+ class FixedDate extends D {
+ constructor(...args) {
+ super(...(args.length ? args : [at]));
+ }
+ static now() {
+ return D.parse(at);
+ }
+ }
+ window.Date = FixedDate;
+ if (!localStorage.getItem("seeded")) {
+ localStorage.setItem("seeded", "yes");
+ localStorage.setItem(
+ "gacha-tracker:v1:prefs",
+ JSON.stringify({
+ onboarded: true,
+ knownGames: [
+ "genshin",
+ "hsr",
+ "zzz",
+ "wuwa",
+ "endfield",
+ "nte",
+ ],
+ view: "soon",
+ region: "europe",
+ }),
+ );
+ }
+ }, at);
+ await p.goto(APP_URL);
+ await p.waitForSelector(".app-navigation");
+ await p.evaluate(
+ ({ variant, theme, sampleState, template, at }) => {
+ let s = JSON.parse(
+ localStorage.getItem("gacha-tracker:v2:reader"),
+ );
+ s.prefs.redesignTourSeen = "2026-10-redesign";
+ s.prefs.interface = variant;
+ s.prefs.theme = theme;
+ s.prefs.changelogSeen = "9999-12-31";
+ const genshin = Object.values(s.accounts).find(
+ (a) => a.game === "genshin",
+ );
+ const asia = structuredClone(genshin);
+ asia.id = "account:visual-asia";
+ asia.region = "asia";
+ asia.clocks[0].region = "asia";
+ asia.clocks[0].automaticBase.offsetUtc = 8;
+ s.accounts[asia.id] = asia;
+ s.data[asia.id] = {
+ progress: {},
+ ignored: {},
+ daily: {},
+ amounts: {},
+ };
+ for (const a of Object.values(s.accounts)) {
+ a.routines = {};
+ a.intent = {
+ events: true,
+ dailies: true,
+ weeklyMonthly: true,
+ cycles: true,
+ };
+ for (const key of sampleState.followed) {
+ const [id, region = "europe"] = key.split("@");
+ if (!id.includes(`:${a.game}:`) || region !== a.region)
+ continue;
+ const rid = id.endsWith(":daily") ? `dailies:${a.game}` : id;
+ const pin = sampleState.pins[key] || {};
+ a.routines[rid] = {
+ followed: true,
+ checklist: !!pin.c,
+ timeline: !!pin.t,
+ };
+ }
+ if (
+ (a.game === "genshin" && a.region === "europe") ||
+ a.game === "hsr"
+ )
+ s.data[a.id].daily.initial = {
+ [`dailies:${a.game}`]: { days: ["2026-10-16"], at },
+ };
+ s.data[a.id].amounts.initial = {
+ "routine:genshin:weekly-bosses": {
+ "2026-10-12": { value: a.region === "asia" ? 3 : 2, at },
+ },
+ "routine:hsr:points": { "2026-10-12": { value: 11000, at } },
+ };
+ }
+ for (const r of sampleState.archive) {
+ const a = Object.values(s.accounts).find(
+ (a) => a.game === r.game && a.region === "europe",
+ );
+ if (!a) continue;
+ const event = {
+ ...template,
+ id: `${r.game}:archive-${r.id}:${r.start}`,
+ game: r.game,
+ title: r.title,
+ summary: r.desc || null,
+ startsAt: new Date(r.start).toISOString(),
+ endsAt: new Date(r.end).toISOString(),
+ startPrecision: "day",
+ endPrecision: "day",
+ sourceUrl: r.src || template.sourceUrl,
+ };
+ const key = JSON.stringify([a.id, event.id]);
+ s.archive[key] = {
+ accountId: a.id,
+ event,
+ outcome: r.status === "expired" ? "unfinished" : r.status,
+ outcomeAt: new Date(r.at).toISOString(),
+ resolvedEnd: Date.parse(r.end),
+ clockRevision: "initial",
+ progress: { note: "Saved note from this event." },
+ days: r.prog.days.map((d) =>
+ new Date(Date.parse(r.start) + d * 86400000)
+ .toISOString()
+ .slice(0, 10),
+ ),
+ amounts:
+ r.prog.t === "amount"
+ ? { [r.start]: { value: r.prog.got, at } }
+ : {},
+ };
+ }
+ localStorage.setItem(
+ "gacha-tracker:v2:reader",
+ JSON.stringify(s),
+ );
+ },
+ { variant, theme, sampleState, template: fixture.events[0], at },
+ );
+ await p.reload();
+ await p.waitForSelector(".event-entry");
+ await p.evaluate(() => {
+ for (const e of document.querySelectorAll(
+ ".updates-panel,.release-update",
+ ))
+ e.style.display = "none";
+ });
+ } else {
+ await p.goto(
+ `${MOCKUP_URL}/${variant === "structured" ? "round6/b-rail.html" : "round7/converged.html"}`,
+ );
+ await p.evaluate((theme) => {
+ S.theme = theme;
+ S.sourceScenario = "current";
+ S.wnSeen = true;
+ S.codeSeen = CODE_DEMO.map((c) => c.code);
+ render();
+ }, theme);
+ await p.addStyleTag({
+ content:
+ "#chrome{display:none}body{padding-top:0}.rail{top:0!important;max-height:100vh!important}.source-status-label{display:none}",
+ });
+ }
+ await p.evaluate(() => document.fonts.ready);
+ await p.screenshot({
+ path: `${out}/${mode}-${variant}-${theme}-${width}.png`,
+ });
+ if (
+ await p.evaluate(
+ () => document.documentElement.scrollWidth > innerWidth,
+ )
+ )
+ errors.push(`${mode} ${variant} ${width} ${theme} overflow`);
+ if (theme === "dark") {
+ for (const page of ["routines", "archive", "timeline", "settings"]) {
+ await p.evaluate(
+ ({ page, mode }) => {
+ if (mode === "reference") {
+ S.tab = page;
+ if (page === "settings") S.sel = "genshin";
+ render();
+ } else {
+ [...document.querySelectorAll(".app-navigation button")]
+ .find((e) => e.textContent.toLowerCase().startsWith(page))
+ .click();
+ }
+ },
+ { page, mode },
+ );
+ await new Promise((r) => setTimeout(r, 100));
+ if (page === "settings" && mode === "app") {
+ await p.evaluate(() =>
+ [...document.querySelectorAll(".settings-row")]
+ .find((e) => e.textContent.startsWith("Genshin Impact"))
+ .click(),
+ );
+ await new Promise((r) => setTimeout(r, 100));
+ }
+ await p.screenshot({
+ path: `${out}/${mode}-${variant}-${page}${width === 1440 ? "" : "-mobile"}.png`,
+ });
+ }
+ if (mode === "app") {
+ const nav = async (name) => {
+ await p.evaluate(
+ (name) =>
+ [...document.querySelectorAll(".app-navigation button")]
+ .find((e) => e.textContent.startsWith(name))
+ .click(),
+ name,
+ );
+ await new Promise((r) => setTimeout(r, 120));
+ };
+ await nav("Archive");
+ await p.click(".archive-open");
+ await p.waitForSelector("dialog[open]");
+ await p.screenshot({
+ path: `${out}/app-${variant}-archive-detail-${width}.png`,
+ });
+ await p.keyboard.press("Escape");
+ await p.evaluate(() =>
+ [...document.querySelectorAll("button")]
+ .find((e) => e.textContent === "History timeline")
+ .click(),
+ );
+ await p.screenshot({
+ path: `${out}/app-${variant}-archive-history-${width}.png`,
+ });
+ await nav("Routines");
+ await p.evaluate(() =>
+ [...document.querySelectorAll("button")]
+ .find((e) => e.textContent === "Create your own routine")
+ .click(),
+ );
+ await p.waitForSelector("dialog[open]");
+ await p.screenshot({
+ path: `${out}/app-${variant}-routine-editor-${width}.png`,
+ });
+ await p.keyboard.press("Escape");
+ await nav("Settings");
+ for (const label of [
+ "Appearance",
+ "Default view",
+ "Your own events and games",
+ "Region & time",
+ "Backup",
+ "About Event Clock",
+ "Changelog",
+ ]) {
+ await p.evaluate(
+ (label) =>
+ [...document.querySelectorAll(".settings-row")]
+ .find(
+ (e) =>
+ e.querySelector(".settings-copy > span")
+ ?.textContent === label,
+ )
+ .click(),
+ label,
+ );
+ await new Promise((r) => setTimeout(r, 100));
+ await p.screenshot({
+ path: `${out}/app-${variant}-settings-${label.replaceAll(" ", "-")}-${width}.png`,
+ });
+ }
+ await nav("Checklist");
+ await p.evaluate(() =>
+ [...document.querySelectorAll("button")]
+ .find((e) => e.textContent === "Compact")
+ .click(),
+ );
+ await p.screenshot({
+ path: `${out}/app-${variant}-compact-${width}.png`,
+ });
+ await p.click(".event-entry");
+ await p.screenshot({
+ path: `${out}/app-${variant}-event-detail-${width}.png`,
+ });
+ await p.keyboard.press("Escape");
+ }
+ }
+ await ctx.close();
+ }
+ }
+console.log(JSON.stringify(errors));
+await b.close();
diff --git a/docs/handoffs/2026-10-16-redesign-fidelity-handoff.md b/docs/handoffs/2026-10-16-redesign-fidelity-handoff.md
new file mode 100644
index 0000000..d4cc333
--- /dev/null
+++ b/docs/handoffs/2026-10-16-redesign-fidelity-handoff.md
@@ -0,0 +1,464 @@
+# Event Clock redesign: handoff to the replacement agent
+
+> **Newer handoff:** the replacement implementation was also rejected. Read
+> [2026-10-17 second-rejection handoff](2026-10-17-redesign-second-rejection-handoff.md) first.
+> The owner's next-step instruction is to launch the current server, then ask them for the biggest
+> differences from the mockup before making more UI changes.
+
+Date: 2026-10-16. Repository: `/home/user/gacha-event-tracker`.
+
+**2026-10-17 owner amendment:** Structured is now the default. Preserve any explicitly saved
+Relaxed choice. The references and the historical rejection described below remain unchanged.
+Correction evidence is recorded in `docs/design-reviews/2026-10-17-redesign-fidelity.md`.
+
+## 1. Assignment and current acceptance status
+
+**Finish the already-approved redesign by faithfully translating the approved mockup markup into
+production React and wiring in real data. The current implementation was rejected by the owner.**
+
+The owner approved Round 7, then explicitly chose to retain two interfaces with a Settings toggle:
+
+- **Relaxed (default):** `design/mockups/round7/converged.html`.
+- **Structured:** `design/mockups/round6/b-rail.html` — specifically Round 6 **B**, not A or C.
+
+The owner subsequently reported that the implementation differed substantially from Round 7 and
+asked why the previous agent had invented a design instead of using the markup and wiring data.
+The previous agent acknowledged that this is what happened: it reused old visual components and
+selected mockup CSS, built substitute pages, then verified functionality rather than fidelity.
+
+**Do not repeat that approach.** Preserve the approved rendered structure, hierarchy, proportions,
+spacing, typography, control arrangements, information architecture, responsive behavior and
+interactions. Translate HTML to JSX, break it into maintainable components and connect real state.
+The existing presentation is not the reference. Matching fonts and outer panel sizes is insufficient.
+
+Do not transplant the prototype's fake data, fixed clocks, demo toolbar, localStorage namespace or
+monolithic demo JavaScript into production. That restriction was previously misinterpreted as
+permission to redesign its presentation. It is not. Real-data integration should retain the markup.
+
+The owner selected **Native execution** and had already approved the spec/plan before implementation.
+The task is to finish that approved design, not restart brainstorming or request permission for each
+routine correction. Resolve actual conflicts explicitly; do not make new product choices silently.
+This handoff-writing turn makes no implementation changes and does not complete the correction.
+
+## 2. Read these records, in this order
+
+1. `AGENTS.md` — complete repository working agreement, not just the excerpt in a chat.
+2. This document and the approved reference HTML files above, **rendered in a browser**.
+3. `docs/design-reviews/2026-10-16-redesign-round7.md` and corresponding round4/round5/round6 records.
+4. `docs/superpowers/specs/2026-10-16-ui-redesign-design.md` — approved functional/state requirements.
+5. `docs/superpowers/plans/2026-10-16-ui-redesign.md` — implementation inventory, not evidence of acceptance.
+6. `docs/PRD.md`, `PRODUCT.md`, `DESIGN.md`, `DATA-MODEL.md`, `ARCHITECTURE.md`; source work additionally
+ requires `INGESTION.md` and `SOURCES.md`.
+7. `docs/design-reviews/2026-10-16-redesign-implementation.md` — prior verification, rejection audit,
+ HTTP follow-up and representative **rejected implementation** screenshots.
+8. `.superpowers/sdd/2026-10-16-ui-redesign/progress.md` and `review-packet.md` — historical ledger.
+
+Important record caveats:
+
+- The plan's checkboxes were all marked complete before the owner rejected the result. Presentation
+ and fidelity gates must be reopened; do not treat those checks as acceptance.
+- The spec's opening “written for owner review; not implemented” status is stale. Its requirements
+ were approved; implementation exists but is rejected. Distinguish intent from implementation status.
+- Most product/design docs were rewritten to describe the rejected implementation. Their design
+ descriptions cannot override the approved mockup or the owner's later correction. Repair this
+ documentation drift as the faithful implementation lands.
+- The Round 7 review initially discussed one UI; its later owner-decision note supersedes that:
+ retain **two**, Round 7 and Round 6 B.
+- `2026-10-16-110223-command-messagesuperpowersusing-superpowersc.txt` is an earlier transcript supplied
+ by the owner. It predates later work; it is supplemental, not the only record of current decisions.
+
+## 3. Workspace and authorization
+
+Last inspected HEAD: `ee41389` (`docs: a handoff note for the next agent, and the worktree feed trap`).
+Implementation was made in place on `main`. **All redesign changes remain uncommitted.** No push,
+PR, publication or deployment was performed by this agent. The owner has separately built/used the
+app through their network, so browser data may already contain v2 state.
+
+Run `git status --short` before work. There are many modified tracked files and many untracked new
+files; `git diff` alone omits most new implementation code. Preserve them. In particular, do not
+`git clean`, reset to HEAD, delete all untracked files, or discard the state work to obtain a clean
+starting point. Earlier untracked `.impeccable/`, design artifacts, transcripts and review documents
+also existed and must not be treated as disposable agent scratch.
+
+The approved plan says to use the current checkout; do not create a branch/worktree, commit, push,
+or publish without a separate owner instruction. The owner's standing workflow is to carry approved
+work through execution without repeated approval stops. Keep progress communication concise and
+report genuine problems directly.
+
+## 4. What went wrong, concretely
+
+The previous implementation created `ReaderApp.tsx`, reused old `EventRow`, `NextUp`, `Timeline`,
+`EventDetail` and `Colophon` presentation, and added `redesign.css` plus new simplified pages.
+That produced functional features with materially different presentation and interactions.
+
+Confirmed divergence examples — this is a starting inventory, not an exhaustive audit:
+
+| Area | Approved reference | Rejected implementation |
+| --- | --- | --- |
+| Header | Navigation counts; intentional Settings placement; mockup header hierarchy | Generic navigation and a different live/upcoming summary |
+| Standard events | Event content, prominent separate deadline/supporting text, chevron, deliberate meter placement | Old EventRow internals decorated with CSS; different countdown hierarchy and layout |
+| Routines | Per-game bands, aligned routine/progress/cycle/pin/menu columns and appropriate library/create flows | Flattened library rows with stacked checkbox options |
+| Settings | Sidebar includes individual game entries/descriptions leading into game profiles | Generic Your games page with a game dropdown |
+| Timeline | Routine periods/progress and reset markers plotted on the same time axis as events | Event timelines followed by ordinary routine controls; textual reset strips |
+| Archive | Story panel plus interactive per-game outcome bars/filtering and the approved per-game/history bodies | Simplified statistics and per-game text totals |
+| Forms/overlays | Approved drawers, sheets, field groupings and hierarchy | Several older production forms and modal layouts reused |
+| Mobile | Mockup-specific compact rail, hierarchy and responsive adaptations | Generic stacking; screenshots showing no overflow were accepted without reference comparison |
+
+The earlier independent review concentrated on storage safety and functional correctness. It found
+important real bugs, which were fixed, but it did not establish design fidelity. **1,590 passing
+tests is not evidence that this is the approved design.** Screenshots in the earlier implementation
+report document the rejected result; do not use them as visual goldens.
+
+## 5. How to read the mockup correctly
+
+The reference HTML is layered: it contains earlier CSS/render functions, then later overrides.
+Reading its first stylesheet or first definition of a renderer in isolation gives the wrong target.
+Render the complete file and exercise its actual controls.
+
+Useful anchors in Round 7 (line numbers are approximate; search the names):
+
+- Final airy system begins around line 1003: `/* A single airier world, informed by the original C planner. */`.
+- Header `tabs()` around 1336; initial `baseRoutinesScreen()` around 1392; `settingsScreen()` around 1480.
+- Later `round4Checklist()` around 1894; final timeline renderer around 1912.
+- Final `checklistScreen()` around 1976; `archiveStory()` / `renderArchive()` around 1989.
+- Final `routinesScreen()` around 2051; later render/tour/number-input adjustments around 2065.
+- Final `generalPane()` around 2073, `onboarding()` around 2085, `returningTour()` around 2087.
+
+The reference has its own review toolbar, shortcuts, fixed `NOW`, synthetic histories and namespace
+`event-clock:mockup:round7:v1`. Those are tools for inspecting states, not product UI/state to ship.
+Capture all relevant states using that toolbar and the product's controls. Do the equivalent for
+Round 6 B; do not assume it is merely Round 7 with a border-radius change.
+
+Some demo behavior is explicitly superseded by owner requirements: e.g. adding a server must start
+fresh choices, not copy follows/pins just because an old prototype handler does. The stale-data label
+must be **Event data may be out of date**, replacing the mockup's **Older schedules**. The two-interface
+selector is an approved addition to Appearance. Keep these necessary adaptations narrow and explicit.
+
+## 6. Approved requirements to retain
+
+The detailed spec remains necessary. This summary preserves the accumulated owner feedback:
+
+- Two visual interfaces over shared real state/components; Relaxed default. Interface, theme,
+ colour scheme and Standard/Compact row density are independent.
+- Standard rows without descriptive text are shorter, with no reserved empty description area.
+- Focus uses the existing All/game pills, counts, toggle-to-All and Next behavior, first in the
+ Checklist rail as in B. It hides for one tracked game and never changes followed games.
+- Checklist, Timeline, Routines, Archive and Settings are the main navigation. Utility navigation
+ must not overwrite the saved preferred main view (`soon` / `timeline`).
+- Source age belongs in the same compact updates structure as new-since-last-look notices, only
+ when tracked games have stale data. Source scope is independent of temporary focus. Codes,
+ event newness, release news and source age are distinct states. Keep reasons/timestamps/source
+ links in detail and Data & sources; never invent a failure cause. Footer always states data age.
+- Routines are rows. Manage built-ins, custom routines, follow/unfollow, Make it mine, and independent
+ Checklist/Timeline pins there. Stopping a routine preserves history. Keep daily ticks one action.
+- Dailies can have their own Checklist panel or be grouped with routines; the same setting appears
+ in Routines and Appearance. Group policy: collapsed, expanded by default, or always expanded.
+ Focus temporarily expands groups without rewriting the policy. Numeric controls have visible
+ native steppers and step one.
+- Timeline routines belong on the time axis. Separate reset strips, when enabled, sit beside their
+ game's events. Preserve the cross-game Ending soonest queue when strips are off; focus applies
+ to events, routines and reset strips together.
+- Add another server explains simultaneous accounts and asks all tracking questions again. It copies
+ no progress, follows or pins. Changing an existing server preserves that account's identity/history.
+ Prevent duplicate active game/server setup; retired histories remain recoverable.
+- Each game's settings includes Setup, Codes and About. About names all relevant real sources,
+ links them and thanks editors, including sources contributing zero events. Game colour has picker,
+ hex, preview and reset, shared across that game's servers.
+- Per-server reset timing has an override and Revert to Automatic; preserve historical clock revisions.
+ Display timezone defaults to Automatic, permits named IANA zones/search and revert, and affects
+ display/manual date entry rather than server periods or source instants.
+- Appearance includes Slate/Warm/Plum/Custom; custom light/dark palettes, game identity and urgency
+ remain legible and distinct. Preserve offline font fallback.
+- About retains the full colophon, acknowledgements, sources, studios, disclaimer and project links.
+ Changelog is its own Settings page, **not a footer link or an About button**.
+- Lists must contain actual settings, not a pointless Done/ignored row linking to Archive.
+- New setup initially selects **events only**. Ask default server, appearance, timezone, games,
+ tracking types and preferred main view. Types explain cycles and remain selectable even when
+ nothing is available; explain the absence. Upcoming visibility belongs in “What do you want to
+ track?”, initially off. Do not change existing readers to new-reader defaults.
+- Returning users get a one-time welcome/tour with Skip/Back; preserve progress and preferred view.
+- Done/Ignore saves immediately, explains movement to Archive for about 1.5 seconds, then fades/
+ collapses. Undo lasts about eight seconds; Archive Restore remains available. Honor reduced motion.
+- Archive retains real metadata/logs/outcomes, defaults to per-game history, offers alternate history
+ timeline and the approved story statistics/outcome bars. Exclude ignored/unresolved entries from
+ completion-rate denominators; unknown ends stay unknown. Explain incomplete pre-redesign history.
+- Backups retain seven daily, four weekly and six monthly snapshots, latest per disjoint UTC bucket,
+ plus recovery. Created during app use, not magically on unused days. View backup → preview →
+ Restore this backup; support recovery/Undo. Keep progress merge distinct from full restore.
+- Real codes notify in-app. Viewing clears per-game newness separately from per-server redemption;
+ codes and event newness do not clear one another. No sample codes/history/health as production data.
+
+## 7. Existing implementation map: preserve useful machinery, replace presentation faithfully
+
+Production entry is `main.tsx` → `App.tsx` (stable re-export) → `ReaderApp.tsx`.
+`ReaderContent` coordinates pages, focus, feed/state, archive transitions, settings and detail adapters.
+Only the **v2 reader store** is mounted for production writes. Older hooks still supply validators,
+types and pure helpers; **do not mount their v1 persistence effects alongside the v2 store**.
+
+| Files | Existing responsibilities / cautions |
+| --- | --- |
+| `src/client/state/reader/schema.ts` | Zod v2 envelope, stable local account IDs, clocks, placements, archive/seen snapshots; passthrough unknown fields; account/clock reference validation |
+| `migration.ts`, `legacy.ts` | v1 migration, original-account mapping, retained raw bytes, startup old-tab deltas and deletion handling |
+| `store.ts`, `reducer.ts`, `lock.ts`, `useReader.ts` | Serialized read-latest writes, operation replay, field timestamps/tombstones, account/pin rebase, storage-event updates, visible save errors |
+| `accounts.ts`, `clock.ts`, `src/shared/readerClock.ts` | Account lifecycle, automatic clock metadata revisions, override/revert, active versus historical-only imported clocks |
+| `eventRows.ts`, `routines.ts` | Account-aware real selectors and custom recurrence projection; useful data sources for faithful new markup |
+| `archive.ts`, `archiveStats.ts`, `aliases.ts` | Durable snapshots/outcomes/restore/expiry, real statistics, retired/merged ID carry |
+| `backups.ts`, `restore.ts`, `export.ts`, `import.ts` | UTC retention, validated versioned envelopes, recovery/pending restore, legacy imports, progress-only merge |
+| `ids.ts`, `actions.ts` | HTTP-compatible identity generation, action timestamps/operation IDs |
+| `src/client/state/appearance.ts` | Saved palette plus readable derived tokens against surfaces; check reference fidelity when applying |
+| `src/client/state/displayTime.ts`, `CustomForms.tsx`, `useCustom.ts` | IANA display formatting, manual wall-time parsing, DST behavior and interpreted-zone context |
+| `src/shared/time.ts`, `daily.ts`, `goal.ts`, `task.ts` | Existing domain rules extended with optional injected reset timing; defaults preserve old behavior |
+| `src/client/components/redesign/` | Current replacement pages — functional starting points, **not accepted presentation** |
+| `src/client/redesign.css`, `styles.css`, `index.html` | New overrides/fonts/prepaint; existing old CSS/components still influence internals |
+| `api.ts`, `sw.js`, `useAppUpdate.ts` | Separate event/code feeds; network-first data; unchanged waiting-worker update handshake |
+
+Existing UI files include AppNavigation, SettingsLayout, GameSettings, RoutinesPage, DailyPanel,
+RoutineGroup, RoutineHistory, TimelinePage, ArchivePage/ArchiveStory, BackupPage, AppearancePage,
+TimeSettings, UpdatesPanel, DataSourcesPage, FeedFooter, SetupWizard, ReturningTour, TrackingChoices,
+CodesPage and ChangelogPage. Reuse their real operations where sound; change their markup/layout to
+match the approved reference. Do not preserve a rejected arrangement just to keep a component name.
+
+The prior wrapper-file substitutions are recorded in the ledger. They do not justify retaining
+presentation mismatches. Likewise, prior DOM/render tests may assert the rejected structure; update
+those intentionally while retaining meaningful state, accessibility and data-preservation coverage.
+
+## 8. Data that must survive this correction
+
+Browser-local accounts are game/server tracking identities, **not authentication accounts**.
+There is no server copy of user progress. UI work must preserve existing v1 and already-created v2
+state, account IDs, original event/task/daily IDs and day/period labels.
+
+Active storage keys:
+
+```
+gacha-tracker:v2:reader
+gacha-tracker:v2:migration-recovery
+gacha-tracker:v2:backups
+gacha-tracker:v2:restore-pending
+gacha-tracker:v2:restore-recovery
+```
+
+Do not delete `gacha-tracker:v1:*`. `ReaderState` contains `accounts`, per-account `data` with progress/
+ignored/daily/amount maps, `archive`, `seenEvents`, prefs, custom games/events/tasks, `codeSeen`,
+`codeRedeemed` and `eventSeen`. Daily/amount maps are nested under clock revision. Archive/seen keys
+are `JSON.stringify([accountId,eventId])`; those are distinct from immutable domain IDs.
+
+Actions carry timestamps and operation IDs. Ordinary writes use Web Locks; browsers without Web
+Locks use an IndexedDB readwrite transaction mutex. Failure is disclosed without publishing a
+successful in-memory save. Account updates capture a base and rebase changed routine/pin fields.
+
+Exports: `format: "gacha-tracker-export"`, `version: 2`, `exportedAt`, `reader`, `legacy`; progress
+exports mark `scope: "progress"`. Full restore validates first and saves recovery plus a pending
+transaction before replacing live state; bootstrap resolves pending restore before mounting writers.
+Progress imports merge by game/server, preserve local prefs/active reset, retain imported clock
+history and preserve unknown extensions. Backups are outside the envelope to avoid recursive growth.
+
+Important fixes already made and regression-tested — retain them:
+
+- Initial event-newness seeding is batched; per-account effect writes previously created an
+ O(accounts²) queue that made settings appear unresponsive.
+- Capture/reconciliation waits for finished onboarding; otherwise unchosen catalogue games enter
+ new readers' seen history before they select games.
+- Deleted custom events/occurrences must not reappear from seen-event fallback. A vanished source
+ event with an unpassed/unknown end remains accessible; distinguish deletion from source absence.
+- Imported clock revisions retain historical logs without changing the local active reset.
+ Equality ignores import-only `historicalOnly` so repeated imports do not duplicate revisions/logs.
+- Incoming opaque root/account extensions survive subsequent exports/transfers and deduplicate.
+ Imported seen markers remain distinct from redemption and are merged appropriately.
+- Retired accounts Resume by actually removing `retiredAt`; changed-field rebase must handle deletion.
+ Independent tabs editing pins on the same routine must preserve both fields.
+- Reset override/revert and automatic metadata correction append revisions, never rewrite old ticks.
+ History resolves the explicit original revision, not today's reset.
+- Alias carry includes amount history and archive/seen snapshots as well as progress/daily/ignore.
+- v2 preferences are read before initial paint; user choices must not be replaced by old v1 defaults.
+- Manual refresh fetches both feeds, ignores build-only timestamp movement and discloses partial
+ code-feed failure. Codes page discloses stale data. Source diagnostics never invent missing causes.
+- Native numeric controls keep a local draft so async persistence does not swallow rapid typing.
+
+These foundations have substantial tests, but are not an assurance that every integration is correct.
+Review real consumers while changing markup; do not label the state layer or presentation complete
+without verifying the resulting flows.
+
+## 9. Critical HTTP/WireGuard regression: do not reintroduce
+
+The owner uses an HTTP network address through WireGuard: **10.20.20.2**.
+They encountered on first visit:
+
+> Event Clock could not open. Your saved data could not be opened safely…
+
+Exact reproduction: plain HTTP hostname in Chrome → `isSecureContext === false`,
+`crypto.randomUUID` undefined, `crypto.getRandomValues` available. Startup account minting threw;
+`initializeReader` caught it and returned generic `unavailable`, misleadingly blaming saved data.
+Localhost-only checks missed it because localhost is treated as a secure context.
+
+Fixed in `state/reader/ids.ts` using 128-bit `crypto.getRandomValues` tokens. Use `mintAccountId()` for
+new accounts and `mintLocalId()` for operations, reset revisions and backups. **Never reintroduce
+direct client `crypto.randomUUID()` or remint existing IDs.** There are three new regressions in
+`test/reader-http.test.ts` that failed before the fix. Existing legacy bytes were preserved.
+
+Actual HTTP-origin browser checks pass: old-progress migration, new setup, editing, extra server,
+reset override/revert, backups and reload. IndexedDB locking works on this origin too. Secure-only
+features must degrade honestly (e.g. clipboard may require manual copy; a service worker is not
+available on ordinary HTTP). Do not claim HTTP offline support just because localhost SW tests pass.
+
+## 10. Codes ingestion is real and already implemented
+
+Do not replace it with prototype codes or repeat external fetching just to work on presentation.
+
+- Source: `https://game8.co/games/Genshin-Impact/archives/304759`.
+- One descriptive-aggregator robots request and one page request returned 200 during capture.
+ No header-shaping/access workaround or repeated community requests were used.
+- Pinned: `fixtures/genshin/game8-codes-2026-10-16.html`, expected JSON and capture metadata.
+- Snapshots: `snapshots/genshin-game8-codes.html`, meta, `snapshots/codes-first-seen.json`.
+- Parser/registry: `src/ingest/codes/game8.ts`, `registry.ts`, `build.ts`.
+- Offline builder: `scripts/build-codes.ts`; published file `data/codes.v1.json`.
+- `src/shared/codes.ts` validates codes/feed. Source-stated calendar expiry is `expiresOn`, not a
+ fabricated `expiresAt` instant. Unstated expiry remains null. First observation survives rebuilds.
+- The captured current section has ten actual codes; the three expired programme codes are fenced
+ out. Tests include a future-expiry variant that admits those programme codes appropriately.
+- `scripts/refresh-sources.ts` combines event adapters and count-only code sources under the original
+ robots, six-hour floor, host spacing, conditional requests and snapshot gates. Event adapters
+ still return GachaEvent arrays; codes are not fake events.
+- `package.json` build includes `build:codes`; the service worker caches the separate feed correctly.
+
+Use the fixtures/snapshots offline. Existing parser/date rules and source conduct remain binding.
+No SQLite, remote sync, push service, unrelated parser changes or new runtime dependencies are needed.
+
+## 11. Verification already available — and its limits
+
+Latest completed verification after the HTTP fix:
+
+- `bun test`: **1,590 pass, zero failures, 75 files**.
+- `bun run typecheck` and `git diff --check`: pass.
+- Full offline build before the HTTP follow-up: 209 events across 20 games, ten codes.
+- Latest JS/static rebuild after HTTP fix: shell `9752bd6bf1d2`, 119 bundled modules.
+- Earlier Docker COPY-layout suite, without docs/public: 1,586 pass, one intentional README-only
+ skip, zero failures (before the three HTTP tests were added). Recheck after your changes.
+- Independent read-only reviews found/fixed several storage/integration issues; focused HTTP review
+ found no important issues. These reviews did **not** establish visual fidelity.
+
+Prior browser checks covered migration, default view, both interfaces/themes at 1440/390, densities,
+reduced motion, an effective 200% CSS viewport, onboarding, direct ticks, extra servers, reset history,
+code redemption, Done/Ignore/Undo, custom unknown-end events, real JSON download/upload/restore,
+backup restore/Undo, timezone search, offline reload and the actual waiting-service-worker prompt.
+These are behavioral evidence and regression starting points; the owner still rejected the UI.
+
+Tests that pin state, clocks, source honesty, archive denominators, retention, quota/recovery and
+imports remain valuable. Some late render tests passed immediately because code already existed;
+not every historical checkbox is evidence of a genuine RED test run.
+
+## 12. Local tools and traps
+
+Commands (Bun, React 19, strict TypeScript, Tailwind, Zod; use existing dependencies):
+
+```bash
+bun test
+bun run typecheck
+bun run build:css
+bun run build:js
+bun run build:static
+PORT= bun run serve.ts
+bun run build # offline feed + codes + shell, no wiki fetch required
+git diff --check
+```
+
+- Use `--production` exactly as the existing JS build does. `NODE_ENV=production` or a define alone
+ can produce a cleanly built bundle that crashes with jsxDEV errors; real-browser loading is required.
+- **Fresh port after each rebuild:** a localhost/HTTPS service worker caches the shell cache-first.
+ Do not diagnose old cached bundles as new-code behavior. Ordinary HTTP also caches JS/CSS in-browser;
+ use fresh origins/cache bypass for verification and a hard refresh for the owner.
+- Stop only your own servers by exact PID/port; never `pkill -f serve.ts`.
+- To inspect the reference directly, serve a separate scratch origin with
+ `PUBLIC_DIR=design/mockups PORT= bun run serve.ts`, then open
+ `/round7/converged.html` or `/round6/b-rail.html`. Keep mockup and production browser contexts
+ separate. Never point prototype handlers at production storage.
+- Last known preview servers were 41880 and 41881, serving rebuilt `public/`; recheck before use.
+ Earlier verification servers 41873–41879 were stopped. Do not assume temporary processes persist.
+- `public/data/` is ignored. A fresh checkout/worktree has no feed until built/copied. No worktree is
+ requested here, but this trap matters if the environment is reconstructed.
+- Browser storage seed is now v2 after initialization. `gacha-tracker:v1:prefs` with `onboarded:true`
+ works only before first migration. Use disposable contexts and reload after intentional seeding;
+ do not let mounted effects race test setup or touch the owner's real storage.
+- Wait for rendered content, not `networkidle0`. Use `textContent`, because CSS uppercase changes
+ `innerText`. Keep capture scale about 3× or less to avoid blank sticky panes.
+- Tests must not require build output or docs. `test/serve.test.ts` creates its own tree.
+- In this tool sandbox, localhost listening misleadingly reported EADDRINUSE; unrestricted runs
+ passed. Tests/Chrome/server calls required the appropriate tool escalation. Do not confuse that
+ restriction with an actual application failure.
+
+Chrome: `/usr/bin/google-chrome-stable`. Scratch Puppeteer import used successfully:
+
+```
+/tmp/claude-1000/-home-user-gacha-event-tracker/06f9b760-325c-487b-bd28-0493ad38d1b2/scratchpad/pp/node_modules/puppeteer-core/lib/esm/puppeteer/puppeteer-core.js
+```
+
+Scratch Prettier: `/tmp/gacha-redesign-format/node_modules/prettier/bin/prettier.cjs`.
+No browser/format dependency was installed into the project. Discover/recreate scratch tooling if
+these paths do not survive; do not add runtime dependencies for it.
+
+Temporary browser scripts (inspect their origins/selectors and adapt; these assert rejected markup):
+`/tmp/redesign-browser-journeys.mjs`, `redesign-browser-matrix.mjs`, `redesign-browser-custom.mjs`,
+`redesign-browser-update.mjs`, `redesign-browser-release-matrix.mjs`, `/tmp/check-reader-release.mjs`.
+HTTP reproduction/fix verification: `/tmp/reproduce-reader-first-visit.mjs`, `/tmp/verify-reader-http.mjs`.
+The latter launches Chrome with `--no-proxy-server` and
+`--host-resolver-rules=MAP event-clock.test 127.0.0.1` and uses `http://event-clock.test:`.
+This exercises the HTTP security boundary locally; no request to the owner's WireGuard machine is
+necessary. Temporary artifacts are not the durable source of requirements or acceptance.
+
+## 13. Recommended correction sequence
+
+1. Inspect the complete approved Round 7 and Round 6 B in a browser. Inventory screens, controls,
+ overlays and states. Capture the target before editing. Start with the real rendered markup.
+2. Reopen the plan's presentation/fidelity gates. Record a concrete component mapping from reference
+ elements to JSX and existing selectors/actions. Keep already-tested domain/state machinery where
+ correct. Do not create another design or hide differences behind new CSS overrides.
+3. Port the shared shell and Checklist faithfully, including exact event structure, header/counts,
+ rail hierarchy, daily tick treatment, grouped routines, update rows, Standard/Compact and mobile.
+4. Port Routines/library/create/edit, per-game settings/sidebar/profiles, appearance/timezone and
+ source/backup/about/changelog pages. Preserve the approved information architecture and controls.
+5. Correct Timeline as a genuine shared time-axis view, with routine periods/progress/reset markers,
+ optional adjacent reset strips and the correct global queue. Preserve account-aware identities
+ and event opening for multiple servers/custom occurrences.
+6. Port Archive story/outcome bars/filtering, per-game/history layouts, details and both onboarding
+ flows. Connect actual empty, unavailable, stale, unknown-end and legacy-history states honestly.
+7. Verify each screen against equivalent reference states while developing, then repeat the full
+ matrix and behavior/data-preservation suite. Update docs to the accepted result, not to a newly
+ invented substitute. Leave integration/commit/publication for explicit owner instruction.
+
+This is suggested sequencing, not permission to defer required screens. Complete the approved scope.
+If production facts require a genuine departure, identify the exact reference behavior and reason;
+do not silently substitute a different interaction.
+
+## 14. Completion criteria for the next agent
+
+- Side-by-side target/production comparison for both references in equivalent states, using matched
+ or deliberately representative content. Explain data-dependent differences rather than dismissing
+ markup/layout mismatches as real-data effects. Inspect DOM structure and computed styles where
+ useful; screenshots alone do not verify hidden interactions.
+- Check Checklist, Timeline, Routines, library/create/edit, game Setup/Codes/About, every Settings
+ page, Archive/history/details, new onboarding and returning tour, including empty/error states.
+- Desktop and phone, light/dark, Standard/Compact, multi-game and focused/single-game, two servers,
+ long titles, missing descriptions, unknown ends, stale/fresh sources, custom colours/timezone,
+ keyboard/focus, reduced motion and 200% effective viewport. No clipped controls or page overflow.
+- All existing meaningful regressions plus updated presentation tests, strict typecheck, production
+ build and real-browser load pass. Tests remain offline and independent of public/docs.
+- Ordinary HTTP network-origin startup and subsequent writes remain working. Preserve all migration,
+ recovery, cross-tab, reset-history, code-newness/redemption and import/export protections above.
+- A final reviewer must explicitly compare against the approved rendered mockups as well as audit
+ correctness. Review “looks similar” or “no overflow” is insufficient. Do not report completion
+ based on counts of passing tests while known design differences remain.
+- Update AGENTS/product/design/data/architecture/source docs and handoff/status records honestly.
+ No false completed checkboxes; record material limits and no invented data. No commit/push/publish
+ without authorization.
+
+## 15. Prompt to give the replacement agent
+
+> Read `AGENTS.md` and `docs/handoffs/2026-10-16-redesign-fidelity-handoff.md`. Finish the approved
+> redesign in the current working tree. The current implementation was rejected because it
+> substituted its own presentation for the approved markup. Faithfully translate
+> `design/mockups/round7/converged.html` (Relaxed/default) and
+> `design/mockups/round6/b-rail.html` (Structured) into React and wire real state/data. Preserve the
+> existing tested migration, storage, reset, backup, codes and HTTP/WireGuard fixes. Compare actual
+> reference and production screens/interactions throughout; do not treat the old completed plan,
+> passing tests or rejected screenshots as design acceptance. Complete all required screens and
+> validation. Leave changes uncommitted and unpublished unless I separately authorize integration.
diff --git a/docs/handoffs/2026-10-17-redesign-second-rejection-handoff.md b/docs/handoffs/2026-10-17-redesign-second-rejection-handoff.md
new file mode 100644
index 0000000..b72c360
--- /dev/null
+++ b/docs/handoffs/2026-10-17-redesign-second-rejection-handoff.md
@@ -0,0 +1,926 @@
+# Redesign handoff: second implementation rejected
+
+Date: 2026-10-17. Repository: `/home/user/gacha-event-tracker`.
+
+## Start here: launch the current server, then ask the owner
+
+**Explicit owner instruction for the next agent:** launch the current server, make it accessible
+on `0.0.0.0`, give the owner its browse URL, **then ask the owner for the biggest differences from
+the mockup**. Get that feedback before making more UI changes. Do not begin another broad visual
+rewrite or substitute your own audit for this conversation.
+
+Suggested question after the server is reachable:
+
+> The current build is running at [URL]. What are the biggest differences you see compared with
+> the approved mockup? Which screen and interface should we fix first?
+
+The owner has not yet identified the specific screen, viewport or visual differences behind this
+second rejection. Do not guess that it is Checklist, Structured, a font-loading problem, or stale
+cache. An optional screen/layout question was sent, but the owner instead requested this handoff.
+
+At the handoff, the current build was running at **http://10.20.20.2:41908**, listening on all
+interfaces (`*:41908`), Bun PID **2536610**. `/api/health` responded successfully. The process may
+not survive the session; check rather than assuming it is running. `serve.ts` uses `Bun.serve`'s
+all-interface default even though its log prints `localhost`. Verify the actual listener.
+
+If it needs restarting, from the repository root:
+
+```bash
+PORT=41908 bun run serve.ts
+```
+
+If that port is occupied by the current server, reuse it; do not kill unknown processes. If the
+shell is rebuilt, use a fresh port to avoid a previous service worker serving an old shell.
+For a missing/stale build, rebuild only the shell, preserving the existing feed:
+
+```bash
+bun run build:css && bun run build:js && bun run build:static
+PORT= bun run serve.ts
+```
+
+Use the machine's reachable network address in the URL, not `0.0.0.0` as a destination. The owner
+uses WireGuard `10.20.20.2`. Binding may require command escalation in the agent sandbox. Do not
+fetch community sources just to start the UI.
+
+## Current acceptance and scope
+
+The owner asked for a faithful implementation of the already-approved mockups, then explicitly
+said: **“Also make the structure layout the default.”** Structured is now the absent-preference
+and new-setup default; an explicitly saved Relaxed choice is preserved.
+
+After this agent claimed the correction was complete and started the build for the owner, the owner
+said:
+
+> “This is still not even close to the mockup.”
+
+The owner then stopped further implementation and requested a handover specification. **The current
+implementation is rejected. The previous completion claim and visual-review verdict do not establish
+acceptance.** This handoff turn makes documentation changes only, retaining two audit screenshots
+captured by a browser inspection that had already started when the owner asked to stop.
+
+The unrelated request about sorting PDF-export examples was explicitly withdrawn as a message meant
+for another agent. It is not part of this repository's task.
+
+## Approved visual authority
+
+1. Latest owner feedback, including the launch-and-ask instruction above.
+2. `design/mockups/round7/converged.html` — Relaxed.
+3. `design/mockups/round6/b-rail.html` — Structured, specifically B.
+4. Approved functional/state requirements in
+ `docs/superpowers/specs/2026-10-16-ui-redesign-design.md`.
+
+Read `AGENTS.md`, relevant product/design/domain docs, and the previous handoff
+`docs/handoffs/2026-10-16-redesign-fidelity-handoff.md`. The earlier handoff remains useful for the
+first failure, reference-file anchors and invariants. This document supersedes its current-status
+and next-action instructions. Historical statements that Relaxed is the default are superseded by
+the owner's Structured-default instruction.
+
+The reference HTML files contain multiple generations of render functions and CSS overrides. The
+fully rendered final file is the reference, not the first function with a matching name. Preserve
+composition, proportions, hierarchy, typography, controls, density, responsive behavior and page
+organization when translating HTML into React. Do not treat the reference as inspiration.
+
+Necessary authorized adaptations remain narrow: focus pills in both interfaces, real data and
+account-aware state, independent placements and servers, and the copy “Event data may be out of
+date.” Never ship the prototype's fixed clock, synthetic events/history, demo toolbar or demo state.
+
+## What this attempt changed
+
+This is an inventory, **not a list of accepted presentation decisions**:
+
+- `src/client/ReaderApp.tsx`: central shared UI composition, navigation counts, release notice,
+ settings routing, dedicated checklist components, archive-detail routing.
+- `components/redesign/ChecklistEvent.tsx`: new event rows and `NextDeadline`, replacing old
+ `EventRow`/`NextUp` usage in the active Checklist.
+- `FocusPicker.tsx`, `DailyPanel.tsx`, `RoutineGroup.tsx`: rail and direct progress controls.
+- `RoutinesPage.tsx`, `RoutineEditor.tsx`: reset/game grouping, table columns, progress bars,
+ library, independent pin controls and native custom-routine drawer.
+- `TimelinePage.tsx`: shared time grid for events and routine periods, sticky names, progress fill,
+ resets, zoom and mixed deadline queue. Sticky bar labels were repaired after review.
+- `ArchivePage.tsx`, `ArchiveStory.tsx`, `ArchiveDetail.tsx`: outcome story/filter/sidebar/list/history
+ and a saved-record dialog. The archive dialog reads recorded progress rather than today's live
+ event state.
+- `SettingsLayout.tsx`, `GameSettings.tsx`, `AppearancePage.tsx`, `TrackingChoices.tsx`: individual
+ game navigation, separate settings pages, interface/scheme controls, tracking/server/colour/reset
+ groups. The final profile uses tabs to choose which server's tracking choices are edited.
+- `SetupWizard.tsx`, `EventDetail.tsx`: modified setup/detail presentation, retaining much existing
+ form content.
+- `src/client/redesign.css`: large stylesheet, with initial rules plus later overrides. It is about
+ 2,900 lines and contains repeated selectors and superseded layout rules.
+- `state/usePrefs.ts`, `state/appearance.ts`, `index.html`: Structured default and interface grounds.
+- Tests and docs were updated. No change in this attempt intentionally reminted event/account IDs.
+
+Component shorthand above resolves to `src/client/components/redesign/`, except `EventDetail.tsx`,
+which is `src/client/components/EventDetail.tsx`. `state/` shorthand resolves under `src/client/`.
+`AppNavigation.tsx` was also changed: live counts and Settings placement. The new component files
+introduced by this attempt were `ChecklistEvent.tsx`, `FocusPicker.tsx`, `RoutineEditor.tsx` and
+`ArchiveDetail.tsx`; many other untracked files were already present from the preceding agent.
+
+Additional implementation details worth retaining:
+
+- `prefs.routineGroup` is a new optional stored preference: absent/`reset` means By reset; `game`
+ means By game. It uses the existing permissive preferences envelope, with no new storage key.
+- `appearance.ts` applies Structured Slate grounds/surfaces separately from Relaxed, while keeping
+ contrast adjustment and existing custom palettes. The CSS and runtime tokens must be examined
+ together: runtime variables can override apparent reference values in the stylesheet.
+- `TimelinePage` exports `routinePeriod`, also used for Routines reset countdowns. It uses resolved
+ reader clocks, not guessed UTC boundaries. Custom timeline occurrences respect account event intent.
+- `placementAccounts` in `RoutinesPage` combines bulk pin changes per account. Reverting to one stale
+ full-account write per row can silently undo an earlier row's placement.
+- `ArchiveDetail` displays the stored outcome/date/note/check-ins/amounts; Restore uses the existing
+ reducer. Already-ended records can be reconciled back into history. It is intentionally separate
+ from the live `ReaderDetail` adapter.
+- `RoutineControl` keeps a numeric draft during editing so a transient empty input is usable.
+ Game colour previews use the rendered, contrast-adjusted game metadata.
+- The profile server tabs are an implementation adaptation, not a separately approved owner design.
+ The reviewer accepted them within its narrow follow-up; the owner's overall rejection supersedes
+ that. Revisit them if they are among the differences the owner identifies.
+
+Tests changed during this attempt:
+
+| File under `test/` | Change |
+| --- | --- |
+| `appearance.test.ts` | Structured default and retained explicit Relaxed distinction |
+| `prefs.test.ts` | Default/fallback expectations and saved Relaxed normalization |
+| `reader-store.test.ts`, `reader-restore.test.ts` | Test choices adjusted so writes/restores still change the now-Structured default |
+| `redesign-timeline.test.tsx` | Routine resets participate in mixed Ending soonest order |
+| `redesign-routines.test.tsx` | Bulk placement combines per account and preserves the other view's pin |
+| `game-settings.test.tsx` | Updated expected guidance for independent server progress |
+
+Documentation touched by this attempt includes `AGENTS.md`, `docs/PRD.md`, `DATA-MODEL.md`,
+`DESIGN.md`, `ARCHITECTURE.md`, the approved spec/plan, the first handoff, the correction report,
+and `.superpowers/sdd/2026-10-16-ui-redesign/progress.md`. Other dirty docs and ingestion files may
+belong to the earlier agent; do not attribute all of `git diff HEAD` to this attempt. There is no
+clean per-agent commit boundary. The real changed code remains in the working tree.
+
+## What went wrong
+
+### 1. Translation remained approximate
+
+The first rejected agent had wrapped old presentation with new CSS. This agent replaced more actual
+markup, but still recreated the reference piecemeal instead of establishing a complete translation
+of the final reference structure. It retained old form internals and filled gaps with implementation
+choices, then added CSS overrides until selected screenshots looked closer.
+
+Concrete examples caught during this attempt's review were substituted Settings navigation, a
+routine Cycle column and missing live summary, invisible timeline bar text, and game colour above
+tracking with repeated tall account blocks. Those particular defects were subsequently corrected.
+**Their correction is not proof that the remaining screens or overall design are faithful.** The
+owner's latest rejection requires a fresh understanding of the gaps, starting with their feedback.
+
+### 2. Comparison fixtures made the evidence narrower than the claim
+
+`docs/design-reviews/fidelity-capture.mjs` freezes the clock, intercepts the event-feed request with
+mockup events, and seeds disposable routine/archive state. That can isolate visual differences, but
+it is not the app the owner browsed. For Checklist it explicitly hides `.updates-panel` and
+`.release-update` to align the first viewport. The comparison's event/account counts also differ
+from the prototype, and the custom prototype routine was not seeded.
+
+A real-feed capture after the rejection shows two prominent notice bands above Running now, many
+short title-only events, and much different content density. A fixture-based image with hidden
+notices did not validate that real first impression. Do not suppress production UI in an acceptance
+capture. If fixture comparison remains useful, label it as a separate diagnostic and also capture
+the exact normal-use state the owner is judging.
+
+### 3. Functional checks became a proxy for the visual task again
+
+Tests, no-overflow checks, correct IDs, working locks and successful actions are necessary. They
+cannot certify fidelity. This attempt's final response led with rebuilt screens and test success
+and reported that review cleared the issues. That was too strong for the evidence and the owner's
+actual experience.
+
+### 4. The independent review's scope was stretched
+
+The reviewer found a finite list of material issues, then scored fixes to that list. Its final
+verdict was **“ship the reviewed corrections”**, explicitly limited to those findings, not blanket
+acceptance. Subsequent passes were instructed not to reopen a wider hunt. That process could verify
+local repairs while leaving broader divergence undiscovered. The owner rejected the result anyway.
+
+Do not continue from the review's ship verdict. Treat the supplied user feedback as the next primary
+input, then review the whole affected composition against the rendered reference.
+
+### 5. Documentation presented implementation as the design truth
+
+`docs/DESIGN.md` and the 2026-10-17 correction report describe the implemented design confidently.
+They are records of what was built, not evidence that it is approved. Earlier failure had the same
+pattern. Keep requirements, current implementation, functional verification and owner acceptance
+separate. Do not rewrite the spec to make the implementation look compliant.
+
+## Evidence and limits
+
+- Historical correction report and staged comparison images:
+ `docs/design-reviews/2026-10-17-redesign-fidelity.md` and its linked assets.
+ These now carry a rejection warning. They document a rejected attempt, not new visual goldens.
+- Fresh real-feed audit image:
+ `docs/design-reviews/assets/2026-10-17-second-rejection/current-structured-checklist.png`.
+- Fresh first-visit setup image:
+ `docs/design-reviews/assets/2026-10-17-second-rejection/current-structured-setup.png`.
+
+The two fresh images used Chrome at 1440×1000 on `http://10.20.20.2:41908`. The setup was a clean
+browser context. For Checklist, that disposable context was marked onboarded and limited to
+Genshin/HSR/ZZZ/Wuwa; the actual feed and notices were retained, with no request interception.
+**These are not screenshots of the owner's browser or stored preferences.** The browser reported
+Structured and loaded Barlow/Barlow Condensed. This rules out missing fonts in that test context
+only; it does not identify the owner's complaint or prove their environment matches it.
+
+One observable item for later inspection: the actual audit showed a Checklist header count of 46
+and Focus All count of 60. They currently count different row sets. Do not claim this is the owner's
+main concern without asking. Likewise, correct shorter rows without descriptions are an approved
+requirement, so do not fabricate summaries merely to match fixture density.
+
+## Preserve the working state and domain work
+
+HEAD remained `ee41389`. The checkout is dirty on `main`; the last status had 89 modified/untracked
+entries including work that predates this agent. No commit, push, PR or deployment was made. Running
+a local preview server was explicitly authorized. Do not reset, clean untracked files, discard the
+reader implementation or create a new branch/worktree without a new reason/owner instruction.
+
+Preserve, or carefully test any necessary changes to:
+
+- v2 reader envelope, migration/recovery, unchanged IDs and legacy bytes;
+- account-specific progress, saved clocks and archive snapshots;
+- Web Locks plus IndexedDB serialization fallback, save failures and cross-tab synchronization;
+- backup/export/import/recovery and independent code seen/redemption state;
+- unknown-end/date-precision semantics and community-source conduct;
+- insecure HTTP identity generation using `crypto.getRandomValues`, not `crypto.randomUUID`;
+- new Structured default and explicit saved Relaxed preference;
+- useful fixes from this attempt: bulk routine placements saved together per account, row keys
+ containing account+routine, and archived details using saved snapshots.
+
+The most recent successful checks before handoff were 1,594 tests passing, TypeScript passing,
+production build `a28021bbe1a8`, ordinary HTTP migration/setup/server/reset/backup checks, numeric
+progress and routine creation, completion/Undo/archive Restore, and separate concurrent/offline
+browser checks. The Docker-equivalent file layout passed 1,593 tests with one intentional skip;
+no Docker image was built. This is functional evidence only, not visual acceptance.
+
+## Recommended continuation after owner feedback
+
+1. Write down the owner's biggest differences in their words, with the relevant interface, screen
+ and viewport. Launch-and-ask comes before implementation.
+2. Render that exact approved reference state and current app state. Compare actual content,
+ visible notices and first-visit/returning conditions as appropriate. Record differences in
+ topology, dimensions, type hierarchy and controls, not just a list of CSS values.
+3. Repair that bounded surface by translating the final reference markup and styles into the real
+ component tree. Replace rejected structure when needed; avoid another stylesheet of corrective
+ overrides over structurally different elements.
+4. Keep an explicit list of necessary real-data/accessibility adaptations, with reasons. Ask the
+ owner when an adaptation changes a visible product decision instead of silently choosing it.
+5. Show the normal running build to the owner at the chosen checkpoint. A fixture capture, passing
+ detector or subagent approval cannot substitute for checking their reported differences.
+6. Then carry accepted treatment across the remaining screens, checking both interfaces and mobile,
+ with functional regression checks proportional to the change. Correct docs only to reflect what
+ has actually been established.
+
+No further UI implementation was performed after the owner requested this handoff.
+
+
+## New-session continuity checklist
+
+- Start the new session in **this same checkout**. All implementation files, reference HTML,
+ screenshots, plans and handoffs are on disk, including untracked files. A fresh clone of HEAD
+ will NOT contain this work; no commit/push was authorized or performed.
+- This handoff plus the linked spec, previous handoff and review records contain the material
+ decisions and continuation context. They are not a verbatim chat transcript. The owner's exact
+ visual objections beyond “not even close” are deliberately marked unknown because they were
+ never supplied; the next session must ask, not reconstruct them from guesses.
+- The latest user instruction is to pause implementation for handoff, then have the next agent
+ launch the current server and ask about the biggest differences. No new design was approved
+ after either rejection. No permission to commit/publish was given.
+- `public/` and `public/data/` are ignored build artifacts. Keep the current feed when starting;
+ if it is absent, use the documented offline feed build, not a network refresh. A changed port
+ is a different browser origin, so it will not automatically carry the owner's saved progress.
+ Do not clear or migrate the owner's browser storage merely to obtain a clean screenshot.
+- Durable reproduction entry: `docs/design-reviews/fidelity-capture.mjs`, with reference fixtures
+ beside its images. Its staged-state limitations are described above. Chrome is at
+ `/usr/bin/google-chrome-stable`; install/use Puppeteer in scratch, not project dependencies.
+- `/tmp/fidelity-http-final.mjs`, `/tmp/fidelity-journeys.mjs`, `/tmp/fidelity-matrix.mjs` and logs
+ were scratch functional checks, not durable requirements. They may still exist, but have fixed
+ ports and scratch imports; do not assume they survive or run unchanged. Standard test commands,
+ checked-in test sources and the recorded browser scenarios are the durable verification contract.
+- Preview/mockup processes may survive: this run used app ports 41902–41908 and mockup41901.
+ Inspect before starting/stopping; never `pkill -f serve.ts`. PID2536610 was the owner preview at
+ recording time, not a permanent identity. Do not treat old port numbers or tool session IDs as
+ proof of the build currently being served.
+- No UI edits were made in the handoff/continuity-check turns. The second-rejection warnings in
+ the older report, plan and design documentation exist to prevent another agent mistaking their
+ earlier positive wording for owner acceptance.
+
+## Session 3 (2026-10-17): owner feedback on new-user setup, and the rebuild
+
+The owner chose to start with new-user setup, against Round 6 (`b-rail.html`, final `onboarding()`).
+Their differences, in their words, summarised:
+
+1. Screen 1: “The margin/padding, sizing and placement of buttons is off.”
+2. Screen 2: “The games are way too big, and not grouped together, not shown with colors, no
+ choose all games instead. The back button does work better though.”
+3. Screen 3: no table to choose events/dailies/weeklies; instead the per-game subscription block
+ was “copy pasted” for every game. “Show events that haven't started” was “a random, flowing
+ button”; in the mockup it sits between dividers with a description.
+4. Screen 4: “a weird checklist-timeline switch, instead of the drawing and descriptions the
+ mockup has.”
+
+What changed: `SetupWizard.tsx` was rewritten as a translation of the rendered mockup markup
+(`.ob`, `.preference-grid`, `.gpick`, `.type-guide`, `.availability`, `.mtx`/`.tg` with a
+“Same for all” row and mixed state, `.upcoming-choice`, `.vcard` previews, `.ob-foot`), and the
+scattered `.setup-wizard` overrides in `redesign.css` were replaced by one block at the end of the
+file translated from the mockup's rules. Game order follows `GAMES`; colours use the app's
+contrast-adjusted `meta`.
+
+Deliberate deviations, for the owner to confirm:
+
+- The Interface select and the “Everything stays in this browser” line were removed from step 1:
+ neither is in the mockup or the spec's setup list. New installs still get Structured, and
+ Interface remains in Settings → Appearance.
+- The back link reads “Back” on every step (the mockup says “Preferences” on step 2), because the
+ owner said the current back button works better.
+- The tracking table's row headers are `
` rather than `
`, for screen readers.
+
+Evidence is functional and side-by-side capture only (1440 and 390, dark, fresh browser, real
+feed, no interception). **Owner acceptance of these screens is still pending.**
+
+### Setup accepted as “much better”; Checklist requests (same session)
+
+The owner then asked, for the Checklist: “Add the time left bar to the compact screen. Make the
+pill menu smaller. Let users sleep/dismiss the event data may be out of date for up to 7 days.”
+
+- Compact rows now show the time-left meter on every row, in a fixed 120px column left of the
+ time (under the time on phones). Standard rows are unchanged: the meter only accompanies a
+ description.
+- Focus pills are smaller (24px high, 11.5px text, tighter gaps), kept at 30px on coarse pointers.
+- The source-age notice offers “Hide for 1 day / 3 days / 7 days”. It stores
+ `prefs.staleSnooze = { until, games }` in the existing permissive prefs envelope (no new storage
+ key). It returns when the time passes, when a game outside the snoozed set goes stale, or if the
+ saved value is malformed or more than seven days ahead. Settings → Source status and the
+ per-event detail notice are not snoozed.
+
+### Checklist follow-up (same session)
+
+Owner: “In compact view, there needs to be some more air, like there is in the mockup compact
+view. In standard view, the space between the bottom of the Running now [heading] and the first
+event in the list is too big. The Next to expire is too small, compared to the easier to read one
+in the mockup, and it doesn't show the next 3 ones to expire, like it does in the mockup.”
+
+- Compact is now a translation of the Round 6 B ledger (`table.led`): column headings Game /
+ Event / Time left / Ends in, a 3px game stripe with the short name, 15px titles, a 34%-wide
+ meter and a 20px condensed time, in 51px rows. It is drawn as a CSS grid over the existing
+ `
` rows (`LedgerColumns`, `ChecklistEvent compact`) so archive fade/confirm transitions keep
+ working. Under 860px the game column hides, the game name moves above the title and the meter
+ spans the row. This replaced the earlier compact `.event-entry` rules entirely.
+- Note on references: Round 6 **B** and Round 7 render but hide the rail's “then” list
+ (`.rail .then{display:none}`); Round 6 **A** shows it with a 30px title and 64px countdown. The
+ owner asked for the mockup's larger rail with the next three, so the rail head now follows A's
+ proportions (52px under 1050px; the phone sizes and hidden list at 700px are unchanged).
+- `.list-heading` bottom padding 22px → 8px (Relaxed 18px → 8px).
+- Relaxed was not visually captured for these changes.
+- Rail routines heading (owner): title is “Routines” with “Showing only pinned” as its subtext;
+ the “Focus on a game to open its routines.” hint was removed as not worth the text (groups still
+ auto-open on focus).
+
+### Timeline (same session)
+
+Owner: the implemented Timeline resembles the mockup well, but lost the liked resemblance to the
+paimon.moe-style board on the live site; keep the new design and add a switch between the looks.
+The scroll-following bar text of the new look is liked. Dailies should not be on the timeline. Add
+a checkbox for time left vs end date. Add a divider where routines start when they have their own
+section, and rename “Separate reset strips” to “Show routines in own section”. Later: in new-user
+setup, when Timeline is chosen, show both designs and ask.
+
+Owner's answers to clarifying questions: default look **New**; switch in the **Timeline controls
+bar**; classic **reproduced as production** (no added scroll-following text); routines in classic
+go in **their own section** under a divider.
+
+What was built:
+
+- `prefs.timelineLook` (`"new"` default | `"classic"`) and `prefs.timelineLabel` (`"left"` default
+ | `"end"`), both in the permissive prefs envelope. The classic look reuses the production board
+ (`components/Timeline.tsx`) with its own By game / Ending soonest, zoom and Jump to today
+ controls; the new look keeps its controls. The look switch sits first in the options bar.
+- Classic gained an optional `routines` section (`ClassicRoutine`): a divider labelled Routines,
+ then per-game lanes of dashed-edge bars in the board's style, clamped to the board's window and
+ with names pinned like its event bars.
+- Dailies (`cadence === "day"`) never appear on either look (`onTimeline`). The Routines page shows
+ a dash instead of a Timeline pin for dailies, and hides bulk “pin to Timeline” on daily-only bands.
+- “Show end dates instead of time left” switches new-look bar text to “Ends 20 Oct” and routine
+ bars to “resets ”. It does not change the classic look, which shows titles only.
+- New-look divider: a dashed rule with a sticky ROUTINES label before each game's routine rows
+ when “Show routines in own section” is on.
+- Setup step 4: choosing Timeline reveals “Which timeline design?” with drawn New / Classic cards;
+ `SetupDraft.timelineLook` is saved only when the reader starts on Timeline.
+
+### Timeline follow-up: shared pill header, look in Settings, focus bar setting
+
+Owner: the options (By game / Ending soonest, routines in own section, end dates) must work in the
+classic look too; the classic board's small pill toggles look better than the big options bar; the
+New/Classic switch belongs in Settings. Then: the focus bar moves under the pill header on the
+Timeline, with a Settings option to show it on the Checklist and/or Timeline.
+
+- `BoardHeader` and `TogglePill` (exported from `components/Timeline.tsx`) are the one header for
+ both looks: stacking pills, “Show routines in own section” and “Show end dates” toggle pills,
+ small −/+ and Jump to today. The old `.timeline-options` bar and its CSS are gone.
+- Classic now honours both options: `routinesInline` (off → routines at the end of each game's
+ lane; on → own section under the divider) and `label` (time left / end date shown inside the
+ pinned name, after the title, shrinking before the title does; omitted on bars under 150px).
+- Settings → Appearance: “Timeline design” (New / Classic) and “Focus bar” with “Show on the
+ Checklist” / “Show on the Timeline” (`prefs.focusBar = { checklist, timeline }`, absent = both).
+ A view whose focus bar is hidden ignores `focusGame`, so a focus chosen elsewhere can never
+ filter a page with no visible way to clear it. `TimelinePage` now receives `focus` and
+ `focusBar` from `ReaderApp` instead of reading `focusGame` itself.
+- The classic board keeps its header and focus bar when it has nothing to plot, and draws the
+ board when only routines exist.
+- Setup copy now points to Settings → Appearance for switching designs.
+
+### Timeline polish (same session)
+
+Owner: focus bar needs one row on large screens and no “Focus” text; By game / Ending soonest
+should share an outline; the two options need an off affordance (checkboxes); routine items could
+not be opened, so progress could not be tracked; the classic NOW chip was almost unreadable; open
+far enough back / zoomed out so most text shows; always prefer the title over time left / end date.
+
+- Header: the stacking pair is one rounded outline with the chosen side filled; the options are
+ small checkboxes (`ToggleOption`, replacing `TogglePill`).
+- Timeline focus row (`.timeline-focus`): pills and Next on one row, heading hidden; wraps on
+ phones.
+- Routines open a modal dialog (`RoutineDetail` in `TimelinePage.tsx`) from both looks: game and
+ server, title, reset time, and the shared `RoutineControl` for progress. It replaced the
+ off-screen `.timeline-routine-detail` section.
+- Opening position (`openingLeft`, both looks; also Jump to today): back to the median running
+ start, with now kept between 25% and 50% across. The default zoom (32px/day) is unchanged;
+ readers who already zoomed keep their saved width.
+- Classic NOW chip: inline critical background and ground-coloured bold text (`.eyebrow` had been
+ overriding the text colour with faint ink).
+- Classic labels: shown only when `labelFits` estimates room for the whole title and the label.
+- **Deferred by the owner:** an option, per routine or for all routines, to show routine dates
+ only without tracking progress.
+- Footer (owner: “sometimes sticky… should not stick and take up space unless you've scrolled all
+ the way down”): the footer was never sticky. The Timeline boards scrolled inside a pane capped at
+ 72vh, so the page bottomed out early and left the footer on screen while the board scrolled.
+ Both panes (`.timeline-scroller`, classic `.scroll-pane`) are now capped at `100dvh`: with the
+ board filling the window the footer is below the fold, and appears only past the board's end.
+
+### NOW chip, server names, labels (same session)
+
+Owner: make “now” even more readable; drop “europe” unless a game has two regions; if title and
+end date cannot both be shown in full, drop the end date rather than truncating it.
+
+- Classic NOW chip: 12px bold display caps, white on a fixed `#d42a47` (≈5:1), 2px ground ring.
+- `state/reader/servers.ts`: `manyServers` / `serverSuffix`. Reading views (Checklist rows, Next to
+ expire, dailies, rail routine groups, Routines table, both Timeline looks, routine dialog,
+ updates, archive list/detail) name a server only when the game has more than one active account
+ (archives also count retired ones). Settings views still name servers, where they are the subject.
+ The “New since you last looked” row now names the game instead of the server.
+- Classic bar names are one-line wrapping rows with overflow hidden: the title truncates only when
+ it alone is too long; labels (and routine progress) never shrink and simply wrap out of view
+ when they do not fit whole. `labelFits` was removed.
+- Header options are now three either/or pill pairs (`PillChoice`, which also draws the stacking
+ pair): By game / Ending soonest, Show time left / Show end dates, Group routines and events / Show
+ routines in own section. `ToggleOption` (checkboxes) was removed.
+
+### Routines view (same session)
+
+Owner: Timeline accepted (“looking good now”). Routines: the Checklist/Timeline pin icons are not
+centred and look odd against the mockup; text does not sit together as in the mockup; keep the
+game colour bars; an always-open editor adds clutter — match the mockup, but show that progress
+is editable and let the reader enter their own.
+
+- The table is now a translation of Round 6 B `table.rt`: quiet 11px headings with the list/bars
+ icons on the pin columns, `.gcell` colour bar + short name (server only when several), 15px/500
+ titles muted when met, `.pin` 34×28 icon buttons (plain when off; near-tinted outline when on),
+ and a `.kebab` “⋯” summary for the existing menu.
+- `RoutineProgress.tsx` replaces `RoutineControl` in this table only: `.tick` (Done / To do, or
+ “n of m days”) and `.amt` (bar + “**2** / 3 runs”, unit from the task or built-in). Amounts read
+ as text; a pencil (brightens, number gets a dashed underline on hover/focus) signals they are
+ editable. Click opens an inline number field + Save; Enter saves, Escape or leaving cancels.
+ `RoutineControl` still serves the Checklist daily panel and the Timeline routine dialog.
+- All `.routine-table`/`.routine-pin`/`.routine-progress`/`.progress-track`/`.rtitle` rules
+ (51, including phone variants) were removed rather than overridden; a single `.rt` block holds
+ the replacement. On phones, hidden Routine/Progress headings keep their cells so pin headings
+ stay aligned.
+- Owner follow-up: pins fade (150ms colour/background/border, skipped under reduced motion), and a
+ pinned pin previews “unpin” on hover (thinner tint, dashed outline). Ticks no longer carry
+ “To do / Done” text; multi-day routines keep “n of m days”, and the aria-label keeps the state.
+
+### Library and create shelves (same session)
+
+Owner: Routines accepted. Library and Create “lack a lot from the mockup… they are shelves in the
+mockup, where they are weird in the live”. Owner chose to add all four missing create fields now
+(Every day, Another hour, Note, Link) rather than defer the data changes.
+
+- `RoutineDrawer.tsx`: one right-docked modal `