Files
Live_Navi/index.html
T
Claude 4da99c913c Navigation an iOS-Tabs angleichen, Benachrichtigungscenter, Einfacher Modus
Phase 2-4 der WebApp-Angleichung an die iOS-App:

- Navigation: Favoriten und Störungen sind jetzt eigene Top-Level-Tabs
  (Desktop-Tableiste + mobile Bottom-Nav) statt nur über das Mehr-Grid bzw.
  den Stern im Header erreichbar. Neue Header-Icons für Einstellungen (⚙)
  und Benachrichtigungen (🔔, mit rotem Punkt bei ungelesenen Einträgen).
  "Mehr" ist jetzt schlanker (In der Nähe, Tickets, Verlauf, App-Info mit
  Verbindungstest gegen /health/full) und die Sprachauswahl in einen neuen
  eigenen "Einstellungen"-Tab umgezogen.

- Benachrichtigungscenter (js/notifications.js, neu): persistenter
  Verlauf über IndexedDB, sowohl vom Service Worker (eingehende Web-Push-
  Nachrichten, auch bei geschlossenem Tab) als auch von der Seite selbst
  (Störungs-Matches bei Favoriten, Fahrtalarm) beschrieben. Neuer
  "Benachrichtigungen"-Tab zeigt den Verlauf, Badge am Glocken-Icon zeigt
  ungelesene Einträge, per postMessage vom Service Worker live aktualisiert.

- Einfacher Modus: neuer Toggle im Einstellungen-Tab, persistiert in
  localStorage, setzt eine .simple-mode-Klasse auf <html> - moderat
  größere Schrift plus Ausblenden von Verkehrsmittel-Filter/Fahrrad-Option/
  "Zwischenhalt hinzufügen" im Routenplaner, analog zur iOS-App (bewusst
  keine Accessibility-Schriftgrößenstufe, die ist für Screenreader gedacht).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01De7H8hR1Q9DdgyuS2ukN6y
2026-09-28 11:58:15 +02:00

