The dashboard is now a single-screen app shell:
- app-bar page tabs (Model + placeholder Explainer/Findings/Guides) via
lightweight hash routing (usePage); the wordmark links home
- a 2x2 graph grid that fills the viewport, rows sized to half its height so
the four cells always show; a + Add tile is the 4th cell, a 5th scenario
scrolls (with a reserved scrollbar gutter)
- a left settings sidebar exposing the forwarding composite as grouped levers
(novelty, harm awareness, program strength, plus baseline forwarding and
education budget); the card is a CSS container so the sliders stack to width
- a full-width dock (legend, full-width player, Export / Reach over time /
Data table); the reach chart and data table open as overlays
- the illustrative note moved under the hero
ScenarioToolbar is removed (the + tile and the dock Export replace it).
Deviates from the original ui-spec, which is amended with a pointer.
The store fetches {config, bounds} at startup and snaps values into
range everywhere they enter: typed fields, sliders, panel overrides,
and shared links (a ?edgesPerNode=150 link now runs at 8 instead of
showing a validation error). Shrinking numStudents pulls numEducated
and origin down with it. If the bounds fetch fails the app still
starts and the engine's validation backstops. Native min/max
attributes follow the same numbers, so spinners stop at the bounds.
The lower grid completes: reach chart left, World card right. Two
headline levers (chance to forward, education budget as count plus
share) move instantly and rerun through the store's debounce; the
advanced disclosure holds students, friends per student, clique
tendency, first poster, and the three named seeds, each seed with a
dice reroll plus a Reroll world button (one coalesced run).
Engine 400s render inline under the card header in rose with the
offending field marked by name match; network failures keep using the
banner with retry. A graph-seed reroll remounts the network svg keyed
by graph hash, fading the new layout in over 200 ms.