01 Concept
Truelevel Plastering & Render is a fictional solid plastering operator based in Maddington, working metro-wide — external sand & cement render, acrylic texture and white set. The audience is renovators with raw brickwork and owner-builders pricing the finishing trades; the page's single job is a render or white-set enquiry with square metres already on the meter. The direction: the finish is the page — set-white ground, near-monochrome discipline, and one blue chalk line snapped across the layout the way a plasterer snaps a line before the first coat.
02 Palette
Argued from the white set and the chalk line. Near-monochrome on purpose — a plasterer's craft argument is restraint, so the page makes the same argument.
03 Type
Archivo (700–800) carries display work — squared and straightedge-flat, it sets like something scribed against a rule. Public Sans is the workhorse: plain, legible at 16.5px, no personality competing with the trade. The typographic moment is the coat sequence SCRATCH · FLOAT · SET run huge and tracked under the hero, each word revealed by a wipe in its own coat colour — scratch grey, render grey, set white — the page literally plastering its own headline.
04 Signature — the Render Reckoner
An SVG wall elevation built up in coats: raw brick shows through at the right edge, the comb-scratched scratch coat covers most of the face, and the selected finish coat sweeps on behind a steel trowel whose leading edge is a live quadratic arc. Finish chips (sand & cement / acrylic texture / white set / patch repair), a 10–120 m² area stepper, and a storey toggle drive a live ledger; the chosen finish and area pre-fill the enquiry form.
Implementation
- Coat reveal: two clipPaths whose d is rebuilt per frame — a rectangle whose right edge is a quadratic bézier bulge (the trowel's arc), oscillating with time. Finish coat rests at 66% coverage, scratch at 86%, so all three layers — brick, scratch, finish — read in a single static frame. The animation floor never drops below the rest coverage, so any capture instant shows the signature alive.
- rAF discipline: dt = min(max(now − last, 0)/1000, 0.05) — clamped at zero (the rAF timestamp can precede the scheduling clock) and capped. The loop pauses on document.hidden and never starts under prefers-reduced-motion, which instead renders the settled 66%-coated frame.
- Dual-speed redraw: the first draw earns the entrance (scratch sweep, then finish coat, ~1.5 s); every later finish swap re-sweeps in 460 ms via a firstDraw flag. Area and storey changes redraw instantly — only a coat change earns a sweep.
- Race safety: every control handler bumps an animation token before touching state, so an in-flight sweep is invalidated in the handler, not in a deferred rebuild. Rapid-fire multi-click drives (eight clicks in one task, then a twelve-click burst) are part of the QA harness.
- Patch honesty: the patch chip swaps the wall for a crack map whose cracks visibly fill with plaster-toned strokes (pathLength=1 dash animation, butt caps, fill fraction 0.6–0.94 so a raw crack tip always shows). The ledger swaps to the fixed statement "patch visits from $280 (half day)" — the control changes the rendering honestly without computing a figure, and the area stepper and storey toggle disable with on-card reasons.
- State probe: window.__plasterer exposes scenario, ledger lines, subtotal, billed, minimum flag and a settled flag; the harness gates on state, never on seconds.
05 Pricing model — anchors and measured outputs
Every figure is pure anchor arithmetic, measured from the running page and cross-checked against an independently reimplemented oracle in the QA harness. Totals display to the whole dollar; the $920 minimum is a total-level clamp whose note appears only when it binds.
| Anchor | Rate | Notes |
|---|---|---|
| Sand & cement render, external, single storey | $52 /m² | Base metered rate |
| Acrylic texture coat over new render | +$34 /m² | Itemised as its own ledger line ($86/m² combined) |
| White set internal, two-coat over brick | $46 /m² | Storey toggle disabled — internal work prices the same on any floor |
| Double-storey access premium | +$8 /m² | External finishes only; its own ledger line |
| Minimum job | $920 billed | Total-level clamp; note shown only when binding |
| Patch repairs | from $280 | Half-day visit — a statement, never a computed total |
| Heritage lime / cornice restoration | no number | Quoted on inspection — substrate and detail drive it |
| Scenario | Arithmetic | Billed (measured) | Minimum note |
|---|---|---|---|
| Render, 60 m², single (rest state) | 60 × $52 | $3,120 | — |
| Acrylic, 60 m², single | 3,120 + 60 × $34 | $5,160 | — |
| Render, 60 m², double | 60 × ($52 + $8) | $3,600 | — |
| White set, 35 m² | 35 × $46 | $1,610 | — |
| White set, 20 m² (boundary) | 20 × $46 = $920 exactly | $920 | none — clamp not binding |
| White set, 15 m² | $690 → clamped | $920 | shown |
| Render, 15 m² | $780 → clamped | $920 | shown |
| Render, 20 m² | 20 × $52 | $1,040 | — |
The exact-$920 boundary is reachable from the stepper grid (white set at 20 m²), so the no-note-at-boundary semantic is asserted at a real UI point, not just at the meter level. The meter's cheapest billed output ($920) equals the services grid's stated minimum, and the patch statement ($280) equals the grid's patch floor — the page never argues with itself.
06 Conversion decisions
- Above the fold at 375px: wordmark, value proposition naming trade and area, the call CTA, and the est. 2013 trust signal in the eyebrow — moved there during Pass 1 when the trust strip proved to sit below the fold on a phone.
- Call-first, form-second: plastering enquiries skew to phone, so tel: leads the header, the hero, the sticky mobile bar and the form aside. The form exists for after-hours browsing and for the Reckoner handoff.
- Reckoner → form handoff: every Reckoner change rewrites the form's finish select, area field, and a visible "pre-filled from the Reckoner" line — the demo interaction becomes a warmer lead with the scenario already attached.
- Price badge on the wall: the live estimate sits on the wall art itself, so the number is visible in the same glance as the signature — added in Pass 1 when the 1200×750 capture showed the ledger below the fold.
- Form fields: name, phone, suburb (required — how a trade actually calls back), optional area and job description, finish select. Front-end validation only; the confirmation panel is local, instant, and says plainly that no details were sent. Empty submits show an error summary and keep the panel hidden.
- Honest controls: the storey toggle disables for white set ("internal work prices the same on any floor") and for patch; the area stepper disables for patch ("priced by the half day, not the square metre"). Every control either changes the price honestly or says why it can't.
07 Local SEO we'd implement for the real business
The live demo page carries only WebPage/WebSite schema naming it a demonstration — fictional structured data in a real index is dishonest markup. A real Truelevel would ship the block below. Schema.org has no plasterer subtype, so the correct top-level type is HomeAndConstructionBusiness — the choice a real SEO build would make, with the trade carried in knowsAbout and service names.
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "HomeAndConstructionBusiness",
"name": "Truelevel Plastering & Render",
"description": "Solid plastering, external render and white set across metro Perth.",
"telephone": "+61 8 9000 0306",
"address": {
"@type": "PostalAddress",
"addressLocality": "Maddington",
"addressRegion": "WA",
"postalCode": "6109",
"addressCountry": "AU"
},
"areaServed": ["Maddington", "Gosnells", "Thornlie", "Canning Vale", "Perth"],
"knowsAbout": ["sand and cement render", "acrylic texture coating",
"white set plaster", "heritage lime plaster"],
"openingHours": "Mo-Fr 06:30-15:30",
"priceRange": "$$",
"review": {
"@type": "Review",
"reviewRating": { "@type": "Rating", "ratingValue": "5" },
"author": { "@type": "Person", "name": "Karen M" },
"reviewBody": "Rendered our 90s red-brick front in two days — flat as glass."
},
"aggregateRating": {
"@type": "AggregateRating", "ratingValue": "4.9", "reviewCount": "47"
}
}
</script>
Plus: Google Business Profile with service areas and photo cadence, suburb-level service pages (render Thornlie, white set Canning Vale), review capture wired to job completion, and citations on the WA trade directories.
What changes for the real client
Their photos replace the procedural wall (though the Reckoner stays — it's the lead magnet), their live review feed replaces the demo copy, and the form posts to their job system. For a plastering operator that's usually ServiceM8 or Tradify for quoting and scheduling, AroFlo where there's a bigger crew, and Xero on the invoicing end — the enquiry lands as a draft job with the Reckoner scenario attached, not as an email someone retypes.
08 Iteration log
- Harness: 47/47 asserts green first run (oracle-checked worked examples, boundary semantics, rapid-fire drives, empty/filled submit, 375px masthead measured wordmark-right 114px vs call-left 202px).
- Screenshot review caught what the asserts couldn't: the trust signal sat below the 375px fold — moved est. 2013 into the hero eyebrow. The live price was invisible in the 1200×750 capture (ledger below fold) — added the estimate badge onto the wall art, updating live.
- The rest state hid the brick entirely (scratch coat at 100%) — scratch coverage now rests at 86% so brick, scratch and finish all read in one frame. Fixed a stale "single storey" caption surviving into patch mode, and a stray attribute space in the map SVG.
- Second-read detail: faint trowel float-marks (7px arcs at 5.5% ink) living only inside the finish-coat clip — visible on the second visit, gone when the coat is.
- Micro-interaction: the wall badge ticks (320 ms overshoot scale) when the figure changes; suppressed under reduced motion.
- Patch cracks got branch fissures and plaster-toned fills (0.6–0.94 fill fraction) so the unfilled crack tip always shows — the repair reads as in-progress, not finished. Added a 6 s timed fallback for the SCRATCH · FLOAT · SET reveal so the display line can never stay unrevealed.
- Re-ran the full drive matrix at 1200 and 375 plus reduced-motion and keyboard paths (chip via Enter, FAQ via keyboard) — 55/55 asserts green; zero console errors across every run.
- Screenshot review of the reduced-motion path caught a leftover ps = 1 in the static-init branch — the scratch coat covered the brick entirely under reduced motion while every state assert stayed green. Fixed to the 86% rest coverage; the rendered image remains the ground truth.
- Chanel rule: removed the "live estimate" tag from the Reckoner head — the wall badge already says it.
- Verified the guide's own 375px behaviour (pricing tables in .table-scroll wrappers), the headless renderer's view of the masthead (no backdrop-filter anywhere), and that the confirmation panel's visibility transition — hidden on load, hidden on empty submit, shown on valid submit — probes correctly on the element itself.