Generative Assets · industry demonstration 425

How the Sandover Civil page was built

A fictional Neerabup subdivision civil contractor — roads, drainage, sewer and water — built as a lead-generation landing page. Its single job is a booked call with the estimator, and its whole conversion argument is that it publishes a per-lot rate the day a developer needs one, while refusing to invent a single figure that belongs to Water Corporation, Western Power or a local government.

The concept

A small developer with a four-to-twenty-lot subdivision has a feasibility spreadsheet with an empty civils line and, usually, no drawings. Every civil contractor in Perth answers “send us a tender pack”, which is exactly what this buyer does not have yet.

So the page answers per lot, in the hero, off three facts a developer already knows: how many lots, what road the plan needs, and what the ground is doing. That is the number that goes into the feaso, and it is the number the estimator quotes on the phone. The page also does the harder half: it says plainly, right beside the result, that authority headworks, connection and contribution charges are not in the figure and that no figure for them appears anywhere on either page.

This is a business-to-business page in business hours, so the call and the form share the work: the call leads on a phone, the form leads at desktop, and the form asks for exactly the six things a callback needs.

Why this converts

The call button sits directly under the headline because this reader is a development manager with a bank deadline, and a fifteen-minute call is faster than any form — so the form is the second door, not the funnel. The estimate sits beside the headline rather than three screens down, which means the answer to “what will the civils cost” is visible before the visitor has decided whether to keep reading, and it is a real per-lot number rather than a range with an asterisk. The form asks for six things — name, phone, company, suburb, lot count, and where the project is up to — and nothing more, because those six are exactly what an estimator needs to ring back with a rate, and every extra field on a B2B form is a reason to close the tab. The trust strip is three claims a competitor cannot copy without changing how they quote: a number before there is a drawing, own plant out of the yard, and authority charges passed through at cost on the authority’s own paperwork. And the estimate volunteers the thing that costs the seller money — twenty lots is cheaper per lot than four, so stage bigger — then immediately corrects it with the total, which is the sentence that makes a developer believe the rest of the page.

The proof element — the per-lot civil estimate

Three controls, one card: lots (4 / 8 / 12 / 20), road treatment (shared driveway, new cul-de-sac road, or a full road with kerb, path and lighting) and ground (sand, mixed, rock and clay). The card answers with a per-lot figure set large and the whole-job total underneath it at body size.

Why the per-lot figure is the hero and the total is secondary

Because that is the cell in the spreadsheet. A feasibility model is built per lot — land cost per lot, civils per lot, holding cost per lot, revenue per lot — and a developer comparing two sites compares per-lot rates, not totals. Leading with a total would be leading with a number that cannot be compared to anything, and it would also be the frightening number: $982,800 reads as a different business from $49,140 a lot, and they are the same job. The total is still printed, in the same card, one line down, because hiding it would be the trick.

The arithmetic

A per-lot base by road treatment, a ground factor, and a lot-count factor that spreads mobilisation, rounded to the nearest $10. Money is held in integer units — the base in whole dollars, both factors in hundredths, divided once at the end — so no cell can land on a floating-point remainder:

per lot = round(base × ground/100 × lots/100 ÷ 10) × 10
total   = per lot × lots

base    shared driveway 18,500 · cul-de-sac 31,000 · full road 42,000
ground  sand 100 · mixed 112 · rock and clay 130
lots    4 → 110 · 8 → 100 · 12 → 95 · 20 → 90

Thirty-six cells, checked by a node script before a line of the page was written and re-checked against a second, independently written integer implementation. All 36 agreed to the dollar. Five things were proven over the whole domain rather than at sample cells:

  • The per-lot rate falls at every step up in lot count, on every road and every ground.
  • The total still rises at every step up — the job never gets cheaper overall.
  • Shared driveway < cul-de-sac < full road, at every lot count and ground.
  • Sand < mixed < rock and clay, at every lot count and road.
  • The cheapest cell on the whole domain is $16,650 a lot (20 lots, shared driveway, sand), and that is exactly the “from” price in the services grid. The dearest is $60,060 a lot (4 lots, full road, rock and clay). Both were swept for, not read off a cell chosen by hand.
