Generative Assets · Wave 17 · Industry Demos VI
A conversion-focused landing page for a fictional Tuart Hill roof-restoration outfit — built to show Perth roofers exactly what Sapience Analytics would build for them. Back to the site or the collection.
Ridgecap Roofing Co. is a fictional restoration-first roofer for owners of 1960s–90s tile-roofed homes across Perth's north — the family watching a ceiling stain spread, the seller pre-sale, the owner quoted a $24k reroof who suspects a $7k restoration would do. The page has one job: book the free 20-point inspection. The art direction is a storm-season roofline — the terracotta roofscape of the northern suburbs against a building June front — respectful of weather, contemptuous of nothing. The single signature is the Roof Health Estimator: an honest ballpark before anyone climbs a ladder.
The spec's bright terracotta and rain slate are line/mark tokens only — both fail 4.5:1 as small text on storm-light, so the build derives an ink twin for each (the W2/W5 ink/line split). Active chips sit on a deepened slate #3E617A so white chip text clears 6.5:1 on the dark estimator band.
Bricolage Grotesque (display) — a sturdy grotesque with enough character to be a masthead on a van door; set tight (-.02em) at 800 for the h1 and section heads. Public Sans (body/UI) — plain-spoken and legible at 16px+ on a phone in one hand, which is where trade traffic lives. IBM Plex Mono — every per-metre rate, roof area and dollar figure renders in mono, so the numbers read like a measured quote rather than marketing.
A procedural SVG house elevation under a storm front: layered gradient sky, drifting cloud groups, dashed rain off the leading edge. Three <pattern> defs paint the roof plane per material — scalloped terracotta courses, flat concrete tiles with course lines, ribbed Colorbond — and the ridge-cap strip recolours to match. Changing roof type redraws the plane through a clipPath rect whose CSS width transitions (SVG geometry properties animate in CSS; the rect has no layout box, so QA probes read getComputedStyle, not getBoundingClientRect). The area slider rescales the whole house via the transform attribute anchored at the slab line — attribute for layout, CSS animation only on separate inner nodes (the W16 rule), which is why the pulsing defect markers animate a child circle inside an attribute-positioned group. Each job chip lights its own marker set: weathered surface, cracked pointing and lichen for restoration; slipped tile and water entry for repairs; ridge faults; rusted gutter runs. Everything the estimator knows is mirrored to window.__roofer on every interaction, and choosing a scenario pre-fills the inspection form — the demo interaction becomes a warmer lead. A second-read detail: once every 14 seconds, distant lightning flickers off the front (phased so the 7-second thumbnail lands in its dark half; killed under reduced motion).
The spec pins the rates; the build derives the geometry. This table is the single source of truth the page, the FAQ and the QA oracle are all checked against.
| Item | Model | At 190 m² (rest state) |
|---|---|---|
| Full restoration | area × rate — $35/m² concrete (clean, rebed where needed, repoint, recoat), $30/m² terracotta (no coating — the colour is fired in), $28/m² Colorbond (clean, screws, seal, repaint) | 190 × $35 = $6,650 |
| Leak & tile repairs | Deliberately flat at from $330 — callout + first hour + photo report is area-independent, so the estimator ships the flatness as a pricing statement, not an invented gradient | from $330 |
| Ridge repoint & rebed | ridge/hip length ≈ round(2.9 × √area) lm — linear dimensions scale with √area; lands 32–54 lm across the 120–350 m² slider, inside the 30–80 lm band typical of Perth hip roofs. Repoint $12/lm, rebed & repoint $28/lm, shown as an honest range | 40 lm → $480–$1,120 |
| Ridge on Colorbond | Special-cased honestly: Colorbond ridges are screw-fixed, not bedded — the card re-routes to a re-screw & seal visit from $330 instead of quoting pointing that doesn't exist | from $330 |
| Gutters & downpipes | gutter run ≈ round(4.6 × √area) lm (eave perimeter, same √ scaling), × $45/lm supplied + fitted | 63 lm → $2,835 |
The spec's worked example (190 m² concrete restoration ≈ $6,650) was a hypothesis to verify; the implemented model computes exactly $6,650, so it ships unchanged. The estimator's cheapest possible output is the flat $330 repairs figure, which equals the services grid's "from $330" floor — the page never argues with itself. The review quoting a $7,000 restoration is a 200 m² concrete roof at $35/m².
aria-invalid; a valid submit renders a local confirmation ("Thanks — request received… we'd call you back within one business day") that plainly says nothing was sent or stored. No fake network round-trip.The demo page carries only WebPage schema naming it a demonstration — fictional structured data in a real index is dishonest markup. For a real Ridgecap, we'd ship LocalBusiness + Review schema wired to their actual details and review feed:
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "RoofingContractor",
"name": "Ridgecap Roofing Co.",
"telephone": "+61 8 XXXX XXXX",
"address": { "@type": "PostalAddress",
"addressLocality": "Tuart Hill", "addressRegion": "WA",
"postalCode": "6060", "addressCountry": "AU" },
"areaServed": ["Tuart Hill","Yokine","Dianella","Balcatta",
"Stirling","Osborne Park","Karrinyup","Morley"],
"openingHours": ["Mo-Fr 07:00-17:00","Sa 08:00-12:00"],
"priceRange": "$$",
"aggregateRating": { "@type": "AggregateRating",
"ratingValue": "4.9", "reviewCount": "187" },
"review": [{ "@type": "Review",
"author": { "@type": "Person", "name": "…" },
"reviewRating": { "@type": "Rating", "ratingValue": "5" },
"reviewBody": "…" }]
}
</script>
Plus: suburb-level service pages, Google Business Profile with the photo reports as posts, and review capture in the job-completion flow.
concrete/restoration/190 → ≈ $6,650, terracotta 250 m² → $7,500, ridge 190 m² → $480–$1,120, gutters 350 m² → $3,870, and the Colorbond-ridge special case → from $330. Empty submit on a fresh load raised all three validation errors; filled submit rendered the confirmation lexicon.clipPath rect has no layout box, so "roof drawn" probes moved from getBoundingClientRect to getComputedStyle; fixed the probe, not the page.