{"project":{"id":"EVzpKeW","userId":"davidyarham@gmail.com","username":null,"userPicture":null,"name":"VHS Bunny Player","visible":true,"contributors":"","githubRepo":null,"forkedFrom":null,"isTemplate":false,"tags":"","files":{"folder":"","files":[{"name":"index.html","content":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"UTF-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <title>Untitled</title>\n  <link rel=\"stylesheet\" href=\"style.css\">\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1, viewport-fit=cover\">\n<link rel=\"preconnect\" href=\"https://fonts.googleapis.com\">\n<link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin>\n<link href=\"https://fonts.googleapis.com/css2?family=Inter:wght@400;500;700&display=swap\" rel=\"stylesheet\">\n<script src=\"https://unpkg.com/lucide@latest/dist/umd/lucide.js\"></script>\n</head>\n<body>\n<main class=\"player\">\n\t<video class=\"player__source\" id=\"sourceVideo\"\n\t\tsrc=\"https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4\" crossorigin=\"anonymous\"\n\t\tplaysinline preload=\"auto\" loop></video>\n\n\t<canvas class=\"player__canvas\" id=\"vhsCanvas\" aria-label=\"VHS filtered video playback\"></canvas>\n\t<div class=\"player__overlay\"></div>\n\n\t<div class=\"badge\" aria-hidden=\"true\">\n\t\t<span class=\"badge__dot\"></span>\n\t\t<span class=\"badge__text\">Play</span>\n\t</div>\n\n\t<section class=\"player__panel\">\n\t\t<div class=\"title-card\" aria-hidden=\"true\">\n\t\t\t<p class=\"title-card__eyebrow\">Archive Transfer</p>\n\t\t\t<h1 class=\"title-card__title\">Big Buck Bunny</h1>\n\t\t</div>\n\n\t\t<section class=\"controls\" aria-label=\"Video controls\">\n\t\t\t<div class=\"controls__top\">\n\t\t\t\t<button class=\"controls__button\" id=\"playToggle\" type=\"button\" aria-label=\"Play video\">\n\t\t\t\t\t<i data-lucide=\"play\"></i>\n\t\t\t\t</button>\n\n\t\t\t\t<div class=\"controls__meta\">\n\t\t\t\t\t<span class=\"controls__time\" id=\"timeCurrent\">00:00</span>\n\t\t\t\t\t<span class=\"controls__divider\">/</span>\n\t\t\t\t\t<span class=\"controls__time\" id=\"timeDuration\">00:00</span>\n\t\t\t\t</div>\n\n\t\t\t\t<button class=\"controls__button\" id=\"muteToggle\" type=\"button\" aria-label=\"Mute video\">\n\t\t\t\t\t<i data-lucide=\"volume-2\"></i>\n\t\t\t\t</button>\n\t\t\t</div>\n\n\t\t\t<div class=\"controls__timeline-block\">\n\t\t\t\t<label class=\"sr-only\" for=\"timeline\">Video progress</label>\n\t\t\t\t<input id=\"timeline\" class=\"controls__timeline\" type=\"range\" min=\"0\" max=\"100\" value=\"0\" step=\"0.1\" />\n\t\t\t</div>\n\t\t</section>\n\t</section>\n</main>\n  <script type=\"module\" src=\"main.js\"></script>\n</body>\n</html>"},{"name":"main.js","content":"const video = document.getElementById('sourceVideo');\nconst canvas = document.getElementById('vhsCanvas');\nconst ctx = canvas.getContext('2d', {\n\twillReadFrequently: true\n});\nconst playToggle = document.getElementById('playToggle');\nconst muteToggle = document.getElementById('muteToggle');\nconst timeline = document.getElementById('timeline');\nconst timeCurrent = document.getElementById('timeCurrent');\nconst timeDuration = document.getElementById('timeDuration');\n\nconst noiseCanvas = document.createElement('canvas');\nconst noiseCtx = noiseCanvas.getContext('2d', {\n\twillReadFrequently: true\n});\nconst frameCanvas = document.createElement('canvas');\nconst frameCtx = frameCanvas.getContext('2d', {\n\twillReadFrequently: true\n});\n\nlet frameCount = 0;\nlet dragging = false;\n\nfunction getViewportSize() {\n\tconst rect = canvas.getBoundingClientRect();\n\tconst width = Math.round(rect.width || window.innerWidth || document.documentElement.clientWidth);\n\tconst height = Math.round(rect.height || window.innerHeight || document.documentElement.clientHeight);\n\treturn {\n\t\twidth,\n\t\theight\n\t};\n}\n\nfunction getViewportSize() {\n\tconst visualViewport = window.visualViewport;\n\tconst width = Math.round(visualViewport ? visualViewport.width : window.innerWidth || document.documentElement.clientWidth);\n\tconst height = Math.round(visualViewport ? visualViewport.height : window.innerHeight || document.documentElement.clientHeight);\n\treturn {\n\t\twidth,\n\t\theight\n\t};\n}\n\nfunction resizeCanvas() {\n\tconst {\n\t\twidth,\n\t\theight\n\t} = getViewportSize();\n\tconst dpr = Math.min(window.devicePixelRatio || 1, 2);\n\n\tcanvas.width = Math.floor(width * dpr);\n\tcanvas.height = Math.floor(height * dpr);\n\tcanvas.style.width = '100%';\n\tcanvas.style.height = '100%';\n\n\tctx.setTransform(1, 0, 0, 1, 0, 0);\n\tctx.scale(dpr, dpr);\n\n\tnoiseCanvas.width = Math.max(160, Math.floor(width / 2));\n\tnoiseCanvas.height = Math.max(90, Math.floor(height / 2));\n\tframeCanvas.width = Math.max(1, Math.floor(width));\n\tframeCanvas.height = Math.max(1, Math.floor(height));\n}\n\nfunction formatTime(seconds) {\n\tif (!Number.isFinite(seconds)) return '00:00';\n\tconst mins = Math.floor(seconds / 60);\n\tconst secs = Math.floor(seconds % 60);\n\treturn `${String(mins).padStart(2, '0')}:${String(secs).padStart(2, '0')}`;\n}\n\nfunction updateTimeline() {\n\tif (!video.duration || dragging) return;\n\tconst progress = (video.currentTime / video.duration) * 100;\n\ttimeline.value = progress;\n\ttimeline.style.setProperty('--progress', `${progress}%`);\n\ttimeCurrent.textContent = formatTime(video.currentTime);\n}\n\nfunction updateDuration() {\n\ttimeDuration.textContent = formatTime(video.duration);\n}\n\nfunction updatePlayIcon() {\n\tplayToggle.innerHTML = `<i data-lucide=\"${video.paused ? 'play' : 'pause'}\"></i>`;\n\tplayToggle.setAttribute('aria-label', video.paused ? 'Play video' : 'Pause video');\n\tlucide.createIcons();\n}\n\nfunction updateMuteIcon() {\n\tmuteToggle.innerHTML = `<i data-lucide=\"${video.muted ? 'volume-x' : 'volume-2'}\"></i>`;\n\tmuteToggle.setAttribute('aria-label', video.muted ? 'Unmute video' : 'Mute video');\n\tlucide.createIcons();\n}\n\nfunction generateNoise() {\n\tconst imageData = noiseCtx.createImageData(noiseCanvas.width, noiseCanvas.height);\n\tconst data = imageData.data;\n\n\tfor (let i = 0; i < data.length; i += 4) {\n\t\tconst value = Math.random() * 255;\n\t\tconst warmth = 0.8 + Math.random() * 0.22;\n\t\tdata[i] = value * warmth;\n\t\tdata[i + 1] = value * 0.88;\n\t\tdata[i + 2] = value * 0.68;\n\t\tdata[i + 3] = 18 + Math.random() * 32;\n\t}\n\n\tnoiseCtx.putImageData(imageData, 0, 0);\n}\n\nfunction drawTapeWave(source, width, height, intensity) {\n\tconst sliceHeight = Math.max(1, Math.floor(height / 140));\n\tfor (let y = 0; y < height; y += sliceHeight) {\n\t\tconst wave = Math.sin((y * 0.05) + frameCount * 0.11) * intensity;\n\t\tconst jitter = (Math.random() - 0.5) * intensity * 0.9;\n\t\tconst offset = wave + jitter;\n\t\tctx.drawImage(source, 0, y, width, sliceHeight, offset, y, width, sliceHeight);\n\t}\n}\n\nfunction applyColorBleed(width, height) {\n\tconst redShift = Math.sin(frameCount * 0.08) * 2.4 + 1.2;\n\tconst blueShift = Math.cos(frameCount * 0.06) * -2.8 - 1.4;\n\n\tctx.save();\n\tctx.globalCompositeOperation = 'screen';\n\tctx.globalAlpha = 0.18;\n\tctx.filter = 'contrast(1.08) saturate(1.05)';\n\tctx.drawImage(canvas, redShift, 0, width, height);\n\tctx.globalAlpha = 0.16;\n\tctx.drawImage(canvas, blueShift, 0, width, height);\n\tctx.restore();\n}\n\nfunction applyTrackingLines(width, height) {\n\tfor (let i = 0; i < 3; i++) {\n\t\tconst y = ((frameCount * (2 + i)) + i * 90) % (height + 160) - 160;\n\t\tconst grad = ctx.createLinearGradient(0, y, 0, y + 140);\n\t\tgrad.addColorStop(0, 'rgba(255,255,255,0)');\n\t\tgrad.addColorStop(0.25, 'rgba(255,255,255,0.03)');\n\t\tgrad.addColorStop(0.5, 'rgba(255,170,110,0.14)');\n\t\tgrad.addColorStop(0.7, 'rgba(255,255,255,0.06)');\n\t\tgrad.addColorStop(1, 'rgba(255,255,255,0)');\n\t\tctx.fillStyle = grad;\n\t\tctx.fillRect(0, y, width, 140);\n\t}\n}\n\nfunction applyDropouts(width, height) {\n\tconst count = width < 480 ? 8 : 14;\n\tfor (let i = 0; i < count; i++) {\n\t\tif (Math.random() > 0.22) continue;\n\t\tconst y = Math.random() * height;\n\t\tconst h = 1 + Math.random() * 3;\n\t\tconst w = width * (0.08 + Math.random() * 0.25);\n\t\tconst x = Math.random() * (width - w);\n\t\tctx.fillStyle = `rgba(255,245,210,${0.04 + Math.random() * 0.12})`;\n\t\tctx.fillRect(x, y, w, h);\n\t}\n}\n\nfunction applyHeadSwitchingNoise(width, height) {\n\tconst bandHeight = Math.max(18, height * 0.045);\n\tconst y = height - bandHeight;\n\tconst grad = ctx.createLinearGradient(0, y, 0, height);\n\tgrad.addColorStop(0, 'rgba(0,0,0,0)');\n\tgrad.addColorStop(0.35, 'rgba(255,255,255,0.05)');\n\tgrad.addColorStop(1, 'rgba(0,0,0,0.28)');\n\tctx.fillStyle = grad;\n\tctx.fillRect(0, y, width, bandHeight);\n\n\tfor (let i = 0; i < 14; i++) {\n\t\tconst lineY = y + Math.random() * bandHeight;\n\t\tconst offset = (Math.random() - 0.5) * 26;\n\t\tctx.drawImage(canvas, 0, lineY, width, 1, offset, lineY, width, 1);\n\t}\n}\n\nfunction applyScanlines(width, height) {\n\tfor (let y = 0; y < height; y += 2) {\n\t\tctx.fillStyle = y % 4 === 0 ? 'rgba(255,255,255,0.025)' : 'rgba(0,0,0,0.085)';\n\t\tctx.fillRect(0, y, width, 1);\n\t}\n}\n\nfunction applyVignette(width, height) {\n\tconst vignette = ctx.createRadialGradient(width / 2, height / 2, width * 0.16, width / 2, height / 2, width * 0.82);\n\tvignette.addColorStop(0, 'rgba(0,0,0,0)');\n\tvignette.addColorStop(0.72, 'rgba(0,0,0,0.08)');\n\tvignette.addColorStop(1, 'rgba(0,0,0,0.42)');\n\tctx.fillStyle = vignette;\n\tctx.fillRect(0, 0, width, height);\n}\n\nfunction drawVHSFrame() {\n\tconst {\n\t\twidth,\n\t\theight\n\t} = getViewportSize();\n\n\tctx.clearRect(0, 0, width, height);\n\tctx.fillStyle = '#050505';\n\tctx.fillRect(0, 0, width, height);\n\n\tif (video.readyState >= 2) {\n\t\tconst videoRatio = video.videoWidth / video.videoHeight;\n\t\tconst canvasRatio = width / height;\n\n\t\tlet sourceX = 0;\n\t\tlet sourceY = 0;\n\t\tlet sourceWidth = video.videoWidth;\n\t\tlet sourceHeight = video.videoHeight;\n\t\tlet drawX = 0;\n\t\tlet drawY = 0;\n\t\tlet drawWidth = width;\n\t\tlet drawHeight = height;\n\n\t\tif (videoRatio > canvasRatio) {\n\t\t\tsourceWidth = video.videoHeight * canvasRatio;\n\t\t\tsourceX = (video.videoWidth - sourceWidth) / 2;\n\t\t} else {\n\t\t\tsourceHeight = video.videoWidth / canvasRatio;\n\t\t\tsourceY = (video.videoHeight - sourceHeight) / 2;\n\t\t}\n\n\t\tdrawX = 0;\n\t\tdrawY = 0;\n\t\tdrawWidth = width;\n\t\tdrawHeight = height;\n\n\t\tconst jitterX = (Math.random() - 0.5) * 7;\n\t\tconst jitterY = (Math.random() - 0.5) * 3.5;\n\t\tconst verticalSkew = Math.sin(frameCount * 0.03) * 1.2;\n\t\tconst flicker = 0.94 + Math.sin(frameCount * 0.17) * 0.03 + Math.random() * 0.025;\n\n\t\tframeCtx.setTransform(1, 0, 0, 1, 0, 0);\n\t\tframeCtx.clearRect(0, 0, frameCanvas.width, frameCanvas.height);\n\t\tframeCtx.filter = `saturate(0.72) contrast(1.12) brightness(${flicker}) sepia(0.16)`;\n\t\tframeCtx.drawImage(video, sourceX, sourceY, sourceWidth, sourceHeight, drawX + jitterX, drawY + jitterY + verticalSkew, drawWidth, drawHeight);\n\t\tframeCtx.filter = 'none';\n\n\t\tctx.drawImage(frameCanvas, 0, 0, width, height);\n\t\tdrawTapeWave(frameCanvas, width, height, width < 480 ? 5 : 8);\n\n\t\tctx.globalAlpha = 0.12;\n\t\tctx.drawImage(frameCanvas, -2.2, 0, width, height);\n\t\tctx.globalAlpha = 0.1;\n\t\tctx.drawImage(frameCanvas, 2.6, 0, width, height);\n\t\tctx.globalAlpha = 1;\n\n\t\tif (frameCount % 2 === 0) {\n\t\t\tgenerateNoise();\n\t\t}\n\n\t\tapplyColorBleed(width, height);\n\n\t\tctx.globalAlpha = 0.16;\n\t\tctx.drawImage(noiseCanvas, 0, 0, width, height);\n\t\tctx.globalAlpha = 1;\n\n\t\tapplyDropouts(width, height);\n\t\tapplyTrackingLines(width, height);\n\t\tapplyHeadSwitchingNoise(width, height);\n\t\tapplyScanlines(width, height);\n\n\t\tctx.fillStyle = 'rgba(18, 10, 6, 0.08)';\n\t\tctx.fillRect(0, 0, width, height);\n\n\t\tapplyVignette(width, height);\n\t}\n\n\tframeCount += 1;\n\trequestAnimationFrame(drawVHSFrame);\n}\n\nasync function togglePlayback() {\n\tif (video.paused) {\n\t\ttry {\n\t\t\tawait video.play();\n\t\t} catch (error) {\n\t\t\tconsole.error(error);\n\t\t}\n\t} else {\n\t\tvideo.pause();\n\t}\n\tupdatePlayIcon();\n}\n\nplayToggle.addEventListener('click', togglePlayback);\n\nmuteToggle.addEventListener('click', () => {\n\tvideo.muted = !video.muted;\n\tupdateMuteIcon();\n});\n\ntimeline.addEventListener('input', () => {\n\tdragging = true;\n\ttimeline.style.setProperty('--progress', `${timeline.value}%`);\n\tif (video.duration) {\n\t\tconst nextTime = (Number(timeline.value) / 100) * video.duration;\n\t\ttimeCurrent.textContent = formatTime(nextTime);\n\t}\n});\n\ntimeline.addEventListener('change', () => {\n\tif (video.duration) {\n\t\tvideo.currentTime = (Number(timeline.value) / 100) * video.duration;\n\t}\n\tdragging = false;\n});\n\nvideo.addEventListener('timeupdate', updateTimeline);\nvideo.addEventListener('loadedmetadata', () => {\n\tupdateDuration();\n\tupdateTimeline();\n});\nvideo.addEventListener('play', updatePlayIcon);\nvideo.addEventListener('pause', updatePlayIcon);\nvideo.addEventListener('volumechange', updateMuteIcon);\n\ncanvas.addEventListener('pointerup', async (event) => {\n\tif (event.pointerType === 'mouse' && event.button !== 0) return;\n\tawait togglePlayback();\n});\n\nwindow.addEventListener('resize', resizeCanvas);\nwindow.addEventListener('orientationchange', () => {\n\tsetTimeout(resizeCanvas, 120);\n});\n\nif (window.visualViewport) {\n\twindow.visualViewport.addEventListener('resize', resizeCanvas);\n}\n\nresizeCanvas();\nupdatePlayIcon();\nupdateMuteIcon();\nlucide.createIcons();\ndrawVHSFrame();\n\nvideo.muted = true;\nvideo.play().then(updatePlayIcon).catch(() => {\n\tupdatePlayIcon();\n});"},{"name":"style.css","content":":root {\n\tcolor-scheme: dark;\n\tfont-family: Inter, system-ui, sans-serif;\n\t--bg: #090909;\n\t--panel: rgba(16, 16, 16, 0.78);\n\t--line: rgba(255, 255, 255, 0.14);\n\t--text: #f3eadb;\n\t--muted: rgba(243, 234, 219, 0.7);\n\t--accent: #d86f3d;\n\t--shadow: rgba(0, 0, 0, 0.35);\n}\n\n* {\n\tbox-sizing: border-box;\n}\n\nhtml,\nbody {\n\tmargin: 0;\n\twidth: 100%;\n\tmax-width: 100%;\n\tmin-height: 100%;\n\tbackground: var(--bg);\n\tcolor: var(--text);\n}\n\nbody {\n\toverflow: hidden;\n}\n\nbutton,\ninput {\n\tfont: inherit;\n}\n\n.sr-only {\n\tposition: absolute;\n\twidth: 1px;\n\theight: 1px;\n\tpadding: 0;\n\tmargin: -1px;\n\toverflow: hidden;\n\tclip: rect(0, 0, 0, 0);\n\twhite-space: nowrap;\n\tborder: 0;\n}\n\n.player {\n\tposition: relative;\n\twidth: 100vw;\n\theight: 100vh;\n\theight: 100dvh;\n\tmin-height: 100svh;\n\tbackground: #000;\n\toverflow: hidden;\n}\n\n.player__source {\n\tdisplay: none;\n}\n\n.player__canvas,\n.player__overlay {\n\tposition: absolute;\n\tinset: 0;\n}\n\n.player__canvas {\n\tdisplay: block;\n\twidth: 100%;\n\theight: 100%;\n\tbackground: #050505;\n\tfilter: saturate(0.82) contrast(1.08) brightness(0.94);\n\ttouch-action: manipulation;\n}\n\n.player__overlay {\n\tpointer-events: none;\n\tbackground:\n\t\tlinear-gradient(to bottom, rgba(255, 255, 255, 0.07), transparent 16%, transparent 76%, rgba(0, 0, 0, 0.34)),\n\t\trepeating-linear-gradient(to bottom, rgba(255, 255, 255, 0.045) 0 1px, transparent 1px 4px),\n\t\tradial-gradient(circle at center, transparent 54%, rgba(0, 0, 0, 0.4) 100%);\n\tmix-blend-mode: screen;\n\topacity: 0.52;\n}\n\n.badge {\n\tposition: absolute;\n\ttop: calc(1rem + env(safe-area-inset-top));\n\tleft: calc(1rem + env(safe-area-inset-left));\n\tz-index: 3;\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 0.5rem;\n\tpadding: 0.5rem 0.72rem;\n\tborder: 1px solid rgba(216, 111, 61, 0.35);\n\tbackground: rgba(20, 12, 9, 0.72);\n\tletter-spacing: 0.18em;\n\ttext-transform: uppercase;\n\tfont-size: 0.72rem;\n\tpointer-events: none;\n\tmax-width: calc(100vw - 2rem - env(safe-area-inset-left) - env(safe-area-inset-right));\n}\n\n.badge__dot {\n\twidth: 0.55rem;\n\theight: 0.55rem;\n\tborder-radius: 50%;\n\tbackground: var(--accent);\n\tbox-shadow: 0 0 14px rgba(216, 111, 61, 0.45);\n\tflex: 0 0 auto;\n}\n\n.player__panel {\n\tposition: absolute;\n\tleft: calc(1rem + env(safe-area-inset-left));\n\tright: calc(1rem + env(safe-area-inset-right));\n\tbottom: calc(1rem + env(safe-area-inset-bottom));\n\tz-index: 3;\n\tdisplay: grid;\n\tgap: 0.85rem;\n\tpadding: 1rem;\n\tbackground: var(--panel);\n\tborder: 1px solid var(--line);\n\tbackdrop-filter: blur(14px);\n\tbox-shadow: 0 12px 32px var(--shadow);\n\tmax-width: calc(100vw - 2rem - env(safe-area-inset-left) - env(safe-area-inset-right));\n}\n\n.title-card {\n\tmin-width: 0;\n}\n\n.title-card__eyebrow {\n\tmargin: 0 0 0.35rem;\n\tcolor: var(--muted);\n\tfont-size: 0.74rem;\n\tletter-spacing: 0.18em;\n\ttext-transform: uppercase;\n}\n\n.title-card__title {\n\tmargin: 0;\n\tfont-size: clamp(1.5rem, 5vw, 4.6rem);\n\tline-height: 0.95;\n\tletter-spacing: -0.05em;\n\tfont-weight: 700;\n\ttext-wrap: balance;\n\tword-break: break-word;\n}\n\n.controls {\n\tdisplay: grid;\n\tgap: 0.75rem;\n\tmin-width: 0;\n}\n\n.controls__top {\n\tdisplay: grid;\n\tgrid-template-columns: auto minmax(0, 1fr) auto;\n\talign-items: center;\n\tgap: 0.75rem;\n\tmin-width: 0;\n}\n\n.controls__button {\n\tdisplay: inline-grid;\n\tplace-items: center;\n\twidth: 2.9rem;\n\theight: 2.9rem;\n\tborder: 1px solid var(--line);\n\tbackground: rgba(255, 255, 255, 0.04);\n\tcolor: var(--text);\n\tcursor: pointer;\n\ttransition: background 150ms ease, border-color 150ms ease, transform 150ms ease;\n\t-webkit-tap-highlight-color: transparent;\n\tflex: 0 0 auto;\n}\n\n.controls__button:hover,\n.controls__button:focus-visible {\n\tbackground: rgba(255, 255, 255, 0.1);\n\tborder-color: rgba(255, 255, 255, 0.22);\n}\n\n.controls__button:active {\n\ttransform: translateY(1px);\n}\n\n.controls__button i {\n\twidth: 1rem;\n\theight: 1rem;\n}\n\n.controls__timeline-block {\n\tmin-width: 0;\n}\n\n.controls__timeline {\n\twidth: 100%;\n\tmargin: 0;\n\tappearance: none;\n\theight: 0.36rem;\n\tbackground: linear-gradient(to right, var(--accent) 0%, var(--accent) var(--progress, 0%), rgba(255, 255, 255, 0.15) var(--progress, 0%), rgba(255, 255, 255, 0.15) 100%);\n\tborder-radius: 999px;\n\toutline: none;\n\tcursor: pointer;\n}\n\n.controls__timeline::-webkit-slider-thumb {\n\tappearance: none;\n\twidth: 0.95rem;\n\theight: 0.95rem;\n\tborder-radius: 50%;\n\tborder: 0;\n\tbackground: var(--text);\n}\n\n.controls__timeline::-moz-range-thumb {\n\twidth: 0.95rem;\n\theight: 0.95rem;\n\tborder-radius: 50%;\n\tborder: 0;\n\tbackground: var(--text);\n}\n\n.controls__meta {\n\tdisplay: flex;\n\tjustify-content: center;\n\talign-items: center;\n\tgap: 0.35rem;\n\tmin-width: 0;\n\tcolor: var(--muted);\n\tfont-size: 0.9rem;\n\tfont-variant-numeric: tabular-nums;\n\twhite-space: nowrap;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n}\n\n.controls__divider {\n\topacity: 0.5;\n}\n\n@media (max-width: 720px) {\n\t.player__panel {\n\t\tleft: max(0.75rem, env(safe-area-inset-left));\n\t\tright: max(0.75rem, env(safe-area-inset-right));\n\t\tbottom: max(0.75rem, env(safe-area-inset-bottom));\n\t\tgap: 0.75rem;\n\t\tpadding: 0.85rem;\n\t}\n\n\t.badge {\n\t\ttop: max(0.75rem, env(safe-area-inset-top));\n\t\tleft: max(0.75rem, env(safe-area-inset-left));\n\t\tmax-width: calc(100vw - 1.5rem - 1.5rem - env(safe-area-inset-left) - env(safe-area-inset-right));\n\t}\n\n\t.title-card__eyebrow {\n\t\tfont-size: 0.68rem;\n\t\tmargin-bottom: 0.2rem;\n\t}\n\n\t.title-card__title {\n\t\tfont-size: clamp(1.15rem, 7.2vw, 2rem);\n\t}\n\n\t.controls__top {\n\t\tgrid-template-columns: auto 1fr auto;\n\t\tgap: 0.6rem;\n\t}\n\n\t.controls__meta {\n\t\tfont-size: 0.82rem;\n\t}\n\n\t.controls__button {\n\t\twidth: 2.75rem;\n\t\theight: 2.75rem;\n\t}\n}\n\n@media (max-width: 420px) {\n\t.player__panel {\n\t\tleft: max(0.6rem, env(safe-area-inset-left));\n\t\tright: max(0.6rem, env(safe-area-inset-right));\n\t\tbottom: max(0.6rem, env(safe-area-inset-bottom));\n\t\tpadding: 0.75rem;\n\t\tgap: 0.65rem;\n\t}\n\n\t.badge {\n\t\ttop: max(0.6rem, env(safe-area-inset-top));\n\t\tleft: max(0.6rem, env(safe-area-inset-left));\n\t\tpadding: 0.42rem 0.58rem;\n\t\tfont-size: 0.62rem;\n\t\tletter-spacing: 0.14em;\n\t\tgap: 0.4rem;\n\t}\n\n\t.badge__text {\n\t\toverflow: hidden;\n\t\ttext-overflow: ellipsis;\n\t}\n\n\t.title-card__eyebrow {\n\t\tfont-size: 0.62rem;\n\t\tletter-spacing: 0.14em;\n\t}\n\n\t.title-card__title {\n\t\tfont-size: clamp(1rem, 8vw, 1.55rem);\n\t\tline-height: 1;\n\t}\n\n\t.controls__top {\n\t\tgrid-template-columns: auto 1fr;\n\t\tgrid-template-areas:\n\t\t\t\"play mute\"\n\t\t\t\"meta meta\";\n\t\tgap: 0.55rem 0.5rem;\n\t}\n\n\t#playToggle {\n\t\tgrid-area: play;\n\t}\n\n\t#muteToggle {\n\t\tgrid-area: mute;\n\t\tjustify-self: end;\n\t}\n\n\t.controls__meta {\n\t\tgrid-area: meta;\n\t\tjustify-content: flex-start;\n\t\tfont-size: 0.78rem;\n\t}\n\n\t.controls__button {\n\t\twidth: 2.6rem;\n\t\theight: 2.6rem;\n\t}\n\n\t.controls__timeline {\n\t\theight: 0.32rem;\n\t}\n}\n\n@media (max-width: 340px) {\n\t.player__panel {\n\t\tpadding: 0.65rem;\n\t}\n\n\t.title-card__title {\n\t\tfont-size: 0.95rem;\n\t}\n\n\t.controls__meta {\n\t\tfont-size: 0.72rem;\n\t}\n}"}],"folders":[]},"variants":null,"createdAt":"2026-03-08T22:43:28.259Z","updatedAt":"2026-03-08T22:43:33.625Z","hasThumbnail":true}}