Fourteen Access — build notes

Generative Assets · site 404 · industry demonstration

A ramp drawing that can’t lie about its gradient.

Fourteen Access is a fictional Bayswater access-ramp and home-modification business. Its landing page has one job: turn “can you put a ramp here” into a booked measure-up — and, where a compliant ramp will not fit, say so on the page rather than at handover. Everything below is how that was built.

The concept

The trade sells a number the customer cannot check: how long a ramp has to be. AS 1428.1-2009 sets 1:14 as the steepest gradient a ramp may be built to, so a 480 mm verandah needs 6.72 m of ramp and, with a 1,200 mm landing at each end, 9.12 m of ground. Most people picture three metres. The gap between those two numbers is where the trade’s arguments live.

So the signature is one thing: a true-aspect elevation. Equal scale in both axes, always. A ramp drawing with a stretched vertical axis makes 9 metres look like 3, and it is the reason people are surprised by the quote and surprised again by the fence they have lost. The page draws the rise, the run, the landings and the handrails at one scale, both ways, against a hard vertical rule marking the setback the customer actually has — and when the ramp runs past that rule, it is drawn running past it, not merely announced.

The audience is older people and their families, so the body copy sets at 17px on mobile rather than 16, every tap target clears 44px, every control carries a real label, and every SVG state has a plain-text mirror beside it. On a site about access, the accessibility floor is the product.

Palette

Argued from the standard itself. AS 1428.1 exists to demand luminance contrast at a step edge, so the boldest colour on the site is the one that only ever marks an edge — and never carries a word. All ratios below are WCAG-computed from the shipped hexes, measured in Pass 1 against the tokens actually in the stylesheet.

Oat paper#F4F0E8
Page ground. Warm-grey rather than cream, which keeps it off the cream-plus-serif default.
Warm ink#231F1C
Body ink. 14.39:1 on paper · 16.35:1 on cards · 11.65:1 on the hairline tint.
Aubergine#5B2A4C
Ramp structure, handrails, dimension lines, primary CTA. 9.83:1 on paper, 11.17:1 on cards, 7.96:1 on the hairline tint; white on it, 11.17:1.
Nosing yellow#E4B93B
Lines and marks only — never text, anywhere. Nosings, kerb rail, tactile indicators.
Soft ink#5F564E
Secondary copy and captions. 6.31:1 on paper, 7.17:1 on cards, 5.11:1 on the hairline tint.
GRP decking#C9C3B6
Aluminium ramp deck fill. Body ink on it: 9.32:1.

The never-text token, measured

The nosing yellow was pinned NEVER-TEXT at spec time and the measurement is the argument for it. Against every fill it can land on, #E4B93B measures:

Ground it can land onNosing yellowBody ink #231F1CAubergine #5B2A4C
GRP decking #C9C3B61.069.326.36
Concrete #D3CFC61.2010.527.18
House wall #E6DFD21.4012.348.43
Lawn #B7BE9C1.048.465.77
Aluminium rail #C4C7C91.099.626.57
Oat paper / sky #F4F0E81.6314.399.83

It fails everything, which is the point: it is a marking colour, not an ink. The aubergine clears 4.5:1 on every scene fill on the page, so it is the one accent allowed to label the drawing as well as draw it. Map labels use the body ink with an oat halo (paint-order: stroke); the lines-only token never labels a map.

Type

Outfit 600/700 for display — geometric, wide apertures, friendly, and legible at distance for an older reader. Figtree 400/600 for body. DM Mono 500 in exactly one role: dimensions and figures (480 mm · 1:14 · 9.1 m), so a number on the drawing and a number in the ledger are visibly the same class of thing.

Body sets at 17px on mobile and 18px from 900px up. That is a deliberate half-step above the collection’s usual 16px floor and the page can defend it: the primary reader is often over seventy, frequently reading on a phone held at arm’s length, and often reading it because someone is coming home from hospital. Line length is capped near 56 characters and line-height sits at 1.62.

The signature: the Rise

One inline SVG, drawn in real CSS pixels rather than a fixed viewBox, so labels stay a legible 11–12.5px at every width instead of scaling down with the frame. A single scale factor s (pixels per millimetre) is applied to both axes — that is the true-aspect guarantee, and it is asserted rather than assumed:

worldW = 1500 (house) + frame + 500 (apron)
worldH = rise + 2340 (wall) + 640
s      = min(boxW / worldW, boxH / worldH)     // one scale, both axes
X(mm)  = ox + mm * s
Y(mm)  = oy - mm * s