The whole domain: per lot, with the whole-job total beneath it. Ex GST, Sandover’s civil works only.
Road & ground4 lots8 lots12 lots20 lots
Shared driveway · sand$20,350
$81,400
$18,500
$148,000
$17,580
$210,960
$16,650
$333,000
Shared driveway · mixed$22,790
$91,160
$20,720
$165,760
$19,680
$236,160
$18,650
$373,000
Shared driveway · rock and clay$26,460
$105,840
$24,050
$192,400
$22,850
$274,200
$21,650
$433,000
Cul-de-sac · sand (rest state)$34,100
$136,400
$31,000
$248,000
$29,450
$353,400
$27,900
$558,000
Cul-de-sac · mixed$38,190
$152,760
$34,720
$277,760
$32,980
$395,760
$31,250
$625,000
Cul-de-sac · rock and clay$44,330
$177,320
$40,300
$322,400
$38,290
$459,480
$36,270
$725,400
Full road · sand$46,200
$184,800
$42,000
$336,000
$39,900
$478,800
$37,800
$756,000
Full road · mixed$51,740
$206,960
$47,040
$376,320
$44,690
$536,280
$42,340
$846,800
Full road · rock and clay$60,060
$240,240
$54,600
$436,800
$51,870
$622,440
$49,140
$982,800

The rest state — the state the page loads in, and the state the preview thumbnail captures — is eight lots on a new cul-de-sac road in sand: $31,000 a lot, $248,000 for the job.

Why the falling per-lot rate is stated in the customer’s favour, and then corrected

Twenty lots costs less per lot than four — $16,650 against $20,350 on the same sand and the same shared driveway, and between $3,700 and $10,920 a lot across the whole domain. That is a real argument for staging bigger, and it is an argument against the contractor’s own short-term interest, because a four-lot job at $20,350 a lot is a better margin per lot than a twenty-lot job at $16,650. It is on the page, in the hero, because a developer who reads it learns that the quote is a model rather than a mood.

But half a truth reads as a sales trick the moment somebody checks it, so the same paragraph carries the correction: the total still rises. Twenty lots of that same work is $333,000 against $81,400. Cheaper per lot, dearer overall, and both halves are true of all 36 combinations — swept, not asserted. The note under the ladder is written against whatever job is currently selected, so the two figures it compares are always cells of the same model rather than a hand-typed slogan.

The drawn subject

A subdivision plan, drawn in SVG and rebuilt on every change: the existing street as a sealed band down the left, the new access running east off it, and the lots as numbered parcels in two rows either side. The lot count sets the number of parcels and their width; the road treatment redraws the access itself — a hatched limestone accessway with no kerb, a sealed cul-de-sac with a turning head, or a full road with kerbs both sides, a verge, a footpath and street lighting on a pole every second lot. The subdivision boundary is drawn in survey magenta, dashed, and deliberately open where it meets the existing street.

Under the plan is a section of the ground: a roadbase pavement layer sitting on what the machines have to get through. Sand is clean; mixed carries clay lenses; rock and clay carries the lenses plus caprock. It is the only part of the drawing that answers the ground control, and it is the part of the job that decides whether the 30% is real.

Second read: when a small stage does not use the whole parent lot, the leftover ground is labelled balance of the parent lot rather than left as dead space — and the label only appears when there is actually room for it, measured before it is drawn. At four lots the drawing shows a small stage on a large piece of land, which is the honest picture of why four lots carry more mobilisation each.

What this page declined to publish

