/**
 * AegisSight Lagebild Page - Dark Theme Design Refresh
 * Count-Up, Timeline, Scroll-Reveal, Particles, Live-Feed, Pulse-Markers
 */

/** Feste Zeitzone fuer alle Anzeigen - NIEMALS aendern. */
var TIMEZONE = 'Europe/Berlin';

var Lagebild = {
    data: null,
    allSnapshots: {},
    currentView: null,
    map: null,
    timelineGroups: null,

    /* ===== Inline SVG Icons ===== */
    /* ===== LANGUAGE SUPPORT ===== */
    lang: {
        de: {
            hero: "LAGEBILD", heroResearch: "RECHERCHE",
            tabBriefing: "Lagebild", tabBriefingResearch: "Recherche",
            tabUeberblick: "Neueste Entwicklungen", tabUeberblickResearch: "Zusammenfassung",
            tabMap: "Karte", tabFactchecks: "Faktenchecks", tabSources: "Quellen",
            statArticles: "Artikel", statSources: "Quellen", statFactchecks: "Faktenchecks",
            dataSource: "Daten bereitgestellt durch AegisSight Monitor",
            timelineCurrent: "Aktuell", timelineArticles: "Artikel", timelineFcs: "Faktenchecks",
            srcArticlesFrom: "{count} Artikel aus {sources} Quellen",
            srcArticle: "Artikel", srcClose: "Schlie\u00dfen",
            mapNoData: "Keine Standortdaten verf\u00fcgbar", mapLegend: "Legende", mapArticles: "Artikel",
            fcTotal: "Gesamt", fcConfirmed: "Best\u00e4tigt", fcOpen: "Offen", fcContradicted: "Widerlegt",
            fcEvidence: "Evidenz:", fcProgression: "Verlauf:", fcSources: "unabh\u00e4ngige Quellen",
            fcNone: "Keine Faktenchecks verf\u00fcgbar.",
            fcCleaned: "{count} von {total} Faktenchecks verf\u00fcgbar (\u00e4ltere wurden bereinigt)",
            ctaText: "AegisSight Monitor f\u00fcr Ihre Organisation", ctaButton: "Kontakt aufnehmen \u2192",
            errorLoad: "Das Lagebild konnte nicht geladen werden. Bitte versuchen Sie es sp\u00e4ter erneut.",
            snapshotHint: "",
            standPrefix: "Stand: ", standSuffix: " Uhr",
            stConfirmed: "Best\u00e4tigt", stUnconfirmed: "Unbest\u00e4tigt", stContradicted: "Widerlegt",
            stDeveloping: "Unklar", stEstablished: "Gesichert", stDisputed: "Umstritten",
            stFalse: "Falsch", stUnverified: "Nicht verifiziert",
            sourceRef: "Quelle",
            lastUpdate: "Letzte Aktualisierung: ",
            minAgo: "vor {n} Min", hrsAgo: "vor {n} Std",
            emptyDevelopments: "Noch keine Entwicklungen erfasst.",
            emptySummary: "Keine Zusammenfassung verfügbar.",
        },
        en: {
            hero: "SITUATION REPORT", heroResearch: "RESEARCH BRIEFING",
            tabBriefing: "Briefing", tabBriefingResearch: "Research",
            tabUeberblick: "Latest Developments", tabUeberblickResearch: "Summary",
            tabMap: "Map", tabFactchecks: "Fact Checks", tabSources: "Sources",
            statArticles: "Articles", statSources: "Sources", statFactchecks: "Fact Checks",
            dataSource: "Data provided by AegisSight Monitor",
            timelineCurrent: "Current", timelineArticles: "Articles", timelineFcs: "Fact Checks",
            srcArticlesFrom: "{count} articles from {sources} sources",
            srcArticle: "Articles", srcClose: "Close",
            mapNoData: "No location data available", mapLegend: "Legend", mapArticles: "Articles",
            fcTotal: "Total", fcConfirmed: "Confirmed", fcOpen: "Open", fcContradicted: "Contradicted",
            fcEvidence: "Evidence:", fcProgression: "History:", fcSources: "independent sources",
            fcNone: "No fact checks available.",
            fcCleaned: "{count} of {total} fact checks available (older ones were cleaned up)",
            ctaText: "AegisSight Monitor for your organization", ctaButton: "Contact us \u2192",
            errorLoad: "The briefing could not be loaded. Please try again later.",
            snapshotHint: "Historical data available in German only",
            standPrefix: "As of: ", standSuffix: "",
            stConfirmed: "Confirmed", stUnconfirmed: "Unconfirmed", stContradicted: "Contradicted",
            stDeveloping: "Developing", stEstablished: "Established", stDisputed: "Disputed",
            stFalse: "False", stUnverified: "Unverified",
            sourceRef: "Source",
            lastUpdate: "Last update: ",
            minAgo: "{n} min ago", hrsAgo: "{n} hrs ago",
            emptyDevelopments: "No developments recorded yet.",
            emptySummary: "No summary available.",
        }
    },

    curLang: function() {
        var l = (document.documentElement.lang || 'de').toLowerCase();
        return l.indexOf('en') === 0 ? 'en' : 'de';
    },

    /* Where to fetch data/current.json from. Defaults to relative ("data/")
       which works on the German pages. English mirror pages set
       window.LAGEBILD_DATA_BASE to the absolute path of the German page so
       both languages share the same data files. */
    dataBase: function() {
        return (typeof window !== 'undefined' && window.LAGEBILD_DATA_BASE) || '';
    },

    t: function(key) {
        var cl = this.curLang();
        return (this.lang[cl] && this.lang[cl][key]) || this.lang.de[key] || key;
    },

    getLocale: function() {
        return this.curLang() === 'en' ? 'en-GB' : 'de-DE';
    },

    getHeadline: function(article) {
        if (this.curLang() === 'en') return article.headline || article.headline_de || '';
        return article.headline_de || article.headline || '';
    },

    icons: {
        clock: '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>',
        fileText: '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/></svg>',
        globe: '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>',
        shieldCheck: '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/><polyline points="9 12 11 14 15 10"/></svg>',
        externalLink: '<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg>'
    },

    async init() {
        this.initScrollProgress();
        this.initParticles();
        try {
            var savedLang = this.curLang();
            var base = this.dataBase();
            var jsonFile = base + (savedLang === 'en' ? 'data/current_en.json' : 'data/current.json');
            var resp = await fetch(jsonFile + '?t=' + Date.now());
            if (!resp.ok && savedLang === 'en') { resp = await fetch(base + 'data/current.json?t=' + Date.now()); }
            if (!resp.ok) throw new Error('HTTP ' + resp.status);
            this.data = await resp.json();
            this.currentView = {
                summary: this.data.current_lagebild.summary_markdown,
                sources_json: this.data.current_lagebild.sources_json,
                updated_at: this.data.current_lagebild.updated_at || this.data.generated_at,
                articles: this.data.articles,
                fact_checks: this.data.fact_checks,
                article_count: this.data.incident.article_count,
                fact_check_count: this.data.incident.factcheck_count
            };
            this.render();
            this.initTabs();
            this.initScrollReveal();
            this.initFloatingCta();
            this.initLiveFeed();
        } catch (e) {
            console.error('Lagebild laden fehlgeschlagen:', e);
            this.showError();
        }
    },

    render: function() {
        this.renderHero();
        this.renderTimeline();
        this.renderTabBadges();
        this.renderCurrentView();
        this.renderUeberblick();
    },

    /* ===== TAB: UEBERBLICK (Neueste Entwicklungen / Zusammenfassung) ===== */
    renderUeberblick: function() {
        var inc = (this.data && this.data.incident) || {};
        var el = document.getElementById('ueberblick-content');
        var tsEl = document.getElementById('ueberblick-timestamp');
        if (!el) return;
        if (tsEl) tsEl.textContent = this.fmtDT(this.currentView && this.currentView.updated_at);

        if (inc.type === 'adhoc') {
            var dev = inc.latest_developments || '';
            var sources = (this.currentView && this.currentView.sources_json) || [];
            var html = this.renderLatestDevelopmentsHtml(dev, sources);
            el.innerHTML = html || '<p class="empty-hint">' + this.t('emptyDevelopments') + '</p>';
        } else {
            var md = (this.currentView && this.currentView.summary) || '';
            var zf = this.extractZusammenfassung(md);
            if (!zf) {
                el.innerHTML = '<p class="empty-hint">' + this.t('emptySummary') + '</p>';
                return;
            }
            var body = this.mdToHtml(this.fixUmlauts(zf));
            var srcMap = {};
            var sources = (this.currentView && this.currentView.sources_json) || [];
            for (var i = 0; i < sources.length; i++) srcMap[String(sources[i].nr)] = sources[i];
            var self = this;
            body = body.replace(/\[(\d+[a-z]?)\]/g, function(match, nr) {
                var src = srcMap[nr];
                if (src && src.url) {
                    return '<a class="citation-ref" href="' + self.esc(src.url) + '" target="_blank" rel="noopener" title="' + self.esc(src.name || '') + '">[' + nr + ']</a>';
                }
                return '<a class="citation-ref" title="' + self.t('sourceRef') + ' ' + nr + '">[' + nr + ']</a>';
            });
            el.innerHTML = body;
        }
    },

    extractZusammenfassung: function(md) {
        if (!md) return '';
        var sections = md.split(/^## /m);
        for (var i = 0; i < sections.length; i++) {
            var s = sections[i];
            if (/^(zusammenfassung|summary)\b/i.test(s.trim())) {
                var next = s.split(/\n## /)[0];
                return next.replace(/^[^\n]*\n?/, '').trim();
            }
        }
        return '';
    },

    stripZusammenfassung: function(md) {
        if (!md) return md;
        var lines = md.split('\n');
        var result = [];
        var skipping = false;
        for (var i = 0; i < lines.length; i++) {
            var line = lines[i];
            if (/^##\s+(zusammenfassung|summary)\b/i.test(line)) { skipping = true; continue; }
            if (skipping && /^##\s+/.test(line)) skipping = false;
            if (!skipping) result.push(line);
        }
        return result.join('\n').replace(/^\s+/, '');
    },

    renderLatestDevelopmentsHtml: function(text, sources) {
        if (!text) return '';
        sources = Array.isArray(sources) ? sources : [];
        var self = this;

        var lines = text.split('\n').map(function(l){return l.trim();})
            .filter(function(l){return l && (l.charAt(0) === '-' || l.charAt(0) === '[');});
        if (!lines.length) return '';

        var bulletRe = /^(?:-\s*)?\[\s*(\d{1,2})\.(\d{1,2})\.?(?:\d{2,4})?\s+(\d{1,2}:\d{2})\s*\]\s*(.+?)\s*$/;
        var trailingRe = /\s*\{([^{}]+)\}\s*\.?\s*$/;
        var citationRe = /\[(\d+[a-z]?)\]/g;
        var junkRe = /^(unbekannt|unknown|n\/?a|keine|keine quelle|tba)$/i;

        function normName(s) { return String(s||'').toLowerCase().replace(/^(der|die|das)\s+/,'').replace(/\s+/g,' ').trim(); }
        function lookupByName(name) {
            var n = normName(name); if (!n) return null;
            var exact = sources.find(function(s){ return normName(s.name) === n; });
            if (exact) return exact;
            return sources.find(function(s){ var sn=normName(s.name); return sn && (sn.indexOf(n)!==-1 || n.indexOf(sn)!==-1); }) || null;
        }
        function buildPill(src, name) {
            var disp = (src && src.name) || name;
            var url = (src && src.url) || '';
            var tgMatch = url.match(/^https?:\/\/t\.me\/([^\/?#]+)/i);
            var label = tgMatch ? disp + ' (t.me/' + tgMatch[1] + ')' : disp;
            var e = self.esc(label);
            var titleEsc = self.esc(disp);
            if (src && src.url) return '<a href="'+self.esc(src.url)+'" target="_blank" rel="noopener" class="dev-source-pill" title="'+titleEsc+'">'+e+'</a>';
            return '<span class="dev-source-pill" title="'+titleEsc+'">'+e+'</span>';
        }

        var cards = [];
        for (var i = 0; i < lines.length; i++) {
            var m = bulletRe.exec(lines[i]); if (!m) continue;
            var date = String(m[1]).padStart(2,'0') + '.' + String(m[2]).padStart(2,'0') + '.';
            var time = m[3]; var body = m[4];

            var pills = '';
            var t = trailingRe.exec(body);
            if (t) {
                body = body.replace(trailingRe, '').trim();
                var items = t[1].split(',').map(function(n){return n.trim();}).filter(Boolean);
                var seen = {};
                pills = items.map(function(item){
                    var pipeIdx = item.indexOf('|');
                    var itemName = pipeIdx >= 0 ? item.slice(0, pipeIdx).trim() : item.trim();
                    var itemUrl  = pipeIdx >= 0 ? item.slice(pipeIdx + 1).trim() : '';
                    if (!itemName || junkRe.test(itemName)) return '';
                    var k = normName(itemName); if (seen[k]) return ''; seen[k] = true;
                    if (itemUrl) {
                        return buildPill({ name: itemName, url: itemUrl }, itemName);
                    }
                    return buildPill(lookupByName(itemName), itemName);
                }).filter(Boolean).join('');
            }
            if (!pills) {
                var nums = []; var cm;
                while ((cm = citationRe.exec(body)) !== null) { if (nums.indexOf(cm[1]) === -1) nums.push(cm[1]); }
                citationRe.lastIndex = 0;
                if (nums.length) {
                    body = body.replace(citationRe, '').replace(/\s+/g, ' ').trim();
                    pills = nums.map(function(num){
                        var src = sources.find(function(s){ return String(s.nr) === num || Number(s.nr) === Number(num); });
                        return src ? buildPill(src, src.name) : '';
                    }).filter(Boolean).join('');
                }
            }

            var head = '<div class="dev-bullet-head">'
                + '<span class="dev-sources">' + pills + '</span>'
                + '<span class="dev-time">' + self.esc(time) + ' \u00b7 ' + self.esc(date) + '</span>'
                + '</div>';
            cards.push('<div class="dev-bullet">' + head + '<div class="dev-body">' + self.esc(body) + '</div></div>');
        }

        return cards.length ? '<div class="dev-list">' + cards.join('') + '</div>' : '';
    },

    /* ===== SCROLL PROGRESS BAR ===== */
    initScrollProgress: function() {
        var bar = document.getElementById('scroll-progress');
        if (!bar) return;
        window.addEventListener('scroll', function() {
            var scrollTop = window.scrollY;
            var docHeight = document.documentElement.scrollHeight - window.innerHeight;
            if (docHeight <= 0) return;
            bar.style.width = ((scrollTop / docHeight) * 100) + '%';
        });
    },

    /* ===== HERO PARTICLES ===== */
    initParticles: function() {
        var canvas = document.getElementById('hero-particles');
        if (!canvas) return;
        var ctx = canvas.getContext('2d');
        var particles = [];
        var count = 35;
        var connectDist = 120;

        function resize() {
            var hero = canvas.parentElement;
            canvas.width = hero.offsetWidth;
            canvas.height = hero.offsetHeight;
        }
        resize();
        window.addEventListener('resize', resize);

        for (var i = 0; i < count; i++) {
            particles.push({
                x: Math.random() * canvas.width,
                y: Math.random() * canvas.height,
                vx: (Math.random() - 0.5) * 0.4,
                vy: (Math.random() - 0.5) * 0.4,
                r: Math.random() * 1.5 + 0.5
            });
        }

        function draw() {
            ctx.clearRect(0, 0, canvas.width, canvas.height);

            // Draw connections
            for (var i = 0; i < particles.length; i++) {
                for (var j = i + 1; j < particles.length; j++) {
                    var dx = particles[i].x - particles[j].x;
                    var dy = particles[i].y - particles[j].y;
                    var dist = Math.sqrt(dx * dx + dy * dy);
                    if (dist < connectDist) {
                        var alpha = (1 - dist / connectDist) * 0.15;
                        ctx.beginPath();
                        ctx.strokeStyle = 'rgba(200, 168, 81, ' + alpha + ')';
                        ctx.lineWidth = 0.5;
                        ctx.moveTo(particles[i].x, particles[i].y);
                        ctx.lineTo(particles[j].x, particles[j].y);
                        ctx.stroke();
                    }
                }
            }

            // Draw & move particles
            for (var k = 0; k < particles.length; k++) {
                var p = particles[k];
                ctx.beginPath();
                ctx.arc(p.x, p.y, p.r, 0, Math.PI * 2);
                ctx.fillStyle = 'rgba(200, 168, 81, 0.4)';
                ctx.fill();

                p.x += p.vx;
                p.y += p.vy;
                if (p.x < 0 || p.x > canvas.width) p.vx *= -1;
                if (p.y < 0 || p.y > canvas.height) p.vy *= -1;
            }

            requestAnimationFrame(draw);
        }
        draw();
    },

    /* ===== LIVE FEED TICKER ===== */
    initLiveFeed: function() {
        var container = document.getElementById('live-feed');
        if (!container) return;

        var d = this.data;
        var genDate = new Date(this.toUTC(d.generated_at));
        var diffMin = Math.max(1, Math.round((Date.now() - genDate.getTime()) / 60000));
        var diffText = diffMin < 60 ? this.t('minAgo').replace('{n}', diffMin) : this.t('hrsAgo').replace('{n}', Math.round(diffMin / 60));

        container.innerHTML = '<div class="live-feed-item active">'
            + '<span class="live-feed-dot"></span>'
            + '<span>' + this.t('lastUpdate') + diffText + '</span>'
            + '</div>';
    },

    /* ===== HERO ===== */
    renderHero: function() {
        var d = this.data;
        document.getElementById('incident-title').innerHTML =
            this.esc(this.fixUmlauts(d.incident.title)) +
            ' <span class="hero-date-info">\u2013 ' + this.t('standPrefix') + this.fmtDateOnly(d.generated_at) + ', ' + this.fmtTimeOnly(d.generated_at) + this.t('standSuffix') + '</span>';

        // Stat Cards (3: Artikel, Quellen, Faktenchecks)
        var statsHtml = '';
        statsHtml += this.statCard(this.icons.fileText, '<span class="count-up" id="hero-art-count" data-target="' + d.incident.article_count + '">0</span>', this.t("statArticles"));
        statsHtml += this.statCard(this.icons.globe, '<span class="count-up" id="hero-src-count" data-target="' + d.incident.source_count + '">0</span>', this.t("statSources"));
        statsHtml += this.statCard(this.icons.shieldCheck, '<span class="count-up" id="hero-fc-count" data-target="' + d.incident.factcheck_count + '">0</span>', this.t("statFactchecks"));
        document.getElementById('hero-stats').innerHTML = statsHtml;

        // Start count-up animations
        var self = this;
        requestAnimationFrame(function() {
            var els = document.querySelectorAll('.count-up');
            for (var i = 0; i < els.length; i++) {
                self.animateCount(els[i], parseInt(els[i].getAttribute('data-target')), 800);
            }
        });
    },

    statCard: function(icon, value, label) {
        return '<div class="stat-card">' +
            '<div class="stat-card-icon">' + icon + '</div>' +
            '<div class="stat-card-content">' +
            '<span class="stat-card-value">' + value + '</span>' +
            '<span class="stat-card-label">' + label + '</span>' +
            '</div></div>';
    },

    /* ===== COUNT-UP ANIMATION ===== */
    animateCount: function(element, target, duration) {
        var start = performance.now();
        function update(now) {
            var elapsed = now - start;
            var progress = Math.min(elapsed / duration, 1);
            var eased = 1 - Math.pow(1 - progress, 3); // easeOutCubic
            var current = Math.round(target * eased);
            element.textContent = current.toLocaleString(Lagebild.getLocale());
            if (progress < 1) {
                requestAnimationFrame(update);
            }
        }
        requestAnimationFrame(update);
    },

    /* ===== TIMELINE STRIP ===== */
    renderTimeline: function() {
        var snaps = this.data.available_snapshots || [];
        var lagebildUpdated = (this.data.current_lagebild.updated_at || '').replace(' ', 'T');
        var newestSnap = snaps.length > 0 ? snaps[0] : null;
        var newestSnapTime = newestSnap ? newestSnap.created_at.replace(' ', 'T') : '';

        var all;
        if (!newestSnap || lagebildUpdated !== newestSnapTime) {
            // Neues Lagebild seit letztem Snapshot – eigenen current-Eintrag zeigen
            var current = {
                id: 'current',
                article_count: this.data.incident.article_count,
                fact_check_count: this.data.incident.factcheck_count,
                created_at: this.data.generated_at
            };
            all = [current].concat(snaps);
        } else {
            // Lagebild identisch mit neuestem Snapshot – kein Geister-Eintrag,
            // stattdessen Snapshot als "current" mit aktuellen Live-Counts markieren
            var merged = {};
            for (var key in newestSnap) {
                if (newestSnap.hasOwnProperty(key)) merged[key] = newestSnap[key];
            }
            merged.id = 'current';
            merged.article_count = this.data.incident.article_count;
            merged.fact_check_count = this.data.incident.factcheck_count;
            all = [merged].concat(snaps.slice(1));
        }

        // Group by date
        var groups = {};
        for (var i = 0; i < all.length; i++) {
            var s = all[i];
            var dateKey = this.toDateKey(s.created_at);
            if (!groups[dateKey]) groups[dateKey] = [];
            groups[dateKey].push(s);
        }

        // Sort each group descending (newest first)
        for (var dk in groups) {
            groups[dk].sort(function(a, b) {
                return new Date(Lagebild.toUTC(b.created_at)) - new Date(Lagebild.toUTC(a.created_at));
            });
        }

        this.timelineGroups = groups;
        var dates = Object.keys(groups).sort();
        var strip = document.getElementById('timeline-strip');
        var h = '';

        for (var j = 0; j < dates.length; j++) {
            var date = dates[j];
            var daySnaps = groups[date];
            var isActive = (j === dates.length - 1);
            var defaultSnap = isActive ? daySnaps[0] : daySnaps[daySnaps.length - 1];
            var d = new Date(date + 'T12:00:00Z');

            h += '<button class="timeline-day' + (isActive ? ' active' : '') + '"';
            h += ' data-date="' + date + '"';
            h += ' data-snapshot-id="' + defaultSnap.id + '"';
            if (daySnaps.length > 1) {
                h += ' title="' + daySnaps.length + ' Updates an diesem Tag"';
            }
            h += '>';
            if (isActive) h += '<span class="timeline-dot"></span>';
            h += '<span class="timeline-day-num">' + d.getUTCDate() + '</span>';
            h += '<span class="timeline-day-month">' + d.toLocaleDateString(this.getLocale(), { month: 'short', timeZone: 'UTC' }) + '</span>';
            h += '<span class="timeline-day-count">' + defaultSnap.article_count + '</span>';
            if (daySnaps.length > 1) {
                h += '<span class="timeline-day-updates">' + daySnaps.length + 'x</span>';
            }
            if (isActive) {
                h += '<span class="timeline-day-label">' + this.t('timelineCurrent') + '</span>';
            }
            h += '</button>';
        }

        strip.innerHTML = h;

        // Scroll to active day
        var active = strip.querySelector('.timeline-day.active');
        if (active) {
            setTimeout(function() {
                active.scrollIntoView({ behavior: 'smooth', inline: 'center', block: 'nearest' });
            }, 150);
        }

        // Click handler for day buttons
        var self = this;
        strip.addEventListener('click', function(e) {
            var btn = e.target.closest('.timeline-day');
            if (!btn) return;

            var allDays = strip.querySelectorAll('.timeline-day');
            for (var k = 0; k < allDays.length; k++) allDays[k].classList.remove('active');
            btn.classList.add('active');

            var dateKey = btn.getAttribute('data-date');
            var snapId = btn.getAttribute('data-snapshot-id');

            self.showTimelineDropdown(dateKey, snapId);

            if (snapId === 'current') {
                self.currentView = {
                    summary: self.data.current_lagebild.summary_markdown,
                    sources_json: self.data.current_lagebild.sources_json,
                    updated_at: self.data.current_lagebild.updated_at || self.data.generated_at,
                    articles: self.data.articles,
                    fact_checks: self.data.fact_checks,
                    article_count: self.data.incident.article_count,
                    fact_check_count: self.data.incident.factcheck_count
                };
                self.renderCurrentView();
            } else {
                self.loadSnapshot(parseInt(snapId));
            }
        });

        // Click handler for dropdown snapshot items (delegated, set up once)
        var dropdown = document.getElementById('timeline-dropdown');
        dropdown.addEventListener('click', function(e) {
            var item = e.target.closest('.h-timeline-point');
            if (!item) return;

            var items = dropdown.querySelectorAll('.h-timeline-point');
            for (var k = 0; k < items.length; k++) items[k].classList.remove('active');
            item.classList.add('active');

            var snapId = item.getAttribute('data-snapshot-id');
            if (snapId === 'current') {
                self.currentView = {
                    summary: self.data.current_lagebild.summary_markdown,
                    sources_json: self.data.current_lagebild.sources_json,
                    updated_at: self.data.current_lagebild.updated_at || self.data.generated_at,
                    articles: self.data.articles,
                    fact_checks: self.data.fact_checks,
                    article_count: self.data.incident.article_count,
                    fact_check_count: self.data.incident.factcheck_count
                };
                self.renderCurrentView();
            } else {
                self.loadSnapshot(parseInt(snapId));
            }
        });

        // Show dropdown for newest day by default
        var newestDate = dates[dates.length - 1];
        if (newestDate) {
            this.showTimelineDropdown(newestDate, groups[newestDate][0].id);
        }
    },

    showTimelineDropdown: function(dateKey, activeSnapId) {
        var dropdown = document.getElementById('timeline-dropdown');
        var snaps = this.timelineGroups[dateKey];

        if (!snaps || snaps.length === 0) {
            dropdown.classList.remove('open');
            dropdown.innerHTML = '';
            return;
        }

        // Oldest left, newest right
        var ordered = snaps.slice().reverse();

        var h = '<div class="h-timeline-track">';
        for (var i = 0; i < ordered.length; i++) {
            var snap = ordered[i];
            var isActive = (String(snap.id) === String(activeSnapId));
            h += '<button class="h-timeline-point' + (isActive ? ' active' : '') + '"';
            h += ' data-snapshot-id="' + snap.id + '">';
            h += '<span class="h-timeline-time">' + this.fmtTimeOnly(snap.created_at) + '</span>';
            h += '<span class="h-timeline-dot"></span>';
            h += '<span class="h-timeline-meta">' + snap.article_count + ' ' + this.t('timelineArticles') + '</span>';
            h += '<span class="h-timeline-meta">' + (snap.fact_check_count || 0) + ' ' + this.t('timelineFcs') + '</span>';
            h += '</button>';
        }
        h += '</div>';

        dropdown.innerHTML = h;
        dropdown.classList.add('open');

        // Scroll to active point
        var activePoint = dropdown.querySelector('.h-timeline-point.active');
        if (activePoint) {
            setTimeout(function() {
                activePoint.scrollIntoView({ behavior: 'smooth', inline: 'center', block: 'nearest' });
            }, 50);
        }
    },

    toDateKey: function(iso) {
        if (!iso) return '';
        var d = new Date(this.toUTC(iso));
        return d.toLocaleDateString('en-CA', { timeZone: TIMEZONE });
    },

    /* ===== TAB BADGES ===== */
    renderTabBadges: function() {
        var quellenBadge = document.getElementById('tab-badge-quellen');
        var fcBadge = document.getElementById('tab-badge-faktenchecks');
        if (quellenBadge) quellenBadge.textContent = this.data.incident.source_count;
        if (fcBadge) fcBadge.textContent = this.data.incident.factcheck_count;
    },

    /* ===== SNAPSHOT LOADING ===== */
    loadSnapshot: async function(id) {
        if (this.allSnapshots[id]) {
            this.currentView = this.allSnapshots[id];
            this.renderCurrentView();
            return;
        }
        try {
            var resp = await fetch(this.dataBase() + 'data/snapshot-' + id + '.json');
            if (!resp.ok) throw new Error('HTTP ' + resp.status);
            var sd = await resp.json();
            var sj = sd.sources_json;
            if (typeof sj === 'string') { try { sj = JSON.parse(sj); } catch(e) { sj = []; } }
            this.currentView = {
                summary: sd.summary,
                sources_json: sj || [],
                updated_at: sd.created_at,
                articles: this.filterArticlesAtTime(sd.created_at),
                fact_checks: this.getFactChecksAtTime(sd.created_at),
                article_count: sd.article_count,
                fact_check_count: sd.fact_check_count
            };
            this.allSnapshots[id] = this.currentView;
            this.renderCurrentView();
        } catch (e) { console.error('Snapshot Fehler:', e); }
    },

    renderCurrentView: function() {
        this.renderSummary();
        this.renderInlineSources();
        this.renderSourcesTab();
        this.renderArticlesTab();
        this.renderFactChecksTab();
        if (document.getElementById('panel-karte').classList.contains('active')) {
            this.renderMap();
        }
        // Counts aktualisieren (Hero + Badges)
        var articles = this.currentView.articles || [];
        var artCount = this.currentView.article_count || articles.length;
        var srcCount = new Set(articles.map(function(a) { return a.source; })).size;
        var fcCount = this.currentView.fact_check_count || (this.currentView.fact_checks || []).length;

        var heroArt = document.getElementById('hero-art-count');
        if (heroArt) heroArt.textContent = artCount.toLocaleString(this.getLocale());
        var heroSrc = document.getElementById('hero-src-count');
        if (heroSrc) heroSrc.textContent = srcCount.toLocaleString(this.getLocale());
        var heroFc = document.getElementById('hero-fc-count');
        if (heroFc) heroFc.textContent = fcCount.toLocaleString(this.getLocale());

        var fcBadge = document.getElementById('tab-badge-faktenchecks');
        if (fcBadge) fcBadge.textContent = fcCount;
        var quellenBadge = document.getElementById('tab-badge-quellen');
        if (quellenBadge) quellenBadge.textContent = srcCount;
    },

    /* ===== TAB: LAGEBILD ===== */
    renderSummary: function() {
        var v = this.currentView;
        document.getElementById('lagebild-timestamp').textContent = this.fmtDT(v.updated_at);
        var md = this.fixUmlauts(v.summary || '');
        md = this.stripZusammenfassung(md);
        var html = this.mdToHtml(md);

        // Build source lookup for citation links
        var srcMap = {};
        var sources = v.sources_json || [];
        for (var i = 0; i < sources.length; i++) {
            srcMap[String(sources[i].nr)] = sources[i];
        }
        var self = this;
        html = html.replace(/\[(\d+[a-z]?)\]/g, function(match, nr) {
            var src = srcMap[nr];
            if (src && src.url) {
                return '<a class="citation-ref" href="' + self.esc(src.url) + '" target="_blank" rel="noopener" title="' + self.esc(src.name || '') + '">[' + nr + ']</a>';
            }
            return '<a class="citation-ref" title="' + self.t('sourceRef') + ' ' + nr + '">[' + nr + ']</a>';
        });

        document.getElementById('summary-content').innerHTML = html;
    },

    renderInlineSources: function() {
        document.getElementById('inline-sources').innerHTML = '';
    },

    /* ===== TAB: QUELLEN (Tile Grid) ===== */
    renderSourcesTab: function() {
        var articles = this.currentView.articles || [];
        var container = document.getElementById('sources-grid-container');
        if (!container) return;

        // Aggregate by source
        var sourceMap = {};
        for (var i = 0; i < articles.length; i++) {
            var a = articles[i];
            var name = a.source || 'Unknown';
            if (!sourceMap[name]) sourceMap[name] = { count: 0, articles: [], languages: {}, domain: null };
            sourceMap[name].count++;
            sourceMap[name].articles.push(a);
            var lang = (a.language || '').toUpperCase();
            if (lang) sourceMap[name].languages[lang] = (sourceMap[name].languages[lang] || 0) + 1;
            if (!sourceMap[name].domain && a.source_url) sourceMap[name].domain = this.extractDomain(a.source_url);
        }

        // Sort by count desc
        var sources = [];
        for (var name in sourceMap) {
            sources.push({ name: name, data: sourceMap[name] });
        }
        sources.sort(function(a, b) { return b.data.count - a.data.count; });

        // Language totals
        var langTotals = {};
        for (var i = 0; i < articles.length; i++) {
            var lang = (articles[i].language || '').toUpperCase();
            if (lang) langTotals[lang] = (langTotals[lang] || 0) + 1;
        }

        var h = '';

        // Header
        h += '<div class="sources-overview-header">';
        h += '<span class="sources-overview-title">' + this.t('srcArticlesFrom').replace('{count}', articles.length).replace('{sources}', sources.length) + '</span>';
        h += '<div class="sources-lang-chips">';
        var langKeys = Object.keys(langTotals).sort(function(a, b) { return langTotals[b] - langTotals[a]; });
        for (var i = 0; i < langKeys.length; i++) {
            h += '<span class="sources-lang-chip">' + langKeys[i] + ' ' + langTotals[langKeys[i]] + '</span>';
        }
        h += '</div></div>';

        // Grid
        h += '<div class="sources-grid" id="sources-grid">';
        for (var i = 0; i < sources.length; i++) {
            var s = sources[i];
            var langBadge = Object.keys(s.data.languages).join('/');
            h += '<div class="source-tile" data-source-index="' + i + '">';
            h += '<div class="source-tile-top">';
            if (s.data.domain) {
                h += '<img class="source-tile-favicon" src="https://www.google.com/s2/favicons?domain=' + encodeURIComponent(s.data.domain) + '&sz=16" width="16" height="16" alt="" loading="lazy">';
            }
            h += '<span class="source-tile-name">' + this.esc(s.name) + '</span>';
            h += '</div>';
            h += '<div class="source-tile-bottom">';
            h += '<span class="source-tile-lang">' + langBadge + '</span>';
            h += '<span class="source-tile-count">' + s.data.count + '</span>';
            h += '</div>';
            h += '</div>';
        }
        h += '</div>';

        container.innerHTML = h;
        this._sourceTiles = sources;

        // Click handler
        var self = this;
        document.getElementById('sources-grid').addEventListener('click', function(e) {
            var tile = e.target.closest('.source-tile');
            if (!tile) return;
            self.toggleSourceDetail(tile);
        });
    },

    toggleSourceDetail: function(tile) {
        var grid = document.getElementById('sources-grid');
        var idx = parseInt(tile.getAttribute('data-source-index'));
        var existingPanel = grid.querySelector('.source-detail-panel');
        var wasActive = tile.classList.contains('active');

        // Remove active from all tiles
        var allTiles = grid.querySelectorAll('.source-tile');
        for (var k = 0; k < allTiles.length; k++) allTiles[k].classList.remove('active');

        // Remove existing panel
        if (existingPanel) existingPanel.remove();

        // If same tile was active, just close
        if (wasActive) return;

        tile.classList.add('active');

        // Find last tile in same visual row (by offsetTop)
        var clickedTop = tile.offsetTop;
        var lastInRow = tile;
        for (var k = 0; k < allTiles.length; k++) {
            if (allTiles[k].offsetTop === clickedTop) {
                lastInRow = allTiles[k];
            }
        }

        // Build detail panel
        var src = this._sourceTiles[idx];
        var arts = src.data.articles.slice().sort(function(a, b) {
            var da = new Date(Lagebild.toUTC(a.published_at || a.collected_at || ''));
            var db = new Date(Lagebild.toUTC(b.published_at || b.collected_at || ''));
            return db - da;
        });

        var h = '<div class="source-detail-panel">';
        h += '<div class="source-detail-header">';
        h += '<span class="source-detail-name">';
        if (src.data.domain) {
            h += '<img class="source-tile-favicon" src="https://www.google.com/s2/favicons?domain=' + encodeURIComponent(src.data.domain) + '&sz=16" width="16" height="16" alt="" loading="lazy"> ';
        }
        h += this.esc(src.name) + '</span>';
        h += '<span class="source-detail-count">' + src.data.count + ' ' + Lagebild.t('srcArticle') + '</span>';
        h += '<button class="source-detail-close" aria-label="' + Lagebild.t('srcClose') + '">&times;</button>';
        h += '</div>';
        h += '<div class="source-detail-articles">';
        for (var j = 0; j < arts.length; j++) {
            var a = arts[j];
            var dt = a.published_at || a.collected_at || '';
            var dObj = dt ? new Date(this.toUTC(dt)) : null;
            var hl = this.fixUmlauts(Lagebild.getHeadline(a));
            h += '<div class="source-detail-article">';
            if (a.source_url) {
                h += '<a href="' + this.esc(a.source_url) + '" target="_blank" rel="noopener" class="source-detail-article-title">' + this.esc(hl) + ' ' + this.icons.externalLink + '</a>';
            } else {
                h += '<span class="source-detail-article-title">' + this.esc(hl) + '</span>';
            }
            if (dObj && !isNaN(dObj.getTime())) {
                h += '<span class="source-detail-article-date">' + dObj.toLocaleDateString(Lagebild.getLocale(), { day: '2-digit', month: '2-digit', year: 'numeric', timeZone: TIMEZONE }) + '</span>';
            }
            h += '</div>';
        }
        h += '</div></div>';

        // Insert after last tile in row
        lastInRow.insertAdjacentHTML('afterend', h);

        // Close button handler
        var panel = grid.querySelector('.source-detail-panel');
        var self = this;
        panel.querySelector('.source-detail-close').addEventListener('click', function(e) {
            e.stopPropagation();
            panel.remove();
            tile.classList.remove('active');
        });

        // Scroll into view
        setTimeout(function() {
            panel.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
        }, 50);
    },

    renderArticlesTab: function() {},

    /* ===== TAB: KARTE (Clustered Pulse Markers) ===== */
    renderMap: function() {
        if (this.map) { this.map.remove(); this.map = null; }
        this.map = L.map('map-container', {
            minZoom: 2,
            maxBounds: [[-85, -180], [85, 180]],
            maxBoundsViscosity: 1.0
        }).setView([33.0, 48.0], 5);

        L.tileLayer('https://tile.openstreetmap.de/{z}/{x}/{y}.png', {
            attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>',
            maxZoom: 19,
            noWrap: true,
        }).addTo(this.map);

        function pulseIcon(color) {
            return L.divIcon({
                className: '',
                html: '<div class="pulse-marker-wrapper">'
                    + '<div class="pulse-marker-ring" style="border-color:' + color + '"></div>'
                    + '<div class="pulse-marker-ring" style="border-color:' + color + '"></div>'
                    + '<div class="pulse-marker-dot" style="background:' + color + ';box-shadow:0 0 10px ' + color + '"></div>'
                    + '</div>',
                iconSize: [20, 20],
                iconAnchor: [10, 10],
                popupAnchor: [0, -12]
            });
        }

        var categoryColors = {
            primary: '#ef4444',
            secondary: '#f59e0b',
            tertiary: '#3b82f6',
            mentioned: '#7b7b7b'
        };
        var defaultCategoryLabels = {
            primary: 'Hauptgeschehen',
            secondary: 'Reaktionen',
            tertiary: 'Beteiligte',
            mentioned: 'Erwaehnt'
        };
        var categoryLabels = {};
        if (this.data && this.data.category_labels) {
            var apiLabels = this.data.category_labels;
            categoryLabels.primary = apiLabels.primary || defaultCategoryLabels.primary;
            categoryLabels.secondary = apiLabels.secondary || defaultCategoryLabels.secondary;
            categoryLabels.tertiary = apiLabels.tertiary || defaultCategoryLabels.tertiary;
            categoryLabels.mentioned = apiLabels.mentioned || defaultCategoryLabels.mentioned;
        } else {
            categoryLabels = defaultCategoryLabels;
        }

        var locs = (this.data && this.data.locations) ? this.data.locations : [];

        if (locs.length === 0) {
            var emptyDiv = document.createElement('div');
            emptyDiv.style.cssText = 'position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);z-index:1000;background:#151D2E;padding:20px 30px;border-radius:8px;border:1px solid #1E2D45;color:#8896AB;text-align:center;';
            emptyDiv.innerHTML = Lagebild.t('mapNoData');
            document.getElementById('map-container').appendChild(emptyDiv);
        }

        var clusterGroup = L.markerClusterGroup({
            maxClusterRadius: 50,
            spiderfyOnMaxZoom: true,
            showCoverageOnHover: false,
            zoomToBoundsOnClick: true,
            disableClusteringAtZoom: 10
        });

        var usedCategories = {};
        var bounds = [];
        for (var i = 0; i < locs.length; i++) {
            var l = locs[i];
            if (!l.lat || !l.lon) continue;
            var cat = l.category || 'mentioned';
            var color = categoryColors[cat] || '#7b7b7b';
            usedCategories[cat] = true;

            // Popup mit Artikel-Links
            var popupText = '<strong style="color:#E8ECF4;">' + (l.name || '') + '</strong>';
            if (l.country_code) popupText += ' <span style="color:#8896AB;font-size:0.8rem;">(' + l.country_code + ')</span>';
            popupText += '<br><span style="font-size:0.85rem;color:#8896AB;">' + (l.article_count || 0) + ' ' + Lagebild.t('mapArticles') + '</span>';
            if (l.top_articles && l.top_articles.length > 0) {
                popupText += '<div style="margin-top:6px;border-top:1px solid #1E2D45;padding-top:6px;">';
                for (var j = 0; j < l.top_articles.length; j++) {
                    var a = l.top_articles[j];
                    var hl = (a.headline || '').replace(/\*\*/g, '');
                    if (hl.length > 60) hl = hl.substring(0, 60) + '\u2026';
                    if (a.url) {
                        popupText += '<a href="' + a.url + '" target="_blank" rel="noopener" style="color:#C8A851;font-size:0.8rem;display:block;margin-top:3px;text-decoration:none;">' + hl + '</a>';
                    } else {
                        popupText += '<span style="color:#8896AB;font-size:0.8rem;display:block;margin-top:3px;">' + hl + '</span>';
                    }
                    popupText += '<span style="color:#556B7A;font-size:0.7rem;">' + (a.source || '') + '</span>';
                }
                popupText += '</div>';
            }

            var marker;
            if (cat === 'primary' || cat === 'secondary') {
                marker = L.marker([l.lat, l.lon], { icon: pulseIcon(color) });
            } else {
                marker = L.circleMarker([l.lat, l.lon], {
                    radius: 5, fillColor: color, fillOpacity: 0.7,
                    color: color, weight: 1, opacity: 0.9
                });
            }
            marker.bindPopup(popupText, { maxWidth: 300 });
            clusterGroup.addLayer(marker);
            bounds.push([l.lat, l.lon]);
        }

        this.map.addLayer(clusterGroup);

        // Dark legend
        var legend = L.control({ position: 'bottomright' });
        legend.onAdd = function() {
            var div = L.DomUtil.create('div', 'map-legend');
            div.style.cssText = 'background:#151D2E;padding:10px 14px;border-radius:4px;border:1px solid #1E2D45;box-shadow:0 2px 8px rgba(0,0,0,0.3);font-size:0.8rem;line-height:1.8;color:#E8ECF4;';
            var html = '<strong style="color:#C8A851;">' + Lagebild.t('mapLegend') + '</strong><br>';
            ['primary', 'secondary', 'tertiary', 'mentioned'].forEach(function(cat) {
                if (usedCategories[cat] && categoryLabels[cat]) {
                    html += '<span style="color:' + categoryColors[cat] + ';">&#9679;</span> ' + categoryLabels[cat] + '<br>';
                }
            });
            div.innerHTML = html;
            return div;
        };
        legend.addTo(this.map);

        if (bounds.length > 0) {
            this.map.fitBounds(bounds, { padding: [30, 30], maxZoom: 7 });
        }

        // Dark popup styling
        if (!document.getElementById('leaflet-dark-style')) {
            var style = document.createElement('style');
            style.id = 'leaflet-dark-style';
            style.textContent = '.lagebild-page .leaflet-popup-content-wrapper{background:#151D2E;color:#E8ECF4;border:1px solid #1E2D45;border-radius:4px;box-shadow:0 4px 16px rgba(0,0,0,0.4);}.lagebild-page .leaflet-popup-tip{background:#151D2E;}';
            document.head.appendChild(style);
        }

        setTimeout(function() { if (Lagebild.map) Lagebild.map.invalidateSize(); }, 300);
    },

    /* ===== TAB: FAKTENCHECKS ===== */
    /* ===== Factcheck Icons (from real Monitor) ===== */
    fcIcons: {
        confirmed: '&#10003;',
        unconfirmed: '?',
        contradicted: '&#10007;',
        developing: '&#8635;',
        established: '&#10003;',
        disputed: '&#9888;',
        'false': '&#10007;',
        unverified: '?'
    },

    fcLabels: {},

    renderFactChecksTab: function() {
        var checks = this.currentView.fact_checks || [];
        if (!checks.length) {
            document.getElementById('factchecks-content').innerHTML = '<p style="color:#8896AB">' + this.t('fcNone') + '</p>';
            return;
        }

        // Count stats
        var stats = { confirmed: 0, unconfirmed: 0, contradicted: 0, developing: 0, established: 0, disputed: 0 };
        for (var k = 0; k < checks.length; k++) {
            var st = checks[k].status || 'developing';
            if (stats[st] !== undefined) stats[st]++;
        }

        var confirmedTotal = stats.confirmed + stats.established;
        var openTotal = stats.unconfirmed + stats.developing;
        var contradictedTotal = stats.contradicted + stats.disputed;

        // Stat cards (clickable filters)
        var h = '<div class="fc-stats">';
        h += '<button class="fc-stat active" data-filter="all"><span class="fc-stat-num">' + checks.length + '</span><span class="fc-stat-label">' + this.t('fcTotal') + '</span></button>';
        h += '<button class="fc-stat confirmed" data-filter="confirmed"><span class="fc-stat-num">' + confirmedTotal + '</span><span class="fc-stat-label">' + this.t('fcConfirmed') + '</span></button>';
        h += '<button class="fc-stat unconfirmed" data-filter="unconfirmed"><span class="fc-stat-num">' + openTotal + '</span><span class="fc-stat-label">' + this.t('fcOpen') + '</span></button>';
        if (contradictedTotal > 0)
            h += '<button class="fc-stat contradicted" data-filter="contradicted"><span class="fc-stat-num">' + contradictedTotal + '</span><span class="fc-stat-label">' + this.t('fcContradicted') + '</span></button>';
        h += '</div>';

        // Hinweis bei unvollständiger Liste
        var storedFcCount = this.currentView.fact_check_count || 0;
        if (storedFcCount > 0 && checks.length < storedFcCount) {
            h += '<div style="padding:8px 12px;margin:8px 0;background:rgba(200,168,81,0.1);border:1px solid rgba(200,168,81,0.3);border-radius:6px;color:#C8A851;font-size:0.85rem;">';
            h += this.t('fcCleaned').replace('{count}', checks.length).replace('{total}', storedFcCount);
            h += '</div>';
        }

        // Sort: status_history first, then by sources_count
        checks = checks.slice().sort(function(a, b) {
            var aH = (a.status_history || []).length;
            var bH = (b.status_history || []).length;
            if (bH !== aH) return bH - aH;
            return (b.sources_count || 0) - (a.sources_count || 0);
        });

        // Compact accordion list
        h += '<div class="fc-list" id="fc-list">';
        for (var i = 0; i < checks.length; i++) {
            var fc = checks[i];
            var status = fc.status || 'developing';
            var filterGroup = 'all';
            if (status === 'confirmed' || status === 'established') filterGroup = 'confirmed';
            else if (status === 'unconfirmed' || status === 'developing') filterGroup = 'unconfirmed';
            else if (status === 'contradicted' || status === 'disputed') filterGroup = 'contradicted';

            var hasProg = fc.status_history && fc.status_history.length > 1;
            var icon = this.fcIcons[status] || '?';
            var label = this.stLabel(status);

            h += '<div class="fc-row" data-status-group="' + filterGroup + '">';
            h += '<div class="fc-row-header" data-fc-index="' + i + '">';
            h += '<span class="fc-icon ' + status + '" title="' + this.esc(label) + '">' + icon + '</span>';
            h += '<span class="fc-row-claim">' + this.esc(this.fixUmlauts(fc.claim || '')) + '</span>';
            h += '<span class="fc-row-sources">' + (fc.sources_count || 0) + '</span>';
            if (hasProg) h += '<span class="fc-row-history-dot" title="Statusverlauf vorhanden"></span>';
            h += '<span class="fc-row-chevron">&#9656;</span>';
            h += '</div>';

            // Expandable detail
            h += '<div class="fc-row-detail">';
            h += '<div class="fc-row-detail-inner">';
            h += '<div class="fc-detail-status"><span class="fc-icon ' + status + '">' + icon + '</span> <strong>' + label + '</strong> – ' + (fc.sources_count || 0) + ' ' + this.t('fcSources') + '</div>';

            if (fc.evidence) {
                var ev = this.fixUmlauts(fc.evidence);
                ev = this.esc(ev).replace(/(https?:\/\/[^\s,)]+)/g, '<a href="$1" target="_blank" rel="noopener">$1</a>');
                h += '<div class="fc-detail-evidence"><strong>' + this.t('fcEvidence') + '</strong> ' + ev + '</div>';
            }

            if (hasProg) {
                h += '<div class="fc-detail-progression">';
                h += '<span class="fc-detail-prog-label">' + this.t('fcProgression') + '</span>';
                for (var j = 0; j < fc.status_history.length; j++) {
                    var step = fc.status_history[j];
                    if (j > 0) h += '<span class="progression-arrow">&rarr;</span>';
                    h += '<span class="progression-step">';
                    h += '<span class="fc-icon small ' + step.status + '">' + (this.fcIcons[step.status] || '?') + '</span>';
                    if (step.at) h += '<span class="progression-time">' + this.fmtShort(step.at) + '</span>';
                    h += '</span>';
                }
                h += '</div>';
            }
            h += '</div></div>';
            h += '</div>';
        }
        h += '</div>';

        document.getElementById('factchecks-content').innerHTML = h;

        // Filter click handler
        var statBtns = document.querySelectorAll('.fc-stat');
        statBtns.forEach(function(btn) {
            btn.addEventListener('click', function() {
                for (var k = 0; k < statBtns.length; k++) statBtns[k].classList.remove('active');
                btn.classList.add('active');
                var filter = btn.getAttribute('data-filter');
                var rows = document.querySelectorAll('.fc-row');
                for (var k = 0; k < rows.length; k++) {
                    if (filter === 'all' || rows[k].getAttribute('data-status-group') === filter) {
                        rows[k].style.display = '';
                    } else {
                        rows[k].style.display = 'none';
                    }
                }
            });
        });

        // Accordion click handler
        document.getElementById('fc-list').addEventListener('click', function(e) {
            var header = e.target.closest('.fc-row-header');
            if (!header) return;
            var row = header.closest('.fc-row');
            var wasOpen = row.classList.contains('open');

            // Close all open rows
            var allRows = document.querySelectorAll('.fc-row.open');
            for (var k = 0; k < allRows.length; k++) allRows[k].classList.remove('open');

            // Toggle clicked row
            if (!wasOpen) {
                row.classList.add('open');
                var detail = row.querySelector('.fc-row-detail');
                if (detail) {
                    setTimeout(function() {
                        detail.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
                    }, 50);
                }
            }
        });
    },

    /* ===== TABS ===== */
    initTabs: function() {
        var btns = document.querySelectorAll('.tab-btn');
        var self = this;
        for (var i = 0; i < btns.length; i++) {
            btns[i].addEventListener('click', function() {
                var tab = this.getAttribute('data-tab');
                for (var j = 0; j < btns.length; j++) btns[j].classList.remove('active');
                this.classList.add('active');
                var panels = document.querySelectorAll('.tab-panel');
                for (var j = 0; j < panels.length; j++) panels[j].classList.remove('active');
                var activePanel = document.getElementById('panel-' + tab);
                activePanel.classList.add('active');

                // Trigger reveal for cards in newly active panel
                var revealCards = activePanel.querySelectorAll('.reveal:not(.revealed)');
                for (var k = 0; k < revealCards.length; k++) {
                    revealCards[k].classList.add('revealed');
                }

                if (tab === 'karte') self.renderMap();
            });
        }
    },

    /* ===== FLOATING CTA ===== */
    initFloatingCta: function() {
        var cta = document.createElement('div');
        cta.className = 'floating-cta';
        cta.innerHTML = '<span class="floating-cta-text">' + this.t('ctaText') + '</span>'
            + '<a href="mailto:info@aegis-sight.de" class="floating-cta-btn">' + this.t('ctaButton') + '</a>'
            + '<button class="floating-cta-close" aria-label="' + this.t('srcClose') + '">&times;</button>';
        document.body.appendChild(cta);

        // Show after scrolling past hero
        var shown = false;
        window.addEventListener('scroll', function() {
            if (shown) return;
            if (window.scrollY > 400) {
                cta.classList.add('visible');
                shown = true;
            }
        });

        // Close button
        cta.querySelector('.floating-cta-close').addEventListener('click', function(e) {
            e.preventDefault();
            cta.classList.add('dismissed');
            setTimeout(function() { cta.classList.remove('dismissed'); }, 60000);
        });
    },

    /* ===== SCROLL REVEAL ===== */
    initScrollReveal: function() {
        var cards = document.querySelectorAll('.content-card, .lagebild-cta');
        if (!('IntersectionObserver' in window)) {
            for (var i = 0; i < cards.length; i++) cards[i].classList.add('revealed');
            return;
        }
        var observer = new IntersectionObserver(function(entries) {
            entries.forEach(function(entry) {
                if (entry.isIntersecting) {
                    entry.target.classList.add('revealed');
                    observer.unobserve(entry.target);
                }
            });
        }, { threshold: 0.1 });

        for (var i = 0; i < cards.length; i++) {
            cards[i].classList.add('reveal');
            // Immediately reveal cards in the active (visible) tab panel
            var panel = cards[i].closest('.tab-panel');
            if (!panel || panel.classList.contains('active')) {
                cards[i].classList.add('revealed');
            } else {
                observer.observe(cards[i]);
            }
        }
    },

    /* ===== FAKTENCHECK-FILTER NACH ZEITRAUM ===== */
    getFactChecksAtTime: function(cutoff) {
        var allFCs = this.data.fact_checks || [];
        if (!cutoff) return allFCs;
        var cutoffTime = new Date(this.toUTC(cutoff)).getTime();
        var filtered = [];
        for (var i = 0; i < allFCs.length; i++) {
            var fc = allFCs[i];
            var hist = fc.status_history || [];
            if (!hist.length) continue;
            // Erster Eintrag = Erstellungszeitpunkt des Faktenchecks
            var firstAt = new Date(this.toUTC(hist[0].at)).getTime();
            if (firstAt > cutoffTime) continue;
            // Status zum gewaehlten Zeitpunkt ermitteln
            var statusAtTime = hist[0].status;
            for (var j = 0; j < hist.length; j++) {
                var stepTime = new Date(this.toUTC(hist[j].at)).getTime();
                if (stepTime <= cutoffTime) {
                    statusAtTime = hist[j].status;
                }
            }
            // Kopie mit angepasstem Status und getrimmter History
            var copy = {};
            for (var key in fc) { if (fc.hasOwnProperty(key)) copy[key] = fc[key]; }
            copy.status = statusAtTime;
            copy.status_history = [];
            for (var j = 0; j < hist.length; j++) {
                if (new Date(this.toUTC(hist[j].at)).getTime() <= cutoffTime) {
                    copy.status_history.push(hist[j]);
                }
            }
            filtered.push(copy);
        }
        return filtered;
    },

    /* ===== ARTIKEL-FILTER NACH ZEITRAUM ===== */
    filterArticlesAtTime: function(cutoff) {
        var all = this.data.articles || [];
        if (!cutoff) return all;
        var filtered = [];
        for (var i = 0; i < all.length; i++) {
            if ((all[i].collected_at || '') <= cutoff) {
                filtered.push(all[i]);
            }
        }
        return filtered;
    },

    /* ===== HILFSFUNKTIONEN ===== */
    extractDomain: function(url) {
        if (!url) return null;
        try { return new URL(url).hostname; } catch(e) { return null; }
    },

    fixUmlauts: function(text) {
        return text || "";
    },

    stLabel: function(s) {
        var key = 'st' + s.charAt(0).toUpperCase() + s.slice(1);
        return this.t(key) || s;
    },

    mdToHtml: function(md) {
        if (!md) return '';
        var lines = md.split('\n'), html = '', inList = false;
        for (var i = 0; i < lines.length; i++) {
            var l = lines[i];
            if (/^### (.+)$/.test(l)) { if (inList) { html += '</ul>'; inList = false; } html += '<h3>' + l.replace(/^### /, '') + '</h3>'; continue; }
            if (/^## (.+)$/.test(l)) { if (inList) { html += '</ul>'; inList = false; } html += '<h2>' + l.replace(/^## /, '') + '</h2>'; continue; }
            if (/^[-*] (.+)$/.test(l)) { if (!inList) { html += '<ul>'; inList = true; } html += '<li>' + l.replace(/^[-*] /, '') + '</li>'; continue; }
            if (inList) { html += '</ul>'; inList = false; }
            if (l.trim() === '') continue;
            html += '<p>' + l + '</p>';
        }
        if (inList) html += '</ul>';
        html = html.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>');
        html = html.replace(/\*(.+?)\*/g, '<em>$1</em>');
        return html;
    },

    esc: function(s) { if (!s) return ''; var d = document.createElement('div'); d.textContent = s; return d.innerHTML; },

    toUTC: function(s) {
        if (!s) return s;
        s = String(s).trim();
        if (/[Zz]$/.test(s) || /[+-]\d{2}:?\d{2}$/.test(s)) return s;
        // Naive Timestamps aus der DB sind Europe/Berlin Lokalzeit.
        // Korrekten UTC-Offset ermitteln (CET +01:00 / CEST +02:00).
        var iso = s.replace(' ', 'T');
        var temp = new Date(iso + 'Z');
        var utc = new Date(temp.toLocaleString('en-US', { timeZone: 'UTC' }));
        var local = new Date(temp.toLocaleString('en-US', { timeZone: TIMEZONE }));
        var offMin = (local - utc) / 60000;
        var sign = offMin >= 0 ? '+' : '-';
        var h = String(Math.floor(Math.abs(offMin) / 60)).padStart(2, '0');
        var m = String(Math.abs(offMin) % 60).padStart(2, '0');
        return iso + sign + h + ':' + m;
    },

    fmtDT: function(iso) {
        if (!iso) return '';
        try {
            var d = new Date(this.toUTC(iso));
            if (isNaN(d.getTime())) return iso;
            var opts = { timeZone: TIMEZONE, weekday: 'long', day: 'numeric', month: 'long', year: 'numeric', hour: '2-digit', minute: '2-digit', hour12: false };
            var locale = Lagebild.getLocale();
            var parts = new Intl.DateTimeFormat(locale, opts).formatToParts(d);
            var p = {};
            parts.forEach(function(x) { p[x.type] = x.value; });
            if (locale === 'en-GB') return p.weekday + ', ' + p.day + ' ' + p.month + ' ' + p.year + ', ' + p.hour + ':' + p.minute;
            return p.weekday + ', ' + p.day + '. ' + p.month + ' ' + p.year + ' um ' + p.hour + ':' + p.minute + ' Uhr';
        } catch(e) { return iso; }
    },

    fmtDateOnly: function(iso) {
        if (!iso) return '';
        try {
            var d = new Date(this.toUTC(iso));
            if (isNaN(d.getTime())) return iso;
            return d.toLocaleDateString(Lagebild.getLocale(), { day: 'numeric', month: 'short', year: 'numeric', timeZone: TIMEZONE });
        } catch(e) { return iso; }
    },

    fmtTimeOnly: function(iso) {
        if (!iso) return '';
        try {
            var d = new Date(this.toUTC(iso));
            if (isNaN(d.getTime())) return iso;
            return d.toLocaleTimeString(Lagebild.getLocale(), { hour: '2-digit', minute: '2-digit', timeZone: TIMEZONE });
        } catch(e) { return iso; }
    },

    fmtShort: function(iso) {
        if (!iso) return '';
        try { return new Date(this.toUTC(iso)).toLocaleDateString(Lagebild.getLocale(), { day: 'numeric', month: 'short', hour: '2-digit', minute: '2-digit', timeZone: TIMEZONE }); }
        catch(e) { return iso; }
    },

    showError: function() {
        document.getElementById('summary-content').innerHTML =
            '<div class="lagebild-error"><p>' + this.t('errorLoad') + '</p></div>';
    }
};

document.addEventListener('DOMContentLoaded', function() { Lagebild.init(); });
