Business Performance Analyst Agent
Course demonstration · Capstone 2 · Agentic AI Workshop 2026

Design System: Business Performance Analyst Agent

Overview

Creative North Star: "The Six-Minute Mixtape"

The demo looks like an unfolded cassette J-card (the paper insert in a cassette case), written up by hand. A vertical spine runs down the left edge with the title, a red triple stripe and an A/B box. The main panel is ruled inlay stock. Every screen is a part of the card: the tracklist is the demo plan, each prepared case is a track with a running time, side A holds the brief's questions and side B the guardrails. The tape counter and a thin red position bar move while a run plays. Text is ballpoint ink throughout. Red is the printed second colour: the stripe, the running bar, corrections and refusals. Charts are drawn in the same ink on the same paper.

The system is flat, square and inked. Depth comes from rules and boxes; there are no shadows. Type carries the brand: hand-lettered block caps for anything you would letter onto a card, handwritten prose for answers, and typewriter for anything the machine produced (SQL, tool names, counters, figures). It deliberately avoids the usual dark AI dashboard with KPI tiles and chat bubbles.

It is built to be read from the back of a lit room on a washed-out projector. The root font size is large (18px, rising to 21px at 1800px and wider), contrast is ink on near-white, and no state relies on colour alone: the active screen gets an underline, the current track gets a wash and a red number, and a dry run gets a red box plus written words.

Main characteristics:

Colors

A two-ink print on cream-white stock: one deep ballpoint blue carries all text and structure, one printed red carries signals, and a set of pale blues is used for rules and washes.

In app/web/app.css the colours are CSS custom properties on :root. The CSS names are shorter than the token names used on this page (--ink is Ballpoint Ink, --ink-2 is Faded Ink, --rule-mid is Mid Rule, --pencil is Pencil Grey):

:root {
  --paper: #fcfbf7;     /* inlay-paper */
  --wash: #eef2fb;      /* blue-wash */
  --ink: #1e3a8a;       /* ballpoint-ink */
  --ink-2: #3f5a9e;     /* faded-ink */
  --rule: #dbe2f1;      /* ruled-line */
  --rule-mid: #a9b8dc;  /* mid-rule */
  --red: #c62828;       /* printed-red */
  --red-ink: #b71c1c;   /* red-ink */
  --pencil: #687083;    /* pencil-grey */
  ...
}

Primary

Secondary

Neutral

Chart palette

Charts are drawn by matplotlib in app/analyst/tools.py on the same paper, with the same fonts. The colours are set at the top of the chart code:

_INK, _INK_SOFT, _PAPER, _RULE, _OTHER = "#1E3A8A", "#3F5A9E", "#FCFBF7", "#D3DBEE", "#8E98AE"
_SERIES = ["#2B4BB0", "#B7791F", "#6B86C5"]   # red is kept for negative values only
_NEG = "#C62828"

This categorical set was checked for colour-blind separation and contrast against Inlay Paper.

Named Rules

The Red Is Not Data Rule. Printed Red never encodes a data series. In charts it marks negative values only (negative bars turn red). In the UI it means signal: brand stripe, running, correction, refusal, current position. If a third series is needed, use Chart Faded Blue, never red.

The Two-Ink Rule. Interface chrome uses only the blues and the red. Ochre lives in charts and nowhere else.

The Paper Is Not White Rule. Surfaces are Inlay Paper. Blank White appears only inside fields and typed blocks, the places where something is written or typed onto the card.

Typography

Display Font: Patrick Hand SC (with Patrick Hand, Marker Felt, cursive) Body Font: Patrick Hand (with Marker Felt, cursive) Label/Mono Font: Courier Prime (with Courier New, monospace)

All three are self-hosted OFL (open-licence) fonts in app/web/fonts, loaded with font-display: block so the card never flashes a system font while they load. The downloadable briefing embeds them as base64, so it looks the same when emailed or printed.

@font-face { font-family: "Patrick Hand SC"; src: url("fonts/PatrickHandSC-Regular.ttf") format("truetype"); font-display: block; }
@font-face { font-family: "Patrick Hand"; src: url("fonts/PatrickHand-Regular.ttf") format("truetype"); font-display: block; }
@font-face { font-family: "Courier Prime"; src: url("fonts/CourierPrime-Regular.ttf") format("truetype"); font-weight: 400; font-display: block; }

