Podcast — Landing Truecrime
A moody, audio-first landing page for a fictional true-crime podcast, featuring a glowing hero player with animated waveform, a dismissable content-warning banner, tabbed case teasers, an expandable season episode list with play states, listener testimonials, and a subscribe form with platform links and an inline success toast.
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));
}
* { 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; }
p { margin: 0; }
a { color: inherit; text-decoration: none; }
img { max-width: 100%; display: block; }
:focus-visible {
outline: 2px solid var(--cyan);
outline-offset: 2px;
border-radius: 4px;
}
.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;
}
button { font-family: inherit; cursor: pointer; }
/* ---------- Content warning ---------- */
.cw {
background: linear-gradient(90deg, rgba(139, 92, 246, 0.16), rgba(34, 211, 238, 0.10));
border-bottom: 1px solid var(--line);
overflow: hidden;
max-height: 200px;
transition: max-height 0.4s ease, opacity 0.3s ease, padding 0.3s ease;
}
.cw.hide { max-height: 0; opacity: 0; }
.cw-inner {
max-width: 1120px; margin: 0 auto;
padding: 12px 24px;
display: flex; align-items: center; gap: 14px;
font-size: 13.5px; color: var(--ink);
}
.cw-dot {
width: 9px; height: 9px; border-radius: 50%;
background: var(--pink); box-shadow: 0 0 12px var(--pink);
flex: none; animation: pulseDot 2s infinite;
}
.cw-inner p { flex: 1; min-width: 0; }
.cw-inner strong { color: var(--white); }
.cw-close {
flex: none;
background: rgba(255, 255, 255, 0.08);
color: var(--ink);
border: 1px solid var(--line-2);
border-radius: 999px;
padding: 7px 16px; font-size: 13px; font-weight: 600;
transition: background 0.2s, transform 0.1s;
}
.cw-close:hover { background: rgba(255, 255, 255, 0.16); }
.cw-close:active { transform: scale(0.96); }
@keyframes pulseDot {
0%, 100% { opacity: 1; transform: scale(1); }
50% { opacity: 0.5; transform: scale(0.75); }
}
/* ---------- Nav ---------- */
.nav {
position: sticky; top: 0; z-index: 40;
display: flex; align-items: center; gap: 20px;
max-width: 1120px; margin: 0 auto;
padding: 16px 24px;
backdrop-filter: blur(10px);
}
.brand {
display: flex; align-items: center; gap: 9px;
font-weight: 800; font-size: 19px; letter-spacing: -0.03em;
}
.brand em { font-style: normal; color: var(--violet); }
.brand-mark { color: var(--cyan); font-size: 16px; }
.nav-links { display: flex; gap: 26px; margin-left: auto; font-size: 14.5px; }
.nav-links a { color: var(--muted); transition: color 0.2s; position: relative; }
.nav-links a:hover { color: var(--ink); }
.nav-cta {
background: var(--grad);
color: #0a0a12; font-weight: 700; font-size: 14px;
padding: 9px 18px; border-radius: 999px;
box-shadow: 0 6px 22px rgba(139, 92, 246, 0.35);
transition: transform 0.15s, box-shadow 0.2s;
}
.nav-cta:hover { transform: translateY(-1px); box-shadow: 0 10px 30px rgba(139, 92, 246, 0.5); }
/* ---------- Hero ---------- */
.hero {
position: relative;
max-width: 1120px; margin: 0 auto;
padding: 60px 24px 40px;
}
.hero-glow {
position: absolute; inset: -40% 20% auto -20%;
height: 620px;
background: radial-gradient(closest-side, rgba(139, 92, 246, 0.28), transparent 70%),
radial-gradient(closest-side, rgba(34, 211, 238, 0.16), transparent 70%);
background-position: 20% 0, 80% 30%;
background-repeat: no-repeat;
filter: blur(6px);
pointer-events: none; z-index: 0;
}
.hero-copy { position: relative; z-index: 1; max-width: 720px; }
.eyebrow {
display: inline-flex; align-items: center; gap: 9px;
background: var(--surface); border: 1px solid var(--line);
color: var(--muted); font-size: 12.5px; font-weight: 600;
letter-spacing: 0.04em; text-transform: uppercase;
padding: 7px 14px; border-radius: 999px; margin-bottom: 22px;
}
.pulse {
width: 8px; height: 8px; border-radius: 50%;
background: var(--cyan); box-shadow: 0 0 10px var(--cyan);
animation: pulseDot 1.8s infinite;
}
h1 {
font-family: "Playfair Display", Georgia, serif;
font-weight: 800; font-size: clamp(38px, 7vw, 68px);
line-height: 1.02; margin-bottom: 20px;
}
.grad {
background: var(--grad);
-webkit-background-clip: text; background-clip: text;
-webkit-text-fill-color: transparent;
}
.lede { color: var(--muted); font-size: 17.5px; max-width: 620px; margin-bottom: 32px; }
/* Hero player */
.hero-player {
display: flex; align-items: center; gap: 20px;
background: var(--surface);
border: 1px solid var(--line);
border-radius: var(--r-lg);
padding: 18px 22px; max-width: 620px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.4);
}
.play-btn {
flex: none;
width: 62px; height: 62px; border-radius: 50%;
border: none; background: var(--grad);
display: grid; place-items: center;
box-shadow: 0 0 0 0 rgba(139, 92, 246, 0.5), 0 10px 30px rgba(139, 92, 246, 0.4);
transition: transform 0.15s, box-shadow 0.3s;
position: relative;
}
.play-btn::after {
content: ""; position: absolute; inset: -6px; border-radius: 50%;
border: 1.5px solid rgba(139, 92, 246, 0.4);
animation: ring 2.6s ease-out infinite;
}
@keyframes ring {
0% { transform: scale(1); opacity: 0.8; }
100% { transform: scale(1.5); opacity: 0; }
}
.play-btn:hover { transform: scale(1.06); }
.play-btn:active { transform: scale(0.97); }
.tri {
width: 0; height: 0;
border-left: 18px solid #0a0a12;
border-top: 11px solid transparent;
border-bottom: 11px solid transparent;
margin-left: 4px;
}
.tri.sm { border-left-width: 11px; border-top-width: 7px; border-bottom-width: 7px; margin-left: 3px; }
.play-btn.playing .tri {
width: 16px; height: 16px; border: none; margin: 0;
background:
linear-gradient(#0a0a12, #0a0a12) left / 5px 100% no-repeat,
linear-gradient(#0a0a12, #0a0a12) right / 5px 100% no-repeat;
}
.hp-meta { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 8px; }
.hp-ep { font-weight: 700; font-size: 15px; }
.hp-time { color: var(--muted); font-size: 12.5px; }
.wave {
display: flex; align-items: flex-end; gap: 3px; height: 30px;
}
.wave span {
flex: 1; border-radius: 3px;
background: var(--grad);
opacity: 0.65;
height: 30%;
}
.wave.on span { animation: bar 1s ease-in-out infinite; }
@keyframes bar {
0%, 100% { height: 25%; }
50% { height: 100%; }
}
.stats {
list-style: none; padding: 0; margin: 34px 0 0;
display: flex; flex-wrap: wrap; gap: 34px;
}
.stats li { display: flex; flex-direction: column; }
.stats b { font-size: 26px; font-weight: 800; letter-spacing: -0.03em; }
.stats span { color: var(--muted); font-size: 12.5px; }
/* ---------- Section shared ---------- */
.sec-head { display: flex; align-items: center; gap: 18px; flex-wrap: wrap; margin-bottom: 26px; }
.sec-head h2 {
font-family: "Playfair Display", Georgia, serif;
font-size: clamp(26px, 4vw, 36px); font-weight: 700;
}
.sec-sub { color: var(--muted); font-size: 14px; }
.cases, .seasons, .voices { max-width: 1120px; margin: 0 auto; padding: 40px 24px; }
/* ---------- Tabs ---------- */
.tabs {
margin-left: auto; display: flex; gap: 6px;
background: var(--surface); border: 1px solid var(--line);
padding: 5px; border-radius: 999px;
}
.tab {
border: none; background: transparent; color: var(--muted);
font-size: 13.5px; font-weight: 600; padding: 8px 16px;
border-radius: 999px; transition: color 0.2s, background 0.2s;
}
.tab:hover { color: var(--ink); }
.tab.is-active { color: #0a0a12; background: var(--grad); }
/* ---------- Case grid ---------- */
.case-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; }
.case {
background: var(--surface); border: 1px solid var(--line);
border-radius: var(--r-lg); overflow: hidden;
transition: transform 0.2s, border-color 0.2s, box-shadow 0.3s;
animation: fade 0.4s both;
}
.case.hidden { display: none; }
.case:hover {
transform: translateY(-4px);
border-color: var(--line-2);
box-shadow: 0 18px 40px rgba(0, 0, 0, 0.45);
}
.case-art {
height: 130px; background: var(--img, var(--surface-2));
position: relative; border-bottom: 1px solid var(--line);
}
.case-art::before {
content: ""; position: absolute; inset: 0;
background: radial-gradient(120% 120% at 80% 20%, rgba(139, 92, 246, 0.22), transparent 60%);
}
.badge {
position: absolute; top: 12px; left: 12px;
font-size: 11px; font-weight: 700; letter-spacing: 0.03em;
text-transform: uppercase; padding: 5px 11px; border-radius: 999px;
backdrop-filter: blur(4px);
}
.b-unsolved { background: rgba(244, 114, 182, 0.18); color: var(--pink); border: 1px solid rgba(244, 114, 182, 0.4); }
.b-cold { background: rgba(34, 211, 238, 0.16); color: var(--cyan); border: 1px solid rgba(34, 211, 238, 0.4); }
.b-solved { background: rgba(139, 92, 246, 0.18); color: #c4b5fd; border: 1px solid rgba(139, 92, 246, 0.4); }
.case-body { padding: 18px; }
.case-body h3 { font-size: 17px; margin-bottom: 8px; }
.case-body p { color: var(--muted); font-size: 14px; }
.case-meta {
display: flex; gap: 16px; margin-top: 14px;
color: var(--muted); font-size: 12.5px;
}
@keyframes fade { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
/* ---------- Seasons / accordion ---------- */
.accordion { display: flex; flex-direction: column; gap: 12px; }
.season {
background: var(--surface); border: 1px solid var(--line);
border-radius: var(--r-md); overflow: hidden;
transition: border-color 0.2s;
}
.season.is-open { border-color: var(--line-2); }
.season-head {
width: 100%; display: flex; align-items: center; gap: 16px;
background: transparent; border: none; color: var(--ink);
padding: 18px 20px; text-align: left;
}
.s-num {
font-weight: 800; font-size: 13px; color: #0a0a12;
background: var(--grad); border-radius: var(--r-sm);
padding: 5px 9px; flex: none;
}
.s-title { font-weight: 700; font-size: 16.5px; flex: 1; min-width: 0; }
.s-count { color: var(--muted); font-size: 13px; }
.chev { color: var(--muted); transition: transform 0.3s; font-size: 13px; }
.season.is-open .chev { transform: rotate(180deg); color: var(--cyan); }
.ep-list {
list-style: none; margin: 0; padding: 0 12px;
max-height: 0; overflow: hidden;
transition: max-height 0.4s ease, padding 0.3s ease;
}
.season.is-open .ep-list { max-height: 520px; padding: 0 12px 12px; }
.ep {
display: flex; align-items: center; gap: 14px;
padding: 11px 10px; border-radius: var(--r-sm);
border-top: 1px solid var(--line);
transition: background 0.2s;
}
.ep:first-child { border-top: none; }
.ep:hover { background: var(--surface-2); }
.ep-play {
flex: none; width: 38px; height: 38px; border-radius: 50%;
border: 1px solid var(--line-2); background: var(--surface-2);
display: grid; place-items: center;
transition: background 0.2s, transform 0.1s, box-shadow 0.3s;
}
.ep-play:hover { background: rgba(139, 92, 246, 0.2); }
.ep-play:active { transform: scale(0.94); }
.ep-play.active {
background: var(--grad); border-color: transparent;
box-shadow: 0 0 18px rgba(139, 92, 246, 0.5);
}
.ep-play.active .tri {
width: 12px; height: 12px; border: none; margin: 0;
background:
linear-gradient(#0a0a12, #0a0a12) left / 4px 100% no-repeat,
linear-gradient(#0a0a12, #0a0a12) right / 4px 100% no-repeat;
}
.ep-info { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.ep-title { font-weight: 600; font-size: 14.5px; }
.ep-sub { color: var(--muted); font-size: 12.5px; }
.ep-tag {
font-size: 11px; font-weight: 700; padding: 4px 9px; border-radius: 999px;
color: var(--cyan); background: rgba(34, 211, 238, 0.14);
border: 1px solid rgba(34, 211, 238, 0.35);
}
/* ---------- Testimonials ---------- */
.voice-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; }
.quote {
margin: 0; background: var(--surface); border: 1px solid var(--line);
border-radius: var(--r-lg); padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.quote:hover { transform: translateY(-3px); border-color: var(--line-2); }
.stars { color: var(--cyan); font-size: 15px; letter-spacing: 2px; margin-bottom: 14px; }
.quote blockquote { margin: 0; font-size: 15px; color: var(--ink); }
.quote figcaption {
display: flex; align-items: center; gap: 10px;
margin-top: 18px; color: var(--muted); font-size: 13px;
}
.av {
width: 34px; height: 34px; border-radius: 50%; flex: none;
display: grid; place-items: center;
font-size: 12px; font-weight: 700; color: #0a0a12;
background: var(--c, var(--violet));
}
/* ---------- Subscribe ---------- */
.subscribe { position: relative; padding: 60px 24px 80px; }
.sub-glow {
position: absolute; inset: 0; pointer-events: none;
background: radial-gradient(closest-side at 50% 40%, rgba(139, 92, 246, 0.22), transparent 70%);
}
.sub-card {
position: relative; z-index: 1;
max-width: 640px; margin: 0 auto; text-align: center;
background: var(--surface); border: 1px solid var(--line-2);
border-radius: var(--r-lg); padding: 44px 34px;
box-shadow: 0 30px 80px rgba(0, 0, 0, 0.5);
}
.sub-card h2 {
font-family: "Playfair Display", Georgia, serif;
font-size: clamp(24px, 4vw, 34px); font-weight: 700; margin-bottom: 12px;
}
.sub-card > p { color: var(--muted); font-size: 15.5px; margin-bottom: 26px; }
.platforms { display: flex; flex-wrap: wrap; justify-content: center; gap: 10px; margin-bottom: 24px; }
.plat {
background: var(--surface-2); color: var(--ink);
border: 1px solid var(--line); border-radius: 999px;
padding: 9px 16px; font-size: 13.5px; font-weight: 600;
transition: border-color 0.2s, background 0.2s, transform 0.1s;
}
.plat:hover { border-color: var(--violet); background: rgba(139, 92, 246, 0.14); }
.plat:active { transform: scale(0.96); }
.plat.picked { border-color: var(--cyan); background: rgba(34, 211, 238, 0.14); color: var(--cyan); }
.sub-form { display: flex; gap: 10px; max-width: 460px; margin: 0 auto; }
.sub-form input {
flex: 1; min-width: 0;
background: var(--bg); border: 1px solid var(--line-2);
color: var(--ink); font-size: 15px;
padding: 13px 16px; border-radius: var(--r-md);
transition: border-color 0.2s, box-shadow 0.2s;
}
.sub-form input::placeholder { color: var(--muted); }
.sub-form input:focus { border-color: var(--violet); box-shadow: 0 0 0 3px rgba(139, 92, 246, 0.25); outline: none; }
.sub-form input.invalid { border-color: var(--pink); box-shadow: 0 0 0 3px rgba(244, 114, 182, 0.2); }
.sub-form button {
background: var(--grad); color: #0a0a12;
border: none; font-weight: 700; font-size: 15px;
padding: 13px 24px; border-radius: var(--r-md);
box-shadow: 0 8px 26px rgba(139, 92, 246, 0.4);
transition: transform 0.15s, box-shadow 0.2s;
}
.sub-form button:hover { transform: translateY(-1px); box-shadow: 0 12px 34px rgba(139, 92, 246, 0.55); }
.sub-form button:active { transform: translateY(0); }
.sub-err { color: var(--pink); font-size: 13px; min-height: 18px; margin-top: 10px; }
.sub-fine { color: var(--muted); font-size: 12.5px; margin-top: 8px; }
/* ---------- Footer ---------- */
.foot {
max-width: 1120px; margin: 0 auto; padding: 30px 24px 60px;
display: flex; justify-content: space-between; flex-wrap: wrap; gap: 12px;
border-top: 1px solid var(--line); color: var(--muted); font-size: 13px;
}
/* ---------- Mini player ---------- */
.miniplayer {
position: fixed; left: 50%; bottom: 20px; transform: translate(-50%, 140%);
z-index: 60; width: min(560px, calc(100% - 32px));
display: flex; align-items: center; gap: 14px;
background: var(--surface-2); border: 1px solid var(--line-2);
border-radius: var(--r-md); padding: 12px 16px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.6);
transition: transform 0.4s cubic-bezier(0.2, 0.8, 0.2, 1);
}
.miniplayer.show { transform: translate(-50%, 0); }
.mini-toggle {
flex: none; width: 42px; height: 42px; border-radius: 50%;
border: none; background: var(--grad);
display: grid; place-items: center;
box-shadow: 0 0 18px rgba(139, 92, 246, 0.5);
}
.pausebars { display: flex; gap: 4px; }
.pausebars i { width: 4px; height: 15px; background: #0a0a12; border-radius: 2px; }
.mini-toggle.paused .pausebars {
width: 0; height: 0; gap: 0;
border-left: 14px solid #0a0a12;
border-top: 9px solid transparent;
border-bottom: 9px solid transparent;
margin-left: 3px;
}
.mini-toggle.paused .pausebars i { display: none; }
.mini-info { flex: 1; min-width: 0; }
.mini-title { display: block; font-weight: 600; font-size: 14px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mini-bar { height: 4px; background: var(--line); border-radius: 4px; margin-top: 8px; overflow: hidden; }
.mini-bar span { display: block; height: 100%; width: 0; background: var(--grad); transition: width 0.3s linear; }
.mini-close {
flex: none; background: transparent; border: none;
color: var(--muted); font-size: 15px; padding: 6px;
transition: color 0.2s;
}
.mini-close:hover { color: var(--ink); }
/* ---------- Toast ---------- */
.toast {
position: fixed; left: 50%; bottom: 90px;
transform: translate(-50%, 20px);
background: var(--surface-2); color: var(--ink);
border: 1px solid var(--cyan);
padding: 13px 22px; border-radius: var(--r-md);
font-size: 14px; font-weight: 600; z-index: 80;
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.5);
opacity: 0; pointer-events: none;
transition: opacity 0.3s, transform 0.3s;
}
.toast.show { opacity: 1; transform: translate(-50%, 0); }
/* ---------- Responsive ---------- */
@media (max-width: 860px) {
.case-grid, .voice-grid { grid-template-columns: 1fr 1fr; }
.nav-links { display: none; }
}
@media (max-width: 520px) {
.cw-inner { flex-wrap: wrap; padding: 12px 18px; }
.cw-inner p { flex-basis: 100%; }
.nav { padding: 14px 18px; }
.nav-cta { display: none; }
.hero { padding: 36px 18px 20px; }
.hero-player { flex-direction: column; align-items: stretch; text-align: left; }
.play-btn { width: 54px; height: 54px; }
.stats { gap: 22px; }
.stats b { font-size: 22px; }
.sec-head { margin-bottom: 20px; }
.tabs { margin-left: 0; width: 100%; justify-content: space-between; }
.tab { padding: 8px 10px; flex: 1; }
.cases, .seasons, .voices { padding: 30px 18px; }
.case-grid, .voice-grid { grid-template-columns: 1fr; }
.season-head { gap: 12px; padding: 16px; }
.s-count { display: none; }
.subscribe { padding: 40px 18px 70px; }
.sub-card { padding: 30px 22px; }
.sub-form { flex-direction: column; }
.foot { flex-direction: column; }
.miniplayer { bottom: 12px; padding: 10px 12px; }
.toast { bottom: 80px; width: calc(100% - 36px); text-align: center; }
}
@media (prefers-reduced-motion: reduce) {
* { animation: none !important; scroll-behavior: auto; transition-duration: 0.01ms !important; }
}(function () {
"use strict";
/* ---------- Toast helper ---------- */
var toastEl = document.getElementById("toast");
var toastTimer;
function toast(msg) {
toastEl.textContent = msg;
toastEl.classList.add("show");
clearTimeout(toastTimer);
toastTimer = setTimeout(function () {
toastEl.classList.remove("show");
}, 2600);
}
/* ---------- Build hero waveform ---------- */
var wave = document.getElementById("wave");
var BARS = 40;
for (var i = 0; i < BARS; i++) {
var s = document.createElement("span");
var h = 20 + Math.round(Math.abs(Math.sin(i * 0.9)) * 70);
s.style.height = h + "%";
s.style.animationDelay = (i * 0.045) + "s";
wave.appendChild(s);
}
/* ---------- Content warning ---------- */
var cw = document.getElementById("cw");
document.getElementById("cwClose").addEventListener("click", function () {
cw.classList.add("hide");
});
/* ---------- Shared mini player ---------- */
var mini = document.getElementById("mini");
var miniToggle = document.getElementById("miniToggle");
var miniClose = document.getElementById("miniClose");
var miniTitle = document.getElementById("miniTitle");
var miniProg = document.getElementById("miniProg");
var progTimer;
var progress = 0;
function runProgress() {
clearInterval(progTimer);
progTimer = setInterval(function () {
progress = (progress + 0.6) % 100;
miniProg.style.width = progress + "%";
}, 400);
}
function stopProgress() { clearInterval(progTimer); }
function openMini(title) {
progress = 0;
miniTitle.textContent = title;
miniProg.style.width = "0%";
mini.classList.add("show");
mini.setAttribute("aria-hidden", "false");
miniToggle.classList.remove("paused");
miniToggle.setAttribute("aria-label", "Pause");
runProgress();
}
function closeMini() {
mini.classList.remove("show");
mini.setAttribute("aria-hidden", "true");
stopProgress();
clearActive();
setHero(false);
}
miniClose.addEventListener("click", closeMini);
miniToggle.addEventListener("click", function () {
var paused = miniToggle.classList.toggle("paused");
if (paused) {
stopProgress();
miniToggle.setAttribute("aria-label", "Play");
pauseAll();
} else {
runProgress();
miniToggle.setAttribute("aria-label", "Pause");
}
});
/* ---------- Hero play ---------- */
var heroPlay = document.getElementById("heroPlay");
var heroTitle = "S4 · E01 — The Harbor Light Killings";
function setHero(on) {
heroPlay.classList.toggle("playing", on);
heroPlay.setAttribute("aria-pressed", String(on));
wave.classList.toggle("on", on);
}
heroPlay.addEventListener("click", function () {
var on = !heroPlay.classList.contains("playing");
if (on) {
clearActive();
setHero(true);
openMini(heroTitle);
toast("Now playing · " + heroTitle);
} else {
setHero(false);
closeMini();
}
});
/* ---------- Episode play buttons ---------- */
var epButtons = Array.prototype.slice.call(document.querySelectorAll(".ep-play"));
function clearActive() {
epButtons.forEach(function (b) { b.classList.remove("active"); });
}
function pauseAll() {
// visually keep active marker but stop hero wave animation
wave.classList.remove("on");
}
epButtons.forEach(function (btn) {
btn.addEventListener("click", function () {
var isActive = btn.classList.contains("active");
clearActive();
setHero(false);
if (isActive) {
closeMini();
return;
}
btn.classList.add("active");
var ep = btn.closest(".ep");
var title = ep.querySelector(".ep-title").textContent;
openMini(title);
toast("Now playing · " + title);
});
});
/* ---------- Case tabs / filter ---------- */
var tabs = Array.prototype.slice.call(document.querySelectorAll(".tab"));
var cases = Array.prototype.slice.call(document.querySelectorAll(".case"));
tabs.forEach(function (tab) {
tab.addEventListener("click", function () {
tabs.forEach(function (t) {
t.classList.remove("is-active");
t.setAttribute("aria-selected", "false");
});
tab.classList.add("is-active");
tab.setAttribute("aria-selected", "true");
var f = tab.getAttribute("data-filter");
var shown = 0;
cases.forEach(function (c) {
var match = f === "all" || c.getAttribute("data-status") === f;
c.classList.toggle("hidden", !match);
if (match) {
shown++;
c.style.animation = "none";
// force reflow to restart entry animation
void c.offsetWidth;
c.style.animation = "";
}
});
toast(shown + (shown === 1 ? " case" : " cases") + " shown");
});
});
/* ---------- Season accordion ---------- */
var heads = Array.prototype.slice.call(document.querySelectorAll(".season-head"));
heads.forEach(function (head) {
head.addEventListener("click", function () {
var season = head.parentElement;
var open = season.classList.contains("is-open");
if (open) {
season.classList.remove("is-open");
head.setAttribute("aria-expanded", "false");
} else {
season.classList.add("is-open");
head.setAttribute("aria-expanded", "true");
}
});
});
/* ---------- Platform picker ---------- */
var plats = Array.prototype.slice.call(document.querySelectorAll(".plat"));
plats.forEach(function (p) {
p.addEventListener("click", function () {
plats.forEach(function (x) { x.classList.remove("picked"); });
p.classList.add("picked");
toast("Opening " + p.getAttribute("data-plat") + "…");
});
});
/* ---------- Subscribe form ---------- */
var form = document.getElementById("subForm");
var email = document.getElementById("email");
var err = document.getElementById("subErr");
var re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
form.addEventListener("submit", function (e) {
e.preventDefault();
var val = email.value.trim();
if (!re.test(val)) {
email.classList.add("invalid");
err.textContent = "Please enter a valid email address.";
email.focus();
return;
}
email.classList.remove("invalid");
err.textContent = "";
email.value = "";
toast("You're on the list — case notes incoming.");
});
email.addEventListener("input", function () {
if (email.classList.contains("invalid") && re.test(email.value.trim())) {
email.classList.remove("invalid");
err.textContent = "";
}
});
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Cold File — A True-Crime Podcast</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=Playfair+Display:ital,wght@0,700;0,800;1,700&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<!-- Content warning -->
<div class="cw" id="cw" role="region" aria-label="Content warning">
<div class="cw-inner">
<span class="cw-dot" aria-hidden="true"></span>
<p><strong>Content warning.</strong> Cold File discusses violent crime, homicide, and topics some listeners may find distressing. Listener discretion is advised.</p>
<button class="cw-close" id="cwClose" aria-label="Dismiss content warning">Understood</button>
</div>
</div>
<header class="nav">
<a class="brand" href="#top">
<span class="brand-mark" aria-hidden="true">◈</span>
<span>Cold<em>File</em></span>
</a>
<nav class="nav-links" aria-label="Primary">
<a href="#cases">Cases</a>
<a href="#seasons">Seasons</a>
<a href="#voices">Listeners</a>
<a href="#subscribe">Subscribe</a>
</nav>
<a class="nav-cta" href="#subscribe">Follow the show</a>
</header>
<main id="top">
<!-- Hero -->
<section class="hero">
<div class="hero-glow" aria-hidden="true"></div>
<div class="hero-copy">
<span class="eyebrow"><span class="pulse" aria-hidden="true"></span>New season now recording</span>
<h1>Every case leaves a <span class="grad">cold file.</span></h1>
<p class="lede">A weekly investigative podcast reopening forgotten cases — the evidence, the timelines, and the questions the record never answered. Hosted by Dana Reyes and Marcus Vale.</p>
<div class="hero-player">
<button class="play-btn" id="heroPlay" aria-pressed="false" aria-label="Play the latest episode">
<span class="tri" aria-hidden="true"></span>
</button>
<div class="hp-meta">
<span class="hp-ep">S4 · E01 — The Harbor Light Killings</span>
<div class="wave" id="wave" aria-hidden="true"></div>
<span class="hp-time">48 min · Released this week</span>
</div>
</div>
<ul class="stats" aria-label="Show statistics">
<li><b>142</b><span>Episodes aired</span></li>
<li><b>19</b><span>Cases reopened</span></li>
<li><b>4.9</b><span>Avg. rating</span></li>
<li><b>3.2M</b><span>Monthly listens</span></li>
</ul>
</div>
</section>
<!-- Case teasers -->
<section class="cases" id="cases">
<div class="sec-head">
<h2>Case files</h2>
<div class="tabs" role="tablist" aria-label="Filter cases by status">
<button role="tab" class="tab is-active" aria-selected="true" data-filter="all">All</button>
<button role="tab" class="tab" aria-selected="false" data-filter="unsolved">Unsolved</button>
<button role="tab" class="tab" aria-selected="false" data-filter="cold">Cold</button>
<button role="tab" class="tab" aria-selected="false" data-filter="solved">Solved</button>
</div>
</div>
<div class="case-grid" id="caseGrid">
<article class="case" data-status="unsolved" style="--img:linear-gradient(135deg,#2a1840,#0c0c10)">
<div class="case-art"><span class="badge b-unsolved">Unsolved</span></div>
<div class="case-body">
<h3>The Harbor Light Killings</h3>
<p>Three vanished from a lighthouse town in one winter. The keeper's log ends mid-sentence.</p>
<div class="case-meta"><span>◷ 48 min</span><span>◉ Astoria, OR</span></div>
</div>
</article>
<article class="case" data-status="cold" style="--img:linear-gradient(135deg,#0a2a2e,#0c0c10)">
<div class="case-art"><span class="badge b-cold">Cold case</span></div>
<div class="case-body">
<h3>Letters from Room 14</h3>
<p>A motel guest checked in under six names. Decades later, the postcards keep arriving.</p>
<div class="case-meta"><span>◷ 52 min</span><span>◉ Barstow, CA</span></div>
</div>
</article>
<article class="case" data-status="solved" style="--img:linear-gradient(135deg,#3a1030,#0c0c10)">
<div class="case-art"><span class="badge b-solved">Solved</span></div>
<div class="case-body">
<h3>The Greenline Confession</h3>
<p>A tramway ticket, a mismatched alibi, and the twelve-year silence that finally broke.</p>
<div class="case-meta"><span>◷ 61 min</span><span>◉ Pittsburgh, PA</span></div>
</div>
</article>
<article class="case" data-status="unsolved" style="--img:linear-gradient(135deg,#241a3f,#0c0c10)">
<div class="case-art"><span class="badge b-unsolved">Unsolved</span></div>
<div class="case-body">
<h3>Nobody Called It In</h3>
<p>A whole street heard the alarm at 3:14 a.m. Every neighbor swears they slept through it.</p>
<div class="case-meta"><span>◷ 44 min</span><span>◉ Flint, MI</span></div>
</div>
</article>
<article class="case" data-status="cold" style="--img:linear-gradient(135deg,#102a1a,#0c0c10)">
<div class="case-art"><span class="badge b-cold">Cold case</span></div>
<div class="case-body">
<h3>The Ferry That Never Docked</h3>
<p>Forty-one passengers boarded at dusk. The manifest and the harbor tell two stories.</p>
<div class="case-meta"><span>◷ 57 min</span><span>◉ Nantucket, MA</span></div>
</div>
</article>
<article class="case" data-status="solved" style="--img:linear-gradient(135deg,#301028,#0c0c10)">
<div class="case-art"><span class="badge b-solved">Solved</span></div>
<div class="case-body">
<h3>Static on the Scanner</h3>
<p>An amateur radio hobbyist recorded a crime as it happened — and didn't know for years.</p>
<div class="case-meta"><span>◷ 39 min</span><span>◉ Tulsa, OK</span></div>
</div>
</article>
</div>
</section>
<!-- Seasons -->
<section class="seasons" id="seasons">
<div class="sec-head"><h2>Seasons & episodes</h2><p class="sec-sub">Tap a season to open the episode list.</p></div>
<div class="accordion" id="accordion">
<div class="season is-open">
<button class="season-head" aria-expanded="true">
<span class="s-num">S4</span>
<span class="s-title">The Harbor Light Killings</span>
<span class="s-count">4 episodes</span>
<span class="chev" aria-hidden="true">▾</span>
</button>
<ul class="ep-list">
<li class="ep">
<button class="ep-play" aria-label="Play episode 1"><span class="tri sm" aria-hidden="true"></span></button>
<div class="ep-info"><span class="ep-title">E01 · The keeper's last log</span><span class="ep-sub">48 min · Evidence intro</span></div>
<span class="ep-tag new">New</span>
</li>
<li class="ep">
<button class="ep-play" aria-label="Play episode 2"><span class="tri sm" aria-hidden="true"></span></button>
<div class="ep-info"><span class="ep-title">E02 · The winter three</span><span class="ep-sub">51 min · Timeline</span></div>
</li>
<li class="ep">
<button class="ep-play" aria-label="Play episode 3"><span class="tri sm" aria-hidden="true"></span></button>
<div class="ep-info"><span class="ep-title">E03 · Tidewater witnesses</span><span class="ep-sub">46 min · Interviews</span></div>
</li>
<li class="ep">
<button class="ep-play" aria-label="Play episode 4"><span class="tri sm" aria-hidden="true"></span></button>
<div class="ep-info"><span class="ep-title">E04 · What the lamp saw</span><span class="ep-sub">55 min · Theory</span></div>
</li>
</ul>
</div>
<div class="season">
<button class="season-head" aria-expanded="false">
<span class="s-num">S3</span>
<span class="s-title">Letters from Room 14</span>
<span class="s-count">6 episodes</span>
<span class="chev" aria-hidden="true">▾</span>
</button>
<ul class="ep-list">
<li class="ep">
<button class="ep-play" aria-label="Play S3 episode 1"><span class="tri sm" aria-hidden="true"></span></button>
<div class="ep-info"><span class="ep-title">E01 · Six names, one guest</span><span class="ep-sub">42 min</span></div>
</li>
<li class="ep">
<button class="ep-play" aria-label="Play S3 episode 2"><span class="tri sm" aria-hidden="true"></span></button>
<div class="ep-info"><span class="ep-title">E02 · The postmark trail</span><span class="ep-sub">49 min</span></div>
</li>
<li class="ep">
<button class="ep-play" aria-label="Play S3 episode 3"><span class="tri sm" aria-hidden="true"></span></button>
<div class="ep-info"><span class="ep-title">E03 · Handwriting analysis</span><span class="ep-sub">47 min</span></div>
</li>
</ul>
</div>
<div class="season">
<button class="season-head" aria-expanded="false">
<span class="s-num">S2</span>
<span class="s-title">The Greenline Confession</span>
<span class="s-count">5 episodes</span>
<span class="chev" aria-hidden="true">▾</span>
</button>
<ul class="ep-list">
<li class="ep">
<button class="ep-play" aria-label="Play S2 episode 1"><span class="tri sm" aria-hidden="true"></span></button>
<div class="ep-info"><span class="ep-title">E01 · The mismatched alibi</span><span class="ep-sub">58 min</span></div>
</li>
<li class="ep">
<button class="ep-play" aria-label="Play S2 episode 2"><span class="tri sm" aria-hidden="true"></span></button>
<div class="ep-info"><span class="ep-title">E02 · Twelve years of silence</span><span class="ep-sub">61 min</span></div>
</li>
</ul>
</div>
</div>
</section>
<!-- Testimonials -->
<section class="voices" id="voices">
<div class="sec-head"><h2>What listeners say</h2></div>
<div class="voice-grid">
<figure class="quote">
<div class="stars" aria-label="5 out of 5 stars">★★★★★</div>
<blockquote>The pacing is relentless. I finished the Harbor Light arc in one sleepless night and immediately started it again.</blockquote>
<figcaption><span class="av" style="--c:var(--violet)">JR</span> Jordan R. · Verified listener</figcaption>
</figure>
<figure class="quote">
<div class="stars" aria-label="5 out of 5 stars">★★★★★</div>
<blockquote>Meticulously sourced without being dry. You can hear how much they respect the people behind each case.</blockquote>
<figcaption><span class="av" style="--c:var(--cyan)">MA</span> Mara A. · Verified listener</figcaption>
</figure>
<figure class="quote">
<div class="stars" aria-label="5 out of 5 stars">★★★★★</div>
<blockquote>The only true-crime show I trust to say I don't know when the evidence runs out. That honesty is rare.</blockquote>
<figcaption><span class="av" style="--c:var(--pink)">DL</span> Devon L. · Verified listener</figcaption>
</figure>
</div>
</section>
<!-- Subscribe -->
<section class="subscribe" id="subscribe">
<div class="sub-glow" aria-hidden="true"></div>
<div class="sub-card">
<h2>Follow the investigation</h2>
<p>New episodes every Thursday at midnight. Get case notes, source docs, and bonus interviews in your inbox.</p>
<div class="platforms" role="group" aria-label="Listen on your platform">
<button class="plat" data-plat="Apple Podcasts">Apple Podcasts</button>
<button class="plat" data-plat="Spotify">Spotify</button>
<button class="plat" data-plat="Pocket Casts">Pocket Casts</button>
<button class="plat" data-plat="RSS">RSS</button>
</div>
<form class="sub-form" id="subForm" novalidate>
<label class="sr-only" for="email">Email address</label>
<input type="email" id="email" name="email" placeholder="[email protected]" autocomplete="email" required />
<button type="submit">Subscribe</button>
</form>
<p class="sub-err" id="subErr" role="alert"></p>
<p class="sub-fine">No spam. Unsubscribe anytime. We keep case details out of the newsletter previews.</p>
</div>
</section>
</main>
<footer class="foot">
<span>◈ Cold File Podcast · A fictional production</span>
<span>© 2026 Cold File Media · Discretion advised</span>
</footer>
<!-- Mini player -->
<div class="miniplayer" id="mini" aria-hidden="true">
<button class="mini-toggle" id="miniToggle" aria-label="Pause"><span class="pausebars" aria-hidden="true"><i></i><i></i></span></button>
<div class="mini-info">
<span class="mini-title" id="miniTitle">The keeper's last log</span>
<div class="mini-bar"><span id="miniProg"></span></div>
</div>
<button class="mini-close" id="miniClose" 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 dark, cinematic landing page for Cold File, a fictional investigative true-crime show. The hero pairs a glowing gradient play button with an animated violet-to-cyan waveform, a case-count stat row, and a dismissable content-warning banner that respects the reader before they dive in. A tabbed teaser rack lets visitors flip between unsolved, solved, and cold cases, each card carrying a status badge, location, and runtime.
Below the fold, an accordion season list expands to reveal episodes with individual play toggles that swap into an active “now playing” state and drive a shared mini-player bar. Listener testimonials and a trust-signal strip build credibility, and the subscribe section offers platform buttons plus an email form that validates inline and fires a toast on success.
Everything is vanilla JS: tab switching, accordion expand/collapse, per-episode play state, the persistent mini-player, warning dismissal, and form handling — all keyboard-accessible with visible focus rings and an aria-live toast region. The layout collapses cleanly to a single column at 520px.