Compare commits

...

2 Commits

Author SHA1 Message Date
d1ed66b7ae console: sparkle for AI, person for human — two clear symbols
The role-icons-plus-corner-spark were busy and didn't read. Replaced with the
two symbols that are actually universal: a sparkle for the machines — the mark
Claude, Gemini and Copilot all use, so an AI step reads as AI with no legend —
in the agent's own colour, and a plain head-and-shoulders person glyph for the
people. The contrast is the whole message; which agent is said by the name
beside the disc and by the colour. Verified crisp at disc size.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-09 11:52:32 +05:30
d43b792dcb console: lift the type scale — body 14, values 15, so hierarchy shows
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>
2026-09-09 11:49:57 +05:30
2 changed files with 30 additions and 37 deletions

View File

@ -1,35 +1,31 @@
import { AGENTS, initialsOf } from '../api/agents.js' import { AGENTS } from '../api/agents.js'
import './AgentChip.css' import './AgentChip.css'
/** /**
* ROLE ICONS one per AI employee, true to what it does. * TWO SYMBOLS, ONE CONTRAST: a sparkle for the machines, a person for the
* people.
* *
* People are shown by their initials; the machines are shown by a symbol. That * The sparkle is the settled 2026 signal for AI the mark Claude, Gemini and
* contrast is the point: an operator can tell a human step from an autonomous * Copilot all use so an agent's disc reads as "made by AI" without a legend,
* one at a glance, before reading a name, and the symbol says which machine * in the agent's own colour. A person's disc carries a plain head-and-shoulders
* the one that CHECKS a lead, the one that TALKS to the customer, the one that * glyph, so a human step reads as human at the same glance. The contrast is the
* recommends COVER, the one that verifies IDENTITY, the one that prepares a * whole message: which of these did the machine do, and which did a person.
* REFERRAL. Monoline at disc size, so they stay legible on every hue. * Who exactly is said by the name beside the disc and by the colour.
*/ */
const ICONS = { function SparkleGlyph() {
// a magnifier Intake checks the lead is real and scores it return (
inspect: <><circle cx="6.6" cy="6.6" r="3.4" /><path d="M9.2 9.2 12 12" strokeLinecap="round" /></>, <svg className="who__glyph" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true">
// a speech bubble Engage talks to the customer <path d="M8 .8c.25 2.4.9 3.9 1.9 4.9 1 1 2.5 1.65 4.9 1.9v.8c-2.4.25-3.9.9-4.9 1.9-1 1-1.65 2.5-1.9 4.9h-.8c-.25-2.4-.9-3.9-1.9-4.9-1-1-2.5-1.65-4.9-1.9v-.8c2.4-.25 3.9-.9 4.9-1.9 1-1 1.65-2.5 1.9-4.9z" />
chat: <path d="M2.6 3.4h10.8v6.4H7l-2.8 2.2v-2.2H2.6z" strokeLinejoin="round" />, </svg>
// 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 }) { function PersonGlyph() {
const g = ICONS[name]
if (!g) return null
return ( return (
<svg className="who__glyph" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.35" aria-hidden="true"> <svg className="who__glyph who__glyph--person" viewBox="0 0 16 16" fill="none"
{g} stroke="currentColor" strokeWidth="1.5" aria-hidden="true">
<circle cx="8" cy="5.6" r="2.7" />
<path d="M2.9 13.8a5.1 5.1 0 0 1 10.2 0" strokeLinecap="round" />
</svg> </svg>
) )
} }
@ -48,7 +44,6 @@ function AgentGlyph({ name }) {
export default function AgentChip({ agentKey, name, size = 'sm', showName = true, onOpen }) { export default function AgentChip({ agentKey, name, size = 'sm', showName = true, onOpen }) {
const a = agentKey ? AGENTS[agentKey] : null const a = agentKey ? AGENTS[agentKey] : null
const label = a ? a.short : (name || 'System') const label = a ? a.short : (name || 'System')
const initials = a ? a.initials : initialsOf(name)
const tone = a ? a.tone : 'grey' const tone = a ? a.tone : 'grey'
const body = ( const body = (
@ -63,14 +58,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 Only agents get it. A trail where a human's disc also carried one
would say nothing at all. */} would say nothing at all. */}
<span className={`who__disc who__disc--${tone}`} aria-hidden="true"> <span className={`who__disc who__disc--${tone}`} aria-hidden="true">
{/* A machine wears its role icon; a person wears their initials. */} {/* A machine wears the sparkle; a person wears the person. */}
{a && a.icon ? <AgentGlyph name={a.icon} /> : initials} {a ? <SparkleGlyph /> : <PersonGlyph />}
{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" />
<path d="M10 8.2 10.5 9.6 11.9 10.1 10.5 10.6 10 12 9.5 10.6 8.1 10.1 9.5 9.6Z" />
</svg>
) : null}
</span> </span>
{showName ? <span className="who__name">{label}</span> : null} {showName ? <span className="who__name">{label}</span> : null}
{a ? <span className="who__sr"> (AI)</span> : null} {a ? <span className="who__sr"> (AI)</span> : null}

View File

@ -93,11 +93,15 @@
at 14, and 12px is the FLOOR reserved for uppercase eyebrows, never at 14, and 12px is the FLOOR reserved for uppercase eyebrows, never
for anything a person has to read. Size does the prioritising so the for anything a person has to read. Size does the prioritising so the
reader does not have to. */ reader does not have to. */
/* Shifted up one notch after an audit found 124 of ~200 sizings sitting at
12-13px the "meta" tier had quietly become the body size, so the whole
app read tiny and uniform with nothing for the eye to land on. Body is 14
now, values 15, emphasis 16; 12px stays for uppercase eyebrows only. */
--fs-3xs: 0.75rem; /* 12px — uppercase eyebrows only */ --fs-3xs: 0.75rem; /* 12px — uppercase eyebrows only */
--fs-2xs: 0.8125rem; /* 13px — timestamps, meta */ --fs-2xs: 0.875rem; /* 14px — was 13; the app's dominant size, now comfortable */
--fs-xs: 0.875rem; /* 14px — field labels */ --fs-xs: 0.9375rem; /* 15px — field labels */
--fs-sm: 0.9375rem; /* 15px — body, field values */ --fs-sm: 1rem; /* 16px — body, field values */
--fs-md: 1rem; /* 16px — emphasis */ --fs-md: 1.0625rem; /* 17px — emphasis */
--fs-lg: 1.125rem; /* 18px — panel titles */ --fs-lg: 1.125rem; /* 18px — panel titles */
--fs-xl: 1.375rem; /* 22px — section headings */ --fs-xl: 1.375rem; /* 22px — section headings */
--fs-2xl: 1.75rem; /* 28px — page title */ --fs-2xl: 1.75rem; /* 28px — page title */