Podcast — Landing Interview
A bold, audio-first landing page for a long-form interview podcast, pairing a host-and-guest hero with a glowing waveform play button, a notable-guests grid, a filterable latest-episodes list, and a subscribe rail. Vibrant violet-to-cyan accents, dark glassy cards, animated waveform bars, and a mock mini-player make the show feel alive before a single episode plays.
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.1);
--line-2: rgba(255, 255, 255, 0.18);
--white: #fff;
--r-sm: 8px;
--r-md: 14px;
--r-lg: 20px;
--grad: linear-gradient(100deg, var(--violet), var(--cyan));
}
* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
margin: 0;
background: 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;
overflow-x: hidden;
}
h1, h2, h3 { margin: 0; letter-spacing: -0.02em; line-height: 1.1; }
p { margin: 0; }
a { color: inherit; text-decoration: none; }
img { max-width: 100%; }
:focus-visible {
outline: 2px solid var(--cyan);
outline-offset: 3px;
border-radius: var(--r-sm);
}
/* ambient glows */
.glow {
position: fixed;
width: 46vw;
height: 46vw;
border-radius: 50%;
filter: blur(90px);
opacity: 0.28;
pointer-events: none;
z-index: 0;
}
.glow-a { top: -14vw; left: -8vw; background: var(--violet); }
.glow-b { top: 30vw; right: -12vw; background: var(--cyan); opacity: 0.18; }
/* nav */
.nav {
position: sticky;
top: 0;
z-index: 40;
display: flex;
align-items: center;
gap: 20px;
padding: 16px clamp(18px, 5vw, 56px);
background: rgba(12, 12, 16, 0.72);
backdrop-filter: blur(14px);
border-bottom: 1px solid var(--line);
}
.brand { display: flex; align-items: center; gap: 11px; font-weight: 800; }
.brand-mark {
display: inline-flex;
align-items: flex-end;
gap: 3px;
height: 22px;
}
.brand-mark span {
width: 4px;
border-radius: 3px;
background: var(--grad);
animation: bounce 1s ease-in-out infinite;
}
.brand-mark span:nth-child(1) { height: 8px; animation-delay: 0s; }
.brand-mark span:nth-child(2) { height: 20px; animation-delay: 0.15s; }
.brand-mark span:nth-child(3) { height: 12px; animation-delay: 0.3s; }
.brand-mark span:nth-child(4) { height: 16px; animation-delay: 0.45s; }
@keyframes bounce { 0%, 100% { transform: scaleY(0.5); } 50% { transform: scaleY(1); } }
.brand-name { font-size: 16px; }
.nav-links { margin-left: auto; display: flex; gap: 26px; }
.nav-links a { color: var(--muted); font-weight: 500; font-size: 14px; transition: color 0.15s; }
.nav-links a:hover { color: var(--ink); }
.nav-cta { padding: 9px 18px; }
/* buttons */
.btn {
font-family: inherit;
font-weight: 600;
font-size: 15px;
border: 0;
border-radius: 999px;
cursor: pointer;
color: var(--ink);
display: inline-flex;
align-items: center;
gap: 10px;
transition: transform 0.15s, box-shadow 0.2s, background 0.2s, border-color 0.2s;
}
.btn:active { transform: translateY(1px) scale(0.99); }
.btn-ghost {
background: var(--surface-2);
border: 1px solid var(--line-2);
padding: 12px 20px;
}
.btn-ghost:hover { background: #26263200; border-color: var(--cyan); background: var(--surface); }
.btn-play {
background: var(--grad);
color: #0b0b12;
padding: 10px 22px 10px 14px;
box-shadow: 0 8px 30px -8px rgba(139, 92, 246, 0.7);
}
.btn-play:hover { box-shadow: 0 12px 40px -6px rgba(34, 211, 238, 0.65); transform: translateY(-2px); }
.btn-sm { padding: 11px 20px; font-size: 14px; }
.play-ico {
width: 34px; height: 34px; flex: none;
border-radius: 50%;
background: rgba(11, 11, 18, 0.16);
display: grid; place-items: center;
}
.play-ico::before {
content: ""; width: 0; height: 0;
border-left: 11px solid #0b0b12;
border-top: 7px solid transparent;
border-bottom: 7px solid transparent;
margin-left: 3px;
}
.play-text { display: flex; flex-direction: column; text-align: left; line-height: 1.15; }
.play-text small { font-size: 11px; font-weight: 500; opacity: 0.72; }
.wave-ico {
width: 16px; height: 14px; display: inline-block;
background:
repeating-linear-gradient(90deg, currentColor 0 2px, transparent 2px 5px);
-webkit-mask: repeating-linear-gradient(90deg, #000 0 2px, transparent 2px 5px);
color: var(--cyan);
clip-path: polygon(0 40%, 20% 40%, 20% 0, 40% 0, 40% 60%, 60% 60%, 60% 20%, 80% 20%, 80% 80%, 100% 80%, 100% 100%, 0 100%);
background: currentColor;
}
/* hero */
main { position: relative; z-index: 1; padding: 0 clamp(18px, 5vw, 56px) 120px; }
.hero {
display: grid;
grid-template-columns: 1.05fr 0.95fr;
gap: clamp(28px, 5vw, 64px);
align-items: center;
padding: clamp(48px, 8vw, 96px) 0 clamp(40px, 6vw, 72px);
max-width: 1180px;
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.08);
border: 1px solid rgba(34, 211, 238, 0.25);
padding: 6px 14px; border-radius: 999px;
}
.eyebrow .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--cyan); box-shadow: 0 0 10px var(--cyan); }
.hero h1 {
font-size: clamp(34px, 5.4vw, 60px);
font-weight: 800;
margin: 20px 0 18px;
max-width: 15ch;
}
.lede { color: var(--muted); font-size: clamp(15px, 1.7vw, 18px); max-width: 46ch; }
.lede strong { color: var(--ink); }
.hero-actions { display: flex; flex-wrap: wrap; gap: 14px; margin: 30px 0 34px; }
.stats { display: flex; gap: 34px; margin: 0; padding-top: 26px; border-top: 1px solid var(--line); }
.stats dt { color: var(--muted); font-size: 12px; text-transform: uppercase; letter-spacing: 0.06em; margin-bottom: 4px; }
.stats dd { margin: 0; font-size: 24px; font-weight: 800; }
.stats dd { background: var(--grad); -webkit-background-clip: text; background-clip: text; color: transparent; }
/* hero card */
.hero-card {
background: linear-gradient(160deg, var(--surface-2), var(--surface));
border: 1px solid var(--line);
border-radius: var(--r-lg);
padding: 22px;
box-shadow: 0 30px 80px -30px rgba(0, 0, 0, 0.8);
position: relative;
overflow: hidden;
}
.hero-card::before {
content: ""; position: absolute; inset: 0;
background: radial-gradient(120% 90% at 80% 0%, rgba(139, 92, 246, 0.22), transparent 60%);
pointer-events: none;
}
.hero-visual { position: relative; border-radius: var(--r-md); padding: 26px 18px 30px; overflow: hidden; }
.waveform {
position: absolute; inset: 0;
display: flex; align-items: center; justify-content: center;
gap: 4px; padding: 0 14px;
opacity: 0.5;
}
.waveform i {
flex: 1;
max-width: 7px;
border-radius: 6px;
background: var(--grad);
height: 20%;
transition: height 0.18s ease;
}
.pair { position: relative; display: flex; align-items: center; justify-content: center; gap: 8px; z-index: 1; }
.pair-x { color: var(--muted); font-weight: 700; font-size: 20px; }
.avatar { margin: 0; text-align: center; }
.ava {
width: 92px; height: 92px; border-radius: 24px;
display: grid; place-items: center;
font-size: 28px; font-weight: 800; color: #0b0b12;
background: linear-gradient(150deg, var(--g1), var(--g2));
box-shadow: 0 12px 30px -8px rgba(0, 0, 0, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.25);
border: 2px solid rgba(255, 255, 255, 0.12);
transition: transform 0.4s;
}
.avatar figcaption { margin-top: 10px; font-weight: 700; font-size: 14px; display: flex; flex-direction: column; }
.avatar figcaption small { color: var(--muted); font-weight: 500; font-size: 12px; }
#featGuest .ava { animation: swap 0.5s ease; }
@keyframes swap { from { transform: scale(0.85) rotate(-4deg); opacity: 0.4; } to { transform: none; opacity: 1; } }
.hero-card-foot { display: flex; align-items: center; justify-content: space-between; margin-top: 16px; }
.badge {
display: inline-flex; align-items: center; gap: 7px;
font-size: 12px; font-weight: 600; padding: 6px 12px;
border-radius: 999px; background: var(--surface-2); border: 1px solid var(--line);
}
.badge-live { color: var(--pink); }
.pulse { width: 7px; height: 7px; border-radius: 50%; background: var(--pink); box-shadow: 0 0 0 0 var(--pink); animation: pulse 1.8s infinite; }
@keyframes pulse { 0% { box-shadow: 0 0 0 0 rgba(244, 114, 182, 0.6); } 70% { box-shadow: 0 0 0 10px rgba(244, 114, 182, 0); } 100% { box-shadow: 0 0 0 0 rgba(244, 114, 182, 0); } }
.chip-btn {
font-family: inherit; font-size: 12px; font-weight: 600;
color: var(--muted); background: transparent;
border: 1px solid var(--line); border-radius: 999px;
padding: 6px 12px; cursor: pointer; transition: 0.15s;
}
.chip-btn:hover, .chip-btn[aria-pressed="true"] { color: var(--ink); border-color: var(--cyan); }
/* blocks */
.block { max-width: 1180px; margin: 0 auto; padding-top: clamp(48px, 7vw, 88px); }
.block-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 20px; flex-wrap: wrap; margin-bottom: 26px; }
.block h2 { font-size: clamp(24px, 3.4vw, 34px); font-weight: 800; }
.sub { color: var(--muted); margin-top: 6px; font-size: 15px; }
/* guests */
.guest-grid {
list-style: none; margin: 0; padding: 0;
display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px;
}
.guest-card {
background: var(--surface); border: 1px solid var(--line);
border-radius: var(--r-md); padding: 22px 18px;
text-align: center; position: relative; overflow: hidden;
transition: transform 0.2s, border-color 0.2s, box-shadow 0.2s;
}
.guest-card:hover {
transform: translateY(-5px);
border-color: var(--line-2);
box-shadow: 0 22px 50px -24px rgba(139, 92, 246, 0.5);
}
.guest-card .ava { width: 72px; height: 72px; border-radius: 20px; font-size: 22px; margin: 0 auto; }
.g-name { font-weight: 700; margin-top: 14px; font-size: 15px; }
.g-role { color: var(--muted); font-size: 13px; margin-top: 3px; }
.g-tag {
display: inline-block; margin-top: 12px;
font-size: 11px; font-weight: 600; letter-spacing: 0.03em;
padding: 4px 10px; border-radius: 999px;
color: var(--cyan); background: rgba(34, 211, 238, 0.09);
border: 1px solid rgba(34, 211, 238, 0.22);
}
/* filters */
.filters { display: flex; gap: 8px; flex-wrap: wrap; }
.chip {
font-family: inherit; font-size: 13px; font-weight: 600;
color: var(--muted); background: var(--surface);
border: 1px solid var(--line); border-radius: 999px;
padding: 8px 16px; cursor: pointer; transition: 0.15s;
}
.chip:hover { color: var(--ink); border-color: var(--line-2); }
.chip.is-active { color: #0b0b12; background: var(--grad); border-color: transparent; }
/* episodes */
.ep-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 12px; }
.ep {
display: grid;
grid-template-columns: auto 1fr auto;
align-items: center;
gap: 18px;
background: var(--surface);
border: 1px solid var(--line);
border-radius: var(--r-md);
padding: 16px 20px;
transition: transform 0.18s, border-color 0.18s, background 0.18s;
}
.ep:hover { border-color: var(--line-2); background: var(--surface-2); transform: translateX(3px); }
.ep.is-playing { border-color: var(--violet); box-shadow: 0 0 0 1px var(--violet) inset, 0 18px 40px -22px rgba(139, 92, 246, 0.7); }
.ep-play {
width: 46px; height: 46px; flex: none;
border-radius: 50%; cursor: pointer;
border: 1px solid var(--line-2);
background: var(--surface-2);
display: grid; place-items: center;
transition: 0.18s;
}
.ep-play:hover { background: var(--grad); border-color: transparent; }
.ep-play::before {
content: ""; width: 0; height: 0; margin-left: 3px;
border-left: 12px solid var(--ink);
border-top: 8px solid transparent;
border-bottom: 8px solid transparent;
transition: border-left-color 0.18s;
}
.ep-play:hover::before { border-left-color: #0b0b12; }
.ep.is-playing .ep-play { background: var(--grad); border-color: transparent; }
.ep.is-playing .ep-play::before {
border: 0; width: 12px; height: 12px; margin: 0;
background: #0b0b12; border-radius: 2px;
}
.ep-meta { min-width: 0; }
.ep-top { display: flex; align-items: center; gap: 10px; margin-bottom: 4px; flex-wrap: wrap; }
.ep-num { font-size: 12px; font-weight: 700; color: var(--violet); }
.ep-season { font-size: 11px; font-weight: 600; color: var(--muted); background: var(--surface-2); border: 1px solid var(--line); padding: 2px 8px; border-radius: 999px; }
.ep-title { font-weight: 700; font-size: 16px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ep-guest { color: var(--muted); font-size: 13px; margin-top: 3px; }
.ep-guest b { color: var(--cyan); font-weight: 600; }
.ep-dur {
font-size: 13px; font-weight: 600; color: var(--muted);
background: var(--surface-2); border: 1px solid var(--line);
padding: 8px 14px; border-radius: 999px; white-space: nowrap;
}
/* subscribe */
.subscribe { }
.sub-card {
display: grid; grid-template-columns: 1.1fr 1fr; gap: clamp(28px, 5vw, 60px);
align-items: center;
background: linear-gradient(150deg, var(--surface-2), var(--surface));
border: 1px solid var(--line);
border-radius: var(--r-lg);
padding: clamp(28px, 5vw, 52px);
position: relative; overflow: hidden;
}
.sub-card::after {
content: ""; position: absolute; right: -10%; top: -30%;
width: 340px; height: 340px; border-radius: 50%;
background: radial-gradient(circle, rgba(34, 211, 238, 0.22), transparent 65%);
pointer-events: none;
}
.platforms { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 22px; }
.plat {
font-family: inherit; font-size: 14px; font-weight: 600;
color: var(--ink); background: var(--surface);
border: 1px solid var(--line-2); border-radius: 999px;
padding: 10px 20px; cursor: pointer; transition: 0.15s;
}
.plat:hover { border-color: var(--violet); background: var(--surface-2); transform: translateY(-2px); }
.news { position: relative; z-index: 1; }
.news label { display: block; font-weight: 700; margin-bottom: 10px; }
.news-row { display: flex; gap: 10px; }
.news input {
flex: 1; min-width: 0;
font-family: inherit; font-size: 15px; color: var(--ink);
background: var(--bg); border: 1px solid var(--line-2);
border-radius: 999px; padding: 12px 18px; transition: border-color 0.15s;
}
.news input::placeholder { color: var(--muted); }
.news input:focus-visible { outline: none; border-color: var(--cyan); }
.news input.invalid { border-color: var(--pink); }
.err { color: var(--pink); font-size: 13px; margin-top: 8px; }
.hint { color: var(--muted); font-size: 12px; margin-top: 10px; }
/* footer */
.foot {
position: relative; z-index: 1;
max-width: 1180px; margin: 0 auto;
padding: 30px clamp(18px, 5vw, 56px) 40px;
display: flex; justify-content: space-between; gap: 16px; flex-wrap: wrap;
color: var(--muted); font-size: 13px;
border-top: 1px solid var(--line);
}
.foot-links { display: flex; gap: 22px; }
.foot-links a:hover { color: var(--ink); }
/* mini player */
.player {
position: fixed; left: 50%; bottom: 18px; transform: translateX(-50%);
z-index: 60; width: min(760px, calc(100% - 32px));
display: grid; grid-template-columns: auto auto 1fr 2fr auto;
align-items: center; gap: 16px;
background: rgba(21, 21, 28, 0.92);
backdrop-filter: blur(16px);
border: 1px solid var(--line-2);
border-radius: var(--r-lg);
padding: 12px 16px;
box-shadow: 0 24px 60px -20px rgba(0, 0, 0, 0.85);
animation: rise 0.28s ease;
}
@keyframes rise { from { transform: translate(-50%, 24px); opacity: 0; } to { transform: translate(-50%, 0); opacity: 1; } }
.pp {
width: 44px; height: 44px; flex: none; cursor: pointer;
border-radius: 50%; border: 0; background: var(--grad);
display: grid; place-items: center;
box-shadow: 0 8px 24px -8px rgba(139, 92, 246, 0.8);
}
.pp-ico[data-state="pause"] { width: 12px; height: 13px; border-left: 4px solid #0b0b12; border-right: 4px solid #0b0b12; }
.pp-ico[data-state="play"] { width: 0; height: 0; margin-left: 3px; border-left: 13px solid #0b0b12; border-top: 8px solid transparent; border-bottom: 8px solid transparent; }
.eq { display: flex; align-items: flex-end; gap: 3px; height: 26px; }
.eq i { width: 4px; border-radius: 3px; background: var(--grad); height: 30%; }
.eq.on i { animation: eq 0.8s ease-in-out infinite; }
.eq.on i:nth-child(2) { animation-delay: 0.15s; }
.eq.on i:nth-child(3) { animation-delay: 0.3s; }
.eq.on i:nth-child(4) { animation-delay: 0.1s; }
.eq.on i:nth-child(5) { animation-delay: 0.4s; }
@keyframes eq { 0%, 100% { height: 25%; } 50% { height: 100%; } }
.now { min-width: 0; }
.now strong { display: block; font-size: 14px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.now small { color: var(--muted); font-size: 12px; }
.track { display: flex; align-items: center; gap: 10px; }
.t-cur, .t-dur { font-size: 12px; color: var(--muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
.bar { flex: 1; height: 6px; border-radius: 999px; background: var(--surface); cursor: pointer; overflow: hidden; }
.bar:focus-visible { outline-offset: 4px; }
.fill { display: block; height: 100%; width: 0%; background: var(--grad); border-radius: 999px; transition: width 0.2s linear; }
.pclose {
width: 30px; height: 30px; flex: none; cursor: pointer;
border-radius: 50%; border: 1px solid var(--line);
background: transparent; color: var(--muted); font-size: 20px; line-height: 1;
transition: 0.15s;
}
.pclose:hover { color: var(--ink); border-color: var(--line-2); }
/* toast */
.toast {
position: fixed; left: 50%; bottom: 96px; transform: translate(-50%, 20px);
z-index: 80;
background: var(--white); color: #0b0b12;
font-weight: 600; font-size: 14px;
padding: 12px 20px; border-radius: 999px;
box-shadow: 0 18px 40px -14px rgba(0, 0, 0, 0.6);
opacity: 0; pointer-events: none; transition: opacity 0.25s, transform 0.25s;
}
.toast.show { opacity: 1; transform: translate(-50%, 0); }
/* responsive */
@media (max-width: 900px) {
.hero { grid-template-columns: 1fr; }
.guest-grid { grid-template-columns: repeat(2, 1fr); }
.sub-card { grid-template-columns: 1fr; }
.nav-links { display: none; }
}
@media (max-width: 520px) {
.stats { gap: 20px; }
.stats dd { font-size: 20px; }
.guest-grid { grid-template-columns: 1fr 1fr; gap: 12px; }
.ep { grid-template-columns: auto 1fr; gap: 12px; padding: 14px 15px; }
.ep-dur { grid-column: 2; justify-self: start; margin-top: 2px; }
.ep-title { white-space: normal; }
.player {
grid-template-columns: auto auto 1fr;
grid-template-areas: "pp eq now" "track track track";
gap: 10px 12px; bottom: 10px;
}
.pp { grid-area: pp; }
.eq { grid-area: eq; }
.now { grid-area: now; }
.track { grid-area: track; }
.pclose { position: absolute; top: 8px; right: 10px; }
.toast { bottom: 120px; width: max-content; max-width: calc(100% - 32px); }
.hero h1 { font-size: 32px; }
.btn-play { flex: 1; justify-content: center; }
}(function () {
"use strict";
/* ---------- data ---------- */
var guests = [
{ init: "YT", name: "Yuki Tanaka", role: "Robotics founder", tag: "Ep. 148", g1: "#f472b6", g2: "#8b5cf6" },
{ init: "RA", name: "Rosa Alvarez", role: "Climate economist", tag: "Ep. 145", g1: "#22d3ee", g2: "#8b5cf6" },
{ init: "DK", name: "Dele Okafor", role: "Neuroscientist", tag: "Ep. 141", g1: "#8b5cf6", g2: "#22d3ee" },
{ init: "ML", name: "Mira Lindqvist", role: "Composer", tag: "Ep. 138", g1: "#f472b6", g2: "#22d3ee" },
{ init: "SC", name: "Samuel Cho", role: "Chess grandmaster", tag: "Ep. 133", g1: "#8b5cf6", g2: "#f472b6" },
{ init: "PN", name: "Priya Nair", role: "Marine biologist", tag: "Ep. 129", g1: "#22d3ee", g2: "#f472b6" },
{ init: "TB", name: "Tomas Berg", role: "Documentary director", tag: "Ep. 124", g1: "#8b5cf6", g2: "#22d3ee" },
{ init: "AW", name: "Amara Wells", role: "Type designer", tag: "Ep. 120", g1: "#f472b6", g2: "#8b5cf6" }
];
var episodes = [
{ n: 148, s: "S8", title: "Machines that learn to doubt", guest: "Yuki Tanaka", role: "Robotics founder", dur: "1:32:04", sec: 5524 },
{ n: 147, s: "S8", title: "The economics of a warming coast", guest: "Rosa Alvarez", role: "Climate economist", dur: "1:18:41", sec: 4721 },
{ n: 146, s: "S8", title: "What memory forgets on purpose", guest: "Dele Okafor", role: "Neuroscientist", dur: "1:44:12", sec: 6252 },
{ n: 144, s: "S7", title: "Writing music for silence", guest: "Mira Lindqvist", role: "Composer", dur: "1:07:55", sec: 4075 },
{ n: 142, s: "S7", title: "Thinking twelve moves ahead", guest: "Samuel Cho", role: "Chess grandmaster", dur: "1:21:30", sec: 4890 },
{ n: 131, s: "S6", title: "Down where the light stops", guest: "Priya Nair", role: "Marine biologist", dur: "1:29:18", sec: 5358 },
{ n: 126, s: "S6", title: "Cutting a story from 400 hours", guest: "Tomas Berg", role: "Documentary director", dur: "1:12:47", sec: 4367 }
];
var $ = function (s, c) { return (c || document).querySelector(s); };
var $$ = function (s, c) { return Array.prototype.slice.call((c || document).querySelectorAll(s)); };
/* ---------- toast ---------- */
var toastEl = $("#toast");
var toastTimer;
function toast(msg) {
toastEl.textContent = msg;
toastEl.classList.add("show");
clearTimeout(toastTimer);
toastTimer = setTimeout(function () { toastEl.classList.remove("show"); }, 2400);
}
/* ---------- subscribe buttons ---------- */
$$("[data-sub]").forEach(function (b) {
b.addEventListener("click", function () {
toast("Opening " + b.getAttribute("data-sub") + " — thanks for following!");
});
});
/* ---------- render guests ---------- */
var grid = $("#guestGrid");
guests.forEach(function (g) {
var li = document.createElement("li");
li.className = "guest-card";
li.innerHTML =
'<span class="ava" style="--g1:' + g.g1 + ';--g2:' + g.g2 + '">' + g.init + "</span>" +
'<div class="g-name">' + g.name + "</div>" +
'<div class="g-role">' + g.role + "</div>" +
'<span class="g-tag">' + g.tag + "</span>";
grid.appendChild(li);
});
/* ---------- render episodes ---------- */
var epList = $("#epList");
function renderEpisodes(filter) {
epList.innerHTML = "";
episodes
.filter(function (e) { return filter === "all" || e.s === filter; })
.forEach(function (e) {
var li = document.createElement("li");
li.className = "ep";
li.dataset.n = e.n;
li.dataset.season = e.s;
var seasonNum = e.s.replace("S", "");
li.innerHTML =
'<button class="ep-play" aria-label="Play episode ' + e.n + ': ' + e.title + '"></button>' +
'<div class="ep-meta">' +
'<div class="ep-top">' +
'<span class="ep-num">EP ' + e.n + "</span>" +
'<span class="ep-season">Season ' + seasonNum + "</span>" +
"</div>" +
'<div class="ep-title">' + e.title + "</div>" +
'<div class="ep-guest">with <b>' + e.guest + "</b> · " + e.role + "</div>" +
"</div>" +
'<span class="ep-dur">' + e.dur + "</span>";
li.querySelector(".ep-play").addEventListener("click", function () { playEpisode(e); });
epList.appendChild(li);
});
highlightPlaying();
}
/* ---------- filters ---------- */
$$(".chip[data-filter]").forEach(function (c) {
c.addEventListener("click", function () {
$$(".chip[data-filter]").forEach(function (x) {
x.classList.remove("is-active");
x.setAttribute("aria-selected", "false");
});
c.classList.add("is-active");
c.setAttribute("aria-selected", "true");
renderEpisodes(c.getAttribute("data-filter"));
});
});
/* ---------- mini player ---------- */
var player = $("#player");
var ppBtn = $("#ppBtn");
var ppIco = $(".pp-ico", ppBtn);
var eq = $("#eq");
var nowTitle = $("#nowTitle");
var nowGuest = $("#nowGuest");
var fill = $("#fill");
var bar = $("#bar");
var tCur = $("#tCur");
var tDur = $("#tDur");
var current = null;
var elapsed = 0;
var playing = false;
var tick;
function fmt(sec) {
sec = Math.max(0, Math.floor(sec));
var m = Math.floor(sec / 60);
var s = sec % 60;
return m + ":" + (s < 10 ? "0" : "") + s;
}
function highlightPlaying() {
$$(".ep").forEach(function (li) {
li.classList.toggle("is-playing", current && +li.dataset.n === current.n && playing);
});
}
function setProgress() {
var pct = current ? (elapsed / current.sec) * 100 : 0;
fill.style.width = Math.min(100, pct) + "%";
tCur.textContent = fmt(elapsed);
bar.setAttribute("aria-valuenow", Math.round(Math.min(100, pct)));
}
function loop() {
tick = setInterval(function () {
if (!playing || !current) return;
elapsed += 1;
if (elapsed >= current.sec) { elapsed = current.sec; setPlaying(false); }
setProgress();
}, 1000);
}
function setPlaying(state) {
playing = state;
ppIco.setAttribute("data-state", state ? "pause" : "play");
ppBtn.setAttribute("aria-label", state ? "Pause" : "Play");
eq.classList.toggle("on", state);
highlightPlaying();
}
function playEpisode(e) {
var same = current && current.n === e.n;
current = e;
if (!same) { elapsed = 0; }
player.hidden = false;
nowTitle.textContent = "EP " + e.n + " · " + e.title;
nowGuest.textContent = "with " + e.guest;
tDur.textContent = fmt(e.sec);
setProgress();
setPlaying(true);
if (!tick) loop();
toast("Now playing · " + e.title);
}
ppBtn.addEventListener("click", function () {
if (!current) return;
setPlaying(!playing);
});
$("#pClose").addEventListener("click", function () {
setPlaying(false);
player.hidden = true;
current = null;
highlightPlaying();
});
/* seek */
function seekFromEvent(clientX) {
if (!current) return;
var r = bar.getBoundingClientRect();
var pct = Math.min(1, Math.max(0, (clientX - r.left) / r.width));
elapsed = Math.round(pct * current.sec);
setProgress();
}
bar.addEventListener("click", function (ev) { seekFromEvent(ev.clientX); });
bar.addEventListener("keydown", function (ev) {
if (!current) return;
if (ev.key === "ArrowRight") { elapsed = Math.min(current.sec, elapsed + 15); setProgress(); ev.preventDefault(); }
else if (ev.key === "ArrowLeft") { elapsed = Math.max(0, elapsed - 15); setProgress(); ev.preventDefault(); }
else if (ev.key === " " || ev.key === "Enter") { setPlaying(!playing); ev.preventDefault(); }
});
/* hero play → first episode */
$("#heroPlay").addEventListener("click", function () { playEpisode(episodes[0]); });
/* ---------- hero waveform ---------- */
var wave = $("#heroWave");
var bars = 40;
for (var i = 0; i < bars; i++) wave.appendChild(document.createElement("i"));
var waveBars = $$("i", wave);
setInterval(function () {
waveBars.forEach(function (b, idx) {
var base = playing ? 30 : 12;
var amp = playing ? 60 : 34;
var h = base + Math.abs(Math.sin(Date.now() / 320 + idx * 0.5)) * amp;
b.style.height = h.toFixed(0) + "%";
});
}, 140);
/* ---------- featured guest rotation ---------- */
var feat = 0;
var featName = $("#featName");
var featRole = $("#featRole");
var featAva = $(".ava", $("#featGuest"));
var rotateBtn = $("#rotatePause");
var rotatePaused = false;
var rotateTimer;
function rotate() {
feat = (feat + 1) % guests.length;
var g = guests[feat];
featName.childNodes[0].nodeValue = g.name;
featRole.textContent = g.role;
featAva.textContent = g.init;
featAva.style.setProperty("--g1", g.g1);
featAva.style.setProperty("--g2", g.g2);
featAva.style.animation = "none";
void featAva.offsetWidth;
featAva.style.animation = "";
}
function startRotate() { rotateTimer = setInterval(function () { if (!rotatePaused) rotate(); }, 3600); }
startRotate();
rotateBtn.addEventListener("click", function () {
rotatePaused = !rotatePaused;
rotateBtn.setAttribute("aria-pressed", String(rotatePaused));
rotateBtn.textContent = rotatePaused ? "Resume rotation" : "Pause rotation";
toast(rotatePaused ? "Guest rotation paused" : "Guest rotation resumed");
});
/* ---------- newsletter ---------- */
var form = $("#newsForm");
var email = $("#email");
var emailErr = $("#emailErr");
form.addEventListener("submit", function (ev) {
ev.preventDefault();
var ok = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email.value.trim());
if (!ok) {
email.classList.add("invalid");
emailErr.hidden = false;
email.focus();
return;
}
email.classList.remove("invalid");
emailErr.hidden = true;
toast("Subscribed! Show notes are on the way.");
email.value = "";
});
email.addEventListener("input", function () {
if (!email.classList.contains("invalid")) return;
email.classList.remove("invalid");
emailErr.hidden = true;
});
/* ---------- init ---------- */
renderEpisodes("all");
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Signal & Noise — Long-form interviews</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="glow glow-a" aria-hidden="true"></div>
<div class="glow glow-b" aria-hidden="true"></div>
<header class="nav">
<a class="brand" href="#top">
<span class="brand-mark" aria-hidden="true">
<span></span><span></span><span></span><span></span>
</span>
<span class="brand-name">Signal & Noise</span>
</a>
<nav class="nav-links" aria-label="Primary">
<a href="#episodes">Episodes</a>
<a href="#guests">Guests</a>
<a href="#subscribe">Subscribe</a>
</nav>
<button class="btn btn-ghost nav-cta" data-sub="RSS">Follow</button>
</header>
<main id="top">
<!-- HERO -->
<section class="hero">
<div class="hero-copy">
<span class="eyebrow"><span class="dot"></span> New episode every Tuesday</span>
<h1>Unhurried conversations with the people shaping what's next.</h1>
<p class="lede">
Host <strong>Mara Delacroix</strong> sits down for 90 uninterrupted minutes with
founders, scientists, and artists — no soundbites, just the story behind the story.
</p>
<div class="hero-actions">
<button class="btn btn-play" id="heroPlay" aria-label="Play the latest episode">
<span class="play-ico" aria-hidden="true"></span>
<span class="play-text">
<b>Play latest</b>
<small>Ep. 148 · Yuki Tanaka</small>
</span>
</button>
<button class="btn btn-ghost" data-sub="Apple Podcasts">
<span class="wave-ico" aria-hidden="true"></span> Subscribe
</button>
</div>
<dl class="stats">
<div><dt>Episodes</dt><dd>148</dd></div>
<div><dt>Monthly listens</dt><dd>2.4M</dd></div>
<div><dt>Avg. rating</dt><dd>4.9★</dd></div>
</dl>
</div>
<aside class="hero-card" aria-label="Featured guest">
<div class="hero-visual">
<div class="waveform" id="heroWave" aria-hidden="true"></div>
<div class="pair">
<figure class="avatar host">
<span class="ava" style="--g1:#8b5cf6;--g2:#22d3ee">MD</span>
<figcaption>Mara D.<small>Host</small></figcaption>
</figure>
<span class="pair-x" aria-hidden="true">×</span>
<figure class="avatar guest" id="featGuest">
<span class="ava" style="--g1:#f472b6;--g2:#8b5cf6">YT</span>
<figcaption id="featName">Yuki Tanaka<small id="featRole">Robotics founder</small></figcaption>
</figure>
</div>
</div>
<div class="hero-card-foot">
<span class="badge badge-live"><span class="pulse"></span> Featured</span>
<button class="chip-btn" id="rotatePause" aria-pressed="false">Pause rotation</button>
</div>
</aside>
</section>
<!-- GUESTS -->
<section id="guests" class="block">
<div class="block-head">
<div>
<h2>Notable guests</h2>
<p class="sub">A few of the voices who've joined Mara at the table.</p>
</div>
</div>
<ul class="guest-grid" id="guestGrid"><!-- populated by JS --></ul>
</section>
<!-- EPISODES -->
<section id="episodes" class="block">
<div class="block-head">
<div>
<h2>Latest episodes</h2>
<p class="sub">Filter by season, then hit play — it loads straight into the player.</p>
</div>
<div class="filters" role="tablist" aria-label="Filter episodes">
<button class="chip is-active" role="tab" aria-selected="true" data-filter="all">All</button>
<button class="chip" role="tab" aria-selected="false" data-filter="S8">Season 8</button>
<button class="chip" role="tab" aria-selected="false" data-filter="S7">Season 7</button>
<button class="chip" role="tab" aria-selected="false" data-filter="S6">Season 6</button>
</div>
</div>
<ul class="ep-list" id="epList"><!-- populated by JS --></ul>
</section>
<!-- SUBSCRIBE -->
<section id="subscribe" class="block subscribe">
<div class="sub-card">
<div class="sub-copy">
<h2>Never miss a conversation.</h2>
<p class="sub">Listen wherever you already are, or get the show notes in your inbox.</p>
<div class="platforms">
<button class="plat" data-sub="Apple Podcasts">Apple</button>
<button class="plat" data-sub="Spotify">Spotify</button>
<button class="plat" data-sub="YouTube">YouTube</button>
<button class="plat" data-sub="Overcast">Overcast</button>
<button class="plat" data-sub="RSS">RSS</button>
</div>
</div>
<form class="news" id="newsForm" novalidate>
<label for="email">Weekly show notes</label>
<div class="news-row">
<input id="email" name="email" type="email" placeholder="[email protected]" autocomplete="email" required />
<button class="btn btn-play btn-sm" type="submit">Subscribe</button>
</div>
<p class="err" id="emailErr" role="alert" hidden>Enter a valid email address.</p>
<p class="hint">No spam. One email a week, unsubscribe anytime.</p>
</form>
</div>
</section>
</main>
<footer class="foot">
<span>© 2026 Signal & Noise Media</span>
<span class="foot-links">
<a href="#top">Back to top</a><a href="#episodes">Archive</a><a href="#subscribe">Press kit</a>
</span>
</footer>
<!-- MINI PLAYER -->
<div class="player" id="player" aria-live="polite" hidden>
<button class="pp" id="ppBtn" aria-label="Pause"><span class="pp-ico" data-state="pause"></span></button>
<div class="eq" id="eq" aria-hidden="true"><i></i><i></i><i></i><i></i><i></i></div>
<div class="now">
<strong id="nowTitle">—</strong>
<small id="nowGuest">—</small>
</div>
<div class="track">
<span class="t-cur" id="tCur">0:00</span>
<div class="bar" id="bar" role="slider" aria-label="Seek" tabindex="0" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0">
<span class="fill" id="fill"></span>
</div>
<span class="t-dur" id="tDur">0:00</span>
</div>
<button class="pclose" id="pClose" aria-label="Close player">×</button>
</div>
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>A landing page for Signal & Noise, a fictional long-form interview podcast. The hero pairs the host with a rotating featured guest, backed by an animated violet-to-cyan waveform and a glowing play button that boots a sticky mini-player at the bottom of the screen. Subscribe buttons for the major platforms sit right alongside the show tagline and listener stats.
Below the fold, a notable guests grid shows past interviewees as gradient avatar cards with role badges and hover lifts. The latest episodes section is a filterable list — chips let you narrow by season, and each row has its own play button, duration badge, and guest line that feed the shared mini-player. Everything is keyboard-usable with visible focus rings.
Interactions are pure vanilla JS: the mini-player tracks the currently playing episode with a live progress bar and animated equalizer, the featured guest rotates on a timer (pausable), the subscribe and share buttons fire a toast, and the newsletter form validates the email inline. The layout collapses cleanly to a single column at 520px.