Bei neuem Service Worker wird die Seite automatisch neu geladen. Kein Popup mehr für die User.
408 lines
14 KiB
HTML
408 lines
14 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="de">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>JGA Prag Packliste</title>
|
||
<link rel="manifest" href="manifest.json">
|
||
<meta name="theme-color" content="#3b82f6">
|
||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||
<meta name="apple-mobile-web-app-title" content="JGA Prag">
|
||
<meta name="mobile-web-app-capable" content="yes">
|
||
<script>
|
||
(function() {
|
||
const saved = localStorage.getItem('jga-theme');
|
||
document.documentElement.setAttribute('data-theme', saved || 'dark');
|
||
})();
|
||
</script>
|
||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🧭</text></svg>">
|
||
<style>
|
||
/* ====== Theme-Variablen ====== */
|
||
:root, [data-theme="dark"] {
|
||
--bg: #1c1c1f;
|
||
--card-bg: #26262a;
|
||
--card-border: #353539;
|
||
--text: #e8e8ea;
|
||
--text-muted: #999;
|
||
--text-strong: #fff;
|
||
--accent: #3b82f6;
|
||
--accent-text: #60a5fa;
|
||
--progress-bg: #2a2a2d;
|
||
}
|
||
[data-theme="light"] {
|
||
--bg: #f5f5f7;
|
||
--card-bg: #ffffff;
|
||
--card-border: #e0e0e3;
|
||
--text: #1a1a1a;
|
||
--text-muted: #666;
|
||
--text-strong: #000;
|
||
--accent: #2563eb;
|
||
--accent-text: #2563eb;
|
||
--progress-bg: #e0e0e3;
|
||
}
|
||
* { box-sizing: border-box; }
|
||
html, body {
|
||
margin: 0; padding: 0;
|
||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||
background: var(--bg);
|
||
color: var(--text);
|
||
min-height: 100vh;
|
||
transition: background 0.3s, color 0.3s;
|
||
}
|
||
.container { max-width: 900px; margin: 0 auto; padding: 40px 24px; }
|
||
h1 {
|
||
font-size: 38px;
|
||
margin: 0 0 8px 0;
|
||
color: var(--text-strong);
|
||
font-weight: 700;
|
||
}
|
||
.subtitle { color: var(--text-muted); margin-bottom: 32px; font-size: 16px; }
|
||
|
||
.back-link {
|
||
display: inline-block;
|
||
margin-bottom: 24px;
|
||
color: var(--accent-text);
|
||
text-decoration: none;
|
||
font-size: 14px;
|
||
}
|
||
.back-link:hover { text-decoration: underline; }
|
||
|
||
/* Theme-Toggle Button */
|
||
.home-btn {
|
||
position: fixed;
|
||
top: 16px; left: 16px;
|
||
z-index: 1000;
|
||
background: var(--card-bg);
|
||
border: 1px solid var(--card-border);
|
||
color: var(--text);
|
||
text-decoration: none;
|
||
padding: 10px 16px;
|
||
border-radius: 22px;
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
box-shadow: 0 2px 8px rgba(0,0,0,0.15);
|
||
transition: all 0.2s;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
}
|
||
@media (max-width: 768px) {
|
||
.home-btn { top: 10px; left: 10px; padding: 8px 12px; font-size: 13px; }
|
||
.theme-toggle { top: 10px; right: 10px; width: 38px; height: 38px; font-size: 18px; }
|
||
}
|
||
.home-btn:hover {
|
||
transform: translateX(-2px);
|
||
border-color: var(--accent);
|
||
color: var(--accent);
|
||
}
|
||
.theme-toggle {
|
||
position: fixed;
|
||
top: 16px;
|
||
right: 16px;
|
||
width: 44px; height: 44px;
|
||
border-radius: 50%;
|
||
background: var(--card-bg);
|
||
border: 1px solid var(--card-border);
|
||
color: var(--text);
|
||
font-size: 20px;
|
||
cursor: pointer;
|
||
z-index: 100;
|
||
box-shadow: 0 2px 8px rgba(0,0,0,0.15);
|
||
transition: all 0.2s;
|
||
display: flex; align-items: center; justify-content: center;
|
||
}
|
||
.theme-toggle:hover {
|
||
transform: rotate(15deg) scale(1.1);
|
||
border-color: var(--accent);
|
||
}
|
||
|
||
.category {
|
||
background: var(--card-bg);
|
||
border: 1px solid var(--card-border);
|
||
border-radius: 12px;
|
||
padding: 20px 24px;
|
||
margin-bottom: 16px;
|
||
}
|
||
.category h2 {
|
||
margin: 0 0 16px 0;
|
||
font-size: 20px;
|
||
color: var(--text-strong);
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
font-weight: 600;
|
||
}
|
||
.category h2 .icon {
|
||
font-size: 28px;
|
||
font-family: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", emoji;
|
||
}
|
||
|
||
.checklist { list-style: none; padding: 0; margin: 0; }
|
||
.checklist li {
|
||
padding: 8px 0;
|
||
border-bottom: 1px solid var(--card-border);
|
||
display: flex;
|
||
align-items: flex-start;
|
||
gap: 10px;
|
||
font-size: 15px;
|
||
color: var(--text);
|
||
}
|
||
.checklist li:last-child { border-bottom: none; }
|
||
.checklist input[type="checkbox"] {
|
||
appearance: none;
|
||
width: 18px;
|
||
height: 18px;
|
||
border: 2px solid var(--accent);
|
||
border-radius: 4px;
|
||
flex-shrink: 0;
|
||
margin-top: 2px;
|
||
cursor: pointer;
|
||
position: relative;
|
||
background: transparent;
|
||
}
|
||
.checklist input[type="checkbox"]:checked {
|
||
background: var(--accent);
|
||
border-color: var(--accent);
|
||
}
|
||
.checklist input[type="checkbox"]:checked::after {
|
||
content: "✓";
|
||
color: white;
|
||
position: absolute;
|
||
left: 2px;
|
||
top: -3px;
|
||
font-size: 14px;
|
||
font-weight: bold;
|
||
}
|
||
.checklist li.checked span {
|
||
text-decoration: line-through;
|
||
color: var(--text-muted);
|
||
}
|
||
.checklist li small {
|
||
display: block;
|
||
color: var(--text-muted);
|
||
font-size: 12px;
|
||
margin-top: 2px;
|
||
}
|
||
|
||
.progress-bar {
|
||
background: var(--progress-bg);
|
||
height: 8px;
|
||
border-radius: 4px;
|
||
overflow: hidden;
|
||
margin: 24px 0;
|
||
}
|
||
.progress-bar .fill {
|
||
height: 100%;
|
||
background: var(--accent);
|
||
width: 0%;
|
||
transition: width 0.3s ease;
|
||
}
|
||
.progress-text {
|
||
text-align: center;
|
||
color: var(--text-muted);
|
||
font-size: 13px;
|
||
margin-bottom: 24px;
|
||
}
|
||
|
||
@media print {
|
||
body { background: white; color: black; }
|
||
.category { background: white; border: 1px solid #ccc; }
|
||
.checklist input[type="checkbox"] {
|
||
border-color: #333;
|
||
}
|
||
button { display: none !important; }
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<a href="index.html" class="home-btn" title="Zurück zur Startseite">🏠 Startseite</a>
|
||
<button class="theme-toggle" id="theme-toggle" aria-label="Theme wechseln" title="Light/Dark Mode">🌙</button>
|
||
<div class="container">
|
||
|
||
<a href="index.html" class="back-link">← Zurück zur Übersicht</a>
|
||
|
||
<h1>🎒 JGA Prag Packliste</h1>
|
||
<p class="subtitle">Für 8 Personen · 25.–26. Juli 2026 · Wochenende</p>
|
||
|
||
<div class="progress-text" id="progress-text">0 von 0 erledigt</div>
|
||
<div class="progress-bar"><div class="fill" id="progress-fill"></div></div>
|
||
|
||
<div style="display:flex;gap:12px;justify-content:center;margin-bottom:24px;flex-wrap:wrap;">
|
||
<button type="button" onclick="window.print()" style="padding:10px 20px;background:#3b82f6;color:#fff;border:none;border-radius:6px;font-weight:500;cursor:pointer;font-size:14px;">🖨️ Als PDF drucken</button>
|
||
<button type="button" id="reset-btn" style="padding:10px 20px;background:transparent;color:#aaa;border:1px solid #353539;border-radius:6px;font-weight:500;cursor:pointer;font-size:14px;">↻ Häkchen zurücksetzen</button>
|
||
</div>
|
||
|
||
<div class="category">
|
||
<h2><span class="icon">📄</span>Dokumente & Geld</h2>
|
||
<ul class="checklist">
|
||
<li><input type="checkbox"><span>Personalausweis oder Reisepass<small>(Personalausweis reicht innerhalb EU)</small></span></li>
|
||
<li><input type="checkbox"><span>Krankenversicherungskarte</span></li>
|
||
<li><input type="checkbox"><span>Bargeld in CZK (~2.000 CZK pro Person für Trinkgeld & kleine Ausgaben)</span></li>
|
||
<li><input type="checkbox"><span>EC-Karte / Kreditkarte<small>(Visa/Mastercard fast überall akzeptiert)</small></span></li>
|
||
<li><input type="checkbox"><span>Hotel-Buchungsbestätigung (auf dem Handy)</span></li>
|
||
<li><input type="checkbox"><span>Bordkarte / Zugticket Hinfahrt + Rückfahrt</span></li>
|
||
</ul>
|
||
</div>
|
||
|
||
<div class="category">
|
||
<h2><span class="icon">👕</span>Kleidung</h2>
|
||
<ul class="checklist">
|
||
<li><input type="checkbox"><span>T-Shirts / Hemden (2–3 Stück)</span></li>
|
||
<li><input type="checkbox"><span>Hose / Shorts</span></li>
|
||
<li><input type="checkbox"><span>JGA-Shirt / Outfit<small>(falls ihr eines habt)</small></span></li>
|
||
<li><input type="checkbox"><span>Leichte Jacke / Hoodie<small>(Abende, Geister-Tour)</small></span></li>
|
||
<li><input type="checkbox"><span>Unterwäsche + Socken (2 Tage)</span></li>
|
||
<li><input type="checkbox"><span>Bequeme Schuhe fürs Laufen</span></li>
|
||
<li><input type="checkbox"><span>Schickere Schuhe für Clubs/Restaurants</span></li>
|
||
<li><input type="checkbox"><span>Schwimmzeug (falls Beer Spa)</span></li>
|
||
</ul>
|
||
</div>
|
||
|
||
<div class="category">
|
||
<h2><span class="icon">🧴</span>Pflege & Hygiene</h2>
|
||
<ul class="checklist">
|
||
<li><input type="checkbox"><span>Zahnbürste + Zahnpasta</span></li>
|
||
<li><input type="checkbox"><span>Duschgel / Shampoo</span></li>
|
||
<li><input type="checkbox"><span>Deo</span></li>
|
||
<li><input type="checkbox"><span>Sonnencreme</span></li>
|
||
<li><input type="checkbox"><span>Handtuch (klein, für unterwegs)</span></li>
|
||
<li><input type="checkbox"><span>Kopfschmerztabletten</span></li>
|
||
</ul>
|
||
</div>
|
||
|
||
<div class="category">
|
||
<h2><span class="icon">📱</span>Technik</h2>
|
||
<ul class="checklist">
|
||
<li><input type="checkbox"><span>Handy + Ladekabel</span></li>
|
||
<li><input type="checkbox"><span>Powerbank<small>(Tag wird lang!)</small></span></li>
|
||
<li><input type="checkbox"><span>Kopfhörer</span></li>
|
||
<li><input type="checkbox"><span>EU-Ladeadapter<small>(Prag nutzt Typ E, wie in DE/AT)</small></span></li>
|
||
<li><input type="checkbox"><span>Kamera (optional)</span></li>
|
||
</ul>
|
||
</div>
|
||
|
||
<div class="category">
|
||
<h2><span class="icon">🚇</span>Prag-spezifisch</h2>
|
||
<ul class="checklist">
|
||
<li><input type="checkbox"><span>Google Translate (Offline-Pack Tschechisch installieren!)</span></li>
|
||
<li><input type="checkbox"><span>Bolt-App installieren<small>(Uber gibt's nicht mehr in Prag!)</small></span></li>
|
||
<li><input type="checkbox"><span>Google Maps Offline-Karte Prag laden</span></li>
|
||
<li><input type="checkbox"><span>PID Lítačka App (für Tram-Tickets)<small>(oder Bargeld im Tram)</small></span></li>
|
||
<li><input type="checkbox"><span>Notfall-Nummer: 112 (EU-Notruf)</span></li>
|
||
<li><input type="checkbox"><span>Kleiner tschechischer Sprachführer (Pivo, Děkuji, Účet prosím)</span></li>
|
||
</ul>
|
||
</div>
|
||
|
||
<div class="category">
|
||
<h2><span class="icon">🎯</span>JGA-Spezial (nach Aktivität)</h2>
|
||
<ul class="checklist">
|
||
<li><input type="checkbox"><span>🎯 Für Schießstand: gültiger Ausweis (wird kontrolliert)</span></li>
|
||
<li><input type="checkbox"><span>⛳ Für Minigolf: nichts spezielles</span></li>
|
||
<li><input type="checkbox"><span>🔫 Für Lasertag: nichts spezielles</span></li>
|
||
<li><input type="checkbox"><span>🎨 Für Paintball: alte Klamotten (es wird schmutzig!)</span></li>
|
||
<li><input type="checkbox"><span>🏎️ Für Kart: lange Hose + feste Schuhe</span></li>
|
||
<li><input type="checkbox"><span>🔐 Für Escape Room: pünktlich da sein (sonst verpasst ihr eure Startzeit)</span></li>
|
||
<li><input type="checkbox"><span>👻 Für Geister-Tour: bequeme Schuhe, 1,75 h zu Fuß</span></li>
|
||
<li><input type="checkbox"><span>⛵ Für Beer Boat: nichts spezielles (Bier ist dabei 🍺)</span></li>
|
||
<li><input type="checkbox"><span>🔍 Für Enigma: Smartphone voll geladen</span></li>
|
||
</ul>
|
||
</div>
|
||
|
||
<div class="category">
|
||
<h2><span class="icon">🍺</span>Verpflegung</h2>
|
||
<ul class="checklist">
|
||
<li><input type="checkbox"><span>Wasserflasche (für unterwegs)</span></li>
|
||
<li><input type="checkbox"><span>Knabberzeug für den Zug</span></li>
|
||
<li><input type="checkbox"><span>Kaugummis / Pfefferminz<small>(für zwischendurch)</small></span></li>
|
||
</ul>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<script>
|
||
const STORAGE_KEY = 'jga-packliste-checked';
|
||
let checked = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]');
|
||
|
||
const checkboxes = document.querySelectorAll('.checklist input[type="checkbox"]');
|
||
const progressText = document.getElementById('progress-text');
|
||
const progressFill = document.getElementById('progress-fill');
|
||
|
||
checkboxes.forEach((cb, i) => {
|
||
if (checked[i]) {
|
||
cb.checked = true;
|
||
cb.parentElement.classList.add('checked');
|
||
}
|
||
cb.dataset.index = i;
|
||
});
|
||
|
||
function updateProgress() {
|
||
const total = checkboxes.length;
|
||
const done = Array.from(checkboxes).filter(cb => cb.checked).length;
|
||
const pct = (done / total * 100).toFixed(0);
|
||
progressFill.style.width = pct + '%';
|
||
progressText.textContent = `${done} von ${total} erledigt (${pct}%)`;
|
||
checked = Array.from(checkboxes).map(cb => cb.checked);
|
||
localStorage.setItem(STORAGE_KEY, JSON.stringify(checked));
|
||
}
|
||
|
||
checkboxes.forEach(cb => {
|
||
cb.addEventListener('change', () => {
|
||
cb.parentElement.classList.toggle('checked', cb.checked);
|
||
updateProgress();
|
||
});
|
||
});
|
||
|
||
document.getElementById('reset-btn').addEventListener('click', () => {
|
||
if (!confirm('Alle Häkchen wirklich zurücksetzen?')) return;
|
||
checkboxes.forEach(cb => {
|
||
cb.checked = false;
|
||
cb.parentElement.classList.remove('checked');
|
||
});
|
||
updateProgress();
|
||
});
|
||
|
||
// Theme-Toggle
|
||
const themeBtn = document.getElementById('theme-toggle');
|
||
function updateThemeIcon() {
|
||
const cur = document.documentElement.getAttribute('data-theme') || 'dark';
|
||
themeBtn.textContent = cur === 'dark' ? '☀️' : '🌙';
|
||
themeBtn.title = cur === 'dark' ? 'Light Mode aktivieren' : 'Dark Mode aktivieren';
|
||
}
|
||
updateThemeIcon();
|
||
themeBtn.addEventListener('click', () => {
|
||
const cur = document.documentElement.getAttribute('data-theme') || 'dark';
|
||
const next = cur === 'dark' ? 'light' : 'dark';
|
||
document.documentElement.setAttribute('data-theme', next);
|
||
localStorage.setItem('jga-theme', next);
|
||
updateThemeIcon();
|
||
});
|
||
|
||
updateProgress();
|
||
</script>
|
||
<script>
|
||
// PWA Service Worker — Network-First Strategie (Updates kommen sofort durch)
|
||
if ('serviceWorker' in navigator) {
|
||
window.addEventListener('load', () => {
|
||
navigator.serviceWorker.register('./sw.js')
|
||
.then(reg => {
|
||
console.log('[PWA] SW registered:', reg.scope);
|
||
// Update-Hook: wenn ein neuer SW installiert wurde, kurz reloaden
|
||
reg.addEventListener('updatefound', () => {
|
||
const newSw = reg.installing;
|
||
if (!newSw) return;
|
||
newSw.addEventListener('statechange', () => {
|
||
if (newSw.state === 'activated' && navigator.serviceWorker.controller) {
|
||
console.log('[PWA] Neue Version aktiviert — lade automatisch neu');
|
||
window.location.reload();
|
||
}
|
||
});
|
||
});
|
||
})
|
||
.catch(err => console.warn('[PWA] SW registration failed:', err));
|
||
});
|
||
}
|
||
</script>
|
||
</body>
|
||
</html>
|