/* ============================================================
   BAN YARO — Tagebuch (Sprint 1)
   Seiten-Modul: Timeline aller Einträge, Erstellen, Bearbeiten,
   Löschen, Foto-Upload, Meilensteine.
   ============================================================ */

window.Page_diary = (() => {

  // ----------------------------------------------------------
  // MODUL-STATE
  // ----------------------------------------------------------
  let _container      = null;
  let _appState       = null;
  let _entries        = [];
  let _offset         = 0;
  let _searchQuery    = '';
  let _filterMilestone = false;
  const LIMIT         = 20;

  function _sourceIcon(source) {
    if (source === 'places') return 'star';
    if (source === 'osm')    return 'map-pin';
    return 'map-trifold';
  }

  // ----------------------------------------------------------
  // DATUM-HILFSFUNKTIONEN (Day One Style)
  // ----------------------------------------------------------
  const _WOCHENTAG = ['SO.', 'MO.', 'DI.', 'MI.', 'DO.', 'FR.', 'SA.'];
  function _weekday(datum) {
    if (!datum) return '';
    return _WOCHENTAG[new Date(datum + 'T12:00').getDay()];
  }
  function _dayNum(datum) {
    return datum ? String(parseInt(datum.slice(8, 10), 10)) : '';
  }
  function _timeStr(createdAt) {
    if (!createdAt) return '';
    const t = createdAt.includes('T') ? createdAt : createdAt.replace(' ', 'T');
    const d = new Date(t);
    return isNaN(d) ? '' : d.toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' });
  }
  function _formatDateLong(datum) {
    if (!datum) return '';
    const d = new Date(datum + 'T12:00');
    return d.toLocaleDateString('de-DE', { weekday: 'long', day: 'numeric', month: 'long', year: 'numeric' });
  }

  // WMO-Wettercodes → Emoji für Badges
  const _WMO_EMOJI = {
    0: '☀️', 1: '🌤️', 2: '⛅', 3: '☁️',
    45: '🌫️', 48: '🌫️',
    51: '🌦️', 53: '🌦️', 55: '🌧️',
    61: '🌦️', 63: '🌧️', 65: '🌧️',
    71: '🌨️', 73: '❄️', 75: '❄️', 77: '🌨️',
    80: '🌦️', 81: '🌧️', 82: '⛈️',
    85: '🌨️', 86: '❄️',
    95: '⛈️', 96: '⛈️', 99: '⛈️',
  };
  function _weatherEmoji(code, isDay) {
    if (code === 0 && isDay === false) return '🌙';
    return _WMO_EMOJI[code] || '🌡️';
  }

  /** Wetter-Badge HTML für Karten (kompakt) */
  function _weatherBadgeHtml(entry) {
    if (!entry.weather_json) return '';
    let w;
    try { w = typeof entry.weather_json === 'string' ? JSON.parse(entry.weather_json) : entry.weather_json; }
    catch (_) { return ''; }
    if (!w || w.temp_c == null) return '';
    const emoji = _weatherEmoji(w.weathercode, w.is_day);
    const temp  = Math.round(w.temp_c);
    return `<span class="diary-weather-badge" title="${UI.escape(w.desc || '')}">${emoji} ${temp}°</span>`;
  }

  /** POI-Chips HTML (max. 3 Items) */
  function _poiChipsHtml(entry, maxItems = 3) {
    if (!entry.poi_json) return '';
    let pois;
    try { pois = typeof entry.poi_json === 'string' ? JSON.parse(entry.poi_json) : entry.poi_json; }
    catch (_) { return ''; }
    if (!Array.isArray(pois) || pois.length === 0) return '';
    const _poiEmoji = (type) => {
      if (!type) return '📍';
      const t = type.toLowerCase();
      if (t === 'veterinary' || t === 'hospital')         return '🏥';
      if (t === 'pet_shop' || t === 'shop')               return '🛒';
      if (t === 'park' || t === 'leisure' || t === 'garden') return '🌳';
      if (t === 'restaurant' || t === 'cafe' || t === 'bar')  return '☕';
      if (t === 'historic' || t === 'monument')           return '🏛️';
      if (t === 'tourism' || t === 'attraction')          return '🗺️';
      if (t === 'playground')                             return '🛝';
      return '📍';
    };
    const chips = pois.slice(0, maxItems)
      .map(p => `${_poiEmoji(p.type)} ${UI.escape(p.name)}`)
      .join(' · ');
    return `<p class="diary-poi-chips">${chips}</p>`;
  }

  const _VIDEO_EXT = new Set(['.mp4','.mov','.webm','.m4v','.avi']);
  function _isVideo(url) {
    if (!url) return false;
    return _VIDEO_EXT.has(url.slice(url.lastIndexOf('.')).toLowerCase());
  }
  function _videoPoster(url) { return url.replace(/\.[^.]+$/, '_thumb.jpg'); }
  function _mediaHtml(url, style = '') {
    if (!url) return '';
    return _isVideo(url)
      ? `<video src="${url}" poster="${_videoPoster(url)}" controls playsinline style="width:100%;border-radius:var(--radius-md);${style}"></video>`
      : `<img src="${url}" alt="Foto" style="width:100%;border-radius:var(--radius-md);${style}">`;
  }

  /** Alle Mediendateien eines Eintrags normalisiert als Array zurückgeben.
   *  Rückwärtskompatibel: wenn media_items leer, aber media_url gesetzt → altes Format. */
  function _allMedia(entry) {
    const items = entry.media_items || [];
    if (items.length > 0) return items;
    if (entry.media_url) {
      return [{ id: null, url: entry.media_url,
                media_type: _isVideo(entry.media_url) ? 'video' : 'image', sort_order: 0 }];
    }
    return [];
  }

  const TYPEN = {
    eintrag:    { label: 'Eintrag',    icon: '<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#book-open"></use></svg>' },
    foto:       { label: 'Foto',       icon: '<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#camera"></use></svg>' },
    meilenstein:{ label: 'Meilenstein',icon: '<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#trophy"></use></svg>' },
    training:   { label: 'Training',   icon: '<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#target"></use></svg>' },
    gesundheit: { label: 'Gesundheit', icon: '<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#syringe"></use></svg>' },
    spaziergang:{ label: 'Spaziergang', icon: '<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#dog"></use></svg>' },
    ausflug:    { label: 'Ausflug',    icon: '<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#car"></use></svg>' },
  };

  // ----------------------------------------------------------
  // INIT — erster Aufruf, Container leer
  // ----------------------------------------------------------
  async function init(container, appState) {
    _container = container;
    _appState  = appState;
    UI.loadLeaflet(); // Leaflet im Hintergrund vorladen — kein await
    await _render();
  }

  // ----------------------------------------------------------
  // REFRESH — erneuter Navigations-Aufruf (Tap auf Tab)
  // ----------------------------------------------------------
  async function refresh() {
    if (!_appState.activeDog) return;
    // Mehrere Hunde → Picker zeigen (User kann Hund wählen)
    if (_appState.dogs.length > 1) {
      _renderDogPicker();
      return;
    }
    // Einzelner Hund → Diary direkt neu laden
    _offset     = 0;
    _entries    = [];
    _totalStats = null;
    await _renderDiary();
  }

  // ----------------------------------------------------------
  // ON DOG CHANGE — vom Header-Switcher ausgelöst
  // ----------------------------------------------------------
  async function onDogChange(dog) {
    _offset      = 0;
    _entries     = [];
    _searchQuery = '';
    await _renderDiary();
  }

  // ----------------------------------------------------------
  // OPEN NEW — vom + Button oder Quick-Add
  // ----------------------------------------------------------
  function openNew() {
    _showForm(null);
  }

  // ----------------------------------------------------------
  // RENDER — Einstieg: Picker bei mehreren Hunden, sonst direkt
  // ----------------------------------------------------------
  async function _render() {
    if (!_appState.activeDog) {
      _container.innerHTML = UI.emptyState({
        icon:   '<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#dog"></use></svg>',
        title:  'Noch kein Hund angelegt',
        text:   'Erstelle zuerst ein Hundeprofil, um das Tagebuch zu nutzen.',
        action: `<button class="btn btn-primary" id="diary-goto-profile">Profil erstellen</button>`,
      });
      _container.querySelector('#diary-goto-profile')
        ?.addEventListener('click', () => App.navigate('dog-profile'));
      return;
    }

    if (_appState.dogs.length > 1) {
      _renderDogPicker();
    } else {
      await _renderDiary();
    }
  }

  // ----------------------------------------------------------
  // HUNDE-PICKER — Einstiegsseite bei mehreren Hunden
  // ----------------------------------------------------------
  function _renderDogPicker() {
    const activeDogId = _appState.activeDog?.id;

    const cards = _appState.dogs.map(dog => {
      const isActive = dog.id === activeDogId;
      const av = dog.foto_url
        ? `<img src="${UI.escape(dog.foto_url)}" alt="${UI.escape(dog.name)}">`
        : `<span>${UI.icon('dog')}</span>`;
      return `
        <div class="diary-picker-card${isActive ? ' diary-picker-card--active' : ''}"
             data-dog-id="${dog.id}">
          <div class="diary-picker-av">${av}</div>
          <div class="diary-picker-name">${UI.escape(dog.name)}</div>
          ${dog.rasse ? `<div class="diary-picker-rasse">${UI.escape(dog.rasse)}</div>` : ''}
        </div>`;
    }).join('');

    _container.innerHTML = `
      <div class="diary-picker-wrap">
        <p class="diary-picker-hint">Wessen Tagebuch?</p>
        <div class="diary-picker-grid">${cards}</div>
      </div>`;

    _container.querySelectorAll('.diary-picker-card').forEach(el => {
      el.addEventListener('click', async () => {
        const id = parseInt(el.dataset.dogId);
        if (id === _appState.activeDog?.id) {
          // Bereits aktiver Hund → direkt Diary laden
          _offset = 0; _entries = [];
          await _renderDiary();
        } else {
          App.setActiveDog(id);
          // onDogChange() → _renderDiary() via _notifyDogChange()
        }
      });
    });
  }

  // ----------------------------------------------------------
  // DIARY-ANSICHT — Timeline mit Einträgen
  // ----------------------------------------------------------
  async function _renderDiary() {
    _container.innerHTML = `
      <div class="by-toolbar diary-toolbar">
        <div class="diary-search-wrap" id="diary-search-wrap">
          <svg class="ph-icon diary-search-icon" aria-hidden="true"><use href="/icons/phosphor.svg#magnifying-glass"></use></svg>
          <input type="search" class="diary-search-input" id="diary-search-input"
                 placeholder="Einträge durchsuchen…" autocomplete="off">
        </div>
        <button class="btn btn-secondary btn-sm${_filterMilestone ? ' btn-active' : ''}" id="diary-milestone-filter" title="Nur Meilensteine">
          <svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#trophy"></use></svg>
        </button>
        <button class="btn btn-secondary btn-sm" id="diary-import-btn" title="Importieren">
          <svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#download-simple"></use></svg>
        </button>
      </div>
      <div id="diary-stats-bar" class="diary-stats-bar" style="display:none"></div>
      <div id="diary-view-content">
        <div id="diary-list"></div>
      </div>
      <div id="diary-load-more" style="display:none; text-align:center; padding:var(--space-4)">
        <button class="btn btn-secondary" id="diary-btn-more">Weitere laden</button>
      </div>
      <!-- FAB: Neuer Eintrag -->
      <button id="diary-fab" class="diary-fab" aria-label="Neuer Tagebucheintrag">
        <svg class="ph-icon" style="width:26px;height:26px" aria-hidden="true">
          <use href="/icons/phosphor.svg#plus"></use>
        </svg>
      </button>
    `;

    _container.querySelector('#diary-milestone-filter')
      ?.addEventListener('click', async () => {
        _filterMilestone = !_filterMilestone;
        _offset = 0; _entries = [];
        const btn = _container.querySelector('#diary-milestone-filter');
        btn?.classList.toggle('btn-active', _filterMilestone);
        await _load();
        _renderList();
      });

    _container.querySelector('#diary-import-btn')
      ?.addEventListener('click', _showImport);
    _container.querySelector('#diary-fab')
      ?.addEventListener('click', () => _showForm(null));
    _container.querySelector('#diary-btn-more')
      ?.addEventListener('click', () => _loadMore());

    // Suche mit Debounce
    let _searchTimer = null;
    _container.querySelector('#diary-search-input')
      ?.addEventListener('input', e => {
        clearTimeout(_searchTimer);
        _searchTimer = setTimeout(async () => {
          _offset  = 0;
          _entries = [];
          _searchQuery = e.target.value.trim();
          await _load();
          _renderList();
        }, 350);
      });

    await Promise.all([_load(), _loadStats()]);
    _renderList();
    _renderStatsBar();
    _loadPraise();
  }

  // ----------------------------------------------------------
  // FORTSCHRITTS-LOBER
  // ----------------------------------------------------------
  async function _loadPraise() {
    const dog = _appState.activeDog;
    if (!dog) return;

    const existing = _container.querySelector('#diary-praise-card');
    if (existing) existing.remove();

    let data;
    try {
      const r = await fetch(`/api/praise/current?dog_id=${dog.id}`, {credentials: 'include'});
      data = r.ok ? await r.json() : null;
    } catch (_) { return; }

    if (!data?.praise) return;

    const card = document.createElement('div');
    card.id = 'diary-praise-card';
    card.style.cssText = `
      margin: var(--space-3) var(--space-4) 0;
      background: linear-gradient(135deg, var(--c-primary-subtle), #fdf6ef);
      border: 1px solid var(--c-primary-light, #e8c99a);
      border-radius: var(--radius-xl);
      padding: var(--space-4) var(--space-5);
      display: flex; gap: var(--space-3); align-items: flex-start;
    `;
    card.innerHTML = `
      <div style="font-size:1.8rem;flex-shrink:0;line-height:1">🐾</div>
      <div style="flex:1;min-width:0">
        <div style="font-size:var(--text-xs);font-weight:var(--weight-semibold);
                    color:var(--c-primary-dark);text-transform:uppercase;
                    letter-spacing:.06em;margin-bottom:var(--space-1)">
          Rückblick der Woche
        </div>
        <p style="font-size:var(--text-sm);color:var(--c-text);
                  line-height:1.6;margin:0">${data.praise}</p>
      </div>
      <button id="diary-praise-close"
              style="background:none;border:none;cursor:pointer;padding:2px;
                     color:var(--c-text-muted);flex-shrink:0;line-height:1;font-size:1.1rem"
              aria-label="Schließen">×</button>
    `;

    const list = _container.querySelector('#diary-list');
    if (list) _container.insertBefore(card, list);

    card.querySelector('#diary-praise-close')?.addEventListener('click', () => {
      card.style.opacity = '0';
      card.style.transition = 'opacity .2s';
      setTimeout(() => card.remove(), 200);
    });
  }

  // ----------------------------------------------------------
  // DATEN LADEN
  // ----------------------------------------------------------
  async function _load() {
    const dog = _appState.activeDog;
    if (!dog) return;
    try {
      const params = { limit: LIMIT, offset: _offset };
      if (_searchQuery) params.q = _searchQuery;
      if (_filterMilestone) params.milestone = 1;
      const batch = await API.diary.list(dog.id, params);
      _entries = _entries.concat(batch);

      // "Mehr laden" anzeigen wenn volle Page geladen wurde
      const loadMore = _container.querySelector('#diary-load-more');
      if (loadMore) {
        loadMore.style.display = batch.length === LIMIT ? 'block' : 'none';
      }

      // Stats-Bar befüllen
      _renderStatsBar();
    } catch (err) {
      UI.toast.error('Einträge konnten nicht geladen werden.');
    }
  }

  let _currentView = 'list'; // 'list' | 'media' | 'calendar' | 'map'
  let _totalStats   = null;   // {entries, photos, days} — Gesamtstatistik aus API

  async function _loadStats() {
    const dog = _appState.activeDog;
    if (!dog) return;
    try {
      _totalStats = await API.diary.stats(dog.id);
    } catch (_) {}
  }

  function _fmt(n) {
    if (n >= 10000) return (n / 1000).toFixed(0) + 'k';
    if (n >= 1000)  return (n / 1000).toFixed(1).replace('.0','') + 'k';
    return String(n);
  }

  function _renderStatsBar() {
    const bar = _container.querySelector('#diary-stats-bar');
    if (!bar) return;
    const s = _totalStats;
    if (!s && _entries.length === 0) { bar.style.display = 'none'; return; }
    const entries = s?.entries ?? _entries.length;
    const photos  = s?.photos  ?? _entries.reduce((n, e) => n + _allMedia(e).length, 0);
    const days    = s?.days    ?? new Set(_entries.map(e => e.datum).filter(Boolean)).size;
    bar.innerHTML = `
      <div class="diary-stats-numbers">
        <div class="diary-stat">
          <span class="diary-stat-num">${_fmt(entries)}</span>
          <span class="diary-stat-label">Einträge</span>
        </div>
        <div class="diary-stat">
          <span class="diary-stat-num">${_fmt(photos)}</span>
          <span class="diary-stat-label">Medien</span>
        </div>
        <div class="diary-stat">
          <span class="diary-stat-num">${_fmt(days)}</span>
          <span class="diary-stat-label">Tage</span>
        </div>
      </div>
      <div class="diary-view-switcher">
        <button class="diary-view-btn${_currentView==='list'?' active':''}" data-view="list" title="Liste">
          <svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#list"></use></svg>
        </button>
        <button class="diary-view-btn${_currentView==='media'?' active':''}" data-view="media" title="Medien">
          <svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#images"></use></svg>
        </button>
        <button class="diary-view-btn${_currentView==='calendar'?' active':''}" data-view="calendar" title="Kalender">
          <svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#calendar-dots"></use></svg>
        </button>
        <button class="diary-view-btn${_currentView==='map'?' active':''}" data-view="map" title="Karte">
          <svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#map-trifold"></use></svg>
        </button>
      </div>
    `;
    bar.style.display = 'flex';

    bar.querySelectorAll('.diary-view-btn').forEach(btn => {
      btn.addEventListener('click', () => {
        _currentView = btn.dataset.view;
        _renderStatsBar();
        _renderCurrentView();
      });
    });
  }

  function _renderCurrentView() {
    const content  = _container.querySelector('#diary-view-content');
    const loadMore = _container.querySelector('#diary-load-more');
    if (!content) return;
    // "Weitere laden" nur in der Listenansicht sinnvoll
    if (loadMore) loadMore.style.display = 'none';
    if (_currentView === 'list') {
      content.innerHTML = '<div id="diary-list"></div>';
      _renderList();
      // Sichtbarkeit des "Weitere laden"-Buttons nach _load() steuern (bereits in _load())
    } else if (_currentView === 'media') {
      _renderMediaGrid(content);
    } else if (_currentView === 'calendar') {
      _renderCalendarView(content);
    } else if (_currentView === 'map') {
      _renderMapView(content);
    }
  }

  async function _renderMapView(content) {
    const dog = _appState.activeDog;
    if (!dog) return;

    content.innerHTML = `<div id="diary-map-view" style="height:calc(100vh - 200px);min-height:400px;position:relative">
      <div style="position:absolute;inset:0;display:flex;align-items:center;justify-content:center;color:var(--c-text-muted);font-size:14px">
        <svg class="ph-icon" style="width:24px;height:24px;margin-right:8px" aria-hidden="true"><use href="/icons/phosphor.svg#map-trifold"></use></svg>
        Karte wird geladen…
      </div>
    </div>`;

    let locations;
    try {
      locations = await API.diary.locations(dog.id);
    } catch (e) {
      content.innerHTML = `<p style="padding:20px;color:var(--c-danger)">Standorte konnten nicht geladen werden.</p>`;
      return;
    }

    if (!locations.length) {
      content.innerHTML = UI.emptyState({ icon: UI.icon('map-pin'), title: 'Keine Standorte', text: 'Füge GPS-Koordinaten zu Tagebucheinträgen hinzu.' });
      return;
    }

    // Leaflet laden
    if (!window.L) {
      await new Promise((res, rej) => {
        const s = document.createElement('script');
        s.src = `/js/leaflet.js?v=${APP_VER}`;
        s.onload = res; s.onerror = rej;
        document.head.appendChild(s);
      });
    }

    const mapEl = content.querySelector('#diary-map-view');
    if (!mapEl) return;

    // Bounds aus allen Punkten berechnen
    const lats = locations.map(l => l.gps_lat);
    const lons = locations.map(l => l.gps_lon);
    const bounds = [[Math.min(...lats), Math.min(...lons)], [Math.max(...lats), Math.max(...lons)]];

    const map = L.map(mapEl, { zoomControl: true, attributionControl: false });
    L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 19 }).addTo(map);

    // Marker für jeden Eintrag
    locations.forEach(loc => {
      const hasPhoto = !!loc.cover_url;
      const dateStr  = loc.datum ? new Date(loc.datum+'T12:00').toLocaleDateString('de-DE', {day:'numeric',month:'short',year:'numeric'}) : '';
      const title    = UI.escape(loc.titel || loc.location_name || dateStr);

      const icon = L.divIcon({
        html: hasPhoto
          ? `<div style="width:44px;height:44px;border-radius:50%;overflow:hidden;border:3px solid var(--c-primary,#C4843A);box-shadow:0 2px 8px rgba(0,0,0,.3);background:#fff">
               <img src="${UI.escape(loc.cover_preview_url || loc.cover_url)}" style="width:100%;height:100%;object-fit:cover" onerror="this.src='${UI.escape(loc.cover_url)}'">
             </div>`
          : `<div style="width:32px;height:32px;border-radius:50%;background:var(--c-primary,#C4843A);border:3px solid #fff;box-shadow:0 2px 8px rgba(0,0,0,.3);display:flex;align-items:center;justify-content:center">
               <svg style="width:16px;height:16px;fill:#fff" viewBox="0 0 256 256"><path d="M128,16a96,96,0,1,0,96,96A96.11,96.11,0,0,0,128,16Zm0,176a80,80,0,1,1,80-80A80.09,80.09,0,0,1,128,192Zm0-104a24,24,0,1,0,24,24A24,24,0,0,0,128,88Z"/></svg>
             </div>`,
        iconSize:   hasPhoto ? [44, 44] : [32, 32],
        iconAnchor: hasPhoto ? [22, 22] : [16, 16],
        className:  '',
      });

      const marker = L.marker([loc.gps_lat, loc.gps_lon], { icon });
      marker.bindPopup(`
        <div style="min-width:160px;cursor:pointer" class="diary-map-popup" data-id="${loc.id}">
          ${hasPhoto ? `<img src="${UI.escape(loc.cover_preview_url || loc.cover_url)}" style="width:100%;height:100px;object-fit:cover;border-radius:6px;display:block;margin-bottom:8px" onerror="this.src='${UI.escape(loc.cover_url)}'">` : ''}
          <div style="font-weight:600;font-size:13px;margin-bottom:2px">${title}</div>
          <div style="font-size:11px;color:#888">${dateStr}</div>
          ${loc.media_count > 1 ? `<div style="font-size:11px;color:#888;margin-top:2px">📷 ${loc.media_count} Medien</div>` : ''}
          <div style="margin-top:6px;text-align:center;font-size:12px;color:var(--c-primary,#C4843A);font-weight:600">→ Öffnen</div>
        </div>`, { maxWidth: 200 });

      marker.on('popupopen', () => {
        setTimeout(() => {
          document.querySelectorAll('.diary-map-popup').forEach(el => {
            el.addEventListener('click', async () => {
              map.closePopup();
              const id = parseInt(el.dataset.id);
              // Eintrag aus _entries holen oder per API nachladen
              if (!_entries.find(e => e.id === id)) {
                try {
                  const fresh = await API.diary.get(_appState.activeDog.id, id);
                  _entries.unshift(fresh);
                } catch { return; }
              }
              _openDetail(id);
            });
          });
        }, 50);
      });

      marker.addTo(map);
    });

    // Karte auf alle Punkte zoomen
    if (locations.length === 1) {
      map.setView([locations[0].gps_lat, locations[0].gps_lon], 14);
    } else {
      map.fitBounds(bounds, { padding: [40, 40] });
    }

    setTimeout(() => map.invalidateSize(), 100);
  }

  function _renderMediaGrid(content) {
    const allMedia = [];
    _entries.forEach(e => {
      _allMedia(e).forEach(m => {
        if (m.media_type === 'image') allMedia.push({ url: m.url, preview_url: m.preview_url, entryId: e.id, datum: e.datum });
      });
    });
    if (allMedia.length === 0) {
      content.innerHTML = UI.emptyState({ icon: UI.icon('images'), title: 'Keine Medien', text: 'Füge Fotos oder Videos zu Tagebucheinträgen hinzu.' });
      return;
    }
    content.innerHTML = `<div class="diary-media-mosaic">${
      allMedia.map(m => `
        <div class="diary-mosaic-item" data-entry-id="${m.entryId}" data-full-url="${UI.escape(m.url)}">
          <img src="${UI.escape(m.preview_url || m.url)}"
               ${m.preview_url ? `srcset="${UI.escape(m.preview_url)} 800w, ${UI.escape(m.url)} 2000w" sizes="(max-width:400px) 200px, 400px"` : ''}
               alt="" loading="lazy"
               onerror="this.src='${UI.escape(m.url)}'">
        </div>`).join('')
    }</div>`;
    content.querySelectorAll('.diary-mosaic-item').forEach(el => {
      el.addEventListener('click', () => _openDetail(parseInt(el.dataset.entryId)));
    });
  }

  async function _renderCalendarView(content) {
    const dog = _appState.activeDog;
    if (!dog) return;

    const today = new Date().toISOString().slice(0, 10);
    const now   = new Date();
    let year = now.getFullYear(), month = now.getMonth();

    content.innerHTML = `<div style="padding:40px;text-align:center;color:var(--c-text-muted)">
      <svg class="ph-icon" style="width:24px;height:24px;fill:currentColor" aria-hidden="true"><use href="/icons/phosphor.svg#calendar-dots"></use></svg>
      <span style="margin-left:8px">Kalender wird geladen…</span></div>`;

    let byDate = {};
    try {
      const all = await API.diary.calendar(dog.id);
      if (!Array.isArray(all)) throw new Error('Keine Array-Antwort: ' + typeof all);
      all.forEach(e => { if (e && e.datum) byDate[e.datum] = e; });
    } catch (err) {
      content.innerHTML = `<p style="padding:20px;color:var(--c-danger)">Kalender-Fehler: ${UI.escape(String(err))}</p>`;
      return;
    }
    // Debug: Anzahl geladener Einträge kurz anzeigen
    const _total = Object.keys(byDate).length;
    if (_total === 0) {
      content.innerHTML = `<p style="padding:20px;color:var(--c-text-muted)">Keine Einträge mit Datum gefunden.</p>`;
      return;
    }

    const render = () => {
      const firstDay    = new Date(year, month, 1).getDay();
      const daysInMonth = new Date(year, month + 1, 0).getDate();
      const monthName   = new Date(year, month).toLocaleDateString('de-DE', { month: 'long', year: 'numeric' });
      const monthPrefix = `${year}-${String(month+1).padStart(2,'0')}`;
      const monthCount  = Object.keys(byDate).filter(k => k.startsWith(monthPrefix)).length;
      const DAYS = ['Mo','Di','Mi','Do','Fr','Sa','So'];
      const offset = firstDay === 0 ? 6 : firstDay - 1;
      const cells = [];
      for (let i = 0; i < offset; i++) cells.push('<div></div>');
      for (let d = 1; d <= daysInMonth; d++) {
        const key   = `${year}-${String(month+1).padStart(2,'0')}-${String(d).padStart(2,'0')}`;
        const entry = byDate[key];
        cells.push(`<div class="diary-cal-cell${entry?' has-entry':''}${key===today?' today':''}" data-entry-id="${entry?.id||''}">
          ${entry?.cover_url ? `<img src="${UI.escape(entry.cover_preview_url || entry.cover_url)}" alt="" loading="lazy" onerror="this.src='${UI.escape(entry.cover_url)}'">` : ''}
          <span class="diary-cal-day">${d}</span>
        </div>`);
      }

      // Nächsten/vorherigen Monat MIT Einträgen finden für Sprungbuttons
      const allMonths = [...new Set(Object.keys(byDate).map(k => k.slice(0,7)))].sort();
      const curM = monthPrefix;
      const prevM = allMonths.filter(m => m < curM).at(-1) || null;
      const nextM = allMonths.filter(m => m > curM)[0] || null;

      content.innerHTML = `
        <div class="diary-calendar">
          <div class="diary-cal-nav">
            <div style="display:flex;gap:2px">
              ${prevM ? `<button class="cal-nav-btn cal-jump-btn" data-jump="${prevM}" title="Springe zu ${prevM}">
                <svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#caret-double-left"></use></svg>
              </button>` : '<div style="width:32px"></div>'}
              <button class="cal-nav-btn" data-dir="-1">
                <svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#caret-left"></use></svg>
              </button>
            </div>
            <span class="diary-cal-month">${monthName}${monthCount > 0 ? `<span style="font-size:11px;font-weight:400;color:var(--c-primary);margin-left:6px">${monthCount}</span>` : ''}</span>
            <div style="display:flex;gap:2px">
              <button class="cal-nav-btn" data-dir="1">
                <svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#caret-right"></use></svg>
              </button>
              ${nextM ? `<button class="cal-nav-btn cal-jump-btn" data-jump="${nextM}" title="Springe zu ${nextM}">
                <svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#caret-double-right"></use></svg>
              </button>` : '<div style="width:32px"></div>'}
            </div>
          </div>
          <div class="diary-cal-weekdays">${DAYS.map(n=>`<div>${n}</div>`).join('')}</div>
          <div class="diary-cal-grid">${cells.join('')}</div>
        </div>`;
    };

    // Event-Delegation auf content — überlebt innerHTML-Erneuerungen
    content.addEventListener('click', async e => {
      const navBtn = e.target.closest('.cal-nav-btn');
      if (navBtn) {
        // Sprungbutton: direkt zu Monat mit Einträgen
        if (navBtn.dataset.jump) {
          const [y, m] = navBtn.dataset.jump.split('-').map(Number);
          year = y; month = m - 1;
          render();
          return;
        }
        const dir = parseInt(navBtn.dataset.dir);
        month += dir;
        if (month < 0)  { month = 11; year--; }
        if (month > 11) { month = 0;  year++; }
        render();
        return;
      }
      const cell = e.target.closest('.diary-cal-cell.has-entry');
      if (cell) {
        const id = parseInt(cell.dataset.entryId);
        if (!id) return;
        if (!_entries.find(en => en.id === id)) {
          try {
            const fresh = await API.diary.get(dog.id, id);
            _entries.unshift(fresh);
          } catch { return; }
        }
        _openDetail(id);
      }
    });

    render();
  }

  async function _loadMore() {
    _offset += LIMIT;
    const btn = _container.querySelector('#diary-btn-more');
    UI.setLoading(btn, true);
    await _load();
    _renderList();
    UI.setLoading(btn, false);
  }

  // ----------------------------------------------------------
  // LISTE RENDERN — Timeline gruppiert nach Monat (Day One Style)
  // ----------------------------------------------------------
  function _renderList() {
    const listEl = _container.querySelector('#diary-list');
    if (!listEl) return;

    const dog = _appState.activeDog;
    const isSitter = dog?.is_guest === true;

    // Sitter: Einträge grundsätzlich ausgeblendet — nur Hinweis + FAB bleibt aktiv
    if (isSitter) {
      listEl.innerHTML = UI.emptyState({
        icon:   UI.icon('lock-simple'),
        title:  'Einträge nicht sichtbar',
        text:   'Du kannst neue Einträge hinzufügen, aber keine bestehenden Einträge sehen.',
      });
      return;
    }

    if (_entries.length === 0) {
      listEl.innerHTML = UI.emptyState({
        icon:   UI.icon('book-open'),
        title:  'Noch keine Tagebucheinträge',
        text:   'Halte besondere Momente mit deinem Hund fest — Spaziergänge, Erlebnisse, Erinnerungen.',
        action: `<button class="btn btn-primary" id="diary-first-entry">Ersten Eintrag schreiben</button>`,
      });
      listEl.querySelector('#diary-first-entry')
        ?.addEventListener('click', () => _showForm(null));
      return;
    }

    // Datenschutz-Hinweis: einmalig anzeigen, per Klick wegklicken
    const privacyNotice = localStorage.getItem('by_diary_privacy_ack') ? '' : `
      <div id="diary-privacy-notice" style="font-size:var(--text-xs);color:var(--c-text-muted);
                  background:var(--c-surface-2);border-radius:var(--radius-md);
                  padding:var(--space-2) var(--space-3);display:flex;align-items:center;
                  gap:var(--space-2);margin-bottom:var(--space-3);cursor:pointer"
           title="Klicken zum Schließen">
        <svg class="ph-icon" aria-hidden="true" style="width:14px;height:14px;flex-shrink:0">
          <use href="/icons/phosphor.svg#lock-simple"></use></svg>
        Deine Tagebucheinträge sind privat — nur du kannst sie sehen.
        <svg class="ph-icon" aria-hidden="true" style="width:12px;height:12px;margin-left:auto;flex-shrink:0;opacity:0.5">
          <use href="/icons/phosphor.svg#x"></use></svg>
      </div>`;

    // Gruppieren nach Jahr-Monat (Anzeigereihenfolge: chronologisch absteigend)
    const groups = new Map();
    _entries.forEach(e => {
      const key = e.datum ? e.datum.slice(0, 7) : 'unbekannt'; // "2025-04"
      if (!groups.has(key)) groups.set(key, []);
      groups.get(key).push(e);
    });

    let html = privacyNotice;
    groups.forEach((items, key) => {
      const monthLabel = key === 'unbekannt' ? 'Datum unbekannt' : _formatMonth(key);
      html += `<div class="diary-month-header">${monthLabel}</div>`;
      html += `<div class="diary-month-entries">`;
      html += items.map(e => _entryCard(e)).join('');
      html += `</div>`;
    });

    listEl.innerHTML = html;

    // Datenschutz-Hinweis wegklicken
    listEl.querySelector('#diary-privacy-notice')?.addEventListener('click', () => {
      localStorage.setItem('by_diary_privacy_ack', '1');
      listEl.querySelector('#diary-privacy-notice')?.remove();
    });

    // Events an Karten binden
    listEl.querySelectorAll('[data-entry-id]').forEach(card => {
      const id = parseInt(card.dataset.entryId);
      card.addEventListener('click', () => _openDetail(id));
    });
    listEl.querySelectorAll('[data-action="open-note"]').forEach(btn => {
      btn.addEventListener('click', e => {
        e.stopPropagation();
        const id       = parseInt(btn.dataset.entryId);
        const label    = btn.dataset.label || '';
        const location = btn.dataset.location || null;
        _openNoteModal('diary', id, label, location || null);
      });
    });
  }

  // ----------------------------------------------------------
  // ENTRY CARD — Day One Row-Style
  // ----------------------------------------------------------
  function _entryCard(e) {
    const isMile   = e.is_milestone || e.typ === 'meilenstein';

    const allMedia   = _allMedia(e);
    const coverMedia = allMedia.find(m => m.is_cover) || allMedia[0] || null;
    const mediaCount = allMedia.length;

    // Thumbnail rechts (72×72)
    let photoHtml = '';
    if (coverMedia) {
      if (coverMedia.media_type === 'video') {
        photoHtml = `<div class="diary-card-photo">
          <div class="diary-card-video-thumb" style="width:100%;height:100%;display:flex;align-items:center;justify-content:center;background:var(--c-surface-2);color:var(--c-primary)">
            <svg class="ph-icon" style="width:28px;height:28px" aria-hidden="true"><use href="/icons/phosphor.svg#play-circle"></use></svg>
          </div>
          ${mediaCount > 1 ? `<span class="diary-card-media-count">${mediaCount}</span>` : ''}
        </div>`;
      } else {
        photoHtml = `<div class="diary-card-photo">
          <img src="${e.cover_preview_url || e.cover_url || coverMedia.preview_url || coverMedia.url}"
               ${(e.cover_preview_url && e.cover_url) ? `srcset="${UI.escape(e.cover_preview_url)} 800w, ${UI.escape(e.cover_url)} 2000w" sizes="(max-width:600px) 300px, 600px"` : ''}
               alt="Foto" loading="lazy"
               ${e.cover_url ? `onerror="this.src='${UI.escape(e.cover_url)}'"` : ''}>
          ${mediaCount > 1 ? `<span class="diary-card-media-count">${mediaCount}</span>` : ''}
        </div>`;
      }
    }

    // Vorschautext (max 2 Zeilen via CSS clamp)
    const cleanedText = e.text ? _cleanText(e.text) : '';
    const textPreview = cleanedText
      ? `<p class="diary-card-text">${UI.escape(cleanedText.slice(0, 160))}</p>`
      : '';

    // Meta-Zeile: Zeit · 📍 Ort · Wetter
    const metaParts = [];
    if (e.created_at) {
      const t = _timeStr(e.created_at);
      if (t) metaParts.push(`<span class="diary-meta-time">${t}</span>`);
    }
    if (e.location_name) {
      metaParts.push(`<span class="diary-meta-loc"><svg class="ph-icon" style="width:11px;height:11px;flex-shrink:0" aria-hidden="true"><use href="/icons/phosphor.svg#map-pin"></use></svg>${UI.escape(e.location_name)}</span>`);
    }
    if (e.weather_json) {
      try {
        const w = typeof e.weather_json === 'string' ? JSON.parse(e.weather_json) : e.weather_json;
        const temp = w?.temperature_2m ?? w?.temp_c;
        if (temp != null) {
          metaParts.push(`<span>${_weatherEmoji(w.weather_code ?? w.weathercode, w.is_day)} ${Math.round(temp)}°</span>`);
        }
      } catch (_) {}
    }
    const metaRow = metaParts.length
      ? `<div class="diary-card-meta-row">${metaParts.join('<span class="diary-meta-dot"> · </span>')}</div>`
      : '';

    // Meilenstein-Icon auf der Datum-Spalte
    const mileIcon = isMile
      ? `<svg class="ph-icon diary-milestone-icon" style="width:14px;height:14px;color:#c4a000;margin-top:4px" aria-hidden="true"><use href="/icons/phosphor.svg#trophy"></use></svg>`
      : '';

    // Titel oder Typ als Fallback
    const typObj  = TYPEN[e.typ] || TYPEN.eintrag;
    const titleText = e.titel
      ? `<div class="diary-card-title">${UI.escape(e.titel)}</div>`
      : `<div class="diary-card-title" style="font-weight:500;color:var(--c-text-secondary)">${typObj.label}</div>`;

    const noteLabel = e.titel || e.datum || '';
    return `
      <div class="diary-card${isMile ? ' diary-card--milestone' : ''}" data-entry-id="${e.id}">
        <!-- Datum-Spalte links -->
        <div class="diary-card-date-col">
          <span class="diary-card-weekday">${_weekday(e.datum)}</span>
          <span class="diary-card-daynum">${_dayNum(e.datum)}</span>
          ${mileIcon}
        </div>
        <!-- Inhalt Mitte -->
        <div class="diary-card-body">
          ${titleText}
          ${textPreview}
          ${metaRow}
        </div>
        <!-- Thumbnail rechts -->
        ${photoHtml}
      </div>
    `;
  }

  function _dogAvatarRow(dogIds) {
    if (!dogIds || dogIds.length <= 1) return '';
    const avatars = dogIds.map(did => {
      const dog = _appState.dogs.find(d => d.id === did);
      if (!dog) return '';
      return `<div class="diary-dog-av" title="${UI.escape(dog.name)}">
        ${dog.foto_url ? `<img src="${UI.escape(dog.foto_url)}" alt="">` : `<span>${UI.icon('dog')}</span>`}
      </div>`;
    }).join('');
    return `<div class="diary-dog-row">${avatars}</div>`;
  }

  // ----------------------------------------------------------
  // LIGHTBOX
  // ----------------------------------------------------------
  // ----------------------------------------------------------
  // LIGHTBOX — Fotos mit Vor/Zurück-Navigation
  // ----------------------------------------------------------
  function _showLightbox(urls, startIdx = 0) {
    const photos = Array.isArray(urls) ? urls : [urls];
    let idx = startIdx;

    const lb = document.createElement('div');
    lb.id = 'diary-lightbox';
    lb.style.cssText = 'position:fixed;inset:0;z-index:1100;background:#000;display:flex;flex-direction:column';

    const render = () => {
      lb.innerHTML = `
        <div style="flex:1;display:flex;align-items:center;justify-content:center;overflow:hidden;position:relative">
          <img src="${UI.escape(photos[idx])}" style="max-width:100%;max-height:100%;object-fit:contain;touch-action:pinch-zoom;display:block">
        </div>
        <!-- Controls unten: Zurück · Counter · Prev/Next — mit Safe-Area für alle Ränder -->
        <div style="display:grid;grid-template-columns:1fr auto 1fr;align-items:center;
                    padding-top:10px;
                    padding-bottom:calc(env(safe-area-inset-bottom,0px) + 12px);
                    padding-left:calc(env(safe-area-inset-left,0px) + 16px);
                    padding-right:calc(env(safe-area-inset-right,0px) + 16px);
                    flex-shrink:0;background:rgba(0,0,0,.5);gap:8px">
          <button id="lb-close" style="background:rgba(255,255,255,.15);border:none;border-radius:24px;
                  height:48px;color:#fff;cursor:pointer;display:flex;align-items:center;
                  justify-content:center;gap:6px;padding:0 16px;font-size:15px;font-weight:500">
            <svg style="width:20px;height:20px;fill:currentColor;flex-shrink:0" viewBox="0 0 256 256">
              <path d="M224,128a8,8,0,0,1-8,8H59.31l58.35,58.34a8,8,0,0,1-11.32,11.32l-72-72a8,8,0,0,1,0-11.32l72-72a8,8,0,0,1,11.32,11.32L59.31,120H216A8,8,0,0,1,224,128Z"/>
            </svg>
            Zurück
          </button>
          <span style="color:rgba(255,255,255,.7);font-size:14px;text-align:center;white-space:nowrap">
            ${photos.length > 1 ? `${idx+1} / ${photos.length}` : ''}
          </span>
          ${photos.length > 1 ? `
          <div style="display:flex;gap:8px;justify-content:flex-end">
            <button id="lb-prev" style="background:rgba(255,255,255,.15);border:none;border-radius:50%;
                    width:48px;height:48px;color:#fff;font-size:24px;cursor:pointer;
                    display:flex;align-items:center;justify-content:center
                    ${idx === 0 ? ';opacity:.3;pointer-events:none' : ''}">‹</button>
            <button id="lb-next" style="background:rgba(255,255,255,.15);border:none;border-radius:50%;
                    width:48px;height:48px;color:#fff;font-size:24px;cursor:pointer;
                    display:flex;align-items:center;justify-content:center
                    ${idx === photos.length-1 ? ';opacity:.3;pointer-events:none' : ''}">›</button>
          </div>` : '<div></div>'}
        </div>
      `;
      lb.querySelector('#lb-close').addEventListener('click', () => lb.remove());
      lb.querySelector('#lb-prev')?.addEventListener('click', () => { if (idx > 0) { idx--; render(); } });
      lb.querySelector('#lb-next')?.addEventListener('click', () => { if (idx < photos.length-1) { idx++; render(); } });
    };
    render();
    document.body.appendChild(lb);
  }

  // ----------------------------------------------------------
  // DETAIL-ANSICHT — Fullscreen Day One-Stil
  // ----------------------------------------------------------
  function _openDetail(entryId) {
    const entry = _entries.find(e => e.id === entryId);
    if (!entry) return;

    const typ    = TYPEN[entry.typ] || TYPEN.eintrag;
    const isMile = entry.is_milestone || entry.typ === 'meilenstein';
    const tags   = (entry.tags || []).filter(t => t && t.trim());
    const allMedia = _allMedia(entry);
    const dogIds   = entry.dog_ids || [entry.dog_id];

    // Hunde-Chips (bei mehreren Hunden)
    const dogsHtml = dogIds.length > 1
      ? `<div class="diary-detail-dogs" style="margin-bottom:var(--space-3)">
           ${dogIds.map(did => {
             const dog = _appState.dogs.find(d => d.id === did);
             return dog ? `<div class="diary-dog-chip">
               <div class="diary-dog-av">
                 ${dog.foto_url ? `<img src="${UI.escape(dog.foto_url)}" alt="">` : `<span>${UI.icon('dog')}</span>`}
               </div><span>${UI.escape(dog.name)}</span></div>` : '';
           }).join('')}
         </div>` : '';

    // Detail-View im Content-Bereich rendern (gleiche Breite wie Liste/Kalender)
    const content = _container.querySelector('#diary-view-content');
    if (!content) return;
    // FAB + "Weitere laden" ausblenden während Detail offen
    _container.querySelector('#diary-fab')?.style.setProperty('display','none');
    const _lm = _container.querySelector('#diary-load-more');
    if (_lm) _lm.style.display = 'none';

    const view = document.createElement('div');
    view.id = 'diary-detail-view';
    view.className = 'diary-detail-view-inner';

    // Medien-HTML für Hero-Bereich (45vh)
    const _heroHtml = (m) => {
      if (m.media_type === 'pdf') {
        return `<a href="${UI.escape(m.url)}" target="_blank" rel="noopener"
            style="display:flex;flex-direction:column;align-items:center;justify-content:center;
                   gap:12px;padding:40px 16px;background:var(--c-surface-2);text-decoration:none;color:var(--c-text);height:100%">
           <svg class="ph-icon" style="width:56px;height:56px;color:var(--c-danger)" aria-hidden="true"><use href="/icons/phosphor.svg#file-text"></use></svg>
           <span style="font-size:14px;font-weight:600">${UI.escape(m.url.split('/').pop())}</span>
           <span style="font-size:12px;color:var(--c-text-secondary)">PDF öffnen</span>
         </a>`;
      }
      if (m.media_type === 'video') {
        return `<video src="${UI.escape(m.url)}" poster="${UI.escape(_videoPoster(m.url))}" controls playsinline style="width:100%;height:100%;display:block;object-fit:contain;background:#000"></video>`;
      }
      return `<img src="${UI.escape(m.url)}" data-idx="${allMedia.indexOf(m)}" style="width:100%;height:100%;object-fit:cover;display:block;cursor:zoom-in">`;
    };

    // Hero-Sektion
    let heroSection = '';
    if (allMedia.length >= 1) {
      const thumbsHtml = allMedia.length > 1
        ? `<div class="diary-detail-thumbs" id="diary-dv-thumbs">
             ${allMedia.map((m, i) => `
               <div class="diary-detail-thumb${i === 0 ? ' diary-detail-thumb--active' : ''}" data-idx="${i}">
                 ${m.media_type === 'pdf'
                   ? `<div style="width:100%;height:100%;display:flex;align-items:center;justify-content:center;background:var(--c-surface-2)"><svg class="ph-icon" style="width:24px;height:24px;color:var(--c-danger)" aria-hidden="true"><use href="/icons/phosphor.svg#file-text"></use></svg></div>`
                   : m.media_type === 'video'
                   ? `<video src="${UI.escape(m.url)}" poster="${UI.escape(_videoPoster(m.url))}" style="width:100%;height:100%;object-fit:cover;pointer-events:none"></video>`
                   : `<img src="${UI.escape(m.url)}" style="width:100%;height:100%;object-fit:cover;display:block">`}
               </div>`).join('')}
           </div>`
        : '';
      heroSection = `
        <div class="diary-detail-hero" id="diary-dv-hero">${_heroHtml(allMedia[0])}</div>
        ${thumbsHtml}`;
    }

    // Datum lang formatiert: "Montag, 21. April 2026"
    const datumLang = _formatDateLong(entry.datum);

    // Meta-Bar: Zeit · Ort · Wetter (korrekte Feldnamen aus Open-Meteo)
    const metaItems = [];
    if (entry.created_at) {
      const t = _timeStr(entry.created_at);
      if (t) metaItems.push(`<span class="diary-detail-meta-item"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#clock"></use></svg>${t}</span>`);
    }
    if (entry.location_name) {
      const locContent = entry.gps_lat
        ? `<a href="https://maps.apple.com/?q=${encodeURIComponent(entry.location_name)}&ll=${entry.gps_lat},${entry.gps_lon}"
              target="_blank" rel="noopener" style="color:inherit;text-decoration:none">${UI.escape(entry.location_name)}</a>`
        : UI.escape(entry.location_name);
      metaItems.push(`<span class="diary-detail-meta-item"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#map-pin"></use></svg>${locContent}</span>`);
    }
    if (entry.weather_json) {
      try {
        const w = typeof entry.weather_json === 'string' ? JSON.parse(entry.weather_json) : entry.weather_json;
        const temp = w?.temperature_2m ?? w?.temp_c;
        if (w && temp != null) {
          const feels = w.apparent_temperature ?? w.feels_like_c;
          const wind  = w.wind_speed_10m ?? w.wind_kmh;
          const parts = [
            `${_weatherEmoji(w.weather_code ?? w.weathercode, w.is_day)} ${Math.round(temp)}°C`,
            feels != null ? `gefühlt ${Math.round(feels)}°` : null,
            wind  != null ? `💨 ${Math.round(wind)} km/h`  : null,
            w.relative_humidity_2m != null ? `💧 ${w.relative_humidity_2m}%` : null,
          ].filter(Boolean).join(' · ');
          metaItems.push(`<span class="diary-detail-meta-item">${parts}</span>`);
        }
      } catch (_) {}
    }
    const metaBar = metaItems.length
      ? `<div class="diary-detail-meta-bar">${metaItems.join('')}</div>`
      : '';

    // Tags
    const tagsSection = tags.length
      ? `<div style="display:flex;flex-wrap:wrap;gap:6px;margin-top:16px">
           ${tags.map(t => `<span class="badge">${t}</span>`).join('')}
         </div>`
      : '';

    // POI-Liste (wie Routen)
    const POI_ICON = { restaurant:'fork-knife', cafe:'coffee', bar:'beer-bottle',
      pharmacy:'first-aid', hospital:'first-aid', park:'tree', playground:'soccer-ball',
      supermarket:'shopping-cart', shop:'shopping-bag', attraction:'star',
      viewpoint:'binoculars', museum:'buildings', hotel:'bed', church:'church',
      school:'graduation-cap', default:'map-pin' };
    let poiListHtml = '';
    if (entry.poi_json) {
      try {
        const pois = typeof entry.poi_json === 'string' ? JSON.parse(entry.poi_json) : entry.poi_json;
        if (pois?.length) {
          poiListHtml = `<div class="diary-detail-poi-list">
            <div class="diary-detail-poi-heading">
              <svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#map-trifold"></use></svg>
              In der Nähe
            </div>
            ${pois.map(p => {
              const icon = POI_ICON[p.type] || POI_ICON.default;
              const dist = p.distance_m < 1000 ? `${p.distance_m} m` : `${(p.distance_m/1000).toFixed(1)} km`;
              return `<div class="diary-detail-poi-row">
                <svg class="ph-icon diary-detail-poi-icon" aria-hidden="true"><use href="/icons/phosphor.svg#${icon}"></use></svg>
                <span class="diary-detail-poi-name">${UI.escape(p.name)}</span>
                <span class="diary-detail-poi-dist">${dist}</span>
              </div>`;
            }).join('')}
          </div>`;
        }
      } catch (_) {}
    }

    // Karte (wenn GPS vorhanden) — Platzhalter-Div, wird nach DOM-Insert befüllt
    const hasGps = entry.gps_lat != null && entry.gps_lon != null;
    const mapSection = hasGps
      ? `<div class="diary-detail-map-wrap">
           <div id="diary-dv-map" class="diary-detail-map"></div>
           ${poiListHtml}
         </div>`
      : '';

    view.innerHTML = `
      <div class="diary-detail-header">
        <button id="diary-dv-back" class="diary-detail-back">
          <svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#arrow-left"></use></svg>
          Zurück
        </button>
        <span class="diary-detail-date-center">${datumLang}</span>
        <div style="display:flex;align-items:center;gap:4px">
          ${!_appState?.activeDog?.is_guest
            ? `<button id="diary-dv-note" class="btn btn-ghost btn-xs" title="Notiz"
                       onclick="event.stopPropagation()">
                 <svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#note-pencil"></use></svg>
               </button>
               <button id="diary-dv-edit" class="diary-detail-edit">
                 <svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#pencil-simple"></use></svg>
               </button>`
            : '<div style="width:40px"></div>'}
        </div>
      </div>

      ${heroSection}

      <div class="diary-detail-body-wrap">
        <div class="diary-detail-content">
          ${isMile ? `<div class="diary-detail-milestone-badge"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#trophy"></use></svg> Meilenstein</div>` : ''}
          ${entry.titel ? `<h1 class="diary-detail-title">${UI.escape(entry.titel)}</h1>` : ''}
          ${metaBar}
          ${dogsHtml}
          ${entry.text
            ? `<p class="diary-detail-body">${UI.escape(_cleanText(entry.text))}</p>`
            : ''}
          ${metaItems.length || entry.text ? '<hr class="diary-detail-divider">' : ''}
          <div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap">
            <span class="badge badge-primary">${typ.icon} ${typ.label}</span>
          </div>
          ${tagsSection}
        </div>
        ${mapSection}
      </div>
    `;

    // In Content-Bereich einsetzen statt als Fixed-Overlay
    content.innerHTML = '';
    content.appendChild(view);
    UI.scrollTop(); // Seite nach oben scrollen

    // Leaflet-Karte initialisieren (wenn GPS vorhanden)
    if (hasGps) {
      setTimeout(async () => {
        const mapEl = view.querySelector('#diary-dv-map');
        if (!mapEl) return;
        if (!window.L) {
          await new Promise((res, rej) => {
            const s = document.createElement('script');
            s.src = `/js/leaflet.js?v=${APP_VER}`;
            s.onload = res; s.onerror = rej;
            document.head.appendChild(s);
          });
        }
        const map = L.map(mapEl, { zoomControl: true, attributionControl: false });
        L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 19 }).addTo(map);
        const svgIcon = L.divIcon({
          html: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" width="32" height="32">
                   <circle cx="128" cy="128" r="96" fill="var(--c-primary,#C4843A)" opacity=".25"/>
                   <circle cx="128" cy="128" r="48" fill="var(--c-primary,#C4843A)"/>
                 </svg>`,
          iconSize: [32, 32], iconAnchor: [16, 16], className: '',
        });
        L.marker([entry.gps_lat, entry.gps_lon], { icon: svgIcon }).addTo(map);
        map.setView([entry.gps_lat, entry.gps_lon], 15);
        map.invalidateSize();
      }, 150);
    }

    // Zurück → vorherige Ansicht wiederherstellen
    const _closeDetail = () => {
      _container.querySelector('#diary-fab')?.style.removeProperty('display');
      _renderCurrentView();
      _renderStatsBar();
    };
    view.querySelector('#diary-dv-back').addEventListener('click', _closeDetail);

    // Notiz-Button in Detailansicht
    view.querySelector('#diary-dv-note')?.addEventListener('click', e => {
      e.stopPropagation();
      const label = entry.titel || entry.datum || String(entry.id);
      _openNoteModal('diary', entry.id, label, entry.location_name || null);
    });

    // Bearbeiten
    view.querySelector('#diary-dv-edit')?.addEventListener('click', async () => {
      _container.querySelector('#diary-fab')?.style.removeProperty('display');
      if (entry.location_name !== undefined || entry.gps_lat !== undefined) {
        _showForm(entry);
      } else {
        try {
          const fresh = await API.diary.get(_appState.activeDog.id, entry.id);
          const idx = _entries.findIndex(e => e.id === entry.id);
          if (idx !== -1) _entries[idx] = fresh;
          _showForm(fresh);
        } catch { _showForm(entry); }
      }
    });

    // Foto in Hero → Lightbox
    const photoUrls = allMedia.filter(m => m.media_type !== 'video').map(m => m.url);
    view.querySelector('#diary-dv-hero')?.querySelector('img')?.addEventListener('click', ev => {
      const clickedIdx = parseInt(ev.target.dataset.idx ?? 0);
      const photoIdx = allMedia.slice(0, clickedIdx + 1).filter(m => m.media_type !== 'video').length - 1;
      _showLightbox(photoUrls, Math.max(0, photoIdx));
    });

    // Thumbnail-Strip → Hero wechseln
    view.querySelector('#diary-dv-thumbs')?.addEventListener('click', ev => {
      const thumb = ev.target.closest('[data-idx]');
      if (!thumb) return;
      const i = parseInt(thumb.dataset.idx);
      const hero = view.querySelector('#diary-dv-hero');
      if (hero) hero.innerHTML = _heroHtml(allMedia[i]);
      // Foto in neuem Hero → Lightbox
      hero?.querySelector('img')?.addEventListener('click', ev2 => {
        const clickedIdx = parseInt(ev2.target.dataset.idx ?? i);
        const photoIdx = allMedia.slice(0, clickedIdx + 1).filter(m => m.media_type !== 'video').length - 1;
        _showLightbox(photoUrls, Math.max(0, photoIdx));
      });
      // Aktive Markierung
      view.querySelectorAll('#diary-dv-thumbs .diary-detail-thumb').forEach((t, j) => {
        t.classList.toggle('diary-detail-thumb--active', j === i);
      });
    });
  }

  // ----------------------------------------------------------
  // FORMULAR — Neu erstellen / Bearbeiten
  // ----------------------------------------------------------
  function _showForm(entry) {
    const isEdit    = !!entry;
    const today     = new Date().toISOString().slice(0, 10);
    const activeDog = _appState.activeDog;
    const typOpts   = Object.entries(TYPEN)
      .map(([val, { icon, label }]) =>
        `<option value="${val}" ${entry?.typ === val ? 'selected' : ''}>${icon} ${label}</option>`)
      .join('');

    // Weitere Hunde: alle außer dem aktiven
    const otherDogs    = _appState.dogs.filter(d => d.id !== activeDog?.id);
    const entryDogIds  = entry?.dog_ids || [activeDog?.id];
    const dogPickerHtml = otherDogs.length > 0 ? `
      <div class="form-group">
        <label class="form-label">Betrifft auch</label>
        <div class="diary-dog-picker">
          ${otherDogs.map(d => `
            <label class="diary-dog-pick-item ${entryDogIds.includes(d.id) ? 'checked' : ''}">
              <input type="checkbox" name="extra_dog" value="${d.id}"
                     ${entryDogIds.includes(d.id) ? 'checked' : ''}>
              <div class="diary-dog-av">
                ${d.foto_url ? `<img src="${UI.escape(d.foto_url)}" alt="">` : `<span>${UI.icon('dog')}</span>`}
              </div>
              <span>${UI.escape(d.name)}</span>
            </label>`).join('')}
        </div>
      </div>` : '';

    const body = `
      <form id="diary-form" autocomplete="off">
        <div class="form-group">
          <label class="form-label">Typ</label>
          <select class="form-control" name="typ">${typOpts}</select>
        </div>
        <div class="form-group">
          <label class="form-label">Datum</label>
          <input class="form-control" type="date" name="datum"
                 value="${entry?.datum || today}" required>
        </div>
        <div class="form-group">
          <label class="form-label">Titel <span style="color:var(--c-text-secondary)">(optional)</span></label>
          <input class="form-control" type="text" name="titel"
                 value="${UI.escape(entry?.titel || '')}" placeholder="z.B. Erster Schultag">
        </div>
        <div class="form-group">
          <label class="form-label">Text</label>
          <textarea class="form-control" name="text" rows="5"
                    placeholder="Was ist passiert? Besonderheiten, Gedanken…">${UI.escape(entry?.text || '')}</textarea>
        </div>
        <div class="form-group">
          <!-- Bestehende Medien (Edit-Modus) -->
          <div id="diary-existing-media"></div>

          <!-- Neue Medien: Vorschau-Grid -->
          <div id="diary-new-media-grid" class="diary-media-grid" style="display:none"></div>

          <!-- versteckter Input — multiple für Mehrfachauswahl -->
          <input type="file" id="diary-media-input" accept="image/*,video/*,application/pdf" multiple style="display:none">

          <!-- Einzelner Button — iOS zeigt nativen Picker (Mediathek / Kamera / Datei) -->
          <label for="diary-media-input" class="btn btn-secondary" style="cursor:pointer;display:flex;align-items:center;gap:var(--space-2);justify-content:center">
            <svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#image"></use></svg>
            Fotos / Videos hinzufügen
          </label>
        </div>
        <div class="form-group" id="diary-location-group">
          <label class="form-label">Ort <span style="color:var(--c-text-secondary)">(optional)</span></label>

          <!-- Karte (Lesemodus, Edit per Button aktivierbar) -->
          <div style="position:relative">
            <div id="diary-map-wrap" style="border-radius:var(--radius-md);overflow:hidden;height:220px;background:var(--c-surface-2)"></div>
            <button type="button" id="diary-map-edit-btn" class="btn btn-secondary btn-sm"
                    style="position:absolute;bottom:var(--space-2);right:var(--space-2);z-index:500">
              <svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#pencil-simple"></use></svg>
              <span id="diary-map-edit-label">Position ändern</span>
            </button>
          </div>

          <!-- POI-Name + Aktionen -->
          <div style="margin-top:var(--space-2)">
            <div id="diary-location-chip-wrap" style="${entry?.location_name ? '' : 'display:none'}">
              <div class="diary-location-chip">
                <svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#map-pin"></use></svg>
                <span id="diary-location-label">${UI.escape(entry?.location_name || '')}</span>
                <button type="button" id="diary-location-clear" aria-label="Name entfernen">
                  <svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#x"></use></svg>
                </button>
              </div>
            </div>
            <div style="display:flex;gap:var(--space-2);margin-top:var(--space-2)">
              <button type="button" class="btn btn-danger" id="diary-coords-clear">Ort entfernen</button>
              <button type="button" class="btn btn-secondary btn-sm" id="diary-location-btn">
                <svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#map-pin"></use></svg>
                <span id="diary-location-btn-label">POI suchen</span>
              </button>
            </div>
            <div id="diary-location-suggestions" style="display:none;margin-top:var(--space-2)"></div>
          </div>
        </div>
        ${dogPickerHtml}
        <div class="form-group" style="margin-top:var(--space-5)">
          <input type="checkbox" name="is_milestone" id="diary-milestone-cb"
                 ${entry?.is_milestone ? 'checked' : ''} style="display:none">
          <button type="button" id="diary-milestone-btn"
                  class="diary-milestone-toggle${entry?.is_milestone ? ' diary-milestone-toggle--active' : ''}">
            <svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#trophy"></use></svg>
            <span>${entry?.is_milestone ? 'Meilenstein ✓' : 'Als Meilenstein markieren'}</span>
          </button>
        </div>
      </form>
    `;

    const footer = `
      <div style="display:flex;flex-direction:column;gap:var(--space-2);width:100%">
        <button type="submit" form="diary-form" class="btn btn-primary" style="width:100%">
          ${isEdit ? 'Speichern' : 'Erstellen'}
        </button>
        <div style="display:flex;gap:var(--space-2)">
          ${isEdit ? `<button type="button" class="btn btn-danger" id="diary-form-delete">Löschen</button>` : ''}
          <button type="button" class="btn btn-secondary flex-1" id="diary-form-cancel">Abbrechen</button>
        </div>
      </div>
    `;

    UI.modal.open({ title: isEdit ? 'Eintrag bearbeiten' : 'Neuer Eintrag', body, footer });

    const form = document.getElementById('diary-form');

    // Fokus auf Titel-Feld → öffnet Keyboard auf Mobile, zeigt dem User was zu tun ist
    setTimeout(() => form?.querySelector('[name="titel"]')?.focus(), 150);

    // ---- Multi-Media-Verwaltung ----
    const mediaInput  = document.getElementById('diary-media-input');

    // Neue Dateien die noch nicht hochgeladen wurden
    const _newFiles = [];

    function _renderNewGrid() {
      const grid = document.getElementById('diary-new-media-grid');
      if (!grid) return;
      if (_newFiles.length === 0) { grid.style.display = 'none'; grid.innerHTML = ''; return; }
      grid.style.cssText = 'display:grid;grid-template-columns:repeat(auto-fill,minmax(90px,1fr));gap:8px;margin-bottom:8px';
      grid.innerHTML = _newFiles.map((f, i) => {
        const objUrl = URL.createObjectURL(f);
        const thumb  = f.type === 'application/pdf' || f.name?.endsWith('.pdf')
          ? `<div style="display:flex;flex-direction:column;align-items:center;justify-content:center;
                         height:100%;gap:4px;padding:8px;text-align:center">
               <svg class="ph-icon" style="width:32px;height:32px;color:var(--c-danger)" aria-hidden="true"><use href="/icons/phosphor.svg#file-text"></use></svg>
               <div style="font-size:10px;color:var(--c-text-secondary);word-break:break-all;line-height:1.2">${f.name}</div>
             </div>`
          : f.type.startsWith('video/')
          ? `<video src="${objUrl}" class="diary-media-thumb" muted playsinline></video>`
          : `<img src="${objUrl}" alt="" class="diary-media-thumb">`;
        return `<div style="position:relative;aspect-ratio:1;border-radius:8px;overflow:hidden;background:var(--c-surface-2)" data-new-idx="${i}">
          ${thumb}
          <button type="button" class="diary-media-thumb-del" data-new-idx="${i}"
                  aria-label="Entfernen"
                  style="position:absolute;top:4px;right:4px;width:24px;height:24px;border-radius:50%;border:none;background:rgba(0,0,0,.55);color:#fff;cursor:pointer;display:flex;align-items:center;justify-content:center;padding:0;font-size:14px">✕</button>
        </div>`;
      }).join('');
      grid.querySelectorAll('.diary-media-thumb-del').forEach(btn => {
        btn.addEventListener('click', () => {
          const idx = parseInt(btn.dataset.newIdx);
          _newFiles.splice(idx, 1);
          _renderNewGrid();
        });
      });
    }

    // Bestehende Medien im Edit-Modus rendern
    function _renderExistingMedia() {
      const wrap = document.getElementById('diary-existing-media');
      if (!wrap) return;
      const items = isEdit ? _allMedia(entry) : [];
      if (items.length === 0) { wrap.innerHTML = ''; return; }
      const GRID_STYLE = 'display:grid;grid-template-columns:repeat(auto-fill,minmax(90px,1fr));gap:8px;margin-bottom:8px';
      const grid = `<div style="${GRID_STYLE}">
        ${items.map((m, idx) => `
          <div style="position:relative;aspect-ratio:1;border-radius:8px;overflow:hidden;background:var(--c-surface-2)"
               data-media-id="${m.id ?? ''}">
            ${m.media_type === 'video'
              ? `<video src="${m.url}" poster="${_videoPoster(m.url)}" style="width:100%;height:100%;object-fit:cover;display:block" muted playsinline></video>`
              : `<img src="${m.url}" alt="" style="width:100%;height:100%;object-fit:cover;display:block">`}
            <button type="button" class="diary-media-thumb-del"
                    data-media-id="${m.id ?? ''}" data-legacy="${m.id == null ? '1' : ''}"
                    aria-label="Entfernen"
                    style="position:absolute;top:4px;right:4px;width:24px;height:24px;border-radius:50%;border:none;background:rgba(0,0,0,.55);color:#fff;cursor:pointer;display:flex;align-items:center;justify-content:center;padding:0;font-size:14px">✕</button>
            ${m.id != null ? `
            <button type="button" class="diary-cover-btn diary-cover-btn--form${m.is_cover ? ' diary-cover-btn--active' : ''}"
                    data-media-id="${m.id}" data-sort="${idx}"
                    aria-label="${m.is_cover ? 'Cover-Bild' : 'Als Cover setzen'}"
                    title="${m.is_cover ? 'Cover-Bild' : 'Als Cover setzen'}"
                    style="position:absolute;bottom:4px;left:4px;width:28px;height:28px;border-radius:50%;border:none;background:${m.is_cover ? '#f5c518' : 'rgba(0,0,0,.45)'};color:${m.is_cover ? '#fff' : 'rgba(255,255,255,.7)'};cursor:pointer;display:flex;align-items:center;justify-content:center;padding:0;z-index:2"><svg style="width:16px;height:16px;display:block;flex-shrink:0" aria-hidden="true"><use href="/icons/phosphor.svg#star"></use></svg></button>` : ''}
          </div>`).join('')}
      </div>`;
      wrap.innerHTML = grid;
      wrap.querySelectorAll('.diary-media-thumb-del').forEach(btn => {
        btn.addEventListener('click', async () => {
          const wrap2 = btn.closest('.diary-media-thumb-wrap');
          const mediaId = btn.dataset.mediaId ? parseInt(btn.dataset.mediaId) : null;
          const isLegacy = !!btn.dataset.legacy;
          btn.disabled = true;
          try {
            if (mediaId != null) {
              await API.diary.deleteMediaItem(_appState.activeDog.id, entry.id, mediaId);
              // aus entry.media_items entfernen
              if (entry.media_items) entry.media_items = entry.media_items.filter(m => m.id !== mediaId);
            } else if (isLegacy) {
              await API.diary.deleteMedia(_appState.activeDog.id, entry.id);
              entry.media_url = null;
            }
            wrap2.remove();
            UI.toast.success('Medium entfernt.');
          } catch (e) {
            btn.disabled = false;
            UI.toast.error(e.message || 'Fehler beim Löschen.');
          }
        });
      });
      // Stern-Buttons im Edit-Formular
      wrap.querySelectorAll('.diary-cover-btn--form').forEach(btn => {
        btn.addEventListener('click', async () => {
          const mediaId = parseInt(btn.dataset.mediaId);
          try {
            await API.diary.setCover(_appState.activeDog.id, entry.id, mediaId);
            if (entry.media_items) {
              entry.media_items.forEach(m => { m.is_cover = m.id === mediaId ? 1 : 0; });
            }
            entry.cover_url = (entry.media_items || []).find(m => m.id === mediaId)?.url || null;
            _updateEntryInList(entry);
            // Alle Sterne in diesem Formular aktualisieren
            wrap.querySelectorAll('.diary-cover-btn--form').forEach(b => {
              const active = parseInt(b.dataset.mediaId) === mediaId;
              b.classList.toggle('diary-cover-btn--active', active);
              b.style.background = active ? '#f5c518' : 'rgba(0,0,0,.45)';
            b.style.color = active ? '#fff' : 'rgba(255,255,255,.7)';
              b.setAttribute('aria-label', active ? 'Cover-Bild' : 'Als Cover setzen');
              b.setAttribute('title',      active ? 'Cover-Bild' : 'Als Cover setzen');
              const use = b.querySelector('use');
              if (use) use.setAttribute('href', `/icons/phosphor.svg#${active ? 'star-fill' : 'star'}`);
            });
            UI.toast.success('Cover-Bild gesetzt.');
          } catch {
            UI.toast.error('Cover konnte nicht gesetzt werden.');
          }
        });
      });
    }
    _renderExistingMedia();

    function _addFiles(fileList) {
      for (const f of fileList) _newFiles.push(f);
      _renderNewGrid();
    }

    function _openPicker(opts = {}) {
      const tmp = document.createElement('input');
      tmp.type = 'file'; tmp.accept = 'image/*,video/*'; tmp.style.display = 'none';
      if (opts.capture) tmp.setAttribute('capture', opts.capture);
      if (opts.noAccept) tmp.removeAttribute('accept');
      tmp.addEventListener('change', () => {
        _addFiles(tmp.files);
        tmp.remove();
      });
      document.body.appendChild(tmp);
      tmp.click();
    }

    mediaInput?.addEventListener('change', () => {
      if (mediaInput.files.length) {
        _addFiles(mediaInput.files);
        mediaInput.value = '';
      }
    });


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

    // Milestone-Toggle
    document.getElementById('diary-milestone-btn')?.addEventListener('click', () => {
      const cb  = document.getElementById('diary-milestone-cb');
      const btn = document.getElementById('diary-milestone-btn');
      cb.checked = !cb.checked;
      btn.classList.toggle('diary-milestone-toggle--active', cb.checked);
      btn.querySelector('span').textContent = cb.checked ? 'Meilenstein ✓' : 'Als Meilenstein markieren';
    });

    // --- Location Picker ---
    let _locLat  = (entry?.gps_lat  != null) ? entry.gps_lat  : null;
    let _locLon  = (entry?.gps_lon  != null) ? entry.gps_lon  : null;
    let _locName = entry?.location_name || null;
    let _miniMap = null, _miniMarker = null;

    const _pinSvg = '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="40" viewBox="0 0 32 40"><path d="M16 0C7.163 0 0 7.163 0 16c0 10 16 24 16 24S32 26 32 16C32 7.163 24.837 0 16 0z" fill="#C4843A"/><circle cx="16" cy="16" r="7" fill="white"/></svg>';
    const _mkIcon = () => L.divIcon({ html: _pinSvg, className: '', iconSize: [32,40], iconAnchor: [16,40] });

    function _setName(name) {
      _locName = name;
      document.getElementById('diary-location-label').textContent = name;
      document.getElementById('diary-location-chip-wrap').style.display = '';
      document.getElementById('diary-location-suggestions').style.display = 'none';
    }

    function _placeMarker(lat, lon) {
      if (_miniMarker) { _miniMarker.setLatLng([lat, lon]); return; }
      _miniMarker = L.marker([lat, lon], { draggable: false, icon: _mkIcon() }).addTo(_miniMap);
      _miniMarker.on('dragend', () => {
        const p = _miniMarker.getLatLng(); _locLat = p.lat; _locLon = p.lng;
        document.getElementById('diary-location-btn-label').textContent = 'POI suchen';
      });
    }

    document.getElementById('diary-location-clear')?.addEventListener('click', () => {
      _locName = null;
      document.getElementById('diary-location-chip-wrap').style.display = 'none';
    });
    const _clearBtn = document.getElementById('diary-coords-clear');
    let _clearPending = false;
    _clearBtn?.addEventListener('click', () => {
      if (!_clearPending) {
        _clearPending = true;
        _clearBtn.textContent = 'Wirklich entfernen?';
        _clearBtn.style.color = 'var(--c-danger)';
        setTimeout(() => {
          if (_clearPending) {
            _clearPending = false;
            _clearBtn.textContent = 'Ort entfernen';
            _clearBtn.style.color = 'var(--c-text-muted)';
          }
        }, 3000);
        return;
      }
      _clearPending = false;
      _clearBtn.textContent = 'Ort entfernen';
      _clearBtn.style.color = 'var(--c-text-muted)';
      _locLat = null; _locLon = null; _locName = null;
      document.getElementById('diary-location-chip-wrap').style.display = 'none';
      document.getElementById('diary-location-suggestions').style.display = 'none';
      document.getElementById('diary-location-btn-label').textContent = 'POI suchen';
      if (_miniMarker) { _miniMarker.remove(); _miniMarker = null; }
      if (_miniMap) { _miniMap.setView([48.0, 11.9], 7); _setMapEditing(false); }
    });

    let _mapEditing = false;

    function _setMapEditing(on) {
      _mapEditing = on;
      const lbl = document.getElementById('diary-map-edit-label');
      if (lbl) lbl.textContent = on ? 'Fertig' : 'Position ändern';
      if (!_miniMap) return;
      if (on) {
        if (_miniMarker) _miniMarker.dragging.enable();
      } else {
        if (_miniMarker) _miniMarker.dragging.disable();
      }
    }

    document.getElementById('diary-map-edit-btn')?.addEventListener('click', () => {
      _setMapEditing(!_mapEditing);
    });

    // Karte beim Formular-Open automatisch laden
    UI.loadLeaflet().then(() => {
      setTimeout(() => {
        const lat = _locLat || 48.0, lon = _locLon || 11.9, zoom = _locLat ? 15 : 7;
        _miniMap = L.map('diary-map-wrap', {
          zoomControl: true, attributionControl: false,
          dragging: true, scrollWheelZoom: false,
        }).setView([lat, lon], zoom);
        L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 19 })
          .addTo(_miniMap);
        _miniMap.invalidateSize();
        if (_locLat) {
          _placeMarker(lat, lon);
          _miniMarker.dragging.disable(); // Lesemodus: kein Drag
        }
        // Klick nur im Edit-Modus
        _miniMap.on('click', e => {
          if (!_mapEditing) return;
          _locLat = e.latlng.lat; _locLon = e.latlng.lng;
          _placeMarker(_locLat, _locLon);
          if (!_mapEditing) _miniMarker.dragging.disable();
          document.getElementById('diary-location-btn-label').textContent = 'POI suchen';
        });
      }, 150);
    });

    async function _showSuggestions() {
      const btn = document.getElementById('diary-location-btn');
      UI.setLoading(btn, true);
      try {
        let lat = _locLat, lon = _locLon;
        if (lat == null || lon == null) {
          const pos = await API.getLocation();
          lat = pos.lat; lon = pos.lon;
          _locLat = lat; _locLon = lon;
          if (_miniMap) { _miniMap.setView([lat, lon], 15); _placeMarker(lat, lon); }
          document.getElementById('diary-location-btn-label').textContent = 'POI suchen';
        }
        const suggestions = await API.diary.nearby(_appState.activeDog.id, lat, lon);
        const sugEl = document.getElementById('diary-location-suggestions');
        if (suggestions.length === 0) {
          sugEl.innerHTML = '<p style="font-size:var(--text-sm);color:var(--c-text-secondary);padding:var(--space-2) 0">Keine Orte in der Nähe gefunden.</p>';
        } else {
          sugEl.innerHTML = suggestions.map(s => `
            <button type="button" class="diary-location-suggestion"
                    data-name="${UI.escape(s.name)}" data-lat="${s.lat}" data-lon="${s.lon}">
              <svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#${_sourceIcon(s.source)}"></use></svg>
              <span>${UI.escape(s.name)}</span>
              <small>${s.distance_m < 1000 ? s.distance_m + ' m' : (s.distance_m / 1000).toFixed(1) + ' km'}</small>
            </button>`).join('');
          sugEl.querySelectorAll('.diary-location-suggestion').forEach(el => {
            el.addEventListener('click', () => _setName(el.dataset.name));
          });
        }
        sugEl.style.display = '';
      } catch (err) {
        UI.toast.error(err?.message?.includes('GPS') || lat == null
          ? 'GPS nicht verfügbar.' : 'Ortssuche fehlgeschlagen.');
      } finally {
        UI.setLoading(btn, false);
      }
    }

    document.getElementById('diary-location-btn')?.addEventListener('click', _showSuggestions);

    document.getElementById('diary-form-delete')?.addEventListener('click', async () => {
      const ok = await UI.modal.confirm({
        title:       'Eintrag löschen?',
        message:     'Dieser Vorgang kann nicht rückgängig gemacht werden.',
        confirmText: 'Löschen',
        danger:      true,
      });
      if (ok) await _deleteEntry(entry.id);
    });

    // Checked-Klasse auf Dog-Picker-Items toggeln
    form.querySelectorAll('.diary-dog-pick-item input').forEach(cb => {
      cb.addEventListener('change', () => {
        cb.closest('.diary-dog-pick-item').classList.toggle('checked', cb.checked);
      });
    });

    form.addEventListener('submit', async e => {
      e.preventDefault();
      const submitBtn = document.querySelector('[form="diary-form"][type="submit"]') || form.querySelector('[type="submit"]');
      const fd = UI.formData(form);

      // dog_ids zusammenbauen: aktiver Hund + gewählte weitere
      const dogIds = [_appState.activeDog.id];
      form.querySelectorAll('.diary-dog-pick-item input:checked').forEach(cb => {
        const id = parseInt(cb.value);
        if (!dogIds.includes(id)) dogIds.push(id);
      });

      await UI.asyncButton(submitBtn, async () => {
        const payload = {
          datum:         fd.datum || null,
          typ:           fd.typ,
          titel:         fd.titel || null,
          text:          fd.text  || null,
          is_milestone:  'is_milestone' in fd,
          dog_ids:       dogIds,
          gps_lat:       _locLat,
          gps_lon:       _locLon,
          location_name: _locName,
          client_time:   API.clientNow(),
        };

        async function _uploadNewFiles(entryId) {
          let failCount = 0;
          const uploaded = [];
          let exifGps = null;
          for (const file of _newFiles) {
            try {
              const formData = new FormData();
              formData.append('file', file);
              const m = await API.diary.uploadMedia(_appState.activeDog.id, entryId, formData);
              uploaded.push(m);
              if (m.exif_lat != null && m.exif_lon != null && !exifGps) {
                exifGps = { lat: m.exif_lat, lon: m.exif_lon };
              }
            } catch {
              failCount++;
            }
          }
          if (failCount > 0) {
            UI.toast.warning(`${failCount} Medium${failCount > 1 ? 'en' : ''} konnte${failCount > 1 ? 'n' : ''} nicht hochgeladen werden.`);
          }
          if (exifGps) {
            UI.toast.success(`📍 Standort aus Foto-GPS übernommen`);
          }
          return { uploaded, exifGps };
        }

        if (isEdit) {
          const updated = await API.diary.update(_appState.activeDog.id, entry.id, payload);
          if (_newFiles.length > 0) {
            const { uploaded, exifGps } = await _uploadNewFiles(entry.id);
            if (!updated.media_items) updated.media_items = [];
            updated.media_items.push(...uploaded);
            if (exifGps && !updated.gps_lat) {
              updated.gps_lat = exifGps.lat;
              updated.gps_lon = exifGps.lon;
            }
          } else {
            updated.media_items = entry.media_items || updated.media_items || [];
            updated.media_url   = entry.media_url ?? updated.media_url;
          }
          _updateEntryInList(updated);
          UI.toast.success('Eintrag gespeichert.');
        } else {
          const created = await API.diary.create(_appState.activeDog.id, payload);
          if (_newFiles.length > 0) {
            const { uploaded, exifGps } = await _uploadNewFiles(created.id);
            created.media_items = uploaded;
            if (exifGps && !created.gps_lat) {
              created.gps_lat = exifGps.lat;
              created.gps_lon = exifGps.lon;
            }
          }
          _entries.unshift(created);
          UI.toast.success('Eintrag erstellt.');
        }

        UI.modal.close();
        _renderList();
        _loadStats().then(() => _renderStatsBar());
      });
    });
  }

  // ----------------------------------------------------------
  // EINTRAG LÖSCHEN
  // ----------------------------------------------------------
  async function _deleteEntry(entryId) {
    try {
      await API.diary.delete(_appState.activeDog.id, entryId);
      _entries = _entries.filter(e => e.id !== entryId);
      UI.modal.close();
      _renderList();
      _loadStats().then(() => _renderStatsBar());
      UI.toast.success('Eintrag gelöscht.');
    } catch (err) {
      UI.toast.error(err.message || 'Fehler beim Löschen.');
    }
  }

  // ----------------------------------------------------------
  // HELPER
  // ----------------------------------------------------------
  function _updateEntryInList(updated) {
    const i = _entries.findIndex(e => e.id === updated.id);
    if (i !== -1) _entries[i] = updated;
  }

  function _formatMonth(yearMonth) {
    const [y, m] = yearMonth.split('-');
    return new Intl.DateTimeFormat('de-DE', { month: 'long', year: 'numeric' })
      .format(new Date(+y, +m - 1, 1));
  }

  // ----------------------------------------------------------
  // IMPORT
  // ----------------------------------------------------------
  function _showImport() {
    UI.modal.open({
      title: 'Tagebuch importieren',
      body: `
        <p style="color:var(--c-text-secondary);font-size:var(--text-sm);margin-bottom:var(--space-4)">
          Importiere Einträge aus einer anderen App in das Tagebuch von
          <strong>${UI.escape(_appState.activeDog?.name || 'deinem Hund')}</strong>.
        </p>

        <div style="display:flex;flex-direction:column;gap:var(--space-3)">

          <label class="import-format-card" id="fmt-nsx">
            <input type="radio" name="import-fmt" value="nsx" checked style="display:none">
            <div class="import-format-icon">
              <svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#note"></use></svg>
            </div>
            <div>
              <div style="font-weight:var(--weight-semibold)">Synology NoteStation</div>
              <div style="font-size:var(--text-xs);color:var(--c-text-muted)">.nsx-Datei aus dem NoteStation-Export</div>
            </div>
          </label>

          <label class="import-format-card" id="fmt-csv">
            <input type="radio" name="import-fmt" value="csv" style="display:none">
            <div class="import-format-icon">
              <svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#file-csv"></use></svg>
            </div>
            <div>
              <div style="font-weight:var(--weight-semibold)">CSV / Excel</div>
              <div style="font-size:var(--text-xs);color:var(--c-text-muted)">Spalten: datum, titel, text, tags, gps_lat, gps_lon, is_milestone</div>
            </div>
          </label>
        </div>

        <div style="margin-top:var(--space-4)">
          <label class="form-label">Datei auswählen</label>
          <input type="file" class="form-control" id="import-file-input"
                 accept=".nsx,.csv" style="cursor:pointer">
        </div>

        <div id="import-result" style="display:none;margin-top:var(--space-4)"></div>`,

      footer: `
        <button class="btn btn-secondary" onclick="UI.modal.close()">Schließen</button>
        <button class="btn btn-primary" id="import-start-btn">Importieren</button>`,
    });

    // Format-Karten klickbar machen
    document.querySelectorAll('.import-format-card').forEach(card => {
      card.addEventListener('click', () => {
        document.querySelectorAll('.import-format-card').forEach(c => c.classList.remove('import-format-card--active'));
        card.classList.add('import-format-card--active');
        card.querySelector('input[type=radio]').checked = true;
        // Accept-Attribut anpassen
        const fmt = card.querySelector('input').value;
        document.getElementById('import-file-input').accept = fmt === 'nsx' ? '.nsx' : '.csv';
      });
    });
    // Erste Karte direkt aktiv setzen
    document.getElementById('fmt-nsx')?.classList.add('import-format-card--active');

    document.getElementById('import-start-btn').addEventListener('click', async () => {
      const fileInput = document.getElementById('import-file-input');
      const fmt       = document.querySelector('input[name="import-fmt"]:checked')?.value;
      const btn       = document.getElementById('import-start-btn');
      const resultEl  = document.getElementById('import-result');

      if (!fileInput.files.length) {
        UI.toast('Bitte zuerst eine Datei auswählen.', 'warning');
        return;
      }
      const file  = fileInput.files[0];
      const dogId = _appState.activeDog?.id;

      UI.setLoading(btn, true);
      resultEl.style.display = 'none';

      try {
        const res = fmt === 'nsx'
          ? await API.importData.notestation(dogId, file)
          : await API.importData.csv(dogId, file);

        const errHtml = res.errors?.length
          ? `<details style="margin-top:var(--space-2)"><summary style="font-size:var(--text-xs);cursor:pointer">${res.errors.length} Fehler anzeigen</summary>
               <pre style="font-size:var(--text-xs);white-space:pre-wrap;margin-top:var(--space-1)">${UI.escape(res.errors.join('\n'))}</pre></details>`
          : '';

        resultEl.innerHTML = `
          <div style="background:var(--c-success-subtle);border-radius:var(--radius-md);
                      padding:var(--space-3) var(--space-4);color:var(--c-success)">
            <strong>${res.imported} Einträge importiert</strong>
            ${res.skipped ? `<span style="color:var(--c-text-muted);font-size:var(--text-sm)"> · ${res.skipped} übersprungen</span>` : ''}
            ${errHtml}
          </div>`;
        resultEl.style.display = 'block';
        UI.setLoading(btn, false);

        // Diary neu laden falls etwas importiert wurde
        if (res.imported > 0) {
          _offset  = 0;
          _entries = [];
          await _load();
          _renderList();
        }
      } catch (e) {
        resultEl.innerHTML = `
          <div style="background:var(--c-danger-subtle);border-radius:var(--radius-md);
                      padding:var(--space-3) var(--space-4);color:var(--c-danger)">
            Fehler: ${UI.escape(e.message || String(e))}
          </div>`;
        resultEl.style.display = 'block';
        UI.setLoading(btn, false);
      }
    });
  }

  // ----------------------------------------------------------
  // NOTIZ-MODAL (custom DOM, kein UI.modal um Konflikte zu vermeiden)
  // ----------------------------------------------------------
  async function _openNoteModal(parentType, parentId, parentLabel, locationName) {
    document.getElementById('by-note-modal')?.remove();

    const overlay = document.createElement('div');
    overlay.id = 'by-note-modal';
    overlay.style.cssText = 'position:fixed;inset:0;z-index:2000;background:rgba(0,0,0,.55);display:flex;align-items:flex-end;justify-content:center';

    overlay.innerHTML = `
      <div style="background:var(--c-surface);border-radius:var(--radius-xl) var(--radius-xl) 0 0;
                  width:100%;max-width:640px;max-height:90vh;display:flex;flex-direction:column;
                  padding-bottom:env(safe-area-inset-bottom,0px)">
        <div style="padding:var(--space-4) var(--space-5);border-bottom:1px solid var(--c-border);
                    display:flex;align-items:center;justify-content:space-between;flex-shrink:0">
          <div>
            <div style="font-weight:var(--weight-semibold);font-size:var(--text-base)"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#note-pencil"></use></svg> Notiz</div>
            <div style="font-size:var(--text-xs);color:var(--c-text-muted);margin-top:2px">${UI.escape(parentLabel)}</div>
          </div>
          <button id="by-note-close" style="background:none;border:none;cursor:pointer;font-size:1.4rem;color:var(--c-text-muted);padding:4px 8px" aria-label="Schließen">×</button>
        </div>
        <div style="padding:var(--space-4) var(--space-5);flex:1;overflow-y:auto">
          <form id="by-note-form">
            <textarea id="by-note-text" class="form-control" rows="5"
                      placeholder="Notiz eingeben…"
                      style="width:100%;resize:vertical"></textarea>
          </form>
        </div>
        <div style="padding:var(--space-3) var(--space-5);border-top:1px solid var(--c-border);
                    display:flex;gap:var(--space-2);flex-shrink:0">
          <button type="button" id="by-note-cancel" class="btn btn-secondary flex-1">Abbrechen</button>
          <button type="submit" form="by-note-form" id="by-note-save" class="btn btn-primary flex-1">Speichern</button>
        </div>
      </div>
    `;

    document.body.appendChild(overlay);

    const textarea  = document.getElementById('by-note-text');
    const saveBtn   = document.getElementById('by-note-save');
    const cancelBtn = document.getElementById('by-note-cancel');
    const closeBtn  = document.getElementById('by-note-close');

    let existingNoteId = null;

    try {
      const existing = await API.notes.get(parentType, parentId);
      if (existing?.id) {
        existingNoteId = existing.id;
        textarea.value = existing.text || '';
      }
    } catch (_) { /* keine Notiz vorhanden — ok */ }

    setTimeout(() => textarea.focus(), 100);

    const _close = () => overlay.remove();
    closeBtn.addEventListener('click', _close);
    cancelBtn.addEventListener('click', _close);
    overlay.addEventListener('click', e => { if (e.target === overlay) _close(); });

    document.getElementById('by-note-form').addEventListener('submit', async e => {
      e.preventDefault();
      const text = textarea.value.trim();
      UI.setLoading(saveBtn, true);
      try {
        const payload = { text, parent_label: parentLabel, location_name: locationName, client_time: API.clientNow() };
        if (existingNoteId) {
          await API.notes.update(existingNoteId, payload);
        } else {
          await API.notes.create(parentType, parentId, payload);
        }
        UI.toast.success('Notiz gespeichert.');
        _close();
      } catch (err) {
        UI.toast.error(err.message || 'Fehler beim Speichern.');
        UI.setLoading(saveBtn, false);
      }
    });
  }

  // ----------------------------------------------------------
  // PUBLIC
  // ----------------------------------------------------------
  function _cleanText(text) {
    if (!text) return text;
    return text
      .replace(/!\[([^\]]*)\]\([^\)]*\)/g, '')  // Markdown-Bilder ![]()
      .replace(/\[([^\]]*)\]\([^\)]*\)/g, '$1')  // Markdown-Links [text](url) → text
      .replace(/^\[\]\s*$/gm, '')                 // leere [] auf eigener Zeile
      .replace(/\n{3,}/g, '\n\n')                 // mehrfache Leerzeilen kürzen
      .trim();
  }

  return { init, refresh, openNew, onDogChange, openDetail: _openDetail };

})();
