Index: src/assets/css/reply-editor.css
===================================================================
--- src/assets/css/reply-editor.css	(revision 33e1dbd11a4cb4638e01c26e3f3c98e352e8892e)
+++ src/assets/css/reply-editor.css	(revision feced2ce628d8bee7974f77404806f4be6f2272b)
@@ -30,4 +30,23 @@
 }
 
+/* Media attachments (chips under the editor). */
+.re-attachments { display: flex; flex-wrap: wrap; gap: .4rem; }
+.re-att {
+  display: inline-flex; align-items: center; gap: .35rem;
+  padding: .25rem .4rem; border-radius: 8px; font-size: .8rem;
+  background: color-mix(in srgb, var(--ink, #000) 7%, transparent);
+  max-width: 100%; overflow: hidden;
+}
+.re-att img { width: 44px; height: 44px; object-fit: cover; border-radius: 6px; display: block; }
+.re-att-del { border: none; background: none; color: inherit; font-size: 1rem; line-height: 1; cursor: pointer; padding: 0 .15rem; }
+.re-att-err { background: color-mix(in srgb, #c00 18%, transparent); }
+.re-editor.re-drop { border-color: var(--accent, #06c); border-style: dashed; }
+
+/* Media on a sent reply in the thread (fedi-node). */
+.re-reply-media { display: flex; flex-wrap: wrap; gap: .5rem; margin-top: .4rem; }
+.re-reply-media img { max-width: min(260px, 100%); max-height: 260px; border-radius: 10px; display: block; }
+.re-reply-media audio { max-width: 100%; }
+.re-reply-media video { max-width: min(320px, 100%); border-radius: 10px; }
+
 /* Full-screen compose (mobile). JS toggles .re-full on the form. */
 .re-head { display: flex; align-items: center; gap: .6rem; }
Index: src/assets/js/reply-editor.js
===================================================================
--- src/assets/js/reply-editor.js	(revision 33e1dbd11a4cb4638e01c26e3f3c98e352e8892e)
+++ src/assets/js/reply-editor.js	(revision feced2ce628d8bee7974f77404806f4be6f2272b)
@@ -32,4 +32,58 @@
     if (lang) lang.hidden = false;
 
+    // ── Media attachments: picker (📎), drag/drop and paste ──────────────
+    var attWrap = form.querySelector('.re-attachments');
+    var attField = form.querySelector('input[name="attachments"]');
+    var fileInput = form.querySelector('.re-file');
+    var attachments = [];
+
+    function syncAtt() {
+      attField.value = attachments.length ? JSON.stringify(attachments) : '';
+      attWrap.hidden = attachments.length === 0;
+    }
+    function addChip(a) {
+      var chip = document.createElement('span');
+      chip.className = 're-att';
+      if (a.mediaType.indexOf('image/') === 0) {
+        var img = document.createElement('img');
+        img.src = a.url; img.alt = a.name || '';
+        chip.appendChild(img);
+      } else {
+        chip.appendChild(document.createTextNode((a.mediaType.indexOf('audio/') === 0 ? '🎵 ' : '🎬 ') + (a.name || a.mediaType)));
+      }
+      var del = document.createElement('button');
+      del.type = 'button'; del.className = 're-att-del'; del.textContent = '×';
+      del.addEventListener('click', function () {
+        attachments = attachments.filter(function (x) { return x !== a; });
+        chip.remove(); syncAtt();
+      });
+      chip.appendChild(del);
+      attWrap.appendChild(chip);
+    }
+    function uploadFiles(files) {
+      Array.prototype.forEach.call(files, function (file) {
+        if (!/^(image|audio|video)\//.test(file.type) || attachments.length >= 4) return;
+        var chip = document.createElement('span');
+        chip.className = 're-att re-att-busy';
+        chip.textContent = '⏳ ' + file.name;
+        attWrap.hidden = false;
+        attWrap.appendChild(chip);
+        var fd = new FormData();
+        fd.append('media', file);
+        fetch(form.getAttribute('data-upload'), { method: 'POST', body: fd })
+          .then(function (r) { return r.json().then(function (j) { return r.ok ? j : Promise.reject(j); }); })
+          .then(function (j) {
+            chip.remove();
+            var a = { url: j.url, mediaType: j.mediaType, name: j.name || file.name };
+            attachments.push(a); addChip(a); syncAtt();
+          })
+          .catch(function (err) {
+            chip.className = 're-att re-att-err';
+            chip.textContent = (form.getAttribute('data-upload-err') || 'Upload failed') + (err && err.error ? ': ' + err.error : '');
+            setTimeout(function () { chip.remove(); syncAtt(); }, 5000);
+          });
+      });
+    }
+
     // Toolbar commands (execCommand is deprecated-but-universal; same approach
     // as the post editor).
@@ -38,6 +92,7 @@
       if (!btn) return;
       e.preventDefault();
+      var cmd = btn.getAttribute('data-cmd');
+      if (cmd === 'attach') { fileInput.click(); return; }   // no editor focus: keeps the picker usable on mobile
       ed.focus();
-      var cmd = btn.getAttribute('data-cmd');
       if (cmd === 'bold') document.execCommand('bold');
       else if (cmd === 'italic') document.execCommand('italic');
@@ -49,12 +104,38 @@
       }
     });
+    fileInput.addEventListener('change', function () {
+      uploadFiles(fileInput.files);
+      fileInput.value = '';
+    });
 
-    // Paste as plain text (rich paste becomes messy HTML; formatting is what
-    // the toolbar is for). Media paste/drop lands in the media phase.
+    // Paste: files become attachments; text pastes as plain text (rich paste
+    // becomes messy HTML; formatting is what the toolbar is for).
     ed.addEventListener('paste', function (e) {
-      var txt = (e.clipboardData || window.clipboardData).getData('text/plain');
+      var cd = e.clipboardData || window.clipboardData;
+      if (cd.files && cd.files.length) {
+        e.preventDefault();
+        uploadFiles(cd.files);
+        return;
+      }
+      var txt = cd.getData('text/plain');
       if (!txt) return;
       e.preventDefault();
       document.execCommand('insertText', false, txt);
+    });
+
+    // Drag/drop media onto the editor.
+    ed.addEventListener('dragover', function (e) {
+      if (e.dataTransfer && Array.prototype.some.call(e.dataTransfer.types || [], function (t) { return t === 'Files'; })) {
+        e.preventDefault();
+        ed.classList.add('re-drop');
+      }
+    });
+    ed.addEventListener('dragleave', function () { ed.classList.remove('re-drop'); });
+    ed.addEventListener('drop', function (e) {
+      ed.classList.remove('re-drop');
+      if (e.dataTransfer && e.dataTransfer.files && e.dataTransfer.files.length) {
+        e.preventDefault();
+        uploadFiles(e.dataTransfer.files);
+      }
     });
 
@@ -72,10 +153,10 @@
     if (cancel) cancel.addEventListener('click', function () { setFull(false); });
 
-    // Serialize on submit; block truly empty replies.
+    // Serialize on submit; block truly empty replies (media-only is fine).
     form.addEventListener('submit', function (e) {
       var html = ed.innerHTML.trim();
       var plain = (ed.innerText || '').replace(/ /g, ' ').trim();
-      if (!plain) { e.preventDefault(); ed.focus(); return; }
-      hidden.value = html;
+      if (!plain && !attachments.length) { e.preventDefault(); ed.focus(); return; }
+      hidden.value = plain ? html : '';
       ta.value = plain;
       document.documentElement.classList.remove('re-lock');
