{"version":3,"file":"LottieInView.DJjJSzZP.mjs","names":["useRef","useState"],"sources":["https:/framerusercontent.com/modules/8aguuC64jFcQLUHfTl62/rIkPoP3ZjXlFnqNt9ape/LottieInView.js"],"sourcesContent":["// @ts-nocheck\nimport{jsx as _jsx}from\"react/jsx-runtime\";import{addPropertyControls,ControlType,RenderTarget}from\"framer\";import{useRef,useEffect,useState}from\"react\";// ─── Why this exists ─────────────────────────────────────────────────────────\n//\n// Framer's built-in Lottie component starts every animation on mount and never\n// stops it. Its `canvas` property is NOT a renderer switch — internally it is\n// `pauseOnCanvas`, which only pauses playback inside the Framer editor. On the\n// published site every animation keeps running forever, including the ones far\n// below the fold that nobody is looking at.\n//\n// On jemmic.com that measured as ~13 percentage points of continuous CPU on the\n// homepage, with five animations running permanently off-screen.\n//\n// This component keeps the loop, but only spends CPU while the animation is\n// actually visible:\n//   • IntersectionObserver pauses playback when the element leaves the viewport\n//     and resumes it when it comes back\n//   • the Page Visibility API pauses everything when the tab is in the\n//     background\n//   • lottie-web itself is imported lazily, so its ~60 KB only downloads once an\n//     animation is genuinely about to play\n//   • prefers-reduced-motion renders a static first frame instead of animating\n// ─── Constants ───────────────────────────────────────────────────────────────\nconst LOTTIE_MODULE=\"https://esm.sh/lottie-web@5.12.2/build/player/lottie_light\";// Start loading slightly before the element scrolls into view so playback does\n// not visibly begin mid-animation.\nconst ROOT_MARGIN=\"200px\";// ─── Module loader ───────────────────────────────────────────────────────────\n// Shared across every instance on the page, so the library is fetched once.\nlet lottiePromise=null;function loadLottie(){if(!lottiePromise){lottiePromise=import(/* webpackIgnore: true */LOTTIE_MODULE).then(m=>m.default??m).catch(error=>{// Reset so a later instance can retry after a transient failure.\nlottiePromise=null;throw error;});}return lottiePromise;}function prefersReducedMotion(){if(typeof window===\"undefined\"||!window.matchMedia)return false;return window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches;}/**\n * Lottie player that only animates while on screen.\n *\n * @framerIntrinsicWidth 200\n * @framerIntrinsicHeight 200\n * @framerSupportedLayoutWidth any\n * @framerSupportedLayoutHeight any\n */export default function LottieInView(props){const{srcType,url,file,loop,speed,direction,renderer,pauseInEditor,threshold,style}=props;const containerRef=useRef(null);const animationRef=useRef(null);const inViewRef=useRef(false);const[failed,setFailed]=useState(false);const source=srcType===\"File\"?file:url;const onEditorCanvas=RenderTarget.current()===RenderTarget.canvas;const inert=pauseInEditor&&onEditorCanvas;// ─── Create and destroy the animation ────────────────────────────────────\nuseEffect(()=>{if(!source||inert)return;let cancelled=false;const container=containerRef.current;if(!container)return;loadLottie().then(lottie=>{if(cancelled||!containerRef.current)return;const animation=lottie.loadAnimation({container:containerRef.current,renderer:renderer===\"Canvas\"?\"canvas\":\"svg\",loop,// Never autoplay: the observer below decides when to start.\nautoplay:false,path:source,rendererSettings:{preserveAspectRatio:\"xMidYMid meet\",progressiveLoad:true}});animation.setSpeed(speed);animation.setDirection(direction?1:-1);animationRef.current=animation;// If the element was already on screen while the library was\n// still downloading, start as soon as the frames are ready.\nanimation.addEventListener(\"DOMLoaded\",()=>{if(cancelled)return;if(prefersReducedMotion()){animation.goToAndStop(0,true);}else if(inViewRef.current){animation.play();}});}).catch(()=>{if(!cancelled)setFailed(true);});return()=>{cancelled=true;if(animationRef.current){animationRef.current.destroy();animationRef.current=null;}};},[source,loop,renderer,inert]);// Speed and direction can change without rebuilding the animation.\nuseEffect(()=>{if(animationRef.current)animationRef.current.setSpeed(speed);},[speed]);useEffect(()=>{if(animationRef.current)animationRef.current.setDirection(direction?1:-1);},[direction]);// ─── Play only while visible ─────────────────────────────────────────────\nuseEffect(()=>{if(inert)return;const container=containerRef.current;if(!container||typeof IntersectionObserver===\"undefined\")return;const apply=()=>{const animation=animationRef.current;if(!animation)return;const shouldRun=inViewRef.current&&document.visibilityState===\"visible\"&&!prefersReducedMotion();if(shouldRun)animation.play();else animation.pause();};const observer=new IntersectionObserver(([entry])=>{inViewRef.current=entry.isIntersecting;apply();},{rootMargin:ROOT_MARGIN,threshold});observer.observe(container);document.addEventListener(\"visibilitychange\",apply);return()=>{observer.disconnect();document.removeEventListener(\"visibilitychange\",apply);};},[inert,threshold]);// ─── Render ──────────────────────────────────────────────────────────────\nif(failed){// Stay silent on the published site rather than showing a broken box.\nreturn /*#__PURE__*/_jsx(\"div\",{style:{...style}});}return /*#__PURE__*/_jsx(\"div\",{ref:containerRef,style:{...style,width:\"100%\",height:\"100%\",overflow:\"hidden\"}});}LottieInView.defaultProps={srcType:\"URL\",url:\"\",loop:true,speed:1,direction:true,renderer:\"SVG\",pauseInEditor:true,threshold:0};addPropertyControls(LottieInView,{srcType:{type:ControlType.Enum,title:\"Source\",options:[\"URL\",\"File\"],optionTitles:[\"URL\",\"File\"],displaySegmentedControl:true},url:{type:ControlType.String,title:\"URL\",placeholder:\"https://…/animation.json\",hidden:p=>p.srcType!==\"URL\"},file:{type:ControlType.File,title:\"File\",allowedFileTypes:[\"json\"],hidden:p=>p.srcType!==\"File\"},renderer:{type:ControlType.Enum,title:\"Renderer\",options:[\"SVG\",\"Canvas\"],optionTitles:[\"SVG\",\"Canvas\"],displaySegmentedControl:true,description:\"SVG matches Framer's built-in Lottie exactly. Canvas is cheaper on the CPU but rasterised, so thin strokes can look softer on high-density screens.\"},loop:{type:ControlType.Boolean,title:\"Loop\"},speed:{type:ControlType.Number,title:\"Speed\",min:.1,max:4,step:.1,displayStepper:true},direction:{type:ControlType.Boolean,title:\"Direction\",enabledTitle:\"Forward\",disabledTitle:\"Reverse\"},pauseInEditor:{type:ControlType.Boolean,title:\"Pause in editor\",description:\"Keeps the Framer canvas still while you design. No effect on the published site.\"},threshold:{type:ControlType.Number,title:\"Threshold\",min:0,max:1,step:.05,description:\"How much of the animation must be on screen before it plays. 0 means any sliver.\"}});\nexport const __FramerMetadata__ = {\"exports\":{\"default\":{\"type\":\"reactComponent\",\"name\":\"LottieInView\",\"slots\":[],\"annotations\":{\"framerContractVersion\":\"1\",\"framerSupportedLayoutHeight\":\"any\",\"framerIntrinsicHeight\":\"200\",\"framerIntrinsicWidth\":\"200\",\"framerSupportedLayoutWidth\":\"any\"}},\"__FramerMetadata__\":{\"type\":\"variable\"}}}\n//# sourceMappingURL=./LottieInView.map"],"mappings":"8MA0BuB,SAAS,GAAY,CACT,MADU,CAAmB,IAAc,OAAgC,EAAc,CAAC,KAAK,GAAG,EAAE,SAAS,CAAC,CAAC,CAAC,MAAM,GAAO,CAC7I,KAAnB,GAAc,KAAW,CAAM,CAAC,EAAU,CAAc,CAAC,SAAS,GAAsB,CAAiE,OAAtD,IAAS,QAAa,CAAC,EAAO,WAAkB,GAAa,EAAO,WAAW,kCAAkC,CAAC,CAAC,OAAQ,CAO3N,SAAwB,EAAa,EAAM,CAAC,GAAK,CAAC,UAAQ,MAAI,OAAK,OAAK,QAAM,YAAU,WAAS,gBAAc,YAAU,SAAO,EAAY,EAAaA,EAAO,IAAI,EAAQ,EAAaA,EAAO,IAAI,EAAQ,EAAUA,EAAO,EAAK,EAAO,CAAC,EAAO,GAAWC,EAAS,EAAK,EAAQ,EAAO,IAAU,OAAO,EAAK,EAAU,EAAe,EAAa,QAAQ,IAAI,EAAa,OAAa,EAAM,GAAe,EAQ/V,OAPpD,MAAc,CAAC,GAAG,CAAC,GAAQ,EAAM,OAAO,IAAI,EAAU,GAAsB,KAAa,QAGgI,OAHnG,EAAW,CAAC,CAAC,KAAK,GAAQ,CAAC,GAAG,GAAW,CAAC,EAAa,QAAQ,OAAO,IAAM,EAAU,EAAO,cAAc,CAAC,UAAU,EAAa,QAAQ,SAAS,IAAW,SAAS,SAAS,MAAM,OAC7S,SAAS,GAAM,KAAK,EAAO,iBAAiB,CAAC,oBAAoB,gBAAgB,gBAAgB,EAAI,CAAC,CAAC,EAAE,EAAU,SAAS,CAAK,EAAE,EAAU,aAAa,EAAU,EAAE,EAAE,EAAE,EAAa,QAAQ,EAE/L,EAAU,iBAAiB,gBAAgB,CAAI,IAAoB,EAAqB,EAAG,EAAU,YAAY,EAAE,EAAI,EAAW,EAAU,SAAS,EAAU,KAAK,EAAG,CAAC,CAAE,CAAC,CAAC,CAAC,UAAU,CAAK,GAAU,EAAU,EAAI,CAAE,CAAC,MAAY,CAAC,EAAU,GAAK,AAAwD,EAAa,WAA5C,EAAa,QAAQ,QAAQ,EAAuB,KAAM,CAAE,EAAE,CAAC,EAAO,EAAK,EAAS,CAAK,CAAC,EACtW,MAAc,CAAI,EAAa,SAAQ,EAAa,QAAQ,SAAS,CAAK,CAAE,EAAE,CAAC,CAAK,CAAC,EAAE,MAAc,CAAI,EAAa,SAAQ,EAAa,QAAQ,aAAa,EAAU,EAAE,EAAE,CAAE,EAAE,CAAC,CAAS,CAAC,EAC7L,MAAc,CAAC,GAAG,EAAM,OAAO,IAAM,EAAU,EAAa,QAAQ,GAAG,CAAC,GAAW,OAAO,qBAAuB,IAAY,OAAO,IAAM,MAAU,CAAC,IAAM,EAAU,EAAa,QAAY,IAAiC,EAAU,SAAS,SAAS,kBAAkB,WAAW,CAAC,EAAqB,EAAe,EAAU,KAAK,EAAO,EAAU,MAAM,EAAE,EAAQ,EAAS,IAAI,sBAAsB,CAAC,KAAS,CAAC,EAAU,QAAQ,EAAM,eAAe,EAAM,CAAE,EAAE,CAAC,WAAW,EAAY,WAAS,CAAC,EAAkF,OAAhF,EAAS,QAAQ,CAAS,EAAE,SAAS,iBAAiB,mBAAmB,CAAK,MAAY,CAAC,EAAS,WAAW,EAAE,SAAS,oBAAoB,mBAAmB,CAAK,CAAE,CAAE,EAAE,CAAC,EAAM,CAAS,CAAC,EAC1qB,EACiB,EAAK,MAAM,CAAC,MAAM,CAAC,GAAG,CAAK,CAAC,CAAC,EAAuB,EAAK,MAAM,CAAC,IAAI,EAAa,MAAM,CAAC,GAAG,EAAM,MAAM,OAAO,OAAO,OAAO,SAAS,QAAQ,CAAC,CAAC,CAAE,2BAzC3H,IAAiE,IAA6C,EAqBlJ,EAAc,6DAEd,EAAY,QAEd,EAAc,KAgBoJ,EAAa,aAAa,CAAC,QAAQ,MAAM,IAAI,GAAG,KAAK,GAAK,MAAM,EAAE,UAAU,GAAK,SAAS,MAAM,cAAc,GAAK,UAAU,CAAC,EAAE,EAAoB,EAAa,CAAC,QAAQ,CAAC,KAAK,EAAY,KAAK,MAAM,SAAS,QAAQ,CAAC,MAAM,MAAM,EAAE,aAAa,CAAC,MAAM,MAAM,EAAE,wBAAwB,EAAI,EAAE,IAAI,CAAC,KAAK,EAAY,OAAO,MAAM,MAAM,YAAY,2BAA2B,OAAO,GAAG,EAAE,UAAU,KAAK,EAAE,KAAK,CAAC,KAAK,EAAY,KAAK,MAAM,OAAO,iBAAiB,CAAC,MAAM,EAAE,OAAO,GAAG,EAAE,UAAU,MAAM,EAAE,SAAS,CAAC,KAAK,EAAY,KAAK,MAAM,WAAW,QAAQ,CAAC,MAAM,QAAQ,EAAE,aAAa,CAAC,MAAM,QAAQ,EAAE,wBAAwB,GAAK,YAAY,qJAAqJ,EAAE,KAAK,CAAC,KAAK,EAAY,QAAQ,MAAM,MAAM,EAAE,MAAM,CAAC,KAAK,EAAY,OAAO,MAAM,QAAQ,IAAI,GAAG,IAAI,EAAE,KAAK,GAAG,eAAe,EAAI,EAAE,UAAU,CAAC,KAAK,EAAY,QAAQ,MAAM,YAAY,aAAa,UAAU,cAAc,SAAS,EAAE,cAAc,CAAC,KAAK,EAAY,QAAQ,MAAM,kBAAkB,YAAY,kFAAkF,EAAE,UAAU,CAAC,KAAK,EAAY,OAAO,MAAM,YAAY,IAAI,EAAE,IAAI,EAAE,KAAK,IAAI,YAAY,kFAAkF,CAAC,CAAC"}