Gym — Membership Pricing
A high-energy gym membership pricing page with a monthly versus annual billing toggle that animates every price and reveals the yearly savings live. Four tier cards (Basic, Plus, Unlimited, Black) carry feature checklists and a highlighted most-popular plan, backed by a stackable class-pack add-on row, a sticky-header comparison table covering every feature, and a single-open FAQ accordion. Built with vanilla JS for the toggle, accordion and CTA toasts.
MCP
Code
:root {
--bg: #0d0f12;
--surface: #15181d;
--surface-2: #1d2127;
--elevated: #23282f;
--ink: #f4f6f8;
--ink-2: #c2c8d0;
--muted: #8b929c;
--neon: #c6ff3a;
--neon-d: #a6e016;
--neon-50: rgba(198, 255, 58, 0.12);
--orange: #ff6a2b;
--orange-soft: rgba(255, 106, 43, 0.14);
--line: rgba(255, 255, 255, 0.08);
--line-2: rgba(255, 255, 255, 0.16);
--ok: #34d399;
--warn: #fbbf24;
--danger: #f87171;
--r-sm: 8px;
--r-md: 14px;
--r-lg: 20px;
--sh-1: 0 1px 0 rgba(255, 255, 255, 0.04) inset, 0 8px 24px rgba(0, 0, 0, 0.4);
--sh-2: 0 1px 0 rgba(255, 255, 255, 0.06) inset, 0 20px 50px rgba(0, 0, 0, 0.55);
}
*,
*::before,
*::after {
box-sizing: border-box;
}
html {
scroll-behavior: smooth;
}
body {
margin: 0;
background: var(--bg);
color: var(--ink);
font-family: "Inter", system-ui, -apple-system, sans-serif;
line-height: 1.5;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.page {
max-width: 1140px;
margin: 0 auto;
padding: 22px 22px 64px;
}
a {
color: inherit;
text-decoration: none;
}
:focus-visible {
outline: 3px solid var(--neon);
outline-offset: 2px;
border-radius: var(--r-sm);
}
/* ---------- Buttons ---------- */
.btn {
font-family: inherit;
font-weight: 800;
font-size: 0.95rem;
letter-spacing: 0.01em;
border: none;
cursor: pointer;
border-radius: var(--r-md);
padding: 14px 20px;
transition: transform 0.12s ease, background 0.15s ease, box-shadow 0.15s ease,
border-color 0.15s ease;
}
.btn:active {
transform: translateY(1px) scale(0.99);
}
.btn-neon {
background: var(--neon);
color: #0d0f12;
box-shadow: 0 8px 22px var(--neon-50);
}
.btn-neon:hover {
background: var(--neon-d);
}
.btn-outline {
background: transparent;
color: var(--ink);
border: 1.5px solid var(--line-2);
}
.btn-outline:hover {
border-color: var(--neon);
color: var(--neon);
}
.btn-ghost {
background: var(--surface-2);
color: var(--ink);
border: 1px solid var(--line);
padding: 10px 16px;
font-size: 0.85rem;
}
.btn-ghost:hover {
border-color: var(--line-2);
}
.btn-mini {
background: var(--neon);
color: #0d0f12;
padding: 9px 18px;
font-size: 0.82rem;
border-radius: var(--r-sm);
}
.btn-mini:hover {
background: var(--neon-d);
}
/* ---------- Masthead ---------- */
.masthead {
display: flex;
align-items: center;
gap: 22px;
padding: 6px 4px 26px;
}
.brand {
display: flex;
align-items: center;
gap: 9px;
font-weight: 900;
}
.brand-mark {
color: var(--neon);
font-size: 1.1rem;
}
.brand-name {
letter-spacing: 0.18em;
font-size: 1.05rem;
}
.masthead-nav {
display: flex;
gap: 24px;
margin-left: 18px;
font-size: 0.9rem;
color: var(--ink-2);
font-weight: 600;
}
.masthead-nav a:hover {
color: var(--neon);
}
.masthead .btn-ghost {
margin-left: auto;
}
/* ---------- Hero ---------- */
.hero {
text-align: center;
padding: 30px 0 14px;
}
.eyebrow {
text-transform: uppercase;
letter-spacing: 0.28em;
font-size: 0.72rem;
font-weight: 800;
color: var(--neon);
margin: 0 0 14px;
}
.hero-title {
font-size: clamp(2.6rem, 7vw, 4.6rem);
line-height: 0.98;
font-weight: 900;
letter-spacing: -0.02em;
margin: 0 0 18px;
}
.hero-title .accent {
color: var(--neon);
}
.hero-sub {
max-width: 560px;
margin: 0 auto 30px;
color: var(--ink-2);
font-size: 1.05rem;
}
/* ---------- Billing toggle ---------- */
.billing {
display: inline-flex;
align-items: center;
gap: 12px;
background: var(--surface);
border: 1px solid var(--line);
border-radius: 999px;
padding: 8px 14px;
box-shadow: var(--sh-1);
flex-wrap: wrap;
justify-content: center;
}
.billing-label {
font-weight: 700;
font-size: 0.9rem;
color: var(--muted);
transition: color 0.15s ease;
}
.billing-label.is-on {
color: var(--ink);
}
.switch {
position: relative;
width: 56px;
height: 30px;
border-radius: 999px;
border: none;
cursor: pointer;
background: var(--elevated);
padding: 0;
transition: background 0.18s ease;
}
.switch[aria-checked="true"] {
background: var(--neon);
}
.switch-thumb {
position: absolute;
top: 3px;
left: 3px;
width: 24px;
height: 24px;
border-radius: 50%;
background: var(--ink);
transition: transform 0.2s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.switch[aria-checked="true"] .switch-thumb {
transform: translateX(26px);
background: #0d0f12;
}
.billing-save {
font-weight: 800;
font-size: 0.75rem;
text-transform: uppercase;
letter-spacing: 0.04em;
color: var(--neon);
background: var(--neon-50);
padding: 5px 11px;
border-radius: 999px;
}
/* ---------- Tiers ---------- */
.tiers {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 16px;
margin-top: 46px;
}
.tier {
position: relative;
background: var(--surface);
border: 1px solid var(--line);
border-radius: var(--r-lg);
padding: 26px 22px 24px;
display: flex;
flex-direction: column;
box-shadow: var(--sh-1);
transition: transform 0.16s ease, border-color 0.16s ease, box-shadow 0.16s ease;
}
.tier:hover {
transform: translateY(-4px);
border-color: var(--line-2);
box-shadow: var(--sh-2);
}
.tier.is-popular {
border-color: var(--neon);
background: linear-gradient(180deg, var(--neon-50), var(--surface) 42%);
box-shadow: 0 0 0 1px var(--neon-d), var(--sh-2);
}
.tier-black {
background: linear-gradient(180deg, #0a0b0d, var(--surface-2));
border-color: var(--line-2);
}
.ribbon {
position: absolute;
top: -12px;
left: 50%;
transform: translateX(-50%);
background: var(--neon);
color: #0d0f12;
font-size: 0.68rem;
font-weight: 900;
text-transform: uppercase;
letter-spacing: 0.08em;
padding: 5px 14px;
border-radius: 999px;
white-space: nowrap;
}
.ribbon-dark {
background: var(--orange);
color: #0d0f12;
}
.tier-head {
margin-bottom: 16px;
}
.tier-name {
margin: 0;
font-size: 1.4rem;
font-weight: 900;
letter-spacing: -0.01em;
}
.tier-tag {
margin: 4px 0 0;
color: var(--muted);
font-size: 0.82rem;
font-weight: 500;
}
.price {
display: flex;
align-items: baseline;
gap: 2px;
margin-bottom: 4px;
}
.price-cur {
font-size: 1.3rem;
font-weight: 800;
color: var(--ink-2);
align-self: flex-start;
margin-top: 6px;
}
.price-amt {
font-size: 3rem;
font-weight: 900;
letter-spacing: -0.03em;
line-height: 1;
}
.price-per {
color: var(--muted);
font-weight: 700;
font-size: 0.95rem;
}
.price-note {
font-size: 0.78rem;
color: var(--muted);
margin: 0 0 18px;
min-height: 1.1em;
}
.features {
list-style: none;
margin: 0 0 22px;
padding: 0;
display: flex;
flex-direction: column;
gap: 11px;
flex: 1;
}
.features li {
position: relative;
padding-left: 26px;
font-size: 0.88rem;
color: var(--ink-2);
}
.features li::before {
position: absolute;
left: 0;
top: -1px;
font-weight: 900;
font-size: 0.9rem;
}
.features li.ok::before {
content: "✓";
color: var(--neon);
}
.features li.no {
color: var(--muted);
opacity: 0.7;
}
.features li.no::before {
content: "✕";
color: var(--muted);
}
.tier .btn {
width: 100%;
}
/* ---------- Add-ons ---------- */
.addons {
margin-top: 56px;
background: var(--surface);
border: 1px solid var(--line);
border-radius: var(--r-lg);
padding: 30px 26px;
box-shadow: var(--sh-1);
}
.addons-head {
margin-bottom: 22px;
}
.addons-title {
margin: 0;
font-size: 1.5rem;
font-weight: 900;
letter-spacing: -0.01em;
}
.addons-sub {
margin: 6px 0 0;
color: var(--muted);
font-size: 0.9rem;
}
.addon-row {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 14px;
}
.addon {
display: flex;
align-items: center;
gap: 14px;
background: var(--surface-2);
border: 1px solid var(--line);
border-radius: var(--r-md);
padding: 16px;
transition: border-color 0.15s ease, transform 0.15s ease;
}
.addon:hover {
border-color: var(--line-2);
transform: translateY(-2px);
}
.addon-icon {
font-size: 1.6rem;
width: 48px;
height: 48px;
display: grid;
place-items: center;
background: var(--elevated);
border-radius: var(--r-sm);
flex-shrink: 0;
}
.addon-body {
flex: 1;
min-width: 0;
}
.addon-body h3 {
margin: 0;
font-size: 0.95rem;
font-weight: 800;
}
.addon-body p {
margin: 3px 0 0;
font-size: 0.78rem;
color: var(--muted);
}
.addon-price {
font-weight: 900;
font-size: 1.15rem;
white-space: nowrap;
}
.addon-price span {
font-size: 0.72rem;
font-weight: 600;
color: var(--muted);
}
/* ---------- Section titles ---------- */
.section-title {
font-size: clamp(1.6rem, 4vw, 2.2rem);
font-weight: 900;
letter-spacing: -0.02em;
margin: 0 0 22px;
text-align: center;
}
/* ---------- Comparison table ---------- */
.compare {
margin-top: 64px;
}
.table-wrap {
overflow-x: auto;
border: 1px solid var(--line);
border-radius: var(--r-lg);
background: var(--surface);
box-shadow: var(--sh-1);
}
.cmp {
width: 100%;
border-collapse: collapse;
min-width: 620px;
}
.cmp th,
.cmp td {
padding: 15px 16px;
text-align: center;
font-size: 0.88rem;
border-bottom: 1px solid var(--line);
}
.cmp thead th {
font-size: 0.95rem;
font-weight: 900;
color: var(--ink);
background: var(--surface-2);
position: sticky;
top: 0;
}
.cmp tbody th[scope="row"],
.cmp .feat-col {
text-align: left;
font-weight: 700;
color: var(--ink-2);
}
.cmp td {
color: var(--ink-2);
font-weight: 600;
}
.cmp .col-pop {
background: var(--neon-50);
}
.cmp thead .col-pop {
color: var(--neon);
}
.cmp .check {
color: var(--neon);
font-weight: 900;
}
.cmp .x {
color: var(--muted);
}
.cmp tbody tr:last-child th,
.cmp tbody tr:last-child td {
border-bottom: none;
}
/* ---------- FAQ accordion ---------- */
.faq {
margin-top: 64px;
max-width: 760px;
margin-left: auto;
margin-right: auto;
}
.accordion {
display: flex;
flex-direction: column;
gap: 12px;
}
.acc-item {
background: var(--surface);
border: 1px solid var(--line);
border-radius: var(--r-md);
overflow: hidden;
transition: border-color 0.15s ease;
}
.acc-item.is-open {
border-color: var(--line-2);
}
.acc-trigger {
width: 100%;
background: none;
border: none;
cursor: pointer;
color: var(--ink);
font-family: inherit;
font-size: 1rem;
font-weight: 700;
text-align: left;
padding: 18px 20px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 14px;
}
.acc-trigger:hover {
color: var(--neon);
}
.acc-icon {
font-size: 1.4rem;
font-weight: 400;
color: var(--neon);
transition: transform 0.2s ease;
flex-shrink: 0;
}
.acc-item.is-open .acc-icon {
transform: rotate(45deg);
}
.acc-panel {
max-height: 0;
overflow: hidden;
transition: max-height 0.28s ease;
}
.acc-panel p {
margin: 0;
padding: 0 20px 20px;
color: var(--ink-2);
font-size: 0.92rem;
}
/* ---------- Footer ---------- */
.foot {
margin-top: 64px;
text-align: center;
color: var(--muted);
font-size: 0.8rem;
}
/* ---------- Toast ---------- */
.toast {
position: fixed;
left: 50%;
bottom: 28px;
transform: translateX(-50%) translateY(24px);
background: var(--neon);
color: #0d0f12;
font-weight: 800;
font-size: 0.9rem;
padding: 13px 22px;
border-radius: 999px;
box-shadow: 0 16px 40px rgba(0, 0, 0, 0.55);
opacity: 0;
pointer-events: none;
transition: transform 0.28s cubic-bezier(0.34, 1.56, 0.64, 1), opacity 0.22s ease;
z-index: 50;
max-width: calc(100vw - 32px);
text-align: center;
}
.toast.show {
opacity: 1;
transform: translateX(-50%) translateY(0);
}
/* ---------- Responsive ---------- */
@media (max-width: 940px) {
.tiers {
grid-template-columns: repeat(2, 1fr);
}
.addon-row {
grid-template-columns: 1fr;
}
}
@media (max-width: 520px) {
.page {
padding: 16px 14px 48px;
}
.masthead {
flex-wrap: wrap;
gap: 12px;
}
.masthead-nav {
order: 3;
margin-left: 0;
width: 100%;
justify-content: space-between;
gap: 10px;
}
.masthead .btn-ghost {
margin-left: 0;
}
.tiers {
grid-template-columns: 1fr;
}
.billing {
gap: 10px;
}
.billing-save {
width: 100%;
text-align: center;
order: 4;
}
.addon {
flex-wrap: wrap;
}
.addon-body {
flex-basis: calc(100% - 62px);
}
.addon-price {
margin-left: 62px;
}
.addon .btn-mini {
margin-left: auto;
}
}(function () {
"use strict";
/* ---------- Toast helper ---------- */
var toastEl = document.getElementById("toast");
var toastTimer = null;
function toast(msg) {
if (!toastEl) return;
toastEl.textContent = msg;
toastEl.classList.add("show");
clearTimeout(toastTimer);
toastTimer = setTimeout(function () {
toastEl.classList.remove("show");
}, 2400);
}
/* ---------- Billing toggle ---------- */
var toggle = document.getElementById("billingToggle");
var saveBadge = document.getElementById("billingSave");
var labels = document.querySelectorAll(".billing-label");
var amounts = document.querySelectorAll(".price-amt");
var notes = document.querySelectorAll(".price-note");
function setBilling(annual) {
toggle.setAttribute("aria-checked", annual ? "true" : "false");
toggle.setAttribute(
"aria-label",
annual ? "Switch to monthly billing" : "Switch to annual billing"
);
labels.forEach(function (label) {
var side = label.getAttribute("data-side");
label.classList.toggle(
"is-on",
(side === "annual" && annual) || (side === "monthly" && !annual)
);
});
amounts.forEach(function (amt) {
var target = annual
? amt.getAttribute("data-annual")
: amt.getAttribute("data-monthly");
animateNumber(amt, parseInt(amt.textContent, 10) || 0, parseInt(target, 10));
});
notes.forEach(function (note) {
note.textContent = annual
? note.getAttribute("data-annual-note")
: "Billed monthly";
});
saveBadge.textContent = annual ? "Save up to 25%" : "Switch to annual & save";
}
function animateNumber(el, from, to) {
if (from === to) {
el.textContent = to;
return;
}
var start = performance.now();
var dur = 320;
function step(now) {
var p = Math.min((now - start) / dur, 1);
var eased = 1 - Math.pow(1 - p, 3);
el.textContent = Math.round(from + (to - from) * eased);
if (p < 1) requestAnimationFrame(step);
}
requestAnimationFrame(step);
}
if (toggle) {
// Default state is annual (aria-checked="true" in markup).
setBilling(true);
toggle.addEventListener("click", function () {
var nowAnnual = toggle.getAttribute("aria-checked") !== "true";
setBilling(nowAnnual);
toast(nowAnnual ? "Annual billing — you save up to 25%" : "Monthly billing selected");
});
}
/* ---------- FAQ accordion ---------- */
var triggers = document.querySelectorAll(".acc-trigger");
triggers.forEach(function (trigger) {
trigger.addEventListener("click", function () {
var item = trigger.closest(".acc-item");
var panel = item.querySelector(".acc-panel");
var isOpen = item.classList.contains("is-open");
// Close all (single-open accordion).
document.querySelectorAll(".acc-item.is-open").forEach(function (open) {
if (open !== item) {
open.classList.remove("is-open");
open.querySelector(".acc-trigger").setAttribute("aria-expanded", "false");
open.querySelector(".acc-panel").style.maxHeight = null;
}
});
if (isOpen) {
item.classList.remove("is-open");
trigger.setAttribute("aria-expanded", "false");
panel.style.maxHeight = null;
} else {
item.classList.add("is-open");
trigger.setAttribute("aria-expanded", "true");
panel.style.maxHeight = panel.scrollHeight + "px";
}
});
});
/* ---------- CTA toasts ---------- */
document.querySelectorAll("[data-cta]").forEach(function (btn) {
btn.addEventListener("click", function () {
var label = btn.getAttribute("data-cta");
var annual = toggle && toggle.getAttribute("aria-checked") === "true";
var suffix = /plan$/.test(label) ? " — " + (annual ? "annual" : "monthly") + " selected" : "";
toast(label + suffix);
});
});
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Ironhaus — Membership Pricing</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap"
rel="stylesheet"
/>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="page">
<header class="masthead">
<div class="brand">
<span class="brand-mark" aria-hidden="true">▲</span>
<span class="brand-name">IRONHAUS</span>
</div>
<nav class="masthead-nav" aria-label="Primary">
<a href="#tiers">Plans</a>
<a href="#compare">Compare</a>
<a href="#faq">FAQ</a>
</nav>
<button class="btn btn-ghost" data-cta="Talk to a coach" type="button">
Talk to a coach
</button>
</header>
<section class="hero">
<p class="eyebrow">Membership</p>
<h1 class="hero-title">Train hard.<br /><span class="accent">Pay less.</span></h1>
<p class="hero-sub">
One gym. Three ways in. Unlimited classes, recovery suites and 24/7 floor
access — pick the membership that matches your grind.
</p>
<div class="billing" role="group" aria-label="Billing period">
<span class="billing-label" data-side="monthly" aria-hidden="true">Monthly</span>
<button
id="billingToggle"
class="switch"
role="switch"
aria-checked="true"
aria-label="Switch to monthly billing"
type="button"
>
<span class="switch-thumb"></span>
</button>
<span class="billing-label is-on" data-side="annual" aria-hidden="true">Annual</span>
<span class="billing-save" id="billingSave">Save up to 25%</span>
</div>
</section>
<section id="tiers" class="tiers" aria-label="Membership tiers">
<!-- BASIC -->
<article class="tier">
<div class="tier-head">
<h2 class="tier-name">Basic</h2>
<p class="tier-tag">Off-peak floor access</p>
</div>
<div class="price">
<span class="price-cur">$</span>
<span class="price-amt" data-monthly="29" data-annual="22">29</span>
<span class="price-per">/mo</span>
</div>
<p class="price-note" data-annual-note="Billed $264 yearly · save $84">
Billed monthly
</p>
<ul class="features">
<li class="ok">Gym floor, 6am–4pm</li>
<li class="ok">Locker & shower access</li>
<li class="ok">Fitness app workouts</li>
<li class="no">Group classes</li>
<li class="no">Recovery suite</li>
<li class="no">Guest passes</li>
</ul>
<button class="btn btn-outline" data-cta="Basic plan" type="button">
Choose Basic
</button>
</article>
<!-- PLUS (popular) -->
<article class="tier is-popular">
<span class="ribbon">Most popular</span>
<div class="tier-head">
<h2 class="tier-name">Plus</h2>
<p class="tier-tag">All-day access + classes</p>
</div>
<div class="price">
<span class="price-cur">$</span>
<span class="price-amt" data-monthly="59" data-annual="46">59</span>
<span class="price-per">/mo</span>
</div>
<p class="price-note" data-annual-note="Billed $552 yearly · save $156">
Billed monthly
</p>
<ul class="features">
<li class="ok">Unlimited gym floor, all hours</li>
<li class="ok">Locker & shower access</li>
<li class="ok">All group classes included</li>
<li class="ok">1 recovery suite session / mo</li>
<li class="ok">2 guest passes / mo</li>
<li class="no">Personal coaching credits</li>
</ul>
<button class="btn btn-neon" data-cta="Plus plan" type="button">
Choose Plus
</button>
</article>
<!-- UNLIMITED -->
<article class="tier">
<div class="tier-head">
<h2 class="tier-name">Unlimited</h2>
<p class="tier-tag">Everything, no limits</p>
</div>
<div class="price">
<span class="price-cur">$</span>
<span class="price-amt" data-monthly="89" data-annual="69">89</span>
<span class="price-per">/mo</span>
</div>
<p class="price-note" data-annual-note="Billed $828 yearly · save $240">
Billed monthly
</p>
<ul class="features">
<li class="ok">24/7 access to all locations</li>
<li class="ok">Unlimited classes + priority booking</li>
<li class="ok">Unlimited recovery suite</li>
<li class="ok">4 guest passes / mo</li>
<li class="ok">2 coaching credits / mo</li>
<li class="ok">Free InBody scans</li>
</ul>
<button class="btn btn-outline" data-cta="Unlimited plan" type="button">
Choose Unlimited
</button>
</article>
<!-- BLACK -->
<article class="tier tier-black">
<span class="ribbon ribbon-dark">Invite</span>
<div class="tier-head">
<h2 class="tier-name">Black</h2>
<p class="tier-tag">Concierge performance</p>
</div>
<div class="price">
<span class="price-cur">$</span>
<span class="price-amt" data-monthly="189" data-annual="159">189</span>
<span class="price-per">/mo</span>
</div>
<p class="price-note" data-annual-note="Billed $1,908 yearly · save $360">
Billed monthly
</p>
<ul class="features">
<li class="ok">Everything in Unlimited</li>
<li class="ok">Weekly 1:1 coaching</li>
<li class="ok">Personalized nutrition plan</li>
<li class="ok">Massage & physio credits</li>
<li class="ok">Reserved locker</li>
<li class="ok">Unlimited guest passes</li>
</ul>
<button class="btn btn-outline" data-cta="Black plan" type="button">
Request invite
</button>
</article>
</section>
<section class="addons" aria-label="Class pack add-ons">
<div class="addons-head">
<h2 class="addons-title">Class packs & add-ons</h2>
<p class="addons-sub">Stack these onto any membership. Cancel anytime.</p>
</div>
<div class="addon-row">
<div class="addon">
<div class="addon-icon" aria-hidden="true">🥊</div>
<div class="addon-body">
<h3>5-Class HIIT Pack</h3>
<p>Bookable across any branded studio class.</p>
</div>
<div class="addon-price">$39<span>/pack</span></div>
<button class="btn btn-mini" data-cta="HIIT class pack" type="button">Add</button>
</div>
<div class="addon">
<div class="addon-icon" aria-hidden="true">🧘</div>
<div class="addon-body">
<h3>Recovery Day Pass</h3>
<p>Sauna, cold plunge & compression boots.</p>
</div>
<div class="addon-price">$19<span>/day</span></div>
<button class="btn btn-mini" data-cta="Recovery day pass" type="button">Add</button>
</div>
<div class="addon">
<div class="addon-icon" aria-hidden="true">🏋️</div>
<div class="addon-body">
<h3>PT Block (4 sessions)</h3>
<p>One-on-one with a certified coach.</p>
</div>
<div class="addon-price">$149<span>/block</span></div>
<button class="btn btn-mini" data-cta="PT session block" type="button">Add</button>
</div>
</div>
</section>
<section id="compare" class="compare" aria-label="Plan comparison">
<h2 class="section-title">Compare every plan</h2>
<div class="table-wrap" role="region" aria-label="Feature comparison table" tabindex="0">
<table class="cmp">
<thead>
<tr>
<th scope="col" class="feat-col">Feature</th>
<th scope="col">Basic</th>
<th scope="col" class="col-pop">Plus</th>
<th scope="col">Unlimited</th>
<th scope="col">Black</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Gym floor access</th>
<td>Off-peak</td>
<td class="col-pop">All hours</td>
<td>24/7</td>
<td>24/7</td>
</tr>
<tr>
<th scope="row">Group classes</th>
<td class="x">—</td>
<td class="col-pop check">✓</td>
<td class="check">✓</td>
<td class="check">✓</td>
</tr>
<tr>
<th scope="row">Recovery suite</th>
<td class="x">—</td>
<td class="col-pop">1 / mo</td>
<td>Unlimited</td>
<td>Unlimited</td>
</tr>
<tr>
<th scope="row">Guest passes</th>
<td class="x">—</td>
<td class="col-pop">2 / mo</td>
<td>4 / mo</td>
<td>Unlimited</td>
</tr>
<tr>
<th scope="row">Coaching credits</th>
<td class="x">—</td>
<td class="col-pop x">—</td>
<td>2 / mo</td>
<td>Weekly 1:1</td>
</tr>
<tr>
<th scope="row">Nutrition plan</th>
<td class="x">—</td>
<td class="col-pop x">—</td>
<td class="x">—</td>
<td class="check">✓</td>
</tr>
<tr>
<th scope="row">Priority booking</th>
<td class="x">—</td>
<td class="col-pop x">—</td>
<td class="check">✓</td>
<td class="check">✓</td>
</tr>
</tbody>
</table>
</div>
</section>
<section id="faq" class="faq" aria-label="Frequently asked questions">
<h2 class="section-title">Questions, answered</h2>
<div class="accordion">
<div class="acc-item">
<button class="acc-trigger" aria-expanded="false" type="button">
<span>Can I freeze my membership?</span>
<span class="acc-icon" aria-hidden="true">+</span>
</button>
<div class="acc-panel" role="region">
<p>
Yes — every plan above Basic includes up to 3 months of freeze per
year at no charge. Pause from the app and your billing stops the same day.
</p>
</div>
</div>
<div class="acc-item">
<button class="acc-trigger" aria-expanded="false" type="button">
<span>Is there a joining fee?</span>
<span class="acc-icon" aria-hidden="true">+</span>
</button>
<div class="acc-panel" role="region">
<p>
No joining fee on annual plans. Monthly plans carry a one-time $25
activation that we waive whenever a coach refers you.
</p>
</div>
</div>
<div class="acc-item">
<button class="acc-trigger" aria-expanded="false" type="button">
<span>What does annual billing actually save?</span>
<span class="acc-icon" aria-hidden="true">+</span>
</button>
<div class="acc-panel" role="region">
<p>
Annual members pay up to 25% less per month — for example Plus drops
from $59 to $46/mo. Flip the toggle at the top to see each plan update live.
</p>
</div>
</div>
<div class="acc-item">
<button class="acc-trigger" aria-expanded="false" type="button">
<span>Can I switch plans later?</span>
<span class="acc-icon" aria-hidden="true">+</span>
</button>
<div class="acc-panel" role="region">
<p>
Upgrade instantly and we prorate the difference. Downgrades take effect
at your next billing date — no penalties, no awkward phone calls.
</p>
</div>
</div>
</div>
</section>
<footer class="foot">
<p>Ironhaus Strength & Conditioning · Fictional demo · No real charges occur.</p>
</footer>
</div>
<div id="toast" class="toast" role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>Membership Pricing
A self-contained membership pricing page for a fictional performance gym, Ironhaus. The hero sits above a pill-shaped billing switch: flip it between Monthly and Annual and every tier price counts up or down with a smooth easing animation while the per-card billing note swaps to show the exact yearly total and dollars saved.
Four tier cards — Basic, Plus, Unlimited and Black — each list included and excluded features with neon check and muted cross markers. Plus wears a “Most popular” ribbon and a neon-tinted card treatment, and Black uses an invite ribbon. Below the tiers, a class-pack add-on row offers stackable extras (HIIT pack, recovery day pass, PT block), and a horizontally scrollable comparison table with a sticky header maps every feature across all four plans.
The page closes with a single-open FAQ accordion that animates panel height, and every actionable button — plan CTAs, add-ons, and the billing toggle — fires a small toast confirming the choice, including whether monthly or annual is active. All interactions are plain vanilla JS with no dependencies, accessible roles and ARIA states, visible focus rings, and a responsive layout that collapses to a single column at 360px.