GENERATIVE ASSETS · INDUSTRY DEMO 259 · TOWING & RECOVERY

How Nighthaul was made

Nighthaul Towing & Recovery is a fictional 24-hour towing operator based in Welshpool, built as a conversion-first landing page demonstration. The audience is a driver on a freeway shoulder at 11pm — so the page's single job is making the phone call happen, with an honest price meter to remove the fear of ringing a tow truck at night.

Concept

Towing is the wave's most call-first trade: nobody on a shoulder fills in a form. Every design decision routes to the phone — header call button, hero call CTA, sticky mobile call bar, a call button inside the price card, and a form that literally says "on the shoulder now? Don't type — call." The signature Tow Planner exists to make the price honest before the phone rings: a procedural SVG night-shoulder scene (tilt-tray mid-load, rotating beacon, kilometre posts) driven by job chips, a distance slider and a day/after-hours toggle, with a mono price card that shows the exact meter.

The mood is the moment the beacon appears in the mirror — relief, not crash drama. Wet bitumen after rain, calm competence in high-vis, and real institutional safety numbers (Main Roads 138 138, 000) above the fold because that is genuinely correct advice.

Palette — beacon-amber night

A wet-bitumen green-black, deliberately distinct from the collection's other dark sites (locksmith's navy porch-light, signage's plum dusk, earthmoving's iron dusk, concreter's wet-slab charcoal). Every colour is argued from the shoulder at 2am.

#15181A · wet bitumen

Ground — the road after rain, faintly green under sodium light.

#EFF1EC · headlight ivory

Body-ink — what the low beam shows you. 13.9:1 on bitumen.

#F5A81E · beacon amber

LINE/MARK only — sweep arcs, distance marks, CTAs, prices. Never long body text.

#CE5A4E · chevron red

Secondary — the rear marker board's stripe. Used with the board's own discipline, always paired against amber.

#AEB6B0 · derived dim ink

Derived muted ink for secondary copy — 8.1:1 on panels, keeping amber for marks (the W2/W5 ink/line split).

#E2796C · chevron ink

Derived lighter red for text-size links — the raw chevron red only clears ~4.5:1, so links get their own ink at 6.2:1.

Type

Off the shoulder — on the tray

BARLOW CONDENSED · DISPLAY

Highway-signage DNA — condensed caps at line-height .96 for the masthead moment. Punctuation kept off line ends in the tight-leading block (W19 phantom-diacritic rule): the three hero lines carry no terminal punctuation at all.

Four-point tie-down on the tray, every car, every time.

BARLOW · BODY & UI

The workhorse — same family, so the page reads as one voice from display to form label.

$180 + 14 × $6 = $264

AZERET MONO · KILOMETRES, TIMES, PRICES

Every number on the page is mono — the meter aesthetic. If the brand promise is "honest meter", the numerals should look like they came off one.

The Tow Planner — technique

The night-shoulder scene is one hand-built SVG (660×400): sky gradient, sodium haze, guardrail, wet road with an amber reflection column, and a tilt-tray truck drawn in side profile with the tray tail on the bitumen. Three vehicle silhouettes (sedan, motorcycle in its wheel cradle, mini-excavator chained at four corners) sit on the tray incline and swap per job chip — toggled via setAttribute('hidden') with the [hidden]{display:none !important} guard, because SVG groups have no .hidden IDL property (LEARNINGS W13).

The planner exposes window.__towing (job, km, time, total, form status) as the QA state probe, and every planner interaction pre-fills the callback form's job and details fields — the estimator becomes a warmer lead (the W12 handoff contract). Pre-fill fires only on interaction, so a fresh load still fails an empty submit on every field (W15).

Pricing model — shape and derivation

Pinned anchors from the spec: breakdown from $180 day including the first 10 km loaded then $6/km; after-hours (8pm–6am) +$70 flat; motorcycle from $160; machinery from $450 (to 4.5 t); accident = insurer-directed; holding yard first 3 days free then $25/day. The base is the pinned minimum for each job — no hidden delivery line.

ScenarioModelMeasured
Rest state — breakdown, 24 km, day180 + 14×6$264 ✓
Same job, after-hours264 + 70$334 ✓
Motorcycle, ≤10 km, day (cheapest output)160$160 ✓ = grid floor
Machinery, 24 km, day450 + 14×6$534 ✓

