Session 53: Social preview fix — OG meta tags + image (2246 tests)

The link preview still showed engineer-speak ("Bayesian intelligence", "kill
conditions") because the OG meta tags were never updated after the S44 landing
cleanup.

- layout.tsx: de-jargoned the openGraph + main description + titles → "Pre-graded
  player props with proprietary archetypes. Correlation-aware Parlay Lab.
  Real-time line movement tracking. Built in Detroit." (Used "proprietary
  archetypes" not "45" — codebase has 41.)
- NEW app/opengraph-image.tsx (+ twitter-image.tsx re-export): dynamically
  generated 1200x630 card — VYNDR wordmark, "The books have every advantage. /
  We built this to give it back." + feature row. Node runtime (not edge —
  self-hosted standalone). Both routes prerender to a real PNG.
- Removed the stale /og-image.png metadata ref so the file convention owns the
  image (no duplicate og:image tag).

Backend 2239 -> 2246 tests (+7), 193 suites. Web build clean (exit 0).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Kev
2026-06-19 14:25:05 -04:00
parent cdedecf55b
commit b012da13f8
7 changed files with 151 additions and 11 deletions
+8 -7
View File
@@ -46,7 +46,7 @@ export const metadata: Metadata = {
template: '%s · VYNDR',
},
description:
"Grade your props across every sport with intelligence the books don't want you to have. NBA, MLB, WNBA, and soccer today — NFL and more through 2026. World Cup 2026 intelligence: xG regression, altitude, referee, penalty taker. Built in Detroit.",
'Pre-graded player props with proprietary archetypes. Correlation-aware Parlay Lab. Real-time line movement tracking. NBA, MLB, WNBA, and soccer. Built in Detroit.',
applicationName: 'VYNDR',
authors: [{ name: 'VYNDR', url: 'https://vyndr.app' }],
manifest: '/manifest.json',
@@ -62,20 +62,21 @@ export const metadata: Metadata = {
'prop betting tools',
],
openGraph: {
title: "VYNDR — Intelligence the books don't want you to have",
title: "VYNDR — The edge the books don't want you to have",
description:
'Read player props with Bayesian intelligence. See the factors. Know the kill conditions. Take the edge back.',
'Pre-graded player props with proprietary archetypes. Correlation-aware Parlay Lab. Real-time line movement tracking. Built in Detroit.',
url: 'https://vyndr.app',
siteName: 'VYNDR',
images: [{ url: '/og-image.png', width: 1200, height: 630, alt: 'VYNDR — Sports Prop Intelligence' }],
locale: 'en_US',
type: 'website',
// og:image is supplied by the file-based opengraph-image.tsx convention.
},
twitter: {
card: 'summary_large_image',
title: 'VYNDR',
description: 'The books have every advantage. We built this to give it back.',
images: ['/og-image.png'],
title: "VYNDR — The edge the books don't want you to have",
description: 'Pre-graded player props. Proprietary archetypes. Parlay Lab. Built in Detroit.',
creator: '@getvyndr',
// twitter:image is supplied by the file-based twitter-image.tsx convention.
},
icons: {
icon: [
+56
View File
@@ -0,0 +1,56 @@
import { ImageResponse } from 'next/og';
// Self-hosted standalone build → Node runtime (NOT edge). next/og renders fine
// under Node; edge runtime isn't available outside Vercel's edge platform.
export const alt = "VYNDR — The edge the books don't want you to have";
export const size = { width: 1200, height: 630 };
export const contentType = 'image/png';
export default function Image() {
return new ImageResponse(
(
<div
style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
width: '100%',
height: '100%',
background: '#06060B',
color: '#fff',
padding: 64,
}}
>
{/* Top hairline accent */}
<div style={{ display: 'flex', position: 'absolute', top: 0, left: 0, right: 0, height: 6, background: '#00D4A0' }} />
{/* Wordmark */}
<div style={{ display: 'flex', fontSize: 96, fontWeight: 900, letterSpacing: '0.06em' }}>
<span>VYND</span>
<span style={{ color: '#00D4A0' }}>R</span>
</div>
{/* Tagline */}
<div style={{ display: 'flex', fontSize: 30, color: '#B8BCC8', marginTop: 28 }}>
The books have every advantage.
</div>
<div style={{ display: 'flex', fontSize: 30, color: '#00D4A0', marginTop: 6, fontWeight: 700 }}>
We built this to give it back.
</div>
{/* Feature row */}
<div style={{ display: 'flex', gap: 18, marginTop: 48, fontSize: 18, color: '#707080', letterSpacing: '0.08em' }}>
<span>PRE-GRADED PROPS</span>
<span style={{ color: '#3A3A48' }}>·</span>
<span>ARCHETYPES</span>
<span style={{ color: '#3A3A48' }}>·</span>
<span>PARLAY LAB</span>
<span style={{ color: '#3A3A48' }}>·</span>
<span>BUILT IN DETROIT</span>
</div>
</div>
),
{ ...size },
);
}
+2
View File
@@ -0,0 +1,2 @@
// Twitter's summary_large_image reuses the same generated card as Open Graph.
export { default, alt, size, contentType } from './opengraph-image';