Grew /admin/theme from colours-only to a full theme editor, all applied
live via website_settings + ThemeVars:
- Typography: body font (10 web-safe + Google options; Google fonts load
via an injected <link>) and H1/H2/H3 sizes (globals.css now reads
--size-heading-* vars).
- Buttons & links: secondary/danger button colours + link/link-hover.
- Custom CSS: a raw textarea injected after the theme variables (staff-
trusted), for anything the controls don't cover.
- Presets: 6 → 13 (added Galaxy, Royal, Cyberpunk, Neon, Coffee, Arctic,
Christmas). ThemeVars now injects all the new vars + the font link.
Verified live (prod, amx_test): saved font=mono / H1=44px / custom CSS →
the public home reflected --font-family "Courier New", --size-heading-h1
44px and the injected rule; reverted the test settings. tsc 0,
vitest 49/49, next build 0.
- New /admin/theme: recolour the whole site from housekeeping. 6 atom-
faithful presets (Atom/Midnight/Ocean/Forest/Sunset/Candy) + per-colour
pickers for the 12 settings ThemeVars injects + border radius. Writes to
website_settings, busts the siteSettings cache, and revalidates the
layout so the new palette applies live with no rebuild. Constants live
in src/lib/theme-presets.ts (a "use server" file can't export objects).
Added to the admin sidebar (System).
- radio/contests/[id] + giveaways/[id] wrapped in ContentCard to match
the public design system.
- Skipped a separate VPN page: /admin/ip already manages the IP
white/blacklist, so it would only duplicate it.
Verified live (prod, amx_test): applied the Ocean preset → home renders
--color-primary #0ea5e9 site-wide; reverted the test rows. tsc 0,
vitest 49/49, next build 0.