Build guide · Generative Assets

How Straightline Panel & Paint was made

Design and build notes for site 253 — a smash repair industry demonstration: one conversion-focused landing page for a fictional Wangara panel shop, built as a single self-contained HTML file with every visual drawn procedurally.

Concept

Straightline Panel & Paint is a fictional smash repairer in Wangara, serving Perth's northern corridor — the audience is a not-at-fault driver holding a claim number, a car-park dent victim deciding whether to claim, and fleet operators with sign-written utes. The page has one job: get a photo estimate or an assessment booked. The art direction is the spray booth at noon — craft under even light, zero crash-scene drama; the wreck is the past, the page is about the repair.

Palette — argued from the booth

Booth white

#F4F4F2 · ground

The lit, filtered walls of the booth — an even, shadowless working light.

Chassis ink

#2E2A28 · structure / primary ink

Bare metal and tyre rubber. Carries all body copy and the dark review band.

Colour-coat crimson

#A63446 · line / mark

The fresh base coat going on: damage markers, the selected panel, CTAs. Never body text — the derived text ink is #8C2A3C (7.6:1 on booth white).

Primer grey-green

#8A9089 · secondary

Guide-coat primer: blend-zone overlays, chip borders, quiet lines. Text set in it uses the darker #5D635C ink (6:1).

Type

  • Saira Semi Condensed (display) — the condensed capitals of a workshop hoarding. The hero's three-line stack ("Straight lines. Matched colour. Like it never happened.") is the page's one moment of typographic bravery.
  • Karla (body/UI) — a warm workhorse grotesque that stays legible at 16px on a phone in a car park.
  • Overpass Mono — panel codes, price bands, hours, the booth stencils. Everything that would be lettered on a job card is set in mono.

Techniques

Everything is procedural: the sedan, the booth, the map and every icon are inline SVG; the light cones and wall are layered gradients; motion is CSS transitions gated on document.fonts.ready with a no-JS fallback and a full reduced-motion path. No canvas, no libraries — a conversion page for a 4G phone.

The signature: the Damage Mapper

A hand-plotted sedan side profile (920×340 viewBox) carries eight tappable panel paths — each with role="button", tabindex="0", aria-pressed and a keydown handler that dispatches a real MouseEvent (SVG elements have no .click()). A transparent 14px stroke widens each hit area for thumbs. Damage renders on the selected panel into a layer clipped by that panel's own path via <clipPath><use>: scuffs are hatched strokes, a dent is a radial-gradient shadow with a highlight arc, a crease is a butt-capped line pair along the swage, and "smashed" builds crumple facets plus radiating crack lines. Respray severities also tint the neighbouring panels in dashed primer — the blend zone, which is the trade's honest answer to "why does one panel quote three surfaces". A booth job-tag (PNL 03 · FRONT GUARD) tracks the selection — the second-read detail. Every selection pre-fills the enquiry form with panel, severity, payment mode and the current band, and updates the window.__panelbeater state probe.

The pricing model — derivation

The spec pinned the severity "from" anchors; the panel factor table is derived here and documented so QA has a source of truth. Private mode computes band = severity base × panel factor, rounded to the nearest $10. Insurance mode disables the price output with the on-page reason that the assessor sets the figure — a disabled control with a reason beats a dishonest number.

Severity base bands (steel panels)

SeverityBandWhy
Scuff$150 – $210Buff + machine polish; no paint touched.
Small dent$180 – $310PDR — worked from behind, factory paint kept.
Crease$580 – $730Repair + respray, blended into neighbours.
Smashed$980 – $1,420Panel replace + paint; teardown checked.

Panel factors

PanelFactorArgument
Front / rear door, sill1.00The baseline flat panel; sill's small area offsets its low working height.
Front guard1.07Bolt-on, but wraps into bumper and bonnet gaps; blends two neighbours.
Bonnet1.20The largest horizontal face — read under full sun, dearest to spray flawless.
Rear quarter1.25Welded, not bolted — repair-biased, blends into door, sill and boot.

Bumpers (plastic substrate — override table)

SeverityBandWhy
Scuff$150 – $210Same machine-polish work as steel.
Small dentdisabledPDR works steel from behind; plastic gets repaired and resprayed — the chip disables with that reason on-page.
Crease / scrape$450 – $620Plastic weld + flex primer, sprayed as a complete unit off the car. A bar is its own colour break, so no blend — which is why bumper repairs start under a panel respray.
Smashed$980 – $1,380New bar + paint + fitment; tabs, absorbers and sensors checked.

Measured worked example (rest state): front guard × crease = $580×1.07 = $620.60 → $620; $730×1.07 = $781.10 → $780. The spec's ≈$620–$780 hypothesis measured out exactly under the derived factors — shipped as $620 – $780. The model's cheapest output (scuff × factor 1.00 = $150) equals the services grid's "from $150" buffing floor, and the $180 / $450 / $580 / $980 grid anchors are each the model's own minima for their scenario — the page never argues with itself. Blend honesty is stated where it applies: a resprayed steel panel quotes three surfaces; a bumper doesn't.

