Podcast — Episode Detail
A dark, audio-first episode page anchored by a glowing gradient player with scrubbable waveform, live elapsed time, and speed control. Clickable chapter markers jump the playhead, a collapsible transcript preview highlights the active line, and rich show notes list guests, links, and timestamps. Share buttons copy the episode link with a toast, and a rail of related episodes invites the next listen — all responsive down to narrow phones with visible focus states and keyboard-usable controls.
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));
}
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
html{-webkit-text-size-adjust:100%}
body{
font-family:"Inter",system-ui,-apple-system,sans-serif;
background:var(--bg); color:var(--ink); line-height:1.5;
-webkit-font-smoothing:antialiased; text-rendering:optimizeLegibility;
min-height:100vh; position:relative; overflow-x:hidden;
}
h1,h2,h3{font-family:"Sora","Inter",sans-serif;line-height:1.15}
a{color:inherit;text-decoration:none}
button{font-family:inherit;cursor:pointer;border:none;background:none;color:inherit}
:focus-visible{outline:2px solid var(--cyan);outline-offset:2px;border-radius:6px}
.glow-bg{
position:fixed;inset:0;z-index:-1;pointer-events:none;
background:
radial-gradient(600px 400px at 15% -5%,rgba(139,92,246,.28),transparent 60%),
radial-gradient(560px 420px at 92% 8%,rgba(34,211,238,.18),transparent 60%),
radial-gradient(700px 500px at 60% 110%,rgba(244,114,182,.10),transparent 60%);
}
/* ---------- top bar ---------- */
.topbar{
display:flex;align-items:center;gap:18px;
max-width:1120px;margin:0 auto;padding:18px 24px;
}
.brand{display:flex;align-items:center;gap:10px;font-weight:800;font-family:"Sora",sans-serif}
.brand-mark{display:flex;align-items:flex-end;gap:2px;height:20px}
.brand-mark span{width:3px;border-radius:2px;background:var(--grad);animation:eq 1s ease-in-out infinite}
.brand-mark span:nth-child(1){height:8px;animation-delay:0s}
.brand-mark span:nth-child(2){height:16px;animation-delay:.15s}
.brand-mark span:nth-child(3){height:11px;animation-delay:.3s}
.brand-mark span:nth-child(4){height:18px;animation-delay:.45s}
@keyframes eq{0%,100%{transform:scaleY(.5)}50%{transform:scaleY(1)}}
.brand-name{font-size:16px;letter-spacing:-.01em}
.topnav{display:flex;gap:22px;margin-left:12px}
.topnav a{color:var(--muted);font-size:14px;font-weight:500;transition:color .18s}
.topnav a:hover{color:var(--ink)}
.btn-sub{
margin-left:auto;background:var(--grad);color:#0b0b12;font-weight:700;
padding:9px 18px;border-radius:999px;font-size:14px;
box-shadow:0 6px 20px rgba(139,92,246,.35);transition:transform .16s,box-shadow .16s;
}
.btn-sub:hover{transform:translateY(-1px);box-shadow:0 10px 28px rgba(139,92,246,.5)}
.btn-sub:active{transform:translateY(0)}
.wrap{max-width:1120px;margin:0 auto;padding:8px 24px 72px}
.crumb{display:inline-block;color:var(--muted);font-size:13px;font-weight:500;margin:6px 0 20px;transition:color .18s}
.crumb:hover{color:var(--cyan)}
/* ---------- player hero ---------- */
.player{
display:grid;grid-template-columns:220px 1fr;gap:28px;
background:linear-gradient(180deg,var(--surface-2),var(--surface));
border:1px solid var(--line);border-radius:var(--r-lg);
padding:26px;box-shadow:0 20px 60px rgba(0,0,0,.4);position:relative;overflow:hidden;
}
.player::before{content:"";position:absolute;inset:0;background:radial-gradient(400px 200px at 10% 0%,rgba(139,92,246,.15),transparent 70%);pointer-events:none}
.player-art{position:relative}
.cover{
aspect-ratio:1;border-radius:var(--r-md);position:relative;overflow:hidden;
background:linear-gradient(140deg,var(--violet-d),#3b1d6e 55%,#0e2b33);
display:flex;align-items:flex-end;padding:16px;
box-shadow:0 0 0 1px var(--line),0 18px 44px rgba(124,58,237,.4);
}
.cover::after{content:"";position:absolute;top:-40%;right:-30%;width:80%;height:80%;background:radial-gradient(circle,rgba(34,211,238,.5),transparent 70%);filter:blur(6px)}
.cover-ep{font-family:"Sora";font-weight:800;font-size:20px;letter-spacing:.04em;position:relative;z-index:1}
.cover-eq{position:absolute;top:16px;left:16px;display:flex;gap:3px;align-items:flex-end;height:26px;z-index:1}
.cover-eq i{width:4px;border-radius:2px;background:rgba(255,255,255,.85);animation:eq 1.1s ease-in-out infinite}
.cover-eq i:nth-child(1){height:10px;animation-delay:0s}
.cover-eq i:nth-child(2){height:22px;animation-delay:.2s}
.cover-eq i:nth-child(3){height:14px;animation-delay:.4s}
.cover-eq i:nth-child(4){height:24px;animation-delay:.1s}
.cover-eq i:nth-child(5){height:12px;animation-delay:.3s}
body.paused .cover-eq i,body.paused .brand-mark span{animation-play-state:paused}
.player-main{min-width:0}
.badges{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:12px}
.badge{
font-size:11px;font-weight:700;letter-spacing:.03em;text-transform:uppercase;
padding:4px 10px;border-radius:999px;background:var(--surface-2);
border:1px solid var(--line-2);color:var(--muted);
}
.badge-live{background:rgba(139,92,246,.18);border-color:rgba(139,92,246,.5);color:#c9b6ff}
.badge-dot{color:#a7f3e0;border-color:rgba(34,211,238,.4);background:rgba(34,211,238,.12);display:inline-flex;align-items:center;gap:6px}
.badge-dot::before{content:"";width:6px;height:6px;border-radius:50%;background:var(--cyan);box-shadow:0 0 8px var(--cyan)}
.ep-title{font-size:26px;font-weight:800;letter-spacing:-.02em;margin-bottom:8px}
.ep-meta{color:var(--muted);font-size:13.5px;display:flex;flex-wrap:wrap;gap:8px;align-items:center}
.ep-meta .sep{opacity:.5}
.controls{display:flex;align-items:center;gap:12px;margin:22px 0 16px}
.btn-round{
position:relative;width:44px;height:44px;border-radius:50%;
background:var(--surface-2);border:1px solid var(--line-2);color:var(--ink);
display:grid;place-items:center;transition:transform .16s,border-color .16s,background .16s;
}
.btn-round svg{width:22px;height:22px}
.btn-round em{position:absolute;bottom:6px;font-style:normal;font-size:8px;font-weight:700;color:var(--muted)}
.btn-round:hover{border-color:var(--violet);background:#232333;transform:translateY(-1px)}
.btn-play{
width:66px;height:66px;border-radius:50%;background:var(--grad);color:#0b0b12;
display:grid;place-items:center;position:relative;
box-shadow:0 0 0 6px rgba(139,92,246,.12),0 12px 34px rgba(139,92,246,.5);
transition:transform .16s,box-shadow .16s;
}
.btn-play:hover{transform:scale(1.05);box-shadow:0 0 0 8px rgba(139,92,246,.16),0 16px 42px rgba(139,92,246,.6)}
.btn-play:active{transform:scale(.97)}
.btn-play svg{width:30px;height:30px;grid-area:1/1}
.btn-play .ic-pause{display:none}
body.playing .btn-play .ic-play{display:none}
body.playing .btn-play .ic-pause{display:block}
.btn-speed{
padding:0 12px;height:36px;min-width:48px;border-radius:999px;font-weight:700;font-size:13px;
background:var(--surface-2);border:1px solid var(--line-2);color:var(--cyan);
transition:border-color .16s,transform .16s;
}
.btn-speed:hover{border-color:var(--cyan);transform:translateY(-1px)}
.btn-save{
margin-left:auto;width:42px;height:42px;border-radius:var(--r-sm);
background:var(--surface-2);border:1px solid var(--line-2);color:var(--muted);
display:grid;place-items:center;transition:.16s;
}
.btn-save svg{width:20px;height:20px}
.btn-save:hover{color:var(--ink);border-color:var(--pink)}
.btn-save[aria-pressed="true"]{color:var(--pink);border-color:var(--pink);background:rgba(244,114,182,.14)}
.btn-save[aria-pressed="true"] svg{fill:var(--pink)}
/* ---------- waveform scrub ---------- */
.scrub{margin-top:6px}
.wave{
--pct:0%;
position:relative;height:56px;border-radius:var(--r-sm);cursor:pointer;
background:
repeating-linear-gradient(90deg,rgba(255,255,255,.10) 0 3px,transparent 3px 7px);
-webkit-mask:repeating-linear-gradient(90deg,#000 0 3px,transparent 3px 7px);
mask:repeating-linear-gradient(90deg,#000 0 3px,transparent 3px 7px);
background:rgba(255,255,255,.10);
}
.wave{
background:
linear-gradient(rgba(255,255,255,.10),rgba(255,255,255,.10)),
conic-gradient(from 0deg,transparent);
}
/* bars via mask so fill shows through cleanly */
.wave{overflow:hidden}
.wave-fill{
position:absolute;inset:0;width:var(--pct);
background:var(--grad);transition:width .15s linear;
}
.wave,.wave-fill{
-webkit-mask:repeating-linear-gradient(90deg,#000 0 4px,transparent 4px 8px);
mask:repeating-linear-gradient(90deg,#000 0 4px,transparent 4px 8px);
}
.wave{background:rgba(255,255,255,.12)}
.wave-head{
position:absolute;top:-4px;bottom:-4px;left:var(--pct);width:2px;
background:var(--white);box-shadow:0 0 10px var(--cyan);transition:left .15s linear;
-webkit-mask:none;mask:none;
}
.wave:hover .wave-head{background:var(--cyan)}
.times{display:flex;justify-content:space-between;font-size:12px;color:var(--muted);margin-top:8px;font-variant-numeric:tabular-nums}
#cur{color:var(--cyan);font-weight:600}
/* ---------- share ---------- */
.share{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-top:20px}
.share-label{font-size:13px;color:var(--muted);font-weight:500}
.chip{
padding:7px 13px;border-radius:999px;font-size:12.5px;font-weight:600;
background:var(--surface-2);border:1px solid var(--line-2);color:var(--ink);
transition:transform .15s,border-color .15s,color .15s;
}
.chip:hover{transform:translateY(-1px);border-color:var(--violet);color:#c9b6ff}
.chip-copy{background:rgba(139,92,246,.14);border-color:rgba(139,92,246,.45);color:#c9b6ff}
/* ---------- grid ---------- */
.grid{display:grid;grid-template-columns:1fr 340px;gap:22px;margin-top:22px}
.card{
background:var(--surface);border:1px solid var(--line);border-radius:var(--r-lg);
padding:22px;margin-bottom:22px;
}
.col-main .card:last-child,.col-side .card:last-child{margin-bottom:0}
.card-head{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:16px}
.card-head h2{font-size:17px;font-weight:700}
.count{font-size:12px;color:var(--muted);background:var(--surface-2);border:1px solid var(--line);padding:3px 10px;border-radius:999px}
.link-btn{color:var(--cyan);font-size:13px;font-weight:600}
.link-btn:hover{text-decoration:underline}
/* chapters */
.chapters{list-style:none;display:flex;flex-direction:column;gap:6px}
.chapters button{
width:100%;display:flex;align-items:center;gap:14px;text-align:left;
padding:11px 12px;border-radius:var(--r-md);border:1px solid transparent;
transition:background .15s,border-color .15s;
}
.chapters button:hover{background:var(--surface-2);border-color:var(--line-2)}
.ch-time{
font-variant-numeric:tabular-nums;font-weight:700;font-size:13px;color:var(--muted);
min-width:48px;padding:3px 8px;background:var(--surface-2);border-radius:6px;text-align:center;
}
.ch-name{font-size:14.5px;font-weight:500}
.chapters li.active button{background:rgba(139,92,246,.14);border-color:rgba(139,92,246,.5)}
.chapters li.active .ch-time{background:var(--grad);color:#0b0b12}
.chapters li.active .ch-name{color:var(--white)}
/* transcript */
.transcript{position:relative;max-height:168px;overflow:hidden;transition:max-height .35s ease}
.transcript.open{max-height:900px}
.transcript p{font-size:14.5px;color:#d4d4de;margin-bottom:14px;padding:6px 8px;border-radius:8px;transition:background .2s}
.transcript p.here{background:rgba(34,211,238,.10);box-shadow:inset 3px 0 0 var(--cyan)}
.spk{font-weight:700;margin-right:8px}
.spk-a{color:var(--violet)}
.spk-b{color:var(--cyan)}
.fade{height:44px;margin-top:-44px;position:relative;background:linear-gradient(transparent,var(--surface));pointer-events:none;transition:opacity .3s}
.transcript.open + .fade{opacity:0}
/* show notes */
.notes-lead{color:#c9c9d4;font-size:14.5px;margin-bottom:18px}
.notes-sub{font-size:12px;text-transform:uppercase;letter-spacing:.06em;color:var(--muted);margin:18px 0 10px}
.notes-list{list-style:none;display:flex;flex-direction:column;gap:9px}
.notes-list li{font-size:14px;color:#d4d4de;display:flex;gap:12px;align-items:baseline}
.nt{font-variant-numeric:tabular-nums;font-weight:700;font-size:12.5px;color:var(--cyan);min-width:44px}
.links{display:flex;flex-wrap:wrap;gap:8px}
.link-pill{
font-size:13px;font-weight:500;padding:7px 13px;border-radius:999px;
background:var(--surface-2);border:1px solid var(--line-2);color:#d4d4de;transition:.15s;
}
.link-pill:hover{border-color:var(--cyan);color:var(--cyan);transform:translateY(-1px)}
/* guest */
.guest-row{display:flex;align-items:center;gap:14px;margin-bottom:14px}
.avatar{
width:52px;height:52px;border-radius:50%;display:grid;place-items:center;
font-weight:800;font-family:"Sora";background:var(--grad);color:#0b0b12;flex-shrink:0;
box-shadow:0 8px 20px rgba(139,92,246,.4);
}
.guest-name{font-weight:700;font-size:15px}
.guest-role{font-size:13px;color:var(--muted)}
.guest-bio{font-size:13.5px;color:#c9c9d4;margin-bottom:14px}
.social{display:flex;gap:8px;flex-wrap:wrap}
/* related */
.related{list-style:none;display:flex;flex-direction:column;gap:8px}
.related button{
width:100%;display:flex;align-items:center;gap:12px;padding:8px;
border-radius:var(--r-md);border:1px solid transparent;transition:.15s;
}
.related button:hover{background:var(--surface-2);border-color:var(--line-2)}
.r-cover{
width:46px;height:46px;border-radius:10px;flex-shrink:0;display:grid;place-items:center;
font-family:"Sora";font-weight:800;font-size:15px;color:var(--white);
}
.r-c1{background:linear-gradient(140deg,var(--violet-d),#2b1856)}
.r-c2{background:linear-gradient(140deg,#0e5563,var(--cyan))}
.r-c3{background:linear-gradient(140deg,#7a2a55,var(--pink))}
.r-body{flex:1;min-width:0}
.r-title{display:block;font-size:14px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.r-meta{display:block;font-size:12px;color:var(--muted)}
.r-play{color:var(--muted);font-size:12px;transition:color .15s,transform .15s}
.related button:hover .r-play{color:var(--cyan);transform:scale(1.2)}
/* toast */
.toast{
position:fixed;left:50%;bottom:28px;transform:translate(-50%,20px);
background:var(--surface-2);border:1px solid var(--line-2);color:var(--ink);
padding:12px 20px;border-radius:999px;font-size:13.5px;font-weight:600;
box-shadow:0 16px 40px rgba(0,0,0,.5);opacity:0;pointer-events:none;
transition:opacity .25s,transform .25s;z-index:50;
}
.toast::before{content:"";display:inline-block;width:8px;height:8px;border-radius:50%;background:var(--cyan);margin-right:9px;vertical-align:middle;box-shadow:0 0 8px var(--cyan)}
.toast.show{opacity:1;transform:translate(-50%,0)}
/* ---------- responsive ---------- */
@media (max-width:860px){
.grid{grid-template-columns:1fr}
.player{grid-template-columns:150px 1fr}
}
@media (max-width:520px){
.topnav{display:none}
.wrap{padding:8px 16px 56px}
.topbar{padding:16px}
.player{grid-template-columns:1fr;padding:18px;gap:18px}
.player-art{max-width:180px}
.ep-title{font-size:21px}
.controls{flex-wrap:wrap;gap:10px}
.btn-save{margin-left:0}
.card{padding:18px}
.ch-name{font-size:13.5px}
}(function () {
"use strict";
var DURATION = 3120; // 52:00 in seconds
var current = 0;
var playing = false;
var timer = null;
var speeds = [1, 1.25, 1.5, 2];
var speedIdx = 0;
var $ = function (sel, ctx) { return (ctx || document).querySelector(sel); };
var $$ = function (sel, ctx) { return Array.prototype.slice.call((ctx || document).querySelectorAll(sel)); };
var body = document.body;
var playBtn = $("#playBtn");
var wave = $("#wave");
var waveFill = $("#waveFill");
var waveHead = $("#waveHead");
var curEl = $("#cur");
var durEl = $("#dur");
var chapterEls = $$("#chapters li");
var transcriptEls = $$("#transcript p");
var toastEl = $("#toast");
var toastTimer = null;
body.classList.add("paused");
/* ---------- helpers ---------- */
function fmt(s) {
s = Math.max(0, Math.floor(s));
var m = Math.floor(s / 60);
var r = s % 60;
return m + ":" + (r < 10 ? "0" + r : r);
}
function toast(msg) {
toastEl.textContent = msg;
toastEl.classList.add("show");
clearTimeout(toastTimer);
toastTimer = setTimeout(function () { toastEl.classList.remove("show"); }, 2200);
}
function render() {
var pct = (current / DURATION) * 100;
wave.style.setProperty("--pct", pct + "%");
curEl.textContent = fmt(current);
durEl.textContent = "-" + fmt(DURATION - current);
wave.setAttribute("aria-valuenow", Math.floor(current));
wave.setAttribute("aria-valuetext", fmt(current) + " of " + fmt(DURATION));
syncChapters();
syncTranscript();
}
function syncChapters() {
var activeIdx = 0;
chapterEls.forEach(function (li, i) {
var t = +li.querySelector("button").dataset.t;
if (current >= t) activeIdx = i;
});
chapterEls.forEach(function (li, i) {
li.classList.toggle("active", i === activeIdx);
});
}
function syncTranscript() {
var activeIdx = -1;
transcriptEls.forEach(function (p, i) {
if (current >= +p.dataset.t) activeIdx = i;
});
transcriptEls.forEach(function (p, i) {
p.classList.toggle("here", i === activeIdx);
});
}
/* ---------- playback ---------- */
function tick() {
current += speeds[speedIdx];
if (current >= DURATION) {
current = DURATION;
pause();
toast("Episode finished — up next: Ep 46");
}
render();
}
function play() {
if (playing) return;
playing = true;
body.classList.add("playing");
body.classList.remove("paused");
playBtn.setAttribute("aria-pressed", "true");
playBtn.setAttribute("aria-label", "Pause episode");
timer = setInterval(tick, 1000);
}
function pause() {
playing = false;
body.classList.remove("playing");
body.classList.add("paused");
playBtn.setAttribute("aria-pressed", "false");
playBtn.setAttribute("aria-label", "Play episode");
clearInterval(timer);
}
function toggle() { playing ? pause() : play(); }
playBtn.addEventListener("click", toggle);
$("#rewind").addEventListener("click", function () {
current = Math.max(0, current - 15); render();
});
$("#forward").addEventListener("click", function () {
current = Math.min(DURATION, current + 30); render();
});
/* ---------- speed ---------- */
$("#speed").addEventListener("click", function () {
speedIdx = (speedIdx + 1) % speeds.length;
this.textContent = speeds[speedIdx] + "×";
if (playing) { clearInterval(timer); timer = setInterval(tick, 1000); }
toast("Speed " + speeds[speedIdx] + "×");
});
/* ---------- scrub ---------- */
function seekFromEvent(e) {
var rect = wave.getBoundingClientRect();
var x = (e.touches ? e.touches[0].clientX : e.clientX) - rect.left;
var pct = Math.min(1, Math.max(0, x / rect.width));
current = Math.round(pct * DURATION);
render();
}
var dragging = false;
wave.addEventListener("mousedown", function (e) { dragging = true; seekFromEvent(e); });
window.addEventListener("mousemove", function (e) { if (dragging) seekFromEvent(e); });
window.addEventListener("mouseup", function () { dragging = false; });
wave.addEventListener("touchstart", function (e) { seekFromEvent(e); }, { passive: true });
wave.addEventListener("touchmove", function (e) { seekFromEvent(e); }, { passive: true });
wave.addEventListener("keydown", function (e) {
var step = 0;
if (e.key === "ArrowRight") step = 15;
else if (e.key === "ArrowLeft") step = -15;
else if (e.key === "Home") { current = 0; render(); e.preventDefault(); return; }
else if (e.key === "End") { current = DURATION; render(); e.preventDefault(); return; }
else return;
current = Math.min(DURATION, Math.max(0, current + step));
render();
e.preventDefault();
});
/* ---------- chapters ---------- */
chapterEls.forEach(function (li) {
var btn = li.querySelector("button");
btn.addEventListener("click", function () {
current = +btn.dataset.t;
render();
if (!playing) play();
toast("Jumped to “" + btn.querySelector(".ch-name").textContent + "”");
});
});
/* ---------- transcript toggle ---------- */
var toggleTr = $("#toggleTr");
var transcript = $("#transcript");
toggleTr.addEventListener("click", function () {
var open = transcript.classList.toggle("open");
toggleTr.textContent = open ? "Collapse" : "Expand";
toggleTr.setAttribute("aria-expanded", open ? "true" : "false");
});
/* ---------- save ---------- */
var saveBtn = $("#save");
saveBtn.addEventListener("click", function () {
var on = saveBtn.getAttribute("aria-pressed") === "true";
saveBtn.setAttribute("aria-pressed", on ? "false" : "true");
toast(on ? "Removed from saved" : "Saved to your library");
});
/* ---------- share ---------- */
$$(".chip[data-share]").forEach(function (c) {
c.addEventListener("click", function () { toast("Sharing to " + c.dataset.share); });
});
$("#copyLink").addEventListener("click", function () {
var url = "https://signalandnoise.fm/ep/47";
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(url).then(
function () { toast("Link copied to clipboard"); },
function () { toast("Copy this: " + url); }
);
} else {
toast("Copy this: " + url);
}
});
/* ---------- related ---------- */
$$("#related button").forEach(function (b) {
b.addEventListener("click", function () {
toast("Loading “" + b.querySelector(".r-title").textContent + "”");
});
});
render();
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Signal & Noise — Episode 47: The Latency Trap</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&family=Sora:wght@600;700;800&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="glow-bg" aria-hidden="true"></div>
<header class="topbar">
<a class="brand" href="#" aria-label="Signal and Noise home">
<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="topnav" aria-label="Primary">
<a href="#">Episodes</a>
<a href="#">Guests</a>
<a href="#">About</a>
</nav>
<button class="btn-sub" type="button">Subscribe</button>
</header>
<main class="wrap">
<a class="crumb" href="#">← All episodes</a>
<!-- ===== PLAYER HERO ===== -->
<section class="player" aria-label="Episode player">
<div class="player-art">
<div class="cover" role="img" aria-label="Episode cover artwork">
<span class="cover-ep">EP 47</span>
<span class="cover-eq" aria-hidden="true"><i></i><i></i><i></i><i></i><i></i></span>
</div>
</div>
<div class="player-main">
<div class="badges">
<span class="badge badge-live">Season 5</span>
<span class="badge">Engineering</span>
<span class="badge badge-dot">New</span>
</div>
<h1 class="ep-title">The Latency Trap: Building Real-Time When the Network Fights Back</h1>
<p class="ep-meta">
<span>Episode 47</span><span class="sep">•</span>
<span>June 28, 2026</span><span class="sep">•</span>
<span>52 min</span>
</p>
<div class="controls">
<button class="btn-round" id="rewind" type="button" aria-label="Rewind 15 seconds">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M11 5V2L6 6l5 4V7a5 5 0 1 1-5 5H4a7 7 0 1 0 7-7z" fill="currentColor"/></svg>
<em>15</em>
</button>
<button class="btn-play" id="playBtn" type="button" aria-label="Play episode" aria-pressed="false">
<svg class="ic-play" viewBox="0 0 24 24" aria-hidden="true"><path d="M8 5v14l11-7z" fill="currentColor"/></svg>
<svg class="ic-pause" viewBox="0 0 24 24" aria-hidden="true"><path d="M6 5h4v14H6zM14 5h4v14h-4z" fill="currentColor"/></svg>
</button>
<button class="btn-round" id="forward" type="button" aria-label="Forward 30 seconds">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M13 5V2l5 4-5 4V7a5 5 0 1 0 5 5h2a7 7 0 1 1-7-7z" fill="currentColor"/></svg>
<em>30</em>
</button>
<button class="btn-speed" id="speed" type="button" aria-label="Playback speed">1×</button>
<button class="btn-save" id="save" type="button" aria-pressed="false" aria-label="Save episode">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M6 3h12a1 1 0 0 1 1 1v17l-7-4-7 4V4a1 1 0 0 1 1-1z" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round"/></svg>
</button>
</div>
<div class="scrub">
<div class="wave" id="wave" role="slider" tabindex="0"
aria-label="Seek within episode" aria-valuemin="0" aria-valuemax="3120" aria-valuenow="0">
<div class="wave-fill" id="waveFill"></div>
<div class="wave-head" id="waveHead"></div>
</div>
<div class="times">
<span id="cur">0:00</span>
<span id="dur">-52:00</span>
</div>
</div>
<div class="share">
<span class="share-label">Share:</span>
<button class="chip" data-share="X" type="button">X</button>
<button class="chip" data-share="LinkedIn" type="button">LinkedIn</button>
<button class="chip" data-share="Reddit" type="button">Reddit</button>
<button class="chip chip-copy" id="copyLink" type="button">Copy link</button>
</div>
</div>
</section>
<div class="grid">
<!-- ===== LEFT COLUMN ===== -->
<div class="col-main">
<section class="card" aria-labelledby="ch-h">
<div class="card-head">
<h2 id="ch-h">Chapters</h2>
<span class="count">6 markers</span>
</div>
<ol class="chapters" id="chapters">
<li><button type="button" data-t="0"><span class="ch-time">0:00</span><span class="ch-name">Cold open & intros</span></button></li>
<li><button type="button" data-t="245"><span class="ch-time">4:05</span><span class="ch-name">Why latency lies to you</span></button></li>
<li><button type="button" data-t="812"><span class="ch-time">13:32</span><span class="ch-name">The p99 that ruined a launch</span></button></li>
<li><button type="button" data-t="1520"><span class="ch-time">25:20</span><span class="ch-name">Edge compute vs. queues</span></button></li>
<li><button type="button" data-t="2260"><span class="ch-time">37:40</span><span class="ch-name">Guest picks & tooling</span></button></li>
<li><button type="button" data-t="2880"><span class="ch-time">48:00</span><span class="ch-name">Wrap & next week</span></button></li>
</ol>
</section>
<section class="card" aria-labelledby="tr-h">
<div class="card-head">
<h2 id="tr-h">Transcript</h2>
<button class="link-btn" id="toggleTr" type="button" aria-expanded="false">Expand</button>
</div>
<div class="transcript" id="transcript">
<p data-t="0"><b class="spk spk-a">Maya</b> Welcome back to Signal and Noise. This is episode forty-seven, and today we are talking about the thing nobody wants to page you about at 3 a.m.</p>
<p data-t="245"><b class="spk spk-b">Dev</b> Latency. And the fun part is that it lies to you. Your average looks great while a slice of users is quietly suffering.</p>
<p data-t="812"><b class="spk spk-a">Maya</b> Tell me about the p99 that ruined a launch. I need the whole embarrassing story.</p>
<p data-t="1520"><b class="spk spk-b">Dev</b> So we shipped an edge cache, felt like heroes, and then a queue backed up three regions away and everything fell over.</p>
<p data-t="2260"><b class="spk spk-a">Maya</b> Okay, before we wrap — give me your tooling picks for actually seeing this stuff in production.</p>
<p data-t="2880"><b class="spk spk-b">Dev</b> Trace everything, sample smartly, and never trust a single number. That is the whole show, honestly.</p>
</div>
<div class="fade" id="trFade"></div>
</section>
<section class="card" aria-labelledby="sn-h">
<div class="card-head"><h2 id="sn-h">Show notes</h2></div>
<p class="notes-lead">A field guide to tail latency: why averages deceive, how p99 hides in queues and cold starts, and the observability habits that keep real-time systems honest under load.</p>
<h3 class="notes-sub">In this episode</h3>
<ul class="notes-list">
<li><span class="nt">04:05</span> The difference between average, median, and the tail that actually pages you</li>
<li><span class="nt">13:32</span> A launch post-mortem where a healthy dashboard hid a broken queue</li>
<li><span class="nt">25:20</span> Edge compute versus centralized queues — the real tradeoffs</li>
<li><span class="nt">37:40</span> Dev’s tooling shortlist for tracing and sampling</li>
</ul>
<h3 class="notes-sub">Links & mentions</h3>
<div class="links">
<a href="#" class="link-pill">Tail Latency Primer (PDF)</a>
<a href="#" class="link-pill">OpenTelemetry docs</a>
<a href="#" class="link-pill">The Latency Numbers gist</a>
<a href="#" class="link-pill">Dev’s newsletter</a>
</div>
</section>
</div>
<!-- ===== RIGHT COLUMN ===== -->
<aside class="col-side">
<section class="card guest" aria-labelledby="g-h">
<div class="card-head"><h2 id="g-h">Guest</h2></div>
<div class="guest-row">
<div class="avatar" aria-hidden="true">DL</div>
<div>
<p class="guest-name">Dev Lindqvist</p>
<p class="guest-role">Principal SRE, Northwind Labs</p>
</div>
</div>
<p class="guest-bio">Dev has spent a decade chasing p99s across three continents and one very unhappy pager. They write about resilience for teams that ship in real time.</p>
<div class="social">
<button class="chip" type="button" data-share="Dev on X">@devlq</button>
<button class="chip" type="button" data-share="Dev site">northwind.dev</button>
</div>
</section>
<section class="card" aria-labelledby="re-h">
<div class="card-head"><h2 id="re-h">Related episodes</h2></div>
<ul class="related" id="related">
<li>
<button type="button">
<span class="r-cover r-c1" aria-hidden="true">46</span>
<span class="r-body">
<span class="r-title">The Cost of a Cold Start</span>
<span class="r-meta">Ep 46 • 44 min</span>
</span>
<span class="r-play" aria-hidden="true">▶</span>
</button>
</li>
<li>
<button type="button">
<span class="r-cover r-c2" aria-hidden="true">45</span>
<span class="r-body">
<span class="r-title">Queues, Backpressure & You</span>
<span class="r-meta">Ep 45 • 58 min</span>
</span>
<span class="r-play" aria-hidden="true">▶</span>
</button>
</li>
<li>
<button type="button">
<span class="r-cover r-c3" aria-hidden="true">41</span>
<span class="r-body">
<span class="r-title">Observability Without the Bill</span>
<span class="r-meta">Ep 41 • 49 min</span>
</span>
<span class="r-play" aria-hidden="true">▶</span>
</button>
</li>
</ul>
</section>
</aside>
</div>
</main>
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>A full episode detail screen for a fictional show, Signal & Noise, built around an audio-first hero. The centerpiece is a large player card: cover art with a soft violet glow, a glowing round play button, a gradient waveform you can click to scrub, live elapsed and remaining timers, and a playback-speed toggle that cycles through 1×, 1.25×, 1.5×, and 2×. A simulated clock advances the playhead so every control feels alive without any external audio.
Below the player, clickable chapter markers list the segments of the episode with their start times; selecting one seeks the playhead and lights up the active row. The transcript preview shows a handful of speaker lines that stay in sync with playback — the current line is highlighted — and a control expands or collapses the full block. Detailed show notes cover the guest bio, mentioned links, and key moments so the page reads like a real podcast landing.
Interactions are all vanilla JS: play/pause, scrubbing, chapter seeking, speed cycling, a save/bookmark toggle, and share buttons that copy the link and confirm with a small toast. A rail of related episodes rounds out the page. Everything is keyboard-operable with visible focus rings, uses WCAG-AA contrast on the dark theme, and reflows cleanly to a single column on screens down to about 360px.