Index: src/routes/circle.js
===================================================================
--- src/routes/circle.js	(revision 1485933f97b84aac69830de1bf053cabba6875f8)
+++ src/routes/circle.js	(revision 520e477cd65e021f65aa5aa7c92fa6a4edc26081)
@@ -42,9 +42,15 @@
 }
 
+const CIRKEL_PAGE = 72; // matches FEED_PAGE in posts.js: divisible by 2/3/4
+
 router.get('/cirkel', (req, res, next) => {
   const site = res.locals.site;
   if (!site || !apEnabled() || (ActivityPubService.autoBoostCount(site.slug) === 0 && ActivityPubService.boostedCount(site.slug) === 0)) return next();
 
-  const posts = ActivityPubService.getCirkelPosts(site.slug, 80).map((r) => {
+  const append = req.query.append === '1';
+  const offset = Math.max(0, parseInt(req.query.offset, 10) || 0);
+  const rows = ActivityPubService.getCirkelPosts(site.slug, CIRKEL_PAGE + 1, offset);
+  const hasMore = rows.length > CIRKEL_PAGE;
+  const posts = rows.slice(0, CIRKEL_PAGE).map((r) => {
     const text = tidySnippet(htmlToText(r.content));
     // Show ONLY the title (the bold first line a Klonkt note carries), not the whole
@@ -77,8 +83,16 @@
   });
 
+  const moreBase = res.locals.siteUrlBase || '';
+  if (append) {
+    return renderPage(req, res, 'partials/home-append', { posts, hasMore, nextOffset: offset + CIRKEL_PAGE, moreBase, morePath: '/cirkel' });
+  }
+
   const sites = ActivityPubService.getCirkelMembers(site.slug)
     .map((s) => ({ name: s.name || 'Onbekend', url: safeUrl(s.url), avatar: safeUrl(s.icon) }));
 
-  renderPage(req, res, 'pages/circle-feed', { pageTitle: 'Cirkel', bodyClass: 'on-cirkel', posts, sites });
+  renderPage(req, res, 'pages/circle-feed', {
+    pageTitle: 'Cirkel', bodyClass: 'on-cirkel', posts, sites,
+    hasMore, nextOffset: offset + CIRKEL_PAGE, moreBase,
+  });
 });
 
Index: src/routes/posts.js
===================================================================
--- src/routes/posts.js	(revision 1485933f97b84aac69830de1bf053cabba6875f8)
+++ src/routes/posts.js	(revision 520e477cd65e021f65aa5aa7c92fa6a4edc26081)
@@ -95,4 +95,8 @@
 const router = express.Router();
 
+// Feed page size for "Load more" (Solo, News, Messages, Cirkel). 72 is divisible
+// by 2/3/4 so every grid column count ends on a full row.
+const FEED_PAGE = 72;
+
 // ==================== UPLOAD IMAGE (cover or content) ====================
 // Returns JSON {url} so the editor can stick it into the cover field or
@@ -210,12 +214,21 @@
   `).all(site.id);
 
-  // Regular posts: anything with pinned = 0
-  const posts = db.prepare(`
+  // Regular posts: anything with pinned = 0. Paged in blocks of 72 (Load more).
+  const append = req.query.append === '1';
+  const offset = Math.max(0, parseInt(req.query.offset, 10) || 0);
+  const rows = db.prepare(`
     SELECT p.*, u.username as author_username
     FROM posts p JOIN users u ON p.author_id = u.id
     WHERE p.site_id = ? AND p.status = 'published' AND p.pinned = 0
     ORDER BY p.published_at DESC
-    LIMIT 30
-  `).all(site.id);
+    LIMIT ? OFFSET ?
+  `).all(site.id, FEED_PAGE + 1, offset);
+  const hasMore = rows.length > FEED_PAGE;
+  const posts = rows.slice(0, FEED_PAGE);
+  const moreBase = res.locals.siteUrlBase || '';
+
+  if (append) {
+    return renderPage(req, res, 'partials/home-append', { posts, hasMore, nextOffset: offset + FEED_PAGE, moreBase });
+  }
 
   recordPageview(site.id, req);
@@ -224,4 +237,5 @@
     pinnedPosts,
     posts,
+    hasMore, nextOffset: offset + FEED_PAGE, moreBase,
     pageTitle: site.title,
     socialDescr: site.description || site.tagline || '',
@@ -893,5 +907,4 @@
 }
 
-const FEED_PAGE = 72; // divisible by 2/3/4 → every grid column count keeps full rows
 router.get('/news', requireSiteManager, (req, res) => {
   const site = res.locals.site;
Index: src/services/ActivityPubService.js
===================================================================
--- src/services/ActivityPubService.js	(revision 1485933f97b84aac69830de1bf053cabba6875f8)
+++ src/services/ActivityPubService.js	(revision 520e477cd65e021f65aa5aa7c92fa6a4edc26081)
@@ -2245,5 +2245,5 @@
   try { if (!_abCount) _abCount = db.prepare('SELECT COUNT(*) AS n FROM ap_following WHERE slug = ? AND auto_boost = 1'); return _abCount.get(slug).n; } catch { return 0; }
 }
-export function getCirkelPosts(slug, limit) {
+export function getCirkelPosts(slug, limit, offset) {
   try {
     // Cirkel = posts from featured (auto_boost) accounts + posts you boosted
@@ -2256,6 +2256,6 @@
       WHERE t.slug = ? AND (f.auto_boost = 1 OR t.boosted = 1)
       ORDER BY COALESCE(t.published, t.created_at) DESC, t.rowid DESC
-      LIMIT ?`);
-    return _cirkelPosts.all(slug, limit || 60);
+      LIMIT ? OFFSET ?`);
+    return _cirkelPosts.all(slug, limit || 60, offset || 0);
   } catch { return []; }
 }
Index: src/views/pages/circle-feed.ejs
===================================================================
--- src/views/pages/circle-feed.ejs	(revision 1485933f97b84aac69830de1bf053cabba6875f8)
+++ src/views/pages/circle-feed.ejs	(revision 520e477cd65e021f65aa5aa7c92fa6a4edc26081)
@@ -67,5 +67,5 @@
   <!-- ===== TIMELINE (standaard) ===== -->
   <div class="container feed-timeline">
-    <ul class="post-list feed-timeline-list">
+    <ul class="post-list feed-timeline-list" id="post-list">
       <% posts.forEach(function(post){ %>
         <%- include('../partials/post-card', { post: post }) %>
@@ -76,5 +76,5 @@
   <!-- ===== GRID (body[data-feed-view="grid"]) ===== -->
   <section class="feed-grid container" aria-label="<%= t('cfeed.grid_view') %>">
-    <div class="grid-tiles">
+    <div class="grid-tiles" id="grid-tiles">
       <% posts.forEach(function(post){ %>
         <%- include('../partials/post-tile', { post: post }) %>
@@ -82,4 +82,10 @@
     </div>
   </section>
+
+  <% if (typeof hasMore !== 'undefined') { %>
+  <div class="container feed-more-wrap">
+    <%- include('../partials/load-more', { hasMore: hasMore, nextOffset: nextOffset, moreBase: moreBase, moreTarget: '#post-list', morePath: '/cirkel' }) %>
+  </div>
+  <% } %>
 
 <% } %>
Index: src/views/pages/home.ejs
===================================================================
--- src/views/pages/home.ejs	(revision 1485933f97b84aac69830de1bf053cabba6875f8)
+++ src/views/pages/home.ejs	(revision 520e477cd65e021f65aa5aa7c92fa6a4edc26081)
@@ -36,5 +36,5 @@
     </div>
   <% } else { %>
-    <ul class="post-list feed-timeline-list">
+    <ul class="post-list feed-timeline-list" id="post-list">
       <% allPosts.forEach(function(post) { %>
         <%- include('../partials/post-card', { post: post }) %>
@@ -48,5 +48,5 @@
 <% if (allPosts.length > 0) { %>
 <section class="feed-grid container" aria-label="<%= t('phome.grid_view') %>">
-  <div class="grid-tiles">
+  <div class="grid-tiles" id="grid-tiles">
     <% allPosts.forEach(function(post) { %>
       <%- include('../partials/post-tile', { post: post }) %>
@@ -55,2 +55,7 @@
 </section>
 <% } %>
+<% if (typeof hasMore !== 'undefined' && allPosts.length > 0) { %>
+<div class="container feed-more-wrap">
+  <%- include('../partials/load-more', { hasMore: hasMore, nextOffset: nextOffset, moreBase: moreBase, moreTarget: '#post-list', morePath: '/' }) %>
+</div>
+<% } %>
Index: src/views/partials/home-append.ejs
===================================================================
--- src/views/partials/home-append.ejs	(revision 520e477cd65e021f65aa5aa7c92fa6a4edc26081)
+++ src/views/partials/home-append.ejs	(revision 520e477cd65e021f65aa5aa7c92fa6a4edc26081)
@@ -0,0 +1,6 @@
+<% posts.forEach(function(post){ %><%- include('post-card', { post: post }) %><% }); %>
+<%# Grid view: OOB-append the same posts as tiles. htmx inserts the wrapper node
+    itself (beforeend), so display:contents lets the tiles lay out as direct grid
+    items instead of nesting under an extra box. %>
+<div hx-swap-oob="beforeend:#grid-tiles" style="display:contents"><% posts.forEach(function(post){ %><%- include('post-tile', { post: post }) %><% }); %></div>
+<%- include('load-more', { hasMore: hasMore, nextOffset: nextOffset, moreBase: moreBase, moreTarget: '#post-list', morePath: (typeof morePath !== 'undefined' ? morePath : '/'), oob: true }) %>
