feat(admin): analytics rollups, threaded comments, email replies and giveaway picker
docsuiapienhancefeat
## Summary
Highlights
- **Rollups:** each day older than 30 days is folded into daily_stats (counts) and daily_sketches (HyperLogLog distinct visitors, ~1 KB per day). That day's page views, key copies, downloads and the sessions they leave empty are then deleted.
- **Never deleted:** reports, comments, suggestions, contact messages, and social / promo / affiliate clicks.
- **Visitors:** one-time visitors with no contributions are pruned after the same 30 days, and tallied per tier so tier totals don't change.
- **Readers:** the analytics summary, timeline, campaign reach, program stats and visitor tiers combine rollups with the raw remainder.
- **Accuracy:** checked against a full copy of production. Whole-day ranges match exactly. Unique visitors over rolled days are estimates (~2–4%). Rolled days count whole, so a range reaching past 30 days can be off by up to half a day at either end.
- **Schedule:** runs inside the daily prune-cron-runs cron. Event timestamps more than a day old are now clamped to now.
Database
- **Already applied to production:** 20261107000000_analytics_rollups.sql and 20261107010000_giveaway_draws.sql, plus the Halloween 2026 giveaway (28 entrants, 12 winners).
- **Rollup has not run yet** (rolled_until is still 1970-01-01). The first prune cron after merge folds and deletes everything older than 30 days.
- **Afterwards, run once in the SQL editor:** vacuum full analytics.events, analytics.sessions, analytics.visitors; so the reported database size drops.
