docs: record round 3 route concept and decisions

This commit is contained in:
2026-10-10 19:06:29 +03:00
parent 7e6f0abbaa
commit da7bd9940e

129
docs/redesign/round3.md Normal file
View File

@@ -0,0 +1,129 @@
# Round 3: The Route
## Concept
Round 2 gave the page one sign language. It still read as one page: eight
identical glass cards laid over a world that never changed. Round 3 makes the
drive a route, with places along it.
- **The world changes per chapter.** A Blender-authored kit of roadside
districts rises out of the ground for each chapter and sinks again behind
you.
- **The page changes its form.** Each chapter opens with its exit tab and a
large heading standing over the road, then a reading panel, then a "next
exit" sign. Open road separates chapters, so the districts can be seen.
- **Navigation is a map of the route.** You can see where you are, which exits
you have passed, and where you stopped last time.
## Changes
### World
- **Roadside districts** (`assets/blender/districts/`, `src/scene/world/districts.ts`).
- Eight original pieces, 5,424 triangles in total. The GLB is 84 KB raw, or
13 KB with brotli.
- The kit is authored by a headless script, `author-districts.py`. It writes
`nightdrive-districts.blend` with its own "Roadside Districts" scene.
- `export-districts.py` writes the GLB and the provenance file.
- Which pieces rise in which chapter:
| Pieces | Chapters |
|---|---|
| Gantries | Profile to Toolkit |
| Billboards | Profile, Career |
| Server rows and radio masts | Infra |
| Workshop and cranes | Builds |
| Container yard | More work |
| Destination arch | Contact |
- Pieces are instanced and wrapped by the same distance shader as the
expressway. Flat normals are derived per fragment, so the GLB carries
positions only.
- Lights take the chapter accent, catch with a neon stutter, and dim close to
the camera.
- Pieces at rest are hidden, so they cost no draw calls.
- The low tier halves the instance counts.
- Shaders compile at the on-ramp, not mid-drive.
### Page
- **Reading stage** (fixed in `71959f7`, before this round's UI work).
- Sticky failed because the viewport used `overflow: clip`.
- The viewport is now a script-scrolled `overflow: hidden` container.
- Browser-initiated inner scrolls (find-in-page, focus) are forwarded to the
document.
- Navigation re-lands before focus if the stage switches on or off mid-journey.
- **Chapter openers** (`PortfolioSection.vue`, `route.css`).
- Each chapter starts with a `clamp(54px, 7.2vw, 124px)` heading over the
road, with a dark halo instead of a plate and an accent bar.
- The panel chrome moves to a new `.chapter-body`.
- Each chapter ends with a `NEXT EXIT 04 / Infrastructure →` sign.
- Phones get the same opener, scaled to the column.
- **Case studies** (≥1100 px).
- Evidence sits beside a sticky media column. This was impossible before the
stage fix.
- Evidence reads as spec rows: a lettered stop, then the text.
- **Destination.**
- A 168 px heading and larger copy and CTA.
- The avatar stands beside the heading.
- The plate reads `END OF ROUTE · 7 OF 8 EXITS · 3 MIN ON THE ROAD`. This
comes from the real visit and is rendered as CSS generated content.
- **Route-map navigation.**
- A road line under the exits fills up to the current one. It changes on
chapter change only, never per scroll.
- Nodes are hollow ahead, filled once passed, and shown as a car marker where
you are.
- On phones the closed bar reads `EXIT 05 · Builds`, and the open list is a
vertical route.
- **Persisted progress** (`useRouteMemory.ts`).
- Exits passed and the last stop are kept in localStorage.
- Storage is read only after mount, and failures are silent.
- A returning visitor gets a "Resume · Exit 05 · Builds →" chip in the hero.
The chip has an aria-label and a CSS label, so the hydrated text is
identical to the server document.
- **Keyboard.** Alt+↓ and Alt+↑ go to the next and previous exit.
## Decisions
| Brief item | Decision | Why |
|---|---|---|
| Per-chapter Blender props and districts | Adopted | The biggest missing piece: the world now tells you which chapter you are in. |
| Gantries | Adopted | They are part of the district kit, and their height clears the chase camera. |
| Better sign geometry | Adapted | Round 2's sign faces read well at 1280, 1440 and 1920. The budget went to the districts instead. |
| Atmosphere and time progression | Kept | The chapter palettes already run from deep night to pre-dawn. District accents now follow them. |
| Rain or wet road | Rejected | A new full-screen pass plus reflections costs a GPU tier, and it fights the synthwave identity. |
| Better hero car lighting | Deferred | The car GLB was not touched this round (see the Blender note below). |
| Draco or meshopt | Rejected | The districts are 13 KB brotli, and the decoder would cost more than it saves. |
| Reading panels and typography | Adopted | Chapter openers, `.chapter-body` panels and next-exit signs. |
| Case studies | Adapted | Sticky two-column at ≥1100 px. Long galleries stay in normal flow when taller than the window. |
| Route map or minimap | Adopted | The navigation itself is the map. No extra widget, and no per-scroll work. |
| Scroll choreography | Adapted | Rising districts and the exit-tab strike carry it. The view-timeline reveals stay off inside the live stage to avoid fighting the reading mask. |
| Mobile first-class | Adapted | Openers, the route bar and the vertical route list on phones. No horizontal scroll at 320 or 390 px. |
| Contact as a destination and a strong ending | Adopted | The arch district, the giant heading and the trip log. |
| Sticky layout | Fixed | See "Reading stage" above. |
| Keyboard journey | Adapted | Alt+arrows, not digits or `[` `]`: single-character shortcuts fail WCAG 2.1.4 without a way to turn them off. |
| Deep links | Kept | Hash links already land correctly and are now more robust. |
| Persisted progress | Adopted | localStorage, client-only. |
| Honest loading states | Kept | Districts stream in silently. A failed fetch leaves the old road. |
| Sound coupling | Deferred | Round 2's music pulse already couples the world to the soundtrack. A pass-by on exit arrival needed audio design time this round did not have. |
| Stylesheet consolidation | Deferred | `route.css` is one new file. Merging the eleven existing files safely needs a cascade-order audit, and its risk is out of proportion to its value. |
## Blender note
An early attempt drove the user's live Blender session through MCP. Calling
`bpy.data.libraries.write` with a scene crashed that Blender. The open file
reported no unsaved changes beforehand. The crash log is at
`/tmp/nightdrive-grand-tourer.crash.txt`.
All later work ran headless (`blender -b --factory-startup`). The author script
refuses to run anywhere else.
## Run
```sh
source ~/.nvm/nvm.sh && nvm use 22.22.1
npm run build:preview # budgets: "violations": []
python3 -m http.server 5175 -d dist
blender -b --factory-startup --python assets/blender/districts/author-districts.py
blender -b assets/blender/districts/nightdrive-districts.blend --python assets/blender/districts/export-districts.py
```