Loading…
inside template literal closing outer script tag) const ldScript = document.createElement("script"); ldScript.type = "application/ld+json"; ldScript.textContent = JSON.stringify(jsonld); document.head.appendChild(ldScript); let html = ` ← All countries
${flag}

${country.toUpperCase()}

${players.length} legend${players.length > 1 ? "s" : ""} Β· ${goats} GOAT Β· ${legends} Legend tier Β· combined squad value $${totalValue}

⚽ Build a ${country}-only squad

πŸ’° ${country}'s $10 cheat sheet

Best value (≀$2)
${bestValue.length ? bestValue.map(p => `${p.name} ($${p.price})`).join(" Β· ") : "No bargain legends"}
Marquee pick
${mvp.name} Β· $${mvp.price} Β· ${mvp.era}
Cheapest legend
${cheapest.name} Β· $${cheapest.price}
${sampleSquad ? `

πŸ† Best all-${country} $10 squad

Auto-built from the cheapest viable lineup. Tap any name to swap.

${sampleSquad.lineup.map(p => `
${PLAYER_POSITION[p.id]}${p.name}$${p.price}
`).join("")}
Total$${sampleSquad.total}/10
⚑ Open in builder
` : ""} `; ["GK","DEF","MID","ATT"].forEach(pos => { if (!byPos[pos].length) return; const label = { GK:"Goalkeepers", DEF:"Defenders", MID:"Midfielders", ATT:"Attackers" }[pos]; html += `

${label} (${byPos[pos].length})

`; byPos[pos].forEach(p => { html += ` `; }); html += `
`; }); // Cities to visit cross-link if (cities.length) { html += `

πŸ“ Visit ${country} on $10

Cities in ${country} ranked by base cost. Each has a wiki guide + $10 sample trip.

${cities.sort((a,b) => a.price - b.price).map(c => `
${c.flag}
${c.name}
$${c.price}
`).join("")}
`; } // Other countries to explore html += `

🌍 Compare other nations

${relatedCountries(country, 6).map(c => `
${c.flag}
${c.name}
${c.count} legend${c.count > 1 ? "s" : ""}
`).join("")}
`; // Country Hall of Fame placeholder (filled async after render) html += `

πŸ† ${country} Hall of Fame

Top user-submitted $10 squads with 2+ ${country} players. 🟒 Live

Loading top squads from this country…
`; // Debate widget html += debateWidget("country:" + country, `Is ${country} the best football nation?`); root.innerHTML = html; // Hydrate HoF from Supabase loadCountryHoF(country); // Hydrate photos root.querySelectorAll(".wp-photo-wrap img[data-wiki]").forEach(img => { hydratePhoto(img, img.getAttribute("data-wiki")); }); attachDebateHandlers(); } // Why is this player worth $X? β€” completely unique content, not on Wikipedia function priceTake(p) { if (p.price === 5) return `Maxes out half your budget. Only worth it if your other 5 picks average $1 each.`; if (p.price === 4) return `Premium tier β€” pairs well with one ${p.era === "GOAT" ? "$1 keeper" : "$1-$2 mid-tier"} and three budget picks.`; if (p.price === 3) return `Sweet spot. Lets you afford a $5 GOAT or two $3 anchors.`; if (p.price === 2) return `Bargain β€” gives you ${10 - p.price === 8 ? "$8" : "$" + (10 - p.price)} for the rest. Perfect for ${p.era === "Legend" || p.era === "GOAT" ? "stretching a legend-heavy lineup" : "filling a balanced squad"}.`; if (p.price === 1) return `Cheapest tier. Bench filler that lets you stack two GOATs.`; return ""; } // Auto-pick a $10 squad from this country's players function buildCountrySquad(country, players) { const byPos = { GK: [], DEF: [], MID: [], ATT: [] }; players.forEach(p => { const pos = PLAYER_POSITION[p.id]; if (pos) byPos[pos].push(p); }); // Greedy: pick best player per position cheapest first const lineup = []; let budget = 10; ["GK","ATT","MID","DEF"].forEach(pos => { const list = [...byPos[pos]].sort((a,b) => (a.era === "GOAT" ? -1 : a.era === "Legend" ? -0.5 : 0) - (b.era === "GOAT" ? -1 : b.era === "Legend" ? -0.5 : 0) || a.price - b.price); const pick = list.find(p => p.price <= budget); if (pick) { lineup.push(pick); budget -= pick.price; } }); if (!lineup.length) return null; const total = lineup.reduce((s,p) => s + p.price, 0); const picks = { GK: null, DEF: [], MID: [], ATT: [] }; lineup.forEach(p => { const pos = PLAYER_POSITION[p.id]; if (pos === "GK") picks.GK = p.id; else picks[pos].push(p.id); }); const formation = picks.DEF.length >= 1 && picks.MID.length >= 1 && picks.ATT.length >= 1 ? "4-3-3" : "4-3-3"; return { lineup, total, hash: encodeState({ f: formation, p: picks }) }; } async function loadCountryHoF(country) { const target = document.getElementById("hof-list"); if (!target) return; try { const rows = await fetch(window.SUPABASE_URL + "/rest/v1/combinations?category=eq.football&order=wins.desc,votes.desc&limit=100", { headers: { apikey: window.SUPABASE_ANON, Authorization: "Bearer " + window.SUPABASE_ANON } }).then(r => r.json()); // Filter: keep combos with 2+ players from this country const matched = (rows || []).filter(c => { const dj = c.data_json || {}; const ids = [dj.p?.GK, ...(dj.p?.DEF||[]), ...(dj.p?.MID||[]), ...(dj.p?.ATT||[])].filter(Boolean); const fromCountry = ids.filter(id => PLAYER_BY_ID[id] && PLAYER_BY_ID[id].country === country).length; return fromCountry >= 2; }).slice(0, 10); if (!matched.length) { target.innerHTML = `

