1(() => { 2 'use strict'; 3 const {gsap,ScrollTrigger,ScrollToPlugin}=window; 4 if(!gsap||!ScrollTrigger)return; 5 gsap.registerPlugin(ScrollTrigger);if(ScrollToPlugin)gsap.registerPlugin(ScrollToPlugin); 6 const root=document.documentElement,reduced=matchMedia('(prefers-reduced-motion: reduce)'); 7 const off=()=>reduced.matches||root.classList.contains('preview-effects-off'); 8 ScrollTrigger.config({ignoreMobileResize:true}); 9 // Use the small viewport height (100svh) so the iOS toolbar showing or hiding never toggles the orbit layout. 10 const touch=matchMedia('(hover: none) and (pointer: coarse)').matches; 11 let vhProbe=document.getElementById('scalz-vh-probe'); 12 if(!vhProbe){vhProbe=document.createElement('div');vhProbe.id='scalz-vh-probe';vhProbe.setAttribute('aria-hidden','true');vhProbe.style.cssText='position:fixed;top:0;left:0;width:1px;height:100vh;height:100svh;visibility:hidden;pointer-events:none;z-index:-1';document.body.appendChild(vhProbe);} 13 const stableHeight=()=>vhProbe.offsetHeight||innerHeight; 14 const applyPhoneTier=()=>{const phone=innerWidth<=760,h=stableHeight();root.classList.toggle('phone-short',phone&&h<700);root.classList.toggle('phone-tiny',phone&&h<640);}; 15 const canOrbit=()=>!off()&&(innerWidth<=760||stableHeight()>=700); 16 const track=document.querySelector('.orbit-track'),cards=[...document.querySelectorAll('.orbit-ring>.spotlight-card')]; 17 const selectors=[...document.querySelectorAll('[data-orbit-index]')],prev=document.querySelector('.orbit-prev'),next=document.querySelector('.orbit-next'); 18 let context,trigger,navigation,current=0,orbitState={position:0,arrival:1,field:1}; 19 const controls=document.querySelector('.orbit-controls'); 20 const explorationStart=.38; 21 const loops=[]; 22 function renderOrbit(value){ 23 const radius=Math.min(440,track.clientWidth*.34),mobile=innerWidth<=760; 24 cards.forEach((card,i)=>{ 25 let angle=(i-value)*72;angle=((angle+180)%360+360)%360-180; 26 const radians=angle*Math.PI/180,cos=Math.cos(radians),arrival=orbitState.arrival,front=i===Math.round(value)&&arrival>.65; 27 const lateral=Math.sin(radians)*radius*(mobile?.22+.78*(1-Math.max(0,cos)):1); 28 gsap.set(card,{xPercent:-50,x:lateral,y:(1-arrival)*90,z:(cos-1)*(mobile?radius*.42:radius*.66)-(1-arrival)*500,rotationX:(1-arrival)*18,rotationY:-angle*.54+(1-arrival)*32,scale:(.86+.14*Math.max(0,cos))*(.76+.24*arrival),opacity:Math.max(.08,.32+cos*.68)*arrival,zIndex:Math.round(100+cos*100)}); 29 card.inert=!front;card.setAttribute('aria-hidden',String(!front));card.classList.toggle('is-front',front);card.removeAttribute('tabindex'); 30 }); 31 current=Math.max(0,Math.min(4,Math.round(value))); 32 selectors.forEach((button,i)=>button.setAttribute('aria-pressed',String(i===current))); 33 if(prev)prev.disabled=current===0;if(next)next.disabled=current===4; 34 track.dataset.position=value.toFixed(3); 35 track.dataset.arrival=orbitState.arrival.toFixed(3); 36 track.dataset.field=orbitState.field.toFixed(3); 37 if(controls){gsap.set(controls,{opacity:orbitState.arrival});controls.inert=orbitState.arrival<.65;} 38 } 39 function navigate(index){ 40 const i=Math.max(0,Math.min(cards.length-1,index));navigation?.kill(); 41 if(trigger&&!off()&&ScrollToPlugin)navigation=gsap.to(window,{scrollTo:{y:trigger.start+(explorationStart+i/4*(1-explorationStart))*(trigger.end-trigger.start),autoKill:true},duration:.8,ease:'power2.inOut',overwrite:'auto'}); 42 else {orbitState.position=i;renderOrbit(i);} 43 } 44 selectors.forEach((button,i)=>button.addEventListener('click',()=>navigate(i))); 45 prev?.addEventListener('click',()=>navigate(current-1));next?.addEventListener('click',()=>navigate(current+1)); 46 document.querySelector('.orbit-controls')?.addEventListener('keydown',event=>{ 47 if(event.key==='ArrowRight'||event.key==='ArrowLeft'){ 48 event.preventDefault();const i=Math.max(0,Math.min(4,current+(event.key==='ArrowRight'?1:-1)));selectors[i].focus();navigate(i); 49 } 50 }); 51 // Each boundary has a different route through the same atmosphere. 52 const sectorConfigs=[ 53 ['.foundation','M-100 230 Q600 -100 1300 230',0], 54 ['.industries','M-100 320 C160 100 700 340 1300 -50',1], 55 ['.experience','M-100 20 C380 350 860 -70 1300 260',2], 56 ['.proof','M-100 230 C240 20 750 360 1300 90',0], 57 ['.process','M-100 360 Q530 -150 1300 180',2], 58 ['.faq-section','M-100 90 C350 340 880 70 1300 190',0], 59 ['.closing','M-100 260 Q620 -130 1300 260',1], 60 ['.mega-footer','M-100 180 C300 350 880 -60 1300 200',0] 61 ];
62 sectorConfigs.forEach(([selector,path,direction])=>{ 63 const section=document.querySelector(selector);if(!section)return; 64 section.classList.add('has-sector'); 65 const crossing=document.createElement('div');crossing.className=`sector-crossing crossing-direction-${direction}`;crossing.setAttribute('aria-hidden','true'); 66 crossing.innerHTML=`<div class="crossing-field"><div class="crossing-cloud"></div><svg viewBox="0 0 1200 360" preserveAspectRatio="none"><path class="crossing-path echo" d="${path}"/><path class="crossing-path" d="${path}"/></svg></div>`; 67 section.prepend(crossing); 68 }); 69 function loopInView(element,timeline){ 70 const state={timeline,visible:false};loops.push(state); 71 ScrollTrigger.create({trigger:element,start:'top bottom',end:'bottom top',onToggle:self=>{state.visible=self.isActive;self.isActive&&!document.hidden?timeline.play():timeline.pause();}}); 72 } 73 function setup(){ 74 navigation?.kill();context?.revert();loops.length=0;trigger=null;applyPhoneTier(); 75 if(track){track.classList.toggle('orbit-ready',canOrbit());track.dataset.field='1';track.dataset.arrival='1';if(controls)controls.inert=false;cards.forEach(card=>{card.inert=false;card.removeAttribute('aria-hidden');card.removeAttribute('tabindex');card.classList.remove('is-front');});} 76 context=gsap.context(()=>{ 77 if(off())return; 78 if(track&&canOrbit()){ 79 orbitState={position:0,arrival:0,field:0}; 80 const animation=gsap.timeline({onUpdate:()=>renderOrbit(orbitState.position),scrollTrigger:{id:'discovery-orbit',trigger:track,start:()=> 'top '+Math.max(96,(stableHeight()-track.querySelector('.orbit-viewport').offsetHeight)/2),end:()=>'+='+Math.max(500,track.offsetHeight-track.querySelector('.orbit-viewport').offsetHeight),scrub:.55,invalidateOnRefresh:true}}); 81 animation.addLabel('signal',0).to(orbitState,{field:1,duration:.16,ease:'power1.out'},'signal') 82 .addLabel('assemble',.18).to(orbitState,{arrival:1,duration:.2,ease:'power2.out'},'assemble') 83 .addLabel('explore',explorationStart).to(orbitState,{position:4,duration:1-explorationStart,ease:'none'},'explore'); 84 trigger=animation.scrollTrigger;renderOrbit(orbitState.position); 85 } 86 document.querySelectorAll('.sector-crossing').forEach((crossing,i)=>{ 87 const field=crossing.querySelector('.crossing-field'),path=crossing.querySelector('.crossing-path:not(.echo)'),cloud=crossing.querySelector('.crossing-cloud'); 88 const length=path.getTotalLength();gsap.set(path,{strokeDasharray:length,strokeDashoffset:length*.75}); 89 gsap.timeline({scrollTrigger:{trigger:crossing.parentElement,start:'top bottom',end:'top 20%',scrub:.65}}) 90 .fromTo(field,{x:i%2?140:-140,y:100,scale:.85},{x:i%2?-110:110,y:-65,scale:1.3,duration:1,ease:'none'},0) 91 .to(path,{strokeDashoffset:0,duration:.8,ease:'none'},0) 92 .fromTo(cloud,{xPercent:i%2?-7:7,scale:1.0},{xPercent:i%2?10:-10,scale:1.22,duration:1,ease:'none'},0); 93 }); 94 document.querySelectorAll('.measurement-card').forEach((card,i)=>{ 95 const tl=gsap.timeline({paused:true}); 96 if(i===0)tl.fromTo(card.querySelector('.instrument-scan'),{yPercent:-100},{yPercent:100,duration:4.2,repeat:-1,ease:'sine.inOut'}); 97 if(i===1){ 98 const line=card.querySelector('.signal-path'),length=line.getTotalLength(); 99 tl.fromTo(line,{strokeDasharray:length,strokeDashoffset:length},{strokeDashoffset:0,duration:3,repeat:-1,repeatDelay:1.6,ease:'power1.inOut'}); 100 tl.to(card.querySelector('.signal-marker'),{opacity:.3,duration:.9,repeat:-1,yoyo:true,ease:'sine.inOut'},0); 101 } 102 if(i===2)tl.to(card.querySelectorAll('.decision-instrument i'),{backgroundColor:'#b7efd5',borderColor:'#3da081',duration:.8,stagger:.9,repeat:-1,yoyo:true,ease:'sine.inOut'}); 103 loopInView(card,tl); 104 }); 105 const lab=document.querySelector('.experience-lab'); 106 if(lab){ 107 const tl=gsap.timeline({paused:true}); 108 tl.to(lab.querySelector('.lab-world'),{xPercent:4,scale:1.07,duration:12,repeat:-1,yoyo:true,ease:'sine.inOut'},0); 109 tl.to(lab.querySelector('.lab-floating-chip'),{y:-10,duration:3.2,repeat:-1,yoyo:true,ease:'sine.inOut'},0); 110 tl.to(lab.querySelector('.lab-orbit'),{rotationZ:18,duration:10,repeat:-1,yoyo:true,ease:'sine.inOut'},0); 111 loopInView(lab,tl); 112 } 113 const launch=document.querySelector('.launch-instrument'); 114 if(launch){ 115 const tl=gsap.timeline({paused:true}); 116 tl.to(launch.querySelector('.ring-one'),{rotationZ:340,duration:24,repeat:-1,ease:'none'},0) 117 .to(launch.querySelector('.ring-two'),{rotationZ:-360,duration:32,repeat:-1,ease:'none'},0) 118 .to(launch.querySelector('.ring-three'),{rotationY:80,rotationX:24,duration:9,repeat:-1,yoyo:true,ease:'sine.inOut'},0) 119 .to(launch.querySelector('.launch-core'),{y:-9,duration:3.5,repeat:-1,yoyo:true,ease:'sine.inOut'},0); 120 loopInView(launch,tl); 121 } 122 }); 123 ScrollTrigger.refresh(true); 124 } 125 const labButtons=[...document.querySelectorAll('[data-lab]')],lab=d
125ocument.querySelector('.experience-lab'),readout=document.querySelector('#lab-readout'); 126 const labCopy=[['Move with the story.','Use depth, transitions and responsive feedback to explain how the experience connects. Give visitors control over motion.','Motion with purpose'],['Make the next step obvious.','Use clear hierarchy, useful content and thoughtful spacing so people can understand the offer on any screen.','Clarity at every size'],['Be understood beyond the screen.','Connect a memorable experience to readable content, relevant pages and a clear information structure.','Built for discovery']]; 127 let labTween; 128 function selectLab(i){ 129 labButtons.forEach((b,n)=>{b.setAttribute('aria-selected',String(n===i));b.tabIndex=n===i?0:-1;});readout.setAttribute('aria-labelledby',labButtons[i].id); 130 readout.querySelector('span').textContent=labCopy[i][0];readout.querySelector('p').textContent=labCopy[i][1];lab.querySelector('.lab-floating-chip span').textContent=labCopy[i][2]; 131 labTween?.kill();labTween=gsap.timeline();const d=off()?0:.65; 132 labTween.to(lab.querySelector('.lab-page'),{rotationY:i===0?-12:i===1?0:8,rotationX:i===0?4:0,x:i===2?-10:0,scale:i===1?1.06:1,duration:d,ease:'power3.out'},0) 133 .to(lab.querySelector('.lab-grid'),{opacity:i===2?.7:0,duration:d},0) 134 .to(lab.querySelectorAll('.lab-page-line'),{scaleX:i===1?1.08:1,transformOrigin:'left',duration:d},0); 135 } 136 labButtons.forEach((button,i)=>{ 137 button.addEventListener('click',()=>selectLab(i)); 138 button.addEventListener('keydown',event=>{let n=i;if(event.key==='ArrowRight')n=(i+1)%3;else if(event.key==='ArrowLeft')n=(i+2)%3;else if(event.key==='Home')n=0;else if(event.key==='End')n=2;else return;event.preventDefault();labButtons[n].focus();selectLab(n);}); 139 }); 140 const sync=()=>{if(off())labTween?.progress(1);setup();}; 141 addEventListener('scalz:preview-effects',sync);document.addEventListener('scalz:preview-effects',sync);reduced.addEventListener('change',sync); 142 document.addEventListener('visibilitychange',()=>loops.forEach(({timeline,visible})=>document.hidden||!visible?timeline.pause():timeline.play())); 143 // Like the film stage, give the orbit controls their own space: the chat bubble steps aside while the orbit fills the screen. 144 const orbitStage=track?.querySelector('.orbit-viewport'); 145 if(orbitStage&&'IntersectionObserver' in window)new IntersectionObserver(entries=>{root.classList.toggle('in-orbit-stage',track.classList.contains('orbit-ready')&&entries[0].isIntersecting&&entries[0].intersectionRatio>=.6);},{threshold:[0,.6]}).observe(orbitStage); 146 let timer,lastWidth=innerWidth,lastHeight=stableHeight(); 147 addEventListener('resize',()=>{clearTimeout(timer);timer=setTimeout(()=>{ 148 const width=innerWidth,height=stableHeight(); 149 if(touch&&width===lastWidth&&Math.abs(height-lastHeight)<2)return; 150 lastWidth=width;lastHeight=height;setup(); 151 },260);},{passive:true}); 152 setup();document.fonts?.ready.then(()=>ScrollTrigger.refresh(true)); 153})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.