/* ---- settings ---- */
const SETTINGS_DEFAULTS = { defaultEnv: "", liveMs: 60000, logMs: 10000, notif: true, sound: false, memWarn: 70, memCrit: 90, queueMax: 100, autoSec: 30 };
let SETTINGS = { ...SETTINGS_DEFAULTS };
function loadSettings() {
	SETTINGS = Object.assign({ ...SETTINGS_DEFAULTS }, JSON.parse(wsGet(localStorage, "me-settings") || "{}"));
}
const saveSettings = () => localStorage.setItem(wsKey("me-settings"), JSON.stringify(SETTINGS));

let wsStateFor = null;
function loadWorkspaceState() {
	const id = CFG?.activeWorkspace || "none";
	if (wsStateFor === id) return;
	wsStateFor = id;
	loadSettings();
	loadIssueState();
	loadInk();
	loadPositions();
	renderNodes();
	drawEdges();
	loadChatState();
}

async function fetchConfig() {
	try {
		const r = await fetch("/api/config");
		const d = await r.json();
		if (d.ok) {
			CFG = d.config;
			loadWorkspaceState();
			if (CFG.envs?.length && !CFG.envs.some((e) => e.key === ENV)) {
				ENV = CFG.envs[0].key;
				$("#sideEnv").textContent = ENV;
			}
			renderEnvPills();
			renderWorkspaceSwitcher();
			renderHomeTiles();
			renderEnvCards();
			renderCrumb();
			applyWorkspaceMode();
		}
	} catch { }
}
function renderCrumb() {
	const region = envCfg().region || CFG?.envs?.[0]?.region || "";
	$("#crumb").innerHTML = `<b>${escHtml(wsName())}</b> · AWS${region ? " · " + escHtml(region) : ""}`;
}
function renderEnvCards() {
	const el = $("#envCards");
	if (!el || !CFG) return;
	const kv = (k, v) => `<div class="kv"><dt>${k}</dt><dd>${escHtml(v)}</dd></div>`;
	el.innerHTML = (CFG.envs || []).map((e, i) => `<div class="env-card"><h3><span class="dot"${i ? ' style="background:var(--yellow)"' : ""}></span>${escHtml(e.name || e.key)}</h3><dl>` +
		(e.account ? kv("Account", e.account) : "") +
		(e.region ? kv("Region", e.region) : "") +
		(e.api ? kv("API", e.api) : "") +
		(e.app ? kv("App", e.app) : "") +
		(e.branch ? kv("Deploys from", e.branch) : "") +
		`</dl></div>`).join("") ||
		`<div class="env-card"><p class="cfg-note">No environments configured. Add one in Settings.</p></div>`;
}
function applyWorkspaceMode() {
	const curated = wsCurated();
	if (!curated && mapSource !== "ai" && NODES.length) {
		NODES = []; EDGES = [];
		renderNodes(); drawEdges();
	}
	if (!curated && flowActive && flowActive !== "__walk") stopFlow();
	renderFlowBtns();
	syncMapEmpty();
	const tabs = $("#depTabs");
	if (tabs) tabs.style.display = curated ? "" : "none";
	if (!curated) {
		$("#depTabIns").classList.add("active");
		$("#depTabPlay").classList.remove("active");
		$("#depInsights").style.display = "block";
		$("#depPlaybooks").style.display = "none";
	}
	const eg = $("#estateGlance");
	if (eg) eg.style.display = curated ? "" : "none";
	const cd = $("#curatedLogsDoc");
	if (cd) cd.style.display = curated ? "" : "none";
	if (!curated && !runbooksFromServer && RUNBOOKS.length) { RUNBOOKS = []; renderRunbooks(); }
	renderQuickLinks();
	$("#wsOnboard").classList.toggle("hidden", !wsZero());
	if (wsZero()) $("#connectGate").classList.add("hidden");
}
function syncMapEmpty() {
	const el = $("#mapEmpty");
	if (!el) return;
	const show = !wsCurated() && mapSource !== "ai";
	el.style.display = show ? "flex" : "none";
	if (show) {
		const b = $("#mapEmptyRemap");
		if (b && !b.disabled) b.textContent = "Remap from live infra";
	}
}

