Build guide · Generative Assets
A fictional independent audiology clinic in Wembley, built as a conversion-first landing page for the hearing-care industry. One job: book a hearing assessment. One signature: a real, interactive audiogram — pick a scenario, watch its typical threshold curve draw itself across the speech banana, and read exactly what the first visit involves, and costs.
Hearing care has a trust problem: much of the industry is manufacturer-owned, so "free hearing test" often means "hearing-aid sales funnel". Ellery's whole positioning is the opposite — printed fees, independence stated plainly, and an audiogram the patient can actually read before they ever sit in the booth. The audience is the person who's been turning the TV up for a year, the FIFO worker needing a baseline certificate, the tinnitus sufferer who's been told to live with it, and parents. The page's single job is the booking enquiry, so the signature is the education: every scenario chip ends at a care card with an honest fee and a pre-filled form.
Health-page tone rule: calm, no scaremongering. The strongest conversion lines are the honest ones — "only if the test says so", "most first visits end with a baseline and advice, not a fitting".
Quiet clinical warmth: a faintly lavender ground for the page, one deep aubergine "sound booth" panel where the audiogram lives, and two chart colours with strict jobs — violet for the threshold curve and every CTA, amber for the speech banana region and nothing else. Amber never colours text; the chart keeps all the drama.
Ink/line discipline: violet is the mark-and-CTA colour; small text on the ground uses aubergine or the muted #5C5570 (6.3:1); labels on the booth use #E5E1F0 and #A59DBE (6.4:1), with dark text halos (paint-order:stroke) wherever a label crosses the amber region.
Gabarito — 250 · 500 · 1k · 2k · 4k · 8k
Nunito Sans carries the body: soft, legible, unthreatening — right for a health page. Gabarito does the display work: geometric warmth with rounded terminals, a voice rather than a signage face.
The chart is one inline SVG built at boot from data: frequency 250 Hz–8 kHz across six log-spaced columns, hearing level 0–90 dB HL down, gridlines every 10 dB. The speech banana is a closed Catmull-Rom spline through pinned top/bottom edge points (e.g. 15–55 dB at 500 Hz, 25–48 dB at 4 kHz), filled with an amber gradient. Each scenario chip defines a typical six-point threshold curve; selecting one draws it as a dash-reveal — stroke-dasharray set to the polyline's measured length, stroke-dashoffset eased from full length to zero, with the stroke kept visibility:hidden until drawn length > 0 so no round-cap dot ghosts at the start. Threshold markers pop in as the drawn length passes each point's cumulative distance.
The exit annotation is computed, not hand-placed: the code finds the first segment where the curve crosses the banana's bottom edge heading down, interpolates the crossing in dB and log-frequency, and parks a pulsing ring, a dashed drop-line to the frequency axis, and the caption there ("consonants start to blur here"). The pulse has a designed opacity floor of 0.55, so the signature is visibly alive at any thumbnail instant. The kids scenario deliberately never crosses — its designed state is the hopeful one ("clear of the speech banana — every speech sound audible"). Chip handlers token-invalidate the in-flight draw before the state swap, and a firstDraw flag gives the entrance a 1.4 s reveal while every subsequent swap redraws in ~0.5 s. The chart's meaning is mirrored in plain text below it (aria-live="polite"), the chips are native buttons (keyboard-drivable), and the honesty line sits on the chart itself: typical patterns, illustrative — yours is measured in the booth. State probe: window.__hearing exposes scenario, fee, draw progress, exit frequency, pulse level and a settled flag.
This page's model is fee-lookup, not arithmetic — every figure is a pinned fee, read from the running page by the QA harness and checked against the schedule below. Rest state measured at $165 (age-related scenario).
| Item | Fee | Where it surfaces |
|---|---|---|
| Full diagnostic hearing assessment (60 min, report included) | $165 | Age-related chip's care card (the rest state); grid |
| Workplace / pre-employment audiometric test with certificate | $95 | Workplace chip; grid — and the "Appointments from $95" floor line |
| Tinnitus consultation (60 min) | $190 | Tinnitus chip; grid |
| Kids' assessment (ages 4+) | $165 | Kids chip; grid — same fee as adult, stated on both |
| Ear wax removal, microsuction | $120 | Grid |
| Assessment fee credited against a fitting | in full | Care card, fees note and FAQ — arithmetic-free, binding as copy |
| Hearing-aid pricing | no number | Care card, grid card and fees note — set by the manufacturer schedule, quoted after assessment, independent of any brand |
| Hearing Services Program | no number | Care card, fees note and FAQ — the Australian Government program schedule applies |
The two price-less lines are designed states, not missing data. Any printed hearing-aid figure would be the dishonest choice — device pricing is authority-set (the manufacturer schedule) and choice-dependent (entry to premium tiers), and the HSP schedule belongs to the program, not the clinic. Both declarations appear wherever fees do: the care card and the fees grid.
The demo ships only honest WebPage/CreativeWork schema naming it a demonstration — fictional structured data in a real index is dishonest markup. A live clinic build would ship the precise organization type with real NAP, geo, hours and review feed:
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "MedicalClinic",
"name": "Ellery Hearing Clinic",
"description": "Independent audiology clinic in Wembley — diagnostic
hearing assessments, workplace baselines, tinnitus consultations,
kids' assessments and wax removal.",
"url": "https://elleryhearing.com.au",
"telephone": "+61 8 XXXX XXXX",
"medicalSpecialty": "Otolaryngologic",
"address": {
"@type": "PostalAddress",
"streetAddress": "XX Cambridge St",
"addressLocality": "Wembley",
"addressRegion": "WA",
"postalCode": "6014",
"addressCountry": "AU"
},
"geo": { "@type": "GeoCoordinates", "latitude": -31.933, "longitude": 115.818 },
"openingHoursSpecification": [
{ "@type": "OpeningHoursSpecification",
"dayOfWeek": ["Monday","Tuesday","Wednesday","Thursday","Friday"],
"opens": "08:30", "closes": "17:30" },
{ "@type": "OpeningHoursSpecification",
"dayOfWeek": "Saturday", "opens": "08:30", "closes": "12:00" }
],
"priceRange": "$95–$190",
"areaServed": ["Wembley","Floreat","Jolimont","Churchlands","Subiaco"],
"review": {
"@type": "Review",
"reviewRating": { "@type": "Rating", "ratingValue": "5" },
"author": { "@type": "Person", "name": "Real Patient" },
"reviewBody": "Sourced from the clinic's live Google reviews feed."
}
}
</script>schema.org has no audiologist organization subtype, so MedicalClinic is the correct top-level @type — an actual LocalBusiness-family organization type. Tokens like Otolaryngologic are MedicalSpecialty enumeration values and belong only in the medicalSpecialty property, never as the organization's @type. Review markup only ever wraps the clinic's real, attributable reviews.
paint-order:stroke outlines, and bumped the SVG annotation/honesty type so it stays legible when the chart scales to 375px.document.hidden and re-anchors the draw clock on resume.