A driveway that cleans itself in front of you.
Kerbside Pressure Co. is a fictional Wangara pressure-cleaning and sealing business — a single conversion-focused landing page showing what Sapience Analytics would build for a real operator in this trade. The page has one job: turn "how much to clean my driveway?" into a quote request, using the trade's own best asset — the before/after — as a live, priced demonstration.
Palette — argued from the job at noon
Every colour comes from the mid-job scene: the clean half, the dirty half, the water, and the moss that loses.
Type
Familjen Grotesk carries the display voice — a confident, round-cornered grotesque that reads like well-kept equipment rather than a corporate serif. Public Sans does the body and UI work: plain, legible, government-grade honest. Sometype Mono owns every number — m², rates, prices — so the pricing always reads as measurement, not marketing.
The signature — the Clean Reveal
A procedural SVG driveway drawn in perspective from the kerb. Surface chips (plain concrete / brick pavers / limestone / exposed aggregate / house soft-wash), a 20–200 m² area stepper and a seal toggle drive both the rendering and an honest price card in mono.
Implementation: the driveway is a clipped trapezoid; each surface's texture is generated into it with a seeded PRNG (mulberry32) — broom lines and control joints for concrete, running-bond joint lines for pavers, soft block courses for limestone, ~430 perspective-scaled pebbles for aggregate. A grime layer (umber wash, tyre shadows, moss speckles at the shaded edges, iron bore-stain blooms) sits above the clean texture, clipped by a rectangle whose edge is the wash boundary. A rAF loop sweeps the boundary up the driveway over 40 seconds — foam line, chemistry dwell band, oscillating wand, spray fan, droplets and gutter run-off all track it — then holds fully clean for 2 s and fades the grime back in over the top stretch, reading as the crew moving on to the next dirty run. The clean fraction never drops below 40% at any instant (visible clean = 1 − (1−p)·grimeOpacity), so the before/after contrast survives any capture time; the rest state parks the wipe at 60% clean. Reduced motion renders that settled frame with no loop.
Pressure honesty in the rendering: selecting limestone swaps the narrow jet wedge for a wide, translucent soft-wash fan and says why on-card. Selecting house soft-wash moves the wand to the render facade, sweeps a soft fan across a grime veil there, parks the area stepper and seal toggle with on-page reasons, and prices "from $450".
Pricing model — shape and derivations
| Job | Meter | Pinned minimum |
|---|---|---|
| Plain concrete | $4.50/m² | $180 |
| Exposed aggregate | $4.50/m² (inherits the concrete meter — stated on-card) | $180 |
| Brick pavers (clean + re-sand) | $8/m² | $320 |
| Limestone soft-wash | $6/m² | $240 |
| Penetrating seal | +$7/m² | none — it rides on a job that already carries a minimum |
| House soft-wash | by storeys & reach | from $450 |
- Measured rest state: plain concrete, 60 m², seal off → 60 × $4.50 = $270, exactly the spec's worked example; seal on → $270 + 60 × $7 = $690, also matching. No corrections needed — both figures were read off the running page and verified against an independently reimplemented oracle.
- Minimum clamps: below 40 m² of concrete/aggregate (or the equivalent for pavers/limestone) the card shows the metre arithmetic and a highlighted "minimum call-out applies" line, so the clamp is explained, not hidden. The reveal's cheapest possible output (concrete/aggregate at 20 m² → $180) equals the services grid's "driveways from $180" floor.
- Deliberate flatness: the seal add-on is a flat $7/m² across all sealable surfaces, and aggregate deliberately inherits the concrete meter — both shipped as stated pricing statements, not invented gradients.
- Derived decision: the spec pins the house-wash area-stepper disable; we also parked the seal toggle in house mode (sealing is a ground-surface treatment) with the reason in the same on-page note. Clean fraction is defined as sweep progress along driveway depth — the probe (window.__pressureclean) reports it live, along with surface, area, seal, price, wand mode and form status.
Conversion decisions
- Above the fold at 375px: name, "pressure cleaning that starts at the kerb", the Get-a-quote CTA and the since-2016 / $10M-insured trust line all land inside the first screen; the Clean Reveal follows immediately. At 1200×750 the signature shares the thumbnail viewport with the hero.
- Calls: tel: link in the header, and a fixed two-button call/quote bar on mobile (≥44px targets) — most of this trade's traffic is a phone in one hand on a dirty driveway.
- Signature → form handoff: every interaction with the reveal pre-fills the quote form (surface, area, a notes line carrying the estimate), so the demo interaction becomes a warmer lead. The form itself stays empty on a fresh load so validation is honest.
- Form fields: name, phone, suburb, surface, approximate area, job notes — what a pressure cleaner actually needs to price a visit. Validation is local with visible error copy; the confirmation is a designed local state ("Thanks — request received… we'd call you back within one business day") that plainly says nothing was sent anywhere.
- Honesty as trust strip: water runoff, oil shadows and bore stain each get the truthful version, because in this trade over-promising is the norm and honesty is the differentiator.
Local SEO we'd implement for the real business
This demo ships only a WebPage schema naming itself a demonstration — fictional structured data in a real index would be dishonest markup. For a real client we'd ship LocalBusiness + Review schema like this:
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "LocalBusiness",
"name": "Kerbside Pressure Co.",
"description": "Pressure cleaning & sealing for driveways, pavers,
limestone and house washes across the Perth metro.",
"telephone": "+61-8-9000-0265",
"address": {
"@type": "PostalAddress",
"addressLocality": "Wangara",
"addressRegion": "WA",
"postalCode": "6065",
"addressCountry": "AU"
},
"geo": { "@type": "GeoCoordinates", "latitude": -31.79, "longitude": 115.83 },
"areaServed": ["Wangara","Wanneroo","Joondalup","Landsdale","Perth"],
"openingHours": "Mo-Sa 07:00-17:00",
"priceRange": "$180–$2,000",
"aggregateRating": {
"@type": "AggregateRating", "ratingValue": "4.9", "reviewCount": "187"
},
"review": [{
"@type": "Review",
"author": { "@type": "Person", "name": "…real customer…" },
"reviewRating": { "@type": "Rating", "ratingValue": "5" },
"reviewBody": "…pulled from the live review feed…"
}]
}
</script>
What would change for the real client
- Jobber or ServiceM8 quoting and route runs — the reveal's scenario posts straight into a real quote record, and suburbs map to run days.
- Before/after photo capture on every job feeding a Podium review funnel — this trade's photos ARE its marketing; the procedural reveal gets replaced (or joined) by the operator's own corners.
- Square tap-to-pay on-site, Xero for invoicing and the quarterly strata schedules synced to the strata manager's maintenance portal.
- Real licence, insurance certificate and ABN replacing the placeholder-patterned demo strings; real reviews replacing the demo copy.
Iteration log
Pass 1 — Craft
Drove the whole page headless (92 asserts): fresh-load empty submit raised all four validation errors; eight oracle scenarios (including all three minimum clamps and both seal cases) matched the displayed card to the dollar; the house-wash disable path, prefill handoff, filled-submit confirmation, FAQ accordion, sticky bar and reduced-motion settled frame all passed. Then the real screenshots caught what the asserts couldn't: a stray empty <span> accidentally nested in an FAQ summary; the mobile hero's call button wrapping mid-phone-number at 375px (fixed with nowrap + row wrapping); and the Scarborough map label floating detached from its dot (re-anchored below-left). Confirmed the 375px masthead first — wordmark and phone number fit on one row with clearance.
Pass 2 — Depth
The spray fan measured only ~20px tall in the capture — barely a mark at the boundary — so the wedge was extended past the wash edge and brightened, with a chemistry-dwell band (dark translucent stroke) ahead of the foam as a second-read detail. Gutter run-off became an animated dashed flow that only runs while the wand runs. Added a 180ms eased count-up on the price total (tabular numerals; the state probe always carries the final figure instantly, and reduced motion skips the tween). Screenshot-reviewed every mode: limestone's wide soft fan + on-card reason, house mode's facade veil sweep with parked controls, pavers' running bond under the $480 + $420 = $900 card, aggregate's pebble field at $270 + $420 = $690.
Pass 3 — Hardening
Re-ran the full 92-assert suite green after all edits, with zero console errors end-to-end. Verified: no horizontal overflow at 375px; h1 + CTA (≥44px) + trust signal above the fold; sticky call bar fixed in the bottom band with ≥44px targets; the signature's bounding box intersecting the first 750px at 1200 wide; clean fraction sampled across the loop never below 0.40 and sitting at ~0.7 at the 7-second thumbnail settle (contrast visible both sides of the wash edge); reduced-motion serving a static 60%-clean frame with the $270 card. Honesty sweeps: every (08) 9xxx number on the 9000 exchange, WebPage-only JSON-LD, colophon present. Chanel rule: deleted the dimension line's arrowheads and stem — the m² pill says it alone.
Techniques
- Single-file vanilla build: no libraries, no rasters — every visual is inline SVG, seeded procedural texture or CSS gradient.
- Perspective projection helpers (linear edge interpolation + a foreshortening curve for row spacing) shared by all four surface generators.
- Clip-rectangle reveal driven by a delta-clamped rAF loop that pauses on document.hidden and never starts under prefers-reduced-motion.
- State probe window.__pressureclean updated on every interaction and every frame, so QA gates on state, not wall-clock.