Booked online at 11pm and they were out the next morning. Pool went from green to crystal clear. Will use again.
The operating system for booked revenue.
Foundational components for the AI Revenue Engine — the managed booking, AI lead-response, and SEO platform for local service businesses. Every screen composes from the four foundations below.
A serif that earns trust, a grotesque that does the work, a mono for the data.
Display moments lean editorial and human so the AI never feels cold. UI is clean and legible. Appointment IDs, metrics, and timestamps render in mono.
Font families
--font-display · 400/500/600/700 + italic · headings & hero
--font-ui · 400/500/600/700 · body, labels, buttons
--font-mono · 400/500/600 · IDs, metrics, timestamps
Type scale
One obvious action per screen. Green means money.
Primary is the conversion button. Secondary supports it. Urgent is reserved for time-sensitive recovery like reclaiming a missed lead.
Variants
States & sizes
Hover lifts 2px · focus shows green ring · loading shows spinner · targets ≥44px tall.
Social sign-in · .btn--social
A neutral/bordered full-width button for SSO, distinct from the primary CTA (white is the Google convention). Provider-agnostic — each provider only supplies its own inline brand SVG (Lucide has no brand glyphs, so it's never used here). Used on Login and Accept Invite. Mockup of the SSO entry point — no real OAuth.
The .or-divider (rule + centered "or") separates SSO from the email form. .auth-legal carries the "By continuing you agree to Terms & Privacy" microcopy.
Grouped, split, clustered — and what they collapse to on a phone.
Extends Buttons: button groups, split buttons, action rows (which collapse to icon-only 44px circles on mobile), pills/badges/tags, and standalone icon buttons. Every icon button carries an accessible name.
Button groups · segmented (single-select) · multi-action · split
Action row · desktop (labeled) vs mobile (icon-only 44px)
Same markup. Resize to ≤760px (or view at 390px) and the labels drop — each action becomes a 44px circular icon button, title/aria-label kept. This matches the Tables row-action rule.
Pills · badges · tags
Icon buttons · sizes · variants · states
Composable layout blocks — the bones of every page.
Pages are assembled from a small set of section containers: a hero, a feature grid, and a conversion banner.
Hero block · .hero
Stop losing jobs to voicemail.
Your AI answers every call and text, checks your real calendar, and books the appointment — even at 11pm on a Saturday.
Feature grid · .feature-grid / .feature
24/7 Booking
Real-time availability, exact-date checks, and instant confirmation with an appointment ID.
AI Lead Response
Catches calls, texts, web forms, and DMs — qualifies the lead and gathers job details.
SEO & Visibility
Climb from page 2 into the local pack where your great reviews can finally convert.
Conversion banner · .banner
Inputs that feel safe to type into.
Owners enter credentials, phone numbers, and job details on a phone, often outdoors. Fields are large, high-contrast, and clear about errors. Every icon is a real Lucide glyph.
Text inputs · states
Password · show / hide toggle
eye ↔ eye-off.
Dropdowns / selects · states
Open state (custom menu)
Native selects render the OS menu when open, so here is the styled custom-menu pattern for richer dropdowns, with a Lucide check on the selected row:
Rows are padded pills inset from the menu border — the hover/selected highlight never touches the edge. Same treatment applies to the typeahead and split-button menus.
Dropdown with icons (actions menu)
A .menu--icons variant: each row has a leading Lucide icon that aligns with the label. Shows all states — default, hover, focused, selected, disabled, and a destructive (danger) row in red. Reuses the same .menu structure and the new inset hover padding.
Icons via Lucide: pencil, copy, bell, archive, lock, trash-2. The Delete row uses .is-danger (red text + red hover).
Label · helper · error pattern
Each field is: label (with optional inline action) → control → helper in resting state, replaced by error text + red border + aria-invalid on failure.
Find the appointment, lead, or customer fast.
A search input built on the same .input sizing, with a leading Lucide search icon and a clear (x) button that appears once there's text. Designed to drop into a table toolbar to filter lists.
States
Typeahead / suggestions (optional variant)
Suggestions reuse the .menu dropdown from Form Controls; each row carries an entity icon + type label.
No results (empty-state pattern)
Pairs with the Tables empty state — same structure, search-specific copy and a clear-search action.
No matches for “xyzzy plumbing”
Try a different name, phone number, or appointment ID.
Composed into a table toolbar (filter a list)
How search sits above a Tables list to filter appointments / leads / customers.
| Customer | Service | Status |
|---|---|---|
| Maria Gomez | HVAC tune-up | Booked |
| Dev Patel | Drain cleaning | Pending |
Booking lives on a calendar. So does the product.
A month grid, a compact date-picker tied to a date input, and an agenda/day view. Dots and counts show where appointments sit; past days are disabled; today and selection are obvious.
Month view · populated (dots + count) & selection
Compact date-picker (drops from a date input)
Agenda / day view · populated & empty
Open all day.
Outcomes, not features. Green is good; red is lost.
KPI cards for the owner dashboard, tied to the product's real outcomes. Trend up in green when it's good; a rising Missed Leads trends red because that's lost revenue.
Default · trend up / down (real outcomes)
With sparkline · loading skeleton · empty / no data
Lists of the things that make money: appointments, leads, customers.
Sortable headers, status badges in the semantic colors, and row actions. Responsive choice: at ~390px each row collapses into a labelled card (not horizontal scroll) — easier to scan and tap for booking data. Action buttons become 44px icon-only targets on mobile.
Populated · sortable · status badges · row actions
| Customer | Service | Date & time | Status | Actions |
|---|---|---|---|---|
Maria Gomez +1 (818) 555-0142 |
HVAC tune-up | Jun 19 · 9:00 AM | Booked | |
Dev Patel +1 (818) 555-0199 |
Drain cleaning | Jun 20 · 10:30 AM | Pending | |
The Reyes family +1 (818) 555-0177 |
Pool service | Jun 21 · 2:00 PM | No-show |
Loading (skeleton rows)
| Customer | Service | Date & time | Status | Actions |
|---|---|---|---|---|
Empty state
No appointments yet
When the AI books a job, it'll show up here. Share your booking link to get the first one.
Toasts & modals — one set of semantics across the app.
Transient toasts and blocking modals, with shared semantic colors/icons: success = green / check-circle, warning = amber / alert-triangle, danger = red / alert-circle or trash-2, info = neutral-blue / info. These back the inline confirmations and destructive actions used elsewhere (Profile save, danger-zone delete, cancel subscription, remove teammate).
Toasts · variants (click to trigger a real one)
Static examples (one of each), with leading icon, message, optional action link, and dismiss:
Placement: fixed top-right on desktop, top-center on phones (.toast-region). They never block interaction. Auto-dismiss after ~4s unless persist. Trigger via window.sgToast({intent,title,msg,action}). Stacked group = the four above rendered together.
Modal · base dialog (reusable shell)
Modal title
Overlay/scrim, centered card, header with title + close, body, and a footer action row. Dismiss on overlay click or close button.
Body content goes here — any composition of foundations.
Modal · warning (risky but not destructive)
Move this appointment?
Rescheduling will text the customer the new time and free up the original slot.
Modal · success (confirming)
Invite sent
taylor@example.com will get an email to join Jordan's Plumbing as a Member.
Confirmation · neutral (Confirm / Cancel)
Mark as no-show?
This frees the slot and flags the customer. You can undo it later.
Confirmation · destructive (default + type-to-confirm + loading)
Remove Alex Reyes?
They'll lose access immediately. Their assigned appointments stay, but become unassigned.
Delete account?
This permanently deletes your account, bookings, and customer data. This can't be undone.
Cancel subscription?
Your AI booking and responder turn off at the end of this cycle (Jul 19).
Removing…
Processing state — the destructive action runs.
Destructive confirms use .btn--danger as the primary; the most dangerous (Delete account) gates the button behind a type-to-confirm field. These are what the Profile danger zone, Subscription cancel, and Team remove-member actions point to.
Proof that sells: completed work + reviews.
The social-proof foundation — before/after work, star ratings, and reviews. These power the public Business Showcase (the SEO/brand landing page), the owner-side Work Gallery and Reviews settings, and the review-request email. Images here are inline SVG placeholders so the components render offline.
Before / after slider — paired + single-image
Drag the handle (or use ← → keys). 44px knob, full-height hit area. Touch-friendly.
.ba-slider--single — single "after-only" photo (a haircut/nail set is one great photo, not a transform). Handle + before layer hidden.
Work grid (4→2→1) + lightbox
Lightbox (opened tile — shown inline)
Tiles show the finished image, a service tag, and a before/after vs single-photo marker. Click opens the lightbox (reuses the modal scrim) with the slider + caption + prev/next.
Star rating (fractional) + rating summary
Stars use a clipped green/amber overlay (--pct) so half-stars are exact; role="img" + aria-label speaks the value (icons are decorative).
Review card + source badges
Great communication and fair pricing. Showed up on time and explained everything.
Each card carries its source badge + required attribution. Reality: Google Places & Yelp Fusion are display-only (a few recent reviews, caps ~3–5, attribution required) — we cannot post or auto-leave reviews.
Connect-source card — connect / connected / error
Mirrors the onboarding calendar-connect pattern. Mock OAuth — connect → connected (shows summary) / error (retry). Google-first.