Podcast — Landing Comedy
A bold, dark, audio-first landing page for a fictional comedy podcast. Features a glowing hero with a gradient waveform play button, a scrollable reel of clip cards with fake laugh-o-meter badges, a live-show tour list with sold-out states and ticket buttons, host bios, listener quotes, and a cheeky subscribe form. Playful energetic violet-to-cyan accents, funny copy, hover micro-interactions, keyboard-friendly controls, and a toast helper make it feel alive without any framework.
MCP
Code
:root {
--bg: #0c0c10;
--surface: #15151c;
--surface-2: #1d1d27;
--violet: #8b5cf6;
--violet-d: #7c3aed;
--cyan: #22d3ee;
--pink: #f472b6;
--ink: #f4f4f8;
--muted: #9a9aab;
--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;
--grad: linear-gradient(90deg, var(--violet), var(--cyan));
--glow: 0 0 0 1px var(--line), 0 18px 50px -20px rgba(139, 92, 246, 0.6);
}
* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
margin: 0;
background:
radial-gradient(900px 600px at 85% -10%, rgba(139, 92, 246, 0.20), transparent 60%),
radial-gradient(800px 500px at 0% 20%, rgba(34, 211, 238, 0.12), transparent 55%),
var(--bg);
color: var(--ink);
font-family: "Inter", system-ui, -apple-system, sans-serif;
line-height: 1.5;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
}
img { max-width: 100%; display: block; }
.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;
}
a { color: inherit; }
:focus-visible {
outline: 2px solid var(--cyan);
outline-offset: 3px;
border-radius: 4px;
}
/* ===== Nav ===== */
.nav {
position: sticky;
top: 0;
z-index: 50;
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
padding: 14px clamp(16px, 5vw, 56px);
background: rgba(12, 12, 16, 0.72);
backdrop-filter: blur(14px);
border-bottom: 1px solid var(--line);
}
.brand {
display: inline-flex;
align-items: center;
gap: 10px;
text-decoration: none;
font-weight: 800;
letter-spacing: -0.02em;
font-size: 18px;
}
.brand-mark { font-size: 22px; filter: drop-shadow(0 0 10px rgba(139, 92, 246, 0.6)); }
.brand-text em { font-style: normal; color: var(--muted); font-weight: 600; }
.nav-links { display: flex; align-items: center; gap: 6px; }
.nav-links a {
text-decoration: none;
color: var(--muted);
font-weight: 600;
font-size: 14px;
padding: 8px 12px;
border-radius: var(--r-sm);
transition: color .15s, background .15s;
}
.nav-links a:hover { color: var(--ink); background: var(--surface); }
.nav-cta {
color: var(--ink) !important;
background: var(--grad);
box-shadow: 0 8px 20px -8px rgba(139, 92, 246, 0.9);
}
.nav-cta:hover { filter: brightness(1.08); background: var(--grad) !important; }
/* ===== Hero ===== */
.hero {
display: grid;
grid-template-columns: 1.25fr 0.75fr;
gap: clamp(24px, 5vw, 64px);
align-items: center;
padding: clamp(40px, 7vw, 88px) clamp(16px, 5vw, 56px) clamp(30px, 5vw, 60px);
max-width: 1200px;
margin: 0 auto;
}
.eyebrow {
display: inline-flex;
align-items: center;
gap: 8px;
font-size: 13px;
font-weight: 600;
color: var(--cyan);
background: rgba(34, 211, 238, 0.10);
border: 1px solid rgba(34, 211, 238, 0.25);
padding: 6px 12px;
border-radius: 999px;
}
.eyebrow .dot {
width: 8px; height: 8px; border-radius: 50%;
background: var(--cyan);
box-shadow: 0 0 0 0 rgba(34, 211, 238, 0.6);
animation: pulse 1.8s infinite;
}
@keyframes pulse {
0% { box-shadow: 0 0 0 0 rgba(34, 211, 238, 0.55); }
70% { box-shadow: 0 0 0 8px rgba(34, 211, 238, 0); }
100% { box-shadow: 0 0 0 0 rgba(34, 211, 238, 0); }
}
.hero h1 {
margin: 18px 0 0;
font-size: clamp(34px, 6vw, 60px);
line-height: 1.02;
letter-spacing: -0.03em;
font-weight: 800;
}
.grad {
background: var(--grad);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
.lede {
margin: 18px 0 26px;
max-width: 52ch;
color: var(--muted);
font-size: clamp(15px, 1.7vw, 18px);
}
/* Player */
.player {
display: flex;
align-items: center;
gap: 16px;
background: var(--surface);
border: 1px solid var(--line);
border-radius: var(--r-lg);
padding: 14px 18px;
box-shadow: var(--glow);
}
.play-btn {
flex: none;
width: 58px;
height: 58px;
border-radius: 50%;
border: none;
cursor: pointer;
display: grid;
place-items: center;
color: var(--white);
background: var(--grad);
box-shadow: 0 0 24px -2px rgba(139, 92, 246, 0.75);
transition: transform .12s, box-shadow .2s;
}
.play-btn:hover { transform: scale(1.06); box-shadow: 0 0 34px 0 rgba(34, 211, 238, 0.6); }
.play-btn:active { transform: scale(0.96); }
.play-btn .ic-pause { display: none; }
.play-btn[aria-pressed="true"] .ic-play { display: none; }
.play-btn[aria-pressed="true"] .ic-pause { display: block; }
.player-meta { flex: 1; min-width: 0; }
.player-ep {
display: block;
font-weight: 700;
font-size: 14px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
margin-bottom: 8px;
}
.wave { display: flex; align-items: flex-end; gap: 3px; height: 26px; }
.wave span {
flex: 1;
min-width: 2px;
border-radius: 3px;
background: var(--grad);
height: 30%;
opacity: 0.55;
}
.player.playing .wave span { animation: bounce 1.1s ease-in-out infinite; opacity: 1; }
.wave span:nth-child(odd) { animation-delay: .15s; }
.wave span:nth-child(3n) { animation-delay: .3s; }
.wave span:nth-child(4n) { animation-delay: .45s; }
@keyframes bounce {
0%, 100% { height: 22%; }
50% { height: 100%; }
}
.player-time { flex: none; font-variant-numeric: tabular-nums; color: var(--muted); font-weight: 600; font-size: 14px; }
.hero-stats {
display: flex;
gap: 28px;
margin: 26px 0 22px;
flex-wrap: wrap;
}
.hero-stats div { display: flex; flex-direction: column; }
.hero-stats strong {
font-size: 24px;
font-weight: 800;
letter-spacing: -0.02em;
background: var(--grad);
-webkit-background-clip: text; background-clip: text; color: transparent;
}
.hero-stats span { font-size: 12.5px; color: var(--muted); }
.listen-on { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.listen-lbl { font-size: 13px; color: var(--muted); margin-right: 4px; }
.chip {
font: inherit;
font-size: 13px;
font-weight: 600;
color: var(--ink);
background: var(--surface-2);
border: 1px solid var(--line);
padding: 8px 14px;
border-radius: 999px;
cursor: pointer;
transition: border-color .15s, transform .12s, background .15s;
}
.chip:hover { border-color: var(--line-2); transform: translateY(-2px); background: var(--surface); }
/* Hero art */
.hero-art { position: relative; display: grid; place-items: center; min-height: 320px; }
.cover {
position: relative;
width: min(300px, 70%);
aspect-ratio: 1;
border-radius: 26px;
background: linear-gradient(150deg, var(--violet-d), #3b1d63 60%, var(--surface));
border: 1px solid var(--line-2);
display: grid;
place-items: center;
box-shadow: 0 40px 80px -30px rgba(139, 92, 246, 0.7);
overflow: hidden;
animation: floaty 6s ease-in-out infinite;
}
.cover-glow {
position: absolute; inset: -40% -10% auto -10%; height: 70%;
background: radial-gradient(60% 60% at 50% 0%, rgba(34, 211, 238, 0.5), transparent 70%);
}
.cover-emoji { font-size: 96px; filter: drop-shadow(0 8px 24px rgba(0,0,0,0.5)); }
.cover-tag {
position: absolute; top: 14px; left: 14px;
font-size: 11px; font-weight: 800; letter-spacing: 0.08em;
color: #0c0c10; background: var(--cyan);
padding: 4px 9px; border-radius: 999px;
text-transform: uppercase;
}
.float {
position: absolute;
font-size: 34px;
filter: drop-shadow(0 6px 14px rgba(0,0,0,0.5));
}
.f1 { top: 6%; right: 4%; animation: floaty 4.5s ease-in-out infinite; }
.f2 { bottom: 10%; left: 0%; animation: floaty 5.5s ease-in-out .4s infinite; }
.f3 { bottom: 2%; right: 10%; animation: floaty 5s ease-in-out .8s infinite; }
@keyframes floaty {
0%, 100% { transform: translateY(0) rotate(-2deg); }
50% { transform: translateY(-12px) rotate(3deg); }
}
/* ===== Sections ===== */
.section {
max-width: 1160px;
margin: 0 auto;
padding: clamp(40px, 6vw, 72px) clamp(16px, 5vw, 56px);
}
.section-head { margin-bottom: 28px; }
.section-head h2 {
margin: 0;
font-size: clamp(26px, 3.6vw, 38px);
letter-spacing: -0.025em;
font-weight: 800;
}
.section-head p { margin: 8px 0 0; color: var(--muted); font-size: 16px; }
/* Clips reel */
.reel {
display: grid;
grid-auto-flow: column;
grid-auto-columns: min(300px, 78vw);
gap: 18px;
overflow-x: auto;
padding: 6px 2px 20px;
scroll-snap-type: x mandatory;
scrollbar-width: thin;
scrollbar-color: var(--violet) transparent;
}
.reel::-webkit-scrollbar { height: 8px; }
.reel::-webkit-scrollbar-thumb { background: var(--violet-d); border-radius: 999px; }
.clip {
scroll-snap-align: start;
background: var(--surface);
border: 1px solid var(--line);
border-radius: var(--r-lg);
overflow: hidden;
display: flex;
flex-direction: column;
cursor: pointer;
transition: transform .16s, border-color .16s, box-shadow .2s;
text-align: left;
font: inherit;
color: inherit;
padding: 0;
}
.clip:hover { transform: translateY(-4px); border-color: var(--line-2); box-shadow: var(--glow); }
.clip-thumb {
position: relative;
aspect-ratio: 16 / 10;
display: grid;
place-items: center;
font-size: 52px;
}
.clip-thumb .dur {
position: absolute; bottom: 10px; right: 10px;
font-size: 12px; font-weight: 700;
background: rgba(0,0,0,0.6); color: var(--white);
padding: 3px 8px; border-radius: 999px;
font-variant-numeric: tabular-nums;
}
.clip-thumb .mini-play {
position: absolute; inset: 0; margin: auto;
width: 46px; height: 46px; border-radius: 50%;
background: rgba(12,12,16,0.55); backdrop-filter: blur(4px);
display: grid; place-items: center;
opacity: 0; transition: opacity .16s, transform .16s;
color: var(--white);
}
.clip:hover .mini-play, .clip:focus-visible .mini-play { opacity: 1; transform: scale(1.05); }
.clip-body { padding: 14px 16px 18px; }
.clip-body h3 { margin: 0 0 6px; font-size: 16px; font-weight: 700; letter-spacing: -0.01em; }
.clip-body p { margin: 0 0 12px; font-size: 13px; color: var(--muted); }
.laugh {
display: flex; align-items: center; gap: 8px;
font-size: 12px; font-weight: 700;
}
.laugh-bar { flex: 1; height: 6px; border-radius: 999px; background: var(--surface-2); overflow: hidden; }
.laugh-bar i { display: block; height: 100%; width: 0; border-radius: 999px; background: var(--grad); transition: width .8s cubic-bezier(.2,.7,.2,1); }
.laugh-val { color: var(--cyan); }
/* Tour */
.tour-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 12px; }
.tour-row {
display: grid;
grid-template-columns: 84px 1fr auto;
align-items: center;
gap: 18px;
background: var(--surface);
border: 1px solid var(--line);
border-radius: var(--r-md);
padding: 16px 20px;
transition: border-color .15s, transform .12s;
}
.tour-row:hover { border-color: var(--line-2); transform: translateX(3px); }
.tour-date {
text-align: center;
border-right: 1px solid var(--line);
padding-right: 12px;
}
.tour-date .m { display: block; font-size: 12px; font-weight: 700; color: var(--pink); text-transform: uppercase; letter-spacing: 0.06em; }
.tour-date .d { display: block; font-size: 24px; font-weight: 800; line-height: 1; }
.tour-info strong { display: block; font-size: 16px; font-weight: 700; }
.tour-info span { font-size: 13px; color: var(--muted); }
.tour-cta { justify-self: end; }
.btn-ticket {
font: inherit; font-weight: 700; font-size: 14px;
color: var(--ink);
background: var(--surface-2);
border: 1px solid var(--line-2);
padding: 10px 18px;
border-radius: var(--r-sm);
cursor: pointer;
transition: transform .12s, filter .15s, background .15s;
}
.btn-ticket:hover { transform: translateY(-2px); background: var(--grad); border-color: transparent; }
.sold {
font-size: 13px; font-weight: 800;
color: var(--muted);
text-transform: uppercase; letter-spacing: 0.06em;
border: 1px dashed var(--line-2);
padding: 9px 16px; border-radius: var(--r-sm);
}
.sold s { color: var(--pink); text-decoration-thickness: 2px; }
/* Hosts */
.hosts { display: grid; grid-template-columns: repeat(3, 1fr); gap: 18px; }
.host {
background: var(--surface);
border: 1px solid var(--line);
border-radius: var(--r-lg);
padding: 24px 22px;
transition: transform .16s, border-color .16s;
}
.host:hover { transform: translateY(-4px); border-color: var(--line-2); }
.host-avatar {
width: 60px; height: 60px; border-radius: 18px;
display: grid; place-items: center;
font-size: 26px; font-weight: 800; color: var(--white);
margin-bottom: 16px;
}
.av-dev { background: linear-gradient(135deg, var(--violet), var(--violet-d)); }
.av-marnie { background: linear-gradient(135deg, var(--cyan), #0891b2); color: #062a30; }
.av-pete { background: linear-gradient(135deg, var(--pink), #db2777); }
.host h3 { margin: 0; font-size: 19px; font-weight: 800; letter-spacing: -0.01em; }
.host-role { display: block; font-size: 13px; color: var(--cyan); font-weight: 600; margin: 4px 0 10px; }
.host p { margin: 0; font-size: 14px; color: var(--muted); }
/* Quotes */
.quotes { border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); }
.quote-wall {
columns: 2;
column-gap: 18px;
}
.quote-wall blockquote {
break-inside: avoid;
margin: 0 0 18px;
background: var(--surface);
border: 1px solid var(--line);
border-left: 3px solid var(--violet);
border-radius: var(--r-md);
padding: 18px 20px;
font-size: 16px;
font-weight: 500;
}
.quote-wall cite { display: block; margin-top: 10px; font-style: normal; font-size: 13px; color: var(--muted); font-weight: 600; }
/* Subscribe */
.subscribe { padding-bottom: clamp(50px, 8vw, 90px); }
.sub-card {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 32px;
align-items: center;
background:
radial-gradient(600px 300px at 100% 0%, rgba(34, 211, 238, 0.18), transparent 60%),
var(--surface);
border: 1px solid var(--line-2);
border-radius: var(--r-lg);
padding: clamp(26px, 4vw, 44px);
box-shadow: var(--glow);
}
.sub-copy h2 { margin: 0 0 10px; font-size: clamp(24px, 3vw, 32px); letter-spacing: -0.025em; }
.sub-copy p { margin: 0; color: var(--muted); font-size: 15px; }
.sub-form { display: flex; flex-wrap: wrap; gap: 12px; }
.sub-form input {
flex: 1 1 220px;
font: inherit;
color: var(--ink);
background: var(--bg);
border: 1px solid var(--line-2);
border-radius: var(--r-sm);
padding: 13px 16px;
}
.sub-form input::placeholder { color: #6b6b7d; }
.sub-form input:focus { outline: 2px solid var(--cyan); outline-offset: 1px; }
.sub-form input.invalid { border-color: var(--pink); }
.btn-primary {
font: inherit; font-weight: 700;
color: var(--white);
background: var(--grad);
border: none;
border-radius: var(--r-sm);
padding: 13px 22px;
cursor: pointer;
box-shadow: 0 10px 26px -10px rgba(139, 92, 246, 0.9);
transition: transform .12s, filter .15s;
}
.btn-primary:hover { transform: translateY(-2px); filter: brightness(1.08); }
.btn-primary:active { transform: translateY(0); }
.sub-err { flex-basis: 100%; margin: 0; font-size: 13px; font-weight: 600; color: var(--pink); min-height: 18px; }
.sub-err.ok { color: var(--cyan); }
/* Footer */
.foot {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
flex-wrap: wrap;
max-width: 1160px;
margin: 0 auto;
padding: 26px clamp(16px, 5vw, 56px) 40px;
border-top: 1px solid var(--line);
color: var(--muted);
font-size: 13px;
}
.foot-links { display: flex; gap: 18px; }
.foot-links a { text-decoration: none; font-weight: 600; }
.foot-links a:hover { color: var(--ink); }
/* Toast */
.toast {
position: fixed;
left: 50%;
bottom: 26px;
transform: translate(-50%, 24px);
background: var(--surface-2);
border: 1px solid var(--line-2);
color: var(--ink);
font-weight: 600;
font-size: 14px;
padding: 12px 20px;
border-radius: 999px;
box-shadow: 0 20px 50px -18px rgba(0,0,0,0.8), 0 0 0 1px rgba(139,92,246,0.3);
opacity: 0;
pointer-events: none;
transition: opacity .22s, transform .22s;
z-index: 100;
max-width: min(90vw, 420px);
text-align: center;
}
.toast.show { opacity: 1; transform: translate(-50%, 0); }
/* ===== Responsive ===== */
@media (max-width: 900px) {
.hero { grid-template-columns: 1fr; }
.hero-art { order: -1; min-height: 240px; }
.hosts { grid-template-columns: 1fr; }
.sub-card { grid-template-columns: 1fr; }
.quote-wall { columns: 1; }
}
@media (max-width: 520px) {
.nav-links a:not(.nav-cta) { display: none; }
.hero { padding-top: 28px; }
.hero h1 { font-size: 32px; }
.player { flex-wrap: wrap; padding: 12px 14px; gap: 12px; }
.player-time { order: 3; }
.hero-stats { gap: 18px; }
.hero-stats strong { font-size: 20px; }
.tour-row { grid-template-columns: 64px 1fr; }
.tour-cta { grid-column: 1 / -1; justify-self: stretch; }
.btn-ticket, .sold { display: block; width: 100%; text-align: center; }
.section { padding-top: 36px; padding-bottom: 36px; }
.foot { flex-direction: column; align-items: flex-start; }
}
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after { animation-duration: 0.001ms !important; animation-iteration-count: 1 !important; transition-duration: 0.001ms !important; scroll-behavior: auto !important; }
}(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");
}, 2600);
}
/* ---------- Data ---------- */
var EPISODES = [
'EP. 214 — “The Great Cereal Debate”',
'EP. 213 — “Is a Hot Dog a Taco?”',
'EP. 212 — “Haunted IKEA, Part 4”',
'EP. 211 — “Pigeons: Government Drones?”'
];
var CLIPS = [
{ emoji: "🥣", title: "Cereal is legally soup", desc: "Dev builds an airtight, deeply wrong legal case.", dur: "0:38", laugh: 96 },
{ emoji: "👻", title: "The haunted meatball", desc: "Marnie screams. Pete drops the mic. Chaos.", dur: "0:52", laugh: 88 },
{ emoji: "🐦", title: "Pigeon surveillance state", desc: "A conspiracy theory that goes way too far.", dur: "1:04", laugh: 92 },
{ emoji: "🌭", title: "Taco or hot dog?", desc: "45 minutes of geometry nobody asked for.", dur: "0:41", laugh: 84 },
{ emoji: "🎸", title: "Dev's terrible jingle", desc: "He wrote a theme song. It has one chord.", dur: "0:29", laugh: 79 }
];
var TOUR = [
{ m: "Jul", d: "18", city: "Austin, TX", venue: "The Laughing Armadillo", status: "open" },
{ m: "Jul", d: "26", city: "Chicago, IL", venue: "Midway Comedy Hall", status: "few" },
{ m: "Aug", d: "03", city: "Brooklyn, NY", venue: "The Basement Room", status: "sold" },
{ m: "Aug", d: "15", city: "Denver, CO", venue: "Altitude Theater", status: "open" },
{ m: "Aug", d: "22", city: "Portland, OR", venue: "The Damp Barn", status: "few" }
];
/* ---------- Player toggle ---------- */
var playBtn = document.getElementById("playBtn");
var player = document.querySelector(".player");
var nowEp = document.getElementById("nowEp");
var epIndex = 0;
var playing = false;
function setPlaying(state) {
playing = state;
playBtn.setAttribute("aria-pressed", String(state));
playBtn.setAttribute("aria-label", state ? "Pause episode" : "Play latest episode");
player.classList.toggle("playing", state);
if (state) {
toast("▶ Now playing — " + nowEp.textContent.replace(/EP\. \d+ — /, ""));
} else {
toast("⏸ Paused. The silence is deafening.");
}
}
if (playBtn) {
playBtn.addEventListener("click", function () {
setPlaying(!playing);
});
}
// Clicking the episode title cycles to the next episode
if (nowEp) {
nowEp.style.cursor = "pointer";
nowEp.title = "Click for another episode";
nowEp.addEventListener("click", function () {
epIndex = (epIndex + 1) % EPISODES.length;
nowEp.textContent = EPISODES[epIndex];
if (playing) toast("⏭ Skipped to — " + EPISODES[epIndex].replace(/EP\. \d+ — /, ""));
});
}
/* ---------- Listen chips ---------- */
document.querySelectorAll(".chip[data-app]").forEach(function (chip) {
chip.addEventListener("click", function () {
toast("Opening " + chip.getAttribute("data-app") + "… (pretend it worked)");
});
});
/* ---------- Build clip reel ---------- */
var reel = document.getElementById("reel");
if (reel) {
CLIPS.forEach(function (c, i) {
var card = document.createElement("button");
card.className = "clip";
card.setAttribute("role", "listitem");
card.setAttribute("aria-label", "Play clip: " + c.title);
card.innerHTML =
'<div class="clip-thumb" style="background:linear-gradient(150deg, hsl(' +
(255 - i * 26) + ' 70% 22%), hsl(' + (190 + i * 8) + ' 65% 16%))">' +
'<span aria-hidden="true">' + c.emoji + "</span>" +
'<span class="mini-play"><svg viewBox="0 0 24 24" width="22" height="22" aria-hidden="true"><path d="M8 5v14l11-7z" fill="currentColor"/></svg></span>' +
'<span class="dur">' + c.dur + "</span>" +
"</div>" +
'<div class="clip-body">' +
"<h3>" + c.title + "</h3>" +
"<p>" + c.desc + "</p>" +
'<div class="laugh"><span aria-hidden="true">😂</span>' +
'<span class="laugh-bar"><i data-fill="' + c.laugh + '"></i></span>' +
'<span class="laugh-val">' + c.laugh + "%</span></div>" +
"</div>";
card.addEventListener("click", function () {
toast("🎧 Clip: “" + c.title + "” — laugh-o-meter says " + c.laugh + "%");
});
reel.appendChild(card);
});
}
/* ---------- Laugh meter animation on view ---------- */
function fillMeters() {
document.querySelectorAll(".laugh-bar i[data-fill]").forEach(function (el) {
el.style.width = el.getAttribute("data-fill") + "%";
});
}
if ("IntersectionObserver" in window && reel) {
var io = new IntersectionObserver(function (entries, obs) {
entries.forEach(function (e) {
if (e.isIntersecting) { fillMeters(); obs.disconnect(); }
});
}, { threshold: 0.2 });
io.observe(reel);
} else {
fillMeters();
}
/* ---------- Build tour list ---------- */
var tourList = document.getElementById("tourList");
if (tourList) {
TOUR.forEach(function (t) {
var li = document.createElement("li");
li.className = "tour-row";
var cta;
if (t.status === "sold") {
cta = '<span class="sold"><s>Get Tickets</s> Sold out</span>';
} else {
var label = t.status === "few" ? "Few left 🔥" : "Get Tickets →";
cta = '<button class="btn-ticket" type="button" data-city="' + t.city + '">' + label + "</button>";
}
li.innerHTML =
'<div class="tour-date"><span class="m">' + t.m + '</span><span class="d">' + t.d + "</span></div>" +
'<div class="tour-info"><strong>' + t.city + "</strong><span>" + t.venue + "</span></div>" +
'<div class="tour-cta">' + cta + "</div>";
tourList.appendChild(li);
});
tourList.addEventListener("click", function (e) {
var btn = e.target.closest(".btn-ticket");
if (!btn) return;
toast("🎟️ Tickets for " + btn.getAttribute("data-city") + " added to cart!");
});
}
/* ---------- Subscribe form ---------- */
var form = document.getElementById("subForm");
var email = document.getElementById("email");
var err = document.getElementById("subErr");
var PUNS = [
"You're in! Prepare for pun-ishment.",
"Subscribed! Your inbox will never be the same.",
"Welcome aboard, you beautiful weirdo."
];
if (form) {
form.addEventListener("submit", function (e) {
e.preventDefault();
var val = (email.value || "").trim();
var valid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(val);
err.classList.remove("ok");
if (!valid) {
email.classList.add("invalid");
email.setAttribute("aria-invalid", "true");
err.textContent = "That email looks as fake as our facts. Try again?";
email.focus();
return;
}
email.classList.remove("invalid");
email.removeAttribute("aria-invalid");
var msg = PUNS[Math.floor(Math.random() * PUNS.length)];
err.textContent = msg;
err.classList.add("ok");
toast("✅ " + msg);
email.value = "";
});
email.addEventListener("input", function () {
email.classList.remove("invalid");
err.textContent = "";
err.classList.remove("ok");
});
}
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Two Idiots and a Microphone — The Comedy Podcast</title>
<meta name="description" content="A weekly comedy podcast where two idiots argue about nothing and somehow make it your problem. New episodes every Thursday." />
<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>
<!-- ===== Nav ===== -->
<header class="nav">
<a class="brand" href="#top" aria-label="Two Idiots and a Microphone home">
<span class="brand-mark" aria-hidden="true">🎙️</span>
<span class="brand-text">Two Idiots <em>& a Mic</em></span>
</a>
<nav class="nav-links" aria-label="Primary">
<a href="#clips">Clips</a>
<a href="#tour">Live Shows</a>
<a href="#hosts">The Idiots</a>
<a href="#subscribe" class="nav-cta">Subscribe</a>
</nav>
</header>
<main id="top">
<!-- ===== Hero ===== -->
<section class="hero">
<div class="hero-copy">
<span class="eyebrow"><span class="dot"></span> New episode every Thursday</span>
<h1>Two idiots. One mic.<br /><span class="grad">Zero good decisions.</span></h1>
<p class="lede">
A weekly comedy podcast where Dev and Marnie argue about hot dogs,
haunted IKEAs, and whether pigeons are government drones. Somehow it's
a top-10 show. We're as confused as you are.
</p>
<div class="player" role="group" aria-label="Episode player">
<button id="playBtn" class="play-btn" aria-pressed="false" aria-label="Play latest episode">
<svg class="ic-play" viewBox="0 0 24 24" width="26" height="26" aria-hidden="true"><path d="M8 5v14l11-7z" fill="currentColor"/></svg>
<svg class="ic-pause" viewBox="0 0 24 24" width="26" height="26" aria-hidden="true"><path d="M6 5h4v14H6zM14 5h4v14h-4z" fill="currentColor"/></svg>
</button>
<div class="player-meta">
<span class="player-ep" id="nowEp">EP. 214 — “The Great Cereal Debate”</span>
<div class="wave" id="wave" aria-hidden="true">
<span></span><span></span><span></span><span></span><span></span>
<span></span><span></span><span></span><span></span><span></span>
<span></span><span></span><span></span><span></span><span></span>
<span></span><span></span><span></span><span></span><span></span>
</div>
</div>
<span class="player-time" id="playTime">54:12</span>
</div>
<div class="hero-stats">
<div><strong>4.2M</strong><span>monthly listens</span></div>
<div><strong>214</strong><span>episodes of chaos</span></div>
<div><strong>4.9★</strong><span>on every app</span></div>
</div>
<div class="listen-on" aria-label="Available on">
<span class="listen-lbl">Listen on</span>
<button class="chip" data-app="Apple Podcasts">Apple</button>
<button class="chip" data-app="Spotify">Spotify</button>
<button class="chip" data-app="YouTube">YouTube</button>
<button class="chip" data-app="Overcast">Overcast</button>
</div>
</div>
<aside class="hero-art" aria-hidden="true">
<div class="cover">
<div class="cover-glow"></div>
<span class="cover-emoji">🎙️</span>
<span class="cover-tag">LIVE-ish</span>
</div>
<div class="float f1">🌭</div>
<div class="float f2">👽</div>
<div class="float f3">🐦</div>
</aside>
</section>
<!-- ===== Clips ===== -->
<section id="clips" class="section">
<div class="section-head">
<h2>Clips that got us in trouble</h2>
<p>Thirty-second reasons your coworkers think you're weird.</p>
</div>
<div class="reel" id="reel" role="list">
<!-- cards injected by JS, with static fallbacks below -->
</div>
</section>
<!-- ===== Tour ===== -->
<section id="tour" class="section tour">
<div class="section-head">
<h2>See us fail live</h2>
<p>We take the nonsense on the road. Sometimes we remember the punchlines.</p>
</div>
<ul class="tour-list" id="tourList"></ul>
</section>
<!-- ===== Hosts ===== -->
<section id="hosts" class="section">
<div class="section-head">
<h2>Meet the idiots</h2>
<p>Legally we have to call them "hosts."</p>
</div>
<div class="hosts">
<article class="host">
<div class="host-avatar av-dev">D</div>
<h3>Dev Okafor</h3>
<span class="host-role">The confident one who is usually wrong</span>
<p>Once argued for 40 minutes that cereal is a soup. Owns 14 hoodies, all the same.</p>
</article>
<article class="host">
<div class="host-avatar av-marnie">M</div>
<h3>Marnie Vasquez</h3>
<span class="host-role">The chaos gremlin with the good notes</span>
<p>Keeps a spreadsheet of Dev's worst takes. It has 900 rows. She's very proud.</p>
</article>
<article class="host">
<div class="host-avatar av-pete">P</div>
<h3>Producer Pete</h3>
<span class="host-role">Presses buttons, sighs a lot</span>
<p>Technically not allowed to talk. Talks constantly. The real star, honestly.</p>
</article>
</div>
</section>
<!-- ===== Quotes ===== -->
<section class="section quotes">
<div class="section-head">
<h2>The reviews are in</h2>
<p>Real* listeners. (*legally fictional)</p>
</div>
<div class="quote-wall">
<blockquote>“I laughed so hard I got a noise complaint. From myself.” <cite>— @sourdough_greg</cite></blockquote>
<blockquote>“Missed my bus stop three weeks in a row. Worth it.” <cite>— transit_menace</cite></blockquote>
<blockquote>“My therapist asked me to stop quoting this show. I didn't.” <cite>— Aisha R.</cite></blockquote>
<blockquote>“Better than my actual friends and cheaper too.” <cite>— lonelyoncall</cite></blockquote>
</div>
</section>
<!-- ===== Subscribe ===== -->
<section id="subscribe" class="section subscribe">
<div class="sub-card">
<div class="sub-copy">
<h2>Get the nonsense in your inbox</h2>
<p>Bonus clips, tour presale codes, and one (1) terrible pun per week. Unsubscribe whenever, we'll cry quietly.</p>
</div>
<form class="sub-form" id="subForm" novalidate>
<label class="sr-only" for="email">Email address</label>
<input id="email" type="email" name="email" placeholder="[email protected]" autocomplete="email" />
<button type="submit" class="btn-primary">Sign me up →</button>
<p class="sub-err" id="subErr" role="alert" aria-live="polite"></p>
</form>
</div>
</section>
</main>
<footer class="foot">
<span>© 2026 Two Idiots and a Microphone. No idiots were harmed.</span>
<div class="foot-links">
<a href="#top">Back to top</a>
<a href="#tour">Tour</a>
<a href="#subscribe">Subscribe</a>
</div>
</footer>
<div id="toast" class="toast" role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>A single-page landing site for Two Idiots and a Microphone, a fictional weekly comedy podcast. The dark, audio-first layout opens on a hero with an animated gradient waveform and a glowing play button that toggles a mock “now playing” state, cycles through episode titles, and fires a toast. Everything is vanilla HTML, CSS, and JavaScript — no build step, no dependencies.
Below the hero, a horizontal clip reel shows the funniest moments as cards, each with a duration pill and an animated laugh-o-meter badge. A live tour section lists upcoming show dates with city, venue, and either a Get Tickets button or a struck-through Sold Out badge; clicking a ticket button fires a celebratory toast. Host bios, a wall of listener one-liners, and a subscribe form with inline validation round it out.
Interactions are keyboard-accessible with visible focus rings, respect prefers-reduced-motion, and the whole thing collapses cleanly to a single column at ~360px. The script wires the play toggle, clip previews, the ticket buttons, the subscribe form, and a small toast() helper used throughout.