Every figure below was considered and left off both pages, because none of them belongs to this contractor. The page states the mechanism instead — who sets the charge, who pays it, and that it reaches the developer at cost with the authority’s own paperwork attached. It names the charges a subdivision typically attracts in words, and stops there.

  • Any Water Corporation charge — headworks, contribution, connection, or a metered service. Named in words on the page, never as a number.
  • Any Western Power charge — connection, design, subsidy or capital contribution.
  • Any local-government development contribution, contribution rate, scheme figure or headworks rate.
  • Any approval timeframe for subdivision, engineering drawing approval, clearance or practical completion — the page will not say how many weeks anybody else takes.
  • Any specification clause number for road pavement, drainage, sewer or water works, and no Australian Standard number anywhere.
  • Any compaction, test-density or geotechnical acceptance figure quoted as though it were a requirement.
  • Any accreditation or prequalification claim. The contractor is fictional; asserting a real registration would be a false claim about a real scheme. The footer carries an ABN placeholder pattern and a plain sentence about insurance, with no insurer and no sum.
  • Any local-government schedule-of-rates figure. Minor works are priced against the council’s own schedule, so the tile that covers them carries a sentence instead of a price — it is their schedule, not Sandover’s.

What is published is the fictional contractor’s own civil rates and nothing else: the 36-cell per-lot table, the 30% ground factor, and a $0 feasibility call. That is the whole of it. The phone number uses the reserved (08) 9000 0xxx pattern, the address is suburb-level, and the ABN is a placeholder pattern.

Palette

Perth sand, roadbase, bitumen and survey magenta — what is actually on the site, and deliberately not the safety-orange-and-charcoal every civil contractor’s website defaults to. Every hex and every ratio below is quoted from the same row of the same run of the wave’s contrast sweep, taken after the last palette change. No colour was minted during this build — the drawing, the map and the interface are all painted from tokens the sweep already declares.

Paper #F1F2F0Page ground. Body ink reads 15.45:1 on it.
Panel #E1E4E1Cards, alternating bands, and the map’s land fill. Ink 13.54:1.
Ink #181B1A15.45:1 on paper, 13.54:1 on panel. Also every label drawn on sand (12.38:1), roadbase (10.16:1), kerb (14.78:1) and verge (11.78:1).
Muted #4B5150Secondary copy. 7.21:1 on paper, 6.32:1 on panel — and declared on nothing else, so it never enters the drawing.
Magenta ink #8E1750The per-lot figure and every link. 7.83:1 on paper, 6.86:1 on panel. Doubles as the CTA hover ground with paper on it at 7.83:1.
Survey magenta #A81B5BCTA ground (paper reversed 6.29:1), selected chips, and the dashed subdivision boundary — as a mark it reads 5.04:1 on sand, 6.02:1 on kerb, 4.79:1 on verge.
Band #20282BDemo band, footer and the mobile sticky bar. Paper reversed 13.36:1.
Rule #C2C6C2Hairlines only. 1.54:1 on paper, 1.35:1 on panel.
Field #6A706EInput and chip borders. 4.50:1 on paper as a mark.
Sand #E3D9C4The plan sheet’s ground. Ink 12.38:1, magenta ink 6.28:1, survey magenta 5.04:1, edge ink 7.27:1.
Roadbase #C9C6BEThe hatched accessway, the pavement layer and the clay lenses. Ink 10.16:1.
Seal #5C6163Bitumen: the existing street and every new carriageway. Paper reversed 5.59:1 — which is why the road name is set in paper, not ink.
Kerb #EDEDE9Kerb strips, footpath and the lot parcels. Ink 14.78:1, so the lot numbers sit on it.
Verge #CFD8C2The verge the footpath and the light poles sit in. Ink 11.78:1.
Edge ink #3D4244Every road-reserve edge and lot boundary. 7.27:1 on sand, 5.97:1 on roadbase, 8.68:1 on kerb — and never on the seal, which is why the reserve edges are drawn on the kerb side.
Kerb line #9AA09BHairline only, never text: 1.56:1 on roadbase, 1.90:1 on sand, 2.35:1 on the seal, 1.81:1 on the verge.
Map water #C1D0D5With the coast at #4C6A72, 3.67:1 on it. No suburb label is ever set over water — asserted, at both widths.
Chip sub-label #E7D4DBPaper at 86% on survey magenta, blending to this. 4.99:1 — the tightest declared pair in the whole wave, so it never goes below 86%. On the hover ground it blends to #E3D3DA at 6.11:1.
Sheet note #41413CInk at 80% on sand, blending to this. 7.33:1 — the drawn annotations and the balance-of-the-parent-lot label.

