diff --git a/services/frontend/src/components/shell/constellation-frame.tsx b/services/frontend/src/components/shell/constellation-frame.tsx
index 28ef1e73..4e4f5909 100644
--- a/services/frontend/src/components/shell/constellation-frame.tsx
+++ b/services/frontend/src/components/shell/constellation-frame.tsx
@@ -18,6 +18,7 @@ import {
resolveScene,
type SceneSeed,
} from "@/components/shell/route-scenes";
+import { SceneA11yMirror } from "@/components/shell/scene-a11y-mirror";
import {
SceneGraphProvider,
useSceneGraph,
@@ -62,6 +63,7 @@ export function ConstellationFrame({
+
{/* Overlay content region — scenes place floating panels inside. */}
diff --git a/services/frontend/src/components/shell/scene-a11y-mirror.tsx b/services/frontend/src/components/shell/scene-a11y-mirror.tsx
new file mode 100644
index 00000000..05391e7e
--- /dev/null
+++ b/services/frontend/src/components/shell/scene-a11y-mirror.tsx
@@ -0,0 +1,55 @@
+"use client";
+
+/**
+ * SceneA11yMirror — screen-reader/keyboard mirror of the live constellation.
+ * The canvas itself is decorative (aria-hidden); this list exposes every
+ * node as a real link/button so keyboard and AT users get the same graph.
+ */
+import { usePathname } from "next/navigation";
+import { useSceneGraph } from "@/components/shell/scene-graph-context";
+
+export function SceneA11yMirror() {
+ const pathname = usePathname() ?? "/";
+ const { state, setFocus } = useSceneGraph();
+ const nodes = state?.graph.nodes ?? [];
+
+ return (
+
+ );
+}