/**
 * ComputeTab — native physics in the panel (KP.2 + KP.3).
 *
 * Two modes:
 *  • Operator — search the 1,500+ registry, pick operator(s), supply typed
 *    inputs, run /api/zeq/compute → value · unit · equation · signed ZeqProof.
 *    On a dimensional mismatch it surfaces the engine's 7-step protocol trace.
 *  • Simulate — the ODE engine: 1 body → /api/solve (master-equation), 2+ bodies
 *    → /api/multibody (N-body coupled). Shows energy, error %, per-body summary.
 *
 * All calls ride the shared SW 'physics' bridge that also powers window.zeq.
 *
 * @version 1.287.27
 * @license CC BY 4.0 — Zeq. H / Zeq 1.287 Hz Limited
 */
const ComputeTab = (() => {
  "use strict";
  let _cid = null, _mode = "operator";
  // operator mode state
  let _rows = [{ k: "mass", v: "1" }, { k: "acceleration", v: "9.8" }];
  let _ops = "KO42";
  let _result = null, _busy = false, _searchHits = [], _verify = null;
  let _codeLang = "window.zeq", _codeOrigin = "https://zeqsdk.com";
  // simulate mode state
  let _sim = { prompt: "a 1 kg mass in free fall under gravity", bodies: [{ mass: "1" }, { mass: "2" }], tMax: "2", dt: "0.25" };
  let _simResult = null, _simBusy = false;

  function esc(s) { return String(s == null ? "" : s).replace(/[&<>"]/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" }[c])); }
  function sw(msg) { return new Promise((res) => { try { chrome.runtime.sendMessage(msg, (r) => res(chrome.runtime.lastError ? null : r)); } catch (_) { res(null); } }); }
  function authed() { return typeof ZeqAuth !== "undefined" && ZeqAuth.isAuthenticated(); }

  const STYLE = "<style>" +
    ".zc{padding:12px;overflow-y:auto;height:100%}" +
    ".zc .modes{display:flex;gap:6px;margin-bottom:10px}" +
    ".zc .mode{flex:1;padding:7px;font-size:12px;font-weight:600;border:1px solid var(--border);border-radius:7px;background:transparent;color:var(--text-secondary);cursor:pointer}" +
    ".zc .mode.active{border-color:var(--accent-mint);color:var(--accent-mint);background:rgba(0,255,136,.06)}" +
    ".zc h3{font-size:13px;margin:0 0 3px;color:var(--text-primary)}" +
    ".zc .lead{font-size:11px;color:var(--text-muted);line-height:1.5;margin:0 0 10px}" +
    ".zc .card{border:1px solid var(--border);border-radius:9px;padding:10px;margin-bottom:9px;background:var(--bg-secondary)}" +
    ".zc label{font-size:10px;letter-spacing:.06em;text-transform:uppercase;color:var(--text-muted);display:block;margin:2px 0 4px}" +
    ".zc input,.zc textarea{width:100%;padding:7px 9px;background:rgba(0,0,0,.45);border:1px solid var(--border);border-radius:7px;color:var(--text-primary);font-size:12px;outline:none;font-family:inherit}" +
    ".zc textarea{min-height:46px;resize:vertical}" +
    ".zc input:focus,.zc textarea:focus{border-color:var(--accent-mint)}" +
    ".zc .ops{font-family:'JetBrains Mono',monospace}" +
    ".zc .hits{display:flex;flex-wrap:wrap;gap:4px;margin:5px 0 0}" +
    ".zc .hit{font-size:10px;padding:3px 7px;border-radius:5px;border:1px solid var(--border);background:transparent;color:var(--accent-mint);cursor:pointer;font-family:'JetBrains Mono',monospace}" +
    ".zc .hit:hover{border-color:var(--accent-mint)}" +
    ".zc .row{display:flex;gap:6px;margin-bottom:6px;align-items:center}" +
    ".zc .row input{flex:1}" +
    ".zc .row .del{flex:0 0 auto;width:26px;height:30px;border:1px solid var(--border);border-radius:6px;background:transparent;color:#ff8a8a;cursor:pointer;font-size:13px}" +
    ".zc .two{display:flex;gap:6px}.zc .two>div{flex:1}" +
    ".zc .add{font-size:10px;padding:4px 9px;border-radius:6px;border:1px dashed var(--border);background:transparent;color:var(--text-secondary);cursor:pointer}" +
    ".zc .btn{width:100%;margin-top:9px;padding:10px;border-radius:8px;border:1px solid var(--accent-mint);background:var(--accent-mint);color:#02120a;font-weight:700;font-size:13px;cursor:pointer}" +
    ".zc .btn:disabled{opacity:.5}" +
    ".zc .val{font-family:'JetBrains Mono',monospace;font-size:26px;font-weight:800;color:var(--accent-mint);word-break:break-all}" +
    ".zc .unit{font-size:14px;color:var(--text-secondary);font-weight:600}" +
    ".zc .eq{font-size:11px;color:var(--text-secondary);margin-top:6px;word-break:break-word;font-family:'JetBrains Mono',monospace}" +
    ".zc .proof{font-size:9.5px;color:var(--text-muted);font-family:'JetBrains Mono',monospace;margin-top:8px;word-break:break-all;border-top:1px solid var(--border);padding-top:7px}" +
    ".zc .proof b{color:var(--accent-mint)}.zc .proof a{color:var(--accent-mint)}" +
    ".zc .stat{display:flex;gap:8px;margin-bottom:6px}.zc .stat .b{flex:1;border:1px solid var(--border);border-radius:8px;padding:8px;text-align:center}" +
    ".zc .stat .v{font-size:16px;font-weight:800;color:var(--accent-mint);font-family:'JetBrains Mono',monospace}.zc .stat .k{font-size:9px;color:var(--text-muted);text-transform:uppercase}" +
    ".zc .err{border-color:#ff5577}.zc .err .hd{font-size:12px;font-weight:700;color:#ff8a8a}" +
    ".zc .err .detail{font-size:11px;color:var(--text-secondary);margin-top:5px;line-height:1.5;word-break:break-word}" +
    ".zc .step{font-size:10px;color:var(--text-muted);margin-top:4px}.zc .step b{color:var(--text-secondary)}" +
    ".zc .vbadge{font-size:11px;font-weight:700;padding:3px 8px;border-radius:6px}" +
    ".zc .vbadge.ok{background:rgba(0,255,136,.15);color:var(--accent-mint)}.zc .vbadge.bad{background:rgba(255,120,120,.15);color:#ff8a8a}" +
    ".zc .empty{font-size:11px;color:var(--text-muted);line-height:1.5}" +
    "</style>";

  function shell(inner) {
    return STYLE + '<div class="zc">' +
      '<div class="modes">' +
      '<button class="mode' + (_mode === "operator" ? " active" : "") + '" data-m="operator">Operator</button>' +
      '<button class="mode' + (_mode === "simulate" ? " active" : "") + '" data-m="simulate">Simulate</button>' +
      '<button class="mode' + (_mode === "code" ? " active" : "") + '" data-m="code">Code</button></div>' +
      inner + "</div>";
  }

  // ── Operator mode ──────────────────────────────────────────────────────────
  function rowsHtml() {
    return _rows.map((r, i) =>
      '<div class="row"><input data-i="' + i + '" data-f="k" placeholder="input (e.g. mass)" value="' + esc(r.k) + '">' +
      '<input data-i="' + i + '" data-f="v" placeholder="value" value="' + esc(r.v) + '">' +
      '<button class="del" data-del="' + i + '" title="remove">×</button></div>'
    ).join("");
  }
  // P17 — a 400 is no longer always the dimensional compiler. The wizard now
  // refuses with four distinct codes, and telling a user "Dimensional compiler
  // rejected this" when they simply typed an operator that does not exist sends
  // them hunting in the wrong place. The message body was always right; the
  // heading was hardcoded to the status.
  function _refusalTitle(d) {
    switch (d && d.code) {
      case "UNKNOWN_OPERATOR":  return "No such operator";
      case "UNKNOWN_DOMAIN":    return "No such domain";
      case "AMBIGUOUS_DOMAIN":  return "That domain is ambiguous";
      case "DIMENSIONAL_MISMATCH": return "Dimensional compiler rejected this";
      default: return "The machine refused";
    }
  }

  function operatorResultHtml() {
    if (!_result) return "";
    if (_result.ok) {
      const d = _result.data || {};
      const explorer = d.explorer_url ? ' · <a href="#" data-explorer="' + esc(d.explorer_url) + '">explorer ↗</a>' : "";
      // The machine refuses rather than inventing (2026-07-17). value is null and
      // inputError names the missing quantity. Without this branch we rendered
      // String(null) — the user saw the word "null" as their result, with a
      // ZeqProof under it. Show them the one thing that unsticks them: the key.
      if (d.inputError) {
        return '<div class="card"><label>Needs one more input</label>' +
          '<div class="step" style="color:#ffd166">' + esc(d.inputError.message || "missing required input") + "</div>" +
          (d.inputError.accepts ? '<div class="step">Accepted keys: <b>' + esc(String(d.inputError.accepts)) + "</b></div>" : "") +
          '<div class="step" style="opacity:.75">The machine will not invent this value — supply it and compute again.</div>' +
          "</div>";
      }
      return '<div class="card"><label>Result' + (d.free_tier ? ' · FREE TIER' : "") + "</label>" +
        '<div><span class="val">' + esc(String(d.value)) + '</span> <span class="unit">' + esc(d.unit || "") + "</span></div>" +
        (d.free_tier ? '<div class="step" style="color:#ffd166">⚠ Out of ZEQ — computed on the free tier: same value, no charged state envelope (no signed receipt). Top up to seal proofs.</div>' : "") +
        (d.uncertainty != null ? '<div class="step">± ' + esc(String(d.uncertainty)) + "</div>" : "") +
        (d.equation ? '<div class="eq">' + esc(d.equation) + "</div>" : "") +
        (d.assumed && Object.keys(d.assumed).length
          ? '<div class="step" style="color:#ffd166">Assumed: ' +
            esc(Object.entries(d.assumed).map(function (e) { return e[0] + " = " + e[1]; }).join("; ")) +
            "</div>"
          : "") +
        '<div class="proof">ZeqProof <b>' + esc(String(d.zeqProof || "").slice(0, 24)) + "…</b>" +
        (d.zeqond != null ? " · z" + esc(String(d.zeqond)) : "") + (d.signed ? " · ed25519" : "") + explorer + "</div>" +
        (d.verify_claim && d.zeqProof
          ? '<div style="margin-top:8px;display:flex;align-items:center;gap:8px">' +
            '<button class="add" id="zc-verify">Verify proof</button>' +
            (_verify ? '<span class="' + (_verify.valid ? "vbadge ok" : "vbadge bad") + '">' + (_verify.valid ? "✓ VALID" : (_verify.error ? "error" : "✗ INVALID")) + "</span>" : "") +
            "</div>"
          : "") + "</div>";
    }
    const d = _result.data || {};
    const msg = (d.error && (d.error.message || d.error)) || _result.error || ("HTTP " + _result.status);
    const steps = Array.isArray(d.protocol_steps) ? d.protocol_steps.slice(0, 7) : [];
    return '<div class="card err"><div class="hd">' + (_result.status === 402 ? "Insufficient ZEQ balance" : (_result.status === 400 ? _refusalTitle(d) : "Compute failed")) + "</div>" +
      '<div class="detail">' + esc(String(msg)) + "</div>" +
      steps.map((s) => '<div class="step"><b>' + esc(s.step + " · " + (s.name || "")) + "</b> — " + esc(s.status || "") + "</div>").join("") + "</div>";
  }
  function renderOperator() {
    return "<h3>Compute</h3>" +
      '<p class="lead">Run an operator from the 1,500+ catalogue against typed physical inputs. Dimensionally checked, returns a signed ZeqProof.</p>' +
      '<div class="card"><label>Operator(s) — comma-separated</label>' +
      '<input class="ops" id="zc-ops" placeholder="KO42, NM19, …" value="' + esc(_ops) + '">' +
      '<input id="zc-search" placeholder="search the registry (e.g. quantum, gravity)" style="margin-top:6px">' +
      '<div class="hits" id="zc-hits">' + _searchHits.map((h) => '<button class="hit" data-add="' + esc(h.id) + '" title="' + esc(h.name || "") + '">' + esc(h.id) + "</button>").join("") + "</div></div>" +
      '<div class="card"><label>Inputs</label>' + rowsHtml() + '<button class="add" id="zc-add">+ add input</button></div>' +
      '<button class="btn" id="zc-run"' + (_busy ? " disabled" : "") + ">" + (_busy ? "Computing…" : "Compute → ZeqProof") + "</button>" +
      operatorResultHtml();
  }

  // ── Simulate mode ────────────────────────────────────────────────────────────
  function simResultHtml() {
    if (!_simResult) return "";
    if (!_simResult.ok) {
      const d = _simResult.data || {};
      const msg = (d.error && (d.error.message || d.error)) || _simResult.error || ("HTTP " + _simResult.status);
      return '<div class="card err"><div class="hd">' + (_simResult.status === 402 ? "Insufficient ZEQ balance" : "Simulation failed") + '</div><div class="detail">' + esc(String(msg)) + "</div></div>";
    }
    const d = _simResult.data || {};
    if (_simResult.kind === "multibody") {
      const bodies = d.bodies || [];
      return '<div class="card"><label>N-body result</label>' +
        '<div class="stat"><div class="b"><div class="v">' + bodies.length + '</div><div class="k">bodies</div></div>' +
        '<div class="b"><div class="v">' + esc(String(d.totalEnergy != null ? Number(d.totalEnergy).toPrecision(4) : "—")) + '</div><div class="k">total E</div></div>' +
        '<div class="b"><div class="v">' + ((d.interactions || []).length) + '</div><div class="k">pairs</div></div></div>' +
        '<div class="step">tEval ' + esc(String(d.tEval != null ? d.tEval : "—")) + ' · mode ' + esc(d.mode || "") + '</div>' +
        (d.ckoId ? '<div class="proof">CKO <b>' + esc(String(d.ckoId).slice(0, 20)) + "…</b></div>" : "") + "</div>";
    }
    return '<div class="card"><label>Master-equation result</label>' +
      '<div class="stat"><div class="b"><div class="v">' + esc(String(d.energy != null ? Number(d.energy).toPrecision(4) : "—")) + '</div><div class="k">energy</div></div>' +
      '<div class="b"><div class="v">' + esc(String(d.errorPct != null ? Number(d.errorPct).toPrecision(3) + "%" : "—")) + '</div><div class="k">error</div></div>' +
      '<div class="b"><div class="v">' + esc(String(d.mass != null ? d.mass : "—")) + '</div><div class="k">mass</div></div></div>' +
      (d.functionalEnergy != null ? '<div class="step"><b>functional E</b> ' + esc(String(d.functionalEnergy)) + "</div>" : "") +
      '<div class="step">' + esc(String(d.object || d.prompt || "")) + "</div>" +
      (d.ckoId ? '<div class="proof">CKO <b>' + esc(String(d.ckoId).slice(0, 20)) + "…</b></div>" : "") + "</div>";
  }
  function renderSimulate() {
    const bodyRows = _sim.bodies.map((b, i) =>
      '<div class="row"><input data-bi="' + i + '" placeholder="mass (kg)" value="' + esc(b.mass) + '">' +
      (_sim.bodies.length > 1 ? '<button class="del" data-bdel="' + i + '" title="remove body">×</button>' : "") + "</div>"
    ).join("");
    return "<h3>Simulate</h3>" +
      '<p class="lead">Run the ODE engine: one body solves the master equation; two or more run the coupled N-body solver. Result is CKO-scored and published to your machine.</p>' +
      '<div class="card"><label>Scenario</label><textarea id="zs-prompt" placeholder="describe the system">' + esc(_sim.prompt) + "</textarea>" +
      '<label style="margin-top:8px">Bodies (mass each)</label>' + bodyRows +
      '<button class="add" id="zs-addbody">+ add body</button>' +
      '<div class="two" style="margin-top:8px"><div><label>t max</label><input id="zs-tmax" value="' + esc(_sim.tMax) + '"></div>' +
      '<div><label>dt</label><input id="zs-dt" value="' + esc(_sim.dt) + '"></div></div></div>' +
      '<button class="btn" id="zs-run"' + (_simBusy ? " disabled" : "") + ">" + (_simBusy ? "Simulating…" : (_sim.bodies.length > 1 ? "Run N-body" : "Run master-equation")) + "</button>" +
      simResultHtml();
  }

  // ── Code samples (ported from the VS Code SDK) ───────────────────────────────
  function _currentInputs() {
    const inp = {};
    _rows.forEach((r) => { if (r.k) { const n = Number(r.v); inp[r.k] = (r.v !== "" && Number.isFinite(n)) ? n : r.v; } });
    return inp;
  }
  function _opsArr() { return _ops.split(",").map((s) => s.trim()).filter(Boolean); }
  const CODE_LANGS = ["window.zeq", "python", "javascript", "go", "rust", "curl"];
  function genSnippet(lang, base, operators, inputs) {
    const opsJson = JSON.stringify(operators);
    const inJson = JSON.stringify(inputs);
    const inPy = inJson.replace(/"([^"]+)":/g, '"$1": ').replace(/true/g, "True").replace(/false/g, "False");
    switch (lang) {
      case "window.zeq":
        return "// In any page the user granted (⚛ Compute). No API key in your code.\n" +
          "const r = await window.zeq.compute(" + opsJson + ", " + inJson + ");\n" +
          "console.log(r.value, r.unit);          // e.g. 6 \"N\"\n" +
          "console.log(r.zeqProof);                // signed receipt\n" +
          "await window.zeq.verify(r.zeqProof, r.verify_claim);  // → { valid: true }";
      case "python":
        return "# pip install requests\nimport os, requests\n" +
          "r = requests.post(\n    \"" + base + "/api/zeq/compute\",\n" +
          "    headers={\"Authorization\": f\"Bearer {os.environ['ZEQ_API_KEY']}\"},\n" +
          "    json={\"operators\": " + opsJson + ", \"inputs\": " + inPy + "},\n).json()\n" +
          "print(r[\"value\"], r[\"unit\"])";
      case "javascript":
        return "// Node 18+ (global fetch) or the browser\n" +
          "const r = await fetch(\"" + base + "/api/zeq/compute\", {\n" +
          "  method: \"POST\",\n" +
          "  headers: { Authorization: `Bearer ${process.env.ZEQ_API_KEY}`, \"Content-Type\": \"application/json\" },\n" +
          "  body: JSON.stringify({ operators: " + opsJson + ", inputs: " + inJson + " })\n" +
          "}).then(x => x.json());\nconsole.log(r.value, r.unit);";
      case "go":
        return "body, _ := json.Marshal(map[string]any{\n" +
          "    \"operators\": " + JSON.stringify(operators).replace(/"/g, "\"") + ",\n" +
          "    \"inputs\":    map[string]any{" + Object.entries(inputs).map(([k, v]) => "\"" + k + "\": " + JSON.stringify(v)).join(", ") + "},\n})\n" +
          "req, _ := http.NewRequest(\"POST\", \"" + base + "/api/zeq/compute\", bytes.NewReader(body))\n" +
          "req.Header.Set(\"Authorization\", \"Bearer \"+os.Getenv(\"ZEQ_API_KEY\"))\n" +
          "req.Header.Set(\"Content-Type\", \"application/json\")";
      case "rust":
        return "// Cargo.toml: reqwest = { version = \"0.12\", features = [\"json\"] }\n" +
          "let body = serde_json::json!({ \"operators\": " + opsJson + ", \"inputs\": " + inJson + " });\n" +
          "let r: serde_json::Value = reqwest::Client::new()\n" +
          "    .post(\"" + base + "/api/zeq/compute\")\n" +
          "    .bearer_auth(std::env::var(\"ZEQ_API_KEY\")?)\n" +
          "    .json(&body).send().await?.json().await?;";
      case "curl":
        return "curl -X POST " + base + "/api/zeq/compute \\\n" +
          "  -H \"Authorization: Bearer $ZEQ_API_KEY\" \\\n" +
          "  -H \"Content-Type: application/json\" \\\n" +
          "  -d '" + JSON.stringify({ operators, inputs }) + "'";
      default: return "";
    }
  }
  function renderCode() {
    const operators = _opsArr().length ? _opsArr() : ["KO42"];
    const inputs = _currentInputs();
    const snip = genSnippet(_codeLang, _codeOrigin, operators, inputs);
    return "<h3>Code</h3>" +
      '<p class="lead">Ready-to-paste integration code for the operator(s) + inputs above — embed the engine into your own software. (Edit them in the Operator tab.)</p>' +
      '<div class="card"><div class="hits">' +
      CODE_LANGS.map((l) => '<button class="hit" data-lang="' + l + '"' + (l === _codeLang ? ' style="border-color:var(--accent-mint);color:var(--accent-mint)"' : "") + ">" + esc(l) + "</button>").join("") + "</div>" +
      '<pre id="zc-code" style="white-space:pre-wrap;word-break:break-word;background:rgba(0,0,0,.5);border:1px solid var(--border);border-radius:7px;padding:9px;font-size:10.5px;color:#cfe;font-family:\'JetBrains Mono\',monospace;margin-top:8px;line-height:1.5">' + esc(snip) + "</pre>" +
      '<button class="add" id="zc-copy">Copy snippet</button></div>';
  }
  function wireCode() {
    document.querySelectorAll(".zc .hit[data-lang]").forEach((b) => b.addEventListener("click", () => { _codeLang = b.getAttribute("data-lang"); render(); }));
    const copy = document.getElementById("zc-copy");
    if (copy) copy.addEventListener("click", () => {
      const pre = document.getElementById("zc-code");
      if (pre && navigator.clipboard) navigator.clipboard.writeText(pre.textContent).then(() => { const o = copy.textContent; copy.textContent = "✓ copied"; setTimeout(() => { copy.textContent = o; }, 1400); });
    });
  }

  function render() {
    const c = document.getElementById(_cid);
    if (!c) return;
    if (!authed()) { c.innerHTML = STYLE + '<div class="zc"><div class="empty">Sign in to run physics — unlimited on paid tiers, metered on Free/Starter. LLM is bring-your-own-key. (pulse/prove/verify are free.)</div></div>'; return; }
    const body = _mode === "operator" ? renderOperator() : (_mode === "simulate" ? renderSimulate() : renderCode());
    c.innerHTML = shell(body);
    c.querySelectorAll(".mode[data-m]").forEach((b) => b.addEventListener("click", () => { _mode = b.getAttribute("data-m"); render(); }));
    if (_mode === "operator") wireOperator(); else if (_mode === "simulate") wireSimulate(); else wireCode();
  }

  function wireOperator() {
    const ops = document.getElementById("zc-ops");
    if (ops) ops.addEventListener("input", () => { _ops = ops.value; });
    document.querySelectorAll('.zc input[data-i]').forEach((el) => el.addEventListener("input", () => {
      const i = +el.getAttribute("data-i"), f = el.getAttribute("data-f"); if (_rows[i]) _rows[i][f] = el.value;
    }));
    document.querySelectorAll(".zc .del[data-del]").forEach((b) => b.addEventListener("click", () => { _rows.splice(+b.getAttribute("data-del"), 1); render(); }));
    const add = document.getElementById("zc-add");
    if (add) add.addEventListener("click", () => { _rows.push({ k: "", v: "" }); render(); });
    const run = document.getElementById("zc-run");
    if (run) run.addEventListener("click", doCompute);
    const vbtn = document.getElementById("zc-verify");
    if (vbtn) vbtn.addEventListener("click", doVerify);
    const search = document.getElementById("zc-search");
    if (search) {
      let t = null;
      search.addEventListener("input", () => {
        clearTimeout(t); const q = search.value.trim();
        if (q.length < 2) { _searchHits = []; return; }
        t = setTimeout(async () => {
          const r = await sw({ type: "physics", method: "operators", args: { search: q, limit: 12 } });
          _searchHits = (r && r.data && r.data.operators) || [];
          const host = document.getElementById("zc-hits");
          if (host) { host.innerHTML = _searchHits.map((h) => '<button class="hit" data-add="' + esc(h.id) + '" title="' + esc(h.name || "") + '">' + esc(h.id) + "</button>").join(""); host.querySelectorAll(".hit[data-add]").forEach((b) => b.addEventListener("click", () => addOp(b.getAttribute("data-add")))); }
        }, 300);
      });
    }
    document.querySelectorAll(".zc .hit[data-add]").forEach((b) => b.addEventListener("click", () => addOp(b.getAttribute("data-add"))));
    document.querySelectorAll(".zc [data-explorer]").forEach((a) => a.addEventListener("click", async (e) => {
      e.preventDefault(); try { const o = (typeof ZeqConfig !== "undefined" && await ZeqConfig.origin()) || ""; window.open(o + a.getAttribute("data-explorer"), "_blank"); } catch (_) {}
    }));
  }
  function addOp(id) { const cur = _ops.split(",").map((s) => s.trim()).filter(Boolean); if (!cur.includes(id)) cur.push(id); _ops = cur.join(", "); render(); }
  async function doCompute() {
    if (_busy) return;
    const operators = _ops.split(",").map((s) => s.trim()).filter(Boolean);
    const inputs = {};
    _rows.forEach((r) => { if (r.k) { const n = Number(r.v); inputs[r.k] = (r.v !== "" && Number.isFinite(n)) ? n : r.v; } });
    _busy = true; _result = null; _verify = null; render();
    const r = await sw({ type: "physics", method: "compute", args: { operators: operators.length ? operators : ["KO42"], inputs } });
    _busy = false;
    _result = r ? { ok: !!r.success, status: r.status, data: r.data, error: r.error } : { ok: false, error: "bridge error" };
    render();
    // Append envelope to the user's chain (graceful fallback if /api/chain/{slug}/append not yet deployed)
    if (_result && _result.ok && _result.data) _tryChainAppend(_result.data);
  }

  /** Commit the envelope as a chain transition on the signed-in machine.
   *  Endpoint is 404 today; ZeqSafeFetch surfaces a friendly "not deployed yet"
   *  notice. Once the backend route ships, this Just Works. */
  async function _tryChainAppend(data) {
    try {
      if (!authed() || !window.ZeqSafeFetch) return;
      const u = ZeqAuth.getUser && ZeqAuth.getUser();
      const slug = u && (u.machineSlug || u.machine_slug || u.zid);
      if (!slug) return;
      const api = await ZeqConfig.api();
      const cko = data.cko || data;
      const body = {
        zeqond: cko.zeqondTick || cko.zeqond,
        phase: cko.phase,
        operator_chain: cko.operatorChain || [],
        master_sum: cko.masterSum || data.master_sum,
        r_t: cko["R(t)"] || cko.R_t,
        zeq_proof: cko.zeqProof || data.zeqProof,
        precision_bound: cko.precisionBound,
      };
      const r = await window.ZeqSafeFetch.safeFetch(api + "/chain/" + encodeURIComponent(slug) + "/append", {
        method: "POST", headers: Object.assign({ "Content-Type": "application/json" }, ZeqAuth.getHeaders()), body: JSON.stringify(body),
      });
      if (r.ok && typeof ObserverTab !== "undefined" && ObserverTab.init) ObserverTab.init && ObserverTab.init(_cid); // best-effort refresh
      if (!r.ok && window.ZeqSafeFetch) window.ZeqSafeFetch.reportSafeResult("Chain append", r);
    } catch (_) { /* silent — observer/console will surface state */ }
  }
  // KP.4 — round-trip verify the just-computed proof against its signed claim.
  async function doVerify() {
    if (!_result || !_result.data || !_result.data.verify_claim) return;
    const d = _result.data;
    const claim = Object.assign({ proof: d.zeqProof }, d.verify_claim);
    _verify = { pending: true }; render();
    const r = await sw({ type: "physics", method: "verify", args: claim });
    _verify = (r && r.success) ? { valid: !!(r.data && (r.data.valid ?? r.data.ok ?? r.data.verified)) } : { error: true };
    render();
  }

  function wireSimulate() {
    const p = document.getElementById("zs-prompt"); if (p) p.addEventListener("input", () => { _sim.prompt = p.value; });
    document.querySelectorAll('.zc input[data-bi]').forEach((el) => el.addEventListener("input", () => { const i = +el.getAttribute("data-bi"); if (_sim.bodies[i]) _sim.bodies[i].mass = el.value; }));
    document.querySelectorAll(".zc .del[data-bdel]").forEach((b) => b.addEventListener("click", () => { _sim.bodies.splice(+b.getAttribute("data-bdel"), 1); render(); }));
    const addb = document.getElementById("zs-addbody"); if (addb) addb.addEventListener("click", () => { _sim.bodies.push({ mass: "1" }); render(); });
    const tmax = document.getElementById("zs-tmax"); if (tmax) tmax.addEventListener("input", () => { _sim.tMax = tmax.value; });
    const dt = document.getElementById("zs-dt"); if (dt) dt.addEventListener("input", () => { _sim.dt = dt.value; });
    const run = document.getElementById("zs-run"); if (run) run.addEventListener("click", doSimulate);
  }
  async function doSimulate() {
    if (_simBusy) return;
    _simBusy = true; _simResult = null; render();
    const tMax = Number(_sim.tMax) || 2, dt = Number(_sim.dt) || 0.25;
    let r, kind;
    if (_sim.bodies.length > 1) {
      kind = "multibody";
      const bodies = _sim.bodies.map((b) => ({ mass: Number(b.mass) || 1 }));
      r = await sw({ type: "physics", method: "multibody", args: { prompt: _sim.prompt, bodies, tMax, dt } });
    } else {
      kind = "solve";
      r = await sw({ type: "physics", method: "solve", args: { prompt: _sim.prompt, mass: Number(_sim.bodies[0] && _sim.bodies[0].mass) || 1, tMax, dt } });
    }
    _simBusy = false;
    _simResult = r ? { ok: !!r.success, status: r.status, data: r.data, error: r.error, kind } : { ok: false, error: "bridge error", kind };
    render();
  }

  function init(cid) {
    _cid = cid; render();
    try { if (typeof ZeqConfig !== "undefined" && ZeqConfig.origin) ZeqConfig.origin().then((o) => { if (o) { _codeOrigin = o; if (_mode === "code") render(); } }); } catch (_) {}
  }
  function stop() {}
  return { init, stop };
})();
if (typeof module !== "undefined" && module.exports) { module.exports = ComputeTab; }
