<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{{.Title}}</title>
    <script src="/static/tailwind.min.js"></script>
    <script src="/static/chart.umd.min.js"></script>
    <style>
        :root {
            --bg1: #050816;
            --bg2: #0b1224;
            --panel: rgba(255,255,255,0.055);
            --body-text: #f4f4f5;
            --button-bg: rgba(255,255,255,0.05);
            --button-border: rgba(255,255,255,0.10);
            --button-text: #e4e4e7;
            --text: var(--body-text);
            --border: var(--button-border);
        }

        * { box-sizing: border-box; }
        html, body { min-height: 100%; }

        body {
            font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
            background:
                radial-gradient(circle at 10% 10%, rgba(34,211,238,0.12), transparent 18%),
                radial-gradient(circle at 90% 10%, rgba(168,85,247,0.14), transparent 18%),
                linear-gradient(180deg, var(--bg1) 0%, var(--bg2) 100%);
            color: var(--body-text);
            transition: background 180ms ease, color 180ms ease;
        }

        body[data-theme="light"] {
            --bg1: #eef4ff;
            --bg2: #f8fafc;
            --panel: rgba(255,255,255,0.80);
            --body-text: #0f172a;
            --button-bg: rgba(255,255,255,0.88);
            --button-border: rgba(15,23,42,0.10);
            --button-text: #0f172a;
            --text: var(--body-text);
            --border: var(--button-border);
            background:
                radial-gradient(circle at 10% 10%, rgba(14,165,233,0.10), transparent 20%),
                radial-gradient(circle at 90% 10%, rgba(168,85,247,0.10), transparent 18%),
                linear-gradient(180deg, var(--bg1) 0%, var(--bg2) 100%);
        }

        .glass {
            background: var(--panel);
            backdrop-filter: blur(14px);
            -webkit-backdrop-filter: blur(14px);
        }

        body[data-theme="light"] .glass,
        body[data-theme="light"] .summary-card,
        body[data-theme="light"] .intel-card,
        body[data-theme="light"] .verdict-card {
            border-color: rgba(15,23,42,0.10) !important;
            box-shadow: 0 10px 28px rgba(15,23,42,0.06);
        }

        body[data-theme="light"] .bg-zinc-900\/60,
        body[data-theme="light"] .bg-white\/5 {
            background: rgba(255,255,255,0.86) !important;
        }

        body[data-theme="light"] .border-zinc-800,
        body[data-theme="light"] .border-white\/10 {
            border-color: rgba(15,23,42,0.10) !important;
        }

        body[data-theme="light"] .text-zinc-100,
        body[data-theme="light"] .text-zinc-200 { color: #0f172a !important; }
        body[data-theme="light"] .text-zinc-300 { color: #1e293b !important; }
        body[data-theme="light"] .text-zinc-400,
        body[data-theme="light"] .text-zinc-500 { color: #475569 !important; }
        body[data-theme="light"] .text-emerald-300 { color: #059669 !important; }
        body[data-theme="light"] .text-emerald-400 { color: #10b981 !important; }
        body[data-theme="light"] .text-sky-100,
        body[data-theme="light"] .text-sky-200 { color: #0369a1 !important; }
        body[data-theme="light"] .text-violet-100,
        body[data-theme="light"] .text-violet-200 { color: #7c3aed !important; }
        body[data-theme="light"] .text-amber-200 { color: #b45309 !important; }
        body[data-theme="light"] .text-sky-400 { color: #0284c7 !important; }
        body[data-theme="light"] .text-violet-400 { color: #7c3aed !important; }
        body[data-theme="light"] .text-red-400 { color: #dc2626 !important; }
        body[data-theme="light"] .text-yellow-400 { color: #b45309 !important; }
        .control-btn {
            display: inline-flex;
            align-items: center;
            justify-content: center;
            min-height: 42px;
            padding: 10px 14px;
            border-radius: 14px;
            border: 1px solid var(--border);
            background: rgba(255,255,255,.05);
            color: var(--text);
            text-decoration: none;
            font-weight: 600;
            font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
            cursor: pointer;
            transition: 160ms ease;
        }

        body[data-theme="light"] .control-btn {
            background: rgba(255,255,255,.88);
        }

        .control-btn.primary {
            background: rgba(14,165,233,0.14);
            border-color: rgba(14,165,233,0.35);
        }

        .control-btn:hover {
            transform: translateY(-1px);
            box-shadow: 0 8px 20px rgba(0,0,0,0.10);
        }

        .soft-glow-green { box-shadow: 0 0 0 1px rgba(34,197,94,0.28), 0 0 38px rgba(34,197,94,0.08); }
        .soft-glow-yellow { box-shadow: 0 0 0 1px rgba(234,179,8,0.28), 0 0 38px rgba(234,179,8,0.08); }
        .soft-glow-red { box-shadow: 0 0 0 1px rgba(239,68,68,0.28), 0 0 38px rgba(239,68,68,0.08); }

        .gauge-header-row {
            display: flex;
            align-items: flex-start;
            justify-content: space-between;
            gap: 24px;
            margin-bottom: 10px;
        }

        .gauge-head {
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 16px;
            text-align: center;
            margin-bottom: 10px;
        }

        .gauge-head.with-digital {
            justify-content: flex-start;
            text-align: left;
            margin-bottom: 0;
        }

        .gauge-head-copy {
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
        }

        .gauge-head-copy.with-digital { align-items: flex-start; }

        .gauge-digital { text-align: right; flex-shrink: 0; }

        .gauge-container {
            position: relative;
            width: 100%;
            max-width: 720px;
            margin: 0 auto;
        }

        .gauge-container.no-digital { height: clamp(430px, 48vw, 560px); }
        .gauge-container.with-digital { height: clamp(360px, 42vw, 500px); }

        .gauge-canvas { width: 100%; height: 100%; display: block; }

        .window-btn.active, .trend-window-btn.active {
            border-color: rgba(34,211,238,0.9);
            color: white;
            background: rgba(34,211,238,0.14);
            box-shadow: 0 0 0 1px rgba(34,211,238,0.18) inset;
        }

        body[data-theme="light"] .window-btn.active,
        body[data-theme="light"] .trend-window-btn.active {
            color: #0f172a;
            background: rgba(14,165,233,0.12);
        }

        .chart-wrap { width: min(92vw, 1800px); margin: 0 auto; }

        .summary-card, .intel-card, .verdict-card {
            display: flex;
            align-items: center;
            justify-content: space-between;
            gap: 16px;
            border-radius: 20px;
            padding: 18px 20px;
            border: 1px solid rgba(255,255,255,0.09);
            background: rgba(255,255,255,0.04);
            transition: 180ms ease;
        }

        .intel-card { min-height: 126px; align-items: flex-start; }

        .summary-card.ok, .intel-card.ok, .verdict-card.ok {
            border-color: rgba(34,197,94,0.35);
            box-shadow: 0 0 0 1px rgba(34,197,94,0.08) inset, 0 0 26px rgba(34,197,94,0.06);
        }
        .summary-card.warning, .intel-card.warning, .verdict-card.warning {
            border-color: rgba(234,179,8,0.35);
            box-shadow: 0 0 0 1px rgba(234,179,8,0.08) inset, 0 0 26px rgba(234,179,8,0.06);
        }
        .summary-card.critical, .intel-card.critical, .verdict-card.critical {
            border-color: rgba(239,68,68,0.35);
            box-shadow: 0 0 0 1px rgba(239,68,68,0.08) inset, 0 0 26px rgba(239,68,68,0.06);
        }
        .summary-card.neutral, .intel-card.neutral, .verdict-card.neutral {
            border-color: rgba(113,113,122,0.35);
            box-shadow: 0 0 0 1px rgba(113,113,122,0.08) inset;
        }

        .summary-dot { width: 14px; height: 14px; border-radius: 999px; }
        .summary-dot.ok { background: #10b981; box-shadow: 0 0 14px rgba(16,185,129,0.55); }
        .summary-dot.warning { background: #f59e0b; box-shadow: 0 0 14px rgba(245,158,11,0.55); }
        .summary-dot.critical { background: #ef4444; box-shadow: 0 0 14px rgba(239,68,68,0.55); }
        .summary-dot.neutral { background: #71717a; box-shadow: 0 0 12px rgba(113,113,122,0.35); }

        .summary-status.ok { color: #34d399; }
        .summary-status.warning { color: #facc15; }
        .summary-status.critical { color: #f87171; }
        .summary-status.neutral { color: #a1a1aa; }

        .intel-value {
            font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
            font-size: 2rem;
            font-weight: 700;
            line-height: 1;
            color: #f4f4f5;
        }

        body[data-theme="light"] .intel-value { color: #0f172a; }

        .intel-sub { font-size: 0.83rem; color: #a1a1aa; margin-top: 10px; line-height: 1.35; }

        .intel-kpi {
            font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
            font-size: 1.1rem;
            font-weight: 700;
        }
        
        .dir-up { color: #facc15; }
        .dir-down { color: #34d399; }
        .dir-flat { color: #a1a1aa; }
        .dir-bad { color: #f87171; }
        .mini-mono { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; }

        .severity-pill {
            display: inline-flex;
            align-items: center;
            justify-content: center;
            min-width: 86px;
            padding: 4px 10px;
            border-radius: 999px;
            font-size: 12px;
            font-weight: 700;
            letter-spacing: 0.04em;
        }

        .severity-pill.info { background: rgba(59,130,246,0.12); color: #93c5fd; border: 1px solid rgba(59,130,246,0.22); }
        .severity-pill.warning { background: rgba(245,158,11,0.12); color: #fde68a; border: 1px solid rgba(245,158,11,0.22); }
        .severity-pill.critical { background: rgba(239,68,68,0.12); color: #fca5a5; border: 1px solid rgba(239,68,68,0.22); }
        body[data-theme="light"] .severity-pill.info { color: #1d4ed8; }
        body[data-theme="light"] .severity-pill.warning { color: #b45309; }
        body[data-theme="light"] .severity-pill.critical { color: #dc2626; }

        .alarm-table tbody tr:hover { background: rgba(255,255,255,0.03); }
        body[data-theme="light"] .alarm-table tbody tr:hover { background: rgba(15,23,42,0.03); }

        .limit-line-note { font-size: 0.8rem; color: #a1a1aa; }

        .process-offline {
            opacity: 0.35;
            filter: grayscale(1) blur(1.5px);
            pointer-events: none;
            user-select: none;
            transition: opacity 180ms ease, filter 180ms ease;
        }

        .process-online {
            opacity: 1;
            filter: none;
            pointer-events: auto;
            user-select: auto;
            transition: opacity 180ms ease, filter 180ms ease;
        }
    </style>
</head>
<body data-theme="dark">
    <div class="w-[95vw] max-w-[1800px] mx-auto p-4 md:p-8 min-h-screen">
        <div class="glass border border-white/10 rounded-3xl p-4 mb-8">
            <div class="flex flex-wrap gap-3 items-center justify-between">
                <div class="flex flex-wrap gap-3">
                    <a href="/" class="control-btn primary" target="_self">Dashboard</a>
                    <a href="/history" class="control-btn" target="_self">History</a>
                    <a href="/alarms" class="control-btn" target="_self">Alarms</a>
                    <a href="/kiosk" class="control-btn" target="_self">Kiosk</a>
                    <a href="/process-capability" class="control-btn" target="_self">Process Capability</a>
                    <a href="/reports" class="control-btn" target="_self">Reports</a>
                    <a href="/license" class="control-btn" target="_self">License</a>
                </div>
                <div class="text-zinc-500 text-sm font-mono">Embedded UI navigation</div>
            </div>
        </div>
        <div id="alarm-banner" class="hidden mb-6 bg-red-600/90 border border-red-500 text-white px-8 py-4 rounded-2xl flex items-center justify-between text-lg font-medium">
            <div class="flex items-center gap-3">
                <span class="text-2xl">&#9888;&#65039;</span>
                <span id="alarm-text">CRITICAL ALARM ACTIVE</span>
            </div>
        </div>

        <div class="flex flex-col gap-6 xl:flex-row xl:items-start xl:justify-between mb-8">
            <div>
                <h1 class="text-4xl md:text-5xl xl:text-6xl font-semibold tracking-tighter bg-gradient-to-r from-sky-300 to-violet-300 bg-clip-text text-transparent">{{.Title}}</h1>
                <p class="text-zinc-400 mt-2 text-base md:text-lg">{{.Subtitle}}</p>
                <p class="text-zinc-500 mt-1 text-sm font-mono">MAX_TONNAGE = {{printf "%.1f" .MaxTonnage}} {{.UnitForce}}</p>
            </div>

            <div class="flex flex-col gap-4 xl:items-end">
                {{if .ShowHeaderControls}}
                <div class="flex flex-wrap gap-3 justify-end">
                    <button id="theme-toggle" class="control-btn" type="button">Light theme</button>
                    <button id="fullscreen-toggle" class="control-btn" type="button">Enter fullscreen</button>
                </div>
                {{end}}

                <div class="glass border border-white/10 px-6 py-4 rounded-3xl flex flex-col md:flex-row md:items-center gap-4 md:gap-8 w-fit">
                    <div class="flex items-center gap-3">
                        <div id="dot" class="w-4 h-4 rounded-full bg-red-500 ring-4 ring-red-500/20"></div>
                        <span id="status-text" class="font-semibold text-lg text-red-400">Disconnected</span>
                    </div>
                    <div class="hidden md:block h-8 w-px bg-zinc-700"></div>
                    <div id="last-update" class="font-mono text-zinc-400 text-sm">Last update: --:--:--.---</div>
                    <div class="hidden md:block h-8 w-px bg-zinc-700"></div>
                    <div class="font-mono text-zinc-500 text-sm">Dropped S: <span id="dropped-samples">0</span> | E: <span id="dropped-events">0</span></div>
                </div>
            </div>
        </div>


        

        <div id="process-content">
        {{if .ShowVerdict}}
        <div id="verdict-card" class="verdict-card neutral mb-8">
            <div>
                <div class="text-xs uppercase tracking-[0.24em] text-zinc-500">Machine verdict</div>
                <div id="verdict-status" class="text-3xl md:text-4xl font-bold mt-2 text-zinc-200">NO DATA</div>
            </div>
            <div id="verdict-reason" class="text-right text-zinc-300 text-base md:text-lg">Waiting for PLC data</div>
        </div>
        {{end}}

        {{if .ShowSummaryBar}}
        <div class="grid grid-cols-1 lg:grid-cols-3 gap-4 mb-8">
            <div id="summary-force-card" class="summary-card neutral">
                <div class="flex items-center gap-3">
                    <div id="summary-force-dot" class="summary-dot neutral"></div>
                    <div>
                        <div class="text-xs uppercase tracking-[0.22em] text-zinc-500">FORCE</div>
                        <div id="summary-force-text" class="summary-status neutral font-semibold mt-1">NO DATA</div>
                    </div>
                </div>
                <div id="summary-force-value" class="font-mono text-zinc-200 text-lg">--</div>
            </div>

            <div id="summary-imbalance-card" class="summary-card neutral">
                <div class="flex items-center gap-3">
                    <div id="summary-imbalance-dot" class="summary-dot neutral"></div>
                    <div>
                        <div class="text-xs uppercase tracking-[0.22em] text-zinc-500">IMBALANCE</div>
                        <div id="summary-imbalance-text" class="summary-status neutral font-semibold mt-1">NO DATA</div>
                    </div>
                </div>
                <div id="summary-imbalance-value" class="font-mono text-zinc-200 text-lg">--</div>
            </div>

            <div id="summary-plc-card" class="summary-card neutral">
                <div class="flex items-center gap-3">
                    <div id="summary-plc-dot" class="summary-dot neutral"></div>
                    <div>
                        <div class="text-xs uppercase tracking-[0.22em] text-zinc-500">PLC</div>
                        <div id="summary-plc-text" class="summary-status neutral font-semibold mt-1">OFFLINE</div>
                    </div>
                </div>
                <div id="summary-plc-value" class="font-mono text-zinc-200 text-lg">Disconnected</div>
            </div>
        </div>
        {{end}}

        {{if .ShowOverview}}
        <div class="glass border border-white/10 rounded-3xl p-6 md:p-8 mb-8">
            <div class="flex flex-col xl:flex-row xl:items-center xl:justify-between gap-6">
                <div>
                    <div class="text-zinc-400 text-sm uppercase tracking-[0.25em]">TOTAL PEAK FORCE</div>
                    <div class="mt-2 flex items-end gap-4">
                        <div class="text-5xl md:text-6xl font-mono font-bold text-emerald-300 leading-none" id="sum-kn">0.0</div>
                        <div class="text-2xl text-emerald-400 mb-1">{{.UnitForce}}</div>
                    </div>
                </div>

                <div class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-4 min-w-[320px]">
                    <div class="bg-zinc-900/60 rounded-2xl px-5 py-4 border border-zinc-800">
                        <div class="text-zinc-500 text-xs uppercase tracking-widest">TOTAL %</div>
                        <div class="text-3xl font-mono font-bold text-sky-200 mt-1"><span id="sum-percent">0.0</span> {{.UnitPct}}</div>
                    </div>
                    <div class="bg-zinc-900/60 rounded-2xl px-5 py-4 border border-zinc-800">
                        <div class="text-zinc-500 text-xs uppercase tracking-widest">IMBALANCE</div>
                        <div class="text-3xl font-mono font-bold text-amber-200 mt-1"><span id="imbalance-pct">0.0</span> {{.UnitPct}}</div>
                        <div class="text-xs text-zinc-500 mt-2 font-mono">abs(L - R)</div>
                    </div>
                    <div class="bg-zinc-900/60 rounded-2xl px-5 py-4 border border-zinc-800">
                        <div class="text-zinc-500 text-xs uppercase tracking-widest">BIAS</div>
                        <div class="text-3xl font-mono font-bold text-violet-200 mt-1"><span id="bias-pct">0.0</span> {{.UnitPct}}</div>
                        <div class="text-xs text-zinc-500 mt-2 font-mono">L - R</div>
                    </div>
                    <div class="bg-zinc-900/60 rounded-2xl px-5 py-4 border border-zinc-800">
                        <div class="text-zinc-500 text-xs uppercase tracking-widest">LIMITS</div>
                        <div class="text-sm font-mono text-zinc-300 mt-2">Force W {{printf "%.0f" .WarningPercent}} / C {{printf "%.0f" .CriticalPercent}}</div>
                        <div class="text-xs text-zinc-500 mt-2 font-mono">Imb W {{printf "%.0f" .ImbalanceWarningPercent}} / C {{printf "%.0f" .ImbalanceCriticalPercent}}</div>
                    </div>
                </div>
            </div>
        </div>
        {{end}}

        {{if .ShowIntelligence}}
        <div class="glass border border-white/10 rounded-3xl p-6 md:p-8 mb-8">
            <div class="flex flex-col xl:flex-row gap-5 xl:items-center xl:justify-between mb-5">
                <div>
                    <h2 class="text-2xl md:text-3xl font-semibold">Drift / Deterioration Intelligence</h2>
                    <div class="text-zinc-400 mt-1 text-sm md:text-base">Averages, drift direction, imbalance deterioration and process stability</div>
                </div>
                <div class="flex flex-wrap items-center gap-2">
                    <button class="trend-window-btn active px-3 py-2 rounded-xl border border-white/10 bg-white/5 text-zinc-300 text-sm font-medium" data-window="5m">5m</button>
                    <button class="trend-window-btn px-3 py-2 rounded-xl border border-white/10 bg-white/5 text-zinc-300 text-sm font-medium" data-window="15m">15m</button>
                    <button class="trend-window-btn px-3 py-2 rounded-xl border border-white/10 bg-white/5 text-zinc-300 text-sm font-medium" data-window="30m">30m</button>
                    <button class="trend-window-btn px-3 py-2 rounded-xl border border-white/10 bg-white/5 text-zinc-300 text-sm font-medium" data-window="1h">1h</button>
                    <div class="flex items-center gap-2 ml-1">
                        <input id="custom-trend-window" type="text" placeholder="e.g. 90m or 2h" class="px-3 py-2 rounded-xl border border-white/10 bg-white/5 text-zinc-100 w-36 outline-none">
                        <button id="apply-trend-window" class="px-3 py-2 rounded-xl border border-sky-400/40 bg-sky-400/10 text-sky-200 text-sm font-medium">Apply</button>
                    </div>
                </div>
            </div>
            <div class="grid grid-cols-1 md:grid-cols-2 2xl:grid-cols-5 gap-4">
                <div id="intel-avg5-card" class="intel-card neutral"><div class="w-full">
                    <div class="text-xs uppercase tracking-[0.22em] text-zinc-500">AVG PEAK 5 MIN</div>
                    <div id="intel-avg5-value" class="intel-value mt-3">--</div>
                    <div id="intel-avg5-sub" class="intel-sub">No data</div>
                </div></div>
                <div id="intel-avg1h-card" class="intel-card neutral"><div class="w-full">
                    <div class="text-xs uppercase tracking-[0.22em] text-zinc-500">AVG PEAK 1 HOUR</div>
                    <div id="intel-avg1h-value" class="intel-value mt-3">--</div>
                    <div id="intel-avg1h-sub" class="intel-sub">No data</div>
                </div></div>
                <div id="intel-force-card" class="intel-card neutral"><div class="w-full">
                    <div class="text-xs uppercase tracking-[0.22em] text-zinc-500">FORCE TREND</div>
                    <div id="intel-force-value" class="intel-value mt-3">--</div>
                    <div id="intel-force-sub" class="intel-sub">No data</div>
                </div></div>
                <div id="intel-imb-card" class="intel-card neutral"><div class="w-full">
                    <div class="text-xs uppercase tracking-[0.22em] text-zinc-500">IMBALANCE TREND</div>
                    <div id="intel-imb-value" class="intel-value mt-3">--</div>
                    <div id="intel-imb-sub" class="intel-sub">No data</div>
                </div></div>
                <div id="intel-stability-card" class="intel-card neutral"><div class="w-full">
                    <div class="text-xs uppercase tracking-[0.22em] text-zinc-500">PROCESS STABILITY</div>
                    <div id="intel-stability-value" class="intel-value mt-3">--</div>
                    <div id="intel-stability-sub" class="intel-sub">No data</div>
                </div></div>
            </div>
        </div>
        {{end}}

        {{if .ShowAlarmTimeline}}
        <div class="glass border border-white/10 rounded-3xl p-6 md:p-8 mb-8">
            <div class="flex flex-col lg:flex-row lg:items-center lg:justify-between gap-4 mb-5">
                <div>
                    <h2 class="text-2xl md:text-3xl font-semibold">Event / Alarm Timeline</h2>
                    <div class="text-zinc-400 mt-1 text-sm md:text-base">Recent transitions show exactly when the process began drifting, overloading, losing balance, or losing PLC communication</div>
                </div>
                <div class="limit-line-note">Newest events first • clear events included</div>
            </div>
            <div class="overflow-x-auto">
                <table class="alarm-table w-full text-sm">
                    <thead>
                        <tr class="text-left text-zinc-500 border-b border-white/10">
                            <th class="py-3 pr-4">Time</th>
                            <th class="py-3 pr-4">Severity</th>
                            <th class="py-3 pr-4">Source</th>
                            <th class="py-3 pr-4">Event</th>
                            <th class="py-3 pr-4 text-right">Value</th>
                            <th class="py-3 text-right">Limit</th>
                        </tr>
                    </thead>
                    <tbody id="alarm-table-body" class="text-zinc-200">
                        <tr><td colspan="6" class="py-6 text-center text-zinc-500">No events yet</td></tr>
                    </tbody>
                </table>
            </div>
        </div>
        {{end}}

        {{if .ShowGauges}}
        <div class="grid grid-cols-1 xl:grid-cols-2 gap-8 mb-8">
            <div id="card-l" class="glass border border-white/10 rounded-3xl p-5 md:p-6 xl:p-8 transition-all duration-300">
                {{if .ShowGaugeDigital}}
                <div class="gauge-header-row">
                    <div class="gauge-head with-digital">
                        <div id="led-l" class="w-6 h-6 bg-emerald-500 rounded-full shadow-lg shadow-emerald-600/40"></div>
                        <div class="gauge-head-copy with-digital">
                            <h2 class="text-2xl md:text-3xl xl:text-4xl font-bold tracking-wider">{{.LeftLabel}}</h2>
                            <div id="state-l" class="text-sm text-zinc-400 mt-1">NORMAL</div>
                        </div>
                    </div>
                    <div id="digital-l" class="gauge-digital">
                        <div class="percent text-5xl md:text-6xl xl:text-7xl font-mono font-bold text-sky-100 leading-none">0.0</div>
                        <div class="text-xl text-sky-400 mt-1">{{.UnitPct}}</div>
                        <div class="kn text-lg text-zinc-300 font-mono mt-3">0.0 {{.UnitForce}}</div>
                    </div>
                </div>
                {{else}}
                <div class="gauge-head">
                    <div id="led-l" class="w-6 h-6 bg-emerald-500 rounded-full shadow-lg shadow-emerald-600/40"></div>
                    <div class="gauge-head-copy">
                        <h2 class="text-2xl md:text-3xl xl:text-4xl font-bold tracking-wider">{{.LeftLabel}}</h2>
                        <div id="state-l" class="text-sm text-zinc-400 mt-1">NORMAL</div>
                    </div>
                </div>
                {{end}}
                <div class="gauge-container {{if .ShowGaugeDigital}}with-digital{{else}}no-digital{{end}}">
                    <canvas id="gaugeL" class="gauge-canvas"></canvas>
                </div>
            </div>

            <div id="card-r" class="glass border border-white/10 rounded-3xl p-5 md:p-6 xl:p-8 transition-all duration-300">
                {{if .ShowGaugeDigital}}
                <div class="gauge-header-row">
                    <div class="gauge-head with-digital">
                        <div id="led-r" class="w-6 h-6 bg-emerald-500 rounded-full shadow-lg shadow-emerald-600/40"></div>
                        <div class="gauge-head-copy with-digital">
                            <h2 class="text-2xl md:text-3xl xl:text-4xl font-bold tracking-wider">{{.RightLabel}}</h2>
                            <div id="state-r" class="text-sm text-zinc-400 mt-1">NORMAL</div>
                        </div>
                    </div>
                    <div id="digital-r" class="gauge-digital">
                        <div class="percent text-5xl md:text-6xl xl:text-7xl font-mono font-bold text-violet-100 leading-none">0.0</div>
                        <div class="text-xl text-violet-400 mt-1">{{.UnitPct}}</div>
                        <div class="kn text-lg text-zinc-300 font-mono mt-3">0.0 {{.UnitForce}}</div>
                    </div>
                </div>
                {{else}}
                <div class="gauge-head">
                    <div id="led-r" class="w-6 h-6 bg-emerald-500 rounded-full shadow-lg shadow-emerald-600/40"></div>
                    <div class="gauge-head-copy">
                        <h2 class="text-2xl md:text-3xl xl:text-4xl font-bold tracking-wider">{{.RightLabel}}</h2>
                        <div id="state-r" class="text-sm text-zinc-400 mt-1">NORMAL</div>
                    </div>
                </div>
                {{end}}
                <div class="gauge-container {{if .ShowGaugeDigital}}with-digital{{else}}no-digital{{end}}">
                    <canvas id="gaugeR" class="gauge-canvas"></canvas>
                </div>
            </div>
        </div>
        {{end}}

        {{if .ShowTrendChart}}
        <div class="chart-wrap">
            <div class="glass border border-white/10 rounded-3xl p-5 md:p-7">
                <div class="flex flex-col xl:flex-row gap-5 xl:items-center xl:justify-between mb-5">
                    <div>
                        <h2 class="text-2xl md:text-3xl font-semibold">Peak Trend</h2>
                        <div class="text-zinc-400 mt-1 text-sm md:text-base">Piezo peak/stroke history from SQLite with visible warning and critical limits</div>
                    </div>
                    <div class="flex flex-wrap items-center gap-2">
                        <button class="window-btn active px-3 py-2 rounded-xl border border-white/10 bg-white/5 text-zinc-300 text-sm font-medium" data-window="30s">30s</button>
                        <button class="window-btn px-3 py-2 rounded-xl border border-white/10 bg-white/5 text-zinc-300 text-sm font-medium" data-window="1m">1m</button>
                        <button class="window-btn px-3 py-2 rounded-xl border border-white/10 bg-white/5 text-zinc-300 text-sm font-medium" data-window="5m">5m</button>
                        <button class="window-btn px-3 py-2 rounded-xl border border-white/10 bg-white/5 text-zinc-300 text-sm font-medium" data-window="15m">15m</button>
                        <button class="window-btn px-3 py-2 rounded-xl border border-white/10 bg-white/5 text-zinc-300 text-sm font-medium" data-window="1h">1h</button>
                        <button class="window-btn px-3 py-2 rounded-xl border border-white/10 bg-white/5 text-zinc-300 text-sm font-medium" data-window="8h">8h</button>
                        <button class="window-btn px-3 py-2 rounded-xl border border-white/10 bg-white/5 text-zinc-300 text-sm font-medium" data-window="24h">24h</button>
                        <div class="flex items-center gap-2 ml-1">
                            <input id="custom-window" type="text" placeholder="e.g. 90m or 2h" class="px-3 py-2 rounded-xl border border-white/10 bg-white/5 text-zinc-100 w-36 outline-none">
                            <button id="apply-window" class="px-3 py-2 rounded-xl border border-sky-400/40 bg-sky-400/10 text-sky-200 text-sm font-medium">Apply</button>
                        </div>
                    </div>
                </div>
                <div class="h-[52vh] min-h-[420px] max-h-[760px]">
                    <canvas id="lineChart"></canvas>
                </div>
            </div>
        </div>
        {{end}}
        </div><!-- #process-content -->
    </div>

    <script src="/static/app-common.js"></script>
  <script>
        const WARNING_PERCENT = {{.WarningPercent}};
        const CRITICAL_PERCENT = {{.CriticalPercent}};
        const GAUGE_MAX_PERCENT = {{.GaugeMaxPercent}};
        const IMBALANCE_WARNING_PERCENT = {{.ImbalanceWarningPercent}};
        const IMBALANCE_CRITICAL_PERCENT = {{.ImbalanceCriticalPercent}};
        const UNIT_FORCE = '{{.UnitForce}}';
        const UNIT_PCT = '{{.UnitPct}}';
        const POLL_MS = {{.PollMs}};
        const DEFAULT_WINDOW = '{{.DefaultWindow}}';
        const DEFAULT_TREND_WINDOW = '{{.DefaultTrendWindow}}';
        const STALE_MS = Math.max(POLL_MS * 4, 2500);

        const SHOW_HEADER_CONTROLS = {{if .ShowHeaderControls}}true{{else}}false{{end}};
        const SHOW_VERDICT = {{if .ShowVerdict}}true{{else}}false{{end}};
        const SHOW_SUMMARY_BAR = {{if .ShowSummaryBar}}true{{else}}false{{end}};
        const SHOW_OVERVIEW = {{if .ShowOverview}}true{{else}}false{{end}};
        const SHOW_INTELLIGENCE = {{if .ShowIntelligence}}true{{else}}false{{end}};
        const SHOW_ALARM_TIMELINE = {{if .ShowAlarmTimeline}}true{{else}}false{{end}};
        const SHOW_GAUGES = {{if .ShowGauges}}true{{else}}false{{end}};
        const SHOW_GAUGE_DIGITAL = {{if .ShowGaugeDigital}}true{{else}}false{{end}};
        const SHOW_TREND_CHART = {{if .ShowTrendChart}}true{{else}}false{{end}};
        const CURRENT_UI_REVISION = {{.UIRevision}};

        const START_ANGLE = Math.PI * 0.75;
        const END_ANGLE   = Math.PI * 2.25;

        let lineChart = null;
        let latestData = null;
        let currentWindow = DEFAULT_WINDOW;
        let currentTrendWindow = DEFAULT_TREND_WINDOW;
        let currentTheme = 'dark';
        let activeUIRevision = CURRENT_UI_REVISION;
        let historyBusy = false;
        let trendBusy = false;
        let alarmsBusy = false;

        function clamp(v, min, max) { return Math.max(min, Math.min(max, v)); }
        function lerp(a, b, t) { return a + (b - a) * t; }
        function isLightTheme() { return currentTheme === 'light'; }

        function escapeHtml(value) {
            return String(value === undefined || value === null ? '' : value)
                .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
                .replace(/"/g, '&quot;').replace(/'/g, '&#39;');
        }

        function setTextById(id, text) {
            const el = document.getElementById(id);
            if (el) el.textContent = text;
        }

        function colorMix(c1, c2, t) {
            return { r: Math.round(lerp(c1.r, c2.r, t)), g: Math.round(lerp(c1.g, c2.g, t)), b: Math.round(lerp(c1.b, c2.b, t)) };
        }

        function colorToCss(c, a) {
            a = a === undefined ? 1 : a;
            return 'rgba(' + c.r + ',' + c.g + ',' + c.b + ',' + a + ')';
        }

        function polar(cx, cy, r, a) { return { x: cx + Math.cos(a) * r, y: cy + Math.sin(a) * r }; }

        function valueToAngle(value) {
            const ratio = clamp((Number(value) || 0) / GAUGE_MAX_PERCENT, 0, 1);
            return START_ANGLE + ratio * (END_ANGLE - START_ANGLE);
        }

        function prepCanvas(canvas) {
            const dpr = window.devicePixelRatio || 1;
            const rect = canvas.getBoundingClientRect();
            const w = Math.max(1, Math.floor(rect.width));
            const h = Math.max(1, Math.floor(rect.height));
            canvas.width = Math.max(1, Math.floor(w * dpr));
            canvas.height = Math.max(1, Math.floor(h * dpr));
            const ctx = canvas.getContext('2d');
            ctx.setTransform(1, 0, 0, 1, 0, 0);
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
            return { ctx, w, h };
        }

        function gaugeBandColor(v) {
            const green  = { r: 34,  g: 197, b: 94  };
            const yellow = { r: 234, g: 179, b: 8   };
            const red    = { r: 239, g: 68,  b: 68  };
            if (v <= WARNING_PERCENT) {
                const t = WARNING_PERCENT <= 0 ? 0 : v / WARNING_PERCENT;
                return colorMix(green, yellow, t * 0.15);
            }
            if (v <= CRITICAL_PERCENT) {
                const span = Math.max(1, CRITICAL_PERCENT - WARNING_PERCENT);
                return colorMix(green, yellow, (v - WARNING_PERCENT) / span);
            }
            const span = Math.max(1, GAUGE_MAX_PERCENT - CRITICAL_PERCENT);
            return colorMix(yellow, red, (v - CRITICAL_PERCENT) / span);
        }

        function drawArc(ctx, cx, cy, r, a1, a2, stroke, width, shadowBlur) {
            shadowBlur = shadowBlur || 0;
            ctx.save();
            ctx.beginPath();
            ctx.arc(cx, cy, r, a1, a2, false);
            ctx.strokeStyle = stroke;
            ctx.lineWidth = width;
            ctx.lineCap = 'butt';
            if (shadowBlur > 0) { ctx.shadowColor = stroke; ctx.shadowBlur = shadowBlur; }
            ctx.stroke();
            ctx.restore();
        }

        function drawColoredBand(ctx, cx, cy, r, width) {
            const segments = 180;
            for (let i = 0; i < segments; i++) {
                const v1 = (i / segments) * GAUGE_MAX_PERCENT;
                const v2 = ((i + 1) / segments) * GAUGE_MAX_PERCENT;
                const c = gaugeBandColor((v1 + v2) / 2);
                drawArc(ctx, cx, cy, r, valueToAngle(v1), valueToAngle(v2), colorToCss(c, 0.95), width, 0);
            }
        }

        function drawGauge(canvasId, percentValue, knValue, sideAccent) {
            if (!SHOW_GAUGES) return;
            const canvas = document.getElementById(canvasId);
            if (!canvas) return;
            const prep = prepCanvas(canvas);
            const ctx = prep.ctx, w = prep.w, h = prep.h;
            const light = isLightTheme();
            const cx = w / 2;
            const radius = Math.min(w * 0.35, h * 0.42);
            const cy = h * 0.58;
            const trackWidth = Math.max(20, radius * 0.17);
            const value = clamp(Number(percentValue) || 0, 0, GAUGE_MAX_PERCENT);
            const valueAngle = valueToAngle(value);

            ctx.save();
            ctx.beginPath();
            ctx.arc(cx, cy, radius + 24, 0, Math.PI * 2);
            ctx.fillStyle = light ? 'rgba(15,23,42,0.04)' : 'rgba(255,255,255,0.015)';
            ctx.shadowColor = light ? 'rgba(15,23,42,0.12)' : 'rgba(0,0,0,0.45)';
            ctx.shadowBlur = 32;
            ctx.fill();
            ctx.restore();

            drawArc(ctx, cx, cy, radius, START_ANGLE, END_ANGLE, light ? 'rgba(15,23,42,0.08)' : 'rgba(255,255,255,0.06)', trackWidth + 10, 0);
            drawColoredBand(ctx, cx, cy, radius, trackWidth);
            drawArc(ctx, cx, cy, radius, valueAngle, END_ANGLE, light ? 'rgba(255,255,255,0.72)' : 'rgba(9,9,11,0.60)', trackWidth - 1, 0);
            drawArc(ctx, cx, cy, radius, START_ANGLE, valueAngle, light ? 'rgba(15,23,42,0.05)' : 'rgba(255,255,255,0.04)', trackWidth - 1, 10);

            for (let v = 0; v <= GAUGE_MAX_PERCENT + 0.0001; v += 5) {
                const a = valueToAngle(v);
                const isMajor = Math.abs(v % 10) < 0.0001;
                const isThreshold = Math.abs(v - WARNING_PERCENT) < 0.0001 || Math.abs(v - CRITICAL_PERCENT) < 0.0001;
                const r1 = isThreshold ? radius * 0.66 : isMajor ? radius * 0.72 : radius * 0.80;
                const p1 = polar(cx, cy, r1, a);
                const p2 = polar(cx, cy, radius * 0.97, a);
                ctx.beginPath();
                ctx.moveTo(p1.x, p1.y);
                ctx.lineTo(p2.x, p2.y);
                if (isThreshold) {
                    ctx.strokeStyle = light ? '#0f172a' : '#ffffff';
                    ctx.lineWidth = 3.2;
                } else if (isMajor) {
                    ctx.strokeStyle = light ? 'rgba(15,23,42,0.80)' : 'rgba(255,255,255,0.86)';
                    ctx.lineWidth = 2.2;
                } else {
                    ctx.strokeStyle = light ? 'rgba(71,85,105,0.65)' : 'rgba(161,161,170,0.74)';
                    ctx.lineWidth = 1.1;
                }
                ctx.stroke();
            }

            ctx.textAlign = 'center';
            ctx.textBaseline = 'middle';
            ctx.fillStyle = light ? 'rgba(15,23,42,0.88)' : 'rgba(244,244,245,0.96)';
            ctx.font = '700 18px system-ui, sans-serif';
            for (const v of [0, 20, 40, 60, 80, 100, 120, 130]) {
                const p = polar(cx, cy, radius * 1.13, valueToAngle(v));
                ctx.fillText(String(v), p.x, p.y);
            }

            const tip   = polar(cx, cy, radius * 0.86, valueAngle);
            const left  = polar(cx, cy, 8, valueAngle + Math.PI / 2);
            const right = polar(cx, cy, 8, valueAngle - Math.PI / 2);
            const tail  = polar(cx, cy, radius * 0.20, valueAngle + Math.PI);
            ctx.save();
            ctx.beginPath();
            ctx.moveTo(left.x, left.y);
            ctx.lineTo(tip.x, tip.y);
            ctx.lineTo(right.x, right.y);
            ctx.lineTo(tail.x, tail.y);
            ctx.closePath();
            ctx.fillStyle = light ? '#0f172a' : '#ffffff';
            ctx.shadowColor = light ? 'rgba(15,23,42,0.12)' : 'rgba(255,255,255,0.18)';
            ctx.shadowBlur = 10;
            ctx.fill();
            ctx.restore();

            ctx.beginPath();
            ctx.arc(cx, cy, 14, 0, Math.PI * 2);
            ctx.fillStyle = light ? '#ffffff' : '#101114';
            ctx.fill();
            ctx.lineWidth = 3;
            ctx.strokeStyle = sideAccent;
            ctx.stroke();

            ctx.beginPath();
            ctx.arc(cx, cy, 4.5, 0, Math.PI * 2);
            ctx.fillStyle = light ? '#0f172a' : '#ffffff';
            ctx.fill();

            const centerPlateRadius = (radius * 0.72) - 18;
            ctx.beginPath();
            ctx.arc(cx, cy + 8, centerPlateRadius, 0, Math.PI * 2);
            ctx.fillStyle = light ? 'rgba(255,255,255,0.98)' : 'rgba(9,9,11,0.90)';
            ctx.fill();
            ctx.lineWidth = 1.2;
            ctx.strokeStyle = light ? 'rgba(15,23,42,0.12)' : 'rgba(255,255,255,0.10)';
            ctx.stroke();

            let valueFontPx = 58;
            if (value >= 100) valueFontPx = 50;
            if (w < 420) valueFontPx -= 6;

            ctx.textAlign = 'center';
            ctx.textBaseline = 'middle';
            ctx.fillStyle = light ? '#0f172a' : '#ffffff';
            ctx.font = '700 ' + valueFontPx + 'px system-ui, sans-serif';
            ctx.fillText(value.toFixed(1), cx, cy - 6);
            ctx.fillStyle = sideAccent;
            ctx.font = '700 19px system-ui, sans-serif';
            ctx.fillText(UNIT_PCT, cx, cy + 30);
            ctx.fillStyle = light ? '#334155' : '#a1a1aa';
            ctx.font = '600 17px system-ui, sans-serif';
            ctx.fillText((Number(knValue) || 0).toFixed(1) + ' ' + UNIT_FORCE, cx, cy + 58);
        }

        function updateDigitalDisplay(side, percent, kn) {
            if (!SHOW_GAUGE_DIGITAL) return;
            const el = document.getElementById('digital-' + side);
            if (!el) return;
            const pctEl = el.querySelector('.percent');
            const knEl  = el.querySelector('.kn');
            if (pctEl) pctEl.textContent = percent.toFixed(1);
            if (knEl)  knEl.textContent  = kn.toFixed(1) + ' ' + UNIT_FORCE;
        }

        function getZone(v) {
            if (v >= CRITICAL_PERCENT)  return 'critical';
            if (v >= WARNING_PERCENT)   return 'warning';
            return 'ok';
        }

        function getImbalanceZone(v) {
            if (v >= IMBALANCE_CRITICAL_PERCENT) return 'critical';
            if (v >= IMBALANCE_WARNING_PERCENT)  return 'warning';
            return 'ok';
        }

        function setProcessVisualState(connected) {
            const el = document.getElementById('process-content');
            if (!el) return;
            el.classList.toggle('process-offline', !connected);
            el.classList.toggle('process-online', connected);
        }

        function setConnectionIndicator(connected, stale) {
            const dot  = document.getElementById('dot');
            const text = document.getElementById('status-text');
            if (!dot || !text) return;
            if (!connected) {
                dot.className  = 'w-4 h-4 rounded-full bg-red-500 ring-4 ring-red-500/20';
                text.textContent = 'Disconnected';
                text.className = 'font-semibold text-lg text-red-400';
                return;
            }
            if (stale) {
                dot.className  = 'w-4 h-4 rounded-full bg-yellow-400 ring-4 ring-yellow-400/20';
                text.textContent = 'Stale';
                text.className = 'font-semibold text-lg text-yellow-300';
                return;
            }
            dot.className  = 'w-4 h-4 rounded-full bg-emerald-400 ring-4 ring-emerald-400/20';
            text.textContent = 'Connected';
            text.className = 'font-semibold text-lg text-emerald-400';
        }

        function applyChannelState(side, percentValue) {
            if (!SHOW_GAUGES) return;
            const zone = getZone(percentValue);
            const card  = document.getElementById('card-' + side);
            const led   = document.getElementById('led-' + side);
            const stTxt = document.getElementById('state-' + side);
            if (!card || !led || !stTxt) return;
            card.classList.remove('soft-glow-green', 'soft-glow-yellow', 'soft-glow-red');
            if (zone === 'critical') {
                led.className = 'w-6 h-6 bg-red-500 rounded-full shadow-lg shadow-red-600/50';
                stTxt.textContent = 'CRITICAL';
                stTxt.className = 'text-sm text-red-400 mt-1 font-semibold';
                card.classList.add('soft-glow-red');
            } else if (zone === 'warning') {
                led.className = 'w-6 h-6 bg-yellow-400 rounded-full shadow-lg shadow-yellow-500/50';
                stTxt.textContent = 'WARNING';
                stTxt.className = 'text-sm text-yellow-400 mt-1 font-semibold';
                card.classList.add('soft-glow-yellow');
            } else {
                led.className = 'w-6 h-6 bg-emerald-500 rounded-full shadow-lg shadow-emerald-600/40';
                stTxt.textContent = 'NORMAL';
                stTxt.className = 'text-sm text-emerald-400 mt-1 font-semibold';
                card.classList.add('soft-glow-green');
            }
        }

        function formatLastUpdate(isoString) {
            if (!isoString) return 'Last update: --:--:--.---';
            const d = new Date(isoString);
            if (isNaN(d.getTime())) return 'Last update: --:--:--.---';
            const hh = String(d.getHours()).padStart(2, '0');
            const mm = String(d.getMinutes()).padStart(2, '0');
            const ss = String(d.getSeconds()).padStart(2, '0');
            const ms = String(d.getMilliseconds()).padStart(3, '0');
            return 'Last update: ' + hh + ':' + mm + ':' + ss + '.' + ms;
        }

        function setSummaryCard(kind, zone, text, value) {
            if (!SHOW_SUMMARY_BAR) return;
            const card   = document.getElementById('summary-' + kind + '-card');
            const dot    = document.getElementById('summary-' + kind + '-dot');
            const status = document.getElementById('summary-' + kind + '-text');
            const val    = document.getElementById('summary-' + kind + '-value');
            if (!card || !dot || !status || !val) return;
            card.className   = 'summary-card ' + zone;
            dot.className    = 'summary-dot '  + zone;
            status.className = 'summary-status ' + zone + ' font-semibold mt-1';
            status.textContent = text;
            val.textContent    = value;
        }

        function setVerdict(zone, statusText, reasonText) {
            if (!SHOW_VERDICT) return;
            const card   = document.getElementById('verdict-card');
            const status = document.getElementById('verdict-status');
            const reason = document.getElementById('verdict-reason');
            if (!card || !status || !reason) return;
            card.className     = 'verdict-card ' + zone;
            status.textContent = statusText;
            reason.textContent = reasonText;
        }

        function updateSummaryBar(connected, stale, leftPct, rightPct, imbalance) {
            if (!SHOW_SUMMARY_BAR) return;
            if (!connected) {
                setSummaryCard('force',     'neutral',  'NO DATA', '--');
                setSummaryCard('imbalance', 'neutral',  'NO DATA', '--');
                setSummaryCard('plc',       'critical', 'OFFLINE', 'Disconnected');
                return;
            }
            const maxForce    = Math.max(leftPct, rightPct);
            const forceZone   = getZone(maxForce);
            const dominantSide = leftPct >= rightPct ? 'L' : 'R';
            const forceText   = forceZone === 'ok' ? 'OK' : forceZone === 'warning' ? 'WARNING' : 'CRITICAL';
            setSummaryCard('force', forceZone, forceText, 'Max ' + maxForce.toFixed(1) + UNIT_PCT + ' (' + dominantSide + ')');
            const imbZone  = getImbalanceZone(imbalance);
            const imbText  = imbZone === 'ok' ? 'OK' : imbZone === 'warning' ? 'WARNING' : 'CRITICAL';
            setSummaryCard('imbalance', imbZone, imbText, imbalance.toFixed(1) + UNIT_PCT);
            if (stale) {
                setSummaryCard('plc', 'warning', 'STALE', 'No fresh data');
            } else {
                setSummaryCard('plc', 'ok', 'OK', 'Online');
            }
        }

        function updateMachineVerdict(connected, stale, leftPct, rightPct, imbalance) {
            if (!SHOW_VERDICT) return;
            if (!connected) { setVerdict('critical', 'OFFLINE', 'No PLC communication'); return; }
            if (stale)      { setVerdict('warning', 'STALE DATA', 'PLC connected but no fresh values received'); return; }
            const leftCrit = leftPct >= CRITICAL_PERCENT;
            const rightCrit = rightPct >= CRITICAL_PERCENT;
            const imbCrit = imbalance >= IMBALANCE_CRITICAL_PERCENT;
            if (leftCrit || rightCrit || imbCrit) {
                const reasons = [];
                if (leftCrit)  reasons.push('left force critical');
                if (rightCrit) reasons.push('right force critical');
                if (imbCrit)   reasons.push('imbalance critical');
                setVerdict('critical', 'CRITICAL', reasons.join(' • '));
                return;
            }
            const leftWarn = leftPct >= WARNING_PERCENT;
            const rightWarn = rightPct >= WARNING_PERCENT;
            const imbWarn = imbalance >= IMBALANCE_WARNING_PERCENT;
            if (leftWarn || rightWarn || imbWarn) {
                const reasons = [];
                if (leftWarn)  reasons.push('left force warning');
                if (rightWarn) reasons.push('right force warning');
                if (imbWarn)   reasons.push('imbalance warning');
                setVerdict('warning', 'WARNING', reasons.join(' • '));
                return;
            }
            setVerdict('ok', 'OK', 'Production stable within configured force and imbalance limits');
        }

        function updateAlarmBanner(leftPct, rightPct, imbalancePct, connected, stale) {
            const banner = document.getElementById('alarm-banner');
            const text   = document.getElementById('alarm-text');
            if (!banner || !text) return;
            if (!connected) { text.textContent = 'CRITICAL ALARM ACTIVE • PLC OFFLINE'; banner.classList.remove('hidden'); return; }
            if (stale) { text.textContent = 'WARNING • PLC DATA STALE'; banner.classList.remove('hidden'); return; }
            const lc = leftPct >= CRITICAL_PERCENT;
            const rc = rightPct >= CRITICAL_PERCENT;
            const ic = imbalancePct >= IMBALANCE_CRITICAL_PERCENT;
            if (!lc && !rc && !ic) { banner.classList.add('hidden'); return; }
            const parts = [];
            if (lc && rc) parts.push('FORCE LEFT + RIGHT');
            else if (lc) parts.push('FORCE LEFT');
            else if (rc) parts.push('FORCE RIGHT');
            if (ic) parts.push('IMBALANCE');
            text.textContent = 'CRITICAL ALARM ACTIVE • ' + parts.join(' • ');
            banner.classList.remove('hidden');
        }

        function redrawGauges() {
            if (!SHOW_GAUGES || !latestData) return;
            const lPct = Number(latestData.sila_l)    || 0;
            const rPct = Number(latestData.sila_r)    || 0;
            const lKN  = Number(latestData.sila_l_kn) || 0;
            const rKN  = Number(latestData.sila_r_kn) || 0;
            drawGauge('gaugeL', lPct, lKN, '#22d3ee');
            drawGauge('gaugeR', rPct, rKN, '#c084fc');
        }

        function directionLabel(d) {
            const map = { rising:'↑ rising', falling:'↓ falling', worsening:'↑ worsening', improving:'↓ improving', stable:'→ stable' };
            return map[d] || 'No data';
        }

        function directionClass(d) {
            const map = { rising:'dir-up', falling:'dir-down', worsening:'dir-bad', improving:'dir-down', stable:'dir-flat' };
            return map[d] || 'dir-flat';
        }

        function trendZoneForForce(dir, delta) {
            if (dir === 'insufficient_data') return 'neutral';
            if (dir === 'stable') return 'ok';
            if (dir === 'rising') return Math.abs(delta) >= 8 ? 'critical' : 'warning';
            return 'ok';
        }

        function trendZoneForImbalance(dir, delta) {
            if (dir === 'insufficient_data') return 'neutral';
            if (dir === 'stable') return 'ok';
            if (dir === 'worsening') return Math.abs(delta) >= 4 ? 'critical' : 'warning';
            return 'ok';
        }

        function stabilityZone(s) {
            return { stable:'ok', caution:'warning', unstable:'critical' }[s] || 'neutral';
        }

        function setIntelCard(idPrefix, zone, valueText, subText) {
            if (!SHOW_INTELLIGENCE) return;
            const card  = document.getElementById(idPrefix + '-card');
            const value = document.getElementById(idPrefix + '-value');
            const sub   = document.getElementById(idPrefix + '-sub');
            if (!card || !value || !sub) return;
            card.className = 'intel-card ' + zone;
            value.innerHTML = valueText;
            sub.innerHTML   = subText;
        }

        function formatSource(source) {
            const map = { force_left:'LEFT', force_right:'RIGHT', imbalance:'IMBALANCE', plc:'PLC' };
            return map[source] || String(source || '').toUpperCase();
        }

        function formatValue(value) {
            const n = Number(value);
            if (!isFinite(n)) return '--';
            return n.toFixed(1) + UNIT_PCT;
        }

        function updateChartTheme() {
            if (!SHOW_TREND_CHART || !lineChart) return;
            const light = isLightTheme();
            const grid = light ? 'rgba(15,23,42,0.10)' : 'rgba(255,255,255,0.06)';
            const tick = light ? '#334155' : '#a1a1aa';
            const legend = light ? '#0f172a' : '#f4f4f5';
            const tooltipBg = light ? 'rgba(255,255,255,0.98)' : 'rgba(9,9,11,0.96)';
            const tooltipText = light ? '#0f172a' : '#f4f4f5';
            lineChart.options.scales.x.grid.color = grid;
            lineChart.options.scales.x.ticks.color = tick;
            lineChart.options.scales.y.grid.color = grid;
            lineChart.options.scales.y.ticks.color = tick;
            lineChart.options.plugins.legend.labels.color = legend;
            lineChart.options.plugins.tooltip.backgroundColor = tooltipBg;
            lineChart.options.plugins.tooltip.titleColor = tooltipText;
            lineChart.options.plugins.tooltip.bodyColor = tooltipText;
            lineChart.update('none');
        }

        async function checkUIRevision() {
            try {
                const res = await fetch('/api/ui-revision', { cache: 'no-store' });
                if (!res.ok) return;
                const d = await res.json();
                const revision = Number(d.revision) || 0;
                if (revision > activeUIRevision) { window.location.reload(); return; }
                activeUIRevision = revision;
            } catch (err) { console.warn('UI revision check error:', err); }
        }

        function computeStaleFromPayload(d, connected) {
            if (!connected) return false;
            if (typeof d.stale === 'boolean') return d.stale;
            if (!d.last_update) return false;

            const lastTs = new Date(d.last_update).getTime();
            if (isNaN(lastTs)) return false;

            const serverNowTs = d.server_time ? new Date(d.server_time).getTime() : Date.now();
            if (isNaN(serverNowTs)) return false;

            return (serverNowTs - lastTs) > STALE_MS;
        }

        async function fetchLiveData() {
            try {
                const res = await fetch('/api/data', { cache: 'no-store' });
                if (!res.ok) throw new Error('HTTP ' + res.status);
                const d = await res.json();
                latestData = d;

                const connected   = !!d.connected;
                const leftPercent = Number(d.sila_l)          || 0;
                const rightPercent= Number(d.sila_r)          || 0;
                const sumPercent  = Number(d.sum_percent)      || 0;
                const sumKN       = Number(d.sum_kn)           || 0;
                const imbalance   = Number(d.imbalance_percent)|| 0;
                const bias        = Number(d.bias_percent)     || 0;
                const leftKN      = Number(d.sila_l_kn)        || 0;
                const rightKN     = Number(d.sila_r_kn)        || 0;
                const stale       = computeStaleFromPayload(d, connected);

                setConnectionIndicator(connected, stale);
                setProcessVisualState(connected && !stale);

                if (SHOW_OVERVIEW) {
                    setTextById('sum-percent', sumPercent.toFixed(1));
                    setTextById('sum-kn',      sumKN.toFixed(1));
                    setTextById('imbalance-pct', imbalance.toFixed(1));
                    setTextById('bias-pct',    bias.toFixed(1));
                }

                setTextById('last-update',      formatLastUpdate(d.last_update));
                setTextById('dropped-samples',  String(d.dropped_samples || 0));
                setTextById('dropped-events',   String(d.dropped_events  || 0));

                applyChannelState('l', leftPercent);
                applyChannelState('r', rightPercent);
                updateDigitalDisplay('l', leftPercent, leftKN);
                updateDigitalDisplay('r', rightPercent, rightKN);

                updateSummaryBar(connected, stale, leftPercent, rightPercent, imbalance);
                updateMachineVerdict(connected, stale, leftPercent, rightPercent, imbalance);
                updateAlarmBanner(leftPercent, rightPercent, imbalance, connected, stale);
                redrawGauges();
            } catch (err) {
                console.warn('Live fetch error:', err);
                latestData = null;
                setConnectionIndicator(false, false);
                setProcessVisualState(false);
                updateSummaryBar(false, false, 0, 0, 0);
                updateMachineVerdict(false, false, 0, 0, 0);
                updateAlarmBanner(0, 0, 0, false, false);
                updateDigitalDisplay('l', 0, 0);
                updateDigitalDisplay('r', 0, 0);
            }
        }

        async function fetchHistory() {
            if (!SHOW_TREND_CHART || !lineChart || historyBusy) return;
            historyBusy = true;
            try {
                const res = await fetch('/api/history?window=' + encodeURIComponent(currentWindow), { cache: 'no-store' });
                if (!res.ok) throw new Error('HTTP ' + res.status);
                const d   = await res.json();
                const pts = Array.isArray(d.points) ? d.points : [];
                const labels  = pts.map(p => p.time);
                const dataL   = pts.map(p => p.sila_l);
                const dataR   = pts.map(p => p.sila_r);
                const warnLine = labels.map(() => WARNING_PERCENT);
                const critLine = labels.map(() => CRITICAL_PERCENT);
                lineChart.data.labels = labels;
                lineChart.data.datasets[0].data = dataL;
                lineChart.data.datasets[1].data = dataR;
                lineChart.data.datasets[2].data = warnLine;
                lineChart.data.datasets[3].data = critLine;
                lineChart.update('none');
            } catch (err) {
                console.warn('History fetch error:', err);
            } finally {
                historyBusy = false;
            }
        }

        async function fetchTrend() {
            if (!SHOW_INTELLIGENCE || trendBusy) return;
            trendBusy = true;
            try {
                const res = await fetch('/api/trend?window=' + encodeURIComponent(currentTrendWindow), { cache: 'no-store' });
                if (!res.ok) throw new Error('HTTP ' + res.status);
                const d = await res.json();
                const avgPeak5m   = Number(d.avg_peak_5m)         || 0;
                const avgPeak1h   = Number(d.avg_peak_1h)         || 0;
                const avgImb5m    = Number(d.avg_imbalance_5m)    || 0;
                const avgImb1h    = Number(d.avg_imbalance_1h)    || 0;
                const forceDelta  = Number(d.force_delta_pct)     || 0;
                const imbDelta    = Number(d.imbalance_delta_pct) || 0;
                const forceDir    = d.force_direction      || 'insufficient_data';
                const imbDir      = d.imbalance_direction  || 'insufficient_data';
                const stability   = d.process_stability    || 'insufficient_data';
                const stReason    = d.stability_reason     || 'No data';
                const forceStd    = Number(d.force_stddev)        || 0;
                const imbStd      = Number(d.imbalance_stddev)    || 0;
                const sampleCount = Number(d.sample_count)        || 0;
                const windowLabel = d.window || currentTrendWindow;

                setIntelCard('intel-avg5', getZone(avgPeak5m),
                    avgPeak5m.toFixed(1) + UNIT_PCT,
                    'Avg imbalance 5m: <span class="intel-kpi">' + avgImb5m.toFixed(1) + UNIT_PCT + '</span>');

                setIntelCard('intel-avg1h', getZone(avgPeak1h),
                    avgPeak1h.toFixed(1) + UNIT_PCT,
                    'Avg imbalance 1h: <span class="intel-kpi">' + avgImb1h.toFixed(1) + UNIT_PCT + '</span>');

                setIntelCard('intel-force', trendZoneForForce(forceDir, forceDelta),
                    (forceDelta >= 0 ? '+' : '') + forceDelta.toFixed(1) + UNIT_PCT,
                    '<span class="' + directionClass(forceDir) + ' font-semibold">' + directionLabel(forceDir) + '</span><br>Δ avg force over ' + windowLabel + ' • σ ' + forceStd.toFixed(2));

                setIntelCard('intel-imb', trendZoneForImbalance(imbDir, imbDelta),
                    (imbDelta >= 0 ? '+' : '') + imbDelta.toFixed(1) + UNIT_PCT,
                    '<span class="' + directionClass(imbDir) + ' font-semibold">' + directionLabel(imbDir) + '</span><br>Δ avg imbalance over ' + windowLabel + ' • σ ' + imbStd.toFixed(2));

                setIntelCard('intel-stability', stabilityZone(stability),
                    String(stability).toUpperCase(),
                    stReason + '<br><span class="mini-mono">Samples: ' + sampleCount + ' • Window: ' + windowLabel + '</span>');
            } catch (err) {
                console.warn('Trend fetch error:', err);
                ['intel-avg5','intel-avg1h','intel-force','intel-imb','intel-stability']
                    .forEach(id => setIntelCard(id, 'neutral', '--', 'No data'));
            } finally {
                trendBusy = false;
            }
        }

        async function fetchAlarms() {
            if (!SHOW_ALARM_TIMELINE || alarmsBusy) return;
            alarmsBusy = true;
            try {
                const res = await fetch('/api/alarms?limit=20', { cache: 'no-store' });
                if (!res.ok) throw new Error('HTTP ' + res.status);
                const d      = await res.json();
                const events = Array.isArray(d.events) ? d.events : [];
                const body   = document.getElementById('alarm-table-body');
                if (!body) return;
                if (events.length === 0) {
                    body.innerHTML = '<tr><td colspan="6" class="py-6 text-center text-zinc-500">No events yet</td></tr>';
                    return;
                }
                let html = '';
                for (const ev of events) {
                    const sev = String(ev.severity || 'info');
                    const val = ev.source === 'plc' ? '--' : formatValue(ev.value);
                    const lim = ev.limit > 0 ? formatValue(ev.limit) : '--';
                    html += '<tr class="border-b border-white/5">';
                    html += '<td class="py-3 pr-4 font-mono text-zinc-300 whitespace-nowrap">' + escapeHtml(ev.time || '--') + '</td>';
                    html += '<td class="py-3 pr-4"><span class="severity-pill ' + escapeHtml(sev) + '">' + escapeHtml(sev.toUpperCase()) + '</span></td>';
                    html += '<td class="py-3 pr-4 font-semibold text-zinc-200">' + escapeHtml(formatSource(ev.source)) + '</td>';
                    html += '<td class="py-3 pr-4 text-zinc-300">' + escapeHtml(ev.message || '--') + '</td>';
                    html += '<td class="py-3 pr-4 text-right font-mono text-zinc-200">' + escapeHtml(val) + '</td>';
                    html += '<td class="py-3 text-right font-mono text-zinc-400">' + escapeHtml(lim) + '</td>';
                    html += '</tr>';
                }
                body.innerHTML = html;
            } catch (err) {
                console.warn('Alarm fetch error:', err);
            } finally {
                alarmsBusy = false;
            }
        }

        function setActiveWindowButton(value) {
            document.querySelectorAll('.window-btn').forEach(btn =>
                btn.classList.toggle('active', btn.dataset.window === value));
        }

        function setActiveTrendWindowButton(value) {
            document.querySelectorAll('.trend-window-btn').forEach(btn =>
                btn.classList.toggle('active', btn.dataset.window === value));
        }

        function useWindow(value) {
            if (!SHOW_TREND_CHART) return;
            currentWindow = value;
            setActiveWindowButton(value);
            fetchHistory();
        }

        function useTrendWindow(value) {
            if (!SHOW_INTELLIGENCE) return;
            currentTrendWindow = value;
            setActiveTrendWindowButton(value);
            fetchTrend();
        }

        window.onload = () => {
            AppUI.initTheme({
                buttonId: 'theme-toggle',
                onChange: (theme) => {
                    currentTheme = theme;
                    updateChartTheme();
                    redrawGauges();
                }
            });
            if (SHOW_HEADER_CONTROLS) {
                AppUI.initFullscreen({ buttonId: 'fullscreen-toggle' });
            }

            setActiveWindowButton(DEFAULT_WINDOW);
            setActiveTrendWindowButton(DEFAULT_TREND_WINDOW);

            document.querySelectorAll('.window-btn').forEach(btn =>
                btn.addEventListener('click', () => useWindow(btn.dataset.window)));

            document.querySelectorAll('.trend-window-btn').forEach(btn =>
                btn.addEventListener('click', () => useTrendWindow(btn.dataset.window)));

            const applyWindowBtn = document.getElementById('apply-window');
            if (applyWindowBtn) {
                applyWindowBtn.addEventListener('click', () => {
                    const input = document.getElementById('custom-window');
                    const val = input ? input.value.trim() : '';
                    if (!val) return;
                    currentWindow = val;
                    document.querySelectorAll('.window-btn').forEach(btn => btn.classList.remove('active'));
                    fetchHistory();
                });
            }

            const applyTrendBtn = document.getElementById('apply-trend-window');
            if (applyTrendBtn) {
                applyTrendBtn.addEventListener('click', () => {
                    const input = document.getElementById('custom-trend-window');
                    const val = input ? input.value.trim() : '';
                    if (!val) return;
                    currentTrendWindow = val;
                    document.querySelectorAll('.trend-window-btn').forEach(btn => btn.classList.remove('active'));
                    fetchTrend();
                });
            }


            if (SHOW_TREND_CHART) {
                const chartCanvas = document.getElementById('lineChart');
                if (chartCanvas) {
                    lineChart = new Chart(chartCanvas, {
                        type: 'line',
                        data: {
                            labels: [],
                            datasets: [
                                { label: 'Levi peak %',   borderColor: '#22d3ee', backgroundColor: 'rgba(34,211,238,0.10)', borderWidth: 3, tension: 0.22, pointRadius: 0, data: [] },
                                { label: 'Desni peak %',  borderColor: '#c084fc', backgroundColor: 'rgba(192,132,252,0.10)', borderWidth: 3, tension: 0.22, pointRadius: 0, data: [] },
                                { label: 'Warning limit', borderColor: 'rgba(245,158,11,0.95)', borderWidth: 2, pointRadius: 0, borderDash: [8, 6], tension: 0, data: [] },
                                { label: 'Critical limit',borderColor: 'rgba(239,68,68,0.95)',  borderWidth: 2, pointRadius: 0, borderDash: [8, 6], tension: 0, data: [] }
                            ]
                        },
                        options: {
                            responsive: true,
                            maintainAspectRatio: false,
                            interaction: { mode: 'index', intersect: false },
                            animation: false,
                            scales: {
                                x: { grid: { color: 'rgba(255,255,255,0.06)' }, ticks: { color: '#a1a1aa', maxTicksLimit: 18 } },
                                y: { min: 0, max: GAUGE_MAX_PERCENT, grid: { color: 'rgba(255,255,255,0.06)' }, ticks: { color: '#a1a1aa', stepSize: 10 } }
                            },
                            plugins: {
                                legend: { position: 'top', labels: { color: '#f4f4f5' } },
                                tooltip: { backgroundColor: 'rgba(9,9,11,0.96)', titleColor: '#f4f4f5', bodyColor: '#f4f4f5' }
                            }
                        }
                    });
                    updateChartTheme();
                }
            }

            setProcessVisualState(false);

            fetchLiveData();
            fetchHistory();
            fetchTrend();
            fetchAlarms();
            checkUIRevision();

            setInterval(fetchLiveData,  POLL_MS);
            setInterval(checkUIRevision, 1200);
            if (SHOW_TREND_CHART)    setInterval(fetchHistory, Math.max(1500, POLL_MS * 3));
            if (SHOW_INTELLIGENCE)   setInterval(fetchTrend,   Math.max(2500, POLL_MS * 5));
            if (SHOW_ALARM_TIMELINE) setInterval(fetchAlarms,  2500);

            window.addEventListener('resize', redrawGauges);
        };
    </script>
</body>
</html>
