Index: src/views/pages/search.ejs
===================================================================
--- src/views/pages/search.ejs	(revision 9eb6e1bceee7058678085d70abe8ff4dffe8bff8)
+++ src/views/pages/search.ejs	(revision fd8faf581c665d69b41dd6f5262d74bf432c0e1b)
@@ -1,265 +1,9 @@
-<% const _base = (typeof siteUrlBase !== 'undefined' && siteUrlBase) ? siteUrlBase : ''; %>
-<% const _tracks = (typeof tracks !== 'undefined' && tracks) ? tracks : []; %>
-<% const _events = (typeof events !== 'undefined' && events) ? events : []; %>
-<% const _pages = (typeof pages !== 'undefined' && pages) ? pages : []; %>
-<div class="container search-page">
-  <h1><%= t('nav.search') %></h1>
+<%#
+  /search?q=... is geen eigen pagina meer met een eigen uiterlijk (Robins
+  ontwerp, 30-9): het is het zoekvlak, open, met de resultaten al op de server
+  gerenderd. Een gedeelde link en Enter zonder JavaScript komen zo op precies
+  hetzelfde beeld uit als de zoekbalk op elke andere pagina.
 
-  <form method="get" action="<%= _base %>/search" class="search-page-form">
-    <input
-      type="search"
-      name="q"
-      value="<%= query %>"
-      placeholder="<%= t('search.placeholder') %>"
-      autocomplete="off"
-      autofocus
-      aria-label="<%= t('nav.search') %>">
-    <button type="submit" class="btn btn-primary"><%= t('search.button') %></button>
-  </form>
-
-  <% if (queryError) { %>
-    <p class="search-error"><%= t('search.error') %></p>
-  <% } %>
-
-  <% if (query && !queryError) { %>
-    <p class="search-meta">
-      <%= t(total === 1 ? 'search.results_one' : 'search.results_other', { n: total, q: query }) %>
-    </p>
-  <% } %>
-
-  <%# Een geplakt adres is geen zoekterm maar een aanwijzing (shaer-utpi): het
-      object van de andere server staat bovenaan, met dezelfde knoppen als op
-      /authorize_interaction -- dezelfde partial, dus één render en één plek
-      waar een poort of een sanitizer kan wonen. %>
-  <% if (typeof remoteUri !== 'undefined' && remoteUri) { %>
-    <h2 class="search-section-title"><%= t('search.section_fediverse') %></h2>
-    <div class="auth-interact search-remote">
-      <% if (!mayLookup) { %>
-        <%# Ophalen is een uitgaand verzoek namens deze server; dat doen we niet
-            op verzoek van een willekeurige bezoeker. %>
-        <p class="auth-interact-note"><%= t('search.fedi_signin') %></p>
-      <% } else if (remote && remoteKind === 'note') { %>
-        <%- include('../partials/remote-object', { target: remote, uri: remoteUri, reacted: remoteReacted, siteTitle: siteTitle, cancelUrl: _base + '/search?q=' + encodeURIComponent(query) }) %>
-      <% } else if (remote && remoteKind === 'actor') { %>
-        <%- include('../partials/remote-actor', { actor: remote, siteTitle: siteTitle }) %>
-      <% } else { %>
-        <p class="auth-interact-note"><%= t('fedi.remote_notfound') %></p>
-      <% } %>
-    </div>
-    <%- include('../partials/fedi-interact-styles') %>
-  <% } %>
-
-  <% if (_tracks.length) { %>
-    <h2 class="search-section-title"><%= t('search.section_tracks') %></h2>
-    <% var _inPost = t('search.in_post'); /* t() vóór de loop ophalen: function(t) overschaduwt 'm */ %>
-    <div class="search-tracks">
-      <% _tracks.forEach(function(t) {
-           const tj = JSON.stringify({ id: t.id, url: t.url, title: t.title, artist: t.artist || '', cover: t.cover || '' })
-             .replace(/&/g, '&amp;').replace(/'/g, '&#39;').replace(/</g, '&lt;'); %>
-        <div class="post-audio-track search-track" id="track-<%= t.id %>"
-             data-pcms-track-id="<%= t.id %>" data-pcms-track-url="<%= t.url %>"
-             data-pcms-track='<%- tj %>'>
-          <button type="button" class="pat-play" aria-label="Speel <%= t.title %>">
-            <svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M8 4l12 8-12 8z"/></svg>
-          </button>
-          <% if (t.cover) { %>
-            <span class="search-track-cover" style="background-image:url('<%= t.cover %>')" aria-hidden="true"></span>
-          <% } %>
-          <div class="pat-info">
-            <div class="pat-title"><%= t.title %></div>
-            <% if (t.artist || t.album) { %>
-              <div class="pat-artist">
-                <%= t.artist %><% if (t.artist && t.album) { %> &middot; <% } %><%= t.album %>
-              </div>
-            <% } %>
-          </div>
-          <% if (t.postUrl) { %>
-            <a class="search-track-link"
-               href="<%= t.postUrl %>"
-               hx-get="<%= t.postUrl %>?partial=1"
-               hx-target="#pcms-main"
-               hx-swap="innerHTML"
-               hx-push-url="<%= t.postUrl %>"
-               aria-label="<%= _inPost %>"><%= _inPost %></a>
-          <% } %>
-        </div>
-      <% }); %>
-    </div>
-  <% } %>
-
-  <% if (results && results.length) { %>
-    <% if (_tracks.length) { %><h2 class="search-section-title"><%= t('search.section_posts') %></h2><% } %>
-    <ol class="search-results">
-      <% results.forEach(function(r) { %>
-        <li class="search-result">
-          <h3 class="search-result-title">
-            <a href="<%= _base %>/<%= r.slug %>"
-               hx-get="<%= _base %>/<%= r.slug %>?partial=1"
-               hx-target="#pcms-main"
-               hx-swap="innerHTML"
-               hx-push-url="<%= _base %>/<%= r.slug %>"
-               hx-indicator="#pcms-loading">
-              <%= r.title || '(zonder titel)' %>
-            </a>
-          </h3>
-          <p class="search-result-meta">
-            <%= r.author_username %>
-            <% if (r.published_at) { %> &middot; <%= formatDate(r.published_at) %><% } %>
-          </p>
-          <p class="search-result-snippet"><%- r.snippet %></p>
-        </li>
-      <% }); %>
-    </ol>
-  <% } %>
-
-  <% if (_events.length) { %>
-    <h2 class="search-section-title"><%= t('search.section_events') %></h2>
-    <ol class="search-results">
-      <% _events.forEach(function(ev) { %>
-        <li class="search-result">
-          <h3 class="search-result-title"><a href="<%= ev.url %>"><%= ev.where || '—' %></a></h3>
-          <p class="search-result-meta"><%= [ev.date, ev.time].filter(Boolean).join(' ') %></p>
-        </li>
-      <% }); %>
-    </ol>
-  <% } %>
-
-  <% if (_pages.length) { %>
-    <h2 class="search-section-title"><%= t('search.section_pages') %></h2>
-    <ul class="search-pages">
-      <% _pages.forEach(function(pg) { %>
-        <li><a class="search-page-link" href="<%= pg.url %>"><%= pg.label %> <span aria-hidden="true">→</span></a></li>
-      <% }); %>
-    </ul>
-  <% } %>
-
-  <% if (query && !queryError && !results.length && !_tracks.length && !_events.length && !_pages.length) { %>
-    <p class="search-empty"><%= t('search.empty') %></p>
-  <% } %>
-</div>
-
-<style>
-.search-page { max-width: 720px; margin: 3rem auto; padding: 0 1rem; }
-.search-page h1 { font-family: var(--font-display, serif); font-size: 2rem; margin: 0 0 1.5rem; }
-
-.search-page-form {
-  display: flex;
-  gap: 0.5rem;
-  margin-bottom: 1.5rem;
-}
-.search-page-form input {
-  flex: 1;
-  padding: 0.6rem 0.9rem;
-  border: 1px solid var(--rule);
-  border-radius: 5px;
-  background: var(--paper-2);
-  color: var(--ink);
-  font-size: 1rem;
-}
-.search-page-form input:focus {
-  outline: none;
-  border-color: var(--accent);
-}
-
-.search-meta {
-  color: var(--ink-muted, var(--ink-soft));
-  font-size: 0.9rem;
-  margin: 0 0 1rem;
-}
-.search-section-title {
-  font-family: var(--font-display, serif);
-  font-size: 1.1rem;
-  margin: 1.75rem 0 0.75rem;
-  color: var(--ink-soft);
-  text-transform: uppercase;
-  letter-spacing: 0.05em;
-}
-.search-error {
-  background: rgba(200, 60, 60, 0.15);
-  color: #c33;
-  padding: 0.75rem 1rem;
-  border-radius: 6px;
-  border: 1px solid rgba(200, 60, 60, 0.3);
-}
-.search-empty {
-  color: var(--ink-muted, var(--ink-soft));
-  text-align: center;
-  margin: 3rem 0;
-}
-
-/* Nummer-resultaten */
-.search-tracks { display: flex; flex-direction: column; gap: 0.25rem; }
-.search-track.post-audio-track {
-  display: flex;
-  align-items: center;
-  gap: 0.75rem;
-  padding: 0.5rem 0.6rem;
-  border-radius: 8px;
-}
-.search-track .search-track-cover {
-  width: 38px; height: 38px;
-  flex: 0 0 38px;
-  border-radius: 5px;
-  background-size: cover;
-  background-position: center;
-  background-color: var(--paper-2);
-}
-.search-track .pat-info { flex: 1; min-width: 0; }
-.search-track .pat-title { font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
-.search-track .pat-artist { font-size: 0.82rem; color: var(--ink-soft); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
-.search-track-link {
-  flex: 0 0 auto;
-  font-size: 0.82rem;
-  color: var(--accent);
-  text-decoration: none;
-  white-space: nowrap;
-}
-.search-track-link:hover { text-decoration: underline; }
-
-.search-results {
-  list-style: none;
-  margin: 0;
-  padding: 0;
-}
-.search-result {
-  padding: 1rem 0;
-  border-bottom: 1px solid var(--rule);
-}
-.search-result:last-child { border-bottom: 0; }
-
-.search-result-title {
-  font-family: var(--font-display, serif);
-  font-size: 1.25rem;
-  margin: 0 0 0.25rem;
-}
-.search-result-title a {
-  color: var(--ink);
-  text-decoration: none;
-}
-.search-result-title a:hover { color: var(--accent); }
-
-.search-result-meta {
-  margin: 0 0 0.5rem;
-  color: var(--ink-muted, var(--ink-soft));
-  font-size: 0.85rem;
-}
-
-.search-result-snippet {
-  margin: 0;
-  color: var(--ink-soft);
-  line-height: 1.5;
-}
-.search-result-snippet mark {
-  background: var(--accent);
-  color: white;
-  padding: 0.05em 0.25em;
-  border-radius: 3px;
-}
-.search-pages { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 0.5rem; }
-.search-page-link {
-  display: inline-flex; align-items: center; gap: 0.3rem;
-  padding: 0.45rem 0.8rem; border: 1px solid var(--rule); border-radius: 999px;
-  color: var(--ink); text-decoration: none; font-size: 0.9rem; background: var(--paper-2);
-}
-.search-page-link:hover { border-color: var(--accent); color: var(--accent); }
-</style>
+  De opmaak staat in partials/search-styles.ejs, die de topnav al meebrengt.
+%>
+<%- include('../partials/search-surface', Object.assign({}, locals, { mode: 'page' })) %>
Index: src/views/partials/fedi-interact-styles.ejs
===================================================================
--- src/views/partials/fedi-interact-styles.ejs	(revision 9eb6e1bceee7058678085d70abe8ff4dffe8bff8)
+++ src/views/partials/fedi-interact-styles.ejs	(revision fd8faf581c665d69b41dd6f5262d74bf432c0e1b)
@@ -159,16 +159,8 @@
     border: 1px solid color-mix(in srgb, var(--ink, #000) 18%, transparent); background: var(--paper, #fff); color: var(--ink, #000); }
 
-  /* De live-preview: open-link onder de kaart, en de kaart zonder rand-marge. */
+  /* De compacte kaart in het zoekvak: de link naar de volle pagina eronder. */
   .rp-open { display: block; padding: 10px 4px 2px; text-align: right; font-weight: 600; font-size: .88rem;
     color: var(--accent, #06c); text-decoration: none; }
   .rp-open:hover { text-decoration: underline; }
-  .ss-remote { padding: 4px 0 6px; }
-  .ss-remote-note { padding: .8rem .6rem; color: var(--ink-soft, var(--ink-muted)); font-size: .9rem; }
-  .ss-remote-loading { display: flex; align-items: center; gap: 10px; padding: .9rem .6rem; color: var(--ink-soft, var(--ink-muted)); font-size: .9rem; }
-  .ss-remote-loading::before { content: ''; width: 16px; height: 16px; border-radius: 50%; flex: 0 0 16px;
-    border: 2px solid color-mix(in srgb, var(--accent, #06c) 30%, transparent); border-top-color: var(--accent, #06c);
-    animation: ss-spin .8s linear infinite; }
-  @keyframes ss-spin { to { transform: rotate(360deg); } }
-  @media (prefers-reduced-motion: reduce) { .ss-remote-loading::before { animation: none; } }
 
   /* Smal scherm: alleen de tekens, gelijk verdeeld over de breedte. De woorden
Index: src/views/partials/remote-preview.ejs
===================================================================
--- src/views/partials/remote-preview.ejs	(revision 9eb6e1bceee7058678085d70abe8ff4dffe8bff8)
+++ 	(revision )
@@ -1,28 +1,0 @@
-<%#
-  De live-preview van een geplakt adres, onder de zoekbalk (shaer-utpi).
-
-  Een los fragment, geen pagina: de zoekbalk haalt dit op terwijl je plakt en
-  zet het in de uitklaplijst. Daarom:
-
-    - GEEN SCRIPT. Wat via innerHTML binnenkomt draait niet, en onder de
-      strenge CSP zou het ook niet mogen. Het gedrag (waarderen en boosten
-      zonder de pagina te verlaten) zit in chrome.js, dat op elke pagina staat.
-    - DE OPMAAK REIST MEE. De overlay staat op elke pagina en die heeft de
-      opmaak van de kaart niet vanzelf; style-src staat inline toe.
-    - COMPACT. Antwoorden en melden gebeuren op de volle pagina: een editor in
-      een uitklaplijst is op een telefoon geen plek om te schrijven.
-
-  Verwacht: remote, remoteKind ('note' | 'actor' | null), remoteUri, reacted,
-  siteTitle.
-%>
-<%- include('./fedi-interact-styles') %>
-<div class="ss-remote">
-  <div class="ss-title"><%= t('search.section_fediverse') %></div>
-  <% if (remote && remoteKind === 'note') { %>
-    <%- include('./remote-object', { target: remote, uri: remoteUri, reacted: reacted, siteTitle: siteTitle, compact: true }) %>
-  <% } else if (remote && remoteKind === 'actor') { %>
-    <%- include('./remote-actor', { actor: remote, siteTitle: siteTitle, compact: true }) %>
-  <% } else { %>
-    <div class="ss-remote-note"><%= t('fedi.remote_notfound') %></div>
-  <% } %>
-</div>
Index: src/views/partials/search-results.ejs
===================================================================
--- src/views/partials/search-results.ejs	(revision fd8faf581c665d69b41dd6f5262d74bf432c0e1b)
+++ src/views/partials/search-results.ejs	(revision fd8faf581c665d69b41dd6f5262d74bf432c0e1b)
@@ -0,0 +1,138 @@
+<%#
+  Wat er onder de zoekbalk staat. EEN render voor beide ingangen: het live
+  resultatenvak (via /search/results) en de pagina /search?q=... zelf. Zo zien
+  ze er per constructie hetzelfde uit; twee renders lopen gegarandeerd uit de
+  pas.
+
+  Verwacht: query, results (posts), tracks, events, pages, queryError,
+  remoteUri, remote, remoteKind, mayLookup, remoteReacted, siteTitle,
+  siteUrlBase, formatDate, t.
+
+  GEEN SCRIPT hierin: het live vak zet dit via innerHTML neer, en daar draait
+  niets. Gedrag (afspelen, waarderen, htmx-links) luistert op document.
+%>
+<%
+  const _base = (typeof siteUrlBase !== 'undefined' && siteUrlBase) ? siteUrlBase : '';
+  const _results = (typeof results !== 'undefined' && results) ? results : [];
+  const _tracks = (typeof tracks !== 'undefined' && tracks) ? tracks : [];
+  const _events = (typeof events !== 'undefined' && events) ? events : [];
+  const _pages = (typeof pages !== 'undefined' && pages) ? pages : [];
+  const _remote = (typeof remoteUri !== 'undefined' && remoteUri) ? remoteUri : '';
+  // Alles via locals: een veld dat een aanroeper vergeet mag de render niet
+  // laten omvallen (een kale verwijzing naar iets ongedefinieerds gooit).
+  const _queryError = !!locals.queryError;
+  const _mayLookup = !!locals.mayLookup;
+  const _found = locals.remote || null;
+  const _kind = locals.remoteKind || null;
+  const _reacted = locals.remoteReacted || { liked: false, boosted: false };
+  const _siteTitle = locals.siteTitle || '';
+  const _inPost = t('search.in_post');
+  const _any = _results.length || _tracks.length || _events.length || _pages.length;
+%>
+<% if (locals.query) { %>
+<div class="sr">
+  <% if (_remote) { %>
+    <%# Een geplakt adres is geen zoekterm maar een aanwijzing (shaer-utpi).
+        Ophalen doen we alleen namens een ingelogde beheerder van de site. %>
+    <section class="sr-section sr-fediverse">
+      <h2 class="sr-title"><%= t('search.section_fediverse') %></h2>
+      <% if (!_mayLookup) { %>
+        <p class="sr-note"><%= t('search.fedi_signin') %></p>
+      <% } else if (_found && _kind === 'note') { %>
+        <%- include('./remote-object', { target: _found, uri: _remote, reacted: _reacted, siteTitle: _siteTitle, compact: true }) %>
+      <% } else if (_found && _kind === 'actor') { %>
+        <%- include('./remote-actor', { actor: _found, siteTitle: _siteTitle, compact: true }) %>
+      <% } else { %>
+        <p class="sr-note"><%= t('fedi.remote_notfound') %></p>
+      <% } %>
+      <% if (_found) { %><%- include('./fedi-interact-styles') %><% } %>
+    </section>
+  <% } %>
+
+  <% if (_queryError) { %>
+    <p class="sr-note"><%= t('search.error') %></p>
+  <% } %>
+
+  <% if (_tracks.length) { %>
+    <section class="sr-section">
+      <h2 class="sr-title"><%= t('search.section_tracks') %></h2>
+      <div class="sr-tracks">
+        <% _tracks.forEach(function (tr) {
+             const tj = JSON.stringify({ id: tr.id, url: tr.url, title: tr.title, artist: tr.artist || '', cover: tr.cover || '' })
+               .replace(/&/g, '&amp;').replace(/'/g, '&#39;').replace(/</g, '&lt;'); %>
+          <div class="post-audio-track sr-track" id="track-<%= tr.id %>"
+               data-pcms-track-id="<%= tr.id %>" data-pcms-track-url="<%= tr.url %>"
+               data-pcms-track='<%- tj %>'>
+            <button type="button" class="pat-play" aria-label="<%= tr.title %>">
+              <svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M8 4l12 8-12 8z"/></svg>
+            </button>
+            <% if (tr.cover) { %>
+              <span class="sr-track-cover" style="background-image:url('<%= tr.cover %>')" aria-hidden="true"></span>
+            <% } %>
+            <div class="pat-info">
+              <div class="pat-title"><%= tr.title %></div>
+              <% if (tr.artist || tr.album) { %>
+                <div class="pat-artist"><%= tr.artist %><% if (tr.artist && tr.album) { %> &middot; <% } %><%= tr.album %></div>
+              <% } %>
+            </div>
+            <% if (tr.postUrl) { %>
+              <a class="sr-track-link" href="<%= tr.postUrl %>"
+                 hx-get="<%= tr.postUrl %>?partial=1" hx-target="#pcms-main" hx-swap="innerHTML" hx-push-url="<%= tr.postUrl %>"><%= _inPost %></a>
+            <% } %>
+          </div>
+        <% }); %>
+      </div>
+    </section>
+  <% } %>
+
+  <% if (_results.length) { %>
+    <section class="sr-section">
+      <h2 class="sr-title"><%= t('search.section_posts') %></h2>
+      <ol class="sr-list">
+        <% _results.forEach(function (r) { %>
+          <li>
+            <a class="sr-item" href="<%= _base %>/<%= r.slug %>"
+               hx-get="<%= _base %>/<%= r.slug %>?partial=1" hx-target="#pcms-main" hx-swap="innerHTML"
+               hx-push-url="<%= _base %>/<%= r.slug %>" hx-indicator="#pcms-loading">
+              <span class="sr-item-title"><%= r.title || '(zonder titel)' %></span>
+              <span class="sr-item-meta"><%= r.author_username %><% if (r.published_at) { %> &middot; <%= formatDate(r.published_at) %><% } %></span>
+              <% if (r.snippet) { %><span class="sr-item-snippet"><%- r.snippet %></span><% } %>
+            </a>
+          </li>
+        <% }); %>
+      </ol>
+    </section>
+  <% } %>
+
+  <% if (_events.length) { %>
+    <section class="sr-section">
+      <h2 class="sr-title"><%= t('search.section_events') %></h2>
+      <ol class="sr-list">
+        <% _events.forEach(function (ev) { %>
+          <li>
+            <a class="sr-item" href="<%= ev.url %>">
+              <span class="sr-item-title"><%= ev.where || ev.date %></span>
+              <span class="sr-item-meta"><%= [ev.date, ev.time].filter(Boolean).join(' ') %></span>
+            </a>
+          </li>
+        <% }); %>
+      </ol>
+    </section>
+  <% } %>
+
+  <% if (_pages.length) { %>
+    <section class="sr-section">
+      <h2 class="sr-title"><%= t('search.section_pages') %></h2>
+      <ul class="sr-list">
+        <% _pages.forEach(function (pg) { %>
+          <li><a class="sr-item sr-item-row" href="<%= pg.url %>"><span class="sr-item-title"><%= pg.label %></span><span aria-hidden="true">→</span></a></li>
+        <% }); %>
+      </ul>
+    </section>
+  <% } %>
+
+  <% if (!_queryError && !_any && !_remote) { %>
+    <p class="sr-empty"><%= t('search.empty') %></p>
+  <% } %>
+</div>
+<% } %>
Index: src/views/partials/search-styles.ejs
===================================================================
--- src/views/partials/search-styles.ejs	(revision fd8faf581c665d69b41dd6f5262d74bf432c0e1b)
+++ src/views/partials/search-styles.ejs	(revision fd8faf581c665d69b41dd6f5262d74bf432c0e1b)
@@ -0,0 +1,70 @@
+<%#
+  De opmaak van het zoeken: het vlak (balk + vak) en wat erin staat. Staat in
+  de topnav, dus op elke pagina: de overlay kan overal open, en de pagina
+  /search gebruikt precies dezelfde regels.
+%>
+<style>
+  /* ── Het vlak: de balk bovenaan, het vak neemt de rest ───────────────────
+     Boven de tabbalk (1050) en de mini-speler (1000), onder een open blad
+     (1100). De vorige overlay lag op 200 en schoof op een telefoon onder de
+     tabbalk, zodat het onderste stuk resultaten wegviel. */
+  .search-surface { position: fixed; inset: 0; z-index: 1060; display: flex; flex-direction: column;
+    background: var(--paper); color: var(--ink); }
+  .search-surface[hidden] { display: none; }
+  .search-bar { flex: 0 0 auto; padding: .75rem 0; padding-top: calc(.75rem + env(safe-area-inset-top, 0px));
+    border-bottom: 1px solid var(--rule);
+    background: color-mix(in srgb, var(--paper) 94%, var(--ink) 6%); }
+  .search-bar-form { display: flex; align-items: center; gap: .5rem; max-width: 760px; margin: 0 auto; padding: 0 1rem; box-sizing: border-box; }
+  .search-bar-form input { flex: 1 1 auto; min-width: 0; min-height: 44px; padding: .7rem 1rem; box-sizing: border-box;
+    border: 1px solid var(--rule); border-radius: 12px; background: var(--paper); color: var(--ink);
+    font-family: var(--font-ui, inherit); font-size: 1rem; }
+  .search-bar-form input:focus { outline: 2px solid var(--accent); outline-offset: 0; border-color: transparent; }
+  .search-bar-btn { flex: 0 0 44px; width: 44px; height: 44px; display: inline-flex; align-items: center; justify-content: center;
+    border: 0; border-radius: 999px; background: transparent; color: var(--ink-soft, var(--ink)); cursor: pointer;
+    text-decoration: none; -webkit-tap-highlight-color: transparent; }
+  .search-bar-btn:hover { background: color-mix(in srgb, var(--ink) 7%, transparent); color: var(--ink); }
+  .search-bar-btn svg { width: 22px; height: 22px; }
+  /* De goot aan beide kanten houdt de kolom op dezelfde lijn als de balk: een
+     scrollbalk alleen rechts schuift de resultaten anders een stukje naar links. */
+  .search-box { flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; -webkit-overflow-scrolling: touch;
+    scrollbar-gutter: stable both-edges; }
+  .search-box-inner { max-width: 760px; margin: 0 auto; padding: .5rem 1rem calc(2rem + env(safe-area-inset-bottom, 0px)); box-sizing: border-box; }
+  .search-box-inner:empty { display: none; }
+  /* Zolang het vlak open is, scrolt de pagina eronder niet mee. */
+  html.search-open, html.search-open body { overflow: hidden; }
+
+  /* ── Wat er in het vak staat ─────────────────────────────────────────── */
+  .sr-section { margin: 1rem 0 0; }
+  .sr-title { margin: 0 0 .35rem; padding: 0 .25rem; font: 700 .72rem/1.4 var(--font-ui, inherit);
+    text-transform: uppercase; letter-spacing: .06em; color: var(--ink-soft, var(--ink-muted)); }
+  .sr-list { list-style: none; margin: 0; padding: 0; }
+  .sr-item { display: flex; flex-direction: column; gap: .1rem; min-height: 44px; padding: .6rem .6rem; border-radius: 10px;
+    color: var(--ink); text-decoration: none; box-sizing: border-box; }
+  .sr-item:hover, .sr-item:focus-visible { background: var(--paper-2); }
+  .sr-item:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
+  .sr-item-row { flex-direction: row; align-items: center; justify-content: space-between; }
+  .sr-item-title { font-weight: 600; overflow-wrap: anywhere; }
+  .sr-item:hover .sr-item-title { color: var(--accent); }
+  .sr-item-meta { font-size: .82rem; color: var(--ink-soft, var(--ink-muted)); }
+  .sr-item-snippet { font-size: .9rem; line-height: 1.5; color: var(--ink-soft); overflow: hidden;
+    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
+  .sr-item-snippet mark { background: color-mix(in srgb, var(--accent) 22%, transparent); color: inherit; padding: 0 .15em; border-radius: 3px; }
+  .sr-tracks { display: flex; flex-direction: column; gap: .15rem; }
+  .sr-track.post-audio-track { display: flex; align-items: center; gap: .75rem; min-height: 44px; padding: .45rem .6rem; border-radius: 10px; }
+  .sr-track-cover { flex: 0 0 38px; width: 38px; height: 38px; border-radius: 6px; background: var(--paper-2) center / cover; }
+  .sr-track .pat-info { flex: 1; min-width: 0; }
+  .sr-track .pat-title { font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
+  .sr-track .pat-artist { font-size: .82rem; color: var(--ink-soft); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
+  .sr-track-link { flex: 0 0 auto; font-size: .82rem; color: var(--accent); text-decoration: none; white-space: nowrap; }
+  .sr-track-link:hover { text-decoration: underline; }
+  .sr-note { margin: .25rem 0; padding: .7rem .8rem; border-radius: 10px; font-size: .9rem; line-height: 1.5;
+    background: var(--paper-2); color: var(--ink-soft, var(--ink-muted)); }
+  .sr-empty { margin: 3rem 0; text-align: center; color: var(--ink-soft, var(--ink-muted)); }
+  .sr-loading { display: flex; align-items: center; gap: 10px; padding: 1rem .6rem; color: var(--ink-soft, var(--ink-muted)); font-size: .9rem; }
+  .sr-loading::before { content: ''; flex: 0 0 16px; width: 16px; height: 16px; border-radius: 50%;
+    border: 2px solid color-mix(in srgb, var(--accent) 30%, transparent); border-top-color: var(--accent);
+    animation: sr-spin .8s linear infinite; }
+  @keyframes sr-spin { to { transform: rotate(360deg); } }
+  @media (prefers-reduced-motion: reduce) { .sr-loading::before { animation: none; } }
+  .sr-section .rp-card { margin-top: .25rem; }
+</style>
Index: src/views/partials/search-surface.ejs
===================================================================
--- src/views/partials/search-surface.ejs	(revision fd8faf581c665d69b41dd6f5262d74bf432c0e1b)
+++ src/views/partials/search-surface.ejs	(revision fd8faf581c665d69b41dd6f5262d74bf432c0e1b)
@@ -0,0 +1,50 @@
+<%#
+  Het zoeken, in twee vormen en verder niets: de zoekbalk bovenaan, en
+  daaronder het vak met resultaten dat alle ruimte krijgt (Robins ontwerp,
+  30-9). Er is geen aparte resultatenpagina meer die er anders uitziet.
+
+  EEN PARTIAL, TWEE INGANGEN:
+    mode 'overlay'  in de topnav, op elke pagina; verborgen tot je op zoeken
+                    tikt. Draagt de id's waar chrome.js hem aan herkent.
+    mode 'page'     /search?q=...: dezelfde vorm, open, met de resultaten al
+                    op de server gerenderd. Een gedeelde link en Enter zonder
+                    JavaScript komen zo op precies hetzelfde beeld uit.
+
+  Verwacht: mode, t, siteUrlBase. Voor 'page' ook query en de resultaten
+  (zie search-results.ejs).
+%>
+<%
+  const _page = locals.mode === 'page';
+  const _base = locals.siteUrlBase || '';
+  const _q = _page ? (locals.query || '') : '';
+%>
+<div class="search-surface<%= _page ? ' is-page' : '' %>"<% if (!_page) { %> id="search-overlay" hidden<% } %>
+     role="search"
+     data-results="<%= _base %>/search/results" data-action="<%= _base %>/search"
+     data-i18n="<%= JSON.stringify({ looking: t('search.fedi_looking') }) %>">
+  <div class="search-bar">
+    <form class="search-bar-form" method="get" action="<%= _base %>/search">
+      <input type="search" name="q" value="<%= _q %>" placeholder="<%= t('search.placeholder') %>"
+             autocomplete="off" aria-label="<%= t('nav.search') %>"<%= (_page && !_q) ? ' autofocus' : '' %>>
+      <button type="submit" class="search-bar-btn" aria-label="<%= t('search.button') %>">
+        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="11" cy="11" r="7"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
+      </button>
+      <% if (_page) { %>
+        <%# Op de pagina is "sluiten" teruggaan waar je vandaan kwam. Zonder JS
+            brengt de link je naar het begin van de site. %>
+        <a class="search-bar-btn" href="<%= _base || '/' %>" data-search-leave aria-label="<%= t('imed.cancel') %>">
+          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
+        </a>
+      <% } else { %>
+        <button type="button" class="search-bar-btn" id="search-close" aria-label="<%= t('imed.cancel') %>">
+          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
+        </button>
+      <% } %>
+    </form>
+  </div>
+  <div class="search-box" data-lenis-prevent>
+    <div class="search-box-inner"<% if (!_page) { %> id="search-suggest"<% } %> aria-live="polite">
+      <% if (_page) { %><%- include('./search-results', locals) %><% } %>
+    </div>
+  </div>
+</div>
Index: src/views/partials/topnav.ejs
===================================================================
--- src/views/partials/topnav.ejs	(revision 9eb6e1bceee7058678085d70abe8ff4dffe8bff8)
+++ src/views/partials/topnav.ejs	(revision fd8faf581c665d69b41dd6f5262d74bf432c0e1b)
@@ -175,22 +175,8 @@
 </header>
 
-<!-- Search overlay (shared by mobile bottom-tab + desktop top-nav) -->
-<%# De teksten staan op het element dat ze gebruikt, niet in het script: een
-    module is een statisch bestand en kan geen vertaling interpoleren. %>
-<div id="search-overlay" class="search-overlay" hidden data-suggest="<%= _siteUrlBase %>/search/suggest" data-remote="<%= _siteUrlBase %>/search/remote" data-action="<%= _siteUrlBase %>/search"
-     data-i18n="<%= JSON.stringify({ posts: t('search.section_posts'), tracks: t('search.section_tracks'), events: t('search.section_events'), pages: t('search.section_pages'), all: t('search.suggest_all'), none: t('search.suggest_empty'), looking: t('search.fedi_looking') }) %>">
-  <div class="container">
-    <form class="search-overlay-form" method="get" action="<%= _siteUrlBase %>/search">
-      <input type="search" name="q" placeholder="<%= t('search.placeholder') %>" autocomplete="off">
-      <button type="submit" class="nav-btn" aria-label="<%= t('search.button') %>">
-        <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="11" cy="11" r="7"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
-      </button>
-      <button type="button" class="nav-btn" id="search-close" aria-label="<%= t('imed.cancel') %>">
-        <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
-      </button>
-    </form>
-    <div id="search-suggest" class="search-suggest" data-lenis-prevent></div>
-  </div>
-</div>
+<!-- Het zoeken (gedeeld door de tabbalk op mobiel en de topnav op desktop) -->
+<%# Balk + vak, dezelfde partial als de pagina /search: zie search-surface.ejs. %>
+<%- include('./search-surface', { mode: 'overlay', t: t, siteUrlBase: _siteUrlBase }) %>
+<%- include('./search-styles') %>
 
 <%# Het script van de topnav staat in assets/js/mod/chrome.js (shaer-bqr). %>
@@ -441,41 +427,5 @@
 }
 
-/* ── Search overlay ── */
-.search-overlay {
-  position: fixed; top: 0; left: 0; right: 0;
-  background: color-mix(in srgb, var(--paper) 96%, transparent);
-  backdrop-filter: blur(16px); -webkit-backdrop-filter: blur(16px);
-  border-bottom: 1px solid var(--rule);
-  padding: 1rem 0; z-index: 200;
-  padding-top: calc(1rem + env(safe-area-inset-top, 0));
-  box-shadow: 0 8px 24px color-mix(in srgb, var(--ink) 8%, transparent);
-}
-.search-overlay-form { display: flex; gap: .5rem; align-items: center; }
-.search-overlay-form input {
-  flex: 1; padding: .85rem 1rem;
-  min-height: 44px;
-  border: 1px solid var(--rule); border-radius: 8px;
-  font-family: var(--font-ui); font-size: 1rem;
-  background: var(--paper); color: var(--ink);
-}
-.search-overlay-form input:focus {
-  outline: 2px solid var(--accent); outline-offset: 0;
-}
-.search-overlay-form .nav-btn {
-  width: 44px; height: 44px;
-  display: inline-flex; align-items: center; justify-content: center;
-}
-/* Live-resultaten dropdown */
-.search-suggest { margin-top: .6rem; max-height: 60vh; max-height: 70dvh; overflow-y: auto; overscroll-behavior: contain; -webkit-overflow-scrolling: touch; display: flex; flex-direction: column; gap: .25rem; }
-.search-suggest:empty { display: none; }
-.ss-group { padding: .25rem 0; }
-.ss-title { font-size: .72rem; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(--ink-soft, var(--ink-muted)); padding: .35rem .25rem .2rem; }
-.ss-item { display: block; padding: .55rem .6rem; border-radius: 8px; color: var(--ink); text-decoration: none; font-size: .95rem; }
-.ss-item:hover { background: var(--paper-2); color: var(--accent); }
-.ss-item.ss-noclick { opacity: .7; cursor: default; }
-.ss-sub { color: var(--ink-soft, var(--ink-muted)); font-size: .85em; }
-.ss-empty { padding: .8rem .6rem; color: var(--ink-soft, var(--ink-muted)); font-size: .9rem; }
-.ss-all { display: block; margin-top: .35rem; padding: .55rem .6rem; border-top: 1px solid var(--rule); color: var(--accent); font-weight: 600; text-decoration: none; font-size: .9rem; }
-.ss-all:hover { text-decoration: underline; }
+/* Het zoeken heeft zijn opmaak in partials/search-styles.ejs. */
 /* Notification bell + unread badge */
 .nav-notif { position: relative; }
Index: src/views/shell.ejs
===================================================================
--- src/views/shell.ejs	(revision 9eb6e1bceee7058678085d70abe8ff4dffe8bff8)
+++ src/views/shell.ejs	(revision fd8faf581c665d69b41dd6f5262d74bf432c0e1b)
@@ -130,5 +130,5 @@
 // ontwikkeling een jaar gecachet, dus zonder bump bereikt een reparatie
 // iedereen behalve wie de fout al heeft.
-const MOD_V = 65;
+const MOD_V = 66;
 %><!DOCTYPE html>
 <html lang="<%- _e(lang) %>" data-palette="<%- _e((typeof palette !== 'undefined' && palette) ? palette : (safeSite.palette || 'klonkt')) %>">