Character: Block caps lettered with a marker for anything a person would write on the card, a looser hand for the answer prose, and a typewriter label for anything the machine produced. Hand-lettered headings (h1–h3) carry a thin -webkit-text-stroke of 0.035em in the current colour (paint-order: stroke fill) so they look as if drawn with a marker. Track titles, nav links, buttons, scorecard questions, the side total and the spine title carry a lighter 0.02em stroke:

h1, h2, h3 { font-family: var(--hand-sc); font-weight: 400; -webkit-text-stroke: 0.035em currentColor; paint-order: stroke fill; }
.track .t, .list li .t, .score-row .q, .masthead nav a, .btn, .rule-head .total, .spine .title { -webkit-text-stroke: 0.02em currentColor; }

Hierarchy

The root is 18px, 21px at 1800px and wider (for projectors), and 16px at 1100px and below. All sizes are in rem, so they scale from that root:

html { font-size: 18px; }
@media (min-width: 1800px) { html { font-size: 21px; } }
@media (max-width: 1100px) { html { font-size: 16px; } }

Named Rules

The Three Voices Rule. Lettered caps for labels and titles, handwriting for prose, typewriter for anything the machine produced. Never set SQL or numbers from a query in a hand face, and never set prose in the typewriter.

The Marker Weight Rule. The hand faces only come in weight 400, so weight comes from the text stroke, never from font-weight: bold (which would synthesise a faux bold). Use 0.035em on headings and 0.02em on lettered labels.

Layout

The page is a two-column grid: a 6rem spine and the card. The spine is sticky and full height: the red triple stripe (three 5px bars, 4px apart), the catalogue mark ("6:00 / CP-02 / 1–2 Oct 26": the demo running time, Capstone Project 2 of 5, and the workshop dates), the vertical title rotated to read bottom-up, and the A/B box with the date span. The masthead is sticky, 4.5rem high, ruled underneath with 2px ink, and holds the screen nav on the left with the mode switch and tape counter on the right. Directly under it is a 4px red position bar that scales from the left as a run plays.

Main content has 2.25rem side gutters and 2rem top padding. The paper is ruled every 2rem (ruled-line), and list rows, requirement rows and track rows are at least one ruled line high so they sit on the lines. The rules are a hard-stop gradient on the page background:

body {
  background-color: var(--paper);
  background-image: linear-gradient(to bottom, transparent calc(var(--line) - 1px), var(--rule) calc(var(--line) - 1px), var(--rule) var(--line), transparent var(--line));
  background-size: 100% var(--line);   /* --line: 2rem */
}

On smaller screens: at 1100px and below the root drops to 16px, and every split collapses to one column; the tapelist stops being sticky. At 700px and below the spine becomes a horizontal top strip (no A/B box, a wrapping title), the masthead wraps and stops being sticky, gutters shrink to 1rem, and requirement rows drop their category and points columns.

Elevation & Depth

The system is flat. There is no box-shadow anywhere in the build. Hierarchy comes from line weight and from the paper: 1px Mid Rule for row dividers, 1.5px ink for boxes, tags and controls, 2px ink for structural edges (spine, masthead, buttons), 2.5px ink under section heads, and 3–4px ink bars under the big titles. The only "lift" is the Blue Wash on hover and on the current row, and the inverted ink fill for active states.

Named Rules

The Ink, Not Light Rule. To separate something, give it a heavier rule or a box. Do not use a shadow, a blur or a soft gradient. Hard-stop gradients are fine only as a way to draw ruled lines (the paper rules, the tracklist margin line).

Shapes

Every corner is square (rounded.none). Inputs explicitly reset to a 0 radius. Boxes are hairline ink outlines on paper, like the printed panels of a J-card. Recurring marks: - the red triple stripe on the spine; - a 1px red vertical margin line running through the tracklist behind the track numbers, like the margin rule on lined paper; - a heavy ink rule under every heading; - bracketed red footnote numbers [1], [2] before each liner-note query; - line icons (play, flip, download, tick, cross, plus, pen) drawn at 2px with round caps and joins, 1.1em, in the current colour.

The one rounded shape is the WebKit scrollbar thumb (6px), which is browser chrome rather than part of the card.

Components

Buttons

