Generative Assets · Industry demo 220

How the Ironbark Building Co. demo was made

Design and build notes for a conversion-focused landing page for a fictional Perth residential builder — every visual procedural, every number argued from the 2026 WA market.

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

Render white#EFEDE8 — acrylic render fresh off the trowel; the page ground.
Charcoal steel#26262A — structural steel; all body ink, the trust strip, the footer.
Jarrah#8C3B2A — the WA timber. LINE/MARK + primary CTA only; ~6.5:1 on render white, but body copy stays in steel per the ink/line split.
Drafting slate#3B4A5C — plan-line blue-grey; SVG linework, secondary data, links.
Slate ink#5C6878 — derived in Pass 1 when the soft slate (#8592A1) measured ~3.2:1 as small text; this holds ≥4.5:1 on white.

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

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

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 with display:block and 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 hidden state 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.