// The Auditor's workspace — WP-U14, the half that reads everything.
//
// THE ONE RULE THIS FILE IS WRITTEN AROUND. The Auditor changes nothing, and
// the screen has to prove it rather than assert it. So there is **no mutation
// affordance anywhere in this file — not even a disabled one.**
//
// WP-U14's common anti-pattern names the temptation exactly: a disabled button
// standing in for an absent right. A greyed-out control says "you could, but not
// now", and invites somebody to go looking for the conditions under which it
// lights up. The truth is "this was never yours", and the honest rendering of a
// right you do not hold is nothing at all. The admin console already reasons
// this way about owner decisions.
//
// So: every `onClick` in this file is a filter, a view toggle, or an export.
// There is no other kind.
//
// WHY THE AUDITOR MAY EXPORT AND THE VIEWER MAY NOT. WP-U14 gives the Auditor
// "CSV export" in terms and gives the Viewer none, deliberately (ADR-0008). The
// Auditor already reads the whole record, so a copy of what they can see adds
// nothing. The reading room shows a contract to somebody OUTSIDE the deal, and
// letting them take a copy away is a different act nobody decided. That
// asymmetry is a decision, not an inconsistency — and 0017 leaves nothing in the
// schema for a future export button to call.
//
// ON THE MARKUP: this file introduces no new CSS and no new visual vocabulary.
// It reuses TileStrip, WaitingList, Empty, PanelHead and the `waiting-row`
// classes the rest of the workspace already uses. An earlier draft invented a
// table style; there is not one table anywhere else in this application, and a
// second design language would have been a bigger cost than the pane is worth.

const { useState, useRef } = React;

// ── The chain explorer ────────────────────────────────────────────────────
function TheRecordPane({ me }) {
  const pane = usePane(() => API.record());
  const health = usePane(() => API.health());
  // The shared filter, above every early return as hooks must be.
  const filter = useListFilter(pane.rows, {
    view: 'record:chain',
    fields: ['event_type', 'actor', 'subject'],
    facet: 'actor_kind',
  });
  const [dense, setDense] = useState(false);

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

  // Filtering over rows the POLICY already returned — the same distinction the
  // access-history pane draws. Nothing here narrows a fetch that was broader
  // than this reader is entitled to; the fetch was already scoped by
  // cw.audit_event's read policy. An auditor is entitled to all of it, which is
  // precisely why this pane must not become the template for a narrower role.
  // ONE FILTER. This pane held its own copy of the search-and-facet logic —
  // one of five, beside the shared hook. Same behaviour, with one improvement
  // that came free: useListFilter lowercases both sides, so the search is now
  // case-insensitive on every field rather than on some of them.
  const rows = filter.shown;

  // THE EXPORT CARRIES ITS OWN SCOPE. This wrote `filter.shown` into a file
  // called `the-record.csv` whatever was in the search box. Measured in one
  // sitting: 379 rows with no filter, 87 with "agreement", 3 with "r.vance",
  // and nothing in the file or its name told them apart once it had left the
  // building. The counts now travel — in the button before it is pressed, and
  // in the filename afterwards.
  const csv = () => downloadCsv({
    stem: 'the-record',
    head: ['seq', 'ts', 'actor', 'actor_role', 'actor_kind', 'event_type', 'subject', 'payload'],
    rows,
    total: filter.total,
    // WHO TOOK IT. `me.person` is the account the doorway bound this
    // request to, not anything this page was told by the browser.
    by: me && me.person,
    cell: (e, k) => (k === 'payload' ? JSON.stringify(e.payload ?? {}) : e[k]),
  });

  // The verified state comes from cw.health_summary, not from this screen. A
  // page that marked its own reading as verified would be checking itself.
  //
  // `never_ran` is its own state throughout this product and is NOT folded into
  // a failure: a check nobody has run and a check that failed are different
  // facts, and collapsing them tells an auditor a comforting lie in one
  // direction and an alarming one in the other.
  // The tile is named 'audit chain' in cw.health_summary. Worth stating: the
  // first draft looked for 'chain', found nothing, and rendered "not available"
  // — which is indistinguishable on screen from the health check genuinely
  // having no answer. A lookup that misses should not be able to impersonate a
  // real state, so `missing` is now its own case below.
  const chain = health.status === 'loaded'
    ? health.rows.find((t) => t.tile === 'audit chain') : null;

  // COUNTED OFF THE WINDOW, not off what is currently shown. A figure computed
  // from the narrowed set cannot be a control — press it and it becomes its own
  // total, which is S360's lesson from the obligations strip. This counts the
  // rows the read answered, and the focus selects exactly the same predicate.
  // ONE PREDICATE FOR THE FIGURE AND ITS DRILL, by construction: the count and
  // the focus share it, so they cannot come apart. Registered at render via
  // `focusable`, so a saved view can put the focus back (handoff 43 §7).
  const isMachineAct = (e) => e.actor_kind !== 'human';
  const machineActs = pane.rows.filter(isMachineAct).length;
  const machineFocus = filter.focusable('machine', 'written by a machine', isMachineAct);

  return (
    <div>
      <PaneHead
        title="The record"
        sub="Every governed act, newest first, in the order it was appended."
        right={<button className="btn btn-sm" onClick={csv} data-testid="export-record">
          {csvLabel(rows.length, filter.total)}
        </button>}
      />

      {/* ── THREE FIGURES, AND TWO OF THEM ARE NOW CONTROLS ──────────────
          `acts shown` was the THIRD rendering of one fact on this pane: it
          equalled `FilterCount` ten pixels below it, which sat beside a
          literal `N of M` saying the same thing again. Three copies of one
          number, none of them pressable. It is replaced by the figure an
          auditor of THIS product actually wants and could not see — how much
          of the record a machine wrote — which the facet could already narrow
          to and which nothing was telling anybody the size of.

          The chain tile stays inert on purpose: it reports a STATE, not a set,
          and there are no rows behind `never_ran` to show. */}
      <TileStrip tiles={[
        {
          label: health.status === 'failed' ? 'audit chain — health unreadable'
            : chain && chain.state === 'never_ran' ? 'audit chain — nobody has verified it yet'
            : chain && chain.detail ? `audit chain — ${chain.detail}`
            : 'audit chain',
          n: health.status === 'loading' ? '…'
            : health.status === 'failed' ? 'unknown'
            : chain ? chain.state : 'unknown',
        },
        // THE WAY BACK. It clears ALL THREE controls, not just the focus:
        // `focusOn` already clears the search and the facet on the way in
        // (S333), so a tile that dropped only the focus on the way out would
        // leave a needle behind and answer "show all 451" with fewer.
        { label: 'in the window', n: pane.rows.length,
          to: () => { filter.setQ(''); filter.setPick(''); filter.setFocus(null); },
          on: !filter.filtering,
          describe: `show all ${pane.rows.length} acts in the window` },
        // WRITTEN BY A MACHINE, COUNTED. `actor_kind` is the column that keeps
        // this product's central promise checkable — a machine approval can
        // never be read as a human one — and until now the number was on no
        // screen at all. Anything that is not `human` counts, so a seventh
        // kind added to the record appears here on the day it exists rather
        // than being silently excluded by a list somebody wrote out.
        { label: 'written by a machine', n: machineActs,
          to: () => filter.focusOn(machineFocus),
          on: Boolean(filter.focus && filter.focus.key === 'machine'),
          describe: `show the ${machineActs} acts a machine wrote` },
      ]} />

      <ListFilter filter={filter} testid="record"
                  placeholder="actor, act or subject"
                  facetLabel="every actor kind" />
      <div className="list-filter flex gap-2 mb-3 mt-4">
        <div className="caption self-center"><FilterCount filter={filter} /></div>
        <button className="btn btn-sm self-center" onClick={() => setDense(!dense)}
                data-testid="record-view">
          {dense ? 'timeline' : 'compact'}
        </button>
        {/* THE THIRD COPY OF `N of M` IS GONE. `FilterCount` two elements to
            the left renders exactly this, from the same filter, in the wording
            every other list in the application uses. Two hand-written copies
            of one figure is how they come to disagree — and this one had
            already drifted in shape, printing `N of M` where the shared
            component prints the bare total when nothing is filtered. */}
      </div>

      {rows.length === 0 ? (
        <Empty
          kicker="the record"
          line="No recorded act matches that."
          sub="The record itself is not empty — clear the filters to see it." />
      ) : dense ? (
        // The compact view. Same rows, one line each, sequence number first —
        // for reading a run of acts rather than examining one.
        <div className="panel" data-testid="record-compact">
          {rows.map((e) => (
            <div className="waiting-row" key={e.seq}>
              <div className="min-w-0 flex gap-3">
                <span className="font-mono caption shrink-0" style={{ width: 48 }}>{e.seq}</span>
                <span className="font-mono text-[13px] truncate">{e.event_type}</span>
                <span className="caption truncate">{e.subject ?? ''}</span>
              </div>
              <div className="flex items-center gap-3 shrink-0">
                <span className="caption">{e.actor}</span>
                <span className="waiting-age" title={e.ts ?? ''}>{since(e.ts)}</span>
              </div>
            </div>
          ))}
        </div>
      ) : (
        <WaitingList
          order="newest"
          items={rows.map((e) => ({
            key: e.seq,
            title: `${e.event_type}${e.subject ? ` · ${e.subject}` : ''}`,
            // A null actor_role is the OWNER, which holds no application role
            // (decision U3). Said out loud rather than left blank, because a
            // blank reads as missing data when it is a fact.
            sub: `${e.actor} ${e.actor_role ? `as ${e.actor_role}` : '— no application role'}`,
            at: e.ts,
            chips: <span className={`chip ${e.actor_kind === 'human' ? 'chip-std'
              : e.actor_kind === 'controller' ? 'chip-pending' : 'chip-unknown'}`}>
              {e.actor_kind}
            </span>,
          }))}
          empty={null}
        />
      )}

      <p className="caption mt-3">
        The newest 500 acts. Whether the chain verifies is reported by the health
        check above, not decided by this screen.
      </p>

      {/* NG-4, owner decision NI-2: the negotiation beside the rest of the
          agreement's chain rather than behind a tab of its own. An auditor
          reads an agreement, not a subsystem. */}
      <NegotiationRecordForAudit />
    </div>
  );
}

