diff --git a/app/layout.tsx b/app/layout.tsx
index c688d64..d7dec35 100644
--- a/app/layout.tsx
+++ b/app/layout.tsx
@@ -96,14 +96,14 @@ export default function RootLayout({
-
+ {children}
+
+
-
-
diff --git a/components/layout/Header.tsx b/components/layout/Header.tsx
index b92aa79..2e4c732 100644
--- a/components/layout/Header.tsx
+++ b/components/layout/Header.tsx
@@ -7,6 +7,8 @@ import { siteConfig } from "@/lib/config";
import { nav } from "@/lib/nav";
import { useTheme } from "@/contexts/ThemeContext";
import { Sun, Moon } from "lucide-react";
+import { HeaderAuthActions } from "@/components/layout/HeaderAuthActions";
+import { HeaderMobileAuthLinks } from "@/components/layout/HeaderMobileAuthLinks";
export function Header() {
const { theme, toggleTheme } = useTheme();
@@ -69,18 +71,7 @@ export function Header() {
-
- Sign In
-
-
- Sign Up
-
+
@@ -98,13 +89,7 @@ export function Header() {
{item.label}
))}
- setMobileOpen(false)}
- >
- Sign In
-
+ setMobileOpen(false)} />
)}
diff --git a/components/layout/HeaderAuthActions.tsx b/components/layout/HeaderAuthActions.tsx
new file mode 100644
index 0000000..0e7a5b1
--- /dev/null
+++ b/components/layout/HeaderAuthActions.tsx
@@ -0,0 +1,65 @@
+"use client";
+
+import Link from "next/link";
+import { usePrivy } from "@privy-io/react-auth";
+import { LogOut } from "lucide-react";
+import { siteConfig } from "@/lib/config";
+import { useSignedIn } from "@/hooks/useSignedIn";
+
+/**
+ * The header's auth-dependent actions on desktop: "Open app" plus a log-out
+ * control when signed in, "Sign In" / "Sign Up" when signed out. Owns its own
+ * auth state so `Header` stays agnostic — a new auth-dependent action lands
+ * here without touching the header shell.
+ *
+ * Mobile counterparts live in `HeaderMobileAuthLinks`, inside the menu. The
+ * log-out icon is `hidden sm:inline-flex` for the same reason "Sign In" is:
+ * below `sm` the labelled menu row is the one that shows, so phones never
+ * render two log-out controls.
+ */
+export function HeaderAuthActions() {
+ const { logout } = usePrivy();
+ const signedIn = useSignedIn();
+
+ if (signedIn) {
+ return (
+ <>
+ {/* Icon-only, matching the theme toggle — the header chrome is
+ achromatic and text-free, and "Open app" stays the only CTA with
+ weight. Labelled for assistive tech and on hover, since a bare
+ glyph carries no accessible name. */}
+
+
+ Open app
+
+ >
+ );
+ }
+
+ return (
+ <>
+
+ Sign In
+
+
+ Sign Up
+
+ >
+ );
+}
diff --git a/components/layout/HeaderMobileAuthLinks.tsx b/components/layout/HeaderMobileAuthLinks.tsx
new file mode 100644
index 0000000..fe06030
--- /dev/null
+++ b/components/layout/HeaderMobileAuthLinks.tsx
@@ -0,0 +1,50 @@
+"use client";
+
+import Link from "next/link";
+import { usePrivy } from "@privy-io/react-auth";
+import { LogOut } from "lucide-react";
+import { siteConfig } from "@/lib/config";
+import { useSignedIn } from "@/hooks/useSignedIn";
+
+/**
+ * The auth-dependent rows inside the mobile menu. Counterpart to
+ * `HeaderAuthActions`, which covers the same states on desktop; both own their
+ * auth state so the header shell carries none.
+ *
+ * Log out is a labelled row here rather than the desktop's bare glyph — an
+ * icon-only entry reads as decoration in a list of text links.
+ *
+ * @param onNavigate - closes the menu after the row is activated.
+ */
+export function HeaderMobileAuthLinks({
+ onNavigate,
+}: {
+ onNavigate: () => void;
+}) {
+ const { logout } = usePrivy();
+ const signedIn = useSignedIn();
+
+ return (
+ <>
+
+ {signedIn ? "Open app" : "Sign In"}
+
+ {signedIn && (
+
+ )}
+ >
+ );
+}
diff --git a/hooks/useSignedIn.ts b/hooks/useSignedIn.ts
new file mode 100644
index 0000000..fa95e57
--- /dev/null
+++ b/hooks/useSignedIn.ts
@@ -0,0 +1,17 @@
+"use client";
+
+import { usePrivy } from "@privy-io/react-auth";
+
+/**
+ * Whether the visitor has a resolved, authenticated Privy session.
+ *
+ * Only trust the auth state once Privy has resolved on the client. Until then
+ * (and on the server) `ready` is false, so both first renders report signed
+ * out — no hydration mismatch, then the UI swaps in place.
+ *
+ * Shared by every auth-dependent header surface so the guard is defined once.
+ */
+export function useSignedIn(): boolean {
+ const { ready, authenticated } = usePrivy();
+ return ready && authenticated;
+}