Type

Chivo 600/700 carries the headings and every figure. It is a grotesque with real weight in the bowls, so the per-lot figure holds its shape at the 56px it runs at without looking inflated, and its numerals are wide enough that $60,060 and $16,650 are instantly different shapes rather than two similar smudges. Source Sans 3 400/500 does the body at 17px on a phone.

The one typographic move is the pairing the concept asked for: the per-lot figure is the hero and the total sits under it at body size. The figure and the words “a lot” share a baseline, and the total is a single sentence directly beneath — so the eye reads the comparable number first and the frightening one second, in that order, every time. That is the order the feasibility spreadsheet is built in.

Technique

One file, all CSS and JavaScript inline, no libraries, no raster images, about 78 KB. Every visual is drawn: the subdivision plan, the service-area map, the phone icon, the favicon.

The layout

The DOM order is the mandated mobile stack — masthead, hero, services grid, estimate. At 960px and up a single CSS grid with named areas moves the estimate into the hero’s second column and drops the services grid underneath, without moving a single element in the markup. The reading order stays honest for a screen reader, and the preview thumbnail gets the answer instead of a menu of tiles.

The animation

The plan rises once on load, driven by performance.now() wall-clock deltas rather than a per-frame accumulator, and it pauses if the tab is hidden. That distinction matters more than it sounds: a headless renderer services animation frames only when one is requested, so a per-frame delta can leave a preview taken seven seconds in photographing a page that thinks it is half a second old. The entrance latches done on completion, so a resize — which is what taking a clipped screenshot does — can never re-enter it. Under prefers-reduced-motion the entrance is skipped and the drawing is placed final on the first paint. After that the page is completely at rest: no loops, no timers, no mutations.

The drawing

The plan is laid out from four numbers — the lot column count, a lot width, the end of the lots and the end of the road — and everything else is derived. Lot width is min(120, span / columns), so twenty lots pack narrow and four lots stay a sensible parcel rather than stretching to fill; the cul-de-sac reserves 98 units for its turning head, so the head can never run off the sheet. The road and its head are one open SVG path (SVG fills an open path as if it were closed), which means the junction with the existing street is filled but never stroked.

Every stroke lands on a fill the contrast sweep declares for that token. Edge ink is not declared on the seal, so the road-reserve edges are drawn on the kerb side of the boundary and the kerb line is inset far enough to sit wholly on the bitumen. The road name is ink on the roadbase accessway and paper on a sealed road, set inline in JavaScript rather than in a stylesheet — a CSS rule silently discards an SVG presentation attribute, and this element legitimately changes colour with the control.

The map

A coastline path over a panel-coloured land fill, eight suburbs as ink dots with the yard as a square. It carries no zone bands and no drive-time rings, because the service area genuinely is “the metro out of one yard” and drawing rings would imply a pricing rule the estimate does not have. Labels are ink on the land fill at 13.54:1 and never sit over water.

The three passes

Each pass drove the estimate and the enquiry form in a real browser and asserted the output. The harness reimplements the rate model independently — it never imports the page’s own function — and finished at 121 browser assertions and 55 file-level checks, all passing, across all 36 priced cells, all 12 lot-count × road combinations of the drawing at two widths, and four form drives.

Pass 1 · Craft

A dollar sign truncated the entire page, labels rendered at 8.4px, and the ground bar was invisible

The first defect was invisible to every DOM assertion and would have shipped a completely inert page: the build step that inlines the script used a string replacement, and String.replace treats $' in the replacement as “everything after the match”. The script contains return '$' + v.toLocaleString(...), so the page was silently cut off mid-string at that exact character and everything after it duplicated. The markup rendered perfectly, the hero looked right, and the whole estimate was a parse error. Fixed with a function replacer, and the build now extracts the inlined script back out and runs node --check on it every time.