// ── The negotiation record, read by the auditor (NG-4) ────────────────────
// READ ONLY, AND STRUCTURALLY SO. There is no act on this surface and no
// import of one: the auditor's grant is select-only across the negotiation
// family, so an act offered here would be a button that refuses. What it shows
// is the chain of one negotiation — what was exchanged, what was contested,
// how each point moved, and what reopened.
function NegotiationRecordForAudit() {
  const negotiations = usePane(() => API.negotiations());
  const rounds    = usePane(() => API.negotiationRounds());
  const positions = usePane(() => API.positions());
  const movements = usePane(() => API.positionMovements());
  const revivals  = usePane(() => API.revivals());
  const [open, setOpen] = useState('');

  if (negotiations.status === 'loading') return null;
  if (negotiations.status === 'failed') {
    return (
      <div className="mt-8">
        <PanelHead title="Negotiations" />
        <LoadFailed reason={negotiations.reason} />
      </div>
    );
  }

  const chosen = negotiations.rows.find((n) => String(n.negotiation_id) === open);
  const mine = (rows, key = 'negotiation_id') => (rows ?? [])
    .filter((r) => chosen && String(r[key]) === String(chosen.negotiation_id));

  // A movement carries its negotiation on the row (the read joins the position
  // to reach it), so the same filter works and nothing is matched by walking a
  // second list in the browser.
  const moved = mine(movements.rows);

  return (
    <div className="mt-8 pt-6 border-t hair" data-testid="audit-negotiations">
      <PanelHead
        title="Negotiations"
        sub="What was exchanged and what was contested, for one agreement at a time. Nothing on this surface can be acted on." />

      {negotiations.rows.length === 0
        ? <div className="caption">No negotiation is on the record.</div>
        : (
          <>
            <select className="font-mono" style={{ padding: '5px 9px', width: 320 }}
                    data-testid="audit-negotiation" aria-label="Which negotiation to read"
                    value={open} onChange={(e) => setOpen(e.target.value)}>
              <option value="">choose a negotiation…</option>
              {negotiations.rows.map((n) => (
                <option key={n.negotiation_id} value={String(n.negotiation_id)}>
                  {n.agreement_id} — {n.paper === 'ours' ? 'our paper' : 'their paper'}
                  {n.renews_agreement_id ? ` (renews ${n.renews_agreement_id})` : ''}
                </option>
              ))}
            </select>

            {chosen && (
              <div className="mt-4">
                <div className="panel p-4">
                  <div className="section-label">Opened</div>
                  <div className="text-[12.5px] mt-1" style={{ color: 'var(--mute)' }}>
                    {chosen.opened_on} by <span className="font-mono">{chosen.opened_by}</span>,
                    from {chosen.baseline === 'executed_agreement'
                      ? "last term's executed positions" : 'current library standard'}
                    {chosen.baseline_chosen_by
                      && <> · chosen by <span className="font-mono">{chosen.baseline_chosen_by}</span></>}
                  </div>
                  {chosen.baseline_note && (
                    <div className="font-serif italic mt-2"
                         style={{ fontSize: 14, color: 'var(--mute)' }}>
                      {chosen.baseline_note}
                    </div>
                  )}
                </div>

                <div className="panel p-4 mt-4">
                  <div className="section-label">Rounds</div>
                  {mine(rounds.rows).length === 0
                    ? <div className="caption mt-1">Nothing has been exchanged.</div>
                    : mine(rounds.rows)
                        .sort((a, b) => a.round_no - b.round_no)
                        .map((r) => (
                          <div className="py-2 border-b hair" key={r.round_no}>
                            <span className="font-mono text-[12.5px]">round {r.round_no}</span>
                            <span className="ml-3 caption">
                              {r.direction} · {r.sent_on} · recorded by {r.actor}
                            </span>
                            <div className="caption font-mono mt-0.5">{r.document_sha256}</div>
                          </div>
                        ))}
                </div>

                <div className="panel p-4 mt-4">
                  <div className="section-label">Positions, and how each moved</div>
                  {mine(positions.rows).length === 0
                    ? <div className="caption mt-1">No point was contested.</div>
                    : mine(positions.rows).map((p) => (
                        <div className="py-2 border-b hair" key={p.position_id}>
                          <div>
                            <span className="font-mono text-[12.5px]">{p.category_key}</span>
                            <span className="ml-3 text-[12.5px]">{p.state}</span>
                            {p.current_rung !== null && p.current_rung !== undefined
                              && <span className="ml-2 caption">rung {p.current_rung}</span>}
                            <span className="ml-2 caption">
                              raised round {p.round_raised} from {p.opened_from.replace('_', ' ')}
                            </span>
                          </div>
                          {moved.filter((m) => m.position_id === p.position_id)
                                .sort((a, b) => a.movement_id - b.movement_id)
                                .map((m) => (
                            <div className="caption mt-1" key={m.movement_id}
                                 style={{ paddingLeft: 16 }}>
                              round {m.round_no} → {m.to_state}
                              {m.current_rung !== null && m.current_rung !== undefined
                                && ` (rung ${m.current_rung})`}
                              {' '}· <span className="font-mono">{m.actor}</span>
                              {m.note && ` · “${m.note}”`}
                            </div>
                          ))}
                        </div>
                      ))}
                </div>

                <div className="panel p-4 mt-4">
                  <div className="section-label">Reopened after settling</div>
                  {/* Empty is good news and says so — a blank area here reads
                      as a screen that failed to load. */}
                  {mine(revivals.rows).length === 0
                    ? <div className="caption mt-1">
                        None. No settled point was argued again.
                      </div>
                    : mine(revivals.rows).map((r) => (
                        <div className="py-2 border-b hair" key={r.position_id}>
                          <span className="font-mono text-[12.5px]">{r.category_key}</span>
                          <span className="ml-3 caption">
                            held {r.times_held} times, rounds {r.first_held_round}–{r.last_round}
                          </span>
                        </div>
                      ))}
                </div>
              </div>
            )}
          </>
        )}
    </div>
  );
}

