diff --git a/services/frontend/src/app/(dashboard)/analysis/page.tsx b/services/frontend/src/app/(dashboard)/analysis/page.tsx
index dd76297..00eaeed 100644
--- a/services/frontend/src/app/(dashboard)/analysis/page.tsx
+++ b/services/frontend/src/app/(dashboard)/analysis/page.tsx
@@ -1,7 +1,12 @@
+import { PageTransition } from "@/components/shared";
import { AnalysisView } from "./view";
export const dynamic = "force-dynamic";
export default function AnalysisPage() {
- return ;
+ return (
+
+
+
+ );
}
diff --git a/services/frontend/src/app/(dashboard)/dashboard/page.tsx b/services/frontend/src/app/(dashboard)/dashboard/page.tsx
index 7052caa..ee1b5c4 100644
--- a/services/frontend/src/app/(dashboard)/dashboard/page.tsx
+++ b/services/frontend/src/app/(dashboard)/dashboard/page.tsx
@@ -1,3 +1,4 @@
+import { PageTransition } from "@/components/shared";
import { getActivity, getDashboardStats } from "@/lib/api/server";
import { DashboardView } from "./view";
@@ -14,5 +15,9 @@ export default async function DashboardPage() {
} catch {
// Backend unavailable — client hooks will surface the error state.
}
- return ;
+ return (
+
+
+
+ );
}
diff --git a/services/frontend/src/app/(dashboard)/media/page.tsx b/services/frontend/src/app/(dashboard)/media/page.tsx
index f4b01b5..587fc7e 100644
--- a/services/frontend/src/app/(dashboard)/media/page.tsx
+++ b/services/frontend/src/app/(dashboard)/media/page.tsx
@@ -1,3 +1,4 @@
+import { PageTransition } from "@/components/shared";
import { getMediaStatus } from "@/lib/api/server";
import { MediaView } from "./view";
@@ -10,5 +11,9 @@ export default async function MediaPage() {
} catch {
/* client hooks surface errors */
}
- return ;
+ return (
+
+
+
+ );
}
diff --git a/services/frontend/src/app/(dashboard)/messages/page.tsx b/services/frontend/src/app/(dashboard)/messages/page.tsx
index de6ce74..cb4c8a0 100644
--- a/services/frontend/src/app/(dashboard)/messages/page.tsx
+++ b/services/frontend/src/app/(dashboard)/messages/page.tsx
@@ -1,3 +1,4 @@
+import { PageTransition } from "@/components/shared";
import { getConfig, getGuilds } from "@/lib/api/server";
import { MessagesView } from "./view";
@@ -12,9 +13,11 @@ export default async function MessagesPage() {
/* client hooks surface errors */
}
return (
-
+
+
+
);
}
diff --git a/services/frontend/src/app/(dashboard)/moderation/page.tsx b/services/frontend/src/app/(dashboard)/moderation/page.tsx
index 9863712..1bfc694 100644
--- a/services/frontend/src/app/(dashboard)/moderation/page.tsx
+++ b/services/frontend/src/app/(dashboard)/moderation/page.tsx
@@ -1,3 +1,4 @@
+import { PageTransition } from "@/components/shared";
import { getModerationActions, getModerationStats } from "@/lib/api/server";
import { ModerationView } from "./view";
@@ -14,5 +15,9 @@ export default async function ModerationPage() {
} catch {
/* client hooks surface errors */
}
- return ;
+ return (
+
+
+
+ );
}
diff --git a/services/frontend/src/app/(dashboard)/recordings/page.tsx b/services/frontend/src/app/(dashboard)/recordings/page.tsx
index 95b2da6..672a088 100644
--- a/services/frontend/src/app/(dashboard)/recordings/page.tsx
+++ b/services/frontend/src/app/(dashboard)/recordings/page.tsx
@@ -1,3 +1,4 @@
+import { PageTransition } from "@/components/shared";
import { getRecordings } from "@/lib/api/server";
import { RecordingsView } from "./view";
@@ -12,5 +13,9 @@ export default async function RecordingsPage() {
} catch {
/* client hooks surface errors */
}
- return ;
+ return (
+
+
+
+ );
}
diff --git a/services/frontend/src/app/(dashboard)/voice/page.tsx b/services/frontend/src/app/(dashboard)/voice/page.tsx
index 7a9bfd6..d6eba00 100644
--- a/services/frontend/src/app/(dashboard)/voice/page.tsx
+++ b/services/frontend/src/app/(dashboard)/voice/page.tsx
@@ -1,3 +1,4 @@
+import { PageTransition } from "@/components/shared";
import { getGuilds, getVoiceStatus } from "@/lib/api/server";
import { VoiceView } from "./view";
@@ -11,5 +12,9 @@ export default async function VoicePage() {
} catch {
/* client hooks surface errors */
}
- return ;
+ return (
+
+
+
+ );
}
diff --git a/services/frontend/src/app/globals.css b/services/frontend/src/app/globals.css
index 6960f4f..86aab0e 100644
--- a/services/frontend/src/app/globals.css
+++ b/services/frontend/src/app/globals.css
@@ -276,6 +276,11 @@
}
.animate-breathe { animation: breathe 3.5s ease-in-out infinite; }
+/* Page-enter: subtle rise+settle. Respect reduced-motion (handled globally). */
+.animate-fade-up {
+ animation: fade-up 0.4s cubic-bezier(0.22, 1, 0.36, 1) both;
+}
+
@media (prefers-reduced-motion: reduce) {
.scan-line::after,
.scan-line,
@@ -283,7 +288,8 @@
.animate-spin-disc,
.animate-pulse-ring,
.animate-shimmer,
- .animate-breathe {
+ .animate-breathe,
+ .animate-fade-up {
animation: none !important;
}
.scan-line {
diff --git a/services/frontend/src/components/shared/index.ts b/services/frontend/src/components/shared/index.ts
index cdac96c..3d5c499 100644
--- a/services/frontend/src/components/shared/index.ts
+++ b/services/frontend/src/components/shared/index.ts
@@ -1,5 +1,6 @@
export { GuildChannelPicker } from "./guild-picker";
export { MarkdownLite } from "./markdown";
+export { PageTransition } from "./page-transition";
export { MetricTile, SectionHeader } from "./section";
export {
EmptyState,
diff --git a/services/frontend/src/components/shared/page-transition.tsx b/services/frontend/src/components/shared/page-transition.tsx
new file mode 100644
index 0000000..216ca41
--- /dev/null
+++ b/services/frontend/src/components/shared/page-transition.tsx
@@ -0,0 +1,15 @@
+import { cn } from "@/lib/utils";
+
+/**
+ * Wraps a view's root so the whole panel stack rises + settles on mount.
+ * The animation is disabled under prefers-reduced-motion via globals.css.
+ */
+export function PageTransition({
+ children,
+ className,
+}: {
+ children: React.ReactNode;
+ className?: string;
+}) {
+ return
{children}
;
+}
diff --git a/services/frontend/src/components/shared/section.tsx b/services/frontend/src/components/shared/section.tsx
index 8c461b0..f5e7e74 100644
--- a/services/frontend/src/components/shared/section.tsx
+++ b/services/frontend/src/components/shared/section.tsx
@@ -55,7 +55,12 @@ export function MetricTile({
? "var(--color-signal)"
: "var(--color-ink)";
return (
-