Files
Portfolio/docs/redesign/round3.md

7.0 KiB

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

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