// ── How much of the machine's wording survived ────────────────────────────
//
// THREE REPORTS, SERVED SINCE 0048 AND REACHABLE BY NOTHING until 2026-08-23.
// `GET /quality/edit`, `/quality/edit/by-category` and
// `/quality/edit/by-agreement` were all registered in the doorway, granted to
// this pane's exact readers, and offered by no method in `api.jsx` — so no
// screen could call them and the census that exists to catch this could not
// see them, because it derives its set from `api.jsx` and they were never in
// it. PRODUCT.md's own warning, one layer lower than the guard reaches.
//
// WHY IT MATTERS THAT IT IS HERE. The strip above says how OFTEN an approval
// went through untouched. This says how much of the machine's wording survived
// when somebody DID edit. They are the two halves of ADR-0010's question and
// only one of them was on a screen: a 0% unedited rate reads as vigilance
// until you learn that every edit moved three words.
//
// `measured` (0101) IS THE SET THE MEAN WAS COMPUTED OVER. avg() and min()
// skip nulls, and a ticket verified before 0029 carries no figure — the check
// that requires one is NOT VALID and grandfathered them. On a database built
// from scratch `measured` equals `verified` and the caveat below never draws;
// where they differ, it is the only thing that can say so.
// THE BAR DRAWS COUNTS, AND THE FIRST DRAFT OF IT DID NOT — caught by driving
// the screen rather than by reading it. It scaled the retained-language MEAN to
// a grain of 1000 and handed BandBar `51` and `949`, so the key read "51 of
// 1000 · 5.1% of the drafted wording kept". Those are not counts of anything.
// BandBar's whole contract, written above it in common.jsx, is that every band
// is a count so the picture cannot disagree with the numbers — and a mean is
// not a proportion of a population, it is an average over one.
//
// SO THE BAR DRAWS WHAT IS ACTUALLY A POPULATION: of the approvals in this cut,
// how many went through untouched, how many were edited, and how many carry no
// figure at all. That is three real counts that sum to `verified`, it compares
// across rows the way a bar should, and it says something the tiles do not.
// The mean stays a number, printed, which is what anybody quotes.
function retainedBands(row) {
  const verified = Number(row.verified ?? 0);
  const measured = Number(row.measured ?? verified);
  const edited = Number(row.edited ?? 0);
  const mean = row.mean_retained === null || row.mean_retained === undefined
    ? null : Number(row.mean_retained);
  if (!verified) return null;
  return {
    mean: mean !== null && Number.isFinite(mean) ? mean : null,
    verified,
    unmeasured: verified - measured,
    // TWO BANDS, AND THE SECOND DRAFT HAD THREE — caught by rendering the case
    // the seeded data cannot reach. A third band counted the approvals carrying
    // no retained-language figure, which is NOT part of this partition: a
    // ticket can be edited AND unmeasured, so the bands double-counted and
    // summed to 52 over a total of 40. The bar then lied about every width.
    //
    // A BAND IS A PART OF THE WHOLE, never just another number that happens to
    // be nearby. Reaching for a third band because there was a third figure is
    // the mistake; the unmeasured count is an orthogonal fact and it belongs in
    // the caption beside the row, where it now is.
    bands: [
      { key: 'untouched', tone: 'ok', n: verified - edited,
        label: 'approved with the wording untouched' },
      { key: 'edited', tone: 'warn', n: edited,
        label: 'edited before approval' },
    ],
  };
}

