Index: src/views/pages/account.ejs
===================================================================
--- src/views/pages/account.ejs	(revision 31680c3360308110ee32435ada564ef21f612bce)
+++ src/views/pages/account.ejs	(revision 746d98a771aa1e4a4241958d88582d516570cf4b)
@@ -113,4 +113,54 @@
     </form>
   </section>
+  <% } %>
+
+  <%# ── PROFIELLINKS ───────────────────────────────────────────
+      Sinds 30-9 hier, bij je profiel, en niet meer op de Appearance-pagina: ze
+      horen bij je naam, bio en foto. In de wardmodus is dat profiel het enige
+      dat een bezoeker van de site ziet. %>
+  <% if (typeof editableSite !== 'undefined' && editableSite && canMutate) { %>
+  <section class="ax-card" id="links">
+    <div class="ax-card-title"><%= t('asite.links') %></div>
+    <p class="form-hint"><%= t('asite.links_hint') %></p>
+    <form action="/account/links" method="post" class="ax-form">
+      <div id="profile-links-rows">
+        <% (profileLinks || []).forEach(function (l) { %>
+          <div class="profile-link-row">
+            <select name="profile_link_platform[]" aria-label="<%= t('acct.link_platform') %>">
+              <% platforms.forEach(function (p) { %>
+                <option value="<%= p.key %>" <%= l.platform === p.key ? 'selected' : '' %>><%= p.label %></option>
+              <% }); %>
+            </select>
+            <input type="text" name="profile_link_url[]" value="<%= l.url %>" placeholder="https://…" required aria-label="<%= t('acct.link_url') %>">
+            <button type="button" class="ax-btn pl-remove" aria-label="<%= t('acct.link_remove') %>">×</button>
+          </div>
+        <% }); %>
+      </div>
+      <template id="profile-link-template">
+        <div class="profile-link-row">
+          <select name="profile_link_platform[]" aria-label="<%= t('acct.link_platform') %>">
+            <% platforms.forEach(function (p) { %>
+              <option value="<%= p.key %>"><%= p.label %></option>
+            <% }); %>
+          </select>
+          <input type="text" name="profile_link_url[]" placeholder="https://…" required aria-label="<%= t('acct.link_url') %>">
+          <button type="button" class="ax-btn pl-remove" aria-label="<%= t('acct.link_remove') %>">×</button>
+        </div>
+      </template>
+      <div class="ax-form-actions">
+        <button type="button" id="profile-link-add" class="ax-btn"><%= t('asite.link_add') %></button>
+        <button type="submit" class="ax-btn ax-btn-primary"><%= t('acct.links_save') %></button>
+      </div>
+    </form>
+  </section>
+  <style>
+    .profile-link-row { display: grid; grid-template-columns: 1fr auto; gap: .5rem; margin-bottom: .5rem; }
+    .profile-link-row select { padding: .45rem .6rem; grid-column: 1 / -1; }
+    .profile-link-row input { min-width: 0; }
+    @media (min-width: 520px) {
+      .profile-link-row { grid-template-columns: 160px 1fr auto; }
+      .profile-link-row select { grid-column: auto; }
+    }
+  </style>
   <% } %>
 
Index: src/views/pages/admin-site-edit.ejs
===================================================================
--- src/views/pages/admin-site-edit.ejs	(revision 31680c3360308110ee32435ada564ef21f612bce)
+++ src/views/pages/admin-site-edit.ejs	(revision 746d98a771aa1e4a4241958d88582d516570cf4b)
@@ -150,36 +150,8 @@
     </fieldset>
 
