Index: src/views/pages/hub-home.ejs
===================================================================
--- src/views/pages/hub-home.ejs	(revision 44cf44d4c9875f687a02e6cb17f444b1caac6bf6)
+++ src/views/pages/hub-home.ejs	(revision 79bba6ed878137279a125184fc81941c85d79a2b)
@@ -1,24 +1,6 @@
 <div class="hub<%= hub.heroImage ? ' hub--img' : '' %>">
 
-  <%# ── Minimale nav (alleen auth) — over de hero, geen brand/switcher ── %>
-  <nav class="hub-nav">
-    <div class="hub-nav-inner container">
-      <% if (user) { %>
-        <% if (typeof canSeeBeheer !== 'undefined' && canSeeBeheer) { %>
-          <a class="hub-nav-link" href="/admin"
-             hx-get="/admin?partial=1" hx-target="#pcms-main" hx-swap="innerHTML"
-             hx-push-url="/admin" hx-indicator="#pcms-loading">Beheer</a>
-        <% } %>
-        <a class="hub-nav-link" href="/account"
-           hx-get="/account?partial=1" hx-target="#pcms-main" hx-swap="innerHTML"
-           hx-push-url="/account" hx-indicator="#pcms-loading"><%= user.username %></a>
-        <a class="hub-nav-link" href="/auth/logout">Uitloggen</a>
-      <% } else { %>
-        <a class="hub-nav-link" href="/auth/login">Inloggen</a>
-      <% } %>
-    </div>
-  </nav>
-
-  <%# ── HERO (label) — de pagina begint hier, geen header erboven ────── %>
+  <%# ── HERO (label) — de mini topnav-balk staat erboven (chrome.ejs);
+        de hero zelf is de "grote" header van de hub-home ──────────────── %>
   <header class="hub-hero<%= hub.heroImage ? ' has-img' : '' %>"<% if (hub.heroImage) { %> style="--hero-img:url('<%= hub.heroImage %>'); --hero-ov:<%= ((hub.heroOverlay == null ? 45 : hub.heroOverlay)/100).toFixed(2) %>"<% } %>>
     <div class="hub-hero-inner container">
Index: src/views/pages/prutter-conversation.ejs
===================================================================
--- src/views/pages/prutter-conversation.ejs	(revision 44cf44d4c9875f687a02e6cb17f444b1caac6bf6)
+++ src/views/pages/prutter-conversation.ejs	(revision 79bba6ed878137279a125184fc81941c85d79a2b)
@@ -1,19 +1,19 @@
-<div class="container prutter-conv-page" data-conversation-id="<%= conversation.id %>" data-me="<%= user.id %>">
+<div class="prutter-conv-page" data-conversation-id="<%= conversation.id %>" data-me="<%= user.id %>">
   <header class="prutter-conv-header">
-    <p class="prutter-back"><a href="<%= siteUrlBase %>/prutter">&larr; Inbox</a></p>
-    <div class="prutter-conv-headline">
-      <div class="prutter-conv-avatar prutter-conv-avatar--lg">
-        <% if (other && other.avatar_url) { %>
-          <img src="<%= other.avatar_url %>" alt="">
-        <% } else { %>
-          <span><%= (other && other.username || '?').charAt(0).toUpperCase() %></span>
-        <% } %>
-      </div>
-      <div>
-        <h1><%= other ? other.username : 'Unknown' %></h1>
-        <% if (other) { %>
-          <p class="prutter-conv-sub"><a href="/users/<%= encodeURIComponent(other.username) %>">View profile</a></p>
-        <% } %>
-      </div>
+    <a class="prutter-back" href="<%= siteUrlBase %>/prutter" aria-label="Terug naar inbox" title="Inbox">
+      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="19" y1="12" x2="5" y2="12"/><polyline points="12 19 5 12 12 5"/></svg>
+    </a>
+    <div class="prutter-conv-avatar">
+      <% if (other && other.avatar_url) { %>
+        <img src="<%= other.avatar_url %>" alt="">
+      <% } else { %>
+        <span><%= (other && other.username || '?').charAt(0).toUpperCase() %></span>
+      <% } %>
+    </div>
+    <div class="prutter-conv-id">
+      <span class="prutter-conv-name"><%= other ? other.username : 'Onbekend' %></span>
+      <% if (other) { %>
+        <a class="prutter-conv-sub" href="/users/<%= encodeURIComponent(other.username) %>">Bekijk profiel</a>
+      <% } %>
     </div>
   </header>