function RetainedRow({ row, name, testid }) {
  const b = retainedBands(row);
  const verified = Number(row.verified ?? 0);
  const measured = Number(row.measured ?? 0);
  return (
    <div className="waiting-row" style={{ alignItems: 'flex-start', display: 'block' }}
         data-testid={testid}>
      <div className="flex gap-3" style={{ alignItems: 'flex-start' }}>
        <div className="min-w-0" style={{ flex: '1 1 auto' }}>
          <div className="text-[13px]" style={{ color: 'var(--ink)' }}>{name}</div>
          <div className="caption mt-0.5">
            {verified} verified · {row.edited ?? 0} edited before approval
            {measured < verified && (
              <span> · <strong>{measured}</strong> carry a figure</span>
            )}
          </div>
        </div>
        <span className="caption shrink-0">
          {/* A MEAN OVER NOTHING IS NOT ZERO. cw.edit_quality returns null when
              no verified ticket in the cut carries a figure, and rendering that
              as 0% would report that a machine's every word was replaced. */}
          {b && b.mean !== null
            ? `${(b.mean * 100).toFixed(1)}% kept` : 'no figure recorded'}
          {row.least_retained !== null && row.least_retained !== undefined
            && ` · least ${(Number(row.least_retained) * 100).toFixed(1)}%`}
        </span>
      </div>
      {b && (
        <div className="mt-2">
          <BandBar bands={b.bands} total={b.verified}
                   empty="nothing verified in this cut" />
        </div>
      )}
    </div>
  );
}

function RetainedLanguage() {
  const overall = usePane(() => API.editQuality());
  const byCategory = usePane(() => API.editQualityByCategory());
  const byAgreement = usePane(() => API.editQualityByAgreement());
  // ONE FILTER EACH, over rows the read already returned. Both cuts can grow
  // past what anybody scrolls: a category list is bounded by the registry, an
  // agreement list is not bounded at all.
  const catFilter = useListFilter(
    byCategory.status === 'loaded' ? byCategory.rows : [],
    { view: 'origin-mix:by-category', fields: ['category_key'] });
  const agFilter = useListFilter(
    byAgreement.status === 'loaded' ? byAgreement.rows : [],
    { view: 'origin-mix:by-agreement', fields: ['agreement_id'] });

  if (overall.status === 'loading') return <Loading />;
  // A REFUSAL IS AN ANSWER, AND IT IS ONLY THIS QUESTION'S ANSWER. The grant
  // on these three views is legal_reviewer, legal_admin and auditor — the same
  // three the strip above has had since 0008 — so a refusal here means the
  // grant changed, and it is reported rather than drawn as an empty report.
  if (overall.status === 'failed') return <LoadFailed reason={overall.reason} />;

  const q = overall.rows[0] ?? {};
  const verified = Number(q.verified ?? 0);
  const measured = Number(q.measured ?? 0);
  const mean = q.mean_retained === null || q.mean_retained === undefined
    ? null : Number(q.mean_retained);
  const threshold = q.threshold === null || q.threshold === undefined
    ? null : Number(q.threshold);

  return (
    <div className="mt-8" data-testid="retained-language">
      <PanelHead
        title="How much of the machine's wording survived"
        sub="The retained-language figure. The rate above says how often nobody edited; this says how much was kept when somebody did." />

      {/* EVERY FIGURE HERE IS A MEAN, A MINIMUM OR A THRESHOLD — a derivation
          over a set rather than the set — so none of them is a control, which
          is S363's taxonomy rather than an oversight. The cuts below are where
          somebody goes to look further, and they are lists. */}
      <TileStrip tiles={[
        { label: mean === null ? 'mean retained — nothing measured yet'
                               : `mean retained · from ${measured} of ${verified}`,
          n: mean === null ? '—' : `${(mean * 100).toFixed(1)}%` },
        { label: 'least retained — the most heavily rewritten approval',
          n: q.least_retained === null || q.least_retained === undefined
            ? '—' : `${(Number(q.least_retained) * 100).toFixed(1)}%` },
        { label: 'edited before approval', n: Number(q.edited ?? 0) },
        { label: threshold === null ? "below Legal's threshold — none is set"
                                    : `below Legal's threshold of ${(threshold * 100).toFixed(0)}%`,
          n: q.below_threshold === null || q.below_threshold === undefined
            ? '—' : Number(q.below_threshold) },
      ]} />

      {measured < verified && (
        <div className="caption mt-2" data-testid="retained-partial">
          <strong>The mean was computed from {measured} of {verified} verified
          approvals.</strong>{' '}
          The other {verified - measured} carry no figure — they were verified
          before the record began storing one, and they average as absent rather
          than as zero. Imputing a zero would invent a terrible score for work
          nobody measured.
        </div>
      )}

      {threshold === null && (
        <div className="caption mt-2" data-testid="retained-no-threshold">
          <strong>No threshold is set</strong>, so nothing can be below one —
          which is why that figure reads as unmeasured rather than as nought.
          What the number should be is Legal's to set with counsel; the system
          measures it and shows it, and must never pick it.
        </div>
      )}

      <div className="mt-6">
        <PanelHead title="By clause category"
                   sub="Where the drafting holds up, and where it does not." />
        {byCategory.status === 'failed'
          ? <LoadFailed reason={byCategory.reason} />
          : byCategory.rows.length === 0
            ? <Empty kicker="by category" line="Nothing verified in any category yet."
                     sub="A cut needs verified approvals to cut. None exists —
                          that is the whole answer, not a filter." />
            : (
              <>
                <ListFilter filter={catFilter} testid="retained-category"
                            placeholder="category" />
                <div className="panel">
                  {catFilter.shown.map((r) => (
                    <RetainedRow key={r.category_key} row={r}
                                 name={r.category_key} testid="retained-category-row" />
                  ))}
                </div>
              </>
            )}
      </div>

      <div className="mt-6">
        <PanelHead title="By contract"
                   sub="Tickets opened outside any deal group under their own row, kept visible rather than dropped." />
        {byAgreement.status === 'failed'
          ? <LoadFailed reason={byAgreement.reason} />
          : byAgreement.rows.length === 0
            ? <Empty kicker="by contract" line="Nothing verified against any contract yet."
                     sub="Same answer as above, cut a different way." />
            : (
              <>
                <ListFilter filter={agFilter} testid="retained-agreement"
                            placeholder="contract reference" />
                <div className="panel">
                  {agFilter.shown.map((r) => (
                    <RetainedRow key={r.agreement_id ?? 'none'} row={r}
                                 name={r.agreement_id || 'opened outside any deal'}
                                 testid="retained-agreement-row" />
                  ))}
                </div>
              </>
            )}
      </div>
    </div>
  );
}