/* ---- workspaces ---- */
function renderWorkspaceSwitcher() {
	const wrap = $("#wsSwitch");
	if (!wrap) return;
	if (!CFG || CFG.workspace === undefined || !Array.isArray(CFG.workspaces)) { wrap.style.display = "none"; return; }
	wrap.style.display = "";
	$("#wsBtnName").textContent = CFG.workspace?.name || "No workspace";
	buildWsMenu();
}
function buildWsMenu(mode) {
	const m = $("#wsMenu");
	const active = CFG?.workspace;
	const list = Array.isArray(CFG?.workspaces) ? CFG.workspaces : [];
	if (mode === "delete" && active) {
		m.innerHTML = `<div class="ws-confirm"><p>Delete "${escHtml(active.name)}"? Its environments, map and settings are removed permanently.</p>
			<div class="ws-confirm-btns"><button class="btn ws-danger" id="wsDelYes">Delete workspace</button><button class="btn" id="wsDelNo">Cancel</button></div>
			<p class="cfg-note" id="wsDelMsg"></p></div>`;
		$("#wsDelYes").onclick = deleteActiveWorkspace;
		$("#wsDelNo").onclick = () => buildWsMenu();
		return;
	}
	const activeId = active ? String(active.id) : "";
	m.innerHTML = list.map((w) => `<button class="ws-item${String(w.id) === activeId ? " on" : ""}" data-ws="${escHtml(w.id)}"><span>${escHtml(w.name)}</span>${String(w.id) === activeId ? `<span class="ws-check">✓</span>` : ""}</button>`).join("") +
		(list.length ? `<div class="ws-sep"></div>` : "") +
		`<button class="ws-item" id="wsNew">New workspace…</button>` +
		(active ? `<button class="ws-item" id="wsRename">Rename…</button><button class="ws-item ws-danger" id="wsDelete">Delete…</button>` : "");
	$$(".ws-item[data-ws]", m).forEach((b) => b.onclick = () => {
		if (b.dataset.ws === activeId) return closeWsMenu();
		activateWorkspace(b.dataset.ws, b);
	});
	$("#wsNew").onclick = () => { closeWsMenu(); openWsModal("create"); };
	if (active) {
		$("#wsRename").onclick = () => { closeWsMenu(); openWsModal("rename"); };
		$("#wsDelete").onclick = () => buildWsMenu("delete");
	}
}
function openWsMenu() {
	const r = $("#wsBtn").getBoundingClientRect();
	const m = $("#wsMenu");
	buildWsMenu();
	m.style.top = (r.bottom + 6) + "px";
	m.style.right = Math.max(8, window.innerWidth - r.right) + "px";
	m.classList.add("open");
}
function closeWsMenu() { $("#wsMenu").classList.remove("open"); }
$("#wsBtn").onclick = (e) => {
	e.stopPropagation();
	$("#wsMenu").classList.contains("open") ? closeWsMenu() : openWsMenu();
};
document.addEventListener("click", (e) => {
	if ($("#wsMenu").classList.contains("open") && !e.target.closest("#wsSwitch") && !e.target.closest("#wsMenu")) closeWsMenu();
});
async function activateWorkspace(id, btn) {
	if (btn) { btn.disabled = true; btn.textContent = "switching…"; }
	try {
		const r = await fetch("/api/workspaces/activate", { method: "POST", body: JSON.stringify({ id }) });
		const d = await r.json();
		if (d.ok) {
			sessionStorage.setItem("me-skip-home", "1");
			return location.reload();
		}
	} catch { }
	if (btn) buildWsMenu();
}
async function deleteActiveWorkspace() {
	const id = CFG?.workspace?.id;
	if (!id) return closeWsMenu();
	const y = $("#wsDelYes");
	y.disabled = true; y.textContent = "deleting…";
	try {
		const r = await fetch("/api/workspaces/delete", { method: "POST", body: JSON.stringify({ id }) });
		const d = await r.json();
		if (d.ok) {
			sessionStorage.setItem("me-skip-home", "1");
			return location.reload();
		}
		$("#wsDelMsg").textContent = d.detail || d.error || "could not delete this workspace";
	} catch { $("#wsDelMsg").textContent = "server unreachable"; }
	y.disabled = false; y.textContent = "Delete workspace";
}
let wsModalMode = "create";
function openWsModal(mode) {
	wsModalMode = mode;
	$("#wsModalTitle").textContent = mode === "create" ? "New workspace" : "Rename workspace";
	$("#wsModalSub").textContent = mode === "create" ? "Workspaces start empty — add environments in Settings afterwards." : "Give this workspace a new name.";
	$("#wsModalGo").textContent = mode === "create" ? "Create workspace" : "Save name";
	$("#wsModalName").value = mode === "rename" ? (CFG?.workspace?.name || "") : "";
	$("#wsModalMsg").textContent = "";
	$("#wsModalGo").disabled = false;
	$("#wsModal").classList.remove("hidden");
	setTimeout(() => $("#wsModalName").focus(), 60);
}
function closeWsModal() { $("#wsModal").classList.add("hidden"); }
async function submitWsModal() {
	const name = $("#wsModalName").value.trim();
	if (!name) { $("#wsModalMsg").textContent = "Enter a name."; return; }
	const go = $("#wsModalGo");
	go.disabled = true;
	$("#wsModalMsg").textContent = "";
	try {
		if (wsModalMode === "create") {
			const r = await fetch("/api/workspaces", { method: "POST", body: JSON.stringify({ name }) });
			const d = await r.json();
			if (!d.ok) { $("#wsModalMsg").textContent = d.detail || d.error || "could not create the workspace"; go.disabled = false; return; }
			const a = await fetch("/api/workspaces/activate", { method: "POST", body: JSON.stringify({ id: d.workspace.id }) });
			const ad = await a.json();
			if (ad.ok) {
				sessionStorage.setItem("me-skip-home", "1");
				return location.reload();
			}
			$("#wsModalMsg").textContent = "Created, but switching failed — pick it from the workspace menu.";
		} else {
			const r = await fetch("/api/workspaces/rename", { method: "POST", body: JSON.stringify({ id: CFG?.workspace?.id, name }) });
			const d = await r.json();
			if (!d.ok) { $("#wsModalMsg").textContent = d.detail || d.error || "rename failed"; go.disabled = false; return; }
			if (Array.isArray(d.workspaces)) CFG.workspaces = d.workspaces;
			if (CFG.workspace) CFG.workspace.name = name;
			renderWorkspaceSwitcher();
			renderHomeTiles();
			renderCrumb();
			closeWsModal();
		}
	} catch { $("#wsModalMsg").textContent = "server unreachable"; }
	go.disabled = false;
}
$("#wsModalGo").onclick = submitWsModal;
$("#wsModalCancel").onclick = closeWsModal;
$("#wsModalName").addEventListener("keydown", (e) => { if (e.key === "Enter") submitWsModal(); });
$("#wsModal").addEventListener("click", (e) => { if (e.target === $("#wsModal")) closeWsModal(); });
document.addEventListener("keydown", (e) => {
	if (e.key !== "Escape") return;
	if (!$("#wsModal").classList.contains("hidden")) closeWsModal();
	if ($("#wsMenu").classList.contains("open")) closeWsMenu();
});
async function submitWsOnboard() {
	const name = $("#wsObName").value.trim();
	if (!name) { $("#wsObErr").textContent = "Enter a name for the workspace."; return; }
	const go = $("#wsObGo");
	go.disabled = true;
	$("#wsObErr").textContent = "";
	try {
		const r = await fetch("/api/workspaces", { method: "POST", body: JSON.stringify({ name }) });
		const d = await r.json();
		if (d.ok) {
			sessionStorage.setItem("me-skip-home", "1");
			return location.reload();
		}
		$("#wsObErr").textContent = d.detail || d.error || "could not create the workspace";
	} catch { $("#wsObErr").textContent = "server unreachable"; }
	go.disabled = false;
}
$("#wsObGo").onclick = submitWsOnboard;
$("#wsObName").addEventListener("keydown", (e) => { if (e.key === "Enter") submitWsOnboard(); });
function renderHomeTiles() {
	const grid = $("#homeScreen .ws-grid");
	if (!grid || !CFG || CFG.workspace === undefined || !Array.isArray(CFG.workspaces)) return;
	const activeId = CFG.workspace ? String(CFG.workspace.id) : "";
	grid.innerHTML = CFG.workspaces.map((w) =>
		`<button class="ws-tile" data-ws="${escHtml(w.id)}"><div class="logo" style="background:var(--blue)">${escHtml((w.name || "?").trim().charAt(0).toUpperCase())}</div><h3>${escHtml(w.name)}</h3><p>${String(w.id) === activeId ? "active workspace" : "switch to this workspace"}</p></button>`).join("") +
		`<button class="ws-tile ghost-add" id="wsHomeAdd"><div class="logo" style="background:var(--surface-3);color:var(--ink-3)">+</div><h3>New workspace</h3><p>create an empty workspace</p></button>`;
	$$(".ws-tile[data-ws]", grid).forEach((t) => t.onclick = () => {
		if (t.dataset.ws === activeId) { $("#homeScreen").classList.add("hidden"); return; }
		t.querySelector("p").textContent = "switching…";
		activateWorkspace(t.dataset.ws, null);
	});
	$("#wsHomeAdd").onclick = () => { $("#homeScreen").classList.add("hidden"); openWsModal("create"); };
}
function renderEnvPills() {
	const t = $("#envToggle");
	t.innerHTML = "";
	t.style.display = (CFG?.envs || []).length ? "" : "none";
	(CFG?.envs || []).forEach((e) => {
		const b = document.createElement("button");
		b.className = "pill" + (e.key === ENV ? " active" : "");
		b.dataset.env = e.key; b.title = e.account || e.name; b.textContent = e.key;
		b.onclick = () => setEnvKey(e.key);
		t.appendChild(b);
	});
}
function setEnvKey(k) {
	ENV = k;
	$$("#envToggle .pill").forEach((x) => x.classList.toggle("active", x.dataset.env === k));
	$("#sideEnv").textContent = k;
	renderNodes(); drawEdges(); renderLogs(); renderQuickLinks(); renderCrumb(); applyLayer(); closeDrawer();
	LIVE = null; LAMBDAS = null; PULSE = null;
	renderLive(); fetchLive(); fetchLambdas(); fetchLogGroups(); fetchCost(); fetchActivity();
	if (typeof secEnvChanged === "function") secEnvChanged();
	if (typeof tlEnvChanged === "function") tlEnvChanged();
}