-    <fieldset class="profile-links-fieldset">
-      <legend><%= t('asite.links') %></legend>
-      <p class="form-hint"><%= t('asite.links_hint') %></p>
-
-      <div id="profile-links-rows">
-        <% (parsedLinks || []).forEach(function(l) { %>
-          <div class="profile-link-row">
-            <select name="profile_link_platform[]">
-              <% platforms.forEach(function(p) { %>
-                <option value="<%= p.key %>" <%= l.platform === p.key ? 'selected' : '' %>><%= p.label %></option>
-              <% }); %>
-            </select>
-            <input type="text" name="profile_link_url[]" value="<%= l.url %>" placeholder="https://…" required>
-            <button type="button" class="btn btn-danger pl-remove">×</button>
-          </div>
-        <% }); %>
-      </div>
-
-      <template id="profile-link-template">
-        <div class="profile-link-row">
-          <select name="profile_link_platform[]">
-            <% platforms.forEach(function(p) { %>
-              <option value="<%= p.key %>"><%= p.label %></option>
-            <% }); %>
-          </select>
-          <input type="text" name="profile_link_url[]" placeholder="https://…" required>
-          <button type="button" class="btn btn-danger pl-remove">×</button>
-        </div>
-      </template>
-
-      <button type="button" id="profile-link-add" class="btn"><%= t('asite.link_add') %></button>
-    </fieldset>
+    <%# De profiellinks staan sinds 30-9 bij je profiel, naast je naam, bio en foto:
+        dat is waar ze bij horen, en in de wardmodus is dat profiel het enige dat
+        een bezoeker ziet. %>
+    <p class="form-hint profile-links-moved"><%= t('asite.links_moved') %> <a href="/account#links"><%= t('asite.links_moved_link') %></a></p>
 
     <%# Aliassen en verhuizen staan sinds 14-8 op /admin/migrate, bij elkaar met
@@ -477,13 +449,3 @@
    button below. Prevents horizontal overflow on phones (fixed 160px column +
    non-shrinkable input together ~447px > screen width). */
-.profile-link-row {
-  display: grid; grid-template-columns: 1fr auto; gap: 0.5rem; margin-bottom: 0.5rem;
-}
-.profile-link-row select { padding: 0.45rem 0.6rem; grid-column: 1 / -1; }
-.profile-link-row input { min-width: 0; }
-@media (min-width: 520px) {
-  .profile-link-row { grid-template-columns: 160px 1fr auto; }
-  .profile-link-row select { grid-column: auto; }
-}
-.profile-links-fieldset details > label { margin-top: 0.4rem; }
 </style>