// ── Review quality ────────────────────────────────────────────────────────
// The unedited-approval rate, which is the figure Legal watches because the
// pressure it measures is real: a fluent draft is approved faster than a blank
// page is filled.
function QualityPane() {
  const pane = usePane(() => API.quality());
  // THE SET THE FIGURES ABOVE WERE COUNTED FROM, and until 2026-08-23 no screen
  // in the application had ever shown it to this role. `0008` grants the
  // Auditor `select on cw.review_ticket` in the same statement that grants them
  // `cw.review_quality`, and only the second of the two ever reached a pane —
  // so the auditor could read "1 verified, 0 rejected" and could not see the
  // ticket. That is the built-with-no-screen defect at the level of a GRANT
  // rather than an endpoint, which neither census in
  // `a-built-thing-has-a-way-in.test.mjs` can see: `GET /tickets` HAS a caller,
  // on the Legal reviewer's desk.
  //
  // ONE SET, BY CONSTRUCTION RATHER THAN BY COINCIDENCE. `cw.review_quality` is
  // a plain aggregate over `cw.review_ticket` with NO WHERE CLAUSE, and
  // `GET /tickets` selects from the same table with none either — so both go
  // through the same row policy for the same caller and cannot disagree. That
  // is provable from the SQL, and it is the property the figure-and-its-drill
  // guard exists to protect.
  const tickets = usePane(() => API.tickets());
  // Hooks BEFORE the early returns below. A hook after `if (…) return
  // <Loading />` blanks the pane on the render after the data lands — the one
  // render nobody watches (S318).
  const filter = useListFilter(tickets.rows, {
    view: 'quality:tickets',
    fields: ['ticket_id', 'agreement_id', 'category_key', 'severity',
             'provenance_badge', 'reason_code', 'decided_by', 'decision_note'],
    facet: 'state',
  });
  const register = useRef(null);

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

  const q = pane.rows[0] ?? {};
  const verified = Number(q.verified ?? 0);
  const rate = q.unedited_rate;

  // WHAT EACH FIGURE NARROWS TO. Through `focusOn`, which clears the search box
  // and the state filter on the way — a needle left over from five minutes ago
  // answers a figure promising 1 with 0 rows while the figure still says 1
  // (S319/S333). The rows are brought into view rather than the reader having
  // to go looking for what they just pressed.
  const focusState = (state) => {
    // Registered at render — focusState is called building each figure — so a
    // saved view can put the focus back (handoff 43 §7).
    const f = filter.focusable(state, `${state} tickets`, (t) => t.state === state);
    return () => {
      filter.focusOn(f);
      requestAnimationFrame(showSection(register));
    };
  };
  // A figure only leads somewhere once the list it leads to has arrived. A
  // failed or still-loading read leaves it inert rather than pressable-and-
  // empty, which would be a control that lies about where it goes.
  const canDrill = tickets.status === 'loaded';
  // NULL IS NOT ZERO, and this is the distinction the whole pane turns on.
  // cw.review_quality returns null when nothing has been verified — there is no
  // denominator. Rendering that as 0% would report perfect discipline from an
  // empty queue, which is the most flattering possible lie.
  const unmeasured = rate === null || rate === undefined;

  return (
    <div>
      <PaneHead
        title="Review quality"
        sub="How often an approval went through with the wording untouched." />

      <TileStrip tiles={[
        {
          label: unmeasured ? 'approved unedited — nothing verified yet'
                            : `approved unedited · ${q.verified_unedited} of ${verified}`,
          n: unmeasured ? '—' : `${Math.round(Number(rate) * 100)}%`,
        },
        {
          label: 'verified — wording that entered the library',
          n: verified,
          to: canDrill ? focusState('verified') : null,
          on: filter.focus?.key === 'verified',
          describe: `show the ${verified} verified tickets`,
        },
        {
          label: 'rejected — turned back, each with a reason',
          n: Number(q.rejected ?? 0),
          to: canDrill ? focusState('rejected') : null,
          on: filter.focus?.key === 'rejected',
          describe: `show the ${Number(q.rejected ?? 0)} rejected tickets`,
        },
      ]} />

      {unmeasured && (
        <p className="caption mt-4" data-testid="quality-unmeasured">
          No approvals to measure yet. That is <strong>not</strong> a rate of
          zero, and it is not a rate of one hundred — there is nothing to divide.
        </p>
      )}

      <p className="caption mt-3">
        Measured from what was stored, not reported by anyone.
        <strong> What the number should be is Legal's to set with counsel</strong> —
        the system measures it and shows it, and must never pick it.
      </p>

      {/* ── THE TICKETS THE FIGURES WERE COUNTED FROM ──────────────────────
          Read-only, and it says so. The Auditor reads everything and changes
          nothing (ADR-0011): no claim, no verify, no reject is offered here,
          and the database would refuse all three regardless — `0008` grants
          this role SELECT and no UPDATE. */}
      <div className="mt-8 pt-6 border-t hair" ref={register}
           data-testid="quality-register">
        <PanelHead
          title="The tickets these numbers were counted from"
          sub="Every ticket the record holds, with what was decided about it and by whom. Nothing here can be acted on."
          right={<FilterCount filter={filter} />} />

        {tickets.status === 'loading' ? <Loading />
          : tickets.status === 'failed' ? <LoadFailed reason={tickets.reason} />
          : tickets.rows.length === 0 ? (
            <Empty
              kicker="review queue"
              line="No ticket has ever been raised."
              sub="An empty queue, not a failed read — the figures above are
                   counted from this same table, and they say nought too." />
          ) : (
            <>
              <ListFilter filter={filter} testid="quality-tickets"
                          placeholder="ticket, agreement, category, source or decider"
                          facetLabel="every state" />
              {filter.shown.length === 0
                ? <NoMatch kicker="review queue" noun="ticket" />
                : (
                  <div className="panel">
                    <table className="ledger">
                      <thead>
                        <tr>
                          <th>Ticket</th><th>Agreement</th><th>Category</th>
                          {/* #153. A column of its own rather than a second
                              quantity under "Source": whose words these are
                              and what sort of thing is being proposed are two
                              different facts, and the badge already answers
                              the first. The auditor gets the WORD on every
                              row, not a chip on the unusual ones — a register
                              that states a fact by omitting it is not one. */}
                          <th>Kind</th>
                          <th>Source</th><th>State</th><th>Decided</th>
                          <th style={{ textAlign: 'right' }}>Wording</th>
                        </tr>
                      </thead>
                      <tbody>
                        {filter.shown.map((t) => (
                          <tr key={t.ticket_id}>
                            <td className="mono">{t.ticket_id}</td>
                            <td className="mono">{t.agreement_id}</td>
                            <td>
                              {t.category_key}
                              <span className="caption"> · {t.severity}</span>
                            </td>
                            <td>{proposalKind(t).label}</td>
                            {/* The badge the record carries, unchanged. Where
                                a wording came from is the auditor's question,
                                and it is immutable on the ticket. */}
                            <td><span className="chip chip-std">{t.provenance_badge}</span></td>
                            <td><Status state={t.state}>{t.state}</Status></td>
                            <td>
                              {t.decided_by
                                ? <><span className="mono">{t.decided_by}</span>
                                    <span className="caption"> · {String(t.decided_on ?? '').slice(0, 10)}</span></>
                                : <span className="caption">not decided</span>}
                            </td>
                            {/* EDITED IS A THREE-WAY ANSWER, not a tick box.
                                `edited_before_approval` is null until a ticket
                                is decided, and null is not false — "nobody has
                                approved this yet" and "approved with the
                                wording untouched" are different facts, and it
                                is the second that the rate above is made of. */}
                            <td style={{ textAlign: 'right' }}>
                              {t.edited_before_approval === null
                                || t.edited_before_approval === undefined
                                ? <span className="caption">—</span>
                                : t.edited_before_approval
                                  ? <span className="chip chip-pending">edited</span>
                                  : <span className="chip chip-ok">untouched</span>}
                            </td>
                          </tr>
                        ))}
                      </tbody>
                    </table>
                  </div>
                )}
            </>
          )}
      </div>

      {/* THE OTHER HALF OF THE SAME QUESTION, and it had three finished reports
          and no screen. Below rather than beside: the unedited rate is the
          headline Legal watches, and this explains what the edits did. */}
      <RetainedLanguage />
    </div>
  );
}

