// The Requester's workspace (WP-U12).
//
// My deals: this person's own engagements, and nothing of anyone else's.
//
// THREE RULES:
//
//   1. THE PIPELINE RAIL IS PER DEAL. v3 drew it as one global state, so two
//      deals at two stages shared a rail and the screen could only be telling
//      the truth about one of them.
//
//   2. REQUEST IS THE ONLY PATH PAST A BLOCKING FINDING. There is no
//      acknowledge, no override button, no "proceed anyway". The v3 button is
//      retired by ADR-0008 and this is the screen where undoing that would be
//      most tempting — "just for the demo flow" is exactly how it would come
//      back.
//
//   3. WHAT A REQUEST DOES NOT PROMISE IS SAID PLAINLY. Asking is not being
//      allowed. The gate stays shut while a request is socialising and while
//      its window runs, and the screen must not imply otherwise for a moment.

const { useState, useRef } = React;

// ── Asking for an override ───────────────────────────────────────────────
// THE REFERENCE A FINDING IS KNOWN BY, IN ONE PLACE ON THIS SIDE.
//
// It must be the same string backend/doorway/executions.py builds, because the
// gate at signature matches an approval against it. If the two ever disagree,
// an approval Legal genuinely gave stops covering the finding it was given for
// — and the failure lands at the signature, which is the worst possible moment
// and the one act that cannot be undone. Matching there is fail-closed on
// purpose, so a mismatch refuses rather than passing.
function findingRef(f) { return `${f.rule_id}@v${f.rule_version}`; }

function RequestOverride({ deal, runId, blocking, onDone, onError, onCancel }) {
  const [justification, setJustification] = useState('');
  const [pressure, setPressure] = useState('');
  const [picked, setPicked] = useState({});
  const [busy, setBusy] = useState(false);

  // TICKED, NOT TYPED — and that changed when the validate stage arrived.
  //
  // These were typed by hand against a placeholder, because there was no list
  // of findings to pick from and inventing one would have been a screen
  // claiming something the system could not do. There is a real list now: these
  // are the findings that actually blocked this run, from the run's own record.
  //
  // Typing them is now the dangerous option rather than the honest one. A
  // reference typed by hand is a reference that can be typed WRONG, and a
  // wrong one is not rejected at the time — it is accepted, decided by Legal,
  // and then fails to cover anything at signature.
  const chosen = (blocking ?? []).filter((f) => picked[findingRef(f)]);

  // The floor the schema enforces, surfaced before the refusal rather than
  // after it. Twenty characters is not a quality bar — nothing here can judge
  // whether a reason is any good, and that is a content judgement belonging to
  // whoever reads it. It stops "n/a".
  const longEnough = justification.trim().length >= 20;

  return (
    <div className="panel p-4 mt-4">
      <PanelHead
        title="Ask for an override"
        sub="You are asking. Legal decides, after the people who should know have been told."
      />

      <div className="panel-2 p-3 mt-1">
        <div className="tag" style={{ color: 'var(--accent-2)' }}>what this does, and does not</div>
        <div className="text-[12.5px] mt-1.5" style={{ color: 'var(--mute)', lineHeight: 1.7 }}>
          This <strong>opens no gate</strong>. It records that you asked, tells
          the people who should know, and waits out a review window so that
          nobody discovers the override at signature. A Legal reviewer then
          decides <strong>each finding on its own</strong> — accepting one does
          not accept the others.
          <br /><br />
          Until a finding is approved, it still blocks.
        </div>
      </div>

      <div className="mt-4">
        <div className="section-label mb-2">Which findings</div>
        {(blocking ?? []).length === 0 ? (
          <div className="caption" style={{ lineHeight: 1.7 }}>
            Nothing on this run is blocking. There is nothing to ask about.
          </div>
        ) : (blocking ?? []).map((f, i) => {
          const ref = findingRef(f);
          return (
            <label className="flex gap-3 items-start py-2" key={ref}
                   style={{ borderTop: '1px solid var(--line)', cursor: 'pointer' }}>
              <input type="checkbox" className="mt-1" checked={!!picked[ref]}
                     data-testid={`finding-pick-${i}`}
                     onChange={(e) => setPicked({ ...picked, [ref]: e.target.checked })} />
              <div className="min-w-0">
                <span className="font-mono" style={{ fontSize: 11, color: 'var(--mute-2)' }}>
                  {ref}
                </span>
                <span className="text-[13px] ml-2">{f.title}</span>
                <div className="caption mt-0.5">{f.detail}</div>
              </div>
            </label>
          );
        })}
        <div className="caption mt-2" style={{ lineHeight: 1.6 }}>
          These are the findings that actually blocked this run, taken from its
          own record. Each one you tick is decided on its own — accepting one
          does not accept the others.
        </div>
      </div>

      <div className="mt-4">
        <label className="section-label">Why (required)</label>
        <textarea aria-label="Why (required)" className="mt-1.5 w-full" rows={3}
                  data-testid="justification"
                  placeholder="What the business reason is, in your own words."
                  value={justification} onChange={(e) => setJustification(e.target.value)} />
        <div className="caption mt-1">
          {longEnough
            ? 'This goes on the permanent record and is what the reviewer reads.'
            : `A real reason, not "n/a" — ${Math.max(0, 20 - justification.trim().length)} more characters.`}
        </div>
      </div>

      <div className="mt-3">
        <label className="section-label">Commercial pressure being cited (optional)</label>
        <input aria-label="Commercial pressure being cited (optional)" className="mt-1.5 w-full" placeholder="The counterparty will not sign before quarter end"
               value={pressure} onChange={(e) => setPressure(e.target.value)} />
        {/* Kept separate from the justification on purpose: "they threatened to
            walk" and "we accept this risk because X" are different claims, and
            one collapsed into the other lets pressure stand in for reasoning. */}
        <div className="caption mt-1">
          Recorded separately from your reason, because pressure and reasoning
          are different things.
        </div>
      </div>

      <div className="flex gap-2 mt-4">
        <button className="btn" onClick={onCancel}>cancel</button>
        <ActButton
          className="btn btn-primary"
          disabled={busy || !longEnough || chosen.length === 0}
          data-testid="submit-override-request"
          onClick={async () => {
            setBusy(true); onError(null);
            const r = await API.requestOverride({
              run_id: runId,
              justification: justification.trim(),
              commercial_pressure: pressure.trim() || null,
              findings: chosen.map((f) => ({
                finding_ref: findingRef(f),
                severity: f.severity,
                summary: f.title,
              })),
            });
            if (!r.ok) { setBusy(false); onError(r.reason); return; }

            // Socialising is a SECOND act, and it is performed here rather than
            // folded into the first — but its refusal is surfaced rather than
            // swallowed. If nobody would be told, the request still exists and
            // the screen has to say that it has not been socialised, because a
            // request nobody was told about cannot be decided.
            const s = await API.socialiseOverride({
              request_id: r.rows[0].request_id,
            });
            setBusy(false);
            if (!s.ok) {
              onError(`Your request was recorded, but nobody could be told: ${s.reason}`);
              onDone();
              return;
            }
            onDone();
          }}
        >✓ ask for an override</ActButton>
      </div>
    </div>
  );
}

