Build guide · site 305 · Industry Demos XVIII
How Cadence Cycle Works was made
Cadence Cycle Works is a fictional Leederville bike servicing workshop, built as a conversion demo for real bike shops. The audience is commuters, weekend roadies, e-bike owners and parents with a shed of kids' bikes; the page has one job — a service booking with the tier already chosen. The signature, the Tune Stand, turns the service menu into the bike itself: pick a tier and the components it touches light up in chain-lube amber, with the itemised checklist and the exact price beside it.
Palette — argued from the alloy and the anodising
A workshop in daylight: pale alloy benches, graphite tools, anodised teal on the good kit, and chain lube as the only warm note. Amber is reserved strictly for highlight marks — it never carries text.
Type
Chivo 700–900 for display — mechanical neutrality with warmth, and its caps set like toolboard labels (TUNE · DRIVETRAIN · FULL STRIP is the display moment, carried through the tier chips and checklist card headers). Karla for body — a workhorse with enough character to keep the workshop voice human. Body is 17px on the site, every money figure set in Chivo 900 so prices read as spec-plate stamps.
Techniques
The Tune Stand is a single procedural SVG (760×460): a side-elevation bike clamped in a repair stand, drawn as distinct component groups — frame, drivetrain, chain, brakes, wheels, cockpit, plus a motor/battery/controller group that fades in only for the E-bike tier. Each group has a matching amber overlay; a tier swap token-invalidates any in-flight animation in the handler, before the state swap (the W25 stale-closure rule), then re-lights the overlays and redraws the chain. The chain is one path: on activation it dash-draws from the cassette around the chainring (stroke held at opacity 0 until drawn length > 0, so no round-cap dot — the W15 lesson), then switches to a 9/5 dash pattern that flows like links at ~11px/s with an opacity pulse floored at 0.66 — visibly alive at any thumbnail capture instant, no phase arithmetic. First render gets the full 1.4s entrance; every subsequent swap redraws in 450ms via a firstDraw flag (the W21 dual-speed rule). The rear wheel spins slowly whenever the chain is lit — a CSS rotation on a spoke group that carries no transform attribute, so no CSS-vs-presentation-attribute collision (W16). Everything else is CSS and vanilla JS; no libraries beyond Google Fonts.
The estimator is pure pinned arithmetic — tier price + $85 per tubeless wheel (0–2) + $75 per brake bleed (0–2) + $65 chain — always whole dollars, verified against an independently reimplemented oracle in the headless harness. Parts beyond consumables deliberately print no number: the at-cost declaration ("quoted and approved by text before we fit anything") appears on the checklist card and beside every total, including the form summary — the W25 honesty primitive. The kids' $45 line is stated separately and never becomes the grid floor; the "from $89" floor equals the estimator's cheapest adult output (the W12 consistency rule). State probe: window.__bikeshop with tier, extras, computed total and a settled flag.
Conversion decisions
- Above the fold at 375: name, "Bike servicing in Leederville", book CTA, call CTA and three proof chips all land inside the first viewport; the Tune Stand is the first thing after them. At 1200×750 the hero and the signature share the frame — checklist card top measured at 610px, inside the thumbnail (W19).
- Two CTAs, one priority: "Book a service" (filled teal) always outranks "Call" (outline). Mobile gets the sticky call/book bar — solid ground, no backdrop-filter anywhere on conversion chrome (W28).
- Tier → form handoff (W12): the Tune Stand pre-fills the tier select, the summary line and the notes field with the configured job and total, so the enquiry arrives warm. The notes field stops syncing the moment the visitor types (dirty flag).
- Form fields: name, phone, suburb (required — enough to call back), bike-type chips and a tier select (pre-filled — zero typing for the main path), notes optional. Confirmation is a designed panel, hidden on load and on empty submit, shown only after a valid submit — "we'd call you back within one business hour".
- Trust strip carries the brand promise (itemised, torqued, re-index guarantee) rather than generic badges; the reviews each name a tier and a suburb from the service-area map.
Pinned anchors & measured outputs
| Scenario | Arithmetic | Displayed |
|---|---|---|
| Rest state — Drivetrain Service, no extras | 159 | $159 ✓ measured |
| Basic / Full Strip / E-bike at rest | 89 · 289 · 95 | $89 · $289 · $95 ✓ |
| Drivetrain + tubeless ×2 | 159 + 170 | $329 ✓ |
| Full Strip + both bleeds + chain | 289 + 150 + 65 | $504 ✓ |
| Kids' Basic Tune (under-24″ wheels) | flat | $45 — its own line, not the floor |
| Parts beyond consumables | — | No number — at-cost declaration, by design |
All totals whole-dollar; every anchor is integer-safe. Extras steppers clamp at 0–2 (every boundary trivially reachable, W28). All six figures verified in headless Chrome against an oracle reimplemented independently of the page's own pricing function (W14).
Local SEO we'd implement for the real business
This demo page ships only honest WebPage/CreativeWork structured data, because the business is fictional. For a real workshop we'd ship BikeStore — schema.org's exact Store subtype for the trade — with real NAP, hours, geo and the live review feed:
{
"@context": "https://schema.org",
"@type": "BikeStore",
"name": "Cadence Cycle Works",
"description": "Bike servicing and repairs in Leederville — itemised services, e-bike health checks.",
"url": "https://cadencecycleworks.com.au",
"telephone": "+61-8-XXXX-XXXX",
"priceRange": "$45–$289",
"address": {
"@type": "PostalAddress",
"streetAddress": "1 Oxford Street",
"addressLocality": "Leederville",
"addressRegion": "WA",
"postalCode": "6007",
"addressCountry": "AU"
},
"geo": { "@type": "GeoCoordinates", "latitude": -31.9360, "longitude": 115.8420 },
"openingHoursSpecification": [
{ "@type": "OpeningHoursSpecification",
"dayOfWeek": ["Tuesday","Wednesday","Thursday","Friday"],
"opens": "08:00", "closes": "17:30" },
{ "@type": "OpeningHoursSpecification",
"dayOfWeek": "Saturday", "opens": "08:00", "closes": "14:00" }
],
"aggregateRating": { "@type": "AggregateRating", "ratingValue": "4.9", "reviewCount": "132" },
"review": [{
"@type": "Review",
"author": { "@type": "Person", "name": "Real Customer" },
"reviewRating": { "@type": "Rating", "ratingValue": "5" },
"reviewBody": "Pulled from the live Google Business Profile feed."
}]
}
What changes for the real client
- Booking: the form posts into their workshop system — Hubtiger or Workstand service modules are the trade standard; both expose booking APIs, and the tier/extras handoff maps straight onto their job templates.
- The text-first approval flow is real workshop software behaviour (Hubtiger quotes-by-SMS) — the page's promise matches the tooling, not marketing.
- Reviews: live Google Business Profile feed replaces the demo cards; the aggregate rating goes into the BikeStore schema above.
- Photos: their actual stand, tank and mechanics replace nothing structural — the SVG Tune Stand stays as the interactive menu; it's the differentiator, not a placeholder.
- Ops: Xero for invoicing, Lightspeed Retail if they run POS-side parts inventory — parts-at-cost pricing reads straight off the supplier feed.
Iteration log
Pass 1 Craft
- The "40 Nm" torque callout collided with the stand column and clipped — re-anchored left of the crank, pointer re-aimed at the bottom bracket.
- The chain's derailleur loop drifted off the drawn jockey wheels — path re-plotted through the pulley centres (216,350 → 205,343 → cassette).
- Uniform amber let the tyres shout over the chain: wheels now light at 0.6 opacity, brakes/cockpit at 0.78, drivetrain and chain at full 0.9 — the tier's focus reads first.
- The hero eyebrow wrapped at 375 ("EST. 2017" orphaned) — 11.5px/.11em tracking under 480px.
Pass 2 Depth
- Itemised mini-ledger added to the checklist card: once any extra is added, base tier + each extra appears as its own priced line above the total — the "every service itemised" promise made literal.
- Rear wheel now spins slowly (7s/rev) whenever the chain is lit — the bike reads as "being worked on", and the rotation lives on a spoke group with no transform attribute so CSS animation can't discard SVG positioning (W16).
- Chain settles into a 9/5 link-dash flow with an opacity pulse floored at 0.66 (measured 0.662 at capture) — never invisible at any screenshot instant (W18).
- Driven the full extras matrix against the oracle: $329 and $504 worked examples, tier swaps 89/289/95, stepper clamps at 2 — plus a rapid-fire drive (nine dispatched clicks in one evaluate, no settle) landing consistent at the oracle figure.
Pass 3 Hardening
- Phone
patternhad unescaped parens — invalid under Chrome's v-flag regex compilation (W19); escaped. - Chanel rule: removed the brake-lever stub from the cockpit group — noise beside the bar curl.
- 375 masthead measured, not eyeballed: wordmark right edge 238px vs call chip left 279px — no collision (W18); scrollWidth 375 on both pages; sticky call bar fixed with the tel link inside the bottom 140px.
- Reduced-motion path verified: no entrance, chain rendered fully drawn and static, no spin, settled flag immediate; empty submit raises 6 validation signals with the panel still hidden, filled submit shows it.
- Final harness: 40+ asserts green, zero console errors, settled-frame screenshots reviewed at 1200 and 375 plus a 2× native-zoom check of the SVG (W26).