Generative Assets · industry demo 216 · HVAC

How Westerly Air was made

Build notes for a conversion-focused landing page: a fictional Perth HVAC operator, a Comfort Calculator that turns pricing anxiety into an honest range, and the local-SEO plan a real client would get.

← View the siteCollection index

Concept

Westerly Air Conditioning is a fictional ducted and split-system specialist based in Balcatta, serving Perth's northern corridor. The audience is a homeowner sweating through a 40° February with a phone in one hand; the page has one job — a call or a quote request. The brand story is the Fremantle Doctor: the sea breeze that breaks Perth's heat every afternoon, promised "on a switch".

Palette — refrigeration against Perth heat

#F4F7FA chilled whiteGround. A page that feels air-conditioned.
#16283C steel navyStructure and ink base — plant-room steel.
#2FA8C8 refrigerant cyanLINE/MARK only — ducts, vents, breeze. Never body text.
#0F6B85 cyan inkDerived text-safe cyan for eyebrows, prices, links (≥4.5:1 on ground).
#E8842C heat amberThe enemy. Hot rooms, the sun — and the one CTA accent, where heat reads as urgency.
#9A4E0E amber inkText-safe amber for the repairs price and heat readout.

The ink/line split from the collection's learnings is applied from the start: cyan and amber draw lines and fills, their darkened inks carry any small text. Body copy sits in navy and #42566B on the chilled ground.

Type

Space Grotesk (500–700) for display — its engineered, slightly ducted geometry suits a trade that runs on kilowatts and zone counts, and it gives the estimate figure a confident, metered voice. Inter as the workhorse: forms, body, labels — legible at 16px on a phone in direct sunlight, which is where this page will actually be read.

Signature — the Comfort Calculator

An SVG cross-section of a single-storey Perth home, built procedurally in JS: room compartments are generated from a per-size model (3×1 → 5×2, each room carrying a width and a cooling priority), so the house genuinely changes shape with the size control. Choosing zones shades rooms from heat amber to refrigerant cyan in priority order — living areas first, the theatre last — while ducted mode drops a roof-space AHU, trunk line and ceiling vents into the drawing, and splits mode hangs wall units instead. The estimate is computed from the same constants the services grid quotes: ducted base by size plus $450 per zone past four (×1.18 for the top of the range), splits $2,190 for the first room plus $1,890 per extra (×1.15). At rest it sits mid-example — 4×2, ducted, 6 zones, one room still hot, $15,100–$17,800 — so the static preview already demonstrates the interaction. Second read: push every zone and the sun dims while the readout flips to "Whole home holding 23.0°".

Other techniques

Conversion decisions

Local SEO we'd implement for the real business

This demo deliberately ships only WebPage schema naming it a demonstration — fictional LocalBusiness or Review structured data in a real index is dishonest markup. A real client would get this, wired to their actual details and live review feed:

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "HVACBusiness",
  "name": "Westerly Air Conditioning",
  "telephone": "+61-8-XXXX-XXXX",
  "url": "https://westerlyair.com.au",
  "address": { "@type": "PostalAddress", "addressLocality": "Balcatta",
               "addressRegion": "WA", "postalCode": "6021", "addressCountry": "AU" },
  "geo": { "@type": "GeoCoordinates", "latitude": -31.87, "longitude": 115.83 },
  "areaServed": ["Joondalup","Wanneroo","Hillarys","Duncraig","Karrinyup",
                 "Scarborough","Stirling","Balcatta","Osborne Park","Morley"],
  "openingHoursSpecification": [
    { "@type": "OpeningHoursSpecification",
      "dayOfWeek": ["Monday","Tuesday","Wednesday","Thursday","Friday"],
      "opens": "07:00", "closes": "17:00" },
    { "@type": "OpeningHoursSpecification",
      "dayOfWeek": "Saturday", "opens": "08:00", "closes": "14:00" }
  ],
  "aggregateRating": { "@type": "AggregateRating",
      "ratingValue": "4.9", "reviewCount": "187" },  // from the live GBP feed
  "review": [ /* pulled from Google Business Profile, never hand-written */ ]
}
</script>

Plus: Google Business Profile as the review source of truth, suburb-level service pages only where the client genuinely works, and FAQ schema mirroring the on-page questions.

What would change for the real client

Iteration log

Pass 1 — Craft

Re-read the full file and drove every control headlessly. Found a corrupted gradient stop in the hot-room fill (#F6C econ — an invalid hex that would have silently killed the amber gradient) and fixed it to #F5C48E. Map geometry: Stirling's label sat 60px left of its dot next to Scarborough, and Scarborough's label ran into the HQ pin — both re-anchored. Ocean label ink lifted #5E8CA4 → #46708A for contrast.

Driven: rest probe returned {4×2, ducted, 6 zones, $15,100–$17,800} — equal to hand-computed 14,200 + 2×450 = 15,100 and round(15,100×1.18) = 17,800. SVG state: 1 hot room, 6 cool, 6 vents.

Pass 2 — Depth

Added the calculator→form handoff micro-interaction: "Book a free measure" pre-fills job type and writes the worked estimate into the description if it's empty. Asserted by driving the click — "3×1, 4 zones, ducted — calculator estimate $10,900 – $12,900" landed in the field, itself hand-checked (base 10,900; round(10,900×1.18) = 12,900). Confirmed the second-read moment: at full zones the sun dims and the readout flips to "Whole home holding 23.0°".

Driven maths: ducted 5×2/8 zones → 18,600–21,900; splits 3×1/3 rooms → 5,970–6,900; splits floor 2,190 = the services-grid anchor; ducted clamp forces ≥4 zones on system switch. All measured from the running page, all matched hand computation.

Pass 3 — Hardening

Chanel rule: removed the duplicate breeze arrow from the calculator SVG — the hero owns the Fremantle Doctor motif; the calculator keeps only the sun it actually uses. 375px: scrollWidth exactly 375, wordmark inside the viewport, sticky-bar targets 48px, h1/CTA/trust chip all above a 720px fold. Empty form submit flags all four fields with visible copy; filled submit renders the local confirmation with the form at display:none under the [hidden]{display:none !important} guard. Reduced-motion session: page settles complete with zero console errors.

Final harness: 36/36 asserts green across two full headless Chrome sessions (desktop + 375px + reduced-motion), zero console errors, drive-then-diff pixel change confirmed.