console: fix the stale "Upload documents" CTA; refine the chain-of-thought

TWO THINGS.

1. Fix: the header "Your move → Upload documents" kept showing after documents
   were uploaded. The render already demotes Collect Documents from a step to a
   "replace or add" recovery lever once the files are in (docsDone), but the
   header CTA computed primaryAction straight off `actions` and never applied
   that demotion — so it surfaced "Upload documents" over a lead whose docs had
   just landed. The same docs-in check now guards primaryAction.

2. Refine: the chain of thought reads as a distinct AI reasoning artifact now,
   not loose text under a link. The affordance is a tinted "thinking" chip; the
   revealed reasoning sits in a soft accent-tinted panel with a stepped rail and
   markers — the record of how the machine decided, given the weight it deserves.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Yashas 2026-09-09 13:23:51 +05:30
parent 818ef871ba
commit 3cc99b718a
2 changed files with 72 additions and 21 deletions

View File

@ -1,59 +1,102 @@
.cot { margin-top: var(--sp-2); } .cot { margin-top: var(--sp-3); }
/* The finding, always visible the one sentence that answers "what did it /* The finding the one sentence that answers "what did it conclude", visible
conclude" without opening anything. */ without opening the reasoning. */
.cot__finding { .cot__finding {
margin: 0 0 var(--sp-2); margin: 0 0 var(--sp-3);
font-size: var(--fs-sm); font-size: var(--fs-sm);
line-height: var(--lh-normal); line-height: var(--lh-normal);
font-weight: var(--fw-medium); font-weight: var(--fw-medium);
color: var(--zk-ink); color: var(--zk-ink);
} }
/* THE THINKING CHIP. A quiet pill, tinted in the accent, that reads as an AI
reasoning affordance rather than a plain link the "how did it decide"
handle. */
.cot__toggle { .cot__toggle {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
gap: var(--sp-2); gap: var(--sp-2);
padding: var(--sp-1) 0; padding: 5px 12px 5px 9px;
border: 0; border: 1px solid var(--zk-blue-light);
background: none; border-radius: var(--r-pill);
background: var(--zk-tint-blue);
cursor: pointer; cursor: pointer;
font: inherit; font: inherit;
font-size: var(--fs-2xs); font-size: var(--fs-2xs);
font-weight: var(--fw-semi); font-weight: var(--fw-semi);
color: var(--zk-blue-dark); color: var(--zk-blue-dark);
transition: background var(--t-fast), border-color var(--t-fast);
} }
.cot__brain { width: 15px; height: 15px; flex: none; } .cot__toggle:hover { background: var(--zk-white); border-color: var(--zk-blue-mid); }
.cot__ct { color: var(--zk-grey); font-weight: var(--fw-normal); } .cot__brain { width: 14px; height: 14px; flex: none; }
.cot__caret { width: 11px; height: 11px; transition: transform .18s var(--ease); } .cot__ct { color: var(--zk-blue-dark); font-weight: var(--fw-normal); opacity: .72; }
.cot__caret { width: 11px; height: 11px; transition: transform .18s var(--ease); opacity: .7; }
.cot__caret.is-open { transform: rotate(90deg); } .cot__caret.is-open { transform: rotate(90deg); }
/* THE REASONING, revealed as its own artifact: a soft accent-tinted panel with
a stepped rail, so the chain reads as a distinct object the record of how
the machine got there not loose text under a link. */
.cot__steps { .cot__steps {
list-style: none; list-style: none;
margin: var(--sp-3) 0 0; margin: var(--sp-3) 0 0;
padding: var(--sp-3) 0 var(--sp-2); padding: var(--sp-4) var(--sp-5) var(--sp-3);
border-left: 2px solid var(--zk-blue-light); border: 1px solid var(--zk-blue-light);
padding-left: var(--sp-4); border-radius: var(--r-md);
background:
linear-gradient(180deg, var(--zk-tint-blue) 0%, transparent 70%),
var(--zk-white);
} }
.cot__step { position: relative; display: grid; grid-template-columns: 82px 1fr; gap: var(--sp-3); padding: var(--sp-2) 0; } .cot__step {
position: relative;
display: grid;
grid-template-columns: 76px 1fr;
gap: var(--sp-3);
padding: var(--sp-2) 0 var(--sp-2) var(--sp-4);
}
/* the marker dot */
.cot__step::before { .cot__step::before {
content: ""; position: absolute; left: calc(var(--sp-4) * -1 - 6px); top: 9px; content: "";
width: 9px; height: 9px; border-radius: 50%; background: var(--zk-white); border: 2px solid var(--zk-blue); position: absolute;
left: 2px;
top: 9px;
width: 9px; height: 9px;
border-radius: 50%;
background: var(--zk-white);
border: 2px solid var(--zk-blue);
z-index: 1;
}
/* the connector between markers */
.cot__step:not(:last-child)::after {
content: "";
position: absolute;
left: 5.5px;
top: 18px;
bottom: -6px;
width: 1.5px;
background: var(--zk-blue-light);
} }
.cot__mark { .cot__mark {
font-size: var(--fs-3xs); font-size: var(--fs-3xs);
font-weight: var(--fw-semi); font-weight: var(--fw-bold);
letter-spacing: .06em; letter-spacing: .06em;
text-transform: uppercase; text-transform: uppercase;
color: var(--zk-blue-dark); color: var(--zk-blue-dark);
padding-top: 1px; padding-top: 2px;
} }
.cot__b { font-size: var(--fs-xs); line-height: var(--lh-normal); color: var(--zk-muted); overflow-wrap: anywhere; } .cot__b { font-size: var(--fs-xs); line-height: var(--lh-normal); color: var(--zk-muted); overflow-wrap: anywhere; }
.cot__b b { color: var(--zk-ink); font-weight: var(--fw-semi); } .cot__b b { color: var(--zk-ink); font-weight: var(--fw-semi); }
.cot__foot { display: flex; flex-wrap: wrap; gap: 6px; margin-top: var(--sp-2); } .cot__foot { display: flex; flex-wrap: wrap; gap: 6px; margin-top: var(--sp-2); }
.cot__conf { font-size: 11.5px; font-weight: var(--fw-semi); color: var(--zk-blue-dark); background: var(--zk-tint-blue); border: 1px solid var(--zk-blue-light); padding: 1px 9px; border-radius: var(--r-pill); } .cot__conf {
font-size: 11.5px;
font-weight: var(--fw-bold);
color: var(--zk-blue-dark);
background: var(--zk-tint-blue);
border: 1px solid var(--zk-blue-light);
padding: 1px 10px;
border-radius: var(--r-pill);
}

View File

@ -314,8 +314,16 @@ export default function Lead() {
// instead. `role: 'do'` is the committing step never a re-run or a // instead. `role: 'do'` is the committing step never a re-run or a
// recovery lever, which must not be dressed as the thing to do. // recovery lever, which must not be dressed as the thing to do.
const acceptedAlready = Boolean(row.acceptance_ref) const acceptedAlready = Boolean(row.acceptance_ref)
// Once the documents are in, Collect Documents stops being the move the AI
// is capturing the risk from them. The render already demotes it from a step
// to a "replace or add" recovery lever (docsDone), but the header CTA read
// straight off `actions` and kept surfacing "Upload documents" over a lead
// whose documents had just been uploaded. Same demotion, applied here.
const docsInAlready = stage?.after === 'documents received'
const primaryAction = actions.find( const primaryAction = actions.find(
(a) => a.role === 'do' && !(acceptedAlready && a.uid === 'zk-act-accept'), (a) => a.role === 'do'
&& !(acceptedAlready && a.uid === 'zk-act-accept')
&& !(docsInAlready && a.uid === 'zk-act-collect-docs'),
) || null ) || null
const goToAction = () => { const goToAction = () => {
if (!primaryAction) return if (!primaryAction) return