The duck curve — mechanics
The fork in this look
The fork is two verbs on one baseline. A wheel, swipe or Space is "↓ scroll": the stage compresses, the sun walks and the whole day lays out in time order. A letter typed on the poster lands in the "ask" rule; / focuses it anywhere.
Asking first keeps the day unlaid (client-side; the server sends it whole): only the answer appears under the band, at its hour, until "↓ lay out the day" or an hour-word fills in the rest. That first ask is one 700 ms view transition: the horizon flattens into the band and the 240 px sun shrinks to 40 px riding the ridge to the answer's hour, on keyframes sampled from the curve. Unsupported browsers jump.
Scroll is free and indexed, 9,800 px with 58 portraits; ask spends a turn (about $0.006–0.012 warm) for one screen.
The standard route
The five hour-words are the nav (theme 08:15, lineup 12:20, agenda 14:30, tickets 19:00, place 20:09), each also a static route under a 24 vh band, the sun parked at its hour. Partners, missing from the mock, take 21:12 as names by tier on the night sky. FAQ answers sit open at their section's hour.
The stairs keep ids d1–d4, so /agenda/#d2 is Tuesday; rows become aria-expanded buttons. Crowd discs link to /lineup/<slug>/. Tickets stay at the peak: two phase-1 prices at display size, one Eventbrite link.
Asking the mind
The rule posts {message, mode, page} to /api/mind: interactive while the day is unlaid, standard with on-screen ids once laid.
Thinking empties the sun: on send the disc becomes a 1.5 px ring, lit records return to ink and say streams beside it. When the first component closes, the sun refills, carries the words to the answer's hour and lights only the answer's records until the visitor scrolls away or presses Escape.
The renderer, never the model, sets that hour from the first component: a session's or day's start, a speaker's first time on stage, tickets 19:00, venue 20:09, partners 21:12, an FAQ its section's. Answers are numbered in ask order, placed in time order; the house answer 01 yields to the visitor's first.
- Text: the question as the italic display heading,
sayin italic 300;faqin roman, the site's voice. - Speakers: lit discs with names;
speaker_cardadds the bio (36 of 77) and stair lines. - Sessions: answer 01's rows;
session_cardadds description (3 of 50) and sponsors. - Agenda day: one stair on the clock axis; a track filter drops the rest to hairline.
- Venue: "Austin" at sunset, status from data.
- Tickets: the peak's grammar, prices from data.
- Choices: up to four verbs on the answer's rule, replacing the band's suggestions.
- Sources: the mono footnote; the sample flag is renderer-owned.
- Fallbacks: text-only and off-topic answers stay parked, sun unmoved; errors leave the ring empty beside "the day still reads ↓"; without the proxy, the mock's keyword router runs as a labelled demo; without JavaScript, the rule is a section anchor.
The assistant in the chrome
The composer is a 1 px rule, never a box: on the poster, at the band's base, in the footer as "02 · ask", and on phones, where the band drops it, a fixed bottom rule.
The edge-parked card is the readout beside the sun: hour, gigawatts and gloss at rest, then "reading", the streaming say and "02 · on the page ↓" ("[ go ]" if the visitor scrolled meanwhile). The band's bottom-right corner holds the thread: numbers and hours as links, then [ clear ].
The footer state line, the polite status region, adds "mind reading · answered 02" and "curve live" or "baked"; the readout is aria-hidden.
Data, performance and accessibility
The fixture has 50 timed sessions with people, 77 speakers (58 portraits), four real ticket tiers, 39 partners and eight FAQ; the concept adds the baked ERCOT day and sun times.
WordPress and Whova must add rooms (all null), descriptions, 19 portraits, title and organization via show_in_rest, venue and hotel, real themes (tracks are invented) and one-colour SVG logos (all 39 are JPEGs). New: a daily job rebuilding yesterday's curve from ERCOT's free registered Public API (5-minute wind and solar actuals), the baked day as fallback, kept out of the prompt so the cache holds.
Motion budget: one --sx/--sy write per frame; light in five-minute steps on the stage and body, not :root; readout rects cached; baked ink and colour portraits, not 58 blend layers; without scroll timelines a JS class keeps the band. Reduced motion: no transition or travel, motion toggle off. Escape stops or clears; focus stays in the rule; screen readers get the index, not 77 positioned discs.
Contrast, by the page's own light: text falls below 4.5:1 from 19:42 to 20:00 (19:54: 3.2:1 body, 2.4:1 labels), as the Eventbrite link passes. Neither ink nor limestone passes between OKLab L 0.52 and 0.59, so the light must step across that band at dusk. Orange text is 2.4:1: lit sessions keep ink titles and take an orange mark.
Build plan
Carries over from prototype/: server.py (SSE, scanner, sanitizer, sessions, guards) with its seven components widened to the brief's catalogue plus sources and coverage; the fixture and builder; mind.html's renderer spine (render by id, reconcile, error copy, demo); standard.html's merge rules, re-aimed: filter lights, reveal moves the sun, materialize inserts at the hour. From the concept: build_duck.py's geometry, duck.js's hour model.
New: hour placement, lighting, transition, unlaid day, card and thread, phone rule, curve job: about four engineer-weeks of front end, two days for the curve.
Risks
Four days share one clock: Monday and Tuesday 09:00 coincide, so labels name the day. Answers can land above the reader, the sun running backwards; the thread keeps it legible. Glosses fixed by hour mislabel a live day without a belly: compute them or freeze the line.