diff --git a/app/layout.tsx b/app/layout.tsx index 5f51af8e..801e80ab 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -24,8 +24,19 @@ const serif = Fraunces({ }); export const metadata = { + // Load-bearing: Next resolves the generated og:image against metadataBase. + // Without it the tag is emitted as http://localhost:3000/opengraph-image — + // present, plausible, and unfetchable by every social scraper. + metadataBase: new URL(site.url), title: `${site.name} — ${site.tagline}`, description: site.description, + openGraph: { + title: `${site.name} — ${site.tagline}`, + description: site.description, + url: site.url, + siteName: site.name, + type: 'website', + }, }; export default function RootLayout({ children }: { children: React.ReactNode }) { diff --git a/app/opengraph-image.tsx b/app/opengraph-image.tsx new file mode 100644 index 00000000..06bd6277 --- /dev/null +++ b/app/opengraph-image.tsx @@ -0,0 +1,75 @@ +import { ImageResponse } from 'next/og'; +import { site } from '@/lib/site'; + +/** + * Social preview card, generated by Next's file convention at 1200x630 — + * the 1.91:1 size Slack, Telegram and the OpenGraph scrapers crop to. + * + * Botsmann shipped no og:image, so every shared link rendered as a blank + * rectangle. Copy comes from lib/site.ts so the card cannot drift from the + * hero and footer; only the palette is repeated, because Satori cannot read + * CSS custom properties. globals.css stays the source of truth for those. + */ + +export const runtime = 'edge'; +export const alt = `${site.name} — ${site.tagline}`; +export const size = { width: 1200, height: 630 }; +export const contentType = 'image/png'; + +// Mirrors --primitive-ink-950 / --primitive-paper / --primitive-ochre-600 +const INK = '#1A1714'; +const PAPER = '#FAF8F4'; +const OCHRE = '#B45309'; +const MUTED = '#6F655C'; + +export default function OGImage() { + return new ImageResponse( +
+
+
+
+ {site.name} +
+
+ +
+
+ {site.tagline} +
+
+ Private AI that works with your data — answers with citations. +
+
+ +
+
+
botsmann.orangecat.ch
+
+
, + { ...size }, + ); +} diff --git a/lib/site.ts b/lib/site.ts index a8005ce6..7d65e363 100644 --- a/lib/site.ts +++ b/lib/site.ts @@ -10,7 +10,11 @@ export const site = { tagline: 'Your Private AI Professionals', description: 'Private AI that works with your data. Upload documents, ask questions, get answers with citations. Pre-built assistants for legal, medical, research, and more.', - url: 'https://botsmann.com', + // Where the site ACTUALLY serves. botsmann.com has no DNS A record, so + // everything derived from this — canonical URL, og:image, sitemap entries — + // pointed at a host that does not resolve. Point it back only once that + // domain is live. + url: 'https://botsmann.orangecat.ch', author: 'Botsmann AI', social: { github: 'https://github.com/g-but',