// A STATEMENT OF WORK THAT DEPARTS FROM ITS MASTER.
//
// WHAT THIS IS. A statement of work hangs off a master agreement and normally
// takes the master's position on every category. Sometimes one genuinely has to
// differ — a stricter data-protection clause for one project, a different
// liability position for one deliverable. `0012` built the whole procedure for
// that: somebody proposes the departure with a written reason, the named
// approvers sign it, and Legal authorises it.
//
// AND THE GATE IS REAL. `cw.sow_hangs_off_a_master()` refuses to record a
// statement of work that contradicts its master on a category unless an
// AUTHORISED override exists.
//
// **NOT ONE READ AND NOT ONE WRITE EVER REACHED ANY OF IT.** Six objects, three
// writable tables with policies, an append-only guarantee, an audit trigger, a
// required-approver check and that gate — and no endpoint in the service layer
// named a single one of them. So a departure that genuinely needed to happen was
// blocked and nobody could authorise it: a locked door with no key, against this
// repository's own rule that every gate needs a door that is not a lie.
// `a-grant-nobody-can-reach.test.mjs` named all six the day it was written.
//
// THE THREE ACTS, AND THE SPLIT IS THE CONTROL — the same one concessions use.
// A requester may PROPOSE a departure on a statement of work they own and record
// their OWN approval; only Legal may AUTHORISE. Every one of those sentences is
// a policy in `0012`, and this file adds no permission logic to them: it decides
// what to OFFER, and the database decides what to allow.
//
// WHAT IS MISSING IS THE DATABASE'S ANSWER, NOT A SUBTRACTION DONE HERE.
// `GET /sow/approvals` unions the signatures with
// `cw.sow_override_missing_approvers()`, a definer function this role may
// execute — so "still waiting on" is computed where the rule lives. A screen
// that worked it out by subtracting one list from another would be a second
// copy of the gate's own logic, one edit away from disagreeing with it.

const { useState, useMemo, useRef } = React;

// WHO MAY DO WHICH ACT, asked of the role rather than of the response. Both
// lists are `0012`'s policies read back: `propose` admits Legal and a requester
// on their own SOW, `authorise` admits Legal alone. Affordances, not
// permissions — the database refuses regardless, and an offer that is always
// refused teaches people to stop pressing.
const MAY_PROPOSE = ['requester', 'legal_reviewer', 'legal_admin'];
const MAY_AUTHORISE = ['legal_reviewer', 'legal_admin'];

// The three hats an approval can be signed in, in the schema's own words.
// `cw.sow_override_approval.approver_kind` carries exactly these three and the
// database refuses a fourth, so this list is a copy of a CHECK constraint and
// not a vocabulary this screen invented.
const APPROVER_KINDS = ['requester', 'attorney', 'required'];