@@ -35,6 +35,6 @@
         hx-swap="beforeend"
         hx-on::after-request="this.querySelector('textarea').value=''; window.__prutterScroll && window.__prutterScroll();">
-    <textarea name="content" rows="2" maxlength="2000" placeholder="Type a message…" required></textarea>
-    <button type="submit" class="btn btn-primary">Send</button>
+    <textarea name="content" rows="1" maxlength="2000" placeholder="Bericht…" required></textarea>
+    <button type="submit" class="btn btn-primary">Stuur</button>
   </form>
 </div>
@@ -87,34 +87,75 @@
 
 <style>
-.prutter-conv-page { max-width: 720px; margin: 2rem auto; padding: 0 1rem; display: flex; flex-direction: column; gap: 1rem; }
-.prutter-back { margin: 0; }
-.prutter-back a { color: var(--accent); text-decoration: none; }
-
-.prutter-conv-headline {
-  display: flex; align-items: center; gap: 0.85rem;
-  padding-bottom: 1rem; border-bottom: 1px solid var(--rule);
-}
-.prutter-conv-headline h1 {
-  font-family: var(--font-display, serif);
-  font-size: 1.5rem; margin: 0;
-}
-.prutter-conv-sub { margin: 0; font-size: 0.85rem; color: var(--ink-muted, var(--ink-soft)); }
-.prutter-conv-sub a { color: var(--accent); }
-
-.prutter-conv-avatar--lg { width: 56px; height: 56px; }
-.prutter-conv-avatar--lg span { font-size: 1.2rem; }
-
+/* ── Full-height chat-layout (mobile-first) ──────────────────────────────
+   De pagina is FIXED gepind tussen de topnav (desktop) en de onderste balken
+   (bottom-tab + audiospeler), zodat ALLEEN de thread scrollt — niet de hele
+   pagina. De offsets volgen de body-classes has-bottom-tab/has-audio-player,
+   dus de composer valt nooit achter een balk. */
+.prutter-conv-page {
+  position: fixed;
+  left: 0; right: 0;
+  top: 0;                 /* mobiel: masthead is verborgen → vanaf de top */
+  bottom: 0;
+  max-width: 760px; margin: 0 auto;
+  display: flex; flex-direction: column;
+  padding: 0.5rem 0.75rem;
+  z-index: 1;
+}
+/* Desktop: de sticky masthead staat erboven → begin eronder. */
+@media (min-width: 768px) {
+  .prutter-conv-page { top: 64px; padding: 0.75rem 1rem; }
+}
+/* Onderste vrije ruimte = de balken die de viewport-bodem overlappen. */
+@media (max-width: 767px) {
+  body.has-bottom-tab .prutter-conv-page { bottom: calc(56px + env(safe-area-inset-bottom, 0)); }
+}
+body.has-audio-player .prutter-conv-page { bottom: calc(76px + env(safe-area-inset-bottom, 0)); }
+@media (max-width: 767px) {
+  body.has-bottom-tab.has-audio-player .prutter-conv-page { bottom: calc(56px + 76px + env(safe-area-inset-bottom, 0)); }
+}
+
+/* Compacte chat-kop: terug-pijl + avatar + naam op één regel. */
+.prutter-conv-header {
+  flex: 0 0 auto;
+  display: flex; align-items: center; gap: 0.65rem;
+  padding-bottom: 0.6rem; margin-bottom: 0.5rem;
+  border-bottom: 1px solid var(--rule);
+}
+.prutter-back {
+  flex: 0 0 auto;
+  display: inline-flex; align-items: center; justify-content: center;
+  width: 40px; height: 40px; border-radius: 8px;
+  color: var(--ink); text-decoration: none;
+}
+.prutter-back:hover { color: var(--accent); background: var(--paper-2); }
+.prutter-back svg { width: 20px; height: 20px; }
+.prutter-conv-avatar {
+  flex: 0 0 auto;
+  width: 40px; height: 40px; border-radius: 50%;
+  overflow: hidden; background: var(--paper-2);
+  display: inline-flex; align-items: center; justify-content: center;
+  color: var(--accent); font-weight: 700;
+}
+.prutter-conv-avatar img { width: 100%; height: 100%; object-fit: cover; }
+.prutter-conv-id { display: flex; flex-direction: column; min-width: 0; line-height: 1.2; }
+.prutter-conv-name {
+  font-family: var(--font-display, serif); font-size: 1.15rem; color: var(--ink);
+  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
+}
+.prutter-conv-sub { font-size: 0.78rem; color: var(--ink-muted, var(--ink-soft)); text-decoration: none; }
+.prutter-conv-sub:hover { color: var(--accent); }
+
+/* De ENIGE scroller. min-height:0 is cruciaal in een flex-kolom, anders
+   groeit de lijst i.p.v. te scrollen. */
 .prutter-thread {
-  list-style: none; padding: 0; margin: 0;
-  display: flex; flex-direction: column; gap: 0.5rem;
-  background: var(--paper-2);
-  border: 1px solid var(--rule);
-  border-radius: 8px;
-  padding: 1rem;
-  height: 60vh;
-  overflow-y: auto;
-}
-
-.prutter-msg { display: flex; flex-direction: column; max-width: 80%; }
+  flex: 1 1 auto; min-height: 0;
+  list-style: none; margin: 0;
+  display: flex; flex-direction: column; gap: 0.4rem;
+  padding: 0.25rem 0.1rem 0.5rem;
+  overflow-y: auto; overscroll-behavior: contain;
+  -webkit-overflow-scrolling: touch;
+}
+
+.prutter-msg { display: flex; flex-direction: column; max-width: 82%; }
 .prutter-msg-bubble {
   padding: 0.55rem 0.85rem;
@@ -148,18 +189,22 @@
 
 .prutter-composer {
+  flex: 0 0 auto;
   display: flex; gap: 0.5rem; align-items: flex-end;
-  padding-bottom: env(safe-area-inset-bottom, 0);
+  padding-top: 0.6rem; margin-top: 0.25rem;
+  border-top: 1px solid var(--rule);
 }
 .prutter-composer textarea {
   flex: 1;
-  padding: 0.6rem 0.85rem;
+  padding: 0.65rem 0.85rem;
   border: 1px solid var(--rule);
-  border-radius: 8px;
+  border-radius: 12px;
   background: var(--paper);
   color: var(--ink);
   font-family: inherit;
-  font-size: 0.95rem;
-  resize: vertical;
-  min-height: 44px;
-}
+  font-size: 1rem;            /* ≥16px → iOS zoomt niet in bij focus */
+  line-height: 1.35;
+  resize: none;
+  min-height: 44px; max-height: 40vh;
+}
+.prutter-composer .btn { flex: 0 0 auto; min-height: 44px; }
 </style>
