Generative Assets · Industry Demos XXVI · site 342
Ironstone Bend Wines — how it was made
The concept: a fictional family winery and cellar door on a bend of the Swan River in Herne Hill — five-wine flights at the bar, seated tastings with a board, a three-course long lunch, winemaker walk-throughs and a return coach from Perth. The audience is the couple planning a Sunday drive, the friend organising a fortieth for twelve, the office booking an end-of-quarter lunch, and the wine-club regular bringing visitors. The page has one job — a cellar-door booking request with the experience, head-count and add-ons already priced. It is the wave's dark site: the cellar door at 6pm, the last hour of light on the long table under the vines.
Palette — argued from the cellar at dusk
A shiraz-stained cellar for the ground, a parchment label for the ink, the gold foil on that label for the CTA, and the vine for the linework. It stays clear of the banned defaults: the accent is old gold on plum, not acid on black; there is no cream page with a terracotta serif; and the rules are vine-coloured, not broadsheet hairlines.
Measured contrast (WCAG arithmetic on the shipped hex, Pass 1)
Type
Cormorant Garamond (600 for the h1, 700 for the display line, badge and cards, 600 italic for the promise) is the serif on a good label — high contrast, long ascenders, a little grand without being stiff. Lato carries everything a guest has to actually read — chips, ledger, form, FAQ — at 17px on mobile, with 900 weight reserved for the scene tags. The typographic moment is the head-count set huge in Cormorant caps, "EIGHT GUESTS at the long table", the number word swapping live with the stepper and the italic tail changing to "at the bar" on the flight and "across two long tables" from thirteen.
Techniques
- The Long Table booking builder (signature) — one inline SVG scene (760 × 480 viewBox) with an HTML chalkboard badge overlaid on a drawn easel at the head of the table. The scene is a dusk sky gradient, two hill silhouettes, a lawn, three trellis posts and three wires with forty procedurally placed vine leaves (a seeded LCG for position, scale and flip; grape bunches on five of them). Each leaf's position lives on an outer
<g transform>and the CSSswaykeyframe runs on an inner group, so the W16 attribute rule holds; the sway swings ±6° with opacity between 0.72 and 1.0, a never-invisible floor above 0.55. The guests stepper redraws the table: a chair per guest split across the back and front rows, a glass per guest, boards per pair on the seated tasting, plates per guest plus a carafe and a lit candle on the long lunch (CSS flicker on the flame path, glow disc behind it). From thirteen guests a second table joins below the first, guests split evenly. The bar-flight chip fades the tables out and a tasting bar in — back bar with bottles, counter, five glasses in a line, a bottle, and up to ten drawn guests standing at the bar with the true count in the tag. Winemaker walk-through fades the winemaker into the lit barrel-hall doorway; the coach fades in at the gate bottom-left; celebration draws a cake and a bow-tied gold ribbon along the front edge with a reserved sign at the far end. - Dual-speed redraw + token invalidation. First draw pops chairs, boards, plates and glasses in with a 42 ms stagger capped at 1.3 s; every later state change redraws at 16 ms / 480 ms cap via a
firstDrawflag. Every handler bumps a token BEFORE the state swap so pop-ins scheduled by a previous drive are dropped, and the probe'ssettledflag flips true only after the last scheduled pop-in plus the fade. The lone rAF loop only counts frames for the probe (dt clamped at zero and capped at 50 ms); the breeze and flame are CSS, so reduced motion simply setsanimation:noneand never starts the loop. - The chalkboard badge. HTML over the drawn board, typeset in
cqwcontainer-query units against the scene, so the price, sub-line, menu lines and the flight declaration scale with the board at every width. Below 520px it ships the tight variant: menu lines drop, the sub-line wraps at 2.15cqw, the declaration stays. - Service-area map — a stylised SVG of the Swan Valley: coast on the left, the Darling Scarp dashed on the right, the Swan River drawn from Fremantle through Perth into the valley, the Great Northern Highway spine labelled along its length, West Swan Road, twelve named suburbs, Herne Hill starred, and the coach run from Fremantle via Cottesloe and Perth CBD as a dashed gold route animated by
stroke-dashoffset(attribute holds the dash array, CSS animates only the offset). - No libraries. Vanilla JS, inline SVG, CSS. Google Fonts only.
Honest numbers — the pricing contract
Every figure is pinned in the wave spec from Swan Valley cellar-door pricing, GST-inclusive, and every booking is a request confirmed by phone or email (stated on-card). All totals are whole dollars by construction.
| Line | Rate | Rule |
|---|---|---|
| Cellar-door flight | $15 pp | Five wines at the bar, 30 min. Bookings from 2. Declaration on badge + ledger: "the flight fee comes off any two bottles you take home" — a statement, never a subtraction. |
| Seated tasting with a board | $38 pp | Six wines, cheese & charcuterie board, 75 min. From 2. |
| Long lunch | $95 pp | Minimum 4 covers billed on the LUNCH LINE only (2–3 guests → note "billed at four covers — the kitchen sets the table for four", priced 4 × 95; at 4 no note). ≥16 guests → NOTE only "sixteen and up is a private function…" — no price change, no disable. |
| Guests | 2–40, ×1 | Second table drawn from 13, guests split evenly. |
| Winemaker walk-through | +$12 pp × actual guests | LIVE on seated and lunch; DISABLED on the flight with the reason "the walk-through pairs with a seated booking — pick seated or lunch". Restore semantics: flight locks it OFF; back to seated/lunch RESTORES the prior choice. |
| Return coach from Perth CBD / Fremantle | +$45 pp × actual guests | LIVE (default OFF) at ≥8 on every experience; OFF and DISABLED below 8 with "the coach runs for eight and up — under that a rideshare is the cheaper trip". Stepping back up to ≥8 RESTORES the prior choice. |
| Celebration set-up | +$40 flat | Live at every combination. Cake, ribbon and reserved sign drawn. |
| Private functions, weddings, corporate | no number | "Quoted on the brief — tell us the date and the head-count" — grid, ledger foot and FAQ. Bottles appear in copy only as the range $22–$48. |
Worked examples (all measured from the page and against a separately coded oracle)
- Rest state — seated, 8 guests, walk-through ON, coach OFF, celebration OFF → 8 × 38 + 8 × 12 = $400. Coach toggle live (unlocked) at 8. Measured $400 on the badge and the ledger.
- Bar flight, 4 guests, no toggles → $60; walk-through disabled with reason, coach disabled with reason, declaration on badge + ledger.
- Long lunch, 12, walk-through + coach → 1,140 + 144 + 540 = $1,824, no private-function note.
- Long lunch, 16, no toggles → $1,520 + private-function note; at 15 → $1,425, no note.
- Long lunch, 3 → billed at four → $380 + minimum note; at 4 → $380, no note. Long lunch, 3 + walk-through → 380 + 3 × 12 = $416 (add-on multiplies actual guests).
- Rest + celebration → $440.
- Coach restore — seated 8, coach ON ($760) → step to 7 → coach locked OFF ($350) → back to 8 → coach restored ON, $760.
- Walk-through restore — rest → flight chip → walk-through locked OFF, $120 → seated chip → restored ON, $400.
- Cheapest priced output: bar flight, 2 guests → $30 = the grid floor "$15 a head".
Conversion decisions
- Above the fold at 375: eyebrow, the h1 naming the trade and the area, a two-sentence lede with the three price anchors, "Book a table" gold + "Call (08) 9000 0342" ghost, and three trust chips — all before the scene, which follows on mobile.
- Click-to-call: tel link in the header (icon-only under 520px), the ghost CTA in the hero, the booking copy, the footer NAP, and a mobile sticky Call / Book a table bar.
- Booking form fields: name, phone, suburb (so we know which coach pick-up is closer), preferred date (a real
type=date, minimum tomorrow), an occasion select (day out / birthday / hen's & buck's / corporate / other — it changes what the floor staff prepare), and a message textarea pre-filled from the builder with the experience, head-count, add-ons and total. Front-end validation with per-field messages, aria-invalid, focus to the first error; a designed confirmation panel hidden on load and on an empty submit, shown only after a valid filled submit ("Thanks, Priya — we'd call you back within one business day…"). Nothing is sent, and the panel says so nowhere near a fake spinner. - Signature → form handoff: the builder writes the scenario into the message until the visitor edits it, and the ledger's "Send this as a booking request" jumps to the form.
- Honesty on the page: reserved phone (08) 9000 0342, suburb-level address, placeholder ABN 00 000 000 342 and producer's licence 6000000000, the fictional-disclosure colophon on both pages, demo reviews unmarked, and live JSON-LD limited to WebPage / CreativeWork / WebSite / BreadcrumbList / ListItem.
Local SEO we'd implement for the real business
For a real cellar door we'd ship Winery — the exact schema.org organization subtype under FoodEstablishment, which Google reads as a local business with opening hours and menu — rather than Restaurant (a winery's primary product is wine, not the kitchen; Winery keeps that signal while still carrying servesCuisine and hasMenu for the lunch). Aggregate rating and reviews would be fed from the live Google Business Profile, never typed in. This panel is the sample; it is not live on this demonstration.
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Winery",
"name": "Ironstone Bend Wines",
"url": "https://www.example-cellar-door.com.au/",
"telephone": "+61 8 9000 0342",
"image": "https://www.example-cellar-door.com.au/og/cellar-door.jpg",
"priceRange": "$15 – $95 per person",
"servesCuisine": "Modern Australian",
"address": {
"@type": "PostalAddress",
"streetAddress": "(real street address)",
"addressLocality": "Herne Hill",
"addressRegion": "WA",
"postalCode": "6056",
"addressCountry": "AU"
},
"geo": { "@type": "GeoCoordinates", "latitude": -31.83, "longitude": 116.0 },
"areaServed": ["Swan Valley", "Perth", "Fremantle"],
"openingHoursSpecification": [
{ "@type": "OpeningHoursSpecification", "dayOfWeek": ["Thursday","Friday","Saturday","Sunday","Monday"], "opens": "10:00", "closes": "17:00" }
],
"hasMenu": "https://www.example-cellar-door.com.au/long-lunch",
"makesOffer": [
{ "@type": "Offer", "name": "Cellar-door flight", "price": "15", "priceCurrency": "AUD" },
{ "@type": "Offer", "name": "Seated tasting with a board", "price": "38", "priceCurrency": "AUD" },
{ "@type": "Offer", "name": "Long lunch, three courses", "price": "95", "priceCurrency": "AUD" }
],
"aggregateRating": { "@type": "AggregateRating", "ratingValue": "4.9", "reviewCount": "184" },
"review": [
{
"@type": "Review",
"author": { "@type": "Person", "name": "(from the live reviews feed)" },
"reviewRating": { "@type": "Rating", "ratingValue": "5" },
"reviewBody": "(from the live reviews feed)"
}
],
"sameAs": ["https://www.facebook.com/…", "https://www.instagram.com/…"]
}
</script>
Alongside the schema: a Google Business Profile with the tasting and lunch as bookable services, "Swan Valley cellar door", "long lunch Swan Valley" and "winery Herne Hill" as the title/H1 targets, an Event schema for the release weekends, and a suburb-level page set for the coach pick-ups (Perth CBD, Fremantle) rather than one thin "areas we serve" page.
What would change for the real client
- Bookings: the builder posts into Rezdy or Bookeo — both take experience, head-count and add-ons as line items, hold a table against live availability, and send the confirmation the demo only promises. The request-only wording goes away once the calendar is real; the coach and walk-through become resources with their own capacity.
- Wine club and cellar-door sales: Commerce7 for the club, allocations and the cellar-door POS — the "flight fee comes off two bottles" credit and the member pricing become real rules at the counter, and the take-home range on the page reads from the live product list.
- Accounts: Xero for the invoices and the coach operator's bills; deposits from Rezdy/Bookeo reconcile against it.
- Their assets: photography of the actual table under the actual vines replaces the drawn scene (the drawing stays as the builder's live diagram), real reviews from Google, their producer's licence and ABN, and the real phone number.
Iteration log
Pass 1 — Craft
Measured every contrast pair with WCAG arithmetic on the shipped hex (figures above): gold on plum 7.48 against a ≈8.5 hypothesis and plum-on-gold 7.48 against ≈7.5 — both comfortably over 4.5, so no two-token remedy was needed for gold; the pre-minted #E2C27A still carries links, eyebrows and accent copy on lifted panels, and the pre-minted #A6BF97 carries the flight declaration. Two real defects surfaced in the first driven pass: the seeded leaf paths produced a doubled minus (--7) on flipped leaves, throwing SVG path errors on load — rebuilt the path string from explicit numbers; and the tables group had no fade class, so the bar-flight chip faded the bar in without fading the tables out (the state asserts were green while the scene overdrew). Screenshot review at 2×: front-row chairs were drawn at the table's front edge and read as pinned under it; the table slab was too shallow for two rows of glasses plus boards; the winemaker's label was struck through by the table's end. Deepened the slab to 30, shortened the table to clear the barrel-hall door, moved the front chairs below the front face, drew boards before glasses so the front glasses sit on them, moved the barrel in front of the door and the WINEMAKER label into the arch, and re-spaced the second table so the head-count tag clears the front chairs at 40. Copy read over the FAQ, the ledger foot (it now names the walk-through, coach and celebration in a sentence) and the trust strip.
Pass 2 — Depth
Enriched the signature: grape bunches on five of the forty leaves, the barrel-hall glow pulsing 0.7–1.0 behind the winemaker, the lit candle's flicker and glow disc on the lunch, a bow on the celebration ribbon at the head of the table and a reserved sign at the far end, and the second table's guests split evenly so "across two long tables" reads true. Second-read details: the chalkboard title "TONIGHT ON THE BEND" and its chalk rules; the display line's italic tail changing per experience; the stepper readout's small scale bump; the ledger foot's closing sentence swapping between the fifteen-minute hold and "walk in any day we're open" on the flight. Micro-interaction: the badge menu lines animate in and out with the toggles because they are the same DOM the ledger reads, so the board, the ledger and the pre-filled message never disagree — asserted as the three-surface declaration check on the flight. Refined the pop-in ease (cubic-bezier(.2,.9,.25,1.15)) and moved the celebration ribbon in bar mode onto the counter's front face, shortened to the counter's length, and the bar-mode cake beside the bottle so it never crowds the fifth glass.
Pass 3 — Hardening
375 masthead checked first: wordmark right edge (178) < phone-icon left (188) < CTA left (238), CTA right 340, one row, no wrap; scrollWidth 375, zero offenders outside SVGs; sticky Call / Book bar present; h1, gold CTA and the trust line all inside the first 740px. Badge tight variant at 375 measured 44 × 26 px price on an 80px board — sub-line dropped to 2.15cqw and allowed to wrap after the native-res review showed it a few pixels wider than the board. Label-collision sweep at native resolution across rest, flight-4, lunch-16 (walk-through + coach + celebration), seated-40, lunch-3 and flight-12: badge clear of the board title and rule, WINEMAKER inside the arch, COACH · PERTH RETURN under the coach and clear of the head-count tag, tag clear of the second table's front chairs at 40, cake clear of the first glass at 8 (touching at 40, by design). Reduced-motion path: no rAF loop, chairs and glasses placed at once, leaves and flame static, route static. Focus rings on every chip, toggle, stepper, field and link; keyboard arrows move the experience chips. rAF dt clamped at zero and capped. Rapid-fire drives (ten clicks in one evaluate, twice) settled clean with the token invalidation. Chanel rule: removed a second decorative chalk rule under the price and the lawn-grass strokes near the gate. Guide tables in .table-scroll, guide 375 clean, one h1 on both pages, colophon on both pages, JSON-LD allowlist on index, no console errors, no backdrop-filter. Final copy read over the confirmation panel, the coach FAQ and the private-function line.