Seeded draw — everyone gets at least 1 top-12 ★ team and 1 ranked 13–20. Hit the button when everyone’s ready!
Draw complete — 12 players, all 48 teams assigned.
1
1
0 pts
ENG
England
★ #4
PAN
Panama
#57
SUI
Switzerland
#19
SCO
Scotland
#36
2
2
0 pts
ESP
Spain
★ #2
HAI
Haiti
#83
CPV
Cape Verde
#90
QAT
Qatar
#65
3
3
0 pts
FRA
France
★ #1
SWE
Sweden
#33
IRN
Iran
#35
TUR
Turkey
#29
4
4
0 pts
BRA
Brazil
★ #6
🇿🇦
RSA
South Africa
#76
ALG
Algeria
#30
IRQ
Iraq
#68
5
5
0 pts
BEL
Belgium
★ #9
SEN
Senegal
#14
🇦🇺
AUS
Australia
#26
ECU
Ecuador
#28
6
6
0 pts
POR
Portugal
★ #5
PAR
Paraguay
#37
TUN
Tunisia
#58
CZE
Czechia
#38
7
7
0 pts
NED
Netherlands
★ #7
KSA
Saudi Arabia
#56
USA
USA
#16
URU
Uruguay
#17
8
8
0 pts
ARG
Argentina
★ #3
NZL
New Zealand
#63
CIV
Ivory Coast
#60
EGY
Egypt
#41
9
9
0 pts
GER
Germany
★ #10
CUW
Curacao
#104
JOR
Jordan
#70
NOR
Norway
#31
1
10
0 pts
COL
Colombia
#13
KOR
South Korea
#22
MEX
Mexico
#15
JPN
Japan
#18
1
11
0 pts
CRO
Croatia
#11
GHA
Ghana
#40
AUT
Austria
#25
BIH
Bosnia-Herz
#66
1
12
0 pts
MAR
Morocco
★ #8
UZB
Uzbekistan
#87
COD
DR Congo
#74
CAN
Canada
#59
12
Players
€60
Total pot
48
Teams still in
🏆 Leaderboard
🥇
1
1
ENG PAN SUI SCO
0pts
🥈
2
2
ESP HAI CPV QAT
0pts
🥉
3
3
FRA SWE IRN TUR
0pts
4
4
4
BRA🇿🇦 RSA ALG IRQ
0pts
5
5
5
BEL SEN🇦🇺 AUS ECU
0pts
6
6
6
POR PAR TUN CZE
0pts
7
7
7
NED KSA USA URU
0pts
8
8
8
ARG NZL CIV EGY
0pts
9
9
9
GER CUW JOR NOR
0pts
10
1
10
COL KOR MEX JPN
0pts
11
1
11
CRO GHA AUT BIH
0pts
12
1
12
MAR UZB COD CAN
0pts
💰 PRIZE BREAKDOWN
🥇
1
1st place
€35
🥈
2
2nd place
€15
🥉
3
3rd place
€10
🔒 Results editing is owner-only.
⛳ Update Results
If auto-sync is on, this updates automatically. You can also mark results manually. Points: Group=1 • R32=2 • R16=4 • QF=6 • SF=9 • Runner-up=12 • Winner=20
FRAFrance3
ESPSpain2
ARGArgentina8
ENGEngland1
PORPortugal6
BRABrazil4
NEDNetherlands7
MARMorocco12
BELBelgium5
GERGermany9
CROCroatia11
COLColombia10
SENSenegal5
MEXMexico10
USAUSA7
URUUruguay7
JPNJapan10
SUISwitzerland1
KORSouth Korea10
AUTAustria11
🇦🇺AUSAustralia5
ECUEcuador5
TURTurkey3
ALGAlgeria4
NORNorway9
SWESweden3
IRNIran3
SCOScotland1
PARParaguay6
CZECzechia6
GHAGhana11
EGYEgypt8
KSASaudi Arabia7
PANPanama1
TUNTunisia6
CANCanada12
CIVIvory Coast8
NZLNew Zealand8
QATQatar2
BIHBosnia-Herz11
IRQIraq4
JORJordan9
CODDR Congo12
🇿🇦RSASouth Africa4
HAIHaiti2
UZBUzbekistan12
CPVCape Verde2
CUWCuracao9
📅 Match Schedule
All 104 matches • UK / BST times • 11 Jun – 19 Jul 2026 • ■ = England or Scotland match
Thu 11 JunToday
MEXvsRSA🇿🇦
20:00 BSTMexico CityGroup A
KORvsCZE
03:00† BSTGuadalajaraGroup A
Fri 12 JunTomorrow
CANvsBIH
20:00 BSTTorontoGroup B
USAvsPAR
02:00† BSTLos AngelesGroup D
Sat 13 JunIn 2 days
QATvsSUI
20:00 BSTSan FranciscoGroup B
BRAvsMAR
23:00 BSTNew York/NJGroup C
HAIvsSCO
02:00† BSTBostonGroup C
🇦🇺AUSvsTUR
05:00† BSTVancouverGroup D
Sun 14 JunIn 3 days
GERvsCUW
19:00 BSTHoustonGroup E
NEDvsJPN
22:00 BSTDallasGroup F
CIVvsECU
01:00† BSTPhiladelphiaGroup E
SWEvsTUN
04:00† BSTMonterreyGroup F
Mon 15 JunIn 4 days
ESPvsCPV
18:00 BSTAtlantaGroup H
BELvsEGY
21:00 BSTVancouverGroup G
KSAvsURU
00:00† BSTMiamiGroup H
IRNvsNZL
05:00† BSTLos AngelesGroup G
Tue 16 JunIn 5 days
FRAvsSEN
21:00 BSTNew York/NJGroup I
IRQvsNOR
00:00† BSTBostonGroup I
ARGvsALG
03:00† BSTKansas CityGroup J
AUTvsJOR
08:00† BSTSan FranciscoGroup J
Wed 17 JunIn 6 days
PORvsCOD
19:00 BSTHoustonGroup K
ENGvsCRO
22:00 BSTDallasGroup L
GHAvsPAN
01:00† BSTTorontoGroup L
UZBvsCOL
04:00† BSTMexico CityGroup K
Thu 18 JunIn 7 days
CZEvsRSA🇿🇦
18:00 BSTAtlantaGroup A
SUIvsBIH
21:00 BSTLos AngelesGroup B
CANvsQAT
02:00† BSTVancouverGroup B
MEXvsKOR
04:00† BSTGuadalajaraGroup A
Fri 19 Jun
🇿🇦RSAvsMAR
21:00 BSTBostonGroup C
USAvsAUS🇦🇺
22:00 BSTSeattleGroup D
BRAvsHAI
02:00† BSTPhiladelphiaGroup C
TURvsPAR
04:00† BSTSan FranciscoGroup D
Sat 20 Jun
NEDvsSWE
19:00 BSTHoustonGroup F
GERvsCIV
22:00 BSTTorontoGroup E
ECUvsCUW
02:00† BSTKansas CityGroup E
TUNvsJPN
06:00† BSTMonterreyGroup F
Sun 21 Jun
ESPvsKSA
18:00 BSTAtlantaGroup H
BELvsIRN
21:00 BSTLos AngelesGroup G
URUvsCPV
00:00† BSTMiamiGroup H
NZLvsEGY
05:00† BSTVancouverGroup G
Mon 22 Jun
ARGvsAUT
19:00 BSTDallasGroup J
FRAvsIRQ
23:00 BSTPhiladelphiaGroup I
NORvsSEN
02:00† BSTNew York/NJGroup I
JORvsALG
07:00† BSTSan FranciscoGroup J
Tue 23 Jun
PORvsUZB
19:00 BSTHoustonGroup K
ENGvsGHA
22:00 BSTSeattleGroup L
CROvsPAN
01:00† BSTTorontoGroup L
COLvsCOD
05:00† BSTKansas CityGroup K
Wed 24 Jun
MEXvsCZE
20:00 BSTMexico CityGroup A
KORvsRSA🇿🇦
20:00 BSTGuadalajaraGroup A
BIHvsQAT
23:00 BSTTorontoGroup B
SUIvsCAN
23:00 BSTVancouverGroup B
Thu 25 Jun
SCOvsBRA
20:00 BSTPhiladelphiaGroup C
MARvsHAI
20:00 BSTNew York/NJGroup C
PARvsAUS🇦🇺
23:00 BSTLos AngelesGroup D
TURvsUSA
23:00 BSTSeattleGroup D
Fri 26 Jun
CUWvsGER
20:00 BSTKansas CityGroup E
ECUvsCIV
20:00 BSTPhiladelphiaGroup E
JPNvsNED
23:00 BSTDallasGroup F
TUNvsSWE
23:00 BSTMonterreyGroup F
Sat 27 Jun
EGYvsBEL
20:00 BSTVancouverGroup G
NZLvsIRN
20:00 BSTLos AngelesGroup G
CPVvsESP
23:00 BSTMiamiGroup H
URUvsKSA
23:00 BSTAtlantaGroup H
Sun 28 Jun
SENvsFRA
20:00 BSTNew York/NJGroup I
NORvsIRQ
20:00 BSTBostonGroup I
ALGvsARG
23:00 BSTKansas CityGroup J
JORvsAUT
23:00 BSTSan FranciscoGroup J
Mon 29 Jun
CODvsPOR
20:00 BSTHoustonGroup K
COLvsUZB
20:00 BSTMexico CityGroup K
PANvsENG
23:00 BSTTorontoGroup L
GHAvsCRO
23:00 BSTDallasGroup L
Fri 3 Jul – Mon 7 Jul
Round of 32 (8 matches)
KNOCKOUT
Sat 5 Jul – Tue 8 Jul
Round of 16 (8 matches)
KNOCKOUT
Fri 10 Jul – Sun 12 Jul
Quarter-finals (4 matches)
KNOCKOUT
Wed 15 – Thu 16 Jul
Semi-finals
KNOCKOUT
Sun 18 Jul
3rd place play-off — Miami
23:00 BSTKNOCKOUT
Sun 19 Jul
🏆 FINAL — New York / New Jersey
20:00 BSTKNOCKOUT
💬 Share to WhatsApp
⚽ FIFA WORLD CUP 2026 SWEEPSTAKE ⚽
━⚽ FIFA WORLD CUP 2026 SWEEPSTAKE ⚽
━⚽ FIFA WORLD CUP 2026 SWEEPSTAKE ⚽
━⚽ FIFA WORLD CUP 2026 SWEEPSTAKE ⚽
━⚽ FIFA WORLD CUP 2026 SWEEPSTAKE ⚽
━⚽ FIFA WORLD CUP 2026 SWEEPSTAKE ⚽
━⚽ FIFA WORLD CUP 2026 SWEEPSTAKE ⚽
━⚽ FIFA WORLD CUP 2026 SWEEPSTAKE ⚽
━⚽ FIFA WORLD CUP 2026 SWEEPSTAKE ⚽
━⚽ FIFA WORLD CUP 2026 SWEEPSTAKE ⚽
━⚽ FIFA WORLD CUP 2026 SWEEPSTAKE ⚽
━⚽ FIFA WORLD CUP 2026 SWEEPSTAKE ⚽
━⚽ FIFA WORLD CUP 2026 SWEEPSTAKE ⚽
━⚽ FIFA WORLD CUP 2026 SWEEPSTAKE ⚽
━⚽ FIFA WORLD CUP 2026 SWEEPSTAKE ⚽
━⚽ FIFA WORLD CUP 2026 SWEEPSTAKE ⚽
━
🏆 LEADERBOARD
🥇 1 — 0pts
ENG, 🇵🇦 PAN, 🇨🇭 SUI, SCO
🥈 2 — 0pts
🇪🇸 ESP, 🇭🇹 HAI, 🇨🇻 CPV, 🇶🇦 QAT
🥉 3 — 0pts
🇫🇷 FRA, 🇸🇪 SWE, 🇮🇷 IRN, 🇹🇷 TUR
4. 4 — 0pts
🇧🇷 BRA, 🇿🇦 RSA, 🇩🇿 ALG, 🇮🇶 IRQ
5. 5 — 0pts
🇧🇪 BEL, 🇸🇳 SEN, 🇦🇺 AUS, 🇪🇨 ECU
6. 6 — 0pts
🇵🇹 POR, 🇵🇾 PAR, 🇹🇳 TUN, 🇨🇿 CZE
7. 7 — 0pts
🇳🇱 NED, 🇸🇦 KSA, 🇺🇸 USA, 🇺🇾 URU
8. 8 — 0pts
🇦🇷 ARG, 🇳🇿 NZL, 🇨🇮 CIV, 🇪🇬 EGY
9. 9 — 0pts
🇩🇪 GER, 🇨🇼 CUW, 🇯🇴 JOR, 🇳🇴 NOR
10. 10 — 0pts
🇨🇴 COL, 🇰🇷 KOR, 🇲🇽 MEX, 🇯🇵 JPN
11. 11 — 0pts
🇭🇷 CRO, 🇬🇭 GHA, 🇦🇹 AUT, 🇧🇦 BIH
12. 12 — 0pts
🇲🇦 MAR, 🇺🇿 UZB, 🇨🇩 COD, 🇨🇦 CAN
💰 Prize pot: €60
⏰ Last updated: 11/06/2026, 12:07:47
━
━
━
━
━
━
━
━
━
━
━
━
━
━
━
━
Good luck everyone! 🎉
💾 Download
Save the app as a single HTML file. Share it with family — they open it in any browser.
🗣 Banter Board Live
Family chat — trash talk welcome. Messages are shared live across all devices.
1
1211 Jun 12:02
hello
1
1210 Jun 23:35
hello
1
1210 Jun 18:27
13
1
1210 Jun 16:12
1-2-3
N
Nina10 Jun 14:48
testing
1
1210 Jun 14:46
hello
N
Nina10 Jun 14:44
test
TU
Test User10 Jun 12:54
Hello from console
TU
Test User10 Jun 12:48
Hello from console
TU
Test User10 Jun 12:48
Hello from console
1
110 Jun 12:36
test
1
110 Jun 12:36
test
1
110 Jun 12:35
test
1
110 Jun 12:32
hi
1
110 Jun 12:31
hi
1
110 Jun 12:31
hi
N
Nina10 Jun 12:09
test
N
Nina10 Jun 10:36
test
N
Nina10 Jun 10:16
test
🏆 Route to the Final
Predict the knockout bracket — in R32 use the dropdowns to pick which team from each group advances, then tap a team to pick the match winner. They advance automatically all the way to the final.
Uses the 12 official WC 2026 groups
🏆 Predicted Champion
Pick team for player
🔓 Owner Access
Enter your owner password to unlock admin features.
⚽ WC 2026 Sweepstake
Add to your home screen for quick access
Install on iPhone
1
Tap the Share button at the bottom of Safari (box with up-arrow)
2
Scroll down and tap Add to Home Screen
3
Tap Add in the top right - done!
',inject+'');
var blob=new Blob([html],{type:'text/html'});
var a=document.createElement('a');a.href=URL.createObjectURL(blob);a.download='wc2026-sweepstake.html';a.click();
}
/* -- BOOT -- */
/* -- TODAY'S MATCHES BAR -- */
/* -- TIME HELPERS -- */
function bstToET(bstStr){
// bstStr like "20:00" or "02:00" (dagger already stripped by caller)
var parts=bstStr.split(':');
if(parts.length<2)return '';
var h=parseInt(parts[0],10);
var m=parts[1];
// NJ/NY is EDT = UTC-4, BST = UTC+1, so ET = BST - 5
var eth=((h-5)+24)%24;
var ampm=eth<12?'am':'pm';
var h12=eth%12||12;
return h12+':'+m+ampm+' ET';
}
function renderTodayBar(){
var bar=document.getElementById('today-bar');
var label=document.getElementById('today-bar-label');
var feed=document.getElementById('today-matches');
if(!bar||!feed)return;
var now=new Date();
var days=['Sun','Mon','Tue','Wed','Thu','Fri','Sat'];
var months=['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
var todayStr=days[now.getDay()]+' '+now.getDate()+' '+months[now.getMonth()];
var myTeamCodes=[];
if(state.drawn){
Object.values(state.assignments).forEach(function(teams){
teams.forEach(function(tn){
var t=TEAMS.find(function(x){return x.name===tn});
if(t)myTeamCodes.push(t.code);
});
});
}
// Find today's matches involving drawn teams
var todayMatches=SCHEDULE.filter(function(m){
return m.d===todayStr&&m.t.length===2&&myTeamCodes.length&&m.t.some(function(c){return myTeamCodes.indexOf(c)>=0});
});
// Find next 2 upcoming matches (any) if no matches today for my teams
var nextMatches=[];
if(!todayMatches.length){
// walk schedule to find first 2 future group matches
var found=0;
for(var i=0;i=0;
var mine2=myTeamCodes.indexOf(m.t[1])>=0;
var f1=flagCache[m.t[0]]?'':(t1.emoji||m.t[0]);
var f2=flagCache[m.t[1]]?'':(t2.emoji||m.t[1]);
var bstClean=m.uk.replace('\u2020','');
var etStr=bstToET(bstClean);
return '
';
}).join('');
} else {
// Show "no matches today, next up"
if(label)label.innerHTML='📅 NEXT UPCOMING MATCHES';
bar.style.background='linear-gradient(135deg,rgba(42,57,141,.15),rgba(2,15,42,.3))';
bar.style.borderBottomColor='rgba(42,57,141,.4)';
feed.innerHTML=nextMatches.map(function(m){
var t1=TEAMS.find(function(x){return x.code===m.t[0]})||{emoji:'',code:m.t[0]};
var t2=TEAMS.find(function(x){return x.code===m.t[1]})||{emoji:'',code:m.t[1]};
var f1=flagCache[m.t[0]]?'':(t1.emoji||m.t[0]);
var f2=flagCache[m.t[1]]?'':(t2.emoji||m.t[1]);
var bstClean=m.uk.replace('\u2020','');
var etStr=bstToET(bstClean);
return '
';
}).join('');
}
}
/* -- TEAM MODAL -- */
function openTeamModal(teamName){
var t=TEAMS.find(function(x){return x.name===teamName});
if(!t)return;
var modal=document.getElementById('team-modal');
var inner=document.getElementById('team-modal-content');
if(!modal||!inner)return;
// Find next matches for this team
var upcoming=SCHEDULE.filter(function(m){
return m.t.indexOf(t.code)>=0&&m.uk;
}).slice(0,3);
var stage=state.teamStages[teamName]||'tbd';
var stageInfo=STAGES[stage]||{label:'Not played yet',pts:0};
// Find who owns this team
var owner=Object.entries(state.assignments).find(function(e){return e[1].indexOf(teamName)>=0});
var flagHtml=flagCache[t.code]
?''
:(t.emoji?''+t.emoji+'':'');
var matchRows=upcoming.length
?upcoming.map(function(m){
var opp=m.t[0]===t.code?m.t[1]:m.t[0];
var oppT=TEAMS.find(function(x){return x.code===opp})||{emoji:'',code:opp};
var oppFlag=flagCache[opp]?'':(oppT.emoji||opp);
return '
FIFA Rank #'+t.rank+(t.rank<=10?' • ★ Top 10':t.rank<=20?' • Top 20':'')+'
'
+(owner?'
Drawn by '+owner[0]+'
':'')
+'
'
+'
'
+'
'
+'Current stage'
+''+stageInfo.label+''
+'
'
+'
UPCOMING FIXTURES
'
+matchRows;
modal.style.display='flex';
}
function closeTeamModal(){
var modal=document.getElementById('team-modal');
if(modal)modal.style.display='none';
}
/* -- LIVE MATCH INDICATOR -- */
function getLiveTeams(){
var now=new Date();
var days=['Sun','Mon','Tue','Wed','Thu','Fri','Sat'];
var months=['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
var todayStr=days[now.getDay()]+' '+now.getDate()+' '+months[now.getMonth()];
var liveSet={};
SCHEDULE.forEach(function(m){
if(m.d!==todayStr||!m.uk||!m.t.length)return;
var timeStr=m.uk.replace('\u2020','');
var parts=timeStr.split(':');
if(parts.length<2)return;
var h=parseInt(parts[0],10),mi=parseInt(parts[1],10);
var kickoff=new Date(now);
kickoff.setUTCHours(h-1,mi,0,0);
var diff=(now-kickoff)/60000;
if(diff>=0&&diff<=105)m.t.forEach(function(c){liveSet[c]=true;});
});
return liveSet;
}
/* -- BANTER BOARD (Firebase realtime) -- */
var replyToId=null;
var _fbApp=null;
var _fbDb=null;
var _fbRef=null;
var _fbMessages=[];
function initFirebase(){
if(_fbApp)return;
// Dynamically load Firebase SDKs then connect
function loadScript(src,cb){
var s=document.createElement('script');
s.src=src;
s.onload=cb;
s.onerror=function(){
document.getElementById('banter-status').textContent='Offline (CDN blocked)';
document.getElementById('banter-status').style.color='var(--gray)';
};
document.head.appendChild(s);
}
var appUrl='https://www.gstatic.com/firebasejs/9.23.0/firebase-app-compat.js';
var dbUrl='https://www.gstatic.com/firebasejs/9.23.0/firebase-database-compat.js';
loadScript(appUrl,function(){
loadScript(dbUrl,function(){
try{
var firebaseConfig={
apiKey:'AIzaSyBU6vWqHUbHSnIPdjcGpC3BlwzlwhL24jg',
authDomain:'wc2026-sweepstake-dbd56.firebaseapp.com',
databaseURL:'https://wc2026-sweepstake-dbd56-default-rtdb.europe-west1.firebasedatabase.app',
projectId:'wc2026-sweepstake-dbd56',
storageBucket:'wc2026-sweepstake-dbd56.firebasestorage.app',
messagingSenderId:'11571340466',
appId:'1:11571340466:web:ebfbf6f935ce22ea98d5ab'
};
_fbApp=firebase.initializeApp(firebaseConfig);
_fbDb=firebase.database();
_fbRef=_fbDb.ref('banter');
_fbRef.on('value',function(snapshot){
var data=snapshot.val();
_fbMessages=[];
if(data){
Object.keys(data).forEach(function(k){_fbMessages.push(data[k]);});
_fbMessages.sort(function(a,b){return a.id-b.id;});
}
renderBanter();
updateBanterDot();
});
document.getElementById('banter-status').textContent='Live';
document.getElementById('banter-status').style.color='var(--wc-green)';
}catch(e){
console.warn('Firebase init failed:',e);
document.getElementById('banter-status').textContent='Offline';
document.getElementById('banter-status').style.color='var(--gray)';
}
});
});
}
function updateBanterDot(){
var lastSeen=parseInt(localStorage.getItem('banter-last-seen')||'0',10);
var hasNew=_fbMessages.some(function(m){return m.id>lastSeen;});
var dot=document.getElementById('banter-dot');
if(dot)dot.style.display=hasNew?'inline-block':'none';
}
function setReply(id){
var msg=_fbMessages.find(function(m){return m.id===id;});
if(!msg)return;
replyToId=id;
var preview=document.getElementById('banter-reply-preview');
var previewText=document.getElementById('banter-reply-text');
if(preview&&previewText){
previewText.innerHTML=''+msg.name+': '+msg.text.slice(0,60)+(msg.text.length>60?'...':'');
preview.style.display='flex';
}
var ta=document.getElementById('banter-msg');
if(ta)ta.focus();
}
function clearReply(){
replyToId=null;
var preview=document.getElementById('banter-reply-preview');
if(preview)preview.style.display='none';
}
function renderBanter(){
var sel=document.getElementById('banter-name');
if(sel){
var cur=sel.value;
sel.innerHTML=state.players.length
?state.players.map(function(p){return '';}).join('')
:'';
}
var lastSeen=parseInt(localStorage.getItem('banter-last-seen')||'0',10);
var msgs=_fbMessages;
var feed=document.getElementById('banter-feed');
if(!feed)return;
if(!msgs.length){
feed.innerHTML='
No messages yet. Start the banter! 🗣
';
return;
}
feed.innerHTML=msgs.slice().reverse().map(function(m,i){
var pi=state.players.indexOf(m.name);
var col=pi>=0?pc(pi):{bg:'#444'};
var initials=m.name.split(' ').map(function(w){return w[0];}).join('').toUpperCase().slice(0,2);
var ts=new Date(m.ts).toLocaleTimeString('en-GB',{hour:'2-digit',minute:'2-digit'});
var dateStr=new Date(m.ts).toLocaleDateString('en-GB',{day:'numeric',month:'short'});
var isNew=m.id>lastSeen;
var replyHtml='';
if(m.replyTo){
var orig=_fbMessages.find(function(x){return x.id===m.replyTo;});
if(orig){
var origPi=state.players.indexOf(orig.name);
var origCol=origPi>=0?pc(origPi):{bg:'#444'};
replyHtml='
';
}).join('');
}
function scrollToBanter(id){
var el=document.getElementById('banter-msg-'+id);
if(el){el.scrollIntoView({behavior:'smooth',block:'center'});el.style.background='rgba(240,180,41,.1)';setTimeout(function(){el.style.background='';},1200);}
}
function markBanterRead(){
if(!_fbMessages.length)return;
var maxId=Math.max.apply(null,_fbMessages.map(function(m){return m.id;}));
localStorage.setItem('banter-last-seen',maxId);
var dot=document.getElementById('banter-dot');
if(dot)dot.style.display='none';
}
function postBanter(){
var name=document.getElementById('banter-name').value;
var msg=document.getElementById('banter-msg').value.trim();
if(!name||!msg)return;
if(!_fbRef){alert('Not connected to Firebase yet. Please wait a moment and try again.');return;}
var entry={id:Date.now(),name:name,text:msg,ts:new Date().toISOString()};
if(replyToId)entry.replyTo=replyToId;
_fbRef.push(entry);
document.getElementById('banter-msg').value='';
clearReply();
markBanterRead();
}
function deleteBanter(id){
if(!_fbRef)return;
// Find the Firebase key for this message
_fbRef.once('value',function(snapshot){
snapshot.forEach(function(child){
if(child.val().id===id){
child.ref.remove();
}
});
});
}
/* -- BRACKET PREDICTOR -- */
/*
Bracket structure: 32 teams -> R32 (16 matches) -> R16 (8) -> QF (4) -> SF (2) -> Final (1)
state.bracket = {
rounds: [ [matchObj,...], ... ] // 5 rounds: r32, r16, qf, sf, final
}
matchObj = { t: [teamNameOrNull, teamNameOrNull], winner: teamNameOrNull }
*/
var BRACKET_ROUND_NAMES=['Round of 32','Round of 16','Quarter-finals','Semi-finals','Final'];
function initBracket(){
if(!state.drawn)return;
// WC 2026 official R32 matchup slots by group position
// Format: [slot description, group codes for team options]
// Each match: {label, groups} where groups are the pool of teams to pick from
// Based on official FIFA WC2026 bracket: 12 group winners + 12 runners-up + 8 best 3rds
// We simplify: show all teams from the relevant groups as options, user taps to pick
var R32_SLOTS=[
// Match 1-16 based on official WC2026 bracket structure
// Group winners vs best 3rds / runners-up
{a:['A'],b:['B']}, // 1A v 2B
{a:['C'],b:['D']}, // 1C v 2D
{a:['E'],b:['F']}, // 1E v 2F
{a:['G'],b:['H']}, // 1G v 2H
{a:['I'],b:['J']}, // 1I v 2J
{a:['K'],b:['L']}, // 1K v 2L
{a:['B'],b:['A']}, // 1B v 2A
{a:['D'],b:['C']}, // 1D v 2C
{a:['F'],b:['E']}, // 1F v 2E
{a:['H'],b:['G']}, // 1H v 2G
{a:['J'],b:['I']}, // 1J v 2I
{a:['L'],b:['K']}, // 1L v 2K
// Last 4 matches involve 3rd-place qualifiers - use cross-group pools
{a:['A','B'],b:['C','D']},
{a:['E','F'],b:['G','H']},
{a:['I','J'],b:['K','L']},
{a:['A','C','E'],b:['B','D','F']}
];
// Build a lookup: group letter -> drawn teams in that group
var groupMap={};
var SCHEDULE_GROUPS={
A:['MEX','RSA','KOR','CZE'],B:['CAN','BIH','QAT','SUI'],
C:['BRA','MAR','HAI','SCO'],D:['USA','PAR','AUS','TUR'],
E:['GER','CUW','CIV','ECU'],F:['NED','JPN','SWE','TUN'],
G:['BEL','EGY','IRN','NZL'],H:['ESP','CPV','KSA','URU'],
I:['FRA','SEN','IRQ','NOR'],J:['ARG','ALG','AUT','JOR'],
K:['POR','COD','UZB','COL'],L:['ENG','CRO','GHA','PAN']
};
var drawnCodes={};
Object.values(state.assignments).forEach(function(ts){
ts.forEach(function(tn){
var t=TEAMS.find(function(x){return x.name===tn;});
if(t)drawnCodes[t.code]=tn;
});
});
Object.keys(SCHEDULE_GROUPS).forEach(function(g){
groupMap[g]=SCHEDULE_GROUPS[g].filter(function(c){return drawnCodes[c];}).map(function(c){return drawnCodes[c];});
});
var matches=R32_SLOTS.map(function(slot){
// pool for side a: all drawn teams from those groups
var poolA=[],poolB=[];
slot.a.forEach(function(g){poolA=poolA.concat(groupMap[g]||[]);});
slot.b.forEach(function(g){poolB=poolB.concat(groupMap[g]||[]);});
// pick first from each pool as default slot (user can change by tapping)
return {t:[poolA[0]||null,poolB[0]||null],poolA:poolA,poolB:poolB,winner:null};
});
var rounds=[matches];
for(var r=0;r<4;r++){
var prev=rounds[r];
var next=[];
for(var j=0;j=4)return;
var m=state.bracket.rounds[round][match];
if(!m)return;
// if the old team (being replaced) was the winner, clear their downstream too
var oldTeam=m.t[slot];
if(oldTeam&&m.winner===oldTeam){
m.winner=null;
var nextMatch=Math.floor(match/2);
var nextSlot=match%2;
clearDownstream(round+1,nextMatch,oldTeam,nextSlot);
if(state.bracket.rounds[round+1][nextMatch]){
state.bracket.rounds[round+1][nextMatch].t[nextSlot]=null;
}
}
}
function resetBracket(){
if(!confirm('Reset bracket prediction?'))return;
state.bracket=null;
save();
renderBracket();
}
function renderBracket(){
var el=document.getElementById('bracket-content');
var champCard=document.getElementById('bracket-champion-card');
if(!el)return;
if(!state.drawn){el.innerHTML='
Do the draw first to unlock the bracket predictor.
';
if(champCard)champCard.style.display='none';
return;
}
var rounds=state.bracket.rounds;
var html='
';
for(var r=0;r';
html+='
'+BRACKET_ROUND_NAMES[r]+'
';
html+='
';
for(var m=0;m
';
}
html+='
';
el.innerHTML=html;
// champion display
var finalMatch=rounds[4][0];
var champ=finalMatch&&finalMatch.winner;
if(champ&&champCard){
champCard.style.display='block';
var ct=TEAMS.find(function(x){return x.name===champ;})||{code:'???',emoji:'',rank:99};
var owner=Object.entries(state.assignments).find(function(e){return e[1].indexOf(champ)>=0;});
var flagHtml=flagCache[ct.code]?'':(ct.emoji?''+ct.emoji+'':'');
document.getElementById('bracket-champion-display').innerHTML=
'
'
+'
\uD83C\uDFC6
'
+'
'+flagHtml+'
'
+'
'+champ+'
'
+(owner?'
Drawn by '+owner[0]+'
':'')
+'
';
} else if(champCard){
champCard.style.display='none';
}
}
function renderBracketMatch(round,matchIdx,m){
var t0=m.t[0];
var t1=m.t[1];
var w=m.winner;
function teamHtml(tn,slot){
var pool=slot===0?(m.poolA||[]):(m.poolB||[]);
if(!tn&&!pool.length){
return '
TBD
';
}
// If round 0 and pool has multiple options, show a mini-select
if(round===0&&pool.length>1){
var selOpts=pool.map(function(ptn){
var pt=TEAMS.find(function(x){return x.name===ptn;})||{code:'???'};
return '';
}).join('');
var curT=tn?TEAMS.find(function(x){return x.name===tn;})||{code:'???',emoji:'',rank:99}:{code:'?',emoji:'',rank:99};
var flagHtml=tn&&flagCache[curT.code]?'': (tn&&curT.emoji?''+curT.emoji+'':'');
return '
';
}
var t=TEAMS.find(function(x){return x.name===tn;})||{code:'???',emoji:'',rank:99};
var isW=w===tn;
var isL=w&&w!==tn;
var flagHtml2=flagCache[t.code]?'':(t.emoji?''+t.emoji+'':'');
var owner=Object.entries(state.assignments).find(function(e){return e[1].indexOf(tn)>=0;});
var ownerInitials=owner?owner[0].split(' ').map(function(w2){return w2[0];}).join('').toUpperCase().slice(0,2):'';
var pi=owner?state.players.indexOf(owner[0]):-1;
var ownerCol=pi>=0?pc(pi).bg:'#666';
return '
';
}
function swapBracketSlot(round,match,slot,teamName){
if(!state.bracket)return;
var m=state.bracket.rounds[round][match];
if(!m)return;
m.t[slot]=teamName;
if(m.winner&&m.winner!==m.t[0]&&m.winner!==m.t[1])m.winner=null;
save();renderBracket();
}
function shareBracket(){
if(!state.bracket)return;
var rounds=state.bracket.rounds;
var rnames=['R32','R16','QF','SF','Final'];
var txt='\u26BD WC 2026 BRACKET PREDICTION \u26BD\n';
for(var r=0;rfetchResults());
} else {
setSyncStatus('','No API key set \u2014 add one in Setup for live results');
}