Index: src/views/pages/ward-profile.ejs
===================================================================
--- src/views/pages/ward-profile.ejs	(revision 746d98a771aa1e4a4241958d88582d516570cf4b)
+++ src/views/pages/ward-profile.ejs	(revision 746d98a771aa1e4a4241958d88582d516570cf4b)
@@ -0,0 +1,145 @@
+<%#
+  Het profiel van een ward, en het enige dat een bezoeker van de site ziet
+  (Robins ontwerp, 30-9; zie middleware/ward-profile.js).
+
+  Omdat dit ALLES is wat er openbaar staat, mag het er verzorgd uitzien: een
+  eigen pagina en geen voorpagina met een lege stroom eronder.
+
+  Dezelfde bronnen als de profielkop (partials/profile-header.ejs), zodat het
+  profiel op geen twee plekken iets anders kan zeggen: naam uit profile_name of
+  title, tekst uit profile_bio, description of tagline, links uit profile_links.
+
+  NERGENS "KIND" OF "WARD". Dat deze account guardians heeft staat al openbaar op
+  de fediverse (shaer:guardians, FEP-633c 2.1); deze pagina hoeft dat niet in
+  koeienletters te herhalen. Wat een bezoeker wel hoort te weten, is dat de
+  berichten er niet voor hem staan -- neutraal gezegd.
+
+  Verwacht: site, wardHandle, wardLinks, platforms_catalog, avatar, t.
+%>
+<%
+  const _name = site.profile_name || site.title || site.slug;
+  const _bio = site.profile_bio || site.description || site.tagline || '';
+  const _photo = site.profile_photo || '';
+  const _catalog = (typeof platforms_catalog !== 'undefined' && platforms_catalog) || {};
+  const _links = (wardLinks || []).filter(function (l) { return l && _catalog[l.platform]; });
+  const _href = function (p, u) {
+    u = String(u || '').trim();
+    if (p === 'email' && u.indexOf('mailto:') !== 0) return 'mailto:' + u;
+    if (p === 'phone' && !/^tel:/i.test(u)) return 'tel:' + u.replace(/[^\d+]/g, '');
+    return u;
+  };
+  const _showFollow = site.slug && (typeof apEnabled === 'undefined' || apEnabled);
+%>
+<div class="wp" aria-labelledby="wp-name">
+  <div class="wp-glow" aria-hidden="true"></div>
+  <article class="wp-card">
+    <div class="wp-photo">
+      <% if (_photo) { %>
+        <img src="<%= avatar(_photo, 320) %>" alt="" width="160" height="160">
+      <% } else { %>
+        <span class="wp-photo-initial" aria-hidden="true"><%= String(_name).charAt(0).toUpperCase() %></span>
+      <% } %>
+    </div>
+
+    <h1 class="wp-name" id="wp-name"><%= _name %></h1>
+    <p class="wp-handle"><span><%= wardHandle %></span></p>
+    <% if (_bio) { %><p class="wp-bio"><%= _bio %></p><% } %>
+
+    <% if (_links.length || _showFollow) { %>
+      <ul class="wp-links">
+        <% if (_showFollow) { %>
+          <li>
+            <%# Opent de volgdialoog uit de profielkop (chrome.js). Een volgverzoek
+                aan deze account gaat eerst langs de guardians (FEP-633c 5.3). %>
+            <button type="button" class="wp-link wp-follow profile-fedi-link" data-fedi-actor-slug="<%= site.slug %>">
+              <span class="wp-link-ico" aria-hidden="true">
+                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><line x1="19" y1="8" x2="19" y2="14"/><line x1="22" y1="11" x2="16" y2="11"/></svg>
+              </span>
+              <span class="wp-link-label"><%= t('fedi.profile_follow') %></span>
+              <span class="wp-link-go" aria-hidden="true">→</span>
+            </button>
+          </li>
+        <% } %>
+        <% _links.forEach(function (l) { var meta = _catalog[l.platform]; var ext = (l.platform !== 'email' && l.platform !== 'phone'); %>
+          <li>
+            <a class="wp-link" href="<%= _href(l.platform, l.url) %>" <% if (ext) { %>target="_blank" rel="noopener noreferrer"<% } %> style="--brand: <%= meta.brand %>">
+              <span class="wp-link-ico" aria-hidden="true"><%- meta.svg %></span>
+              <span class="wp-link-label"><%= meta.label %></span>
+              <span class="wp-link-go" aria-hidden="true"><%= ext ? '↗' : '→' %></span>
+            </a>
+          </li>
+        <% }); %>
+      </ul>
+    <% } %>
+
+    <p class="wp-note">
+      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="4" y="11" width="16" height="10" rx="2"/><path d="M8 11V7a4 4 0 0 1 8 0v4"/></svg>
+      <span><%= t('ward.followers_only', { name: _name }) %></span>
+    </p>
+  </article>
+</div>
+
+<style>
+  /* ── Het profiel, en verder niets ───────────────────────────────────────── */
+  body.on-ward-profile { background: var(--paper); }
+  .wp { position: relative; min-height: calc(100svh - 9rem); display: grid; place-items: center;
+    padding: clamp(2rem, 6vw, 4rem) 1.25rem calc(3rem + env(safe-area-inset-bottom, 0px)); overflow: hidden; isolation: isolate; }
+  /* Een zachte gloed in de accentkleur van de site, achter de kaart. */
+  .wp-glow { position: absolute; inset: -20% -10% auto; height: 70%; z-index: -1; pointer-events: none;
+    background:
+      radial-gradient(40% 55% at 30% 40%, color-mix(in srgb, var(--accent) 28%, transparent), transparent 70%),
+      radial-gradient(35% 50% at 72% 30%, color-mix(in srgb, var(--accent) 16%, var(--ink) 4%), transparent 70%);
+    filter: blur(28px); opacity: .9; }
+  .wp-card { width: 100%; max-width: 30rem; text-align: center; display: flex; flex-direction: column; align-items: center; }
+
+  .wp-photo { position: relative; width: 9.5rem; height: 9.5rem; border-radius: 50%; padding: 5px;
+    background: conic-gradient(from 210deg, var(--accent), color-mix(in srgb, var(--accent) 35%, var(--paper)), var(--accent));
+    box-shadow: 0 18px 50px color-mix(in srgb, var(--accent) 30%, transparent); }
+  .wp-photo img, .wp-photo-initial { display: block; width: 100%; height: 100%; border-radius: 50%; object-fit: cover;
+    border: 4px solid var(--paper); box-sizing: border-box; background: var(--paper-2, var(--paper)); }
+  .wp-photo-initial { display: grid; place-items: center; font: 700 3.5rem/1 var(--font-display, var(--font-ui, inherit)); color: var(--accent); }
+
+  .wp-name { margin: 1.4rem 0 .35rem; font-family: var(--font-display, inherit); font-size: clamp(2rem, 7vw, 2.8rem);
+    line-height: 1.1; letter-spacing: -.01em; color: var(--ink); overflow-wrap: anywhere; }
+  .wp-handle { margin: 0; }
+  .wp-handle span { display: inline-block; max-width: 100%; padding: .3rem .8rem; border-radius: 999px;
+    background: color-mix(in srgb, var(--ink) 6%, transparent); color: var(--ink-soft, var(--ink)); font-size: .9rem;
+    user-select: all; -webkit-user-select: all; overflow-wrap: anywhere; }
+  .wp-bio { margin: 1.1rem 0 0; font-size: 1.05rem; line-height: 1.6; color: var(--ink); max-width: 26rem; white-space: pre-line; }
+
+  /* De links: brede knoppen, zoals een link-in-bio. */
+  .wp-links { list-style: none; margin: 1.8rem 0 0; padding: 0; width: 100%; display: flex; flex-direction: column; gap: .6rem; }
+  .wp-link { display: flex; align-items: center; gap: .85rem; width: 100%; min-height: 3.25rem; padding: .55rem .9rem .55rem .6rem;
+    box-sizing: border-box; border-radius: 16px; border: 1px solid var(--rule); background: color-mix(in srgb, var(--paper) 88%, var(--ink) 4%);
+    color: var(--ink); text-decoration: none; font: inherit; font-weight: 600; text-align: left; cursor: pointer;
+    transition: transform .15s ease, border-color .15s ease, box-shadow .15s ease; -webkit-tap-highlight-color: transparent; }
+  .wp-link:hover, .wp-link:focus-visible { transform: translateY(-1px); border-color: color-mix(in srgb, var(--brand, var(--accent)) 55%, var(--rule));
+    box-shadow: 0 8px 22px color-mix(in srgb, var(--brand, var(--accent)) 18%, transparent); }
+  .wp-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
+  .wp-link-ico { flex: 0 0 2.2rem; width: 2.2rem; height: 2.2rem; border-radius: 12px; display: grid; place-items: center;
+    background: var(--brand, var(--accent)); color: #fff; }
+  .wp-link-ico svg { width: 1.2rem; height: 1.2rem; fill: currentColor; }
+  .wp-follow .wp-link-ico { background: var(--accent); }
+  .wp-follow .wp-link-ico svg { fill: none; }
+  .wp-link-label { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
+  .wp-link-go { flex: 0 0 auto; color: var(--ink-soft, var(--ink)); opacity: .7; }
+
+  /* Loopt de zin over twee regels, dan hoort hij naast het slotje te beginnen en
+     niet gecentreerd ver ervan af -- vandaar links uitgelijnd en een ronding die
+     ook bij twee regels klopt. */
+  .wp-note { display: inline-flex; align-items: center; gap: .6rem; max-width: 24rem; margin: 2rem 0 0; padding: .6rem .95rem;
+    border-radius: 14px; text-align: left; font-size: .85rem; line-height: 1.45;
+    color: var(--ink-soft, var(--ink)); background: color-mix(in srgb, var(--ink) 5%, transparent); }
+  .wp-note svg { flex: 0 0 1rem; width: 1rem; height: 1rem; }
+
+  /* Rustig binnenkomen; niet voor wie minder beweging wil. */
+  @media (prefers-reduced-motion: no-preference) {
+    .wp-card > * { animation: wp-in .5s cubic-bezier(.2, .7, .2, 1) both; }
+    .wp-card > :nth-child(2) { animation-delay: .05s; }
+    .wp-card > :nth-child(3) { animation-delay: .1s; }
+    .wp-card > :nth-child(4) { animation-delay: .15s; }
+    .wp-card > :nth-child(5) { animation-delay: .2s; }
+    .wp-card > :nth-child(6) { animation-delay: .25s; }
+    @keyframes wp-in { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
+  }
+</style>