Buttons use lettered caps in square ink boxes, like boxes ruled onto the card. - Shape: square (rounded.none), 2px ink border, icon and label 0.55rem apart. - Primary: ink fill with paper text; for the main action on a screen ("Play side A", "Ask"). - Secondary: paper fill with an ink outline and ink text. - Warn: red outline with Red Ink text, for destructive or reset actions. - Hover / Active: secondary hovers to Blue Wash, primary darkens to Pressed Ink (160ms); press nudges down 1px (120ms). - Disabled: Pencil Grey text and a Mid Rule border on paper; not-allowed cursor. - Link-style action: an underlined handwriting link ("New conversation") for low-stakes actions.

Mode switch

A segmented box of three lettered buttons (Live AI, Replay, Dry run) joined by 1.5px ink dividers. The active mode is filled with ink. The mode is also restated on the now-playing inlay as a tag: Live is a solid ink tag, Replay an outline tag, Dry run a red-outlined tag plus a written red note. The mode is never shown by colour alone.

Tags

Cards / Containers (panels)

Inputs / Fields

:focus-visible { outline: 2px solid var(--red); outline-offset: 3px; }
body { caret-color: var(--red); }

Lettered caps links in the masthead, 1.6rem apart, with no default underline. Hover shows a 2.5px Mid Rule underline. The current page gets a 2.5px Printed Red underline (aria-current="page"). At 700px and below the links wrap.

Tracklist (signature)

The demo plan as a cassette tracklist inside a ruled box. Side A / Side B tabs (a segmented box like the mode switch) flip the face with a y-axis turn (rotate to 90°, 220ms, opacity to 0.2). Each track is a full-width button laid out as number · title · running time, with a Pencil Grey sub-line when a recording is missing. Hover and current rows get Blue Wash, and the current number turns Red Ink. The side's summed time sits in the heading against 6:00, and the "both sides" budget sits underneath. A hidden bonus track sits below a typed gap marker and is labelled as a planted rehearsal anomaly. Pressing a track plays it: this is the screen's primary action.

Step log (signature)

A numbered list where each tool step is inked in as it arrives, with a left-to-right clip-path reveal of 340ms:

@keyframes ink { from { clip-path: inset(0 100% 0 0); } to { clip-path: inset(0 0 0 0); } }
.steps li { border-bottom: 1px solid var(--rule-mid); animation: ink 340ms var(--ease) both; }

Each row shows a lettered number, the bold typed tool name and a handwriting summary. Refused or errored steps turn Red Ink. Queries fold open in a typed SQL block. While the agent works, a "writing…" line ends in a blinking red caret block (1s, stepped).

Liner notes

The "How I got this" section is an ordered list of the exact queries from the tool log. Each has a red bracketed number, a typed label and a typed SQL block (Blank White, 1px Mid Rule border). A typed footer gives tool calls, tokens, seconds and model.

Tape counter and position bar

A bold typed counter (tabular numbers) in the masthead counts up while a run plays, with a smaller Faded Ink suffix, and turns Red Ink when over. A 4px red bar under the masthead scales across in step (250ms linear).

Margin note and slip

Presenter-only "Point out:" notes, and refusal or limit slips: handwriting in Red Ink inside a 1.5px red box on paper, with the lead word in lettered caps.

Ruled table

Lettered header cells on a 2.5px ink rule, rows on 1px Mid Rule, numeric cells in the typewriter, right-aligned with tabular numbers. Used for KPIs, tool results and the briefing.

Chart figure

Charts are matplotlib PNGs on Inlay Paper, with a lettered caps title on the left (19pt), handwriting tick labels (15pt), typewriter value labels (14pt), gridlines only on the value axis, and a single ink baseline. They are shown with a 1px Mid Rule border and a Faded Ink caption ("chart_1: …").

Downloadable briefing page

A self-contained miniature of the card at fixed pixel sizes: a 64px spine with a 3-bar red stripe and vertical title, 17px handwriting body, rules every 28px, a 32px lettered h1 on a 3px rule, tables and liner notes as in the app, and embedded fonts. The outer border is dropped when printing.

Do's and Don'ts

Do:

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; transition: none !important; }
}

Don't:

A learning project, not a product. AdventureWorks is Microsoft's public sample data about a made-up bicycle company. Made by Victor Saly, Akashdeep Nijjar, Manuel Verduzco Valenzuela, Mazen Ahmed, Buddhika Gamage, Nathan Fryatt, Michael Kampouridis and Malak Sheat (the team). Source of this page: docs/design.md.