// ── Composing a manifest, and assembling the contract ────────────────────
//
// The manifest is the ONE thing that crosses from a language model into the
// deterministic core, and this pane is where a person composes one by hand.
// That is not a lesser path: the trust boundary accepts a composed manifest
// from anywhere and checks it identically, so what is typed here is checked by
// exactly the same rule as what a model would produce.
//
// PRE-FLIGHT, THEN ASSEMBLE, and they are two separate acts on purpose. The
// pre-flight says whether the categories are ones the library defines; it
// records nothing and produces no contract. Assembling records a run that
// cannot afterwards be edited or removed.
//
// TWO ENTRANCES, ONE COMPONENT (AI-2). This is reached from an open deal with
// nothing filled in, and from the intake walk with the classifier's confirmed
// proposal already in it. The second entrance passes `start` and nothing else
// changes: same pre-flight, same act, same refusals. A second copy of this
// panel behind the intake tab is exactly how two paths to one recorded fact
// start disagreeing about what they recorded.
function AssembleContract({ deal, categories, onAssembled, onError, start }) {
  const [source, setSource] = useState(start?.source || 'manual');
  const [risks, setRisks] = useState(start?.risks?.length
    ? start.risks
    : [{ category: '', severity: 'Standard', justification: '' }]);
  const [checked, setChecked] = useState(null);
  const [busy, setBusy] = useState(false);

  const ready = risks.filter((r) => r.category.trim());

  const body = () => ({
    agreement_id: deal.agreement_id,       // FROM THE OPEN DEAL, never typed.
    vendor: deal.counterparty,
    source,
    risks: ready.map((r) => ({
      category: r.category.trim(),
      severity: r.severity,
      justification: r.justification.trim(),
    })),
  });

  return (
    <div className="panel p-4 mt-6">
      <PanelHead
        title="Assemble a contract"
        sub="Check it first. Checking records nothing; assembling records a run that cannot be edited."
      />

      <div className="mt-3">
        <div className="section-label mb-2">Risks in scope</div>
        {risks.map((r, i) => (
          <div className="flex gap-2 mb-2 items-end" key={i}>
            <div style={{ width: 200 }}>
              <label className="caption">Category</label>
              <select aria-label="Category" className="mt-1 w-full" style={{ padding: '5px 8px' }}
                      data-testid={`risk-category-${i}`}
                      value={r.category}
                      onChange={(e) => setRisks(risks.map((x, j) =>
                        j === i ? { ...x, category: e.target.value } : x))}>
                <option value="">choose…</option>
                {/* THE LIBRARY'S OWN CATEGORIES, read from the library screen's
                    endpoint. Not a list written here: a category this screen
                    invented would be refused by the trust boundary, and the
                    person would be told the model hallucinated when in fact
                    this page did. */}
                {categories.map((c) => <option key={c} value={c}>{c}</option>)}
              </select>
            </div>
            <div className="flex-1">
              <label className="caption">Why it is in scope</label>
              <input aria-label="Why it is in scope" className="mt-1 w-full" style={{ padding: '4px 8px' }}
                     placeholder="The counterparty processes EU personal data"
                     value={r.justification}
                     onChange={(e) => setRisks(risks.map((x, j) =>
                       j === i ? { ...x, justification: e.target.value } : x))} />
            </div>
            <select className="font-mono" style={{ padding: '5px 8px' }} value={r.severity}
                    aria-label="Severity of this risk"
                    onChange={(e) => setRisks(risks.map((x, j) =>
                      j === i ? { ...x, severity: e.target.value } : x))}>
              <option value="Standard">Standard</option>
              <option value="High">High</option>
            </select>
            {risks.length > 1 && (
              <button className="btn btn-sm"
                      onClick={() => setRisks(risks.filter((_, j) => j !== i))}>−</button>
            )}
          </div>
        ))}
        <button className="btn btn-sm"
                onClick={() => setRisks([...risks, { category: '', severity: 'Standard', justification: '' }])}>
          + another risk
        </button>
      </div>

      <div className="flex gap-2 items-end mt-4">
        <div style={{ width: 160 }}>
          <label className="section-label">Where it came from</label>
          <select aria-label="Where it came from" className="mt-1.5 w-full font-mono" style={{ padding: '5px 8px' }}
                  data-testid="manifest-source"
                  value={source} onChange={(e) => setSource(e.target.value)}>
            <option value="manual">manual</option>
            <option value="llm">llm</option>
            <option value="fallback">fallback</option>
          </select>
        </div>
        <ActButton className="btn" disabled={busy || ready.length === 0}
                data-testid="check-manifest"
                onClick={async () => {
                  setBusy(true); onError(null); setChecked(null);
                  const r = await API.checkManifest(body());
                  setBusy(false);
                  setChecked(r);
                  if (!r.ok) onError(null);   // shown in place, below
                }}>
          check it
        </ActButton>
        {/* ASSEMBLING IS ONLY REACHABLE ONCE THE CHECK HAS PASSED. Not because
            the endpoint would let a bad manifest through — it runs the same
            check itself — but because a run is permanent, and finding out that
            a category was invented AFTER recording one is a worse way to learn
            it than before. */}
        <ActButton className="btn btn-primary" disabled={busy || !checked?.ok}
                data-testid="assemble"
                onClick={async () => {
                  setBusy(true); onError(null);
                  const r = await API.recordRun(body());
                  setBusy(false);
                  if (!r.ok) { onError(r.reason); return; }
                  setChecked(null);
                  onAssembled(r.body);
                }}>
          ✓ assemble the contract
        </ActButton>
      </div>

      {checked && !checked.ok && (
        <div className="panel-2 p-3 mt-3" style={{ borderColor: 'var(--danger)' }}>
          <div className="tag" style={{ color: 'var(--danger)' }}>refused</div>
          {/* The engine's own sentence, unchanged. It says which category it
              does not know and why that matters; a friendlier sentence written
              here would throw away the only part anybody can act on. */}
          <div className="text-[12.5px] mt-1.5" style={{ color: 'var(--mute)' }}>
            {checked.reason}
          </div>
        </div>
      )}

      {checked?.ok && (
        <div className="panel-2 p-3 mt-3">
          <div className="tag" style={{ color: 'var(--accent-2)' }}>checked, not recorded</div>
          <div className="text-[12.5px] mt-1.5" style={{ color: 'var(--mute)', lineHeight: 1.7 }}>
            Every category is one the library defines. Nothing has been recorded
            yet.
            {(checked.body?.coerced ?? []).length > 0 && (
              <>
                <br /><br />
                <strong>Severities that were rewritten:</strong>{' '}
                {checked.body.coerced.map((c) => `${c.category} (you said ${c.claimed})`).join(', ')}.
                {' '}An accepted manifest is not necessarily an untouched one.
              </>
            )}
          </div>
        </div>
      )}
    </div>
  );
}

