+ {CLV_FLAT_LINE} +
+
@@ -230,10 +261,16 @@ function ProfileCard({ row, index }: { row: ProfileRow; index: number }) {
- {row.side} {row.line} {row.stat.replace(/_/g, ' ')} -
+ {/* Real entity identity (DS0) — headshot / team-colored monogram. */} ++ {row.side} {row.line} {row.stat.replace(/_/g, ' ')} +
+{row.book || '—'}{row.locked_odds ? ` · ${row.locked_odds}` : ''} · {row.game_date} {row.model_value != null && · MODEL {row.model_value}} diff --git a/web/src/app/u/[handle]/opengraph-image.tsx b/web/src/app/u/[handle]/opengraph-image.tsx index 1473020..8b63f46 100644 --- a/web/src/app/u/[handle]/opengraph-image.tsx +++ b/web/src/app/u/[handle]/opengraph-image.tsx @@ -1,15 +1,36 @@ import { ImageResponse } from 'next/og'; -// A1 Session 10 — every shared /u/:handle link unfurls as a record card. -// Self-hosted standalone build → Node runtime (NOT edge; Session-53 rule — -// next/og breaks under edge off Vercel). +// A1 Session 10 · elevated DS4 — every shared /u/:handle link unfurls as a +// record card at social crop (1200×630). The billboard now carries the REAL +// record (hit% + beat-close%) when the handle is published + past the n-gate, +// so the timeline (customer #1) sees the proof, not just a tagline. Self-hosted +// standalone build → Node runtime (NOT edge; Session-53 rule). export const alt = 'VYNDR Public Ledger'; export const size = { width: 1200, height: 630 }; export const contentType = 'image/png'; +const BACKEND_URL = process.env.BACKEND_URL || 'http://localhost:3000'; + +async function fetchRecord(handle: string): Promise<{ hit_pct: number | null; beat_close_pct: number | null; hits: number; misses: number } | null> { + try { + const r = await fetch(`${BACKEND_URL}/api/profiles/${encodeURIComponent(handle)}`, { + headers: { Accept: 'application/json' }, + cache: 'no-store', + }); + if (!r.ok) return null; + const d = await r.json(); + const agg = d && d.aggregate; + if (!agg || agg.hit_pct == null) return null; + return { hit_pct: agg.hit_pct, beat_close_pct: agg.beat_close_pct ?? null, hits: agg.hits, misses: agg.misses }; + } catch { + return null; + } +} + export default async function Image({ params }: { params: Promise<{ handle: string }> }) { const { handle } = await params; const h = decodeURIComponent(handle || '').slice(0, 20).toLowerCase() || 'handle'; + const rec = await fetchRecord(h); return new ImageResponse( (