Gym — Member Dashboard
A high-energy member home for a performance gym on a dark neon-lime theme. A greeting header with avatar and tier sits above a quick-action bar, then a live next-class countdown with an animating check-in, a fiery streak widget with a seven-day dots row, weekly activity stat cards, an interactive goals ring that fills as you toggle targets, an achievements badge row, and a cancellable upcoming-bookings list. Vanilla JS, responsive to 360px.
MCP
Kod
:root {
--bg: #0d0f12;
--surface: #15181d;
--surface-2: #1d2127;
--elevated: #23282f;
--ink: #f4f6f8;
--ink-2: #c2c8d0;
--muted: #8b929c;
--neon: #c6ff3a;
--neon-d: #a6e016;
--neon-50: rgba(198, 255, 58, 0.12);
--orange: #ff6a2b;
--orange-soft: rgba(255, 106, 43, 0.14);
--line: rgba(255, 255, 255, 0.08);
--line-2: rgba(255, 255, 255, 0.16);
--ok: #34d399;
--warn: #fbbf24;
--danger: #f87171;
--r-sm: 8px;
--r-md: 14px;
--r-lg: 20px;
--sh-1: 0 1px 2px rgba(0, 0, 0, 0.4);
--sh-2: 0 8px 24px rgba(0, 0, 0, 0.45);
--sh-3: 0 18px 48px rgba(0, 0, 0, 0.55);
}
*,
*::before,
*::after {
box-sizing: border-box;
}
html {
-webkit-text-size-adjust: 100%;
}
body {
margin: 0;
min-height: 100vh;
background:
radial-gradient(1100px 600px at 88% -8%, rgba(198, 255, 58, 0.07), transparent 60%),
radial-gradient(900px 500px at -5% 6%, rgba(255, 106, 43, 0.07), transparent 55%),
var(--bg);
color: var(--ink);
font-family: "Inter", system-ui, -apple-system, sans-serif;
line-height: 1.5;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
button {
font-family: inherit;
cursor: pointer;
}
:focus-visible {
outline: 3px solid var(--neon);
outline-offset: 2px;
border-radius: var(--r-sm);
}
.app {
width: min(1080px, 100%);
margin: 0 auto;
padding: 28px 22px 56px;
}
/* ---------- Topbar ---------- */
.topbar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
margin-bottom: 22px;
}
.greet {
display: flex;
align-items: center;
gap: 16px;
min-width: 0;
}
.avatar {
flex: none;
width: 58px;
height: 58px;
border-radius: 50%;
display: grid;
place-items: center;
font-weight: 800;
font-size: 20px;
letter-spacing: 0.5px;
color: #0d0f12;
background: linear-gradient(140deg, var(--neon), var(--neon-d));
box-shadow: 0 0 0 3px var(--bg), 0 0 0 5px var(--neon-50);
}
.eyebrow {
margin: 0;
font-size: 11px;
font-weight: 700;
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--muted);
}
.greet-text h1 {
margin: 2px 0 1px;
font-size: clamp(22px, 5vw, 30px);
font-weight: 900;
letter-spacing: -0.02em;
}
.greet-text .sub {
margin: 0;
font-size: 13px;
color: var(--ink-2);
}
.topbar-actions {
display: flex;
align-items: center;
gap: 12px;
flex: none;
}
.icon-btn {
position: relative;
width: 44px;
height: 44px;
display: grid;
place-items: center;
border-radius: var(--r-md);
border: 1px solid var(--line);
background: var(--surface);
color: var(--ink-2);
transition: background 0.18s, color 0.18s, transform 0.1s;
}
.icon-btn:hover {
background: var(--surface-2);
color: var(--ink);
}
.icon-btn:active {
transform: scale(0.94);
}
.icon-btn .dot {
position: absolute;
top: 9px;
right: 10px;
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--orange);
box-shadow: 0 0 0 2px var(--surface);
}
.rank {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 9px 14px;
border-radius: 999px;
font-weight: 800;
font-size: 13px;
letter-spacing: 0.02em;
color: var(--warn);
background: rgba(251, 191, 36, 0.12);
border: 1px solid rgba(251, 191, 36, 0.28);
}
.rank-icon {
font-size: 14px;
}
/* ---------- Quick action bar ---------- */
.quickbar {
display: flex;
gap: 12px;
margin-bottom: 22px;
flex-wrap: wrap;
}
.qa {
flex: 1 1 0;
min-width: 140px;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 10px;
padding: 15px 18px;
border-radius: var(--r-md);
border: 1px solid var(--line);
background: var(--surface);
color: var(--ink);
font-weight: 700;
font-size: 15px;
letter-spacing: 0.01em;
transition: transform 0.12s, background 0.18s, border-color 0.18s, box-shadow 0.18s;
}
.qa:hover {
background: var(--surface-2);
border-color: var(--line-2);
}
.qa:active {
transform: translateY(1px) scale(0.99);
}
.qa-ic {
font-size: 17px;
line-height: 1;
}
.qa-primary {
background: linear-gradient(140deg, var(--neon), var(--neon-d));
color: #0d0f12;
border-color: transparent;
box-shadow: 0 8px 22px rgba(198, 255, 58, 0.22);
}
.qa-primary:hover {
background: linear-gradient(140deg, #d2ff5e, var(--neon));
}
.qa-orange {
background: var(--orange-soft);
border-color: rgba(255, 106, 43, 0.35);
color: #ffcaaf;
}
.qa-orange:hover {
background: rgba(255, 106, 43, 0.22);
}
/* ---------- Grid + cards ---------- */
.grid {
display: grid;
grid-template-columns: repeat(6, 1fr);
gap: 16px;
}
.card {
background: linear-gradient(180deg, var(--surface), var(--surface-2));
border: 1px solid var(--line);
border-radius: var(--r-lg);
padding: 20px;
box-shadow: var(--sh-2);
}
.card-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
margin-bottom: 14px;
}
.badge {
display: inline-flex;
align-items: center;
gap: 5px;
padding: 4px 10px;
border-radius: 999px;
font-size: 11px;
font-weight: 700;
letter-spacing: 0.05em;
text-transform: uppercase;
color: var(--ink-2);
background: var(--elevated);
border: 1px solid var(--line);
}
.badge-live {
color: var(--neon);
background: var(--neon-50);
border-color: rgba(198, 255, 58, 0.3);
}
.badge-fire {
color: #ffcaaf;
background: var(--orange-soft);
border-color: rgba(255, 106, 43, 0.32);
}
.link-btn {
border: 0;
background: none;
color: var(--neon);
font-weight: 700;
font-size: 13px;
padding: 2px 2px;
}
.link-btn:hover {
color: var(--neon-d);
text-decoration: underline;
}
/* spans */
.next-class { grid-column: span 3; }
.streak { grid-column: span 3; }
.stats { grid-column: span 3; }
.goals { grid-column: span 3; }
.achievements { grid-column: span 3; }
.bookings { grid-column: span 3; }
/* ---------- Next class ---------- */
.next-class {
position: relative;
overflow: hidden;
}
.next-class::after {
content: "";
position: absolute;
inset: 0;
background: radial-gradient(420px 200px at 110% -10%, rgba(198, 255, 58, 0.1), transparent 70%);
pointer-events: none;
}
.next-class h2 {
margin: 0 0 4px;
font-size: 26px;
font-weight: 900;
letter-spacing: -0.02em;
}
.nc-meta {
margin: 0 0 16px;
color: var(--ink-2);
font-size: 14px;
}
.nc-meta strong {
color: var(--ink);
}
.countdown {
display: flex;
align-items: flex-end;
gap: 10px;
margin-bottom: 18px;
}
.cd-unit {
display: flex;
flex-direction: column;
align-items: center;
background: var(--bg);
border: 1px solid var(--line);
border-radius: var(--r-md);
padding: 10px 14px;
min-width: 64px;
}
.cd-num {
font-size: 30px;
font-weight: 900;
line-height: 1;
font-variant-numeric: tabular-nums;
color: var(--neon);
}
.cd-lbl {
margin-top: 5px;
font-size: 10px;
font-weight: 700;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--muted);
}
.cd-sep {
font-size: 26px;
font-weight: 900;
color: var(--muted);
padding-bottom: 18px;
}
.checkin-btn {
position: relative;
width: 100%;
padding: 15px;
border: 0;
border-radius: var(--r-md);
background: linear-gradient(140deg, var(--neon), var(--neon-d));
color: #0d0f12;
font-weight: 800;
font-size: 16px;
letter-spacing: 0.02em;
overflow: hidden;
transition: transform 0.12s, box-shadow 0.2s;
box-shadow: 0 10px 26px rgba(198, 255, 58, 0.24);
}
.checkin-btn:hover {
box-shadow: 0 12px 32px rgba(198, 255, 58, 0.34);
}
.checkin-btn:active {
transform: translateY(1px) scale(0.995);
}
.checkin-btn.checked {
background: rgba(52, 211, 153, 0.16);
color: var(--ok);
box-shadow: none;
cursor: default;
}
.checkin-btn.pulse::before {
content: "";
position: absolute;
inset: 0;
border-radius: inherit;
background: rgba(255, 255, 255, 0.55);
animation: pulse 0.55s ease-out;
}
@keyframes pulse {
from { transform: scale(0.2); opacity: 0.8; }
to { transform: scale(1.8); opacity: 0; }
}
/* ---------- Streak ---------- */
.streak-num {
display: flex;
align-items: baseline;
gap: 10px;
margin-bottom: 16px;
}
.streak-num .big {
font-size: 52px;
font-weight: 900;
line-height: 1;
letter-spacing: -0.03em;
background: linear-gradient(140deg, var(--orange), #ffb088);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
.streak-num .unit {
font-size: 14px;
font-weight: 700;
color: var(--ink-2);
}
.dots {
display: flex;
justify-content: space-between;
gap: 6px;
list-style: none;
margin: 0 0 14px;
padding: 0;
}
.day {
display: flex;
flex-direction: column-reverse;
align-items: center;
gap: 7px;
flex: 1;
font-size: 11px;
font-weight: 700;
color: var(--muted);
text-transform: uppercase;
}
.dot-pill {
width: 100%;
max-width: 30px;
height: 30px;
border-radius: 9px;
background: var(--elevated);
border: 1px solid var(--line);
}
.day.done .dot-pill {
background: linear-gradient(140deg, var(--neon), var(--neon-d));
border-color: transparent;
box-shadow: 0 0 14px rgba(198, 255, 58, 0.3);
}
.day.done {
color: var(--ink);
}
.day.today .dot-pill {
background: transparent;
border: 2px dashed var(--orange);
}
.day.today {
color: var(--orange);
}
.day.rest .dot-pill {
background: var(--surface-2);
}
.streak-hint {
margin: 0;
font-size: 13px;
color: var(--muted);
}
.streak-hint strong {
color: var(--ink-2);
}
/* ---------- Stats ---------- */
.stat-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
list-style: none;
margin: 0;
padding: 0;
}
.stat {
display: flex;
flex-direction: column;
gap: 2px;
padding: 14px;
border-radius: var(--r-md);
background: var(--bg);
border: 1px solid var(--line);
transition: border-color 0.18s, transform 0.12s;
}
.stat:hover {
border-color: var(--line-2);
transform: translateY(-2px);
}
.stat-ic {
font-size: 18px;
margin-bottom: 4px;
}
.stat-val {
font-size: 26px;
font-weight: 900;
letter-spacing: -0.02em;
font-variant-numeric: tabular-nums;
}
.stat-lbl {
font-size: 11px;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--muted);
}
/* ---------- Goals / ring ---------- */
.goals-body {
display: flex;
align-items: center;
gap: 20px;
}
.ring-wrap {
position: relative;
flex: none;
width: 120px;
height: 120px;
}
.ring {
transform: rotate(-90deg);
}
.ring-track {
fill: none;
stroke: var(--elevated);
stroke-width: 12;
}
.ring-fill {
fill: none;
stroke: var(--neon);
stroke-width: 12;
stroke-linecap: round;
stroke-dasharray: 326.7;
stroke-dashoffset: 326.7;
transition: stroke-dashoffset 0.6s cubic-bezier(0.22, 1, 0.36, 1), stroke 0.4s;
filter: drop-shadow(0 0 6px rgba(198, 255, 58, 0.4));
}
.ring-fill.complete {
stroke: var(--ok);
filter: drop-shadow(0 0 8px rgba(52, 211, 153, 0.45));
}
.ring-center {
position: absolute;
inset: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
pointer-events: none;
}
.ring-num {
font-size: 34px;
font-weight: 900;
line-height: 1;
letter-spacing: -0.03em;
}
.ring-sub {
font-size: 12px;
font-weight: 600;
color: var(--muted);
}
.goal-list {
flex: 1;
list-style: none;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
gap: 8px;
}
.goal-toggle {
display: flex;
align-items: center;
gap: 11px;
padding: 9px 11px;
border-radius: var(--r-sm);
background: var(--bg);
border: 1px solid var(--line);
font-size: 14px;
font-weight: 600;
color: var(--ink-2);
transition: border-color 0.16s, background 0.16s;
}
.goal-toggle:hover {
border-color: var(--line-2);
}
.goal-toggle input {
position: absolute;
opacity: 0;
width: 0;
height: 0;
}
.check {
flex: none;
width: 22px;
height: 22px;
border-radius: 7px;
border: 2px solid var(--line-2);
display: grid;
place-items: center;
transition: background 0.16s, border-color 0.16s;
}
.check::after {
content: "";
width: 6px;
height: 11px;
border: solid #0d0f12;
border-width: 0 3px 3px 0;
transform: rotate(45deg) scale(0);
margin-top: -2px;
transition: transform 0.16s;
}
.goal-toggle input:checked ~ .check {
background: var(--neon);
border-color: var(--neon);
}
.goal-toggle input:checked ~ .check::after {
transform: rotate(45deg) scale(1);
}
.goal-toggle input:focus-visible ~ .check {
outline: 3px solid var(--neon);
outline-offset: 2px;
}
.goal-toggle input:checked ~ .goal-text {
color: var(--ink);
}
/* ---------- Achievements ---------- */
.badge-row {
display: flex;
gap: 12px;
list-style: none;
margin: 0;
padding: 2px;
overflow-x: auto;
scrollbar-width: thin;
}
.ach {
flex: 1 0 auto;
min-width: 92px;
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
padding: 16px 12px;
border-radius: var(--r-md);
background: var(--bg);
border: 1px solid var(--line);
text-align: center;
transition: transform 0.14s, border-color 0.16s;
}
.ach:hover {
transform: translateY(-3px);
border-color: var(--line-2);
}
.ach-ic {
font-size: 26px;
}
.ach-name {
font-size: 12px;
font-weight: 700;
color: var(--ink-2);
}
.ach.locked {
opacity: 0.5;
filter: grayscale(0.4);
}
/* ---------- Bookings ---------- */
.booking-list {
list-style: none;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
gap: 10px;
}
.booking {
display: flex;
align-items: center;
gap: 14px;
padding: 12px 14px;
border-radius: var(--r-md);
background: var(--bg);
border: 1px solid var(--line);
transition: border-color 0.16s, opacity 0.3s, transform 0.3s;
}
.booking:hover {
border-color: var(--line-2);
}
.booking.removing {
opacity: 0;
transform: translateX(20px);
}
.bk-when {
flex: none;
width: 50px;
display: flex;
flex-direction: column;
align-items: center;
padding: 6px 0;
border-radius: var(--r-sm);
background: var(--neon-50);
border: 1px solid rgba(198, 255, 58, 0.22);
}
.bk-day {
font-size: 10px;
font-weight: 800;
letter-spacing: 0.1em;
color: var(--neon);
}
.bk-date {
font-size: 20px;
font-weight: 900;
line-height: 1;
color: var(--ink);
}
.bk-info {
flex: 1;
min-width: 0;
}
.bk-name {
margin: 0;
font-size: 15px;
font-weight: 700;
}
.bk-sub {
margin: 1px 0 0;
font-size: 12px;
color: var(--muted);
}
.bk-cancel {
flex: none;
padding: 7px 12px;
border-radius: var(--r-sm);
border: 1px solid var(--line-2);
background: transparent;
color: var(--ink-2);
font-size: 12px;
font-weight: 700;
transition: color 0.16s, border-color 0.16s, background 0.16s;
}
.bk-cancel:hover {
color: var(--danger);
border-color: var(--danger);
background: rgba(248, 113, 113, 0.1);
}
/* ---------- Toast ---------- */
.toast {
position: fixed;
left: 50%;
bottom: 26px;
transform: translate(-50%, 24px);
background: var(--elevated);
color: var(--ink);
border: 1px solid var(--line-2);
border-left: 3px solid var(--neon);
padding: 13px 18px;
border-radius: var(--r-md);
font-weight: 600;
font-size: 14px;
box-shadow: var(--sh-3);
opacity: 0;
pointer-events: none;
transition: opacity 0.25s, transform 0.25s;
z-index: 50;
max-width: calc(100vw - 32px);
}
.toast.show {
opacity: 1;
transform: translate(-50%, 0);
}
/* ---------- Responsive ---------- */
@media (max-width: 860px) {
.grid {
grid-template-columns: 1fr 1fr;
}
.next-class,
.streak,
.stats,
.goals,
.achievements,
.bookings {
grid-column: span 2;
}
}
@media (max-width: 520px) {
.app {
padding: 20px 14px 48px;
}
.grid {
grid-template-columns: 1fr;
}
.next-class,
.streak,
.stats,
.goals,
.achievements,
.bookings {
grid-column: span 1;
}
.avatar {
width: 50px;
height: 50px;
font-size: 17px;
}
.rank {
display: none;
}
.quickbar {
gap: 8px;
}
.qa {
min-width: 0;
padding: 13px 10px;
font-size: 13px;
}
.cd-unit {
min-width: 0;
flex: 1;
padding: 9px 6px;
}
.cd-num {
font-size: 24px;
}
.goals-body {
flex-direction: column;
align-items: stretch;
}
.ring-wrap {
align-self: center;
}
.stat-val {
font-size: 22px;
}
}(function () {
"use strict";
/* ---------- Toast helper ---------- */
var toastEl = document.getElementById("toast");
var toastTimer = null;
function toast(msg) {
if (!toastEl) return;
toastEl.textContent = msg;
toastEl.classList.add("show");
clearTimeout(toastTimer);
toastTimer = setTimeout(function () {
toastEl.classList.remove("show");
}, 2400);
}
/* ---------- Time-aware greeting ---------- */
(function () {
var el = document.getElementById("time-greeting");
if (!el) return;
var h = new Date().getHours();
var g = h < 12 ? "Good morning" : h < 18 ? "Good afternoon" : "Good evening";
el.textContent = g;
})();
/* ---------- Next-class countdown ---------- */
(function () {
var cd = document.getElementById("countdown");
if (!cd) return;
var hEl = cd.querySelector('[data-cd="h"]');
var mEl = cd.querySelector('[data-cd="m"]');
var sEl = cd.querySelector('[data-cd="s"]');
// Target: ~1h 47m 30s from load (deterministic feel, still ticks).
var target = Date.now() + (1 * 3600 + 47 * 60 + 30) * 1000;
function pad(n) {
return String(n).padStart(2, "0");
}
function tick() {
var diff = Math.max(0, target - Date.now());
var total = Math.floor(diff / 1000);
var h = Math.floor(total / 3600);
var m = Math.floor((total % 3600) / 60);
var s = total % 60;
hEl.textContent = pad(h);
mEl.textContent = pad(m);
sEl.textContent = pad(s);
if (diff <= 0) {
clearInterval(timer);
var badge = document.querySelector(".badge-live");
if (badge) badge.textContent = "Class started";
}
}
tick();
var timer = setInterval(tick, 1000);
})();
/* ---------- Check-in button ---------- */
(function () {
var btn = document.getElementById("checkin-btn");
if (!btn) return;
var label = btn.querySelector(".ci-label");
btn.addEventListener("click", function () {
if (btn.classList.contains("checked")) return;
btn.classList.add("pulse");
setTimeout(function () {
btn.classList.remove("pulse");
}, 600);
btn.classList.add("checked");
label.textContent = "✓ Checked in — see you in Studio 2";
var streak = document.getElementById("streak-count");
if (streak) {
streak.textContent = String((parseInt(streak.textContent, 10) || 0) + 1);
}
var today = document.querySelector(".day.today");
if (today) {
today.classList.remove("today");
today.classList.add("done");
}
toast("Checked in to HIIT Inferno 🔥");
});
})();
/* ---------- Goals ring ---------- */
(function () {
var list = document.getElementById("goal-list");
var ring = document.getElementById("ring-fill");
var ringNum = document.getElementById("ring-num");
var totalEl = document.getElementById("goal-total");
var pctBadge = document.getElementById("goal-pct-badge");
if (!list || !ring) return;
var boxes = Array.prototype.slice.call(list.querySelectorAll("[data-goal]"));
var total = boxes.length;
var CIRC = 2 * Math.PI * 52; // r = 52
ring.style.strokeDasharray = CIRC.toFixed(1);
if (totalEl) totalEl.textContent = String(total);
function update(announce) {
var done = boxes.filter(function (b) {
return b.checked;
}).length;
var frac = total ? done / total : 0;
ring.style.strokeDashoffset = (CIRC * (1 - frac)).toFixed(1);
ring.classList.toggle("complete", done === total && total > 0);
if (ringNum) ringNum.textContent = String(done);
var pct = Math.round(frac * 100);
if (pctBadge) pctBadge.textContent = pct + "%";
if (announce) {
if (done === total) {
toast("All weekly goals complete! 💪");
} else {
toast(done + " of " + total + " goals done");
}
}
}
boxes.forEach(function (b) {
b.addEventListener("change", function () {
update(true);
});
});
update(false);
})();
/* ---------- Quick actions ---------- */
(function () {
var labels = {
book: "Opening class schedule…",
log: "Log a workout — pick a routine",
scan: "Hold your QR to the reader to scan in",
badges: "Showing all 18 achievements"
};
document.querySelectorAll("[data-action]").forEach(function (el) {
el.addEventListener("click", function () {
var a = el.getAttribute("data-action");
toast(labels[a] || "Done");
});
});
})();
/* ---------- Cancel bookings ---------- */
(function () {
document.querySelectorAll(".bk-cancel").forEach(function (btn) {
btn.addEventListener("click", function () {
var row = btn.closest(".booking");
if (!row) return;
var name = row.querySelector(".bk-name");
var nm = name ? name.textContent : "Booking";
row.classList.add("removing");
setTimeout(function () {
row.remove();
toast("Cancelled: " + nm);
}, 300);
});
});
})();
/* ---------- Bell ---------- */
(function () {
var bell = document.getElementById("bell-btn");
if (!bell) return;
bell.addEventListener("click", function () {
var dot = bell.querySelector(".dot");
if (dot) dot.style.display = "none";
toast("2 new: class reminder · new PR logged");
});
})();
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Gym — Member Dashboard</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<main class="app" role="main">
<!-- Greeting header -->
<header class="topbar">
<div class="greet">
<div class="avatar" aria-hidden="true">MR</div>
<div class="greet-text">
<p class="eyebrow" id="time-greeting">Good morning</p>
<h1>Maya Reyes</h1>
<p class="sub">Crossfit Pro · Member since 2023</p>
</div>
</div>
<div class="topbar-actions">
<button class="icon-btn" type="button" aria-label="Notifications" id="bell-btn">
<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 8a6 6 0 0 0-12 0c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.7 21a2 2 0 0 1-3.4 0"/></svg>
<span class="dot" aria-hidden="true"></span>
</button>
<div class="rank" aria-label="Tier: Gold">
<span class="rank-icon" aria-hidden="true">★</span> Gold
</div>
</div>
</header>
<!-- Quick action bar -->
<nav class="quickbar" aria-label="Quick actions">
<button class="qa qa-primary" type="button" data-action="book">
<span class="qa-ic" aria-hidden="true">+</span> Book class
</button>
<button class="qa" type="button" data-action="log">
<span class="qa-ic" aria-hidden="true">✎</span> Log workout
</button>
<button class="qa qa-orange" type="button" data-action="scan">
<span class="qa-ic" aria-hidden="true">⧉</span> Scan in
</button>
</nav>
<div class="grid">
<!-- Next class card -->
<section class="card next-class" aria-labelledby="nc-title">
<div class="card-head">
<p class="eyebrow">Next class</p>
<span class="badge badge-live">Starts soon</span>
</div>
<h2 id="nc-title">HIIT Inferno</h2>
<p class="nc-meta">Studio 2 · with <strong>Coach Dré</strong></p>
<div class="countdown" id="countdown" aria-live="polite" aria-label="Time until class">
<div class="cd-unit"><span class="cd-num" data-cd="h">00</span><span class="cd-lbl">hrs</span></div>
<span class="cd-sep">:</span>
<div class="cd-unit"><span class="cd-num" data-cd="m">00</span><span class="cd-lbl">min</span></div>
<span class="cd-sep">:</span>
<div class="cd-unit"><span class="cd-num" data-cd="s">00</span><span class="cd-lbl">sec</span></div>
</div>
<button class="checkin-btn" type="button" id="checkin-btn">
<span class="ci-label">Check in</span>
</button>
</section>
<!-- Streak widget -->
<section class="card streak" aria-labelledby="streak-title">
<div class="card-head">
<p class="eyebrow">Current streak</p>
<span class="badge badge-fire" aria-hidden="true">🔥 On fire</span>
</div>
<div class="streak-num">
<span class="big" id="streak-count">12</span>
<span class="unit">day streak</span>
</div>
<h3 id="streak-title" class="sr-only">Last 7 days</h3>
<ul class="dots" aria-label="Last 7 days of activity">
<li class="day done"><span class="dot-pill" aria-hidden="true"></span>M</li>
<li class="day done"><span class="dot-pill" aria-hidden="true"></span>T</li>
<li class="day done"><span class="dot-pill" aria-hidden="true"></span>W</li>
<li class="day rest"><span class="dot-pill" aria-hidden="true"></span>T</li>
<li class="day done"><span class="dot-pill" aria-hidden="true"></span>F</li>
<li class="day done"><span class="dot-pill" aria-hidden="true"></span>S</li>
<li class="day today"><span class="dot-pill" aria-hidden="true"></span>S</li>
</ul>
<p class="streak-hint">Train today to keep it alive — best streak: <strong>21</strong></p>
</section>
<!-- Weekly stats -->
<section class="card stats" aria-label="This week's activity">
<div class="card-head">
<p class="eyebrow">This week</p>
<span class="badge">Mon–Sun</span>
</div>
<ul class="stat-grid">
<li class="stat">
<span class="stat-ic" aria-hidden="true">🏋️</span>
<span class="stat-val">5</span>
<span class="stat-lbl">Workouts</span>
</li>
<li class="stat">
<span class="stat-ic" aria-hidden="true">⏱️</span>
<span class="stat-val">312</span>
<span class="stat-lbl">Minutes</span>
</li>
<li class="stat">
<span class="stat-ic" aria-hidden="true">🔥</span>
<span class="stat-val">2,940</span>
<span class="stat-lbl">Calories</span>
</li>
<li class="stat">
<span class="stat-ic" aria-hidden="true">📈</span>
<span class="stat-val">+18%</span>
<span class="stat-lbl">vs last wk</span>
</li>
</ul>
</section>
<!-- Goals / progress ring -->
<section class="card goals" aria-labelledby="goals-title">
<div class="card-head">
<p class="eyebrow">Weekly goals</p>
<span class="badge" id="goal-pct-badge">0%</span>
</div>
<div class="goals-body">
<div class="ring-wrap" aria-hidden="true">
<svg class="ring" viewBox="0 0 120 120" width="120" height="120">
<circle class="ring-track" cx="60" cy="60" r="52" />
<circle class="ring-fill" id="ring-fill" cx="60" cy="60" r="52" />
</svg>
<div class="ring-center">
<span class="ring-num" id="ring-num">0</span>
<span class="ring-sub">of <span id="goal-total">4</span></span>
</div>
</div>
<h2 id="goals-title" class="sr-only">Toggle your goals</h2>
<ul class="goal-list" id="goal-list">
<li>
<label class="goal-toggle">
<input type="checkbox" data-goal />
<span class="check" aria-hidden="true"></span>
<span class="goal-text">3 strength sessions</span>
</label>
</li>
<li>
<label class="goal-toggle">
<input type="checkbox" data-goal checked />
<span class="check" aria-hidden="true"></span>
<span class="goal-text">2 cardio sessions</span>
</label>
</li>
<li>
<label class="goal-toggle">
<input type="checkbox" data-goal />
<span class="check" aria-hidden="true"></span>
<span class="goal-text">Hit 10k steps × 5</span>
</label>
</li>
<li>
<label class="goal-toggle">
<input type="checkbox" data-goal />
<span class="check" aria-hidden="true"></span>
<span class="goal-text">Mobility / stretch day</span>
</label>
</li>
</ul>
</div>
</section>
<!-- Achievements -->
<section class="card achievements" aria-label="Recent achievements">
<div class="card-head">
<p class="eyebrow">Achievements</p>
<button class="link-btn" type="button" data-action="badges">View all</button>
</div>
<ul class="badge-row">
<li class="ach" title="Logged 7 days in a row">
<span class="ach-ic" aria-hidden="true">🔥</span>
<span class="ach-name">Week Warrior</span>
</li>
<li class="ach" title="Lifted a new personal best">
<span class="ach-ic" aria-hidden="true">🏆</span>
<span class="ach-name">New PR</span>
</li>
<li class="ach" title="50 classes attended">
<span class="ach-ic" aria-hidden="true">💪</span>
<span class="ach-name">50 Classes</span>
</li>
<li class="ach locked" title="Reach a 30-day streak to unlock">
<span class="ach-ic" aria-hidden="true">🔒</span>
<span class="ach-name">30-Day</span>
</li>
</ul>
</section>
<!-- Upcoming bookings -->
<section class="card bookings" aria-labelledby="bk-title">
<div class="card-head">
<p class="eyebrow" id="bk-title">Upcoming bookings</p>
<button class="link-btn" type="button" data-action="book">Book class</button>
</div>
<ul class="booking-list">
<li class="booking">
<div class="bk-when"><span class="bk-day">TUE</span><span class="bk-date">10</span></div>
<div class="bk-info">
<p class="bk-name">HIIT Inferno</p>
<p class="bk-sub">6:30 PM · Coach Dré · Studio 2</p>
</div>
<button class="bk-cancel" type="button" aria-label="Cancel HIIT Inferno booking">Cancel</button>
</li>
<li class="booking">
<div class="bk-when"><span class="bk-day">WED</span><span class="bk-date">11</span></div>
<div class="bk-info">
<p class="bk-name">Power Yoga Flow</p>
<p class="bk-sub">7:00 AM · Liv Tanaka · Mind Room</p>
</div>
<button class="bk-cancel" type="button" aria-label="Cancel Power Yoga Flow booking">Cancel</button>
</li>
<li class="booking">
<div class="bk-when"><span class="bk-day">FRI</span><span class="bk-date">13</span></div>
<div class="bk-info">
<p class="bk-name">Olympic Lifting</p>
<p class="bk-sub">5:45 PM · Coach Idris · Platform</p>
</div>
<button class="bk-cancel" type="button" aria-label="Cancel Olympic Lifting booking">Cancel</button>
</li>
</ul>
</section>
</div>
</main>
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>Member Dashboard
A bold, athletic home screen for a gym member, built on the dark performance palette with a neon-lime and orange accent system. The header greets the member by time of day with an avatar and a Gold-tier pill, followed by a large tap-friendly quick-action bar — Book class, Log workout, and Scan in. Below it a responsive card grid lays out everything at a glance: a featured next class with a ticking hours/minutes/seconds countdown, a streak widget showing a 12-day run and the last seven days as glowing dots, weekly stat cards for workouts, minutes, calories and trend, a weekly goals panel, a recent-achievements badge row, and a list of upcoming bookings.
The screen is genuinely interactive. The countdown updates every second toward the next session; checking in fires a ripple animation, locks the button into a confirmed state, bumps the streak and fills today’s dot. Toggling any goal recomputes the SVG progress ring — its stroke offset animates and turns from lime to green at 100% — while the percentage badge and center count stay in sync. Every action surfaces a small toast, bookings can be cancelled with a slide-out, and the notification bell clears its unread dot.
The layout collapses from a six-column grid to two columns and then a single stacked column, staying usable down to ~360px with visible focus rings and keyboard-operable controls throughout.