Setline Paving

Generative Assets · Industry demo guide

How Setline Paving was built

Setline Paving is a fictional Wangara brick paving operator — driveways, alfresco and pool surrounds for Perth's northern suburbs. The page has one job: turn "the builder left me a sand pad" into a quote request. Everything on it — the noon-worksite palette, the string-line motif, the Course Planner that lays itself — exists to make a real paving prospect think that could be my site.

Palette — a Wangara worksite at noon

Argued from the site, not from taste: screeded yellow builder's sand is Perth's actual base, the cut edge is bitumen-dark, the paver face is kiln oxide, and the chalked string line is blue. Deliberately not the banned cream+terracotta default: yellow sand, not cream; kiln oxide as a line/mark token, not body serif terracotta; a grotesque, not a serif. Per the ink/line split, the two bright accents each carry a derived darker ink for text duty.

#E7DDC4 compacted sandground — screeded yellow sand, ruled flat
#332F28 bitumenstructure & primary ink — the cut edge
#A03A26 fired clayLINE/MARK — course strokes, dimension rules, CTA; never body text on sand
#7E2D1C clay-inkderived text ink for clay-toned small type (6.8:1 on sand)
#3E6FA6 stringline bluethe taut chalked string — focus rings, active chips, the frontier line
#2E5680 stringline-inkderived link/text blue (5.6:1 on sand)

Type

Archivo 800 for display — a worksite grotesque with flat shoulders, set in caps for the masthead moment ("LAID TO THE LINE" carries a literal string-line underline strung between two pin marks). Public Sans does the body and UI work: professional and legible beats expressive on a trade page, body ≥16px on mobile. Red Hat Mono owns every number — m², rates, totals, the price meta line — so the arithmetic always reads as arithmetic.

The Course Planner — signature & how it works

The signature is a procedural SVG plan view that lays itself the way the job does. Every brick is generated, not drawn: pavers are true 230 × 115 format at the plan's scale, so the paver count on the card (38/m²) is the same arithmetic a real estimator runs.

Genuinely redrawn patterns

Stretcher is rows of half-offset running bond; basketweave alternates paired horizontal/vertical 2×1 blocks; herringbone is a true 90° domino tiling — cell rule s = (gx+gy) mod 4, a horizontal brick anchored at s=0 and a vertical at s=2 — rotated 45° about the pad centre and clipped to the pad. The patterns are different geometry, not retints: a full-path census at 60 m² counts stretcher at 2,543 horizontal bricks and 0 vertical, basketweave at 1,243 / 1,254.

Course-by-course reveal, two speeds

Bricks batch into 28 course groups by laying-direction progress, then into one <path> per group per shade (≤ ~140 nodes for ~4,500 bricks at 120 m²). The first render earns the entrance: 53 ms per course, capped at 1.5 s. Every subsequent chip or stepper tap redraws snappy — 11 ms per course, settling well under the ~600 ms cap — via a firstDraw flag, so the planner reads as response, not replay. A window.__paving probe carries the live state and a settled flag so QA gates on state, never wall-clock.

The rest state is mid-make

At rest the job holds two-thirds laid (19 of 28 courses down), the string line strung taut in blue at the frontier with a pin at each end, and the next brick outlined in clay, pulsing with a 0.55 opacity floor — never invisible at any capture instant. Reduced motion renders the job fully laid and settled, no frontier, no pulse.

Area type redraws the plan, honestly not the price

Driveway gains the crossover splay and a vehicle outline; alfresco draws the house wall, door threshold and slab edge; pool surround wraps the courses around a blue coping line (an even-odd clip cuts the water out of the field); paths narrow to 1.2 m with edge restraints, planting marks, and honest break marks when the run is longer than the drawing ("16.0 m drawn of a 50.0 m run"). The card says why the price doesn't move: the meter is metres and material.

Pricing model — shape, anchors, measurements

MeterAnchorNotes
Concrete pavers$85 /m²supply & lay on a prepared sand base — the fixed dimension, stated on-card
Clay pavers$110 /m²
Limestone pavers$135 /m²
Granite pavers$175 /m²
Stretcher bond+$0 /m²included in the rate
Basketweave+$6 /m²paired blocks, fewer cuts than herringbone
Herringbone 45°+$10 /m²every edge is a cut
Job minimum$1,900 flata DOLLAR clamp, one semantic, regardless of material
Pool copingfrom $95 /lmoutside the planner — per-metre on top, measured on site

The model is area × (rate + pattern), clamped to max(metered, $1,900). When the clamp binds, the card shows the metered arithmetic and a visible "minimum applies" badge — the page never hides that you're buying the crew's morning, not just the metres. The clamp is a dollar clamp: sub-minimum granite and sub-minimum concrete both read $1,900.

