diff --git a/app/about/page.tsx b/app/about/page.tsx
index a9bd6b0a..161c992c 100644
--- a/app/about/page.tsx
+++ b/app/about/page.tsx
@@ -1,5 +1,7 @@
+import type { Metadata } from "next";
import Link from "next/link";
import { PageHeader } from "@/components/page-header";
+import { buildOgMeta } from "@/lib/og";
import {
BookOpen,
BrainCircuit,
@@ -45,6 +47,14 @@ const PHASES = [
},
];
+export function generateMetadata(): Metadata {
+ return buildOgMeta({
+ title: "About — Prabhupāda Says",
+ quote:
+ "Built by a small team of Prabhupādānugas — preserving and presenting the teachings of Śrīla Prabhupāda exactly as he gave them, without alteration.",
+ });
+}
+
export default function AboutPage() {
return (
diff --git a/app/api/og/route.tsx b/app/api/og/route.tsx
index 8063d3f5..d4bf7361 100644
--- a/app/api/og/route.tsx
+++ b/app/api/og/route.tsx
@@ -4,215 +4,218 @@ import { join } from "node:path";
export const runtime = "nodejs";
+// Cache the font data at module level so it's only loaded once
+let geistBoldData: ArrayBuffer | null = null;
+let geistRegularData: ArrayBuffer | null = null;
+
+async function loadFonts() {
+ if (!geistBoldData) {
+ // Fetch Geist Bold from Google Fonts CDN
+ const boldRes = await fetch(
+ "https://cdn.jsdelivr.net/fontsource/fonts/geist-sans@latest/latin-700-normal.woff",
+ );
+ geistBoldData = await boldRes.arrayBuffer();
+ }
+ if (!geistRegularData) {
+ const regularRes = await fetch(
+ "https://cdn.jsdelivr.net/fontsource/fonts/geist-sans@latest/latin-400-normal.woff",
+ );
+ geistRegularData = await regularRes.arrayBuffer();
+ }
+ return { bold: geistBoldData, regular: geistRegularData };
+}
+
export async function GET(request: Request) {
- const { searchParams } = new URL(request.url);
+ try {
+ const { searchParams } = new URL(request.url);
- const title = searchParams.get("title") || "Prabhupāda Says";
- const detail = searchParams.get("detail") || "";
- const quote = searchParams.get("quote") || "";
+ const title = searchParams.get("title") || "Prabhupāda Says";
+ const detail = searchParams.get("detail") || "";
+ const rawQuote = searchParams.get("quote") || "";
- // Truncate for readability and to prevent overflow
- const displayTitle = title.length > 60 ? title.slice(0, 57) + "..." : title;
- const displayDetail =
- detail.length > 80 ? detail.slice(0, 77) + "..." : detail;
- const displayQuote = quote.length > 220 ? quote.slice(0, 217) + "..." : quote;
+ // Fallback: if no quote provided, generate one from context
+ const quote =
+ rawQuote ||
+ (title === "Prabhupāda Says"
+ ? "Browse the original, unedited books, letters, and teachings of His Divine Grace A.C. Bhaktivedanta Swami Prabhupāda — presented as they are."
+ : `Read ${title}${detail ? ` — ${detail}` : ""} by His Divine Grace A.C. Bhaktivedanta Swami Prabhupāda`);
- // Load the Prabhupāda image as base64
- const imgData = await readFile(
- join(process.cwd(), "public/images/og-left-prabhupada-reading.png"),
- "base64",
- );
- const imgSrc = `data:image/png;base64,${imgData}`;
+ // Truncate for readability and to prevent overflow
+ const displayTitle = title.length > 60 ? title.slice(0, 57) + "..." : title;
+ const displayDetail =
+ detail.length > 80 ? detail.slice(0, 77) + "..." : detail;
+ const displayQuote =
+ quote.length > 220 ? quote.slice(0, 217) + "..." : quote;
- return new ImageResponse(
-
- {/* Left side — image + branding */}
+ // Load fonts and image
+ const fonts = await loadFonts();
+
+ const imgData = await readFile(
+ join(process.cwd(), "public/images/og-left-prabhupada-reading.png"),
+ "base64",
+ );
+ const imgSrc = `data:image/png;base64,${imgData}`;
+
+ return new ImageResponse(
- {/* Decorative quote mark */}
+ {/* Left side — image + branding */}
- “
+ {/* Prabhupāda image — already contains quote mark and branding */}
+
- {/* Prabhupāda image */}
-

-
- {/* Branding at the bottom */}
+ {/* Right side — content */}
-
- Prabhupāda
-
-
- says
-
-
-
-
- {/* Right side — content */}
-
- {/* Title */}
-
- {displayTitle}
-
-
- {/* Detail (chapter, verse, etc.) */}
- {displayDetail && (
+ {/* Title */}
- {displayDetail}
+ {displayTitle}
- )}
- {/* Quote text */}
- {displayQuote && (
-
+ {/* Detail (chapter, verse, etc.) */}
+ {displayDetail ? (
- {displayQuote}
+ {displayDetail}
-
- )}
+ ) : null}
- {/* Read more */}
-
+ ) : null}
+
+ {/* Read more */}
+
- Read more at
-
-
- prabhupadasays.org
-
+
+ Read more at
+
+
+ prabhupadasays.org
+
+
-
-
,
- {
- width: 1200,
- height: 630,
- },
- );
+ ,
+ {
+ width: 1200,
+ height: 630,
+ fonts: [
+ {
+ name: "Geist",
+ data: fonts.regular,
+ style: "normal" as const,
+ weight: 400,
+ },
+ {
+ name: "Geist",
+ data: fonts.bold,
+ style: "normal" as const,
+ weight: 700,
+ },
+ ],
+ },
+ );
+ } catch (e) {
+ console.error("OG image generation failed:", e);
+ return new Response("Failed to generate OG image", { status: 500 });
+ }
}
diff --git a/app/disciples/page.tsx b/app/disciples/page.tsx
index 0c25f81c..ce9d6e45 100644
--- a/app/disciples/page.tsx
+++ b/app/disciples/page.tsx
@@ -1,7 +1,9 @@
export const dynamic = "force-dynamic";
+import type { Metadata } from "next";
import Link from "next/link";
import { PageHeader } from "@/components/page-header";
+import { ogForDisciples } from "@/lib/og";
import {
getTotalDiscipleCount,
getFilteredDisciples,
@@ -14,6 +16,10 @@ import {
import type { SuffixFilter } from "@/lib/disciples";
import { DisciplesBrowser } from "./disciples-browser";
+export async function generateMetadata(): Promise