diff --git a/bun.lock b/bun.lock
index e55d569..3f6727c 100644
--- a/bun.lock
+++ b/bun.lock
@@ -5,6 +5,7 @@
"": {
"name": "datamart-web",
"dependencies": {
+ "@profullstack/footer": "latest",
"hono": "4.13.12",
"next": "16.3.8",
"react": "19.2.3",
@@ -94,6 +95,8 @@
"@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@16.3.8", "", { "os": "win32", "cpu": "x64" }, "sha512-WcTlaKt/TWkh5kUjdJcUmB1XgZ+1c6fz4Y9fDHL73YNSdGaUWjceeWrrlwF0nv19iABYWC4iAq1oX1w4Bn0vfg=="],
+ "@profullstack/footer": ["@profullstack/footer@0.2.1", "", { "peerDependencies": { "react": ">=18" }, "optionalPeers": ["react"], "bin": { "profullstack-footer": "bin/profullstack-footer.js" } }, "sha512-KHmKVwrlzCBrSAAJ4zobNFCxTg8VF0gvNMeAut00dX3+gbWCtk6eOwIR2AaqEC7smj6Aio6cIexzt/dUnv1McA=="],
+
"@swc/helpers": ["@swc/helpers@0.5.23", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw=="],
"@types/bun": ["@types/bun@1.3.14", "", { "dependencies": { "bun-types": "1.3.14" } }, "sha512-h1hFqFVcvAvD9j9K7ZW7vd82aSA+rTdznZa+5bwvCwqSB1jmmfLcbIWhOLx1/+boy/xmjgCs/OMUL8hRJSmnPw=="],
diff --git a/package.json b/package.json
index 4cf1f7d..cb244aa 100644
--- a/package.json
+++ b/package.json
@@ -15,6 +15,7 @@
"smoke": "bun scripts/smoke.ts"
},
"dependencies": {
+ "@profullstack/footer": "latest",
"hono": "4.13.12",
"next": "16.3.8",
"react": "19.2.3",
diff --git a/src/app/globals.css b/src/app/globals.css
index f6a67d2..f85b33b 100644
--- a/src/app/globals.css
+++ b/src/app/globals.css
@@ -124,7 +124,7 @@ dl.facts dd { margin: 0; overflow-wrap: anywhere; }
.split { display: grid; gap: 1.5rem; grid-template-columns: 1fr; }
@media (min-width: 52rem) { .split { grid-template-columns: 2fr 1fr; } }
-.site-footer { border-top: 1px solid var(--line); padding: 1.5rem 0 2.5rem; color: var(--muted); font-size: 0.85rem; }
+.site-footer { border-top: 1px solid var(--line); padding: 1.5rem 0 0.5rem; color: var(--muted); font-size: 0.85rem; }
.site-footer nav { display: flex; gap: 1rem; flex-wrap: wrap; margin-bottom: 0.75rem; }
.site-footer a { color: var(--muted); }
-.site-footer nav.webring { justify-content: center; gap: 0.75rem; margin: 0.75rem 0 0; }
+.site-footer + .pfs-footer { border-top: 0; }
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index 1e9ff8c..4b57f8c 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -1,9 +1,14 @@
import type { Metadata, Viewport } from "next";
+import { Footer } from "@profullstack/footer/react";
import Link from "next/link";
import { RegisterServiceWorker } from "@/components/register-sw";
import { DISCLOSURE } from "@/lib/site";
import "./globals.css";
+// Pages prerender at build; revalidating hourly lets the shared footer pick up a new
+// @profullstack/footer template (served from jsDelivr @latest) without a redeploy.
+export const revalidate = 3600;
+
export const metadata: Metadata = {
metadataBase: new URL("https://datamart.help"),
title: { default: "Datamart: public services near you", template: "%s · Datamart" },
@@ -58,23 +63,20 @@ export default function RootLayout({ children }: { children: React.ReactNode })
{children}
-