Index: src/assets/css/guardian.css
===================================================================
--- src/assets/css/guardian.css	(revision 70677e962e64de43ac4076f9b87a4ccb49017561)
+++ src/assets/css/guardian.css	(revision 742ba7e930b4fb298e2637a195f865b23cc32476)
@@ -9,4 +9,5 @@
   --accent: #ff6b35;
   --ok: #4caf7d;
+  --danger: #e05a5a;   /* alleen voor onomkeerbare stappen, zoals een ward loslaten */
 }
 * { box-sizing: border-box; }
@@ -134,2 +135,12 @@
 .g-empty.small { margin: 0; font-size: .85rem; }
 .tag.help { color: var(--accent); border-color: var(--accent); }
+
+/* Loslaten van een ward: geen browser-confirm maar een stap in het paneel, met
+   de gevolgen erbij. Rustig van kleur; het is een besluit, geen alarm. */
+.g-warn { margin-top: 10px; padding: 12px 14px; border-radius: 10px;
+  border: 1px solid var(--danger); background: color-mix(in srgb, var(--danger) 8%, transparent); }
+.g-warn strong { display: block; margin-bottom: 6px; }
+.g-warn p { margin: 0 0 8px; font-size: .88rem; line-height: 1.45; color: var(--sub); }
+.g-warn p.grave { color: var(--ink); font-weight: 500; }
+.g-warn .row { margin-top: 10px; gap: 8px; }
+button.danger { background: var(--danger); color: #fff; border-color: var(--danger); }
Index: src/assets/js/guardian.js
===================================================================
--- src/assets/js/guardian.js	(revision 70677e962e64de43ac4076f9b87a4ccb49017561)
+++ src/assets/js/guardian.js	(revision 742ba7e930b4fb298e2637a195f865b23cc32476)
@@ -184,4 +184,39 @@
   }
 
+  /**
+   * The second step of releasing a ward: what it does, then yes or no.
+   *
+   * The warning is assembled from what the server found, not from a fixed
+   * sentence, because releasing means two different things (FEP-633c): stepping
+   * down while other guardians remain (§3.3), or being the last one, which is
+   * emancipation and explicitly not one guardian's call (§3.4). And as long as
+   * the Undo does not federate, the ward's server keeps listing you either way
+   * — a guardian has to know that before pressing, not after.
+   */
+  function releaseStep(w, check, host, relBtn) {
+    var uri = w.other_uri;
+    var who = handleOf(uri, w.other_handle);
+    var box = el('div', 'g-warn');
+    box.appendChild(el('strong', null, (T.release_title || 'Release {who}?').replace('{who}', who)));
+    box.appendChild(el('p', null, T.release_effect || ''));
+    if (check.last === true) box.appendChild(el('p', 'grave', T.release_last || ''));
+    else if (check.last === false) box.appendChild(el('p', null, T.release_step_down || ''));
+    else box.appendChild(el('p', 'grave', T.release_unknown || ''));
+    if (check.federates === false) box.appendChild(el('p', null, T.release_local || ''));
+
+    var row = el('div', 'row');
+    var yes = el('button', 'danger small', T.release_yes || 'Yes');
+    yes.addEventListener('click', function () { yes.disabled = true; remove(uri, yes); });
+    var no = el('button', 'small', T.release_no || 'No');
+    no.addEventListener('click', function () {
+      host.removeChild(box);
+      relBtn.hidden = false; relBtn.disabled = false;
+    });
+    // No first: the way out should be the easy one to hit.
+    row.appendChild(no); row.appendChild(yes);
+    box.appendChild(row);
+    return box;
+  }
+
   function wardPanel(w) {
     var uri = w.other_uri;
@@ -215,10 +250,19 @@
     actRow.appendChild(wave);
     var rel = el('button', 'quiet small', T.release);
-    // Releasing a ward is heavy and hard to undo (coming back needs a fresh
-    // offer the ward accepts), so it asks first and spells out what changes.
+    // Letting a child go is a decision, not a click. It opens a step that first
+    // asks the server what releasing this particular ward actually does, then
+    // says it plainly and asks yes or no. Never window.confirm: that hides a
+    // long explanation behind an OK button people press to make it go away.
     rel.addEventListener('click', function () {
-      var who = handleOf(uri, w.other_handle);
-      var msg = (T.release_confirm || 'Release {who}?').replace('{who}', who);
-      if (window.confirm(msg)) remove(uri, rel);
+      rel.disabled = true;
+      // site matters: with several of your own sites the server would otherwise
+      // check this ward against the wrong one and answer "not my ward".
+      fetch('/guardian/wards/release-check?site=' + encodeURIComponent(S.site) + '&uri=' + encodeURIComponent(uri))
+        .then(function (r) { return r.json(); })
+        .then(function (c) {
+          rel.hidden = true;
+          act.appendChild(releaseStep(w, c || {}, act, rel));
+        })
+        .catch(function () { rel.disabled = false; });
     });
     actRow.appendChild(rel);