307 lines
25 KiB
HTML
Raw 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, maximum-scale=1.0, user-scalable=no" />
<title>ÖPNV Navi – Live-Abfahrten für Berlin &amp; Brandenburg</title>
<meta name="description" content="Live-Abfahrten, Live-Karte und Routenplaner für den VBB-Verbund." />
<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="ÖPNV Navi" />
<meta name="theme-color" content="#0C1116" />
<link rel="manifest" href="manifest.json" />
<link rel="icon" href="icons/favicon.ico" sizes="any" />
<link rel="icon" type="image/png" sizes="32x32" href="icons/favicon-32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="icons/favicon-16.png" />
<link rel="apple-touch-icon" href="icons/icon-180.png" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@500;700&family=IBM+Plex+Sans:wght@400;500;600&family=IBM+Plex+Sans+Condensed:wght@600&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<a class="skip-link" href="#main-content" data-i18n="a11y.skipLink">Zum Inhalt springen</a>
<header class="app-header">
<button class="brand" data-tab="departures" aria-label="Zur Startseite">
<span class="brand__mark">ÖPNV</span><span class="brand__mark brand__mark--accent">NAVI</span>
</button>
<div class="search-box search-box--global">
<span class="search-box__icon" aria-hidden="true">⌕</span>
<input type="text" id="station-search" placeholder="Haltestelle, Ort oder Ziel suchen…" data-i18n-placeholder="search.placeholder" autocomplete="off" role="combobox" aria-expanded="false" aria-controls="search-results" aria-autocomplete="list" aria-label="Haltestelle, Ort oder Ziel suchen" data-i18n-aria-label="search.placeholder" />
<button id="search-clear" class="search-clear" type="button" aria-label="Suche leeren" data-i18n-aria-label="search.clear" hidden>×</button>
<ul id="search-results" class="search-results" role="listbox" aria-label="Suchergebnisse" data-i18n-aria-label="search.resultsLabel" hidden></ul>
</div>
<button id="header-settings-btn" class="header-icon-btn" data-tab="settings" type="button" title="Einstellungen" data-i18n-title="nav.settings" aria-label="Einstellungen" data-i18n-aria-label="nav.settings">⚙</button>
<button id="header-notifications-btn" class="header-icon-btn" data-tab="notifications" type="button" title="Benachrichtigungen" data-i18n-title="nav.notifications" aria-label="Benachrichtigungen" data-i18n-aria-label="nav.notifications">
🔔<span id="notifications-badge" class="notifications-badge" hidden></span>
</button>
<button id="header-favorites-btn" class="header-icon-btn" data-tab="favorites" type="button" title="Favoriten" data-i18n-title="nav.favorites" aria-label="Favoriten" data-i18n-aria-label="nav.favorites">★</button>
</header>
<nav class="tab-nav" aria-label="Hauptnavigation">
<div role="tablist" aria-label="Hauptnavigation" class="tab-nav__list">
<button class="tab-button is-active" role="tab" aria-selected="true" aria-controls="panel-departures" id="tab-departures" tabindex="0" data-tab="departures"><span class="tab-button__icon" aria-hidden="true">🚏</span> <span data-i18n="nav.departures">Abfahrten</span></button>
<button class="tab-button" role="tab" aria-selected="false" aria-controls="panel-favorites" id="tab-favorites" tabindex="-1" data-tab="favorites"><span class="tab-button__icon" aria-hidden="true">★</span> <span data-i18n="favorites.title">Favoriten</span></button>
<button class="tab-button" role="tab" aria-selected="false" aria-controls="panel-journey" id="tab-journey" tabindex="-1" data-tab="journey"><span class="tab-button__icon" aria-hidden="true">🧭</span> <span data-i18n="nav.route">Route</span></button>
<button class="tab-button" role="tab" aria-selected="false" aria-controls="panel-map" id="tab-map" tabindex="-1" data-tab="map"><span class="tab-button__icon" aria-hidden="true">🗺</span> <span data-i18n="nav.map">Karte</span></button>
<button class="tab-button" role="tab" aria-selected="false" aria-controls="panel-disruptions" id="tab-disruptions" tabindex="-1" data-tab="disruptions"><span class="tab-button__icon" aria-hidden="true">⚠</span> <span data-i18n="disruptions.title">Störungen</span></button>
<button class="tab-button" role="tab" aria-selected="false" aria-controls="panel-more" id="tab-more" tabindex="-1" data-tab="more"><span class="tab-button__icon" aria-hidden="true">☰</span> <span data-i18n="nav.more">Mehr</span></button>
</div>
</nav>
<main class="app-main" id="main-content">
<section class="tab-panel" data-tab-panel="departures" id="panel-departures" role="tabpanel" aria-labelledby="tab-departures mobile-tab-departures" tabindex="-1">
<div class="welcome-panel" id="welcome-panel">
<div class="welcome-panel__eyebrow">LIVE · VBB</div>
<h1 data-i18n="welcome.title">Was fährt jetzt?</h1>
<p data-i18n="welcome.text">Suche eine Haltestelle oder nutze deinen Standort, um die nächsten Abfahrten zu sehen.</p>
<div class="welcome-actions">
<button id="welcome-nearby" class="action-card action-card--primary" type="button">
<span class="action-card__icon" aria-hidden="true">📍</span>
<span><strong data-i18n="welcome.nearbyTitle">In meiner Nähe</strong><small data-i18n="welcome.nearbyText">Haltestellen im Umkreis finden</small></span>
</button>
<button id="welcome-route" class="action-card" type="button">
<span class="action-card__icon" aria-hidden="true">🧭</span>
<span><strong data-i18n="welcome.routeTitle">Route planen</strong><small data-i18n="welcome.routeText">Von A nach B mit Bus &amp; Bahn</small></span>
</button>
</div>
</div>
<div id="nearby-home" class="home-section" hidden>
<div class="section-heading">
<div><span class="section-heading__eyebrow" data-i18n="common.optional">OPTIONAL</span><h2 data-i18n="nearby.homeTitle">Haltestellen in der Nähe</h2></div>
<button id="nearby-home-open" class="text-button" type="button" data-i18n="common.showAll">Alle anzeigen</button>
</div>
<div id="nearby-home-list" class="nearby-preview"></div>
</div>
<div id="departures-view" hidden>
<div class="board-header">
<div>
<span class="section-heading__eyebrow" data-i18n="departures.eyebrow">LIVE-ABFAHRTEN</span>
<h1 id="board-station-name" class="board-station-name" data-i18n="departures.noStation">Keine Haltestelle ausgewählt</h1>
</div>
<button id="favorite-toggle" class="favorite-toggle" hidden>☆ <span data-i18n="favorites.favorite">Favorit</span></button>
</div>
<div id="board-status" class="board-status" aria-live="polite"><span class="status-dot" aria-hidden="true"></span> <span data-i18n="departures.refreshInfo">Aktualisierung alle 30 Sekunden</span></div>
<div id="departures-mode-filter" class="mode-filter"></div>
<div id="departure-board" class="departure-board" role="list" aria-label="Abfahrtstafel"></div>
<div id="board-empty-state" class="board-empty-state" data-i18n="departures.emptyState">Suche oben eine Haltestelle, um Live-Abfahrten zu sehen.</div>
<div id="elevator-status" class="elevator-status" aria-live="polite" hidden></div>
<div id="station-disruption" class="station-disruption" aria-live="polite" hidden></div>
</div>
</section>
<section class="tab-panel" data-tab-panel="map" id="panel-map" role="tabpanel" aria-labelledby="tab-map mobile-tab-map" tabindex="-1" hidden>
<div class="panel-heading"><div><span class="section-heading__eyebrow" data-i18n="common.live">LIVE</span><h1 data-i18n="map.title">Karte</h1><p data-i18n="map.text">Fahrzeuge, Routen und deine aktuelle Fahrt live verfolgen.</p></div></div>
<div class="map-controls">
<div id="map-mode-filter" class="mode-filter"></div>
<button id="map-locate" class="map-locate-btn" type="button">📍 <span data-i18n="map.locate">Mein Standort</span></button>
</div>
<div id="map-isolation-banner" class="map-isolation-banner" aria-live="polite">
<span><span data-i18n="map.following">Verfolge Linie</span> <span id="map-isolation-line" class="map-isolation-banner__line"></span> <span data-i18n="map.live">live</span></span>
<button id="map-isolation-reset" type="button" data-i18n="map.showAllLines">Alle Linien anzeigen</button>
</div>
<div id="live-map" class="live-map" role="application" aria-label="Live-Karte mit Fahrzeugpositionen. Für Screenreader-Nutzer eingeschränkt zugänglich - Details zu einzelnen Fahrten sind über die Abfahrtstafel und den Routenplaner verfügbar." data-i18n-aria-label="map.a11yDescription"></div>
</section>
<section class="tab-panel" data-tab-panel="journey" id="panel-journey" role="tabpanel" aria-labelledby="tab-journey mobile-tab-journey" tabindex="-1" hidden>
<div class="panel-heading"><div><span class="section-heading__eyebrow" data-i18n="journey.eyebrow">VERBINDUNG SUCHEN</span><h1 data-i18n="journey.title">Wohin möchtest du?</h1><p data-i18n="journey.text">Starte mit deinem Standort oder suche einen Ausgangspunkt.</p></div></div>
<div class="journey-card-form">
<div class="journey-input-row">
<div class="journey-input-marker journey-input-marker--start" aria-hidden="true">●</div>
<div class="search-box">
<input type="text" id="journey-from" placeholder="Mein Standort oder Start…" data-i18n-placeholder="journey.fromPlaceholder" aria-label="Start der Route" data-i18n-aria-label="journey.fromPlaceholder" role="combobox" aria-expanded="false" aria-controls="journey-from-results" aria-autocomplete="list" autocomplete="off" />
<ul id="journey-from-results" class="search-results" role="listbox" aria-label="Vorschläge für Start" hidden></ul>
</div>
<button id="journey-use-location" class="location-btn" title="Meinen Standort verwenden" data-i18n-title="journey.useLocation" aria-label="Meinen Standort verwenden" data-i18n-aria-label="journey.useLocation">📍</button>
</div>
<div class="journey-connector" aria-hidden="true"></div>
<div class="journey-input-row">
<div class="journey-input-marker journey-input-marker--end" aria-hidden="true">●</div>
<div class="search-box">
<input type="text" id="journey-to" placeholder="Wohin?" data-i18n-placeholder="journey.toPlaceholder" aria-label="Ziel der Route" data-i18n-aria-label="journey.toPlaceholder" role="combobox" aria-expanded="false" aria-controls="journey-to-results" aria-autocomplete="list" autocomplete="off" />
<ul id="journey-to-results" class="search-results" role="listbox" aria-label="Vorschläge für Ziel" hidden></ul>
</div>
</div>
</div>
<div class="journey-when" role="group" aria-label="Zeitpunkt der Fahrt">
<button class="when-choice is-active" data-when="now" type="button" aria-pressed="true">● <span data-i18n="journey.now">Jetzt</span></button>
<button class="when-choice" data-when="departure" type="button" aria-pressed="false" data-i18n="journey.departureAt">Abfahrt um</button>
<button class="when-choice" data-when="arrival" type="button" aria-pressed="false" data-i18n="journey.arrivalAt">Ankunft um</button>
<input type="datetime-local" id="journey-when" class="journey-datetime" aria-label="Datum und Uhrzeit" hidden />
</div>
<div id="journey-vias" class="journey-vias"></div>
<button id="journey-add-via" class="text-button journey-add-via" type="button">+ <span data-i18n="journey.addVia">Zwischenhalt hinzufügen</span></button>
<label class="wheelchair-toggle">
<input type="checkbox" id="journey-wheelchair" />
<span><span aria-hidden="true">♿</span> <span data-i18n="journey.wheelchair">Nur rollstuhlgerechte Verbindungen</span></span>
</label>
<label class="wheelchair-toggle" id="journey-bike-row">
<input type="checkbox" id="journey-bike" />
<span><span aria-hidden="true">🚲</span> <span data-i18n="journey.bike">Fahrrad mitnehmen</span></span>
</label>
<div class="journey-modes-field" id="journey-modes-field">
<span class="journey-modes-field__label" data-i18n="journey.modes">Verkehrsmittel</span>
<div id="journey-mode-filter" class="mode-filter"></div>
</div>
<div class="journey-options-row">
<button id="journey-submit" class="journey-submit" type="button"><span data-i18n="journey.submit">Verbindungen suchen</span> <span aria-hidden="true">→</span></button>
</div>
<p class="journey-source-note" data-i18n="journey.sourceNote">🛰 Routenplanung mit Echtzeitdaten von VBB &amp; Deutscher Bahn</p>
<div id="journey-share-row" class="journey-share-row" hidden>
<button id="journey-share-btn" class="text-button" type="button"><span aria-hidden="true">🔗</span> <span data-i18n="journey.share">Route teilen</span></button>
</div>
<div id="journey-results" class="journey-results" aria-live="polite"></div>
<div id="journey-load-more-row" class="journey-load-more-row" hidden>
<button id="journey-earlier-btn" class="journey-load-more-btn" type="button" disabled>↑ <span data-i18n="journey.earlier">Früher</span></button>
<button id="journey-later-btn" class="journey-load-more-btn" type="button" disabled>↓ <span data-i18n="journey.later">Später</span></button>
</div>
</section>
<section class="tab-panel" data-tab-panel="nearby" hidden>
<div class="panel-heading"><div><span class="section-heading__eyebrow" data-i18n="nearby.eyebrow">STANDORT</span><h1 data-i18n="nearby.title">In der Nähe</h1><p data-i18n="nearby.text">Finde Haltestellen rund um deinen aktuellen Standort.</p></div></div>
<button id="nearby-locate" class="location-btn location-btn--wide">📍 <span data-i18n="nearby.locate">Meinen Standort verwenden</span></button>
<div id="nearby-list" class="nearby-list"><div class="board-empty-state" data-i18n="nearby.emptyState">Nutze deinen Standort, um Haltestellen in deiner Nähe zu finden.</div></div>
</section>
<section class="tab-panel" data-tab-panel="favorites" id="panel-favorites" role="tabpanel" aria-labelledby="tab-favorites mobile-tab-favorites" tabindex="-1" hidden>
<div class="panel-heading">
<div><span class="section-heading__eyebrow" data-i18n="favorites.eyebrow">SCHNELLZUGRIFF</span><h1 data-i18n="favorites.title">Meine Haltestellen</h1><p data-i18n="favorites.text">Deine gespeicherten Haltestellen mit Live-Abfahrten und Störungshinweisen.</p></div>
</div>
<div id="favorites-home" class="home-section">
<div id="notify-callout" class="notify-callout" hidden>
<div class="notify-callout__icon" aria-hidden="true">🔔</div>
<div class="notify-callout__text">
<strong data-i18n="favorites.enableNotifyTitle">Störungen sofort mitbekommen</strong>
<small data-i18n="favorites.enableNotifyText">Auch wenn die App gerade nicht offen ist.</small>
</div>
<button id="notify-toggle" class="notify-callout__btn" type="button" data-i18n="favorites.enableNotifyShort">Aktivieren</button>
</div>
<p id="favorites-empty-hint" class="favorites-empty-hint" data-i18n="favorites.emptyHint">Noch keine Favoriten - tippe bei einer Haltestelle auf ☆, um sie hier anzupinnen.</p>
<div id="favorite-cards" class="favorite-cards" role="list"></div>
</div>
</section>
<section class="tab-panel" data-tab-panel="disruptions" id="panel-disruptions" role="tabpanel" aria-labelledby="tab-disruptions mobile-tab-disruptions" tabindex="-1" hidden>
<div class="panel-heading"><div><span class="section-heading__eyebrow" data-i18n="common.current">AKTUELL</span><h1 data-i18n="disruptions.title">Störungen</h1><p data-i18n="disruptions.text">Ortsbezogene und netzweite Hinweise.</p></div></div>
<div class="disruption-section">
<div class="section-heading"><div><h2 data-i18n="disruptions.nearbyTitle">In deiner Nähe</h2></div></div>
<button id="disruptions-locate" class="location-btn location-btn--wide">📍 <span data-i18n="disruptions.useLocation">Standort verwenden</span></button>
<div id="nearby-disruptions-list" class="remarks-list" aria-live="polite"><div class="board-empty-state" data-i18n="disruptions.nearbyEmpty">Nutze deinen Standort für ortsbezogene Störungen.</div></div>
</div>
<div class="disruption-section">
<div class="section-heading"><div><h2 data-i18n="disruptions.networkTitle">Netzweit</h2></div></div>
<div id="remarks-list" class="remarks-list" aria-live="polite"><div class="board-empty-state" data-i18n="disruptions.loading">Störungsmeldungen werden geladen…</div></div>
</div>
</section>
<section class="tab-panel" data-tab-panel="fares" hidden>
<div class="panel-heading"><div><span class="section-heading__eyebrow" data-i18n="fares.eyebrow">PREISE</span><h1 data-i18n="fares.title">Tickets</h1><p data-i18n="fares.text">Übliche VBB-Fahrpreise auf einen Blick.</p></div></div>
<div class="disclaimer-banner">
<span class="disclaimer-banner__icon" aria-hidden="true">⚠</span>
<span class="disclaimer-banner__text"><span data-i18n="fares.disclaimer">Angaben ohne Gewähr – Preise können sich ändern.</span> <span id="fares-stand"></span></span>
</div>
<div id="fares-content"><div class="board-empty-state" data-i18n="fares.loading">Lade Fahrpreise…</div></div>
<div class="section-heading"><div><h2 data-i18n="fares.buyTitle">Tickets kaufen</h2></div></div>
<div id="fares-companies" class="company-links"></div>
</section>
<section class="tab-panel" data-tab-panel="more" id="panel-more" role="tabpanel" aria-labelledby="tab-more mobile-tab-more" tabindex="-1" hidden>
<div class="panel-heading"><div><span class="section-heading__eyebrow" data-i18n="more.eyebrow">WEITERE FUNKTIONEN</span><h1 data-i18n="nav.more">Mehr</h1><p data-i18n="more.text">Alles, was nicht bei jeder Fahrt gebraucht wird.</p></div></div>
<div class="more-grid">
<button class="more-card" data-open-tab="nearby" type="button"><span aria-hidden="true">📍</span><strong data-i18n="nearby.title">In der Nähe</strong><small data-i18n="more.nearbyText">Haltestellen in deiner Umgebung</small></button>
<button class="more-card" data-open-tab="fares" type="button"><span aria-hidden="true">🎫</span><strong data-i18n="fares.title">Tickets</strong><small data-i18n="more.faresText">Preise &amp; wo kaufen</small></button>
<button class="more-card" data-open-tab="notifications" type="button"><span aria-hidden="true">🔔</span><strong data-i18n="notifications.title">Benachrichtigungen</strong><small data-i18n="more.notificationsText">Verlauf &amp; Push-Status</small></button>
<button class="more-card" data-open-tab="settings" type="button"><span aria-hidden="true">⚙</span><strong data-i18n="nav.settings">Einstellungen</strong><small data-i18n="more.settingsText">Sprache, Anzeige &amp; mehr</small></button>
</div>
<div class="recent-section">
<div class="section-heading"><div><span class="section-heading__eyebrow" data-i18n="more.historyEyebrow">VERLAUF</span><h2 data-i18n="more.historyTitle">Zuletzt gesucht</h2></div><button id="clear-recent" class="text-button" type="button" data-i18n="common.clear">Leeren</button></div>
<div id="recent-searches" class="recent-searches" role="list"></div>
</div>
<div class="recent-section">
<div class="section-heading"><div><span class="section-heading__eyebrow" data-i18n="more.aboutEyebrow">ÜBER</span><h2 data-i18n="more.aboutTitle">App-Info</h2></div></div>
<div class="connection-check">
<div class="connection-check__row">
<span data-i18n="more.backend">Backend</span>
<span id="connection-status" class="connection-status">–</span>
</div>
<button id="connection-test-btn" class="text-button" type="button" data-i18n="more.testConnection">Verbindung testen</button>
</div>
</div>
</section>
<section class="tab-panel" data-tab-panel="settings" id="panel-settings" role="tabpanel" aria-labelledby="header-settings-btn" tabindex="-1" hidden>
<div class="panel-heading"><div><span class="section-heading__eyebrow" data-i18n="settings.eyebrow">EINSTELLUNGEN</span><h1 data-i18n="nav.settings">Einstellungen</h1><p data-i18n="settings.text">Anzeige, Sprache und Benachrichtigungen anpassen.</p></div></div>
<div class="settings-section">
<div class="section-heading"><div><h2 data-i18n="settings.displayTitle">Anzeige</h2></div></div>
<label class="settings-toggle">
<div class="settings-toggle__text">
<strong data-i18n="settings.simpleModeTitle">Einfacher Modus</strong>
<small data-i18n="settings.simpleModeText">Größere Schrift, weniger Optionen auf einen Blick.</small>
</div>
<input type="checkbox" id="settings-simple-mode" />
</label>
</div>
<div class="settings-section">
<div class="section-heading"><div><h2 data-i18n="more.langTitle">Sprache</h2></div></div>
<div class="lang-switch" role="group" aria-label="Sprache / Language">
<button id="lang-de" class="lang-switch__btn" type="button" data-lang="de" aria-pressed="true">Deutsch</button>
<button id="lang-en" class="lang-switch__btn" type="button" data-lang="en" aria-pressed="false">English</button>
</div>
</div>
</section>
<section class="tab-panel" data-tab-panel="notifications" id="panel-notifications" role="tabpanel" aria-labelledby="header-notifications-btn" tabindex="-1" hidden>
<div class="panel-heading">
<div><span class="section-heading__eyebrow" data-i18n="notifications.eyebrow">VERLAUF</span><h1 data-i18n="notifications.title">Benachrichtigungen</h1><p data-i18n="notifications.text">Alles, was zuletzt eine Push- oder In-App-Benachrichtigung ausgelöst hat.</p></div>
<button id="notifications-clear-btn" class="text-button" type="button" data-i18n="common.clear">Leeren</button>
</div>
<div id="notifications-list" class="notifications-list" role="list">
<div class="board-empty-state" data-i18n="notifications.empty">Noch keine Benachrichtigungen.</div>
</div>
</section>
</main>
<div class="global-disclaimer">
<span class="global-disclaimer__icon" aria-hidden="true">ℹ</span>
<span data-i18n="common.footer">Live-Daten von VBB · Angaben ohne Gewähr</span>
</div>
<nav class="mobile-nav" aria-label="Hauptnavigation mobil">
<div role="tablist" aria-label="Hauptnavigation mobil" class="mobile-nav__list">
<button class="mobile-nav__item is-active" role="tab" aria-selected="true" aria-controls="panel-departures" id="mobile-tab-departures" tabindex="0" data-tab="departures"><span aria-hidden="true">🚏</span><small data-i18n="nav.departures">Abfahrten</small></button>
<button class="mobile-nav__item" role="tab" aria-selected="false" aria-controls="panel-favorites" id="mobile-tab-favorites" tabindex="-1" data-tab="favorites"><span aria-hidden="true">★</span><small data-i18n="favorites.title">Favoriten</small></button>
<button class="mobile-nav__item" role="tab" aria-selected="false" aria-controls="panel-journey" id="mobile-tab-journey" tabindex="-1" data-tab="journey"><span aria-hidden="true">🧭</span><small data-i18n="nav.route">Route</small></button>
<button class="mobile-nav__item" role="tab" aria-selected="false" aria-controls="panel-map" id="mobile-tab-map" tabindex="-1" data-tab="map"><span aria-hidden="true">🗺</span><small data-i18n="nav.map">Karte</small></button>
<button class="mobile-nav__item" role="tab" aria-selected="false" aria-controls="panel-disruptions" id="mobile-tab-disruptions" tabindex="-1" data-tab="disruptions"><span aria-hidden="true">⚠</span><small data-i18n="disruptions.title">Störungen</small></button>
<button class="mobile-nav__item" role="tab" aria-selected="false" aria-controls="panel-more" id="mobile-tab-more" tabindex="-1" data-tab="more"><span aria-hidden="true">☰</span><small data-i18n="nav.more">Mehr</small></button>
</div>
</nav>
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script src="js/config.js"></script>
<script src="js/i18n.js"></script>
<script src="js/api.js"></script>
<script src="js/notifications.js"></script>
<script src="js/splitflap.js"></script>
<script src="js/modefilter.js"></script>
<script src="js/livemap.js"></script>
<script src="js/tripalarm.js"></script>
<script src="js/tripmodal.js"></script>
<script src="js/push.js"></script>
<script src="js/app.js"></script>
<div id="toast-container" class="toast-container" aria-live="polite"></div>
</body>
</html>