// Capabilities section — min-h-screen, full-bleed video bg, 3 cards
const { motion: capMotion } = window.Motion;
const { useEffect: capUseEffect, useRef: capUseRef, useState: capUseState } = React;
const CAP_VIDEO = "https://d8j0ntlcm91z4.cloudfront.net/user_38xzZboKViGWJOttwIXH07lWA1P/hf_20260418_094631_d30ab262-45ee-4b7d-99f3-5d5848c8ef13.mp4";
// whileInView is unreliable on the framer-motion 10.18.0 UMD build. Use a
// hook-driven IntersectionObserver that flips an `animate` prop instead.
function useInViewOnce(threshold = 0.2) {
const ref = capUseRef(null);
const [shown, setShown] = capUseState(false);
capUseEffect(() => {
const el = ref.current;
if (!el) return;
const io = new IntersectionObserver(
(entries) => {
entries.forEach((e) => {
if (e.isIntersecting) {
setShown(true);
io.disconnect();
}
});
},
{ threshold }
);
io.observe(el);
return () => io.disconnect();
}, []);
return [ref, shown];
}
const fadeUp = (delay = 0, shown = true) => ({
initial: { filter: 'blur(10px)', opacity: 0, y: 20 },
animate: shown
? { filter: 'blur(0px)', opacity: 1, y: 0 }
: { filter: 'blur(10px)', opacity: 0, y: 20 },
transition: { duration: 0.8, ease: 'easeOut', delay },
});
function CapTag({ children }) {
return (
{children}
);
}
function CapCard({ icon, tags, title, body, delay }) {
const [ref, shown] = useInViewOnce(0.15);
return (
{body}
{title}
{/* Tag pills */}
// Cómo funciona