← back to Stayclaim

src/app/opengraph-image.tsx

85 lines

import { ImageResponse } from 'next/og';
import { headers } from 'next/headers';
import { siteForHost } from '@/lib/site';

// Per-host OG card. Next.js auto-routes this for every page in app/ unless a
// page-specific opengraph-image is provided. Picks up the same surface as the
// rest of layout.tsx via x-pastdoor-host (set in middleware).
//
// Output is 1200x630 (Open Graph standard) and renders a typographic title
// card themed to the surface — green/cream for flagship, red/bone for claim,
// gold/cream for community.

export const runtime = 'edge';
export const contentType = 'image/png';
export const size = { width: 1200, height: 630 };
export const alt = 'pastdoor — every address has a story';

const SURFACE_OG = {
  flagship:  { bg: '#f3ebd9', accent: '#1f4d40', ink: '#1a1a1a', kicker: 'Open archive' },
  claim:     { bg: '#e8dcc4', accent: '#c9292e', ink: '#1a1a1a', kicker: 'Filed here'   },
  community: { bg: '#ede0c4', accent: '#c5a572', ink: '#1a1a1a', kicker: 'Neighborhood screenings' },
} as const;

export default async function OG() {
  const h = await headers();
  const surface = siteForHost(h.get('x-pastdoor-host') || h.get('host'));
  const t = SURFACE_OG[surface.key];

  return new ImageResponse(
    (
      <div
        style={{
          width: '100%', height: '100%',
          display: 'flex', flexDirection: 'column',
          background: t.bg,
          padding: '80px 96px',
          fontFamily: 'serif',
        }}
      >
        {/* GucciStripe at top — shared family signal */}
        <div style={{ display: 'flex', height: 6, width: '100%', marginBottom: 60 }}>
          <div style={{ flex: 1, background: '#1f4d40' }} />
          <div style={{ flex: 1, background: '#c9292e' }} />
          <div style={{ flex: 1, background: '#1f4d40' }} />
        </div>

        {/* Kicker */}
        <div
          style={{
            display: 'flex',
            background: t.accent,
            color: '#fdf9ee',
            padding: '6px 14px',
            fontSize: 18,
            letterSpacing: 4,
            textTransform: 'uppercase',
            fontWeight: 700,
            alignSelf: 'flex-start',
            marginBottom: 32,
          }}
        >
          {t.kicker}
        </div>

        {/* Brand line */}
        <div style={{ display: 'flex', flexDirection: 'column', flex: 1, justifyContent: 'center' }}>
          <div style={{ fontSize: 96, color: t.ink, fontWeight: 400, letterSpacing: -3, lineHeight: 1.05, fontStyle: 'italic' }}>
            {surface.tagline}
          </div>
          <div style={{ fontSize: 36, color: t.accent, marginTop: 24, fontWeight: 500 }}>
            {surface.brandName}
          </div>
        </div>

        {/* Footer */}
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-end', fontSize: 18, color: t.ink, opacity: 0.55 }}>
          <span>{surface.domain}</span>
          <span>Public record · public domain · public archive</span>
        </div>
      </div>
    ),
    size
  );
}