Then the rendered page was looked at rather than asserted, and three things were wrong. (a) The plan’s drawn labels were sized in sheet units against a fixed 680-unit viewBox, so at 1200 wide they rendered at 8.4 CSS pixels — legible in the source, unreadable on screen. Every drawn type size was re-cut and a standing assertion now measures the rendered pixel size at both widths and fails below 9px. (b) The lot width was capped at 96 units, which left between 150 and 430 units of empty sand at low lot counts — the drawing looked like a mistake rather than a small stage. The cap went to 120 and the leftover ground is now labelled balance of the parent lot, but only when the measured room exceeds 200 units, so it never appears where it would not fit. (c) The ground bar was a sand rectangle drawn on a sand ground: an outline around nothing. It was rebuilt as a proper section with a roadbase pavement layer over the ground, which is also what made the ground control worth having.

Last: at high lot counts the magenta boundary dashes ran straight through the north point. The north point is now drawn last, on its own sand patch, so the two never collide at any lot count.

Pass 2 · Depth

The sentence that costs the seller money was a slogan; now it is computed against the job in front of you

Driven first across the entire 36-cell domain in the browser: every cell’s window.__civilcontractor figures, the displayed per-lot and total strings, the on-scene price note, the four ladder cells and the accessible text mirror were compared against the independent oracle. 36 of 36.

The staging note originally read like a slogan — “staging bigger is cheaper per lot, not cheaper overall” — which is a claim rather than an argument. It now writes itself against whatever job is selected: at eight lots on a cul-de-sac in sand it reads that twenty lots of the same work is $27,900 a lot against $31,000 here, but the whole job goes from $248,000 to $558,000; at twenty lots it inverts and shows what four lots of the same work would cost. Every figure in it is a cell of the same licensed model, and the harness now checks the note’s content on all 36 cells rather than checking that a note exists.

The lot-count ladder gained the whole-job total under each per-lot rate, so both claims are visible in one glance instead of one being a promise about the other. And the estimate now hands off to the form: Send this job to the estimator fills the lot-count field from the current estimate and jumps to the enquiry. It only ever fires on an explicit click, never on load — a form that arrives pre-filled makes an “empty submit” partially valid and quietly weakens its own validation, which is a defect the empty-submit probe cannot see.

Micro-interactions: the chips are a real radiogroup with a roving tabindex and arrow-key navigation, verified end to end — ArrowRight on the 8-lot chip moves selection and focus to 12 lots, re-roves the tabindex and reprices the job to $29,450; ArrowLeft steps back.

Pass 3 · Hardening

A map label collided only on a phone, and a harness that could not see it

The map labels are sized in map units and bumped at phone widths so nothing renders at four pixels — and that bump is exactly what made Neerabup — the yard 243 units wide at 375, running straight through Ellenbrook. Every state assertion was green; the two labels were simply printed on top of each other. Shortened to Neerabup (yard) and every pair of the nine labels is now checked for overlap at 375 and at 1200.

Finding it needed a harness fix first. The label audit used getBBox, which ignores an element’s own transform — so the rotated INDIAN OCEAN label reported as 41 units off the left edge of a map it sits comfortably inside, while a genuine collision elsewhere was reported at the same weight. The audit now measures painted screen rectangles, which is what the reader actually sees.

The other standing checks: every drawn element in the plan — every lot, number, kerb, pole, rock and dashed boundary — is measured against the 680×404 sheet across all twelve lot-count and road combinations at 375 and at 1200, and every lot number is checked to sit inside its own parcel (132 numbered lots per width, zero escapes). The phone field’s pattern is compiled with the v flag in the checker, because that is how a browser compiles it and unescaped brackets are legal in Node and fatal in Chrome; the form is then driven three ways — empty, junk in the phone field, and good data — and the junk drive confirms the browser’s own checkValidity rejects it, which is the only proof the attribute is alive. A rapid-fire drive fires ten control clicks in one evaluation with no settle between them and asserts the state, the price and the rebuilt drawing all land together.

