DS4 (design): billboards — STREAKS row, grade reveal, CLV reframe, /u profile

P0 billboards (the timeline is customer #1). Pixel-level craft: one bold hero
among muted context, real entities from DS0.

- STREAKS row: rebuilt from a log line into a Bloomberg alert. Streak LENGTH is
  now the mono/tabular HERO (38px, the largest figure in the row); real
  PlayerAvatar identity; muted "built vs [opponents]" lens; ONE severity accent
  (step-up amber / step-down green); grade badge tier-gated (the READ is paid).
- Grade reveal: edge is now sign-colored — negative edge uses var(--miss),
  never green (color contract #3). Fixed in BOTH the confidence strip and the
  MODEL/LINE/EDGE row; that row is now mono + tabular. Letter stays the hero.
- CLV reframe: new pure lib/clvDisplay.js (clvMode flat/spread/none). A near-
  flat distribution (73/74) now renders a confident VOICE line — "CLV flat — we
  grade the outcome, not the close" — instead of a broken-looking histogram;
  bars show only on real spread.
- /u profile: hit% is the bold record hero (56px mono tabular) + beat-close
  secondary; honest CLV-VERIFIED badge (only when closing value is tracked);
  real PlayerAvatar identity on cards; OG image elevated to carry the real
  record at 1200x630 social crop (graceful tagline fallback).

Tests: +23 (tests/unit/ds4Billboards.test.js). Full suite green (2732).
web build exit 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Kev
2026-07-12 19:25:57 -04:00
parent 24af247b29
commit 45bafbc01a
7 changed files with 452 additions and 74 deletions
+57 -20
View File
@@ -2,6 +2,7 @@
import { useEffect, useState } from 'react';
import { GradePill } from '@/components/GradeCard';
import PlayerAvatar from '@/components/vyndr/PlayerAvatar';
/**
* PublicProfile (A1 Session 10) — the public ledger record for one handle.
@@ -144,30 +145,60 @@ export default function PublicProfile({ handle }: { handle: string }) {
);
}
/**
* The record HERO (DS4 · billboard — DESIGN-SPEC Part 3 + Part 6 #4). This is
* the shareable surface Kev pitches partners with. One bold figure among muted
* context: the HIT RATE as a large mono tabular number, beat-close as the
* secondary bold figure, the raw record demoted. CLV-VERIFIED badge only when
* closing-line value is actually tracked (honest — never a decorative badge).
*/
function RecordHeader({ agg, minSample }: { agg: ProfileAggregate | null; minSample: number }) {
const ready = Boolean(agg && agg.settled >= minSample && agg.hit_pct != null);
const clvVerified = Boolean(ready && agg && agg.beat_close_pct != null);
return (
<div
className="surface diagonal-cut"
style={{ padding: 20, marginBottom: 20, border: `1px solid ${ready ? 'var(--g-a, #00D4A0)' : 'var(--border)'}` }}
style={{ padding: 24, marginBottom: 20, border: `1px solid ${ready ? 'var(--g-a, #00D4A0)' : 'var(--border)'}` }}
>
{ready && agg ? (
<div style={{ display: 'flex', gap: 24, flexWrap: 'wrap', alignItems: 'baseline' }}>
<span className="mono" style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.1em', color: 'var(--text-tertiary)' }}>
RECORD · LAST 30D
</span>
<span className="mono" style={{ fontSize: 20, fontWeight: 800, color: 'var(--g-a, #00D4A0)' }}>
{agg.hits}-{agg.misses} · {agg.hit_pct}% HIT
</span>
{agg.beat_close_pct != null && (
<span className="mono" style={{ fontSize: 20, fontWeight: 800, color: 'var(--text-primary)' }}>
{agg.beat_close_pct}% BEAT CLOSE
<>
<div style={{ display: 'flex', gap: 12, alignItems: 'center', flexWrap: 'wrap', marginBottom: 16 }}>
<span className="mono" style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.1em', color: 'var(--text-tertiary)' }}>
RECORD · LAST 30D
</span>
)}
<span className="mono" style={{ fontSize: 12, color: 'var(--text-tertiary)' }}>
{agg.pending} pending
</span>
</div>
{clvVerified && (
<span className="mono" style={{ fontSize: 10.5, fontWeight: 800, letterSpacing: '0.08em', color: 'var(--g-a, #00D4A0)', border: '1px solid var(--g-a, #00D4A0)', borderRadius: 999, padding: '3px 10px', background: 'color-mix(in srgb, var(--g-a, #00D4A0) 12%, transparent)' }}>
CLV-VERIFIED
</span>
)}
</div>
{/* HERO figures — hit rate is the largest thing on the page. */}
<div style={{ display: 'flex', gap: 40, flexWrap: 'wrap', alignItems: 'flex-end' }}>
<div>
<div className="mono" style={{ fontSize: 56, fontWeight: 800, lineHeight: 0.95, letterSpacing: '-0.03em', color: 'var(--g-a, #00D4A0)', fontVariantNumeric: 'tabular-nums' }}>
{agg.hit_pct}%
</div>
<div className="mono" style={{ fontSize: 10.5, fontWeight: 700, letterSpacing: '0.12em', color: 'var(--text-tertiary)', marginTop: 4 }}>
HIT RATE · {agg.hits}-{agg.misses}
</div>
</div>
{agg.beat_close_pct != null && (
<div>
<div className="mono" style={{ fontSize: 40, fontWeight: 800, lineHeight: 0.95, letterSpacing: '-0.02em', color: 'var(--text-primary)', fontVariantNumeric: 'tabular-nums' }}>
{agg.beat_close_pct}%
</div>
<div className="mono" style={{ fontSize: 10.5, fontWeight: 700, letterSpacing: '0.12em', color: 'var(--text-tertiary)', marginTop: 4 }}>
BEAT CLOSE
</div>
</div>
)}
<div style={{ marginLeft: 'auto' }}>
<div className="mono" style={{ fontSize: 14, fontWeight: 600, color: 'var(--text-tertiary)', fontVariantNumeric: 'tabular-nums' }}>
{agg.pending} pending
</div>
</div>
</div>
</>
) : (
<div>
<p className="mono" style={{ fontSize: 13, fontWeight: 800, letterSpacing: '0.1em', color: 'var(--amber, #FFB347)', marginBottom: 6 }}>
@@ -230,10 +261,16 @@ function ProfileCard({ row, index }: { row: ProfileRow; index: number }) {
<GradePill grade={row.grade} />
</span>
</div>
<h3 style={{ fontSize: 15, fontWeight: 600, marginBottom: 2 }}>{row.player_name}</h3>
<p className="mono" style={{ fontSize: 12, color: 'var(--text-secondary)', textTransform: 'capitalize', marginBottom: 4 }}>
{row.side} {row.line} {row.stat.replace(/_/g, ' ')}
</p>
{/* Real entity identity (DS0) — headshot / team-colored monogram. */}
<div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 4 }}>
<PlayerAvatar name={row.player_name} sport={row.sport} size={34} />
<div style={{ minWidth: 0 }}>
<h3 style={{ fontSize: 15, fontWeight: 600, marginBottom: 1, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{row.player_name}</h3>
<p className="mono" style={{ fontSize: 12, color: 'var(--text-secondary)', textTransform: 'capitalize', margin: 0 }}>
{row.side} {row.line} {row.stat.replace(/_/g, ' ')}
</p>
</div>
</div>
<p className="mono" style={{ fontSize: 11, color: 'var(--text-tertiary)', marginBottom: 12 }}>
{row.book || '—'}{row.locked_odds ? ` · ${row.locked_odds}` : ''} · {row.game_date}
{row.model_value != null && <span> · MODEL {row.model_value}</span>}