diff --git a/.gitignore b/.gitignore
index 317506e..f29b2ed 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
node_modules/
*.png
+!website/public/logo.png
!website/public/icon.png
.playwright-mcp/
diff --git a/docs/anchors/chatham-house-rule.adoc b/docs/anchors/chatham-house-rule.adoc
index 50e2f4b..42d1aa4 100644
--- a/docs/anchors/chatham-house-rule.adoc
+++ b/docs/anchors/chatham-house-rule.adoc
@@ -62,9 +62,10 @@ Historical Context:: Originally created to enable diplomats and government offic
[discrete]
== *Example Invocation*:
-----
+[quote]
+____
This retrospective will be conducted under the Chatham House Rule.
You may share the insights we generate, but don't attribute comments
to specific team members.
-----
+____
====
diff --git a/docs/anchors/chatham-house-rule.de.adoc b/docs/anchors/chatham-house-rule.de.adoc
index 5b92f5b..e16fdda 100644
--- a/docs/anchors/chatham-house-rule.de.adoc
+++ b/docs/anchors/chatham-house-rule.de.adoc
@@ -62,9 +62,10 @@ Historischer Kontext:: Ursprünglich geschaffen, um Diplomaten und Regierungsbea
[discrete]
== *Beispielhafte Anwendung*:
-----
+[quote]
+____
Diese Retrospektive wird unter der Chatham House Rule durchgeführt.
Sie dürfen die generierten Erkenntnisse teilen, aber ordnen Sie
Kommentare nicht spezifischen Teammitgliedern zu.
-----
+____
====
diff --git a/docs/plans/2026-03-08-onboarding-modal-design.md b/docs/plans/2026-03-08-onboarding-modal-design.md
new file mode 100644
index 0000000..f905ed5
--- /dev/null
+++ b/docs/plans/2026-03-08-onboarding-modal-design.md
@@ -0,0 +1,69 @@
+# Onboarding Modal Design
+
+**Issue:** #145
+**Date:** 2026-03-08
+
+## Overview
+
+Modal dialog explaining Semantic Anchors to first-time visitors. Combines logo, explainer video (YouTube Shorts), and concise text.
+
+## Components
+
+### 1. `onboarding-modal.js`
+
+New component (separate from anchor-modal.js):
+- `createOnboardingModal()`: Singleton DOM creation
+- `showOnboarding()`: Opens modal, sets `overflow: hidden`
+- `closeOnboarding()`: Closes, saves `localStorage.setItem('onboarding-seen', 'true')`
+- `shouldShowOnboarding()`: Checks `localStorage.getItem('onboarding-seen')`
+
+### 2. Layout
+
+**Desktop:** Logo top, slogan highlighted, video (YouTube embed) left + text right, CTA button bottom.
+
+**Mobile:** Logo top, slogan highlighted, text, YouTube link (no embed), CTA button bottom.
+
+### 3. Header Change
+
+Info icon (i) button next to site title. Calls `showOnboarding()`.
+
+### 4. i18n Keys
+
+New keys in en.json/de.json: `onboarding.slogan1`, `onboarding.slogan2`, `onboarding.text1`-`text4`, `onboarding.cta`, `onboarding.watchVideo`, `onboarding.infoButton`
+
+### 5. Videos
+
+- EN: https://youtube.com/shorts/Fb7t45E8_HE
+- DE: https://youtube.com/shorts/cp-qqiHU-MA
+
+### 6. Text Content
+
+**Slogan:**
+- EN: "Semantic Anchors. One word, and the AI gets the rest."
+- DE: "Semantic Anchors. Ein Wort, und die KI versteht den Rest."
+
+**Body (4 paragraphs, condensed from video scripts):**
+
+EN:
+1. Imagine saying just one word - and your counterpart instantly understands an entire concept.
+2. A Semantic Anchor is an established term that activates an entire body of knowledge. Like an anchor holding a ship in place - a Semantic Anchor pins your conversation to a precise concept.
+3. This works because AI models were trained on millions of texts. Terms like MECE, Clean Architecture, or the Feynman Technique instantly trigger deep contextual knowledge.
+4. Instead of writing long prompts, just use the right anchor - and the AI delivers.
+
+DE:
+1. Stell dir vor, du sagst ein einziges Wort - und dein Gegenüber versteht sofort ein ganzes Konzept.
+2. Ein Semantic Anchor ist ein etablierter Begriff, der ein ganzes Wissensgebiet aktiviert. Wie ein Anker, der ein Schiff an einem festen Punkt hält - so verankert ein Semantic Anchor dein Gespräch an einem präzisen Konzept.
+3. Das funktioniert, weil KI-Modelle auf Millionen von Texten trainiert wurden. Begriffe wie MECE, Clean Architecture oder Feynman-Technik lösen sofort tiefes Kontextwissen aus.
+4. Statt lange Prompts zu schreiben, sagst du einfach den richtigen Anker - und die KI liefert.
+
+### 7. Accessibility
+
+- Focus trap in modal
+- ESC to close
+- `role="dialog"`, `aria-modal="true"`
+
+### 8. Not Included (YAGNI)
+
+- No open/close animation
+- No "don't show again" checkbox
+- No analytics
diff --git a/scripts/sync-anchors.js b/scripts/sync-anchors.js
new file mode 100644
index 0000000..5457b94
--- /dev/null
+++ b/scripts/sync-anchors.js
@@ -0,0 +1,52 @@
+#!/usr/bin/env node
+/**
+ * Sync anchor .adoc files from docs/anchors/ to website/public/docs/anchors/
+ *
+ * Ensures the website always has the latest anchor files available for
+ * client-side rendering in the anchor modal. Runs as a pre-step for
+ * both dev and build.
+ *
+ * Usage: node scripts/sync-anchors.js
+ */
+
+const fs = require('fs')
+const path = require('path')
+
+const ROOT = path.join(__dirname, '..')
+const SRC = path.join(ROOT, 'docs', 'anchors')
+const DEST = path.join(ROOT, 'website', 'public', 'docs', 'anchors')
+
+function sync() {
+ if (!fs.existsSync(SRC)) {
+ console.warn(`[sync-anchors] Source directory not found: ${SRC}`)
+ return
+ }
+
+ fs.mkdirSync(DEST, { recursive: true })
+
+ const srcFiles = fs.readdirSync(SRC).filter((f) => f.endsWith('.adoc'))
+ let copied = 0
+ let skipped = 0
+
+ for (const file of srcFiles) {
+ const srcPath = path.join(SRC, file)
+ const destPath = path.join(DEST, file)
+
+ const srcStat = fs.statSync(srcPath)
+
+ if (fs.existsSync(destPath)) {
+ const destStat = fs.statSync(destPath)
+ if (srcStat.mtimeMs <= destStat.mtimeMs) {
+ skipped++
+ continue
+ }
+ }
+
+ fs.copyFileSync(srcPath, destPath)
+ copied++
+ }
+
+ console.log(`[sync-anchors] ${copied} copied, ${skipped} up-to-date (${srcFiles.length} total)`)
+}
+
+sync()
diff --git a/website/package.json b/website/package.json
index 31ca9ac..ea3d8bf 100644
--- a/website/package.json
+++ b/website/package.json
@@ -4,8 +4,10 @@
"version": "0.1.0",
"type": "module",
"scripts": {
+ "sync-anchors": "node ../scripts/sync-anchors.js",
+ "predev": "node ../scripts/sync-anchors.js",
"dev": "vite",
- "prebuild": "node ../scripts/render-docs.js",
+ "prebuild": "node ../scripts/sync-anchors.js && node ../scripts/render-docs.js",
"build": "vite build",
"preview": "vite preview",
"test": "vitest run",
diff --git a/website/public/logo.png b/website/public/logo.png
new file mode 100644
index 0000000..1939a32
Binary files /dev/null and b/website/public/logo.png differ
diff --git a/website/src/components/anchor-modal.js b/website/src/components/anchor-modal.js
index 2382ce1..a6fba09 100644
--- a/website/src/components/anchor-modal.js
+++ b/website/src/components/anchor-modal.js
@@ -27,7 +27,7 @@ export function createModal() {
const modal = document.createElement('div')
modal.id = 'anchor-modal'
modal.className =
- 'fixed inset-0 bg-black bg-opacity-50 hidden items-center justify-center z-50 p-4'
+ 'fixed inset-0 bg-black/30 backdrop-blur-sm hidden items-center justify-center z-50 p-4'
modal.innerHTML = `
diff --git a/website/src/components/card-grid.js b/website/src/components/card-grid.js
index 1026c79..015e38e 100644
--- a/website/src/components/card-grid.js
+++ b/website/src/components/card-grid.js
@@ -360,9 +360,10 @@ export function applyCardFilters(roleId, searchQuery) {
* Update the anchor counter display
*/
export function updateAnchorCount(visible, total) {
- const visibleCountEl = document.getElementById('visible-count')
- const totalCountEl = document.getElementById('total-count')
-
- if (visibleCountEl) visibleCountEl.textContent = visible
- if (totalCountEl) totalCountEl.textContent = total
+ document.querySelectorAll('#visible-count, #visible-count-mobile').forEach((el) => {
+ el.textContent = visible
+ })
+ document.querySelectorAll('#total-count, #total-count-mobile').forEach((el) => {
+ el.textContent = total
+ })
}
diff --git a/website/src/components/header.js b/website/src/components/header.js
index ef36804..3c6cdfa 100644
--- a/website/src/components/header.js
+++ b/website/src/components/header.js
@@ -5,51 +5,132 @@ export function renderHeader() {
return `
-