diff --git a/.sync/log/64b9f7dd93863370ada09dc3be31bb05a8a5a4ce.md b/.sync/log/64b9f7dd93863370ada09dc3be31bb05a8a5a4ce.md
new file mode 100644
index 000000000..da4140f0d
--- /dev/null
+++ b/.sync/log/64b9f7dd93863370ada09dc3be31bb05a8a5a4ce.md
@@ -0,0 +1,52 @@
+# Port: feat(ContentToc): scroll list independently and center active link (#6697)
+
+**Upstream:** `64b9f7dd93863370ada09dc3be31bb05a8a5a4ce` (nuxt/ui)
+**Decision:** port (adapted to b24ui's TOC layout)
+
+## Upstream change
+On desktop the ContentToc list becomes independently scrollable (the outer
+sticky wrapper stops scrolling — `lg:overflow-hidden` — and the inner content
+list scrolls with `lg:overflow-y-auto`), and the active link is kept centered:
+an `activeIndex` computed is extracted from `indicatorStyle`, and a
+`watch(activeIndex)` scrolls the content container (not the page) to center the
+active link on change. A scroll shadow (`useScrollShadow`) is applied to the
+content. Theme adds `flex flex-col` + `overflow` handling on root, `shrink-0`
+on the fixed rows, `min-h-0` on the flexing ones, and `overflow-y-auto` on the
+content. `[...slug].vue` additionally enables `highlight`/`circuit`.
+
+## b24ui port — adapted
+b24ui's ContentToc already diverges (no indicator/highlight machinery — those
+are `@memo not supported`), and its sticky/`max-h`/`overflow-y-auto` wrapper
+styling lives on the docs `class` prop, not the theme `root` (which was empty).
+The independent-scroll + centering feature is orthogonal to the indicator, so it
+ports cleanly:
+
+- `src/runtime/components/content/ContentToc.vue` — added `useScrollShadow`
+ import, `contentRef` + `scrollShadowStyle`, the `activeIndex` computed, and the
+ centering `watch` (verbatim; b24ui links already carry `data-slot="link"`).
+ The desktop content `
` gets `ref="contentRef"` + `:style="scrollShadowStyle"`.
+- `src/theme/content/content-toc.ts` — `root` → `flex flex-col lg:overflow-hidden`
+ (b24ui's docs `class` keeps the sticky/`max-h`/mobile `overflow-y-auto`);
+ `container` +`lg:min-h-0`; `top`/`bottom`/`trigger` +`lg:shrink-0`; `content`
+ +`lg:min-h-0 lg:overflow-y-auto`.
+
+### Deviation — scrollbar utility
+Upstream hides the list scrollbar with `lg:scrollbar-none`. b24ui has no
+`scrollbar-none` utility — its scrollbar helpers (`air-design-tokens/components/
+scrollbar.css`) are `scrollbar-thin` / `scrollbar-transparent`. Used
+`lg:scrollbar-thin lg:scrollbar-transparent` (b24ui's idiom, matching the sidebar
+and the docs ContentToc wrapper) instead.
+
+### Not ported
+`docs/app/pages/docs/[...slug].vue` — the only hunk adds `highlight`
+`highlight-variant="circuit"`, which b24ui explicitly does not support. No change.
+
+## Tests
+No component spec changes upstream. 16 ContentToc snapshots updated — class-only
+(root gains `flex flex-col lg:overflow-hidden`; content gains the `lg:` overflow/
+scrollbar classes; rows gain `lg:shrink-0`/`lg:min-h-0`). `scrollShadowStyle` is
+`undefined` when not overflowing, so no inline style appears in snapshots. Suite
+5309 passed / 6 skipped.
+
+## Verify (CI=true)
+`dev:prepare` · `lint` · `typecheck` · `test` · `build` — all green.
diff --git a/.sync/nuxt-ui.json b/.sync/nuxt-ui.json
index e59305059..0e480c9e5 100644
--- a/.sync/nuxt-ui.json
+++ b/.sync/nuxt-ui.json
@@ -2,7 +2,7 @@
"upstream": "nuxt/ui",
"branch": "v4",
"sync_enabled": false,
- "cursor": "1a3a9dc35b6a4e7a3aea57d93532b155de51c0ec",
+ "cursor": "64b9f7dd93863370ada09dc3be31bb05a8a5a4ce",
"_cursor_note": "cursor = last upstream commit ported into b24ui (oldest-first, manual cadence). sync_enabled stays false until Phase 2 (porter workflow #67 + CLAUDE_CODE_OAUTH_TOKEN) is wired and trusted. `processed` is maintained per port from now on (backfilled #68-#72 on 2026-06-09).",
"stats": {
"queue_depth": 0,
@@ -947,10 +947,16 @@
"summary": "fix(inertia): make useRoute().fullPath reactive across navigations (#6696) — inertia useRoute stub returned fullPath: page.url (captured once, stale across navigations). Change to getter get fullPath() { return page.url }. b24ui: src/runtime/vue/stubs/inertia.ts matched, applied verbatim. Direct 1:1. Runtime stub, no snapshot churn. Tests 5289"
},
"1a3a9dc35b6a4e7a3aea57d93532b155de51c0ec": {
+ "pr": 257,
+ "b24ui_sha": "5f789029ecb613c900b1b33bc6dcac332eab8b71",
+ "decision": "port",
+ "summary": "feat(ChatTool): add actions prop for tool approval (#6699) — ChatTool gains actions?: ButtonProps[] rendering a button row below the trigger + actions slot, auto-opens once when actions appear (uncontrolled, respects defaultOpen). Theme: actions slot (inline/card), chevron.leading->'' with group-hover:opacity-0 moved into alone.false.leadingIcon. ai.ts: isToolApprovalPending() + isToolStreaming treats approval-requested as paused (pure string helpers, harmless on v6). Ported component/theme/util/docs/playgrounds/tests. DEFERRED the v7 tool-approval flow (chat.md, chat.vue, chat.post.ts, ChatToolApprovalExample.vue — toolApproval/addToolApprovalResponse). Skipped chat-message.ts body:w-full (b24ui has no naked variant). Tests 5309."
+ },
+ "64b9f7dd93863370ada09dc3be31bb05a8a5a4ce": {
"pr": null,
"b24ui_sha": "pending-merge",
"decision": "port",
- "summary": "feat(ChatTool): add actions prop for tool approval (#6699) — ChatTool gains actions?: ButtonProps[] rendering a button row below the trigger + actions slot, auto-opens once when actions appear (uncontrolled, respects defaultOpen). Theme: actions slot (inline/card), chevron.leading->'' with group-hover:opacity-0 moved into alone.false.leadingIcon. ai.ts: isToolApprovalPending() + isToolStreaming treats approval-requested as paused (pure string helpers, harmless on v6). Ported component/theme/util/docs/playgrounds/tests. DEFERRED the v7 tool-approval flow (chat.md, chat.vue, chat.post.ts, ChatToolApprovalExample.vue — toolApproval/addToolApprovalResponse). Skipped chat-message.ts body:w-full (b24ui has no naked variant). Tests 5309."
+ "summary": "feat(ContentToc): scroll list independently and center active link (#6697) — desktop TOC list scrolls independently (root lg:overflow-hidden + content lg:overflow-y-auto) and active link is centered via activeIndex computed + watch scrolling contentRef; useScrollShadow on content. b24ui adapted: sticky/max-h stays on docs class prop; theme root flex flex-col lg:overflow-hidden, container/top/bottom/trigger get min-h-0/shrink-0, content lg:overflow-y-auto. Deviation: no scrollbar-none in b24ui -> lg:scrollbar-thin lg:scrollbar-transparent. Not ported: [...slug].vue highlight/circuit (b24ui unsupported). 16 snapshots class-only. Tests 5309."
}
}
}
diff --git a/src/runtime/components/content/ContentToc.vue b/src/runtime/components/content/ContentToc.vue
index bd0486ef5..68df89fe7 100644
--- a/src/runtime/components/content/ContentToc.vue
+++ b/src/runtime/components/content/ContentToc.vue
@@ -58,13 +58,14 @@ export interface ContentTocSlots {