8223a566e4
A design handoff bundle generated by Claude Design (claude.ai/design) on 2026-05-02. Defines the Bloomberg/F1-pit-wall aesthetic for TRM: - ink-on-paper base + race-flag red accent (#E8412B) - square-edged everything, sharp printed offset shadows - mono numerics (JetBrains Mono) for any changing value - Goldplay (real licensed font, three weights in bundle fonts/) - four surfaces designed: dashboard / leaderboard / mobile / marketing (SPA scope is the first two) The bundle is committed in-tree at TRM_Design_System-handoff/ so 3.8 has the full source material when it picks the work up. Includes: - Top-level + project READMEs (the design spec) - chats/chat1.md (intent + iteration history) - colors_and_type.css (token set, drop-in for Tailwind 4 @theme) - fonts/ (Goldplay regular/semibold/bold) - ui_kits/ (HTML prototypes per surface) - preview/ (per-token visual reference cards) Updated phase-3-dogfood-readiness/README.md task 3.8 row to point at the bundle and document the recommended approach (retheme shadcn via CSS variable overrides + Tailwind 4 @theme, not replace). Why deferred: foundational tokens are non-blocking for Phase 1 (login + placeholder home) and Phase 2 (live map without chrome). Applying them now would either delay dogfood-blocking work or land partial styling that gets reworked when 3.8 lands the full pass.
97 lines
5.2 KiB
HTML
97 lines
5.2 KiB
HTML
<!doctype html>
|
|
<html><head><meta charset="utf-8">
|
|
<title>TRM Live Leaderboard</title>
|
|
<link rel="stylesheet" href="../../colors_and_type.css">
|
|
<link rel="stylesheet" href="../kit.css">
|
|
<style>
|
|
body { background: var(--night); color: var(--night-fg); }
|
|
.pill.live { color: var(--flag); border-color: var(--flag); }
|
|
table.lb.night { border-color: var(--night-line); }
|
|
table.lb.night th { background: var(--night-3); color: var(--night-fg-3); border-bottom-color: var(--night-line); }
|
|
table.lb.night td { border-bottom-color: var(--night-line); color: var(--night-fg); font-size: 14px; height: 36px; }
|
|
table.lb.night tr:hover td { background: var(--night-3); }
|
|
.loss { color: #ff8a78 !important; }
|
|
.card { background: var(--night-2); border-color: var(--night-line); color: var(--night-fg); }
|
|
.card-head { background: var(--night-3); border-bottom-color: var(--night-line); }
|
|
.card-head h3 { color: var(--night-fg); }
|
|
</style>
|
|
<script src="https://unpkg.com/react@18.3.1/umd/react.development.js" integrity="sha384-hD6/rw4ppMLGNu3tX5cjIb+uRZ7UkRJ6BPkLpg4hAu/6onKUg4lLsHAs9EBPT82L" crossorigin="anonymous"></script>
|
|
<script src="https://unpkg.com/react-dom@18.3.1/umd/react-dom.development.js" integrity="sha384-u6aeetuaXnQ38mYT8rp6sbXaQe3NL9t+IBXmnYxwkUI2Hw4bsp2Wvmx4yRQF1uAm" crossorigin="anonymous"></script>
|
|
<script src="https://unpkg.com/@babel/standalone@7.29.0/babel.min.js" integrity="sha384-m08KidiNqLdpJqLq95G/LEi8Qvjl/xUYll3QILypMoQ65QorJ9Lvtp2RXYGBFj1y" crossorigin="anonymous"></script>
|
|
</head>
|
|
<body>
|
|
<div id="root"></div>
|
|
<script type="text/babel" src="HeroClock.jsx"></script>
|
|
<script type="text/babel" src="LiveTable.jsx"></script>
|
|
<script type="text/babel" src="MiniMap.jsx"></script>
|
|
<script type="text/babel" src="Ticker.jsx"></script>
|
|
<script type="text/babel">
|
|
const { useState, useEffect } = React;
|
|
|
|
const ROWS = [
|
|
{ pos: 1, bib: 247, name: 'Maya Chen', wave: 'Elite', split: '00:42:18.4', gap: '—', pace: '04:13', last: '0:18 ago' },
|
|
{ pos: 2, bib: 188, name: 'Ravi Park', wave: 'Elite', split: '00:42:22.6', gap: '+0:04.2', pace: '04:14', last: '0:22 ago' },
|
|
{ pos: 3, bib: 44, name: 'Noemi Vega', wave: 'Elite', split: '00:42:30.1', gap: '+0:11.7', pace: '04:15', last: '0:30 ago' },
|
|
{ pos: 4, bib: 302, name: 'Tomás Riera', wave: 'Elite', split: '00:42:55.0', gap: '+0:36.6', pace: '04:18', last: '0:55 ago' },
|
|
{ pos: 5, bib: 119, name: 'Anika Joshi', wave: 'Elite', split: '00:43:01.2', gap: '+0:42.8', pace: '04:19', last: '1:01 ago' },
|
|
{ pos: 6, bib: 401, name: 'Felix Okafor', wave: 'Open', split: '00:43:08.7', gap: '+0:50.3', pace: '04:19', last: '1:08 ago' },
|
|
{ pos: 7, bib: 77, name: 'Sara Lindqvist',wave: 'Open', split: '00:43:14.0', gap: '+0:55.6', pace: '04:20', last: '1:14 ago' },
|
|
{ pos: 8, bib: 215, name: 'David Müller', wave: 'Open', split: '00:43:22.4', gap: '+1:04.0', pace: '04:21', last: '1:22 ago' },
|
|
{ pos: 9, bib: 503, name: 'Inés Báez', wave: 'Open', split: '00:43:30.6', gap: '+1:12.2', pace: '04:21', last: '1:30 ago' },
|
|
{ pos:10, bib: 162, name: 'Kenji Sato', wave: 'Open', split: '00:43:38.0', gap: '+1:19.6', pace: '04:22', last: '1:38 ago' },
|
|
];
|
|
|
|
const CHECKPOINTS = [
|
|
{ label: 'START', km: 0, x: 30, y: 120, passed: true },
|
|
{ label: 'CP1', km: 2.5, x: 150, y: 60, passed: true },
|
|
{ label: 'CP2', km: 5.0, x: 270, y: 80, passed: true },
|
|
{ label: 'CP3', km: 7.5, x: 380, y: 70, passed: true },
|
|
{ label: 'CP4', km: 9.0, x: 480, y: 90, passed: false },
|
|
{ label: 'FINISH', km: 10, x: 570, y: 110, passed: false },
|
|
];
|
|
|
|
const TICKER = [
|
|
{ cp: 'CP3', bib: 247, name: 'M. Chen', time: '00:42:18.4', delta: '-0:01.6' },
|
|
{ cp: 'CP3', bib: 188, name: 'R. Park', time: '00:42:22.6', delta: '+0:00.4' },
|
|
{ cp: 'CP3', bib: 44, name: 'N. Vega', time: '00:42:30.1', delta: '+0:01.9' },
|
|
{ cp: 'CP3', bib: 302, name: 'T. Riera', time: '00:42:55.0', delta: '+0:04.4' },
|
|
{ cp: 'CP3', bib: 119, name: 'A. Joshi', time: '00:43:01.2', delta: '-0:02.1' },
|
|
{ cp: 'CP3', bib: 401, name: 'F. Okafor', time: '00:43:08.7', delta: '+0:00.6' },
|
|
];
|
|
|
|
function App() {
|
|
const [t, setT] = useState({ h: 1, m: 23, s: 45, ms: 6 });
|
|
useEffect(() => {
|
|
const id = setInterval(() => {
|
|
setT(p => {
|
|
let ms = p.ms + 1;
|
|
let s = p.s, m = p.m, h = p.h;
|
|
if (ms > 9) { ms = 0; s++; }
|
|
if (s > 59) { s = 0; m++; }
|
|
if (m > 59) { m = 0; h++; }
|
|
return { h, m, s, ms };
|
|
});
|
|
}, 100);
|
|
return () => clearInterval(id);
|
|
}, []);
|
|
const pad = n => String(n).padStart(2, '0');
|
|
const time = `${pad(t.h)}:${pad(t.m)}:${pad(t.s)}.${t.ms}`;
|
|
|
|
return (
|
|
<div style={{ minHeight: '100vh', display: 'flex', flexDirection: 'column' }}>
|
|
<HeroClock time={time} eventName="Coastline 10K · Spring 2026" sport="Run · 10K" distance="Coastal route, flat" weather="18°C · light SW · dry" />
|
|
<div style={{ flex: 1, padding: '24px 32px', display: 'grid', gridTemplateColumns: '2fr 1fr', gap: 24, minHeight: 0 }}>
|
|
<div style={{ minHeight: 0, overflow: 'hidden' }}>
|
|
<LiveTable rows={ROWS} />
|
|
</div>
|
|
<MiniMap checkpoints={CHECKPOINTS} />
|
|
</div>
|
|
<Ticker items={TICKER} />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
|
|
</script>
|
|
</body></html>
|