// ── What a run produced ──────────────────────────────────────────────────
function RunResult({ run, decisions, findings, onError }) {
  const [busy, setBusy] = useState(false);
  const mine = decisions.filter((d) => d.run_id === run.run_id);
  const flagged = findings.filter((f) => f.run_id === run.run_id);

  return (
    <div className="panel p-4 mt-3">
      <div className="flex items-baseline justify-between">
        <div>
          <div className="section-label">Assembled {new Date(run.created_at).toLocaleString()}</div>
          <div className="font-mono caption mt-1">{run.run_id}</div>
        </div>
        {run.gate_open
          ? <span className="chip chip-ok">nothing blocking</span>
          : <span className="chip chip-err">blocked</span>}
      </div>

      <div className="caption mt-2">
        {/* ZERO READS AS ZERO. A contract checked against no rules is not a
            clean contract, it is an unchecked one, and the difference has to
            be visible rather than implied by an empty findings list. */}
        {run.findings} {run.findings === 1 ? 'finding' : 'findings'} from{' '}
        {run.decisions} {run.decisions === 1 ? 'clause' : 'clauses'}.
        {run.unresolved > 0 && ` ${run.unresolved} risk${run.unresolved === 1 ? '' : 's'} the library covers with nothing.`}
      </div>

      <div className="mt-3">
        {mine.map((d) => (
          <div className="py-1.5" key={d.seq} style={{ borderTop: '1px solid var(--line)' }}>
            <div className="flex items-baseline justify-between gap-2">
              <div className="min-w-0">
                <span className="text-[13px]">{d.category}</span>
                {d.clause_id
                  ? <span className="font-mono ml-2" style={{ fontSize: 11, color: 'var(--mute-2)' }}>
                      {d.clause_id}@v{d.version}
                    </span>
                  : <span className="chip chip-unknown ml-2">no clause</span>}
              </div>
              <span className="chip chip-std">{d.severity}</span>
            </div>
            {/* The engine's own reason, rendered and never rewritten. "No
                clause available in Ledger" is a statement about the LIBRARY,
                and softening it here would hide a gap that belongs to somebody. */}
            <div className="caption mt-0.5">{d.reason}</div>
            {d.warning && <div className="caption mt-0.5" style={{ color: 'var(--warn)' }}>{d.warning}</div>}
          </div>
        ))}
      </div>

      {flagged.length > 0 && (
        <div className="mt-3 pt-3 border-t hair">
          <div className="section-label mb-1">What the rules found</div>
          {flagged.map((f) => (
            <div className="py-1.5" key={f.seq} style={{ borderTop: '1px solid var(--line)' }}>
              <div className="flex items-baseline gap-2">
                <span className={f.severity === 'High' ? 'chip chip-err' : 'chip chip-pending'}>
                  {f.severity}
                </span>
                <span className="text-[13px]">{f.title}</span>
                <span className="font-mono" style={{ fontSize: 11, color: 'var(--mute-2)' }}>
                  {f.rule_id}@v{f.rule_version}
                </span>
              </div>
              <div className="caption mt-0.5">{f.detail}</div>
            </div>
          ))}
        </div>
      )}

      <div className="mt-4">
        <button className="btn btn-sm" disabled={busy}
                data-testid="download-contract"
                onClick={async () => {
                  setBusy(true); onError(null);
                  const r = await API.contract(run.run_id);
                  setBusy(false);
                  // A REFUSED DOWNLOAD IS A SENTENCE, NOT A BROKEN FILE. The
                  // endpoint refuses if the run no longer rebuilds, and saving
                  // whatever came back would put an unexplainable document on
                  // somebody's desktop.
                  if (!r.ok) { onError(r.reason); return; }

                  // The DOM half of a download lives HERE and in no other
                  // screen — see api.jsx. ADR-0008 gave the viewer no export
                  // path, and that survives because saving a file is a thing
                  // one screen does rather than a thing the transport does.
                  const url = URL.createObjectURL(r.blob);
                  const a = document.createElement('a');
                  a.href = url;
                  a.download = r.filename;
                  document.body.appendChild(a);
                  a.click();
                  a.remove();
                  URL.revokeObjectURL(url);
                }}>
          download the contract
        </button>
        <div className="caption mt-1.5">
          Rebuilt from this run every time, and refused if it no longer
          reproduces. Nothing is stored.
        </div>
      </div>
    </div>
  );
}

// ── A deal that is over ──────────────────────────────────────────────────
// Stands where the acts stand on a live deal, and says which acts are gone
// and why. NOT a disabled button: a control that is present but dead invites
// somebody to hunt for the permission that would wake it, when the truth is
// that the deal is finished and no permission exists.
//
// The history above it is untouched. Reading a closed deal's assemblies,
// findings and override requests is exactly what a closed deal is for.
function DealIsClosed({ deal }) {
  const ended = deal.status === 'terminated';
  return (
    <div className="panel p-4 mt-6">
      <PanelHead
        title={ended ? 'This deal was terminated' : 'This deal is executed'}
        sub={ended
          ? 'It ended without an executed agreement. Nothing further is assembled against it.'
          : 'It is signed and in force. A change to a signed agreement is a new deal, not another assembly against this one.'} />
      <div className="flex items-center gap-3 mt-3">
        {ended
          ? <span className="chip chip-gone">terminated</span>
          : <span className="chip chip-ok">executed</span>}
        <span className="caption">
          The record above stays readable in full.
        </span>
      </div>
    </div>
  );
}

