01 · Foundations

The raw material.

Twenty-three color tokens with a light and dark value each, one type family in two voices, eight radius steps, two shadows, five durations. Every swatch and specimen below is rendered live from tokens.css — flip the theme and watch.

tokens.css — copy link tag
01.1

Color

One palette, two rooms.

Each token holds a light and a dark value; consumers only ever write the variable. Click any name or hex to copy it. Note what isn't here: no brand blue, no decorative hues. Violet belongs to the agent, green/amber/red belong to the operation.

Surfaces & lines

TokenLightDarkRole
--bg #ffffff #08090a Page background
--bg-elev #ffffff #101113 Cards, panels, anything raised
--bg-sunken #fafafa #0c0d0e Wells, code blocks, stat cards
--line #e9e9e9 #1c1d20 Default hairline border
--line-strong #d8d8d8 #2a2b2f Hover borders, emphasized rules

Ink

TokenLightDarkRole
--fg #0a0a0a #f5f5f5 Headlines, primary text
--fg-secondary #3a3a3a #b8b8b8 Body copy in dense UI
--fg-muted #6b6b6b #8a8a8a Supporting copy, ledes, metadata
--fg-subtle #9a9a9a #5a5a5a Index numbers, faint labels

Accent — the accent is ink

TokenLightDarkRole
--accent #0a0a0a #f5f5f5 Primary actions — near-black, never a hue
--accent-hover #2a2a2a #e4e4e4 Primary action hover
--accent-soft #f4f4f5 rgba(255,255,255,0.1) Selected states, quiet emphasis fills
--accent-line #e4e4e7 rgba(255,255,255,0.18) Borders on accent-soft surfaces
--accent-fg #ffffff #0a0a0a Text on accent fills

AI — the only hue in the chrome

TokenLightDarkRole
--ai #6b4fe0 #b3a4ff Only where an agent perceives, decides, or acts
--ai-soft #f0ecfb rgba(179,164,255,0.14) Agent surface fills
--ai-line #ddd3f7 rgba(179,164,255,0.3) Agent surface borders

Status — real states only

TokenLightDarkRole
--ok #067647 #4ade80 Filled zone, verified check-in, live
--ok-soft #ecfdf3 rgba(74,222,128,0.12) ok pill fill
--warn #b45309 #f59e0b Zone trending short, silent worker
--warn-soft #fef3c7 rgba(245,158,11,0.12) warn pill fill
--danger #b42318 #f87171 Short gate, failed credential
--danger-soft #fee4e2 rgba(248,113,113,0.12) danger pill fill
01.2

Typography

Geist speaks. Geist Mono reports.

One family, two voices. Sans carries the argument; mono carries the data — timestamps, zone names, fill percentages, anything an operator reads off a radio. Headings run tight (−0.02 to −0.035em) and medium-to-semibold, never bold. tokens.css ships stacks with system fallbacks; load Geist yourself for the full effect.

display
clamp(2.75rem–5rem) · 600
−0.035em · lh 0.98
Orchestrate
h2
clamp(1.875rem–2.25rem) · 500
−0.025em
A thousand shifts. Every source.
h3 / card title
19px · 500 · −0.015em
Your people get first claim
lede
18px · 400 · lh 1.6
--fg-muted
Internal-first scheduling pushes only the true gaps outside.
body
16px · 400 · lh 1.6
Confirmed, location-verified hours become itemized invoices and payroll — in minutes.
small / meta
13px · 400 · --fg-muted
Product · Jul 8, 2026
eyebrow (mono)
11px · 500 · uppercase
0.14em tracking
Intelligent fill · Gap analysis
mono data
11–12px · 400/500
tabular-nums
Gate A · 24/24  ·  T-1 day  ·  91% · forecast beaten $8,220
--font-sans: "Geist", "Geist Variable", ui-sans-serif, system-ui, sans-serif;
--font-mono: "Geist Mono", "Geist Mono Variable", ui-monospace, monospace;

/* eyebrow — the system's smallest, loudest voice */
.eyebrow {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--fg-muted);
}
01.3

Radius

4 to 28, then full.

Small elements stay crisp; containers get generous. Buttons and pills always run --radius-full. Click a box to copy its variable.

--radius-xs · 4px
--radius-sm · 6px
--radius-md · 8px
--radius-lg · 12px
--radius-xl · 16px
--radius-2xl · 20px
--radius-3xl · 28px
--radius-full

Assignments: inline code 4px · swatches 6px · inputs and nav links 8px · mock rows 12px · stat cards 16px · cards and specimens 20px · photo cutouts and hero media 28px · buttons, pills, chips full.

01.4

Layout & spacing

1280 wide, on a 4px grid.

One container: --container = 1280px, with 20 / 24 / 32px gutters as the viewport grows. Sections breathe at 96px vertical (56px on docs pages); cards pad 20–24px. Everything sits on the 4px grid.

4pxhairline gaps
8pxchip gaps, icon gaps
12pxrow padding, tool gaps
16pxgrid gaps between cards
24pxcard padding
32pxdesktop gutters
48pxheading → content
64pxpage head padding
96pxsection rhythm (py-24)
01.5

Elevation

Two shadows. Borders do the rest.

Depth is mostly a 1px line in this system. Shadows exist for two jobs only: pop lifts floating chrome (toasts, mega panels), cmd anchors product mockups so they read as windows. Both are black-based and shared across themes — in dark they read as deeper ambient shade, and the slightly stronger dark hairlines carry the edges.

--shadow-pop
--shadow-cmd
--shadow-pop: 0 1px 2px rgba(0,0,0,0.04), 0 8px 24px -8px rgba(0,0,0,0.4);
--shadow-cmd: 0 1px 2px rgba(0,0,0,0.4), 0 16px 48px -16px rgba(0,0,0,0.8);
01.6

Motion

Motion states facts.

Four moves, each with one meaning: fade-up reveals content as you reach it, pulse-dot means live data, marquee carries long lists, aurora drift warms empty backgrounds. Ease is --ease-out-expo; hovers resolve in 150ms; reveals in 600ms. Everything respects prefers-reduced-motion.

fade-up · 600ms · ease-out-expo

Gates open
every section reveals like this

pulse-dot · 2s · means live

LIVE deploy · live gap analysis

marquee · 40s · linear · edge-faded

Gate A · 24/24 Club Level · 18/20 Concourse · 40/40 Suites · 10/12 Paddock · 16/16 Back of House · 30/30 Gate A · 24/24 Club Level · 18/20 Concourse · 40/40 Suites · 10/12 Paddock · 16/16 Back of House · 30/30

aurora drift · 16–20s · hero backgrounds only

--ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
--dur-fast: 150ms;    /* hovers, toggles */
--dur-med: 300ms;     /* accordions, panels */
--dur-reveal: 600ms;  /* fade-up on scroll */
--dur-pulse: 2s;      /* live dots */
--dur-marquee: 40s;   /* ticker loops */
01.7

Grain

Grain kills the banding.

Every editorial gradient wears a fine SVG-noise overlay at 3.5% opacity — enough to break up banding and give flat color a printed feel, never enough to read as texture. Add class .grain to any positioned element.

without grain

with .grain

.grain { position: relative; }
.grain::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0.035;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}