WM 2026 – Interaktive Spielorte & Gruppen-Spielplan https://unpkg.com/leaflet@1.9.4/dist/leaflet.js :root{ –bg: #0b1020; –panel: #121a33; –panel-2:#0f1730; –text: #e7ecf6; –muted:#97a3c7; –accent:#6aa7ff; –accent-2:#9bffcf; –danger:#ff7b7b; –ok:#9cff9c; } *{box-sizing:border-box;} body{ margin:0; padding:0; background: var(–bg); color: var(–text); font: 14px/1.4 system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial; display:grid; grid-template-columns: 360px 1fr; height:100vh; overflow:hidden; } #sidebar{ background: linear-gradient(180deg,var(–panel),var(–panel-2)); padding:16px; border-right:1px solid rgba(255,255,255,0.06); display:flex; flex-direction:column; gap:12px; overflow:auto; } h1{ font-size:18px; margin:0 0 4px 0; font-weight:700; letter-spacing:0.2px; } .sub{ color:var(–muted); font-size:12px; } .group-grid{ display:grid; grid-template-columns: repeat(4,1fr); gap:8px; } .btn{ background:#0e1530; color:var(–text); border:1px solid rgba(255,255,255,0.08); padding:8px 6px; border-radius:10px; cursor:pointer; font-weight:600; text-align:center; user-select:none; transition:120ms ease; } .btn:hover{ transform: translateY(-1px); border-color: rgba(255,255,255,0.18); } .btn.active{ background: radial-gradient(120% 140% at 0% 0%, #2b61ff 0%, #1431a6 55%, #0e1530 100%); border-color: rgba(106,167,255,0.9); box-shadow: 0 6px 18px rgba(34,88,255,0.35); } .btn.small{ padding:6px 8px; font-size:12px; } #map{ width:100%; height:100%; } .card{ background: rgba(255,255,255,0.03); border:1px solid rgba(255,255,255,0.06); border-radius:12px; padding:12px; } .legend{ display:flex; gap:8px; flex-wrap:wrap; font-size:12px; color:var(–muted); } .dot{ width:10px; height:10px; border-radius:999px; display:inline-block; margin-right:6px; vertical-align:middle; } .dot.highlight{background:var(–accent-2);} .dot.dim{background:#4a577c;} .dot.host{background:var(–accent);} #schedule{ display:flex; flex-direction:column; gap:8px; max-height:45vh; overflow:auto; padding-right:2px; } .match{ display:grid; grid-template-columns: 52px 1fr; gap:8px; padding:8px; border-radius:10px; background: rgba(0,0,0,0.25); border:1px solid rgba(255,255,255,0.05); } .match .no{ font-weight:800; color:#cfe0ff; } .match .meta{ color:var(–muted); font-size:12px; margin-top:2px; } .pill{ font-size:11px; font-weight:700; letter-spacing:0.2px; padding:2px 6px; border-radius:999px; background: rgba(106,167,255,0.12); border:1px solid rgba(106,167,255,0.35); color:#cfe0ff; margin-left:6px; } .footer-note{ color:var(–muted); font-size:11px; margin-top:6px; }
/** ———- Daten ———- **/ // 16 Host Cities / Stadien const venues = [ { id:“mexico_city“, name:“Mexico City Stadium (Estadio Azteca)“, city:“Mexico City“, country:“MEX“, lat:19.3029, lng:-99.1505 }, { id:“guadalajara“, name:“Estadio Guadalajara (Akron)“, city:“Guadalajara“, country:“MEX“, lat:20.6810, lng:-103.4613 }, { id:“monterrey“, name:“Estadio Monterrey (BBVA)“, city:“Monterrey“, country:“MEX“, lat:25.6690, lng:-100.2440 }, { id:“toronto“, name:“Toronto Stadium (BMO Field)“, city:“Toronto“, country:“CAN“, lat:43.6332, lng:-79.4186 }, { id:“vancouver“, name:“BC Place Vancouver“, city:“Vancouver“, country:“CAN“, lat:49.2767, lng:-123.1119 }, { id:“los_angeles“, name:“Los Angeles Stadium (SoFi)“, city:“Los Angeles“, country:“USA“, lat:33.9535, lng:-118.3392 }, { id:“seattle“, name:“Seattle Stadium (Lumen Field)“, city:“Seattle“, country:“USA“, lat:47.5952, lng:-122.3316 }, { id:“san_francisco“, name:“San Francisco Bay Area Stadium (Levi’s)“, city:“Santa Clara“, country:“USA“, lat:37.4030, lng:-121.9700 }, { id:“dallas“, name:“Dallas Stadium (AT&T)“, city:“Dallas/Arlington“, country:“USA“, lat:32.7473, lng:-97.0945 }, { id:“houston“, name:“Houston Stadium (NRG)“, city:“Houston“, country:“USA“, lat:29.6847, lng:-95.4107 }, { id:“kansas_city“, name:“Kansas City Stadium (Arrowhead)“, city:“Kansas City“, country:“USA“, lat:39.0489, lng:-94.4839 }, { id:“atlanta“, name:“Atlanta Stadium (Mercedes-Benz)“, city:“Atlanta“, country:“USA“, lat:33.7553, lng:-84.4006 }, { id:“miami“, name:“Miami Stadium (Hard Rock)“, city:“Miami Gardens“, country:“USA“, lat:25.9580, lng:-80.2389 }, { id:“boston“, name:“Boston Stadium (Gillette)“, city:“Boston/Foxborough“, country:“USA“, lat:42.0909, lng:-71.2643 }, { id:“philadelphia“, name:“Philadelphia Stadium (Lincoln Financial)“, city:“Philadelphia“, country:“USA“, lat:39.9008, lng:-75.1675 }, { id:“new_york_nj“, name:“New York New Jersey Stadium (MetLife)“, city:“New York/New Jersey“, country:“USA“, lat:40.8135, lng:-74.0745 }, ]; // Gruppenphasen-Matches 1–72 (ohne Teams) // Quelle: offizieller vorläufiger Spielplan mit Gruppenzuordnung. (siehe Fox Sports Auflistung) const matches = [ // Matchday 1 { no:1, group:“A“, date:“2026-06-11″, venue:“mexico_city“ }, { no:2, group:“A“, date:“2026-06-11″, venue:“guadalajara“ }, { no:3, group:“B“, date:“2026-06-12″, venue:“toronto“ }, { no:4, group:“D“, date:“2026-06-12″, venue:“los_angeles“ }, { no:5, group:“C“, date:“2026-06-13″, venue:“boston“ }, { no:6, group:“D“, date:“2026-06-13″, venue:“vancouver“ }, { no:7, group:“C“, date:“2026-06-13″, venue:“new_york_nj“ }, { no:8, group:“B“, date:“2026-06-13″, venue:“san_francisco“ }, { no:9, group:“E“, date:“2026-06-14″, venue:“philadelphia“ }, { no:10, group:“E“, date:“2026-06-14″, venue:“houston“ }, { no:11, group:“F“, date:“2026-06-14″, venue:“dallas“ }, { no:12, group:“F“, date:“2026-06-14″, venue:“monterrey“ }, { no:13, group:“H“, date:“2026-06-15″, venue:“miami“ }, { no:14, group:“H“, date:“2026-06-15″, venue:“atlanta“ }, { no:15, group:“G“, date:“2026-06-15″, venue:“los_angeles“ }, { no:16, group:“G“, date:“2026-06-15″, venue:“seattle“ }, { no:17, group:“I“, date:“2026-06-16″, venue:“new_york_nj“ }, { no:18, group:“I“, date:“2026-06-16″, venue:“boston“ }, { no:19, group:“J“, date:“2026-06-16″, venue:“kansas_city“ }, { no:20, group:“J“, date:“2026-06-16″, venue:“san_francisco“ }, { no:21, group:“L“, date:“2026-06-17″, venue:“toronto“ }, { no:22, group:“L“, date:“2026-06-17″, venue:“dallas“ }, { no:23, group:“K“, date:“2026-06-17″, venue:“houston“ }, { no:24, group:“K“, date:“2026-06-17″, venue:“mexico_city“ }, // Matchday 2 { no:25, group:“A“, date:“2026-06-18″, venue:“atlanta“ }, { no:26, group:“B“, date:“2026-06-18″, venue:“los_angeles“ }, { no:27, group:“B“, date:“2026-06-18″, venue:“vancouver“ }, { no:28, group:“A“, date:“2026-06-18″, venue:“guadalajara“ }, { no:29, group:“C“, date:“2026-06-19″, venue:“philadelphia“ }, { no:30, group:“C“, date:“2026-06-19″, venue:“boston“ }, { no:31, group:“D“, date:“2026-06-19″, venue:“san_francisco“ }, { no:32, group:“D“, date:“2026-06-19″, venue:“seattle“ }, { no:33, group:“E“, date:“2026-06-20″, venue:“toronto“ }, { no:34, group:“E“, date:“2026-06-20″, venue:“kansas_city“ }, { no:35, group:“F“, date:“2026-06-20″, venue:“houston“ }, { no:36, group:“F“, date:“2026-06-20″, venue:“monterrey“ }, { no:37, group:“H“, date:“2026-06-21″, venue:“miami“ }, { no:38, group:“H“, date:“2026-06-21″, venue:“atlanta“ }, { no:39, group:“G“, date:“2026-06-21″, venue:“los_angeles“ }, { no:40, group:“G“, date:“2026-06-21″, venue:“vancouver“ }, { no:41, group:“I“, date:“2026-06-22″, venue:“new_york_nj“ }, { no:42, group:“I“, date:“2026-06-22″, venue:“philadelphia“ }, { no:43, group:“J“, date:“2026-06-22″, venue:“dallas“ }, { no:44, group:“J“, date:“2026-06-22″, venue:“san_francisco“ }, { no:45, group:“L“, date:“2026-06-23″, venue:“boston“ }, { no:46, group:“L“, date:“2026-06-23″, venue:“toronto“ }, { no:47, group:“K“, date:“2026-06-23″, venue:“houston“ }, { no:48, group:“K“, date:“2026-06-23″, venue:“guadalajara“ }, // Matchday 3 { no:49, group:“C“, date:“2026-06-24″, venue:“miami“ }, { no:50, group:“C“, date:“2026-06-24″, venue:“atlanta“ }, { no:51, group:“B“, date:“2026-06-24″, venue:“vancouver“ }, { no:52, group:“B“, date:“2026-06-24″, venue:“seattle“ }, { no:53, group:“A“, date:“2026-06-24″, venue:“mexico_city“ }, { no:54, group:“A“, date:“2026-06-24″, venue:“monterrey“ }, { no:55, group:“E“, date:“2026-06-25″, venue:“philadelphia“ }, { no:56, group:“E“, date:“2026-06-25″, venue:“new_york_nj“ }, { no:57, group:“F“, date:“2026-06-25″, venue:“dallas“ }, { no:58, group:“F“, date:“2026-06-25″, venue:“kansas_city“ }, { no:59, group:“D“, date:“2026-06-25″, venue:“los_angeles“ }, { no:60, group:“D“, date:“2026-06-25″, venue:“san_francisco“ }, { no:61, group:“I“, date:“2026-06-26″, venue:“boston“ }, { no:62, group:“I“, date:“2026-06-26″, venue:“toronto“ }, { no:63, group:“G“, date:“2026-06-26″, venue:“seattle“ }, { no:64, group:“G“, date:“2026-06-26″, venue:“vancouver“ }, { no:65, group:“H“, date:“2026-06-26″, venue:“houston“ }, { no:66, group:“H“, date:“2026-06-26″, venue:“guadalajara“ }, { no:67, group:“L“, date:“2026-06-27″, venue:“new_york_nj“ }, { no:68, group:“L“, date:“2026-06-27″, venue:“philadelphia“ }, { no:69, group:“J“, date:“2026-06-27″, venue:“kansas_city“ }, { no:70, group:“J“, date:“2026-06-27″, venue:“dallas“ }, { no:71, group:“K“, date:“2026-06-27″, venue:“miami“ }, { no:72, group:“K“, date:“2026-06-27″, venue:“atlanta“ }, ]; // Groups list A–L const groups = [„A“,“B“,“C“,“D“,“E“,“F“,“G“,“H“,“I“,“J“,“K“,“L“]; /** ———- Map init ———- **/ const map = L.map(„map“, { zoomControl: true }).setView([38.5, -97.5], 4); // neutral tiles L.tileLayer(„https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png“, { maxZoom: 18, attribution: „© OpenStreetMap“ }).addTo(map); // icons (simple circle markers via styles) function markerStyle(highlight=false){ return { radius: highlight ? 9 : 6, weight: 2, color: highlight ? „#9bffcf“ : „#6a789e“, fillColor: highlight ? „#9bffcf“ : „#2b3558“, fillOpacity: highlight ? 0.9 : 0.6, opacity: highlight ? 1 : 0.5 }; } const venueIndex = Object.fromEntries(venues.map(v => [v.id, v])); // create markers const venueMarkers = {}; venues.forEach(v => { const m = L.circleMarker([v.lat, v.lng], markerStyle(false)) .addTo(map) .bindPopup(`${v.city}
${v.name}
${v.country}`); venueMarkers[v.id] = m; }); /** ———- UI ———- **/ const groupButtonsEl = document.getElementById(„groupButtons“); const scheduleEl = document.getElementById(„schedule“); const groupTitleEl = document.getElementById(„groupTitle“); let activeGroup = null; function renderButtons(){ groupButtonsEl.innerHTML = „“; groups.forEach(g => { const b = document.createElement(„div“); b.className = „btn“; b.textContent = `Gruppe ${g}`; b.onclick = () => selectGroup(g); b.dataset.group = g; groupButtonsEl.appendChild(b); }); } renderButtons(); document.getElementById(„btnAll“).onclick = () => selectGroup(null); document.getElementById(„btnResetView“).onclick = () => map.setView([38.5, -97.5], 4); function setActiveButton(group){ document.querySelectorAll(„.btn[data-group]“).forEach(b => { b.classList.toggle(„active“, b.dataset.group === group); }); } function venuesForGroup(group){ if(!group) return new Set(venues.map(v => v.id)); const set = new Set(matches.filter(m => m.group===group).map(m => m.venue)); return set; } function highlightVenues(group){ const set = venuesForGroup(group); venues.forEach(v => { const isHi = set.has(v.id); venueMarkers[v.id].setStyle(markerStyle(isHi)); }); } function formatDate(iso){ const d = new Date(iso + „T00:00:00“); return d.toLocaleDateString(„de-DE“, { weekday:“short“, day:“2-digit“, month:“2-digit“, year:“numeric“ }); } function renderSchedule(group){ scheduleEl.innerHTML = „“; groupTitleEl.textContent = group ? `Gruppe ${group}` : „Alle“; const list = group ? matches.filter(m => m.group===group) : matches.slice(); list.sort((a,b)=> a.date.localeCompare(b.date) || a.no-b.no); list.forEach(m => { const v = venueIndex[m.venue]; const row = document.createElement(„div“); row.className = „match“; row.innerHTML = `
M${m.no}
${formatDate(m.date)} Gruppe ${m.group}
${v.city} – ${v.name}
`; row.onclick = () => { map.setView([v.lat, v.lng], 6, { animate:true }); venueMarkers[v.id].openPopup(); }; scheduleEl.appendChild(row); }); if(list.length===0){ scheduleEl.innerHTML = `
Keine Matches gefunden.
`; } } function selectGroup(group){ activeGroup = group; setActiveButton(group); highlightVenues(group); renderSchedule(group); } selectGroup(null); // initial