14.1 Kanban column & draggable card
The deals view's spine. Each column = a pipeline stage with header (stage · count · sum value) and a scrollable list of cards. Each card has industry chip, value, company, primary contact, an activity-glyphs row, and the fit/intent rings (from 7.15) so “is this real?” reads at a glance. Drag-drop with mouse and keyboard (focus a card, space to pick up, arrows to move, space to drop). Multi-select with shift/ctrl-click. Reduced motion: hard cuts, no rotation overshoot.
Four-stage pipeline (one card lifted, one column flagged as drop)
.kanban · .kb-col · .kb-cardShowing the visual states: hover (one card raised), selected (one with the strong accent ring), dragging (one rotated and elevated), drop-target (one column highlighted). Real drag-drop wiring is the consumer's job — this is the visual contract.
BlueRock Health
Alicia Chen · CTO
Northpeak Logistics
Marcus Reid · Head of Ops
Skyhook Manufacturing
Sara Kim · VP Sales
Verkada Networks
James Park · Product
DataHub Ledger
Fran McAllister · CFO
CareSync Clinical
Dr. Liu · Director
Globalfreight Co.
Esra Dogan · COO
FastLane Couriers
Won by Alicia · 3d ago
<div class="kanban">
<div class="kb-col">
<div class="kb-col-head">
<span class="kb-col-stage">Qualified</span>
<span class="kb-col-count">12</span>
</div>
<div class="kb-col-sum">$148k pipeline</div>
<div class="kb-cards">
<div class="kb-card" tabindex="0">
<div class="kb-card-row1">
<span class="kb-card-industry">Healthcare</span>
<span class="kb-card-value">$48k</span>
</div>
<h4 class="kb-card-name">BlueRock Health</h4>
<p class="kb-card-contact">Alicia Chen · CTO</p>
<div class="kb-card-meta">
<span class="kb-card-glyphs">...activity icons...</span>
<span class="kb-card-rings">
<span class="score-ring score-ring--xs" data-band="high">...</span>
<span class="score-ring score-ring--xs score-ring--accent">...</span>
</span>
</div>
</div>
</div>
<button class="kb-col-add">+ Add deal</button>
</div>
<!-- Highlight a column as drop target during a drag -->
<div class="kb-col is-drop-target">...</div>
</div>.kanban { display: grid;
grid-template-columns: repeat(4, minmax(220px, 1fr));
gap: var(--s-4); align-items: flex-start; }
.kb-col { background: var(--bg-sunk);
border: 1px solid var(--hair); border-radius: var(--r-lg);
padding: var(--s-3); }
.kb-col.is-drop-target { background: var(--accent-soft);
border-color: var(--accent); border-style: dashed; }
.kb-card { background: var(--bg-paper); cursor: grab;
border: 1px solid var(--hair); border-radius: var(--r-md);
padding: var(--s-3) var(--s-4); }
.kb-card:hover { border-color: var(--accent);
transform: translateY(-2px); box-shadow: var(--sh-2); }
.kb-card.is-selected { box-shadow: var(--ring-accent-strong);
border-color: var(--accent); }
.kb-card.is-dragging { transform: rotate(-2deg) translateY(-2px);
box-shadow: var(--sh-3); cursor: grabbing; }
@media (prefers-reduced-motion: reduce) {
.kb-card.is-dragging { transform: none; }
}14.2 Pipeline stage segmented bar
Sits in a deal's page header. Past stages get a soft ink wash; the current stage fills in --accent; future stages stay transparent. Each cell takes a stage name plus an optional one-line metric (e.g. 2d spent, Day 4 current) — same body font throughout, no mono cruft. Click a future stage to advance (with a confirmation modal); click a past stage to revert (also with confirmation).
Five-stage segmented bar
.pip-bar · .pip-stageDrives both visual feedback (where am I?) and quick action (move me). Pair with the kanban (14.1) on a record-detail page so the user has both the high-level “deals” view AND a per-deal stage indicator.
<div class="pip-bar" role="navigation" aria-label="Pipeline stages">
<button class="pip-stage" data-state="past">
<span class="pip-stage-label">Lead</span>
<span class="pip-stage-days">2d</span>
</button>
<button class="pip-stage" data-state="past">
<span class="pip-stage-label">Qualified</span>
<span class="pip-stage-days">5d</span>
</button>
<button class="pip-stage" data-state="current">
<span class="pip-stage-label">Negotiation</span>
<span class="pip-stage-days">Day 4</span>
</button>
<button class="pip-stage" data-state="future">
<span class="pip-stage-label">Won</span>
</button>
</div>.pip-bar { display: flex; background: var(--bg-paper);
border: 1px solid var(--hair); border-radius: var(--r-pill);
overflow: hidden; }
.pip-stage { flex: 1; min-height: 64px; padding: 14px 12px;
background: transparent; border: 0;
display: flex; flex-direction: column; align-items: center;
justify-content: center; gap: 4px; cursor: pointer; }
.pip-stage:not(:last-child) { box-shadow: inset -1px 0 0 var(--hair); }
.pip-stage .pip-stage-label { font: 500 13px var(--f-body); color: var(--fg); }
.pip-stage .pip-stage-days { font: 400 11px var(--f-body); color: var(--fg-faint); }
.pip-stage[data-state="past"] {
background: color-mix(in oklab, var(--ink) 5%, transparent); }
.pip-stage[data-state="past"] .pip-stage-label { color: var(--fg-dim); }
.pip-stage[data-state="current"] { background: var(--accent); }
.pip-stage[data-state="current"] .pip-stage-label { color: var(--paper); font-weight: 600; }
.pip-stage[data-state="future"] .pip-stage-label { color: var(--fg-dim); }14.3 Activity timeline
The heart of the contact / company / deal page. A vertical hairline rail with iconified nodes per event. Filter chips at the top scope by type. Date dividers (“Today”, “Yesterday”) keep the chronology readable. Six row variants — email, conversation, meeting, note, stage-change, custom — each with collapsed and expanded states.
All six row variants in one feed
.act-timeline · .act-row[data-type]Email rows expand to show body + attachments + open/click meters. Conversation rows show transcript snippet. Meeting rows surface attendees + conference link. Notes render markdown. Stage-change shows from→to chips. Custom is the generic catch-all.
Email · “Re: Renewal proposal” to Alicia Chen
Hi Alicia — thanks for the quick turn. Attached is the proposal we discussed. Happy to walk through it on Thursday if helpful…
Conversation · 8m on chat with Alicia Chen
Email · “Quarterly check-in” to Alicia Chen
Quick note as we head into Q2 — wanted to share a couple of patterns we're seeing…
<div class="act-timeline">
<div class="act-filters">
<button class="act-filter is-active">All</button>
<button class="act-filter">Email</button>
...
</div>
<div class="act-divider">Today</div>
<!-- Stage-change row -->
<div class="act-row" data-type="stage-change">
<span class="act-icon">...</span>
<div class="act-body">
<div class="act-title">Stage moved <span class="act-by">by Alicia</span></div>
<div class="act-meta-row">
<span class="act-chip">Qualified</span>→
<span class="act-chip">Negotiation</span>
</div>
</div>
<span class="act-time">2:14 pm</span>
</div>
<!-- Email row (uses native <details> for expand/collapse) -->
<div class="act-row" data-type="email" data-expandable="true">
<span class="act-icon">...</span>
<details open>
<summary>
<div class="act-body">
<div class="act-title">Email · "Re: Renewal proposal"</div>
<div class="act-snippet">Hi Alicia...</div>
</div>
</summary>
<div class="act-expand">
...full body, attachments, open/click meters...
</div>
</details>
<span class="act-time">11:42 am</span>
</div>
<!-- 6 sub-types: email, conversation, meeting, note, stage-change, custom -->
</div>.act-row { display: grid;
grid-template-columns: 28px 1fr auto;
gap: var(--s-3); padding: var(--s-3) 0; position: relative; }
/* Vertical rail */
.act-row::before { content: ""; position: absolute;
left: 13px; top: 0; bottom: 0; width: 1px; background: var(--hair); }
.act-row:last-child::before { bottom: 50%; }
.act-row .act-icon { width: 28px; height: 28px; border-radius: 50%;
background: var(--bg-paper); border: 2px solid var(--hair);
position: relative; z-index: 1; }
/* Type-specific icon tints */
.act-row[data-type="email"] .act-icon { background: var(--info-soft); color: var(--info-text); }
.act-row[data-type="conversation"] .act-icon { background: var(--accent-soft); color: var(--accent-text); }
.act-row[data-type="meeting"] .act-icon { background: color-mix(in oklab, var(--ink) 8%, transparent); }
.act-row[data-type="note"] .act-icon { background: var(--warning-soft); color: var(--warning-text); }
.act-row[data-type="stage-change"] .act-icon { background: var(--success-soft); color: var(--success-text); }14.4 Inbox row
The unified inbox is the most-touched view. Each row is one task to dispatch: avatar of the related contact, title (the action verb-phrase), sub-line (company · industry · last activity), due time + priority chip on the right, snooze + complete buttons on hover. States: open · snoozed (muted) · overdue (red dot) · completed (struck-through, fades). Keyboard: e to complete, s to snooze, click to open.
All four states in one inbox
.inbox · .inbox-row[data-state]The row honours --row-h so it auto-densifies when body[data-density="compact"] is set (see 13.3). Overdue rows get the red dot indicator on the left. Completed rows fade so they're visible-but-de-emphasised before scrolling out.
<div class="inbox">
<div class="inbox-row" data-state="overdue" tabindex="0">
<span class="av">CV</span>
<div class="ix-body">
<div class="ix-title">Follow up on Verkada renewal</div>
<div class="ix-sub">Verkada Networks · tech · last Apr 18</div>
</div>
<span class="ix-due">overdue · 8d</span>
<span class="ix-priority" data-p="high">High</span>
</div>
<!-- States: open | snoozed | overdue | completed -->
<!-- Keyboard: e = complete · s = snooze -->
</div>.inbox-row { display: grid;
grid-template-columns: 36px 1fr auto auto;
gap: var(--s-3); align-items: center;
padding: 0 var(--s-4);
height: var(--row-h, var(--row-h-comfortable));
/* honours density mode automatically */ }
.inbox-row[data-state="overdue"]::before {
content: ""; position: absolute; left: 0; top: 50%;
transform: translateY(-50%);
width: 6px; height: 6px; border-radius: 50%;
background: var(--error);
box-shadow: 0 0 0 3px color-mix(in oklab, var(--error) 20%, transparent); }
.inbox-row[data-state="snoozed"] { opacity: 0.55; }
.inbox-row[data-state="completed"] .ix-title {
text-decoration: line-through; color: var(--fg-faint); }14.5 Onboarding checklist
Used for customer onboarding, renewal, and customer-success playbooks. Vertical list with tickbox · title · description · due-date · assignee · “more” menu. Progress bar at the top. Item states: open · done (struck-through, green tick) · skipped (italic, dimmed). Drag-to-reorder is admin-only.
Customer-onboarding playbook (10 items, 4 done)
.checklistClick the tickbox to toggle done/open. The progress bar updates from the count of done items. Description wraps; long descriptions get truncated with a “Show more” affordance in real use.
Customer onboarding · BlueRock Health
#customer-success + per-customer channels.<div class="checklist">
<div class="checklist-head">
<h4>Customer onboarding · BlueRock Health</h4>
<div class="checklist-progress">
<span><strong>4</strong> of 10 done</span>
<div class="checklist-progress-bar">
<div class="checklist-progress-fill" style="width: 40%;"></div>
</div>
<span>40%</span>
</div>
</div>
<div class="checklist-items">
<div class="checklist-item" data-state="done">
<span class="checklist-tick" role="checkbox" aria-checked="true">
<svg ...check.../>
</span>
<div class="checklist-body">
<div class="checklist-title">Kickoff call scheduled</div>
<div class="checklist-desc">30-min intro...</div>
</div>
<div class="checklist-item-meta">
<span class="checklist-due">Apr 8</span>
<span class="av">AC</span>
</div>
</div>
<!-- states: open | done | skipped -->
</div>
</div>.checklist-tick { width: 22px; height: 22px;
border: 1.8px solid var(--hair); border-radius: 50%;
background: var(--bg-paper); cursor: pointer; }
.checklist-item[data-state="done"] .checklist-tick {
background: var(--success); border-color: var(--success);
color: var(--paper); }
.checklist-item[data-state="done"] .checklist-title {
text-decoration: line-through; color: var(--fg-faint); }
.checklist-item[data-state="skipped"] {
opacity: 0.5; }
.checklist-item[data-state="skipped"] .checklist-title {
font-style: italic; }14.6 MRR composition chart
A vanilla SVG diverging-bar chart for the revenue dashboard. Positive components (New + Expansion) stack above the zero line; negative components (Contraction + Churn) stack below it. The hero number is the current month’s net; the breakdown row directly under it shows the math. An operator can scan the bars and instantly see which months were good, which had spiking churn, and whether expansion is keeping up with cancellations.
12-month MRR composition
.mrr-chartStatic SVG demo. In production, render the bars from your monthly data — the visual contract is what matters: positive stacks above zero, negative stacks below, bold zero rule, hairline gridlines, mono axis labels.
<div class="mrr-chart">
<div class="mrr-chart-head">
<div>
<div class="mrr-chart-title">Net new MRR · Last 12 months</div>
</div>
<div class="mrr-chart-value">$48.2k
<span class="mrr-chart-delta">+12.4%</span>
</div>
</div>
<!-- Breakdown row: shows the math under the headline number -->
<div class="mrr-breakdown">
<span class="mrr-bd"><i class="sw" style="background:var(--success);"></i>+58.0k <small>New</small></span>
<span class="mrr-bd"><i class="sw" style="background:var(--accent);"></i>+12.0k <small>Exp</small></span>
<span class="mrr-bd"><i class="sw" style="background:var(--warning);"></i>−7.5k <small>Contr</small></span>
<span class="mrr-bd"><i class="sw" style="background:var(--error);"></i>−14.3k <small>Churn</small></span>
<span class="mrr-bd-equals">=</span>
<span class="mrr-bd is-net">+48.2k <small>Net</small></span>
</div>
<!-- Diverging bars: positives stack ABOVE the zero rule, negatives BELOW.
Scale: 2 px per $1k. Zero line at y=200. Each month is a 53px column
with a 36px bar. Per month emit 4 rects in the bar group. -->
<svg viewBox="0 0 720 320">
<line x1="60" y1="200" x2="696" y2="200" stroke="var(--hair)" stroke-width="1.2"/> <!-- zero rule -->
<g> <!-- one <g> per month: New, Expansion, Contraction, Churn -->
<rect x="68" y="84" width="36" height="116" fill="var(--success)"/> <!-- New (above 0) -->
<rect x="68" y="60" width="36" height="24" fill="var(--accent)"/> <!-- Expansion (stacked) -->
<rect x="68" y="200" width="36" height="15" fill="var(--warning)"/> <!-- Contraction (below 0) -->
<rect x="68" y="215" width="36" height="29" fill="var(--error)"/> <!-- Churn (stacked below) -->
</g>
<!-- ... 11 more month groups ... -->
</svg>
<div class="mrr-legend">
<span class="mrr-legend-item">
<span class="mrr-legend-swatch" style="background:var(--success);"></span>
New <small>(acquisition)</small>
</span> ...
</div>
</div>.mrr-chart { background: var(--bg-paper); padding: var(--s-5);
border: 1px solid var(--hair); border-radius: var(--r-lg); }
.mrr-chart svg { width: 100%; height: auto; display: block; }
/* Breakdown — the math under the hero number */
.mrr-breakdown {
display: flex; align-items: center; gap: var(--s-4);
padding: var(--s-3) var(--s-4); margin-bottom: var(--s-4);
background: var(--warm-3); border-radius: var(--r-md);
font: 500 12px var(--f-mono); font-variant-numeric: tabular-nums;
}
body[data-theme="dark"] .mrr-breakdown { background: var(--bg-sunk); }
.mrr-bd { display: inline-flex; align-items: center; gap: 6px; }
.mrr-bd .sw { width: 10px; height: 10px; border-radius: 2px; }
.mrr-bd small { color: var(--fg-dim); text-transform: uppercase; letter-spacing: 0.06em; }
.mrr-bd.is-net { font-weight: 700; }
.mrr-legend-item { display: inline-flex; align-items: center;
gap: 6px; font: 500 12px var(--f-body); cursor: pointer; }
.mrr-legend-item.is-off { opacity: 0.4; }
.mrr-legend-item.is-off .mrr-legend-swatch { filter: grayscale(0.8); }
/* In real use, render bars from your monthly data: per month, emit
four rects — two stacked above y=0 (New, Expansion), two stacked
below (Contraction, Churn). Scale = px-per-dollar. */