Index: src/views/partials/chrome.ejs
===================================================================
--- src/views/partials/chrome.ejs	(revision 44cf44d4c9875f687a02e6cb17f444b1caac6bf6)
+++ src/views/partials/chrome.ejs	(revision 79bba6ed878137279a125184fc81941c85d79a2b)
@@ -8,14 +8,24 @@
 //
 // Wordt zowel door shell.ejs (oob niet gezet) als door render.js bij partials
-// (oob=true) gerenderd. bareChrome-logica staat hier zodat beide identiek zijn.
-var _isAdmin = typeof bodyClass === 'string' && bodyClass.indexOf('on-admin') >= 0;
-var bareChrome = (typeof bodyClass === 'string' && (bodyClass.indexOf('on-hub') >= 0 || bodyClass.indexOf('on-auth') >= 0))
+// (oob=true) gerenderd. De logica staat hier zodat beide identiek zijn.
+//
+//   _isAuth     → focus-scherm (in/uitloggen): helemaal geen chrome.
+//   _headerless → hub-homepagina én de Prutter-chat: WÉL de mini topnav-balk,
+//                 GEEN profielkop (hub: hero = header; chat: full-height view).
+//   anders      → topnav + profielkop (+ view-switcher buiten admin).
+var _isAdmin   = typeof bodyClass === 'string' && bodyClass.indexOf('on-admin') >= 0;
+var _isAuth    = typeof bodyClass === 'string' && bodyClass.indexOf('on-auth') >= 0;
+var _isChat    = typeof bodyClass === 'string' && bodyClass.indexOf('on-chat') >= 0;
+var hubLanding = (typeof bodyClass === 'string' && bodyClass.indexOf('on-hub') >= 0)
    || ((typeof tenancy !== 'undefined' && tenancy === 'hub') && !siteUrlBase && !_isAdmin);
