Index: src/views/pages/messages.ejs
===================================================================
--- src/views/pages/messages.ejs	(revision 2c0c33844b942102315e27fbf3ae0155f374b9a6)
+++ src/views/pages/messages.ejs	(revision 824d1d0ebb0fae955ca5052db3f3489400f082f0)
@@ -127,8 +127,8 @@
      waard. */
   .msg-thread.is-collapsed .msg-thread-msgs,
-  .msg-thread.is-collapsed .msg-thread-foot { display: none; }
+  .msg-thread.is-collapsed .msg-thread-actions { display: none; }
   /* Tijdens het zoeken wint de treffer van de dichtgeklapte stand. */
   .msg-thread.is-collapsed.is-search-open .msg-thread-msgs { display: flex; }
-  .msg-thread.is-collapsed.is-search-open .msg-thread-foot { display: flex; }
+  .msg-thread.is-collapsed.is-search-open .msg-thread-actions { display: flex; }
   .msg-thread.is-collapsed .msg-thread-chevron { transform: rotate(-90deg); }
   .msg-thread.is-collapsed .msg-thread-head { margin-bottom: 0; }
@@ -156,8 +156,11 @@
   .msg-sub .msg-line { flex-wrap: wrap; }
 
-  /* De voet van een gesprek: antwoorden en zwaaien naast elkaar, ingeklapt.
+  /* Antwoorden en zwaaien naast elkaar, ingeklapt, en sinds 25-8 BOVEN de
+     berichten -- daar staat het nieuwste, en daar antwoord je dus op. Heette
+     msg-thread-foot toen het onderaan stond; de marge verhuisde mee van top
+     naar bottom, anders plakt de rij tegen de eerste bubbel.
      Een open editor onder elk gesprek maakt de lijst weer onleesbaar -- precies
      wat deze weergave moest oplossen. */
