import { ImageResponse } from "next/og";
import { api } from "@/lib/api";
import { fmtInt, fmtMw } from "@/lib/format";
/**
* Shared Open Graph template (1200×630) — dark ink, thin world-grid texture, mark + wordmark,
* title/tagline, stats strip. Entity pages reuse `renderOg({ title, subtitle, stats, eyebrow })`.
* Satori subset of CSS only: flex layout, no grid, no CSS vars.
*/
export const OG_SIZE = { width: 1200, height: 630 };
export const OG_CONTENT_TYPE = "image/png";
const INK = "#0b0f14";
const PAPER = "#eceae5";
const MUTED = "#8a9099";
const ACCENT = "#e8763a";
const HAIR = "rgba(236,234,229,0.08)";
export interface OgStat {
label: string;
value: string;
}
export interface OgProps {
eyebrow?: string;
title: string;
subtitle?: string;
stats?: OgStat[];
footer?: string;
}
function Mark({ size }: { size: number }) {
return (
);
}
function Graticule() {
const cols = Array.from({ length: 25 }, (_, i) => i * 50);
const rows = Array.from({ length: 13 }, (_, i) => i * 50);
return (
{cols.map((x) => (
))}
{rows.map((y) => (
))}
{/* meridian arcs as thin ellipses */}
{[220, 420, 640].map((rx, i) => (
))}
);
}
export function OgTemplate({ eyebrow, title, subtitle, stats, footer }: OgProps) {
return (
{eyebrow &&
{eyebrow}
}
40 ? 52 : 64, fontWeight: 600, letterSpacing: -2, lineHeight: 1.05 }}>{title}
{subtitle &&
{subtitle}
}
{(stats ?? []).map((s, i) => (
))}
{footer ?? "www.datacenterindex.io"}
published figures · sourced · versioned
);
}
/** Live stats for the default share image, with a graceful fallback when the API is down. */
export async function ogDashboardStats(): Promise {
const r = await api.dashboard();
if (!r.ok || !r.data?.stats) return [{ label: "Global coverage", value: "Facilities · MW · Countries" }];
const s = r.data.stats;
return [
{ label: "Facilities indexed", value: fmtInt(s.facilities) },
{ label: "Known operational MW", value: fmtMw(s.knownOperationalMw) },
{ label: "Under construction (≈)", value: fmtMw(s.constructionMw) },
{ label: "Countries", value: fmtInt(s.countries) },
{ label: "Events · 24 h", value: fmtInt(s.events24h) },
];
}
export function renderOg(props: OgProps): ImageResponse {
return new ImageResponse(, { ...OG_SIZE });
}