diff --git a/app/(marketing)/blog/layout.tsx b/app/(marketing)/blog/layout.tsx
new file mode 100644
index 00000000..380c22a6
--- /dev/null
+++ b/app/(marketing)/blog/layout.tsx
@@ -0,0 +1,22 @@
+import { AdUnit } from "@/components/ads/ad-unit";
+
+/**
+ * Ads run on /blog/** only — the editorial pages — and nowhere else on the
+ * site. Scoping them to this layout keeps them off the marketing pages, the
+ * app, and transactional email; the RSS route under /blog is a route handler
+ * and never sees a layout, so the feed stays clean too.
+ *
+ * Both units leave `format` unset so /ad.js sizes them to the column: a
+ * leaderboard on desktop, a mobile banner on narrow screens.
+ */
+export default function BlogLayout({ children }: { children: React.ReactNode }) {
+ return (
+ <>
+ {children}
+
+ >
+ );
+}
diff --git a/components/ads/ad-unit.tsx b/components/ads/ad-unit.tsx
new file mode 100644
index 00000000..a9b1cee1
--- /dev/null
+++ b/components/ads/ad-unit.tsx
@@ -0,0 +1,51 @@
+"use client";
+
+import { useEffect } from "react";
+import type { AdFormatId } from "@/lib/ads/formats";
+
+// Our own slot on the network — the one this site earns from. Overridable so a
+// preview/staging deploy can point at a test slot instead of billing the real
+// one for impressions nobody sees.
+const SLOT_ID =
+ process.env.NEXT_PUBLIC_AD_SLOT_ID ?? "94cd644a-bcc2-4bba-ab66-b4a89f3727cc";
+
+const SCRIPT_SRC = "/ad.js";
+
+declare global {
+ interface Window {
+ crawlproofAds?: { scan: () => void };
+ }
+}
+
+/**
+ * A single ad container filled by /ad.js.
+ *
+ * Omitting `format` lets the script pick by container width (leaderboard on
+ * desktop, mobile banner on narrow screens), which is what we want in the blog
+ * column. The script only auto-scans once at load, so mounting a unit via
+ * client-side navigation has to re-trigger the scan by hand.
+ */
+export function AdUnit({
+ format,
+ className,
+}: {
+ format?: AdFormatId;
+ className?: string;
+}) {
+ useEffect(() => {
+ if (window.crawlproofAds) {
+ window.crawlproofAds.scan();
+ return;
+ }
+ // First unit on the page loads the script; later ones ride the same tag.
+ if (document.querySelector(`script[src="${SCRIPT_SRC}"]`)) return;
+ const s = document.createElement("script");
+ s.src = SCRIPT_SRC;
+ s.async = true;
+ document.body.appendChild(s);
+ }, []);
+
+ return (
+
+ );
+}