The frame view-fits to max(footprint, available length, 4,140 mm), so the ramp and the setback rule are both always in view. Over the whole control grid the frame spans 4.14 m to 26.4 m: 4,140 mm is the smallest ramp footprint that exists (a 200 mm rise at 1:14, folded as a switchback), and 26,400 mm is the largest (1,200 mm of rise at 1:20, straight). Because the available-length stepper bottoms out at 2.0 m, the 4,140 mm floor is what makes 4.14 m the true minimum in every reachable state, including the flat regimes.

Drawn quantities

  • The deck profile is a polyline in world millimetres: 1,200 mm top landing, then each flight and each intermediate landing, then the 1,200 mm bottom landing. A switchback folds it — out and down to the 1,540 mm turn landing, then back and down to the ground.
  • Handrails draw at 1,000 mm and 700 mm above the walking surface with posts on a spacing derived from the run, so the rail geometry shrinks with everything else instead of staying a constant number of pixels.
  • Nosings, the kerb rail and the tactile ground surface indicator at the bottom landing are the only things drawn in #E4B93B. Nosing ticks are suppressed below 5 px of spacing rather than degenerating into a solid band.
  • A 1 m scale bar sits bottom-left and swaps to a 5 m bar when a metre falls under 26 px — the constant reference that makes the true-aspect claim checkable by eye.
  • A small aubergine marker travels up the ramp toward the door on a 6.2 s loop, never dropping below 0.55 opacity. It is the liveness floor for the 7-second thumbnail settle, and it is the second-read detail: the direction of travel is the direction that matters.

Below 190 mm, the drawing zooms

A 20 mm sill on a 4.14 m frame is 0.5 % of the width. Squinting at it would be dishonest in a different way, so below 190 mm of rise the elevation swaps to a detail: the threshold or step ramp drawn at true aspect, with a small key thumbnail showing the whole elevation and a marker rectangle where the detail sits, and a caption band carrying the regime’s own rule. The detail prints its own multiplier rather than claiming one.

The concept pinned that detail at 1:5 — five times the elevation’s scale. Neither regime lands on it, for opposite reasons, and the shipped code says which is which. The scale starts at ×5 of the elevation, is raised where ×5 would leave the ramp too small to read, and is lowered where the ramp and the context either side of it would not fit the frame:

ViewportStep ramp, 180 mmOn screenThreshold ramp, 20 mmOn screen
375 px×3.923.06 m×20.570.58 m
1000 px×3.923.06 m×7.081.69 m
1200 px×3.923.06 m×8.661.39 m

The 180 mm step ramp is 1,800 mm long at 1:10 — long enough that ×5 would run it and its context off both edges of the frame, so the zoom backs off to ×3.92 and the whole ramp stays in view. It holds that figure at all three widths, because the constraint that sets it is the frame’s own width. The 160 mm threshold ramp goes the other way: 160 mm long by 20 mm high, a sliver eight times longer than it is tall, and at ×5 of a 26 m frame it is four pixels of ramp — exactly the case the zoom exists to fix. So it zooms past ×5 instead. Neither number is claimed anywhere; both are printed on the drawing that produced them.

“On screen” is the second half of the same honesty, and it is deliberately a different quantity from the clamp. The zoom is clamped so that the ramp plus a band of context either side — floored at 180 mm — always fits; that clamp is a floor on how much world stays visible, not a description of the finished drawing. Where the frame’s width is the binding constraint, the two coincide: the 180 mm step ramp shows exactly its 3.06 m clamp at every viewport. Where a different constraint binds, they part. The 20 mm threshold is scaled by the frame’s height — its drawing is 1 m = 523 px at all three widths — so a wider viewport simply shows more ground either side of the same sliver, and the extent runs 0.58 m, 1.69 m, 1.39 m. The frame note prints that extent, measured as the drawing box divided by the drawn scale, so the two figures it carries multiply back to the box you are looking at. It is viewport-dependent because the drawing is: this page lays out in real CSS pixels at every width rather than scaling one fixed canvas, so the honest answer to “how much of the world am I looking at” is a different number at 375 than at 1200.

The model — integer millimetres, whole dollars

Every quantity in the page is an integer. Money is whole dollars by construction (every rate is a whole dollar and every multiplier a whole number), lengths are whole millimetres, and Number.isInteger() is asserted on every field the price function returns, not just on the total. The independent oracle enumerated the full grid — 62,640 cells — and found 0 non-integer cells.

