Build guide · site 354 · Wave 39, Industry Demos XXVIII
A fictional dive shop and training centre on the Rockingham beachfront — try-dives, Open Water and Advanced courses, guided shore dives on the Shoalwater reefs, boat double-dives on the wrecks, gear hire, air and nitrox fills. The page has one job: a booking request with the trip or course, the divers and the extras already priced.
The audience is the couple who want to learn before their Ningaloo trip, the visiting diver with a card and no gear, the group of mates booking a boat day, and the parent booking a teenager's try-dive. Dive pricing is per person, mostly all-inclusive, and lives or dies on what's in the price — so the page declares "tanks, weights and your guide or instructor included" on the card, on the slate and in the ledger, and prices the only three things that genuinely move: the trip, the head-count and the extras.
The direction is ten metres down on the Shoalwater reef, looking up: deep-water ground, sunlit-foam ink, aqua CTAs, hose-orange linework. The signature is the Dive Planner — a drawn SVG cross-section of the water column with a bright surface at the top, sun shafts breathing down through it, a depth gauge down the left from 0 to 30 m, a reef ledge, and the divers descending along a drawn line toward it. Trip chips redraw the surface (a boat, a beach, a pool edge), the depth line and the ledge; the divers stepper draws the figures; the toggles draw their marks (a hire-kit rack, green-and-yellow nitrox bands on every tank, a camera housing on the guide); and the price is a dive slate hung on the gauge rule with an orange cord.
Every ratio below was computed by WCAG relative-luminance arithmetic from the shipped hex tokens in Pass 1 (the spec's figures were hypotheses). All four spec tokens cleared their grounds by a wide margin, so no two-token remedy was needed: aqua #4FC8BE measures 7.65:1 on the water and 6.74:1 on the lifted card, and carries dark labels on buttons at 7.65:1; hose orange #F2A65A is 7.68:1 / 6.77:1 and is used for lines and marks; the pre-minted lighter inks (#8FE0D8 10.23:1, #F7C289 9.65:1) carry aqua- and orange-coloured copy anyway, for headroom over gradients. The slate is the one light surface: deep ink 11.81:1, subtitle 8.55:1, note 6.05:1, name line 6.16:1 on the slate cream.
Four divers, two tanks, under by nine
Josefin Sans 700 for the wordmark, headings, the display line and the slate price — a geometric with a 1930s poster-diver feel. The display moment re-words itself with the controls: the count word from the stepper (One diver, … Eight divers,) and the trip's phrase (one tank, pool first · four days, eighteen metres · five dives, thirty metres · one tank, ten metres · two tanks, under by nine).
Manrope 400–700
Manrope for everything read: body at 17px on mobile and 18px from 760px, chips, ledger, form, scene labels. Weight carries hierarchy (700 legends and buttons, 600 chips, 500 fine print); tracking only on the uppercase eyebrows, the slate name line and the DEPTH title.
ledgePath(L, topY, side) draws a plateau to an edge and a bank whose run scales with the drop, mirrored across the frame for the shore-side trips: the boat and Advanced trips put the ledge under the water column at left (divers descend down-left from the boat); try-dive, Open Water and shore dives put it at right (divers descend down-right from the pool ladder or the beach). Corals on the boat/deep ledges, seagrass tufts on the shore ones, a skills mat on the Open Water sand.diverFigure() draws tank, fins, body, head, mask and hose in a rotated group aligned to the descent line, spaced from 16% to 78% along it and offset alternately across it, with the guide (orange hood, 1.5× scale, camera housing when photos is on, a hand-line to the student on try-dives) at 90%. Drawn cap 8 = the stepper cap = the boat's survey; the count tag prints the same integer.dt = min(max(now − last, 0)/1000, .05): emitters sit at every diver's mouth, bubbles rise, wobble, grow and fade into the last 30 px below the surface; the sun shafts breathe between 0.55 and 0.95 opacity (the never-invisible floor); the foam line ripples; the fish drift. Reduced motion renders one settled frame with four static bubbles per emitter; the loop pauses on document.hidden.calc(6% + 24px) from the left so its cord meets the gauge rule exactly, with a drilled hole and a horizontal orange cord as pseudo-elements. Three width variants: wide (25% of the stage, extras itemised in the subtitle), mid 600–1099px (28%, subtitle shortened to the multiplier), mobile (48% cap, tight padding, smaller price, tier note swapped for its pinned short form)..hidden IDL), animated by a CSS mark-in on the group while positioning stays on attributes.<style>, Google Fonts the only external request.State is one object — trip, divers, three toggle memories — and compute() is a pure function over it: the boat rate is $170 at four or more divers else $190, other trips are flat per person; gear (+$75 pp) is live on shore and boat only, nitrox (+$20 pp) on boat only, photos (+$60 flat) on shore and boat; per-person lines multiply the actual count; the tier note fires only at boat × 3. render() writes the result to every surface in one pass — the display line, the slate (name, price, subtitle, note), the chips, the stepper, the three switches (disabled with the on-card reason where not live, aria-checked mirroring the effective state), the itemised ledger, the plain-text mirror under the scene and the booking textarea — then drawTrip() rebuilds the scene. Restore-vs-reset is by memory: a course chip never overwrites state.gear/nitrox/photos, so returning to a boat day restores the prior choices exactly. Every handler bumps the scene token before touching state; the first draw earns a 90 ms/layer stagger (1.2 s cap), every later draw 26 ms/layer (480 ms cap); the settle timer publishes window.__diveshop.settled only for the latest token, so a rapid-fire run of clicks ends on one definite, settled state.
| Trip / course | Per person | Depth drawn | Gear | Nitrox | Photos |
|---|---|---|---|---|---|
| Try-dive · Discover Scuba (pool + shore, half day) | $199 | 12 m line, pool edge, instructor holds the student's hand | disabled | disabled | disabled |
| Open Water course (four days) | $650 | 18 m line, shore entry, skills mat | disabled | disabled | disabled |
| Advanced course (two days, five dives) | $550 | 30 m line, boat, deep-blue gradient below 18 m | disabled | disabled | disabled |
| Guided shore dive (Shoalwater, single dive) | $95 | 10 m line, beach, Penguin Island, seagrass | live +$75 pp | disabled | live +$60 |
| Boat double-dive (two dives) | $190 · $170 at 4+ (everyone) | 18–30 m band, boat, two dive-flag markers | live +$75 pp | live +$20 pp | live +$60 |
| Rule | Semantic | Copy |
|---|---|---|
| Boat × 3 divers | NOTE only, never forced | "at four divers the boat re-rates to $170 each for everyone" — mobile short form on the slate: "four divers → $170 each for all"; ledger and mirror always carry the full template |
| Boat × 4+ divers | declaration line on badge, ledger and mirror; note ABSENT | "$170 pp — group rate" |
| Gear on courses | DISABLED with reason; unlock RESTORES the prior choice | "the course fee includes all your gear" |
| Nitrox on courses and shore dives | DISABLED with reason; unlock RESTORES | "nitrox needs the nitrox ticket and a boat-depth profile — add it to a boat day" |
| Photos on courses | DISABLED with reason; unlock RESTORES | "course days are for skills — book photos on your first fun dive" |
| Rescue and pro courses, charters, servicing, sales | no number | "quoted" — grid, ledger footnote and FAQ; diver medical statement declared in the FAQ |
| Tanks, weights, guide/instructor | declaration line, $0 | on the card, the slate, the ledger and the mirror |
Divers 1–8 on every trip (no empty state; the boat carries eight so the drawn cap equals the range). Every anchor and multiplier is an integer, so totals are whole dollars by construction. The estimator's cheapest outputs equal the services grid floors: shore dives from $95, try-dives $199, boat double-dives $170 pp (groups of four) / $190 pp, Open Water $650, Advanced $550.
| Scenario (full control vector set explicitly) | Spec | Measured | Also asserted |
|---|---|---|---|
| Rest: boat double-dive · 4 divers · gear ON · nitrox OFF · photos OFF | $980 | $980 | display "FOUR DIVERS, TWO TANKS, UNDER BY NINE"; group-rate line on slate, ledger and mirror; tier note absent on all three; four divers + guide drawn; slate and scene inside the first 750 px at 1200 |
| Guided shore dive · 1 · all OFF | $95 | $95 | grid floor; nitrox disabled with its reason |
| Guided shore dive · 1 · gear ON | $170 | $170 | hire-kit rack drawn on the beach |
| Try-dive · 2 | $398 | $398 | gear, nitrox and photos all disabled, three reasons visible |
| Boat · 3 · all OFF | $570 | $570 | tier note PRESENT on slate, ledger, mirror; group line absent |
| Boat · 4 · all OFF | $680 | $680 | note ABSENT, group-rate line PRESENT (three surfaces) |
| Boat · 2 · nitrox ON | $420 | $420 | nitrox bands drawn on every tank |
| Open Water · 1 | $650 | $650 | all three toggles disabled with reasons |
| Boat · 8 · gear + nitrox + photos | $2,180 | $2,180 | eight divers drawn (cap), camera on the guide, rack + bands |
| Boat · nitrox ON → shore → boat | restore | $420 → $190 → $420 | nitrox locked OFF on shore (price = shore), RESTORED ON on return; gear + photos memory also asserted across an Open Water hop |
| Rest + photos · rest gear OFF · rest divers 3 | $1,040 · $680 · $795 | same | divers 3 carries the tier note |
| Advanced · 2 · all OFF | $1,100 | $1,100 | deep gradient below 18 m |
| Floors: try 1 · adv 1 · boat 1 | $199 · $550 · $190 | same | match the grid |
The demo's live JSON-LD is only WebPage / WebSite / BreadcrumbList, because fictional business data in a real index is dishonest markup. For the real Reefline we'd ship a SportsActivityLocation (the schema.org LocalBusiness subtype for a dive centre; the retail counter and servicing arm would carry a separate SportingGoodsStore node) with real reviews from the shop's Google profile:
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "SportsActivityLocation",
"name": "Reefline Dive",
"description": "Dive shop and training centre in Rockingham: try-dives, Open Water and Advanced courses, guided shore dives on the Shoalwater reefs, boat double-dives on the wrecks, gear hire, air and nitrox fills.",
"url": "https://www.example-reefline.com.au/",
"telephone": "+61 8 9000 0354",
"image": "https://www.example-reefline.com.au/photos/boat.jpg",
"priceRange": "$95–$650",
"address": { "@type": "PostalAddress", "streetAddress": "(real street address)", "addressLocality": "Rockingham", "addressRegion": "WA", "postalCode": "6168", "addressCountry": "AU" },
"geo": { "@type": "GeoCoordinates", "latitude": -32.28, "longitude": 115.73 },
"areaServed": ["Rockingham", "Shoalwater", "Safety Bay", "Warnbro", "Baldivis", "Kwinana", "Perth"],
"openingHoursSpecification": [
{ "@type": "OpeningHoursSpecification", "dayOfWeek": ["Wednesday","Thursday","Friday"], "opens": "08:00", "closes": "17:00" },
{ "@type": "OpeningHoursSpecification", "dayOfWeek": ["Saturday","Sunday"], "opens": "06:30", "closes": "16:00" }
],
"hasOfferCatalog": { "@type": "OfferCatalog", "name": "Courses and trips", "itemListElement": [
{ "@type": "Offer", "name": "Guided shore dive", "price": "95", "priceCurrency": "AUD" },
{ "@type": "Offer", "name": "Boat double-dive", "price": "190", "priceCurrency": "AUD" },
{ "@type": "Offer", "name": "Open Water course", "price": "650", "priceCurrency": "AUD" }
] },
"aggregateRating": { "@type": "AggregateRating", "ratingValue": "4.9", "reviewCount": "184" },
"review": [ { "@type": "Review", "author": { "@type": "Person", "name": "(real reviewer)" }, "reviewRating": { "@type": "Rating", "ratingValue": "5" }, "reviewBody": "(real review text, synced from Google)" } ]
}
</script>
Plus: a Google Business Profile with the boat, the classroom and the fill station as photos; a Course node per certification with the agency named; suburb landing content for Rockingham, Baldivis and Mandurah searches ("learn to dive Rockingham", "shore dive Shoalwater"); the FAQ marked up as FAQPage once the answers are the shop's own words.
calc(6% + 24px) cord.tel: link. The tall layout keeps the ledge/entry logic and its own slate variant (48% cap, brief tier note); measured slate bottom 157 px on a 338 px stage — under the 10 m line's label. On the shore-side trips at 375 the slate hides the first third of the descent line (the divers nearest the beach); the count tag, guide, depth label and ledge stay clear — accepted and noted..table-scroll; both pages one <h1>; both carry the fictional-disclosure colophon.This is a demonstration build by Sapience Analytics. Want one pointed at your customers?
Reefline Dive is a fictional business — a dive shop industry demonstration designed & built by Sapience Analytics, part of the Generative Assets collection. Back to the site · Index.