Concept
The audience is a new-estate owner on a sand block, or two neighbours staring at a leaning fence neither wants to ring about. The whole art direction is a fresh boundary line at noon — string line taut, first panels dead level — and the whole sales argument is transparency: the per-metre rate lists what's in it, and a dividing fence prints both halves so the neighbour conversation starts with numbers instead of guesses.
Palette — argued from the fence line
Both fence colours are real Colorbond market facts, which lets the interface literally wear the product: the estimate card is Monument, the CTAs are Deep Ocean, the drawn fence is the thing being sold.
Type
- Barlow Semi Condensed — display. The condensed capitals of a site signboard; the hero sets the trade's promise at wordmark scale.
- Barlow — body and UI. Same family DNA, so the page reads as one voice.
- DM Mono — metres, rates and shares. Everything with a dollar sign or a unit prints in mono, like the quote it's imitating.
The signature: Fence Line Builder
A length slider (10–60 lm), four material chips, a 0–2 gate stepper and a shared-boundary toggle drive a procedural SVG elevation. Every change clears and rebuilds the run: gates first, then panels at the material's real module (2.4 m Colorbond sheets and rail bays, 1.2 m glass panels), each panel an outer <g transform> for position with an inner <g class="p-in"> carrying the CSS build-in — the attribute/animation split the LEARNINGS SVG rules demand, so no presentation attribute is ever CSS-animated on the same element. Panel detail adapts to scale: Colorbond rib spacing and picket pitch have pixel floors so the profile stays crisp at 60 lm instead of dissolving into sub-pixel mush. Posts draw at panel joins (spigots for glass), a drafting-style dimension line prints the configuration under the run, and when the shared toggle is on, two small house glyphs — YOUR SIDE / NEIGHBOUR — sit at either end: the second-read detail that makes the 50/50 argument visual. The stagger finishes ≈1.4 s after load, so the 7-second thumbnail always captures a fully-built fence; reduced-motion renders every panel settled with no animation classes at all.
The pricing model — shape and derivation
All anchors are pinned by the wave spec; the model's shape is deliberately linear because that's how fencing is genuinely quoted — a rate per lineal metre that includes posts, rails, concrete and clean-up, plus gates priced each:
| Component | Rate |
|---|---|
| Colorbond 1.8 m, standard colours | $95 /lm |
| Hardwood picket | $185 /lm |
| Aluminium slat | $260 /lm |
| Semi-frameless pool glass | $290 /lm |
| Matching gate | $380 each |
| Pool-compliant self-closing gate (auto-selected with glass) | $520 each |
Formula: total = length × rate + gates × gate price. The slider's 10 lm minimum is the minimum job: 10 lm Colorbond = $950, and the services grid states the same "$950 minimum job" — the floors agree by construction, verified by assert.
The split rule: with the shared toggle on, the fence run halves under the Dividing Fences Act treatment and gates stay entirely yours: your half = fence ÷ 2 + gates. Odd-metre halves print cents (23 lm Colorbond → $1,092.50 each) rather than rounding shares that wouldn't sum to the whole job. Glass is the deliberate exception: pool barriers aren't dividing fences, so selecting glass suspends the split and the card says why — a documented flatness, not a missing feature.
Worked example (measured from the running page, not asserted from copy): the resting scenario 24 lm Colorbond + 1 gate, shared → whole job $2,660, your half + your gate $1,520, neighbour's half $1,140. The spec's ≈$1,520 hypothesis lands exactly on the model (24 × 95 ÷ 2 + 380). Harness scenarios were verified against an independently reimplemented oracle: 18 lm picket + 2 gates shared → $4,090 / $2,425 / $1,665; 30 lm glass + 1 pool gate → $9,220, all yours.
Conversion decisions
- Above the fold at 375px: name, trade + area in the h1, call CTA, builder CTA, and three trust marks — all verified inside the first viewport by probe.
- Header tel: + sticky mobile bar: a fencing enquiry is a phone in one hand on a sand block; the bottom bar splits Call / Get an estimate at 56 px tap height.
- Estimator → form handoff: "Book a measure with this estimate" writes the configured scenario and both shares into the job field, sets the suburb, scrolls, and focuses the name field — the demo interaction becomes a warm lead. The form is empty at rest so validation stays honest.
- Form fields: name, phone, suburb, job line — the four things a scheduler actually needs before a measure. Local confirmation only ("Received — thanks… we'd call you back within one business day"), clearly labelled a demonstration; no fake network round-trip.
- The neighbour letter is surfaced three times (trust strip, reviews, FAQ) because it's the real objection: fences fail on the conversation, not the posts.
Local SEO we'd implement for the real business
This demo page ships only a WebPage schema naming it a demonstration — fictional structured data in a real index would be dishonest markup. A real Panelcraft would ship LocalBusiness + Review schema fed by their actual details and live Google reviews:
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "LocalBusiness",
"@id": "https://panelcraftfencing.com.au/#business",
"name": "Panelcraft Fencing",
"description": "Colorbond, timber and pool fencing across Butler
and Perth's northern corridor.",
"url": "https://panelcraftfencing.com.au",
"telephone": "+61-8-XXXX-XXXX",
"address": {
"@type": "PostalAddress",
"addressLocality": "Butler",
"addressRegion": "WA",
"postalCode": "6036",
"addressCountry": "AU"
},
"areaServed": ["Butler","Alkimos","Eglinton","Yanchep","Jindalee",
"Quinns Rocks","Merriwa","Ridgewood","Clarkson","Mindarie",
"Banksia Grove","Carramar","Tapping"],
"openingHoursSpecification": [{
"@type": "OpeningHoursSpecification",
"dayOfWeek": ["Monday","Tuesday","Wednesday","Thursday","Friday"],
"opens": "07:00", "closes": "16:00"
}],
"priceRange": "$$",
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "4.9",
"reviewCount": "87"
},
"review": [{
"@type": "Review",
"author": { "@type": "Person", "name": "(real customer)" },
"reviewRating": { "@type": "Rating", "ratingValue": "5" },
"reviewBody": "(pulled live from the Google Business Profile)"
}]
}
</script>
What would change for the real client
- Tradify — the enquiry form books straight into quoting and job cards; the estimator's scenario lands as the job description.
- Buildxact — takeoffs for larger runs and multi-boundary estate work.
- Xero — invoicing and progress claims wired to job completion.
- DocuSign — the neighbour contribution letter becomes a signable agreement with both shares printed, closing the 50/50 loop digitally.
- Stratco / supplier ordering feeds — panel and post quantities from the builder's configuration flow into supplier orders.
- Podium + Google Business Profile — real reviews replace the demo copy and feed the schema above.
Iteration log
Pass 1 — Craft
19/19 asserts green · desktop + 375 full-page screenshots reviewed
Headless run at 1280 and 375: zero console errors, exactly one h1, resting state verified against the independent oracle ($2,660 / $1,520 / $1,140 on the card), floor consistency proven (10 lm Colorbond = $950 = the services grid's stated minimum), above-fold h1 + CTA + trust at 375, sticky call bar present with ≥44 px targets. The screenshot review — not the asserts — caught the FAQ heading orphaning "WITH" onto its own line; fixed with text-wrap:balance on section headings. Flagged the hero sun's double-circle halo as one ornament too many.
Pass 2 — Depth
29/29 asserts green · glass, slat and picket close-ups reviewed
Drove every control against the reimplemented oracle: all four material chips, slider (including keyboard arrows), stepper to both bounds (floor 0, cap 2, buttons disable), toggle off/on, and the odd-length case — 23 lm shared prints $1,092.50 halves in cents so the shares always sum to the whole job. Verified the glass exception end-to-end: split suspended, gate note and card swap to the $520 pool self-closing gate, honesty line explains the Act doesn't cover pool barriers. Prefill handoff asserted (scenario text + suburb + focus lands on the name field), FAQ driven by mouse and keyboard, drive-then-diff liveness passed. The glass close-up showed a part-drawn run — diagnosed as the capture catching the build-in stagger mid-flight, not a layout defect; the 7 s settle check later proved every panel visible. Second-read details confirmed rendering: the YOUR SIDE / NEIGHBOUR house glyphs and the glass sheen stroke.
Pass 3 — Hardening
23/23 asserts green · 375 builder, invalid/confirm states and 1200×750 thumbnail reviewed
Fresh-load empty submit flagged all four fields (aria-invalid + visible messages, probe reads invalid, focus to first error); filled submit rendered the confirmation in the contract lexicon ("Received — thanks… we'd call you back") echoing the suburb. Honesty sweeps: positive phone sweep (every (08) 9xxx number uses the 9000 exchange), a single WebPage-only JSON-LD block, exact colophon line, Index and guide links, Sapience band. Reduced-motion path renders every panel settled with zero animation classes; the 7 s thumbnail frame captures the fence fully drawn with the h1 in view. The screenshot review caught two defects every assert had sailed past: the slider's landmark labels colliding at 375 (fixed with short-form labels under 560 px) and section headings tucking under the sticky header on anchor navigation (fixed with scroll-margin-top) — and the re-shot then caught my own fix's CSS ordering bug hiding the "10 lm min" mark, fixed by moving the base rule above the media query. Chanel rule: the hero sun's halo circle was removed. Final copy read done; guide numbers re-checked against the shipped code.