Gym — CrossFit Box Landing
A raw, industrial landing page for a fictional CrossFit box in a near-black, safety-yellow and concrete palette, with caution-stripe accents, a heavy condensed hero and a free intro-WOD call to action, a whiteboard WOD-of-the-day block with RX and scaling options plus a working stopwatch and AMRAP box timer, a four-program grid, a coaches row, a results section whose stats count up on view, a class schedule teaser, a sticky blurred nav with a mobile menu, scroll reveals, and an accessible booking dialog with a focus trap and toast feedback.
MCP
Kod
:root {
--bg: #101113;
--surface: #191b1e;
--surface-2: #202327;
--elevated: #282b30;
--ink: #f2f3f4;
--ink-2: #b7bbc0;
--muted: #7d8288;
--yellow: #ffd21f;
--yellow-d: #e6bd00;
--yellow-50: rgba(255, 210, 31, 0.12);
--concrete: #3a3d42;
--line: rgba(255, 255, 255, 0.1);
--line-2: rgba(255, 255, 255, 0.18);
--ok: #5fd38a;
--warn: #ffb84d;
--danger: #ff6b6b;
--r-sm: 4px;
--r-md: 6px;
--r-lg: 10px;
--shadow: 0 18px 50px rgba(0, 0, 0, 0.55);
--tape: repeating-linear-gradient(
-45deg,
var(--yellow) 0 22px,
#101113 22px 44px
);
}
* {
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;
text-rendering: optimizeLegibility;
}
img {
max-width: 100%;
display: block;
}
a {
color: inherit;
text-decoration: none;
}
.wrap {
width: min(1180px, 92vw);
margin-inline: auto;
}
.skip {
position: absolute;
left: -999px;
top: 0;
background: var(--yellow);
color: #101113;
padding: 10px 16px;
font-weight: 800;
z-index: 200;
}
.skip:focus {
left: 12px;
top: 12px;
}
/* ---- Typography helpers ---- */
.eyebrow {
display: inline-flex;
align-items: center;
gap: 8px;
font-family: "Oswald", "Inter", sans-serif;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.16em;
font-size: 0.74rem;
color: var(--ink-2);
margin: 0 0 14px;
}
.eyebrow .dot {
width: 8px;
height: 8px;
background: var(--yellow);
display: inline-block;
}
.eyebrow--y {
color: var(--yellow);
}
.up {
text-transform: uppercase;
letter-spacing: 0.12em;
font-size: 0.72rem;
font-weight: 700;
}
.muted {
color: var(--muted);
}
.ok {
color: var(--ok);
}
.warn {
color: var(--warn);
}
.danger {
color: var(--danger);
}
.sec-head {
max-width: 640px;
margin-bottom: 40px;
}
.sec-title {
font-family: "Oswald", "Inter", sans-serif;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.01em;
font-size: clamp(2rem, 5vw, 3.2rem);
line-height: 0.98;
margin: 0 0 12px;
}
.sec-sub {
color: var(--ink-2);
margin: 0;
font-size: 1.02rem;
}
/* ---- Buttons ---- */
.btn {
--pad-y: 12px;
--pad-x: 22px;
font-family: "Oswald", "Inter", sans-serif;
font-weight: 600;
letter-spacing: 0.05em;
text-transform: uppercase;
font-size: 0.92rem;
border: 1px solid transparent;
border-radius: var(--r-sm);
padding: var(--pad-y) var(--pad-x);
cursor: pointer;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
transition: transform 0.12s ease, background 0.15s ease, box-shadow 0.15s ease,
border-color 0.15s ease;
background: var(--surface-2);
color: var(--ink);
}
.btn:hover {
transform: translateY(-2px);
}
.btn:active {
transform: translateY(0);
}
.btn:focus-visible {
outline: 3px solid var(--yellow);
outline-offset: 2px;
}
.btn--yellow {
background: var(--yellow);
color: #101113;
box-shadow: 0 0 0 1px var(--yellow-d) inset;
}
.btn--yellow:hover {
background: var(--yellow-d);
}
.btn--dark {
background: #101113;
color: var(--ink);
border-color: var(--line-2);
}
.btn--dark:hover {
border-color: var(--yellow);
}
.btn--ghost {
background: transparent;
color: var(--ink);
border-color: var(--line-2);
}
.btn--ghost:hover {
border-color: var(--yellow);
color: var(--yellow);
}
.btn--block {
width: 100%;
}
.btn--lg {
--pad-y: 16px;
--pad-x: 30px;
font-size: 1rem;
}
.btn--sm {
--pad-y: 9px;
--pad-x: 16px;
font-size: 0.82rem;
}
.btn--xs {
--pad-y: 6px;
--pad-x: 12px;
font-size: 0.74rem;
}
.badge {
display: inline-flex;
align-items: center;
gap: 7px;
font-family: "Oswald", sans-serif;
text-transform: uppercase;
letter-spacing: 0.08em;
font-size: 0.72rem;
font-weight: 600;
padding: 5px 10px;
border-radius: var(--r-sm);
border: 1px solid var(--line-2);
color: var(--ink-2);
}
.badge--live {
color: var(--ok);
border-color: rgba(95, 211, 138, 0.4);
}
.pulse {
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--ok);
box-shadow: 0 0 0 0 rgba(95, 211, 138, 0.5);
animation: pulse 1.8s infinite;
}
@keyframes pulse {
70% {
box-shadow: 0 0 0 8px rgba(95, 211, 138, 0);
}
100% {
box-shadow: 0 0 0 0 rgba(95, 211, 138, 0);
}
}
.tag {
display: inline-block;
font-family: "Oswald", sans-serif;
text-transform: uppercase;
letter-spacing: 0.08em;
font-size: 0.7rem;
color: var(--ink-2);
border: 1px solid var(--line);
padding: 4px 9px;
border-radius: var(--r-sm);
}
/* ---- Nav ---- */
.nav {
position: sticky;
top: 0;
z-index: 100;
background: rgba(16, 17, 19, 0.82);
backdrop-filter: blur(10px);
border-bottom: 1px solid var(--line);
transition: box-shadow 0.2s ease, background 0.2s ease;
}
.nav.is-scrolled {
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.5);
background: rgba(16, 17, 19, 0.95);
}
.nav__inner {
display: flex;
align-items: center;
justify-content: space-between;
height: 68px;
gap: 20px;
}
.brand {
display: inline-flex;
align-items: center;
gap: 10px;
}
.brand__mark {
display: grid;
place-items: center;
width: 36px;
height: 36px;
background: var(--yellow);
color: #101113;
font-family: "Oswald", sans-serif;
font-weight: 700;
font-size: 0.95rem;
border-radius: var(--r-sm);
transform: skewX(-6deg);
}
.brand__txt {
font-family: "Oswald", sans-serif;
font-weight: 700;
letter-spacing: 0.06em;
font-size: 1.18rem;
}
.brand__txt em {
font-style: normal;
color: var(--yellow);
}
.nav__links {
display: flex;
gap: 26px;
margin-left: auto;
}
.nav__links a {
font-family: "Oswald", sans-serif;
text-transform: uppercase;
letter-spacing: 0.08em;
font-size: 0.86rem;
color: var(--ink-2);
position: relative;
padding: 6px 0;
transition: color 0.15s ease;
}
.nav__links a::after {
content: "";
position: absolute;
left: 0;
bottom: 0;
height: 2px;
width: 0;
background: var(--yellow);
transition: width 0.2s ease;
}
.nav__links a:hover {
color: var(--ink);
}
.nav__links a:hover::after {
width: 100%;
}
.nav__actions {
display: flex;
align-items: center;
gap: 10px;
}
.nav__burger {
display: none;
flex-direction: column;
gap: 4px;
background: none;
border: 1px solid var(--line-2);
border-radius: var(--r-sm);
padding: 9px 8px;
cursor: pointer;
}
.nav__burger span {
width: 20px;
height: 2px;
background: var(--ink);
transition: transform 0.2s ease, opacity 0.2s ease;
}
.nav__burger[aria-expanded="true"] span:nth-child(1) {
transform: translateY(6px) rotate(45deg);
}
.nav__burger[aria-expanded="true"] span:nth-child(2) {
opacity: 0;
}
.nav__burger[aria-expanded="true"] span:nth-child(3) {
transform: translateY(-6px) rotate(-45deg);
}
.nav__mobile {
display: flex;
flex-direction: column;
gap: 4px;
padding: 14px 4vw 22px;
border-bottom: 1px solid var(--line);
background: var(--surface);
}
.nav__mobile[hidden] {
display: none;
}
.nav__mobile a {
font-family: "Oswald", sans-serif;
text-transform: uppercase;
letter-spacing: 0.06em;
padding: 12px 8px;
border-bottom: 1px solid var(--line);
color: var(--ink-2);
}
.nav__mobile .btn {
margin-top: 12px;
}
/* ---- Hero ---- */
.hero {
position: relative;
padding: clamp(48px, 8vw, 96px) 0 clamp(56px, 9vw, 110px);
overflow: hidden;
background: radial-gradient(
900px 500px at 78% -10%,
rgba(255, 210, 31, 0.1),
transparent 60%
),
linear-gradient(180deg, var(--bg), #0c0d0f);
}
.hero__tape {
position: absolute;
inset: 0 auto 0 0;
width: 10px;
background: var(--tape);
}
.hero__inner {
display: grid;
grid-template-columns: 1.25fr 0.85fr;
gap: 48px;
align-items: center;
}
.hero__title {
font-family: "Oswald", sans-serif;
font-weight: 700;
text-transform: uppercase;
font-size: clamp(2.6rem, 7vw, 5rem);
line-height: 0.92;
margin: 0 0 20px;
letter-spacing: -0.01em;
}
.hero__title .hl {
color: var(--yellow);
position: relative;
}
.hero__lede {
color: var(--ink-2);
font-size: 1.12rem;
max-width: 46ch;
margin: 0 0 28px;
}
.hero__cta {
display: flex;
gap: 12px;
flex-wrap: wrap;
margin-bottom: 26px;
}
.hero__chips {
list-style: none;
display: flex;
gap: 26px;
padding: 0;
margin: 0;
flex-wrap: wrap;
}
.hero__chips li {
font-family: "Oswald", sans-serif;
text-transform: uppercase;
letter-spacing: 0.06em;
font-size: 0.84rem;
color: var(--ink-2);
padding-left: 14px;
position: relative;
}
.hero__chips li::before {
content: "";
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
width: 6px;
height: 6px;
background: var(--yellow);
}
.hero__card {
background: var(--surface);
border: 1px solid var(--line);
border-radius: var(--r-lg);
padding: 22px;
box-shadow: var(--shadow);
position: relative;
}
.hero__card::before {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
height: 6px;
background: var(--tape);
border-radius: var(--r-lg) var(--r-lg) 0 0;
}
.hero__card-top {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
margin-bottom: 18px;
flex-wrap: wrap;
}
.hero__temp {
font-size: 0.78rem;
color: var(--muted);
}
.hero__next {
margin-bottom: 14px;
}
.hero__next-name {
font-family: "Oswald", sans-serif;
font-weight: 700;
text-transform: uppercase;
font-size: 1.7rem;
margin: 6px 0 4px;
}
.hero__next-meta {
color: var(--ink-2);
margin: 0;
font-size: 0.92rem;
}
.hero__bars {
display: flex;
align-items: flex-end;
gap: 7px;
height: 64px;
margin: 18px 0;
}
.hero__bars span {
flex: 1;
height: var(--h);
background: linear-gradient(180deg, var(--yellow), var(--yellow-d));
border-radius: 2px 2px 0 0;
opacity: 0.85;
}
.hero__bars span:nth-child(even) {
background: var(--concrete);
}
/* ---- WOD board ---- */
.wod {
padding: clamp(56px, 9vw, 96px) 0;
background: var(--surface);
border-top: 1px solid var(--line);
border-bottom: 1px solid var(--line);
}
.board {
display: grid;
grid-template-columns: 1.4fr 0.9fr;
gap: 0;
border: 1px solid var(--line-2);
border-radius: var(--r-lg);
overflow: hidden;
background: #0b0c0d;
}
.board__main {
position: relative;
padding: 38px;
background: #0b0c0d
radial-gradient(
circle at 20% 0%,
rgba(255, 255, 255, 0.04),
transparent 60%
);
}
.board__chalk {
position: absolute;
inset: 0;
background: repeating-linear-gradient(
0deg,
transparent 0 30px,
rgba(255, 255, 255, 0.015) 30px 31px
);
pointer-events: none;
}
.board__type {
font-family: "Oswald", sans-serif;
text-transform: uppercase;
letter-spacing: 0.14em;
font-size: 0.82rem;
color: var(--yellow);
margin: 0 0 8px;
}
.board__name {
font-family: "Oswald", sans-serif;
font-weight: 700;
text-transform: uppercase;
font-size: clamp(2rem, 5vw, 3rem);
margin: 0 0 24px;
letter-spacing: 0.02em;
}
.board__moves {
list-style: none;
margin: 0 0 26px;
padding: 0;
counter-reset: m;
}
.board__moves li {
display: flex;
align-items: baseline;
gap: 14px;
padding: 12px 0;
border-bottom: 1px dashed var(--line);
font-size: 1.15rem;
}
.board__moves span {
font-family: "Oswald", sans-serif;
font-weight: 700;
color: var(--yellow);
min-width: 84px;
}
.board__moves em {
font-style: normal;
color: var(--muted);
font-size: 0.92rem;
}
.board__scale ul {
list-style: none;
padding: 0;
margin: 8px 0 0;
display: grid;
gap: 8px;
}
.board__scale li {
font-size: 0.95rem;
color: var(--ink-2);
}
.board__scale b {
display: inline-block;
min-width: 78px;
color: var(--ink);
font-family: "Oswald", sans-serif;
text-transform: uppercase;
letter-spacing: 0.04em;
font-size: 0.82rem;
}
.board__timer {
padding: 38px 30px;
background: var(--surface-2);
border-left: 1px solid var(--line-2);
display: flex;
flex-direction: column;
gap: 14px;
}
.timer__face {
font-family: "Oswald", sans-serif;
font-weight: 700;
font-size: clamp(3rem, 9vw, 4.6rem);
line-height: 1;
letter-spacing: 0.02em;
text-align: center;
font-variant-numeric: tabular-nums;
background: #0b0c0d;
border: 1px solid var(--line);
border-radius: var(--r-md);
padding: 18px 0;
color: var(--yellow);
}
.timer__face.is-running {
animation: glow 1s ease-in-out infinite alternate;
}
@keyframes glow {
to {
box-shadow: 0 0 0 1px var(--yellow-d) inset;
}
}
.timer__mode {
display: flex;
gap: 8px;
}
.chip {
flex: 1;
font-family: "Oswald", sans-serif;
text-transform: uppercase;
letter-spacing: 0.05em;
font-size: 0.78rem;
padding: 9px;
background: transparent;
color: var(--ink-2);
border: 1px solid var(--line-2);
border-radius: var(--r-sm);
cursor: pointer;
transition: all 0.15s ease;
}
.chip:hover {
border-color: var(--yellow);
}
.chip.is-active {
background: var(--yellow);
color: #101113;
border-color: var(--yellow);
}
.chip:focus-visible {
outline: 3px solid var(--yellow);
outline-offset: 2px;
}
.timer__btns {
display: flex;
gap: 8px;
}
.timer__hint {
font-size: 0.74rem;
text-align: center;
}
/* ---- Programs ---- */
.programs {
padding: clamp(56px, 9vw, 96px) 0;
}
.grid {
display: grid;
gap: 18px;
}
.grid--4 {
grid-template-columns: repeat(4, 1fr);
}
.prog {
position: relative;
background: var(--surface);
border: 1px solid var(--line);
border-radius: var(--r-md);
padding: 26px 22px 22px;
overflow: hidden;
transition: transform 0.18s ease, border-color 0.18s ease,
background 0.18s ease;
cursor: default;
}
.prog::before {
content: "";
position: absolute;
inset: 0 auto 0 0;
width: 4px;
background: var(--concrete);
transition: background 0.18s ease;
}
.prog:hover,
.prog:focus-visible {
transform: translateY(-4px);
border-color: var(--line-2);
background: var(--surface-2);
outline: none;
}
.prog:hover::before,
.prog:focus-visible::before {
background: var(--yellow);
}
.prog__num {
font-family: "Oswald", sans-serif;
font-weight: 700;
font-size: 1.6rem;
color: var(--concrete);
transition: color 0.18s ease;
}
.prog:hover .prog__num {
color: var(--yellow);
}
.prog h3 {
font-family: "Oswald", sans-serif;
text-transform: uppercase;
letter-spacing: 0.03em;
font-size: 1.3rem;
margin: 10px 0 8px;
}
.prog p {
color: var(--ink-2);
font-size: 0.92rem;
margin: 0 0 16px;
}
/* ---- Coaches ---- */
.coaches {
padding: clamp(56px, 9vw, 96px) 0;
background: var(--surface);
border-top: 1px solid var(--line);
}
.coach {
text-align: center;
}
.coach__photo {
aspect-ratio: 4 / 5;
border-radius: var(--r-md);
background: linear-gradient(160deg, var(--concrete), #16181b);
border: 1px solid var(--line);
margin-bottom: 14px;
display: grid;
place-items: center;
position: relative;
overflow: hidden;
transition: border-color 0.18s ease;
}
.coach__photo::after {
content: attr(data-init);
font-family: "Oswald", sans-serif;
font-weight: 700;
font-size: 3rem;
color: rgba(255, 255, 255, 0.14);
}
.coach__photo::before {
content: "";
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 5px;
background: var(--tape);
opacity: 0;
transition: opacity 0.18s ease;
}
.coach:hover .coach__photo {
border-color: var(--yellow);
}
.coach:hover .coach__photo::before {
opacity: 1;
}
.coach h3 {
font-family: "Oswald", sans-serif;
text-transform: uppercase;
font-size: 1.1rem;
margin: 0 0 4px;
}
.coach__role {
color: var(--yellow);
font-family: "Oswald", sans-serif;
text-transform: uppercase;
letter-spacing: 0.05em;
font-size: 0.76rem;
margin: 0 0 2px;
}
.coach__spec {
color: var(--muted);
font-size: 0.86rem;
margin: 0;
}
/* ---- Results ---- */
.results {
padding: clamp(56px, 9vw, 96px) 0;
}
.results__inner {
display: grid;
grid-template-columns: 1.1fr 0.9fr;
gap: 48px;
align-items: center;
}
.quote {
margin: 24px 0 0;
padding: 20px 24px;
border-left: 4px solid var(--yellow);
background: var(--surface);
border-radius: 0 var(--r-md) var(--r-md) 0;
font-size: 1.05rem;
color: var(--ink);
}
.quote cite {
display: block;
margin-top: 12px;
font-style: normal;
color: var(--muted);
font-size: 0.86rem;
}
.stats {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 14px;
}
.stat {
background: var(--surface);
border: 1px solid var(--line);
border-radius: var(--r-md);
padding: 26px 20px;
position: relative;
overflow: hidden;
}
.stat::before {
content: "";
position: absolute;
top: 0;
left: 0;
width: 36px;
height: 4px;
background: var(--yellow);
}
.stat__num {
display: block;
font-family: "Oswald", sans-serif;
font-weight: 700;
font-size: clamp(2.2rem, 5vw, 3rem);
line-height: 1;
font-variant-numeric: tabular-nums;
}
.stat__label {
display: block;
margin-top: 6px;
color: var(--ink-2);
font-family: "Oswald", sans-serif;
text-transform: uppercase;
letter-spacing: 0.06em;
font-size: 0.78rem;
}
/* ---- Schedule ---- */
.schedule {
padding: clamp(56px, 9vw, 96px) 0;
background: var(--surface);
border-top: 1px solid var(--line);
}
.sched {
border: 1px solid var(--line);
border-radius: var(--r-md);
overflow: hidden;
}
.sched__row {
display: grid;
grid-template-columns: 0.8fr 1.6fr 0.8fr 0.7fr 0.7fr;
align-items: center;
gap: 14px;
padding: 16px 22px;
border-bottom: 1px solid var(--line);
transition: background 0.15s ease;
}
.sched__row:last-child {
border-bottom: 0;
}
.sched__row:not(.sched__row--head):hover {
background: var(--surface-2);
}
.sched__row--head {
background: #0b0c0d;
font-family: "Oswald", sans-serif;
text-transform: uppercase;
letter-spacing: 0.08em;
font-size: 0.74rem;
color: var(--muted);
}
.sched__time {
font-family: "Oswald", sans-serif;
font-weight: 600;
font-size: 1.02rem;
}
/* ---- CTA band ---- */
.cta {
position: relative;
padding: clamp(64px, 10vw, 110px) 0;
background: var(--yellow);
color: #101113;
overflow: hidden;
}
.cta__tape {
position: absolute;
top: 0;
left: 0;
right: 0;
height: 8px;
background: repeating-linear-gradient(
-45deg,
#101113 0 22px,
var(--yellow) 22px 44px
);
}
.cta__inner {
text-align: center;
}
.cta h2 {
font-family: "Oswald", sans-serif;
font-weight: 700;
text-transform: uppercase;
font-size: clamp(2rem, 6vw, 4rem);
line-height: 0.95;
margin: 0 0 14px;
}
.cta p {
font-size: 1.1rem;
font-weight: 600;
margin: 0 0 28px;
}
/* ---- Footer ---- */
.foot {
background: var(--bg);
border-top: 1px solid var(--line);
padding: 36px 0;
}
.foot__inner {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
flex-wrap: wrap;
}
.foot .brand {
display: inline-flex;
}
.foot p {
margin: 0;
font-size: 0.84rem;
}
/* ---- Modal ---- */
.modal {
position: fixed;
inset: 0;
z-index: 300;
display: grid;
place-items: center;
padding: 20px;
}
.modal[hidden] {
display: none;
}
.modal__backdrop {
position: absolute;
inset: 0;
background: rgba(8, 9, 10, 0.78);
backdrop-filter: blur(3px);
}
.modal__panel {
position: relative;
width: min(460px, 100%);
background: var(--surface);
border: 1px solid var(--line-2);
border-radius: var(--r-lg);
padding: 32px 30px;
box-shadow: var(--shadow);
animation: pop 0.18s ease;
}
.modal__panel::before {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
height: 6px;
background: var(--tape);
border-radius: var(--r-lg) var(--r-lg) 0 0;
}
@keyframes pop {
from {
transform: translateY(12px) scale(0.98);
opacity: 0;
}
}
.modal__x {
position: absolute;
top: 14px;
right: 16px;
background: none;
border: 0;
color: var(--muted);
font-size: 1.8rem;
line-height: 1;
cursor: pointer;
}
.modal__x:hover {
color: var(--ink);
}
.modal__title {
font-family: "Oswald", sans-serif;
text-transform: uppercase;
font-size: 1.6rem;
margin: 0 0 8px;
}
.form {
display: grid;
gap: 14px;
margin-top: 20px;
}
.form label {
display: grid;
gap: 6px;
}
.form label span {
font-family: "Oswald", sans-serif;
text-transform: uppercase;
letter-spacing: 0.06em;
font-size: 0.74rem;
color: var(--ink-2);
}
.form input,
.form select {
background: #0b0c0d;
border: 1px solid var(--line-2);
border-radius: var(--r-sm);
color: var(--ink);
padding: 12px 14px;
font: inherit;
font-size: 0.95rem;
}
.form input:focus,
.form select:focus {
outline: none;
border-color: var(--yellow);
box-shadow: 0 0 0 3px var(--yellow-50);
}
.form input.is-invalid {
border-color: var(--danger);
}
/* ---- Toast ---- */
.toast {
position: fixed;
bottom: 24px;
left: 50%;
transform: translateX(-50%) translateY(20px);
background: var(--elevated);
color: var(--ink);
border: 1px solid var(--line-2);
border-left: 4px solid var(--yellow);
border-radius: var(--r-sm);
padding: 14px 20px;
font-weight: 600;
font-size: 0.92rem;
box-shadow: var(--shadow);
z-index: 400;
opacity: 0;
transition: opacity 0.25s ease, transform 0.25s ease;
}
.toast.is-show {
opacity: 1;
transform: translateX(-50%) translateY(0);
}
/* ---- Reveal ---- */
.reveal {
opacity: 0;
transform: translateY(22px);
transition: opacity 0.55s ease, transform 0.55s ease;
}
.reveal.is-in {
opacity: 1;
transform: none;
}
/* ---- Responsive ---- */
@media (max-width: 920px) {
.hero__inner,
.results__inner {
grid-template-columns: 1fr;
}
.board {
grid-template-columns: 1fr;
}
.board__timer {
border-left: 0;
border-top: 1px solid var(--line-2);
}
.grid--4 {
grid-template-columns: repeat(2, 1fr);
}
}
@media (max-width: 760px) {
.nav__links {
display: none;
}
.nav__actions .btn--ghost {
display: none;
}
.nav__burger {
display: flex;
}
.sched__row {
grid-template-columns: 0.7fr 1.4fr 0.6fr;
}
.sched__row span:nth-child(3),
.sched__row span:nth-child(4) {
display: none;
}
}
@media (max-width: 520px) {
.grid--4 {
grid-template-columns: 1fr;
}
.stats {
grid-template-columns: 1fr;
}
.hero__chips {
gap: 14px;
}
.board__main,
.board__timer {
padding: 26px 20px;
}
.board__moves li {
flex-direction: column;
gap: 2px;
}
.nav__actions .btn[data-book] {
display: none;
}
.foot__inner {
flex-direction: column;
align-items: flex-start;
text-align: left;
}
}
@media (prefers-reduced-motion: reduce) {
* {
animation: none !important;
scroll-behavior: auto;
}
.reveal {
opacity: 1;
transform: none;
}
}(function () {
"use strict";
/* ---------- Toast helper ---------- */
var toastEl = document.getElementById("toast");
var toastTimer = null;
function toast(msg) {
if (!toastEl) return;
toastEl.textContent = msg;
toastEl.hidden = false;
// force reflow so the transition runs
void toastEl.offsetWidth;
toastEl.classList.add("is-show");
clearTimeout(toastTimer);
toastTimer = setTimeout(function () {
toastEl.classList.remove("is-show");
setTimeout(function () {
toastEl.hidden = true;
}, 280);
}, 2800);
}
/* ---------- Today's date on the whiteboard ---------- */
var wodDate = document.getElementById("wodDate");
if (wodDate) {
try {
wodDate.textContent = new Date().toLocaleDateString(undefined, {
weekday: "long",
month: "long",
day: "numeric",
});
} catch (e) {
/* keep fallback text */
}
}
/* ---------- Sticky nav shadow ---------- */
var nav = document.getElementById("nav");
function onScroll() {
if (!nav) return;
nav.classList.toggle("is-scrolled", window.scrollY > 8);
}
window.addEventListener("scroll", onScroll, { passive: true });
onScroll();
/* ---------- Mobile menu ---------- */
var burger = document.getElementById("burger");
var mobileMenu = document.getElementById("mobileMenu");
function setMenu(open) {
if (!burger || !mobileMenu) return;
burger.setAttribute("aria-expanded", String(open));
mobileMenu.hidden = !open;
burger.setAttribute("aria-label", open ? "Close menu" : "Open menu");
}
if (burger) {
burger.addEventListener("click", function () {
setMenu(burger.getAttribute("aria-expanded") !== "true");
});
}
if (mobileMenu) {
mobileMenu.addEventListener("click", function (e) {
if (e.target.closest("a")) setMenu(false);
});
}
/* ---------- Reveal on scroll ---------- */
var reveals = document.querySelectorAll(".reveal");
if ("IntersectionObserver" in window) {
var io = new IntersectionObserver(
function (entries) {
entries.forEach(function (entry) {
if (entry.isIntersecting) {
entry.target.classList.add("is-in");
io.unobserve(entry.target);
if (entry.target.querySelector("[data-count]")) runCounters(entry.target);
}
});
},
{ threshold: 0.16 }
);
reveals.forEach(function (el) {
io.observe(el);
});
} else {
reveals.forEach(function (el) {
el.classList.add("is-in");
});
runCounters(document);
}
/* ---------- Count-up stats ---------- */
function runCounters(scope) {
var nums = scope.querySelectorAll("[data-count]");
nums.forEach(function (el) {
if (el.dataset.done) return;
el.dataset.done = "1";
var target = parseInt(el.getAttribute("data-count"), 10) || 0;
var suffix = el.getAttribute("data-suffix") || "";
var dur = 1300;
var start = performance.now();
function tick(now) {
var p = Math.min((now - start) / dur, 1);
var eased = 1 - Math.pow(1 - p, 3);
el.textContent = Math.round(target * eased).toLocaleString() + suffix;
if (p < 1) requestAnimationFrame(tick);
}
requestAnimationFrame(tick);
});
}
/* ---------- WOD timer demo ---------- */
var face = document.getElementById("timerFace");
var startBtn = document.getElementById("timerStart");
var resetBtn = document.getElementById("timerReset");
var modeBtns = document.querySelectorAll("[data-mode]");
var AMRAP_SECS = 12 * 60;
var mode = "stopwatch";
var running = false;
var rafId = null;
var startTs = 0;
var elapsedAtPause = 0;
function fmt(totalSecs) {
totalSecs = Math.max(0, Math.floor(totalSecs));
var m = Math.floor(totalSecs / 60);
var s = totalSecs % 60;
return String(m).padStart(2, "0") + ":" + String(s).padStart(2, "0");
}
function render(elapsed) {
if (!face) return;
if (mode === "amrap") {
var remaining = AMRAP_SECS - elapsed;
if (remaining <= 0) {
face.textContent = "00:00";
stopTimer(true);
toast("Time! AMRAP complete.");
return;
}
face.textContent = fmt(remaining);
} else {
face.textContent = fmt(elapsed);
}
}
function loop(now) {
var elapsed = elapsedAtPause + (now - startTs) / 1000;
render(elapsed);
if (running) rafId = requestAnimationFrame(loop);
}
function startTimer() {
if (running) return;
running = true;
startTs = performance.now();
if (face) face.classList.add("is-running");
if (startBtn) startBtn.textContent = "Pause";
rafId = requestAnimationFrame(loop);
}
function pauseTimer() {
if (!running) return;
running = false;
elapsedAtPause += (performance.now() - startTs) / 1000;
if (rafId) cancelAnimationFrame(rafId);
if (face) face.classList.remove("is-running");
if (startBtn) startBtn.textContent = "Resume";
}
function stopTimer(finished) {
running = false;
if (rafId) cancelAnimationFrame(rafId);
if (face) face.classList.remove("is-running");
if (startBtn) startBtn.textContent = finished ? "Start" : "Resume";
if (finished) elapsedAtPause = 0;
}
function resetTimer() {
running = false;
if (rafId) cancelAnimationFrame(rafId);
elapsedAtPause = 0;
if (face) face.classList.remove("is-running");
if (startBtn) startBtn.textContent = "Start";
render(0);
}
if (startBtn) {
startBtn.addEventListener("click", function () {
if (running) pauseTimer();
else startTimer();
});
}
if (resetBtn) resetBtn.addEventListener("click", resetTimer);
modeBtns.forEach(function (b) {
b.addEventListener("click", function () {
modeBtns.forEach(function (x) {
x.classList.remove("is-active");
});
b.classList.add("is-active");
mode = b.getAttribute("data-mode");
resetTimer();
});
});
render(0);
/* ---------- Booking modal (focus trap) ---------- */
var modal = document.getElementById("modal");
var form = document.getElementById("bookForm");
var lastFocused = null;
function getFocusable() {
return modal
? Array.prototype.slice.call(
modal.querySelectorAll(
'button, input, select, textarea, a[href], [tabindex]:not([tabindex="-1"])'
)
).filter(function (el) {
return !el.disabled && el.offsetParent !== null;
})
: [];
}
function openModal() {
if (!modal) return;
lastFocused = document.activeElement;
modal.hidden = false;
setMenu(false);
document.body.style.overflow = "hidden";
var f = getFocusable();
if (f[0]) f[0].focus();
document.addEventListener("keydown", onModalKey);
}
function closeModal() {
if (!modal) return;
modal.hidden = true;
document.body.style.overflow = "";
document.removeEventListener("keydown", onModalKey);
if (lastFocused && lastFocused.focus) lastFocused.focus();
}
function onModalKey(e) {
if (e.key === "Escape") {
closeModal();
return;
}
if (e.key === "Tab") {
var f = getFocusable();
if (!f.length) return;
var first = f[0];
var last = f[f.length - 1];
if (e.shiftKey && document.activeElement === first) {
e.preventDefault();
last.focus();
} else if (!e.shiftKey && document.activeElement === last) {
e.preventDefault();
first.focus();
}
}
}
document.addEventListener("click", function (e) {
if (e.target.closest("[data-book]")) {
e.preventDefault();
openModal();
} else if (e.target.closest("[data-close]")) {
closeModal();
} else if (e.target.closest("[data-waitlist]")) {
toast("Added to the waitlist — we'll text you if a spot opens.");
}
});
if (form) {
form.addEventListener("submit", function (e) {
e.preventDefault();
var name = form.elements.name;
var email = form.elements.email;
var valid = true;
[name, email].forEach(function (input) {
input.classList.remove("is-invalid");
});
if (!name.value.trim()) {
name.classList.add("is-invalid");
valid = false;
}
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email.value.trim())) {
email.classList.add("is-invalid");
valid = false;
}
if (!valid) {
toast("Check the highlighted fields.");
return;
}
var who = name.value.trim().split(" ")[0];
closeModal();
form.reset();
toast("Booked! See you on the floor, " + who + ".");
});
}
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>IRONHAUS — CrossFit Box</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&family=Oswald:wght@500;600;700&display=swap"
rel="stylesheet"
/>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<a class="skip" href="#main">Skip to content</a>
<!-- ===== Nav ===== -->
<header class="nav" id="nav">
<div class="wrap nav__inner">
<a href="#top" class="brand" aria-label="Ironhaus home">
<span class="brand__mark" aria-hidden="true">IH</span>
<span class="brand__txt">IRON<em>HAUS</em></span>
</a>
<nav class="nav__links" aria-label="Primary">
<a href="#wod">WOD</a>
<a href="#programs">Programs</a>
<a href="#coaches">Coaches</a>
<a href="#results">Results</a>
<a href="#schedule">Schedule</a>
</nav>
<div class="nav__actions">
<a class="btn btn--ghost btn--sm" href="#schedule">Schedule</a>
<button class="btn btn--yellow btn--sm" data-book>Book intro WOD</button>
<button class="nav__burger" id="burger" aria-label="Open menu" aria-expanded="false" aria-controls="mobileMenu">
<span></span><span></span><span></span>
</button>
</div>
</div>
<div class="nav__mobile" id="mobileMenu" hidden>
<a href="#wod">WOD of the day</a>
<a href="#programs">Programs</a>
<a href="#coaches">Coaches</a>
<a href="#results">Results</a>
<a href="#schedule">Schedule</a>
<button class="btn btn--yellow" data-book>Book your intro WOD</button>
</div>
</header>
<main id="main">
<!-- ===== Hero ===== -->
<section class="hero" id="top">
<div class="hero__tape" aria-hidden="true"></div>
<div class="wrap hero__inner">
<div class="hero__copy reveal">
<p class="eyebrow"><span class="dot"></span> Est. 2014 · No. 47 Forge Street</p>
<h1 class="hero__title">
FORGE A <span class="hl">STRONGER</span><br />VERSION OF YOU.
</h1>
<p class="hero__lede">
A no-frills CrossFit box built on barbells, sweat and community. Coached classes,
real scaling, zero ego. Your first session is on us.
</p>
<div class="hero__cta">
<button class="btn btn--yellow btn--lg" data-book>Book your intro WOD</button>
<a class="btn btn--ghost btn--lg" href="#programs">See programs</a>
</div>
<ul class="hero__chips">
<li>900+ members</li>
<li>6 coaches</li>
<li>32 classes / week</li>
</ul>
</div>
<aside class="hero__card reveal" aria-label="Next class">
<header class="hero__card-top">
<span class="badge badge--live"><span class="pulse"></span> Box open now</span>
<span class="hero__temp">14°C · garage doors up</span>
</header>
<div class="hero__next">
<p class="muted up">Next class</p>
<p class="hero__next-name">Metcon Hour</p>
<p class="hero__next-meta">5:30 PM · Coach Marisol · 8 spots left</p>
</div>
<div class="hero__bars" aria-hidden="true">
<span style="--h: 62%"></span>
<span style="--h: 88%"></span>
<span style="--h: 40%"></span>
<span style="--h: 95%"></span>
<span style="--h: 71%"></span>
<span style="--h: 54%"></span>
</div>
<button class="btn btn--block btn--dark" data-book>Reserve a spot</button>
</aside>
</div>
</section>
<!-- ===== WOD board ===== -->
<section class="wod" id="wod">
<div class="wrap">
<div class="sec-head reveal">
<p class="eyebrow eyebrow--y"><span class="dot"></span> Whiteboard</p>
<h2 class="sec-title">WOD OF THE DAY</h2>
<p class="sec-sub" id="wodDate">Today</p>
</div>
<div class="board reveal">
<div class="board__main">
<div class="board__chalk" aria-hidden="true"></div>
<p class="board__type">For Time · 20 min cap</p>
<h3 class="board__name">“THE ANVIL”</h3>
<ol class="board__moves">
<li><span>21-15-9</span> Thrusters <em>(43/30 kg)</em></li>
<li><span>21-15-9</span> Pull-ups</li>
<li><span>400 m</span> Run after each round</li>
</ol>
<div class="board__scale">
<p class="up muted">Scaling</p>
<ul>
<li><b>RX</b> as written</li>
<li><b>Scaled</b> 30/20 kg · banded pull-ups</li>
<li><b>Beginner</b> goblet squats · ring rows · 200 m</li>
</ul>
</div>
</div>
<aside class="board__timer" aria-label="WOD timer demo">
<p class="up muted">Box timer</p>
<div class="timer__face" id="timerFace" role="timer" aria-live="polite">00:00</div>
<div class="timer__mode" role="group" aria-label="Timer mode">
<button class="chip is-active" data-mode="stopwatch">Stopwatch</button>
<button class="chip" data-mode="amrap">AMRAP 12</button>
</div>
<div class="timer__btns">
<button class="btn btn--yellow btn--block" id="timerStart">Start</button>
<button class="btn btn--ghost btn--block" id="timerReset">Reset</button>
</div>
<p class="timer__hint muted">Demo timer — counts up, or down from 12:00.</p>
</aside>
</div>
</div>
</section>
<!-- ===== Programs ===== -->
<section class="programs" id="programs">
<div class="wrap">
<div class="sec-head reveal">
<p class="eyebrow eyebrow--y"><span class="dot"></span> Train with intent</p>
<h2 class="sec-title">PROGRAMS</h2>
<p class="sec-sub">Pick your path — or stack them all. Every class is coach-led.</p>
</div>
<div class="grid grid--4">
<article class="prog reveal" tabindex="0">
<span class="prog__num">01</span>
<h3>CrossFit</h3>
<p>Daily varied WODs blending barbell, gymnastics and engine work. The core of the box.</p>
<span class="tag">60 min · all levels</span>
</article>
<article class="prog reveal" tabindex="0">
<span class="prog__num">02</span>
<h3>Olympic Lifting</h3>
<p>Snatch and clean & jerk technique, accessory strength and tested PR cycles.</p>
<span class="tag">75 min · intermediate</span>
</article>
<article class="prog reveal" tabindex="0">
<span class="prog__num">03</span>
<h3>Gymnastics</h3>
<p>From your first pull-up to muscle-ups, handstands and ring strength progressions.</p>
<span class="tag">50 min · skill focus</span>
</article>
<article class="prog reveal" tabindex="0">
<span class="prog__num">04</span>
<h3>Conditioning</h3>
<p>Pure engine — rowers, bikes and intervals to build a relentless aerobic base.</p>
<span class="tag">45 min · all levels</span>
</article>
</div>
</div>
</section>
<!-- ===== Coaches ===== -->
<section class="coaches" id="coaches">
<div class="wrap">
<div class="sec-head reveal">
<p class="eyebrow eyebrow--y"><span class="dot"></span> The crew</p>
<h2 class="sec-title">YOUR COACHES</h2>
<p class="sec-sub">Certified, competitive, and genuinely in your corner.</p>
</div>
<div class="grid grid--4 coaches__row">
<article class="coach reveal">
<div class="coach__photo" data-init="MR" aria-hidden="true"></div>
<h3>Marisol Reyes</h3>
<p class="coach__role">Head Coach · L3</p>
<p class="coach__spec">Metcons & mindset</p>
</article>
<article class="coach reveal">
<div class="coach__photo" data-init="DK" aria-hidden="true"></div>
<h3>Darius King</h3>
<p class="coach__role">Barbell Coach · L2</p>
<p class="coach__spec">Olympic lifting</p>
</article>
<article class="coach reveal">
<div class="coach__photo" data-init="TN" aria-hidden="true"></div>
<h3>Tara Nowak</h3>
<p class="coach__role">Gymnastics · L2</p>
<p class="coach__spec">Skill progressions</p>
</article>
<article class="coach reveal">
<div class="coach__photo" data-init="OB" aria-hidden="true"></div>
<h3>Omar Bello</h3>
<p class="coach__role">Conditioning · L1</p>
<p class="coach__spec">Engine & rowing</p>
</article>
</div>
</div>
</section>
<!-- ===== Results / community ===== -->
<section class="results" id="results">
<div class="wrap results__inner">
<div class="results__copy reveal">
<p class="eyebrow eyebrow--y"><span class="dot"></span> Proof, not promises</p>
<h2 class="sec-title">A COMMUNITY THAT SHOWS UP.</h2>
<p class="sec-sub">
We track the numbers that matter — consistency, PRs and people who keep coming back.
These are our last twelve months.
</p>
<blockquote class="quote">
“I walked in unable to do a single pull-up. Eight months later I hit my first
bar muscle-up. This place ruins gyms for you — in the best way.”
<cite>— Priya Anand, member since 2024</cite>
</blockquote>
</div>
<div class="stats reveal">
<div class="stat">
<span class="stat__num" data-count="912" data-suffix="">0</span>
<span class="stat__label">Active members</span>
</div>
<div class="stat">
<span class="stat__num" data-count="48" data-suffix="k">0</span>
<span class="stat__label">Classes coached</span>
</div>
<div class="stat">
<span class="stat__num" data-count="1340" data-suffix="">0</span>
<span class="stat__label">PRs logged</span>
</div>
<div class="stat">
<span class="stat__num" data-count="94" data-suffix="%">0</span>
<span class="stat__label">Retention rate</span>
</div>
</div>
</div>
</section>
<!-- ===== Schedule teaser ===== -->
<section class="schedule" id="schedule">
<div class="wrap">
<div class="sec-head reveal">
<p class="eyebrow eyebrow--y"><span class="dot"></span> This week</p>
<h2 class="sec-title">CLASS SCHEDULE</h2>
<p class="sec-sub">A taste of a typical day. Full timetable in the member app.</p>
</div>
<div class="sched reveal" role="table" aria-label="Class schedule teaser">
<div class="sched__row sched__row--head" role="row">
<span role="columnheader">Time</span>
<span role="columnheader">Class</span>
<span role="columnheader">Coach</span>
<span role="columnheader">Spots</span>
<span role="columnheader" aria-hidden="true"></span>
</div>
<div class="sched__row" role="row">
<span role="cell" class="sched__time">6:00 AM</span>
<span role="cell">CrossFit · Sunrise</span>
<span role="cell" class="muted">Omar</span>
<span role="cell"><b class="ok">12 left</b></span>
<span role="cell"><button class="btn btn--yellow btn--xs" data-book>Book</button></span>
</div>
<div class="sched__row" role="row">
<span role="cell" class="sched__time">9:30 AM</span>
<span role="cell">Olympic Lifting</span>
<span role="cell" class="muted">Darius</span>
<span role="cell"><b class="warn">4 left</b></span>
<span role="cell"><button class="btn btn--yellow btn--xs" data-book>Book</button></span>
</div>
<div class="sched__row" role="row">
<span role="cell" class="sched__time">12:00 PM</span>
<span role="cell">Conditioning</span>
<span role="cell" class="muted">Marisol</span>
<span role="cell"><b class="ok">9 left</b></span>
<span role="cell"><button class="btn btn--yellow btn--xs" data-book>Book</button></span>
</div>
<div class="sched__row" role="row">
<span role="cell" class="sched__time">5:30 PM</span>
<span role="cell">Metcon Hour</span>
<span role="cell" class="muted">Marisol</span>
<span role="cell"><b class="warn">8 left</b></span>
<span role="cell"><button class="btn btn--yellow btn--xs" data-book>Book</button></span>
</div>
<div class="sched__row" role="row">
<span role="cell" class="sched__time">7:00 PM</span>
<span role="cell">Gymnastics Skills</span>
<span role="cell" class="muted">Tara</span>
<span role="cell"><b class="danger">Full</b></span>
<span role="cell"><button class="btn btn--ghost btn--xs" data-waitlist>Waitlist</button></span>
</div>
</div>
</div>
</section>
<!-- ===== CTA band ===== -->
<section class="cta">
<div class="cta__tape" aria-hidden="true"></div>
<div class="wrap cta__inner reveal">
<h2>STOP THINKING ABOUT IT.<br />START LIFTING.</h2>
<p>First class free. No contracts. Just walk in and try.</p>
<button class="btn btn--dark btn--lg" data-book>Book your intro WOD</button>
</div>
</section>
</main>
<footer class="foot">
<div class="wrap foot__inner">
<span class="brand">
<span class="brand__mark" aria-hidden="true">IH</span>
<span class="brand__txt">IRON<em>HAUS</em></span>
</span>
<p class="muted">No. 47 Forge Street · Open Mon–Sat · Fictional demo box.</p>
<p class="muted">© 2026 Ironhaus CrossFit. Illustrative UI only.</p>
</div>
</footer>
<!-- ===== Booking dialog ===== -->
<div class="modal" id="modal" hidden>
<div class="modal__backdrop" data-close></div>
<div class="modal__panel" role="dialog" aria-modal="true" aria-labelledby="modalTitle">
<button class="modal__x" data-close aria-label="Close">×</button>
<p class="eyebrow eyebrow--y"><span class="dot"></span> Free intro session</p>
<h2 id="modalTitle" class="modal__title">BOOK YOUR INTRO WOD</h2>
<p class="muted">No experience needed — we scale everything. Tell us when to expect you.</p>
<form class="form" id="bookForm" novalidate>
<label>
<span>Full name</span>
<input type="text" name="name" required placeholder="Alex Carter" />
</label>
<label>
<span>Email</span>
<input type="email" name="email" required placeholder="[email protected]" />
</label>
<label>
<span>Preferred class</span>
<select name="class">
<option>6:00 AM · CrossFit Sunrise</option>
<option selected>5:30 PM · Metcon Hour</option>
<option>9:30 AM · Olympic Lifting</option>
<option>7:00 PM · Gymnastics Skills</option>
</select>
</label>
<button class="btn btn--yellow btn--block btn--lg" type="submit">Confirm booking</button>
</form>
</div>
</div>
<div class="toast" id="toast" role="status" aria-live="polite" hidden></div>
<script src="script.js"></script>
</body>
</html>CrossFit Box Landing
A gritty, high-contrast marketing page for Ironhaus, a fictional CrossFit box, built on a near-black, safety-yellow and concrete theme with caution-stripe accents and hard edges. A sticky, frosted nav gains a shadow as you scroll and collapses into a mobile menu below 760px. The condensed Oswald display hero pairs a Forge a stronger version of you headline and a free intro-WOD call to action with a live “box open” status card showing the next class and an animated bar motif.
The centrepiece is a whiteboard WOD-of-the-day block: the workout is written out in full with loads and a 21-15-9 scheme, three scaling tiers (RX, Scaled, Beginner), and a working box timer beside it that runs as a count-up stopwatch or counts down from a 12-minute AMRAP, with start, pause, resume and reset. Below sit a four-program grid (CrossFit, Olympic Lifting, Gymnastics, Conditioning), a four-coach row, a community results section whose stats count up the first time they scroll into view, and a class schedule teaser with live spot counts and per-row booking.
Every section reveals on scroll, the date on the whiteboard reflects today, and any Book button opens an accessible modal with a focus trap, Escape-to-close and inline validation; confirming fires a friendly toast. All interactions are vanilla JS with no external libraries.
Illustrative UI only — fictional box, fictional names and data.