// ── One deal, opened ─────────────────────────────────────────────────────
function OpenDeal({ deal, me, onBack }) {
  const overrides = usePane(() => API.overrides());
  const findings  = usePane(() => API.overrideFindings());
  const runs      = usePane(() => API.runs());
  const decisions = usePane(() => API.runDecisions());
  const runFindings = usePane(() => API.runFindings());
  const library   = usePane(() => API.library());
  // Above every early return in this component, which is the rule S318 cost
  // four blanked panes to learn.
  const routes    = usePane(() => API.noticeRoutes());
  const [error, setError] = useState(null);
  const [asking, setAsking] = useState(false);
  const [runId, setRunId] = useState('');

  const mine = (overrides.rows ?? []).filter((r) => r.agreement_id === deal.agreement_id);

  // THIS DEAL'S RUNS, filtered from what the rule already returned. The reads
  // take no parameter — the scoping is "these runs, this person" and it comes
  // from the connection, not from anything the browser can name.
  const dealRuns = (runs.rows ?? [])
    .filter((r) => r.agreement_id === deal.agreement_id);
  const chosen = dealRuns.find((r) => r.run_id === runId) || dealRuns[0] || null;

  // What actually blocked the chosen run, from the run's own record. High is
  // the severity that closes a gate; a Standard finding is worth reading and
  // does not stop anything, so offering to override one would be theatre.
  const blocking = (runFindings.rows ?? [])
    .filter((f) => chosen && f.run_id === chosen.run_id && f.severity === 'High');

  // The LABEL, which is the string a manifest carries and the trust boundary
  // checks against. The key is for the database's own foreign keys; sending one
  // here would be refused as an invented category, and the person would be told
  // the model hallucinated when in fact this page did.
  const categories = [...new Set((library.rows ?? []).map((c) => c.category_label))]
    .filter(Boolean).sort();

  const reload = () => {
    overrides.reload(); findings.reload();
    runs.reload(); decisions.reload(); runFindings.reload();
  };

  return (
    <div>
      <button className="btn btn-sm mb-4" onClick={onBack}>← my deals</button>

      <div className="panel p-4">
        {/* WRAPPING, so the compose form drops BELOW the deal title at a narrow
            width instead of being squeezed beside it. Without this the row
            keeps both on one line and the form is pushed past the right edge —
            the page never scrolls, but the panel does, and a form you have to
            scroll sideways to reach is not a form somebody uses. */}
        <div className="flex items-baseline justify-between gap-3 flex-wrap">
          <div>
            {/* THE RECORD'S OWN TITLE IS A HEADING. #14 gave all 37 panes a
                page title and measured them in their LIST state; a record
                that REPLACES its pane was never in that census, and these
                views had no heading at all — the big line naming the
                counterparty was a styled div. The deal room already did this
                correctly, which is what showed the other two up.

                Tailwind's preflight resets heading size, weight and margin,
                so the element changes and nothing moves. */}
            <h1 className="display-sm">{deal.counterparty}</h1>
            <div className="font-mono caption mt-1">{deal.agreement_id}</div>
          </div>
          {/* NOT `shrink-0`. It was, and a `shrink-0` group holds its
              max-content width whatever the viewport — so at 375px the
              opened compose form measured 1337px inside a 222px parent
              and dragged a horizontal scrollbar through the panel. The
              button alone is narrow, which is why a sweep of this page
              with the form CLOSED came back clean: the fault only
              exists in the state nobody thought to measure. */}
          <div className="flex items-center gap-3 min-w-0 flex-wrap justify-end">
            {/* THE INTERNAL HALF OF THE OWNER DECISION (Mike, 2026-08-22:
                everyone can send and receive messages). Without a control on
                this side the widening would run one way in practice: a viewer
                could ask a question about a deal shown to them and the person
                who OWNS that deal would have nowhere to answer from.
                Cited off the record that is open, like every other entry
                point, so the reference cannot be mistyped. Absent rather than
                disabled when no route exists. */}
            {routes.status === 'loaded' && (
              <RaiseNotice
                me={me} routes={routes.rows}
                subject={{ kind: 'agreement', ref: deal.agreement_id,
                           about: `${deal.counterparty} · ${deal.agreement_id}` }} />
            )}
            {/* The same three marks the deal table carries. This header wore
                chip-std for all three, so an executed agreement and a
                terminated one were told apart only by the word inside. */}
            {deal.status === 'executed'
              ? <span className="chip chip-ok">executed</span>
              : deal.status === 'terminated'
              ? <span className="chip chip-gone">terminated</span>
              : <span className="chip chip-std">{deal.status}</span>}
          </div>
        </div>
        {/* THIS deal's rail, and no other's. */}
        <div className="mt-4 pt-4 border-t hair"><PipelineRail deal={deal} /></div>
      </div>

      {error && (
        <div className="panel p-3 mt-4" style={{ borderColor: 'var(--danger)' }}>
          <div className="tag" style={{ color: 'var(--danger)' }}>refused</div>
          <div className="text-[12.5px] mt-1.5" style={{ color: 'var(--mute)' }}>{error}</div>
        </div>
      )}

      {/* ── Override requests on this deal ──────────────────────────────── */}
      <div className="mt-6">
        <PanelHead
          title="Override requests"
          sub="Asking is recorded. Being allowed is a separate thing, and Legal's."
        />
        {overrides.status === 'failed' ? (
          <LoadFailed reason={overrides.reason} />
        ) : findings.status === 'failed' ? (
          /* Said plainly rather than rendered as a request with no findings
             under it. `(findings.rows ?? [])` turns a failed read into an
             empty list, and "we could not ask" must never wear the clothes of
             "there is nothing here". */
          <LoadFailed reason={findings.reason} />
        ) : mine.length === 0 ? (
          <Empty
            kicker="overrides"
            line="You have not asked for an override on this deal."
            sub="If a blocking finding stands in the way, asking is the only way past it — and asking is not the same as being allowed." />
        ) : mine.map((r) => {
          const fs = (findings.rows ?? []).filter((f) => f.request_id === r.request_id);
          return (
            <div className="panel p-4 mb-3" key={r.request_id}>
              <div className="flex items-start justify-between">
                <div>
                  <div className="section-label">Request {r.request_id}</div>
                  <div className="caption mt-1">asked {new Date(r.requested_at).toLocaleString()}</div>
                </div>
                {/* The status a requester actually needs: is it socialising, is
                    the window still running, has it been decided. Each is a
                    different answer to "can I proceed yet", and all three
                    answers are no until the last one. */}
                {r.state === 'requested'
                  ? <span className="chip chip-unknown">not socialised</span>
                  : r.state === 'socialised' && !r.window_closed
                    ? <span className="chip chip-pending">
                        window closes {new Date(r.window_closes).toLocaleString()}
                      </span>
                    : r.state === 'socialised'
                      ? <span className="chip chip-pending">waiting on Legal</span>
                      : r.state === 'approved'
                        ? <span className="chip chip-ok">decided</span>
                        : <span className="chip chip-err">{r.state}</span>}
              </div>

              <div className="caption mt-2">
                {r.notified_count
                  ? `${r.notified_count} ${r.notified_count === 1 ? 'person was' : 'people were'} told.`
                  : 'Nobody has been told yet, so nothing can be decided.'}
                {' '}{r.decided} of {r.findings} findings decided, {r.approved} approved.
              </div>

              <div className="mt-3">
                {fs.map((f) => (
                  <div className="flex items-center justify-between py-1.5" key={f.finding_ref}
                       style={{ borderTop: '1px solid var(--line)' }}>
                    <div className="min-w-0">
                      <span className="font-mono" style={{ fontSize: 11, color: 'var(--mute-2)' }}>
                        {f.finding_ref}
                      </span>
                      <span className="text-[13px] ml-2">{f.summary}</span>
                    </div>
                    {f.decision === 'approved'
                      ? <span className="chip chip-ok">past the gate</span>
                      : f.decision === 'rejected'
                        ? <span className="chip chip-err" title={f.note}>still blocks</span>
                        : <span className="chip chip-pending">still blocks</span>}
                  </div>
                ))}
              </div>
              {fs.some((f) => f.decision === 'rejected') && (
                <div className="caption mt-2">
                  A rejected finding still blocks, and the reviewer's note says
                  why. Changing the contract is the way past it — asking again is
                  not.
                </div>
              )}
            </div>
          );
        })}
      </div>

      {/* ── The contract itself ─────────────────────────────────────────── */}
      <div className="mt-6">
        <PanelHead
          title="The contract"
          sub="Assembled from approved wording only. Nothing here is written by the system."
        />
        {runs.status === 'failed' ? (
          <LoadFailed reason={runs.reason} />
        ) : dealRuns.length === 0 ? (
          <Empty
            kicker="not assembled"
            line="No contract has been assembled for this deal."
            sub="Say which risks are in scope and the system selects approved wording for each." />
        ) : (
          <>
            {dealRuns.length > 1 && (
              <div className="panel p-3 mb-1">
                <label className="section-label">Which assembly</label>
                <select aria-label="Which assembly" className="mt-1.5 w-full font-mono" style={{ padding: '5px 8px' }}
                        data-testid="run-choice"
                        value={chosen ? chosen.run_id : ''}
                        onChange={(e) => setRunId(e.target.value)}>
                  {dealRuns.map((r) => (
                    <option key={r.run_id} value={r.run_id}>
                      {new Date(r.created_at).toLocaleString()} — {r.run_id.slice(0, 8)}
                    </option>
                  ))}
                </select>
              </div>
            )}
            {chosen && (
              <RunResult
                run={chosen}
                decisions={decisions.rows ?? []}
                findings={runFindings.rows ?? []}
                onError={setError} />
            )}
          </>
        )}
      </div>

      {/* ── The acts, and only while there is something to act on ───────
          Until 2026-08-21 these were drawn for every deal. An EXECUTED
          agreement and a TERMINATED one both offered "assemble the contract",
          and the doorway accepted it — a permanent assembly run recorded
          against a contract that was signed months ago, or one that ended.
          Offering an act the deal cannot sensibly take is how that happens.

          THE DOORWAY STILL ACCEPTS IT. Withdrawing the offer is the screen's
          half; a closed agreement that refuses new runs is the record's half,
          and it is a lifecycle decision rather than an audit repair — written
          up for Mike in PLAN-2026-08-21. Do not read this branch as a
          control. */}
      {!isLive(deal) ? <DealIsClosed deal={deal} /> : (
      <>
      <AssembleContract
        deal={deal}
        categories={categories}
        onError={setError}
        onAssembled={() => { setRunId(''); reload(); }} />

      {asking ? (
        <RequestOverride
          deal={deal}
          runId={chosen ? chosen.run_id : ''}
          blocking={blocking}
          onCancel={() => setAsking(false)}
          onError={setError}
          onDone={() => { setAsking(false); setAsking(false); reload(); }}
        />
      ) : (
        <div className="mt-6">
          <div className="panel p-4">
            <PanelHead
              title="Blocked by a finding?"
              sub="Asking is the only way past one. There is no acknowledge button."
            />
            <div className="caption" style={{ lineHeight: 1.7 }}>
              The v3 prototype had a single <em>acknowledge · override</em>
              button that opened the gate on one click, with no record of who
              else should have known. It is retired, and nothing here replaces
              it: you <strong>ask</strong>, the people who should know are told,
              a window passes, and Legal decides each finding on its own.
            </div>
            <div className="flex gap-2 items-end mt-4">
              {/* NEITHER THE RUN NOR THE FINDINGS ARE TYPED ANY MORE. Both are
                  taken from the assembly above — the run this person is looking
                  at, and the findings that actually blocked it. A reference
                  typed by hand is one that can be typed wrong, and a wrong one
                  is not caught when it is written: it is decided by Legal and
                  then covers nothing at signature. */}
              <button className="btn btn-primary"
                      disabled={!chosen || blocking.length === 0}
                      data-testid="ask-for-override"
                      onClick={() => setAsking(true)}>
                request an override…
              </button>
            </div>
            <div className="caption mt-2">
              {!chosen
                ? 'Assemble the contract first — an override is asked about a specific finding on a specific assembly.'
                : blocking.length === 0
                  ? 'Nothing on this assembly is blocking, so there is nothing to ask about.'
                  : `${blocking.length} ${blocking.length === 1 ? 'finding blocks' : 'findings block'} this assembly.`}
            </div>
          </div>

        </div>
      )}
      </>
      )}
    </div>
  );
}

