/* ============================================================
   BAN YARO — Gassi-Routen (Komoot-Stil)
   Sammlung, Suche, Bewertung, GPX-Download, Fotos, Nearby POIs
   ============================================================ */

window.Page_routes = (() => {

  let _container = null;
  let _appState  = null;
  let _data      = [];
  let _filtered  = [];
  let _userPos   = null;

  let _search     = '';
  let _difficulty = '';
  let _terrain    = '';
  let _sortBy     = 'newest';
  let _onlyMine   = false;

  let _isRecording = false;
  let _filterOpen  = false;

  // Navigation-Overlay state
  let _navOvl = null, _navMap = null, _navWatchId = null;
  let _navWakeLock = null, _navInactTimer = null, _navDimmed = false;
  let _navCurrentIdx = 0;
  let _navPois = [];
  let _navMaxIdx = 0;
  let _navWalkMeta = null; // { routeId, totalKm, trackLen }
  let _navOrientCleanup = null;
  let _navLastBearing = null;
  let _navCompassHeading = null;
  let _navHeadingSmoothed = null;

  // Recording-Overlay state
  let _recOvl = null, _recMap = null;
  let _recActive = false;
  let _recTrack = [], _recDistKm = 0, _recStartTime = null;
  let _recTimerInt = null, _recWatchId = null;
  let _recPolyline = null, _recLocMarker = null;
  let _recWakeLock = null, _recInactTimer = null, _recDimmed = false;

  // 'mine' | 'discover' | 'suggest'
  let _browseMode = 'mine';

  // Vorschläge-Tab state
  let _suggestKm   = 4;          // gewählte Distanz: 2, 4 oder 6
  let _suggestSeed = 0;          // Variante: 0, 1, 2
  let _suggestResult = null;     // letzte API-Antwort
  let _suggestMap    = null;     // Leaflet-Instanz der Vorschau-Karte

  // Ansichts-Modus: 'list' | 'map'
  let _viewMode      = 'list';
  let _searchMap     = null;       // L.map Instanz der Suchkarte
  let _searchLines   = new Map();  // routeId → { line, route }

  // Mini-Karten auf den Route-Cards
  let _miniMaps      = new Map(); // routeId → L.map
  const DIFFICULTY_LABEL = { leicht: 'Leicht', mittel: 'Mittel', anspruchsvoll: 'Schwer' };
  const TERRAIN_LABEL    = { wald: 'Wald', asphalt: 'Asphalt', wiese: 'Wiese', mix: 'Mix' };
  const HUNDE_LABEL      = { eingeschränkt: '🐾', gut: '🐾🐾', sehr_gut: '🐾🐾🐾', premium: '🐾🐾🐾🐾' };
  const HUNDE_TEXT       = { eingeschränkt: 'Leine', gut: 'gut',
                              sehr_gut: 'sehr gut', premium: 'premium' };
  const DIFF_COLOR       = { leicht: 'background:rgba(22,163,74,0.10);color:#4ade80;border-color:rgba(22,163,74,0.30)',
                              mittel: 'background:rgba(234,179,8,0.10);color:#facc15;border-color:rgba(234,179,8,0.30)',
                              anspruchsvoll: 'background:rgba(220,38,38,0.10);color:#f87171;border-color:rgba(220,38,38,0.30)' };

  // POI-Typen entlang der Route — nur relevante/interessante Orte
  const NEARBY_TYPES = [
    { type: 'restaurant', icon: '🍽️', label: 'Restaurant/Café', svgIcon: 'fork-knife',    color: '#F97316' },
    { type: 'tierarzt',   icon: '🏥', label: 'Tierarzt',         svgIcon: 'first-aid',     color: '#EF4444' },
    { type: 'shop',       icon: '🐾', label: 'Zoobedarf',        svgIcon: 'shopping-cart', color: '#3B82F6' },
  ];

  // _esc und _emptyState ersetzt durch UI.escape() / UI.emptyState()

  async function init(container, appState, params = {}) {
    _container = container;
    _appState  = appState;

    // Vorberechneter Vorschlag vom Welcome-Chip → direkt in Suggest-Tab anzeigen
    if (params._suggestResult) {
      _suggestResult = params._suggestResult;
      _suggestKm     = params._suggestKm  || _suggestKm;
      _suggestSeed   = params._suggestSeed || _suggestSeed;
      _browseMode    = 'suggest';
    }

    _render();
    UI.loadLeaflet(); // fire & forget — bereit wenn Cards gerendert werden
    try { _userPos = await API.getLocation(); } catch {}
    await _loadData();

    // Vorschlag sofort rendern (Leaflet war noch nicht bereit bei _render)
    if (params._suggestResult) {
      _renderSuggestTab();
      _showSuggestResult(params._suggestResult);
    }

    // Deep-Link: /#routes?id=123 → direkt Route-Detail öffnen
    const urlParams = new URLSearchParams((location.hash.split('?')[1] || ''));
    const deepId = urlParams.get('id');
    if (deepId) {
      _openDetail(parseInt(deepId, 10));
    }
  }

  function refresh() {
    // Button-Zeile neu rendern damit style-Änderungen nach einem JS-Update sichtbar werden
    const btnRow = document.querySelector('#rk-filter-btn')?.parentElement;
    if (btnRow) {
      btnRow.innerHTML = `
        <button id="rk-filter-btn" style="${_btnStyle()}position:relative">
          ${UI.icon('gear')} Filter
          <span class="rk-filter-badge" id="rk-filter-badge" style="display:none"></span>
        </button>
        <label id="rk-imp-wrap" title="GPX / KML / TCX importieren" style="${_btnStyle()}">
          ${UI.icon('download-simple')} Import
          <input type="file" id="rk-import-input" accept=".gpx,.kml,.tcx" style="display:none">
        </label>
        <button class="rk-rec-btn" id="rk-rec-btn" style="${_btnStyle(true)}">${UI.icon('path')} Aufzeichnen</button>
      `;
      document.getElementById('rk-filter-btn').addEventListener('click', _toggleFilterPanel);
      document.getElementById('rk-rec-btn').addEventListener('click', _openRecOvl);
      document.getElementById('rk-import-input').addEventListener('change', e => {
        const file = e.target.files?.[0]; if (file) _importFile(file); e.target.value = '';
      });
      _updateFilterBadge();
    }
    _syncRecBtn();
    _loadData();
  }
  function onDogChange() {}

  // ----------------------------------------------------------
  // Render
  // ----------------------------------------------------------
  function _render() {
    _container.innerHTML = `
      <div class="rk-layout">
        <div class="rk-header">
          <div class="rk-mode-toggle" id="rk-mode-toggle">
            <button class="rk-mode-btn${_browseMode==='mine'?' active':''}" id="rk-mode-mine">${UI.icon('user')} Meine Routen</button>
            <button class="rk-mode-btn${_browseMode==='discover'?' active':''}" id="rk-mode-discover">${UI.icon('map-pin')} Entdecken</button>
            <button class="rk-mode-btn${_browseMode==='suggest'?' active':''}" id="rk-mode-suggest">${UI.icon('sparkle')} Vorschläge</button>
          </div>
          <!-- Zeile 2: Suche + View-Toggle (gleiche Höhe wie Aktions-Buttons) -->
          <div id="rk-search-row" style="display:flex;gap:8px;align-items:stretch;margin-bottom:var(--space-3)">
            <div style="position:relative;flex:1;min-width:0">
              <svg class="ph-icon" aria-hidden="true"
                   style="position:absolute;left:12px;top:50%;transform:translateY(-50%);
                          color:var(--c-text-muted);pointer-events:none;width:16px;height:16px">
                <use href="/icons/phosphor.svg#magnifying-glass"></use>
              </svg>
              <input id="rk-search" type="search" placeholder="Routen suchen…" autocomplete="off"
                     style="width:100%;height:46px;padding:0 12px 0 36px;
                            border:1.5px solid var(--c-border);border-radius:10px;
                            font-size:14px;font-family:inherit;
                            background:var(--c-surface);color:var(--c-text);outline:none;
                            box-sizing:border-box;">
            </div>
            <div style="display:flex;border:1.5px solid var(--c-border);border-radius:10px;
                        overflow:hidden;flex-shrink:0;height:46px;box-sizing:border-box">
              <button id="rk-view-list" title="Liste"
                      style="width:44px;height:100%;border:none;cursor:pointer;
                             display:flex;align-items:center;justify-content:center;
                             background:${_viewMode==='list' ? 'var(--c-primary)' : 'var(--c-surface)'};
                             color:${_viewMode==='list' ? '#fff' : 'var(--c-text-secondary)'}">
                ${UI.icon('list')}
              </button>
              <button id="rk-view-map" title="Karte"
                      style="width:44px;height:100%;border:none;border-left:1.5px solid var(--c-border);
                             cursor:pointer;display:flex;align-items:center;justify-content:center;
                             background:${_viewMode==='map' ? 'var(--c-primary)' : 'var(--c-surface)'};
                             color:${_viewMode==='map' ? '#fff' : 'var(--c-text-secondary)'}">
                ${UI.icon('map-trifold')}
              </button>
            </div>
          </div>
          <!-- Zeile 3: Aktions-Buttons gleichmäßig (Inline-Styles, cache-unabhängig) -->
          <div style="display:flex;gap:8px">
            <button id="rk-filter-btn" style="${_btnStyle()}position:relative">
              ${UI.icon('gear')} Filter
              <span class="rk-filter-badge" id="rk-filter-badge" style="display:none"></span>
            </button>
            <label id="rk-imp-wrap" title="GPX / KML / TCX importieren" style="${_btnStyle()}">
              ${UI.icon('download-simple')} Import
              <input type="file" id="rk-import-input" accept=".gpx,.kml,.tcx" style="display:none">
            </label>
            <button class="rk-rec-btn" id="rk-rec-btn" style="${_btnStyle(true)}">${UI.icon('path')} Aufzeichnen</button>
          </div>
          <div class="rk-filter-panel" id="rk-filter-panel" style="display:none">
            <div class="rk-filters" id="rk-filters">
              <div class="rk-filter-group">
                <div class="rk-filter-label">Schwierigkeit</div>
                <div class="rk-chips-row">
                  <button class="rk-chip active" data-filter="difficulty" data-val="">Alle</button>
                  <button class="rk-chip" data-filter="difficulty" data-val="leicht">🟢 Leicht</button>
                  <button class="rk-chip" data-filter="difficulty" data-val="mittel">🟡 Mittel</button>
                  <button class="rk-chip" data-filter="difficulty" data-val="anspruchsvoll">🔴 Anspruchsvoll</button>
                </div>
              </div>
              <div class="rk-filter-group">
                <div class="rk-filter-label">Untergrund</div>
                <div class="rk-chips-row">
                  <button class="rk-chip active" data-filter="terrain" data-val="">Alle Wege</button>
                  <button class="rk-chip" data-filter="terrain" data-val="wald">🌲 Wald</button>
                  <button class="rk-chip" data-filter="terrain" data-val="asphalt">🛣️ Asphalt</button>
                  <button class="rk-chip" data-filter="terrain" data-val="wiese">🌿 Wiese</button>
                  <button class="rk-chip" data-filter="terrain" data-val="mix">🔀 Mix</button>
                </div>
              </div>
              <div class="rk-filter-group">
                <div class="rk-filter-label">Sortierung</div>
                <div class="rk-chips-row">
                  <button class="rk-chip active" data-filter="sort" data-val="newest">Neueste</button>
                  <button class="rk-chip" data-filter="sort" data-val="distance">Längste</button>
                  <button class="rk-chip" data-filter="sort" data-val="rating">Beste</button>
                  <button class="rk-chip" data-filter="sort" data-val="dog">Hundefreundlich</button>
                </div>
              </div>
              <div class="rk-filter-group" id="rk-mine-group" style="display:none">
                <div class="rk-filter-label">Eigene</div>
                <div class="rk-chips-row">
                  <button class="rk-chip" data-filter="mine" data-val="mine">🔒 Nur meine</button>
                </div>
              </div>
              <div class="rk-filter-group" id="rk-nearby-group" style="display:none">
                <div class="rk-filter-label">Umgebung</div>
                <div class="rk-chips-row">
                  <button class="rk-chip" id="rk-nearby-btn" data-filter="nearby" data-val="">${UI.icon('map-pin')} In meiner Nähe</button>
                </div>
              </div>
            </div>
          </div>
        </div>
        <div class="rk-grid" id="rk-grid">
          <div class="rk-loading">Lädt Routen…</div>
        </div>
      </div>
    `;

    let _searchTimer = null;
    document.getElementById('rk-search').addEventListener('input', e => {
      clearTimeout(_searchTimer);
      _searchTimer = setTimeout(() => {
        _search = e.target.value.toLowerCase().trim();
        _applyFilter();
      }, 300);
    });
    document.getElementById('rk-view-list').addEventListener('click', () => _switchView('list'));
    document.getElementById('rk-view-map').addEventListener('click',  () => _switchView('map'));
    document.getElementById('rk-filter-btn').addEventListener('click', _toggleFilterPanel);
    document.getElementById('rk-rec-btn').addEventListener('click', _openRecOvl);
    document.getElementById('rk-import-input').addEventListener('change', e => {
      const file = e.target.files?.[0];
      if (file) _importFile(file);
      e.target.value = ''; // reset so same file can be re-selected
    });
    document.getElementById('rk-filters').addEventListener('click', e => {
      const chip = e.target.closest('.rk-chip');
      if (!chip) return;
      const { filter, val } = chip.dataset;
      chip.closest('.rk-chips-row').querySelectorAll('.rk-chip')
        .forEach(c => c.classList.remove('active'));
      chip.classList.add('active');
      if (filter === 'difficulty') _difficulty = val;
      if (filter === 'terrain')    _terrain    = val;
      if (filter === 'sort')       _sortBy     = val;
      if (filter === 'mine')       _onlyMine   = chip.classList.contains('active') && val === 'mine';
      if (filter === 'nearby')     { _loadDataNearby(); return; } // async, calls _applyFilter itself
      _updateFilterBadge();
      _applyFilter();
    });
    // Mode toggle
    document.getElementById('rk-mode-mine').addEventListener('click', () => _setBrowseMode('mine'));
    document.getElementById('rk-mode-discover').addEventListener('click', () => _setBrowseMode('discover'));
    document.getElementById('rk-mode-suggest').addEventListener('click', () => _setBrowseMode('suggest'));
  }

  function _syncRecBtn() {
    // no-op: recording now handled by self-contained overlay
  }

  function _toggleFilterPanel() {
    _filterOpen = !_filterOpen;
    const panel = document.getElementById('rk-filter-panel');
    const btn   = document.getElementById('rk-filter-btn');
    if (panel) panel.style.display = _filterOpen ? '' : 'none';
    if (btn)   btn.classList.toggle('active', _filterOpen);
  }

  function _updateFilterBadge() {
    const badge = document.getElementById('rk-filter-badge');
    if (!badge) return;
    const hasFilter = _difficulty !== '' || _terrain !== '' || _sortBy !== 'newest' || _onlyMine;
    badge.style.display = hasFilter ? '' : 'none';
  }

  function _setBrowseMode(mode) {
    _browseMode = mode;
    document.getElementById('rk-mode-mine')?.classList.toggle('active', mode === 'mine');
    document.getElementById('rk-mode-discover')?.classList.toggle('active', mode === 'discover');
    document.getElementById('rk-mode-suggest')?.classList.toggle('active', mode === 'suggest');

    const recBtn    = document.getElementById('rk-rec-btn');
    const impWrap   = document.getElementById('rk-imp-wrap');
    const mineGrp   = document.getElementById('rk-mine-group');
    const nearbyGrp = document.getElementById('rk-nearby-group');
    const searchRow = document.getElementById('rk-search-row'); // Zeile 2: Suche + View-Toggle
    const filterBtn = document.getElementById('rk-filter-btn');
    const actRow    = filterBtn?.parentElement; // Zeile 3: Aktions-Buttons

    if (mode === 'suggest') {
      if (recBtn)   recBtn.style.display   = 'none';
      if (impWrap)  impWrap.style.display  = 'none';
      if (mineGrp)  mineGrp.style.display  = 'none';
      if (nearbyGrp) nearbyGrp.style.display = 'none';
      if (searchRow) searchRow.style.display = 'none';
      if (actRow)    actRow.style.display    = 'none';
      const filterPanel = document.getElementById('rk-filter-panel');
      if (filterPanel) filterPanel.style.display = 'none';
      _renderSuggestTab();
    } else {
      if (searchRow) searchRow.style.display = '';
      if (actRow)    actRow.style.display    = '';
      if (mode === 'discover') {
        if (recBtn)  recBtn.style.display  = 'none';
        if (impWrap) impWrap.style.display = 'none';
        if (mineGrp) mineGrp.style.display = 'none';
        if (nearbyGrp && _userPos) nearbyGrp.style.display = '';
      } else {
        if (recBtn)  recBtn.style.display  = '';
        if (impWrap) impWrap.style.display = '';
        if (_appState.user && mineGrp) mineGrp.style.display = '';
        if (nearbyGrp) nearbyGrp.style.display = 'none';
      }
      _onlyMine = false;
      document.querySelectorAll('#rk-mine-group .rk-chip').forEach(c => c.classList.remove('active'));
      _applyFilter();
    }
  }

  // ----------------------------------------------------------
  // Vorschläge-Tab
  // ----------------------------------------------------------
  function _renderSuggestTab() {
    const grid = document.getElementById('rk-grid');
    if (!grid) return;

    // Leaflet-Karte aus vorherigem Besuch aufräumen
    if (_suggestMap) { _suggestMap.remove(); _suggestMap = null; }

    // Styles einmalig injizieren
    if (!document.getElementById('rk-suggest-styles')) {
      const style = document.createElement('style');
      style.id = 'rk-suggest-styles';
      style.textContent = `
        .rks-km-chip {
          flex:1;padding:14px 8px;border-radius:var(--radius-lg);
          border:2px solid var(--c-border-light);background:var(--c-surface);
          color:var(--c-text);font-size:1.1rem;font-weight:700;cursor:pointer;
          transition:border-color .15s,background .15s,color .15s;text-align:center;
        }
        .rks-km-chip.active {
          border-color:var(--c-primary);background:var(--c-primary);color:#fff;
        }
        .rks-var-btn {
          flex:1;padding:8px 4px;border-radius:8px;font-size:0.8rem;font-weight:600;
          border:1.5px solid var(--c-border-light);background:var(--c-surface);
          color:var(--c-text-secondary);cursor:pointer;
          transition:border-color .15s,background .15s,color .15s;
        }
        .rks-var-btn.active {
          border-color:var(--c-primary);color:var(--c-primary);background:rgba(var(--c-primary-rgb,99,102,241),0.08);
        }
        #rks-map { border-radius:var(--radius-lg);overflow:hidden; }
      `;
      document.head.appendChild(style);
    }

    grid.innerHTML = `
      <div style="padding:var(--space-4) var(--space-2);display:flex;flex-direction:column;gap:var(--space-5)">

        <!-- Distanz-Auswahl -->
        <div>
          <div style="font-size:0.75rem;font-weight:600;color:var(--c-text-secondary);
                      text-transform:uppercase;letter-spacing:.06em;margin-bottom:var(--space-3)">
            Gewünschte Distanz
          </div>
          <div style="display:flex;gap:var(--space-3)" id="rks-km-row">
            <button class="rks-km-chip${_suggestKm===2?' active':''}" data-km="2">2 km</button>
            <button class="rks-km-chip${_suggestKm===4?' active':''}" data-km="4">4 km</button>
            <button class="rks-km-chip${_suggestKm===6?' active':''}" data-km="6">6 km</button>
          </div>
        </div>

        <!-- Varianten-Auswahl -->
        <div>
          <div style="font-size:0.75rem;font-weight:600;color:var(--c-text-secondary);
                      text-transform:uppercase;letter-spacing:.06em;margin-bottom:var(--space-3)">
            Variante
          </div>
          <div style="display:flex;gap:var(--space-2)" id="rks-var-row">
            <button class="rks-var-btn${_suggestSeed===0?' active':''}" data-seed="0">Variante 1</button>
            <button class="rks-var-btn${_suggestSeed===1?' active':''}" data-seed="1">Variante 2</button>
            <button class="rks-var-btn${_suggestSeed===2?' active':''}" data-seed="2">Variante 3</button>
          </div>
        </div>

        <!-- Berechnen-Button -->
        <button id="rks-calc-btn" style="${_btnStyle(true)}">
          <svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#path"></use></svg>
          Route berechnen
        </button>

        <!-- Ergebnis-Bereich (initial leer) -->
        <div id="rks-result"></div>

      </div>
    `;

    // Distanz-Chips
    grid.querySelector('#rks-km-row').addEventListener('click', e => {
      const btn = e.target.closest('.rks-km-chip');
      if (!btn) return;
      _suggestKm = parseInt(btn.dataset.km, 10);
      grid.querySelectorAll('.rks-km-chip').forEach(b => b.classList.toggle('active', b === btn));
    });

    // Varianten-Buttons
    grid.querySelector('#rks-var-row').addEventListener('click', e => {
      const btn = e.target.closest('.rks-var-btn');
      if (!btn) return;
      _suggestSeed = parseInt(btn.dataset.seed, 10);
      grid.querySelectorAll('.rks-var-btn').forEach(b => b.classList.toggle('active', b === btn));
    });

    // Berechnen
    grid.querySelector('#rks-calc-btn').addEventListener('click', _calcSuggestRoute);
  }

  async function _calcSuggestRoute() {
    // Standort prüfen
    if (!_userPos) {
      try { _userPos = await API.getLocation(); } catch {
        const res = document.getElementById('rks-result');
        if (res) res.innerHTML = `
          <div style="padding:var(--space-5);text-align:center;color:var(--c-text-secondary);
                      border:1.5px solid var(--c-border-light);border-radius:var(--radius-lg);
                      background:var(--c-surface)">
            <svg class="ph-icon" aria-hidden="true" style="width:32px;height:32px;color:var(--c-text-muted);margin-bottom:var(--space-3)">
              <use href="/icons/phosphor.svg#map-pin-slash"></use>
            </svg>
            <p style="margin:0;font-size:0.9rem">
              Standort wird benötigt. Bitte erlaube den Zugriff in den Browser-Einstellungen.
            </p>
          </div>`;
        return;
      }
    }

    // Alten Karteninhalt aufräumen
    if (_suggestMap) { _suggestMap.remove(); _suggestMap = null; }

    // Spinner anzeigen
    const res = document.getElementById('rks-result');
    if (!res) return;
    res.innerHTML = `
      <div style="display:flex;flex-direction:column;align-items:center;gap:var(--space-4);
                  padding:var(--space-6);color:var(--c-text-secondary)">
        <div style="width:32px;height:32px;border:3px solid var(--c-border);
                    border-top-color:var(--c-primary);border-radius:50%;
                    animation:spin 0.8s linear infinite"></div>
        <span style="font-size:0.9rem">Berechne Rundweg…</span>
      </div>
      <style>@keyframes spin{to{transform:rotate(360deg)}}</style>
    `;

    const calcBtn = document.getElementById('rks-calc-btn');
    if (calcBtn) calcBtn.disabled = true;

    let result;
    try {
      result = await API.post('/routes/suggest', {
        lat:         _userPos.lat,
        lon:         _userPos.lon,
        distance_km: _suggestKm,
        seed:        _suggestSeed,
      });
      _suggestResult = result;
    } catch (err) {
      const is429 = err.status === 429 || String(err.message).includes('Wochenlimit');
      if (res) res.innerHTML = `
        <div style="padding:var(--space-5);border-radius:var(--radius-lg);
                    background:${is429 ? 'rgba(234,179,8,0.08)' : 'rgba(220,38,38,0.08)'};
                    border:1px solid ${is429 ? 'rgba(234,179,8,0.3)' : 'rgba(220,38,38,0.25)'};
                    color:${is429 ? '#facc15' : '#f87171'};text-align:center">
          <svg class="ph-icon" style="width:28px;height:28px;margin-bottom:var(--space-3)" aria-hidden="true">
            <use href="/icons/phosphor.svg#${is429 ? 'calendar-x' : 'warning'}"></use>
          </svg>
          <p style="margin:0;font-size:var(--text-sm);font-weight:var(--weight-semibold)">
            ${is429 ? 'Wochenlimit erreicht' : 'Fehler beim Berechnen'}
          </p>
          <p style="margin:var(--space-2) 0 0;font-size:var(--text-xs);opacity:0.85">
            ${is429 ? 'Du hast diese Woche alle 20 Routenvorschläge genutzt. Montag gibt es neue.' : UI.escape(err.message || 'Unbekannter Fehler')}
          </p>
        </div>`;
      if (calcBtn) calcBtn.disabled = false;
      return;
    }
    if (calcBtn) calcBtn.disabled = false;

    _showSuggestResult(result);
  }

  function _showSuggestResult(result) {
    _suggestResult = result;
    const res = document.getElementById('rks-result');
    if (!res) return;

    const distStr   = result.distanz_km ? result.distanz_km.toFixed(2) + ' km' : '–';
    const durStr    = result.dauer_min
      ? (result.dauer_min < 60 ? result.dauer_min + ' min' : Math.floor(result.dauer_min/60) + 'h ' + (result.dauer_min%60||'') + 'min').trim()
      : '–';
    const diffLabel = { leicht: 'Leicht', mittel: 'Mittel', anspruchsvoll: 'Schwer' }[result.schwierigkeit] || '';

    const limitHint = (result.weekly_remaining != null)
      ? `<p style="font-size:var(--text-xs);color:var(--c-text-muted);text-align:right;margin:0 0 var(--space-2)">
           Noch ${result.weekly_remaining} von 20 Anfragen diese Woche
         </p>`
      : '';

    res.innerHTML = `
      ${limitHint}<div id="rks-map" style="height:250px;background:var(--c-surface);margin-bottom:var(--space-3)"></div>
      <div style="display:flex;gap:var(--space-3);align-items:center;flex-wrap:wrap;margin-bottom:var(--space-4)">
        <span style="${_pillStyle('rgba(107,114,128,0.10)','#9ca3af','rgba(107,114,128,0.30)')}">
          ${UI.icon('map-trifold')} ${UI.escape(distStr)}
        </span>
        <span style="${_pillStyle('rgba(107,114,128,0.10)','#9ca3af','rgba(107,114,128,0.30)')}">
          ${UI.icon('timer')} ${UI.escape(durStr)}
        </span>
        ${diffLabel ? `<span style="${_pillStyle(
          {leicht:'rgba(22,163,74,0.10)',mittel:'rgba(234,179,8,0.10)',anspruchsvoll:'rgba(220,38,38,0.10)'}[result.schwierigkeit]||'rgba(107,114,128,0.10)',
          {leicht:'#4ade80',mittel:'#facc15',anspruchsvoll:'#f87171'}[result.schwierigkeit]||'#9ca3af',
          {leicht:'rgba(22,163,74,0.30)',mittel:'rgba(234,179,8,0.30)',anspruchsvoll:'rgba(220,38,38,0.30)'}[result.schwierigkeit]||'rgba(107,114,128,0.30)')}">${UI.escape(diffLabel)}</span>` : ''}
        <span style="font-size:0.85rem;color:var(--c-text-secondary);flex:1;min-width:0;
                     overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${UI.escape(result.name || '')}</span>
      </div>
      <div style="display:flex;gap:var(--space-3)">
        <button id="rks-nav-btn" style="${_btnStyle(false)}flex:1">
          <svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#navigation-arrow"></use></svg>
          Navigation starten
        </button>
        <button id="rks-save-btn" style="${_btnStyle(true)}flex:1">
          <svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#floppy-disk"></use></svg>
          Route speichern
        </button>
      </div>`;

    const _initMap = () => {
      const mapEl = document.getElementById('rks-map');
      if (!mapEl || !window.L) return;
      if (_suggestMap) { _suggestMap.remove(); _suggestMap = null; }
      const track = result.gps_track || [];
      if (track.length < 2) return;
      const lls = track.map(p => [p.lat, p.lon]);
      _suggestMap = L.map(mapEl, { zoomControl: false, attributionControl: false,
                                   dragging: true, touchZoom: true, scrollWheelZoom: false });
      L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 19 }).addTo(_suggestMap);
      const poly = L.polyline(lls, { color: '#C4843A', weight: 4, opacity: 0.9 }).addTo(_suggestMap);
      L.circleMarker(lls[0],     { radius:7, color:'#22C55E', fillColor:'#22C55E', fillOpacity:1, weight:2 }).addTo(_suggestMap);
      L.circleMarker(lls.at(-1), { radius:7, color:'#EF4444', fillColor:'#EF4444', fillOpacity:1, weight:2 }).addTo(_suggestMap);
      _addRouteArrows(_suggestMap, track, '#3b82f6');
      _suggestMap.fitBounds(poly.getBounds(), { padding: [16, 16] });
      setTimeout(() => _suggestMap?.invalidateSize(), 120);
    };
    if (window.L) { _initMap(); } else {
      let tries = 0;
      const poll = setInterval(() => {
        if (window.L || ++tries > 40) { clearInterval(poll); if (window.L) _initMap(); }
      }, 100);
    }

    document.getElementById('rks-nav-btn')?.addEventListener('click', () => {
      _openNavOverlay({ id: 'suggest-' + Date.now(), name: result.name,
                        gps_track: result.gps_track, distanz_km: result.distanz_km });
    });
    document.getElementById('rks-save-btn')?.addEventListener('click', async () => {
      const btn = document.getElementById('rks-save-btn');
      if (!btn) return;
      await UI.asyncButton(btn, async () => {
        await API.post('/routes', { name: result.name, gps_track: result.gps_track,
          distanz_km: result.distanz_km, dauer_min: result.dauer_min, schwierigkeit: result.schwierigkeit });
        UI.toast.success('Route gespeichert!');
        await _loadData();
        _setBrowseMode('mine');
      });
    });
  }

  async function _loadDataNearby() {
    if (!_userPos) {
      try { _userPos = await API.getLocation(); } catch { UI.toast.warning('Standort nicht verfügbar.'); return; }
    }
    try {
      _data = await API.routes.listNearby(_userPos.lat, _userPos.lon, 10000);
      _applyFilter();
    } catch (err) {
      UI.toast.error('Fehler beim Laden: ' + err.message);
    }
  }

  // ----------------------------------------------------------
  // Recording Overlay
  // ----------------------------------------------------------
  function _haversineKm(lat1, lon1, lat2, lon2) {
    const R = 6371, dLat = (lat2-lat1)*Math.PI/180, dLon = (lon2-lon1)*Math.PI/180;
    const a = Math.sin(dLat/2)**2 + Math.cos(lat1*Math.PI/180)*Math.cos(lat2*Math.PI/180)*Math.sin(dLon/2)**2;
    return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
  }

  async function _openRecOvl() {
    if (!_appState.user) { UI.toast.warning('Bitte anmelden.'); return; }
    if (_recOvl) return;

    await UI.loadLeaflet?.() ?? Promise.resolve();

    const ovl = document.createElement('div');
    ovl.id = 'rk-rec-ovl';
    ovl.style.cssText = 'position:fixed;inset:0;z-index:900;display:flex;flex-direction:column;background:var(--c-bg)';
    ovl.innerHTML = `
      <div id="rk-rec-map-wrap" style="flex:1;min-height:0;position:relative"></div>
      <div id="rk-rec-stats-bar" style="display:none;padding:14px 16px;background:var(--c-surface);border-top:1px solid var(--c-border)">
        <div style="display:flex;justify-content:space-around;text-align:center">
          <div>
            <div style="font-size:10px;color:var(--c-text-secondary);text-transform:uppercase;letter-spacing:.06em;margin-bottom:2px">Strecke</div>
            <div id="rk-rec-dist" style="font-size:22px;font-weight:700;font-variant-numeric:tabular-nums">0.00 km</div>
          </div>
          <div>
            <div style="font-size:10px;color:var(--c-text-secondary);text-transform:uppercase;letter-spacing:.06em;margin-bottom:2px">Zeit</div>
            <div id="rk-rec-time" style="font-size:22px;font-weight:700;font-variant-numeric:tabular-nums">00:00</div>
          </div>
          <div>
            <div style="font-size:10px;color:var(--c-text-secondary);text-transform:uppercase;letter-spacing:.06em;margin-bottom:2px">Tempo</div>
            <div id="rk-rec-pace" style="font-size:22px;font-weight:700;font-variant-numeric:tabular-nums">–:––</div>
          </div>
        </div>
      </div>
      <div id="rk-rec-ctrl" style="padding:16px;display:flex;gap:8px;background:var(--c-surface);border-top:1px solid var(--c-border)">
        <button id="rk-rec-cancel" style="${_btnStyle()}flex:1">Abbruch</button>
        <button id="rk-rec-startbtn" style="${_btnStyle(true)}flex:1">${UI.icon('path')} Start</button>
      </div>
      <div id="rk-rec-dim" style="display:none;position:fixed;inset:0;background:#000;z-index:950;flex-direction:column;align-items:center;justify-content:center;color:#fff;touch-action:none;user-select:none;-webkit-user-select:none">
        <div style="font-size:11px;opacity:.35;letter-spacing:.12em;text-transform:uppercase;margin-bottom:12px">Route</div>
        <div style="display:flex;gap:20px;opacity:.55;font-size:14px">
          <span id="rk-rec-dim-dauer">00:00</span>
          <span>·</span>
          <span id="rk-rec-dim-dist">0.00 km</span>
        </div>
        <svg width="56" height="56" viewBox="0 0 56 56" style="margin-top:36px">
          <circle cx="28" cy="28" r="24" fill="none" stroke="rgba(255,255,255,.12)" stroke-width="2.5"/>
          <circle id="rk-dim-prog" cx="28" cy="28" r="24" fill="none" stroke="rgba(255,255,255,.7)" stroke-width="2.5"
            stroke-dasharray="150.8" stroke-dashoffset="150.8" stroke-linecap="round"
            transform="rotate(-90 28 28)" style="transition:none"/>
        </svg>
        <div style="font-size:11px;opacity:.3;margin-top:8px">2 Sek. halten</div>
      </div>
    `;
    document.body.appendChild(ovl);
    _recOvl = ovl;

    const pos = _userPos || { lat: 48.1, lon: 11.5 };
    _recMap = L.map(ovl.querySelector('#rk-rec-map-wrap'), { zoomControl: false, attributionControl: false })
      .setView([pos.lat, pos.lon], 15);
    L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 19 }).addTo(_recMap);
    _recLocMarker = L.circleMarker([pos.lat, pos.lon], {
      radius: 8, color: '#fff', weight: 2.5, fillColor: '#3b82f6', fillOpacity: 1
    }).addTo(_recMap);

    // Get accurate position
    try {
      const p = await API.getLocation();
      _userPos = p;
      _recMap.setView([p.lat, p.lon], 16);
      _recLocMarker.setLatLng([p.lat, p.lon]);
    } catch {}

    ovl.querySelector('#rk-rec-cancel').addEventListener('click', () => _closeRecOvlClean());
    ovl.querySelector('#rk-rec-startbtn').addEventListener('click', _startRecInOvl);
  }

  function _startRecInOvl() {
    if (!navigator.geolocation) { UI.toast.error('GPS nicht verfügbar.'); return; }
    _recActive = true;
    _recTrack = []; _recDistKm = 0; _recStartTime = Date.now();

    // iOS-Hinweis: Display muss wach bleiben
    if (/iPad|iPhone|iPod/.test(navigator.userAgent)) {
      const banner = document.createElement('div');
      banner.style.cssText = 'position:absolute;top:0;left:0;right:0;z-index:960;' +
        'background:rgba(30,30,30,0.92);color:#fff;font-size:13px;line-height:1.4;' +
        'padding:10px 14px;display:flex;align-items:flex-start;gap:10px;' +
        'border-bottom:1px solid rgba(255,255,255,0.1)';
      banner.innerHTML = `
        <svg style="width:18px;height:18px;flex-shrink:0;margin-top:1px;color:#f59e0b" aria-hidden="true">
          <use href="/icons/phosphor.svg#warning"></use></svg>
        <span><strong>Display wach lassen!</strong> Auf iPhone stoppt die GPS-Aufzeichnung, wenn das Display ausgeht — Helligkeit hochsetzen oder Bildschirm nicht sperren.</span>`;
      document.getElementById('rk-rec-map-wrap')?.appendChild(banner);
      setTimeout(() => banner.remove(), 9000);
    }

    const ctrl = document.getElementById('rk-rec-ctrl');
    ctrl.innerHTML = `
      <button id="rk-rec-stopbtn" style="${_btnStyle()}flex:1;border-color:var(--c-danger);background:var(--c-danger);color:#fff;position:relative;overflow:hidden;touch-action:none;user-select:none;">
        <span id="rk-stop-label" style="position:relative;z-index:1;display:flex;align-items:center;gap:6px;pointer-events:none">
          ${UI.icon('path')} Gedrückt halten zum Stoppen
        </span>
        <div id="rk-stop-fill" style="position:absolute;inset:0;background:rgba(0,0,0,0.35);transform:scaleX(0);transform-origin:left center;transition:transform 0.05s linear;pointer-events:none"></div>
      </button>`;

    // Long-Press 1.8s zum Stoppen
    let _stopTimer = null, _stopTick = null;
    const btn = ctrl.querySelector('#rk-rec-stopbtn');
    const fill = ctrl.querySelector('#rk-stop-fill');
    const startHold = () => {
      if (_stopTimer) return;
      const DURATION = 1800;
      const start = Date.now();
      _stopTick = setInterval(() => {
        const p = Math.min((Date.now() - start) / DURATION, 1);
        fill.style.transition = 'none';
        fill.style.transform = `scaleX(${p})`;
      }, 30);
      _stopTimer = setTimeout(() => {
        clearInterval(_stopTick); _stopTick = null; _stopTimer = null;
        fill.style.transform = 'scaleX(1)';
        _stopRecInOvl(true);
      }, DURATION);
    };
    const cancelHold = () => {
      if (!_stopTimer && !_stopTick) return;
      clearTimeout(_stopTimer); clearInterval(_stopTick);
      _stopTimer = null; _stopTick = null;
      fill.style.transition = 'transform 0.25s ease';
      fill.style.transform = 'scaleX(0)';
    };
    btn.addEventListener('pointerdown', e => { e.preventDefault(); startHold(); });
    btn.addEventListener('pointerup',     cancelHold);
    btn.addEventListener('pointerleave',  cancelHold);
    btn.addEventListener('pointercancel', cancelHold);
    document.getElementById('rk-rec-stats-bar').style.display = '';

    _recPolyline = L.polyline([], { color: '#ef4444', weight: 5, opacity: 0.9 }).addTo(_recMap);

    navigator.wakeLock?.request('screen').then(wl => { _recWakeLock = wl; }).catch(() => {});

    _recWatchId = navigator.geolocation.watchPosition(pos => {
      const lat = pos.coords.latitude, lon = pos.coords.longitude;
      const alt = pos.coords.altitude ?? null;
      if (_recTrack.length) {
        const prev = _recTrack[_recTrack.length - 1];
        const d = _haversineKm(prev.lat, prev.lon, lat, lon);
        if (d < 0.003) return;
        _recDistKm += d;
      }
      _recTrack.push({ lat, lon, ...(alt !== null ? { alt: Math.round(alt) } : {}) });
      _recPolyline.addLatLng([lat, lon]);
      _recLocMarker.setLatLng([lat, lon]);
      if (_recTrack.length === 1) _recMap.setView([lat, lon], 16);
      _updateRecStats();
    }, () => {}, { enableHighAccuracy: true, maximumAge: 2000 });

    _recTimerInt = setInterval(_updateRecStats, 1000);
    _resetRecInactTimer();

    // Interaktion auf der Aufzeichnungsseite → Inaktivitäts-Timer zurücksetzen
    _recOvl.addEventListener('touchstart', _onRecOvlTouch, { passive: true });
    _recOvl.addEventListener('pointerdown', _onRecOvlTouch);

    // Long-Press auf dem Schwarzbildschirm → nach 4s zurück zur Aufzeichnung
    const dim = document.getElementById('rk-rec-dim');
    let _lpTimer = null;
    const cancelLp = () => {
      clearTimeout(_lpTimer);
      const prog = document.getElementById('rk-dim-prog');
      if (prog) { prog.style.transition = 'none'; prog.style.strokeDashoffset = '150.8'; }
    };
    dim.addEventListener('pointerdown', e => {
      e.stopPropagation();
      const prog = document.getElementById('rk-dim-prog');
      if (prog) { prog.style.transition = 'stroke-dashoffset 2s linear'; prog.style.strokeDashoffset = '0'; }
      _lpTimer = setTimeout(() => {
        dim.style.display = 'none';
        _recDimmed = false;
        _resetRecInactTimer();
      }, 2000);
    });
    dim.addEventListener('pointerup',     cancelLp);
    dim.addEventListener('pointercancel', cancelLp);
    dim.addEventListener('pointerleave',  cancelLp);
  }

  function _onRecOvlTouch(e) {
    // Touches auf dem Dim-Overlay ignorieren (dort Long-Press-Logik)
    if (document.getElementById('rk-rec-dim')?.contains(e.target)) return;
    _resetRecInactTimer();
  }

  function _updateRecStats() {
    if (!_recStartTime) return;
    const elapsed = Math.floor((Date.now() - _recStartTime) / 1000);
    const mm = String(Math.floor(elapsed / 60)).padStart(2, '0');
    const ss = String(elapsed % 60).padStart(2, '0');
    const timeStr = `${mm}:${ss}`;
    const distStr = `${_recDistKm.toFixed(2)} km`;
    let paceStr = '–:––';
    if (_recDistKm >= 0.05) {
      const mPerKm = (elapsed / 60) / _recDistKm;
      const pm = Math.floor(mPerKm);
      const ps = String(Math.round((mPerKm - pm) * 60)).padStart(2, '0');
      paceStr = `${pm}:${ps}`;
    }
    const q = id => document.getElementById(id);
    if (q('rk-rec-time'))      q('rk-rec-time').textContent     = timeStr;
    if (q('rk-rec-dist'))      q('rk-rec-dist').textContent     = distStr;
    if (q('rk-rec-pace'))      q('rk-rec-pace').textContent     = paceStr;
    if (q('rk-rec-dim-dauer')) q('rk-rec-dim-dauer').textContent = timeStr;
    if (q('rk-rec-dim-dist'))  q('rk-rec-dim-dist').textContent = distStr;
  }

  function _resetRecInactTimer() {
    if (_recDimmed) return; // Dim wird nur per Long-Press aufgehoben
    clearTimeout(_recInactTimer);
    if (!_recActive) return;
    _recInactTimer = setTimeout(() => {
      const dim = document.getElementById('rk-rec-dim');
      if (dim) {
        dim.style.display = 'flex';
        _recDimmed = true;
      }
    }, 5000);
  }

  async function _stopRecInOvl(save) {
    if (!_recActive && save) return;
    _recActive = false;
    if (_recWatchId !== null) { navigator.geolocation.clearWatch(_recWatchId); _recWatchId = null; }
    if (_recTimerInt)  { clearInterval(_recTimerInt); _recTimerInt = null; }
    if (_recInactTimer){ clearTimeout(_recInactTimer); _recInactTimer = null; }
    if (_recWakeLock)  { try { await _recWakeLock.release(); } catch {} _recWakeLock = null; }
    _recOvl?.removeEventListener('touchstart', _onRecOvlTouch);
    _recOvl?.removeEventListener('pointerdown', _onRecOvlTouch);

    if (!save) { _closeRecOvlClean(); return; }

    const track = [..._recTrack], distKm = _recDistKm;
    const dauMin = Math.round((Date.now() - _recStartTime) / 60000);
    _closeRecOvlClean();
    if (track.length < 2) { UI.toast.warning('Zu wenige GPS-Punkte zum Speichern.'); return; }
    _showRecSaveModal(track, distKm, dauMin);
  }

  function _closeRecOvlClean() {
    if (_recMap) { _recMap.remove(); _recMap = null; }
    if (_recOvl) { _recOvl.remove(); _recOvl = null; }
    _recPolyline = null; _recLocMarker = null;
    _recTrack = []; _recDistKm = 0; _recStartTime = null; _recDimmed = false;
  }

  async function _prefillRouteName(track, distKm) {
    const inp = document.querySelector('#rk-rms-form [name="name"]');
    if (!inp || inp.value) return;
    const pt = track[0];
    const date = new Date().toLocaleDateString('de-DE', { day:'2-digit', month:'2-digit', year:'numeric' });
    const km = distKm.toFixed(1);
    let ort = '';
    try {
      const r = await fetch(`https://nominatim.openstreetmap.org/reverse?lat=${pt.lat}&lon=${pt.lon}&format=json&zoom=13&addressdetails=1&accept-language=de`, { cache: 'no-store' });
      const data = await r.json();
      const a = data.address || {};
      ort = a.village || a.town || a.suburb || a.city_district || a.city || a.municipality || '';
    } catch {}
    if (inp && !inp.value) inp.value = ort
      ? `Gassirunde ${ort} · ${date} · ${km} km`
      : `Gassirunde · ${date} · ${km} km`;
  }

  function _showRecSaveModal(track, distKm, dauMin) {
    const body = `
      <p style="color:var(--c-text-secondary);margin-bottom:var(--space-4)">
        ${track.length} GPS-Punkte · ${distKm.toFixed(2)} km · ca. ${dauMin} min
      </p>
      <form id="rk-rms-form" autocomplete="off">
        <div class="form-group">
          <label class="form-label">Name der Route *</label>
          <input class="form-control" type="text" name="name" placeholder="Wird automatisch ermittelt…" required>
        </div>
        <div style="display:grid;grid-template-columns:1fr 1fr;gap:var(--space-3)">
          <div class="form-group">
            <label class="form-label">Schwierigkeit</label>
            <select class="form-control" name="schwierigkeit">
              <option value="leicht">Leicht</option>
              <option value="mittel">Mittel</option>
              <option value="anspruchsvoll">Anspruchsvoll</option>
            </select>
          </div>
          <div class="form-group">
            <label class="form-label">Untergrund</label>
            <select class="form-control" name="untergrund">
              <option value="">– unbekannt –</option>
              <option value="wald">Wald</option>
              <option value="asphalt">Asphalt</option>
              <option value="wiese">Wiese</option>
              <option value="mix">Mix</option>
            </select>
          </div>
        </div>
        <div class="form-group">
          <label class="form-label">Hundetauglichkeit</label>
          <div class="rk-paw-select" id="rk-rms-paw">
            <button type="button" class="rk-paw-btn" data-val="eingeschränkt">${UI.icon('paw-print')} Eingeschränkt</button>
            <button type="button" class="rk-paw-btn" data-val="gut">${UI.icon('paw-print')}${UI.icon('paw-print')} Gut</button>
            <button type="button" class="rk-paw-btn selected" data-val="sehr_gut">${UI.icon('paw-print')}${UI.icon('paw-print')}${UI.icon('paw-print')} Sehr gut</button>
            <button type="button" class="rk-paw-btn" data-val="premium">${UI.icon('paw-print')}${UI.icon('paw-print')}${UI.icon('paw-print')}${UI.icon('paw-print')} Premium</button>
          </div>
          <input type="hidden" name="hunde_tauglichkeit" id="rk-rms-paw-val" value="sehr_gut">
        </div>
        <div class="form-group" style="display:flex;gap:var(--space-4)">
          <label style="display:flex;align-items:center;gap:var(--space-2);cursor:pointer">
            <input type="checkbox" name="schatten"> ${UI.icon('leaf')} Viel Schatten
          </label>
          <label style="display:flex;align-items:center;gap:var(--space-2);cursor:pointer">
            <input type="checkbox" name="leine_empfohlen"> ${UI.icon('tag')} Leine empfohlen
          </label>
        </div>
        <div class="form-group">
          <label style="display:flex;align-items:center;gap:var(--space-2);cursor:pointer">
            <input type="checkbox" name="is_public"> ${UI.icon('eye')} Öffentlich (von allen sichtbar)
          </label>
        </div>
        <div class="form-group">
          <label class="form-label">Beschreibung <span style="color:var(--c-text-secondary)">(optional)</span></label>
          <textarea class="form-control" name="beschreibung" rows="2" placeholder="Besonderheiten, Highlights, Tipps…"></textarea>
        </div>
      </form>
    `;
    const footer = `
      <button type="button" class="btn btn-secondary flex-1" id="rk-rms-discard">Verwerfen</button>
      <button type="submit" form="rk-rms-form" class="btn btn-primary flex-1">${UI.icon('floppy-disk')} Speichern</button>
    `;
    UI.modal.open({ title: `${UI.icon('path')} Route benennen`, body, footer });
    _prefillRouteName(track, distKm);

    document.getElementById('rk-rms-paw')?.addEventListener('click', e => {
      const btn = e.target.closest('.rk-paw-btn');
      if (!btn) return;
      document.querySelectorAll('.rk-paw-btn').forEach(b => b.classList.remove('selected'));
      btn.classList.add('selected');
      document.getElementById('rk-rms-paw-val').value = btn.dataset.val;
    });

    document.getElementById('rk-rms-discard')?.addEventListener('click', () => UI.modal.close());

    document.getElementById('rk-rms-form')?.addEventListener('submit', async e => {
      e.preventDefault();
      const btn = document.querySelector('[form="rk-rms-form"][type="submit"]');
      const fd  = UI.formData(e.target);
      await UI.asyncButton(btn, async () => {
        const saved = await API.routes.create({
          name:               fd.name?.trim(),
          beschreibung:       fd.beschreibung || null,
          gps_track:          track,
          distanz_km:         Math.round(distKm * 100) / 100,
          dauer_min:          dauMin,
          schwierigkeit:      fd.schwierigkeit || 'leicht',
          untergrund:         fd.untergrund    || null,
          schatten:           'schatten'        in fd,
          leine_empfohlen:    'leine_empfohlen' in fd,
          is_public:          'is_public'       in fd,
          hunde_tauglichkeit: fd.hunde_tauglichkeit || 'sehr_gut',
          client_time:        API.clientNow(),
        });
        UI.modal.close();
        UI.toast.success(`Route „${saved.name}" gespeichert!`);
        _loadData();
      });
    });
  }

  // ----------------------------------------------------------
  // View-Toggle
  // ----------------------------------------------------------
  function _switchView(mode) {
    _viewMode = mode;
    document.getElementById('rk-view-list')?.classList.toggle('active', mode === 'list');
    document.getElementById('rk-view-map')?.classList.toggle('active', mode === 'map');

    const layout = document.querySelector('.rk-layout');
    const grid   = document.getElementById('rk-grid');

    if (mode === 'map') {
      if (grid) grid.style.display = 'none';

      // Alten Map-Container entfernen falls vorhanden
      document.getElementById('rk-map-section')?.remove();
      if (_searchMap) { _searchMap.remove(); _searchMap = null; _searchLines.clear(); }

      // Als fixed Overlay direkt in <body> — kein Konflikt mit .rk-layout overflow:hidden
      const mapH = window.innerHeight - 160;
      const sec = document.createElement('div');
      sec.id = 'rk-map-section';
      sec.className = 'rk-map-section';
      sec.innerHTML = `
        <div class="rk-map-bar">
          <button class="btn btn-secondary btn-sm" id="rk-map-back" title="Zurück zur Liste">${UI.icon('arrow-left')}</button>
          <input class="rk-map-loc-input" id="rk-map-loc" type="search"
                 placeholder="🔍 Ort suchen…" autocomplete="off">
          <button class="btn btn-secondary btn-sm" id="rk-map-gps" title="Mein Standort">${UI.icon('map-pin')}</button>
        </div>
        <div id="rk-search-map" style="flex:1;width:100%"></div>
        <div id="rk-map-hint" class="rk-map-hint">Route antippen um Details zu sehen</div>
      `;
      document.body.appendChild(sec);
      document.getElementById('rk-map-back')?.addEventListener('click', () => _switchView('list'));

      // Wie _initMiniMaps: pollen bis window.L bereit ist
      _pollAndInitSearchMap();

    } else {
      document.getElementById('rk-map-section')?.remove();
      if (_searchMap) { _searchMap.remove(); _searchMap = null; _searchLines.clear(); }
      if (grid) grid.style.display = '';
    }
  }

  // ----------------------------------------------------------
  // Suchkarte
  // ----------------------------------------------------------
  function _pollAndInitSearchMap() {
    if (window.L) { _initSearchMap(); return; }
    let tries = 0;
    const poll = setInterval(() => {
      if (window.L || ++tries > 40) {
        clearInterval(poll);
        if (window.L) _initSearchMap();
      }
    }, 100);
  }

  function _initSearchMap() {
    if (!document.getElementById('rk-search-map')) return;

    const center = _userPos ? [_userPos.lat, _userPos.lon] : [51.1, 10.4];
    const zoom   = _userPos ? 13 : 6;

    _searchMap = L.map('rk-search-map', { zoomControl: true, attributionControl: false })
                  .setView(center, zoom);
    L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 19 }).addTo(_searchMap);
    setTimeout(() => _searchMap?.invalidateSize(), 100);
    setTimeout(() => _searchMap?.invalidateSize(), 600);
    _renderRoutesOnMap();

    // Standort-Button
    document.getElementById('rk-map-gps')?.addEventListener('click', async () => {
      try {
        const pos = await API.getLocation();
        _userPos = pos;
        _searchMap.setView([pos.lat, pos.lon], 14);
      } catch { UI.toast.warning('Standort nicht verfügbar.'); }
    });

    // Geocoding-Suche
    const locInput = document.getElementById('rk-map-loc');
    let _geoDebounce;
    locInput?.addEventListener('keydown', e => {
      if (e.key !== 'Enter') return;
      clearTimeout(_geoDebounce);
      _geocodeAndFly(locInput.value.trim());
    });
    locInput?.addEventListener('input', () => {
      clearTimeout(_geoDebounce);
      const q = locInput.value.trim();
      if (q.length < 3) return;
      _geoDebounce = setTimeout(() => _geocodeAndFly(q), 800);
    });
  }

  async function _geocodeAndFly(query) {
    if (!query || !_searchMap) return;
    try {
      const r = await fetch(
        `https://nominatim.openstreetmap.org/search?q=${encodeURIComponent(query)}&format=json&limit=1&accept-language=de`,
        { cache: 'no-store' }
      );
      const data = await r.json();
      if (!data.length) { UI.toast.info('Ort nicht gefunden.'); return; }
      const { lat, lon, boundingbox } = data[0];
      if (boundingbox) {
        _searchMap.fitBounds([[+boundingbox[0], +boundingbox[2]], [+boundingbox[1], +boundingbox[3]]],
          { maxZoom: 14 });
      } else {
        _searchMap.setView([+lat, +lon], 13);
      }
    } catch { UI.toast.warning('Suche fehlgeschlagen.'); }
  }

  function _renderRoutesOnMap() {
    if (!_searchMap || !window.L) return;

    // Alte Linien entfernen
    _searchLines.forEach(({ line }) => line.remove());
    _searchLines.clear();

    const hint = document.getElementById('rk-map-hint');

    _data.forEach(route => {
      const pts = (route.preview_track || []).map(p => [p.lat, p.lon]);
      if (pts.length < 2) return;

      const line = L.polyline(pts, {
        color: '#C4843A', weight: 4, opacity: 0.75,
      }).addTo(_searchMap);

      // Start-/End-Marker
      const startM = L.circleMarker(pts[0], {
        radius: 6, color: '#22C55E', fillColor: '#22C55E', fillOpacity: 1, weight: 1.5
      }).addTo(_searchMap);
      const endM = L.circleMarker(pts[pts.length - 1], {
        radius: 6, color: '#EF4444', fillColor: '#EF4444', fillOpacity: 1, weight: 1.5
      }).addTo(_searchMap);

      // Tooltip mit Namen und Distanz
      const tip = `<b>${UI.escape(route.name)}</b>${route.distanz_km ? ` · ${route.distanz_km.toFixed(1)} km` : ''}`;
      line.bindTooltip(tip, { sticky: true, className: 'rk-map-tooltip' });

      // Hover-Highlight
      line.on('mouseover', () => line.setStyle({ color: '#e67e22', weight: 6, opacity: 1 }));
      line.on('mouseout',  () => line.setStyle({ color: '#C4843A', weight: 4, opacity: 0.75 }));

      // Klick → Detail-Modal (Karte bleibt im Hintergrund erhalten)
      const onClick = () => {
        if (hint) hint.textContent = `Lädt „${route.name}"…`;
        _openDetail(route.id).finally(() => {
          if (hint) hint.textContent = 'Route antippen um Details zu sehen';
        });
      };
      line.on('click', onClick);
      startM.on('click', onClick);

      _searchLines.set(route.id, { line, startM, endM });
    });

    // Wenn Routen vorhanden: Karte auf alle Routes zoomen (nur beim ersten Mal)
    if (_data.length && _searchLines.size && !_userPos) {
      const allPts = [..._searchLines.values()].flatMap(({ line }) => line.getLatLngs());
      if (allPts.length) {
        try { _searchMap.fitBounds(L.latLngBounds(allPts), { padding: [20, 20], maxZoom: 14 }); }
        catch {}
      }
    }
  }

  // ----------------------------------------------------------
  // Daten
  // ----------------------------------------------------------
  async function _loadData() {
    try {
      _data = await API.routes.list();
      // "Meine Routen"-Filter nur zeigen wenn eingeloggt und im Mine-Modus
      if (_appState.user && _browseMode === 'mine') {
        document.getElementById('rk-mine-group')?.style.setProperty('display', '');
      }
      // Standort-abhängiger Filter im Entdecken-Modus
      if (_browseMode === 'discover' && _userPos) {
        document.getElementById('rk-nearby-group')?.style.setProperty('display', '');
      }
      _applyFilter();
    } catch (err) {
      document.getElementById('rk-grid').innerHTML =
        `<p style="color:var(--c-danger);padding:var(--space-6)">Fehler: ${UI.escape(err.message)}</p>`;
    }
  }

  // ----------------------------------------------------------
  // Filter
  // ----------------------------------------------------------
  const DOG_ORDER = { premium: 4, sehr_gut: 3, gut: 2, eingeschränkt: 1 };

  function _geoDistKm(r) {
    if (!_userPos || !r.start_lat || !r.start_lon) return Infinity;
    const R = 6371, dLat = (r.start_lat - _userPos.lat) * Math.PI / 180;
    const dLon = (r.start_lon - _userPos.lon) * Math.PI / 180;
    const a = Math.sin(dLat/2)**2 +
              Math.cos(_userPos.lat*Math.PI/180) * Math.cos(r.start_lat*Math.PI/180) *
              Math.sin(dLon/2)**2;
    return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
  }

  function _applyFilter() {
    let list = [..._data];

    // Browse-Modus-Filter
    if (_browseMode === 'mine' && _appState.user) {
      list = list.filter(r => r.user_id === _appState.user.id);
    } else if (_browseMode === 'discover') {
      list = list.filter(r => r.is_public === true);
    }

    if (_search)     list = list.filter(r =>
      (r.name||'').toLowerCase().includes(_search) ||
      (r.beschreibung||'').toLowerCase().includes(_search) ||
      (r.user_name||'').toLowerCase().includes(_search));
    if (_difficulty) list = list.filter(r => r.schwierigkeit === _difficulty);
    if (_terrain)    list = list.filter(r => r.untergrund    === _terrain);
    if (_onlyMine && _appState.user && _browseMode === 'mine')
      list = list.filter(r => r.user_id === _appState.user.id);

    if (_browseMode === 'discover') {
      list.sort((a, b) => _geoDistKm(a) - _geoDistKm(b));
    } else if (_sortBy === 'distance') list.sort((a,b) => (b.distanz_km||0) - (a.distanz_km||0));
    else if (_sortBy === 'rating') list.sort((a,b) => (b.bewertung||0) - (a.bewertung||0));
    else if (_sortBy === 'dog')    list.sort((a,b) =>
      (DOG_ORDER[b.hunde_tauglichkeit]||0) - (DOG_ORDER[a.hunde_tauglichkeit]||0));

    _filtered = list;
    _renderGrid();
    if (_viewMode === 'map' && _searchMap) _renderRoutesOnMap();
  }

  // ----------------------------------------------------------
  // Grid
  // ----------------------------------------------------------
  function _renderGrid() {
    const grid = document.getElementById('rk-grid');
    if (!grid) return;
    if (!_filtered.length) {
      if (_data.length) {
        // Filter aktiv aber kein Ergebnis
        const emptyMsg = _search
          ? `Keine Routen gefunden für „${UI.escape(_search)}".`
          : 'Keine Routen passen zu deinen Filtern.';
        grid.innerHTML = `<div class="rk-empty">
          <div class="rk-empty-icon">🔍</div>
          <p>${emptyMsg}</p>
          <button class="btn btn-secondary" id="rk-empty-reset">Filter zurücksetzen</button>
        </div>`;
        document.getElementById('rk-empty-reset')?.addEventListener('click', () => {
          _search = ''; _difficulty = ''; _terrain = ''; _sortBy = 'newest'; _onlyMine = false;
          document.getElementById('rk-search').value = '';
          document.querySelectorAll('.rk-chip').forEach(c => c.classList.remove('active'));
          document.querySelectorAll('.rk-chip[data-val=""]').forEach(c => c.classList.add('active'));
          document.querySelector('.rk-chip[data-val="newest"]')?.classList.add('active');
          _updateFilterBadge();
          _applyFilter();
        });
      } else if (_browseMode === 'discover') {
        // Entdecken: keine fremden Routen vorhanden
        grid.innerHTML = `<div class="rk-empty">
          <div class="rk-empty-icon">${UI.icon('compass')}</div>
          <h3 class="rk-empty-title">Noch keine öffentlichen Routen</h3>
          <p class="rk-empty-text">Andere Nutzer haben noch keine Routen geteilt. Sei der Erste!</p>
        </div>`;
      } else {
        // Noch gar keine eigenen Routen
        grid.innerHTML = UI.emptyState({
          icon:   UI.icon('map-trifold'),
          title:  'Noch keine Routen',
          text:   'Zeichne Lieblingsrouten auf oder importiere GPX-Dateien. Teile Routen mit Freunden.',
          action: `<button class="btn btn-primary" id="rk-empty-rec">${UI.icon('path')} Route aufzeichnen</button>`,
        });
        document.getElementById('rk-empty-rec')?.addEventListener('click', () => {
          App.navigate('map');
          setTimeout(() => window.Page_map?.startRecording?.(), 600);
        });
      }
      return;
    }
    // Alte Mini-Maps zerstören bevor DOM neu geschrieben wird
    _miniMaps.forEach(m => m.remove());
    _miniMaps.clear();

    grid.innerHTML = _filtered.map(r => _cardHTML(r)).join('');
    _initMiniMaps();
    grid.querySelectorAll('.rk-card').forEach(card => {
      card.addEventListener('click', e => {
        if (e.target.closest('.rk-stars,.rk-dl-btn')) return;
        _openDetail(parseInt(card.dataset.id));
      });
    });
    grid.querySelectorAll('.rk-star').forEach(star => {
      star.addEventListener('click', e => {
        e.stopPropagation();
        _rateRoute(parseInt(star.dataset.id), parseFloat(star.dataset.val));
      });
    });
    grid.querySelectorAll('.rk-dl-btn').forEach(btn => {
      btn.addEventListener('click', e => {
        e.stopPropagation();
        _downloadGpx(parseInt(btn.dataset.id));
      });
    });
  }

  // ----------------------------------------------------------
  // Karte HTML
  // ----------------------------------------------------------
  function _cardHTML(r) {
    const isDiscover = _browseMode === 'discover';
    const privBadge = !r.is_public ? `<span class="rk-badge rk-badge--private">${UI.icon('lock')} Privat</span>` : '';
    const diffLabel = DIFFICULTY_LABEL[r.schwierigkeit] || '';
    const terrain   = TERRAIN_LABEL[r.untergrund] || '';
    const paws      = HUNDE_LABEL[r.hunde_tauglichkeit] || '';
    const dist      = r.distanz_km ? `${r.distanz_km.toFixed(1)} km` : '';
    const dur       = r.dauer_min  ? _fmtDur(r.dauer_min) : '';
    const firstPhoto = (r.foto_urls || [])[0];
    const previewContent = firstPhoto
      ? `<img src="${UI.escape(firstPhoto)}" style="width:100%;height:100%;object-fit:cover">`
      : `<div class="rk-mini-map" data-id="${r.id}"
              data-track='${JSON.stringify(r.preview_track||[])}'
              style="width:100%;height:100%"></div>`;

    const authorLine = isDiscover
      ? `<div class="rk-card-creator">${UI.icon('user')} ${UI.escape(r.user_name||'Anonym')}</div>`
      : '';

    return `
      <div class="rk-card" data-id="${r.id}">
        <div class="rk-card-preview">${previewContent}</div>
        <div class="rk-card-body">
          ${authorLine}
          <div class="rk-card-name">${UI.escape(r.name)}</div>
          <div style="display:flex;flex-wrap:wrap;gap:5px;margin:var(--space-2) 0">
            ${dist ? _pill(dist, 'rgba(107,114,128,0.10)','#9ca3af','rgba(107,114,128,0.30)') : ''}
            ${dur  ? _pill(dur,  'rgba(107,114,128,0.10)','#9ca3af','rgba(107,114,128,0.30)') : ''}
            ${diffLabel ? _pill(diffLabel,
                ({leicht:'rgba(22,163,74,0.10)',mittel:'rgba(234,179,8,0.10)',anspruchsvoll:'rgba(220,38,38,0.10)'})[r.schwierigkeit]||'rgba(107,114,128,0.10)',
                ({leicht:'#4ade80',mittel:'#facc15',anspruchsvoll:'#f87171'})[r.schwierigkeit]||'#9ca3af',
                ({leicht:'rgba(22,163,74,0.30)',mittel:'rgba(234,179,8,0.30)',anspruchsvoll:'rgba(220,38,38,0.30)'})[r.schwierigkeit]||'rgba(107,114,128,0.30)') : ''}
            ${r.hunde_tauglichkeit ? _pill(HUNDE_TEXT[r.hunde_tauglichkeit]||'', 'rgba(234,179,8,0.10)','#facc15','rgba(234,179,8,0.30)') : ''}
            ${!isDiscover && !r.is_public ? _pill('Privat','rgba(59,130,246,0.10)','#60a5fa','rgba(59,130,246,0.30)') : ''}
          </div>
          <div class="rk-card-footer">
            <div class="rk-stars">${_starsHTML(r.id, r.bewertung||0, r.anz_bewertungen||0)}</div>
            <div class="rk-card-actions">
              ${isDiscover ? '' : `<span class="rk-card-author">${UI.escape(r.user_name||'Anonym')}</span>`}
              <button class="rk-dl-btn" data-id="${r.id}">${UI.icon('download-simple')} GPX</button>
            </div>
          </div>
        </div>
      </div>`;
  }

  function _starsHTML(id, avg, count) {
    const stars = [1,2,3,4,5].map(n =>
      `<span class="rk-star${n<=Math.round(avg)?' filled':''}" data-id="${id}" data-val="${n}">★</span>`
    ).join('');
    return stars + (count > 0 ? `<span class="rk-star-count">${avg.toFixed(1)} (${count})</span>` : '');
  }

  // ----------------------------------------------------------
  // Mini-Karten (OSM-Tiles via Leaflet, lazy per IntersectionObserver)
  // ----------------------------------------------------------
  function _initMiniMaps() {
    const init = () => {
      const obs = new IntersectionObserver((entries) => {
        entries.forEach(entry => {
          if (!entry.isIntersecting) return;
          obs.unobserve(entry.target);
          _buildMiniMap(entry.target);
        });
      }, { rootMargin: '150px' });
      document.querySelectorAll('.rk-mini-map').forEach(el => obs.observe(el));
    };

    if (window.L) { init(); return; }
    // Leaflet noch am Laden — kurz pollen
    let tries = 0;
    const poll = setInterval(() => {
      if (window.L || ++tries > 30) { clearInterval(poll); if (window.L) init(); }
    }, 100);
  }

  function _buildMiniMap(el) {
    const track = JSON.parse(el.dataset.track || '[]');
    const routeId = parseInt(el.dataset.id);
    if (track.length < 2) {
      el.innerHTML = '<div class="rk-preview-empty">🗺️</div>';
      return;
    }
    const lls = track.map(p => [p.lat, p.lon]);
    const m = L.map(el, {
      zoomControl: false, attributionControl: false,
      dragging: false, touchZoom: false, scrollWheelZoom: false,
      doubleClickZoom: false, keyboard: false, boxZoom: false,
    });
    L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 17 }).addTo(m);
    const poly = L.polyline(lls, { color: '#C4843A', weight: 3, opacity: 0.9 }).addTo(m);
    L.circleMarker(lls[0],      { radius: 5, color: '#22C55E', fillColor: '#22C55E', fillOpacity: 1, weight: 1.5 }).addTo(m);
    L.circleMarker(lls.at(-1),  { radius: 5, color: '#EF4444', fillColor: '#EF4444', fillOpacity: 1, weight: 1.5 }).addTo(m);
    m.fitBounds(poly.getBounds(), { padding: [8, 8] });
    _miniMaps.set(routeId, m);
  }

  // ----------------------------------------------------------
  // SVG-Vorschau (Fallback, wird nicht mehr direkt genutzt)
  // ----------------------------------------------------------
  function _svgPreview(track) {
    if (!track || track.length < 2)
      return `<div class="rk-preview-empty">🗺️</div>`;
    const lats = track.map(p=>p.lat), lons = track.map(p=>p.lon);
    const minLat=Math.min(...lats), maxLat=Math.max(...lats);
    const minLon=Math.min(...lons), maxLon=Math.max(...lons);
    const latR=maxLat-minLat||0.0001, lonR=maxLon-minLon||0.0001;
    const W=200,H=120,PAD=10;
    const pts = track.map(p=>{
      const x=(PAD+(p.lon-minLon)/lonR*(W-2*PAD)).toFixed(1);
      const y=(H-PAD-(p.lat-minLat)/latR*(H-2*PAD)).toFixed(1);
      return `${x},${y}`;
    }).join(' ');
    const [sx,sy]=pts.split(' ')[0].split(',');
    const [ex,ey]=pts.split(' ').at(-1).split(',');
    return `<svg viewBox="0 0 ${W} ${H}" xmlns="http://www.w3.org/2000/svg" style="width:100%;height:100%;display:block">
      <rect width="${W}" height="${H}" fill="#e8f0e8"/>
      <polyline points="${pts}" fill="none" stroke="#C4843A" stroke-width="3"
                stroke-linecap="round" stroke-linejoin="round" opacity="0.9"/>
      <circle cx="${sx}" cy="${sy}" r="5" fill="#22C55E" stroke="#fff" stroke-width="1.5"/>
      <circle cx="${ex}" cy="${ey}" r="5" fill="#EF4444" stroke="#fff" stroke-width="1.5"/>
    </svg>`;
  }

  // Button-Hilfsfunktion für Header-Buttons (cache-unabhängig)
  const _btnStyle = (primary = false) =>
    `flex:1;display:flex;align-items:center;justify-content:center;gap:6px;` +
    `height:46px;padding:0 16px;font-size:14px;font-weight:600;` +
    `border-radius:10px;border:1.5px solid ${primary ? 'var(--c-primary)' : 'var(--c-border)'};` +
    `background:${primary ? 'var(--c-primary)' : 'var(--c-surface)'};` +
    `color:${primary ? '#fff' : 'var(--c-text)'};white-space:nowrap;box-sizing:border-box;cursor:pointer;`;

  // Pill-Hilfsfunktionen (inline styles — unabhängig vom CSS-Cache)
  const _pillStyle = (bg, color, border) =>
    `display:inline-flex;align-items:center;font-size:10px;font-weight:600;` +
    `padding:2px 8px;border-radius:999px;white-space:nowrap;` +
    `background:${bg};color:${color};border:1px solid ${border};`;
  const _pill = (text, bg, color, border) =>
    `<span style="${_pillStyle(bg, color, border)}">${text}</span>`;

  // ----------------------------------------------------------
  // Detail-Modal
  // ----------------------------------------------------------
  // ----------------------------------------------------------
  // Navigation-Overlay
  // ----------------------------------------------------------
  async function _openNavOverlay(route) {
    const track = route.gps_track || [];
    if (track.length < 2) return;

    _navMaxIdx = 0;
    _navLastBearing = null;
    _navWalkMeta = { routeId: route.id, totalKm: route.distanz_km || 0, trackLen: track.length };

    // Kompass-Permission iOS 13+ — muss synchron in User-Gesture sein
    if (typeof DeviceOrientationEvent?.requestPermission === 'function') {
      try { await DeviceOrientationEvent.requestPermission(); } catch {}
    }

    await UI.loadLeaflet?.() ?? Promise.resolve();

    const ovl = document.createElement('div');
    ovl.id = 'rk-nav-ovl';
    ovl.style.cssText = 'position:fixed;inset:0;z-index:850;display:flex;flex-direction:column;background:var(--c-bg)';
    ovl.innerHTML = `
      <div style="display:flex;align-items:center;justify-content:space-between;
                  padding:max(env(safe-area-inset-top,44px),44px) 16px 8px;
                  background:var(--c-surface);border-bottom:1px solid var(--c-border);flex-shrink:0">
        <button id="rk-nav-back" style="background:none;border:none;color:var(--c-primary);font-size:16px;cursor:pointer;padding:4px 0">← Zurück</button>
        <span style="font-weight:700;font-size:14px;flex:1;text-align:center;margin:0 8px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${UI.escape(route.name)}</span>
        <button id="rk-nav-center-btn" style="background:none;border:none;color:var(--c-primary);cursor:pointer;padding:4px 6px;display:flex;align-items:center" title="Auf Standort zentrieren">${UI.icon('crosshair')}</button>
      </div>

      <div id="rk-nav-map" style="flex:1;min-height:0"></div>

      <div id="rk-nav-elev" style="height:64px;background:var(--c-surface);border-top:1px solid var(--c-border);flex-shrink:0;display:none"></div>

      <div style="padding:10px 16px;background:var(--c-surface);border-top:1px solid var(--c-border);flex-shrink:0">
        <div style="display:flex;justify-content:space-around;text-align:center;margin-bottom:8px">
          <div>
            <div style="font-size:9px;color:var(--c-text-secondary);text-transform:uppercase;letter-spacing:.06em">Fortschritt</div>
            <div id="rk-nav-pct" style="font-size:20px;font-weight:700">0%</div>
          </div>
          <div>
            <div style="font-size:9px;color:var(--c-text-secondary);text-transform:uppercase;letter-spacing:.06em">Verbleibend</div>
            <div id="rk-nav-remain" style="font-size:20px;font-weight:700">– km</div>
          </div>
          <div>
            <div style="font-size:9px;color:var(--c-text-secondary);text-transform:uppercase;letter-spacing:.06em">Zur Route</div>
            <div id="rk-nav-offdist" style="font-size:20px;font-weight:700">– m</div>
          </div>
        </div>
        <div id="rk-nav-offwarn" style="display:none;text-align:center;padding:4px 8px;border-radius:6px;
             background:rgba(220,38,38,0.10);color:#f87171;font-size:12px;font-weight:600;margin-bottom:6px">
          ⚠️ Du hast die Route verlassen
        </div>
        <div style="height:4px;background:var(--c-border);border-radius:2px;overflow:hidden">
          <div id="rk-nav-progbar" style="height:100%;width:0%;background:var(--c-primary);border-radius:2px;transition:width .5s"></div>
        </div>
      </div>

      <!-- Aktions-Buttons -->
      <div style="display:flex;gap:8px;padding:8px 16px calc(8px + env(safe-area-inset-bottom,0px));
                  background:var(--c-surface);border-top:1px solid var(--c-border);flex-shrink:0">
        <button id="rk-nav-pois" class="btn btn-secondary btn-sm" style="flex:1">${UI.icon('map-pin')} POIs</button>
        <button id="rk-nav-rate" class="btn btn-secondary btn-sm" style="flex:1">${UI.icon('star')} Bewerten</button>
        <button id="rk-nav-feedback" class="btn btn-secondary btn-sm" style="flex:1">${UI.icon('chat-circle-dots')} Feedback</button>
      </div>

      <!-- Dim-Overlay -->
      <div id="rk-nav-dim" style="display:none;position:fixed;inset:0;z-index:950;background:#000;
           flex-direction:column;align-items:center;justify-content:center;color:#fff;
           touch-action:none;user-select:none;-webkit-user-select:none">
        <div style="display:flex;flex-direction:column;align-items:center;gap:4px;opacity:.45;margin-bottom:28px">
          <div style="font-size:14px"><span id="rk-nav-dim-pct">0%</span><span style="margin:0 8px">·</span><span id="rk-nav-dim-remain">– km</span></div>
          <div id="rk-nav-dim-label" style="font-size:11px;opacity:.7">verbleibend</div>
        </div>
        <!-- Navigation-Pfeil (dreht sich zum nächsten Routenpunkt) -->
        <svg id="rk-nav-dim-arrow" width="120" height="120" viewBox="0 0 100 100"
             style="margin-bottom:28px;transform:rotate(0deg);transform-origin:50% 50%;transition:transform .4s ease">
          <!-- Einfacher Navi-Pfeil, spitze zeigt nach oben (0° = Nord) -->
          <path d="M50,8 L88,90 L50,73 L12,90 Z"
                fill="rgba(255,255,255,0.15)"
                stroke="rgba(255,255,255,0.9)"
                stroke-width="3.5"
                stroke-linejoin="round"
                stroke-linecap="round"/>
        </svg>
        <!-- 2-Sek-Halten-Ring -->
        <svg width="56" height="56" viewBox="0 0 56 56">
          <circle cx="28" cy="28" r="24" fill="none" stroke="rgba(255,255,255,.12)" stroke-width="2.5"/>
          <circle id="rk-nav-dim-prog" cx="28" cy="28" r="24" fill="none" stroke="rgba(255,255,255,.7)" stroke-width="2.5"
            stroke-dasharray="150.8" stroke-dashoffset="150.8" stroke-linecap="round"
            transform="rotate(-90 28 28)" style="transition:none"/>
        </svg>
        <div style="font-size:11px;opacity:.3;margin-top:8px">2 Sek. halten</div>
      </div>
    `;
    document.body.appendChild(ovl);
    _navOvl = ovl;

    // Kompass-Listener (Pfeil dreht sich mit Geräteausrichtung)
    const _updateDimArrow = () => {
      const el = document.getElementById('rk-nav-dim-arrow');
      if (!el || _navLastBearing == null) return;
      let compassHeading = null;
      if (_navCompassHeading != null) compassHeading = _navCompassHeading;
      const rot = compassHeading != null
        ? _navLastBearing - compassHeading   // relativ zur Geräteausrichtung
        : _navLastBearing;                   // absolut (Nordoben-Fallback)
      el.style.transform = `rotate(${rot.toFixed(1)}deg)`;
    };
    const _onOrientation = (e) => {
      let raw = null;
      if (e.webkitCompassHeading != null) raw = e.webkitCompassHeading;
      else if (e.alpha != null)           raw = (360 - e.alpha) % 360;
      if (raw == null) return;
      // Exponential-Moving-Average mit Wrap-Around-Behandlung
      if (_navHeadingSmoothed == null) {
        _navHeadingSmoothed = raw;
      } else {
        const diff = ((raw - _navHeadingSmoothed + 540) % 360) - 180;
        _navHeadingSmoothed = (_navHeadingSmoothed + 0.12 * diff + 360) % 360;
      }
      _navCompassHeading = _navHeadingSmoothed;
      _updateDimArrow();
    };
    window.addEventListener('deviceorientation', _onOrientation, true);
    _navOrientCleanup = () => window.removeEventListener('deviceorientation', _onOrientation, true);

    // Karte initialisieren
    const mapEl = document.getElementById('rk-nav-map');
    const mid = track[Math.floor(track.length / 2)];
    _navMap = L.map(mapEl, { zoomControl: false, attributionControl: false })
      .setView([mid.lat, mid.lon], 15);
    L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 19 }).addTo(_navMap);

    // Route-Polylines: erledigt (grün) + ausstehend (orange)
    const doneLine   = L.polyline([], { color: '#22c55e', weight: 5, opacity: 0.85 }).addTo(_navMap);
    const remainLine = L.polyline(track.map(p => [p.lat, p.lon]), { color: '#f97316', weight: 5, opacity: 0.9 }).addTo(_navMap);
    _navMap.fitBounds(remainLine.getBounds(), { padding: [20, 20] });
    _addRouteArrows(_navMap, track, '#3b82f6');

    // Start/End-Marker (als Variable damit Reverse sie neu setzen kann)
    const mkPin = (p, color) => L.circleMarker([p.lat, p.lon], {
      radius: 8, color: '#fff', weight: 2, fillColor: color, fillOpacity: 1
    }).addTo(_navMap);
    let startPin = mkPin(track[0], '#22c55e');
    let endPin   = mkPin(track[track.length - 1], '#ef4444');

    // Live-Position-Marker
    let locMarker = null;

    // POIs laden und als kleine Kreis-Marker einfügen
    _loadNearbyPois(track).then(pois => {
      _navPois = pois;
      pois.forEach(poi => {
        const svgIcon = poi._svgIcon || 'map-pin';
        const color   = poi._color   || '#6b7280';
        const icon = L.divIcon({
          className: '',
          html: `<div style="background:${color};color:#fff;width:32px;height:32px;border-radius:50%;
                 display:flex;align-items:center;justify-content:center;
                 box-shadow:0 2px 5px rgba(0,0,0,0.35);border:2px solid rgba(255,255,255,.7)">
                 <svg style="width:16px;height:16px;fill:currentColor" viewBox="0 0 256 256" aria-hidden="true">
                   <use href="/icons/phosphor.svg#${svgIcon}"></use>
                 </svg></div>`,
          iconSize:   [32, 32],
          iconAnchor: [16, 16],
        });
        L.marker([poi.lat, poi.lon], { icon })
          .bindTooltip(poi.name || poi._label, { direction: 'top', offset: [0, -16] })
          .bindPopup(`<strong>${UI.escape(poi.name||poi._label)}</strong>
            ${poi.phone ? `<br>📞 <a href="tel:${UI.escape(poi.phone)}">${UI.escape(poi.phone)}</a>` : ''}
            ${poi.opening_hours ? `<br>🕐 ${UI.escape(poi.opening_hours)}` : ''}`)
          .addTo(_navMap);
      });
    }).catch(() => {});

    // Höhenprofil laden
    API.routes.elevation(route.id).then(data => {
      const elevs = data.elevations || [];
      if (elevs.length < 2) return;
      const elevEl = document.getElementById('rk-nav-elev');
      if (!elevEl) return;
      elevEl.style.display = '';
      elevEl.innerHTML = _buildElevationSVG(elevs);
    }).catch(() => {});

    // Hilfsfunktionen
    const _navHaversine = (a, b) => _haversineKm(a.lat, a.lon, b.lat, b.lon);

    const _closestIdx = (lat, lon) => {
      let best = 0, bestD = Infinity;
      track.forEach((p, i) => {
        const d = _haversineKm(lat, lon, p.lat, p.lon);
        if (d < bestD) { bestD = d; best = i; }
      });
      return best;
    };

    const _remainingKm = (fromIdx) => {
      let d = 0;
      for (let i = fromIdx; i < track.length - 1; i++)
        d += _navHaversine(track[i], track[i+1]);
      return d;
    };

    const _bearingTo = (a, b) => {
      const φ1 = a.lat * Math.PI / 180, φ2 = b.lat * Math.PI / 180;
      const Δλ = (b.lon - a.lon) * Math.PI / 180;
      const y = Math.sin(Δλ) * Math.cos(φ2);
      const x = Math.cos(φ1) * Math.sin(φ2) - Math.sin(φ1) * Math.cos(φ2) * Math.cos(Δλ);
      return (Math.atan2(y, x) * 180 / Math.PI + 360) % 360;
    };

    const _updateStats = (idx, distToRoute, userLat, userLon) => {
      if (idx > _navMaxIdx) _navMaxIdx = idx;
      const pct = Math.round(idx / (track.length - 1) * 100);
      const rem = _remainingKm(idx);
      document.getElementById('rk-nav-pct').textContent     = pct + '%';
      document.getElementById('rk-nav-progbar').style.width = pct + '%';
      document.getElementById('rk-nav-remain').textContent  = rem.toFixed(2) + ' km';
      document.getElementById('rk-nav-offdist').textContent = distToRoute < 1
        ? Math.round(distToRoute * 1000) + ' m' : distToRoute.toFixed(1) + ' km';
      // Screensaver: "zur Route" wenn weit weg, sonst "verbleibend"
      const offRoute = distToRoute > 0.5;
      document.getElementById('rk-nav-dim-pct').textContent    = offRoute ? '–' : pct + '%';
      document.getElementById('rk-nav-dim-remain').textContent = offRoute
        ? (distToRoute < 1 ? Math.round(distToRoute * 1000) + ' m' : distToRoute.toFixed(1) + ' km')
        : rem.toFixed(2) + ' km';
      const dimLabel = document.getElementById('rk-nav-dim-label');
      if (dimLabel) dimLabel.textContent = offRoute ? 'zur Route' : 'verbleibend';
      // Bearing zum nächsten Punkt aktualisieren → _onOrientation übernimmt das Rendern
      if (userLat != null && idx < track.length - 1) {
        _navLastBearing = _bearingTo({ lat: userLat, lon: userLon }, track[idx + 1]);
        _updateDimArrow();
      }
      const offWarn = document.getElementById('rk-nav-offwarn');
      if (distToRoute * 1000 > 50) {
        offWarn.style.display = '';
        if (navigator.vibrate) navigator.vibrate([200, 100, 200]);
      } else {
        offWarn.style.display = 'none';
      }
      // Polylines aktualisieren
      doneLine.setLatLngs(track.slice(0, idx + 1).map(p => [p.lat, p.lon]));
      remainLine.setLatLngs(track.slice(idx).map(p => [p.lat, p.lon]));
    };

    // GPS-Watch
    navigator.wakeLock?.request('screen').then(wl => { _navWakeLock = wl; }).catch(() => {});
    let _navFirstFix = true;
    _navWatchId = navigator.geolocation.watchPosition(pos => {
      const { latitude: lat, longitude: lon } = pos.coords;
      if (!locMarker) {
        locMarker = L.circleMarker([lat, lon], {
          radius: 10, color: '#fff', weight: 3, fillColor: '#3b82f6', fillOpacity: 1,
          className: 'rk-nav-loc-pulse'
        }).addTo(_navMap);
      } else {
        locMarker.setLatLng([lat, lon]);
      }

      // Nächsten Trackpunkt nur nutzen wenn User < 500m von der Route entfernt ist
      const closestIdx = _closestIdx(lat, lon);
      const distToRoute = _haversineKm(lat, lon, track[closestIdx].lat, track[closestIdx].lon);
      if (distToRoute < 0.5) {
        _navCurrentIdx = closestIdx;
      }
      // Karte beim ersten Fix zoomen — User-Position nur einschließen wenn < 20 km entfernt
      if (_navFirstFix) {
        _navFirstFix = false;
        try {
          const bounds = distToRoute < 20
            ? remainLine.getBounds().extend([lat, lon])
            : remainLine.getBounds();
          _navMap.fitBounds(bounds, { padding: [40, 40] });
        } catch {}
      }
      _updateStats(_navCurrentIdx, distToRoute, lat, lon);
    }, () => {}, { enableHighAccuracy: true, maximumAge: 3000 });

    // Dim-Modus
    const _navOnTouch = (e) => {
      if (document.getElementById('rk-nav-dim')?.contains(e.target)) return;
      _navResetInactTimer();
    };
    ovl.addEventListener('touchstart', _navOnTouch, { passive: true });
    ovl.addEventListener('pointerdown', _navOnTouch);
    _navResetInactTimer();

    const dim = document.getElementById('rk-nav-dim');
    let _lpTimer = null;
    const cancelLp = () => {
      clearTimeout(_lpTimer);
      const prog = document.getElementById('rk-nav-dim-prog');
      if (prog) { prog.style.transition = 'none'; prog.style.strokeDashoffset = '150.8'; }
    };
    dim.addEventListener('pointerdown', e => {
      e.stopPropagation();
      const prog = document.getElementById('rk-nav-dim-prog');
      if (prog) { prog.style.transition = 'stroke-dashoffset 2s linear'; prog.style.strokeDashoffset = '0'; }
      _lpTimer = setTimeout(() => {
        dim.style.display = 'none'; _navDimmed = false; _navResetInactTimer();
      }, 2000);
    });
    dim.addEventListener('pointerup', cancelLp);
    dim.addEventListener('pointercancel', cancelLp);
    dim.addEventListener('pointerleave', cancelLp);

    // Aktions-Buttons
    document.getElementById('rk-nav-back').addEventListener('click', _closeNav);

    document.getElementById('rk-nav-rate')?.addEventListener('click', () => {
      UI.modal.open({
        title: `${UI.icon('star')} Route bewerten`,
        body: `<div style="display:flex;gap:8px;justify-content:center;padding:var(--space-4) 0">
          ${[1,2,3,4,5].map(n =>
            `<button class="btn btn-secondary" data-stars="${n}"
                     style="font-size:1.4rem;padding:8px 12px">${'⭐'.repeat(n)}</button>`
          ).join('')}
        </div>`,
        footer: '',
      });
      document.querySelector('#modal-container .modal-body')?.addEventListener('click', async e => {
        const btn = e.target.closest('[data-stars]');
        if (!btn) return;
        try {
          await API.routes.rate(route.id, parseInt(btn.dataset.stars));
          UI.modal.close(); UI.toast.success('Bewertung gespeichert!');
        } catch (err) { UI.toast.error(err.message); }
      });
    });

    document.getElementById('rk-nav-feedback')?.addEventListener('click', () => {
      const body = `
        <p style="color:var(--c-text-secondary);font-size:var(--text-sm);margin-bottom:var(--space-3)">
          Dein Feedback wird direkt an den Route-Ersteller gesendet.
        </p>
        <textarea id="rk-nav-fb-text" class="form-control" rows="4"
                  placeholder="z.B. Der Weg nach links ist gesperrt…" maxlength="500"></textarea>`;
      const footer = `
        <button type="button" class="btn btn-secondary flex-1" onclick="UI.modal.close()">Abbrechen</button>
        <button type="button" class="btn btn-primary flex-1" id="rk-nav-fb-send">${UI.icon('paper-plane-tilt')} Senden</button>`;
      UI.modal.open({ title: `${UI.icon('chat-circle-dots')} Feedback senden`, body, footer });
      document.getElementById('rk-nav-fb-send')?.addEventListener('click', async () => {
        const text = document.getElementById('rk-nav-fb-text')?.value?.trim();
        if (!text || text.length < 5) { UI.toast.warning('Bitte etwas mehr schreiben.'); return; }
        try {
          await API.routes.feedback(route.id, text);
          UI.modal.close(); UI.toast.success('Feedback gesendet!');
        } catch (err) { UI.toast.error(err.message); }
      });
    });

    document.getElementById('rk-nav-center-btn')?.addEventListener('click', () => {
      if (locMarker) _navMap.setView(locMarker.getLatLng(), 16);
    });

    document.getElementById('rk-nav-pois')?.addEventListener('click', () => {
      if (!_navPois.length) { UI.toast.info('Keine POIs entlang dieser Route.'); return; }
      const byType = {};
      _navPois.forEach(p => {
        if (!byType[p._label]) byType[p._label] = { icon: p._icon, items: [] };
        byType[p._label].items.push(p);
      });
      const body = Object.entries(byType).map(([label, group]) => `
        <div style="margin-bottom:12px">
          <div style="font-size:12px;font-weight:700;color:var(--c-text-secondary);text-transform:uppercase;
               letter-spacing:.05em;margin-bottom:6px">${group.icon} ${UI.escape(label)}</div>
          ${group.items.map(p => `
            <div style="padding:8px 0;border-bottom:1px solid var(--c-border);display:flex;justify-content:space-between;align-items:flex-start">
              <div>
                <div style="font-weight:600;font-size:14px">${UI.escape(p.name||label)}</div>
                ${p.opening_hours ? `<div style="font-size:12px;color:var(--c-text-secondary)">🕐 ${UI.escape(p.opening_hours)}</div>` : ''}
                ${p.phone ? `<div style="font-size:12px;color:var(--c-text-secondary)">📞 <a href="tel:${UI.escape(p.phone)}" style="color:var(--c-primary)">${UI.escape(p.phone)}</a></div>` : ''}
              </div>
              <a href="${/iPad|iPhone|iPod/.test(navigator.userAgent)
                ? `maps://maps.apple.com/?q=${encodeURIComponent(p.name||label)}&ll=${p.lat},${p.lon}`
                : `https://www.google.com/maps/search/?api=1&query=${p.lat},${p.lon}`}"
                 target="_blank" style="flex-shrink:0;margin-left:8px;color:var(--c-primary);font-size:12px">Navi</a>
            </div>`).join('')}
        </div>`).join('');
      UI.modal.open({ title: `${UI.icon('map-pin')} POIs entlang der Route`, body, footer: `<button class="btn btn-secondary flex-1" onclick="UI.modal.close()">Schließen</button>` });
    });
  }

  function _navResetInactTimer() {
    if (_navDimmed) return;
    clearTimeout(_navInactTimer);
    _navInactTimer = setTimeout(() => {
      const dim = document.getElementById('rk-nav-dim');
      if (dim) { dim.style.display = 'flex'; _navDimmed = true; }
    }, 10000);
  }

  function _closeNav() {
    if (_navWatchId !== null) { navigator.geolocation.clearWatch(_navWatchId); _navWatchId = null; }
    if (_navInactTimer) { clearTimeout(_navInactTimer); _navInactTimer = null; }
    if (_navWakeLock)  { try { _navWakeLock.release(); } catch {} _navWakeLock = null; }
    if (_navWalkMeta && _navWalkMeta.trackLen > 1) {
      const pct = Math.round(_navMaxIdx / (_navWalkMeta.trackLen - 1) * 100);
      if (pct >= 50) {
        const walkedKm = Math.round((_navMaxIdx / (_navWalkMeta.trackLen - 1)) * _navWalkMeta.totalKm * 100) / 100;
        API.routes.walked(_navWalkMeta.routeId, walkedKm, pct)
          .then(res => { if (res.new_badges?.length) UI.toast.success(`🏅 Neues Badge: ${res.new_badges[0].name}!`); })
          .catch(() => {});
      }
    }
    if (_navOrientCleanup) { _navOrientCleanup(); _navOrientCleanup = null; }
    _navWalkMeta = null;
    _navMaxIdx = 0;
    _navLastBearing = null;
    _navCompassHeading = null;
    _navHeadingSmoothed = null;
    if (_navMap) { _navMap.remove(); _navMap = null; }
    if (_navOvl) { _navOvl.remove(); _navOvl = null; }
    _navDimmed = false;
    _navPois = [];
  }

  function _buildElevationSVG(points) {
    const alts = points.map(p => p.alt || 0);
    const minA = Math.min(...alts), maxA = Math.max(...alts);
    const range = maxA - minA || 1;
    const W = 800, H = 56, pad = 4;
    const x = (i) => pad + (i / (points.length - 1)) * (W - 2 * pad);
    const y = (a) => H - pad - ((a - minA) / range) * (H - 2 * pad);
    const pts = points.map((p, i) => `${x(i).toFixed(1)},${y(p.alt||0).toFixed(1)}`).join(' ');
    const fill = points.map((p, i) => `${x(i).toFixed(1)},${y(p.alt||0).toFixed(1)}`).join(' ') +
      ` ${x(points.length-1).toFixed(1)},${H} ${x(0).toFixed(1)},${H}`;
    return `<div style="position:relative;width:100%;height:100%">
      <svg viewBox="0 0 ${W} ${H}" xmlns="http://www.w3.org/2000/svg"
           style="width:100%;height:100%;display:block">
        <polygon points="${fill}" fill="var(--c-primary)" opacity="0.15"/>
        <polyline points="${pts}" fill="none" stroke="var(--c-primary)" stroke-width="2" stroke-linecap="round"/>
      </svg>
      <span style="position:absolute;left:5px;top:2px;font-size:10px;font-weight:600;color:var(--c-text-muted);
                   background:var(--c-surface);padding:0 2px;border-radius:2px;line-height:1.2">${Math.round(maxA)}m</span>
      <span style="position:absolute;left:5px;bottom:2px;font-size:10px;font-weight:600;color:var(--c-text-muted);
                   background:var(--c-surface);padding:0 2px;border-radius:2px;line-height:1.2">${Math.round(minA)}m</span>
    </div>`;
  }

  // ----------------------------------------------------------
  // Route kürzen (Datenschutz-Overlay)
  // ----------------------------------------------------------
  function _trimCalcKm(track) {
    let d = 0;
    for (let i = 1; i < track.length; i++)
      d += _haversineKm(track[i-1].lat, track[i-1].lon, track[i].lat, track[i].lon);
    return Math.round(d * 100) / 100;
  }

  async function _openTrimOverlay(route) {
    const fullTrack = route.gps_track || [];
    if (fullTrack.length < 4) return;

    let startIdx = 0;
    let endIdx   = fullTrack.length - 1;
    let clickMode = 'start'; // 'start' | 'end'

    const origKm  = route.original_km  ?? route.distanz_km  ?? 0;
    const origMin = route.original_dauer_min ?? route.dauer_min ?? 0;

    const ovl = document.createElement('div');
    ovl.id = 'rk-trim-ovl';
    ovl.style.cssText = 'position:fixed;inset:0;z-index:850;display:flex;flex-direction:column;background:var(--c-bg)';
    ovl.innerHTML = `
      <div style="position:sticky;top:0;z-index:10;display:flex;align-items:center;
                  justify-content:space-between;
                  padding-top:max(env(safe-area-inset-top,44px),44px);
                  padding-left:16px;padding-right:16px;padding-bottom:10px;
                  background:var(--c-surface);border-bottom:1px solid var(--c-border);flex-shrink:0">
        <button id="rk-trim-cancel" style="background:none;border:none;color:var(--c-primary);font-size:16px;cursor:pointer;padding:4px 0">← Abbrechen</button>
        <span style="font-weight:700;font-size:15px">Route kürzen</span>
        <button id="rk-trim-save" class="btn btn-primary btn-sm">${UI.icon('floppy-disk')} Speichern</button>
      </div>

      <div id="rk-trim-map" style="flex:1;min-height:0"></div>

      <div style="padding:16px;background:var(--c-surface);border-top:1px solid var(--c-border);flex-shrink:0">
        <!-- Modus-Toggle -->
        <div style="display:flex;gap:8px;margin-bottom:12px">
          <button id="rk-trim-mode-start" class="btn btn-sm btn-primary" style="flex:1">
            📍 Klick setzt: Start
          </button>
          <button id="rk-trim-mode-end" class="btn btn-sm btn-secondary" style="flex:1">
            📍 Klick setzt: Ende
          </button>
        </div>

        <!-- Slider Anfang -->
        <div style="margin-bottom:10px">
          <div style="display:flex;justify-content:space-between;font-size:12px;color:var(--c-text-secondary);margin-bottom:4px">
            <span>Anfang abschneiden</span>
            <span id="rk-trim-start-lbl">0 Punkte</span>
          </div>
          <input type="range" id="rk-trim-slider-start" min="0" max="${Math.floor(fullTrack.length/2)-1}" value="0"
                 style="width:100%;accent-color:var(--c-primary)">
        </div>

        <!-- Slider Ende -->
        <div style="margin-bottom:12px">
          <div style="display:flex;justify-content:space-between;font-size:12px;color:var(--c-text-secondary);margin-bottom:4px">
            <span>Ende abschneiden</span>
            <span id="rk-trim-end-lbl">0 Punkte</span>
          </div>
          <input type="range" id="rk-trim-slider-end" min="0" max="${Math.floor(fullTrack.length/2)-1}" value="0"
                 style="width:100%;accent-color:var(--c-danger)">
        </div>

        <!-- Stats -->
        <div id="rk-trim-stats" style="font-size:13px;color:var(--c-text-secondary);text-align:center"></div>
      </div>
    `;
    document.body.appendChild(ovl);

    // Map initialisieren
    await UI.loadLeaflet?.() ?? Promise.resolve();
    const mapEl = document.getElementById('rk-trim-map');
    const center = fullTrack[Math.floor(fullTrack.length/2)];
    const trimMap = L.map(mapEl, { zoomControl: false, attributionControl: false })
      .setView([center.lat, center.lon], 14);
    L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 19 }).addTo(trimMap);

    // Marker & Polylines
    let greyBefore = L.polyline([], { color: '#9ca3af', weight: 3, opacity: 0.5 }).addTo(trimMap);
    let activeLine = L.polyline([], { color: '#ef4444', weight: 5, opacity: 0.9 }).addTo(trimMap);
    let greyAfter  = L.polyline([], { color: '#9ca3af', weight: 3, opacity: 0.5 }).addTo(trimMap);

    const mkMarker = (lat, lon, color) => L.circleMarker([lat, lon], {
      radius: 9, color: '#fff', weight: 2.5, fillColor: color, fillOpacity: 1
    }).addTo(trimMap);

    let startMarker = mkMarker(fullTrack[0].lat, fullTrack[0].lon, '#22c55e');
    let endMarker   = mkMarker(fullTrack[fullTrack.length-1].lat, fullTrack[fullTrack.length-1].lon, '#ef4444');

    const update = () => {
      const slice = fullTrack.slice(startIdx, endIdx + 1);
      const lls   = (pts) => pts.map(p => [p.lat, p.lon]);
      greyBefore.setLatLngs(lls(fullTrack.slice(0, startIdx + 1)));
      activeLine.setLatLngs(lls(slice));
      greyAfter.setLatLngs(lls(fullTrack.slice(endIdx)));

      startMarker.setLatLng([fullTrack[startIdx].lat, fullTrack[startIdx].lon]);
      endMarker.setLatLng([fullTrack[endIdx].lat, fullTrack[endIdx].lon]);

      const newKm  = _trimCalcKm(slice);
      const pace   = origKm > 0 ? origMin / origKm : 10;
      const newMin = Math.max(1, Math.round(newKm * pace));

      document.getElementById('rk-trim-start-lbl').textContent = `${startIdx} Punkte`;
      document.getElementById('rk-trim-end-lbl').textContent   = `${fullTrack.length - 1 - endIdx} Punkte`;
      document.getElementById('rk-trim-stats').innerHTML =
        `Neue Länge: <strong>${newKm.toFixed(2)} km</strong> · ca. <strong>${newMin} min</strong>
         &nbsp;·&nbsp; <span style="color:var(--c-text-muted)">Original: ${origKm.toFixed(2)} km · ${origMin} min (bleibt angerechnet)</span>`;
    };
    update();
    trimMap.fitBounds(L.polyline(fullTrack.map(p => [p.lat, p.lon])).getBounds(), { padding: [20, 20] });

    // Nächsten Track-Punkt zu einem Klick finden
    const nearestIdx = (latlng) => {
      let best = 0, bestD = Infinity;
      fullTrack.forEach((p, i) => {
        const d = trimMap.distance(latlng, L.latLng(p.lat, p.lon));
        if (d < bestD) { bestD = d; best = i; }
      });
      return best;
    };

    // Karten-Klick
    trimMap.on('click', e => {
      const idx = nearestIdx(e.latlng);
      if (clickMode === 'start') {
        startIdx = Math.min(idx, endIdx - 1);
        document.getElementById('rk-trim-slider-start').value = startIdx;
      } else {
        endIdx = Math.max(idx, startIdx + 1);
        document.getElementById('rk-trim-slider-end').value = fullTrack.length - 1 - endIdx;
      }
      update();
    });

    // Slider
    document.getElementById('rk-trim-slider-start').addEventListener('input', e => {
      startIdx = Math.min(parseInt(e.target.value), endIdx - 1);
      update();
    });
    document.getElementById('rk-trim-slider-end').addEventListener('input', e => {
      endIdx = Math.max(fullTrack.length - 1 - parseInt(e.target.value), startIdx + 1);
      update();
    });

    // Modus-Toggle
    const modeStart = document.getElementById('rk-trim-mode-start');
    const modeEnd   = document.getElementById('rk-trim-mode-end');
    modeStart.addEventListener('click', () => {
      clickMode = 'start';
      modeStart.className = 'btn btn-sm btn-primary'; modeStart.style.flex = '1';
      modeEnd.className   = 'btn btn-sm btn-secondary'; modeEnd.style.flex = '1';
    });
    modeEnd.addEventListener('click', () => {
      clickMode = 'end';
      modeEnd.className   = 'btn btn-sm btn-primary'; modeEnd.style.flex = '1';
      modeStart.className = 'btn btn-sm btn-secondary'; modeStart.style.flex = '1';
    });

    // Abbrechen
    document.getElementById('rk-trim-cancel').addEventListener('click', () => {
      trimMap.remove(); ovl.remove();
    });

    // Speichern
    document.getElementById('rk-trim-save').addEventListener('click', async () => {
      const btn = document.getElementById('rk-trim-save');
      const trimmed = fullTrack.slice(startIdx, endIdx + 1);
      await UI.asyncButton(btn, async () => {
        const saved = await API.routes.trim(route.id, trimmed);
        // Lokalen State aktualisieren
        const idx = _data.findIndex(r => r.id === route.id);
        if (idx !== -1) Object.assign(_data[idx], saved);
        trimMap.remove(); ovl.remove();
        UI.toast.success('Route gekürzt — Originaldaten bleiben für die Statistik erhalten.');
        _applyFilter();
      });
    });
  }

  async function _openDetail(routeId) {
    let route;
    try { route = await API.routes.get(routeId); }
    catch (err) { UI.toast.error(err.message); return; }

    const isOwn  = _appState.user?.id === route.user_id;
    const track  = route.gps_track || [];
    const photos = route.foto_urls || [];
    const paws   = HUNDE_LABEL[route.hunde_tauglichkeit] || '';

    const photoGallery = photos.length ? `
      <div class="rk-photo-gallery">
        ${photos.map(u => `<img src="${UI.escape(u)}" class="rk-photo-thumb" onclick="window.open('${UI.escape(u)}','_blank')">`).join('')}
        ${isOwn ? `<label class="rk-photo-add" title="Foto hinzufügen">
          <span>+</span>
          <input type="file" id="rk-photo-input" accept="image/*" multiple style="display:none">
        </label>` : ''}
      </div>` :
      isOwn ? `<label class="rk-photo-add-empty">
        ${UI.icon('camera')} Foto hinzufügen
        <input type="file" id="rk-photo-input" accept="image/*" multiple style="display:none">
      </label>` : '';

    const body = `
      <div id="rk-detail-map" style="height:200px;border-radius:var(--radius-md);
           margin-bottom:var(--space-3);background:var(--c-surface-2)"></div>
      ${photoGallery}
      <div style="display:flex;flex-wrap:wrap;gap:6px;margin:var(--space-3) 0">
        ${route.distanz_km ? _pill(route.distanz_km.toFixed(1)+' km', 'rgba(107,114,128,0.10)','#9ca3af','rgba(107,114,128,0.30)') : ''}
        ${route.dauer_min  ? _pill(_fmtDur(route.dauer_min),           'rgba(107,114,128,0.10)','#9ca3af','rgba(107,114,128,0.30)') : ''}
        ${route.schwierigkeit ? _pill(DIFFICULTY_LABEL[route.schwierigkeit]||route.schwierigkeit,
            ({leicht:'rgba(22,163,74,0.10)',mittel:'rgba(234,179,8,0.10)',anspruchsvoll:'rgba(220,38,38,0.10)'})[route.schwierigkeit]||'rgba(107,114,128,0.10)',
            ({leicht:'#4ade80',mittel:'#facc15',anspruchsvoll:'#f87171'})[route.schwierigkeit]||'#9ca3af',
            ({leicht:'rgba(22,163,74,0.30)',mittel:'rgba(234,179,8,0.30)',anspruchsvoll:'rgba(220,38,38,0.30)'})[route.schwierigkeit]||'rgba(107,114,128,0.30)') : ''}
        ${route.hunde_tauglichkeit ? _pill(HUNDE_TEXT[route.hunde_tauglichkeit]||route.hunde_tauglichkeit,'rgba(234,179,8,0.10)','#facc15','rgba(234,179,8,0.30)') : ''}
        ${isOwn
          ? `<button type="button" id="rd-vis-pill" title="${route.is_public ? 'Auf Privat setzen' : 'Auf Öffentlich setzen'}"
                     style="${_pillStyle(route.is_public?'rgba(22,163,74,0.10)':'rgba(59,130,246,0.10)', route.is_public?'#4ade80':'#60a5fa', route.is_public?'rgba(22,163,74,0.30)':'rgba(59,130,246,0.30)')}cursor:pointer;">
               ${route.is_public ? 'Öffentlich' : 'Privat'}
             </button>`
          : _pill(route.is_public?'Öffentlich':'Privat', route.is_public?'rgba(22,163,74,0.10)':'rgba(59,130,246,0.10)', route.is_public?'#4ade80':'#60a5fa', route.is_public?'rgba(22,163,74,0.30)':'rgba(59,130,246,0.30)')
        }
      </div>
      ${route.beschreibung ? `<p style="color:var(--c-text-secondary);margin-bottom:var(--space-3)">${UI.escape(route.beschreibung)}</p>` : ''}
      <div id="rk-nearby" class="rk-nearby-section">
        <div style="color:var(--c-text-muted);font-size:var(--text-sm)">Lädt Orte entlang der Route…</div>
      </div>
      <div id="rk-rating-${route.id}"></div>
      <p style="color:var(--c-text-muted);font-size:0.75rem;margin-top:var(--space-2)">
        ${track.length} GPS-Punkte · von ${UI.escape(route.user_name||'Anonym')}
      </p>
    `;

    const _actionBtn = (id, icon, label, danger = false) =>
      `<button type="button" id="${id}"
               style="flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;
                      gap:4px;padding:var(--space-2) var(--space-1);min-height:56px;
                      border:1px solid var(--c-border);border-radius:var(--radius-md);
                      background:var(--c-surface);cursor:pointer;
                      color:${danger ? 'var(--c-danger)' : 'var(--c-text)'};
                      font-size:var(--text-xs);font-weight:var(--weight-semibold);
                      transition:background var(--transition-fast)">
         ${UI.icon(icon)}
         <span>${label}</span>
       </button>`;

    const ownerRow = isOwn ? `
      <div style="display:flex;gap:var(--space-2)">
        ${_actionBtn('rd-send-friend', 'paper-plane-tilt', 'Senden')}
        ${track.length >= 4 ? _actionBtn('rd-trim', 'pencil-simple', 'Kürzen') : ''}
        ${_actionBtn('rd-reverse', 'path', 'Umkehren')}
        ${_actionBtn('rd-del', 'trash', 'Löschen', true)}
      </div>` : '';

    const footer = `
      <div style="display:flex;flex-direction:column;gap:var(--space-2);width:100%">
        <div style="display:flex;gap:var(--space-2)">
          ${_actionBtn('rd-gpx',   'download-simple',  'GPX')}
          ${_actionBtn('rd-share', 'arrow-square-out', 'Teilen')}
          ${_actionBtn('rd-navi',  'map-pin',          'Navi')}
          ${_appState.user ? _actionBtn('rd-note', 'note-pencil', 'Notiz') : ''}
        </div>
        ${ownerRow}
        <button type="button" class="btn btn-primary w-full" id="rd-close">Schließen</button>
      </div>
    `;

    UI.modal.open({ title: `🥾 ${UI.escape(route.name)}`, body, footer });

    UI.ratingStars({
      containerId: `rk-rating-${route.id}`,
      targetType:  'route',
      targetId:    route.id,
      isLoggedIn:  !!_appState.user,
    });

    document.getElementById('rd-close')?.addEventListener('click', UI.modal.close);
    document.getElementById('rd-gpx')?.addEventListener('click', () => _downloadGpxDirect(route));

    // Teilen-Button
    document.getElementById('rd-share')?.addEventListener('click', async () => {
      const shareUrl = location.origin + '/#routes?id=' + route.id;
      const text = `${route.name} — ${(route.distanz_km||0).toFixed(1)} km`;
      if (navigator.share) {
        navigator.share({ title: route.name, text, url: shareUrl }).catch(() => {});
      } else {
        await navigator.clipboard.writeText(shareUrl);
        UI.toast.info('Link kopiert!');
      }
    });

    // Navi-Button
    document.getElementById('rd-navi')?.addEventListener('click', () => {
      if ((route.gps_track || []).length < 2) {
        UI.toast.warning('Keine GPS-Daten vorhanden.'); return;
      }
      UI.modal.close();
      _openNavOverlay(route);
    });

    // An Freund senden
    document.getElementById('rd-send-friend')?.addEventListener('click', () => _openSendToFriendModal(route));

    // Sichtbarkeit toggle — Pill im Body
    document.getElementById('rd-vis-pill')?.addEventListener('click', async () => {
      const pill = document.getElementById('rd-vis-pill');
      try {
        await API.routes.update(route.id, { is_public: !route.is_public });
        route.is_public = !route.is_public;
        if (pill) {
          pill.style.cssText = _pillStyle(
            route.is_public ? 'rgba(22,163,74,0.10)' : 'rgba(59,130,246,0.10)',
            route.is_public ? '#4ade80' : '#60a5fa',
            route.is_public ? 'rgba(22,163,74,0.30)' : 'rgba(59,130,246,0.30)') + 'cursor:pointer;';
          pill.innerHTML = route.is_public ? 'Öffentlich' : 'Privat';
          pill.title     = route.is_public ? 'Auf Privat setzen' : 'Auf Öffentlich setzen';
        }
        const r = _data.find(x => x.id === route.id);
        if (r) r.is_public = route.is_public;
        _applyFilter();
        UI.toast.success(route.is_public ? 'Route ist jetzt öffentlich.' : 'Route ist jetzt privat.');
      } catch (err) { UI.toast.error(err.message); }
    });

    // Umkehren
    document.getElementById('rd-reverse')?.addEventListener('click', async () => {
      try {
        await API.routes.reverse(route.id);
        route.gps_track = [...route.gps_track].reverse();
        // Karte neu aufbauen mit umgekehrtem Track
        const el = document.getElementById('rk-detail-map');
        if (el && window.L) {
          if (_detailMap) { _detailMap.remove(); _detailMap = null; }
          _detailMap = _buildDetailMap(el, route.gps_track);
        }
        UI.toast.success('Route dauerhaft umgekehrt');
      } catch (err) { UI.toast.error(err.message); }
    });

    // Löschen
    document.getElementById('rd-del')?.addEventListener('click', async () => {
      const ok = await UI.modal.confirm({
        title: 'Route löschen?', message: `„${route.name}" wird dauerhaft entfernt.`,
        confirmText: 'Löschen', danger: true,
      });
      if (!ok) return;
      try {
        await API.routes.delete(route.id);
        _data = _data.filter(r => r.id !== route.id);
        UI.modal.close();
        _applyFilter();
        UI.toast.success('Route gelöscht.');
      } catch (err) { UI.toast.error(err.message); }
    });

    // Route kürzen
    document.getElementById('rd-trim')?.addEventListener('click', () => {
      UI.modal.close();
      _openTrimOverlay(route);
    });

    // Foto-Upload
    document.getElementById('rk-photo-input')?.addEventListener('change', async e => {
      const files = Array.from(e.target.files || []);
      if (!files.length) return;
      try {
        for (const file of files) {
          const res = await API.routes.addPhoto(route.id, file);
          route.foto_urls = res.foto_urls;
        }
        UI.toast.success(files.length > 1 ? `${files.length} Fotos gespeichert!` : 'Foto gespeichert!');
        UI.modal.close();
        setTimeout(() => _openDetail(route.id), 200);
      } catch (err) { UI.toast.error(err.message); }
    });

    // Notiz-Button
    document.getElementById('rd-note')?.addEventListener('click', () => {
      const label = route.name || (route.distanz_km ? route.distanz_km.toFixed(1) + ' km' : 'Route');
      _openNoteModal('route', route.id, label, null);
    });

    // Mini-Map
    let _detailMap = null;
    setTimeout(() => {
      const el = document.getElementById('rk-detail-map');
      if (!el || !track.length) return;
      if (window.L) _detailMap = _buildDetailMap(el, track);
    }, 80);

    // Nearby POIs laden
    if (track.length >= 2) {
      _loadNearbyPois(track).then(pois => _renderNearby(pois));
    } else {
      const nb = document.getElementById('rk-nearby');
      if (nb) nb.innerHTML = '';
    }
  }

  // Richtungspfeile gleichmäßig entlang des Tracks platzieren
  function _addRouteArrows(map, track, color = '#fff') {
    if (track.length < 2) return;
    const R = 6371;
    const hav = (a, b) => {
      const dLat = (b.lat - a.lat) * Math.PI / 180;
      const dLon = (b.lon - a.lon) * Math.PI / 180;
      const s = Math.sin(dLat/2)**2 + Math.cos(a.lat*Math.PI/180)*Math.cos(b.lat*Math.PI/180)*Math.sin(dLon/2)**2;
      return R * 2 * Math.atan2(Math.sqrt(s), Math.sqrt(1-s));
    };
    const brng = (a, b) => {
      const φ1 = a.lat*Math.PI/180, φ2 = b.lat*Math.PI/180;
      const Δλ = (b.lon - a.lon)*Math.PI/180;
      return (Math.atan2(Math.sin(Δλ)*Math.cos(φ2), Math.cos(φ1)*Math.sin(φ2) - Math.sin(φ1)*Math.cos(φ2)*Math.cos(Δλ)) * 180/Math.PI + 360) % 360;
    };
    // Gesamtdistanz und kumulierte Abstände berechnen
    let total = 0;
    const cum = [0];
    for (let i = 1; i < track.length; i++) {
      total += hav(track[i-1], track[i]);
      cum.push(total);
    }
    const spacing = Math.max(0.4, total / 7); // max 7 Pfeile, min 400m Abstand
    let next = spacing * 0.5; // ersten Pfeil bei halber Spacing-Distanz
    for (let i = 1; i < track.length - 1; i++) {
      if (cum[i] >= next) {
        const deg = brng(track[i-1], track[i]);
        const icon = L.divIcon({
          className: '',
          html: `<svg width="20" height="20" viewBox="0 0 20 20"
                      style="transform:rotate(${deg.toFixed(0)}deg);transform-origin:10px 10px;display:block">
                   <path d="M10,3 L15,15 L10,12 L5,15 Z"
                         fill="${color}" fill-opacity="0.85"
                         stroke="rgba(0,0,0,0.25)" stroke-width="1" stroke-linejoin="round"/>
                 </svg>`,
          iconSize: [20, 20], iconAnchor: [10, 10],
        });
        L.marker([track[i].lat, track[i].lon], { icon, interactive: false, zIndexOffset: -100 }).addTo(map);
        next += spacing;
      }
    }
  }

  function _buildDetailMap(el, track) {
    const m = L.map(el, { zoomControl: false, attributionControl: false });
    L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 19 }).addTo(m);
    const lls  = track.map(p => [p.lat, p.lon]);
    const poly = L.polyline(lls, { color: '#C4843A', weight: 4, opacity: 0.85 }).addTo(m);
    _addRouteArrows(m, track, '#3b82f6');
    L.circleMarker(lls[0],     { radius:7, color:'#22C55E', fillColor:'#22C55E', fillOpacity:1 }).addTo(m);
    L.circleMarker(lls.at(-1), { radius:7, color:'#EF4444', fillColor:'#EF4444', fillOpacity:1 }).addTo(m);
    m.fitBounds(poly.getBounds(), { padding:[10,10] });
    return m;
  }

  // ----------------------------------------------------------
  // Nearby POIs
  // ----------------------------------------------------------

  // Gibt true zurück wenn poi.lat/lon innerhalb maxMeters eines Track-Punkts liegt
  function _isNearTrack(poi, track, maxMeters) {
    const R = 6371000;
    const plat = poi.lat * Math.PI / 180;
    const plon = poi.lon * Math.PI / 180;
    for (const pt of track) {
      const dlat = plat - pt.lat * Math.PI / 180;
      const dlon = plon - pt.lon * Math.PI / 180;
      const a = dlat*dlat + Math.cos(plat) * Math.cos(pt.lat * Math.PI/180) * dlon*dlon;
      if (R * Math.sqrt(a) <= maxMeters) return true;
    }
    return false;
  }

  async function _loadNearbyPois(track) {
    const lats = track.map(p => p.lat), lons = track.map(p => p.lon);
    const south = Math.min(...lats), north = Math.max(...lats);
    const west  = Math.min(...lons), east  = Math.max(...lons);
    // Bbox-Padding zum Abrufen (ca. 150m) — echte Distanzfilterung danach
    const pad = 0.0015;
    const bbox = { south: south-pad, north: north+pad, west: west-pad, east: east+pad };

    const results = [];
    await Promise.all(NEARBY_TYPES.map(async ({ type, icon, label, svgIcon, color }) => {
      try {
        const params = new URLSearchParams({ type, fast: 'true', ...bbox });
        const pois = await fetch(`/api/osm/pois?${params}`).then(r => r.json());
        pois
          .filter(p => _isNearTrack(p, track, 100)) // max 100m vom Track-Verlauf
          .forEach(p => results.push({ ...p, _icon: icon, _label: label, _svgIcon: svgIcon, _color: color }));
      } catch {}
    }));
    return results;
  }

  function _renderNearby(pois) {
    const el = document.getElementById('rk-nearby');
    if (!el) return;
    if (!pois.length) { el.innerHTML = ''; return; }

    const byType = {};
    pois.forEach(p => {
      const key = p._label;
      if (!byType[key]) byType[key] = { icon: p._icon, label: key, items: [] };
      byType[key].items.push(p);
    });

    let gIdx = 0;
    el.innerHTML = `
      <div class="rk-nearby-title">${UI.icon('map-pin')} Entlang der Route</div>
      ${Object.values(byType).map(group => {
        const id = `rk-ng-${gIdx++}`;
        return `
          <div class="rk-nearby-group">
            <button class="rk-nearby-group-header" data-target="${id}" type="button">
              <span>${group.icon} ${UI.escape(group.label)} (${group.items.length})</span>
              <svg class="ph-icon rk-nearby-chevron" aria-hidden="true" style="transition:transform 0.2s">
                <use href="/icons/phosphor.svg#caret-down"></use>
              </svg>
            </button>
            <div id="${id}" class="rk-nearby-items">
              ${group.items.map(p => `
                <button class="rk-nearby-item rk-nearby-item--link" type="button"
                        data-lat="${p.lat}" data-lon="${p.lon}"
                        data-name="${UI.escape(p.name || group.label)}">
                  <div>
                    <span class="rk-nearby-name">${UI.escape(p.name || group.label)}</span>
                    ${p.opening_hours ? `<span class="rk-nearby-detail">${UI.icon('clock')} ${UI.escape(p.opening_hours)}</span>` : ''}
                    ${p.phone ? `<span class="rk-nearby-detail">${UI.icon('phone')} ${UI.escape(p.phone)}</span>` : ''}
                  </div>
                  <svg class="ph-icon" style="width:14px;height:14px;color:var(--c-text-muted);flex-shrink:0" aria-hidden="true">
                    <use href="/icons/phosphor.svg#map-pin"></use>
                  </svg>
                </button>
              `).join('')}
            </div>
          </div>`;
      }).join('')}
    `;

    // Einklapp-Logik
    el.querySelectorAll('.rk-nearby-group-header').forEach(btn => {
      const target   = document.getElementById(btn.dataset.target);
      const chevron  = btn.querySelector('.rk-nearby-chevron');
      let open = false;
      target.style.display    = 'none';
      chevron.style.transform = 'rotate(-90deg)';
      btn.addEventListener('click', () => {
        open = !open;
        target.style.display    = open ? '' : 'none';
        chevron.style.transform = open ? '' : 'rotate(-90deg)';
      });
    });

    // POI auf Karte zeigen
    el.querySelectorAll('.rk-nearby-item--link').forEach(btn => {
      btn.addEventListener('click', () => {
        const lat  = parseFloat(btn.dataset.lat);
        const lon  = parseFloat(btn.dataset.lon);
        const name = btn.dataset.name;
        const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent);
        const url = isIOS
          ? `maps://maps.apple.com/?q=${encodeURIComponent(name)}&ll=${lat},${lon}`
          : `https://www.google.com/maps/search/?api=1&query=${lat},${lon}`;
        window.open(url, '_blank');
      });
    });
  }

  // ----------------------------------------------------------
  // Bewerten
  // ----------------------------------------------------------
  async function _rateRoute(id, wertung) {
    try {
      const res = await API.routes.rate(id, wertung);
      const r = _data.find(x => x.id === id);
      if (r) { r.bewertung = res.bewertung; r.anz_bewertungen = res.anz_bewertungen; }
      _applyFilter();
      UI.toast.success(`Bewertet: ${wertung} ★`);
    } catch (err) { UI.toast.error(err.message || 'Fehler beim Bewerten.'); }
  }

  // ----------------------------------------------------------
  // GPX
  // ----------------------------------------------------------
  async function _downloadGpx(id) {
    try {
      const route = await API.routes.get(id);
      _downloadGpxDirect(route);
    } catch (err) { UI.toast.error(err.message); }
  }

  function _downloadGpxDirect(route) {
    const track = route.gps_track || [];
    if (!track.length) { UI.toast.warning('Keine GPS-Daten.'); return; }
    const pts = track.map(p => `      <trkpt lat="${p.lat}" lon="${p.lon}"></trkpt>`).join('\n');
    const gpx = `<?xml version="1.0" encoding="UTF-8"?>
<gpx version="1.1" creator="Ban Yaro" xmlns="http://www.topografix.com/GPX/1/1">
  <trk><name>${UI.escape(route.name)}</name><trkseg>\n${pts}\n  </trkseg></trk>
</gpx>`;
    const blob = new Blob([gpx], { type: 'application/gpx+xml' });
    const url  = URL.createObjectURL(blob);
    const a    = document.createElement('a');
    a.href = url;
    a.download = `${route.name.replace(/[^a-z0-9äöü]/gi,'_')}.gpx`;
    a.click();
    URL.revokeObjectURL(url);
    UI.toast.success('GPX heruntergeladen!');
  }

  function _fmtDur(min) {
    if (min < 60) return `${min} min`;
    return `${Math.floor(min/60)}h ${min%60?(min%60)+'min':''}`.trim();
  }

  // ----------------------------------------------------------
  // Import: GPX / KML / TCX
  // ----------------------------------------------------------
  async function _importFile(file) {
    const text = await file.text();
    const ext  = file.name.split('.').pop().toLowerCase();
    let parsed;
    try {
      if (ext === 'gpx')       parsed = _parseGpx(text, file.name);
      else if (ext === 'kml')  parsed = _parseKml(text, file.name);
      else if (ext === 'tcx')  parsed = _parseTcx(text, file.name);
      else { UI.toast.error('Format nicht unterstützt. Bitte GPX, KML oder TCX.'); return; }
    } catch (err) {
      UI.toast.error(`Datei konnte nicht gelesen werden: ${err.message}`);
      return;
    }
    if (!parsed || parsed.track.length < 2) {
      UI.toast.error('Keine GPS-Punkte in der Datei gefunden.');
      return;
    }
    _openImportModal(parsed);
  }

  function _parseGpx(text, filename) {
    const doc    = new DOMParser().parseFromString(text, 'application/xml');
    const ns     = doc.documentElement.getAttribute('xmlns') || '';
    const resolve = tag => {
      // Namespace-aware oder fallback
      const els = doc.getElementsByTagNameNS(ns, tag);
      return els.length ? els : doc.getElementsByTagName(tag);
    };

    // Track-Punkte aus <trkpt>
    const trkpts = resolve('trkpt');
    const track  = [];
    let startTime = null, endTime = null;

    for (const pt of trkpts) {
      const lat = parseFloat(pt.getAttribute('lat'));
      const lon = parseFloat(pt.getAttribute('lon'));
      if (isNaN(lat) || isNaN(lon)) continue;
      const point = { lat, lon };

      // Elevation (optional)
      const eleEl = pt.getElementsByTagName('ele')[0] || pt.getElementsByTagNameNS(ns,'ele')[0];
      if (eleEl) point.ele = parseFloat(eleEl.textContent);

      // Timestamp (optional)
      const timeEl = pt.getElementsByTagName('time')[0] || pt.getElementsByTagNameNS(ns,'time')[0];
      if (timeEl) {
        const t = new Date(timeEl.textContent);
        if (!startTime) startTime = t;
        endTime = t;
      }
      track.push(point);
    }

    // Falls keine <trkpt> → Route-Punkte <rtept> versuchen
    if (!track.length) {
      const rtepts = resolve('rtept');
      for (const pt of rtepts) {
        const lat = parseFloat(pt.getAttribute('lat'));
        const lon = parseFloat(pt.getAttribute('lon'));
        if (!isNaN(lat) && !isNaN(lon)) track.push({ lat, lon });
      }
    }

    // Name aus Datei
    const nameEl = resolve('name')[0];
    const name   = nameEl?.textContent?.trim() ||
                   filename.replace(/\.gpx$/i, '').replace(/_/g,' ');

    const dauer_min = (startTime && endTime)
      ? Math.round((endTime - startTime) / 60000) : null;

    return { track, name, dauer_min, source: 'GPX' };
  }

  function _parseKml(text, filename) {
    const doc   = new DOMParser().parseFromString(text, 'application/xml');
    const track = [];

    // <coordinates> enthält "lon,lat,ele lon,lat,ele …" oder newline-getrennt
    const coordEls = doc.getElementsByTagName('coordinates');
    for (const el of coordEls) {
      const raw = el.textContent.trim().replace(/\n/g, ' ');
      for (const tuple of raw.split(/\s+/)) {
        const parts = tuple.split(',');
        if (parts.length >= 2) {
          const lon = parseFloat(parts[0]);
          const lat = parseFloat(parts[1]);
          if (!isNaN(lat) && !isNaN(lon)) {
            const point = { lat, lon };
            if (parts[2]) point.ele = parseFloat(parts[2]);
            track.push(point);
          }
        }
      }
    }

    const nameEl = doc.getElementsByTagName('name')[0];
    const name   = nameEl?.textContent?.trim() ||
                   filename.replace(/\.kml$/i, '').replace(/_/g,' ');

    return { track, name, dauer_min: null, source: 'KML' };
  }

  function _parseTcx(text, filename) {
    const doc    = new DOMParser().parseFromString(text, 'application/xml');
    const track  = [];
    let startTime = null, endTime = null;

    const trackpoints = doc.getElementsByTagName('Trackpoint');
    for (const tp of trackpoints) {
      const latEl = tp.getElementsByTagName('LatitudeDegrees')[0];
      const lonEl = tp.getElementsByTagName('LongitudeDegrees')[0];
      if (!latEl || !lonEl) continue;
      const lat = parseFloat(latEl.textContent);
      const lon = parseFloat(lonEl.textContent);
      if (isNaN(lat) || isNaN(lon)) continue;

      const point = { lat, lon };
      const altEl = tp.getElementsByTagName('AltitudeMeters')[0];
      if (altEl) point.ele = parseFloat(altEl.textContent);

      const timeEl = tp.getElementsByTagName('Time')[0];
      if (timeEl) {
        const t = new Date(timeEl.textContent);
        if (!startTime) startTime = t;
        endTime = t;
      }
      track.push(point);
    }

    const nameEl = doc.getElementsByTagName('Name')[0];
    const name   = nameEl?.textContent?.trim() ||
                   filename.replace(/\.tcx$/i, '').replace(/_/g,' ');

    const dauer_min = (startTime && endTime)
      ? Math.round((endTime - startTime) / 60000) : null;

    return { track, name, dauer_min, source: 'TCX' };
  }

  // Haversine (client-seitig für Import-Stats)
  function _calcDistance(track) {
    const R2RAD = Math.PI / 180;
    let dist = 0;
    for (let i = 1; i < track.length; i++) {
      const a = track[i-1], b = track[i];
      const dlat = (b.lat - a.lat) * R2RAD;
      const dlon = (b.lon - a.lon) * R2RAD;
      const sin2 = Math.sin(dlat/2)**2 +
                   Math.cos(a.lat*R2RAD)*Math.cos(b.lat*R2RAD)*Math.sin(dlon/2)**2;
      dist += 2 * 6371000 * Math.asin(Math.sqrt(sin2));
    }
    return dist / 1000; // km
  }

  // ----------------------------------------------------------
  // Import-Modal
  // ----------------------------------------------------------
  function _openImportModal(parsed) {
    const { track, name, dauer_min, source } = parsed;
    const distanz_km = _calcDistance(track);
    const preview    = _svgPreview(_simplifyPreview(track, 60));

    const body = `
      <div class="rk-import-preview">${preview}</div>
      <div class="rk-import-stats">
        <span>${UI.icon('map-pin')} ${track.length} Punkte</span>
        <span>${UI.icon('map-trifold')} ${distanz_km.toFixed(2)} km</span>
        ${dauer_min ? `<span>${UI.icon('timer')} ${_fmtDur(dauer_min)}</span>` : ''}
        <span class="rk-badge rk-badge--info">${source}</span>
      </div>
      <form id="rk-import-form" style="display:flex;flex-direction:column;gap:var(--space-3);margin-top:var(--space-4)">
        <div class="form-group">
          <label class="form-label">Name *</label>
          <input class="form-input" id="ri-name" value="${UI.escape(name)}" required maxlength="120">
        </div>
        <div class="form-group">
          <label class="form-label">Beschreibung</label>
          <textarea class="form-input" id="ri-desc" rows="2" placeholder="Kurze Beschreibung der Route…"></textarea>
        </div>
        <div style="display:grid;grid-template-columns:1fr 1fr;gap:var(--space-3)">
          <div class="form-group">
            <label class="form-label">Schwierigkeit</label>
            <select class="form-input" id="ri-diff">
              <option value="leicht">🟢 Leicht</option>
              <option value="mittel">🟡 Mittel</option>
              <option value="anspruchsvoll">🔴 Anspruchsvoll</option>
            </select>
          </div>
          <div class="form-group">
            <label class="form-label">Untergrund</label>
            <select class="form-input" id="ri-terrain">
              <option value="">– wählen –</option>
              <option value="wald">🌲 Wald</option>
              <option value="asphalt">🛣️ Asphalt</option>
              <option value="wiese">🌿 Wiese</option>
              <option value="mix">🔀 Mix</option>
            </select>
          </div>
        </div>
        <div class="form-group">
          <label class="form-label">Hundetauglichkeit</label>
          <div class="rk-paw-selector" id="ri-paws">
            <button type="button" class="rk-paw-btn" data-val="eingeschränkt">🐾 Eingeschränkt</button>
            <button type="button" class="rk-paw-btn" data-val="gut">🐾🐾 Gut</button>
            <button type="button" class="rk-paw-btn active" data-val="sehr_gut">🐾🐾🐾 Sehr gut</button>
            <button type="button" class="rk-paw-btn" data-val="premium">🐾🐾🐾🐾 Premium</button>
          </div>
        </div>
        <div style="display:flex;gap:var(--space-4)">
          <label style="display:flex;align-items:center;gap:var(--space-2);cursor:pointer">
            <input type="checkbox" id="ri-leine"> Leine empfohlen
          </label>
          <label style="display:flex;align-items:center;gap:var(--space-2);cursor:pointer">
            <input type="checkbox" id="ri-schatten"> Viel Schatten
          </label>
          <label style="display:flex;align-items:center;gap:var(--space-2);cursor:pointer">
            <input type="checkbox" id="ri-public"> Öffentlich
            ${UI.help('Öffentliche Routen können von allen Nutzern in der Entdecken-Ansicht gefunden werden.')}
          </label>
        </div>
      </form>
    `;

    const footer = `
      <button type="button" class="btn btn-ghost" id="ri-cancel">Abbrechen</button>
      <button type="button" class="btn btn-primary flex-1" id="ri-save">${UI.icon('floppy-disk')} Route speichern</button>
    `;

    UI.modal.open({ title: '📥 Route importieren', body, footer });

    document.getElementById('ri-cancel')?.addEventListener('click', UI.modal.close);

    // Paw-Selector
    let _selPaw = 'sehr_gut';
    document.getElementById('ri-paws')?.addEventListener('click', e => {
      const btn = e.target.closest('.rk-paw-btn');
      if (!btn) return;
      document.querySelectorAll('#ri-paws .rk-paw-btn').forEach(b => b.classList.remove('active'));
      btn.classList.add('active');
      _selPaw = btn.dataset.val;
    });

    document.getElementById('ri-save')?.addEventListener('click', async () => {
      const nameVal = document.getElementById('ri-name')?.value.trim();
      if (!nameVal) { UI.toast.error('Bitte einen Namen eingeben.'); return; }

      const saveBtn = document.getElementById('ri-save');
      saveBtn.disabled = true;
      saveBtn.textContent = 'Speichert…';

      try {
        // Track auf max 2000 Punkte reduzieren (API-Limit / Performance)
        const reducedTrack = _simplifyPreview(track, 2000);
        await API.routes.create({
          name:            nameVal,
          beschreibung:    document.getElementById('ri-desc')?.value.trim() || null,
          gps_track:       reducedTrack,
          distanz_km:      Math.round(distanz_km * 100) / 100,
          dauer_min:       dauer_min || null,
          schwierigkeit:   document.getElementById('ri-diff')?.value  || 'leicht',
          untergrund:      document.getElementById('ri-terrain')?.value || null,
          schatten:        document.getElementById('ri-schatten')?.checked,
          leine_empfohlen: document.getElementById('ri-leine')?.checked,
          is_public:       document.getElementById('ri-public')?.checked,
          hunde_tauglichkeit: _selPaw,
          client_time:     API.clientNow(),
        });
        UI.modal.close();
        UI.toast.success('Route importiert! 🥾');
        _loadData();
      } catch (err) {
        UI.toast.error(err.message || 'Fehler beim Speichern.');
        saveBtn.disabled = false;
        saveBtn.innerHTML = UI.icon('floppy-disk') + ' Route speichern';
      }
    });
  }

  function _simplifyPreview(track, maxPts) {
    if (track.length <= maxPts) return track;
    const step = track.length / maxPts;
    return Array.from({ length: maxPts }, (_, i) => track[Math.round(i * step)]);
  }

  // ----------------------------------------------------------
  // An Freund senden
  // ----------------------------------------------------------
  async function _openSendToFriendModal(route) {
    const shareUrl = location.origin + '/#routes?id=' + route.id;

    // Freunde laden
    let friends = [];
    try {
      friends = await API.friends.list();
    } catch (err) {
      UI.toast.error('Freunde konnten nicht geladen werden.');
      return;
    }

    if (!friends.length) {
      UI.toast.info('Du hast noch keine Freunde hinzugefügt.');
      return;
    }

    const friendRows = friends.map(f => {
      const initial = (f.name || '?')[0].toUpperCase();
      return `<div class="rk-friend-row" data-id="${f.id}" data-name="${UI.escape(f.name || 'Anonym')}"
                   style="display:flex;align-items:center;gap:var(--space-3);padding:var(--space-3);
                          cursor:pointer;border-radius:var(--radius-md);transition:background .15s"
                   onmouseover="this.style.background='var(--c-surface-2)'"
                   onmouseout="this.style.background=''">
        <div style="width:36px;height:36px;border-radius:50%;background:var(--c-primary);
                    color:#fff;display:flex;align-items:center;justify-content:center;
                    font-weight:600;flex-shrink:0">${UI.escape(initial)}</div>
        <span>${UI.escape(f.name || 'Anonym')}</span>
      </div>`;
    }).join('');

    const body = `<div id="rk-friend-list">${friendRows}</div>`;
    const footer = `<button type="button" class="btn btn-ghost" id="rsf-cancel">Abbrechen</button>`;

    UI.modal.open({
      title: `${UI.icon('chat-circle-dots')} An Freund senden`,
      body,
      footer,
    });

    document.getElementById('rsf-cancel')?.addEventListener('click', UI.modal.close);

    document.getElementById('rk-friend-list')?.addEventListener('click', async e => {
      const row = e.target.closest('.rk-friend-row');
      if (!row) return;

      const partnerId = parseInt(row.dataset.id, 10);
      const partnerName = row.dataset.name;

      try {
        const conv = await API.chat.start(partnerId);
        const convId = conv.id;
        const text = `Ich habe eine Route für dich: ${route.name}\n${shareUrl}`;
        await API.chat.send(convId, text);
        UI.modal.close();
        UI.toast.success(`Gesendet an ${partnerName}`);
      } catch (err) {
        UI.toast.error('Senden fehlgeschlagen: ' + (err.message || 'Unbekannter Fehler'));
      }
    });
  }

  // ----------------------------------------------------------
  // Notiz-Modal (Custom DOM — kein UI.modal um Konflikte zu vermeiden)
  // ----------------------------------------------------------
  async function _openNoteModal(parentType, parentId, parentLabel, locationName) {
    let existingNote = null;
    try { existingNote = await API.notes.get(parentType, String(parentId)); } catch {}

    const ovl = document.createElement('div');
    ovl.style.cssText = 'position:fixed;inset:0;z-index:1200;background:rgba(0,0,0,0.55);display:flex;align-items:flex-end;justify-content:center';
    ovl.innerHTML = `
      <div style="width:100%;max-width:600px;background:var(--c-surface);border-radius:var(--radius-lg) var(--radius-lg) 0 0;
                  padding:var(--space-4);box-sizing:border-box;max-height:80vh;display:flex;flex-direction:column">
        <div style="display:flex;align-items:center;gap:var(--space-3);margin-bottom:var(--space-3)">
          <svg class="ph-icon" aria-hidden="true" style="color:var(--c-primary)"><use href="/icons/phosphor.svg#note-pencil"></use></svg>
          <span style="font-weight:600;flex:1"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#note-pencil"></use></svg> Notiz — ${UI.escape(parentLabel)}</span>
          <button id="rk-note-close" style="background:none;border:none;cursor:pointer;color:var(--c-text-muted);padding:4px">
            <svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#x"></use></svg>
          </button>
        </div>
        <textarea id="rk-note-text" rows="5"
                  style="width:100%;box-sizing:border-box;padding:var(--space-3);
                         border:1.5px solid var(--c-border);border-radius:var(--radius-md);
                         font-size:var(--text-sm);font-family:inherit;
                         background:var(--c-bg);color:var(--c-text);resize:vertical;flex:1"
                  placeholder="Deine Notiz zu dieser Route…">${UI.escape(existingNote?.text || '')}</textarea>
        <div style="display:flex;gap:var(--space-2);margin-top:var(--space-3)">
          <button id="rk-note-cancel" class="btn btn-secondary flex-1">Abbrechen</button>
          <button id="rk-note-save"   class="btn btn-primary flex-1">Speichern</button>
        </div>
      </div>
    `;
    document.body.appendChild(ovl);

    const close = () => ovl.remove();
    ovl.querySelector('#rk-note-close')?.addEventListener('click', close);
    ovl.querySelector('#rk-note-cancel')?.addEventListener('click', close);
    ovl.addEventListener('click', e => { if (e.target === ovl) close(); });

    ovl.querySelector('#rk-note-save')?.addEventListener('click', async () => {
      const text = ovl.querySelector('#rk-note-text')?.value?.trim() || '';
      const payload = { text, parent_label: parentLabel, location_name: locationName || null, client_time: API.clientNow() };
      try {
        if (existingNote?.id) {
          await API.notes.update(existingNote.id, payload);
        } else {
          await API.notes.create(parentType, String(parentId), payload);
        }
        UI.toast.success('Notiz gespeichert.');
        close();
      } catch (err) { UI.toast.error(err.message || 'Fehler beim Speichern.'); }
    });
  }

  return { init, refresh, onDogChange };

})();
