Industry demo 458 · radiation safety
How Halfvalue X-ray Compliance was built
Halfvalue is a fictional Leederville business that tests diagnostic x-ray units for dental, chiropractic and medical practices across Perth and Peel. Its customer is a practice manager holding a certificate that expires next autumn, and the page has one job: show which month to book so every unit keeps its anniversary, price the visit to the cent, and take the booking or the free reminder.
Palette: the trefoil, deepened to text strength
The radiation trefoil is magenta on yellow. Magenta darkened to #7A1F5C carries the CTA and the new certificate; the warning yellow stays a drawn fill only (the booking window, the tube-head sticker), never behind words. Every token sits only on a ground contrast-w60.log declares for it.
Colours minted during the build, measured by rs-check.mjs: tint #F8EFF5 (ink 16.08:1, magenta 8.57:1), soft #F0EDF3 (muted 7.41:1), map ocean #E6E2EC (muted 6.73:1), current-certificate bar #8A8398 (3.63:1 as a mark on white), field border #7D7788 (4.32:1).
Type
Onest 600/700 for headings and figures: a modern grotesque with round, open counters that reads like a well-made clinical form rather than a brochure. Albert Sans 400/500 for body copy at 16 px and up. Dates and prices use tabular numerals so “1 Mar 2030” and “$925.65” don’t shimmer when the estimator changes them.
The proof element: “Keep your anniversary”
Three steppers (intraoral 0–6, OPG or ceph 0–3, CBCT 0–2), the current expiry as month and year selects, and the month you’d like the test. The model works in month indices (year × 12 + month) with certificates expiring on the 1st, and applies the Radiological Council’s four-row anniversary table: dental units on 36 months, CBCT on 24.
| Input | Output on the page |
|---|---|
| 3 intraoral + 1 OPG, expiry Mar 2027, test Jan 2027 (the default) | New expiry 1 Mar 2030, anniversary kept; $990 − $148.50 = $841.50 + $84.15 GST = $925.65 |
| Same units, test Sep 2026 | “Wait — book from Dec 2026”, $0 today; testing then gives 1 Sep 2029, 6 months of cover given up |
| CBCT, expiry Mar 2027, test Jan 2027 | 1 Mar 2029 |
| 2 intraoral + 1 CBCT | $940 − $141 = $799 + GST = $878.90 |
| 1 intraoral | $240 + GST = $264.00, no multi-unit discount |
| Expiry Mar 2027, test Jul 2027 | Lapsed: 1 Mar 2030 kept, the offence sentence replaces the saving line, hatched “do not use on patients” zone |
| Expiry Mar 2027, test Jan 2028 | 1 Jan 2031, anniversary moves to January |
The drawing is one inline SVG: a static intraoral tube head on its wall arm (yoke, housing, cone, a yellow trefoil sticker built from three annulus sectors) with a certificate tag whose date follows the model, beside one timeline bar per unit type. Each bar shows the current certificate, the three-month window shaded in yellow, a hatched lapse zone when the test falls after expiry, the new certificate in magenta, a dashed ghost for any months given up, and a diamond at the new expiry. Unit types not on the visit stay drawn at 40 % so the CBCT’s 24-month date is visible beside the dental 36. The bars grow once on load, driven by performance.now(), pausing while the tab is hidden and skipped entirely under reduced motion; the page is at rest well before the 7-second thumbnail.
Everything the card shows is exposed on window.__radiationsafety: inputs, the table row, months before or after expiry, each type’s new expiry and months given up, the itemised quote, today’s price, the $0 alternative with its isSubstitute flag and reason, and the text actually rendered.
One place this page disagrees with the wave oracle
figures-w60.mjs keeps the anniversary for a test in the month six months after expiry. With certificates expiring on the 1st, a test on any day of that month but the 1st is more than six months after expiry, so this page resets the date there and says a test on the 1st itself would still count. Across 4,800 expiry, test and interval combinations the two models agree on 4,720; the 80 differences are all at that month. Every worked example matches.
Where the page argues against its own sale
When the chosen month is more than three months early, the card stops selling: “Wait — book from Dec 2026”, $0 today, the months the early test would throw away, and a button that moves the test into the window. Inside the window the $0 option is still on screen, marked “Not a substitute” with the Council’s offence sentence as the reason. A full-width licence panel quotes the Council that AHPRA-registered dentists need no licence for intraoral, OPG or lateral ceph units, and that CBCT does, then ends on the $0 reminder.
Verified at source, 14 September 2026
- Program Requirements (2025) PDF, via
pdftotext: the test-period table (dental 36 months, Cone Beam CT 24, radiography 24), the four-row anniversary table, the offence sentence in 1.4, “All tests must be assessed and certified by a qualified expert.”, the one-month report limit, the Dental X-ray Equipment Workbook 2023, and the before-first-patient sentence in 1.3. - Compliance Testing page: the “It is an offence to use x-ray equipment for human diagnosis unless…” sentence.
- Licence Prerequisites page: the AHPRA dentist sentence.
- Specific Requirements page: the CBCT licence sentence and both shielding-assessment sentences.
What this page declined to publish
Anything about veterinary test cycles, Radiation Safety Officer or radiation management plan duties, registration or licence fees, the history of the CBCT interval, per-category test counts, and any claim about a unit being unable to remain in clinical use beyond the Council’s offence sentence. No advice about a specific unit’s result.
Conversion decisions
- The estimator beside the headline at desktop. The first screen answers the practice manager’s actual question, a month and a dollar figure; on a phone it follows the price tiles so the prices come first.
- Two CTAs that name their size. “Find your booking month” and “Set a $0 reminder”. The reminder is the smallest yes and gets its own services tile and its own radio in the form, where the submit button relabels itself.
- Form fields a tester actually needs: name, practice, phone, suburb, units by type and the soonest expiry month (with “Not sure” and “New unit”). Units and expiry copy across from the estimator until the visitor edits them, so most people type four fields.
- The confirmation computes the window from the expiry chosen (“Inside your window: Dec 2026 to Feb 2027”), which proves the enquiry was understood.
- Mobile sticky bar with a call link and the form anchor; the hero trust line ends 31 px above it at 375×667.
Why this converts
A practice manager lands wanting one answer, which month to book, and the estimator beside the headline gives it with the price in the same glance. The primary button finds that month and the second button offers a free reminder, so someone not ready to book still leaves their details. The form asks only what a tester needs and fills the units and expiry in from the estimator, so most visitors type four fields. When the visitor picks a month that is too early, the page tells them to wait and shows the months they would lose, and that single honest moment is what makes the price believable. The trust line and the licence panel quote the regulator rather than the business, which answers “can I trust these people” without a badge.
Local SEO we would implement for the real business
The demo page carries only WebPage markup naming it a demonstration. For the real client we would ship LocalBusiness data with their verified details and genuine reviews, like this sample:
{
"@context": "https://schema.org",
"@type": "ProfessionalService",
"name": "Halfvalue X-ray Compliance",
"url": "https://www.example.com.au/",
"telephone": "+61 8 9000 0603",
"address": {
"@type": "PostalAddress",
"streetAddress": "Suite 0, 00 Example Street",
"addressLocality": "Leederville",
"addressRegion": "WA",
"postalCode": "6007",
"addressCountry": "AU"
},
"areaServed": ["Perth metropolitan area", "Peel region"],
"openingHours": "Mo-Fr 07:00-17:30",
"hasOfferCatalog": {
"@type": "OfferCatalog",
"name": "X-ray compliance testing",
"itemListElement": [
{ "@type": "Offer", "name": "Intraoral unit test", "price": "240.00", "priceCurrency": "AUD" },
{ "@type": "Offer", "name": "OPG or OPG-ceph test", "price": "270.00", "priceCurrency": "AUD" },
{ "@type": "Offer", "name": "CBCT unit test", "price": "460.00", "priceCurrency": "AUD" }
]
}
}
Review markup would be added only from the client’s genuine reviews. Beyond the schema: one page per suburb cluster (northern suburbs, south-east, Rockingham and Peel), an FAQ page marked up from the real answers, and a Google Business Profile with the booking link.
What would change for the real client
- Their certificate register becomes the estimator. A signed-in practice sees its own units and expiry dates instead of steppers, fed from the tester’s job system.
- Reminders run themselves: a scheduled job three months before each expiry sends SMS and email (Twilio or MessageMedia) and books through the calendar, rather than a person watching a spreadsheet.
- Practice software: many clinics run Dental4Windows, Praktika or Cliniko; the reminder can land as a task in the practice’s own system, and the booking slot can avoid patient lists.
- Quotes and invoices flow to Xero with the same line items and GST the page prints, so the web quote and the invoice never disagree.
- Their real reviews from Google, and their real licence and ABN, replace the placeholders.
- Market check for the owner, not published on the page: dental units in Perth commonly test at about $250–$280 + GST each, which is where these anchors sit.
Iteration log
Pass 1 · Craft
- Looked at the 1200×750 thumbnail: the three-month window was a faint 34 % wash and the current certificate showed only four months before expiry, so the picture did not explain itself. The window is now 50 % (blend re-measured in
rs-check.mjs; every mark on it stays above 3:1) and the axis starts nine months before the earlier of test and expiry. - The chiropractic strip wrapped its Council quote in a
<q>as well as typed quote marks, so it printed doubled marks. Now plain text with one pair. - At 375 the masthead’s brand line wrapped to two lines beside the phone number; below 420 px the suburb drops from that line (it stays in the hero and footer).
- Drove the form: an empty submit shows four field errors and a summary and puts focus on the name field; “ring me maybe” in the phone box is rejected; a filled form shows the confirmation with “Inside your window: Dec 2026 to Feb 2027”.
Pass 2 · Depth
- At 375 the timeline shared one viewBox with the tube head and its labels rendered at about 5 px. The drawing now has a narrow mode below 560 px: the viewBox crops to the bars, labels are 13.5 units, and the timeline re-renders when a resize crosses the breakpoint. Measured: every label at least 9 px tall.
- Drove every branch and asserted the rendered text: Sep 2026 gives “Wait — book from Dec 2026”, $0 today, 6 months given up, and the move button lands on Dec 2026 inside the window; Jul 2027 gives 1 Mar 2030 with the hatched lapse and the offence sentence; Jan 2028 gives 1 Jan 2031; Sep 2027 resets with the note about a test on the 1st; a test in the expiry month itself reads as lapsed; 2 intraoral + 1 CBCT is $878.90; 1 intraoral is $264.00; CBCT alone leads with the CBCT expiry, 1 Mar 2029; zero units asks for a unit.
- Second read: the certificate tag hanging on the tube-head arm prints the new expiry and changes with every input.
Pass 3 · Hardening
- At 375×667 the hero trust line ends at 571 px and the sticky bar starts at 602 px: 31 px clear. No horizontal overflow at 375, 768 or 1200; mobile order runs hero, services, estimator, how, reviews, map, FAQ, form.
- On a phone the confirmation received focus but stayed off screen below the collapsed form; it now scrolls into view.
- Reduced motion: the page is settled on first paint with no transforms left on the bars. The load animation runs on wall-clock time, pauses while the tab is hidden, and the state probe reads identically three seconds after the 7-second thumbnail.
- Removed one ornament: the tube arm used to swing into place on load. The bars growing is now the page’s only motion.
- Scanned both pages for curly quotes inside tags, street addresses in prose, AI attribution, the reserved phone pattern and the JSON-LD type allowlist; checked the page model against the wave oracle across 4,800 date combinations and all 84 unit mixes. Page weight: 79.5 KB, and this guide about 21 KB.
- Harness note: the resize check failed once because its tab sat behind others and headless Chrome throttled animation frames. The page was fine; the check now brings the tab to the front first.