// ── Origin mix ────────────────────────────────────────────────────────────
// Where the approved wording actually came from. The provenance question
// ADR-0006 exists to keep answerable: how much of our library did a machine
// draft?
// ── HOW EVERY VERSION GOT INTO THE LIBRARY (0008; issue #212) ─────────────
//
// `cw.clause_entrance` derives, for every clause version, the DOOR it came
// through — a review ticket, a promoted concession, or the founding seed —
// and marks anything that came through none of them UNACCOUNTED. The view's
// own comment in 0008 says what that means: *"a clause nobody can explain,
// and there should never be one."*
//
// IT WAS SERVED AND IT REACHED NOBODY. `GET /entrance` has been in the
// doorway's read table since the view was written and `api.jsx` has offered
// the `entrance` method; not one pane called it. So in a product whose
// central promise is that the origin of every clause is recorded permanently,
// the one view that names the clauses whose origin CANNOT be accounted for
// was invisible to every person in the building.
//
// IT IS NOT `provenance_gap`, AND THE TWO MUST NOT BE CONFLATED. The library
// pane already draws `cw.library_entry.provenance_gap`, which flags a version
// with no approval or expiry DATE. This asks an entirely different question —
// which door — and the two answer about different rows: a version can carry
// both dates and still be UNACCOUNTED, and a seeded version with no dates is
// a provenance gap that is perfectly well accounted for. They were checked
// against each other before this was built, and the sentence is here so
// nobody undoes that by assuming they overlap.
//
// THE TAB IS ALREADY WHERE THE GRANT IS, so this needs no new tab and no role
// check. 0008 grants SELECT on cw.clause_entrance to cw_legal_reviewer,
// cw_legal_admin and cw_auditor in one statement, and `origin-mix` sits on
// exactly those three rails and no others. The origin-mix pane already asks
// the neighbouring question — what ORIGIN the wording carries — so the door
// it came through belongs beside it rather than on a rail of its own.
//
// AN EMPTY RESULT IS THE GOOD ANSWER, AND IS DRAWN AS ONE. "Every version in
// the library can be accounted for" and "this read returned nothing" must not
// look the same, so the clean state says what it was measured over and says
// that the panel is an answer rather than an absent read — the shape
// `portfolio` and `departures` already use for exactly this.
const ENTRANCE_DOORS = [
  { key: 'review_ticket',        label: 'through a review ticket',      chip: 'chip-ok' },
  { key: 'concession_promotion', label: 'promoted from a concession',   chip: 'chip-std' },
  { key: 'seeded',               label: 'in the founding library',      chip: 'chip-std' },
  { key: 'UNACCOUNTED',          label: 'through no door on record',    chip: 'chip-err' },
];

