Files
JGA-2026/countdown.html
Hermes Agent 98baff599b Switch hotel from Hotel Uno Prague → Iris Hotel Eden
Komplett-Ersatz in 6 Files:
- index.html (Hero-Sub, Trip-Pill Basis, Trip-Pill Tram, Hotel-Card, URL)
- tourenplan.html (Header-Sub, Hotel-Definition, Map.setView, Label,
  Layer-Legende, restaurantKeyFor-Key, RESTAURANTS-Key)
- tourenplan-mit-tram.html (alles + Tram-Station Strašnická →
  Kubánské náměstí + Na Hroudě bleibt als Tram-Stop aber 'near' updated)
- countdown.html (Sub-Title)
- memory.html (Hotel-Eintrag in Locations-Array)
- README.md (Tabelle + Tram-Linien-Liste)

Hotel-Daten (verifiziert via OSM):
- Name: Iris Hotel Eden (Czech Leading Hotels)
- Adresse: Vladivostocká 1539/2, Praha 10 – Vršovice
- GPS: 50.0682815, 14.4716891
- Website: https://www.irishoteleden.cz
- Tram: Slavia – Nádraží Eden (3 Min Fußweg, Linien 6, 7, 22, 95, 97)

NICHT geändert (echte Restaurant-/Haltestellen-Namen):
- 'Pizza Coloseum Strašnice' (Restaurant liegt real dort)
- 'Strašnická' als U-Bahn-Station (existiert real, wird evtl.
  in Tourenplan-mit-tram noch referenziert — als Tram-Stop beibehalten)
- 'Na Hroudě' Tram-Haltestelle bleibt im Tram-Netz

Manuelle Verifikation nötig:
- Tram-Beschreibungen in tourenplan-mit-tram.html (Na Hroudě entfernt
  aus Hotel-Kontext, bleibt aber als Tram-Haltestelle)
- RestaurantKeyFor-Mapping: hotel → 'Iris Hotel Eden (Vršovice)'
2026-07-25 13:04:45 +00:00

