Index: src/views/pages/news.ejs
===================================================================
--- src/views/pages/news.ejs	(revision 88788148df903019ffbc5b7d8b3dca7d1d759712)
+++ src/views/pages/news.ejs	(revision 476d6e7bb1352031dec41407d91c16837efa540b)
@@ -110,5 +110,5 @@
           %>
           <% if (_nsfwText) { %>
-            <div class="tl-content nsfw-media"><span class="nsfw-veil"><span class="nsfw-veil-label">🔞 <%= p.cw || t('post.nsfw_warning') %></span><span class="nsfw-veil-btn"><%= t('post.nsfw_show') %></span></span><%- p.content %></div>
+            <div class="tl-content nsfw-media"><span class="nsfw-veil"><%- include('../partials/nsfw-veil', { cw: p.cw }) %></span><%- p.content %></div>
           <% } else { %>
             <div class="tl-content"><%- p.content %></div>
@@ -127,5 +127,5 @@
                 players here. Posts without a Klonkt embed (e.g. a plain remote audio) keep them. */ %>
           <% if (!p.embedHtml && !p.embedUrl) { auds.forEach(function(m){ %><audio class="tl-media-audio" src="<%= m.url %>" controls preload="none"></audio><% }); } %>
-          <% if (_nsfwVisual) { %><div class="nsfw-veil"><span class="nsfw-veil-label">🔞 <%= p.cw || t('post.nsfw_warning') %></span><span class="nsfw-veil-btn"><%= t('post.nsfw_show') %></span></div></div><% } %>
+          <% if (_nsfwVisual) { %><div class="nsfw-veil"><%- include('../partials/nsfw-veil', { cw: p.cw }) %></div></div><% } %>
 
           <% if (p.embedHtml) { %><div class="tl-embed"><%- p.embedHtml %></div><% } %>
Index: src/views/pages/post.ejs
===================================================================
--- src/views/pages/post.ejs	(revision 88788148df903019ffbc5b7d8b3dca7d1d759712)
+++ src/views/pages/post.ejs	(revision 476d6e7bb1352031dec41407d91c16837efa540b)
@@ -209,7 +209,7 @@
                hx-push-url="/<%= rp.slug %>" hx-indicator="#pcms-loading"<% } %>>
               <% if (rp.cover_image_url) { %>
-                <span class="post-related-cover<%= rp.nsfw ? ' nsfw-media' : '' %>" aria-hidden="true"><img class="post-related-img" src="<%= rp.cover_image_url %>" alt="" loading="lazy" decoding="async"<% if (rp.cover_video_url) { %> data-ios-mp4="<%= rp.cover_video_url %>"<% } %>><% if (rp.nsfw) { %><span class="nsfw-veil"><span class="nsfw-veil-label">🔞 <%= rp.content_warning || t('post.nsfw_warning') %></span><span class="nsfw-veil-btn"><%= t('post.nsfw_show') %></span></span><% } %></span>
+                <span class="post-related-cover<%= rp.nsfw ? ' nsfw-media' : '' %>" aria-hidden="true"><img class="post-related-img" src="<%= rp.cover_image_url %>" alt="" loading="lazy" decoding="async"<% if (rp.cover_video_url) { %> data-ios-mp4="<%= rp.cover_video_url %>"<% } %>><% if (rp.nsfw) { %><span class="nsfw-veil"><%- include('../partials/nsfw-veil', { cw: rp.content_warning }) %></span><% } %></span>
               <% } else { %>
-                <span class="post-related-cover post-related-cover--empty<%= rp.nsfw ? ' nsfw-media' : '' %>" aria-hidden="true"><% if (rp.nsfw) { %><span class="nsfw-veil"><span class="nsfw-veil-label">🔞 <%= rp.content_warning || t('post.nsfw_warning') %></span><span class="nsfw-veil-btn"><%= t('post.nsfw_show') %></span></span><% } %></span>
+                <span class="post-related-cover post-related-cover--empty<%= rp.nsfw ? ' nsfw-media' : '' %>" aria-hidden="true"><% if (rp.nsfw) { %><span class="nsfw-veil"><%- include('../partials/nsfw-veil', { cw: rp.content_warning }) %></span><% } %></span>
               <% } %>
               <span class="post-related-meta">
Index: src/views/partials/nsfw-veil.ejs
===================================================================
--- src/views/partials/nsfw-veil.ejs	(revision 476d6e7bb1352031dec41407d91c16837efa540b)
+++ src/views/partials/nsfw-veil.ejs	(revision 476d6e7bb1352031dec41407d91c16837efa540b)
@@ -0,0 +1,3 @@
+<%# NSFW veil contents (label + reveal button). The caller supplies the wrapper
+    element (<span|div> class="nsfw-veil"), which stays as-is so nothing shifts.
+    Param: cw = the post's content_warning (may be empty → generic warning). %><span class="nsfw-veil-label">🔞 <%= cw || t('post.nsfw_warning') %></span><span class="nsfw-veil-btn"><%= t('post.nsfw_show') %></span>
Index: src/views/partials/post-card.ejs
===================================================================
--- src/views/partials/post-card.ejs	(revision 88788148df903019ffbc5b7d8b3dca7d1d759712)
+++ src/views/partials/post-card.ejs	(revision 476d6e7bb1352031dec41407d91c16837efa540b)
@@ -54,5 +54,5 @@
            alt="" loading="lazy" decoding="async"<% if (post.cover_video_url) { %> data-ios-mp4="<%= post.cover_video_url %>"<% } %>>
       <% } else if (post.cover_video_url) { %><video src="<%= post.cover_video_url %>" poster="<%= thumb(post.cover_video_url, 480) %>" autoplay loop muted playsinline></video><% } %>
-      <% if (post.nsfw) { %><span class="nsfw-veil"><span class="nsfw-veil-label">🔞 <%= post.content_warning || t('post.nsfw_warning') %></span><span class="nsfw-veil-btn"><%= t('post.nsfw_show') %></span></span><% } %>
+      <% if (post.nsfw) { %><span class="nsfw-veil"><%- include('nsfw-veil', { cw: post.content_warning }) %></span><% } %>
     </a>
   <% } else if (post.nsfw) { %>
@@ -60,5 +60,5 @@
        <% 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">
-      <span class="nsfw-veil"><span class="nsfw-veil-label">🔞 <%= post.content_warning || t('post.nsfw_warning') %></span><span class="nsfw-veil-btn"><%= t('post.nsfw_show') %></span></span>
+      <span class="nsfw-veil"><%- include('nsfw-veil', { cw: post.content_warning }) %></span>
     </a>
   <% } %>
Index: src/views/partials/post-tile.ejs
===================================================================
--- src/views/partials/post-tile.ejs	(revision 88788148df903019ffbc5b7d8b3dca7d1d759712)
+++ src/views/partials/post-tile.ejs	(revision 476d6e7bb1352031dec41407d91c16837efa540b)
@@ -39,4 +39,4 @@
     <% if (_src) { %><span class="grid-tile-gradient-src">van <%= _src %></span><% } %>
   <% } %>
-  <% if (post.nsfw) { %><div class="nsfw-veil"><span class="nsfw-veil-label">🔞 <%= post.content_warning || t('post.nsfw_warning') %></span><span class="nsfw-veil-btn"><%= t('post.nsfw_show') %></span></div><% } %>
+  <% if (post.nsfw) { %><div class="nsfw-veil"><%- include('nsfw-veil', { cw: post.content_warning }) %></div><% } %>
 </a>