/* ---- live sync (read-only, via local server) ---- */
const agoTxt = (ts) => {
	const m = Math.round((Date.now() - new Date(ts)) / 60000);
	return m < 1 ? "just now" : m < 60 ? m + "m ago" : Math.round(m / 60) + "h ago";
};
let PULSE = null;
const firingAlarms = () => {
	const base = (LIVE?.sections?.alarms || []).filter(a => a.state === "ALARM" && !a.name.startsWith("TargetTracking"));
	(PULSE?.alarms || []).forEach(n => { if (!base.some(a => a.name === n)) base.push({ name: n, state: "ALARM", reason: "detected by live pulse" }); });
	return base;
};
async function fetchPulse() {
	if (!LIVE?.ok) return;
	try {
		const r = await fetch(`/api/pulse?env=${ENV}`, { cache: "no-store" });
		const p = await r.json();
		if (!p.ok) return;
		PULSE = p;
		if (p.memMax != null && LIVE.sections.memory?.length)
			LIVE.sections.memory[LIVE.sections.memory.length - 1] = { ...LIVE.sections.memory.at(-1), max: p.memMax };
		if (p.ecs && LIVE.sections.ecs) Object.assign(LIVE.sections.ecs, p.ecs);
		renderLive();
	} catch { }
}
async function fetchLive() {
	try {
		const r = await fetch("/api/live?env=" + encodeURIComponent(ENV), { cache: "no-store" });
		LIVE = await r.json();
	} catch { LIVE = { ok: false, reason: "server" }; }
	renderLive();
}
function renderLive() {
	const b = $("#syncBadge"), t = $("#syncTxt");
	if (!LIVE?.ok) {
		b.className = "sync err";
		t.textContent = LIVE?.reason === "auth" ? "sync off · session expired"
			: LIVE?.reason === "no-workspace" ? "no workspace"
			: LIVE?.reason === "no-env" || noEnvsConfigured() ? "no environments" : "sync offline";
	} else {
		b.className = "sync live";
		t.textContent = `live · ${LIVE.profile} · ${agoTxt(LIVE.fetchedAt)}`;
	}
	const cg = $("#connectGate");
	if (LIVE && !LIVE.ok) {
		renderConnectGate();
	} else if (LIVE?.ok) {
		cg.classList.add("hidden");
		stopSsoPoll();
		ssoState = "";
		ssoAutoTried = false;
	}
	renderLiveTiles();
	applyNodeStatus();
	if (typeof renderCharts === "function") { renderCharts(); renderIssues(); }
	if (liveDrawerNode && $("#drawer").classList.contains("open")) openDrawer(liveDrawerNode);
}
/* ---- sso renewal ---- */
let ssoState = "", ssoProfile = "", ssoDetail = "", ssoAutoTried = false, ssoPoll = null;

