diff --git a/src/components/PageHeader.tsx b/src/components/PageHeader.tsx
new file mode 100644
index 0000000..bf43faf
--- /dev/null
+++ b/src/components/PageHeader.tsx
@@ -0,0 +1,66 @@
+import type { ComponentType, ReactNode } from 'react';
+import { cn } from '@/lib/utils';
+
+/**
+ * Shared page header + width tokens (UX foundations).
+ *
+ * Pages previously each hand-rolled their own title markup and container
+ * width (max-w-2xl / -3xl / -4xl / -7xl / unbounded), so navigating between
+ * sections felt like moving between different apps. New/edited pages should
+ * use these; existing pages can be migrated incrementally.
+ */
+export function PageHeader({
+ title,
+ subtitle,
+ icon: Icon,
+ accent = '#ccff00',
+ actions,
+ className,
+}: {
+ title: ReactNode;
+ subtitle?: ReactNode;
+ icon?: ComponentType<{ className?: string }>;
+ /** Title colour. Defaults to the app's lime accent. */
+ accent?: string;
+ actions?: ReactNode;
+ className?: string;
+}) {
+ return (
+
+
+
+ {Icon && }
+ {title}
+
+ {subtitle &&
{subtitle}
}
+
+ {actions &&
{actions}
}
+
+ );
+}
+
+const WIDTHS = {
+ narrow: 'max-w-2xl',
+ list: 'max-w-3xl',
+ detail: 'max-w-4xl',
+ wide: 'max-w-7xl',
+} as const;
+
+export function PageContainer({
+ children,
+ width = 'detail',
+ className,
+}: {
+ children: ReactNode;
+ width?: keyof typeof WIDTHS;
+ className?: string;
+}) {
+ return (
+
+ {children}
+
+ );
+}
diff --git a/src/pages/ContactsPage.tsx b/src/pages/ContactsPage.tsx
index 1264cb4..2f447f9 100644
--- a/src/pages/ContactsPage.tsx
+++ b/src/pages/ContactsPage.tsx
@@ -3,6 +3,7 @@ import { useSeoMeta } from '@unhead/react';
import { Link } from 'react-router-dom';
import { nip19 } from 'nostr-tools';
import { Layout } from '@/components/Layout';
+import { PageHeader } from '@/components/PageHeader';
import { Card, CardContent } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Skeleton } from '@/components/ui/skeleton';
@@ -221,17 +222,12 @@ export default function ContactsPage() {
{/* Header */}
-
-
- Contacts
-
-
- Your scene — crew-mates, follows & conversations
-
-
+
{/* Search */}
diff --git a/src/pages/NIP19Page.tsx b/src/pages/NIP19Page.tsx
index 062fb46..b9ce2b8 100644
--- a/src/pages/NIP19Page.tsx
+++ b/src/pages/NIP19Page.tsx
@@ -706,7 +706,9 @@ function ProfilePage({ pubkey }: { pubkey: string }) {
{/* Sidebar */}
-
+ {/* On mobile the identity/contact cards come first — the info card was
+ previously buried below every content section on phones. */}
+
{/* Zap achievements (only renders when this profile has sent zaps) */}