Generative Assets · Industry demo 282 · Wave 25
A fictional Subiaco travel agency for Perth travellers — families doing Bali properly, couples finally booking Europe, cruise regulars who want the good cabin. The page has one job: book a $180 trip-design session, with the great-circle Trip Sketcher doing the persuading. View the site · Collection index
The wave's cool blue-white light site, argued from the studio's wall chart: pale sky off the morning ocean, deep water, inked routes, and one lei-coloured pin on arrival. The line/mark accent (ultramarine) and the secondary (coral) both fail 4.5:1 as small text on this ground, so body copy never uses them — the ink/line split gives text its own tokens and keeps the bright colours for routes, rules and pins.
Destination chips (Bali / Japan / Europe / South Pacific cruise ex-Fremantle), travellers and nights steppers, and comfort chips drive a procedural SVG chart of the Indian Ocean rim. The chart is built entirely in JS at runtime: a 20° graticule, ~19 hand-set coastline polylines, a dashed 32°S latitude line through Perth, and routes that are true great circles — each is a spherical slerp between Perth and the destination sampled at 64 points, projected equirectangular (lon −12°→158°, lat 62°→−46°). The route dash-draws with butt caps (the W15 round-cap dot lesson), one tide-mark per night is placed with getPointAtLength, and the coral pin drops with a one-shot ring pulse when the arc completes. The cruise renders the coastal leg north-west out of Fremantle instead of a flight arc — illustrative of "it leaves from home", with itineraries quoted exactly at booking.
The planning card prints the full maths line by line in mono under a declaration that is part of the card, always visible: "Planning figures from recent bookings, not quotes — fares move daily; we quote exact at booking." Dual-speed redraw per the W21 rule: the load entrance takes 1.4s and rests at two-thirds arced (pin not yet dropped, a marker patrolling the drawn portion so the demonstration is alive at any capture instant); every subsequent state change animates in 520ms. Reduced motion renders the route complete with the pin down, no patrol. State probe window.__travelagent carries dest, travellers, nights, comfort, total, rooms, drawn and a settled flag; QA gates on state, never on seconds. A second-read detail: all four routes Latitude South inks are on the chart wall faintly the whole time — choosing a destination just inks one of them boldly.
Per-person flights + per-room accommodation + per-person daily allowance, each pinned per destination and comfort tier. Rooms are 1 room per 2 travellers, rounded up — stated on-card, so a family of three sees the second room appear in the maths, not in the fine print. The cruise has no flights line ("it leaves from home — that's the point"); fares are per person twin-share per night, the comfort chips relabel to inside / balcony / suite, and the shore allowance is deliberately flat at $45 pp/night whatever the cabin — a documented pricing statement (the ship feeds you either way), not an invented gradient. The $180 trip-design fee sits outside the band (credited in full at booking), which is why the sketcher's cheapest outputs equal the services grid's "from" floors exactly.
| Scenario | Arithmetic | Spec | Measured |
|---|---|---|---|
| Japan · 2 trav · 10 n · comfort (rest state) | 2×$1,150 + 1×10×$280 + 2×10×$140 | $7,900 | $7,900 ✓ |
| Japan · 2 · 10 · luxe | $2,300 + $5,200 + $4,800 | $12,300 | $12,300 ✓ |
| Bali · 4 · 7 · comfort | 4×$580 + 2rm×7×$240 + 4×7×$110 | $8,760 | $8,760 ✓ (rooms=2) |
| Bali · 3 · 7 · comfort (round-up check) | rooms = ceil(3/2) = 2 | rooms 2 | $7,410, rooms 2 ✓ |
| Bali floor · 1 · 5 · essentials | $580 + $650 + $350 | $1,580 | $1,580 ✓ = grid floor |
| Japan floor · 1 · 5 · essentials | $1,150 + $900 + $450 | $2,500 | $2,500 ✓ |
| Europe floor · 1 · 5 · essentials | $1,850 + $1,100 + $550 | $3,500 | $3,500 ✓ |
| Cruise floor · 2 · 5 · inside | 2×5×$210 + 2×5×$45 | $2,550 | $2,550 ✓ |
| Cruise · 2 · 5 · suite | 2×5×$520 + 2×5×$45 | — | $5,650 ✓ vs oracle |
Zero spec corrections — all worked examples were pure anchor arithmetic and every one matched the implemented model to the dollar.
Flagged, by design — the solo-cruise gap: cruise fares are per person twin-share, so with cruise selected the travellers stepper clamps to a minimum of 2, with the reason on-card: "solo cabins exist and we quote them honestly, on the phone." A solo cruise price is deliberately not computable in the sketcher — a real build would add a solo-supplement rate. Also declared on-card: the flat cruise shore allowance, and the rooms rounding rule.
This demonstration page ships only WebPage schema naming it a demo — fictional structured data in a real index would be dishonest markup. A real Latitude South would ship TravelAgency + review schema like this:
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "TravelAgency",
"name": "Latitude South Travel",
"url": "https://latitudesouthtravel.com.au",
"telephone": "+61 8 XXXX XXXX",
"address": {
"@type": "PostalAddress",
"streetAddress": "…", "addressLocality": "Subiaco",
"addressRegion": "WA", "postalCode": "6008", "addressCountry": "AU"
},
"geo": { "@type": "GeoCoordinates", "latitude": -31.949, "longitude": 115.826 },
"openingHoursSpecification": [{ "@type": "OpeningHoursSpecification",
"dayOfWeek": ["Monday","Tuesday","Wednesday","Thursday","Friday"],
"opens": "09:00", "closes": "17:30" }],
"priceRange": "$$",
"areaServed": "Perth, Western Australia",
"aggregateRating": { "@type": "AggregateRating", "ratingValue": "4.9", "reviewCount": "187" },
"review": [{
"@type": "Review",
"author": { "@type": "Person", "name": "…" },
"reviewRating": { "@type": "Rating", "ratingValue": "5" },
"reviewBody": "…"
}]
}
</script>
The 48-assert harness (independent pricing oracle, fresh-load empty submit, filled submit, chip/stepper drives, cruise clamp, 375 probes, reduced motion) caught three ship-blockers: a specificity collision where a.cta's display beat .head-cta{display:none}, leaving the header CTA alive at 375px and pushing scrollWidth to 467px (the W6 shorthand-vs-specificity family); the reduced-motion rest state holding at two-thirds with no pin (spec: complete, pin down); and nights-changes while the arc was incomplete not rebuilding tide-marks. Screenshot review at 1200×750 and 375 then caught what asserts can't: hero copy said "the sketcher on the right" (wrong on mobile, now position-neutral) and the eyebrow duplicated the wordmark word-for-word.
Added the chart-wall second read (all four routes inked faintly at all times, the chosen one drawn bold over them), the one-shot pin-drop ring pulse (JS-driven — the pin group is positioned by an SVG transform attribute, so CSS animation is off-limits per W16/W17), and the resting marker patrol along the drawn arc. Driven checks: +1 night from rest rebuilt to 11 tide-marks and completed the arc ($8,460, matching the oracle); the cruise pin tag had clipped past the chart's right edge in the Pass-1 screenshot — re-anchored left of the pin and re-shot to confirm. One harness false alarm documented: getBBox() reports the tag in the pin group's local frame, so a raw bounds assert "fails" while the screenshot shows it fully in-frame — trust the rendered image.
Masthead first at 375 (W18/W21): wordmark right edge vs call-button left edge compared explicitly (rect-edge assert) and eyeballed — clean, with the header CTA correctly gone below 560px. Re-ran the full suite after every change: 48/48, zero console errors (which also proves the form's v-flag pattern compiles), sticky bar fixed with a tel: link in the bottom 140px, FAQ opens by dispatched click, date min set to today. Chanel rule: removed the sketcher's "CHART · PERTH 32°S" sub-caption — the chart already says both. Final copy read fixed the Swan on the metro map to actually meet the ocean at Fremantle. Reduced motion re-verified: route complete, pin down, no patrol, totals intact.