run          = rise x gradient denominator
flights      = maxFlightRise ? ceil(rise / maxFlightRise) : 1      // 1:20 -> 1
intermediate = flights - 1
landingLen   = straight 1,200 mm  ·  switchback 1,540 mm
travel       = run + intermediate x landingLen + 2 x 1,200
footprint    = straight: travel  ·  switchback: ceil(travel / 2) + 1,540
fits         = footprint <= available length

max flight rise:  1:14 -> 643 mm (9.0 m)   1:16 -> 750 mm (12.0 m)   1:20 -> no limit

threshold ramp (rise <= 35)  -> 1:8   ·  length = rise x 8   ·  $340
step ramp (rise 40-190)      -> 1:10  ·  length = rise x 10  ·  $890
     (the standard's 1,900 mm ceiling is never reached: the rise stepper's
      tallest step-ramp state is 180 mm -> 1,800 mm)
ramp:
  runM     = ceil(run / 1000)                      // billed to the whole metre
  landings = intermediate x (switchback ? turn : landing) + 2 x landing
  subtotal = $680 set-up + runM x material rate + landings + runM x $240 handrails
total = max(subtotal, $650 minimum)

Worked figures, re-derived independently

Full control vectorGeometryTotal
480 mm · 10.0 m · 1:14 · straight · aluminium · front (rest state)6.72 m run · 1 flight · 2 landings · 9.12 m footprint · 7 billed m$10,120
480 mm · 10.0 m · 1:14 · straight · concrete · frontsame geometry, concrete rates$13,370
20 mm · 2.0 m · (gradients disabled) · straight · aluminium · frontthreshold ramp, 160 mm long at 1:8 — prices $340$650
160 mm · 11.0 m · (gradients disabled) · straight · aluminium · side pathstep ramp, 1,600 mm at 1:10$890
480 mm · 11.0 m · 1:16 forced · straight · aluminium · front7.68 m run · 10.08 m footprint · 8 billed m$11,140
1,160 mm · 14.5 m · 1:20 · switchback · concrete · rear (dearest)23.2 m run · 24 billed m · 14.34 m footprint$37,000
1,200 mm · 14.0 m · any gradient · straight · any material · any doorrefusal, form 1 — the switchback at 1:16 fits in 13.11 mno price
1,200 mm · 6.0 m · any gradient · either form · any material · any doorrefusal, form 2 — nothing compliant existsno price

The cheapest reachable output is $650, which is exactly the services grid’s “from $650” floor — the $650 visit minimum binds on the $340 threshold ramp and nowhere else, in 348 enumerated cells (1,044 with the price-neutral door axis expanded). Every line item on this page sits inside that minimum: it is a floor on the whole job, never an addition.

The refusal is the product

Four of the five sites in this wave carry a designed refusal. This one carries two, because the honest answer splits: sometimes there is a way out and sometimes there isn’t, and running them together would either raise false hope or hide a solution.

Form 1 — a computed way out

At 1,200 mm of rise with 14.0 m available, straight: “A straight ramp for 1,200 mm needs 20.4 m of ramp and landings at its steepest allowed gradient, and you have 14.0 m. Folded as a switchback at 1:16 it comes to 13.1 m — that fits. Switch the form above and we’ll draw it.” The form chip is never forced: the page computes the way out and offers it, it does not silently redraw something the customer did not ask for.

Form 2 — nothing fits

At 1,200 mm with 6.0 m: “1,200 mm of rise needs 20.4 m straight, or 11.9 m folded as a switchback. You have 6.0 m. There is no compliant ramp for this house, and a non-compliant one fails at handover and doesn’t get funded. What you want is a platform lift — we don’t install them, and we’ll tell you who does.”

In both states the badge prints a pinned bare string with no dollar figure anywhere — not in the badge, not in the ledger, not in the hero readout, not in the plain-text mirror. The harness asserts the absence of a dollar sign on all four surfaces in both forms. There is no price because there is nothing we would build.

Forcing, memory and the partial unwind

The gradient chip is capped by either of two controls: the available length (a gradient whose footprint exceeds it is disabled with a computed reason), or the rise itself (below 190 mm the regime changes and the gradient chips do not apply at all). The force order is flattest valid first — 1:20, then 1:16, then 1:14 — because a flatter ramp is the better outcome and a force must never quietly pick the steepest thing that fits. The remembered gradient is restored the moment it becomes valid again, and the memory is cleared by any explicit pick made while the chip set is free.

The eight-step chain below is driven in full by the harness, and every step is asserted on the state probe:

