Concept
Second Shot Espresso — the name is the promise: if the first shot isn't right, the second's on the house. Dark espresso warmth, the 6am glow of a bench light on timber, editorial food confidence with zero bistro cliché. The audience splits in two — Mount Hawthorn regulars checking hours on a phone, and office managers pricing Friday morning tea — so the hero sells the walk-in and the very next section sells the catering.
Palette — argued from the cup
The caramel is deliberately never used for body-size text — it draws the drizzle lines, plate rims and the total's progress rule, while #E4AF74 carries eyebrows and price labels. Matcha is rationed to a single role per section so the warm field stays warm.
Type
Fraunces (variable) is the food-world serif with the wonk to carry a café's charm without a chalkboard in sight. The axes are used deliberately: the h1 and wordmark run 'SOFT' 80–100, 'WONK' 1 — full soft-serif warmth with the wonky a and e; section headings pull back to 'SOFT' 55, 'WONK' 0 so the wonk stays a voice, not a tic. Work Sans does everything utilitarian — menu prices, form labels, body — at ≥16px on mobile.
The signature: the Catering Builder
A headcount stepper (6–120, ±6 per tap, direct entry allowed) and three package chips drive a procedural SVG counter scene. The scene composes rather than multiplies: each package has its own item grammar — morning tea lays trays (one per 8 heads), lights the urn at 12, stacks cups per 24, brings fruit out at 36 and a second urn at 60; lunch boxes stack kraft columns of four, chill juices at 24, add the urn at 30 and a fruit crate at 40; grazing lays boards (one per 12), brings fruit at 24 and stacks the cheese tower at 40. When the front bench fills, a back riser shelf appears and the layout wraps onto it — 6 heads is a single tray, 120 fills both shelves. Items are string-templated SVG groups re-laid on every change; new items animate in with a placed-down pop (staggered 45ms), and under prefers-reduced-motion the bench renders fully laid with no draw-on.
The maths is the conversion argument: the total is computed live (heads × rate, $150 minimum spend rounding small orders up, both shown honestly: 6 heads × $22.00 = $132.00 with a visible min-spend note) and a caramel drizzle rule under the total lengthens with the spend. At rest the scene shows a worked example — 24 heads, morning tea, $228.00, bench mid-laid — so the static preview already demonstrates the interaction. Builder → form handoff: one button carries the configured headcount, package and quoted total into the enquiry form, turning the toy into a warmer lead. Live state is exposed at window.__cafe for testability.
Conversion decisions
- Above the fold at 375px: name, trade + area in the h1's supporting line, primary CTA ("Build a catering quote") and three trust signals — all inside the first viewport, verified by probe (trust line bottom 627px in a 720px viewport).
- Two CTAs, ranked: catering (revenue per enquiry is 20–50× a coffee) leads; "Find us · hours" is the quiet second for the walk-in job.
- tel: everywhere a thumb lands: header pill (≥44px), footer, and a mobile sticky bar — Call now + Catering enquiry — fixed to the bottom 70px.
- Form fields are exactly what a caterer needs to quote: name, phone, date (min = today + 48h, matching the stated notice period), headcount, package, dietaries. No email field — cafés confirm by phone. Empty submit flags every missing field inline; a valid submit renders a local confirmation that restates the enquiry and plainly says no data was sent.
- Price consistency: the builder's cheapest output ($9.50/head morning tea) equals the services grid's office-catering floor; the min spend, delivery ring and notice period read identically in the builder, services, FAQ and footer.
Techniques
Vanilla single-file: no libraries. The bench scene, hero cup, service icons and delivery map are all inline SVG — the map is a stylised inner-north with a 5 km ring centred on Mount Hawthorn, nine named suburbs and Lake Monger as the one landmark locals orient by. Gradients do the 6am light: a radial caramel glow clipped by the pendant's cone polygon. Entrance choreography is CSS-only (staggered rise keyframes gated on a js-ready class, with a no-JS fallback that leaves everything visible). The [hidden]{display:none !important} guard protects the form/confirmation swap from any authored display rule.
Local SEO we'd implement for the real business
This demo page ships only WebPage schema naming it a demonstration — fictional structured data in a real index is dishonest markup. For a real café we'd ship:
{
"@context": "https://schema.org",
"@type": "CafeOrCoffeeShop",
"name": "…",
"servesCuisine": "Coffee, breakfast, catering",
"telephone": "+61 8 …",
"address": { "@type": "PostalAddress", "addressLocality": "Mount Hawthorn",
"addressRegion": "WA", "postalCode": "6016", "streetAddress": "…" },
"geo": { "@type": "GeoCoordinates", "latitude": -31.92, "longitude": 115.84 },
"openingHoursSpecification": [
{ "@type": "OpeningHoursSpecification",
"dayOfWeek": ["Monday","Tuesday","Wednesday","Thursday","Friday"],
"opens": "06:00", "closes": "14:00" },
{ "@type": "OpeningHoursSpecification", "dayOfWeek": "Saturday",
"opens": "06:30", "closes": "13:00" }
],
"hasOfferCatalog": { "@type": "OfferCatalog", "name": "Catering packages", … },
"aggregateRating": { … from the live Google reviews feed, never hand-written … }
}
Plus suburb-level landing content for the delivery ring (Leederville, North Perth, Wembley…), Google Business Profile with the catering menu attached, and review schema fed from the real reviews source only.
What would change for the real client
- Square or Lightspeed POS wired to the enquiry flow — catering invoices raised in the till system the café already reconciles, not a second inbox.
- Mr Yum / me&u for the QR menu if table service grows; the site's menu section reads from the same source so prices never fork.
- Foodbomb (or their current supplier portal) informs the catering margin maths — the builder's per-head rates would be set from live ingredient costs, reviewed quarterly.
- Their photography replacing the SVG scenes (the layout already holds photographic weight), their Google reviews feed replacing the demo copy, and the form posting to email + POS webhook with an honest auto-reply.
Iteration log
Pass 1 — Craft
Drove the full suite headless (40 asserts): rest state 24 × $9.50 = $228.00 exact, chips at 30 heads ($285 / $495 / $660 hand-checked), extremes 6 (min spend $150 applied, note visible) and 120 ($2,640.00, riser appears, zero items out of the 760-unit scene), empty submit (5 fields flagged) and filled submit (confirmation renders, form hides).
Looked at the screenshots and caught what the asserts couldn't: "THE SECOND SHOT" caption clipped at the hero art's right edge; the free-shot glass floating above the ground line with no shadow; the 375 trust line landing 14px below the fold because the hero sub ran 8 lines. Shortened the sub to three lines, tightened mobile hero padding, grounded the glass, moved the caption — and raised the header tel pill to 44px min-height.
Pass 2 — Depth
Wired the drizzle rule under the total to the spend: a caramel fill (a real block element — W12's invisible-inline-span lesson) that lengthens from 6% to full as the total approaches the $2,640 maximum, asserted at 6 heads (~24px) and 120 heads (~394px). Added the second-read details: the two house cups that never leave the bench (the second shot, always poured), urn steam on a 3.2s ease loop, and a caption easter egg past 70 heads that points standing-function crowds at the room hire. Stepper buttons got press-down feedback.
Screenshots reviewed: desktop hero now clean at the right edge, 375 shows h1 + CTA + all three trust chips above the fold with the sticky bar clear of the content.
Pass 3 — Hardening
Reduced-motion verified end-to-end: rm class set, hero settled at opacity 1, bench fully laid with zero draw-on items, steam stilled, smooth-scroll dropped. Focus-visible matcha outlines on every control; FAQ toggles keyboard-clean; date input min enforces the 48-hour notice in the picker itself. Chanel rule: removed the "Scene drawn live" tag from the bench caption — the scene demonstrates itself. Final copy read for internal consistency: $9.50 floor, $150 min spend, 5 km ring, 48 hours and 46/70 capacity each appear identically everywhere they're quoted. Final suite: all asserts green, zero console errors, one h1 per page, 375 scrollWidth clean on both pages.