/* global React */ // frames.jsx — animated cinematic "film still" placeholders. // Pure CSS/SVG gradients with grain — no hand-drawn imagery. // Each Frame takes a `tone` (color-grade) and renders a moving abstract still. const { useEffect, useRef } = React; function Frame({ tone = 'dusk', label, motion = true }) { const palettes = { dusk: ['#3b1d2e', '#7a2e3d', '#d68a5a', '#f1c98a'], blue: ['#0a1426', '#1a3656', '#3a6e9c', '#9bc0e0'], ember: ['#160a08', '#3a1408', '#7a2a14', '#d96a3a'], void: ['#070707', '#181818', '#2a2a2a', '#5a5a5a'], desert: ['#2a1812', '#6e3a22', '#c97a3a', '#f0c277'], neon: ['#0a0418', '#2a1454', '#6a2ec0', '#f06ed8'], forest: ['#08120c', '#1a3a26', '#3a6e44', '#9ec48a'], pale: ['#1a1a1a', '#3a3a38', '#8a857a', '#dcd6c8'], }[tone] || ['#1a1a1a','#333','#666','#aaa']; const id = useRef('grad-' + Math.random().toString(36).slice(2, 9)).current; return (
{/* moving abstract orbs */} {motion && } {motion && } {!motion && } {motion && } {motion && } {motion && } {motion && } {/* horizon hint */} {/* color grade overlays */} {/* film perforation marks */} ▢ {label} 24fps 2.39:1 · · · {/* scanlines on hover */}
); } // css for scanlines added via inline style block if (!document.getElementById('frame-host-style')) { const s = document.createElement('style'); s.id = 'frame-host-style'; s.textContent = ` .frame-host{ background:#000; } .frame-lines{ position:absolute; inset:0; pointer-events:none; background: repeating-linear-gradient(0deg, rgba(255,255,255,0) 0px, rgba(255,255,255,0) 3px, rgba(255,255,255,.04) 4px, rgba(255,255,255,0) 5px); opacity:0; transition: opacity .5s ease; } .work-card:hover .frame-lines{ opacity:1; } `; document.head.appendChild(s); } window.Frame = Frame;