PageSourceSearch

https://ultimaker.com/wp-content/themes/ultimaker/js/animation.js?ver=1789418214

js ultimaker.com collected 2026-09-24 08:31:30 UTC 63,053 bytes, 2,445 lines download raw bytes

1gsap.registerPlugin( ScrollTrigger );
2
3document.addEventListener( 'DOMContentLoaded', function () {
4
5	const sections = document.querySelectorAll( '[data-main-feature-animate]' );
6
7	// quiz module animation
8	const quizSections = document.querySelectorAll( '[data-quiz-animate]' );
9
10	quizSections.forEach( function ( section ) {
11		const bgImage = section.querySelector( '.bg-image img' );
12		const h2      = section.querySelector( '.content-block h2' );
13		const p       = section.querySelector( '.content-block p' );
14		const btn     = section.querySelector( '.content-block a' );
15
16		if ( bgImage ) {
17			gsap.fromTo(
18				bgImage,
19				{ y: 80, opacity: 0 },
20				{
21					y: 0,
22					opacity: 1,
23					duration: 0.9,
24					ease: 'power3.out',
25					scrollTrigger: {
26						trigger: section,
27						start: 'top 85%',
28						toggleActions: 'play none none none',
29					},
30				}
31			);
32		}
33
34		const elements = [ h2, p, btn ].filter( Boolean );
35
36		gsap.fromTo(
37			elements,
38			{ y: 80, opacity: 0 },
39			{
40				y: 0,
41				opacity: 1,
42				duration: 0.9,
43				ease: 'power3.out',
44				stagger: 0.15,
45				scrollTrigger: {
46					trigger: section,
47					start: 'top 85%',
48					toggleActions: 'play none none none',
49				},
50			}
51		);
52	} );
53
54	// card module animation
55	const cardModules = document.querySelectorAll( '[data-card-animate]' );
56
57	cardModules.forEach( function ( card ) {
58		const leftCol  = card.querySelector( '.__left--col' );
59		const rightCol = card.querySelector( '.__right--col' );
60
61		if ( leftCol ) {
62			gsap.fromTo(
63				leftCol,
64				{ y: 80, opacity: 0 },
65				{
66					y: 0,
67					opacity: 1,
68					duration: 0.9,
69					ease: 'power3.out',
70					scrollTrigger: {
71						trigger: card,
72						start: 'top 85%',
73						toggleActions: 'play none none none',
74					},
75				}
76			);
77		}
78
79		if ( rightCol ) {
80			gsap.fromTo(
81				rightCol,
82				{ x: 80, opacity: 0 },
83				{
84					x: 0,
85					opacity: 1,
86					duration: 0.9,
87					ease: 'power3.out',
88					delay: 0.25,
89					scrollTrigger: {
90						trigger: card,
91						start: 'top 85%',
92						toggleActions: 'play none none none',
93					},
94				}
95			);
96		}
97	} );
98
99	// full width module animation
100	const fullWidthModules = document.querySelectorAll( '[data-fullwidth-animate]' );
101
102	fullWidthModules.forEach( function ( module ) {
103		const leftCol  = module.querySelector( '.__left--col' );
104		const rightCol = module.querySelector( '.__right--col' );
105
106		if ( leftCol ) {
107			const h3     = leftCol.querySelector( 'h3' );
108			const h1     = leftCol.querySelector( 'h1' );
109			const para   = leftCol.querySelector( 'p' );
110			const btn    = leftCol.querySelector( 'a.primary-btn-large' );
111
112			const textElements = [ h3, h1, para, btn ].filter( Boolean );
113
114			gsap.fromTo(
115				textElements,
116				{ y: 80, opacity: 0 },
117				{
118					y: 0,
119					opacity: 1,
120					duration: 0.9,
121					ease: 'power3.out',
122					stagger: 0.15,
123					scrollTrigger: {
124						trigger: module,
125						start: 'top 85%',
126						toggleActions: 'play none none none',
127					},
128				}
129			);
130		}
131
132		if ( rightCol ) {
133			gsap.fromTo(
134				rightCol,
135				{ x: 80, opacity: 0 },
136				{
137					x: 0,
138					opacity: 1,
139					duration: 0.9,
140					ease: 'power3.out',
141					delay: 0.25,
142					scrollTrigger: {
143						trigger: module,
144						start: 'top 85%',
145						toggleActions: 'play none none none',
146					},
147				}
148			);
149		}
150	} );
151
152	// video module animation
153	const videoModules = document.querySelectorAll( '[data-scroll-reveal="video"]' );
154
155	videoModules.forEach( function ( module ) {
156		const image      = module.querySelector( '.video-poster' );
157		const playBtn    = module.querySelector( '.video__playbtn-wrapper' );
158		const caption    = module.querySelector( '.picture__desc' );
159
160		const elements = [ image, playBtn, caption ].filter( Boolean );
161
162		gsap.fromTo(
163			elements,
164			{
165				y: 80,
166				opacity: 0,
167			},
168			{
169				y: 0,
170				opacity: 1,
171				duration: 0.9,
172				ease: 'power3.out',
173				stagger: 0.25,
174				scrollTrigger: {
175					trigger: module,
176					start: 'top 85%',
177					toggleActions: 'play none none none',
178				},
179			}
180		);
181	} );
182
183	sections.forEach( function ( section ) {
184		const leftBlock   = section.querySelector( '.__left--block' );
185		const rightBlock  = section.querySelector( '.__right--block' );
186		const bottomBlock = section.querySelector( '.main_feature--bottom--block' );
187
188		const elements = [ leftBlock, rightBlock, bottomBlock ].filter( Boolean );
189
190		gsap.fromTo(
191			elements,
192			{
193				y: 80,
194				opacity: 0,
195			},
196			{
197				y: 0,
198				opacity: 1,
199				duration: 0.9,
200				ease: 'power3.out',
201				stagger: 0.2,
202				scrollTrigger: {
203					trigger: section,
204					start: 'top 85%',
205					toggleActions: 'play none none none',
206				},
207			}
208		);
209	} );
210
211	// accessories module animation
212	const accessoriesModules = document.querySelectorAll( '[data-accessories-animate]' );
213
214	accessoriesModules.forEach( function ( module ) {
215		const heading = module.querySelector( 'h2' );
216		const cards   = module.querySelectorAll( '.acc__block_global' );
217
218		if ( heading ) {
219			gsap.fromTo(
220				heading,
221				{ y: 80, opacity: 0 },
222				{
223					y: 0,
224					opacity: 1,
225					duration: 0.9,
226					ease: 'power3.out',
227					scrollTrigger: {
228						trigger: module,
229						start: 'top 85%',
230						toggleActions: 'play none none none',
231					},
232				}
233			);
234		}
235
236		if ( cards.length ) {
237			// all cards stagger bottom to top together
238			gsap.fromTo(
239				Array.from( cards ),
240				{ y: 80, opacity: 0 },
241				{
242					y: 0,
243					opacity: 1,
244					duration: 0.9,
245					ease: 'power3.out',
246					stagger: 0.2,
247					scrollTrigger: {
248						trigger: module,
249						start: 'top 85%',
250						toggleActions: 'play none none none',
251					},
252				}
253			);
254
255			// image inside each card — scale + right to left
256			cards.forEach( function ( card ) {
257				const img = card.querySelector( '.acc__col--right img' );
258
259				if ( img ) {
260					gsap.fromTo(
261						img,
262						{ x: 60, scale: 1.1, opacity: 0 },
263						{
264							x: 0,
265							scale: 1,
266							opacity: 1,
267							duration: 1,
268							ease: 'power3.out',
269							delay: 0.3,
270							scrollTrigger: {
271								trigger: card,
272								start: 'top 85%',
273								toggleActions: 'play none none none',
274							},
275						}
276					);
277				}
278			} );
279		}
280	} );
281
282	// material module animation
283	const materialModules = document.querySelectorAll( '[data-material-animate]' );
284
285	materialModules.forEach( function ( module ) {
286		const titleH2  = module.querySelector( '.__title--block h2' );
287		const titleP   = module.querySelector( '.__title--block p' );
288		const cards    = module.querySelectorAll( '.material--card--col' );
289
290		// heading + paragraph bottom to top
291		const titleEls = [ titleH2, titleP ].filter( Boolean );
292		if ( titleEls.length ) {
293			gsap.fromTo(
294				titleEls,
295				{ y: 80, opacity: 0 },
296				{
297					y: 0,
298					opacity: 1,
299					duration: 0.9,
300					ease: 'power3.out',
301					stagger: 0.15,
302					scrollTrigger: {
303						trigger: module,
304						start: 'top 85%',
305						toggleActions: 'play none none none',
306					},
307				}
308			);
309		}
310
311		// cards stagger bottom to top
312		if ( cards.length ) {
313			gsap.fromTo(
314				Array.from( cards ),
315				{ y: 80, opacity: 0 },
316				{
317					y: 0,
318					opacity: 1,
319					duration: 0.9,
320					ease: 'power3.out',
321					stagger: 0.2,
322					delay: 0.3,
323					scrollTrigger: {
324						trigger: module,
325						start: 'top 85%',
326						toggleActions: 'play none none none',
327					},
328				}
329			);
330
331			// image inside each card — scale + fade
332			cards.forEach( function ( card ) {
333				const img     = card.querySelector( '.__picture--row img' );
334				const infoRow = card.querySelector( '.__infor--row' );
335
336				if ( img ) {
337					gsap.fromTo(
338						img,
339						{ scale: 1.1, opacity: 0 },
340						{
341							scale: 1,
342							opacity: 1,
343							duration: 1,
344							ease: 'power3.out',
345							delay: 0.4,
346							scrollTrigger: {
347								trigger: card,
348								start: 'top 85%',
349								toggleActions: 'play none none none',
350							},
351						}
352					);
353				}
354
355				if ( infoRow ) {
356					gsap.fromTo(
357						infoRow,
358						{ y: 40, opacity: 0 },
359						{
360							y: 0,
361							opacity: 1,
362							duration: 0.9,
363							ease: 'power3.out',
364							delay: 0.5,
365							scrollTrigger: {
366								trigger: card,
367								start: 'top 85%',
368								toggleActions: 'play none none none',
369							},
370						}
371					);
372				}
373			} );
374		}
375	} );
376
377	// comparison module animation
378	const compModules = document.querySelectorAll( '[data-comparison-animate]' );
379
380	compModules.forEach( function ( module ) {
381		const heading      = module.querySelector( '.comparison__module--header h3' );
382		const printerImgs  = module.querySelectorAll( '.comp__header--block' );
383		const selBlocks    = module.querySelectorAll( '.comp__sel--block' );
384
385		if ( heading ) {
386			gsap.fromTo(
387				heading,
388				{ y: 80, opacity: 0 },
389				{
390					y: 0,
391					opacity: 1,
392					duration: 0.9,
393					ease: 'power3.out',
394					scrollTrigger: {
395						trigger: module,
396						start: 'top 85%',
397						toggleActions: 'play none none none',
398					},
399				}
400			);
401		}
402
403		if ( printerImgs.length ) {
404			gsap.fromTo(
405				Array.from( printerImgs ),
406				{ y: 80, opacity: 0 },
407				{
408					y: 0,
409					opacity: 1,
410					duration: 0.9,
411					ease: 'power3.out',
412					stagger: 0.15,
413					delay: 0.2,
414					scrollTrigger: {
415						trigger: module,
416						start: 'top 85%',
417						toggleActions: 'play none none none',
418					},
419				}
420			);
421		}
422
423		if ( selBlocks.length ) {
424			gsap.fromTo(
425				Array.from( selBlocks ),
426				{ y: 80, opacity: 0 },
427				{
428					y: 0,
429					opacity: 1,
430					duration: 0.9,
431					ease: 'power3.out',
432					stagger: 0.15,
433					delay: 0.4,
434					scrollTrigger: {
435						trigger: module,
436						start: 'top 85%',
437						toggleActions: 'play none none none',
438					},
439				}
440			);
441		}
442	} );
443
444	// checkerboard module animation
445	const checkerboards = document.querySelectorAll( '[data-checkerboard-animate]' );
446
447	checkerboards.forEach( function ( module ) {
448		const card     = module.querySelector( '.card' );
449		const leftCol  = module.querySelector( '.__left--col' );
450		const rightCol = module.querySelector( '.__right--col' );
451
452		if ( leftCol ) {
453			const h2   = leftCol.querySelector( 'h2' );
454			const h4   = leftCol.querySelector( 'h4' );
455			const para = leftCol.querySelector( '.body-md-ibm-plex-sans' );
456			const link = leftCol.querySelector( 'a' );
457
458			const elements = [ h2, h4, para, link ].filter( Boolean );
459
460			gsap.fromTo(
461				elements,
462				{ y: 80, opacity: 0 },
463				{
464					y: 0,
465					opacity: 1,
466					duration: 0.9,
467					ease: 'power3.out',
468					stagger: 0.15,
469					scrollTrigger: {
470						trigger: module,
471						start: 'top 85%',
472						toggleActions: 'play none none none',
473					},
474				}
475			);
476		}
477
478		if ( rightCol ) {
479			const isImageLeft = card && card.classList.contains( 'row-reverse' );
480			const startX      = isImageLeft ? -80 : 80;
481
482			gsap.fromTo(
483				rightCol,
484				{ x: startX, opacity: 0 },
485				{
486					x: 0,
487					opacity: 1,
488					duration: 0.9,
489					ease: 'power3.out',
490					delay: 0.25,
491					scrollTrigger: {
492						trigger: module,
493						start: 'top 85%',
494						toggleActions: 'play none none none',
495					},
496				}
497			);
498		}
499	} );
500
501	// carousel block animation
502	const carouselSections = document.querySelectorAll( '[data-carousel-animate]' );
503
504	carouselSections.forEach( function ( section ) {
505		ScrollTrigger.create( {
506			trigger: section,
507			start: 'top 85%',
508			once: true,
509			onEnter: function () {
510				const activeSlide = section.querySelector( '.owl-item.active .text-block' );
511				if ( ! activeSlide ) return;
512
513				const h2  = activeSlide.querySelector( 'h2' );
514				const h4  = activeSlide.querySelector( 'h4' );
515				const p   = activeSlide.querySelector( 'p' );
516				const btn = activeSlide.querySelector( '.anchor-block' );
517
518				const elements = [ h2, h4, p, btn ].filter( Boolean );
519
520				gsap.fromTo(
521					elements,
522					{ y: 80, opacity: 0 },
523					{
524						y: 0,
525						opacity: 1,
526						duration: 0.9,
527						ease: 'power3.out',
528						stagger: 0.15,
529					}
530				);
531			},
532		} );
533	} );
534
535	// brand module animation
536	const brandModules = document.querySelectorAll( '[data-brand-animate]' );
537
538	brandModules.forEach( function ( module ) {
539		const heading = module.querySelector( 'h3' );
540		const logos   = module.querySelectorAll( '.brand__logo' );
541
542		if ( heading ) {
543			gsap.fromTo(
544				heading,
545				{ y: 60, opacity: 0 },
546				{
547					y: 0,
548					opacity: 1,
549					duration: 0.9,
550					ease: 'power3.out',
551					scrollTrigger: {
552						trigger: module,
553						start: 'top 85%',
554						toggleActions: 'play none none none',
555					},
556				}
557			);
558		}
559
560		if ( logos.length ) {
561			gsap.fromTo(
562				Array.from( logos ),
563				{ y: 60, opacity: 0 },
564				{
565					y: 0,
566					opacity: 1,
567					duration: 0.7,
568					ease: 'power3.out',
569					stagger: 0.08,
570					delay: 0.3,
571					scrollTrigger: {
572						trigger: module,
573						start: 'top 85%',
574						toggleActions: 'play none none none',
575					},
576				}
577			);
578		}
579	} );
580
581	// anchor link module animation
582	const anchorModules = document.querySelectorAll( '[data-anchor-animate]' );
583
584	anchorModules.forEach( function ( module ) {
585		const cards = module.querySelectorAll( '.anchor-link--card' );
586
587		if ( cards.length ) {
588			// cards stagger bottom to top
589			gsap.fromTo(
590				Array.from( cards ),
591				{ y: 80, opacity: 0 },
592				{
593					y: 0,
594					opacity: 1,
595					duration: 0.9,
596					ease: 'power3.out',
597					stagger: 0.2,
598					scrollTrigger: {
599						trigger: module,
600						start: 'top 85%',
601						toggleActions: 'play none none none',
602					},
603				}
604			);
605
606			// SVG chevron — continuous bounce downward after cards appear
607			cards.forEach( function ( card ) {
608				const svg = card.querySelector( 'svg' );
609				if ( ! svg ) return;
610
611				gsap.fromTo(
612					svg,
613					{ y: 0 },
614					{
615						y: 6,
616						duration: 1.2,
617						ease: 'power1.inOut',
618						repeat: -1,
619						yoyo: true,
620						delay: 1,
621					}
622				);
623			} );
624		}
625	} );
626
627	// printer hero animation
628	const printerHeroes = document.querySelectorAll( '[data-printer-hero-animate]' );
629
630	printerHeroes.forEach( function ( hero ) {
631		const leftCol  = hero.querySelector( '.printer-hero-grid .__left--col' );
632		const rightCol = hero.querySelector( '.printer-hero-grid .__right--col' );
633
634		if ( leftCol ) {
635			const title = leftCol.querySelector( '.display-2' );
636			const h3    = leftCol.querySelector( 'h3' );
637			const p     = leftCol.querySelector( 'p' );
638			const btn   = leftCol.querySelector( 'a, .modal-toggle' );
639
640			const elements = [ title, h3, p, btn ].filter( Boolean );
641
642			gsap.fromTo(
643				elements,
644				{ y: 80, opacity: 0 },
645				{
646					y: 0,
647					opacity: 1,
648					duration: 0.9,
649					ease: 'power3.out',
650					stagger: 0.15,
651					scrollTrigger: {
652						trigger: hero,
653						start: 'top 85%',
654						toggleActions: 'play none none none',
655					},
656				}
657			);
658		}
659
660		if ( rightCol ) {
661			gsap.fromTo(
662				rightCol,
663				{ x: 80, opacity: 0 },
664				{
665					x: 0,
666					opacity: 1,
667					duration: 0.9,
668					ease: 'power3.out',
669					delay: 0.3,
670					scrollTrigger: {
671						trigger: hero,
672						start: 'top 85%',
673						toggleActions: 'play none none none',
674					},
675				}
676			);
677		}
678	} );
679
680	// sub nav animation
681	const subNavs = document.querySelectorAll( '[data-subnav-animate]' );
682
683	subNavs.forEach( function ( nav ) {
684		gsap.fromTo(
685			nav,
686			{ y: 60, opacity: 0 },
687			{
688				y: 0,
689				opacity: 1,
690				duration: 0.9,
691				ease: 'power3.out',
692				delay: 0.5,
693				scrollTrigger: {
694					trigger: nav,
695					start: 'top 95%',
696					toggleActions: 'play none none none',
697				},
698			}
699		);
700	} );
701
702	// pillar module animation
703	const pillarModules = document.querySelectorAll( '[data-pillar-animate]' );
704
705	pillarModules.forEach( function ( module ) {
706		const h3    = module.querySelector( 'h3' );
707		const p     = module.querySelector( 'p' );
708		const cards = module.querySelectorAll( '.pillar__card' );
709
710		const headingEls = [ h3, p ].filter( Boolean );
711
712		if ( headingEls.length ) {
713			gsap.fromTo(
714				headingEls,
715				{ y: 80, opacity: 0 },
716				{
717					y: 0,
718					opacity: 1,
719					duration: 0.9,
720					ease: 'power3.out',
721					stagger: 0.15,
722					scrollTrigger: {
723						trigger: module,
724						start: 'top 85%',
725						toggleActions: 'play none none none',
726					},
727				}
728			);
729		}
730
731		if ( cards.length ) {
732			// cards stagger bottom to top
733			gsap.fromTo(
734				Array.from( cards ),
735				{ y: 80, opacity: 0 },
736				{
737					y: 0,
738					opacity: 1,
739					duration: 0.9,
740					ease: 'power3.out',
741					stagger: 0.12,
742					delay: 0.3,
743					scrollTrigger: {
744						trigger: module,
745						start: 'top 85%',
746						toggleActions: 'play none none none',
747					},
748				}
749			);
750
751			// icon inside each card — scale + fade pop
752			cards.forEach( function ( card ) {
753				const icon = card.querySelector( '.pillar__icon img' );
754				if ( ! icon ) return;
755
756				gsap.fromTo(
757					icon,
758					{ scale: 0.5, opacity: 0 },
759					{
760						scale: 1,
761						opacity: 1,
762						duration: 0.6,
763						ease: 'back.out(1.7)',
764						delay: 0.5,
765						scrollTrigger: {
766							trigger: card,
767							start: 'top 90%',
768							toggleActions: 'play none none none',
769						},
770					}
771				);
772			} );
773		}
774	} );
775
776	// hero module animations — all 6 variations
777
778	// variation 1 — left text + right image
779	document.querySelectorAll( '[data-hero-animate="variation-1"]' ).forEach( function ( hero ) {
780		const leftEl  = hero.querySelector( '.printer-hero-grid .__left--col' );
781		const rightEl = hero.querySelector( '.printer-hero-grid .__right--col' );
782
783		if ( leftEl ) {
784			const els = [ leftEl.querySelector( '.display-2' ), leftEl.querySelector( 'h3' ), leftEl.querySelector( 'p' ), leftEl.querySelector( 'a, .modal
784-toggle' ) ].filter( Boolean );
785			gsap.fromTo( els, { y: 80, opacity: 0 }, { y: 0, opacity: 1, duration: 0.9, ease: 'power3.out', stagger: 0.15 } );
786		}
787		if ( rightEl ) {
788			gsap.fromTo( rightEl, { x: 80, opacity: 0 }, { x: 0, opacity: 1, duration: 0.9, ease: 'power3.out', delay: 0.3 } );
789		}
790	} );
791
792	// variation 2 — two images flanking center text
793	document.querySelectorAll( '[data-hero-animate="variation-2"]' ).forEach( function ( hero ) {
794		const leftImg   = hero.querySelector( '.col-1.col-picture' );
795		const content   = hero.querySelector( '.col-2.col-content' );
796		const rightImg  = hero.querySelector( '.col-3.col-picture' );
797
798		if ( leftImg )  gsap.fromTo( leftImg,  { x: -80, opacity: 0 }, { x: 0, opacity: 1, duration: 0.9, ease: 'power3.out' } );
799		if ( rightImg ) gsap.fromTo( rightImg, { x: 80,  opacity: 0 }, { x: 0, opacity: 1, duration: 0.9, ease: 'power3.out' } );
800
801		if ( content ) {
802			const els = [ content.querySelector( 'h1' ), content.querySelector( 'h3' ), content.querySelector( 'p' ), content.querySelector( '.__link--block' ) ].filter( Boolean );
803			gsap.fromTo( els, { y: 80, opacity: 0 }, { y: 0, opacity: 1, duration: 0.9, ease: 'power3.out', stagger: 0.15, delay: 0.2 } );
804		}
805	} );
806
807	// variation 3 — display-1 heading + image in picture-block
808	document.querySelectorAll( '[data-hero-animate="variation-3"]' ).forEach( function ( hero ) {
809		const leftEl  = hero.querySelector( '.__left--col' );
810		const rightEl = hero.querySelector( '.__right--col .picture-block' );
811
812		if ( leftEl ) {
813			const els = [ leftEl.querySelector( '.display-1' ), leftEl.querySelector( 'h3' ), leftEl.querySelector( 'p' ), leftEl.querySelector( 'a.primary-btn-large' ) ].filter( Boolean );
814			gsap.fromTo( els, { y: 80, opacity: 0 }, { y: 0, opacity: 1, duration: 0.9, ease: 'power3.out', stagger: 0.15 } );
815		}
816		if ( rightEl ) gsap.fromTo( rightEl, { x: 80, opacity: 0 }, { x: 0, opacity: 1, duration: 0.9, ease: 'power3.out', delay: 0.3 } );
817	} );
818
819	// variation 4 — same as 3 + sub links
820	document.querySelectorAll( '[data-hero-animate="variation-4"]' ).forEach( function ( hero ) {
821		const leftEl  = hero.querySelector( '.__left--col' );
822		const rightEl = hero.querySelector( '.__right--col .picture-block' );
823
824		if ( leftEl ) {
825			const els = [ leftEl.querySelector( '.heading-1' ), leftEl.querySelector( 'h3' ), leftEl.querySelector( 'div.body-lg-ibm-plex-sans' ), leftEl.querySelector( 'a.primary-btn-large' ), leftEl.querySelector( '.sub__links' ) ].filter( Boolean );
826			gsap.fromTo( els, { y: 80, opacity: 0 }, { y: 0, opacity: 1, duration: 0.9, ease: 'power3.out', stagger: 0.15 } );
827		}
828		if ( rightEl ) gsap.fromTo( rightEl, { x: 80, opacity: 0 }, { x: 0, opacity: 1, duration: 0.9, ease: 'power3.out', delay: 0.3 } );
829	} );
830
831	// variation 5 — h1 + image with border-top-left
832	document.querySelectorAll( '[data-hero-animate="variation-5"]' ).forEach( function ( hero ) {
833		const leftEl  = hero.querySelector( '.__left--col' );
834		const rightEl = hero.querySelector( '.__right--col .picture-block' );
835
836		if ( leftEl ) {
837			const els = [ leftEl.querySelector( 'h1' ), leftEl.querySelector( 'h3' ), leftEl.querySelector( 'p' ), leftEl.querySelector( 'a.primary-btn-large' ) ].filter( Boolean );
838			gsap.fromTo( els, { y: 80, opacity: 0 }, { y: 0, opacity: 1, duration: 0.9, ease: 'power3.out', stagger: 0.15 } );
839		}
840		if ( rightEl ) gsap.fromTo( rightEl, { x: 80, opacity: 0 }, { x: 0, opacity: 1, duration: 0.9, ease: 'power3.out', delay: 0.3 } );
841	} );
842
843	// variation 6 — row-reverse + color swatches
844	document.querySelectorAll( '[data-hero-animate="variation-6"]' ).forEach( function ( hero ) {
845		const leftEl  = hero.querySelector( '.__left--col' );
846		const rightEl = hero.querySelector( '.__right--col .picture-block' );
847
848		if ( leftEl ) {
849			const els = [ leftEl.querySelector( '.display-1' ), leftEl.querySelector( 'h3' ), leftEl.querySelector( 'p' ), leftEl.querySelector( '.color-options' ), leftEl.querySelector( 'a.primary-btn-large' ), leftEl.querySelector( '.hypelink-container' ) ].filter( Boolean );
850			gsap.fromTo( els, { y: 80, opacity: 0 }, { y: 0, opacity: 1, duration: 0.9, ease: 'power3.out', stagger: 0.15 } );
851		}
852		// image is on left (row-reverse) so animate left to right
853		if ( rightEl ) gsap.fromTo( rightEl, { x: -80, opacity: 0 }, { x: 0, opacity: 1, duration: 0.9, ease: 'power3.out', delay: 0.3 } );
854	} );
855
856	// 3-column content module animations
857
858	// variation 1 — related content grid (image card + text)
859	document.querySelectorAll( '[data-3col-animate="variation-1"]' ).forEach( function ( section ) {
860		const heading = section.querySelector( '.related--content--module h2' );
861		const cards   = section.querySelectorAll( '.related__content--card' );
862
863		if ( heading ) {
864			gsap.fromTo( heading,
865				{ y: 60, opacity: 0 },
866				{ y: 0, opacity: 1, duration: 0.8, ease: 'power3.out',
867				  scrollTrigger: { trigger: section, start: 'top 85%', toggleActions: 'play none none none' } }
868			);
869		}
870
871		if ( cards.length ) {
872			gsap.fromTo( cards,
873				{ y: 80, opacity: 0 },
874				{ y: 0, opacity: 1, duration: 0.8, ease: 'power3.out', stagger: 0.15,
875				  scrollTrigger: { trigger: section, start: 'top 85%', toggleActions: 'play none none none' } }
876			);
877
878			cards.forEach( function ( card ) {
879				const img = card.querySelector( '.picture--cont img' );
880				if ( img ) {
881					gsap.fromTo( img,
882						{ scale: 1.1, opacity: 0 },
883						{ scale: 1, opacity: 1, duration: 0.9, ease: 'power3.out',
884						  scrollTrigger: { trigger: section, start: 'top 85%', toggleActions: 'play none none none' } }
885					);
886				}
887			} );
888		}
889	} );
890
891	// variation 2 — column3 content grid with heading, sub heading, image cards
892	document.querySelectorAll( '[data-3col-animate="variation-2"]' ).forEach( function ( section ) {
893		const heading    = section.querySelector( '.__title--block h2' );
894		const subHeading = section.querySelector( '.__title--block p' );
895		const cards      = section.querySelectorAll( '.column3__content--card' );
896
897		if ( heading ) {
898			gsap.fromTo( heading,
899				{ y: 60, opacity: 0 },
900				{ y: 0, opacity: 1, duration: 0.8, ease: 'power3.out',
901				  scrollTrigger: { trigger: section, start: 'top 85%', toggleActions: 'play none none none' } }
902			);
903		}
904
905		if ( subHeading ) {
906			gsap.fromTo( subHeading,
907				{ y: 40, opacity: 0 },
908				{ y: 0, opacity: 1, duration: 0.8, ease: 'power3.out', delay: 0.15,
909				  scrollTrigger: { trigger: section, start: 'top 85%', toggleActions: 'play none none none' } }
910			);
911		}
912
913		if ( cards.length ) {
914			gsap.fromTo( cards,
915				{ y: 80, opacity: 0 },
916				{ y: 0, opacity: 1, duration: 0.8, ease: 'power3.out', stagger: 0.15,
917				  scrollTrigger: { trigger: section, start: 'top 85%', toggleActions: 'play none none none' } }
918			);
919
920			cards.forEach( function ( card ) {
921				const img = card.querySelector( '.picture--cont img' );
922				if ( img ) {
923					gsap.fromTo( img,
924						{ scale: 1.1, opacity: 0 },
925						{ scale: 1, opacity: 1, duration: 0.9, ease: 'power3.out',
926						  scrollTrigger: { trigger: section, start: 'top 85%', toggleActions: 'play none none none' } }
927					);
928				}
929			} );
930		}
931	} );
932
933	// variation 3 — column3 content grid with load-more, same structure as variation 2
934	document.querySelectorAll( '[data-3col-animate="variation-3"]' ).forEach( function ( section ) {
935		const heading    = section.querySelector( '.__title--block h2' );
936		const subHeading = section.querySelector( '.__title--block p' );
937		const cards      = section.querySelectorAll( '.column3__content--card' );
938
939		if ( heading ) {
940			gsap.fromTo( heading,
941				{ y: 60, opacity: 0 },
942				{ y: 0, opacity: 1, duration: 0.8, ease: 'power3.out',
943				  scrollTrigger: { trigger: section, start: 'top 85%', toggleActions: 'play none none none' } }
944			);
945		}
946
947		if ( subHeading ) {
948			gsap.fromTo( subHeading,
949				{ y: 40, opacity: 0 },
950				{ y: 0, opacity: 1, duration: 0.8, ease: 'power3.out', delay: 0.15,
951				  scrollTrigger: { trigger: section, start: 'top 85%', toggleActions: 'play none none none' } }
952			);
953		}
954
955		if ( cards.length ) {
956			gsap.fromTo( cards,
957				{ y: 80, opacity: 0 },
958				{ y: 0, opacity: 1, duration: 0.8, ease: 'power3.out', stagger: 0.15,
959				  scrollTrigger: { trigger: section, start: 'top 85%', toggleActions: 'play none none none' } }
960			);
961
962			cards.forEach( function ( card ) {
963				const img = card.querySelector( '.picture--cont img' );
964				if ( img ) {
965					gsap.fromTo( img,
966						{ scale: 1.1, opacity: 0 },
967						{ scale: 1, opacity: 1, duration: 0.9, ease: 'power3.out',
968						  scrollTrigger: { trigger: section, start: 'top 85%', toggleActions: 'play none none none' } }
969					);
970				}
971			} );
972		}
973	} );
974
975	// variation 4 — featured group columns (text left, image right layout)
976	document.querySelectorAll( '[data-3col-animate="variation-4"]' ).forEach( function ( section ) {
977		const heading    = section.querySelector( '.bottom--spacing-xl-dt-div h2' );
978		const subHeading = section.querySelector( '.bottom--spacing-xl-dt-div p' );
979		const cards      = section.querySelectorAll( '.featured-gc--grid .col-1' );
980
981		if ( heading ) {
982			gsap.fromTo( heading,
983				{ y: 60, opacity: 0 },
984				{ y: 0, opacity: 1, duration: 0.8, ease: 'power3.out',
985				  scrollTrigger: { trigger: section, start: 'top 85%', toggleActions: 'play none none none' } }
986			);
987		}
988
989		if ( subHeading ) {
990			gsap.fromTo( subHeading,
991				{ y: 40, opacity: 0 },
992				{ y: 0, opacity: 1, duration: 0.8, ease: 'power3.out', delay: 0.15,
993				  scrollTrigger: { trigger: section, start: 'top 85%', toggleActions: 'play none none none' } }
994			);
995		}
996
997		if ( cards.length ) {
998			gsap.fromTo( cards,
999				{ y: 80, opacity: 0 },
1000				{ y: 0, opacity: 1, duration: 0.8, ease: 'power3.out', stagger: 0.15,
1001				  scrollTrigger: { trigger: section, start: 'top 85%', toggleActions: 'play none none none' } }
1002			);
1003
1004			cards.forEach( function ( card ) {
1005				// image is on the right col — animate right to left
1006				const img = card.querySelector( '.__right--col img' );
1007				if ( img ) {
1008					gsap.fromTo( img,
1009						{ x: 60, opacity: 0 },
1010						{ x: 0, opacity: 1, duration: 0.9, ease: 'power3.out',
1011						  scrollTrigger: { trigger: section, start: 'top 85%', toggleActions: 'play none none none' } }
1012					);
1013				}
1014			} );
1015		}
1016	} );
1017
1018	// filter module animation
1019	const filterModules = document.querySelectorAll( '[data-filter-animate]' );
1020
1021	filterModules.forEach( function ( module ) {
1022		const h2      = module.querySelector( 'h2' );
1023		const p       = module.querySelector( '.text-center p' );
1024		const buttons = module.querySelectorAll( '.filter-btn' );
1025		const cards   = module.querySelectorAll( '.element-item' );
1026
1027		// heading + subheading bottom to top
1028		const headingEls = [ h2, p ].filter( Boolean );
1029		if ( headingEls.length ) {
1030			gsap.fromTo(
1031				headingEls,
1032				{ y: 80, opacity: 0 },
1033				{
1034					y: 0,
1035					opacity: 1,
1036					duration: 0.9,
1037					ease: 'power3.out',
1038					stagger: 0.15,
1039					scrollTrigger: {
1040						trigger: module,
1041						start: 'top 85%',
1042						toggleActions: 'play none none none',
1043					},
1044				}
1045			);
1046		}
1047
1048		// filter buttons stagger left to right bottom to top
1049		if ( buttons.length ) {
1050			gsap.fromTo(
1051				Array.from( buttons ),
1052				{ y: 40, opacity: 0 },
1053				{
1054					y: 0,
1055					opacity: 1,
1056					duration: 0.6,
1057					ease: 'power3.out',
1058					stagger: 0.08,
1059					delay: 0.3,
1060					scrollTrigger: {
1061						trigger: module,
1062						start: 'top 85%',
1063						toggleActions: 'play none none none',
1064					},
1065				}
1066			);
1067		}
1068
1069		// initial cards — animate line by line (row by row)
1070		if ( cards.length ) {
1071			// group cards by their top position (same top = same row)
1072			const rows = {};
1073			cards.forEach( function ( card ) {
1074				const top = card.getBoundingClientRect().top + window.scrollY;
1075				const key = Math.round( top / 10 ) * 10;
1076				if ( ! rows[ key ] ) rows[ key ] = [];
1077				rows[ key ].push( card );
1078			} );
1079
1080			const sortedRows = Object.keys( rows ).sort( function ( a, b ) {
1081				return a - b;
1082			} );
1083
1084			sortedRows.forEach( function ( key, rowIndex ) {
1085				gsap.fromTo(
1086					rows[ key ],
1087					{ y: 80, opacity: 0 },
1088					{
1089						y: 0,
1090						opacity: 1,
1091						duration: 0.8,
1092						ease: 'power3.out',
1093						stagger: 0.08,
1094						delay: 0.5 + rowIndex * 0.2,
1095						scrollTrigger: {
1096							trigger: module,
1097							start: 'top 85%',
1098							toggleActions: 'play none none none',
1099						},
1100					}
1101				);
1102			} );
1103		}
1104	} );
1105
1106	// two column card module animation
1107	const twoColModules = document.querySelectorAll( '[data-twocol-animate]' );
1108
1109	twoColModules.forEach( function ( module ) {
1110		const h2    = module.querySelector( 'h2' );
1111		const cards = module.querySelectorAll( '.common--col' );
1112
1113		if ( h2 ) {
1114			gsap.fromTo(
1115				h2,
1116				{ y: 80, opacity: 0 },
1117				{
1118					y: 0,
1119					opacity: 1,
1120					duration: 0.9,
1121					ease: 'power3.out',
1122					scrollTrigger: {
1123						trigger: module,
1124						start: 'top 85%',
1125						toggleActions: 'play none none none',
1126					},
1127				}
1128			);
1129		}
1130
1131		if ( cards.length ) {
1132			// both cards stagger bottom to top
1133			gsap.fromTo(
1134				Array.from( cards ),
1135				{ y: 80, opacity: 0 },
1136				{
1137					y: 0,
1138					opacity: 1,
1139					duration: 0.9,
1140					ease: 'power3.out',
1141					stagger: 0.2,
1142					delay: 0.2,
1143					scrollTrigger: {
1144						trigger: module,
1145						start: 'top 85%',
1146						toggleActions: 'play none none none',
1147					},
1148				}
1149			);
1150
1151			cards.forEach( function ( card ) {
1152				const img      = card.querySelector( '.picture__block img' );
1153				const descBlock = card.querySelector( '.desc__block' );
1154
1155				if ( img ) {
1156					gsap.fromTo(
1157						img,
1158						{ scale: 1.1, opacity: 0 },
1159						{
1160							scale: 1,
1161							opacity: 1,
1162							duration: 1,
1163							ease: 'power3.out',
1164							delay: 0.3,
1165							scrollTrigger: {
1166								trigger: card,
1167								start: 'top 85%',
1168								toggleActions: 'play none none none',
1169							},
1170						}
1171					);
1172				}
1173
1174				if ( descBlock ) {
1175					gsap.fromTo(
1176						descBlock,
1177						{ y: 40, opacity: 0 },
1178						{
1179							y: 0,
1180							opacity: 1,
1181							duration: 0.9,
1182							ease: 'power3.out',
1183							delay: 0.45,
1184							scrollTrigger: {
1185								trigger: card,
1186								start: 'top 85%',
1187								toggleActions: 'play none none none',
1188							},
1189						}
1190					);
1191				}
1192			} );
1193		}
1194	} );
1195
1196	// featured group animation
1197	const featuredGroups = document.querySelectorAll( '[data-featured-group-animate]' );
1198
1199	featuredGroups.forEach( function ( module ) {
1200		const h2    = module.querySelector( 'h2' );
1201		const cards = module.querySelectorAll( '.common--col' );
1202
1203		if ( h2 ) {
1204			gsap.fromTo(
1205				h2,
1206				{ y: 80, opacity: 0 },
1207				{
1208					y: 0,
1209					opacity: 1,
1210					duration: 0.9,
1211					ease: 'power3.out',
1212					scrollTrigger: {
1213						trigger: module,
1214						start: 'top 85%',
1215						toggleActions: 'play none none none',
1216					},
1217				}
1218			);
1219		}
1220
1221		if ( cards.length ) {
1222			// cards stagger bottom to top
1223			gsap.fromTo(
1224				Array.from( cards ),
1225				{ y: 80, opacity: 0 },
1226				{
1227					y: 0,
1228					opacity: 1,
1229					duration: 0.9,
1230					ease: 'power3.out',
1231					stagger: 0.2,
1232					delay: 0.2,
1233					scrollTrigger: {
1234						trigger: module,
1235						start: 'top 85%',
1236						toggleActions: 'play none none none',
1237					},
1238				}
1239			);
1240
1241			cards.forEach( function ( card ) {
1242				const icon     = card.querySelector( '.__icon img' );
1243				const leftCol  = card.querySelector( '.__left--col' );
1244				const rightCol = card.querySelector( '.__right--col' );
1245
1246				// icon — springy pop
1247				if ( icon ) {
1248					gsap.fromTo(
1249						icon,
1250						{ scale: 0.5, opacity: 0 },
1251						{
1252							scale: 1,
1253							opacity: 1,
1254							duration: 0.6,
1255							ease: 'back.out(1.7)',
1256							delay: 0.4,
1257							scrollTrigger: {
1258								trigger: card,
1259								start: 'top 85%',
1260								toggleActions: 'play none none none',
1261							},
1262						}
1263					);
1264				}
1265
1266				// left col text — bottom to top
1267				if ( leftCol ) {
1268					const els = [ leftCol.querySelector( 'h3' ), leftCol.querySelector( 'p' ) ].filter( Boolean );
1269					gsap.fromTo(
1270						els,
1271						{ y: 40, opacity: 0 },
1272						{
1273							y: 0,
1274							opacity: 1,
1275							duration: 0.9,
1276							ease: 'power3.out',
1277							stagger: 0.12,
1278							delay: 0.35,
1279							scrollTrigger: {
1280								trigger: card,
1281								start: 'top 85%',
1282								toggleActions: 'play none none none',
1283							},
1284						}
1285					);
1286				}
1287
1288				// right col image — scale + fade
1289				if ( rightCol ) {
1290					const img = rightCol.querySelector( 'img' );
1291					if ( img ) {
1292						gsap.fromTo(
1293							img,
1294							{ scale: 1.1, opacity: 0 },
1295							{
1296								scale: 1,
1297								opacity: 1,
1298								duration: 1,
1299								ease: 'power3.out',
1300								delay: 0.4,
1301								scrollTrigger: {
1302									trigger: card,
1303									start: 'top 85%',
1304									toggleActions: 'play none none none',
1305								},
1306							}
1307						);
1308					}
1309				}
1310			} );
1311		}
1312	} );
1313
1314	// rich text module animation
1315	const richTextModules = document.querySelectorAll( '[data-richtext-animate]' );
1316
1317	richTextModules.forEach( function ( module ) {
1318		const leftCol  = module.querySelector( '.__left-col' );
1319		const rightCol = module.querySelector( '.__right-col' );
1320
1321		if ( leftCol ) {
1322			const h2  = leftCol.querySelector( 'h2' );
1323			const h4  = leftCol.querySelector( 'h4' );
1324			const p   = leftCol.querySelector( 'p' );
1325			const btn = leftCol.querySelector( 'a.primary-btn-large' );
1326
1327			const els = [ h2, h4, p, btn ].filter( Boolean );
1328
1329			gsap.fromTo(
1330				els,
1331				{ y: 80, opacity: 0 },
1332				{
1333					y: 0,
1334					opacity: 1,
1335					duration: 0.9,
1336					ease: 'power3.out',
1337					stagger: 0.15,
1338					scrollTrigger: {
1339						trigger: module,
1340						start: 'top 85%',
1341						toggleActions: 'play none none none',
1342					},
1343				}
1344			);
1345		}
1346
1347		if ( rightCol ) {
1348			gsap.fromTo(
1349				rightCol,
1350				{ x: 80, opacity: 0 },
1351				{
1352					x: 0,
1353					opacity: 1,
1354					duration: 0.9,
1355					ease: 'power3.out',
1356					delay: 0.3,
1357					scrollTrigger: {
1358						trigger: module,
1359						start: 'top 85%',
1360						toggleActions: 'play none none none',
1361					},
1362				}
1363			);
1364		}
1365	} );
1366
1367	// text module animation
1368	const textModules = document.querySelectorAll( '[data-text-module-animate]' );
1369
1370	textModules.forEach( function ( module ) {
1371		const leftBlock  = module.querySelector( '.__left--block' );
1372		const rightBlock = module.querySelector( '.__right--block' );
1373
1374		if ( leftBlock ) {
1375			const els = [ leftBlock.querySelector( 'h2' ), leftBlock.querySelector( 'div.--company--info' ), leftBlock.querySelector( 'a.primary-btn-large' ) ].filter( Boolean );
1376			gsap.fromTo(
1377				els,
1378				{ x: -60, y: 40, opacity: 0 },
1379				{
1380					x: 0,
1381					y: 0,
1382					opacity: 1,
1383					duration: 0.9,
1384					ease: 'power3.out',
1385					stagger: 0.15,
1386					scrollTrigger: {
1387						trigger: module,
1388						start: 'top 85%',
1389						toggleActions: 'play none none none',
1390					},
1391				}
1392			);
1393		}
1394
1395		if ( rightBlock ) {
1396			const els = [ rightBlock.querySelector( 'h2' ), rightBlock.querySelector( 'div.--company--info' ), rightBlock.querySelector( 'a.primary-btn-large' ) ].filter( Boolean );
1397			gsap.fromTo(
1398				els,
1399				{ x: 60, y: 40, opacity: 0 },
1400				{
1401					x: 0,
1402					y: 0,
1403					opacity: 1,
1404					duration: 0.9,
1405					ease: 'power3.out',
1406					stagger: 0.15,
1407					scrollTrigger: {
1408						trigger: module,
1409						start: 'top 85%',
1410						toggleActions: 'play none none none',
1411					},
1412				}
1413			);
1414		}
1415	} );
1416
1417	// reseller module animation
1418	const resellerHead = document.querySelector( '.reseller--head' );
1419
1420	if ( resellerHead ) {
1421		const h1     = resellerHead.querySelector( 'h1' );
1422		const select = resellerHead.querySelector( '.select-container' );
1423		const link   = resellerHead.querySelector( '.link' );
1424
1425		const els = [ h1, select, link ].filter( Boolean );
1426		gsap.fromTo(
1427			els,
1428			{ y: 80, opacity: 0 },
1429			{
1430				y: 0,
1431				opacity: 1,
1432				duration: 0.9,
1433				ease: 'power3.out',
1434				stagger: 0.15,
1435				scrollTrigger: {
1436					trigger: resellerHead,
1437					start: 'top 85%',
1438					toggleActions: 'play none none none',
1439				},
1440			}
1441		);
1442	}
1443
1444	document.querySelectorAll( '.resellers' ).forEach( function ( section ) {
1445		const h2    = section.querySelector( '.module__title--block h2' );
1446		const cards = section.querySelectorAll( '.resellers--grid .col' );
1447
1448		if ( h2 ) {
1449			gsap.fromTo(
1450				h2,
1451				{ y: 60, opacity: 0 },
1452				{
1453					y: 0,
1454					opacity: 1,
1455					duration: 0.9,
1456					ease: 'power3.out',
1457					scrollTrigger: {
1458						trigger: section,
1459						start: 'top 85%',
1460						toggleActions: 'play none none none',
1461					},
1462				}
1463			);
1464		}
1465
1466		if ( cards.length ) {
1467			// group cards row by row
1468			const rows = {};
1469			cards.forEach( function ( card ) {
1470				const top = Math.round( ( card.getBoundingClientRect().top + window.scrollY ) / 10 ) * 10;
1471				if ( ! rows[ top ] ) rows[ top ] = [];
1472				rows[ top ].push( card );
1473			} );
1474
1475			Object.keys( rows ).sort( function ( a, b ) { return a - b; } ).forEach( function ( key, rowIndex ) {
1476				gsap.fromTo(
1477					rows[ key ],
1478					{ y: 80, opacity: 0 },
1479					{
1480						y: 0,
1481						opacity: 1,
1482						duration: 0.8,
1483						ease: 'power3.out',
1484						stagger: 0.1,
1485						delay: 0.2 + rowIndex * 0.15,
1486						scrollTrigger: {
1487							trigger: section,
1488							start: 'top 85%',
1489							toggleActions: 'play none none none',
1490						},
1491					}
1492				);
1493			} );
1494		}
1495	} );
1496
1497	// material filters animation
1498	const materialFilterSections = document.querySelectorAll( '[data-material-filters-animate]' );
1499
1500	materialFilterSections.forEach( function ( section ) {
1501		const searchBar    = section.querySelector( '.search-bar' );
1502		const filterH3     = section.querySelector( '.filters-wrap h3' );
1503		const filterGroups = section.querySelectorAll( '.filter-type' );
1504
1505		// search bar — bottom to top
1506		if ( searchBar ) {
1507			gsap.fromTo(
1508				searchBar,
1509				{ y: 60, opacity: 0 },
1510				{
1511					y: 0,
1512					opacity: 1,
1513					duration: 0.9,
1514					ease: 'power3.out',
1515					scrollTrigger: {
1516						trigger: section,
1517						start: 'top 85%',
1518						toggleActions: 'play none none none',
1519					},
1520				}
1521			);
1522		}
1523
1524		// filter heading — bottom to top
1525		if ( filterH3 ) {
1526			gsap.fromTo(
1527				filterH3,
1528				{ y: 60, opacity: 0 },
1529				{
1530					y: 0,
1531					opacity: 1,
1532					duration: 0.9,
1533					ease: 'power3.out',
1534					delay: 0.2,
1535					scrollTrigger: {
1536						trigger: section,
1537						start: 'top 85%',
1538						toggleActions: 'play none none none',
1539					},
1540				}
1541			);
1542		}
1543
1544		// filter groups stagger bottom to top
1545		if ( filterGroups.length ) {
1546			gsap.fromTo(
1547				Array.from( filterGroups ),
1548				{ y: 60, opacity: 0 },
1549				{
1550					y: 0,
1551					opacity: 1,
1552					duration: 0.8,
1553					ease: 'power3.out',
1554					stagger: 0.15,
1555					delay: 0.35,
1556					scrollTrigger: {
1557						trigger: section,
1558						start: 'top 85%',
1559						toggleActions: 'play none none none',
1560					},
1561				}
1562			);
1563		}
1564	} );
1565
1566	// sliding html module animation
1567	const slidingHtmlSections = document.querySelectorAll( '[data-sli
1567ding-html-animate]' );
1568
1569	slidingHtmlSections.forEach( function ( section ) {
1570		const h2         = section.querySelector( '.heading-block h2' );
1571		const p          = section.querySelector( '.heading-block p' );
1572		const sliderNav  = section.querySelector( '.html__slider--nav' );
1573		const sliderMain = section.querySelector( '.html__slider-main' );
1574
1575		// heading + sub heading bottom to top staggered
1576		const headingEls = [ h2, p ].filter( Boolean );
1577		if ( headingEls.length ) {
1578			gsap.fromTo(
1579				headingEls,
1580				{ y: 80, opacity: 0 },
1581				{
1582					y: 0,
1583					opacity: 1,
1584					duration: 0.9,
1585					ease: 'power3.out',
1586					stagger: 0.15,
1587					scrollTrigger: {
1588						trigger: section,
1589						start: 'top 85%',
1590						toggleActions: 'play none none none',
1591					},
1592				}
1593			);
1594		}
1595
1596		// tab nav — bottom to top after heading
1597		if ( sliderNav ) {
1598			gsap.fromTo(
1599				sliderNav,
1600				{ y: 80, opacity: 0 },
1601				{
1602					y: 0,
1603					opacity: 1,
1604					duration: 0.9,
1605					ease: 'power3.out',
1606					delay: 0.3,
1607					scrollTrigger: {
1608						trigger: section,
1609						start: 'top 85%',
1610						toggleActions: 'play none none none',
1611					},
1612				}
1613			);
1614		}
1615
1616		// main slider — bottom to top after nav
1617		if ( sliderMain ) {
1618			gsap.fromTo(
1619				sliderMain,
1620				{ y: 80, opacity: 0 },
1621				{
1622					y: 0,
1623					opacity: 1,
1624					duration: 0.9,
1625					ease: 'power3.out',
1626					delay: 0.5,
1627					scrollTrigger: {
1628						trigger: section,
1629						start: 'top 85%',
1630						toggleActions: 'play none none none',
1631					},
1632				}
1633			);
1634		}
1635	} );
1636
1637	// sliding image module animation
1638	const slidingImageSections = document.querySelectorAll( '[data-sliding-image-animate]' );
1639
1640	slidingImageSections.forEach( function ( section ) {
1641		const h2        = section.querySelector( '.heading-block h2' );
1642		const p         = section.querySelector( '.heading-block p' );
1643		const btn       = section.querySelector( '.heading-block a.primary-btn-large' );
1644		const textBlock = section.querySelector( '.text-block' );
1645		const mediaResult = section.querySelector( '.media-result' );
1646		const isReverse = section.querySelector( '.wrapper.reverse' );
1647
1648		// heading + sub heading + button — bottom to top staggered
1649		const headingEls = [ h2, p, btn ].filter( Boolean );
1650		if ( headingEls.length ) {
1651			gsap.fromTo(
1652				headingEls,
1653				{ y: 80, opacity: 0 },
1654				{
1655					y: 0,
1656					opacity: 1,
1657					duration: 0.9,
1658					ease: 'power3.out',
1659					stagger: 0.15,
1660					scrollTrigger: {
1661						trigger: section,
1662						start: 'top 85%',
1663						toggleActions: 'play none none none',
1664					},
1665				}
1666			);
1667		}
1668
1669		// text block — from left side (or right if reversed layout)
1670		if ( textBlock ) {
1671			const startX = isReverse ? 80 : -80;
1672			gsap.fromTo(
1673				textBlock,
1674				{ x: startX, opacity: 0 },
1675				{
1676					x: 0,
1677					opacity: 1,
1678					duration: 0.9,
1679					ease: 'power3.out',
1680					delay: 0.3,
1681					scrollTrigger: {
1682						trigger: section,
1683						start: 'top 85%',
1684						toggleActions: 'play none none none',
1685					},
1686				}
1687			);
1688		}
1689
1690		// media result — from right side (or left if reversed layout)
1691		if ( mediaResult ) {
1692			const startX = isReverse ? -80 : 80;
1693			gsap.fromTo(
1694				mediaResult,
1695				{ x: startX, opacity: 0 },
1696				{
1697					x: 0,
1698					opacity: 1,
1699					duration: 0.9,
1700					ease: 'power3.out',
1701					delay: 0.3,
1702					scrollTrigger: {
1703						trigger: section,
1704						start: 'top 85%',
1705						toggleActions: 'play none none none',
1706					},
1707				}
1708			);
1709		}
1710	} );
1711
1712	// chart comparison animation
1713	const chartComparisonSections = document.querySelectorAll( '[data-chart-comparison-animate]' );
1714
1715	chartComparisonSections.forEach( function ( section ) {
1716		const titleBlock = section.querySelector( '.module__title--block' );
1717		const cols = section.querySelectorAll( '.static-comparison--grid .col' );
1718
1719		// title block stagger bottom to top
1720		if ( titleBlock ) {
1721			const titleEls = titleBlock.querySelectorAll( '.heading-2, .large__size--text, .primary-btn-large' );
1722			if ( titleEls.length ) {
1723				gsap.fromTo(
1724					Array.from( titleEls ),
1725					{ y: 60, opacity: 0 },
1726					{
1727						y: 0,
1728						opacity: 1,
1729						duration: 0.8,
1730						ease: 'power3.out',
1731						stagger: 0.15,
1732						scrollTrigger: {
1733							trigger: titleBlock,
1734							start: 'top 85%',
1735							toggleActions: 'play none none none',
1736						},
1737					}
1738				);
1739			}
1740		}
1741
1742		// cards stagger bottom to top
1743		if ( cols.length ) {
1744			gsap.fromTo(
1745				Array.from( cols ),
1746				{ y: 80, opacity: 0 },
1747				{
1748					y: 0,
1749					opacity: 1,
1750					duration: 0.85,
1751					ease: 'power3.out',
1752					stagger: 0.12,
1753					scrollTrigger: {
1754						trigger: section.querySelector( '.static-comparison--grid' ),
1755						start: 'top 85%',
1756						toggleActions: 'play none none none',
1757					},
1758				}
1759			);
1760
1761			cols.forEach( function ( col ) {
1762				// product image — y only (card handles opacity)
1763				const img = col.querySelector( '.picture--row img' );
1764				if ( img ) {
1765					gsap.fromTo(
1766						img,
1767						{ y: 40 },
1768						{
1769							y: 0,
1770							duration: 0.9,
1771							ease: 'power2.out',
1772							scrollTrigger: {
1773								trigger: col,
1774								start: 'top 85%',
1775								toggleActions: 'play none none none',
1776							},
1777						}
1778					);
1779				}
1780
1781				// popular ribbon — springy pop from top
1782				const ribbon = col.querySelector( '.popular-ribbon' );
1783				if ( ribbon ) {
1784					gsap.fromTo(
1785						ribbon,
1786						{ y: -30, scale: 0.6, opacity: 0 },
1787						{
1788							y: 0,
1789							scale: 1,
1790							opacity: 1,
1791							duration: 0.7,
1792							ease: 'back.out(2)',
1793							delay: 0.5,
1794							scrollTrigger: {
1795								trigger: col,
1796								start: 'top 85%',
1797								toggleActions: 'play none none none',
1798							},
1799						}
1800					);
1801				}
1802			} );
1803		}
1804	} );
1805
1806	// form module animation (all variations)
1807	const formSections = document.querySelectorAll( '[data-form-animate]' );
1808
1809	formSections.forEach( function ( section ) {
1810		const leftSection  = section.querySelector( '.left_section' );
1811		const rightSection = section.querySelector( '.right_section' );
1812		const titleBlock   = section.querySelector( '.module__title--block' );
1813		const formWrapper  = section.querySelector( '.form--module-wrapper' );
1814
1815		// two-column layout (newsletter / roadshow)
1816		if ( leftSection && rightSection ) {
1817			const leftEls = leftSection.querySelectorAll( 'h2, p' );
1818			if ( leftEls.length ) {
1819				gsap.fromTo(
1820					Array.from( leftEls ),
1821					{ x: -60, opacity: 0 },
1822					{
1823						x: 0,
1824						opacity: 1,
1825						duration: 0.8,
1826						ease: 'power3.out',
1827						stagger: 0.15,
1828						scrollTrigger: {
1829							trigger: section,
1830							start: 'top 85%',
1831							toggleActions: 'play none none none',
1832						},
1833					}
1834				);
1835			}
1836			gsap.fromTo(
1837				rightSection,
1838				{ x: 60, opacity: 0 },
1839				{
1840					x: 0,
1841					opacity: 1,
1842					duration: 0.9,
1843					ease: 'power3.out',
1844					delay: 0.2,
1845					scrollTrigger: {
1846						trigger: section,
1847						start: 'top 85%',
1848						toggleActions: 'play none none none',
1849					},
1850				}
1851			);
1852		}
1853
1854		// single-column layout (contact / quote)
1855		if ( titleBlock ) {
1856			const titleEls = titleBlock.querySelectorAll( '.display-2, h2, p' );
1857			if ( titleEls.length ) {
1858				gsap.fromTo(
1859					Array.from( titleEls ),
1860					{ y: 50, opacity: 0 },
1861					{
1862						y: 0,
1863						opacity: 1,
1864						duration: 0.8,
1865						ease: 'power3.out',
1866						stagger: 0.15,
1867						scrollTrigger: {
1868							trigger: section,
1869							start: 'top 85%',
1870							toggleActions: 'play none none none',
1871						},
1872					}
1873				);
1874			}
1875		}
1876
1877		if ( formWrapper ) {
1878			gsap.fromTo(
1879				formWrapper,
1880				{ y: 60, opacity: 0 },
1881				{
1882					y: 0,
1883					opacity: 1,
1884					duration: 0.9,
1885					ease: 'power3.out',
1886					delay: 0.3,
1887					scrollTrigger: {
1888						trigger: section,
1889						start: 'top 85%',
1890						toggleActions: 'play none none none',
1891					},
1892				}
1893			);
1894		}
1895	} );
1896
1897	// markdown module animation
1898	const markdownSections = document.querySelectorAll( '[data-markdown-animate]' );
1899
1900	markdownSections.forEach( function ( section ) {
1901		const content = section.querySelector( '.wrapper--block_inner' );
1902		if ( ! content ) return;
1903
1904		const children = Array.from( content.children );
1905		if ( ! children.length ) return;
1906
1907		gsap.fromTo(
1908			children,
1909			{ y: 50, opacity: 0 },
1910			{
1911				y: 0,
1912				opacity: 1,
1913				duration: 0.8,
1914				ease: 'power3.out',
1915				stagger: 0.12,
1916				scrollTrigger: {
1917					trigger: section,
1918					start: 'top 85%',
1919					toggleActions: 'play none none none',
1920				},
1921			}
1922		);
1923	} );
1924
1925	// material chart — header animation
1926	const materialChartHeads = document.querySelectorAll( '[data-material-chart-animate]' );
1927
1928	materialChartHeads.forEach( function ( section ) {
1929		const leftBlock  = section.querySelector( '.left--block' );
1930		const rightBlock = section.querySelector( '.right--block' );
1931
1932		if ( leftBlock ) {
1933			gsap.fromTo(
1934				leftBlock,
1935				{ x: -60, opacity: 0 },
1936				{
1937					x: 0,
1938					opacity: 1,
1939					duration: 0.85,
1940					ease: 'power3.out',
1941					scrollTrigger: {
1942						trigger: section,
1943						start: 'top 85%',
1944						toggleActions: 'play none none none',
1945					},
1946				}
1947			);
1948		}
1949
1950		if ( rightBlock ) {
1951			gsap.fromTo(
1952				rightBlock,
1953				{ x: 60, opacity: 0 },
1954				{
1955					x: 0,
1956					opacity: 1,
1957					duration: 0.85,
1958					ease: 'power3.out',
1959					delay: 0.15,
1960					scrollTrigger: {
1961						trigger: section,
1962						start: 'top 85%',
1963						toggleActions: 'play none none none',
1964					},
1965				}
1966			);
1967		}
1968	} );
1969
1970	// material chart — chart body animation
1971	const materialChartConts = document.querySelectorAll( '[data-material-chart-cont-animate]' );
1972
1973	materialChartConts.forEach( function ( section ) {
1974		const leftPanel  = section.querySelector( '.chart__left--block' );
1975		const chartInner = section.querySelector( '.chart__inner--cont' );
1976
1977		if ( leftPanel ) {
1978			gsap.fromTo(
1979				leftPanel,
1980				{ x: -80, opacity: 0 },
1981				{
1982					x: 0,
1983					opacity: 1,
1984					duration: 0.9,
1985					ease: 'power3.out',
1986					scrollTrigger: {
1987						trigger: section,
1988						start: 'top 85%',
1989						toggleActions: 'play none none none',
1990					},
1991				}
1992			);
1993		}
1994
1995		if ( chartInner ) {
1996			gsap.fromTo(
1997				chartInner,
1998				{ x: 80, opacity: 0 },
1999				{
2000					x: 0,
2001					opacity: 1,
2002					duration: 0.9,
2003					ease: 'power3.out',
2004					delay: 0.2,
2005					scrollTrigger: {
2006						trigger: section,
2007						start: 'top 85%',
2008						toggleActions: 'play none none none',
2009					},
2010				}
2011			);
2012		}
2013	} );
2014
2015	// multiple link module animation
2016	const multipleLinkSections = document.querySelectorAll( '[data-multiple-link-animate]' );
2017
2018	multipleLinkSections.forEach( function ( section ) {
2019		const headingCol  = section.querySelector( '.heading-col' );
2020		const linkCols    = section.querySelectorAll( '.link-col' );
2021
2022		// heading col — stagger h2, h3, h4, btn bottom to top
2023		if ( headingCol ) {
2024			const headEls = headingCol.querySelectorAll( 'h2, h3, h4, .anchor-wrap' );
2025			if ( headEls.length ) {
2026				gsap.fromTo(
2027					Array.from( headEls ),
2028					{ y: 50, opacity: 0 },
2029					{
2030						y: 0,
2031						opacity: 1,
2032						duration: 0.8,
2033						ease: 'power3.out',
2034						stagger: 0.15,
2035						scrollTrigger: {
2036							trigger: section,
2037							start: 'top 85%',
2038							toggleActions: 'play none none none',
2039						},
2040					}
2041				);
2042			}
2043		}
2044
2045		// link columns — stagger bottom to top
2046		if ( linkCols.length ) {
2047			gsap.fromTo(
2048				Array.from( linkCols ),
2049				{ y: 60, opacity: 0 },
2050				{
2051					y: 0,
2052					opacity: 1,
2053					duration: 0.85,
2054					ease: 'power3.out',
2055					stagger: 0.12,
2056					scrollTrigger: {
2057						trigger: section.querySelector( '.links-cols-wrap' ),
2058						start: 'top 85%',
2059						toggleActions: 'play none none none',
2060					},
2061				}
2062			);
2063
2064			// icon inside each col — springy pop (scale only, card handles opacity)
2065			linkCols.forEach( function ( col ) {
2066				const icon = col.querySelector( '.icon-wrap img' );
2067				if ( icon ) {
2068					gsap.fromTo(
2069						icon,
2070						{ scale: 0.5 },
2071						{
2072							scale: 1,
2073							duration: 0.6,
2074							ease: 'back.out(1.7)',
2075							scrollTrigger: {
2076								trigger: col,
2077								start: 'top 85%',
2078								toggleActions: 'play none none none',
2079							},
2080						}
2081					);
2082				}
2083			} );
2084		}
2085	} );
2086
2087	// specification module animation
2088	const specSections = document.querySelectorAll( '[data-specification-animate]' );
2089
2090	specSections.forEach( function ( section ) {
2091		const heading  = section.querySelector( '.spec__heading' );
2092		const specGrids = section.querySelectorAll( '.spec__detail--grid' );
2093
2094		// heading h3 — bottom to top
2095		if ( heading ) {
2096			gsap.fromTo(
2097				heading,
2098				{ y: 50, opacity: 0 },
2099				{
2100					y: 0,
2101					opacity: 1,
2102					duration: 0.8,
2103					ease: 'power3.out',
2104					scrollTrigger: {
2105						trigger: section,
2106						start: 'top 85%',
2107						toggleActions: 'play none none none',
2108					},
2109				}
2110			);
2111		}
2112
2113		// each spec row — stagger bottom to top, left col from left, right col from right
2114		specGrids.forEach( function ( grid ) {
2115			const col1 = grid.querySelector( '.spec__col-1' );
2116			const col2 = grid.querySelector( '.spec__col-2' );
2117
2118			if ( col1 ) {
2119				gsap.fromTo(
2120					col1,
2121					{ x: -40, opacity: 0 },
2122					{
2123						x: 0,
2124						opacity: 1,
2125						duration: 0.7,
2126						ease: 'power3.out',
2127						scrollTrigger: {
2128							trigger: grid,
2129							start: 'top 88%',
2130							toggleActions: 'play none none none',
2131						},
2132					}
2133				);
2134			}
2135
2136			if ( col2 ) {
2137				const rows = col2.querySelectorAll( '.spec__col--row' );
2138				if ( rows.length ) {
2139					gsap.fromTo(
2140						Array.from( rows ),
2141						{ y: 30, opacity: 0 },
2142						{
2143							y: 0,
2144							opacity: 1,
2145							duration: 0.6,
2146							ease: 'power3.out',
2147							stagger: 0.1,
2148							scrollTrigger: {
2149								trigger: grid,
2150								start: 'top 88%',
2151								toggleActions: 'play none none none',
2152							},
2153						}
2154					);
2155				}
2156			}
2157		} );
2158	} );
2159
2160	// blog header animation
2161	const blogHeaders = document.querySelectorAll( '.blog-header-animate' );
2162	blogHeaders.forEach( function ( section ) {
2163		const tags       = section.querySelector( '.blog__tags--cont' );
2164		const title      = section.querySelector( 'h1' );
2165		const authorCard = section.querySelector( '.blog__author--card' );
2166		const heroImg    = section.querySelector( '.blogpost__large--image' );
2167
2168		const topEls = [ tags, title, authorCard ].filter( Boolean );
2169		if ( topEls.length ) {
2170			gsap.fromTo(
2171				topEls,
2172				{ y: 40, opacity: 0 },
2173				{ y: 0, opacity: 1, duration: 0.75, ease: 'power3.out', stagger: 0.15,
2174				  scrollTrigger: { trigger: section, start: 'top 85%', toggleActions: 'play none none none' } }
2175			);
2176		}
2177		if ( heroImg ) {
2178			gsap.fromTo(
2179				heroImg,
2180				{ y: 60, opacity: 0 },
2181				{ y: 0, opacity: 1, duration: 0.9, ease: 'power3.out', delay: 0.35,
2182				  scrollTrigger: { trigger: section, start: 'top 85%', toggleActions: 'play none none none' } }
2183			);
2184		}
2185	} );
2186
2187	// blog webinar header animation
2188	const blogWebinarHeaders = document.querySelectorAll( '[data-blog-webinar-animate]' );
2189	blogWebinarHeaders.forEach( function ( section ) {
2190		const tags       = section.querySelector( '.blog__tags--cont' );
2191		const title      = section.querySelector( 'h1' );
2192		const authorCard = section.querySelector( '.webinar__author--card' );
2193		const video      = section.querySelector( '.blog--global--module' );
2194
2195		const topEls = [ tags, title, authorCard ].filter( Boolean );
2196		if ( topEls.length ) {
2197			gsap.fromTo(
2198				topEls,
2199				{ y: 40, opacity: 0 },
2200				{ y: 0, opacity: 1, duration: 0.75, ease: 'power3.out', stagger: 0.15,
2201				  scrollTrigger: { trigger: section, start: 'top 85%', toggleActions: 'play none none none' } }
2202			);
2203		}
2204		if ( video ) {
2205			gsap.fromTo(
2206				video,
2207				{ y: 60, opacity: 0 },
2208				{ y: 0, opacity: 1, duration: 0.9, ease: 'power3.out', delay: 0.4,
2209				  scrollTrigger: { trigger: section, start: 'top 85%', toggleActions: 'play none none none' } }
2210			);
2211		}
2212	} );
2213
2214	// blog one-column image animation
2215	const blog1ColImages = document.querySelectorAll( '[data-blog-1col-image-animate]' );
2216	blog1ColImages.forEach( function ( section ) {
2217		const img     = section.querySelector( 'img' );
2218		const caption = section.querySelector( '.picture__desc' );
2219		if ( img ) {
2220			gsap.fromTo(
2221				img,
2222				{ scale: 1.05, opacity: 0 },
2223				{ scale: 1, opacity: 1, duration: 0.9, ease: 'power2.out',
2224				  scrollTrigger: { trigger: section, start: 'top 85%', toggleActions: 'play none none none' } }
2225			);
2226		}
2227		if ( caption ) {
2228			gsap.fromTo(
2229				caption,
2230				{ y: 20, opacity: 0 },
2231				{ y: 0, opacity: 1, duration: 0.6, ease: 'power3.out', delay: 0.3,
2232				  scrollTrigger: { trigger: section, start: 'top 85%', toggleActions: 'play none none none' } }
2233			);
2234		}
2235	} );
2236
2237	// blog text module animation (one_column_text_module)
2238	const blogTextModules = document.querySelectorAll( '[data-blog-text-animate]' );
2239	blogTextModules.forEach( function ( section ) {
2240		const picture = section.querySelector( '.blog__global--picture' );
2241		const content = section.querySelector( '.blog__global--content' );
2242
2243		if ( picture ) {
2244			gsap.fromTo(
2245				picture,
2246				{ x: -60, opacity: 0 },
2247				{ x: 0, opacity: 1, duration: 0.85, ease: 'power3.out',
2248				  scrollTrigger: { trigger: section, start: 'top 85%', toggleActions: 'play none none none' } }
2249			);
2250		}
2251		if ( content ) {
2252			const contentEls = content.querySelectorAll( 'h2, h4, p, a' );
2253			gsap.fromTo(
2254				Array.from( contentEls.length ? contentEls : [ content ] ),
2255				{ y: 50, opacity: 0 },
2256				{ y: 0, opacity: 1, duration: 0.8, ease: 'power3.out', stagger: 0.12, delay: picture ? 0.2 : 0,
2257				  scrollTrigger: { trigger: section, start: 'top 85%', toggleActions: 'play none none none' } }
2258			);
2259		}
2260	} );
2261
2262	// blog two-columns image animation
2263	const blog2ColImages = document.querySelectorAll( '[data-blog-2col-image-animate]' );
2264	blog2ColImages.forEach( function ( section ) {
2265		const pictures = section.querySelectorAll( '.blog__global--picture' );
2266		if ( pictures.length ) {
2267			gsap.fromTo(
2268				Array.from( pictures ),
2269				{ y: 60, opacity: 0 },
2270				{ y: 0, opacity: 1, duration: 0.85, ease: 'power3.out', stagger: 0.15,
2271				  scrollTrigger: { trigger: section, start: 'top 85%', toggleActions: 'play none none none' } }
2272			);
2273		}
2274	} );
2275
2276	// blog video animation
2277	const blogVideos = document.querySelectorAll( '[data-blog-video-animate]' );
2278	blogVideos.forEach( function ( section ) {
2279		const poster  = section.querySelector( '.video-poster' );
2280		const playBtn = section.querySelector( '.video__playbtn-wrapper' );
2281		const caption = section.querySelector( '.picture__desc' );
2282
2283		if ( poster ) {
2284			gsap.fromTo(
2285				poster,
2286				{ scale: 1.05, opacity: 0 },
2287				{ scale: 1, opacity: 1, duration: 0.9, ease: 'power2.out',
2288				  scrollTrigger: { trigger: section, start: 'top 85%', toggleActions: 'play none none none' } }
2289			);
2290		}
2291		if ( playBtn ) {
2292			gsap.fromTo(
2293				playBtn,
2294				{ y: 30, opacity: 0 },
2295				{ y: 0, opacity: 1, duration: 0.6, ease: 'power3.out', delay: 0.3,
2296				  scrollTrigger: { trigger: section, start: 'top 85%', toggleActions: 'play none none none' } }
2297			);
2298		}
2299		if ( caption ) {
2300			gsap.fromTo(
2301				caption,
2302				{ y: 20, opacity: 0 },
2303				{ y: 0, opacity: 1, duration: 0.5, ease: 'power3.out', delay: 0.45,
2304				  scrollTrigger: { trigger: section, start: 'top 85%', toggleActions: 'play none none none' } }
2305			);
2306		}
2307	} );
2308
2309	// blog promotional module animation
2310	const blogPromos = document.querySelectorAll( '[data-blog-promo-animate]' );
2311	blogPromos.forEach( function ( section ) {
2312		const image   = section.querySelector( '.prom--picture-block' );
2313		const content = section.querySelector( '.prom--content--block' );
2314
2315		if ( image ) {
2316			gsap.fromTo(
2317				image,
2318				{ x: -70, opacity: 0 },
2319				{ x: 0, opacity: 1, duration: 0.9, ease: 'power3.out',
2320				  scrollTrigger: { trigger: section, start: 'top 85%', toggleActions: 'play none none none' } }
2321			);
2322		}
2323		if ( content ) {
2324			const els = content.querySelectorAll( 'h3, p, a' );
2325			gsap.fromTo(
2326				Array.from( els ),
2327				{ y: 50, opacity: 0 },
2328				{ y: 0, opacity: 1, duration: 0.8, ease: 'power3.out', stagger: 0.15, delay: 0.2,
2329				  scrollTrigger: { trigger: section, start: 'top 85%', toggleActions: 'play none none none' } }
2330			);
2331		}
2332	} );
2333
2334	// blog related content animation
2335	const blogRelated = document.querySelectorAll( '[data-blog-related-animate]' );
2336	blogRelated.forEach( function ( section ) {
2337		const heading = section.querySelector( '.module__title--block' );
2338		const cards   = section.querySelectorAll( '.related__content--card' );
2339
2340		if ( heading ) {
2341			gsap.fromTo(
2342				heading,
2343				{ y: 40, opacity: 0 },
2344				{ y: 0, opacity: 1, duration: 0.75, ease: 'power3.out',
2345				  scrollTrigger: { trigger: section, start: 'top 85%', toggleActions: 'play none none none' } }
2346			);
2347		}
2348		if ( cards.length ) {
2349			gsap.fromTo(
2350				Array.from( cards ),
2351				{ y: 70, opacity: 0 },
2352				{ y: 0, opacity: 1, duration: 0.85, ease: 'power3.out', stagger: 0.12,
2353				  scrollTrigger: { trigger: section.querySelector( '.related__content--grid' ), start: 'top 85%', toggleActions: 'play none none none' } }
2354			);
2355		}
2356	} );
2357
2358	// blog speaker module animation
2359	const blogSpeakers = document.querySelectorAll( '[data-blog-speaker-animate]' );
2360	blogSpeakers.forEach( function ( section ) {
2361		const cards = section.querySelectorAll( '.speaker__module--card' );
2362		if ( cards.length ) {
2363			gsap.fromTo(
2364				Array.from( cards ),
2365				{ y: 60, opacity: 0 },
2366				{ y: 0, opacity: 1, duration: 0.8, ease: 'power3.out', stagger: 0.15,
2367				  scrollTrigger: { trigger: section, start: 'top 85%', toggleActions: 'play none none none' } }
2368			);
2369			cards.forEach( function ( card ) {
2370				const photo = card.querySelector( '.--module--picture img' );
2371				if ( photo ) {
2372					gsap.fromTo(
2373						photo,
2374						{ scale: 1.1 },
2375						{ scale: 1, duration: 0.8, ease: 'power2.out',
2376						  scrollTrigger: { trigger: card, start: 'top 85%', toggleActions: 'play none none none' } }
2377					);
2378				}
2379			} );
2380		}
2381	} );
2382
2383	// blog markdown animation
2384	const blogMarkdowns = document.querySelectorAll( '[data-blog-markdown-animate]' );
2385	blogMarkdowns.forEach( function ( section ) {
2386		const content = section.querySelector( '.wrapper--block_inner' );
2387		if ( ! content ) return;
2388		const children = Array.from( content.children );
2389		if ( ! children.length ) return;
2390		gsap.fromTo(
2391			children,
2392			{ y: 40, opacity: 0 },
2393			{ y: 0, opacity: 1, duration: 0.75, ease: 'power3.out', stagger: 0.1,
2394			  scrollTrigger: { trigger: section, start: 'top 85%', toggleActions: 'play none none none' } }
2395		);
2396	} );
2397
2398	// blog plain html animation
2399	const blogPlainHtmls = document.querySelectorAll( '[data-blog-plainhtml-animate]' );
2400	blogPlainHtmls.forEach( function ( section ) {
2401		const content = section.querySelector( '.blog__global--content' );
2402		if ( ! content ) return;
2403		gsap.fromTo(
2404			content,
2405			{ y: 40, opacity: 0 },
2406			{
2407				y: 0,
2408				opacity: 1,
2409				duration: 0.8,
2410				ease: 'power3.out',
2411				scrollTrigger: {
2412					trigger: section,
2413					start: 'top 85%',
2414					toggleActions: 'play none none none',
2415				},
2416			}
2417		);
2418	} );
2419
2420	// generic section animation — bottom to top
2421	const animateSections = document.querySelectorAll( '[data-animate-section]' );
2422
2423	animateSections.forEach( function ( section ) {
2424		const children = section.children;
2425		if ( ! children.length ) return;
2426
2427		gsap.fromTo(
2428			Array.from( children ),
2429			{ y: 80, opacity: 0 },
2430			{
2431				y: 0,
2432				opacity: 1,
2433				duration: 0.9,
2434				ease: 'power3.out',
2435				stagger: 0.15,
2436				scrollTrigger: {
2437					trigger: section,
2438					start: 'top 85%',
2439					toggleActions: 'play none none none',
2440				},
2441			}
2442		);
2443	} );
2444
2445} );

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.