Surrounding screens refresh: live hero, row-style match cards, form bars, unified bracket

Design handoff steps 4-5:
- LivePage: headline live-match hero (gradient card, live ring, smallcaps
  stage/venue + minute, big mono score, 3-segment in-play win-prob bar);
  favorite team's live match takes the hero slot
- MatchCard: played matches as stacked team rows with the beaten side
  dimmed; scheduled matches as a centered 'vs' pairing (model hint and
  venue stay)
- GroupTable: Form column — tiny W/D/L bars (accent/gold/loss) computed
  from finished group matches
- BracketPage: one round-selector layout on every screen size (cards in
  a 2-col grid, losers dimmed), gold 'Projected champion' banner on the
  Final view; what-if mode, advance bars, R32 and the third-place
  play-off all unchanged
- Predict title race: leader bar, rank and percentage in gold

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-06-12 11:37:06 +02:00
parent d1d294da5d
commit 4faedd60c4
8 changed files with 204 additions and 77 deletions
+27 -1
View File
@@ -4,8 +4,32 @@ import { teamFlag } from '@/lib/teams';
import { fmt, useT } from '@/lib/i18n';
import type { StandingRow } from '@/lib/types';
export type FormLetter = 'W' | 'D' | 'L';
/** Tiny result bars, oldest → newest: W accent · D gold · L loss. */
function FormBars({ form }: { form: FormLetter[] }) {
const t = useT();
if (!form.length) return <span className="text-faint"></span>;
return (
<span className="inline-flex items-end gap-[3px]" role="img" aria-label={form.join(' ')}>
{form.map((r, i) => (
<span
key={i}
title={t.groups.formLetter[r]}
className={cn(
'inline-block w-1.5 rounded-[2px]',
r === 'W' && 'h-3.5 bg-accent',
r === 'D' && 'h-2.5 bg-gold',
r === 'L' && 'h-2.5 bg-loss',
)}
/>
))}
</span>
);
}
/** One group's standings. Top 2 qualify directly; 3rd may advance as a best-third. */
export function GroupTable({ group, rows }: { group: string; rows: StandingRow[] }) {
export function GroupTable({ group, rows, form }: { group: string; rows: StandingRow[]; form?: Record<string, FormLetter[]> }) {
const t = useT();
return (
<div className="overflow-hidden rounded-xl border border-line bg-panel">
@@ -20,6 +44,7 @@ export function GroupTable({ group, rows }: { group: string; rows: StandingRow[]
<th className="w-8 py-1.5 text-center font-semibold">{t.groups.colPlayed}</th>
<th className="w-8 py-1.5 text-center font-semibold">{t.groups.colGoalDiff}</th>
<th className="w-9 py-1.5 text-center font-semibold">{t.groups.colPoints}</th>
{form && <th className="w-10 py-1.5 pr-3 text-center font-semibold">{t.groups.colForm}</th>}
</tr>
</thead>
<tbody>
@@ -51,6 +76,7 @@ export function GroupTable({ group, rows }: { group: string; rows: StandingRow[]
{r.gd > 0 ? `+${r.gd}` : r.gd}
</td>
<td className="py-1.5 text-center font-bold tabular-nums text-ink">{r.points}</td>
{form && <td className="py-1.5 pr-3 text-center"><FormBars form={form[r.team] ?? []} /></td>}
</tr>
))}
</tbody>