Derived (not spec-pinned): the spec pins only the motorcycle and machinery bases, so both jobs inherit the breakdown meter's distance shape — first 10 km in the base, then $6/km. That flat per-km across jobs is shipped as a deliberate pricing statement: the kilometres cost the truck the same whatever's on the tray; the base carries the job's real difference (cradle gear, ramps and chains). The after-hours surcharge is deliberately flat across all jobs ("the driver getting out of bed is the surcharge — it's flat, not a meter").

Disabled with a reason (W18): the accident chip switches the card to claim guidance and disables the distance slider and time toggle, because after a crash the insurer usually directs and pays for the tow — a number quoted here would be theirs to set. Uninsured drivers are told plainly they get the same breakdown meter. All figures were verified against an independently reimplemented oracle in the harness, at rest and across four driven scenarios.

Conversion decisions

Local SEO we'd implement for the real business

This demo ships only WebPage schema naming itself a demonstration — fictional LocalBusiness/Review markup in a real index would be dishonest. For a real operator we'd ship:

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "AutomotiveBusiness",
  "additionalType": "https://en.wikipedia.org/wiki/Towing",
  "name": "Real Towing Co",
  "telephone": "+61 8 XXXX XXXX",
  "address": {
    "@type": "PostalAddress",
    "addressLocality": "Welshpool",
    "addressRegion": "WA",
    "postalCode": "6106",
    "addressCountry": "AU"
  },
  "areaServed": "Perth metropolitan area",
  "openingHoursSpecification": {
    "@type": "OpeningHoursSpecification",
    "dayOfWeek": ["Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"],
    "opens": "00:00", "closes": "23:59"
  },
  "priceRange": "$$",
  "aggregateRating": {
    "@type": "AggregateRating",
    "ratingValue": "4.9", "reviewCount": "187"
  },
  "review": [{
    "@type": "Review",
    "author": { "@type": "Person", "name": "Real Customer" },
    "reviewRating": { "@type": "Rating", "ratingValue": "5" },
    "reviewBody": "Fed from the live Google reviews profile."
  }]
}
</script>

Plus a Google Business Profile wired to the review flow, suburb-level service pages for the towing head terms, and 24-hour structured hours so the "open now" panel works at 2am — when this trade's customers actually search.

What would change for the real client

Iteration log

Pass 1 — Craft

Caught and fixed five corrupted hex tokens from authoring (stray whitespace inside colour values — would have silently broken the sky gradient, cab glass, map ground and river strokes). Drove the full harness: rest state $264 verified against the reimplemented oracle, after-hours $334, motorcycle floor $160 = grid floor, machinery $534, accident disable, posts stepping (12 km → 1 lit, 55 km → 5 lit), FAQ, beacon glow sampled at 14 points across the sweep (min 0.56 ≥ 0.5 floor). Screenshot review at 1200×750 confirmed the planner shares the thumbnail with the hero (top at 172px). Empty submit initially bypassed the styled error path because native interactive validation swallowed the submit event — added novalidate and let the handler run reportValidity() itself; all five fields now flag.

Pass 2 — Depth

The 375px screenshot showed the hero CTA wrapped to two lines and pushed below the fold, and the sticky call label stacked three-deep — compacted the mobile hero (44px display floor, full-width CTA, ghost link dropped in favour of the sticky bar's twin) and shortened the sticky label; CTA now sits at 52px tall well inside the fold with the trust line at 584px. Added the in-scene live readout ("BREAKDOWN · DAY / $264") so the thumbnail shows the meter, blinking hazards on the towed sedan as the second-read detail, a price tick micro-interaction on the total, and a scene caption that flips between "DAY RATE" and "2:14 AM · AFTER-HOURS CREW" with the toggle (the caption previously said 2:14 AM while the meter read day rate — the page argued with itself). Full-page capture had also shown every below-fold section black: the IntersectionObserver reveals never fired off-screen, so a 4s reveal-all fallback now makes the page capture-proof.

Pass 3 — Hardening

Masthead screenshot at 375 first (W18): wordmark 143px, call button right edge 351px — one row, no collision. 34/34 harness checks green: scrollWidth 375, sticky bar fixed with a 48px tel target, exactly one h1 per page, reserved-phone positive sweep (7 × (08) 9000 0259, zero other 9xxx exchanges), WebPage-only JSON-LD, zero console errors, and the reduced-motion run confirmed SMIL fully stripped with the sweep parked at rotate(200) and glow at opacity 1. The phone pattern [0-9\+\(\) ]{8,16} was written against v-flag rules (all class specials escaped) and parses clean. Chanel rule: removed a near-invisible ivory reflection streak that added nothing at any viewport. Final copy read: no punctuation ends a condensed-caps display line anywhere on either page.