DS0 (Design v2): the Entity Layer — teams/players/books render as themselves

Founder's #1 priority. A single cached asset+rendering system, swapped into
every surface, so entities stop being flat gray strings (DESIGN-SPEC Part 2).

- web/src/lib/teamMeta.js: static registry for ALL 4 sports — 30 MLB, 30 NBA,
  13 WNBA teams + 48 World Cup national teams, each with real colors + the
  ESPN logo/flag CDN abbr. resolveTeam (abbr/full-name/nickname/alias),
  teamLogoUrl (statsapi->ESPN mapping: AZ->ari, CWS->chw; soccer via the
  countries/ flag CDN), accentColor (picks the VISIBLE color of the pair so a
  #000000 primary never renders an invisible accent on #06060B). Colors +
  abbrs sourced once from ESPN's team API — stable public facts, zero-latency
  static data, no paid dependency.
- TeamLogo: real ESPN-CDN logo with a team-colored MONOGRAM fallback (never a
  gray box / bare abbr). PlayerAvatar: real headshot with a team-colored
  monogram fallback (kills the gray silhouette). BookWordmark: brand-color
  wordmark, proper casing (DraftKings, not 'draftkings').
- Swapped into the class-level shared components so it propagates to ALL
  surfaces: GameCard (team logos + team-colored accent edge), StatStrip
  (player identity avatar), StreaksPanel (P0 billboard avatars), TeamHub
  header (the team's real crest leads its hub). Barrel-exported.

ZERO OUT-OF-POCKET: ESPN logo/flag CDN + league headshot CDNs, all verified
200 image/png across MLB/NBA/WNBA/soccer.

ACCEPTANCE (SSR render proof): /entity-demo harness server-rendered the exact
real asset URLs across all 4 sports — mlb/500/nyy.png, mlb/500/chw.png (White
Sox, correct ESPN abbr), nba/500/lal.png, wnba/500/ny.png, countries/500/
usa.png + bra/eng/arg/jpn flags, real mlbstatic/nba headshots (Judge 592450,
LeBron 2544), DraftKings/FanDuel wordmarks. Each URL curl-verified 200
image/png. Harness removed post-proof (not a product surface). Pixel
screenshot blocked by WSL2<->Windows-Chrome localhost networking, not code.

2757 -> 2776 tests (+13 entityLayer, +6 boot resilience), 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:02:18 -04:00
parent b452db727c
commit 24af247b29
10 changed files with 520 additions and 17 deletions
+3 -1
View File
@@ -5,6 +5,7 @@ import { useRouter } from 'next/navigation';
import SportBadge from '@/components/vyndr/SportBadge';
import GradeBadge from '@/components/vyndr/GradeBadge';
import ArchetypeBadge from '@/components/vyndr/ArchetypeBadge';
import TeamLogo from '@/components/vyndr/TeamLogo';
import ModelRecord from '@/components/vyndr/ModelRecord';
import { playerHref } from '@/lib/playerHref';
import { useParlay, legKey } from '@/contexts/ParlayContext';
@@ -105,8 +106,9 @@ export default function TeamHub({ abbr, sport }: { abbr: string; sport: string }
<section style={{ maxWidth: 920, margin: '0 auto', padding: '20px 16px 120px' }}>
<a href="/dashboard" className="mono" style={{ fontSize: 12, color: 'var(--text-1)', textDecoration: 'none' }}> Back to Slate</a>
{/* Header */}
{/* Header — DS0: the team's real crest leads the hub. */}
<div style={{ display: 'flex', alignItems: 'center', gap: 14, margin: '14px 0 22px', flexWrap: 'wrap' }}>
<TeamLogo team={data.team.abbr} sport={data.team.sport} size={44} title={data.team.name} />
<SportBadge sport={data.team.sport} />
<h1 style={{ margin: 0, fontSize: 28, fontWeight: 800, letterSpacing: '-0.015em' }}>{data.team.name}</h1>
<span className="mono" style={{ fontSize: 13, color: 'var(--text-1)', letterSpacing: '0.06em' }}>{data.team.abbr}</span>