// Projected Standings (from simulations)
{
if (leagueView !== "Projected") return html``
const isCup = cupCodes.has(selectedLeague.value)
const sourceData = isCup ? cupSimData : simData
if (!sourceData || !selectedLeague) return html`<p class="text-muted">Simulation data not available.</p>`
if (window.footballMaps.loadCrests) await window.footballMaps.loadCrests()
const leagueData = sourceData.filter(d => d.league === selectedLeague.value)
if (leagueData.length === 0) return html`<p class="text-muted">No simulation data for this league.</p>`
const pctFmt = x => x != null ? (x * 100).toFixed(1) + "%" : ""
// Join current standings from fixture data (null if no finished matches in this league)
const currentMap = new Map()
if (standings) {
for (const s of standings) currentMap.set(s.team, s)
}
// Compute projected totals (current actuals + mean simulated remaining)
const enriched = leagueData.map(d => {
const curr = currentMap.get(d.team) || {}
const curPts = curr.pts ?? d.current_points ?? 0
const curGd = curr.gd ?? d.current_gd ?? 0
const curGp = curr.p ?? d.games_played ?? null
return {
...d,
games_played: curGp,
current_points: curPts,
current_gd: curGd,
proj_total_pts: curPts + (d.avg_points || 0),
proj_total_gd: curGd + (d.avg_gd || 0),
}
})
const teamRender = window.footballMaps?.renderTeamCell || ((v) => `<strong>${statsEsc(v)}</strong>`)
if (isCup) {
// Cup-specific projected view: league phase position + knockout progression
return statsTable(enriched, {
columns: [
"team", "games_played", "current_points", "current_gd",
"avg_position", "auto_r16_pct", "playoff_pct", "eliminated_league_pct",
"r16_pct", "qf_pct", "sf_pct", "final_pct", "winner_pct"
],
mobileCols: ["team", "current_points", "avg_position", "r16_pct", "winner_pct"],
header: {
team: "Team", games_played: "GP", current_points: "Pts", current_gd: "GD",
avg_position: "Pos", auto_r16_pct: "Auto", playoff_pct: "Playoff", eliminated_league_pct: "Out",
r16_pct: "R16", qf_pct: "QF", sf_pct: "SF", final_pct: "Final", winner_pct: "Win"
},
groups: [
{ label: "", span: 1 }, { label: "Current", span: 3 },
{ label: "League Phase", span: 4 }, { label: "Knockout Probability", span: 5 }
],
format: {
current_gd: x => x != null ? (x > 0 ? "+" : "") + x : "",
avg_position: x => x?.toFixed(1) ?? "",
auto_r16_pct: pctFmt, playoff_pct: pctFmt, eliminated_league_pct: pctFmt,
r16_pct: pctFmt, qf_pct: pctFmt, sf_pct: pctFmt, final_pct: pctFmt, winner_pct: pctFmt
},
render: { team: teamRender },
heatmap: {
avg_position: "low-good",
auto_r16_pct: "high-good", playoff_pct: "high-good", eliminated_league_pct: "low-good",
r16_pct: "high-good", qf_pct: "high-good", sf_pct: "high-good", final_pct: "high-good", winner_pct: "high-good"
},
tooltip: {
games_played: "League phase games played", current_points: "Current league phase points", current_gd: "Current goal difference",
avg_position: "Average league phase finishing position",
auto_r16_pct: "Probability of finishing top 8 (automatic R16 qualification)",
playoff_pct: "Probability of finishing 9th-24th (playoff round)",
eliminated_league_pct: "Probability of elimination in league phase (25th-36th)",
r16_pct: "Probability of reaching the Round of 16",
qf_pct: "Probability of reaching the Quarter-Finals",
sf_pct: "Probability of reaching the Semi-Finals",
final_pct: "Probability of reaching the Final",
winner_pct: "Probability of winning the competition"
},
sort: "winner_pct",
reverse: true,
rows: 36
})
}
// Domestic league projected view
return statsTable(enriched, {
columns: [
"team", "games_played", "current_points", "current_gd",
"proj_total_pts", "proj_total_gd", "avg_position",
"title_pct", "top_4_pct", "top_half_pct", "bottom_3_pct"
],
mobileCols: ["team", "current_points", "proj_total_pts", "title_pct", "top_4_pct"],
header: {
team: "Team", games_played: "GP", current_points: "Pts", current_gd: "GD",
proj_total_pts: "Pts", proj_total_gd: "GD", avg_position: "Pos",
title_pct: "Title", top_4_pct: "Top 4", top_half_pct: "Top Half", bottom_3_pct: "Bottom 3"
},
groups: [
{ label: "", span: 1 }, { label: "Current", span: 3 },
{ label: "Projected", span: 3 }, { label: "Probabilities", span: 4 }
],
format: {
current_gd: x => x != null ? (x > 0 ? "+" : "") + x : "",
proj_total_pts: x => x?.toFixed(0) ?? "",
proj_total_gd: x => x != null ? (x > 0 ? "+" : "") + x.toFixed(0) : "",
avg_position: x => x?.toFixed(1) ?? "",
title_pct: pctFmt, top_4_pct: pctFmt, top_half_pct: pctFmt, bottom_3_pct: pctFmt
},
render: { team: teamRender },
heatmap: {
proj_total_pts: "high-good", avg_position: "low-good",
title_pct: "high-good", top_4_pct: "high-good", top_half_pct: "high-good", bottom_3_pct: "low-good"
},
tooltip: {
games_played: "Games played so far", current_points: "Current points", current_gd: "Current goal difference",
proj_total_pts: "Projected final points (current + simulated remaining)",
proj_total_gd: "Projected final goal difference",
avg_position: "Average finishing position from simulations",
title_pct: "Probability of winning the league",
top_4_pct: "Probability of finishing in top 4",
top_half_pct: "Probability of finishing in top half",
bottom_3_pct: "Probability of finishing in bottom 3 (relegation zone)"
},
sort: "proj_total_pts",
reverse: true,
rows: 25
})
}