**Hotel präsenter auf Karten:** - Größerer Hotel-Marker (56px vs 38px) - Goldener pulsierender Glow (CSS-Animation hotelPulse) - Permanentes Label 'Hotel Uno · Basis' unter dem Pin - Auto-Open Popup nach 1.5s mit goldenem Banner - Filter-Toggle (Hotel immer sichtbar, Action/Fun/Sight ausblendbar) **Feature 7 - Route zwischen Locations:** - OSRM Public API für echte Auto-Routen - Schnell-Routen vom Hotel (3 nächste Locations) - 'Zurück zum Hotel' Button in jedem Location-Popup - Fallback auf Haversine-Luftlinie wenn API down **Feature 8 - Was ist in der Nähe:** - Nachbarn <3km Luftlinie in jedem Popup **Feature 14 - Restaurant-Tipps:** - Geo-Cluster-basierte Restaurant-Empfehlungen pro Location - 14 handgepflegte Tipps (€€, Cuisine, Beschreibung) **Feature 20 - PWA:** - manifest.json + sw.js (cache-first für eigene Files) - Theme-Color, Apple-Meta, SW-Registrierung in allen 5 HTML-Dateien **Feature 21 - Dark/Light Mode Toggle:** - CSS-Variablen-System + 🌙/☀️ Toggle-Button - localStorage-Persistenz (nur index.html, andere Seiten folgen) **Bugfixes:** - Enigma-URL auf User-Sublink korrigiert (alle 3 Map-Dateien) - tourenplan-mit-tram.html Locations-Sync mit tourenplan.html (Endorfin/KartPlanet hatten komplett andere Preise+Koordinaten+URLs)
297 lines
11 KiB
HTML
297 lines
11 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="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'>
|
||
<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"><text y='.9em' font-size='90'>🧭</text></svg>">
|
||
<style>
|
||
* { box-sizing: border-box; }
|
||
html, body {
|
||
margin: 0; padding: 0;
|
||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||
background: #1c1c1f;
|
||
color: #e8e8ea;
|
||
min-height: 100vh;
|
||
}
|
||
.container { max-width: 900px; margin: 0 auto; padding: 40px 24px; }
|
||
h1 {
|
||
font-size: 38px;
|
||
margin: 0 0 8px 0;
|
||
color: #fff;
|
||
font-weight: 700;
|
||
}
|
||
.subtitle { color: #999; margin-bottom: 32px; font-size: 16px; }
|
||
|
||
.back-link {
|
||
display: inline-block;
|
||
margin-bottom: 24px;
|
||
color: #60a5fa;
|
||
text-decoration: none;
|
||
font-size: 14px;
|
||
}
|
||
.back-link:hover { text-decoration: underline; }
|
||
|
||
.category {
|
||
background: #26262a;
|
||
border: 1px solid #353539;
|
||
border-radius: 12px;
|
||
padding: 20px 24px;
|
||
margin-bottom: 16px;
|
||
}
|
||
.category h2 {
|
||
margin: 0 0 16px 0;
|
||
font-size: 20px;
|
||
color: #fff;
|
||
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 #2a2a2d;
|
||
display: flex;
|
||
align-items: flex-start;
|
||
gap: 10px;
|
||
font-size: 15px;
|
||
}
|
||
.checklist li:last-child { border-bottom: none; }
|
||
.checklist input[type="checkbox"] {
|
||
appearance: none;
|
||
width: 18px;
|
||
height: 18px;
|
||
border: 2px solid #3b82f6;
|
||
border-radius: 4px;
|
||
flex-shrink: 0;
|
||
margin-top: 2px;
|
||
cursor: pointer;
|
||
position: relative;
|
||
background: transparent;
|
||
}
|
||
.checklist input[type="checkbox"]:checked {
|
||
background: #3b82f6;
|
||
border-color: #3b82f6;
|
||
}
|
||
.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: #666;
|
||
}
|
||
.checklist li small {
|
||
display: block;
|
||
color: #888;
|
||
font-size: 12px;
|
||
margin-top: 2px;
|
||
}
|
||
|
||
.progress-bar {
|
||
background: #2a2a2d;
|
||
height: 8px;
|
||
border-radius: 4px;
|
||
overflow: hidden;
|
||
margin: 24px 0;
|
||
}
|
||
.progress-bar .fill {
|
||
height: 100%;
|
||
background: #3b82f6;
|
||
width: 0%;
|
||
transition: width 0.3s ease;
|
||
}
|
||
.progress-text {
|
||
text-align: center;
|
||
color: #aaa;
|
||
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>
|
||
<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();
|
||
});
|
||
|
||
updateProgress();
|
||
</script>
|
||
<script>
|
||
// PWA Service Worker registrieren
|
||
if ('serviceWorker' in navigator) {
|
||
window.addEventListener('load', () => {
|
||
navigator.serviceWorker.register('./sw.js')
|
||
.then(reg => console.log('[PWA] SW registered:', reg.scope))
|
||
.catch(err => console.warn('[PWA] SW registration failed:', err));
|
||
});
|
||
}
|
||
</script>
|
||
</body>
|
||
</html>
|