423 lines
14 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JGA Prag 2026 — Countdown</title>
<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>">
<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() {
var saved = localStorage.getItem('jga-theme');
document.documentElement.setAttribute('data-theme', saved || 'dark');
})();
</script>
<style>
:root, [data-theme="dark"] {
--bg: #1c1c1f; --card-bg: #26262a; --card-border: #353539;
--text: #e8e8ea; --text-strong: #fff; --text-muted: #999;
--accent: #3b82f6; --accent-text: #60a5fa;
--gradient-start: #1c1c1f; --gradient-end: #2a2a30;
}
[data-theme="light"] {
--bg: #f5f5f7; --card-bg: #ffffff; --card-border: #e0e0e3;
--text: #1a1a1a; --text-strong: #000; --text-muted: #666;
--accent: #2563eb; --accent-text: #2563eb;
--gradient-start: #ffffff; --gradient-end: #e0e7ff;
}
* { 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;
display: flex; align-items: center; justify-content: center;
}
.container {
text-align: center;
max-width: 700px;
padding: 40px 24px;
width: 100%;
}
.emoji-big {
font-size: 100px;
line-height: 1;
margin-bottom: 16px;
animation: bounce 2s ease-in-out infinite;
}
@keyframes bounce {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-15px); }
}
h1 {
font-size: 38px;
margin: 0 0 8px 0;
color: var(--text-strong);
font-weight: 700;
}
.subtitle {
color: var(--text-muted);
font-size: 18px;
margin: 0 0 40px 0;
}
.countdown-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 16px;
margin-bottom: 32px;
}
.countdown-cell {
background: linear-gradient(135deg, var(--gradient-start) 0%, var(--gradient-end) 100%);
border: 1px solid var(--card-border);
border-radius: 16px;
padding: 24px 12px;
display: flex;
flex-direction: column;
align-items: center;
box-shadow: 0 4px 12px rgba(0,0,0,0.2);
}
.countdown-num {
font-size: 64px;
font-weight: 800;
color: var(--accent-text);
line-height: 1;
font-variant-numeric: tabular-nums;
}
.countdown-label {
font-size: 12px;
color: var(--text-muted);
text-transform: uppercase;
letter-spacing: 1.5px;
margin-top: 8px;
font-weight: 600;
}
.countdown-message {
font-size: 22px;
color: var(--text);
margin: 24px 0;
line-height: 1.5;
}
.countdown-message.celebrating {
font-size: 32px;
color: var(--accent-text);
font-weight: 700;
}
.progress-bar {
background: var(--card-border);
height: 12px;
border-radius: 6px;
overflow: hidden;
margin: 32px 0 16px 0;
}
.progress-bar .fill {
height: 100%;
background: linear-gradient(90deg, var(--accent) 0%, #06b6d4 100%);
width: 0%;
transition: width 1s ease;
border-radius: 6px;
}
.progress-text {
font-size: 14px;
color: var(--text-muted);
margin-top: 8px;
}
.info-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: 12px;
margin-top: 32px;
}
.info-item {
background: var(--card-bg);
border: 1px solid var(--card-border);
border-left: 3px solid var(--accent);
padding: 12px 16px;
border-radius: 8px;
text-align: left;
}
.info-item .label {
font-size: 11px;
color: var(--text-muted);
text-transform: uppercase;
letter-spacing: 1px;
margin-bottom: 4px;
}
.info-item .value {
font-size: 15px;
color: var(--text-strong);
font-weight: 600;
}
.share-btn {
display: inline-block;
padding: 12px 28px;
background: var(--accent);
color: #fff;
border: none;
border-radius: 8px;
font-size: 15px;
font-weight: 600;
cursor: pointer;
margin-top: 24px;
text-decoration: none;
transition: transform 0.2s, background 0.2s;
}
.share-btn:hover {
transform: translateY(-2px);
}
.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);
}
.back-link {
display: inline-block;
margin-bottom: 16px;
color: var(--accent-text);
text-decoration: none;
font-size: 14px;
}
.back-link:hover { text-decoration: underline; }
@media (max-width: 480px) {
.countdown-num { font-size: 40px; }
h1 { font-size: 28px; }
}
</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>
<div class="emoji-big" id="emoji">🥳</div>
<h1 id="title">JGA Prag 2026</h1>
<p class="subtitle" id="subtitle">Countdown bis zum Junggesellenabschied</p>
<div class="countdown-grid">
<div class="countdown-cell">
<div class="countdown-num" id="days">--</div>
<div class="countdown-label">Tage</div>
</div>
<div class="countdown-cell">
<div class="countdown-num" id="hours">--</div>
<div class="countdown-label">Stunden</div>
</div>
<div class="countdown-cell">
<div class="countdown-num" id="minutes">--</div>
<div class="countdown-label">Minuten</div>
</div>
<div class="countdown-cell">
<div class="countdown-num" id="seconds">--</div>
<div class="countdown-label">Sekunden</div>
</div>
</div>
<div class="countdown-message" id="message">Noch so lange bis zur wildesten Party des Jahres!</div>
<div class="progress-bar"><div class="fill" id="progress-fill"></div></div>
<div class="progress-text" id="progress-text">Vorbereitung: 0%</div>
<div class="info-grid">
<div class="info-item">
<div class="label">📅 Datum</div>
<div class="value">25.26. Juli 2026</div>
</div>
<div class="info-item">
<div class="label">👥 Gruppe</div>
<div class="value">8 Personen</div>
</div>
<div class="info-item">
<div class="label">🏨 Hotel</div>
<div class="value">Iris Hotel Eden</div>
</div>
<div class="info-item">
<div class="label">🎯 Aktivitäten</div>
<div class="value">11 Locations</div>
</div>
</div>
<button class="share-btn" id="share-btn">📤 Teilen</button>
</div>
<script>
// JGA-Daten: 25. Juli 2026, Start 12:00 Uhr Berlin (Annahme)
const JGA_START = new Date('2026-07-25T12:00:00+02:00').getTime();
// Planungsstart: heute minus 30 Tage = ca. 1. Juni 2026
const PLANNING_START = new Date('2026-06-01T00:00:00+02:00').getTime();
function update() {
const now = Date.now();
const diff = JGA_START - now;
if (diff <= 0) {
// JGA läuft oder vorbei
document.getElementById('days').textContent = '🎉';
document.getElementById('hours').textContent = '🎉';
document.getElementById('minutes').textContent = '🎉';
document.getElementById('seconds').textContent = '🎉';
document.getElementById('message').classList.add('celebrating');
const jgaEnd = new Date('2026-07-26T23:59:00+02:00').getTime();
if (now > jgaEnd) {
document.getElementById('message').innerHTML = '🥲 Der JGA ist vorbei — was für ein Wochenende!';
document.getElementById('emoji').textContent = '🥲';
document.getElementById('title').textContent = 'Erinnerungen';
} else {
document.getElementById('message').innerHTML = '🎉 Der JGA LÄUFT! Genießt jeden Moment! 🎉';
document.getElementById('emoji').textContent = '🥳';
document.getElementById('title').textContent = 'Es ist soweit!';
}
document.getElementById('progress-fill').style.width = '100%';
document.getElementById('progress-text').textContent = 'Planung abgeschlossen';
return;
}
const days = Math.floor(diff / (1000 * 60 * 60 * 24));
const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
const seconds = Math.floor((diff % (1000 * 60)) / 1000);
document.getElementById('days').textContent = days;
document.getElementById('hours').textContent = String(hours).padStart(2, '0');
document.getElementById('minutes').textContent = String(minutes).padStart(2, '0');
document.getElementById('seconds').textContent = String(seconds).padStart(2, '0');
// Progress Bar
const total = JGA_START - PLANNING_START;
const elapsed = now - PLANNING_START;
const pct = Math.min(100, Math.max(0, (elapsed / total) * 100));
document.getElementById('progress-fill').style.width = pct.toFixed(1) + '%';
document.getElementById('progress-text').textContent = `Vorbereitung: ${pct.toFixed(1)}%`;
// Kontextabhängige Nachricht
let msg = 'Noch so lange bis zur wildesten Party des Jahres!';
let emoji = '🥳';
if (days === 0) {
msg = '🔥 Heute ist der Tag! Auf geht\'s nach Prag!';
emoji = '🔥';
} else if (days === 1) {
msg = '😱 MORGEN ist es soweit! Letzte Vorbereitungen!';
emoji = '😱';
} else if (days <= 7) {
msg = '⚡ Nur noch ' + days + ' Tage! Packlisten checken!';
emoji = '⚡';
} else if (days <= 30) {
msg = '📦 Bald geht\'s los! Restliche Buchungen klären.';
emoji = '📦';
} else if (days <= 90) {
msg = '🗓️ ' + days + ' Tage — genug Zeit für Feinschliff.';
emoji = '🗓️';
}
document.getElementById('message').textContent = msg;
document.getElementById('emoji').textContent = emoji;
}
update();
setInterval(update, 1000);
// Theme-Toggle
var themeBtn = document.getElementById('theme-toggle');
function syncThemeIcon() {
var t = document.documentElement.getAttribute('data-theme') || 'dark';
themeBtn.textContent = t === 'dark' ? '☀️' : '🌙';
themeBtn.title = t === 'dark' ? 'Light Mode aktivieren' : 'Dark Mode aktivieren';
}
syncThemeIcon();
themeBtn.addEventListener('click', function() {
var t = document.documentElement.getAttribute('data-theme') || 'dark';
var next = t === 'dark' ? 'light' : 'dark';
document.documentElement.setAttribute('data-theme', next);
try { localStorage.setItem('jga-theme', next); } catch (e) {}
syncThemeIcon();
});
// Share-Button
document.getElementById('share-btn').addEventListener('click', async function() {
var days = document.getElementById('days').textContent;
var shareText = '🥳 JGA Prag 2026 — noch ' + days + ' Tage! 25.26. Juli 2026 · jga.orfel.de';
if (navigator.share) {
try {
await navigator.share({ title: 'JGA Prag 2026', text: shareText, url: window.location.href });
} catch (e) { /* user cancelled */ }
} else if (navigator.clipboard) {
try {
await navigator.clipboard.writeText(shareText);
this.textContent = '✅ Kopiert!';
setTimeout(() => { this.textContent = '📤 Teilen'; }, 2000);
} catch (e) { /* clipboard failed */ }
} else {
prompt('Zum Kopieren:', shareText);
}
});
// PWA Service Worker — Network-First Strategie (Updates kommen sofort durch)
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('./sw.js')
.then(function(reg) {
console.log('[PWA] SW registered:', reg.scope);
reg.addEventListener('updatefound', function() {
var newSw = reg.installing;
if (!newSw) return;
newSw.addEventListener('statechange', function() {
if (newSw.state === 'activated' && navigator.serviceWorker.controller) {
console.log('[PWA] Neue Version aktiviert — lade automatisch neu');
window.location.reload();
}
});
});
})
.catch(function(err) { console.warn('[PWA] SW failed:', err); });
});
}
</script>
</body>
</html>