diff --git a/README.md b/README.md
index 13f9e43..066a4c2 100644
--- a/README.md
+++ b/README.md
@@ -19,7 +19,7 @@ Route paths are configuration, not files. The layer's page components live in `a
## Features
- **Post list** (`journal-index`): paginated with `?page=N`, or `
//N` when vue-seo-layer's `seo.pagination.form` is `path`. A non-numeric page or a page past the last one answers 404.
-- **Post** (`journal-post`): title, date, reading time, categories, author, featured image, body, previous and next links, and related posts. When the API returns no related posts (a post without a category), the latest posts are shown instead.
+- **Post** (`journal-post`): title, date, reading time, categories, author, featured image, body, previous and next links, and related posts. When the API returns no related posts (a post without a category), the latest posts are shown instead. With the Journal's opt-in `golem15.journal.related_mode: scored`, the related posts are the Journal's ranked ones and the previous and next links are the post's neighbours within its first category (`previous_in_category`, `next_in_category`, server-rendered links with the title in the anchor text); without them, the site-wide `previous_post` and `next_post`.
- **Category** (`journal-category`): name, description and the category's rich HTML content (the pillar text, on page 1 only; the description shows on every page) above its posts. Categories are always requested with `include_empty=1`, so a category without posts still has a page. An unknown category answers 404, which makes the route safe as a root-level catch-all such as `/:category`.
- **Tag** (`journal-tag`) and **author** (`journal-author`) pages. An author resolves by author slug, or by login once the author has a published post; a login URL sets the canonical to the slug URL. Post meta links to the author by `author.slug` (sm-journal-plugin v0.3.1+), falling back to the login on older APIs.
- **Search** (`journal-search`, opt-in): a results page for `?q=` and the `JournalSearchBox` header component with suggestions as the visitor types. Off until the app sets `journal.routes.search`. See [Search](#search).
diff --git a/app/journal-pages/PostPage.vue b/app/journal-pages/PostPage.vue
index b41aa7a..d706379 100644
--- a/app/journal-pages/PostPage.vue
+++ b/app/journal-pages/PostPage.vue
@@ -13,7 +13,7 @@
// The head comes from the CMS through vue-seo-layer; the post's own title,
// excerpt and featured image apply only while there is no CMS answer.
import { cmsHeadPresent, journalPostTitleTag, journalSiteOrigin, postSeoMeta } from '../utils/seo.ts'
-import { demotesH1, toPostView } from '../utils/postView.ts'
+import { demotesH1, postNeighbours, toPostView } from '../utils/postView.ts'
import { journalBreadcrumbsOn, toListPost } from '../utils/view.ts'
import type { JournalModuleOptions } from '../utils/types.ts'
@@ -65,6 +65,9 @@ useSeoMeta({
const titleTag = computed(() => journalPostTitleTag(cfg.postTitleTag, post.value?.content_h1_to_h2))
const demoteH1 = computed(() => demotesH1(post.value))
const crumbs = journalBreadcrumbsOn(cfg.breadcrumbs)
+// previous_in_category / next_in_category (the Journal's opt-in scored
+// mode) win over the site-wide neighbours when the Journal answers one.
+const siblings = computed(() => (post.value ? postNeighbours(post.value) : { previous: null, next: null, inCategory: false }))
@@ -76,22 +79,22 @@ const crumbs = journalBreadcrumbsOn(cfg.breadcrumbs)
-