Principles
- One story per screen. Every screen has a protagonist; everything else supports it or leaves.
- The scenario is the product. The four futures are clickable in exactly one place per page, and that control is the legend for every chart.
- Layouts resolve. Lists for variable counts, grids only for fixed ones. No orphan cells, no accidental voids.
- Hierarchy through scale, not caps. Real type jumps, sentence case; tracked caps live in exactly one tier (the eyebrow).
- Motion confirms, never gates. Springs attached to intent — press, switch, arrive. Content is always readable; prefers-reduced-motion collapses everything to instant.
- Data carries its own why. Every chart states what its future means in words; every threshold states the consequence of crossing it.
Color
Four jobs, strictly separated: interaction, scenario identity, system identity, and semantic deltas. A hue never moonlights.
Type
Space Grotesk for display, Inter for text, JetBrains Mono for every number. Sentence case everywhere; tracked caps in one tier only.
Motion
Powered by motion.dev (vanilla build, CDN — the three.js precedent), with a hand-rolled spring integrator for number counting and as full fallback. Everything below is live — try it.
- Press physics — every interactive element squishes to 0.962 under the pointer and springs back with overshoot (stiffness 430, damping 16). Delegated, so re-rendered content keeps it.
- Emphasis pop — the element that just gained meaning (chosen segment's dot, newly-active landing dot, a threshold chip that changed state) overshoots ~1.25–1.7× and settles.
- Morph — the bold chart line reshapes point-by-point between futures (460ms ease-out); the area fill retints; the end value counts.
- Glide — indicators (landing bubble, score-band dot) travel on the shared spring easing, compiled to CSS linear().
- Entrance — sections rise 20px on an inView spring (stiffness 130, damping 17), staggered ~55ms by sibling, preloaded 18% below the viewport. One-shot; never re-hides.
- Count — numbers arrive via the spring integrator (stiffness 170, damping 26), never fade-swap.
Data visualization — one geometry, six forms
Every chart shares one anatomy: hairline grid, mono axis labels in ink-4, no borders, no per-chart legends — the scenario control is the legend. The form is chosen by the data's job. All live below.
- Emphasis, not category soup. The chosen future is bold with an end value; the other three are ghosts with name labels. Identity is never color-alone.
- Scenario hues only on scenario identity. Single-measure charts use one neutral hue; semantic green/red only on deltas, always with ▲▼.
- One tooltip. Crosshair + year label + all four values, sorted, active row emphasized — identical on every chart.
- End labels own the right rail. Axis ticks yield to them; collision chains stay inside the plot.
Components
The scenario control (live — it drives this page too), and the rule that governs it.
The one-control rule: the four futures are clickable in exactly one place per page. Pages with their own scenario instrument (Pathways) suppress the deck's segment via deck.noSeg. Cards that reflect the choice are display-only.
Hover a group in the top bar to open it; the menus are the site's only disclosure component, so their behavior is specified tightly:
- Exactly one menu is open, ever. Opening one always closes the other first. A single state variable in v3.js owns visibility — hover, click, and keyboard focus all route through it.
- Never open a menu from CSS :focus-within. A clicked trigger keeps focus, so a focus-based rule holds that menu open while hover opens a second — two menus at once. This was a real bug; the CSS carries a comment so it isn't reintroduced.
- Open instantly, switch instantly, close on a 160ms grace. Sweeping the bar leaves no trail; a stray pointer exit doesn't kill the menu you were reaching for. An invisible bridge spans the trigger→menu gap.
- Every gesture closes it: hovering a menu-less item, clicking the trigger again, Escape, pointer-down anywhere outside, or tabbing out.
Stat tiles · landing tracks · trajectory cards · threshold cards · milestone rail · composition stack · income-bridge calculator · impact rows · ledger — all specified by their live use: see Climate (thresholds, nine metrics), AI (risks, milestones), Governance (boards, stack, audit), Transition (bridge calculator), Pathways (route instrument, slopes).
Voice
Using the system
- Files: css/v3.css (tokens + components) · css/v3-bridge.css (translates the original design's class vocabulary — for long-form docs like the paper) · css/v3-instrument.css (HUD skin for the full-screen 3D instruments) · js/v3.js (chrome, scenario store, motion, chart kit) · js/v3-data.js (machine-extracted page data — regenerate, don't hand-edit) · motion.dev UMD from jsdelivr before v3.js.
- Every page belongs to one of three tiers: native (v3.css + v3.js — all content pages), bridged (v3.css + v3-bridge.css, for documents written against the old vocabulary), or instrument (own canvas UI + v3-instrument.css). Nothing loads the retired style.css.
- Scenario contract: default is always Business as usual; a user's choice persists sitewide via aicivsim-scenario + URL hash. Theme via aicivsim-theme.
- Every consumer degrades gracefully: no Motion CDN → spring fallback; no live-data network → baked baselines; no JS → content still readable (reveals are JS-gated).
- Cache rule: bump the ?v= token on every deploy-bound change — same golden rule as the live site.