#ActionResult
1480 mm · 14.0 m · straight · pick 1:20valid — 12.0 m footprint fits
2available → 11.0 mforced 1:16 (1:20 needs 12.0 m) · memory = 1:20
3rise → 160 mmstep-ramp regime, all gradients disabled, 1:10 at 1,600 mm
4available → 14.0 mholds the step ramp — the length constraint lifted, but the rise regime still forbids every gradient, so the memory is held rather than restored
5rise → 480 mmrestores 1:20 · memory cleared
6rise → 1,200 mmrefusal — no straight gradient fits 14.0 m
7form → switchbackforced 1:16 at 13.11 m folded
8available → 6.0 mrefusal — nothing fits in either form

Step 4 is the partial unwind, and it is the one a builder would get wrong by accident: the constraint that caused the force has lifted, but a different constraint now stands, so the correct behaviour is to hold — not to step back through intermediates and not to restore a value that is still invalid.

Where our oracle disagreed with the spec

The concept’s figures came from a spec-time enumeration. Every one of them was re-derived here in a separate 139-assertion oracle written from the standard and the model, not copied from the document. Every pinned endpoint reproduced: the rest state, both refusal forms, the $650 floor, the $37,000 ceiling at exactly its pinned control vector, and the 4.14 m / 26.4 m frame extremes. Seven things did not. One of them — the first — turned out to be an error in the spec and has been corrected in the shipped page. The other six are reported as they were found, and neither side was quietly changed.

Seven findings — one corrected, six reported

  1. The step ramp’s gradient — raised at build time, checked, and corrected. The page now says 1:10. The spec pinned 1:8 for both flat regimes and pinned the lengths that follow from it (160 mm of rise → 1,280 mm of ramp), citing a 1,520 mm maximum length. The builder’s reading of AS 1428.1-2009 was that 1:8 with a 1,520 mm maximum length describes a kerb ramp, and that a step ramp is capped at 1:10 with a maximum rise of 190 mm. The wave lead went back to the standard independently and reached the same reading, so the spec was corrected rather than the page. A step ramp is now drawn and priced at 1:10 with length = rise × 10: 160 mm of rise is 1,600 mm of ramp, 180 mm is 1,800 mm. The 1,900 mm ceiling that 190 mm implies is never reached and is not pinned anywhere, because 190 mm is not a state the 20 mm rise stepper can produce — 180 mm is the tallest step ramp on the grid. The threshold ramp is untouched at 1:8 to 35 mm, which was never in dispute, and no price moved: both flat regimes are flat-priced, so the correction is a length and a gradient string and nothing else. The cheapest reachable output is still $650, the dearest still $37,000, and the 348 minimum-binding cells are still 348. This is the general rule the wave runs on: where a pinned figure and a real Australian Standard disagree, the standard wins and the spec gets fixed — “ship the reading that reproduces the pinned endpoints” governs arithmetic, not claims about regulation.
  2. The flat-regime strings said “step ramp” for the threshold case. The pinned disabled-reason and badge line 2 both read “AS 1428.1 lets it be a step ramp” for every rise at or below 190 mm — but the pinned badge line 1 correctly calls the 20 mm case a threshold ramp. A single reason string covering two regimes is untrue of one of them, so both strings are parameterised by regime and the page says “threshold ramp” at 20 mm and “step ramp” from 40 mm up.
  3. The cell counts use two different axis conventions. The spec quotes 29,154 priced cells and 11,162 does-not-fit cells against a 62,640-cell grid; those do not partition it. Our enumeration reproduces the 29,154 exactly with all six axes expanded — and finds that 11,162 is the does-not-fit count with the price-neutral door axis collapsed. On one convention the split is 29,154 priced / 33,486 does-not-fit = 62,640, and 11,162 × 3 = 33,486 exactly. The same is true of the minimum-binding count: 348 is the singly-collapsed figure and 1,044 is the full-grid one. Both figures are correct; only their pairing isn’t.
  4. Display rounding renders 13.11 m as 13.1 m. The spec’s own rule pins lengths to one decimal of a metre, and its refusal prose quotes the raw oracle value 13.11 m. The page prints 13.1 m; the underlying integer is 13,110 mm. Same for the 4.14 m frame minimum (4,140 mm) and the 14.34 m dearest-case footprint.
  5. 643 mm at 1:14 is a 9,002 mm flight. The pinned maximum flight rise of 643 mm exceeds the 9.0 m flight it comes from by 2 mm; 642 mm would be the floor. We enumerated it: ceil(r/643) === ceil(r/642) for all sixty rises on the 20 mm grid, so the difference is unreachable and we shipped the pinned 643.
  6. The pinned partial-unwind chain is not walkable on the shipped stepper grid. Its steps are written as discontinuous state settings — “rise 480 → rise 160” — but the page only has a 20 mm stepper, and walking down from 480 mm at 11.0 m passes through 420 mm, where the remembered 1:20 becomes valid again and is correctly restored. So the memory the chain expects held at step 4 has already been spent. Every pinned outcome still reproduces (forced 1:16 at step 2; the step ramp at 1,600 mm at steps 3–4; 1:20 at step 5; refusal at step 6; forced 1:16 at 13.11 m folded at step 7; refusal at step 8) and the harness drives and asserts all eight. Beside it we ship and drive a chain of the same shape that is reachable — 240 mm at 8.0 m, pick 1:20; drop to 6.0 m and 1:14 is forced; step down to 180 mm and the regime takes over; lift back to 14.0 m and the memory is held, not restored, because the rise still forbids every gradient; return to 480 mm and 1:20 comes back. That is the partial unwind the concept is asking for, on a route a user can actually take.
  7. The 26.4 m frame maximum is reachable, but only by one route. 1:20 straight at 1,200 mm of rise needs 26.4 m and the setback stepper stops at 16.0 m, so that pairing never fits and its gradient chip is never enabled from a straight run. It is reached by picking 1:20 while the switchback fits at 16.0 m — 14.74 m folded — and then unfolding to a straight run, at which point the page draws the full 26.4 m running 10.4 m past the setback line and refuses. The harness drives exactly that route at all three widths and asserts the 26,400 mm frame.

