The spreadsheet — mechanics

The fork in this look

Row 2 is the fork: any printable key, =, / or the black = TYPE cell starts a formula in the frozen bar; a wheel, swipe or ↓ SCROLL reads on. SCROLL, bold on grey, is the resting state: complete without JavaScript, indexable, free. TYPE is black, the mind's colour, and each Enter costs a model turn, about a cent.

Enter from the hero switches on the sheet's own filter: programme rows no answer cites hide behind the heavy gutter line the phone lineup filter already draws. One 400 ms startViewTransition lifts the typed words into the new block's 40 px heading as the blocks below fold into that line. [ clear filter ] returns the full site, unfolding them onto the answer's source rows inside marching ants.

The standard route

Asking the mind

Plain words become =MIND("…"); a selection becomes its second argument, =MIND("which are about AI", A147:L149), and its ids lead the page note, so the context sent is visible. Typing dims unmatched rows locally (the mockup's route()).

Thinking is recalculation. On phase: thinking a numbered block inserts under the last answer, its caption cell reads #BUSY! under the mind's flag, and the status bar counts "Calculating… 1.4 s". say types over #BUSY! at about 25 ms a character, never half a second behind the stream. Each ui event is Point mode: ants circle the cited rows wherever they show, then the cursor pastes copies; done reconciles and writes the footnote.

Components copy the sheet's rows by id (data-sid, data-pid):

Fallbacks are the sheet's error values, each with a one-line note: #N/A beside the nearest real rows (not_in_snapshot, or a partial answer's gap); #NAME? off topic; #BLOCKED! for a block or refusal; #CALC! with [ recalculate ] when truncated or invalid; #CONNECT! when the proxy is down, route() answering from the rows and saying so. Failed turns append nothing to the history. Older blocks group under the gutter's outline +.

The assistant in the chrome

Data, performance and accessibility

The fixture holds every printed fact: 50 sessions (3 described, no rooms), 77 speakers (59 photos, 36 bios), four tiers, the unannounced venue, 8 FAQ, 39 sponsors, placeholder flags; faces.json holds crop landmarks. Ticket, place, FAQ and partner rows need ids. WordPress must expose the hidden title, organisation and TLOY fields and add an Event record (venue, hotel, coordinates, answer 01's question); Whova must supply the ETS27 programme, rooms and descriptions.

Motion: the 2.3 s intro once per session, skippable by any key; at rest only caret, ants, live cell and clock, paused when hidden; per answer a 400 ms transition and a paste capped at 1.2 s. Bake 2x crops; stop scaling hotlinked Whova portraits. Reduced motion: finished states, static ants, no live loop.

Keyboard: = and / need the prototype's [ shortcuts off ] (WCAG 2.1.4); arrows move the selection, Shift extends it into the range argument; column labels become buttons. Screen readers: blocks become <table>s with captions, the title an <h1>, the live region only the mode cell (today it wraps the ticking clock); answers announce once, aria-busy while streaming. EEA 2 keys (4.24:1), #N/A text (3.63:1) and gutter ink (3.84:1) fail AA: use #E2433A, #B8261E, #5D5D58.

Build plan

Carries over: server.py (SSE contract, sanitizer, cache, fallbacks, budget), taught to serve this folder and take page on standard turns; the fixture and gen.py; the seven-component catalogue; the renderer spine (streamAnswer, reconcile, offlineAnswer) and the mockup's typeBlock, renumber, sortBy.

New: rows.json from gen.py (a row per record id); speaker_card, session_card, agenda_day, faq, sources, coverage; filter, error values, note, range argument, table semantics; answer 01 recalculated at publish and seeded as each session's first exchange. About three front-end weeks beyond the shared Next.js engine, plus a data week.

Risks