No user squads with 2+ ${country} players yet. Be the first to build one β†’

`; return; } target.innerHTML = `
${matched.map((c, i) => { const dj = c.data_json || {}; const all = [PLAYER_BY_ID[dj.p?.GK], ...(dj.p?.DEF||[]).map(i2 => PLAYER_BY_ID[i2]), ...(dj.p?.MID||[]).map(i2 => PLAYER_BY_ID[i2]), ...(dj.p?.ATT||[]).map(i2 => PLAYER_BY_ID[i2])].filter(Boolean); const countryPlayers = all.filter(p => p.country === country); const badge = comboBadge(c.wins); return `

#${i+1} ${escapeHtml(c.title || "Anonymous")} ${badge ? `${badge.label}` : ""}

${dj.f || ""} Β· ${countryPlayers.length} ${country} player${countryPlayers.length > 1 ? "s" : ""}
$${c.total_cost || "?"}/10 Β· ${c.wins || 0}W-${c.losses || 0}L Β· ${c.votes || 0} votes
`; }).join("")}
`; } catch (e) { target.innerHTML = `

Hall of Fame loading failed.

`; } } function relatedCountries(current, n) { return Object.keys(PLAYERS_BY_COUNTRY) .filter(c => c !== current) .map(c => ({ name: c, flag: PLAYERS_BY_COUNTRY[c][0].flag, count: PLAYERS_BY_COUNTRY[c].length })) .sort((a, b) => b.count - a.count) .slice(0, n); } async function renderDebate(container, kind, key, prompt) { const args = await sbListArguments(kind, key); const pros = args.filter(x => x.side === "pro"); const cons = args.filter(x => x.side === "con"); const liveBadge = window.SUPABASE && window.SUPABASE.on ? `🟒 Live` : `πŸ’Ύ Local`; container.innerHTML = `

πŸ’¬ Debate: ${escapeHtml(prompt)} ${liveBadge}

Text 280자 λ˜λŠ” 🎀 μŒμ„± 30초. Be civil.

${debateColHtml("pro", "βœ… Pros", pros)} ${debateColHtml("con", "❌ Cons", cons)}
`; bindDebateCol(container, kind, key, prompt); } function debateColHtml(side, label, list) { return `

${label} (${list.length})

`; } function argHtml(a) { const audio = a.voice_url ? `` : ""; const text = (a.body && a.body.length) ? `${escapeHtml(a.body)}` : ""; return `
  • ${text}${text && audio ? "
    " : ""}${audio}
    ${escapeHtml(a.author_name || (a.author_fp || "").slice(0,6))}
  • `; } const voiceState = { pro: null, con: null }; // recorder + blob per side function bindDebateCol(container, kind, key, prompt) { container.querySelectorAll(".mic-btn").forEach(btn => { btn.addEventListener("click", async () => { const side = btn.dataset.side; const timerEl = container.querySelector(`.mic-timer[data-side="${side}"]`); const previewEl = container.querySelector(`.voice-preview[data-side="${side}"]`); const st = voiceState[side]; if (st && st.rec && st.rec.isRecording()) { const blob = await st.rec.stop(); st.blob = blob; btn.classList.remove("recording"); timerEl.textContent = ""; previewEl.innerHTML = ` `; previewEl.querySelector("[data-discard]").addEventListener("click", () => { voiceState[side] = null; previewEl.innerHTML = ""; }); return; } try { const rec = await makeVoiceRecorder((sec) => { timerEl.textContent = sec + "s"; }, 30); voiceState[side] = { rec, blob: null }; rec.start(); btn.classList.add("recording"); } catch (e) { toast(e.message || "Mic blocked"); } }); }); container.querySelectorAll(".debate-submit").forEach(btn => { btn.addEventListener("click", async () => { const side = btn.dataset.side; const ta = container.querySelector(`.debate-input[data-side="${side}"]`); const text = (ta.value || "").trim(); const v = voiceState[side]; if (!text && (!v || !v.blob)) { toast("Type something or record a voice note"); return; } btn.disabled = true; try { let voiceURL = null; if (v && v.blob) { const up = await sbUploadVoice(v.blob, "webm"); voiceURL = up.url; } await sbPostArgumentWithVoice(kind, key, side, text, voiceURL); ta.value = ""; voiceState[side] = null; await renderDebate(container, kind, key, prompt); } catch (e) { toast("Post failed: " + e.message); } finally { btn.disabled = false; } }); }); container.querySelectorAll(".arg-upvote").forEach(btn => { btn.addEventListener("click", async () => { const argId = btn.getAttribute("data-id"); const r = await sbUpvoteArgument(argId); if (r.already) toast("Already upvoted"); else { btn.textContent = "β–² " + (parseInt(btn.dataset.count, 10) + 1); btn.classList.add("upvoted"); } }); }); } // Stub for legacy code path (no longer used β€” debate now lives in its own container) function debateWidget(key, prompt) { const [kind, ...rest] = key.split(":"); return `
    Loading debate…
    `; } function attachDebateHandlers() { document.querySelectorAll(".debate-mount").forEach(el => { renderDebate(el, el.dataset.kind, el.dataset.key, el.dataset.prompt); }); } render();