diff --git a/index.html b/index.html
index 28ebd5d..bf55e6a 100644
--- a/index.html
+++ b/index.html
@@ -24,43 +24,43 @@
LIVE · VBB
-
Was fährt jetzt?
-
Suche eine Haltestelle oder nutze deinen Standort, um die nächsten Abfahrten zu sehen.
+
Was fährt jetzt?
+
Suche eine Haltestelle oder nutze deinen Standort, um die nächsten Abfahrten zu sehen.
-
OPTIONAL
Haltestellen in der Nähe
-
+
OPTIONAL
Haltestellen in der Nähe
+
@@ -68,152 +68,160 @@
-
Aktualisierung alle 30 Sekunden
+
Aktualisierung alle 30 Sekunden
-
Suche oben eine Haltestelle, um Live-Abfahrten zu sehen.
+
Suche oben eine Haltestelle, um Live-Abfahrten zu sehen.
- LIVEKarte
Fahrzeuge, Routen und deine aktuelle Fahrt live verfolgen.
+ LIVEKarte
Fahrzeuge, Routen und deine aktuelle Fahrt live verfolgen.
-
+
- Verfolge Linie live
-
+ Verfolge Linie live
+
- VERBINDUNG SUCHENWohin möchtest du?
Starte mit deinem Standort oder suche einen Ausgangspunkt.
+ VERBINDUNG SUCHENWohin möchtest du?
Starte mit deinem Standort oder suche einen Ausgangspunkt.
-
-
-
+
+
+
-
+
-
+
- 🛰 Routenplanung über unsere selbst gehostete, offene Instanz (VBB-Region)
+ 🛰 Routenplanung über unsere selbst gehostete, offene Instanz (VBB-Region)
-
+
- STANDORTIn der Nähe
Finde Haltestellen rund um deinen aktuellen Standort.
-
- Nutze deinen Standort, um Haltestellen in deiner Nähe zu finden.
+ STANDORTIn der Nähe
Finde Haltestellen rund um deinen aktuellen Standort.
+
+ Nutze deinen Standort, um Haltestellen in deiner Nähe zu finden.
-
SCHNELLZUGRIFFMeine Haltestellen
Deine gespeicherten Haltestellen mit Live-Abfahrten und Störungshinweisen.
+
SCHNELLZUGRIFFMeine Haltestellen
Deine gespeicherten Haltestellen mit Live-Abfahrten und Störungshinweisen.
-
+
-
Noch keine Favoriten - tippe bei einer Haltestelle auf ☆, um sie hier anzupinnen.
+
Noch keine Favoriten - tippe bei einer Haltestelle auf ☆, um sie hier anzupinnen.
- AKTUELLStörungen
Ortsbezogene und netzweite Hinweise.
+ AKTUELLStörungen
Ortsbezogene und netzweite Hinweise.
-
-
-
+
+
+
- PREISETickets
Übliche VBB-Fahrpreise auf einen Blick.
- ⚠ Angaben ohne Gewähr – Preise können sich ändern.
-
-
+ PREISETickets
Übliche VBB-Fahrpreise auf einen Blick.
+ ⚠ Angaben ohne Gewähr – Preise können sich ändern.
+
+
- WEITERE FUNKTIONENMehr
Alles, was nicht bei jeder Fahrt gebraucht wird.
+ WEITERE FUNKTIONENMehr
Alles, was nicht bei jeder Fahrt gebraucht wird.
-
-
-
-
+
+
+
+
-
VERLAUF
Zuletzt gesucht
+
VERLAUF
Zuletzt gesucht
+
+
SPRACHE / LANGUAGE
Sprache
+
+
+
+
+
- Noch keine Suchen.
';
+ container.innerHTML = `Keine Abfahrten für die gewählten Verkehrsmittel in den nächsten 30 Minuten.
';
+ board.innerHTML = `Bitte Start und Ziel aus der Liste auswählen.
';
+ resultsEl.innerHTML = `Keine Verbindungen gefunden.
';
+ container.innerHTML = `
Lade Fahrt-Details…
@@ -75,6 +76,33 @@ const TripModal = (() => {
return new Date(iso).toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' });
}
+ // Verspätungsstatistik pro Linie - Datenbasis wächst erst über Zeit, auf
+ // einem frisch aufgesetzten Server gibt es also erstmal nichts anzuzeigen.
+ async function loadLineStats(line) {
+ const el = document.getElementById('trip-modal-stats');
+ if (!el) return;
+ el.hidden = true;
+ if (!line) return;
+
+ try {
+ const stats = await API.getLineDelayStats(line);
+ if (!stats || !stats.sufficientData) return; // still hidden - keine Aussage besser als eine unsichere
+
+ const avgMin = Math.round(stats.avgDelaySeconds / 60);
+ const parts = [];
+ parts.push(avgMin > 0 ? `Ø +${avgMin} Min` : 'Ø pünktlich');
+ parts.push(`${stats.pctDelayed5plus}% ≥5 Min verspätet`);
+ if (stats.pctCancelled > 0) parts.push(`${stats.pctCancelled}% Ausfälle`);
+
+ el.textContent = `📊 ${parts.join(' · ')} (${stats.sampleCount} Fahrten, ${stats.days} Tage)`;
+ el.hidden = false;
+ } catch (err) {
+ // Statistik ist ein "nice to have" - bei Fehlern einfach ausblenden,
+ // ohne die eigentliche Fahrt-Detail-Ansicht zu stören.
+ console.error('Verspätungsstatistik-Fehler:', err.message);
+ }
+ }
+
function getAlarmOffset() {
return parseInt(localStorage.getItem(ALARM_OFFSET_KEY), 10) || 1;
}
@@ -183,9 +211,11 @@ const TripModal = (() => {
currentTripId = tripId;
currentTrip = null;
+ const statsEl = document.getElementById('trip-modal-stats');
+ if (statsEl) { statsEl.hidden = true; }
+
document.getElementById('trip-modal-line').textContent = '…';
- document.getElementById('trip-modal-line').style.background = '#8E99A6';
- document.getElementById('trip-modal-direction').textContent = '';
+ document.getElementById('trip-modal-line').style.background = '#8E99A6'; document.getElementById('trip-modal-direction').textContent = '';
document.getElementById('trip-modal-meta').textContent = '';
document.getElementById('trip-modal-body').innerHTML = '
Lade Fahrt-Details…
';
@@ -209,6 +239,8 @@ const TripModal = (() => {
? 'Fahrt fällt aus'
: `${trip.origin || ''} → ${trip.destination || ''}`;
+ loadLineStats(trip.line);
+
// Mini-Karte mit dem Streckenverlauf zeichnen
if (miniMap) { miniMap.remove(); miniMap = null; }
if (trip.polyline) {
diff --git a/styles.css b/styles.css
index b1ee731..70819d5 100644
--- a/styles.css
+++ b/styles.css
@@ -866,7 +866,7 @@ button:active { transform: translateY(1px); }
border-bottom: 1px solid var(--line);
}
.tab-button { padding: 14px 18px; display: inline-flex; gap: 8px; align-items: center; }
-.tab-button span { filter: grayscale(.35); }
+.tab-button__icon { filter: grayscale(.35); }
.app-main { max-width: 920px; padding: 30px 20px 50px; }
@@ -1072,6 +1072,14 @@ button:active { transform: translateY(1px); }
.recent-item__remove:hover { background: var(--panel-raised); color: var(--signal-red); }
.recent-empty { color: var(--ink-muted); font-size: 12px; padding: 8px 0; }
+.lang-switch { display: flex; gap: 8px; }
+.lang-switch__btn {
+ background: var(--panel); color: var(--ink-muted); border: 1px solid var(--line);
+ border-radius: 999px; padding: 8px 16px; font-size: 13px;
+}
+.lang-switch__btn:hover { border-color: var(--signal-amber); }
+.lang-switch__btn.is-active { color: var(--signal-amber); border-color: var(--signal-amber); background: rgba(245,166,35,.08); }
+
.search-result-heading {
padding: 8px 12px 4px;
color: var(--ink-muted);
@@ -1174,6 +1182,14 @@ button:active { transform: translateY(1px); }
border-color: var(--signal-amber);
}
+.trip-modal__stats {
+ padding: 8px 18px;
+ font-size: 12px;
+ color: var(--ink-muted);
+ background: var(--panel-raised);
+ border-bottom: 1px solid var(--line);
+}
+
.trip-modal__map {
height: 220px;
border-bottom: 1px solid var(--line);