Ticketing — Event Detail
A high-energy event detail page for a fictional music festival. Photographic gradient hero with title, date, venue and a live countdown to doors. Three ticket tiers with color-coded seat legend, low-stock and sold-out badges, and quantity steppers that drive a running order summary and sticky checkout bar. Includes a tabbed lineup schedule, a stylized venue map with info, and an FAQ accordion. Vanilla JS only.
MCP
Code
:root {
--brand: #7c3aed;
--brand-d: #6d28d9;
--ink: #0e0e16;
--ink-2: #3a3a4d;
--muted: #6c6c80;
--bg: #f5f4f9;
--surface: #ffffff;
--line: rgba(14, 14, 22, 0.1);
--ok: #16a34a;
--warn: #d97706;
--danger: #dc2626;
--accent: #ff3d81;
--ga: #38bdf8;
--vip: #a855f7;
--plat: #f59e0b;
--r-sm: 8px;
--r-md: 14px;
--r-lg: 20px;
--sh-1: 0 1px 2px rgba(14, 14, 22, 0.06), 0 4px 14px rgba(14, 14, 22, 0.06);
--sh-2: 0 10px 30px rgba(14, 14, 22, 0.14);
--sh-brand: 0 10px 26px rgba(124, 58, 237, 0.4);
}
* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
margin: 0;
font-family: "Inter", system-ui, -apple-system, sans-serif;
line-height: 1.5;
color: var(--ink);
background: var(--bg);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
h1, h2, h3, p { margin: 0; }
a { color: inherit; }
img { max-width: 100%; display: block; }
.wrap { width: min(1120px, 100% - 40px); margin-inline: auto; }
/* ---- Buttons ---- */
.btn {
--bg: var(--surface);
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
border: 1px solid var(--line);
background: var(--bg);
color: var(--ink);
font: inherit;
font-weight: 700;
padding: 11px 18px;
border-radius: var(--r-md);
cursor: pointer;
text-decoration: none;
transition: transform .12s ease, box-shadow .15s ease, background .15s ease, opacity .15s;
}
.btn:hover { transform: translateY(-1px); }
.btn:active { transform: translateY(0); }
.btn:focus-visible { outline: 3px solid color-mix(in srgb, var(--brand) 50%, white); outline-offset: 2px; }
.btn--brand { background: var(--brand); border-color: var(--brand); color: #fff; box-shadow: var(--sh-brand); }
.btn--brand:hover { background: var(--brand-d); }
.btn--ghost { background: transparent; }
.btn--ghost:hover { background: rgba(14,14,22,.04); }
.btn--sm { padding: 8px 13px; font-size: .85rem; border-radius: var(--r-sm); }
.btn--lg { padding: 14px 26px; font-size: 1.05rem; }
.btn--block { width: 100%; }
.btn[disabled] { opacity: .45; cursor: not-allowed; box-shadow: none; transform: none; }
/* ---- Topbar ---- */
.topbar {
position: sticky; top: 0; z-index: 30;
background: color-mix(in srgb, var(--surface) 86%, transparent);
backdrop-filter: blur(10px);
border-bottom: 1px solid var(--line);
}
.topbar__inner { display: flex; align-items: center; gap: 22px; height: 62px; }
.brand { display: flex; align-items: center; gap: 9px; font-weight: 800; text-decoration: none; letter-spacing: -.02em; }
.brand__mark { color: var(--accent); font-size: 1.2rem; }
.brand__name span { color: var(--brand); }
.topbar__nav { display: flex; gap: 20px; margin-left: auto; }
.topbar__nav a { text-decoration: none; color: var(--ink-2); font-weight: 600; font-size: .92rem; }
.topbar__nav a:hover { color: var(--brand); }
/* ---- Hero ---- */
.hero { position: relative; color: #fff; overflow: hidden; }
.hero__media {
position: absolute; inset: 0;
background:
radial-gradient(120% 100% at 80% -10%, rgba(255,61,129,.55), transparent 55%),
radial-gradient(120% 120% at 10% 110%, rgba(56,189,248,.45), transparent 55%),
linear-gradient(135deg, #1a0b3a, #2a0f57 45%, #11061f);
}
.hero__glow {
position: absolute; inset: 0;
background:
repeating-linear-gradient(115deg, rgba(255,255,255,.05) 0 2px, transparent 2px 18px);
mix-blend-mode: screen;
opacity: .6;
}
.hero__inner { position: relative; padding: 92px 0 64px; }
.hero__content { max-width: 720px; }
.hero__badges { display: flex; gap: 8px; margin-bottom: 16px; flex-wrap: wrap; }
.pill {
display: inline-flex; align-items: center;
font-size: .76rem; font-weight: 700; letter-spacing: .03em; text-transform: uppercase;
padding: 5px 11px; border-radius: 999px;
}
.pill--accent { background: var(--accent); color: #fff; }
.pill--ghost { background: rgba(255,255,255,.14); color: #fff; border: 1px solid rgba(255,255,255,.3); }
.pill--low { background: var(--warn); color: #fff; }
.hero__title {
font-size: clamp(2.3rem, 6vw, 4rem);
font-weight: 800; letter-spacing: -.03em; line-height: 1.04;
text-shadow: 0 6px 30px rgba(0,0,0,.4);
}
.hero__meta {
margin-top: 18px; display: flex; flex-wrap: wrap; gap: 10px;
color: rgba(255,255,255,.86); font-size: 1.02rem;
}
.hero__meta strong { color: #fff; }
.hero__cta { margin-top: 30px; display: flex; align-items: center; gap: 22px; flex-wrap: wrap; }
.countdown { display: flex; gap: 8px; }
.countdown__unit {
background: rgba(255,255,255,.1);
border: 1px solid rgba(255,255,255,.2);
border-radius: var(--r-sm);
padding: 8px 12px; min-width: 56px; text-align: center;
backdrop-filter: blur(6px);
}
.countdown__unit b { display: block; font-size: 1.35rem; font-weight: 800; font-variant-numeric: tabular-nums; }
.countdown__unit small { font-size: .65rem; text-transform: uppercase; letter-spacing: .08em; color: rgba(255,255,255,.7); }
/* ---- Layout ---- */
.layout { display: grid; grid-template-columns: 1fr 340px; gap: 26px; align-items: start; padding: 34px 0 90px; }
.layout__main { display: grid; gap: 22px; min-width: 0; }
.layout__side { position: sticky; top: 80px; }
.card {
background: var(--surface);
border: 1px solid var(--line);
border-radius: var(--r-lg);
box-shadow: var(--sh-1);
}
.section { padding: 24px; }
.section__head { display: flex; align-items: center; justify-content: space-between; gap: 14px; margin-bottom: 18px; flex-wrap: wrap; }
.section__head h2 { font-size: 1.3rem; font-weight: 800; letter-spacing: -.02em; }
.legend { display: flex; align-items: center; gap: 12px; font-size: .82rem; color: var(--muted); font-weight: 600; }
.dot { width: 10px; height: 10px; border-radius: 50%; display: inline-block; margin-right: 4px; vertical-align: -1px; }
.dot--ga { background: var(--ga); }
.dot--vip { background: var(--vip); }
.dot--plat { background: var(--plat); }
/* ---- Tiers ---- */
.tiers { list-style: none; margin: 0; padding: 0; display: grid; gap: 12px; }
.tier {
position: relative; overflow: hidden;
display: grid; grid-template-columns: 1fr auto auto; align-items: center; gap: 18px;
padding: 16px 18px 16px 22px;
border: 1px solid var(--line);
border-radius: var(--r-md);
background: #fff;
transition: border-color .15s, box-shadow .15s, background .15s;
}
.tier:hover { box-shadow: var(--sh-1); }
.tier.is-selected { border-color: var(--brand); background: color-mix(in srgb, var(--brand) 5%, #fff); box-shadow: var(--sh-1); }
.tier__bar { position: absolute; left: 0; top: 0; bottom: 0; width: 5px; }
.tier[data-color="ga"] .tier__bar { background: var(--ga); }
.tier[data-color="vip"] .tier__bar { background: var(--vip); }
.tier[data-color="plat"] .tier__bar { background: var(--plat); }
.tier__info h3 { font-size: 1.05rem; font-weight: 700; display: flex; align-items: center; gap: 8px; }
.tier__tag { font-size: .66rem; text-transform: uppercase; letter-spacing: .05em; background: var(--accent); color: #fff; padding: 3px 7px; border-radius: 999px; font-weight: 700; }
.tier__info p { color: var(--muted); font-size: .9rem; margin-top: 3px; }
.tier__stock { display: inline-block; margin-top: 7px; font-size: .76rem; font-weight: 700; color: var(--ok); }
.tier__stock--low { color: var(--warn); }
.tier__stock--out { color: var(--danger); }
.tier__price { text-align: right; }
.tier__price .amt { font-size: 1.4rem; font-weight: 800; letter-spacing: -.02em; }
.tier__price small { display: block; color: var(--muted); font-size: .72rem; }
.tier--soldout { opacity: .62; }
.tier--soldout:hover { box-shadow: none; }
.stepper { display: inline-flex; align-items: center; border: 1px solid var(--line); border-radius: 999px; background: #fff; overflow: hidden; }
.step {
width: 36px; height: 36px; border: 0; background: transparent; cursor: pointer;
font-size: 1.2rem; font-weight: 700; color: var(--brand); line-height: 1;
transition: background .12s;
}
.step:hover:not([disabled]) { background: color-mix(in srgb, var(--brand) 12%, #fff); }
.step[disabled] { color: var(--muted); cursor: not-allowed; }
.step__val { min-width: 26px; text-align: center; font-weight: 800; font-variant-numeric: tabular-nums; }
.tickets__note { margin-top: 14px; font-size: .8rem; color: var(--muted); }
/* ---- Tabs / schedule ---- */
.tabs { display: inline-flex; gap: 4px; background: var(--bg); padding: 4px; border-radius: 999px; }
.tab {
border: 0; background: transparent; font: inherit; font-weight: 700; font-size: .85rem;
padding: 7px 15px; border-radius: 999px; cursor: pointer; color: var(--ink-2);
transition: background .15s, color .15s;
}
.tab.is-active { background: var(--surface); color: var(--brand); box-shadow: var(--sh-1); }
.sched { list-style: none; margin: 0; padding: 0; display: grid; gap: 2px; }
.slot {
display: grid; grid-template-columns: 92px 1fr auto; align-items: center; gap: 14px;
padding: 12px 14px; border-radius: var(--r-sm);
border-left: 3px solid var(--line);
transition: background .12s;
}
.slot:hover { background: var(--bg); }
.slot__time { font-weight: 700; font-variant-numeric: tabular-nums; color: var(--ink-2); font-size: .9rem; }
.slot__act { font-weight: 700; }
.slot__role { font-size: .76rem; color: var(--muted); text-transform: uppercase; letter-spacing: .05em; }
.slot--head { border-left-color: var(--accent); background: color-mix(in srgb, var(--accent) 6%, #fff); }
.slot--head .slot__act { font-size: 1.15rem; letter-spacing: -.01em; }
.slot--head .slot__role { color: var(--accent); font-weight: 700; }
/* ---- Venue ---- */
.venue { display: grid; grid-template-columns: 1.2fr 1fr; gap: 22px; }
.venue__map {
position: relative; min-height: 240px; border-radius: var(--r-md); overflow: hidden;
background:
linear-gradient(135deg, #e6e9f5, #d8def2),
radial-gradient(circle at 30% 40%, #fff 1px, transparent 1px);
display: grid; place-items: center;
}
.venue__map::before {
content: ""; position: absolute; inset: 0;
background-image:
linear-gradient(rgba(124,58,237,.1) 1px, transparent 1px),
linear-gradient(90deg, rgba(124,58,237,.1) 1px, transparent 1px);
background-size: 28px 28px;
}
.venue__path {
position: absolute; width: 70%; height: 70%;
border: 2px dashed rgba(124,58,237,.4); border-radius: 40% 60% 55% 45%;
}
.venue__pin { position: relative; font-size: 2.4rem; color: var(--accent); filter: drop-shadow(0 4px 8px rgba(255,61,129,.5)); animation: bob 2.4s ease-in-out infinite; }
@keyframes bob { 50% { transform: translateY(-7px); } }
.venue__info h3 { font-size: 1.15rem; font-weight: 700; }
.venue__info > p { color: var(--muted); margin-top: 4px; }
.kv { margin: 16px 0; display: grid; grid-template-columns: 1fr 1fr; gap: 12px 18px; }
.kv div { border-left: 2px solid var(--brand); padding-left: 10px; }
.kv dt { font-size: .72rem; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); font-weight: 700; }
.kv dd { margin: 2px 0 0; font-weight: 700; font-size: .92rem; }
/* ---- FAQ ---- */
.faq { display: grid; gap: 8px; }
.qa { border: 1px solid var(--line); border-radius: var(--r-md); background: #fff; overflow: hidden; }
.qa summary {
list-style: none; cursor: pointer; padding: 15px 18px; font-weight: 700;
display: flex; align-items: center; justify-content: space-between; gap: 12px;
}
.qa summary::-webkit-details-marker { display: none; }
.qa__icon { color: var(--brand); font-size: 1.3rem; font-weight: 700; transition: transform .2s; }
.qa[open] .qa__icon { transform: rotate(45deg); }
.qa p { padding: 0 18px 16px; color: var(--ink-2); }
/* ---- Summary aside ---- */
.summary { padding: 20px; }
.summary__h { font-size: 1.05rem; font-weight: 800; }
.summary__lines { list-style: none; margin: 14px 0; padding: 0; display: grid; gap: 8px; }
.summary__empty { color: var(--muted); font-size: .9rem; }
.summary__line { display: flex; justify-content: space-between; gap: 10px; font-size: .9rem; align-items: center; }
.summary__line b { font-weight: 700; }
.summary__line .ln-name { display: flex; align-items: center; gap: 7px; }
.summary__line .ln-name i { width: 9px; height: 9px; border-radius: 50%; }
.summary__line .ln-name small { color: var(--muted); }
.summary__totals { border-top: 1px dashed var(--line); padding-top: 12px; display: grid; gap: 6px; }
.row { display: flex; justify-content: space-between; font-size: .92rem; }
.row--muted { color: var(--muted); }
.row--grand { font-weight: 800; font-size: 1.15rem; margin-top: 4px; padding-top: 8px; border-top: 1px solid var(--line); }
.summary .btn { margin-top: 14px; }
/* ---- Sticky bar ---- */
.sticky {
position: fixed; left: 0; right: 0; bottom: 0; z-index: 40;
background: var(--ink); color: #fff;
box-shadow: 0 -8px 30px rgba(0,0,0,.25);
transform: translateY(0);
}
.sticky[hidden] { display: none; }
.sticky__inner { display: flex; align-items: center; justify-content: space-between; gap: 14px; padding: 13px 0; }
.sticky__sum { display: flex; flex-direction: column; }
.sticky__qty { font-size: .78rem; color: rgba(255,255,255,.65); }
.sticky__total { font-size: 1.3rem; font-weight: 800; font-variant-numeric: tabular-nums; }
/* ---- Toast ---- */
.toast {
position: fixed; left: 50%; bottom: 90px;
transform: translateX(-50%) translateY(20px);
background: var(--ink); color: #fff; padding: 12px 20px; border-radius: var(--r-md);
font-weight: 600; font-size: .9rem; box-shadow: var(--sh-2);
opacity: 0; pointer-events: none; transition: opacity .25s, transform .25s; z-index: 60;
}
.toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }
/* ---- Responsive ---- */
@media (max-width: 900px) {
.layout { grid-template-columns: 1fr; }
.layout__side { position: static; order: -1; }
.venue { grid-template-columns: 1fr; }
}
@media (max-width: 520px) {
.wrap { width: min(1120px, 100% - 28px); }
.topbar__nav { display: none; }
.hero__inner { padding: 64px 0 44px; }
.countdown__unit { min-width: 48px; padding: 7px 9px; }
.section { padding: 18px; }
.tier { grid-template-columns: 1fr auto; row-gap: 12px; }
.tier__price { grid-column: 2; }
.stepper { grid-column: 1 / -1; justify-self: start; }
.slot { grid-template-columns: 72px 1fr; }
.slot__role { grid-column: 2; }
.kv { grid-template-columns: 1fr; }
}(function () {
"use strict";
var MAX_TOTAL = 8;
var FEE_RATE = 0.12;
var COLORS = { ga: "#38bdf8", vip: "#a855f7", plat: "#f59e0b" };
var tierList = document.getElementById("tierList");
var tiers = Array.prototype.slice.call(tierList.querySelectorAll(".tier"));
var summaryLines = document.getElementById("summaryLines");
var subTotalEl = document.getElementById("subTotal");
var feeTotalEl = document.getElementById("feeTotal");
var grandTotalEl = document.getElementById("grandTotal");
var stickyBar = document.getElementById("stickyBar");
var stickyQty = document.getElementById("stickyQty");
var stickyTotal = document.getElementById("stickyTotal");
var lowStockPill = document.getElementById("lowStockPill");
var toastEl = document.getElementById("toast");
var state = {}; // tier -> qty
function money(n) {
return "$" + n.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 });
}
var toastTimer;
function toast(msg) {
toastEl.textContent = msg;
toastEl.classList.add("show");
clearTimeout(toastTimer);
toastTimer = setTimeout(function () { toastEl.classList.remove("show"); }, 2200);
}
function totalCount() {
return Object.keys(state).reduce(function (a, k) { return a + state[k]; }, 0);
}
function meta(tier) {
return {
id: tier.getAttribute("data-tier"),
price: parseFloat(tier.getAttribute("data-price")),
stock: parseInt(tier.getAttribute("data-stock"), 10),
color: tier.getAttribute("data-color"),
name: tier.querySelector("h3").firstChild.textContent.trim()
};
}
function setQty(tier, qty) {
var m = meta(tier);
qty = Math.max(0, Math.min(qty, m.stock));
var others = totalCount() - (state[m.id] || 0);
if (others + qty > MAX_TOTAL) {
qty = MAX_TOTAL - others;
toast("Max " + MAX_TOTAL + " tickets per order");
}
state[m.id] = qty;
tier.querySelector(".step__val").textContent = qty;
tier.classList.toggle("is-selected", qty > 0);
render();
}
function render() {
var sub = 0;
var lines = [];
tiers.forEach(function (tier) {
var m = meta(tier);
var qty = state[m.id] || 0;
if (qty > 0) {
sub += qty * m.price;
lines.push(
'<li class="summary__line"><span class="ln-name">' +
'<i style="background:' + COLORS[m.color] + '"></i>' +
m.name + ' <small>×' + qty + '</small></span>' +
'<b>' + money(qty * m.price) + '</b></li>'
);
}
});
var fee = sub * FEE_RATE;
var grand = sub + fee;
var count = totalCount();
summaryLines.innerHTML = lines.length
? lines.join("")
: '<li class="summary__empty">No tickets selected yet.</li>';
subTotalEl.textContent = money(sub);
feeTotalEl.textContent = money(fee);
grandTotalEl.textContent = money(grand);
document.getElementById("checkoutSide").disabled = count === 0;
stickyQty.textContent = count + (count === 1 ? " ticket" : " tickets");
stickyTotal.textContent = money(grand);
stickyBar.hidden = count === 0;
}
// Stepper handlers
tiers.forEach(function (tier) {
var minus = tier.querySelector(".step--minus");
var plus = tier.querySelector(".step--plus");
minus.addEventListener("click", function () { setQty(tier, (state[meta(tier).id] || 0) - 1); });
plus.addEventListener("click", function () { setQty(tier, (state[meta(tier).id] || 0) + 1); });
});
// Low-stock pill (any available tier under 15)
var anyLow = tiers.some(function (t) {
var s = meta(t).stock;
return s > 0 && s < 15;
});
if (anyLow && lowStockPill) lowStockPill.hidden = false;
// Schedule tabs
var tabs = Array.prototype.slice.call(document.querySelectorAll(".tab"));
tabs.forEach(function (tab) {
tab.addEventListener("click", function () {
tabs.forEach(function (t) {
t.classList.remove("is-active");
t.setAttribute("aria-selected", "false");
});
tab.classList.add("is-active");
tab.setAttribute("aria-selected", "true");
var day = tab.getAttribute("data-day");
document.querySelectorAll(".sched").forEach(function (s) {
s.hidden = s.getAttribute("data-day") !== day;
});
});
});
// Checkout actions
function checkout() {
var count = totalCount();
if (count === 0) { toast("Pick at least one ticket first"); return; }
toast("Reserved " + count + " ticket" + (count === 1 ? "" : "s") + " — held for 10:00");
}
document.getElementById("checkoutSide").addEventListener("click", checkout);
document.getElementById("checkoutBar").addEventListener("click", checkout);
// Misc buttons
document.getElementById("shareBtn").addEventListener("click", function () {
toast("Event link copied to clipboard");
});
document.getElementById("dirBtn").addEventListener("click", function () {
toast("Opening directions to Aurora Riverside Grounds");
});
// Countdown to doors (Aug 22, 2026 16:00 local)
var target = new Date(2026, 7, 22, 16, 0, 0).getTime();
var cdEls = {
d: document.querySelector('[data-cd="d"]'),
h: document.querySelector('[data-cd="h"]'),
m: document.querySelector('[data-cd="m"]'),
s: document.querySelector('[data-cd="s"]')
};
function pad(n) { return (n < 10 ? "0" : "") + n; }
function tick() {
var diff = Math.max(0, target - Date.now());
var s = Math.floor(diff / 1000);
cdEls.d.textContent = pad(Math.floor(s / 86400));
cdEls.h.textContent = pad(Math.floor((s % 86400) / 3600));
cdEls.m.textContent = pad(Math.floor((s % 3600) / 60));
cdEls.s.textContent = pad(s % 60);
}
tick();
setInterval(tick, 1000);
// Smooth-scroll for in-page nav
document.querySelectorAll('a[href^="#"]').forEach(function (a) {
a.addEventListener("click", function (e) {
var id = a.getAttribute("href");
if (id.length < 2) return;
var t = document.querySelector(id);
if (t) { e.preventDefault(); t.scrollIntoView({ behavior: "smooth", block: "start" }); }
});
});
render();
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Neon Pulse Festival — Event Detail</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>
<header class="topbar">
<div class="wrap topbar__inner">
<a class="brand" href="#" aria-label="Pulse Tickets home">
<span class="brand__mark" aria-hidden="true">◈</span>
<span class="brand__name">Pulse<span>Tix</span></span>
</a>
<nav class="topbar__nav" aria-label="Primary">
<a href="#lineup">Lineup</a>
<a href="#venue">Venue</a>
<a href="#faq">FAQ</a>
</nav>
<button class="btn btn--ghost btn--sm" type="button" id="shareBtn">Share</button>
</div>
</header>
<main>
<!-- HERO -->
<section class="hero" aria-label="Event hero">
<div class="hero__media" role="img" aria-label="Stage lights at a music festival">
<div class="hero__glow"></div>
</div>
<div class="wrap hero__inner">
<div class="hero__content">
<div class="hero__badges">
<span class="pill pill--accent">Festival</span>
<span class="pill pill--ghost">18+</span>
<span class="pill pill--low" id="lowStockPill" hidden>Low stock</span>
</div>
<h1 class="hero__title">Neon Pulse Festival 2026</h1>
<p class="hero__meta">
<span><strong>Sat, Aug 22 · 2026</strong> · 4:00 PM – 2:00 AM</span>
<span aria-hidden="true">·</span>
<span>Aurora Riverside Grounds, Portland OR</span>
</p>
<div class="hero__cta">
<a class="btn btn--brand btn--lg" href="#tickets">Get tickets</a>
<div class="countdown" id="countdown" aria-live="polite" aria-label="Time until doors open">
<div class="countdown__unit"><b data-cd="d">00</b><small>days</small></div>
<div class="countdown__unit"><b data-cd="h">00</b><small>hrs</small></div>
<div class="countdown__unit"><b data-cd="m">00</b><small>min</small></div>
<div class="countdown__unit"><b data-cd="s">00</b><small>sec</small></div>
</div>
</div>
</div>
</div>
</section>
<div class="wrap layout">
<div class="layout__main">
<!-- TICKETS -->
<section class="card section" id="tickets" aria-labelledby="ticketsH">
<div class="section__head">
<h2 id="ticketsH">Choose your tickets</h2>
<span class="legend">
<i class="dot dot--ga"></i>GA
<i class="dot dot--vip"></i>VIP
<i class="dot dot--plat"></i>Platinum
</span>
</div>
<ul class="tiers" id="tierList">
<li class="tier" data-tier="ga" data-price="89" data-stock="40" data-color="ga">
<span class="tier__bar" aria-hidden="true"></span>
<div class="tier__info">
<h3>General Admission</h3>
<p>Festival grounds access · all stages · re-entry.</p>
<span class="tier__stock">40 left</span>
</div>
<div class="tier__price"><span class="amt">$89</span><small>+ fees</small></div>
<div class="stepper" role="group" aria-label="General Admission quantity">
<button type="button" class="step step--minus" aria-label="Decrease General Admission">−</button>
<output class="step__val" aria-live="polite">0</output>
<button type="button" class="step step--plus" aria-label="Increase General Admission">+</button>
</div>
</li>
<li class="tier" data-tier="vip" data-price="199" data-stock="12" data-color="vip">
<span class="tier__bar" aria-hidden="true"></span>
<div class="tier__info">
<h3>VIP Lounge <span class="tier__tag">Popular</span></h3>
<p>Elevated viewing deck · fast lane · 2 drink tokens.</p>
<span class="tier__stock tier__stock--low">Only 12 left</span>
</div>
<div class="tier__price"><span class="amt">$199</span><small>+ fees</small></div>
<div class="stepper" role="group" aria-label="VIP Lounge quantity">
<button type="button" class="step step--minus" aria-label="Decrease VIP Lounge">−</button>
<output class="step__val" aria-live="polite">0</output>
<button type="button" class="step step--plus" aria-label="Increase VIP Lounge">+</button>
</div>
</li>
<li class="tier tier--soldout" data-tier="plat" data-price="349" data-stock="0" data-color="plat">
<span class="tier__bar" aria-hidden="true"></span>
<div class="tier__info">
<h3>Platinum Pit</h3>
<p>Front-of-stage pit · backstage tour · merch pack.</p>
<span class="tier__stock tier__stock--out">Sold out</span>
</div>
<div class="tier__price"><span class="amt">$349</span><small>+ fees</small></div>
<div class="stepper" role="group" aria-label="Platinum Pit quantity">
<button type="button" class="step step--minus" aria-label="Decrease Platinum Pit" disabled>−</button>
<output class="step__val" aria-live="polite">0</output>
<button type="button" class="step step--plus" aria-label="Increase Platinum Pit" disabled>+</button>
</div>
</li>
</ul>
<p class="tickets__note">Max 8 tickets per order · Prices in USD · Service fee 12% added at checkout.</p>
</section>
<!-- LINEUP / SCHEDULE -->
<section class="card section" id="lineup" aria-labelledby="lineupH">
<div class="section__head">
<h2 id="lineupH">Lineup & schedule</h2>
<div class="tabs" role="tablist" aria-label="Stage day">
<button class="tab is-active" role="tab" aria-selected="true" data-day="main">Main Stage</button>
<button class="tab" role="tab" aria-selected="false" data-day="electric">Electric Tent</button>
</div>
</div>
<ol class="sched" data-day="main">
<li class="slot"><span class="slot__time">4:00 PM</span><span class="slot__act">Velvet Static</span><span class="slot__role">Opener</span></li>
<li class="slot"><span class="slot__time">5:30 PM</span><span class="slot__act">The Lantern Birds</span><span class="slot__role">Support</span></li>
<li class="slot"><span class="slot__time">7:15 PM</span><span class="slot__act">Mara Voss</span><span class="slot__role">Special guest</span></li>
<li class="slot slot--head"><span class="slot__time">9:30 PM</span><span class="slot__act">NOVA REIGN</span><span class="slot__role">Headliner</span></li>
</ol>
<ol class="sched" data-day="electric" hidden>
<li class="slot"><span class="slot__time">6:00 PM</span><span class="slot__act">Glasswave</span><span class="slot__role">Opener</span></li>
<li class="slot"><span class="slot__time">8:00 PM</span><span class="slot__act">Kō & the Echo</span><span class="slot__role">Support</span></li>
<li class="slot slot--head"><span class="slot__time">11:00 PM</span><span class="slot__act">DJ HALOGEN</span><span class="slot__role">Headliner</span></li>
<li class="slot"><span class="slot__time">12:45 AM</span><span class="slot__act">Subfreq Collective</span><span class="slot__role">Late set</span></li>
</ol>
</section>
<!-- VENUE -->
<section class="card section" id="venue" aria-labelledby="venueH">
<div class="section__head"><h2 id="venueH">Venue & map</h2></div>
<div class="venue">
<div class="venue__map" role="img" aria-label="Stylized map of Aurora Riverside Grounds">
<span class="venue__pin" aria-hidden="true">◉</span>
<span class="venue__path" aria-hidden="true"></span>
</div>
<div class="venue__info">
<h3>Aurora Riverside Grounds</h3>
<p>1400 Marine Dr, Portland, OR 97203</p>
<dl class="kv">
<div><dt>Doors</dt><dd>4:00 PM</dd></div>
<div><dt>Capacity</dt><dd>12,000</dd></div>
<div><dt>Parking</dt><dd>Lot C · $25 prepaid</dd></div>
<div><dt>Transit</dt><dd>MAX Yellow Line → Expo</dd></div>
</dl>
<button class="btn btn--ghost btn--sm" type="button" id="dirBtn">Get directions</button>
</div>
</div>
</section>
<!-- FAQ -->
<section class="card section" id="faq" aria-labelledby="faqH">
<div class="section__head"><h2 id="faqH">Frequently asked</h2></div>
<div class="faq" id="faqList">
<details class="qa" open>
<summary>What's the refund policy?<span class="qa__icon" aria-hidden="true">+</span></summary>
<p>All sales are final, but tickets are 100% transferable up to 24 hours before doors via your order page.</p>
</details>
<details class="qa">
<summary>Is it all ages?<span class="qa__icon" aria-hidden="true">+</span></summary>
<p>The festival is 18+. Bring a valid government-issued photo ID — wristbands are issued at the gate.</p>
</details>
<details class="qa">
<summary>Can I bring a bag?<span class="qa__icon" aria-hidden="true">+</span></summary>
<p>Clear bags up to 12"×6"×12" are allowed. No outside food, glass, or professional cameras.</p>
</details>
<details class="qa">
<summary>What if it rains?<span class="qa__icon" aria-hidden="true">+</span></summary>
<p>The event is rain or shine. Most stages are covered; ponchos are sold on-site.</p>
</details>
</div>
</section>
</div>
<!-- ASIDE SUMMARY -->
<aside class="layout__side" aria-label="Order summary">
<div class="summary card">
<h2 class="summary__h">Your order</h2>
<ul class="summary__lines" id="summaryLines">
<li class="summary__empty">No tickets selected yet.</li>
</ul>
<div class="summary__totals">
<div class="row"><span>Subtotal</span><span id="subTotal">$0.00</span></div>
<div class="row row--muted"><span>Service fee (12%)</span><span id="feeTotal">$0.00</span></div>
<div class="row row--grand"><span>Total</span><span id="grandTotal">$0.00</span></div>
</div>
<button class="btn btn--brand btn--block" type="button" id="checkoutSide" disabled>Checkout</button>
</div>
</aside>
</div>
</main>
<!-- STICKY CHECKOUT BAR -->
<div class="sticky" id="stickyBar" hidden>
<div class="wrap sticky__inner">
<div class="sticky__sum">
<span class="sticky__qty" id="stickyQty">0 tickets</span>
<span class="sticky__total" id="stickyTotal">$0.00</span>
</div>
<button class="btn btn--brand" type="button" id="checkoutBar">Checkout</button>
</div>
</div>
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>Event Detail
A bold, photographic event page for the fictional Neon Pulse Festival. The hero pairs a gradient stage-lights backdrop with the headline date, venue and a ticking countdown to doors, plus festival and age badges. A “Get tickets” call to action drops you straight into the tier selector.
Three ticket tiers — General Admission, VIP Lounge and a sold-out Platinum Pit — each carry a color bar that matches the seat-tier legend, live stock counts, low-stock and sold-out badges, and a quantity stepper. Steppers update per-line subtotals in a running order summary (subtotal, 12% service fee, grand total), enforce a per-tier stock cap and an 8-ticket order limit, and reveal a sticky checkout bar the moment anything is selected. A small toast helper confirms reservations and other actions.
Below the fold, a tabbed lineup switches between the Main Stage and Electric Tent set times, a stylized venue map sits beside parking and transit details, and an FAQ accordion answers refund, age and bag policies. The layout reflows from a two-column desktop view down to a single column at ~360px.
Illustrative UI only — fictional events, not a real ticketing service.