import { MARK_DARK, MarkImg } from './mark';
/**
* Shared chrome for Open Graph images (ImageResponse cannot read CSS variables → dark-theme token hex values inlined).
* `Frame` = header lockup + content + footer; `Wallpaper` = the dark atlas plate with a faint coordinate grid and the mark
* large at left — used by the root OG image and by the per-type OG images (D1–D3) through `Wallpaper` + `Counters`.
*/
export const INK = '#e9ebf0';
export const INK2 = '#a3a9b8';
export const INK3 = '#6f7688';
export const ACCENT = '#6d95ff';
export const ACCENT2 = '#f2b04a';
export const CANVAS = '#0b0d11';
export const SURFACE = '#12151b';
export const RULE = 'rgba(190,200,225,0.14)';
export function Mark({ px = 56 }: { px?: number }) {
return ;
}
/** Faint coordinate grid covering the whole image (absolute, behind content). */
export function Grid({ step = 60, opacity = 1 }: { step?: number; opacity?: number }) {
return (
);
}
export function Lockup({ px = 56, fontSize = 32 }: { px?: number; fontSize?: number }) {
return (
);
}
export function Frame({ children, footer }: { children: React.ReactNode; footer: string }) {
return (
{children}
{footer}
who said it · when · where · how it was extracted
);
}
export function Eyebrow({ children, color = ACCENT }: { children: string; color?: string }) {
return {children}
;
}
export function Title({ children }: { children: string }) {
const long = children.length > 26;
return (
{children}
);
}
export function Facts({ items }: { items: [string, string][] }) {
if (!items.length) return null;
return (
{items.map(([label, value]) => (
{label}
{value}
))}
);
}
/** Live counter strip (label above value, hairline separators). Empty array → renders nothing (graceful fallback). */
export function Counters({ items, size = 'md' }: { items: [string, string][]; size?: 'sm' | 'md' }) {
if (!items.length) return null;
const vs = size === 'sm' ? 30 : 38;
return (
{items.map(([label, value], i) => (
{label}
{value}
))}
);
}
/**
* The "wallpaper": dark plate, faint grid, the mark large at left, title + tagline at right and a counter strip at the bottom.
* `eyebrow` defaults to the site tagline; `title` to "AI Atlas". Per-type OG images pass their own eyebrow/title/counters.
*/
export function Wallpaper({ eyebrow, title = 'AI Atlas', subtitle, counters = [], footer = 'www.ai-atlas.co', markPx = 300 }: { eyebrow?: string; title?: string; subtitle?: string; counters?: [string, string][]; footer?: string; markPx?: number }) {
return (
{eyebrow &&
{eyebrow}}
22 ? 56 : 84, fontWeight: 700, lineHeight: 1, letterSpacing: -3, maxHeight: 180, overflow: 'hidden' }}>
{title === 'AI Atlas' ? (
<>
AI
Atlas
>
) : (
{title}
)}
{subtitle &&
{subtitle}
}
{footer}
who said it · when · where · how it was extracted
);
}
export function Fallback({ label }: { label: string }) {
return (
{label}
Explore the AI ecosystem.
Models · organizations · research · providers & pricing · benchmarks · hardware
);
}