The concept
Karriwood Flooring is an invented four-van supply-and-install outfit doing hybrid, engineered oak and laminate across Perth’s northern and coastal suburbs. The audience is a homeowner or a landlord standing in a room with a tape measure, deciding between three products they cannot price, because every flooring site in Perth ends at “book a free measure and quote”.
So the page prints the rate card and lets the visitor run their own room through it. The single conversion job is to get them from “I have no idea what this costs” to “I know the number and I want it confirmed” without a phone call in between — because a visitor who already knows the price rings to book, not to ask.
Palette
A warm oak showroom rather than a warehouse: paper, a walnut band, and a cool teal so the decision element does not read as more timber. Every pair below was measured by the wave’s contrast sweep and each ratio is quoted from the same row of contrast-w54.log as its hex.
The one that mattered in practice: a dark seam drawn on the walnut fill measures 1.06:1 and is forbidden by the sweep, so the hybrid plank uses a light seam (#C6A784, 2.88:1) while oak and laminate use the dark one. The seam colour therefore switches with the product, not just the fill. The room outline is drawn 2.5 px outside the plank field for the same reason: #4A3925 is licensed on paper (9.88:1), on oak (4.72:1) and on laminate (5.35:1), but not on walnut (1.69:1), so the stroke is kept off the fill entirely.
Four colours were minted during this build
The wave palette had no failure colour and no translucent edges, and the page needs both. All four were added to the sweep and re-measured; it now declares 205 pairs across the wave with zero failures.
| Minted token | Where it is used | Measured |
|---|---|---|
errorInk #8A2B12 | Invalid-field message and border on the enquiry form | 7.72:1 on paper 6.7:1 on panel |
stickyEdge #F6F2EA @60 % → #A8A097 | The sticky bar’s secondary button edge | 5.68:1 on band |
footRule #F6F2EA @18 % → #4A4139 | The footer’s divider rule | 1.71:1 on band hover |
focusGlow #1A6875 @24 % → #C1D1CE | The glow behind a focused form field | 1.42:1 on paper |
The last one is worth naming: it was first written at 16 % opacity, which blends to #D3DCD7 and measures 1.25:1 — under the sweep’s 1.3:1 hairline floor and effectively invisible. The sweep caught it on the first run and the alpha was raised to 24 %. Everything else on either page is a token quoted straight out of the flooring block of contrast-w54.log.
Type
Fraunces carries the headings — a warm, slightly editorial serif that reads as a showroom rather than a trade counter, and holds up at 40 px in the hero and at 38 px on the estimate total. Inter does everything else: controls, tables, prices, form. Every figure on both pages is set with font-variant-numeric: tabular-nums, so the price does not jitter horizontally as the slider moves — which is the difference between a calculator that feels engineered and one that feels like a widget.
The scale is deliberate rather than modular: 40 / 24 / 21 / 20 / 17 / 16 / 14.5 / 13.5 / 13 / 12 / 11. The 11 px sizes are reserved for uppercase tracked labels on high-contrast grounds, never for body copy.
The Floor Estimate
The proof element is a single formula, published on the page:
total = max(area, 20) × (productRate + prepRate)
Rates are held in whole cents and divided once at display, so no floating-point residue can reach a price. Three products (hybrid $69/m², engineered oak $128/m², laminate $52/m²), three prep options (none, subfloor levelling +$18/m², lift and remove +$8/m²) and a 20 m² minimum billed area. Every figure the page can print at a preset is licensed by check-w54.mjs, which runs 62 asserts across the wave and is green.
| What | Figure |
|---|---|
| Living area 32 m², hybrid, no prep — the rest state | $2,208 |
| Same room, engineered oak | $4,096 |
| Same room, laminate | $1,664 |
| The oak-to-laminate gap the page prints out loud | $2,432 |
| Subfloor levelling on 32 m² | +$576 |
| Bedroom 14 m², hybrid — billed at the 20 m² minimum | $1,380 |
| Whole house 110 m², hybrid, old floor lifted and removed | $8,470 |
| Whole house 110 m², engineered oak, no prep | $14,080 |
The drawing
Above the result sits a plan plate drawn in inline SVG at a fixed 16 px per metre. The room rectangle scales with the area; the plank field inside it re-lays itself in the chosen product, with the real board sizes — hybrid 1.20 × 0.19 m, engineered oak 1.90 × 0.24 m, laminate 1.30 × 0.195 m — so the board pattern visibly changes, not just the colour. Rows are staggered by a third of a board length, which is how the floors actually go down.
Because the scale is fixed rather than fit-to-box, two areas can be compared honestly, and a one-metre grid aligned to the room’s top-left corner makes that scale visible instead of merely claimed. A dashed 110 m² whole-house outline stays on the plate as a constant reference. When the room falls under 20 m², a second ghost outline appears at exactly 20 m² and is labelled “billed at the 20 m² minimum” — the minimum is a picture, not a footnote.
The two floors you did not pick
All three products are priced in the result card, at the same area, with the same prep, in three tiles of identical size and identical type size. The chosen one is reversed onto the band; the other two are not smaller and not greyed. Underneath, the page prints the live gap — at the rest state, “laminate does this room for $2,432 less than engineered oak” — and says it would put the laminate in a rental. That sentence costs the seller money on every rental job, which is precisely why it earns trust.
Motion
When the product or the area changes, the plank rows fade in on a wall-clock schedule — performance.now() deltas, a 620 ms cubic ease and a 7 ms per-row stagger — and the loop stops when the last row reaches 1. There is no per-frame accumulator anywhere: headless Chrome services requestAnimationFrame only when a frame is requested, so a frame-counted animation would still be halfway through at the 7-second thumbnail instant. The loop also stops on document.hidden and settles instantly on return rather than replaying, and prefers-reduced-motion skips the reveal altogether.
The live state is exposed on window.__flooring — area, billed area, product, rate, prep, total, all three product totals, the oak-to-laminate gap, and the reveal progress — as plain numbers, so the checks read state rather than scraping text.
Where the refusal sits, and why
The block naming the three charges Karriwood will not price — a waste levy, a licensed asbestos removalist, a builder’s repair to a rotten subfloor — is the immediate next sibling of the result card, 12 px under the price, on the dark band so it cannot be mistaken for small print. It is not in the FAQ, and both facts are asserted five ways: the refusal must be #result’s next element sibling, the gap between them must be under 40 px (it measures 12), it must live in the same section, it must not be inside the FAQ, and the FAQ text must contain none of its terms. The assert exists because a later tidying pass will otherwise move it.
What this page declined to publish
The concept brief flagged waste levies and asbestos-removal licensing as the places a flooring page is most likely to invent a real-world figure. This page publishes no figure, rate, threshold or licence class belonging to any regulator, scheme or published charge:
- The waste levy. Not quoted, not estimated, not given a range. It is set by the state, it moves, and a page that prints it is publishing somebody else’s number as its own. The page says it is charged at cost.
- Asbestos removal. No price, no licence class, no threshold in square metres, no statement about what does or does not require a licensed removalist. That is a real regulatory boundary and the page does not describe it — it says the work is somebody else’s and is charged at cost with their paperwork attached.
- Structural or subfloor repair. Named as a builder’s job and left unpriced.
- Product warranties. The FAQ says the manufacturer’s warranty “varies by range” and gives no term in years, because that is a manufacturer’s figure. Only Karriwood’s own five-year workmanship guarantee — a figure the fictional business sets for itself — is stated.
Everything the page does price is its own labour and its own supply. That is the whole rule: a demonstration may invent a business, but it may not invent a fact about the real world.
The three passes
Pass 1 — Craft
- The answer was below the thumbnail frame. On the first render the result card ran 755–1067 px at 1200×750, so the collection’s headline sales asset would have shown a drawing and no price. Two fixes: the controls moved above the plate (title → controls → plate → result), and at desktop the result was split into two grid columns — the total on the left, the three product prices on the right — which took the card from 303 px tall to 216 px. The drawing now sits 384–567 px, the headline total 609–647 px and all three product prices 596–709 px, all inside the top 750.
- The 375 fold was 26 px short. The trust line finished at 628 px against a sticky-bar top of 602 px, so the last line of the page’s honesty claim was physically under the call bar. The trust copy was cut from three sentences to two; it now finishes at 586 px.
- The controls cost 250 px. The chips were flattened from a two-line stacked label to a single row with the rate inline, taking the product and prep groups from 89 px each to 53 px.
- The plate had 150 px of dead space between the label block and the room at small areas, and the room floated in it with nothing to read it against. Added the one-metre grid aligned to the room’s corner and the dashed 110 m² whole-house reference — the plate now reads as a plan sheet and the fixed scale is visible rather than asserted.
- The hero was 130 px out of alignment at 1440. The top block had page padding but no
max-width, so the hero and the estimate ran edge to edge while the masthead and every section below sat in an 1180 px column. Capped and centred to match. - Copy read: cut “cart away” to “lift and remove” (the street-noun scan flags away, and the shorter phrase is better copy anyway), removed a redundant scale caption under the plate, and cut the estimate lede from two lines to one.
- Drove the estimate across all three products at 14 / 32 / 110 m² and measured $1,380 / $2,208 / $7,590 (hybrid), $2,560 / $4,096 / $14,080 (oak), $1,040 / $1,664 / $5,720 (laminate) — every preset figure matching
check-w54.mjs. Drove the form empty (name, phone and suburb all flagged, no confirmation), with junk in the phone field, and with good data (confirmation shown, estimate carried into it).
Pass 2 — Depth
- The plate was illegible at 375 and nobody would have known from a state probe. At 375 the SVG renders about 283 px wide against a 520-unit viewBox, so the 12-unit label block came out at 6.5 px and the 32 m² room at 58 px across. Fixed by switching the viewBox to a window on the room itself (
150 0 320 196) below 561 px and moving the whole label block out of the SVG into real HTML type underneath — the room now renders at ~100 px with the grid and the reference outline both readable. - The same defect was live on the service-area map. Thirteen suburb labels set at 13 units rendered at 6.8 px on a phone. The map type scales up below 561 px and five labels that then collide are dropped — and three labels sitting to the left of their dot were running straight through it, because they were positioned for a 13-unit width; they now use
text-anchor="end". A dashed key was added so the two catchment rings say what they mean. - The 20 m² minimum became a picture. Below 20 m² a dashed ghost outline appears at exactly the billed area with its own label, and the label block switches from “billed as 14 m²” to “billed as 20 m²”. The floor is now visible in the drawing, in the detail line and in the state probe, not just in a footnote.
- Second read: the board sizes are real and different per product, and the plank field’s row height, plank length and stagger are computed from them — so a visitor who switches from oak to laminate sees the boards get narrower and shorter, which is what actually happens on the floor. Nobody notices on the first visit; anybody who switches twice notices on the second.
- Micro-interaction: chips ease their ground over 150 ms on a
cubic-bezier(.2,.7,.3,1), and a form field clears its error state on the first valid keystroke rather than waiting for another submit. - The gap sentence was made live. It shipped as a fixed string; it now recomputes from the current area and prep, so it stays true at every point on the slider rather than only at the rest state.
- Drove the estimate again across all three products at 14 / 32 / 110 m², plus both prep options and the 12 m² case that falls to the minimum: the levelling delta on 32 m² measured exactly $576, the 110 m² hybrid with the old floor removed $8,470, and 12 m² hybrid $1,380 with
billedAreareading 20. Drove the form three ways again, plus an out-of-range area.
Pass 3 — Hardening
- Every
patternattribute was compiled with the browser’svflag, in Node and again in the page. The phone field ships[0-9 +\(\)\-]{8,20}with the brackets and the hyphen escaped; the unescaped form is valid in Node and dead in Chrome, where it throws on every keystroke and silently accepts anything. Verified in the running page that it accepts0400 111 222and(08) 9000-0426and rejectsring me maybe. - Breakpoint selectors were asserted to match something. The rule that hides five crowded suburb labels on the service-area map at 375 is checked with
querySelectorAll('.m-lab.opt').length > 0and then checked again for computeddisplay: noneat 375 — a compound selector that matches nothing fails silently and no state probe can see it. - Negative-tested the fold check. Growing the sticky bar to 520 px was confirmed to fire the h1, CTA and trust-line asserts, then reverted. A fold check whose CTA lookup resolves the masthead button passes on every page ever built, so the CTA is taken as the first one at or below the h1’s top — and the negative test asserts that the button it found is “Price my room” in the hero, not the one in the header.
- Negative-tested the refusal-placement check by appending the block into the FAQ in a live copy of the page: all five placement asserts fired — sibling, 40 px proximity, same-section, not-in-FAQ, and the FAQ-text scan.
- Negative-tested the thumbnail-frame check by pushing the estimate card down 320 px and confirming the drawing, the headline total and the three-prices asserts all fire.
- Negative-tested the honesty scans by feeding the checker a fabricated phone number outside the reserved block and a fabricated street address, in the harness rather than on the page, and confirming both fire; and negative-tested the map rule by stripping the
.optclass and confirming that rule is genuinely what hides those five labels. - Every
patternattribute and every breakpoint selector is asserted..m-lab.optmatches five elements and all five compute todisplay:noneat 375. An empty NodeList is the most expensive kind of silence. - Thumbnail contract: at 7 s the page is at rest —
revealis 1,settledis 1, and re-reading every numeric probe field 8 s later shows nothing moved. The plate sits 384–567 px, the headline total 609–647 px and the three product prices 596–709 px, all inside the top 750. The result card itself runs to 799 px, so the only part of the estimate below the frame is the seller-costing gap sentence. The generated thumbnail was opened and looked at, not merely asserted. - 375 sweep: no horizontal overflow, nothing in the masthead, hero or sticky bar outside the viewport box, and the h1 (238 px), the primary CTA (410 px) and the first trust line (586 px) each finish above the bar at 602 px. Reduced-motion confirmed under
--force-prefers-reduced-motion: every plank row is opaque on the first frame, the probe reads settled, and the price is still right. Focus-visible rings on every chip, link, field and button. - Chanel rule: removed the scale caption under the plate at desktop. The metre grid and the 5 m bar already say it, and the sentence was the third thing in a row explaining the same drawing.
- Drove the estimate a third time across all three products at 14 / 32 / 110 m² at 375 as well as 1200, and drove the form three ways at 375, where the sticky bar sits over the submit button. Final harness: 107 of 107, including fifteen deliberate negative tests, with zero console errors on either page.
Why this converts
The call button and the estimate link sit in the masthead, in the hero, in a fixed bar that follows the visitor down a phone screen, and again in the footer — so the decision to ring is never more than a thumb away, and the bar carries a real tel: link rather than a form scroll dressed up as one. The enquiry form asks for four things, only three of them required, because every extra field on a trade form is a person who closes the tab: a name, a number, a suburb so the office knows which van it lands on, and the room size if they happen to know it — and the estimate they just built travels with the enquiry, so the first phone call starts with a figure both sides can already see. The proof element is the whole argument: it answers the one question a flooring customer actually has, it answers it in the visitor’s own room rather than in a generic example, and it prices the two products they did not choose at the same size as the one they did, which turns a quote into a model instead of a pitch. The trust strip does the work that a photo gallery usually pretends to do — twelve years, four vans, a stated minimum billed area, and an explicit line that three charges will not be priced here — because a specific limit is more persuasive than a general promise. The whole page is one file, about 63 KB, with no libraries and no images to load, which is why it renders instantly on a phone standing in an empty room with one bar of reception, which is exactly where this decision gets made.
What would change for a real client
Their rate card in place of ours, their photography (a real flooring business should absolutely show its own work — this page draws instead because the collection ships no raster images), and their actual reviews pulled from Google rather than written as demonstration copy. The enquiry form would post to whatever they already run: ServiceM8, Simpro, Tradify or AroFlo for the job card, with the estimate’s inputs written into the job notes so the measure is booked with the product and the area already attached. Their suburb list drives the van scheduling. And the rate card would live in one small config object — as it does here — so a price rise is a one-line edit and every figure on the page moves with it.
Local SEO notes
The page names the trade and the area in the <title>, the h1 and the meta description, lists twenty serviced suburbs in text as well as on the map, and answers seven real questions in plain language in the FAQ. What it deliberately does not do is emit LocalBusiness, Service, Offer, Review or AggregateRating structured data: the business is fictional, so marking it up as a real trading entity with real reviews would put invented facts into a search index. The only structured data is a WebPage and a CreativeWork, both of which say in their own description that this is a demonstration. On a real client’s site the full local markup is the first thing we would add.