Five fixes to the confusing nav:
- The two per-row numbers had no key. The AI's share now reads "N with AI" in
quiet grey, so it stops competing with the pink "needs you" badge.
- Removed the outline node circles (and the rail line they hung on) — with one
queue per group they read as empty checkboxes, not a pipeline. Rows now align
flush and the active queue is shown by its tint alone.
- Dropped the section-header count that just repeated the single row beneath it.
- Lightened and tightened the group labels so the queues lead, not the headers.
- Closed now matches a queue row — same padding, hover and count badge — while
staying a foldable disclosure.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The previous change over-corrected: left-anchoring a capped block pushed the
whole page to the left and left the right half of a wide monitor empty. Drop
the width cap so the content fills from the nav to the right edge, the way a
sidebar dashboard should.
- shell__main: no max-width, fills the width beside the nav.
- lead grid: story column takes the slack (1fr), facts rail fixed at its
content width (360-420px).
- metric tiles: auto-fit, so three or four tiles always fill the row instead
of leaving an empty hard-coded 4th cell on the right.
Reading comfort is still handled inside the feed (paragraphs cap their own
line length), so a wide panel does not sprawl the text.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The content block was capped at 1460px and centred with margin:0 auto, so on a
wide or zoomed-out monitor a large empty river opened between the sidebar and
the content, with the whole page floating in the middle.
Fix is the standard sidebar-dashboard layout: one governing width, anchored to
the nav instead of centred. shell__main caps at 1320 and left-aligns, so the
header, metric tiles, stage strip and the two-column grid all share that width
and line up. The grid's own columns cap at their natural content widths (the
760px story column at its reading width, the 340-440px facts rail at what its
key/value pairs need) so neither card stretches into internal whitespace. Any
spare width on a very wide screen falls to the right as margin, the way a
sidebar layout should read.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Visual and information-architecture pass across every screen. No API call,
payload, poll, permission rule or workflow mirror changes; forms still render
from the live schema and the timeline's merge/grouping is unchanged (only the
display order is reversible).
Design language
- Ground + white cards with one hairline and one radius; sentence-case
headings; shared primitives in index.css (.card .pill .btn .avatar .live).
- One inline icon set (components/Icon.jsx); no more filled navy/red tiles.
- Read-side labels in api/display.js: roles and enum tokens humanized,
customer initials/tones, money and date formatters. Nothing sent changes.
Shell
- 56px header with brand, global lead search (portfolio rows, "/" to focus)
and identity chip with a human role label.
- Sidebar rows carry icons and a clear active bar; AI roster at the foot,
each opening its remit card.
Login
- Operator copy instead of retail copy; centred sign-in card with show/hide
password; demo persona picker that fills the EMAIL only.
Overview
- "Who holds the work" as four quiet cards that warm only when owed.
- Pipeline flow bar with every stage named beneath it; KPI strip in a card.
- Action rows with chevron / view-only eye; expiry bands filter the table.
Queues
- Filter bar (search, product, channel, expiry band, stalled, hide closed)
and sortable columns, all client-side on the fetched page.
- Avatars, product/channel chips, stage chips for closed leads, tinted
stalled rows, count inline with the title.
Lead
- Avatar + chips in the header; metrics card with the stage rail inside,
and a branch node for Referred / Parked / Lost / Declined.
- Timeline newest-first by default (toggle), the worker's face as the node,
one footer row per entry, AI markdown rendered as bold.
- "AI team on this lead" panel with per-agent step counts; PDF tags on docs.
Forms & dialogs
- Required marked with an asterisk + legend; "Select…" placeholder; dd Mon
yyyy hint under date inputs; open activity form in a titled panel;
one-line dialog footnotes.
Fixes: horizontal overflow at 1024px, boxed "in N days" figure, raw tokens
(sme_underwriter, verbal_call, Rsa), findings cut at "1.".
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Text looked soft across the whole app. Cause: the entrance animation used
transform: translateY, and .shell__main (the entire content area) ran it with
fill-mode both. An element that animated transform stays on a GPU layer, and
text composited on a transformed layer renders blurry in Chrome on Linux — so
every word on the page was soft.
zk-rise is opacity-only now — it fades in without ever moving text onto a
fractional-pixel layer — and the page-wide container no longer animates at all
(a full-page fade on every navigation only softened text while it played, for
nothing). The remaining transforms are hover-only and transient.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The real problem was never the font or the shadows — it was the design LANGUAGE.
Floating white cards on a grey-blue ground is the generic admin-template look,
and no restyle escapes it. This ports the approved mockup: one canvas, sections
set off by hairlines and space, the activity feed as the hero.
- ONE CANVAS. The content ground is white; the sidebar is the only tinted
surface. Panels drop their border, radius and shadow and become plain
sections under their own headings.
- The metric strips (lead + overview) stop being caged tiles and become an
inline stat row between two hairlines — money at display size, the rest
quieter.
- The stage rail loses its card and becomes a slim inline stepper.
- The property rail flattens: Customer / Vehicle / Documents are hairline-
divided groups aligned to the edge, headings at label size — it is
reference, and should not shout over the story.
- "Who holds it" drops from a filled pill to a coloured dot and a line of text.
- Bricolage is gone. Display and body are both Zurich Sans, the brand face;
headings and figures lean on weight and tracking, not a second family. The
palette is the ZK blues throughout.
Same data, same functionality — only the surface. Revertible in one push.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Continues 009153f into the surfaces it had not reached.
The top bar was 66px with a hard rule under it — admin chrome. It is 72 now,
on the softer line, with the product name at 16px instead of 15. The sidebar
gets wider gutters and taller rows, so the queue list stops reading as a dense
menu and starts reading as the pipeline it is drawn to represent.
The audit trail is where people spend their time on this screen, so it gets the
most air: entries at 24px apart rather than 18, quoted text with 12/16 padding
instead of 8/12, and the activity name at semibold so an entry has a heading
rather than a first line.
The stage badge was 15px under a 12px caption. It is the single most important
fact about a lead and now reads at 16px semibold, which is legible from across
a desk — which is how it will actually be read in a demo.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The app had FIFTY-FOUR distinct font sizes and forty of them sat between
0.58rem and 0.95rem — a three-pixel band. The most-used size was 0.78rem, so
body copy was 12.5px. That is not a hierarchy, it is a fog of small grey text,
and it is the main reason the screen read as a decade old: nothing could be
emphasised because everything was already the same size.
Nine steps now, with real jumps. 12px is the floor and is reserved for
uppercase eyebrows — every one of the 53 declarations sitting there that was
not an eyebrow has been lifted off it. The surfaces people actually read — the
trail's entries, WhatsApp bubbles, field values, table cells — are at body size
rather than meta size.
The hairline cages are gone with them. The lead metrics and the overview KPIs
were seven equal cells in a 1px grid, every value the same size as its label,
which left the reader to do the prioritising themselves on every lead. They are
separated by whitespace now, values at 22px against 12px labels, with premium
and commission larger again: size does the prioritising instead.
Panels breathe at 32px rather than 24, radii went 10/14 -> 12/16/20, borders
dropped to the softer line, and the stage rail, the queue table and the cards
were rebuilt on the same spacing rhythm.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
"New lead" sat in the sidebar. A sidebar is for moving between places;
filing a lead is an action, and an action in a list of destinations is
the one item that does not behave like its neighbours — everything above
and below it changes what you are looking at, and it did something.
It now sits on the overview and on every queue, beside the lists it adds
to, which is also where somebody is standing when they realise they have
a lead to file. Same role gate as before: shown only to a user who holds
an entry door.
The sidebar's own button CSS went with it rather than being left for
someone to wonder about.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
THE SIDEBAR NOW CARRIES COUNTS. It deliberately did not, on the argument
that a tally could only come from a second full list call that would then
disagree with the queue's own total. Right about the cost, wrong about
the conclusion: with no numbers the only way to learn whether anything
was waiting on you was to open all nine queues in turn, which is the
question navigation exists to answer.
There is no second call. The overview's existing fetch moved into a
PortfolioProvider that both surfaces read, so this is one call fewer than
before and the two agree by construction. Bounded at 200 rows, as the
overview always was; past that the honest answer is an aggregate
endpoint, not a bigger limit.
What the badge counts is what needs a PERSON — phaseOf again, so a lead
whose documents are in and whose AI chain is running is reported beside
the badge rather than inside it. An amber dot marks a queue holding a
renewal inside a week, which is the only reason to open one queue before
another and was previously invisible. Zero is shown rather than hidden:
"nothing here" is an answer, and a queue that disappears when it empties
makes the sidebar move under the cursor.
Closed is folded into a summary. Three of the twelve queues, opened about
once a week, and at equal weight they made the live ones harder to find.
REASONING OPENS IN A DIALOG, NOT INLINE. The audit rail is 320px wide
with one entry per step, and a 1,500-character rationale expanding in
place pushed a lead's whole history off screen to read one sentence of
it. Nobody reads a paragraph in a sidebar. The finding — the AI's own
first sentence, which is already the conclusion — stays on the line; the
working is one click away and one Escape back, headed by what is being
read ("Call — Log Contact") rather than by nothing. Escape closes, the
page behind does not scroll, and focus returns to the button that opened
it so a keyboard reader keeps their place.
Two headings that described the container rather than the contents:
Record → "Lead file", and the audit trail's subtitle now says what a
reader gets from it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Three problems, and the first one is the reason the other two were hard
to see.
THERE WAS NO DASHBOARD. "/" redirected to the underwriting queue — one
stage, usually empty, and belonging to somebody else. Every screen in
the app answered "show me this queue"; none answered "how is the book
doing, and is anything waiting on me?", which is the only question a
person has before they have picked a queue.
So: a Today page. Three blocks, in the order somebody cares about them.
What is waiting on a person, as three cards that colour only when they
have something in them. Renewals running out, worst first, capped at 30
days because a renewal six months away is not a thing to look at today.
Then every stage with its count, zeroes included — a stage quietly
receiving nothing is only visible if its zero is on screen.
It is one list call, counted in the browser. That is honest at this size
and it is the same call the queues already make. If the book outgrows it
the answer is a counts endpoint, not a bigger page.
THE WORDS WERE WRITTEN FOR WHOEVER BUILT THE WORKFLOW. "What happens
next" was followed by "these are the only activities this state allows
— but the workflow decides, server-side, whether you may". A failed list
said "this queue has no record view yet". Panels were called "Flow
details". A closed lead read "nothing runs from here". None of that is
wrong; all of it is addressed to the wrong reader.
Now: "What you can do", "Lead details", "History", "This queue could not
be loaded". Queue names say what they want — "Documents needed",
"Underwriter to review", "Payment to confirm". The sidebar group called
"Not yet" says "Not due yet", and "Running by itself" says "The agents
have it", which is the actual claim being made.
THE QUEUE HAD NINE COLUMNS AND LED WITH THE WRONG ONE. Two were internal
vocabulary: attribution status renders "clear" or "contested" and means
nothing to an operator, and the channel is background rather than
something anyone scans a queue for. Both fold into a subtitle under the
customer. That leaves six columns and puts the renewal countdown — the
number that decides whether to act today — second instead of fifth.
Also: the timeline printed a raw slug when a step came back without a
name. It is the screen this product is demonstrated on, so a
"zk-act-doc-reminder" in the middle of an otherwise readable story is
expensive. It now reads as English.
The pipeline could be looked at but not driven. Adds the three pieces
that make the machine walkable end to end from the UI.
- ActivityForm renders whatever /view/form-screens returns — labels,
types, select options, mandatory flags — and submits it back. No form
is defined in this repo, so a field added in Studio appears here with
no code change.
- AddLead presents the three doors. Each is a separate INIT activity with
its own permissions; the bank one lands further along because the bank
already did CKYC.
- Lead shows the file grouped in the order it was worked, ending with
what the sourcing agent earns, plus the activities this state allows
and who normally performs each.
The "who normally performs this" label is presentational only. Nothing
here enforces anything — the workflow refuses server-side and the form
reports what it said, including a note when a 403 is the platform
declining rather than the console misbehaving.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The scaffold had the brand right and nothing behind it: the login was a
600ms setTimeout with a TODO, and three pieces of the frontgen deploy
contract were missing.
- Sign in against POST /usr/login (org_id as a STRING — a number is
rejected by the gateway) with the session in a provider; surface the
gateway's own message rather than a generic "invalid credentials",
because a wrong password and a user without access to this app look
identical from here and are not.
- Runtime config: the API URL is read from the config.js the server
writes at placement, never compiled in, and requireConfigValue throws
so a build with no config.js fails loudly instead of calling whichever
backend built it.
- base: './' plus a router basename taken from <base href>, so one build
serves any mount path.
- Move the fonts and logo from public/ into src/assets/ — Vite rewrites
bundled asset URLs to be relative, while a public/ file referenced as
"/fonts/..." stays absolute and 404s under the /zurich-kotak/ mount.
- API client for recordview / detailview / form-screens / start /
activity, and the pipeline shell whose sidebar is the state machine in
the order a lead moves.
Queue and lead-file data wait on the record and detail views.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>