{"version":3,"file":"ScrollText.AjEVoXwW.mjs","names":["useRef"],"sources":["https:/framerusercontent.com/modules/5kanfNvUwda2beheOs4F/XO4Xq284hkGgusdRxjx8/ScrollText.js"],"sourcesContent":["import{jsx as _jsx}from\"react/jsx-runtime\";import{useEffect,useRef}from\"react\";import{RenderTarget}from\"framer\";/**\n * Scroll Text\n *\n * Reveals a text layer word by word, tied directly to the scroll position.\n * The animation runs forward as you scroll down and backward as you scroll\n * up, and always finishes exactly where you tell it to.\n *\n * Nothing here plays on a timer: every word's opacity is a function of how\n * far the layer has travelled up the screen, so the reveal follows the\n * wheel one to one.\n *\n * Position is sampled on an animation frame rather than from scroll events,\n * because Framer's published pages do not reliably emit them.\n *\n * Apply it to a text layer. Everything below is safe to tune.\n *//**\n * DIM — the opacity of a word before it is revealed.\n *   0    = invisible until revealed, the highest contrast\n *   0.16 = ghosted, so the whole paragraph stays readable throughout\n */const DIM=.16;/**\n * START_AT / END_AT — where on the screen the reveal runs, measured down\n * from the top of the viewport as a fraction of its height.\n *\n *   START_AT = 0.9  the first word starts when the layer's top edge is 90%\n *                   down the screen, just as it rises into view\n *   END_AT   = 0.3  the last word lands when the layer's bottom edge\n *                   reaches 30% down the screen\n *\n * Push the two further apart for a longer, slower reveal; bring them\n * together to make it snappier.\n */const START_AT=.9;const END_AT=.3;/**\n * OVERLAP — how much of the whole reveal a single word occupies.\n *   0.05 = words light up almost one at a time, a sharp travelling edge\n *   0.3  = words overlap, a soft wash moving across the paragraph\n */const OVERLAP=.3;export function withScrollText(Component){return props=>{const wrapper=useRef(null);const isCanvas=RenderTarget.current()===RenderTarget.canvas;useEffect(()=>{if(isCanvas)return;if(typeof window===\"undefined\")return;const root=wrapper.current;if(!root)return;const target=root.firstElementChild;if(!target)return;// Keep a pristine copy so the split can always be undone.\nconst original=target.innerHTML;const restore=()=>{target.innerHTML=original;};// Split each run of text into per-word spans, leaving the spaces\n// between them as plain text so the paragraph still wraps.\nconst walker=document.createTreeWalker(target,NodeFilter.SHOW_TEXT);const textNodes=[];while(walker.nextNode()){const node=walker.currentNode;if((node.nodeValue||\"\").trim().length>0)textNodes.push(node);}const words=[];for(const node of textNodes){const fragment=document.createDocumentFragment();for(const part of(node.nodeValue||\"\").split(/(\\s+)/)){if(!part)continue;if(/^\\s+$/.test(part)){fragment.appendChild(document.createTextNode(part));continue;}const span=document.createElement(\"span\");span.textContent=part;span.style.display=\"inline-block\";span.style.willChange=\"opacity\";fragment.appendChild(span);words.push(span);}if(node.parentNode)node.parentNode.replaceChild(fragment,node);}if(words.length===0)return restore;// Readers who ask for reduced motion get the finished state.\nif(window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches){for(const word of words)word.style.opacity=\"1\";return restore;}const count=words.length;const step=count>1?(1-OVERLAP)/(count-1):0;// The element's position in the document only changes on\n// layout, so measure it once and derive its on-screen position\n// from the scroll offset. Calling getBoundingClientRect() every\n// frame forces the browser to recompute layout mid-scroll, which\n// drops frames and makes the whole page judder.\nlet docTop=0;let height=0;const measure=()=>{const rect=target.getBoundingClientRect();docTop=rect.top+window.scrollY;height=rect.height;};const paint=()=>{const viewport=window.innerHeight;const top=docTop-window.scrollY;const distance=(START_AT-END_AT)*viewport+height;const travelled=START_AT*viewport-top;const progress=distance>0?travelled/distance:1;for(let i=0;i<count;i++){const local=(progress-i*step)/OVERLAP;const clamped=local<0?0:local>1?1:local;words[i].style.opacity=String(DIM+(1-DIM)*clamped);}};measure();paint();// Track scroll three ways, because no single trigger is\n// dependable: animation frames are suspended in background tabs,\n// and Framer's published pages do not always emit scroll events.\nlet frame=0;let lastY=NaN;const update=()=>{const y=window.scrollY;if(y!==lastY){lastY=y;paint();}};const remeasure=()=>{measure();paint();};const tick=()=>{update();frame=requestAnimationFrame(tick);};frame=requestAnimationFrame(tick);window.addEventListener(\"scroll\",update,{passive:true});window.addEventListener(\"resize\",remeasure);let observer=null;if(typeof ResizeObserver!==\"undefined\"){observer=new ResizeObserver(remeasure);observer.observe(target);}return()=>{cancelAnimationFrame(frame);window.removeEventListener(\"scroll\",update);window.removeEventListener(\"resize\",remeasure);if(observer)observer.disconnect();restore();};},[isCanvas]);if(isCanvas)return /*#__PURE__*/_jsx(Component,{...props});return /*#__PURE__*/_jsx(\"div\",{ref:wrapper,style:{display:\"contents\"},children:/*#__PURE__*/_jsx(Component,{...props})});};}\nexport const __FramerMetadata__ = {\"exports\":{\"withScrollText\":{\"type\":\"reactHoc\",\"name\":\"withScrollText\",\"annotations\":{\"framerContractVersion\":\"1\"}},\"__FramerMetadata__\":{\"type\":\"variable\"}}}\n//# sourceMappingURL=./ScrollText.map"],"mappings":"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"}