// The catalogue the saved view puts a focus back from. A tile narrows the
// register to the set it counted; the key is what survives a save, and this
// is the pane's half of that bargain (0110).
const ENTRANCE_FOCUSES = ENTRANCE_DOORS.map((d) => ({
  key: d.key, label: d.label, test: (r) => r.entrance === d.key,
}));
const entranceFocus = (key) => ENTRANCE_FOCUSES.find((f) => f.key === key) || null;
// A door the view learns to name later still draws, muted, rather than
// vanishing from the register. An unknown door is not the same as no row.
const entranceDoor = (key) =>
  ENTRANCE_DOORS.find((d) => d.key === key) || { key, label: key, chip: 'chip-std' };

function ClauseEntrance() {
  const pane = usePane(() => API.entrance());
  // Above every early return, as hooks must be.
  const filter = useListFilter(pane.status === 'loaded' ? pane.rows : [], {
    view: 'origin-mix:entrance',
    focuses: ENTRANCE_FOCUSES,
    fields: ['clause_id', 'reviewer', 'provenance', 'entrance'],
    facet: 'entrance',
  });

  if (pane.status === 'loading') return <Loading />;
  // A REFUSAL IS AN ANSWER AND IT IS ONLY THIS QUESTION'S ANSWER. The grant is
  // the three roles that hold this tab, so a refusal here means the grant
  // changed — reported, never drawn as a library that can all be accounted
  // for. Saying "nothing is unaccounted" to somebody the database refused
  // would be the worst possible reading of a failed read.
  if (pane.status === 'failed') return <LoadFailed reason={pane.reason} />;

  const all = pane.rows;
  const unaccounted = all.filter((r) => r.entrance === 'UNACCOUNTED');
  const counted = (key) => all.filter((r) => r.entrance === key).length;
  const rows = filter.shown;

  return (
    <div className="mt-8 pt-6 border-t hair" data-testid="clause-entrance">
      <PanelHead
        title="How every version got into the library"
        sub="The door each clause version came through — a review ticket, a promoted concession, or the founding library — and any that came through none of them."
        right={<FilterCount filter={filter} />} />

      {all.length === 0 ? (
        <Empty
          kicker="the doors"
          line="The library holds no clause versions, so there is no entrance to account for."
          sub="An empty library, not a failed read." />
      ) : (
        <>
          {/* THE VERDICT, BEFORE THE EVIDENCE. This is the whole reason the
              view exists, so it is stated in a sentence rather than left to
              be inferred from a tile reading nought. */}
          {unaccounted.length === 0 ? (
            <Empty
              kicker="all accounted for"
              line="Every clause version in the library came through a door the record can name."
              sub={`Measured over ${all.length} version${all.length === 1 ? '' : 's'}, every one of them. `
                 + 'An empty answer here is the answer this view exists to give, not an '
                 + 'unscoped read: the door is derived from the version’s own provenance '
                 + 'and the review ticket it cites, so a version carrying neither would appear.'} />
          ) : (
            <div className="panel mt-4 p-4" data-testid="entrance-unaccounted"
                 style={{ borderColor: 'var(--danger)', borderWidth: '2px' }}>
              <div style={{ color: 'var(--ink)', fontSize: '13px' }}>
                <span className="chip chip-err">unaccounted</span>{' '}
                <strong>
                  {unaccounted.length} clause version{unaccounted.length === 1 ? '' : 's'} cannot
                  be accounted for.
                </strong>{' '}
                Each one is approved language the library will select, and nothing on
                record says how it got in — neither a review ticket nor a promotion
                nor the founding seed. The record cannot be repaired from this
                screen; a version’s provenance is immutable once approved.
              </div>
              <ul className="mt-3" style={{ margin: 0, paddingLeft: '1.1rem' }}>
                {unaccounted.map((r) => (
                  <li className="caption font-mono" key={`${r.clause_id}@${r.version}`}
                      style={{ lineHeight: 1.7 }}>
                    {r.clause_id}@v{r.version} · provenance {r.provenance}
                    {r.reviewer ? ` · approved by ${r.reviewer}` : ' · no reviewer recorded'}
                  </li>
                ))}
              </ul>
            </div>
          )}

          {/* EVERY TILE IS A WAY INTO THE SET IT COUNTED. The total has
              nothing to narrow to, so it clears the focus instead — the
              honest act for a whole. Counted over `all`, never over the
              narrowed set, so a figure cannot describe a set the register is
              not showing. */}
          <TileStrip tiles={[
            { label: 'versions', n: all.length,
              to: () => filter.setFocus(null),
              describe: `show all ${all.length} versions`,
              on: filter.focus === null },
            ...ENTRANCE_DOORS.map((d) => ({
              label: d.label, n: counted(d.key),
              to: () => filter.focusOn(entranceFocus(d.key)),
              on: Boolean(filter.focus) && filter.focus.key === d.key,
            })),
          ]} />

          <ListFilter filter={filter} testid="entrance"
                      placeholder="clause, reviewer or door"
                      facetLabel="every door" minRows={4} />

          {rows.length === 0 ? (
            <NoMatch kicker="the doors" noun="version" />
          ) : (
            <div className="panel">
              <table className="ledger">
                <thead>
                  <tr><th>version</th><th>door</th><th>provenance</th>
                      <th>ticket</th><th>approved by</th></tr>
                </thead>
                <tbody>
                  {rows.map((r) => {
                    const door = entranceDoor(r.entrance);
                    return (
                      <tr key={`${r.clause_id}@${r.version}`}>
                        <td className="mono">{r.clause_id}@v{r.version}</td>
                        <td><span className={`chip ${door.chip}`}>{r.entrance}</span></td>
                        <td>{r.provenance}</td>
                        <td className="mono">
                          {r.source_ticket_id === null || r.source_ticket_id === undefined
                            ? '—' : `#${r.source_ticket_id}`}
                        </td>
                        <td>{r.reviewer || '—'}</td>
                      </tr>
                    );
                  })}
                </tbody>
              </table>
            </div>
          )}

          <p className="caption mt-2">
            The door is derived, never stored: a version citing a review ticket came
            through the queue, one marked <span className="font-mono">promoted</span> came
            from a settled concession, one marked <span className="font-mono">seeded</span> was
            in the library before the record began, and anything else is
            unaccounted for. <strong>This is not the same as a missing approval or
            expiry date</strong>, which the library pane flags separately — a
            version can be fully dated and still unaccounted for, and a seeded
            version with no dates is accounted for perfectly well.
          </p>
        </>
      )}
    </div>
  );
}

