diff --git a/src/design-system/primitives/Card/Card.module.css b/src/design-system/primitives/Card/Card.module.css
new file mode 100644
index 0000000..aa9085b
--- /dev/null
+++ b/src/design-system/primitives/Card/Card.module.css
@@ -0,0 +1,13 @@
+.card {
+ background: var(--bg-surface);
+ border: 1px solid var(--border);
+ border-radius: var(--radius-md);
+ box-shadow: var(--shadow-card);
+ overflow: hidden;
+}
+
+.accent-amber { border-top: 3px solid var(--amber); }
+.accent-success { border-top: 3px solid var(--success); }
+.accent-warning { border-top: 3px solid var(--warning); }
+.accent-error { border-top: 3px solid var(--error); }
+.accent-running { border-top: 3px solid var(--running); }
diff --git a/src/design-system/primitives/Card/Card.tsx b/src/design-system/primitives/Card/Card.tsx
new file mode 100644
index 0000000..e6656d4
--- /dev/null
+++ b/src/design-system/primitives/Card/Card.tsx
@@ -0,0 +1,18 @@
+import styles from './Card.module.css'
+import type { ReactNode } from 'react'
+
+interface CardProps {
+ children: ReactNode
+ accent?: 'amber' | 'success' | 'warning' | 'error' | 'running' | 'none'
+ className?: string
+}
+
+export function Card({ children, accent = 'none', className }: CardProps) {
+ const classes = [
+ styles.card,
+ accent !== 'none' ? styles[`accent-${accent}`] : '',
+ className ?? '',
+ ].filter(Boolean).join(' ')
+
+ return
{children}
+}
diff --git a/src/design-system/primitives/FilterPill/FilterPill.module.css b/src/design-system/primitives/FilterPill/FilterPill.module.css
new file mode 100644
index 0000000..d588449
--- /dev/null
+++ b/src/design-system/primitives/FilterPill/FilterPill.module.css
@@ -0,0 +1,54 @@
+.pill {
+ display: inline-flex;
+ align-items: center;
+ gap: 5px;
+ padding: 4px 10px;
+ border: 1px solid var(--border);
+ border-radius: 20px;
+ background: var(--bg-raised);
+ color: var(--text-secondary);
+ font-family: var(--font-body);
+ font-size: 11px;
+ font-weight: 500;
+ cursor: pointer;
+ transition: all 0.15s;
+ white-space: nowrap;
+}
+
+.pill:hover {
+ border-color: var(--text-faint);
+ color: var(--text-primary);
+}
+
+.active {
+ background: var(--amber-bg);
+ border-color: var(--amber-light);
+ color: var(--amber-deep);
+ font-weight: 600;
+}
+
+.dot {
+ width: 6px;
+ height: 6px;
+ border-radius: 50%;
+ background: var(--text-faint);
+ flex-shrink: 0;
+}
+
+.label {
+ line-height: 1;
+}
+
+.count {
+ font-family: var(--font-mono);
+ font-size: 10px;
+ font-weight: 600;
+ background: var(--bg-inset);
+ border-radius: 8px;
+ padding: 0 5px;
+ line-height: 1.6;
+}
+
+.active .count {
+ background: var(--amber-light);
+}
diff --git a/src/design-system/primitives/FilterPill/FilterPill.tsx b/src/design-system/primitives/FilterPill/FilterPill.tsx
new file mode 100644
index 0000000..90173f6
--- /dev/null
+++ b/src/design-system/primitives/FilterPill/FilterPill.tsx
@@ -0,0 +1,42 @@
+import styles from './FilterPill.module.css'
+
+interface FilterPillProps {
+ label: string
+ count?: number
+ active?: boolean
+ dot?: boolean
+ dotColor?: string
+ onClick?: () => void
+ className?: string
+}
+
+export function FilterPill({
+ label,
+ count,
+ active = false,
+ dot = false,
+ dotColor,
+ onClick,
+ className,
+}: FilterPillProps) {
+ const classes = [
+ styles.pill,
+ active ? styles.active : '',
+ className ?? '',
+ ].filter(Boolean).join(' ')
+
+ return (
+
+ )
+}
diff --git a/src/design-system/primitives/Sparkline/Sparkline.test.tsx b/src/design-system/primitives/Sparkline/Sparkline.test.tsx
new file mode 100644
index 0000000..a0210db
--- /dev/null
+++ b/src/design-system/primitives/Sparkline/Sparkline.test.tsx
@@ -0,0 +1,16 @@
+import { describe, it, expect } from 'vitest'
+import { render } from '@testing-library/react'
+import { Sparkline } from './Sparkline'
+
+describe('Sparkline', () => {
+ it('renders an SVG with a polyline', () => {
+ const { container } = render()
+ expect(container.querySelector('svg')).toBeInTheDocument()
+ expect(container.querySelector('polyline')).toBeInTheDocument()
+ })
+
+ it('returns null for less than 2 data points', () => {
+ const { container } = render()
+ expect(container.firstChild).toBeNull()
+ })
+})
diff --git a/src/design-system/primitives/Sparkline/Sparkline.tsx b/src/design-system/primitives/Sparkline/Sparkline.tsx
new file mode 100644
index 0000000..93f06c7
--- /dev/null
+++ b/src/design-system/primitives/Sparkline/Sparkline.tsx
@@ -0,0 +1,51 @@
+interface SparklineProps {
+ data: number[]
+ color?: string
+ width?: number
+ height?: number
+ strokeWidth?: number
+ className?: string
+}
+
+export function Sparkline({
+ data,
+ color = 'var(--amber)',
+ width = 60,
+ height = 20,
+ strokeWidth = 1.5,
+ className,
+}: SparklineProps) {
+ if (data.length < 2) return null
+
+ const max = Math.max(...data)
+ const min = Math.min(...data)
+ const range = max - min || 1
+ const padding = 1
+
+ const points = data
+ .map((val, i) => {
+ const x = (i / (data.length - 1)) * (width - padding * 2) + padding
+ const y = height - padding - ((val - min) / range) * (height - padding * 2)
+ return `${x},${y}`
+ })
+ .join(' ')
+
+ return (
+
+ )
+}
diff --git a/src/design-system/primitives/StatCard/StatCard.module.css b/src/design-system/primitives/StatCard/StatCard.module.css
new file mode 100644
index 0000000..4e4d4fc
--- /dev/null
+++ b/src/design-system/primitives/StatCard/StatCard.module.css
@@ -0,0 +1,62 @@
+.card {
+ background: var(--bg-surface);
+ border: 1px solid var(--border);
+ border-radius: var(--radius-md);
+ box-shadow: var(--shadow-card);
+ padding: 16px 18px;
+ overflow: hidden;
+}
+
+.accent-amber { border-top: 3px solid var(--amber); }
+.accent-success { border-top: 3px solid var(--success); }
+.accent-warning { border-top: 3px solid var(--warning); }
+.accent-error { border-top: 3px solid var(--error); }
+.accent-running { border-top: 3px solid var(--running); }
+
+.header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-bottom: 6px;
+}
+
+.label {
+ font-size: 10px;
+ font-weight: 600;
+ text-transform: uppercase;
+ letter-spacing: 1.2px;
+ color: var(--text-muted);
+}
+
+.sparkline {
+ opacity: 0.7;
+}
+
+.valueRow {
+ display: flex;
+ align-items: baseline;
+ gap: 8px;
+}
+
+.value {
+ font-family: var(--font-mono);
+ font-size: 28px;
+ font-weight: 600;
+ color: var(--text-primary);
+ line-height: 1;
+}
+
+.trend {
+ font-size: 11px;
+ font-weight: 500;
+}
+
+.trend.up { color: var(--success); }
+.trend.down { color: var(--error); }
+.trend.neutral { color: var(--text-muted); }
+
+.detail {
+ font-size: 11px;
+ color: var(--text-muted);
+ margin-top: 6px;
+}
diff --git a/src/design-system/primitives/StatCard/StatCard.tsx b/src/design-system/primitives/StatCard/StatCard.tsx
new file mode 100644
index 0000000..ac13196
--- /dev/null
+++ b/src/design-system/primitives/StatCard/StatCard.tsx
@@ -0,0 +1,48 @@
+import styles from './StatCard.module.css'
+import { Sparkline } from '../Sparkline/Sparkline'
+
+interface StatCardProps {
+ label: string
+ value: string | number
+ detail?: string
+ trend?: 'up' | 'down' | 'neutral'
+ trendValue?: string
+ accent?: 'amber' | 'success' | 'warning' | 'error' | 'running'
+ sparkline?: number[]
+ className?: string
+}
+
+const TREND_ICONS = {
+ up: '↑',
+ down: '↓',
+ neutral: '→',
+}
+
+export function StatCard({
+ label,
+ value,
+ detail,
+ trend,
+ trendValue,
+ accent = 'amber',
+ sparkline,
+ className,
+}: StatCardProps) {
+ return (
+
+
+ {label}
+ {sparkline && }
+
+
+ {value}
+ {trend && trendValue && (
+
+ {TREND_ICONS[trend]} {trendValue}
+
+ )}
+
+ {detail &&
{detail}
}
+
+ )
+}