Index: src/assets/css/style.css
===================================================================
--- src/assets/css/style.css	(revision 03ff677c4f973495c41211748b0c9c1a27dd00ae)
+++ src/assets/css/style.css	(revision 2d2fd8c352950d0d5ff366c901fb7e2e0cf6d614)
@@ -2353,5 +2353,6 @@
     padding: .35rem 0;
 }
-.view-switch-btn {
+.view-switch-btn,
+.view-switch-link {
     display: inline-flex;
     align-items: center;
@@ -2368,5 +2369,6 @@
     transition: all .15s;
 }
-.view-switch-btn:hover {
+.view-switch-btn:hover,
+.view-switch-link:hover {
     color: var(--ink);
     border-color: var(--ink-muted);
Index: src/assets/js/mod/read.js
===================================================================
--- src/assets/js/mod/read.js	(revision 03ff677c4f973495c41211748b0c9c1a27dd00ae)
+++ src/assets/js/mod/read.js	(revision 2d2fd8c352950d0d5ff366c901fb7e2e0cf6d614)
@@ -62,5 +62,5 @@
 
 async function fetchArticle(slug, base) {
-  const r = await fetch(`${base}/read/${encodeURIComponent(slug)}?partial=1`, {
+  const r = await fetch(`${base}/read/${encodeURIComponent(slug)}?fragment=1`, {
     headers: { 'HX-Request': 'true' },
     credentials: 'same-origin',
Index: src/routes/posts.js
===================================================================
--- src/routes/posts.js	(revision 03ff677c4f973495c41211748b0c9c1a27dd00ae)
+++ src/routes/posts.js	(revision 2d2fd8c352950d0d5ff366c901fb7e2e0cf6d614)
@@ -295,5 +295,16 @@
     pageJs: 'read',
   };
-  if (req.query.partial === '1' || req.headers['hx-request'] === 'true') {
+  // ALLEEN de leesmodule krijgt één artikel; ?fragment=1 is haar eigen teken.
+  //
+  // Hier stond `?partial=1 || hx-request`, en dat waren twee dingen door elkaar.
+  // Op deze site betekent ?partial=1 al iets anders -- "de pagina zonder de
+  // schil", voor htmx -- dus een gebooste link naar /read kreeg één kaal
+  // artikel in #pcms-main geschoven: geen stroom, geen on-read, geen script.
+  // Onzichtbaar zolang je /read alleen intikt; zichtbaar zodra er een link naar
+  // toe bestaat (de Lezen-knop in de view-switcher).
+  //
+  // Alles wat GEEN fragment vraagt gaat naar pages/read, en renderPage bepaalt
+  // daar zelf of de schil eromheen moet -- net als bij elke andere pagina.
+  if (req.query.fragment === '1') {
     return renderPage(req, res, 'partials/read-article', model);
   }
Index: src/services/i18n.js
===================================================================
--- src/services/i18n.js	(revision 03ff677c4f973495c41211748b0c9c1a27dd00ae)
+++ src/services/i18n.js	(revision 2d2fd8c352950d0d5ff366c901fb7e2e0cf6d614)
@@ -34,5 +34,5 @@
     'switch.circle': 'Cirkels',
     'switch.timeline': 'Tijdlijn',
-    'switch.grid': 'Grid',
+    'switch.grid': 'Grid', 'switch.reader': 'Lezen',
     'postnav.newer': 'Nieuwer',
     'postnav.older': 'Ouder',
@@ -1049,5 +1049,5 @@
     'switch.circle': 'Circles',
     'switch.timeline': 'Timeline',
-    'switch.grid': 'Grid',
+    'switch.grid': 'Grid', 'switch.reader': 'Reader',
     'postnav.newer': 'Newer',
     'postnav.older': 'Older',
@@ -2055,5 +2055,5 @@
     'switch.circle': 'Zirkel',
     'switch.timeline': 'Zeitleiste',
-    'switch.grid': 'Raster',
+    'switch.grid': 'Raster', 'switch.reader': 'Lesen',
     'postnav.newer': 'Neuer',
     'postnav.older': 'Älter',
Index: src/views/partials/shared-styles.ejs
===================================================================
--- src/views/partials/shared-styles.ejs	(revision 03ff677c4f973495c41211748b0c9c1a27dd00ae)
+++ src/views/partials/shared-styles.ejs	(revision 2d2fd8c352950d0d5ff366c901fb7e2e0cf6d614)
@@ -27,5 +27,6 @@
   padding: 0.15rem;
 }
-.view-switch-btn {
+.view-switch-btn,
+.view-switch-link {
   display: inline-flex; align-items: center; gap: 0.35rem;
   padding: 0.35rem 0.85rem;
@@ -35,9 +36,12 @@
   transition: background 120ms, color 120ms;
 }
-.view-switch-btn:hover { color: var(--ink); }
+.view-switch-btn:hover,
+.view-switch-link:hover { color: var(--ink); }
+.view-switch-link { text-decoration: none; }
 /* Active = white (light background, dark text). Via aria-selected (element
    attribute, set by syncAria + on htmx:afterSettle) → htmx-robust. On non-
    feed pages syncAria sets aria-selected=false → both grey. */
-.view-switch-btn[aria-selected="true"] { background: var(--ink); color: var(--paper); border-color: var(--ink); }
+.view-switch-btn[aria-selected="true"],
+.view-switch-link[aria-selected="true"] { background: var(--ink); color: var(--paper); border-color: var(--ink); }
 
 /* Agenda = its OWN pill (separate from Timeline/Grid, so the feed-toggle is never
Index: src/views/partials/view-switcher.ejs
===================================================================
--- src/views/partials/view-switcher.ejs	(revision 03ff677c4f973495c41211748b0c9c1a27dd00ae)
+++ src/views/partials/view-switcher.ejs	(revision 2d2fd8c352950d0d5ff366c901fb7e2e0cf6d614)
@@ -8,6 +8,8 @@
   // Pagina's zonder actieve feed-weergave → niets in de pill is 'actief' (Solo/Cirkel
   // + Tijdlijn/Grid grijs). Alleen Agenda krijgt z'n eigen is-active op de agenda.
-  var _neutral = ['on-shows','on-downloads','on-post','on-epk','on-linkbio','on-newsletter','on-archive']
+  var _neutral = ['on-shows','on-downloads','on-post','on-epk','on-linkbio','on-newsletter','on-archive','on-read']
                    .some(function(c){ return _bc.indexOf(c) >= 0; });
+  var _onRead = _bc.indexOf('on-read') >= 0;
+  var _sbR = (typeof siteUrlBase !== 'undefined' && siteUrlBase) ? siteUrlBase : '';
 %>
 <div class="view-switcher-wrap">
@@ -45,4 +47,16 @@
         <span><%= t('switch.grid') %></span>
       </button>
+      <%# Lezen is GEEN in-place omschakeling maar een andere route (/read), dus
+          een <a> en niet een <button>. En bewust NIET de klasse view-switch-btn:
+          de gedelegeerde klik in shell.ejs pakt alles met die klasse op, zet
+          body[data-feed-view] erop en springt op niet-feedpagina's terug naar
+          home -- dat zou deze link opeten. Andere klasse, zelfde uiterlijk.
+          aria-selected staat hier vast: syncAria() raakt alleen .view-switch-btn
+          aan, dus de actieve staat op /read blijft staan na een htmx-navigatie. %>
+      <a class="view-switch-link" href="<%= _sbR %>/read" role="tab"
+         aria-selected="<%= _onRead ? 'true' : 'false' %>">
+        <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"/><path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"/></svg>
+        <span><%= t('switch.reader') %></span>
+      </a>
     </div>
     <% if (!_neutral) { %>
Index: src/views/shell.ejs
===================================================================
--- src/views/shell.ejs	(revision 03ff677c4f973495c41211748b0c9c1a27dd00ae)
+++ src/views/shell.ejs	(revision 2d2fd8c352950d0d5ff366c901fb7e2e0cf6d614)
@@ -222,5 +222,5 @@
 
 <!-- v9 stylesheet (full palette system) -->
-<link rel="stylesheet" href="/assets/css/style.css?v=67">
+<link rel="stylesheet" href="/assets/css/style.css?v=68">
 <script>
 /* iOS safe-area, built by hand. env(safe-area-inset-top) resolves to 0 on this iOS in
@@ -434,7 +434,13 @@
   // we opnieuw wat de server stuurde. 'on-auth' staat erbij zodat het login-
   // focusscherm óók via htmx z'n styling krijgt (geen chrome, geen tab, audio op 0).
+  // ALLES wat de server als page-class kan sturen moet hier staan: wat er niet in
+  // staat wordt bij een htmx-navigatie stilletjes weggefilterd. 'on-read' ontbrak,
+  // en dan kwam je via de Lezen-knop op een leesweergave waarvan ELKE regel
+  // (.on-read ...) niet aansloeg: geen schermhoge berichten, geen snappen, geen
+  // wegschuivende balken. Bij intikken van /read ging het wel goed, want dan
+  // rendert de shell de class zelf -- vandaar dat het lang onzichtbaar bleef.
   var PAGE_CLASSES = ['on-home','on-post','on-special','on-archive','on-search','on-admin','on-auth',
     'on-shows','on-downloads','on-download','on-epk','on-linkbio','on-newsletter',
-    'on-tag','on-type','on-user','on-cirkel','on-hub','on-chat'];
+    'on-tag','on-type','on-user','on-cirkel','on-hub','on-chat','on-read'];
 
   document.body.addEventListener('pcmsNav', function(ev) {
@@ -503,5 +509,5 @@
   // Eén nummer voor de hele map. Te vaak bumpen kost één download; te weinig
   // bumpen kost een bugfix die nooit aankomt.
-  var MOD_V = 2;
+  var MOD_V = 3;
 
   // name -> 1 (aan het laden) of de module-namespace (geladen). Een module