Three contrast figures in the spec also measured slightly differently from our own WCAG arithmetic (lawn 8.46 vs 7.68, house wall 12.34 vs 12.54, aubergine hover on white 13.66 vs 13.83). All of them clear 4.5:1 by a wide margin in both readings, so no token changed; the measured values are what is tabled above.

Conversion decisions

  • The price is in the hero and on the drawing. The rest-state total prints as a live text node in the hero strip and as a badge on the elevation, so the 1200×750 thumbnail carries the commercial argument whichever way the page is cropped. The badge docks below the artwork at 640px and sits over the sky band above it from 641px up — and its box is asserted never to intersect any drawn group at 375, 1000 and 1200, at DPR 2.
  • The signature feeds the form. A read-only field carries the drawn scenario into the enquiry — rise, setback, gradient, form, material, door and price — so a demo interaction arrives as a warm lead rather than a blank enquiry.
  • Six fields, one of them a date. Name, phone, suburb, who it is for, when it is needed, and a description. The date field exists because a hospital discharge date is the single fact that changes the order of the run sheet, and asking for it is the most useful question on the form.
  • The sticky bar owns both intents. A tel: link and an in-page anchor to the quote form, both 48px tall, in the trade’s own voice rather than a generic “Call now / Get quote” pair.
  • The refusal is above the ledger, not buried in an FAQ. When the answer is no, the panel replaces the price where the price would have been.

Local SEO we’d implement for the real business

This demonstration ships only WebPage / CreativeWork / WebSite structured data, because publishing a fictional business as LocalBusiness with invented reviews would be dishonest markup in a real index. For the real Fourteen Access — with a real address, a real registration number and a real reviews feed — this is the schema that would ship, alongside the Google Business Profile, the suburb landing pages and the AS 1428.1 explainer that would earn the informational traffic:

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "HomeAndConstructionBusiness",
  "@id": "https://fourteenaccess.com.au/#business",
  "name": "Fourteen Access",
  "description": "Access ramps, step ramps and home modifications built to AS 1428.1 across Perth’s inner eastern suburbs.",
  "url": "https://fourteenaccess.com.au/",
  "telephone": "+61 8 XXXX XXXX",
  "image": "https://fourteenaccess.com.au/img/ramp-bayswater.jpg",
  "priceRange": "$$",
  "address": {
    "@type": "PostalAddress",
    "streetAddress": "Unit 4, XX Example Street",
    "addressLocality": "Bayswater",
    "addressRegion": "WA",
    "postalCode": "6053",
    "addressCountry": "AU"
  },
  "geo": { "@type": "GeoCoordinates", "latitude": -31.9166, "longitude": 115.9166 },
  "areaServed": [
    { "@type": "City", "name": "Bayswater" },
    { "@type": "City", "name": "Maylands" },
    { "@type": "City", "name": "Morley" },
    { "@type": "City", "name": "Bassendean" }
  ],
  "openingHoursSpecification": [{
    "@type": "OpeningHoursSpecification",
    "dayOfWeek": ["Monday","Tuesday","Wednesday","Thursday","Friday"],
    "opens": "07:00", "closes": "17:00"
  }],
  "hasOfferCatalog": {
    "@type": "OfferCatalog",
    "name": "Access ramps and home modifications",
    "itemListElement": [
      { "@type": "Offer",
        "itemOffered": { "@type": "Service", "name": "Modular aluminium access ramp to AS 1428.1" },
        "priceSpecification": {
          "@type": "UnitPriceSpecification",
          "price": "780", "priceCurrency": "AUD",
          "unitText": "metre of ramp run"
        } },
      { "@type": "Offer",
        "itemOffered": { "@type": "Service", "name": "Threshold ramp, supplied and fitted" },
        "priceSpecification": {
          "@type": "PriceSpecification",
          "minPrice": "650", "priceCurrency": "AUD"
        } }
    ]
  },
  "aggregateRating": {
    "@type": "AggregateRating",
    "ratingValue": "4.9", "reviewCount": "118"
  },
  "review": [{
    "@type": "Review",
    "author": { "@type": "Person", "name": "Real verified customer" },
    "reviewRating": { "@type": "Rating", "ratingValue": "5" },
    "reviewBody": "Pulled from the live Google Business Profile feed, never hand-written."
  }]
}
</script>

