Full styling reset to the approved mockup — from scratch, no functional change.
- PALETTE: clean white ground, warm near-black ink, ONE confident magenta-pink
accent (#e5227f) reserved for action and active state. The accent keeps the
--zk-blue-* token names it always had; only the values changed. Teal is the
customer, amber a person; pink is "act / here", nothing else. Neutrals warmed,
shadows softened to a plum-ink low alpha.
- TYPE: Plus Jakarta Sans — geometric-humanist, bold on headings (page title
800, panel titles and figures 700+), the modern-product register assured has.
- SHAPE: the flat single-canvas is gone; back to clean rounded cards that float
a little on the off-white — panels, metric tiles (money in the accent tint),
the stage rail, the facts rail. Status ribbon in teal, the primary action a
pink pill with an accent glow.
Same components, same data, same behaviour — only the surface.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
An audit found 124 of ~200 sizings at 12-13px: the 13px "meta" tier had become
the de-facto body size (97 uses), so the app read tiny and uniform with nothing
for the eye to land on, and the upper scale was starved. Shifted the crowded
lower tiers up one notch — dominant text 13->14, field labels 14->15, values
15->16, emphasis 16->17. One change to the scale definitions, lifting every
surface at once; 12px stays reserved for uppercase eyebrows.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Zurich Sans (a soft humanist OTF) read poorly at the data-density sizes this
console runs, and did not carry the modern-enterprise register we are after.
The UI moves to Geist — Vercel's typeface, the register of Linear/Vercel-class
tools: neutral, technical, and sharp at small sizes. Refs and figures use Geist
Mono. Zurich Sans stays in the fallback stack, so a blocked CDN degrades to the
brand face rather than to a system default.
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>
The layout was right but the surface still read as a flat corporate form. Three
levers, all from the approved mockup:
- A DISPLAY FACE (Bricolage Grotesque) on the things the eye lands on — the
lead name, panel headings, and every figure. Zurich Sans stays the body
voice; this gives the numbers and titles the tighter, more current character
the plain grotesque could not. Loaded from Google Fonts with Zurich Sans as
the fallback, so a blocked CDN degrades to the brand face rather than breaking.
- DEPTH. Panels and the metric tiles lift from the barely-there --sh-sm to a
real --sh-md with rounder corners, so cards read as objects on a surface
rather than boxes drawn on paper.
- WHOSE MOVE IT IS. The header said "STAGE / Quote Presented" — a label. It now
carries a coloured pill: "Right now · With the customer" (teal), "With the AI"
(blue), "Waiting on a person" (amber). The stage name is in the rail below;
the pill answers the question an operator actually opens a lead to ask.
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>
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>