The app read as a control panel. Every activity a stage allowed was
rendered as an equal button, so operating it required already knowing
the workflow — which is the opposite of what a workflow tool is for.
Two concrete failures, both visible in the data:
Ops on Document collection got SIX equal buttons. Exactly one was the
next step: upload the three documents. Capture Motor Risk, Capture SME
Risk, AI Cover Recommendation and Generate Quote are the AI's own chain,
present only so a stalled lead can be pushed by hand.
And FOUR stages offered a partner agent nothing but "Mark Lost" — New
Lead, Contacted, Underwriting referral, Premium confirmation. At each of
those the system was actively working the lead, and the only thing the
app suggested was giving up on it.
So an activity now carries what it IS at a stage, not only who may press
it:
do the step this stage is waiting on. Usually one. Referred has
two, because clear and decline are a decision pair rather than
a step and the option of not taking it.
again a bounded loop — retry, reminder, re-quote. Legitimate, never
the answer to "what now".
force an AI employee's own job, offered to a person only so a stalled
lead can be moved. Folded behind "Lead not moving?".
exit Mark Lost. Always reachable, never presented as a step.
The panel is "Next step". With a step, one button at full weight. Without
one it says what is actually happening and who has it, which is the true
answer at six of the nine working stages and was previously rendered as
an empty bar or a lone Mark Lost.
Result per role, checked rather than assumed: an underwriter sees two
buttons, on one stage, and nothing anywhere else. A partner agent sees a
step at three stages. Ops sees a step at four and can still force any
AI activity from the folded section.
Nothing about permissions changed, and the action list still matches
tbl_wf_state_allowed_activities exactly — this is only about which of
them is presented as the thing to do.
1151 lines
28 KiB
CSS
1151 lines
28 KiB
CSS
/* ---- page header ---- */
|
|
.page__head {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
justify-content: space-between;
|
|
gap: 24px;
|
|
margin-bottom: 24px;
|
|
}
|
|
|
|
.page__title {
|
|
margin: 0;
|
|
font-size: 1.7rem;
|
|
font-weight: 500;
|
|
letter-spacing: -0.022em;
|
|
line-height: 1.2;
|
|
color: var(--zk-ink);
|
|
}
|
|
|
|
.page__sub {
|
|
margin: 6px 0 0;
|
|
max-width: 72ch;
|
|
font-size: 0.855rem;
|
|
line-height: 1.5;
|
|
color: var(--zk-muted);
|
|
}
|
|
|
|
/* The queue depth, as a stat tile rather than a loose number. */
|
|
.page__count {
|
|
flex: none;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 1px;
|
|
min-width: 84px;
|
|
padding: 10px 18px;
|
|
border-radius: var(--r-lg);
|
|
background: var(--zk-white);
|
|
border: 1px solid var(--zk-line);
|
|
box-shadow: var(--sh-sm);
|
|
}
|
|
|
|
.page__count strong {
|
|
font-size: 1.6rem;
|
|
font-weight: 500;
|
|
line-height: 1.1;
|
|
color: var(--zk-blue);
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
|
|
.page__count span {
|
|
font-size: 0.6rem;
|
|
font-weight: 500;
|
|
letter-spacing: 0.1em;
|
|
text-transform: uppercase;
|
|
color: var(--zk-grey);
|
|
}
|
|
|
|
.empty {
|
|
margin: 0;
|
|
color: var(--zk-muted);
|
|
font-size: 0.88rem;
|
|
text-align: center;
|
|
padding: 44px 24px;
|
|
border-radius: var(--r-lg);
|
|
border: 1px dashed var(--zk-line);
|
|
background: linear-gradient(180deg, var(--zk-white) 0%, var(--zk-tint) 100%);
|
|
}
|
|
|
|
.notice {
|
|
background: var(--zk-white);
|
|
border: 1px solid var(--zk-line);
|
|
border-left: 3px solid var(--zk-blue);
|
|
border-radius: var(--r-xs) var(--r-lg) var(--r-lg) var(--r-xs);
|
|
box-shadow: var(--sh-sm);
|
|
padding: 18px 22px;
|
|
max-width: 64ch;
|
|
}
|
|
|
|
.notice strong {
|
|
display: block;
|
|
font-weight: 500;
|
|
margin-bottom: 7px;
|
|
}
|
|
|
|
.notice p {
|
|
margin: 0 0 6px;
|
|
font-size: 0.86rem;
|
|
color: var(--zk-muted);
|
|
line-height: 1.55;
|
|
}
|
|
|
|
.notice__detail {
|
|
font-size: 0.76rem !important;
|
|
color: var(--zk-grey) !important;
|
|
}
|
|
|
|
.notice code {
|
|
font-family: var(--font-mono);
|
|
font-size: 0.8em;
|
|
background: var(--zk-tint);
|
|
border: 1px solid var(--zk-line-soft);
|
|
border-radius: var(--r-xs);
|
|
padding: 1px 5px;
|
|
}
|
|
|
|
/* ---- queue table ---- */
|
|
.gridwrap {
|
|
background: var(--zk-white);
|
|
border: 1px solid var(--zk-line);
|
|
border-radius: var(--r-lg);
|
|
box-shadow: var(--sh-sm);
|
|
overflow: hidden;
|
|
overflow-x: auto;
|
|
}
|
|
|
|
.grid {
|
|
width: 100%;
|
|
/* Eight columns of operational data do not compress: below this the card
|
|
scrolls sideways rather than squeezing every cell into two words a line. */
|
|
min-width: 1000px;
|
|
border-collapse: separate;
|
|
border-spacing: 0;
|
|
font-size: 0.86rem;
|
|
}
|
|
|
|
.grid th {
|
|
position: sticky;
|
|
top: 0;
|
|
z-index: 1;
|
|
text-align: left;
|
|
font-size: 0.65rem;
|
|
font-weight: 500;
|
|
letter-spacing: 0.1em;
|
|
text-transform: uppercase;
|
|
color: var(--zk-muted);
|
|
padding: 12px 16px;
|
|
background: var(--zk-tint);
|
|
border-bottom: 1px solid var(--zk-line);
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.grid td {
|
|
padding: 13px 16px;
|
|
border-bottom: 1px solid var(--zk-line-soft);
|
|
vertical-align: top;
|
|
}
|
|
|
|
.grid tbody tr {
|
|
transition: background var(--t-fast);
|
|
}
|
|
|
|
.grid tbody tr:last-child td {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.grid tbody tr:hover {
|
|
background: var(--zk-tint);
|
|
}
|
|
|
|
/* An accent that arrives on hover, drawn inside the cell so it never disturbs
|
|
the table's own borders. */
|
|
.grid tbody tr:hover td:first-child {
|
|
box-shadow: inset 3px 0 0 var(--zk-blue);
|
|
}
|
|
|
|
.grid .num {
|
|
text-align: right;
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
|
|
/* The lead reference is the thing an operator quotes on a call. */
|
|
.grid__ref {
|
|
font-variant-numeric: tabular-nums;
|
|
white-space: nowrap;
|
|
color: var(--zk-muted);
|
|
}
|
|
|
|
.grid__sub {
|
|
font-size: 0.76rem;
|
|
color: var(--zk-muted);
|
|
margin-top: 3px;
|
|
}
|
|
|
|
.grid__link {
|
|
color: var(--zk-blue);
|
|
text-decoration: none;
|
|
font-weight: 500;
|
|
border-bottom: 1px solid transparent;
|
|
transition: border-color var(--t-fast);
|
|
}
|
|
|
|
.grid__link:hover {
|
|
border-bottom-color: currentColor;
|
|
}
|
|
|
|
/* Row action. Renders as a button; stays an anchor so an operator can open a
|
|
lead in a new tab from a queue.
|
|
NOT `.act` — that class belongs to the Lead page's activity buttons and is
|
|
display:flex, which stretched this anchor to fill the cell. */
|
|
.grid__act {
|
|
text-align: right;
|
|
white-space: nowrap;
|
|
width: 1%;
|
|
}
|
|
|
|
.grid__btn {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
text-decoration: none;
|
|
white-space: nowrap;
|
|
font-size: 0.78rem;
|
|
font-weight: 500;
|
|
padding: 6px 14px;
|
|
border-radius: var(--r-pill);
|
|
background: var(--zk-white);
|
|
color: var(--zk-blue);
|
|
border: 1px solid var(--zk-line);
|
|
transition: background var(--t-fast), border-color var(--t-fast), box-shadow var(--t-fast);
|
|
}
|
|
|
|
.grid__btn::after {
|
|
content: '→';
|
|
font-size: 0.85em;
|
|
transition: transform var(--t-fast);
|
|
}
|
|
|
|
.grid__btn:hover {
|
|
background: var(--zk-tint-blue);
|
|
border-color: var(--zk-blue-light);
|
|
}
|
|
|
|
.grid__btn:hover::after {
|
|
transform: translateX(2px);
|
|
}
|
|
|
|
.grid__btn:focus-visible {
|
|
outline: none;
|
|
border-color: var(--zk-blue);
|
|
box-shadow: var(--ring);
|
|
}
|
|
|
|
/* ---- loading skeleton ---- */
|
|
.skel {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 1px;
|
|
padding: 14px 0;
|
|
background: var(--zk-white);
|
|
border: 1px solid var(--zk-line);
|
|
border-radius: var(--r-lg);
|
|
box-shadow: var(--sh-sm);
|
|
overflow: hidden;
|
|
}
|
|
|
|
.skel__row {
|
|
height: 18px;
|
|
margin: 9px 16px;
|
|
border-radius: var(--r-sm);
|
|
background: linear-gradient(90deg, var(--zk-line-soft) 0%, var(--zk-tint) 40%, var(--zk-line-soft) 80%);
|
|
background-size: 420px 100%;
|
|
animation: zk-shimmer 1.25s linear infinite;
|
|
}
|
|
|
|
.skel__row:nth-child(odd) { width: calc(100% - 32px); }
|
|
.skel__row:nth-child(3n) { width: 72%; }
|
|
.skel__row:nth-child(3n + 2) { width: 88%; }
|
|
|
|
/* ---- chips ---- */
|
|
.chip {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
font-size: 0.72rem;
|
|
font-weight: 500;
|
|
padding: 3px 10px;
|
|
border-radius: var(--r-pill);
|
|
background: var(--zk-tint-blue);
|
|
color: var(--zk-blue-dark);
|
|
border: 1px solid var(--zk-blue-light);
|
|
}
|
|
|
|
.chip--warn {
|
|
background: var(--zk-amber-tint);
|
|
color: var(--zk-amber-ink);
|
|
border-color: var(--zk-amber-line);
|
|
}
|
|
|
|
/* ---- doors ---- */
|
|
.doors {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(268px, 1fr));
|
|
gap: 18px;
|
|
}
|
|
|
|
.door {
|
|
position: relative;
|
|
font: inherit;
|
|
text-align: left;
|
|
cursor: pointer;
|
|
background: var(--zk-white);
|
|
border: 1px solid var(--zk-line);
|
|
border-radius: var(--r-lg);
|
|
box-shadow: var(--sh-sm);
|
|
padding: 22px 22px 20px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 10px;
|
|
overflow: hidden;
|
|
transition: box-shadow var(--t), transform var(--t-fast), border-color var(--t-fast);
|
|
}
|
|
|
|
/* The accent bar is the door's handle: 3px at rest, the full brand gradient
|
|
once the pointer is on it. */
|
|
.door::before {
|
|
content: '';
|
|
position: absolute;
|
|
inset: 0 0 auto;
|
|
height: 3px;
|
|
background: var(--grad-blue);
|
|
transform-origin: left;
|
|
transition: height var(--t);
|
|
}
|
|
|
|
.door:hover {
|
|
border-color: var(--zk-blue-light);
|
|
box-shadow: var(--sh-md);
|
|
transform: translateY(-2px);
|
|
}
|
|
|
|
.door:hover::before {
|
|
height: 5px;
|
|
}
|
|
|
|
.door:focus-visible {
|
|
border-color: var(--zk-blue);
|
|
box-shadow: var(--ring);
|
|
}
|
|
|
|
.door strong {
|
|
font-size: 1.02rem;
|
|
font-weight: 500;
|
|
letter-spacing: -0.01em;
|
|
color: var(--zk-ink);
|
|
}
|
|
|
|
.door p {
|
|
margin: 0;
|
|
font-size: 0.82rem;
|
|
color: var(--zk-muted);
|
|
line-height: 1.55;
|
|
}
|
|
|
|
.door .chip {
|
|
align-self: flex-start;
|
|
}
|
|
|
|
/* ---- panel ---- */
|
|
.panel {
|
|
background: var(--zk-white);
|
|
border: 1px solid var(--zk-line);
|
|
border-radius: var(--r-lg);
|
|
box-shadow: var(--sh-sm);
|
|
padding: 24px 26px;
|
|
margin-bottom: 22px;
|
|
}
|
|
|
|
.panel__head {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
justify-content: space-between;
|
|
gap: 20px;
|
|
margin-bottom: 18px;
|
|
}
|
|
|
|
.panel__title {
|
|
margin: 0;
|
|
font-size: 1.06rem;
|
|
font-weight: 500;
|
|
letter-spacing: -0.012em;
|
|
}
|
|
|
|
.panel__sub {
|
|
margin: 5px 0 0;
|
|
font-size: 0.82rem;
|
|
color: var(--zk-muted);
|
|
max-width: 62ch;
|
|
line-height: 1.55;
|
|
}
|
|
|
|
/* ---- actions ---- */
|
|
.acts {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 10px;
|
|
}
|
|
|
|
.act {
|
|
font: inherit;
|
|
cursor: pointer;
|
|
text-align: left;
|
|
background: var(--zk-white);
|
|
border: 1px solid var(--zk-line);
|
|
border-radius: var(--r-md);
|
|
padding: 11px 16px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 3px;
|
|
min-width: 196px;
|
|
transition: border-color var(--t-fast), background var(--t-fast), box-shadow var(--t-fast), transform var(--t-fast);
|
|
}
|
|
|
|
.act:hover {
|
|
border-color: var(--zk-blue-light);
|
|
background: var(--zk-tint);
|
|
box-shadow: var(--sh-sm);
|
|
transform: translateY(-1px);
|
|
}
|
|
|
|
.act.is-open {
|
|
border-color: var(--zk-blue);
|
|
background: var(--zk-tint-blue);
|
|
box-shadow: var(--ring);
|
|
transform: none;
|
|
}
|
|
|
|
.act strong {
|
|
font-size: 0.885rem;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.act__by {
|
|
font-size: 0.72rem;
|
|
color: var(--zk-muted);
|
|
}
|
|
|
|
.acts__form {
|
|
margin-top: 22px;
|
|
padding-top: 22px;
|
|
border-top: 1px solid var(--zk-line-soft);
|
|
animation: zk-rise 0.24s var(--ease) both;
|
|
}
|
|
|
|
/* ---- what the machine is doing ----
|
|
Shown where an empty action bar used to be. Most stages are carried by an AI
|
|
employee that takes a minute or two, and a screen that says nothing for two
|
|
minutes reads as broken. */
|
|
.doing {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
gap: 14px;
|
|
margin-bottom: 22px;
|
|
padding: 16px 20px;
|
|
border-radius: var(--r-lg);
|
|
border: 1px solid var(--zk-blue-light);
|
|
background: var(--zk-tint-blue);
|
|
}
|
|
|
|
.doing div {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 3px;
|
|
min-width: 0;
|
|
}
|
|
|
|
.doing strong {
|
|
font-size: 0.95rem;
|
|
font-weight: 500;
|
|
color: var(--zk-blue-dark);
|
|
}
|
|
|
|
.doing span {
|
|
font-size: 0.82rem;
|
|
line-height: 1.5;
|
|
color: var(--zk-muted);
|
|
}
|
|
|
|
.doing__pulse {
|
|
flex: none;
|
|
margin-top: 5px;
|
|
width: 10px;
|
|
height: 10px;
|
|
border-radius: 50%;
|
|
background: var(--zk-blue);
|
|
box-shadow: 0 0 0 0 rgba(33, 103, 174, 0.5);
|
|
animation: zk-pulse 1.9s var(--ease) infinite;
|
|
}
|
|
|
|
@keyframes zk-pulse {
|
|
70% { box-shadow: 0 0 0 9px rgba(33, 103, 174, 0); }
|
|
100% { box-shadow: 0 0 0 0 rgba(33, 103, 174, 0); }
|
|
}
|
|
|
|
/* ---- what the workflow said ----
|
|
The message the API returns per activity, shown verbatim: it is the workflow
|
|
telling the operator what it just set in motion. */
|
|
.said {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
gap: 14px;
|
|
margin-bottom: 22px;
|
|
padding: 14px 16px 14px 20px;
|
|
border-radius: var(--r-lg);
|
|
border: 1px solid var(--zk-line);
|
|
border-left: 3px solid var(--zk-blue);
|
|
background: var(--zk-white);
|
|
box-shadow: var(--sh-sm);
|
|
animation: zk-rise 0.24s var(--ease) both;
|
|
}
|
|
|
|
.said p {
|
|
flex: 1;
|
|
margin: 0;
|
|
font-size: 0.88rem;
|
|
line-height: 1.5;
|
|
color: var(--zk-ink);
|
|
}
|
|
|
|
.said button {
|
|
flex: none;
|
|
font: inherit;
|
|
font-size: 1.1rem;
|
|
line-height: 1;
|
|
cursor: pointer;
|
|
padding: 2px 6px;
|
|
border: 0;
|
|
border-radius: var(--r-sm);
|
|
background: none;
|
|
color: var(--zk-grey);
|
|
transition: background var(--t-fast), color var(--t-fast);
|
|
}
|
|
|
|
.said button:hover {
|
|
background: var(--zk-tint);
|
|
color: var(--zk-ink);
|
|
}
|
|
|
|
/* The stage a queue named by need actually maps to. */
|
|
.page__stage {
|
|
display: block;
|
|
margin-top: 4px;
|
|
font-size: 0.72rem;
|
|
color: var(--zk-grey);
|
|
}
|
|
|
|
/* ---- back ----
|
|
An icon, not a sentence: the way out of a record is a known shape and does not
|
|
need naming twice on the page. It carries a title and an aria-label, so the
|
|
name is still there for a pointer that hovers and for a screen reader. */
|
|
.backbtn {
|
|
flex: none;
|
|
margin-top: 4px;
|
|
width: 34px;
|
|
height: 34px;
|
|
display: grid;
|
|
place-items: center;
|
|
cursor: pointer;
|
|
border-radius: var(--r-md);
|
|
border: 1px solid var(--zk-line);
|
|
background: var(--zk-white);
|
|
color: var(--zk-muted);
|
|
box-shadow: var(--sh-xs);
|
|
transition: border-color var(--t-fast), color var(--t-fast), background var(--t-fast), box-shadow var(--t-fast);
|
|
}
|
|
|
|
.backbtn svg {
|
|
width: 16px;
|
|
height: 16px;
|
|
transition: transform var(--t-fast);
|
|
}
|
|
|
|
.backbtn:hover {
|
|
border-color: var(--zk-blue-light);
|
|
background: var(--zk-tint-blue);
|
|
color: var(--zk-blue-dark);
|
|
box-shadow: var(--sh-sm);
|
|
}
|
|
|
|
.backbtn:hover svg {
|
|
transform: translateX(-2px);
|
|
}
|
|
|
|
.backbtn:focus-visible {
|
|
outline: none;
|
|
border-color: var(--zk-blue);
|
|
box-shadow: var(--ring);
|
|
}
|
|
|
|
.page__lead {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
gap: 14px;
|
|
min-width: 0;
|
|
}
|
|
|
|
/* ---- lead: the file beside the story ---- */
|
|
.lead {
|
|
display: grid;
|
|
grid-template-columns: minmax(0, 1fr) 384px;
|
|
gap: 20px;
|
|
align-items: start;
|
|
}
|
|
|
|
.lead__main {
|
|
min-width: 0;
|
|
}
|
|
|
|
/* The trail stays in view while the file is read against it. */
|
|
.lead__side {
|
|
position: sticky;
|
|
top: 90px;
|
|
min-width: 0;
|
|
}
|
|
|
|
.panel--rail {
|
|
padding: 0;
|
|
margin-bottom: 0;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.panel--rail .panel__head {
|
|
padding: 20px 22px 0;
|
|
margin-bottom: 14px;
|
|
}
|
|
|
|
.lead__feed {
|
|
max-height: calc(100vh - 210px);
|
|
overflow-y: auto;
|
|
overscroll-behavior: contain;
|
|
padding: 0 22px 20px;
|
|
}
|
|
|
|
/* One column below the width where a 384px rail stops paying for itself. */
|
|
@media (max-width: 1180px) {
|
|
.lead {
|
|
grid-template-columns: minmax(0, 1fr);
|
|
}
|
|
|
|
.lead__side {
|
|
position: static;
|
|
}
|
|
|
|
.lead__feed {
|
|
max-height: 560px;
|
|
}
|
|
}
|
|
|
|
/* ---- tabs ----
|
|
Underline rather than pills: these swap the whole panel below them, which is
|
|
what a tab is for; a pill segment reads as a filter on shared content. The
|
|
2px rule lives on the button, so a strip that wraps to two rows still marks
|
|
the active one correctly. */
|
|
.tabs {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 2px;
|
|
margin-bottom: 20px;
|
|
border-bottom: 1px solid var(--zk-line);
|
|
}
|
|
|
|
.tab {
|
|
font: inherit;
|
|
font-size: 0.82rem;
|
|
white-space: nowrap;
|
|
cursor: pointer;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 7px;
|
|
padding: 9px 13px;
|
|
background: none;
|
|
border: 0;
|
|
border-bottom: 2px solid transparent;
|
|
border-radius: var(--r-sm) var(--r-sm) 0 0;
|
|
color: var(--zk-muted);
|
|
transition: color var(--t-fast), background var(--t-fast), border-color var(--t-fast);
|
|
}
|
|
|
|
.tab:hover {
|
|
background: var(--zk-tint);
|
|
color: var(--zk-ink);
|
|
}
|
|
|
|
.tab.is-on {
|
|
color: var(--zk-blue-dark);
|
|
font-weight: 500;
|
|
border-bottom-color: var(--zk-blue);
|
|
}
|
|
|
|
.tab__n {
|
|
font-size: 0.66rem;
|
|
font-variant-numeric: tabular-nums;
|
|
min-width: 18px;
|
|
padding: 1px 6px;
|
|
border-radius: var(--r-pill);
|
|
background: var(--zk-line-soft);
|
|
color: var(--zk-muted);
|
|
text-align: center;
|
|
}
|
|
|
|
.tab.is-on .tab__n {
|
|
background: var(--zk-tint-blue);
|
|
color: var(--zk-blue);
|
|
}
|
|
|
|
/* ---- property rows ----
|
|
Facts read as label/value pairs on hairlines rather than as a wall of filled
|
|
boxes: the tint was carrying no information and made every field shout as
|
|
loudly as every other. */
|
|
.props {
|
|
margin: 0;
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(330px, 1fr));
|
|
gap: 0 36px;
|
|
}
|
|
|
|
.prop {
|
|
display: grid;
|
|
grid-template-columns: minmax(96px, 33%) minmax(0, 1fr);
|
|
gap: 16px;
|
|
align-items: baseline;
|
|
padding: 11px 10px;
|
|
margin: 0 -10px;
|
|
border-radius: var(--r-md);
|
|
border-bottom: 1px solid var(--zk-line-soft);
|
|
transition: background var(--t-fast);
|
|
}
|
|
|
|
.prop:hover {
|
|
background: var(--zk-tint);
|
|
}
|
|
|
|
.prop dt {
|
|
font-size: 0.78rem;
|
|
color: var(--zk-muted);
|
|
line-height: 1.45;
|
|
}
|
|
|
|
.prop dd {
|
|
margin: 0;
|
|
font-size: 0.875rem;
|
|
color: var(--zk-ink);
|
|
line-height: 1.45;
|
|
overflow-wrap: anywhere;
|
|
}
|
|
|
|
.prop__num {
|
|
font-variant-numeric: tabular-nums;
|
|
font-weight: 500;
|
|
}
|
|
|
|
/* Prose is not a property. It takes the full width, is labelled above rather
|
|
than beside, and is folded by ClampText. */
|
|
.prop--note {
|
|
grid-column: 1 / -1;
|
|
display: block;
|
|
margin: 10px -10px;
|
|
padding: 14px 16px;
|
|
border-bottom: 0;
|
|
border: 1px solid var(--zk-line-soft);
|
|
border-left: 3px solid var(--zk-blue-light);
|
|
border-radius: 0 var(--r-md) var(--r-md) 0;
|
|
background: var(--zk-tint);
|
|
}
|
|
|
|
.prop--note:hover {
|
|
background: var(--zk-tint);
|
|
border-color: var(--zk-blue-light);
|
|
}
|
|
|
|
.prop--note dt {
|
|
margin-bottom: 7px;
|
|
font-size: 0.66rem;
|
|
font-weight: 500;
|
|
letter-spacing: 0.09em;
|
|
text-transform: uppercase;
|
|
color: var(--zk-blue-dark);
|
|
}
|
|
|
|
.prop--note dd {
|
|
margin: 0;
|
|
}
|
|
|
|
/* ---- lead file ---- */
|
|
.lead__stage {
|
|
flex: none;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: flex-end;
|
|
gap: 5px;
|
|
text-align: right;
|
|
}
|
|
|
|
.lead__stagelabel {
|
|
display: block;
|
|
font-size: 0.6rem;
|
|
font-weight: 500;
|
|
letter-spacing: 0.12em;
|
|
text-transform: uppercase;
|
|
color: var(--zk-grey);
|
|
}
|
|
|
|
.lead__closed {
|
|
font-size: 0.7rem;
|
|
color: var(--zk-grey);
|
|
}
|
|
|
|
.lead__stage strong {
|
|
font-size: 0.9rem;
|
|
font-weight: 500;
|
|
color: var(--zk-blue-dark);
|
|
padding: 6px 14px;
|
|
border-radius: var(--r-pill);
|
|
background: var(--zk-tint-blue);
|
|
border: 1px solid var(--zk-blue-light);
|
|
}
|
|
|
|
/* A closed lead is not a live stage; it should not wear the live stage's blue. */
|
|
.lead__stage strong.is-closed {
|
|
color: var(--zk-muted);
|
|
background: var(--zk-tint);
|
|
border-color: var(--zk-line);
|
|
}
|
|
|
|
/* ---- renewal countdown ---- */
|
|
.due {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
font-size: 0.75rem;
|
|
font-weight: 500;
|
|
padding: 3px 10px;
|
|
border-radius: var(--r-pill);
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.due--lapsed {
|
|
background: var(--zk-danger-tint);
|
|
color: var(--zk-danger-ink);
|
|
border: 1px solid var(--zk-danger-line);
|
|
}
|
|
|
|
.due--urgent {
|
|
background: var(--zk-amber-tint);
|
|
color: var(--zk-amber-ink);
|
|
border: 1px solid var(--zk-amber-line);
|
|
}
|
|
|
|
.due--soon {
|
|
background: var(--zk-tint-blue);
|
|
color: var(--zk-blue-dark);
|
|
border: 1px solid var(--zk-blue-light);
|
|
}
|
|
|
|
.due--later {
|
|
background: var(--zk-tint);
|
|
color: var(--zk-muted);
|
|
border: 1px solid var(--zk-line);
|
|
}
|
|
|
|
/* ---- lead header meta ---- */
|
|
.lead__meta {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 10px;
|
|
margin: 0 0 22px;
|
|
}
|
|
|
|
.lead__meta div {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 3px;
|
|
padding: 9px 16px;
|
|
border-radius: var(--r-md);
|
|
background: var(--zk-white);
|
|
border: 1px solid var(--zk-line);
|
|
box-shadow: var(--sh-xs);
|
|
}
|
|
|
|
.lead__meta dt {
|
|
font-size: 0.6rem;
|
|
font-weight: 500;
|
|
letter-spacing: 0.1em;
|
|
text-transform: uppercase;
|
|
color: var(--zk-grey);
|
|
}
|
|
|
|
.lead__meta dd {
|
|
margin: 0;
|
|
font-size: 0.85rem;
|
|
color: var(--zk-ink);
|
|
}
|
|
|
|
.lead__meta--money {
|
|
border-color: var(--zk-blue-light) !important;
|
|
background: var(--zk-tint-blue) !important;
|
|
}
|
|
|
|
.lead__meta--money dd {
|
|
font-size: 1rem;
|
|
font-weight: 500;
|
|
color: var(--zk-blue-dark);
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
|
|
/* ── Portfolio overview ──────────────────────────────────────────────────
|
|
An operations dashboard: numbers first, prose nowhere. Every measure on
|
|
this page is derived from the lead list in the browser, so nothing here is
|
|
a figure the reader cannot get back to by opening a queue. */
|
|
|
|
.stamp {
|
|
flex: none;
|
|
align-self: center;
|
|
font-size: 11.5px;
|
|
letter-spacing: .04em;
|
|
text-transform: uppercase;
|
|
color: var(--zk-grey);
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
|
|
/* KPI strip. Tabular figures so the row does not jitter as it refreshes. */
|
|
.kpis {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(168px, 1fr));
|
|
gap: 1px;
|
|
background: var(--zk-line);
|
|
border: 1px solid var(--zk-line);
|
|
border-radius: var(--r-md);
|
|
overflow: hidden;
|
|
margin-bottom: 26px;
|
|
}
|
|
.kpi { background: var(--zk-white); padding: 16px 18px 14px; }
|
|
.kpi__l {
|
|
display: block;
|
|
font-size: 11px;
|
|
font-weight: 500;
|
|
letter-spacing: .07em;
|
|
text-transform: uppercase;
|
|
color: var(--zk-grey);
|
|
}
|
|
.kpi__v {
|
|
display: block;
|
|
margin: 7px 0 3px;
|
|
font-size: 26px;
|
|
font-weight: 400;
|
|
line-height: 1.05;
|
|
color: var(--zk-navy);
|
|
font-variant-numeric: tabular-nums;
|
|
letter-spacing: -.02em;
|
|
}
|
|
.kpi__s { display: block; font-size: 12px; color: var(--zk-muted); }
|
|
|
|
/* Two columns on a desk, one on anything narrower. The distribution is the
|
|
half that survives being pushed below the fold. */
|
|
.split { display: grid; grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr); gap: 30px; align-items: start; }
|
|
|
|
.sec {
|
|
display: flex;
|
|
align-items: baseline;
|
|
gap: 10px;
|
|
margin: 0 0 12px;
|
|
font-size: 12px;
|
|
font-weight: 500;
|
|
letter-spacing: .07em;
|
|
text-transform: uppercase;
|
|
color: var(--zk-muted);
|
|
}
|
|
.split > div > .sec:not(:first-child) { margin-top: 30px; }
|
|
.sec__hint { font-size: 11.5px; font-weight: 400; letter-spacing: 0; text-transform: none; color: var(--zk-grey); }
|
|
|
|
/* Work queues. A row rather than a card: five of them as cards is a wall. */
|
|
.qlist { display: flex; flex-direction: column; gap: 6px; }
|
|
.q {
|
|
display: grid;
|
|
grid-template-columns: 44px 1fr auto auto;
|
|
align-items: center;
|
|
gap: 12px;
|
|
padding: 11px 14px;
|
|
border: 1px solid var(--zk-line-soft);
|
|
border-radius: var(--r-sm);
|
|
background: var(--zk-white);
|
|
text-decoration: none;
|
|
color: inherit;
|
|
transition: border-color .12s, background .12s;
|
|
}
|
|
.q:hover { border-color: var(--zk-blue-light); background: var(--zk-tint); }
|
|
.q__n { font-size: 20px; font-weight: 500; color: var(--zk-grey); font-variant-numeric: tabular-nums; text-align: right; }
|
|
.q__t { font-size: 14px; color: var(--zk-ink); }
|
|
.q__t em { display: block; font-style: normal; font-size: 12px; color: var(--zk-muted); margin-top: 1px; }
|
|
.q__age { font-size: 12px; color: var(--zk-grey); font-variant-numeric: tabular-nums; }
|
|
.q__ro {
|
|
font-size: 10px; letter-spacing: .06em; text-transform: uppercase;
|
|
color: var(--zk-grey); border: 1px solid var(--zk-line); border-radius: 3px; padding: 1px 5px;
|
|
}
|
|
/* Only a queue with work in it earns weight. The eye should land on the
|
|
number that needs clearing, not read five identical rows. */
|
|
.q.is-live { border-color: var(--zk-line); }
|
|
.q.is-live .q__n { color: var(--zk-navy); }
|
|
|
|
.buckets { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
|
|
.bucket {
|
|
padding: 13px 15px;
|
|
border: 1px solid var(--zk-line);
|
|
border-left-width: 3px;
|
|
border-radius: var(--r-sm);
|
|
background: var(--zk-white);
|
|
}
|
|
.bucket strong { display: block; font-size: 21px; font-weight: 500; line-height: 1.1; font-variant-numeric: tabular-nums; }
|
|
.bucket span { display: block; margin-top: 2px; font-size: 12px; color: var(--zk-muted); }
|
|
.bucket--lapsed { border-left-color: var(--zk-danger); }
|
|
.bucket--lapsed strong { color: var(--zk-danger-ink); }
|
|
.bucket--urgent { border-left-color: var(--zk-amber); }
|
|
.bucket--urgent strong { color: var(--zk-amber-ink); }
|
|
.bucket--soon { border-left-color: var(--zk-blue-mid); }
|
|
.bucket--soon strong { color: var(--zk-blue-dark); }
|
|
|
|
/* Stage distribution. A bar per stage, scaled to the largest — enough to see
|
|
where the book is banked without pulling in a charting library. */
|
|
.dist { display: flex; flex-direction: column; gap: 3px; }
|
|
.dist__r {
|
|
display: grid;
|
|
grid-template-columns: 1fr 74px 26px;
|
|
align-items: center;
|
|
gap: 10px;
|
|
padding: 5px 8px;
|
|
border-radius: var(--r-xs);
|
|
text-decoration: none;
|
|
color: inherit;
|
|
}
|
|
.dist__r:hover { background: var(--zk-tint); }
|
|
.dist__l { font-size: 12.5px; color: var(--zk-ink); }
|
|
.dist__bar { height: 6px; border-radius: 3px; background: var(--zk-line-soft); overflow: hidden; }
|
|
.dist__f { display: block; height: 100%; border-radius: 3px; background: var(--zk-blue-mid); min-width: 0; }
|
|
.dist__f--needs { background: var(--zk-amber); }
|
|
.dist__f--customer { background: var(--zk-blue); }
|
|
.dist__f--auto { background: var(--zk-blue-light); }
|
|
.dist__f--waiting { background: var(--zk-grey); }
|
|
.dist__f--end { background: var(--zk-line); }
|
|
.dist__n { font-size: 12.5px; text-align: right; color: var(--zk-muted); font-variant-numeric: tabular-nums; }
|
|
.dist__r.is-zero .dist__l, .dist__r.is-zero .dist__n { color: var(--zk-grey); }
|
|
|
|
.grid--tight th, .grid--tight td { padding-top: 8px; padding-bottom: 8px; }
|
|
|
|
@media (max-width: 1080px) {
|
|
.split { grid-template-columns: 1fr; gap: 26px; }
|
|
}
|
|
@media (max-width: 640px) {
|
|
.buckets { grid-template-columns: 1fr; }
|
|
}
|
|
|
|
/* ── Lead measures ───────────────────────────────────────────────────────
|
|
The same strip as the portfolio KPIs, at lead scale. Age and dwell time are
|
|
computed rather than stored, and they are the two figures that answer "is
|
|
this moving?" — a question the record itself cannot answer. */
|
|
.lmetrics {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
|
gap: 1px;
|
|
background: var(--zk-line);
|
|
border: 1px solid var(--zk-line);
|
|
border-radius: var(--r-md);
|
|
overflow: hidden;
|
|
margin-bottom: 22px;
|
|
}
|
|
.lm { background: var(--zk-white); padding: 13px 15px 12px; }
|
|
.lm__l {
|
|
display: block;
|
|
font-size: 10.5px; font-weight: 500; letter-spacing: .07em;
|
|
text-transform: uppercase; color: var(--zk-grey);
|
|
}
|
|
.lm__v {
|
|
display: block; margin: 6px 0 2px;
|
|
font-size: 19px; font-weight: 400; line-height: 1.1;
|
|
color: var(--zk-navy); font-variant-numeric: tabular-nums; letter-spacing: -.01em;
|
|
}
|
|
.lm__v--sm { font-size: 16px; }
|
|
.lm__s {
|
|
display: block; font-size: 11.5px; color: var(--zk-muted);
|
|
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
}
|
|
/* The countdown carries its own tone here, the same one the queues use, so a
|
|
lapsed renewal reads the same wherever it appears. */
|
|
.lm__v.due--lapsed { color: var(--zk-danger-ink); }
|
|
.lm__v.due--urgent { color: var(--zk-amber-ink); }
|
|
|
|
/* ── Next step ───────────────────────────────────────────────────────────
|
|
One decision, sized like one. The stage's own step is the only thing at
|
|
full weight; loops sit under it, and an AI's own work is folded away
|
|
entirely — it is recovery, not an outstanding task. */
|
|
|
|
.step { display: flex; flex-wrap: wrap; gap: 10px; }
|
|
.step__btn {
|
|
flex: 1 1 220px;
|
|
display: block;
|
|
padding: 14px 18px;
|
|
border: 1px solid var(--zk-blue);
|
|
border-radius: var(--r-md);
|
|
background: var(--zk-blue);
|
|
color: var(--zk-white);
|
|
text-align: left;
|
|
cursor: pointer;
|
|
transition: background .12s, border-color .12s, box-shadow .12s;
|
|
}
|
|
.step__btn:hover { background: var(--zk-blue-dark); border-color: var(--zk-blue-dark); box-shadow: 0 2px 10px rgba(33,103,174,.25); }
|
|
.step__btn strong { display: block; font-size: 15px; font-weight: 500; }
|
|
.step__btn span { display: block; margin-top: 2px; font-size: 12px; opacity: .85; }
|
|
.step__btn.is-open { background: var(--zk-blue-deep); border-color: var(--zk-blue-deep); }
|
|
|
|
/* Decline sits beside Clear as an equal — an underwriter is choosing between
|
|
two decisions, not deciding whether to act. It is outlined rather than
|
|
filled so the pair does not read as two identical recommendations. */
|
|
.step__btn--no {
|
|
background: var(--zk-white);
|
|
color: var(--zk-danger-ink);
|
|
border-color: var(--zk-danger-line);
|
|
}
|
|
.step__btn--no:hover { background: var(--zk-danger-tint); border-color: var(--zk-danger); box-shadow: none; }
|
|
.step__btn--no.is-open { background: var(--zk-danger-tint); border-color: var(--zk-danger); }
|
|
|
|
/* Loops. Real, bounded, and never the answer to "what now". */
|
|
.alt { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
|
|
.alt__btn {
|
|
padding: 8px 13px;
|
|
border: 1px solid var(--zk-line);
|
|
border-radius: var(--r-sm);
|
|
background: var(--zk-white);
|
|
color: var(--zk-ink);
|
|
font-size: 13px;
|
|
cursor: pointer;
|
|
transition: border-color .12s, background .12s;
|
|
}
|
|
.alt__btn:hover { border-color: var(--zk-blue-light); background: var(--zk-tint); }
|
|
.alt__btn.is-open { border-color: var(--zk-blue); background: var(--zk-tint-blue); }
|
|
.alt__btn em { font-style: normal; margin-left: 7px; font-size: 11.5px; color: var(--zk-grey); }
|
|
.alt__btn--exit { color: var(--zk-danger-ink); }
|
|
.alt__btn--exit:hover { border-color: var(--zk-danger-line); background: var(--zk-danger-tint); }
|
|
|
|
/* Recovery, folded. Needed on the day an agent stalls and never otherwise. */
|
|
.stuck { margin-top: 18px; border-top: 1px solid var(--zk-line-soft); padding-top: 14px; }
|
|
.stuck > summary {
|
|
cursor: pointer;
|
|
font-size: 13px;
|
|
color: var(--zk-muted);
|
|
list-style: none;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
}
|
|
.stuck > summary::-webkit-details-marker { display: none; }
|
|
.stuck > summary::before { content: '▸'; font-size: 10px; color: var(--zk-grey); }
|
|
.stuck[open] > summary::before { content: '▾'; }
|
|
.stuck > summary:hover { color: var(--zk-blue-dark); }
|
|
.stuck__why { margin: 10px 0 0; font-size: 12.5px; color: var(--zk-muted); max-width: 64ch; }
|