function OriginMixPane() {
  const pane = usePane(() => API.originMix());
  // ADR-0010's other half, per contract build: "which executed contracts
  // contain AI-originated wording?" — granted since 0009 and served by
  // nothing until 2026-08-24.
  const runs = usePane(() => API.originMixRuns());
  if (pane.status === 'loading') return <Loading />;
  if (pane.status === 'failed') return <LoadFailed reason={pane.reason} />;

  const rows = pane.rows;

  if (rows.length === 0) {
    return (
      <div>
        <PaneHead title="Origin mix" sub="Where the approved wording came from." />
        <Empty
          kicker="origin mix"
          line="The library has no clause versions, so there is no origin to report."
          sub="An empty library, not a failed read." />
      </div>
    );
  }

  const totalChars = rows.reduce((n, r) => n + Number(r.characters ?? 0), 0);

  return (
    <div>
      <PaneHead
        title="Origin mix"
        sub="Where the approved wording came from, by character as well as by count." />

      <WaitingList
        order="given"
        items={rows.map((r) => {
          const chars = Number(r.characters ?? 0);
          const share = totalChars === 0 ? 0 : Math.round((chars / totalChars) * 100);
          return {
            key: r.origin,
            title: `${r.origin} — ${share}% of the library's text`,
            sub: `${Number(r.versions ?? 0)} versions, `
               + `${Number(r.selectable_versions ?? 0)} selectable, `
               + `${chars.toLocaleString()} characters`,
            at: null,
            chips: <span className={`chip ${r.origin === 'legal_authored' ? 'chip-ok'
              : r.origin === 'external' ? 'chip-pending' : 'chip-std'}`}>
              {r.origin}
            </span>,
          };
        })}
        empty={null}
      />

      <p className="caption mt-3">
        Counted by characters as well as by version, because one long AI-drafted
        clause and one short one are not the same amount of machine authorship.
        <strong> Origin is immutable</strong> — a version's origin cannot be
        rewritten after approval, so this is a record and not a current opinion.
      </p>

      {/* The same question PER CONTRACT BUILD — ADR-0010's other half, which
          must be answerable at any moment and until 2026-08-24 was not.
          Drawn only when the read answered: a role refused it must not be
          shown "no build carries machine-drafted wording". */}
      {runs.status === 'loaded' && runs.rows.length > 0 && (() => {
        const byRun = new Map();
        for (const r of runs.rows) {
          if (!byRun.has(r.run_id)) byRun.set(r.run_id, []);
          byRun.get(r.run_id).push(r);
        }
        return (
          <div className="mt-6" data-testid="run-origin-mix">
            <PanelHead title="Origin mix per contract build"
                       sub="Which builds carry wording of which origin, by character — the question ADR-0010 keeps answerable per executed contract." />
            <div style={{ overflowX: 'auto' }}>
              <table className="ledger">
                <thead>
                  <tr><th>build</th><th>origin</th><th>clauses</th><th>characters</th></tr>
                </thead>
                <tbody>
                  {[...byRun.entries()].map(([runId, rows]) =>
                    rows.map((r, i) => (
                      <tr key={`${runId}·${r.origin}`}>
                        <td className="font-mono text-[12px]">{i === 0 ? runId : ''}</td>
                        <td>
                          <span className={`chip ${r.origin === 'legal_authored' ? 'chip-ok'
                            : r.origin === 'external' ? 'chip-pending' : 'chip-std'}`}>
                            {r.origin}
                          </span>
                        </td>
                        <td>{Number(r.clauses ?? 0)}</td>
                        <td>{Number(r.characters ?? 0).toLocaleString()}</td>
                      </tr>
                    )))}
                </tbody>
              </table>
            </div>
            <p className="caption mt-2">
              The 500 most recent builds. A build absent here decided no
              clauses, which is a real answer and not a missing row.
            </p>
          </div>
        );
      })()}

      {/* THE DOOR EACH VERSION CAME THROUGH — 0008's cw.clause_entrance, which
          nothing has ever drawn. Its own hooks, so this pane's early returns
          above cannot sit between them. */}
      <ClauseEntrance />
    </div>
  );
}
