Gym — Yoga / Pilates Studio Landing
A serene, light-filled landing page for a fictional yoga and Pilates studio, built in a bone-and-sage wellness palette with a serif-paired type system. It opens on a calm gradient hero with floating orbs and a book-a-class call to action, then flows through class-style cards, an instructors row, a weekly schedule teaser, three membership packs, a rotating testimonial and a newsletter footer — all stitched together with gentle scroll reveals and soft micro-interactions in vanilla JavaScript.
MCP
الكود
:root {
--bg: #f6f3ec;
--surface: #ffffff;
--surface-2: #fbf9f4;
--ink: #2e2a24;
--ink-2: #5b554b;
--muted: #8c8579;
--sage: #8a9a7b;
--sage-d: #6f8061;
--rose: #c98b86;
--rose-soft: #f0dedb;
--line: rgba(46, 42, 36, 0.1);
--line-2: rgba(46, 42, 36, 0.18);
--r-sm: 10px;
--r-md: 18px;
--r-lg: 28px;
--r-pill: 999px;
--shadow-sm: 0 2px 10px rgba(46, 42, 36, 0.05);
--shadow-md: 0 14px 40px rgba(46, 42, 36, 0.08);
--shadow-lg: 0 30px 70px rgba(46, 42, 36, 0.12);
--serif: "Cormorant Garamond", Georgia, "Times New Roman", serif;
--sans: "Inter", system-ui, -apple-system, sans-serif;
--maxw: 1120px;
--ease: cubic-bezier(0.22, 0.61, 0.36, 1);
}
*,
*::before,
*::after {
box-sizing: border-box;
}
* {
margin: 0;
}
html {
scroll-behavior: smooth;
}
body {
font-family: var(--sans);
background: var(--bg);
color: var(--ink);
line-height: 1.5;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
img {
max-width: 100%;
display: block;
}
a {
color: inherit;
text-decoration: none;
}
button {
font: inherit;
cursor: pointer;
}
.wrap {
width: 100%;
max-width: var(--maxw);
margin: 0 auto;
padding: 0 24px;
}
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
.skip-link {
position: absolute;
left: -999px;
top: 0;
z-index: 200;
background: var(--ink);
color: var(--bg);
padding: 10px 16px;
border-radius: 0 0 var(--r-sm) 0;
}
.skip-link:focus {
left: 0;
}
:focus-visible {
outline: 3px solid var(--sage);
outline-offset: 3px;
border-radius: 4px;
}
/* ---------- Typography helpers ---------- */
.eyebrow {
font-family: var(--sans);
text-transform: uppercase;
letter-spacing: 0.22em;
font-size: 0.72rem;
font-weight: 600;
color: var(--sage-d);
margin-bottom: 0.9rem;
}
.section-title {
font-family: var(--serif);
font-weight: 500;
font-size: clamp(1.9rem, 4vw, 3rem);
line-height: 1.08;
letter-spacing: -0.01em;
}
.section-sub {
color: var(--ink-2);
max-width: 46ch;
margin: 0.9rem auto 0;
font-size: 1.02rem;
}
/* ---------- Buttons ---------- */
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
border: 1px solid transparent;
border-radius: var(--r-pill);
padding: 0.7rem 1.4rem;
font-weight: 600;
font-size: 0.95rem;
transition: transform 0.2s var(--ease), background 0.2s var(--ease),
box-shadow 0.2s var(--ease), border-color 0.2s var(--ease);
}
.btn:hover {
transform: translateY(-2px);
}
.btn:active {
transform: translateY(0);
}
.btn-lg {
padding: 0.95rem 1.9rem;
font-size: 1.02rem;
}
.btn-block {
width: 100%;
}
.btn-primary {
background: var(--sage);
color: #fff;
box-shadow: var(--shadow-sm);
}
.btn-primary:hover {
background: var(--sage-d);
box-shadow: var(--shadow-md);
}
.btn-ghost {
background: transparent;
color: var(--ink);
border-color: var(--line-2);
}
.btn-ghost:hover {
background: var(--surface);
border-color: var(--ink);
}
/* ---------- Header ---------- */
.site-header {
position: sticky;
top: 0;
z-index: 100;
background: rgba(246, 243, 236, 0.78);
backdrop-filter: blur(14px);
-webkit-backdrop-filter: blur(14px);
border-bottom: 1px solid transparent;
transition: border-color 0.3s var(--ease), background 0.3s var(--ease);
}
.site-header.scrolled {
border-bottom-color: var(--line);
background: rgba(246, 243, 236, 0.92);
}
.header-inner {
display: flex;
align-items: center;
justify-content: space-between;
height: 74px;
gap: 1rem;
}
.brand {
display: inline-flex;
align-items: center;
gap: 0.6rem;
}
.brand-mark {
width: 26px;
height: 26px;
border-radius: 50% 50% 50% 0;
background: linear-gradient(135deg, var(--sage), var(--rose));
transform: rotate(-20deg);
flex: none;
box-shadow: var(--shadow-sm);
}
.brand-name {
font-family: var(--serif);
font-size: 1.5rem;
font-weight: 600;
letter-spacing: 0.01em;
}
.nav {
display: flex;
gap: 1.8rem;
font-size: 0.95rem;
font-weight: 500;
}
.nav a {
position: relative;
color: var(--ink-2);
padding: 0.25rem 0;
transition: color 0.2s var(--ease);
}
.nav a::after {
content: "";
position: absolute;
left: 0;
bottom: -2px;
width: 0;
height: 2px;
background: var(--sage);
border-radius: 2px;
transition: width 0.25s var(--ease);
}
.nav a:hover {
color: var(--ink);
}
.nav a:hover::after {
width: 100%;
}
.header-actions {
display: flex;
gap: 0.7rem;
align-items: center;
}
.nav-toggle {
display: none;
flex-direction: column;
gap: 5px;
background: transparent;
border: 1px solid var(--line-2);
border-radius: var(--r-sm);
padding: 10px;
}
.nav-toggle span {
width: 22px;
height: 2px;
background: var(--ink);
border-radius: 2px;
transition: transform 0.25s var(--ease), opacity 0.25s var(--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;
overflow: hidden;
padding: clamp(4rem, 9vw, 8rem) 0 clamp(3.5rem, 7vw, 6rem);
background: radial-gradient(
120% 90% at 80% -10%,
var(--rose-soft) 0%,
transparent 55%
),
radial-gradient(90% 80% at 0% 110%, rgba(138, 154, 123, 0.18) 0%, transparent 55%);
}
.hero-orb {
position: absolute;
border-radius: 50%;
filter: blur(50px);
opacity: 0.6;
z-index: 0;
animation: float 13s ease-in-out infinite;
}
.hero-orb--1 {
width: 320px;
height: 320px;
background: rgba(201, 139, 134, 0.4);
top: -60px;
right: -40px;
}
.hero-orb--2 {
width: 260px;
height: 260px;
background: rgba(138, 154, 123, 0.4);
bottom: -80px;
left: -30px;
animation-delay: -5s;
}
@keyframes float {
0%,
100% {
transform: translateY(0) translateX(0);
}
50% {
transform: translateY(-26px) translateX(14px);
}
}
.hero-inner {
position: relative;
z-index: 1;
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
}
.hero-title {
font-family: var(--serif);
font-weight: 500;
font-size: clamp(2.6rem, 7vw, 5rem);
line-height: 1.04;
letter-spacing: -0.015em;
}
.hero-title em {
font-style: italic;
color: var(--sage-d);
}
.hero-lede {
margin-top: 1.3rem;
max-width: 54ch;
color: var(--ink-2);
font-size: clamp(1rem, 2vw, 1.18rem);
}
.hero-cta {
display: flex;
gap: 0.9rem;
margin-top: 2rem;
flex-wrap: wrap;
justify-content: center;
}
.hero-stats {
list-style: none;
padding: 0;
margin: 3rem 0 0;
display: flex;
gap: clamp(1.5rem, 5vw, 3.5rem);
flex-wrap: wrap;
justify-content: center;
}
.hero-stats li {
display: flex;
flex-direction: column;
gap: 0.2rem;
}
.hero-stats strong {
font-family: var(--serif);
font-size: 2rem;
font-weight: 600;
color: var(--ink);
}
.hero-stats span {
font-size: 0.78rem;
text-transform: uppercase;
letter-spacing: 0.12em;
color: var(--muted);
}
/* ---------- Sections ---------- */
.section {
padding: clamp(4rem, 8vw, 7rem) 0;
}
.section--alt {
background: var(--surface-2);
border-top: 1px solid var(--line);
border-bottom: 1px solid var(--line);
}
.section-head {
text-align: center;
max-width: 640px;
margin: 0 auto clamp(2.5rem, 5vw, 3.5rem);
}
/* ---------- Class cards ---------- */
.class-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 1.4rem;
}
.class-card {
position: relative;
background: var(--surface);
border: 1px solid var(--line);
border-radius: var(--r-lg);
padding: 2rem 1.7rem 1.8rem;
box-shadow: var(--shadow-sm);
transition: transform 0.3s var(--ease), box-shadow 0.3s var(--ease),
border-color 0.3s var(--ease);
overflow: hidden;
}
.class-card::before {
content: "";
position: absolute;
inset: 0 auto auto 0;
width: 100%;
height: 4px;
background: linear-gradient(90deg, var(--sage), var(--rose));
transform: scaleX(0);
transform-origin: left;
transition: transform 0.4s var(--ease);
}
.class-card:hover {
transform: translateY(-6px);
box-shadow: var(--shadow-lg);
border-color: transparent;
}
.class-card:hover::before {
transform: scaleX(1);
}
.class-badge {
display: inline-block;
font-size: 0.7rem;
text-transform: uppercase;
letter-spacing: 0.12em;
font-weight: 600;
color: var(--sage-d);
background: rgba(138, 154, 123, 0.14);
padding: 0.3rem 0.7rem;
border-radius: var(--r-pill);
margin-bottom: 1rem;
}
.class-card h3 {
font-family: var(--serif);
font-size: 1.65rem;
font-weight: 600;
margin-bottom: 0.6rem;
}
.class-card p {
color: var(--ink-2);
font-size: 0.95rem;
}
.class-meta {
margin-top: 1.1rem;
font-size: 0.82rem !important;
color: var(--muted) !important;
font-weight: 500;
}
/* ---------- Teachers ---------- */
.teacher-row {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
gap: 1.4rem;
}
.teacher {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
gap: 1rem;
background: var(--surface);
border: 1px solid var(--line);
border-radius: var(--r-lg);
padding: 2rem 1.4rem;
box-shadow: var(--shadow-sm);
transition: transform 0.3s var(--ease), box-shadow 0.3s var(--ease);
}
.teacher:hover {
transform: translateY(-5px);
box-shadow: var(--shadow-md);
}
.teacher-avatar {
width: 78px;
height: 78px;
border-radius: 50%;
display: grid;
place-items: center;
font-family: var(--serif);
font-size: 1.6rem;
font-weight: 600;
color: #fff;
background: linear-gradient(135deg, var(--sage), var(--sage-d));
box-shadow: var(--shadow-sm);
}
.teacher:nth-child(2n) .teacher-avatar {
background: linear-gradient(135deg, var(--rose), #b1726d);
}
.teacher figcaption {
display: flex;
flex-direction: column;
gap: 0.25rem;
}
.teacher figcaption strong {
font-family: var(--serif);
font-size: 1.25rem;
font-weight: 600;
}
.teacher figcaption span {
font-size: 0.82rem;
color: var(--muted);
text-transform: uppercase;
letter-spacing: 0.08em;
}
/* ---------- Schedule ---------- */
.schedule-list {
list-style: none;
padding: 0;
margin: 0;
max-width: 760px;
margin-inline: auto;
display: flex;
flex-direction: column;
gap: 0.7rem;
}
.schedule-item {
display: grid;
grid-template-columns: 56px 64px 1fr auto auto;
align-items: center;
gap: 1rem;
background: var(--surface);
border: 1px solid var(--line);
border-radius: var(--r-md);
padding: 1rem 1.3rem;
box-shadow: var(--shadow-sm);
transition: transform 0.25s var(--ease), box-shadow 0.25s var(--ease);
}
.schedule-item:hover {
transform: translateX(4px);
box-shadow: var(--shadow-md);
}
.sch-day {
font-weight: 700;
text-transform: uppercase;
font-size: 0.8rem;
letter-spacing: 0.08em;
color: var(--sage-d);
}
.sch-time {
font-family: var(--serif);
font-size: 1.3rem;
font-weight: 600;
}
.sch-class {
font-weight: 600;
}
.sch-teacher {
color: var(--muted);
font-size: 0.9rem;
}
.sch-status {
font-size: 0.76rem;
font-weight: 600;
padding: 0.3rem 0.75rem;
border-radius: var(--r-pill);
white-space: nowrap;
}
.sch-status.open {
color: var(--sage-d);
background: rgba(138, 154, 123, 0.16);
}
.sch-status.full {
color: #b1726d;
background: var(--rose-soft);
}
/* ---------- Pricing ---------- */
.price-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 1.4rem;
align-items: stretch;
}
.price-card {
position: relative;
background: var(--surface);
border: 1px solid var(--line);
border-radius: var(--r-lg);
padding: 2.2rem 1.9rem;
display: flex;
flex-direction: column;
box-shadow: var(--shadow-sm);
transition: transform 0.3s var(--ease), box-shadow 0.3s var(--ease);
}
.price-card:hover {
transform: translateY(-5px);
box-shadow: var(--shadow-md);
}
.price-card--featured {
border-color: var(--sage);
box-shadow: 0 20px 50px rgba(138, 154, 123, 0.22);
background: linear-gradient(180deg, #fff, #fbfaf6);
}
.price-card--featured:hover {
box-shadow: 0 30px 70px rgba(138, 154, 123, 0.28);
}
.price-flag {
position: absolute;
top: -13px;
left: 50%;
transform: translateX(-50%);
background: var(--sage);
color: #fff;
font-size: 0.7rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.1em;
padding: 0.35rem 0.9rem;
border-radius: var(--r-pill);
box-shadow: var(--shadow-sm);
}
.price-card h3 {
font-family: var(--serif);
font-size: 1.5rem;
font-weight: 600;
}
.price {
margin: 0.9rem 0 1.4rem;
display: flex;
align-items: baseline;
gap: 0.4rem;
}
.price .amount {
font-family: var(--serif);
font-size: 2.7rem;
font-weight: 600;
color: var(--ink);
}
.price .per {
color: var(--muted);
font-size: 0.95rem;
}
.price-feat {
list-style: none;
padding: 0;
margin: 0 0 1.8rem;
display: flex;
flex-direction: column;
gap: 0.7rem;
flex: 1;
}
.price-feat li {
position: relative;
padding-left: 1.6rem;
color: var(--ink-2);
font-size: 0.95rem;
}
.price-feat li::before {
content: "";
position: absolute;
left: 0;
top: 0.45em;
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--sage);
}
/* ---------- Testimonial ---------- */
.testimonial {
background: linear-gradient(
160deg,
rgba(138, 154, 123, 0.1),
rgba(201, 139, 134, 0.1)
);
}
.testimonial-inner {
position: relative;
text-align: center;
max-width: 760px;
}
.quote-mark {
font-family: var(--serif);
font-size: 6rem;
line-height: 0.6;
color: var(--sage);
opacity: 0.4;
display: block;
margin-bottom: 0.4rem;
}
.quote {
margin: 0;
transition: opacity 0.45s var(--ease), transform 0.45s var(--ease);
}
.quote.fading {
opacity: 0;
transform: translateY(8px);
}
.quote-text {
font-family: var(--serif);
font-size: clamp(1.4rem, 3.4vw, 2.1rem);
font-weight: 500;
font-style: italic;
line-height: 1.3;
color: var(--ink);
}
.quote-cite {
margin-top: 1.6rem;
display: flex;
flex-direction: column;
gap: 0.2rem;
}
.quote-name {
font-weight: 600;
}
.quote-role {
font-size: 0.85rem;
color: var(--muted);
}
.quote-dots {
display: flex;
gap: 0.55rem;
justify-content: center;
margin-top: 2rem;
}
.quote-dots button {
width: 9px;
height: 9px;
border-radius: 50%;
border: none;
background: var(--line-2);
transition: background 0.25s var(--ease), transform 0.25s var(--ease);
}
.quote-dots button[aria-selected="true"] {
background: var(--sage);
transform: scale(1.35);
}
/* ---------- CTA / newsletter ---------- */
.cta-inner {
text-align: center;
max-width: 620px;
margin: 0 auto;
}
.cta-title {
font-family: var(--serif);
font-weight: 500;
font-size: clamp(2rem, 5vw, 3.2rem);
line-height: 1.08;
}
.cta-sub {
margin: 1rem auto 0;
color: var(--ink-2);
max-width: 48ch;
}
.newsletter {
margin: 2rem auto 0;
display: flex;
gap: 0.6rem;
max-width: 460px;
}
.newsletter input {
flex: 1;
font: inherit;
padding: 0.85rem 1.1rem;
border: 1px solid var(--line-2);
border-radius: var(--r-pill);
background: var(--surface);
color: var(--ink);
transition: border-color 0.2s var(--ease), box-shadow 0.2s var(--ease);
}
.newsletter input::placeholder {
color: var(--muted);
}
.newsletter input:focus {
outline: none;
border-color: var(--sage);
box-shadow: 0 0 0 4px rgba(138, 154, 123, 0.18);
}
.newsletter input.invalid {
border-color: var(--rose);
box-shadow: 0 0 0 4px var(--rose-soft);
}
.newsletter-note {
margin-top: 0.9rem;
font-size: 0.8rem;
color: var(--muted);
}
/* ---------- Footer ---------- */
.site-footer {
background: var(--surface);
border-top: 1px solid var(--line);
padding: 3rem 0 2rem;
}
.footer-inner {
display: flex;
justify-content: space-between;
gap: 2rem;
flex-wrap: wrap;
align-items: flex-start;
}
.footer-brand {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.footer-brand .brand-mark {
margin-bottom: 0.2rem;
}
.footer-brand p {
color: var(--muted);
font-size: 0.88rem;
margin-top: 0.3rem;
}
.footer-nav {
display: flex;
gap: 1.6rem;
flex-wrap: wrap;
font-size: 0.95rem;
}
.footer-nav a {
color: var(--ink-2);
transition: color 0.2s var(--ease);
}
.footer-nav a:hover {
color: var(--ink);
}
.footer-bottom {
margin-top: 2.2rem;
padding-top: 1.4rem;
border-top: 1px solid var(--line);
color: var(--muted);
}
/* ---------- Toast ---------- */
.toast {
position: fixed;
left: 50%;
bottom: 28px;
transform: translate(-50%, 140%);
background: var(--ink);
color: var(--bg);
padding: 0.85rem 1.4rem;
border-radius: var(--r-pill);
font-size: 0.92rem;
font-weight: 500;
box-shadow: var(--shadow-lg);
z-index: 300;
transition: transform 0.4s var(--ease), opacity 0.4s var(--ease);
opacity: 0;
pointer-events: none;
max-width: calc(100% - 40px);
text-align: center;
}
.toast.show {
transform: translate(-50%, 0);
opacity: 1;
}
/* ---------- Reveal ---------- */
.reveal {
opacity: 0;
transform: translateY(22px);
transition: opacity 0.7s var(--ease), transform 0.7s var(--ease);
}
.reveal.in {
opacity: 1;
transform: none;
}
/* ---------- Responsive ---------- */
@media (max-width: 860px) {
.nav,
.header-actions {
display: none;
}
.nav-toggle {
display: inline-flex;
}
.nav.open {
display: flex;
flex-direction: column;
position: absolute;
top: 74px;
left: 0;
right: 0;
background: var(--surface);
border-bottom: 1px solid var(--line);
padding: 1rem 24px 1.4rem;
gap: 0.4rem;
box-shadow: var(--shadow-md);
}
.nav.open a {
padding: 0.7rem 0;
border-bottom: 1px solid var(--line);
}
}
@media (max-width: 520px) {
.wrap {
padding: 0 18px;
}
.hero-stats {
gap: 1.6rem;
}
.schedule-item {
grid-template-columns: 48px 1fr auto;
grid-template-areas:
"day class status"
"time teacher status";
row-gap: 0.3rem;
}
.sch-day {
grid-area: day;
}
.sch-time {
grid-area: time;
font-size: 1.05rem;
}
.sch-class {
grid-area: class;
}
.sch-teacher {
grid-area: teacher;
}
.sch-status {
grid-area: status;
justify-self: end;
}
.newsletter {
flex-direction: column;
}
.newsletter .btn {
width: 100%;
}
.footer-inner {
flex-direction: column;
}
}
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.001ms !important;
transition-duration: 0.001ms !important;
}
.reveal {
opacity: 1;
transform: none;
}
}(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");
var nav = document.getElementById("nav");
if (navToggle && nav) {
navToggle.addEventListener("click", function () {
var open = nav.classList.toggle("open");
navToggle.setAttribute("aria-expanded", String(open));
});
nav.addEventListener("click", function (e) {
if (e.target.tagName === "A") {
nav.classList.remove("open");
navToggle.setAttribute("aria-expanded", "false");
}
});
}
/* ---------- Scroll reveal ---------- */
var reveals = Array.prototype.slice.call(document.querySelectorAll(".reveal"));
if ("IntersectionObserver" in window) {
var io = new IntersectionObserver(
function (entries) {
entries.forEach(function (entry, i) {
if (entry.isIntersecting) {
var el = entry.target;
// gentle stagger within the same viewport batch
setTimeout(function () {
el.classList.add("in");
}, Math.min(i * 70, 280));
io.unobserve(el);
}
});
},
{ threshold: 0.12, rootMargin: "0px 0px -8% 0px" }
);
reveals.forEach(function (el) {
io.observe(el);
});
} else {
reveals.forEach(function (el) {
el.classList.add("in");
});
}
/* ---------- Quote rotator ---------- */
var quoteEl = document.getElementById("quote");
var dotsEl = document.getElementById("quoteDots");
if (quoteEl && dotsEl) {
var quotes = [];
try {
quotes = JSON.parse(quoteEl.getAttribute("data-quotes")) || [];
} catch (e) {
quotes = [];
}
if (quotes.length) {
var current = 0;
var autoTimer;
var textEl = quoteEl.querySelector(".quote-text");
var nameEl = quoteEl.querySelector(".quote-name");
var roleEl = quoteEl.querySelector(".quote-role");
// build dots
quotes.forEach(function (_, idx) {
var b = document.createElement("button");
b.type = "button";
b.setAttribute("role", "tab");
b.setAttribute("aria-label", "Testimonial " + (idx + 1));
b.addEventListener("click", function () {
show(idx);
restartAuto();
});
dotsEl.appendChild(b);
});
var dots = Array.prototype.slice.call(dotsEl.children);
function render(idx) {
var q = quotes[idx];
textEl.textContent = q.text;
nameEl.textContent = q.name;
roleEl.textContent = q.role;
dots.forEach(function (d, i) {
d.setAttribute("aria-selected", String(i === idx));
});
}
function show(idx) {
if (idx === current) return;
current = idx;
quoteEl.classList.add("fading");
setTimeout(function () {
render(current);
quoteEl.classList.remove("fading");
}, 300);
}
function next() {
show((current + 1) % quotes.length);
}
function startAuto() {
autoTimer = setInterval(next, 6000);
}
function restartAuto() {
clearInterval(autoTimer);
startAuto();
}
render(0);
startAuto();
// pause on hover for accessibility
quoteEl.addEventListener("mouseenter", function () {
clearInterval(autoTimer);
});
quoteEl.addEventListener("mouseleave", restartAuto);
}
}
/* ---------- Pricing buttons ---------- */
document.querySelectorAll("[data-plan]").forEach(function (btn) {
btn.addEventListener("click", function () {
toast("Nice choice — " + btn.getAttribute("data-plan") + " selected. First class is free!");
});
});
/* ---------- Newsletter ---------- */
var form = document.getElementById("newsletter");
if (form) {
var input = document.getElementById("email");
form.addEventListener("submit", function (e) {
e.preventDefault();
var value = (input.value || "").trim();
var valid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
if (!valid) {
input.classList.add("invalid");
input.focus();
toast("Please enter a valid email address.");
return;
}
input.classList.remove("invalid");
input.value = "";
toast("Welcome to the practice — check your inbox to confirm.");
});
input.addEventListener("input", function () {
input.classList.remove("invalid");
});
}
/* ---------- Smooth-scroll active focus for in-page links ---------- */
document.querySelectorAll('a[href^="#"]').forEach(function (a) {
a.addEventListener("click", function (e) {
var id = a.getAttribute("href");
if (id.length < 2) return;
var target = document.querySelector(id);
if (target) {
e.preventDefault();
target.scrollIntoView({ behavior: "smooth", block: "start" });
target.setAttribute("tabindex", "-1");
target.focus({ preventScroll: true });
}
});
});
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Stillpoint — Yoga & Pilates Studio</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=Cormorant+Garamond:ital,wght@0,400;0,500;0,600;1,400&family=Inter:wght@400;500;600;700&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="wrap header-inner">
<a class="brand" href="#top" aria-label="Stillpoint home">
<span class="brand-mark" aria-hidden="true"></span>
<span class="brand-name">Stillpoint</span>
</a>
<nav class="nav" id="nav" aria-label="Primary">
<a href="#classes">Classes</a>
<a href="#instructors">Teachers</a>
<a href="#schedule">Schedule</a>
<a href="#pricing">Pricing</a>
</nav>
<div class="header-actions">
<a class="btn btn-ghost" href="#schedule">Visit us</a>
<a class="btn btn-primary" href="#pricing">Book a class</a>
</div>
<button
class="nav-toggle"
id="navToggle"
aria-expanded="false"
aria-controls="nav"
aria-label="Toggle menu"
>
<span></span><span></span><span></span>
</button>
</div>
</header>
<main id="main">
<!-- Hero -->
<section class="hero">
<div class="hero-orb hero-orb--1" aria-hidden="true"></div>
<div class="hero-orb hero-orb--2" aria-hidden="true"></div>
<div class="wrap hero-inner">
<p class="eyebrow reveal">A studio for slow movement</p>
<h1 class="hero-title reveal">
Find your <em>stillpoint</em><br />in every breath.
</h1>
<p class="hero-lede reveal">
A light-filled yoga & Pilates studio in the heart of Wren Hollow.
Mindful classes for every body, led by teachers who move with you —
never ahead of you.
</p>
<div class="hero-cta reveal">
<a class="btn btn-primary btn-lg" href="#pricing">Book a class</a>
<a class="btn btn-ghost btn-lg" href="#classes">Explore styles</a>
</div>
<ul class="hero-stats reveal" aria-label="Studio highlights">
<li><strong>12+</strong><span>weekly classes</span></li>
<li><strong>6</strong><span>certified teachers</span></li>
<li><strong>4.9</strong><span>member rating</span></li>
</ul>
</div>
</section>
<!-- Classes -->
<section class="section" id="classes">
<div class="wrap">
<header class="section-head reveal">
<p class="eyebrow">Our practice</p>
<h2 class="section-title">Class styles to meet your day</h2>
<p class="section-sub">
Whether you want to sweat, stretch or simply slow down, there's a
mat waiting for you.
</p>
</header>
<div class="class-grid">
<article class="class-card reveal">
<span class="class-badge">All levels</span>
<h3>Vinyasa Flow</h3>
<p>
Breath-led movement that builds heat and fluid strength. Expect
a creative sequence and a steady, energizing rhythm.
</p>
<p class="class-meta">60 min · Heated studio</p>
</article>
<article class="class-card reveal">
<span class="class-badge">Gentle</span>
<h3>Yin & Restore</h3>
<p>
Long, supported holds that open the deep connective tissue.
Props, bolsters and plenty of permission to rest.
</p>
<p class="class-meta">75 min · Quiet room</p>
</article>
<article class="class-card reveal">
<span class="class-badge">Core focus</span>
<h3>Mat Pilates</h3>
<p>
Precise, controlled work for a strong centre. Build stability,
posture and mobility from the inside out.
</p>
<p class="class-meta">50 min · Equipment-free</p>
</article>
<article class="class-card reveal">
<span class="class-badge">Beginner</span>
<h3>Restorative</h3>
<p>
Deep nervous-system reset through stillness and breath. The
softest landing for tired bodies and busy minds.
</p>
<p class="class-meta">60 min · Candlelit</p>
</article>
</div>
</div>
</section>
<!-- Instructors -->
<section class="section section--alt" id="instructors">
<div class="wrap">
<header class="section-head reveal">
<p class="eyebrow">The people</p>
<h2 class="section-title">Teachers who hold the room</h2>
<p class="section-sub">
Warm, experienced guides who tailor every class to who walks
through the door.
</p>
</header>
<div class="teacher-row">
<figure class="teacher reveal">
<span class="teacher-avatar" data-initials="MA" aria-hidden="true">MA</span>
<figcaption>
<strong>Maya Aldridge</strong>
<span>Vinyasa · Breathwork</span>
</figcaption>
</figure>
<figure class="teacher reveal">
<span class="teacher-avatar" data-initials="TF" aria-hidden="true">TF</span>
<figcaption>
<strong>Theo Fenn</strong>
<span>Mat Pilates · Mobility</span>
</figcaption>
</figure>
<figure class="teacher reveal">
<span class="teacher-avatar" data-initials="NR" aria-hidden="true">NR</span>
<figcaption>
<strong>Nadia Reyes</strong>
<span>Yin · Restorative</span>
</figcaption>
</figure>
<figure class="teacher reveal">
<span class="teacher-avatar" data-initials="JO" aria-hidden="true">JO</span>
<figcaption>
<strong>Jonah Okafor</strong>
<span>Power Flow · Strength</span>
</figcaption>
</figure>
</div>
</div>
</section>
<!-- Schedule teaser -->
<section class="section" id="schedule">
<div class="wrap">
<header class="section-head reveal">
<p class="eyebrow">This week</p>
<h2 class="section-title">A glimpse of the schedule</h2>
<p class="section-sub">
A few of our most-loved slots. Full timetable opens when you
create a free account.
</p>
</header>
<ul class="schedule-list">
<li class="schedule-item reveal">
<span class="sch-day">Mon</span>
<span class="sch-time">07:00</span>
<span class="sch-class">Vinyasa Flow</span>
<span class="sch-teacher">Maya A.</span>
<span class="sch-status open">3 mats left</span>
</li>
<li class="schedule-item reveal">
<span class="sch-day">Tue</span>
<span class="sch-time">12:30</span>
<span class="sch-class">Mat Pilates</span>
<span class="sch-teacher">Theo F.</span>
<span class="sch-status open">Open</span>
</li>
<li class="schedule-item reveal">
<span class="sch-day">Wed</span>
<span class="sch-time">18:45</span>
<span class="sch-class">Yin & Restore</span>
<span class="sch-teacher">Nadia R.</span>
<span class="sch-status full">Waitlist</span>
</li>
<li class="schedule-item reveal">
<span class="sch-day">Fri</span>
<span class="sch-time">17:30</span>
<span class="sch-class">Power Flow</span>
<span class="sch-teacher">Jonah O.</span>
<span class="sch-status open">5 mats left</span>
</li>
<li class="schedule-item reveal">
<span class="sch-day">Sun</span>
<span class="sch-time">09:30</span>
<span class="sch-class">Restorative</span>
<span class="sch-teacher">Nadia R.</span>
<span class="sch-status open">Open</span>
</li>
</ul>
</div>
</section>
<!-- Pricing / class packs -->
<section class="section section--alt" id="pricing">
<div class="wrap">
<header class="section-head reveal">
<p class="eyebrow">Membership</p>
<h2 class="section-title">Simple ways to practice</h2>
<p class="section-sub">
No lock-in. Pause any time. Your first class is always on us.
</p>
</header>
<div class="price-grid">
<article class="price-card reveal">
<h3>Drop-in</h3>
<p class="price"><span class="amount">$24</span><span class="per">/ class</span></p>
<ul class="price-feat">
<li>Any single class</li>
<li>Mat & props included</li>
<li>No commitment</li>
</ul>
<button class="btn btn-ghost btn-block" data-plan="Drop-in">Choose drop-in</button>
</article>
<article class="price-card price-card--featured reveal">
<span class="price-flag">Most loved</span>
<h3>Class pack — 10</h3>
<p class="price"><span class="amount">$180</span><span class="per">/ 10 classes</span></p>
<ul class="price-feat">
<li>Save $60 vs drop-in</li>
<li>Valid for 4 months</li>
<li>Shareable with a friend</li>
<li>Priority waitlist</li>
</ul>
<button class="btn btn-primary btn-block" data-plan="Class pack of 10">Get the pack</button>
</article>
<article class="price-card reveal">
<h3>Unlimited</h3>
<p class="price"><span class="amount">$129</span><span class="per">/ month</span></p>
<ul class="price-feat">
<li>Every class, every day</li>
<li>2 guest passes monthly</li>
<li>Workshop discounts</li>
<li>Pause any time</li>
</ul>
<button class="btn btn-ghost btn-block" data-plan="Unlimited monthly">Go unlimited</button>
</article>
</div>
</div>
</section>
<!-- Testimonial / quote rotator -->
<section class="section testimonial">
<div class="wrap testimonial-inner">
<span class="quote-mark" aria-hidden="true">“</span>
<blockquote
class="quote reveal"
id="quote"
aria-live="polite"
data-quotes='[
{"text":"I came for the stretching and stayed for the calm. Stillpoint changed how I move through my whole week.","name":"Priya N.","role":"Member, 2 years"},
{"text":"The teachers actually see you. I have never felt rushed or judged — just gently encouraged.","name":"Daniel O.","role":"Member, 8 months"},
{"text":"Pilates here rebuilt my back after an injury. The precision and care are unmatched.","name":"Esme L.","role":"Member, 1 year"}
]'
>
<p class="quote-text">
I came for the stretching and stayed for the calm. Stillpoint
changed how I move through my whole week.
</p>
<footer class="quote-cite">
<strong class="quote-name">Priya N.</strong>
<span class="quote-role">Member, 2 years</span>
</footer>
</blockquote>
<div class="quote-dots" id="quoteDots" role="tablist" aria-label="Testimonials"></div>
</div>
</section>
<!-- Newsletter / CTA -->
<section class="section cta">
<div class="wrap cta-inner reveal">
<h2 class="cta-title">Begin where you are.</h2>
<p class="cta-sub">
Join our gentle weekly note — class openings, breath tips and a
little stillness in your inbox.
</p>
<form class="newsletter" id="newsletter" novalidate>
<label class="sr-only" for="email">Email address</label>
<input
type="email"
id="email"
name="email"
placeholder="[email protected]"
autocomplete="email"
required
/>
<button class="btn btn-primary" type="submit">Join the list</button>
</form>
<p class="newsletter-note">No spam. Unsubscribe with one tap.</p>
</div>
</section>
</main>
<!-- Footer -->
<footer class="site-footer">
<div class="wrap footer-inner">
<div class="footer-brand">
<span class="brand-mark" aria-hidden="true"></span>
<span class="brand-name">Stillpoint</span>
<p>14 Wren Hollow Lane · Open 7 days · 06:00–21:00</p>
</div>
<nav class="footer-nav" aria-label="Footer">
<a href="#classes">Classes</a>
<a href="#instructors">Teachers</a>
<a href="#schedule">Schedule</a>
<a href="#pricing">Pricing</a>
</nav>
</div>
<div class="wrap footer-bottom">
<small>© 2026 Stillpoint Studio. A fictional studio.</small>
</div>
</footer>
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>Yoga / Pilates Studio Landing
A calm, airy marketing page for Stillpoint, a fictional yoga and Pilates studio. The design leans into a serene wellness aesthetic — a bone-coloured background, dusty sage and rose accents, soft layered shadows and organic rounded shapes — with headings set in Cormorant Garamond against Inter body text. The hero pairs a layered radial gradient with two slowly drifting blurred orbs, a clear “Book a class” call to action and a row of studio stats.
Below the fold the page moves through elegant class-style cards (Vinyasa, Yin & Restore, Mat Pilates, Restorative) with badges and a gradient top-border on hover, an instructors row of gradient avatar monograms, a five-row weekly schedule teaser with open / waitlist status pills, and three membership cards where the class pack is highlighted as “Most loved”. A testimonial section rotates through member quotes, and a newsletter footer rounds it out.
Interactions are all vanilla JavaScript: a sticky header that gains a border on scroll, a mobile nav
toggle, IntersectionObserver-driven scroll reveals with a gentle stagger, an auto-advancing quote
rotator with clickable dots that pauses on hover, validated newsletter sign-up, smooth in-page
anchor scrolling with focus management, and a small toast() helper for feedback. The layout is
responsive down to ~360px and respects prefers-reduced-motion.