/* ===================================================
   JAESWIFT.XYZ — Main JavaScript
   Animations, particles, typing, scroll effects
   =================================================== */

(function () {
    'use strict';

    // ─── CONFIG ───
    const CONFIG = {
        typingStrings: [
            'Developer // Tinkerer // Builder',
            'Self-hosted everything.',
            'Linux enthusiast since day one.',
            'Building the future, one commit at a time.',
            'root@jaeswift:~# echo "Hello, World"',
            'Cybersecurity & Infrastructure.',
            'AI Agent Operator.',
        ],
        typingSpeed: 60,
        typingDeleteSpeed: 30,
        typingPause: 2500,
        particleCount: 80,
        particleMaxSpeed: 0.3,
        particleConnectionDist: 120,
        startTime: Date.now(),
    };

    // ─── UTILITIES ───
    const $ = (sel, ctx = document) => ctx.querySelector(sel);
    const $$ = (sel, ctx = document) => [...ctx.querySelectorAll(sel)];

    // ─── NAVBAR ───
    function initNavbar() {
        const navbar = $('#navbar');
        const toggle = $('#navToggle');
        const menu = $('#navMenu');
        const navItems = $$('.nav-item');

        // Scroll effect
        let lastScroll = 0;
        window.addEventListener('scroll', () => {
            const scrollY = window.scrollY;
            navbar.classList.toggle('scrolled', scrollY > 50);
            lastScroll = scrollY;
        }, { passive: true });

        // Mobile toggle
        toggle.addEventListener('click', () => {
            menu.classList.toggle('active');
            toggle.classList.toggle('active');
        });

        // Mobile dropdown toggle
        if (window.innerWidth <= 768) {
            navItems.forEach(item => {
                const link = item.querySelector('.nav-link');
                link.addEventListener('click', (e) => {
                    if (item.querySelector('.dropdown')) {
                        e.preventDefault();
                        item.classList.toggle('active');
                    }
                });
            });
        }

        // Active link on scroll
        const sections = $$('section[id]');
        window.addEventListener('scroll', () => {
            const scrollPos = window.scrollY + 100;
            sections.forEach(section => {
                const top = section.offsetTop;
                const height = section.offsetHeight;
                const id = section.getAttribute('id');
                if (scrollPos >= top && scrollPos < top + height) {
                    $$('.nav-link').forEach(l => l.classList.remove('active'));
                    const activeLink = $(`.nav-link[href="#${id}"]`);
                    if (activeLink) activeLink.classList.add('active');
                }
            });
        }, { passive: true });

        // Smooth scroll for nav links
        $$('.nav-link, .dropdown a[href^="#"]').forEach(link => {
            link.addEventListener('click', (e) => {
                const href = link.getAttribute('href');
                if (href && href.startsWith('#')) {
                    e.preventDefault();
                    const target = $(href);
                    if (target) {
                        target.scrollIntoView({ behavior: 'smooth', block: 'start' });
                        menu.classList.remove('active');
                        toggle.classList.remove('active');
                    }
                }
            });
        });
    }

    // ─── LIVE CLOCK ───
    function initClock() {
        const navTime = $('#navTime');
        if (!navTime) return;

        function update() {
            const now = new Date();
            const h = String(now.getHours()).padStart(2, '0');
            const m = String(now.getMinutes()).padStart(2, '0');
            const s = String(now.getSeconds()).padStart(2, '0');
            navTime.textContent = `${h}:${m}:${s}`;
        }

        update();
        setInterval(update, 1000);
    }

    // ─── UPTIME COUNTER ───
    function initUptime() {
        const uptimeEl = $('#uptime');
        if (!uptimeEl) return;

        function update() {
            const elapsed = Date.now() - CONFIG.startTime;
            const totalSec = Math.floor(elapsed / 1000);
            const days = Math.floor(totalSec / 86400);
            const hours = Math.floor((totalSec % 86400) / 3600);
            const mins = Math.floor((totalSec % 3600) / 60);
            uptimeEl.textContent = `${days}d ${String(hours).padStart(2, '0')}h ${String(mins).padStart(2, '0')}m`;
        }

        update();
        setInterval(update, 60000);
    }

    // ─── TYPING EFFECT ───
    function initTyping() {
        const el = $('#typingText');
        if (!el) return;

        let stringIndex = 0;
        let charIndex = 0;
        let isDeleting = false;

        function type() {
            const current = CONFIG.typingStrings[stringIndex];

            if (isDeleting) {
                el.textContent = current.substring(0, charIndex - 1);
                charIndex--;
            } else {
                el.textContent = current.substring(0, charIndex + 1);
                charIndex++;
            }

            let delay = isDeleting ? CONFIG.typingDeleteSpeed : CONFIG.typingSpeed;

            if (!isDeleting && charIndex === current.length) {
                delay = CONFIG.typingPause;
                isDeleting = true;
            } else if (isDeleting && charIndex === 0) {
                isDeleting = false;
                stringIndex = (stringIndex + 1) % CONFIG.typingStrings.length;
                delay = 400;
            }

            setTimeout(type, delay);
        }

        setTimeout(type, 1200);
    }

    // ─── SCROLL REVEAL ───
    function initScrollReveal() {
        const elements = $$('[data-animate]');
        if (!elements.length) return;

        const observer = new IntersectionObserver((entries) => {
            entries.forEach((entry, index) => {
                if (entry.isIntersecting) {
                    setTimeout(() => {
                        entry.target.classList.add('visible');
                    }, index * 100);
                    observer.unobserve(entry.target);
                }
            });
        }, {
            threshold: 0.1,
            rootMargin: '0px 0px -50px 0px'
        });

        elements.forEach(el => observer.observe(el));
    }

    // ─── SKILL BARS ANIMATION ───
    function initSkillBars() {
        const bars = $$('.bar-fill');
        if (!bars.length) return;

        const observer = new IntersectionObserver((entries) => {
            entries.forEach(entry => {
                if (entry.isIntersecting) {
                    const bar = entry.target;
                    const width = bar.getAttribute('data-width');
                    bar.style.setProperty('--target-width', width + '%');
                    bar.classList.add('animated');
                    observer.unobserve(bar);
                }
            });
        }, { threshold: 0.5 });

        bars.forEach(bar => observer.observe(bar));
    }

    // ─── PARTICLE SYSTEM ───
    function initParticles() {
        const canvas = $('#particles');
        if (!canvas) return;

        const ctx = canvas.getContext('2d');
        let particles = [];
        let animationId;
        let mouseX = -1000;
        let mouseY = -1000;

        function resize() {
            canvas.width = window.innerWidth;
            canvas.height = window.innerHeight;
        }

        function createParticle() {
            return {
                x: Math.random() * canvas.width,
                y: Math.random() * canvas.height,
                vx: (Math.random() - 0.5) * CONFIG.particleMaxSpeed,
                vy: (Math.random() - 0.5) * CONFIG.particleMaxSpeed,
                size: Math.random() * 1.5 + 0.5,
                opacity: Math.random() * 0.5 + 0.1,
            };
        }

        function initParticleArray() {
            particles = [];
            for (let i = 0; i < CONFIG.particleCount; i++) {
                particles.push(createParticle());
            }
        }

        function drawParticle(p) {
            ctx.beginPath();
            ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
            ctx.fillStyle = `rgba(51, 51, 51, ${p.opacity})`;
            ctx.fill();
        }

        function drawConnections() {
            for (let i = 0; i < particles.length; i++) {
                for (let j = i + 1; j < particles.length; j++) {
                    const dx = particles[i].x - particles[j].x;
                    const dy = particles[i].y - particles[j].y;
                    const dist = Math.sqrt(dx * dx + dy * dy);

                    if (dist < CONFIG.particleConnectionDist) {
                        const opacity = (1 - dist / CONFIG.particleConnectionDist) * 0.15;
                        ctx.beginPath();
                        ctx.moveTo(particles[i].x, particles[i].y);
                        ctx.lineTo(particles[j].x, particles[j].y);
                        ctx.strokeStyle = `rgba(51, 51, 51, ${opacity})`;
                        ctx.lineWidth = 0.5;
                        ctx.stroke();
                    }
                }
            }

            // Mouse connections
            particles.forEach(p => {
                const dx = p.x - mouseX;
                const dy = p.y - mouseY;
                const dist = Math.sqrt(dx * dx + dy * dy);
                if (dist < 200) {
                    const opacity = (1 - dist / 200) * 0.3;
                    ctx.beginPath();
                    ctx.moveTo(p.x, p.y);
                    ctx.lineTo(mouseX, mouseY);
                    ctx.strokeStyle = `rgba(51, 51, 51, ${opacity})`;
                    ctx.lineWidth = 0.8;
                    ctx.stroke();
                }
            });
        }

        function update() {
            particles.forEach(p => {
                p.x += p.vx;
                p.y += p.vy;

                // Wrap around
                if (p.x < 0) p.x = canvas.width;
                if (p.x > canvas.width) p.x = 0;
                if (p.y < 0) p.y = canvas.height;
                if (p.y > canvas.height) p.y = 0;

                // Mouse repulsion
                const dx = p.x - mouseX;
                const dy = p.y - mouseY;
                const dist = Math.sqrt(dx * dx + dy * dy);
                if (dist < 100) {
                    const force = (100 - dist) / 100 * 0.02;
                    p.vx += (dx / dist) * force;
                    p.vy += (dy / dist) * force;
                }

                // Speed limit
                const speed = Math.sqrt(p.vx * p.vx + p.vy * p.vy);
                if (speed > CONFIG.particleMaxSpeed * 2) {
                    p.vx *= 0.98;
                    p.vy *= 0.98;
                }
            });
        }

        function animate() {
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            update();
            drawConnections();
            particles.forEach(drawParticle);
            animationId = requestAnimationFrame(animate);
        }

        // Event listeners
        window.addEventListener('resize', () => {
            resize();
            initParticleArray();
        });

        window.addEventListener('mousemove', (e) => {
            mouseX = e.clientX;
            mouseY = e.clientY;
        }, { passive: true });

        window.addEventListener('mouseleave', () => {
            mouseX = -1000;
            mouseY = -1000;
        });

        // Visibility API - pause when tab hidden
        document.addEventListener('visibilitychange', () => {
            if (document.hidden) {
                cancelAnimationFrame(animationId);
            } else {
                animate();
            }
        });

        resize();
        initParticleArray();
        animate();
    }

    // ─── GLITCH EFFECT (random intensification) ───
    function initGlitchEffect() {
        const glitch = $('.glitch');
        if (!glitch) return;

        setInterval(() => {
            glitch.classList.add('glitch-active');
            setTimeout(() => glitch.classList.remove('glitch-active'), 200);
        }, 5000 + Math.random() * 5000);
    }

    // ─── TERMINAL ANIMATION ───
    function initTerminal() {
        const terminal = $('#terminal');
        if (!terminal) return;

        const commands = [
            { prompt: 'uptime', output: ' up 47 days, 3:22, 1 user, load: 0.12' },
            { prompt: 'df -h / | tail -1', output: '/dev/sda1  50G  12G  36G  25% /' },
            { prompt: 'curl -s ifconfig.me', output: '███.███.███.███' },
            { prompt: 'docker ps --format "table {{.Names}}"', output: 'gitea\nnginx-proxy\nagent-zero\nmonitoring' },
            { prompt: 'echo $SHELL', output: '/bin/zsh' },
            { prompt: 'neofetch --off | head -3', output: 'OS: Debian GNU/Linux 12\nKernel: 6.1.0-18-amd64\nUptime: 47 days, 3 hours' },
        ];

        let cmdIndex = 0;

        function addTermLine(content, isOutput = false) {
            const line = document.createElement('div');
            line.className = 'term-line' + (isOutput ? ' term-output' : '');
            if (isOutput) {
                line.textContent = content;
            } else {
                line.innerHTML = `<span class="term-prompt">jae@swift:~$</span> <span class="term-cmd">${content}</span>`;
            }
            // Insert before the last line (cursor line)
            const cursorLine = terminal.lastElementChild;
            terminal.insertBefore(line, cursorLine);
        }

        function typeCommand() {
            if (cmdIndex >= commands.length) cmdIndex = 0;
            const cmd = commands[cmdIndex];
            let charIdx = 0;

            // Remove old cursor line, add new command line being typed
            const cursorLine = terminal.lastElementChild;

            const typingLine = document.createElement('div');
            typingLine.className = 'term-line';
            typingLine.innerHTML = `<span class="term-prompt">jae@swift:~$</span> <span class="term-cmd"></span><span class="term-cursor">█</span>`;
            terminal.insertBefore(typingLine, cursorLine);
            cursorLine.remove();

            const cmdSpan = typingLine.querySelector('.term-cmd');

            function typeChar() {
                if (charIdx < cmd.prompt.length) {
                    cmdSpan.textContent += cmd.prompt[charIdx];
                    charIdx++;
                    setTimeout(typeChar, 50 + Math.random() * 80);
                } else {
                    // Remove cursor from typing line
                    const cursor = typingLine.querySelector('.term-cursor');
                    if (cursor) cursor.remove();

                    // Show output
                    setTimeout(() => {
                        const outputLines = cmd.output.split('\n');
                        outputLines.forEach(line => {
                            const outputEl = document.createElement('div');
                            outputEl.className = 'term-line term-output';
                            outputEl.textContent = line;
                            terminal.appendChild(outputEl);
                        });

                        // Add new cursor line
                        const newCursor = document.createElement('div');
                        newCursor.className = 'term-line';
                        newCursor.innerHTML = `<span class="term-prompt">jae@swift:~$</span> <span class="term-cursor">█</span>`;
                        terminal.appendChild(newCursor);

                        // Scroll terminal
                        terminal.scrollTop = terminal.scrollHeight;

                        // Keep only last ~15 lines
                        while (terminal.children.length > 15) {
                            terminal.removeChild(terminal.firstChild);
                        }

                        cmdIndex++;
                    }, 300);
                }
            }

            typeChar();
        }

        // Run terminal animation every 6-10 seconds
        setInterval(typeCommand, 8000 + Math.random() * 4000);
    }

    // ─── CONTACT FORM ───
    function initContactForm() {
        const form = $('#contactForm');
        if (!form) return;

        form.addEventListener('submit', async (e) => {
            e.preventDefault();
            const btn = form.querySelector('.form-submit');
            const submitText = btn.querySelector('.submit-text');
            const originalText = submitText.textContent;

            const name = $('#contactName').value.trim();
            const email = $('#contactEmail').value.trim();
            const subject = $('#contactSubject').value.trim();
            const message = $('#contactMessage').value.trim();

            if (!name || !email || !message) return;

            submitText.textContent = 'TRANSMITTING...';
            btn.disabled = true;
            btn.style.borderColor = 'var(--warning)';
            btn.style.color = 'var(--warning)';

            try {
                const payload = {
                    name: name,
                    email: email,
                    message: subject ? `[${subject}] ${message}` : message
                };
                const res = await fetch('/api/contact', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify(payload)
                });
                const data = await res.json();

                if (res.ok) {
                    submitText.textContent = '✓ TRANSMITTED';
                    btn.style.borderColor = 'var(--accent)';
                    btn.style.color = 'var(--accent)';
                    form.reset();
                } else {
                    submitText.textContent = '✗ FAILED';
                    btn.style.borderColor = '#ff2d2d';
                    btn.style.color = '#ff2d2d';
                }
            } catch (err) {
                submitText.textContent = '✗ ERROR';
                btn.style.borderColor = '#ff2d2d';
                btn.style.color = '#ff2d2d';
            }

            setTimeout(() => {
                submitText.textContent = originalText;
                btn.disabled = false;
                btn.style.borderColor = '';
                btn.style.color = '';
            }, 2500);
        });
    }

    // ─── FOOTER SIGNAL ANIMATION ───
    function initFooterSignal() {
        const signal = $('#footerSignal');
        if (!signal) return;

        const blocks = ['░', '█'];
        function update() {
            const strength = 6 + Math.floor(Math.random() * 4); // 6-9 out of 10
            let bar = '';
            for (let i = 0; i < 10; i++) {
                bar += i < strength ? blocks[1] : blocks[0];
            }
            signal.textContent = `SIGNAL: ${bar} ${strength * 10}%`;
        }

        setInterval(update, 3000);
    }

    // ─── RANDOM HUD DATA FLICKERS ───
    function initHUDFlickers() {
        const statValues = $$('.stat-value:not(.stat-online)');
        
        setInterval(() => {
            statValues.forEach(el => {
                if (Math.random() > 0.7) {
                    el.style.opacity = '0.3';
                    setTimeout(() => {
                        el.style.opacity = '1';
                    }, 100 + Math.random() * 200);
                }
            });
        }, 2000);
    }

    // ─── PANEL HOVER GLOW TRAIL ───
    function initPanelGlow() {
        $$('.panel, .blog-card, .dev-card, .link-card').forEach(card => {
            card.addEventListener('mousemove', (e) => {
                const rect = card.getBoundingClientRect();
                const x = e.clientX - rect.left;
                const y = e.clientY - rect.top;
                card.style.setProperty('--mouse-x', `${x}px`);
                card.style.setProperty('--mouse-y', `${y}px`);
                card.style.background = `radial-gradient(circle 200px at ${x}px ${y}px, rgba(51, 51, 51, 0.04), var(--bg-panel))`;
            });

            card.addEventListener('mouseleave', () => {
                card.style.background = '';
            });
        });
    }

    // ─── INIT ───

    // ─── NETWORK GRAPH ───
    function initNetworkGraph() {
        const canvas = document.getElementById('networkGraph');
        if (!canvas) return;
        const ctx = canvas.getContext('2d');

        function resize() {
            const rect = canvas.parentElement.getBoundingClientRect();
            canvas.width = rect.width;
            canvas.height = 100;
        }
        resize();
        window.addEventListener('resize', resize);

        const dlData = [];
        const ulData = [];
        const maxPoints = 80;

        // Seed with zeros — graph populates with real samples from initLiveStats
        for (let i = 0; i < maxPoints; i++) {
            dlData.push(0);
            ulData.push(0);
        }
        // Auto-scaling peak (starts at 10 Mbps floor)
        let peakMbps = 10;

        function drawLine(data, color, alpha) {
            const w = canvas.width;
            const h = canvas.height;
            const step = w / (maxPoints - 1);

            ctx.beginPath();
            ctx.strokeStyle = color;
            ctx.lineWidth = 1.5;
            ctx.globalAlpha = alpha;

            for (let i = 0; i < data.length; i++) {
                const x = i * step;
                const scaled = Math.min(100, (data[i] / peakMbps) * 100);
                const y = h - (scaled / 100) * h;
                if (i === 0) ctx.moveTo(x, y);
                else ctx.lineTo(x, y);
            }
            ctx.stroke();

            // Fill under the line
            ctx.lineTo(canvas.width, canvas.height);
            ctx.lineTo(0, canvas.height);
            ctx.closePath();
            ctx.fillStyle = color;
            ctx.globalAlpha = alpha * 0.08;
            ctx.fill();
            ctx.globalAlpha = 1;
        }

        function drawGrid() {
            const w = canvas.width;
            const h = canvas.height;
            ctx.strokeStyle = 'rgba(0,255,200,0.06)';
            ctx.lineWidth = 0.5;

            // Horizontal
            for (let y = 0; y < h; y += 20) {
                ctx.beginPath();
                ctx.moveTo(0, y);
                ctx.lineTo(w, y);
                ctx.stroke();
            }
            // Vertical
            for (let x = 0; x < w; x += 30) {
                ctx.beginPath();
                ctx.moveTo(x, 0);
                ctx.lineTo(x, h);
                ctx.stroke();
            }
        }

        // Readouts (dlSpeed/ulSpeed/packetCount) are updated by initLiveStats with real values

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

            // Pull latest real Mbps from shared state set by initLiveStats
            const stats = window.__serverStats || { dlMbps: 0, ulMbps: 0 };
            dlData.push(stats.dlMbps || 0);
            ulData.push(stats.ulMbps || 0);
            if (dlData.length > maxPoints) dlData.shift();
            if (ulData.length > maxPoints) ulData.shift();

            // Smooth auto-scaling peak
            const curMax = Math.max.apply(null, dlData.concat(ulData).concat([1]));
            peakMbps = peakMbps * 0.95 + Math.max(10, curMax * 1.2) * 0.05;

            drawLine(dlData, '#00ff9d', 0.9);  // download = green
            drawLine(ulData, '#00a8ff', 0.7);  // upload = blue

            requestAnimationFrame(animate);
        }
        animate();
    }

    // ─── METRIC BARS (fluctuating) ───
    function initMetricBars() {
        const metrics = [
            { bar: 'cpuBar', val: 'cpuVal', base: 23, range: 18 },
            { bar: 'memBar', val: 'memVal', base: 67, range: 8 },
            { bar: 'diskBar', val: 'diskVal', base: 45, range: 25 },
            { bar: 'bwBar', val: 'bwVal', base: 78, range: 15 },
        ];

        function updateMetrics() {
            metrics.forEach(m => {
                const barEl = document.getElementById(m.bar);
                const valEl = document.getElementById(m.val);
                if (!barEl || !valEl) return;

                const val = Math.max(5, Math.min(98, m.base + (Math.random() - 0.5) * m.range));
                barEl.style.width = val + '%';
                valEl.textContent = Math.round(val) + '%';

                // Colour shift for high values
                if (val > 85) {
                    barEl.style.background = 'linear-gradient(90deg, #ff2d2d, rgba(255,71,87,0.4))';
                    barEl.style.boxShadow = '0 0 8px rgba(255,71,87,0.4)';
                    valEl.style.color = '#ff2d2d';
                } else if (val > 70) {
                    barEl.style.background = 'linear-gradient(90deg, #c9a227, rgba(255,165,2,0.4))';
                    barEl.style.boxShadow = '0 0 8px rgba(255,165,2,0.3)';
                    valEl.style.color = '#c9a227';
                } else {
                    barEl.style.background = '';
                    barEl.style.boxShadow = '';
                    valEl.style.color = '';
                }
            });
        }

        updateMetrics();
        setInterval(updateMetrics, 2000 + Math.random() * 1000);
    }

    // ─── SCAN BAR ───
    function initScanBar() {
        const fill = document.getElementById('scanFill');
        const pct = document.getElementById('scanPct');
        if (!fill || !pct) return;

        let progress = 0;
        function tick() {
            progress += 0.5 + Math.random() * 1.5;
            if (progress >= 100) {
                progress = 0;
                fill.style.transition = 'none';
                fill.style.width = '0%';
                pct.textContent = '0%';
                setTimeout(() => {
                    fill.style.transition = 'width 0.3s linear';
                    requestAnimationFrame(tick);
                }, 800);
                return;
            }
            fill.style.width = progress + '%';
            pct.textContent = Math.round(progress) + '%';
            setTimeout(tick, 80 + Math.random() * 120);
        }

        fill.style.transition = 'width 0.3s linear';
        tick();
    }

    // ─── POWER FLICKER ───
    function initPowerFlicker() {
        const el = document.getElementById('powerPct');
        if (!el) return;

        setInterval(() => {
            const vals = [97, 98, 99, 98, 100, 98, 97, 99];
            const v = vals[Math.floor(Math.random() * vals.length)];
            el.innerHTML = v + '<span class="power-unit">%</span>';
        }, 4000);
    }

    // ─── SERVER HEALTH ───
    function initServerHealth() {
        const el = document.getElementById('serverHealth');
        if (!el) return;

        setInterval(() => {
            const v = 95 + Math.floor(Math.random() * 5);
            el.textContent = v + '%';
        }, 5000);
    }

    // ─── API CONFIG ───
    const API_BASE = window.location.hostname === 'localhost' ? 'http://localhost:5000' : '/api';

    // ─── BLOG FEED (dynamic from API) ───
    function initBlogFeed() {
        const grid = document.getElementById('blogGrid');
        if (!grid) return;

        fetch(API_BASE + '/posts')
            .then(r => r.json())
            .then(posts => {
                posts.sort((a, b) => new Date(b.date) - new Date(a.date));
                const latest = posts.slice(0, 3);

                if (latest.length === 0) {
                    grid.innerHTML = '<div class="blog-loading-placeholder">NO TRANSMISSIONS FOUND</div>';
                    return;
                }

                grid.innerHTML = latest.map((post, i) => `
                    <article class="blog-card" data-animate style="opacity:0; transform:translateY(20px); transition:all 0.5s ease ${i * 0.15}s;">
                        <div class="blog-card-header">
                            <span class="blog-date">${post.date.replace(/-/g, '.')}</span>
                            <span class="blog-tag">${(post.tags && post.tags[0]) ? post.tags[0].toUpperCase() : 'POST'}</span>
                        </div>
                        <h3 class="blog-title">${post.title}</h3>
                        <p class="blog-excerpt">${post.excerpt || (post.content || '').substring(0, 120) + '...'}</p>
                        <div class="blog-footer">
                            <span class="blog-read-time">◷ ${Math.max(1, Math.ceil((post.word_count || 300) / 250))} MIN READ</span>
                            <a href="/blog/post/${post.slug}" class="blog-link">READ →</a>
                        </div>
                    </article>
                `).join('');

                // Animate cards in
                requestAnimationFrame(() => {
                    grid.querySelectorAll('.blog-card').forEach(card => {
                        card.style.opacity = '1';
                        card.style.transform = 'translateY(0)';
                    });
                });
            })
            .catch(() => {
                grid.innerHTML = '<div class="blog-loading-placeholder">SIGNAL LOST — RETRY LATER</div>';
            });
    }

    // ─── LIVE SERVER STATS (from API) — REAL DATA ───
    // Shared state used by the network graph
    window.__serverStats = {
        lastRx: null, lastTx: null, lastT: null,
        dlMbps: 0, ulMbps: 0, bwPct: 0
    };

    function formatUptime(sec) {
        sec = Math.floor(sec);
        const d = Math.floor(sec / 86400);
        const h = Math.floor((sec % 86400) / 3600);
        const m = Math.floor((sec % 3600) / 60);
        return d + 'd ' + String(h).padStart(2, '0') + 'h ' + String(m).padStart(2, '0') + 'm';
    }

    function applyBar(barId, valId, value, unit) {
        const barEl = document.getElementById(barId);
        const valEl = document.getElementById(valId);
        if (!barEl || !valEl) return;
        const v = Math.max(0, Math.min(100, value));
        barEl.style.width = v + '%';
        valEl.textContent = Math.round(v) + (unit || '%');
        if (v > 85) {
            barEl.style.background = 'linear-gradient(90deg, #ff2d2d, rgba(255,71,87,0.4))';
            barEl.style.boxShadow = '0 0 8px rgba(255,71,87,0.4)';
            valEl.style.color = '#ff2d2d';
        } else if (v > 70) {
            barEl.style.background = 'linear-gradient(90deg, #c9a227, rgba(255,165,2,0.4))';
            barEl.style.boxShadow = '0 0 8px rgba(255,165,2,0.3)';
            valEl.style.color = '#c9a227';
        } else {
            barEl.style.background = '';
            barEl.style.boxShadow = '';
            valEl.style.color = '';
        }
    }

    function initLiveStats() {
        // Assumed max link capacity for bandwidth % scaling (1 Gbps)
        const LINK_MBPS = 1000;

        function fetchStats() {
            fetch(API_BASE + '/stats')
                .then(r => r.json())
                .then(d => {
                    // CPU / MEM / DISK bars
                    applyBar('cpuBar', 'cpuVal', d.cpu_percent);
                    applyBar('memBar', 'memVal', d.memory_percent);
                    applyBar('diskBar', 'diskVal', d.disk_percent);

                    // Bandwidth — compute MB/s delta from network_rx_bytes + network_tx_bytes
                    const now = (d.timestamp || Date.now() / 1000);
                    const s = window.__serverStats;
                    if (s.lastRx !== null && s.lastT !== null && now > s.lastT) {
                        const dt = Math.max(0.1, now - s.lastT);
                        const rxBps = Math.max(0, (d.network_rx_bytes - s.lastRx) / dt);
                        const txBps = Math.max(0, (d.network_tx_bytes - s.lastTx) / dt);
                        const rxMbps = (rxBps * 8) / 1e6;
                        const txMbps = (txBps * 8) / 1e6;
                        s.dlMbps = rxMbps;
                        s.ulMbps = txMbps;
                        const totalMbps = rxMbps + txMbps;
                        s.bwPct = Math.min(100, (totalMbps / LINK_MBPS) * 100);
                        applyBar('bwBar', 'bwVal', s.bwPct);

                        // Network graph stat readouts
                        const dlEl = document.getElementById('dlSpeed');
                        const ulEl = document.getElementById('ulSpeed');
                        const pkEl = document.getElementById('packetCount');
                        const fmtSpeed = mbps => mbps >= 1000 ? (mbps / 1000).toFixed(2) + ' Gbps'
                                              : mbps >= 1 ? mbps.toFixed(1) + ' Mbps'
                                              : (mbps * 1000).toFixed(0) + ' Kbps';
                        if (dlEl) dlEl.textContent = fmtSpeed(rxMbps);
                        if (ulEl) ulEl.textContent = fmtSpeed(txMbps);
                        if (pkEl) pkEl.textContent = (d.active_connections || 0).toLocaleString() + ' conns';
                    } else {
                        // Seed on first sample
                        applyBar('bwBar', 'bwVal', 0);
                    }
                    s.lastRx = d.network_rx_bytes;
                    s.lastTx = d.network_tx_bytes;
                    s.lastT = now;

                    // Uptime (HTML id is serverUptime)
                    const uptimeEl = document.getElementById('serverUptime') || document.getElementById('uptime');
                    if (uptimeEl && d.uptime_seconds) {
                        uptimeEl.textContent = formatUptime(d.uptime_seconds);
                    }

                    // Server health — weighted score from cpu/mem/disk
                    const healthEl = document.getElementById('serverHealth');
                    if (healthEl) {
                        const penalty = (d.cpu_percent * 0.35 + d.memory_percent * 0.35 + d.disk_percent * 0.30);
                        const health = Math.round(100 - penalty * 0.35);
                        healthEl.textContent = Math.min(99, Math.max(40, health)) + '%';
                    }

                    // Power panel — use inverse of CPU load as "reliability" %
                    const powerEl = document.getElementById('powerPct');
                    if (powerEl) {
                        const rel = Math.max(60, Math.min(100, 100 - d.cpu_percent * 0.4));
                        powerEl.innerHTML = Math.round(rel) + '<span class="power-unit">%</span>';
                    }

                    // Containers
                    const containerEl = document.getElementById('containerUp');
                    const containerTotalEl = document.querySelector('.container-total');
                    const containerBar = document.getElementById('containerBar');
                    if (containerEl) containerEl.textContent = d.container_running;
                    if (containerTotalEl) containerTotalEl.textContent = d.container_total;
                    if (containerBar && d.container_total) {
                        containerBar.style.width = ((d.container_running / d.container_total) * 100) + '%';
                    }
                })
                .catch(() => {});
        }

        fetchStats();
        setInterval(fetchStats, 5000); // refresh every 5s
    }

    // ─── LIVE WEATHER ───
    function initLiveWeather() {
        const tempEl = document.getElementById('weatherTemp');
        const condEl = document.getElementById('weatherCond');
        if (!tempEl && !condEl) return;

        fetch(API_BASE + '/weather')
            .then(r => r.json())
            .then(d => {
                if (tempEl) tempEl.textContent = d.temp_c + '°C';
                if (condEl) condEl.textContent = d.condition;
                const feelsEl = document.getElementById('weatherFeels');
                if (feelsEl) feelsEl.textContent = 'FEELS ' + d.feels_like + '°C';
                const windEl = document.getElementById('weatherWind');
                if (windEl) windEl.textContent = d.wind_kph + ' KPH ' + d.wind_dir;
                const humEl = document.getElementById('weatherHumidity');
                if (humEl) humEl.textContent = d.humidity + '% RH';
            })
            .catch(() => {});
    }

    // ─── NOW PLAYING ───
    function initNowPlaying() {
        const trackEl = document.getElementById('npTrack');
        const artistEl = document.getElementById('npArtist');
        if (!trackEl && !artistEl) return;

        function fetchTrack() {
            fetch(API_BASE + '/nowplaying')
                .then(r => r.json())
                .then(d => {
                    if (trackEl) trackEl.textContent = d.track;
                    if (artistEl) artistEl.textContent = d.artist;
                    const albumEl = document.getElementById('npAlbum');
                    if (albumEl) albumEl.textContent = d.album;
                })
                .catch(() => {});
        }

        fetchTrack();
        setInterval(fetchTrack, 30000);
    }


    document.addEventListener('DOMContentLoaded', () => {
        initNavbar();
        initClock();
        initUptime();
        initTyping();
        initScrollReveal();
        initSkillBars();
        initParticles();
        initGlitchEffect();
        initTerminal();
        initContactForm();
        initFooterSignal();
        initHUDFlickers();
        initPanelGlow();
        initNetworkGraph();
        // initMetricBars();  // disabled — real data from /api/stats via initLiveStats
        initScanBar();
        // initPowerFlicker();   // disabled — real load_avg-based via initLiveStats
        // initServerHealth();   // disabled — real health computed in initLiveStats

        // Live API integrations
        initBlogFeed();
        initLiveStats();
        initLiveWeather();
        initNowPlaying();


        // Page load animation
        document.body.style.opacity = '0';
        document.body.style.transition = 'opacity 0.8s ease';
        requestAnimationFrame(() => {
            document.body.style.opacity = '1';
        });

        console.log('%c[JAESWIFT] %cSystems Online', 
            'color: #d0d0d0; font-weight: bold; font-size: 14px;', 
            'color: #c0c0c0; font-size: 12px;');
    });

})();
