Podcast — Clip Share
An audio-first audiogram builder where you scrub a gradient waveform, drag start and end handles to trim a highlight, then watch an animated share card come alive with bouncing violet-to-cyan bars, live captions and episode art. Duration, timecodes and caption text update in real time, and one-tap share buttons copy a link or push the clip to social — a dark, glowing clip studio for podcast moments.
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;
--accent: var(--violet);
}
* {
box-sizing: border-box;
}
html {
-webkit-text-size-adjust: 100%;
}
body {
margin: 0;
min-height: 100vh;
font-family: "Inter", system-ui, -apple-system, sans-serif;
line-height: 1.5;
color: var(--ink);
background:
radial-gradient(900px 500px at 15% -10%, rgba(139, 92, 246, 0.16), transparent 60%),
radial-gradient(800px 500px at 100% 0%, rgba(34, 211, 238, 0.12), transparent 55%),
var(--bg);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
display: flex;
justify-content: center;
padding: 28px 18px 64px;
}
.studio {
width: 100%;
max-width: 720px;
display: flex;
flex-direction: column;
gap: 18px;
}
/* ---------- header ---------- */
.studio__head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
}
.brand {
display: flex;
align-items: center;
gap: 14px;
}
.brand__glyph {
width: 46px;
height: 46px;
border-radius: var(--r-md);
background: linear-gradient(150deg, var(--violet-d), var(--cyan));
display: flex;
align-items: flex-end;
justify-content: center;
gap: 3px;
padding: 10px;
box-shadow: 0 10px 26px rgba(124, 58, 237, 0.4);
}
.brand__glyph span {
width: 4px;
border-radius: 4px;
background: rgba(255, 255, 255, 0.92);
animation: glyph 1.1s ease-in-out infinite;
}
.brand__glyph span:nth-child(1) { height: 40%; animation-delay: 0s; }
.brand__glyph span:nth-child(2) { height: 85%; animation-delay: 0.15s; }
.brand__glyph span:nth-child(3) { height: 55%; animation-delay: 0.3s; }
.brand__glyph span:nth-child(4) { height: 70%; animation-delay: 0.45s; }
@keyframes glyph {
0%, 100% { transform: scaleY(0.5); }
50% { transform: scaleY(1); }
}
.brand__kicker {
margin: 0;
font-size: 11px;
letter-spacing: 0.16em;
text-transform: uppercase;
color: var(--muted);
font-weight: 600;
}
.brand__title {
margin: 0;
font-size: 20px;
font-weight: 800;
letter-spacing: -0.02em;
}
.pill {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 7px 13px;
border-radius: 999px;
background: var(--surface-2);
border: 1px solid var(--line);
font-size: 12.5px;
font-weight: 600;
color: var(--muted);
white-space: nowrap;
}
.pill__dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--cyan);
box-shadow: 0 0 0 3px rgba(34, 211, 238, 0.2);
}
/* ---------- panels ---------- */
.panel {
background: linear-gradient(180deg, var(--surface), var(--surface));
border: 1px solid var(--line);
border-radius: var(--r-lg);
padding: 20px;
}
.panel__title {
margin: 0;
font-size: 16px;
font-weight: 700;
letter-spacing: -0.01em;
}
.panel__sub {
margin: 2px 0 0;
font-size: 13px;
color: var(--muted);
}
.trimmer__meta {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 16px;
margin-bottom: 18px;
}
.dur {
text-align: right;
display: flex;
flex-direction: column;
gap: 2px;
}
.dur__label {
font-size: 11px;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--muted);
font-weight: 600;
}
.dur__val {
font-size: 20px;
font-weight: 800;
font-variant-numeric: tabular-nums;
background: linear-gradient(120deg, var(--violet), var(--cyan));
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
/* ---------- waveform ---------- */
.wave {
position: relative;
height: 108px;
border-radius: var(--r-md);
background: var(--surface-2);
border: 1px solid var(--line);
padding: 0 4px;
cursor: pointer;
overflow: hidden;
outline: none;
}
.wave:focus-visible {
border-color: var(--violet);
box-shadow: 0 0 0 3px rgba(139, 92, 246, 0.35);
}
.wave__bars {
position: absolute;
inset: 0;
display: flex;
align-items: center;
gap: 2px;
padding: 0 6px;
}
.wave__bars i {
flex: 1;
border-radius: 3px;
background: rgba(255, 255, 255, 0.14);
transition: background 0.2s;
}
.wave__bars i.on {
background: linear-gradient(180deg, var(--violet), var(--cyan));
box-shadow: 0 0 8px rgba(139, 92, 246, 0.4);
}
.wave__region {
position: absolute;
top: 0;
bottom: 0;
background: rgba(139, 92, 246, 0.14);
border-left: 2px solid var(--violet);
border-right: 2px solid var(--cyan);
z-index: 2;
}
.handle {
position: absolute;
top: 50%;
width: 16px;
height: 54px;
transform: translate(-50%, -50%);
border: none;
border-radius: 6px;
cursor: ew-resize;
padding: 0;
z-index: 3;
background: var(--white);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
}
.handle::after {
content: "";
position: absolute;
inset: 0;
margin: auto;
width: 4px;
height: 20px;
border-left: 1px solid rgba(0, 0, 0, 0.35);
border-right: 1px solid rgba(0, 0, 0, 0.35);
}
.handle--start { left: 0; background: var(--violet); }
.handle--end { right: 0; background: var(--cyan); }
.handle:focus-visible {
outline: 2px solid var(--white);
outline-offset: 2px;
}
.wave__playhead {
position: absolute;
top: 6px;
bottom: 6px;
width: 2px;
background: var(--pink);
box-shadow: 0 0 10px var(--pink);
z-index: 4;
pointer-events: none;
border-radius: 2px;
}
.wave__playhead::before {
content: "";
position: absolute;
top: -4px;
left: 50%;
transform: translateX(-50%);
width: 10px;
height: 10px;
border-radius: 50%;
background: var(--pink);
box-shadow: 0 0 8px var(--pink);
}
.times {
display: flex;
justify-content: space-between;
margin-top: 14px;
gap: 10px;
}
.tc {
display: flex;
flex-direction: column;
gap: 1px;
font-variant-numeric: tabular-nums;
}
.tc em {
font-style: normal;
font-size: 10.5px;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--muted);
font-weight: 600;
}
.tc strong {
font-size: 15px;
font-weight: 700;
}
.tc--mid {
text-align: center;
align-items: center;
}
.tc--mid strong { color: var(--pink); }
.tc--end {
text-align: right;
align-items: flex-end;
}
/* ---------- preview card ---------- */
.preview {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
align-items: start;
}
.card {
background: radial-gradient(140% 120% at 0% 0%, rgba(139, 92, 246, 0.22), transparent 55%),
var(--surface-2);
border: 1px solid var(--line-2);
border-radius: var(--r-lg);
padding: 18px;
display: flex;
flex-direction: column;
gap: 16px;
box-shadow: 0 24px 60px rgba(0, 0, 0, 0.45);
}
.card__top {
display: flex;
gap: 13px;
align-items: center;
}
.card__art {
width: 60px;
height: 60px;
flex: none;
border-radius: var(--r-md);
background-size: cover;
background-position: center;
border: 1px solid var(--line-2);
}
.card__id {
min-width: 0;
}
.card__show {
margin: 0;
font-size: 10px;
letter-spacing: 0.18em;
color: var(--cyan);
font-weight: 700;
}
.card__ep {
margin: 3px 0 6px;
font-size: 16px;
font-weight: 800;
letter-spacing: -0.01em;
line-height: 1.2;
}
.card__badge {
display: inline-block;
font-size: 10px;
font-weight: 700;
letter-spacing: 0.08em;
padding: 3px 8px;
border-radius: 999px;
color: var(--ink);
background: rgba(255, 255, 255, 0.08);
border: 1px solid var(--line);
}
.viz {
display: flex;
align-items: flex-end;
justify-content: center;
gap: 3px;
height: 66px;
padding: 0 2px;
}
.viz i {
flex: 1;
max-width: 8px;
border-radius: 4px;
background: linear-gradient(180deg, var(--accent), var(--cyan));
height: 30%;
transform-origin: bottom;
animation: bounce 0.9s ease-in-out infinite;
}
.viz.paused i {
animation-play-state: paused;
}
@keyframes bounce {
0%, 100% { transform: scaleY(0.3); }
50% { transform: scaleY(1); }
}
.card__caption {
margin: 0;
font-size: 15px;
font-weight: 600;
line-height: 1.4;
letter-spacing: -0.01em;
min-height: 42px;
}
.card__foot {
display: flex;
align-items: center;
justify-content: space-between;
padding-top: 12px;
border-top: 1px solid var(--line);
}
.tag {
font-size: 12px;
font-weight: 700;
font-variant-numeric: tabular-nums;
color: var(--accent);
background: rgba(139, 92, 246, 0.14);
padding: 4px 10px;
border-radius: 999px;
}
.card__wm {
font-size: 11px;
color: var(--muted);
font-weight: 600;
letter-spacing: 0.04em;
}
/* ---------- controls ---------- */
.preview__controls {
display: flex;
flex-direction: column;
gap: 16px;
}
.play {
display: inline-flex;
align-items: center;
gap: 10px;
padding: 12px 18px;
border: none;
border-radius: var(--r-md);
font: inherit;
font-weight: 700;
color: var(--white);
cursor: pointer;
background: linear-gradient(120deg, var(--violet-d), var(--violet));
box-shadow: 0 10px 26px rgba(124, 58, 237, 0.42);
transition: transform 0.12s, box-shadow 0.2s;
}
.play:hover {
transform: translateY(-1px);
box-shadow: 0 14px 32px rgba(124, 58, 237, 0.55);
}
.play:active { transform: translateY(0); }
.play:focus-visible {
outline: 2px solid var(--cyan);
outline-offset: 3px;
}
.play__icon {
width: 0;
height: 0;
border-style: solid;
border-width: 7px 0 7px 12px;
border-color: transparent transparent transparent var(--white);
}
.play.is-paused .play__icon {
border: none;
width: 11px;
height: 12px;
border-left: 4px solid var(--white);
border-right: 4px solid var(--white);
}
.field {
display: flex;
flex-direction: column;
gap: 6px;
position: relative;
}
.field__label {
font-size: 11px;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--muted);
font-weight: 600;
padding: 0;
}
.field__input {
font: inherit;
font-size: 14px;
color: var(--ink);
background: var(--bg);
border: 1px solid var(--line);
border-radius: var(--r-md);
padding: 11px 12px;
resize: vertical;
min-height: 72px;
transition: border-color 0.2s, box-shadow 0.2s;
}
.field__input:focus-visible {
outline: none;
border-color: var(--violet);
box-shadow: 0 0 0 3px rgba(139, 92, 246, 0.28);
}
.field__count {
align-self: flex-end;
font-size: 11px;
color: var(--muted);
font-variant-numeric: tabular-nums;
}
.swatches {
border: none;
margin: 0;
padding: 0;
display: flex;
align-items: center;
gap: 10px;
flex-wrap: wrap;
}
.swatches legend {
margin-bottom: 8px;
float: left;
width: 100%;
}
.swatch {
width: 30px;
height: 30px;
border-radius: 50%;
border: 2px solid transparent;
background: var(--sw);
cursor: pointer;
padding: 0;
transition: transform 0.12s, box-shadow 0.2s;
}
.swatch:hover { transform: scale(1.08); }
.swatch.is-active {
border-color: var(--white);
box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.14);
}
.swatch:focus-visible {
outline: 2px solid var(--white);
outline-offset: 2px;
}
/* ---------- share ---------- */
.share {
display: flex;
gap: 10px;
margin-top: 20px;
flex-wrap: wrap;
}
.btn {
flex: 1;
min-width: 130px;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
padding: 12px 14px;
border-radius: var(--r-md);
font: inherit;
font-weight: 600;
font-size: 14px;
cursor: pointer;
color: var(--ink);
background: var(--surface-2);
border: 1px solid var(--line-2);
transition: transform 0.12s, border-color 0.2s, background 0.2s;
}
.btn:hover {
transform: translateY(-1px);
border-color: var(--violet);
background: rgba(139, 92, 246, 0.12);
}
.btn:active { transform: translateY(0); }
.btn:focus-visible {
outline: 2px solid var(--cyan);
outline-offset: 2px;
}
.btn--primary {
color: var(--white);
background: linear-gradient(120deg, var(--violet-d), var(--violet));
border-color: transparent;
box-shadow: 0 10px 24px rgba(124, 58, 237, 0.4);
}
.btn--primary:hover {
background: linear-gradient(120deg, var(--violet), var(--violet-d));
}
/* ---------- toast ---------- */
.toast {
position: fixed;
left: 50%;
bottom: 26px;
transform: translate(-50%, 20px);
background: var(--surface-2);
border: 1px solid var(--line-2);
color: var(--ink);
font-size: 14px;
font-weight: 600;
padding: 12px 18px;
border-radius: var(--r-md);
box-shadow: 0 16px 40px rgba(0, 0, 0, 0.5);
opacity: 0;
pointer-events: none;
transition: opacity 0.24s, transform 0.24s;
z-index: 40;
}
.toast.show {
opacity: 1;
transform: translate(-50%, 0);
}
.toast::before {
content: "";
display: inline-block;
width: 8px;
height: 8px;
margin-right: 8px;
border-radius: 50%;
background: var(--cyan);
vertical-align: middle;
}
/* ---------- responsive ---------- */
@media (max-width: 520px) {
body { padding: 18px 12px 56px; }
.preview { grid-template-columns: 1fr; }
.brand__title { font-size: 18px; }
.studio__head { align-items: flex-start; }
.wave { height: 90px; }
.handle { height: 46px; }
.btn { min-width: 0; }
.dur__val { font-size: 18px; }
}
@media (prefers-reduced-motion: reduce) {
.viz i,
.brand__glyph span { animation: none; }
}(function () {
"use strict";
var EPISODE_SECONDS = 48 * 60 + 12; // 48:12 total
var BAR_COUNT = 72;
var VIZ_BARS = 28;
var wave = document.getElementById("wave");
var waveBars = document.getElementById("waveBars");
var region = document.getElementById("region");
var playhead = document.getElementById("playhead");
var handleStart = document.getElementById("handleStart");
var handleEnd = document.getElementById("handleEnd");
var clipLength = document.getElementById("clipLength");
var tcStart = document.getElementById("tcStart");
var tcEnd = document.getElementById("tcEnd");
var tcPlay = document.getElementById("tcPlay");
var cardRange = document.getElementById("cardRange");
var cardCaption = document.getElementById("cardCaption");
var cardEp = document.getElementById("cardEp");
var captionInput = document.getElementById("captionInput");
var capCount = document.getElementById("capCount");
var viz = document.getElementById("viz");
var playBtn = document.getElementById("playBtn");
var playLabel = document.getElementById("playLabel");
var toastEl = document.getElementById("toast");
// state as fraction (0..1) of full episode
var state = { start: 0.16, end: 0.35, play: 0.16 };
/* ---------- helpers ---------- */
function fmt(sec) {
sec = Math.max(0, Math.round(sec));
var m = Math.floor(sec / 60);
var s = sec % 60;
return m + ":" + (s < 10 ? "0" : "") + s;
}
var toastTimer;
function toast(msg) {
toastEl.textContent = msg;
toastEl.classList.add("show");
clearTimeout(toastTimer);
toastTimer = setTimeout(function () {
toastEl.classList.remove("show");
}, 2200);
}
/* ---------- build static waveform ---------- */
var amps = [];
for (var i = 0; i < BAR_COUNT; i++) {
// deterministic pseudo-random envelope so it looks like real audio
var base = Math.sin(i * 0.45) * 0.5 + 0.5;
var jitter = ((Math.sin(i * 13.13) + 1) / 2) * 0.55;
var env = 0.25 + base * 0.5 + jitter * 0.35;
amps.push(Math.min(1, env));
var bar = document.createElement("i");
bar.style.height = Math.round(18 + amps[i] * 78) + "%";
waveBars.appendChild(bar);
}
var barEls = waveBars.querySelectorAll("i");
/* ---------- build audiogram viz bars ---------- */
for (var v = 0; v < VIZ_BARS; v++) {
var b = document.createElement("i");
b.style.animationDelay = (v * 0.055).toFixed(3) + "s";
b.style.animationDuration = (0.7 + (v % 5) * 0.09).toFixed(2) + "s";
viz.appendChild(b);
}
/* ---------- render ---------- */
function render() {
var sPct = state.start * 100;
var ePct = state.end * 100;
region.style.left = sPct + "%";
region.style.right = 100 - ePct + "%";
playhead.style.left = state.play * 100 + "%";
var startSec = state.start * EPISODE_SECONDS;
var endSec = state.end * EPISODE_SECONDS;
var playSec = state.play * EPISODE_SECONDS;
clipLength.textContent = fmt(endSec - startSec);
tcStart.textContent = fmt(startSec);
tcEnd.textContent = fmt(endSec);
tcPlay.textContent = fmt(playSec);
cardRange.textContent = fmt(startSec) + " – " + fmt(endSec);
wave.setAttribute("aria-valuenow", Math.round(state.play * 100));
// highlight bars inside selection
for (var i = 0; i < barEls.length; i++) {
var pos = (i + 0.5) / BAR_COUNT;
if (pos >= state.start && pos <= state.end) {
barEls[i].classList.add("on");
} else {
barEls[i].classList.remove("on");
}
}
}
/* ---------- pointer helpers ---------- */
function fracFromEvent(e) {
var rect = wave.getBoundingClientRect();
var x = (e.touches ? e.touches[0].clientX : e.clientX) - rect.left;
return Math.min(1, Math.max(0, x / rect.width));
}
var MIN_GAP = 0.03;
var dragging = null;
function startDrag(which) {
return function (e) {
e.preventDefault();
dragging = which;
window.addEventListener("mousemove", onMove);
window.addEventListener("mouseup", stopDrag);
window.addEventListener("touchmove", onMove, { passive: false });
window.addEventListener("touchend", stopDrag);
};
}
function onMove(e) {
if (!dragging) return;
e.preventDefault();
var f = fracFromEvent(e);
if (dragging === "start") {
state.start = Math.min(f, state.end - MIN_GAP);
if (state.play < state.start) state.play = state.start;
} else if (dragging === "end") {
state.end = Math.max(f, state.start + MIN_GAP);
if (state.play > state.end) state.play = state.end;
}
render();
}
function stopDrag() {
dragging = null;
window.removeEventListener("mousemove", onMove);
window.removeEventListener("mouseup", stopDrag);
window.removeEventListener("touchmove", onMove);
window.removeEventListener("touchend", stopDrag);
}
handleStart.addEventListener("mousedown", startDrag("start"));
handleEnd.addEventListener("mousedown", startDrag("end"));
handleStart.addEventListener("touchstart", startDrag("start"), { passive: false });
handleEnd.addEventListener("touchstart", startDrag("end"), { passive: false });
// click / scrub track to move playhead
wave.addEventListener("mousedown", function (e) {
if (e.target === handleStart || e.target === handleEnd) return;
var f = fracFromEvent(e);
state.play = Math.min(state.end, Math.max(state.start, f));
render();
});
/* ---------- keyboard on handles ---------- */
function keyStep(which) {
return function (e) {
var step = e.shiftKey ? 0.05 : 0.01;
var handled = true;
if (e.key === "ArrowLeft" || e.key === "ArrowDown") {
if (which === "start") state.start = Math.max(0, state.start - step);
else state.end = Math.max(state.start + MIN_GAP, state.end - step);
} else if (e.key === "ArrowRight" || e.key === "ArrowUp") {
if (which === "start") state.start = Math.min(state.end - MIN_GAP, state.start + step);
else state.end = Math.min(1, state.end + step);
} else {
handled = false;
}
if (handled) {
e.preventDefault();
state.play = Math.min(state.end, Math.max(state.start, state.play));
render();
}
};
}
handleStart.addEventListener("keydown", keyStep("start"));
handleEnd.addEventListener("keydown", keyStep("end"));
// keyboard on wave = scrub playhead
wave.addEventListener("keydown", function (e) {
if (e.target !== wave) return;
var step = e.shiftKey ? 0.05 : 0.01;
if (e.key === "ArrowLeft" || e.key === "ArrowDown") {
state.play = Math.max(state.start, state.play - step);
e.preventDefault();
render();
} else if (e.key === "ArrowRight" || e.key === "ArrowUp") {
state.play = Math.min(state.end, state.play + step);
e.preventDefault();
render();
}
});
/* ---------- play / pause animation ---------- */
var playing = true;
function setPlaying(next) {
playing = next;
viz.classList.toggle("paused", !playing);
playBtn.classList.toggle("is-paused", !playing);
playBtn.setAttribute("aria-pressed", String(playing));
playLabel.textContent = playing ? "Pause" : "Play";
}
playBtn.addEventListener("click", function () {
setPlaying(!playing);
toast(playing ? "Previewing clip" : "Preview paused");
});
/* ---------- caption editing ---------- */
function syncCaption() {
var val = captionInput.value.trim();
cardCaption.textContent = val || "Add a caption for your clip…";
capCount.textContent = captionInput.value.length + " / 120";
}
captionInput.addEventListener("input", syncCaption);
/* ---------- accent swatches ---------- */
var swatches = document.querySelectorAll(".swatch");
swatches.forEach(function (sw) {
sw.addEventListener("click", function () {
swatches.forEach(function (o) {
o.classList.remove("is-active");
});
sw.classList.add("is-active");
var map = { violet: "#8b5cf6", cyan: "#22d3ee", pink: "#f472b6" };
document.documentElement.style.setProperty("--accent", map[sw.dataset.accent]);
toast(sw.dataset.accent + " accent applied");
});
});
/* ---------- share actions ---------- */
function clipUrl() {
var s = Math.round(state.start * EPISODE_SECONDS);
var e = Math.round(state.end * EPISODE_SECONDS);
return "https://stealthis.fm/e/214/clip?t=" + s + "," + e;
}
document.getElementById("copyBtn").addEventListener("click", function () {
var url = clipUrl();
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(url).then(
function () { toast("Clip link copied"); },
function () { toast("Copy failed — " + url); }
);
} else {
toast("Link: " + url);
}
});
document.getElementById("xBtn").addEventListener("click", function () {
toast("Opening composer for X…");
});
document.getElementById("dlBtn").addEventListener("click", function () {
toast("Rendering audiogram… " + clipLength.textContent);
});
/* ---------- init ---------- */
syncCaption();
render();
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Clip Share — Audiogram Studio</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>
<main class="studio" aria-label="Podcast clip share studio">
<header class="studio__head">
<div class="brand">
<span class="brand__glyph" aria-hidden="true">
<span></span><span></span><span></span><span></span>
</span>
<div>
<p class="brand__kicker">Clip Studio</p>
<h1 class="brand__title">Share a moment</h1>
</div>
</div>
<span class="pill">
<span class="pill__dot" aria-hidden="true"></span>
Draft · Ep. 214
</span>
</header>
<!-- WAVEFORM TRIMMER -->
<section class="panel trimmer" aria-label="Waveform clip trimmer">
<div class="trimmer__meta">
<div>
<h2 class="panel__title">Signals & Static</h2>
<p class="panel__sub">Ep. 214 — The Attention Economy · 48:12</p>
</div>
<div class="dur" aria-live="polite">
<span class="dur__label">Clip length</span>
<strong id="clipLength" class="dur__val">0:24</strong>
</div>
</div>
<div
class="wave"
id="wave"
role="slider"
tabindex="0"
aria-label="Playhead position on episode"
aria-valuemin="0"
aria-valuemax="100"
aria-valuenow="18"
>
<div class="wave__bars" id="waveBars" aria-hidden="true"></div>
<div class="wave__region" id="region">
<button
class="handle handle--start"
id="handleStart"
type="button"
aria-label="Clip start, use arrow keys to adjust"
></button>
<button
class="handle handle--end"
id="handleEnd"
type="button"
aria-label="Clip end, use arrow keys to adjust"
></button>
</div>
<div class="wave__playhead" id="playhead" aria-hidden="true"></div>
</div>
<div class="times">
<span class="tc"><em>Start</em><strong id="tcStart">0:18</strong></span>
<span class="tc tc--mid"
><em>Preview</em><strong id="tcPlay">0:18</strong></span
>
<span class="tc tc--end"><em>End</em><strong id="tcEnd">0:42</strong></span>
</div>
</section>
<!-- AUDIOGRAM PREVIEW -->
<section class="panel" aria-label="Audiogram preview card">
<div class="preview">
<article class="card" id="card">
<div class="card__top">
<div
class="card__art"
style="
background-image: url('https://images.unsplash.com/photo-1478737270239-2f02b77fc618?auto=format&fit=crop&w=240&q=70');
"
role="img"
aria-label="Episode artwork"
></div>
<div class="card__id">
<p class="card__show">SIGNALS & STATIC</p>
<h3 class="card__ep" id="cardEp">The Attention Economy</h3>
<span class="card__badge">EP 214 · CLIP</span>
</div>
</div>
<div class="viz" id="viz" aria-hidden="true"></div>
<p class="card__caption" id="cardCaption">
We keep optimizing for the wrong metric — attention isn't the same
thing as trust.
</p>
<div class="card__foot">
<span class="tag" id="cardRange">0:18 – 0:42</span>
<span class="card__wm">stealthis.fm</span>
</div>
</article>
<div class="preview__controls">
<button class="play" id="playBtn" type="button" aria-pressed="true">
<span class="play__icon" id="playIcon" aria-hidden="true"></span>
<span id="playLabel">Pause</span>
</button>
<label class="field">
<span class="field__label">Caption</span>
<textarea
id="captionInput"
class="field__input"
rows="3"
maxlength="120"
aria-describedby="capCount"
>
We keep optimizing for the wrong metric — attention isn't the same thing as trust.</textarea
>
<span class="field__count" id="capCount">0 / 120</span>
</label>
<fieldset class="swatches">
<legend class="field__label">Accent</legend>
<button
class="swatch is-active"
type="button"
data-accent="violet"
aria-label="Violet accent"
style="--sw: #8b5cf6"
></button>
<button
class="swatch"
type="button"
data-accent="cyan"
aria-label="Cyan accent"
style="--sw: #22d3ee"
></button>
<button
class="swatch"
type="button"
data-accent="pink"
aria-label="Pink accent"
style="--sw: #f472b6"
></button>
</fieldset>
</div>
</div>
<div class="share" role="group" aria-label="Share actions">
<button class="btn btn--primary" id="copyBtn" type="button">
<span aria-hidden="true">🔗</span> Copy link
</button>
<button class="btn" id="xBtn" type="button">
<span aria-hidden="true">✳</span> Post to X
</button>
<button class="btn" id="dlBtn" type="button">
<span aria-hidden="true">⬇</span> Download
</button>
</div>
</section>
</main>
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>The Clip Share studio turns a single podcast moment into a shareable audiogram. A full-episode waveform sits at the top, rendered as gradient bars from violet to cyan. Two draggable handles let you set the clip’s start and end — the selection region highlights between them and the timecodes plus computed duration update instantly. A scrubbable playhead lets you preview position along the track.
Below, an animated audiogram card previews exactly what listeners will see: episode artwork, show and episode titles, a caption line you can edit, and a row of bars that bounce with a staggered, looping animation to mimic live audio. A play/pause control toggles the animation, and the caption input rewrites the card in real time.
A share bar rounds it out with Copy link, X, and Download actions, each firing a small toast for feedback. Everything is keyboard-operable — handles respond to arrow keys, buttons have visible focus rings — and the layout collapses gracefully to a single column under 520px so the studio stays usable on a phone.