Coach — Programs List
Energetic dark programs catalog for a fitness coach — training program cards showing name, week count, difficulty level, focus area and price, with live filtering by goal and level, a glowing electric-lime enroll action, and a standout popular badge. Built with semantic HTML, a bold uppercase display system and vanilla JS that filters instantly, keeps counts in sync, animates enroll feedback and stays keyboard-accessible down to small screens.
MCP
Code
:root {
--bg: #0f1115;
--surface: #171a21;
--surface-2: #1f232c;
--lime: #c2f542;
--lime-d: #a3d62f;
--orange: #ff6b35;
--ink: #f2f4f0;
--muted: #9aa0a6;
--line: rgba(255, 255, 255, 0.10);
--line-2: rgba(255, 255, 255, 0.18);
--white: #fff;
--r-sm: 8px;
--r-md: 14px;
--r-lg: 20px;
}
* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
margin: 0;
background:
radial-gradient(1100px 520px at 82% -8%, rgba(194, 245, 66, 0.08), transparent 60%),
radial-gradient(900px 480px at 6% 4%, rgba(255, 107, 53, 0.07), transparent 55%),
var(--bg);
color: var(--ink);
font-family: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;
line-height: 1.5;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
min-height: 100vh;
}
.page {
max-width: 1080px;
margin: 0 auto;
padding: 32px 22px 72px;
}
/* ---------- Hero ---------- */
.hero { margin-bottom: 26px; }
.hero__brand {
display: inline-flex;
align-items: center;
gap: 8px;
margin-bottom: 18px;
}
.hero__mark {
color: var(--lime);
font-size: 20px;
line-height: 1;
text-shadow: 0 0 16px rgba(194, 245, 66, 0.6);
}
.hero__name {
font-weight: 800;
font-size: 15px;
letter-spacing: 0.22em;
text-transform: uppercase;
}
.hero__kicker {
margin: 0 0 10px;
color: var(--lime-d);
font-weight: 600;
font-size: 13px;
letter-spacing: 0.14em;
text-transform: uppercase;
}
.hero__title {
margin: 0 0 12px;
font-weight: 800;
font-size: clamp(38px, 8vw, 66px);
line-height: 0.95;
letter-spacing: -0.02em;
text-transform: uppercase;
}
.hero__title em {
font-style: normal;
color: var(--lime);
text-shadow: 0 0 30px rgba(194, 245, 66, 0.35);
}
.hero__sub {
margin: 0;
max-width: 52ch;
color: var(--muted);
font-size: 15px;
}
/* ---------- Filters ---------- */
.filters {
position: sticky;
top: 12px;
z-index: 20;
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 18px 26px;
padding: 16px 18px;
margin-bottom: 26px;
background: rgba(23, 26, 33, 0.86);
backdrop-filter: blur(12px);
border: 1px solid var(--line);
border-radius: var(--r-lg);
box-shadow: 0 18px 40px rgba(0, 0, 0, 0.35);
}
.filters__group {
display: flex;
align-items: center;
gap: 12px;
flex-wrap: wrap;
}
.filters__label {
font-size: 11px;
font-weight: 700;
letter-spacing: 0.16em;
text-transform: uppercase;
color: var(--muted);
}
.chips { display: flex; flex-wrap: wrap; gap: 8px; }
.chip {
appearance: none;
cursor: pointer;
font: inherit;
font-weight: 600;
font-size: 13px;
color: var(--ink);
padding: 7px 14px;
border: 1px solid var(--line-2);
border-radius: 999px;
background: var(--surface-2);
transition: transform 0.12s ease, background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}
.chip:hover { transform: translateY(-1px); border-color: var(--lime-d); }
.chip.is-active {
background: var(--lime);
border-color: var(--lime);
color: #10130a;
box-shadow: 0 0 18px rgba(194, 245, 66, 0.4);
}
.chip:focus-visible,
.btn:focus-visible,
.filters__reset:focus-visible {
outline: 2px solid var(--lime);
outline-offset: 2px;
}
.filters__meta {
display: flex;
align-items: center;
gap: 14px;
margin-left: auto;
}
.filters__count {
font-weight: 700;
font-size: 13px;
color: var(--ink);
white-space: nowrap;
}
.filters__reset {
appearance: none;
cursor: pointer;
font: inherit;
font-weight: 600;
font-size: 13px;
color: var(--orange);
background: none;
border: 0;
padding: 4px 2px;
text-transform: uppercase;
letter-spacing: 0.06em;
}
.filters__reset:hover { text-decoration: underline; }
/* ---------- Grid ---------- */
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 20px;
}
.card {
position: relative;
display: flex;
flex-direction: column;
background: linear-gradient(180deg, var(--surface) 0%, #14171d 100%);
border: 1px solid var(--line);
border-radius: var(--r-lg);
padding: 22px;
overflow: hidden;
transition: transform 0.16s ease, border-color 0.16s ease, box-shadow 0.16s ease;
animation: rise 0.4s ease both;
}
@keyframes rise {
from { opacity: 0; transform: translateY(14px); }
to { opacity: 1; transform: translateY(0); }
}
.card:hover {
transform: translateY(-4px);
border-color: var(--line-2);
box-shadow: 0 22px 46px rgba(0, 0, 0, 0.45);
}
.card::before {
content: "";
position: absolute;
inset: 0 0 auto 0;
height: 4px;
background: linear-gradient(90deg, var(--lime), var(--orange));
opacity: 0.85;
}
.card--popular {
border-color: rgba(194, 245, 66, 0.5);
box-shadow: 0 0 0 1px rgba(194, 245, 66, 0.2), 0 22px 46px rgba(0, 0, 0, 0.4);
}
.badge {
position: absolute;
top: 16px;
right: 16px;
display: inline-flex;
align-items: center;
gap: 5px;
font-size: 10px;
font-weight: 800;
letter-spacing: 0.12em;
text-transform: uppercase;
color: #10130a;
background: var(--lime);
padding: 5px 10px;
border-radius: 999px;
box-shadow: 0 0 16px rgba(194, 245, 66, 0.5);
}
.card__level {
display: inline-block;
font-size: 11px;
font-weight: 700;
letter-spacing: 0.1em;
text-transform: uppercase;
padding: 4px 10px;
border-radius: var(--r-sm);
margin-bottom: 14px;
}
.card__level[data-level="beginner"] { color: var(--lime); background: rgba(194, 245, 66, 0.12); }
.card__level[data-level="intermediate"] { color: #ffd166; background: rgba(255, 209, 102, 0.12); }
.card__level[data-level="advanced"] { color: var(--orange); background: rgba(255, 107, 53, 0.14); }
.card__name {
margin: 0 0 6px;
font-weight: 800;
font-size: 22px;
line-height: 1.05;
letter-spacing: -0.01em;
text-transform: uppercase;
padding-right: 70px;
}
.card__blurb {
margin: 0 0 16px;
color: var(--muted);
font-size: 13.5px;
}
.card__stats {
display: flex;
gap: 10px;
margin-bottom: 16px;
}
.stat {
flex: 1;
background: var(--surface-2);
border: 1px solid var(--line);
border-radius: var(--r-md);
padding: 10px 12px;
}
.stat__val {
display: block;
font-weight: 800;
font-size: 18px;
line-height: 1;
}
.stat__key {
display: block;
margin-top: 5px;
font-size: 10px;
font-weight: 600;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--muted);
}
.tags {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-bottom: 18px;
}
.tag {
font-size: 11px;
font-weight: 600;
color: var(--muted);
padding: 4px 9px;
border: 1px solid var(--line-2);
border-radius: 999px;
}
.card__foot {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
margin-top: auto;
}
.price { display: flex; align-items: baseline; gap: 4px; }
.price__amt {
font-weight: 800;
font-size: 26px;
letter-spacing: -0.01em;
}
.price__per {
font-size: 12px;
color: var(--muted);
font-weight: 500;
}
.btn {
appearance: none;
cursor: pointer;
font: inherit;
font-weight: 800;
font-size: 13px;
letter-spacing: 0.06em;
text-transform: uppercase;
color: #10130a;
background: var(--lime);
border: 0;
border-radius: var(--r-md);
padding: 12px 20px;
transition: transform 0.12s ease, box-shadow 0.16s ease, background 0.16s ease;
box-shadow: 0 6px 18px rgba(194, 245, 66, 0.28);
}
.btn:hover { transform: translateY(-2px); box-shadow: 0 10px 26px rgba(194, 245, 66, 0.5); }
.btn:active { transform: translateY(0); }
.btn.is-enrolled {
background: var(--surface-2);
color: var(--lime);
box-shadow: none;
border: 1px solid var(--lime-d);
cursor: default;
}
.btn--ghost {
background: transparent;
color: var(--ink);
border: 1px solid var(--line-2);
box-shadow: none;
}
.btn--ghost:hover { border-color: var(--lime); box-shadow: none; }
/* ---------- Empty state ---------- */
.empty {
text-align: center;
padding: 60px 20px;
border: 1px dashed var(--line-2);
border-radius: var(--r-lg);
background: var(--surface);
}
.empty__title {
margin: 0 0 6px;
font-weight: 800;
font-size: 20px;
text-transform: uppercase;
}
.empty__sub { margin: 0 0 20px; color: var(--muted); font-size: 14px; }
/* ---------- Toast ---------- */
.toast {
position: fixed;
left: 50%;
bottom: 26px;
transform: translateX(-50%) translateY(24px);
background: var(--lime);
color: #10130a;
font-weight: 700;
font-size: 14px;
padding: 12px 20px;
border-radius: var(--r-md);
box-shadow: 0 12px 34px rgba(0, 0, 0, 0.5), 0 0 20px rgba(194, 245, 66, 0.4);
opacity: 0;
pointer-events: none;
transition: opacity 0.25s ease, transform 0.25s ease;
z-index: 60;
}
.toast.is-show { opacity: 1; transform: translateX(-50%) translateY(0); }
/* ---------- Responsive ---------- */
@media (max-width: 520px) {
.page { padding: 24px 16px 56px; }
.grid { grid-template-columns: 1fr; }
.filters {
top: 8px;
gap: 14px;
padding: 14px;
}
.filters__meta { margin-left: 0; width: 100%; justify-content: space-between; }
.hero__title { font-size: clamp(34px, 12vw, 46px); }
.card__name { padding-right: 60px; }
.card__foot { flex-direction: column; align-items: stretch; }
.btn { width: 100%; text-align: center; }
}(function () {
"use strict";
var PROGRAMS = [
{
id: "titan-strength",
name: "Titan Strength",
blurb: "Build a raw, brutal base of strength on the big three lifts.",
goal: "strength",
level: "advanced",
weeks: 12,
days: 4,
price: 149,
focus: ["Squat", "Bench", "Deadlift", "Powerbuilding"],
popular: true
},
{
id: "lean-machine",
name: "Lean Machine",
blurb: "High-intensity conditioning to strip fat while keeping muscle.",
goal: "fat-loss",
level: "intermediate",
weeks: 8,
days: 5,
price: 99,
focus: ["HIIT", "Metcon", "Nutrition"]
},
{
id: "hyper-mass",
name: "Hyper Mass",
blurb: "Science-based hypertrophy blocks to pack on serious size.",
goal: "hypertrophy",
level: "intermediate",
weeks: 10,
days: 5,
price: 119,
focus: ["Volume", "Progressive Overload", "Split"]
},
{
id: "first-rep",
name: "First Rep",
blurb: "Your zero-to-hero onboarding into the world of lifting.",
goal: "strength",
level: "beginner",
weeks: 6,
days: 3,
price: 59,
focus: ["Form", "Fundamentals", "Full Body"]
},
{
id: "engine-builder",
name: "Engine Builder",
blurb: "Grow your aerobic engine for runs, rides and long efforts.",
goal: "endurance",
level: "intermediate",
weeks: 10,
days: 4,
price: 89,
focus: ["Zone 2", "Intervals", "Tempo"]
},
{
id: "flow-restore",
name: "Flow & Restore",
blurb: "Daily mobility flows to unlock hips, shoulders and spine.",
goal: "mobility",
level: "beginner",
weeks: 4,
days: 6,
price: 45,
focus: ["Mobility", "Recovery", "Stability"]
}
];
var state = { goal: "all", level: "all" };
var grid = document.getElementById("programGrid");
var emptyState = document.getElementById("emptyState");
var resultCount = document.getElementById("resultCount");
var resetBtn = document.getElementById("resetBtn");
var toastEl = document.getElementById("toast");
var enrolled = {};
var toastTimer;
function toast(msg) {
toastEl.textContent = msg;
toastEl.classList.add("is-show");
clearTimeout(toastTimer);
toastTimer = setTimeout(function () {
toastEl.classList.remove("is-show");
}, 2400);
}
function esc(s) {
return String(s).replace(/[&<>"']/g, function (c) {
return { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[c];
});
}
function cardMarkup(p) {
var tags = p.focus
.map(function (f) { return '<span class="tag">' + esc(f) + "</span>"; })
.join("");
var badge = p.popular
? '<span class="badge">★ Most popular</span>'
: "";
var levelLabel = p.level.charAt(0).toUpperCase() + p.level.slice(1);
var isEnrolled = !!enrolled[p.id];
var btnClass = isEnrolled ? "btn is-enrolled" : "btn";
var btnLabel = isEnrolled ? "✓ Enrolled" : "Enroll now";
return (
'<article class="card' + (p.popular ? " card--popular" : "") +
'" data-goal="' + p.goal + '" data-level="' + p.level + '">' +
badge +
'<span class="card__level" data-level="' + p.level + '">' + levelLabel + "</span>" +
'<h2 class="card__name">' + esc(p.name) + "</h2>" +
'<p class="card__blurb">' + esc(p.blurb) + "</p>" +
'<div class="card__stats">' +
'<div class="stat"><span class="stat__val">' + p.weeks + '</span><span class="stat__key">Weeks</span></div>' +
'<div class="stat"><span class="stat__val">' + p.days + '</span><span class="stat__key">Days / wk</span></div>' +
'<div class="stat"><span class="stat__val">' + esc(capGoal(p.goal)) + '</span><span class="stat__key">Focus</span></div>' +
"</div>" +
'<div class="tags">' + tags + "</div>" +
'<div class="card__foot">' +
'<div class="price"><span class="price__amt">$' + p.price + '</span><span class="price__per">/ full block</span></div>' +
'<button class="' + btnClass + '" data-enroll="' + p.id + '"' +
(isEnrolled ? " disabled" : "") + '>' + btnLabel + "</button>" +
"</div>" +
"</article>"
);
}
function capGoal(g) {
var map = {
"strength": "Strength",
"hypertrophy": "Size",
"fat-loss": "Fat Loss",
"endurance": "Cardio",
"mobility": "Mobility"
};
return map[g] || g;
}
function matches(p) {
return (
(state.goal === "all" || p.goal === state.goal) &&
(state.level === "all" || p.level === state.level)
);
}
function render() {
var visible = PROGRAMS.filter(matches);
grid.innerHTML = visible.map(cardMarkup).join("");
var isEmpty = visible.length === 0;
emptyState.hidden = !isEmpty;
grid.hidden = isEmpty;
resultCount.textContent =
visible.length + (visible.length === 1 ? " program" : " programs");
var filtered = state.goal !== "all" || state.level !== "all";
resetBtn.hidden = !filtered;
}
function setFilter(type, value) {
state[type] = value;
var container = type === "goal" ? "goalChips" : "levelChips";
var chips = document.getElementById(container).querySelectorAll(".chip");
chips.forEach(function (chip) {
var on = chip.dataset.value === value;
chip.classList.toggle("is-active", on);
chip.setAttribute("aria-pressed", on ? "true" : "false");
});
render();
}
function reset() {
setFilter("goal", "all");
setFilter("level", "all");
toast("Filters cleared");
}
// ---- Event delegation ----
document.addEventListener("click", function (e) {
var chip = e.target.closest(".chip");
if (chip) {
setFilter(chip.dataset.filter, chip.dataset.value);
return;
}
var enrollBtn = e.target.closest("[data-enroll]");
if (enrollBtn && !enrollBtn.disabled) {
var id = enrollBtn.dataset.enroll;
var prog = PROGRAMS.find(function (p) { return p.id === id; });
enrolled[id] = true;
enrollBtn.classList.add("is-enrolled");
enrollBtn.disabled = true;
enrollBtn.textContent = "✓ Enrolled";
toast("You're in — " + (prog ? prog.name : "program") + " unlocked!");
return;
}
if (e.target.closest("[data-reset]") || e.target === resetBtn) {
reset();
}
});
render();
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Ironpath Coaching — Training Programs</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&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="page">
<header class="hero">
<div class="hero__brand">
<span class="hero__mark" aria-hidden="true">◤</span>
<span class="hero__name">Ironpath</span>
</div>
<p class="hero__kicker">Coach Mara Vance · Certified S&C</p>
<h1 class="hero__title">Pick Your <em>Program</em></h1>
<p class="hero__sub">Structured, progressive training blocks built to get you moving, lifting, and feeling unstoppable. No fluff — just results.</p>
</header>
<section class="filters" aria-label="Filter programs">
<div class="filters__group" role="group" aria-label="Filter by goal">
<span class="filters__label">Goal</span>
<div class="chips" id="goalChips">
<button class="chip is-active" data-filter="goal" data-value="all" aria-pressed="true">All</button>
<button class="chip" data-filter="goal" data-value="strength" aria-pressed="false">Strength</button>
<button class="chip" data-filter="goal" data-value="hypertrophy" aria-pressed="false">Hypertrophy</button>
<button class="chip" data-filter="goal" data-value="fat-loss" aria-pressed="false">Fat Loss</button>
<button class="chip" data-filter="goal" data-value="endurance" aria-pressed="false">Endurance</button>
<button class="chip" data-filter="goal" data-value="mobility" aria-pressed="false">Mobility</button>
</div>
</div>
<div class="filters__group" role="group" aria-label="Filter by level">
<span class="filters__label">Level</span>
<div class="chips" id="levelChips">
<button class="chip is-active" data-filter="level" data-value="all" aria-pressed="true">All</button>
<button class="chip" data-filter="level" data-value="beginner" aria-pressed="false">Beginner</button>
<button class="chip" data-filter="level" data-value="intermediate" aria-pressed="false">Intermediate</button>
<button class="chip" data-filter="level" data-value="advanced" aria-pressed="false">Advanced</button>
</div>
</div>
<div class="filters__meta">
<span class="filters__count" id="resultCount" aria-live="polite">6 programs</span>
<button class="filters__reset" id="resetBtn" type="button" hidden>Reset</button>
</div>
</section>
<main class="grid" id="programGrid" aria-label="Available programs">
<!-- Cards injected by script.js -->
</main>
<div class="empty" id="emptyState" hidden>
<p class="empty__title">No programs match that combo</p>
<p class="empty__sub">Try widening your goal or level filters.</p>
<button class="btn btn--ghost" type="button" data-reset>Clear filters</button>
</div>
</div>
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>A high-energy programs catalog for an online fitness coach. The page opens with a bold uppercase hero and a sticky filter bar that lets athletes narrow the grid by training goal (strength, hypertrophy, fat loss, endurance, mobility) and by experience level. Each program is a self-contained card with its name, week count, level chip, focus tags and price, plus a glowing electric-lime enroll button. A “Most popular” ribbon highlights the flagship program so it never gets lost in the grid.
The filter bar is fully interactive: selecting chips filters the cards instantly, updates a live result counter, and shows a friendly empty state when no program matches. Enrolling animates the button into a confirmed state and fires a toast, giving immediate motivational feedback without a page reload.
Everything is vanilla — no frameworks. The layout uses CSS grid that collapses to a single column under 520px, all controls are keyboard-operable with visible focus rings, and the color system leans on the dark lime-and-orange coach palette for that gym-floor energy.