Index: src/middleware/render.js
===================================================================
--- src/middleware/render.js	(revision 746d98a771aa1e4a4241958d88582d516570cf4b)
+++ src/middleware/render.js	(revision 495b0191f40d1e9e9de36d8ce98cad627ffb13cb)
@@ -21,4 +21,5 @@
 import { emojiHtml, emojiName, parseQuote } from '../services/NoteRender.js';
 import ActivityPubService from '../services/ActivityPubService.js';
+import ThemeService from '../services/ThemeService.js';
 import { imgProxyUrl } from '../services/ThumbnailService.js';
 import { audioEnabled as audioFeatureEnabled } from '../config/features.js';
@@ -187,4 +188,16 @@
     defaultLang: getSetting('default_lang'),
   });
+
+  // Lay-out (experimental/theme): wie de site beheert, mag met ?layout= een
+  // ander thema voorproeven; een bezoeker kiest niets.
+  const _canManageSite = !!(_u && (_role === 'god' || _role === 'admin' || userOwnsSite));
+  const _isAdminPage = String(viewName || '').startsWith('pages/admin')
+    || String(data.bodyClass || '').split(/\s+/).includes('on-admin');
+  const _preview = typeof req.query.layout === 'string' ? req.query.layout : null;
+  const _layout = ThemeService.resolveLayout(_site, { isAdminPage: _isAdminPage, canManage: _canManageSite, preview: _preview });
+  const _siteLayout = _site && _site.layout ? ThemeService.getLayout(_site.layout) : null;
+  const _previewLayout = (_canManageSite && _preview) ? ThemeService.getLayout(_preview) : null;
+  const _layoutSheets = [...new Set([_siteLayout, _previewLayout].filter(Boolean)
+    .map((m) => ThemeService.layoutStylesheetUrl(m)))];
 
   // Common locals
@@ -252,4 +265,13 @@
     appVersion: APP_VERSION,
     bodyClass: data.bodyClass || 'on-home',
+    // De lay-out van deze pagina (experimental/theme). null = de gewone kop.
+    // Beheerpagina's altijd de gewone kop; ?layout= alleen voor wie de site
+    // beheert (zie ThemeService.resolveLayout).
+    layoutTheme: _layout ? { id: _layout.id, name: _layout.name } : null,
+    // De stylesheets die mee moeten: die van de site en, bij een voorproefje,
+    // die van het voorproefje. Ze staan er ook op een pagina die zelf geen
+    // lay-out draagt (beheer): hun regels gelden alleen onder data-layout, en
+    // wie vanuit het beheer naar de site navigeert heeft ze dan al.
+    layoutSheets: _layoutSheets,
     // Welke module(s) deze pagina nodig heeft (shaer-bqr). De shell zet ze op
     // body[data-js]; de bootstrap daar importeert ze uit /assets/js/mod/.
@@ -299,5 +321,5 @@
       const _navJs = ('chrome ' + (locals.pageJs || '')).trim();
       const triggerJson = JSON.stringify({
-        pcmsNav: { bodyClass: locals.bodyClass, accent: _navAccent, palette: _navPalette, js: _navJs },
+        pcmsNav: { bodyClass: locals.bodyClass, accent: _navAccent, palette: _navPalette, js: _navJs, layout: locals.layoutTheme ? locals.layoutTheme.id : 'top' },
         pcmsPostSwap: data.post ? {
           title: data.post.title,
