import { useState, useEffect } from "react"; import { ArrowLeft, RefreshCw } from "lucide-react"; /* ------------------------------------------------------------------ */ /* Stil: Flutlicht über nassem Rasen, Kreide auf der Taktiktafel */ /* ------------------------------------------------------------------ */ const CSS = ` @import url('https://fonts.googleapis.com/css2?family=Anton&family=Barlow:wght@400;500;600;700&family=Space+Mono:wght@400;700&display=swap'); .ap-root{ --night:#0A1F1C; --night2:#0E2B26; --panel:#102F29; --line:rgba(242,239,227,0.16); --chalk:#F2EFE3; --dim:rgba(242,239,227,0.60); --flood:#FFC24B; --grass:#59B389; --red:#E2574C; background: radial-gradient(120% 60% at 50% -10%, rgba(255,194,75,0.16), transparent 60%), var(--night); color:var(--chalk); font-family:'Barlow', system-ui, sans-serif; min-height:100%; } .ap-root *{box-sizing:border-box;} .disp{font-family:'Anton', Impact, sans-serif; letter-spacing:0.02em; text-transform:uppercase; font-weight:400;} .mono{font-family:'Space Mono', ui-monospace, monospace;} .chalk{color:var(--chalk);} .dim{color:var(--dim);} .flood{color:var(--flood);} .grass{color:var(--grass);} .red{color:var(--red);} .hair{border-color:var(--line);} .panel{background:var(--panel); border:1px solid var(--line);} .board{background:linear-gradient(180deg, rgba(255,255,255,0.03), rgba(0,0,0,0.18)); border:1px solid var(--line);} .btn{ font-family:'Barlow',sans-serif; font-weight:600; letter-spacing:0.06em; text-transform:uppercase; border:1px solid var(--line); color:var(--chalk); background:transparent; padding:0.6rem 1.1rem; font-size:0.78rem; cursor:pointer; transition:background .15s, border-color .15s, color .15s; } .btn:hover{background:rgba(242,239,227,0.07); border-color:rgba(242,239,227,0.4);} .btn-solid{background:var(--flood); color:#12211B; border-color:var(--flood);} .btn-solid:hover{background:#ffd47a; border-color:#ffd47a; color:#12211B;} .btn[disabled]{opacity:.4; cursor:not-allowed;} .ap-root :focus-visible{outline:2px solid var(--flood); outline-offset:2px;} .tile{ position:relative; background:rgba(242,239,227,0.03); border:1px solid var(--line); color:var(--chalk); cursor:pointer; text-align:left; line-height:1.15; transition:background .15s, border-color .15s, transform .12s; } .tile:hover{background:rgba(242,239,227,0.08); border-color:rgba(242,239,227,0.35);} .tile.on{background:rgba(255,194,75,0.14); border-color:var(--flood); color:var(--flood);} .tile.free{background:rgba(89,179,137,0.12); border-color:var(--grass); color:var(--grass); cursor:default;} .tile.won{background:rgba(89,179,137,0.16); border-color:var(--grass);} .goal{position:relative; width:100%; padding-top:52%; background:linear-gradient(180deg, rgba(255,255,255,0.05), rgba(0,0,0,0.25)); border:3px solid var(--chalk); border-bottom:none;} .net{position:absolute; inset:0; opacity:.16; background-image:linear-gradient(rgba(242,239,227,.9) 1px, transparent 1px), linear-gradient(90deg, rgba(242,239,227,.9) 1px, transparent 1px); background-size:22px 22px;} .zone{position:absolute; border:1px dashed transparent; cursor:pointer; transition:background .15s, border-color .15s;} .zone:hover{background:rgba(255,194,75,0.14); border-color:rgba(255,194,75,.6);} .zone[disabled]{cursor:default;} .zone[disabled]:hover{background:transparent; border-color:transparent;} .ball{position:absolute; width:7%; padding-top:7%; border-radius:50%; background:var(--chalk); box-shadow:0 0 0 2px rgba(10,31,28,.8), 0 6px 18px rgba(0,0,0,.5); transform:translate(-50%,-50%); transition:left .55s cubic-bezier(.2,.8,.3,1), top .55s cubic-bezier(.2,.8,.3,1);} .keeper{position:absolute; width:15%; height:26%; border-radius:6px; background:var(--flood); opacity:.85; transform:translate(-50%,-50%); transition:left .4s ease, top .4s ease;} .dot{width:10px; height:10px; border-radius:50%; display:inline-block; border:1px solid var(--line);} .dot.g{background:var(--grass); border-color:var(--grass);} .dot.r{background:var(--red); border-color:var(--red);} .opt{width:100%; text-align:left; padding:0.85rem 1rem; background:rgba(242,239,227,0.03); border:1px solid var(--line); color:var(--chalk); cursor:pointer; transition:background .15s, border-color .15s;} .opt:hover:not([disabled]){background:rgba(242,239,227,0.08); border-color:rgba(242,239,227,0.4);} .opt.right{background:rgba(89,179,137,0.16); border-color:var(--grass); color:var(--grass);} .opt.wrong{background:rgba(226,87,76,0.14); border-color:var(--red); color:var(--red);} .opt[disabled]{cursor:default;} .card{background:rgba(242,239,227,0.03); border:1px solid var(--line); transition:background .15s, border-color .15s, transform .15s; cursor:pointer; text-align:left; width:100%;} .card:hover{background:rgba(242,239,227,0.07); border-color:rgba(255,194,75,.55); transform:translateY(-2px);} .bar{height:3px; background:rgba(242,239,227,0.12);} .bar > div{height:100%; background:var(--flood); transition:width .3s ease;} @keyframes flicker{0%,100%{opacity:.9} 45%{opacity:.55} 47%{opacity:.95}} .flick{animation:flicker 5s ease-in-out infinite;} @media (prefers-reduced-motion: reduce){ .ap-root *{animation:none !important; transition:none !important;} } `; const shuffle = (arr) => { const a = [...arr]; for (let i = a.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [a[i], a[j]] = [a[j], a[i]]; } return a; }; /* ------------------------------------------------------------------ */ /* Daten */ /* ------------------------------------------------------------------ */ const BINGO_EVENTS = [ "Tor aus dem Strafraum", "Kopfballtor", "Freistoßtor", "Elfmeter gepfiffen", "Torwart hält den Elfmeter", "Latte oder Pfosten", "Eigentor", "Gelbe Karte", "Gelb wegen Meckern", "Rote Karte", "Abseitspfiff", "Tor nach Videobeweis aberkannt", "Schiri lässt Vorteil laufen", "Trainer diskutiert an der Linie", "Doppelwechsel", "Wechsel in der Halbzeit", "Spieler bleibt liegen und macht weiter", "Krampf in der Verlängerung", "Fehlpass direkt zum Gegner", "Rückpass zum Torwart unter Druck", "Kurz ausgeführte Ecke", "Ecke bringt eine Großchance", "Schuss aus über 25 Metern", "Fallrückzieher-Versuch", "Ball landet auf der Tribüne", "Weiter Abschlag des Torwarts", "Grätsche mit sauberer Balleroberung", "Konter über die Außenbahn", "Doppelpass in den Sechzehner", "Tor in den letzten 10 Minuten", "Nachspielzeit von 5 Minuten oder mehr", "Torjubel vor der eigenen Kurve", "Trikottausch nach Abpfiff", "Kommentator nennt eine kuriose Statistik", "Freistoß bleibt in der Mauer hängen", "Ball an die Hand im Strafraum", "Torwart läuft zur Ecke mit hoch", "Spieler wird ausgepfiffen", "Fahne oder Schal groß im Bild", "Standbild-Wiederholung in Zeitlupe", "Innenverteidiger dribbelt sich fest", "Schuss geht meterweit drüber", ]; const QUIZ = [ { q: "Wer wurde 2014 in Brasilien Weltmeister?", a: "Deutschland", w: ["Argentinien", "Brasilien", "Spanien"] }, { q: "Wie breit ist ein Fußballtor?", a: "7,32 m", w: ["6,50 m", "8,00 m", "9,15 m"] }, { q: "Welcher Klub hat die meisten Titel im Europapokal der Landesmeister bzw. der Champions League?", a: "Real Madrid", w: ["AC Mailand", "FC Bayern", "FC Liverpool"] }, { q: "Wer erzielte das Siegtor im WM-Finale 2014?", a: "Mario Götze", w: ["Thomas Müller", "André Schürrle", "Miroslav Klose"] }, { q: "In welchem Stadion spielt Borussia Dortmund?", a: "Signal Iduna Park", w: ["Veltins-Arena", "Weserstadion", "Volksparkstadion"] }, { q: "Wie viele Klubs spielen in der Bundesliga?", a: "18", w: ["16", "20", "22"] }, { q: "Wofür steht die Abkürzung VAR?", a: "Video Assistant Referee", w: ["Visual Action Review", "Video Analysis Rule", "Verified Assistant Replay"] }, { q: "Welches Land gewann die erste Fußball-WM 1930?", a: "Uruguay", w: ["Brasilien", "Italien", "Argentinien"] }, { q: "Welche Nation gewann die EM 2016?", a: "Portugal", w: ["Frankreich", "Deutschland", "Spanien"] }, { q: "Wer ist Rekordtorschütze bei Weltmeisterschaften?", a: "Miroslav Klose", w: ["Ronaldo", "Gerd Müller", "Pelé"] }, { q: "Wie viele Punkte gibt es für einen Sieg?", a: "3", w: ["1", "2", "4"] }, { q: "Wie groß ist der Radius des Mittelkreises?", a: "9,15 m", w: ["7,32 m", "11,00 m", "16,50 m"] }, { q: "Wie weit ist der Elfmeterpunkt von der Torlinie entfernt?", a: "11 m", w: ["9,15 m", "12 m", "16,50 m"] }, { q: "Welcher Klub trägt den Spitznamen „Werkself“?", a: "Bayer 04 Leverkusen", w: ["VfL Wolfsburg", "TSG Hoffenheim", "FC Schalke 04"] }, { q: "Wer gewann die WM 2022 in Katar?", a: "Argentinien", w: ["Frankreich", "Kroatien", "Marokko"] }, { q: "Wer gewann die EM 2024 in Deutschland?", a: "Spanien", w: ["England", "Frankreich", "Deutschland"] }, { q: "Wie viele Bundesliga-Tore erzielte Robert Lewandowski in der Rekordsaison 2020/21?", a: "41", w: ["37", "40", "44"] }, { q: "Wer wurde 2002 als bester Spieler der WM ausgezeichnet?", a: "Oliver Kahn", w: ["Ronaldo", "Rivaldo", "Michael Ballack"] }, { q: "Wie lang ist die reguläre Spielzeit?", a: "90 Minuten", w: ["80 Minuten", "100 Minuten", "120 Minuten"] }, { q: "Welcher Verein spielt im Camp Nou?", a: "FC Barcelona", w: ["Real Madrid", "Atlético Madrid", "FC Valencia"] }, ]; const PLAYERS = [ { name: "Miroslav Klose", w: ["Lukas Podolski", "Mario Gomez", "Oliver Bierhoff"], clues: [ "Geboren in Polen, spielte er später für Deutschland.", "Sein Markenzeichen war ein Salto als Torjubel.", "Er spielte für Kaiserslautern, Werder Bremen, Bayern und Lazio Rom.", "2014 wurde er in Brasilien Weltmeister.", "Mit 16 Treffern ist er Rekordtorschütze bei Weltmeisterschaften.", ], }, { name: "Thomas Müller", w: ["Mario Götze", "Toni Kroos", "Philipp Lahm"], clues: [ "In München wurde er zur Vereinslegende.", "Seine Position nennt er selbst „Raumdeuter“.", "Bei der WM 2010 holte er den Goldenen Schuh als bester Torschütze.", "2014 wurde er mit Deutschland Weltmeister.", "Bei Bayern trug er jahrelang die Nummer 25.", ], }, { name: "Oliver Kahn", w: ["Jens Lehmann", "Manuel Neuer", "Sepp Maier"], clues: [ "Er stand seine ganze Karriere zwischen den Pfosten.", "Sein Spitzname: „Der Titan“.", "Er spielte für den Karlsruher SC und den FC Bayern.", "2002 wurde er als bester Spieler der WM ausgezeichnet — als bislang einziger Torwart.", "Später war er Vorstandsvorsitzender des FC Bayern.", ], }, { name: "Lionel Messi", w: ["Neymar", "Sergio Agüero", "Andrés Iniesta"], clues: [ "Als Jugendlicher zog er aus Südamerika nach Spanien.", "Sein Spitzname ist „La Pulga“ — der Floh.", "Achtmal gewann er den Ballon d’Or.", "Mit Barcelona holte er vier Champions-League-Titel.", "2022 wurde er in Katar Weltmeister.", ], }, { name: "Zinédine Zidane", w: ["Ronaldinho", "Luís Figo", "Roberto Baggio"], clues: [ "Er spielte im Mittelfeld und wurde später Trainer.", "Im WM-Finale 2006 sah er Rot nach einem Kopfstoß.", "1998 wurde er im eigenen Land Weltmeister — mit zwei Kopfballtoren im Finale.", "Als Trainer gewann er dreimal in Folge die Champions League.", "Als Spieler stand er bei Juventus und Real Madrid unter Vertrag.", ], }, { name: "Robert Lewandowski", w: ["Erling Haaland", "Zlatan Ibrahimović", "Pierre-Emerick Aubameyang"], clues: [ "Er ist Rekordtorschütze seiner Nationalmannschaft.", "2015 traf er als Einwechselspieler fünfmal in neun Minuten.", "Ablösefrei wechselte er von Dortmund nach München.", "In der Saison 2020/21 erzielte er 41 Bundesliga-Tore — Rekord.", "Danach ging er zum FC Barcelona.", ], }, { name: "Manuel Neuer", w: ["Marc-André ter Stegen", "Bernd Leno", "Roman Weidenfeller"], clues: [ "Seine Karriere begann auf Schalke.", "Der „mitspielende Torwart“ wurde durch ihn zum Standard.", "2014 wurde er Weltmeister und bekam den Goldenen Handschuh.", "2020 gewann er mit Bayern das Triple.", "Er trägt die Nummer 1 und lange Zeit die Kapitänsbinde.", ], }, { name: "Diego Maradona", w: ["Pelé", "Ronaldo", "Gabriel Batistuta"], clues: [ "Er wurde 1960 in Buenos Aires geboren.", "1986 führte er sein Land zum WM-Titel.", "Die „Hand Gottes“ und das „Tor des Jahrhunderts“ fielen im selben Spiel.", "In Neapel wird er bis heute verehrt.", "Das Stadion von Napoli trägt seinen Namen.", ], }, { name: "Toni Kroos", w: ["Mesut Özil", "Bastian Schweinsteiger", "İlkay Gündoğan"], clues: [ "Er stammt aus Greifswald und wurde bei Bayern ausgebildet.", "2014 wurde er Weltmeister.", "Sechs Champions-League-Titel stehen in seiner Vita — fünf davon mit Real Madrid.", "Nach der Heim-EM 2024 beendete er seine Karriere.", "Sein Markenzeichen: der Pass aus dem Zentrum, Rückennummer 8.", ], }, { name: "Erling Haaland", w: ["Kylian Mbappé", "Harry Kane", "Vinícius Júnior"], clues: [ "Sein Vater spielte selbst in der Premier League.", "Über Salzburg kam er nach Dortmund.", "In seinem Bundesliga-Debüt traf er nach Einwechslung dreimal.", "2023 gewann er mit Manchester City das Triple.", "Er ist Norweger und trägt die Nummer 9.", ], }, ]; /* ------------------------------------------------------------------ */ /* Kreide-Glyphen fürs Menü */ /* ------------------------------------------------------------------ */ const glyphProps = { fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round" }; function GlyphBingo() { return ( ); } function GlyphGoal() { return ( ); } function GlyphQuiz() { return ( ); } function GlyphPerson() { return ( ); } /* ------------------------------------------------------------------ */ /* Fußball-Bingo */ /* ------------------------------------------------------------------ */ function makeCard(size) { const picks = shuffle(BINGO_EVENTS).slice(0, size * size); const cells = picks.map((t) => ({ text: t, marked: false, free: false })); if (size === 5) cells[12] = { text: "Anpfiff", marked: true, free: true }; return cells; } function linesOf(size) { const out = []; for (let r = 0; r < size; r++) out.push([...Array(size)].map((_, c) => r * size + c)); for (let c = 0; c < size; c++) out.push([...Array(size)].map((_, r) => r * size + c)); out.push([...Array(size)].map((_, i) => i * size + i)); out.push([...Array(size)].map((_, i) => i * size + (size - 1 - i))); return out; } function Bingo() { const [size, setSize] = useState(5); const [cells, setCells] = useState(() => makeCard(5)); const newCard = (s) => { setSize(s); setCells(makeCard(s)); }; const toggle = (i) => { if (cells[i].free) return; setCells(cells.map((c, k) => (k === i ? { ...c, marked: !c.marked } : c))); }; const full = linesOf(size).filter((l) => l.every((i) => cells[i].marked)); const winSet = new Set(full.flat()); const marked = cells.filter((c) => c.marked && !c.free).length; return (
Tippt an, was ihr im Spiel seht. Eine volle Reihe – waagerecht, senkrecht oder diagonal – ist Bingo.
{marked} von {size * size - (size === 5 ? 1 : 0)} Feldern markiert
Fünf Schüsse, fünf Paraden. Bei Gleichstand geht es ins Sudden Death.
{msg}
{phase === "keep" ? "HALTEN" : phase === "shoot" ? "SCHIESSEN" : "ENDE"}ENDSTAND
{score} / {set.length}
{rank}
Zehn Fragen, vier Antworten, kein Videobeweis.
{q.q}
{picked === q.a ? "Richtig." : `Falsch — richtig ist: ${q.a}`}
GESAMMELTE PUNKTE
{score}
Maximal möglich waren 250.
Je weniger Hinweise du brauchst, desto mehr Punkte gibt es.
{picked === p.name ? `Richtig — ${value} Punkte.` : `Gesucht war ${p.name}.`}
Aktueller Wert: {value} Punkte
)}Vier Spiele für den Fußballabend — vor dem Fernseher, in der Kabine oder am Tresen. Alles läuft auf einem Gerät, das ihr herumreicht.
Tipp: Beim Bingo reicht ein Handy pro Team — Karte zeigen, Feld antippen, weiterschauen.
> ) : ( <> {view === "bingo" &&