Build guide · Site 230 · Industry Demos III
How Standfast
was made
A conversion-focused landing page for a fictional small-group strength club in East Victoria Park — dark iron lit by chalk, priced honestly, with a Training Week Builder that turns a browser into a warm lead.
Concept
Standfast Strength Club sells coached small-group strength training to locals who are done with big-box gyms: desk workers who want to deadlift properly, parents getting strong again, over-50s told to “do some resistance training” with no idea where to start. The page has one job — book a free intro session — and every section funnels there. The design refuses the industry’s neon-transformation clichés: it’s premium the way a well-kept barbell is premium.
Palette — argued from the platform
#191B1EThe ground: black rubber flooring and steel. Everything sits on it.
#EAE7DFBody-ink on iron — the dust on every good bar. ~13.9:1 on the ground.
#4E7FD0The 20 kg competition plate. LINE/MARK + CTA fills only — at ~4.3:1 it never sets body text; small blue text uses a derived ink #8FB1E8 (~7.8:1).
#D9B23AThe 15 kg plate. One accent moment per section: eyebrows, focus rings, ticks (~8.4:1, safe at label sizes).
The ink/line split is the load-bearing decision on a dark ground: chalk carries every paragraph, the plate colours carry lines, marks and the two calibrated plates in the builder — exactly the trade’s own colour system, so the palette is literally the equipment’s.
Type
Get strong, properly.
Big Shoulders (display) — condensed athletic verticality, set tall, tight and uppercase. It reads like lane signage and gym-wall lettering without cosplaying a varsity tee. Weight 800 for the hero, 700 for section heads, 600 for eyebrows.
Coached every rep, capped at eight.
Albert Sans (body/UI) — a plain-spoken geometric that stays legible at 14–16px on the dark ground, with enough warmth that the honesty copy (“we’re coaches, not dietitians”) sounds human.
Signature — the Training Week Builder
Goal chips (get strong / build your engine / move well again) and a 2–4 days stepper fill a procedural SVG week grid. Each chosen day gets a loaded-bar session row: a chalk bar shaft spans the week, and on the session’s day the plates slide onto the sleeve — rounded-rect plates in calibration colours (blue 20 kg, yellow 15 kg, chalk-ring 10 kg), a collar, and a chalk puff when they land. The grid is built with createElementNS at render time; plate entrances are CSS transforms with per-plate stagger delays, so reduced-motion simply renders them seated. The weekly price updates live — $59 / $79 / $99, the same floors as the memberships grid, asserted in QA so the page never argues with itself — and “Book this week” pre-fills the intro form with goal + days, turning the toy into a warmer lead. The resting state is already mid-demonstration (get strong · 3 days · $79) so a static thumbnail reads instantly. Live state is exposed at window.__gym.
Techniques
- Procedural SVG everywhere a photo would be: the end-on plate stack in the hero (concentric calibrated plates over a knurl pattern), the week grid, and the chalk-line service-area map with a dashed ten-minute ring. No raster assets.
- JS-gated entrance choreography: reveals only exist under an
.jsclass with an IntersectionObserver + fallback timer, so no-JS never hides content;prefers-reduced-motionis honoured in CSS and JS. - Local-only form: front-end validation with per-field errors and aria-invalid, then a designed confirmation state — no fake network round-trip, and the demo says so in plain text.
- Mobile-first conversion frame: value prop + CTA + trust signal inside the first 375×667 viewport, header tel: link, sticky call/book bar with ≥44px targets.
Conversion decisions
- “Book a free intro session” is the only primary CTA — gyms convert on a low-risk first visit, not a contact form. The secondary CTA routes into the builder, which routes back to the form warmer.
- Price transparency as trust: the builder’s live price and the “the price is the price” memberships grid answer the question every gym site hides. The Foundations gate (“everyone starts here unless you can show us your squat”) is framed as care, not upsell.
- Form fields: name, phone, goal, days, free-text — nothing more. Goal/days mirror the builder so the handoff is lossless; “not sure yet” is a legal answer everywhere.
- Objection-shaped FAQ: unfit, inexperienced, cancellation terms, nutrition honesty — the four conversations that actually happen at a front desk.
Local SEO we’d implement for the real business
This demo page ships only WebPage schema naming it a demonstration — fictional review markup in a real index is dishonest. For a real club we’d ship:
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "ExerciseGym",
"name": "Standfast Strength Club",
"url": "https://standfaststrength.com.au",
"telephone": "+61 8 XXXX XXXX",
"priceRange": "$59–$99 per week",
"address": {
"@type": "PostalAddress",
"addressLocality": "East Victoria Park",
"addressRegion": "WA",
"postalCode": "6101",
"addressCountry": "AU"
},
"geo": { "@type": "GeoCoordinates", "latitude": -31.99, "longitude": 115.90 },
"openingHoursSpecification": [
{ "@type": "OpeningHoursSpecification",
"dayOfWeek": ["Monday","Tuesday","Wednesday","Thursday","Friday"],
"opens": "05:30", "closes": "19:30" },
{ "@type": "OpeningHoursSpecification",
"dayOfWeek": "Saturday", "opens": "07:00", "closes": "10:00" }
],
"aggregateRating": { "@type": "AggregateRating",
"ratingValue": "4.9", "reviewCount": "87" },
"review": [{ "@type": "Review",
"author": { "@type": "Person", "name": "Real member name" },
"reviewRating": { "@type": "Rating", "ratingValue": "5" },
"reviewBody": "Pulled from the club's Google Business Profile feed." }]
}
</script>
Plus: Google Business Profile with the review feed synced to the page, suburb landing content for Vic Park / Carlisle / Kensington, and FAQPage markup on the real FAQ.
What would change for the real client
- Booking: the intro form books straight into Mindbody, Glofox, TeamUp or Wodify — whichever runs their timetable — with class-capacity awareness, waitlists and SMS reminders.
- Billing: weekly debits via the platform’s payments or Stripe Billing; pause/cancel become self-serve flows matching the 30-day policy on the page.
- Proof: their coach photos and member PB wall replace the procedural art where it earns it; reviews pull live from Google with real names and dates.
- Measurement: call tracking on the tel: links, conversion events on builder→form handoffs, and a simple owner dashboard for cost-per-intro.
Iteration log
Pass 1 · Craft
The 375px screenshot was the ground truth this pass — three defects the DOM asserts sailed past:
- The header “Book a free intro” button leaked through its mobile
display:none— the later.btndisplay rule won the cascade at equal specificity. Escalated the hide; the phone pill also wrapped to three lines until it gotwhite-space:nowrap. - The trust line sat below the 375×667 fold. Shortened the hero copy, tightened hero margins and dropped the h1 clamp floor 46→40px — h1, CTA and trust signal now all clear the first viewport.
- In the week grid, Monday/Tuesday plate groups landed on top of the session-name text (both live at the left edge). Moved the bar line 32px below the name row and re-spaced rows (viewBox 322→352). Also stopped the hero eyebrow wrapping mid-phrase at 1440 by cutting its size and tracking.
Pass 2 · Depth
- Screenshot review caught the week’s worst bug: the big weekly price rendered at 16px because
#pp-amountwas a<span>and inherited the “/wk” unit style — the $ floated into the label above. Retyped the unit as its own class; $79 now lands at 76px. - Added the price-tick micro-interaction: on any change the figure drops in with a brief yellow flash (skipped under reduced motion), plus hover lift on membership tiers.
- Second-read details confirmed in the render: the chalk puff when plates land, the Saturday “open gym” ghost in the grid, and the calibration-colour footnote (blue 20 / yellow 15 / chalk-ring 10) that makes the plate palette literal.
Pass 3 · Hardening
- 28/28 headless asserts: drove every chip and both stepper ends — measured $59 (2 days, Mon/Thu), $79 (3, Mon/Wed/Fri), $99 (4, Mon/Tue/Thu/Sat), each equal to the memberships grid floors; goal swap re-renders sessions and changes pixels (drive-then-diff).
- Form: empty submit raises all three validation errors and focuses the first; filled submit renders the local confirmation (“received — thanks… we’d be in touch… we’d call”); “Book this week” pre-fills goal + days and sets the probe’s
prefilledstate. - 375: no horizontal overflow, wordmark in-viewport, sticky call/book bar inside the bottom 140px with 50px targets. Reduced-motion run renders the grid fully seated with zero page errors. Reserved-number sweep: every (08) 9xxx occurrence uses the 9000 exchange.
Standfast Strength Club is fictional. Prices ($59–$99/wk small-group, $199 Foundations, $95 1:1) are argued from the 2026 Perth small-group strength market; the phone number uses the collection’s reserved (08) 9000 0xxx pattern.