/* FORGE — pump-eco · ScreenERPReleaseQueue
   The "go live" surface. Lists every ECO that has cleared approval, surfaces
   the LIVE blockers per ECO, and offers a Release-to-ERP button that's only
   enabled when zero blockers remain.

   Babel-standalone IIFE; no imports.

   Live blocker rules (in order of severity):
     1. any approval_chain entry status != "approved"          → blocked
     2. any supplier with status="blocked" supplying an affected part → blocked
     3. window.forgeApi.fcp.health() returns false              → blocked
     4. cost_delta_inr > 500 without Plant Director sign-off    → blocked

   Plant Director override: any blocker can be overridden by an actor with
   role "Plant Director" (or by manually toggling the supplier override on
   this screen). Overrides are recorded into the provenance receipt.

   Mounts at:  #/build/erp-release   (mode: build)
*/

(function erp_release_queue_iife() {
  if (typeof window === "undefined") return;

  const useS = React.useState;
  const useE = React.useEffect;
  const useM = React.useMemo;

  // ── Compute the live blocker set for one ECO.
  //    state: PUMP_TENANT object
  //    eco:   one ECO object
  //    overrides: { supplier: Set<id>, fcp: bool, cost: bool, chain: bool }
  function computeBlockers(state, eco, overrides) {
    const out = [];
    overrides = overrides || { supplier: new Set(), fcp: false, cost: false, chain: false };

    // 1. Chain: every entry must be approved
    if (!overrides.chain) {
      const chain = eco.approval_chain || [];
      chain.forEach(function(c) {
        if (c.status !== "approved") {
          out.push({
            kind: "chain",
            reason: "awaiting " + c.role + " sign (" + (c.name || "—") + ")",
            severity: c.status === "rejected" ? "err" : "warn",
            actor_role: c.role,
            actor_name: c.name,
            overridable: false,  // chain rejections must be re-submitted, not overridden
          });
        }
      });
    }

    // 2. Suppliers: if any supplier with status="blocked" supplies an affected part
    const partsAffected = eco.parts_affected || [];
    const newParts = partsAffected.map(function(p) {
      // parts_affected.new looks like "20.07b NRV 1.5\" 86mm HS S.G.-400" OR just "20.07b"
      const m = /^([A-Za-z0-9.\-]+)/.exec(p.new || "");
      return m ? m[1] : null;
    }).filter(Boolean);
    const oldParts = partsAffected.map(function(p) {
      const m = /^([A-Za-z0-9.\-]+)/.exec(p.old || "");
      return m ? m[1] : null;
    }).filter(Boolean);
    const affectedNums = newParts.concat(oldParts);
    const suppliers = (state && Array.isArray(state.suppliers)) ? state.suppliers : [];
    suppliers.forEach(function(s) {
      if (s.status !== "blocked") return;
      if (overrides.supplier && overrides.supplier.has(s.id)) return;
      const hits = (s.parts_supplied || []).filter(function(pn) { return affectedNums.indexOf(pn) >= 0; });
      if (hits.length === 0) return;
      out.push({
        kind: "supplier",
        reason: "supplier blocked · " + s.name + (s.note ? " (" + s.note + ")" : "") + " supplies " + hits.join(", "),
        severity: "err",
        supplier_id: s.id,
        supplier_name: s.name,
        hits: hits,
        overridable: true,
      });
    });

    // 3. FCP target health
    if (!overrides.fcp && state.__fcpHealth === false) {
      out.push({
        kind: "fcp",
        reason: "FCP target unreachable",
        severity: "err",
        overridable: true,
      });
    }

    // 4. Cost delta > ₹500 without Plant Director sign.
    //    Reads the cosmopolitan shape (cost_delta_amount_minor + currency)
    //    when present, falling back to the legacy cost_delta_inr field. The
    //    threshold is held in major units (500 INR for Tritan); a non-INR
    //    ECO is still measured in its native currency at the same numeric
    //    threshold (sane default — the demo only exercises INR for Tritan).
    if (!overrides.cost) {
      var cdMajor = 0;
      var cdCcy = "INR";
      if (eco.cost_delta_amount_minor != null) {
        cdMajor = Number(eco.cost_delta_amount_minor) / 100;
        cdCcy = eco.cost_delta_currency || "INR";
      } else if (typeof eco.cost_delta_inr === "number") {
        cdMajor = eco.cost_delta_inr;
      }
      if (cdMajor > 500) {
        const chain = eco.approval_chain || [];
        const directorSigned = chain.some(function(c) { return c.role === "Plant Director" && c.status === "approved"; });
        if (!directorSigned) {
          var i18n = (typeof window !== "undefined") ? window.forgeI18n : null;
          var cdLabel = "₹" + cdMajor;
          var thresholdLabel = "₹500";
          if (i18n && typeof i18n.formatMoneyMinor === "function") {
            cdLabel = i18n.formatMoneyMinor(String(Math.round(cdMajor * 100)), cdCcy) || cdLabel;
            thresholdLabel = i18n.formatMoneyMinor("50000", cdCcy) || thresholdLabel;
          }
          out.push({
            kind: "cost",
            reason: "cost delta +" + cdLabel + "/u exceeds " + thresholdLabel + " without Plant Director sign-off",
            severity: "err",
            overridable: false,
          });
        }
      }
    }

    return out;
  }

  function ScreenERPReleaseQueue() {
    // useTenantState (when present) re-renders on every forge:tenant-change.
    // Otherwise fall back to useActiveTenant for non-tritan tenants.
    if (typeof window.useTenantState === "function") window.useTenantState();
    const activeTenant = (typeof window.useActiveTenant === "function")
      ? window.useActiveTenant()
      : (typeof window.getActiveTenant === "function" ? window.getActiveTenant() : null);

    const P = window.A56_PAPER || {
      bg: "#fefefe", bg2: "#f5f5f5", bg3: "#e8e8e8",
      ink: "#111", ink2: "#444", ink3: "#666", ink4: "#999",
      line: "#ccc", lineSoft: "#e0e0e0",
      blue: "#1e6fd9", green: "#1f8a5b", red: "#c0392b", amber: "#b07c00",
      blueSoft: "#e6f0fb", greenSoft: "#e6f6ee", redSoft: "#fbe9e7", amberSoft: "#fdf3d8",
    };
    const FONT_UI = window.A56_FONT_UI || "var(--font-ui)";
    const FONT_MONO = window.A56_FONT_MONO || "var(--font-mono)";
    const FONT_SERIF = window.A56_FONT_SERIF || "var(--font-serif)";

    const A56Btn = window.A56Btn || (function(props) {
      return React.createElement("button", { type: "button", onClick: props.onClick, style: { padding: "6px 12px" } }, props.children);
    });
    const A56Pill = window.A56Pill || (function(props) {
      return React.createElement("span", null, props.children);
    });
    const SwitchUserPill = window.SwitchUserPill;

    // Resolve tenant via the multi-tenant resolver (was hard-coded to
    // window.PUMP_TENANT, which left Aetherion + Mittelstand with an empty
    // queue). Fall back to PUMP_TENANT for legacy callers/tests.
    const tenant = activeTenant || window.PUMP_TENANT;

    // Current actor must come from the active tenant's people, not a
    // hardcoded Tritan fallback. If window.__forgeCurrentUser doesn't match
    // a person in the active tenant (e.g. fresh Aetherion session), pick a
    // sensible default from the tenant roster instead of leaking Ishaan R.
    const people = (tenant && Array.isArray(tenant.people)) ? tenant.people : [];
    const fromGlobal = window.__forgeCurrentUser;
    let cur;
    if (fromGlobal && people.find(function(p) { return p.name === fromGlobal.name; })) {
      cur = fromGlobal;
    } else if (people.length > 0) {
      const defaultPerson = people.find(function(p) { return /compliance|quality|design|lead/i.test(p.role); }) || people[0];
      cur = { id: defaultPerson.id, name: defaultPerson.name, role: defaultPerson.role };
    } else {
      cur = { id: null, name: "—", role: "—" };
    }
    const isPlantDirector = (cur.role === "Plant Director");

    const allEcos = (tenant && Array.isArray(tenant.eco)) ? tenant.eco : [];
    // Queue scope per spec: status in [approved, released]
    const queue = allEcos.filter(function(e) { return e.status === "approved" || e.status === "released" || e.status === "applied"; });

    const [overridesByEco, setOverridesByEco] = useS({}); // ecoId -> { supplier: Set, fcp, cost, chain }
    const [fcpHealthy, setFcpHealthy] = useS(true);
    const [provenanceFor, setProvenanceFor] = useS(null); // ecoId or null
    const [flash, setFlash] = useS(null);
    const [pendingRelease, setPendingRelease] = useS(null); // ecoId mid-release
    // Wave 8D: when a release 403s, remember which ECO so we can render an
    // inline "Override · Plant Director sign" CTA + open the escalation
    // modal. Cleared on retry success or when the user dismisses the modal.
    const [denied403ByEco, setDenied403ByEco] = useS({}); // ecoId -> true
    const [overrideModalFor, setOverrideModalFor] = useS(null); // ecoId or null

    // Plant Director lookup is tenant-scoped — this screen registers for all
    // three tenants. If a tenant has no Plant Director on roster, the
    // Override CTA is suppressed (there's no one to escalate to).
    const plantDirector = (tenant && Array.isArray(tenant.people))
      ? tenant.people.find(function(p) { return p.role === "Plant Director"; })
      : null;

    // Probe FCP health on mount, then keep state.__fcpHealth in sync.
    useE(function() {
      if (!window.forgeApi || !window.forgeApi.fcp || typeof window.forgeApi.fcp.health !== "function") return;
      let cancelled = false;
      window.forgeApi.fcp.health().then(function(ok) {
        if (cancelled) return;
        setFcpHealthy(!!ok);
        if (tenant) tenant.__fcpHealth = !!ok;
      });
      return function() { cancelled = true; };
    }, []);

    function getOverrides(ecoId) {
      const o = overridesByEco[ecoId];
      return o || { supplier: new Set(), fcp: false, cost: false, chain: false };
    }

    function toggleSupplierOverride(ecoId, supplierId) {
      if (!isPlantDirector) {
        setFlash({ kind: "err", msg: "Only the Plant Director can override a supplier block. Switch user to Meera S." });
        return;
      }
      setOverridesByEco(function(prev) {
        const cur = prev[ecoId] || { supplier: new Set(), fcp: false, cost: false, chain: false };
        const sup = new Set(cur.supplier);
        if (sup.has(supplierId)) sup.delete(supplierId);
        else sup.add(supplierId);
        return Object.assign({}, prev, { [ecoId]: Object.assign({}, cur, { supplier: sup }) });
      });
    }

    function toggleFcpOverride(ecoId) {
      if (!isPlantDirector) {
        setFlash({ kind: "err", msg: "Only the Plant Director can override an FCP-unreachable block." });
        return;
      }
      setOverridesByEco(function(prev) {
        const cur = prev[ecoId] || { supplier: new Set(), fcp: false, cost: false, chain: false };
        return Object.assign({}, prev, { [ecoId]: Object.assign({}, cur, { fcp: !cur.fcp }) });
      });
    }

    function toggleFcpHealth() {
      // demo-only switch — flip the stub to show the FCP-blocker pathway
      const next = !fcpHealthy;
      setFcpHealthy(next);
      if (tenant) tenant.__fcpHealth = next;
      window.forgeApi.fcp.health = function() { return Promise.resolve(next); };
      window.dispatchEvent(new CustomEvent("forge:tenant-change", { detail: { reason: "fcp-toggle" } }));
    }

    // Build the envelope fresh from the *current* window.__forgeCurrentUser
    // so a retry under a different actor (e.g. Meera after a 403) signs the
    // provenance receipt with the right name & role. Reading `cur` from the
    // render closure would leak Priya into Meera's signed envelope.
    function buildEnvelope(ecoId, o) {
      const actor = (typeof window !== "undefined" && window.__forgeCurrentUser) || cur;
      const actorName = (actor && actor.name) || cur.name;
      const actorRole = (actor && actor.role) || cur.role;
      const ts = new Date().toISOString();
      const overridesList = [];
      o.supplier.forEach(function(supId) {
        const sup = (tenant.suppliers || []).find(function(s) { return s.id === supId; });
        overridesList.push({
          kind: "supplier",
          supplier_id: supId,
          supplier_name: sup ? sup.name : supId,
          actor: actorName,
          actor_role: actorRole,
          ts: ts,
        });
      });
      if (o.fcp) overridesList.push({ kind: "fcp", actor: actorName, actor_role: actorRole, ts: ts });
      if (o.cost) overridesList.push({ kind: "cost", actor: actorName, actor_role: actorRole, ts: ts });
      return {
        actor: actorName,
        fcp_envelope_id: "FCP-" + ecoId + "-" + ts.slice(0, 10).replace(/-/g, ""),
        overrides: overridesList,
      };
    }

    function fireRelease(ecoId, opts) {
      const eco = allEcos.find(function(e) { return e.id === ecoId; });
      if (!eco) return;
      const o = getOverrides(ecoId);
      const blockers = computeBlockers(tenant, eco, o);
      if (blockers.length > 0) {
        setFlash({ kind: "err", msg: "Cannot release · " + blockers.length + " active blocker(s)." });
        return;
      }
      const envelope = buildEnvelope(ecoId, o);
      setPendingRelease(ecoId);
      window.forgeApi.eco.release(ecoId, envelope)
        .then(function(updated) {
          setPendingRelease(null);
          // Clear any prior 403 denial — release succeeded.
          setDenied403ByEco(function(prev) {
            if (!prev[ecoId]) return prev;
            const next = Object.assign({}, prev);
            delete next[ecoId];
            return next;
          });
          setFlash({ kind: "ok", msg: ecoId + " released to ERP. Envelope " + (updated.provenance && updated.provenance.fcp_envelope_id) });
        })
        .catch(function(err) {
          setPendingRelease(null);
          const status = err && err.status;
          if (status === 403) {
            // Permission denied — keep ECO in-state and surface the override path.
            setDenied403ByEco(function(prev) {
              return Object.assign({}, prev, { [ecoId]: true });
            });
            const directorName = plantDirector ? plantDirector.name : "Plant Director";
            const baseMsg = (err && err.message) ? err.message : "permission denied";
            const msg = "Permission denied — need " + directorName + " override (" + baseMsg + ")";
            setFlash({ kind: "err", msg: msg });
            return;
          }
          setFlash({ kind: "err", msg: "Release failed: " + (err && err.message || err) });
        });
    }

    function onRelease(ecoId) {
      if (pendingRelease) return;
      fireRelease(ecoId, {});
    }

    // Wave 8D — Path B retry: switch active user to the Plant Director
    // (mirrors SwitchUserPill.pick exactly so api.js#ensureAuth swaps the
    // server session cookie), then auto-fire the release with a fresh
    // envelope signed by the director.
    function onOverrideAndRelease(ecoId) {
      if (pendingRelease) return;
      if (!plantDirector) {
        setFlash({ kind: "err", msg: "No Plant Director on roster — cannot escalate." });
        setOverrideModalFor(null);
        return;
      }
      const p = { id: plantDirector.id, name: plantDirector.name, role: plantDirector.role };
      // Optimistic UI lock so the user can't fire twice mid-switch.
      setPendingRelease(ecoId);
      // Step 1: update the in-memory actor.
      window.__forgeCurrentUser = p;
      // Step 2: invalidate caches via tenant-change broadcast (api.js listens).
      try {
        window.dispatchEvent(new CustomEvent("forge:tenant-change", { detail: { reason: "user-switch", actor: p.name } }));
      } catch (e) { /* ignore */ }
      // Step 3: re-auth so the server cookie carries Meera's session BEFORE
      // we hit the permission-gated release endpoint. Without this, the next
      // release call would still 403 under Priya's cookie.
      const ensure = (window.forgeApi && typeof window.forgeApi.ensureAuth === "function")
        ? window.forgeApi.ensureAuth()
        : Promise.resolve();
      ensure
        .catch(function(e) {
          // Non-fatal — log and continue; fireRelease will surface any real
          // auth error via its own catch.
          if (typeof console !== "undefined") console.warn("override re-auth failed:", e && e.message);
        })
        .then(function() {
          // Step 4: notify downstream actor-switched listeners.
          try {
            window.dispatchEvent(new CustomEvent("forge:user-switched", { detail: { user: p } }));
          } catch (e) { /* ignore */ }
          // Step 5: close the modal and fire the release with a fresh
          // envelope (which reads the new __forgeCurrentUser, so Meera signs).
          setOverrideModalFor(null);
          // Release the pendingRelease lock so fireRelease's own setPendingRelease takes over.
          setPendingRelease(null);
          fireRelease(ecoId, {});
        });
    }

    // ── styles
    const sH = { fontFamily: FONT_SERIF, fontSize: 22, color: P.ink };
    const sCol = { padding: 14, display: "grid", gridTemplateRows: "auto 1fr", gap: 14, minHeight: 0, height: "100%" };
    const sCard = { background: P.bg2, border: "1px solid " + P.line, borderRadius: 8, padding: 16, overflow: "auto" };
    const sLabel = { fontFamily: FONT_MONO, fontSize: 12, letterSpacing: "0.10em", textTransform: "uppercase", color: P.ink3 };

    // ── render
    const provEco = provenanceFor ? allEcos.find(function(e) { return e.id === provenanceFor; }) : null;

    return (
      <div style={sCol}>
        {/* top strip */}
        <div style={Object.assign({}, sCard, { padding: "10px 14px", display: "flex", justifyContent: "space-between", alignItems: "center", gap: 14 })}>
          <div>
            <div style={sH}>ERP release queue</div>
            <div style={{ fontFamily: FONT_UI, fontSize: 12, color: P.ink3, marginTop: 2 }}>
              {queue.length} ECO{queue.length === 1 ? "" : "s"} ready or released · live blockers computed against suppliers + FCP + cost rules
            </div>
          </div>
          <div style={{ display: "flex", gap: 10, alignItems: "center", flexWrap: "wrap" }}>
            {SwitchUserPill && React.createElement(SwitchUserPill)}
            <div style={{ fontFamily: FONT_MONO, fontSize: 12, color: fcpHealthy ? P.green : P.red, display: "flex", alignItems: "center", gap: 6 }}>
              FCP: <b>{fcpHealthy ? "healthy" : "unreachable"}</b>
              <A56Btn small onClick={toggleFcpHealth}>{fcpHealthy ? "simulate FCP outage" : "restore FCP"}</A56Btn>
            </div>
          </div>
        </div>

        {/* queue */}
        <div style={Object.assign({}, sCard, { padding: 0 })}>
          {queue.length === 0 && (
            <div style={{ padding: 28, fontFamily: FONT_UI, fontSize: 13, color: P.ink3, textAlign: "center" }}>
              No ECOs in queue. ECOs land here once every approval chain entry is signed off.
            </div>
          )}
          {queue.map(function(eco) {
            const o = getOverrides(eco.id);
            const blockers = computeBlockers(tenant, eco, o);
            const isReleased = (eco.status === "released" || eco.status === "applied");
            const canRelease = blockers.length === 0 && !isReleased;

            return (
              <div key={eco.id} style={{ borderBottom: "1px solid " + P.lineSoft, padding: 14 }}>
                <div style={{ display: "grid", gridTemplateColumns: "1fr auto", gap: 12, alignItems: "start" }}>
                  <div>
                    <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
                      <span style={{ fontFamily: FONT_MONO, fontSize: 13, color: P.ink2 }}>{eco.id}</span>
                      <A56Pill tone={
                        eco.status === "released" || eco.status === "applied" ? "ok"
                          : blockers.length === 0 ? "ok"
                          : "err"
                      }>{eco.status}</A56Pill>
                      <span style={{ fontFamily: FONT_UI, fontSize: 14, color: P.ink }}>{eco.title}</span>
                    </div>
                    <div style={{ display: "flex", gap: 14, marginTop: 6, fontFamily: FONT_MONO, fontSize: 12, color: P.ink3 }}>
                      <span>raised by <span style={{ color: P.ink2 }}>{eco.originator || "—"}</span> · {(window.forgeI18n && window.forgeI18n.formatDate && eco.date_raised && /^\d{4}-\d{2}-\d{2}$/.test(eco.date_raised) && window.forgeI18n.formatDate(eco.date_raised)) || eco.date_raised || "—"}</span>
                      {(function () {
                        // PR-10 cosmopolitan: ECOs carry cost_delta_amount_minor (decimal
                        // string of minor units) and cost_delta_currency. Some fixtures
                        // still use cost_delta_inr (Number) — coerce that to minor units
                        // (INR has 100 minor per major) so the formatter sees one shape.
                        const cur = eco.cost_delta_currency
                          || (eco.cost_delta_inr != null ? "INR" : null);
                        const minor = (eco.cost_delta_amount_minor != null)
                          ? eco.cost_delta_amount_minor
                          : (eco.cost_delta_inr != null
                              ? String(Math.round(Number(eco.cost_delta_inr) * 100))
                              : null);
                        if (minor == null || cur == null) {
                          return React.createElement("span", null, "cost Δ —");
                        }
                        // Sign handling. minor is a decimal string ("-280000", "19000").
                        const negative = String(minor).startsWith("-");
                        const absMinor = negative ? String(minor).slice(1) : String(minor);
                        const major = Number(absMinor) / 100;
                        // Tone thresholds carry over from the legacy ₹ logic (>500/unit
                        // amber, >0 warn-amber, <=0 green). Compare on major value with
                        // sign baked in.
                        const signed = negative ? -major : major;
                        const tone = signed > 500 ? P.red : signed > 0 ? P.amber : P.green;
                        // Prefer forgeI18n if available; fall back to currency + major.
                        let formatted;
                        if (window.forgeI18n && typeof window.forgeI18n.formatMoneyMinor === "function") {
                          formatted = window.forgeI18n.formatMoneyMinor(minor, cur);
                        } else {
                          formatted = (negative ? "-" : "") + cur + " " + major.toFixed(2);
                        }
                        const sign = negative ? "" : (signed > 0 ? "+" : "");
                        return React.createElement(
                          "span",
                          null,
                          "cost Δ ",
                          React.createElement("span", { style: { color: tone } }, sign + formatted + "/u")
                        );
                      })()}
                      <span>{(eco.parts_affected || []).length} part(s) affected</span>
                    </div>
                  </div>
                  <div style={{ display: "flex", gap: 6 }}>
                    {!isReleased && (
                      <A56Btn variant={canRelease ? "primary" : "ghost"} onClick={function() { onRelease(eco.id); }}>
                        {pendingRelease === eco.id ? "Releasing…" : "Release to ERP"}
                      </A56Btn>
                    )}
                    {/* Wave 8D — inline override CTA appears after a 403 from
                        the server. Suppressed if the active tenant has no
                        Plant Director on roster (nothing to escalate to). */}
                    {!isReleased && denied403ByEco[eco.id] && plantDirector && (
                      <A56Btn
                        variant="primary"
                        onClick={function() { setOverrideModalFor(eco.id); }}
                      >
                        Override · Plant Director sign
                      </A56Btn>
                    )}
                    {eco.provenance && (
                      <A56Btn onClick={function() { setProvenanceFor(eco.id); }}>View provenance receipt</A56Btn>
                    )}
                  </div>
                </div>

                {/* blockers list */}
                {blockers.length > 0 && (
                  <div style={{ marginTop: 10, padding: 10, background: P.redSoft, border: "1px solid " + P.red, borderRadius: 4 }}>
                    <div style={{ fontFamily: FONT_MONO, fontSize: 12, color: P.red, marginBottom: 6 }}>
                      {blockers.length} active blocker{blockers.length === 1 ? "" : "s"} — release is disabled
                    </div>
                    {blockers.map(function(b, i) {
                      return (
                        <div key={i} style={{ display: "flex", justifyContent: "space-between", alignItems: "center", gap: 10, padding: "4px 0", borderTop: i === 0 ? 0 : "1px dashed " + P.line }}>
                          <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
                            <span style={{ fontFamily: FONT_MONO, fontSize: 12, color: P.red, textTransform: "uppercase", letterSpacing: "0.04em" }}>{b.kind}</span>
                            <span style={{ fontFamily: FONT_UI, fontSize: 13, color: P.ink2 }}>{b.reason}</span>
                          </div>
                          {b.overridable && b.kind === "supplier" && (
                            <A56Btn small onClick={function() { toggleSupplierOverride(eco.id, b.supplier_id); }}>
                              {isPlantDirector ? "Override (Plant Director)" : "Requires Plant Director"}
                            </A56Btn>
                          )}
                          {b.overridable && b.kind === "fcp" && (
                            <A56Btn small onClick={function() { toggleFcpOverride(eco.id); }}>
                              {isPlantDirector ? "Override (Plant Director)" : "Requires Plant Director"}
                            </A56Btn>
                          )}
                        </div>
                      );
                    })}
                  </div>
                )}

                {/* active overrides (only when something is overridden but not yet released) */}
                {!isReleased && (o.supplier.size > 0 || o.fcp || o.cost) && (
                  <div style={{ marginTop: 8, padding: 10, background: P.amberSoft, border: "1px solid " + P.amber, borderRadius: 4 }}>
                    <div style={{ fontFamily: FONT_MONO, fontSize: 12, color: P.amber, marginBottom: 4 }}>
                      Active overrides — will be recorded on the provenance receipt
                    </div>
                    {Array.from(o.supplier).map(function(supId) {
                      const sup = (tenant.suppliers || []).find(function(s) { return s.id === supId; });
                      return <div key={supId} style={{ fontFamily: FONT_UI, fontSize: 12, color: P.ink2 }}>· supplier override · {sup ? sup.name : supId} (by {cur.name})</div>;
                    })}
                    {o.fcp && <div style={{ fontFamily: FONT_UI, fontSize: 12, color: P.ink2 }}>· FCP-unreachable override (by {cur.name})</div>}
                  </div>
                )}

                {/* released receipt summary */}
                {isReleased && eco.provenance && (
                  <div style={{ marginTop: 8, padding: 10, background: P.greenSoft, border: "1px solid " + P.green, borderRadius: 4 }}>
                    <div style={{ fontFamily: FONT_MONO, fontSize: 12, color: P.green }}>
                      released to ERP · envelope <b>{eco.provenance.fcp_envelope_id}</b> · by {eco.provenance.released_by} at {eco.provenance.released_ts}
                    </div>
                    {eco.provenance.overrides && eco.provenance.overrides.length > 0 && (
                      <div style={{ marginTop: 4, fontFamily: FONT_UI, fontSize: 12, color: P.ink2 }}>
                        Overrides recorded: {eco.provenance.overrides.map(function(ov) { return ov.kind + (ov.supplier_name ? " (" + ov.supplier_name + ")" : ""); }).join(", ")}
                      </div>
                    )}
                  </div>
                )}
              </div>
            );
          })}
        </div>

        {flash && (
          <div style={{
            position: "fixed", top: 64, right: 24, zIndex: 50,
            padding: "10px 14px", borderRadius: 8,
            background: flash.kind === "ok" ? P.greenSoft : flash.kind === "err" ? P.redSoft : P.blueSoft,
            border: "1px solid " + (flash.kind === "ok" ? P.green : flash.kind === "err" ? P.red : P.blue),
            color: flash.kind === "ok" ? P.green : flash.kind === "err" ? P.red : P.blue,
            fontFamily: FONT_UI, fontSize: 13, boxShadow: "0 6px 18px rgba(0,0,0,0.08)",
            display: "flex", alignItems: "center", gap: 10,
          }}>
            <span>{flash.msg}</span>
            <button onClick={function() { setFlash(null); }} style={{ border: 0, background: "transparent", color: "inherit", cursor: "pointer", fontSize: 16 }}>×</button>
          </div>
        )}

        {provEco && provEco.provenance && (
          <div onClick={function() { setProvenanceFor(null); }} style={{
            position: "fixed", inset: 0, background: "rgba(0,0,0,0.45)", zIndex: 60,
            display: "flex", alignItems: "center", justifyContent: "center",
          }}>
            <div onClick={function(ev) { ev.stopPropagation(); }} style={{
              width: 720, maxHeight: "82vh", overflow: "auto",
              background: P.bg, border: "1px solid " + P.ink, borderRadius: 8, padding: 18,
            }}>
              <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 8 }}>
                <div style={sH}>Provenance receipt · {provEco.id}</div>
                <A56Btn onClick={function() { setProvenanceFor(null); }}>Close</A56Btn>
              </div>
              <pre style={{ margin: 0, padding: 12, background: P.bg2, border: "1px solid " + P.lineSoft, borderRadius: 4, fontFamily: FONT_MONO, fontSize: 12, color: P.ink2, whiteSpace: "pre-wrap", wordBreak: "break-all" }}>
{JSON.stringify(provEco.provenance, null, 2)}
              </pre>
            </div>
          </div>
        )}

        {/* Wave 8D — Plant Director override modal. Opens after a 403 + the
            inline "Override" CTA. One button switches the active user to the
            director (mirrors SwitchUserPill.pick) and auto-fires the release
            with a fresh envelope signed by the director. */}
        {overrideModalFor && plantDirector && (
          <div onClick={function() { if (pendingRelease !== overrideModalFor) setOverrideModalFor(null); }} style={{
            position: "fixed", inset: 0, background: "rgba(0,0,0,0.45)", zIndex: 60,
            display: "flex", alignItems: "center", justifyContent: "center",
          }}>
            <div onClick={function(ev) { ev.stopPropagation(); }} style={{
              width: 480, maxHeight: "82vh", overflow: "auto",
              background: P.bg, border: "1px solid " + P.ink, borderRadius: 8, padding: 18,
            }}>
              <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 12 }}>
                <div style={sH}>Plant Director sign required</div>
                <A56Btn onClick={function() { if (pendingRelease !== overrideModalFor) setOverrideModalFor(null); }}>Close</A56Btn>
              </div>
              <div style={{ fontFamily: FONT_UI, fontSize: 13, color: P.ink2, lineHeight: 1.5, marginBottom: 14 }}>
                <div style={{ marginBottom: 8 }}>
                  Releasing <b style={{ fontFamily: FONT_MONO }}>{overrideModalFor}</b> to ERP is restricted to the Plant Director (<b>eco.*</b> permission).
                </div>
                <div style={{ marginBottom: 8 }}>
                  You are currently signed in as <b>{cur.name}</b> ({cur.role}), which only carries <b>eco.submit</b>. To proceed, the release must be signed by:
                </div>
                <div style={{
                  padding: "10px 12px", background: P.bg2, border: "1px solid " + P.lineSoft,
                  borderRadius: 4, fontFamily: FONT_MONO, fontSize: 12, color: P.ink,
                }}>
                  {plantDirector.name} · {plantDirector.role}
                </div>
              </div>
              <div style={{ fontFamily: FONT_MONO, fontSize: 11, color: P.ink3, marginBottom: 12, letterSpacing: "0.04em" }}>
                The override is recorded on the FCP envelope so the provenance receipt names {plantDirector.name.split(" ")[0]} as the signer, not {cur.name.split(" ")[0]}.
              </div>
              <div style={{ display: "flex", gap: 8, justifyContent: "flex-end" }}>
                <A56Btn onClick={function() { if (pendingRelease !== overrideModalFor) setOverrideModalFor(null); }}>
                  Cancel
                </A56Btn>
                <A56Btn
                  variant="primary"
                  onClick={function() { onOverrideAndRelease(overrideModalFor); }}
                >
                  {pendingRelease === overrideModalFor
                    ? "Switching & releasing…"
                    : "Switch to " + plantDirector.name + " and release"}
                </A56Btn>
              </div>
            </div>
          </div>
        )}
      </div>
    );
  }

  // ── route registration (all 3 tenants — was tritan-only)
  const releaseRoute = {
    path: "/build/erp-release",
    mode: "build",
    title: "ERP release queue",
    renderer: function() { return React.createElement(ScreenERPReleaseQueue); },
  };
  if (typeof registerPumpRoute === "function") {
    registerPumpRoute(releaseRoute);
  }
  if (typeof registerAetherionRoute === "function") {
    registerAetherionRoute(releaseRoute);
  }
  if (typeof registerMittelstandRoute === "function") {
    registerMittelstandRoute(releaseRoute);
  }

  // ── global exposure
  window.ScreenERPReleaseQueue = ScreenERPReleaseQueue;
  window.computeEcoBlockers = computeBlockers;  // for testing / integration probes
})();
