The host marks the answering team "Got it" or "Wrong" (G / W). Wrong
passes the turn to the next team in the buzz order; Got it closes the
buzzers until "Next question" (B). Undo reverses either mark.
In a direct game a correct answer adds 1 to the team's score in the
game's first criterion, as an ordinary admin score row, so it flows
through the scoring engine (multiplier, placement) and the existing
range/step trigger. The point is written before the mark, so a refused
point never leaves a "correct" with nothing behind it. Judged games
only get the mark: the judges' scores stand.
Needs supabase/migrations/0004_buzz_result.sql applied before deploying.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each team gets a buzzer link (/b/[token]); a game opts in with a
"Teams buzz in" checkbox. The database's clock orders the buzzes, so
the result doesn't depend on phone clocks. The control room lists the
order and clears it per question (B); the scoreboard shows who buzzed
first with a ding.
Buzzes follow the existing rules: token-gated route handlers with the
service-role client, RLS on the new table, and data-free realtime pings
on a new buzzer:{eventId} channel.
Needs supabase/migrations/0003_buzzer.sql applied before deploying.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Timer: one per event, set from the control room (duration, presets,
start/pause/resume/reset, show/hide, T shortcut). Rules are pure
functions in src/lib/timer.ts; the server's clock sets the end time and
each screen counts down locally, corrected for its own clock.
Needs supabase/migrations/0002_timer.sql.
- Sound effects on the scoreboard, synthesised with Web Audio (no files):
drum-roll + crash per revealed team, fanfare for first place, a chime
on game change, ticks for the last 10 seconds and a buzzer at zero.
Triggers are pure functions comparing consecutive payloads.
- 'Turn sound on' button on the scoreboard, since browsers block audio
until a click; the choice is remembered across reloads.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Setup: nominate judges, copy personal links, QR codes (generated on
demand in the browser), revoke/restore, and an open-link toggle.
- GET /api/judge/[token] returns only that judge's own scores; PUT
.../scores runs every check from ARCHITECTURE.md in checkJudgeWrite
(pure, unit-tested) after one parallel lookup, plus a rate limit.
- /j/[token]: mobile cards per team, spotlight pinned first, autosave
with per-card status, read-only when locked/done, link-off when revoked.
- /jo/[token]: name form -> personal link, remembered in localStorage so
a re-scan doesn't create a second judge.
- Control room: averages with on-request overrides, judge progress
matrix, per-judge breakdown, spotlight select and lock toggle.
- usePendingEdits shares the optimistic autosave logic between the admin
grid and the judge page.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- buildScoreboardPayload is the single place deciding what the public may
see: visible, frozen (snapshot scores, live current game), hidden (no
scores, alphabetical) and reveal (unrevealed teams carry no score/order).
- GET /api/public/scoreboard/[token] validates the token, then loads the
event with the service client in one parallel round trip.
- notify() pings sb/admin/game channels over Realtime's REST endpoint with
no data; useRefreshChannel refetches immediately on a ping, throttles
bursts, and falls back to a 10s poll, focus and reconnect.
- /s/[token]: FLIP rank animation, count-up totals, reduced motion,
fullscreen, cursor hide and a connection indicator.
- Admin setup actions now notify, so renames and reorders show up live.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- /login sends a Supabase magic link (admin emails only); /auth/confirm
completes sign-in; proxy.ts guards /admin and every server action
re-checks with requireAdmin(), since actions are plain POST endpoints.
- /admin lists, creates, duplicates and deletes events.
- /admin/events/[eventId] edits teams (colour, order) and games with
criteria, presets and notes. All input goes through zod schemas that
mirror the database constraints so errors read as plain sentences.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Next.js 16 App Router with strict TypeScript, Tailwind 4 theme tokens from
docs/UI.md, Vitest, and the three Supabase clients. Env vars are validated
with zod in next.config.ts so dev and build fail fast with a readable error.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>