const ssoEnvProfile = () => CFG?.envs?.find((x) => x.key === ENV)?.profiles?.[0] || "";
const ssoManualCmd = () => {
	const p = ssoEnvProfile();
	return p ? "aws sso login --profile " + p : "aws sso login";
};
const noEnvsConfigured = () => (!!CFG && Array.isArray(CFG.envs) && !CFG.envs.length) || LIVE?.reason === "no-env";

function renderConnectGate() {
	if (wsZero()) { $("#connectGate").classList.add("hidden"); return; }
	$("#connectGate").classList.remove("hidden");
	const why = $("#cgWhy"), cmd = $("#cgCmd"), prog = $("#cgProgress"), retry = $("#cgRetry"), again = $("#cgSsoRetry");
	cmd.textContent = ssoManualCmd();
	if (noEnvsConfigured()) {
		if ($("#viewSettings").classList.contains("active")) { $("#connectGate").classList.add("hidden"); return; }
		why.textContent = "No environments configured. Add your first environment in Settings.";
		prog.style.display = "none"; cmd.style.display = "none"; again.style.display = "none"; retry.style.display = "none";
		return;
	}
	if (LIVE?.reason !== "auth") {
		why.textContent = "The local sync server is unreachable.";
		prog.style.display = "none"; cmd.style.display = "none"; again.style.display = "none"; retry.style.display = "";
		return;
	}
	if (ssoState === "running") {
		why.textContent = "Renewing AWS session — approve the request in your browser.";
		$("#cgProgressTxt").textContent = "Waiting for approval · " + ssoProfile;
		prog.style.display = "flex"; cmd.style.display = "none"; again.style.display = "none"; retry.style.display = "none";
		return;
	}
	why.textContent = ssoState === "failed"
		? (ssoDetail || "The sign-in attempt did not complete.") + " You can try again, or run the command below in a terminal."
		: "The AWS session for this environment has expired, or no configured profile works.";
	prog.style.display = "none"; cmd.style.display = ""; again.style.display = ""; retry.style.display = "none";
	if (ssoState !== "failed" && !ssoAutoTried && $("#authGate").classList.contains("hidden")) {
		ssoAutoTried = true;
		startSsoRenew();
	}
}

