The concept
Millform Shopfitting is a fictional commercial fitout builder for café operators, retail tenants and allied-health owners across the Perth metro. The page has one job — book a site walk-through — and one argument: a fitout is a building built indoors in six weeks, so the firm that runs the program (landlord approval, trades in sequence, certifier and health signoffs) is the firm you want holding the keys deadline. Everything on the page, from the m² maths to the make-good line, performs that competence.
Palette — ply and blueprint
Argued from the workshop at 8am: sanded birch sheet under skylight, steel-blue shadow, setout lines on a taped plan, one timber-oil tin.
Type
- Hanken Grotesk (display) — a quietly confident grotesque with joinery-flat terminals; the masthead and headings carry the brand's calm authority at 800 weight, −0.02em.
- Inter (body, UI) — the workhorse; 16.5px body keeps the mobile floor comfortably above 16px.
- JetBrains Mono — every m², week and dollar. The price card, dimension line, trust figures and footer NAP are set in mono so the numbers read as measurements, not marketing.
The signature — the Fitout Scoper
Three venue chips, a 40–300 m² stepper and three spec chips drive a procedural SVG cutaway of the tenancy that fits out live. The scene is built as markup strings per element (stud walls, counter, joinery runs, exhaust hood, ceiling grid, duct, pendants), each wrapped in an outer <g transform> for position and an inner <g class="ein"> for the CSS entrance — position by attribute and animation by stylesheet stay on separate nested groups (the W16/W17 rule). The floor area stretches the drawn tenancy between 372 and 584 viewBox units with a blueprint dimension line pencilling the live m² beneath; spec chips add layers (refresh: paint, counter, pendants; mid: partitions, joinery, floor finish; full: ceiling grid, services duct, extra joinery). The rest state holds the tenancy about two-thirds fitted — the trailing third of the build sequence renders as work-in-progress at 0.62 opacity (dashed pendants, unfinished banquette), comfortably above the 0.4 visibility floor at any capture instant. Completed elements carry a small orange site-tag; offcuts sit swept into the slab corner — the second-read details. Redraws are dual-speed: the first render staggers 90 ms per element (capped ~1.1 s), every subsequent chip or stepper change staggers 34 ms capped at 360 ms plus a 300 ms fade, and a window.__shopfitter.settled flag flips when the redraw lands so QA gates on state, never seconds.
The pricing model — anchors and shape
Rates are flat $/m² per venue × spec, all-in (supplied, built and certified, trade-managed). The scoper's output is area × rate — deliberately linear, shipped as a documented pricing statement: at demo scale, the honest story is that the rate carries the compliance load of the venue class, not that price curves with area. Site-time bands are pinned per spec, not computed.
| Venue | Refresh $/m² | Mid-spec $/m² | Full custom $/m² | Smallest honest job (40 m² refresh) |
|---|---|---|---|---|
| Retail | $450 | $950 | $1,500 | $18,000 — the page floor |
| Café & hospitality | $700 | $1,600 | $2,400 | $28,000 |
| Office & consult | $500 | $1,050 | $1,650 | $20,000 |
- Minimum job $18,000 — the clamp semantic is the stepper's own floor: 40 m² retail refresh × $450 = $18,000, pure arithmetic, no separate dollar clamp exists. Measured from the running page: the scoper's cheapest output is exactly $18,000 and equals the services grid's "retail fitouts from $18,000".
- Worked example at rest — café & hospitality, 120 m², mid-spec: 120 × $1,600 = $192,000, "6–8 weeks on site". Measured $192,000 — matches the spec's anchor arithmetic exactly. Chipped to full custom: 120 × $2,400 = $288,000, measured $288,000.
- All nine venue × spec combinations were driven against an independently reimplemented oracle in the QA harness; every displayed figure matched, including the 300 m² ceiling (café full custom, 300 × $2,400 = $720,000).
- Site-time bands — refresh 2–3 weeks, mid-spec 6–8, full custom 10–14: pinned copy on the price card, repeated in the FAQ from the same source values.
- Custom joinery (from $6,500), make-good (from $4,500) and drawings & approvals (from $2,800) are their own service lines outside the scoper and say so on-card — they price off drawings, not m².
Conversion decisions
- Scoper in the hero. The signature shares the 1200×750 thumbnail with the h1 (card top lands at ~117px) — a prospect sees the live tenancy and the price maths without scrolling, and the hardening pass asserts the intersection.
- Scope → form handoff. "Send this scope to the quote form" copies venue into the select and writes a one-line scope summary into the notes field — the demo interaction becomes a warmer lead, and the venue select means the fictional office triages before calling back.
- Form fields mirror how fitout enquiries actually qualify: suburb of the tenancy (travel and centre inductions), lease status (signed / negotiating / still looking — the whole program hangs on it), and target opening date (the rent-free clock,
type=datemin today). Confirmation is local and honest: "we'd call you back within one business day", with a plain "nothing is sent anywhere" demo note. - Landlord band directly under the hero — fitout approval is the trade's defining gate, so it sits before services, not in the FAQ fine print.
- Sticky call/quote bar below 720px with ≥48px tap targets; header carries the tel: link at every width.
Local SEO we'd implement for the real business
This demo page ships only WebPage schema naming it a demonstration — fictional LocalBusiness/Review data in a real index is dishonest markup. For a real client the page would carry:
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "LocalBusiness",
"@id": "https://millformshopfitting.com.au/#business",
"name": "Millform Shopfitting",
"description": "Commercial fitouts for cafés, retail and consult suites across Perth.",
"telephone": "+61-8-XXXX-XXXX",
"address": { "@type": "PostalAddress", "addressLocality": "Osborne Park",
"addressRegion": "WA", "addressCountry": "AU" },
"geo": { "@type": "GeoCoordinates", "latitude": -31.9016, "longitude": 115.8106 },
"areaServed": "Perth metropolitan area",
"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": "Fitted out nine days before the rent-free ran out." }]
}
</script>
— fed by a live review pipeline, never hand-typed. Plus suburb-level service pages, GBP with fitout-category photos, and case-study pages per venue class (café / retail / consult) matching the query families tenants actually search.
What would change for the real client
- Estimating: the scoper's rates would read from the client's Buildxact or Wunderbuild estimating data, so the web maths and the quote maths never argue.
- Program: a shared Gantt (Smartsheet / MS Project-style) surfaced to the tenant — the "program on the wall" the reviews talk about, live.
- Approvals: lessor drawing-pack workflows — versioned packs, landlord comments, approval status the tenant can see.
- Trades: Assignar for crew scheduling and inductions across concurrent fitouts.
- Money: Xero progress claims tied to program milestones.
- Proof: Podium review flow replacing the demo review cards, feeding the schema above; real photography replacing the procedural scene — though we'd argue for keeping the live scoper beside it.
Iteration log
Pass 1 — Craft
Headless harness (puppeteer, port 7001): 28 asserts, all green first run — one h1, canonical, WebPage-only schema, rest state café/120/mid displaying $192,000 against the independent oracle, full-custom crossing $288,000, floor scenario $18,000 equal to the services grid floor with the minimum-job note showing, empty submit on a fresh load raising 6 invalid fields + aria-invalid, filled submit confirming in the "we'd call" lexical family, sticky bar fixed with a tel: link at 375px, masthead collision-free (wordmark right edge 95px vs tel left 225px). The screenshot review then caught two things no assert saw: the café pendant run collided with the exhaust hood at the kitchen end, and the scene's "MID-MAKE" annotation was set in raw tag orange at ~3.4:1. Fixed both — pendants confine to front-of-house for café mid/full, annotation demoted to tag-ink.
Pass 2 — Depth
Added the second-read layer: offcuts swept into the slab corner, orange site-tags on completed elements (work-in-progress elements get blueprint dashes and 0.62 opacity instead), and a 500 ms price-tick colour pulse when the figure changes. Drove all nine venue × spec combos against the reimplemented oracle — nine matches; stepper ceiling 300 m² disables the up control; keyboard drive of chips and FAQ passed; reduced-motion emulation renders the scene settled and complete with no entrance. One harness bug caught (the combo loop left spec at full custom, so the 300 m² assert expected the wrong rate — the displayed $720,000 was the page being right); fixed the harness, not the site.
Pass 3 — Hardening
375px masthead screenshot reviewed first — clean single row, wordmark and phone clear of each other. Chanel cut: removed the scene's duplicate "TENANCY SECTION" header line (two stacked mono annotations was one too many). Guide page probed at 375px with the pricing table in a scroll wrapper; reserved-phone positive sweep confirms every (08) 9xxx number on both pages uses the 9000 exchange; resource allowlist scan clean (Google Fonts only); the scoper's bounding box intersects the first 750px at 1200 wide for the thumbnail; final copy read killed two flabby phrases. Zero console errors across every run.
Techniques
- Procedural SVG throughout — the scoper scene, the setout-style Perth service-area map (coastline, Swan River, dashed metro boundary, dimension flourish), the favicon. No raster assets.
- CSS-only texture: layered
repeating-linear-gradientply grain on the body, blueprint grid in the scene box. - IntersectionObserver reveals with a no-IO guard class; entrance gated on
document.fonts.readywith a 900 ms fallback timer. - Dual-speed scene redraw behind a
firstDrawflag;window.__shopfitterstate probe with asettledflag for QA gating. - Reduced-motion: media query plus a
matchMediacheck that skips the entrance entirely and renders every element settled.