Index: src/routes/paid.js
===================================================================
--- src/routes/paid.js	(revision ec288dc84c580f311e1c6cd5900dbd67738f7f62)
+++ src/routes/paid.js	(revision 072a24208b5f98d3ac8b491384e7a63293f98239)
@@ -15,5 +15,4 @@
 import PaidPatreon from '../services/PaidPatreonService.js';
 import Passkey from '../services/PasskeyService.js';
-import { renderPostBodyHtml } from './posts.js';
 
 const router = express.Router();
@@ -128,5 +127,10 @@
   const post = db.prepare("SELECT * FROM posts WHERE site_id = ? AND slug = ? AND status = 'published'").get(r.site.id, String(payload.post || ''));
   if (!post || !post.paid) return res.status(404).json({ error: 'gone' });
-  res.json({ ok: true, title: post.title || '', html: renderPostBodyHtml(r.site, post, req) });
+  // Hand back a short-lived, single-post unlock capability. The client reloads
+  // the real post page with it (?u=), so the post renders through its normal
+  // template: correct layout, scoped styles, working audio. Not a cookie and
+  // not stored: a 120s signed blob that lives only in that one URL.
+  const token = signBlob({ purpose: 'unlocked', siteId: r.site.id, post: post.slug }, 120);
+  res.json({ ok: true, redirect: `${res.locals.siteUrlBase || ''}/${encodeURIComponent(post.slug)}?u=${encodeURIComponent(token)}` });
 });
 
Index: src/routes/posts.js
===================================================================
--- src/routes/posts.js	(revision ec288dc84c580f311e1c6cd5900dbd67738f7f62)
+++ src/routes/posts.js	(revision 072a24208b5f98d3ac8b491384e7a63293f98239)
@@ -22,4 +22,5 @@
 import { premiumUnlocked } from '../services/PatreonService.js';
 import { defaultMinCents as paidDefaultMinCents, patreonUrl as paidPatronUrl } from '../services/PaidPatreonService.js';
+import { verifyBlob } from '../services/CryptoBox.js';
 import MusicMeta from '../services/MusicMeta.js';
 
@@ -1238,5 +1239,11 @@
   // never see the unlock button).
   const canEditThis = req.session?.user && PermissionsService.canEditPost(req.session.user, post, site);
-  if (post.paid && !canEditThis) {
+  // A fresh unlock capability (?u=) from /paid/unlock lets a just-verified
+  // supporter render the FULL post through this normal template (correct layout,
+  // scoped styles, working audio). Short-lived signed blob, single post, not a
+  // cookie and not stored.
+  const _u = req.query.u ? verifyBlob(String(req.query.u)) : null;
+  const _unlocked = _u && _u.purpose === 'unlocked' && _u.siteId === site.id && String(_u.post) === String(post.slug);
+  if (post.paid && !canEditThis && !_unlocked) {
     const { newerPost, olderPost } = postNeighbors(site, post, res.locals.tenancy === 'hub');
     return renderPage(req, res, 'pages/paid-gate', {