The aggregateRating and review nodes above are shown for completeness and would be wired to the real reviews feed — never typed by hand, and never present at all until there are real reviews behind them.

What would change for the real client

  • Their photographs. Every visual here is procedural, which is the collection’s constraint rather than a recommendation. A real ramp business lives or dies on before-and-after photos of actual verandahs, and they would sit beside the elevation, not replace it.
  • Their job management. The enquiry would post into ServiceM8, Tradify or simPRO with the drawn scenario as custom fields, so the measure-up is booked from the diary rather than re-keyed. AroFlo suits the ones already running NDIS compliance workflows.
  • Their NDIS paperwork. A quote that has to match an OT report line-for-line is a document-generation job: the ledger above is already itemised the right way, and it would render straight to the plan manager’s required format rather than being retyped into a PDF.
  • Their real rates. Every figure here is invented but internally consistent and plausible for the WA market. On a real build the rate card comes from their last twelve months of invoices, and the estimator is rebuilt against it in an afternoon.
  • A booking calendar. Calendly or a ServiceM8 booking widget on the measure-up, with discharge-date jobs flagged for manual triage — the one case where an automated calendar should not be trusted.

Iteration log

Pass 1 — Craft

  • Measured every shipped token with WCAG arithmetic rather than trusting the spec’s figures, and found three scene ratios that differed from the document (lawn 8.46 against a stated 7.68, house wall 12.34 against 12.54, aubergine hover on white 13.66 against 13.83). All clear comfortably in both readings, so no token moved; the tables above carry the measured values.
  • Minted the soft ink #5F564E for secondary copy — the spec pinned no secondary text token, and reusing the body ink flattened the hierarchy while reusing the aubergine would have made every caption read as a link.
  • Killed a real bug: .rig.intro .part{opacity:0} in the stylesheet would have silently overridden the SVG opacity presentation attribute the entrance choreography writes — the failure class where the state is right and only the pixels are wrong. Entrance opacity is now driven by setAttribute alone, with no CSS rule touching the property.
  • Caught a second one while inlining the script: writing it into the page with String.replace mangled every $’ in the source, because $’ is a replacement pattern. The page threw a parse error and rendered a dead SVG while every piece of markup looked perfect and every probe would have reported a well-formed document. The inline step now uses slice-and-join, and the extracted inline script is node --checked on every rebuild.
  • Gave the deck mass. It was shipping as a hairline with a yellow line under it; it is now a filled wedge to the ground for a straight run, a stroked deck band at true thickness, legs under the modular version, and the walking surface as an aubergine line on top. The nosing suppression threshold moved from “total drawn length” to per-tick spacing, which was the difference between a legible dashed nosing line and a solid yellow band at the widest frame.
  • Put a ground-coloured halo (paint-order: stroke) on every label the drawing paints, and clamped the rise dimension’s label inside the frame — at 26.4 m the house is seventeen pixels wide and the label was running off the left edge.