Conversion decisions

  • Mapper in the hero. At desktop the Damage Mapper is the hero's right column, mid-demonstration at rest (front guard, crease, private, $620–$780 showing) — the "that could be my site" moment happens in the first viewport, and the estimate card's CTA hands straight into the form.
  • Private/insurance toggle up front because "repair or claim?" is the real first question — the FAQ's excess-vs-range maths backs it.
  • Form fields: name, phone, suburb, vehicle make/model (parts pricing), optional claim number, and a description that the mapper pre-fills — "photos help" framing because photo estimates are how this trade actually triages. Confirmation is local, instant, and clearly a demo: "Thanks — that's received … we'd call you back within the hour."
  • Sticky mobile bar pairs Call with Photo estimate — a smash customer is standing next to the damage with a phone in hand.
  • Choice-of-repairer honesty gets its own banner: it's the single most valuable thing a WA driver doesn't know, and saying it plainly is the trust play.

Local SEO we'd implement for the real business

This demonstration deliberately ships only WebPage schema naming it a demo — fictional structured data in a real index is dishonest markup. A real Straightline would ship LocalBusiness + Review schema like this:

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "AutoBodyShop",
  "name": "Straightline Panel & Paint",
  "telephone": "+61 8 XXXX XXXX",
  "address": {
    "@type": "PostalAddress",
    "streetAddress": "[real street address]",
    "addressLocality": "Wangara",
    "addressRegion": "WA",
    "postalCode": "6065",
    "addressCountry": "AU"
  },
  "geo": { "@type": "GeoCoordinates", "latitude": -31.79, "longitude": 115.83 },
  "openingHoursSpecification": [{
    "@type": "OpeningHoursSpecification",
    "dayOfWeek": ["Monday","Tuesday","Wednesday","Thursday","Friday"],
    "opens": "07:30", "closes": "17:00"
  }],
  "areaServed": ["Wangara","Landsdale","Madeley","Darch","Wanneroo","Joondalup"],
  "review": [{
    "@type": "Review",
    "author": { "@type": "Person", "name": "[real customer]" },
    "reviewRating": { "@type": "Rating", "ratingValue": "5" },
    "reviewBody": "[real review, with permission]"
  }],
  "aggregateRating": {
    "@type": "AggregateRating", "ratingValue": "4.9", "reviewCount": "[real count]"
  }
}
</script>

What would change for the real client

  • Estimage — assessor quoting straight from the photo-estimate form, so the claim path on the page maps to the shop's actual workflow.
  • PartsCheck — parts procurement feeding honest lead-time answers into the "how long" conversation.
  • iBodyShop — shop management: bookings, courtesy car allocation, job status SMS.
  • PPG colour retrieval — the spectro story on the page is their real mixing bench.
  • Xero for invoicing, Podium for the review feed that replaces this page's demo copy.
  • Photo upload on the form, their real licence numbers, their real before/afters shot in their own booth.

Iteration log

Pass 1 — Craft

Drove the full suite headless (Chrome + puppeteer-core): all 8 panels × 4 severities against an independently reimplemented pricing oracle, fresh-load empty submit, filled submit, toggle, FAQ, sticky bar. Found the phone field's pattern attribute threw a console error on validation — Chrome now compiles patterns with the v flag, where unescaped ( ) in a character class are illegal; escaped them. Screenshot review at 375 caught the header CTA wrapping to two lines (the recurring masthead offender) — swapped in a short "Estimate" label under 430px. Darkened the SVG booth stencil text for contrast and vertically centred the stage.

Pass 2 — Depth

The 1200×750 thumbnail preview showed only the hero — the signature was below the fold at capture, breaking the mid-demonstration rule. Restructured the hero into a two-column grid with the whole mapper as the right column: the crimson-mapped guard, chips and band now sit in the first viewport. Added the booth job-tag readout (PNL 0n · PANEL) that follows the selection, the dashed blend-zone overlays with their on-card explanation, and a one-shot sheen sweep across a freshly selected panel (skipped entirely under reduced motion). Fixed a flex-gap artefact doubling the space in the header CTA.

Pass 3 — Hardening

Final run: 136/136 checks. Verified at 375/768/1440 — masthead screenshot first at 375 (wordmark, phone icon and Estimate button on one row, no overflow); sticky call/quote bar fixed in the bottom 140px with working anchor scroll; keyboard Enter/Space drives the SVG panel zones; reduced-motion renders everything settled with zero console errors; the empty submit on a fresh load raises validation on four fields (the fifth required field — the damage description — arrives pre-filled by the mapper, which is exactly why the probe runs on a fresh navigation); honesty sweeps confirm every (08) number uses the reserved 9000 exchange, no forbidden JSON-LD types outside this guide's escaped panel, and the colophon is present. Booth-light pulse keeps a 0.78 opacity floor, so the signature is visible at any capture instant. Re-read every number on this page against the shipped model before committing.