const { Icon, Button } = window.AIProductionStudioDesignSystem_16a811;
// 4. Infinite ribbon — film-strip timeline of micro case studies, drag / wheel / auto-scroll
const PF_TOOL_ICON = { 'Nuke': 'atom', 'After Effects': 'layers', 'Premiere Pro': 'film', 'Custom AI models': 'cpu', 'Houdini': 'box', 'Photoshop': 'image', 'Silhouette': 'spline', 'Mocha Pro': 'crosshair', 'SynthEyes': 'eye', 'Beauty Box': 'scan-face', 'DaVinci Resolve': 'palette' };
function PfRibbon({ onOpen }) {
const track = React.useRef(null);
const st = React.useRef({ off: 0, drag: false, moved: 0, last: 0, hover: false, vel: 0 });
React.useEffect(() => {
let raf;
const tick = () => {
const s = st.current, el = track.current;
if (el) {
const w = el.scrollWidth / 2;
if (!s.drag) { s.off += s.hover ? s.vel : .5 + s.vel; s.vel *= .92; }
if (w) s.off = ((s.off % w) + w) % w;
el.style.transform = `translate3d(${-s.off}px,0,0)`;
}
raf = requestAnimationFrame(tick);
};
raf = requestAnimationFrame(tick);
return () => cancelAnimationFrame(raf);
}, []);
const down = (e) => { const s = st.current; s.drag = true; s.moved = 0; s.last = e.clientX; e.currentTarget.setPointerCapture(e.pointerId); };
const mv = (e) => { const s = st.current; if (!s.drag) return; const dx = e.clientX - s.last; s.last = e.clientX; s.off -= dx; s.moved += Math.abs(dx); s.vel = -dx * .6; };
const up = () => { st.current.drag = false; };
const wheel = (e) => { const d = Math.abs(e.deltaX) > Math.abs(e.deltaY) ? e.deltaX : 0; if (d) { st.current.off += d; } };
const items = [...PF_CASES, ...PF_CASES];
const holes = { height: 14, backgroundImage: 'linear-gradient(90deg,rgba(255,255,255,.16) 0 10px,transparent 10px 28px)', backgroundSize: '28px 6px', backgroundRepeat: 'repeat-x', backgroundPosition: '0 50%' };
return (
Brief, toolkit, deliverable
{c.brief}
Sample available on request.