Component Library
All UI components currently in use across the site.
Typography
h1 — text-4xl font-semibold tracking-tight
I ship product.
I also write the code.
h2 — text-3xl font-semibold tracking-tight
About
Section label — text-xs font-mono uppercase tracking-widest text-zinc-400
Work
Eyebrow — text-sm font-mono text-zinc-500
Senior PM · Builder
Body large — text-lg text-zinc-500 leading-relaxed
Nine years in product at HubSpot across AI, mobile, and growth. I use AI-native workflows to go from idea to working product — and I build my own tools in the gaps.
Body — text-sm text-zinc-500 leading-relaxed
Built mobile reporting from scratch, targeting the sales leader persona. Grew to 55k weekly users. Led the React Native migration that cut feature delivery time by 3×.
Label / meta — text-xs font-mono text-zinc-400
2021 – 2024
Hero
Senior PM · Builder
I ship product.
I also write the code.
Nine years in product at HubSpot across AI, mobile, and growth. I use AI-native workflows to go from idea to working product — and I build my own tools in the gaps.
Variants
Sizes
States
Badge
shadcn variants
As used in the site — tech stack labels
Input
Default
Password (as used in CaseStudyGate)
Disabled
ProjectCard
A personal finance tracker that turns bank statements into weekly insights. No manual entry, no syncing — just upload a CSV and get a clean summary.
Interview Prep Tool
AI-powered mock interview sessions with structured feedback. Generates questions from a job description and scores answers against the rubric.
ProjectGrid — section header pattern
Work
↳ followed by the grid of ProjectCards above
SkillCard — used on /skills
Skill name in mono (it's invoked as a command), description, optional external source link. Used in a 2-column grid per tier.
Experience row
Senior PM, AI Personalization & Ecosystem
HubSpot
2025 – present
Building Breeze Projects — the personalization layer for Breeze Assistant — and the MCP ecosystem strategy that connects third-party apps to Breeze.
Senior PM, Growth & Onboarding
HubSpot
2024 – 2025
Led HubSpot's #1 2025 product priority: product-led onboarding for Pro customers. Shipped an AI conversational onboarding agent that doubled plan adoption.
CaseStudyGate
This case study is private.
Loading state
Screenshot — ChromeBar
ChromeBar with URL
screenshot image here
ChromeBar without URL
screenshot image here
BeforeAfter
Before/after pair, side by side. Each image is click-to-expand (full-screen lightbox, Escape or click to close). Stacks on mobile.
Before
before.png
After
after.png
MediaPair
Two media side by side, top-aligned, equal-width columns (stacks on mobile). Pass any two children — typically a Screenshot and a Video. Use each child's mediaClassName to lock both to the same height, e.g. h-64 sm:h-72 with object-contain / object-cover.
screenshot.png
demo.mp4
MobileScreenshots
2-column grid
mobile-1.png
mobile-2.png
3-column grid
1
2
3
Single (max-w-xs centered)
single mobile screen
Video
HTML5 player with optional caption — for screen recordings and demos. Chrome-free, controls on.
video player (controls, poster)
Metrics
A row of hard numbers. Value + mono label. Supports 2, 3, or 4 items.
Card (shadcn — available, not used directly on site yet)
Card content area. Any elements can go here.
ComparisonTable
Options laid side by side. First column is the row label (zinc-700), the rest is prose (zinc-500). Cells accept any ReactNode. Scrolls horizontally below ~680px.
| Approach | How it works | Glass box? | Verdict |
|---|---|---|---|
| Learned baseline | A model trained on past runs flags anomalies. | No - opaque by construction | Left out. Verdicts can’t be traced. |
| Explicit expectation | Declare what a step should return, check it. | Yes | Built. Inspectable by construction. |
Color tokens
zinc-900
Primary text
zinc-500
Secondary text
zinc-400
Labels / meta
zinc-300
Decorative (dots)
zinc-200
Borders (hover)
zinc-100
Borders (rest)
zinc-50
Badge bg / captions
white
Page / card bg
Favicon / App Icon
SVG source — src/app/icon.svg
32×32
64×64
128×128
On dark background
32×32
64×64
Design tokens
AssuranceRailPrototype (case-study-only — Tines)
Bounded, low-fidelity interactive mock in Tines' light visual idiom, framed like a contained product surface. Toggle Autonomy vs Governed autonomy and run the agent. The point it dramatizes: in autonomy the agent certifies its own steps; in governed an independent rail enforces the agent's declared expectation and withholds its verdict on a divergence, then hands off to Workbench. Logic is unit-tested in src/lib/assurance-rail.ts.
- Receive phishing reportWebhookexpects: report has a URL· inferred from downstream use
- Extract domainTransformexpects: domain is resolvable· inferred from downstream use
- Enrich IPipinfoexpects: hosting_provider is non-empty· declared by agent · AI
- Check threat feedURLhausexpects: threat status returned· declared by agent · AI
- Score riskTransformexpects: score derived from full enrichment· inferred from downstream use
- Open ticketTicketingexpects: ticket created with enrichment· builder assertion