Podcast — Hero Landing
A bold, audio-first landing hero for a fictional podcast network. Features a giant gradient headline, a live animated waveform accent that reacts to a glowing play button, listen-now and platform-follow controls, an episode-count ticker, and a row of featured show cards with hover glow and mini playback states. Dark, vibrant, and fully interactive with keyboard-accessible controls and a toast confirmation system.
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;
}
* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
margin: 0;
min-height: 100vh;
background: var(--bg);
color: var(--ink);
font-family: "Inter", system-ui, -apple-system, sans-serif;
line-height: 1.5;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
overflow-x: hidden;
position: relative;
}
.glow {
position: fixed;
z-index: 0;
border-radius: 50%;
filter: blur(120px);
opacity: 0.5;
pointer-events: none;
}
.glow--a {
width: 520px; height: 520px;
background: radial-gradient(circle, var(--violet), transparent 70%);
top: -180px; left: -120px;
}
.glow--b {
width: 480px; height: 480px;
background: radial-gradient(circle, var(--cyan), transparent 70%);
bottom: -220px; right: -140px;
opacity: 0.35;
}
/* ---------- nav ---------- */
.nav {
position: relative;
z-index: 2;
display: flex;
align-items: center;
gap: 24px;
max-width: 1200px;
margin: 0 auto;
padding: 22px 28px;
}
.brand {
display: inline-flex;
align-items: center;
gap: 12px;
text-decoration: none;
color: var(--ink);
font-weight: 800;
letter-spacing: -0.02em;
font-size: 19px;
}
.brand__name em { font-style: normal; color: var(--muted); font-weight: 600; }
.brand__mark {
display: inline-flex;
align-items: flex-end;
gap: 3px;
height: 26px; width: 26px;
padding: 4px;
border-radius: var(--r-sm);
background: linear-gradient(135deg, var(--violet), var(--cyan));
}
.brand__mark span {
flex: 1;
border-radius: 2px;
background: var(--bg);
animation: brandbar 1.2s ease-in-out infinite;
}
.brand__mark span:nth-child(1) { height: 45%; animation-delay: 0s; }
.brand__mark span:nth-child(2) { height: 100%; animation-delay: .2s; }
.brand__mark span:nth-child(3) { height: 65%; animation-delay: .4s; }
@keyframes brandbar { 50% { transform: scaleY(0.4); } }
.nav__links {
display: flex;
gap: 26px;
margin-left: auto;
}
.nav__links a {
color: var(--muted);
text-decoration: none;
font-weight: 500;
font-size: 15px;
transition: color .2s;
}
.nav__links a:hover { color: var(--ink); }
/* ---------- buttons ---------- */
.btn {
font-family: inherit;
font-weight: 600;
font-size: 15px;
border-radius: var(--r-md);
border: 1px solid transparent;
cursor: pointer;
padding: 10px 18px;
transition: transform .15s, background .2s, border-color .2s;
}
.btn:active { transform: translateY(1px); }
.btn--ghost {
background: var(--surface);
border-color: var(--line-2);
color: var(--ink);
}
.btn--ghost:hover { border-color: var(--violet); background: var(--surface-2); }
:focus-visible {
outline: 2px solid var(--cyan);
outline-offset: 3px;
}
/* ---------- hero ---------- */
.hero {
position: relative;
z-index: 1;
max-width: 1200px;
margin: 0 auto;
padding: 40px 28px 80px;
display: grid;
grid-template-columns: 1.15fr 0.85fr;
gap: 48px;
align-items: start;
}
.eyebrow {
display: inline-flex;
align-items: center;
gap: 9px;
padding: 7px 14px;
border-radius: 999px;
background: var(--surface);
border: 1px solid var(--line);
color: var(--muted);
font-size: 13px;
font-weight: 500;
}
.eyebrow .dot {
width: 8px; height: 8px; border-radius: 50%;
background: var(--cyan);
box-shadow: 0 0 0 0 rgba(34, 211, 238, .6);
animation: pulse 1.8s infinite;
}
@keyframes pulse {
0% { box-shadow: 0 0 0 0 rgba(34,211,238,.5); }
70% { box-shadow: 0 0 0 8px rgba(34,211,238,0); }
100% { box-shadow: 0 0 0 0 rgba(34,211,238,0); }
}
.headline {
margin: 22px 0 0;
font-size: clamp(2.6rem, 6vw, 4.4rem);
line-height: 1.02;
letter-spacing: -0.03em;
font-weight: 800;
}
.grad {
background: linear-gradient(100deg, var(--violet), var(--cyan) 60%, var(--pink));
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
.lede {
margin: 22px 0 0;
max-width: 30rem;
color: var(--muted);
font-size: 17px;
}
/* ---------- player ---------- */
.player {
margin-top: 30px;
display: flex;
align-items: center;
gap: 18px;
padding: 16px 20px;
border-radius: var(--r-lg);
background: linear-gradient(180deg, var(--surface-2), var(--surface));
border: 1px solid var(--line);
max-width: 520px;
}
.playbtn {
flex: none;
position: relative;
width: 58px; height: 58px;
border-radius: 50%;
border: none;
cursor: pointer;
color: var(--white);
background: linear-gradient(135deg, var(--violet), var(--violet-d));
box-shadow: 0 0 0 6px rgba(139, 92, 246, .15), 0 8px 24px rgba(139, 92, 246, .45);
display: grid;
place-items: center;
transition: transform .15s, box-shadow .25s;
}
.playbtn:hover { transform: scale(1.06); box-shadow: 0 0 0 8px rgba(139,92,246,.18), 0 10px 30px rgba(139,92,246,.6); }
.playbtn:active { transform: scale(0.97); }
.playbtn .ic-play { margin-left: 3px; }
.playbtn .ic-pause { display: none; }
.playbtn[aria-pressed="true"] { background: linear-gradient(135deg, var(--cyan), var(--violet)); }
.playbtn[aria-pressed="true"] .ic-play { display: none; }
.playbtn[aria-pressed="true"] .ic-pause { display: block; }
.player__meta { flex: 1; min-width: 0; }
.player__label {
display: block;
font-size: 11px;
text-transform: uppercase;
letter-spacing: .12em;
color: var(--muted);
}
.player__title {
display: block;
font-size: 15px;
font-weight: 700;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.player__time {
flex: none;
font-variant-numeric: tabular-nums;
font-size: 14px;
color: var(--muted);
}
/* ---------- waveform ---------- */
.wave {
margin-top: 8px;
display: flex;
align-items: center;
gap: 3px;
height: 26px;
}
.wave span {
flex: 1;
min-width: 2px;
border-radius: 999px;
background: linear-gradient(180deg, var(--cyan), var(--violet));
transform: scaleY(0.25);
transform-origin: center;
opacity: .85;
}
.wave.is-playing span {
animation: wavebar var(--dur, 1s) ease-in-out infinite;
}
@keyframes wavebar {
0%, 100% { transform: scaleY(0.2); }
50% { transform: scaleY(1); }
}
/* ---------- platforms ---------- */
.platforms {
margin-top: 26px;
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 10px;
}
.platforms__hint { color: var(--muted); font-size: 14px; margin-right: 4px; }
.chip {
font-family: inherit;
font-weight: 600;
font-size: 14px;
color: var(--ink);
background: var(--surface);
border: 1px solid var(--line-2);
border-radius: 999px;
padding: 8px 16px;
cursor: pointer;
transition: border-color .2s, background .2s, transform .15s;
}
.chip:hover {
border-color: var(--cyan);
background: var(--surface-2);
transform: translateY(-2px);
}
.chip:active { transform: translateY(0); }
/* ---------- stats ---------- */
.stats {
margin: 40px 0 0;
display: flex;
gap: 40px;
flex-wrap: wrap;
}
.stat dt { color: var(--muted); font-size: 13px; margin: 0; }
.stat dd {
margin: 4px 0 0;
font-size: 28px;
font-weight: 800;
letter-spacing: -0.02em;
font-variant-numeric: tabular-nums;
}
/* ---------- shows ---------- */
.shows {
background: var(--surface);
border: 1px solid var(--line);
border-radius: var(--r-lg);
padding: 22px;
}
.shows__head {
display: flex;
align-items: baseline;
justify-content: space-between;
margin-bottom: 16px;
}
.shows__head h2 { margin: 0; font-size: 18px; font-weight: 700; }
.shows__hint { color: var(--muted); font-size: 12px; }
.showlist { list-style: none; margin: 0; padding: 0; display: grid; gap: 12px; }
.show {
width: 100%;
display: flex;
align-items: center;
gap: 14px;
padding: 12px;
text-align: left;
background: var(--surface-2);
border: 1px solid var(--line);
border-radius: var(--r-md);
cursor: pointer;
font-family: inherit;
color: var(--ink);
transition: transform .18s, border-color .2s, box-shadow .25s;
}
.show:hover {
transform: translateY(-3px);
border-color: var(--line-2);
box-shadow: 0 12px 28px rgba(0, 0, 0, .45);
}
.show.is-active {
border-color: var(--violet);
box-shadow: 0 0 0 1px var(--violet), 0 12px 30px rgba(139, 92, 246, .35);
}
.show__art {
flex: none;
width: 54px; height: 54px;
border-radius: 10px;
background: var(--cover, linear-gradient(135deg, var(--violet), var(--cyan)));
display: grid;
place-items: center;
position: relative;
overflow: hidden;
}
.show__bars {
width: 26px; height: 20px;
background:
linear-gradient(var(--white), var(--white)) 0 60%/4px 40% no-repeat,
linear-gradient(var(--white), var(--white)) 7px 20%/4px 80% no-repeat,
linear-gradient(var(--white), var(--white)) 14px 45%/4px 55% no-repeat,
linear-gradient(var(--white), var(--white)) 21px 30%/4px 70% no-repeat;
opacity: .9;
}
.show__body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.show__cat {
align-self: flex-start;
font-size: 10px;
font-weight: 700;
letter-spacing: .08em;
text-transform: uppercase;
color: var(--cyan);
background: rgba(34, 211, 238, .12);
border-radius: 999px;
padding: 2px 8px;
}
.show__name { font-size: 15px; font-weight: 700; }
.show__host { font-size: 13px; color: var(--muted); }
.show__play {
flex: none;
width: 34px; height: 34px;
border-radius: 50%;
background: var(--surface);
border: 1px solid var(--line-2);
position: relative;
transition: background .2s, border-color .2s;
}
.show__play::before {
content: "";
position: absolute;
top: 50%; left: 54%;
transform: translate(-50%, -50%);
border-style: solid;
border-width: 6px 0 6px 10px;
border-color: transparent transparent transparent var(--ink);
}
.show:hover .show__play { border-color: var(--cyan); background: var(--surface-2); }
.show.is-active .show__play {
background: linear-gradient(135deg, var(--violet), var(--cyan));
border-color: transparent;
}
.show.is-active .show__play::before { border-color: transparent transparent transparent var(--white); }
/* ---------- toast ---------- */
.toast {
position: fixed;
z-index: 20;
left: 50%;
bottom: 28px;
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: var(--r-md);
box-shadow: 0 12px 32px rgba(0, 0, 0, .55);
opacity: 0;
pointer-events: none;
transition: opacity .25s, transform .25s;
}
.toast.show { opacity: 1; transform: translate(-50%, 0); }
/* ---------- responsive ---------- */
@media (max-width: 900px) {
.hero { grid-template-columns: 1fr; gap: 36px; }
.nav__links { display: none; }
}
@media (max-width: 520px) {
.nav { padding: 18px; gap: 12px; }
.hero { padding: 24px 18px 60px; }
.player { flex-wrap: wrap; }
.player__time { order: 3; }
.stats { gap: 24px; }
.stat dd { font-size: 22px; }
.platforms__hint { width: 100%; }
.shows { padding: 16px; }
}(function () {
"use strict";
var playBtn = document.getElementById("playBtn");
var wave = document.getElementById("wave");
var nowTitle = document.getElementById("nowTitle");
var playTime = document.getElementById("playTime");
var toastEl = document.getElementById("toast");
var showList = document.getElementById("showList");
var subscribeBtn = document.getElementById("subscribeBtn");
var statWeek = document.getElementById("statWeek");
var playing = false;
var toastTimer = null;
/* ---- toast helper ---- */
function toast(msg) {
toastEl.textContent = msg;
toastEl.classList.add("show");
clearTimeout(toastTimer);
toastTimer = setTimeout(function () {
toastEl.classList.remove("show");
}, 2400);
}
/* ---- build waveform bars ---- */
var BAR_COUNT = 40;
for (var i = 0; i < BAR_COUNT; i++) {
var bar = document.createElement("span");
// vary animation duration + delay per bar for an organic ripple
var dur = (0.6 + Math.random() * 0.8).toFixed(2);
bar.style.setProperty("--dur", dur + "s");
bar.style.animationDelay = (-Math.random() * 1.2).toFixed(2) + "s";
wave.appendChild(bar);
}
/* ---- play / pause ---- */
function setPlaying(state) {
playing = state;
playBtn.setAttribute("aria-pressed", state ? "true" : "false");
playBtn.setAttribute("aria-label", state ? "Pause featured episode" : "Play featured episode");
wave.classList.toggle("is-playing", state);
var active = showList.querySelector(".show.is-active");
if (active) {
active.classList.toggle("is-playing-src", state);
}
}
playBtn.addEventListener("click", function () {
setPlaying(!playing);
toast(playing ? "Playing · " + nowTitle.textContent : "Paused");
});
/* ---- show cards -> load into player ---- */
var shows = showList.querySelectorAll(".show");
shows.forEach(function (btn) {
btn.addEventListener("click", function () {
shows.forEach(function (s) { s.classList.remove("is-active"); });
btn.classList.add("is-active");
nowTitle.textContent = btn.getAttribute("data-title");
playTime.textContent = btn.getAttribute("data-time");
// vary waveform tempo slightly per show
var speed = 0.7 + Math.random() * 0.5;
wave.querySelectorAll("span").forEach(function (b) {
var base = 0.6 + Math.random() * 0.8;
b.style.setProperty("--dur", (base * speed).toFixed(2) + "s");
});
setPlaying(true);
toast("Loaded · " + btn.querySelector(".show__name").textContent);
});
});
// mark first show active by default
if (shows.length) shows[0].classList.add("is-active");
/* ---- platform chips ---- */
document.querySelectorAll(".chip").forEach(function (chip) {
chip.addEventListener("click", function () {
toast("Following on " + chip.getAttribute("data-platform"));
});
});
/* ---- subscribe ---- */
subscribeBtn.addEventListener("click", function () {
toast("Subscribed to Frequency Network");
});
/* ---- count-up ticker ---- */
function countUp(el) {
var target = parseInt(el.getAttribute("data-count"), 10) || 0;
var start = 0;
var t0 = null;
var dur = 1100;
function step(ts) {
if (t0 === null) t0 = ts;
var p = Math.min((ts - t0) / dur, 1);
var eased = 1 - Math.pow(1 - p, 3);
el.textContent = Math.round(start + (target - start) * eased);
if (p < 1) requestAnimationFrame(step);
}
requestAnimationFrame(step);
}
if (statWeek) countUp(statWeek);
/* ---- keyboard: space toggles play when body focused ---- */
document.addEventListener("keydown", function (e) {
if (e.code === "Space" && (e.target === document.body)) {
e.preventDefault();
playBtn.click();
}
});
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Frequency Network — Podcast Hero</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="#" aria-label="Frequency Network home">
<span class="brand__mark" aria-hidden="true">
<span></span><span></span><span></span>
</span>
<span class="brand__name">Frequency<em>.fm</em></span>
</a>
<nav class="nav__links" aria-label="Primary">
<a href="#shows">Shows</a>
<a href="#">Live</a>
<a href="#">Hosts</a>
<a href="#">Blog</a>
</nav>
<button class="btn btn--ghost" id="subscribeBtn" type="button">Subscribe</button>
</header>
<main class="hero" id="top">
<section class="hero__copy">
<span class="eyebrow">
<span class="dot" aria-hidden="true"></span>
New season · 4 originals dropping weekly
</span>
<h1 class="headline">
Stories that <span class="grad">sound</span><br />like nothing else.
</h1>
<p class="lede">
Frequency Network is a home for bold audio — true crime, deep tech,
late-night talk and everything in between. Hit play and tune your ears.
</p>
<div class="player" role="group" aria-label="Featured player">
<button class="playbtn" id="playBtn" type="button" aria-pressed="false" aria-label="Play featured 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="M7 5h4v14H7zM13 5h4v14h-4z" fill="currentColor"/></svg>
</button>
<div class="player__meta">
<span class="player__label">Now playing</span>
<strong class="player__title" id="nowTitle">Signal & Noise — Ep. 112</strong>
<div class="wave" id="wave" aria-hidden="true"></div>
</div>
<span class="player__time" id="playTime">32:04</span>
</div>
<div class="platforms" aria-label="Listen on your platform">
<span class="platforms__hint">Listen on</span>
<button class="chip" data-platform="Spotify" type="button">Spotify</button>
<button class="chip" data-platform="Apple Podcasts" type="button">Apple</button>
<button class="chip" data-platform="YouTube" type="button">YouTube</button>
<button class="chip" data-platform="RSS feed" type="button">RSS</button>
</div>
<dl class="stats">
<div class="stat">
<dt>Episodes this week</dt>
<dd data-count="18" id="statWeek">0</dd>
</div>
<div class="stat">
<dt>Monthly listeners</dt>
<dd>2.4M</dd>
</div>
<div class="stat">
<dt>Original shows</dt>
<dd>27</dd>
</div>
</dl>
</section>
<aside class="shows" id="shows" aria-label="Featured shows">
<div class="shows__head">
<h2>Featured shows</h2>
<span class="shows__hint">Click to load into player</span>
</div>
<ul class="showlist" id="showList">
<li>
<button class="show" type="button" data-title="Signal & Noise — Ep. 112" data-time="32:04" style="--cover:linear-gradient(135deg,#8b5cf6,#22d3ee)">
<span class="show__art" aria-hidden="true"><span class="show__bars"></span></span>
<span class="show__body">
<span class="show__cat">Deep Tech</span>
<strong class="show__name">Signal & Noise</strong>
<span class="show__host">with Dana Okafor</span>
</span>
<span class="show__play" aria-hidden="true"></span>
</button>
</li>
<li>
<button class="show" type="button" data-title="Midnight Verdict — Ep. 47" data-time="48:20" style="--cover:linear-gradient(135deg,#f472b6,#7c3aed)">
<span class="show__art" aria-hidden="true"><span class="show__bars"></span></span>
<span class="show__body">
<span class="show__cat">True Crime</span>
<strong class="show__name">Midnight Verdict</strong>
<span class="show__host">with Reyna Cole</span>
</span>
<span class="show__play" aria-hidden="true"></span>
</button>
</li>
<li>
<button class="show" type="button" data-title="Overtone — Ep. 09" data-time="26:51" style="--cover:linear-gradient(135deg,#22d3ee,#8b5cf6)">
<span class="show__art" aria-hidden="true"><span class="show__bars"></span></span>
<span class="show__body">
<span class="show__cat">Music</span>
<strong class="show__name">Overtone</strong>
<span class="show__host">with Milo Vance</span>
</span>
<span class="show__play" aria-hidden="true"></span>
</button>
</li>
<li>
<button class="show" type="button" data-title="After Hours — Ep. 203" data-time="59:12" style="--cover:linear-gradient(135deg,#7c3aed,#f472b6)">
<span class="show__art" aria-hidden="true"><span class="show__bars"></span></span>
<span class="show__body">
<span class="show__cat">Talk</span>
<strong class="show__name">After Hours</strong>
<span class="show__host">with Priya & Sol</span>
</span>
<span class="show__play" aria-hidden="true"></span>
</button>
</li>
</ul>
</aside>
</main>
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>A dark, audio-first hero for the fictional Frequency Network podcast collective. The layout pairs an oversized gradient headline and supporting copy with a glowing primary play button and a set of platform-follow chips (Spotify, Apple, YouTube, RSS). Behind the controls sits an animated waveform built from dozens of gradient bars (violet → cyan) that idles gently and springs to life when playback is toggled.
The right column holds a stacked strip of featured show cards, each with cover art gradients, host names, category badges, and a mini play state. Cards lift and glow on hover, and clicking one promotes it into the hero player, updating the now-playing label and animating the waveform tempo. A live “episodes this week” ticker counts up on load to add momentum.
Everything is vanilla JS: the play button toggles a global playing state that drives the waveform animation speed, platform chips fire a toast() confirmation, and all controls are keyboard-operable with visible focus rings. Realistic but clearly fictional show and host names keep it production-flavored without hotlinking real brands.