Measured from the running code (all verified against an independently reimplemented oracle across all 36 scenario combinations, plus the spec's pinned readings — zero corrections needed):

Deliberate flatness, stated rather than invented: area-type chips honestly no-op on price while genuinely re-rendering the scene — a control may stay live without moving the price only if it says so visibly and still changes the drawing, which is exactly what the on-card note does. The drawing's pad proportions (driveway width clamped 3.2–6 m, alfresco 4:3, an 8 × 4 m pool, 1.2 m paths) shape the picture only — the meter never reads them. Pool coping stays outside the m² model entirely.

Conversion decisions

Local SEO we'd implement for the real business

This demo page ships only WebPage schema naming it a demonstration — fictional structured data in a real index is dishonest markup. For a real Setline, we'd ship the tightest schema.org type with live review data, like this:

{
  "@context": "https://schema.org",
  "@type": "HomeAndConstructionBusiness",
  "name": "Setline Paving",
  "description": "Brick paving, driveways, alfresco and pool surrounds
                  across Perth's northern suburbs.",
  "telephone": "+61-8-XXXX-XXXX",
  "url": "https://setlinepaving.com.au",
  "address": {
    "@type": "PostalAddress",
    "addressLocality": "Wangara",
    "addressRegion": "WA",
    "postalCode": "6065",
    "addressCountry": "AU"
  },
  "areaServed": ["Wangara","Landsdale","Madeley","Darch","Wanneroo",
                 "Joondalup","Kingsley","Hillarys","Ballajura","Ellenbrook"],
  "openingHoursSpecification": [{
    "@type": "OpeningHoursSpecification",
    "dayOfWeek": ["Monday","Tuesday","Wednesday","Thursday","Friday"],
    "opens": "06:30", "closes": "16:00"
  }],
  "priceRange": "$$",
  "aggregateRating": {
    "@type": "AggregateRating",
    "ratingValue": "4.9",
    "reviewCount": "127"
  },
  "review": [{
    "@type": "Review",
    "author": { "@type": "Person", "name": "— real customer —" },
    "reviewRating": { "@type": "Rating", "ratingValue": "5" },
    "reviewBody": "— pulled live from the Google Business Profile —"
  }]
}

Plus: suburb-level service pages fed from the areaServed list, a Google Business Profile review pipeline, and FAQ schema on the six questions the trade actually gets asked.

What changes for the real client

Their photos replace the procedural scenes (though the planner stays — it's the differentiator), their Google reviews feed the review strip, and the quote form posts to their actual workflow. The integrations we'd wire for a paving operator: Buildxact or Groundplan-style takeoff & estimating from site plans, ServiceM8 job cards with photo records of the base prep (the compactor-passes promise, made auditable), supplier pallet ordering through Midland Brick-style trade portals, the council crossover application workflow, Square progress payments, Xero for the books, and a Podium-style review flow at handover.

Iteration log — three passes, driven not skimmed

Every pass drove the real controls headlessly (chips, stepper, form, FAQ, sticky bar) with asserted outputs, and included at least one real screenshot review. Final harness: 54 assertions green, zero console errors.

Pass 1 · Craft

  • Drove all 36 paver × pattern × area scenarios against an independently reimplemented pricing oracle — every displayed figure matched, including the three pinned clamp-boundary readings ($1,275→$1,900, $1,700→$1,900, $2,125 release).
  • Screenshot review at 1200×750 caught three real defects no assert saw: the "string line" caption sitting on the brickwork with no contrast (added a sand paint-order halo), the driveway's crossover + area captions colliding (restacked as one centred caption block), and map label collisions — Duncraig/Greenwood/Madeley overlapping and the ~15 min ring label on Wanneroo (all repositioned).
  • One harness false-fail, fixed in the harness not the site: comparing only the first 120 chars of the first course path called stretcher and basketweave "identical"; a full-path orientation census proved them different geometry (2,543/0 vs 1,243/1,254 horizontal/vertical bricks).

Pass 2 · Depth

  • Added the price tick — the total dips and rises 5px over 300ms when the figure changes (suppressed under reduced motion), so the arithmetic visibly answers the control.
  • Second-read details: the next-brick outline pulsing at the frontier (0.55 opacity floor — never invisible at any thumbnail instant), the paver-count line doing real 38/m² arithmetic, and break marks with "16.0 m drawn of a 50.0 m run" honesty on long paths.
  • Measured the dual-speed redraw wall-clock (median of 5 driven redraws): the visual re-lay completes at ~460ms (28 courses × 11ms + 250ms transition) and the state-probe's settled flag lands at 584ms with its 120ms slack included — inside the ~600ms cap; the first render keeps the full ~1.4s course-by-course entrance.
  • Drove the pattern chips by keyboard (tab + Enter) as well as dispatched clicks — state and scene respond identically.

Pass 3 · Hardening

  • 375px masthead first: measured wordmark right edge vs call-chip left edge (clear, >6px gap) and looked at the screenshot; under 400px the phone number collapses to a "Call" chip so nothing wraps.
  • Fresh-load empty submit raises 4 aria-invalid fields with visible error copy and no confirmation; filled submit confirms in the contract lexicon naming the suburb. Sticky bar's tel: link sits inside the bottom 140px at 375; FAQ is native <details> (keyboard free); scrollWidth ≤375 on both pages.
  • Reduced-motion path renders the job fully laid (28/28 courses), settled flag set, no string line, no pulse, no entrance.
  • Chanel rule — removed one ornament: the second slab-edge hairline on the alfresco scene. Zero console errors across every drive, including the v-flag-safe form (no pattern attributes; validation is JS + required).