+var _headerless = hubLanding || _isChat;
 %>
 <div id="pcms-chrome"<% if (typeof oob !== 'undefined' && oob) { %> hx-swap-oob="true"<% } %>>
-<% if (!bareChrome) { %>
+<% if (!_isAuth) { %>
   <%- include('topnav') %>
-  <%- include('profile-header') %>
-  <% if (!_isAdmin) { %><%- include('view-switcher') %><% } %>
+  <% if (!_headerless) { %>
+    <%- include('profile-header') %>
+    <% if (!_isAdmin) { %><%- include('view-switcher') %><% } %>
+  <% } %>
 <% } %>
 </div>
Index: src/views/shell.ejs
===================================================================
--- src/views/shell.ejs	(revision 44cf44d4c9875f687a02e6cb17f444b1caac6bf6)
+++ src/views/shell.ejs	(revision 79bba6ed878137279a125184fc81941c85d79a2b)
@@ -419,4 +419,59 @@
 </script>
 
+<!-- Globale link-boost: alle interne navigatie-links lopen via htmx in #pcms-main,
+     zodat de audioplayer (los in document.body) blijft spelen i.p.v. te verspringen
+     bij een full page-load. Werkt overal — Beheer, Account, posts, sites — zonder
+     elke link los htmx te maken. Links die écht een volledige load nodig hebben
+     (uitloggen/auth, downloads, feeds, media, assets, bestanden) worden overgeslagen,
+     net als links die al hun eigen hx-* hebben. -->
+<script>
+(function () {
+  if (!window.htmx) return;
+  // Back/forward moet her-ophalen, zodat de OOB-site-chrome (kop) meekomt met de
+  // herstelde pagina — htmx' snapshot-cache zou anders een stale kop tonen.
+  try { window.htmx.config.historyCacheSize = 0; } catch (_) {}
+
+  function fullLoad(a, url) {
+    if (a.hasAttribute('download') || a.hasAttribute('data-full-load')) return true;
+    if (a.hasAttribute('hx-get') || a.hasAttribute('hx-post') || a.hasAttribute('hx-boost')) return true;
+    if (a.target && a.target !== '_self') return true;
+    if (a.getAttribute('rel') === 'external') return true;
+    var p = url.pathname;
+    // Sessie/security/oauth → volledige navigatie (cookies, redirects).
+    if (/^\/(logout|login|auth|oauth)(?:\/|$)/.test(p)) return true;
+    // Feeds, PWA, service-worker, statics, media-streams, downloads.
+    if (/^\/(feed|atom|sitemap|manifest|robots|sw\.js|assets|media|audio|uploads)(?:\/|\.|$)/.test(p)) return true;
+    if (/\.[a-z0-9]{2,5}$/i.test(p)) return true; // bestandsextensie → laat de browser 't halen
+    return false;
+  }
+
+  var lastPath = location.pathname + location.search;
+
+  document.addEventListener('click', function (e) {
+    if (e.defaultPrevented || e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;
+    var a = e.target.closest('a[href]');
+    if (!a) return;
+    var href = a.getAttribute('href');
+    if (!href || href.charAt(0) === '#') return;
+    var url; try { url = new URL(a.href, location.href); } catch (_) { return; }
+    if (url.origin !== location.origin) return;
+    if (fullLoad(a, url)) return;
+    e.preventDefault();
+    var dest = url.pathname + url.search;
+    if (dest !== lastPath) history.pushState({ b: 1 }, '', dest);
+    lastPath = dest;
+    window.htmx.ajax('GET', dest, { target: '#pcms-main', swap: 'innerHTML' });
+    try { window.scrollTo(0, 0); } catch (_) {}
+  });
+
+  window.addEventListener('popstate', function () {
+    var here = location.pathname + location.search;
+    if (here === lastPath) return;
+    lastPath = here;
+    window.htmx.ajax('GET', here, { target: '#pcms-main', swap: 'innerHTML' });
+  });
+})();
+</script>
+
 <!-- PWA install prompt — show button when browser fires beforeinstallprompt -->
 <script>
