diff --git a/app/[locale]/(site)/template.tsx b/app/[locale]/(site)/template.tsx
index c4a3e9e..c699126 100644
--- a/app/[locale]/(site)/template.tsx
+++ b/app/[locale]/(site)/template.tsx
@@ -1,27 +1,48 @@
"use client";
-import { motion, useReducedMotion } from "framer-motion";
+import { AnimatePresence, motion, useReducedMotion } from "framer-motion";
+import { LayoutRouterContext } from "next/dist/shared/lib/app-router-context.shared-runtime";
import { usePathname } from "next/navigation";
-import type { ReactNode } from "react";
+import { useContext, useState, type ReactNode } from "react";
/**
- * Next.js re-mounts a `template` on every navigation, so this wrapper runs its
- * enter animation each time the visitor moves between pages (via the dock or
- * any link) — giving one unified page transition instead of per-element
- * entrance animations.
+ * App Router swaps in the new page's content as soon as you navigate, so the
+ * outgoing page would normally vanish before it can animate out. `FrozenRouter`
+ * pins the layout-router context to the value it had when this instance
+ * mounted, keeping the *old* subtree rendered while its exit animation plays.
+ */
+function FrozenRouter({ children }: { children: ReactNode }) {
+ const context = useContext(LayoutRouterContext);
+ // Freeze the context captured at mount so the outgoing subtree keeps
+ // rendering (with its old route data) while its exit animation plays.
+ const [frozen] = useState(context);
+
+ if (!frozen) {
+ return <>{children}>;
+ }
+
+ return {children};
+}
+
+/**
+ * One unified page transition on every navigation: the old page animates out
+ * (blur + rise up) before the new page animates in (blur + rise from below).
*/
export default function SiteTemplate({ children }: { children: ReactNode }) {
const reducedMotion = useReducedMotion();
const pathname = usePathname();
return (
-
- {children}
-
+
+
+ {children}
+
+
);
}