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 (
AI Atlas
); } 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
); }