// ── My deals ─────────────────────────────────────────────────────────────
// The buyer home of the 2026-08-10 design set: the desk at a glance. The
// pipeline strip counts this person's own deals by derived stage, the
// actions list reads the same waiting derivation the digest reads, and the
// recent table is the deal list itself. Every figure is a measured fact;
// a stage nobody's deal has reached shows its zero, muted, never hidden.
function MyDealsPane({ me }) {
  // One act at a time. useActs guards with a ref, so a second click in the
  // same tick never reaches the network — `disabled` alone cannot, because it
  // only takes effect after a render.
  const acts = useActs();

  const deals = usePane(() => API.deals());
  const overrides = usePane(() => API.overrides());
  const waiting = usePane(() => API.waiting());
  // The open deal lives in the address, so this list can be linked into.
  const [open, setOpen] = useAddressedRecord('my-deals');
  const [error, setError] = useState(null);
  const [newDeal, setNewDeal] = useState({ id: '', counterparty: '', sector: '', value: '' });
  // Search and status filter, the same pair the library, the audit record and
  // the access history already carry. This pane holds the most rows of the
  // four and was the only one without them.
  const [q, setQ] = useState('');
  const [status, setStatus] = useState('');
  // HOW THE LIST IS ORDERED, and it is a CONTROL rather than a preference. A
  // buyer's two questions about a list of forty-eight deals are "which is
  // biggest" and "which has been sitting longest", and neither is answerable
  // by reading a table ordered by reference. The order is stated on screen —
  // an ordering nobody can see reads as an accident somebody is free to
  // change, which is the note `v4.css` makes about the oldest-wait mark.
  const [order, setOrder] = useState('reference');
  // What every figure on this desk drills TO. Declared here with the other
  // hooks and ABOVE the early returns below, because a hook placed after
  // `if (…) return <Loading />` blanks the pane on the render after the data
  // lands — the one render nobody watches (S318).
  const dealList = useRef(null);

  if (deals.status === 'loading') return <Loading />;
  if (deals.status === 'failed') return <LoadFailed reason={deals.reason} />;

  if (open) {
    const deal = deals.rows.find((d) => d.agreement_id === open);
    if (!deal) return <LoadFailed reason="that deal is no longer in your list" />;
    return <OpenDeal deal={deal} me={me} onBack={() => setOpen(null)} />;
  }

  // Filtered in the browser because the read already answered only this
  // person's deals — this narrows what is SHOWN, never what was fetched, and
  // there is no wider list behind it to leak.
  const needle = q.trim().toLowerCase();
  const matched = deals.rows.filter((d) =>
    (!status || d.status === status) &&
    (!needle
      || String(d.agreement_id).toLowerCase().includes(needle)
      || String(d.counterparty || '').toLowerCase().includes(needle)
      || String(d.sector || '').toLowerCase().includes(needle)));

  // SORTED ON A COPY, never in place. `deals.rows` is what `usePane` holds and
  // what every figure on this desk counts; sorting it would reorder the set
  // three other panels are reading from, on a control that was only meant to
  // reorder one table.
  //
  // AN ABSENT VALUE SORTS LAST IN BOTH DIRECTIONS, and that is deliberate. A
  // deal with no recorded value is not worth nothing — it is a deal nobody has
  // priced — and letting it sort as zero would put the unpriced ones at the top
  // of "smallest first" as though they were the cheapest.
  const missingLast = (a, b, pick) => {
    const x = pick(a);
    const y = pick(b);
    if (x === null || x === undefined) return (y === null || y === undefined) ? 0 : 1;
    if (y === null || y === undefined) return -1;
    return null;   // both present; the caller compares
  };
  const shown = [...matched].sort((a, b) => {
    if (order === 'value') {
      const gap = missingLast(a, b, (d) => d.value_usd);
      return gap === null ? Number(b.value_usd) - Number(a.value_usd) : gap;
    }
    if (order === 'oldest') {
      const gap = missingLast(a, b, (d) => d.created_at);
      return gap === null
        ? String(a.created_at).localeCompare(String(b.created_at)) : gap;
    }
    if (order === 'renewal') {
      const gap = missingLast(a, b, (d) => d.renews_on);
      return gap === null
        ? String(a.renews_on).localeCompare(String(b.renews_on)) : gap;
    }
    return String(a.agreement_id).localeCompare(String(b.agreement_id));
  });

  const socialised = (overrides.rows ?? []).filter((r) => r.state === 'socialised').length;
  const executed = deals.rows.filter((d) => d.status === 'executed');
  const terminated = deals.rows.filter((d) => d.status === 'terminated');
  // Counted through the SAME derivation the open deal's rail draws — a
  // second stage rule here would drift from the first, which is exactly what
  // it had done: this filtered on `status !== 'executed'`, so the three
  // TERMINATED deals were counted as live and drawn standing at Manifest.
  // stageOf now answers null for them, and no card claims them.
  const atStage = (i) => deals.rows.filter((d) => stageOf(d) === i).length;

  // ── Drilling down ───────────────────────────────────────────────────────
  // EVERY MEASURED FIGURE ON THIS DESK IS A WAY INTO THE LIST BELOW IT.
  // Until now the strip and the header stated numbers and led nowhere: to see
  // the nine executed deals the card announced, you scrolled past forty-eight
  // rows and worked a select. A dashboard whose figures are not controls is a
  // picture of the data, not a way through it.
  //
  // THE SEARCH BOX IS CLEARED, DELIBERATELY. A stale needle in `q` would
  // answer a card promising 9 with 2 rows while the card still said 9 — the
  // filtered-sample trap this repo has already paid for (S319). The figure on
  // the card and the rows you land on are the same set, or the control lies.
  //
  // Clicking the active one again clears the filter, so a card is a toggle
  // rather than a state you can only leave through the select.
  const drillTo = (next) => {
    setStatus((cur) => (cur === next ? '' : next));
    setQ('');
    // After the render that applies the filter, not before it.
    requestAnimationFrame(() => dealList.current?.scrollIntoView({ block: 'nearest' }));
  };

  // WHICH STATUS A STAGE IS, asked of stageOf rather than remembered here.
  // stageOf maps a status to a stage; this inverts it by asking the same
  // function about every status the record allows, so a fourth status landing
  // on a stage becomes drillable without anybody editing this line. Listing
  // the pairs by hand is how the six `status !== 'executed'` sites came to
  // disagree with each other.
  const statusAtStage = (i) =>
    AGREEMENT_STATUSES.find((st) => stageOf({ status: st }) === i) ?? null;

  // The props that turn a card into a control, or nothing at all when there is
  // no measured set behind it. An unmeasured card MUST NOT become clickable:
  // filtering to a stage nothing was counted at would answer with an empty
  // list, which reads as "you have none" — a fact this screen does not hold.
  const drillLabel = (st, n, on) =>
    (on ? `showing your ${st} deals; press to show every status`
        : `show your ${st} deals (${n})`);

  // For a CARD, which is a <div> and therefore owns none of a button's
  // behaviour: openableRow is the app's answer to "anything you can click"
  // and carries the tab stop, the role and the Enter/Space handling with it.
  const drillProps = (st, n) => {
    if (!st) return {};
    const on = status === st;
    return {
      ...openableRow(() => drillTo(st), drillLabel(st, n, on)),
      'aria-pressed': on,
    };
  };

  // For a real <button>, which already has all of that. Giving it
  // openableRow's role and key handler as well would be restating what the
  // element is — and the app's own idiom is to let the element do its job.
  const drillButton = (st, n) => {
    if (!st) return { disabled: true };
    const on = status === st;
    return {
      onClick: () => drillTo(st),
      'aria-label': drillLabel(st, n, on),
      'aria-pressed': on,
    };
  };

  // WHAT THE REFERENCE IS, per kind. `cw.waiting_for()` answers one
  // `subject_ref` column across six kinds, so the column holds a deal
  // reference on one row and a bare envelope id on the next — and a naked "9"
  // sitting under the same heading as "AG-26-041" reads as though it were the
  // same sort of thing. It is not, and the screen now says which it is.
  //
  // The deeper repair belongs to the derivation: an envelope row should be
  // able to name the agreement it is out on. That is a migration against a
  // read model the daily digest shares, so what the digest says is a decision
  // rather than a defect — raised, not taken. Meanwhile this stops the screen
  // overstating what it holds.
  // THE SHARED VOCABULARY, not this screen's own. It was written out here and
  // again in obligations.jsx, and the two had already drifted: this one names
  // what kind of reference a row carries and that one did not, so the same row
  // read "· AG-26-041" here and "· 9" there. It is a fact about the
  // derivation, so it lives beside the derivation's other shared parts.
  const REF_KINDS = WAITING_REF_KINDS;

  return (
    <div>
      <div className="sheet-head">
        <div>
          <div className="sheet-kicker">The desk of {me.display_name || me.person}</div>
          <h1 className="sheet-title mt-1">Buyer Home</h1>
        </div>
        <div className="flex items-center gap-4">
          {overrides.status === 'loaded' && socialised > 0 && (
            <span className="stamp stamp-pending stamp-sm" style={{ '--rot': '1.2deg' }}>
              {socialised} awaiting Legal
            </span>
          )}
          {/* Three outcomes, three numbers. Folding terminated into "open" —
              which is what `status !== 'executed'` did — told this buyer they
              had 39 live engagements when 36 was the truth. */}
          {/* The first figures anybody reads on this desk, and they drill
              to the same list the cards below do. "open" is the live set,
              which is what isLive counts and what the one reachable stage
              holds — asked through statusAtStage so the three agree. */}
          <span className="sheet-note count-drills">
            <button type="button" className="count-drill"
                    {...drillButton(statusAtStage(3), deals.rows.filter(isLive).length)}>
              {deals.rows.filter(isLive).length} open
            </button>
            {' · '}
            <button type="button" className="count-drill"
                    {...drillButton('executed', executed.length)}>
              {executed.length} executed
            </button>
            {' · '}
            <button type="button" className="count-drill"
                    {...drillButton('terminated', terminated.length)}>
              {terminated.length} terminated
            </button>
          </span>
        </div>
      </div>
      <div className="font-serif italic mt-2" style={{ fontSize: 14, color: 'var(--mute)' }}>
        Your desk at a glance. Every figure below is a measured fact about your
        own deals — nobody else's reach this browser.
      </div>

      {error && (
        <div className="panel p-3 mt-4" style={{ borderColor: 'var(--danger)' }}>
          <div className="tag" style={{ color: 'var(--danger)' }}>refused</div>
          <div className="text-[12.5px] mt-1.5" style={{ color: 'var(--mute)' }}>{error}</div>
        </div>
      )}

      <div className="mt-6">
        <PanelHead title="My deals — by pipeline stage"
                   sub="Derived from each deal's recorded status, the same way an open deal's rail derives it." />
        <div className="stage-strip">
          {STAGES.map((s, i) => {
            // A NUMBER ONLY WHERE ONE WAS MEASURED. Intake, Forge and Dossier
            // are derived from tables this screen does not read, so stageOf can
            // never place a deal at them — and a card printing 0 states a fact
            // nobody holds. shell.jsx keeps this rule for the navigation rack;
            // the strip was breaking it at three of its five cards. The
            // never-ran mark is the vocabulary's word for neither-pass-nor-
            // fail, and it carries that WORD, not just a colour.
            const measured = REACHABLE_STAGES.has(i);
            const n = measured ? atStage(i) : null;
            // Only a measured card drills. The other four stay exactly as
            // inert as the sentence on them says they are.
            const st = measured ? statusAtStage(i) : null;
            return (
              <div className={`stage-card${st ? ' stage-card--drill' : ''}`}
                   key={s} data-stage={s}
                   data-measured={measured ? 'true' : 'false'}
                   {...drillProps(st, n)}>
                <div className="stage-name">{s}</div>
                <div className="stage-mark">
                  {measured
                    ? <span className="stage-n"
                            style={{ fontSize: 26, color: n === 0 ? 'var(--mute-2)' : 'var(--ink)' }}>
                        {n}
                      </span>
                    : <span className="chip chip-unknown">not measured</span>}
                </div>
                <div className="stage-n">
                  {measured
                    ? (n === 1 ? 'deal' : 'deals')
                    : 'no reading behind this stage'}
                </div>
              </div>
            );
          })}
          <div className="stage-card stage-card--drill" data-stage="Executed" data-measured="true"
               {...drillProps('executed', executed.length)}>
            <div className="stage-name">Executed</div>
            <div className="stage-mark">
              {executed.length > 0
                ? <span className="stamp stamp-ok stamp-sm">effective</span>
                : <span className="stage-n" style={{ fontSize: 26, color: 'var(--mute-2)' }}>0</span>}
            </div>
            <div className="stage-n">{executed.length > 0
              ? `${executed.length} ${executed.length === 1 ? 'deal' : 'deals'}`
              : 'deals'}</div>
          </div>
          {/* The second closed outcome, and it needs its own card. Excluding
              terminated deals from the stages without showing them anywhere
              would drop three of this buyer's forty-eight off the strip
              altogether — a quieter version of the same untruth. */}
          <div className="stage-card stage-card--drill" data-stage="Terminated" data-measured="true"
               {...drillProps('terminated', terminated.length)}>
            <div className="stage-name">Terminated</div>
            <div className="stage-mark">
              {terminated.length > 0
                ? <span className="stamp stamp-gone stamp-sm">terminated</span>
                : <span className="stage-n" style={{ fontSize: 26, color: 'var(--mute-2)' }}>0</span>}
            </div>
            <div className="stage-n">{terminated.length > 0
              ? `${terminated.length} ${terminated.length === 1 ? 'deal' : 'deals'}`
              : 'deals'}</div>
          </div>
        </div>
      </div>

      <div className="mt-6 grid gap-6" style={{ gridTemplateColumns: 'minmax(0,1fr) minmax(0,1fr)' }}>
        <div className="min-w-0">
          <PanelHead title="My actions"
                     sub="The same derivation the notification digest reads." />
          {waiting.status === 'failed' ? (
            <LoadFailed reason={waiting.reason} />
          ) : waiting.status === 'loading' ? (
            <Loading />
          ) : waiting.rows.length === 0 ? (
            <Empty kicker="waiting" line="Nothing is waiting on you." />
          ) : (
            <div className="panel">
              <table className="ledger">
                <thead>
                  {/* "Due" WAS FALSE FOR TEN OF THE TWELVE KINDS. Only an
                      obligation and a renewal window carry a deadline; every
                      other row carries the moment it started waiting, and this
                      column printed both bare and identically. Three envelopes
                      sent three weeks ago read as three deadlines missed three
                      weeks ago. The column now holds two quantities and the
                      row says which one it is showing, so the heading says
                      "When" rather than naming one of them. */}
                  <tr><th>Item</th><th>Ref</th><th style={{ textAlign: 'right' }}>When</th></tr>
                </thead>
                <tbody>
                  {waiting.rows.map((w, i) => {
                    const noun = REF_KINDS[w.kind];
                    // An action naming one of YOUR deals opens it. The whole
                    // point of a list of what is waiting on you is to be able
                    // to go and do it; this one could only be read.
                    const deal = w.kind === 'renewal_window'
                      ? deals.rows.find((d) => d.agreement_id === w.subject_ref) : null;
                    const rowProps = deal
                      ? openableRow(() => setOpen(deal.agreement_id),
                          `open ${deal.agreement_id}, ${deal.counterparty}`)
                      : {};
                    return (
                      <tr key={`${w.kind}-${w.subject_ref}-${i}`} {...rowProps}>
                        <td>{WAITING_KINDS[w.kind] ?? w.kind}</td>
                        <td className="mono">
                          {noun && <span className="caption">{noun} </span>}
                          {w.subject_ref}
                    <div><WaitingRecordLink row={w} me={me} /></div>
                        </td>
                        <td className="mono" style={{ textAlign: 'right' }}>
                          <WaitingWhen row={w} />
                        </td>
                      </tr>
                    );
                  })}
                </tbody>
              </table>
            </div>
          )}
        </div>

        <div className="min-w-0" ref={dealList}>
          {/* WAS "My deals — recent", above a subtitle saying "Every
              engagement you opened". The two disagreed and the subtitle was
              the true one: nothing here is recent, it is all of them. */}
          <PanelHead title="My deals"
                     sub="Every engagement you opened, and nobody else's."
                     right={<span className="caption">
                       {shown.length === deals.rows.length
                         ? `${deals.rows.length}`
                         : `${shown.length} of ${deals.rows.length}`}
                     </span>} />
          {deals.rows.length > 0 && (
            <div className="list-filter flex gap-2 mb-3 mt-2">
              <input className="font-mono min-w-0 grow" style={{ padding: '5px 9px' }}
                     placeholder="deal or counterparty" value={q}
                     aria-label="Search your deals by reference or counterparty"
                     onChange={(e) => setQ(e.target.value)} data-testid="deals-search" />
              {/* Named from the same list stageOf is checked against, so a
                  fourth status reaches this filter without anyone editing
                  this pane. */}
              <select className="font-mono" style={{ padding: '5px 9px' }} value={status}
                      aria-label="Filter your deals by status"
                      onChange={(e) => setStatus(e.target.value)} data-testid="deals-status">
                <option value="">every status</option>
                {AGREEMENT_STATUSES.map((st) => (
                  <option key={st} value={st}>{st}</option>
                ))}
              </select>
              {/* THE ORDER, SAID OUT LOUD. Sorting a table by clicking its
                  headers hides the ordering in a state nobody can read at a
                  glance; a named control says which question the list is
                  currently answering. The four are the four a buyer asks. */}
              <select className="font-mono" style={{ padding: '5px 9px' }} value={order}
                      aria-label="Order your deals"
                      onChange={(e) => setOrder(e.target.value)} data-testid="deals-order">
                <option value="reference">by reference</option>
                <option value="value">largest value first</option>
                <option value="oldest">oldest first</option>
                <option value="renewal">renewing soonest</option>
              </select>
            </div>
          )}
          {deals.rows.length === 0 ? (
            <Empty
              kicker="my deals"
              line="You have no deals open."
              sub="Nothing is shown from anyone else's list — the database scopes this to you, so another buyer's engagements never reach this browser at all." />
          ) : shown.length === 0 ? (
            /* A filter matching nothing is not an empty book, and must not
               wear its clothes. */
            <Empty
              kicker="my deals"
              line="No deal of yours matches that."
              sub="Clear the search and the status filter to see all of them again." />
          ) : (
            <div className="panel">
              <table className="ledger">
                <thead>
                  {/* THE COLUMNS THE RECORD ALREADY HELD. `cw.agreement`
                      carries value, sector and the day it was opened, and this
                      list showed none of them — so the person whose job is
                      buying could not see what a deal was worth or how long it
                      had been sitting. The renewal date comes from the executed
                      record and is absent until there is one, which is a fact
                      rather than a gap. */}
                  <tr>
                    <th>Deal</th><th>Counterparty</th><th>Sector</th>
                    <th style={{ textAlign: 'right' }}>Value</th>
                    <th style={{ textAlign: 'right' }}>Open</th>
                    <th>Renews</th><th>Status</th>
                  </tr>
                </thead>
                <tbody>
                  {shown.map((d) => (
                    <tr key={d.agreement_id}
                        {...openableRow(() => setOpen(d.agreement_id),
                          `open ${d.agreement_id}, ${d.counterparty}`)}>
                      <td className="mono">{d.agreement_id}</td>
                      <td>{d.counterparty}</td>
                      <td className="caption">{d.sector || '—'}</td>
                      {/* AN ABSENT VALUE SAYS SO, and does not draw a zero.
                          "nobody has priced this" and "this is worth nothing"
                          are different facts and only one of them is ever
                          true. */}
                      <td className="mono" style={{ textAlign: 'right' }}
                          title={d.value_usd === null || d.value_usd === undefined
                            ? 'no value recorded' : String(d.value_usd)}>
                        {money(d.value_usd)}
                      </td>
                      <td className="mono" style={{ textAlign: 'right' }}
                          title={d.created_at || ''}>
                        {d.created_at ? since(d.created_at) : '—'}
                      </td>
                      <td className="mono">
                        {d.renews_on
                          ? d.renews_on
                          : <span className="caption">not executed</span>}
                      </td>
                      <td>
                        {/* Three statuses, three marks. Terminated wore the
                            same neutral chip as a live negotiation, so a dead
                            deal and one under active discussion were
                            indistinguishable in the one table listing both.
                            superseded is struck and kept — which is exactly
                            what a terminated agreement is. */}
                        {d.status === 'executed'
                          ? <span className="chip chip-ok">executed</span>
                          : d.status === 'terminated'
                          ? <span className="chip chip-gone">terminated</span>
                          : <span className="chip chip-std">{d.status}</span>}
                      </td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
          )}
        </div>
      </div>

      <div className="note-card note-card--rule mt-6" style={{ maxWidth: 430, '--rot': '-.5deg' }}>
        <div className="note-title">The rule of this desk</div>
        <div className="rule-text">
          What your grant cannot read never reaches this desk. Hiding a paper
          is not the control — the vault refusing to hand it over is.
        </div>
      </div>

      <div className="panel p-4 mt-6">
        <PanelHead title="Open a deal" sub="It is yours, and it is opened in your name." />
        {/* WRAPPING, AND A BASIS RATHER THAN A WIDTH. Two fixed widths and a
            button in a `nowrap` row measured 260px inside a 254px panel at
            375 — six pixels over, which is a fix sized for the widths somebody
            tried (S323). `flex` with a basis keeps the same proportions at
            1440 and lets the fields drop onto their own lines when there is no
            room. Pre-existing; found by the sweep for this cycle's change. */}
        <div className="flex gap-2 items-end flex-wrap">
          <div style={{ flex: '1 1 180px', minWidth: 0 }}>
            <label className="section-label">Reference</label>
            <input aria-label="Reference" className="mt-1.5 w-full font-mono" placeholder="AG-001"
                   value={newDeal.id}
                   onChange={(e) => setNewDeal({ ...newDeal, id: e.target.value })} />
          </div>
          <div style={{ flex: '1 1 240px', minWidth: 0 }}>
            <label className="section-label">Counterparty</label>
            <input aria-label="Counterparty" className="mt-1.5 w-full" placeholder="Northwind"
                   value={newDeal.counterparty}
                   onChange={(e) => setNewDeal({ ...newDeal, counterparty: e.target.value })} />
          </div>
          {/* The two 0003 columns the list below draws and orders by. Until
              2026-08-26 only a seed script could fill them, so a real deal
              showed a blank value forever. Both optional: absence is drawn as
              absence, never as a zero. */}
          <div style={{ flex: '1 1 150px', minWidth: 0 }}>
            <label className="section-label">Value, USD (optional)</label>
            <input aria-label="Value in US dollars (optional)" className="mt-1.5 w-full"
                   type="number" min="0" placeholder="250000"
                   value={newDeal.value}
                   onChange={(e) => setNewDeal({ ...newDeal, value: e.target.value })} />
          </div>
          <div style={{ flex: '1 1 150px', minWidth: 0 }}>
            <label className="section-label">Sector (optional)</label>
            <input aria-label="Sector (optional)" className="mt-1.5 w-full" placeholder="Logistics"
                   value={newDeal.sector}
                   onChange={(e) => setNewDeal({ ...newDeal, sector: e.target.value })} />
          </div>
          <button className="btn btn-primary"
                  disabled={!newDeal.id.trim() || !newDeal.counterparty.trim()
                            || acts.busy !== null}
                  onClick={() => acts.run('open-deal', async () => {
                    setError(null);
                    const r = await API.openDeal({ agreement_id: newDeal.id.trim(),
                      counterparty: newDeal.counterparty.trim(),
                      sector: newDeal.sector.trim(), value_usd: newDeal.value });
                    if (!r.ok) { setError(r.reason); return; }
                    setNewDeal({ id: '', counterparty: '', sector: '', value: '' });
                    deals.reload();
                  })}>✓ open</button>
        </div>
        {/* The requester is the session's person and cannot be anything else —
            said here because a form with no "requester" field looks like an
            omission otherwise. */}
        <div className="caption mt-3">
          The deal is opened in your name. There is no field for whose deal it is,
          because it is yours — opening one "on behalf of" somebody else would put
          the wrong name on every scoping decision that follows.
        </div>
      </div>
    </div>
  );
}

// ── My record ────────────────────────────────────────────────────────────
// The requester's own slice of the audit chain. Not a filtered view of
// everybody's — cw.audit_event's policy scopes a requester to rows where THEY
// are the actor, so this browser never receives anybody else's.
function MyRecordPane({ me }) {
  const pane = usePane(() => API.record());
  if (pane.status === 'loading') return <Loading />;
  if (pane.status === 'failed') return <LoadFailed reason={pane.reason} />;
  return (
    <div>
      <PaneHead
        title="My record"
        sub="Everything you have done, as the system recorded it."
      />
      <WaitingList
        order="newest"
        items={pane.rows.map((e) => ({
          key: e.seq,
          title: e.event_type,
          sub: e.subject ?? '',
          at: e.ts,
          chips: <span className="chip chip-std">{e.actor_role ?? 'no role'}</span>,
        }))}
        empty={<Empty kicker="my record" line="You have not done anything yet."
                      sub="Every act you take is recorded here, permanently." />}
      />
      <div className="caption mt-3">
        This is only your own acts. The database scopes it that way — the rest of
        the record is not filtered out in this browser, it never arrives.
      </div>
    </div>
  );
}
