Gym — Landing Page
A high-energy, dark-themed gym landing page built with plain HTML, CSS and vanilla JavaScript. It features a full-bleed hero with a neon CTA, an animated counting stats strip, a featured-classes grid, a trainers preview row, a three-tier membership teaser, a rotating testimonials carousel and a final free-trial band with inline email validation. A sticky header, mobile nav toggle, scroll-reveal animations and a lightweight toast helper round out the experience.
MCP
Código
: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;
--shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.4);
--shadow-md: 0 12px 30px rgba(0, 0, 0, 0.45);
--shadow-lg: 0 28px 60px rgba(0, 0, 0, 0.55);
--maxw: 1160px;
}
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
}
html {
scroll-behavior: smooth;
}
body {
font-family: "Inter", system-ui, -apple-system, sans-serif;
background: var(--bg);
color: var(--ink);
line-height: 1.5;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
overflow-x: hidden;
}
img {
max-width: 100%;
display: block;
}
a {
color: inherit;
text-decoration: none;
}
ul {
list-style: none;
padding: 0;
}
.container {
width: 100%;
max-width: var(--maxw);
margin: 0 auto;
padding: 0 22px;
}
.sr-only,
.skip-link {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
overflow: hidden;
clip: rect(0 0 0 0);
white-space: nowrap;
border: 0;
}
.skip-link:focus {
position: fixed;
top: 12px;
left: 12px;
width: auto;
height: auto;
clip: auto;
z-index: 200;
background: var(--neon);
color: #0b0d10;
padding: 10px 16px;
border-radius: var(--r-sm);
font-weight: 700;
}
:focus-visible {
outline: 3px solid var(--neon);
outline-offset: 2px;
border-radius: 4px;
}
.eyebrow {
text-transform: uppercase;
letter-spacing: 0.18em;
font-size: 0.72rem;
font-weight: 800;
color: var(--neon);
}
/* ===== Buttons ===== */
.btn {
--btn-bg: transparent;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
font-family: inherit;
font-weight: 700;
font-size: 0.95rem;
line-height: 1;
padding: 13px 20px;
border-radius: var(--r-md);
border: 1px solid transparent;
cursor: pointer;
transition: transform 0.15s ease, box-shadow 0.2s ease,
background 0.2s ease, border-color 0.2s ease, color 0.2s ease;
white-space: nowrap;
}
.btn:active {
transform: translateY(1px) scale(0.99);
}
.btn-neon {
background: var(--neon);
color: #0b0d10;
box-shadow: 0 8px 22px var(--neon-50);
}
.btn-neon:hover {
background: var(--neon-d);
box-shadow: 0 12px 30px rgba(198, 255, 58, 0.28);
transform: translateY(-2px);
}
.btn-outline {
border-color: var(--line-2);
color: var(--ink);
background: rgba(255, 255, 255, 0.02);
}
.btn-outline:hover {
border-color: var(--neon);
color: var(--neon);
transform: translateY(-2px);
}
.btn-ghost {
color: var(--ink-2);
padding: 13px 14px;
}
.btn-ghost:hover {
color: var(--ink);
}
.btn-lg {
padding: 16px 28px;
font-size: 1.02rem;
}
.btn-sm {
padding: 10px 16px;
font-size: 0.85rem;
}
.btn-block {
width: 100%;
}
/* ===== Header ===== */
.site-header {
position: sticky;
top: 0;
z-index: 100;
background: rgba(13, 15, 18, 0.72);
backdrop-filter: blur(14px);
-webkit-backdrop-filter: blur(14px);
border-bottom: 1px solid transparent;
transition: border-color 0.25s ease, background 0.25s ease;
}
.site-header.scrolled {
border-bottom-color: var(--line);
background: rgba(13, 15, 18, 0.92);
}
.header-inner {
display: flex;
align-items: center;
gap: 18px;
height: 70px;
}
.brand {
display: flex;
align-items: center;
gap: 10px;
font-weight: 900;
}
.brand-mark {
color: var(--neon);
font-size: 1.25rem;
letter-spacing: -2px;
transform: skewX(-8deg);
}
.brand-name {
font-weight: 900;
letter-spacing: 0.08em;
font-size: 1.1rem;
}
.nav {
display: flex;
gap: 26px;
margin-left: 28px;
}
.nav a {
color: var(--ink-2);
font-weight: 600;
font-size: 0.95rem;
position: relative;
padding: 4px 0;
transition: color 0.18s ease;
}
.nav a::after {
content: "";
position: absolute;
left: 0;
bottom: -2px;
height: 2px;
width: 0;
background: var(--neon);
transition: width 0.22s ease;
}
.nav a:hover {
color: var(--ink);
}
.nav a:hover::after {
width: 100%;
}
.header-cta {
margin-left: auto;
display: flex;
align-items: center;
gap: 10px;
}
.nav-toggle {
display: none;
margin-left: auto;
flex-direction: column;
gap: 5px;
background: none;
border: 0;
padding: 8px;
cursor: pointer;
}
.nav-toggle span {
width: 24px;
height: 2px;
background: var(--ink);
border-radius: 2px;
transition: transform 0.25s ease, opacity 0.2s ease;
}
.nav-toggle[aria-expanded="true"] span:nth-child(1) {
transform: translateY(7px) rotate(45deg);
}
.nav-toggle[aria-expanded="true"] span:nth-child(2) {
opacity: 0;
}
.nav-toggle[aria-expanded="true"] span:nth-child(3) {
transform: translateY(-7px) rotate(-45deg);
}
/* ===== Hero ===== */
.hero {
position: relative;
padding: clamp(56px, 11vw, 130px) 0 clamp(50px, 8vw, 96px);
overflow: hidden;
}
.hero-glow {
position: absolute;
inset: -20% -10% auto -10%;
height: 620px;
background: radial-gradient(
60% 70% at 22% 18%,
rgba(198, 255, 58, 0.16),
transparent 62%
),
radial-gradient(
55% 65% at 88% 8%,
rgba(255, 106, 43, 0.14),
transparent 60%
);
filter: blur(8px);
pointer-events: none;
}
.hero-inner {
position: relative;
max-width: 760px;
}
.hero-title {
font-size: clamp(2.8rem, 9vw, 5.6rem);
font-weight: 900;
letter-spacing: -0.03em;
line-height: 0.96;
margin: 16px 0 0;
text-transform: uppercase;
}
.hero-title .accent {
color: var(--neon);
position: relative;
}
.hero-sub {
color: var(--ink-2);
font-size: clamp(1.02rem, 2.4vw, 1.25rem);
max-width: 540px;
margin: 22px 0 0;
}
.hero-actions {
display: flex;
flex-wrap: wrap;
gap: 14px;
margin-top: 32px;
}
.hero-trust {
display: flex;
align-items: center;
gap: 14px;
margin-top: 30px;
color: var(--muted);
font-size: 0.92rem;
}
.hero-trust strong {
color: var(--ink);
}
.avatars {
display: flex;
}
.avatars span {
width: 38px;
height: 38px;
border-radius: 50%;
display: grid;
place-items: center;
font-size: 0.7rem;
font-weight: 800;
color: #0b0d10;
background: linear-gradient(135deg, var(--neon), var(--neon-d));
border: 2px solid var(--bg);
margin-left: -10px;
}
.avatars span:first-child {
margin-left: 0;
}
.avatars span:nth-child(2) {
background: linear-gradient(135deg, var(--orange), #ff8a5b);
}
.avatars span:nth-child(3) {
background: linear-gradient(135deg, #6ea8ff, #3d7bff);
color: #fff;
}
.avatars span:nth-child(4) {
background: linear-gradient(135deg, #f0f3f6, #c2c8d0);
}
/* ===== Stats ===== */
.stats {
border-top: 1px solid var(--line);
border-bottom: 1px solid var(--line);
background: var(--surface);
}
.stats-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 1px;
}
.stat {
padding: 30px 14px;
text-align: center;
position: relative;
}
.stat:not(:last-child)::after {
content: "";
position: absolute;
right: 0;
top: 22%;
height: 56%;
width: 1px;
background: var(--line);
}
.stat-num {
display: block;
font-size: clamp(2rem, 5vw, 3rem);
font-weight: 900;
letter-spacing: -0.02em;
color: var(--neon);
line-height: 1;
}
.stat-label {
display: block;
margin-top: 8px;
color: var(--muted);
font-size: 0.82rem;
text-transform: uppercase;
letter-spacing: 0.1em;
font-weight: 600;
}
/* ===== Sections ===== */
.section {
padding: clamp(56px, 9vw, 100px) 0;
}
.section-alt {
background: var(--surface);
}
.section-head {
max-width: 620px;
margin: 0 auto 44px;
text-align: center;
}
.section-title {
font-size: clamp(2rem, 5vw, 3rem);
font-weight: 900;
letter-spacing: -0.02em;
margin-top: 12px;
text-transform: uppercase;
}
.section-lead {
color: var(--ink-2);
margin-top: 14px;
font-size: 1.02rem;
}
/* ===== Class grid ===== */
.class-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 20px;
}
.class-card {
background: var(--surface-2);
border: 1px solid var(--line);
border-radius: var(--r-lg);
overflow: hidden;
display: flex;
flex-direction: column;
transition: transform 0.22s ease, border-color 0.22s ease,
box-shadow 0.22s ease;
}
.class-card:hover {
transform: translateY(-6px);
border-color: var(--line-2);
box-shadow: var(--shadow-md);
}
.class-media {
position: relative;
aspect-ratio: 5 / 4;
background-size: cover;
background-position: center;
}
.media-1 {
background: linear-gradient(140deg, #1f2a12, #3a5c12),
radial-gradient(80% 60% at 70% 20%, rgba(198, 255, 58, 0.35), transparent);
}
.media-2 {
background: linear-gradient(140deg, #25180f, #5c2c12),
radial-gradient(80% 60% at 30% 20%, rgba(255, 106, 43, 0.4), transparent);
}
.media-3 {
background: linear-gradient(140deg, #11212a, #18465c),
radial-gradient(80% 60% at 70% 30%, rgba(110, 168, 255, 0.35), transparent);
}
.media-4 {
background: linear-gradient(140deg, #221230, #4a1a5c),
radial-gradient(80% 60% at 30% 25%, rgba(200, 110, 255, 0.35), transparent);
}
.tag {
position: absolute;
top: 12px;
left: 12px;
font-size: 0.68rem;
text-transform: uppercase;
letter-spacing: 0.1em;
font-weight: 800;
padding: 6px 10px;
border-radius: 999px;
background: rgba(13, 15, 18, 0.7);
color: var(--ink);
backdrop-filter: blur(6px);
}
.tag-hot {
background: var(--neon);
color: #0b0d10;
}
.tag-new {
background: var(--orange);
color: #0b0d10;
}
.class-body {
padding: 18px;
display: flex;
flex-direction: column;
gap: 8px;
flex: 1;
}
.class-body h3 {
font-size: 1.15rem;
font-weight: 800;
}
.class-body p {
color: var(--ink-2);
font-size: 0.9rem;
}
.meta-row {
display: flex;
flex-wrap: wrap;
gap: 6px;
color: var(--muted);
font-size: 0.78rem;
font-weight: 600;
margin-top: 2px;
}
.class-body .btn {
margin-top: auto;
}
/* ===== Trainers ===== */
.trainer-row {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 20px;
}
.trainer {
background: var(--surface-2);
border: 1px solid var(--line);
border-radius: var(--r-lg);
overflow: hidden;
transition: transform 0.22s ease, border-color 0.22s ease;
}
.trainer:hover {
transform: translateY(-6px);
border-color: var(--neon);
}
.trainer-photo {
aspect-ratio: 1 / 1;
position: relative;
}
.t-1 {
background: linear-gradient(160deg, #3a5c12, #1f2a12);
}
.t-2 {
background: linear-gradient(160deg, #5c2c12, #25180f);
}
.t-3 {
background: linear-gradient(160deg, #18465c, #11212a);
}
.t-4 {
background: linear-gradient(160deg, #4a1a5c, #221230);
}
.trainer-photo::after {
content: "";
position: absolute;
inset: 0;
background: radial-gradient(
60% 60% at 50% 30%,
rgba(255, 255, 255, 0.12),
transparent 70%
);
}
.trainer figcaption {
padding: 16px 18px 20px;
}
.trainer figcaption h3 {
font-size: 1.05rem;
font-weight: 800;
}
.trainer figcaption p {
color: var(--muted);
font-size: 0.85rem;
margin-top: 3px;
}
/* ===== Pricing ===== */
.tier-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 22px;
align-items: start;
}
.tier {
position: relative;
background: var(--surface-2);
border: 1px solid var(--line);
border-radius: var(--r-lg);
padding: 28px 24px;
transition: transform 0.22s ease, border-color 0.22s ease;
}
.tier:hover {
transform: translateY(-4px);
border-color: var(--line-2);
}
.tier-feat {
border-color: var(--neon);
background: linear-gradient(
180deg,
rgba(198, 255, 58, 0.06),
var(--surface-2) 40%
);
box-shadow: var(--shadow-md);
}
.tier-badge {
position: absolute;
top: -12px;
left: 24px;
background: var(--neon);
color: #0b0d10;
font-size: 0.68rem;
text-transform: uppercase;
letter-spacing: 0.1em;
font-weight: 800;
padding: 6px 12px;
border-radius: 999px;
}
.tier-name {
font-size: 1.05rem;
font-weight: 800;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--ink-2);
}
.tier-price {
margin: 12px 0 20px;
color: var(--muted);
font-weight: 600;
}
.tier-price span {
font-size: 2.6rem;
font-weight: 900;
color: var(--ink);
letter-spacing: -0.02em;
}
.tier-list {
display: flex;
flex-direction: column;
gap: 12px;
margin-bottom: 24px;
}
.tier-list li {
position: relative;
padding-left: 28px;
color: var(--ink-2);
font-size: 0.92rem;
}
.tier-list li::before {
content: "";
position: absolute;
left: 0;
top: 2px;
width: 18px;
height: 18px;
border-radius: 50%;
background: var(--neon-50);
background-image: linear-gradient(var(--neon), var(--neon));
background-size: 9px 2px, 2px 9px;
background-position: center;
background-repeat: no-repeat;
}
/* ===== Rotator / testimonials ===== */
.rotator {
max-width: 760px;
margin: 0 auto;
text-align: center;
}
.rotator-track {
position: relative;
min-height: 220px;
}
.quote {
position: absolute;
inset: 0;
opacity: 0;
transform: translateY(14px);
transition: opacity 0.5s ease, transform 0.5s ease;
pointer-events: none;
}
.quote.is-active {
opacity: 1;
transform: translateY(0);
position: relative;
pointer-events: auto;
}
.quote blockquote {
font-size: clamp(1.15rem, 3vw, 1.6rem);
font-weight: 600;
line-height: 1.4;
color: var(--ink);
}
.quote-by {
display: inline-flex;
align-items: center;
gap: 12px;
margin-top: 26px;
text-align: left;
}
.q-avatar {
width: 46px;
height: 46px;
border-radius: 50%;
display: grid;
place-items: center;
font-weight: 800;
font-size: 0.85rem;
color: #0b0d10;
background: linear-gradient(135deg, var(--neon), var(--neon-d));
}
.quote-by strong {
font-weight: 800;
}
.quote-by p {
color: var(--muted);
font-size: 0.85rem;
}
.rotator-dots {
display: flex;
justify-content: center;
gap: 10px;
margin-top: 34px;
}
.rotator-dots button {
width: 10px;
height: 10px;
border-radius: 999px;
border: 0;
background: var(--line-2);
cursor: pointer;
padding: 0;
transition: width 0.25s ease, background 0.25s ease;
}
.rotator-dots button[aria-selected="true"] {
width: 28px;
background: var(--neon);
}
/* ===== CTA band ===== */
.cta-band {
position: relative;
padding: clamp(60px, 10vw, 110px) 0;
background: var(--surface-2);
overflow: hidden;
border-top: 1px solid var(--line);
}
.cta-glow {
position: absolute;
inset: auto -10% -50% -10%;
height: 520px;
background: radial-gradient(
50% 70% at 50% 100%,
rgba(198, 255, 58, 0.18),
transparent 65%
);
pointer-events: none;
}
.cta-inner {
position: relative;
max-width: 640px;
margin: 0 auto;
text-align: center;
}
.cta-inner h2 {
font-size: clamp(2rem, 6vw, 3.4rem);
font-weight: 900;
letter-spacing: -0.02em;
text-transform: uppercase;
}
.cta-inner > p {
color: var(--ink-2);
margin-top: 14px;
font-size: 1.05rem;
}
.cta-form {
display: flex;
gap: 10px;
margin: 30px auto 0;
max-width: 480px;
}
.cta-form input {
flex: 1;
background: var(--bg);
border: 1px solid var(--line-2);
border-radius: var(--r-md);
color: var(--ink);
font: inherit;
padding: 16px 18px;
transition: border-color 0.2s ease;
}
.cta-form input::placeholder {
color: var(--muted);
}
.cta-form input:focus {
border-color: var(--neon);
outline: none;
}
.cta-form input.invalid {
border-color: var(--danger);
}
.cta-fine {
color: var(--muted);
font-size: 0.85rem;
margin-top: 18px;
}
/* ===== Footer ===== */
.site-footer {
border-top: 1px solid var(--line);
background: var(--surface);
padding: 56px 0 28px;
}
.footer-grid {
display: grid;
grid-template-columns: 1.6fr 1fr 1fr 1.2fr;
gap: 30px;
}
.footer-brand p {
color: var(--muted);
font-size: 0.9rem;
margin-top: 12px;
max-width: 260px;
}
.site-footer h4 {
font-size: 0.78rem;
text-transform: uppercase;
letter-spacing: 0.12em;
color: var(--ink-2);
margin-bottom: 14px;
}
.site-footer nav {
display: flex;
flex-direction: column;
gap: 10px;
}
.site-footer nav a {
color: var(--muted);
font-size: 0.92rem;
transition: color 0.18s ease;
}
.site-footer nav a:hover {
color: var(--neon);
}
.footer-bottom {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
gap: 8px;
margin-top: 44px;
padding-top: 22px;
border-top: 1px solid var(--line);
color: var(--muted);
font-size: 0.82rem;
}
/* ===== Toast ===== */
.toast {
position: fixed;
left: 50%;
bottom: 26px;
transform: translate(-50%, 140%);
background: var(--elevated);
color: var(--ink);
border: 1px solid var(--line-2);
border-left: 3px solid var(--neon);
padding: 14px 20px;
border-radius: var(--r-md);
font-weight: 600;
font-size: 0.92rem;
box-shadow: var(--shadow-lg);
z-index: 300;
opacity: 0;
transition: transform 0.35s cubic-bezier(0.2, 0.9, 0.3, 1), opacity 0.3s ease;
max-width: calc(100% - 32px);
}
.toast.show {
transform: translate(-50%, 0);
opacity: 1;
}
/* ===== Reveal animation ===== */
.reveal {
opacity: 0;
transform: translateY(22px);
transition: opacity 0.6s ease, transform 0.6s ease;
will-change: opacity, transform;
}
.reveal.in {
opacity: 1;
transform: none;
}
@media (prefers-reduced-motion: reduce) {
.reveal {
opacity: 1;
transform: none;
transition: none;
}
html {
scroll-behavior: auto;
}
}
/* ===== Responsive ===== */
@media (max-width: 980px) {
.class-grid,
.trainer-row {
grid-template-columns: repeat(2, 1fr);
}
.tier-grid {
grid-template-columns: 1fr;
max-width: 460px;
margin: 0 auto;
}
.footer-grid {
grid-template-columns: 1fr 1fr;
}
}
@media (max-width: 760px) {
.nav,
.header-cta {
display: none;
}
.nav-toggle {
display: flex;
}
.site-header.nav-open .nav {
display: flex;
position: absolute;
top: 70px;
left: 0;
right: 0;
flex-direction: column;
gap: 0;
margin: 0;
background: var(--surface);
border-bottom: 1px solid var(--line);
padding: 8px 0;
}
.site-header.nav-open .nav a {
padding: 14px 22px;
}
.site-header.nav-open .header-cta {
display: flex;
position: absolute;
top: calc(70px + 4 * 50px);
left: 0;
right: 0;
flex-direction: column;
gap: 8px;
padding: 14px 22px 18px;
background: var(--surface);
border-bottom: 1px solid var(--line);
}
.stats-grid {
grid-template-columns: repeat(2, 1fr);
}
.stat:nth-child(2)::after {
display: none;
}
}
@media (max-width: 520px) {
.container {
padding: 0 16px;
}
.class-grid,
.trainer-row,
.footer-grid {
grid-template-columns: 1fr;
}
.footer-brand p {
max-width: none;
}
.hero-actions .btn,
.cta-form .btn {
width: 100%;
}
.cta-form {
flex-direction: column;
}
.hero-trust {
align-items: flex-start;
flex-direction: column;
gap: 10px;
}
.footer-bottom {
flex-direction: column;
}
}(function () {
"use strict";
/* ---------- Toast helper ---------- */
var toastEl = document.getElementById("toast");
var toastTimer;
function toast(msg) {
if (!toastEl) return;
toastEl.textContent = msg;
toastEl.classList.add("show");
clearTimeout(toastTimer);
toastTimer = setTimeout(function () {
toastEl.classList.remove("show");
}, 2800);
}
/* ---------- Sticky header shadow ---------- */
var header = document.querySelector(".site-header");
function onScroll() {
if (!header) return;
header.classList.toggle("scrolled", window.scrollY > 8);
}
window.addEventListener("scroll", onScroll, { passive: true });
onScroll();
/* ---------- Mobile nav toggle ---------- */
var navToggle = document.getElementById("navToggle");
if (navToggle && header) {
navToggle.addEventListener("click", function () {
var open = header.classList.toggle("nav-open");
navToggle.setAttribute("aria-expanded", open ? "true" : "false");
});
// Close menu after tapping a link
header.querySelectorAll(".nav a, .header-cta a").forEach(function (a) {
a.addEventListener("click", function () {
header.classList.remove("nav-open");
navToggle.setAttribute("aria-expanded", "false");
});
});
}
/* ---------- Reveal on scroll ---------- */
var revealEls = Array.prototype.slice.call(document.querySelectorAll(".reveal"));
if ("IntersectionObserver" in window) {
var io = new IntersectionObserver(
function (entries) {
entries.forEach(function (entry) {
if (entry.isIntersecting) {
entry.target.classList.add("in");
io.unobserve(entry.target);
}
});
},
{ threshold: 0.12, rootMargin: "0px 0px -40px 0px" }
);
revealEls.forEach(function (el) {
io.observe(el);
});
} else {
revealEls.forEach(function (el) {
el.classList.add("in");
});
}
/* ---------- Animated stat counters ---------- */
var counters = Array.prototype.slice.call(
document.querySelectorAll(".stat-num[data-count]")
);
function animateCount(el) {
var target = parseInt(el.getAttribute("data-count"), 10) || 0;
var suffix = el.getAttribute("data-suffix") || "";
var dur = 1400;
var start = null;
function step(ts) {
if (start === null) start = ts;
var p = Math.min((ts - start) / dur, 1);
var eased = 1 - Math.pow(1 - p, 3);
var val = Math.round(target * eased);
el.textContent = val.toLocaleString("en-US") + suffix;
if (p < 1) requestAnimationFrame(step);
}
requestAnimationFrame(step);
}
if ("IntersectionObserver" in window && counters.length) {
var cio = new IntersectionObserver(
function (entries) {
entries.forEach(function (entry) {
if (entry.isIntersecting) {
animateCount(entry.target);
cio.unobserve(entry.target);
}
});
},
{ threshold: 0.5 }
);
counters.forEach(function (el) {
cio.observe(el);
});
} else {
counters.forEach(animateCount);
}
/* ---------- Testimonial rotator ---------- */
var track = document.getElementById("rotatorTrack");
var dotsWrap = document.getElementById("rotatorDots");
if (track && dotsWrap) {
var quotes = Array.prototype.slice.call(track.querySelectorAll(".quote"));
var index = 0;
var rotateTimer;
var interval = 6000;
quotes.forEach(function (_, i) {
var dot = document.createElement("button");
dot.setAttribute("role", "tab");
dot.setAttribute("aria-label", "Story " + (i + 1));
dot.setAttribute("aria-selected", i === 0 ? "true" : "false");
dot.addEventListener("click", function () {
show(i);
restart();
});
dotsWrap.appendChild(dot);
});
var dots = Array.prototype.slice.call(dotsWrap.children);
function show(i) {
index = (i + quotes.length) % quotes.length;
quotes.forEach(function (q, qi) {
q.classList.toggle("is-active", qi === index);
});
dots.forEach(function (d, di) {
d.setAttribute("aria-selected", di === index ? "true" : "false");
});
}
function next() {
show(index + 1);
}
function restart() {
clearInterval(rotateTimer);
rotateTimer = setInterval(next, interval);
}
restart();
// Pause on hover for usability
var rotator = track.closest(".rotator");
if (rotator) {
rotator.addEventListener("mouseenter", function () {
clearInterval(rotateTimer);
});
rotator.addEventListener("mouseleave", restart);
}
}
/* ---------- Trial CTAs ---------- */
document.querySelectorAll("[data-trial]").forEach(function (btn) {
btn.addEventListener("click", function () {
// Allow the anchor jump, then nudge focus to the email field
setTimeout(function () {
var input = document.getElementById("trialEmail");
if (input) input.focus();
}, 450);
});
});
/* ---------- Trial form ---------- */
var form = document.getElementById("trialForm");
if (form) {
var emailInput = document.getElementById("trialEmail");
form.addEventListener("submit", function (e) {
e.preventDefault();
var value = (emailInput.value || "").trim();
var ok = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
if (!ok) {
emailInput.classList.add("invalid");
emailInput.focus();
toast("Enter a valid email to claim your trial.");
return;
}
emailInput.classList.remove("invalid");
form.reset();
toast("You're in! Check " + value + " for your free pass.");
});
emailInput.addEventListener("input", function () {
emailInput.classList.remove("invalid");
});
}
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Ironpulse — Train Without Limits</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>
<a class="skip-link" href="#main">Skip to content</a>
<!-- ===== Header ===== -->
<header class="site-header" id="top">
<div class="container header-inner">
<a class="brand" href="#top" aria-label="Ironpulse home">
<span class="brand-mark" aria-hidden="true">/\</span>
<span class="brand-name">IRONPULSE</span>
</a>
<nav class="nav" id="primary-nav" aria-label="Primary">
<a href="#classes">Classes</a>
<a href="#trainers">Trainers</a>
<a href="#pricing">Pricing</a>
<a href="#stories">Stories</a>
</nav>
<div class="header-cta">
<a class="btn btn-ghost" href="#classes">View classes</a>
<a class="btn btn-neon" href="#trial" data-trial>Start free trial</a>
</div>
<button
class="nav-toggle"
id="navToggle"
aria-expanded="false"
aria-controls="primary-nav"
aria-label="Toggle navigation"
>
<span></span><span></span><span></span>
</button>
</div>
</header>
<main id="main">
<!-- ===== Hero ===== -->
<section class="hero">
<div class="hero-glow" aria-hidden="true"></div>
<div class="container hero-inner">
<p class="eyebrow reveal">No excuses · Open 24/7</p>
<h1 class="hero-title reveal">
Train without<br /><span class="accent">limits.</span>
</h1>
<p class="hero-sub reveal">
Strength, conditioning and recovery under one roof. Coached classes,
free-weight floor and a community that shows up — every single day.
</p>
<div class="hero-actions reveal">
<a class="btn btn-neon btn-lg" href="#trial" data-trial>Start free trial</a>
<a class="btn btn-outline btn-lg" href="#classes">View classes</a>
</div>
<div class="hero-trust reveal" aria-label="Member rating">
<div class="avatars" aria-hidden="true">
<span>JM</span><span>RK</span><span>AL</span><span>TS</span>
</div>
<p><strong>4.9/5</strong> from 2,300+ members · Cancel anytime</p>
</div>
</div>
</section>
<!-- ===== Stats strip ===== -->
<section class="stats" aria-label="Gym statistics">
<div class="container stats-grid">
<div class="stat reveal">
<span class="stat-num" data-count="8400" data-suffix="+">0</span>
<span class="stat-label">Active members</span>
</div>
<div class="stat reveal">
<span class="stat-num" data-count="120" data-suffix="/wk">0</span>
<span class="stat-label">Classes weekly</span>
</div>
<div class="stat reveal">
<span class="stat-num" data-count="46">0</span>
<span class="stat-label">Expert trainers</span>
</div>
<div class="stat reveal">
<span class="stat-num" data-count="7">0</span>
<span class="stat-label">City locations</span>
</div>
</div>
</section>
<!-- ===== Featured classes ===== -->
<section class="section" id="classes">
<div class="container">
<div class="section-head reveal">
<p class="eyebrow">Featured classes</p>
<h2 class="section-title">Pick your pace</h2>
<p class="section-lead">
From metabolic burners to heavy lifting — every session is
coach-led and capped for real attention.
</p>
</div>
<div class="class-grid">
<article class="class-card reveal">
<div class="class-media media-1" aria-hidden="true">
<span class="tag tag-hot">Most booked</span>
</div>
<div class="class-body">
<h3>Hyrox Conditioning</h3>
<p>Functional intervals that build engine and grit.</p>
<ul class="meta-row">
<li>50 min</li>
<li>·</li>
<li>High intensity</li>
<li>·</li>
<li>Coach Mara</li>
</ul>
<a class="btn btn-neon btn-sm" href="#trial" data-trial>Book a spot</a>
</div>
</article>
<article class="class-card reveal">
<div class="class-media media-2" aria-hidden="true">
<span class="tag">Strength</span>
</div>
<div class="class-body">
<h3>Barbell Club</h3>
<p>Squat, bench, deadlift — technique first, PRs follow.</p>
<ul class="meta-row">
<li>60 min</li>
<li>·</li>
<li>All levels</li>
<li>·</li>
<li>Coach Diego</li>
</ul>
<a class="btn btn-neon btn-sm" href="#trial" data-trial>Book a spot</a>
</div>
</article>
<article class="class-card reveal">
<div class="class-media media-3" aria-hidden="true">
<span class="tag">Recovery</span>
</div>
<div class="class-body">
<h3>Mobility & Flow</h3>
<p>Restore range, breathe, undo the desk damage.</p>
<ul class="meta-row">
<li>40 min</li>
<li>·</li>
<li>Low impact</li>
<li>·</li>
<li>Coach Ivy</li>
</ul>
<a class="btn btn-neon btn-sm" href="#trial" data-trial>Book a spot</a>
</div>
</article>
<article class="class-card reveal">
<div class="class-media media-4" aria-hidden="true">
<span class="tag tag-new">New</span>
</div>
<div class="class-body">
<h3>Ride45 Cycle</h3>
<p>Beat-driven indoor cycling under stadium lights.</p>
<ul class="meta-row">
<li>45 min</li>
<li>·</li>
<li>Cardio</li>
<li>·</li>
<li>Coach Theo</li>
</ul>
<a class="btn btn-neon btn-sm" href="#trial" data-trial>Book a spot</a>
</div>
</article>
</div>
</div>
</section>
<!-- ===== Trainers ===== -->
<section class="section section-alt" id="trainers">
<div class="container">
<div class="section-head reveal">
<p class="eyebrow">Coaching staff</p>
<h2 class="section-title">Trained by the best</h2>
<p class="section-lead">
Certified, obsessed, and in your corner. Meet a few of the people
who'll push you further.
</p>
</div>
<div class="trainer-row">
<figure class="trainer reveal">
<div class="trainer-photo t-1" aria-hidden="true"></div>
<figcaption>
<h3>Mara Voss</h3>
<p>Conditioning & Hyrox</p>
</figcaption>
</figure>
<figure class="trainer reveal">
<div class="trainer-photo t-2" aria-hidden="true"></div>
<figcaption>
<h3>Diego Marín</h3>
<p>Strength & Powerlifting</p>
</figcaption>
</figure>
<figure class="trainer reveal">
<div class="trainer-photo t-3" aria-hidden="true"></div>
<figcaption>
<h3>Ivy Okafor</h3>
<p>Mobility & Recovery</p>
</figcaption>
</figure>
<figure class="trainer reveal">
<div class="trainer-photo t-4" aria-hidden="true"></div>
<figcaption>
<h3>Theo Lang</h3>
<p>Cycle & Endurance</p>
</figcaption>
</figure>
</div>
</div>
</section>
<!-- ===== Pricing teaser ===== -->
<section class="section" id="pricing">
<div class="container">
<div class="section-head reveal">
<p class="eyebrow">Membership</p>
<h2 class="section-title">One pass. Zero limits.</h2>
<p class="section-lead">
No contracts, no hidden fees. Start free for 7 days, then choose
the tier that fits.
</p>
</div>
<div class="tier-grid">
<article class="tier reveal">
<h3 class="tier-name">Flex</h3>
<p class="tier-price"><span>$29</span>/mo</p>
<ul class="tier-list">
<li>Open-gym floor access</li>
<li>2 classes / week</li>
<li>Mobile app workouts</li>
<li>1 home location</li>
</ul>
<a class="btn btn-outline btn-block" href="#trial" data-trial>Choose Flex</a>
</article>
<article class="tier tier-feat reveal">
<span class="tier-badge">Most popular</span>
<h3 class="tier-name">Unlimited</h3>
<p class="tier-price"><span>$59</span>/mo</p>
<ul class="tier-list">
<li>Unlimited classes</li>
<li>All 7 locations</li>
<li>Recovery zone + sauna</li>
<li>Free guest passes</li>
</ul>
<a class="btn btn-neon btn-block" href="#trial" data-trial>Choose Unlimited</a>
</article>
<article class="tier reveal">
<h3 class="tier-name">Performance</h3>
<p class="tier-price"><span>$99</span>/mo</p>
<ul class="tier-list">
<li>Everything in Unlimited</li>
<li>4 personal-training sessions</li>
<li>InBody scans monthly</li>
<li>Nutrition coaching</li>
</ul>
<a class="btn btn-outline btn-block" href="#trial" data-trial>Choose Performance</a>
</article>
</div>
</div>
</section>
<!-- ===== Testimonials ===== -->
<section class="section section-alt" id="stories">
<div class="container">
<div class="section-head reveal">
<p class="eyebrow">Member stories</p>
<h2 class="section-title">Results that talk</h2>
</div>
<div class="rotator reveal" aria-roledescription="carousel">
<ul class="rotator-track" id="rotatorTrack">
<li class="quote is-active" role="group" aria-label="1 of 3">
<blockquote>
"I came to lose 10kg and stayed for the people. Six months in
I'm stronger than I've ever been — and I actually look forward
to Mondays."
</blockquote>
<div class="quote-by">
<span class="q-avatar" aria-hidden="true">SR</span>
<div>
<strong>Sofía Rivas</strong>
<p>Member since 2024</p>
</div>
</div>
</li>
<li class="quote" role="group" aria-label="2 of 3">
<blockquote>
"The Barbell Club coaches fixed my deadlift in one session.
Added 40kg to my pull in a quarter with zero pain."
</blockquote>
<div class="quote-by">
<span class="q-avatar" aria-hidden="true">KN</span>
<div>
<strong>Kenji Nakamura</strong>
<p>Powerlifting member</p>
</div>
</div>
</li>
<li class="quote" role="group" aria-label="3 of 3">
<blockquote>
"Three locations on my commute means no more excuses. The app
books my spot in two taps and the recovery sauna is unreal."
</blockquote>
<div class="quote-by">
<span class="q-avatar" aria-hidden="true">AB</span>
<div>
<strong>Amara Bello</strong>
<p>Unlimited member</p>
</div>
</div>
</li>
</ul>
<div class="rotator-dots" id="rotatorDots" role="tablist" aria-label="Choose story"></div>
</div>
</div>
</section>
<!-- ===== Final CTA band ===== -->
<section class="cta-band" id="trial">
<div class="cta-glow" aria-hidden="true"></div>
<div class="container cta-inner reveal">
<h2>Your first week is on us.</h2>
<p>No card required. Walk in, train, decide. It's that simple.</p>
<form class="cta-form" id="trialForm" novalidate>
<label class="sr-only" for="trialEmail">Email address</label>
<input
id="trialEmail"
name="email"
type="email"
inputmode="email"
placeholder="[email protected]"
autocomplete="email"
required
/>
<button class="btn btn-neon btn-lg" type="submit">Claim free trial</button>
</form>
<p class="cta-fine">Join 8,400+ members training at Ironpulse.</p>
</div>
</section>
</main>
<!-- ===== Footer ===== -->
<footer class="site-footer">
<div class="container footer-grid">
<div class="footer-brand">
<span class="brand-name">IRONPULSE</span>
<p>Train without limits. Open 24/7 across 7 city locations.</p>
</div>
<nav aria-label="Explore">
<h4>Explore</h4>
<a href="#classes">Classes</a>
<a href="#trainers">Trainers</a>
<a href="#pricing">Pricing</a>
<a href="#stories">Stories</a>
</nav>
<nav aria-label="Company">
<h4>Company</h4>
<a href="#top">About</a>
<a href="#top">Careers</a>
<a href="#top">Locations</a>
<a href="#top">Contact</a>
</nav>
<div class="footer-cta">
<h4>Ready?</h4>
<a class="btn btn-neon btn-block" href="#trial" data-trial>Start free trial</a>
</div>
</div>
<div class="container footer-bottom">
<p>© 2026 Ironpulse Fitness. All rights reserved.</p>
<p>Made for people who show up.</p>
</div>
</footer>
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>Landing Page
A bold, conversion-focused landing page for the fictional Ironpulse gym. The full-bleed hero pairs an oversized uppercase headline with a neon “Start free trial” CTA and a social-proof rating, sitting on a soft radial glow. Below it, a stats strip animates from zero into real-feeling numbers (members, weekly classes, trainers, locations) the moment it scrolls into view.
The page keeps moving with a four-card featured-classes grid (each with its own gradient cover, badge and coach), a trainers preview row, a three-tier membership teaser with a highlighted “Most popular” plan, and a testimonials carousel that auto-rotates, pauses on hover and is dot-navigable. A final free-trial band captures an email with inline validation and confirmation toast.
All interactions are vanilla JavaScript: a sticky header that gains a border on
scroll, an accessible mobile nav toggle, IntersectionObserver-driven
scroll-reveal and stat counters, the testimonial rotator, and a small
toast() helper. The layout is fully responsive down to ~360px and respects
prefers-reduced-motion.