diff --git a/client/src/sites/pg/PgLayout.tsx b/client/src/sites/pg/PgLayout.tsx
index 9361d009..2117cb56 100644
--- a/client/src/sites/pg/PgLayout.tsx
+++ b/client/src/sites/pg/PgLayout.tsx
@@ -30,6 +30,7 @@ import { FooterLinkIcon, FooterSectionKind } from "~/graphql/enums";
import { useSnapshot } from "valtio";
import { siteConfigState, type FooterSection } from "@/sites/pg/siteConfigState";
import { PgHeroHeader } from "@/sites/pg/components/PgHeader";
+import { PgScrollToTop } from "@/sites/pg/components/PgScrollToTop";
import { layout } from "@/sites/pg/PgLayoutConfig";
const style = layout.style.container;
@@ -67,6 +68,8 @@ export default function PgLayout() {
+
+
);
diff --git a/client/src/sites/pg/components/PgScrollToTop.tsx b/client/src/sites/pg/components/PgScrollToTop.tsx
new file mode 100644
index 00000000..1ebd23be
--- /dev/null
+++ b/client/src/sites/pg/components/PgScrollToTop.tsx
@@ -0,0 +1,59 @@
+import { useEffect, useRef } from "react";
+import { Box, IconButton } from "@chakra-ui/react";
+import { LuArrowUp } from "react-icons/lu";
+import { useStateValtio } from "@neuronhub/shared/utils/useStateValtio";
+
+const containerHalfWithGap = 551;
+
+const style = {
+ button: {
+ pos: "fixed",
+ zIndex: "sticky",
+ bottom: { base: "gap.sm", lg: "gap.md" },
+ right: { base: "gap.sm", md: "6", xl: `calc(50% - ${containerHalfWithGap}px)` },
+ w: "10",
+ h: "10",
+ borderWidth: "1px",
+ borderColor: "subtle",
+ transition: "opacity 0.2s, border-color 0.3s",
+ bg: "bg.card",
+ borderRadius: "full",
+ cursor: "pointer",
+ color: "fg",
+ _hover: { borderColor: "brand.black" },
+ },
+} as const;
+
+export function PgScrollToTop() {
+ const sentinelRef = useRef(null);
+
+ const state = useStateValtio({ isVisible: false });
+
+ useEffect(() => {
+ const sentinel = sentinelRef.current;
+ if (!sentinel) {
+ return;
+ }
+ const observer = new IntersectionObserver(([entry]) => {
+ state.mutable.isVisible = !entry!.isIntersecting;
+ });
+ observer.observe(sentinel);
+ return () => observer.disconnect();
+ }, []);
+
+ return (
+ <>
+
+ window.scrollTo({ top: 0, behavior: "smooth" })}
+ variant="plain"
+ {...style.button}
+ opacity={state.snap.isVisible ? 1 : 0}
+ pointerEvents={state.snap.isVisible ? "auto" : "none"}
+ >
+
+
+ >
+ );
+}