let lastOpenedType = null; let isAnimating = false; function animateOpen(ausklappBox) { ausklappBox.style.maxHeight = '0px'; ausklappBox.classList.add('open'); ausklappBox.style.opacity = '1'; // Höhe messen const scrollHeight = ausklappBox.scrollHeight; // Im nächsten Frame auf die echte Höhe animieren requestAnimationFrame(() => { ausklappBox.style.maxHeight = scrollHeight + 'px'; }); } function animateClose(ausklappBox, callback) { // Höhe auf aktuellen Wert setzen, dann auf 0 animieren ausklappBox.style.maxHeight = ausklappBox.scrollHeight + 'px'; requestAnimationFrame(() => { ausklappBox.style.maxHeight = '0px'; ausklappBox.style.opacity = '0'; }); ausklappBox.addEventListener('transitionend', function handler(e) { if (e.propertyName === 'max-height') { ausklappBox.classList.remove('open'); ausklappBox.removeEventListener('transitionend', handler); if (callback) callback(); } }); } function getBoxHtml(type) { if (type === 'ausdruck') { return `

E-Rezept Ausdruck hochladen

Datei auswählen oder hier ablegen (PDF, JPG, PNG)

`; } else if (type === 'klassisch') { return `

Klassisches Rezept hochladen

Foto oder Scan des Rezepts hochladen (PDF, JPG, PNG)

`; } else if (type === 'egk') { return `

E-Rezept mit Ihrer Gesundheitskarte einlösen

Einfach, sicher und schnell: Nutzen Sie Ihre elektronische Gesundheitskarte
und lösen Sie Ihr E-Rezept direkt über unsere App ein.

`; } return ''; } document.querySelectorAll('.rezeptbox').forEach(box => { box.addEventListener('click', function() { if (isAnimating) return; const type = this.dataset.type; const container = document.getElementById('container-ausdruck'); const currentBox = container.querySelector('.ausklapp-box'); // Funktion zum neuen Inhalt einblenden function showNewBox(newBoxHtml) { container.innerHTML = newBoxHtml; container.style.display = 'block'; setTimeout(() => { const ausklappBox = container.querySelector('.ausklapp-box'); if (ausklappBox) { animateOpen(ausklappBox); } // Sanft scrollen, sodass der Container mittig im Viewport ist setTimeout(() => { const rect = container.getBoundingClientRect(); const scrollTop = window.pageYOffset || document.documentElement.scrollTop; const containerMiddle = rect.top + scrollTop - (window.innerHeight / 2) + (rect.height / 2); window.scrollTo({ top: containerMiddle, behavior: 'smooth' }); }, 200); isAnimating = false; lastOpenedType = type; }, 20); // kurz warten, damit die Transition greift } // Wenn dieselbe Box erneut geklickt wird: sanft schließen if (container.style.display === 'block' && lastOpenedType === type) { if (currentBox) { isAnimating = true; animateClose(currentBox, () => { container.style.display = 'none'; container.innerHTML = ''; lastOpenedType = null; isAnimating = false; }); } else { container.style.display = 'none'; container.innerHTML = ''; lastOpenedType = null; } return; } // Wenn eine andere Box geöffnet ist: erst sanft schließen, dann neue öffnen if (container.style.display === 'block' && lastOpenedType !== type) { if (currentBox) { isAnimating = true; animateClose(currentBox, () => { showNewBox(getBoxHtml(type)); }); } else { showNewBox(getBoxHtml(type)); } return; } // Wenn noch keine Box offen ist: einfach öffnen showNewBox(getBoxHtml(type)); }); }); (() => { const root = document.getElementById('ml-locations'); if (!root) return; // Links mit ersetzten Umlauten (ae/oe/ue/ss, Leerzeichen -> -) const locations = [ { name:"MAXMO Apotheke Erkelenz Karolingerring", city:"Erkelenz", url:"/MAXMO-Apotheke-Erkelenz-Karolingerring" }, { name:"MAXMO Apotheke im Kaufland Arnsberg", city:"Arnsberg", url:"/MAXMO-Apotheke-im-Kaufland-Arnsberg" }, { name:"MAXMO Apotheke StadtCenter Düren", city:"Düren", url:"/MAXMO-Apotheke-StadtCenter-Dueren" }, { name:"MAXMO Apotheke Kaufland Düren", city:"Düren", url:"/MAXMO-Apotheke-Kaufland-Dueren" }, { name:"MAXMO Apotheke Düsseldorf-Benrath", city:"Düsseldorf", url:"/MAXMO-Apotheke-Duesseldorf-Benrath" }, { name:"MAXMO Apotheke Bergkamen", city:"Bergkamen", url:"/MAXMO-Apotheke-Bergkamen" }, { name:"MAXMO Apotheke Jüchen-Gierath", city:"Jüchen", url:"/MAXMO-Apotheke-Juechen-Gierath" }, { name:"MAXMO Apotheke im Kaufland Grevenbroich", city:"Grevenbroich", url:"/MAXMO-Apotheke-im-Kaufland-Grevenbroich" }, { name:"MAXMO Apotheke im Kaufland Hückelhoven", city:"Hückelhoven", url:"/MAXMO-Apotheke-im-Kaufland-Hueckelhoven" }, { name:"MAXMO Apotheke am Maubishof", city:"Kaarst", url:"/MAXMO-Apotheke-am-Maubishof-Kaarst" }, { name:"MAXMO Apotheke Gremberg", city:"Köln", url:"/MAXMO-Apotheke-Gremberg" }, { name:"MAXMO Apotheke E‑Center Am Röttgen", city:"Mönchengladbach", url:"/MAXMO-Apotheke-im-E-Center-Am-Roettgen" }, { name:"MAXMO Apotheke Marienplatz Rheydt", city:"Mönchengladbach", url:"/MAXMO-Apotheke-Marienplatz-Rheydt" }, { name:"MAXMO Apotheke Hindenburgstraße", city:"Mönchengladbach", url:"/MAXMO-Apotheke-Hindenburgstrasse" }, { name:"MAXMO Apotheke Krefelder Straße", city:"Krefeld", url:"/MAXMO-Apotheke-Krefelder-Strasse" }, { name:"MAXMO Apotheke medicentrum Dahlener Straße", city:"Mönchengladbach", url:"/MAXMO-Apotheke-medicentrum-Dahlener-Strasse" }, { name:"MAXMO Apotheke Viersener Straße", city:"Mönchengladbach", url:"/MAXMO-Apotheke-Viersener-Strasse" }, { name:"MAXMO Apotheke Moses‑Stern‑Straße", city:"Mönchengladbach", url:"/MAXMO-Apotheke-Moses-Stern-Strasse" }, { name:"MAXMO Apotheke Eschweiler", city:"Eschweiler", url:"/MAXMO-Apotheke-Eschweiler" }, { name:"MAXMO Apotheke Übach‑Palenberg", city:"Übach‑Palenberg", url:"/MAXMO-Apotheke-Uebach-Palenberg" }, { name:"MAXMO Apotheke St. Helena", city:"Mönchengladbach", url:"/MAXMO-Apotheke-St-Helena" }, { name:"MAXMO Apotheke Würselen", city:"Würselen", url:"/MAXMO-Apotheke-Wuerselen" } ]; const searchInput = root.querySelector('.ml-search'); const filtersContainer = root.querySelector('#ml-filters'); const gridContainer = root.querySelector('#ml-grid'); const collator = new Intl.Collator('de', { sensitivity:'base' }); const cities = [...new Set(locations.map(l => l.city))].sort(collator.compare); cities.forEach(city => { const b = document.createElement('button'); b.className = 'ml-filter'; b.textContent = city; b.dataset.city = city; filtersContainer.appendChild(b); }); const filterToggle = root.querySelector('#ml-filter-toggle'); const filterDropdown = root.querySelector('#ml-filter-dropdown'); filterToggle.addEventListener('click', () => { filterDropdown.classList.toggle('is-open'); const icon = filterToggle.querySelector('.ml-filter-icon'); icon.style.transform = filterDropdown.classList.contains('is-open') ? 'rotate(180deg)' : 'rotate(0deg)'; }); document.addEventListener('click', (e) => { if (!e.target.closest('#ml-locations .ml-filter-wrapper')) { filterDropdown.classList.remove('is-open'); const icon = filterToggle.querySelector('.ml-filter-icon'); icon.style.transform = 'rotate(0deg)'; } }); let currentFilter = 'all'; let currentSearch = ''; filtersContainer.addEventListener('click', (e) => { if (e.target.classList.contains('ml-filter')) { filtersContainer.querySelectorAll('.ml-filter').forEach(f => f.classList.remove('is-active')); e.target.classList.add('is-active'); currentFilter = e.target.dataset.city; filterDropdown.classList.remove('is-open'); const icon = filterToggle.querySelector('.ml-filter-icon'); icon.style.transform = 'rotate(0deg)'; render(); } }); searchInput.addEventListener('input', (e) => { currentSearch = e.target.value.toLowerCase(); render(); }); function render() { const filtered = locations .filter(loc => { const byCity = currentFilter === 'all' || loc.city === currentFilter; const byQuery = !currentSearch || loc.name.toLowerCase().includes(currentSearch) || loc.city.toLowerCase().includes(currentSearch); return byCity && byQuery; }) .sort((a,b) => collator.compare(a.city, b.city)); if (filtered.length === 0) { gridContainer.innerHTML = '
Keine Standorte gefunden
'; return; } gridContainer.innerHTML = filtered.map(loc => `
${loc.name}
${loc.city}
`).join(''); } render(); })(); (function () { 'use strict'; /* ---------- KONFIGURATION ---------- */ // Formspree-Endpoint (MAXMO WM-Tippspiel) var FORMSPREE_ENDPOINT = 'https://formspree.io/f/xeedbryj'; var STORAGE_KEY = 'maxmo_wm_tipps_v2'; // Tippschluss = so viele Minuten VOR Anpfiff. Sicherheitspuffer gegen // Uhr-Abweichungen am Gerät; verhindert Tipps in der letzten Sekunde. var CLOSE_BUFFER_MIN = 2; // Alle Zeiten in MESZ (+02:00). unlockAt = ab wann tippbar (null = ab Seitenstart). var GAMES = { 1: { home: 'Deutschland', homeCode: 'GER', homeFlag: 'ger', away: 'Curaçao', awayCode: 'CUR', awayFlag: 'cur', // Spiel 1 ist ab Veröffentlichung der Seite offen (kein Freischalt-Datum). unlockAt: null, kickoffAt: '2026-06-14T19:00:00+02:00' }, 2: { home: 'Deutschland', homeCode: 'GER', homeFlag: 'ger', away: 'Elfenbeinküste', awayCode: 'CIV', awayFlag: 'civ', unlockAt: '2026-06-15T00:00:00+02:00', kickoffAt: '2026-06-20T22:00:00+02:00' }, 3: { home: 'Ecuador', homeCode: 'ECU', homeFlag: 'ecu', away: 'Deutschland', awayCode: 'GER', awayFlag: 'ger', unlockAt: '2026-06-21T00:00:00+02:00', kickoffAt: '2026-06-25T22:00:00+02:00' } }; /* ---------- FLAGGEN ---------- */ var flags = { ger: '', cur: '', civ: '', ecu: '' }; var ICON_LOCK = ''; var ICON_WHISTLE= ''; /* ---------- HELPERS ---------- */ function $(s, sc) { return (sc || document).querySelector(s); } function $$(s, sc) { return [].slice.call((sc || document).querySelectorAll(s)); } function openSheet(id) { var el = $('#' + id); if (el) el.classList.add('open'); } function closeSheet(id) { var el = $('#' + id); if (el) el.classList.remove('open'); } function closeAll() { $$('.scrim').forEach(function (s) { s.classList.remove('open'); }); } function fmtDateShort(d) { var dd = ('0' + d.getDate()).slice(-2), mm = ('0' + (d.getMonth() + 1)).slice(-2); return dd + '.' + mm + '.'; } // "Jetzt" — nur über die interne Preview überschreibbar (window.__PREVIEW_NOW__). // Bewusst KEIN Lesen aus der URL, damit in Produktion niemand per ?preview= // ein gesperrtes Spiel vorzeitig freischalten kann. var PREVIEW_NOW = (typeof window !== 'undefined' && window.__PREVIEW_NOW__) ? window.__PREVIEW_NOW__ : null; function now() { return PREVIEW_NOW ? new Date(PREVIEW_NOW) : new Date(); } // Sicherer localStorage-Zugriff (Private Mode / deaktiviert) var store = { read: function () { try { return JSON.parse(localStorage.getItem(STORAGE_KEY) || '{}') || {}; } catch (e) { return {}; } }, write: function (obj) { try { localStorage.setItem(STORAGE_KEY, JSON.stringify(obj)); } catch (e) {} } }; /* ---------- STATE ---------- */ var tipps = store.read(); var scores = { 1: { home: 0, away: 0 }, 2: { home: 0, away: 0 }, 3: { home: 0, away: 0 } }; var pendingGame = null; var pendingForm = null; var submitting = false; // Flaggen einsetzen $$('[data-flag]').forEach(function (el) { el.innerHTML = flags[el.dataset.flag] || ''; }); /* ---------- SCORE ---------- */ function renderScore(game) { $$('.vstepper__num[data-side="home"][data-game="' + game + '"]').forEach(function (el) { el.textContent = scores[game].home; }); $$('.vstepper__num[data-side="away"][data-game="' + game + '"]').forEach(function (el) { el.textContent = scores[game].away; }); } function setScore(game, side, val) { scores[game][side] = Math.max(0, Math.min(20, val)); renderScore(game); } /* ---------- ZUSTAND PRO SPIEL ---------- */ // Tippschluss-Zeitpunkt = Anpfiff minus Puffer function closeTime(game) { return new Date(new Date(GAMES[game].kickoffAt).getTime() - CLOSE_BUFFER_MIN * 60000); } // Liefert: 'getippt' | 'gesperrt' | 'offen' | 'tippschluss' function gameState(game) { if (tipps[game] && typeof tipps[game].home === 'number') return 'getippt'; var cfg = GAMES[game], t = now(); if (cfg.unlockAt && t < new Date(cfg.unlockAt)) return 'gesperrt'; if (t >= closeTime(game)) return 'tippschluss'; return 'offen'; } function applyState(game) { var card = $('#match-' + game); if (!card) return; var cfg = GAMES[game]; var state = gameState(game); var overlay = card.querySelector('.lock-overlay'); var pill = overlay ? overlay.querySelector('.lock-pill') : null; var submitBtn = card.querySelector('[data-action="submit"]'); var countdown = card.querySelector('.countdown'); lastState[game] = state; card.classList.remove('match--locked', 'match--closed', 'match--done'); if (countdown) countdown.hidden = true; if (state === 'getippt') { scores[game] = { home: tipps[game].home, away: tipps[game].away }; renderScore(game); card.classList.add('match--done'); // Button wird per CSS (.match--done) ausgeblendet, grüne Bestätigungszeile eingeblendet if (submitBtn) submitBtn.disabled = true; return; } if (submitBtn) { submitBtn.disabled = false; submitBtn.innerHTML = 'Tipp abgeben '; } if (state === 'gesperrt') { card.classList.add('match--locked'); if (pill) pill.innerHTML = ICON_LOCK + 'Wird am ' + fmtDateShort(new Date(cfg.unlockAt)) + ' freigeschaltet'; } else if (state === 'tippschluss') { card.classList.add('match--closed'); if (pill) pill.innerHTML = ICON_WHISTLE + 'Tippschluss'; } else { // offen if (countdown) countdown.hidden = false; updateCountdown(game); } } /* ---------- LIVE-COUNTDOWN ---------- */ function updateCountdown(game) { var card = $('#match-' + game); if (!card) return; var el = card.querySelector('.countdown__text'); if (!el) return; var diff = closeTime(game) - now(); // zählt auf den Tippschluss (2 Min vor Anpfiff) if (diff <= 0) { applyState(game); return; } // gerade Tippschluss erreicht var mins = Math.floor(diff / 60000); var d = Math.floor(mins / 1440); var h = Math.floor((mins % 1440) / 60); var m = mins % 60; var txt; if (d > 0) txt = 'Noch ' + d + ' ' + (d === 1 ? 'Tag' : 'Tage') + ' ' + h + ' Std bis Tippschluss'; else if (h > 0) txt = 'Noch ' + h + ' Std ' + m + ' Min bis Tippschluss'; else txt = 'Noch ' + m + ' Min bis Tippschluss'; el.textContent = txt; } function refreshAllStates() { [1, 2, 3].forEach(applyState); } // Tick: erkennt Zustandswechsel (z. B. Tippschluss erreicht) und sperrt die Karte // automatisch — auch ohne Reload — sonst nur Countdown-Text aktualisieren. var lastState = {}; function tick() { [1, 2, 3].forEach(function (g) { var s = gameState(g); if (s !== lastState[g]) applyState(g); else if (s === 'offen') updateCountdown(g); }); } /* ---------- SCORE BUTTONS ---------- */ document.addEventListener('click', function (e) { var btn = e.target.closest('.vstepper__btn'); if (!btn) return; var game = btn.dataset.game; if (gameState(game) !== 'offen') return; // Sicherheits-Gate if (btn.dataset.action === 'inc') setScore(game, btn.dataset.side, scores[game][btn.dataset.side] + 1); if (btn.dataset.action === 'dec') setScore(game, btn.dataset.side, scores[game][btn.dataset.side] - 1); }); /* ---------- FORMULAR ÖFFNEN ---------- */ document.addEventListener('click', function (e) { var btn = e.target.closest('[data-action="submit"]'); if (!btn) return; var game = btn.dataset.game; if (gameState(game) !== 'offen') return; // gesperrt/tippschluss/getippt pendingGame = game; clearFormErrors(); openSheet('scrim-form'); var first = $('#vorname'); if (first) setTimeout(function () { first.focus(); }, 300); }); /* ---------- VALIDIERUNG ---------- */ var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; function setFieldError(name, msg) { var input = $('#' + name); var err = $('#err-' + name); if (input) input.classList.toggle('field__input--error', !!msg); if (err) { err.textContent = msg || ''; err.hidden = !msg; } } function clearFormErrors() { ['vorname', 'nachname', 'email'].forEach(function (n) { setFieldError(n, ''); }); var agbErr = $('#err-agb'); if (agbErr) agbErr.hidden = true; } function validateForm(data) { var ok = true; if (!data.vorname || !data.vorname.trim()) { setFieldError('vorname', 'Bitte Vorname angeben.'); ok = false; } else setFieldError('vorname', ''); if (!data.nachname || !data.nachname.trim()) { setFieldError('nachname', 'Bitte Nachname angeben.'); ok = false; } else setFieldError('nachname', ''); if (!data.email || !EMAIL_RE.test(data.email)) { setFieldError('email', 'Bitte gültige E-Mail-Adresse angeben.'); ok = false; } else setFieldError('email', ''); var agbErr = $('#err-agb'); if (!data.agb) { if (agbErr) agbErr.hidden = false; ok = false; } else if (agbErr) agbErr.hidden = true; return ok; } // Fehler beim Tippen wieder ausblenden ['vorname', 'nachname', 'email'].forEach(function (n) { var input = $('#' + n); if (input) input.addEventListener('input', function () { setFieldError(n, ''); }); }); /* ---------- FORMULAR -> BESTÄTIGUNG ---------- */ $('#tipp-form').addEventListener('submit', function (e) { e.preventDefault(); var fd = new FormData(e.target), data = {}; fd.forEach(function (v, k) { data[k] = v; }); if (!validateForm(data)) return; pendingForm = data; closeSheet('scrim-form'); $('#confirm-match').textContent = GAMES[pendingGame].home + ' vs ' + GAMES[pendingGame].away; $('#confirm-home').textContent = scores[pendingGame].home; $('#confirm-away').textContent = scores[pendingGame].away; var cErr = $('#confirm-error'); if (cErr) cErr.hidden = true; setTimeout(function () { openSheet('scrim-confirm'); }, 220); }); /* ---------- "ZURÜCK" -> wieder zum Formular ---------- */ var backBtn = $('#confirm-back'); if (backBtn) backBtn.addEventListener('click', function () { closeSheet('scrim-confirm'); setTimeout(function () { openSheet('scrim-form'); }, 220); }); /* ---------- ABSCHICKEN (robust, await) ---------- */ $('#confirm-submit').addEventListener('click', function () { if (submitting || !pendingGame || !pendingForm) return; // Letzter Gate-Check: nur ein offenes Spiel darf abgeschickt werden // (fängt 'getippt' = schon getippt, 'tippschluss' und 'gesperrt' ab) var stNow = gameState(pendingGame); if (stNow !== 'offen') { var cErr0 = $('#confirm-error'); if (cErr0) { cErr0.textContent = (stNow === 'getippt') ? 'Für dieses Spiel hast du bereits einen Tipp abgegeben.' : 'Der Anpfiff ist erreicht — für dieses Spiel ist die Teilnahme leider beendet.'; cErr0.hidden = false; } return; } var btn = this; var payload = { spieltag: pendingGame, match: GAMES[pendingGame].home + ' vs ' + GAMES[pendingGame].away, tipp_heim: scores[pendingGame].home, tipp_gast: scores[pendingGame].away, vorname: pendingForm.vorname.trim(), nachname: pendingForm.nachname.trim(), email: pendingForm.email.trim(), newsletter: pendingForm.newsletter ? 'ja' : 'nein', zeitpunkt: now().toISOString(), // Formspree-Spezialfelder: Betreff + Antwort-an für die Benachrichtigungs-Mail _subject: 'WM-Tipp Spieltag ' + pendingGame + ': ' + GAMES[pendingGame].home + ' ' + scores[pendingGame].home + ':' + scores[pendingGame].away + ' ' + GAMES[pendingGame].away, _replyto: pendingForm.email.trim() }; submitting = true; btn.classList.add('is-loading'); btn.disabled = true; btn.dataset.label = btn.textContent; btn.textContent = 'Wird gesendet …'; var cErr = $('#confirm-error'); if (cErr) cErr.hidden = true; sendTipp(payload).then(function () { // Erst bei echtem Erfolg: lokal speichern + Erfolg zeigen tipps[pendingGame] = { home: scores[pendingGame].home, away: scores[pendingGame].away }; store.write(tipps); submitting = false; btn.classList.remove('is-loading'); btn.disabled = false; btn.textContent = btn.dataset.label || 'Ja, abschicken'; closeSheet('scrim-confirm'); setTimeout(function () { openSheet('scrim-success'); fireConfetti(); applyState(pendingGame); }, 220); }).catch(function () { submitting = false; btn.classList.remove('is-loading'); btn.disabled = false; btn.textContent = btn.dataset.label || 'Ja, abschicken'; if (cErr) { cErr.textContent = 'Übermittlung fehlgeschlagen. Bitte Internetverbindung prüfen und erneut versuchen.'; cErr.hidden = false; } }); }); function sendTipp(payload) { // Kein konfigurierter Endpoint -> im Test/Vorschau trotzdem "Erfolg" if (!FORMSPREE_ENDPOINT || FORMSPREE_ENDPOINT.indexOf('REPLACE_ME') !== -1) { return Promise.resolve(); } return fetch(FORMSPREE_ENDPOINT, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }, body: JSON.stringify(payload) }).then(function (res) { if (!res.ok) throw new Error('HTTP ' + res.status); return res; }); } /* ---------- SHEETS SCHLIESSEN ---------- */ document.addEventListener('click', function (e) { if (e.target.matches('[data-close]')) closeAll(); }); $$('.scrim').forEach(function (scrim) { scrim.addEventListener('click', function (e) { if (e.target === scrim) closeAll(); }); }); document.addEventListener('keydown', function (e) { if (e.key === 'Escape' && !submitting) closeAll(); }); /* ---------- "Teilnahmebedingungen"-Link -> Sheet schließen + aufklappen ---------- */ document.addEventListener('click', function (e) { var link = e.target.closest('[data-tob]'); if (!link) return; e.preventDefault(); closeAll(); var tob = $('#tob'); if (tob) { tob.open = true; setTimeout(function () { if (tob.scrollIntoView) tob.scrollIntoView({ behavior: 'smooth', block: 'start' }); }, 260); } }); /* ---------- KONFETTI ---------- */ function fireConfetti() { var colors = ['#ff0000', '#0a0a0a', '#ffffff', '#ffce00']; var el = $('#confetti'); if (!el) return; el.innerHTML = ''; for (var i = 0; i < 40; i++) { var s = document.createElement('span'); s.style.left = (i / 40 * 100) + '%'; s.style.background = colors[i % colors.length]; s.style.animationDelay = ((i % 8) * 0.05) + 's'; s.style.animationDuration = (1.2 + (i % 5) * 0.16) + 's'; s.style.transform = 'rotate(' + (i * 37 % 360) + 'deg)'; el.appendChild(s); } } /* ---------- VERSTECKTER RESET (8× auf den unteren Hinweistext tippen) ---------- */ // Nur für Tests. Löscht ausschließlich den lokalen Browser-Stand (localStorage) — // bereits an Formspree abgeschickte Tipps bleiben unberührt. (function () { var hotzone = $('.footer') || $('.app'); if (!hotzone) return; var taps = 0, timer = null; hotzone.addEventListener('click', function () { taps++; clearTimeout(timer); timer = setTimeout(function () { taps = 0; }, 2000); if (taps >= 8) { taps = 0; try { localStorage.removeItem(STORAGE_KEY); } catch (e) {} alert('Tipps zurückgesetzt (nur lokal — abgeschickte Tipps bleiben erhalten).'); location.reload(); } }); })(); /* ---------- INIT ---------- */ refreshAllStates(); setInterval(tick, 30000); // alle 30 s: Countdown + automatische Sperre bei Tippschluss })();