Address the Copilot review on #26.
#1 The game_start / art_style_select payload fields were typed as bare
`string`, so free text could still slip through despite the "content-free
by construction" claim. Add lib/options.ts as the single source of truth
for the selector option sets (`as const` → literal-union types), have the
home OPTS render from those arrays, and type the analytics fields from the
derived unions (gender/art_style/plot_style/pacing/style) plus a template
type for `card`. Free text now fails to compile; no casts at call sites.
#2 The /play heartbeat scheduled its 30s interval unconditionally. Gate the
effect on the same NEXT_PUBLIC_UMAMI_* env used for script injection, so
nothing is scheduled when the tracker is off (visibility check kept — a
hidden tab still never emits).
#3 choice_select no longer emits a -1 choice_index: skip the event when the
index can't be resolved instead of polluting the index distribution.
Verified with tsc (exit 0) and a throwaway negative test: free text in any
of the six fields raises TS2322, valid enum/template values compile.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Instrument the play flow with 9 content-free custom events (game_start,
art_style_select, style_image_upload, scene_reached, choice_select,
vision_click, tts_toggle, fullscreen_toggle, play_heartbeat) to measure
retention, engagement depth and session duration.
Privacy is enforced by construction, not convention:
- lib/analytics.ts types each event with a discriminated union, so a
payload has no slot for free text — prompts, world guides, uploaded
images and vision output can never reach analytics (compile-time
guarantee, not a comment).
- track() no-ops without window.umami and never throws into the app.
- coarse 30s heartbeat fires only while the tab is visible.
- script stays gated on NEXT_PUBLIC_UMAMI_* env (blank → no script),
honours Do-Not-Track, and locks to an exact data-domains allowlist.
- one-line on-site disclosure with a link, shown only when tracking is on.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>