async function startSsoRenew() {
	stopSsoPoll();
	ssoProfile = ssoEnvProfile();
	ssoDetail = "";
	if (!ssoProfile) {
		ssoState = "failed";
		ssoDetail = "No profile is configured for this environment.";
		return renderConnectGate();
	}
	ssoState = "running";
	renderConnectGate();
	try {
		const r = await fetch("/api/aws/login", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ profile: ssoProfile }) });
		if (r.status === 401) { ssoState = ""; return; }
		const d = await r.json();
		if (d.state === "running") return pollSsoRenew();
		if (d.state === "ok") return ssoRenewed();
		ssoState = "failed";
		ssoDetail = ssoFailText(d);
	} catch {
		ssoState = "failed";
		ssoDetail = "Could not reach the local server.";
	}
	renderConnectGate();
}

function pollSsoRenew() {
	stopSsoPoll();
	ssoPoll = setInterval(async () => {
		try {
			const r = await fetch("/api/aws/login?profile=" + encodeURIComponent(ssoProfile), { cache: "no-store" });
			if (r.status === 401) { stopSsoPoll(); ssoState = ""; return; }
			const d = await r.json();
			if (d.state === "running") return;
			stopSsoPoll();
			if (d.state === "ok") return ssoRenewed();
			ssoState = "failed";
			ssoDetail = ssoFailText(d);
			renderConnectGate();
		} catch { }
	}, 2000);
}