-  .msg-thread-foot { display: flex; align-items: flex-start; gap: .5rem; margin-top: .5rem; flex-wrap: wrap; }
+  .msg-thread-actions { display: flex; align-items: flex-start; gap: .5rem; margin-bottom: .5rem; flex-wrap: wrap; }
   .msg-reply { flex: 1 1 12rem; min-width: 0; }
   .msg-reply > summary { cursor: pointer; font-size: .82rem; color: var(--ink-soft, #888); }
Index: src/views/partials/msg-item.ejs
===================================================================
--- src/views/partials/msg-item.ejs	(revision 2c0c33844b942102315e27fbf3ae0155f374b9a6)
+++ src/views/partials/msg-item.ejs	(revision 824d1d0ebb0fae955ca5052db3f3489400f082f0)
@@ -50,27 +50,15 @@
             <% } %>
           </div>
-          <%# NIEUWSTE BOVENAAN binnen een gesprek (Robins besluit, 25-8). De
-              LIJST van gesprekken en de meldingen blijven staan zoals ze
-              stonden; alleen binnen een draad leest het van nieuw naar oud, dus
-              wat er net bij kwam staat er meteen en niet onderaan een lange
-              draad.
-
-              Op een KOPIE, want groupConversations sorteert `messages`
-              oplopend en de rest van deze pagina rekent daarop -- `_tnew` en
-              `_tto` hierboven lezen dezelfde array. Een reverse() zonder
-              slice() draait het origineel om en dat werkt door naar alles wat
-              er daarna nog naar kijkt.
-
-              In de MARKUP en niet met column-reverse: die draait alleen het
-              beeld om, terwijl een schermlezer en het toetsenbord de oude
-              volgorde houden. %>
-          <ul class="msg-thread-msgs">
-            <% (n.messages || []).slice().reverse().forEach(function (m) { %><%- include('msg-item', { n: m, seen: _tseen, inThread: true }) %><% }); %>
-          </ul>
-          <%# Antwoorden vanuit de draad zelf. Ingeklapt, want een open editor
-              onder elk gesprek maakt de lijst onleesbaar -- juist het probleem
-              dat deze weergave moest oplossen. De route kiest het pad: een
-              draad aan een post krijgt een reply, een draad aan een persoon een
-              direct bericht. %>
+          <%# Antwoorden vanuit de draad zelf, BOVEN de berichten (Robins
+              besluit, 25-8). Sinds het nieuwste bovenaan staat, is de bovenkant
+              waar het gesprek nu is: antwoorden hoort bij het laatste bericht,
+              en dat staat er direct onder. Onderaan zou de knop bij het OUDSTE
+              bericht komen te staan, en dat is precies waar je niet op
+              antwoordt.
+
+              Nog steeds ingeklapt, want een open editor onder elk gesprek maakt
+              de lijst onleesbaar -- juist het probleem dat deze weergave moest
+              oplossen. De route kiest het pad: een draad aan een post krijgt
+              een reply, een draad aan een persoon een direct bericht. %>
           <%# shaer-r4c: precies de tak die de server ook kiest. /messages/reply
               gaat naar deliverReply als er een interaction+post is (dat is een
@@ -80,5 +68,8 @@
           <% var _kan = canMutate && ((_thread && locals.mayReply !== false) || (!_thread && _rt.actorUri)); %>
           <% if (_kan) { %>
-            <div class="msg-thread-foot">
+            <%# Heette msg-thread-foot toen hij onderaan stond. Een naam die
+                zegt waar iets staat is een leugen zodra het verhuist, dus nu
+                zegt hij wat het IS. %>
+            <div class="msg-thread-actions">
               <details class="msg-reply">
                 <summary><%= t('fedi.reply') %></summary>
@@ -107,4 +98,22 @@
             </div>
           <% } %>
+          <%# NIEUWSTE BOVENAAN binnen een gesprek (Robins besluit, 25-8). De
+              LIJST van gesprekken en de meldingen blijven staan zoals ze
+              stonden; alleen binnen een draad leest het van nieuw naar oud, dus
+              wat er net bij kwam staat er meteen en niet onderaan een lange
+              draad.
+
+              Op een KOPIE, want groupConversations sorteert `messages`
+              oplopend en de rest van deze pagina rekent daarop -- `_tnew` en
+              `_tto` hierboven lezen dezelfde array. Een reverse() zonder
+              slice() draait het origineel om en dat werkt door naar alles wat
+              er daarna nog naar kijkt.
+
+              In de MARKUP en niet met column-reverse: die draait alleen het
+              beeld om, terwijl een schermlezer en het toetsenbord de oude
+              volgorde houden. %>
+          <ul class="msg-thread-msgs">
+            <% (n.messages || []).slice().reverse().forEach(function (m) { %><%- include('msg-item', { n: m, seen: _tseen, inThread: true }) %><% }); %>
+          </ul>
         </li>
 <% } else { %>
Index: test/messages-thread-order.test.js
===================================================================
--- test/messages-thread-order.test.js	(revision 2c0c33844b942102315e27fbf3ae0155f374b9a6)
+++ test/messages-thread-order.test.js	(revision 824d1d0ebb0fae955ca5052db3f3489400f082f0)
@@ -69,2 +69,19 @@
   assert.equal((await render(eigen)).includes('msg-thread-wave'), false);
 });
+
+test('antwoorden en zwaaien staan BOVEN de berichten', async () => {
+  // Sinds het nieuwste bovenaan staat hoort de knop daar ook: onderaan zou hij
+  // bij het OUDSTE bericht komen te staan, en daar antwoord je niet op.
+  const html = await ejs.renderFile(partial, {
+    ...helpers, canMutate: true,
+    n: draad({ replyTo: { actorUri: 'https://pruts.nl/ap/users/bart' } }),
+  });
+  const acties = html.indexOf('msg-thread-actions');
+  const bubbels = html.indexOf('msg-thread-msgs');
+  assert.ok(acties > -1, 'de antwoordrij staat er');
+  assert.ok(bubbels > -1, 'en de berichten ook');
+  assert.ok(acties < bubbels, `antwoordrij boven de berichten, kreeg acties=${acties} bubbels=${bubbels}`);
+  // De zwaai-hand hoort in diezelfde rij, dus ook boven de berichten.
+  const zwaai = html.indexOf('class="msg-wave"');
+  assert.ok(zwaai > -1 && zwaai < bubbels, 'de zwaai-hand staat in de rij erboven');
+});
