Concept
Ironbark Building Co. is a fictional custom-home and extension builder based in Midland, serving Perth from the Darling Scarp to the coast. The audience is families who fear the two classic building horrors — budget blowouts and vanishing builders — so the page's single job is to convert that anxiety into a booked site consult by being radically transparent about money and process. The art direction is "a well-run site": render white, charcoal steel, jarrah heart; everything level, everything labelled.
Palette
The banned AI-default palettes don't apply here by accident: every colour is a building material a Perth site actually uses, and the one warm accent is load-bearing — it marks money (claims, CTAs) and nothing else.
Type
Archivo, one family at two widths — the typographic move of the site. Display runs at font-stretch:125% (Expanded, 700–800), set wide like site-hoarding signage; body runs at normal width. One variable-font file, two voices: the wide cut carries authority, the normal cut carries the paperwork. Eyebrows sit at 113% width with 0.22em tracking, echoing stencilled stage labels on a drawing.
Techniques & the signature
Build Stages timeline. The house elevation is a single inline SVG drawn in five layer groups — slab, plate-height frame, roof cover, lockup, practical completion — on a drafting-grid panel (two repeating-linear-gradients). A range input scrubs the stage and five buttons jump to it; both drive one setStage() that toggles layer visibility (CSS opacity/translate transitions), rewrites the drawing's title block ("REV D · LOCKUP"), and fills the progress-payment ledger beside it. The ledger is generated from one data array — deposit 6.5% plus five staged claims of 13.5 / 20 / 20 / 25 / 15 — and the script throws at load if the percentages don't sum to exactly 100, so the page cannot ship with a dishonest schedule. Dollar figures are computed from the same array against a $780,000 worked example, so ledger rows, the claimed-to-date total and the copy can never drift apart. At rest the page parks at lockup with 85% claimed — mid-demonstration for the static thumbnail.
Everything else is procedural SVG in the same drafting language: the Perth service-area map (coastline, Swan River, Darling Scarp hatching, dashed jarrah service boundary, thirteen named suburbs plus the Midland HQ pin) and the service icons. No canvas, no WebGL, no libraries — the whole page is one HTML file that renders fast on a 4G phone.
A state probe window.__builder exposes stage, ledger figures and form status so QA gates on state, not wall-clock time. Second-read details: the title block's revision letter advances A→E with the stages, the deposit row notes it's claimed "at contract signing", and the claimed total turns green only at 100% — keys handed over.
Conversion decisions
- Above the fold at 375px: name, "custom homes and extensions, built level and priced straight", the Book-a-site-consult CTA and the BP-registration trust line all land inside the first screen; the hero was compacted in Pass 1 specifically to protect this.
- Call paths:
tel:link in the header at every width, plus a mobile sticky bar (call-first, consult second, 56px targets). Building enquiries are considered purchases, so "Book a consult" outranks "Call now" on desktop but the phone stays one tap away on mobile. - The signature sells honesty, not decoration: the stages timeline answers the prospect's actual fear — "where does my money go and when" — and the FAQ repeats the schedule in words, with the WA 6.5% deposit cap named.
- Form fields: name, phone, suburb, project type, description — exactly what a builder needs to send the right estimator, and nothing more. Validation is inline per field; the confirmation is local and openly a demo ("nothing was actually sent — your details never left this browser"), never a fake network round-trip.
- Honest anchors: $2,400–$3,400/m² custom builds, extensions from $180,000, second storeys from $320,000, granny flats from $165,000 — 2026 Perth ranges, deliberately not teaser rates, and the FAQ says what the $/m² excludes. Timeline honesty (14–20 months) is stated where competitors quote fantasy.
Local SEO we'd implement for the real business
This demo ships only WebPage schema naming it a demonstration — fictional LocalBusiness or Review markup in a real index would be dishonest. For a real builder we would ship:
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "HomeAndConstructionBusiness",
"name": "<Client trading name>",
"url": "https://<client-domain>.com.au",
"telephone": "+61 8 XXXX XXXX",
"address": {
"@type": "PostalAddress",
"streetAddress": "<street address>",
"addressLocality": "Midland",
"addressRegion": "WA",
"postalCode": "6056",
"addressCountry": "AU"
},
"geo": { "@type": "GeoCoordinates", "latitude": -31.89, "longitude": 116.01 },
"areaServed": ["Perth", "Midland", "Kalamunda", "Mundaring", "..."],
"openingHoursSpecification": [{
"@type": "OpeningHoursSpecification",
"dayOfWeek": ["Monday","Tuesday","Wednesday","Thursday","Friday"],
"opens": "07:00", "closes": "16:30"
}],
"hasCredential": "Registered Building Practitioner BP XXXXX",
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "4.9", "reviewCount": "87"
// fed live from the client's Google Business Profile, never hand-typed
},
"review": [{
"@type": "Review",
"author": { "@type": "Person", "name": "<real reviewer>" },
"reviewRating": { "@type": "Rating", "ratingValue": "5" },
"reviewBody": "<real review, synced from GBP>"
}]
}
</script>
Plus: suburb-level service pages for the hills corridor (low competition, high intent), GBP posts fed from progress photos, and FAQPage markup on the questions above — which are written from real query patterns ("fixed price vs cost plus WA", "how long to build a house Perth").
What would change for the real client
- Job & client systems: the enquiry form posts to their CRM, and the build-stages ledger becomes real — Buildertrend or Databuild integration surfacing each client's actual claim schedule and stage sign-offs in a login portal, with the public page keeping the worked example.
- Progress-photo feed: the SVG elevation is the demo's stand-in for a supervisor's weekly site photos — auto-published per stage from Buildertrend's daily logs.
- Reviews: live Google Business Profile feed replacing the demo cards, with genuine AggregateRating markup.
- Booking: "Book a site consult" writes into the estimator's calendar (Microsoft Bookings / Calendly) instead of a demo confirmation.
- Compliance: real BP registration, ABN, HIA membership number and indemnity insurer named in the footer; real display-home NAP if they run one.
Iteration log
Pass 1 — Craft
- Contrast audit: pending ledger amounts and the stage-button sublabels were set in soft slate
#8592A1(~3.2:1 on white) — minted a dedicated slate-ink #5C6878 (≥5:1) for small pending text and dropped the sublabel opacity; SVG annotation text (FFL, SWAN RIVER, DWG line) moved to the same ink. - Service-area map: geometry check showed the Fremantle and Mundaring dots sitting outside the dashed service boundary — redrew the polygon to hug the coast at Fremantle and clear Mundaring by a suburb's width.
- Compacted the mobile hero (padding, h1 and lede scale) so h1 + CTA + trust line all clear the 375×667 fold — verified by bounding-rect probe, not eye.
Pass 2 — Depth (every interactive driven, output asserted)
- Drove all five stages headlessly and asserted at each: probe stage, visible SVG layer count (1→5, all at computed opacity 1), claimed rows (2→6), and ledger figures — 20% / $156,000, 40% / $312,000, 60% / $468,000, 85% / $663,000, 100% / $780,000 — every figure matching the hand-computed schedule on a $780,000 contract; the 6.5 + 13.5 + 20 + 20 + 25 + 15 = 100 assertion is load-bearing in the shipped code.
- The screenshot caught what the state asserts couldn't: the ledger fill bars were inline
spans, so their width was silently ignored — class and style asserts all passed while no bar ever visually filled. Fixed withdisplay:blockand added a rendered-width (getBoundingClientRect) assert per stage so the regression can't return. - Added the second-read details: title-block revision letter advancing A→E with the stage, and the claimed total turning settlement-green only at 100%. Drive-then-diff liveness passes (pixels differ after driving the scrub) and keyboard arrows on the focused scrub move the stage (asserted 4→3).
Pass 3 — Hardening
- Form driven both ways: empty submit fires all five validations (5 invalid fields, 5 visible messages, focus jumps to the first), filled submit renders the personalised local confirmation with the form hidden — and a resource-timing probe confirms zero fetch/XHR, so no fake network round-trip. The confirmation panel's
hiddenstate is guarded by[hidden]{display:none !important}and asserted invisible at rest. - Visual sweep at full size found the drawing's title block overflowing its cells ("IRONBARK BUILDING CO." colliding with the stage cell, and "PRACTICAL COMPLETION" never fitting) — rebuilt it as two full-width rows; moved the Mundaring map label off the dashed service boundary.
- 375px: scrollWidth 375, no edge offenders, h1 + CTA + trust line above the fold, sticky call/quote bar visible with ≥44px targets; FAQ accordion driven open; reduced-motion emulation renders the hero at opacity 1 and the lockup rest state settled. Chanel rule: deleted the redundant FFL 0.00 annotation — the slab label and plate dimension carry the drafting story. Zero console errors across index (desktop, 375, reduced-motion) and guide.