Compare commits

...

2 Commits

Author SHA1 Message Date
60f6c97201 fix(console): sharp text — stop compositing content on a transformed layer
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>
2026-09-09 01:36:07 +05:30
edda6e9540 console: give each AI employee a role icon; people keep their initials
The avatars were initials-plus-spark for the machines and bare initials for
people — thin, and it made five differently-skilled agents look like the same
badge in five colours. The convention that reads instantly is icons for
machines, initials for people: the contrast itself tells a human step from an
autonomous one before a name is read, and the symbol says WHICH machine.

Each employee now wears a glyph true to its job — a magnifier for Intake (checks
the lead), a speech bubble for Engage (talks to the customer), a shield for the
Advisor (recommends the cover), an ID badge for KYC (verifies identity), balance
scales for the Referral (prepares a file for a human to weigh). The AI spark
stays at the corner. People keep their initials, which is the right avatar for a
person and now the clear signal that a person, not a machine, did the step.

Verified legible at disc size (20-26px) across all five brand hues.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-09 01:34:03 +05:30
5 changed files with 51 additions and 4 deletions

View File

@ -28,6 +28,7 @@
*/
export const AGENTS = {
ai_intake: {
icon: 'inspect',
short: 'Intake',
full: 'Intake & Attribution',
initials: 'IA',
@ -44,6 +45,7 @@ export const AGENTS = {
},
ai_engage: {
icon: 'chat',
short: 'Engage',
full: 'Engage',
initials: 'EN',
@ -55,6 +57,7 @@ export const AGENTS = {
},
ai_advisor: {
icon: 'shield',
short: 'Advisor',
full: 'Advisor',
initials: 'AD',
@ -66,6 +69,7 @@ export const AGENTS = {
},
ai_kyc: {
icon: 'id',
short: 'KYC',
full: 'KYC & Evidence',
initials: 'KY',
@ -77,6 +81,7 @@ export const AGENTS = {
},
ai_uw_referral: {
icon: 'scales',
short: 'Referral',
full: 'Underwriting Referral',
initials: 'UW',

View File

@ -89,3 +89,8 @@
white-space: nowrap;
border: 0;
}
/* Role icon fills most of the disc, leaving room for the AI spark at the
corner. Sized off the disc so it scales with .who--md. */
.who__glyph { width: 60%; height: 60%; }
.who--md .who__glyph { width: 58%; height: 58%; }

View File

@ -1,6 +1,39 @@
import { AGENTS, initialsOf } from '../api/agents.js'
import './AgentChip.css'
/**
* ROLE ICONS one per AI employee, true to what it does.
*
* People are shown by their initials; the machines are shown by a symbol. That
* contrast is the point: an operator can tell a human step from an autonomous
* one at a glance, before reading a name, and the symbol says which machine
* the one that CHECKS a lead, the one that TALKS to the customer, the one that
* recommends COVER, the one that verifies IDENTITY, the one that prepares a
* REFERRAL. Monoline at disc size, so they stay legible on every hue.
*/
const ICONS = {
// a magnifier Intake checks the lead is real and scores it
inspect: <><circle cx="6.6" cy="6.6" r="3.4" /><path d="M9.2 9.2 12 12" strokeLinecap="round" /></>,
// a speech bubble Engage talks to the customer
chat: <path d="M2.6 3.4h10.8v6.4H7l-2.8 2.2v-2.2H2.6z" strokeLinejoin="round" />,
// a shield the Advisor recommends the cover that protects them
shield: <path d="M8 2.2 12.6 4v3.4c0 2.8-2 4.7-4.6 6.1C5.4 12.1 3.4 10.2 3.4 7.4V4z" strokeLinejoin="round" />,
// an ID badge KYC verifies identity
id: <><rect x="2.6" y="3.4" width="10.8" height="9.2" rx="1.4" /><circle cx="6.2" cy="7" r="1.5" /><path d="M9.2 6.4h2.6M9.2 8.6h2.6M4.2 10.4h5" strokeLinecap="round" /></>,
// balance scales the Referral prepares a file for a human to weigh
scales: <path d="M8 2.4v10.4M4 12.8h8M3 5.2h10M3 5.2 1.6 8.4h2.8zM13 5.2 11.6 8.4h2.8zM8 3.2 4 5.2M8 3.2l4 2" strokeLinecap="round" strokeLinejoin="round" />,
}
function AgentGlyph({ name }) {
const g = ICONS[name]
if (!g) return null
return (
<svg className="who__glyph" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.35" aria-hidden="true">
{g}
</svg>
)
}
/**
* Who did this as a face, not a slug.
*
@ -30,7 +63,8 @@ export default function AgentChip({ agentKey, name, size = 'sm', showName = true
Only agents get it. A trail where a human's disc also carried one
would say nothing at all. */}
<span className={`who__disc who__disc--${tone}`} aria-hidden="true">
{initials}
{/* A machine wears its role icon; a person wears their initials. */}
{a && a.icon ? <AgentGlyph name={a.icon} /> : initials}
{a ? (
<svg className="who__ai" viewBox="0 0 12 12" aria-hidden="true">
<path d="M6 .9 7.2 4.3 10.6 5.5 7.2 6.7 6 10.1 4.8 6.7 1.4 5.5 4.8 4.3Z" />

View File

@ -210,8 +210,12 @@ textarea {
}
@keyframes zk-rise {
from { opacity: 0; transform: translateY(6px); }
to { opacity: 1; transform: none; }
/* Opacity ONLY. A translateY here promoted every element it touched to a GPU
layer, and text composited on a transformed layer renders blurry in Chrome
on Linux which made the whole app look soft. The fade alone gives the
entrance without ever moving text onto a fractional-pixel layer. */
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes zk-shimmer {

View File

@ -190,7 +190,6 @@
max-width: 1460px;
margin: 0 auto;
padding: 30px 34px 76px;
animation: zk-rise 0.28s var(--ease) both;
}
/* ── Narrow viewports: the rail lies down and scrolls ──────── */