PageSourceSearch

https://unboundia.com/wp-content/themes/unboundia/js/custom-gsap-animation.js?ver=1790296943

js unboundia.com collected 2026-09-25 03:09:45 UTC 105,690 bytes, 3,584 lines download raw bytes

1//*************** Clients logos marquee animation *************** 
2window.addEventListener("load", () => {
3
4    document.querySelectorAll(".client-logos").forEach((list, index) => {
5
6        const maxScroll = list.scrollWidth - list.parentElement.clientWidth;
7        const offset = 20;
8
9        const startX = index % 2 === 0 ? offset : -(maxScroll - offset);
10        const endX = index % 2 === 0 ? -(maxScroll - offset) : offset;
11
12        gsap.set(list, {
13            x: index === 0 ? 20 : -maxScroll - 20
14        });
15
16        gsap.to(list, {
17            x: index === 0 ? -maxScroll - 20 : 20,
18            duration: 50,
19            ease: "ease",
20            repeat: -1,
21            yoyo: true
22        });
23
24    });
25  ScrollTrigger.refresh();
26});
27//*************** Clients logos marquee animation *************** 
28
29//***************  All over the page UnboundIA animation *************** 
30// (function ($) {
31// document.addEventListener("DOMContentLoaded", () => {
32
33//         gsap.registerPlugin(SplitText, ScrollTrigger);
34//         function fadeInAnime() {
35
36//             $(".ub_animefade").each(function () {
37
38//                 const section = $(this);
39
40//                 const ub_animefadeInLeft = section.find(".ub_animefadeInLeft");
41//                 const ub_animefadeInLeftSlow = section.find(".ub_animefadeInLeftSlow");
42//                 const ub_animefadeInRight = section.find(".ub_animefadeInRight");
43//                 const ub_animefadeInRightSlow = section.find(".ub_animefadeInRightSlow");
44//                 const ub_animefadeInUp = section.find(".ub_animefadeInUp");
45//                 const ub_animefadeInUpImage = section.find(".ub_animefadeInUpImage");
46//                 const ub_animefadeInImageGroup = section.find(".ub_animefadeInImage");
47//                 const ub_animefadeInUpImageGroup = section.find(".ub_animefadeInUpImageGroup");
48//                 const ub_animefadeIn = section.find(".ub_animefadeIn");
49//                 const ub_animefadeInSlowup = section.find(".ub_animefadeInSlowup");
50//                 const ub_line = section.find(".ub_line");
51//                 const ub_animeLeftToRight = section.find(".ub_animeLeftToRight");
52//                 const splitElement = section.find(".ub_split_text");
53//                 const ub_rewrite_text = section.find(".ub_rewrite_text")
54//                 const ub_revel_text = section.find(".ub_revel_text")
55//                 const ub_revel_textRTL = section.find(".ub_revel_textRTL")
56
57//                 let splitText;
58
59//                 if (splitElement.length) {
60//                     splitText = SplitText.create(splitElement[0], {
61//                         type: "words"
62//                     });
63//                 }
64
65//                 if (ub_animefadeInLeft.length) {
66//                     gsap.fromTo(ub_animefadeInLeft, {
67//                         x: -200,
68//                         opacity: 0,
69//                     },
70//                     {
71//                          x:0,
72//                         opacity: 1,
73//                         stagger: 0.06,
74//                         duration:1,
75//                         ease: "power2.out",
76//                         scrollTrigger: {
77//                             trigger: section[0],
78//                             start: "top 90%",
79//                             toggleActions: "play play play reverse",
80//                             markers: false
81//                         }
82//                     });
83//                     // gsap.from(ub_animefadeInLeft, {
84//                     //     x: -200,
85//                     //     opacity: 0,
86//                     //     stagger: 0.06,
87//                     //     duration:1,
88//                     //     ease: "power2.out",
89//                     //     scrollTrigger: {
90//                     //         trigger: section[0],
91//                     //         start: "top 90%",
92//                     //         toggleActions: "play play play reverse",
93//                     //         markers: false
94//                     //     }
95//                     // });
96//                 }
97
98//                 if (ub_animefadeInRight.length) {
99//                     gsap.fromTo(ub_animefadeInRight, {
100//                         x: 200,
101//                         opacity: 0,
102//                     },{
103//                         x: 0,
104//                         opacity: 1,
105//                         stagger: 0.06,
106//                         duration: 1,
107//                         ease: "power2.out",
108//                         scrollTrigger: {
109//                             trigger: section[0],
110//                             start: "top 90%",
111//                             toggleActions: "play play play reverse",
112//                             markers: false
113//                         }
114//                     });
115//                 }
116
117//                 if (ub_animefadeInLeftSlow.length) {
118//                     gsap.from(ub_animefadeInLeftSlow, {
119//                         x: -200,
120//                         opacity: 0,
121//                         duration: 2,
122//                         ease: "power2.out",
123//                         scrollTrigger: {
124//                             trigger: section[0],
125//                             start: "top 90%",
126//                             toggleActions: "play play play reverse",
127//                             markers: false
128//                         }
129//                     });
130
131//                     gsap.utils.toArray(".delay-1").forEach((el) => {
132//                         gsap.set(el, { delay: 0.5 });
133//                     });
134//                 }
135
136//                 if (ub_animefadeInUp.length) {
137//                     gsap.fromTo(ub_animefadeInUp, {
138//                         y: 200,
139//                         opacity: 0,
140//                     },{
141//                          y: 0,
142//                         opacity: 1,                        
143//                         stagger: 0.06,
144//                         duration: 1,
145//                         ease: "power2.out",
146//                         scrollTrigger: {
147//                             trigger: section[0],
148//                             start: "top center",
149//                             toggleActions: "play play play reverse",
150//                             markers: false
151//                         }
152//                     });
153//                 }
154
155//                 /*
156//                  * Images that carry the class directly.
157//                  * FIX: this previously targeted ub_animefadeInUp,
158//                  * so the images never animated.
159//                  */
160//                 if (ub_animefadeInUpImage.length) {
161//                     gsap.from(ub_animefadeInUpImage, {
162//                         y: 200,
163//                         opacity: 0,
164//                         stagger: 0.06,
165//                         duration: 0.5,
166//                         ease: "power2.out",
167//                         scrollTrigger: {
168//                             trigger: section[0],
169//                             start: "top center",
170//                             toggleActions: "play play play reverse",
171//                             markers: false
172//                         }
173//                     });
174//                 }
175
176//                 if (ub_animefadeInUpImageGroup.length) {
177//                         ub_animefadeInUpImageGroup.each(function () {
178
179//                         const groupImages = $(this).find("img");
180
181//                         if (!groupImages.length) {
182//                             return;
183//                         }
184
185//                         gsap.fromTo(groupImages, {
186//                             y: 200,
187//                             opacity: 0,
188//                             }
189//                             ,{
190//                             y: 0,
191//                             opacity: 1,
192//                             stagger: 0.5,
193//                             duration: 0.5,
194//                             ease: "power2.out",
195//                             scrollTrigger: {
196//                                 trigger: this,
197//                                 start: "top 85%",
198//                                 toggleActions: "play none none reverse",
199//                                 markers: false
200//                             }
201//                         });
202//                     });
203//                 }
204//                 if (ub_animefadeInImageGroup.length) {
205//                     ub_animefadeInImageGroup.each(function () {
206
207//                         const groupImages = $(this).find("img");
208
209//                         if (!groupImages.length) {
210//                             return;
211//                         }
212
213//                         gsap.from(groupImages, {
214//                             // y: 100,
215//                             opacity: 0,
216//                             stagger: 0.5,
217//                             duration: 0.3,
218//                             ease: "power2.out",
219//                             scrollTrigger: {
220//                                 trigger: this,
221//                                 start: "top 85%",
222//                                 toggleActions: "play none none reverse",
223//                                 markers: false
224//                             }
225//                         });
226//                     });
227//                 }
228
229//                 if (ub_animefadeInSlowup.length) {
230//                     gsap.from(ub_animefadeInSlowup, {
231//                         y: 200,
232//                         opacity: 0,
233//                         stagger: 0.06,
234//                         duration: 2,
235//                         ease: "power2.out",
236//                         scrollTrigger: {
237//                             trigger: section[0],
238//                             start: "top center",
239//                             toggleActions: "play play play reverse",
240//                             markers: false
241//                         }
242//                     });
243//                 }
244
245//                 if (ub_animeLeftToRight.length) {
246//                     gsap.fromTo(
247//                         ub_animeLeftToRight,
248//                         {
249//                             width: "0%"
250//                         },
251//                         {
252//                             width: "100%",
253//                             stagger: 1,
254//                             duration: 6,
255//                             ease: "power2.out",
256//                             scrollTrigger: {
257//                                 trigger: section[0],
258//                                 start: "top center",
259//                                 toggleActions: "play play play reverse",
260//                                 markers: false
261//                             }
262//                         }
263//                     );
264//                 }
265
266//                 if (splitElement.length) {
267//                             gsap.fromTo(splitElement,
268//                                 {opacity:0},
269//                                 {opacity:1,
270//                                         scrollTrigger: {
271//                                         trigger: section[0],
272//                                         start: "top 85%",
273//                                         toggleActions: "play play play reverse"
274//                                     }
275//                                 }
276//                             )
277//                             gsap.fromTo(
278//                                 splitText.words,
279//                                 {
280//                                     opacity: 0,
281//                                     y: 24,
282//                                     filter: "blur(6px)"
283//                                 },
284//                                 {
285//                                     opacity: 1,
286//                                     y: 0,
287//                                     filter: "blur(0px)",
288//                                     stagger: 0.10,
289//                                     duration: 0.5,
290//                                     ease: "power3.out",
291//                                     delay: 0.15,
292//                                     scrollTrigger: {
293//                                         trigger: splitElement[0],
294//                                         start: "top 85%",
295//                                         toggleActions: "play play play reverse"
296//                                     }
297//                                 }
298//                             );
299//                 }
300
301//                 if (ub_line.length) {
302//                     gsap.from(ub_line, {
303//                         y: 30,
304//                         opacity: 0,
305//                         stagger: 0.2,
306//                         duration: 0.8,
307//                         ease: "power2.out",
308//                         scrollTrigger: {
309//                             trigger: ub_line[0],
310//                             start: "top 90%",
311//                             toggleActions: "play play play reverse",
312//                             markers:false
313//                         }
314//                     });
315//                 }
316//                 if (ub_rewrite_text.length) {
317//                     const newText = ub_rewrite_text.data("text");
318//                     gsap.to(ub_rewrite_text, {
319//                         text: newText,
320//                         duration: 2,
321//                         ease: "none",
322//                         scrollTrigger: {
323//                             trigger: ub_rewrite_text[0],
324//                             start: "top 90%",
325//                             toggleActions: "play play play reverse"
326//                         }
327//                     });
328
329//                 }
330//                 if (ub_revel_text.length) {
331//                     const newText = ub_rewrite_text.data("text");
332//                     gsap.from(ub_revel_text, {
333//                         clipPath: "inset(0 100% 0 0)",
334//                         duration: 1.2,
335//                         ease: "power3.inOut",
336//                         scrollTrigger: {
337//                             trigger: ub_revel_text[0],
338//                             start: "top 90%",
339//                             toggleActions: "play play play reverse"
340//                         }
341//                     });
342
343//                 }
344//                 if (ub_revel_textRTL.length) {
345//                     // const newText = ub_rewrite_text.data("text");
346//                     gsap.from(ub_revel_textRTL, {
347//                         clipPath: "inset(0px 0px 0px 100%)",
348//                         duration: 1.2,
349//                         ease: "power3.inOut",
350//                         scrollTrigger: {
351//                             trigger: ub_revel_textRTL[0],
352//                             start: "top 90%",
353//                             toggleActions: "play play play reverse"
354//                         }
355//                     });
356
357//                 }
358//             });
359
360//             if (document.body.classList.contains("single")) {
361//                 const ub_line = gsap.utils.toArray(
362//                     ".blog-container p, .blog-container li, .blog-container h2, .blog-container h3, .blog-container h4, .blog-container img, .blog-container table,.blog-container .auth-quote"
363//                 );
364
365//                 if (ub_line.length) {
366//                     ub_line.forEach((element) => {
367//                         gsap.from(element, {
368//                             y: 30,
369//                             opacity: 0,
370//                             duration: 0.8,
371//                             ease: "power2.out",
372//                             scrollTrigger: {
373//                                 trigger: element,
374//                                 start: "top 90%",
375//                                 toggleActions: "play none none reverse",
376//                                 markers: false
377//                             }
378//                         });
379//                     });
380//                 }
381//             }
382
383//         }
384
385
386//         fadeInAnime();
387
388//     });
389//       ScrollTrigger.refresh();
390// })(jQuery);
391//***************  All over the page UnboundIA animation *************** 
392
393
394//*************** About Us page - leaders section fadein next slide on scroll *************** 
395document.addEventListener("DOMContentLoaded", () => {
396
397    const wrap = document.querySelector("#images");
398    if (!wrap) return;
399
400    const items = gsap.utils.toArray("#images .leaders-img-container");
401    if (!items.length) return;
402
403    const figures = items.map(it => it.querySelector("figure"));
404    const bgs     = items.map(it => it.querySelector(".leaders-bg"));
405
406    /*
407     * Timing (seconds)
408     */
409    const HOLD    = 1;
410    const IN      = 0.5;
411    const OUT     = 0.5;
412    const OVERLAP = 0;
413
414    /*
415     * How far the image travels (%).
416     */
417    const SHIFT = 20;
418
419    /*
420     * Image direction:
421     * even (0,2,4) => image from LEFT
422     * odd  (1,3,5) => image from RIGHT
423     */
424    const dirFor = i => (i % 2 === 0 ? -1 : 1);
425
426    /*
427     * BG reveals from the OPPOSITE side
428     */
429    const bgHidden = i =>
430        dirFor(i) < 0
431            ? "inset(0% 0% 0% 100%)"
432            : "inset(0% 100% 0% 0%)";
433
434    const bgShown = "inset(0% 0% 0% 0%)";
435
436    /*
437     * Initial hidden state
438     */
439    items.forEach((it, i) => {
440        gsap.set(it, { autoAlpha: 0 });
441
442        gsap.set(figures[i], {
443            xPercent: dirFor(i) * SHIFT,
444            autoAlpha: 0
445        });
446
447        gsap.set(bgs[i], {
448            clipPath: bgHidden(i),
449            webkitClipPath: bgHidden(i),
450            autoAlpha: 1
451        });
452    });
453
454    /*
455     * Create timeline paused initially.
456     */
457    const tl = gsap.timeline({
458        repeat: -1,
459        paused: true
460    });
461
462    const total = items.length;
463
464    for (let i = 0; i < total; i++) {
465
466        // --- ENTER CARD ---
467        tl.add("enter" + i);
468
469        tl.set(items[i], {
470            autoAlpha: 1
471        }, "enter" + i);
472
473        // Image enters
474        tl.to(figures[i], {
475            xPercent: 0,
476            autoAlpha: 1,
477            duration: IN,
478            ease: "power3.out"
479        }, "enter" + i);
480
481        // Background reveals
482        tl.to(bgs[i], {
483            clipPath: bgShown,
484            webkitClipPath: bgShown,
485            duration: IN,
486            ease: "power3.out"
487        }, "enter" + i);
488
489        // --- HOLD ---
490        tl.to({}, {
491            duration: HOLD
492        });
493
494        // --- EXIT ---
495        tl.to(items[i], {
496            autoAlpha: 0,
497            duration: OUT,
498            ease: "power2.inOut"
499        });
500
501        // Reset current card
502        tl.set(figures[i], {
503            xPercent: dirFor(i) * SHIFT,
504            autoAlpha: 0
505        });
506
507        tl.set(bgs[i], {
508            clipPath: bgHidden(i),
509            webkitClipPath: bgHidden(i)
510        });
511
512        // Next card
513        tl.add(
514            "enter" + ((i + 1) % total),
515            "-=" + OVERLAP
516        );
517    }
518
519    /*
520     * START ANIMATION WHEN #images
521     * REACHES CENTER OF VIEWPORT
522     */
523    ScrollTrigger.create({
524        trigger: wrap,
525        start: "top center",
526
527        onEnter: () => {
528            tl.play();
529        },
530
531        onEnterBack: () => {
532            tl.play();
533        },
534
535        onLeaveBack: () => {
536            tl.pause();
537        }
538    });
539
540});
541//*************** About Us page - leaders section fadein next slide on scroll *************** 
542
543//*************** Home Page - Proof in Numbers Section****************************
544document.addEventListener("DOMContentLoaded", function () {
545
546    const section = document.querySelector(".home-feture-scrollTween-sec");
547    const track = document.querySelector(".feture-scrollTween-row");
548    const targetFigure = document.querySelector(".prf-in-num-col-5 figure");
549
550    if (!track || !section || !targetFigure) {
551        return;
552    }
553
554    const mm = gsap.matchMedia();
555
556    mm.add("(min-width: 769px)", () => {
557
558        const culturalScroll = gsap.to(section, {
559            x: () => {
560                const figureRect = targetFigure.getBoundingClientRect();
561                const distance = figureRect.right - window.innerWidth;
562                return -Math.max(0, distance);
563            },
564
565            ease: "none",
566
567            scrollTrigger: {
568                trigger: track,
569                pin: section,
570                start: "top top",
571
572                end: () => {
573                    const figureRect = targetFigure.getBoundingClientRect();
574                    const distance = figureRect.right - window.innerWidth;
575                    return `+=${Math.max(0, distance)}`;
576                },
577
578                scrub: 2,
579                anticipatePin: 1,
580                markers: false,
581                invalidateOnRefresh: true
582            }
583        });
584
585        return () => {
586            culturalScroll.kill();
587        };
588    });
589
590    window.addEventListener("load", function () {
591        ScrollTrigger.refresh();
592    });
593
594});
595//*************** Home Page - Proof in Numbers Section****************************
596
597//*************** Case study single page - cards horizontal scroll Section******************
598document.addEventListener("DOMContentLoaded", function () {
599    const sliderSection = document.querySelector(".case-study-text-slider");
600    const singleCard = document.querySelector(".case-study-single-card");
601
602    if (!sliderSection) {
603        return;
604    }
605
606    const sliderViewport = sliderSection.querySelector(".main-content-slider");
607    const sliderWrapper = sliderSection.querySelector(".case-study-swiper");
608    const slides = sliderSection.querySelectorAll(".case-study-slide");
609
610    if (!sliderViewport || !sliderWrapper || !slides.length) {
611        return;
612    }
613
614    function getHeaderHeight() {
615        const header =
616            document.querySelector("header") ||
617            document.querySelector(".site-header") ||
618            document.querySelector(".header");
619        if (!header) {
620            return 0;
621        }
622        return header.getBoundingClientRect().height;
623    }
624
625    function updateBlurSlides(progress) {
626        const totalSlides = slides.length;
627        let visibleSlides = 3;
628
629        if (window.innerWidth < 768) {
630            visibleSlides = 1;
631        } else if (window.innerWidth < 1024) {
632            visibleSlides = 2;
633        }
634
635        if (totalSlides <= visibleSlides) {
636            slides.forEach(function (slide) {
637                slide.classList.remove("is-blur-slide");
638                slide.classList.add("is-visible-slide");
639            });
640            return;
641        }
642
643        const maxStartIndex = totalSlides - visibleSlides;
644        const currentIndex = Math.round(progress * maxStartIndex);
645
646        slides.forEach(function (slide, index) {
647            slide.classList.remove("is-visible-slide", "is-blur-slide");
648
649            if (index >= currentIndex && index < currentIndex + visibleSlides) {
650                slide.classList.add("is-visible-slide");
651            } else {
652                slide.classList.add("is-blur-slide");
653            }
654        });
655    }
656
657    function initCaseStudyHorizontalScroll() {
658        const oldTrigger = ScrollTrigger.getById("caseStudyHorizontalScroll");
659        if (oldTrigger) {
660            oldTrigger.kill();
661        }
662
663        gsap.set(sliderWrapper, { x: 0 });
664
665        const viewportWidth = sliderViewport.getBoundingClientRect().width;
666        const wrapperWidth = sliderWrapper.scrollWidth;
667        const extraScroll = 60;
668        const actualScrollDistance = wrapperWidth - viewportWidth;
669        const scrollDistance = actualScrollDistance + extraScroll;
670
671        if (actualScrollDistance <= 0) {
672            updateBlurSlides(0);
673            return;
674        }
675
676        updateBlurSlides(0);
677
678        const screenWidth = window.innerWidth;
679
680        /*
681         * Above 1430px  → pin the entire .case-study-single-card,
682         *                 centered vertically in the viewport,
683         *                 triggered at top (no header-height offset).
684         * 1024–1429px   → pin .case-study-text-slider only.
685         * Below 1024px  → pin the entire .case-study-single-card,
686         *                 centered vertically in the viewport,
687         *                 triggered at top (no header-height offset).
688         */
689
690        const isMidRange = screenWidth >= 1024 && screenWidth <= 1429;
691
692        var triggerEl, startValue;
693
694        if (isMidRange) {
695            // Mid-range: slider section only
696            triggerEl = sliderSection;
697            startValue = "center center";
698        } else {
699            // Large desktop AND below 1024: whole card block
700            triggerEl = singleCard ? singleCard : sliderSection;
701
702            /*
703             * Centred vertically, measured against the top of the
704             * viewport — no header-height compensation.
705             */
706            startValue = "center center";
707        }
708
709        gsap.to(sliderWrapper, {
710            x: -scrollDistance,
711            ease: "none",
712
713            scrollTrigger: {
714                id: "caseStudyHorizontalScroll",
715                trigger: triggerEl,
716                start: startValue,
717                end: "+=" + scrollDistance,
718                pin: true,
719                pinSpacing: true,
720                scrub: 1,
721                anticipatePin: 1,
722                invalidateOnRefresh: true,
723                markers:false,
724                onUpdate: function (self) {
725                    var slideProgress = Math.min(
726                        self.progress * (scrollDistance / actualScrollDistance),
727                        1
728                    );
729                    updateBlurSlides(slideProgress);
730                }
731            }
732        });
733
734        ScrollTrigger.refresh();
735    }
736
737    setTimeout(function () {
738        initCaseStudyHorizontalScroll();
739    }, 100);
740
741    var resizeTimer;
742    window.addEventListener("resize", function () {
743        clearTimeout(resizeTimer);
744        resizeTimer = setTimeout(function () {
745            initCaseStudyHorizontalScroll();
746        }, 300);
747    });
748
749    ScrollTrigger.refresh();
750});
751//*************** Case study single page - cards horizontal scroll Section******************
752
753//*************** Single - thinking list section -all over the page ******************
754document.addEventListener('DOMContentLoaded', function () {
755  function initReveal() {
756    const thinking = document.querySelector('.thinking-list');
757
758    if (!thinking) return;
759
760    const rows = thinking.querySelectorAll('.thinking-item');
761
762    gsap.set(rows, {
763      opacity: 0,
764      y: -80
765    });
766
767    gsap.to(rows, {
768      opacity: 1,
769      y: 0,
770      duration: 0.85,
771      ease: 'power3.out',
772      stagger: {
773        each: 0.13,
774        from: 'start'
775      },
776      scrollTrigger: {
777        trigger: thinking,
778        start: 'top 88%',
779        once: true,
780        // markers: true
781      }
782    });
783  }
784
785  initReveal();
786  ScrollTrigger.refresh();
787});
788//*************** Single - thinking list section -all over the page ******************
789
790//*************** Single - roundup post expert panel horizontally scroll ***************//
791document.addEventListener("DOMContentLoaded", function () {
792
793    if (
794        typeof gsap === "undefined" ||
795        typeof ScrollTrigger === "undefined"
796    ) {
797        console.warn("GSAP or ScrollTrigger is not available.");
798        return;
799    }
800
801    gsap.registerPlugin(ScrollTrigger);
802
803    const section = document.querySelector(".expert-panel");
804
805    if (!section) {
806        return;
807    }
808
809    const list = section.querySelector(".expert-list");
810
811    if (!list) {
812        return;
813    }
814
815    const cards = gsap.utils.toArray(
816        list.querySelectorAll(".scroll-section")
817    );
818
819    if (!cards.length) {
820        return;
821    }
822
823    let expertTween = null;
824
825    function initExpertHorizontalScroll() {
826
827        /*
828         * Remove previous ScrollTrigger
829         */
830        if (expertTween) {
831            expertTween.scrollTrigger?.kill();
832            expertTween.kill();
833            expertTween = null;
834        }
835
836        const oldTrigger = ScrollTrigger.getById(
837            "expertHorizontalScroll"
838        );
839
840        if (oldTrigger) {
841            oldTrigger.kill();
842        }
843
844        /*
845         * Reset transform
846         */
847        gsap.set(list, {
848            x: 0
849        });
850
851        /*
852         * Force layout calculation
853         */
854        ScrollTrigger.refresh();
855
856        /*
857         * ==========================================
858         * GET ACTUAL POSITIONS
859         * ==========================================
860         */
861
862        const sectionRect = section.getBoundingClientRect();
863
864        const firstCard = cards[0];
865        const lastCard = cards[cards.length - 1];
866
867        if (!firstCard || !lastCard) {
868            return;
869        }
870
871        const firstRect = firstCard.getBoundingClientRect();
872        const lastRect = lastCard.getBoundingClientRect();
873
874        /*
875         * How much the list needs to move
876         * so the LAST card reaches the right side.
877         */
878        const finalX =
879            sectionRect.right - lastRect.right;
880
881        /*
882         * Make sure the first card starts
883         * completely inside the viewport.
884         *
885         * If your existing CSS is already correct,
886         * this will normally be 0.
887         */
888        let startX = 0;
889
890        if (firstRect.left < sectionRect.left) {
891            startX = sectionRect.left - firstRect.left;
892        }
893
894        /*
895         * ==========================================
896         * HORIZONTAL ANIMATION
897         * ==========================================
898         */
899
900        expertTween = gsap.fromTo(
901        list,
902
903        {
904            x: startX
905        },
906
907        {
908            x: finalX,
909
910            ease: "none",
911
912            scrollTrigger: {
913
914                id: "expertHorizontalScroll",
915
916                trigger: section,
917
918                start: "center center",
919
920                /*
921                * IMPORTANT:
922                * End exactly when the horizontal
923                * movement is finished.
924                */
925                end: function () {
926
927                    const distance = Math.abs(
928                        finalX - startX
929                    );
930
931                    return "+=" + distance;
932                },
933
934                scrub: 0.8,
935
936                pin: true,
937
938                pinSpacing: true,
939
940                anticipatePin: 1,
941
942                invalidateOnRefresh: true,
943
944                markers: false
945            }
946        }
947    );
948    }
949
950    /*
951     * ==========================================
952     * INITIALIZE
953     * ==========================================
954     */
955
956    initExpertHorizontalScroll();
957
958    /*
959     * ==========================================
960     * WAIT FOR IMAGES
961     * ==========================================
962     */
963
964    window.addEventListener("load", function () {
965
966        setTimeout(function () {
967
968            initExpertHorizontalScroll();
969
970            ScrollTrigger.refresh();
971
972        }, 300);
973
974    });
975
976    /*
977     * ==========================================
978     * RESIZE
979     * ==========================================
980     */
981
982    let resizeTimer;
983
984    window.addEventListener("resize", function () {
985
986        clearTimeout(resizeTimer);
987
988        resizeTimer = setTimeout(function () {
989
990            initExpertHorizontalScroll();
991
992            ScrollTrigger.refresh();
993
994        }, 250);
995
996    });
997
998});
999//*************** Single - roundup post expert panel horizontally scroll ***************//
1000
1001
1002
1003// ####Soma####################################################################################################################################
1004// ########################################################################################################################################
1005// ########################################################################################################################################
1006// ########################################################################################################################################
1007
1008//*************** Home Page OurUsps Section ****************************
1009document.addEventListener("DOMContentLoaded", function () {
1010 // return;
1011 const section = document.querySelector(".our-usps-sec");
1012 
1013     if (!section) {
1014         return;
1015     }
1016    const track = section.closest(".our-usps-track") || section.parentElement;
1017
1018    if (
1019        typeof gsap === "undefined" ||
1020        typeof ScrollTrigger === "undefined"
1021    ) {
1022        return;
1023    }
1024
1025    gsap.registerPlugin(ScrollTrigger);
1026
1027    const rows = Array.from(
1028        section.querySelectorAll(".our-usps-container")
1029    );
1030
1031    if (!rows.length) {
1032        return;
1033    }
1034
1035    const items = rows.map(function (row, index) {
1036
1037        return {
1038            row: row,
1039            image: row.querySelector(".our-usps-img-col"),
1040            content: row.querySelector(".text-container"),
1041            icon: row.querySelector(".our-usps-img-svg"),
1042            index: index
1043        };
1044
1045    }).filter(function (item) {
1046
1047        return item.image && item.content;
1048
1049    });
1050
1051    if (!items.length) {
1052        return;
1053    }
1054
1055
1056    /*
1057    |--------------------------------------------------------------------------
1058    | INITIAL STATE
1059    |--------------------------------------------------------------------------
1060    */
1061
1062    items.forEach(function (item) {
1063
1064        gsap.set(item.row, {
1065            opacity: 1,
1066            zIndex: item.index + 1
1067        });
1068
1069        // IMAGE starts from LEFT
1070        gsap.set(item.image, {
1071            x: -120,
1072            y: 0,
1073            opacity: 0
1074        });
1075
1076        // CONTENT starts from RIGHT
1077        gsap.set(item.content, {
1078            x: 120,
1079            y: 0,
1080            opacity: 0
1081        });
1082
1083        // ICON starts from BOTTOM
1084        if (item.icon) {
1085            gsap.set(item.icon, {
1086                x: 0,
1087                y: 120,
1088                opacity: 0,
1089                rotation: 0
1090            });
1091        }
1092
1093    });
1094
1095
1096    /*
1097    |--------------------------------------------------------------------------
1098    | SHOW ITEM
1099    |--------------------------------------------------------------------------
1100    */
1101
1102    function showItem(timeline, item) {
1103
1104        // IMAGE — LEFT → CENTER
1105        timeline.to(item.image, {
1106            x: 0,
1107            y: 0,
1108            opacity: 1,
1109            duration: 1,
1110            ease: "power3.out"
1111        });
1112
1113        // CONTENT — RIGHT → CENTER
1114        timeline.to(item.content, {
1115            x: 0,
1116            y: 0,
1117            opacity: 1,
1118            duration: 1,
1119            ease: "power3.out"
1120        }, "<0.15");
1121
1122        // ICON — BOTTOM → CENTER
1123        if (item.icon) {
1124
1125            timeline.to(item.icon, {
1126                x: 0,
1127                y: 0,
1128                opacity: 1,
1129                rotation: 0,
1130                duration: 0.9,
1131                ease: "power3.out"
1132            }, "<0.15");
1133
1134        }
1135
1136    }
1137
1138
1139    /*
1140    |--------------------------------------------------------------------------
1141    | HIDE ITEM
1142    |--------------------------------------------------------------------------
1143    */
1144
1145    function hideItem(timeline, item) {
1146
1147        // IMAGE — CENTER → LEFT
1148        timeline.to(item.image, {
1149            x: -120,
1150            y: 0,
1151            opacity: 0,
1152            duration: 0.8,
1153            ease: "power2.inOut"
1154        });
1155
1156        // CONTENT — CENTER → RIGHT
1157        timeline.to(item.content, {
1158            x: 120,
1159            y: 0,
1160            opacity: 0,
1161            duration: 0.8,
1162            ease: "power2.inOut"
1163        }, "<");
1164
1165        // ICON — CENTER → BOTTOM
1166        if (item.icon) {
1167
1168            timeline.to(item.icon, {
1169                x: 0,
1170                y: 120,
1171                opacity: 0,
1172                rotation: 0,
1173                duration: 0.7,
1174                ease: "power2.inOut"
1175            }, "<");
1176
1177        }
1178
1179    }
1180
1181
1182    /*
1183    |--------------------------------------------------------------------------
1184    | FIRST ITEM — ALREADY VISIBLE
1185    |--------------------------------------------------------------------------
1186    */
1187
1188    const firstItem = items[0];
1189
1190    gsap.set(firstItem.row, {
1191        opacity: 1,
1192        zIndex: 10
1193    });
1194
1195    gsap.set(firstItem.image, {
1196        x: 0,
1197        y: 0,
1198        opacity: 1
1199    });
1200
1201    gsap.set(firstItem.content, {
1202        x: 0,
1203        y: 0,
1204        opacity: 1
1205    });
1206
1207    if (firstItem.icon) {
1208
1209        gsap.set(firstItem.icon, {
1210            x: 0,
1211            y: 0,
1212            opacity: 1,
1213            rotation: 0
1214        });
1215
1216    }
1217
1218
1219    /*
1220    |--------------------------------------------------------------------------
1221    | MAIN TIMELINE
1222    |--------------------------------------------------------------------------
1223    */
1224
1225    const tl = gsap.timeline({
1226
1227        scrollTrigger: {
1228            trigger: track,
1229            start: "top top",
1230            end: "bottom bottom",
1231            // pin: true,
1232            scrub: 1,
1233            // pinSpacing: true,
1234            // anticipatePin: 1,
1235            invalidateOnRefresh: true,
1236            markers:false
1237        }
1238
1239    });
1240
1241
1242    /*
1243    |--------------------------------------------------------------------------
1244    | NEXT ITEMS
1245    |--------------------------------------------------------------------------
1246    */
1247    const stopTimes = [0];
1248    for (let i = 1; i < items.length; i++) {
1249
1250        const previousItem = items[i - 1];
1251        const currentItem = items[i];
1252
1253
1254        /*
1255         * HOLD FIRST/CURRENT ITEM
1256         */
1257        tl.to({}, {
1258            duration: 1
1259        });
1260
1261
1262        /*
1263         * HIDE PREVIOUS
1264         */
1265        hideItem(tl, previousItem);
1266
1267
1268        /*
1269         * Move previous behind
1270         */
1271        tl.set(previousItem.row, {
1272            zIndex: 1
1273        });
1274
1275
1276        /*
1277         * Bring current row forward
1278         */
1279        tl.set(currentItem.row, {
1280            opacity: 1,
1281            zIndex: 10
1282        });
1283
1284
1285        /*
1286         * SHOW CURRENT
1287         */
1288        showItem(tl, currentItem);
1289        stopTimes.push(tl.duration());
1290    }
1291
1292
1293    /*
1294    |--------------------------------------------------------------------------
1295    | NAVIGATION DOTS
1296    |--------------------------------------------------------------------------
1297    */
1298
1299    const dotsWrap = document.createElement("div");
1300    dotsWrap.className = "usps-dots";
1301
1302    const dots = items.map(function (item, i) {
1303
1304        const dot = document.createElement("button");
1305
1306        dot.type = "button";
1307        dot.className = "usps-dot" + (i === 0 ? " is-active" : "");
1308        dot.setAttribute("aria-label", "Go to slide " + (i + 1));
1309
1310        dot.addEventListener("click", function () {
1311            scrollToIndex(i);
1312            // jQuery('.usps-dot').removeClass('is-active')
1313            // this.classList.add('is-active');
1314            // console.log(this);
1315        });
1316
1317        dotsWrap.appendChild(dot);
1318
1319        return dot;
1320
1321    });
1322
1323    section.appendChild(dotsWrap);
1324
1325
1326    function scrollToIndex(i) {
1327
1328        const st = tl.scrollTrigger;
1329        const total = tl.duration();
1330
1331        if (!st || !total) {
1332            return;
1333        }
1334
1335        // keep the last stop just inside the pin so it doesn't unpin
1336        const time = Math.min(stopTimes[i], total - 0.001);
1337        const y = st.start + (st.end - st.start) * (time / total);
1338
1339        window.scrollTo({
1340            top: y,
1341            behavior: "smooth"
1342        });
1343
1344    }
1345
1346
1347    function setActiveDot(progress) {
1348
1349        const total = tl.duration();
1350
1351        if (!total) {
1352            return;
1353        }
1354
1355        const time = progress * total;
1356
1357        let active = 0;
1358        let best = Infinity;
1359
1360        stopTimes.forEach(function (t, i) {
1361
1362            const distance = Math.abs(t - time);
1363
1364            if (distance < best) {
1365                best = distance;
1366                active = i;
1367            }
1368
1369        });
1370
1371        dots.forEach(function (dot, i) {
1372            dot.classList.toggle("is-active", i === active);
1373        });
1374
1375    }
1376
1377    tl.eventCallback("onUpdate", function () {
1378        setActiveDot(tl.progress());
1379    });
1380    ScrollTrigger.refresh();
1381
1382});
1383//*************** Home Page OurUsps Section ****************************
1384
1385//*************** Single - Thinking list image show on hover Section ****************************
1386jQuery(document).ready(function ($) {
1387
1388    let activeItem = null;
1389    let activeImage = null;
1390
1391    const imageMap = new Map();
1392
1393    const IMAGE_WIDTH = 280;
1394    const IMAGE_HEIGHT = 180;
1395    const GAP = 15;
1396
1397    function initializeItem(item) {
1398
1399        // Already initialized
1400        if (imageMap.has(item)) {
1401            return;
1402        }
1403
1404        const image = item.querySelector('.cursor-thumbnail');
1405
1406        if (!image) {
1407            return;
1408        }
1409
1410        imageMap.set(item, image);
1411
1412        document.body.appendChild(image);
1413
1414        image.style.position = 'fixed';
1415
1416        image.style.left = '0px';
1417        image.style.top = '0px';
1418
1419        image.style.width = IMAGE_WIDTH + 'px';
1420        image.style.height = IMAGE_HEIGHT + 'px';
1421
1422        image.style.zIndex = '999999';
1423
1424        image.style.pointerEvents = 'none';
1425
1426        image.style.overflow = 'hidden';
1427
1428        image.style.opacity = '0';
1429
1430        image.style.visibility = 'hidden';
1431
1432        image.style.transform = 'none';
1433
1434        image.style.translate = 'none';
1435
1436        const img = image.querySelector('img');
1437
1438        if (img) {
1439
1440            img.style.width = '196px';
1441            img.style.height = '124px';
1442
1443            img.style.display = 'block';
1444
1445            img.style.objectFit = 'cover';
1446        }
1447
1448    }
1449
1450    function initializeAllItems() {
1451
1452        const items = document.querySelectorAll(
1453            '.thinking-item'
1454        );
1455
1456        items.forEach(function (item) {
1457
1458            initializeItem(item);
1459
1460        });
1461
1462    }
1463
1464    initializeAllItems();
1465
1466    const observer = new MutationObserver(function (mutations) {
1467
1468        mutations.forEach(function (mutation) {
1469
1470            mutation.addedNodes.forEach(function (node) {
1471
1472                if (node.nodeType !== 1) {
1473                    return;
1474                }
1475
1476                if (
1477                    node.matches &&
1478                    node.matches('.thinking-item')
1479                ) {
1480
1481                    initializeItem(node);
1482
1483                }
1484
1485                if (node.querySelectorAll) {
1486
1487                    const newItems =
1488                        node.querySelectorAll(
1489                            '.thinking-item'
1490                        );
1491
1492                    newItems.forEach(function (item) {
1493
1494                        initializeItem(item);
1495
1496                    });
1497
1498                }
1499
1500            });
1501
1502        });
1503
1504    });
1505
1506    observer.observe(document.body, {
1507        childList: true,
1508        subtree: true
1509    });
1510
1511    document.addEventListener('mousemove', function (e) {
1512
1513        let currentItem = null;
1514        const items = document.querySelectorAll(
1515            '.thinking-item'
1516        );
1517
1518        items.forEach(function (item) {
1519
1520            const rect =
1521                item.getBoundingClientRect();
1522
1523
1524            if (
1525                e.clientX >= rect.left &&
1526                e.clientX <= rect.right &&
1527                e.clientY >= rect.top &&
1528                e.clientY <= rect.bottom
1529            ) {
1530
1531                currentItem = item;
1532
1533            }
1534
1535        });
1536
1537        if (currentItem !== activeItem) {
1538
1539            if (activeImage) {
1540
1541                activeImage.style.opacity = '0';
1542
1543                activeImage.style.visibility = 'hidden';
1544
1545            }
1546
1547            activeItem = currentItem;
1548
1549            if (!activeItem) {
1550
1551                activeImage = null;
1552
1553                return;
1554
1555            }
1556
1557            activeImage =
1558                imageMap.get(activeItem);
1559
1560            if (!activeImage) {
1561
1562                initializeItem(activeItem);
1563
1564                activeImage =
1565                    imageMap.get(activeItem);
1566
1567            }
1568
1569
1570            if (!activeImage) {
1571
1572                return;
1573
1574            }
1575
1576            activeImage.style.visibility =
1577                'visible';
1578
1579            activeImage.style.opacity =
1580                '1';
1581
1582        }
1583
1584        if (activeImage) {
1585
1586            let x =
1587                e.clientX + GAP;
1588
1589            let y =
1590                e.clientY + GAP;
1591
1592            if (
1593                x + IMAGE_WIDTH >
1594                window.innerWidth
1595            ) {
1596
1597                x =
1598                    e.clientX -
1599                    IMAGE_WIDTH -
1600                    GAP;
1601
1602            }
1603
1604            if (
1605                y + IMAGE_HEIGHT >
1606                window.innerHeight
1607            ) {
1608
1609                y =
1610                    e.clientY -
1611                    IMAGE_HEIGHT -
1612                    GAP;
1613
1614            }
1615
1616            if (x < 0) {
1617
1618                x = 10;
1619
1620            }
1621
1622            if (y < 0) {
1623
1624                y = 10;
1625
1626            }
1627
1628            activeImage.style.left =
1629                x + 'px';
1630
1631            activeImage.style.top =
1632                y + 'px';
1633
1634        }
1635
1636    });
1637
1638    document.addEventListener('mouseleave', function () {
1639
1640        if (activeImage) {
1641
1642            activeImage.style.opacity = '0';
1643
1644            activeImage.style.visibility = 'hidden';
1645
1646        }
1647
1648        activeItem = null;
1649
1650        activeImage = null;
1651
1652    });
1653
1654    window.addEventListener('scroll', function () {
1655
1656        if (activeImage) {
1657
1658            activeImage.style.opacity = '0';
1659
1660            activeImage.style.visibility = 'hidden';
1661
1662        }
1663
1664        activeItem = null;
1665
1666        activeImage = null;
1667
1668    }, {
1669        passive: true
1670    });
1671
1672        ScrollTrigger.refresh();
1673});
1674//*************** Single - Thinking list image show on hover Section ****************************
1675
1676//*************** MS and DS listical Page horizontal scroll ****************************
1677document.addEventListener("DOMContentLoaded", function () {
1678    gsap.registerPlugin(ScrollTrigger);
1679
1680    const section = document.querySelector(
1681        ".marketing-services-horizontal-sec"
1682    );
1683
1684    const slider = section?.querySelector(
1685        ".services-horizontal-slider"
1686    );
1687
1688    const wrapper = slider?.querySelector(
1689        ".swiper-wrapper"
1690    );
1691
1692    if (!section || !slider || !wrapper) {
1693        return;
1694    }
1695
1696    const triggerId =
1697        "marketingScroll-" +
1698        Math.random().toString(36).slice(2, 9);
1699
1700    let mobileSwiper = null;
1701    let desktopTween = null;
1702    let currentMode = null;
1703    let resizeTimer = null;
1704
1705
1706    /*
1707     * ==========================================
1708     * HELPERS
1709     * ==========================================
1710     */
1711
1712    // ✅ MODIFIED: renamed for clarity
1713    const isBelowBreakpoint = () => window.innerWidth < 991;
1714
1715    const getSlides = () =>
1716        Array.from(
1717            wrapper.querySelectorAll(".swiper-slide")
1718        );
1719
1720
1721    /*
1722     * ==========================================
1723     * CLEANUP GSAP (kill horizontal scroll)
1724     * ==========================================
1725     */
1726
1727    function cleanupDesktop() {
1728
1729        if (desktopTween) {
1730            desktopTween.kill();
1731            desktopTween = null;
1732        }
1733
1734        const trigger =
1735            ScrollTrigger.getById(triggerId);
1736
1737        if (trigger) {
1738            trigger.kill(true);
1739        }
1740
1741        // ✅ ADDED: kill ALL ScrollTriggers on this section
1742        ScrollTrigger.getAll().forEach(function (st) {
1743            if (st.vars.trigger === section) {
1744                st.kill(true);
1745            }
1746        });
1747
1748        gsap.killTweensOf(wrapper);
1749
1750        gsap.set(wrapper, {
1751            clearProps: "all"
1752        });
1753
1754        section.style.removeProperty("height");
1755        section.style.removeProperty("min-height");
1756        section.style.removeProperty("display");
1757        section.style.removeProperty("align-items");
1758
1759        // ✅ ADDED: clear wrapper inline styles GSAP may leave behind
1760        wrapper.style.removeProperty("transform");
1761        wrapper.style.removeProperty("width");
1762        wrapper.style.removeProperty("gap");
1763        wrapper.style.removeProperty("padding-right");
1764
1765        getSlides().forEach(function (slide) {
1766            [
1767                "width",
1768                "flex",
1769                "filter",
1770                "opacity",
1771                "visibility",
1772                "transform",
1773                "z-index"
1774            ].forEach(function (property) {
1775                slide.style.removeProperty(property);
1776            });
1777        });
1778
1779        // ✅ ADDED: remove pin-spacer div GSAP leaves in the DOM
1780        const pinSpacer = section.parentElement;
1781        if (
1782            pinSpacer &&
1783            pinSpacer.classList.contains("pin-spacer")
1784        ) {
1785            pinSpacer.parentElement.insertBefore(
1786                section,
1787                pinSpacer
1788            );
1789            pinSpacer.remove();
1790        }
1791    }
1792
1793
1794    /*
1795     * ==========================================
1796     * DESTROY MOBILE SWIPER
1797     * ==========================================
1798     */
1799
1800    /*
1801    * ==========================================
1802    * DESTROY MOBILE SWIPER
1803    * ==========================================
1804    */
1805
1806 function destroyMobile() {
1807
1808    if (!mobileSwiper) {
1809        return;
1810    }
1811
1812    mobileSwiper.destroy(true, true);
1813    mobileSwiper = null;
1814
1815    // ✅ Remove Swiper duplicate slides FIRST (before cleaning styles)
1816    wrapper
1817        .querySelectorAll(".swiper-slide-duplicate")
1818        .forEach(function (dup) {
1819            dup.remove();
1820        });
1821
1822    // ✅ Strip ALL inline styles Swiper sets on wrapper
1823    wrapper.removeAttribute("style");
1824
1825    // ✅ Strip ALL inline styles Swiper sets on slider
1826    slider.removeAttribute("style");
1827
1828    // ✅ Remove Swiper classes from slider
1829    slider.classList.remove(
1830        "swiper-initialized",
1831        "swiper-horizontal",
1832        "swiper-vertical",
1833        "swiper-backface-hidden"
1834    );
1835
1836    // ✅ Clean every slide completely
1837    getSlides().forEach(function (slide) {
1838
1839        // Remove ALL inline styles (width, margin-right, etc.)
1840        slide.removeAttribute("style");
1841
1842        slide.classList.remove(
1843            "swiper-slide-active",
1844            "swiper-slide-next",
1845            "swiper-slide-prev",
1846            "swiper-slide-duplicate",
1847            "swiper-slide-duplicate-active",
1848            "swiper-slide-duplicate-next",
1849            "swiper-slide-duplicate-prev",
1850            "swiper-slide-visible",
1851            "merk-demd-slider"
1852        );
1853
1854        // ✅ Remove Swiper data attributes
1855        slide.removeAttribute("data-swiper-slide-index");
1856        slide.removeAttribute("role");
1857        slide.removeAttribute("aria-label");
1858    });
1859 }
1860
1861
1862    /*
1863     * ==========================================
1864     * MOBILE SWIPER (below 991)
1865     * ==========================================
1866     */
1867
1868    function createMobile() {
1869
1870        // ✅ First: kill GSAP completely
1871        cleanupDesktop();
1872
1873        getSlides().forEach(function (slide, index) {
1874            slide.classList.remove("merk-demd-slider");
1875
1876            if (index % 2 === 0) {
1877                slide.classList.add("merk-demd-slider");
1878            }
1879        });
1880
1881        mobileSwiper = new Swiper(slider, {
1882
1883            slidesPerView: 1.5,
1884            spaceBetween: 76,
1885            breakpoints: {
1886                    500: {
1887                        slidesPerView: 2.5,
1888                        spaceBetween: 70
1889                    },
1890                    768: {
1891                        slidesPerView: 3.5,
1892                        spaceBetween: 70
1893                    }
1894                },
1895            loop: true,
1896            centeredSlides: false,
1897
1898            slidesOffsetBefore: 0,
1899            slidesOffsetAfter: 0,
1900
1901            speed: 700,
1902
1903            autoplay: {
1904                delay: 3000,
1905                disableOnInteraction: false,
1906                pauseOnMouseEnter: false
1907            },
1908
1909            allowTouchMove: true,
1910
1911            resistance: true,
1912            resistanceRatio: 0,
1913
1914            watchOverflow: false,
1915
1916            observer: true,
1917            observeParents: true,
1918
1919            on: {
1920                init: function () {
1921                    this.update();
1922                    this.autoplay.start();
1923                }
1924            }
1925
1926        });
1927
1928        currentMode = "mobile";
1929    }
1930
1931
1932    /*
1933     * ==========================================
1934     * DESKTOP GSAP (above 991)
1935     * ==========================================
1936     */
1937
1938    function createDesktop() {
1939
1940        // ✅ First: kill Swiper completely
1941        destroyMobile();
1942
1943        // ✅ Then: clean any stale GSAP state
1944        cleanupDesktop();
1945
1946        const slides = getSlides();
1947
1948        const sliderWidth = slider.clientWidth;
1949
1950        const columns =
1951            window.innerWidth < 1024
1952                ? 2.5
1953                : 3.5;
1954
1955        const slideWidth = sliderWidth / columns;
1956
1957        // slides.forEach(function (slide) {
1958        //     slide.style.width = slideWidth + "px";
1959        //     slide.style.flex = "0 0 " + slideWidth + "px";
1960        // });
1961
1962        wrapper.style.gap = "0";
1963        wrapper.style.paddingRight = "0";
1964
1965        // Force layout recalculation
1966        void wrapper.offsetWidth;
1967
1968        const scrollDistance =
1969            Math.max(
1970                0,
1971                wrapper.scrollWidth - slider.clientWidth
1972            );
1973
1974        if (scrollDistance <= 0) {
1975            currentMode = "desktop";
1976            return;
1977        }
1978
1979        const header = document.querySelector("header");
1980        const headerHeight = header?.offsetHeight || 0;
1981
1982        if (window.innerWidth < 1024) {
1983            Object.assign(section.style, {
1984                height: "100vh",
1985                minHeight: "100vh",
1986                display: "flex",
1987                alignItems: "center"
1988            });
1989        }
1990
1991        desktopTween = gsap.to(wrapper, {
1992            x: -scrollDistance,
1993            ease: "none",
1994            scrollTrigger: {
1995                id: triggerId,
1996                trigger: section,
1997                start: "top " + headerHeight + "px",
1998                end: "+=" + scrollDistance,
1999                pin: true,
2000                pinSpacing: true,
2001                scrub: 1,
2002                anticipatePin: 1,
2003                invalidateOnRefresh: true
2004            }
2005        });
2006
2007        currentMode = "desktop";
2008    }
2009
2010
2011    /*
2012     * ==========================================
2013     * INITIALIZE (mode router)
2014     * ==========================================
2015     */
2016
2017    function initialize() {
2018
2019        var newMode = isBelowBreakpoint()
2020            ? "mobile"
2021            : "desktop";
2022
2023        // ✅ MODIFIED: same mode — just refresh, don't rebuild
2024        if (currentMode === newMode) {
2025
2026            if (newMode === "mobile" && mobileSwiper) {
2027                mobileSwiper.update();
2028            }
2029
2030            if (newMode === "desktop") {
2031                ScrollTrigger.refresh();
2032            }
2033
2034            return;
2035        }
2036
2037        // ✅ MODIFIED: always tear down BOTH before switching
2038        destroyMobile();
2039        cleanupDesktop();
2040
2041        if (newMode === "mobile") {
2042            createMobile();
2043        } else {
2044            createDesktop();
2045        }
2046    }
2047
2048
2049    /*
2050     * ==========================================
2051     * INITIAL LOAD
2052     * ==========================================
2053     */
2054
2055    function initAfterLoad() {
2056        requestAnimationFrame(function () {
2057            requestAnimationFrame(function () {
2058                initialize();
2059            });
2060        });
2061    }
2062
2063    if (document.readyState === "complete") {
2064        initAfterLoad();
2065    } else {
2066        window.addEventListener(
2067            "load",
2068            initAfterLoad,
2069            { once: true }
2070        );
2071    }
2072
2073
2074    /*
2075     * ==========================================
2076     * RESPONSIVE RESIZE
2077     * ==========================================
2078     */
2079
2080    window.addEventListener(
2081        "resize",
2082        function () {
2083
2084            clearTimeout(resizeTimer);
2085
2086            resizeTimer = setTimeout(
2087                function () {
2088
2089                    var newMode = isBelowBreakpoint()
2090                        ? "mobile"
2091                        : "desktop";
2092
2093                    // Breakpoint crossed — full rebuild
2094                    if (newMode !== currentMode) {
2095                        currentMode = null;
2096                        initialize();
2097                        return;
2098                    }
2099
2100                    // Same mode — lightweight refresh
2101                    if (
2102                        newMode === "mobile" &&
2103                        mobileSwiper
2104                    ) {
2105                        mobileSwiper.update();
2106                    } else if (newMode === "desktop") {
2107                        // ✅ ADDED: recalculate slide widths on resize
2108                        createDesktop();
2109                    }
2110
2111                },
2112                250
2113            );
2114
2115        }
2116    );
2117
2118});
2119//*************** MS and DS listical Page horizontal scroll ****************************
2120
2121//***************home page recognised sec*************//
2122document.addEventListener("DOMContentLoaded", function () {
2123
2124    if (typeof Swiper === "undefined") return;
2125
2126    document.querySelectorAll(".recognised-grid").forEach(function (grid) {
2127
2128        if (window.innerWidth > 767) return;
2129
2130        const slides = [...grid.children];
2131
2132        grid.classList.remove(
2133            "is-layout-grid",
2134            "wp-container-core-group-is-layout-9d260ee2"
2135        );
2136
2137        grid.classList.add("swiper");
2138
2139        const wrapper = document.createElement("div");
2140        wrapper.className = "swiper-wrapper";
2141
2142        slides.forEach(function (slide) {
2143            slide.classList.add("swiper-slide");
2144            wrapper.appendChild(slide);
2145        });
2146
2147        grid.appendChild(wrapper);
2148
2149        const pagination = document.createElement("div");
2150        pagination.className = "recognised-pagination";
2151        grid.parentNode.appendChild(pagination);
2152
2153        new Swiper(grid, {
2154            slidesPerView: 1,
2155            loop: true,
2156            speed: 800,
2157            autoplay: {
2158                delay: 2000,
2159                disableOnInteraction: false
2160            },
2161            pagination: {
2162                el: pagination,
2163                clickable: true
2164            }
2165        });
2166
2167    });
2168});
2169//***************home page recognised sec*************//
2170
2171// ####Gopi####################################################################################################################################
2172// ########################################################################################################################################
2173// ########################################################################################################################################
2174// ########################################################################################################################################
2175// ########################################################################################################################################
2176
2177//*************** Testimonial section Icon drop animation section ****************************
2178document.addEventListener("DOMContentLoaded", function () {
2179    /*
2180    ============================================================
2181    GSAP CHECK
2182    ============================================================
2183    */
2184 //    return;
2185    if (
2186        typeof gsap === "undefined" ||
2187        typeof ScrollTrigger === "undefined"
2188    ) {
2189        console.warn("GSAP / ScrollTrigger is not loaded.");
2190        return;
2191    }
2192    gsap.registerPlugin(ScrollTrigger);
2193   
2194    const icons = gsap.utils.toArray(".cilent-icon");
2195    if (!icons.length) {
2196        return;
2197    }
2198    
2199    const iconSettings = [
2200        {
2201            selector: ".cilent-greenflower-des",
2202            drop: -500,
2203            rotation: -360,
2204            duration: 1.2
2205        },
2206        {
2207            selector: ".cilent-pink-des",
2208            drop: -450,
2209            rotation: 0,
2210            duration: 1
2211        },
2212        {
2213            selector: ".cilent-lightblue-des",
2214            drop: -500,
2215            rotation: 360,
2216            duration: 1.1
2217        },
2218        {
2219            selector: ".cilent-fourcricles-des",
2220            drop: -600,
2221            rotation: 0,
2222            duration: 1.3
2223        },
2224        {
2225            selector: ".cilent-love-des",
2226            drop: -450,
2227            rotation: -180,
2228            duration: 1
2229        },
2230        {
2231            selector: ".cilent-green-des",
2232            drop: -900,
2233            rotation: 0,
2234            duration: 1.1
2235        }
2236    ];
2237    /*
2238    ============================================================
2239    INITIAL STATE
2240    ============================================================
2241    */
2242    iconSettings.forEach(function (item) {
2243        const icon = document.querySelector(item.selector);
2244        if (!icon) return;
2245        gsap.set(icon, {
2246            y: item.drop,
2247            opacity: 0,
2248            rotation: item.rotation,
2249            scale: 0.8,
2250            transformOrigin: "50% 50%"
2251        });
2252    });
2253    /*
2254    ============================================================
2255    ENTRANCE ANIMATION
2256    ============================================================
2257    */
2258    const entranceTimeline = gsap.timeline({
2259        scrollTrigger: {
2260            
2261            trigger: ".client-testimonial-sec",
2262            start: "top top",
2263            toggleActions: "play none none reverse",
2264            markers: false,
2265            stagger:1,
2266            scrub:false
2267        }
2268    });
2269    iconSettings.forEach(function (item, index) {
2270        const icon = document.querySelector(item.selector);
2271        if (!icon) return;
2272        entranceTimeline.to(
2273            icon,
2274            {
2275                y: 0,
2276                opacity: 1,
2277                rotation: 0,
2278                scale: 1,
2279                duration: item.duration,
2280                ease: "back.out(1.7)"
2281            },
2282            /*
2283            One after another
2284            */
2285            index * 0.12
2286        );
2287    });
2288  
2289    const idleAnimations = [];
2290    entranceTimeline.eventCallback(
2291        "onComplete",
2292        function () {
2293            icons.forEach(function (icon, index) {
2294                const direction =
2295                    index % 2 === 0 ? 1 : -1;
2296                const idle = gsap.to(icon, {
2297                    y: "+=" + (5 + (index % 3) * 3),
2298                    rotation:
2299                        "+=" + (direction * (2 + (index % 3))),
2300                    duration:
2301                        4 + (index % 3),
2302                    ease: "sine.inOut",
2303                    yoyo: true,
2304                    repeat: -1
2305                });
2306                idleAnimations.push(idle);
2307            });
2308        }
2309    );
2310    
2311    const RADIUS = 220;
2312    
2313    const quickAnimations = icons.map(function (icon) {
2314        return {
2315            x: gsap.quickTo(
2316                icon,
2317                "x",
2318                {
2319                    duration: 0.45,
2320                    ease: "power3.out"
2321                }
2322            ),
2323            y: gsap.quickTo(
2324                icon,
2325                "y",
2326                {
2327                    duration: 0.45,
2328                    ease: "power3.out"
2329                }
2330            ),
2331            rotation: gsap.quickTo(
2332                icon,
2333                "rotation",
2334                {
2335                    duration: 0.5,
2336                    ease: "power3.out"
2337                }
2338            ),
2339            scale: gsap.quickTo(
2340                icon,
2341                "scale",
2342                {
2343                    duration: 0.4,
2344                    ease: "power3.out"
2345                }
2346            )
2347        };
2348    });
2349    
2350    let raf = null;
2351    const pointer = {
2352        x: -9999,
2353        y: -9999
2354    };
2355    
2356    function applyMouseEffect() {
2357        raf = null;
2358        icons.forEach(function (icon, index) {
2359            const rect =
2360                icon.getBoundingClientRect();
2361            /*
2362            Icon center
2363            */
2364            const iconX =
2365                rect.left +
2366                rect.width / 2;
2367            const iconY =
2368                rect.top +
2369                rect.height / 2;
2370           
2371            const dx =
2372                iconX - pointer.x;
2373            const dy =
2374                iconY - pointer.y;
2375            const distance =
2376                Math.sqrt(
2377                    dx * dx +
2378                    dy * dy
2379                );
2380            /*
2381            Mouse is close to icon
2382            */
2383            if (distance < RADIUS) {
2384                /*
2385                1 = very close
2386                0 = edge of radius
2387                */
2388                const force =
2389                    1 - distance / RADIUS;
2390                /*
2391                Direction away from mouse
2392                */
2393                const angle =
2394                    Math.atan2(dy, dx);
2395                /*
2396                Movement strength
2397                */
2398                const moveX =
2399                    Math.cos(angle) *
2400                    force *
2401                    35;
2402                const moveY =
2403                    Math.sin(angle) *
2404                    force *
2405                    25;
2406                /*
2407                Move icon
2408                */
2409                quickAnimations[index].x(
2410                    moveX
2411                );
2412                quickAnimations[index].y(
2413                    moveY
2414                );
2415                /*
2416                Small shake/rotation
2417                */
2418                quickAnimations[index].rotation(
2419                    force *
2420                    (index % 2 === 0 ? 5 : -5)
2421                );
2422                /*
2423                Slight scale
2424                */
2425                quickAnimations[index].scale(
2426                    1 + force * 0.08
2427                );
2428            } else {
2429                /*
2430                Return to original position
2431                */
2432                quickAnimations[index].x(0);
2433                quickAnimations[index].y(0);
2434                quickAnimations[index].rotation(0);
2435                quickAnimations[index].scale(1);
2436            }
2437        });
2438    }
2439    /*
2440    ============================================================
2441    MOUSE MOVE
2442    ============================================================
2443    */
2444    function onMouseMove(event) {
2445        // console.log(event);
2446        pointer.x =
2447            event.clientX;
2448        pointer.y =
2449            event.clientY;
2450        /*
2451        Only request one animation frame
2452        */
2453        if (!raf) {
2454            raf =
2455                requestAnimationFrame(
2456                    applyMouseEffect
2457                );
2458        }
2459    }
2460    
2461    const section =
2462        document.querySelector(
2463            ".client-quotes__bg"
2464        ) ||
2465        document.querySelector(
2466            ".cilent-icon"
2467        )?.parentElement;
2468    let listening = false;
2469    function setMouseListener(enabled) {
2470        if (enabled === listening) {
2471            return;
2472        }
2473        listening = enabled;
2474        if (enabled) {
2475            window.addEventListener(
2476                "mousemove",
2477                onMouseMove,
2478                {
2479                    passive: true
2480                }
2481            );
2482        } else {
2483            window.removeEventListener(
2484                "mousemove",
2485                onMouseMove
2486            );
2487            /*
2488            Move pointer outside the active area
2489            */
2490            pointer.x = -9999;
2491            pointer.y = -9999;
2492            if (!raf) {
2493                raf =
2494                    requestAnimationFrame(
2495                        applyMouseEffect
2496                    );
2497            }
2498        }
2499    }
2500    
2501    if (
2502        section &&
2503        "IntersectionObserver" in window
2504    ) {
2505        const observer =
2506            new IntersectionObserver(
2507                function (entries) {
2508                    setMouseListener(
2509                        entries[0].isIntersecting
2510                    );
2511                },
2512                {
2513                    rootMargin: "150px"
2514                }
2515            );
2516        observer.observe(section);
2517    } else {
2518        setMouseListener(true);
2519    }
2520    /*
2521    ============================================================
2522    PAUSE IDLE ANIMATION WHEN TAB IS HIDDEN
2523    ============================================================
2524    */
2525    document.addEventListener(
2526        "visibilitychange",
2527        function () {
2528            idleAnimations.forEach(
2529                function (animation) {
2530                    if (document.hidden) {
2531                        animation.pause();
2532                    } else {
2533                        animation.resume();
2534                    }
2535                }
2536            );
2537        }
2538    );
2539    /*
2540    ============================================================
2541    REFRESH AFTER PAGE LOAD
2542    ============================================================
2543    */
2544    window.addEventListener(
2545        "load",
2546        function () {
2547            ScrollTrigger.refresh();
2548        }
2549    );
2550     ScrollTrigger.refresh();
2551 });
2552//*************** Testimonial section Icon drop animation section ****************************
2553
2554//*************** About Us Cultural section ****************************
2555document.addEventListener("DOMContentLoaded", function () {
2556
2557    const track = document.querySelector(".cultural-sec-track");
2558    const section = document.querySelector(".cultural-sec");
2559    const heading = document.querySelector(".company-culture-row h2");
2560    if (!track || !section) {
2561        return;
2562    }
2563    const culturalScroll = gsap.to(section, {
2564        x: () => {
2565            return -(section.scrollWidth - window.innerWidth);
2566        },
2567
2568        ease: "none",
2569        scrollTrigger: {
2570            trigger: track,
2571            pin: ".cultural-sec-wrapper",
2572            start: "bottom bottom",
2573            end: () => {
2574                return `+=${section.scrollWidth - window.innerWidth}`;
2575            },
2576            scrub: 2,
2577             anticipatePin: 1,
2578            markers: false,
2579            invalidateOnRefresh: true
2580        }
2581    });
2582    if (window.innerWidth >= 991) {
2583        gsap.to(heading, {
2584        y: -300,
2585        opacity:0,
2586        ease: "none",
2587
2588        scrollTrigger: {
2589            trigger: track,
2590            start: "bottom bottom",
2591            end: () => {
2592                return `+=${section.scrollWidth - window.innerWidth}`;
2593            },
2594            scrub: 0.5,
2595            invalidateOnRefresh: true
2596        }
2597        });
2598    }
2599    window.addEventListener("load", function () {
2600        ScrollTrigger.refresh();
2601    });
2602});
2603//***************About us cultural section ****************************
2604
2605//*************** Map section ****************************
2606document.addEventListener("DOMContentLoaded", (event) => {
2607    // return;
2608    function rotateGlobeScroll() {
2609        let mapSection = document.querySelector(".map-section");
2610        if (!mapSection) return;
2611        let svgImage = mapSection.querySelector(".map-svg-container svg");
2612        let locationPaths = svgImage.querySelectorAll(".location");
2613        gsap.from(".map-section .map-container", {
2614            y: 200,
2615            opacity: 0,
2616            scrollTrigger: {
2617                trigger: mapSection,
2618                start: "top 90%",
2619                scrub: true,
2620            },
2621        });
2622        ScrollTrigger.matchMedia({
2623            // Tab
2624            "(max-width: 1099px)": function () {
2625                let tl = gsap.timeline({
2626                    scrollTrigger: {
2627                        trigger: mapSection,
2628                        start: "top 80%",
2629                        end: "+=50%",
2630                        pin: false,
2631                        pinSpacing: false,
2632                        scrub: true,
2633                        stagger: 0.5,
2634                        markers: false,
2635                    },
2636                });
2637                tl.from(svgImage, { opacity: 0 });
2638                tl.from(
2639                    svgImage,
2640                    {
2641                        rotate: -90,
2642                        transformOrigin: "50% 50%",
2643                        ease: "power3.inOut",
2644                        duration: 2,
2645                    },
2646                    1,
2647                );
2648                tl.from(
2649                    locationPaths,
2650                    {
2651                        y: -200,
2652                        opacity: 0,
2653                        ease: "power3.out",
2654                        duration: 2,
2655                        stagger: 0.7,
2656                    },
2657                    2,
2658                );
2659            },
2660
2661            // Desktop
2662            "(min-width: 1100px)": function () {
2663                let tl = gsap.timeline({
2664                    scrollTrigger: {
2665                        trigger: ".map-section",
2666                        start: "top top",
2667                        end: "+=200%",
2668                        pin: true,
2669                        pinSpacing: true,
2670                        anticipatePin: 1,
2671                        scrub: true,
2672                        stagger: 0.5,
2673                        markers: false,
2674                    },
2675                });
2676                
2677                tl.from(svgImage, { opacity: 0 });
2678                tl.from(
2679                    svgImage,
2680                    {
2681                        rotate: -90,
2682                        transformOrigin: "50% 50%",
2683                        ease: "power3.inOut",
2684                        duration: 2,
2685                    },
2686                    1,
2687                );
2688                tl.from(
2689                    locationPaths,
2690                    {
2691                        y: -200,
2692                        opacity: 0,
2693                        ease: "power3.out",
2694                        duration: 2,
2695                        stagger: 0.7,
2696                    },
2697                    2,
2698                );
2699            },
2700        });
2701    }
2702    rotateGlobeScroll();
2703      ScrollTrigger.refresh();
2704})
2705//*************** Map section ****************************
2706
2707//*************** section next to map section revel ****************************
2708document.addEventListener("DOMContentLoaded", () => {
2709  const pinSection = document.querySelector(".map-client-scroll-sec");
2710  if (!pinSection) return;
2711
2712  ScrollTrigger.matchMedia({
2713    "(min-width: 1100px)": function () {
2714      gsap.set(pinSection, { marginTop: "-100vh" });
2715
2716      const tl = gsap.timeline({
2717        scrollTrigger: {
2718          trigger: pinSection,
2719          start: "top top",     
2720          end: "+=100%",       
2721          pin: true,
2722          pinSpacing: true,
2723          anticipatePin: 1,
2724          scrub: 1,
2725          markers: false,       
2726        },
2727      });
2728    }
2729  });
2730});
2731//*************** section next to map section revel ****************************
2732
2733// #####Work Module ###################################################################################################################################
2734// ########################################################################################################################################
2735// ########################################################################################################################################
2736// ########################################################################################################################################
2737
2738//*************** card shuffle on scroll ****************************
2739document.addEventListener('DOMContentLoaded', function () {
2740
2741    //*************** Home, MS and DS listical Page card shuffle on scroll ****************************
2742    (function () {
2743
2744    if (!document.body.querySelector('#case-study-work-results')) {
2745
2746    var prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
2747    if (prefersReducedMotion) return;
2748
2749    var sections = Array.prototype.slice.call(document.querySelectorAll('.ub-work'));
2750    if (!sections.length) return;
2751
2752    if (window.innerWidth < 767) {
2753        mobileInit(sections);
2754    } else {
2755        desktopInit(sections);
2756    }
2757
2758    /* ==============================================================
2759        MOBILE – auto-scrolling infinite carousel (logic only, no CSS)
2760        ============================================================== */
2761    function mobileInit(sections) {
2762        sections.forEach(setupMobileCarousel);
2763    }
2764
2765    function setupMobileCarousel(section) {
2766        var cards = Array.prototype.slice.call(section.querySelectorAll('.ub-work__card'));
2767        if (!cards.length) return;
2768
2769        var content = section.querySelector('.ub-work__content');
2770        var pin     = section.querySelector('.ub-work__pin');
2771
2772        // Clear any stale inline styles from desktop logic
2773        if (content) content.style.cssText = '';
2774        cards.forEach(function (card) {
2775        var bg = card.getAttribute('style') || '';
2776        card.style.cssText = bg;
2777        });
2778
2779        // --- Build carousel DOM ---
2780        var viewport = document.createElement('div');
2781        viewport.className = 'ub-work-mob-viewport';
2782
2783        var track = document.createElement('div');
2784        track.className = 'ub-work-mob-track';
2785
2786        // Move original cards into track
2787        cards.forEach(function (card) { track.appendChild(card); });
2788
2789        // Clone all cards for seamless infinite loop
2790        cards.forEach(function (card) {
2791        var clone = card.cloneNode(true);
2792        clone.setAttribute('data-clone', 'true');
2793        clone.setAttribute('aria-hidden', 'true');
2794        track.appendChild(clone);
2795        });
2796
2797        viewport.appendChild(track);
2798        pin.appendChild(viewport);
2799
2800        // --- Dots ---
2801        var dotsWrap = document.createElement('div');
2802        dotsWrap.className = 'ub-work-mob-dots';
2803        cards.forEach(function (_, i) {
2804        var dot = document.createElement('button');
2805        dot.className = 'ub-work-mob-dot' + (i === 0 ? ' active' : '');
2806        dot.setAttribute('aria-label', 'Go to slide ' + (i + 1));
2807        dot.addEventListener('click', function () {
2808            goTo(i, true);
2809            restartAuto();
2810        });
2811        dotsWrap.appendChild(dot);
2812        });
2813        viewport.after(dotsWrap);
2814
2815        var dots = Array.prototype.slice.call(dotsWrap.querySelectorAll('.ub-work-mob-dot'));
2816
2817        // --- State ---
2818        var current       = 0;
2819        var total         = cards.length;
2820        var autoTimer     = null;
2821        var transitioning = false;
2822        var AUTO_DELAY    = 3500;
2823        var TRANS_MS      = 700;
2824
2825        function cardStep() {
2826        var c = track.children[0];
2827        if (!c) return window.innerWidth;
2828        var gap = parseFloat(getComputedStyle(track).gap) || 0;
2829        return c.offsetWidth + gap;
2830        }
2831
2832        function updateDots(activeIndex) {
2833        dots.forEach(function (d, i) {
2834            d.classList.toggle('active', i === activeIndex);
2835        });
2836        }
2837
2838        function slideTo(index, animated) {
2839        if (animated) {
2840            track.style.transition = 'transform ' + TRANS_MS + 'ms cubic-bezier(.4,0,.2,1)';
2841            transitioning = true;
2842        } else {
2843            track.style.transition = 'none';
2844            transitioning = false;
2845        }
2846        track.style.transform = 'translateX(' + (-index * cardStep()) + 'px)';
2847        updateDots(((index % total) + total) % total);
2848        }
2849
2850        track.addEventListener('transitionend', function () {
2851        transitioning = false;
2852        if (current >= total) {
2853            current = current % total;
2854            slideTo(current, false);
2855        }
2856        });
2857
2858        function goTo(index, animated) {
2859        if (transitioning) return;
2860        current = index;
2861        slideTo(current, animated);
2862        }
2863
2864        function advance() {
2865        if (transitioning) return;
2866        current++;
2867        slideTo(current, true);
2868        }
2869
2870        function startAuto() { stopAuto(); autoTimer = setInterval(advance, AUTO_DELAY); }
2871        function stopAuto()  { if (autoTimer) { clearInterval(autoTimer); autoTimer = null; } }
2872        function restartAuto() { stopAuto(); startAuto(); }
2873
2874        // --- Touch / swipe ---
2875        var touchStartX = 0;
2876        var touchDiffX  = 0;
2877        var swiping     = false;
2878        var dragOffset  = 0;
2879
2880        viewport.addEventListener('touchstart', function (e) {
2881        if (transitioning) return;
2882        stopAuto();
2883        touchStartX = e.touches[0].clientX;
2884        touchDiffX  = 0;
2885        swiping     = false;
2886        track.style.transition = 'none';
2887        }, { passive: true });
2888
2889        viewport.addEventListener('touchmove', function (e) {
2890        if (transitioning) return;
2891        touchDiffX = e.touches[0].clientX - touchStartX;
2892        if (!swiping && Math.abs(touchDiffX) > 8) swiping = true;
2893        if (swiping) {
2894            dragOffset = touchDiffX;
2895            track.style.transform = 'translateX(' + (-current * cardStep() + dragOffset) + 'px)';
2896        }
2897        }, { passive: true });
2898
2899        viewport.addEventListener('touchend', function () {
2900        if (swiping && !transitioning) {
2901            var threshold = cardStep() * 0.2;
2902            if (dragOffset < -threshold) {
2903            current++;
2904            if (current >= total * 2) current = total;
2905            } else if (dragOffset > threshold && current > 0) {
2906            current--;
2907            }
2908            slideTo(current, true);
2909        }
2910        dragOffset = 0;
2911        swiping = false;
2912        restartAuto();
2913        });
2914
2915        // Pause when tab hidden
2916        document.addEventListener('visibilitychange', function () {
2917        if (document.hidden) stopAuto(); else startAuto();
2918        });
2919
2920        // Recalc on resize while still mobile
2921        window.addEventListener('resize', function () {
2922        if (window.innerWidth >= 767) return;
2923        slideTo(current, false);
2924        });
2925
2926        // --- Init ---
2927        slideTo(0, false);
2928        startAuto();
2929    }
2930
2931
2932    /* ==============================================================
2933        DESKTOP – original scroll-driven card-stack (unchanged)
2934        ============================================================== */
2935    function desktopInit(sections) {
2936
2937        var CARD_START_RATIO = 5 / 4;
2938        var CARD_END_RATIO = 16 / 9;
2939        var OVERLAPPED_SCALE = 0.85;
2940        var CONTENT_EXIT_MULTIPLIER = 1.3;
2941        var CARD0_VISIBLE_FRACTION = 2 / 3;
2942        var ACTIVE_FRACTION = 0.5;
2943
2944        var instances = sections.map(function (section) {
2945        var cards = Array.prototype.slice.call(section.querySelectorAll('.ub-work__card'));
2946        cards.forEach(function (card, i) { card.style.setProperty('--card-z', String(i + 1)); });
2947        return {
2948            section: section,
2949            content: section.querySelector('.ub-work__content'),
2950            cards: cards,
2951            cardCount: cards.length,
2952            card0RestOffsetX: null,
2953            lastSegment: -1,
2954            dots: null,
2955            dotsWrap: null
2956        };
2957        });
2958
2959        function measureCard0Offset(instance) {
2960        if (!instance.cards.length) return;
2961        var card0 = instance.cards[0];
2962        var previousAspect = card0.style.aspectRatio;
2963        card0.style.aspectRatio = String(CARD_START_RATIO);
2964        var cardWidth = card0.offsetWidth;
2965        card0.style.aspectRatio = previousAspect;
2966        var restLeftEdge = window.innerWidth - cardWidth * CARD0_VISIBLE_FRACTION;
2967        var centeredLeftEdge = (window.innerWidth - cardWidth) / 2;
2968        instance.card0RestOffsetX = restLeftEdge - centeredLeftEdge;
2969        }
2970
2971        function buildDots(instance) {
2972        if (instance.cardCount < 2) return;
2973
2974        var host = instance.section.querySelector('.ub-work__pin') || instance.section;
2975
2976        var wrap = document.createElement('div');
2977        wrap.className = 'ub-work-dots';
2978
2979        instance.cards.forEach(function (_, i) {
2980            var dot = document.createElement('button');
2981            dot.type = 'button';
2982            dot.className = 'ub-work-dot' + (i === 0 ? ' active' : '');
2983            dot.setAttribute('aria-label', 'Go to card ' + (i + 1));
2984            dot.setAttribute('data-index', String(i));
2985            dot.setAttribute('data-active', i === 0 ? 'true' : 'false');
2986            dot.addEventListener('click', function () {
2987            // updateDots(instance, i + 0.5);   // paint immediately
2988            scrollToCard(instance, i);
2989            });
2990            wrap.appendChild(dot);
2991        });
2992
2993        host.appendChild(wrap);
2994
2995        instance.dotsWrap = wrap;
2996        instance.dots = Array.prototype.slice.call(wrap.children);
2997        }
2998
2999        function scrollToCard(instance, index) {
3000        var rect = instance.section.getBoundingClientRect();
3001        var scrollableDistance = rect.height - window.innerHeight;
3002        if (scrollableDistance <= 0) return;
3003
3004        var sectionTop = rect.top + window.scrollY;
3005
3006        // same resting point the CTA targets: card fully in, not yet exiting
3007        var targetLocal = index + (ACTIVE_FRACTION + 1) / 2;
3008        var targetProgress = Math.min(1, targetLocal / instance.cardCount);
3009        var DOT_SCROLL_DURATION_MS = 600;   
3010        animatedScrollTo(sectionTop + targetProgress * scrollableDistance, DOT_SCROLL_DURATION_MS);
3011        }
3012
3013        function updateDots(instance, segmentPosition) {
3014        if (!instance.dots) return;
3015
3016        var active = Math.min(
3017            instance.cardCount - 1,
3018            Math.max(0, Math.floor(segmentPosition))
3019        );
3020
3021        if (active === instance.activeDot) return;
3022        instance.activeDot = active;
3023
3024        instance.dots.forEach(function (dot, i) {
3025            var on = (i === active);
3026            dot.classList.toggle('active', on);
3027            dot.setAttribute('data-active', on ? 'true' : 'false');
3028        });
3029        }
3030
3031        function update(instance) {
3032        var rect = instance.section.getBoundingClientRect();
3033        var scrollableDistance = rect.height - window.innerHeight;
3034        var overallProgress = scrollableDistance <= 0
3035            ? 0
3036            : Math.min(1, Math.max(0, -rect.top / scrollableDistance));
3037
3038        var segmentPosition = overallProgress * instance.cardCount;
3039        if (segmentPosition === instance.lastSegment) return;
3040        instance.lastSegment = segmentPosition;
3041        
3042        updateDots(instance, segmentPosition);
3043
3044        if (instance.card0RestOffsetX === null) measureCard0Offset(instance);
3045
3046        var currentIndex = Math.min(instance.cardCount - 1, Math.floor(segmentPosition));
3047        var rawLocalProgress = Math.min(1, segmentPosition - currentIndex);
3048        var localProgress = Math.min(1, rawLocalProgress / ACTIVE_FRACTION);
3049
3050        if (instance.content) {
3051            var contentRaw = Math.min(1, segmentPosition);
3052            var contentProgress = Math.min(1, contentRaw / ACTIVE_FRACTION);
3053            var contentWidth = instance.content.offsetWidth;
3054            var contentX = -contentProgress * contentWidth * CONTENT_EXIT_MULTIPLIER;
3055            instance.content.style.transform =
3056            'translateY(-50%) translateX(' + contentX + 'px)';
3057            instance.content.style.opacity = String(Math.max(0, 1 - contentProgress * 1.4));
3058            instance.content.style.pointerEvents = contentProgress >= 0.99 ? 'none' : 'auto';
3059        }
3060
3061        instance.cards.forEach(function (card, i) {
3062            var offsetX = 0, offsetY = 0, scale = 1, opacity = 1, ratio, copyOpacity = 0;
3063
3064            if (i > currentIndex) {
3065            ratio = CARD_START_RATIO;
3066            copyOpacity = 0;
3067            if (i === 0) offsetX = instance.card0RestOffsetX;
3068            else offsetY = window.innerHeight;
3069            } else if (i === currentIndex) {
3070            ratio = CARD_START_RATIO + (CARD_END_RATIO - CARD_START_RATIO) * localProgress;
3071            copyOpacity = localProgress;
3072            if (i === 0) offsetX = instance.card0RestOffsetX * (1 - localProgress);
3073            else offsetY = window.innerHeight * (1 - localProgress);
3074            } else if (i === currentIndex - 1) {
3075            ratio = CARD_END_RATIO;
3076            scale = 1 - (1 - OVERLAPPED_SCALE) * localProgress;
3077            opacity = 1 - localProgress;
3078            copyOpacity = opacity;
3079            } else {
3080            ratio = CARD_END_RATIO;
3081            scale = OVERLAPPED_SCALE;
3082            opacity = 0;
3083            copyOpacity = 0;
3084            }
3085
3086            card.style.transform =
3087            'translate(calc(-50% + ' + offsetX + 'px), calc(-50% + ' + offsetY + 'px)) scale(' + scale + ')';
3088            card.style.aspectRatio = String(ratio);
3089            card.style.opacity = String(opacity);
3090            card.style.pointerEvents = (i === currentIndex && localProgress >= 1) ? 'auto' : 'none';
3091            var cardCopy = card.querySelector('.ub-work__card-copy');
3092            if (cardCopy) cardCopy.style.opacity = String(copyOpacity);
3093        });
3094
3095        instance.section.style.setProperty('--progress', String(overallProgress));
3096        }
3097
3098        function init(instance) {
3099        instance.section.style.setProperty('--segment-count', Str
3099ing(instance.cardCount));
3100        }
3101
3102        var CTA_SCROLL_DURATION_MS = 1400;
3103        var activeScrollAnim = null;
3104
3105        function cancelScrollAnim() {
3106        activeScrollAnim = null;
3107        }
3108
3109        function animatedScrollTo(targetY, duration) {
3110        var startY = window.scrollY;
3111        var distance = targetY - startY;
3112        var startTime = null;
3113        var token = {};
3114
3115        activeScrollAnim = token;
3116
3117        function step(timestamp) {
3118            if (activeScrollAnim !== token) return;   // superseded or cancelled
3119            if (startTime === null) startTime = timestamp;
3120
3121            var t = Math.min(1, (timestamp - startTime) / duration);
3122            var eased = 1 - Math.pow(1 - t, 3);       // easeOutCubic
3123
3124            window.scrollTo(0, startY + distance * eased);
3125
3126            if (t < 1) requestAnimationFrame(step);
3127            else activeScrollAnim = null;
3128        }
3129
3130        requestAnimationFrame(step);
3131        }
3132
3133        window.addEventListener('wheel', cancelScrollAnim, { passive: true });
3134        window.addEventListener('touchstart', cancelScrollAnim, { passive: true });
3135
3136        instances.forEach(function (instance) {
3137        var cta = instance.section.querySelector('.ub-work__cta');
3138        if (!cta) return;
3139        cta.addEventListener('click', function (e) {
3140            var rect = instance.section.getBoundingClientRect();
3141            var scrollableDistance = rect.height - window.innerHeight;
3142            if (scrollableDistance <= 0) return;
3143            e.preventDefault();
3144            var sectionTop = rect.top + window.scrollY;
3145            var targetLocal = (ACTIVE_FRACTION + 1) / 2;
3146            var targetProgress = targetLocal / instance.cardCount;
3147            animatedScrollTo(sectionTop + targetProgress * scrollableDistance, CTA_SCROLL_DURATION_MS);
3148        });
3149        });
3150
3151        var ticking = false;
3152        function onScrollOrResize() {
3153        if (ticking) return;
3154        ticking = true;
3155        requestAnimationFrame(function () {
3156            instances.forEach(update);
3157            ticking = false;
3158        });
3159        }
3160
3161        function forceRemeasure() {
3162        instances.forEach(function (instance) {
3163            instance.card0RestOffsetX = null;
3164            instance.lastSegment = -1;
3165        });
3166        instances.forEach(update);
3167        }
3168
3169        window.addEventListener('scroll', onScrollOrResize, { passive: true });
3170        window.addEventListener('resize', forceRemeasure);
3171        
3172        instances.forEach(buildDots);
3173        instances.forEach(init);
3174        instances.forEach(update);
3175
3176        if (document.readyState === 'complete') {
3177        forceRemeasure();
3178        } else {
3179        window.addEventListener('load', forceRemeasure);
3180        }
3181    }
3182
3183    }
3184    })();
3185    //*************** Home, MS and DS listical Page card shuffle on scroll ****************************
3186
3187    //*************** Case study listical Page card shuffle on scroll ****************************
3188    (function () {
3189    var prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
3190    if (prefersReducedMotion) return;
3191
3192    var sections = Array.prototype.slice.call(document.querySelectorAll('#case-study-work-results .ub-work'));
3193    if (!sections.length) return;
3194
3195    if (window.innerWidth < 767) {
3196        mobileInit(sections);
3197    } else {
3198        desktopInit(sections);
3199    }
3200
3201    /* ==============================================================
3202        MOBILE – auto-scrolling infinite carousel
3203        ============================================================== */
3204    function mobileInit(sections) {
3205        sections.forEach(setupMobileCarousel);
3206    }
3207
3208    function setupMobileCarousel(section) {
3209        var cards = Array.prototype.slice.call(section.querySelectorAll('.ub-work__card'));
3210        if (!cards.length) return;
3211
3212        var pin = section.querySelector('.ub-work__pin') || section;
3213
3214        // Clear any stale inline styles from desktop logic
3215        cards.forEach(function (card) {
3216        var bg = card.getAttribute('style') || '';
3217        card.style.cssText = bg;
3218        });
3219
3220        // --- Build carousel DOM ---
3221        var viewport = document.createElement('div');
3222        viewport.className = 'ub-work-mob-viewport';
3223
3224        var track = document.createElement('div');
3225        track.className = 'ub-work-mob-track';
3226
3227        // Move original cards into track
3228        cards.forEach(function (card) { track.appendChild(card); });
3229
3230        // Clone all cards for seamless infinite loop
3231        cards.forEach(function (card) {
3232        var clone = card.cloneNode(true);
3233        clone.setAttribute('data-clone', 'true');
3234        clone.setAttribute('aria-hidden', 'true');
3235        track.appendChild(clone);
3236        });
3237
3238        viewport.appendChild(track);
3239        pin.appendChild(viewport);
3240
3241        // --- Dots ---
3242        var dotsWrap = document.createElement('div');
3243        dotsWrap.className = 'ub-work-mob-dots';
3244        cards.forEach(function (_, i) {
3245        var dot = document.createElement('button');
3246        dot.className = 'ub-work-mob-dot' + (i === 0 ? ' active' : '');
3247        dot.setAttribute('aria-label', 'Go to slide ' + (i + 1));
3248        dot.addEventListener('click', function () {
3249            goTo(i, true);
3250            restartAuto();
3251        });
3252        dotsWrap.appendChild(dot);
3253        });
3254        viewport.after(dotsWrap);
3255
3256        var dots = Array.prototype.slice.call(dotsWrap.querySelectorAll('.ub-work-mob-dot'));
3257
3258        // --- State ---
3259        var current       = 0;
3260        var total         = cards.length;
3261        var autoTimer     = null;
3262        var transitioning = false;
3263        var AUTO_DELAY    = 3500;
3264        var TRANS_MS      = 700;
3265
3266        function cardStep() {
3267        var c = track.children[0];
3268        if (!c) return window.innerWidth;
3269        var gap = parseFloat(getComputedStyle(track).gap) || 0;
3270        return c.offsetWidth + gap;
3271        }
3272
3273        function updateDots(activeIndex) {
3274        dots.forEach(function (d, i) {
3275            d.classList.toggle('active', i === activeIndex);
3276        });
3277        }
3278
3279        function slideTo(index, animated) {
3280        if (animated) {
3281            track.style.transition = 'transform ' + TRANS_MS + 'ms cubic-bezier(.4,0,.2,1)';
3282            transitioning = true;
3283        } else {
3284            track.style.transition = 'none';
3285            transitioning = false;
3286        }
3287        track.style.transform = 'translateX(' + (-index * cardStep()) + 'px)';
3288        updateDots(((index % total) + total) % total);
3289        }
3290
3291        track.addEventListener('transitionend', function () {
3292        transitioning = false;
3293        if (current >= total) {
3294            current = current % total;
3295            slideTo(current, false);
3296        }
3297        });
3298
3299        function goTo(index, animated) {
3300        if (transitioning) return;
3301        current = index;
3302        slideTo(current, animated);
3303        }
3304
3305        function advance() {
3306        if (transitioning) return;
3307        current++;
3308        slideTo(current, true);
3309        }
3310
3311        function startAuto() { stopAuto(); autoTimer = setInterval(advance, AUTO_DELAY); }
3312        function stopAuto()  { if (autoTimer) { clearInterval(autoTimer); autoTimer = null; } }
3313        function restartAuto() { stopAuto(); startAuto(); }
3314
3315        // --- Touch / swipe ---
3316        var touchStartX = 0;
3317        var touchDiffX  = 0;
3318        var swiping     = false;
3319        var dragOffset  = 0;
3320
3321        viewport.addEventListener('touchstart', function (e) {
3322        if (transitioning) return;
3323        stopAuto();
3324        touchStartX = e.touches[0].clientX;
3325        touchDiffX  = 0;
3326        swiping     = false;
3327        track.style.transition = 'none';
3328        }, { passive: true });
3329
3330        viewport.addEventListener('touchmove', function (e) {
3331        if (transitioning) return;
3332        touchDiffX = e.touches[0].clientX - touchStartX;
3333        if (!swiping && Math.abs(touchDiffX) > 8) swiping = true;
3334        if (swiping) {
3335            dragOffset = touchDiffX;
3336            track.style.transform = 'translateX(' + (-current * cardStep() + dragOffset) + 'px)';
3337        }
3338        }, { passive: true });
3339
3340        viewport.addEventListener('touchend', function () {
3341        if (swiping && !transitioning) {
3342            var threshold = cardStep() * 0.2;
3343            if (dragOffset < -threshold) {
3344            current++;
3345            if (current >= total * 2) current = total;
3346            } else if (dragOffset > threshold && current > 0) {
3347            current--;
3348            }
3349            slideTo(current, true);
3350        }
3351        dragOffset = 0;
3352        swiping = false;
3353        restartAuto();
3354        });
3355
3356        // Pause when tab hidden
3357        document.addEventListener('visibilitychange', function () {
3358        if (document.hidden) stopAuto(); else startAuto();
3359        });
3360
3361        // Recalc on resize while still mobile
3362        window.addEventListener('resize', function () {
3363        if (window.innerWidth >= 767) return;
3364        slideTo(current, false);
3365        });
3366
3367        // --- Init ---
3368        slideTo(0, false);
3369        startAuto();
3370    }
3371
3372
3373    /* ==============================================================
3374        DESKTOP – scroll-driven card-stack + navigation dots
3375        ============================================================== */
3376    function desktopInit(sections) {
3377
3378        var CARD_START_RATIO = 5 / 4;
3379        var CARD_END_RATIO = 16 / 9;
3380        var OVERLAPPED_SCALE = 0.85;
3381        var ACTIVE_FRACTION = 0.5;
3382        var DOT_SCROLL_DURATION_MS = 600;
3383
3384        var instances = sections.map(function (section) {
3385        var cards = Array.prototype.slice.call(section.querySelectorAll('.ub-work__card'));
3386        cards.forEach(function (card, i) { card.style.setProperty('--card-z', String(i + 1)); });
3387        return {
3388            section: section,
3389            cards: cards,
3390            cardCount: cards.length,
3391            lastSegment: -1,
3392            dots: null,
3393            activeDot: -1
3394        };
3395        });
3396
3397
3398        /* ---------- smooth scroll helper ---------- */
3399
3400        var activeScrollAnim = null;
3401
3402        function cancelScrollAnim() {
3403        activeScrollAnim = null;
3404        }
3405
3406        function animatedScrollTo(targetY, duration) {
3407        var startY = window.scrollY;
3408        var distance = targetY - startY;
3409        var startTime = null;
3410        var token = {};
3411
3412        activeScrollAnim = token;
3413
3414        function step(timestamp) {
3415            if (activeScrollAnim !== token) return;
3416            if (startTime === null) startTime = timestamp;
3417
3418            var t = Math.min(1, (timestamp - startTime) / duration);
3419            var eased = 1 - Math.pow(1 - t, 3);
3420
3421            window.scrollTo(0, startY + distance * eased);
3422
3423            if (t < 1) requestAnimationFrame(step);
3424            else activeScrollAnim = null;
3425        }
3426
3427        requestAnimationFrame(step);
3428        }
3429
3430        window.addEventListener('wheel', cancelScrollAnim, { passive: true });
3431        window.addEventListener('touchstart', cancelScrollAnim, { passive: true });
3432
3433
3434        /* ---------- navigation dots ---------- */
3435
3436        function buildDots(instance) {
3437        if (instance.cardCount < 2) return;
3438
3439        var host = instance.section.querySelector('.ub-work__pin') || instance.section;
3440
3441        var wrap = document.createElement('div');
3442        wrap.className = 'ub-work-dots';
3443
3444        instance.cards.forEach(function (_, i) {
3445            var dot = document.createElement('button');
3446            dot.type = 'button';
3447            dot.className = 'ub-work-dot' + (i === 0 ? ' active' : '');
3448            dot.setAttribute('aria-label', 'Go to card ' + (i + 1));
3449            dot.setAttribute('data-active', i === 0 ? 'true' : 'false');
3450
3451            dot.addEventListener('click', function () {
3452            // updateDots(instance, i + 0.5);
3453            scrollToCard(instance, i);
3454            });
3455
3456            wrap.appendChild(dot);
3457        });
3458
3459        host.appendChild(wrap);
3460        instance.dots = Array.prototype.slice.call(wrap.children);
3461        }
3462
3463        function scrollToCard(instance, index) {
3464        var rect = instance.section.getBoundingClientRect();
3465        var scrollableDistance = rect.height - window.innerHeight;
3466        if (scrollableDistance <= 0) return;
3467
3468        var sectionTop = rect.top + window.scrollY;
3469
3470        // land where the card has fully scaled in but hasn't started handing off
3471        var targetLocal = index + (ACTIVE_FRACTION + 1) / 2;
3472        var targetProgress = Math.min(1, targetLocal / instance.cardCount);
3473
3474        animatedScrollTo(sectionTop + targetProgress * scrollableDistance, DOT_SCROLL_DURATION_MS);
3475        }
3476
3477        function updateDots(instance, segmentPosition) {
3478        if (!instance.dots) return;
3479
3480        var active = Math.min(
3481            instance.cardCount - 1,
3482            Math.max(0, Math.floor(segmentPosition))
3483        );
3484
3485        if (active === instance.activeDot) return;
3486        instance.activeDot = active;
3487
3488        instance.dots.forEach(function (dot, i) {
3489            var on = (i === active);
3490            dot.classList.toggle('active', on);
3491            dot.setAttribute('data-active', on ? 'true' : 'false');
3492        });
3493        }
3494
3495
3496        /* ---------- card stack ---------- */
3497
3498        function update(instance) {
3499        var rect = instance.section.getBoundingClientRect();
3500        var scrollableDistance = rect.height - window.innerHeight;
3501        var overallProgress = scrollableDistance <= 0
3502            ? 0
3503            : Math.min(1, Math.max(0, -rect.top / scrollableDistance));
3504
3505        var segmentPosition = overallProgress * instance.cardCount;
3506        if (segmentPosition === instance.lastSegment) return;
3507        instance.lastSegment = segmentPosition;
3508
3509        updateDots(instance, segmentPosition);
3510
3511        var currentIndex = Math.min(instance.cardCount - 1, Math.floor(segmentPosition));
3512        var rawLocalProgress = Math.min(1, segmentPosition - currentIndex);
3513        var localProgress = Math.min(1, rawLocalProgress / ACTIVE_FRACTION);
3514
3515        instance.cards.forEach(function (card, i) {
3516            var offsetY = 0, scale = 1, opacity = 1, ratio, copyOpacity = 0;
3517
3518            if (i > currentIndex) {
3519            ratio = CARD_START_RATIO;
3520            copyOpacity = 0;
3521            offsetY = window.innerHeight;
3522            } else if (i === currentIndex) {
3523            ratio = CARD_START_RATIO + (CARD_END_RATIO - CARD_START_RATIO) * localProgress;
3524            copyOpacity = localProgress;
3525            if (i > 0) offsetY = window.innerHeight * (1 - localProgress);
3526            } else if (i === currentIndex - 1) {
3527            ratio = CARD_END_RATIO;
3528            scale = 1 - (1 - OVERLAPPED_SCALE) * localProgress;
3529            opacity = 1 - localProgress;
3530            copyOpacity = opacity;
3531            } else {
3532            ratio = CARD_END_RATIO;
3533            scale = OVERLAPPED_SCALE;
3534            opacity = 0;
3535            copyOpacity = 0;
3536            }
3537
3538            card.style.transform =
3539            'translate(-50%, calc(-50% + ' + offsetY + 'px)) scale(' + scale + ')';
3540            card.style.aspectRatio = String(ratio);
3541            card.style.opacity = String(opacity);
3542            card.style.pointerEvents = (i === currentIndex && localProgress >= 1) ? 'auto' : 'none';
3543            var cardCopy = card.querySelector('.ub-work__card-copy');
3544            if (cardCopy) cardCopy.style.opacity = String(copyOpacity);
3545        });
3546
3547        instance.section.style.setProperty('--progress', String(overallProgress));
3548        }
3549
3550        function init(instance) {
3551        instance.section.style.setProperty('--segment-count', String(instance.cardCount));
3552        }
3553
3554        function forceRemeasure() {
3555        instances.forEach(function (instance) {
3556            instance.lastSegment = -1;
3557            instance.activeDot = -1;
3558        });
3559        instances.forEach(update);
3560        }
3561
3562        var ticking = false;
3563        function onScrollOrResize() {
3564        if (ticking) return;
3565        ticking = true;
3566        requestAnimationFrame(function () {
3567            instances.forEach(update);
3568            ticking = false;
3569        });
3570        }
3571
3572        window.addEventListener('scroll', onScrollOrResize, { passive: true });
3573        window.addEventListener('resize', forceRemeasure);
3574
3575        instances.forEach(buildDots);
3576        instances.forEach(init);
3577        instances.forEach(update);
3578    }
3579
3580    })();
3581    //*************** Case study listical Page card shuffle on scroll ****************************
3582
3583})
3584//*************** card shuffle on scroll ****************************

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.