From cde66729734c919e7fe64d178f8693170a005164 Mon Sep 17 00:00:00 2001 From: "crawlproof[bot]" <286981042+crawlproof[bot]@users.noreply.github.com> Date: Fri, 24 Jul 2026 18:35:43 +0000 Subject: [PATCH 1/2] Add CrawlProof ad units --- lib/email.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/email.ts b/lib/email.ts index b1e81be3..e4012be7 100644 --- a/lib/email.ts +++ b/lib/email.ts @@ -63,6 +63,11 @@ function emailShell(input: { +
+
+
+
+ `; } From 751db7e14765438411ba74c2fd94ad4d6dfcccaa Mon Sep 17 00:00:00 2001 From: Anthony Ettinger Date: Fri, 24 Jul 2026 18:42:22 +0000 Subject: [PATCH 2/2] fix(ads): scope ad units to /blog/** instead of transactional email MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The auto-installer injected the embed into lib/email.ts, so the units would have rendered inside every transactional email we send (where the script never runs and the markup is just dead weight) and nowhere on the site itself. Revert that and mount the units in a /blog layout instead: it covers the index and every post, and nothing outside /blog/**. The RSS route is a route handler, so the feed stays ad-free. AdUnit re-triggers /ad.js's scan on mount — the script only auto-scans once at load, so units arriving via client-side navigation need the manual trigger it exposes. Format is left unset so each unit sizes to the column: leaderboard on desktop, mobile banner on narrow screens. Co-Authored-By: Claude Opus 5 (1M context) --- app/(marketing)/blog/layout.tsx | 22 ++++++++++++++ components/ads/ad-unit.tsx | 51 +++++++++++++++++++++++++++++++++ lib/email.ts | 5 ---- 3 files changed, 73 insertions(+), 5 deletions(-) create mode 100644 app/(marketing)/blog/layout.tsx create mode 100644 components/ads/ad-unit.tsx 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 ( +
+ ); +} diff --git a/lib/email.ts b/lib/email.ts index e4012be7..b1e81be3 100644 --- a/lib/email.ts +++ b/lib/email.ts @@ -63,11 +63,6 @@ function emailShell(input: { -
-
-
-
- `; }