function ssoFailText(d) {
	if (d.state === "timeout") return "The sign-in request was not approved in time.";
	if (d.detail) return "Sign-in failed: " + d.detail;
	return "The sign-in attempt did not complete.";
}

function ssoRenewed() {
	stopSsoPoll();
	ssoState = "";
	$("#cgWhy").textContent = "Session renewed.";
	$("#cgProgressTxt").textContent = "Reconnecting · " + ssoProfile;
	$("#cgProgress").style.display = "flex";
	$("#cgCmd").style.display = "none"; $("#cgSsoRetry").style.display = "none"; $("#cgRetry").style.display = "none";
	LIVE = null; LAMBDAS = null; PULSE = null;
	fetchLive(); fetchLambdas(); fetchLogGroups(); fetchCost(); fetchActivity();
}

function stopSsoPoll() {
	if (ssoPoll) { clearInterval(ssoPoll); ssoPoll = null; }
}

$("#cgSsoRetry").onclick = () => startSsoRenew();

function renderLiveTiles() {
	const el = $("#liveTiles");
	if (!LIVE?.ok) {
		el.innerHTML = `<div class="tile"><div class="num">–</div><div class="lbl">${LIVE?.reason === "auth" ? "session expired · " + escHtml(ssoManualCmd())
			: LIVE?.reason === "no-workspace" ? "no workspace selected"
			: LIVE?.reason === "no-env" || noEnvsConfigured() ? "no environments configured" : "sync offline"}</div></div>`;
		return;
	}
	const s = LIVE.sections, mem = s.memory?.at(-1), cpu = s.cpu?.at(-1);
	const firing = firingAlarms().length;
	const backlog = (s.queues || []).reduce((a, q) => a + q.visible, 0);
	const tiles = [];
	if (s.ecs) tiles.push([`${s.ecs.running}<span class="u">/${s.ecs.desired}</span>`, "tasks running", s.ecs.running < s.ecs.desired ? "t-warn" : "", awsConsoleUrl("ecs")]);
	if (s.ecs) tiles.push([`<span style="font-size:16px">${s.ecs.rollout}</span>`, "deployment · " + s.ecs.taskDef, s.ecs.rollout !== "COMPLETED" ? "t-warn" : "", awsConsoleUrl("ecs")]);
	if (mem) tiles.push([`${mem.max}<span class="u">%</span>`, "memory now (max task)", mem.max > SETTINGS.memCrit ? "t-crit" : mem.max > SETTINGS.memWarn ? "t-warn" : "", awsConsoleUrl("ecs")]);
	if (cpu) tiles.push([`${cpu.max}<span class="u">%</span>`, "cpu now (max task)", cpu.max > 85 ? "t-warn" : "", awsConsoleUrl("ecs")]);
	tiles.push([String(firing), "alarms firing", firing ? "t-crit" : "", awsConsoleUrl("alarm")]);
	if (s.queues) tiles.push([String(backlog), "queued messages", backlog > 50 ? "t-warn" : "", awsConsoleUrl("sqs")]);
	if (s.targets) tiles.push([`${s.targets.healthy}<span class="u">/${s.targets.total}</span>`, "healthy targets", s.targets.healthy < s.targets.total ? "t-crit" : "", awsConsoleUrl("ecs")]);
	el.innerHTML = tiles.map(([n, l, c, u]) => u
		? `<a class="tile ${c}" href="${u}" target="_blank" rel="noopener"><div class="num">${n}</div><div class="lbl">${l}</div></a>`
		: `<div class="tile ${c}"><div class="num">${n}</div><div class="lbl">${l}</div></div>`).join("");
}
function applyNodeStatus() {
	$$(".node .st").forEach(x => x.remove());
	const travel = typeof timeTravelDots === "function" ? timeTravelDots() : null;
	if (!travel && !LIVE?.ok) return;
	let dot = travel;
	if (!dot) {
		const s = LIVE.sections;
		dot = {};
		if (s.ecs) dot.ecs = s.ecs.running >= s.ecs.desired && s.ecs.rollout === "COMPLETED" ? "g" : "w";
		dot.obs = firingAlarms().length ? "c" : "g";
		if (s.queues) {
			const dlq = s.queues.filter(q => /dlq|dead/.test(q.name)).reduce((a, q) => a + q.visible, 0);
			dot.sqsN = dlq > 0 ? "c" : "g";
		}
		if (s.targets) dot.alb = s.targets.total > 0 && s.targets.healthy === s.targets.total ? "g" : "c";
		const memNow = s.memory?.at(-1);
		if (memNow && dot.ecs === "g" && memNow.max > SETTINGS.memWarn) dot.ecs = "w";
	}
	Object.entries(dot).forEach(([id, cls]) => {
		const el = $("#n-" + id);
		if (el) el.insertAdjacentHTML("beforeend", `<span class="st ${cls}"></span>`);
	});
}
function liveSection(n) {
	if (!LIVE?.ok) return "";
	const s = LIVE.sections;
	const rows = [];
	const row = (k, v, cls = "") => rows.push(`<div class="live-row"><span class="lk">${k}</span><span class="lv ${cls}">${v}</span></div>`);
	if (n.id === "ecs" && s.ecs) {
		row("Tasks", `${s.ecs.running} running / ${s.ecs.desired} desired`, s.ecs.running < s.ecs.desired ? "warn" : "ok");
		row("Deployment", s.ecs.rollout, s.ecs.rollout === "COMPLETED" ? "ok" : "warn");
		row("Task definition", s.ecs.taskDef);
		const m = s.memory?.at(-1), c = s.cpu?.at(-1);
		if (m) row("Memory (max task)", m.max + "%", m.max > 90 ? "crit" : m.max > 70 ? "warn" : "ok");
		if (c) row("CPU (max task)", c.max + "%", c.max > 85 ? "warn" : "ok");
		if (s.ecs.events?.[0]) row("Last event", agoTxt(s.ecs.events[0].at));
	} else if (n.id === "obs" && s.alarms) {
		s.alarms.filter(a => !a.name.startsWith("TargetTracking")).slice(0, 6).forEach(a =>
			row(a.name.replace(/^fairground-/, ""), a.state, a.state === "ALARM" ? "crit" : "ok"));
	} else if (n.id === "alb" && s.targets) {
		row("Healthy targets", `${s.targets.healthy} / ${s.targets.total}`, s.targets.healthy === s.targets.total ? "ok" : "crit");
	} else if (n.id === "ecr" && s.images) {
		s.images.forEach(i => row(i.tags[0] ? i.tags[0].slice(0, 12) : "untagged", agoTxt(i.pushedAt)));
	} else if ((n.id === "sqsN" || n.id === "sftpq" || n.id === "del") && s.queues) {
		const match = { sqsN: /notification/, sftpq: /sftp/, del: /delete-account/ }[n.id];
		s.queues.filter(q => match.test(q.name)).forEach(q =>
			row(q.name.replace(/^fairground-/, "").slice(0, 34), `${q.visible} queued · ${q.inflight} in-flight`, /dlq|dead/.test(q.name) && q.visible > 0 ? "crit" : q.visible > 20 ? "warn" : "ok"));
	}
	return rows.length ? `<div class="dsec"><h4>Live · ${agoTxt(LIVE.fetchedAt)}</h4>${rows.join("")}</div>` : "";
}