function DeparturesPane({ me }) {
  const acts = useActs();
  const overrides = usePane(() => API.sowOverrides());
  const approvals = usePane(() => API.sowApprovals());
  const conflicts = usePane(() => API.sowConflicts());
  const orphans   = usePane(() => API.sowOrphans());
  // WHO THE ATTORNEY IS, so the gate's refusal is explained BEFORE somebody
  // meets it. `cw.sow_override_gate()` fails closed on a deal with no
  // assigned attorney — deliberately — and until 2026-08-23 nothing could
  // assign one, so every authorisation refused for a reason no screen said.
  const attorneys = usePane(() => API.attorneys());
  // AND WHO ELSE HAS TO SIGN. The same gate counts the outstanding signatures
  // by joining cw.required_approver, and until 2026-08-24 nothing could add a
  // row to it — so the list was empty on every deal and this side of the gate
  // always passed. Read here so the departure below can say who is owed
  // BEFORE somebody presses authorise and is refused.
  const required  = usePane(() => API.requiredApprovers());
  const [error, setError] = useState(null);
  const [open, setOpen] = useAddressedRecord('departures');
  const [form, setForm] = useRetainedState('departure',
    { sow_id: '', category_key: '', reason: '' });
  const registerRef = useRef(null);
  const conflictRef = useRef(null);
  const orphanRef = useRef(null);

  // Hooks above every early return: one placed after `if (…) return <Loading />`
  // blanks the pane on the render after the data lands (S318).
  const filter = useListFilter(overrides.rows, {
    view: 'departures:overrides',
    fields: ['sow_id', 'category_key', 'reason', 'proposed_by', 'settled_by'],
    facet: 'category_key',
  });

  const rows = overrides.rows ?? [];
  const measured = overrides.status === 'loaded';
  // THE BOUND, AND WHY IT IS SAID OUT LOUD. `GET /sow/overrides` and
  // `GET /sow/approvals` both carry `limit 500` because the tables under them
  // are append-only — a departure is evidence, not working state. Below it
  // nothing is hidden and this says nothing; at it, every figure on this page
  // is counted over a truncated set and would quietly stop being true.
  const AT_THE_BOUND = 500;
  const truncated = rows.length >= AT_THE_BOUND;
  const inForce = rows.filter((o) => o.in_force);
  const proposed = rows.filter((o) => !o.in_force);
  // A DEPARTURE THAT IS HAPPENING WITH NOTHING ON RECORD. The conflict view
  // says where a statement of work already contradicts its master; an override
  // in force says that was authorised. The pairs that do not match are the
  // whole reason this screen exists, and the set is computed here because it is
  // a JOIN ACROSS TWO READS rather than a rule — the database is not being
  // asked a question it already answers.
  const authorisedKey = new Set(inForce.map((o) => `${o.sow_id}|${o.category_key}`));
  const unauthorised = (conflicts.rows ?? [])
    .filter((c) => !authorisedKey.has(`${c.sow_id}|${c.category_key}`));

  const mayPropose = MAY_PROPOSE.includes(me.role);
  const mayAuthorise = MAY_AUTHORISE.includes(me.role);

  const reloadAll = () => {
    overrides.reload(); approvals.reload(); conflicts.reload(); orphans.reload();
    attorneys.reload(); required.reload();
  };

  const run = (key, fn) => acts.run(key, async () => {
    setError(null);
    const r = await fn();
    // THE DATABASE'S OWN SENTENCE, unchanged. `cw.sow_override_gate()` refuses
    // by NAMING who is still to sign, and `cw.sow_approval_names_the_right_person()`
    // says the approval is not from the person the configuration names. Both are
    // actionable; "could not authorise" is not.
    if (!r.ok) setError(r.reason); else reloadAll();
    return r;
  });

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

  const focusOn = (key, label, test) => {
    // Registered as the tile renders, so a saved view can put the focus back.
    const f = filter.focusable(key, label, test);
    return {
      to: measured ? () => { filter.focusOn(f);
                             requestAnimationFrame(showSection(registerRef)); } : null,
      on: filter.focus?.key === key,
    };
  };

  const current = open ? rows.find((o) => String(o.override_id) === String(open)) : null;

  return (
    <div>
      <PaneHead
        title="Departures from the master"
        kicker="Statements of work"
        sub="Where one statement of work is allowed to differ from the agreement it hangs off — proposed with a reason, signed by the people the deal names, and authorised by Legal."
        right={<ActButton className="btn btn-sm" data-testid="departures-reload"
                          onClick={async () => reloadAll()}>read it again</ActButton>} />

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

      {truncated && (
        <div className="panel-2 p-3 mt-4" data-testid="departures-bounded">
          <div className="tag" style={{ color: 'var(--accent-2)' }}>the oldest are not here</div>
          <div className="text-[12.5px] mt-1.5" style={{ color: 'var(--mute)' }}>
            This holds the most recent {AT_THE_BOUND} departures, newest first —
            <strong> the figures below are counted over those</strong>, not over
            every departure there has ever been.
          </div>
        </div>
      )}

      {/* FOUR FIGURES. The first two narrow the register; the last two are
          different SETS on this page and scroll to their own sections, which is
          why they are not a focus. A measured nought still drills — "nothing is
          departing without authority" is the answer somebody came here for. */}
      <div className="mt-5 grid gap-3"
           style={{ gridTemplateColumns: 'repeat(auto-fit, minmax(170px, 1fr))' }}>
        <StatBox label="authorised" n={measured ? inForce.length : null}
                 describe={`show the ${inForce.length} authorised departures`}
                 {...focusOn('in-force', 'authorised', (o) => o.in_force)} />
        <StatBox label="proposed, not yet authorised" n={measured ? proposed.length : null}
                 nStyle={proposed.length > 0 ? { color: 'var(--accent-2)' } : undefined}
                 describe={`show the ${proposed.length} still waiting`}
                 {...focusOn('proposed', 'proposed, not yet authorised', (o) => !o.in_force)} />
        <StatBox label="departing with nothing on record"
                 n={conflicts.status === 'loaded' ? unauthorised.length : null}
                 nStyle={unauthorised.length > 0 ? { color: 'var(--danger)' } : undefined}
                 describe={`show the ${unauthorised.length} contradicting their master with no authority`}
                 to={conflicts.status === 'loaded' ? showSection(conflictRef) : null} />
        <StatBox label="master terminated, work live"
                 n={orphans.status === 'loaded' ? orphans.rows.length : null}
                 nStyle={(orphans.rows ?? []).length > 0 ? { color: 'var(--accent-2)' } : undefined}
                 describe={`show the ${(orphans.rows ?? []).length} orphaned statements of work`}
                 to={orphans.status === 'loaded' ? showSection(orphanRef) : null} />
      </div>

      {mayPropose && (
        <div className="panel p-4 mt-6" data-testid="propose-form">
          <PanelHead
            title="Propose a departure"
            sub="The reason is not optional — the record refuses one without it, because an override nobody justified is the artefact an auditor asks about first." />
          <div className="flex gap-2 flex-wrap mt-1">
            <input className="font-mono" style={{ padding: '6px 9px', minWidth: 150 }}
                   placeholder="the statement of work" aria-label="Which statement of work"
                   data-testid="propose-sow" value={form.sow_id}
                   onChange={(e) => setForm({ ...form, sow_id: e.target.value })} />
            <input className="font-mono" style={{ padding: '6px 9px', minWidth: 130 }}
                   placeholder="category" aria-label="Which category it departs on"
                   data-testid="propose-category" value={form.category_key}
                   onChange={(e) => setForm({ ...form, category_key: e.target.value })} />
            <input className="grow" style={{ padding: '6px 9px', minWidth: 220 }}
                   placeholder="why this departure is justified"
                   aria-label="Why this departure is justified"
                   data-testid="propose-reason" value={form.reason}
                   onChange={(e) => setForm({ ...form, reason: e.target.value })} />
            <ActButton className="btn btn-primary" data-testid="propose-do"
                       disabled={!form.sow_id.trim() || !form.category_key.trim()
                                 || !form.reason.trim()}
                       onClick={() => run('propose', async () => {
                         const r = await API.proposeSowOverride({
                           sow_id: form.sow_id.trim(),
                           category_key: form.category_key.trim(),
                           reason: form.reason.trim(),
                         });
                         if (r.ok) {
                           setForm({ sow_id: '', category_key: '', reason: '' });
                           discardDraft('departure');
                         }
                         return r;
                       })}>
              propose it
            </ActButton>
          </div>
          <p className="caption mt-2">
            Proposing authorises nothing. A departure binds only once every person
            the deal names has signed and Legal has settled it — and the record
            refuses to settle one while anybody is still owed.
          </p>
        </div>
      )}

      {/* ── The register ─────────────────────────────────────────────────── */}
      <div className="mt-6" ref={registerRef}>
        <PanelHead title="Every departure on the record"
                   sub="Proposed and authorised alike. Opening one shows who has signed and who is still owed."
                   right={<FilterCount filter={filter} />} />
        {rows.length === 0 ? (
          <Empty
            kicker="departures"
            line="No departure has ever been proposed."
            sub="An empty record, not a failed read. Until one is proposed, every
                 statement of work takes its master's position on every category —
                 and the record refuses any that does not." />
        ) : (
          <>
            <ListFilter filter={filter} testid="departures"
                        placeholder="statement of work, category, reason or person"
                        facetLabel="every category" />
            {filter.shown.length === 0
              ? <NoMatch kicker="departures" noun="departure" />
              : (
                <div className="panel">
                  <table className="ledger">
                    <thead>
                      <tr><th>Statement of work</th><th>Category</th><th>Why</th>
                          <th>Proposed by</th><th>Standing</th></tr>
                    </thead>
                    <tbody>
                      {filter.shown.map((o) => (
                        <tr key={o.override_id}
                            {...openableRow(
                              () => setOpen(String(o.override_id) === String(open)
                                ? null : String(o.override_id)),
                              `open the departure on ${o.sow_id}, ${o.category_key}`)}>
                          <td className="mono">{o.sow_id}</td>
                          <td>{o.category_key}</td>
                          <td>{o.reason}</td>
                          <td>
                            <span className="mono">{o.proposed_by}</span>
                            {/* A MACHINE MAY PROPOSE AND MAY NEVER APPROVE, and
                                the record says which this was. Immutable once
                                written, by 0012's own trigger. */}
                            {o.proposer_kind === 'machine' && (
                              <span className="chip chip-pending" style={{ marginLeft: 6 }}>
                                machine
                              </span>)}
                          </td>
                          <td>
                            {o.in_force
                              ? <><span className="chip chip-ok">authorised</span>
                                  <span className="caption"> by {o.settled_by} · {String(o.settled_on ?? '').slice(0, 10)}</span></>
                              : <span className="chip chip-pending">waiting on signatures</span>}
                          </td>
                        </tr>
                      ))}
                    </tbody>
                  </table>
                </div>
              )}
          </>
        )}
      </div>

      {current && (
        <div className="mt-5">
          <OneDeparture
            me={me} row={current} approvals={approvals}
            attorney={(attorneys.rows ?? [])
              .find((a) => a.agreement_id === current.sow_id)}
            attorneysKnown={attorneys.status === 'loaded'}
            required={(required.rows ?? [])
              .filter((r) => r.agreement_id === current.sow_id)}
            requiredKnown={required.status === 'loaded'}
            mayAuthorise={mayAuthorise} run={run}
            onClose={() => setOpen(null)} />
        </div>
      )}

      {/* ── Departing with nothing on record ─────────────────────────────── */}
      <div className="mt-8 pt-6 border-t hair" ref={conflictRef} data-testid="sow-conflicts">
        <PanelHead
          title="Departing with nothing on record"
          sub="Where a statement of work already takes a different clause from its master, and no authorised departure covers it." />
        {conflicts.status === 'loading' ? <Loading />
          : conflicts.status === 'failed' ? <LoadFailed reason={conflicts.reason} />
          : unauthorised.length === 0 ? (
            <Empty
              kicker="conflicts"
              line="Nothing is departing from its master without authority."
              sub="Every difference on record is covered by an authorised departure.
                   That is a measured answer, not an empty read." />
          ) : (
            <div className="panel">
              <table className="ledger">
                <thead>
                  <tr><th>Statement of work</th><th>Master</th><th>Category</th>
                      <th>Master takes</th><th>The work takes</th></tr>
                </thead>
                <tbody>
                  {unauthorised.map((c) => (
                    <tr key={`${c.sow_id}|${c.category_key}`}>
                      <td className="mono">{c.sow_id}</td>
                      <td className="mono">{c.master_id}</td>
                      <td>{c.category_key}</td>
                      <td className="mono">{c.master_clause}</td>
                      <td className="mono">{c.sow_clause}</td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
          )}
      </div>

      {/* ── A live statement of work under a terminated master ───────────── */}
      <div className="mt-8 pt-6 border-t hair" ref={orphanRef} data-testid="sow-orphans">
        <PanelHead
          title="Master terminated, work still live"
          sub="Not a fault the system can fix — somebody has to decide what happens to the work." />
        {orphans.status === 'loading' ? <Loading />
          : orphans.status === 'failed' ? <LoadFailed reason={orphans.reason} />
          : (orphans.rows ?? []).length === 0 ? (
            <Empty
              kicker="orphans"
              line="Every live statement of work has a live master."
              sub="A measured answer. When a master is terminated and work under it is
                   not, it appears here — the system records the fact and decides
                   nothing about it." />
          ) : (
            <div className="panel">
              <table className="ledger">
                <thead><tr><th>Statement of work</th><th>Its status</th>
                           <th>Master</th><th>Its status</th></tr></thead>
                <tbody>
                  {orphans.rows.map((o) => (
                    <tr key={o.sow_id}>
                      <td className="mono">{o.sow_id}</td>
                      <td><span className="chip chip-std">{o.sow_status}</span></td>
                      <td className="mono">{o.master_id}</td>
                      <td><span className="chip chip-gone">{o.master_status}</span></td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
          )}
      </div>

      <p className="caption mt-4">
        A machine may propose a departure and may never approve one — the record
        marks which, and cannot be rewritten afterwards. Authorising is Legal's
        act alone, and the record refuses it while anybody the deal names is
        still to sign.
      </p>
    </div>
  );
}

// ── One departure: who has signed, who is still owed, and the act ─────────
//
// THE MISSING SIDE IS THE DATABASE'S ANSWER. `GET /sow/approvals` unions the
// signatures with `cw.sow_override_missing_approvers()`, so this renders two
// kinds of row from one read rather than subtracting one list from another —
// a subtraction here would be a second copy of the gate's own logic.
function OneDeparture({ me, row, approvals, attorney, attorneysKnown,
                       required, requiredKnown, mayAuthorise, run, onClose }) {
  const [kind, setKind] = useState(APPROVER_KINDS[0]);

  const mine = (approvals.rows ?? [])
    .filter((a) => String(a.override_id) === String(row.override_id));
  const signed = mine.filter((a) => !a.missing);
  const owed = mine.filter((a) => a.missing);

  return (
    <div className="panel p-4" data-testid="one-departure">
      <PanelHead
        title={`${row.sow_id} — ${row.category_key}`}
        sub={row.reason}
        right={<button type="button" className="chip chip-focus"
                       data-testid="departure-close" onClick={onClose}>
                 close <span aria-hidden="true">×</span>
               </button>} />

      {approvals.status === 'loading' ? <Loading />
        : approvals.status === 'failed' ? <LoadFailed reason={approvals.reason} />
        : (
          <>
            <div className="grid gap-3 mt-3"
                 style={{ gridTemplateColumns: 'repeat(auto-fit, minmax(150px, 1fr))' }}>
              <StatBox label="signed" n={signed.length} />
              {/* STILL OWED IS NOUGHT ONCE IT IS AUTHORISED, and that is a fact
                  rather than a gap: the gate counts the approvals into the
                  settlement row, so an authorised departure has nobody left. */}
              <StatBox label="still owed" n={row.in_force ? 0 : owed.length}
                       nStyle={!row.in_force && owed.length > 0
                         ? { color: 'var(--accent-2)' } : undefined} />
              <StatBox label="counted at authorisation"
                       n={row.in_force ? Number(row.approvals_at_settlement ?? 0) : null} />
            </div>

            {/* OUTSIDE THE WINDOW IS NOT "NOBODY SIGNED", and the two are
                distinguishable by construction: an UNSETTLED departure always
                has at least one row here, because the missing-approver arm of
                the read emits the requester. So no rows at all can only mean
                this departure is older than the 500 the read holds — and a
                settled one has at least one signature, because the gate counted
                them before it would settle. */}
            {mine.length === 0 && (
              <div className="panel-2 p-3 mt-3" data-testid="departure-outside-window">
                <div className="tag" style={{ color: 'var(--accent-2)' }}>older than this screen holds</div>
                <div className="text-[12.5px] mt-1.5" style={{ color: 'var(--mute)' }}>
                  The signatures for this departure are outside the 500 most
                  recent records. <strong>This is not "nobody signed"</strong> —
                  an unauthorised departure always shows at least whoever is
                  still owed.
                </div>
              </div>
            )}

            <div className="mt-4">
              <div className="section-label">Signatures</div>
              {signed.length === 0
                ? <div className="caption mt-1">
                    {mine.length === 0
                      ? 'Not in the window this screen holds.'
                      : 'Nobody has signed this yet.'}
                  </div>
                : (
                  <table className="ledger mt-2">
                    <thead><tr><th>Signed as</th><th>Who</th>
                               <th style={{ textAlign: 'right' }}>On</th></tr></thead>
                    <tbody>
                      {signed.map((a) => (
                        <tr key={`${a.approver_kind}-${a.approver}`}>
                          <td><span className="chip chip-std">{a.approver_kind}</span></td>
                          <td className="mono">{a.approver}</td>
                          <td className="mono" style={{ textAlign: 'right' }}>
                            {String(a.approved_on ?? '').slice(0, 10)}
                          </td>
                        </tr>
                      ))}
                    </tbody>
                  </table>
                )}
            </div>

            {!row.in_force && (
              <div className="mt-4">
                <div className="section-label">Still owed</div>
                {owed.length === 0
                  ? <div className="caption mt-1">
                      Everybody the deal names has signed. It is Legal's to authorise.
                    </div>
                  : (
                    <table className="ledger mt-2">
                      <thead><tr><th>Wanted as</th><th>Who</th></tr></thead>
                      <tbody>
                        {owed.map((a) => (
                          <tr key={`owed-${a.approver_kind}-${a.approver}`}>
                            <td><span className="chip chip-unknown">{a.approver_kind}</span></td>
                            <td className="mono">{a.approver}</td>
                          </tr>
                        ))}
                      </tbody>
                    </table>
                  )}
              </div>
            )}

            {/* THE REFUSAL, EXPLAINED BEFORE IT HAPPENS. The gate fails closed
                on a deal with no assigned attorney, and that is not something a
                person can guess from the authorise control. Drawn only when the
                read has actually answered — "we do not know yet" and "there is
                none" are different facts, and only the second is a blocker. */}
            {!row.in_force && attorneysKnown && !attorney && (
              <div className="panel-2 p-3 mt-4" data-testid="no-attorney">
                <div className="tag" style={{ color: 'var(--accent-2)' }}>nobody is staffed on this</div>
                <div className="text-[12.5px] mt-1.5" style={{ color: 'var(--mute)' }}>
                  <span className="mono">{row.sow_id}</span> has no assigned
                  attorney, so this cannot be authorised however many people sign
                  — the record refuses it deliberately, so a deal nobody has
                  staffed does not quietly need one fewer approval.
                  <strong> A Legal admin assigns one on the governance screen.</strong>
                </div>
              </div>
            )}

            {!row.in_force && attorneysKnown && attorney && (
              <div className="caption mt-3" data-testid="the-attorney">
                Assigned attorney: <span className="mono">{attorney.attorney}</span>.
              </div>
            )}

            {/* WHO ELSE THIS COMPANY REQUIRES, drawn only once the read has
                answered. An empty list and an unread one are different facts,
                and only one of them means "nobody else has to sign". */}
            {!row.in_force && requiredKnown && required.length > 0 && (
              <div className="caption mt-2" data-testid="the-required">
                This company also requires{' '}
                {required.map((r, i) => (
                  <React.Fragment key={r.required_approver_id}>
                    {i > 0 && ', '}
                    <span className="mono">{r.approver}</span>
                    {' '}({r.label})
                  </React.Fragment>
                ))}
                {' '}to sign this deal off. A Legal admin maintains that list on
                the governance screen.
              </div>
            )}

            {!row.in_force && requiredKnown && required.length === 0 && (
              <div className="caption mt-2" data-testid="no-required">
                No sign-off beyond the requester and the attorney is required on
                this deal — so this side of the gate has nothing outstanding.
              </div>
            )}

            {!row.in_force && (
              <div className="flex gap-2 flex-wrap mt-5 items-center">
                {/* SIGNING IS OFFERED TO EVERYBODY, and refused by the database
                    when the signer is not the person the configuration names.
                    The hat is chosen here because one person can be more than
                    one of them; who may wear it is `0012`'s to decide. */}
                <label className="section-label">sign as</label>
                <select className="font-mono" style={{ padding: '5px 9px' }}
                        aria-label="Which hat you are signing in"
                        data-testid="approve-kind"
                        value={kind} onChange={(e) => setKind(e.target.value)}>
                  {APPROVER_KINDS.map((k) => <option key={k} value={k}>{k}</option>)}
                </select>
                <ActButton className="btn btn-sm" data-testid="approve-do"
                           onClick={() => run(`approve-${row.override_id}`,
                             () => API.approveSowOverride({
                               override_id: row.override_id, approver_kind: kind }))}>
                  record my approval
                </ActButton>

                {mayAuthorise && (
                  <ActButton className="btn btn-primary" data-testid="authorise-do"
                             onClick={() => run(`authorise-${row.override_id}`,
                               () => API.authoriseSowOverride({ override_id: row.override_id }))}>
                    authorise the departure
                  </ActButton>
                )}
              </div>
            )}

            {!row.in_force && (
              <p className="caption mt-2">
                Authorising is refused while anybody above is still owed, and the
                refusal names them. It is also refused on a statement of work with
                no assigned attorney — a deal nobody has staffed must not quietly
                need one fewer signature than it should.
              </p>
            )}
          </>
        )}
    </div>
  );
}
