Lets a deployer set the in-app color scheme a fresh visitor sees (e.g.
cfaun → darkmatter light) without forking. Two optional brand.json
fields, `theme` (light|dark|system) and `palette` (one of PALETTES),
distinct from `themeColor` which is PWA chrome only.
- vite-branding.ts surfaces them as VITE_BRAND_THEME / VITE_BRAND_PALETTE
at module load, so the default applies app-wide (hub + all standalones)
with no per-config wiring.
- theme-provider reads them as the INITIAL value of theme/palette; a
user's stored choice in localStorage still wins and persists.
- Splits the catppuccin = bare `:root` invariant (now BASE_PALETTE, used
by applyPalette to drop data-theme) from the configurable default.
Without this, a non-catppuccin brand default would strip the
data-theme attribute and silently render catppuccin instead.
Unset → the app's built-ins (dark + catppuccin), unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A brand may ship a wide banner (logo + wordmark in one image) that
replaces the brand-kit logo + app-name pair in a standalone's header.
Events is the first consumer.
Banners are optional and resolve at build time, mirroring the existing
@brand-app-logo chain:
- resolveAppBanner(app?) checks per-standalone override first
(branding/<dep>/icons/<app>/banner.{svg,png}) then the brand's primary
banner (branding/<dep>/banner.{svg,png}); returns null when absent
instead of throwing, so brands without a banner keep logo + name.
- brandAppBannerAliasEntry() always registers the @brand-app-banner
alias (falling back to the logo) so the static import resolves; whether
it renders is gated by the VITE_APP_BANNER build flag.
- EventsPage renders the banner when the flag is set, else logo + name.
Deployers override per-standalone without touching the component. SVG
banners must have their text outlined to paths (browsers lack designer
fonts) — documented in branding/README.md.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>