Pass 2 — Depth

  • Added the travelling marker that runs up the ramp toward the door on a 6.2 s loop with an opacity floor of 0.55 — the liveness floor for the seven-second thumbnail settle, and the second-read detail: the direction of travel is the direction the whole page is about.
  • Split the switchback into a near leg and a far leg. As one continuous polyline the two flights and their handrails read as a grey web; drawn as an out-leg at half opacity, a 1,540 TURN landing at the far end and a solid return leg, the fold becomes legible — and the drawing now shows why folding halves the footprint.
  • Enriched the elevation with handrail posts spaced from the run, the tactile ground surface indicator at the bottom landing, and the kerb rail on the deck edge — all in the never-text yellow, all shrinking with the frame instead of holding a constant pixel size.
  • Rebuilt the flat-regime detail as a real technical detail: hatched finished floor inside, the sill face, the path outside, the key thumbnail showing where the zoom came from, and a caption band carrying the regime’s own rule instead of empty sky. The multiplier is printed, not claimed.
  • Drew the refusal instead of announcing it. When the ramp crosses the setback rule the deck is visibly drawn past it and the overshoot is dimensioned on the ground line — 10.4 m PAST THE LINE at the widest reachable state.
  • Turned the greyed-out gradient chips into information. At the rest state two of the three are unavailable, and the page now says why in the chip set’s own note: “1:16 needs 10.1 m and 1:20 needs 12.0 m for a 480 mm rise. You have 10.0 m.” Forced and restored states carry their own notes beside it, so the force is legible rather than silent.

Pass 3 — Hardening

  • Found the worst defect of the build by looking at a 375px screenshot: the header CTA was sitting on top of the wordmark’s subtitle. A box-level masthead probe passed it, because flex-shrink had narrowed the wordmark’s box while its nowrap text overflowed. The probe now measures ink — scrollWidth against clientWidth on the wordmark, plus a pairwise overlap test across every header control — and the subtitle and header CTA drop below 620px, where the sticky bar already carries both intents.
  • Found the second-worst the same way: the service-area map’s 19-unit labels in a 900-unit viewBox render at about six pixels on a phone. A second, portrait map is authored for widths below 700px with type sized for that frame, and the twenty-minute-ring caption moved out of the SVG into HTML, where it was colliding with Belmont and Victoria Park.
  • Proved the entrance cannot be re-entered by a resize: the resize path renders with skipEntrance, the animation token is bumped in the handler before any state swap, and a settle timer backs it up. A clipped screenshot changes device metrics and fires the resize handler — that is exactly the failure this gate exists for — and a standing assert drives the controls, waits with nothing poking the page, and requires every animated group opaque.
  • Drove a rapid-fire burst of twelve control clicks in one evaluate with no settle between; zero new console errors, settled and sane afterwards. Drove both form patterns through checkValidity on deliberately invalid values rather than reading the attribute. Drove the whole rig by keyboard — Enter on the steppers, Space on the chips — and asserted every field is labelled and every control named.
  • Ran a pass whose only job was re-deriving every number in body copy from the shipped model — 129 assertions across both pages, covering the FAQ’s 20.4 m and 13.1 m, the rate card, the landing sizes, the flight limits, the contrast table and the “from $650” floor against the estimator’s cheapest reachable output. It caught the last inconsistency in the build: the page was mixing straight and curly apostrophes between its markup and its script strings.
  • Corrected the one spec figure that was a claim about a real Australian Standard rather than a claim about our own arithmetic. The build had flagged it and shipped the pinned reading; on review the flag was right, and a page that names AS 1428.1-2009 by number cannot print 1:8 against a step ramp — 1:8 with a 1,520 mm maximum length is the kerb ramp. The step regime is now 1:10 with length = rise × 10, so 160 mm of rise draws and prints as 1,600 mm and 180 mm as 1,800 mm, on the badge, the disabled-gradient note, the ledger line, the hero readout, the plain-text mirror, the handoff field and the drawn detail — which is now a visibly shallower slope over a longer run, and backs its zoom off from ×4.90 to ×3.92 to keep the whole ramp in frame. The threshold ramp stayed at 1:8 to 35 mm. Not one money figure moved in either direction: the flat regimes are flat-priced, the oracle re-enumerated the full 62,640-cell grid, and the $650 floor, the $37,000 ceiling, the 29,154 priced cells and the 348 minimum-binding cells all came back identical.
  • Twenty-one class attributes in this page were written with curly quotes, and every one of them was silently dropping its styling. class=”mono” is valid HTML: the parser reads an unquoted value and hands back a class literally named ”mono”, so .mono never matches and nothing errors. All three .pass blocks in this log were rendering with no left rule and no indent, and every drawn label quoted inline in Passes 1–3 was rendering in body type instead of DM Mono. This is a class of defect worth naming: it passes every state assert, because the state is correct and only the pixels are wrong — the same shape as the entrance-opacity bug in Pass 1, and the reason a screenshot is a test. Fixed as a targeted rewrite of attribute delimiters only, leaving the typographic quotes in prose alone; a sweep of every tag in both pages found no other attribute affected, and the one remaining curly quote inside a tag is an apostrophe inside a correctly quoted og:description.
  • The frame note over-reported the detail’s width, because the note and the drawing each carried their own copy of the same constant. The zoom clamps itself so that the ramp plus a band of context either side fits the frame, with that band floored at 180 mm; the note printed the same expression with the floor written as 240 mm. Below about 690 mm of ramp the floor binds and the two diverge, so the 20 mm threshold ramp printed a 0.64 m frame against a 0.52 m clamp, and the 40 and 60 mm step ramps printed 0.88 m and 1.08 m against clamps of 0.76 m and 1.02 m. There is now one detailFrameMm() and the clamp and the note both read it — which fixed the divergence but, as the next entry records, reconciled the note to the wrong quantity. Nothing about the drawing changed: the multipliers in the table above are still ×3.92 for the step ramp and ×20.57 / ×7.08 / ×8.66 for the threshold, and the oracle re-enumerated the 62,640-cell grid with the $650 floor, the $37,000 ceiling, the 29,154 priced cells and the 1,044 minimum-binding cells all identical.
  • And the reconciled note was still naming the wrong quantity: detailFrameMm() is a floor the zoom is clamped to, not the extent the drawing ends up showing. Four constraints compete for the detail’s scale — ×5 of the elevation, a legibility minimum on the drawn ramp, the frame’s height, and the frame’s width — and only the last of them is the one detailFrameMm() expresses. Whenever a different one binds, the drawing shows more world than the clamp asks for and the note under-reported it. The 20 mm threshold is scaled by height at every width, so at 1200 px the note printed “0.52 m detail · 1 m = 523 px” over a box holding about 1.39 m — and a reader could catch it with a divide, because the two figures the note prints are supposed to multiply back to the box in front of them. The flat regimes now print boxW / scale, the extent actually on screen, which is the same quantity the ramp regime’s “m across” reports; detailFrameMm() keeps its real job as the clamp and is no longer read for display. The step ramp’s figures are unchanged, because for that state the width clamp is the binding one — 3.06 m at all three viewports, identical to the floor. The threshold’s go from a flat 0.52 m to 0.58 / 1.69 / 1.39 m at 375 / 1000 / 1200, and the table above now carries them. The multipliers did not move (×3.92, and ×20.57 / ×7.08 / ×8.66), no drawing changed by a pixel, and the oracle re-enumerated the 62,640-cell grid to the same $650 floor, $37,000 ceiling, 29,154 priced cells, 33,486 does-not-fit cells and 0 non-integer cells. The frame-width harness now asserts the printed width against the extent it measures off the drawn scale bar, and asserts that the note’s two numbers multiply back to the drawing box, at six rises across three widths — so a caption can no longer name a number the drawing does not honour.
  • Removed two excess ornaments: a dashed suburb grid behind the service-area map, which read as texture and competed with the twenty-minute ring — the only dashed thing on that map that means anything — and the handrail’s second rail at 700 mm, which was decoration masquerading as compliance and made the switchback unreadable.

