feat: viewer role + artists directory + Klonkt Hub Beta rebrand
Viewer role (replaces the separate view-mode/readonly toggle):
- 'kijker' is now a real role (VALID_ROLES) selectable in Admin.
May view EVERYTHING including Admin, but cannot modify anything.
- isViewer(user) (= role kijker or legacy readonly flag) is the source;
requireGod/requireSiteManager(BySlug) let a viewer through (viewing),
the global guard 403s every write. Existing readonly accounts are
migrated on each role change (readonly=0).
- Write leaks via GET patched: /prutter/new (INSERT) blocks for viewers,
/prutter/:id skips markAsRead (UPDATE); WS upgrade rejects viewers
(the HTTP guard doesn't cover WebSockets).
- Clean "Viewer mode" page (viewer-blocked) instead of raw 403 text;
styled sticky banner; account page shows read-only UI instead of an
upload button that silently 403s. canMutate hides write buttons.
Scalability (>50 artists):
- Hub home shows max 24 (most active first) + "All N artists ->".
- New searchable, paginated /artiesten directory (hub only).
- 'user' + 'artiesten' reserved as slugs.
Rebrand PrutFolio v1 -> Klonkt Hub Beta (footer, PWA manifest, account/
admin texts, default page title, startup, README; internal package +
PWA id 'prutfolio' remain for stability).
Co-Authored-By: Claude <noreply@…>