Index: src/views/pages/post-edit.ejs
===================================================================
--- src/views/pages/post-edit.ejs	(revision aa45208c239be20a3667852f4cc17c43e78c4394)
+++ src/views/pages/post-edit.ejs	(revision 837fc9c1dc50db6b6a042e84da63ed9989edf2fa)
@@ -237,4 +237,8 @@
           <input type="checkbox" name="noindex" value="1" <%= post.noindex ? 'checked' : '' %>>
           <span>🚫 <%= t('pedit.noindex_label') %></span>
+        </label>
+        <label class="pe-checkbox">
+          <input type="checkbox" name="nsfw" value="1" <%= post.nsfw ? 'checked' : '' %>>
+          <span>🔞 <%= t('pedit.nsfw_label') %></span>
         </label>
         <% if (typeof premiumUnlocked === 'undefined' || premiumUnlocked) { %>
Index: src/views/pages/post.ejs
===================================================================
--- src/views/pages/post.ejs	(revision aa45208c239be20a3667852f4cc17c43e78c4394)
+++ src/views/pages/post.ejs	(revision 837fc9c1dc50db6b6a042e84da63ed9989edf2fa)
@@ -5,8 +5,16 @@
 const _base = (typeof siteUrlBase !== 'undefined' && siteUrlBase) ? siteUrlBase : '';
 %>
-<article class="container post-page">
+<article class="container post-page<%= post.nsfw ? ' nsfw-gate' : '' %>">
 
   <%# Navigation across ALL posts — ABOVE the post (shared partial). %>
   <%- include('../partials/post-nav', { newerPost: newerPost, olderPost: olderPost }) %>
+
+  <% if (post.nsfw) { %>
+    <div class="nsfw-banner">
+      <span class="nsfw-banner-icon">🔞</span>
+      <span class="nsfw-banner-text"><%= t('post.nsfw_warning') %></span>
+      <button type="button" class="nsfw-banner-btn nsfw-reveal"><%= t('post.nsfw_show') %></button>
+    </div>
+  <% } %>
 
   <% if (post.cover_image_url) { %>
Index: src/views/partials/post-card.ejs
===================================================================
--- src/views/partials/post-card.ejs	(revision aa45208c239be20a3667852f4cc17c43e78c4394)
+++ src/views/partials/post-card.ejs	(revision 837fc9c1dc50db6b6a042e84da63ed9989edf2fa)
@@ -46,8 +46,9 @@
 
   <% if (_hasCover) { %>
-    <a class="post-list-cover" href="<%= _href %>"
+    <a class="post-list-cover<%= post.nsfw ? ' nsfw-media' : '' %>" href="<%= _href %>"
        <% if (_ext) { %>target="_blank" rel="noopener"<% } else { %>hx-get="<%= _href %>?partial=1" hx-target="#pcms-main" hx-swap="innerHTML" hx-push-url="<%= _href %>" hx-indicator="#pcms-loading"<% } %>
        aria-label="<%= post.title || '(zonder titel)' %>" tabindex="-1">
       <img src="<%= post.cover_image_url %>" alt="" loading="lazy" decoding="async">
+      <% if (post.nsfw) { %><span class="nsfw-veil"><span class="nsfw-veil-label">🔞 <%= t('post.nsfw_warning') %></span><span class="nsfw-veil-btn"><%= t('post.nsfw_show') %></span></span><% } %>
     </a>
   <% } %>
Index: src/views/partials/post-tile.ejs
===================================================================
--- src/views/partials/post-tile.ejs	(revision aa45208c239be20a3667852f4cc17c43e78c4394)
+++ src/views/partials/post-tile.ejs	(revision 837fc9c1dc50db6b6a042e84da63ed9989edf2fa)
@@ -14,5 +14,5 @@
 const _ext = !!_external;
 %>
-<a class="grid-tile<%= _hasCover ? '' : ' grid-tile-gradient' %><%= (_isPinned || _isBoost) ? ' is-pinned' : '' %>"
+<a class="grid-tile<%= _hasCover ? '' : ' grid-tile-gradient' %><%= (_isPinned || _isBoost) ? ' is-pinned' : '' %><%= post.nsfw ? ' nsfw-media' : '' %>"
    href="<%= _href %>"
    <% if (_hasCover) { %>style="background-image: url('<%= post.cover_image_url %>'); --tile-hue: <%= _tileHue %>;"<% } else { %>style="--tile-hue: <%= _tileHue %>;"<% } %>
@@ -37,3 +37,4 @@
     <% if (_src) { %><span class="grid-tile-gradient-src">van <%= _src %></span><% } %>
   <% } %>
+  <% if (post.nsfw) { %><div class="nsfw-veil"><span class="nsfw-veil-label">🔞 <%= t('post.nsfw_warning') %></span><span class="nsfw-veil-btn"><%= t('post.nsfw_show') %></span></div><% } %>
 </a>
Index: src/views/shell.ejs
===================================================================
--- src/views/shell.ejs	(revision aa45208c239be20a3667852f4cc17c43e78c4394)
+++ src/views/shell.ejs	(revision 837fc9c1dc50db6b6a042e84da63ed9989edf2fa)
@@ -198,5 +198,5 @@
 
 <!-- v9 stylesheet (full palette system) -->
-<link rel="stylesheet" href="/assets/css/style.css?v=55">
+<link rel="stylesheet" href="/assets/css/style.css?v=56">
 <script>
 /* iOS safe-area, built by hand. env(safe-area-inset-top) resolves to 0 on this iOS in
@@ -725,4 +725,19 @@
 </script>
 
+<!-- NSFW / sensitive content: click a veil/reveal to un-blur. Capture-phase so the
+     click reveals instead of following the card link or firing htmx navigation. -->
+<script>
+(function () {
+  if (window.__nsfwWired) return; window.__nsfwWired = true;
+  document.addEventListener('click', function (e) {
+    var hit = e.target.closest && e.target.closest('.nsfw-veil, .nsfw-reveal');
+    if (!hit) return;
+    e.preventDefault(); e.stopPropagation();
+    var box = hit.closest('.nsfw-media, .nsfw-gate');
+    if (box) box.classList.add('is-shown');
+  }, true);
+})();
+</script>
+
 <!-- Per-site custom footer HTML -->
 <% if (safeSite.custom_foot_html) { %>