Chanel rule: the service-area map carried two dashed latitude lines that divided nothing and were labelled nothing. Gone. Final state: no console errors or failed requests on either page, no horizontal overflow at 375, 768 or 1440 with nothing crossing the viewport box, exactly one h1 per page, no curly quotes in any attribute, [hidden] guarded with display:none !important, reduced motion settling on the first paint, and the probe figures byte-identical between the 7-second thumbnail instant and 15 seconds.

Local SEO we would implement for the real business

This demonstration page deliberately emits only WebPage, CreativeWork and BreadcrumbList structured data, and it names itself a demonstration. Putting a fictional contractor into a real search index with LocalBusiness markup and invented reviews would be dishonest markup, so the schema we would ship for a real operator is shown here as a sample only. It is not live on either page.

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "GeneralContractor",
  "name": "<real trading name>",
  "image": "https://<domain>/yard.jpg",
  "telephone": "+61 8 <real number>",
  "address": {
    "@type": "PostalAddress",
    "streetAddress": "<real street address>",
    "addressLocality": "Neerabup",
    "addressRegion": "WA",
    "postalCode": "6031",
    "addressCountry": "AU"
  },
  "geo": { "@type": "GeoCoordinates", "latitude": "<lat>", "longitude": "<lng>" },
  "areaServed": [
    { "@type": "City", "name": "Perth" },
    { "@type": "AdministrativeArea", "name": "Peel" }
  ],
  "openingHoursSpecification": [{
    "@type": "OpeningHoursSpecification",
    "dayOfWeek": ["Monday","Tuesday","Wednesday","Thursday","Friday"],
    "opens": "06:00", "closes": "16:00"
  }],
  "hasOfferCatalog": {
    "@type": "OfferCatalog",
    "name": "Subdivision civil works",
    "itemListElement": [
      { "@type": "Offer", "itemOffered": { "@type": "Service", "name": "Subdivision roadworks" } },
      { "@type": "Offer", "itemOffered": { "@type": "Service", "name": "Stormwater drainage" } },
      { "@type": "Offer", "itemOffered": { "@type": "Service", "name": "Sewer and water reticulation" } }
    ]
  },
  "review": [{
    "@type": "Review",
    "author": { "@type": "Person", "name": "<from the live reviews feed>" },
    "reviewRating": { "@type": "Rating", "ratingValue": "<from the feed>" }
  }],
  "aggregateRating": {
    "@type": "AggregateRating",
    "ratingValue": "<from the live reviews feed>",
    "reviewCount": "<from the live reviews feed>"
  }
}
</script>

Alongside it: a Google Business Profile on the real yard with photographs of the actual plant, the four service corridors turned into four indexable suburb sections rather than one page, and the FAQ marked up as FAQPage — those six questions are the ones the trade is actually asked, so they earn the markup. The review and aggregateRating blocks are filled from the live feed, never typed by hand. The three quotes on the demonstration page are written copy and are deliberately not marked up as anything.

What would change for the real client

  • Their rate model, behind the same three controls. The estimate’s shape is the deliverable; the numbers come from the client’s own completed jobs, and the sensible build reads them from a spreadsheet the estimator already maintains rather than hard-coding them into a web page.
  • Civil Pro for the contract admin. Variations, progress claims and the ITP register live there, and the “you get the survey, the volume and the rate the day the machine finds it” promise on the page is only worth making if that is where the variation is raised.
  • HammerTech for site and ITP records. The as-constructed and compaction records the page promises at practical completion come out of it, so the claim and the paperwork match.
  • Their photographs, not our drawing. Real plant and real completed stages replace the illustrative panels — but the drawn plan stays, because it changes with the controls and a photograph cannot.
  • The form into the estimating inbox, with a same-day SLA. Six fields is the right number precisely because it is what a callback needs; adding a seventh is how a B2B form starts leaking.
  • Their referees, pulled live. The three quotes here are demonstration copy. On a real build they come from the client’s reviews feed and the rating block above fills itself.
  • Authority charges stay off the page permanently. That is not a demonstration constraint; it is the right call for the real client too. Publishing a headworks figure that moves is how a page ages into a liability, and refusing to is the thing developers remember about it.