From 74b6b7b77feb5ee9987555765d9f4b8e9503b518 Mon Sep 17 00:00:00 2001 From: Nils Briggen Date: Fri, 12 Jun 2026 08:24:49 +0200 Subject: [PATCH] Best-thirds race: the cross-group table nobody can track by eye MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All twelve third-placed teams ranked by points/GD/goals under the group grid — top 8 highlighted (they reach the Round of 32), the rest dimmed. Appears once group play has started. Co-Authored-By: Claude Fable 5 --- src/features/groups/GroupsPage.tsx | 35 ++++++++++++++++++++++++++++++ src/lib/i18n/de.ts | 2 ++ src/lib/i18n/en.ts | 2 ++ 3 files changed, 39 insertions(+) diff --git a/src/features/groups/GroupsPage.tsx b/src/features/groups/GroupsPage.tsx index 72ea4c5..96b2ec8 100644 --- a/src/features/groups/GroupsPage.tsx +++ b/src/features/groups/GroupsPage.tsx @@ -41,6 +41,39 @@ function Scenarios({ scenarios }: { scenarios: TeamScenario[] }) { ); } +/** The cross-group race nobody can track by eye: every 3rd-placed team ranked + * by points/GD/goals — the top 8 advance to the Round of 32. */ +function BestThirdsRace({ tables }: { tables: Record }) { + const t = useT(); + const thirds = Object.entries(tables) + .map(([g, rows]) => ({ g, r: rows.find((r) => r.rank === 3) })) + .filter((x): x is { g: string; r: import('@/lib/types').StandingRow } => Boolean(x.r)) + .sort((a, b) => b.r.points - a.r.points || b.r.gd - a.r.gd || b.r.gf - a.r.gf || a.r.team.localeCompare(b.r.team)); + if (thirds.length < 12 || thirds.every((x) => x.r.played === 0)) return null; + return ( +
+
{t.groups.thirdsRace}
+
    + {thirds.map(({ g, r }, i) => ( +
  • + {i + 1} + {teamFlag(r.team)} + {r.team} + {fmt(t.common.group, { group: g })} + + {r.played} · {r.gd > 0 ? `+${r.gd}` : r.gd} · {r.points} + +
  • + ))} +
+

{t.groups.thirdsRaceNote}

+
+ ); +} + export function GroupsPage() { const t = useT(); const snapshot = useTournamentStore((s) => s.snapshot); @@ -91,6 +124,8 @@ export function GroupsPage() { )} + +
diff --git a/src/lib/i18n/de.ts b/src/lib/i18n/de.ts index 8b46ff4..4b5f09e 100644 --- a/src/lib/i18n/de.ts +++ b/src/lib/i18n/de.ts @@ -174,6 +174,8 @@ export const de: Dict = { scAlive: "Noch möglich ({n}/{total} Ausgänge)", scOut: "Top 2 nicht mehr erreichbar", scNote: "Die Szenarien zählen Sieg/Unentschieden/Niederlage der verbleibenden Gruppenspiele; knappe Fälle können trotzdem an der Tordifferenz hängen. Platz 3 läuft über den gruppenübergreifenden Vergleich der besten Dritten.", + thirdsRace: "Rennen der besten Dritten", + thirdsRaceNote: "Alle Gruppendritten, sortiert nach Punkten, Tordifferenz und Toren — die besten 8 erreichen das Sechzehntelfinale. Spiele · TD · Punkte.", tableHeaderPldPts: "Sp. · Pkt.", colTeam: "Team", colPlayed: "Sp", diff --git a/src/lib/i18n/en.ts b/src/lib/i18n/en.ts index c5e0557..18e0fab 100644 --- a/src/lib/i18n/en.ts +++ b/src/lib/i18n/en.ts @@ -173,6 +173,8 @@ export const en = { scAlive: "Still possible ({n}/{total} outcomes)", scOut: "Can't reach the top 2", scNote: "Scenarios count win/draw/loss outcomes of the remaining group matches; tight cases can still hinge on goal difference. Third place runs through the best-thirds race across all groups.", + thirdsRace: "Best-thirds race", + thirdsRaceNote: "All third-placed teams ranked by points, goal difference and goals — the top 8 advance to the Round of 32. Played · GD · Points.", tableHeaderPldPts: "Pld · Pts", colTeam: "Team", colPlayed: "P",