diff --git a/app/layout.tsx b/app/layout.tsx
index e5930fd..db45356 100644
--- a/app/layout.tsx
+++ b/app/layout.tsx
@@ -2,6 +2,7 @@ import type { Metadata, Viewport } from "next";
import { Geist } from "next/font/google";
import { Header } from "@/components/Header";
import { BottomNav } from "@/components/BottomNav";
+import { ViewportRedrawOnResume } from "@/components/ViewportRedrawOnResume";
import "./globals.css";
const geistSans = Geist({
@@ -48,6 +49,7 @@ export default function RootLayout({
suppressHydrationWarning
>
+
{children}
diff --git a/components/ViewportRedrawOnResume.tsx b/components/ViewportRedrawOnResume.tsx
new file mode 100644
index 0000000..a9bb6ee
--- /dev/null
+++ b/components/ViewportRedrawOnResume.tsx
@@ -0,0 +1,20 @@
+"use client";
+
+import { useEffect } from "react";
+
+// iOS Safari bug: with viewportFit=cover + maximumScale=1, restoring from
+// bfcache after lock/unlock can leave the visual viewport in a stale
+// "zoomed" state. Nudging scroll + forcing a reflow on pageshow
+// (persisted) realigns it without changing the zoom level.
+export function ViewportRedrawOnResume() {
+ useEffect(() => {
+ const onShow = (e: PageTransitionEvent) => {
+ if (!e.persisted) return;
+ window.scrollTo(window.scrollX, window.scrollY);
+ void document.body.offsetHeight;
+ };
+ window.addEventListener("pageshow", onShow);
+ return () => window.removeEventListener("pageshow", onShow);
+ }, []);
+ return null;
+}