Generative Assets · Industry demo 246 · Painting · Perth
A fictional Mount Hawthorn painting contractor, built as a single conversion-focused landing page — every visual procedural, every price argued from the WA market, one signature interaction doing the selling.
Cutline Painting Co. paints interiors and exteriors across Mount Hawthorn and Perth's inner north — character homes, rentals between tenants, pre-sale refreshes. The brand's whole personality is the trade's own point of pride: a cut line you can't fault from a foot away. The page has one job: book a free colour-and-quote visit. Everything on it — the estimator, the prep honesty, the two-coat line — exists to make a visitor with a scrappy card-quote in their pocket think "these are the careful ones", and then call.
Fraunces (display) — a serif with brush-drawn warmth, set tight; it carries the craftsman-calm register without tipping into heritage pastiche. The one moment of typographic bravery is the masthead's cut clean. — the word is half-filled gum green, half scaffold ink, split by a 3px masking-gold vertical bar: a wall mid-coat, the cut line dead straight through the letterforms. (The first attempt ran the tape line horizontally and read as strikethrough — caught in Pass 1's screenshot review and rotated vertical.)
Inter (body, UI) does the legibility work at 16px+. Space Mono carries everything numeric — prices, the estimate card, suburb chips, coat counts — so figures read like a written quote, not marketing.
Scope chips (feature wall / single room / whole 3-bed interior / exterior single-storey), a finish toggle (walls only / walls + ceilings + trims) and three curated scheme chips drive a procedural SVG room elevation — cornice, skirting, doorframe, window, a taped feature wall, drop sheets and a ladder, all vector. Choosing a scheme repaints the room live: fills are set via element.style.fill from JS with a CSS transition on fill (never a CSS rule fighting an SVG presentation attribute — position and paint state live on separate elements per the collection's W16/W17 rule). The exterior scope swaps to a second SVG group (a rendered single-storey elevation) toggled with setAttribute('hidden','') under a [hidden]{display:none !important} guard. A second-read detail: three painter's swatch cards taped to the wall, with a dashed pencil ring that moves to the active scheme.
The estimate card renders in Space Mono like a written quote — scope, finish, scheme, itemised lines, a "from" total — and a handoff button pre-fills the enquiry form with the configured scenario and price, so the demo interaction becomes a warmer lead. At rest the studio sits mid-demonstration: whole 3-bed interior, full finish, "Limewash & Gum" applied, the room painted and $6,900 showing — a premium static frame at the 7-second thumbnail settle (no looping animation to phase; colour transitions complete in under a second).
Colour never changes the price in the model — stated plainly on the card, with the honest caveat that very deep bases sometimes want a third coat, called at the visit rather than after.
The spec pins five anchors: feature walls from $290, single rooms from $440 walls-only, $690 with ceilings and trims, whole 3-bed interiors from $4,900 walls-only, exteriors from $7,900. Everything else is derived and lives in one place in the page's JS (and is exposed at window.__painter.model for QA).
The $440 room anchor is a standard bedroom. A whole home is that bedroom re-weighted across every painted space — bigger living areas, a door-heavy hallway, small wet areas — summing exactly to the pinned $4,900 floor:
| Space | Walls-only | Ceiling-bearing? |
|---|---|---|
| Bedroom 2 | $440 | yes |
| Bedroom 3 | $440 | yes |
| Master suite | $560 | yes |
| Living & family | $900 | yes |
| Dining & meals | $520 | yes |
| Kitchen | $440 | yes |
| Hallway, entry & passage | $900 | yes |
| Laundry & WC | $460 | yes |
| Robes & linen ×3 | $240 | no |
| Whole 3-bed, walls only | $4,900 | 8 spaces |
The room anchors fix the bundled uplift: $690 − $440 = $250 per space when ceilings and trims are booked with the walls. That's cheaper than the standalone $340/room ceiling anchor on purpose — the room is already sheeted, cut in and under protection, so the set-up cost is paid once. The uplift is flat per space as a pricing statement, not an invented gradient: bigger ceilings pair with smaller trim runs and vice versa, and it averages true across a home. Robes and linen take no uplift (no ceiling work booked in them).
Whole 3-bed interior, walls + ceilings + trims = $4,900 + 8 × $250 = $6,900. The spec's ≈$6,900 hypothesis was verified by measurement: the QA harness re-derives this model in independent oracle code and asserts the displayed figure at rest and across all eight scope × finish combinations. The estimator's cheapest output ($290 feature wall) equals the services grid floor, and the whole-home walls-only output equals the grid's $4,900 — the page never argues with itself.
aria-invalid; the confirmation renders locally and instantly ("Thanks — request received. We'd call you back within one business day…") with a plain line saying nothing was sent — never a faked network round-trip.This demo page ships only WebPage schema naming it a demonstration — fictional structured data in a real index would be dishonest markup. For a real painting contractor we'd ship LocalBusiness + Review schema like this (shown here as a code sample, not emitted):
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "HousePainter",
"name": "Cutline Painting Co.",
"telephone": "+61 8 XXXX XXXX",
"url": "https://cutlinepainting.com.au",
"address": {
"@type": "PostalAddress",
"addressLocality": "Mount Hawthorn",
"addressRegion": "WA",
"postalCode": "6016",
"addressCountry": "AU"
},
"areaServed": ["Mount Hawthorn", "Leederville", "North Perth",
"Mount Lawley", "Joondanna", "Yokine", "Wembley"],
"openingHours": "Mo-Fr 07:00-16:30",
"priceRange": "$$",
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "4.9",
"reviewCount": "87"
},
"review": [{
"@type": "Review",
"author": { "@type": "Person", "name": "Real customer" },
"reviewRating": { "@type": "Rating", "ratingValue": "5" },
"reviewBody": "Fed from the live review platform, never hand-typed."
}]
}
</script>
Plus: Google Business Profile with service-area suburbs, per-suburb service pages only where genuine work history exists, and review markup fed from the live review platform so ratings stay true.
window.__painter state probe. No libraries; the page is a single request plus fonts.prefers-reduced-motion, which renders the settled, fully-painted rest state.Pass 1 · Craft
.head-actions 31px off-edge. Fixed by hiding the header button under 900px (the sticky bar owns the quote CTA on phones).white-space:nowrap plus a smaller wordmark under 420px.aria-pressed state.Pass 2 · Depth
setAttribute('hidden'), never the SVG-less .hidden property). Asserted the ring follows a driven scheme change.align-items:flex-start on the chip rows and align-content:start on fieldsets. The rendered image is ground truth.Pass 3 · Hardening
(08) 9xxx number on the page uses the reserved 9000 exchange (only 0246 appears); zero LocalBusiness/Review/AggregateRating types in live JSON-LD — only the WebPage demonstration schema.