﻿// OP1 Operations Portal v1.0 — CDN build (React 18 UMD + Babel standalone, no build step)
// React hooks pulled from the global React object loaded by index.html
const { useState, useEffect, useCallback, useRef } = React;

// ═══════════════════════════════════════════════════════════════════════════
// OP1 Operations Portal v1.0 — Dashboard
// Role-based: Admin (full) | User/Operator | Viewer (read-only)
// ═══════════════════════════════════════════════════════════════════════════

const API_BASE = (typeof window !== "undefined" && window.OP1_API) || "";
const API = `${API_BASE}/api`;

// ── Color palette ──
const c = {
  bg: "#FFFFFF", surface: "#FFFFFF", surfaceAlt: "#F0F7FB",
  border: "#C0D8E8", borderLight: "#D4E8F4",
  text: "#1A1A1A", textMuted: "#4A4A4A", textDim: "#4A4A4A", textDimmer: "#6B6B6B",
  green: "#008C6F", yellow: "#E89A2E", red: "#D95C5C", blue: "#006D8C",
  cyan: "#006D8C", purple: "#5E5A9E", amber: "#E89A2E", teal: "#008C6F",
  greenLight: "#DCF2EA", yellowLight: "#FDF3E0", redLight: "#FAEAEA", blueLight: "#E0F2F7",
  sidebarBg: "#1E2B3C", sidebarText: "#F4F8FC", sidebarText2: "#FFFFFF",
  sidebarMuted: "#7A9AB8", sidebarActiveBg: "rgba(0,109,140,0.35)",
  sidebarActive: "#5DD4F4", sidebarBorder: "rgba(255,255,255,0.1)",
};

const TYPE_COLOR = { Server: c.blue, Application: c.purple, Database: c.amber, Website: c.teal, Synthetic: c.green };
const STATUS_COLOR = { healthy: c.green, warning: c.yellow, degraded: c.yellow, critical: c.red, unknown: "#9CA3AF", suppressed: "#9CA3AF" };
const TICKER_DURATION_SEC = 120;

// ── Bulk Add constants ──
const BULK_PROFILES = {
  win:  { label: 'Windows standard',    checks: [1,1,1,1,1,1, 0,0,0,0, 0,0,0,0,0, 0,0,0,0] },
  sql:  { label: 'SQL server standard', checks: [1,1,1,1,0,0, 0,0,0,0, 1,1,1,1,1, 0,0,0,0] },
  web:  { label: 'Web server',          checks: [1,0,0,0,0,0, 1,1,0,0, 0,0,0,0,0, 1,1,1,1] },
  '':   { label: '— select profile —',  checks: [0,0,0,0,0,0, 0,0,0,0, 0,0,0,0,0, 0,0,0,0] },
};
const BULK_CATS = [
  { id:'i', label:'Infrastructure', short:'Infra', color:'#005A75', borderColor:'#007A99',
    cols:['Ping','CPU','Mem','Disk','Net','Svcs'], indices:[0,1,2,3,4,5] },
  { id:'a', label:'Application',    short:'App',   color:'#3A2860', borderColor:'#5A3890',
    cols:['Svc avail','API','App log','Perf ctr'], indices:[6,7,8,9] },
  { id:'d', label:'Database',       short:'DB',    color:'#6A3000', borderColor:'#8A4000',
    cols:['DB conn','Sessions','Blocking','Log','Long qry'], indices:[10,11,12,13,14] },
  { id:'w', label:'Website',        short:'Web',   color:'#004A38', borderColor:'#006A50',
    cols:['HTTP/S','SSL','DNS','Content'], indices:[15,16,17,18] },
];
const BULK_TOTAL_COLS = 19;

// ── Bulk Add type definitions ──
const BULK_TYPE_DEFS = [
  { colIndex:0,  typeKey:'ping',     label:'Ping / ICMP',           cat:'i', unit:'ms',    defaults:{ warn:'500',  crit:'1000', freq:'60',   extra:'3'   }, extra:{ key:'retries',  label:'Retries'          } },
  { colIndex:1,  typeKey:'cpu',      label:'CPU utilization',        cat:'i', unit:'%',     defaults:{ warn:'80',   crit:'95',   freq:'60',   extra:'3'   }, extra:{ key:'sustained', label:'Sustained checks' } },
  { colIndex:2,  typeKey:'mem',      label:'Memory utilization',     cat:'i', unit:'%',     defaults:{ warn:'85',   crit:'95',   freq:'60',   extra:''    }, extra:null },
  { colIndex:3,  typeKey:'disk',     label:'Disk space',             cat:'i', unit:'%',     defaults:{ warn:'80',   crit:'90',   freq:'300',  extra:''    }, extra:null },
  { colIndex:4,  typeKey:'net',      label:'Network interface',      cat:'i', unit:'%',     defaults:{ warn:'80',   crit:'95',   freq:'60',   extra:''    }, extra:null },
  { colIndex:5,  typeKey:'svcs',     label:'Windows services',       cat:'i', unit:'',      defaults:{ warn:'',     crit:'',     freq:'60',   extra:''    }, extra:null },
  { colIndex:6,  typeKey:'svcavail', label:'Service availability',   cat:'a', unit:'ms',    defaults:{ warn:'500',  crit:'2000', freq:'60',   extra:''    }, extra:null },
  { colIndex:7,  typeKey:'api',      label:'API endpoint',           cat:'a', unit:'ms',    defaults:{ warn:'2000', crit:'5000', freq:'60',   extra:''    }, extra:null },
  { colIndex:8,  typeKey:'applog',   label:'Application log',        cat:'a', unit:'',      defaults:{ warn:'',     crit:'',     freq:'120',  extra:''    }, extra:null },
  { colIndex:9,  typeKey:'perfctr',  label:'Perf counter',           cat:'a', unit:'',      defaults:{ warn:'',     crit:'',     freq:'60',   extra:''    }, extra:null },
  { colIndex:10, typeKey:'dbconn',   label:'DB connectivity',        cat:'d', unit:'ms',    defaults:{ warn:'500',  crit:'2000', freq:'60',   extra:'30'  }, extra:{ key:'timeout',  label:'Timeout (sec)'   } },
  { colIndex:11, typeKey:'sessions', label:'Session count',          cat:'d', unit:'',      defaults:{ warn:'80',   crit:'150',  freq:'120',  extra:''    }, extra:null },
  { colIndex:12, typeKey:'blocking', label:'Blocking / locking',     cat:'d', unit:'',      defaults:{ warn:'1',    crit:'5',    freq:'60',   extra:''    }, extra:null },
  { colIndex:13, typeKey:'log',      label:'Log space',              cat:'d', unit:'%',     defaults:{ warn:'80',   crit:'90',   freq:'300',  extra:''    }, extra:null },
  { colIndex:14, typeKey:'longqry',  label:'Long-running queries',   cat:'d', unit:'sec',   defaults:{ warn:'30',   crit:'60',   freq:'120',  extra:''    }, extra:null },
  { colIndex:15, typeKey:'http',     label:'HTTP / HTTPS',           cat:'w', unit:'ms',    defaults:{ warn:'2000', crit:'5000', freq:'60',   extra:'200' }, extra:{ key:'status',   label:'Expected status' } },
  { colIndex:16, typeKey:'ssl',      label:'SSL certificate',        cat:'w', unit:'days',  defaults:{ warn:'80',   crit:'30',   freq:'3600', extra:''    }, extra:null },
  { colIndex:17, typeKey:'dns',      label:'DNS resolution',         cat:'w', unit:'ms',    defaults:{ warn:'500',  crit:'2000', freq:'300',  extra:''    }, extra:null },
  { colIndex:18, typeKey:'content',  label:'Content match',          cat:'w', unit:'',      defaults:{ warn:'',     crit:'',     freq:'300',  extra:''    }, extra:null },
];
const BULK_CAT_META = {
  i: { label:'Infrastructure', color:'#007A99', badgeBg:'#E0F2F7', badgeColor:'#005A75', border:'#006D8C' },
  a: { label:'Application',    color:'#5A3890', badgeBg:'#EDE8FF', badgeColor:'#3A2880', border:'#5A3890' },
  d: { label:'Database',       color:'#8A4000', badgeBg:'#FFF0DC', badgeColor:'#7A3800', border:'#8A4000' },
  w: { label:'Website',        color:'#006A50', badgeBg:'#E0FFF4', badgeColor:'#004A38', border:'#006A50' },
};
function buildThresholdGroups(rows) {
  const groups = [];
  for (const td of BULK_TYPE_DEFS) {
    const servers = [];
    rows.forEach((r, ri) => {
      if ((r.checks || [])[td.colIndex]) {
        servers.push({ serverName: r.name || '', ip: r.ip || '', rowIndex: ri });
      }
    });
    if (servers.length > 0) {
      const m = BULK_CAT_META[td.cat];
      groups.push({ ...td, catLabel: m.label, catColor: m.color, catBadgeBg: m.badgeBg, catBadgeColor: m.badgeColor, catBorder: m.border, servers });
    }
  }
  return groups;
}
function initThresholds(groups) {
  const out = {};
  for (const g of groups) {
    const d = g.defaults;
    const entry = { ctrl: { warn: d.warn, crit: d.crit, freq: d.freq, extra: d.extra } };
    g.servers.forEach((s, si) => { entry[si] = { warn: d.warn, crit: d.crit, freq: d.freq, extra: d.extra }; });
    out[g.typeKey] = entry;
  }
  return out;
}
const BULK_SUBTYPE_MAP = {
  ping:     'infra-ping',
  cpu:      'infra-cpu',
  mem:      'infra-mem',
  disk:     'infra-disk',
  net:      'infra-net',
  svcs:     'infra-svc',
  svcavail: 'app-port',
  api:      'app-http',
  applog:   'app-log',
  perfctr:  'app-perfctr',
  dbconn:   'db-connectivity',
  sessions: 'db-session-count',
  blocking: 'db-blocking',
  log:      'db-log-space',
  longqry:  'db-longquery',
  http:     'web-http',
  ssl:      'web-ssl',
  dns:      'web-dns',
  content:  'web-content',
};
const BULK_MONITOR_TYPE_MAP = { i:'Server', a:'Application', d:'Database', w:'Website' };

// ── Help content ──
const HELP_CONTENT = {
  'server-health': {
    title: 'OP1 Server Health',
    sections: [
      {
        heading: 'CPU',
        text: 'Shows the percentage of total server CPU capacity used by the OP1 monitoring process. Measured across all CPU cores — on a 14-core machine, 1% here equals roughly 14% of a single core. Green below 70%, amber 71–85%, red above 85%.'
      },
      {
        heading: 'System Memory',
        text: 'Total RAM usage across the entire server — all running applications combined, not just OP1. If this is consistently red, consider closing other applications or upgrading server RAM.'
      },
      {
        heading: 'App Memory',
        text: 'RAM used by the OP1 process specifically (WorkingSet). This should match the memory shown for OpsPortal.Api.exe in Windows Task Manager → Details tab. Informational only — does not affect the status indicator.'
      },
      {
        heading: 'Disk',
        text: 'Percentage of disk space used on the server drive where OP1 is installed. Monitor check history and logs are stored here — if this reaches red, consider adjusting data retention settings in Config → Retention.'
      },
      {
        heading: 'Database',
        text: 'Response time of a lightweight ping (SELECT 1) to the OpsPortal SQL Server database. Green = connected under 500ms. Amber = connected but slow over 500ms. Red = unreachable. If red, monitor checks will fail to record results.'
      },
      {
        heading: 'Checks/min',
        text: 'Number of monitor checks executed in the last 60 seconds by the OP1 scheduler. A healthy platform typically shows 1 check per minute per active monitor.'
      }
    ],
    learnMore: null
  },
  'source-monitor': {
    title: 'Source Monitor',
    sections: [
      {
        heading: 'What is Source Monitor?',
        text: 'Identifies which OP1 instance created and monitors this check. When multiple OP1 instances share the same database (e.g. development and production servers), Source Monitor lets you see exactly which machine owns each monitor and generated each result.'
      },
      {
        heading: 'Multiple instances',
        text: 'Each OP1 server automatically registers itself using its Windows machine name. You can assign a friendly alias (e.g. "Production Server") in Config → Source Monitor.'
      },
      {
        heading: 'Filtering',
        text: 'Use the Source Monitor filter on the Logs and Alerts screens to view results from a specific machine only.'
      }
    ],
    learnMore: null,
    position: { left: 500, top: 120 }
  }
};

// ── UTC timestamp parser ──
function parseUTC(ts) {
  if (!ts) return null;
  const s = String(ts);
  return new Date(s.endsWith('Z') || s.includes('+') ? s : s + 'Z');
}

// ── Subtype → primary metric metadata ──
function primaryMetric(subtype) {
  const s = (subtype || '').toLowerCase();
  if (s === 'infra-cpu')                          return { label:'Avg CPU',      unit:'%',    kpiLabel:'AVG CPU' };
  if (s === 'infra-mem')                          return { label:'Avg Memory',   unit:'%',    kpiLabel:'AVG MEMORY' };
  if (s === 'infra-disk' || s === 'infra-diskio') return { label:'Avg Disk',     unit:'%',    kpiLabel:'AVG DISK' };
  if (s === 'infra-net')                          return { label:'Network',       unit:'Mbps', kpiLabel:'AVG NETWORK' };
  if (s === 'infra-ping')                         return { label:'Avg RTT',       unit:'ms',   kpiLabel:'AVG RTT' };
  if (s === 'infra-tcp' || s === 'app-port')      return { label:'Avg Connect',   unit:'ms',   kpiLabel:'AVG CONNECT' };
  if (s === 'web-ssl')                            return { label:'Days Left',     unit:'d',    kpiLabel:'AVG DAYS' };
  if (s === 'web-dns')                            return { label:'Resolve Time',  unit:'ms',   kpiLabel:'AVG RESOLVE' };
  if (s === 'db-query' || s === 'db-sqlctr')      return { label:'Avg Query',     unit:'ms',   kpiLabel:'AVG QUERY' };
  return { label:'Avg Response', unit:'ms', kpiLabel:'AVG RESPONSE' };
}

// ── Utility Components ──
const StatusDot = ({ status }) => (
  <span style={{ display: "inline-flex", alignItems: "center", gap: 6 }}>
    <span style={{ width: 8, height: 8, borderRadius: "50%", background: STATUS_COLOR[status] || STATUS_COLOR.unknown, boxShadow: status === "critical" ? `0 0 0 3px rgba(239,68,68,0.4)` : "none", animation: status === "critical" ? "pulse 1.5s infinite" : "none" }} />
    <span style={{ textTransform: "uppercase", fontSize: 10, fontWeight: 700, letterSpacing: "0.08em", color: STATUS_COLOR[status] || STATUS_COLOR.unknown }}>{status}</span>
  </span>
);

const Badge = ({ children, color = "#9CA3AF" }) => (
  <span style={{ display: "inline-block", padding: "2px 8px", borderRadius: 4, fontSize: 10, fontWeight: 600, background: color + "22", color, border: `1px solid ${color}35`, fontFamily: "mono", whiteSpace: "nowrap" }}>{children}</span>
);

const SevBadge = ({ severity }) => {
  const m = { critical: c.red, warning: c.yellow, info: c.blue, low: c.teal };
  return <Badge color={m[severity] || "#6b7280"}>{severity}</Badge>;
};

const TypeBadge = ({ type }) => <Badge color={TYPE_COLOR[type] || c.textDim}>{type}</Badge>;

const Card = ({ children, style = {}, ...rest }) => (
  <div style={{ background: c.surface, border: `1px solid ${c.border}`, borderRadius: 10, padding: 20, ...style }} {...rest}>{children}</div>
);

const MetricCard = ({ label, value, sub, accent = c.blue }) => (
  <Card style={{ textAlign: "center", flex: "1 1 130px", minWidth: 120, borderTop: `2px solid ${accent}` }}>
    <div style={{ fontSize: 10, color: c.textDim, fontWeight: 600, letterSpacing: "0.06em", textTransform: "uppercase", marginBottom: 6 }}>{label}</div>
    <div style={{ fontSize: 24, fontWeight: 800, color: c.text, fontFamily: "mono" }}>{value ?? "—"}</div>
    {sub && <div style={{ fontSize: 10, color: c.textDimmer, marginTop: 4 }}>{sub}</div>}
  </Card>
);

const WmiCombo = ({ label, hint, hintInline, value, onChange, discoveryType, host, credentialId, api }) => {
  const [open,    setOpen]    = React.useState(false);
  const [items,   setItems]   = React.useState([]);
  const [loading, setLoading] = React.useState(false);
  const [error,   setError]   = React.useState(null);
  const [filter,  setFilter]  = React.useState("");
  const wrapRef = React.useRef(null);

  const canDiscover = host && credentialId && parseInt(credentialId) > 0;

  const colDefs = {
    processes:  [{ k:"name", lbl:"Name", flex:2 }, { k:"pid",  lbl:"PID",     flex:1, mono:true }, { k:"cpu", lbl:"CPU", flex:1 }, { k:"memory", lbl:"Memory", flex:1 }],
    services:   [{ k:"name", lbl:"Service name", flex:1, mono:true }, { k:"displayName", lbl:"Display name", flex:2 }, { k:"state", lbl:"State", flex:1 }, { k:"startMode", lbl:"Start", flex:1 }],
    drives:     [{ k:"device", lbl:"Device", flex:1 }, { k:"label", lbl:"Label", flex:1 }, { k:"size", lbl:"Size", flex:1 }, { k:"free", lbl:"Free", flex:1 }, { k:"usedPct", lbl:"Used", flex:1 }],
    interfaces: [{ k:"name", lbl:"Interface name", flex:3 }, { k:"speed", lbl:"Speed", flex:1 }, { k:"mac", lbl:"MAC", flex:2, mono:true }],
    eventlogs:  [{ k:"name", lbl:"Channel", flex:3 }, { k:"entries", lbl:"Entries", flex:1, mono:true }],
  };

  const cols = colDefs[discoveryType] || colDefs.processes;

  const statusColor = s => s === "running" || s === "connected" || s === "healthy" ? "#008C6F" : s === "stopped" || s === "disconnected" ? "#E89A2E" : "#9CA3AF";

  const fetchItems = async () => {
    if (!canDiscover) return;
    setLoading(true); setError(null); setItems([]);
    try {
      const data = await api("GET", `/discover?type=${discoveryType}&host=${encodeURIComponent(host)}&credentialId=${credentialId}`);
      setItems(Array.isArray(data) ? data : []);
    } catch(e) {
      setError(e?.message || "Discovery failed");
    } finally {
      setLoading(false);
    }
  };

  const handleOpen = () => {
    if (!canDiscover) return;
    setOpen(o => {
      if (!o) { setFilter(""); fetchItems(); }
      return !o;
    });
  };

  const handleSelect = item => {
    onChange(item.value);
    setOpen(false);
  };

  React.useEffect(() => {
    const handler = e => { if (wrapRef.current && !wrapRef.current.contains(e.target)) setOpen(false); };
    document.addEventListener("mousedown", handler);
    return () => document.removeEventListener("mousedown", handler);
  }, []);

  const filtered = items.filter(item =>
    !filter || (item.value || "").toLowerCase().includes(filter.toLowerCase()) ||
    (item.displayName || "").toLowerCase().includes(filter.toLowerCase())
  );

  const gridCols = `14px ${cols.map(c => `${c.flex}fr`).join(" ")}`;

  return (
    <div ref={wrapRef} style={{ display:"flex", flexDirection:"column", gap:4, position:"relative" }}>
      {label && <label style={{ fontSize:11, color:c.textMuted, fontWeight:600, letterSpacing:"0.05em", textTransform:"uppercase", display:"flex", alignItems:"baseline", gap:8 }}>
        {label}
        {hintInline && <span style={{ fontSize:10, fontWeight:400, color:"#94a3b8", textTransform:"none", letterSpacing:0, fontStyle:"italic" }}>{hintInline}</span>}
      </label>}
      <div style={{ position:"relative" }}>
        <input
          readOnly
          value={value || ""}
          placeholder={canDiscover ? "Click to browse..." : "Enter host + credential first"}
          onClick={handleOpen}
          style={{ background:c.surface, border:`1px solid ${open ? "#006D8C" : c.border}`, borderRadius:open?"6px 6px 0 0":6, padding:"8px 28px 8px 12px", color:value?c.text:c.textDimmer, fontSize:13, fontFamily:"inherit", outline:"none", width:"100%", cursor:canDiscover?"pointer":"default" }}
        />
        <span style={{ position:"absolute", right:10, top:"50%", transform:"translateY(-50%)", fontSize:10, color:c.textDim, pointerEvents:"none" }}>{open ? "▲" : "▼"}</span>
      </div>
      {open && (
        <div style={{ position:"absolute", top:"100%", left:0, right:0, zIndex:200, background:c.surface, border:`1px solid #006D8C`, borderTop:"none", borderRadius:"0 0 6px 6px", overflow:"hidden" }}>
          <div style={{ display:"grid", gridTemplateColumns:gridCols, gap:"0 8px", padding:"4px 8px", background:c.bg, borderBottom:`1px solid ${c.border}` }}>
            <span></span>
            {cols.map(col => (
              <span key={col.k} style={{ fontSize:10, color:c.textDim, textTransform:"uppercase", letterSpacing:"0.04em", fontWeight:600 }}>{col.lbl}</span>
            ))}
          </div>
          <div style={{ maxHeight:192, overflowY:"auto" }}>
            {loading && (
              <div style={{ padding:"16px 12px", textAlign:"center", fontSize:12, color:c.textDim }}>Connecting to {host}...</div>
            )}
            {error && (
              <div style={{ padding:"12px", fontSize:11, color:"#D95C5C", background:"#FFF5F5" }}>{error}</div>
            )}
            {!loading && !error && filtered.length === 0 && (
              <div style={{ padding:"12px", fontSize:12, color:c.textDim, textAlign:"center" }}>No items found</div>
            )}
            {!loading && filtered.map((item, i) => (
              <div key={i} onClick={() => handleSelect(item)}
                style={{ display:"grid", gridTemplateColumns:gridCols, gap:"0 8px", padding:"3px 8px", alignItems:"center", cursor:"pointer", borderBottom:`1px solid ${c.border}`, background: item.value === value ? "#E0F2F7" : "transparent" }}
                onMouseEnter={e => { if(item.value !== value) e.currentTarget.style.background = c.bg; }}
                onMouseLeave={e => { if(item.value !== value) e.currentTarget.style.background = "transparent"; }}
              >
                <span style={{ width:6, height:6, borderRadius:"50%", background:statusColor(item.status), display:"inline-block" }}></span>
                {cols.map(col => (
                  <span key={col.k} style={{ fontSize:11, color:col.mono?c.textDim:c.text, fontFamily:col.mono?"monospace":"inherit", whiteSpace:"nowrap", overflow:"hidden", textOverflow:"ellipsis" }}>
                    {item[col.k] ?? ""}
                  </span>
                ))}
              </div>
            ))}
          </div>
          <div style={{ display:"flex", justifyContent:"space-between", alignItems:"center", padding:"5px 8px", borderTop:`1px solid ${c.border}`, background:c.bg }}>
            <span style={{ fontSize:10, color:c.textDim }}>{loading ? "Loading..." : `${filtered.length} item${filtered.length!==1?"s":""}`}</span>
            <input
              type="text"
              placeholder="Filter..."
              value={filter}
              onChange={e => setFilter(e.target.value)}
              onClick={e => e.stopPropagation()}
              style={{ fontSize:11, padding:"2px 7px", border:`1px solid ${c.border}`, borderRadius:4, background:c.surface, color:c.text, width:110, outline:"none" }}
            />
          </div>
        </div>
      )}
      {hint && <div style={{ fontSize:11, color:c.textDimmer, marginTop:1 }}>{hint}</div>}
    </div>
  );
};

const Input = ({ label, hint, hintInline, ...p }) => (
  <div style={{ display: "flex", flexDirection: "column", gap: 4 }}>
    {label && <label style={{ fontSize: 11, color: c.textMuted, fontWeight: 600, letterSpacing: "0.05em", textTransform: "uppercase", display:"flex", alignItems:"baseline", gap:8 }}>
      {label}
      {hintInline && <span style={{ fontSize:10, fontWeight:400, color:"#94a3b8", textTransform:"none", letterSpacing:0, fontStyle:"italic" }}>{hintInline}</span>}
    </label>}
    <input {...p} style={{ background: c.surface, border: `1px solid ${c.border}`, borderRadius: 6, padding: "8px 12px", color: c.text, fontSize: 13, fontFamily: "inherit", outline: "none", width: "100%", ...p.style }} />
    {hint && <div style={{ fontSize: 11, color: c.textDimmer, marginTop: 1 }}>{hint}</div>}
  </div>
);

const PasswordInput = ({ label, value, onChange, placeholder, onKeyDown, autoComplete, style }) => {
  const [show, setShow] = useState(false);
  return (
    <div style={{ display:"flex", flexDirection:"column", gap:4, ...style }}>
      {label && <label style={{ fontSize:11, color:c.textMuted, fontWeight:600, letterSpacing:"0.05em", textTransform:"uppercase" }}>{label}</label>}
      <div style={{ position:"relative" }}>
        <input
          type={show ? "text" : "password"}
          value={value}
          onChange={onChange}
          onKeyDown={onKeyDown}
          autoComplete={autoComplete}
          placeholder={placeholder || ""}
          style={{ background:c.surface, border:`1px solid ${c.border}`, borderRadius:6, padding:"8px 40px 8px 12px", color:c.text, fontSize:13, fontFamily:"inherit", outline:"none", width:"100%", boxSizing:"border-box" }}
        />
        <button
          type="button"
          onClick={() => setShow(s => !s)}
          tabIndex={-1}
          style={{ position:"absolute", right:8, top:"50%", transform:"translateY(-50%)", background:"none", border:"none", cursor:"pointer", padding:0, color:"#94a3b8", fontSize:11, lineHeight:1 }}
        >{show ? "Hide" : "Show"}</button>
      </div>
    </div>
  );
};

const Sel = ({ label, options, ...p }) => (
  <div style={{ display: "flex", flexDirection: "column", gap: 4 }}>
    {label && <label style={{ fontSize: 11, color: c.textMuted, fontWeight: 600, letterSpacing: "0.05em", textTransform: "uppercase" }}>{label}</label>}
    <select {...p} style={{ background: c.surface, border: `1px solid ${c.border}`, borderRadius: 6, padding: "8px 12px", color: c.text, fontSize: 13, fontFamily: "inherit", outline: "none", ...p.style }}>
      {options.map(o => <option key={o.value ?? o} value={o.value ?? o}>{o.label ?? o}</option>)}
    </select>
  </div>
);

const Btn = ({ children, variant = "primary", loading, ...p }) => {
  const s = {
    primary:   { background: c.blue,        color: "#fff",  border: "none" },
    secondary: { background: c.surface,     color: c.text,  border: `1px solid ${c.border}` },
    danger:    { background: "transparent", color: c.red,   border: `1px solid ${c.red}` },
    ghost:     { background: "transparent", color: c.textDim,   border: "none" },
    success:   { background: c.green,       color: "#fff",  border: "none" },
    warning:   { background: c.amber,       color: "#fff",  border: "none" },
  };
  return (
    <button {...p} disabled={loading || p.disabled}
      style={{ ...s[variant], borderRadius: 6, padding: "8px 16px", fontSize: 12, fontWeight: 700,
               cursor: loading || p.disabled ? "not-allowed" : "pointer",
               opacity: loading || p.disabled ? 0.5 : 1, transition: "opacity 0.15s", ...p.style }}>
      {loading ? "…" : children}
    </button>
  );
};

const SectionHead = ({ children }) => (
  <h3 style={{ fontSize: 11, fontWeight: 700, color: c.blue, letterSpacing: "0.08em", textTransform: "uppercase", marginBottom: 14 }}>{children}</h3>
);

const Spinner = () => (
  <div style={{ display: "flex", alignItems: "center", justifyContent: "center", padding: 40, color: c.textDim, fontSize: 12 }}>Loading…</div>
);

const ErrBox = ({ msg }) => msg ? (
  <div style={{ background: `${c.red}18`, border: `1px solid ${c.red}40`, borderRadius: 8, padding: "10px 14px", color: c.red, fontSize: 12, marginBottom: 12 }}>{msg}</div>
) : null;

const OkBox = ({ msg }) => msg ? (
  <div style={{ background: `${c.green}18`, border: `1px solid ${c.green}40`, borderRadius: 8, padding: "10px 14px", color: c.green, fontSize: 12, marginBottom: 12 }}>{msg}</div>
) : null;

// ── HelpIcon — ? circle trigger for contextual help popovers ──
const HelpIcon = ({ hkey, setHelpKey, theme = "dark" }) => {
  const [hov, setHov] = useState(false);
  const borderColor = theme === "light"
    ? (hov ? "#7A9AB8" : "#C0D8E8")
    : (hov ? "rgba(255,255,255,1)" : "rgba(255,255,255,0.6)");
  const textColor = theme === "light"
    ? (hov ? "#006D8C" : "#7A9AB8")
    : (hov ? "#FFFFFF" : "rgba(255,255,255,0.75)");
  return (
    <span
      onMouseDown={e => e.stopPropagation()}
      onClick={() => setHelpKey(k => k === hkey ? null : hkey)}
      onMouseEnter={() => setHov(true)}
      onMouseLeave={() => setHov(false)}
      style={{
        width: 13, height: 13, borderRadius: "50%",
        background: "transparent",
        border: `1px solid ${borderColor}`,
        color: textColor,
        fontSize: 9, fontWeight: 600,
        display: "inline-flex", alignItems: "center", justifyContent: "center",
        cursor: "pointer", marginLeft: 5, flexShrink: 0, userSelect: "none",
      }}
    >?</span>
  );
};

// ── HelpPopover — contextual help panel, anchored via fixed positioning ──
const HelpPopover = ({ helpKey, setHelpKey }) => {
  const panelRef = useRef(null);
  const content = helpKey ? HELP_CONTENT[helpKey] : null;

  useEffect(() => {
    if (!helpKey) return;
    const onDown = e => {
      if (panelRef.current && !panelRef.current.contains(e.target)) setHelpKey(null);
    };
    const onKey = e => { if (e.key === "Escape") setHelpKey(null); };
    document.addEventListener("mousedown", onDown);
    document.addEventListener("keydown", onKey);
    return () => {
      document.removeEventListener("mousedown", onDown);
      document.removeEventListener("keydown", onKey);
    };
  }, [helpKey]);

  if (!content) return null;
  const pos = content.position || { left: 225, bottom: 140 };
  return (
    <div ref={panelRef} style={{
      position: "fixed", ...pos,
      width: 280, background: "#FFFFFF",
      border: "1px solid #C0D8E8", borderRadius: 8,
      boxShadow: "0 4px 12px rgba(0,0,0,0.12)",
      padding: "14px 16px", zIndex: 1000,
      animation: "fadeIn 0.15s ease",
    }}>
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start", marginBottom: 10 }}>
        <span style={{ fontSize: 13, fontWeight: 700, color: "#1A1A1A" }}>{content.title}</span>
        <button onClick={() => setHelpKey(null)} style={{
          background: "none", border: "none", cursor: "pointer",
          color: "#7A9AB8", fontSize: 16, lineHeight: 1, padding: "0 0 0 8px", flexShrink: 0,
        }}>×</button>
      </div>
      {content.sections.map((s, i) => (
        <div key={i}>
          {i > 0 && <div style={{ height: 1, background: "#F0F7FB", margin: "8px 0" }} />}
          <div style={{ fontSize: 11, fontWeight: 700, color: "#1A1A1A", marginBottom: 3 }}>{s.heading}</div>
          <div style={{ fontSize: 12, color: "#4A4A4A", lineHeight: 1.6 }}>{s.text}</div>
        </div>
      ))}
      {content.learnMore && (
        <div style={{ marginTop: 10, paddingTop: 8, borderTop: "1px solid #F0F7FB" }}>
          <a href={content.learnMore} target="_blank" rel="noreferrer"
            style={{ fontSize: 11, color: "#006D8C", textDecoration: "none" }}>Learn more →</a>
        </div>
      )}
    </div>
  );
};

// ── CollapsibleSection ──
function CollapsibleSection({ title, defaultOpen = true, alwaysOpen = false, hideTitle = false, children }) {
  const [open, setOpen] = useState(defaultOpen);
  if (alwaysOpen) {
    return (
      <div style={{ marginBottom: hideTitle ? 6 : 14 }}>
        {!hideTitle && (
          <div style={{ padding: "8px 0 4px 0" }}>
            <span className="cs-title" style={{ fontSize: 11, fontWeight: 700, color: c.blue, letterSpacing: "0.08em", textTransform: "uppercase" }}>{title}</span>
          </div>
        )}
        {children}
      </div>
    );
  }
  return (
    <div style={{ marginBottom: 18 }}>
      <button onClick={() => setOpen(o => !o)} className="cs-btn"
        style={{ background: "none", border: "none", cursor: "pointer", display: "flex", alignItems: "center", gap: 6, padding: "0 0 8px 0", width: "100%" }}>
        <span className="cs-title" style={{ fontSize: 11, fontWeight: 700, color: c.blue, letterSpacing: "0.08em", textTransform: "uppercase" }}>{title}</span>
        <span style={{ fontSize: 10, color: c.textDimmer, marginLeft: "auto" }}>{open ? "▲" : "▼"}</span>
      </button>
      {open && children}
    </div>
  );
}

// ── GroupAutocomplete ──
function GroupAutocomplete({ value, groups, onChange, tabIndex }) {
  // value = group name string; onChange(name)
  const [open, setOpen] = useState(false);
  const [input, setInput] = useState(value || "");
  const ref = useRef(null);

  useEffect(() => { setInput(value || ""); }, [value]);

  useEffect(() => {
    const handler = e => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener("mousedown", handler);
    return () => document.removeEventListener("mousedown", handler);
  }, []);

  const matches = (groups || []).filter(g =>
    !input || g.GroupName.toLowerCase().includes(input.toLowerCase())
  );

  const select = g => { onChange(g.GroupName); setInput(g.GroupName); setOpen(false); };
  const clear  = () => { onChange(""); setInput(""); };

  return (
    <div ref={ref} style={{ display: "flex", flexDirection: "column", gap: 4, position: "relative" }}>
      <label style={{ fontSize: 11, color: c.textMuted, fontWeight: 600, letterSpacing: "0.05em", textTransform: "uppercase" }}>Group</label>
      <div style={{ position: "relative" }}>
        <input
          value={input}
          onFocus={() => setOpen(true)}
          onChange={e => { setInput(e.target.value); onChange(e.target.value); setOpen(true); }}
          placeholder="Type or select group…"
          tabIndex={tabIndex}
          style={{ background: c.surface, border: `1px solid ${c.border}`, borderRadius: 6, padding: "8px 28px 8px 12px", color: c.text, fontSize: 13, fontFamily: "inherit", outline: "none", width: "100%", boxSizing: "border-box" }}
        />
        {input && (
          <button onClick={clear} style={{ position: "absolute", right: 6, top: "50%", transform: "translateY(-50%)", background: "none", border: "none", cursor: "pointer", color: c.textDimmer, fontSize: 14, padding: 0 }}>×</button>
        )}
      </div>
      {open && matches.length > 0 && (
        <div style={{ position: "absolute", top: "100%", left: 0, right: 0, background: c.surface, border: `1px solid ${c.border}`, borderRadius: 6, zIndex: 100, boxShadow: "0 4px 12px rgba(0,0,0,0.1)", maxHeight: 180, overflowY: "auto", marginTop: 2 }}>
          {matches.map(g => {
            const parent = g.ParentGroupID ? groups.find(p => p.GroupID === g.ParentGroupID) : null;
            return (
              <div key={g.GroupID} onClick={() => select(g)}
                style={{ padding: "8px 12px", cursor: "pointer", display: "flex", alignItems: "center", gap: 8, fontSize: 12 }}
                onMouseEnter={e => e.currentTarget.style.background = c.surfaceAlt}
                onMouseLeave={e => e.currentTarget.style.background = ""}>
                <span>{g.GroupName}</span>
                {parent && (
                  <span style={{ fontSize: 10, color: '#6B6B6B', background: '#F0EDE8', borderRadius: 3, padding: '1px 6px' }}>
                    sub of {parent.GroupName}
                  </span>
                )}
                <span style={{ marginLeft: "auto", fontSize: 10, color: c.textDimmer }}>
                  {g.MonitorCount} monitor{g.MonitorCount !== 1 ? 's' : ''}
                </span>
              </div>
            );
          })}
        </div>
      )}
    </div>
  );
}

// ── Tabs ──
const ADMIN_TABS = [
  { key: "dashboard", label: "Dashboard",  icon: "◉" },
  { key: "monitors",  label: "Monitors",   icon: "⬡" },
  { key: "topology",  label: "Topology",   icon: "⬡" },
  { key: "synthetic", label: "Synthetic",  icon: "⬡" },
  { key: "alerts",    label: "Alerts",     icon: "!" },
  { key: "logs",      label: "Logs",       icon: "☰" },
  { key: "reports",   label: "Reports",    icon: "📋" },
  { key: "config",    label: "Config",     icon: "⚙" },
  { key: "agents",    label: "Agents",     icon: "▣" },
  { key: "users",     label: "Users",      icon: "👤" },
  { key: "profile",   label: "My Profile", icon: "🔑" },
];
const USER_TABS = [
  { key: "dashboard", label: "Dashboard",  icon: "◉" },
  { key: "monitors",  label: "Monitors",   icon: "⬡" },
  { key: "topology",  label: "Topology",   icon: "⬡" },
  { key: "synthetic", label: "Synthetic",  icon: "⬡" },
  { key: "alerts",    label: "Alerts",     icon: "!" },
  { key: "logs",      label: "Logs",       icon: "☰" },
  { key: "reports",   label: "Reports",    icon: "📋" },
  { key: "profile",   label: "My Profile", icon: "🔑" },
];
const VIEWER_TABS = [
  { key: "dashboard", label: "Dashboard",  icon: "◉" },
  { key: "monitors",  label: "Monitors",   icon: "⬡" },
  { key: "topology",  label: "Topology",   icon: "⬡" },
  { key: "alerts",    label: "Alerts",     icon: "!" },
  { key: "logs",      label: "Logs",       icon: "☰" },
  { key: "profile",   label: "My Profile", icon: "🔑" },
];

// ── API helper ──
function useApi(token, onUnauthorized) {
  return useCallback(async (method, path, body, signal) => {
    const headers = { "Content-Type": "application/json" };
    if (token) headers["Authorization"] = `Bearer ${token}`;
    const res = await fetch(`${API}${path}`, {
      method, headers,
      body: body !== undefined ? JSON.stringify(body) : undefined,
      signal,
    });
    if (res.status === 401) {
      localStorage.removeItem("op1_token");
      localStorage.removeItem("op1_user");
      localStorage.setItem("op1_session_expired", "true");
      if (onUnauthorized) onUnauthorized();
      throw new Error("Session expired");
    }
    if (res.status === 204) return null;
    const text = await res.text();
    let data;
    try { data = text ? JSON.parse(text) : null; } catch { data = text; }
    if (!res.ok) throw new Error(data?.title || data?.Message || data?.message || data?.Error || `HTTP ${res.status}`);
    return data;
  }, [token, onUnauthorized]);
}

// ── Sidebar nav button ──
function TabBtn({ t, active, onClick, api }) {
  const [alertCount, setAlertCount] = useState(0);
  const [hov, setHov] = useState(false);
  useEffect(() => {
    if (t.key !== "alerts" || !api) return;
    api("GET", "/logs/alerts?unresolvedOnly=true&top=1000")
      .then(r => setAlertCount(Array.isArray(r) ? r.length : 0))
      .catch(() => {});
  }, [t.key, api]);
  return (
    <button onClick={onClick}
      onMouseEnter={() => setHov(true)}
      onMouseLeave={() => setHov(false)}
      style={{
        background: active ? "rgba(255,255,255,0.10)" : hov ? "rgba(255,255,255,0.08)" : "transparent",
        border: "none",
        borderLeft: active ? "2px solid #FFFFFF" : "2px solid transparent",
        color: "#FFFFFF",
        borderRadius: "0 6px 6px 0",
        padding: "9px 14px 9px 13px",
        fontSize: 12, fontWeight: 400,
        cursor: "pointer", display: "flex", alignItems: "center",
        width: "100%", textAlign: "left", marginBottom: 2, transition: "all 0.15s",
      }}>
      <span style={{ width: 18, flexShrink: 0, textAlign: "center", marginRight: 8, fontSize: 12 }}>{t.icon}</span>
      <span>{t.label}</span>
      {t.key === "alerts" && alertCount > 0 && (
        <span style={{ background: c.red, color: "#fff", borderRadius: 8, padding: "1px 5px", fontSize: 9, fontWeight: 700, marginLeft: "auto" }}>{alertCount}</span>
      )}
    </button>
  );
}

// ── Group delete trash icon button ──
const GroupDeleteBtn = ({ onClick }) => {
  const [hov, setHov] = useState(false);
  return (
    <button
      onClick={onClick}
      onMouseEnter={() => setHov(true)}
      onMouseLeave={() => setHov(false)}
      title="Delete group"
      style={{ background:"transparent", border:"none", cursor:"pointer", padding:"2px 3px",
        display:"flex", alignItems:"center", color: hov ? "#D95C5C" : "#7A9AB8", transition:"color 0.15s" }}
    >
      <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round">
        <polyline points="3 6 5 6 21 6"/>
        <path d="M19 6l-1 14a2 2 0 01-2 2H8a2 2 0 01-2-2L5 6"/>
        <path d="M10 11v6M14 11v6"/>
        <path d="M9 6V4a1 1 0 011-1h4a1 1 0 011 1v2"/>
      </svg>
    </button>
  );
};

// ── Add Monitor sidebar sub-item ──
const AddMonitorNavItem = ({ active, onClick }) => {
  const [hov, setHov] = useState(false);
  return (
    <button
      onClick={onClick}
      onMouseEnter={() => setHov(true)}
      onMouseLeave={() => setHov(false)}
      style={{
        background: hov ? "rgba(255,255,255,0.08)" : "transparent",
        border: "none",
        borderLeft: "2px solid transparent",
        padding: "5px 14px 5px 13px",
        fontSize: 11, cursor: "pointer",
        color: "#FFFFFF",
        display: "flex", alignItems: "center",
        width: "100%", textAlign: "left", marginBottom: 2,
        transition: "all 0.15s",
      }}
    >
      <div style={{ width: 26, flexShrink: 0 }} />
      <span>+ Add Monitor</span>
    </button>
  );
};

// ── Server Health Widget (sidebar bottom) ────────────────────────────────
function ServerHealthWidget({ api, setHelpKey }) {
  const [health, setHealth] = useState(null);
  const [unavail, setUnavail] = useState(false);
  const [lastUpdated, setLastUpdated] = useState(null);

  const fetchHealth = useCallback(() => {
    api("GET", "/dashboard/health")
      .then(d => { setHealth(d); setUnavail(false); setLastUpdated(new Date()); })
      .catch(() => { setUnavail(true); });
  }, [api]);

  useEffect(() => {
    fetchHealth();
    const id = setInterval(fetchHealth, 30000);
    return () => clearInterval(id);
  }, [fetchHealth]);

  const barColor = pct => pct > 85 ? "#D95C5C" : pct > 70 ? "#E89A2E" : "#008C6F";

  const statusDot = !health ? "#7A9AB8"
    : (health.CpuPercent > 85 || (health.SystemMemoryPercent ?? health.MemoryPercent) > 85 || (health.DiskPercent ?? 0) > 85 || health.DbStatus === "error") ? "#D95C5C"
    : (health.CpuPercent > 70 || (health.SystemMemoryPercent ?? health.MemoryPercent) > 70 || (health.DiskPercent ?? 0) > 70 || health.DbStatus === "slow") ? "#E89A2E"
    : "#008C6F";

  const updatedStr = lastUpdated
    ? lastUpdated.toLocaleTimeString([], { hour:"2-digit", minute:"2-digit", second:"2-digit" })
    : null;

  return (
    <div style={{ margin:"0 8px 6px", borderTop:`1px solid rgba(255,255,255,0.08)`, paddingTop:8, flexShrink:0 }}>
      <div style={{ display:"flex", flexDirection:"column", gap:3, marginBottom:5 }}>
        <span style={{ fontSize:9, fontWeight:700, color:"#7A9AB8", textTransform:"uppercase", letterSpacing:"0.08em" }}>OP1 Server Health</span>
        <div style={{ display:"flex", alignItems:"center", gap:4 }}>
          <span style={{ width:7, height:7, borderRadius:"50%", background:statusDot, display:"inline-block", flexShrink:0 }} />
          {setHelpKey && <HelpIcon hkey="server-health" setHelpKey={setHelpKey} />}
          {updatedStr && <span style={{ fontSize:8, color:"#F4F8FC" }}>{updatedStr}</span>}
        </div>
      </div>
      {unavail ? (
        <div style={{ fontSize:10, color:"#7A9AB8", textAlign:"center", paddingTop:8 }}>Unavailable</div>
      ) : !health ? (
        <div style={{ fontSize:10, color:"#7A9AB8" }}>Loading…</div>
      ) : (
        <div style={{ display:"flex", flexDirection:"column", gap:4 }}>
          {[
            { label:"CPU",        pct: health.CpuPercent,                                                          sub: null },
            { label:"System Mem", pct: health.SystemMemoryPercent ?? health.MemoryPercent,                         sub: health.SystemMemoryTotalMb != null ? `${(health.SystemMemoryUsedMb/1024).toFixed(1)}gb used of ${(health.SystemMemoryTotalMb/1024).toFixed(1)}gb` : null },
            { label:"App Mem",    pct: health.AppMemoryPercent ?? 0,                                               sub: health.AppMemoryMb != null ? `${health.AppMemoryMb}mb (OP1 process)` : null },
            { label:"Disk",       pct: health.DiskPercent ?? 0,                                                    sub: health.DiskGB || null },
          ].map(row => (
            <div key={row.label}>
              <div style={{ display:"flex", alignItems:"center", gap:5 }}>
                <span style={{ fontSize:9, color:"#7A9AB8", width:60, flexShrink:0 }}>{row.label}</span>
                <div style={{ flex:1, height:4, background:"rgba(255,255,255,0.1)", borderRadius:2, overflow:"hidden" }}>
                  <div style={{ width:`${Math.min(100,row.pct)}%`, height:"100%", background:barColor(row.pct), borderRadius:2, transition:"width 0.4s" }} />
                </div>
                <span style={{ fontSize:9, color:"#7A9AB8", width:28, textAlign:"right", flexShrink:0 }}>{row.pct}%</span>
              </div>
              {row.sub && <div style={{ fontSize:8, color:"#4A6A80", marginLeft:65, marginTop:1 }}>{row.sub}</div>}
            </div>
          ))}
          <div style={{ height:1, background:"rgba(255,255,255,0.06)", margin:"5px 0" }} />
          <div style={{ display:"flex", alignItems:"center", gap:6, padding:"2px 0" }}>
            <span style={{ width:7, height:7, borderRadius:"50%", flexShrink:0, display:"inline-block",
              background: health.DbStatus === "online" ? "#008C6F" : health.DbStatus === "slow" ? "#E89A2E" : "#D95C5C" }} />
            <span style={{ fontSize:9, color:"#7A9AB8", flex:1 }}>Database</span>
            <span style={{ fontSize:9, fontWeight:600, color:"#5DD4F4" }}>
              {health.DbResponseMs >= 0 ? `${health.DbResponseMs}ms` : "—"}
            </span>
            <span style={{ fontSize:9, color:"#4A6A80", marginLeft:4 }}>
              · {health.DbStatus === "online" ? "Online" : health.DbStatus === "slow" ? "Slow" : "Error"}
            </span>
          </div>
          {health.DbName && (
            <div style={{ fontSize:8, color:"#4A6A80", marginLeft:13, marginBottom:2 }}>
              {health.DbName} · {health.DbHost}
            </div>
          )}
          <div style={{ height:1, background:"rgba(255,255,255,0.06)", margin:"5px 0" }} />
          <div style={{ display:"flex", alignItems:"center", gap:5 }}>
            <span style={{ fontSize:9, color:"#7A9AB8", width:60, flexShrink:0 }}>Checks/min</span>
            <span style={{ fontSize:10, fontWeight:600, color:"#5DD4F4" }}>{health.ChecksPerMin ?? "—"}</span>
          </div>
        </div>
      )}
    </div>
  );
}

// ═══════════════════════════════════════════════════════════════════
// MAIN APP
// ═══════════════════════════════════════════════════════════════════
function OpsPortalApp() {
  const [token, setToken] = useState(() => localStorage.getItem("op1_token") || "");
  const [user, setUser]   = useState(() => { try { return JSON.parse(localStorage.getItem("op1_user") || "null"); } catch { return null; } });
  const [tab, setTab]     = useState("dashboard");
  const navInterceptRef = useRef(null);
  const pendingAddMonitorRef = useRef(false);
  const [addMonitorActive, setAddMonitorActive] = useState(false);
  const handleTabClick = (key) => {
    if (key === "monitors") {
      setAddMonitorActive(false);
      setMonitorsNav({});
      setMonitorsSubViewReset(prev => prev + 1);
    }
    if (navInterceptRef.current) navInterceptRef.current(key);
    else setTab(key);
  };
  useEffect(() => { if (tab !== "monitors") setAddMonitorActive(false); }, [tab]);

  const [helpKey, setHelpKey] = useState(null);

  const [loginForm, setLoginForm]       = useState({ email: "", password: "", rememberMe: false });
  const [loginStep, setLoginStep]       = useState("login");
  const [mfaChallenge, setMfaChallenge] = useState("");
  const [mfaCode, setMfaCode]           = useState("");
  const [ssoProviders, setSsoProviders] = useState([]);
  const [loginErr, setLoginErr]         = useState("");
  const [loginLoading, setLoginLoading] = useState(false);
  const [changePassForm, setChangePassForm] = useState({ current: "", newPass: "", confirm: "" });
  const [changePassErr, setChangePassErr]   = useState("");
  const [sessionExpired, setSessionExpired] = useState(() => {
    const f = localStorage.getItem("op1_session_expired");
    if (f) { localStorage.removeItem("op1_session_expired"); return true; }
    return false;
  });

  const handleUnauthorized = useCallback(() => {
    setToken(""); setUser(null); setSessionExpired(true);
  }, []);
  const api = useApi(token, handleUnauthorized);

  // ── Monitoring control state ──
  const [monCtrl, setMonCtrl]           = useState(null);
  const [machines, setMachines]         = useState([]);
  const [pausePopover, setPausePopover] = useState(false);
  const [pauseReason, setPauseReason]   = useState("");
  const [pauseBusy, setPauseBusy]       = useState(false);
  const currentMachine = monCtrl?.currentMachine || "";
  const currentCtrl    = monCtrl?.machines?.find(m => m.MachineName === currentMachine);
  const isPaused       = currentCtrl ? !currentCtrl.IsActive : false;

  const loadMonCtrl = useCallback(() => {
    if (!token) return;
    api("GET", "/config/monitoring-control")
      .then(r => { setMonCtrl(r); setMachines(r?.machines || []); })
      .catch(() => {});
  }, [api, token]);

  useEffect(() => {
    loadMonCtrl();
    const id = setInterval(loadMonCtrl, 30000);
    return () => clearInterval(id);
  }, [loadMonCtrl]);

  const togglePause = async () => {
    setPauseBusy(true);
    try {
      await api("POST", "/config/monitoring-control/toggle", {
        MachineName: currentMachine,
        IsActive: isPaused,
        Reason: isPaused ? null : (pauseReason || "Manual pause"),
      });
      setPausePopover(false);
      setPauseReason("");
      loadMonCtrl();
    } catch(e) { /* ignore */ }
    finally { setPauseBusy(false); }
  };

  useEffect(() => {
    fetch(`${API}/auth/sso/providers`)
      .then(r => r.ok ? r.json() : [])
      .then(setSsoProviders)
      .catch(() => {});
  }, []);

  const saveSession = (tok, usr) => {
    localStorage.setItem("op1_token", tok);
    localStorage.setItem("op1_user", JSON.stringify(usr));
    setToken(tok); setUser(usr); setLoginStep("login"); setSessionExpired(false);
  };

  const handleLogin = async () => {
    setLoginErr(""); setLoginLoading(true);
    try {
      const r = await fetch(`${API}/auth/login`, {
        method: "POST", headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ Email: loginForm.email, Password: loginForm.password, RememberMe: loginForm.rememberMe }),
      });
      const data = await r.json();
      if (!r.ok) { setLoginErr(data?.title || data?.Error || "Login failed"); return; }
      if (data.MFARequired)        { setMfaChallenge(data.MFAChallenge); setLoginStep("mfa"); return; }
      if (data.MustChangePassword) {
        saveSession(data.Token, { UserID: data.UserID, DisplayName: data.DisplayName, RoleName: data.RoleName, Username: data.Username });
        setLoginStep("changepass"); return;
      }
      saveSession(data.Token, { UserID: data.UserID, DisplayName: data.DisplayName, RoleName: data.RoleName, Username: data.Username });
    } catch (e) { setLoginErr(e.message); }
    finally { setLoginLoading(false); }
  };

  const handleMfa = async () => {
    setLoginErr(""); setLoginLoading(true);
    try {
      const r = await fetch(`${API}/auth/mfa-verify`, {
        method: "POST", headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ MFAChallenge: mfaChallenge, Code: mfaCode }),
      });
      const data = await r.json();
      if (!r.ok) { setLoginErr(data?.title || data?.Error || "MFA failed"); return; }
      if (data.MustChangePassword) {
        saveSession(data.Token, { UserID: data.UserID, DisplayName: data.DisplayName, RoleName: data.RoleName, Username: data.Username });
        setLoginStep("changepass"); return;
      }
      saveSession(data.Token, { UserID: data.UserID, DisplayName: data.DisplayName, RoleName: data.RoleName, Username: data.Username });
    } catch (e) { setLoginErr(e.message); }
    finally { setLoginLoading(false); }
  };

  const handleChangePass = async () => {
    if (changePassForm.newPass !== changePassForm.confirm) { setChangePassErr("Passwords do not match"); return; }
    setChangePassErr(""); setLoginLoading(true);
    try {
      await api("POST", "/auth/change-password", { CurrentPassword: changePassForm.current, NewPassword: changePassForm.newPass, ConfirmPassword: changePassForm.confirm });
      setLoginStep("login"); setLoginForm(p => ({ ...p, password: "" }));
    } catch (e) { setChangePassErr(e.message); }
    finally { setLoginLoading(false); }
  };

  const handleLogout = async () => {
    try { await api("POST", "/auth/logout", {}); } catch {}
    localStorage.removeItem("op1_token"); localStorage.removeItem("op1_user");
    setToken(""); setUser(null); setTab("dashboard"); setLoginStep("login");
  };

  const role = user?.RoleName || "";
  const isAdmin  = role === "Admin";
  const isUser   = role === "User" || role === "Operator";
  const tabs     = isAdmin ? ADMIN_TABS : isUser ? USER_TABS : VIEWER_TABS;
  const canManageMonitors = isAdmin || isUser;
  const canAckAlerts      = isAdmin || isUser;
  const canManageUsers    = isAdmin;
  const canManageConfig   = isAdmin;

  // Navigation state for cross-view navigation
  const [monitorsNav, setMonitorsNav] = useState({});
  const [monitorsSubViewReset, setMonitorsSubViewReset] = useState(0);
  const [syntheticNav, setSyntheticNav] = useState({});
  const navigateToSynthetic = (flowId) => { setSyntheticNav({ editFlowId: flowId }); setTab("synthetic"); };
  const navigateToSyntheticResults = (flowId, flowResultId, autodiagnose) => { setSyntheticNav({ resultsFlowId: flowId, resultsResultId: flowResultId, autodiagnose: !!autodiagnose }); setTab("synthetic"); };
  const navigateToMonitors = (groupId, editId, editMonitor, options) => {
    setMonitorsNav({ groupId: groupId || null, editId: editId || null, editMonitor: editMonitor || null, ...(options||{}) });
    setTab("monitors");
  };

  // Hash-based deep-link routing from alert emails (#monitor/{id}, #flow/{id})
  const hashNavApplied = useRef(false);
  useEffect(() => {
    if (!token || hashNavApplied.current) return;
    const m = window.location.hash.match(/^#(monitor|flow)\/(\d+)$/);
    if (!m) return;
    hashNavApplied.current = true;
    if (m[1] === "monitor") navigateToMonitors(null, parseInt(m[2]), null, { origin: "Email" });
    else                    navigateToSynthetic(parseInt(m[2]));
  }, [token]);

  // Wrapped navigate passed to MonitorsView — handles pending "open add form" after dirty-discard
  const handleMonitorFormNavigate = useCallback((key) => {
    if (key === "correlation analysis") return; // onClose handles subView; stay on monitors tab
    if (pendingAddMonitorRef.current && key === "monitors") {
      setMonitorsNav({ openAdd: true });
      setAddMonitorActive(true);
      pendingAddMonitorRef.current = false;
    }
    setTab(key);
  }, []);

  const handleAddMonitorClick = () => {
    if (navInterceptRef.current) {
      // Dirty form is open — queue the openAdd action, then trigger dirty check
      pendingAddMonitorRef.current = true;
      navInterceptRef.current("monitors");
    } else {
      setMonitorsNav({ openAdd: true });
      setAddMonitorActive(true);
      setTab("monitors");
    }
  };

  const GLOBAL_STYLES = `
    @import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;700&display=swap');
    @keyframes pulse    { 0%,100%{opacity:1} 50%{opacity:.4} }
    @keyframes fadeIn   { from{opacity:0;transform:translateY(8px)} to{opacity:1;transform:translateY(0)} }
    @keyframes slideIn  { from{opacity:0;transform:translateX(-12px)} to{opacity:1;transform:translateX(0)} }
    @keyframes spin     { from{transform:rotate(0deg)} to{transform:rotate(360deg)} }
    @keyframes op1pulse { from{opacity:0.4} to{opacity:1} }
    input:focus, select:focus { border-color:${c.blue}!important; box-shadow:0 0 0 3px rgba(0,109,140,0.12)!important; }
    button:hover { opacity:.85; }
    ::-webkit-scrollbar       { width:6px; }
    ::-webkit-scrollbar-track { background:${c.surfaceAlt}; }
    ::-webkit-scrollbar-thumb { background:${c.border}; border-radius:3px; }
    input, select, textarea { color-scheme: light; }
    /* White panel overrides — form page light panels */
    .dp label { color: #4A4A4A !important; }
    .dp .cs-title { color: #1A1A1A !important; }
    .dp .cs-btn > span:last-child { color: #7A9AB8 !important; }
    .dp select { background:#FFFFFF; color:#1A1A1A; border-color:#C0D8E8; }
    .dp input[type=text],.dp input[type=number],.dp input[type=email],.dp input[type=password] { background:#FFFFFF; color:#1A1A1A; border-color:#C0D8E8; }
    .dp textarea { background:#FFFFFF; color:#1A1A1A; border-color:#C0D8E8; }
    /* Clickable monitor row hover */
    .mon-row:hover { background:#F8FAFC !important; cursor:pointer; }
  `;

  // ── LOGIN / MFA / CHANGE-PASS SCREENS ──
  if (!user || loginStep === "mfa" || loginStep === "changepass") {
    return (
      <div style={{ minHeight:"100vh", background:"#4DA2CC", display:"flex", alignItems:"center", justifyContent:"center", fontFamily:"'IBM Plex Sans',system-ui,sans-serif" }}>
        <style>{GLOBAL_STYLES}</style>

        {loginStep === "changepass" && (
          <Card style={{ width:420, animation:"fadeIn 0.4s ease", borderTop:`3px solid ${c.amber}` }}>
            <div style={{ textAlign:"center", marginBottom:20 }}>
              <div style={{ fontSize:18, fontWeight:800, color:c.text }}>Change Password Required</div>
              <p style={{ fontSize:12, color:c.textDim, marginTop:6 }}>You must set a new password before continuing.</p>
            </div>
            <ErrBox msg={changePassErr} />
            <div style={{ display:"flex", flexDirection:"column", gap:12 }}>
              <PasswordInput label="Current Password" value={changePassForm.current} onChange={e => setChangePassForm(p => ({...p, current:e.target.value}))} />
              <PasswordInput label="New Password" placeholder="Min 12 chars, upper+lower+digit+special" value={changePassForm.newPass} onChange={e => setChangePassForm(p => ({...p, newPass:e.target.value}))} />
              <div style={{ fontSize:11, color:"#94a3b8", marginTop:-6 }}>Min 12 characters · uppercase · lowercase · digit · special character</div>
              <PasswordInput label="Confirm New Password" value={changePassForm.confirm} onChange={e => setChangePassForm(p => ({...p, confirm:e.target.value}))} onKeyDown={e => e.key==="Enter" && handleChangePass()} />
              <Btn onClick={handleChangePass} loading={loginLoading} style={{ width:"100%", padding:12, marginTop:4 }}>Set New Password</Btn>
            </div>
          </Card>
        )}

        {loginStep === "mfa" && (
          <Card style={{ width:360, animation:"fadeIn 0.4s ease", borderTop:`3px solid ${c.blue}` }}>
            <div style={{ textAlign:"center", marginBottom:20 }}>
              <div style={{ fontSize:18, fontWeight:800, color:c.text }}>Two-Factor Authentication</div>
              <p style={{ fontSize:12, color:c.textDim, marginTop:6 }}>Enter the 6-digit code from your authenticator app.</p>
            </div>
            <ErrBox msg={loginErr} />
            <div style={{ display:"flex", flexDirection:"column", gap:12 }}>
              <Input label="Authenticator Code" placeholder="000000" value={mfaCode} onChange={e => setMfaCode(e.target.value)} onKeyDown={e => e.key==="Enter" && handleMfa()} style={{ textAlign:"center", fontSize:22, letterSpacing:"0.3em" }} />
              <Btn onClick={handleMfa} loading={loginLoading} style={{ width:"100%", padding:12 }}>Verify</Btn>
              <Btn variant="ghost" onClick={() => { setLoginStep("login"); setMfaCode(""); setLoginErr(""); }} style={{ width:"100%", fontSize:11 }}>← Back</Btn>
            </div>
          </Card>
        )}

        {loginStep === "login" && (
          <Card style={{ width:400, animation:"fadeIn 0.4s ease", borderTop:`3px solid ${c.blue}` }}>
            {sessionExpired && (
              <div style={{ background:"#fffbeb", border:"0.5px solid #fde68a", color:"#b45309",
                borderRadius:6, padding:"10px 14px", fontSize:13, marginBottom:16 }}>
                Your session has expired. Please sign in to continue.
              </div>
            )}
            <div style={{ textAlign:"center", marginBottom:24 }}>
              <div style={{ width:52, height:52, borderRadius:14, background:"linear-gradient(135deg,#006D8C,#5DD4F4)", display:"inline-flex", alignItems:"center", justifyContent:"center", fontSize:13, fontWeight:800, color:"#fff", marginBottom:12, letterSpacing:"-0.02em" }}>OP1</div>
              <h1 style={{ fontSize:22, fontWeight:800, color:c.text, margin:0, letterSpacing:"-0.02em" }}>OP1 Operations Portal</h1>
              <p style={{ fontSize:12, color:c.textDim, marginTop:4 }}>v1.0 — Operations Monitoring Platform</p>
            </div>
            <ErrBox msg={loginErr} />
            <div style={{ display:"flex", flexDirection:"column", gap:12 }}>
              <Input label="Email" type="email" placeholder="admin@localhost" autoComplete="email" value={loginForm.email} onChange={e => setLoginForm(p => ({...p, email:e.target.value}))} onKeyDown={e => e.key==="Enter" && handleLogin()} />
              <PasswordInput label="Password" autoComplete="current-password" value={loginForm.password} onChange={e => setLoginForm(p => ({...p, password:e.target.value}))} onKeyDown={e => e.key==="Enter" && handleLogin()} />
              <label style={{ display:"flex", alignItems:"center", gap:8, fontSize:12, color:c.textMuted, cursor:"pointer" }}>
                <input type="checkbox" checked={loginForm.rememberMe} onChange={e => setLoginForm(p => ({...p, rememberMe:e.target.checked}))} style={{ accentColor:c.blue }} />
                Remember me for 30 days
              </label>
              <Btn onClick={handleLogin} loading={loginLoading} style={{ width:"100%", padding:12, fontSize:14, marginTop:4 }}>Sign In</Btn>
            </div>
            {ssoProviders.length > 0 && (
              <div style={{ marginTop:16 }}>
                <div style={{ textAlign:"center", fontSize:11, color:c.textDimmer, marginBottom:8 }}>— or sign in with —</div>
                <div style={{ display:"flex", gap:8, flexWrap:"wrap", justifyContent:"center" }}>
                  {ssoProviders.map(p => (
                    <Btn key={p.ConfigID} variant="secondary" onClick={() => window.location.href=`${API}/auth/sso/initiate/${p.ConfigID}`} style={{ flex:"1 1 auto" }}>
                      {p.DisplayName || p.Provider}
                    </Btn>
                  ))}
                </div>
              </div>
            )}
            <div style={{ marginTop:20, textAlign:"center", fontSize:11, color:c.textDimmer }}>© 2026 OP1 Operations Portal · All rights reserved.</div>
          </Card>
        )}
      </div>
    );
  }

  // ═══ AUTHENTICATED SHELL ═══
  return (
    <div style={{ minHeight:"100vh", background:c.bg, color:c.text, fontFamily:"'IBM Plex Sans',system-ui,sans-serif", display:"flex" }}>
      <style>{GLOBAL_STYLES}</style>

      {/* ── Sidebar ── */}
      <aside style={{ width:150, flexShrink:0, background:c.sidebarBg, display:"flex", flexDirection:"column", position:"fixed", top:0, left:0, bottom:0, zIndex:100 }}>

        <div style={{ padding:"18px 16px 14px", borderBottom:`1px solid ${c.sidebarBorder}` }}>
          <div style={{ display:"flex", alignItems:"center", gap:10 }}>
            <div style={{ width:34, height:34, borderRadius:8, background:"linear-gradient(135deg,#006D8C,#5DD4F4)", display:"flex", alignItems:"center", justifyContent:"center", fontSize:11, fontWeight:800, color:"#fff", letterSpacing:"-0.02em", flexShrink:0 }}>OP1</div>
            <div>
              <div style={{ fontWeight:800, fontSize:13, color:c.sidebarText, letterSpacing:"-0.02em", lineHeight:1.2 }}>OP1 Operations</div>
              <div style={{ fontSize:10, color:c.sidebarMuted }}>Portal v1.0</div>
            </div>
          </div>
        </div>
        <nav style={{ flex:1, padding:"10px 8px", overflowY:"auto" }}>
          {tabs.flatMap(t => {
            const btn = <TabBtn key={t.key} t={t} active={tab===t.key} onClick={() => handleTabClick(t.key)} api={api} />;
            if (t.key === "monitors" && canManageMonitors) {
              return [btn, <AddMonitorNavItem key="add-monitor" active={addMonitorActive && tab==="monitors"} onClick={handleAddMonitorClick} />];
            }
            return [btn];
          })}
        </nav>
        {/* ── Server Health Widget ── */}
        <ServerHealthWidget api={api} setHelpKey={setHelpKey} />
        <div style={{ padding:"12px 16px", borderTop:`1px solid ${c.sidebarBorder}` }}>
          <div style={{ fontSize:11, color:c.sidebarMuted, marginBottom:6, overflow:"hidden", textOverflow:"ellipsis", whiteSpace:"nowrap" }}>{user.DisplayName}</div>
          <Badge color={isAdmin ? "#D95C5C" : isUser ? "#5DD4F4" : c.sidebarMuted}>{role}</Badge>
          <button onClick={handleLogout} style={{ display:"block", marginTop:10, background:"transparent", border:`1px solid ${c.sidebarBorder}`, borderRadius:5, color:c.sidebarMuted, fontSize:11, padding:"5px 10px", cursor:"pointer", width:"100%" }}>Sign Out</button>
          <div style={{ marginTop:10, fontSize:9, color:c.sidebarMuted, lineHeight:1.3, opacity:0.6, wordBreak:"break-word" }}>© 2026 OP1 Operations Portal. All rights reserved.</div>
        </div>
      </aside>

      {/* ── Contextual help popovers ── */}
      <HelpPopover helpKey={helpKey} setHelpKey={setHelpKey} />

      {/* ── Main content ── */}
      <div style={{ marginLeft:150, flex:1, minWidth:0, background:"#ffffff" }}>
        {/* Pause banner — Config tab only */}
        {isPaused && tab === "config" && (
          <div style={{ background:"#B45309", color:"#FEF3C7", padding:"8px 20px", fontSize:12, fontWeight:600, display:"flex", alignItems:"center", gap:10, position:"sticky", top:0, zIndex:50 }}>
            <span style={{ display:"inline-block", width:8, height:8, borderRadius:"50%", background:"#FDE68A", animation:"pulse 1.5s infinite", flexShrink:0 }} />
            ⚠ Monitoring paused by {currentCtrl?.PausedBy || "unknown"} at {currentCtrl?.PausedAt ? parseUTC(currentCtrl.PausedAt)?.toLocaleString() : "—"} — all checks suspended on {currentCtrl?.MachineAlias || currentMachine}
            {canManageConfig && <button onClick={()=>{ setPausePopover(true); }} style={{ marginLeft:"auto", background:"#FDE68A", color:"#78350F", border:"none", borderRadius:4, padding:"3px 10px", fontSize:11, fontWeight:700, cursor:"pointer" }}>Resume Monitoring</button>}
          </div>
        )}
        <main style={{ padding:"8px 16px 8px 20px" }}>
          {/* Topbar monitoring status indicator — hidden on all branded screens (each has its own pill) */}
          {!["dashboard","monitors","topology","alerts","logs","reports","config","agents","users","profile"].includes(tab) && canManageConfig && monCtrl && (
            <div style={{ display:"flex", justifyContent:"flex-end", marginBottom:10, position:"relative" }}>
              <button
                onClick={()=>setPausePopover(p=>!p)}
                style={{ display:"flex", alignItems:"center", gap:7, background: isPaused ? "rgba(180,83,9,0.08)" : "rgba(0,0,0,0.05)", border: `1px solid ${isPaused?"rgba(180,83,9,0.3)":"rgba(0,0,0,0.12)"}`, borderRadius:20, padding:"5px 12px", cursor:"pointer", color: isPaused ? "#B45309" : "#374151", fontSize:11, fontWeight:600 }}
              >
                <span style={{ width:8, height:8, borderRadius:"50%", background: isPaused ? "#F59E0B" : "#10B981", flexShrink:0, animation: isPaused ? "pulse 1.5s infinite" : "none" }} />
                {isPaused ? "Monitoring Paused" : "Monitoring Active"}
              </button>
              {pausePopover && (
                <div style={{ position:"absolute", top:"calc(100% + 8px)", right:0, width:280, background:"#FFFFFF", border:`1px solid ${c.border}`, borderRadius:8, boxShadow:"0 8px 24px rgba(0,0,0,0.15)", zIndex:200, padding:16 }}>
                  <div style={{ fontSize:13, fontWeight:700, color:c.text, marginBottom:8 }}>
                    {isPaused ? "Resume Monitoring" : "Pause Monitoring"}
                  </div>
                  <div style={{ fontSize:11, color:c.textDim, marginBottom:10 }}>
                    Machine: <span style={{ fontFamily:"mono", color:c.text }}>{currentMachine}</span>
                  </div>
                  {!isPaused && (
                    <Input label="Reason (optional)" value={pauseReason} onChange={e=>setPauseReason(e.target.value)} placeholder="e.g. Maintenance window" style={{ marginBottom:10, fontSize:11 }} />
                  )}
                  {isPaused && currentCtrl?.PauseReason && (
                    <div style={{ fontSize:11, color:c.textDim, marginBottom:8 }}>Pause reason: {currentCtrl.PauseReason}</div>
                  )}
                  {isPaused && currentCtrl?.PausedAt && (
                    <div style={{ fontSize:10, color:c.textDimmer, marginBottom:10 }}>Paused at: {parseUTC(currentCtrl.PausedAt)?.toLocaleString()}</div>
                  )}
                  <div style={{ display:"flex", gap:6 }}>
                    <Btn loading={pauseBusy} onClick={togglePause} variant={isPaused?"success":"warning"} style={{ flex:1, fontSize:11 }}>
                      {isPaused ? "Resume" : "Pause"}
                    </Btn>
                    <Btn variant="ghost" onClick={()=>{ setPausePopover(false); setPauseReason(""); }} style={{ fontSize:11 }}>Cancel</Btn>
                  </div>
                </div>
              )}
            </div>
          )}
          {tab==="dashboard" && <DashboardView api={api} onNavigate={navigateToMonitors} onNavigateAlerts={() => setTab("alerts")} machines={machines} currentMachine={currentMachine} monitoringControl={{ isPaused, canManageConfig, monCtrl, pausePopover, setPausePopover, pauseReason, setPauseReason, pauseBusy, togglePause, currentCtrl }} />}
          {tab==="monitors"  && <MonitorsView  api={api} canManage={canManageMonitors} isAdmin={isAdmin} initialNav={monitorsNav} onNavConsumed={()=>setMonitorsNav({})} navInterceptRef={navInterceptRef} onNavigate={handleMonitorFormNavigate} machines={machines} currentMachine={currentMachine} setHelpKey={setHelpKey} monitoringControl={{ isPaused, canManageConfig, monCtrl, pausePopover, setPausePopover, pauseReason, setPauseReason, pauseBusy, togglePause, currentCtrl }} onNavigateSynthetic={navigateToSynthetic} subViewReset={monitorsSubViewReset} />}
          {tab==="topology"  && <TopologyView  api={api} monitoringControl={{ isPaused, canManageConfig, monCtrl, pausePopover, setPausePopover, pauseReason, setPauseReason, pauseBusy, togglePause, currentCtrl, currentMachine }} onNavigate={(id)=>navigateToMonitors(null,id,null,{origin:"Topology"})} />}
          {tab==="synthetic" && <SyntheticView api={api} canManage={canManageMonitors} initialNav={syntheticNav} onNavConsumed={()=>setSyntheticNav({})} />}
          {tab==="alerts"    && <AlertsView    api={api} canAck={canAckAlerts} machines={machines} currentMachine={currentMachine} onNavigate={(id)=>navigateToMonitors(null,id,null,{origin:"Alerts"})} onNavigateSynthetic={navigateToSyntheticResults} monitoringControl={{ isPaused, canManageConfig, monCtrl, pausePopover, setPausePopover, pauseReason, setPauseReason, pauseBusy, togglePause, currentCtrl }} />}
          {tab==="logs"      && <LogsView      api={api} machines={machines} currentMachine={currentMachine} onNavigate={(id)=>navigateToMonitors(null,id,null,{origin:"Logs"})} monitoringControl={{ isPaused, canManageConfig, monCtrl, pausePopover, setPausePopover, pauseReason, setPauseReason, pauseBusy, togglePause, currentCtrl }} />}
          {tab==="reports"   && <ReportsView   api={api} canGenerate={canManageMonitors} onNavigate={(id)=>navigateToMonitors(null,id,null,{origin:"Reports"})} monitoringControl={{ isPaused, canManageConfig, monCtrl, pausePopover, setPausePopover, pauseReason, setPauseReason, pauseBusy, togglePause, currentCtrl }} />}
          {tab==="config"    && canManageConfig && <ConfigView api={api} machines={machines} currentMachine={currentMachine} onMachinesChange={loadMonCtrl} monitoringControl={{ isPaused, canManageConfig, monCtrl, pausePopover, setPausePopover, pauseReason, setPauseReason, pauseBusy, togglePause, currentCtrl }} />}
          {tab==="agents"    && isAdmin         && <AgentsView api={api} />}
          {tab==="users"     && canManageUsers  && <UsersView  api={api} monitoringControl={{ isPaused, canManageConfig, monCtrl, pausePopover, setPausePopover, pauseReason, setPauseReason, pauseBusy, togglePause, currentCtrl }} />}
          {tab==="profile"   && <ProfileView   api={api} user={user} monitoringControl={{ isPaused, canManageConfig, monCtrl, pausePopover, setPausePopover, pauseReason, setPauseReason, pauseBusy, togglePause, currentCtrl }} />}
        </main>
      </div>
    </div>
  );
}

// ═══ DASHBOARD ═══

// ── Dashboard chart renderer (top-level, no inner functions) ──
function renderDashChart(chartType, canvasEl, summary, responseTrend, alertTrend, uptimeTrend) {
  if (!canvasEl || typeof Chart === "undefined") return null;
  const s = summary;
  const _synCrit = (s.GroupSummaries || []).reduce((acc, gs) =>
    acc + (gs.SyntheticFlows || []).filter(f => f.IsEnabled && (
      f.LastRunPassed === false ||
      (f.DurationCritMs > 0 && f.LastDurationMs != null && f.LastDurationMs >= f.DurationCritMs)
    )).length, 0);
  const _synWarn = (s.GroupSummaries || []).reduce((acc, gs) =>
    acc + (gs.SyntheticFlows || []).filter(f => f.IsEnabled &&
      f.LastRunPassed === true &&
      f.DurationWarnMs > 0 && f.LastDurationMs != null && f.LastDurationMs >= f.DurationWarnMs &&
      !(f.DurationCritMs > 0 && f.LastDurationMs >= f.DurationCritMs)
    ).length, 0);
  const _synHealthy = (s.GroupSummaries || []).reduce((acc, gs) =>
    acc + (gs.SyntheticFlows || []).filter(f => f.IsEnabled &&
      f.LastRunPassed === true &&
      !(f.DurationWarnMs > 0 && f.LastDurationMs != null && f.LastDurationMs >= f.DurationWarnMs)
    ).length, 0);
  const _synTotal = (s.GroupSummaries || []).reduce((acc, gs) =>
    acc + (gs.SyntheticFlows || []).filter(f => f.IsEnabled).length, 0);
  const _totHealthy  = (s.HealthyCount  ?? 0) + _synHealthy;
  const _totWarning  = (s.WarningCount  ?? 0) + _synWarn;
  const _totCritical = (s.CriticalCount ?? 0) + _synCrit;
  const _totDisabled = (s.UnknownCount  ?? 0);
  const allMonitors = s.Monitors || [];
  const groupSummaries = s.GroupSummaries || [];
  const FONT = { size:10, family:"ui-sans-serif, system-ui, sans-serif" };

  if (chartType === "status-donut") {
    return new Chart(canvasEl, {
      type: "doughnut",
      data: {
        labels: ["Healthy","Warning","Critical","Disabled"],
        datasets: [{ data:[_totHealthy||0,_totWarning||0,_totCritical||0,_totDisabled||0],
          backgroundColor:["#86efac","#fcd34d","#fca5a5","#e2e8f0"], borderWidth:0, hoverOffset:4 }]
      },
      options: { responsive:true, maintainAspectRatio:false, cutout:"72%", plugins:{ legend:{ display:false } } }
    });
  }

  if (chartType === "type-health-bar") {
    const types = ["Server","Infrastructure","Application","Database","Website","PowerShell","Synthetic"];
    const healthy = types.map(t => allMonitors.filter(m=>m.IsEnabled!==false&&m.MonitorType===t&&(m.EffectiveStatus||m.CurrentStatus)==="healthy").length);
    const warning = types.map(t => allMonitors.filter(m=>m.IsEnabled!==false&&m.MonitorType===t&&(m.EffectiveStatus||m.CurrentStatus)==="warning").length);
    const critical = types.map(t => allMonitors.filter(m=>m.IsEnabled!==false&&m.MonitorType===t&&(m.EffectiveStatus||m.CurrentStatus)==="critical").length);
    return new Chart(canvasEl, {
      type: "bar",
      data: { labels: types, datasets: [
        { label:"Healthy", data:healthy, backgroundColor:"#86efac", borderRadius:2, stack:"s" },
        { label:"Warning", data:warning, backgroundColor:"#fcd34d", borderRadius:2, stack:"s" },
        { label:"Critical",data:critical,backgroundColor:"#fca5a5", borderRadius:2, stack:"s" },
      ]},
      options: { indexAxis:"y", responsive:true, maintainAspectRatio:false, layout:{ padding:{ bottom:8 } },
        plugins:{ legend:{ display:false } },
        scales:{ x:{ stacked:true, grid:{ color:"rgba(0,0,0,0.04)" }, ticks:{ stepSize:1, callback:v=>Number.isInteger(v)?v:null, font:FONT } }, y:{ stacked:true, grid:{ display:false }, ticks:{ font:FONT } } } }
    });
  }

  if (chartType === "group-health-bar") {
    const labels = groupSummaries.map(g=>g.GroupName||"Ungrouped");
    const colors = groupSummaries.map(g=>g.CriticalCount>0?"#fca5a5":g.WarningCount>0?"#fcd34d":"#86efac");
    const vals   = groupSummaries.map(g=>g.TotalCount||0);
    return new Chart(canvasEl, {
      type: "bar",
      data: { labels, datasets:[{ data:vals, backgroundColor:colors, borderRadius:3 }] },
      options: { indexAxis:"y", responsive:true, maintainAspectRatio:false,
        plugins:{ legend:{ display:false } },
        scales:{ x:{ grid:{ color:"rgba(0,0,0,0.04)" }, ticks:{ stepSize:1, callback:v=>Number.isInteger(v)?v:null, font:FONT } }, y:{ grid:{ display:false }, ticks:{ font:FONT } } } }
    });
  }

  if (chartType === "slowest-monitors") {
    const sorted = [...allMonitors].filter(m=>m.LastResponseTimeMs!=null).sort((a,b)=>b.LastResponseTimeMs-a.LastResponseTimeMs).slice(0,5);
    return new Chart(canvasEl, {
      type: "bar",
      data: { labels:sorted.map(m=>m.MonitorName), datasets:[{ data:sorted.map(m=>m.LastResponseTimeMs), backgroundColor:"#3b82f6", borderRadius:3 }] },
      options: { indexAxis:"y", responsive:true, maintainAspectRatio:false,
        plugins:{ legend:{ display:false } },
        scales:{ x:{ grid:{ color:"rgba(0,0,0,0.04)" }, ticks:{ callback:v=>v+"ms", font:FONT } }, y:{ grid:{ display:false }, ticks:{ font:FONT } } } }
    });
  }

  if (chartType === "most-alerted") {
    const sorted = [...allMonitors].filter(m=>(m.OpenAlerts||0)>0).sort((a,b)=>(b.OpenAlerts||0)-(a.OpenAlerts||0)).slice(0,5);
    if (sorted.length === 0) return null;
    return new Chart(canvasEl, {
      type: "bar",
      data: { labels:sorted.map(m=>m.MonitorName), datasets:[{ data:sorted.map(m=>m.OpenAlerts||0), backgroundColor:"#fca5a5", borderRadius:3 }] },
      options: { indexAxis:"y", responsive:true, maintainAspectRatio:false,
        plugins:{ legend:{ display:false } },
        scales:{ x:{ grid:{ color:"rgba(0,0,0,0.04)" }, ticks:{ stepSize:1, callback:v=>Number.isInteger(v)?v:null, font:FONT } }, y:{ grid:{ display:false }, ticks:{ font:FONT } } } }
    });
  }

  if (chartType === "response-trend" && responseTrend.length > 0) {
    // Sort ascending: oldest (left) → newest (right)
    const sorted = [...responseTrend].sort((a, b) => {
      if ((a.date||"") !== (b.date||"")) return (a.date||"") < (b.date||"") ? -1 : 1;
      return (a.hour||0) - (b.hour||0);
    });
    const MONTHS = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
    // d.date arrives as a full datetime string (e.g. "2026-03-30T00:00:00") because
    // Dapper maps SQL date → C# DateTime. Use the date portion only for parsing.
    const dateKey = (d) => typeof d.date === "string" ? d.date.slice(0, 10) : "";
    const rtLabels = sorted.map((d, i) => {
      const hh = String(d.hour ?? 0).padStart(2,"0") + ":00";
      const dk = dateKey(d);
      const prevDk = i > 0 ? dateKey(sorted[i-1]) : null;
      if (dk && (!prevDk || dk !== prevDk)) {
        const dt = new Date(dk + "T00:00:00");
        if (!isNaN(dt.getTime()))
          return `${MONTHS[dt.getMonth()]} ${dt.getDate()} ${hh}`;
      }
      return hh;
    });
    return new Chart(canvasEl, {
      type: "line",
      data: { labels: rtLabels, datasets:[{ data: sorted.map(d=>d.avgMs),
        borderColor:"#3b82f6", backgroundColor:"rgba(59,130,246,0.06)", fill:true, tension:0.4, pointRadius:3, borderWidth:2 }] },
      options: { responsive:true, maintainAspectRatio:false, layout:{ padding:{ bottom:4 } },
        plugins:{ legend:{ display:false }, title:{ display:true, text:"Hourly avg — last 24h", font:FONT, color:"#94a3b8", padding:{ bottom:0 } } },
        scales:{ y:{ ticks:{ callback:v=>v+"ms", font:FONT }, grid:{ color:"rgba(0,0,0,0.04)" } },
          x:{ grid:{ display:false }, ticks:{ maxTicksLimit:8, font:FONT, maxRotation:30 },
            title:{ display:true, text:"\u2190 24 hours ago\u2003\u2003Now \u2192", font:{ size:9, family:FONT.family }, color:"#94a3b8", padding:{ top:2 } } } } }
    });
  }

  if (chartType === "alert-trend" && alertTrend.length > 0) {
    return new Chart(canvasEl, {
      type: "bar",
      data: { labels:alertTrend.map(d=>d.day), datasets:[{ data:alertTrend.map(d=>d.count), backgroundColor:"#fca5a5", borderRadius:3 }] },
      options: { responsive:true, maintainAspectRatio:false, plugins:{ legend:{ display:false } },
        scales:{ y:{ ticks:{ stepSize:1, callback:v=>Number.isInteger(v)?v:null, font:FONT }, grid:{ color:"rgba(0,0,0,0.04)" } }, x:{ grid:{ display:false }, ticks:{ font:FONT } } } }
    });
  }

  if (chartType === "uptime-trend" && uptimeTrend.length > 0) {
    return new Chart(canvasEl, {
      type: "line",
      data: { labels:uptimeTrend.map(d=>d.day), datasets:[{ data:uptimeTrend.map(d=>d.avgUptime),
        borderColor:"#86efac", backgroundColor:"rgba(134,239,172,0.1)", fill:true, tension:0.4, pointRadius:3, borderWidth:2 }] },
      options: { responsive:true, maintainAspectRatio:false, layout:{ padding:{ bottom:8 } }, plugins:{ legend:{ display:false } },
        scales:{ y:{ min:0, max:100, ticks:{ callback:v=>v+"%", font:FONT }, grid:{ color:"rgba(0,0,0,0.04)" } }, x:{ grid:{ display:false }, ticks:{ font:FONT } } } }
    });
  }

  return null;
}

function computeSparkPoints(trend) {
  if (!trend || trend.length < 2) return null;
  const vals = trend.map(d => Number(d.avgMs) || 0);
  const mn = Math.min(...vals), mx = Math.max(...vals);
  const range = mx - mn || 1;
  const W = 120, yMin = 3, yMax = 21;
  return trend.map((d, i) => {
    const x = Math.round((i / (trend.length - 1)) * W);
    const y = Math.round(yMax - ((Number(d.avgMs) || 0) - mn) / range * (yMax - yMin));
    return `${x},${y}`;
  }).join(" ");
}

const CHART_LIBRARY = [
  { id:"status-donut",     label:"Status Donut",        alwaysData:true  },
  { id:"type-health-bar",  label:"Health by Type",       alwaysData:true  },
  { id:"group-health-bar", label:"Health by Group",      alwaysData:true  },
  { id:"slowest-monitors", label:"Slowest Monitors",     alwaysData:true  },
  { id:"most-alerted",     label:"Most Alerted",         alwaysData:false },
  { id:"response-trend",   label:"Response Trend (24h)", alwaysData:false },
  { id:"alert-trend",      label:"Alert Trend (7d)",     alwaysData:false },
  { id:"uptime-trend",     label:"Uptime Trend (7d)",    alwaysData:false },
];
const CHART_DEFAULTS = ["status-donut","type-health-bar","group-health-bar","response-trend"];

function DashboardView({ api, onNavigate, onNavigateAlerts, machines, monitoringControl }) {
  const [summary, setSummary]           = useState(null);
  const [responseTrend, setRespTrend]   = useState([]);
  const [alertTrend, setAlertTrend]     = useState([]);
  const [uptimeTrend, setUptimeTrend]   = useState([]);
  const [groupTrends, setGroupTrends]   = useState({});
  const [err, setErr]                   = useState("");
  const [countdown, setCountdown]       = useState(30);
  const [refreshing, setRefreshing]     = useState(false);
  const [flashUpdated, setFlashUpdated] = useState(false);
  const [refreshInterval, setRefreshInterval] = useState(() => {
    const saved = localStorage.getItem("op1_refresh_interval");
    return saved ? parseInt(saved, 10) : 30;
  });
  const [slots, setSlots] = useState(() => [
    localStorage.getItem("op1_chart_slot_1") || CHART_DEFAULTS[0],
    localStorage.getItem("op1_chart_slot_2") || CHART_DEFAULTS[1],
    localStorage.getItem("op1_chart_slot_3") || CHART_DEFAULTS[2],
    localStorage.getItem("op1_chart_slot_4") || CHART_DEFAULTS[3],
  ]);
  const [openPicker, setOpenPicker] = useState(null);
  const [recentAlerts, setRecentAlerts] = useState([]);
  const [runningGroup, setRunningGroup] = useState(null);
  const [hovStat, setHovStat] = useState(null);
  const [aiSummaryOpen, setAiSummaryOpen]       = useState(false);
  const [aiSummaryLoading, setAiSummaryLoading] = useState(false);
  const [aiSummaryData, setAiSummaryData]       = useState(null);
  const [aiSummaryError, setAiSummaryError]     = useState(null);
  const mc = monitoringControl || {};

  const fetchAiSummary = useCallback(async () => {
    setAiSummaryLoading(true);
    setAiSummaryError(null);
    try {
      const result = await api("POST", "/dashboard/ai-summary");
      setAiSummaryData(result?.summary ?? null);
    } catch(e) {
      setAiSummaryError(e.message);
    } finally {
      setAiSummaryLoading(false);
    }
  }, [api]);

  const handleAiPillClick = useCallback(() => {
    if (!aiSummaryOpen) {
      setAiSummaryOpen(true);
      if (!aiSummaryData) fetchAiSummary();
    } else {
      setAiSummaryOpen(false);
    }
  }, [aiSummaryOpen, aiSummaryData, fetchAiSummary]);

  const canvas0 = useRef(null); const canvas1 = useRef(null); const canvas2 = useRef(null); const canvas3 = useRef(null);
  const chart0  = useRef(null); const chart1  = useRef(null); const chart2  = useRef(null); const chart3  = useRef(null);
  const canvasRefs = [canvas0, canvas1, canvas2, canvas3];
  const chartRefs  = [chart0,  chart1,  chart2,  chart3];

  const fetchData = useCallback((signal) => {
    setRefreshing(true);
    Promise.all([
      api("GET", "/dashboard/summary",        undefined, signal),
      api("GET", "/dashboard/response-trend", undefined, signal).catch(() => []),
      api("GET", "/dashboard/alert-trend",    undefined, signal).catch(() => []),
      api("GET", "/dashboard/uptime-trend",   undefined, signal).catch(() => []),
      api("GET", "/alerts/recent?top=20",     undefined, signal).catch(() => []),
    ]).then(([s, rt, at, ut, ra]) => {
      setSummary(s);
      setRespTrend(Array.isArray(rt) ? rt : []);
      setAlertTrend(Array.isArray(at) ? at : []);
      setUptimeTrend(Array.isArray(ut) ? ut : []);
      setRecentAlerts(Array.isArray(ra) ? ra : []);
      setErr("");
      setRefreshing(false);
      setFlashUpdated(true);
      setTimeout(() => setFlashUpdated(false), 1000);
      // Phase 2: fetch group trends in parallel
      const groupIds = [0, ...((s && s.GroupSummaries) || []).map(g => g.GroupID)];
      Promise.all(
        groupIds.map(id => api("GET", `/dashboard/group-trend/${id}`, undefined, signal).catch(() => []))
      ).then(results => {
        const gt = {};
        groupIds.forEach((id, i) => { gt[id] = Array.isArray(results[i]) ? results[i] : []; });
        setGroupTrends(gt);
      }).catch(() => {});
    }).catch(e => {
      if (e.name === "AbortError") return;
      setRefreshing(false);
      setSummary(prev => { if (!prev) setErr(e.message); return prev; });
    });
  }, [api]);

  useEffect(() => { setCountdown(refreshInterval); }, [refreshInterval]);

  useEffect(() => {
    const ctrl = new AbortController();
    fetchData(ctrl.signal);
    const pollId = setInterval(() => { setCountdown(refreshInterval); fetchData(ctrl.signal); }, refreshInterval * 1000);
    const onVisible = () => { if (document.visibilityState === "visible") { setCountdown(refreshInterval); fetchData(ctrl.signal); } };
    document.addEventListener("visibilitychange", onVisible);
    return () => { ctrl.abort(); clearInterval(pollId); document.removeEventListener("visibilitychange", onVisible); };
  }, [fetchData, refreshInterval]);

  useEffect(() => {
    const id = setInterval(() => setCountdown(n => n > 0 ? n - 1 : 0), 1000);
    return () => clearInterval(id);
  }, []);

  useEffect(() => {
    if (!summary || typeof Chart === "undefined") return;
    const destroyAll = () => {
      chartRefs.forEach(r => { if (r.current) { r.current.destroy(); r.current = null; } });
    };
    destroyAll();
    slots.forEach((chartType, i) => {
      if (!canvasRefs[i].current) return;
      chartRefs[i].current = renderDashChart(chartType, canvasRefs[i].current, summary, responseTrend, alertTrend, uptimeTrend);
    });
    return destroyAll;
  }, [summary, responseTrend, alertTrend, uptimeTrend, slots]);

  // Close picker on outside click
  useEffect(() => {
    if (openPicker === null) return;
    const handler = (e) => {
      if (!e.target.closest("[data-chartpicker]")) setOpenPicker(null);
    };
    document.addEventListener("mousedown", handler);
    return () => document.removeEventListener("mousedown", handler);
  }, [openPicker]);

  // Inject ticker keyframe CSS once
  useEffect(() => {
    const id = "op1-ticker-kf";
    if (!document.getElementById(id)) {
      const el = document.createElement("style");
      el.id = id;
      el.textContent = "@keyframes op1-ticker { from { transform:translateX(0) } to { transform:translateX(-50%) } }";
      document.head.appendChild(el);
    }
  }, []);

  const hasData = (chartType) => {
    const lib = CHART_LIBRARY.find(c => c.id === chartType);
    if (!lib) return false;
    if (lib.alwaysData) return true;
    if (chartType === "most-alerted") return (summary?.Monitors||[]).some(m=>(m.OpenAlerts||0)>0);
    if (chartType === "response-trend") return responseTrend.length > 0;
    if (chartType === "alert-trend") return alertTrend.length > 0;
    if (chartType === "uptime-trend") return uptimeTrend.length > 0;
    return false;
  };

  const changeSlot = (slotIdx, chartId) => {
    const newSlots = [...slots];
    newSlots[slotIdx] = chartId;
    setSlots(newSlots);
    localStorage.setItem(`op1_chart_slot_${slotIdx+1}`, chartId);
    setOpenPicker(null);
  };

  const handleIntervalChange = (e) => {
    const v = parseInt(e.target.value, 10);
    setRefreshInterval(v);
    localStorage.setItem("op1_refresh_interval", String(v));
  };

  if (err) return <ErrBox msg={err} />;
  if (!summary) return <Spinner />;

  const s = summary;
  const allMonitors = s.Monitors || [];
  const syntheticCritCount = (s.GroupSummaries || []).reduce((acc, gs) =>
    acc + (gs.SyntheticFlows || []).filter(f => f.IsEnabled && (
      f.LastRunPassed === false ||
      (f.DurationCritMs > 0 && f.LastDurationMs != null && f.LastDurationMs >= f.DurationCritMs)
    )).length, 0);
  const syntheticWarnCount = (s.GroupSummaries || []).reduce((acc, gs) =>
    acc + (gs.SyntheticFlows || []).filter(f => f.IsEnabled &&
      f.LastRunPassed === true &&
      f.DurationWarnMs > 0 && f.LastDurationMs != null && f.LastDurationMs >= f.DurationWarnMs &&
      !(f.DurationCritMs > 0 && f.LastDurationMs >= f.DurationCritMs)
    ).length, 0);
  const syntheticHealthyCount = (s.GroupSummaries || []).reduce((acc, gs) =>
    acc + (gs.SyntheticFlows || []).filter(f => f.IsEnabled &&
      f.LastRunPassed === true &&
      !(f.DurationWarnMs > 0 && f.LastDurationMs != null && f.LastDurationMs >= f.DurationWarnMs)
    ).length, 0);
  const syntheticTotalCount = (s.GroupSummaries || []).reduce((acc, gs) =>
    acc + (gs.SyntheticFlows || []).filter(f => f.IsEnabled).length, 0);
  const totalHealthy   = (s.HealthyCount  ?? 0) + syntheticHealthyCount;
  const totalWarning   = (s.WarningCount  ?? 0) + syntheticWarnCount;
  const totalCritical  = (s.CriticalCount ?? 0) + syntheticCritCount;
  const totalDisabled  = (s.UnknownCount  ?? 0);
  const totalMonitors  = (s.TotalMonitors ?? 0) + syntheticTotalCount;
  const groupSummaries = s.GroupSummaries || [];
  const ungroupedMonitors = allMonitors.filter(m => !m.GroupID && !m.GroupName);
  const hasUngrouped = ungroupedMonitors.length > 0;
  const ugCritical = ungroupedMonitors.filter(m => m.IsEnabled!==false&&(m.EffectiveStatus||m.CurrentStatus) === "critical").length;
  const ugWarning  = ungroupedMonitors.filter(m => m.IsEnabled!==false&&(m.EffectiveStatus||m.CurrentStatus) === "warning").length;
  const ugHealthy  = ungroupedMonitors.filter(m => m.IsEnabled!==false&&(m.EffectiveStatus||m.CurrentStatus) === "healthy").length;
  const grpTopBorder  = (gs) => gs.CriticalCount>0?"#ef4444":gs.WarningCount>0?"#f59e0b":gs.TotalCount===0?"#d1d5db":"#22c55e";
  const grpWorstStatus = (gs) => gs.CriticalCount>0?"critical":gs.WarningCount>0?"warning":gs.TotalCount===0?"unknown":"healthy";
  const statusDotColor = { healthy:"#22c55e", warning:"#f59e0b", critical:"#ef4444", unknown:"#d1d5db" };
  const MON_TYPES = ["Server","Infrastructure","Application","Database","Website","PowerShell","Synthetic"];

  // Ungrouped per-type breakdown — enabled dots first, disabled grey dots after
  const ugByType = MON_TYPES.map(t => {
    const en = ungroupedMonitors.filter(m => m.MonitorType === t && m.IsEnabled !== false && m.IsEnabled !== 0);
    const dis = ungroupedMonitors.filter(m => m.MonitorType === t && (m.IsEnabled === false || m.IsEnabled === 0));
    return { type: t, monitors: [...en, ...dis] };
  }).filter(x => x.monitors.length > 0);

  return (
    <div style={{ height:"calc(100vh - 20px)", display:"flex", flexDirection:"column", gap:0, overflow:"hidden" }}>

      {/* ── Branding header ── */}
      <div style={{ display:"flex", alignItems:"center", justifyContent:"space-between",
        background:"#ffffff", borderBottom:"0.5px solid #f1f5f9", padding:"10px 16px", flexShrink:0 }}>
        <div style={{ display:"flex", alignItems:"center", gap:12 }}>
          <div style={{ width:36, height:36, borderRadius:8, background:"#006D8C", display:"flex", alignItems:"center", justifyContent:"center",
            fontSize:13, fontWeight:700, color:"#ffffff", letterSpacing:"0.02em", flexShrink:0 }}>OP1</div>
          <div>
            <div style={{ fontSize:18, fontWeight:600, color:"#1e293b" }}>
              <span style={{ color:"#006D8C" }}>OP1</span> Operations Portal
            </div>
            <div style={{ fontSize:11, color:"#94a3b8" }}>Enterprise Monitoring · v1.4.457</div>
          </div>
        </div>
        <div style={{ display:"flex", alignItems:"center", gap:8 }}>
          <button onClick={handleAiPillClick}
            style={{ display:"flex", alignItems:"center", gap:5, background: aiSummaryOpen ? "#E0F2F7" : "transparent",
              border:"1px solid #006D8C", borderRadius:20, padding:"5px 12px", cursor:"pointer",
              color:"#006D8C", fontSize:12, fontWeight:600, transition:"background 0.15s" }}
            onMouseEnter={e=>{ if(!aiSummaryOpen) e.currentTarget.style.background="#E0F2F7"; }}
            onMouseLeave={e=>{ if(!aiSummaryOpen) e.currentTarget.style.background="transparent"; }}>
            ⚡ AI Summary
          </button>
          {mc.canManageConfig && mc.monCtrl && (
            <div style={{ position:"relative" }}>
              <button onClick={() => mc.setPausePopover(p => !p)}
                style={{ display:"flex", alignItems:"center", gap:6, background: mc.isPaused ? "rgba(180,83,9,0.08)" : "rgba(0,0,0,0.04)", border: `1px solid ${mc.isPaused?"rgba(180,83,9,0.3)":"rgba(0,0,0,0.1)"}`, borderRadius:20, padding:"5px 12px", cursor:"pointer", color: mc.isPaused ? "#B45309" : "#374151", fontSize:11, fontWeight:600 }}>
                <span style={{ width:7, height:7, borderRadius:"50%", background: mc.isPaused ? "#F59E0B" : "#10B981", flexShrink:0, animation: mc.isPaused ? "pulse 1.5s infinite" : "none" }} />
                {mc.isPaused ? "Monitoring Paused" : "Monitoring Active"}
              </button>
              {mc.pausePopover && (
                <div style={{ position:"absolute", top:"calc(100% + 8px)", right:0, width:280, background:"#FFFFFF", border:`1px solid ${c.border}`, borderRadius:8, boxShadow:"0 8px 24px rgba(0,0,0,0.15)", zIndex:200, padding:16 }}>
                  <div style={{ fontSize:13, fontWeight:700, color:c.text, marginBottom:8 }}>{mc.isPaused ? "Resume Monitoring" : "Pause Monitoring"}</div>
                  <div style={{ fontSize:11, color:c.textDim, marginBottom:10 }}>Machine: <span style={{ fontFamily:"monospace", color:c.text }}>{mc.currentCtrl?.MachineName}</span></div>
                  {!mc.isPaused && <textarea value={mc.pauseReason} onChange={e=>mc.setPauseReason(e.target.value)} placeholder="Reason (optional)" style={{ width:"100%", fontSize:11, padding:6, borderRadius:4, border:"1px solid #e2e5ea", resize:"none", height:48, marginBottom:8 }} />}
                  {mc.isPaused && mc.currentCtrl?.PauseReason && <div style={{ fontSize:11, color:c.textDim, marginBottom:8 }}>Reason: {mc.currentCtrl.PauseReason}</div>}
                  {mc.isPaused && mc.currentCtrl?.PausedAt && <div style={{ fontSize:10, color:c.textDimmer, marginBottom:10 }}>Paused at: {parseUTC(mc.currentCtrl.PausedAt)?.toLocaleString()}</div>}
                  <div style={{ display:"flex", gap:6 }}>
                    <button onClick={mc.togglePause} disabled={mc.pauseBusy} style={{ flex:1, padding:"6px 0", borderRadius:6, border:"none", cursor:"pointer", background: mc.isPaused ? "#16a34a" : "#dc2626", color:"#fff", fontSize:12, fontWeight:600 }}>
                      {mc.pauseBusy ? "..." : mc.isPaused ? "Resume" : "Pause"}
                    </button>
                    <button onClick={() => mc.setPausePopover(false)} style={{ padding:"6px 10px", borderRadius:6, border:"1px solid #e2e5ea", cursor:"pointer", background:"#fff", fontSize:12 }}>Cancel</button>
                  </div>
                </div>
              )}
            </div>
          )}
        </div>
      </div>

      {/* ── Stat bar ── */}
      <div style={{ display:"flex", alignItems:"center", borderBottom:"0.5px solid #e2e5ea", height:28, flexShrink:0, background:"#ffffff" }}>
        {[
          ["TOTAL",          totalMonitors,  "#1e293b",  () => onNavigate && onNavigate(null,null,null,{statusFilter:"all",expandAll:true}),      "View all monitors"],
          ["HEALTHY",        totalHealthy,   "#16a34a",  () => onNavigate && onNavigate(null,null,null,{statusFilter:"healthy",expandAll:true}),   "View healthy monitors"],
          ["WARNING",        totalWarning,   totalWarning > 0 ? "#d97706" : "#1e293b",  () => onNavigate && onNavigate(null,null,null,{statusFilter:"warning",expandAll:true}),   "View warning monitors"],
          ["CRITICAL",       totalCritical,  totalCritical > 0 ? "#dc2626" : "#1e293b", () => onNavigate && onNavigate(null,null,null,{statusFilter:"critical",expandAll:true}), "View critical monitors"],
          ["DISABLED",       totalDisabled,  "#1e293b",  () => onNavigate && onNavigate(null,null,null,{statusFilter:"disabled",expandAll:true}),  "View disabled monitors"],
          ["AVG RESPONSE",   s.AvgResponseTimeMs != null ? `${Number(s.AvgResponseTimeMs).toFixed(0)}ms` : "—", "#1e293b", null, null],
          ["AVG UPTIME 30D", s.AvgUptime30Day != null ? `${Number(s.AvgUptime30Day).toFixed(1)}%` : "—", "#1e293b", null, null],
          ["OPEN ALERTS",    s.OpenAlertCount ?? 0, s.OpenAlertCount > 0 ? "#dc2626" : "#1e293b", () => onNavigateAlerts && onNavigateAlerts(), "View all alerts"],
        ].map(([lbl, val, col, onClick, title]) => (
            <div key={lbl} style={{ padding:"4px 10px", borderRight:"0.5px solid #f1f5f9", display:"flex", alignItems:"center", gap:6, flexShrink:0 }}>
              <span style={{ fontSize:9, color:"#94a3b8", textTransform:"uppercase", letterSpacing:"0.06em" }}>{lbl}</span>
              <span
                title={title||undefined}
                onClick={onClick||undefined}
                onMouseEnter={onClick ? ()=>setHovStat(lbl) : undefined}
                onMouseLeave={onClick ? ()=>setHovStat(null) : undefined}
                style={{ fontSize:13, fontWeight:600, color:col, cursor:onClick?"pointer":"default", textDecoration:onClick&&hovStat===lbl?"underline":"none" }}
              >{val}</span>
            </div>
        ))}
        <div style={{ marginLeft:"auto", display:"flex", alignItems:"center", gap:5, paddingLeft:10, paddingRight:8, borderLeft:"0.5px solid #e2e5ea", flexShrink:0 }}>
          <span style={{ fontSize:9, color:"#94a3b8", textTransform:"uppercase", letterSpacing:"0.06em" }}>Refresh:</span>
          <select value={refreshInterval} onChange={handleIntervalChange}
            style={{ fontSize:10, border:"1px solid #e2e5ea", borderRadius:4, padding:"1px 4px", color:"#374151", background:"#f8fafc", cursor:"pointer" }}>
            <option value={10}>10s</option>
            <option value={30}>30s</option>
            <option value={60}>60s</option>
            <option value={300}>5min</option>
          </select>
          <button title="Refresh now" onClick={() => { setCountdown(refreshInterval); fetchData(); }}
            style={{ background:"none", border:"none", cursor:"pointer", padding:0, lineHeight:1, fontSize:14, color:"#64748b",
              opacity:refreshing?1:0.5, animation:refreshing?"spin 1s linear infinite":"none" }}>&#x27F3;</button>
          <span style={{ fontSize:10, color:flashUpdated?"#16a34a":refreshing?"#3b82f6":"#64748b" }}>
            {flashUpdated ? "✓" : refreshing ? "…" : `${countdown}s`}
          </span>
        </div>
      </div>

      {/* ── AI Summary panel ── */}
      {aiSummaryOpen && (
        <div style={{ flexShrink:0, background:"#fff", borderBottom:"1px solid #D8CCBA", padding:"16px 20px" }}>
          {/* Header row */}
          <div style={{ display:"flex", alignItems:"center", justifyContent:"space-between", marginBottom:12 }}>
            <div style={{ display:"flex", alignItems:"center", gap:8 }}>
              <div style={{ width:24, height:24, borderRadius:4, background:"#1E2B3C", display:"flex", alignItems:"center", justifyContent:"center", fontSize:13, flexShrink:0 }}>⚡</div>
              <span style={{ fontSize:13, fontWeight:700, color:"#1E2B3C" }}>AI Dashboard Summary</span>
              {aiSummaryData?.generatedAt && (
                <span style={{ fontFamily:"'DM Mono',monospace", fontSize:10, color:"#94a3b8" }}>
                  Generated {new Date(aiSummaryData.generatedAt).toLocaleTimeString([], {hour:"2-digit", minute:"2-digit"})}
                </span>
              )}
            </div>
            <div style={{ display:"flex", alignItems:"center", gap:12 }}>
              <button onClick={fetchAiSummary} disabled={aiSummaryLoading}
                style={{ fontFamily:"'DM Mono',monospace", fontSize:11, color:"#006D8C", background:"none", border:"none", cursor:"pointer", textDecoration:"underline", padding:0 }}>
                {aiSummaryLoading ? "Refreshing…" : "Refresh"}
              </button>
              <button onClick={() => setAiSummaryOpen(false)}
                style={{ background:"none", border:"none", cursor:"pointer", fontSize:16, color:"#94a3b8", lineHeight:1, padding:0 }}>×</button>
            </div>
          </div>

          {/* Loading state */}
          {aiSummaryLoading && !aiSummaryData && (
            <div style={{ display:"flex", alignItems:"center", justifyContent:"center", gap:10, padding:"20px 0", color:"#64748b", fontSize:13 }}>
              <span style={{ display:"inline-block", width:16, height:16, border:"2px solid #e2e5ea", borderTopColor:"#006D8C", borderRadius:"50%", animation:"spin 1s linear infinite" }} />
              Analysing dashboard data…
            </div>
          )}

          {/* Error state */}
          {aiSummaryError && !aiSummaryLoading && (
            <div style={{ color:"#D95C5C", fontSize:12, padding:"8px 0" }}>⚠ {aiSummaryError}</div>
          )}

          {/* Loaded state */}
          {aiSummaryData && !aiSummaryLoading && (() => {
            const d = aiSummaryData;
            const dotCol = d.statusColour === "healthy" ? "#008C6F" : d.statusColour === "critical" ? "#D95C5C" : "#E89A2E";
            const healthy  = summary?.HealthyCount  ?? 0;
            const critical = summary?.CriticalCount ?? 0;
            const openAlerts = summary?.OpenAlertCount ?? 0;
            return (
              <div>
                {/* Stat pills row */}
                <div style={{ display:"flex", gap:10, marginBottom:12 }}>
                  {[
                    { label:"Overall Status", content: (
                      <span style={{ display:"flex", alignItems:"center", gap:6, fontSize:12, color:"#1E2B3C" }}>
                        <span style={{ width:8, height:8, borderRadius:"50%", background:dotCol, flexShrink:0 }} />
                        {d.overallStatus}
                      </span>
                    )},
                    { label:"Monitors", content: (
                      <span style={{ fontSize:12 }}>
                        <span style={{ color:"#008C6F", fontWeight:600 }}>{healthy} healthy</span>
                        {critical > 0 && <span style={{ color:"#D95C5C", fontWeight:600 }}> · {critical} critical</span>}
                      </span>
                    )},
                    { label:"Open Alerts", content: (
                      <span style={{ fontSize:12, fontWeight:600, color: openAlerts > 0 ? "#D95C5C" : "#008C6F" }}>
                        {openAlerts}
                      </span>
                    )},
                  ].map(pill => (
                    <div key={pill.label} style={{ flex:1, background:"#F5F0E8", border:"1px solid #D8CCBA", borderRadius:6, padding:"10px 12px" }}>
                      <div style={{ fontSize:9, fontWeight:600, color:"#94a3b8", textTransform:"uppercase", letterSpacing:"0.06em", marginBottom:4 }}>{pill.label}</div>
                      {pill.content}
                    </div>
                  ))}
                </div>

                {/* Active Issues */}
                <div style={{ background:"#F5F0E8", border:"1px solid #D8CCBA", borderLeft:"3px solid #E89A2E", borderRadius:4, padding:"10px 12px", marginBottom:10 }}>
                  <div style={{ fontFamily:"'DM Mono',monospace", fontSize:9, fontWeight:600, color:"#E89A2E", textTransform:"uppercase", letterSpacing:"0.08em", marginBottom:6 }}>Active Issues</div>
                  {(!d.issues || d.issues.length === 0) ? (
                    <div style={{ fontSize:12, color:"#64748b" }}>No active issues detected — all monitors healthy</div>
                  ) : (
                    <div style={{ display:"flex", flexDirection:"column", gap:4 }}>
                      {d.issues.map((iss, i) => (
                        <div key={i} style={{ display:"flex", alignItems:"flex-start", gap:6, fontSize:12 }}>
                          <span style={{ color: iss.severity === "critical" ? "#D95C5C" : "#E89A2E", fontSize:13, lineHeight:1.3, flexShrink:0 }}>
                            {iss.severity === "critical" ? "!" : "~"}
                          </span>
                          <span><strong style={{ color:"#1E2B3C" }}>{iss.monitorName}</strong>{iss.groupName ? <span style={{ color:"#94a3b8" }}> · {iss.groupName}</span> : null} — {iss.description}</span>
                        </div>
                      ))}
                    </div>
                  )}
                </div>

                {/* Recommended Actions */}
                <div style={{ background:"#F5F0E8", border:"1px solid #D8CCBA", borderLeft:"3px solid #006D8C", borderRadius:4, padding:"10px 12px", marginBottom:10 }}>
                  <div style={{ fontFamily:"'DM Mono',monospace", fontSize:9, fontWeight:600, color:"#006D8C", textTransform:"uppercase", letterSpacing:"0.08em", marginBottom:6 }}>Recommended Actions</div>
                  <div style={{ display:"flex", flexDirection:"column", gap:3 }}>
                    {(d.recommendations || []).map((rec, i) => (
                      <div key={i} style={{ display:"flex", gap:6, fontSize:12, color:"#1E2B3C" }}>
                        <span style={{ fontFamily:"'DM Mono',monospace", color:"#006D8C", fontWeight:600, flexShrink:0 }}>{i+1}.</span>
                        {rec}
                      </div>
                    ))}
                  </div>
                </div>

                {/* Footer */}
                <div style={{ display:"flex", justifyContent:"flex-end", alignItems:"center", gap:5 }}>
                  <span style={{ width:6, height:6, borderRadius:"50%", background:"#006D8C", display:"inline-block", animation:"pulse 2s infinite" }} />
                  <span style={{ fontFamily:"'DM Mono',monospace", fontSize:10, color:"#94a3b8" }}>Powered by Claude · claude-haiku-4-5</span>
                </div>
              </div>
            );
          })()}
        </div>
      )}

      {/* ── Body: zone row + ticker ── */}
      <div style={{ flex:1, minHeight:0, display:"flex", flexDirection:"column", overflow:"hidden" }}>

        {/* ── Zone row ── */}
        <div style={{ flex:1, minHeight:0, display:"flex", overflow:"hidden" }}>

          {/* ── Zone Center: Monitor groups ── */}
          <div style={{ flex:1, minWidth:0, overflowY:"auto", padding:"8px 10px", borderRight:"0.5px solid #e2e5ea" }}>
            <div style={{ fontSize:9, fontWeight:600, color:"#64748b", letterSpacing:"0.08em", textTransform:"uppercase", marginBottom:6 }}>Monitor Groups</div>
            <div style={{ display:"grid", gridTemplateColumns:"repeat(5,1fr)", gap:5, alignContent:"start" }}>
              {groupSummaries.map(gs => {
                const ws = grpWorstStatus(gs);
                const dc = statusDotColor[ws] || "#d1d5db";
                const grpMonitors = allMonitors.filter(m => m.GroupID === gs.GroupID);
                const typeRows = MON_TYPES.map(t => {
                  const en = grpMonitors.filter(m => m.MonitorType === t && m.IsEnabled !== false && m.IsEnabled !== 0);
                  const dis = grpMonitors.filter(m => m.MonitorType === t && (m.IsEnabled === false || m.IsEnabled === 0));
                  return { type: t, items: [...en, ...dis] };
                }).filter(x => x.items.length > 0);
                const sparkPts = computeSparkPoints(groupTrends[gs.GroupID] || []);
                return (
                  <div key={gs.GroupID}
                    onClick={() => onNavigate && onNavigate(gs.GroupID)}
                    style={{ background:"#ffffff", border:"0.5px solid #e2e5ea", borderRadius:7, borderTop:`3px solid ${grpTopBorder(gs)}`, padding:"6px 9px", minWidth:0, cursor:"pointer", display:"flex", flexDirection:"column" }}
                    onMouseEnter={e => e.currentTarget.style.background="#f8fafc"}
                    onMouseLeave={e => e.currentTarget.style.background="#ffffff"}>
                    <div style={{ display:"flex", alignItems:"center", justifyContent:"space-between", flexWrap:"wrap", gap:2, marginBottom:4 }}>
                      <span style={{ fontSize:11, fontWeight:600, color:"#1e293b", minWidth:0, overflow:"hidden", textOverflow:"ellipsis", whiteSpace:"nowrap" }}>{gs.GroupName}</span>
                      <div style={{ display:"flex", alignItems:"center", gap:3, flexShrink:0 }}>
                        <span style={{ width:6, height:6, borderRadius:"50%", background:dc }} />
                        <span style={{ fontSize:9, color:dc, fontWeight:600, textTransform:"uppercase" }}>{ws}</span>
                      </div>
                    </div>
                    <div style={{ flex:1 }}>
                    <div style={{ borderBottom:"0.5px solid #f1f5f9", margin:"4px 0" }} />
                    {gs.SubGroups && gs.SubGroups.length > 0 && (
                      <div>
                        {gs.SubGroups.map((sub, si) => {
                          const subColor = sub.WorstStatus === "healthy" ? "#22c55e" : sub.WorstStatus === "warning" ? "#f59e0b" : sub.WorstStatus === "critical" ? "#ef4444" : "#9CA3AF";
                          return (
                            <div key={si} style={{ display:"flex", alignItems:"center", gap:5, marginBottom:2, justifyContent:"space-between", minWidth:0, overflow:"hidden" }}>
                              <div style={{ display:"flex", alignItems:"center", gap:4, flex:1, minWidth:0 }}>
                                <span style={{ width:8, height:8, borderRadius:2, background:subColor, flexShrink:0 }} />
                                <span style={{ fontSize:10, color:"#374151", fontWeight:500, overflow:"hidden", textOverflow:"ellipsis", whiteSpace:"nowrap", flex:1, minWidth:0 }}>{sub.GroupName}</span>
                              </div>
                              {(() => {
                                const sgItems = sub.MonitorsByType || [];
                                const sgTotal = sgItems.length;
                                const sgCrit  = sgItems.filter(m => (m.Status||"") === "critical").length;
                                const sgWarn  = sgItems.filter(m => (m.Status||"") === "warning").length;
                                const sgDis   = sgItems.filter(m => m.IsEnabled === false).length;
                                const sgDots  = sgTotal <= 10
                                  ? sgItems.map(m => ({ color: m.IsEnabled===false?"#9CA3AF":(m.Status||"")==="critical"?"#ef4444":(m.Status||"")==="warning"?"#f59e0b":(m.Status||"")==="healthy"?"#22c55e":"#9CA3AF", title: m.IsEnabled===false?"disabled":(m.Status||"unknown") }))
                                  : (() => {
                                      const cd = sgCrit > 0 ? Math.max(1, Math.round(sgCrit/sgTotal*10)) : 0;
                                      const wd = sgWarn > 0 ? Math.max(1, Math.min(Math.round(sgWarn/sgTotal*10), 10-cd)) : 0;
                                      const dd = sgDis  > 0 ? Math.max(1, Math.min(Math.round(sgDis/sgTotal*10),  10-cd-wd)) : 0;
                                      const hd = Math.max(0, 10-cd-wd-dd);
                                      return [...Array(cd).fill({color:"#ef4444"}),...Array(wd).fill({color:"#f59e0b"}),...Array(dd).fill({color:"#9CA3AF"}),...Array(Math.max(0,hd)).fill({color:"#22c55e"})];
                                    })();
                                const sgTip = sgTotal > 10 ? `${sgTotal} monitors — ${sgCrit} critical, ${sgWarn} warning, ${sgTotal-sgCrit-sgWarn-sgDis} healthy, ${sgDis} disabled` : null;
                                return (
                                  <div style={{ display:"flex", gap:2, alignItems:"center" }} title={sgTip||undefined}>
                                    {sgDots.map((d, idx) => (
                                      <span key={idx} title={d.title||undefined} style={{ width:7, height:7, borderRadius:"50%", flexShrink:0, background:d.color }} />
                                    ))}
                                  </div>
                                );
                              })()}
                            </div>
                          );
                        })}
                        {(typeRows.length > 0 || (gs.SyntheticFlows && gs.SyntheticFlows.length > 0)) && (
                          <div style={{ borderBottom:"0.5px dashed #e2e5ea", margin:"3px 0" }} />
                        )}
                      </div>
                    )}
                    {typeRows.map(({ type, items }) => {
                      const total = items.length;
                      const crit  = items.filter(m => !m.IsEnabled ? false : (m.EffectiveStatus||m.CurrentStatus) === "critical").length;
                      const warn  = items.filter(m => !m.IsEnabled ? false : (m.EffectiveStatus||m.CurrentStatus) === "warning").length;
                      const dis   = items.filter(m => m.IsEnabled === false).length;
                      const hlthy = total - crit - warn - dis;
                      const dots  = total <= 10
                        ? items.map(m => {
                            const isDisabledMon = m.IsEnabled === false;
                            const st = isDisabledMon ? "disabled" : (m.EffectiveStatus || m.CurrentStatus || "unknown");
                            return { color: isDisabledMon?"#9CA3AF":st==="critical"?"#ef4444":st==="warning"?"#f59e0b":st==="healthy"?"#22c55e":"#9CA3AF", mon: m, individual: true };
                          })
                        : (() => {
                            const critDots  = crit > 0 ? Math.max(1, Math.round(crit / total * 10)) : 0;
                            const warnDots  = warn > 0 ? Math.max(1, Math.min(Math.round(warn / total * 10), 10 - critDots)) : 0;
                            const disDots   = dis  > 0 ? Math.max(1, Math.min(Math.round(dis  / total * 10), 10 - critDots - warnDots)) : 0;
                            const hlthDots  = Math.max(0, 10 - critDots - warnDots - disDots);
                            return [
                              ...Array(critDots).fill({ color:"#ef4444", individual:false }),
                              ...Array(warnDots).fill({ color:"#f59e0b", individual:false }),
                              ...Array(disDots).fill({ color:"#9CA3AF", individual:false }),
                              ...Array(Math.max(0,hlthDots)).fill({ color:"#22c55e", individual:false }),
                            ];
                          })();
                      const tooltipSummary = total > 10
                        ? `${total} ${type} monitors — ${crit} critical, ${warn} warning, ${hlthy} healthy, ${dis} disabled`
                        : null;
                      return (
                        <div key={type} style={{ display:"flex", alignItems:"center", justifyContent:"space-between", marginBottom:2 }}>
                          <span style={{ fontSize:10, color:"#64748b" }}>{type}</span>
                          <div style={{ display:"flex", gap:2, alignItems:"center" }} title={tooltipSummary||undefined}>
                            {dots.map((d, idx) => (
                              <span key={idx}
                                title={d.individual ? `${d.mon.MonitorName} — ${d.mon.EffectiveStatus||"unknown"}` : tooltipSummary}
                                onClick={d.individual ? (ev => { ev.stopPropagation(); onNavigate && onNavigate(null, d.mon.MonitorID, d.mon); }) : undefined}
                                style={{ width:7, height:7, borderRadius:"50%", cursor:d.individual?"pointer":"default", flexShrink:0,
                                  background: d.color }} />
                            ))}
                          </div>
                        </div>
                      );
                    })}
                    {gs.SyntheticFlows && gs.SyntheticFlows.length > 0 && (
                      <div style={{ display:"flex", alignItems:"center", justifyContent:"space-between", marginBottom:2 }}>
                        <span style={{ fontSize:10, color:"#64748b" }}>Synthetic</span>
                        <div style={{ display:"flex", gap:2 }}>
                          {gs.SyntheticFlows.map((f, idx) => {
                            const dotColor = (!f.IsEnabled || f.LastRunPassed == null) ? "#9CA3AF" : !f.LastRunPassed ? "#ef4444" : (f.DurationCritMs > 0 && f.LastDurationMs != null && f.LastDurationMs >= f.DurationCritMs) ? "#ef4444" : (f.DurationWarnMs > 0 && f.LastDurationMs != null && f.LastDurationMs >= f.DurationWarnMs) ? "#F59E0B" : "#22c55e";
                            const label = !f.IsEnabled ? "disabled" : f.LastRunPassed == null ? "no runs" : !f.LastRunPassed ? "failing" : (f.DurationCritMs > 0 && f.LastDurationMs != null && f.LastDurationMs >= f.DurationCritMs) ? "slow (critical)" : (f.DurationWarnMs > 0 && f.LastDurationMs != null && f.LastDurationMs >= f.DurationWarnMs) ? "slow (warning)" : "passing";
                            return (
                              <span key={idx}
                                title={`${f.FlowName} — ${label}`}
                                style={{ width:7, height:7, borderRadius:"50%", background:dotColor }} />
                            );
                          })}
                        </div>
                      </div>
                    )}
                    <div style={{ width:"100%", height:24, margin:"4px 0" }}>
                      <svg viewBox="0 0 120 24" width="100%" height="100%" preserveAspectRatio="none">
                        {sparkPts
                          ? <polyline points={sparkPts} fill="none" stroke={dc} strokeWidth="1.5" strokeLinejoin="round" strokeLinecap="round" />
                          : <line x1="0" y1="12" x2="120" y2="12" stroke="#e2e5ea" strokeWidth="1.5" />}
                      </svg>
                    </div>
                    </div>
                    <div style={{ display:"flex", alignItems:"center", justifyContent:"space-between", marginTop:"auto" }}>
                      <span style={{ fontSize:9, color:"#94a3b8" }}>
                        {runningGroup === gs.GroupID
                          ? `Running ${grpMonitors.length} check${grpMonitors.length!==1?"s":""}…`
                          : gs.SubGroups && gs.SubGroups.length > 0
                            ? `${(gs.SubGroups||[]).reduce((a,s)=>a+s.TotalCount,0)+grpMonitors.length+(gs.SyntheticFlows||[]).length} monitors across ${gs.SubGroups.length} sub-group${gs.SubGroups.length!==1?"s":""} · Click to view`
                            : `${grpMonitors.length+(gs.SyntheticFlows||[]).length} monitor${(grpMonitors.length+(gs.SyntheticFlows||[]).length)!==1?"s":""} · Click to view`}
                      </span>
                      <div style={{ display:"flex", gap:4 }} onClick={e=>e.stopPropagation()}>
                        <button
                          disabled={runningGroup !== null}
                          onClick={async (e) => {
                            e.stopPropagation();
                            setRunningGroup(gs.GroupID);
                            try {
                              await api("POST", `/monitors/groups/${gs.GroupID}/run`);
                              fetchData();
                            } catch(_) {}
                            finally { setRunningGroup(null); }
                          }}
                          style={{
                            fontSize:9, padding:"1px 6px", border:"0.5px solid #94a3b8", borderRadius:3,
                            background:"#ffffff", color:"#64748b", cursor: runningGroup !== null ? "not-allowed" : "pointer",
                            opacity: runningGroup !== null ? 0.5 : 1, lineHeight:1.6,
                          }}
                          title="Run all checks in this group now"
                        >▶ Run All</button>
                      </div>
                    </div>
                  </div>
                );
              })}
              {hasUngrouped && (() => {
                const ugSparkPts = computeSparkPoints(groupTrends[0] || []);
                return (
                  <div onClick={() => onNavigate && onNavigate("ungrouped")}
                    style={{ background:"#ffffff", border:"0.5px solid #e2e5ea", borderRadius:7, borderTop:"2px dashed #d1d5db", padding:"6px 9px", minWidth:0, cursor:"pointer", display:"flex", flexDirection:"column" }}
                    onMouseEnter={e => e.currentTarget.style.background="#f8fafc"}
                    onMouseLeave={e => e.currentTarget.style.background="#ffffff"}>
                    <div style={{ fontSize:11, fontWeight:600, color:"#64748b", marginBottom:4 }}>Ungrouped</div>
                    <div style={{ flex:1 }}>
                    <div style={{ borderBottom:"0.5px solid #f1f5f9", margin:"4px 0" }} />
                    {ugByType.slice(0,4).map(({ type, monitors }) => (
                      <div key={type} style={{ display:"flex", alignItems:"center", justifyContent:"space-between", marginBottom:2 }}>
                        <span style={{ fontSize:10, color:"#64748b" }}>{type}</span>
                        <div style={{ display:"flex", gap:2 }}>
                          {monitors.map((m, idx) => {
                            const isDisabledMon = m.IsEnabled === false;
                            const st = isDisabledMon ? "disabled" : (m.EffectiveStatus || m.CurrentStatus || "unknown");
                            return (
                              <span key={idx}
                                title={`${m.MonitorName} — ${st}`}
                                onClick={ev => { ev.stopPropagation(); onNavigate && onNavigate(null, m.MonitorID, m); }}
                                style={{ width:7, height:7, borderRadius:"50%", cursor:"pointer",
                                  background: isDisabledMon?"#9CA3AF":st==="healthy"?"#22c55e":st==="warning"?"#f59e0b":st==="critical"?"#ef4444":"#9CA3AF" }} />
                            );
                          })}
                        </div>
                      </div>
                    ))}
                    <div style={{ width:"100%", height:24, margin:"4px 0" }}>
                      <svg viewBox="0 0 120 24" width="100%" height="100%" preserveAspectRatio="none">
                        {ugSparkPts
                          ? <polyline points={ugSparkPts} fill="none" stroke="#d1d5db" strokeWidth="1.5" strokeLinejoin="round" strokeLinecap="round" />
                          : <line x1="0" y1="12" x2="120" y2="12" stroke="#e2e5ea" strokeWidth="1.5" />}
                      </svg>
                    </div>
                    </div>
                    <div style={{ display:"flex", alignItems:"center", justifyContent:"space-between", marginTop:"auto" }} onClick={e=>e.stopPropagation()}>
                      <span style={{ fontSize:9, color:"#94a3b8" }}>
                        {runningGroup === "ungrouped"
                          ? `Running ${ungroupedMonitors.length} check${ungroupedMonitors.length!==1?"s":""}…`
                          : `${ungroupedMonitors.length} monitor${ungroupedMonitors.length!==1?"s":""} · Click to view`}
                      </span>
                      <button
                        disabled={runningGroup !== null}
                        onClick={async (e) => {
                          e.stopPropagation();
                          setRunningGroup("ungrouped");
                          try {
                            for (const m of ungroupedMonitors) {
                              await api("POST", `/monitors/${m.MonitorID}/check`);
                            }
                            fetchData();
                          } catch(_) {}
                          finally { setRunningGroup(null); }
                        }}
                        style={{
                          fontSize:9, padding:"1px 6px", border:"0.5px solid #94a3b8", borderRadius:3,
                          background:"#ffffff", color:"#64748b", cursor: runningGroup !== null ? "not-allowed" : "pointer",
                          opacity: runningGroup !== null ? 0.5 : 1, lineHeight:1.6,
                        }}
                        title="Run all checks for ungrouped monitors now"
                      >&#x25B6; Run All</button>
                    </div>
                  </div>
                );
              })()}
            </div>
          </div>

          {/* ── Zone Right: Health overview ── */}
          <div style={{ width:320, flexShrink:0, display:"flex", flexDirection:"column", overflow:"hidden", padding:"8px 10px", gap:6 }}>
            <div style={{ display:"flex", alignItems:"center", justifyContent:"space-between", flexShrink:0 }}>
              <span style={{ fontSize:9, fontWeight:600, color:"#64748b", letterSpacing:"0.08em", textTransform:"uppercase" }}>Health Overview</span>
              <button onClick={() => setOpenPicker(openPicker === 0 ? null : 0)}
                style={{ fontSize:9, color:"#94a3b8", background:"none", border:"none", cursor:"pointer", padding:"2px 4px" }}>
                &#x229E; Customize
              </button>
            </div>
            {slots.map((chartType, slotIdx) => {
              const libEntry = CHART_LIBRARY.find(x => x.id === chartType) || { label: chartType };
              const isDonut = chartType === "status-donut";
              const pickerOpen = openPicker === slotIdx;
              return (
                <div key={slotIdx} data-chartpicker style={{ flex:1, minHeight:0, background:"#ffffff", border:"0.5px solid #e2e5ea", borderRadius:8, padding:"6px 8px", display:"flex", flexDirection:"column", position:"relative", overflow:"hidden" }}>
                  <div style={{ display:"flex", alignItems:"center", justifyContent:"space-between", marginBottom:4, flexShrink:0 }}>
                    <span style={{ fontSize:9, fontWeight:600, color:"#64748b", letterSpacing:"0.08em", textTransform:"uppercase" }}>{libEntry.label}</span>
                    <button onClick={() => setOpenPicker(pickerOpen ? null : slotIdx)}
                      style={{ fontSize:9, color:"#94a3b8", background:"none", border:"none", cursor:"pointer", padding:"2px 4px", borderRadius:3 }}>
                      &#x229E;
                    </button>
                  </div>
                  {pickerOpen && (
                    <div style={{ position:"absolute", top:28, right:0, zIndex:100, width:200, background:"#ffffff", border:"0.5px solid #e2e5ea", borderRadius:8, boxShadow:"0 4px 16px rgba(0,0,0,0.12)", padding:8 }}>
                      {CHART_LIBRARY.map(lib => {
                        const available = hasData(lib.id);
                        const selected = slots[slotIdx] === lib.id;
                        return (
                          <div key={lib.id} onClick={() => available && changeSlot(slotIdx, lib.id)}
                            style={{ padding:"6px 8px", borderRadius:4, cursor:available?"pointer":"default",
                              background:selected?"#f0f9ff":"transparent", color:available?"#1e293b":"#94a3b8",
                              display:"flex", alignItems:"center", justifyContent:"space-between" }}>
                            <span style={{ fontSize:12 }}>{lib.label}</span>
                            {!available && <span style={{ fontSize:10, color:"#94a3b8" }}>No data</span>}
                          </div>
                        );
                      })}
                    </div>
                  )}
                  {isDonut ? (
                    <div style={{ flex:1, minHeight:0, display:"flex", alignItems:"center", gap:8, overflow:"hidden" }}>
                      <div style={{ width:70, height:70, flexShrink:0, position:"relative" }}>
                        <canvas ref={canvasRefs[slotIdx]} style={{ display:"block", width:"100%", height:"100%" }} />
                        {summary && (
                          <div style={{ position:"absolute", inset:0, display:"flex", alignItems:"center", justifyContent:"center", pointerEvents:"none" }}>
                            <div style={{ textAlign:"center" }}>
                              <div style={{ fontSize:14, fontWeight:600, color:"#1e293b", lineHeight:1 }}>{totalMonitors}</div>
                              <div style={{ fontSize:8, color:"#94a3b8" }}>total</div>
                            </div>
                          </div>
                        )}
                      </div>
                      <div style={{ flex:1, minWidth:0 }}>
                        {[["Healthy",totalHealthy,"#86efac"],["Warning",totalWarning,"#fcd34d"],["Critical",totalCritical,"#fca5a5"],["Disabled",totalDisabled,"#e2e8f0"]].map(([lbl,val,col]) => (
                          <div key={lbl} style={{ display:"flex", alignItems:"center", gap:4, marginBottom:3 }}>
                            <span style={{ width:6, height:6, borderRadius:"50%", background:col, flexShrink:0 }} />
                            <span style={{ fontSize:10, color:"#64748b", flex:1 }}>{lbl}</span>
                            <span style={{ fontSize:10, fontWeight:600, color:"#1e293b" }}>{val}</span>
                          </div>
                        ))}
                      </div>
                    </div>
                  ) : (
                    <div style={{ flex:1, minHeight:0, position:"relative", paddingBottom:4 }}>
                      <canvas ref={canvasRefs[slotIdx]} style={{ display:"block", width:"100%", height:"100%" }} />
                      {!hasData(chartType) && (
                        <div style={{ position:"absolute", inset:0, display:"flex", alignItems:"center", justifyContent:"center", fontSize:11, color:"#94a3b8" }}>No data yet</div>
                      )}
                    </div>
                  )}
                </div>
              );
            })}
          </div>

        </div>

        {/* ── News ticker ── */}
        <div style={{ height:28, background:"#1e2b3c", overflow:"hidden", display:"flex", alignItems:"center", flexShrink:0 }}>
          {recentAlerts.length > 0 ? (
            <div style={{ display:"inline-flex", animation:`op1-ticker ${TICKER_DURATION_SEC}s linear infinite`, whiteSpace:"nowrap" }}>
              {[...recentAlerts, ...recentAlerts].map((item, i) => (
                <span key={i} style={{ padding:"0 24px", fontSize:11, color:"rgba(255,255,255,0.85)", borderRight:"1px solid rgba(255,255,255,0.1)", display:"inline-flex", alignItems:"center", gap:5 }}>
                  <span style={{ color:(item.Status||"").toLowerCase()==="critical"?"#ef4444":(item.Status||"").toLowerCase()==="warning"?"#f59e0b":"#22c55e", fontSize:8 }}>&#9679;</span>
                  {item.MonitorName}{item.Message ? ` \u00b7 ${item.Message}` : ""}
                  {item.CreatedAt && <span style={{ color:"rgba(255,255,255,0.4)", fontSize:10, marginLeft:4 }}>{parseUTC(item.CreatedAt)?.toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"})}</span>}
                </span>
              ))}
            </div>
          ) : (
            <span style={{ fontSize:11, color:"rgba(255,255,255,0.35)", paddingLeft:16, letterSpacing:"0.04em" }}>No recent alerts</span>
          )}
        </div>

      </div>

    </div>
  );
}

// ═══ TOGGLE ═══



// ── SmallToggle — top-level so all views can use it ──
const SmallToggle = ({ checked, onClick }) => (
  <div onClick={onClick}
    style={{ width:28, height:15, borderRadius:8, background:checked?c.blue:c.border, position:"relative", transition:"background 0.2s", cursor:"pointer", flexShrink:0 }}>
    <div style={{ position:"absolute", top:2, left:checked?13:2, width:11, height:11, borderRadius:"50%", background:"#fff", transition:"left 0.2s" }} />
  </div>
);

// ═══ TOPOLOGY ═══
const isAncestorOrDescendant = (node, groupFilter, byId) => {
  let cur = node;
  while (cur.ParentMonitorID && byId[cur.ParentMonitorID]) {
    cur = byId[cur.ParentMonitorID];
    if (cur.GroupName === groupFilter) return true;
  }
  const hasDescInGroup = (n) => {
    if (n.GroupName === groupFilter) return true;
    return (n.children||[]).some(ch => hasDescInGroup(ch));
  };
  return hasDescInGroup(node);
};

function TopologyView({ api, monitoringControl, onNavigate }) {
  const [monitors, setMonitors]         = useState([]);
  const [err, setErr]                   = useState("");
  const [topoConfirmModal, setTopoConfirmModal] = useState(null);
  const [loading, setLoading]           = useState(true);
  const [recentAlerts, setRecentAlerts] = useState([]);
  const [topoGroupFilter, setTopoGroupFilter]             = useState("all");
  const [topoTransform, setTopoTransform]                 = useState({ x:0, y:0, scale:1 });
  const [topoInitialTransform, setTopoInitialTransform]   = useState({ x:0, y:0, scale:1 });
  const [topoPanning, setTopoPanning]   = useState(false);
  const [topoPanStart, setTopoPanStart] = useState({ x:0, y:0 });
  const [topoLinkMode, setTopoLinkMode] = useState(false);
  const [topoLinkSource, setTopoLinkSource] = useState(null);
  const [topoReloadKey, setTopoReloadKey] = useState(0);
  const topoContainerRef = useRef(null);

  const { isPaused:monIsPaused, canManageConfig, pausePopover, setPausePopover,
          pauseReason, setPauseReason, pauseBusy, togglePause,
          currentCtrl, currentMachine } = monitoringControl || {};

  useEffect(()=>{
    const id = "op1-ticker-kf";
    if (!document.getElementById(id)) {
      const el = document.createElement("style"); el.id = id;
      el.textContent = "@keyframes op1-ticker { from{transform:translateX(0)} to{transform:translateX(-50%)} }";
      document.head.appendChild(el);
    }
    setLoading(true);
    api("GET","/monitors").then(r=>{ setMonitors(Array.isArray(r)?r:[]); setLoading(false); }).catch(e=>{ setErr(e.message); setLoading(false); });
    api("GET","/alerts/recent?top=20").then(r=>setRecentAlerts(Array.isArray(r)?r:[])).catch(()=>{});
  },[api, topoReloadKey]);

  /* ── Build tree ── */
  const topoById = {};
  monitors.forEach(m => { topoById[m.MonitorID] = { ...m, children:[] }; });
  const topoRoots = [];
  monitors.forEach(m => {
    if (m.ParentMonitorID && topoById[m.ParentMonitorID])
      topoById[m.ParentMonitorID].children.push(topoById[m.MonitorID]);
    else
      topoRoots.push(topoById[m.MonitorID]);
  });

  /* ── Layout ── */
  const NODE_W = 150, NODE_H = 130;
  const assignTopoPos = (node, x, y) => {
    node.y = y;
    if (!node.children.length) { node.x = x + NODE_W/2; return NODE_W; }
    let cx = x;
    node.children.forEach(child => { cx += assignTopoPos(child, cx, y + NODE_H); });
    node.x = x + (cx - x) / 2;
    return cx - x;
  };
  const topoTreeRoots = topoRoots.filter(r => r.children.length > 0);
  const topoIsoRoots  = topoRoots.filter(r => r.children.length === 0);
  let tX = 0;
  topoTreeRoots.forEach(r => { tX += assignTopoPos(r, tX, 60); });
  const placedTopo = Object.values(topoById).filter(n => n.x !== undefined);
  const isoY = placedTopo.length ? Math.max(...placedTopo.map(n=>n.y)) + NODE_H + 40 : 60;
  topoIsoRoots.forEach((n,i) => { n.x = i * NODE_W + NODE_W/2; n.y = isoY; });

  /* ── Bounding box ── */
  const allTopo = Object.values(topoById);
  const topoMinX = allTopo.length ? Math.min(...allTopo.map(n=>n.x||0)) : 0;
  const topoMaxX = allTopo.length ? Math.max(...allTopo.map(n=>n.x||0)) : 0;
  const topoMinY = allTopo.length ? Math.min(...allTopo.map(n=>n.y||0)) : 0;
  const topoMaxY = allTopo.length ? Math.max(...allTopo.map(n=>n.y||0)) : 0;
  const topoContentW = topoMaxX - topoMinX + 100;
  const topoContentH = topoMaxY - topoMinY + 100;

  /* ── Auto-fit on load ── */
  useEffect(()=>{
    if (!loading && monitors.length > 0 && topoContainerRef.current) {
      const vpW = topoContainerRef.current.clientWidth  || 800;
      const vpH = topoContainerRef.current.clientHeight || 500;
      const scale = Math.min((vpW-80)/topoContentW, (vpH-80)/topoContentH, 1.0);
      const fit = {
        x: (vpW - topoContentW*scale)/2 - topoMinX*scale,
        y: (vpH - topoContentH*scale)/2 - topoMinY*scale,
        scale
      };
      setTopoTransform(fit);
      setTopoInitialTransform(fit);
    }
  },[loading, monitors.length]);

  /* ── Visibility set (group filter) ── */
  const topoStatusColor = s => s==='healthy'?'#008C6F':s==='warning'?'#E89A2E':s==='critical'?'#D95C5C':'#4A4A4A';
  const visibleTopoIds = new Set(
    Object.values(topoById)
      .filter(node => topoGroupFilter==='all' || node.GroupName===topoGroupFilter || isAncestorOrDescendant(node, topoGroupFilter, topoById))
      .map(n => n.MonitorID)
  );
  const nHealthy  = monitors.filter(m=>m.IsEnabled!==false&&m.EffectiveStatus==='healthy').length;
  const nWarning  = monitors.filter(m=>m.IsEnabled!==false&&m.EffectiveStatus==='warning').length;
  const nCritical = monitors.filter(m=>m.IsEnabled!==false&&m.EffectiveStatus==='critical').length;
  const nDisabled = monitors.filter(m=>m.IsEnabled===false).length;

  const topoHandleNodeClick = (node) => {
    if (!topoLinkMode) { onNavigate && onNavigate(node.MonitorID); return; }
    if (topoLinkSource === null) { setTopoLinkSource(node.MonitorID); return; }
    if (topoLinkSource === node.MonitorID) { setTopoLinkSource(null); return; }
    const srcNode = topoById[topoLinkSource];
    const srcName = srcNode ? srcNode.MonitorName : String(topoLinkSource);
    const dstName = node.MonitorName;
    setTopoConfirmModal({ msg: `Set "${srcName}" as child of "${dstName}"? This will update the ParentMonitorID of "${srcName}".`, onOk: () => {
      api("PATCH", `/monitors/${topoLinkSource}/parent`, { ParentMonitorID: node.MonitorID })
        .then(() => { setTopoReloadKey(k=>k+1); setTopoLinkSource(null); setTopoLinkMode(false); setTopoConfirmModal(null); })
        .catch(() => setTopoConfirmModal({ msg: "Failed to save relationship.", onOk: ()=>{}, okLabel: 'OK' }));
    }});
  };

  const topoHandleNodeRightClick = (e, node) => {
    e.preventDefault();
    if (!node.ParentMonitorID) return;
    setTopoConfirmModal({ msg: `Remove parent relationship from "${node.MonitorName}"? This will clear its ParentMonitorID.`, onOk: () => {
      api("PATCH", `/monitors/${node.MonitorID}/parent`, { ParentMonitorID: null })
        .then(() => { setTopoReloadKey(k=>k+1); setTopoConfirmModal(null); })
        .catch(() => setTopoConfirmModal({ msg: "Failed to remove relationship.", onOk: ()=>{}, okLabel: 'OK' }));
    }});
  };

  return (
    <div style={{ height:"calc(100vh - 20px)", overflow:"hidden", display:"flex", flexDirection:"column", background:"#ffffff" }}>

      {/* ── Topbar ── */}
      <div style={{ background:'#ffffff', borderBottom:`1px solid ${c.border}`, padding:'10px 20px', display:'flex', alignItems:'center', gap:16, flexShrink:0 }}>
        <div style={{ fontWeight:700, fontSize:15, color:c.text }}>Topology</div>
        <div style={{ fontSize:11, color:c.textDimmer }}>Dependency graph · {monitors.length} nodes</div>
        <div style={{ marginLeft:'auto', display:'flex', alignItems:'center', gap:12 }}>
          <span style={{ fontSize:11, color:'#008C6F', fontWeight:600 }}>● {nHealthy} Healthy</span>
          <span style={{ fontSize:11, color:'#E89A2E', fontWeight:600 }}>● {nWarning} Warning</span>
          <span style={{ fontSize:11, color:'#D95C5C', fontWeight:600 }}>● {nCritical} Critical</span>
          {nDisabled > 0 && <span style={{ fontSize:11, color:'#4A4A4A', fontWeight:600 }}>● {nDisabled} Disabled</span>}
          <select style={{ fontSize:11, border:`1px solid ${c.border}`, borderRadius:4, padding:'3px 8px', background:'#ffffff', color:c.text }}
            value={topoGroupFilter} onChange={e => setTopoGroupFilter(e.target.value)}>
            <option value="all">All Groups</option>
            {[...new Set(monitors.map(m=>m.GroupName).filter(Boolean))].map(g =>
              <option key={g} value={g}>{g}</option>
            )}
          </select>
          <button onClick={() => setTopoTransform(topoInitialTransform)}
            style={{ fontSize:11, border:`1px solid ${c.blue}`, borderRadius:4, padding:'3px 10px', background:'#ffffff', color:c.blue, cursor:'pointer' }}>
            Reset View
          </button>
          <button onClick={()=>{ setTopoLinkMode(m=>{ if(m) setTopoLinkSource(null); return !m; }); }}
            style={{ fontSize:11, borderRadius:4, padding:'3px 10px', cursor:'pointer',
              border:`1px solid ${topoLinkMode ? c.red : c.blue}`,
              background: topoLinkMode ? c.redLight : '#ffffff',
              color: topoLinkMode ? c.red : c.blue }}>
            {topoLinkMode ? '✕ Cancel Link' : '⚡ Link Monitors'}
          </button>
        </div>
      </div>

      {/* ── Link mode hint bar ── */}
      {topoLinkMode && (
        <div style={{ background:c.blueLight, borderBottom:`1px solid ${c.border}`, padding:'6px 20px', fontSize:11, color:c.blue, flexShrink:0 }}>
          Click a monitor to select it as the child, then click another monitor to set it as the parent. Right-click any monitor to remove its parent relationship.
        </div>
      )}

      {/* ── SVG Canvas ── */}
      <div style={{ flex:1, overflow:'hidden', position:'relative', background:'#FFFFFF' }} ref={topoContainerRef}>
        {err && <div style={{ padding:16 }}><ErrBox msg={err} /></div>}
        {loading && <div style={{ position:'absolute', inset:0, display:'flex', alignItems:'center', justifyContent:'center' }}><Spinner /></div>}
        {!loading && monitors.length > 0 && (
          <svg width="100%" height="100%"
            style={{ cursor:topoPanning?'grabbing':'grab', display:'block', userSelect:'none' }}
            onMouseDown={e=>{ if(e.button!==0)return; setTopoPanning(true); setTopoPanStart({x:e.clientX-topoTransform.x, y:e.clientY-topoTransform.y}); }}
            onMouseMove={e=>{ if(topoPanning) setTopoTransform(t=>({...t, x:e.clientX-topoPanStart.x, y:e.clientY-topoPanStart.y})); }}
            onMouseUp={()=>setTopoPanning(false)}
            onMouseLeave={()=>setTopoPanning(false)}
            onWheel={e=>{ e.preventDefault(); const delta=e.deltaY>0?0.9:1.1; setTopoTransform(t=>{ const ns=Math.min(2.5,Math.max(0.4,t.scale*delta)); const r=ns/t.scale; return {scale:ns, x:e.clientX-r*(e.clientX-t.x), y:e.clientY-r*(e.clientY-t.y)}; }); }}>
            <rect width="100%" height="100%" fill="#FFFFFF" />
            <g transform={`translate(${topoTransform.x},${topoTransform.y}) scale(${topoTransform.scale})`}>
              {/* Edges */}
              {Object.values(topoById).filter(n => n.ParentMonitorID && topoById[n.ParentMonitorID] && visibleTopoIds.has(n.MonitorID) && visibleTopoIds.has(n.ParentMonitorID)).map(node => {
                const parent = topoById[node.ParentMonitorID];
                return <path key={`edge-${node.MonitorID}`}
                  d={`M${parent.x},${parent.y+26} C${parent.x},${parent.y+70} ${node.x},${node.y-70} ${node.x},${node.y-26}`}
                  stroke="#006D8C" strokeOpacity="0.4" strokeWidth="1.5" fill="none" />;
              })}
              {/* Nodes */}
              {Object.values(topoById).filter(n => visibleTopoIds.has(n.MonitorID)).map(node => {
                const col       = node.IsEnabled === false ? '#4A4A4A' : topoStatusColor(node.EffectiveStatus);
                const label     = (node.MonitorName||'').length > 16 ? (node.MonitorName||'').substring(0,16)+'\u2026' : (node.MonitorName||'');
                const grp       = (node.GroupName||'').length > 14 ? (node.GroupName||'').substring(0,14)+'\u2026' : (node.GroupName||'');
                const letter    = (node.MonitorType||'?')[0].toUpperCase();
                const isLinkSrc = topoLinkMode && topoLinkSource === node.MonitorID;
                return (
                  <g key={node.MonitorID}
                    style={{ cursor: topoLinkMode ? 'crosshair' : 'pointer' }}
                    transform={isLinkSrc ? `translate(${node.x},${node.y}) scale(1.1)` : `translate(${node.x},${node.y})`}
                    onClick={() => topoHandleNodeClick(node)}
                    onContextMenu={e => topoHandleNodeRightClick(e, node)}
                    onMouseDown={e => e.stopPropagation()}
                    onMouseEnter={e => e.currentTarget.setAttribute('opacity','0.82')}
                    onMouseLeave={e => e.currentTarget.setAttribute('opacity','1')}>
                    {topoLinkMode && (isLinkSrc
                      ? <circle cx={0} cy={0} r={32} fill="none" stroke={c.blue} strokeWidth={3} />
                      : <circle cx={0} cy={0} r={32} fill="none" stroke={c.blue} strokeWidth={1} strokeDasharray="3 2" />
                    )}
                    <circle cx={0} cy={0} r={26} fill={col} stroke="white" strokeWidth={2}
                      strokeDasharray={node.IsSuppressed ? '4 2' : undefined} />
                    <text x={0} y={0} textAnchor="middle" dominantBaseline="central"
                      fill="white" fontSize={13} fontWeight="bold" style={{pointerEvents:'none'}}>{letter}</text>
                    <text x={0} y={42} textAnchor="middle"
                      fill={c.text} fontSize={10} style={{pointerEvents:'none'}}>{label}</text>
                    <text x={0} y={54} textAnchor="middle"
                      fill={c.textDimmer} fontSize={9} style={{pointerEvents:'none'}}>{grp}</text>
                  </g>
                );
              })}
            </g>
          </svg>
        )}
        {!loading && monitors.length === 0 && !err && (
          <div style={{ position:'absolute', inset:0, display:'flex', alignItems:'center', justifyContent:'center', color:c.textDimmer, fontSize:14 }}>No monitors configured</div>
        )}
      </div>

      {/* ── News ticker ── */}
      <div style={{ height:28, background:"#1e2b3c", borderTop:"0.5px solid rgba(255,255,255,0.07)", overflow:"hidden", display:"flex", alignItems:"center", flexShrink:0 }}>
        <div style={{ paddingLeft:10, paddingRight:10, borderRight:"0.5px solid rgba(255,255,255,0.15)", flexShrink:0, height:"100%", display:"flex", alignItems:"center" }}>
          <span style={{ fontSize:9, color:"rgba(255,255,255,0.4)", textTransform:"uppercase", letterSpacing:"0.08em" }}>&#9679; LIVE</span>
        </div>
        {recentAlerts.length > 0 ? (
          <div style={{ display:"inline-flex", animation:`op1-ticker ${TICKER_DURATION_SEC}s linear infinite`, whiteSpace:"nowrap" }}>
            {[...recentAlerts, ...recentAlerts].map((item,i)=>(
              <span key={i} style={{ padding:"0 24px", fontSize:11, color:"rgba(255,255,255,0.85)", borderRight:"1px solid rgba(255,255,255,0.1)", display:"inline-flex", alignItems:"center", gap:5 }}>
                <span style={{ color:(item.Status||"").toLowerCase()==="critical"?"#ef4444":(item.Status||"").toLowerCase()==="warning"?"#f59e0b":"#22c55e", fontSize:8 }}>&#9679;</span>
                {(item.Status||"").toUpperCase()} &middot; {item.MonitorName}{item.Message ? ` \u00b7 ${item.Message}` : ""}
                {item.CreatedAt && <span style={{ color:"rgba(255,255,255,0.4)", fontSize:10, marginLeft:4 }}>{parseUTC(item.CreatedAt)?.toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"})}</span>}
              </span>
            ))}
          </div>
        ) : (
          <span style={{ fontSize:11, color:"rgba(255,255,255,0.35)", paddingLeft:16, letterSpacing:"0.04em" }}>No recent alerts</span>
        )}
      </div>

      {topoConfirmModal && (
        <div style={{ position:'fixed', inset:0, background:'rgba(0,0,0,0.75)', zIndex:1300, display:'flex', alignItems:'center', justifyContent:'center' }}
          onClick={e => { if(e.target===e.currentTarget) setTopoConfirmModal(null); }}>
          <div style={{ background:'#fff', borderRadius:8, width:380, maxWidth:'90vw', padding:'24px', boxShadow:'0 8px 32px rgba(0,0,0,0.18)' }}>
            <div style={{ fontSize:13, color:'#1A1A1A', marginBottom:20, lineHeight:1.5 }}>{topoConfirmModal.msg}</div>
            <div style={{ display:'flex', justifyContent:'flex-end', gap:8 }}>
              <button onClick={()=>setTopoConfirmModal(null)}
                style={{ fontSize:12, padding:'6px 16px', border:'1px solid #006D8C', borderRadius:6, background:'#fff', color:'#006D8C', cursor:'pointer' }}>Cancel</button>
              <button onClick={()=>{ topoConfirmModal.onOk(); setTopoConfirmModal(null); }}
                style={{ fontSize:12, padding:'6px 16px', border:'none', borderRadius:6, background:'#D95C5C', color:'#fff', cursor:'pointer', fontWeight:600 }}>{topoConfirmModal.okLabel || 'Confirm'}</button>
            </div>
          </div>
        </div>
      )}

    </div>
  );
}

// ═══ ALERTS ═══
function AlertsView({ api, canAck, machines, currentMachine, onNavigate, monitoringControl, onNavigateSynthetic }) {
  const [alerts, setAlerts]               = useState([]);
  const [groups, setGroups]               = useState([]);
  const [loading, setLoading]             = useState(true);
  const [err, setErr]                     = useState("");
  const [anomalyOnly, setAnomalyOnly]     = useState(false);
  const [unresolvedOnly, setUnresolvedOnly] = useState(true);
  const [groupFilter, setGroupFilter]     = useState("");
  const [machineFilter, setMachineFilter] = useState("");
  const [busy, setBusy]                   = useState({});
  const [recentAlerts, setRecentAlerts]   = useState([]);
  const [activeDiagId, setActiveDiagId]   = useState(null);
  const [diagLoading, setDiagLoading]     = useState(false);
  const [diagData, setDiagData]           = useState(null);
  const [diagError, setDiagError]         = useState("");
  const [diagAlert, setDiagAlert]         = useState(null);
  const [severityFilter, setSeverityFilter] = useState("all");
  const [selectedAlerts, setSelectedAlerts] = useState(new Set());

  useEffect(()=>{
    const id = "op1-ticker-kf";
    if (!document.getElementById(id)) {
      const el = document.createElement("style"); el.id = id;
      el.textContent = "@keyframes op1-ticker { from { transform:translateX(0) } to { transform:translateX(-50%) } }";
      document.head.appendChild(el);
    }
    api("GET","/monitors/groups").then(r=>setGroups(Array.isArray(r)?r:[])).catch(()=>{});
    api("GET","/alerts/recent?top=20").then(r=>setRecentAlerts(Array.isArray(r)?r:[])).catch(()=>{});
  },[api]);

  const load = useCallback(()=>{
    setLoading(true);
    const q=new URLSearchParams({top:"200",unresolvedOnly:unresolvedOnly?"true":"false"});
    if(anomalyOnly)   q.set("anomalyOnly","true");
    if(groupFilter)   q.set("groupId", groupFilter);
    if(machineFilter) q.set("sourceMachine", machineFilter);
    api("GET",`/logs/alerts?${q}`).then(r=>{setAlerts(Array.isArray(r)?r:[]); setErr("");}).catch(e=>setErr(e.message)).finally(()=>setLoading(false));
  },[api,anomalyOnly,unresolvedOnly,groupFilter,machineFilter]);
  useEffect(()=>{load();},[load]);
  useEffect(()=>{ setSelectedAlerts(new Set()); },[severityFilter,groupFilter,machineFilter,unresolvedOnly,anomalyOnly]);

  const act = async (action,id) => {
    setBusy(b=>({...b,[id]:true}));
    try { await api("POST",`/logs/alerts/${id}/${action}`,{}); load(); } catch(e){setErr(e.message);}
    finally { setBusy(b=>({...b,[id]:false})); }
  };

  const { isPaused: monIsPaused, canManageConfig, pausePopover, setPausePopover, pauseReason, setPauseReason, pauseBusy, togglePause, currentCtrl } = monitoringControl || {};

  const aCritical   = alerts.filter(a=>a.Severity==="critical").length;
  const aUnresolved = alerts.filter(a=>!a.ResolvedAt).length;
  const aSuppressed = alerts.filter(a=>a.IsSuppressed).length;

  function alertHint(alertType, severity, message) {
    if (alertType === 'recovery')
      return 'Monitor self-recovered — no action required unless recurrence is frequent.';
    if (alertType === 'ping_fail')
      return 'Target is unreachable. Check host availability, firewall rules, or network path.';
    if (alertType === 'threshold_breach') {
      if (message && message.includes('301'))
        return 'HTTP 301 redirect — content match will always fail until the monitor URL is updated to the final destination.';
      if (message && message.includes('302'))
        return 'HTTP 302 temporary redirect — target URL may have moved. Verify expected endpoint.';
      if (message && (message.toLowerCase().includes('row') || message.toLowerCase().includes('count')))
        return 'Table row count breached threshold — review data growth rate and consider a purge or archival job.';
      if (severity === 'warning')
        return 'Warning threshold crossed. Monitor the trend — if persistent, investigate the root cause before it escalates.';
      if (severity === 'critical')
        return 'Critical threshold breached. Immediate investigation recommended.';
      return 'Threshold exceeded — review monitor configuration and current target state.';
    }
    if (alertType === 'ssl_expiry')
      return 'SSL certificate expiring soon — renew before expiry to avoid service disruption.';
    return 'Review monitor details and recent check history for context.';
  }

  function formatAlertTime(dateStr) {
    if (!dateStr) return { relative: "—", absolute: "—" };
    const d = parseUTC(dateStr);
    if (!d) return { relative: "—", absolute: "—" };
    const diffMs = Date.now() - d.getTime();
    const diffS  = Math.floor(diffMs / 1000);
    const diffM  = Math.floor(diffMs / 60000);
    const diffH  = Math.floor(diffMs / 3600000);
    const diffD  = Math.floor(diffMs / 86400000);
    let relative;
    if (diffS < 60)      relative = "just now";
    else if (diffM < 60) relative = `${diffM}m ago`;
    else if (diffH < 24) relative = `${diffH}h ago`;
    else if (diffD < 7)  relative = `${diffD}d ago`;
    else                 relative = d.toLocaleDateString();
    const absolute = d.toLocaleString([], { month:"2-digit", day:"2-digit", year:"numeric", hour:"2-digit", minute:"2-digit" });
    return { relative, absolute };
  }

  function parseDiagnosisText(text) {
    if (!text) return { what: "", cause: "", steps: [] };
    const t = text.trim();
    try {
      const j = JSON.parse(t);
      if (j && typeof j === 'object' && !Array.isArray(j)) {
        return {
          what:  j.whatThisMeans   || "",
          cause: j.likelyRootCause || "",
          steps: Array.isArray(j.recommendedSteps) ? j.recommendedSteps : []
        };
      }
    } catch(e) { /* fall through to markdown */ }
    const whatMatch  = t.match(/##\s*What This Means\s*\n([\s\S]*?)(?=##|$)/i);
    const causeMatch = t.match(/##\s*Likely Root Cause\s*\n([\s\S]*?)(?=##|$)/i);
    const stepsMatch = t.match(/##\s*Recommended Steps\s*\n([\s\S]*?)(?=##|$)/i);
    const steps = stepsMatch
      ? stepsMatch[1].trim().split('\n').filter(s => s.trim()).map(s => s.replace(/^\d+\.\s*/, '').trim())
      : [];
    return {
      what:  whatMatch  ? whatMatch[1].trim()  : "",
      cause: causeMatch ? causeMatch[1].trim() : "",
      steps
    };
  }

  function openDiagnosis(alert) {
    setActiveDiagId(alert.AlertID);
    setDiagAlert(alert);
    setDiagData(null);
    setDiagError("");
    setDiagLoading(true);
    api("GET", `/logs/alerts/${alert.AlertID}/diagnosis`)
      .then(r => {
        if (r && r.status === "pending") {
          setDiagData({ pending: true });
        } else {
          setDiagData(r);
        }
      })
      .catch(e => {
        if (e.message === "HTTP 404") {
          setDiagData({ pending: true });
        } else {
          setDiagError(e.message);
        }
      })
      .finally(() => setDiagLoading(false));
  }

  function closeDiagnosis() {
    setActiveDiagId(null);
    setDiagAlert(null);
    setDiagData(null);
    setDiagError("");
  }

  async function markStale(alertId) {
    await api("POST", `/logs/alerts/${alertId}/mark-stale`, {});
    openDiagnosis(diagAlert);
  }

  const diagParsed = parseDiagnosisText(diagData?.diagnosisText);

  const diagAiContent = diagLoading ? (
    <div style={{ display:"flex", alignItems:"center", gap:8, padding:"8px 0" }}>
      <div style={{ width:12, height:12, border:"2px solid #D8CCBA", borderTopColor:"#006D8C", borderRadius:"50%", animation:"spin 0.8s linear infinite", flexShrink:0 }} />
      <span style={{ fontSize:11, color:"#4A4A4A", fontStyle:"italic" }}>Analysing alert context…</span>
    </div>
  ) : diagError ? (
    <div style={{ fontSize:11, color:"#D95C5C" }}>Diagnosis unavailable.</div>
  ) : diagData?.pending ? (
    <div>
      <div style={{ fontSize:11, color:"#4A4A4A", marginBottom:8 }}>Analysis queued — check back in a moment.</div>
      <button onClick={()=>openDiagnosis(diagAlert)} style={{ fontSize:10, padding:"3px 10px", border:"1px solid #006D8C", borderRadius:3, background:"#E0F2F7", color:"#006D8C", cursor:"pointer" }}>Retry</button>
    </div>
  ) : diagData?.isSynthetic ? (
    (() => {
      const parseBold = (s) => s.split(/(\*\*[^*]+\*\*)/g).map((p,j) =>
        p.startsWith("**")&&p.endsWith("**") ? <strong key={j}>{p.slice(2,-2)}</strong> : p
      );
      return (
        <div>
          {(diagData.diagnosisText||"").split("\n").map((line,i) => {
            const t=line.trim();
            if (/^\d+\./.test(t)) { const n=t.match(/^\d+/)[0]; return <div key={i} style={{ display:"flex", gap:6, marginBottom:3 }}><span style={{ flexShrink:0, color:"#006D8C", minWidth:14, fontSize:11 }}>{n}.</span><span style={{ fontSize:11, lineHeight:1.6 }}>{parseBold(t.slice(n.length+1).trim())}</span></div>; }
            if (t==="") return <div key={i} style={{ height:6 }} />;
            return <div key={i} style={{ fontSize:11, color:"#1A1A1A", lineHeight:1.6, marginBottom:2 }}>{parseBold(line)}</div>;
          })}
        </div>
      );
    })()
  ) : diagData ? (
    <div>
      {diagData.isRepeatMatch && (
        <div style={{ background:"#E0F2F7", border:"1px solid #006D8C", borderRadius:4, padding:"6px 8px", fontSize:11, color:"#006D8C", marginBottom:10 }}>
          Sourced from a prior alert on this monitor — same root cause likely applies.
        </div>
      )}
      {diagData.diagnosisIsStale && (
        <div style={{ background:"#FDF3E0", border:"1px solid #E89A2E", borderRadius:4, padding:"6px 8px", fontSize:11, color:"#C07A10", marginBottom:10, display:"flex", alignItems:"center", justifyContent:"space-between" }}>
          <span>Diagnosis may be outdated — conditions may have changed.</span>
          <button onClick={()=>markStale(diagAlert.AlertID)} style={{ fontSize:10, padding:"2px 8px", border:"1px solid #E89A2E", borderRadius:3, background:"#ffffff", color:"#C07A10", cursor:"pointer", marginLeft:8, flexShrink:0 }}>Re-investigate →</button>
        </div>
      )}
      {diagParsed.what && (
        <div style={{ marginBottom:12 }}>
          <div style={{ fontSize:10, fontWeight:500, textTransform:"uppercase", letterSpacing:"0.06em", color:"#006D8C", marginBottom:5 }}>What this means</div>
          <div style={{ fontSize:11, color:"#1A1A1A", lineHeight:1.6 }}>{diagParsed.what}</div>
        </div>
      )}
      {diagParsed.cause && (
        <div style={{ marginBottom:12 }}>
          <div style={{ fontSize:10, fontWeight:500, textTransform:"uppercase", letterSpacing:"0.06em", color:"#006D8C", marginBottom:5 }}>Likely root cause</div>
          <div style={{ fontSize:11, color:"#1A1A1A", lineHeight:1.6 }}>{diagParsed.cause}</div>
        </div>
      )}
      {diagParsed.steps.length > 0 && (
        <div>
          <div style={{ fontSize:10, fontWeight:500, textTransform:"uppercase", letterSpacing:"0.06em", color:"#006D8C", marginBottom:5 }}>Recommended steps</div>
          <ol style={{ margin:0, paddingLeft:16 }}>
            {diagParsed.steps.map((step, i) => (
              <li key={i} style={{ fontSize:11, color:"#1A1A1A", lineHeight:1.6, marginBottom:3 }}>{step}</li>
            ))}
          </ol>
        </div>
      )}
    </div>
  ) : null;

  const diagPanel = (
    <div style={{ flex:1, background:"#ffffff", border:"1px solid #D8CCBA", borderRadius:6, overflow:"hidden", position:"sticky", top:14, display:"flex", flexDirection:"column" }}>
      {!activeDiagId ? (
        <div style={{ flex:1, display:"flex", flexDirection:"column", alignItems:"center", justifyContent:"center", padding:"40px 20px", textAlign:"center" }}>
          <div style={{ width:36, height:36, borderRadius:"50%", background:"#E0F2F7", display:"flex", alignItems:"center", justifyContent:"center", fontSize:18, color:"#006D8C", marginBottom:12 }}>✦</div>
          <div style={{ fontSize:13, fontWeight:500, color:"#1A1A1A", marginBottom:6 }}>AI Diagnostics</div>
          <div style={{ fontSize:11, color:"#4A4A4A" }}>Click Diagnose on any alert to run AI-assisted root cause analysis.</div>
        </div>
      ) : (
        <div style={{ display:"flex", flexDirection:"column", height:"100%" }}>
          <div style={{ background:"#1E2B3C", padding:"10px 14px", display:"flex", justifyContent:"space-between", alignItems:"flex-start", flexShrink:0 }}>
            <div>
              <div style={{ fontSize:12, fontWeight:500, color:"#F4F8FC" }}>{diagAlert?.MonitorName || diagAlert?.FlowName || '—'}</div>
              <div style={{ fontSize:10, color:"#7A9AB8", marginTop:2 }}>
                {diagAlert?.GroupName || (diagAlert?.FlowName ? 'synthetic' : '—')}{" · "}
                {diagAlert?.AlertTimestamp ? parseUTC(diagAlert.AlertTimestamp)?.toLocaleTimeString([], { hour:"2-digit", minute:"2-digit" }) : ""}
                {" · "}
                <span style={{ fontSize:9, fontWeight:500, padding:"1px 5px", borderRadius:3, textTransform:"uppercase",
                  background: diagAlert?.Severity==="critical" ? "#FAEAEA" : diagAlert?.Severity==="warning" ? "#FDF3E0" : "#E0F2F7",
                  color: diagAlert?.Severity==="critical" ? "#D95C5C" : diagAlert?.Severity==="warning" ? "#C07A10" : "#006D8C" }}>
                  {diagAlert?.Severity}
                </span>
              </div>
            </div>
            <button onClick={closeDiagnosis} style={{ background:"none", border:"none", cursor:"pointer", color:"#7A9AB8", fontSize:16, lineHeight:1, padding:"0 2px" }}>✕</button>
          </div>
          <div style={{ flex:1, overflowY:"auto", padding:14 }}>
            <div style={{ display:"grid", gridTemplateColumns:"1fr 1fr", gap:6, marginBottom:12 }}>
              {[
                ["Status",     diagAlert?.Severity,  diagAlert?.Severity==="critical" ? "#D95C5C" : diagAlert?.Severity==="warning" ? "#E89A2E" : "#008C6F"],
                ["Alert type", diagAlert?.AlertType, "#1A1A1A"],
                ["Timestamp",  diagAlert?.AlertTimestamp ? parseUTC(diagAlert.AlertTimestamp)?.toLocaleDateString() : "", "#1A1A1A"],
                ["Occurrence", diagData?.isRepeatMatch ? "Repeat of prior alert" : "First occurrence", "#1A1A1A"],
              ].map(([label, value, col]) => (
                <div key={label} style={{ background:"#F5F0E8", borderRadius:4, padding:"6px 8px" }}>
                  <div style={{ fontSize:9, color:"#4A4A4A", textTransform:"uppercase", letterSpacing:"0.05em", marginBottom:2 }}>{label}</div>
                  <div style={{ fontSize:11, fontWeight:500, color:col }}>{value}</div>
                </div>
              ))}
            </div>
            <div style={{ display:"flex", gap:4, flexWrap:"wrap", marginBottom:12 }}>
              {[
                diagAlert?.GroupName,
                diagAlert?.SourceMachine,
                diagAlert?.MonitorName && !diagAlert?.FlowName ? diagAlert.MonitorName : null,
                diagAlert?.FlowName ? diagAlert.FlowName : null,
                diagAlert?.AlertType,
              ].filter(Boolean).map((tag, i) => (
                <span key={i} style={{ fontSize:10, padding:"2px 8px", borderRadius:3, background:"#E0F2F7", color:"#006D8C", border:"1px solid #D8CCBA", fontWeight:500 }}>{tag}</span>
              ))}
            </div>
            <div style={{ borderTop:"1px solid #D8CCBA", margin:"12px 0" }} />
            {diagAiContent}
          </div>
        </div>
      )}
    </div>
  );

  const bulkAct = async (action, ids) => {
    for (const id of ids) {
      try { await api("POST", `/logs/alerts/${id}/${action}`, {}); } catch(e) { setErr(e.message); }
    }
    setSelectedAlerts(new Set());
    load();
  };

  const filteredAlerts = severityFilter === "all" ? alerts : alerts.filter(a => a.Severity?.toLowerCase() === severityFilter);

  const alertBlocks = filteredAlerts.map(a => {
    const isActive = activeDiagId === a.AlertID;
    const activeBg = a.Severity==="critical" ? "#FDF0F0" : a.Severity==="warning" ? "#FDF8EE" : "#E6F4FA";
    const activeBorderCol = a.Severity==="critical" ? "#D95C5C" : a.Severity==="warning" ? "#E89A2E" : "#006D8C";
    const blockBg = isActive ? activeBg : "#ffffff";
    const blockBorderLeft = isActive ? `3px solid ${activeBorderCol}` : "3px solid transparent";
    const machineName = machines?.find(m => m.MachineName === a.SourceMachine)?.MachineAlias || a.SourceMachine;
    const hint = alertHint(a.AlertType, a.Severity, a.Message);
    const sevBg  = a.Severity==="critical" ? "#FAEAEA" : a.Severity==="warning" ? "#FDF3E0" : "#E0F2F7";
    const sevCol = a.Severity==="critical" ? "#D95C5C" : a.Severity==="warning" ? "#C07A10" : "#006D8C";
    const fmtTime = formatAlertTime(a.AlertTimestamp);
    const isSelected = selectedAlerts.has(a.AlertID);
    const hasRecovered = a.Message && a.Message.toLowerCase().includes("recovered");
    return (
      <div key={a.AlertID} style={{ borderBottom:"1px solid #D8CCBA", background:blockBg, borderLeft:blockBorderLeft }}
        onMouseEnter={e => { if (!isActive) e.currentTarget.style.background = "#F0F7FB"; }}
        onMouseLeave={e => { if (!isActive) e.currentTarget.style.background = "#ffffff"; }}>
        <div style={{ display:"grid", gridTemplateColumns:"32px 72px 130px 120px 80px minmax(0,1fr) 130px", alignItems:"center", padding:"8px 12px 3px 12px" }}>
          <div style={{ display:"flex", justifyContent:"center" }} onClick={e => e.stopPropagation()}>
            <input type="checkbox" checked={isSelected} onChange={e => {
              setSelectedAlerts(prev => {
                const next = new Set(prev);
                e.target.checked ? next.add(a.AlertID) : next.delete(a.AlertID);
                return next;
              });
            }} style={{ cursor:"pointer", accentColor:"#006D8C" }} />
          </div>
          <div>
            <span style={{ fontSize:9, fontWeight:500, padding:"2px 6px", borderRadius:3, textTransform:"uppercase", background:sevBg, color:sevCol }}>{a.Severity}</span>
          </div>
          <div>
            {a.MonitorName
              ? <span onClick={()=>onNavigate&&onNavigate(a.MonitorID)} style={{ fontSize:12, color:"#006D8C", fontWeight:500, cursor:onNavigate?"pointer":"default" }}>{a.MonitorName}</span>
              : a.FlowName
                ? <span onClick={()=>onNavigateSynthetic&&onNavigateSynthetic(a.FlowId,null,false)} style={{ fontSize:12, color:"#006D8C", fontWeight:500, cursor:onNavigateSynthetic?"pointer":"default" }}>{a.FlowName}</span>
                : null}
            {a.IsAnomaly && <span style={{ fontSize:9, fontWeight:500, padding:"1px 5px", borderRadius:3, background:"#ede9fe", color:"#6d28d9", marginLeft:4 }}>⚡ {a.AnomalyScore != null ? `${Number(a.AnomalyScore).toFixed(1)}σ` : "ANOMALY"}</span>}
          </div>
          <div>
            <div style={{ fontSize:11, color:"#006D8C" }}>{a.GroupName}</div>
            <div style={{ fontSize:10, color:"#4A4A4A" }}>{machineName}</div>
          </div>
          <div>
            <div style={{ fontWeight:600, fontSize:12, color:c.text }}>{fmtTime.relative}</div>
            <div style={{ fontSize:10, color:c.textMuted, marginTop:1 }}>{fmtTime.absolute}</div>
          </div>
          <div style={{ fontSize:12, color:"#1A1A1A", paddingRight:8, minWidth:0, display:"flex", alignItems:"center", gap:4 }}>
            <span style={{ overflow:"hidden", textOverflow:"ellipsis", whiteSpace:"nowrap", flex:1, minWidth:0 }}>{a.Message}</span>
            {hasRecovered && <span style={{ fontSize:9, fontWeight:600, background:"#DCF2EA", color:"#008C6F", padding:"1px 5px", borderRadius:3, marginLeft:6, flexShrink:0 }}>SELF-RECOVERED</span>}
          </div>
          <div style={{ display:"flex", flexDirection:"column", gap:3, alignItems:"flex-start", minWidth:130 }}>
            {(a.AlertType === 'synthetic-flow' || a.AlertType === 'synthetic-duration') && onNavigateSynthetic && a.FlowId && (
              <div style={{ display:"flex", gap:3, flexWrap:"wrap", width:"100%" }}>
                <button onClick={() => onNavigateSynthetic(a.FlowId, a.FlowResultId || null, false)}
                  style={{ fontSize:9, padding:"2px 5px", border:"0.5px solid #e2e5ea", borderRadius:3, background:"#f1f5f9", color:"#475569", cursor:"pointer" }}>
                  View Run →
                </button>
                <button
                  disabled={!a.FlowResultId}
                  title={!a.FlowResultId ? "Diagnosis requires a result ID — re-run the flow to generate a diagnosable result" : undefined}
                  onClick={() => { if (a.FlowResultId) onNavigateSynthetic(a.FlowId, a.FlowResultId, true); }}
                  style={{ fontSize:9, padding:"2px 5px", border:"1px solid #006D8C", borderRadius:3, background:"#006D8C", color:"#fff", fontWeight:500, cursor:a.FlowResultId?"pointer":"not-allowed", opacity:a.FlowResultId?1:0.5 }}>
                  ⚡ Diagnose
                </button>
              </div>
            )}
            {!a.ResolvedAt && canAck && (
              <div style={{ display:"flex", gap:3, flexWrap:"wrap" }}>
                {!a.AcknowledgedAt && (
                  <button disabled={busy[a.AlertID]} onClick={() => act("acknowledge", a.AlertID)}
                    style={{ fontSize:9, padding:"2px 5px", border:"0.5px solid #e2e5ea", borderRadius:3, background:"#f1f5f9", color:"#475569", cursor:"pointer" }}>
                    {busy[a.AlertID] ? "…" : "ACK"}
                  </button>
                )}
                <button disabled={busy[a.AlertID]} onClick={() => act("resolve", a.AlertID)}
                  style={{ fontSize:9, padding:"2px 5px", border:"0.5px solid #bbf7d0", borderRadius:3, background:"#dcfce7", color:"#166534", cursor:"pointer" }}>
                  {busy[a.AlertID] ? "…" : "Resolve"}
                </button>
                <button disabled={busy[a.AlertID]} onClick={() => act("suppress", a.AlertID)}
                  style={{ fontSize:9, padding:"2px 5px", border:"0.5px solid #fde68a", borderRadius:3, background:"#fef9c3", color:"#854d0e", cursor:"pointer" }}>
                  {busy[a.AlertID] ? "…" : "Suppress"}
                </button>
              </div>
            )}
            <button onClick={e => { e.stopPropagation(); openDiagnosis(a); }}
              style={{ fontSize:10, padding:"2px 8px", borderRadius:3,
                border:"1px solid #006D8C", fontWeight:500,
                background: isActive ? "#006D8C" : "#E0F2F7",
                color:       isActive ? "#ffffff"  : "#006D8C",
                cursor:"pointer", whiteSpace:"nowrap", width:"100%" }}>Diagnose</button>
          </div>
        </div>
        <div style={{ fontSize:11, color:"#4A4A4A", fontStyle:"italic", lineHeight:1.45, padding:"2px 12px 8px 14px", width:"100%", boxSizing:"border-box" }}>
          ↳ {hint}
        </div>
      </div>
    );
  });

  return (
    <div style={{ display:"flex", flexDirection:"column", height:"calc(100vh - 20px)", overflow:"hidden", background:"#ffffff" }}>

      {/* ── Branding header ── */}
      <div style={{ display:"flex", alignItems:"center", justifyContent:"space-between", background:"#ffffff", borderBottom:"0.5px solid #f1f5f9", padding:"10px 16px", flexShrink:0 }}>
        <div style={{ display:"flex", alignItems:"center", gap:12 }}>
          <div style={{ width:36, height:36, borderRadius:8, background:"#006D8C", display:"flex", alignItems:"center", justifyContent:"center", fontSize:13, fontWeight:700, color:"#ffffff", letterSpacing:"0.02em", flexShrink:0 }}>OP1</div>
          <div>
            <div style={{ fontSize:16, fontWeight:600, color:"#1e293b" }}>
              <span style={{ color:"#006D8C" }}>OP1</span> Operations Portal
            </div>
            <div style={{ fontSize:10, color:"#94a3b8" }}>Enterprise Monitoring · v1.4.457</div>
          </div>
        </div>
        <div style={{ display:"flex", alignItems:"center", gap:0 }}>
          {[
            ["Total Alerts", alerts.length,  "#1e293b"],
            ["Critical",     aCritical,      aCritical   > 0 ? "#dc2626" : "#1e293b"],
            ["Unresolved",   aUnresolved,    aUnresolved > 0 ? "#d97706" : "#1e293b"],
            ["Suppressed",   aSuppressed,    "#1e293b"],
          ].flatMap(([lbl, val, col], i, arr) => [
            <div key={lbl} style={{ padding:"0 16px", textAlign:"center" }}>
              <div style={{ fontSize:13, fontWeight:600, color:col }}>{val}</div>
              <div style={{ fontSize:9, color:"#94a3b8", textTransform:"uppercase", letterSpacing:"0.06em" }}>{lbl}</div>
            </div>,
            i < arr.length-1 && <div key={`div-${i}`} style={{ width:"0.5px", height:28, background:"#e2e5ea", flexShrink:0 }} />,
          ].filter(Boolean))}
        </div>
      </div>

      {/* ── Filter bar ── */}
      <div style={{ display:"flex", alignItems:"center", background:"#f8fafc", borderBottom:"0.5px solid #e2e5ea", height:36, padding:"0 12px", flexShrink:0, gap:12 }}>
        <label style={{ display:"flex", alignItems:"center", gap:6, fontSize:11, color:"#475569", cursor:"pointer", flexShrink:0, whiteSpace:"nowrap" }}>
          <input type="checkbox" checked={unresolvedOnly} onChange={e=>setUnresolvedOnly(e.target.checked)} style={{ accentColor:"#006D8C" }} /> Unresolved only
        </label>
        <label style={{ display:"flex", alignItems:"center", gap:6, fontSize:11, color:"#7c3aed", cursor:"pointer", flexShrink:0, whiteSpace:"nowrap" }}>
          <input type="checkbox" checked={anomalyOnly} onChange={e=>setAnomalyOnly(e.target.checked)} style={{ accentColor:"#7c3aed" }} /> Anomaly only
        </label>
        <select value={severityFilter} onChange={e=>setSeverityFilter(e.target.value)}
          style={{ height:24, fontSize:11, border:"0.5px solid #e2e5ea", borderRadius:4, background:"#fff", color:"#374151", padding:"0 6px", outline:"none", cursor:"pointer" }}>
          <option value="all">All Severities</option>
          <option value="critical">Critical</option>
          <option value="warning">Warning</option>
          <option value="info">Info</option>
        </select>
        <div style={{ width:"0.5px", height:18, background:"#e2e5ea", flexShrink:0 }} />
        <select value={groupFilter} onChange={e=>setGroupFilter(e.target.value)}
          style={{ height:24, fontSize:11, border:"0.5px solid #e2e5ea", borderRadius:4, background:"#fff", color:"#374151", padding:"0 6px", outline:"none", cursor:"pointer" }}>
          <option value="">All Groups</option>
          {groups.map(g=><option key={g.GroupID} value={String(g.GroupID)}>{g.GroupName}</option>)}
        </select>
        {machines && machines.length >= 1 && (
          <select value={machineFilter} onChange={e=>setMachineFilter(e.target.value)}
            style={{ height:24, fontSize:11, border:"0.5px solid #e2e5ea", borderRadius:4, background:"#fff", color:"#374151", padding:"0 6px", outline:"none", cursor:"pointer" }}>
            <option value="">All Machines</option>
            {machines.map(m=><option key={m.MachineName} value={m.MachineName}>{m.MachineAlias||m.MachineName}{m.MachineName===currentMachine?" (this)":""}</option>)}
          </select>
        )}
        <div style={{ marginLeft:"auto", flexShrink:0 }}>
          <button onClick={load} style={{ fontSize:11, padding:"3px 10px", border:"0.5px solid #e2e5ea", borderRadius:5, background:"#fff", color:"#475569", cursor:"pointer" }}>Refresh</button>
        </div>
      </div>

      {/* ── Workspace ── */}
      <div style={{ display:"flex", gap:14, padding:"14px 16px", width:"100%", boxSizing:"border-box", alignItems:"flex-start" }}>

        {/* Left panel — alerts table */}
        <div style={{ flex:"0 0 68%", display:"flex", flexDirection:"column", background:"#ffffff", border:"1px solid #D8CCBA", borderRadius:6, overflow:"hidden", minWidth:0 }}>
          <div style={{ display:"grid", gridTemplateColumns:"32px 72px 130px 120px 80px minmax(0,1fr) 130px", background:"#D8CCBA", borderBottom:"1px solid #D8CCBA", fontSize:11, color:"#1A1A1A", fontWeight:600, textTransform:"uppercase", letterSpacing:"0.05em", padding:"6px 12px", flexShrink:0, position:"sticky", top:0, zIndex:1 }}>
            <span style={{ display:"flex", justifyContent:"center" }}>
              <input type="checkbox" style={{ cursor:"pointer", accentColor:"#006D8C" }}
                checked={filteredAlerts.length > 0 && filteredAlerts.every(a => selectedAlerts.has(a.AlertID))}
                onChange={e => { setSelectedAlerts(e.target.checked ? new Set(filteredAlerts.map(a => a.AlertID)) : new Set()); }} />
            </span>
            <span>Severity</span><span>Monitor</span><span>Group</span><span>Time</span><span>Alert Message</span><span>Actions</span>
          </div>
          {selectedAlerts.size > 0 && (
            <div style={{ background:"#E0F2F7", borderBottom:"1px solid #006D8C", padding:"7px 16px", display:"flex", alignItems:"center", gap:8, flexShrink:0 }}>
              <span style={{ fontSize:11, color:"#1A1A1A", fontWeight:500 }}>{selectedAlerts.size} alert(s) selected</span>
              <button onClick={() => setSelectedAlerts(new Set())}
                style={{ fontSize:11, padding:"2px 8px", border:"0.5px solid #e2e5ea", borderRadius:3, background:"#fff", color:"#475569", cursor:"pointer" }}>Clear</button>
              {canAck && <button onClick={() => bulkAct("acknowledge", [...selectedAlerts])}
                style={{ fontSize:11, padding:"2px 8px", border:"0.5px solid #e2e5ea", borderRadius:3, background:"#f1f5f9", color:"#475569", cursor:"pointer" }}>ACK selected</button>}
              {canAck && <button onClick={() => bulkAct("resolve", [...selectedAlerts])}
                style={{ fontSize:11, padding:"2px 8px", border:"0.5px solid #bbf7d0", borderRadius:3, background:"#dcfce7", color:"#166534", cursor:"pointer" }}>Resolve selected</button>}
              <div style={{ flex:1 }} />
              <button onClick={() => bulkAct("resolve", filteredAlerts.map(a => a.AlertID))}
                style={{ fontSize:11, padding:"2px 8px", border:"0.5px solid #C0D8E8", borderRadius:3, background:"#fff", color:"#006D8C", cursor:"pointer" }}>
                Resolve all filtered ({filteredAlerts.length})
              </button>
            </div>
          )}
          <div style={{ flex:1, overflowY:"auto", overflowX:"hidden" }}>
            <ErrBox msg={err} />
            {loading ? <Spinner /> : filteredAlerts.length === 0 ? (
              <div style={{ padding:32, textAlign:"center", color:"#94a3b8" }}>No alerts found.</div>
            ) : alertBlocks}
          </div>
        </div>

        {/* Right panel — diagnosis */}
        {diagPanel}

      </div>

      {/* ── News ticker ── */}
      <div style={{ height:28, background:"#1e2b3c", borderTop:"0.5px solid rgba(255,255,255,0.07)", overflow:"hidden", display:"flex", alignItems:"center", flexShrink:0 }}>
        <div style={{ paddingLeft:10, paddingRight:10, borderRight:"0.5px solid rgba(255,255,255,0.15)", flexShrink:0, height:"100%", display:"flex", alignItems:"center" }}>
          <span style={{ fontSize:9, color:"rgba(255,255,255,0.4)", textTransform:"uppercase", letterSpacing:"0.08em" }}>&#9679; LIVE</span>
        </div>
        {recentAlerts.length > 0 ? (
          <div style={{ display:"inline-flex", animation:`op1-ticker ${TICKER_DURATION_SEC}s linear infinite`, whiteSpace:"nowrap" }}>
            {[...recentAlerts, ...recentAlerts].map((item, i) => (
              <span key={i} style={{ padding:"0 24px", fontSize:11, color:"rgba(255,255,255,0.85)", borderRight:"1px solid rgba(255,255,255,0.1)", display:"inline-flex", alignItems:"center", gap:5 }}>
                <span style={{ color:(item.Status||"").toLowerCase()==="critical"?"#ef4444":(item.Status||"").toLowerCase()==="warning"?"#f59e0b":"#22c55e", fontSize:8 }}>&#9679;</span>
                {(item.Status||"").toUpperCase()} &middot; {item.MonitorName}{item.Message ? ` \u00b7 ${item.Message}` : ""}
                {item.CreatedAt && <span style={{ color:"rgba(255,255,255,0.4)", fontSize:10, marginLeft:4 }}>{parseUTC(item.CreatedAt)?.toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"})}</span>}
              </span>
            ))}
          </div>
        ) : (
          <span style={{ fontSize:11, color:"rgba(255,255,255,0.35)", paddingLeft:16, letterSpacing:"0.04em" }}>No recent alerts</span>
        )}
      </div>

    </div>
  );
}

// ═══ LOGS ═══
function LogsView({ api, machines, currentMachine, onNavigate, monitoringControl }) {
  const [logs, setLogs]                 = useState([]);
  const [loading, setLoading]           = useState(true);
  const [err, setErr]                   = useState("");
  const [filter, setFilter]             = useState({ severity:"all", search:"", machine:"" });
  const [logSort, setLogSort]           = useState({ col:'time', dir:'desc' });
  const [hoverCol, setHoverCol]         = useState(null);
  const [hoverRow, setHoverRow]         = useState(null);
  const [refreshInterval, setRefreshInterval] = useState(() => parseInt(localStorage.getItem('op1_logs_refresh_interval') || '30'));
  const [countdown, setCountdown]       = useState(() => parseInt(localStorage.getItem('op1_logs_refresh_interval') || '30'));
  const [refreshing, setRefreshing]     = useState(false);
  const [flashUpdated, setFlashUpdated] = useState(false);
  const [recentAlerts, setRecentAlerts] = useState([]);

  useEffect(()=>{
    const id = "op1-ticker-kf";
    if (!document.getElementById(id)) {
      const el = document.createElement("style"); el.id = id;
      el.textContent = "@keyframes op1-ticker { from { transform:translateX(0) } to { transform:translateX(-50%) } }";
      document.head.appendChild(el);
    }
    api("GET","/alerts/recent?top=20").then(r=>setRecentAlerts(Array.isArray(r)?r:[])).catch(()=>{});
  },[api]);

  const fetchLogs = useCallback((signal) => {
    setRefreshing(true);
    const q = new URLSearchParams({top:"200"});
    if (filter.machine) q.set("sourceMachine", filter.machine);
    api("GET", `/logs/ping?${q}`, undefined, signal)
      .then(r => {
        setLogs(Array.isArray(r) ? r : []);
        setErr("");
        setLoading(false);
        setRefreshing(false);
        setFlashUpdated(true);
        setTimeout(() => setFlashUpdated(false), 1000);
      })
      .catch(e => {
        if (e.name === "AbortError") return;
        setRefreshing(false);
        setLoading(false);
        setErr(e.message);
      });
  }, [api, filter.machine]);

  const handleIntervalChange = (e) => {
    const v = parseInt(e.target.value, 10);
    localStorage.setItem('op1_logs_refresh_interval', String(v));
    setRefreshInterval(v);
    setCountdown(v);
  };

  useEffect(() => {
    const ctrl = new AbortController();
    fetchLogs(ctrl.signal);
    const pollId = setInterval(() => { setCountdown(refreshInterval); fetchLogs(ctrl.signal); }, refreshInterval * 1000);
    const onVisible = () => { if (document.visibilityState === "visible") { setCountdown(refreshInterval); fetchLogs(ctrl.signal); } };
    document.addEventListener("visibilitychange", onVisible);
    return () => { ctrl.abort(); clearInterval(pollId); document.removeEventListener("visibilitychange", onVisible); };
  }, [fetchLogs, refreshInterval]);

  useEffect(() => {
    const id = setInterval(() => setCountdown(n => n > 0 ? n - 1 : 0), 1000);
    return () => clearInterval(id);
  }, []);

  const filtered = logs.filter(l=>{
    const sev = l.Severity||(l.IsSuccess?"info":"critical");
    if (filter.severity!=="all" && sev!==filter.severity) return false;
    if (filter.search && !(l.MonitorName||"").toLowerCase().includes(filter.search.toLowerCase()) && !(l.ErrorMessage||"").toLowerCase().includes(filter.search.toLowerCase())) return false;
    return true;
  });

  const handleSortClick = col => setLogSort(prev =>
    prev.col === col ? { col, dir: prev.dir==='asc'?'desc':'asc' } : { col, dir: col==='time'?'desc':'asc' }
  );

  const SUBTYPE_LABELS = {
    'infra-ping':'Ping / ICMP','infra-tcp':'TCP Port Check','infra-svc':'Service / Daemon',
    'infra-cpu':'CPU Utilization','infra-mem':'Memory Utilization','infra-disk':'Disk Space',
    'infra-diskio':'Disk I/O','infra-net':'Network Interface','infra-proc':'Process Monitor',
    'infra-log':'System Log','infra-hw':'Hardware Health',
    'app-svc':'Service Availability','app-api':'API Endpoint','app-port':'Port Check',
    'app-jvm':'JVM / .NET Runtime','app-thread':'Thread / Worker Pool',
    'app-mq':'Message Queue','app-cache':'Cache Monitor',
    'app-kpi':'Business KPI Counter','app-log':'Application Log','app-perf':'Performance Counter',
    'db-conn':'Connectivity','db-sess':'Session Count','db-block':'Blocking / Locking',
    'db-wait':'Wait Events','db-longq':'Long-Running Queries','db-perf':'DB Performance Counter',
    'db-space':'Tablespace / Storage','db-txlog':'Transaction Log',
    'db-query':'Query / Row Count','db-sqlctr':'SQL Counter',
    'web-http':'HTTP / HTTPS','web-ssl':'SSL Certificate','web-dns':'DNS Resolution',
    'web-redir':'Redirect / URL Path','web-cm':'Content Match','web-perf':'Page Load Time',
    'web-hdr':'HTTP Headers','web-api':'API Response Validation',
    'syn-login':'Login Flow','syn-multi':'Multi-Step Web',
    'syn-pay':'Payment / Checkout','syn-app':'Application Workflow',
    'syn-apiwf':'API Workflow','syn-sql':'SQL Query Check',
    'syn-ftp':'File Transfer','syn-email':'Email Flow'
  };
  const SEV_WEIGHT = { healthy:0, warning:1, critical:2, failed:3 };
  const sortedFiltered = [...filtered].sort((a,b)=>{
    const { col, dir } = logSort;
    let av, bv;
    if      (col==='time')     { av=a.CheckTimestamp||"";     bv=b.CheckTimestamp||""; }
    else if (col==='status')   { av=SEV_WEIGHT[a.Severity||"healthy"]??0; bv=SEV_WEIGHT[b.Severity||"healthy"]??0; }
    else if (col==='type')     { const ct=x=>(x.MonitorType||"")+(x.MonitorSubType?" \u00b7 "+x.MonitorSubType:""); av=ct(a).toLowerCase(); bv=ct(b).toLowerCase(); }
    else if (col==='group')    { av=a.GroupName||"\uffff";    bv=b.GroupName||"\uffff"; }
    else if (col==='name')     { av=(a.MonitorName||"").toLowerCase(); bv=(b.MonitorName||"").toLowerCase(); }
    else if (col==='source')   { av=(a.SourceMachine||"").toLowerCase(); bv=(b.SourceMachine||"").toLowerCase(); }
    else if (col==='target')   { av=a.Target||"\uffff";       bv=b.Target||"\uffff"; }
    else if (col==='response') { av=a.ResponseTimeMs??999999; bv=b.ResponseTimeMs??999999; }
    if (av < bv) return dir==='asc'?-1:1;
    if (av > bv) return dir==='asc'?1:-1;
    return 0;
  });

  const { isPaused: monIsPaused, canManageConfig, pausePopover, setPausePopover, pauseReason, setPauseReason, pauseBusy, togglePause, currentCtrl } = monitoringControl || {};

  return (
    <div style={{ display:"flex", flexDirection:"column", height:"calc(100vh - 20px)", overflow:"hidden", background:"#ffffff" }}>

      {/* ── Branding header ── */}
      <div style={{ display:"flex", alignItems:"center", justifyContent:"space-between", background:"#ffffff", borderBottom:"0.5px solid #f1f5f9", padding:"10px 16px", flexShrink:0 }}>
        <div style={{ display:"flex", alignItems:"center", gap:12 }}>
          <div style={{ width:36, height:36, borderRadius:8, background:"#006D8C", display:"flex", alignItems:"center", justifyContent:"center", fontSize:13, fontWeight:700, color:"#ffffff", letterSpacing:"0.02em", flexShrink:0 }}>OP1</div>
          <div>
            <div style={{ fontSize:16, fontWeight:600, color:"#1e293b" }}><span style={{ color:"#006D8C" }}>OP1</span> Operations Portal</div>
            <div style={{ fontSize:10, color:"#94a3b8" }}>Enterprise Monitoring · v1.4.457</div>
          </div>
        </div>
        <div style={{ display:"flex", alignItems:"center", gap:0 }}>
          {[
            ["Shown",    String(filtered.length), "#1e293b"],
            ["Severity", filter.severity==="all" ? "All" : filter.severity, "#1e293b"],
            ["Source",   filter.machine ? (machines?.find(m=>m.MachineName===filter.machine)?.MachineAlias||filter.machine) : "All", "#1e293b"],
          ].flatMap(([lbl, val, col], i, arr) => [
            <div key={lbl} style={{ padding:"0 16px", textAlign:"center" }}>
              <div style={{ fontSize:13, fontWeight:600, color:col }}>{val}</div>
              <div style={{ fontSize:9, color:"#94a3b8", textTransform:"uppercase", letterSpacing:"0.06em" }}>{lbl}</div>
            </div>,
            i < arr.length-1 && <div key={`div-${i}`} style={{ width:"0.5px", height:28, background:"#e2e5ea", flexShrink:0 }} />,
          ].filter(Boolean))}
        </div>
      </div>

      {/* ── Filter bar ── */}
      <div style={{ display:"flex", alignItems:"center", background:"#f8fafc", borderBottom:"0.5px solid #e2e5ea", height:36, padding:"0 12px", flexShrink:0, gap:10 }}>
        <select value={filter.severity} onChange={e=>setFilter(p=>({...p,severity:e.target.value}))}
          style={{ height:24, fontSize:11, border:"0.5px solid #e2e5ea", borderRadius:4, background:"#fff", color:"#374151", padding:"0 6px", outline:"none", cursor:"pointer" }}>
          <option value="all">All Severities</option>
          <option value="critical">Critical</option>
          <option value="warning">Warning</option>
          <option value="info">Info</option>
        </select>
        <div style={{ position:"relative", display:"inline-flex", alignItems:"center" }}>
          <span style={{ position:"absolute", left:7, fontSize:10, color:"#9CA3AF", pointerEvents:"none" }}>&#x1F50D;</span>
          <input placeholder="Search monitor or message…" value={filter.search} onChange={e=>setFilter(p=>({...p,search:e.target.value}))}
            style={{ height:24, width:200, paddingLeft:24, paddingRight:6, fontSize:11, border:"0.5px solid #e2e5ea", borderRadius:4, outline:"none", color:"#374151", background:"#fff", fontFamily:"inherit" }} />
        </div>
        {machines && machines.length >= 1 && (
          <select value={filter.machine} onChange={e=>setFilter(p=>({...p,machine:e.target.value}))}
            style={{ height:24, fontSize:11, border:"0.5px solid #e2e5ea", borderRadius:4, background:"#fff", color:"#374151", padding:"0 6px", outline:"none", cursor:"pointer" }}>
            <option value="">All Machines</option>
            {machines.map(m=><option key={m.MachineName} value={m.MachineName}>{m.MachineAlias||m.MachineName}{m.MachineName===currentMachine?" (this)":""}</option>)}
          </select>
        )}
        <div style={{ marginLeft:"auto", display:"flex", alignItems:"center", gap:5, flexShrink:0 }}>
          <select value={refreshInterval} onChange={handleIntervalChange}
            style={{ height:24, fontSize:11, border:"0.5px solid #e2e5ea", borderRadius:4, background:"#fff", color:"#374151", padding:"0 6px", outline:"none", cursor:"pointer" }}>
            <option value={10}>10s</option>
            <option value={30}>30s</option>
            <option value={60}>1m</option>
            <option value={300}>5m</option>
          </select>
          <button title="Refresh now" onClick={() => { setCountdown(refreshInterval); fetchLogs(); }}
            style={{ background:"none", border:"none", cursor:"pointer", padding:0, lineHeight:1, fontSize:14, color:"#64748b",
              opacity:refreshing?1:0.5, animation:refreshing?"spin 1s linear infinite":"none" }}>&#x27F3;</button>
          <span style={{ fontSize:10, color:flashUpdated?"#008C6F":refreshing?"#006D8C":"#4A4A4A" }}>
            {flashUpdated ? "\u2713" : refreshing ? "\u2026" : `${countdown}s`}
          </span>
        </div>
      </div>

      {/* ── Scrollable content ── */}
      <div style={{ flex:1, overflowY:"auto", paddingBottom:28 }}>
        <div style={{ display:"grid", gridTemplateColumns:"80px 62px 220px 220px 1fr 220px 220px 88px", padding:"6px 14px", position:"sticky", top:0, zIndex:1, background:"#FFFFFF", borderBottom:"1px solid #D8CCBA" }}>
          <span onClick={()=>handleSortClick('time')} onMouseEnter={()=>setHoverCol('time')} onMouseLeave={()=>setHoverCol(null)} style={{ fontSize:10, fontWeight:600, textTransform:"uppercase", color:"#4A4A4A", letterSpacing:"0.04em", cursor:"pointer", userSelect:"none", background:hoverCol==='time'?"#F5F0E8":"transparent" }}>Time{logSort.col==='time'&&<span style={{opacity:0.6,marginLeft:2}}>{logSort.dir==='asc'?'↑':'↓'}</span>}</span>
          <span onClick={()=>handleSortClick('status')} onMouseEnter={()=>setHoverCol('status')} onMouseLeave={()=>setHoverCol(null)} style={{ fontSize:10, fontWeight:600, textTransform:"uppercase", color:"#4A4A4A", letterSpacing:"0.04em", cursor:"pointer", userSelect:"none", background:hoverCol==='status'?"#F5F0E8":"transparent" }}>Status{logSort.col==='status'&&<span style={{opacity:0.6,marginLeft:2}}>{logSort.dir==='asc'?'↑':'↓'}</span>}</span>
          <span onClick={()=>handleSortClick('type')} onMouseEnter={()=>setHoverCol('type')} onMouseLeave={()=>setHoverCol(null)} style={{ fontSize:10, fontWeight:600, textTransform:"uppercase", color:"#4A4A4A", letterSpacing:"0.04em", cursor:"pointer", userSelect:"none", background:hoverCol==='type'?"#F5F0E8":"transparent" }}>Type{logSort.col==='type'&&<span style={{opacity:0.6,marginLeft:2}}>{logSort.dir==='asc'?'↑':'↓'}</span>}</span>
          <span onClick={()=>handleSortClick('group')} onMouseEnter={()=>setHoverCol('group')} onMouseLeave={()=>setHoverCol(null)} style={{ fontSize:10, fontWeight:600, textTransform:"uppercase", color:"#4A4A4A", letterSpacing:"0.04em", cursor:"pointer", userSelect:"none", background:hoverCol==='group'?"#F5F0E8":"transparent" }}>Group{logSort.col==='group'&&<span style={{opacity:0.6,marginLeft:2}}>{logSort.dir==='asc'?'↑':'↓'}</span>}</span>
          <span onClick={()=>handleSortClick('name')} onMouseEnter={()=>setHoverCol('name')} onMouseLeave={()=>setHoverCol(null)} style={{ fontSize:10, fontWeight:600, textTransform:"uppercase", color:"#4A4A4A", letterSpacing:"0.04em", cursor:"pointer", userSelect:"none", background:hoverCol==='name'?"#F5F0E8":"transparent" }}>Name{logSort.col==='name'&&<span style={{opacity:0.6,marginLeft:2}}>{logSort.dir==='asc'?'↑':'↓'}</span>}</span>
          <span onClick={()=>handleSortClick('source')} onMouseEnter={()=>setHoverCol('source')} onMouseLeave={()=>setHoverCol(null)} style={{ fontSize:10, fontWeight:600, textTransform:"uppercase", color:"#4A4A4A", letterSpacing:"0.04em", cursor:"pointer", userSelect:"none", paddingRight:8, textAlign:"left", background:hoverCol==='source'?"#F5F0E8":"transparent" }}>Source{logSort.col==='source'&&<span style={{opacity:0.6,marginLeft:2}}>{logSort.dir==='asc'?'↑':'↓'}</span>}</span>
          <span onClick={()=>handleSortClick('target')} onMouseEnter={()=>setHoverCol('target')} onMouseLeave={()=>setHoverCol(null)} style={{ fontSize:10, fontWeight:600, textTransform:"uppercase", color:"#4A4A4A", letterSpacing:"0.04em", cursor:"pointer", userSelect:"none", textAlign:"left", background:hoverCol==='target'?"#F5F0E8":"transparent" }}>Target{logSort.col==='target'&&<span style={{opacity:0.6,marginLeft:2}}>{logSort.dir==='asc'?'↑':'↓'}</span>}</span>
          <span onClick={()=>handleSortClick('response')} onMouseEnter={()=>setHoverCol('response')} onMouseLeave={()=>setHoverCol(null)} style={{ fontSize:10, fontWeight:600, textTransform:"uppercase", color:"#4A4A4A", letterSpacing:"0.04em", cursor:"pointer", userSelect:"none", textAlign:"right", background:hoverCol==='response'?"#F5F0E8":"transparent" }}>Response{logSort.col==='response'&&<span style={{opacity:0.6,marginLeft:2}}>{logSort.dir==='asc'?'↑':'↓'}</span>}</span>
        </div>
        <ErrBox msg={err} />
        {loading ? <Spinner /> : (
          <div>
            {sortedFiltered.length===0 && <div style={{ padding:32, textAlign:"center", color:"#94a3b8" }}>No log entries.</div>}
            {sortedFiltered.map((l,i)=>{
              const sev      = l.Severity||(l.IsSuccess?"healthy":"critical");
              const sevBg    = sev==="critical"||sev==="failed"?"#fca5a5":sev==="warning"?"#fcd34d":"#f1f5f9";
              const sevCol   = sev==="critical"||sev==="failed"?"#991b1b":sev==="warning"?"#92400e":"#475569";
              const sevLabel = sev==="critical"||sev==="failed"?"CRIT":sev==="warning"?"WARN":"OK";
              const leftCol  = sev==="warning"?"#E89A2E":sev==="critical"||sev==="failed"?"#D95C5C":"#008C6F";
              const rowBg    = hoverRow===i ? "#F0F7FB" : i % 2 === 0 ? "#FFFFFF" : "#F5F0E8";
              const rtText   = l.ResponseTimeMs ? `${l.ResponseTimeMs}ms` : "—";
              const srcLabel = l.SourceMachine ? (machines?.find(m=>m.MachineName===l.SourceMachine)?.MachineAlias||l.SourceMachine) : "";
              return (
                <div key={l.ResultID||i} onClick={()=>onNavigate&&onNavigate(l.MonitorID)} onMouseEnter={()=>setHoverRow(i)} onMouseLeave={()=>setHoverRow(null)} style={{ display:"grid", gridTemplateColumns:"80px 62px 220px 220px 1fr 220px 220px 88px", padding:"6px 14px", minHeight:34, alignItems:"center", borderLeft:`3px solid ${leftCol}`, background:rowBg, cursor:"pointer" }}>
                  <span style={{ fontFamily:"'DM Mono', monospace", fontSize:11, color:"#4A4A4A" }}>{l.CheckTimestamp?parseUTC(l.CheckTimestamp)?.toLocaleTimeString("en-US",{hour12:false}):"—"}</span>
                  <span style={{ fontSize:10, fontWeight:600, borderRadius:4, padding:"1px 6px", background:sevBg, color:sevCol, display:"inline-block", width:"fit-content" }}>{sevLabel}</span>
                  {(()=>{ const subLabel=SUBTYPE_LABELS[l.MonitorSubType]||l.MonitorSubType; const typeDisplay=subLabel?`${l.MonitorType} \u00b7 ${subLabel}`:(l.MonitorType||"—"); return <span style={{ fontSize:11, color:"#1A1A1A", whiteSpace:"nowrap", overflow:"hidden", textOverflow:"ellipsis", paddingRight:8 }}>{typeDisplay}</span>; })()}
                  <span style={{ fontSize:11, color:"#4A4A4A", whiteSpace:"nowrap", overflow:"hidden", textOverflow:"ellipsis", paddingRight:8 }}>{l.GroupName||"—"}</span>
                  <span style={{ fontSize:12, color:"#1A1A1A", whiteSpace:"nowrap", overflow:"hidden", textOverflow:"ellipsis", paddingRight:8 }}>{l.MonitorName}</span>
                  <span style={{ fontSize:11, color:"#4A4A4A", whiteSpace:"nowrap", overflow:"hidden", textOverflow:"ellipsis", textAlign:"left", paddingRight:8 }}>{srcLabel}</span>
                  <span style={{ fontSize:12, color:"#4A4A4A", whiteSpace:"nowrap", overflow:"hidden", textOverflow:"ellipsis", paddingRight:8 }}>{l.Target||""}</span>
                  <span style={{ fontFamily:"'DM Mono', monospace", fontSize:11, color:"#006D8C", fontWeight:600, textAlign:"right", paddingRight:8 }}>{rtText}</span>
                </div>
              );
            })}
          </div>
        )}
      </div>

      {/* ── News ticker ── */}
      <div style={{ position:"fixed", bottom:0, left:220, right:0, height:28, background:"#1e2b3c", borderTop:"0.5px solid rgba(255,255,255,0.07)", overflow:"hidden", display:"flex", alignItems:"center", zIndex:100 }}>
        <div style={{ paddingLeft:10, paddingRight:10, borderRight:"0.5px solid rgba(255,255,255,0.15)", flexShrink:0, height:"100%", display:"flex", alignItems:"center" }}>
          <span style={{ fontSize:9, color:"rgba(255,255,255,0.4)", textTransform:"uppercase", letterSpacing:"0.08em" }}>&#9679; LIVE</span>
        </div>
        {recentAlerts.length > 0 ? (
          <div style={{ display:"inline-flex", animation:`op1-ticker ${TICKER_DURATION_SEC}s linear infinite`, whiteSpace:"nowrap" }}>
            {[...recentAlerts, ...recentAlerts].map((item, i) => (
              <span key={i} style={{ padding:"0 24px", fontSize:11, color:"rgba(255,255,255,0.85)", borderRight:"1px solid rgba(255,255,255,0.1)", display:"inline-flex", alignItems:"center", gap:5 }}>
                <span style={{ color:(item.Status||"").toLowerCase()==="critical"?"#ef4444":(item.Status||"").toLowerCase()==="warning"?"#f59e0b":"#22c55e", fontSize:8 }}>&#9679;</span>
                {(item.Status||"").toUpperCase()} &middot; {item.MonitorName}{item.Message?` \u00b7 ${item.Message}`:""}
                {item.CreatedAt && <span style={{ color:"rgba(255,255,255,0.4)", fontSize:10, marginLeft:4 }}>{parseUTC(item.CreatedAt)?.toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"})}</span>}
              </span>
            ))}
          </div>
        ) : (
          <span style={{ fontSize:11, color:"rgba(255,255,255,0.35)", paddingLeft:16, letterSpacing:"0.04em" }}>No recent alerts</span>
        )}
      </div>

    </div>
  );
}

// ═══ REPORTS ═══
function ReportsView({ api, canGenerate, onNavigate, monitoringControl }) {
  const REPORT_TYPES = ["Availability & Uptime","Performance Summary","Alert History","Incident Report","Monitor Inventory"];
  const DATE_OPTIONS = [{ label:"Last 7 days", days:7 }, { label:"Last 30 days", days:30 }, { label:"Last 90 days", days:90 }];
  const ALERT_TABS   = ["All","Critical","Warning","Resolved"];

  const [reportType,      setReportType]      = useState(REPORT_TYPES[0]);
  const [filterGroup,     setFilterGroup]     = useState("");
  const [filterType,      setFilterType]      = useState("");
  const [filterDays,   setFilterDays]   = useState(7);
  const [filterMonitor,setFilterMonitor]= useState(0);

  const [filters,      setFilters]      = useState(null);
  const [summary,      setSummary]      = useState(null);
  const [uptimeRows,   setUptimeRows]   = useState([]);
  const [trendData,    setTrendData]    = useState([]);
  const [alertData,    setAlertData]    = useState([]);
  const [alertTab,     setAlertTab]     = useState("All");
  const [loading,      setLoading]      = useState(false);
  const [loadErr,      setLoadErr]      = useState("");
  const [recentAlerts, setRecentAlerts] = useState([]);

  const donutRef = useRef(null);
  const barRef   = useRef(null);
  const donutChart = useRef(null);
  const barChart   = useRef(null);

  // ── Build filter query string ──
  const buildQS = (days, gId, mId, mType) => {
    const p = [`days=${days||7}`];
    if (gId)    p.push(`groupName=${encodeURIComponent(gId)}`);
    if (mId>0)  p.push(`monitorId=${mId}`);
    if (mType)  p.push(`monitorType=${encodeURIComponent(mType)}`);
    return "?" + p.join("&");
  };

  // ── Fetch all report data ──
  const fetchData = useCallback(async (days, gId, mId, mType) => {
    setLoading(true); setLoadErr("");
    const qs = buildQS(days, gId, mId, mType);
    try {
      const [s, u, t, a] = await Promise.all([
        api("GET", `/reports/summary${qs}`),
        api("GET", `/reports/uptime${qs}`),
        api("GET", `/reports/trend${qs}`),
        api("GET", `/reports/alerts${qs}`),
      ]);
      setSummary(s);
      setUptimeRows(Array.isArray(u) ? u : []);
      setTrendData(Array.isArray(t) ? t : []);
      setAlertData(Array.isArray(a) ? a : []);
    } catch(e) { setLoadErr("Failed to load report data — check connection."); }
    finally { setLoading(false); }
  }, [api]);

  // ── Initial load ──
  useEffect(() => {
    const id = "op1-ticker-kf";
    if (!document.getElementById(id)) {
      const el = document.createElement("style"); el.id = id;
      el.textContent = "@keyframes op1-ticker { from { transform:translateX(0) } to { transform:translateX(-50%) } }";
      document.head.appendChild(el);
    }
    api("GET","/reports/filters").then(r=>setFilters(r)).catch(()=>{});
    fetchData(7, "", 0, "");
    api("GET","/alerts/recent?top=20").then(r=>setRecentAlerts(Array.isArray(r)?r:[])).catch(()=>{});
  }, []);

  // ── Chart.js donut ──
  useEffect(() => {
    if (!donutRef.current || !summary) return;
    const h = summary.monitorsHealthy || 0;
    const w = summary.monitorCount - h - (summary.monitorsDown||0) - (summary.monitorsSuppressed||0);
    const d = summary.monitorsDown || 0;
    const s = summary.monitorsSuppressed || 0;
    const total = summary.monitorCount || 1;
    const centerTextPlugin = {
      id: "op1CenterText",
      beforeDraw(chart) {
        const { width, height, ctx } = chart;
        ctx.restore();
        const fs = Math.min(height, width) * 0.18;
        ctx.font = `700 ${fs}px DM Sans, sans-serif`;
        ctx.fillStyle = "#1A1A1A";
        ctx.textBaseline = "middle";
        const text = String(total);
        const tx = (width - ctx.measureText(text).width) / 2;
        ctx.fillText(text, tx, height / 2 - 6);
        ctx.font = `400 ${fs*0.45}px DM Sans, sans-serif`;
        ctx.fillStyle = "#7A9AB8";
        const sub = "monitors";
        const sx = (width - ctx.measureText(sub).width) / 2;
        ctx.fillText(sub, sx, height / 2 + fs * 0.55);
        ctx.save();
      }
    };
    if (donutChart.current) { donutChart.current.destroy(); donutChart.current = null; }
    donutChart.current = new Chart(donutRef.current, {
      type: "doughnut",
      data: {
        labels: ["Healthy","Warning","Critical","Suppressed"],
        datasets: [{ data:[h, Math.max(w,0), d, s],
          backgroundColor:["#008C6F","#E89A2E","#D95C5C","#D8CCBA"],
          borderWidth:0, hoverOffset:4 }]
      },
      options: { responsive:false, cutout:"68%", plugins:{ legend:{ display:false }, tooltip:{ callbacks:{ label: ctx => ` ${ctx.label}: ${ctx.raw}` } } } },
      plugins: [centerTextPlugin]
    });
    return () => { if(donutChart.current){donutChart.current.destroy();donutChart.current=null;} };
  }, [summary]);

  // ── Chart.js bar ──
  useEffect(() => {
    if (!barRef.current) return;
    if (barChart.current) { barChart.current.destroy(); barChart.current = null; }
    if (!trendData || trendData.length === 0) return;
    const MONTHS = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
    const labels = trendData.map(d => {
      const dk = typeof d.date === "string" ? d.date.slice(0,10) : "";
      const dt = new Date(dk + "T00:00:00");
      return isNaN(dt) ? dk : `${MONTHS[dt.getMonth()]} ${dt.getDate()}`;
    });
    const values = trendData.map(d => d.avgResponseMs || 0);
    barChart.current = new Chart(barRef.current, {
      type: "bar",
      data: {
        labels,
        datasets: [{ label:"Avg Response (ms)", data:values,
          backgroundColor:"rgba(0,109,140,0.8)", borderRadius:4, borderSkipped:false }]
      },
      options: {
        responsive:true, maintainAspectRatio:false,
        plugins:{ legend:{ display:false }, tooltip:{ callbacks:{ label: ctx => ` ${ctx.raw} ms` } } },
        scales: {
          x: { grid:{ display:false }, ticks:{ color:"#7A9AB8", font:{ size:11 } } },
          y: { grid:{ color:"rgba(0,0,0,0.05)" }, ticks:{ color:"#7A9AB8", font:{ size:11 }, callback: v => `${v}ms` }, beginAtZero:true }
        }
      }
    });
    return () => { if(barChart.current){barChart.current.destroy();barChart.current=null;} };
  }, [trendData]);

  // ── Apply filters ──
  const doApply = () => fetchData(filterDays, filterGroup, filterMonitor, filterType);
  const doReset = () => {
    setFilterGroup(""); setFilterType(""); setFilterDays(7); setFilterMonitor(0);
    fetchData(7, "", 0, "");
  };

  // ── Date range display ──
  const now = new Date();
  const since = new Date(now.getTime() - filterDays * 86400000);
  const fmtDate = d => { const MONTHS=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]; return `${MONTHS[d.getMonth()]} ${d.getDate()}, ${d.getFullYear()}`; };
  const dateRangeText = `${fmtDate(since)} – ${fmtDate(now)}`;

  // ── Monitor options for current group ──
  const monitorOptions = filters ? (
    filterGroup
      ? (filters.monitorsByGroup?.[filterGroup] || [])
      : Object.values(filters.monitorsByGroup || {}).flat()
  ) : [];

  // ── Alert table filtered by tab ──
  const filteredAlerts = alertData.filter(a => {
    if (alertTab === "All") return true;
    if (alertTab === "Critical") return (a.Severity||"").toLowerCase() === "critical";
    if (alertTab === "Warning")  return (a.Severity||"").toLowerCase() === "warning";
    if (alertTab === "Resolved") return !!a.ResolvedAt;
    return true;
  }).slice(0, 20);

  // ── Branding props ──
  const { isPaused: monIsPaused, canManageConfig, pausePopover, setPausePopover, pauseReason, setPauseReason, pauseBusy, togglePause, currentCtrl } = monitoringControl || {};

  // ── Uptime bar width ──
  const uptimePct = pct => Math.max(0, Math.min(100, pct || 0));
  const uptimeColor = pct => pct >= 99 ? "#008C6F" : pct >= 95 ? "#E89A2E" : "#D95C5C";

  // ── Alert duration ──
  const alertDuration = (a) => {
    if (!a.CreatedAt) return "—";
    const start = parseUTC(a.CreatedAt);
    if (!start) return "—";
    const end = a.ResolvedAt ? parseUTC(a.ResolvedAt) : new Date();
    if (!end) return "Ongoing";
    const diffMs = end - start;
    if (diffMs < 0) return "—";
    const mins = Math.floor(diffMs / 60000);
    if (mins < 60) return `${mins}m`;
    return `${Math.floor(mins/60)}h ${mins%60}m`;
  };

  // ── Monitor options by group for select ──
  const monitorOptGroups = filters ? Object.entries(filters.monitorsByGroup || {}) : [];

  // ── Saved report presets ──
  const applyPreset = (days) => {
    setFilterDays(days); setFilterGroup(""); setFilterType(""); setFilterMonitor(0);
    fetchData(days, "", 0, "");
  };

  return (
    <div style={{ display:"flex", flexDirection:"column", height:"calc(100vh - 20px)", overflow:"hidden", background:"#ffffff" }}>

      {/* ── Branding header ── */}
      <div style={{ display:"flex", alignItems:"center", justifyContent:"space-between", background:"#ffffff", borderBottom:"0.5px solid #f1f5f9", padding:"10px 16px", flexShrink:0 }}>
        <div style={{ display:"flex", alignItems:"center", gap:12 }}>
          <div style={{ width:36, height:36, borderRadius:8, background:"#006D8C", display:"flex", alignItems:"center", justifyContent:"center", fontSize:13, fontWeight:700, color:"#ffffff", letterSpacing:"0.02em", flexShrink:0 }}>OP1</div>
          <div>
            <div style={{ fontSize:16, fontWeight:600, color:"#1e293b" }}><span style={{ color:"#006D8C" }}>OP1</span> Operations Portal</div>
            <div style={{ fontSize:10, color:"#94a3b8" }}>Enterprise Monitoring · v1.4.457</div>
          </div>
        </div>
        <div style={{ display:"flex", alignItems:"center", gap:12 }}>
          <button onClick={()=>window.print()} style={{ padding:"6px 14px", borderRadius:6, border:"1px solid #006D8C", background:"#ffffff", color:"#006D8C", fontSize:12, fontWeight:600, cursor:"pointer" }}>Save Report</button>
          <button onClick={()=>window.print()} style={{ padding:"6px 14px", borderRadius:6, border:"none", background:"#006D8C", color:"#ffffff", fontSize:12, fontWeight:600, cursor:"pointer" }}>Export PDF</button>
        </div>
      </div>

      {/* ── Body: left panel + main content ── */}
      <div style={{ display:"flex", flex:1, overflow:"hidden" }}>

        {/* ── Left panel ── */}
        <div style={{ width:232, flexShrink:0, background:"#ffffff", borderRight:"1px solid #D8CCBA", display:"flex", flexDirection:"column", overflowY:"auto" }}>

          {/* Report Type */}
          <div style={{ padding:"14px 14px 10px" }}>
            <div style={{ fontSize:10, fontWeight:700, color:"#7A9AB8", textTransform:"uppercase", letterSpacing:"0.07em", marginBottom:7 }}>Report Type</div>
            <select value={reportType} onChange={e=>setReportType(e.target.value)}
              style={{ width:"100%", padding:"6px 8px", borderRadius:6, border:"1px solid #D8CCBA", fontSize:12, color:"#1A1A1A", background:"#fff", cursor:"pointer" }}>
              {REPORT_TYPES.map(t => <option key={t} value={t}>{t}</option>)}
            </select>
          </div>

          <div style={{ borderTop:"1px solid #F0EDE7", margin:"0 14px" }} />

          {/* Filters */}
          <div style={{ padding:"10px 14px" }}>
            <div style={{ fontSize:10, fontWeight:700, color:"#7A9AB8", textTransform:"uppercase", letterSpacing:"0.07em", marginBottom:9 }}>Filters</div>

            <div style={{ marginBottom:8 }}>
              <div style={{ fontSize:11, fontWeight:600, color:"#4A4A4A", marginBottom:3 }}>Monitor Group</div>
              <select value={filterGroup} onChange={e=>{ setFilterGroup(e.target.value); setFilterMonitor(0); }}
                style={{ width:"100%", padding:"5px 7px", borderRadius:5, border:"1px solid #D8CCBA", fontSize:12, color:"#1A1A1A", background:"#fff" }}>
                <option value="">All Groups</option>
                {(filters?.groups||[]).map(g=><option key={g} value={g}>{g||"Ungrouped"}</option>)}
              </select>
            </div>

            <div style={{ marginBottom:8 }}>
              <div style={{ fontSize:11, fontWeight:600, color:"#4A4A4A", marginBottom:3 }}>Monitor Type</div>
              <select value={filterType} onChange={e=>setFilterType(e.target.value)}
                style={{ width:"100%", padding:"5px 7px", borderRadius:5, border:"1px solid #D8CCBA", fontSize:12, color:"#1A1A1A", background:"#fff" }}>
                <option value="">All Types</option>
                {(filters?.monitorTypes||[]).map(t=><option key={t} value={t}>{t}</option>)}
              </select>
            </div>

            <div style={{ marginBottom:8 }}>
              <div style={{ fontSize:11, fontWeight:600, color:"#4A4A4A", marginBottom:3 }}>Date Range</div>
              <select value={filterDays} onChange={e=>setFilterDays(parseInt(e.target.value))}
                style={{ width:"100%", padding:"5px 7px", borderRadius:5, border:"1px solid #D8CCBA", fontSize:12, color:"#1A1A1A", background:"#fff" }}>
                {DATE_OPTIONS.map(o=><option key={o.days} value={o.days}>{o.label}</option>)}
              </select>
            </div>

            <div style={{ marginBottom:12 }}>
              <div style={{ fontSize:11, fontWeight:600, color:"#4A4A4A", marginBottom:3 }}>
                {filterGroup ? `All in ${filterGroup||"Ungrouped"}` : "Specific Monitor"}
              </div>
              <select value={filterMonitor} onChange={e=>setFilterMonitor(parseInt(e.target.value)||0)}
                style={{ width:"100%", padding:"5px 7px", borderRadius:5, border:"1px solid #D8CCBA", fontSize:12, color:"#1A1A1A", background:"#fff" }}>
                <option value={0}>{filterGroup ? `All in ${filterGroup||"group"}` : "All Monitors"}</option>
                {filterGroup
                  ? (filters?.monitorsByGroup?.[filterGroup]||[]).map(m=><option key={m.id} value={m.id}>{m.name}</option>)
                  : monitorOptGroups.map(([grp, mons]) =>
                      <optgroup key={grp} label={grp||"Ungrouped"}>
                        {mons.map(m=><option key={m.id} value={m.id}>{m.name}</option>)}
                      </optgroup>
                    )
                }
              </select>
            </div>

            <div style={{ display:"flex", gap:6 }}>
              <button onClick={doApply} style={{ flex:1, padding:"7px 0", borderRadius:6, border:"none", background:"#006D8C", color:"#fff", fontSize:12, fontWeight:600, cursor:"pointer" }}>Apply</button>
              <button onClick={doReset} style={{ flex:1, padding:"7px 0", borderRadius:6, border:"1px solid #006D8C", background:"#fff", color:"#006D8C", fontSize:12, fontWeight:600, cursor:"pointer" }}>Reset</button>
            </div>
          </div>

          <div style={{ borderTop:"1px solid #F0EDE7", margin:"0 14px" }} />

          {/* Saved Reports */}
          <div style={{ padding:"10px 14px 16px" }}>
            <div style={{ fontSize:10, fontWeight:700, color:"#7A9AB8", textTransform:"uppercase", letterSpacing:"0.07em", marginBottom:9 }}>Saved Reports</div>
            {[{label:"Weekly Ops Summary", days:7},{label:"Monthly SLA Report", days:30}].map(p =>
              <button key={p.label} onClick={()=>applyPreset(p.days)}
                style={{ width:"100%", display:"flex", alignItems:"center", gap:8, padding:"7px 8px", borderRadius:6, border:"none", background:"transparent", textAlign:"left", cursor:"pointer", fontSize:12, color:"#4A4A4A", marginBottom:2 }}
                onMouseEnter={e=>e.currentTarget.style.background="#F5F0E8"}
                onMouseLeave={e=>e.currentTarget.style.background="transparent"}>
                <svg width="13" height="13" viewBox="0 0 16 16" fill="none">
                  <rect x="2" y="1" width="10" height="14" rx="1.5" stroke="#7A9AB8" strokeWidth="1.5"/>
                  <line x1="4.5" y1="5.5" x2="9.5" y2="5.5" stroke="#7A9AB8" strokeWidth="1.2"/>
                  <line x1="4.5" y1="8.5" x2="9.5" y2="8.5" stroke="#7A9AB8" strokeWidth="1.2"/>
                  <line x1="4.5" y1="11.5" x2="7.5" y2="11.5" stroke="#7A9AB8" strokeWidth="1.2"/>
                </svg>
                {p.label}
              </button>
            )}
          </div>
        </div>

        {/* ── Main content ── */}
        <div style={{ flex:1, overflowY:"auto", background:"#F5F0E8" }}>

          {/* Date range strip */}
          <div style={{ display:"flex", alignItems:"center", justifyContent:"space-between", padding:"10px 16px", background:"#ffffff", borderBottom:"1px solid #D8CCBA", flexShrink:0 }}>
            <div style={{ display:"flex", gap:6 }}>
              {[{l:"Today",d:1},{l:"7 days",d:7},{l:"30 days",d:30},{l:"90 days",d:90}].map(o =>
                <button key={o.d} onClick={()=>{ setFilterDays(o.d); fetchData(o.d, filterGroup, filterMonitor, filterType); }}
                  style={{ padding:"4px 10px", borderRadius:14, border:`1px solid ${filterDays===o.d?"#006D8C":"#D8CCBA"}`, background:filterDays===o.d?"#006D8C":"#fff", color:filterDays===o.d?"#fff":"#4A4A4A", fontSize:11, fontWeight:600, cursor:"pointer" }}>
                  {o.l}
                </button>
              )}
            </div>
            <span style={{ fontSize:11, color:"#7A9AB8", fontWeight:500 }}>{dateRangeText}</span>
          </div>

          {loading && <div style={{ padding:"24px 16px", color:"#7A9AB8", fontSize:12, textAlign:"center" }}>Loading…</div>}
          {loadErr && <div style={{ padding:"16px", color:"#D95C5C", fontSize:12 }}>{loadErr}</div>}

          {!loading && !loadErr && (
            <div style={{ padding:"14px 16px" }}>

              {/* KPI row */}
              <div style={{ display:"grid", gridTemplateColumns:"repeat(4,1fr)", gap:12, marginBottom:14 }}>
                <div style={{ background:"#fff", borderRadius:8, border:"1px solid #D8CCBA", padding:"14px 16px" }}>
                  <div style={{ fontSize:9, fontWeight:700, color:"#7A9AB8", textTransform:"uppercase", letterSpacing:"0.07em", marginBottom:4 }}>Overall Uptime</div>
                  <div style={{ fontSize:22, fontWeight:700, color:"#008C6F" }}>{summary ? `${summary.uptimePct.toFixed(2)}%` : "—"}</div>
                  <div style={{ fontSize:10, color:"#7A9AB8", marginTop:2 }}>last {filterDays} days</div>
                </div>
                <div style={{ background:"#fff", borderRadius:8, border:"1px solid #D8CCBA", padding:"14px 16px" }}>
                  <div style={{ fontSize:9, fontWeight:700, color:"#7A9AB8", textTransform:"uppercase", letterSpacing:"0.07em", marginBottom:4 }}>Avg Response Time</div>
                  <div style={{ fontSize:22, fontWeight:700, color:"#006D8C" }}>{summary ? `${summary.avgResponseMs} ms` : "—"}</div>
                  <div style={{ fontSize:10, color:"#7A9AB8", marginTop:2 }}>{summary?.monitorCount||0} monitors</div>
                </div>
                <div style={{ background:"#fff", borderRadius:8, border:"1px solid #D8CCBA", padding:"14px 16px" }}>
                  <div style={{ fontSize:9, fontWeight:700, color:"#7A9AB8", textTransform:"uppercase", letterSpacing:"0.07em", marginBottom:4 }}>Total Alerts</div>
                  <div style={{ fontSize:22, fontWeight:700, color:"#D95C5C" }}>{summary ? summary.totalAlerts : "—"}</div>
                  <div style={{ fontSize:10, color:"#7A9AB8", marginTop:2 }}>{summary?.criticalAlerts||0} critical · {summary?.warningAlerts||0} warning</div>
                </div>
                <div style={{ background:"#fff", borderRadius:8, border:"1px solid #D8CCBA", padding:"14px 16px" }}>
                  <div style={{ fontSize:9, fontWeight:700, color:"#7A9AB8", textTransform:"uppercase", letterSpacing:"0.07em", marginBottom:4 }}>Monitors Down</div>
                  <div style={{ fontSize:22, fontWeight:700, color:"#E89A2E" }}>{summary ? summary.monitorsDown : "—"}</div>
                  <div style={{ fontSize:10, color:"#7A9AB8", marginTop:2 }}>{summary?.monitorsHealthy||0} healthy · {summary?.monitorsSuppressed||0} suppressed</div>
                </div>
              </div>

              {/* Uptime by Monitor — full width, grouped */}
              <div style={{ background:"#fff", borderRadius:8, border:"1px solid #D8CCBA", overflow:"hidden", marginBottom:14 }}>
                {/* Section header with inline compact donut */}
                <div style={{ padding:"11px 14px", borderBottom:"1px solid #F0EDE7", display:"flex", alignItems:"center", justifyContent:"space-between" }}>
                  <div style={{ display:"flex", alignItems:"center", gap:6 }}>
                    <span style={{ fontSize:12, fontWeight:600, color:"#1A1A1A" }}>Uptime by Monitor</span>
                  </div>
                  <div style={{ display:"flex", alignItems:"center", gap:10 }}>
                    <canvas ref={donutRef} width={60} height={60} style={{ flexShrink:0 }} />
                    <div style={{ display:"flex", flexDirection:"column", gap:3 }}>
                      {[["Healthy","#008C6F",summary?.monitorsHealthy||0],["Warning","#E89A2E",Math.max(0,(summary?.monitorCount||0)-(summary?.monitorsHealthy||0)-(summary?.monitorsDown||0)-(summary?.monitorsSuppressed||0))],["Critical","#D95C5C",summary?.monitorsDown||0]].map(([lbl,col,val])=>(
                        <span key={lbl} style={{ display:"inline-flex", alignItems:"center", gap:5, fontSize:10, color:"#4A4A4A" }}>
                          <span style={{ width:7, height:7, borderRadius:"50%", background:col, display:"inline-block", flexShrink:0 }} />
                          {lbl} <strong style={{ color:"#1A1A1A" }}>{val}</strong>
                        </span>
                      ))}
                    </div>
                  </div>
                </div>
                {/* Grouped monitor rows */}
                {uptimeRows.length === 0
                  ? <div style={{ padding:"24px", textAlign:"center", color:"#7A9AB8", fontSize:12 }}>No check history for this period.</div>
                  : (() => {
                      const groups = {};
                      uptimeRows.forEach(r => {
                        const gn = r.groupName || "";
                        if (!groups[gn]) groups[gn] = [];
                        groups[gn].push(r);
                      });
                      const sortedKeys = Object.keys(groups).sort((a, b) => {
                        if (!a) return 1; if (!b) return -1;
                        return a.localeCompare(b);
                      });
                      const colHdr = (
                        <div style={{ display:"flex", alignItems:"center", padding:"6px 12px", borderBottom:"1px solid #D8CCBA", background:"#fff" }}>
                          <div style={{ flex:1, minWidth:0, fontSize:10, fontWeight:600, color:"#4A4A4A", textTransform:"uppercase", letterSpacing:"0.06em" }}>Monitor</div>
                          <div style={{ width:120, flexShrink:0, fontSize:10, fontWeight:600, color:"#4A4A4A", textTransform:"uppercase", letterSpacing:"0.06em" }}>Type</div>
                          <div style={{ width:80, flexShrink:0, fontSize:10, fontWeight:600, color:"#4A4A4A", textTransform:"uppercase", letterSpacing:"0.06em", textAlign:"right" }}>Uptime</div>
                          <div style={{ width:120, flexShrink:0, marginLeft:10 }} />
                        </div>
                      );
                      return sortedKeys.map((gn, gi) => {
                        const mons = groups[gn].slice().sort((a,b) => a.uptimePct - b.uptimePct);
                        const avgPct = mons.reduce((s,r) => s + r.uptimePct, 0) / mons.length;
                        const allHealthy = mons.every(r => r.uptimePct >= 100);
                        const groupLabel = gn || "Ungrouped";
                        return (
                          <div key={gn||"ungrouped"} style={{ marginTop: gi > 0 ? 12 : 0 }}>
                            {gi === 0 && colHdr}
                            <div style={{ background:"#F5F0E8", padding:"8px 12px", borderBottom:"1px solid #D8CCBA", display:"flex", alignItems:"center", justifyContent:"space-between" }}>
                              <span style={{ fontSize:12, fontWeight:700, color:"#1A1A1A" }}>{groupLabel}</span>
                              <div style={{ display:"flex", alignItems:"center", gap:10 }}>
                                {allHealthy
                                  ? <span style={{ fontSize:11, color:"#008C6F", fontWeight:600 }}>✓ All healthy</span>
                                  : <span style={{ fontSize:12, fontWeight:700, color: avgPct>=99?"#008C6F":avgPct>=95?"#E89A2E":"#D95C5C" }}>{avgPct.toFixed(2)}%</span>
                                }
                                <span style={{ fontSize:11, color:"#7A9AB8" }}>{mons.length} monitor{mons.length!==1?"s":""}</span>
                              </div>
                            </div>
                            {mons.map((r,i) => (
                              <div key={r.monitorId} style={{ display:"flex", alignItems:"center", padding:"7px 12px", borderTop:"1px solid #F0EDE7", background: i%2===0?"#FFFFFF":"#F5F0E8", fontSize:12 }}>
                                <div onClick={()=>onNavigate&&onNavigate(r.monitorId)} style={{ flex:1, minWidth:0, color:"#006D8C", overflow:"hidden", textOverflow:"ellipsis", whiteSpace:"nowrap", cursor:onNavigate?"pointer":"default" }}>{r.name}</div>
                                <div style={{ width:120, flexShrink:0 }}>
                                  <span style={{ fontSize:10, borderRadius:4, padding:"1px 6px", background:"#E0F2F7", color:"#006D8C" }}>{r.monitorType}</span>
                                </div>
                                <div style={{ width:80, flexShrink:0, textAlign:"right", color: r.uptimePct>=99?"#008C6F":r.uptimePct>=95?"#E89A2E":"#D95C5C", fontWeight:600 }}>{r.uptimePct.toFixed(2)}%</div>
                                <div style={{ width:120, flexShrink:0, marginLeft:10 }}>
                                  <div style={{ height:6, borderRadius:3, background:"#F0EDE7", overflow:"hidden" }}>
                                    <div style={{ height:"100%", width:`${uptimePct(r.uptimePct)}%`, background:uptimeColor(r.uptimePct), borderRadius:3 }} />
                                  </div>
                                </div>
                              </div>
                            ))}
                          </div>
                        );
                      });
                    })()
                }
              </div>

              {/* Response trend bar chart */}
              <div style={{ background:"#fff", borderRadius:8, border:"1px solid #D8CCBA", padding:"14px", marginBottom:14 }}>
                <div style={{ fontSize:12, fontWeight:600, color:"#1A1A1A", marginBottom:12 }}>Response Time Trend</div>
                {trendData.length === 0
                  ? <div style={{ padding:"24px", textAlign:"center", color:"#7A9AB8", fontSize:12 }}>No response time data for this period.</div>
                  : <div style={{ height:200, position:"relative" }}><canvas ref={barRef} /></div>
                }
              </div>

              {/* Alert history */}
              <div style={{ background:"#fff", borderRadius:8, border:"1px solid #D8CCBA", overflow:"hidden" }}>
                <div style={{ padding:"11px 14px", borderBottom:"1px solid #F0EDE7", display:"flex", alignItems:"center", justifyContent:"space-between" }}>
                  <span style={{ fontSize:12, fontWeight:600, color:"#1A1A1A" }}>Alert History</span>
                  <div style={{ display:"flex", gap:4 }}>
                    {ALERT_TABS.map(t=>(
                      <button key={t} onClick={()=>setAlertTab(t)}
                        style={{ padding:"3px 10px", borderRadius:12, border:`1px solid ${alertTab===t?"#006D8C":"#D8CCBA"}`, background:alertTab===t?"#006D8C":"#fff", color:alertTab===t?"#fff":"#4A4A4A", fontSize:11, fontWeight:500, cursor:"pointer" }}>
                        {t}
                      </button>
                    ))}
                  </div>
                </div>
                {filteredAlerts.length === 0
                  ? <div style={{ padding:"24px", textAlign:"center", color:"#7A9AB8", fontSize:12 }}>No alerts in this period.</div>
                  : (
                    <table style={{ width:"100%", borderCollapse:"collapse", fontSize:12 }}>
                      <thead>
                        <tr style={{ background:"#F5F0E8" }}>
                          {["Time","Monitor","Severity","Message","Duration"].map(h=>(
                            <th key={h} style={{ padding:"7px 12px", textAlign:"left", fontSize:9, fontWeight:700, color:"#7A9AB8", textTransform:"uppercase", letterSpacing:"0.06em" }}>{h}</th>
                          ))}
                        </tr>
                      </thead>
                      <tbody>
                        {filteredAlerts.map((a,i) => {
                          const sev = (a.Severity||"").toLowerCase();
                          const sevColors = { critical:{ bg:"#FAEAEA", color:"#D95C5C" }, warning:{ bg:"#FDF3E0", color:"#E89A2E" }, info:{ bg:"#F5F0E8", color:"#4A4A4A" } };
                          const sc = sevColors[sev] || { bg:"#F0EDE7", color:"#4A4A4A" };
                          return (
                            <tr key={a.AlertID||i} style={{ borderTop:"1px solid #F0EDE7", background: i%2===0?"#fff":"#FAFAFA" }}>
                              <td style={{ padding:"7px 12px", color:"#7A9AB8", fontSize:11, whiteSpace:"nowrap" }}>{a.CreatedAt ? parseUTC(a.CreatedAt)?.toLocaleString([],{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit"})||"—" : "—"}</td>
                              <td style={{ padding:"7px 12px", maxWidth:160, overflow:"hidden", textOverflow:"ellipsis", whiteSpace:"nowrap" }}><span onClick={()=>a.MonitorID&&onNavigate&&onNavigate(a.MonitorID)} style={{ color:"#006D8C", cursor:a.MonitorID&&onNavigate?"pointer":"default" }}>{a.MonitorName||"—"}</span></td>
                              <td style={{ padding:"7px 12px" }}>
                                <span style={{ fontSize:10, borderRadius:4, padding:"2px 7px", background:sc.bg, color:sc.color, fontWeight:600 }}>{a.Severity||"—"}</span>
                              </td>
                              <td style={{ padding:"7px 12px", color:"#4A4A4A", maxWidth:220, overflow:"hidden", textOverflow:"ellipsis", whiteSpace:"nowrap" }}>{a.Message||"—"}</td>
                              <td style={{ padding:"7px 12px", color:"#7A9AB8", fontSize:11, whiteSpace:"nowrap" }}>{a.ResolvedAt ? alertDuration(a) : <span style={{ color:"#E89A2E", fontWeight:600 }}>Ongoing</span>}</td>
                            </tr>
                          );
                        })}
                      </tbody>
                    </table>
                  )
                }
              </div>

            </div>
          )}
        </div>
      </div>

      {/* ── News ticker ── */}
      <div style={{ height:28, background:"#1e2b3c", borderTop:"0.5px solid rgba(255,255,255,0.07)", overflow:"hidden", display:"flex", alignItems:"center", flexShrink:0 }}>
        <div style={{ paddingLeft:10, paddingRight:10, borderRight:"0.5px solid rgba(255,255,255,0.15)", flexShrink:0, height:"100%", display:"flex", alignItems:"center" }}>
          <span style={{ fontSize:9, color:"rgba(255,255,255,0.4)", textTransform:"uppercase", letterSpacing:"0.08em" }}>&#9679; LIVE</span>
        </div>
        {recentAlerts.length > 0 ? (
          <div style={{ display:"inline-flex", animation:`op1-ticker ${TICKER_DURATION_SEC}s linear infinite`, whiteSpace:"nowrap" }}>
            {[...recentAlerts, ...recentAlerts].map((item, i) => (
              <span key={i} style={{ padding:"0 24px", fontSize:11, color:"rgba(255,255,255,0.85)", borderRight:"1px solid rgba(255,255,255,0.1)", display:"inline-flex", alignItems:"center", gap:5 }}>
                <span style={{ color:(item.Status||"").toLowerCase()==="critical"?"#ef4444":(item.Status||"").toLowerCase()==="warning"?"#f59e0b":"#22c55e", fontSize:8 }}>&#9679;</span>
                {(item.Status||"").toUpperCase()} &middot; {item.MonitorName}{item.Message?` \u00b7 ${item.Message}`:""}
                {item.CreatedAt && <span style={{ color:"rgba(255,255,255,0.4)", fontSize:10, marginLeft:4 }}>{parseUTC(item.CreatedAt)?.toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"})}</span>}
              </span>
            ))}
          </div>
        ) : (
          <span style={{ fontSize:11, color:"rgba(255,255,255,0.35)", paddingLeft:16, letterSpacing:"0.04em" }}>No recent alerts</span>
        )}
      </div>

    </div>
  );
}

// ═══ USERS ═══
function UsersView({ api, monitoringControl }) {
  const [users, setUsers]               = useState([]);
  const [loading, setLoading]           = useState(true);
  const [err, setErr]                   = useState("");
  const [ok, setOk]                     = useState("");
  const [page, setPage]                 = useState(1);
  const [search, setSearch]             = useState("");
  const [roleFilter, setRoleFilter]     = useState("all");
  const [statusFilter, setStatusFilter] = useState("all");
  const [newUser, setNewUser]           = useState({ Email:"", DisplayName:"", RoleID:"2", Password:"" });
  const [busy, setBusy]                 = useState({});
  const [recentAlerts, setRecentAlerts] = useState([]);
  const [setPwdModal, setSetPwdModal]   = useState({ open:false, userId:null, displayName:"" });
  const [setPwdForm, setSetPwdForm]     = useState({ newPass:"", confirm:"" });
  const [setPwdBusy, setSetPwdBusy]     = useState(false);
  const [setPwdErr, setSetPwdErr]       = useState("");
  // S90: edit + delete state
  const BLANK_EDIT = { Email:"", DisplayName:"", RoleID:"", IsActive:true };
  const [editUserId, setEditUserId]     = useState(null);
  const [editForm, setEditForm]         = useState(BLANK_EDIT);
  const [savingEdit, setSavingEdit]     = useState(false);
  const [deleteModal, setDeleteModal]   = useState(null);  // { userId, email } or null
  const [deleteBusy, setDeleteBusy]     = useState(false);
  const currentUserId = (() => { try { return JSON.parse(localStorage.getItem("op1_user")||"null")?.UserID || null; } catch { return null; } })();
  const PAGE_SIZE = 20;

  const load = useCallback(()=>{
    setLoading(true);
    const q=new URLSearchParams({page,pageSize:PAGE_SIZE});
    if(roleFilter!=="all") q.set("roleFilter",roleFilter);
    if(search) q.set("search",search);
    api("GET",`/users?${q}`).then(r=>{setUsers(Array.isArray(r)?r:(r?.Data||r?.Users||[])); setErr("");}).catch(e=>setErr(e.message)).finally(()=>setLoading(false));
  },[api,page,roleFilter,search]);

  useEffect(()=>{
    const id = "op1-ticker-kf";
    if (!document.getElementById(id)) {
      const el = document.createElement("style"); el.id = id;
      el.textContent = "@keyframes op1-ticker { from { transform:translateX(0) } to { transform:translateX(-50%) } }";
      document.head.appendChild(el);
    }
    api("GET","/alerts/recent?top=20").then(r=>setRecentAlerts(Array.isArray(r)?r:[])).catch(()=>{});
  },[api]);

  useEffect(()=>{load();},[load]);

  const createUser = async () => {
    setErr(""); setOk("");
    try {
      await api("POST","/users",{ Email:newUser.Email, DisplayName:newUser.DisplayName, Password:newUser.Password, RoleID:parseInt(newUser.RoleID,10) });
      setOk("User created."); setNewUser({Email:"",DisplayName:"",RoleID:"2",Password:""}); load();
    } catch(e){setErr(e.message);}
  };

  const lockUser = async (id,lock) => {
    setBusy(b=>({...b,[id]:true}));
    try { await api("POST",`/users/${id}/${lock?"lock":"unlock"}`,{}); load(); } catch(e){setErr(e.message);} finally{setBusy(b=>({...b,[id]:false}));}
  };

  const openSetPwd = (userId, displayName) => {
    setSetPwdModal({ open:true, userId, displayName });
    setSetPwdForm({ newPass:"", confirm:"" });
    setSetPwdErr("");
  };

  // S90: inline edit-in-place
  const startEdit = (u) => {
    setEditUserId(u.UserID);
    setEditForm({ Email:u.Email||"", DisplayName:u.DisplayName||"", RoleID:String(u.RoleID), IsActive:!!u.IsActive });
    setErr(""); setOk("");
  };
  const cancelEdit = () => { setEditUserId(null); setEditForm(BLANK_EDIT); };
  const saveEdit = async () => {
    if (!editForm.Email.trim()) { setErr("Email is required."); return; }
    setSavingEdit(true); setErr("");
    try {
      await api("PUT", `/users/${editUserId}`, {
        Email: editForm.Email,
        DisplayName: editForm.DisplayName,
        RoleID: parseInt(editForm.RoleID, 10),
        IsActive: editForm.IsActive
      });
      setOk("User updated."); setTimeout(()=>setOk(""), 3000);
      cancelEdit();
      load();
    } catch (e) { setErr(e.message); }
    finally { setSavingEdit(false); }
  };
  const confirmDelete = async () => {
    if (!deleteModal) return;
    setDeleteBusy(true); setErr("");
    try {
      await api("DELETE", `/users/${deleteModal.userId}`);
      setOk("User deleted."); setTimeout(()=>setOk(""), 3000);
      setDeleteModal(null);
      load();
    } catch (e) { setErr(e.message); }
    finally { setDeleteBusy(false); }
  };

  const filtered = users.filter(u=>{
    if(statusFilter==="active"   && !u.IsActive)  return false;
    if(statusFilter==="inactive" && u.IsActive)   return false;
    if(statusFilter==="locked"   && !u.IsLocked)  return false;
    return true;
  });

  const { isPaused: monIsPaused, canManageConfig, pausePopover, setPausePopover, pauseReason, setPauseReason, pauseBusy, togglePause, currentCtrl } = monitoringControl || {};

  const uAdmins = users.filter(u=>u.RoleName==="Admin").length;
  const uActive = users.filter(u=>u.IsActive && !u.IsLocked).length;

  return (
    <div style={{ display:"flex", flexDirection:"column", height:"calc(100vh - 20px)", overflow:"hidden", background:"#ffffff" }}>

      {/* ── Branding header ── */}
      <div style={{ display:"flex", alignItems:"center", justifyContent:"space-between", background:"#ffffff", borderBottom:"0.5px solid #f1f5f9", padding:"10px 16px", flexShrink:0 }}>
        <div style={{ display:"flex", alignItems:"center", gap:12 }}>
          <div style={{ width:36, height:36, borderRadius:8, background:"#006D8C", display:"flex", alignItems:"center", justifyContent:"center", fontSize:13, fontWeight:700, color:"#ffffff", letterSpacing:"0.02em", flexShrink:0 }}>OP1</div>
          <div>
            <div style={{ fontSize:16, fontWeight:600, color:"#1e293b" }}><span style={{ color:"#006D8C" }}>OP1</span> Operations Portal</div>
            <div style={{ fontSize:10, color:"#94a3b8" }}>Enterprise Monitoring · v1.4.457</div>
          </div>
        </div>
        <div style={{ display:"flex", alignItems:"center", gap:0 }}>
          {[
            ["Total Users", String(users.length), "#1e293b"],
            ["Admins",      String(uAdmins),       uAdmins > 0 ? "#0369a1" : "#1e293b"],
            ["Active",      String(uActive),        "#16a34a"],
          ].flatMap(([lbl, val, col], i, arr) => [
            <div key={lbl} style={{ padding:"0 16px", textAlign:"center" }}>
              <div style={{ fontSize:13, fontWeight:600, color:col }}>{val}</div>
              <div style={{ fontSize:9, color:"#94a3b8", textTransform:"uppercase", letterSpacing:"0.06em" }}>{lbl}</div>
            </div>,
            i < arr.length-1 && <div key={`div-${i}`} style={{ width:"0.5px", height:28, background:"#e2e5ea", flexShrink:0 }} />,
          ].filter(Boolean))}
        </div>
      </div>

      {/* ── Filter bar ── */}
      <div style={{ display:"flex", alignItems:"center", background:"#f8fafc", borderBottom:"0.5px solid #e2e5ea", height:36, padding:"0 12px", flexShrink:0, gap:10 }}>
        <div style={{ position:"relative", display:"inline-flex", alignItems:"center" }}>
          <span style={{ position:"absolute", left:7, fontSize:10, color:"#9CA3AF", pointerEvents:"none" }}>&#x1F50D;</span>
          <input placeholder="Search email or name…" value={search} onChange={e=>{setSearch(e.target.value);setPage(1);}}
            style={{ height:24, width:180, paddingLeft:24, paddingRight:6, fontSize:11, border:"0.5px solid #e2e5ea", borderRadius:4, outline:"none", color:"#374151", background:"#fff", fontFamily:"inherit" }} />
        </div>
        <select value={roleFilter} onChange={e=>{setRoleFilter(e.target.value);setPage(1);}}
          style={{ height:24, fontSize:11, border:"0.5px solid #e2e5ea", borderRadius:4, background:"#fff", color:"#374151", padding:"0 6px", outline:"none", cursor:"pointer" }}>
          <option value="all">All Roles</option>
          <option value="Admin">Admin</option>
          <option value="User">User</option>
          <option value="Viewer">Viewer</option>
        </select>
        <select value={statusFilter} onChange={e=>setStatusFilter(e.target.value)}
          style={{ height:24, fontSize:11, border:"0.5px solid #e2e5ea", borderRadius:4, background:"#fff", color:"#374151", padding:"0 6px", outline:"none", cursor:"pointer" }}>
          <option value="all">All Statuses</option>
          <option value="active">Active</option>
          <option value="inactive">Inactive</option>
          <option value="locked">Locked</option>
        </select>
        <div style={{ marginLeft:"auto", flexShrink:0 }}>
          <button onClick={load} style={{ fontSize:11, padding:"3px 10px", border:"0.5px solid #e2e5ea", borderRadius:5, background:"#fff", color:"#475569", cursor:"pointer" }}>Refresh</button>
        </div>
      </div>

      {/* ── Scrollable content ── */}
      <div style={{ flex:1, overflowY:"auto", padding:"16px" }}>
        <ErrBox msg={err} /><OkBox msg={ok} />
        {loading ? <Spinner /> : (
          <div style={{ background:"#ffffff", border:"0.5px solid #e2e5ea", borderRadius:8, overflow:"hidden", marginBottom:16 }}>
            <div style={{ overflowX:"auto" }}>
              <table style={{ width:"100%", borderCollapse:"collapse", fontSize:12 }}>
                <thead><tr style={{ borderBottom:"1px solid #C0C0B0", background:"#D8CCBA" }}>
                  {["Email","Display Name","Role","Status","Last Login","Actions"].map(h=>(
                    <th key={h} style={{ padding:"10px 14px", textAlign:"left", fontSize:11, fontWeight:600, color:"#1A1A1A", textTransform:"uppercase", letterSpacing:"0.05em", position:"sticky", top:0, zIndex:1, background:"#D8CCBA" }}>{h}</th>
                  ))}
                </tr></thead>
                <tbody>
                  {filtered.map(u=>{
                    const isEditing = editUserId === u.UserID;
                    const isSystemAccount = (u.Email||"").toLowerCase() === "admin@localhost";
                    const isSelf = currentUserId != null && u.UserID === currentUserId;
                    const guardTitle = isSystemAccount ? "System account" : isSelf ? "Cannot edit/delete your own account" : "";
                    const guarded = isSystemAccount || isSelf;
                    return (
                    <tr key={u.UserID} style={{ borderBottom:"0.5px solid #f1f5f9", background: isEditing ? "#FFFBEB" : "transparent" }}>
                      <td style={{ padding:"10px 14px", fontFamily:"monospace", fontSize:11 }}>
                        {isEditing
                          ? <input type="email" value={editForm.Email} onChange={e=>setEditForm(p=>({...p,Email:e.target.value}))}
                              style={{ width:"100%", fontSize:11, fontFamily:"monospace", padding:"4px 6px", border:"0.5px solid #C0D8E8", borderRadius:4 }} />
                          : u.Email}
                      </td>
                      <td style={{ padding:"10px 14px", fontSize:12 }}>
                        {isEditing
                          ? <input type="text" value={editForm.DisplayName} onChange={e=>setEditForm(p=>({...p,DisplayName:e.target.value}))}
                              style={{ width:"100%", fontSize:12, padding:"4px 6px", border:"0.5px solid #C0D8E8", borderRadius:4 }} />
                          : u.DisplayName}
                      </td>
                      <td style={{ padding:"10px 14px" }}>
                        {isEditing ? (
                          <select value={editForm.RoleID} onChange={e=>setEditForm(p=>({...p,RoleID:e.target.value}))}
                            style={{ fontSize:11, padding:"3px 6px", border:"0.5px solid #C0D8E8", borderRadius:4, background:"#fff" }}>
                            <option value="1">Admin</option>
                            <option value="2">User</option>
                            <option value="3">Viewer</option>
                          </select>
                        ) : (
                          <span style={{ fontSize:10, borderRadius:4, padding:"1px 6px",
                            background: u.RoleName==="Admin"?"#e0f2fe":u.RoleName==="Viewer"?"#f3e8ff":"#f1f5f9",
                            color:      u.RoleName==="Admin"?"#0369a1":u.RoleName==="Viewer"?"#6b21a8":"#475569" }}>
                            {u.RoleName}
                          </span>
                        )}
                      </td>
                      <td style={{ padding:"10px 14px" }}>
                        {isEditing ? (
                          <label style={{ display:"flex", alignItems:"center", gap:6, fontSize:11, color:"#475569", cursor:"pointer" }}>
                            <input type="checkbox" checked={editForm.IsActive} onChange={e=>setEditForm(p=>({...p,IsActive:e.target.checked}))} />
                            Active
                          </label>
                        ) : u.IsLocked
                          ? <span style={{ fontSize:10, borderRadius:4, padding:"1px 6px", background:"#fca5a5", color:"#991b1b" }}>Locked</span>
                          : u.IsActive
                            ? <span style={{ fontSize:10, borderRadius:4, padding:"1px 6px", background:"#dcfce7", color:"#166534" }}>Active</span>
                            : <span style={{ fontSize:10, borderRadius:4, padding:"1px 6px", background:"#f1f5f9", color:"#475569" }}>Inactive</span>}
                      </td>
                      <td style={{ padding:"10px 14px", fontSize:11, color:"#94a3b8" }}>{u.LastLoginAt?parseUTC(u.LastLoginAt)?.toLocaleString():"Never"}</td>
                      <td style={{ padding:"10px 14px" }}>
                        <div style={{ display:"flex", gap:4, flexWrap:"wrap" }}>
                          {isEditing ? (
                            <>
                              <button disabled={savingEdit} onClick={saveEdit}
                                style={{ fontSize:10, padding:"2px 8px", borderRadius:4, border:"none", background:savingEdit?"#94a3b8":"#006D8C", color:"#fff", cursor:savingEdit?"default":"pointer", fontWeight:600 }}>
                                {savingEdit?"Saving…":"Save"}
                              </button>
                              <button onClick={cancelEdit}
                                style={{ fontSize:10, padding:"2px 8px", borderRadius:4, border:"0.5px solid #e2e5ea", background:"#f8fafc", color:"#475569", cursor:"pointer" }}>Cancel</button>
                            </>
                          ) : (
                            <>
                              <button disabled={busy[u.UserID]} onClick={()=>lockUser(u.UserID,!u.IsLocked)}
                                style={{ fontSize:10, padding:"2px 8px", borderRadius:4, border:"0.5px solid #e2e5ea", background:"#f1f5f9", color: u.IsLocked?"#166534":"#854d0e", cursor:"pointer" }}>
                                {u.IsLocked?"Unlock":"Lock"}
                              </button>
                              <button onClick={()=>openSetPwd(u.UserID, u.DisplayName||u.Email)}
                                style={{ fontSize:10, padding:"2px 8px", borderRadius:4, border:"0.5px solid #006D8C", background:"#e0f2fe", color:"#006D8C", cursor:"pointer" }}>Set Password</button>
                              <button disabled={guarded || editUserId !== null} onClick={()=>startEdit(u)} title={guardTitle}
                                style={{ fontSize:10, padding:"2px 8px", borderRadius:4, border:"0.5px solid #006D8C", background:guarded?"#f1f5f9":"#e0f2fe", color:guarded?"#94a3b8":"#006D8C", cursor:guarded?"not-allowed":"pointer", opacity:guarded?0.6:1 }}>Edit</button>
                              <button disabled={guarded || editUserId !== null} onClick={()=>setDeleteModal({ userId:u.UserID, email:u.Email })} title={guardTitle}
                                style={{ fontSize:10, padding:"2px 8px", borderRadius:4, border:"0.5px solid #D95C5C", background:guarded?"#f1f5f9":"#FAEAEA", color:guarded?"#94a3b8":"#D95C5C", cursor:guarded?"not-allowed":"pointer", opacity:guarded?0.6:1 }}>Delete</button>
                            </>
                          )}
                        </div>
                      </td>
                    </tr>
                    );
                  })}
                  {filtered.length===0 && <tr><td colSpan={6} style={{ padding:32, textAlign:"center", color:"#94a3b8" }}>No users found.</td></tr>}
                </tbody>
              </table>
            </div>
            <div style={{ padding:"10px 16px", display:"flex", gap:8, alignItems:"center", borderTop:"0.5px solid #f1f5f9" }}>
              <button onClick={()=>setPage(p=>Math.max(1,p-1))} disabled={page===1}
                style={{ fontSize:10, padding:"4px 10px", border:"0.5px solid #e2e5ea", borderRadius:4, background:"#fff", color:"#475569", cursor:page===1?"default":"pointer", opacity:page===1?0.5:1 }}>← Prev</button>
              <span style={{ fontSize:11, color:"#64748b" }}>Page {page}</span>
              <button onClick={()=>setPage(p=>p+1)} disabled={filtered.length<PAGE_SIZE}
                style={{ fontSize:10, padding:"4px 10px", border:"0.5px solid #e2e5ea", borderRadius:4, background:"#fff", color:"#475569", cursor:filtered.length<PAGE_SIZE?"default":"pointer", opacity:filtered.length<PAGE_SIZE?0.5:1 }}>Next →</button>
            </div>
          </div>
        )}
        <div style={{ background:"#ffffff", border:"0.5px solid #e2e5ea", borderRadius:8, padding:16 }}>
          <div style={{ fontSize:10, fontWeight:700, color:"#94a3b8", textTransform:"uppercase", letterSpacing:"0.06em", marginBottom:12, paddingBottom:8, borderBottom:"0.5px solid #f1f5f9" }}>Add New User</div>
          <div style={{ display:"grid", gridTemplateColumns:"1fr 1fr", gap:12, marginBottom:14 }}>
            <Input label="Email" type="email" placeholder="user@company.com" value={newUser.Email} onChange={e=>setNewUser(p=>({...p,Email:e.target.value}))} />
            <Input label="Display Name" placeholder="Jane Smith" value={newUser.DisplayName} onChange={e=>setNewUser(p=>({...p,DisplayName:e.target.value}))} />
            <PasswordInput label="Temporary Password" placeholder="Min 12 chars" value={newUser.Password} onChange={e=>setNewUser(p=>({...p,Password:e.target.value}))} />
            <Sel label="Role" value={newUser.RoleID} onChange={e=>setNewUser(p=>({...p,RoleID:e.target.value}))} options={[{value:"1",label:"Admin — Full Access"},{value:"2",label:"User — Operator"},{value:"3",label:"Viewer — Read Only"}]} />
          </div>
          <div style={{ display:"flex", justifyContent:"flex-end" }}><Btn onClick={createUser}>Create User</Btn></div>
        </div>
      </div>

      {/* ── News ticker ── */}
      <div style={{ height:28, background:"#1e2b3c", borderTop:"0.5px solid rgba(255,255,255,0.07)", overflow:"hidden", display:"flex", alignItems:"center", flexShrink:0 }}>
        <div style={{ paddingLeft:10, paddingRight:10, borderRight:"0.5px solid rgba(255,255,255,0.15)", flexShrink:0, height:"100%", display:"flex", alignItems:"center" }}>
          <span style={{ fontSize:9, color:"rgba(255,255,255,0.4)", textTransform:"uppercase", letterSpacing:"0.08em" }}>&#9679; LIVE</span>
        </div>
        {recentAlerts.length > 0 ? (
          <div style={{ display:"inline-flex", animation:`op1-ticker ${TICKER_DURATION_SEC}s linear infinite`, whiteSpace:"nowrap" }}>
            {[...recentAlerts, ...recentAlerts].map((item, i) => (
              <span key={i} style={{ padding:"0 24px", fontSize:11, color:"rgba(255,255,255,0.85)", borderRight:"1px solid rgba(255,255,255,0.1)", display:"inline-flex", alignItems:"center", gap:5 }}>
                <span style={{ color:(item.Status||"").toLowerCase()==="critical"?"#ef4444":(item.Status||"").toLowerCase()==="warning"?"#f59e0b":"#22c55e", fontSize:8 }}>&#9679;</span>
                {(item.Status||"").toUpperCase()} &middot; {item.MonitorName}{item.Message?` \u00b7 ${item.Message}`:""}
                {item.CreatedAt && <span style={{ color:"rgba(255,255,255,0.4)", fontSize:10, marginLeft:4 }}>{parseUTC(item.CreatedAt)?.toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"})}</span>}
              </span>
            ))}
          </div>
        ) : (
          <span style={{ fontSize:11, color:"rgba(255,255,255,0.35)", paddingLeft:16, letterSpacing:"0.04em" }}>No recent alerts</span>
        )}
      </div>

      {/* ── S90: Delete user confirmation modal ── */}
      {deleteModal && (
        <div style={{ position:"fixed", inset:0, background:"rgba(0,0,0,0.75)", zIndex:3000, display:"flex", alignItems:"center", justifyContent:"center" }}
          onClick={e=>{ if(e.target===e.currentTarget && !deleteBusy) setDeleteModal(null); }}>
          <div style={{ background:"#fff", borderRadius:12, padding:24, width:440, maxWidth:"90vw", boxShadow:"0 20px 60px rgba(0,0,0,0.4)" }}>
            <div style={{ fontSize:14, fontWeight:700, color:"#1A1A1A", marginBottom:16 }}>Delete user?</div>
            <div style={{ fontSize:12, color:"#475569", lineHeight:1.55, marginBottom:18 }}>
              Permanently delete user <strong style={{ fontFamily:"monospace", color:"#1A1A1A" }}>{deleteModal.email}</strong>?
              This will remove their sessions, API keys, and login history. This action cannot be undone.
            </div>
            <div style={{ display:"flex", gap:10, justifyContent:"flex-end" }}>
              <button disabled={deleteBusy} onClick={()=>setDeleteModal(null)}
                style={{ fontSize:12, padding:"6px 14px", border:"0.5px solid #e2e5ea", borderRadius:6, background:"#f8fafc", color:"#475569", cursor:deleteBusy?"default":"pointer" }}>Cancel</button>
              <button disabled={deleteBusy} onClick={confirmDelete}
                style={{ fontSize:12, padding:"6px 14px", border:"none", borderRadius:6, background:deleteBusy?"#94a3b8":"#D95C5C", color:"#fff", cursor:deleteBusy?"default":"pointer", fontWeight:600 }}>
                {deleteBusy?"Deleting…":"Delete"}
              </button>
            </div>
          </div>
        </div>
      )}

      {/* ── Set Password modal ── */}
      {setPwdModal.open && (
        <div style={{ position:"fixed", inset:0, background:"rgba(0,0,0,0.75)", zIndex:3000, display:"flex", alignItems:"center", justifyContent:"center" }}
          onClick={e=>{ if(e.target===e.currentTarget) setSetPwdModal({open:false,userId:null,displayName:""}); }}>
          <div style={{ background:"#fff", borderRadius:12, padding:24, width:400, maxWidth:"90vw", boxShadow:"0 20px 60px rgba(0,0,0,0.4)" }}>
            <div style={{ fontSize:14, fontWeight:700, color:"#1A1A1A", marginBottom:16 }}>Set Password — {setPwdModal.displayName}</div>
            <div style={{ marginBottom:12 }}>
              <PasswordInput label="New Password" value={setPwdForm.newPass} onChange={e=>setSetPwdForm(p=>({...p,newPass:e.target.value}))} />
            </div>
            <div style={{ marginBottom:16 }}>
              <PasswordInput label="Confirm Password" value={setPwdForm.confirm} onChange={e=>setSetPwdForm(p=>({...p,confirm:e.target.value}))} />
            </div>
            {setPwdErr && <div style={{ fontSize:12, color:"#D95C5C", marginBottom:12 }}>{setPwdErr}</div>}
            <div style={{ display:"flex", gap:10, justifyContent:"flex-end" }}>
              <button onClick={()=>setSetPwdModal({open:false,userId:null,displayName:""})}
                style={{ fontSize:12, padding:"6px 14px", border:"0.5px solid #e2e5ea", borderRadius:6, background:"#f8fafc", color:"#475569", cursor:"pointer" }}>Cancel</button>
              <button disabled={setPwdBusy} onClick={async()=>{
                setSetPwdErr("");
                if(!setPwdForm.newPass||!setPwdForm.confirm){ setSetPwdErr("Both fields are required."); return; }
                if(setPwdForm.newPass!==setPwdForm.confirm){ setSetPwdErr("Passwords do not match."); return; }
                setSetPwdBusy(true);
                try {
                  await api("POST",`/users/${setPwdModal.userId}/reset-password`,{ NewPassword:setPwdForm.newPass, ConfirmPassword:setPwdForm.confirm });
                  setSetPwdModal({open:false,userId:null,displayName:""});
                  setSetPwdForm({newPass:"",confirm:""});
                  setOk("Password updated.");
                } catch(e){ setSetPwdErr(e.message||"Failed to set password."); }
                finally{ setSetPwdBusy(false); }
              }}
                style={{ fontSize:12, padding:"6px 14px", border:"none", borderRadius:6, background:setPwdBusy?"#94a3b8":"#006D8C", color:"#fff", cursor:setPwdBusy?"default":"pointer", fontWeight:600 }}>
                {setPwdBusy?"Setting…":"Set Password"}
              </button>
            </div>
          </div>
        </div>
      )}

    </div>
  );
}

// ═══ MY PROFILE ═══
function ProfileView({ api, user, monitoringControl }) {
  const [sessions, setSessions]         = useState([]);
  const [history, setHistory]           = useState([]);
  const [passForm, setPassForm]         = useState({ CurrentPassword:"", NewPassword:"", ConfirmPassword:"" });
  const [err, setErr]                   = useState("");
  const [ok, setOk]                     = useState("");
  const [loading, setLoading]           = useState(false);
  const [busy, setBusy]                 = useState({});
  const [recentAlerts, setRecentAlerts] = useState([]);

  useEffect(()=>{
    const id = "op1-ticker-kf";
    if (!document.getElementById(id)) {
      const el = document.createElement("style"); el.id = id;
      el.textContent = "@keyframes op1-ticker { from { transform:translateX(0) } to { transform:translateX(-50%) } }";
      document.head.appendChild(el);
    }
    api("GET","/auth/sessions").then(r=>setSessions(Array.isArray(r)?r:[])).catch(()=>{});
    api("GET","/auth/login-history").then(r=>setHistory(Array.isArray(r)?r:[])).catch(()=>{});
    api("GET","/alerts/recent?top=20").then(r=>setRecentAlerts(Array.isArray(r)?r:[])).catch(()=>{});
  },[api]);

  const changePass = async () => {
    setErr(""); setOk("");
    if(passForm.NewPassword!==passForm.ConfirmPassword){setErr("Passwords do not match");return;}
    setLoading(true);
    try { await api("POST","/auth/change-password",passForm); setOk("Password changed."); setPassForm({CurrentPassword:"",NewPassword:"",ConfirmPassword:""}); }
    catch(e){setErr(e.message);} finally{setLoading(false);}
  };

  const revokeSession = async (sid) => {
    setBusy(b=>({...b,[sid]:true}));
    try { await api("POST",`/auth/sessions/${sid}/revoke`,{}); setSessions(s=>s.filter(x=>x.SessionID!==sid)); } catch{}
    finally{setBusy(b=>({...b,[sid]:false}));}
  };

  const logoutAll = async () => {
    try { await api("POST","/auth/sessions/logout-all",{}); setSessions([]); setOk("All other sessions logged out."); } catch(e){setErr(e.message);}
  };

  const { isPaused: monIsPaused, canManageConfig, pausePopover, setPausePopover, pauseReason, setPauseReason, pauseBusy, togglePause, currentCtrl } = monitoringControl || {};
  const lastLogin = history.find(h=>h.WasSuccessful)?.AttemptedAt;

  const SecHead = ({ children }) => (
    <div style={{ fontSize:10, fontWeight:700, color:"#94a3b8", textTransform:"uppercase", letterSpacing:"0.06em", marginBottom:12, paddingBottom:8, borderBottom:"0.5px solid #f1f5f9" }}>{children}</div>
  );

  return (
    <div style={{ display:"flex", flexDirection:"column", height:"calc(100vh - 20px)", overflow:"hidden", background:"#ffffff" }}>

      {/* ── Branding header ── */}
      <div style={{ display:"flex", alignItems:"center", justifyContent:"space-between", background:"#ffffff", borderBottom:"0.5px solid #f1f5f9", padding:"10px 16px", flexShrink:0 }}>
        <div style={{ display:"flex", alignItems:"center", gap:12 }}>
          <div style={{ width:36, height:36, borderRadius:8, background:"#006D8C", display:"flex", alignItems:"center", justifyContent:"center", fontSize:13, fontWeight:700, color:"#ffffff", letterSpacing:"0.02em", flexShrink:0 }}>OP1</div>
          <div>
            <div style={{ fontSize:16, fontWeight:600, color:"#1e293b" }}><span style={{ color:"#006D8C" }}>OP1</span> Operations Portal</div>
            <div style={{ fontSize:10, color:"#94a3b8" }}>Enterprise Monitoring · v1.4.457</div>
          </div>
        </div>
        <div style={{ display:"flex", alignItems:"center", gap:0 }}>
          {[
            ["Role",         user.RoleName||"—",    "#1e293b"],
            ["Sessions",     String(sessions.length), "#1e293b"],
            ["Last Login",   lastLogin ? parseUTC(lastLogin)?.toLocaleDateString() : "—", "#1e293b"],
          ].flatMap(([lbl, val, col], i, arr) => [
            <div key={lbl} style={{ padding:"0 16px", textAlign:"center" }}>
              <div style={{ fontSize:13, fontWeight:600, color:col }}>{val}</div>
              <div style={{ fontSize:9, color:"#94a3b8", textTransform:"uppercase", letterSpacing:"0.06em" }}>{lbl}</div>
            </div>,
            i < arr.length-1 && <div key={`div-${i}`} style={{ width:"0.5px", height:28, background:"#e2e5ea", flexShrink:0 }} />,
          ].filter(Boolean))}
        </div>
      </div>

      {/* ── Scrollable content ── */}
      <div style={{ flex:1, overflowY:"auto", padding:"16px", display:"flex", flexDirection:"column", gap:16 }}>
        <ErrBox msg={err} /><OkBox msg={ok} />

        {/* Account Info */}
        <div style={{ background:"#ffffff", border:"0.5px solid #e2e5ea", borderRadius:8, padding:16 }}>
          <SecHead>Account Info</SecHead>
          <div style={{ display:"grid", gridTemplateColumns:"1fr 1fr", gap:16, fontSize:13 }}>
            <div>
              <div style={{ fontSize:9, color:"#94a3b8", textTransform:"uppercase", letterSpacing:"0.06em", marginBottom:4 }}>Display Name</div>
              <div style={{ fontWeight:600, color:"#1e293b" }}>{user.DisplayName}</div>
            </div>
            <div>
              <div style={{ fontSize:9, color:"#94a3b8", textTransform:"uppercase", letterSpacing:"0.06em", marginBottom:4 }}>Role</div>
              <span style={{ fontSize:11, borderRadius:4, padding:"2px 8px",
                background: user.RoleName==="Admin"?"#e0f2fe":user.RoleName==="Viewer"?"#f3e8ff":"#f1f5f9",
                color:      user.RoleName==="Admin"?"#0369a1":user.RoleName==="Viewer"?"#6b21a8":"#475569" }}>
                {user.RoleName}
              </span>
            </div>
            <div>
              <div style={{ fontSize:9, color:"#94a3b8", textTransform:"uppercase", letterSpacing:"0.06em", marginBottom:4 }}>Username</div>
              <div style={{ fontFamily:"monospace", color:"#1e293b" }}>{user.Username}</div>
            </div>
          </div>
        </div>

        {/* Change Password */}
        <div style={{ background:"#ffffff", border:"0.5px solid #e2e5ea", borderRadius:8, padding:16 }}>
          <SecHead>Change Password</SecHead>
          <div style={{ display:"flex", flexDirection:"column", gap:12 }}>
            <PasswordInput label="Current Password" value={passForm.CurrentPassword} onChange={e=>setPassForm(p=>({...p,CurrentPassword:e.target.value}))} />
            <PasswordInput label="New Password" placeholder="Min 12 chars, upper+lower+digit+special" value={passForm.NewPassword} onChange={e=>setPassForm(p=>({...p,NewPassword:e.target.value}))} />
            <PasswordInput label="Confirm New Password" value={passForm.ConfirmPassword} onChange={e=>setPassForm(p=>({...p,ConfirmPassword:e.target.value}))} />
            <div style={{ display:"flex", justifyContent:"flex-end" }}><Btn onClick={changePass} loading={loading}>Change Password</Btn></div>
          </div>
        </div>

        {/* Active Sessions */}
        <div style={{ background:"#ffffff", border:"0.5px solid #e2e5ea", borderRadius:8, padding:16 }}>
          <div style={{ display:"flex", justifyContent:"space-between", alignItems:"center", marginBottom:12, paddingBottom:8, borderBottom:"0.5px solid #f1f5f9" }}>
            <div style={{ fontSize:10, fontWeight:700, color:"#94a3b8", textTransform:"uppercase", letterSpacing:"0.06em" }}>Active Sessions</div>
            <Btn variant="danger" onClick={logoutAll} style={{ fontSize:10, padding:"4px 10px" }}>Log out all other sessions</Btn>
          </div>
          {sessions.length===0 ? <div style={{ color:"#94a3b8", fontSize:12 }}>No active sessions.</div> : (
            <table style={{ width:"100%", borderCollapse:"collapse", fontSize:12 }}>
              <thead><tr style={{ borderBottom:"1px solid #C0C0B0", background:"#D8CCBA" }}>
                {["IP Address","Created","Last Active",""].map(h=><th key={h} style={{ padding:"7px 10px", textAlign:"left", fontSize:11, fontWeight:600, color:"#1A1A1A", textTransform:"uppercase", letterSpacing:"0.05em", position:"sticky", top:0, zIndex:1, background:"#D8CCBA" }}>{h}</th>)}
              </tr></thead>
              <tbody>
                {sessions.map(s=>(
                  <tr key={s.SessionID} style={{ borderBottom:"0.5px solid #f1f5f9" }}>
                    <td style={{ padding:"7px 10px", fontFamily:"monospace", fontSize:11 }}>{s.IPAddress||"—"}</td>
                    <td style={{ padding:"7px 10px", fontSize:11, color:"#64748b" }}>{s.CreatedAt?parseUTC(s.CreatedAt)?.toLocaleString():"—"}</td>
                    <td style={{ padding:"7px 10px", fontSize:11, color:"#64748b" }}>{s.LastActivityAt?parseUTC(s.LastActivityAt)?.toLocaleString():"—"}</td>
                    <td style={{ padding:"7px 10px" }}><Btn variant="danger" loading={busy[s.SessionID]} onClick={()=>revokeSession(s.SessionID)} style={{ fontSize:10, padding:"3px 8px" }}>Revoke</Btn></td>
                  </tr>
                ))}
              </tbody>
            </table>
          )}
        </div>

        {/* Login History */}
        <div style={{ background:"#ffffff", border:"0.5px solid #e2e5ea", borderRadius:8, padding:16 }}>
          <SecHead>Login History (Last 20)</SecHead>
          {history.length===0 ? <div style={{ color:"#94a3b8", fontSize:12 }}>No login history.</div> : (
            <table style={{ width:"100%", borderCollapse:"collapse", fontSize:12 }}>
              <thead><tr style={{ borderBottom:"0.5px solid #e2e5ea" }}>
                {["Time","IP Address","Result","User Agent"].map(h=><th key={h} style={{ padding:"7px 10px", textAlign:"left", fontSize:9, fontWeight:700, color:"#94a3b8", textTransform:"uppercase" }}>{h}</th>)}
              </tr></thead>
              <tbody>
                {history.slice(0,20).map((h,i)=>(
                  <tr key={i} style={{ borderBottom:"0.5px solid #f1f5f9" }}>
                    <td style={{ padding:"7px 10px", fontFamily:"monospace", fontSize:10, color:"#94a3b8" }}>{h.AttemptedAt?parseUTC(h.AttemptedAt)?.toLocaleString():"—"}</td>
                    <td style={{ padding:"7px 10px", fontFamily:"monospace", fontSize:11 }}>{h.IPAddress||"—"}</td>
                    <td style={{ padding:"7px 10px" }}>{h.WasSuccessful?<span style={{ fontSize:10, borderRadius:4, padding:"1px 6px", background:"#dcfce7", color:"#166534" }}>Success</span>:<span style={{ fontSize:10, borderRadius:4, padding:"1px 6px", background:"#fca5a5", color:"#991b1b" }}>Failed</span>}</td>
                    <td style={{ padding:"7px 10px", fontSize:10, color:"#94a3b8", maxWidth:180, overflow:"hidden", textOverflow:"ellipsis", whiteSpace:"nowrap" }}>{h.UserAgent||"—"}</td>
                  </tr>
                ))}
              </tbody>
            </table>
          )}
        </div>

      </div>

      {/* ── News ticker ── */}
      <div style={{ height:28, background:"#1e2b3c", borderTop:"0.5px solid rgba(255,255,255,0.07)", overflow:"hidden", display:"flex", alignItems:"center", flexShrink:0 }}>
        <div style={{ paddingLeft:10, paddingRight:10, borderRight:"0.5px solid rgba(255,255,255,0.15)", flexShrink:0, height:"100%", display:"flex", alignItems:"center" }}>
          <span style={{ fontSize:9, color:"rgba(255,255,255,0.4)", textTransform:"uppercase", letterSpacing:"0.08em" }}>&#9679; LIVE</span>
        </div>
        {recentAlerts.length > 0 ? (
          <div style={{ display:"inline-flex", animation:`op1-ticker ${TICKER_DURATION_SEC}s linear infinite`, whiteSpace:"nowrap" }}>
            {[...recentAlerts, ...recentAlerts].map((item, i) => (
              <span key={i} style={{ padding:"0 24px", fontSize:11, color:"rgba(255,255,255,0.85)", borderRight:"1px solid rgba(255,255,255,0.1)", display:"inline-flex", alignItems:"center", gap:5 }}>
                <span style={{ color:(item.Status||"").toLowerCase()==="critical"?"#ef4444":(item.Status||"").toLowerCase()==="warning"?"#f59e0b":"#22c55e", fontSize:8 }}>&#9679;</span>
                {(item.Status||"").toUpperCase()} &middot; {item.MonitorName}{item.Message?` \u00b7 ${item.Message}`:""}
                {item.CreatedAt && <span style={{ color:"rgba(255,255,255,0.4)", fontSize:10, marginLeft:4 }}>{parseUTC(item.CreatedAt)?.toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"})}</span>}
              </span>
            ))}
          </div>
        ) : (
          <span style={{ fontSize:11, color:"rgba(255,255,255,0.35)", paddingLeft:16, letterSpacing:"0.04em" }}>No recent alerts</span>
        )}
      </div>

    </div>
  );
}

// ═══ AGENTS VIEW ═══
function AgentsView({ api }) {
  const [agents, setAgents]               = useState([]);
  const [loading, setLoading]             = useState(true);
  const [err, setErr]                     = useState("");
  const [confirmModal, setConfirmModal]   = useState(null);
  const showConfirm = (msg, onOk) => setConfirmModal({ msg, onOk });
  const [showModal, setShowModal]         = useState(false);
  const [regForm, setRegForm]             = useState({ name: "", description: "" });
  const [regResult, setRegResult]         = useState(null);
  const [registering, setRegistering]     = useState(false);
  const [regErr, setRegErr]               = useState("");
  const [copied, setCopied]               = useState(false);
  const [shownKeyId, setShownKeyId]       = useState(null);
  const [shownKey, setShownKey]           = useState("");
  const [keyCopied, setKeyCopied]         = useState(false);

  const loadAgents = useCallback(() => {
    api("GET", "/agents")
      .then(r => { setAgents(Array.isArray(r) ? r : []); setErr(""); })
      .catch(e => setErr(e.message || "Failed to load agents"))
      .finally(() => setLoading(false));
  }, [api]);

  useEffect(() => { loadAgents(); }, [loadAgents]);

  const relTime = (ts) => {
    if (!ts) return "Never";
    const d = parseUTC(ts);
    if (!d) return "Never";
    const secs = Math.floor((Date.now() - d.getTime()) / 1000);
    if (secs < 60) return `${secs}s ago`;
    if (secs < 3600) return `${Math.floor(secs / 60)}m ago`;
    if (secs < 86400) return `${Math.floor(secs / 3600)}h ago`;
    return `${Math.floor(secs / 86400)}d ago`;
  };

  const agentHealthState = (ts) => {
    if (!ts) return "never";
    const d = parseUTC(ts);
    if (!d) return "never";
    const secs = (Date.now() - d.getTime()) / 1000;
    if (secs < 90) return "healthy";
    if (secs < 180) return "degraded";
    return "offline";
  };

  const isOnline = (ts) => agentHealthState(ts) === "healthy";

  const handleRegister = async () => {
    if (!regForm.name.trim()) { setRegErr("Host name is required."); return; }
    setRegistering(true); setRegErr("");
    try {
      const r = await api("POST", "/agents", { Name: regForm.name.trim(), Description: regForm.description.trim() });
      setRegResult({ agentHostId: r.AgentHostId, name: r.Name, apiKey: r.ApiKey });
    } catch (e) {
      setRegErr(e.message || "Registration failed.");
    } finally {
      setRegistering(false);
    }
  };

  const handleDownload = (apiKey) => {
    const a = document.createElement("a");
    a.href = `/api/agent/download?key=${encodeURIComponent(apiKey)}`;
    a.click();
  };

  const handleRedownload = (agent) => {
    window.open('/api/agents/download', '_blank');
  };

  const handleRevoke = (agentHostId) => {
    showConfirm("Revoke this agent host? The agent will stop being able to check in. This cannot be undone.", async () => {
      try {
        await api("DELETE", `/agents/${agentHostId}`);
        if (shownKeyId === agentHostId) { setShownKeyId(null); setShownKey(""); }
        loadAgents();
      } catch (e) {
        setConfirmModal({ msg: "Revoke failed: " + (e.message || "Unknown error"), onOk: ()=>{}, okLabel: 'OK' });
      }
    });
  };

  const handleShowKey = async (agentHostId) => {
    if (shownKeyId === agentHostId) { setShownKeyId(null); setShownKey(""); return; }
    try {
      const r = await api("GET", `/agents/${agentHostId}/key`);
      setShownKey(r.apiKey || r.ApiKey || "");
      setShownKeyId(agentHostId);
      setKeyCopied(false);
    } catch (e) {
      setErr("Failed to retrieve key: " + (e.message || "Unknown error"));
    }
  };

  const handleKeyCopy = () => {
    if (shownKey) {
      navigator.clipboard.writeText(shownKey).catch(() => {});
      setKeyCopied(true);
      setTimeout(() => setKeyCopied(false), 2000);
    }
  };

  const handleCopy = () => {
    if (regResult?.apiKey) {
      navigator.clipboard.writeText(regResult.apiKey).catch(() => {});
      setCopied(true);
      setTimeout(() => setCopied(false), 2000);
    }
  };

  const handleCloseModal = () => {
    setShowModal(false);
    setRegForm({ name: "", description: "" });
    setRegResult(null);
    setRegErr("");
    setCopied(false);
    if (regResult) loadAgents();
  };

  // ── Agent card ──
  const agentCards = agents.map(agent => {
    const healthState = agentHealthState(agent.LastSeenAt);
    const statusPill = healthState === "never"
      ? { label: "Never contacted", bg: "#9CA3AF22", color: "#6B7280", border: "#9CA3AF35" }
      : healthState === "healthy"
        ? { label: "Healthy", bg: "#DCF2EA", color: "#008C6F", border: "#008C6F35" }
        : healthState === "degraded"
          ? { label: "Degraded", bg: "#FEF3CD", color: "#E89A2E", border: "#E89A2E35" }
          : { label: "Offline", bg: "#FAEAEA", color: "#D95C5C", border: "#D95C5C35" };

    const lastSeenColor = healthState === "never" ? "#9CA3AF" : healthState === "healthy" ? "#008C6F" : healthState === "degraded" ? "#E89A2E" : "#D95C5C";
    const keyPreview = agent.ApiKey
      ? `${agent.ApiKey.slice(0, 6)}...${agent.ApiKey.slice(-4)}`
      : "—";

    return (
      <div key={agent.AgentHostId} style={{ background: "#ffffff", border: `1px solid ${c.border}`, borderRadius: 8, marginBottom: 14, opacity: healthState === "offline" ? 0.75 : 1 }}>

        {/* Row 1 — Header */}
        <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "14px 16px", borderBottom: `1px solid ${c.border}` }}>
          <div style={{ display: "flex", alignItems: "center", gap: 12 }}>
            <div style={{ width: 36, height: 36, borderRadius: 8, background: "#006D8C", display: "flex", alignItems: "center", justifyContent: "center", flexShrink: 0 }}>
              <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#ffffff" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                <rect x="2" y="3" width="20" height="14" rx="2" />
                <line x1="8" y1="21" x2="16" y2="21" />
                <line x1="12" y1="17" x2="12" y2="21" />
              </svg>
            </div>
            <div>
              <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 3 }}>
                <span style={{ fontSize: 13, fontWeight: 700, color: c.text }}>{agent.Name}</span>
                <span style={{ fontSize: 10, fontWeight: 600, padding: "1px 7px", borderRadius: 10, background: statusPill.bg, color: statusPill.color, border: `1px solid ${statusPill.border}` }}>{statusPill.label}</span>
              </div>
              <div style={{ fontSize: 11, color: c.textDimmer }}>
                {agent.Platform || "Windows"} · {agent.IpAddress || "IP unknown"} · Agent v{agent.Version || "not yet connected"}
              </div>
            </div>
          </div>
          <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
            <span style={{ fontSize: 10, fontWeight: 600, padding: "2px 9px", borderRadius: 10, background: "#E0F2F7", color: "#006D8C", border: "1px solid #006D8C35" }}>Agent</span>
            <Btn variant="secondary" style={{ fontSize: 11, padding: "5px 10px" }} onClick={() => alert("Coming soon")}>Configure</Btn>
            <Btn variant="secondary" style={{ fontSize: 11, padding: "5px 10px" }} onClick={() => handleRedownload(agent)}>↓ Re-download</Btn>
          </div>
        </div>

        {/* Row 2 — Stats bar */}
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr 1fr" }}>
          {[
            { label: "Monitors Assigned", value: agent.MonitorCount ?? 0, color: c.text },
            { label: "Healthy", value: agent.HealthyMonitorCount ?? 0, color: "#008C6F" },
            { label: "Heartbeat", value: "60s", color: c.textDimmer },
            { label: "Last Check-in", value: relTime(agent.LastSeenAt), color: lastSeenColor },
          ].map((stat, i) => (
            <div key={stat.label} style={{ padding: "10px 16px", borderRight: i < 3 ? `1px solid ${c.border}` : "none", textAlign: "center" }}>
              <div style={{ fontSize: 15, fontWeight: 700, color: stat.color, fontFamily: "monospace" }}>{stat.value}</div>
              <div style={{ fontSize: 10, color: c.textDimmer, textTransform: "uppercase", letterSpacing: "0.05em", marginTop: 2 }}>{stat.label}</div>
            </div>
          ))}
        </div>

        {/* Row 3 — Footer */}
        <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "10px 16px", background: "#F0F7FB", borderTop: `1px solid ${c.border}`, borderRadius: shownKeyId === agent.AgentHostId ? "0" : "0 0 8px 8px" }}>
          <span style={{ fontSize: 11, color: c.textDimmer }}>
            Last seen: <span style={{ color: lastSeenColor }}>{relTime(agent.LastSeenAt)}</span>
            {" · "}Key: <span style={{ fontFamily: "monospace" }}>{keyPreview}</span>
          </span>
          <div style={{ display: "flex", gap: 6 }}>
            <Btn variant="secondary" style={{ fontSize: 11, padding: "4px 10px" }} onClick={() => handleShowKey(agent.AgentHostId)}>{shownKeyId === agent.AgentHostId ? "Hide Key" : "Show Key"}</Btn>
            <Btn variant="danger" style={{ fontSize: 11, padding: "4px 10px" }} onClick={() => handleRevoke(agent.AgentHostId)}>Revoke</Btn>
          </div>
        </div>

        {/* Row 4 — Key reveal (inline, only when shown) */}
        {shownKeyId === agent.AgentHostId && (
          <div style={{ padding: "12px 16px", borderTop: `1px solid ${c.border}`, background: "#FAFAFA", borderRadius: "0 0 8px 8px" }}>
            <div style={{ fontSize: 10, color: c.textDimmer, textTransform: "uppercase", letterSpacing: "0.05em", marginBottom: 6 }}>API Key</div>
            <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
              <div style={{ flex: 1, background: "#F0F7FB", border: `1px solid ${c.border}`, borderRadius: 4, padding: "7px 10px", fontFamily: "monospace", fontSize: 11, color: c.text, wordBreak: "break-all", userSelect: "all" }}>{shownKey}</div>
              <Btn variant="secondary" style={{ fontSize: 11, padding: "6px 12px", flexShrink: 0 }} onClick={handleKeyCopy}>{keyCopied ? "Copied ✓" : "Copy"}</Btn>
            </div>
          </div>
        )}
      </div>
    );
  });

  // ── Empty state ──
  const emptyState = (
    <div style={{ display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", padding: "60px 20px", textAlign: "center" }}>
      <div style={{ border: `2px dashed ${c.border}`, borderRadius: 12, padding: "40px 48px", maxWidth: 520 }}>
        <div style={{ width: 48, height: 48, borderRadius: 10, background: "#E0F2F7", display: "flex", alignItems: "center", justifyContent: "center", margin: "0 auto 16px" }}>
          <svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="#006D8C" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
            <rect x="2" y="3" width="20" height="14" rx="2" />
            <line x1="8" y1="21" x2="16" y2="21" />
            <line x1="12" y1="17" x2="12" y2="21" />
          </svg>
        </div>
        <div style={{ fontSize: 15, fontWeight: 700, color: c.text, marginBottom: 8 }}>No agent hosts registered</div>
        <div style={{ fontSize: 12, color: c.textDimmer, lineHeight: 1.7, marginBottom: 20 }}>
          Register an agent host to begin monitoring machines behind firewalls or on remote networks. Download the agent, install it as a Windows service, and it will connect automatically.
        </div>
        <Btn onClick={() => setShowModal(true)}>+ Register Agent Host</Btn>
      </div>
    </div>
  );

  // ── Register modal ──
  const modalContent = showModal && (
    <div style={{ position: "fixed", inset: 0, background: "rgba(0,0,0,0.75)", zIndex: 500, display: "flex", alignItems: "center", justifyContent: "center" }}
      onClick={e => { if (e.target === e.currentTarget) handleCloseModal(); }}>
      <div style={{ background: "#ffffff", borderRadius: 12, padding: 24, width: 480, maxWidth: "calc(100vw - 32px)", boxShadow: "0 20px 60px rgba(0,0,0,0.35)", animation: "fadeIn 0.2s ease" }}>
        <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 20 }}>
          <span style={{ fontSize: 15, fontWeight: 700, color: c.text }}>Register Agent Host</span>
          <button onClick={handleCloseModal} style={{ background: "none", border: "none", cursor: "pointer", color: c.textDimmer, fontSize: 20, lineHeight: 1, padding: "0 0 0 8px" }}>×</button>
        </div>

        {!regResult ? (
          <div>
            {regErr && <ErrBox msg={regErr} />}
            <div style={{ display: "flex", flexDirection: "column", gap: 14, marginBottom: 20 }}>
              <Input label="HOST NAME" placeholder="e.g. Sheldon, Office-Server-01" value={regForm.name} onChange={e => setRegForm(f => ({ ...f, name: e.target.value }))} onKeyDown={e => e.key === "Enter" && handleRegister()} />
              <Input label="DESCRIPTION" placeholder="e.g. Home lab machine, Houston office server" value={regForm.description} onChange={e => setRegForm(f => ({ ...f, description: e.target.value }))} />
            </div>
            <Btn loading={registering} onClick={handleRegister} style={{ width: "100%", padding: "10px 16px", fontSize: 13 }}>Generate &amp; Register</Btn>
          </div>
        ) : (
          <div style={{ display: "flex", flexDirection: "column", alignItems: "center", textAlign: "center", gap: 12 }}>
            <div style={{ width: 32, height: 32, borderRadius: "50%", background: "#DCF2EA", display: "flex", alignItems: "center", justifyContent: "center" }}>
              <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#008C6F" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
                <polyline points="20 6 9 17 4 12" />
              </svg>
            </div>
            <div style={{ fontSize: 14, fontWeight: 700, color: c.text }}>Agent host registered</div>
            <div style={{ fontSize: 11, color: "#B45309", background: "#FEF3C7", border: "1px solid #FDE68A", borderRadius: 6, padding: "8px 12px", width: "100%" }}>
              Your API key has been generated. Copy it now — it will not be shown again.
            </div>
            <div style={{ background: "#F0F7FB", borderRadius: 6, padding: 12, width: "100%", textAlign: "left" }}>
              <div style={{ fontFamily: "monospace", fontSize: 11, color: c.text, wordBreak: "break-all", userSelect: "all" }}>{regResult.apiKey}</div>
            </div>
            <div style={{ display: "flex", gap: 8, width: "100%" }}>
              <Btn variant="secondary" onClick={handleCopy} style={{ flex: 1, fontSize: 12 }}>{copied ? "Copied ✓" : "Copy Key"}</Btn>
            </div>
            <div style={{ width: "100%", height: 1, background: c.border, margin: "4px 0" }} />
            <div style={{ fontSize: 11, color: c.textDimmer, textAlign: "left", width: "100%" }}>
              Extract both files to the same folder. Run <span style={{ fontFamily: "monospace" }}>OP1AgentSetup.exe</span> as Administrator.
            </div>
            <Btn onClick={() => handleDownload(regResult.apiKey)} style={{ width: "100%", fontSize: 12, padding: "10px 16px" }}>↓ Download Agent Setup</Btn>
            <Btn variant="secondary" onClick={handleCloseModal} style={{ width: "100%", fontSize: 12 }}>Close</Btn>
          </div>
        )}
      </div>
    </div>
  );

  return (
    <div style={{ display: "flex", flexDirection: "column", height: "calc(100vh - 20px)", overflow: "hidden", background: "#ffffff" }}>

      {/* Branding header */}
      <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", background: "#ffffff", borderBottom: "0.5px solid #f1f5f9", padding: "10px 16px", flexShrink: 0 }}>
        <div style={{ display: "flex", alignItems: "center", gap: 12 }}>
          <div style={{ width: 36, height: 36, borderRadius: 8, background: "#006D8C", display: "flex", alignItems: "center", justifyContent: "center", fontSize: 13, fontWeight: 700, color: "#ffffff", flexShrink: 0 }}>OP1</div>
          <div>
            <div style={{ fontSize: 16, fontWeight: 600, color: "#1e293b" }}><span style={{ color: "#006D8C" }}>OP1</span> Operations Portal</div>
            <div style={{ fontSize: 10, color: "#94a3b8" }}>Enterprise Monitoring · v1.4.457</div>
          </div>
        </div>
        <div style={{ display: "flex", alignItems: "center", gap: 16 }}>
          <div style={{ textAlign: "center" }}>
            <div style={{ fontSize: 13, fontWeight: 600, color: "#1e293b" }}>{agents.length}</div>
            <div style={{ fontSize: 9, color: "#94a3b8", textTransform: "uppercase", letterSpacing: "0.06em" }}>Agent Hosts</div>
          </div>
          <div style={{ width: "0.5px", height: 28, background: "#e2e5ea" }} />
          <div style={{ textAlign: "center" }}>
            <div style={{ fontSize: 13, fontWeight: 600, color: "#008C6F" }}>{agents.filter(a => isOnline(a.LastSeenAt)).length}</div>
            <div style={{ fontSize: 9, color: "#94a3b8", textTransform: "uppercase", letterSpacing: "0.06em" }}>Online</div>
          </div>
          <div style={{ width: "0.5px", height: 28, background: "#e2e5ea" }} />
          <Btn onClick={() => setShowModal(true)} style={{ fontSize: 12, padding: "7px 14px" }}>+ Register Agent Host</Btn>
        </div>
      </div>

      {/* Subtitle bar */}
      <div style={{ background: "#F0F7FB", borderBottom: `1px solid ${c.border}`, padding: "6px 20px", flexShrink: 0 }}>
        <span style={{ fontSize: 11, color: c.textDimmer, fontWeight: 500 }}>Agents · Agent-based host monitoring</span>
      </div>

      {/* Main content */}
      <div style={{ flex: 1, overflowY: "auto", padding: 20, background: "#F0F7FB" }}>
        {loading && <Spinner />}
        {!loading && err && <ErrBox msg={err} />}
        {!loading && !err && agents.length === 0 && emptyState}
        {!loading && !err && agents.length > 0 && agentCards}
      </div>

      {/* Modal */}
      {modalContent}

      {confirmModal && (
        <div style={{ position:'fixed', inset:0, background:'rgba(0,0,0,0.75)', zIndex:1300, display:'flex', alignItems:'center', justifyContent:'center' }}
          onClick={e => { if(e.target===e.currentTarget) setConfirmModal(null); }}>
          <div style={{ background:'#fff', borderRadius:8, width:380, maxWidth:'90vw', padding:'24px', boxShadow:'0 8px 32px rgba(0,0,0,0.18)' }}>
            <div style={{ fontSize:13, color:'#1A1A1A', marginBottom:20, lineHeight:1.5 }}>{confirmModal.msg}</div>
            <div style={{ display:'flex', justifyContent:'flex-end', gap:8 }}>
              <button onClick={()=>setConfirmModal(null)}
                style={{ fontSize:12, padding:'6px 16px', border:'1px solid #006D8C', borderRadius:6, background:'#fff', color:'#006D8C', cursor:'pointer' }}>Cancel</button>
              <button onClick={()=>{ confirmModal.onOk(); setConfirmModal(null); }}
                style={{ fontSize:12, padding:'6px 16px', border:'none', borderRadius:6, background:'#D95C5C', color:'#fff', cursor:'pointer', fontWeight:600 }}>{confirmModal.okLabel || 'Confirm'}</button>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

// ═══ SYNTHETIC USER FLOWS ═══

function SyntheticView({ api, canManage, initialNav, onNavConsumed }) {
  const [view, setView] = useState("list");
  const [editFlowId, setEditFlowId] = useState(null);
  const [confirmModal, setConfirmModal] = useState(null);
  const showConfirm = (msg, onOk) => setConfirmModal({ msg, onOk });
  const [resultsFlowId, setResultsFlowId] = useState(null);
  const [targetResultId, setTargetResultId] = useState(null);

  useEffect(() => {
    if (!initialNav?.editFlowId) return;
    setEditFlowId(initialNav.editFlowId);
    setView("edit");
    if (onNavConsumed) onNavConsumed();
  }, [initialNav?.editFlowId]);

  useEffect(() => {
    if (!initialNav?.resultsFlowId) return;
    setResultsFlowId(initialNav.resultsFlowId);
    setView("results");
    if (initialNav.resultsResultId) setTargetResultId(initialNav.resultsResultId);
    if (initialNav.autodiagnose) setSynthAutodiagnose(true);
    if (onNavConsumed) onNavConsumed();
  }, [initialNav?.resultsFlowId]);

  // ── List state ──
  const [flows, setFlows] = useState([]);
  const [loading, setLoading] = useState(true);
  const [err, setErr] = useState(null);
  const [ok, setOk] = useState(null);
  const [runningId, setRunningId] = useState(null);

  const loadFlows = useCallback(() => {
    setLoading(true); setErr(null);
    api("GET", "/synthetic/flows")
      .then(r => setFlows(Array.isArray(r) ? r : []))
      .catch(e => setErr(e.message))
      .finally(() => setLoading(false));
  }, [api]);

  useEffect(() => { if (view === "list") loadFlows(); }, [view, loadFlows]);

  // ── Edit state ──
  const [flowDraft, setFlowDraft] = useState(null);
  const [steps, setSteps] = useState([]);
  const [editLoading, setEditLoading] = useState(false);
  const [editErr, setEditErr] = useState(null);
  const [saving, setSaving] = useState(false);
  const [stepErrors, setStepErrors] = useState({});
  const [editGroups, setEditGroups] = useState([]);

  // S89: vault entries for the fill-step Use vault picker
  const [vaultEntries, setVaultEntries] = useState([]);

  useEffect(() => {
    if (view !== "edit") return;
    api("GET", "/monitors/groups")
      .then(r => setEditGroups(Array.isArray(r) ? r.sort((a,b)=>a.GroupName.localeCompare(b.GroupName)) : []))
      .catch(() => setEditGroups([]));
  }, [view, api]);

  // S89: load vault entries when entering the editor; picker writes
  // {{vault.NAME}} tokens that ResolveVaultTokensAsync resolves at runtime.
  useEffect(() => {
    if (view !== "edit") return;
    api("GET", "/credentials")
      .then(r => setVaultEntries(Array.isArray(r) ? r : []))
      .catch(() => setVaultEntries([]));
  }, [view, api]);

  // ── Results state ──
  const [results, setResults] = useState([]);
  const [selectedResult, setSelectedResult] = useState(null);
  const [resultsLoading, setResultsLoading] = useState(false);
  const [resultsErr, setResultsErr] = useState(null);

  // ── S87: rolling 7d step averages (flow-level, NOT keyed on selectedResult)
  const [stepAverages, setStepAverages] = useState(null);

  // ── Diagnosis state ──
  const [synthDiagnosis, setSynthDiagnosis] = useState(null);
  const [synthDiagnosing, setSynthDiagnosing] = useState(false);
  const [synthDiagError, setSynthDiagError] = useState(null);
  const [synthDiagThread, setSynthDiagThread] = useState([]);
  const [synthDiagFollowUp, setSynthDiagFollowUp] = useState('');
  const [synthDiagSending, setSynthDiagSending] = useState(false);
  const [synthAutodiagnose, setSynthAutodiagnose] = useState(false);

  useEffect(() => {
    if (view !== "edit") return;
    if (editFlowId === null) {
      setFlowDraft({ FlowName:"", GroupName:"", Description:"", IsEnabled:true, ScheduleIntervalMinutes:5, ExecutionTarget:"server", AgentHostId:null, BaselineDays:30, DurationWarnMs:3000, DurationCritMs:6000, RetentionDays:14, FailureThreshold:1, AlertsEnabled:false, CooldownEnabled:false });
      setSteps([]); setEditErr(null);
      return;
    }
    setEditLoading(true); setEditErr(null);
    api("GET", `/synthetic/flows/${editFlowId}`)
      .then(r => {
        if (!r) { setEditErr("Flow not found."); return; }
        setFlowDraft({ FlowName:r.FlowName, GroupName:r.GroupName||"", Description:r.Description||"", IsEnabled:r.IsEnabled, ScheduleIntervalMinutes:r.ScheduleIntervalMinutes, ExecutionTarget:r.ExecutionTarget, AgentHostId:r.AgentHostId, BaselineDays:r.BaselineDays, DurationWarnMs:r.DurationWarnMs, DurationCritMs:r.DurationCritMs, RetentionDays:r.RetentionDays, FailureThreshold:r.FailureThreshold??1, AlertsEnabled:r.AlertsEnabled===true, CooldownEnabled:r.CooldownEnabled===true });
        setSteps((r.Steps||[]).map((s,i) => ({ ...s, _key: i })));
      })
      .catch(e => setEditErr(e.message))
      .finally(() => setEditLoading(false));
  }, [view, editFlowId, api]);

  useEffect(() => {
    if (view !== "results" || !resultsFlowId) return;
    setResultsLoading(true); setResultsErr(null);
    api("GET", `/synthetic/flows/${resultsFlowId}/results`)
      .then(r => {
        const list = Array.isArray(r) ? r : [];
        setResults(list);
        const target = targetResultId ? list.find(x => x.ResultId === targetResultId) : null;
        setSelectedResult(target || list[0] || null);
        setTargetResultId(null);
      })
      .catch(e => setResultsErr(e.message))
      .finally(() => setResultsLoading(false));
  }, [view, resultsFlowId, api]);

  // S87: fetch 7d step averages once per flow (not per run)
  useEffect(() => {
    if (!resultsFlowId) { setStepAverages(null); return; }
    let alive = true;
    api("GET", `/synthetic/flows/${resultsFlowId}/step-averages`).then(d => {
      if (alive) setStepAverages(d);
    }).catch(() => { if (alive) setStepAverages(null); });
    return () => { alive = false; };
  }, [resultsFlowId, api]);

  const fmtDur = ms => ms == null ? "—" : ms < 1000 ? `${ms}ms` : `${(ms/1000).toFixed(1)}s`;
  const fmtDate = dt => { const d = parseUTC(dt); return d ? d.toLocaleString() : "—"; };
  const uptimeColor = pct => pct == null ? "#9CA3AF" : pct >= 99 ? c.green : pct >= 95 ? c.amber : c.red;

  const runFlow = async (flowId) => {
    setRunningId(flowId);
    try {
      await api("POST", `/synthetic/flows/${flowId}/run`);
      setOk(`Flow ${flowId} started.`); setTimeout(() => setOk(null), 3000);
    } catch(e) { setErr(e.message); }
    finally { setRunningId(null); }
  };

  const toggleEnabled = async (flow) => {
    try {
      const detail = await api("GET", `/synthetic/flows/${flow.FlowId}`);
      if (!detail) return;
      await api("PUT", `/synthetic/flows/${flow.FlowId}`, { ...detail, IsEnabled: !flow.IsEnabled, Steps: detail.Steps||[] });
      setFlows(prev => prev.map(f => f.FlowId === flow.FlowId ? { ...f, IsEnabled: !f.IsEnabled } : f));
    } catch(e) { setErr(e.message); }
  };

  const deleteFlow = (flowId) => {
    showConfirm("Delete this flow?", async () => {
      try {
        await api("DELETE", `/synthetic/flows/${flowId}`);
        setFlows(prev => prev.filter(f => f.FlowId !== flowId));
        setOk("Flow deleted."); setTimeout(() => setOk(null), 3000);
      } catch(e) { setErr(e.message); }
    });
  };

  const synthDiagPanelRef = React.useRef(null);

  const runSynthDiagnosis = useCallback(async (followUp = null) => {
    if (!selectedResult?.ResultId) return;
    setSynthDiagnosing(true);
    if (synthDiagPanelRef.current) { setTimeout(() => synthDiagPanelRef.current?.scrollIntoView({behavior:'smooth', block:'nearest'}), 50); }
    try {
      const data = await api("POST", `/synthetic/diagnose/${selectedResult.ResultId}`, { followUp });
      if (data && data.diagnosis) {
        if (!followUp) {
          setSynthDiagnosis(data.diagnosis);
          setSynthDiagThread([{ role:'assistant', content:data.diagnosis }]);
        } else {
          setSynthDiagThread(prev => [...prev, { role:'user', content:followUp }, { role:'assistant', content:data.diagnosis }]);
        }
      } else {
        setSynthDiagError((data && data.error) || 'Diagnosis failed');
      }
    } catch(e) {
      setSynthDiagError(e.message);
    } finally {
      setSynthDiagnosing(false);
      setSynthDiagSending(false);
      setSynthDiagFollowUp('');
    }
  }, [api, selectedResult?.ResultId]);

  // Reset diagnosis state when selected result changes
  useEffect(() => {
    setSynthDiagnosis(null);
    setSynthDiagThread([]);
    setSynthDiagError(null);
    setSynthDiagFollowUp('');
    setSynthDiagnosing(false);
  }, [selectedResult?.ResultId]);

  // Auto-fire diagnosis when navigated from Alert Inspector
  useEffect(() => {
    if (!synthAutodiagnose || !selectedResult?.ResultId || synthDiagnosis || synthDiagnosing) return;
    setSynthAutodiagnose(false);
    runSynthDiagnosis();
  }, [synthAutodiagnose, selectedResult?.ResultId, synthDiagnosis, synthDiagnosing, runSynthDiagnosis]);

  const saveFlow = async () => {
    if (!flowDraft?.FlowName?.trim()) { setEditErr("Flow name is required."); return; }
    const errs = {};
    steps.forEach((s, i) => { if (s.StepType?.toLowerCase() === "navigate" && !s.TargetUrl?.trim()) errs[i] = "Navigate step requires a URL"; });
    setStepErrors(errs);
    if (Object.keys(errs).length > 0) return;
    setStepErrors({});
    setSaving(true); setEditErr(null);
    try {
      const body = { ...flowDraft, FailureThreshold:Math.max(1,parseInt(flowDraft.FailureThreshold)||1), AlertsEnabled:flowDraft.AlertsEnabled===true, CooldownEnabled:flowDraft.CooldownEnabled===true, Steps: steps.map((s,i) => ({ StepOrder:i+1, StepType:s.StepType, Label:s.Label||null, TargetUrl:s.TargetUrl||null, FieldLabel:s.FieldLabel||null, FieldValue:s.FieldValue||null, ButtonText:s.ButtonText||null, AssertType:s.AssertType||null, AssertValue:s.AssertValue||null, ExtractLabel:s.ExtractLabel||null, ExtractAs:s.ExtractAs||null, WaitForLoad:s.WaitForLoad!==false, TimeoutMs:s.TimeoutMs||10000, WarnMs:s.WarnMs||null, CritMs:s.CritMs||null })) };
      if (editFlowId === null) { await api("POST", "/synthetic/flows", body); } else { await api("PUT", `/synthetic/flows/${editFlowId}`, body); }
      setOk("Flow saved."); setTimeout(() => setOk(null), 3000);
      setView("list");
    } catch(e) { setEditErr(e.message); }
    finally { setSaving(false); }
  };

  const addStep = (type) => setSteps(prev => [...prev, { _key:Date.now(), StepType:type, StepOrder:prev.length+1, Label:"", TargetUrl:"", FieldLabel:"", FieldValue:"", ButtonText:"", AssertType:"visible_text_contains", AssertValue:"", ExtractLabel:"", ExtractAs:"", WaitForLoad:true, TimeoutMs:10000, WarnMs:null, CritMs:null }]);
  const removeStep = (key) => setSteps(prev => prev.filter(s => s._key !== key));
  const updateStep = (key, field, val) => setSteps(prev => prev.map(s => s._key === key ? { ...s, [field]:val } : s));
  const moveStep = (key, dir) => setSteps(prev => {
    const idx = prev.findIndex(s => s._key === key); const next = [...prev]; const swap = idx + dir;
    if (swap < 0 || swap >= next.length) return prev;
    [next[idx], next[swap]] = [next[swap], next[idx]]; return next;
  });

  // ══ LIST VIEW ══
  const FLOW_COL_GRID = "24px 1fr 80px 50px 160px 70px 80px 56px 180px";

  const listView = (() => {
    const total = flows.length;
    const passing = flows.filter(f => f.LastRunPassed === true).length;
    const failing = flows.filter(f => f.LastRunPassed === false).length;
    const durVals = flows.map(f => f.LastDurationMs).filter(v => v != null);
    const avgDur = durVals.length ? Math.round(durVals.reduce((a,b)=>a+b,0)/durVals.length) : null;
    const uptVals = flows.map(f => f.UptimePct30d).filter(v => v != null);
    const avgUpt = uptVals.length ? (uptVals.reduce((a,b)=>a+b,0)/uptVals.length).toFixed(1) : null;

    const statBar = (
      <div style={{ display:"flex", gap:0, background:"#FFFFFF", borderBottom:`1px solid ${c.border}`, padding:"10px 20px", flexShrink:0, alignItems:"center" }}>
        {[["TOTAL FLOWS", total, "#1A1A1A"], ["PASSING", passing, c.green], ["FAILING", failing, failing>0?c.red:"#9CA3AF"], ["AVG DURATION", avgDur!=null?fmtDur(avgDur):"—", "#1A1A1A"], ["AVG UPTIME 30D", avgUpt!=null?`${avgUpt}%`:"—", uptimeColor(avgUpt?parseFloat(avgUpt):null)]].map(([label,val,color]) => (
          <div key={label} style={{ display:"flex", flexDirection:"column", alignItems:"flex-start", minWidth:110, padding:"4px 20px 4px 0", borderRight:`1px solid ${c.borderLight}`, marginRight:20 }}>
            <div style={{ fontSize:9, fontWeight:700, color:"#94a3b8", textTransform:"uppercase", letterSpacing:"0.06em", marginBottom:2 }}>{label}</div>
            <div style={{ fontSize:20, fontWeight:700, color, lineHeight:1 }}>{val}</div>
          </div>
        ))}
        <div style={{ marginLeft:"auto" }}>
          {canManage && <Btn variant="primary" onClick={() => { setEditFlowId(null); setView("edit"); }}>+ New Flow</Btn>}
        </div>
      </div>
    );

    const groups = {};
    flows.forEach(f => { const g = f.GroupName||""; if (!groups[g]) groups[g]=[]; groups[g].push(f); });
    const groupNames = Object.keys(groups).sort((a,b) => a===""?1:b===""?-1:a.localeCompare(b));

    const hCell = { fontSize:10, textTransform:"uppercase", color:"#4A4A4A", fontWeight:500, letterSpacing:"0.04em", padding:"5px 8px", overflow:"hidden" };
    const subHeader = (
      <div style={{ display:"grid", gridTemplateColumns:FLOW_COL_GRID, background:"#FAF8F4", borderBottom:"1.5px solid #D8CCBA" }}>
        <div style={hCell}></div>
        <div style={hCell}>Name</div>
        <div style={hCell}>Target</div>
        <div style={hCell}>Steps</div>
        <div style={hCell}>Last Run</div>
        <div style={hCell}>Duration</div>
        <div style={hCell}>Uptime 30d</div>
        <div style={{ ...hCell, textAlign:"center" }}>On</div>
        <div style={hCell}>Actions</div>
      </div>
    );

    const flowRow = (f) => {
      const dotColor = f.LastRunPassed==null?"#9CA3AF":f.LastRunPassed?c.green:c.red;
      const durColor = f.LastDurationMs==null?"#9CA3AF":f.LastDurationMs>6000?c.red:f.LastDurationMs>3000?c.amber:c.green;
      return (
        <div key={f.FlowId} style={{ display:"grid", gridTemplateColumns:FLOW_COL_GRID, alignItems:"center", padding:"3px 0", borderBottom:`1px solid #F0F4F8`, background:"#FFFFFF", fontSize:12 }}>
          <div style={{ display:"flex", justifyContent:"center" }}>
            <span style={{ display:"inline-block", width:8, height:8, borderRadius:"50%", background:dotColor }} />
          </div>
          <div style={{ padding:"0 8px", fontWeight:500, overflow:"hidden", textOverflow:"ellipsis", whiteSpace:"nowrap" }}>
            <span style={{ cursor:"pointer", color:c.blue }} onClick={() => { setResultsFlowId(f.FlowId); setView("results"); }}>{f.FlowName}</span>
          </div>
          <div style={{ padding:"0 8px", color:"#64748b", fontSize:11 }}>{f.ExecutionTarget==="server"?"Server":"Agent"}</div>
          <div style={{ padding:"0 8px", color:"#64748b" }}>{f.StepCount}</div>
          <div style={{ padding:"0 8px", color:"#64748b", fontSize:11 }}>{f.LastRunAt?new Date(f.LastRunAt).toLocaleString():"Never"}</div>
          <div style={{ padding:"0 8px", color:durColor, fontFamily:"monospace" }}>{fmtDur(f.LastDurationMs)}</div>
          <div style={{ padding:"0 8px", color:uptimeColor(f.UptimePct30d), fontFamily:"monospace" }}>{f.UptimePct30d!=null?`${f.UptimePct30d}%`:"—"}</div>
          <div style={{ display:"flex", justifyContent:"center" }}>
            <SmallToggle checked={f.IsEnabled} onClick={() => toggleEnabled(f)} />
          </div>
          <div style={{ display:"flex", gap:3, padding:"0 4px", alignItems:"center" }}>
            <Btn variant="secondary" disabled={runningId===f.FlowId} onClick={() => runFlow(f.FlowId)} style={{ padding:"3px 6px", fontSize:11, minWidth:24, textAlign:"center" }} title="Run now">▶</Btn>
            <Btn variant="secondary" onClick={() => { setResultsFlowId(f.FlowId); setView("results"); }} style={{ padding:"3px 6px", fontSize:11 }}>Res</Btn>
            {canManage && <Btn variant="secondary" onClick={() => { setEditFlowId(f.FlowId); setView("edit"); }} style={{ padding:"3px 6px", fontSize:11 }}>Edit</Btn>}
            {canManage && <Btn variant="danger" onClick={() => deleteFlow(f.FlowId)} style={{ padding:"3px 6px", fontSize:11, marginLeft:12 }}>Del</Btn>}
          </div>
        </div>
      );
    };

    const groupPanels = groupNames.map(g => {
      const gFlows = groups[g]; const gName = g||"Ungrouped";
      const gPassing = gFlows.filter(f => f.LastRunPassed===true).length; const gTotal = gFlows.length;
      const gColor = gPassing===gTotal&&gTotal>0?c.green:gFlows.some(f=>f.LastRunPassed===false)?c.red:"#9CA3AF";
      return (
        <div key={g} style={{ marginBottom:12, border:`1px solid #D8CCBA`, borderRadius:6, overflow:"hidden" }}>
          <div style={{ display:"flex", alignItems:"center", background:"#EDE8DF", borderLeft:`3px solid ${gColor}`, padding:"6px 12px", gap:8 }}>
            <span style={{ fontWeight:600, fontSize:12, color:"#1A1A1A", flex:1 }}>{gName}</span>
            <span style={{ fontSize:11, color:"#64748b" }}>{gPassing}/{gTotal} passing</span>
          </div>
          {subHeader}
          {gFlows.map(f => flowRow(f))}
        </div>
      );
    });

    return (
      <div style={{ flex:1, display:"flex", flexDirection:"column", overflow:"hidden" }}>
        {statBar}
        <div style={{ background:"#FFFFFF", borderBottom:`1px solid ${c.border}`, padding:"6px 20px", flexShrink:0 }}>
          <span style={{ fontSize:11, color:c.textDimmer, fontWeight:500 }}>Synthetic Flows · Browser-based user flow monitoring</span>
        </div>
        <div style={{ flex:1, overflowY:"auto", padding:20, background:"#FFFFFF" }}>
          {loading && <Spinner />}
          {!loading && err && <ErrBox msg={err} />}
          {ok && <div style={{ background:"#DCF2EA", border:`1px solid ${c.green}`, borderRadius:6, padding:"8px 14px", marginBottom:12, fontSize:12, color:c.green, fontWeight:500 }}>{ok}</div>}
          {!loading && !err && flows.length===0 && (
            <div style={{ textAlign:"center", padding:"60px 20px", color:"#94a3b8" }}>
              <div style={{ fontSize:32, marginBottom:12 }}>🌐</div>
              <div style={{ fontSize:16, fontWeight:600, marginBottom:8, color:"#1e293b" }}>No synthetic flows yet</div>
              <div style={{ fontSize:12, marginBottom:20 }}>Create a flow to start monitoring user journeys</div>
              {canManage && <Btn variant="primary" onClick={() => { setEditFlowId(null); setView("edit"); }}>+ Create First Flow</Btn>}
            </div>
          )}
          {!loading && !err && flows.length>0 && groupPanels}
        </div>
      </div>
    );
  })();

  // ══ EDIT VIEW ══
  const editView = (() => {
    if (!flowDraft) return editLoading ? <div style={{ padding:32 }}><Spinner /></div> : <ErrBox msg={editErr||"Loading…"} />;
    const fd = flowDraft;
    const setFd = (patch) => setFlowDraft(p => ({ ...p, ...(typeof patch==="function"?patch(p):patch) }));
    const inpS = { width:"100%", padding:"6px 10px", border:`1px solid ${c.border}`, borderRadius:4, fontSize:12, background:"#fff", color:"#1e293b", boxSizing:"border-box" };
    const lblS = { fontSize:11, fontWeight:600, color:"#64748b", marginBottom:3, display:"block" };
    const sxS = { padding:"6px 10px", border:`1px solid ${c.border}`, borderRadius:4, fontSize:12, background:"#fff", color:"#1e293b" };
    const stepTypeLabel = { navigate:"Navigate", fill:"Fill Field", click:"Click Button", assert:"Assert", extract:"Extract" };

    const stepEditor = (s, idx) => {
      const type = s.StepType;
      const fields = (() => {
        if (type==="navigate") return (
          <div style={{ display:"grid", gridTemplateColumns:"1fr 1fr", gap:8 }}>
            <div><label style={lblS}>URL *</label><input style={inpS} value={s.TargetUrl||""} onChange={e=>{ updateStep(s._key,"TargetUrl",e.target.value); if(stepErrors[idx]) setStepErrors(prev=>{ const n={...prev}; delete n[idx]; return n; }); }} placeholder="https://example.com" /></div>
            <div><label style={lblS}>Step Description</label><input style={inpS} value={s.Label||""} onChange={e=>updateStep(s._key,"Label",e.target.value)} placeholder="Open login page" /></div>
          </div>
        );
        if (type==="fill") return (
          <div style={{ display:"grid", gridTemplateColumns:"1fr 1fr 130px 1fr", gap:8 }}>
            <div><label style={lblS}>Field Identifier *</label><input style={inpS} value={s.FieldLabel||""} onChange={e=>updateStep(s._key,"FieldLabel",e.target.value)} placeholder="Username" /><div style={{ fontSize:10, color:"#94a3b8", marginTop:3 }}>Placeholder, label text, input name, id, or type (e.g. 'password')</div></div>
            <div><label style={lblS}>Value *</label><input style={inpS} value={s.FieldValue||""} onChange={e=>updateStep(s._key,"FieldValue",e.target.value)} placeholder="admin (or pick from Use vault →)" /></div>
            <div>
              <label style={lblS}>Use vault</label>
              <select
                style={{ ...sxS, width:"100%" }}
                value=""
                onChange={e => {
                  if (e.target.value) {
                    updateStep(s._key, "FieldValue", `{{vault.${e.target.value}}}`);
                  }
                }}
              >
                <option value="">— none —</option>
                {vaultEntries.map(v => (
                  <option key={v.Name} value={v.Name}>{v.Name}</option>
                ))}
              </select>
              <div style={{ fontSize:10, color:"#94a3b8", marginTop:3 }}>
                Sets value to a vault token. Decrypted at runtime.
              </div>
            </div>
            <div><label style={lblS}>Step Description</label><input style={inpS} value={s.Label||""} onChange={e=>updateStep(s._key,"Label",e.target.value)} placeholder="Enter username" /></div>
          </div>
        );
        if (type==="click") return (
          <div style={{ display:"grid", gridTemplateColumns:"1fr 1fr", gap:8 }}>
            <div><label style={lblS}>Button Text *</label><input style={inpS} value={s.ButtonText||""} onChange={e=>updateStep(s._key,"ButtonText",e.target.value)} placeholder="Log In" /></div>
            <div><label style={lblS}>Step Description</label><input style={inpS} value={s.Label||""} onChange={e=>updateStep(s._key,"Label",e.target.value)} placeholder="Click login button" /></div>
          </div>
        );
        if (type==="assert") return (
          <div style={{ display:"grid", gridTemplateColumns:"160px 1fr 1fr", gap:8 }}>
            <div><label style={lblS}>Assert Type *</label>
              <select style={{ ...sxS, width:"100%" }} value={s.AssertType||"visible_text_contains"} onChange={e=>updateStep(s._key,"AssertType",e.target.value)}>
                <option value="visible_text_contains">Visible Text Contains</option>
                <option value="element_visible">Element Visible</option>
                <option value="element_not_visible">Element Not Visible</option>
                <option value="url_contains">URL Contains</option>
                <option value="contains_text">HTML Source Contains</option>
              </select>
              <div style={{ fontSize:10, color:"#94a3b8", marginTop:3 }}>
                Most flows want <b>Visible Text Contains</b> — matches what a user actually sees.{" "}
                <b>HTML Source Contains</b> matches raw HTML including hidden React component
                source, which can produce false PASSes on single-page apps.
              </div>
            </div>
            <div><label style={lblS}>Expected Value *</label><input style={inpS} value={s.AssertValue||""} onChange={e=>updateStep(s._key,"AssertValue",e.target.value)} placeholder="Welcome, admin" /></div>
            <div><label style={lblS}>Step Description</label><input style={inpS} value={s.Label||""} onChange={e=>updateStep(s._key,"Label",e.target.value)} placeholder="Verify login" /></div>
          </div>
        );
        if (type==="extract") return (
          <div style={{ display:"grid", gridTemplateColumns:"1fr 1fr 1fr", gap:8 }}>
            <div><label style={lblS}>Field Label *</label><input style={inpS} value={s.ExtractLabel||""} onChange={e=>updateStep(s._key,"ExtractLabel",e.target.value)} placeholder="Element label" /></div>
            <div><label style={lblS}>Store As *</label><input style={inpS} value={s.ExtractAs||""} onChange={e=>updateStep(s._key,"ExtractAs",e.target.value)} placeholder="extracted_value" /></div>
            <div><label style={lblS}>Step Description</label><input style={inpS} value={s.Label||""} onChange={e=>updateStep(s._key,"Label",e.target.value)} placeholder="Extract page title" /></div>
          </div>
        );
        return null;
      })();
      return (
        <div key={s._key} style={{ border:`1px solid ${c.border}`, borderRadius:6, background:"#fff", marginBottom:8, overflow:"hidden" }}>
          <div style={{ display:"flex", alignItems:"center", background:"#F8FAFC", padding:"6px 12px", gap:8, borderBottom:`1px solid ${c.border}` }}>
            <span style={{ width:20, height:20, background:c.blue, color:"#fff", borderRadius:"50%", display:"inline-flex", alignItems:"center", justifyContent:"center", fontSize:10, fontWeight:700, flexShrink:0 }}>{idx+1}</span>
            <span style={{ fontSize:11, fontWeight:600, color:c.blue, flex:1 }}>{stepTypeLabel[type]||type}</span>
            <div style={{ display:"flex", gap:2 }}>
              <button onClick={()=>moveStep(s._key,-1)} disabled={idx===0} style={{ background:"transparent", border:"none", cursor:idx===0?"default":"pointer", color:"#94a3b8", padding:"2px 4px", fontSize:11 }}>▲</button>
              <button onClick={()=>moveStep(s._key,1)} disabled={idx===steps.length-1} style={{ background:"transparent", border:"none", cursor:idx===steps.length-1?"default":"pointer", color:"#94a3b8", padding:"2px 4px", fontSize:11 }}>▼</button>
              <button onClick={()=>removeStep(s._key)} style={{ background:"transparent", border:"none", cursor:"pointer", color:c.red, padding:"2px 4px", fontSize:12, fontWeight:700 }}>✕</button>
            </div>
          </div>
          <div style={{ padding:12 }}>
            {fields}
            {stepErrors[idx] && <div style={{ color:"#DC2626", fontSize:12, marginTop:4 }}>{stepErrors[idx]}</div>}
            <div style={{ marginTop:8, display:"flex", gap:16, alignItems:"center" }}>
              <label style={{ display:"flex", alignItems:"center", gap:4, fontSize:11, color:"#64748b", cursor:"pointer" }}>
                <input type="checkbox" checked={s.WaitForLoad!==false} onChange={e=>updateStep(s._key,"WaitForLoad",e.target.checked)} />
                Wait for load
              </label>
              <label style={{ display:"flex", alignItems:"center", gap:4, fontSize:11, color:"#64748b" }}>
                Timeout (ms):
                <input type="number" value={s.TimeoutMs||10000} onChange={e=>updateStep(s._key,"TimeoutMs",parseInt(e.target.value)||10000)}
                  style={{ width:72, padding:"3px 6px", border:`1px solid ${c.border}`, borderRadius:4, fontSize:11 }} />
              </label>
            </div>
            {(type==="navigate"||type==="click"||type==="fill") && (
              <div style={{ marginTop:6, display:"flex", gap:16, alignItems:"center" }}>
                <label style={{ display:"flex", alignItems:"center", gap:4, fontSize:11, color:"#64748b" }}>
                  Step warn (ms):
                  <input type="number" min={0} value={s.WarnMs||""} onChange={e=>updateStep(s._key,"WarnMs",e.target.value?parseInt(e.target.value)||null:null)}
                    style={{ width:72, padding:"3px 6px", border:`1px solid ${c.border}`, borderRadius:4, fontSize:11 }} placeholder="e.g. 2000" />
                </label>
                <label style={{ display:"flex", alignItems:"center", gap:4, fontSize:11, color:"#64748b" }}>
                  Step crit (ms):
                  <input type="number" min={0} value={s.CritMs||""} onChange={e=>updateStep(s._key,"CritMs",e.target.value?parseInt(e.target.value)||null:null)}
                    style={{ width:72, padding:"3px 6px", border:`1px solid ${c.border}`, borderRadius:4, fontSize:11 }} placeholder="e.g. 5000" />
                </label>
              </div>
            )}
          </div>
        </div>
      );
    };

    const addStepBtns = ["navigate","fill","click","assert","extract"].map(type => (
      <button key={type} onClick={() => addStep(type)}
        style={{ background:c.blue, color:"#fff", border:"none", borderRadius:4, padding:"5px 12px", fontSize:11, cursor:"pointer", fontWeight:500 }}>
        + {stepTypeLabel[type]}
      </button>
    ));

    return (
      <div style={{ flex:1, display:"flex", flexDirection:"column", overflow:"hidden" }}>
        <div style={{ background:"#FFFFFF", borderBottom:`1px solid ${c.border}`, padding:"10px 20px", display:"flex", alignItems:"center", gap:12, flexShrink:0 }}>
          <button onClick={() => setView("list")} style={{ background:"transparent", border:"none", cursor:"pointer", color:c.blue, fontSize:12, padding:"4px 8px", borderRadius:4 }}>← Back</button>
          <span style={{ fontSize:14, fontWeight:700, color:"#1e293b" }}>{editFlowId===null?"New Flow":"Edit Flow"}</span>
          <div style={{ marginLeft:"auto", display:"flex", gap:8 }}>
            <Btn
              variant="secondary"
              disabled={editFlowId === null}
              onClick={() => { setResultsFlowId(editFlowId); setView("results"); }}
              title={editFlowId === null ? "Save the flow first to view results" : "View run history"}
            >
              Results
            </Btn>
            <Btn variant="secondary" onClick={() => setView("list")}>Cancel</Btn>
            <Btn variant="primary" loading={saving} onClick={saveFlow}>Save Flow</Btn>
          </div>
        </div>
        <div style={{ flex:1, overflowY:"auto", padding:20, background:"#FFFFFF" }}>
          {editErr && <ErrBox msg={editErr} />}
          <div style={{ display:"grid", gridTemplateColumns:"1fr", gap:16, maxWidth:1100 }}>
            <div>
              <div style={{ background:"#fff", border:`1px solid ${c.border}`, borderRadius:8, padding:16, marginBottom:16 }}>
                <div style={{ fontSize:11, fontWeight:700, color:"#94a3b8", textTransform:"uppercase", letterSpacing:"0.06em", marginBottom:12 }}>Flow Settings</div>
                <div style={{ display:"grid", gridTemplateColumns:"1fr 1fr", gap:12, marginBottom:12 }}>
                  <div><label style={lblS}>Flow Name *</label><input style={inpS} value={fd.FlowName} onChange={e=>setFd({FlowName:e.target.value})} placeholder="Login Flow" /></div>
                  <div><label style={lblS}>Group</label>
                    <select style={{ ...sxS, width:"100%" }} value={fd.GroupName||""} onChange={e=>setFd({GroupName:e.target.value})}>
                      <option value="">Select group...</option>
                      {editGroups.map(g => <option key={g.GroupID} value={g.GroupName}>{g.GroupName}</option>)}
                    </select>
                  </div>
                </div>
                <div style={{ marginBottom:12 }}><label style={lblS}>Description</label><input style={inpS} value={fd.Description||""} onChange={e=>setFd({Description:e.target.value})} placeholder="Monitors the login flow" /></div>
                <div style={{ display:"grid", gridTemplateColumns:"1fr 1fr 1fr 1fr", gap:12 }}>
                  <div><label style={lblS}>Interval (min)</label><input type="number" min={1} style={inpS} value={fd.ScheduleIntervalMinutes} onChange={e=>setFd({ScheduleIntervalMinutes:parseInt(e.target.value)||5})} /></div>
                  <div><label style={lblS}>Target</label>
                    <select style={{ ...sxS, width:"100%" }} value={fd.ExecutionTarget} onChange={e=>setFd({ExecutionTarget:e.target.value})}>
                      <option value="server">Server</option>
                      <option value="agent">Agent</option>
                    </select>
                  </div>
                  <div><label style={lblS}>Warn (ms)</label><input type="number" min={0} style={inpS} value={fd.DurationWarnMs} onChange={e=>setFd({DurationWarnMs:parseInt(e.target.value)||3000})} /></div>
                  <div><label style={lblS}>Crit (ms)</label><input type="number" min={0} style={inpS} value={fd.DurationCritMs} onChange={e=>setFd({DurationCritMs:parseInt(e.target.value)||6000})} /></div>
                </div>
                <div style={{ marginTop:12, display:"flex", alignItems:"center", gap:8 }}>
                  <SmallToggle checked={fd.IsEnabled} onClick={()=>setFd({IsEnabled:!fd.IsEnabled})} />
                  <span style={{ fontSize:12, color:"#64748b" }}>Enabled (schedule this flow)</span>
                </div>
              </div>
              <div style={{ background:"#fff", border:`1px solid ${c.border}`, borderRadius:8, overflow:"hidden", marginBottom:16 }}>
                <div style={{ background:"#F5F0E8", padding:"8px 16px", fontSize:11, fontWeight:700, color:"#94a3b8", textTransform:"uppercase", letterSpacing:"0.06em" }}>Alerting</div>
                <div style={{ padding:12 }}>
                  <div style={{ display:"grid", gridTemplateColumns:"1fr 1fr", gap:12, marginBottom:10 }}>
                    <div>
                      <label style={lblS}>Failure Threshold</label>
                      <input type="number" min={1} max={20} style={inpS} value={fd.FailureThreshold??1} onChange={e=>setFd({FailureThreshold:Math.max(1,parseInt(e.target.value)||1)})} />
                      <div style={{ fontSize:10, color:"#94a3b8", marginTop:2 }}>Consecutive failing runs required to trigger an alert</div>
                    </div>
                  </div>
                  <div style={{ display:"flex", alignItems:"center", gap:8 }}>
                    <SmallToggle checked={fd.AlertsEnabled===true} onClick={()=>setFd({AlertsEnabled:fd.AlertsEnabled!==true, ...(fd.AlertsEnabled===true ? {CooldownEnabled:false} : {})})} />
                    <span style={{ fontSize:12, color:"#64748b" }}>Send notifications when this flow crosses its failure threshold</span>
                  </div>
                  <div style={{ display:"flex", alignItems:"center", gap:8, marginTop:6, marginLeft:20, opacity: fd.AlertsEnabled===true ? 1 : 0.4, pointerEvents: fd.AlertsEnabled===true ? 'auto' : 'none' }}>
                    <SmallToggle checked={fd.CooldownEnabled===true} onClick={()=>setFd({CooldownEnabled:fd.CooldownEnabled!==true})} />
                    <span style={{ fontSize:12, color:"#64748b" }}>Apply cooldown window between repeated notifications</span>
                  </div>
                </div>
              </div>
              <div style={{ background:"#fff", border:`1px solid ${c.border}`, borderRadius:8, padding:16 }}>
                <div style={{ fontSize:11, fontWeight:700, color:"#94a3b8", textTransform:"uppercase", letterSpacing:"0.06em", marginBottom:12 }}>Steps ({steps.length})</div>
                {steps.length===0 && (
                  <div style={{ textAlign:"center", padding:"24px", color:"#94a3b8", fontSize:12, border:"1px dashed #e2e5ea", borderRadius:6, marginBottom:12 }}>No steps yet. Add a step below.</div>
                )}
                {steps.map((s,i) => stepEditor(s,i))}
                <div style={{ display:"flex", gap:6, flexWrap:"wrap", paddingTop:8, borderTop:`1px solid #f1f5f9` }}>{addStepBtns}</div>
              </div>
            </div>
          </div>
        </div>
      </div>
    );
  })();

  // ══ RESULTS VIEW ══
  const resultsView = (() => {
    const flowName = (resultsFlowId && flows.find(f=>f.FlowId===resultsFlowId)?.FlowName)||`Flow ${resultsFlowId}`;

    const statusBadge = (passed) => (
      <span style={{ display:"inline-flex", alignItems:"center", gap:4, padding:"2px 8px", borderRadius:12, fontSize:10, fontWeight:700, background:passed?"#DCF2EA":"#FAEAEA", color:passed?c.green:c.red }}>
        {passed?"PASS":"FAIL"}
      </span>
    );

    const flowMetaOuter = flows.find(f => f.FlowId === resultsFlowId);

    const resultDetail = selectedResult ? (() => {
      const r = selectedResult;
      const stepRes = (r.StepResults||r.stepResults||r.steps)||[];
      const flowMeta = flows.find(f=>f.FlowId===resultsFlowId);
      // Compute DurationStatus: per-step rollup + flow-level safety net
      const durationStatus = (() => {
        if (stepRes.some(s=>s.StepStatus==="critical")) return "critical";
        if (stepRes.some(s=>s.StepStatus==="warn")) return "warn";
        if (flowMeta && flowMeta.DurationCritMs>0 && r.TotalDurationMs>=flowMeta.DurationCritMs) return "critical";
        if (flowMeta && flowMeta.DurationWarnMs>0 && r.TotalDurationMs>=flowMeta.DurationWarnMs) return "warn";
        return "ok";
      })();
      const renderSynthDiagMd = (text) => {
        if (!text) return null;
        const parseBold = (s) => s.split(/(\*\*[^*]+\*\*)/g).map((p,j) =>
          p.startsWith("**")&&p.endsWith("**") ? <strong key={j}>{p.slice(2,-2)}</strong> : p
        );
        return text.split("\n").map((line,i) => {
          const t=line.trim();
          if (t.startsWith("## "))
            return <div key={i} style={{ fontSize:11, fontWeight:700, textTransform:"uppercase", color:"#5DD4F4", letterSpacing:"0.5px", borderBottom:"1px solid rgba(93,212,244,0.3)", paddingBottom:3, marginBottom:6, marginTop:i>0?10:0 }}>{t.slice(3)}</div>;
          if (t==="---") return <hr key={i} style={{ border:"none", borderTop:"1px solid #2D4A66", margin:"8px 0" }} />;
          if (t.startsWith("- ")||t.startsWith("• "))
            return <div key={i} style={{ display:"flex", gap:6, marginBottom:3 }}><span style={{ flexShrink:0, color:"#5DD4F4", marginTop:2 }}>•</span><span>{parseBold(t.slice(2))}</span></div>;
          if (/^\d+\./.test(t)) {
            const num=t.match(/^\d+/)[0];
            return <div key={i} style={{ display:"flex", gap:6, marginBottom:3 }}><span style={{ flexShrink:0, color:"#5DD4F4", minWidth:14 }}>{num}.</span><span>{parseBold(t.slice(num.length+1).trim())}</span></div>;
          }
          if (t==="") return <div key={i} style={{ height:6 }} />;
          return <div key={i} style={{ marginBottom:3, lineHeight:1.6 }}>{parseBold(line)}</div>;
        });
      };

      const WF_HEALTHY = "#8ECAC0";
      const WF_WARN    = "#DCBD7A";
      const WF_CRIT    = "#D4908F";
      const WF_THRESH_WARN = "#B8860B";
      const WF_THRESH_CRIT = "#C0392B";
      // S87: column header row
      const waterfallHeader = (
        <div style={{
          display:"grid",
          gridTemplateColumns:"20px 180px 1fr 70px 70px",
          gap:8, alignItems:"center",
          padding:"4px 0",
          borderBottom:`1px solid ${c.border}`,
          fontSize:10, color:"#94a3b8",
          textTransform:"uppercase", letterSpacing:"0.05em"
        }}>
          <div></div>
          <div>Step</div>
          <div>Duration vs thresholds</div>
          <div style={{ textAlign:"right" }}>Run</div>
          <div style={{ textAlign:"right" }}>7d avg</div>
        </div>
      );
      const waterfall = stepRes.map((s,i) => {
        // Per-step independent scale with 20% headroom past the largest value
        const stepMax = Math.max(s.DurationMs || 0, s.CritMs || 0, s.WarnMs || 0, 1) * 1.2;
        const pct = Math.round((s.DurationMs || 0) / stepMax * 100);
        // Muted bar colours by StepStatus
        const barColor = s.Skipped?"#e2e5ea"
          : s.StepStatus==="critical"?WF_CRIT
          : s.StepStatus==="warn"?WF_WARN
          : s.StepStatus==="failed"?WF_CRIT
          : s.StepStatus==="ok"?WF_HEALTHY
          : s.Passed?WF_HEALTHY:WF_CRIT;
        const dotColor = s.Skipped?"#9CA3AF":s.StepStatus==="critical"||s.StepStatus==="failed"?c.red:s.StepStatus==="warn"?c.amber:s.Passed?c.green:c.red;
        // Per-step threshold positions (naturally ≤100 due to stepMax headroom)
        const warnPct = (s.WarnMs && stepMax > 0) ? Math.round(s.WarnMs / stepMax * 100) : null;
        const critPct = (s.CritMs && stepMax > 0) ? Math.round(s.CritMs / stepMax * 100) : null;
        return (
          <div key={s.StepResultId||i} style={{ display:"grid", gridTemplateColumns:"20px 180px 1fr 70px 70px", gap:8, alignItems:"center", padding:"4px 0", borderBottom:"1px solid #f1f5f9", fontSize:11 }}>
            <div style={{ display:"flex", justifyContent:"center" }}>
              <span style={{ display:"inline-block", width:7, height:7, borderRadius:"50%", background:dotColor }} />
            </div>
            <div style={{ overflow:"hidden", textOverflow:"ellipsis", whiteSpace:"nowrap", color:"#1e293b" }}>{s.Label||s.StepType}</div>
            <div style={{ background:"#f1f5f9", borderRadius:3, height:12, position:"relative" }}>
              <div style={{ position:"absolute", left:0, top:0, height:"100%", width:`${pct}%`, background:barColor, borderRadius:3, opacity:0.85 }} />
              {warnPct!=null && (
                <div style={{ position:"absolute", left:`${warnPct}%`, top:0, width:2, height:"100%", background:WF_THRESH_WARN }}>
                  <span style={{ position:"absolute", top:-11, left:3, fontSize:9, color:WF_THRESH_WARN, whiteSpace:"nowrap", lineHeight:1, fontWeight:700, pointerEvents:"none" }}>warn</span>
                </div>
              )}
              {critPct!=null && (
                <div style={{ position:"absolute", left:`${critPct}%`, top:0, width:2, height:"100%", background:WF_THRESH_CRIT }}>
                  <span style={{ position:"absolute", top:-11, left:3, fontSize:9, color:WF_THRESH_CRIT, whiteSpace:"nowrap", lineHeight:1, fontWeight:700, pointerEvents:"none" }}>crit</span>
                </div>
              )}
            </div>
            <div style={{ textAlign:"right", fontFamily:"monospace", color:"#64748b" }}>{fmtDur(s.DurationMs)}</div>
            <div style={{ textAlign:"right", fontFamily:"monospace", color:"#94a3b8" }}>
              {fmtDur(stepAverages?.Steps?.find(x => x.StepOrder === s.StepOrder)?.AvgDurationMs)}
            </div>
          </div>
        );
      });
      // S87: waterfall summary row (mirrors step row grid)
      const summaryStepMax = Math.max(
        r.TotalDurationMs || 0,
        flowMeta?.DurationCritMs || 0,
        flowMeta?.DurationWarnMs || 0,
        1
      ) * 1.2;
      const summaryPct = Math.round((r.TotalDurationMs || 0) / summaryStepMax * 100);
      const summaryWarnPct = (flowMeta?.DurationWarnMs && summaryStepMax > 0)
        ? Math.round(flowMeta.DurationWarnMs / summaryStepMax * 100) : null;
      const summaryCritPct = (flowMeta?.DurationCritMs && summaryStepMax > 0)
        ? Math.round(flowMeta.DurationCritMs / summaryStepMax * 100) : null;
      const summaryBarColor = durationStatus === "critical" ? WF_CRIT
                            : durationStatus === "warn"     ? WF_WARN
                            :                                 WF_HEALTHY;

      const waterfallSummaryRow = (
        <div style={{
          display:"grid",
          gridTemplateColumns:"20px 180px 1fr 70px 70px",
          gap:8, alignItems:"center",
          padding:"10px 0 4px 0",
          marginTop:6,
          borderTop:"2px solid #cbd5e1",
          fontSize:11, fontWeight:600
        }}>
          <div></div>
          <div style={{ fontWeight:700, color:"#1e293b" }}>Total run</div>
          <div style={{ background:"#f1f5f9", borderRadius:3, height:14, position:"relative" }}>
            <div style={{ position:"absolute", left:0, top:0, height:"100%",
                          width:`${summaryPct}%`, background:summaryBarColor,
                          borderRadius:3, opacity:0.85 }} />
            {summaryWarnPct != null && (
              <div style={{ position:"absolute", left:`${summaryWarnPct}%`, top:0,
                            width:2, height:"100%", background:WF_THRESH_WARN }}>
                <span style={{ position:"absolute", top:-12, left:3, fontSize:9,
                                color:WF_THRESH_WARN, whiteSpace:"nowrap",
                                lineHeight:1, fontWeight:700, pointerEvents:"none" }}>
                  warn {fmtDur(flowMeta.DurationWarnMs)}
                </span>
              </div>
            )}
            {summaryCritPct != null && (
              <div style={{ position:"absolute", left:`${summaryCritPct}%`, top:0,
                            width:2, height:"100%", background:WF_THRESH_CRIT }}>
                <span style={{ position:"absolute", top:-12, left:3, fontSize:9,
                                color:WF_THRESH_CRIT, whiteSpace:"nowrap",
                                lineHeight:1, fontWeight:700, pointerEvents:"none" }}>
                  crit {fmtDur(flowMeta.DurationCritMs)}
                </span>
              </div>
            )}
          </div>
          <div style={{ textAlign:"right", fontFamily:"monospace",
                        color:"#1e293b", fontWeight:700 }}>
            {fmtDur(r.TotalDurationMs)}
          </div>
          <div style={{ textAlign:"right", fontFamily:"monospace",
                        color:"#94a3b8", fontWeight:400 }}>
            {fmtDur(stepAverages?.AvgTotalDurationMs)}
          </div>
        </div>
      );

      const scaleCaption = (
        <div style={{
          marginTop:12, padding:"8px 10px",
          background:"#F8FAFC", borderLeft:"3px solid #cbd5e1",
          borderRadius:3, fontSize:11, color:"#64748b", lineHeight:1.5
        }}>
          <strong style={{ color:"#1e293b" }}>How to read this chart:</strong>{" "}
          each row is scaled to its own values, not a shared scale across rows.
          Compare the numbers in the right column, not bar widths between rows.
        </div>
      );
      const screenshots = stepRes.filter(s=>s.ScreenshotPath).map((s,i) => {
        const fname = s.ScreenshotPath.split("/").pop().split("\\").pop();
        const url = `/api/synthetic/screenshots/${r.FlowId}/${r.ResultId}/${fname}`;
        return (
          <div key={i} style={{ marginBottom:12 }}>
            <div style={{ fontSize:10, color:"#94a3b8", marginBottom:4 }}>{s.Label||s.StepType} — step {s.StepOrder}</div>
            <img src={url} alt={`Step ${s.StepOrder}`} style={{ maxWidth:"100%", border:"1px solid #e2e5ea", borderRadius:4 }} />
          </div>
        );
      });
      const synthDiagPanel = (synthDiagnosis || synthDiagnosing || synthDiagError) ? (
        <div ref={synthDiagPanelRef} style={{ border:`1px solid ${c.border}`, borderRadius:8, overflow:"hidden", marginTop:16 }}>
          <div style={{ background:"#1E2B3C", padding:"8px 14px", display:"flex", alignItems:"center", justifyContent:"space-between" }}>
            <div style={{ display:"flex", alignItems:"center", gap:8 }}>
              <span style={{ color:"#5DD4F4", fontSize:14 }}>⚡</span>
              <span style={{ color:"#F4F8FC", fontSize:12, fontWeight:600 }}>AI Failure Diagnosis</span>
            </div>
            <div style={{ display:"flex", alignItems:"center", gap:10 }}>
              <span style={{ color:"#7A9AB8", fontSize:10 }}>claude-haiku · synthetic context</span>
              {synthDiagnosis && <button onClick={() => { setSynthDiagnosis(null); setSynthDiagThread([]); setSynthDiagError(null); }} style={{ background:"none", border:"none", color:"#7A9AB8", cursor:"pointer", fontSize:12 }}>×</button>}
            </div>
          </div>
          <div style={{ padding:14, background:"#fff" }}>
            {synthDiagnosing && !synthDiagnosis && <div style={{ color:"#4A4A4A", fontSize:12 }}>⏳ Analysing failure context...</div>}
            {synthDiagError && <div style={{ color:"#D95C5C", fontSize:12 }}>{synthDiagError}</div>}
            {synthDiagThread.map((turn, ti) => (
              <div key={ti} style={{ marginBottom:12 }}>
                <div style={{ fontSize:10, fontWeight:600, color:"#7A9AB8", textTransform:"uppercase", letterSpacing:".04em", marginBottom:4 }}>
                  {turn.role === 'user' ? '▶ YOU' : '⚡ AI DIAGNOSIS'}
                </div>
                <div style={{ fontSize:12, color:"#1A1A1A", lineHeight:1.7 }}>
                  {renderSynthDiagMd(turn.content)}
                </div>
              </div>
            ))}
            {synthDiagnosis && !synthDiagnosing && (
              <div style={{ display:"flex", gap:8, marginTop:10, borderTop:"1px solid #D8CCBA", paddingTop:10 }}>
                <textarea
                  value={synthDiagFollowUp}
                  onChange={e => setSynthDiagFollowUp(e.target.value)}
                  onKeyDown={e => { if(e.key==='Enter'&&!e.shiftKey){ e.preventDefault(); if(synthDiagFollowUp.trim()){ setSynthDiagSending(true); runSynthDiagnosis(synthDiagFollowUp.trim()); } }}}
                  placeholder="Ask a follow-up question... (Enter to send)"
                  style={{ flex:1, border:"1px solid #D8CCBA", borderRadius:6, padding:"6px 10px", fontSize:11, resize:"none", height:52, fontFamily:"inherit" }}
                />
                <button
                  onClick={() => { if(synthDiagFollowUp.trim()){ setSynthDiagSending(true); runSynthDiagnosis(synthDiagFollowUp.trim()); } }}
                  disabled={!synthDiagFollowUp.trim() || synthDiagSending}
                  style={{ background:"#006D8C", color:"#fff", border:"none", borderRadius:6, padding:"6px 14px", fontSize:11, fontWeight:600, cursor:(!synthDiagFollowUp.trim()||synthDiagSending)?"not-allowed":"pointer", alignSelf:"flex-end", opacity:(!synthDiagFollowUp.trim()||synthDiagSending)?0.5:1 }}>
                  {synthDiagSending?"…":"Send"}
                </button>
              </div>
            )}
          </div>
        </div>
      ) : null;

      return (
        <div style={{ display:"grid", gridTemplateColumns:"1fr 240px", gap:16 }}>
          <div>
            <div style={{ background:"#fff", border:`1px solid ${c.border}`, borderRadius:8, padding:16, marginBottom:16 }}>
              <div style={{ display:"flex", alignItems:"center", gap:12, marginBottom:12 }}>
                {statusBadge(r.Passed??r.passed)}
                <span style={{ fontSize:12, color:"#1e293b", fontWeight:600 }}>{fmtDate(r.StartedAt)}</span>
                <span style={{ fontSize:12, color:"#64748b", marginLeft:"auto" }}>{fmtDur(r.TotalDurationMs)}</span>
              </div>
              {r.ErrorMessage && <div style={{ background:"#FAEAEA", border:`1px solid ${c.red}`, borderRadius:4, padding:"8px 12px", fontSize:11, color:c.red, marginBottom:12 }}>{r.ErrorMessage}</div>}
              {stepRes.length===0 && !(r.Passed??r.passed) ? (
                <div style={{ padding:"20px 0", textAlign:"center" }}>
                  <div style={{ fontSize:12, fontWeight:600, color:c.red, marginBottom:4 }}>Flow failed before any steps executed</div>
                  {!r.ErrorMessage && <div style={{ fontSize:11, color:"#64748b", marginTop:4 }}>No error detail was recorded. The flow may have failed to launch the browser or reach the target host.</div>}
                </div>
              ) : (
                <>
                  <div style={{ fontSize:10, fontWeight:700, color:"#94a3b8", textTransform:"uppercase", marginBottom:8 }}>Waterfall</div>
                  {waterfallHeader}
                  {waterfall}
                  {waterfallSummaryRow}
                  {scaleCaption}
                </>
              )}
            </div>
            {screenshots.length>0 && (
              <div style={{ background:"#fff", border:`1px solid ${c.border}`, borderRadius:8, padding:16 }}>
                <div style={{ fontSize:11, fontWeight:700, color:"#94a3b8", textTransform:"uppercase", letterSpacing:"0.06em", marginBottom:12 }}>Screenshots</div>
                {screenshots}
              </div>
            )}
            {synthDiagPanel}
          </div>
          <div>
            <div style={{ background:"#fff", border:`1px solid ${c.border}`, borderRadius:8, padding:16 }}>
              <div style={{ fontSize:11, fontWeight:700, color:"#94a3b8", textTransform:"uppercase", letterSpacing:"0.06em", marginBottom:12 }}>Run Summary</div>
              {[["Status", statusBadge(r.Passed??r.passed)], ["Started", fmtDate(r.StartedAt)], ["Completed", fmtDate(r.CompletedAt)], ["Total Duration", fmtDur(r.TotalDurationMs)], ["7d avg total", fmtDur(stepAverages?.AvgTotalDurationMs)], ["Steps", `${((r.StepResults||r.stepResults||r.steps)||[]).filter(s=>!s.Skipped).length} / ${((r.StepResults||r.stepResults||r.steps)||[]).length}`], ["Failed At Step", r.FailedAtStep?`Step ${r.FailedAtStep}`:"—"]].map(([label,val]) => (
                <div key={label} style={{ display:"flex", justifyContent:"space-between", padding:"6px 0", borderBottom:"1px solid #f1f5f9", fontSize:11 }}>
                  <span style={{ color:"#64748b" }}>{label}</span>
                  <span style={{ fontWeight:600, color:"#1e293b" }}>{val}</span>
                </div>
              ))}
              {durationStatus!=="ok" && (
                <div style={{ display:"flex", justifyContent:"space-between", padding:"6px 0", borderBottom:"1px solid #f1f5f9", fontSize:11 }}>
                  <span style={{ color:"#64748b" }}>Duration Status</span>
                  <span style={{ display:"inline-flex", alignItems:"center", padding:"2px 8px", borderRadius:12, fontSize:10, fontWeight:700, background:durationStatus==="critical"?"#FAEAEA":"#FEF3C7", color:durationStatus==="critical"?c.red:c.amber }}>
                    {durationStatus==="critical"?"CRITICAL":"WARN"}
                  </span>
                </div>
              )}
            </div>
          </div>
        </div>
      );
    })() : <div style={{ textAlign:"center", padding:32, color:"#94a3b8", fontSize:12 }}>Select a result to view details.</div>;

    return (
      <div style={{ flex:1, display:"flex", flexDirection:"column", overflow:"hidden" }}>
        <div style={{ background:"#FFFFFF", borderBottom:`1px solid ${c.border}`, padding:"10px 20px", display:"flex", alignItems:"center", gap:12, flexShrink:0 }}>
          <button onClick={() => setView("list")} style={{ background:"transparent", border:"none", cursor:"pointer", color:c.blue, fontSize:12, padding:"4px 8px", borderRadius:4 }}>← Back</button>
          <span style={{ fontSize:14, fontWeight:700, color:"#1e293b" }}>Results — {flowName}</span>
          <div style={{ marginLeft:"auto", display:"flex", gap:6 }}>
            {selectedResult && <Btn variant="primary" style={{ fontSize:11, padding:"5px 12px" }} loading={synthDiagnosing} onClick={() => runSynthDiagnosis()}>⚡ Diagnose</Btn>}
            {canManage && <Btn variant="secondary" onClick={() => { setEditFlowId(resultsFlowId); setView("edit"); }}>Edit Flow</Btn>}
            {canManage && <Btn variant="ghost" onClick={() => runFlow(resultsFlowId)}>▶ Run Now</Btn>}
          </div>
        </div>
        <div style={{ flex:1, overflow:"hidden", display:"grid", gridTemplateColumns:"280px 1fr", gap:0 }}>
          <div style={{ borderRight:`1px solid ${c.border}`, overflowY:"auto", background:"#fff" }}>
            <div style={{ padding:"8px 12px", background:"#FAF8F4", borderBottom:`1px solid ${c.border}`, fontSize:10, fontWeight:700, color:"#94a3b8", textTransform:"uppercase" }}>Recent Runs</div>
            {resultsLoading && <div style={{ padding:16 }}><Spinner /></div>}
            {!resultsLoading && resultsErr && <ErrBox msg={resultsErr} />}
            {!resultsLoading && !resultsErr && results.length===0 && <div style={{ padding:"24px", textAlign:"center", color:"#94a3b8", fontSize:12 }}>No results yet.</div>}
            {!resultsLoading && results.map(r => (
              <div key={r.ResultId} onClick={() => setSelectedResult(r)}
                style={{ padding:"8px 12px", cursor:"pointer", borderBottom:"1px solid #f1f5f9", background:selectedResult?.ResultId===r.ResultId?"#E0F2F7":"#fff", display:"flex", alignItems:"center", gap:8 }}>
                <span style={{ display:"inline-block", width:8, height:8, borderRadius:"50%", background:(() => {
                  const passed = r.Passed ?? r.passed;
                  if (passed == null) return "#9CA3AF";
                  if (!passed) return c.red;
                  const dur = r.TotalDurationMs;
                  const warn = flowMetaOuter?.DurationWarnMs;
                  const crit = flowMetaOuter?.DurationCritMs;
                  if (dur != null && crit > 0 && dur >= crit) return c.red;
                  if (dur != null && warn > 0 && dur >= warn) return c.amber;
                  return c.green;
                })(), flexShrink:0 }} />
                <div style={{ flex:1, minWidth:0 }}>
                  <div style={{ fontSize:11, color:"#1e293b", fontWeight:500 }}>{parseUTC(r.StartedAt)?.toLocaleString() ?? "—"}</div>
                  <div style={{ fontSize:10, color:"#94a3b8" }}>{fmtDur(r.TotalDurationMs)}</div>
                </div>
              </div>
            ))}
          </div>
          <div style={{ overflowY:"auto", padding:20, background:"#FFFFFF" }}>
            {resultsLoading ? <Spinner /> : resultDetail}
          </div>
        </div>
      </div>
    );
  })();

  return (
    <div style={{ display:"flex", flexDirection:"column", height:"100%", background:"#FFFFFF" }}>
      {view==="list"    && listView}
      {view==="edit"    && editView}
      {view==="results" && resultsView}

      {confirmModal && (
        <div style={{ position:'fixed', inset:0, background:'rgba(0,0,0,0.75)', zIndex:1300, display:'flex', alignItems:'center', justifyContent:'center' }}
          onClick={e => { if(e.target===e.currentTarget) setConfirmModal(null); }}>
          <div style={{ background:'#fff', borderRadius:8, width:380, maxWidth:'90vw', padding:'24px', boxShadow:'0 8px 32px rgba(0,0,0,0.18)' }}>
            <div style={{ fontSize:13, color:'#1A1A1A', marginBottom:20, lineHeight:1.5 }}>{confirmModal.msg}</div>
            <div style={{ display:'flex', justifyContent:'flex-end', gap:8 }}>
              <button onClick={()=>setConfirmModal(null)}
                style={{ fontSize:12, padding:'6px 16px', border:'1px solid #006D8C', borderRadius:6, background:'#fff', color:'#006D8C', cursor:'pointer' }}>Cancel</button>
              <button onClick={()=>{ confirmModal.onOk(); setConfirmModal(null); }}
                style={{ fontSize:12, padding:'6px 16px', border:'none', borderRadius:6, background:'#D95C5C', color:'#fff', cursor:'pointer', fontWeight:600 }}>{confirmModal.okLabel || 'Confirm'}</button>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

// ── Mount ──
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(React.createElement(OpsPortalApp));