-- 036: per-collective branding — logo, tagline, background, accent. -- -- Owner need: the ACTIVE COLLECTIVE must be unmistakable in the shell, and -- pages/screenshots must look different per workspace. That is presentation -- state of the TENANT itself, so it lives on `workspaces` — not a new table, -- not instance_settings (028 is the deployment's legal posture, one per -- instance; branding is per collective). -- -- One nullable jsonb column, nothing else: -- * logoAssetKey text — storage key (sha256/…) of the collective's mark -- * tagline text — one-line subtitle under the name -- * backgroundKey text — storage key of the shell background image -- * accent text — CSS hex color overriding the palette accent -- * NULL / missing key ⇒ the Hub default for that field -- -- NULL (the default) means "no branding": every existing workspace keeps the -- stock look. The key set is validated at the API seam (branding.service.ts) -- because the values are cross-cutting (storage keys must match real objects, -- accent must be a safe CSS color); the DB only guarantees "object or absent" -- so an undecodable row degrades to defaults instead of failing the shell. -- -- `workspaces` is deliberately NOT under RLS (001): it holds no tenant rows, -- it IS the tenant spine. Reads/writes stay behind the RbacGuard + service -- seam exactly as before this migration. BEGIN; ALTER TABLE workspaces ADD COLUMN branding jsonb CHECK (branding IS NULL OR jsonb_typeof(branding) = 'object'); COMMIT;