Verification

Three suites, all green, none of them reading a number off the other:

  • oracle-accessramps.mjs — 139 assertions. An independent re-derivation of the geometry, the price model, the force order, both refusal forms, the badge templates and integer-safety, enumerated over the full 62,640-cell grid. Written from the standard and the model, never from the concept document.
  • verify-accessramps.mjs — 355 assertions. Drives the shipped page headlessly: every stepper, every chip, all three regimes, both refusal forms with the absence of a dollar figure asserted on four surfaces each, the eight-step chain and the reachable partial unwind, a rapid-fire burst, empty and filled submits against button[type=submit] specifically, the form patterns through checkValidity, the keyboard path, reduced motion, badge-versus-scene clearance at three widths and DPR 2, and screenshots at 375 / 1000 / 1200 reviewed at native resolution — including the widest reachable state at 26.4 m of footprint and the threshold detail at 20 mm.
  • copy-accessramps.mjs — 129 assertions. The pass whose only job is re-deriving every number that appears in prose on both pages, plus the honesty sweeps: the reserved phone pattern, the live JSON-LD allowlist, the schema panel, the colophon on both pages, and the no-raster rule scoped to resource loads.
  • check-accessramps-framewidth.mjs — 202 assertions. The frame note against the drawing that produced it. It measures px-per-metre off the drawn scale bar and the drawing box off the shipped SVG, then asserts that the printed extent is the extent on screen, that the note’s two figures multiply back to the box, and that the printed multiplier is the drawn scale over the elevation’s — at six flat rises and five ramp rises, across 375 / 1000 / 1200, so both regimes are held to the same standard.