diff --git a/src/app/not-found.tsx b/src/app/not-found.tsx
index 0c08cf9..0f5e04d 100644
--- a/src/app/not-found.tsx
+++ b/src/app/not-found.tsx
@@ -1,20 +1,11 @@
-'use client';
-
-import Link from 'next/link';
-import { motion } from 'framer-motion';
-import { ArrowLeft, Leaf, Telescope } from 'lucide-react';
import Header from '@/components/Header';
-import SearchBox from '@/components/explore/SearchBox';
+import NotFoundPanel from '@/components/NotFoundPanel';
/**
- * Global 404 page. Next.js falls back to its plain-text default when this
- * file is absent; this version mirrors the site's hero treatment so a
- * mistyped URL doesn't read as a regression to a different product.
- *
- * Bias toward "give the visitor a way out": leaf-anchored eyebrow, big
- * serif headline, two-line explainer, the explorer's SearchBox (so a
- * fat-fingered handle is one re-keystroke away from working), and a Home
- * button next to a /explore link.
+ * Global 404 page. Lives at src/app/not-found.tsx so it covers every
+ * Next.js 404 — the notFound() helper, missing dynamic routes, mistyped
+ * paths. Shares NotFoundPanel with the client-side resolve-error states
+ * inside the explorer so every dead end reads the same.
*/
export default function NotFound() {
return (
@@ -22,121 +13,7 @@ export default function NotFound() {
-
-
- {/* Eyebrow */}
-
-
- 404 · Not found
-
-
-
-
- Lost in the Atmosphere.
-
-
-
-
- The path you tried didn’t resolve. Search for a handle, DID, or
- AT URI to keep looking — or head home.
-
-
- {/* Search */}
-
-
-
-
- {/* Quick actions */}
-
-
- Home
-
-
- Explore
-
-
-
+
);
}
-
-const primaryBtn: React.CSSProperties = {
- display: 'inline-flex',
- alignItems: 'center',
- gap: '0.4rem',
- padding: '0.55rem 1rem',
- background: 'var(--accent-moss)',
- color: 'var(--text-on-accent)',
- border: '1px solid var(--accent-moss)',
- fontFamily: 'var(--font-serif)',
- fontSize: '0.875rem',
- textDecoration: 'none',
-};
-
-const ghostBtn: React.CSSProperties = {
- display: 'inline-flex',
- alignItems: 'center',
- gap: '0.4rem',
- padding: '0.55rem 1rem',
- background: 'var(--bg-tertiary)',
- color: 'var(--text-primary)',
- border: '1px solid var(--border-medium)',
- fontFamily: 'var(--font-serif)',
- fontSize: '0.875rem',
- textDecoration: 'none',
-};
diff --git a/src/components/NotFoundPanel.tsx b/src/components/NotFoundPanel.tsx
new file mode 100644
index 0000000..7e40d28
--- /dev/null
+++ b/src/components/NotFoundPanel.tsx
@@ -0,0 +1,148 @@
+'use client';
+
+import Link from 'next/link';
+import { motion } from 'framer-motion';
+import { ArrowLeft, Leaf, Telescope } from 'lucide-react';
+import SearchBox from '@/components/explore/SearchBox';
+
+type Props = {
+ /** Small uppercase chip text. Defaults to "404 · NOT FOUND". */
+ eyebrow?: string;
+ /** Big gradient-serif headline. */
+ headline?: string;
+ /** Body copy under the headline. */
+ body?: string;
+ /**
+ * Optional pre-filled value for the inline explorer SearchBox — useful
+ * on resolve-error pages where the visitor is likely about to retry
+ * what they just typed.
+ */
+ initialQuery?: string;
+};
+
+/**
+ * Shared "we couldn't find what you asked for" panel. Used by the global
+ * Next.js not-found route AND by client-side resolve-error states inside
+ * the explorer (handle didn't resolve, record didn't load, etc.) so every
+ * dead end gets the same recovery affordance: the explorer SearchBox
+ * inline, plus Home + Explore buttons.
+ */
+export default function NotFoundPanel({
+ eyebrow = '404 · Not found',
+ headline = 'Lost in the Atmosphere.',
+ body = "The path you tried didn't resolve. Search for a handle, DID, or AT URI to keep looking — or head home.",
+ initialQuery,
+}: Props) {
+ return (
+
+